Merge branch 'master' into omegapirate

This commit is contained in:
Luke Street 2020-04-18 18:14:51 -04:00
commit 30f2ac3f26
475 changed files with 8868 additions and 6713 deletions

View File

@ -68,7 +68,7 @@ before_build:
build_script: build_script:
- mkdir build - mkdir build
- cd build - cd build
- cmake -DCMAKE_BUILD_TYPE=%CONFIGURATION% -GNinja .. - cmake -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DLLVM_ROOT_DIR=C:\projects\deps\llvm -GNinja ..
- ninja urde - ninja urde
#notifications: #notifications:

View File

@ -2,6 +2,7 @@
BasedOnStyle: LLVM BasedOnStyle: LLVM
ColumnLimit: 120 ColumnLimit: 120
UseTab: Never UseTab: Never
TabWidth: 4
--- ---
Language: Cpp Language: Cpp
DerivePointerAlignment: false DerivePointerAlignment: false
@ -25,4 +26,4 @@ BinPackParameters: true
SortIncludes: false SortIncludes: false
AccessModifierOffset: -2 AccessModifierOffset: -2
ConstructorInitializerIndentWidth: 0 ConstructorInitializerIndentWidth: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerAllOnOneLineOrOnePerLine: true

View File

@ -40,8 +40,8 @@ project(urde VERSION 0.1.0)
# when available. GCC and Clang posess no such flag, and must be # when available. GCC and Clang posess no such flag, and must be
# manually enforced. CMake, curiously, also doesn't have a "latest" # manually enforced. CMake, curiously, also doesn't have a "latest"
# standard flag either. # standard flag either.
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") if (NOT MSVC)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif() endif()
@ -86,30 +86,45 @@ if(MSVC)
add_compile_options(/IGNORE:4221 /wd4018 /wd4800 /wd4005 /wd4311 /wd4068 add_compile_options(/IGNORE:4221 /wd4018 /wd4800 /wd4005 /wd4311 /wd4068
/wd4267 /wd4244 /wd4200 /wd4305 /wd4067 /wd4146 /wd4309 /wd4805 ${VS_OPTIONS}) /wd4267 /wd4244 /wd4200 /wd4305 /wd4067 /wd4146 /wd4309 /wd4805 ${VS_OPTIONS})
if(WINDOWS_STORE) add_compile_options(
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /AI\"$ENV{PROGRAMFILES\(X86\)}/Microsoft Visual Studio/2017/Community/Common7/IDE/VC/vcpackages\" /AI\"$ENV{PROGRAMFILES\(X86\)}/Windows Kits/10/UnionMetadata\"") # Disable exceptions
set(HAVE_WORDS_BIGENDIAN_EXITCODE 0) $<$<COMPILE_LANGUAGE:CXX>:/EHsc->
endif()
add_compile_options(/EHsc) # Disable RTTI
$<$<COMPILE_LANGUAGE:CXX>:/GR->
# Enforce various standards compliant behavior.
$<$<COMPILE_LANGUAGE:CXX>:/permissive->
# Enable standard volatile semantics.
$<$<COMPILE_LANGUAGE:CXX>:/volatile:iso>
# Reports the proper value for the __cplusplus preprocessor macro.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>
# Use latest C++ standard.
$<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>
)
add_compile_definitions(FMT_EXCEPTIONS=0 _HAS_EXCEPTIONS=0)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Flags for MSVC (not clang-cl)
add_compile_options( add_compile_options(
/std:c++latest # Use latest C++ standard. # Allow constexpr variables to have explicit external linkage.
/permissive- # Enforce various standards compliance features. $<$<COMPILE_LANGUAGE:CXX>:/Zc:externConstexpr>
/Zc:externConstexpr # Allow extern constexpr variables according to the standard.
/Zc:throwingNew # Assume new throws, allowing for better code generation. # Assume that new throws exceptions, allowing better code generation.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:throwingNew>
# Link-time Code Generation for Release builds
$<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:/GL>
) )
# Link-time Code Generation for Release builds (excluding clang-cl) # Link-time Code Generation for Release builds
set(CMAKE_C_FLAGS_RELEASE "/DNDEBUG /O2 /Oy /GL /Gy /MD")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "/DNDEBUG /Zi /O2 /Oy- /GL /Gy /MD")
set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "/LTCG") set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "/LTCG")
set(CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "/LTCG") set(CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "/LTCG")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO") set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/DEBUG /RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO /DEBUGTYPE:cv,fixup") set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/DEBUG /RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO /DEBUGTYPE:cv,fixup")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
endif() endif()
else() else()
@ -144,21 +159,19 @@ else()
endif() endif()
if(URDE_MSAN) if(URDE_MSAN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-stdlib=libc++> -fsanitize=memory
add_compile_options(-fsanitize=memory -fsanitize-memory-track-origins -fsanitize-recover=all) -fsanitize-memory-track-origins -fsanitize-recover=all)
endif() endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions") add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>
add_compile_options(-Wall -Wno-multichar -Werror=implicit-fallthrough -Wno-unknown-warning-option $<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
-Wall -Wno-multichar -Werror=implicit-fallthrough -Wno-unknown-warning-option
-Wno-lto-type-mismatch -Wno-unused-variable -Wno-unused-private-field -Wno-lto-type-mismatch -Wno-unused-variable -Wno-unused-private-field
-Wno-unused-function -Wno-sign-compare -Wno-unknown-pragmas -Werror) -Wno-unused-function -Wno-sign-compare -Wno-unknown-pragmas -Werror)
add_compile_definitions(FMT_EXCEPTIONS=0) add_compile_definitions(FMT_EXCEPTIONS=0)
if(APPLE) if(APPLE)
add_compile_options(-Wno-error=deprecated-declarations) add_compile_options(-Wno-error=deprecated-declarations
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -flto=thin") $<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:-flto=thin>)
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -flto=thin")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto=thin")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -flto=thin")
endif() endif()
endif() endif()

186
CMakeSettings.json Normal file
View File

@ -0,0 +1,186 @@
{
"configurations": [
{
"name": "x64-Clang-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "clang_cl_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"variables": [
{
"name": "CMAKE_PREFIX_PATH",
"value": "C:\\Qt\\5.14.2\\msvc2017_64",
"type": "FILEPATH"
},
{
"name": "CMAKE_CXX_FLAGS",
"value": "-m64 -fdiagnostics-absolute-paths",
"type": "STRING"
},
{
"name": "CMAKE_C_FLAGS",
"value": "-m64 -fdiagnostics-absolute-paths",
"type": "STRING"
},
{
"name": "CMAKE_LINKER",
"value": "C:\\Program Files\\LLVM\\bin\\lld-link.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_C_COMPILER",
"value": "C:\\Program Files\\LLVM\\bin\\clang-cl.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_CXX_COMPILER",
"value": "C:\\Program Files\\LLVM\\bin\\clang-cl.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_C_COMPILER_RANLIB",
"value": "C:\\Program Files\\LLVM\\bin\\llvm-ranlib.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_C_COMPILER_AR",
"value": "C:\\Program Files\\LLVM\\bin\\llvm-ar.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_CXX_COMPILER_AR",
"value": "C:\\Program Files\\LLVM\\bin\\llvm-ar.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_CXX_COMPILER_RANLIB",
"value": "C:\\Program Files\\LLVM\\bin\\llvm-ranlib.exe",
"type": "FILEPATH"
}
]
},
{
"name": "x64-Clang-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"inheritEnvironments": [ "clang_cl_x64" ],
"variables": [
{
"name": "CMAKE_PREFIX_PATH",
"value": "C:\\Qt\\5.14.2\\msvc2017_64",
"type": "FILEPATH"
},
{
"name": "CMAKE_CXX_FLAGS",
"value": "-m64 -fdiagnostics-absolute-paths",
"type": "STRING"
},
{
"name": "CMAKE_C_FLAGS",
"value": "-m64 -fdiagnostics-absolute-paths",
"type": "STRING"
},
{
"name": "CMAKE_LINKER",
"value": "C:\\Program Files\\LLVM\\bin\\lld-link.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_C_COMPILER",
"value": "C:\\Program Files\\LLVM\\bin\\clang-cl.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_CXX_COMPILER",
"value": "C:\\Program Files\\LLVM\\bin\\clang-cl.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_C_COMPILER_RANLIB",
"value": "C:\\Program Files\\LLVM\\bin\\llvm-ranlib.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_C_COMPILER_AR",
"value": "C:\\Program Files\\LLVM\\bin\\llvm-ar.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_CXX_COMPILER_AR",
"value": "C:\\Program Files\\LLVM\\bin\\llvm-ar.exe",
"type": "FILEPATH"
},
{
"name": "CMAKE_CXX_COMPILER_RANLIB",
"value": "C:\\Program Files\\LLVM\\bin\\llvm-ranlib.exe",
"type": "FILEPATH"
}
]
},
{
"name": "x64-MSVC-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x64" ],
"variables": [
{
"name": "CMAKE_PREFIX_PATH",
"value": "C:\\Qt\\5.14.2\\msvc2017_64",
"type": "FILEPATH"
},
{
"name": "CMAKE_CXX_FLAGS",
"value": "",
"type": "STRING"
},
{
"name": "CMAKE_C_FLAGS",
"value": "",
"type": "STRING"
}
]
},
{
"name": "x64-MSVC-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x64" ],
"variables": [
{
"name": "CMAKE_PREFIX_PATH",
"value": "C:\\Qt\\5.14.2\\msvc2017_64",
"type": "FILEPATH"
},
{
"name": "CMAKE_CXX_FLAGS",
"value": "",
"type": "STRING"
},
{
"name": "CMAKE_C_FLAGS",
"value": "",
"type": "STRING"
}
]
}
]
}

View File

@ -32,7 +32,7 @@ void LoadAssetMap(athena::io::MemoryReader& ar) {
ar.readBytesToBuf(&magic, 4); ar.readBytesToBuf(&magic, 4);
if (magic != FOURCC('AIDM')) if (magic != FOURCC('AIDM'))
Log.report(logvisor::Warning, Log.report(logvisor::Warning,
fmt(_SYS_STR("Unable to load asset map; Assets will not have proper filenames for most files."))); FMT_STRING(_SYS_STR("Unable to load asset map; Assets will not have proper filenames for most files.")));
else { else {
uint32_t assetCount = ar.readUint32Big(); uint32_t assetCount = ar.readUint32Big();
g_AssetNameMap.reserve(assetCount); g_AssetNameMap.reserve(assetCount);
@ -50,7 +50,7 @@ void InitAssetNameMap() {
if (g_AssetNameMapInit) if (g_AssetNameMapInit)
return; return;
Log.report(logvisor::Info, fmt("Initializing asset name database...")); Log.report(logvisor::Info, FMT_STRING("Initializing asset name database..."));
/* First load the 32bit map for MP1/2 */ /* First load the 32bit map for MP1/2 */
{ {

File diff suppressed because it is too large Load Diff

View File

@ -15,10 +15,11 @@
namespace DataSpec::DNAANCS { namespace DataSpec::DNAANCS {
template <class PAKRouter, class ANCSDNA, class MaterialSet, class SurfaceHeader, atUint32 CMDLVersion> template <class PAKRouter, class ANCSDNA, class MaterialSet, class SurfaceHeader, atUint32 CMDLVersion>
bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, const hecl::ProjectPath& outPath, bool ReadANCSToBlender(hecl::blender::Token& btok, const ANCSDNA& ancs, const hecl::ProjectPath& outPath,
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec, PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec,
std::function<void(const hecl::SystemChar*)> fileChanged, bool force) { std::function<void(const hecl::SystemChar*)> fileChanged, bool force) {
/* Extract character CMDL/CSKR first */ auto& conn = btok.getBlenderConnection();
/* Extract character CMDL/CSKR/CINF first */
std::vector<CharacterResInfo<typename PAKRouter::IDType>> chResInfo; std::vector<CharacterResInfo<typename PAKRouter::IDType>> chResInfo;
ancs.getCharacterResInfo(chResInfo); ancs.getCharacterResInfo(chResInfo);
for (const auto& info : chResInfo) { for (const auto& info : chResInfo) {
@ -49,11 +50,19 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
conn.saveBlend(); conn.saveBlend();
} }
} }
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(info.cinf, &node, true, false)) {
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
if (cinfPath.getPathType() == hecl::ProjectPath::Type::None) {
PAKEntryReadStream rs = cinfE->beginReadStream(*node);
ANCSDNA::CINFType::Extract(dataspec, rs, cinfPath, pakRouter, *cinfE, false, btok, fileChanged);
}
}
} }
/* Extract attachment CMDL/CSKRs first */ /* Extract attachment CMDL/CSKR/CINFs first */
auto attRange = pakRouter.lookupCharacterAttachmentRigs(entry.id); auto attRange = pakRouter.lookupCharacterAttachmentRigs(entry.id);
for (auto it = attRange.first; it != attRange.second; ++it) { for (auto it = attRange.first; it != attRange.second; ++it) {
auto cinfid = it->second.first.cinf;
auto cmdlid = it->second.first.cmdl; auto cmdlid = it->second.first.cmdl;
const nod::Node* node; const nod::Node* node;
@ -61,8 +70,9 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE); hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
if (force || cmdlPath.isNone()) { if (force || cmdlPath.isNone()) {
cmdlPath.makeDirChain(false); cmdlPath.makeDirChain(false);
if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh)) if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh)) {
return false; return false;
}
std::string bestName = pakRouter.getBestEntryName(*cmdlE); std::string bestName = pakRouter.getBestEntryName(*cmdlE);
hecl::SystemStringConv bestNameView(bestName); hecl::SystemStringConv bestNameView(bestName);
@ -84,6 +94,15 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
conn.saveBlend(); conn.saveBlend();
} }
} }
if (cinfid.isValid()) {
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(cinfid, &node, true, false)) {
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
if (cinfPath.getPathType() == hecl::ProjectPath::Type::None) {
PAKEntryReadStream rs = cinfE->beginReadStream(*node);
ANCSDNA::CINFType::Extract(dataspec, rs, cinfPath, pakRouter, *cinfE, false, btok, fileChanged);
}
}
}
} }
std::string bestName = pakRouter.getBestEntryName(entry); std::string bestName = pakRouter.getBestEntryName(entry);
@ -99,7 +118,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
{ {
hecl::blender::PyOutStream os = conn.beginPythonOut(true); hecl::blender::PyOutStream os = conn.beginPythonOut(true);
os.format(fmt( os.format(FMT_STRING(
"import bpy\n" "import bpy\n"
"from mathutils import Vector\n" "from mathutils import Vector\n"
"bpy.context.scene.name = '{}'\n" "bpy.context.scene.name = '{}'\n"
@ -116,7 +135,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
std::unordered_set<typename PAKRouter::IDType> cinfsDone; std::unordered_set<typename PAKRouter::IDType> cinfsDone;
for (const auto& info : chResInfo) { for (const auto& info : chResInfo) {
/* Provide data to add-on */ /* Provide data to add-on */
os.format(fmt( os.format(FMT_STRING(
"actor_subtype = actor_data.subtypes.add()\n" "actor_subtype = actor_data.subtypes.add()\n"
"actor_subtype.name = '{}'\n\n"), "actor_subtype.name = '{}'\n\n"),
info.name); info.name);
@ -125,7 +144,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
if (cinfsDone.find(info.cinf) == cinfsDone.end()) { if (cinfsDone.find(info.cinf) == cinfsDone.end()) {
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(info.cinf, nullptr, true, false)) { if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(info.cinf, nullptr, true, false)) {
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE); hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
os.linkArmature(cinfPath.getAbsolutePathUTF8(), fmt::format(fmt("CINF_{}"), info.cinf)); os.linkArmature(cinfPath.getAbsolutePathUTF8(), fmt::format(FMT_STRING("CINF_{}"), info.cinf));
os << "if obj.name not in bpy.context.scene.objects:\n" os << "if obj.name not in bpy.context.scene.objects:\n"
" bpy.context.scene.collection.objects.link(obj)\n"; " bpy.context.scene.collection.objects.link(obj)\n";
} }
@ -135,7 +154,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
} }
cinfsDone.insert(info.cinf); cinfsDone.insert(info.cinf);
} }
os.format(fmt("arm_obj = bpy.data.objects['CINF_{}']\n"), info.cinf); os.format(FMT_STRING("arm_obj = bpy.data.objects['CINF_{}']\n"), info.cinf);
os << "actor_subtype.linked_armature = arm_obj.name\n"; os << "actor_subtype.linked_armature = arm_obj.name\n";
/* Link CMDL */ /* Link CMDL */
@ -154,7 +173,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
/* Link overlays */ /* Link overlays */
for (const auto& overlay : info.overlays) { for (const auto& overlay : info.overlays) {
os << "overlay = actor_subtype.overlays.add()\n"; os << "overlay = actor_subtype.overlays.add()\n";
os.format(fmt("overlay.name = '{}'\n"), overlay.first); os.format(FMT_STRING("overlay.name = '{}'\n"), overlay.first);
/* Link CMDL */ /* Link CMDL */
if (const typename PAKRouter::EntryType* cmdlE = if (const typename PAKRouter::EntryType* cmdlE =
@ -175,7 +194,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
/* Link attachments */ /* Link attachments */
for (auto it = attRange.first; it != attRange.second; ++it) { for (auto it = attRange.first; it != attRange.second; ++it) {
os << "attachment = actor_data.attachments.add()\n"; os << "attachment = actor_data.attachments.add()\n";
os.format(fmt("attachment.name = '{}'\n"), it->second.second); os.format(FMT_STRING("attachment.name = '{}'\n"), it->second.second);
auto cinfid = it->second.first.cinf; auto cinfid = it->second.first.cinf;
auto cmdlid = it->second.first.cmdl; auto cmdlid = it->second.first.cmdl;
@ -185,7 +204,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
if (cinfsDone.find(cinfid) == cinfsDone.end()) { if (cinfsDone.find(cinfid) == cinfsDone.end()) {
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(cinfid, nullptr, true, false)) { if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(cinfid, nullptr, true, false)) {
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE); hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
os.linkArmature(cinfPath.getAbsolutePathUTF8(), fmt::format(fmt("CINF_{}"), cinfid)); os.linkArmature(cinfPath.getAbsolutePathUTF8(), fmt::format(FMT_STRING("CINF_{}"), cinfid));
os << "if obj.name not in bpy.context.scene.objects:\n" os << "if obj.name not in bpy.context.scene.objects:\n"
" bpy.context.scene.collection.objects.link(obj)\n"; " bpy.context.scene.collection.objects.link(obj)\n";
} }
@ -195,7 +214,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
} }
cinfsDone.insert(cinfid); cinfsDone.insert(cinfid);
} }
os.format(fmt("arm_obj = bpy.data.objects['CINF_{}']\n"), cinfid); os.format(FMT_STRING("arm_obj = bpy.data.objects['CINF_{}']\n"), cinfid);
os << "attachment.linked_armature = arm_obj.name\n"; os << "attachment.linked_armature = arm_obj.name\n";
} }
@ -230,7 +249,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
for (const auto& id : animResInfo) { for (const auto& id : animResInfo) {
typename ANCSDNA::ANIMType anim; typename ANCSDNA::ANIMType anim;
if (pakRouter.lookupAndReadDNA(id.second.animId, anim, true)) { if (pakRouter.lookupAndReadDNA(id.second.animId, anim, true)) {
os.format(fmt( os.format(FMT_STRING(
"act = bpy.data.actions.new('{}')\n" "act = bpy.data.actions.new('{}')\n"
"act.use_fake_user = True\n" "act.use_fake_user = True\n"
"act.anim_id = '{}'\n"), "act.anim_id = '{}'\n"),
@ -238,7 +257,7 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
anim.sendANIMToBlender(os, inverter, id.second.additive); anim.sendANIMToBlender(os, inverter, id.second.additive);
} }
os.format(fmt( os.format(FMT_STRING(
"actor_action = actor_data.actions.add()\n" "actor_action = actor_data.actions.add()\n"
"actor_action.name = '{}'\n"), "actor_action.name = '{}'\n"),
id.second.name); id.second.name);
@ -253,17 +272,17 @@ bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, con
template bool template bool
ReadANCSToBlender<PAKRouter<DNAMP1::PAKBridge>, DNAMP1::ANCS, DNAMP1::MaterialSet, DNACMDL::SurfaceHeader_1, 2>( ReadANCSToBlender<PAKRouter<DNAMP1::PAKBridge>, DNAMP1::ANCS, DNAMP1::MaterialSet, DNACMDL::SurfaceHeader_1, 2>(
hecl::blender::Connection& conn, const DNAMP1::ANCS& ancs, const hecl::ProjectPath& outPath, hecl::blender::Token& btok, const DNAMP1::ANCS& ancs, const hecl::ProjectPath& outPath,
PAKRouter<DNAMP1::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP1::PAKBridge>::EntryType& entry, PAKRouter<DNAMP1::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP1::PAKBridge>::EntryType& entry,
const SpecBase& dataspec, std::function<void(const hecl::SystemChar*)> fileChanged, bool force); const SpecBase& dataspec, std::function<void(const hecl::SystemChar*)> fileChanged, bool force);
template bool template bool
ReadANCSToBlender<PAKRouter<DNAMP2::PAKBridge>, DNAMP2::ANCS, DNAMP2::MaterialSet, DNACMDL::SurfaceHeader_2, 4>( ReadANCSToBlender<PAKRouter<DNAMP2::PAKBridge>, DNAMP2::ANCS, DNAMP2::MaterialSet, DNACMDL::SurfaceHeader_2, 4>(
hecl::blender::Connection& conn, const DNAMP2::ANCS& ancs, const hecl::ProjectPath& outPath, hecl::blender::Token& btok, const DNAMP2::ANCS& ancs, const hecl::ProjectPath& outPath,
PAKRouter<DNAMP2::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP2::PAKBridge>::EntryType& entry, PAKRouter<DNAMP2::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP2::PAKBridge>::EntryType& entry,
const SpecBase& dataspec, std::function<void(const hecl::SystemChar*)> fileChanged, bool force); const SpecBase& dataspec, std::function<void(const hecl::SystemChar*)> fileChanged, bool force);
template bool template bool
ReadANCSToBlender<PAKRouter<DNAMP3::PAKBridge>, DNAMP3::CHAR, DNAMP3::MaterialSet, DNACMDL::SurfaceHeader_3, 4>( ReadANCSToBlender<PAKRouter<DNAMP3::PAKBridge>, DNAMP3::CHAR, DNAMP3::MaterialSet, DNACMDL::SurfaceHeader_3, 4>(
hecl::blender::Connection& conn, const DNAMP3::CHAR& ancs, const hecl::ProjectPath& outPath, hecl::blender::Token& btok, const DNAMP3::CHAR& ancs, const hecl::ProjectPath& outPath,
PAKRouter<DNAMP3::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP3::PAKBridge>::EntryType& entry, PAKRouter<DNAMP3::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP3::PAKBridge>::EntryType& entry,
const SpecBase& dataspec, std::function<void(const hecl::SystemChar*)> fileChanged, bool force); const SpecBase& dataspec, std::function<void(const hecl::SystemChar*)> fileChanged, bool force);

View File

@ -41,7 +41,7 @@ struct AnimationResInfo {
}; };
template <class PAKRouter, class ANCSDNA, class MaterialSet, class SurfaceHeader, atUint32 CMDLVersion> template <class PAKRouter, class ANCSDNA, class MaterialSet, class SurfaceHeader, atUint32 CMDLVersion>
bool ReadANCSToBlender(hecl::blender::Connection& conn, const ANCSDNA& ancs, const hecl::ProjectPath& outPath, bool ReadANCSToBlender(hecl::blender::Token& btok, const ANCSDNA& ancs, const hecl::ProjectPath& outPath,
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec, PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec,
std::function<void(const hecl::SystemChar*)> fileChanged, bool force = false); std::function<void(const hecl::SystemChar*)> fileChanged, bool force = false);

View File

@ -175,7 +175,7 @@ std::vector<std::vector<Value>> BitstreamReader::read(const atUint8* data, size_
for (size_t f = 0; f < keyFrameCount; ++f) { for (size_t f = 0; f < keyFrameCount; ++f) {
#if DUMP_KEYS #if DUMP_KEYS
fmt::print(stderr, fmt("\nFRAME {} {} {}\n"), f, (m_bitCur / 32) * 4, m_bitCur % 32); fmt::print(stderr, FMT_STRING("\nFRAME {} {} {}\n"), f, (m_bitCur / 32) * 4, m_bitCur % 32);
int lastId = -1; int lastId = -1;
#endif #endif
auto kit = chanKeys.begin(); auto kit = chanKeys.begin();
@ -197,7 +197,7 @@ std::vector<std::vector<Value>> BitstreamReader::read(const atUint8* data, size_
QuantizedRot qr = {{p[0], p[1], p[2]}, wBit}; QuantizedRot qr = {{p[0], p[1], p[2]}, wBit};
kit->emplace_back(DequantizeRotation(qr, rotDiv)); kit->emplace_back(DequantizeRotation(qr, rotDiv));
#if DUMP_KEYS #if DUMP_KEYS
fmt::print(stderr, fmt("{} R: {} {} {} {}\t"), chan.id, wBit, p[0], p[1], p[2]); fmt::print(stderr, FMT_STRING("{} R: {} {} {} {}\t"), chan.id, wBit, p[0], p[1], p[2]);
#endif #endif
break; break;
} }
@ -210,7 +210,7 @@ std::vector<std::vector<Value>> BitstreamReader::read(const atUint8* data, size_
p[2] += val3; p[2] += val3;
kit->push_back({p[0] * transMult, p[1] * transMult, p[2] * transMult}); kit->push_back({p[0] * transMult, p[1] * transMult, p[2] * transMult});
#if DUMP_KEYS #if DUMP_KEYS
fmt::print(stderr, fmt("{} T: {} {} {}\t"), chan.id, p[0], p[1], p[2]); fmt::print(stderr, FMT_STRING("{} T: {} {} {}\t"), chan.id, p[0], p[1], p[2]);
#endif #endif
break; break;
} }
@ -220,7 +220,7 @@ std::vector<std::vector<Value>> BitstreamReader::read(const atUint8* data, size_
p[2] += dequantize(data, chan.q[2]); p[2] += dequantize(data, chan.q[2]);
kit->push_back({p[0] * scaleMult, p[1] * scaleMult, p[2] * scaleMult}); kit->push_back({p[0] * scaleMult, p[1] * scaleMult, p[2] * scaleMult});
#if DUMP_KEYS #if DUMP_KEYS
fmt::print(stderr, fmt("{} S: {} {} {}\t"), chan.id, p[0], p[1], p[2]); fmt::print(stderr, FMT_STRING("{} S: {} {} {}\t"), chan.id, p[0], p[1], p[2]);
#endif #endif
break; break;
} }

View File

@ -4,7 +4,7 @@
#include <array> #include <array>
#include "hecl/Blender/Connection.hpp" #include "hecl/Blender/Connection.hpp"
#include "../DNAMP1/PATH.hpp" #include "PATH.hpp"
namespace DataSpec { namespace DataSpec {
logvisor::Module Log("AROTBuilder"); logvisor::Module Log("AROTBuilder");
@ -140,7 +140,7 @@ void AROTBuilder::Node::nodeCount(size_t& sz, size_t& idxRefs, BitmapPool& bmpPo
sz += 1; sz += 1;
poolIdx = bmpPool.addIndices(childIndices); poolIdx = bmpPool.addIndices(childIndices);
if (poolIdx > 65535) if (poolIdx > 65535)
Log.report(logvisor::Fatal, fmt("AROT bitmap exceeds 16-bit node addressing; area too complex")); Log.report(logvisor::Fatal, FMT_STRING("AROT bitmap exceeds 16-bit node addressing; area too complex"));
uint32_t childCount = AROTChildCounts[compSubdivs]; uint32_t childCount = AROTChildCounts[compSubdivs];
nodeOff = curOff; nodeOff = curOff;
@ -180,7 +180,7 @@ void AROTBuilder::Node::writeNodes(athena::io::MemoryWriter& w, int nodeIdx) {
if (childNodes.size()) { if (childNodes.size()) {
int curIdx = nodeIdx + 1; int curIdx = nodeIdx + 1;
if (curIdx > 65535) if (curIdx > 65535)
Log.report(logvisor::Fatal, fmt("AROT node exceeds 16-bit node addressing; area too complex")); Log.report(logvisor::Fatal, FMT_STRING("AROT node exceeds 16-bit node addressing; area too complex"));
std::array<int, 8> childIndices; std::array<int, 8> childIndices;
@ -278,10 +278,10 @@ void AROTBuilder::Node::pathCountNodesAndLookups(size_t& nodeCount, size_t& look
} }
} }
void AROTBuilder::Node::pathWrite(DNAMP1::PATH& path, const zeus::CAABox& curAABB) { template <class PAKBridge>
void AROTBuilder::Node::pathWrite(DNAPATH::PATH<PAKBridge>& path, const zeus::CAABox& curAABB) {
if (childNodes.empty()) { if (childNodes.empty()) {
path.octree.emplace_back(); auto& n = path.octree.emplace_back();
DNAMP1::PATH::OctreeNode& n = path.octree.back();
n.isLeaf = 1; n.isLeaf = 1;
n.aabb[0] = curAABB.min; n.aabb[0] = curAABB.min;
n.aabb[1] = curAABB.max; n.aabb[1] = curAABB.max;
@ -299,8 +299,7 @@ void AROTBuilder::Node::pathWrite(DNAMP1::PATH& path, const zeus::CAABox& curAAB
children[i] = path.octree.size() - 1; children[i] = path.octree.size() - 1;
} }
path.octree.emplace_back(); auto& n = path.octree.emplace_back();
DNAMP1::PATH::OctreeNode& n = path.octree.back();
n.isLeaf = 0; n.isLeaf = 0;
n.aabb[0] = curAABB.min; n.aabb[0] = curAABB.min;
n.aabb[1] = curAABB.max; n.aabb[1] = curAABB.max;
@ -311,6 +310,10 @@ void AROTBuilder::Node::pathWrite(DNAMP1::PATH& path, const zeus::CAABox& curAAB
} }
} }
template void AROTBuilder::Node::pathWrite(DNAPATH::PATH<DNAMP1::PAKBridge>& path, const zeus::CAABox& curAABB);
template void AROTBuilder::Node::pathWrite(DNAPATH::PATH<DNAMP2::PAKBridge>& path, const zeus::CAABox& curAABB);
template void AROTBuilder::Node::pathWrite(DNAPATH::PATH<DNAMP3::PAKBridge>& path, const zeus::CAABox& curAABB);
void AROTBuilder::build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAABox& fullAabb, void AROTBuilder::build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAABox& fullAabb,
const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes) { const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes) {
/* Recursively split */ /* Recursively split */
@ -406,15 +409,14 @@ std::pair<std::unique_ptr<uint8_t[]>, uint32_t> AROTBuilder::buildCol(const ColM
return {std::move(ret), totalSize}; return {std::move(ret), totalSize};
} }
void AROTBuilder::buildPath(DNAMP1::PATH& path) { template <class PAKBridge>
void AROTBuilder::buildPath(DNAPATH::PATH<PAKBridge>& path) {
/* Accumulate total AABB and gather region boxes */ /* Accumulate total AABB and gather region boxes */
std::vector<zeus::CAABox> regionBoxes; std::vector<zeus::CAABox> regionBoxes;
regionBoxes.reserve(path.regions.size()); regionBoxes.reserve(path.regions.size());
zeus::CAABox fullAABB; zeus::CAABox fullAABB;
for (const DNAMP1::PATH::Region& r : path.regions) { for (const auto& r : path.regions)
regionBoxes.emplace_back(r.aabb[0], r.aabb[1]); fullAABB.accumulateBounds(regionBoxes.emplace_back(r.aabb[0], r.aabb[1]));
fullAABB.accumulateBounds(regionBoxes.back());
}
/* Recursively split */ /* Recursively split */
BspNodeType dontCare; BspNodeType dontCare;
@ -431,4 +433,8 @@ void AROTBuilder::buildPath(DNAMP1::PATH& path) {
rootNode.pathWrite(path, fullAABB); rootNode.pathWrite(path, fullAABB);
} }
template void AROTBuilder::buildPath(DNAPATH::PATH<DNAMP1::PAKBridge>& path);
template void AROTBuilder::buildPath(DNAPATH::PATH<DNAMP2::PAKBridge>& path);
template void AROTBuilder::buildPath(DNAPATH::PATH<DNAMP3::PAKBridge>& path);
} // namespace DataSpec } // namespace DataSpec

View File

@ -7,10 +7,12 @@
#include <set> #include <set>
namespace DataSpec { namespace DataSpec {
namespace DNAMP1 { namespace DNAPATH {
template <class PAKBridge>
struct PATH; struct PATH;
} }
struct AROTBuilder { struct AROTBuilder {
using ColMesh = hecl::blender::ColMesh; using ColMesh = hecl::blender::ColMesh;
@ -42,13 +44,15 @@ struct AROTBuilder {
void writeColNodes(uint8_t*& ptr, const zeus::CAABox& curAABB); void writeColNodes(uint8_t*& ptr, const zeus::CAABox& curAABB);
void pathCountNodesAndLookups(size_t& nodeCount, size_t& lookupCount); void pathCountNodesAndLookups(size_t& nodeCount, size_t& lookupCount);
void pathWrite(DNAMP1::PATH& path, const zeus::CAABox& curAABB); template <class PAKBridge>
void pathWrite(DNAPATH::PATH<PAKBridge>& path, const zeus::CAABox& curAABB);
} rootNode; } rootNode;
void build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAABox& fullAabb, void build(std::vector<std::vector<uint8_t>>& secs, const zeus::CAABox& fullAabb,
const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes); const std::vector<zeus::CAABox>& meshAabbs, const std::vector<DNACMDL::Mesh>& meshes);
std::pair<std::unique_ptr<uint8_t[]>, uint32_t> buildCol(const ColMesh& mesh, BspNodeType& rootOut); std::pair<std::unique_ptr<uint8_t[]>, uint32_t> buildCol(const ColMesh& mesh, BspNodeType& rootOut);
void buildPath(DNAMP1::PATH& path); template <class PAKBridge>
void buildPath(DNAPATH::PATH<PAKBridge>& path);
}; };
} // namespace DataSpec } // namespace DataSpec

View File

@ -24,7 +24,7 @@ bool ATBL::Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
uint16_t idx = rs.readUint16Big(); uint16_t idx = rs.readUint16Big();
if (idx == 0xffff) if (idx == 0xffff)
continue; continue;
w.writeUint16(fmt::format(fmt("0x{:04X}"), i), idx); w.writeUint16(fmt::format(FMT_STRING("0x{:04X}"), i), idx);
} }
athena::io::FileWriter fw(outPath.getAbsolutePath()); athena::io::FileWriter fw(outPath.getAbsolutePath());

View File

@ -18,13 +18,13 @@ void ReadBabeDeadLightToBlender(hecl::blender::PyOutStream& os, const BabeDeadLi
switch (light.lightType) { switch (light.lightType) {
case BabeDeadLight::LightType::LocalAmbient: case BabeDeadLight::LightType::LocalAmbient:
case BabeDeadLight::LightType::LocalAmbient2: case BabeDeadLight::LightType::LocalAmbient2:
os.format(fmt( os.format(FMT_STRING(
"bg_node.inputs[0].default_value = ({},{},{},1.0)\n" "bg_node.inputs[0].default_value = ({},{},{},1.0)\n"
"bg_node.inputs[1].default_value = {}\n"), "bg_node.inputs[1].default_value = {}\n"),
light.color.simd[0], light.color.simd[1], light.color.simd[2], light.q / 8.f); light.color.simd[0], light.color.simd[1], light.color.simd[2], light.q / 8.f);
return; return;
case BabeDeadLight::LightType::Directional: case BabeDeadLight::LightType::Directional:
os.format(fmt( os.format(FMT_STRING(
"lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'SUN')\n" "lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'SUN')\n"
"lamp.color = ({},{},{})\n" "lamp.color = ({},{},{})\n"
"lamp_obj = bpy.data.objects.new(lamp.name, lamp)\n" "lamp_obj = bpy.data.objects.new(lamp.name, lamp)\n"
@ -36,7 +36,7 @@ void ReadBabeDeadLightToBlender(hecl::blender::PyOutStream& os, const BabeDeadLi
light.direction.simd[1], light.direction.simd[2], light.castShadows ? "True" : "False"); light.direction.simd[1], light.direction.simd[2], light.castShadows ? "True" : "False");
return; return;
case BabeDeadLight::LightType::Custom: case BabeDeadLight::LightType::Custom:
os.format(fmt( os.format(FMT_STRING(
"lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'POINT')\n" "lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'POINT')\n"
"lamp.color = ({},{},{})\n" "lamp.color = ({},{},{})\n"
"lamp_obj = bpy.data.objects.new(lamp.name, lamp)\n" "lamp_obj = bpy.data.objects.new(lamp.name, lamp)\n"
@ -48,7 +48,7 @@ void ReadBabeDeadLightToBlender(hecl::blender::PyOutStream& os, const BabeDeadLi
break; break;
case BabeDeadLight::LightType::Spot: case BabeDeadLight::LightType::Spot:
case BabeDeadLight::LightType::Spot2: case BabeDeadLight::LightType::Spot2:
os.format(fmt( os.format(FMT_STRING(
"lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'SPOT')\n" "lamp = bpy.data.lights.new('LAMP_{:01d}_{:03d}', 'SPOT')\n"
"lamp.color = ({},{},{})\n" "lamp.color = ({},{},{})\n"
"lamp.spot_size = {:.6g}\n" "lamp.spot_size = {:.6g}\n"
@ -66,7 +66,7 @@ void ReadBabeDeadLightToBlender(hecl::blender::PyOutStream& os, const BabeDeadLi
return; return;
} }
os.format(fmt( os.format(FMT_STRING(
"lamp.retro_layer = {}\n" "lamp.retro_layer = {}\n"
"lamp.retro_origtype = {}\n" "lamp.retro_origtype = {}\n"
"lamp.falloff_type = 'INVERSE_COEFFICIENTS'\n" "lamp.falloff_type = 'INVERSE_COEFFICIENTS'\n"
@ -90,17 +90,17 @@ void ReadBabeDeadLightToBlender(hecl::blender::PyOutStream& os, const BabeDeadLi
os << "falloff_node.inputs[0].default_value *= 150.0\n" os << "falloff_node.inputs[0].default_value *= 150.0\n"
"lamp.node_tree.links.new(falloff_node.outputs[2], lamp.node_tree.nodes['Emission'].inputs[1])\n"; "lamp.node_tree.links.new(falloff_node.outputs[2], lamp.node_tree.nodes['Emission'].inputs[1])\n";
if (light.q > FLT_EPSILON) if (light.q > FLT_EPSILON)
os.format(fmt("lamp.constant_coefficient = 2.0 / {}\n"), light.q); os.format(FMT_STRING("lamp.constant_coefficient = 2.0 / {}\n"), light.q);
break; break;
case BabeDeadLight::Falloff::Linear: case BabeDeadLight::Falloff::Linear:
os << "lamp.node_tree.links.new(falloff_node.outputs[1], lamp.node_tree.nodes['Emission'].inputs[1])\n"; os << "lamp.node_tree.links.new(falloff_node.outputs[1], lamp.node_tree.nodes['Emission'].inputs[1])\n";
if (light.q > FLT_EPSILON) if (light.q > FLT_EPSILON)
os.format(fmt("lamp.linear_coefficient = 250 / {}\n"), light.q); os.format(FMT_STRING("lamp.linear_coefficient = 250 / {}\n"), light.q);
break; break;
case BabeDeadLight::Falloff::Quadratic: case BabeDeadLight::Falloff::Quadratic:
os << "lamp.node_tree.links.new(falloff_node.outputs[0], lamp.node_tree.nodes['Emission'].inputs[1])\n"; os << "lamp.node_tree.links.new(falloff_node.outputs[0], lamp.node_tree.nodes['Emission'].inputs[1])\n";
if (light.q > FLT_EPSILON) if (light.q > FLT_EPSILON)
os.format(fmt("lamp.quadratic_coefficient = 25000 / {}\n"), light.q); os.format(FMT_STRING("lamp.quadratic_coefficient = 25000 / {}\n"), light.q);
break; break;
default: default:
break; break;

View File

@ -84,7 +84,7 @@ void ReadMaterialSetToBlender_1_2(hecl::blender::PyOutStream& os, const Material
} }
hecl::SystemString resPath = pakRouter.getResourceRelativePath(entry, tex); hecl::SystemString resPath = pakRouter.getResourceRelativePath(entry, tex);
hecl::SystemUTF8Conv resPathView(resPath); hecl::SystemUTF8Conv resPathView(resPath);
os.format(fmt( os.format(FMT_STRING(
"if '{}' in bpy.data.images:\n" "if '{}' in bpy.data.images:\n"
" image = bpy.data.images['{}']\n" " image = bpy.data.images['{}']\n"
"else:\n" "else:\n"
@ -148,7 +148,7 @@ public:
for (const std::pair<atUint16, std::vector<std::pair<atInt16, atUint16>>>& ev : m_extraVerts) { for (const std::pair<atUint16, std::vector<std::pair<atInt16, atUint16>>>& ev : m_extraVerts) {
for (const std::pair<atInt16, atUint16>& se : ev.second) { for (const std::pair<atInt16, atUint16>& se : ev.second) {
if (se.second == nextVert) { if (se.second == nextVert) {
os.format(fmt( os.format(FMT_STRING(
"bm.verts.ensure_lookup_table()\n" "bm.verts.ensure_lookup_table()\n"
"orig_vert = bm.verts[{}]\n" "orig_vert = bm.verts[{}]\n"
"vert = bm.verts.new(orig_vert.co)\n"), "vert = bm.verts.new(orig_vert.co)\n"),
@ -466,7 +466,7 @@ void InitGeomBlenderContext(hecl::blender::PyOutStream& os, const hecl::ProjectP
"\n"; "\n";
/* Link master shader library */ /* Link master shader library */
os.format(fmt( os.format(FMT_STRING(
"# Master shader library\n" "# Master shader library\n"
"with bpy.data.libraries.load('{}', link=True, relative=True) as (data_from, data_to):\n" "with bpy.data.libraries.load('{}', link=True, relative=True) as (data_from, data_to):\n"
" data_to.node_groups = data_from.node_groups\n" " data_to.node_groups = data_from.node_groups\n"
@ -485,13 +485,13 @@ void FinishBlenderMesh(hecl::blender::PyOutStream& os, unsigned matSetCount, int
"obj = bpy.data.objects.new(mesh.name, mesh)\n" "obj = bpy.data.objects.new(mesh.name, mesh)\n"
"obj.show_transparent = True\n" "obj.show_transparent = True\n"
"coll.objects.link(obj)\n"; "coll.objects.link(obj)\n";
os.format(fmt("mesh.hecl_material_count = {}\n"), matSetCount); os.format(FMT_STRING("mesh.hecl_material_count = {}\n"), matSetCount);
} else { } else {
os.format(fmt("mesh = bpy.data.meshes.new(bpy.context.scene.name + '_{:03d}')\n"), meshIdx); os.format(FMT_STRING("mesh = bpy.data.meshes.new(bpy.context.scene.name + '_{:03d}')\n"), meshIdx);
os << "obj = bpy.data.objects.new(mesh.name, mesh)\n" os << "obj = bpy.data.objects.new(mesh.name, mesh)\n"
"obj.show_transparent = True\n" "obj.show_transparent = True\n"
"coll.objects.link(obj)\n"; "coll.objects.link(obj)\n";
os.format(fmt("mesh.hecl_material_count = {}\n"), matSetCount); os.format(FMT_STRING("mesh.hecl_material_count = {}\n"), matSetCount);
} }
os << "mesh.use_auto_smooth = True\n" os << "mesh.use_auto_smooth = True\n"
@ -521,7 +521,10 @@ void FinishBlenderMesh(hecl::blender::PyOutStream& os, unsigned matSetCount, int
" use_vert_dict = vert_dict[0]\n" " use_vert_dict = vert_dict[0]\n"
" merge_verts = [use_vert_dict[fv[od_entry['bm'].verts.layers.int['CMDLOriginalPosIdxs']]] for fv in " " merge_verts = [use_vert_dict[fv[od_entry['bm'].verts.layers.int['CMDLOriginalPosIdxs']]] for fv in "
"face.verts]\n" "face.verts]\n"
" if bm.faces.get(merge_verts) is not None:\n" " try:\n"
" if bm.faces.get(merge_verts) is not None:\n"
" continue\n"
" except:\n"
" continue\n" " continue\n"
" merge_face = bm.faces.new(merge_verts)\n" " merge_face = bm.faces.new(merge_verts)\n"
" for i in range(len(face.loops)):\n" " for i in range(len(face.loops)):\n"
@ -598,7 +601,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
DLReader::DLPrimVert maxIdxs; DLReader::DLPrimVert maxIdxs;
std::vector<atInt16> skinIndices; std::vector<atInt16> skinIndices;
DLReader::ExtraVertTracker extraTracker; DLReader::ExtraVertTracker extraTracker;
for (size_t s = 0; s < lastDlSec; ++s) { for (atUint32 s = 0; s < lastDlSec; ++s) {
atUint64 secStart = reader.position(); atUint64 secStart = reader.position();
if (s < matSecCount) { if (s < matSecCount) {
if (!s) { if (!s) {
@ -680,7 +683,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
unsigned createdUVLayers = 0; unsigned createdUVLayers = 0;
unsigned surfIdx = 0; unsigned surfIdx = 0;
for (size_t s = 0; s < lastDlSec; ++s) { for (atUint32 s = 0; s < lastDlSec; ++s) {
atUint64 secStart = reader.position(); atUint64 secStart = reader.position();
if (s < matSecCount) { if (s < matSecCount) {
MaterialSet matSet; MaterialSet matSet;
@ -695,10 +698,10 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
atUint32 vertCount = maxIdxs.pos + 1; atUint32 vertCount = maxIdxs.pos + 1;
std::vector<atVec3f> positions; std::vector<atVec3f> positions;
positions.reserve(vertCount); positions.reserve(vertCount);
for (size_t i = 0; i <= maxIdxs.pos; ++i) { for (atUint16 i = 0; i <= maxIdxs.pos; ++i) {
positions.push_back(reader.readVec3fBig()); positions.push_back(reader.readVec3fBig());
const atVec3f& pos = positions.back(); const atVec3f& pos = positions.back();
os.format(fmt("vert = bm.verts.new(({},{},{}))\n"), pos.simd[0], pos.simd[1], pos.simd[2]); os.format(FMT_STRING("vert = bm.verts.new(({},{},{}))\n"), pos.simd[0], pos.simd[1], pos.simd[2]);
if (rp.first.second) { if (rp.first.second) {
if (SurfaceHeader::UseMatrixSkinning() && !skinIndices.empty()) if (SurfaceHeader::UseMatrixSkinning() && !skinIndices.empty())
rp.first.second->weightVertex(os, *rp.second.second, skinIndices[i]); rp.first.second->weightVertex(os, *rp.second.second, skinIndices[i]);
@ -708,10 +711,10 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
} }
if (rp.first.second && SurfaceHeader::UseMatrixSkinning() && !skinIndices.empty()) if (rp.first.second && SurfaceHeader::UseMatrixSkinning() && !skinIndices.empty())
vertCount += extraTracker.sendAdditionalVertsToBlender(os, rp, 0); vertCount += extraTracker.sendAdditionalVertsToBlender(os, rp, 0);
os.format(fmt("two_face_vert = {}\n"), vertCount); os.format(FMT_STRING("two_face_vert = {}\n"), vertCount);
for (size_t i = 0; i <= maxIdxs.pos; ++i) { for (atUint16 i = 0; i <= maxIdxs.pos; ++i) {
const atVec3f& pos = positions[i]; const atVec3f& pos = positions[i];
os.format(fmt("vert = bm.verts.new(({},{},{}))\n"), pos.simd[0], pos.simd[1], pos.simd[2]); os.format(FMT_STRING("vert = bm.verts.new(({},{},{}))\n"), pos.simd[0], pos.simd[1], pos.simd[2]);
if (rp.first.second) { if (rp.first.second) {
if (SurfaceHeader::UseMatrixSkinning() && !skinIndices.empty()) if (SurfaceHeader::UseMatrixSkinning() && !skinIndices.empty())
rp.first.second->weightVertex(os, *rp.second.second, skinIndices[i]); rp.first.second->weightVertex(os, *rp.second.second, skinIndices[i]);
@ -727,18 +730,18 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
/* Normals */ /* Normals */
os << "norm_list = []\n"; os << "norm_list = []\n";
if (shortNormals) { if (shortNormals) {
size_t normCount = secSizes[s] / 6; atUint32 normCount = secSizes[s] / 6;
for (size_t i = 0; i < normCount; ++i) { for (atUint32 i = 0; i < normCount; ++i) {
float x = reader.readInt16Big() / 16384.0f; float x = reader.readInt16Big() / 16384.0f;
float y = reader.readInt16Big() / 16384.0f; float y = reader.readInt16Big() / 16384.0f;
float z = reader.readInt16Big() / 16384.0f; float z = reader.readInt16Big() / 16384.0f;
os.format(fmt("norm_list.append(({},{},{}))\n"), x, y, z); os.format(FMT_STRING("norm_list.append(({},{},{}))\n"), x, y, z);
} }
} else { } else {
size_t normCount = secSizes[s] / 12; atUint32 normCount = secSizes[s] / 12;
for (size_t i = 0; i < normCount; ++i) { for (atUint32 i = 0; i < normCount; ++i) {
const atVec3f norm = reader.readVec3fBig(); const atVec3f norm = reader.readVec3fBig();
os.format(fmt("norm_list.append(({},{},{}))\n"), norm.simd[0], norm.simd[1], norm.simd[2]); os.format(FMT_STRING("norm_list.append(({},{},{}))\n"), norm.simd[0], norm.simd[1], norm.simd[2]);
} }
} }
break; break;
@ -750,10 +753,10 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
case 3: { case 3: {
/* Float UVs */ /* Float UVs */
os << "uv_list = []\n"; os << "uv_list = []\n";
size_t uvCount = secSizes[s] / 8; atUint32 uvCount = secSizes[s] / 8;
for (size_t i = 0; i < uvCount; ++i) { for (atUint32 i = 0; i < uvCount; ++i) {
const atVec2f uv = reader.readVec2fBig(); const atVec2f uv = reader.readVec2fBig();
os.format(fmt("uv_list.append(({},{}))\n"), uv.simd[0], uv.simd[1]); os.format(FMT_STRING("uv_list.append(({},{}))\n"), uv.simd[0], uv.simd[1]);
} }
break; break;
} }
@ -765,11 +768,11 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
/* Short UVs */ /* Short UVs */
os << "suv_list = []\n"; os << "suv_list = []\n";
if (shortUVs) { if (shortUVs) {
size_t uvCount = secSizes[s] / 4; atUint32 uvCount = secSizes[s] / 4;
for (size_t i = 0; i < uvCount; ++i) { for (atUint32 i = 0; i < uvCount; ++i) {
float x = reader.readInt16Big() / 32768.0f; float x = reader.readInt16Big() / 32768.0f;
float y = reader.readInt16Big() / 32768.0f; float y = reader.readInt16Big() / 32768.0f;
os.format(fmt("suv_list.append(({},{}))\n"), x, y); os.format(FMT_STRING("suv_list.append(({},{}))\n"), x, y);
} }
break; break;
} }
@ -796,10 +799,10 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
if (SurfaceHeader::UseMatrixSkinning() && rp.first.second) if (SurfaceHeader::UseMatrixSkinning() && rp.first.second)
bankIn = rp.first.second->getMatrixBank(sHead.skinMatrixBankIdx()); bankIn = rp.first.second->getMatrixBank(sHead.skinMatrixBankIdx());
os.format(fmt("materials[{}].pass_index = {}\n"), sHead.matIdx, surfIdx++); os.format(FMT_STRING("materials[{}].pass_index = {}\n"), sHead.matIdx, surfIdx++);
if (matUVCount > createdUVLayers) { if (matUVCount > createdUVLayers) {
for (unsigned l = createdUVLayers; l < matUVCount; ++l) for (unsigned l = createdUVLayers; l < matUVCount; ++l)
os.format(fmt("bm.loops.layers.uv.new('UV_{}')\n"), l); os.format(FMT_STRING("bm.loops.layers.uv.new('UV_{}')\n"), l);
createdUVLayers = matUVCount; createdUVLayers = matUVCount;
} }
@ -821,7 +824,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
atUint8 flip = 0; atUint8 flip = 0;
for (int v = 0; v < vertCount - 2; ++v) { for (int v = 0; v < vertCount - 2; ++v) {
if (flip) { if (flip) {
os.format(fmt( os.format(FMT_STRING(
"last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, " "last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, "
"two_face_vert)\n"), "two_face_vert)\n"),
primVerts[c % 3].pos, primVerts[(c + 2) % 3].pos, primVerts[(c + 1) % 3].pos, primVerts[c % 3].norm, primVerts[c % 3].pos, primVerts[(c + 2) % 3].pos, primVerts[(c + 1) % 3].pos, primVerts[c % 3].norm,
@ -830,7 +833,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
os << "if last_face is not None:\n"; os << "if last_face is not None:\n";
for (unsigned j = 0; j < matUVCount; ++j) { for (unsigned j = 0; j < matUVCount; ++j) {
if (j == 0 && matShortUVs) if (j == 0 && matShortUVs)
os.format(fmt( os.format(FMT_STRING(
" uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], " " uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], "
"suv_list[{}])\n" "suv_list[{}])\n"
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
@ -842,7 +845,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
primVerts[c % 3].uvs[j], primVerts[(c + 2) % 3].uvs[j], primVerts[(c + 1) % 3].uvs[j], primVerts[c % 3].uvs[j], primVerts[(c + 2) % 3].uvs[j], primVerts[(c + 1) % 3].uvs[j],
primVerts[c % 3].pos, j, primVerts[(c + 2) % 3].pos, j, primVerts[(c + 1) % 3].pos, j); primVerts[c % 3].pos, j, primVerts[(c + 2) % 3].pos, j, primVerts[(c + 1) % 3].pos, j);
else else
os.format(fmt( os.format(FMT_STRING(
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
"uv_list[{}]\n" "uv_list[{}]\n"
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
@ -854,7 +857,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
} }
} }
} else { } else {
os.format(fmt( os.format(FMT_STRING(
"last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, " "last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, "
"two_face_vert)\n"), "two_face_vert)\n"),
primVerts[c % 3].pos, primVerts[(c + 1) % 3].pos, primVerts[(c + 2) % 3].pos, primVerts[c % 3].norm, primVerts[c % 3].pos, primVerts[(c + 1) % 3].pos, primVerts[(c + 2) % 3].pos, primVerts[c % 3].norm,
@ -863,7 +866,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
os << "if last_face is not None:\n"; os << "if last_face is not None:\n";
for (unsigned j = 0; j < matUVCount; ++j) { for (unsigned j = 0; j < matUVCount; ++j) {
if (j == 0 && matShortUVs) if (j == 0 && matShortUVs)
os.format(fmt( os.format(FMT_STRING(
" uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], " " uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], "
"suv_list[{}])\n" "suv_list[{}])\n"
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
@ -875,7 +878,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
primVerts[c % 3].uvs[j], primVerts[(c + 1) % 3].uvs[j], primVerts[(c + 2) % 3].uvs[j], primVerts[c % 3].uvs[j], primVerts[(c + 1) % 3].uvs[j], primVerts[(c + 2) % 3].uvs[j],
primVerts[c % 3].pos, j, primVerts[(c + 1) % 3].pos, j, primVerts[(c + 2) % 3].pos, j); primVerts[c % 3].pos, j, primVerts[(c + 1) % 3].pos, j, primVerts[(c + 2) % 3].pos, j);
else else
os.format(fmt( os.format(FMT_STRING(
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
"uv_list[{}]\n" "uv_list[{}]\n"
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
@ -898,7 +901,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
} else if (ptype == GX::TRIANGLES) { } else if (ptype == GX::TRIANGLES) {
for (int v = 0; v < vertCount; v += 3) { for (int v = 0; v < vertCount; v += 3) {
os.format(fmt( os.format(FMT_STRING(
"last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, " "last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, "
"two_face_vert)\n"), "two_face_vert)\n"),
primVerts[0].pos, primVerts[1].pos, primVerts[2].pos, primVerts[0].norm, primVerts[1].norm, primVerts[0].pos, primVerts[1].pos, primVerts[2].pos, primVerts[0].norm, primVerts[1].norm,
@ -907,7 +910,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
os << "if last_face is not None:\n"; os << "if last_face is not None:\n";
for (unsigned j = 0; j < matUVCount; ++j) { for (unsigned j = 0; j < matUVCount; ++j) {
if (j == 0 && matShortUVs) if (j == 0 && matShortUVs)
os.format(fmt( os.format(FMT_STRING(
" uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], " " uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], "
"suv_list[{}])\n" "suv_list[{}])\n"
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
@ -919,7 +922,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
primVerts[0].uvs[j], primVerts[1].uvs[j], primVerts[2].uvs[j], primVerts[0].pos, j, primVerts[0].uvs[j], primVerts[1].uvs[j], primVerts[2].uvs[j], primVerts[0].pos, j,
primVerts[1].pos, j, primVerts[2].pos, j); primVerts[1].pos, j, primVerts[2].pos, j);
else else
os.format(fmt( os.format(FMT_STRING(
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
"uv_list[{}]\n" "uv_list[{}]\n"
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
@ -942,7 +945,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
} else if (ptype == GX::TRIANGLEFAN) { } else if (ptype == GX::TRIANGLEFAN) {
++c; ++c;
for (int v = 0; v < vertCount - 2; ++v) { for (int v = 0; v < vertCount - 2; ++v) {
os.format(fmt( os.format(FMT_STRING(
"last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, " "last_face, last_mesh = add_triangle(bm, bm.verts, ({},{},{}), norm_list, ({},{},{}), {}, od_list, "
"two_face_vert)\n"), "two_face_vert)\n"),
firstPrimVert.pos, primVerts[c % 3].pos, primVerts[(c + 1) % 3].pos, firstPrimVert.norm, firstPrimVert.pos, primVerts[c % 3].pos, primVerts[(c + 1) % 3].pos, firstPrimVert.norm,
@ -951,7 +954,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
os << "if last_face is not None:\n"; os << "if last_face is not None:\n";
for (unsigned j = 0; j < matUVCount; ++j) { for (unsigned j = 0; j < matUVCount; ++j) {
if (j == 0 && matShortUVs) if (j == 0 && matShortUVs)
os.format(fmt( os.format(FMT_STRING(
" uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], " " uv_tri = expand_lightmap_triangle(lightmap_tri_tracker, suv_list[{}], suv_list[{}], "
"suv_list[{}])\n" "suv_list[{}])\n"
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
@ -963,7 +966,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
firstPrimVert.uvs[j], primVerts[c % 3].uvs[j], primVerts[(c + 1) % 3].uvs[j], firstPrimVert.pos, firstPrimVert.uvs[j], primVerts[c % 3].uvs[j], primVerts[(c + 1) % 3].uvs[j], firstPrimVert.pos,
j, primVerts[c % 3].pos, j, primVerts[(c + 1) % 3].pos, j); j, primVerts[c % 3].pos, j, primVerts[(c + 1) % 3].pos, j);
else else
os.format(fmt( os.format(FMT_STRING(
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
"uv_list[{}]\n" "uv_list[{}]\n"
" loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = " " loop_from_facevert(last_mesh, last_face, {})[last_mesh.loops.layers.uv[{}]].uv = "
@ -999,7 +1002,7 @@ atUint32 ReadGeomSectionsToBlender(hecl::blender::PyOutStream& os, athena::io::I
FinishBlenderMesh(os, matSetCount, meshIdx); FinishBlenderMesh(os, matSetCount, meshIdx);
if (rp.first.second) { if (rp.first.second) {
os.format(fmt("mesh.cskr_id = '{}'\n"), rp.first.first); os.format(FMT_STRING("mesh.cskr_id = '{}'\n"), rp.first.first);
rp.second.second->sendVertexGroupsToBlender(os); rp.second.second->sendVertexGroupsToBlender(os);
} }
@ -1013,18 +1016,18 @@ bool ReadCMDLToBlender(hecl::blender::Connection& conn, athena::io::IStreamReade
head.read(reader); head.read(reader);
if (head.magic != 0xDEADBABE) { if (head.magic != 0xDEADBABE) {
LogDNACommon.report(logvisor::Error, fmt("invalid CMDL magic")); LogDNACommon.report(logvisor::Error, FMT_STRING("invalid CMDL magic"));
return false; return false;
} }
if (head.version != Version) { if (head.version != Version) {
LogDNACommon.report(logvisor::Error, fmt("invalid CMDL version")); LogDNACommon.report(logvisor::Error, FMT_STRING("invalid CMDL version"));
return false; return false;
} }
/* Open Py Stream and read sections */ /* Open Py Stream and read sections */
hecl::blender::PyOutStream os = conn.beginPythonOut(true); hecl::blender::PyOutStream os = conn.beginPythonOut(true);
os.format(fmt( os.format(FMT_STRING(
"import bpy\n" "import bpy\n"
"import bmesh\n" "import bmesh\n"
"\n" "\n"
@ -1075,14 +1078,14 @@ void NameCMDL(athena::io::IStreamReader& reader, PAKRouter& pakRouter, typename
const SpecBase& dataspec) { const SpecBase& dataspec) {
Header head; Header head;
head.read(reader); head.read(reader);
std::string bestName = fmt::format(fmt("CMDL_{}"), entry.id); std::string bestName = fmt::format(FMT_STRING("CMDL_{}"), entry.id);
/* Pre-read pass to determine maximum used vert indices */ /* Pre-read pass to determine maximum used vert indices */
atUint32 matSecCount = 0; atUint32 matSecCount = 0;
if (head.matSetCount) if (head.matSetCount)
matSecCount = MaterialSet::OneSection() ? 1 : head.matSetCount; matSecCount = MaterialSet::OneSection() ? 1 : head.matSetCount;
atUint32 lastDlSec = head.secCount; atUint32 lastDlSec = head.secCount;
for (size_t s = 0; s < lastDlSec; ++s) { for (atUint32 s = 0; s < lastDlSec; ++s) {
atUint64 secStart = reader.position(); atUint64 secStart = reader.position();
if (s < matSecCount) { if (s < matSecCount) {
MaterialSet matSet; MaterialSet matSet;
@ -1149,7 +1152,7 @@ bool WriteCMDL(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath
size_t endOff = 0; size_t endOff = 0;
for (const Material& mat : mset) { for (const Material& mat : mset) {
std::string diagName = fmt::format(fmt("{}:{}"), inPath.getLastComponentUTF8(), mat.name); std::string diagName = fmt::format(FMT_STRING("{}:{}"), inPath.getLastComponentUTF8(), mat.name);
hecl::Frontend::IR matIR = FE.compileSource(mat.source, diagName); hecl::Frontend::IR matIR = FE.compileSource(mat.source, diagName);
setBackends.emplace_back(); setBackends.emplace_back();
hecl::Backend::GX& matGX = setBackends.back(); hecl::Backend::GX& matGX = setBackends.back();
@ -1242,7 +1245,7 @@ bool WriteCMDL(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath
for (const Mesh::Surface& surf : mesh.surfaces) { for (const Mesh::Surface& surf : mesh.surfaces) {
size_t vertSz = matSets.at(0).materials.at(surf.materialIdx).getVAFlags().vertDLSize(); size_t vertSz = matSets.at(0).materials.at(surf.materialIdx).getVAFlags().vertDLSize();
if (surf.verts.size() > 65536) if (surf.verts.size() > 65536)
LogDNACommon.report(logvisor::Fatal, fmt("GX DisplayList overflow")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("GX DisplayList overflow"));
size_t secSz = 64; size_t secSz = 64;
for (auto it = surf.verts.cbegin(); it != surf.verts.cend();) { for (auto it = surf.verts.cbegin(); it != surf.verts.cend();) {
atUint16 vertCount = 0; atUint16 vertCount = 0;
@ -1336,7 +1339,7 @@ bool WriteCMDL(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath
else if (mesh.topology == hecl::HMDLTopology::TriStrips) else if (mesh.topology == hecl::HMDLTopology::TriStrips)
prim = GX::TRIANGLESTRIP; prim = GX::TRIANGLESTRIP;
else else
LogDNACommon.report(logvisor::Fatal, fmt("unrecognized mesh output mode")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("unrecognized mesh output mode"));
auto surfSizeIt = head.secSizes.begin() + firstSurfSec; auto surfSizeIt = head.secSizes.begin() + firstSurfSec;
for (const Mesh::Surface& surf : mesh.surfaces) { for (const Mesh::Surface& surf : mesh.surfaces) {
const typename MaterialSet::Material::VAFlags& vaFlags = matSets.at(0).materials.at(surf.materialIdx).getVAFlags(); const typename MaterialSet::Material::VAFlags& vaFlags = matSets.at(0).materials.at(surf.materialIdx).getVAFlags();
@ -1784,7 +1787,7 @@ bool WriteMREASecs(std::vector<std::vector<uint8_t>>& secsOut, const hecl::Proje
else if (mesh.topology == hecl::HMDLTopology::TriStrips) else if (mesh.topology == hecl::HMDLTopology::TriStrips)
prim = GX::TRIANGLESTRIP; prim = GX::TRIANGLESTRIP;
else else
LogDNACommon.report(logvisor::Fatal, fmt("unrecognized mesh output mode")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("unrecognized mesh output mode"));
auto surfEndOffIt = surfEndOffs.begin(); auto surfEndOffIt = surfEndOffs.begin();
size_t lastEndOff = 0; size_t lastEndOff = 0;
for (const Mesh::Surface& surf : mesh.surfaces) { for (const Mesh::Surface& surf : mesh.surfaces) {

View File

@ -4,10 +4,12 @@ make_dnalist(CMDL
FSM2 FSM2
MAPA MAPA
MAPU MAPU
PATH
MayaSpline MayaSpline
EGMC EGMC
SAVWCommon SAVWCommon
ParticleCommon ParticleCommon
URDEVersionInfo
Tweaks/ITweakPlayerGun) Tweaks/ITweakPlayerGun)
set(DNACOMMON_SOURCES set(DNACOMMON_SOURCES
@ -19,6 +21,7 @@ set(DNACOMMON_SOURCES
CMDL.cpp CMDL.cpp
MAPA.cpp MAPA.cpp
MAPU.cpp MAPU.cpp
PATH.hpp PATH.cpp
STRG.hpp STRG.cpp STRG.hpp STRG.cpp
TXTR.hpp TXTR.cpp TXTR.hpp TXTR.cpp
ANCS.hpp ANCS.cpp ANCS.hpp ANCS.cpp
@ -38,6 +41,7 @@ set(DNACOMMON_SOURCES
RigInverter.hpp RigInverter.cpp RigInverter.hpp RigInverter.cpp
AROTBuilder.hpp AROTBuilder.cpp AROTBuilder.hpp AROTBuilder.cpp
OBBTreeBuilder.hpp OBBTreeBuilder.cpp OBBTreeBuilder.hpp OBBTreeBuilder.cpp
URDEVersionInfo.hpp
Tweaks/ITweak.hpp Tweaks/ITweak.hpp
Tweaks/TweakWriter.hpp Tweaks/TweakWriter.hpp
Tweaks/ITweakGame.hpp Tweaks/ITweakGame.hpp

View File

@ -26,11 +26,11 @@ hecl::ProjectPath UniqueIDBridge::TranslatePakIdToPath(const IDType& id, bool si
if (!project) { if (!project) {
if (pakRouter) { if (pakRouter) {
if (hecl::VerbosityLevel >= 1 && !silenceWarnings && id.isValid()) if (hecl::VerbosityLevel >= 1 && !silenceWarnings && id.isValid())
LogDNACommon.report(logvisor::Warning, fmt("unable to translate {} to path"), id); LogDNACommon.report(logvisor::Warning, FMT_STRING("unable to translate {} to path"), id);
return {}; return {};
} }
LogDNACommon.report(logvisor::Fatal, LogDNACommon.report(logvisor::Fatal,
fmt("g_PakRouter or s_Project must be set to non-null before " FMT_STRING("g_PakRouter or s_Project must be set to non-null before "
"calling UniqueIDBridge::TranslatePakIdToPath")); "calling UniqueIDBridge::TranslatePakIdToPath"));
return {}; return {};
} }
@ -38,7 +38,7 @@ hecl::ProjectPath UniqueIDBridge::TranslatePakIdToPath(const IDType& id, bool si
const hecl::ProjectPath* search = project->lookupBridgePath(id.toUint64()); const hecl::ProjectPath* search = project->lookupBridgePath(id.toUint64());
if (!search) { if (!search) {
if (hecl::VerbosityLevel >= 1 && !silenceWarnings && id.isValid()) if (hecl::VerbosityLevel >= 1 && !silenceWarnings && id.isValid())
LogDNACommon.report(logvisor::Warning, fmt("unable to translate {} to path"), id); LogDNACommon.report(logvisor::Warning, FMT_STRING("unable to translate {} to path"), id);
return {}; return {};
} }
return *search; return *search;
@ -53,7 +53,7 @@ hecl::ProjectPath UniqueIDBridge::MakePathFromString(std::string_view str) {
return {}; return {};
hecl::Database::Project* project = s_Project.get(); hecl::Database::Project* project = s_Project.get();
if (!project) if (!project)
LogDNACommon.report(logvisor::Fatal, fmt("UniqueIDBridge::setGlobalProject must be called before MakePathFromString")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("UniqueIDBridge::setGlobalProject must be called before MakePathFromString"));
hecl::ProjectPath path = hecl::ProjectPath(*project, str); hecl::ProjectPath path = hecl::ProjectPath(*project, str);
project->addBridgePathToCache(IDType(path).toUint64(), path); project->addBridgePathToCache(IDType(path).toUint64(), path);
return path; return path;
@ -91,7 +91,7 @@ void UniqueID32::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s)
} }
std::string UniqueID32::toString() const { std::string UniqueID32::toString() const {
return fmt::format(fmt("{}"), *this); return fmt::format(FMT_STRING("{}"), *this);
} }
template <> template <>
@ -143,7 +143,7 @@ void UniqueID64::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s)
} }
std::string UniqueID64::toString() const { std::string UniqueID64::toString() const {
return fmt::format(fmt("{}"), *this); return fmt::format(FMT_STRING("{}"), *this);
} }
/** PAK 128-bit Unique ID */ /** PAK 128-bit Unique ID */
@ -176,7 +176,7 @@ void UniqueID128::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s)
} }
std::string UniqueID128::toString() const { std::string UniqueID128::toString() const {
return fmt::format(fmt("{}"), *this); return fmt::format(FMT_STRING("{}"), *this);
} }
/** Word Bitmap reader/writer */ /** Word Bitmap reader/writer */

View File

@ -79,15 +79,15 @@ public:
PAKRouterBase(const SpecBase& dataSpec) : m_dataSpec(dataSpec) {} PAKRouterBase(const SpecBase& dataSpec) : m_dataSpec(dataSpec) {}
hecl::Database::Project& getProject() const { return m_dataSpec.getProject(); } hecl::Database::Project& getProject() const { return m_dataSpec.getProject(); }
virtual hecl::ProjectPath getWorking(const UniqueID32&, bool silenceWarnings = false) const { virtual hecl::ProjectPath getWorking(const UniqueID32&, bool silenceWarnings = false) const {
LogDNACommon.report(logvisor::Fatal, fmt("PAKRouter IDType mismatch; expected UniqueID32 specialization")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter IDType mismatch; expected UniqueID32 specialization"));
return hecl::ProjectPath(); return hecl::ProjectPath();
} }
virtual hecl::ProjectPath getWorking(const UniqueID64&, bool silenceWarnings = false) const { virtual hecl::ProjectPath getWorking(const UniqueID64&, bool silenceWarnings = false) const {
LogDNACommon.report(logvisor::Fatal, fmt("PAKRouter IDType mismatch; expected UniqueID64 specialization")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter IDType mismatch; expected UniqueID64 specialization"));
return hecl::ProjectPath(); return hecl::ProjectPath();
} }
virtual hecl::ProjectPath getWorking(const UniqueID128&, bool silenceWarnings = false) const { virtual hecl::ProjectPath getWorking(const UniqueID128&, bool silenceWarnings = false) const {
LogDNACommon.report(logvisor::Fatal, fmt("PAKRouter IDType mismatch; expected UniqueID128 specialization")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter IDType mismatch; expected UniqueID128 specialization"));
return hecl::ProjectPath(); return hecl::ProjectPath();
} }
}; };

View File

@ -22,7 +22,7 @@ void DeafBabeSendToBlender(hecl::blender::PyOutStream& os, const DEAFBABE& db, b
"col_bm = bmesh.new()\n"; "col_bm = bmesh.new()\n";
for (const atVec3f& vert : db.verts) { for (const atVec3f& vert : db.verts) {
zeus::simd_floats f(vert.simd); zeus::simd_floats f(vert.simd);
os.format(fmt("col_bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]); os.format(FMT_STRING("col_bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]);
} }
os << "col_bm.verts.ensure_lookup_table()\n"; os << "col_bm.verts.ensure_lookup_table()\n";
@ -49,11 +49,11 @@ void DeafBabeSendToBlender(hecl::blender::PyOutStream& os, const DEAFBABE& db, b
} }
os << "tri_verts = []\n"; os << "tri_verts = []\n";
os.format(fmt("tri_verts.append(col_bm.verts[{}])\n"), vindices[0]); os.format(FMT_STRING("tri_verts.append(col_bm.verts[{}])\n"), vindices[0]);
os.format(fmt("tri_verts.append(col_bm.verts[{}])\n"), vindices[1]); os.format(FMT_STRING("tri_verts.append(col_bm.verts[{}])\n"), vindices[1]);
os.format(fmt("tri_verts.append(col_bm.verts[{}])\n"), vindices[2]); os.format(FMT_STRING("tri_verts.append(col_bm.verts[{}])\n"), vindices[2]);
os.format(fmt( os.format(FMT_STRING(
"face = col_bm.faces.get(tri_verts)\n" "face = col_bm.faces.get(tri_verts)\n"
"if face is None:\n" "if face is None:\n"
" face = col_bm.faces.new(tri_verts)\n" " face = col_bm.faces.new(tri_verts)\n"
@ -72,7 +72,7 @@ void DeafBabeSendToBlender(hecl::blender::PyOutStream& os, const DEAFBABE& db, b
db.insertNoClimb(os); db.insertNoClimb(os);
if (isDcln) if (isDcln)
os.format(fmt("col_mesh = bpy.data.meshes.new('CMESH_{}')\n"), idx); os.format(FMT_STRING("col_mesh = bpy.data.meshes.new('CMESH_{}')\n"), idx);
else else
os << "col_mesh = bpy.data.meshes.new('CMESH')\n"; os << "col_mesh = bpy.data.meshes.new('CMESH')\n";

View File

@ -13,7 +13,7 @@ void FONT<IDType>::_read(athena::io::IStreamReader& __dna_reader) {
DNAFourCC magic; DNAFourCC magic;
magic.read(__dna_reader); magic.read(__dna_reader);
if (magic != SBIG('FONT')) { if (magic != SBIG('FONT')) {
LogModule.report(logvisor::Fatal, fmt("Invalid FONT magic '{}'"), magic); LogModule.report(logvisor::Fatal, FMT_STRING("Invalid FONT magic '{}'"), magic);
return; return;
} }
/* version */ /* version */

View File

@ -16,7 +16,7 @@ template <class Op>
void FSM2<IDType>::Enumerate(typename Op::StreamT& s) { void FSM2<IDType>::Enumerate(typename Op::StreamT& s) {
Do<Op>(athena::io::PropId{"header"}, header, s); Do<Op>(athena::io::PropId{"header"}, header, s);
if (header.magic != SBIG('FSM2')) { if (header.magic != SBIG('FSM2')) {
LogDNAFSM2.report(logvisor::Fatal, fmt("Invalid FSM2 magic '{}' expected 'FSM2'"), header.magic); LogDNAFSM2.report(logvisor::Fatal, FMT_STRING("Invalid FSM2 magic '{}' expected 'FSM2'"), header.magic);
return; return;
} }
@ -29,7 +29,7 @@ void FSM2<IDType>::Enumerate(typename Op::StreamT& s) {
detail.reset(new FSMV2); detail.reset(new FSMV2);
Do<Op>(athena::io::PropId{"detail"}, static_cast<FSMV2&>(*detail), s); Do<Op>(athena::io::PropId{"detail"}, static_cast<FSMV2&>(*detail), s);
} else { } else {
LogDNAFSM2.report(logvisor::Fatal, fmt("Invalid FSM2 version '{}'"), header.version); LogDNAFSM2.report(logvisor::Fatal, FMT_STRING("Invalid FSM2 version '{}'"), header.version);
return; return;
} }
} }

View File

@ -21,7 +21,7 @@ void MAPA::Enumerate<BigDNA::Read>(typename Read::StreamT& __dna_reader) {
/* magic */ /* magic */
magic = __dna_reader.readUint32Big(); magic = __dna_reader.readUint32Big();
if (magic != 0xDEADD00D) { if (magic != 0xDEADD00D) {
LogDNACommon.report(logvisor::Error, fmt("invalid MAPA magic")); LogDNACommon.report(logvisor::Error, FMT_STRING("invalid MAPA magic"));
return; return;
} }
/* version */ /* version */
@ -33,7 +33,7 @@ void MAPA::Enumerate<BigDNA::Read>(typename Read::StreamT& __dna_reader) {
else if (version == 5) else if (version == 5)
header = std::make_unique<HeaderMP3>(); header = std::make_unique<HeaderMP3>();
else { else {
LogDNACommon.report(logvisor::Error, fmt("invalid MAPA version")); LogDNACommon.report(logvisor::Error, FMT_STRING("invalid MAPA version"));
return; return;
} }
@ -146,7 +146,7 @@ bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const
" edge.seam = True\n" " edge.seam = True\n"
"\n"; "\n";
os.format(fmt( os.format(FMT_STRING(
"bpy.context.scene.name = 'MAPA_{}'\n" "bpy.context.scene.name = 'MAPA_{}'\n"
"bpy.context.scene.retro_map_vis_mode = '{}'\n"), "bpy.context.scene.retro_map_vis_mode = '{}'\n"),
entry.id, RetroMapVisModes[mapa.header->visMode()]); entry.id, RetroMapVisModes[mapa.header->visMode()]);
@ -159,7 +159,7 @@ bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const
zeus::simd_floats mtxF[3]; zeus::simd_floats mtxF[3];
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
moMP12->transformMtx[i].simd.copy_to(mtxF[i]); moMP12->transformMtx[i].simd.copy_to(mtxF[i]);
os.format(fmt( os.format(FMT_STRING(
"obj = bpy.data.objects.new('MAPOBJ_{:02d}', None)\n" "obj = bpy.data.objects.new('MAPOBJ_{:02d}', None)\n"
"bpy.context.scene.collection.objects.link(obj)\n" "bpy.context.scene.collection.objects.link(obj)\n"
"obj.retro_mappable_type = {}\n" "obj.retro_mappable_type = {}\n"
@ -181,7 +181,7 @@ bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const
zeus::simd_floats mtxF[3]; zeus::simd_floats mtxF[3];
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
moMP3->transformMtx[i].simd.copy_to(mtxF[i]); moMP3->transformMtx[i].simd.copy_to(mtxF[i]);
os.format(fmt( os.format(FMT_STRING(
"obj = bpy.data.objects.new('MAPOBJ_{:02d}', None)\n" "obj = bpy.data.objects.new('MAPOBJ_{:02d}', None)\n"
"bpy.context.scene.collection.objects.link(obj)\n" "bpy.context.scene.collection.objects.link(obj)\n"
"obj.retro_mappable_type = {}\n" "obj.retro_mappable_type = {}\n"
@ -208,7 +208,7 @@ bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const
/* Read in verts */ /* Read in verts */
for (const atVec3f& vert : mapa.vertices) { for (const atVec3f& vert : mapa.vertices) {
zeus::simd_floats f(vert.simd); zeus::simd_floats f(vert.simd);
os.format(fmt("bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]); os.format(FMT_STRING("bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]);
} }
os << "bm.verts.ensure_lookup_table()\n"; os << "bm.verts.ensure_lookup_table()\n";
@ -225,10 +225,10 @@ bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const
atUint8 flip = 0; atUint8 flip = 0;
for (size_t v = 0; v < prim.indexCount - 2; ++v) { for (size_t v = 0; v < prim.indexCount - 2; ++v) {
if (flip) { if (flip) {
os.format(fmt("add_triangle(bm, ({},{},{}))\n"), primVerts[c % 3], primVerts[(c + 2) % 3], os.format(FMT_STRING("add_triangle(bm, ({},{},{}))\n"), primVerts[c % 3], primVerts[(c + 2) % 3],
primVerts[(c + 1) % 3]); primVerts[(c + 1) % 3]);
} else { } else {
os.format(fmt("add_triangle(bm, ({},{},{}))\n"), primVerts[c % 3], primVerts[(c + 1) % 3], os.format(FMT_STRING("add_triangle(bm, ({},{},{}))\n"), primVerts[c % 3], primVerts[(c + 1) % 3],
primVerts[(c + 2) % 3]); primVerts[(c + 2) % 3]);
} }
flip ^= 1; flip ^= 1;
@ -248,7 +248,7 @@ bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const
} }
} else if (GX::Primitive(prim.type) == GX::TRIANGLES) { } else if (GX::Primitive(prim.type) == GX::TRIANGLES) {
for (size_t v = 0; v < prim.indexCount; v += 3) { for (size_t v = 0; v < prim.indexCount; v += 3) {
os.format(fmt("add_triangle(bm, ({},{},{}))\n"), primVerts[0], primVerts[1], primVerts[2]); os.format(FMT_STRING("add_triangle(bm, ({},{},{}))\n"), primVerts[0], primVerts[1], primVerts[2]);
/* Break if done */ /* Break if done */
if (v + 3 >= prim.indexCount) if (v + 3 >= prim.indexCount)
@ -264,7 +264,7 @@ bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const
for (const typename MAPA::Surface::Border& border : surf.borders) { for (const typename MAPA::Surface::Border& border : surf.borders) {
auto iit = border.indices.cbegin(); auto iit = border.indices.cbegin();
for (size_t i = 0; i < border.indexCount - 1; ++i) { for (size_t i = 0; i < border.indexCount - 1; ++i) {
os.format(fmt("add_border(bm, ({},{}))\n"), *iit, *(iit + 1)); os.format(FMT_STRING("add_border(bm, ({},{}))\n"), *iit, *(iit + 1));
++iit; ++iit;
} }
} }
@ -278,7 +278,7 @@ bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const
const zeus::CMatrix4f* tmpMtx = pakRouter.lookupMAPATransform(entry.id); const zeus::CMatrix4f* tmpMtx = pakRouter.lookupMAPATransform(entry.id);
const zeus::CMatrix4f& mtx = tmpMtx ? *tmpMtx : zeus::skIdentityMatrix4f; const zeus::CMatrix4f& mtx = tmpMtx ? *tmpMtx : zeus::skIdentityMatrix4f;
os.format(fmt( os.format(FMT_STRING(
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n" "mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
"mtxd = mtx.decompose()\n" "mtxd = mtx.decompose()\n"
"obj.rotation_mode = 'QUATERNION'\n" "obj.rotation_mode = 'QUATERNION'\n"
@ -294,7 +294,7 @@ bool ReadMAPAToBlender(hecl::blender::Connection& conn, const MAPA& mapa, const
if (hecl::StringUtils::BeginsWith(ent.m_name, _SYS_STR("!world_")) && if (hecl::StringUtils::BeginsWith(ent.m_name, _SYS_STR("!world_")) &&
hecl::StringUtils::EndsWith(ent.m_name, _SYS_STR(".blend"))) { hecl::StringUtils::EndsWith(ent.m_name, _SYS_STR(".blend"))) {
hecl::SystemUTF8Conv conv(ent.m_name); hecl::SystemUTF8Conv conv(ent.m_name);
os.linkBackground(fmt::format(fmt("//../{}"), conv), "World"sv); os.linkBackground(fmt::format(FMT_STRING("//../{}"), conv), "World"sv);
break; break;
} }
} }
@ -326,7 +326,7 @@ template bool ReadMAPAToBlender<PAKRouter<DNAMP3::PAKBridge>>(hecl::blender::Con
template <typename MAPAType> template <typename MAPAType>
bool Cook(const hecl::blender::MapArea& mapaIn, const hecl::ProjectPath& out) { bool Cook(const hecl::blender::MapArea& mapaIn, const hecl::ProjectPath& out) {
if (mapaIn.verts.size() >= 256) { if (mapaIn.verts.size() >= 256) {
Log.report(logvisor::Error, fmt(_SYS_STR("MAPA {} vertex range exceeded [{}/{}]")), out.getRelativePath(), Log.report(logvisor::Error, FMT_STRING(_SYS_STR("MAPA {} vertex range exceeded [{}/{}]")), out.getRelativePath(),
mapaIn.verts.size(), 255); mapaIn.verts.size(), 255);
return false; return false;
} }

View File

@ -43,7 +43,7 @@ bool ReadMAPUToBlender(hecl::blender::Connection& conn, const MAPU& mapu, const
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
wldXf.xf[i].simd.copy_to(wldXfF[i]); wldXf.xf[i].simd.copy_to(wldXfF[i]);
zeus::simd_floats hexColorF(wld.hexColor.mSimd); zeus::simd_floats hexColorF(wld.hexColor.mSimd);
os.format(fmt( os.format(FMT_STRING(
"wldObj = bpy.data.objects.new('{}', None)\n" "wldObj = bpy.data.objects.new('{}', None)\n"
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n" "mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
"mtxd = mtx.decompose()\n" "mtxd = mtx.decompose()\n"
@ -62,7 +62,7 @@ bool ReadMAPUToBlender(hecl::blender::Connection& conn, const MAPU& mapu, const
zeus::simd_floats hexXfF[3]; zeus::simd_floats hexXfF[3];
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
hexXf.xf[i].simd.copy_to(hexXfF[i]); hexXf.xf[i].simd.copy_to(hexXfF[i]);
os.format(fmt( os.format(FMT_STRING(
"obj = bpy.data.objects.new('{}_{}', hexMesh)\n" "obj = bpy.data.objects.new('{}_{}', hexMesh)\n"
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n" "mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
"mtxd = mtx.decompose()\n" "mtxd = mtx.decompose()\n"

View File

@ -41,7 +41,7 @@ bool ReadMLVLToBlender(hecl::blender::Connection& conn, const MLVL& mlvl, const
zeus::simd_floats xfMtxF[3]; zeus::simd_floats xfMtxF[3];
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
area.transformMtx[i].simd.copy_to(xfMtxF[i]); area.transformMtx[i].simd.copy_to(xfMtxF[i]);
os.format(fmt( os.format(FMT_STRING(
"box_mesh = bpy.data.meshes.new('''{}''')\n" "box_mesh = bpy.data.meshes.new('''{}''')\n"
"bm.to_mesh(box_mesh)\n" "bm.to_mesh(box_mesh)\n"
"bm.free()\n" "bm.free()\n"
@ -67,7 +67,7 @@ bool ReadMLVLToBlender(hecl::blender::Connection& conn, const MLVL& mlvl, const
int idx = 0; int idx = 0;
for (const atVec3f& pv : dock.planeVerts) { for (const atVec3f& pv : dock.planeVerts) {
const zeus::CVector3f pvRel = zeus::CVector3f(pv) - pvAvg; const zeus::CVector3f pvRel = zeus::CVector3f(pv) - pvAvg;
os.format(fmt( os.format(FMT_STRING(
"bm.verts.new(({},{},{}))\n" "bm.verts.new(({},{},{}))\n"
"bm.verts.ensure_lookup_table()\n"), "bm.verts.ensure_lookup_table()\n"),
pvRel[0], pvRel[1], pvRel[2]); pvRel[0], pvRel[1], pvRel[2]);
@ -76,13 +76,13 @@ bool ReadMLVLToBlender(hecl::blender::Connection& conn, const MLVL& mlvl, const
++idx; ++idx;
} }
os << "bm.edges.new((bm.verts[-1], bm.verts[0]))\n"; os << "bm.edges.new((bm.verts[-1], bm.verts[0]))\n";
os.format(fmt("dockMesh = bpy.data.meshes.new('DOCK_{:02d}_{:02d}')\n"), areaIdx, dockIdx); os.format(FMT_STRING("dockMesh = bpy.data.meshes.new('DOCK_{:02d}_{:02d}')\n"), areaIdx, dockIdx);
os << "dockObj = bpy.data.objects.new(dockMesh.name, dockMesh)\n" os << "dockObj = bpy.data.objects.new(dockMesh.name, dockMesh)\n"
"bpy.context.scene.collection.objects.link(dockObj)\n" "bpy.context.scene.collection.objects.link(dockObj)\n"
"bm.to_mesh(dockMesh)\n" "bm.to_mesh(dockMesh)\n"
"bm.free()\n" "bm.free()\n"
"dockObj.parent = box\n"; "dockObj.parent = box\n";
os.format(fmt("dockObj.location = ({},{},{})\n"), float(pvAvg[0]), float(pvAvg[1]), float(pvAvg[2])); os.format(FMT_STRING("dockObj.location = ({},{},{})\n"), float(pvAvg[0]), float(pvAvg[1]), float(pvAvg[2]));
++dockIdx; ++dockIdx;
} }
++areaIdx; ++areaIdx;

View File

@ -14,6 +14,8 @@ struct MayaSpline : public BigDNA {
Value<float> amplitude; Value<float> amplitude;
Value<atUint8> unk1; Value<atUint8> unk1;
Value<atUint8> unk2; Value<atUint8> unk2;
Vector<atVec2f, AT_DNA_COUNT(unk1 == 5)> unk1Floats;
Vector<atVec2f, AT_DNA_COUNT(unk2 == 5)> unk2Floats;
}; };
Vector<Knot, AT_DNA_COUNT(knotCount)> knots; Vector<Knot, AT_DNA_COUNT(knotCount)> knots;

View File

@ -207,7 +207,7 @@ void PAKRouter<BRIDGETYPE>::enterPAKBridge(const BRIDGETYPE& pakBridge) {
++pit; ++pit;
++bridgeIdx; ++bridgeIdx;
} }
LogDNACommon.report(logvisor::Fatal, fmt("PAKBridge provided to PAKRouter::enterPAKBridge() was not part of build()")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKBridge provided to PAKRouter::enterPAKBridge() was not part of build()"));
} }
template <class BRIDGETYPE> template <class BRIDGETYPE>
@ -287,7 +287,7 @@ hecl::ProjectPath PAKRouter<BRIDGETYPE>::getWorking(const EntryType* entry,
return sharedPath.ensureAuxInfo(auxInfo); return sharedPath.ensureAuxInfo(auxInfo);
} }
LogDNACommon.report(logvisor::Fatal, fmt("Unable to find entry {}"), entry->id); LogDNACommon.report(logvisor::Fatal, FMT_STRING("Unable to find entry {}"), entry->id);
return hecl::ProjectPath(); return hecl::ProjectPath();
} }
@ -338,7 +338,7 @@ hecl::ProjectPath PAKRouter<BRIDGETYPE>::getCooked(const EntryType* entry) const
if (sharedSearch != m_sharedEntries.end()) { if (sharedSearch != m_sharedEntries.end()) {
return hecl::ProjectPath(m_sharedCooked, getBestEntryName(*entry)); return hecl::ProjectPath(m_sharedCooked, getBestEntryName(*entry));
} }
LogDNACommon.report(logvisor::Fatal, fmt("Unable to find entry {}"), entry->id); LogDNACommon.report(logvisor::Fatal, FMT_STRING("Unable to find entry {}"), entry->id);
return hecl::ProjectPath(); return hecl::ProjectPath();
} }
@ -353,7 +353,7 @@ hecl::SystemString PAKRouter<BRIDGETYPE>::getResourceRelativePath(const EntryTyp
const PAKType* pak = m_pak.get(); const PAKType* pak = m_pak.get();
if (!pak) if (!pak)
LogDNACommon.report(logvisor::Fatal, LogDNACommon.report(logvisor::Fatal,
fmt("PAKRouter::enterPAKBridge() must be called before PAKRouter::getResourceRelativePath()")); FMT_STRING("PAKRouter::enterPAKBridge() must be called before PAKRouter::getResourceRelativePath()"));
const typename BRIDGETYPE::PAKType::Entry* be = lookupEntry(b); const typename BRIDGETYPE::PAKType::Entry* be = lookupEntry(b);
if (!be) if (!be)
return hecl::SystemString(); return hecl::SystemString();
@ -377,17 +377,17 @@ std::string PAKRouter<BRIDGETYPE>::getBestEntryName(const EntryType& entry, bool
if (stdOverride && !pak.m_noShare) { if (stdOverride && !pak.m_noShare) {
if (entry.type == FOURCC('MLVL')) if (entry.type == FOURCC('MLVL'))
return fmt::format(fmt("!world_{}"), entry.id); return fmt::format(FMT_STRING("!world_{}"), entry.id);
else if (entry.type == FOURCC('MREA')) else if (entry.type == FOURCC('MREA'))
return fmt::format(fmt("!area_{}"), entry.id); return fmt::format(FMT_STRING("!area_{}"), entry.id);
else if (entry.type == FOURCC('MAPA')) else if (entry.type == FOURCC('MAPA'))
return fmt::format(fmt("!map_{}"), entry.id); return fmt::format(FMT_STRING("!map_{}"), entry.id);
else if (entry.type == FOURCC('PATH')) else if (entry.type == FOURCC('PATH'))
return fmt::format(fmt("!path_{}"), entry.id); return fmt::format(FMT_STRING("!path_{}"), entry.id);
else if (entry.type == FOURCC('MAPW')) else if (entry.type == FOURCC('MAPW'))
return fmt::format(fmt("!mapw_{}"), entry.id); return fmt::format(FMT_STRING("!mapw_{}"), entry.id);
else if (entry.type == FOURCC('SAVW')) else if (entry.type == FOURCC('SAVW'))
return fmt::format(fmt("!savw_{}"), entry.id); return fmt::format(FMT_STRING("!savw_{}"), entry.id);
} }
std::string catalogueName; std::string catalogueName;
@ -409,17 +409,17 @@ std::string PAKRouter<BRIDGETYPE>::getBestEntryName(const IDType& entry, bool st
if (stdOverride && !pak.m_noShare) { if (stdOverride && !pak.m_noShare) {
if (e->type == FOURCC('MLVL')) if (e->type == FOURCC('MLVL'))
return fmt::format(fmt("!world_{}"), e->id); return fmt::format(FMT_STRING("!world_{}"), e->id);
else if (e->type == FOURCC('MREA')) else if (e->type == FOURCC('MREA'))
return fmt::format(fmt("!area_{}"), e->id); return fmt::format(FMT_STRING("!area_{}"), e->id);
else if (e->type == FOURCC('MAPA')) else if (e->type == FOURCC('MAPA'))
return fmt::format(fmt("!map_{}"), e->id); return fmt::format(FMT_STRING("!map_{}"), e->id);
else if (e->type == FOURCC('PATH')) else if (e->type == FOURCC('PATH'))
return fmt::format(fmt("!path_{}"), e->id); return fmt::format(FMT_STRING("!path_{}"), e->id);
else if (e->type == FOURCC('MAPW')) else if (e->type == FOURCC('MAPW'))
return fmt::format(fmt("!mapw_{}"), e->id); return fmt::format(FMT_STRING("!mapw_{}"), e->id);
else if (e->type == FOURCC('SAVW')) else if (e->type == FOURCC('SAVW'))
return fmt::format(fmt("!savw_{}"), e->id); return fmt::format(FMT_STRING("!savw_{}"), e->id);
} }
std::string catalogueName; std::string catalogueName;
@ -495,7 +495,7 @@ const typename BRIDGETYPE::PAKType::Entry* PAKRouter<BRIDGETYPE>::lookupEntry(co
return nullptr; return nullptr;
if (!m_bridges) if (!m_bridges)
LogDNACommon.report(logvisor::Fatal, fmt("PAKRouter::build() must be called before PAKRouter::lookupEntry()")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter::build() must be called before PAKRouter::lookupEntry()"));
const PAKType* pak = m_pak.get(); const PAKType* pak = m_pak.get();
const nod::Node* node = m_node.get(); const nod::Node* node = m_node.get();
@ -511,7 +511,7 @@ const typename BRIDGETYPE::PAKType::Entry* PAKRouter<BRIDGETYPE>::lookupEntry(co
if (currentPAK) { if (currentPAK) {
#ifndef NDEBUG #ifndef NDEBUG
if (!silenceWarnings) if (!silenceWarnings)
LogDNACommon.report(logvisor::Warning, fmt("unable to find PAK entry {} in current PAK"), entry); LogDNACommon.report(logvisor::Warning, FMT_STRING("unable to find PAK entry {} in current PAK"), entry);
#endif #endif
return nullptr; return nullptr;
} }
@ -528,7 +528,7 @@ const typename BRIDGETYPE::PAKType::Entry* PAKRouter<BRIDGETYPE>::lookupEntry(co
#ifndef NDEBUG #ifndef NDEBUG
if (!silenceWarnings) if (!silenceWarnings)
LogDNACommon.report(logvisor::Warning, fmt("unable to find PAK entry {}"), entry); LogDNACommon.report(logvisor::Warning, FMT_STRING("unable to find PAK entry {}"), entry);
#endif #endif
if (nodeOut) if (nodeOut)
*nodeOut = nullptr; *nodeOut = nullptr;
@ -561,7 +561,7 @@ const zeus::CMatrix4f* PAKRouter<BRIDGETYPE>::lookupMAPATransform(const IDType&
template <class BRIDGETYPE> template <class BRIDGETYPE>
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerWorking(const IDType& areaId, int layerIdx) const { hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerWorking(const IDType& areaId, int layerIdx) const {
if (!m_bridges) if (!m_bridges)
LogDNACommon.report(logvisor::Fatal, fmt("PAKRouter::build() must be called before PAKRouter::getAreaLayerWorking()")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter::build() must be called before PAKRouter::getAreaLayerWorking()"));
auto bridgePathIt = m_bridgePaths.cbegin(); auto bridgePathIt = m_bridgePaths.cbegin();
for (const BRIDGETYPE& bridge : *m_bridges) { for (const BRIDGETYPE& bridge : *m_bridges) {
for (const auto& level : bridge.m_levelDeps) for (const auto& level : bridge.m_levelDeps)
@ -583,7 +583,7 @@ hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerWorking(const IDType& areaI
bool& activeOut) const { bool& activeOut) const {
activeOut = false; activeOut = false;
if (!m_bridges) if (!m_bridges)
LogDNACommon.report(logvisor::Fatal, fmt("PAKRouter::build() must be called before PAKRouter::getAreaLayerWorking()")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter::build() must be called before PAKRouter::getAreaLayerWorking()"));
auto bridgePathIt = m_bridgePaths.cbegin(); auto bridgePathIt = m_bridgePaths.cbegin();
for (const BRIDGETYPE& bridge : *m_bridges) { for (const BRIDGETYPE& bridge : *m_bridges) {
for (const auto& level : bridge.m_levelDeps) for (const auto& level : bridge.m_levelDeps)
@ -605,7 +605,7 @@ hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerWorking(const IDType& areaI
template <class BRIDGETYPE> template <class BRIDGETYPE>
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerCooked(const IDType& areaId, int layerIdx) const { hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerCooked(const IDType& areaId, int layerIdx) const {
if (!m_bridges) if (!m_bridges)
LogDNACommon.report(logvisor::Fatal, fmt("PAKRouter::build() must be called before PAKRouter::getAreaLayerCooked()")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter::build() must be called before PAKRouter::getAreaLayerCooked()"));
auto bridgePathIt = m_bridgePaths.cbegin(); auto bridgePathIt = m_bridgePaths.cbegin();
for (const BRIDGETYPE& bridge : *m_bridges) { for (const BRIDGETYPE& bridge : *m_bridges) {
for (const auto& level : bridge.m_levelDeps) for (const auto& level : bridge.m_levelDeps)
@ -626,7 +626,7 @@ template <class BRIDGETYPE>
hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerCooked(const IDType& areaId, int layerIdx, bool& activeOut) const { hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerCooked(const IDType& areaId, int layerIdx, bool& activeOut) const {
activeOut = false; activeOut = false;
if (!m_bridges) if (!m_bridges)
LogDNACommon.report(logvisor::Fatal, fmt("PAKRouter::build() must be called before PAKRouter::getAreaLayerCooked()")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter::build() must be called before PAKRouter::getAreaLayerCooked()"));
auto bridgePathIt = m_bridgePaths.cbegin(); auto bridgePathIt = m_bridgePaths.cbegin();
for (const BRIDGETYPE& bridge : *m_bridges) { for (const BRIDGETYPE& bridge : *m_bridges) {
for (const auto& level : bridge.m_levelDeps) for (const auto& level : bridge.m_levelDeps)
@ -648,7 +648,7 @@ hecl::ProjectPath PAKRouter<BRIDGETYPE>::getAreaLayerCooked(const IDType& areaId
template <class BRIDGETYPE> template <class BRIDGETYPE>
void PAKRouter<BRIDGETYPE>::enumerateResources(const std::function<bool(const EntryType*)>& func) { void PAKRouter<BRIDGETYPE>::enumerateResources(const std::function<bool(const EntryType*)>& func) {
if (!m_bridges) if (!m_bridges)
LogDNACommon.report(logvisor::Fatal, fmt("PAKRouter::build() must be called before PAKRouter::enumerateResources()")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAKRouter::build() must be called before PAKRouter::enumerateResources()"));
for (const auto& entryPair : m_uniqueEntries) for (const auto& entryPair : m_uniqueEntries)
if (!func(entryPair.second.second)) if (!func(entryPair.second.second))
return; return;
@ -662,7 +662,7 @@ bool PAKRouter<BRIDGETYPE>::mreaHasDupeResources(const IDType& id) const {
const PAKType* pak = m_pak.get(); const PAKType* pak = m_pak.get();
if (!pak) if (!pak)
LogDNACommon.report(logvisor::Fatal, LogDNACommon.report(logvisor::Fatal,
fmt("PAKRouter::enterPAKBridge() must be called before PAKRouter::mreaHasDupeResources()")); FMT_STRING("PAKRouter::enterPAKBridge() must be called before PAKRouter::mreaHasDupeResources()"));
return pak->mreaHasDupeResources(id); return pak->mreaHasDupeResources(id);
} }

View File

@ -31,7 +31,7 @@ public:
PAKEntryReadStream(std::unique_ptr<atUint8[]>&& buf, atUint64 sz, atUint64 pos) PAKEntryReadStream(std::unique_ptr<atUint8[]>&& buf, atUint64 sz, atUint64 pos)
: m_buf(std::move(buf)), m_sz(sz), m_pos(pos) { : m_buf(std::move(buf)), m_sz(sz), m_pos(pos) {
if (m_pos >= m_sz) if (m_pos >= m_sz)
LogDNACommon.report(logvisor::Fatal, fmt("PAK stream cursor overrun")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAK stream cursor overrun"));
} }
void seek(atInt64 pos, athena::SeekOrigin origin) override { void seek(atInt64 pos, athena::SeekOrigin origin) override {
if (origin == athena::SeekOrigin::Begin) { if (origin == athena::SeekOrigin::Begin) {
@ -42,7 +42,7 @@ public:
m_pos = m_sz + pos; m_pos = m_sz + pos;
} }
if (m_pos > m_sz) { if (m_pos > m_sz) {
LogDNACommon.report(logvisor::Fatal, fmt("PAK stream cursor overrun")); LogDNACommon.report(logvisor::Fatal, FMT_STRING("PAK stream cursor overrun"));
} }
} }
atUint64 position() const override { return m_pos; } atUint64 position() const override { return m_pos; }

248
DataSpec/DNACommon/PATH.cpp Normal file
View File

@ -0,0 +1,248 @@
#include "PATH.hpp"
#include "hecl/Blender/Connection.hpp"
#include "zeus/CAABox.hpp"
#include "DataSpec/DNACommon/AROTBuilder.hpp"
namespace DataSpec::DNAPATH {
#define DUMP_OCTREE 0
#if DUMP_OCTREE
/* octree dumper */
static void OutputOctreeNode(hecl::blender::PyOutStream& os, int idx, const zeus::CAABox& aabb) {
const zeus::CVector3f pos = aabb.center();
const zeus::CVector3f extent = aabb.extents();
os.format(
"obj = bpy.data.objects.new('Leaf_%d', None)\n"
"bpy.context.scene.collection.objects.link(obj)\n"
"obj.location = (%f,%f,%f)\n"
"obj.scale = (%f,%f,%f)\n"
"obj.empty_display_type = 'CUBE'\n"
"obj.layers[1] = True\n"
"obj.layers[0] = False\n",
idx, pos.x(), pos.y(), pos.z(), extent.x(), extent.y(), extent.z());
}
#endif
template <class PAKBridge>
void PATH<PAKBridge>::sendToBlender(hecl::blender::Connection& conn, std::string_view entryName,
const zeus::CMatrix4f* xf, const std::string& areaPath) {
/* Open Py Stream and read sections */
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
os << "import bpy\n"
"import bmesh\n"
"from mathutils import Vector, Matrix\n"
"\n"
"bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask')\n"
"bpy.types.Material.retro_path_type_mask = bpy.props.IntProperty(name='Retro: Path Type Mask')\n"
"\n"
"material_dict = {}\n"
"material_index = []\n"
"def make_ground_material(idxMask):\n"
" mat = bpy.data.materials.new('Ground %X' % idxMask)\n"
" mat.diffuse_color = (0.8, 0.460, 0.194, 1.0)\n"
" return mat\n"
"def make_flyer_material(idxMask):\n"
" mat = bpy.data.materials.new('Flyer %X' % idxMask)\n"
" mat.diffuse_color = (0.016, 0.8, 0.8, 1.0)\n"
" return mat\n"
"def make_swimmer_material(idxMask):\n"
" mat = bpy.data.materials.new('Swimmer %X' % idxMask)\n"
" mat.diffuse_color = (0.074, 0.293, 0.8, 1.0)\n"
" return mat\n"
"def select_material(meshIdxMask, meshTypeMask):\n"
" key = (meshIdxMask, meshTypeMask)\n"
" if key in material_index:\n"
" return material_index.index(key)\n"
" elif key in material_dict:\n"
" material_index.append(key)\n"
" return len(material_index)-1\n"
" else:\n"
" if meshTypeMask == 0x2:\n"
" mat = make_flyer_material(meshIdxMask)\n"
" elif meshTypeMask == 0x4:\n"
" mat = make_swimmer_material(meshIdxMask)\n"
" else:\n"
" mat = make_ground_material(meshIdxMask)\n"
" mat.retro_path_idx_mask = meshIdxMask\n"
" mat.retro_path_type_mask = meshTypeMask\n"
" material_dict[key] = mat\n"
" material_index.append(key)\n"
" return len(material_index)-1\n"
"\n";
os.format(FMT_STRING("bpy.context.scene.name = '{}'\n"), entryName);
os << "# Clear Scene\n"
"if len(bpy.data.collections):\n"
" bpy.data.collections.remove(bpy.data.collections[0])\n"
"\n"
"bm = bmesh.new()\n"
"height_lay = bm.faces.layers.float.new('Height')\n";
for (const Node& n : nodes) {
zeus::simd_floats f(n.position.simd);
os.format(FMT_STRING("bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]);
}
os << "bm.verts.ensure_lookup_table()\n";
for (const Region& r : regions) {
os << "tri_verts = []\n";
for (atUint32 i = 0; i < r.nodeCount; ++i)
os.format(FMT_STRING("tri_verts.append(bm.verts[{}])\n"), r.nodeStart + i);
os.format(FMT_STRING("face = bm.faces.get(tri_verts)\n"
"if face is None:\n"
" face = bm.faces.new(tri_verts)\n"
" face.normal_flip()\n"
"face.material_index = select_material(0x{:04X}, 0x{:04X})\n"
"face.smooth = False\n"
"face[height_lay] = {}\n"
"\n"),
r.meshIndexMask, r.meshTypeMask, r.height);
#if 0
const zeus::CVector3f center = xf->multiplyOneOverW(r.centroid);
zeus::CAABox aabb(xf->multiplyOneOverW(r.aabb[0]), xf->multiplyOneOverW(r.aabb[1]));
os.format(FMT_STRING("aabb = bpy.data.objects.new('AABB', None)\n")
"aabb.location = (%f,%f,%f)\n"
"aabb.scale = (%f,%f,%f)\n"
"aabb.empty_display_type = 'CUBE'\n"
"bpy.context.scene.collection.objects.link(aabb)\n"
"centr = bpy.data.objects.new('Center', None)\n"
"centr.location = (%f,%f,%f)\n"
"bpy.context.scene.collection.objects.link(centr)\n",
aabb.min[0] + (aabb.max[0] - aabb.min[0]) / 2.f,
aabb.min[1] + (aabb.max[1] - aabb.min[1]) / 2.f,
aabb.min[2] + (aabb.max[2] - aabb.min[2]) / 2.f,
(aabb.max[0] - aabb.min[0]) / 2.f,
(aabb.max[1] - aabb.min[1]) / 2.f,
(aabb.max[2] - aabb.min[2]) / 2.f,
center.x(), center.y(), center.z());
#endif
}
#if 0
for (const Node& n : nodes) {
zeus::simd_floats f(n.position.simd);
zeus::simd_floats no(n.position.simd + n.normal.simd);
os.format(FMT_STRING("v = bm.verts.new((%f,%f,%f))\n")
"v2 = bm.verts.new((%f,%f,%f))\n"
"bm.edges.new((v, v2))\n", f[0], f[1], f[2], no[0], no[1], no[2]);
}
#endif
os << "bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)\n"
"path_mesh = bpy.data.meshes.new('PATH')\n"
"bm.to_mesh(path_mesh)\n"
"path_mesh_obj = bpy.data.objects.new(path_mesh.name, path_mesh)\n"
"\n"
"for mat_name in material_index:\n"
" mat = material_dict[mat_name]\n"
" path_mesh.materials.append(mat)\n"
"\n"
"bpy.context.scene.collection.objects.link(path_mesh_obj)\n"
"path_mesh_obj.display_type = 'SOLID'\n"
"bpy.context.scene.hecl_path_obj = path_mesh_obj.name\n"
"\n";
if (xf) {
const zeus::CMatrix4f& w = *xf;
zeus::simd_floats xfMtxF[4];
for (int i = 0; i < 4; ++i)
w.m[i].mSimd.copy_to(xfMtxF[i]);
os.format(FMT_STRING("mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
"mtxd = mtx.decompose()\n"
"path_mesh_obj.rotation_mode = 'QUATERNION'\n"
"path_mesh_obj.location = mtxd[0]\n"
"path_mesh_obj.rotation_quaternion = mtxd[1]\n"
"path_mesh_obj.scale = mtxd[2]\n"),
xfMtxF[0][0], xfMtxF[1][0], xfMtxF[2][0], xfMtxF[3][0], xfMtxF[0][1], xfMtxF[1][1], xfMtxF[2][1],
xfMtxF[3][1], xfMtxF[0][2], xfMtxF[1][2], xfMtxF[2][2], xfMtxF[3][2]);
}
#if DUMP_OCTREE
{
int idx = 0;
for (const auto& n : octree) {
if (n.isLeaf)
OutputOctreeNode(os, idx, zeus::CAABox(n.aabb[0], n.aabb[1]));
++idx;
}
}
#endif
os.linkBackground(fmt::format(FMT_STRING("//{}"), areaPath));
os.centerView();
os.close();
}
template <class PAKBridge>
bool PATH<PAKBridge>::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const typename PAKBridge::PAKType::Entry& entry,
bool force, hecl::blender::Token& btok,
std::function<void(const hecl::SystemChar*)> fileChanged) {
PATH path;
path.read(rs);
hecl::blender::Connection& conn = btok.getBlenderConnection();
if (!conn.createBlend(outPath, hecl::blender::BlendType::PathMesh))
return false;
std::string areaPath;
for (const auto& ent : hecl::DirectoryEnumerator(outPath.getParentPath().getAbsolutePath())) {
if (hecl::StringUtils::BeginsWith(ent.m_name, _SYS_STR("!area_"))) {
areaPath = hecl::SystemUTF8Conv(ent.m_name).str();
break;
}
}
const zeus::CMatrix4f* xf = pakRouter.lookupMAPATransform(entry.id);
path.sendToBlender(conn, pakRouter.getBestEntryName(entry, false), xf, areaPath);
return conn.saveBlend();
}
template <class PAKBridge>
bool PATH<PAKBridge>::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const PathMesh& mesh,
hecl::blender::Token& btok) {
athena::io::MemoryReader r(mesh.data.data(), mesh.data.size());
PATH path;
path.read(r);
if (!path.regions.empty()) {
AROTBuilder octreeBuilder;
octreeBuilder.buildPath(path);
} else {
path.octreeNodeCount = 1;
path.octree.emplace_back();
OctreeNode& n = path.octree.back();
n.isLeaf = 1;
n.aabb[0] = zeus::CVector3f{FLT_MAX, FLT_MAX, FLT_MAX};
n.aabb[1] = zeus::CVector3f{-FLT_MAX, -FLT_MAX, -FLT_MAX};
for (int i = 0; i < 8; ++i)
n.children[i] = 0xffffffff;
}
#if DUMP_OCTREE
{
hecl::blender::Connection& conn = btok.getBlenderConnection();
if (!conn.createBlend(inPath.getWithExtension(_SYS_STR(".octree.blend"), true), hecl::blender::BlendType::PathMesh))
return false;
zeus::CMatrix4f xf;
path.sendToBlender(conn, "PATH"sv, &xf);
conn.saveBlend();
}
#endif
athena::io::FileWriter w(outPath.getAbsolutePath());
path.write(w);
int64_t rem = w.position() % 32;
if (rem)
for (int64_t i = 0; i < 32 - rem; ++i)
w.writeUByte(0xff);
return true;
}
template struct PATH<DataSpec::DNAMP1::PAKBridge>;
template struct PATH<DataSpec::DNAMP2::PAKBridge>;
template struct PATH<DataSpec::DNAMP3::PAKBridge>;
} // namespace DataSpec::DNAPATH

112
DataSpec/DNACommon/PATH.hpp Normal file
View File

@ -0,0 +1,112 @@
#pragma once
#include "DataSpec/DNACommon/DNACommon.hpp"
#include "DataSpec/DNACommon/PAK.hpp"
#include "DataSpec/DNAMP1/DNAMP1.hpp"
#include "DataSpec/DNAMP2/DNAMP2.hpp"
#include "DataSpec/DNAMP3/DNAMP3.hpp"
namespace DataSpec::DNAPATH {
template <class PAKBridge>
struct RegionPointers {};
template <>
struct RegionPointers<DataSpec::DNAMP1::PAKBridge> : BigDNA {
AT_DECL_DNA
Value<atUint32> regionIdxPtr;
};
template <>
struct RegionPointers<DataSpec::DNAMP2::PAKBridge> : BigDNA {
AT_DECL_DNA
Value<atUint32> unk0;
Value<atUint32> unk1;
Value<atUint32> unk2;
Value<atUint32> regionIdxPtr;
};
template <>
struct RegionPointers<DataSpec::DNAMP3::PAKBridge> : BigDNA {
AT_DECL_DNA
Value<atUint32> unk0;
Value<atUint32> unk1;
Value<atUint32> unk2;
Value<atUint32> regionIdxPtr;
};
template <class PAKBridge>
struct AT_SPECIALIZE_PARMS(DataSpec::DNAMP1::PAKBridge, DataSpec::DNAMP2::PAKBridge, DataSpec::DNAMP3::PAKBridge) PATH
: BigDNA {
using PathMesh = hecl::blender::PathMesh;
AT_DECL_DNA
Value<atUint32> version;
struct Node : BigDNA {
AT_DECL_DNA
Value<atVec3f> position;
Value<atVec3f> normal;
};
Value<atUint32> nodeCount;
Vector<Node, AT_DNA_COUNT(nodeCount)> nodes;
struct Link : BigDNA {
AT_DECL_DNA
Value<atUint32> nodeIdx;
Value<atUint32> regionIdx;
Value<float> width2d;
Value<float> oneOverWidth2d;
};
Value<atUint32> linkCount;
Vector<Link, AT_DNA_COUNT(linkCount)> links;
struct Region : BigDNA {
AT_DECL_DNA
Value<atUint32> nodeCount;
Value<atUint32> nodeStart;
Value<atUint32> linkCount;
Value<atUint32> linkStart;
Value<atUint16> meshIndexMask;
Value<atUint16> meshTypeMask;
Value<float> height;
Value<atVec3f> normal;
Value<atUint32> regionIdx;
Value<atVec3f> centroid;
Value<atVec3f> aabb[2];
Value<RegionPointers<PAKBridge>> pointers;
};
Value<atUint32> regionCount;
Vector<Region, AT_DNA_COUNT(regionCount)> regions;
Vector<atUint32, AT_DNA_COUNT((((regionCount * (regionCount - 1)) / 2) + 31) / 32)> bitmap1;
Vector<atUint32, AT_DNA_COUNT(bitmap1.size())> bitmap2;
/* Unused in all games, removed in MP3 */
Vector<atUint32, AT_DNA_COUNT(std::is_same_v<PAKBridge, DataSpec::DNAMP3::PAKBridge>
? 0
: (((((regionCount * regionCount) + 31) / 32) - bitmap1.size()) * 2))>
bitmap3;
Value<atUint32> octreeRegionLookupCount;
Vector<atUint32, AT_DNA_COUNT(octreeRegionLookupCount)> octreeRegionLookup;
struct OctreeNode : BigDNA {
AT_DECL_DNA
Value<atUint32> isLeaf;
Value<atVec3f> aabb[2];
Value<atVec3f> centroid;
Value<atUint32> children[8];
Value<atUint32> regionCount;
Value<atUint32> regionStart;
};
Value<atUint32> octreeNodeCount;
Vector<OctreeNode, AT_DNA_COUNT(octreeNodeCount)> octree;
void sendToBlender(hecl::blender::Connection& conn, std::string_view entryName, const zeus::CMatrix4f* xf,
const std::string& areaPath);
static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const typename PAKBridge::PAKType::Entry& entry, bool force,
hecl::blender::Token& btok, std::function<void(const hecl::SystemChar*)> fileChanged);
static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath, const PathMesh& mesh,
hecl::blender::Token& btok);
};
} // namespace DataSpec::DNAPATH

View File

@ -45,7 +45,7 @@ struct PPImpl : BigDNA, _Basis {
constexpr FourCC RefType = uint32_t(_Basis::Type); constexpr FourCC RefType = uint32_t(_Basis::Type);
DNAFourCC clsId(r); DNAFourCC clsId(r);
if (clsId != RefType) { if (clsId != RefType) {
LogModule.report(logvisor::Warning, fmt("non {} provided to {} parser"), RefType, RefType); LogModule.report(logvisor::Warning, FMT_STRING("non {} provided to {} parser"), RefType, RefType);
return; return;
} }
clsId.read(r); clsId.read(r);
@ -68,7 +68,7 @@ struct PPImpl : BigDNA, _Basis {
p.read(r); p.read(r);
} }
})) { })) {
LogModule.report(logvisor::Fatal, fmt("Unknown {} class {} @{}"), RefType, clsId, r.position()); LogModule.report(logvisor::Fatal, FMT_STRING("Unknown {} class {} @{}"), RefType, clsId, r.position());
} }
clsId.read(r); clsId.read(r);
} }
@ -124,7 +124,7 @@ struct PPImpl : BigDNA, _Basis {
if (key == "DNAType"sv) if (key == "DNAType"sv)
continue; continue;
if (key.size() < 4) { if (key.size() < 4) {
LogModule.report(logvisor::Warning, fmt("short FourCC in element '{}'"), key); LogModule.report(logvisor::Warning, FMT_STRING("short FourCC in element '{}'"), key);
continue; continue;
} }
@ -142,7 +142,7 @@ struct PPImpl : BigDNA, _Basis {
p.read(r); p.read(r);
} }
})) { })) {
LogModule.report(logvisor::Fatal, fmt("Unknown {} class {}"), RefType, clsId); LogModule.report(logvisor::Fatal, FMT_STRING("Unknown {} class {}"), RefType, clsId);
} }
} }
} }
@ -201,7 +201,7 @@ struct PEImpl : BigDNA {
m_elem = std::make_unique<typename Tp::Type>(); m_elem = std::make_unique<typename Tp::Type>();
m_elem->read(r); m_elem->read(r);
})) { })) {
LogModule.report(logvisor::Fatal, fmt("Unknown {} class {} @{}"), _PtrType::TypeName, clsId, r.position()); LogModule.report(logvisor::Fatal, FMT_STRING("Unknown {} class {} @{}"), _PtrType::TypeName, clsId, r.position());
} }
} }
@ -229,7 +229,7 @@ struct PEImpl : BigDNA {
const auto& [key, value] = mapChildren[0]; const auto& [key, value] = mapChildren[0];
if (key.size() < 4) if (key.size() < 4)
LogModule.report(logvisor::Fatal, fmt("short FourCC in element '{}'"), key); LogModule.report(logvisor::Fatal, FMT_STRING("short FourCC in element '{}'"), key);
if (auto rec = r.enterSubRecord(key)) { if (auto rec = r.enterSubRecord(key)) {
const DNAFourCC clsId = key.c_str(); const DNAFourCC clsId = key.c_str();
@ -238,7 +238,7 @@ struct PEImpl : BigDNA {
m_elem = std::make_unique<typename Tp::Type>(); m_elem = std::make_unique<typename Tp::Type>();
m_elem->read(r); m_elem->read(r);
})) { })) {
LogModule.report(logvisor::Fatal, fmt("Unknown {} class {}"), _PtrType::TypeName, clsId); LogModule.report(logvisor::Fatal, FMT_STRING("Unknown {} class {}"), _PtrType::TypeName, clsId);
} }
} }
} }
@ -253,7 +253,7 @@ struct PEImpl : BigDNA {
_Basis::gatherDependencies(deps, m_elem); _Basis::gatherDependencies(deps, m_elem);
} }
operator bool() const { return m_elem.operator bool(); } explicit operator bool() const { return m_elem.operator bool(); }
auto* get() const { return m_elem.get(); } auto* get() const { return m_elem.get(); }
auto* operator->() const { return get(); } auto* operator->() const { return get(); }
void reset() { m_elem.reset(); } void reset() { m_elem.reset(); }
@ -565,7 +565,7 @@ struct IUVElement : IElement {
struct BoolHelper : IElement { struct BoolHelper : IElement {
AT_DECL_EXPLICIT_DNA_YAMLV_NO_TYPE AT_DECL_EXPLICIT_DNA_YAMLV_NO_TYPE
bool value = false; bool value = false;
operator bool() const { return value; } explicit operator bool() const { return value; }
BoolHelper& operator=(bool val) { BoolHelper& operator=(bool val) {
value = val; value = val;
return *this; return *this;
@ -610,7 +610,7 @@ struct ValueHelper : BigDNA {
std::optional<Tp> value = {}; std::optional<Tp> value = {};
void emplace(Tp val) { value.emplace(val); } void emplace(Tp val) { value.emplace(val); }
Tp operator*() const { return *value; } Tp operator*() const { return *value; }
operator bool() const { return value.operator bool(); } explicit operator bool() const { return value.operator bool(); }
}; };
struct RELifetimeTween : IRealElement { struct RELifetimeTween : IRealElement {
@ -1333,7 +1333,7 @@ struct SpawnSystemKeyframeData : BigDNA {
AT_DECL_EXPLICIT_DNA_YAML AT_DECL_EXPLICIT_DNA_YAML
AT_SUBDECL_DNA AT_SUBDECL_DNA
operator bool() const { return spawns.size() != 0; } explicit operator bool() const { return spawns.size() != 0; }
void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const { void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const {
for (const auto& p : spawns) for (const auto& p : spawns)
@ -1347,7 +1347,7 @@ struct ChildResourceFactory : BigDNA {
IDType id; IDType id;
AT_DECL_EXPLICIT_DNA_YAML AT_DECL_EXPLICIT_DNA_YAML
AT_SUBDECL_DNA AT_SUBDECL_DNA
operator bool() const { return id.isValid(); } explicit operator bool() const { return id.isValid(); }
void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const { void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const {
if (id.isValid()) if (id.isValid())
g_curSpec->flattenDependencies(id, pathsOut); g_curSpec->flattenDependencies(id, pathsOut);

View File

@ -141,6 +141,5 @@ zeus::CVector3f RigInverter<CINFType>::restorePosition(atUint32 boneId, const ze
template class RigInverter<DNAMP1::CINF>; template class RigInverter<DNAMP1::CINF>;
template class RigInverter<DNAMP2::CINF>; template class RigInverter<DNAMP2::CINF>;
template class RigInverter<DNAMP3::CINF>;
} // namespace DataSpec::DNAANIM } // namespace DataSpec::DNAANIM

View File

@ -13,7 +13,7 @@ void ISTRG::gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const {
std::unique_ptr<ISTRG> LoadSTRG(athena::io::IStreamReader& reader) { std::unique_ptr<ISTRG> LoadSTRG(athena::io::IStreamReader& reader) {
uint32_t magic = reader.readUint32Big(); uint32_t magic = reader.readUint32Big();
if (magic != 0x87654321) { if (magic != 0x87654321) {
LogDNACommon.report(logvisor::Error, fmt("invalid STRG magic")); LogDNACommon.report(logvisor::Error, FMT_STRING("invalid STRG magic"));
return std::unique_ptr<ISTRG>(); return std::unique_ptr<ISTRG>();
} }

View File

@ -826,9 +826,9 @@ static void EncodeCMPR(const uint8_t* rgbaIn, uint8_t* texels, int width, int he
} }
} }
static void PNGErr(png_structp png, png_const_charp msg) { Log.report(logvisor::Error, fmt("{}"), msg); } static void PNGErr(png_structp png, png_const_charp msg) { Log.report(logvisor::Error, FMT_STRING("{}"), msg); }
static void PNGWarn(png_structp png, png_const_charp msg) { Log.report(logvisor::Warning, fmt("{}"), msg); } static void PNGWarn(png_structp png, png_const_charp msg) { Log.report(logvisor::Warning, FMT_STRING("{}"), msg); }
bool TXTR::Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) { bool TXTR::Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
const uint32_t format = rs.readUint32Big(); const uint32_t format = rs.readUint32Big();
@ -838,7 +838,7 @@ bool TXTR::Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
auto fp = hecl::FopenUnique(outPath.getAbsolutePath().data(), _SYS_STR("wb")); auto fp = hecl::FopenUnique(outPath.getAbsolutePath().data(), _SYS_STR("wb"));
if (fp == nullptr) { if (fp == nullptr) {
Log.report(logvisor::Error, fmt(_SYS_STR("Unable to open '{}' for writing")), outPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to open '{}' for writing")), outPath.getAbsolutePath());
return false; return false;
} }
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, PNGErr, PNGWarn); png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, PNGErr, PNGWarn);
@ -1041,7 +1041,7 @@ static int GetNumPaletteEntriesForGCN(png_structp png, png_infop info) {
bool TXTR::Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath) { bool TXTR::Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath) {
auto inf = hecl::FopenUnique(inPath.getAbsolutePath().data(), _SYS_STR("rb")); auto inf = hecl::FopenUnique(inPath.getAbsolutePath().data(), _SYS_STR("rb"));
if (inf == nullptr) { if (inf == nullptr) {
Log.report(logvisor::Error, fmt(_SYS_STR("Unable to open '{}' for reading")), inPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to open '{}' for reading")), inPath.getAbsolutePath());
return false; return false;
} }
@ -1049,25 +1049,25 @@ bool TXTR::Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPat
char header[8]; char header[8];
std::fread(header, 1, sizeof(header), inf.get()); std::fread(header, 1, sizeof(header), inf.get());
if (png_sig_cmp((png_const_bytep)header, 0, 8)) { if (png_sig_cmp((png_const_bytep)header, 0, 8)) {
Log.report(logvisor::Error, fmt(_SYS_STR("invalid PNG signature in '{}'")), inPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("invalid PNG signature in '{}'")), inPath.getAbsolutePath());
return false; return false;
} }
/* Setup PNG reader */ /* Setup PNG reader */
png_structp pngRead = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); png_structp pngRead = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!pngRead) { if (!pngRead) {
Log.report(logvisor::Error, fmt("unable to initialize libpng")); Log.report(logvisor::Error, FMT_STRING("unable to initialize libpng"));
return false; return false;
} }
png_infop info = png_create_info_struct(pngRead); png_infop info = png_create_info_struct(pngRead);
if (!info) { if (!info) {
Log.report(logvisor::Error, fmt("unable to initialize libpng info")); Log.report(logvisor::Error, FMT_STRING("unable to initialize libpng info"));
png_destroy_read_struct(&pngRead, nullptr, nullptr); png_destroy_read_struct(&pngRead, nullptr, nullptr);
return false; return false;
} }
if (setjmp(png_jmpbuf(pngRead))) { if (setjmp(png_jmpbuf(pngRead))) {
Log.report(logvisor::Error, fmt(_SYS_STR("unable to initialize libpng I/O for '{}'")), inPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to initialize libpng I/O for '{}'")), inPath.getAbsolutePath());
png_destroy_read_struct(&pngRead, &info, nullptr); png_destroy_read_struct(&pngRead, &info, nullptr);
return false; return false;
} }
@ -1083,7 +1083,7 @@ bool TXTR::Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPat
const png_byte bitDepth = png_get_bit_depth(pngRead, info); const png_byte bitDepth = png_get_bit_depth(pngRead, info);
if (width < 4 || height < 4) { if (width < 4 || height < 4) {
Log.report(logvisor::Error, fmt("image must be 4x4 or larger")); Log.report(logvisor::Error, FMT_STRING("image must be 4x4 or larger"));
png_destroy_read_struct(&pngRead, &info, nullptr); png_destroy_read_struct(&pngRead, &info, nullptr);
return false; return false;
} }
@ -1112,7 +1112,7 @@ bool TXTR::Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPat
} }
if (bitDepth != 8) { if (bitDepth != 8) {
Log.report(logvisor::Error, fmt(_SYS_STR("'{}' is not 8 bits-per-channel")), inPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("'{}' is not 8 bits-per-channel")), inPath.getAbsolutePath());
png_destroy_read_struct(&pngRead, &info, nullptr); png_destroy_read_struct(&pngRead, &info, nullptr);
return false; return false;
} }
@ -1143,7 +1143,7 @@ bool TXTR::Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPat
nComps = 1; nComps = 1;
break; break;
default: default:
Log.report(logvisor::Error, fmt(_SYS_STR("unsupported color type in '{}'")), inPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unsupported color type in '{}'")), inPath.getAbsolutePath());
png_destroy_read_struct(&pngRead, &info, nullptr); png_destroy_read_struct(&pngRead, &info, nullptr);
return false; return false;
} }
@ -1163,7 +1163,7 @@ bool TXTR::Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPat
bufOut.reset(new uint8_t[bufLen]); bufOut.reset(new uint8_t[bufLen]);
if (setjmp(png_jmpbuf(pngRead))) { if (setjmp(png_jmpbuf(pngRead))) {
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to read image in '{}'")), inPath.getAbsolutePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to read image in '{}'")), inPath.getAbsolutePath());
png_destroy_read_struct(&pngRead, &info, nullptr); png_destroy_read_struct(&pngRead, &info, nullptr);
return false; return false;
} }
@ -1344,7 +1344,7 @@ bool TXTR::Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPat
/* Do write out */ /* Do write out */
athena::io::FileWriter outf(outPath.getAbsolutePath(), true, false); athena::io::FileWriter outf(outPath.getAbsolutePath(), true, false);
if (outf.hasError()) { if (outf.hasError()) {
Log.report(logvisor::Error, fmt(_SYS_STR("Unable to open '{}' for writing")), outPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to open '{}' for writing")), outPath.getAbsolutePath());
return false; return false;
} }
@ -1360,7 +1360,7 @@ bool TXTR::Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPat
bool TXTR::CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath) { bool TXTR::CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath) {
auto inf = hecl::FopenUnique(inPath.getAbsolutePath().data(), _SYS_STR("rb")); auto inf = hecl::FopenUnique(inPath.getAbsolutePath().data(), _SYS_STR("rb"));
if (inf == nullptr) { if (inf == nullptr) {
Log.report(logvisor::Error, fmt(_SYS_STR("Unable to open '{}' for reading")), inPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to open '{}' for reading")), inPath.getAbsolutePath());
return false; return false;
} }
@ -1368,25 +1368,25 @@ bool TXTR::CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outP
char header[8]; char header[8];
std::fread(header, 1, sizeof(header), inf.get()); std::fread(header, 1, sizeof(header), inf.get());
if (png_sig_cmp((png_const_bytep)header, 0, 8)) { if (png_sig_cmp((png_const_bytep)header, 0, 8)) {
Log.report(logvisor::Error, fmt(_SYS_STR("invalid PNG signature in '{}'")), inPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("invalid PNG signature in '{}'")), inPath.getAbsolutePath());
return false; return false;
} }
/* Setup PNG reader */ /* Setup PNG reader */
png_structp pngRead = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); png_structp pngRead = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!pngRead) { if (!pngRead) {
Log.report(logvisor::Error, fmt("unable to initialize libpng")); Log.report(logvisor::Error, FMT_STRING("unable to initialize libpng"));
return false; return false;
} }
png_infop info = png_create_info_struct(pngRead); png_infop info = png_create_info_struct(pngRead);
if (!info) { if (!info) {
Log.report(logvisor::Error, fmt("unable to initialize libpng info")); Log.report(logvisor::Error, FMT_STRING("unable to initialize libpng info"));
png_destroy_read_struct(&pngRead, nullptr, nullptr); png_destroy_read_struct(&pngRead, nullptr, nullptr);
return false; return false;
} }
if (setjmp(png_jmpbuf(pngRead))) { if (setjmp(png_jmpbuf(pngRead))) {
Log.report(logvisor::Error, fmt(_SYS_STR("unable to initialize libpng I/O for '{}'")), inPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to initialize libpng I/O for '{}'")), inPath.getAbsolutePath());
png_destroy_read_struct(&pngRead, &info, nullptr); png_destroy_read_struct(&pngRead, &info, nullptr);
return false; return false;
} }
@ -1425,7 +1425,7 @@ bool TXTR::CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outP
} }
if (bitDepth != 8) { if (bitDepth != 8) {
Log.report(logvisor::Error, fmt(_SYS_STR("'{}' is not 8 bits-per-channel")), inPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("'{}' is not 8 bits-per-channel")), inPath.getAbsolutePath());
png_destroy_read_struct(&pngRead, &info, nullptr); png_destroy_read_struct(&pngRead, &info, nullptr);
return false; return false;
} }
@ -1454,7 +1454,7 @@ bool TXTR::CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outP
paletteBuf = ReadPalette(pngRead, info, paletteSize); paletteBuf = ReadPalette(pngRead, info, paletteSize);
break; break;
default: default:
Log.report(logvisor::Error, fmt(_SYS_STR("unsupported color type in '{}'")), inPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unsupported color type in '{}'")), inPath.getAbsolutePath());
png_destroy_read_struct(&pngRead, &info, nullptr); png_destroy_read_struct(&pngRead, &info, nullptr);
return false; return false;
} }
@ -1472,7 +1472,7 @@ bool TXTR::CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outP
bufOut.reset(new uint8_t[bufLen]); bufOut.reset(new uint8_t[bufLen]);
if (setjmp(png_jmpbuf(pngRead))) { if (setjmp(png_jmpbuf(pngRead))) {
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to read image in '{}'")), inPath.getAbsolutePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to read image in '{}'")), inPath.getAbsolutePath());
png_destroy_read_struct(&pngRead, &info, nullptr); png_destroy_read_struct(&pngRead, &info, nullptr);
return false; return false;
} }
@ -1593,7 +1593,7 @@ bool TXTR::CookPC(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outP
/* Do write out */ /* Do write out */
athena::io::FileWriter outf(outPath.getAbsolutePath(), true, false); athena::io::FileWriter outf(outPath.getAbsolutePath(), true, false);
if (outf.hasError()) { if (outf.hasError()) {
Log.report(logvisor::Error, fmt(_SYS_STR("Unable to open '{}' for writing")), outPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to open '{}' for writing")), outPath.getAbsolutePath());
return false; return false;
} }

View File

@ -21,6 +21,9 @@ struct ITweakPlayerRes : ITweak {
ResId x18_minesBreakSecondTopIcon; ResId x18_minesBreakSecondTopIcon;
ResId x1c_minesBreakSecondBottomIcon; ResId x1c_minesBreakSecondBottomIcon;
ResId rs5_mapArrowUp;
ResId rs5_mapArrowDown;
/* N, U, UL, L, DL, D, DR, R, UR */ /* N, U, UL, L, DL, D, DR, R, UR */
std::array<ResId, 9> x24_lStick; std::array<ResId, 9> x24_lStick;
std::array<ResId, 9> x4c_cStick; std::array<ResId, 9> x4c_cStick;

View File

@ -0,0 +1,24 @@
#pragma once
#include <string>
#include "DataSpec/DNACommon/DNACommon.hpp"
namespace DataSpec {
enum class ERegion { Invalid = -1, NTSC_U = 'E', PAL = 'P', NTSC_J = 'J' };
enum class EGame {
Invalid = 0,
MetroidPrime1,
MetroidPrime2,
MetroidPrime3,
};
struct URDEVersionInfo : BigDNA {
AT_DECL_DNA_YAML
String<-1> version;
Value<ERegion> region;
Value<EGame> game;
Value<bool> isTrilogy;
};
} // namespace DataSpec

View File

@ -975,9 +975,8 @@ bool ANCS::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
} }
if (force || blendType == hecl::ProjectPath::Type::None) { if (force || blendType == hecl::ProjectPath::Type::None) {
hecl::blender::Connection& conn = btok.getBlenderConnection();
DNAANCS::ReadANCSToBlender<PAKRouter<PAKBridge>, ANCS, MaterialSet, DNACMDL::SurfaceHeader_1, 2>( DNAANCS::ReadANCSToBlender<PAKRouter<PAKBridge>, ANCS, MaterialSet, DNACMDL::SurfaceHeader_1, 2>(
conn, ancs, blendPath, pakRouter, entry, dataSpec, fileChanged, force); btok, ancs, blendPath, pakRouter, entry, dataSpec, fileChanged, force);
} }
} }
@ -988,19 +987,19 @@ bool ANCS::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
/* Search for yaml */ /* Search for yaml */
hecl::ProjectPath yamlPath = inPath.getWithExtension(_SYS_STR(".yaml"), true); hecl::ProjectPath yamlPath = inPath.getWithExtension(_SYS_STR(".yaml"), true);
if (!yamlPath.isFile()) if (!yamlPath.isFile())
Log.report(logvisor::Fatal, fmt(_SYS_STR("'{}' not found as file")), yamlPath.getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("'{}' not found as file")), yamlPath.getRelativePath());
athena::io::FileReader reader(yamlPath.getAbsolutePath()); athena::io::FileReader reader(yamlPath.getAbsolutePath());
if (!reader.isOpen()) if (!reader.isOpen())
Log.report(logvisor::Fatal, fmt(_SYS_STR("can't open '{}' for reading")), yamlPath.getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("can't open '{}' for reading")), yamlPath.getRelativePath());
if (!athena::io::ValidateFromYAMLStream<ANCS>(reader)) { if (!athena::io::ValidateFromYAMLStream<ANCS>(reader)) {
Log.report(logvisor::Fatal, fmt(_SYS_STR("'{}' is not urde::DNAMP1::ANCS type")), yamlPath.getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("'{}' is not urde::DNAMP1::ANCS type")), yamlPath.getRelativePath());
} }
athena::io::YAMLDocReader yamlReader; athena::io::YAMLDocReader yamlReader;
if (!yamlReader.parse(&reader)) { if (!yamlReader.parse(&reader)) {
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to parse '{}'")), yamlPath.getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to parse '{}'")), yamlPath.getRelativePath());
} }
ANCS ancs; ANCS ancs;
ancs.read(yamlReader); ancs.read(yamlReader);
@ -1020,9 +1019,9 @@ bool ANCS::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
hecl::SystemStringConv chSysName(ch.name); hecl::SystemStringConv chSysName(ch.name);
if (!sub.cskrId.empty()) { if (!sub.cskrId.empty()) {
hecl::SystemStringConv cskrSysName(sub.cskrId); hecl::SystemStringConv cskrSysName(sub.cskrId);
ch.cskr = inPath.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}_{}.CSKR")), chSysName, cskrSysName)); ch.cskr = inPath.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}_{}.CSKR")), chSysName, cskrSysName));
} else { } else {
ch.cskr = inPath.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}.CSKR")), chSysName)); ch.cskr = inPath.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}.CSKR")), chSysName));
} }
/* Add subtype AABBs */ /* Add subtype AABBs */
@ -1048,10 +1047,10 @@ bool ANCS::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
if (!search->cskrId.empty()) { if (!search->cskrId.empty()) {
hecl::SystemStringConv cskrSys(search->cskrId); hecl::SystemStringConv cskrSys(search->cskrId);
ch.cskrIce = inPath.ensureAuxInfo( ch.cskrIce = inPath.ensureAuxInfo(
fmt::format(fmt(_SYS_STR("{}.{}_{}.CSKR")), chSysName, overlaySys, cskrSys)); fmt::format(FMT_STRING(_SYS_STR("{}.{}_{}.CSKR")), chSysName, overlaySys, cskrSys));
} else { } else {
ch.cskrIce = inPath.ensureAuxInfo( ch.cskrIce = inPath.ensureAuxInfo(
fmt::format(fmt(_SYS_STR("{}.{}.CSKR")), chSysName, overlaySys)); fmt::format(FMT_STRING(_SYS_STR("{}.{}.CSKR")), chSysName, overlaySys));
} }
} }
} }
@ -1074,9 +1073,9 @@ bool ANCS::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
hecl::ProjectPath pathOut; hecl::ProjectPath pathOut;
if (!act.animId.empty()) { if (!act.animId.empty()) {
hecl::SystemStringConv idSys(act.animId); hecl::SystemStringConv idSys(act.animId);
pathOut = inPath.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}_{}.ANIM")), sysStr, idSys)); pathOut = inPath.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}_{}.ANIM")), sysStr, idSys));
} else { } else {
inPath.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}.ANIM")), sysStr)); inPath.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}.ANIM")), sysStr));
} }
prim.animId = pathOut; prim.animId = pathOut;
break; break;
@ -1093,9 +1092,9 @@ bool ANCS::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
hecl::ProjectPath pathOut; hecl::ProjectPath pathOut;
if (!act.animId.empty()) { if (!act.animId.empty()) {
hecl::SystemStringConv animIdSys(act.animId); hecl::SystemStringConv animIdSys(act.animId);
pathOut = inPath.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}_{}.ANIM")), sysStr, animIdSys)); pathOut = inPath.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}_{}.ANIM")), sysStr, animIdSys));
} else { } else {
pathOut = inPath.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}.ANIM")), sysStr)); pathOut = inPath.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}.ANIM")), sysStr));
} }
ancs.animationSet.animResources.emplace_back(); ancs.animationSet.animResources.emplace_back();
@ -1103,7 +1102,7 @@ bool ANCS::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
/* Check for associated EVNT YAML */ /* Check for associated EVNT YAML */
hecl::SystemString testPrefix(inPath.getWithExtension( hecl::SystemString testPrefix(inPath.getWithExtension(
fmt::format(fmt(_SYS_STR(".{}_")), sysStr).c_str(), true).getLastComponent()); fmt::format(FMT_STRING(_SYS_STR(".{}_")), sysStr).c_str(), true).getLastComponent());
hecl::ProjectPath evntYamlPath; hecl::ProjectPath evntYamlPath;
for (const auto& ent : dEnum) { for (const auto& ent : dEnum) {
if (hecl::StringUtils::BeginsWith(ent.m_name, testPrefix.c_str()) && if (hecl::StringUtils::BeginsWith(ent.m_name, testPrefix.c_str()) &&
@ -1163,7 +1162,7 @@ bool ANCS::CookCSKR(const hecl::ProjectPath& outPath, const hecl::ProjectPath& i
} }
} }
if (!subtype) if (!subtype)
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to find subtype '{}'")), subName); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to find subtype '{}'")), subName);
} }
const hecl::ProjectPath* modelPath = nullptr; const hecl::ProjectPath* modelPath = nullptr;
@ -1176,7 +1175,7 @@ bool ANCS::CookCSKR(const hecl::ProjectPath& outPath, const hecl::ProjectPath& i
} }
} }
if (!attachment) if (!attachment)
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to find attachment '{}'")), overName); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to find attachment '{}'")), overName);
modelPath = &attachment->mesh; modelPath = &attachment->mesh;
} else if (overName.empty()) { } else if (overName.empty()) {
modelPath = &subtype->mesh; modelPath = &subtype->mesh;
@ -1188,15 +1187,15 @@ bool ANCS::CookCSKR(const hecl::ProjectPath& outPath, const hecl::ProjectPath& i
} }
} }
if (!modelPath) if (!modelPath)
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to resolve model path of {}:{}")), subName, overName); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to resolve model path of {}:{}")), subName, overName);
if (!modelPath->isFile()) if (!modelPath->isFile())
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to resolve '{}'")), modelPath->getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to resolve '{}'")), modelPath->getRelativePath());
hecl::ProjectPath skinIntPath = modelPath->getCookedPath(SpecEntMP1).getWithExtension(_SYS_STR(".skinint")); hecl::ProjectPath skinIntPath = modelPath->getCookedPath(SpecEntMP1).getWithExtension(_SYS_STR(".skinint"));
if (!skinIntPath.isFileOrGlob() || skinIntPath.getModtime() < modelPath->getModtime()) if (!skinIntPath.isFileOrGlob() || skinIntPath.getModtime() < modelPath->getModtime())
if (!modelCookFunc(*modelPath)) if (!modelCookFunc(*modelPath))
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to cook '{}'")), modelPath->getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to cook '{}'")), modelPath->getRelativePath());
std::vector<std::pair<std::vector<std::pair<uint32_t, float>>, uint32_t>> skins; std::vector<std::pair<std::vector<std::pair<uint32_t, float>>, uint32_t>> skins;
uint32_t posCount = 0; uint32_t posCount = 0;
@ -1221,7 +1220,7 @@ bool ANCS::CookCSKR(const hecl::ProjectPath& outPath, const hecl::ProjectPath& i
const std::string& name = boneNames[bIdx]; const std::string& name = boneNames[bIdx];
auto search = boneIdMap.find(name); auto search = boneIdMap.find(name);
if (search == boneIdMap.cend()) if (search == boneIdMap.cend())
Log.report(logvisor::Fatal, fmt("unable to find bone '{}' in {}"), name, Log.report(logvisor::Fatal, FMT_STRING("unable to find bone '{}' in {}"), name,
inPath.getRelativePathUTF8()); inPath.getRelativePathUTF8());
virtualBone.first.emplace_back(search->second, weight); virtualBone.first.emplace_back(search->second, weight);
} }
@ -1287,7 +1286,7 @@ bool ANCS::CookCSKRPC(const hecl::ProjectPath& outPath, const hecl::ProjectPath&
} }
} }
if (!subtype) if (!subtype)
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to find subtype '{}'")), subName); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to find subtype '{}'")), subName);
} }
const hecl::ProjectPath* modelPath = nullptr; const hecl::ProjectPath* modelPath = nullptr;
@ -1300,7 +1299,7 @@ bool ANCS::CookCSKRPC(const hecl::ProjectPath& outPath, const hecl::ProjectPath&
} }
} }
if (!attachment) if (!attachment)
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to find attachment '{}'")), overName); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to find attachment '{}'")), overName);
modelPath = &attachment->mesh; modelPath = &attachment->mesh;
} else if (overName.empty()) { } else if (overName.empty()) {
modelPath = &subtype->mesh; modelPath = &subtype->mesh;
@ -1312,15 +1311,15 @@ bool ANCS::CookCSKRPC(const hecl::ProjectPath& outPath, const hecl::ProjectPath&
} }
} }
if (!modelPath) if (!modelPath)
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to resolve model path of {}:{}")), subName, overName); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to resolve model path of {}:{}")), subName, overName);
if (!modelPath->isFile()) if (!modelPath->isFile())
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to resolve '{}'")), modelPath->getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to resolve '{}'")), modelPath->getRelativePath());
hecl::ProjectPath skinIntPath = modelPath->getCookedPath(SpecEntMP1PC).getWithExtension(_SYS_STR(".skinint")); hecl::ProjectPath skinIntPath = modelPath->getCookedPath(SpecEntMP1PC).getWithExtension(_SYS_STR(".skinint"));
if (!skinIntPath.isFileOrGlob() || skinIntPath.getModtime() < modelPath->getModtime()) if (!skinIntPath.isFileOrGlob() || skinIntPath.getModtime() < modelPath->getModtime())
if (!modelCookFunc(*modelPath)) if (!modelCookFunc(*modelPath))
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to cook '{}'")), modelPath->getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to cook '{}'")), modelPath->getRelativePath());
uint32_t bankCount = 0; uint32_t bankCount = 0;
std::vector<std::vector<uint32_t>> skinBanks; std::vector<std::vector<uint32_t>> skinBanks;
@ -1360,7 +1359,7 @@ bool ANCS::CookCSKRPC(const hecl::ProjectPath& outPath, const hecl::ProjectPath&
const std::string& name = boneNames[bIdx]; const std::string& name = boneNames[bIdx];
auto search = boneIdMap.find(name); auto search = boneIdMap.find(name);
if (search == boneIdMap.cend()) if (search == boneIdMap.cend())
Log.report(logvisor::Fatal, fmt("unable to find bone '{}' in {}"), name, Log.report(logvisor::Fatal, FMT_STRING("unable to find bone '{}' in {}"), name,
inPath.getRelativePathUTF8()); inPath.getRelativePathUTF8());
virtualBone.emplace_back(search->second, weight); virtualBone.emplace_back(search->second, weight);
} }
@ -1381,7 +1380,7 @@ bool ANCS::CookCSKRPC(const hecl::ProjectPath& outPath, const hecl::ProjectPath&
const std::string& name = boneNames[bIdx]; const std::string& name = boneNames[bIdx];
auto search = boneIdMap.find(name); auto search = boneIdMap.find(name);
if (search == boneIdMap.cend()) if (search == boneIdMap.cend())
Log.report(logvisor::Fatal, fmt("unable to find bone '{}' in {}"), name, Log.report(logvisor::Fatal, FMT_STRING("unable to find bone '{}' in {}"), name,
inPath.getRelativePathUTF8()); inPath.getRelativePathUTF8());
skinOut.writeUint32Big(search->second); skinOut.writeUint32Big(search->second);
} }
@ -1420,7 +1419,7 @@ bool ANCS::CookANIM(const hecl::ProjectPath& outPath, const hecl::ProjectPath& i
DNAANCS::Action action = ds.compileActionChannelsOnly(actNameView.str()); DNAANCS::Action action = ds.compileActionChannelsOnly(actNameView.str());
if (!actor.armatures.size()) if (!actor.armatures.size())
Log.report(logvisor::Fatal, fmt(_SYS_STR("0 armatures in {}")), inPath.getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("0 armatures in {}")), inPath.getRelativePath());
/* Build bone ID map */ /* Build bone ID map */
std::unordered_map<std::string, atInt32> boneIdMap; std::unordered_map<std::string, atInt32> boneIdMap;
@ -1440,7 +1439,7 @@ bool ANCS::CookANIM(const hecl::ProjectPath& outPath, const hecl::ProjectPath& i
/* Check for associated EVNT YAML */ /* Check for associated EVNT YAML */
hecl::SystemString testPrefix(inPath.getWithExtension( hecl::SystemString testPrefix(inPath.getWithExtension(
fmt::format(fmt(_SYS_STR(".{}_")), actName).c_str(), true).getLastComponent()); fmt::format(FMT_STRING(_SYS_STR(".{}_")), actName).c_str(), true).getLastComponent());
hecl::ProjectPath evntYamlPath; hecl::ProjectPath evntYamlPath;
for (const auto& ent : hecl::DirectoryEnumerator(inPath.getParentPath().getAbsolutePath())) { for (const auto& ent : hecl::DirectoryEnumerator(inPath.getParentPath().getAbsolutePath())) {
if (hecl::StringUtils::BeginsWith(ent.m_name, testPrefix.c_str()) && if (hecl::StringUtils::BeginsWith(ent.m_name, testPrefix.c_str()) &&

View File

@ -7,7 +7,7 @@ namespace DataSpec::DNAMP1 {
using ANIMOutStream = hecl::blender::ANIMOutStream; using ANIMOutStream = hecl::blender::ANIMOutStream;
void ANIM::IANIM::sendANIMToBlender(hecl::blender::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig) const { void ANIM::IANIM::sendANIMToBlender(hecl::blender::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig) const {
os.format(fmt( os.format(FMT_STRING(
"act.hecl_fps = round({})\n" "act.hecl_fps = round({})\n"
"act.hecl_looping = {}\n"), "act.hecl_looping = {}\n"),
(1.0f / mainInterval), looping ? "True" : "False"); (1.0f / mainInterval), looping ? "True" : "False");
@ -26,7 +26,7 @@ void ANIM::IANIM::sendANIMToBlender(hecl::blender::PyOutStream& os, const DNAANI
continue; continue;
} }
os.format(fmt("bone_string = '{}'\n"), *bName); os.format(FMT_STRING("bone_string = '{}'\n"), *bName);
os << "action_group = act.groups.new(bone_string)\n" os << "action_group = act.groups.new(bone_string)\n"
"\n" "\n"
"rotCurves = []\n" "rotCurves = []\n"
@ -111,7 +111,7 @@ UniqueID32 ANIM::GetEVNTId(athena::io::IStreamReader& reader) {
reader.seek(4); reader.seek(4);
return reader.readUint32Big(); return reader.readUint32Big();
default: default:
Log.report(logvisor::Error, fmt("unrecognized ANIM version")); Log.report(logvisor::Error, FMT_STRING("unrecognized ANIM version"));
break; break;
} }
return {}; return {};
@ -134,7 +134,7 @@ void ANIM::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
m_anim->read(reader); m_anim->read(reader);
break; break;
default: default:
Log.report(logvisor::Error, fmt("unrecognized ANIM version")); Log.report(logvisor::Error, FMT_STRING("unrecognized ANIM version"));
break; break;
} }
} }
@ -559,7 +559,7 @@ ANIM::ANIM(const BlenderAction& act, const std::unordered_map<std::string, atInt
for (const BlenderAction::Channel& chan : act.channels) { for (const BlenderAction::Channel& chan : act.channels) {
auto search = idMap.find(chan.boneName); auto search = idMap.find(chan.boneName);
if (search == idMap.cend()) { if (search == idMap.cend()) {
Log.report(logvisor::Warning, fmt("unable to find id for bone '{}'"), chan.boneName); Log.report(logvisor::Warning, FMT_STRING("unable to find id for bone '{}'"), chan.boneName);
continue; continue;
} }
if (addedBones.find(search->second) != addedBones.cend()) if (addedBones.find(search->second) != addedBones.cend())

View File

@ -192,7 +192,7 @@ struct ANIM : BigDNA {
if (m_anim->evnt.isValid()) { if (m_anim->evnt.isValid()) {
hecl::SystemStringConv sysStr(animInfo.name); hecl::SystemStringConv sysStr(animInfo.name);
hecl::ProjectPath evntYamlPath = outPath.getWithExtension( hecl::ProjectPath evntYamlPath = outPath.getWithExtension(
fmt::format(fmt(_SYS_STR(".{}_{}.evnt.yaml")), sysStr, m_anim->evnt).c_str(), true); fmt::format(FMT_STRING(_SYS_STR(".{}_{}.evnt.yaml")), sysStr, m_anim->evnt).c_str(), true);
hecl::ProjectPath::Type evntYamlType = evntYamlPath.getPathType(); hecl::ProjectPath::Type evntYamlType = evntYamlPath.getPathType();
if (force || evntYamlType == hecl::ProjectPath::Type::None) { if (force || evntYamlType == hecl::ProjectPath::Type::None) {

View File

@ -34,7 +34,7 @@ void CINF::sendVertexGroupsToBlender(hecl::blender::PyOutStream& os) const {
for (atUint32 bid : boneIds) { for (atUint32 bid : boneIds) {
for (const Name& name : names) { for (const Name& name : names) {
if (name.boneId == bid) { if (name.boneId == bid) {
os.format(fmt("obj.vertex_groups.new(name='{}')\n"), name.name); os.format(FMT_STRING("obj.vertex_groups.new(name='{}')\n"), name.name);
break; break;
} }
} }
@ -44,7 +44,7 @@ void CINF::sendVertexGroupsToBlender(hecl::blender::PyOutStream& os) const {
void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& cinfId) const { void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& cinfId) const {
DNAANIM::RigInverter<CINF> inverter(*this); DNAANIM::RigInverter<CINF> inverter(*this);
os.format(fmt( os.format(FMT_STRING(
"arm = bpy.data.armatures.new('CINF_{}')\n" "arm = bpy.data.armatures.new('CINF_{}')\n"
"arm_obj = bpy.data.objects.new(arm.name, arm)\n" "arm_obj = bpy.data.objects.new(arm.name, arm)\n"
"bpy.context.scene.collection.objects.link(arm_obj)\n" "bpy.context.scene.collection.objects.link(arm_obj)\n"
@ -56,7 +56,7 @@ void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& c
for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones()) { for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones()) {
zeus::simd_floats originF(bone.m_origBone.origin.simd); zeus::simd_floats originF(bone.m_origBone.origin.simd);
zeus::simd_floats tailF(bone.m_tail.mSimd); zeus::simd_floats tailF(bone.m_tail.mSimd);
os.format(fmt( os.format(FMT_STRING(
"bone = arm.edit_bones.new('{}')\n" "bone = arm.edit_bones.new('{}')\n"
"bone.head = ({},{},{})\n" "bone.head = ({},{},{})\n"
"bone.tail = ({},{},{})\n" "bone.tail = ({},{},{})\n"
@ -68,16 +68,16 @@ void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& c
for (const Bone& bone : bones) for (const Bone& bone : bones)
if (bone.parentId != 2) if (bone.parentId != 2)
os.format(fmt("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId); os.format(FMT_STRING("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId);
os << "bpy.ops.object.mode_set(mode='OBJECT')\n"; os << "bpy.ops.object.mode_set(mode='OBJECT')\n";
for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones()) for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones())
os.format(fmt("arm_obj.pose.bones['{}'].rotation_mode = 'QUATERNION'\n"), os.format(FMT_STRING("arm_obj.pose.bones['{}'].rotation_mode = 'QUATERNION'\n"),
*getBoneNameFromId(bone.m_origBone.id)); *getBoneNameFromId(bone.m_origBone.id));
} }
std::string CINF::GetCINFArmatureName(const UniqueID32& cinfId) { return fmt::format(fmt("CINF_{}"), cinfId); } std::string CINF::GetCINFArmatureName(const UniqueID32& cinfId) { return fmt::format(FMT_STRING("CINF_{}"), cinfId); }
int CINF::RecursiveAddArmatureBone(const Armature& armature, const BlenderBone* bone, int parent, int& curId, int CINF::RecursiveAddArmatureBone(const Armature& armature, const BlenderBone* bone, int parent, int& curId,
std::unordered_map<std::string, atInt32>& idMap, std::unordered_map<std::string, atInt32>& idMap,
@ -167,7 +167,7 @@ bool CINF::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
return false; return false;
auto os = conn.beginPythonOut(true); auto os = conn.beginPythonOut(true);
os.format(fmt("import bpy\n" os.format(FMT_STRING("import bpy\n"
"from mathutils import Vector\n" "from mathutils import Vector\n"
"bpy.context.scene.name = 'CINF_{}'\n" "bpy.context.scene.name = 'CINF_{}'\n"
"bpy.context.scene.hecl_arm_obj = bpy.context.scene.name\n" "bpy.context.scene.hecl_arm_obj = bpy.context.scene.name\n"

View File

@ -26,7 +26,7 @@ void Material::AddTexture(Stream& out, GX::TexGenSrc type, int mtxIdx, uint32_t
if (mtxIdx == -1) if (mtxIdx == -1)
mtxLabel = "IDENTITY"; mtxLabel = "IDENTITY";
else else
mtxLabel = fmt::format(fmt("MTX_{}"), mtxIdx); mtxLabel = fmt::format(FMT_STRING("MTX_{}"), mtxIdx);
std::string texLabel; std::string texLabel;
if (diffuse) if (diffuse)
@ -34,15 +34,14 @@ void Material::AddTexture(Stream& out, GX::TexGenSrc type, int mtxIdx, uint32_t
else else
texLabel = "Texture"; texLabel = "Texture";
out.format(fmt( out.format(FMT_STRING("# Texture\n"
"# Texture\n" "tex_node = new_nodetree.nodes.new('ShaderNodeTexImage')\n"
"tex_node = new_nodetree.nodes.new('ShaderNodeTexImage')\n" "tex_node.label = '{} {}'\n"
"tex_node.label = '{} {}'\n" "texture_nodes.append(tex_node)\n"),
"texture_nodes.append(tex_node)\n"), texLabel, texIdx);
texLabel, texIdx);
if (texIdx != 0xff) if (texIdx != 0xff)
out.format(fmt("tex_node.image = tex_maps[{}]\n"), texIdx); out.format(FMT_STRING("tex_node.image = tex_maps[{}]\n"), texIdx);
if (type == GX::TG_POS) if (type == GX::TG_POS)
out << "tex_uv_node = new_nodetree.nodes.new('ShaderNodeTexCoord')\n" out << "tex_uv_node = new_nodetree.nodes.new('ShaderNodeTexCoord')\n"
@ -52,14 +51,13 @@ void Material::AddTexture(Stream& out, GX::TexGenSrc type, int mtxIdx, uint32_t
"tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['Normal'], tex_node.inputs['Vector']))\n"; "tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['Normal'], tex_node.inputs['Vector']))\n";
else if (type >= GX::TG_TEX0 && type <= GX::TG_TEX7) { else if (type >= GX::TG_TEX0 && type <= GX::TG_TEX7) {
uint8_t texIdx = type - GX::TG_TEX0; uint8_t texIdx = type - GX::TG_TEX0;
out.format(fmt( out.format(FMT_STRING("tex_uv_node = new_nodetree.nodes.new('ShaderNodeUVMap')\n"
"tex_uv_node = new_nodetree.nodes.new('ShaderNodeUVMap')\n" "tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['UV'], tex_node.inputs['Vector']))\n"
"tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['UV'], tex_node.inputs['Vector']))\n" "tex_uv_node.uv_map = 'UV_{}'\n"),
"tex_uv_node.uv_map = 'UV_{}'\n"), texIdx);
texIdx);
} }
out.format(fmt("tex_uv_node.label = '{}'\n"), mtxLabel); out.format(FMT_STRING("tex_uv_node.label = '{}'\n"), mtxLabel);
out << "gridder.place_node(tex_uv_node, 0)\n" out << "gridder.place_node(tex_uv_node, 0)\n"
"gridder.place_node(tex_node, 0)\n" "gridder.place_node(tex_node, 0)\n"
@ -72,171 +70,162 @@ void Material::AddTexture(Stream& out, GX::TexGenSrc type, int mtxIdx, uint32_t
void Material::AddTextureAnim(Stream& out, UVAnimation::Mode type, unsigned idx, const float* vals) { void Material::AddTextureAnim(Stream& out, UVAnimation::Mode type, unsigned idx, const float* vals) {
switch (type) { switch (type) {
case UVAnimation::Mode::MvInvNoTranslation: case UVAnimation::Mode::MvInvNoTranslation:
out.format(fmt( out.format(FMT_STRING("for link in list(tex_links):\n"
"for link in list(tex_links):\n" " if link.from_node.label == 'MTX_{}':\n"
" if link.from_node.label == 'MTX_{}':\n" " tex_links.remove(link)\n"
" tex_links.remove(link)\n" " soc_from = link.from_socket\n"
" soc_from = link.from_socket\n" " soc_to = link.to_socket\n"
" soc_to = link.to_socket\n" " node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n" " node.node_tree = bpy.data.node_groups['RetroUVMode0NodeN']\n"
" node.node_tree = bpy.data.node_groups['RetroUVMode0NodeN']\n" " node.location[0] = link.from_node.location[0] + 50\n"
" node.location[0] = link.from_node.location[0] + 50\n" " node.location[1] = link.from_node.location[1] - 50\n"
" node.location[1] = link.from_node.location[1] - 50\n" " new_nodetree.links.remove(link)\n"
" new_nodetree.links.remove(link)\n" " new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n" " new_nodetree.links.new(node.outputs[0], soc_to)\n\n"),
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n"), idx);
idx);
break; break;
case UVAnimation::Mode::MvInv: case UVAnimation::Mode::MvInv:
out.format(fmt( out.format(FMT_STRING("for link in list(tex_links):\n"
"for link in list(tex_links):\n" " if link.from_node.label == 'MTX_{}':\n"
" if link.from_node.label == 'MTX_{}':\n" " tex_links.remove(link)\n"
" tex_links.remove(link)\n" " soc_from = link.from_socket\n"
" soc_from = link.from_socket\n" " soc_to = link.to_socket\n"
" soc_to = link.to_socket\n" " node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n" " node.node_tree = bpy.data.node_groups['RetroUVMode1NodeN']\n"
" node.node_tree = bpy.data.node_groups['RetroUVMode1NodeN']\n" " node.location[0] = link.from_node.location[0] + 50\n"
" node.location[0] = link.from_node.location[0] + 50\n" " node.location[1] = link.from_node.location[1] - 50\n"
" node.location[1] = link.from_node.location[1] - 50\n" " new_nodetree.links.remove(link)\n"
" new_nodetree.links.remove(link)\n" " new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n" " new_nodetree.links.new(node.outputs[0], soc_to)\n\n"),
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n"), idx);
idx);
break; break;
case UVAnimation::Mode::Scroll: case UVAnimation::Mode::Scroll:
out.format(fmt( out.format(FMT_STRING("for link in list(tex_links):\n"
"for link in list(tex_links):\n" " if link.from_node.label == 'MTX_{}':\n"
" if link.from_node.label == 'MTX_{}':\n" " tex_links.remove(link)\n"
" tex_links.remove(link)\n" " soc_from = link.from_socket\n"
" soc_from = link.from_socket\n" " soc_to = link.to_socket\n"
" soc_to = link.to_socket\n" " node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n" " node.node_tree = bpy.data.node_groups['RetroUVMode2Node']\n"
" node.node_tree = bpy.data.node_groups['RetroUVMode2Node']\n" " node.location[0] = link.from_node.location[0] + 50\n"
" node.location[0] = link.from_node.location[0] + 50\n" " node.location[1] = link.from_node.location[1] - 50\n"
" node.location[1] = link.from_node.location[1] - 50\n" " node.inputs[1].default_value = ({},{},0)\n"
" node.inputs[1].default_value = ({},{},0)\n" " node.inputs[2].default_value = ({},{},0)\n"
" node.inputs[2].default_value = ({},{},0)\n" " new_nodetree.links.remove(link)\n"
" new_nodetree.links.remove(link)\n" " new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n" " new_nodetree.links.new(node.outputs[0], soc_to)\n\n"),
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n"), idx, vals[0], vals[1], vals[2], vals[3]);
idx, vals[0], vals[1], vals[2], vals[3]);
break; break;
case UVAnimation::Mode::Rotation: case UVAnimation::Mode::Rotation:
out.format(fmt( out.format(FMT_STRING("for link in list(tex_links):\n"
"for link in list(tex_links):\n" " if link.from_node.label == 'MTX_{}':\n"
" if link.from_node.label == 'MTX_{}':\n" " tex_links.remove(link)\n"
" tex_links.remove(link)\n" " soc_from = link.from_socket\n"
" soc_from = link.from_socket\n" " soc_to = link.to_socket\n"
" soc_to = link.to_socket\n" " node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n" " node.node_tree = bpy.data.node_groups['RetroUVMode3Node']\n"
" node.node_tree = bpy.data.node_groups['RetroUVMode3Node']\n" " node.location[0] = link.from_node.location[0] + 50\n"
" node.location[0] = link.from_node.location[0] + 50\n" " node.location[1] = link.from_node.location[1] - 50\n"
" node.location[1] = link.from_node.location[1] - 50\n" " node.inputs[1].default_value = {}\n"
" node.inputs[1].default_value = {}\n" " node.inputs[2].default_value = {}\n"
" node.inputs[2].default_value = {}\n" " new_nodetree.links.remove(link)\n"
" new_nodetree.links.remove(link)\n" " new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n" " new_nodetree.links.new(node.outputs[0], soc_to)\n\n"),
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n"), idx, vals[0], vals[1]);
idx, vals[0], vals[1]);
break; break;
case UVAnimation::Mode::HStrip: case UVAnimation::Mode::HStrip:
out.format(fmt( out.format(FMT_STRING("for link in list(tex_links):\n"
"for link in list(tex_links):\n" " if link.from_node.label == 'MTX_{}':\n"
" if link.from_node.label == 'MTX_{}':\n" " tex_links.remove(link)\n"
" tex_links.remove(link)\n" " soc_from = link.from_socket\n"
" soc_from = link.from_socket\n" " soc_to = link.to_socket\n"
" soc_to = link.to_socket\n" " node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n" " node.node_tree = bpy.data.node_groups['RetroUVMode4Node']\n"
" node.node_tree = bpy.data.node_groups['RetroUVMode4Node']\n" " node.location[0] = link.from_node.location[0] + 50\n"
" node.location[0] = link.from_node.location[0] + 50\n" " node.location[1] = link.from_node.location[1] - 50\n"
" node.location[1] = link.from_node.location[1] - 50\n" " node.inputs[1].default_value = {}\n"
" node.inputs[1].default_value = {}\n" " node.inputs[2].default_value = {}\n"
" node.inputs[2].default_value = {}\n" " node.inputs[3].default_value = {}\n"
" node.inputs[3].default_value = {}\n" " node.inputs[4].default_value = {}\n"
" node.inputs[4].default_value = {}\n" " new_nodetree.links.remove(link)\n"
" new_nodetree.links.remove(link)\n" " new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n" " new_nodetree.links.new(node.outputs[0], soc_to)\n\n"),
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n"), idx, vals[0], vals[1], vals[2], vals[3]);
idx, vals[0], vals[1], vals[2], vals[3]);
break; break;
case UVAnimation::Mode::VStrip: case UVAnimation::Mode::VStrip:
out.format(fmt( out.format(FMT_STRING("for link in list(tex_links):\n"
"for link in list(tex_links):\n" " if link.from_node.label == 'MTX_{}':\n"
" if link.from_node.label == 'MTX_{}':\n" " tex_links.remove(link)\n"
" tex_links.remove(link)\n" " soc_from = link.from_socket\n"
" soc_from = link.from_socket\n" " soc_to = link.to_socket\n"
" soc_to = link.to_socket\n" " node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n" " node.node_tree = bpy.data.node_groups['RetroUVMode5Node']\n"
" node.node_tree = bpy.data.node_groups['RetroUVMode5Node']\n" " node.location[0] = link.from_node.location[0] + 50\n"
" node.location[0] = link.from_node.location[0] + 50\n" " node.location[1] = link.from_node.location[1] - 50\n"
" node.location[1] = link.from_node.location[1] - 50\n" " node.inputs[1].default_value = {}\n"
" node.inputs[1].default_value = {}\n" " node.inputs[2].default_value = {}\n"
" node.inputs[2].default_value = {}\n" " node.inputs[3].default_value = {}\n"
" node.inputs[3].default_value = {}\n" " node.inputs[4].default_value = {}\n"
" node.inputs[4].default_value = {}\n" " new_nodetree.links.remove(link)\n"
" new_nodetree.links.remove(link)\n" " new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n" " new_nodetree.links.new(node.outputs[0], soc_to)\n\n"),
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n"), idx, vals[0], vals[1], vals[2], vals[3]);
idx, vals[0], vals[1], vals[2], vals[3]);
break; break;
case UVAnimation::Mode::Model: case UVAnimation::Mode::Model:
out.format(fmt( out.format(FMT_STRING("for link in list(tex_links):\n"
"for link in list(tex_links):\n" " if link.from_node.label == 'MTX_{}':\n"
" if link.from_node.label == 'MTX_{}':\n" " tex_links.remove(link)\n"
" tex_links.remove(link)\n" " soc_from = link.from_socket\n"
" soc_from = link.from_socket\n" " soc_to = link.to_socket\n"
" soc_to = link.to_socket\n" " node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n" " node.node_tree = bpy.data.node_groups['RetroUVMode6NodeN']\n"
" node.node_tree = bpy.data.node_groups['RetroUVMode6NodeN']\n" " node.location[0] = link.from_node.location[0] + 50\n"
" node.location[0] = link.from_node.location[0] + 50\n" " node.location[1] = link.from_node.location[1] - 50\n"
" node.location[1] = link.from_node.location[1] - 50\n" " new_nodetree.links.remove(link)\n"
" new_nodetree.links.remove(link)\n" " new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n" " new_nodetree.links.new(node.outputs[0], soc_to)\n\n"),
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n"), idx);
idx);
break; break;
case UVAnimation::Mode::CylinderEnvironment: case UVAnimation::Mode::CylinderEnvironment:
out.format(fmt( out.format(FMT_STRING("for link in list(tex_links):\n"
"for link in list(tex_links):\n" " if link.from_node.label == 'MTX_{}':\n"
" if link.from_node.label == 'MTX_{}':\n" " tex_links.remove(link)\n"
" tex_links.remove(link)\n" " soc_from = link.from_socket\n"
" soc_from = link.from_socket\n" " soc_to = link.to_socket\n"
" soc_to = link.to_socket\n" " node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n" " node.node_tree = bpy.data.node_groups['RetroUVMode7NodeN']\n"
" node.node_tree = bpy.data.node_groups['RetroUVMode7NodeN']\n" " node.location[0] = link.from_node.location[0] + 50\n"
" node.location[0] = link.from_node.location[0] + 50\n" " node.location[1] = link.from_node.location[1] - 50\n"
" node.location[1] = link.from_node.location[1] - 50\n" " node.inputs[1].default_value = {}\n"
" node.inputs[1].default_value = {}\n" " node.inputs[2].default_value = {}\n"
" node.inputs[2].default_value = {}\n" " new_nodetree.links.remove(link)\n"
" new_nodetree.links.remove(link)\n" " new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n" " new_nodetree.links.new(node.outputs[0], soc_to)\n\n"),
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n"), idx, vals[0], vals[1]);
idx, vals[0], vals[1]);
break; break;
case UVAnimation::Mode::Eight: case UVAnimation::Mode::Eight:
out.format(fmt( out.format(FMT_STRING("for link in list(tex_links):\n"
"for link in list(tex_links):\n" " if link.from_node.label == 'MTX_{}':\n"
" if link.from_node.label == 'MTX_{}':\n" " tex_links.remove(link)\n"
" tex_links.remove(link)\n" " soc_from = link.from_socket\n"
" soc_from = link.from_socket\n" " soc_to = link.to_socket\n"
" soc_to = link.to_socket\n" " node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
" node = new_nodetree.nodes.new('ShaderNodeGroup')\n" " node.node_tree = bpy.data.node_groups['RetroUVMode8Node']\n"
" node.node_tree = bpy.data.node_groups['RetroUVMode8Node']\n" " node.location[0] = link.from_node.location[0] + 50\n"
" node.location[0] = link.from_node.location[0] + 50\n" " node.location[1] = link.from_node.location[1] - 50\n"
" node.location[1] = link.from_node.location[1] - 50\n" " node.inputs[1].default_value = {}\n"
" node.inputs[1].default_value = {}\n" " node.inputs[2].default_value = {}\n"
" node.inputs[2].default_value = {}\n" " node.inputs[3].default_value = {}\n"
" node.inputs[3].default_value = {}\n" " node.inputs[4].default_value = {}\n"
" node.inputs[4].default_value = {}\n" " node.inputs[5].default_value = {}\n"
" node.inputs[5].default_value = {}\n" " node.inputs[6].default_value = {}\n"
" node.inputs[6].default_value = {}\n" " node.inputs[7].default_value = {}\n"
" node.inputs[7].default_value = {}\n" " node.inputs[8].default_value = {}\n"
" node.inputs[8].default_value = {}\n" " node.inputs[9].default_value = {}\n"
" node.inputs[9].default_value = {}\n" " new_nodetree.links.remove(link)\n"
" new_nodetree.links.remove(link)\n" " new_nodetree.links.new(soc_from, node.inputs[0])\n"
" new_nodetree.links.new(soc_from, node.inputs[0])\n" " new_nodetree.links.new(node.outputs[0], soc_to)\n\n"),
" new_nodetree.links.new(node.outputs[0], soc_to)\n\n"), idx, vals[0], vals[1], vals[2], vals[3], vals[4], vals[5], vals[6], vals[7], vals[8]);
idx, vals[0], vals[1], vals[2], vals[3], vals[4], vals[5], vals[6], vals[7], vals[8]);
break; break;
default: default:
break; break;
@ -244,12 +233,12 @@ void Material::AddTextureAnim(Stream& out, UVAnimation::Mode type, unsigned idx,
} }
void Material::AddKcolor(Stream& out, const GX::Color& col, unsigned idx) { void Material::AddKcolor(Stream& out, const GX::Color& col, unsigned idx) {
out.format(fmt( out.format(FMT_STRING("kcolors[{}] = ({}, {}, {}, {})\n"
"kcolors[{}] = ({}, {}, {}, {})\n" "kalphas[{}] = {}\n"
"kalphas[{}] = {}\n" "\n"),
"\n"), idx, (float)col.color[0] / (float)0xff, (float)col.color[1] / (float)0xff,
idx, (float)col.color[0] / (float)0xff, (float)col.color[1] / (float)0xff, (float)col.color[2] / (float)0xff, (float)col.color[2] / (float)0xff, (float)col.color[3] / (float)0xff, idx,
(float)col.color[3] / (float)0xff, idx, (float)col.color[3] / (float)0xff); (float)col.color[3] / (float)0xff);
} }
template <class MAT> template <class MAT>
@ -354,15 +343,14 @@ template <class MAT>
static void _DescribeTEV(const MAT& mat) { static void _DescribeTEV(const MAT& mat) {
for (uint32_t i = 0; i < mat.tevStageCount; ++i) { for (uint32_t i = 0; i < mat.tevStageCount; ++i) {
const auto& stage = mat.tevStages[i]; const auto& stage = mat.tevStages[i];
fmt::print(stderr, fmt("A:{} B:{} C:{} D:{} -> {} | A:{} B:{} C:{} D:{} -> {}\n"), fmt::print(stderr, FMT_STRING("A:{} B:{} C:{} D:{} -> {} | A:{} B:{} C:{} D:{} -> {}\n"), ToString(stage.colorInA()),
ToString(stage.colorInA()), ToString(stage.colorInB()), ToString(stage.colorInB()), ToString(stage.colorInC()), ToString(stage.colorInD()),
ToString(stage.colorInC()), ToString(stage.colorInD()), ToString(stage.colorOpOutReg()), ToString(stage.colorOpOutReg()), ToString(stage.alphaInA()), ToString(stage.alphaInB()),
ToString(stage.alphaInA()), ToString(stage.alphaInB()), ToString(stage.alphaInC()), ToString(stage.alphaInD()), ToString(stage.alphaOpOutReg()));
ToString(stage.alphaInC()), ToString(stage.alphaInD()), ToString(stage.alphaOpOutReg()));
} }
bool hasInd = mat.flags.samusReflectionIndirectTexture(); bool hasInd = mat.flags.samusReflectionIndirectTexture();
bool hasLm = mat.flags.lightmap(); bool hasLm = mat.flags.lightmap();
fmt::print(stderr, fmt("HasIndirect: {} HasLightmap: {}\n"), hasInd, hasLm); fmt::print(stderr, FMT_STRING("HasIndirect: {} HasLightmap: {}\n"), hasInd, hasLm);
} }
struct TexLink { struct TexLink {
@ -388,12 +376,10 @@ struct KColLink {
struct WhiteColorLink { struct WhiteColorLink {
const char* shaderInput; const char* shaderInput;
explicit WhiteColorLink(const char* shaderInput) explicit WhiteColorLink(const char* shaderInput) : shaderInput(shaderInput) {}
: shaderInput(shaderInput) {}
}; };
static void _GenerateRootShader(Stream& out, int) { static void _GenerateRootShader(Stream& out, int) { /* End of shader links */
/* End of shader links */
} }
template <typename... Targs> template <typename... Targs>
@ -401,8 +387,8 @@ static void _GenerateRootShader(Stream& out, int tidx, TexLink tex, Targs... arg
int texIdx = tex.texidx == -1 ? tidx : tex.texidx; int texIdx = tex.texidx == -1 ? tidx : tex.texidx;
out << "texture_nodes[" << texIdx << "].name = '" << tex.shaderInput << "'\n"; out << "texture_nodes[" << texIdx << "].name = '" << tex.shaderInput << "'\n";
out << "texture_nodes[" << texIdx << "].label = '" << tex.shaderInput << "'\n"; out << "texture_nodes[" << texIdx << "].label = '" << tex.shaderInput << "'\n";
out << "new_nodetree.links.new(texture_nodes[" << texIdx << "].outputs['" << out << "new_nodetree.links.new(texture_nodes[" << texIdx << "].outputs['" << (tex.alpha ? "Alpha" : "Color")
(tex.alpha ? "Alpha" : "Color") << "'], node.inputs['" << tex.shaderInput << "'])\n"; << "'], node.inputs['" << tex.shaderInput << "'])\n";
if (tex.texidx == -1) if (tex.texidx == -1)
++tidx; ++tidx;
_GenerateRootShader(out, tidx, args...); _GenerateRootShader(out, tidx, args...);
@ -422,8 +408,8 @@ static void _GenerateRootShader(Stream& out, int tidx, ExtendedSpecularLink tex,
template <typename... Targs> template <typename... Targs>
static void _GenerateRootShader(Stream& out, int tidx, KColLink kcol, Targs... args) { static void _GenerateRootShader(Stream& out, int tidx, KColLink kcol, Targs... args) {
out << "node.inputs['" << kcol.shaderInput << "'].default_value = " << out << "node.inputs['" << kcol.shaderInput << "'].default_value = " << (kcol.alpha ? "kalphas[" : "kcolors[")
(kcol.alpha ? "kalphas[" : "kcolors[") << kcol.kcidx << "]\n"; << kcol.kcidx << "]\n";
_GenerateRootShader(out, tidx, args...); _GenerateRootShader(out, tidx, args...);
} }
@ -437,22 +423,24 @@ template <typename... Targs>
static void _GenerateRootShader(Stream& out, const char* type, Targs... args) { static void _GenerateRootShader(Stream& out, const char* type, Targs... args) {
out << "node = new_nodetree.nodes.new('ShaderNodeGroup')\n" out << "node = new_nodetree.nodes.new('ShaderNodeGroup')\n"
"node.name = 'Output'\n" "node.name = 'Output'\n"
"node.node_tree = bpy.data.node_groups['" << type << "']\n" "node.node_tree = bpy.data.node_groups['"
<< type
<< "']\n"
"gridder.place_node(node, 1)\n" "gridder.place_node(node, 1)\n"
"new_nodetree.links.new(node.outputs['Surface'], blend_node.inputs['Surface'])\n"; "new_nodetree.links.new(node.outputs['Surface'], blend_node.inputs['Surface'])\n";
_GenerateRootShader(out, 0, args...); _GenerateRootShader(out, 0, args...);
} }
static TexLink operator "" _tex(const char* str, size_t) { return TexLink(str); } static TexLink operator"" _tex(const char* str, size_t) { return TexLink(str); }
static TexLink operator "" _texa(const char* str, size_t) { return TexLink(str, -1, true); } static TexLink operator"" _texa(const char* str, size_t) { return TexLink(str, -1, true); }
static KColLink operator "" _kcol(const char* str, size_t) { return KColLink(str); } static KColLink operator"" _kcol(const char* str, size_t) { return KColLink(str); }
static KColLink operator "" _kcola(const char* str, size_t) { return KColLink(str, 0, true); } static KColLink operator"" _kcola(const char* str, size_t) { return KColLink(str, 0, true); }
template <class MAT> template <class MAT>
static void _ConstructMaterial(Stream& out, const MAT& material, unsigned groupIdx, unsigned matIdx) { static void _ConstructMaterial(Stream& out, const MAT& material, unsigned groupIdx, unsigned matIdx) {
unsigned i; unsigned i;
out.format(fmt("new_material = bpy.data.materials.new('MAT_{}_{}')\n"), groupIdx, matIdx); out.format(FMT_STRING("new_material = bpy.data.materials.new('MAT_{}_{}')\n"), groupIdx, matIdx);
out << "new_material.use_fake_user = True\n" out << "new_material.use_fake_user = True\n"
"new_material.use_nodes = True\n" "new_material.use_nodes = True\n"
"new_material.use_backface_culling = True\n" "new_material.use_backface_culling = True\n"
@ -470,27 +458,26 @@ static void _ConstructMaterial(Stream& out, const MAT& material, unsigned groupI
"\n"; "\n";
/* Material Flags */ /* Material Flags */
out.format(fmt( out.format(FMT_STRING("new_material.retro_depth_sort = {}\n"
"new_material.retro_depth_sort = {}\n" "new_material.retro_alpha_test = {}\n"
"new_material.retro_alpha_test = {}\n" "new_material.retro_samus_reflection = {}\n"
"new_material.retro_samus_reflection = {}\n" "new_material.retro_depth_write = {}\n"
"new_material.retro_depth_write = {}\n" "new_material.retro_samus_reflection_persp = {}\n"
"new_material.retro_samus_reflection_persp = {}\n" "new_material.retro_shadow_occluder = {}\n"
"new_material.retro_shadow_occluder = {}\n" "new_material.retro_samus_reflection_indirect = {}\n"
"new_material.retro_samus_reflection_indirect = {}\n" "new_material.retro_lightmapped = {}\n"
"new_material.retro_lightmapped = {}\n" "new_material.diffuse_color = (1, 1, 1, {})\n"),
"new_material.diffuse_color = (1, 1, 1, {})\n"), material.flags.depthSorting() ? "True" : "False", material.flags.alphaTest() ? "True" : "False",
material.flags.depthSorting() ? "True" : "False", material.flags.alphaTest() ? "True" : "False", material.flags.samusReflection() ? "True" : "False", material.flags.depthWrite() ? "True" : "False",
material.flags.samusReflection() ? "True" : "False", material.flags.depthWrite() ? "True" : "False", material.flags.samusReflectionSurfaceEye() ? "True" : "False",
material.flags.samusReflectionSurfaceEye() ? "True" : "False", material.flags.shadowOccluderMesh() ? "True" : "False",
material.flags.shadowOccluderMesh() ? "True" : "False", material.flags.samusReflectionIndirectTexture() ? "True" : "False",
material.flags.samusReflectionIndirectTexture() ? "True" : "False", material.flags.lightmap() ? "True" : "False", material.flags.lightmap() ? "True" : "False", material.flags.shadowOccluderMesh() ? "0" : "1");
material.flags.shadowOccluderMesh() ? "0" : "1");
/* Texture Indices */ /* Texture Indices */
out << "tex_maps = []\n"; out << "tex_maps = []\n";
for (atUint32 idx : material.textureIdxs) for (atUint32 idx : material.textureIdxs)
out.format(fmt("tex_maps.append(texmap_list[{}])\n"), idx); out.format(FMT_STRING("tex_maps.append(texmap_list[{}])\n"), idx);
/* KColor entries */ /* KColor entries */
if (material.flags.konstValuesEnabled()) { if (material.flags.konstValuesEnabled()) {
@ -537,131 +524,268 @@ static void _ConstructMaterial(Stream& out, const MAT& material, unsigned groupI
/* Select appropriate root shader and link textures */ /* Select appropriate root shader and link textures */
uint32_t hash = _HashTextureConfig(material); uint32_t hash = _HashTextureConfig(material);
switch (hash) { switch (hash) {
case 0x03FEE002: /* RetroShader: Diffuse, Emissive, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, "Reflection"_tex);
break;
case 0x0473AE40: /* RetroShader: Lightmap, Diffuse, Emissive, Alpha=1.0 */ case 0x0473AE40: /* RetroShader: Lightmap, Diffuse, Emissive, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex);
break;
case 0x072D2CB3: /* RetroShader: Diffuse, Emissive, Reflection, Alpha=1.0 */ case 0x072D2CB3: /* RetroShader: Diffuse, Emissive, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, WhiteColorLink("Specular"), "Reflection"_tex); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, WhiteColorLink("Specular"),
"Reflection"_tex);
break;
case 0x07AA75D7: /* RetroShader: Diffuse, Emissive, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, TexLink("Alpha", 0, true));
break;
case 0x0879D346: /* RetroShader: KColorDiffuse, Alpha=Texture */ case 0x0879D346: /* RetroShader: KColorDiffuse, Alpha=Texture */
_GenerateRootShader(out, "RetroShader", "Diffuse"_kcol, "Alpha"_tex); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_kcol, "Alpha"_tex);
break;
case 0x0DA256BB: /* Lightmap, Diffuse, Specular, Reflection, Alpha=KAlpha */ case 0x0DA256BB: /* Lightmap, Diffuse, Specular, Reflection, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "Reflection"_tex,
"Alpha"_kcola);
break;
case 0x11C41DA4: /* RetroDynamicCharacterShader: Diffuse, DynamicMaskTex, Specular, Reflection, Alpha=1.0 */ case 0x11C41DA4: /* RetroDynamicCharacterShader: Diffuse, DynamicMaskTex, Specular, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroDynamicCharacterShader", "Diffuse"_tex, "Emissive"_tex, "Specular"_tex, "Reflection"_tex); break; _GenerateRootShader(out, "RetroDynamicCharacterShader", "Diffuse"_tex, "Emissive"_tex, "Specular"_tex,
"Reflection"_tex);
break;
case 0x1218F83E: /* RetroShader: ObjLightmap, Diffuse, ExtendedSpecular, Reflection, Alpha=DiffuseAlpha */ case 0x1218F83E: /* RetroShader: ObjLightmap, Diffuse, ExtendedSpecular, Reflection, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, ExtendedSpecularLink(), "Reflection"_tex, TexLink("Alpha", 1, true)); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, ExtendedSpecularLink(), "Reflection"_tex,
TexLink("Alpha", 1, true));
break;
case 0x129B8578: /* RetroShader: KColorDiffuse, Emissive, Alpha=KAlpha */ case 0x129B8578: /* RetroShader: KColorDiffuse, Emissive, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_kcol, "Emissive"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_kcol, "Emissive"_tex, "Alpha"_kcola);
break;
case 0x15A00948: /* RetroShader: Diffuse, Emissive, Specular, ExtendedSpecular, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, "Specular"_tex, "ExtendedSpecular"_tex,
"Reflection"_tex);
break;
case 0x15A3E6E5: /* RetroShader: Diffuse, Alpha=KAlpha */ case 0x15A3E6E5: /* RetroShader: Diffuse, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Alpha"_kcola);
break;
case 0x1BEB3E15: /* RetroShader: Diffuse, Alpha=DiffuseAlpha */ case 0x1BEB3E15: /* RetroShader: Diffuse, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, TexLink("Alpha", 0, true)); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, TexLink("Alpha", 0, true));
break;
case 0x2261E0EB: /* RetroShader: Diffuse, Emissive, Specular, Reflection, Alpha=1.0 */ case 0x2261E0EB: /* RetroShader: Diffuse, Emissive, Specular, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, "Specular"_tex, "Reflection"_tex); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, "Specular"_tex, "Reflection"_tex);
break;
case 0x239C7724: /* RetroDynamicShader: Diffuse*Dynamic, Emissive*Dynamic, Alpha=1.0 */ case 0x239C7724: /* RetroDynamicShader: Diffuse*Dynamic, Emissive*Dynamic, Alpha=1.0 */
_GenerateRootShader(out, "RetroDynamicShader", "Diffuse"_tex, "Emissive"_tex); break; _GenerateRootShader(out, "RetroDynamicShader", "Diffuse"_tex, "Emissive"_tex);
break;
case 0x240C4C84: /* RetroShader: Lightmap, KColorDiffuse, Specular, Reflection, Alpha=KAlpha */ case 0x240C4C84: /* RetroShader: Lightmap, KColorDiffuse, Specular, Reflection, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_kcol, "Specular"_tex, "Reflection"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_kcol, "Specular"_tex, "Reflection"_tex,
"Alpha"_kcola);
break;
case 0x2523A379: /* RetroDynamicShader: Emissive*Dynamic, Specular, Reflection, Alpha=1.0 */ case 0x2523A379: /* RetroDynamicShader: Emissive*Dynamic, Specular, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroDynamicShader", "Emissive"_tex, "Specular"_tex, "Reflection"_tex); break; _GenerateRootShader(out, "RetroDynamicShader", "Emissive"_tex, "Specular"_tex, "Reflection"_tex);
break;
case 0x25E85017: /* RetroShader: Lightmap, KColorDiffuse, Alpha=KAlpha */ case 0x25E85017: /* RetroShader: Lightmap, KColorDiffuse, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_kcol, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_kcol, "Alpha"_kcola);
break;
case 0x27FD5C6C: /* RetroShader: ObjLightmap, Diffuse, Specular, Reflection, Alpha=DiffuseAlpha */ case 0x27FD5C6C: /* RetroShader: ObjLightmap, Diffuse, Specular, Reflection, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, TexLink("Alpha", 1, true)); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "Reflection"_tex,
TexLink("Alpha", 1, true));
break;
case 0x2AD9F535: /* RetroShader: Emissive, Reflection, Alpha=1.0 */ case 0x2AD9F535: /* RetroShader: Emissive, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Emissive"_tex, WhiteColorLink("Specular"), "Reflection"_tex); break; _GenerateRootShader(out, "RetroShader", "Emissive"_tex, WhiteColorLink("Specular"), "Reflection"_tex);
break;
case 0x2C9F5104: /* RetroShader: Diffuse, Specular, Reflection, Alpha=KAlpha */ case 0x2C9F5104: /* RetroShader: Diffuse, Specular, Reflection, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "Alpha"_kcola);
break;
case 0x2D059429: /* RetroShader: Diffuse, Emissive, ExtendedSpecular, Reflection, Alpha=1.0 */ case 0x2D059429: /* RetroShader: Diffuse, Emissive, ExtendedSpecular, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, ExtendedSpecularLink(), "Reflection"_tex); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, ExtendedSpecularLink(), "Reflection"_tex);
break;
case 0x30AC64BB: /* RetroShader: Diffuse, Specular, Reflection, Alpha=KAlpha, IndirectTex */ case 0x30AC64BB: /* RetroShader: Diffuse, Specular, Reflection, Alpha=KAlpha, IndirectTex */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "IndirectTex"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "IndirectTex"_tex,
case 0x39BC4809: /* RetroDynamicShader: ObjLightmap*Dynamic, Diffuse*Dynamic, Emissive*Dynamic, Specular, Reflection, Alpha=1.0 */ "Alpha"_kcola);
_GenerateRootShader(out, "RetroDynamicShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex, "Specular"_tex, "Reflection"_tex); break; break;
case 0x39BC4809: /* RetroDynamicShader: ObjLightmap*Dynamic, Diffuse*Dynamic, Emissive*Dynamic, Specular, Reflection,
Alpha=1.0 */
_GenerateRootShader(out, "RetroDynamicShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex, "Specular"_tex,
"Reflection"_tex);
break;
case 0x3BF97299: /* RetroShader: Lightmap, Diffuse, Specular, Reflection, Alpha=KAlpha, IndirectTex */ case 0x3BF97299: /* RetroShader: Lightmap, Diffuse, Specular, Reflection, Alpha=KAlpha, IndirectTex */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "IndirectTex"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "Reflection"_tex,
"IndirectTex"_tex, "Alpha"_kcola);
break;
case 0x4184FBCA: /* RetroShader: Lightmap, Diffuse, Emissive, DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex, TexLink("Alpha", 1, true));
break;
case 0x47ECF3ED: /* RetroShader: Diffuse, Specular, Reflection, Emissive, Alpha=1.0 */ case 0x47ECF3ED: /* RetroShader: Diffuse, Specular, Reflection, Emissive, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "Emissive"_tex); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "Emissive"_tex);
break;
case 0x4BBDFFA6: /* RetroShader: Diffuse, Emissive, Alpha=1.0 */ case 0x4BBDFFA6: /* RetroShader: Diffuse, Emissive, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex);
break;
case 0x4D4127A3: /* RetroShader: Lightmap, Diffuse, Specular, Reflection, Alpha=DiffuseAlpha */ case 0x4D4127A3: /* RetroShader: Lightmap, Diffuse, Specular, Reflection, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, TexLink("Alpha", 1, true)); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "Reflection"_tex,
TexLink("Alpha", 1, true));
break;
case 0x54A92F25: /* RetroShader: ObjLightmap, KColorDiffuse, Alpha=KAlpha */ case 0x54A92F25: /* RetroShader: ObjLightmap, KColorDiffuse, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_kcol, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_kcol, "Alpha"_kcola);
break;
case 0x58BAA415: /* RetroShader: Lightmap, Diffuse, Emissive, Alpha=1.0 */
// TODO: Last stage assigns into unused reg2, perhaps for runtime material mod?
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex);
break;
case 0x54C6204C: case 0x54C6204C:
_GenerateRootShader(out, "RetroShader"); break; _GenerateRootShader(out, "RetroShader");
break;
case 0x5A62D5F0: /* RetroShader: Lightmap, Diffuse, UnusedExtendedSpecular?, Alpha=DiffuseAlpha */ case 0x5A62D5F0: /* RetroShader: Lightmap, Diffuse, UnusedExtendedSpecular?, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, TexLink("Alpha", 1, true)); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, TexLink("Alpha", 1, true));
break;
case 0x5CB59821: /* RetroShader: Diffuse, UnusedSpecular?, Alpha=KAlpha */ case 0x5CB59821: /* RetroShader: Diffuse, UnusedSpecular?, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Alpha"_kcola);
break;
case 0x5D0F0069: /* RetroShader: Diffuse, Emissive, Alpha=DiffuseAlpha */ case 0x5D0F0069: /* RetroShader: Diffuse, Emissive, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, TexLink("Alpha", 0, true)); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, TexLink("Alpha", 0, true));
break;
case 0x5D80E53C: /* RetroShader: Emissive, Specular, Reflection, Alpha=1.0 */ case 0x5D80E53C: /* RetroShader: Emissive, Specular, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Emissive"_tex, "Specular"_tex, "Reflection"_tex); break; _GenerateRootShader(out, "RetroShader", "Emissive"_tex, "Specular"_tex, "Reflection"_tex);
break;
case 0x5F0AB0E9: /* RetroShader: Lightmap, Diffuse, UnusedSpecular?, Alpha=DiffuseAlpha */ case 0x5F0AB0E9: /* RetroShader: Lightmap, Diffuse, UnusedSpecular?, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, TexLink("Alpha", 1, true)); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, TexLink("Alpha", 1, true));
break;
case 0x5F189425: /* RetroShader: Lightmap, Diffuse, UnusedSpecular?, Alpha=KAlpha */ case 0x5F189425: /* RetroShader: Lightmap, Diffuse, UnusedSpecular?, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Alpha"_kcola);
break;
case 0x6601D113: /* RetroShader: Emissive, Alpha=1.0 */ case 0x6601D113: /* RetroShader: Emissive, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Emissive"_tex); break; _GenerateRootShader(out, "RetroShader", "Emissive"_tex);
break;
case 0x694287FA: /* RetroShader: Diffuse, Emissive, Reflection, Alpha=1.0 */ case 0x694287FA: /* RetroShader: Diffuse, Emissive, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, WhiteColorLink("Specular"), "Reflection"_tex); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, WhiteColorLink("Specular"),
"Reflection"_tex);
break;
case 0x6D98D689: /* RetroDynamicAlphaShader: Diffuse*Dynamic, Specular, Reflection, Alpha=KAlpha*Dynamic */ case 0x6D98D689: /* RetroDynamicAlphaShader: Diffuse*Dynamic, Specular, Reflection, Alpha=KAlpha*Dynamic */
_GenerateRootShader(out, "RetroDynamicAlphaShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroDynamicAlphaShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "Alpha"_kcola);
break;
case 0x7252CB90: /* RetroShader: Lightmap, Diffuse, Alpha=KAlpha */ case 0x7252CB90: /* RetroShader: Lightmap, Diffuse, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Alpha"_kcola);
break;
case 0x72BEDDAC: /* RetroShader: DiffuseMod, Diffuse, Emissive, Specular, Reflection Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "DiffuseMod"_tex, "Diffuse"_tex, "Emissive"_tex, "Specular"_tex,
"Reflection"_tex);
break;
case 0x76BEA57E: /* RetroShader: Lightmap, Diffuse, Emissive, Specular, Reflection, Alpha=1.0, IndirectTex */ case 0x76BEA57E: /* RetroShader: Lightmap, Diffuse, Emissive, Specular, Reflection, Alpha=1.0, IndirectTex */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex, "Specular"_tex, "Reflection"_tex, "IndirectTex"_tex); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex, "Specular"_tex,
"Reflection"_tex, "IndirectTex"_tex);
break;
case 0x7D6A4487: /* RetroShader: Diffuse, Specular, Reflection, Alpha=DiffuseAlpha */ case 0x7D6A4487: /* RetroShader: Diffuse, Specular, Reflection, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, TexLink("Alpha", 0, true)); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, TexLink("Alpha", 0, true));
break;
case 0x81106196: /* RetroDynamicShader: Emissive, Alpha=1.0 */
_GenerateRootShader(out, "RetroDynamicShader", "Emissive"_tex);
break;
case 0x84319328: /* RetroShader: Reflection, UnusedSpecular?, Alpha=1.0 */ case 0x84319328: /* RetroShader: Reflection, UnusedSpecular?, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", WhiteColorLink("Specular"), "Reflection"_tex); break; _GenerateRootShader(out, "RetroShader", WhiteColorLink("Specular"), "Reflection"_tex);
break;
case 0x846215DA: /* RetroShader: Diffuse, Specular, Reflection, Alpha=DiffuseAlpha, IndirectTex */ case 0x846215DA: /* RetroShader: Diffuse, Specular, Reflection, Alpha=DiffuseAlpha, IndirectTex */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "IndirectTex"_tex, TexLink("Alpha", 0, true)); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "IndirectTex"_tex,
TexLink("Alpha", 0, true));
break;
case 0x8C562AB1: /* RetroShader: Diffuse, Emissive, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex);
break;
case 0x8E916C01: /* RetroShader: NULL, all inputs 0 */ case 0x8E916C01: /* RetroShader: NULL, all inputs 0 */
_GenerateRootShader(out, "RetroShader"); break; _GenerateRootShader(out, "RetroShader");
break;
case 0x957709F8: /* RetroShader: Emissive, Alpha=1.0 */ case 0x957709F8: /* RetroShader: Emissive, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Emissive"_tex); break; _GenerateRootShader(out, "RetroShader", "Emissive"_tex);
break;
case 0x96ABB2D3: /* RetroShader: Lightmap, Diffuse, Alpha=DiffuseAlpha */ case 0x96ABB2D3: /* RetroShader: Lightmap, Diffuse, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, TexLink("Alpha", 1, true)); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, TexLink("Alpha", 1, true));
break;
case 0x985A0B67: /* RetroShader: Diffuse, UnusedSpecular?, Alpha=DiffuseAlpha */ case 0x985A0B67: /* RetroShader: Diffuse, UnusedSpecular?, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, TexLink("Alpha", 0, true)); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, TexLink("Alpha", 0, true));
break;
case 0x9B4453A2: /* RetroShader: Diffuse, Emissive, ExtendedSpecular, Reflection, Alpha=1.0 */ case 0x9B4453A2: /* RetroShader: Diffuse, Emissive, ExtendedSpecular, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, ExtendedSpecularLink(), "Reflection"_tex); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, ExtendedSpecularLink(), "Reflection"_tex);
break;
case 0xA187C630: /* RetroShader: Diffuse, Emissive, UnusedReflection?, Alpha=1.0 */ case 0xA187C630: /* RetroShader: Diffuse, Emissive, UnusedReflection?, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex);
break;
case 0xB26E9E2E: /* RetroShader: Diffuse, Emissive, Alpha=1.0 */
// TODO: Last two stages assign into unused reg2, perhaps for runtime material mod?
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex);
break;
case 0xC0E3FF1F: /* RetroShader: KColorDiffuse, Specular, Reflection, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_kcol, "Specular"_tex, "Reflection"_tex, "Alpha"_kcola);
break;
case 0xC138DCFA: /* RetroShader: Diffuse, Emissive, Alpha=1.0 */ case 0xC138DCFA: /* RetroShader: Diffuse, Emissive, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex);
break;
case 0xC3C8B1C8: /* RetroShader: KColorDiffuse, Alpha=KAlpha */ case 0xC3C8B1C8: /* RetroShader: KColorDiffuse, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_kcol, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_kcol, "Alpha"_kcola);
break;
case 0xC689C8C6: /* RetroShader: Diffuse, ExtendedSpecular, Reflection, Alpha=DiffuseAlpha */ case 0xC689C8C6: /* RetroShader: Diffuse, ExtendedSpecular, Reflection, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, ExtendedSpecularLink(), "Reflection"_tex, TexLink("Alpha", 0, true)); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, ExtendedSpecularLink(), "Reflection"_tex,
TexLink("Alpha", 0, true));
break;
case 0xC6B18B28: /* RetroShader: Diffuse, Alpha=DiffuseAlpha */ case 0xC6B18B28: /* RetroShader: Diffuse, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, TexLink("Alpha", 0, true)); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, TexLink("Alpha", 0, true));
break;
case 0xCD92D4C5: /* RetroShader: Diffuse, Reflection, Alpha=KAlpha */ case 0xCD92D4C5: /* RetroShader: Diffuse, Reflection, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, WhiteColorLink("Specular"), "Reflection"_tex, "Alpha"_kcola); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, WhiteColorLink("Specular"), "Reflection"_tex, "Alpha"_kcola);
break;
case 0xCE06F3F2: /* RetroShader: Diffuse, Alpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, TexLink("Alpha", 1, true));
break;
case 0xD73E7728: /* RetroShader: ObjLightmap, Diffuse, Alpha=DiffuseAlpha */ case 0xD73E7728: /* RetroShader: ObjLightmap, Diffuse, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, TexLink("Alpha", 1, true)); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, TexLink("Alpha", 1, true));
break;
case 0xDB8F01AD: /* RetroDynamicShader: Diffuse*Dynamic, Emissive*Dynamic, UnusedSpecular?, Alpha=1.0 */ case 0xDB8F01AD: /* RetroDynamicShader: Diffuse*Dynamic, Emissive*Dynamic, UnusedSpecular?, Alpha=1.0 */
_GenerateRootShader(out, "RetroDynamicShader", "Diffuse"_tex, "Emissive"_tex); break; _GenerateRootShader(out, "RetroDynamicShader", "Diffuse"_tex, "Emissive"_tex);
break;
case 0xE64D1085: /* RetroShader: Lightmap, Diffuse, Emissive, Reflection, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex, "Reflection"_tex,
TexLink("Alpha", 1, true));
break;
case 0xE6784B10: /* RetroShader: Lightmap, Diffuse, Specular, Reflection, Alpha=DiffuseAlpha, IndirectTex */ case 0xE6784B10: /* RetroShader: Lightmap, Diffuse, Specular, Reflection, Alpha=DiffuseAlpha, IndirectTex */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "Reflection"_tex, "IndirectTex"_tex, TexLink("Alpha", 1, true)); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "Reflection"_tex,
"IndirectTex"_tex, TexLink("Alpha", 1, true));
break;
case 0xE68FF182: /* RetroShader: Diffuse, Emissive, Specular, Reflection, Alpha=1.0 */ case 0xE68FF182: /* RetroShader: Diffuse, Emissive, Specular, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, "Specular"_tex, "Reflection"_tex); break; _GenerateRootShader(out, "RetroShader", "Diffuse"_tex, "Emissive"_tex, "Specular"_tex, "Reflection"_tex);
break;
case 0xE92F1340: /* RetroShader: Diffuse, Alpha=DiffuseAlpha*AlphaMod */
_GenerateRootShader(out, "RetroShader", "Diffuse"_tex, TexLink("Alpha", 0, true), TexLink("AlphaMod", 1, true));
break;
case 0xEB4645CF: /* RetroDynamicAlphaShader: Diffuse*Dynamic, Alpha=DiffuseAlpha*Dynamic */ case 0xEB4645CF: /* RetroDynamicAlphaShader: Diffuse*Dynamic, Alpha=DiffuseAlpha*Dynamic */
_GenerateRootShader(out, "RetroDynamicAlphaShader", "Diffuse"_tex, TexLink("Alpha", 0, true)); break; _GenerateRootShader(out, "RetroDynamicAlphaShader", "Diffuse"_tex, TexLink("Alpha", 0, true));
break;
case 0xECEF8D1F: /* RetroDynamicShader: Diffuse*Dynamic, Emissive*Dynamic, Specular, Reflection, Alpha=1.0 */ case 0xECEF8D1F: /* RetroDynamicShader: Diffuse*Dynamic, Emissive*Dynamic, Specular, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroDynamicShader", "Diffuse"_tex, "Emissive"_tex, "Specular"_tex, "Reflection"_tex); break; _GenerateRootShader(out, "RetroDynamicShader", "Diffuse"_tex, "Emissive"_tex, "Specular"_tex, "Reflection"_tex);
break;
case 0xF1C26570: /* RetroShader: Lightmap, Diffuse, Specular, ExtendedSpecular, Reflection, Alpha=DiffuseAlpha */ case 0xF1C26570: /* RetroShader: Lightmap, Diffuse, Specular, ExtendedSpecular, Reflection, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "ExtendedSpecular"_tex, "Reflection"_tex, TexLink("Alpha", 1, true)); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Specular"_tex, "ExtendedSpecular"_tex,
"Reflection"_tex, TexLink("Alpha", 1, true));
break;
case 0xF345C16E: /* RetroShader: Emissive, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Emissive"_tex, "Reflection"_tex);
break;
case 0xF4DA0A86: /* RetroShader: KColorDiffuse, Emissive, Alpha=KAlpha */
_GenerateRootShader(out, "RetroShader", "Diffuse"_kcol, "Emissive"_tex, "Alpha"_kcola); break;
break;
case 0xF559DB08: /* RetroShader: Lightmap, Diffuse, Emissive, Specular, Reflection, Alpha=1.0 */ case 0xF559DB08: /* RetroShader: Lightmap, Diffuse, Emissive, Specular, Reflection, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex, "Specular"_tex, "Reflection"_tex); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex, "Specular"_tex,
"Reflection"_tex);
break;
case 0xF9324367: /* RetroShader: Lightmap, Diffuse, Emissive, Alpha=1.0 */ case 0xF9324367: /* RetroShader: Lightmap, Diffuse, Emissive, Alpha=1.0 */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex);
break;
case 0xFC2761B8: /* RetroShader: Lightmap, Diffuse, Alpha=DiffuseAlpha*AlphaMod */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, TexLink("Alpha", 1, true),
TexLink("AlphaMod", 2, true));
break;
case 0xFD95D7FD: /* RetroShader: ObjLightmap, Diffuse, Alpha=DiffuseAlpha */ case 0xFD95D7FD: /* RetroShader: ObjLightmap, Diffuse, Alpha=DiffuseAlpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, TexLink("Alpha", 1, true)); break; _GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, TexLink("Alpha", 1, true));
break;
case 0xFFF3CEBB: /* RetroShader: Lightmap, Diffuse, Emissive, Alpha */
_GenerateRootShader(out, "RetroShader", "Lightmap"_tex, "Diffuse"_tex, "Emissive"_tex, TexLink("Alpha", 3, true));
break;
default: default:
_DescribeTEV(material); _DescribeTEV(material);
Log.report(logvisor::Fatal, fmt("Unable to resolve shader hash {:08X}\n"), hash); break; Log.report(logvisor::Fatal, FMT_STRING("Unable to resolve shader hash {:08X}\n"), hash);
break;
} }
/* Has Lightmap? */ /* Has Lightmap? */
@ -682,8 +806,7 @@ void MaterialSet::ConstructMaterial(Stream& out, const MaterialSet::Material& ma
_ConstructMaterial(out, material, groupIdx, matIdx); _ConstructMaterial(out, material, groupIdx, matIdx);
} }
MaterialSet::Material::Material(const hecl::blender::Material& mat, MaterialSet::Material::Material(const hecl::blender::Material& mat, std::vector<hecl::ProjectPath>& texPathsOut,
std::vector<hecl::ProjectPath>& texPathsOut,
int colorCount, bool lightmapUVs, bool matrixSkinning) { int colorCount, bool lightmapUVs, bool matrixSkinning) {
/* TODO: Rewrite for new shader rep */ /* TODO: Rewrite for new shader rep */
XXH32_state_t xxHash; XXH32_state_t xxHash;
@ -1009,7 +1132,7 @@ MaterialSet::Material::UVAnimation::UVAnimation(const std::string& gameFunction,
else if (gameFunction == "RetroUVMode2Node") { else if (gameFunction == "RetroUVMode2Node") {
mode = Mode::Scroll; mode = Mode::Scroll;
if (gameArgs.size() < 2) if (gameArgs.size() < 2)
Log.report(logvisor::Fatal, fmt("Mode2 UV anim requires 2 vector arguments")); Log.report(logvisor::Fatal, FMT_STRING("Mode2 UV anim requires 2 vector arguments"));
vals[0] = gameArgs[0].simd[0]; vals[0] = gameArgs[0].simd[0];
vals[1] = gameArgs[0].simd[1]; vals[1] = gameArgs[0].simd[1];
vals[2] = gameArgs[1].simd[0]; vals[2] = gameArgs[1].simd[0];
@ -1017,13 +1140,13 @@ MaterialSet::Material::UVAnimation::UVAnimation(const std::string& gameFunction,
} else if (gameFunction == "RetroUVMode3Node") { } else if (gameFunction == "RetroUVMode3Node") {
mode = Mode::Rotation; mode = Mode::Rotation;
if (gameArgs.size() < 2) if (gameArgs.size() < 2)
Log.report(logvisor::Fatal, fmt("Mode3 UV anim requires 2 arguments")); Log.report(logvisor::Fatal, FMT_STRING("Mode3 UV anim requires 2 arguments"));
vals[0] = gameArgs[0].simd[0]; vals[0] = gameArgs[0].simd[0];
vals[1] = gameArgs[1].simd[0]; vals[1] = gameArgs[1].simd[0];
} else if (gameFunction == "RetroUVMode4Node") { } else if (gameFunction == "RetroUVMode4Node") {
mode = Mode::HStrip; mode = Mode::HStrip;
if (gameArgs.size() < 4) if (gameArgs.size() < 4)
Log.report(logvisor::Fatal, fmt("Mode4 UV anim requires 4 arguments")); Log.report(logvisor::Fatal, FMT_STRING("Mode4 UV anim requires 4 arguments"));
vals[0] = gameArgs[0].simd[0]; vals[0] = gameArgs[0].simd[0];
vals[1] = gameArgs[1].simd[0]; vals[1] = gameArgs[1].simd[0];
vals[2] = gameArgs[2].simd[0]; vals[2] = gameArgs[2].simd[0];
@ -1031,7 +1154,7 @@ MaterialSet::Material::UVAnimation::UVAnimation(const std::string& gameFunction,
} else if (gameFunction == "RetroUVMode5Node") { } else if (gameFunction == "RetroUVMode5Node") {
mode = Mode::VStrip; mode = Mode::VStrip;
if (gameArgs.size() < 4) if (gameArgs.size() < 4)
Log.report(logvisor::Fatal, fmt("Mode5 UV anim requires 4 arguments")); Log.report(logvisor::Fatal, FMT_STRING("Mode5 UV anim requires 4 arguments"));
vals[0] = gameArgs[0].simd[0]; vals[0] = gameArgs[0].simd[0];
vals[1] = gameArgs[1].simd[0]; vals[1] = gameArgs[1].simd[0];
vals[2] = gameArgs[2].simd[0]; vals[2] = gameArgs[2].simd[0];
@ -1041,11 +1164,11 @@ MaterialSet::Material::UVAnimation::UVAnimation(const std::string& gameFunction,
else if (gameFunction == "RetroUVMode7NodeN") { else if (gameFunction == "RetroUVMode7NodeN") {
mode = Mode::CylinderEnvironment; mode = Mode::CylinderEnvironment;
if (gameArgs.size() < 2) if (gameArgs.size() < 2)
Log.report(logvisor::Fatal, fmt("Mode7 UV anim requires 2 arguments")); Log.report(logvisor::Fatal, FMT_STRING("Mode7 UV anim requires 2 arguments"));
vals[0] = gameArgs[0].simd[0]; vals[0] = gameArgs[0].simd[0];
vals[1] = gameArgs[1].simd[0]; vals[1] = gameArgs[1].simd[0];
} else } else
Log.report(logvisor::Fatal, fmt("unsupported UV anim '{}'"), gameFunction); Log.report(logvisor::Fatal, FMT_STRING("unsupported UV anim '{}'"), gameFunction);
} }
template <class Op> template <class Op>
@ -1090,7 +1213,6 @@ void HMDLMaterialSet::Material::PASS::Enumerate(typename Op::StreamT& s) {
AT_SPECIALIZE_DNA(HMDLMaterialSet::Material::PASS) AT_SPECIALIZE_DNA(HMDLMaterialSet::Material::PASS)
std::string_view HMDLMaterialSet::Material::PASS::DNAType() { std::string_view HMDLMaterialSet::Material::PASS::DNAType() {
return "DataSpec::DNAMP1::HMDLMaterialSet::Material::PASS"sv; return "DataSpec::DNAMP1::HMDLMaterialSet::Material::PASS"sv;
} }

View File

@ -506,9 +506,9 @@ struct MaterialSet : BigDNA {
continue; continue;
} }
if (setIdx < 0) if (setIdx < 0)
texEntry->name = fmt::format(fmt("{}_{}_{}"), prefix, matIdx, stageIdx); texEntry->name = fmt::format(FMT_STRING("{}_{}_{}"), prefix, matIdx, stageIdx);
else else
texEntry->name = fmt::format(fmt("{}_{}_{}_{}"), prefix, setIdx, matIdx, stageIdx); texEntry->name = fmt::format(FMT_STRING("{}_{}_{}_{}"), prefix, setIdx, matIdx, stageIdx);
if (mat.flags.lightmap() && stageIdx == 0) { if (mat.flags.lightmap() && stageIdx == 0) {
texEntry->name += "light"; texEntry->name += "light";

View File

@ -11,7 +11,6 @@ make_dnalist(PAK
CINF CINF
CSKR CSKR
EVNT EVNT
PATH
CMDLMaterials CMDLMaterials
MREA MREA
DeafBabe DeafBabe
@ -50,7 +49,7 @@ set(DNAMP1_SOURCES
ANIM.cpp ANIM.cpp
CINF.cpp CINF.cpp
EVNT.cpp EVNT.cpp
PATH.cpp PATH.hpp
CMDL.hpp CMDL.cpp CMDL.hpp CMDL.cpp
CMDLMaterials.cpp CMDLMaterials.cpp
DCLN.cpp DCLN.cpp

View File

@ -8,7 +8,7 @@ void CSKR::weightVertex(hecl::blender::PyOutStream& os, const CINF& cinf, atUint
for (const SkinningRule& rule : skinningRules) { for (const SkinningRule& rule : skinningRules) {
if (idx >= accum && idx < accum + rule.vertCount) if (idx >= accum && idx < accum + rule.vertCount)
for (const SkinningRule::Weight& weight : rule.weights) for (const SkinningRule::Weight& weight : rule.weights)
os.format(fmt("vert[dvert_lay][{}] = {}\n"), cinf.getBoneIdxFromId(weight.boneId), weight.weight); os.format(FMT_STRING("vert[dvert_lay][{}] = {}\n"), cinf.getBoneIdxFromId(weight.boneId), weight.weight);
accum += rule.vertCount; accum += rule.vertCount;
} }
} }

View File

@ -44,8 +44,8 @@ bool CSNG::Extract(PAKEntryReadStream& rs, const hecl::ProjectPath& outPath) {
r.emplace(songsPath.getAbsolutePath()); r.emplace(songsPath.getAbsolutePath());
athena::io::YAMLDocWriter ydw("amuse::Songs", r ? &*r : nullptr); athena::io::YAMLDocWriter ydw("amuse::Songs", r ? &*r : nullptr);
r = std::nullopt; r = std::nullopt;
ydw.writeString(fmt::format(fmt("{:04X}"), head.midiSetupId), ydw.writeString(fmt::format(FMT_STRING("{:04X}"), head.midiSetupId),
fmt::format(fmt("../MidiData/{}"), midPath.getLastComponentUTF8())); fmt::format(FMT_STRING("../MidiData/{}"), midPath.getLastComponentUTF8()));
athena::io::FileWriter w(songsPath.getAbsolutePath()); athena::io::FileWriter w(songsPath.getAbsolutePath());
ydw.finish(&w); ydw.finish(&w);

View File

@ -30,7 +30,7 @@ void DCLN::Collision::Node::sendToBlender(hecl::blender::PyOutStream& os) const
void DCLN::sendToBlender(hecl::blender::Connection& conn, std::string_view entryName) { void DCLN::sendToBlender(hecl::blender::Connection& conn, std::string_view entryName) {
/* Open Py Stream and read sections */ /* Open Py Stream and read sections */
hecl::blender::PyOutStream os = conn.beginPythonOut(true); hecl::blender::PyOutStream os = conn.beginPythonOut(true);
os.format(fmt( os.format(FMT_STRING(
"import bpy\n" "import bpy\n"
"import bmesh\n" "import bmesh\n"
"from mathutils import Vector, Matrix\n" "from mathutils import Vector, Matrix\n"

View File

@ -30,6 +30,7 @@
#include "PATH.hpp" #include "PATH.hpp"
#include "DataSpec/DNACommon/Tweaks/TweakWriter.hpp" #include "DataSpec/DNACommon/Tweaks/TweakWriter.hpp"
#include "DataSpec/DNACommon/URDEVersionInfo.hpp"
#include "Tweaks/CTweakPlayerRes.hpp" #include "Tweaks/CTweakPlayerRes.hpp"
#include "Tweaks/CTweakGunRes.hpp" #include "Tweaks/CTweakGunRes.hpp"
#include "Tweaks/CTweakPlayer.hpp" #include "Tweaks/CTweakPlayer.hpp"
@ -154,7 +155,7 @@ void PAKBridge::build() {
std::string idStr = area.areaMREAId.toString(); std::string idStr = area.areaMREAId.toString();
areaDeps.name = hecl::SystemString(_SYS_STR("MREA_")) + hecl::SystemStringConv(idStr).c_str(); areaDeps.name = hecl::SystemString(_SYS_STR("MREA_")) + hecl::SystemStringConv(idStr).c_str();
} }
hecl::SystemString num = fmt::format(fmt(_SYS_STR("{:02d} ")), ai); hecl::SystemString num = fmt::format(FMT_STRING(_SYS_STR("{:02d} ")), ai);
areaDeps.name = num + areaDeps.name; areaDeps.name = num + areaDeps.name;
std::string lowerName(hecl::SystemUTF8Conv(areaDeps.name).str()); std::string lowerName(hecl::SystemUTF8Conv(areaDeps.name).str());
@ -178,7 +179,7 @@ void PAKBridge::build() {
layer.active = layerFlags.flags >> (l - 1) & 0x1; layer.active = layerFlags.flags >> (l - 1) & 0x1;
layer.name = hecl::StringUtils::TrimWhitespace(layer.name); layer.name = hecl::StringUtils::TrimWhitespace(layer.name);
num = fmt::format(fmt(_SYS_STR("{:02d} ")), l - 1); num = fmt::format(FMT_STRING(_SYS_STR("{:02d} ")), l - 1);
layer.name = num + layer.name; layer.name = num + layer.name;
layer.resources.reserve(area.depLayers[l] - r); layer.resources.reserve(area.depLayers[l] - r);
@ -210,35 +211,35 @@ void PAKBridge::addCMDLRigPairs(PAKRouter<PAKBridge>& pakRouter, CharacterAssoci
for (const ANCS::CharacterSet::CharacterInfo& ci : ancs.characterSet.characters) { for (const ANCS::CharacterSet::CharacterInfo& ci : ancs.characterSet.characters) {
charAssoc.m_cmdlRigs[ci.cmdl] = {ci.cskr, ci.cinf}; charAssoc.m_cmdlRigs[ci.cmdl] = {ci.cskr, ci.cinf};
charAssoc.m_cskrToCharacter[ci.cskr] = charAssoc.m_cskrToCharacter[ci.cskr] =
std::make_pair(entry.id, fmt::format(fmt("{}_{}.CSKR"), ci.name, ci.cskr)); std::make_pair(entry.id, fmt::format(FMT_STRING("{}_{}.CSKR"), ci.name, ci.cskr));
PAK::Entry* cmdlEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cmdl); PAK::Entry* cmdlEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cmdl);
PAK::Entry* cskrEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cskr); PAK::Entry* cskrEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cskr);
PAK::Entry* cinfEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cinf); PAK::Entry* cinfEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cinf);
cmdlEnt->name = fmt::format(fmt("ANCS_{}_{}_model"), id, ci.name); cmdlEnt->name = fmt::format(FMT_STRING("ANCS_{}_{}_model"), id, ci.name);
cskrEnt->name = fmt::format(fmt("ANCS_{}_{}_skin"), id, ci.name); cskrEnt->name = fmt::format(FMT_STRING("ANCS_{}_{}_skin"), id, ci.name);
cinfEnt->name = fmt::format(fmt("ANCS_{}_{}_skel"), id, ci.name); cinfEnt->name = fmt::format(FMT_STRING("ANCS_{}_{}_skel"), id, ci.name);
if (ci.cmdlIce.isValid() && ci.cskrIce.isValid()) { if (ci.cmdlIce.isValid() && ci.cskrIce.isValid()) {
charAssoc.m_cmdlRigs[ci.cmdlIce] = {ci.cskrIce, ci.cinf}; charAssoc.m_cmdlRigs[ci.cmdlIce] = {ci.cskrIce, ci.cinf};
charAssoc.m_cskrToCharacter[ci.cskrIce] = charAssoc.m_cskrToCharacter[ci.cskrIce] =
std::make_pair(entry.id, fmt::format(fmt("{}.ICE_{}.CSKR"), ci.name, ci.cskrIce)); std::make_pair(entry.id, fmt::format(FMT_STRING("{}.ICE_{}.CSKR"), ci.name, ci.cskrIce));
PAK::Entry* cmdlEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cmdlIce); PAK::Entry* cmdlEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cmdlIce);
PAK::Entry* cskrEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cskrIce); PAK::Entry* cskrEnt = (PAK::Entry*)m_pak.lookupEntry(ci.cskrIce);
cmdlEnt->name = fmt::format(fmt("ANCS_{}_{}_icemodel"), id, ci.name); cmdlEnt->name = fmt::format(FMT_STRING("ANCS_{}_{}_icemodel"), id, ci.name);
cskrEnt->name = fmt::format(fmt("ANCS_{}_{}_iceskin"), id, ci.name); cskrEnt->name = fmt::format(FMT_STRING("ANCS_{}_{}_iceskin"), id, ci.name);
} }
} }
std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>> animInfo; std::map<atUint32, DNAANCS::AnimationResInfo<UniqueID32>> animInfo;
ancs.getAnimationResInfo(&pakRouter, animInfo); ancs.getAnimationResInfo(&pakRouter, animInfo);
for (auto& [animIdx, animResInfo] : animInfo) { for (auto& [animIdx, animResInfo] : animInfo) {
PAK::Entry* animEnt = (PAK::Entry*)m_pak.lookupEntry(animResInfo.animId); PAK::Entry* animEnt = (PAK::Entry*)m_pak.lookupEntry(animResInfo.animId);
animEnt->name = fmt::format(fmt("ANCS_{}_{}"), id, animResInfo.name); animEnt->name = fmt::format(FMT_STRING("ANCS_{}_{}"), id, animResInfo.name);
charAssoc.m_cskrToCharacter[animResInfo.animId] = charAssoc.m_cskrToCharacter[animResInfo.animId] =
std::make_pair(entry.id, fmt::format(fmt("{}_{}.ANIM"), animResInfo.name, animResInfo.animId)); std::make_pair(entry.id, fmt::format(FMT_STRING("{}_{}.ANIM"), animResInfo.name, animResInfo.animId));
if (animResInfo.evntId.isValid()) { if (animResInfo.evntId.isValid()) {
PAK::Entry* evntEnt = (PAK::Entry*)m_pak.lookupEntry(animResInfo.evntId); PAK::Entry* evntEnt = (PAK::Entry*)m_pak.lookupEntry(animResInfo.evntId);
evntEnt->name = fmt::format(fmt("ANCS_{}_{}_evnt"), id, animResInfo.name); evntEnt->name = fmt::format(FMT_STRING("ANCS_{}_{}_evnt"), id, animResInfo.name);
charAssoc.m_cskrToCharacter[animResInfo.evntId] = charAssoc.m_cskrToCharacter[animResInfo.evntId] = std::make_pair(
std::make_pair(entry.id, fmt::format(fmt("{}_{}.evnt.yaml"), animResInfo.name, animResInfo.evntId)); entry.id, fmt::format(FMT_STRING("{}_{}.evnt.yaml"), animResInfo.name, animResInfo.evntId));
} }
} }
} else if (entry.type == FOURCC('MREA')) { } else if (entry.type == FOURCC('MREA')) {
@ -275,8 +276,8 @@ void PAKBridge::addMAPATransforms(PAKRouter<PAKBridge>& pakRouter,
hecl::ProjectPath mlvlDirPath = pakRouter.getWorking(&entry).getParentPath(); hecl::ProjectPath mlvlDirPath = pakRouter.getWorking(&entry).getParentPath();
if (mlvl.worldNameId.isValid()) if (mlvl.worldNameId.isValid())
pathOverrides[mlvl.worldNameId] = hecl::ProjectPath(mlvlDirPath, pathOverrides[mlvl.worldNameId] =
fmt::format(fmt(_SYS_STR("!name_{}.yaml")), mlvl.worldNameId)); hecl::ProjectPath(mlvlDirPath, fmt::format(FMT_STRING(_SYS_STR("!name_{}.yaml")), mlvl.worldNameId));
for (const MLVL::Area& area : mlvl.areas) { for (const MLVL::Area& area : mlvl.areas) {
{ {
@ -292,8 +293,8 @@ void PAKBridge::addMAPATransforms(PAKRouter<PAKBridge>& pakRouter,
hecl::ProjectPath areaDirPath = pakRouter.getWorking(area.areaMREAId).getParentPath(); hecl::ProjectPath areaDirPath = pakRouter.getWorking(area.areaMREAId).getParentPath();
if (area.areaNameId.isValid()) if (area.areaNameId.isValid())
pathOverrides[area.areaNameId] = hecl::ProjectPath(areaDirPath, pathOverrides[area.areaNameId] =
fmt::format(fmt(_SYS_STR("!name_{}.yaml")), area.areaNameId)); hecl::ProjectPath(areaDirPath, fmt::format(FMT_STRING(_SYS_STR("!name_{}.yaml")), area.areaNameId));
} }
if (mlvl.worldMap.isValid()) { if (mlvl.worldMap.isValid()) {
@ -383,8 +384,14 @@ ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const nod::Node& pakNode, con
std::string catalogueName; std::string catalogueName;
std::string name = pak.bestEntryName(pakNode, entry, catalogueName); std::string name = pak.bestEntryName(pakNode, entry, catalogueName);
if (!catalogueName.empty()) { if (!catalogueName.empty()) {
if (catalogueName == "PlayerRes"sv) if (catalogueName == "PlayerRes"sv) {
return {ExtractTweak<CTweakPlayerRes>, {_SYS_STR(".yaml")}}; if (isCurrentSpecWii() || getCurrentRegion() == ERegion::PAL || getCurrentRegion() == ERegion::NTSC_J) {
/* We need to use the new rep for these tweaks */
return {ExtractTweak<CTweakPlayerRes<true>>, {_SYS_STR(".yaml")}};
}
/* We need to use the old rep for these tweaks */
return {ExtractTweak<CTweakPlayerRes<false>>, {_SYS_STR(".yaml")}};
}
if (catalogueName == "GunRes"sv) if (catalogueName == "GunRes"sv)
return {ExtractTweak<CTweakGunRes>, {_SYS_STR(".yaml")}}; return {ExtractTweak<CTweakGunRes>, {_SYS_STR(".yaml")}};
if (catalogueName == "Player"sv) if (catalogueName == "Player"sv)
@ -395,8 +402,14 @@ ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const nod::Node& pakNode, con
return {ExtractTweak<CTweakSlideShow>, {_SYS_STR(".yaml")}}; return {ExtractTweak<CTweakSlideShow>, {_SYS_STR(".yaml")}};
if (catalogueName == "Game"sv) if (catalogueName == "Game"sv)
return {ExtractTweak<CTweakGame>, {_SYS_STR(".yaml")}}; return {ExtractTweak<CTweakGame>, {_SYS_STR(".yaml")}};
if (catalogueName == "Targeting"sv) if (catalogueName == "Targeting"sv) {
return {ExtractTweak<CTweakTargeting>, {_SYS_STR(".yaml")}}; if (isCurrentSpecWii() || getCurrentRegion() == ERegion::PAL || getCurrentRegion() == ERegion::NTSC_J) {
/* We need to use the new rep for these tweaks */
return {ExtractTweak<CTweakTargeting<true>>, {_SYS_STR(".yaml")}};
}
/* We need to use the old rep for these tweaks */
return {ExtractTweak<CTweakTargeting<false>>, {_SYS_STR(".yaml")}};
}
if (catalogueName == "Gui"sv) if (catalogueName == "Gui"sv)
return {ExtractTweak<CTweakGui>, {_SYS_STR(".yaml")}}; return {ExtractTweak<CTweakGui>, {_SYS_STR(".yaml")}};
if (catalogueName == "AutoMapper"sv) if (catalogueName == "AutoMapper"sv)

View File

@ -93,7 +93,7 @@ void FRME::Widget::Enumerate<BigDNA::Read>(athena::io::IStreamReader& __dna_read
widgetInfo = std::make_unique<SLGPInfo>(); widgetInfo = std::make_unique<SLGPInfo>();
break; break;
default: default:
Log.report(logvisor::Fatal, fmt(_SYS_STR("Unsupported FRME widget type {}")), type); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unsupported FRME widget type {}")), type);
} }
/* widgetInfo */ /* widgetInfo */
@ -174,7 +174,7 @@ void FRME::Widget::CAMRInfo::Enumerate<BigDNA::Read>(athena::io::IStreamReader&
} else if (projectionType == ProjectionType::Orthographic) { } else if (projectionType == ProjectionType::Orthographic) {
projection = std::make_unique<OrthographicProjection>(); projection = std::make_unique<OrthographicProjection>();
} else { } else {
Log.report(logvisor::Fatal, fmt(_SYS_STR("Invalid CAMR projection mode! {}")), int(projectionType)); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Invalid CAMR projection mode! {}")), int(projectionType));
} }
projection->read(__dna_reader); projection->read(__dna_reader);
@ -183,9 +183,9 @@ void FRME::Widget::CAMRInfo::Enumerate<BigDNA::Read>(athena::io::IStreamReader&
template <> template <>
void FRME::Widget::CAMRInfo::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& __dna_writer) { void FRME::Widget::CAMRInfo::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& __dna_writer) {
if (!projection) if (!projection)
Log.report(logvisor::Fatal, fmt(_SYS_STR("Invalid CAMR projection object!"))); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Invalid CAMR projection object!")));
if (projection->type != projectionType) if (projection->type != projectionType)
Log.report(logvisor::Fatal, fmt(_SYS_STR("CAMR projection type does not match actual projection type!"))); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("CAMR projection type does not match actual projection type!")));
__dna_writer.writeUint32Big(atUint32(projectionType)); __dna_writer.writeUint32Big(atUint32(projectionType));
projection->write(__dna_writer); projection->write(__dna_writer);
@ -306,7 +306,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
" bpy.context.scene.collection.objects.link(ob_new)\n" " bpy.context.scene.collection.objects.link(ob_new)\n"
" return ob_new\n"; " return ob_new\n";
os.format(fmt( os.format(FMT_STRING(
"bpy.context.scene.name = '{}'\n" "bpy.context.scene.name = '{}'\n"
"bpy.context.scene.render.resolution_x = 640\n" "bpy.context.scene.render.resolution_x = 640\n"
"bpy.context.scene.render.resolution_y = 480\n" "bpy.context.scene.render.resolution_y = 480\n"
@ -321,7 +321,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
"angle = Quaternion((1.0, 0.0, 0.0), 0)\n"; "angle = Quaternion((1.0, 0.0, 0.0), 0)\n";
if (w.type == SBIG('CAMR')) { if (w.type == SBIG('CAMR')) {
using CAMRInfo = Widget::CAMRInfo; using CAMRInfo = Widget::CAMRInfo;
os.format(fmt( os.format(FMT_STRING(
"cam = bpy.data.cameras.new(name='{}')\n" "cam = bpy.data.cameras.new(name='{}')\n"
"binding = cam\n"), "binding = cam\n"),
w.header.name); w.header.name);
@ -329,7 +329,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
if (info->projectionType == CAMRInfo::ProjectionType::Orthographic) { if (info->projectionType == CAMRInfo::ProjectionType::Orthographic) {
CAMRInfo::OrthographicProjection* proj = CAMRInfo::OrthographicProjection* proj =
static_cast<CAMRInfo::OrthographicProjection*>(info->projection.get()); static_cast<CAMRInfo::OrthographicProjection*>(info->projection.get());
os.format(fmt( os.format(FMT_STRING(
"cam.type = 'ORTHO'\n" "cam.type = 'ORTHO'\n"
"cam.ortho_scale = {}\n" "cam.ortho_scale = {}\n"
"cam.clip_start = {}\n" "cam.clip_start = {}\n"
@ -337,7 +337,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
std::fabs(proj->right - proj->left), proj->znear, proj->zfar); std::fabs(proj->right - proj->left), proj->znear, proj->zfar);
} else if (info->projectionType == CAMRInfo::ProjectionType::Perspective) { } else if (info->projectionType == CAMRInfo::ProjectionType::Perspective) {
CAMRInfo::PerspectiveProjection* proj = static_cast<CAMRInfo::PerspectiveProjection*>(info->projection.get()); CAMRInfo::PerspectiveProjection* proj = static_cast<CAMRInfo::PerspectiveProjection*>(info->projection.get());
os.format(fmt( os.format(FMT_STRING(
"cam.type = 'PERSP'\n" "cam.type = 'PERSP'\n"
"cam.lens_unit = 'FOV'\n" "cam.lens_unit = 'FOV'\n"
"cam.clip_start = {}\n" "cam.clip_start = {}\n"
@ -345,10 +345,10 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
"bpy.context.scene.render.resolution_x = 480 * {}\n"), "bpy.context.scene.render.resolution_x = 480 * {}\n"),
proj->znear, proj->zfar, proj->aspect); proj->znear, proj->zfar, proj->aspect);
if (proj->aspect > 1.f) if (proj->aspect > 1.f)
os.format(fmt("cam.angle = math.atan2({}, 1.0 / math.tan(math.radians({} / 2.0))) * 2.0\n"), proj->aspect, os.format(FMT_STRING("cam.angle = math.atan2({}, 1.0 / math.tan(math.radians({} / 2.0))) * 2.0\n"), proj->aspect,
proj->fov); proj->fov);
else else
os.format(fmt("cam.angle = math.radians({})\n"), proj->fov); os.format(FMT_STRING("cam.angle = math.radians({})\n"), proj->fov);
} }
} }
os << "angle = Quaternion((1.0, 0.0, 0.0), math.radians(90.0))\n"; os << "angle = Quaternion((1.0, 0.0, 0.0), math.radians(90.0))\n";
@ -358,7 +358,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
switch (info->type) { switch (info->type) {
case LITEInfo::ELightType::LocalAmbient: { case LITEInfo::ELightType::LocalAmbient: {
zeus::simd_floats colorF(w.header.color.simd); zeus::simd_floats colorF(w.header.color.simd);
os.format(fmt( os.format(FMT_STRING(
"bg_node.inputs[0].default_value = ({},{},{},1.0)\n" "bg_node.inputs[0].default_value = ({},{},{},1.0)\n"
"bg_node.inputs[1].default_value = {}\n"), "bg_node.inputs[1].default_value = {}\n"),
colorF[0], colorF[1], colorF[2], info->distQ / 8.0); colorF[0], colorF[1], colorF[2], info->distQ / 8.0);
@ -370,7 +370,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
[[fallthrough]]; [[fallthrough]];
default: { default: {
zeus::simd_floats colorF(w.header.color.simd); zeus::simd_floats colorF(w.header.color.simd);
os.format(fmt( os.format(FMT_STRING(
"lamp = bpy.data.lights.new(name='{}', type='POINT')\n" "lamp = bpy.data.lights.new(name='{}', type='POINT')\n"
"lamp.color = ({}, {}, {})\n" "lamp.color = ({}, {}, {})\n"
"lamp.hecl_falloff_constant = {}\n" "lamp.hecl_falloff_constant = {}\n"
@ -384,7 +384,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
w.header.name, colorF[0], colorF[1], colorF[2], info->distC, info->distL, info->distQ, info->angC, w.header.name, colorF[0], colorF[1], colorF[2], info->distC, info->distL, info->distQ, info->angC,
info->angL, info->angQ, info->loadedIdx); info->angL, info->angQ, info->loadedIdx);
if (info->type == LITEInfo::ELightType::Spot) if (info->type == LITEInfo::ELightType::Spot)
os.format(fmt( os.format(FMT_STRING(
"lamp.type = 'SPOT'\n" "lamp.type = 'SPOT'\n"
"lamp.spot_size = {}\n"), "lamp.spot_size = {}\n"),
info->cutoff); info->cutoff);
@ -413,7 +413,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
if (resPath.size()) { if (resPath.size()) {
hecl::SystemUTF8Conv resPathView(resPath); hecl::SystemUTF8Conv resPathView(resPath);
os.format(fmt( os.format(FMT_STRING(
"if '{}' in bpy.data.images:\n" "if '{}' in bpy.data.images:\n"
" image = bpy.data.images['{}']\n" " image = bpy.data.images['{}']\n"
"else:\n" "else:\n"
@ -424,7 +424,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
os << "image = None\n"; os << "image = None\n";
} }
os.format(fmt( os.format(FMT_STRING(
"material = bpy.data.materials.new('{}')\n" "material = bpy.data.materials.new('{}')\n"
"material.use_nodes = True\n" "material.use_nodes = True\n"
"new_nodetree = material.node_tree\n" "new_nodetree = material.node_tree\n"
@ -445,7 +445,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
else else
ti = i; ti = i;
zeus::simd_floats f(info->quadCoords[ti].simd); zeus::simd_floats f(info->quadCoords[ti].simd);
os.format(fmt("verts.append(bm.verts.new(({},{},{})))\n"), f[0], f[1], f[2]); os.format(FMT_STRING("verts.append(bm.verts.new(({},{},{})))\n"), f[0], f[1], f[2]);
} }
os << "bm.faces.new(verts)\n" os << "bm.faces.new(verts)\n"
"bm.loops.layers.uv.new('UV')\n" "bm.loops.layers.uv.new('UV')\n"
@ -459,9 +459,9 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
else else
ti = i; ti = i;
zeus::simd_floats f(info->uvCoords[ti].simd); zeus::simd_floats f(info->uvCoords[ti].simd);
os.format(fmt("bm.verts[{}].link_loops[0][bm.loops.layers.uv[0]].uv = ({},{})\n"), i, f[0], f[1]); os.format(FMT_STRING("bm.verts[{}].link_loops[0][bm.loops.layers.uv[0]].uv = ({},{})\n"), i, f[0], f[1]);
} }
os.format(fmt( os.format(FMT_STRING(
"binding = bpy.data.meshes.new('{}')\n" "binding = bpy.data.meshes.new('{}')\n"
"bm.to_mesh(binding)\n" "bm.to_mesh(binding)\n"
"bm.free()\n" "bm.free()\n"
@ -471,7 +471,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
} }
zeus::simd_floats colorF(w.header.color.simd); zeus::simd_floats colorF(w.header.color.simd);
os.format(fmt( os.format(FMT_STRING(
"frme_obj = bpy.data.objects.new(name='{}', object_data=binding)\n" "frme_obj = bpy.data.objects.new(name='{}', object_data=binding)\n"
"frme_obj.pass_index = {}\n" "frme_obj.pass_index = {}\n"
"parentName = '{}'\n" "parentName = '{}'\n"
@ -501,7 +501,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
os.linkMesh(modelPath.getAbsolutePathUTF8(), pakRouter.getBestEntryName(*cmdlE)); os.linkMesh(modelPath.getAbsolutePathUTF8(), pakRouter.getBestEntryName(*cmdlE));
os.format(fmt("frme_obj.retro_model_light_mask = {}\n"), info->lightMask); os.format(FMT_STRING("frme_obj.retro_model_light_mask = {}\n"), info->lightMask);
os << "print(obj.name)\n" os << "print(obj.name)\n"
"copy_obj = duplicateObject(obj)\n" "copy_obj = duplicateObject(obj)\n"
"copy_obj.parent = frme_obj\n" "copy_obj.parent = frme_obj\n"
@ -516,7 +516,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
using PANEInfo = Widget::PANEInfo; using PANEInfo = Widget::PANEInfo;
if (PANEInfo* info = static_cast<PANEInfo*>(w.widgetInfo.get())) { if (PANEInfo* info = static_cast<PANEInfo*>(w.widgetInfo.get())) {
zeus::simd_floats f(info->scaleCenter.simd); zeus::simd_floats f(info->scaleCenter.simd);
os.format(fmt( os.format(FMT_STRING(
"frme_obj.retro_pane_dimensions = ({},{})\n" "frme_obj.retro_pane_dimensions = ({},{})\n"
"frme_obj.retro_pane_scale_center = ({},{},{})\n"), "frme_obj.retro_pane_scale_center = ({},{},{})\n"),
info->xDim, info->zDim, f[0], f[1], f[2]); info->xDim, info->zDim, f[0], f[1], f[2]);
@ -533,7 +533,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
zeus::simd_floats fillF(info->fillColor.simd); zeus::simd_floats fillF(info->fillColor.simd);
zeus::simd_floats outlineF(info->outlineColor.simd); zeus::simd_floats outlineF(info->outlineColor.simd);
zeus::simd_floats extentF(info->blockExtent.simd); zeus::simd_floats extentF(info->blockExtent.simd);
os.format(fmt( os.format(FMT_STRING(
"frme_obj.retro_pane_dimensions = ({},{})\n" "frme_obj.retro_pane_dimensions = ({},{})\n"
"frme_obj.retro_pane_scale_center = ({},{},{})\n" "frme_obj.retro_pane_scale_center = ({},{},{})\n"
"frme_obj.retro_textpane_font_path = '{}'\n" "frme_obj.retro_textpane_font_path = '{}'\n"
@ -557,7 +557,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
} else if (w.type == SBIG('TBGP')) { } else if (w.type == SBIG('TBGP')) {
using TBGPInfo = Widget::TBGPInfo; using TBGPInfo = Widget::TBGPInfo;
if (TBGPInfo* info = static_cast<TBGPInfo*>(w.widgetInfo.get())) { if (TBGPInfo* info = static_cast<TBGPInfo*>(w.widgetInfo.get())) {
os.format(fmt( os.format(FMT_STRING(
"frme_obj.retro_tablegroup_elem_count = {}\n" "frme_obj.retro_tablegroup_elem_count = {}\n"
"frme_obj.retro_tablegroup_elem_default = {}\n" "frme_obj.retro_tablegroup_elem_default = {}\n"
"frme_obj.retro_tablegroup_wraparound = {}\n"), "frme_obj.retro_tablegroup_wraparound = {}\n"),
@ -566,12 +566,12 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
} else if (w.type == SBIG('GRUP')) { } else if (w.type == SBIG('GRUP')) {
using GRUPInfo = Widget::GRUPInfo; using GRUPInfo = Widget::GRUPInfo;
if (GRUPInfo* info = static_cast<GRUPInfo*>(w.widgetInfo.get())) { if (GRUPInfo* info = static_cast<GRUPInfo*>(w.widgetInfo.get())) {
os.format(fmt("frme_obj.retro_group_default_worker = {}\n"), info->defaultWorker); os.format(FMT_STRING("frme_obj.retro_group_default_worker = {}\n"), info->defaultWorker);
} }
} else if (w.type == SBIG('SLGP')) { } else if (w.type == SBIG('SLGP')) {
using SLGPInfo = Widget::SLGPInfo; using SLGPInfo = Widget::SLGPInfo;
if (SLGPInfo* info = static_cast<SLGPInfo*>(w.widgetInfo.get())) { if (SLGPInfo* info = static_cast<SLGPInfo*>(w.widgetInfo.get())) {
os.format(fmt( os.format(FMT_STRING(
"frme_obj.retro_slider_min = {}\n" "frme_obj.retro_slider_min = {}\n"
"frme_obj.retro_slider_max = {}\n" "frme_obj.retro_slider_max = {}\n"
"frme_obj.retro_slider_default = {}\n" "frme_obj.retro_slider_default = {}\n"
@ -583,12 +583,12 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
if (ENRGInfo* info = static_cast<ENRGInfo*>(w.widgetInfo.get())) { if (ENRGInfo* info = static_cast<ENRGInfo*>(w.widgetInfo.get())) {
hecl::ProjectPath txtrPath = pakRouter.getWorking(info->texture); hecl::ProjectPath txtrPath = pakRouter.getWorking(info->texture);
if (txtrPath) if (txtrPath)
os.format(fmt("frme_obj.retro_energybar_texture_path = '{}'\n"), txtrPath.getRelativePathUTF8()); os.format(FMT_STRING("frme_obj.retro_energybar_texture_path = '{}'\n"), txtrPath.getRelativePathUTF8());
} }
} else if (w.type == SBIG('METR')) { } else if (w.type == SBIG('METR')) {
using METRInfo = Widget::METRInfo; using METRInfo = Widget::METRInfo;
if (METRInfo* info = static_cast<METRInfo*>(w.widgetInfo.get())) { if (METRInfo* info = static_cast<METRInfo*>(w.widgetInfo.get())) {
os.format(fmt( os.format(FMT_STRING(
"frme_obj.retro_meter_no_round_up = {}\n" "frme_obj.retro_meter_no_round_up = {}\n"
"frme_obj.retro_meter_max_capacity = {}\n" "frme_obj.retro_meter_max_capacity = {}\n"
"frme_obj.retro_meter_worker_count = {}\n"), "frme_obj.retro_meter_worker_count = {}\n"),
@ -600,7 +600,7 @@ bool FRME::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
w.basis[i].simd.copy_to(xfMtxF[i]); w.basis[i].simd.copy_to(xfMtxF[i]);
zeus::simd_floats originF(w.origin.simd); zeus::simd_floats originF(w.origin.simd);
os.format(fmt( os.format(FMT_STRING(
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n" "mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
"mtxd = mtx.decompose()\n" "mtxd = mtx.decompose()\n"
"frme_obj.rotation_mode = 'QUATERNION'\n" "frme_obj.rotation_mode = 'QUATERNION'\n"

View File

@ -137,7 +137,7 @@ bool MLVL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
if (!areaPath.isFile()) if (!areaPath.isFile())
continue; continue;
Log.report(logvisor::Info, fmt(_SYS_STR("Visiting {}")), area.path.getRelativePath()); Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Visiting {}")), area.path.getRelativePath());
hecl::ProjectPath memRelayPath(area.path, _SYS_STR("!memoryrelays.yaml")); hecl::ProjectPath memRelayPath(area.path, _SYS_STR("!memoryrelays.yaml"));
@ -311,7 +311,7 @@ bool MLVL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
} }
if (!areaInit) if (!areaInit)
Log.report(logvisor::Info, fmt(_SYS_STR("No layer directories for area {}")), area.path.getRelativePath()); Log.report(logvisor::Info, FMT_STRING(_SYS_STR("No layer directories for area {}")), area.path.getRelativePath());
/* Build deplist */ /* Build deplist */
MLVL::Area& areaOut = mlvl.areas.back(); MLVL::Area& areaOut = mlvl.areas.back();

View File

@ -20,7 +20,7 @@ namespace DataSpec::DNAMP1 {
void MREA::ReadBabeDeadToBlender_1_2(hecl::blender::PyOutStream& os, athena::io::IStreamReader& rs) { void MREA::ReadBabeDeadToBlender_1_2(hecl::blender::PyOutStream& os, athena::io::IStreamReader& rs) {
atUint32 bdMagic = rs.readUint32Big(); atUint32 bdMagic = rs.readUint32Big();
if (bdMagic != 0xBABEDEAD) if (bdMagic != 0xBABEDEAD)
Log.report(logvisor::Fatal, fmt("invalid BABEDEAD magic")); Log.report(logvisor::Fatal, FMT_STRING("invalid BABEDEAD magic"));
os << "bpy.context.scene.world.use_nodes = True\n" os << "bpy.context.scene.world.use_nodes = True\n"
"bg_node = bpy.context.scene.world.node_tree.nodes['Background']\n" "bg_node = bpy.context.scene.world.node_tree.nodes['Background']\n"
"bg_node.inputs[1].default_value = 0.0\n"; "bg_node.inputs[1].default_value = 0.0\n";
@ -202,7 +202,7 @@ bool MREA::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
"from mathutils import Vector\n" "from mathutils import Vector\n"
"bpy.context.scene.render.fps = 60\n" "bpy.context.scene.render.fps = 60\n"
"\n"; "\n";
os.format(fmt("bpy.context.scene.name = '{}'\n"), os.format(FMT_STRING("bpy.context.scene.name = '{}'\n"),
pakRouter.getBestEntryName(entry, false)); pakRouter.getBestEntryName(entry, false));
DNACMDL::InitGeomBlenderContext(os, dataSpec.getMasterShaderPath()); DNACMDL::InitGeomBlenderContext(os, dataSpec.getMasterShaderPath());
MaterialSet::RegisterMaterialProps(os); MaterialSet::RegisterMaterialProps(os);
@ -244,7 +244,7 @@ bool MREA::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
rs.seek(secStart + head.secSizes[curSec++], athena::SeekOrigin::Begin); rs.seek(secStart + head.secSizes[curSec++], athena::SeekOrigin::Begin);
curSec += DNACMDL::ReadGeomSectionsToBlender<PAKRouter<PAKBridge>, MaterialSet, RigPair, DNACMDL::SurfaceHeader_1>( curSec += DNACMDL::ReadGeomSectionsToBlender<PAKRouter<PAKBridge>, MaterialSet, RigPair, DNACMDL::SurfaceHeader_1>(
os, rs, pakRouter, entry, dummy, true, true, vertAttribs, m, head.secCount, 0, &head.secSizes[curSec]); os, rs, pakRouter, entry, dummy, true, true, vertAttribs, m, head.secCount, 0, &head.secSizes[curSec]);
os.format(fmt( os.format(FMT_STRING(
"obj.retro_disable_enviro_visor = {}\n" "obj.retro_disable_enviro_visor = {}\n"
"obj.retro_disable_thermal_visor = {}\n" "obj.retro_disable_thermal_visor = {}\n"
"obj.retro_disable_xray_visor = {}\n" "obj.retro_disable_xray_visor = {}\n"
@ -314,7 +314,7 @@ bool MREA::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
"bpy.context.view_layer.layer_collection.children['Collision'].hide_viewport = True\n"; "bpy.context.view_layer.layer_collection.children['Collision'].hide_viewport = True\n";
/* Link MLVL scene as background */ /* Link MLVL scene as background */
os.linkBackground(fmt::format(fmt("//../!world_{}.blend"), os.linkBackground(fmt::format(FMT_STRING("//../!world_{}.blend"),
pakRouter.getCurrentBridge().getLevelId()), "World"sv); pakRouter.getCurrentBridge().getLevelId()), "World"sv);
os.centerView(); os.centerView();
@ -332,7 +332,7 @@ void MREA::Name(const SpecBase& dataSpec, PAKEntryReadStream& rs, PAKRouter<PAKB
atUint64 secStart = rs.position(); atUint64 secStart = rs.position();
MaterialSet matSet; MaterialSet matSet;
matSet.read(rs); matSet.read(rs);
matSet.nameTextures(pakRouter, fmt::format(fmt("MREA_{}"), entry.id).c_str(), -1); matSet.nameTextures(pakRouter, fmt::format(FMT_STRING("MREA_{}"), entry.id).c_str(), -1);
rs.seek(secStart + head.secSizes[0], athena::SeekOrigin::Begin); rs.seek(secStart + head.secSizes[0], athena::SeekOrigin::Begin);
/* Skip to SCLY */ /* Skip to SCLY */
@ -700,12 +700,12 @@ bool MREA::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
#if _WIN32 #if _WIN32
VisiGenPath += _SYS_STR(".exe"); VisiGenPath += _SYS_STR(".exe");
#endif #endif
hecl::SystemString thrIdx = fmt::format(fmt(_SYS_STR("{}")), hecl::ClientProcess::GetThreadWorkerIdx()); hecl::SystemString thrIdx = fmt::format(FMT_STRING(_SYS_STR("{}")), hecl::ClientProcess::GetThreadWorkerIdx());
hecl::SystemString parPid; hecl::SystemString parPid;
#if _WIN32 #if _WIN32
parPid = fmt::format(fmt(_SYS_STR("{}")), reinterpret_cast<unsigned long long>(GetCurrentProcess())); parPid = fmt::format(FMT_STRING(_SYS_STR("{}")), reinterpret_cast<unsigned long long>(GetCurrentProcess()));
#else #else
parPid = fmt::format(fmt(_SYS_STR("{}")), (unsigned long long)getpid()); parPid = fmt::format(FMT_STRING(_SYS_STR("{}")), (unsigned long long)getpid());
#endif #endif
const hecl::SystemChar* args[] = {VisiGenPath.c_str(), const hecl::SystemChar* args[] = {VisiGenPath.c_str(),
visiIntOut.getAbsolutePath().data(), visiIntOut.getAbsolutePath().data(),
@ -720,7 +720,7 @@ bool MREA::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
r.readBytesToBuf(secs.back().data(), length); r.readBytesToBuf(secs.back().data(), length);
visiGood = true; visiGood = true;
} else { } else {
Log.report(logvisor::Fatal, fmt(_SYS_STR("Unable to launch {}")), VisiGenPath); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unable to launch {}")), VisiGenPath);
} }
} }
#endif #endif

View File

@ -10,7 +10,7 @@ template <>
void PAK::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) { void PAK::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
atUint32 version = reader.readUint32Big(); atUint32 version = reader.readUint32Big();
if (version != 0x00030005) if (version != 0x00030005)
Log.report(logvisor::Fatal, fmt("unexpected PAK magic")); Log.report(logvisor::Fatal, FMT_STRING("unexpected PAK magic"));
reader.readUint32Big(); reader.readUint32Big();
atUint32 nameCount = reader.readUint32Big(); atUint32 nameCount = reader.readUint32Big();
@ -147,6 +147,7 @@ const PAK::Entry* PAK::lookupEntry(const UniqueID32& id) const {
} }
const PAK::Entry* PAK::lookupEntry(std::string_view name) const { const PAK::Entry* PAK::lookupEntry(std::string_view name) const {
// TODO: Heterogeneous lookup when C++20 available
auto result = m_nameMap.find(name.data()); auto result = m_nameMap.find(name.data());
if (result != m_nameMap.end()) { if (result != m_nameMap.end()) {
auto result1 = m_entries.find(result->second); auto result1 = m_entries.find(result->second);
@ -164,19 +165,19 @@ std::string PAK::bestEntryName(const nod::Node& pakNode, const Entry& entry, std
AGSC::Header header; AGSC::Header header;
header.read(rs); header.read(rs);
catalogueName = header.groupName; catalogueName = header.groupName;
return fmt::format(fmt("{}_{}"), header.groupName, entry.id); return fmt::format(FMT_STRING("{}_{}"), header.groupName, entry.id);
} }
/* Prefer named entries first */ /* Prefer named entries first */
for (const NameEntry& nentry : m_nameEntries) { for (const NameEntry& nentry : m_nameEntries) {
if (nentry.id == entry.id) { if (nentry.id == entry.id) {
catalogueName = nentry.name; catalogueName = nentry.name;
return fmt::format(fmt("{}_{}"), nentry.name, entry.id); return fmt::format(FMT_STRING("{}_{}"), nentry.name, entry.id);
} }
} }
/* Otherwise return ID format string */ /* Otherwise return ID format string */
return fmt::format(fmt("{}_{}"), entry.type, entry.id); return fmt::format(FMT_STRING("{}_{}"), entry.type, entry.id);
} }
} // namespace DataSpec::DNAMP1 } // namespace DataSpec::DNAMP1

View File

@ -1,244 +0,0 @@
#include "PATH.hpp"
#include "hecl/Blender/Connection.hpp"
#include "zeus/CAABox.hpp"
#include "DataSpec/DNACommon/AROTBuilder.hpp"
namespace DataSpec::DNAMP1 {
#define DUMP_OCTREE 0
#if DUMP_OCTREE
/* octree dumper */
static void OutputOctreeNode(hecl::blender::PyOutStream& os, int idx, const zeus::CAABox& aabb) {
const zeus::CVector3f pos = aabb.center();
const zeus::CVector3f extent = aabb.extents();
os.format(
"obj = bpy.data.objects.new('Leaf_%d', None)\n"
"bpy.context.scene.collection.objects.link(obj)\n"
"obj.location = (%f,%f,%f)\n"
"obj.scale = (%f,%f,%f)\n"
"obj.empty_display_type = 'CUBE'\n"
"obj.layers[1] = True\n"
"obj.layers[0] = False\n", idx,
pos.x(), pos.y(), pos.z(), extent.x(), extent.y(), extent.z());
}
#endif
void PATH::sendToBlender(hecl::blender::Connection& conn, std::string_view entryName, const zeus::CMatrix4f* xf,
const std::string& areaPath) {
/* Open Py Stream and read sections */
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
os <<
"import bpy\n"
"import bmesh\n"
"from mathutils import Vector, Matrix\n"
"\n"
"bpy.types.Material.retro_path_idx_mask = bpy.props.IntProperty(name='Retro: Path Index Mask')\n"
"bpy.types.Material.retro_path_type_mask = bpy.props.IntProperty(name='Retro: Path Type Mask')\n"
"\n"
"material_dict = {}\n"
"material_index = []\n"
"def make_ground_material(idxMask):\n"
" mat = bpy.data.materials.new('Ground %X' % idxMask)\n"
" mat.diffuse_color = (0.8, 0.460, 0.194, 1.0)\n"
" return mat\n"
"def make_flyer_material(idxMask):\n"
" mat = bpy.data.materials.new('Flyer %X' % idxMask)\n"
" mat.diffuse_color = (0.016, 0.8, 0.8, 1.0)\n"
" return mat\n"
"def make_swimmer_material(idxMask):\n"
" mat = bpy.data.materials.new('Swimmer %X' % idxMask)\n"
" mat.diffuse_color = (0.074, 0.293, 0.8, 1.0)\n"
" return mat\n"
"def select_material(meshIdxMask, meshTypeMask):\n"
" key = (meshIdxMask, meshTypeMask)\n"
" if key in material_index:\n"
" return material_index.index(key)\n"
" elif key in material_dict:\n"
" material_index.append(key)\n"
" return len(material_index)-1\n"
" else:\n"
" if meshTypeMask == 0x2:\n"
" mat = make_flyer_material(meshIdxMask)\n"
" elif meshTypeMask == 0x4:\n"
" mat = make_swimmer_material(meshIdxMask)\n"
" else:\n"
" mat = make_ground_material(meshIdxMask)\n"
" mat.retro_path_idx_mask = meshIdxMask\n"
" mat.retro_path_type_mask = meshTypeMask\n"
" material_dict[key] = mat\n"
" material_index.append(key)\n"
" return len(material_index)-1\n"
"\n";
os.format(fmt("bpy.context.scene.name = '{}'\n"), entryName);
os <<
"# Clear Scene\n"
"if len(bpy.data.collections):\n"
" bpy.data.collections.remove(bpy.data.collections[0])\n"
"\n"
"bm = bmesh.new()\n"
"height_lay = bm.faces.layers.float.new('Height')\n";
for (const Node& n : nodes) {
zeus::simd_floats f(n.position.simd);
os.format(fmt("bm.verts.new(({},{},{}))\n"), f[0], f[1], f[2]);
}
os << "bm.verts.ensure_lookup_table()\n";
for (const Region& r : regions) {
os << "tri_verts = []\n";
for (atUint32 i = 0; i < r.nodeCount; ++i)
os.format(fmt("tri_verts.append(bm.verts[{}])\n"), r.nodeStart + i);
os.format(fmt(
"face = bm.faces.get(tri_verts)\n"
"if face is None:\n"
" face = bm.faces.new(tri_verts)\n"
" face.normal_flip()\n"
"face.material_index = select_material(0x{:04X}, 0x{:04X})\n"
"face.smooth = False\n"
"face[height_lay] = {}\n"
"\n"),
r.meshIndexMask, r.meshTypeMask, r.height);
#if 0
const zeus::CVector3f center = xf->multiplyOneOverW(r.centroid);
zeus::CAABox aabb(xf->multiplyOneOverW(r.aabb[0]), xf->multiplyOneOverW(r.aabb[1]));
os.format(fmt("aabb = bpy.data.objects.new('AABB', None)\n")
"aabb.location = (%f,%f,%f)\n"
"aabb.scale = (%f,%f,%f)\n"
"aabb.empty_display_type = 'CUBE'\n"
"bpy.context.scene.collection.objects.link(aabb)\n"
"centr = bpy.data.objects.new('Center', None)\n"
"centr.location = (%f,%f,%f)\n"
"bpy.context.scene.collection.objects.link(centr)\n",
aabb.min[0] + (aabb.max[0] - aabb.min[0]) / 2.f,
aabb.min[1] + (aabb.max[1] - aabb.min[1]) / 2.f,
aabb.min[2] + (aabb.max[2] - aabb.min[2]) / 2.f,
(aabb.max[0] - aabb.min[0]) / 2.f,
(aabb.max[1] - aabb.min[1]) / 2.f,
(aabb.max[2] - aabb.min[2]) / 2.f,
center.x(), center.y(), center.z());
#endif
}
#if 0
for (const Node& n : nodes) {
zeus::simd_floats f(n.position.simd);
zeus::simd_floats no(n.position.simd + n.normal.simd);
os.format(fmt("v = bm.verts.new((%f,%f,%f))\n")
"v2 = bm.verts.new((%f,%f,%f))\n"
"bm.edges.new((v, v2))\n", f[0], f[1], f[2], no[0], no[1], no[2]);
}
#endif
os << "bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)\n"
"path_mesh = bpy.data.meshes.new('PATH')\n"
"bm.to_mesh(path_mesh)\n"
"path_mesh_obj = bpy.data.objects.new(path_mesh.name, path_mesh)\n"
"\n"
"for mat_name in material_index:\n"
" mat = material_dict[mat_name]\n"
" path_mesh.materials.append(mat)\n"
"\n"
"bpy.context.scene.collection.objects.link(path_mesh_obj)\n"
"path_mesh_obj.display_type = 'SOLID'\n"
"bpy.context.scene.hecl_path_obj = path_mesh_obj.name\n"
"\n";
if (xf) {
const zeus::CMatrix4f& w = *xf;
zeus::simd_floats xfMtxF[4];
for (int i = 0; i < 4; ++i)
w.m[i].mSimd.copy_to(xfMtxF[i]);
os.format(fmt(
"mtx = Matrix((({},{},{},{}),({},{},{},{}),({},{},{},{}),(0.0,0.0,0.0,1.0)))\n"
"mtxd = mtx.decompose()\n"
"path_mesh_obj.rotation_mode = 'QUATERNION'\n"
"path_mesh_obj.location = mtxd[0]\n"
"path_mesh_obj.rotation_quaternion = mtxd[1]\n"
"path_mesh_obj.scale = mtxd[2]\n"),
xfMtxF[0][0], xfMtxF[1][0], xfMtxF[2][0], xfMtxF[3][0], xfMtxF[0][1], xfMtxF[1][1], xfMtxF[2][1], xfMtxF[3][1],
xfMtxF[0][2], xfMtxF[1][2], xfMtxF[2][2], xfMtxF[3][2]);
}
#if DUMP_OCTREE
{
int idx = 0;
for (const auto& n : octree) {
if (n.isLeaf)
OutputOctreeNode(os, idx, zeus::CAABox(n.aabb[0], n.aabb[1]));
++idx;
}
}
#endif
os.linkBackground(fmt::format(fmt("//{}"), areaPath));
os.centerView();
os.close();
}
bool PATH::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok,
std::function<void(const hecl::SystemChar*)> fileChanged) {
PATH path;
path.read(rs);
hecl::blender::Connection& conn = btok.getBlenderConnection();
if (!conn.createBlend(outPath, hecl::blender::BlendType::PathMesh))
return false;
std::string areaPath;
for (const auto& ent : hecl::DirectoryEnumerator(outPath.getParentPath().getAbsolutePath())) {
if (hecl::StringUtils::BeginsWith(ent.m_name, _SYS_STR("!area_"))) {
areaPath = hecl::SystemUTF8Conv(ent.m_name).str();
break;
}
}
const zeus::CMatrix4f* xf = pakRouter.lookupMAPATransform(entry.id);
path.sendToBlender(conn, pakRouter.getBestEntryName(entry, false), xf, areaPath);
return conn.saveBlend();
}
bool PATH::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath,
const PathMesh& mesh, hecl::blender::Token& btok) {
athena::io::MemoryReader r(mesh.data.data(), mesh.data.size());
PATH path;
path.read(r);
if (!path.regions.empty()) {
AROTBuilder octreeBuilder;
octreeBuilder.buildPath(path);
} else {
path.octreeNodeCount = 1;
path.octree.emplace_back();
OctreeNode& n = path.octree.back();
n.isLeaf = 1;
n.aabb[0] = zeus::CVector3f{FLT_MAX, FLT_MAX, FLT_MAX};
n.aabb[1] = zeus::CVector3f{-FLT_MAX, -FLT_MAX, -FLT_MAX};
for (int i = 0; i < 8; ++i)
n.children[i] = 0xffffffff;
}
#if DUMP_OCTREE
{
hecl::blender::Connection& conn = btok.getBlenderConnection();
if (!conn.createBlend(inPath.getWithExtension(_SYS_STR(".octree.blend"), true), hecl::blender::BlendType::PathMesh))
return false;
zeus::CMatrix4f xf;
path.sendToBlender(conn, "PATH"sv, &xf);
conn.saveBlend();
}
#endif
athena::io::FileWriter w(outPath.getAbsolutePath());
path.write(w);
int64_t rem = w.position() % 32;
if (rem)
for (int64_t i = 0; i < 32 - rem; ++i)
w.writeUByte(0xff);
return true;
}
} // namespace DataSpec::DNAMP1

View File

@ -1,79 +1,6 @@
#pragma once #pragma once
#include "DataSpec/DNACommon/PATH.hpp"
#include "DataSpec/DNACommon/DNACommon.hpp"
#include "DataSpec/DNACommon/PAK.hpp"
#include "DNAMP1.hpp"
namespace DataSpec::DNAMP1 { namespace DataSpec::DNAMP1 {
struct PATH : BigDNA { using PATH = DNAPATH::PATH<PAKBridge>;
using PathMesh = hecl::blender::PathMesh;
AT_DECL_DNA
Value<atUint32> version;
struct Node : BigDNA {
AT_DECL_DNA
Value<atVec3f> position;
Value<atVec3f> normal;
};
Value<atUint32> nodeCount;
Vector<Node, AT_DNA_COUNT(nodeCount)> nodes;
struct Link : BigDNA {
AT_DECL_DNA
Value<atUint32> nodeIdx;
Value<atUint32> regionIdx;
Value<float> width2d;
Value<float> oneOverWidth2d;
};
Value<atUint32> linkCount;
Vector<Link, AT_DNA_COUNT(linkCount)> links;
struct Region : BigDNA {
AT_DECL_DNA
Value<atUint32> nodeCount;
Value<atUint32> nodeStart;
Value<atUint32> linkCount;
Value<atUint32> linkStart;
Value<atUint16> meshIndexMask;
Value<atUint16> meshTypeMask;
Value<float> height;
Value<atVec3f> normal;
Value<atUint32> regionIdx;
Value<atVec3f> centroid;
Value<atVec3f> aabb[2];
Value<atUint32> regionIdxPtr;
};
Value<atUint32> regionCount;
Vector<Region, AT_DNA_COUNT(regionCount)> regions;
Vector<atUint32, AT_DNA_COUNT((((regionCount * (regionCount - 1)) / 2) + 31) / 32)> bitmap1;
Vector<atUint32, AT_DNA_COUNT(bitmap1.size())> bitmap2;
Vector<atUint32, AT_DNA_COUNT(((((regionCount * regionCount) + 31) / 32) - bitmap1.size()) * 2)> bitmap3;
Value<atUint32> octreeRegionLookupCount;
Vector<atUint32, AT_DNA_COUNT(octreeRegionLookupCount)> octreeRegionLookup;
struct OctreeNode : BigDNA {
AT_DECL_DNA
Value<atUint32> isLeaf;
Value<atVec3f> aabb[2];
Value<atVec3f> centroid;
Value<atUint32> children[8];
Value<atUint32> regionCount;
Value<atUint32> regionStart;
};
Value<atUint32> octreeNodeCount;
Vector<OctreeNode, AT_DNA_COUNT(octreeNodeCount)> octree;
void sendToBlender(hecl::blender::Connection& conn, std::string_view entryName, const zeus::CMatrix4f* xf,
const std::string& areaPath);
static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool force, hecl::blender::Token& btok,
std::function<void(const hecl::SystemChar*)> fileChanged);
static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath,
const PathMesh& mesh, hecl::blender::Token& btok);
};
} // namespace DataSpec::DNAMP1 } // namespace DataSpec::DNAMP1

View File

@ -87,13 +87,13 @@ struct SCAN : BigDNA {
scan.read(rs); scan.read(rs);
if (scan.string.isValid()) { if (scan.string.isValid()) {
PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(scan.string); PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(scan.string);
ent->name = fmt::format(fmt("SCAN_{}_strg"), entry.id); ent->name = fmt::format(FMT_STRING("SCAN_{}_strg"), entry.id);
} }
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
const Texture& tex = scan.textures[i]; const Texture& tex = scan.textures[i];
if (tex.texture.isValid()) { if (tex.texture.isValid()) {
PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(tex.texture); PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(tex.texture);
ent->name = fmt::format(fmt("SCAN_{}_tex{}"), entry.id, i + 1); ent->name = fmt::format(FMT_STRING("SCAN_{}_tex{}"), entry.id, i + 1);
} }
} }
} }

View File

@ -115,12 +115,12 @@ void SCLY::ScriptLayer::Enumerate<BigDNA::Read>(athena::io::IStreamReader& rs) {
size_t actualLen = rs.position() - start; size_t actualLen = rs.position() - start;
if (actualLen != len) if (actualLen != len)
Log.report(logvisor::Fatal, Log.report(logvisor::Fatal,
fmt(_SYS_STR("Error while reading object of type 0x{:02X}, did not read the expected amount of " FMT_STRING(_SYS_STR("Error while reading object of type 0x{:02X}, did not read the expected amount of "
"data, read 0x{:x}, expected 0x{:x}")), "data, read 0x{:x}, expected 0x{:x}")),
(atUint32)type, actualLen, len); (atUint32)type, actualLen, len);
rs.seek(start + len, athena::SeekOrigin::Begin); rs.seek(start + len, athena::SeekOrigin::Begin);
} else { } else {
Log.report(logvisor::Fatal, fmt(_SYS_STR("Unable to find type 0x{:X} in object database")), (atUint32)type); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unable to find type 0x{:X} in object database")), (atUint32)type);
} }
} }
} }
@ -145,7 +145,7 @@ void SCLY::ScriptLayer::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& r
obj->type = type; obj->type = type;
objects.push_back(std::move(obj)); objects.push_back(std::move(obj));
} else } else
Log.report(logvisor::Fatal, fmt(_SYS_STR("Unable to find type 0x{:X} in object database")), (atUint32)type); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unable to find type 0x{:X} in object database")), (atUint32)type);
} }
} }
} else } else
@ -165,7 +165,7 @@ void SCLY::ScriptLayer::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& ws)
obj->write(ws); obj->write(ws);
auto wrote = ws.position() - start; auto wrote = ws.position() - start;
if (wrote != expLen) if (wrote != expLen)
Log.report(logvisor::Error, fmt("expected writing {} byte SCLY obj; wrote {}"), expLen, wrote); Log.report(logvisor::Error, FMT_STRING("expected writing {} byte SCLY obj; wrote {}"), expLen, wrote);
} }
} }

View File

@ -62,7 +62,7 @@ static std::u16string_view::const_iterator CookTextureList(std::u16string& ret,
while (true) { while (true) {
auto end = str.find_first_of(u",;", it - str.begin()); auto end = str.find_first_of(u",;", it - str.begin());
if (end == std::u16string::npos) if (end == std::u16string::npos)
Log.report(logvisor::Fatal, fmt("Missing comma/semicolon token while pasing font tag")); Log.report(logvisor::Fatal, FMT_STRING("Missing comma/semicolon token while pasing font tag"));
auto endIt = str.begin() + end; auto endIt = str.begin() + end;
hecl::ProjectPath path = hecl::ProjectPath path =
UniqueIDBridge::MakePathFromString<UniqueID32>(hecl::Char16ToUTF8(std::u16string(it, endIt))); UniqueIDBridge::MakePathFromString<UniqueID32>(hecl::Char16ToUTF8(std::u16string(it, endIt)));
@ -92,7 +92,7 @@ static std::u16string_view::const_iterator GatherTextureList(std::vector<hecl::P
while (true) { while (true) {
auto end = str.find_first_of(u",;", it - str.begin()); auto end = str.find_first_of(u",;", it - str.begin());
if (end == std::u16string::npos) if (end == std::u16string::npos)
Log.report(logvisor::Fatal, fmt("Missing comma/semicolon token while pasing font tag")); Log.report(logvisor::Fatal, FMT_STRING("Missing comma/semicolon token while pasing font tag"));
auto endIt = str.begin() + end; auto endIt = str.begin() + end;
hecl::ProjectPath path = hecl::ProjectPath path =
UniqueIDBridge::MakePathFromString<UniqueID32>(hecl::Char16ToUTF8(std::u16string(it, endIt))); UniqueIDBridge::MakePathFromString<UniqueID32>(hecl::Char16ToUTF8(std::u16string(it, endIt)));
@ -198,7 +198,7 @@ static std::u16string CookString(std::u16string_view str) {
it += 5; it += 5;
auto scpos = str.find(u';', it - str.begin()); auto scpos = str.find(u';', it - str.begin());
if (scpos == std::u16string::npos) if (scpos == std::u16string::npos)
Log.report(logvisor::Fatal, fmt("Missing semicolon token while pasing font tag")); Log.report(logvisor::Fatal, FMT_STRING("Missing semicolon token while pasing font tag"));
hecl::ProjectPath path = hecl::ProjectPath path =
UniqueIDBridge::MakePathFromString<UniqueID32>(hecl::Char16ToUTF8(std::u16string(it, str.begin() + scpos))); UniqueIDBridge::MakePathFromString<UniqueID32>(hecl::Char16ToUTF8(std::u16string(it, str.begin() + scpos)));
ret.append(hecl::UTF8ToChar16(UniqueID32(path).toString())); ret.append(hecl::UTF8ToChar16(UniqueID32(path).toString()));
@ -249,7 +249,7 @@ void STRG::gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const {
it += 5; it += 5;
auto scpos = str.find(u';', it - strView.begin()); auto scpos = str.find(u';', it - strView.begin());
if (scpos == std::u16string::npos) if (scpos == std::u16string::npos)
Log.report(logvisor::Fatal, fmt("Missing semicolon token while pasing font tag")); Log.report(logvisor::Fatal, FMT_STRING("Missing semicolon token while pasing font tag"));
hecl::ProjectPath path = UniqueIDBridge::MakePathFromString<UniqueID32>( hecl::ProjectPath path = UniqueIDBridge::MakePathFromString<UniqueID32>(
hecl::Char16ToUTF8(std::u16string(it, strView.begin() + scpos))); hecl::Char16ToUTF8(std::u16string(it, strView.begin() + scpos)));
if (path) if (path)
@ -311,11 +311,11 @@ template <>
void STRG::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) { void STRG::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
atUint32 magic = reader.readUint32Big(); atUint32 magic = reader.readUint32Big();
if (magic != 0x87654321) if (magic != 0x87654321)
Log.report(logvisor::Error, fmt("invalid STRG magic")); Log.report(logvisor::Error, FMT_STRING("invalid STRG magic"));
atUint32 version = reader.readUint32Big(); atUint32 version = reader.readUint32Big();
if (version != 0) if (version != 0)
Log.report(logvisor::Error, fmt("invalid STRG version")); Log.report(logvisor::Error, FMT_STRING("invalid STRG version"));
_read(reader); _read(reader);
} }
@ -412,22 +412,22 @@ void STRG::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& reader) {
continue; continue;
if (lang.first.size() != 4) { if (lang.first.size() != 4) {
Log.report(logvisor::Warning, fmt("STRG language string '{}' must be exactly 4 characters; skipping"), lang.first); Log.report(logvisor::Warning, FMT_STRING("STRG language string '{}' must be exactly 4 characters; skipping"), lang.first);
return; return;
} }
if (lang.second->m_type != YAML_SEQUENCE_NODE) { if (lang.second->m_type != YAML_SEQUENCE_NODE) {
Log.report(logvisor::Warning, fmt("STRG language string '{}' must contain a sequence; skipping"), lang.first); Log.report(logvisor::Warning, FMT_STRING("STRG language string '{}' must contain a sequence; skipping"), lang.first);
return; return;
} }
for (const auto& str : lang.second->m_seqChildren) { for (const auto& str : lang.second->m_seqChildren) {
if (str->m_type != YAML_SCALAR_NODE) { if (str->m_type != YAML_SCALAR_NODE) {
Log.report(logvisor::Warning, fmt("STRG language '{}' must contain all scalars; skipping"), lang.first); Log.report(logvisor::Warning, FMT_STRING("STRG language '{}' must contain all scalars; skipping"), lang.first);
return; return;
} }
} }
} }
} else { } else {
Log.report(logvisor::Warning, fmt("STRG must have a mapping root node; skipping")); Log.report(logvisor::Warning, FMT_STRING("STRG must have a mapping root node; skipping"));
return; return;
} }

View File

@ -49,7 +49,7 @@ struct Babygoth : IScriptObject {
charAssoc.m_cmdlRigs[noShellModel] = {noShellSkin, cinf}; charAssoc.m_cmdlRigs[noShellModel] = {noShellSkin, cinf};
charAssoc.m_cskrToCharacter[noShellSkin] = charAssoc.m_cskrToCharacter[noShellSkin] =
std::make_pair(patternedInfo.animationParameters.animationCharacterSet, std::make_pair(patternedInfo.animationParameters.animationCharacterSet,
fmt::format(fmt("ATTACH.SHELLESS_{}.CSKR"), noShellSkin)); fmt::format(FMT_STRING("ATTACH.SHELLESS_{}.CSKR"), noShellSkin));
charAssoc.addAttachmentRig(patternedInfo.animationParameters.animationCharacterSet, {}, noShellModel, "SHELLESS"); charAssoc.addAttachmentRig(patternedInfo.animationParameters.animationCharacterSet, {}, noShellModel, "SHELLESS");
} }
} }

View File

@ -47,7 +47,7 @@ struct Magdolite : IScriptObject {
charAssoc.m_cmdlRigs[cmdlHeadless] = {cskrHeadless, cinf}; charAssoc.m_cmdlRigs[cmdlHeadless] = {cskrHeadless, cinf};
charAssoc.m_cskrToCharacter[cskrHeadless] = charAssoc.m_cskrToCharacter[cskrHeadless] =
std::make_pair(patternedInfo.animationParameters.animationCharacterSet, std::make_pair(patternedInfo.animationParameters.animationCharacterSet,
fmt::format(fmt("ATTACH.HEADLESS_{}.CSKR"), cskrHeadless)); fmt::format(FMT_STRING("ATTACH.HEADLESS_{}.CSKR"), cskrHeadless));
charAssoc.addAttachmentRig(patternedInfo.animationParameters.animationCharacterSet, {}, cmdlHeadless, "HEADLESS"); charAssoc.addAttachmentRig(patternedInfo.animationParameters.animationCharacterSet, {}, cmdlHeadless, "HEADLESS");
} }
} }

View File

@ -60,7 +60,7 @@ struct OmegaPirate : IScriptObject {
charAssoc.m_cmdlRigs[cmdlPhazonVeins] = {cskrPhazonVeins, cinfPhazonVeins}; charAssoc.m_cmdlRigs[cmdlPhazonVeins] = {cskrPhazonVeins, cinfPhazonVeins};
charAssoc.m_cskrToCharacter[cskrPhazonVeins] = charAssoc.m_cskrToCharacter[cskrPhazonVeins] =
std::make_pair(patternedInfo.animationParameters.animationCharacterSet, std::make_pair(patternedInfo.animationParameters.animationCharacterSet,
fmt::format(fmt("ATTACH.VEINS_{}.CSKR"), cskrPhazonVeins)); fmt::format(FMT_STRING("ATTACH.VEINS_{}.CSKR"), cskrPhazonVeins));
charAssoc.addAttachmentRig(patternedInfo.animationParameters.animationCharacterSet, cinfPhazonVeins, charAssoc.addAttachmentRig(patternedInfo.animationParameters.animationCharacterSet, cinfPhazonVeins,
cmdlPhazonVeins, "VEINS"); cmdlPhazonVeins, "VEINS");
} }

View File

@ -380,13 +380,13 @@ struct ActorParameters : BigDNA {
if (cmdlXray.isValid() && cskrXray.isValid()) { if (cmdlXray.isValid() && cskrXray.isValid()) {
charAssoc.m_cmdlRigs[cmdlXray] = {cskrXray, cinf}; charAssoc.m_cmdlRigs[cmdlXray] = {cskrXray, cinf};
charAssoc.m_cskrToCharacter[cskrXray] = std::make_pair(animParms.animationCharacterSet, charAssoc.m_cskrToCharacter[cskrXray] = std::make_pair(animParms.animationCharacterSet,
fmt::format(fmt("ATTACH.XRAY_{}.CSKR"), cskrXray)); fmt::format(FMT_STRING("ATTACH.XRAY_{}.CSKR"), cskrXray));
charAssoc.addAttachmentRig(animParms.animationCharacterSet, {}, cmdlXray, "XRAY"); charAssoc.addAttachmentRig(animParms.animationCharacterSet, {}, cmdlXray, "XRAY");
} }
if (cmdlThermal.isValid() && cskrThermal.isValid()) { if (cmdlThermal.isValid() && cskrThermal.isValid()) {
charAssoc.m_cmdlRigs[cmdlThermal] = {cskrThermal, cinf}; charAssoc.m_cmdlRigs[cmdlThermal] = {cskrThermal, cinf};
charAssoc.m_cskrToCharacter[cskrThermal] = charAssoc.m_cskrToCharacter[cskrThermal] =
std::make_pair(animParms.animationCharacterSet, fmt::format(fmt("ATTACH.THERMAL_{}.CSKR"), cskrThermal)); std::make_pair(animParms.animationCharacterSet, fmt::format(FMT_STRING("ATTACH.THERMAL_{}.CSKR"), cskrThermal));
charAssoc.addAttachmentRig(animParms.animationCharacterSet, {}, cmdlThermal, "THERMAL"); charAssoc.addAttachmentRig(animParms.animationCharacterSet, {}, cmdlThermal, "THERMAL");
} }
} }

View File

@ -8,10 +8,12 @@ namespace DataSpec::DNAMP1 {
hecl::CVar* tw_fov = nullptr; hecl::CVar* tw_fov = nullptr;
hecl::CVar* tw_hardmodeDMult = nullptr; hecl::CVar* tw_hardmodeDMult = nullptr;
hecl::CVar* tw_hardmodeWMult = nullptr; hecl::CVar* tw_hardmodeWMult = nullptr;
hecl::CVar* tw_splashScreensDisabled = nullptr;
namespace { namespace {
constexpr std::string_view skFov = "tweak.game.FieldOfView"sv; constexpr std::string_view skFov = "tweak.game.FieldOfView"sv;
constexpr std::string_view skHardModeDamageMultName = "tweak.game.HardModeDamageMult"sv; constexpr std::string_view skHardModeDamageMultName = "tweak.game.HardModeDamageMult"sv;
constexpr std::string_view skHardModeWeaponMultName = "tweak.game.HardModeWeaponMult"sv; constexpr std::string_view skHardModeWeaponMultName = "tweak.game.HardModeWeaponMult"sv;
constexpr std::string_view skSplashScreensDisabled = "tweak.game.SplashScreensDisabled"sv;
} // anonymous namespace } // anonymous namespace
void CTweakGame::_tweakGameListener(hecl::CVar* cv) { void CTweakGame::_tweakGameListener(hecl::CVar* cv) {
@ -21,6 +23,8 @@ void CTweakGame::_tweakGameListener(hecl::CVar* cv) {
x60_hardmodeDamageMult = cv->toReal(); x60_hardmodeDamageMult = cv->toReal();
} else if (cv == tw_hardmodeWMult) { } else if (cv == tw_hardmodeWMult) {
x64_hardmodeWeaponMult = cv->toReal(); x64_hardmodeWeaponMult = cv->toReal();
} else if (cv == tw_splashScreensDisabled) {
x2b_splashScreensDisabled = cv->toBoolean();
} }
} }
@ -33,14 +37,23 @@ void CTweakGame::initCVars(hecl::CVarManager* mgr) {
cv->addListener([this](hecl::CVar* cv) { _tweakGameListener(cv); }); cv->addListener([this](hecl::CVar* cv) { _tweakGameListener(cv); });
return cv; return cv;
}; };
auto assignBoolValue = [this, mgr](std::string_view name, std::string_view desc, bool& v, hecl::CVar::EFlags flags) {
hecl::CVar* cv = mgr->findOrMakeCVar(name, desc, v, flags);
// Check if the CVar was deserialized, this avoid an unnecessary conversion
if (cv->wasDeserialized())
v = cv->toBoolean();
cv->addListener([this](hecl::CVar* cv) { _tweakGameListener(cv); });
return cv;
};
tw_fov = assignRealValue(skFov, "", x24_fov, tw_fov = assignRealValue(skFov, "", x24_fov, hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive);
hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive);
tw_hardmodeDMult = tw_hardmodeDMult =
assignRealValue(skHardModeDamageMultName, "", x60_hardmodeDamageMult, assignRealValue(skHardModeDamageMultName, "", x60_hardmodeDamageMult,
hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::Cheat); hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::Cheat);
tw_hardmodeWMult = tw_hardmodeWMult =
assignRealValue(skHardModeWeaponMultName, "", x64_hardmodeWeaponMult, assignRealValue(skHardModeWeaponMultName, "", x64_hardmodeWeaponMult,
hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::Cheat); hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::Cheat);
tw_splashScreensDisabled = assignBoolValue(skSplashScreensDisabled, "", x2b_splashScreensDisabled,
hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive);
} }
} // namespace DataSpec::DNAMP1 } // namespace DataSpec::DNAMP1

View File

@ -1,10 +1,11 @@
#pragma once #pragma once
#include "../../DNACommon/Tweaks/ITweakPlayerRes.hpp" #include "DataSpec/DNACommon/Tweaks/ITweakPlayerRes.hpp"
namespace DataSpec::DNAMP1 { namespace DataSpec::DNAMP1 {
struct CTweakPlayerRes final : ITweakPlayerRes { template <bool NewRep>
struct AT_SPECIALIZE_PARMS(true, false) CTweakPlayerRes final : ITweakPlayerRes {
AT_DECL_DNA_YAML AT_DECL_DNA_YAML
String<-1> m_saveStationIcon; String<-1> m_saveStationIcon;
@ -16,6 +17,9 @@ struct CTweakPlayerRes final : ITweakPlayerRes {
String<-1> m_minesBreakSecondTopIcon; String<-1> m_minesBreakSecondTopIcon;
String<-1> m_minesBreakSecondBottomIcon; String<-1> m_minesBreakSecondBottomIcon;
String<AT_DNA_COUNT(NewRep == true ? -1 : 0)> m_mapArrowDown;
String<AT_DNA_COUNT(NewRep == true ? -1 : 0)> m_mapArrowUp;
String<-1> m_lStickN; String<-1> m_lStickN;
String<-1> m_lStickU; String<-1> m_lStickU;
String<-1> m_lStickUL; String<-1> m_lStickUL;

View File

@ -1,9 +1,10 @@
#pragma once #pragma once
#include "../../DNACommon/Tweaks/ITweakTargeting.hpp" #include "DataSpec/DNACommon/Tweaks/ITweakTargeting.hpp"
namespace DataSpec::DNAMP1 { namespace DataSpec::DNAMP1 {
struct CTweakTargeting final : public ITweakTargeting { template<bool NewRep>
struct AT_SPECIALIZE_PARMS(true, false) CTweakTargeting final : public ITweakTargeting {
AT_DECL_DNA_YAML AT_DECL_DNA_YAML
Value<atUint32> x4_targetRadiusMode; Value<atUint32> x4_targetRadiusMode;
Value<float> x8_currLockOnExitDuration; Value<float> x8_currLockOnExitDuration;
@ -132,6 +133,69 @@ struct CTweakTargeting final : public ITweakTargeting {
Value<float> x220_scanTargetClampMax; Value<float> x220_scanTargetClampMax;
Value<float> x224_angularLagSpeed; Value<float> x224_angularLagSpeed;
// RS5
Vector<float, AT_DNA_COUNT(NewRep == true ? 1 : 0)> x218_;
Vector<float, AT_DNA_COUNT(NewRep == true ? 1 : 0)> x21c_;
bool x224_ = true;
bool x225_ = false;
bool x226_ = true;
bool x227_ = true;
bool x22c_ = true;
bool x22d_ = false;
bool x22e_ = true;
bool x22f_ = true;
bool x234_ = true;
bool x235_ = false;
bool x236_ = true;
bool x237_ = true;
zeus::CVector3f x23c_ = zeus::skZero3f;
float x2c8_ = 0.25f;
float x2cc_ = 0.35f;
zeus::CColor x2d0_ = (zeus::Comp32)0xb6e6ffff;
float x2d4_ = 0.39215687f;
zeus::CColor x2d8_ = (zeus::Comp32)0xa82a00ff;
float x2dc_ = 0.78431374f;
zeus::CVector3f x2e0_ = zeus::CVector3f(0.f, 0.f, 0.46f);
float x2ec_ = 0.25f;
float x2f0_ = 0.25f;
float x2f4_ = 120.f;
float x2f8_ = 0.25f;
float x2fc_ = 3.5f;
float x300_ = 0.35f;
zeus::CColor x304_ = (zeus::Comp32)0xa82a00ff;
float x308_ = 0.78431374f;
zeus::CColor x30c_ = (zeus::Comp32)0x89d6ffff;
float x310_ = 0.5019608f;
float x314_ = 11.25f;
float x318_ = 0.25f;
float x31c_ = 0.125f;
zeus::CColor x320_ = (zeus::Comp32)0xffca28ff;
float x324_ = 0.78431374f;
zeus::CColor x328_ = (zeus::Comp32)0x89d6ffff;
float x32c_ = 0.19607843f;
float x330_ = 0.f;
float x334_ = 0.25f;
float x338_ = 3.f;
float x33c_ = 0.25f;
float x340_ = 0.25f;
float x344_ = 0.25f;
float x348_ = 0.25f;
float x34c_ = 45.f;
float x350_ = 0.5f;
float x354_ = 0.65f;
float x358_ = 1.5f;
float x35c_ = 0.18f;
float x360_ = 0.15f;
float x364_ = 0.25f;
zeus::CColor x368_ = (zeus::Comp32)0x56c1fb9f;
zeus::CColor x36c_ = (zeus::Comp32)0x49c3f6a0;
zeus::CColor x370_ = (zeus::Comp32)0x49c3f631;
zeus::CColor x374_ = (zeus::Comp32)0xff8930ff;
zeus::CColor x378_ = (zeus::Comp32)0xff2f28ff;
zeus::CColor x37c_ = (zeus::Comp32)0x93e9ffff;
zeus::CColor x380_ = (zeus::Comp32)0xff6b60ff;
CTweakTargeting() = default; CTweakTargeting() = default;
CTweakTargeting(athena::io::IStreamReader& r) { CTweakTargeting(athena::io::IStreamReader& r) {
this->read(r); this->read(r);

View File

@ -215,9 +215,8 @@ struct ANCS : BigDNA {
} }
if (force || blendType == hecl::ProjectPath::Type::None) { if (force || blendType == hecl::ProjectPath::Type::None) {
hecl::blender::Connection& conn = btok.getBlenderConnection();
DNAANCS::ReadANCSToBlender<PAKRouter<PAKBridge>, ANCS, MaterialSet, DNACMDL::SurfaceHeader_2, 4>( DNAANCS::ReadANCSToBlender<PAKRouter<PAKBridge>, ANCS, MaterialSet, DNACMDL::SurfaceHeader_2, 4>(
conn, ancs, blendPath, pakRouter, entry, dataSpec, fileChanged, force); btok, ancs, blendPath, pakRouter, entry, dataSpec, fileChanged, force);
} }
} }

View File

@ -6,7 +6,7 @@ namespace DataSpec::DNAMP2 {
using ANIMOutStream = hecl::blender::ANIMOutStream; using ANIMOutStream = hecl::blender::ANIMOutStream;
void ANIM::IANIM::sendANIMToBlender(hecl::blender::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig) const { void ANIM::IANIM::sendANIMToBlender(hecl::blender::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig) const {
os.format(fmt( os.format(FMT_STRING(
"act.hecl_fps = round({})\n" "act.hecl_fps = round({})\n"
"act.hecl_looping = {}\n"), "act.hecl_looping = {}\n"),
(1.0f / mainInterval), looping ? "True" : "False"); (1.0f / mainInterval), looping ? "True" : "False");
@ -28,7 +28,7 @@ void ANIM::IANIM::sendANIMToBlender(hecl::blender::PyOutStream& os, const DNAANI
continue; continue;
} }
os.format(fmt("bone_string = '{}'\n"), *bName); os.format(FMT_STRING("bone_string = '{}'\n"), *bName);
os << "action_group = act.groups.new(bone_string)\n" os << "action_group = act.groups.new(bone_string)\n"
"\n"; "\n";
@ -134,7 +134,7 @@ void ANIM::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
m_anim->read(reader); m_anim->read(reader);
break; break;
default: default:
Log.report(logvisor::Fatal, fmt("unrecognized ANIM version")); Log.report(logvisor::Fatal, FMT_STRING("unrecognized ANIM version"));
break; break;
} }
} }

View File

@ -1,5 +1,6 @@
#include "CINF.hpp" #include "CINF.hpp"
#include "hecl/Blender/Connection.hpp" #include "hecl/Blender/Connection.hpp"
#include "DataSpec/DNAMP3/DNAMP3.hpp"
namespace DataSpec::DNAMP2 { namespace DataSpec::DNAMP2 {
@ -34,17 +35,18 @@ void CINF::sendVertexGroupsToBlender(hecl::blender::PyOutStream& os) const {
for (atUint32 bid : boneIds) { for (atUint32 bid : boneIds) {
for (const Name& name : names) { for (const Name& name : names) {
if (name.boneId == bid) { if (name.boneId == bid) {
os.format(fmt("obj.vertex_groups.new(name='{}')\n"), name.name); os.format(FMT_STRING("obj.vertex_groups.new(name='{}')\n"), name.name);
break; break;
} }
} }
} }
} }
void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& cinfId) const { template <class PAKBridge>
void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const typename PAKBridge::PAKType::IDType& cinfId) const {
DNAANIM::RigInverter<CINF> inverter(*this); DNAANIM::RigInverter<CINF> inverter(*this);
os.format(fmt( os.format(FMT_STRING(
"arm = bpy.data.armatures.new('CINF_{}')\n" "arm = bpy.data.armatures.new('CINF_{}')\n"
"arm_obj = bpy.data.objects.new(arm.name, arm)\n" "arm_obj = bpy.data.objects.new(arm.name, arm)\n"
"bpy.context.scene.collection.objects.link(arm_obj)\n" "bpy.context.scene.collection.objects.link(arm_obj)\n"
@ -56,7 +58,7 @@ void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& c
for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones()) { for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones()) {
zeus::simd_floats originF(bone.m_origBone.origin.simd); zeus::simd_floats originF(bone.m_origBone.origin.simd);
zeus::simd_floats tailF(bone.m_tail.mSimd); zeus::simd_floats tailF(bone.m_tail.mSimd);
os.format(fmt( os.format(FMT_STRING(
"bone = arm.edit_bones.new('{}')\n" "bone = arm.edit_bones.new('{}')\n"
"bone.head = ({},{},{})\n" "bone.head = ({},{},{})\n"
"bone.tail = ({},{},{})\n" "bone.tail = ({},{},{})\n"
@ -66,17 +68,159 @@ void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& c
tailF[2], bone.m_origBone.id); tailF[2], bone.m_origBone.id);
} }
for (const Bone& bone : bones) if constexpr (std::is_same_v<PAKBridge, DNAMP3::PAKBridge>) {
if (bone.parentId != 97) if (bones.size()) {
os.format(fmt("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId); atUint32 nullId = bones[0].parentId;
for (const Bone& bone : bones)
if (bone.parentId != nullId)
os.format(FMT_STRING("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId);
}
} else {
for (const Bone& bone : bones)
if (bone.parentId != 97)
os.format(FMT_STRING("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId);
}
os << "bpy.ops.object.mode_set(mode='OBJECT')\n"; os << "bpy.ops.object.mode_set(mode='OBJECT')\n";
for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones()) for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones())
os.format(fmt("arm_obj.pose.bones['{}'].rotation_mode = 'QUATERNION'\n"), os.format(FMT_STRING("arm_obj.pose.bones['{}'].rotation_mode = 'QUATERNION'\n"),
*getBoneNameFromId(bone.m_origBone.id)); *getBoneNameFromId(bone.m_origBone.id));
} }
template void CINF::sendCINFToBlender<PAKBridge>(hecl::blender::PyOutStream& os, const UniqueID32& cinfId) const;
template void CINF::sendCINFToBlender<DNAMP3::PAKBridge>(hecl::blender::PyOutStream& os,
const UniqueID64& cinfId) const;
std::string CINF::GetCINFArmatureName(const UniqueID32& cinfId) { return fmt::format(fmt("CINF_{}"), cinfId); } template <class UniqueID>
std::string CINF::GetCINFArmatureName(const UniqueID& cinfId) { return fmt::format(FMT_STRING("CINF_{}"), cinfId); }
template std::string CINF::GetCINFArmatureName(const UniqueID32& cinfId);
template std::string CINF::GetCINFArmatureName(const UniqueID64& cinfId);
int CINF::RecursiveAddArmatureBone(const Armature& armature, const BlenderBone* bone, int parent, int& curId,
std::unordered_map<std::string, atInt32>& idMap,
std::map<std::string, int>& nameMap) {
int selId;
auto search = idMap.find(bone->name);
if (search == idMap.end()) {
selId = curId++;
idMap.emplace(std::make_pair(bone->name, selId));
} else
selId = search->second;
bones.emplace_back();
Bone& boneOut = bones.back();
nameMap[bone->name] = selId;
boneOut.id = selId;
boneOut.parentId = parent;
boneOut.origin = bone->origin;
boneOut.linkedCount = bone->children.size() + 1;
boneOut.linked.reserve(boneOut.linkedCount);
const BlenderBone* child;
boneOut.linked.push_back(parent);
for (size_t i = 0; (child = armature.getChild(bone, i)); ++i)
boneOut.linked.push_back(RecursiveAddArmatureBone(armature, child, boneOut.id, curId, idMap, nameMap));
return boneOut.id;
}
CINF::CINF(const Armature& armature, std::unordered_map<std::string, atInt32>& idMap) {
idMap.reserve(armature.bones.size());
bones.reserve(armature.bones.size());
std::map<std::string, int> nameMap;
const BlenderBone* bone = armature.getRoot();
if (bone) {
if (bone->children.size()) {
int curId = 4;
const BlenderBone* child;
for (size_t i = 0; (child = armature.getChild(bone, i)); ++i)
RecursiveAddArmatureBone(armature, child, 3, curId, idMap, nameMap);
}
bones.emplace_back();
Bone& boneOut = bones.back();
nameMap[bone->name] = 3;
boneOut.id = 3;
boneOut.parentId = 2;
boneOut.origin = bone->origin;
idMap.emplace(std::make_pair(bone->name, 3));
if (bone->children.size()) {
boneOut.linkedCount = 2;
boneOut.linked = {2, 4};
} else {
boneOut.linkedCount = 1;
boneOut.linked = {2};
}
}
boneCount = bones.size();
names.reserve(nameMap.size());
nameCount = nameMap.size();
for (const auto& name : nameMap) {
names.emplace_back();
Name& nameOut = names.back();
nameOut.name = name.first;
nameOut.boneId = name.second;
}
boneIdCount = boneCount;
boneIds.reserve(boneIdCount);
for (auto it = bones.crbegin(); it != bones.crend(); ++it)
boneIds.push_back(it->id);
}
template <class PAKBridge>
bool CINF::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const typename PAKBridge::PAKType::Entry& entry, bool force,
hecl::blender::Token& btok, std::function<void(const hecl::SystemChar*)> fileChanged) {
if (!force && outPath.isFile())
return true;
auto& conn = btok.getBlenderConnection();
if (!conn.createBlend(outPath, hecl::blender::BlendType::Armature))
return false;
auto os = conn.beginPythonOut(true);
os.format(FMT_STRING("import bpy\n"
"from mathutils import Vector\n"
"bpy.context.scene.name = 'CINF_{}'\n"
"bpy.context.scene.hecl_arm_obj = bpy.context.scene.name\n"
"\n"
"# Clear Scene\n"
"if len(bpy.data.collections):\n"
" bpy.data.collections.remove(bpy.data.collections[0])\n"
"\n"), entry.id);
CINF cinf;
cinf.read(rs);
cinf.sendCINFToBlender<PAKBridge>(os, entry.id);
os.centerView();
os.close();
return conn.saveBlend();
}
template bool CINF::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const typename PAKBridge::PAKType::Entry& entry,
bool force, hecl::blender::Token& btok,
std::function<void(const hecl::SystemChar*)> fileChanged);
template bool CINF::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<DNAMP3::PAKBridge>& pakRouter,
const typename DNAMP3::PAKBridge::PAKType::Entry& entry, bool force,
hecl::blender::Token& btok, std::function<void(const hecl::SystemChar*)> fileChanged);
bool CINF::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath,
const hecl::blender::Armature& armature) {
std::unordered_map<std::string, atInt32> boneIdMap;
CINF cinf(armature, boneIdMap);
/* Write out CINF resource */
athena::io::TransactionalFileWriter w(outPath.getAbsolutePath());
cinf.write(w);
return true;
}
} // namespace DataSpec::DNAMP2 } // namespace DataSpec::DNAMP2

View File

@ -2,6 +2,7 @@
#include "DataSpec/DNACommon/DNACommon.hpp" #include "DataSpec/DNACommon/DNACommon.hpp"
#include "DataSpec/DNACommon/RigInverter.hpp" #include "DataSpec/DNACommon/RigInverter.hpp"
#include "DNAMP2.hpp"
namespace DataSpec::DNAMP2 { namespace DataSpec::DNAMP2 {
@ -35,8 +36,27 @@ struct CINF : BigDNA {
atUint32 getBoneIdxFromId(atUint32 id) const; atUint32 getBoneIdxFromId(atUint32 id) const;
const std::string* getBoneNameFromId(atUint32 id) const; const std::string* getBoneNameFromId(atUint32 id) const;
void sendVertexGroupsToBlender(hecl::blender::PyOutStream& os) const; void sendVertexGroupsToBlender(hecl::blender::PyOutStream& os) const;
void sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID32& cinfId) const; template <class PAKBridge>
static std::string GetCINFArmatureName(const UniqueID32& cinfId); void sendCINFToBlender(hecl::blender::PyOutStream& os, const typename PAKBridge::PAKType::IDType& cinfId) const;
template <class UniqueID>
static std::string GetCINFArmatureName(const UniqueID& cinfId);
CINF() = default;
using Armature = hecl::blender::Armature;
using BlenderBone = hecl::blender::Bone;
int RecursiveAddArmatureBone(const Armature& armature, const BlenderBone* bone, int parent, int& curId,
std::unordered_map<std::string, atInt32>& idMap, std::map<std::string, int>& nameMap);
CINF(const Armature& armature, std::unordered_map<std::string, atInt32>& idMap);
template <class PAKBridge>
static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const typename PAKBridge::PAKType::Entry& entry, bool force,
hecl::blender::Token& btok, std::function<void(const hecl::SystemChar*)> fileChanged);
static bool Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPath,
const hecl::blender::Armature& armature);
}; };
} // namespace DataSpec::DNAMP2 } // namespace DataSpec::DNAMP2

View File

@ -23,6 +23,7 @@ set(DNAMP2_SOURCES
MREA.cpp MREA.cpp
MAPA.hpp MAPA.hpp
MAPU.hpp MAPU.hpp
PATH.hpp
AFSM.hpp AFSM.hpp
STRG.hpp STRG.cpp) STRG.hpp STRG.cpp)

View File

@ -8,7 +8,7 @@ void CSKR::weightVertex(hecl::blender::PyOutStream& os, const CINF& cinf, atUint
for (const SkinningRule& rule : skinningRules) { for (const SkinningRule& rule : skinningRules) {
if (idx >= accum && idx < accum + rule.vertCount) if (idx >= accum && idx < accum + rule.vertCount)
for (const SkinningRule::Weight& weight : rule.weights) for (const SkinningRule::Weight& weight : rule.weights)
os.format(fmt("vert[dvert_lay][{}] = {}\n"), cinf.getBoneIdxFromId(weight.boneId), weight.weight); os.format(FMT_STRING("vert[dvert_lay][{}] = {}\n"), cinf.getBoneIdxFromId(weight.boneId), weight.weight);
accum += rule.vertCount; accum += rule.vertCount;
} }
} }

View File

@ -4,9 +4,11 @@
#include "MLVL.hpp" #include "MLVL.hpp"
#include "CMDL.hpp" #include "CMDL.hpp"
#include "ANCS.hpp" #include "ANCS.hpp"
#include "CINF.hpp"
#include "MREA.hpp" #include "MREA.hpp"
#include "MAPA.hpp" #include "MAPA.hpp"
#include "MAPU.hpp" #include "MAPU.hpp"
#include "PATH.hpp"
#include "AFSM.hpp" #include "AFSM.hpp"
#include "SAVW.hpp" #include "SAVW.hpp"
#include "AGSC.hpp" #include "AGSC.hpp"
@ -25,7 +27,9 @@ logvisor::Module Log("urde::DNAMP2");
static bool GetNoShare(std::string_view name) { static bool GetNoShare(std::string_view name) {
std::string lowerName(name); std::string lowerName(name);
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower); std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
if (!lowerName.compare(0, 7, "metroid")) if (lowerName.compare(0, 7, "metroid") == 0)
return false;
if (lowerName.compare(0, 8, "frontend") == 0)
return false; return false;
return true; return true;
} }
@ -115,7 +119,7 @@ void PAKBridge::build() {
areaDeps.name = hecl::SystemString(_SYS_STR("MREA_")) + hecl::SystemStringConv(idStr).c_str(); areaDeps.name = hecl::SystemString(_SYS_STR("MREA_")) + hecl::SystemStringConv(idStr).c_str();
} }
} }
hecl::SystemString num = fmt::format(fmt(_SYS_STR("{:02d} ")), ai); hecl::SystemString num = fmt::format(FMT_STRING(_SYS_STR("{:02d} ")), ai);
areaDeps.name = num + areaDeps.name; areaDeps.name = num + areaDeps.name;
areaDeps.layers.reserve(area.depLayerCount - 1); areaDeps.layers.reserve(area.depLayerCount - 1);
@ -126,7 +130,7 @@ void PAKBridge::build() {
layer.name = LayerName(mlvl.layerNames[layerIdx++]); layer.name = LayerName(mlvl.layerNames[layerIdx++]);
layer.active = layerFlags.flags >> (l - 1) & 0x1; layer.active = layerFlags.flags >> (l - 1) & 0x1;
layer.name = hecl::StringUtils::TrimWhitespace(layer.name); layer.name = hecl::StringUtils::TrimWhitespace(layer.name);
num = fmt::format(fmt(_SYS_STR("{:02d} ")), l - 1); num = fmt::format(FMT_STRING(_SYS_STR("{:02d} ")), l - 1);
layer.name = num + layer.name; layer.name = num + layer.name;
layer.resources.reserve(area.depLayers[l] - r); layer.resources.reserve(area.depLayers[l] - r);
@ -159,17 +163,29 @@ void PAKBridge::addCMDLRigPairs(PAKRouter<PAKBridge>& pakRouter, CharacterAssoci
for (const ANCS::CharacterSet::CharacterInfo& ci : ancs.characterSet.characters) { for (const ANCS::CharacterSet::CharacterInfo& ci : ancs.characterSet.characters) {
charAssoc.m_cmdlRigs[ci.cmdl] = {ci.cskr, ci.cinf}; charAssoc.m_cmdlRigs[ci.cmdl] = {ci.cskr, ci.cinf};
charAssoc.m_cskrToCharacter[ci.cskr] = charAssoc.m_cskrToCharacter[ci.cskr] =
std::make_pair(entry.second.id, fmt::format(fmt("{}_{}.CSKR"), ci.name, ci.cskr)); std::make_pair(entry.second.id, fmt::format(FMT_STRING("{}_{}.CSKR"), ci.name, ci.cskr));
if (ci.cmdlIce.isValid()) { if (ci.cmdlIce.isValid()) {
charAssoc.m_cmdlRigs[ci.cmdlIce] = {ci.cskrIce, ci.cinf}; charAssoc.m_cmdlRigs[ci.cmdlIce] = {ci.cskrIce, ci.cinf};
charAssoc.m_cskrToCharacter[ci.cskrIce] = charAssoc.m_cskrToCharacter[ci.cskrIce] =
std::make_pair(entry.second.id, fmt::format(fmt("{}.ICE_{}.CSKR"), ci.name, ci.cskrIce)); std::make_pair(entry.second.id, fmt::format(FMT_STRING("{}.ICE_{}.CSKR"), ci.name, ci.cskrIce));
} }
} }
} }
} }
} }
void PAKBridge::addPATHToMREA(PAKRouter<PAKBridge>& pakRouter,
std::unordered_map<UniqueID32, UniqueID32>& pathToMrea) const {
for (const auto& [id, entry] : m_pak.m_entries) {
if (entry.type == FOURCC('MREA')) {
PAKEntryReadStream rs = entry.beginReadStream(m_node);
UniqueID32 pathID = MREA::GetPATHId(rs);
if (pathID.isValid())
pathToMrea[pathID] = id;
}
}
}
static const atVec4f BottomRow = {{0.f, 0.f, 0.f, 1.f}}; static const atVec4f BottomRow = {{0.f, 0.f, 0.f, 1.f}};
void PAKBridge::addMAPATransforms(PAKRouter<PAKBridge>& pakRouter, void PAKBridge::addMAPATransforms(PAKRouter<PAKBridge>& pakRouter,
@ -186,13 +202,23 @@ void PAKBridge::addMAPATransforms(PAKRouter<PAKBridge>& pakRouter,
if (mlvl.worldNameId.isValid()) if (mlvl.worldNameId.isValid())
pathOverrides[mlvl.worldNameId] = hecl::ProjectPath(mlvlDirPath, pathOverrides[mlvl.worldNameId] = hecl::ProjectPath(mlvlDirPath,
fmt::format(fmt(_SYS_STR("!name_{}.yaml")), mlvl.worldNameId)); fmt::format(FMT_STRING(_SYS_STR("!name_{}.yaml")), mlvl.worldNameId));
for (const MLVL::Area& area : mlvl.areas) { for (const MLVL::Area& area : mlvl.areas) {
{
/* Get PATH transform */
const nod::Node* areaNode;
const PAK::Entry* areaEntry = pakRouter.lookupEntry(area.areaMREAId, &areaNode);
PAKEntryReadStream rs = areaEntry->beginReadStream(*areaNode);
UniqueID32 pathId = MREA::GetPATHId(rs);
if (pathId.isValid())
addTo[pathId] = zeus::CMatrix4f(area.transformMtx[0], area.transformMtx[1], area.transformMtx[2], BottomRow)
.transposed();
}
hecl::ProjectPath areaDirPath = pakRouter.getWorking(area.areaMREAId).getParentPath(); hecl::ProjectPath areaDirPath = pakRouter.getWorking(area.areaMREAId).getParentPath();
if (area.areaNameId.isValid()) if (area.areaNameId.isValid())
pathOverrides[area.areaNameId] = hecl::ProjectPath(areaDirPath, pathOverrides[area.areaNameId] = hecl::ProjectPath(areaDirPath,
fmt::format(fmt(_SYS_STR("!name_{}.yaml")), area.areaNameId)); fmt::format(FMT_STRING(_SYS_STR("!name_{}.yaml")), area.areaNameId));
} }
if (mlvl.worldMap.isValid()) { if (mlvl.worldMap.isValid()) {
@ -234,6 +260,8 @@ ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const nod::Node& pakNode, con
return {SAVWCommon::ExtractSAVW<SAVW>, {_SYS_STR(".yaml")}}; return {SAVWCommon::ExtractSAVW<SAVW>, {_SYS_STR(".yaml")}};
case SBIG('CMDL'): case SBIG('CMDL'):
return {CMDL::Extract, {_SYS_STR(".blend")}, 1}; return {CMDL::Extract, {_SYS_STR(".blend")}, 1};
case SBIG('CINF'):
return {CINF::Extract<PAKBridge>, {_SYS_STR(".blend")}, 1};
case SBIG('ANCS'): case SBIG('ANCS'):
return {ANCS::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 2}; return {ANCS::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 2};
case SBIG('MLVL'): case SBIG('MLVL'):
@ -244,6 +272,8 @@ ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const nod::Node& pakNode, con
return {MAPA::Extract, {_SYS_STR(".blend")}, 4}; return {MAPA::Extract, {_SYS_STR(".blend")}, 4};
case SBIG('MAPU'): case SBIG('MAPU'):
return {MAPU::Extract, {_SYS_STR(".blend")}, 5}; return {MAPU::Extract, {_SYS_STR(".blend")}, 5};
case SBIG('PATH'):
return {PATH::Extract, {_SYS_STR(".blend")}, 5};
case SBIG('FSM2'): case SBIG('FSM2'):
return {DNAFSM2::ExtractFSM2<UniqueID32>, {_SYS_STR(".yaml")}}; return {DNAFSM2::ExtractFSM2<UniqueID32>, {_SYS_STR(".yaml")}};
case SBIG('FONT'): case SBIG('FONT'):

View File

@ -31,6 +31,9 @@ public:
void addCMDLRigPairs(PAKRouter<PAKBridge>& pakRouter, CharacterAssociations<UniqueID32>& charAssoc) const; void addCMDLRigPairs(PAKRouter<PAKBridge>& pakRouter, CharacterAssociations<UniqueID32>& charAssoc) const;
void addPATHToMREA(PAKRouter<PAKBridge>& pakRouter,
std::unordered_map<UniqueID32, UniqueID32>& pathToMrea) const;
void addMAPATransforms(PAKRouter<PAKBridge>& pakRouter, std::unordered_map<UniqueID32, zeus::CMatrix4f>& addTo, void addMAPATransforms(PAKRouter<PAKBridge>& pakRouter, std::unordered_map<UniqueID32, zeus::CMatrix4f>& addTo,
std::unordered_map<UniqueID32, hecl::ProjectPath>& pathOverrides) const; std::unordered_map<UniqueID32, hecl::ProjectPath>& pathOverrides) const;
}; };

View File

@ -26,12 +26,15 @@ void DeafBabe::BlenderInit(hecl::blender::PyOutStream& os) {
" 'Rubber':(0.09, 0.02, 0.01)}\n" " 'Rubber':(0.09, 0.02, 0.01)}\n"
"\n" "\n"
"# Diffuse Color Maker\n" "# Diffuse Color Maker\n"
"from mathutils import Color\n"
"def make_color(index, mat_type, name):\n" "def make_color(index, mat_type, name):\n"
" new_mat = bpy.data.materials.new(name)\n" " new_mat = bpy.data.materials.new(name)\n"
" if mat_type in TYPE_COLORS:\n" " if mat_type in TYPE_COLORS:\n"
" new_mat.diffuse_color = TYPE_COLORS[mat_type]\n" " new_mat.diffuse_color = TYPE_COLORS[mat_type] + (1.0,)\n"
" else:\n" " else:\n"
" new_mat.diffuse_color.hsv = ((index / 6.0) % 1.0, 1.0-((index // 6) / 6.0), 1)\n" " col = Color()\n"
" col.hsv = ((index / 6.0) % 1.0, 1.0-((index // 6) / 6.0), 1)\n"
" new_mat.diffuse_color = tuple(col) + (1.0,)\n"
" return new_mat\n" " return new_mat\n"
"\n" "\n"
"bpy.types.Material.retro_unknown = bpy.props.BoolProperty(description='Retro: Unknown (U)')\n" "bpy.types.Material.retro_unknown = bpy.props.BoolProperty(description='Retro: Unknown (U)')\n"
@ -262,7 +265,7 @@ void DeafBabe::insertNoClimb(hecl::blender::PyOutStream& os) const {
if (edgeIdx == -1) if (edgeIdx == -1)
continue; continue;
const Edge& edge = edgeVertConnections[edgeIdx]; const Edge& edge = edgeVertConnections[edgeIdx];
os.format(fmt( os.format(FMT_STRING(
"edge = col_bm.edges.get((col_bm.verts[{}], col_bm.verts[{}]))\n" "edge = col_bm.edges.get((col_bm.verts[{}], col_bm.verts[{}]))\n"
"if edge:\n" "if edge:\n"
" edge.seam = True\n"), " edge.seam = True\n"),

View File

@ -13,7 +13,7 @@ namespace DNAMP2 {
void MREA::StreamReader::nextBlock() { void MREA::StreamReader::nextBlock() {
if (m_nextBlk >= m_blkCount) if (m_nextBlk >= m_blkCount)
Log.report(logvisor::Fatal, fmt("MREA stream overrun")); Log.report(logvisor::Fatal, FMT_STRING("MREA stream overrun"));
BlockInfo& info = m_blockInfos[m_nextBlk++]; BlockInfo& info = m_blockInfos[m_nextBlk++];
@ -68,8 +68,7 @@ MREA::StreamReader::StreamReader(athena::io::IStreamReader& source, atUint32 blk
, m_blkCount(blkCount) { , m_blkCount(blkCount) {
m_blockInfos.reserve(blkCount); m_blockInfos.reserve(blkCount);
for (atUint32 i = 0; i < blkCount; ++i) { for (atUint32 i = 0; i < blkCount; ++i) {
m_blockInfos.emplace_back(); BlockInfo& info = m_blockInfos.emplace_back();
BlockInfo& info = m_blockInfos.back();
info.read(source); info.read(source);
m_totalDecompLen += info.decompSize; m_totalDecompLen += info.decompSize;
} }
@ -87,7 +86,7 @@ void MREA::StreamReader::seek(atInt64 diff, athena::SeekOrigin whence) {
} }
if (target >= m_totalDecompLen) if (target >= m_totalDecompLen)
Log.report(logvisor::Fatal, fmt("MREA stream seek overrun")); Log.report(logvisor::Fatal, FMT_STRING("MREA stream seek overrun"));
/* Determine which block contains position */ /* Determine which block contains position */
atUint32 dAccum = 0; atUint32 dAccum = 0;
@ -98,11 +97,11 @@ void MREA::StreamReader::seek(atInt64 diff, athena::SeekOrigin whence) {
if (newAccum > target) if (newAccum > target)
break; break;
dAccum = newAccum; dAccum = newAccum;
++bIdx;
if (info.compSize) if (info.compSize)
cAccum += ROUND_UP_32(info.compSize); cAccum += ROUND_UP_32(info.compSize);
else else
cAccum += info.decompSize; cAccum += info.decompSize;
++bIdx;
} }
/* Seek source if needed */ /* Seek source if needed */
@ -116,6 +115,41 @@ void MREA::StreamReader::seek(atInt64 diff, athena::SeekOrigin whence) {
m_posInBlk = target - dAccum; m_posInBlk = target - dAccum;
} }
void MREA::StreamReader::seekToSection(atUint32 sec, const std::vector<atUint32>& secSizes) {
/* Determine which block contains section */
atUint32 sAccum = 0;
atUint32 dAccum = 0;
atUint32 cAccum = 0;
atUint32 bIdx = 0;
for (BlockInfo& info : m_blockInfos) {
atUint32 newSAccum = sAccum + info.secCount;
if (newSAccum > sec)
break;
sAccum = newSAccum;
dAccum += info.decompSize;
if (info.compSize)
cAccum += ROUND_UP_32(info.compSize);
else
cAccum += info.decompSize;
++bIdx;
}
/* Seek source if needed */
if (bIdx != m_nextBlk - 1) {
m_source.seek(m_blkBase + cAccum, athena::SeekOrigin::Begin);
m_nextBlk = bIdx;
nextBlock();
}
/* Seek within block */
atUint32 target = dAccum;
while (sAccum != sec)
target += secSizes[sAccum++];
m_pos = target;
m_posInBlk = target - dAccum;
}
atUint64 MREA::StreamReader::readUBytesToBuf(void* buf, atUint64 len) { atUint64 MREA::StreamReader::readUBytesToBuf(void* buf, atUint64 len) {
atUint8* bufCur = reinterpret_cast<atUint8*>(buf); atUint8* bufCur = reinterpret_cast<atUint8*>(buf);
atUint64 rem = len; atUint64 rem = len;
@ -191,7 +225,7 @@ bool MREA::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
/* Open Py Stream and read sections */ /* Open Py Stream and read sections */
hecl::blender::PyOutStream os = conn.beginPythonOut(true); hecl::blender::PyOutStream os = conn.beginPythonOut(true);
os.format(fmt( os.format(FMT_STRING(
"import bpy\n" "import bpy\n"
"import bmesh\n" "import bmesh\n"
"from mathutils import Vector\n" "from mathutils import Vector\n"
@ -238,7 +272,7 @@ bool MREA::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
drs.seek(secStart + head.secSizes[curSec++], athena::SeekOrigin::Begin); drs.seek(secStart + head.secSizes[curSec++], athena::SeekOrigin::Begin);
curSec += DNACMDL::ReadGeomSectionsToBlender<PAKRouter<PAKBridge>, MaterialSet, RigPair, DNACMDL::SurfaceHeader_2>( curSec += DNACMDL::ReadGeomSectionsToBlender<PAKRouter<PAKBridge>, MaterialSet, RigPair, DNACMDL::SurfaceHeader_2>(
os, drs, pakRouter, entry, dummy, true, true, vertAttribs, m, head.secCount, 0, &head.secSizes[curSec]); os, drs, pakRouter, entry, dummy, true, true, vertAttribs, m, head.secCount, 0, &head.secSizes[curSec]);
os.format(fmt( os.format(FMT_STRING(
"obj.retro_disable_enviro_visor = {}\n" "obj.retro_disable_enviro_visor = {}\n"
"obj.retro_disable_thermal_visor = {}\n" "obj.retro_disable_thermal_visor = {}\n"
"obj.retro_disable_xray_visor = {}\n" "obj.retro_disable_xray_visor = {}\n"
@ -296,5 +330,19 @@ bool MREA::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
return conn.saveBlend(); return conn.saveBlend();
} }
UniqueID32 MREA::GetPATHId(PAKEntryReadStream& rs) {
/* Do extract */
Header head;
head.read(rs);
rs.seekAlign32();
/* MREA decompression stream */
StreamReader drs(rs, head.compressedBlockCount);
/* Skip to PATH */
drs.seekToSection(head.pathSecIdx, head.secSizes);
return {drs};
}
} // namespace DNAMP2 } // namespace DNAMP2
} // namespace DataSpec } // namespace DataSpec

View File

@ -43,6 +43,7 @@ struct MREA {
public: public:
StreamReader(athena::io::IStreamReader& source, atUint32 blkCount); StreamReader(athena::io::IStreamReader& source, atUint32 blkCount);
void seek(atInt64 diff, athena::SeekOrigin whence) override; void seek(atInt64 diff, athena::SeekOrigin whence) override;
void seekToSection(atUint32 sec, const std::vector<atUint32>& secSizes);
atUint64 position() const override { return m_pos; } atUint64 position() const override { return m_pos; }
atUint64 length() const override { return m_totalDecompLen; } atUint64 length() const override { return m_totalDecompLen; }
atUint64 readUBytesToBuf(void* buf, atUint64 len) override; atUint64 readUBytesToBuf(void* buf, atUint64 len) override;
@ -118,6 +119,8 @@ struct MREA {
Value<atVec3f> aabb[2]; Value<atVec3f> aabb[2];
}; };
static UniqueID32 GetPATHId(PAKEntryReadStream& rs);
static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath, static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const DNAMP2::PAK::Entry& entry, bool, PAKRouter<PAKBridge>& pakRouter, const DNAMP2::PAK::Entry& entry, bool,
hecl::blender::Token& btok, std::function<void(const hecl::SystemChar*)>); hecl::blender::Token& btok, std::function<void(const hecl::SystemChar*)>);

View File

@ -11,7 +11,7 @@ std::string PAK::bestEntryName(const nod::Node& pakNode, const Entry& entry, std
AGSC::Header header; AGSC::Header header;
header.read(rs); header.read(rs);
catalogueName = header.groupName; catalogueName = header.groupName;
return fmt::format(fmt("{}_{}"), header.groupName, entry.id); return fmt::format(FMT_STRING("{}_{}"), header.groupName, entry.id);
} }
return DNAMP1::PAK::bestEntryName(pakNode, entry, catalogueName); return DNAMP1::PAK::bestEntryName(pakNode, entry, catalogueName);

6
DataSpec/DNAMP2/PATH.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include "DataSpec/DNACommon/PATH.hpp"
namespace DataSpec::DNAMP2 {
using PATH = DNAPATH::PATH<PAKBridge>;
} // namespace DataSpec::DNAMP2

View File

@ -49,11 +49,11 @@ template <>
void STRG::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) { void STRG::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
atUint32 magic = reader.readUint32Big(); atUint32 magic = reader.readUint32Big();
if (magic != 0x87654321) if (magic != 0x87654321)
Log.report(logvisor::Error, fmt("invalid STRG magic")); Log.report(logvisor::Error, FMT_STRING("invalid STRG magic"));
atUint32 version = reader.readUint32Big(); atUint32 version = reader.readUint32Big();
if (version != 1) if (version != 1)
Log.report(logvisor::Error, fmt("invalid STRG version")); Log.report(logvisor::Error, FMT_STRING("invalid STRG version"));
_read(reader); _read(reader);
} }
@ -158,22 +158,22 @@ void STRG::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
if (lang.first == "names") if (lang.first == "names")
continue; continue;
if (lang.first.size() != 4) { if (lang.first.size() != 4) {
Log.report(logvisor::Warning, fmt("STRG language string '{}' must be exactly 4 characters; skipping"), lang.first); Log.report(logvisor::Warning, FMT_STRING("STRG language string '{}' must be exactly 4 characters; skipping"), lang.first);
return; return;
} }
if (lang.second->m_type != YAML_SEQUENCE_NODE) { if (lang.second->m_type != YAML_SEQUENCE_NODE) {
Log.report(logvisor::Warning, fmt("STRG language string '{}' must contain a sequence; skipping"), lang.first); Log.report(logvisor::Warning, FMT_STRING("STRG language string '{}' must contain a sequence; skipping"), lang.first);
return; return;
} }
for (const auto& str : lang.second->m_seqChildren) { for (const auto& str : lang.second->m_seqChildren) {
if (str->m_type != YAML_SCALAR_NODE) { if (str->m_type != YAML_SCALAR_NODE) {
Log.report(logvisor::Warning, fmt("STRG language '{}' must contain all scalars; skipping"), lang.first); Log.report(logvisor::Warning, FMT_STRING("STRG language '{}' must contain all scalars; skipping"), lang.first);
return; return;
} }
} }
} }
} else { } else {
Log.report(logvisor::Warning, fmt("STRG must have a mapping root node; skipping")); Log.report(logvisor::Warning, FMT_STRING("STRG must have a mapping root node; skipping"));
return; return;
} }

View File

@ -14,6 +14,7 @@ struct STRG : ISTRG {
std::map<std::string, int32_t> names; std::map<std::string, int32_t> names;
int32_t lookupIdx(std::string_view name) const override { int32_t lookupIdx(std::string_view name) const override {
// TODO: Heterogeneous lookup when C++20 available
auto search = names.find(name.data()); auto search = names.find(name.data());
if (search == names.end()) if (search == names.end())
return -1; return -1;

View File

@ -9,7 +9,7 @@ using ANIMOutStream = hecl::blender::ANIMOutStream;
void ANIM::IANIM::sendANIMToBlender(hecl::blender::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig, void ANIM::IANIM::sendANIMToBlender(hecl::blender::PyOutStream& os, const DNAANIM::RigInverter<CINF>& rig,
bool additive) const { bool additive) const {
os.format(fmt( os.format(FMT_STRING(
"act.hecl_fps = round({})\n" "act.hecl_fps = round({})\n"
"act.hecl_additive = {}\n" "act.hecl_additive = {}\n"
"act.hecl_looping = {}\n"), "act.hecl_looping = {}\n"),
@ -32,7 +32,7 @@ void ANIM::IANIM::sendANIMToBlender(hecl::blender::PyOutStream& os, const DNAANI
continue; continue;
} }
os.format(fmt("bone_string = '{}'\n"), *bName); os.format(FMT_STRING("bone_string = '{}'\n"), *bName);
os << "action_group = act.groups.new(bone_string)\n" os << "action_group = act.groups.new(bone_string)\n"
"\n"; "\n";
@ -138,7 +138,7 @@ void ANIM::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
m_anim->read(reader); m_anim->read(reader);
break; break;
default: default:
Log.report(logvisor::Fatal, fmt("unrecognized ANIM version")); Log.report(logvisor::Fatal, FMT_STRING("unrecognized ANIM version"));
break; break;
} }
} }

View File

@ -283,9 +283,8 @@ struct CHAR : BigDNA {
} }
if (force || blendType == hecl::ProjectPath::Type::None) { if (force || blendType == hecl::ProjectPath::Type::None) {
hecl::blender::Connection& conn = btok.getBlenderConnection();
DNAANCS::ReadANCSToBlender<PAKRouter<PAKBridge>, CHAR, MaterialSet, DNACMDL::SurfaceHeader_3, 4>( DNAANCS::ReadANCSToBlender<PAKRouter<PAKBridge>, CHAR, MaterialSet, DNACMDL::SurfaceHeader_3, 4>(
conn, aChar, blendPath, pakRouter, entry, dataSpec, fileChanged, force); btok, aChar, blendPath, pakRouter, entry, dataSpec, fileChanged, force);
} }
} }

View File

@ -1,48 +0,0 @@
#include "CINF.hpp"
#include "hecl/Blender/Connection.hpp"
namespace DataSpec::DNAMP3 {
void CINF::sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID64& cinfId) const {
DNAANIM::RigInverter<CINF> inverter(*this);
os.format(fmt("arm = bpy.data.armatures.new('CINF_{}')\n"
"arm_obj = bpy.data.objects.new(arm.name, arm)\n"
"bpy.context.scene.collection.objects.link(arm_obj)\n"
"bpy.context.view_layer.objects.active = arm_obj\n"
"bpy.ops.object.mode_set(mode='EDIT')\n"
"arm_bone_table = {{}}\n"),
cinfId);
for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones()) {
zeus::simd_floats originF(bone.m_origBone.origin.simd);
zeus::simd_floats tailF(bone.m_tail.mSimd);
os.format(fmt(
"bone = arm.edit_bones.new('{}')\n"
"bone.head = ({},{},{})\n"
"bone.tail = ({},{},{})\n"
"bone.use_inherit_scale = False\n"
"arm_bone_table[{}] = bone\n"),
*getBoneNameFromId(bone.m_origBone.id), originF[0], originF[1], originF[2], tailF[0], tailF[1],
tailF[2], bone.m_origBone.id);
}
if (bones.size()) {
atUint32 nullId = bones[0].parentId;
for (const Bone& bone : bones)
if (bone.parentId != nullId)
os.format(fmt("arm_bone_table[{}].parent = arm_bone_table[{}]\n"), bone.id, bone.parentId);
}
os << "bpy.ops.object.mode_set(mode='OBJECT')\n";
for (const DNAANIM::RigInverter<CINF>::Bone& bone : inverter.getBones())
os.format(fmt("arm_obj.pose.bones['{}'].rotation_mode = 'QUATERNION'\n"),
*getBoneNameFromId(bone.m_origBone.id));
}
std::string CINF::GetCINFArmatureName(const UniqueID64& cinfId) {
return fmt::format(fmt("CINF_{}"), cinfId);
}
} // namespace DataSpec::DNAMP3

View File

@ -1,14 +1,8 @@
#pragma once #pragma once
#include "DataSpec/DNACommon/DNACommon.hpp" #include "DataSpec/DNACommon/DNACommon.hpp"
#include "../DNAMP2/CINF.hpp" #include "DataSpec/DNAMP2/CINF.hpp"
namespace DataSpec::DNAMP3 { namespace DataSpec::DNAMP3 {
using CINF = DNAMP2::CINF;
struct CINF : DNAMP2::CINF {
Delete expl;
void sendCINFToBlender(hecl::blender::PyOutStream& os, const UniqueID64& cinfId) const;
static std::string GetCINFArmatureName(const UniqueID64& cinfId);
};
} // namespace DataSpec::DNAMP3 } // namespace DataSpec::DNAMP3

View File

@ -6,341 +6,295 @@ using Stream = hecl::blender::PyOutStream;
namespace DataSpec::DNAMP3 { namespace DataSpec::DNAMP3 {
using Material = MaterialSet::Material; using Material = MaterialSet::Material;
template <>
void MaterialSet::Material::SectionFactory::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
DNAFourCC type;
type.read(reader);
switch (ISection::Type(type.toUint32())) {
case ISection::Type::PASS:
section = std::make_unique<SectionPASS>();
section->read(reader);
break;
case ISection::Type::CLR:
section = std::make_unique<SectionCLR>();
section->read(reader);
break;
case ISection::Type::INT:
section = std::make_unique<SectionINT>();
section->read(reader);
break;
default:
section.reset();
break;
}
}
template <>
void MaterialSet::Material::SectionFactory::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
if (!section)
return;
writer.writeUBytes((atUint8*)&section->m_type, 4);
section->write(writer);
}
template <>
void MaterialSet::Material::SectionFactory::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
s += 4;
section->binarySize(s);
}
template <> template <>
void MaterialSet::Material::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) { void MaterialSet::Material::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
header.read(reader); header.read(reader);
sections.clear(); chunks.clear();
do { do { chunks.emplace_back().read(reader); } while (!chunks.back().holds_alternative<END>());
sections.emplace_back(); chunks.pop_back();
sections.back().read(reader);
} while (sections.back().section);
sections.pop_back();
} }
template <> template <>
void MaterialSet::Material::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) { void MaterialSet::Material::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
header.write(writer); header.write(writer);
for (const SectionFactory& section : sections) for (const auto& chunk : chunks)
section.write(writer); chunk.visit([&](auto& arg) { arg.write(writer); });
writer.writeUBytes((atUint8*)"END ", 4); DNAFourCC(FOURCC('END ')).write(writer);
} }
template <> template <>
void MaterialSet::Material::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) { void MaterialSet::Material::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
header.binarySize(s); header.binarySize(s);
for (const SectionFactory& section : sections) for (const auto& chunk : chunks)
section.binarySize(s); chunk.visit([&](auto& arg) { arg.binarySize(s); });
s += 4; s += 4;
} }
void MaterialSet::RegisterMaterialProps(Stream& out) { void MaterialSet::RegisterMaterialProps(Stream& out) {
out << "bpy.types.Material.retro_alpha_test = bpy.props.BoolProperty(name='Retro: Punchthrough Alpha')\n" out << "bpy.types.Material.retro_enable_bloom = bpy.props.BoolProperty(name='Retro: Enable Bloom')\n"
"bpy.types.Material.retro_force_lighting_stage = bpy.props.BoolProperty(name='Retro: Force Lighting Stage')\n"
"bpy.types.Material.retro_pre_inca_transparency = bpy.props.BoolProperty(name='Retro: Pre-INCA Transparency')\n"
"bpy.types.Material.retro_alpha_test = bpy.props.BoolProperty(name='Retro: Alpha Test')\n"
"bpy.types.Material.retro_shadow_occluder = bpy.props.BoolProperty(name='Retro: Shadow Occluder')\n" "bpy.types.Material.retro_shadow_occluder = bpy.props.BoolProperty(name='Retro: Shadow Occluder')\n"
"bpy.types.Material.retro_lightmapped = bpy.props.BoolProperty(name='Retro: Lightmapped')\n" "bpy.types.Material.retro_solid_white = bpy.props.BoolProperty(name='Retro: Solid White Only')\n"
"bpy.types.Material.retro_opac = bpy.props.IntProperty(name='Retro: OPAC')\n" "bpy.types.Material.retro_reflection_alpha_target = bpy.props.BoolProperty(name='Retro: Reflection Alpha Target')\n"
"bpy.types.Material.retro_blod = bpy.props.IntProperty(name='Retro: BLOD')\n" "bpy.types.Material.retro_solid_color = bpy.props.BoolProperty(name='Retro: Solid Color Only')\n"
"bpy.types.Material.retro_bloi = bpy.props.IntProperty(name='Retro: BLOI')\n" "bpy.types.Material.retro_exclude_scan = bpy.props.BoolProperty(name='Retro: Exclude From Scan Visor')\n"
"bpy.types.Material.retro_bnif = bpy.props.IntProperty(name='Retro: BNIF')\n" "bpy.types.Material.retro_xray_opaque = bpy.props.BoolProperty(name='Retro: XRay Opaque')\n"
"bpy.types.Material.retro_xrbr = bpy.props.IntProperty(name='Retro: XRBR')\n" "bpy.types.Material.retro_xray_alpha_target = bpy.props.BoolProperty(name='Retro: XRay Alpha Target')\n"
"bpy.types.Material.retro_inca_color_mod = bpy.props.BoolProperty(name='Retro: INCA Color Mod')\n"
"\n"; "\n";
} }
static void LoadTexture(Stream& out, const UniqueID64& tex,
const PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry) {
if (!tex.isValid()) {
out << "image = None\n";
return;
}
std::string texName = pakRouter.getBestEntryName(tex);
const nod::Node* node;
const typename PAKRouter<PAKBridge>::EntryType* texEntry = pakRouter.lookupEntry(tex, &node);
hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry);
if (!txtrPath.isNone()) {
txtrPath.makeDirChain(false);
PAKEntryReadStream rs = texEntry->beginReadStream(*node);
TXTR::Extract(rs, txtrPath);
}
hecl::SystemString resPath = pakRouter.getResourceRelativePath(entry, tex);
hecl::SystemUTF8Conv resPathView(resPath);
out.format(FMT_STRING("if '{}' in bpy.data.images:\n"
" image = bpy.data.images['{}']\n"
"else:\n"
" image = bpy.data.images.load('''//{}''')\n"
" image.name = '{}'\n"
"\n"), texName, texName, resPathView, texName);
}
void MaterialSet::ConstructMaterial(Stream& out, const PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, void MaterialSet::ConstructMaterial(Stream& out, const PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry,
const Material& material, unsigned groupIdx, unsigned matIdx) { const Material& material, unsigned groupIdx, unsigned matIdx) {
unsigned i; out.format(FMT_STRING("new_material = bpy.data.materials.new('MAT_{}_{}')\n"), groupIdx, matIdx);
out << "new_material.use_fake_user = True\n"
out.format(fmt( "new_material.use_nodes = True\n"
"new_material = bpy.data.materials.new('MAT_{}_{}')\n" "new_material.use_backface_culling = True\n"
"new_material.use_shadows = True\n" "new_material.show_transparent_back = False\n"
"new_material.use_transparent_shadows = True\n" "new_material.blend_method = 'BLEND'\n"
"new_material.diffuse_color = (1.0,1.0,1.0)\n" "new_nodetree = new_material.node_tree\n"
"new_material.use_nodes = True\n" "for n in new_nodetree.nodes:\n"
"new_material.blend_method = 'BLEND'\n" " new_nodetree.nodes.remove(n)\n"
"new_nodetree = new_material.node_tree\n" "\n"
"material_node = new_nodetree.nodes['Material']\n" "gridder = hecl.Nodegrid(new_nodetree)\n"
"final_node = new_nodetree.nodes['Output']\n" "new_nodetree.nodes.remove(gridder.frames[2])\n"
"\n" "\n"
"gridder = hecl.Nodegrid(new_nodetree)\n" "texture_nodes = []\n"
"gridder.place_node(final_node, 3)\n" "kcolors = {}\n"
"gridder.place_node(material_node, 0)\n" "kalphas = {}\n"
"material_node.material = new_material\n" "tex_links = []\n"
"\n" "\n";
"texture_nodes = []\n"
"kcolor_nodes = []\n"
"color_combiner_nodes = []\n"
"alpha_combiner_nodes = []\n"
"tex_links = []\n"
"tev_reg_sockets = [None]*4\n"
"\n"),
groupIdx, matIdx);
/* Material Flags */ /* Material Flags */
out.format(fmt( out.format(FMT_STRING(
"new_material.retro_enable_bloom = {}\n"
"new_material.retro_force_lighting_stage = {}\n"
"new_material.retro_pre_inca_transparency = {}\n"
"new_material.retro_alpha_test = {}\n" "new_material.retro_alpha_test = {}\n"
"new_material.retro_shadow_occluder = {}\n" "new_material.retro_shadow_occluder = {}\n"
"new_material.diffuse_color = (1, 1, 1, {})\n"), "new_material.retro_solid_white = {}\n"
"new_material.retro_reflection_alpha_target = {}\n"
"new_material.retro_solid_color = {}\n"
"new_material.retro_exclude_scan = {}\n"
"new_material.retro_xray_opaque = {}\n"
"new_material.retro_xray_alpha_target = {}\n"
"new_material.retro_inca_color_mod = False\n"),
material.header.flags.enableBloom() ? "True" : "False",
material.header.flags.forceLightingStage() ? "True" : "False",
material.header.flags.preIncaTransparency() ? "True" : "False",
material.header.flags.alphaTest() ? "True" : "False", material.header.flags.alphaTest() ? "True" : "False",
material.header.flags.shadowOccluderMesh() ? "True" : "False", material.header.flags.shadowOccluderMesh() ? "True" : "False",
material.header.flags.shadowOccluderMesh() ? "0" : "1"); material.header.flags.justWhite() ? "True" : "False",
material.header.flags.reflectionAlphaTarget() ? "True" : "False",
material.header.flags.justSolidColor() ? "True" : "False",
material.header.flags.excludeFromScanVisor() ? "True" : "False",
material.header.flags.xrayOpaque() ? "True" : "False",
material.header.flags.xrayAlphaTarget() ? "True" : "False");
/* Blend factors */ out << "pnode = new_nodetree.nodes.new('ShaderNodeGroup')\n"
out << "blend_node = new_nodetree.nodes.new('ShaderNodeGroup')\n" "pnode.name = 'Output'\n"
"blend_node.name = 'Blend'\n" "pnode.node_tree = bpy.data.node_groups['RetroShaderMP3']\n"
"gridder.place_node(blend_node, 2)\n"; "gridder.place_node(pnode, 1)\n";
if (material.header.flags.alphaBlending())
out << "blend_node.node_tree = bpy.data.node_groups['HECLBlendOutput']\n";
else if (material.header.flags.additiveBlending())
out << "blend_node.node_tree = bpy.data.node_groups['HECLAdditiveOutput']\n";
else {
out << "blend_node.node_tree = bpy.data.node_groups['HECLOpaqueOutput']\n"
"new_material.blend_method = 'OPAQUE'\n";
}
/* Texmap list */ if (material.header.flags.additiveIncandecence())
out << "tex_maps = []\n" out << "pnode.inputs['Add INCA'].default_value = 1\n";
"pnode = None\n"
"anode = None\n"
"rflv_tex_node = None\n";
/* Add PASSes */ int texMtxIdx = 0;
i = 0; for (const auto& chunk : material.chunks) {
unsigned texMapIdx = 0; if (const Material::PASS* pass = chunk.get_if<Material::PASS>()) {
unsigned texMtxIdx = 0; LoadTexture(out, pass->txtrId, pakRouter, entry);
unsigned kColorIdx = 0; out << "# Texture\n"
Material::ISection* prevSection = nullptr; "tex_node = new_nodetree.nodes.new('ShaderNodeTexImage')\n"
for (const Material::SectionFactory& factory : material.sections) { "texture_nodes.append(tex_node)\n"
factory.section->constructNode(out, pakRouter, entry, prevSection, i++, texMapIdx, texMtxIdx, kColorIdx); "tex_node.image = image\n";
Material::SectionPASS* pass = Material::SectionPASS::castTo(factory.section.get());
if (!pass ||
(pass && Material::SectionPASS::Subtype(pass->subtype.toUint32()) != Material::SectionPASS::Subtype::RFLV))
prevSection = factory.section.get();
}
/* Connect final PASS */ if (!pass->uvAnim.empty()) {
out << "if pnode:\n" const auto& uva = pass->uvAnim[0];
" new_nodetree.links.new(pnode.outputs['Next Color'], final_node.inputs['Color'])\n" switch (uva.uvSource) {
"else:\n" case Material::UVAnimationUVSource::Position:
" new_nodetree.links.new(kcolor_nodes[-1][0].outputs[0], final_node.inputs['Color'])\n" default:
"if anode:\n" out << "tex_uv_node = new_nodetree.nodes.new('ShaderNodeTexCoord')\n"
" new_nodetree.links.new(anode.outputs['Value'], final_node.inputs['Alpha'])\n" "tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['Window'], tex_node.inputs['Vector']))\n";
"elif pnode:\n" break;
" new_nodetree.links.new(pnode.outputs['Next Alpha'], final_node.inputs['Alpha'])\n" case Material::UVAnimationUVSource::Normal:
"else:\n" out << "tex_uv_node = new_nodetree.nodes.new('ShaderNodeTexCoord')\n"
" new_nodetree.links.new(kcolor_nodes[-1][1].outputs[0], final_node.inputs['Alpha'])\n"; "tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['Normal'], tex_node.inputs['Vector']))\n";
} break;
case Material::UVAnimationUVSource::UV:
out.format(FMT_STRING("tex_uv_node = new_nodetree.nodes.new('ShaderNodeUVMap')\n"
"tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['UV'], tex_node.inputs['Vector']))\n"
"tex_uv_node.uv_map = 'UV_{}'\n"), pass->uvSrc);
break;
}
out.format(FMT_STRING("tex_uv_node.label = 'MTX_{}'\n"), texMtxIdx);
} else {
out.format(FMT_STRING("tex_uv_node = new_nodetree.nodes.new('ShaderNodeUVMap')\n"
"tex_links.append(new_nodetree.links.new(tex_uv_node.outputs['UV'], tex_node.inputs['Vector']))\n"
"tex_uv_node.uv_map = 'UV_{}'\n"), pass->uvSrc);
}
void Material::SectionPASS::constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter, out << "gridder.place_node(tex_uv_node, 0)\n"
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx, "gridder.place_node(tex_node, 0)\n"
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const { "tex_uv_node.location[0] -= 120\n"
/* Add Texture nodes */ "tex_node.location[0] += 120\n"
if (txtrId.isValid()) { "tex_node.location[1] += 176\n"
std::string texName = pakRouter.getBestEntryName(txtrId); "\n";
const nod::Node* node;
const PAK::Entry* texEntry = pakRouter.lookupEntry(txtrId, &node); if (!pass->uvAnim.empty()) {
hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry); const auto& uva = pass->uvAnim[0];
if (txtrPath.isNone()) { DNAMP1::MaterialSet::Material::AddTextureAnim(out, uva.anim.mode, texMtxIdx++, uva.anim.vals);
txtrPath.makeDirChain(false); }
PAKEntryReadStream rs = texEntry->beginReadStream(*node);
TXTR::Extract(rs, txtrPath); auto DoSwap = [&]() {
if (pass->flags.swapColorComponent() == Material::SwapColorComponent::Alpha) {
out << "swap_output = tex_node.outputs['Alpha']\n";
} else {
out << "separate_node = new_nodetree.nodes.new('ShaderNodeSeparateRGB')\n"
"gridder.place_node(separate_node, 0, False)\n"
"separate_node.location[0] += 350\n"
"separate_node.location[1] += 350\n"
"new_nodetree.links.new(tex_node.outputs['Color'], separate_node.inputs[0])\n";
out.format(FMT_STRING("swap_output = separate_node.outputs[{}]\n"), int(pass->flags.swapColorComponent()));
}
};
using Subtype = Material::PASS::Subtype;
switch (Subtype(pass->subtype.toUint32())) {
case Subtype::DIFF:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['DIFFC'])\n"
"new_nodetree.links.new(tex_node.outputs['Alpha'], pnode.inputs['DIFFA'])\n";
break;
case Subtype::BLOL:
DoSwap();
out << "new_nodetree.links.new(swap_output, pnode.inputs['BLOL'])\n";
break;
case Subtype::BLOD:
DoSwap();
out << "new_nodetree.links.new(swap_output, pnode.inputs['BLOD'])\n";
break;
case Subtype::CLR:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['CLR'])\n"
"new_nodetree.links.new(tex_node.outputs['Alpha'], pnode.inputs['CLRA'])\n";
break;
case Subtype::TRAN:
DoSwap();
if (pass->flags.TRANInvert())
out << "invert_node = new_nodetree.nodes.new('ShaderNodeInvert')\n"
"gridder.place_node(invert_node, 0, False)\n"
"invert_node.location[0] += 400\n"
"invert_node.location[1] += 350\n"
"new_nodetree.links.new(swap_output, invert_node.inputs['Color'])\n"
"swap_output = invert_node.outputs['Color']\n";
out << "new_nodetree.links.new(swap_output, pnode.inputs['TRAN'])\n";
break;
case Subtype::INCA:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['INCAC'])\n";
if (pass->flags.alphaContribution()) {
DoSwap();
out << "new_nodetree.links.new(swap_output, pnode.inputs['INCAA'])\n";
}
out.format(FMT_STRING("new_material.retro_inca_color_mod = {}\n"), pass->flags.INCAColorMod() ? "True" : "False");
break;
case Subtype::RFLV:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['RFLV'])\n";
break;
case Subtype::RFLD:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['RFLD'])\n"
"new_nodetree.links.new(tex_node.outputs['Alpha'], pnode.inputs['RFLDA'])\n";
break;
case Subtype::LRLD:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['LRLD'])\n";
break;
case Subtype::LURD:
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['LURDC'])\n"
"new_nodetree.links.new(tex_node.outputs['Alpha'], pnode.inputs['LURDA'])\n";
break;
case Subtype::BLOI:
DoSwap();
out << "new_nodetree.links.new(swap_output, pnode.inputs['BLOI'])\n";
break;
case Subtype::XRAY:
DoSwap();
out << "new_nodetree.links.new(tex_node.outputs['Color'], pnode.inputs['XRAYC'])\n"
"new_nodetree.links.new(swap_output, pnode.inputs['XRAYA'])\n";
break;
default:
Log.report(logvisor::Fatal, FMT_STRING("Unknown PASS subtype"));
break;
}
} else if (const Material::CLR* clr = chunk.get_if<Material::CLR>()) {
using Subtype = Material::CLR::Subtype;
athena::simd_floats vec4;
clr->color.toVec4f().simd.copy_to(vec4);
switch (Subtype(clr->subtype.toUint32())) {
case Subtype::CLR:
out.format(FMT_STRING("pnode.inputs['CLR'].default_value = ({}, {}, {}, 1.0)\n"
"pnode.inputs['CLRA'].default_value = {}\n"),
vec4[0], vec4[1], vec4[2], vec4[3]);
break;
case Subtype::DIFB:
out.format(FMT_STRING("pnode.inputs['DIFBC'].default_value = ({}, {}, {}, 1.0)\n"
"pnode.inputs['DIFBA'].default_value = {}\n"),
vec4[0], vec4[1], vec4[2], vec4[3]);
break;
default:
Log.report(logvisor::Fatal, FMT_STRING("Unknown CLR subtype"));
break;
}
} else if (const Material::INT* val = chunk.get_if<Material::INT>()) {
using Subtype = Material::INT::Subtype;
switch (Subtype(val->subtype.toUint32())) {
case Subtype::OPAC:
out.format(FMT_STRING("pnode.inputs['OPAC'].default_value = {}\n"), val->value / 255.f);
break;
case Subtype::BLOD:
out.format(FMT_STRING("pnode.inputs['BLOD'].default_value = {}\n"), val->value / 255.f);
break;
case Subtype::BLOI:
out.format(FMT_STRING("pnode.inputs['BLOI'].default_value = {}\n"), val->value / 255.f);
break;
case Subtype::BNIF:
out.format(FMT_STRING("pnode.inputs['BNIF'].default_value = {}\n"), val->value / 255.f);
break;
case Subtype::XRBR:
out.format(FMT_STRING("pnode.inputs['XRBR'].default_value = {}\n"), val->value / 255.f);
break;
default:
Log.report(logvisor::Fatal, FMT_STRING("Unknown INT subtype"));
break;
}
} }
hecl::SystemString resPath = pakRouter.getResourceRelativePath(entry, txtrId);
hecl::SystemUTF8Conv resPathView(resPath);
out.format(fmt(
"if '{}' in bpy.data.textures:\n"
" image = bpy.data.images['{}']\n"
" texture = bpy.data.textures[image.name]\n"
"else:\n"
" image = bpy.data.images.load('''//{}''')\n"
" image.name = '{}'\n"
" texture = bpy.data.textures.new(image.name, 'IMAGE')\n"
" texture.image = image\n"
"tex_maps.append(texture)\n"
"\n"),
texName, texName, resPathView, texName);
if (uvAnim.size()) {
const UVAnimation& uva = uvAnim[0];
DNAMP1::MaterialSet::Material::AddTexture(out, GX::TexGenSrc(uva.unk1 + (uva.unk1 < 2 ? 0 : 2)), texMtxIdx,
texMapIdx++, false);
DNAMP1::MaterialSet::Material::AddTextureAnim(out, uva.anim.mode, texMtxIdx++, uva.anim.vals);
} else
DNAMP1::MaterialSet::Material::AddTexture(out, GX::TexGenSrc(uvSrc + 4), -1, texMapIdx++, false);
}
/* Special case for RFLV (environment UV mask) */
if (Subtype(subtype.toUint32()) == Subtype::RFLV) {
if (txtrId.isValid())
out << "rflv_tex_node = texture_nodes[-1]\n";
return;
}
/* Add PASS node */
bool linkRAS = false;
out << "prev_pnode = pnode\n"
"pnode = new_nodetree.nodes.new('ShaderNodeGroup')\n";
switch (Subtype(subtype.toUint32())) {
case Subtype::DIFF: {
out << "pnode.node_tree = bpy.data.node_groups['RetroPassDIFF']\n";
if (txtrId.isValid()) {
out << "new_material.hecl_lightmap = texture.name\n"
<< "texture.image.use_fake_user = True\n";
}
linkRAS = true;
break;
}
case Subtype::RIML:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassRIML']\n";
if (idx == 0)
linkRAS = true;
break;
case Subtype::BLOL:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassBLOL']\n";
if (idx == 0)
linkRAS = true;
break;
case Subtype::BLOD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassBLOD']\n";
if (idx == 0)
linkRAS = true;
break;
case Subtype::CLR:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassCLR']\n";
if (idx == 0)
linkRAS = true;
break;
case Subtype::TRAN:
if (flags.TRANInvert())
out << "pnode.node_tree = bpy.data.node_groups['RetroPassTRANInv']\n";
else
out << "pnode.node_tree = bpy.data.node_groups['RetroPassTRAN']\n";
break;
case Subtype::INCA:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassINCA']\n";
break;
case Subtype::RFLV:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassRFLV']\n";
break;
case Subtype::RFLD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassRFLD']\n"
"if rflv_tex_node:\n"
" new_nodetree.links.new(rflv_tex_node.outputs['Color'], pnode.inputs['Mask Color'])\n"
" new_nodetree.links.new(rflv_tex_node.outputs['Value'], pnode.inputs['Mask Alpha'])\n";
break;
case Subtype::LRLD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassLRLD']\n";
break;
case Subtype::LURD:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassLURD']\n";
break;
case Subtype::BLOI:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassBLOI']\n";
break;
case Subtype::XRAY:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassXRAY']\n";
break;
case Subtype::TOON:
out << "pnode.node_tree = bpy.data.node_groups['RetroPassTOON']\n";
break;
default:
break;
}
out << "gridder.place_node(pnode, 2)\n";
if (txtrId.isValid()) {
out << "new_nodetree.links.new(texture_nodes[-1].outputs['Color'], pnode.inputs['Tex Color'])\n"
"new_nodetree.links.new(texture_nodes[-1].outputs['Value'], pnode.inputs['Tex Alpha'])\n";
}
if (linkRAS)
out << "new_nodetree.links.new(material_node.outputs['Color'], pnode.inputs['Prev Color'])\n"
"new_nodetree.links.new(material_node.outputs['Alpha'], pnode.inputs['Prev Alpha'])\n";
else if (prevSection) {
if (prevSection->m_type == ISection::Type::PASS &&
Subtype(static_cast<const SectionPASS*>(prevSection)->subtype.toUint32()) != Subtype::RFLV)
out << "new_nodetree.links.new(prev_pnode.outputs['Next Color'], pnode.inputs['Prev Color'])\n"
"new_nodetree.links.new(prev_pnode.outputs['Next Alpha'], pnode.inputs['Prev Alpha'])\n";
else if (prevSection->m_type == ISection::Type::CLR)
out << "new_nodetree.links.new(kcolor_nodes[-1][0].outputs[0], pnode.inputs['Prev Color'])\n"
"new_nodetree.links.new(kcolor_nodes[-1][1].outputs[0], pnode.inputs['Prev Alpha'])\n";
}
/* Row Break in gridder */
out << "gridder.row_break(2)\n";
}
void Material::SectionCLR::constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const {
DNAMP1::MaterialSet::Material::AddKcolor(out, color, kColorIdx++);
switch (Subtype(subtype.toUint32())) {
case Subtype::DIFB:
out << "kc_node.label += ' DIFB'\n"
"ka_node.label += ' DIFB'\n";
break;
default:
break;
}
}
void Material::SectionINT::constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const {
switch (Subtype(subtype.toUint32())) {
case Subtype::OPAC: {
GX::Color clr(value);
out.format(fmt(
"anode = new_nodetree.nodes.new('ShaderNodeValue')\n"
"anode.outputs['Value'].default_value = {}\n"),
float(clr[3]) / float(0xff));
out << "gridder.place_node(anode, 1)\n";
} break;
case Subtype::BLOD:
out.format(fmt("new_material.retro_blod = {}\n"), value);
break;
case Subtype::BLOI:
out.format(fmt("new_material.retro_bloi = {}\n"), value);
break;
case Subtype::BNIF:
out.format(fmt("new_material.retro_bnif = {}\n"), value);
break;
case Subtype::XRBR:
out.format(fmt("new_material.retro_xrbr = {}\n"), value);
break;
default:
break;
} }
} }
} // namespace DataSpec::DNAMP3 } // namespace DataSpec::DNAMP3
AT_SPECIALIZE_TYPED_VARIANT_BIGDNA(DataSpec::DNAMP3::MaterialSet::Material::PASS,
DataSpec::DNAMP3::MaterialSet::Material::CLR,
DataSpec::DNAMP3::MaterialSet::Material::INT,
DataSpec::DNAMP3::MaterialSet::Material::END)

View File

@ -18,6 +18,10 @@ struct MaterialSet : BigDNA {
void addMaterialEndOff(atUint32) { ++materialCount; } void addMaterialEndOff(atUint32) { ++materialCount; }
struct Material : BigDNA { struct Material : BigDNA {
enum class SwapColorComponent { Red, Green, Blue, Alpha };
enum class UVAnimationUVSource : atUint16 { Position, Normal, UV };
enum class UVAnimationMatrixConfig : atUint16 { NoMtxNoPost, MtxNoPost, NoMtxPost, MtxPost };
AT_DECL_EXPLICIT_DNA AT_DECL_EXPLICIT_DNA
using VAFlags = DNAMP1::MaterialSet::Material::VAFlags; using VAFlags = DNAMP1::MaterialSet::Material::VAFlags;
struct Header : BigDNA { struct Header : BigDNA {
@ -26,8 +30,18 @@ struct MaterialSet : BigDNA {
struct Flags : BigDNA { struct Flags : BigDNA {
AT_DECL_DNA AT_DECL_DNA
Value<atUint32> flags; Value<atUint32> flags;
bool alphaBlending() const { return (flags & 0x8) != 0; } bool enableBloom() const { return (flags & 0x1) != 0; }
void setAlphaBlending(bool enabled) { void setEnableBloom(bool enabled) {
flags &= ~0x1;
flags |= atUint32(enabled) << 0;
}
bool forceLightingStage() const { return (flags & 0x4) != 0; }
void setForceLightingStage(bool enabled) {
flags &= ~0x4;
flags |= atUint32(enabled) << 2;
}
bool preIncaTransparency() const { return (flags & 0x8) != 0; }
void setPreIncaTransparency(bool enabled) {
flags &= ~0x8; flags &= ~0x8;
flags |= atUint32(enabled) << 3; flags |= atUint32(enabled) << 3;
} }
@ -36,8 +50,8 @@ struct MaterialSet : BigDNA {
flags &= ~0x10; flags &= ~0x10;
flags |= atUint32(enabled) << 4; flags |= atUint32(enabled) << 4;
} }
bool additiveBlending() const { return (flags & 0x20) != 0; } bool additiveIncandecence() const { return (flags & 0x20) != 0; }
void setAdditiveBlending(bool enabled) { void setAdditiveIncandecence(bool enabled) {
flags &= ~0x20; flags &= ~0x20;
flags |= atUint32(enabled) << 5; flags |= atUint32(enabled) << 5;
} }
@ -46,6 +60,36 @@ struct MaterialSet : BigDNA {
flags &= ~0x100; flags &= ~0x100;
flags |= atUint32(enabled) << 8; flags |= atUint32(enabled) << 8;
} }
bool justWhite() const { return (flags & 0x200) != 0; }
void setJustWhite(bool enabled) {
flags &= ~0x200;
flags |= atUint32(enabled) << 9;
}
bool reflectionAlphaTarget() const { return (flags & 0x400) != 0; }
void setReflectionAlphaTarget(bool enabled) {
flags &= ~0x400;
flags |= atUint32(enabled) << 10;
}
bool justSolidColor() const { return (flags & 0x800) != 0; }
void setJustSolidColor(bool enabled) {
flags &= ~0x800;
flags |= atUint32(enabled) << 11;
}
bool excludeFromScanVisor() const { return (flags & 0x4000) != 0; }
void setExcludeFromScanVisor(bool enabled) {
flags &= ~0x4000;
flags |= atUint32(enabled) << 14;
}
bool xrayOpaque() const { return (flags & 0x8000) != 0; }
void setXRayOpaque(bool enabled) {
flags &= ~0x8000;
flags |= atUint32(enabled) << 15;
}
bool xrayAlphaTarget() const { return (flags & 0x10000) != 0; }
void setXRayAlphaTarget(bool enabled) {
flags &= ~0x10000;
flags |= atUint32(enabled) << 16;
}
bool lightmapUVArray() const { return false; } /* For polymorphic compatibility with MP1/2 */ bool lightmapUVArray() const { return false; } /* For polymorphic compatibility with MP1/2 */
} flags; } flags;
Value<atUint32> uniqueIdx; Value<atUint32> uniqueIdx;
@ -58,20 +102,12 @@ struct MaterialSet : BigDNA {
const Header::Flags& getFlags() const { return header.flags; } const Header::Flags& getFlags() const { return header.flags; }
const VAFlags& getVAFlags() const { return header.vaFlags; } const VAFlags& getVAFlags() const { return header.vaFlags; }
struct ISection : BigDNAV { enum class ChunkType : atUint32 {
Delete expl; PASS = 'PASS', CLR = 'CLR ', INT = 'INT ', END = 'END '
enum class Type : atUint32 { PASS = SBIG('PASS'), CLR = SBIG('CLR '), INT = SBIG('INT ') } m_type;
ISection(Type type) : m_type(type) {}
virtual void constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const = 0;
}; };
struct SectionPASS : ISection {
SectionPASS() : ISection(ISection::Type::PASS) {} struct PASS : hecl::TypedRecordBigDNA<ChunkType::PASS> {
static SectionPASS* castTo(ISection* sec) { AT_DECL_DNA
return sec->m_type == Type::PASS ? static_cast<SectionPASS*>(sec) : nullptr;
}
AT_DECL_DNAV
Value<atUint32> size; Value<atUint32> size;
enum class Subtype : atUint32 { enum class Subtype : atUint32 {
DIFF = SBIG('DIFF'), DIFF = SBIG('DIFF'),
@ -93,6 +129,21 @@ struct MaterialSet : BigDNA {
struct Flags : BigDNA { struct Flags : BigDNA {
AT_DECL_DNA AT_DECL_DNA
Value<atUint32> flags; Value<atUint32> flags;
SwapColorComponent swapColorComponent() const { return SwapColorComponent(flags & 0x3); }
void setSwapColorComponent(SwapColorComponent comp) {
flags &= ~0x3;
flags |= atUint32(comp) << 0;
}
bool alphaContribution() const { return (flags & 0x4) != 0; }
void setAlphaContribution(bool enabled) {
flags &= ~0x4;
flags |= atUint32(enabled) << 2;
}
bool INCAColorMod() const { return (flags & 0x8) != 0; }
void setINCAColorMod(bool enabled) {
flags &= ~0x8;
flags |= atUint32(enabled) << 3;
}
bool TRANInvert() const { return (flags & 0x10) != 0; } bool TRANInvert() const { return (flags & 0x10) != 0; }
void setTRANInvert(bool enabled) { void setTRANInvert(bool enabled) {
flags &= ~0x10; flags &= ~0x10;
@ -104,36 +155,21 @@ struct MaterialSet : BigDNA {
Value<atUint32> uvAnimSize; Value<atUint32> uvAnimSize;
struct UVAnimation : BigDNA { struct UVAnimation : BigDNA {
AT_DECL_DNA AT_DECL_DNA
Value<atUint16> unk1; Value<UVAnimationUVSource> uvSource;
Value<atUint16> unk2; Value<UVAnimationMatrixConfig> mtxConfig;
DNAMP1::MaterialSet::Material::UVAnimation anim; DNAMP1::MaterialSet::Material::UVAnimation anim;
}; };
Vector<UVAnimation, AT_DNA_COUNT(uvAnimSize != 0)> uvAnim; Vector<UVAnimation, AT_DNA_COUNT(uvAnimSize != 0)> uvAnim;
void constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const override;
}; };
struct SectionCLR : ISection { struct CLR : hecl::TypedRecordBigDNA<ChunkType::CLR> {
SectionCLR() : ISection(ISection::Type::CLR) {} AT_DECL_DNA
static SectionCLR* castTo(ISection* sec) {
return sec->m_type == Type::CLR ? static_cast<SectionCLR*>(sec) : nullptr;
}
AT_DECL_DNAV
enum class Subtype : atUint32 { CLR = SBIG('CLR '), DIFB = SBIG('DIFB') }; enum class Subtype : atUint32 { CLR = SBIG('CLR '), DIFB = SBIG('DIFB') };
DNAFourCC subtype; DNAFourCC subtype;
GX::Color color; GX::Color color;
CLR() = default;
void constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const override;
}; };
struct SectionINT : ISection { struct INT : hecl::TypedRecordBigDNA<ChunkType::INT> {
SectionINT() : ISection(ISection::Type::INT) {} AT_DECL_DNA
static SectionINT* castTo(ISection* sec) {
return sec->m_type == Type::INT ? static_cast<SectionINT*>(sec) : nullptr;
}
AT_DECL_DNAV
enum class Subtype : atUint32 { enum class Subtype : atUint32 {
OPAC = SBIG('OPAC'), OPAC = SBIG('OPAC'),
BLOD = SBIG('BLOD'), BLOD = SBIG('BLOD'),
@ -143,16 +179,12 @@ struct MaterialSet : BigDNA {
}; };
DNAFourCC subtype; DNAFourCC subtype;
Value<atUint32> value; Value<atUint32> value;
void constructNode(hecl::blender::PyOutStream& out, const PAKRouter<PAKBridge>& pakRouter,
const PAK::Entry& entry, const Material::ISection* prevSection, unsigned idx,
unsigned& texMapIdx, unsigned& texMtxIdx, unsigned& kColorIdx) const override;
}; };
struct SectionFactory : BigDNA { struct END : hecl::TypedRecordBigDNA<ChunkType::END> {
AT_DECL_EXPLICIT_DNA AT_DECL_DNA
std::unique_ptr<ISection> section;
}; };
std::vector<SectionFactory> sections; using Chunk = hecl::TypedVariantBigDNA<PASS, CLR, INT, END>;
std::vector<Chunk> chunks;
}; };
Vector<Material, AT_DNA_COUNT(materialCount)> materials; Vector<Material, AT_DNA_COUNT(materialCount)> materials;

View File

@ -14,11 +14,12 @@ set(DNAMP3_SOURCES
DNAMP3.hpp DNAMP3.cpp DNAMP3.hpp DNAMP3.cpp
PAK.cpp PAK.cpp
ANIM.cpp ANIM.cpp
CINF.cpp CINF.hpp
CHAR.cpp CHAR.cpp
CMDL.hpp CMDL.cpp CMDL.hpp CMDL.cpp
CMDLMaterials.cpp CMDLMaterials.cpp
CSKR.cpp CSKR.cpp
PATH.hpp
STRG.hpp STRG.cpp STRG.hpp STRG.cpp
MAPA.hpp MAPA.hpp
MREA.cpp) MREA.cpp)

View File

@ -8,7 +8,7 @@ void CSKR::weightVertex(hecl::blender::PyOutStream& os, const CINF& cinf, atInt1
return; return;
const DNAMP2::CSKR::SkinningRule& rule = data.skinningRules[skinIdx]; const DNAMP2::CSKR::SkinningRule& rule = data.skinningRules[skinIdx];
for (const DNAMP2::CSKR::SkinningRule::Weight& weight : rule.weights) for (const DNAMP2::CSKR::SkinningRule::Weight& weight : rule.weights)
os.format(fmt("vert[dvert_lay][{}] = {}\n"), cinf.getBoneIdxFromId(weight.boneId), weight.weight); os.format(FMT_STRING("vert[dvert_lay][{}] = {}\n"), cinf.getBoneIdxFromId(weight.boneId), weight.weight);
} }
} // namespace DataSpec::DNAMP3 } // namespace DataSpec::DNAMP3

View File

@ -9,6 +9,7 @@
#include "CHAR.hpp" #include "CHAR.hpp"
#include "MREA.hpp" #include "MREA.hpp"
#include "MAPA.hpp" #include "MAPA.hpp"
#include "PATH.hpp"
#include "SAVW.hpp" #include "SAVW.hpp"
#include "HINT.hpp" #include "HINT.hpp"
#include "DataSpec/DNACommon/TXTR.hpp" #include "DataSpec/DNACommon/TXTR.hpp"
@ -23,9 +24,8 @@ logvisor::Module Log("urde::DNAMP3");
static bool GetNoShare(std::string_view name) { static bool GetNoShare(std::string_view name) {
std::string lowerName(name); std::string lowerName(name);
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower); std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), tolower);
if (!lowerName.compare(0, 7, "metroid")) return !lowerName.starts_with("metroid"sv) && !lowerName.starts_with("frontend"sv) &&
return false; !lowerName.starts_with("rs5fe"sv) && !lowerName.starts_with("universearea"sv);
return true;
} }
PAKBridge::PAKBridge(const nod::Node& node, bool doExtract) PAKBridge::PAKBridge(const nod::Node& node, bool doExtract)
@ -119,7 +119,7 @@ void PAKBridge::build() {
areaDeps.name = hecl::SystemString(_SYS_STR("MREA_")) + hecl::SystemStringConv(idStr).c_str(); areaDeps.name = hecl::SystemString(_SYS_STR("MREA_")) + hecl::SystemStringConv(idStr).c_str();
} }
} }
hecl::SystemString num = fmt::format(fmt(_SYS_STR("{:02d} ")), ai); hecl::SystemString num = fmt::format(FMT_STRING(_SYS_STR("{:02d} ")), ai);
areaDeps.name = num + areaDeps.name; areaDeps.name = num + areaDeps.name;
const MLVL::LayerFlags& layerFlags = *layerFlagsIt++; const MLVL::LayerFlags& layerFlags = *layerFlagsIt++;
@ -131,7 +131,7 @@ void PAKBridge::build() {
layer.name = LayerName(mlvl.layerNames[layerIdx++]); layer.name = LayerName(mlvl.layerNames[layerIdx++]);
layer.active = layerFlags.flags >> (l - 1) & 0x1; layer.active = layerFlags.flags >> (l - 1) & 0x1;
layer.name = hecl::StringUtils::TrimWhitespace(layer.name); layer.name = hecl::StringUtils::TrimWhitespace(layer.name);
num = fmt::format(fmt(_SYS_STR("{:02d} ")), l - 1); num = fmt::format(FMT_STRING(_SYS_STR("{:02d} ")), l - 1);
layer.name = num + layer.name; layer.name = num + layer.name;
} }
} }
@ -165,11 +165,11 @@ void PAKBridge::addCMDLRigPairs(PAKRouter<PAKBridge>& pakRouter, CharacterAssoci
const CHAR::CharacterInfo& ci = aChar.characterInfo; const CHAR::CharacterInfo& ci = aChar.characterInfo;
charAssoc.m_cmdlRigs[ci.cmdl] = {ci.cskr, ci.cinf}; charAssoc.m_cmdlRigs[ci.cmdl] = {ci.cskr, ci.cinf};
charAssoc.m_cskrToCharacter[ci.cskr] = charAssoc.m_cskrToCharacter[ci.cskr] =
std::make_pair(entry.second.id, fmt::format(fmt("{}_{}.CSKR"), ci.name, ci.cskr)); std::make_pair(entry.second.id, fmt::format(FMT_STRING("{}_{}.CSKR"), ci.name, ci.cskr));
for (const CHAR::CharacterInfo::Overlay& overlay : ci.overlays) { for (const CHAR::CharacterInfo::Overlay& overlay : ci.overlays) {
charAssoc.m_cmdlRigs[overlay.cmdl] = {overlay.cskr, ci.cinf}; charAssoc.m_cmdlRigs[overlay.cmdl] = {overlay.cskr, ci.cinf};
charAssoc.m_cskrToCharacter[overlay.cskr] = charAssoc.m_cskrToCharacter[overlay.cskr] =
std::make_pair(entry.second.id, fmt::format(fmt("{}.{}_{}.CSKR"), ci.name, overlay.type, overlay.cskr)); std::make_pair(entry.second.id, fmt::format(FMT_STRING("{}.{}_{}.CSKR"), ci.name, overlay.type, overlay.cskr));
} }
} }
} }
@ -191,13 +191,23 @@ void PAKBridge::addMAPATransforms(PAKRouter<PAKBridge>& pakRouter,
if (mlvl.worldNameId.isValid()) if (mlvl.worldNameId.isValid())
pathOverrides[mlvl.worldNameId] = hecl::ProjectPath(mlvlDirPath, pathOverrides[mlvl.worldNameId] = hecl::ProjectPath(mlvlDirPath,
fmt::format(fmt(_SYS_STR("!name_{}.yaml")), mlvl.worldNameId)); fmt::format(FMT_STRING(_SYS_STR("!name_{}.yaml")), mlvl.worldNameId));
for (const MLVL::Area& area : mlvl.areas) { for (const MLVL::Area& area : mlvl.areas) {
{
/* Get PATH transform */
const nod::Node* areaNode;
const PAK::Entry* areaEntry = pakRouter.lookupEntry(area.areaMREAId, &areaNode);
PAKEntryReadStream rs = areaEntry->beginReadStream(*areaNode);
UniqueID64 pathId = MREA::GetPATHId(rs);
if (pathId.isValid())
addTo[pathId] = zeus::CMatrix4f(area.transformMtx[0], area.transformMtx[1], area.transformMtx[2], BottomRow)
.transposed();
}
hecl::ProjectPath areaDirPath = pakRouter.getWorking(area.areaMREAId).getParentPath(); hecl::ProjectPath areaDirPath = pakRouter.getWorking(area.areaMREAId).getParentPath();
if (area.areaNameId.isValid()) if (area.areaNameId.isValid())
pathOverrides[area.areaNameId] = hecl::ProjectPath(areaDirPath, pathOverrides[area.areaNameId] = hecl::ProjectPath(areaDirPath,
fmt::format(fmt(_SYS_STR("!name_{}.yaml")), area.areaNameId)); fmt::format(FMT_STRING(_SYS_STR("!name_{}.yaml")), area.areaNameId));
} }
if (mlvl.worldMap.isValid()) { if (mlvl.worldMap.isValid()) {
@ -236,16 +246,20 @@ ResExtractor<PAKBridge> PAKBridge::LookupExtractor(const nod::Node& pakNode, con
return {SAVWCommon::ExtractSAVW<SAVW>, {_SYS_STR(".yaml")}}; return {SAVWCommon::ExtractSAVW<SAVW>, {_SYS_STR(".yaml")}};
case SBIG('HINT'): case SBIG('HINT'):
return {HINT::Extract, {_SYS_STR(".yaml")}}; return {HINT::Extract, {_SYS_STR(".yaml")}};
// case SBIG('CMDL'): case SBIG('CMDL'):
// return {CMDL::Extract, {_SYS_STR(".blend")}, 1}; return {CMDL::Extract, {_SYS_STR(".blend")}, 1};
// case SBIG('CHAR'): case SBIG('CINF'):
// return {CHAR::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 2}; return {CINF::Extract<PAKBridge>, {_SYS_STR(".blend")}, 1};
// case SBIG('MLVL'): case SBIG('CHAR'):
// return {MLVL::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 3}; return {CHAR::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 2};
// case SBIG('MREA'): case SBIG('MLVL'):
// return {MREA::Extract, {_SYS_STR(".blend")}, 4}; return {MLVL::Extract, {_SYS_STR(".yaml"), _SYS_STR(".blend")}, 3};
// case SBIG('MAPA'): case SBIG('MREA'):
// return {MAPA::Extract, {_SYS_STR(".blend")}, 4}; return {MREA::Extract, {_SYS_STR(".blend")}, 4};
case SBIG('MAPA'):
return {MAPA::Extract, {_SYS_STR(".blend")}, 4};
case SBIG('PATH'):
return {PATH::Extract, {_SYS_STR(".blend")}, 5};
case SBIG('FSM2'): case SBIG('FSM2'):
return {DNAFSM2::ExtractFSM2<UniqueID64>, {_SYS_STR(".yaml")}}; return {DNAFSM2::ExtractFSM2<UniqueID64>, {_SYS_STR(".yaml")}};
case SBIG('FONT'): case SBIG('FONT'):

View File

@ -14,16 +14,14 @@ MREA::StreamReader::StreamReader(athena::io::IStreamReader& source, atUint32 blk
m_blkCount = blkCount; m_blkCount = blkCount;
m_blockInfos.reserve(blkCount); m_blockInfos.reserve(blkCount);
for (atUint32 i = 0; i < blkCount; ++i) { for (atUint32 i = 0; i < blkCount; ++i) {
m_blockInfos.emplace_back(); BlockInfo& info = m_blockInfos.emplace_back();
BlockInfo& info = m_blockInfos.back();
info.read(source); info.read(source);
m_totalDecompLen += info.decompSize; m_totalDecompLen += info.decompSize;
} }
source.seekAlign32(); source.seekAlign32();
m_secIdxs.reserve(secIdxCount); m_secIdxs.reserve(secIdxCount);
for (atUint32 i = 0; i < secIdxCount; ++i) { for (atUint32 i = 0; i < secIdxCount; ++i) {
m_secIdxs.emplace_back(); std::pair<DNAFourCC, atUint32>& idx = m_secIdxs.emplace_back();
std::pair<DNAFourCC, atUint32>& idx = m_secIdxs.back();
idx.first.read(source); idx.first.read(source);
idx.second = source.readUint32Big(); idx.second = source.readUint32Big();
} }
@ -39,10 +37,19 @@ void MREA::StreamReader::writeSecIdxs(athena::io::IStreamWriter& writer) const {
} }
} }
bool MREA::StreamReader::seekToSection(FourCC sec, const std::vector<atUint32>& secSizes) {
auto search = std::find_if(m_secIdxs.begin(), m_secIdxs.end(), [sec](const auto& s) { return s.first == sec; });
if (search != m_secIdxs.end()) {
DNAMP2::MREA::StreamReader::seekToSection(search->second, secSizes);
return true;
}
return false;
}
void MREA::ReadBabeDeadToBlender_3(hecl::blender::PyOutStream& os, athena::io::IStreamReader& rs) { void MREA::ReadBabeDeadToBlender_3(hecl::blender::PyOutStream& os, athena::io::IStreamReader& rs) {
atUint32 bdMagic = rs.readUint32Big(); atUint32 bdMagic = rs.readUint32Big();
if (bdMagic != 0xBABEDEAD) if (bdMagic != 0xBABEDEAD)
Log.report(logvisor::Fatal, fmt("invalid BABEDEAD magic")); Log.report(logvisor::Fatal, FMT_STRING("invalid BABEDEAD magic"));
os << "bpy.context.scene.world.use_nodes = True\n" os << "bpy.context.scene.world.use_nodes = True\n"
"bg_node = bpy.context.scene.world.node_tree.nodes['Background']\n" "bg_node = bpy.context.scene.world.node_tree.nodes['Background']\n"
"bg_node.inputs[1].default_value = 0.0\n"; "bg_node.inputs[1].default_value = 0.0\n";
@ -93,7 +100,7 @@ bool MREA::Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl:
/* Open Py Stream and read sections */ /* Open Py Stream and read sections */
hecl::blender::PyOutStream os = conn.beginPythonOut(true); hecl::blender::PyOutStream os = conn.beginPythonOut(true);
os.format(fmt( os.format(FMT_STRING(
"import bpy\n" "import bpy\n"
"import bmesh\n" "import bmesh\n"
"from mathutils import Vector\n" "from mathutils import Vector\n"
@ -254,5 +261,20 @@ bool MREA::ExtractLayerDeps(PAKEntryReadStream& rs, PAKBridge::Level::Area& area
return false; return false;
} }
UniqueID64 MREA::GetPATHId(PAKEntryReadStream& rs) {
/* Do extract */
Header head;
head.read(rs);
rs.seekAlign32();
/* MREA decompression stream */
StreamReader drs(rs, head.compressedBlockCount, head.secIndexCount);
/* Skip to PATH */
if (drs.seekToSection(FOURCC('PFL2'), head.secSizes))
return {drs};
return {};
}
} // namespace DNAMP3 } // namespace DNAMP3
} // namespace DataSpec } // namespace DataSpec

View File

@ -13,6 +13,7 @@ struct MREA {
StreamReader(athena::io::IStreamReader& source, atUint32 blkCount, atUint32 secIdxCount); StreamReader(athena::io::IStreamReader& source, atUint32 blkCount, atUint32 secIdxCount);
std::vector<std::pair<DNAFourCC, atUint32>>::const_iterator beginSecIdxs() { return m_secIdxs.begin(); } std::vector<std::pair<DNAFourCC, atUint32>>::const_iterator beginSecIdxs() { return m_secIdxs.begin(); }
void writeSecIdxs(athena::io::IStreamWriter& writer) const; void writeSecIdxs(athena::io::IStreamWriter& writer) const;
bool seekToSection(FourCC sec, const std::vector<atUint32>& secSizes);
}; };
struct Header : BigDNA { struct Header : BigDNA {
@ -84,6 +85,8 @@ struct MREA {
static void ReadBabeDeadToBlender_3(hecl::blender::PyOutStream& os, athena::io::IStreamReader& rs); static void ReadBabeDeadToBlender_3(hecl::blender::PyOutStream& os, athena::io::IStreamReader& rs);
static UniqueID64 GetPATHId(PAKEntryReadStream& rs);
static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath, static bool Extract(const SpecBase& dataSpec, PAKEntryReadStream& rs, const hecl::ProjectPath& outPath,
PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool, hecl::blender::Token& btok, PAKRouter<PAKBridge>& pakRouter, const PAK::Entry& entry, bool, hecl::blender::Token& btok,
std::function<void(const hecl::SystemChar*)>); std::function<void(const hecl::SystemChar*)>);

View File

@ -9,7 +9,7 @@ template <>
void PAK::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) { void PAK::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
m_header.read(reader); m_header.read(reader);
if (m_header.version != 2) if (m_header.version != 2)
Log.report(logvisor::Fatal, fmt("unexpected PAK magic")); Log.report(logvisor::Fatal, FMT_STRING("unexpected PAK magic"));
reader.seek(8, athena::SeekOrigin::Current); reader.seek(8, athena::SeekOrigin::Current);
atUint32 strgSz = reader.readUint32Big(); atUint32 strgSz = reader.readUint32Big();
@ -141,7 +141,7 @@ std::unique_ptr<atUint8[]> PAK::Entry::getBuffer(const nod::Node& pak, atUint64&
} head; } head;
strm->read(&head, 8); strm->read(&head, 8);
if (head.magic != CMPD) { if (head.magic != CMPD) {
Log.report(logvisor::Error, fmt("invalid CMPD block")); Log.report(logvisor::Error, FMT_STRING("invalid CMPD block"));
return nullptr; return nullptr;
} }
head.blockCount = hecl::SBig(head.blockCount); head.blockCount = hecl::SBig(head.blockCount);
@ -206,6 +206,7 @@ const PAK::Entry* PAK::lookupEntry(const UniqueID64& id) const {
} }
const PAK::Entry* PAK::lookupEntry(std::string_view name) const { const PAK::Entry* PAK::lookupEntry(std::string_view name) const {
// TODO: Heterogeneous lookup when C++20 available
auto result = m_nameMap.find(name.data()); auto result = m_nameMap.find(name.data());
if (result != m_nameMap.end()) { if (result != m_nameMap.end()) {
auto result1 = m_entries.find(result->second); auto result1 = m_entries.find(result->second);
@ -220,11 +221,11 @@ std::string PAK::bestEntryName(const nod::Node& pakNode, const Entry& entry, std
for (const NameEntry& nentry : m_nameEntries) for (const NameEntry& nentry : m_nameEntries)
if (nentry.id == entry.id) { if (nentry.id == entry.id) {
catalogueName = nentry.name; catalogueName = nentry.name;
return fmt::format(fmt("{}_{}"), nentry.name, entry.id); return fmt::format(FMT_STRING("{}_{}"), nentry.name, entry.id);
} }
/* Otherwise return ID format string */ /* Otherwise return ID format string */
return fmt::format(fmt("{}_{}"), entry.type, entry.id); return fmt::format(FMT_STRING("{}_{}"), entry.type, entry.id);
} }
} // namespace DataSpec::DNAMP3 } // namespace DataSpec::DNAMP3

6
DataSpec/DNAMP3/PATH.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include "DataSpec/DNACommon/PATH.hpp"
namespace DataSpec::DNAMP3 {
using PATH = DNAPATH::PATH<PAKBridge>;
} // namespace DataSpec::DNAMP3

View File

@ -59,13 +59,13 @@ template <>
void STRG::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) { void STRG::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
atUint32 magic = reader.readUint32Big(); atUint32 magic = reader.readUint32Big();
if (magic != 0x87654321) { if (magic != 0x87654321) {
Log.report(logvisor::Error, fmt("invalid STRG magic")); Log.report(logvisor::Error, FMT_STRING("invalid STRG magic"));
return; return;
} }
atUint32 version = reader.readUint32Big(); atUint32 version = reader.readUint32Big();
if (version != 3) { if (version != 3) {
Log.report(logvisor::Error, fmt("invalid STRG version")); Log.report(logvisor::Error, FMT_STRING("invalid STRG version"));
return; return;
} }
@ -82,22 +82,22 @@ void STRG::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
if (lang.first == "names") if (lang.first == "names")
continue; continue;
if (lang.first.size() != 4) { if (lang.first.size() != 4) {
Log.report(logvisor::Warning, fmt("STRG language string '{}' must be exactly 4 characters; skipping"), lang.first); Log.report(logvisor::Warning, FMT_STRING("STRG language string '{}' must be exactly 4 characters; skipping"), lang.first);
return; return;
} }
if (lang.second->m_type != YAML_SEQUENCE_NODE) { if (lang.second->m_type != YAML_SEQUENCE_NODE) {
Log.report(logvisor::Warning, fmt("STRG language string '{}' must contain a sequence; skipping"), lang.first); Log.report(logvisor::Warning, FMT_STRING("STRG language string '{}' must contain a sequence; skipping"), lang.first);
return; return;
} }
for (const auto& str : lang.second->m_seqChildren) { for (const auto& str : lang.second->m_seqChildren) {
if (str->m_type != YAML_SCALAR_NODE) { if (str->m_type != YAML_SCALAR_NODE) {
Log.report(logvisor::Warning, fmt("STRG language '{}' must contain all scalars; skipping"), lang.first); Log.report(logvisor::Warning, FMT_STRING("STRG language '{}' must contain all scalars; skipping"), lang.first);
return; return;
} }
} }
} }
} else { } else {
Log.report(logvisor::Warning, fmt("STRG must have a mapping root node; skipping")); Log.report(logvisor::Warning, FMT_STRING("STRG must have a mapping root node; skipping"));
return; return;
} }

View File

@ -14,6 +14,7 @@ struct STRG : ISTRG {
std::map<std::string, int32_t> names; std::map<std::string, int32_t> names;
int32_t lookupIdx(std::string_view name) const override { int32_t lookupIdx(std::string_view name) const override {
// TODO: Heterogeneous lookup when C++20 available
auto search = names.find(name.data()); auto search = names.find(name.data());
if (search == names.end()) if (search == names.end())
return -1; return -1;

View File

@ -3,11 +3,12 @@
#include <cstdlib> #include <cstdlib>
#endif #endif
#include "SpecBase.hpp" #include "DataSpec/SpecBase.hpp"
#include "Blender/BlenderSupport.hpp" #include "DataSpec/Blender/BlenderSupport.hpp"
#include "DNACommon/DNACommon.hpp" #include "DataSpec/DNACommon/DNACommon.hpp"
#include "DNACommon/TXTR.hpp" #include "DataSpec/DNACommon/TXTR.hpp"
#include "AssetNameMap.hpp" #include "DataSpec/AssetNameMap.hpp"
#include "DataSpec/DNACommon/URDEVersionInfo.hpp"
#include "hecl/ClientProcess.hpp" #include "hecl/ClientProcess.hpp"
#include "nod/DiscBase.hpp" #include "nod/DiscBase.hpp"
#include "nod/nod.hpp" #include "nod/nod.hpp"
@ -38,11 +39,19 @@ static const hecl::SystemChar* MomErr[] = {_SYS_STR("Your metroid is in another
constexpr uint32_t MomErrCount = std::extent<decltype(MomErr)>::value; constexpr uint32_t MomErrCount = std::extent<decltype(MomErr)>::value;
static ERegion g_CurRegion = ERegion::Invalid;
static bool g_CurSpecIsWii = false;
ERegion getCurrentRegion() { return g_CurRegion; }
bool isCurrentSpecWii() { return g_CurSpecIsWii; }
SpecBase::SpecBase(const hecl::Database::DataSpecEntry* specEntry, hecl::Database::Project& project, bool pc) SpecBase::SpecBase(const hecl::Database::DataSpecEntry* specEntry, hecl::Database::Project& project, bool pc)
: hecl::Database::IDataSpec(specEntry) : hecl::Database::IDataSpec(specEntry)
, m_project(project) , m_project(project)
, m_pc(pc) , m_pc(pc)
, m_masterShader(project.getProjectWorkingPath(), ".hecl/RetroMasterShader.blend") { , m_masterShader(project.getProjectWorkingPath(), ".hecl/RetroMasterShader.blend")
, m_region(ERegion::Invalid)
, m_game(EGame::Invalid) {
AssetNameMap::InitAssetNameMap(); AssetNameMap::InitAssetNameMap();
SpecBase::setThreadProject(); SpecBase::setThreadProject();
} }
@ -65,7 +74,7 @@ bool SpecBase::canExtract(const ExtractPassInfo& info, std::vector<ExtractReport
if (!memcmp(gameID, "R3O", 3)) { if (!memcmp(gameID, "R3O", 3)) {
std::srand(std::time(0)); std::srand(std::time(0));
int r = std::rand() % MomErrCount; int r = std::rand() % MomErrCount;
Log.report(logvisor::Fatal, fmt(_SYS_STR("{}")), MomErr[r]); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("{}")), MomErr[r]);
} }
m_standalone = true; m_standalone = true;
@ -75,20 +84,25 @@ bool SpecBase::canExtract(const ExtractPassInfo& info, std::vector<ExtractReport
if (m_standalone && !checkStandaloneID(gameID)) if (m_standalone && !checkStandaloneID(gameID))
return false; return false;
char region = m_disc->getHeader().m_gameID[3]; m_region = ERegion(m_disc->getHeader().m_gameID[3]);
const hecl::SystemString* regstr = &regNONE; const hecl::SystemString* regstr = &regNONE;
switch (region) { switch (m_region) {
case 'E': case ERegion::NTSC_U:
regstr = &regE; regstr = &regE;
break; break;
case 'J': case ERegion::NTSC_J:
regstr = &regJ; regstr = &regJ;
break; break;
case 'P': case ERegion::PAL:
regstr = &regP; regstr = &regP;
break; break;
default:
break;
} }
setCurRegion(m_region);
setCurSpecIsWii(m_isWii);
if (m_standalone) if (m_standalone)
return checkFromStandaloneDisc(*m_disc, *regstr, info.extractArgs, reps); return checkFromStandaloneDisc(*m_disc, *regstr, info.extractArgs, reps);
else else
@ -99,7 +113,7 @@ void SpecBase::doExtract(const ExtractPassInfo& info, const hecl::MultiProgressP
setThreadProject(); setThreadProject();
DataSpec::g_curSpec.reset(this); DataSpec::g_curSpec.reset(this);
if (!Blender::BuildMasterShader(m_masterShader)) if (!Blender::BuildMasterShader(m_masterShader))
Log.report(logvisor::Fatal, fmt("Unable to build master shader blend")); Log.report(logvisor::Fatal, FMT_STRING("Unable to build master shader blend"));
if (m_isWii) { if (m_isWii) {
/* Extract root files for repacking later */ /* Extract root files for repacking later */
hecl::ProjectPath outDir(m_project.getProjectWorkingPath(), _SYS_STR("out")); hecl::ProjectPath outDir(m_project.getProjectWorkingPath(), _SYS_STR("out"));
@ -126,8 +140,7 @@ bool IsPathAudioGroup(const hecl::ProjectPath& path) {
} }
static bool IsPathSong(const hecl::ProjectPath& path) { static bool IsPathSong(const hecl::ProjectPath& path) {
if (path.getPathType() != hecl::ProjectPath::Type::Glob || if (path.getPathType() != hecl::ProjectPath::Type::Glob || !path.getWithExtension(_SYS_STR(".mid"), true).isFile() ||
!path.getWithExtension(_SYS_STR(".mid"), true).isFile() ||
!path.getWithExtension(_SYS_STR(".yaml"), true).isFile()) { !path.getWithExtension(_SYS_STR(".yaml"), true).isFile()) {
return path.isFile() && path.getLastComponentExt() == _SYS_STR("mid") && return path.isFile() && path.getLastComponentExt() == _SYS_STR("mid") &&
path.getWithExtension(_SYS_STR(".yaml"), true).isFile(); path.getWithExtension(_SYS_STR(".yaml"), true).isFile();
@ -182,7 +195,7 @@ const hecl::Database::DataSpecEntry* SpecBase::overrideDataSpec(const hecl::Proj
hecl::blender::BlendType type = hecl::blender::GetBlendType(asBlend.getAbsolutePath()); hecl::blender::BlendType type = hecl::blender::GetBlendType(asBlend.getAbsolutePath());
if (type == hecl::blender::BlendType::None) { if (type == hecl::blender::BlendType::None) {
Log.report(logvisor::Error, fmt(_SYS_STR("unable to cook '{}'")), path.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to cook '{}'")), path.getAbsolutePath());
return nullptr; return nullptr;
} }
if (type == hecl::blender::BlendType::Mesh || type == hecl::blender::BlendType::Area) if (type == hecl::blender::BlendType::Mesh || type == hecl::blender::BlendType::Area)
@ -309,9 +322,10 @@ void SpecBase::flattenDependenciesBlend(const hecl::ProjectPath& in, std::vector
hecl::SystemStringConv chSysName(sub.name); hecl::SystemStringConv chSysName(sub.name);
if (!sub.cskrId.empty()) { if (!sub.cskrId.empty()) {
hecl::SystemStringConv cskrSysName(sub.cskrId); hecl::SystemStringConv cskrSysName(sub.cskrId);
pathsOut.push_back(asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}_{}.CSKR")), chSysName, cskrSysName))); pathsOut.push_back(
asGlob.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}_{}.CSKR")), chSysName, cskrSysName)));
} else { } else {
pathsOut.push_back(asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}.CSKR")), chSysName))); pathsOut.push_back(asGlob.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}.CSKR")), chSysName)));
} }
const auto& arm = actor.armatures[sub.armature]; const auto& arm = actor.armatures[sub.armature];
@ -325,7 +339,8 @@ void SpecBase::flattenDependenciesBlend(const hecl::ProjectPath& in, std::vector
flattenDependenciesBlend(overlay.mesh, pathsOut, btok); flattenDependenciesBlend(overlay.mesh, pathsOut, btok);
pathsOut.push_back(overlay.mesh); pathsOut.push_back(overlay.mesh);
} }
pathsOut.push_back(asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}.{}_{}.CSKR")), chSysName, overlaySys, overlayCskrId))); pathsOut.push_back(asGlob.ensureAuxInfo(
fmt::format(FMT_STRING(_SYS_STR("{}.{}_{}.CSKR")), chSysName, overlaySys, overlayCskrId)));
} }
} }
}; };
@ -343,7 +358,8 @@ void SpecBase::flattenDependenciesBlend(const hecl::ProjectPath& in, std::vector
hecl::SystemStringConv chSysName(att.name); hecl::SystemStringConv chSysName(att.name);
hecl::SystemStringConv sysCskrId(att.cskrId); hecl::SystemStringConv sysCskrId(att.cskrId);
pathsOut.push_back(asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("ATTACH.{}_{}.CSKR")), chSysName, sysCskrId))); pathsOut.push_back(
asGlob.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("ATTACH.{}_{}.CSKR")), chSysName, sysCskrId)));
if (att.armature >= 0) { if (att.armature >= 0) {
const auto& arm = actor.armatures[att.armature]; const auto& arm = actor.armatures[att.armature];
@ -355,9 +371,10 @@ void SpecBase::flattenDependenciesBlend(const hecl::ProjectPath& in, std::vector
for (const auto& act : actNames) { for (const auto& act : actNames) {
hecl::SystemStringConv actSysName(act.first); hecl::SystemStringConv actSysName(act.first);
hecl::SystemStringConv actAnimId(act.second); hecl::SystemStringConv actAnimId(act.second);
pathsOut.push_back(asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}_{}.ANIM")), actSysName, actAnimId))); pathsOut.push_back(asGlob.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}_{}.ANIM")), actSysName, actAnimId)));
hecl::SystemString searchPrefix(asGlob.getWithExtension( hecl::SystemString searchPrefix(
fmt::format(fmt(_SYS_STR(".{}_")), actSysName).c_str(), true).getLastComponent()); asGlob.getWithExtension(fmt::format(FMT_STRING(_SYS_STR(".{}_")), actSysName).c_str(), true)
.getLastComponent());
hecl::ProjectPath evntPath; hecl::ProjectPath evntPath;
for (const auto& ent : dEnum) { for (const auto& ent : dEnum) {
if (hecl::StringUtils::BeginsWith(ent.m_name, searchPrefix.c_str()) && if (hecl::StringUtils::BeginsWith(ent.m_name, searchPrefix.c_str()) &&
@ -468,7 +485,7 @@ void SpecBase::copyBuildListData(std::vector<std::tuple<size_t, size_t, bool>>&
fileIndex.reserve(buildList.size()); fileIndex.reserve(buildList.size());
int loadIdx = 0; int loadIdx = 0;
for (const auto& tag : buildList) { for (const auto& tag : buildList) {
hecl::SystemString str = fmt::format(fmt(_SYS_STR("Copying {}")), tag); hecl::SystemString str = fmt::format(FMT_STRING(_SYS_STR("Copying {}")), tag);
progress.print(str.c_str(), nullptr, ++loadIdx / float(buildList.size())); progress.print(str.c_str(), nullptr, ++loadIdx / float(buildList.size()));
auto& [positionOut, sizeOut, compressedOut] = fileIndex.emplace_back(); auto& [positionOut, sizeOut, compressedOut] = fileIndex.emplace_back();
@ -476,7 +493,7 @@ void SpecBase::copyBuildListData(std::vector<std::tuple<size_t, size_t, bool>>&
if (tag.type == FOURCC('MLVL')) { if (tag.type == FOURCC('MLVL')) {
auto search = mlvlData.find(tag.id); auto search = mlvlData.find(tag.id);
if (search == mlvlData.end()) if (search == mlvlData.end())
Log.report(logvisor::Fatal, fmt(_SYS_STR("Unable to find MLVL {}")), tag.id); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unable to find MLVL {}")), tag.id);
positionOut = pakOut.position(); positionOut = pakOut.position();
sizeOut = ROUND_UP_32(search->second.size()); sizeOut = ROUND_UP_32(search->second.size());
@ -492,7 +509,7 @@ void SpecBase::copyBuildListData(std::vector<std::tuple<size_t, size_t, bool>>&
hecl::ProjectPath cooked = getCookedPath(path, true); hecl::ProjectPath cooked = getCookedPath(path, true);
athena::io::FileReader r(cooked.getAbsolutePath()); athena::io::FileReader r(cooked.getAbsolutePath());
if (r.hasError()) if (r.hasError())
Log.report(logvisor::Fatal, fmt(_SYS_STR("Unable to open resource {}")), cooked.getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unable to open resource {}")), cooked.getRelativePath());
atUint64 size = r.length(); atUint64 size = r.length();
auto data = r.readUBytes(size); auto data = r.readUBytes(size);
auto compData = compressPakData(tag, data.get(), size); auto compData = compressPakData(tag, data.get(), size);
@ -616,7 +633,7 @@ void SpecBase::doPackage(const hecl::ProjectPath& path, const hecl::Database::Da
/* Async cook resource list if using ClientProcess */ /* Async cook resource list if using ClientProcess */
if (cp) { if (cp) {
Log.report(logvisor::Info, fmt(_SYS_STR("Validating resources"))); Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Validating resources")));
progress.setMainIndeterminate(true); progress.setMainIndeterminate(true);
std::vector<urde::SObjectTag> cookTags; std::vector<urde::SObjectTag> cookTags;
cookTags.reserve(buildList.size()); cookTags.reserve(buildList.size());
@ -626,8 +643,7 @@ void SpecBase::doPackage(const hecl::ProjectPath& path, const hecl::Database::Da
std::unordered_set<urde::SObjectTag> addedTags; std::unordered_set<urde::SObjectTag> addedTags;
addedTags.reserve(buildList.size()); addedTags.reserve(buildList.size());
for (auto& tag : buildList) { for (auto& tag : buildList) {
if ((i == 0 && tag.type == FOURCC('CMDL')) || if ((i == 0 && tag.type == FOURCC('CMDL')) || (i == 1 && tag.type != FOURCC('CMDL'))) {
(i == 1 && tag.type != FOURCC('CMDL'))) {
if (addedTags.find(tag) != addedTags.end()) if (addedTags.find(tag) != addedTags.end())
continue; continue;
addedTags.insert(tag); addedTags.insert(tag);
@ -640,7 +656,7 @@ void SpecBase::doPackage(const hecl::ProjectPath& path, const hecl::Database::Da
for (auto& tag : cookTags) { for (auto& tag : cookTags) {
hecl::ProjectPath depPath = pathFromTag(tag); hecl::ProjectPath depPath = pathFromTag(tag);
if (!depPath) if (!depPath)
Log.report(logvisor::Fatal, fmt(_SYS_STR("Unable to resolve {}")), tag); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unable to resolve {}")), tag);
m_project.cookPath(depPath, progress, false, false, fast, entry, cp); m_project.cookPath(depPath, progress, false, false, fast, entry, cp);
} }
progress.setMainIndeterminate(false); progress.setMainIndeterminate(false);
@ -650,7 +666,7 @@ void SpecBase::doPackage(const hecl::ProjectPath& path, const hecl::Database::Da
/* Write resource data and build file index */ /* Write resource data and build file index */
std::vector<std::tuple<size_t, size_t, bool>> fileIndex; std::vector<std::tuple<size_t, size_t, bool>> fileIndex;
Log.report(logvisor::Info, fmt(_SYS_STR("Copying data into {}")), outPath.getRelativePath()); Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Copying data into {}")), outPath.getRelativePath());
copyBuildListData(fileIndex, buildList, entry, fast, progress, pakOut, mlvlData); copyBuildListData(fileIndex, buildList, entry, fast, progress, pakOut, mlvlData);
/* Write file index */ /* Write file index */
@ -688,9 +704,9 @@ hecl::ProjectPath SpecBase::getCookedPath(const hecl::ProjectPath& working, bool
return working.getCookedPath(*spec); return working.getCookedPath(*spec);
} }
static void PNGErr(png_structp png, png_const_charp msg) { Log.report(logvisor::Error, fmt("{}"), msg); } static void PNGErr(png_structp png, png_const_charp msg) { Log.report(logvisor::Error, FMT_STRING("{}"), msg); }
static void PNGWarn(png_structp png, png_const_charp msg) { Log.report(logvisor::Warning, fmt("{}"), msg); } static void PNGWarn(png_structp png, png_const_charp msg) { Log.report(logvisor::Warning, FMT_STRING("{}"), msg); }
constexpr uint8_t Convert4To8(uint8_t v) { constexpr uint8_t Convert4To8(uint8_t v) {
/* Swizzle bits: 00001234 -> 12341234 */ /* Swizzle bits: 00001234 -> 12341234 */
@ -703,12 +719,12 @@ void SpecBase::extractRandomStaticEntropy(const uint8_t* buf, const hecl::Projec
entropyPath.makeDirChain(false); entropyPath.makeDirChain(false);
if (const auto fp = hecl::FopenUnique(catalogPath.getAbsolutePath().data(), _SYS_STR("a"))) { if (const auto fp = hecl::FopenUnique(catalogPath.getAbsolutePath().data(), _SYS_STR("a"))) {
fmt::print(fp.get(), fmt("RandomStaticEntropy: {}\n"), entropyPath.getRelativePathUTF8()); fmt::print(fp.get(), FMT_STRING("RandomStaticEntropy: {}\n"), entropyPath.getRelativePathUTF8());
} }
auto fp = hecl::FopenUnique(entropyPath.getAbsolutePath().data(), _SYS_STR("wb")); auto fp = hecl::FopenUnique(entropyPath.getAbsolutePath().data(), _SYS_STR("wb"));
if (fp == nullptr) { if (fp == nullptr) {
Log.report(logvisor::Error, fmt(_SYS_STR("Unable to open '{}' for writing")), entropyPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to open '{}' for writing")), entropyPath.getAbsolutePath());
return; return;
} }
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, PNGErr, PNGWarn); png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, PNGErr, PNGWarn);
@ -746,7 +762,7 @@ void SpecBase::clearTagCache() {
} }
hecl::ProjectPath SpecBase::pathFromTag(const urde::SObjectTag& tag) const { hecl::ProjectPath SpecBase::pathFromTag(const urde::SObjectTag& tag) const {
std::unique_lock<std::mutex> lk(const_cast<SpecBase&>(*this).m_backgroundIndexMutex); std::unique_lock lk(m_backgroundIndexMutex);
auto search = m_tagToPath.find(tag); auto search = m_tagToPath.find(tag);
if (search != m_tagToPath.cend()) if (search != m_tagToPath.cend())
return search->second; return search->second;
@ -761,7 +777,7 @@ urde::SObjectTag SpecBase::tagFromPath(const hecl::ProjectPath& path) const {
} }
bool SpecBase::waitForTagReady(const urde::SObjectTag& tag, const hecl::ProjectPath*& pathOut) { bool SpecBase::waitForTagReady(const urde::SObjectTag& tag, const hecl::ProjectPath*& pathOut) {
std::unique_lock<std::mutex> lk(m_backgroundIndexMutex); std::unique_lock lk(m_backgroundIndexMutex);
auto search = m_tagToPath.find(tag); auto search = m_tagToPath.find(tag);
if (search == m_tagToPath.end()) { if (search == m_tagToPath.end()) {
if (m_backgroundRunning) { if (m_backgroundRunning) {
@ -787,7 +803,7 @@ const urde::SObjectTag* SpecBase::getResourceIdByName(std::string_view name) con
std::string lower(name); std::string lower(name);
std::transform(lower.cbegin(), lower.cend(), lower.begin(), tolower); std::transform(lower.cbegin(), lower.cend(), lower.begin(), tolower);
std::unique_lock<std::mutex> lk(const_cast<SpecBase&>(*this).m_backgroundIndexMutex); std::unique_lock lk(m_backgroundIndexMutex);
auto search = m_catalogNameToTag.find(lower); auto search = m_catalogNameToTag.find(lower);
if (search == m_catalogNameToTag.end()) { if (search == m_catalogNameToTag.end()) {
if (m_backgroundRunning) { if (m_backgroundRunning) {
@ -811,7 +827,7 @@ FourCC SpecBase::getResourceTypeById(urde::CAssetId id) const {
if (!id.IsValid()) if (!id.IsValid())
return {}; return {};
std::unique_lock<std::mutex> lk(const_cast<SpecBase&>(*this).m_backgroundIndexMutex); std::unique_lock lk(m_backgroundIndexMutex);
urde::SObjectTag searchTag = {FourCC(), id}; urde::SObjectTag searchTag = {FourCC(), id};
auto search = m_tagToPath.find(searchTag); auto search = m_tagToPath.find(searchTag);
if (search == m_tagToPath.end()) { if (search == m_tagToPath.end()) {
@ -852,7 +868,7 @@ void SpecBase::enumerateNamedResources(
static void WriteTag(athena::io::YAMLDocWriter& cacheWriter, const urde::SObjectTag& pathTag, static void WriteTag(athena::io::YAMLDocWriter& cacheWriter, const urde::SObjectTag& pathTag,
const hecl::ProjectPath& path) { const hecl::ProjectPath& path) {
auto key = fmt::format(fmt("{}"), pathTag.id); auto key = fmt::format(FMT_STRING("{}"), pathTag.id);
if (auto* existing = cacheWriter.getCurNode()->findMapChild(key)) { if (auto* existing = cacheWriter.getCurNode()->findMapChild(key)) {
existing->m_seqChildren.emplace_back(athena::io::ValToNode(path.getEncodableStringUTF8())); existing->m_seqChildren.emplace_back(athena::io::ValToNode(path.getEncodableStringUTF8()));
} else if (auto v = cacheWriter.enterSubVector(key)) { } else if (auto v = cacheWriter.enterSubVector(key)) {
@ -863,7 +879,7 @@ static void WriteTag(athena::io::YAMLDocWriter& cacheWriter, const urde::SObject
static void WriteNameTag(athena::io::YAMLDocWriter& nameWriter, const urde::SObjectTag& pathTag, static void WriteNameTag(athena::io::YAMLDocWriter& nameWriter, const urde::SObjectTag& pathTag,
std::string_view name) { std::string_view name) {
nameWriter.writeString(name.data(), fmt::format(fmt("{}"), pathTag.id)); nameWriter.writeString(name.data(), fmt::format(FMT_STRING("{}"), pathTag.id));
} }
void SpecBase::readCatalog(const hecl::ProjectPath& catalogPath, athena::io::YAMLDocWriter& nameWriter) { void SpecBase::readCatalog(const hecl::ProjectPath& catalogPath, athena::io::YAMLDocWriter& nameWriter) {
@ -901,13 +917,13 @@ void SpecBase::readCatalog(const hecl::ProjectPath& catalogPath, athena::io::YAM
continue; continue;
urde::SObjectTag pathTag = tagFromPath(path); urde::SObjectTag pathTag = tagFromPath(path);
if (pathTag) { if (pathTag) {
std::unique_lock<std::mutex> lk(m_backgroundIndexMutex); std::unique_lock lk(m_backgroundIndexMutex);
m_catalogNameToTag[pLower] = pathTag; m_catalogNameToTag[pLower] = pathTag;
m_catalogTagToNames[pathTag].insert(p.first); m_catalogTagToNames[pathTag].insert(p.first);
WriteNameTag(nameWriter, pathTag, p.first); WriteNameTag(nameWriter, pathTag, p.first);
#if 0 #if 0
fmt::print(stderr, fmt("{} {} {:08X}\n"), p.first, pathTag.type.toString(), pathTag.id.Value()); fmt::print(stderr, FMT_STRING("{} {} {:08X}\n"), p.first, pathTag.type.toString(), pathTag.id.Value());
#endif #endif
} }
} }
@ -948,7 +964,7 @@ void SpecBase::insertPathTag(athena::io::YAMLDocWriter& cacheWriter, const urde:
if (search != m_tagToPath.end() && search->second != path && if (search != m_tagToPath.end() && search->second != path &&
tag.type != FOURCC('CINF') && tag.type != FOURCC('CSKR') && tag.type != FOURCC('CINF') && tag.type != FOURCC('CSKR') &&
tag.type != FOURCC('ANIM') && tag.type != FOURCC('EVNT')) { tag.type != FOURCC('ANIM') && tag.type != FOURCC('EVNT')) {
Log.report(logvisor::Fatal, fmt(_SYS_STR("'{}|{}' already exists for tag {} as '{}|{}'")), Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("'{}|{}' already exists for tag {} as '{}|{}'")),
path.getRelativePath(), path.getAuxInfo(), tag, path.getRelativePath(), path.getAuxInfo(), tag,
search->second.getRelativePath(), search->second.getAuxInfo()); search->second.getRelativePath(), search->second.getAuxInfo());
} }
@ -958,7 +974,7 @@ void SpecBase::insertPathTag(athena::io::YAMLDocWriter& cacheWriter, const urde:
WriteTag(cacheWriter, tag, path); WriteTag(cacheWriter, tag, path);
#if DUMP_CACHE_FILL #if DUMP_CACHE_FILL
if (dump) if (dump)
fmt::print(stderr, fmt("{} {}\n"), tag, path.getRelativePathUTF8()); fmt::print(stderr, FMT_STRING("{} {}\n"), tag, path.getRelativePathUTF8());
#endif #endif
} }
@ -997,9 +1013,9 @@ bool SpecBase::addFileToIndex(const hecl::ProjectPath& path, athena::io::YAMLDoc
hecl::ProjectPath subPath; hecl::ProjectPath subPath;
if (!sub.second.empty()) { if (!sub.second.empty()) {
hecl::SystemStringConv cskrId(sub.second); hecl::SystemStringConv cskrId(sub.second);
subPath = asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}_{}.CSKR")), subName, cskrId)); subPath = asGlob.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}_{}.CSKR")), subName, cskrId));
} else { } else {
subPath = asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}.CSKR")), subName)); subPath = asGlob.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}.CSKR")), subName));
} }
insertPathTag(cacheWriter, buildTagFromPath(subPath), subPath); insertPathTag(cacheWriter, buildTagFromPath(subPath), subPath);
@ -1008,11 +1024,10 @@ bool SpecBase::addFileToIndex(const hecl::ProjectPath& path, athena::io::YAMLDoc
hecl::SystemStringConv overlaySys(overlay.first); hecl::SystemStringConv overlaySys(overlay.first);
hecl::SystemStringConv overlayCskrId(overlay.second); hecl::SystemStringConv overlayCskrId(overlay.second);
if (!overlay.second.empty()) { if (!overlay.second.empty()) {
subPath = subPath = asGlob.ensureAuxInfo(
asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}.{}_{}.CSKR")), subName, overlaySys, overlayCskrId)); fmt::format(FMT_STRING(_SYS_STR("{}.{}_{}.CSKR")), subName, overlaySys, overlayCskrId));
} else { } else {
subPath = subPath = asGlob.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}.{}.CSKR")), subName, overlaySys));
asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}.{}.CSKR")), subName, overlaySys));
} }
insertPathTag(cacheWriter, buildTagFromPath(subPath), subPath); insertPathTag(cacheWriter, buildTagFromPath(subPath), subPath);
} }
@ -1024,11 +1039,10 @@ bool SpecBase::addFileToIndex(const hecl::ProjectPath& path, athena::io::YAMLDoc
hecl::SystemStringConv attachmentCskrId(attachment.second); hecl::SystemStringConv attachmentCskrId(attachment.second);
hecl::ProjectPath subPath; hecl::ProjectPath subPath;
if (!attachment.second.empty()) { if (!attachment.second.empty()) {
subPath = subPath = asGlob.ensureAuxInfo(
asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("ATTACH.{}_{}.CSKR")), attachmentSys, attachmentCskrId)); fmt::format(FMT_STRING(_SYS_STR("ATTACH.{}_{}.CSKR")), attachmentSys, attachmentCskrId));
} else { } else {
subPath = subPath = asGlob.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("ATTACH.{}.CSKR")), attachmentSys));
asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("ATTACH.{}.CSKR")), attachmentSys));
} }
insertPathTag(cacheWriter, buildTagFromPath(subPath), subPath); insertPathTag(cacheWriter, buildTagFromPath(subPath), subPath);
} }
@ -1038,9 +1052,9 @@ bool SpecBase::addFileToIndex(const hecl::ProjectPath& path, athena::io::YAMLDoc
hecl::SystemStringConv animId(act.second); hecl::SystemStringConv animId(act.second);
hecl::ProjectPath subPath; hecl::ProjectPath subPath;
if (!act.second.empty()) { if (!act.second.empty()) {
subPath = asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}_{}.ANIM")), sysStr, animId)); subPath = asGlob.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}_{}.ANIM")), sysStr, animId));
} else { } else {
subPath = asGlob.ensureAuxInfo(fmt::format(fmt(_SYS_STR("{}.ANIM")), sysStr)); subPath = asGlob.ensureAuxInfo(fmt::format(FMT_STRING(_SYS_STR("{}.ANIM")), sysStr));
} }
insertPathTag(cacheWriter, buildTagFromPath(subPath), subPath); insertPathTag(cacheWriter, buildTagFromPath(subPath), subPath);
} }
@ -1108,10 +1122,10 @@ void SpecBase::backgroundIndexProc() {
if (tagCachePath.isFile()) { if (tagCachePath.isFile()) {
athena::io::FileReader reader(tagCachePath.getAbsolutePath()); athena::io::FileReader reader(tagCachePath.getAbsolutePath());
if (reader.isOpen()) { if (reader.isOpen()) {
Log.report(logvisor::Info, fmt(_SYS_STR("Cache index of '{}' loading")), getOriginalSpec().m_name); Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Cache index of '{}' loading")), getOriginalSpec().m_name);
athena::io::YAMLDocReader cacheReader; athena::io::YAMLDocReader cacheReader;
if (cacheReader.parse(&reader)) { if (cacheReader.parse(&reader)) {
std::unique_lock<std::mutex> lk(m_backgroundIndexMutex); std::unique_lock lk(m_backgroundIndexMutex);
size_t tagCount = cacheReader.getRootNode()->m_mapChildren.size(); size_t tagCount = cacheReader.getRootNode()->m_mapChildren.size();
m_tagToPath.reserve(tagCount); m_tagToPath.reserve(tagCount);
m_pathToTag.reserve(tagCount); m_pathToTag.reserve(tagCount);
@ -1134,20 +1148,20 @@ void SpecBase::backgroundIndexProc() {
++loadIdx; ++loadIdx;
if (!(loadIdx % 100)) if (!(loadIdx % 100))
fmt::print(stderr, fmt("\r {} / {}"), loadIdx, tagCount); fmt::print(stderr, FMT_STRING("\r {} / {}"), loadIdx, tagCount);
} }
fmt::print(stderr, fmt("\r {} / {}\n"), loadIdx, tagCount); fmt::print(stderr, FMT_STRING("\r {} / {}\n"), loadIdx, tagCount);
} }
Log.report(logvisor::Info, fmt(_SYS_STR("Cache index of '{}' loaded; {} tags")), getOriginalSpec().m_name, Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Cache index of '{}' loaded; {} tags")), getOriginalSpec().m_name,
m_tagToPath.size()); m_tagToPath.size());
if (nameCachePath.isFile()) { if (nameCachePath.isFile()) {
/* Read in name cache */ /* Read in name cache */
Log.report(logvisor::Info, fmt(_SYS_STR("Name index of '{}' loading")), getOriginalSpec().m_name); Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Name index of '{}' loading")), getOriginalSpec().m_name);
athena::io::FileReader nreader(nameCachePath.getAbsolutePath()); athena::io::FileReader nreader(nameCachePath.getAbsolutePath());
athena::io::YAMLDocReader nameReader; athena::io::YAMLDocReader nameReader;
if (nameReader.parse(&nreader)) { if (nameReader.parse(&nreader)) {
std::unique_lock<std::mutex> lk(m_backgroundIndexMutex); std::unique_lock lk(m_backgroundIndexMutex);
m_catalogNameToTag.reserve(nameReader.getRootNode()->m_mapChildren.size()); m_catalogNameToTag.reserve(nameReader.getRootNode()->m_mapChildren.size());
m_catalogTagToNames.reserve(nameReader.getRootNode()->m_mapChildren.size()); m_catalogTagToNames.reserve(nameReader.getRootNode()->m_mapChildren.size());
for (const auto& child : nameReader.getRootNode()->m_mapChildren) { for (const auto& child : nameReader.getRootNode()->m_mapChildren) {
@ -1162,13 +1176,13 @@ void SpecBase::backgroundIndexProc() {
} }
} }
} }
Log.report(logvisor::Info, fmt(_SYS_STR("Name index of '{}' loaded; {} names")), getOriginalSpec().m_name, Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Name index of '{}' loaded; {} names")),
m_catalogNameToTag.size()); getOriginalSpec().m_name, m_catalogNameToTag.size());
} }
} }
} }
Log.report(logvisor::Info, fmt(_SYS_STR("Background index of '{}' started")), getOriginalSpec().m_name); Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Background index of '{}' started")), getOriginalSpec().m_name);
backgroundIndexRecursiveProc(specRoot, cacheWriter, nameWriter, 0); backgroundIndexRecursiveProc(specRoot, cacheWriter, nameWriter, 0);
tagCachePath.makeDirChain(false); tagCachePath.makeDirChain(false);
@ -1179,7 +1193,7 @@ void SpecBase::backgroundIndexProc() {
nameWriter.finish(&nwriter); nameWriter.finish(&nwriter);
m_backgroundBlender.shutdown(); m_backgroundBlender.shutdown();
Log.report(logvisor::Info, fmt(_SYS_STR("Background index of '{}' complete; {} tags, {} names")), Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Background index of '{}' complete; {} tags, {} names")),
getOriginalSpec().m_name, m_tagToPath.size(), m_catalogNameToTag.size()); getOriginalSpec().m_name, m_tagToPath.size(), m_catalogNameToTag.size());
m_backgroundRunning = false; m_backgroundRunning = false;
} }
@ -1198,7 +1212,7 @@ void SpecBase::beginBackgroundIndex() {
} }
void SpecBase::waitForIndexComplete() const { void SpecBase::waitForIndexComplete() const {
std::unique_lock<std::mutex> lk(const_cast<SpecBase&>(*this).m_backgroundIndexMutex); std::unique_lock lk(m_backgroundIndexMutex);
while (m_backgroundRunning) { while (m_backgroundRunning) {
lk.unlock(); lk.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(2)); std::this_thread::sleep_for(std::chrono::milliseconds(2));
@ -1206,4 +1220,23 @@ void SpecBase::waitForIndexComplete() const {
} }
} }
void SpecBase::WriteVersionInfo(hecl::Database::Project& project, const hecl::ProjectPath& pakPath) {
hecl::ProjectPath versionPath(pakPath, _SYS_STR("version.yaml"));
versionPath.makeDirChain(false);
URDEVersionInfo info;
info.version = m_version;
info.region = m_region;
info.game = m_game;
info.isTrilogy = !m_standalone;
athena::io::FileWriter writer(versionPath.getAbsolutePath());
athena::io::ToYAMLStream(info, writer);
}
void SpecBase::setCurRegion(ERegion region) {
g_CurRegion = region;
}
void SpecBase::setCurSpecIsWii(bool isWii) {
g_CurSpecIsWii = isWii;
}
} // namespace DataSpec } // namespace DataSpec

View File

@ -23,6 +23,10 @@ class YAMLDocWriter;
} // namespace athena::io } // namespace athena::io
namespace DataSpec { namespace DataSpec {
enum class ERegion;
enum class EGame;
ERegion getCurrentRegion();
bool isCurrentSpecWii();
struct SpecBase : hecl::Database::IDataSpec { struct SpecBase : hecl::Database::IDataSpec {
/* HECL Adaptors */ /* HECL Adaptors */
@ -175,12 +179,12 @@ protected:
hecl::blender::Token m_backgroundBlender; hecl::blender::Token m_backgroundBlender;
std::thread m_backgroundIndexTh; std::thread m_backgroundIndexTh;
std::mutex m_backgroundIndexMutex; mutable std::mutex m_backgroundIndexMutex;
bool m_backgroundRunning = false; bool m_backgroundRunning = false;
void readCatalog(const hecl::ProjectPath& catalogPath, athena::io::YAMLDocWriter& nameWriter); void readCatalog(const hecl::ProjectPath& catalogPath, athena::io::YAMLDocWriter& nameWriter);
void insertPathTag(athena::io::YAMLDocWriter& cacheWriter, const urde::SObjectTag& tag, void insertPathTag(athena::io::YAMLDocWriter& cacheWriter, const urde::SObjectTag& tag, const hecl::ProjectPath& path,
const hecl::ProjectPath& path, bool dump = true); bool dump = true);
bool addFileToIndex(const hecl::ProjectPath& path, athena::io::YAMLDocWriter& cacheWriter); bool addFileToIndex(const hecl::ProjectPath& path, athena::io::YAMLDocWriter& cacheWriter);
void backgroundIndexRecursiveProc(const hecl::ProjectPath& path, athena::io::YAMLDocWriter& cacheWriter, void backgroundIndexRecursiveProc(const hecl::ProjectPath& path, athena::io::YAMLDocWriter& cacheWriter,
athena::io::YAMLDocWriter& nameWriter, int level); athena::io::YAMLDocWriter& nameWriter, int level);
@ -197,8 +201,16 @@ protected:
const std::unordered_map<urde::CAssetId, std::vector<uint8_t>>& mlvlData); const std::unordered_map<urde::CAssetId, std::vector<uint8_t>>& mlvlData);
std::unique_ptr<nod::DiscBase> m_disc; std::unique_ptr<nod::DiscBase> m_disc;
bool m_isWii; bool m_isWii{};
bool m_standalone; bool m_standalone{};
ERegion m_region;
EGame m_game;
std::string m_version;
void WriteVersionInfo(hecl::Database::Project& project, const hecl::ProjectPath& pakPath);
static void setCurRegion(ERegion region);
static void setCurSpecIsWii(bool isWii);
}; };
bool IsPathAudioGroup(const hecl::ProjectPath& path); bool IsPathAudioGroup(const hecl::ProjectPath& path);

View File

@ -29,6 +29,7 @@
#include "DNACommon/DPSC.hpp" #include "DNACommon/DPSC.hpp"
#include "DNACommon/DGRP.hpp" #include "DNACommon/DGRP.hpp"
#include "DNACommon/MAPU.hpp" #include "DNACommon/MAPU.hpp"
#include "DNACommon/URDEVersionInfo.hpp"
#include "DNACommon/Tweaks/TweakWriter.hpp" #include "DNACommon/Tweaks/TweakWriter.hpp"
#include "DNAMP1/Tweaks/CTweakPlayerRes.hpp" #include "DNAMP1/Tweaks/CTweakPlayerRes.hpp"
#include "DNAMP1/Tweaks/CTweakGunRes.hpp" #include "DNAMP1/Tweaks/CTweakGunRes.hpp"
@ -46,7 +47,6 @@
#include "DNAMP1/Tweaks/CTweakPlayerGun.hpp" #include "DNAMP1/Tweaks/CTweakPlayerGun.hpp"
#include "DNAMP1/MazeSeeds.hpp" #include "DNAMP1/MazeSeeds.hpp"
#include "DNAMP1/SnowForces.hpp" #include "DNAMP1/SnowForces.hpp"
#include "hecl/ClientProcess.hpp" #include "hecl/ClientProcess.hpp"
#include "hecl/MultiProgressPrinter.hpp" #include "hecl/MultiProgressPrinter.hpp"
#include "hecl/Blender/Connection.hpp" #include "hecl/Blender/Connection.hpp"
@ -63,15 +63,17 @@ extern hecl::Database::DataSpecEntry SpecEntMP1PC;
extern hecl::Database::DataSpecEntry SpecEntMP1ORIG; extern hecl::Database::DataSpecEntry SpecEntMP1ORIG;
struct TextureCache { struct TextureCache {
static void Generate(PAKRouter<DNAMP1::PAKBridge>& pakRouter, hecl::Database::Project& project, const hecl::ProjectPath& pakPath) { static void Generate(PAKRouter<DNAMP1::PAKBridge>& pakRouter, hecl::Database::Project& project,
const hecl::ProjectPath& pakPath) {
hecl::ProjectPath texturePath(pakPath, _SYS_STR("texture_cache.yaml")); hecl::ProjectPath texturePath(pakPath, _SYS_STR("texture_cache.yaml"));
hecl::ProjectPath catalogPath(pakPath, _SYS_STR("!catalog.yaml")); hecl::ProjectPath catalogPath(pakPath, _SYS_STR("!catalog.yaml"));
texturePath.makeDirChain(false);
if (const auto fp = hecl::FopenUnique(catalogPath.getAbsolutePath().data(), _SYS_STR("a"))) { if (const auto fp = hecl::FopenUnique(catalogPath.getAbsolutePath().data(), _SYS_STR("a"))) {
fmt::print(fp.get(), fmt("TextureCache: {}\n"), texturePath.getRelativePathUTF8()); fmt::print(fp.get(), FMT_STRING("TextureCache: {}\n"), texturePath.getRelativePathUTF8());
} }
Log.report(logvisor::Level::Info, fmt("Gathering Texture metadata (this can take up to 10 seconds)...")); Log.report(logvisor::Level::Info, FMT_STRING("Gathering Texture metadata (this can take up to 10 seconds)..."));
std::unordered_map<UniqueID32, TXTR::Meta> metaMap; std::unordered_map<UniqueID32, TXTR::Meta> metaMap;
pakRouter.enumerateResources([&](const DNAMP1::PAK::Entry* ent) { pakRouter.enumerateResources([&](const DNAMP1::PAK::Entry* ent) {
@ -91,7 +93,7 @@ struct TextureCache {
athena::io::FileWriter fileW(texturePath.getAbsolutePath()); athena::io::FileWriter fileW(texturePath.getAbsolutePath());
yamlW.finish(&fileW); yamlW.finish(&fileW);
Log.report(logvisor::Level::Info, fmt("Done...")); Log.report(logvisor::Level::Info, FMT_STRING("Done..."));
} }
static void Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath) { static void Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath) {
@ -111,9 +113,8 @@ struct TextureCache {
metaPairs.emplace_back(projectPath.parsedHash32(), meta); metaPairs.emplace_back(projectPath.parsedHash32(), meta);
} }
std::sort(metaPairs.begin(), metaPairs.end(), [](const auto& a, const auto& b) -> bool { std::sort(metaPairs.begin(), metaPairs.end(),
return a.first < b.first; [](const auto& a, const auto& b) -> bool { return a.first < b.first; });
});
athena::io::FileWriter w(outPath.getAbsolutePath()); athena::io::FileWriter w(outPath.getAbsolutePath());
w.writeUint32Big(metaPairs.size()); w.writeUint32Big(metaPairs.size());
@ -144,7 +145,8 @@ struct SpecMP1 : SpecBase {
, m_workPath(project.getProjectWorkingPath(), _SYS_STR("MP1")) , m_workPath(project.getProjectWorkingPath(), _SYS_STR("MP1"))
, m_cookPath(project.getProjectCookedPath(SpecEntMP1), _SYS_STR("MP1")) , m_cookPath(project.getProjectCookedPath(SpecEntMP1), _SYS_STR("MP1"))
, m_pakRouter(*this, m_workPath, m_cookPath) { , m_pakRouter(*this, m_workPath, m_cookPath) {
setThreadProject(); m_game = EGame::MetroidPrime1;
SpecBase::setThreadProject();
} }
void buildPaks(nod::Node& root, const std::vector<hecl::SystemString>& args, ExtractReport& rep) { void buildPaks(nod::Node& root, const std::vector<hecl::SystemString>& args, ExtractReport& rep) {
@ -196,8 +198,9 @@ struct SpecMP1 : SpecBase {
/* Sort PAKs alphabetically */ /* Sort PAKs alphabetically */
m_orderedPaks.clear(); m_orderedPaks.clear();
for (DNAMP1::PAKBridge& dpak : m_paks) for (DNAMP1::PAKBridge& dpak : m_paks) {
m_orderedPaks[std::string(dpak.getName())] = &dpak; m_orderedPaks[std::string(dpak.getName())] = &dpak;
}
/* Assemble extract report */ /* Assemble extract report */
rep.childOpts.reserve(m_orderedPaks.size()); rep.childOpts.reserve(m_orderedPaks.size());
@ -217,18 +220,19 @@ struct SpecMP1 : SpecBase {
const std::vector<hecl::SystemString>& args, std::vector<ExtractReport>& reps) override { const std::vector<hecl::SystemString>& args, std::vector<ExtractReport>& reps) override {
nod::IPartition* partition = disc.getDataPartition(); nod::IPartition* partition = disc.getDataPartition();
m_dolBuf = partition->getDOLBuf(); m_dolBuf = partition->getDOLBuf();
const char* buildInfo = (char*)memmem(m_dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19; const char* buildInfo =
static_cast<char*>(memmem(m_dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16)) + 19;
if (!buildInfo) if (buildInfo == nullptr)
return false; return false;
m_version = std::string(buildInfo);
/* Root Report */ /* Root Report */
ExtractReport& rep = reps.emplace_back(); ExtractReport& rep = reps.emplace_back();
rep.name = _SYS_STR("MP1"); rep.name = _SYS_STR("MP1");
rep.desc = _SYS_STR("Metroid Prime ") + regstr; rep.desc = _SYS_STR("Metroid Prime ") + regstr;
if (buildInfo) { if (buildInfo) {
std::string buildStr(buildInfo); hecl::SystemStringConv buildView(m_version);
hecl::SystemStringConv buildView(buildStr);
rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")"); rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")");
} }
@ -274,15 +278,15 @@ struct SpecMP1 : SpecBase {
} }
m_dolBuf = dolIt->getBuf(); m_dolBuf = dolIt->getBuf();
const char* buildInfo = (char*)memmem(m_dolBuf.get(), dolIt->size(), "MetroidBuildInfo", 16) + 19; const char* buildInfo = static_cast<char*>(memmem(m_dolBuf.get(), dolIt->size(), "MetroidBuildInfo", 16)) + 19;
/* Root Report */ /* Root Report */
ExtractReport& rep = reps.emplace_back(); ExtractReport& rep = reps.emplace_back();
rep.name = _SYS_STR("MP1"); rep.name = _SYS_STR("MP1");
rep.desc = _SYS_STR("Metroid Prime ") + regstr; rep.desc = _SYS_STR("Metroid Prime ") + regstr;
if (buildInfo) { if (buildInfo != nullptr) {
std::string buildStr(buildInfo); m_version = std::string(buildInfo);
hecl::SystemStringConv buildView(buildStr); hecl::SystemStringConv buildView(m_version);
rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")"); rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")");
} }
@ -377,6 +381,14 @@ struct SpecMP1 : SpecBase {
/* Generate Texture Cache containing meta data for every texture file */ /* Generate Texture Cache containing meta data for every texture file */
TextureCache::Generate(m_pakRouter, m_project, noAramPath); TextureCache::Generate(m_pakRouter, m_project, noAramPath);
/* Write version data */
hecl::ProjectPath versionPath;
if (m_standalone) {
versionPath = hecl::ProjectPath(m_project.getProjectWorkingPath(), _SYS_STR("out/files"));
} else {
versionPath = hecl::ProjectPath(m_project.getProjectWorkingPath(), _SYS_STR("out/files/MP1"));
}
WriteVersionInfo(m_project, versionPath);
return true; return true;
} }
@ -416,7 +428,9 @@ struct SpecMP1 : SpecBase {
return true; return true;
else if (classType == DNAFont::FONT<UniqueID32>::DNAType()) else if (classType == DNAFont::FONT<UniqueID32>::DNAType())
return true; return true;
else if (classType == DNAMP1::CTweakPlayerRes::DNAType()) else if (classType == DNAMP1::CTweakPlayerRes<true>::DNAType())
return true;
else if (classType == DNAMP1::CTweakPlayerRes<false>::DNAType())
return true; return true;
else if (classType == DNAMP1::CTweakGunRes::DNAType()) else if (classType == DNAMP1::CTweakGunRes::DNAType())
return true; return true;
@ -430,7 +444,9 @@ struct SpecMP1 : SpecBase {
return true; return true;
else if (classType == DNAMP1::CTweakAutoMapper::DNAType()) else if (classType == DNAMP1::CTweakAutoMapper::DNAType())
return true; return true;
else if (classType == DNAMP1::CTweakTargeting::DNAType()) else if (classType == DNAMP1::CTweakTargeting<true>::DNAType())
return true;
else if (classType == DNAMP1::CTweakTargeting<false>::DNAType())
return true; return true;
else if (classType == DNAMP1::CTweakGui::DNAType()) else if (classType == DNAMP1::CTweakGui::DNAType())
return true; return true;
@ -571,13 +587,15 @@ struct SpecMP1 : SpecBase {
} else if (className == DataSpec::DNAMP1::SCAN::DNAType()) { } else if (className == DataSpec::DNAMP1::SCAN::DNAType()) {
resTag.type = SBIG('SCAN'); resTag.type = SBIG('SCAN');
return true; return true;
} else if (className == DataSpec::DNAMP1::CTweakPlayerRes::DNAType() || } else if (className == DataSpec::DNAMP1::CTweakPlayerRes<true>::DNAType() ||
className == DataSpec::DNAMP1::CTweakPlayerRes<false>::DNAType() ||
className == DataSpec::DNAMP1::CTweakGunRes::DNAType() || className == DataSpec::DNAMP1::CTweakGunRes::DNAType() ||
className == DataSpec::DNAMP1::CTweakSlideShow::DNAType() || className == DataSpec::DNAMP1::CTweakSlideShow::DNAType() ||
className == DataSpec::DNAMP1::CTweakPlayer::DNAType() || className == DataSpec::DNAMP1::CTweakPlayer::DNAType() ||
className == DataSpec::DNAMP1::CTweakCameraBob::DNAType() || className == DataSpec::DNAMP1::CTweakCameraBob::DNAType() ||
className == DataSpec::DNAMP1::CTweakGame::DNAType() || className == DataSpec::DNAMP1::CTweakGame::DNAType() ||
className == DataSpec::DNAMP1::CTweakTargeting::DNAType() || className == DataSpec::DNAMP1::CTweakTargeting<true>::DNAType() ||
className == DataSpec::DNAMP1::CTweakTargeting<false>::DNAType() ||
className == DataSpec::DNAMP1::CTweakAutoMapper::DNAType() || className == DataSpec::DNAMP1::CTweakAutoMapper::DNAType() ||
className == DataSpec::DNAMP1::CTweakGui::DNAType() || className == DataSpec::DNAMP1::CTweakGui::DNAType() ||
className == DataSpec::DNAMP1::CTweakPlayerControl::DNAType() || className == DataSpec::DNAMP1::CTweakPlayerControl::DNAType() ||
@ -626,7 +644,7 @@ struct SpecMP1 : SpecBase {
pathPrefix += pakName; pathPrefix += pakName;
pathPrefix += '/'; pathPrefix += '/';
std::unique_lock<std::mutex> lk(const_cast<SpecMP1&>(*this).m_backgroundIndexMutex); std::unique_lock lk(m_backgroundIndexMutex);
for (const auto& tag : m_tagToPath) for (const auto& tag : m_tagToPath)
if (!tag.second.getRelativePathUTF8().compare(0, pathPrefix.size(), pathPrefix)) if (!tag.second.getRelativePathUTF8().compare(0, pathPrefix.size(), pathPrefix))
out.push_back(tag.first); out.push_back(tag.first);
@ -739,7 +757,8 @@ struct SpecMP1 : SpecBase {
} }
if (!colMesh) if (!colMesh)
Log.report(logvisor::Fatal, fmt(_SYS_STR("unable to find mesh named 'CMESH' in {}")), in.getAbsolutePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to find mesh named 'CMESH' in {}")),
in.getAbsolutePath());
std::vector<Light> lights = ds.compileLights(); std::vector<Light> lights = ds.compileLights();
@ -821,8 +840,12 @@ struct SpecMP1 : SpecBase {
DNAFont::FONT<UniqueID32> font; DNAFont::FONT<UniqueID32> font;
font.read(reader); font.read(reader);
DNAFont::WriteFONT(font, out); DNAFont::WriteFONT(font, out);
} else if (classStr == DNAMP1::CTweakPlayerRes::DNAType()) { } else if (classStr == DNAMP1::CTweakPlayerRes<true>::DNAType()) {
DNAMP1::CTweakPlayerRes playerRes; DNAMP1::CTweakPlayerRes<true> playerRes;
playerRes.read(reader);
WriteTweak(playerRes, out);
} else if (classStr == DNAMP1::CTweakPlayerRes<false>::DNAType()) {
DNAMP1::CTweakPlayerRes<false> playerRes;
playerRes.read(reader); playerRes.read(reader);
WriteTweak(playerRes, out); WriteTweak(playerRes, out);
} else if (classStr == DNAMP1::CTweakGunRes::DNAType()) { } else if (classStr == DNAMP1::CTweakGunRes::DNAType()) {
@ -849,8 +872,12 @@ struct SpecMP1 : SpecBase {
DNAMP1::CTweakAutoMapper autoMapper; DNAMP1::CTweakAutoMapper autoMapper;
autoMapper.read(reader); autoMapper.read(reader);
WriteTweak(autoMapper, out); WriteTweak(autoMapper, out);
} else if (classStr == DNAMP1::CTweakTargeting::DNAType()) { } else if (classStr == DNAMP1::CTweakTargeting<true>::DNAType()) {
DNAMP1::CTweakTargeting targeting; DNAMP1::CTweakTargeting<false> targeting;
targeting.read(reader);
WriteTweak(targeting, out);
} else if (classStr == DNAMP1::CTweakTargeting<false>::DNAType()) {
DNAMP1::CTweakTargeting<false> targeting;
targeting.read(reader); targeting.read(reader);
WriteTweak(targeting, out); WriteTweak(targeting, out);
} else if (classStr == DNAMP1::CTweakGui::DNAType()) { } else if (classStr == DNAMP1::CTweakGui::DNAType()) {
@ -977,7 +1004,7 @@ struct SpecMP1 : SpecBase {
{ {
athena::io::FileReader r(worldPathCooked.getAbsolutePath()); athena::io::FileReader r(worldPathCooked.getAbsolutePath());
if (r.hasError()) if (r.hasError())
Log.report(logvisor::Fatal, fmt(_SYS_STR("Unable to open world {}")), worldPathCooked.getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unable to open world {}")), worldPathCooked.getRelativePath());
mlvl.read(r); mlvl.read(r);
} }
@ -1073,10 +1100,10 @@ struct SpecMP1 : SpecBase {
if (hecl::ProjectPath mapCookedPath = getCookedPath(mapPath, true)) { if (hecl::ProjectPath mapCookedPath = getCookedPath(mapPath, true)) {
athena::io::FileReader r(mapCookedPath.getAbsolutePath()); athena::io::FileReader r(mapCookedPath.getAbsolutePath());
if (r.hasError()) if (r.hasError())
Log.report(logvisor::Fatal, fmt(_SYS_STR("Unable to open {}")), mapCookedPath.getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unable to open {}")), mapCookedPath.getRelativePath());
if (r.readUint32Big() != 0xDEADF00D) if (r.readUint32Big() != 0xDEADF00D)
Log.report(logvisor::Fatal, fmt(_SYS_STR("Corrupt MAPW {}")), mapCookedPath.getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Corrupt MAPW {}")), mapCookedPath.getRelativePath());
r.readUint32Big(); r.readUint32Big();
atUint32 mapaCount = r.readUint32Big(); atUint32 mapaCount = r.readUint32Big();
for (atUint32 i = 0; i < mapaCount; ++i) { for (atUint32 i = 0; i < mapaCount; ++i) {
@ -1099,7 +1126,7 @@ struct SpecMP1 : SpecBase {
for (const auto& tex : textures) { for (const auto& tex : textures) {
urde::SObjectTag texTag = tagFromPath(tex); urde::SObjectTag texTag = tagFromPath(tex);
if (!texTag) if (!texTag)
Log.report(logvisor::Fatal, fmt(_SYS_STR("Unable to resolve {}")), tex.getRelativePath()); Log.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unable to resolve {}")), tex.getRelativePath());
listOut.push_back(texTag); listOut.push_back(texTag);
} }
} }
@ -1130,7 +1157,8 @@ struct SpecMP1 : SpecBase {
} }
void buildPakList(hecl::blender::Token& btok, athena::io::FileWriter& w, const std::vector<urde::SObjectTag>& list, void buildPakList(hecl::blender::Token& btok, athena::io::FileWriter& w, const std::vector<urde::SObjectTag>& list,
const std::vector<std::pair<urde::SObjectTag, std::string>>& nameList, atUint64& resTableOffset) override { const std::vector<std::pair<urde::SObjectTag, std::string>>& nameList,
atUint64& resTableOffset) override {
w.writeUint32Big(m_pc ? 0x80030005 : 0x00030005); w.writeUint32Big(m_pc ? 0x80030005 : 0x00030005);
w.writeUint32Big(0); w.writeUint32Big(0);

View File

@ -6,9 +6,13 @@
#include "DNAMP2/MLVL.hpp" #include "DNAMP2/MLVL.hpp"
#include "DNAMP2/STRG.hpp" #include "DNAMP2/STRG.hpp"
#include "DNAMP2/AGSC.hpp" #include "DNAMP2/AGSC.hpp"
#include "DNAMP2/PATH.hpp"
#include "DNAMP2/MAPA.hpp" #include "DNAMP2/MAPA.hpp"
#include "DNAMP1/CSNG.hpp" #include "DNAMP1/CSNG.hpp"
#include "DNACommon/MAPU.hpp" #include "DNACommon/MAPU.hpp"
#include "DNACommon/PATH.hpp"
#include "DNACommon/TXTR.hpp"
#include "DNACommon/URDEVersionInfo.hpp"
#include "hecl/ClientProcess.hpp" #include "hecl/ClientProcess.hpp"
#include "hecl/Blender/Connection.hpp" #include "hecl/Blender/Connection.hpp"
@ -25,6 +29,69 @@ static logvisor::Module Log("urde::SpecMP2");
extern hecl::Database::DataSpecEntry SpecEntMP2; extern hecl::Database::DataSpecEntry SpecEntMP2;
extern hecl::Database::DataSpecEntry SpecEntMP2ORIG; extern hecl::Database::DataSpecEntry SpecEntMP2ORIG;
struct TextureCache {
static void Generate(PAKRouter<DNAMP2::PAKBridge>& pakRouter, hecl::Database::Project& project, const hecl::ProjectPath& pakPath) {
hecl::ProjectPath texturePath(pakPath, _SYS_STR("texture_cache.yaml"));
hecl::ProjectPath catalogPath(pakPath, _SYS_STR("!catalog.yaml"));
texturePath.makeDirChain(false);
if (const auto fp = hecl::FopenUnique(catalogPath.getAbsolutePath().data(), _SYS_STR("a"))) {
fmt::print(fp.get(), FMT_STRING("TextureCache: {}\n"), texturePath.getRelativePathUTF8());
}
Log.report(logvisor::Level::Info, FMT_STRING("Gathering Texture metadata (this can take up to 10 seconds)..."));
std::unordered_map<UniqueID32, TXTR::Meta> metaMap;
pakRouter.enumerateResources([&](const DNAMP2::PAK::Entry* ent) {
if (ent->type == FOURCC('TXTR') && metaMap.find(ent->id) == metaMap.end()) {
PAKEntryReadStream rs = pakRouter.beginReadStreamForId(ent->id);
metaMap[ent->id] = TXTR::GetMetaData(rs);
}
return true;
});
athena::io::YAMLDocWriter yamlW("MP2TextureCache");
for (const auto& pair : metaMap) {
hecl::ProjectPath path = pakRouter.getWorking(pair.first);
auto rec = yamlW.enterSubRecord(path.getRelativePathUTF8());
pair.second.write(yamlW);
}
athena::io::FileWriter fileW(texturePath.getAbsolutePath());
yamlW.finish(&fileW);
Log.report(logvisor::Level::Info, FMT_STRING("Done..."));
}
static void Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath) {
hecl::Database::Project& project = inPath.getProject();
athena::io::YAMLDocReader r;
athena::io::FileReader fr(inPath.getAbsolutePath());
if (!fr.isOpen() || !r.parse(&fr))
return;
std::vector<std::pair<UniqueID32, TXTR::Meta>> metaPairs;
metaPairs.reserve(r.getRootNode()->m_mapChildren.size());
for (const auto& node : r.getRootNode()->m_mapChildren) {
hecl::ProjectPath projectPath(project, node.first);
auto rec = r.enterSubRecord(node.first.c_str());
TXTR::Meta meta;
meta.read(r);
metaPairs.emplace_back(projectPath.parsedHash32(), meta);
}
std::sort(metaPairs.begin(), metaPairs.end(), [](const auto& a, const auto& b) -> bool {
return a.first < b.first;
});
athena::io::FileWriter w(outPath.getAbsolutePath());
w.writeUint32Big(metaPairs.size());
for (const auto& pair : metaPairs) {
pair.first.write(w);
pair.second.write(w);
}
}
};
struct SpecMP2 : SpecBase { struct SpecMP2 : SpecBase {
bool checkStandaloneID(const char* id) const override { bool checkStandaloneID(const char* id) const override {
if (!memcmp(id, "G2M", 3)) if (!memcmp(id, "G2M", 3))
@ -45,7 +112,8 @@ struct SpecMP2 : SpecBase {
, m_workPath(project.getProjectWorkingPath(), _SYS_STR("MP2")) , m_workPath(project.getProjectWorkingPath(), _SYS_STR("MP2"))
, m_cookPath(project.getProjectCookedPath(SpecEntMP2), _SYS_STR("MP2")) , m_cookPath(project.getProjectCookedPath(SpecEntMP2), _SYS_STR("MP2"))
, m_pakRouter(*this, m_workPath, m_cookPath) { , m_pakRouter(*this, m_workPath, m_cookPath) {
setThreadProject(); m_game = EGame::MetroidPrime2;
SpecBase::setThreadProject();
} }
void buildPaks(nod::Node& root, const std::vector<hecl::SystemString>& args, ExtractReport& rep) { void buildPaks(nod::Node& root, const std::vector<hecl::SystemString>& args, ExtractReport& rep) {
@ -117,16 +185,17 @@ struct SpecMP2 : SpecBase {
const std::vector<hecl::SystemString>& args, std::vector<ExtractReport>& reps) override { const std::vector<hecl::SystemString>& args, std::vector<ExtractReport>& reps) override {
nod::IPartition* partition = disc.getDataPartition(); nod::IPartition* partition = disc.getDataPartition();
std::unique_ptr<uint8_t[]> dolBuf = partition->getDOLBuf(); std::unique_ptr<uint8_t[]> dolBuf = partition->getDOLBuf();
const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19; const char* buildInfo = static_cast<char*>(memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16)) + 19;
if (!buildInfo) if (buildInfo == nullptr) {
return false; return false;
}
m_version = std::string(buildInfo);
/* Root Report */ /* Root Report */
ExtractReport& rep = reps.emplace_back(); ExtractReport& rep = reps.emplace_back();
rep.name = _SYS_STR("MP2"); rep.name = _SYS_STR("MP2");
rep.desc = _SYS_STR("Metroid Prime 2 ") + regstr; rep.desc = _SYS_STR("Metroid Prime 2 ") + regstr;
std::string buildStr(buildInfo); hecl::SystemStringConv buildView(m_version);
hecl::SystemStringConv buildView(buildStr);
rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")"); rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")");
/* Iterate PAKs and build level options */ /* Iterate PAKs and build level options */
@ -140,7 +209,7 @@ struct SpecMP2 : SpecBase {
const std::vector<hecl::SystemString>& args, std::vector<ExtractReport>& reps) override { const std::vector<hecl::SystemString>& args, std::vector<ExtractReport>& reps) override {
std::vector<hecl::SystemString> mp2args; std::vector<hecl::SystemString> mp2args;
bool doExtract = false; bool doExtract = false;
if (args.size()) { if (!args.empty()) {
/* Needs filter */ /* Needs filter */
for (const hecl::SystemString& arg : args) { for (const hecl::SystemString& arg : args) {
hecl::SystemString lowerArg = arg; hecl::SystemString lowerArg = arg;
@ -171,15 +240,15 @@ struct SpecMP2 : SpecBase {
} }
std::unique_ptr<uint8_t[]> dolBuf = dolIt->getBuf(); std::unique_ptr<uint8_t[]> dolBuf = dolIt->getBuf();
const char* buildInfo = (char*)memmem(dolBuf.get(), dolIt->size(), "MetroidBuildInfo", 16) + 19; const char* buildInfo = static_cast<char*>(memmem(dolBuf.get(), dolIt->size(), "MetroidBuildInfo", 16)) + 19;
/* Root Report */ /* Root Report */
ExtractReport& rep = reps.emplace_back(); ExtractReport& rep = reps.emplace_back();
rep.name = _SYS_STR("MP2"); rep.name = _SYS_STR("MP2");
rep.desc = _SYS_STR("Metroid Prime 2 ") + regstr; rep.desc = _SYS_STR("Metroid Prime 2 ") + regstr;
if (buildInfo) { if (buildInfo != nullptr) {
std::string buildStr(buildInfo); m_version = std::string(buildInfo);
hecl::SystemStringConv buildView(buildStr); hecl::SystemStringConv buildView(m_version);
rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")"); rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")");
} }
@ -247,6 +316,18 @@ struct SpecMP2 : SpecBase {
process.waitUntilComplete(); process.waitUntilComplete();
/* Generate Texture Cache containing meta data for every texture file */
hecl::ProjectPath noAramPath(m_project.getProjectWorkingPath(), _SYS_STR("MP2/URDE"));
TextureCache::Generate(m_pakRouter, m_project, noAramPath);
/* Write version data */
hecl::ProjectPath versionPath;
if (m_standalone) {
versionPath = hecl::ProjectPath(m_project.getProjectWorkingPath(), _SYS_STR("out/files"));
} else {
versionPath = hecl::ProjectPath(m_project.getProjectWorkingPath(), _SYS_STR("out/files/MP2"));
}
WriteVersionInfo(m_project, versionPath);
return true; return true;
} }
@ -286,7 +367,11 @@ struct SpecMP2 : SpecBase {
hecl::blender::Token& btok, FCookProgress progress) override {} hecl::blender::Token& btok, FCookProgress progress) override {}
void cookPathMesh(const hecl::ProjectPath& out, const hecl::ProjectPath& in, BlendStream& ds, bool fast, void cookPathMesh(const hecl::ProjectPath& out, const hecl::ProjectPath& in, BlendStream& ds, bool fast,
hecl::blender::Token& btok, FCookProgress progress) override {} hecl::blender::Token& btok, FCookProgress progress) override {
PathMesh mesh = ds.compilePathMesh();
ds.close();
DNAMP2::PATH::Cook(out, in, mesh, btok);
}
void cookActor(const hecl::ProjectPath& out, const hecl::ProjectPath& in, BlendStream& ds, bool fast, void cookActor(const hecl::ProjectPath& out, const hecl::ProjectPath& in, BlendStream& ds, bool fast,
hecl::blender::Token& btok, FCookProgress progress) override {} hecl::blender::Token& btok, FCookProgress progress) override {}

View File

@ -1,13 +1,15 @@
#include <utility> #include <utility>
#include <set> #include <set>
#include "SpecBase.hpp" #include "DataSpec/SpecBase.hpp"
#include "DNAMP3/DNAMP3.hpp" #include "DataSpec/DNAMP3/DNAMP3.hpp"
#include "DNAMP3/MLVL.hpp" #include "DataSpec/DNAMP3/MLVL.hpp"
#include "DNAMP3/STRG.hpp" #include "DataSpec/DNAMP3/STRG.hpp"
#include "DNAMP3/MAPA.hpp" #include "DataSpec/DNAMP3/MAPA.hpp"
#include "DNAMP2/STRG.hpp" #include "DataSpec/DNAMP2/STRG.hpp"
#include "DataSpec/DNACommon/TXTR.hpp"
#include "DataSpec/DNACommon/URDEVersionInfo.hpp"
#include "hecl/ClientProcess.hpp" #include "hecl/ClientProcess.hpp"
#include "hecl/Blender/Connection.hpp" #include "hecl/Blender/Connection.hpp"
@ -24,13 +26,72 @@ static logvisor::Module Log("urde::SpecMP3");
extern hecl::Database::DataSpecEntry SpecEntMP3; extern hecl::Database::DataSpecEntry SpecEntMP3;
extern hecl::Database::DataSpecEntry SpecEntMP3ORIG; extern hecl::Database::DataSpecEntry SpecEntMP3ORIG;
struct SpecMP3 : SpecBase { struct TextureCache {
bool checkStandaloneID(const char* id) const override { static void Generate(PAKRouter<DNAMP3::PAKBridge>& pakRouter, hecl::Database::Project& project, const hecl::ProjectPath& pakPath) {
if (!memcmp(id, "RM3", 3)) hecl::ProjectPath texturePath(pakPath, _SYS_STR("texture_cache.yaml"));
hecl::ProjectPath catalogPath(pakPath, _SYS_STR("!catalog.yaml"));
texturePath.makeDirChain(false);
if (const auto fp = hecl::FopenUnique(catalogPath.getAbsolutePath().data(), _SYS_STR("a"))) {
fmt::print(fp.get(), FMT_STRING("TextureCache: {}\n"), texturePath.getRelativePathUTF8());
}
Log.report(logvisor::Level::Info, FMT_STRING("Gathering Texture metadata (this can take up to 10 seconds)..."));
std::unordered_map<UniqueID64, TXTR::Meta> metaMap;
pakRouter.enumerateResources([&](const DNAMP3::PAK::Entry* ent) {
if (ent->type == FOURCC('TXTR') && metaMap.find(ent->id) == metaMap.end()) {
PAKEntryReadStream rs = pakRouter.beginReadStreamForId(ent->id);
metaMap[ent->id] = TXTR::GetMetaData(rs);
}
return true; return true;
return false; });
athena::io::YAMLDocWriter yamlW("MP3TextureCache");
for (const auto& pair : metaMap) {
hecl::ProjectPath path = pakRouter.getWorking(pair.first);
auto rec = yamlW.enterSubRecord(path.getRelativePathUTF8());
pair.second.write(yamlW);
}
athena::io::FileWriter fileW(texturePath.getAbsolutePath());
yamlW.finish(&fileW);
Log.report(logvisor::Level::Info, FMT_STRING("Done..."));
} }
static void Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath) {
hecl::Database::Project& project = inPath.getProject();
athena::io::YAMLDocReader r;
athena::io::FileReader fr(inPath.getAbsolutePath());
if (!fr.isOpen() || !r.parse(&fr))
return;
std::vector<std::pair<UniqueID32, TXTR::Meta>> metaPairs;
metaPairs.reserve(r.getRootNode()->m_mapChildren.size());
for (const auto& node : r.getRootNode()->m_mapChildren) {
hecl::ProjectPath projectPath(project, node.first);
auto rec = r.enterSubRecord(node.first.c_str());
TXTR::Meta meta;
meta.read(r);
metaPairs.emplace_back(projectPath.parsedHash32(), meta);
}
std::sort(metaPairs.begin(), metaPairs.end(), [](const auto& a, const auto& b) -> bool {
return a.first < b.first;
});
athena::io::FileWriter w(outPath.getAbsolutePath());
w.writeUint32Big(metaPairs.size());
for (const auto& pair : metaPairs) {
pair.first.write(w);
pair.second.write(w);
}
}
};
struct SpecMP3 : SpecBase {
bool checkStandaloneID(const char* id) const override { return memcmp(id, "RM3", 3) == 0; }
bool doMP3 = false; bool doMP3 = false;
std::vector<const nod::Node*> m_nonPaks; std::vector<const nod::Node*> m_nonPaks;
std::vector<DNAMP3::PAKBridge> m_paks; std::vector<DNAMP3::PAKBridge> m_paks;
@ -58,7 +119,8 @@ struct SpecMP3 : SpecBase {
, m_feWorkPath(project.getProjectWorkingPath(), _SYS_STR("fe")) , m_feWorkPath(project.getProjectWorkingPath(), _SYS_STR("fe"))
, m_feCookPath(project.getProjectCookedPath(SpecEntMP3), _SYS_STR("fe")) , m_feCookPath(project.getProjectCookedPath(SpecEntMP3), _SYS_STR("fe"))
, m_fePakRouter(*this, m_feWorkPath, m_feCookPath) { , m_fePakRouter(*this, m_feWorkPath, m_feCookPath) {
setThreadProject(); m_game = EGame::MetroidPrime3;
SpecBase::setThreadProject();
} }
void buildPaks(nod::Node& root, const std::vector<hecl::SystemString>& args, ExtractReport& rep, bool fe) { void buildPaks(nod::Node& root, const std::vector<hecl::SystemString>& args, ExtractReport& rep, bool fe) {
@ -159,20 +221,22 @@ struct SpecMP3 : SpecBase {
doMP3 = true; doMP3 = true;
nod::IPartition* partition = disc.getDataPartition(); nod::IPartition* partition = disc.getDataPartition();
std::unique_ptr<uint8_t[]> dolBuf = partition->getDOLBuf(); std::unique_ptr<uint8_t[]> dolBuf = partition->getDOLBuf();
const char* buildInfo = (char*)memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16) + 19; const char* buildInfo = static_cast<char*>(memmem(dolBuf.get(), partition->getDOLSize(), "MetroidBuildInfo", 16)) + 19;
if (!buildInfo) if (buildInfo == nullptr) {
return false; return false;
}
/* We don't want no stinking demo dammit */ /* We don't want no stinking demo dammit */
if (!strcmp(buildInfo, "Build v3.068 3/2/2006 14:55:13")) if (strcmp(buildInfo, "Build v3.068 3/2/2006 14:55:13") == 0) {
return false; return false;
}
m_version = std::string(buildInfo);
/* Root Report */ /* Root Report */
ExtractReport& rep = reps.emplace_back(); ExtractReport& rep = reps.emplace_back();
rep.name = _SYS_STR("MP3"); rep.name = _SYS_STR("MP3");
rep.desc = _SYS_STR("Metroid Prime 3 ") + regstr; rep.desc = _SYS_STR("Metroid Prime 3 ") + regstr;
std::string buildStr(buildInfo); hecl::SystemStringConv buildView(m_version);
hecl::SystemStringConv buildView(buildStr);
rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")"); rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")");
/* Iterate PAKs and build level options */ /* Iterate PAKs and build level options */
@ -253,8 +317,8 @@ struct SpecMP3 : SpecBase {
rep.name = _SYS_STR("MP3"); rep.name = _SYS_STR("MP3");
rep.desc = _SYS_STR("Metroid Prime 3 ") + regstr; rep.desc = _SYS_STR("Metroid Prime 3 ") + regstr;
std::string buildStr(buildInfo); m_version = std::string(buildInfo);
hecl::SystemStringConv buildView(buildStr); hecl::SystemStringConv buildView(m_version);
rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")"); rep.desc += _SYS_STR(" (") + buildView + _SYS_STR(")");
/* Iterate PAKs and build level options */ /* Iterate PAKs and build level options */
@ -410,6 +474,19 @@ struct SpecMP3 : SpecBase {
process.waitUntilComplete(); process.waitUntilComplete();
} }
/* Extract part of .dol for RandomStatic entropy */
hecl::ProjectPath noAramPath(m_project.getProjectWorkingPath(), _SYS_STR("MP3/URDE"));
/* Generate Texture Cache containing meta data for every texture file */
TextureCache::Generate(m_pakRouter, m_project, noAramPath);
/* Write version data */
hecl::ProjectPath versionPath;
if (m_standalone) {
versionPath = hecl::ProjectPath(m_project.getProjectWorkingPath(), _SYS_STR("out/files"));
} else {
versionPath = hecl::ProjectPath(m_project.getProjectWorkingPath(), _SYS_STR("out/files/MP3"));
}
WriteVersionInfo(m_project, versionPath);
return true; return true;
} }

View File

@ -48,7 +48,7 @@ ProjectManager::ProjectManager(ViewManager& vm)
bool ProjectManager::newProject(hecl::SystemStringView path) { bool ProjectManager::newProject(hecl::SystemStringView path) {
hecl::ProjectRootPath projPath = hecl::SearchForProject(path); hecl::ProjectRootPath projPath = hecl::SearchForProject(path);
if (projPath) { if (projPath) {
Log.report(logvisor::Warning, fmt(_SYS_STR("project already exists at '{}'")), path); Log.report(logvisor::Warning, FMT_STRING(_SYS_STR("project already exists at '{}'")), path);
return false; return false;
} }
@ -63,7 +63,7 @@ bool ProjectManager::newProject(hecl::SystemStringView path) {
m_vm.SetupEditorView(); m_vm.SetupEditorView();
saveProject(); saveProject();
m_vm.m_mainWindow->setTitle(fmt::format(fmt(_SYS_STR("{} - URDE [{}]")), m_vm.m_mainWindow->setTitle(fmt::format(FMT_STRING(_SYS_STR("{} - URDE [{}]")),
m_proj->getProjectRootPath().getLastComponent(), m_vm.platformName())); m_proj->getProjectRootPath().getLastComponent(), m_vm.platformName()));
m_vm.DismissSplash(); m_vm.DismissSplash();
m_vm.FadeInEditors(); m_vm.FadeInEditors();
@ -75,7 +75,7 @@ bool ProjectManager::openProject(hecl::SystemStringView path) {
hecl::SystemString subPath; hecl::SystemString subPath;
hecl::ProjectRootPath projPath = hecl::SearchForProject(path, subPath); hecl::ProjectRootPath projPath = hecl::SearchForProject(path, subPath);
if (!projPath) { if (!projPath) {
Log.report(logvisor::Warning, fmt(_SYS_STR("project doesn't exist at '{}'")), path); Log.report(logvisor::Warning, FMT_STRING(_SYS_STR("project doesn't exist at '{}'")), path);
return false; return false;
} }
@ -103,7 +103,7 @@ bool ProjectManager::openProject(hecl::SystemStringView path) {
if (needsSave) if (needsSave)
saveProject(); saveProject();
m_vm.m_mainWindow->setTitle(fmt::format(fmt(_SYS_STR("{} - URDE [{}]")), m_vm.m_mainWindow->setTitle(fmt::format(FMT_STRING(_SYS_STR("{} - URDE [{}]")),
m_proj->getProjectRootPath().getLastComponent(), m_vm.platformName())); m_proj->getProjectRootPath().getLastComponent(), m_vm.platformName()));
m_vm.DismissSplash(); m_vm.DismissSplash();
m_vm.FadeInEditors(); m_vm.FadeInEditors();

View File

@ -22,7 +22,7 @@ void ProjectResourceFactoryBase::BeginBackgroundIndex(hecl::Database::Project& p
} }
bool ProjectResourceFactoryBase::SyncCook(const hecl::ProjectPath& working) { bool ProjectResourceFactoryBase::SyncCook(const hecl::ProjectPath& working) {
Log.report(logvisor::Warning, fmt(_SYS_STR("sync-cooking {}")), working.getRelativePath()); Log.report(logvisor::Warning, FMT_STRING(_SYS_STR("sync-cooking {}")), working.getRelativePath());
return m_clientProc.syncCook(working, m_cookSpec.get(), hecl::blender::SharedBlenderToken, false, false); return m_clientProc.syncCook(working, m_cookSpec.get(), hecl::blender::SharedBlenderToken, false, false);
} }
@ -38,12 +38,12 @@ CFactoryFnReturn ProjectResourceFactoryBase::BuildSync(const SObjectTag& tag, co
u32 length = fr->length(); u32 length = fr->length();
std::unique_ptr<u8[]> memBuf = fr->readUBytes(length); std::unique_ptr<u8[]> memBuf = fr->readUBytes(length);
CFactoryFnReturn ret = m_factoryMgr.MakeObjectFromMemory(tag, std::move(memBuf), length, false, paramXfer, selfRef); CFactoryFnReturn ret = m_factoryMgr.MakeObjectFromMemory(tag, std::move(memBuf), length, false, paramXfer, selfRef);
Log.report(logvisor::Info, fmt("sync-built {}"), tag); Log.report(logvisor::Info, FMT_STRING("sync-built {}"), tag);
return ret; return ret;
} }
CFactoryFnReturn ret = m_factoryMgr.MakeObject(tag, *fr, paramXfer, selfRef); CFactoryFnReturn ret = m_factoryMgr.MakeObject(tag, *fr, paramXfer, selfRef);
Log.report(logvisor::Info, fmt("sync-built {}"), tag); Log.report(logvisor::Info, FMT_STRING("sync-built {}"), tag);
return ret; return ret;
} }
@ -53,7 +53,7 @@ void ProjectResourceFactoryBase::AsyncTask::EnsurePath(const urde::SObjectTag& t
/* Ensure requested resource is on the filesystem */ /* Ensure requested resource is on the filesystem */
if (!path.isFileOrGlob()) { if (!path.isFileOrGlob()) {
Log.report(logvisor::Error, fmt(_SYS_STR("unable to find resource path '{}'")), path.getRelativePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to find resource path '{}'")), path.getRelativePath());
m_failed = true; m_failed = true;
return; return;
} }
@ -66,7 +66,7 @@ void ProjectResourceFactoryBase::AsyncTask::EnsurePath(const urde::SObjectTag& t
/* Last chance type validation */ /* Last chance type validation */
urde::SObjectTag verifyTag = m_parent.TagFromPath(path); urde::SObjectTag verifyTag = m_parent.TagFromPath(path);
if (verifyTag.type != tag.type) { if (verifyTag.type != tag.type) {
Log.report(logvisor::Error, fmt(_SYS_STR("{}: expected type '{}', found '{}'")), path.getRelativePath(), Log.report(logvisor::Error, FMT_STRING(_SYS_STR("{}: expected type '{}', found '{}'")), path.getRelativePath(),
tag.type, verifyTag.type); tag.type, verifyTag.type);
m_failed = true; m_failed = true;
return; return;
@ -91,7 +91,7 @@ void ProjectResourceFactoryBase::AsyncTask::CookComplete() {
/* Ensure cooked rep is on the filesystem */ /* Ensure cooked rep is on the filesystem */
athena::io::FileReader fr(m_cookedPath.getAbsolutePath(), 32 * 1024, false); athena::io::FileReader fr(m_cookedPath.getAbsolutePath(), 32 * 1024, false);
if (fr.hasError()) { if (fr.hasError()) {
Log.report(logvisor::Error, fmt(_SYS_STR("unable to open cooked resource path '{}'")), m_cookedPath.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open cooked resource path '{}'")), m_cookedPath.getAbsolutePath());
m_failed = true; m_failed = true;
return; return;
} }
@ -171,7 +171,7 @@ bool ProjectResourceFactoryBase::PrepForReadSync(const SObjectTag& tag, const he
std::optional<athena::io::FileReader>& fr) { std::optional<athena::io::FileReader>& fr) {
/* Ensure requested resource is on the filesystem */ /* Ensure requested resource is on the filesystem */
if (!path.isFileOrGlob()) { if (!path.isFileOrGlob()) {
Log.report(logvisor::Error, fmt(_SYS_STR("unable to find resource path '{}'")), path.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to find resource path '{}'")), path.getAbsolutePath());
return false; return false;
} }
@ -183,7 +183,7 @@ bool ProjectResourceFactoryBase::PrepForReadSync(const SObjectTag& tag, const he
/* Last chance type validation */ /* Last chance type validation */
urde::SObjectTag verifyTag = TagFromPath(path); urde::SObjectTag verifyTag = TagFromPath(path);
if (verifyTag.type != tag.type) { if (verifyTag.type != tag.type) {
Log.report(logvisor::Error, fmt(_SYS_STR("{}: expected type '{}', found '{}'")), path.getRelativePath(), Log.report(logvisor::Error, FMT_STRING(_SYS_STR("{}: expected type '{}', found '{}'")), path.getRelativePath(),
tag.type, verifyTag.type); tag.type, verifyTag.type);
return false; return false;
} }
@ -195,7 +195,7 @@ bool ProjectResourceFactoryBase::PrepForReadSync(const SObjectTag& tag, const he
if (!cooked.isFile() || cooked.getModtime() < path.getModtime()) { if (!cooked.isFile() || cooked.getModtime() < path.getModtime()) {
/* Do a blocking cook here */ /* Do a blocking cook here */
if (!SyncCook(path)) { if (!SyncCook(path)) {
Log.report(logvisor::Error, fmt(_SYS_STR("unable to cook resource path '{}'")), path.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to cook resource path '{}'")), path.getAbsolutePath());
return false; return false;
} }
} }
@ -204,7 +204,7 @@ bool ProjectResourceFactoryBase::PrepForReadSync(const SObjectTag& tag, const he
/* Ensure cooked rep is on the filesystem */ /* Ensure cooked rep is on the filesystem */
fr.emplace(cooked.getAbsolutePath(), 32 * 1024, false); fr.emplace(cooked.getAbsolutePath(), 32 * 1024, false);
if (fr->hasError()) { if (fr->hasError()) {
Log.report(logvisor::Error, fmt(_SYS_STR("unable to open cooked resource path '{}'")), cooked.getAbsolutePath()); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open cooked resource path '{}'")), cooked.getAbsolutePath());
return false; return false;
} }
@ -215,7 +215,7 @@ std::unique_ptr<urde::IObj> ProjectResourceFactoryBase::Build(const urde::SObjec
const urde::CVParamTransfer& paramXfer, const urde::CVParamTransfer& paramXfer,
CObjectReference* selfRef) { CObjectReference* selfRef) {
if (!tag.id.IsValid()) if (!tag.id.IsValid())
Log.report(logvisor::Fatal, fmt("attempted to access null id on type '{}'"), tag.type); Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id on type '{}'"), tag.type);
const hecl::ProjectPath* resPath = nullptr; const hecl::ProjectPath* resPath = nullptr;
if (!WaitForTagReady(tag, resPath)) if (!WaitForTagReady(tag, resPath))
@ -243,14 +243,14 @@ std::unique_ptr<urde::IObj> ProjectResourceFactoryBase::Build(const urde::SObjec
} }
//*task.xc_targetObjPtr = newObj.get(); //*task.xc_targetObjPtr = newObj.get();
Log.report(logvisor::Warning, fmt("spin-built {}"), task.x0_tag); Log.report(logvisor::Warning, FMT_STRING("spin-built {}"), task.x0_tag);
_RemoveTask(asyncSearch); _RemoveTask(asyncSearch);
return newObj; return newObj;
} else if (task.m_complete) { } else if (task.m_complete) {
Log.report(logvisor::Error, fmt("unable to spin-build {}; Resource requested as cook-only"), task.x0_tag); Log.report(logvisor::Error, FMT_STRING("unable to spin-build {}; Resource requested as cook-only"), task.x0_tag);
} else { } else {
Log.report(logvisor::Error, fmt("unable to spin-build {}"), task.x0_tag); Log.report(logvisor::Error, FMT_STRING("unable to spin-build {}"), task.x0_tag);
} }
_RemoveTask(asyncSearch); _RemoveTask(asyncSearch);
@ -273,14 +273,14 @@ std::shared_ptr<AsyncTask> ProjectResourceFactoryBase::BuildAsyncInternal(const
void ProjectResourceFactoryBase::BuildAsync(const urde::SObjectTag& tag, const urde::CVParamTransfer& paramXfer, void ProjectResourceFactoryBase::BuildAsync(const urde::SObjectTag& tag, const urde::CVParamTransfer& paramXfer,
std::unique_ptr<urde::IObj>* objOut, CObjectReference* selfRef) { std::unique_ptr<urde::IObj>* objOut, CObjectReference* selfRef) {
if (!tag.id.IsValid()) if (!tag.id.IsValid())
Log.report(logvisor::Fatal, fmt("attempted to access null id on type '{}'"), tag.type); Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id on type '{}'"), tag.type);
BuildAsyncInternal(tag, paramXfer, objOut, selfRef); BuildAsyncInternal(tag, paramXfer, objOut, selfRef);
} }
u32 ProjectResourceFactoryBase::ResourceSize(const SObjectTag& tag) { u32 ProjectResourceFactoryBase::ResourceSize(const SObjectTag& tag) {
if (!tag.id.IsValid()) if (!tag.id.IsValid())
Log.report(logvisor::Fatal, fmt("attempted to access null id on type '{}'"), tag.type); Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id on type '{}'"), tag.type);
/* Ensure resource at requested path is indexed and not cooking */ /* Ensure resource at requested path is indexed and not cooking */
const hecl::ProjectPath* resPath = nullptr; const hecl::ProjectPath* resPath = nullptr;
@ -298,7 +298,7 @@ u32 ProjectResourceFactoryBase::ResourceSize(const SObjectTag& tag) {
std::shared_ptr<urde::IDvdRequest> ProjectResourceFactoryBase::LoadResourceAsync(const urde::SObjectTag& tag, std::shared_ptr<urde::IDvdRequest> ProjectResourceFactoryBase::LoadResourceAsync(const urde::SObjectTag& tag,
void* target) { void* target) {
if (!tag.id.IsValid()) if (!tag.id.IsValid())
Log.report(logvisor::Fatal, fmt("attempted to access null id")); Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end()) if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end())
return {}; return {};
return std::static_pointer_cast<urde::IDvdRequest>( return std::static_pointer_cast<urde::IDvdRequest>(
@ -308,7 +308,7 @@ std::shared_ptr<urde::IDvdRequest> ProjectResourceFactoryBase::LoadResourceAsync
std::shared_ptr<urde::IDvdRequest> ProjectResourceFactoryBase::LoadResourcePartAsync(const urde::SObjectTag& tag, std::shared_ptr<urde::IDvdRequest> ProjectResourceFactoryBase::LoadResourcePartAsync(const urde::SObjectTag& tag,
u32 off, u32 size, void* target) { u32 off, u32 size, void* target) {
if (!tag.id.IsValid()) if (!tag.id.IsValid())
Log.report(logvisor::Fatal, fmt("attempted to access null id")); Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end()) if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end())
return {}; return {};
return std::static_pointer_cast<urde::IDvdRequest>( return std::static_pointer_cast<urde::IDvdRequest>(
@ -317,7 +317,7 @@ std::shared_ptr<urde::IDvdRequest> ProjectResourceFactoryBase::LoadResourcePartA
std::unique_ptr<u8[]> ProjectResourceFactoryBase::LoadResourceSync(const urde::SObjectTag& tag) { std::unique_ptr<u8[]> ProjectResourceFactoryBase::LoadResourceSync(const urde::SObjectTag& tag) {
if (!tag.id.IsValid()) if (!tag.id.IsValid())
Log.report(logvisor::Fatal, fmt("attempted to access null id")); Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
/* Ensure resource at requested path is indexed and not cooking */ /* Ensure resource at requested path is indexed and not cooking */
const hecl::ProjectPath* resPath = nullptr; const hecl::ProjectPath* resPath = nullptr;
@ -335,7 +335,7 @@ std::unique_ptr<u8[]> ProjectResourceFactoryBase::LoadResourceSync(const urde::S
std::unique_ptr<u8[]> ProjectResourceFactoryBase::LoadNewResourcePartSync(const urde::SObjectTag& tag, u32 off, std::unique_ptr<u8[]> ProjectResourceFactoryBase::LoadNewResourcePartSync(const urde::SObjectTag& tag, u32 off,
u32 size) { u32 size) {
if (!tag.id.IsValid()) if (!tag.id.IsValid())
Log.report(logvisor::Fatal, fmt("attempted to access null id")); Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
/* Ensure resource at requested path is indexed and not cooking */ /* Ensure resource at requested path is indexed and not cooking */
const hecl::ProjectPath* resPath = nullptr; const hecl::ProjectPath* resPath = nullptr;
@ -354,7 +354,7 @@ std::unique_ptr<u8[]> ProjectResourceFactoryBase::LoadNewResourcePartSync(const
std::shared_ptr<AsyncTask> ProjectResourceFactoryBase::CookResourceAsync(const urde::SObjectTag& tag) { std::shared_ptr<AsyncTask> ProjectResourceFactoryBase::CookResourceAsync(const urde::SObjectTag& tag) {
if (!tag.id.IsValid()) if (!tag.id.IsValid())
Log.report(logvisor::Fatal, fmt("attempted to access null id")); Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end()) if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end())
return {}; return {};
return _AddTask(std::make_shared<AsyncTask>(*this, tag)); return _AddTask(std::make_shared<AsyncTask>(*this, tag));
@ -364,7 +364,7 @@ void ProjectResourceFactoryBase::CancelBuild(const urde::SObjectTag& tag) { _Rem
bool ProjectResourceFactoryBase::CanBuild(const urde::SObjectTag& tag) { bool ProjectResourceFactoryBase::CanBuild(const urde::SObjectTag& tag) {
if (!tag.id.IsValid()) if (!tag.id.IsValid())
Log.report(logvisor::Fatal, fmt("attempted to access null id")); Log.report(logvisor::Fatal, FMT_STRING("attempted to access null id"));
const hecl::ProjectPath* resPath = nullptr; const hecl::ProjectPath* resPath = nullptr;
if (!WaitForTagReady(tag, resPath)) if (!WaitForTagReady(tag, resPath))
@ -400,7 +400,7 @@ bool ProjectResourceFactoryBase::AsyncPumpTask(ItType& it) {
hecl::ProjectPath path = static_cast<DataSpec::SpecBase&>(*m_cookSpec).pathFromTag(task.x0_tag); hecl::ProjectPath path = static_cast<DataSpec::SpecBase&>(*m_cookSpec).pathFromTag(task.x0_tag);
if (!path) { if (!path) {
if (!static_cast<DataSpec::SpecBase&>(*m_cookSpec).backgroundIndexRunning()) { if (!static_cast<DataSpec::SpecBase&>(*m_cookSpec).backgroundIndexRunning()) {
Log.report(logvisor::Error, fmt(_SYS_STR("unable to find async load resource ({})")), task.x0_tag); Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to find async load resource ({})")), task.x0_tag);
it = _RemoveTask(it); it = _RemoveTask(it);
} }
return true; return true;
@ -423,14 +423,14 @@ bool ProjectResourceFactoryBase::AsyncPumpTask(ItType& it) {
} }
*task.xc_targetObjPtr = std::move(newObj); *task.xc_targetObjPtr = std::move(newObj);
Log.report(logvisor::Info, fmt("async-built {}"), task.x0_tag); Log.report(logvisor::Info, FMT_STRING("async-built {}"), task.x0_tag);
} else if (task.xc_targetDataPtr) { } else if (task.xc_targetDataPtr) {
/* Buffer only */ /* Buffer only */
*task.xc_targetDataPtr = std::move(task.x10_loadBuffer); *task.xc_targetDataPtr = std::move(task.x10_loadBuffer);
Log.report(logvisor::Info, fmt("async-loaded {}"), task.x0_tag); Log.report(logvisor::Info, FMT_STRING("async-loaded {}"), task.x0_tag);
} else if (task.xc_targetDataRawPtr) { } else if (task.xc_targetDataRawPtr) {
/* Buffer only raw */ /* Buffer only raw */
Log.report(logvisor::Info, fmt("async-loaded {}"), task.x0_tag); Log.report(logvisor::Info, FMT_STRING("async-loaded {}"), task.x0_tag);
} }
} }

View File

@ -100,7 +100,7 @@ specter::View* SplitSpace::buildContentView(specter::ViewResources& res) {
void SplitSpace::setChildSlot(unsigned slot, std::unique_ptr<Space>&& space) { void SplitSpace::setChildSlot(unsigned slot, std::unique_ptr<Space>&& space) {
if (slot > 1) if (slot > 1)
Log.report(logvisor::Fatal, fmt("invalid slot {} for SplitView"), slot); Log.report(logvisor::Fatal, FMT_STRING("invalid slot {} for SplitView"), slot);
m_slots[slot] = std::move(space); m_slots[slot] = std::move(space);
m_slots[slot]->m_parent = this; m_slots[slot]->m_parent = this;
} }
@ -162,7 +162,7 @@ std::unique_ptr<Space> RootSpace::exchangeSpaceSplitJoin(Space* removeSpace, std
m_spaceTree.swap(ret); m_spaceTree.swap(ret);
m_spaceTree->m_parent = this; m_spaceTree->m_parent = this;
} else } else
Log.report(logvisor::Fatal, fmt("RootSpace::exchangeSpaceSplitJoin() failure")); Log.report(logvisor::Fatal, FMT_STRING("RootSpace::exchangeSpaceSplitJoin() failure"));
return ret; return ret;
} }
@ -177,7 +177,7 @@ std::unique_ptr<Space> SplitSpace::exchangeSpaceSplitJoin(Space* removeSpace, st
m_slots[1].swap(ret); m_slots[1].swap(ret);
m_slots[1]->m_parent = this; m_slots[1]->m_parent = this;
} else } else
Log.report(logvisor::Fatal, fmt("SplitSpace::exchangeSpaceSplitJoin() failure")); Log.report(logvisor::Fatal, FMT_STRING("SplitSpace::exchangeSpaceSplitJoin() failure"));
return ret; return ret;
} }

View File

@ -36,12 +36,12 @@ SplashScreen::SplashScreen(ViewManager& vm, specter::ViewResources& res)
if (GIT_COMMIT_DATE[0] != '\0' && GIT_COMMIT_HASH[0] != '\0' && GIT_BRANCH[0] != '\0') { if (GIT_COMMIT_DATE[0] != '\0' && GIT_COMMIT_HASH[0] != '\0' && GIT_BRANCH[0] != '\0') {
#ifdef URDE_DLPACKAGE #ifdef URDE_DLPACKAGE
if ((URDE_DLPACKAGE)[0]) if ((URDE_DLPACKAGE)[0])
m_buildInfoStr = fmt::format(fmt("{}: {}\n{}: {}\n{}: {}"), vm.translate<locale::release>(), m_buildInfoStr = fmt::format(FMT_STRING("{}: {}\n{}: {}\n{}: {}"), vm.translate<locale::release>(),
URDE_DLPACKAGE, vm.translate<locale::commit>(), GIT_COMMIT_HASH, URDE_DLPACKAGE, vm.translate<locale::commit>(), GIT_COMMIT_HASH,
vm.translate<locale::date>(), GIT_COMMIT_DATE); vm.translate<locale::date>(), GIT_COMMIT_DATE);
else else
#endif #endif
m_buildInfoStr = fmt::format(fmt("{}: {}\n{}: {}\n{}: {}"), vm.translate<locale::branch>(), GIT_BRANCH, m_buildInfoStr = fmt::format(FMT_STRING("{}: {}\n{}: {}\n{}: {}"), vm.translate<locale::branch>(), GIT_BRANCH,
vm.translate<locale::commit>(), GIT_COMMIT_HASH, vm.translate<locale::commit>(), GIT_COMMIT_HASH,
vm.translate<locale::date>(), GIT_COMMIT_DATE); vm.translate<locale::date>(), GIT_COMMIT_DATE);
} }
@ -65,15 +65,15 @@ void SplashScreen::think() {
m_openButt.m_view->think(); m_openButt.m_view->think();
if (m_newProjBind.m_deferPath.size()) { if (m_newProjBind.m_deferPath.size()) {
Log.report(logvisor::Info, fmt(_SYS_STR("Making project '{}'")), m_newProjBind.m_deferPath); Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Making project '{}'")), m_newProjBind.m_deferPath);
m_vm.projectManager().newProject(m_newProjBind.m_deferPath); m_vm.projectManager().newProject(m_newProjBind.m_deferPath);
m_newProjBind.m_deferPath.clear(); m_newProjBind.m_deferPath.clear();
} else if (m_openProjBind.m_deferPath.size()) { } else if (m_openProjBind.m_deferPath.size()) {
Log.report(logvisor::Info, fmt(_SYS_STR("Opening project '{}'")), m_openProjBind.m_deferPath); Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Opening project '{}'")), m_openProjBind.m_deferPath);
m_vm.projectManager().openProject(m_openProjBind.m_deferPath); m_vm.projectManager().openProject(m_openProjBind.m_deferPath);
m_openProjBind.m_deferPath.clear(); m_openProjBind.m_deferPath.clear();
} else if (m_extractProjBind.m_deferPath.size()) { } else if (m_extractProjBind.m_deferPath.size()) {
Log.report(logvisor::Info, fmt(_SYS_STR("Extracting game '{}'")), m_extractProjBind.m_deferPath); Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Extracting game '{}'")), m_extractProjBind.m_deferPath);
m_vm.projectManager().extractGame(m_extractProjBind.m_deferPath); m_vm.projectManager().extractGame(m_extractProjBind.m_deferPath);
m_extractProjBind.m_deferPath.clear(); m_extractProjBind.m_deferPath.clear();
} }

Some files were not shown because too many files have changed in this diff Show More