The Great Removal

dawn-optick
Luke Street 1 year ago
parent 7186f5d4e5
commit 13b1ebb12e
  1. 15
      .gitmodules
  2. 98
      CMakeLists.txt
  3. 3
      DataSpec/AssetMap32Download.cmake
  4. 3
      DataSpec/AssetMap64Download.cmake
  5. 95
      DataSpec/AssetNameMap.cpp
  6. 11
      DataSpec/AssetNameMap.hpp
  7. 9
      DataSpec/AssetNameMapNull.cpp
  8. 22
      DataSpec/Blender/BlenderSupport.cpp
  9. 9
      DataSpec/Blender/BlenderSupport.hpp
  10. 1876
      DataSpec/Blender/RetroMasterShader.py
  11. 91
      DataSpec/CMakeLists.txt
  12. 282
      DataSpec/DNACommon/ANCS.cpp
  13. 47
      DataSpec/DNACommon/ANCS.hpp
  14. 462
      DataSpec/DNACommon/ANIM.cpp
  15. 73
      DataSpec/DNACommon/ANIM.hpp
  16. 440
      DataSpec/DNACommon/AROTBuilder.cpp
  17. 58
      DataSpec/DNACommon/AROTBuilder.hpp
  18. 66
      DataSpec/DNACommon/ATBL.cpp
  19. 19
      DataSpec/DNACommon/ATBL.hpp
  20. 161
      DataSpec/DNACommon/BabeDead.cpp
  21. 16
      DataSpec/DNACommon/BabeDead.hpp
  22. 2273
      DataSpec/DNACommon/CMDL.cpp
  23. 168
      DataSpec/DNACommon/CMDL.hpp
  24. 60
      DataSpec/DNACommon/CMakeLists.txt
  25. 50
      DataSpec/DNACommon/CRSC.cpp
  26. 222
      DataSpec/DNACommon/CRSC.def
  27. 57
      DataSpec/DNACommon/CRSC.hpp
  28. 40
      DataSpec/DNACommon/DGRP.cpp
  29. 45
      DataSpec/DNACommon/DGRP.hpp
  30. 199
      DataSpec/DNACommon/DNACommon.cpp
  31. 394
      DataSpec/DNACommon/DNACommon.hpp
  32. 51
      DataSpec/DNACommon/DPSC.cpp
  33. 28
      DataSpec/DNACommon/DPSC.def
  34. 76
      DataSpec/DNACommon/DPSC.hpp
  35. 258
      DataSpec/DNACommon/DeafBabe.cpp
  36. 20
      DataSpec/DNACommon/DeafBabe.hpp
  37. 18
      DataSpec/DNACommon/EGMC.hpp
  38. 51
      DataSpec/DNACommon/ELSC.cpp
  39. 56
      DataSpec/DNACommon/ELSC.def
  40. 56
      DataSpec/DNACommon/ELSC.hpp
  41. 235
      DataSpec/DNACommon/FONT.cpp
  42. 127
      DataSpec/DNACommon/FONT.hpp
  43. 82
      DataSpec/DNACommon/FSM2.cpp
  44. 147
      DataSpec/DNACommon/FSM2.hpp
  45. 18
      DataSpec/DNACommon/GX.cpp
  46. 298
      DataSpec/DNACommon/GX.hpp
  47. 426
      DataSpec/DNACommon/MAPA.cpp
  48. 177
      DataSpec/DNACommon/MAPA.hpp
  49. 146
      DataSpec/DNACommon/MAPU.cpp
  50. 45
      DataSpec/DNACommon/MAPU.hpp
  51. 109
      DataSpec/DNACommon/MLVL.cpp
  52. 22
      DataSpec/DNACommon/MLVL.hpp
  53. 26
      DataSpec/DNACommon/MayaSpline.hpp
  54. 24
      DataSpec/DNACommon/MetaforceVersionInfo.hpp
  55. 258
      DataSpec/DNACommon/OBBTreeBuilder.cpp
  56. 15
      DataSpec/DNACommon/OBBTreeBuilder.hpp
  57. 679
      DataSpec/DNACommon/PAK.cpp
  58. 234
      DataSpec/DNACommon/PAK.hpp
  59. 51
      DataSpec/DNACommon/PART.cpp
  60. 141
      DataSpec/DNACommon/PART.def
  61. 64
      DataSpec/DNACommon/PART.hpp
  62. 249
      DataSpec/DNACommon/PATH.cpp
  63. 112
      DataSpec/DNACommon/PATH.hpp
  64. 586
      DataSpec/DNACommon/ParticleCommon.cpp
  65. 1553
      DataSpec/DNACommon/ParticleCommon.hpp
  66. 145
      DataSpec/DNACommon/RigInverter.cpp
  67. 44
      DataSpec/DNACommon/RigInverter.hpp
  68. 39
      DataSpec/DNACommon/SAVWCommon.hpp
  69. 43
      DataSpec/DNACommon/STRG.cpp
  70. 30
      DataSpec/DNACommon/STRG.hpp
  71. 51
      DataSpec/DNACommon/SWHC.cpp
  72. 69
      DataSpec/DNACommon/SWHC.def
  73. 59
      DataSpec/DNACommon/SWHC.hpp
  74. 1702
      DataSpec/DNACommon/TXTR.cpp
  75. 38
      DataSpec/DNACommon/TXTR.hpp
  76. 7
      DataSpec/DNACommon/Tweaks/ITweak.hpp
  77. 8
      DataSpec/DNACommon/Tweaks/ITweakAutoMapper.hpp
  78. 69
      DataSpec/DNACommon/Tweaks/ITweakBall.hpp
  79. 8
      DataSpec/DNACommon/Tweaks/ITweakGame.hpp
  80. 13
      DataSpec/DNACommon/Tweaks/ITweakGui.hpp
  81. 102
      DataSpec/DNACommon/Tweaks/ITweakGuiColors.hpp
  82. 142
      DataSpec/DNACommon/Tweaks/ITweakGunRes.hpp
  83. 9
      DataSpec/DNACommon/Tweaks/ITweakParticle.hpp
  84. 10
      DataSpec/DNACommon/Tweaks/ITweakPlayer.hpp
  85. 9
      DataSpec/DNACommon/Tweaks/ITweakPlayerControl.hpp
  86. 47
      DataSpec/DNACommon/Tweaks/ITweakPlayerGun.hpp
  87. 176
      DataSpec/DNACommon/Tweaks/ITweakPlayerRes.hpp
  88. 15
      DataSpec/DNACommon/Tweaks/ITweakSlideShow.hpp
  89. 95
      DataSpec/DNACommon/Tweaks/ITweakTargeting.hpp
  90. 32
      DataSpec/DNACommon/Tweaks/TweakWriter.hpp
  91. 51
      DataSpec/DNACommon/WPSC.cpp
  92. 87
      DataSpec/DNACommon/WPSC.def
  93. 60
      DataSpec/DNACommon/WPSC.hpp
  94. 104
      DataSpec/DNAMP1/AFSM.cpp
  95. 51
      DataSpec/DNAMP1/AFSM.hpp
  96. 266
      DataSpec/DNAMP1/AGSC.cpp
  97. 19
      DataSpec/DNAMP1/AGSC.hpp
  98. 1442
      DataSpec/DNAMP1/ANCS.cpp
  99. 420
      DataSpec/DNAMP1/ANCS.hpp
  100. 624
      DataSpec/DNAMP1/ANIM.cpp
  101. Some files were not shown because too many files have changed in this diff Show More

15
.gitmodules vendored

@ -14,14 +14,6 @@
path = extern/jbus
url = ../jbus.git
branch = master
[submodule "extern/tinyxml2"]
path = extern/tinyxml2
url = ../tinyxml2.git
branch = master
[submodule "extern/sanitizers-cmake"]
path = extern/sanitizers-cmake
url = https://github.com/arsenm/sanitizers-cmake.git
branch = master
[submodule "extern/discord-rpc"]
path = extern/discord-rpc
url = https://github.com/discordapp/discord-rpc.git
@ -34,10 +26,6 @@
path = extern/fixNES
url = https://github.com/FIX94/fixNES.git
branch = master
[submodule "extern/libSquish"]
path = extern/libSquish
url = ../libSquish.git
branch = master
[submodule "extern/athena"]
path = extern/athena
url = ../../libAthena/athena.git
@ -66,3 +54,6 @@
[submodule "extern/nativefiledialog"]
path = extern/nativefiledialog
url = https://github.com/mlabbe/nativefiledialog.git
[submodule "extern/optick"]
path = extern/optick
url = https://github.com/AxioDL/optick.git

@ -92,15 +92,6 @@ if(APPLE AND NOT CMAKE_OSX_SYSROOT)
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
option(METAFORCE_CROSSCOMPILING "Don't build tools; attempt package import" OFF)
if (METAFORCE_CROSSCOMPILING)
set(CMAKE_CROSSCOMPILING On)
endif()
if(CMAKE_CROSSCOMPILING)
set(HAVE_WORDS_BIGENDIAN_EXITCODE 0 CACHE INTEGER "Makes soxr happy" FORCE)
endif()
# MSVC has a "latest" flag, which always uses the newest standard
# when available. GCC and Clang posess no such flag, and must be
# manually enforced. CMake, curiously, also doesn't have a "latest"
@ -346,45 +337,15 @@ if (NOT WIN32)
set(ZLIB_LIBRARIES ZLIB::ZLIB CACHE STRING "zlib libraries" FORCE)
endif()
# TODO migrate bintoc
include(hecl/ApplicationTools.cmake)
include(ExternalProject)
ExternalProject_Add(bintoc
SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/bintoc"
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --config Release --target install)
include(${CMAKE_CURRENT_LIST_DIR}/bintoc/bintocHelpers.cmake)
add_subdirectory(extern)
set(DATA_SPEC_LIBS RetroDataSpec AssetNameMap)
set(HECL_DATASPEC_DECLS
"/* RetroCommon specs */
namespace DataSpec
{
extern hecl::Database::DataSpecEntry SpecEntMP1;
extern hecl::Database::DataSpecEntry SpecEntMP1PC;
extern hecl::Database::DataSpecEntry SpecEntMP1ORIG;
extern hecl::Database::DataSpecEntry SpecEntMP2;
extern hecl::Database::DataSpecEntry SpecEntMP2PC;
extern hecl::Database::DataSpecEntry SpecEntMP2ORIG;
extern hecl::Database::DataSpecEntry SpecEntMP3;
extern hecl::Database::DataSpecEntry SpecEntMP3PC;
extern hecl::Database::DataSpecEntry SpecEntMP3ORIG;
}")
set(HECL_DATASPEC_PUSHES
" /* RetroCommon */
hecl::Database::DATA_SPEC_REGISTRY.reserve(9);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP1);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP1PC);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP1ORIG);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP2);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP2PC);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP2ORIG);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP3);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP3PC);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP3ORIG);")
add_subdirectory(imgui)
add_subdirectory(hecl EXCLUDE_FROM_ALL)
target_include_directories(hecl-full PRIVATE ${CMAKE_SOURCE_DIR})
target_include_directories(hecl-light PRIVATE ${CMAKE_SOURCE_DIR})
target_link_libraries(hecl-full PRIVATE zeus nod)
target_link_libraries(hecl-light PRIVATE zeus nod)
if(NOT TARGET atdna)
# Import native atdna if cross-compiling
@ -394,65 +355,20 @@ if(NOT TARGET atdna)
endif()
endif()
if (NOT CMAKE_CROSSCOMPILING)
add_subdirectory(assetnameparser EXCLUDE_FROM_ALL)
endif ()
add_compile_definitions(URDE_ZIP_INPUT_STREAM=1) # Enable CZipInputStream now that zlib header is known
add_subdirectory(DataSpec EXCLUDE_FROM_ALL)
add_subdirectory(NESEmulator EXCLUDE_FROM_ALL)
add_subdirectory(aurora)
add_subdirectory(Runtime)
add_subdirectory(mpcksum EXCLUDE_FROM_ALL)
add_subdirectory(gbalink EXCLUDE_FROM_ALL)
if (NOT WINDOWS_STORE AND NOT NX)
if (APPLE AND EXISTS /opt/local/libexec/qt5)
# macports qt5 (build with +universal)
set(Qt5Widgets_DIR /opt/local/libexec/qt5)
elseif (APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64)
set(QT_HOMEBREW_PATH /usr/local/opt/qt)
elseif (APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL arm64)
set(QT_HOMEBREW_PATH /opt/homebrew/opt/qt)
else ()
set(QT_HOMEBREW_PATH "")
endif ()
find_package(Qt6Widgets QUIET PATHS ${QT_HOMEBREW_PATH})
find_package(Qt5Widgets QUIET PATHS ${QT_HOMEBREW_PATH})
if (Qt6Widgets_FOUND)
message(STATUS "Qt6 found, metaforce-gui will be built")
add_subdirectory(metaforce-gui EXCLUDE_FROM_ALL)
elseif(Qt5Widgets_FOUND)
message(STATUS "Qt5 found, metaforce-gui will be built")
add_subdirectory(metaforce-gui EXCLUDE_FROM_ALL)
else()
message(STATUS "Qt5-6 not found, metaforce-gui will not be built")
endif()
endif()
configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_BINARY_DIR}/version.h)
# Packaging logic
list(APPEND BINARY_TARGETS metaforce) # hecl visigen
list(APPEND BINARY_TARGETS metaforce)
set(DSYM_ONLY_TARGETS "")
if (TARGET crashpad_handler)
list(APPEND BINARY_TARGETS crashpad_handler)
endif ()
set(BIN_PREFIX "${CMAKE_INSTALL_PREFIX}")
#if (TARGET metaforce-gui)
# if (APPLE)
# # app bundle already has all needed binaries
# install(TARGETS metaforce-gui DESTINATION ${BIN_PREFIX})
# list(APPEND DSYM_ONLY_TARGETS metaforce-gui)
# # we have to rename here, cmake is inflexible about bundle naming
# install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND rm -fr Metaforce.app)")
# install(CODE "execute_process(WORKING_DIRECTORY \"${BIN_PREFIX}\" COMMAND mv metaforce-gui.app Metaforce.app)")
# set(BIN_PREFIX "${BIN_PREFIX}/Metaforce.app/Contents/MacOS")
# else()
# list(APPEND BINARY_TARGETS metaforce-gui)
# endif ()
#endif ()
install(TARGETS ${BINARY_TARGETS} DESTINATION ${BIN_PREFIX})
if (CMAKE_BUILD_TYPE STREQUAL Debug OR CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo)
foreach (target IN LISTS BINARY_TARGETS DSYM_ONLY_TARGETS)

@ -1,3 +0,0 @@
message(STATUS "32-bit asset name map not found; downloading to '${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap32.bin'")
file(DOWNLOAD "https://axiodl.com/files/AssetNameMap32.dat"
${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap32.bin SHOW_PROGRESS EXPECTED_HASH SHA1=90b4e941c192eef41c81e60314f348bc787d1336)

@ -1,3 +0,0 @@
message(STATUS "64-bit asset name map not found; downloading to '${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap64.bin'")
file(DOWNLOAD "https://axiodl.com/files/AssetNameMap64.dat"
${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap64.bin SHOW_PROGRESS EXPECTED_HASH SHA1=e49c03c9fff66adccec7af8120dda091636513e2)

@ -1,95 +0,0 @@
#include "AssetNameMap.hpp"
#include "athena/Compression.hpp"
#include "athena/MemoryReader.hpp"
extern "C" const uint8_t ASSET_NAME_MP32[];
extern "C" const size_t ASSET_NAME_MP32_SZ;
extern "C" const size_t ASSET_NAME_MP32_DECOMPRESSED_SZ;
extern "C" const uint8_t ASSET_NAME_MP64[];
extern "C" const size_t ASSET_NAME_MP64_SZ;
extern "C" const size_t ASSET_NAME_MP64_DECOMPRESSED_SZ;
namespace DataSpec::AssetNameMap {
logvisor::Module Log("AssetNameMap");
struct SAsset {
std::string name;
std::string directory;
hecl::FourCC type;
SAsset() = default;
SAsset(const hecl::FourCC& typeIn, athena::io::IStreamReader& in) : type(typeIn) {
uint32_t nameLen = in.readUint32Big();
name = in.readString(nameLen);
uint32_t dirLen = in.readUint32Big();
directory = in.readString(dirLen);
}
};
static std::unordered_map<uint64_t, SAsset> g_AssetNameMap;
static bool g_AssetNameMapInit = false;
void LoadAssetMap(athena::io::MemoryReader& ar) {
if (!ar.hasError()) {
hecl::FourCC magic;
if (ar.length() >= 4)
ar.readBytesToBuf(&magic, 4);
if (magic != FOURCC('AIDM'))
Log.report(
logvisor::Warning,
FMT_STRING("Unable to load asset map; Assets will not have proper filenames for most files."));
else {
uint32_t assetCount = ar.readUint32Big();
g_AssetNameMap.reserve(assetCount);
for (uint32_t i = 0; i < assetCount; ++i) {
hecl::FourCC type;
ar.readBytesToBuf(&type, 4);
uint64_t id = ar.readUint64Big();
g_AssetNameMap[id] = SAsset(type, ar);
}
}
}
}
void InitAssetNameMap() {
if (g_AssetNameMapInit)
return;
Log.report(logvisor::Info, FMT_STRING("Initializing asset name database..."));
/* First load the 32bit map for MP1/2 */
if (ASSET_NAME_MP32_DECOMPRESSED_SZ != 0u) {
auto* decompressed = new uint8_t[ASSET_NAME_MP32_DECOMPRESSED_SZ];
athena::io::Compression::decompressZlib(ASSET_NAME_MP32, ASSET_NAME_MP32_SZ, decompressed,
ASSET_NAME_MP32_DECOMPRESSED_SZ);
athena::io::MemoryReader ar(decompressed, ASSET_NAME_MP32_DECOMPRESSED_SZ);
LoadAssetMap(ar);
delete[](decompressed);
} else {
Log.report(
logvisor::Warning,
FMT_STRING("AssetNameMap32 unavailable; Assets will not have proper filenames for most files."));
}
/* Now load the 64bit map for MP3 */
if (ASSET_NAME_MP64_DECOMPRESSED_SZ != 0u) {
auto* decompressed = new uint8_t[ASSET_NAME_MP64_DECOMPRESSED_SZ];
athena::io::Compression::decompressZlib(ASSET_NAME_MP64, ASSET_NAME_MP64_SZ, decompressed,
ASSET_NAME_MP64_DECOMPRESSED_SZ);
athena::io::MemoryReader ar(decompressed, ASSET_NAME_MP64_DECOMPRESSED_SZ);
LoadAssetMap(ar);
delete[](decompressed);
} else {
Log.report(
logvisor::Warning,
FMT_STRING("AssetNameMap64 unavailable; Assets will not have proper filenames for most files."));
}
g_AssetNameMapInit = true;
}
const std::string* TranslateIdToName(const UniqueID32& id) {
if (g_AssetNameMap.find(id.toUint64()) == g_AssetNameMap.cend())
return nullptr;
return &g_AssetNameMap[id.toUint64()].name;
}
} // namespace DataSpec::AssetNameMap

@ -1,11 +0,0 @@
#pragma once
#include <unordered_map>
#include <string>
#include "DNACommon/DNACommon.hpp"
namespace DataSpec::AssetNameMap {
void InitAssetNameMap();
const std::string* TranslateIdToName(const UniqueID32&);
const std::string* TranslateIdToName(const UniqueID64&);
} // namespace DataSpec::AssetNameMap

@ -1,9 +0,0 @@
#include <cstddef>
#include <cstdint>
extern "C" const uint8_t ASSET_NAME_MP32[] = {0};
extern "C" const size_t ASSET_NAME_MP32_SZ = 0;
extern "C" const size_t ASSET_NAME_MP32_DECOMPRESSED_SZ = 0;
extern "C" const uint8_t ASSET_NAME_MP64[] = {0};
extern "C" const size_t ASSET_NAME_MP64_SZ = 0;
extern "C" const size_t ASSET_NAME_MP64_DECOMPRESSED_SZ = 0;

@ -1,22 +0,0 @@
#include <cstdint>
#include "hecl/Blender/Connection.hpp"
#include "BlenderSupport.hpp"
extern "C" uint8_t RETRO_MASTER_SHADER[];
extern "C" size_t RETRO_MASTER_SHADER_SZ;
namespace DataSpec::Blender {
bool BuildMasterShader(const hecl::ProjectPath& path) {
hecl::blender::Connection& conn = hecl::blender::Connection::SharedConnection();
if (!conn.createBlend(path, hecl::blender::BlendType::None))
return false;
{
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
os << std::string_view((char*)RETRO_MASTER_SHADER, RETRO_MASTER_SHADER_SZ);
os << "make_master_shader_library()\n"sv;
}
return conn.saveBlend();
}
} // namespace DataSpec::Blender

@ -1,9 +0,0 @@
#pragma once
#include <hecl/hecl.hpp>
namespace DataSpec::Blender {
bool BuildMasterShader(const hecl::ProjectPath& path);
}

File diff suppressed because it is too large Load Diff

@ -1,91 +0,0 @@
# Assembles a source/header pair list for use in a DNA library
macro(make_dnalist)
file(RELATIVE_PATH subdir "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_LIST_DIR}")
set(CMAKE_CURRENT_LIST_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${subdir}")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_LIST_BINARY_DIR}")
foreach (type ${ARGN})
get_filename_component(dir ${type} DIRECTORY)
if (dir)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_LIST_BINARY_DIR}/${dir}")
set(dir "${dir}/")
endif ()
get_filename_component(name ${type} NAME)
list(APPEND DNA_SOURCES "${subdir}/${dir}atdna_${name}.cpp")
list(APPEND DNA_HEADERS "${subdir}/${dir}${name}.hpp")
endforeach ()
endmacro()
# Assembles source files together for the main DataSpecCommon library
macro(dataspec_add_list rel_path a_list)
unset(tmp_list)
foreach (path IN LISTS ${a_list})
if (IS_ABSOLUTE ${path})
list(APPEND tmp_list "${path}")
else ()
list(APPEND tmp_list "${rel_path}/${path}")
endif ()
endforeach (path)
set(${a_list} "${tmp_list}")
endmacro(dataspec_add_list)
# Each game's DNA library
unset(DNA_SOURCES)
unset(DNA_HEADERS)
include(DNACommon/CMakeLists.txt)
include(DNAMP1/CMakeLists.txt)
include(DNAMP2/CMakeLists.txt)
include(DNAMP3/CMakeLists.txt)
# Embed master shader script
bintoc(RetroMasterShader.cpp Blender/RetroMasterShader.py RETRO_MASTER_SHADER)
# Download asset name databases
add_custom_command(OUTPUT AssetNameMap32.bin COMMAND ${CMAKE_COMMAND} ARGS -P
${CMAKE_CURRENT_SOURCE_DIR}/AssetMap32Download.cmake)
bintoc_compress(AssetNameMap32.cpp ${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap32.bin ASSET_NAME_MP32)
add_custom_command(OUTPUT AssetNameMap64.bin COMMAND ${CMAKE_COMMAND} ARGS -P
${CMAKE_CURRENT_SOURCE_DIR}/AssetMap64Download.cmake)
bintoc_compress(AssetNameMap64.cpp ${CMAKE_CURRENT_BINARY_DIR}/AssetNameMap64.bin ASSET_NAME_MP64)
# Each game's DataSpec implementation
add_library(RetroDataSpec
SpecBase.cpp
${DNACOMMON_SOURCES}
SpecMP1.cpp
${DNAMP1_SOURCES}
${ScriptObjectsMP1_SOURCES}
${DNAMP1_SFX_SOURCES}
SpecMP2.cpp
${DNAMP2_SOURCES}
SpecMP3.cpp
${DNAMP3_SOURCES}
Blender/BlenderSupport.hpp
Blender/BlenderSupport.cpp
Blender/RetroMasterShader.py
AssetNameMap.hpp
AssetNameMap.cpp
RetroMasterShader.cpp)
add_library(AssetNameMap
AssetNameMap32.bin AssetNameMap32.cpp
AssetNameMap64.bin AssetNameMap64.cpp)
add_library(AssetNameMapNull
AssetNameMapNull.cpp)
get_target_property(HECL_INCLUDES hecl-full INCLUDE_DIRECTORIES)
target_include_directories(RetroDataSpec PUBLIC ${HECL_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR})
target_link_libraries(RetroDataSpec PUBLIC amuse zeus nod squish ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} lzokay logvisor aurora xxhash)
if (COMMAND add_sanitizers)
add_sanitizers(RetroDataSpec)
endif ()
# Resolve all DNA sources into target
list(LENGTH DNA_SOURCES count)
math(EXPR count "${count}-1")
foreach (i RANGE ${count})
list(GET DNA_SOURCES ${i} src)
list(GET DNA_HEADERS ${i} header)
target_atdna(RetroDataSpec ${src} ${header})
endforeach ()
add_custom_target(genexdebug COMMAND ${CMAKE_COMMAND} -E echo "$<TARGET_PROPERTY:RetroDataSpec,INCLUDE_DIRECTORIES>")

@ -1,282 +0,0 @@
#include "DataSpec/DNACommon/ANCS.hpp"
#include "DataSpec/DNACommon/CMDL.hpp"
#include "DataSpec/DNACommon/DNACommon.hpp"
#include "DataSpec/DNACommon/RigInverter.hpp"
#include "DataSpec/DNAMP1/DNAMP1.hpp"
#include "DataSpec/DNAMP1/ANCS.hpp"
#include "DataSpec/DNAMP2/DNAMP2.hpp"
#include "DataSpec/DNAMP2/ANCS.hpp"
#include "DataSpec/DNAMP3/DNAMP3.hpp"
#include "DataSpec/DNAMP3/CHAR.hpp"
#include <hecl/Blender/Connection.hpp>
namespace DataSpec::DNAANCS {
template <class PAKRouter, class ANCSDNA, class MaterialSet, class SurfaceHeader, atUint32 CMDLVersion>
bool ReadANCSToBlender(hecl::blender::Token& btok, const ANCSDNA& ancs, const hecl::ProjectPath& outPath,
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec,
std::function<void(const char*)> fileChanged, bool force) {
auto& conn = btok.getBlenderConnection();
/* Extract character CMDL/CSKR/CINF first */
std::vector<CharacterResInfo<typename PAKRouter::IDType>> chResInfo;
ancs.getCharacterResInfo(chResInfo);
for (const auto& info : chResInfo) {
const nod::Node* node;
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info.cmdl, &node, true, false)) {
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
if (force || cmdlPath.isNone()) {
cmdlPath.makeDirChain(false);
if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh))
return false;
std::string bestName = pakRouter.getBestEntryName(*cmdlE);
fileChanged(bestName.c_str());
typename ANCSDNA::CSKRType cskr;
pakRouter.lookupAndReadDNA(info.cskr, cskr);
typename ANCSDNA::CINFType cinf;
pakRouter.lookupAndReadDNA(info.cinf, cinf);
using RigPair = std::pair<std::pair<typename PAKRouter::IDType, typename ANCSDNA::CSKRType*>,
std::pair<typename PAKRouter::IDType, typename ANCSDNA::CINFType*>>;
RigPair rigPair({info.cskr, &cskr}, {info.cinf, &cinf});
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, RigPair, SurfaceHeader, CMDLVersion>(
conn, rs, pakRouter, *cmdlE, dataspec, rigPair);
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/CSKR/CINFs first */
auto attRange = pakRouter.lookupCharacterAttachmentRigs(entry.id);
for (auto it = attRange.first; it != attRange.second; ++it) {
auto cinfid = it->second.first.cinf;
auto cmdlid = it->second.first.cmdl;
const nod::Node* node;
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(cmdlid, &node, true, false)) {
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
if (force || cmdlPath.isNone()) {
cmdlPath.makeDirChain(false);
if (!conn.createBlend(cmdlPath, hecl::blender::BlendType::Mesh)) {
return false;
}
std::string bestName = pakRouter.getBestEntryName(*cmdlE);
fileChanged(bestName.c_str());
const auto* rp = pakRouter.lookupCMDLRigPair(cmdlid);
typename ANCSDNA::CSKRType cskr;
pakRouter.lookupAndReadDNA(rp->cskr, cskr);
typename ANCSDNA::CINFType cinf;
pakRouter.lookupAndReadDNA(rp->cinf, cinf);
using RigPair = std::pair<std::pair<typename PAKRouter::IDType, typename ANCSDNA::CSKRType*>,
std::pair<typename PAKRouter::IDType, typename ANCSDNA::CINFType*>>;
RigPair rigPair({rp->cskr, &cskr}, {rp->cinf, &cinf});
PAKEntryReadStream rs = cmdlE->beginReadStream(*node);
DNACMDL::ReadCMDLToBlender<PAKRouter, MaterialSet, RigPair, SurfaceHeader, CMDLVersion>(
conn, rs, pakRouter, *cmdlE, dataspec, rigPair);
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);
fileChanged(bestName.c_str());
/* Establish ANCS blend */
if (!conn.createBlend(outPath, hecl::blender::BlendType::Actor))
return false;
std::string firstName;
typename ANCSDNA::CINFType firstCinf;
{
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
os.format(FMT_STRING("import bpy\n"
"from mathutils import Vector\n"
"bpy.context.scene.name = '{}'\n"
"bpy.context.scene.hecl_mesh_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"
"actor_data = bpy.context.scene.hecl_sact_data\n"
"arm_obj = None\n"),
pakRouter.getBestEntryName(entry));
std::unordered_set<typename PAKRouter::IDType> cinfsDone;
for (const auto& info : chResInfo) {
/* Provide data to add-on */
os.format(FMT_STRING("actor_subtype = actor_data.subtypes.add()\n"
"actor_subtype.name = '{}'\n\n"),
info.name);
/* Build CINF if needed */
if (cinfsDone.find(info.cinf) == cinfsDone.end()) {
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(info.cinf, nullptr, true, false)) {
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
os.linkArmature(cinfPath.getAbsolutePath(), fmt::format(FMT_STRING("CINF_{}"), info.cinf));
os << "if obj.name not in bpy.context.scene.objects:\n"
" bpy.context.scene.collection.objects.link(obj)\n";
}
if (cinfsDone.empty()) {
firstName = ANCSDNA::CINFType::GetCINFArmatureName(info.cinf);
pakRouter.lookupAndReadDNA(info.cinf, firstCinf);
}
cinfsDone.insert(info.cinf);
}
os.format(FMT_STRING("arm_obj = bpy.data.objects['CINF_{}']\n"), info.cinf);
os << "actor_subtype.linked_armature = arm_obj.name\n";
/* Link CMDL */
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info.cmdl, nullptr, true, false)) {
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
os.linkMesh(cmdlPath.getAbsolutePath(), pakRouter.getBestEntryName(*cmdlE));
/* Attach CMDL to CINF */
os << "if obj.name not in bpy.context.scene.objects:\n"
" bpy.context.scene.collection.objects.link(obj)\n"
"obj.parent = arm_obj\n"
"obj.parent_type = 'ARMATURE'\n"
"actor_subtype.linked_mesh = obj.name\n\n";
}
/* Link overlays */
for (const auto& overlay : info.overlays) {
os << "overlay = actor_subtype.overlays.add()\n";
os.format(FMT_STRING("overlay.name = '{}'\n"), overlay.first);
/* Link CMDL */
if (const typename PAKRouter::EntryType* cmdlE =
pakRouter.lookupEntry(overlay.second.first, nullptr, true, false)) {
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
os.linkMesh(cmdlPath.getAbsolutePath(), pakRouter.getBestEntryName(*cmdlE));
/* Attach CMDL to CINF */
os << "if obj.name not in bpy.context.scene.objects:\n"
" bpy.context.scene.collection.objects.link(obj)\n"
"obj.parent = arm_obj\n"
"obj.parent_type = 'ARMATURE'\n"
"overlay.linked_mesh = obj.name\n\n";
}
}
}
/* Link attachments */
for (auto it = attRange.first; it != attRange.second; ++it) {
os << "attachment = actor_data.attachments.add()\n";
os.format(FMT_STRING("attachment.name = '{}'\n"), it->second.second);
auto cinfid = it->second.first.cinf;
auto cmdlid = it->second.first.cmdl;
if (cinfid.isValid()) {
/* Build CINF if needed */
if (cinfsDone.find(cinfid) == cinfsDone.end()) {
if (const typename PAKRouter::EntryType* cinfE = pakRouter.lookupEntry(cinfid, nullptr, true, false)) {
hecl::ProjectPath cinfPath = pakRouter.getWorking(cinfE);
os.linkArmature(cinfPath.getAbsolutePath(), fmt::format(FMT_STRING("CINF_{}"), cinfid));
os << "if obj.name not in bpy.context.scene.objects:\n"
" bpy.context.scene.collection.objects.link(obj)\n";
}
if (cinfsDone.empty()) {
firstName = ANCSDNA::CINFType::GetCINFArmatureName(cinfid);
pakRouter.lookupAndReadDNA(cinfid, firstCinf);
}
cinfsDone.insert(cinfid);
}
os.format(FMT_STRING("arm_obj = bpy.data.objects['CINF_{}']\n"), cinfid);
os << "attachment.linked_armature = arm_obj.name\n";
}
/* Link CMDL */
if (const typename PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(cmdlid, nullptr, true, false)) {
hecl::ProjectPath cmdlPath = pakRouter.getWorking(cmdlE);
os.linkMesh(cmdlPath.getAbsolutePath(), pakRouter.getBestEntryName(*cmdlE));
/* Attach CMDL to CINF */
os << "if obj.name not in bpy.context.scene.objects:\n"
" bpy.context.scene.collection.objects.link(obj)\n"
"obj.parent = arm_obj\n"
"obj.parent_type = 'ARMATURE'\n"
"attachment.linked_mesh = obj.name\n\n";
}
}
}
{
hecl::blender::DataStream ds = conn.beginData();
std::unordered_map<std::string, hecl::blender::Matrix3f> matrices = ds.getBoneMatrices(firstName);
ds.close();
DNAANIM::RigInverter<typename ANCSDNA::CINFType> inverter(firstCinf, matrices);
hecl::blender::PyOutStream os = conn.beginPythonOut(true);
os << "import bpy\n"
"actor_data = bpy.context.scene.hecl_sact_data\n";
/* Get animation primitives */
std::map<atUint32, AnimationResInfo<typename PAKRouter::IDType>> animResInfo;
ancs.getAnimationResInfo(&pakRouter, animResInfo);
for (const auto& id : animResInfo) {
typename ANCSDNA::ANIMType anim;
if (pakRouter.lookupAndReadDNA(id.second.animId, anim, true)) {
os.format(FMT_STRING("act = bpy.data.actions.new('{}')\n"
"act.use_fake_user = True\n"
"act.anim_id = '{}'\n"),
id.second.name, id.second.animId);
anim.sendANIMToBlender(os, inverter, id.second.additive);
}
os.format(FMT_STRING("actor_action = actor_data.actions.add()\n"
"actor_action.name = '{}'\n"),
id.second.name);
/* Extract EVNT if present */
anim.extractEVNT(id.second, outPath, pakRouter, force);
}
}
conn.saveBlend();
return true;
}
template bool
ReadANCSToBlender<PAKRouter<DNAMP1::PAKBridge>, DNAMP1::ANCS, DNAMP1::MaterialSet, DNACMDL::SurfaceHeader_1, 2>(
hecl::blender::Token& btok, const DNAMP1::ANCS& ancs, const hecl::ProjectPath& outPath,
PAKRouter<DNAMP1::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP1::PAKBridge>::EntryType& entry,
const SpecBase& dataspec, std::function<void(const char*)> fileChanged, bool force);
template bool
ReadANCSToBlender<PAKRouter<DNAMP2::PAKBridge>, DNAMP2::ANCS, DNAMP2::MaterialSet, DNACMDL::SurfaceHeader_2, 4>(
hecl::blender::Token& btok, const DNAMP2::ANCS& ancs, const hecl::ProjectPath& outPath,
PAKRouter<DNAMP2::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP2::PAKBridge>::EntryType& entry,
const SpecBase& dataspec, std::function<void(const char*)> fileChanged, bool force);
template bool
ReadANCSToBlender<PAKRouter<DNAMP3::PAKBridge>, DNAMP3::CHAR, DNAMP3::MaterialSet, DNACMDL::SurfaceHeader_3, 4>(
hecl::blender::Token& btok, const DNAMP3::CHAR& ancs, const hecl::ProjectPath& outPath,
PAKRouter<DNAMP3::PAKBridge>& pakRouter, const typename PAKRouter<DNAMP3::PAKBridge>::EntryType& entry,
const SpecBase& dataspec, std::function<void(const char*)> fileChanged, bool force);
} // namespace DataSpec::DNAANCS

@ -1,47 +0,0 @@
#pragma once
#include <functional>
#include <string>
#include <utility>
#include <vector>
#include "athena/Types.hpp"
#include "hecl/Blender/Connection.hpp"
namespace DataSpec {
struct SpecBase;
}
namespace hecl {
class ProjectPath;
}
namespace DataSpec::DNAANCS {
using Actor = hecl::blender::Actor;
using Armature = Actor::ActorArmature;
using Action = hecl::blender::Action;
template <typename IDTYPE>
struct CharacterResInfo {
std::string name;
IDTYPE cmdl;
IDTYPE cskr;
IDTYPE cinf;
std::vector<std::pair<std::string, std::pair<IDTYPE, IDTYPE>>> overlays;
};
template <typename IDTYPE>
struct AnimationResInfo {
std::string name;
IDTYPE animId;
IDTYPE evntId;
bool additive;
};
template <class PAKRouter, class ANCSDNA, class MaterialSet, class SurfaceHeader, atUint32 CMDLVersion>
bool ReadANCSToBlender(hecl::blender::Token& btok, const ANCSDNA& ancs, const hecl::ProjectPath& outPath,
PAKRouter& pakRouter, const typename PAKRouter::EntryType& entry, const SpecBase& dataspec,
std::function<void(const char*)> fileChanged, bool force = false);
} // namespace DataSpec::DNAANCS

@ -1,462 +0,0 @@
#include "DataSpec/DNACommon/ANIM.hpp"
#include <cfloat>
#include <cmath>
#include <cstring>
#include <hecl/hecl.hpp>
#include <zeus/Global.hpp>
#include <zeus/Math.hpp>
#define DUMP_KEYS 0
#if DUMP_KEYS
#include <cstdio>
#include <fmt/format.h>
#endif
namespace DataSpec::DNAANIM {
size_t ComputeBitstreamSize(size_t keyFrameCount, const std::vector<Channel>& channels) {
size_t bitsPerKeyFrame = 0;
for (const Channel& chan : channels) {
switch (chan.type) {
case Channel::Type::Rotation:
bitsPerKeyFrame += 1;
[[fallthrough]];
case Channel::Type::Translation:
case Channel::Type::Scale:
bitsPerKeyFrame += chan.q[0];
bitsPerKeyFrame += chan.q[1];
bitsPerKeyFrame += chan.q[2];
break;
case Channel::Type::KfHead:
bitsPerKeyFrame += 1;
break;
case Channel::Type::RotationMP3:
bitsPerKeyFrame += chan.q[0];
bitsPerKeyFrame += chan.q[1];
bitsPerKeyFrame += chan.q[2];
bitsPerKeyFrame += chan.q[3];
break;
default:
break;
}
}
return (bitsPerKeyFrame * keyFrameCount + 31) / 32 * 4;
}
static QuantizedRot QuantizeRotation(const Value& quat, atUint32 div) {
float q = float(div) / (M_PIF / 2.0f);
zeus::simd_floats f(quat.simd);
assert(std::abs(f[1]) <= 1.f && "Out of range quat X component");
assert(std::abs(f[2]) <= 1.f && "Out of range quat Y component");
assert(std::abs(f[3]) <= 1.f && "Out of range quat Z component");
return {{
atInt32(std::asin(f[1]) * q),
atInt32(std::asin(f[2]) * q),
atInt32(std::asin(f[3]) * q),
},
(f[0] < 0.f)};
}
static Value DequantizeRotation(const QuantizedRot& v, atUint32 div) {
float q = (M_PIF / 2.0f) / float(div);
athena::simd_floats f = {
0.0f,
std::sin(v.v[0] * q),
std::sin(v.v[1] * q),
std::sin(v.v[2] * q),
};
f[0] = std::sqrt(std::max((1.0f - (f[1] * f[1] + f[2] * f[2] + f[3] * f[3])), 0.0f));
f[0] = v.w ? -f[0] : f[0];
Value retval;
retval.simd.copy_from(f);
return retval;
}
static Value DequantizeRotation_3(const QuantizedRot& v, atUint32 div) {
float q = 1.0f / float(div);
athena::simd_floats f = {
0.0f,
v.v[0] * q,
v.v[1] * q,
v.v[2] * q,
};
f[0] = std::sqrt(std::max((1.0f - (f[1] * f[1] + f[2] * f[2] + f[3] * f[3])), 0.0f));
f[0] = v.w ? -f[0] : f[0];
Value retval;
retval.simd.copy_from(f);
return retval;
}
bool BitstreamReader::dequantizeBit(const atUint8* data) {
atUint32 byteCur = (m_bitCur / 32) * 4;
atUint32 bitRem = m_bitCur % 32;
/* Fill 32 bit buffer with region containing bits */
/* Make them least significant */
atUint32 tempBuf = hecl::SBig(*reinterpret_cast<const atUint32*>(data + byteCur)) >> bitRem;
/* That's it */
m_bitCur += 1;
return tempBuf & 0x1;
}
atInt32 BitstreamReader::dequantize(const atUint8* data, atUint8 q) {
atUint32 byteCur = (m_bitCur / 32) * 4;
atUint32 bitRem = m_bitCur % 32;
/* Fill 32 bit buffer with region containing bits */
/* Make them least significant */
atUint32 tempBuf = hecl::SBig(*reinterpret_cast<const atUint32*>(data + byteCur)) >> bitRem;
/* If this shift underflows the value, buffer the next 32 bits */
/* And tack onto shifted buffer */
if ((bitRem + q) > 32) {
atUint32 tempBuf2 = hecl::SBig(*reinterpret_cast<const atUint32*>(data + byteCur + 4));
tempBuf |= (tempBuf2 << (32 - bitRem));
}
/* Mask it */
atUint32 mask = (1 << q) - 1;
tempBuf &= mask;
/* Sign extend */
atUint32 sign = (tempBuf >> (q - 1)) & 0x1;
if (sign)
tempBuf |= ~0u << q;
/* Return delta value */
m_bitCur += q;
return atInt32(tempBuf);
}
std::vector<std::vector<Value>> BitstreamReader::read(const atUint8* data, size_t keyFrameCount,
const std::vector<Channel>& channels, atUint32 rotDiv,
float transMult, float scaleMult) {
m_bitCur = 0;
std::vector<std::vector<Value>> chanKeys;
std::vector<QuantizedValue> chanAccum;
chanKeys.reserve(channels.size());
chanAccum.reserve(channels.size());
for (const Channel& chan : channels) {
chanAccum.push_back(chan.i);
chanKeys.emplace_back();
std::vector<Value>& keys = chanKeys.back();
keys.reserve(keyFrameCount);
switch (chan.type) {
case Channel::Type::Rotation: {
QuantizedRot qr = {{chan.i[0], chan.i[1], chan.i[2]}, false};
keys.emplace_back(DequantizeRotation(qr, rotDiv));
break;
}
case Channel::Type::Translation: {
keys.push_back({chan.i[0] * transMult, chan.i[1] * transMult, chan.i[2] * transMult});
break;
}
case Channel::Type::Scale: {
keys.push_back({chan.i[0] * scaleMult, chan.i[1] * scaleMult, chan.i[2] * scaleMult});
break;
}
case Channel::Type::KfHead: {
break;
}
case Channel::Type::RotationMP3: {
QuantizedRot qr = {{chan.i[1], chan.i[2], chan.i[3]}, bool(chan.i[0] & 0x1)};
keys.emplace_back(DequantizeRotation_3(qr, rotDiv));
break;
}
default:
break;
}
}
for (size_t f = 0; f < keyFrameCount; ++f) {
#if DUMP_KEYS
fmt::print(stderr, FMT_STRING("\nFRAME {} {} {}\n"), f, (m_bitCur / 32) * 4, m_bitCur % 32);
int lastId = -1;
#endif
auto kit = chanKeys.begin();
auto ait = chanAccum.begin();
for (const Channel& chan : channels) {
#if DUMP_KEYS
if (chan.id != lastId) {
lastId = chan.id;
std::fputc('\n', stderr);
}
#endif
QuantizedValue& p = *ait;
switch (chan.type) {
case Channel::Type::Rotation: {
bool wBit = dequantizeBit(data);
p[0] += dequantize(data, chan.q[0]);
p[1] += dequantize(data, chan.q[1]);
p[2] += dequantize(data, chan.q[2]);
QuantizedRot qr = {{p[0], p[1], p[2]}, wBit};
kit->emplace_back(DequantizeRotation(qr, rotDiv));
#if DUMP_KEYS
fmt::print(stderr, FMT_STRING("{} R: {} {} {} {}\t"), chan.id, wBit, p[0], p[1], p[2]);
#endif
break;
}
case Channel::Type::Translation: {
atInt32 val1 = dequantize(data, chan.q[0]);
p[0] += val1;
atInt32 val2 = dequantize(data, chan.q[1]);
p[1] += val2;
atInt32 val3 = dequantize(data, chan.q[2]);
p[2] += val3;
kit->push_back({p[0] * transMult, p[1] * transMult, p[2] * transMult});
#if DUMP_KEYS
fmt::print(stderr, FMT_STRING("{} T: {} {} {}\t"), chan.id, p[0], p[1], p[2]);
#endif
break;
}
case Channel::Type::Scale: {
p[0] += dequantize(data, chan.q[0]);
p[1] += dequantize(data, chan.q[1]);
p[2] += dequantize(data, chan.q[2]);
kit->push_back({p[0] * scaleMult, p[1] * scaleMult, p[2] * scaleMult});
#if DUMP_KEYS
fmt::print(stderr, FMT_STRING("{} S: {} {} {}\t"), chan.id, p[0], p[1], p[2]);
#endif
break;
}
case Channel::Type::KfHead: {
dequantizeBit(data);
break;
}
case Channel::Type::RotationMP3: {
atInt32 val1 = dequantize(data, chan.q[0]);
p[0] += val1;
atInt32 val2 = dequantize(data, chan.q[1]);
p[1] += val2;
atInt32 val3 = dequantize(data, chan.q[2]);
p[2] += val3;
atInt32 val4 = dequantize(data, chan.q[3]);
p[3] += val4;
QuantizedRot qr = {{p[1], p[2], p[3]}, bool(p[0] & 0x1)};
kit->emplace_back(DequantizeRotation_3(qr, rotDiv));
break;
}
default:
break;
}
++kit;
++ait;
}
#if DUMP_KEYS
std::fputc('\n', stderr);
#endif
}
return chanKeys;
}
void BitstreamWriter::quantizeBit(atUint8* data, bool val) {
atUint32 byteCur = (m_bitCur / 32) * 4;
atUint32 bitRem = m_bitCur % 32;
/* Fill 32 bit buffer with region containing bits */
/* Make them least significant */
*(atUint32*)(data + byteCur) = hecl::SBig(hecl::SBig(*(atUint32*)(data + byteCur)) | (val << bitRem));
m_bitCur += 1;
}
void BitstreamWriter::quantize(atUint8* data, atUint8 q, atInt32 val) {
atUint32 byteCur = (m_bitCur / 32) * 4;
atUint32 bitRem = m_bitCur % 32;
atUint32 masked = val & ((1 << q) - 1);
assert(((((val >> 31) & 0x1) == 0x1) || (((masked >> (q - 1)) & 0x1) == 0)) && "Twos compliment fail");
/* Fill 32 bit buffer with region containing bits */
/* Make them least significant */
*(atUint32*)(data + byteCur) = hecl::SBig(hecl::SBig(*(atUint32*)(data + byteCur)) | (masked << bitRem));
/* If this shift underflows the value, buffer the next 32 bits */
/* And tack onto shifted buffer */
if ((bitRem + q) > 32) {
*(atUint32*)(data + byteCur + 4) =
hecl::SBig(hecl::SBig(*(atUint32*)(data + byteCur + 4)) | (masked >> (32 - bitRem)));
}
m_bitCur += q;
}
std::unique_ptr<atUint8[]> BitstreamWriter::write(const std::vector<std::vector<Value>>& chanKeys, size_t keyFrameCount,
std::vector<Channel>& channels, atUint32 quantRange,
atUint32& rotDivOut, float& transMultOut, float& scaleMultOut,
size_t& sizeOut) {
m_bitCur = 0;
rotDivOut = quantRange; /* Normalized range of values */
float quantRangeF = float(quantRange);
/* Pre-pass to calculate translation multiplier */
float maxTransDelta = 0.0f;
float maxScaleDelta = 0.0f;
auto kit = chanKeys.begin();
for (Channel& chan : channels) {
switch (chan.type) {
case Channel::Type::Translation: {
zeus::simd<float> lastVal = {};
for (auto it = kit->begin(); it != kit->end(); ++it) {
const Value* key = &*it;
zeus::simd_floats f(key->simd - lastVal);
lastVal = key->simd;
maxTransDelta = std::max(maxTransDelta, std::fabs(f[0]));
maxTransDelta = std::max(maxTransDelta, std::fabs(f[1]));
maxTransDelta = std::max(maxTransDelta, std::fabs(f[2]));
}
break;
}
case Channel::Type::Scale: {
zeus::simd<float> lastVal = {};
for (auto it = kit->begin(); it != kit->end(); ++it) {
const Value* key = &*it;
zeus::simd_floats f(key->simd - lastVal);
lastVal = key->simd;
maxScaleDelta = std::max(maxScaleDelta, std::fabs(f[0]));
maxScaleDelta = std::max(maxScaleDelta, std::fabs(f[1]));
maxScaleDelta = std::max(maxScaleDelta, std::fabs(f[2]));
}
break;
}
default:
break;
}
++kit;
}
transMultOut = maxTransDelta / quantRangeF + FLT_EPSILON;
scaleMultOut = maxScaleDelta / quantRangeF + FLT_EPSILON;
/* Output channel inits */
std::vector<QuantizedValue> initVals;
initVals.reserve(channels.size());
kit = chanKeys.begin();
for (Channel& chan : channels) {
chan.q[0] = 1;
chan.q[1] = 1;
chan.q[2] = 1;
switch (chan.type) {
case Channel::Type::Rotation: {
QuantizedRot qr = QuantizeRotation((*kit)[0], rotDivOut);
chan.i = qr.v;
initVals.push_back(chan.i);
break;
}
case Channel::Type::Translation: {
zeus::simd_floats f((*kit)[0].simd);
chan.i = {atInt32(f[0] / transMultOut), atInt32(f[1] / transMultOut), atInt32(f[2] / transMultOut)};
initVals.push_back(chan.i);
break;
}
case Channel::Type::Scale: {
zeus::simd_floats f((*kit)[0].simd);
chan.i = {atInt32(f[0] / scaleMultOut), atInt32(f[1] / scaleMultOut), atInt32(f[2] / scaleMultOut)};
initVals.push_back(chan.i);
break;
}
default:
break;
}
++kit;
}
/* Pre-pass to analyze quantization factors for channels */
std::vector<QuantizedValue> lastVals = initVals;
kit = chanKeys.begin();
auto vit = lastVals.begin();
for (Channel& chan : channels) {
QuantizedValue& last = *vit++;
switch (chan.type) {
case Channel::Type::Rotation: {
for (auto it = kit->begin() + 1; it != kit->end(); ++it) {
QuantizedRot qrCur = QuantizeRotation(*it, rotDivOut);
chan.q[0] = std::max(chan.q[0], atUint8(qrCur.v.qFrom(last, 0)));
chan.q[1] = std::max(chan.q[1], atUint8(qrCur.v.qFrom(last, 1)));
chan.q[2] = std::max(chan.q[2], atUint8(qrCur.v.qFrom(last, 2)));
last = qrCur.v;
}
break;
}
case Channel::Type::Translation: {
for (auto it = kit->begin() + 1; it != kit->end(); ++it) {