diff --git a/docs/SupportedAssetTypes.md b/docs/SupportedAssetTypes.md index e268bdcd9..3f02a716b 100644 --- a/docs/SupportedAssetTypes.md +++ b/docs/SupportedAssetTypes.md @@ -199,7 +199,7 @@ using `Linker`): |------------------------|-----------------|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| | PhysPreset | ✅ | ✅ | | | PhysConstraints | ✅ | ✅ | | -| DestructibleDef | ❌ | ❌ | | +| DestructibleDef | ✅ | ❌ | | | XAnimParts | ✅ | ✅ | | | XModel | ✅ | ✅ | Model data can be exported to `XMODEL_EXPORT/XMODEL_BIN`, `OBJ`, `GLB/GLTF`. | | Material | ✅ | ✅ | Dumping/Loading is currently possible for materials in their compiled form. There is currently no material pipeline. | diff --git a/src/ObjCommon/DestructibleDef/DestructibleDefCommon.cpp b/src/ObjCommon/DestructibleDef/DestructibleDefCommon.cpp new file mode 100644 index 000000000..f9044dffa --- /dev/null +++ b/src/ObjCommon/DestructibleDef/DestructibleDefCommon.cpp @@ -0,0 +1,16 @@ +#include "DestructibleDefCommon.h" + +#include + +namespace destructible_def +{ + std::string GetFileNameForDestructibleDef(const std::string& assetName) + { + return std::format("destructibledef/{}", assetName); + } + + std::string GetFileNameForDestructiblePiece(const std::string& assetName, const int pieceIndex) + { + return std::format("destructiblepiece/{}_{}", assetName, pieceIndex); + } +} // namespace destructible_def diff --git a/src/ObjCommon/DestructibleDef/DestructibleDefCommon.h b/src/ObjCommon/DestructibleDef/DestructibleDefCommon.h new file mode 100644 index 000000000..17eb7ace5 --- /dev/null +++ b/src/ObjCommon/DestructibleDef/DestructibleDefCommon.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace destructible_def +{ + std::string GetFileNameForDestructibleDef(const std::string& assetName); + std::string GetFileNameForDestructiblePiece(const std::string& assetName, const int pieceIndex); +} // namespace destructible_def diff --git a/src/ObjCommon/Game/T5/DestructibleDef/DestructibleDefFields.h b/src/ObjCommon/Game/T5/DestructibleDef/DestructibleDefFields.h new file mode 100644 index 000000000..6469c2ad9 --- /dev/null +++ b/src/ObjCommon/Game/T5/DestructibleDef/DestructibleDefFields.h @@ -0,0 +1,79 @@ +#pragma once +#include "Game/T5/T5.h" + +namespace T5 +{ + inline cspField_t destructible_piece_fields[]{ + {"showBone", offsetof(DestructiblePiece, stages[0].showBone), CSPFT_SCRIPT_STRING}, + {"breakHealth", offsetof(DestructiblePiece, stages[0].breakHealth), CSPFT_FLOAT }, + {"maxTime", offsetof(DestructiblePiece, stages[0].maxTime), CSPFT_FLOAT }, + + {"breakEffect", offsetof(DestructiblePiece, stages[0].breakEffect), CSPFT_FX }, + {"breakSound", offsetof(DestructiblePiece, stages[0].breakSound), CSPFT_STRING }, + {"breakNotify", offsetof(DestructiblePiece, stages[0].breakNotify), CSPFT_STRING }, + {"loopSound", offsetof(DestructiblePiece, stages[0].loopSound), CSPFT_STRING }, + + {"spawnModel1", offsetof(DestructiblePiece, stages[0].spawnModel[0]), CSPFT_XMODEL }, + {"spawnModel2", offsetof(DestructiblePiece, stages[0].spawnModel[1]), CSPFT_XMODEL }, + {"spawnModel3", offsetof(DestructiblePiece, stages[0].spawnModel[2]), CSPFT_XMODEL }, + + {"physPreset", offsetof(DestructiblePiece, stages[0].physPreset), CSPFT_PHYS_PRESET }, + + {"showBoneD1", offsetof(DestructiblePiece, stages[1].showBone), CSPFT_SCRIPT_STRING}, + {"breakHealthD1", offsetof(DestructiblePiece, stages[1].breakHealth), CSPFT_FLOAT }, + {"maxTimeD1", offsetof(DestructiblePiece, stages[1].maxTime), CSPFT_FLOAT }, + {"breakEffectD1", offsetof(DestructiblePiece, stages[1].breakEffect), CSPFT_FX }, + {"breakSoundD1", offsetof(DestructiblePiece, stages[1].breakSound), CSPFT_STRING }, + {"breakNotifyD1", offsetof(DestructiblePiece, stages[1].breakNotify), CSPFT_STRING }, + {"loopSoundD1", offsetof(DestructiblePiece, stages[1].loopSound), CSPFT_STRING }, + {"spawnModel1D1", offsetof(DestructiblePiece, stages[1].spawnModel[0]), CSPFT_XMODEL }, + {"spawnModel2D1", offsetof(DestructiblePiece, stages[1].spawnModel[1]), CSPFT_XMODEL }, + {"spawnModel3D1", offsetof(DestructiblePiece, stages[1].spawnModel[2]), CSPFT_XMODEL }, + {"physPresetD1", offsetof(DestructiblePiece, stages[1].physPreset), CSPFT_PHYS_PRESET }, + + {"showBoneD2", offsetof(DestructiblePiece, stages[2].showBone), CSPFT_SCRIPT_STRING}, + {"breakHealthD2", offsetof(DestructiblePiece, stages[2].breakHealth), CSPFT_FLOAT }, + {"maxTimeD2", offsetof(DestructiblePiece, stages[2].maxTime), CSPFT_FLOAT }, + {"breakEffectD2", offsetof(DestructiblePiece, stages[2].breakEffect), CSPFT_FX }, + {"breakSoundD2", offsetof(DestructiblePiece, stages[2].breakSound), CSPFT_STRING }, + {"breakNotifyD2", offsetof(DestructiblePiece, stages[2].breakNotify), CSPFT_STRING }, + {"loopSoundD2", offsetof(DestructiblePiece, stages[2].loopSound), CSPFT_STRING }, + {"spawnModel1D2", offsetof(DestructiblePiece, stages[2].spawnModel[0]), CSPFT_XMODEL }, + {"spawnModel2D2", offsetof(DestructiblePiece, stages[2].spawnModel[1]), CSPFT_XMODEL }, + {"spawnModel3D2", offsetof(DestructiblePiece, stages[2].spawnModel[2]), CSPFT_XMODEL }, + {"physPresetD2", offsetof(DestructiblePiece, stages[2].physPreset), CSPFT_PHYS_PRESET }, + + {"showBoneD3", offsetof(DestructiblePiece, stages[3].showBone), CSPFT_SCRIPT_STRING}, + {"breakHealthD3", offsetof(DestructiblePiece, stages[3].breakHealth), CSPFT_FLOAT }, + {"maxTimeD3", offsetof(DestructiblePiece, stages[3].maxTime), CSPFT_FLOAT }, + {"breakEffectD3", offsetof(DestructiblePiece, stages[3].breakEffect), CSPFT_FX }, + {"breakSoundD3", offsetof(DestructiblePiece, stages[3].breakSound), CSPFT_STRING }, + {"breakNotifyD3", offsetof(DestructiblePiece, stages[3].breakNotify), CSPFT_STRING }, + {"loopSoundD3", offsetof(DestructiblePiece, stages[3].loopSound), CSPFT_STRING }, + {"spawnModel1D3", offsetof(DestructiblePiece, stages[3].spawnModel[0]), CSPFT_XMODEL }, + {"spawnModel2D3", offsetof(DestructiblePiece, stages[3].spawnModel[1]), CSPFT_XMODEL }, + {"spawnModel3D3", offsetof(DestructiblePiece, stages[3].spawnModel[2]), CSPFT_XMODEL }, + {"physPresetD3", offsetof(DestructiblePiece, stages[3].physPreset), CSPFT_PHYS_PRESET }, + + {"showBoneD4", offsetof(DestructiblePiece, stages[4].showBone), CSPFT_SCRIPT_STRING}, + {"breakHealthD4", offsetof(DestructiblePiece, stages[4].breakHealth), CSPFT_FLOAT }, + {"maxTimeD4", offsetof(DestructiblePiece, stages[4].maxTime), CSPFT_FLOAT }, + {"breakEffectD4", offsetof(DestructiblePiece, stages[4].breakEffect), CSPFT_FX }, + {"breakSoundD4", offsetof(DestructiblePiece, stages[4].breakSound), CSPFT_STRING }, + {"breakNotifyD4", offsetof(DestructiblePiece, stages[4].breakNotify), CSPFT_STRING }, + {"loopSoundD4", offsetof(DestructiblePiece, stages[4].loopSound), CSPFT_STRING }, + {"spawnModel1D4", offsetof(DestructiblePiece, stages[4].spawnModel[0]), CSPFT_XMODEL }, + {"spawnModel2D4", offsetof(DestructiblePiece, stages[4].spawnModel[1]), CSPFT_XMODEL }, + {"spawnModel3D4", offsetof(DestructiblePiece, stages[4].spawnModel[2]), CSPFT_XMODEL }, + {"physPresetD4", offsetof(DestructiblePiece, stages[4].physPreset), CSPFT_PHYS_PRESET }, + }; + + inline cspField_t destructible_def_fields[]{ + {"name", offsetof(DestructibleDef, name), CSPFT_STRING}, + {"model", offsetof(DestructibleDef, model), CSPFT_XMODEL}, + {"pristineModel", offsetof(DestructibleDef, pristineModel), CSPFT_XMODEL}, + {"numPieces", offsetof(DestructibleDef, numPieces), CSPFT_INT }, + {"pieces", offsetof(DestructibleDef, pieces), CSPFT_INT }, + {"clientOnly", offsetof(DestructibleDef, clientOnly), CSPFT_INT }, + }; +} // namespace T5 diff --git a/src/ObjCommon/Game/T5/ObjConstantsT5.h b/src/ObjCommon/Game/T5/ObjConstantsT5.h index e71556f64..6916678fb 100644 --- a/src/ObjCommon/Game/T5/ObjConstantsT5.h +++ b/src/ObjCommon/Game/T5/ObjConstantsT5.h @@ -2,6 +2,8 @@ namespace T5 { + static constexpr auto INFO_STRING_PREFIX_DESTRUCTIBLE_DEF = "DESTRUCTIBLEDEF"; + static constexpr auto INFO_STRING_PREFIX_DESTRUCTIBLE_PIECE = "DESTRUCTIBLEPIECE"; static constexpr auto INFO_STRING_PREFIX_FLAME_TABLE = "FLAMETABLEFILE"; static constexpr auto INFO_STRING_PREFIX_PHYS_CONSTRAINTS = "PHYSCONSTRAINTS"; static constexpr auto INFO_STRING_PREFIX_PHYS_PRESET = "PHYSIC"; diff --git a/src/ObjCommon/Game/T6/DestructibleDef/DestructibleDefFields.h b/src/ObjCommon/Game/T6/DestructibleDef/DestructibleDefFields.h new file mode 100644 index 000000000..1f3482c58 --- /dev/null +++ b/src/ObjCommon/Game/T6/DestructibleDef/DestructibleDefFields.h @@ -0,0 +1,79 @@ +#pragma once +#include "Game/T6/T6.h" + +namespace T6 +{ + inline cspField_t destructible_piece_fields[]{ + {"showBone", offsetof(DestructiblePiece, stages[0].showBone), CSPFT_SCRIPT_STRING}, + {"breakHealth", offsetof(DestructiblePiece, stages[0].breakHealth), CSPFT_FLOAT }, + {"maxTime", offsetof(DestructiblePiece, stages[0].maxTime), CSPFT_FLOAT }, + + {"breakEffect", offsetof(DestructiblePiece, stages[0].breakEffect), CSPFT_FX }, + {"breakSound", offsetof(DestructiblePiece, stages[0].breakSound), CSPFT_STRING }, + {"breakNotify", offsetof(DestructiblePiece, stages[0].breakNotify), CSPFT_STRING }, + {"loopSound", offsetof(DestructiblePiece, stages[0].loopSound), CSPFT_STRING }, + + {"spawnModel1", offsetof(DestructiblePiece, stages[0].spawnModel[0]), CSPFT_XMODEL }, + {"spawnModel2", offsetof(DestructiblePiece, stages[0].spawnModel[1]), CSPFT_XMODEL }, + {"spawnModel3", offsetof(DestructiblePiece, stages[0].spawnModel[2]), CSPFT_XMODEL }, + + {"physPreset", offsetof(DestructiblePiece, stages[0].physPreset), CSPFT_PHYS_PRESET }, + + {"showBoneD1", offsetof(DestructiblePiece, stages[1].showBone), CSPFT_SCRIPT_STRING}, + {"breakHealthD1", offsetof(DestructiblePiece, stages[1].breakHealth), CSPFT_FLOAT }, + {"maxTimeD1", offsetof(DestructiblePiece, stages[1].maxTime), CSPFT_FLOAT }, + {"breakEffectD1", offsetof(DestructiblePiece, stages[1].breakEffect), CSPFT_FX }, + {"breakSoundD1", offsetof(DestructiblePiece, stages[1].breakSound), CSPFT_STRING }, + {"breakNotifyD1", offsetof(DestructiblePiece, stages[1].breakNotify), CSPFT_STRING }, + {"loopSoundD1", offsetof(DestructiblePiece, stages[1].loopSound), CSPFT_STRING }, + {"spawnModel1D1", offsetof(DestructiblePiece, stages[1].spawnModel[0]), CSPFT_XMODEL }, + {"spawnModel2D1", offsetof(DestructiblePiece, stages[1].spawnModel[1]), CSPFT_XMODEL }, + {"spawnModel3D1", offsetof(DestructiblePiece, stages[1].spawnModel[2]), CSPFT_XMODEL }, + {"physPresetD1", offsetof(DestructiblePiece, stages[1].physPreset), CSPFT_PHYS_PRESET }, + + {"showBoneD2", offsetof(DestructiblePiece, stages[2].showBone), CSPFT_SCRIPT_STRING}, + {"breakHealthD2", offsetof(DestructiblePiece, stages[2].breakHealth), CSPFT_FLOAT }, + {"maxTimeD2", offsetof(DestructiblePiece, stages[2].maxTime), CSPFT_FLOAT }, + {"breakEffectD2", offsetof(DestructiblePiece, stages[2].breakEffect), CSPFT_FX }, + {"breakSoundD2", offsetof(DestructiblePiece, stages[2].breakSound), CSPFT_STRING }, + {"breakNotifyD2", offsetof(DestructiblePiece, stages[2].breakNotify), CSPFT_STRING }, + {"loopSoundD2", offsetof(DestructiblePiece, stages[2].loopSound), CSPFT_STRING }, + {"spawnModel1D2", offsetof(DestructiblePiece, stages[2].spawnModel[0]), CSPFT_XMODEL }, + {"spawnModel2D2", offsetof(DestructiblePiece, stages[2].spawnModel[1]), CSPFT_XMODEL }, + {"spawnModel3D2", offsetof(DestructiblePiece, stages[2].spawnModel[2]), CSPFT_XMODEL }, + {"physPresetD2", offsetof(DestructiblePiece, stages[2].physPreset), CSPFT_PHYS_PRESET }, + + {"showBoneD3", offsetof(DestructiblePiece, stages[3].showBone), CSPFT_SCRIPT_STRING}, + {"breakHealthD3", offsetof(DestructiblePiece, stages[3].breakHealth), CSPFT_FLOAT }, + {"maxTimeD3", offsetof(DestructiblePiece, stages[3].maxTime), CSPFT_FLOAT }, + {"breakEffectD3", offsetof(DestructiblePiece, stages[3].breakEffect), CSPFT_FX }, + {"breakSoundD3", offsetof(DestructiblePiece, stages[3].breakSound), CSPFT_STRING }, + {"breakNotifyD3", offsetof(DestructiblePiece, stages[3].breakNotify), CSPFT_STRING }, + {"loopSoundD3", offsetof(DestructiblePiece, stages[3].loopSound), CSPFT_STRING }, + {"spawnModel1D3", offsetof(DestructiblePiece, stages[3].spawnModel[0]), CSPFT_XMODEL }, + {"spawnModel2D3", offsetof(DestructiblePiece, stages[3].spawnModel[1]), CSPFT_XMODEL }, + {"spawnModel3D3", offsetof(DestructiblePiece, stages[3].spawnModel[2]), CSPFT_XMODEL }, + {"physPresetD3", offsetof(DestructiblePiece, stages[3].physPreset), CSPFT_PHYS_PRESET }, + + {"showBoneD4", offsetof(DestructiblePiece, stages[4].showBone), CSPFT_SCRIPT_STRING}, + {"breakHealthD4", offsetof(DestructiblePiece, stages[4].breakHealth), CSPFT_FLOAT }, + {"maxTimeD4", offsetof(DestructiblePiece, stages[4].maxTime), CSPFT_FLOAT }, + {"breakEffectD4", offsetof(DestructiblePiece, stages[4].breakEffect), CSPFT_FX }, + {"breakSoundD4", offsetof(DestructiblePiece, stages[4].breakSound), CSPFT_STRING }, + {"breakNotifyD4", offsetof(DestructiblePiece, stages[4].breakNotify), CSPFT_STRING }, + {"loopSoundD4", offsetof(DestructiblePiece, stages[4].loopSound), CSPFT_STRING }, + {"spawnModel1D4", offsetof(DestructiblePiece, stages[4].spawnModel[0]), CSPFT_XMODEL }, + {"spawnModel2D4", offsetof(DestructiblePiece, stages[4].spawnModel[1]), CSPFT_XMODEL }, + {"spawnModel3D4", offsetof(DestructiblePiece, stages[4].spawnModel[2]), CSPFT_XMODEL }, + {"physPresetD4", offsetof(DestructiblePiece, stages[4].physPreset), CSPFT_PHYS_PRESET }, + }; + + inline cspField_t destructible_def_fields[]{ + {"name", offsetof(DestructibleDef, name), CSPFT_STRING}, + {"model", offsetof(DestructibleDef, model), CSPFT_XMODEL}, + {"pristineModel", offsetof(DestructibleDef, pristineModel), CSPFT_XMODEL}, + {"numPieces", offsetof(DestructibleDef, numPieces), CSPFT_INT }, + {"pieces", offsetof(DestructibleDef, pieces), CSPFT_INT }, + {"clientOnly", offsetof(DestructibleDef, clientOnly), CSPFT_INT }, + }; +} // namespace T6 diff --git a/src/ObjCommon/Game/T6/ObjConstantsT6.h b/src/ObjCommon/Game/T6/ObjConstantsT6.h index 9935ba13f..76074c196 100644 --- a/src/ObjCommon/Game/T6/ObjConstantsT6.h +++ b/src/ObjCommon/Game/T6/ObjConstantsT6.h @@ -2,6 +2,8 @@ namespace T6 { + static constexpr auto INFO_STRING_PREFIX_DESTRUCTIBLE_DEF = "DESTRUCTIBLEDEF"; + static constexpr auto INFO_STRING_PREFIX_DESTRUCTIBLE_PIECE = "DESTRUCTIBLEPIECE"; static constexpr auto INFO_STRING_PREFIX_FLAME_TABLE = "FLAMETABLEFILE"; static constexpr auto INFO_STRING_PREFIX_PHYS_CONSTRAINTS = "PHYSCONSTRAINTS"; static constexpr auto INFO_STRING_PREFIX_PHYS_PRESET = "PHYSIC"; @@ -12,6 +14,8 @@ namespace T6 static constexpr auto INFO_STRING_PREFIX_WEAPON_ATTACHMENT_UNIQUE = "ATTACHMENTUNIQUEFILE"; static constexpr auto INFO_STRING_PREFIX_ZBARRIER = "ZBARRIER"; + static constexpr auto GDF_FILENAME_DESTRUCTIBLE_DEF = "destructibledef.gdf"; + static constexpr auto GDF_FILENAME_DESTRUCTIBLE_PIECE = "destructiblepiece.gdf"; static constexpr auto GDF_FILENAME_PHYS_CONSTRAINTS = "physconstraints.gdf"; static constexpr auto GDF_FILENAME_PHYS_PRESET = "physpreset.gdf"; static constexpr auto GDF_FILENAME_TRACER = "tracer.gdf"; diff --git a/src/ObjWriting/Game/T5/DestructibleDef/DestructibleDefInfoStringDumperT5.cpp b/src/ObjWriting/Game/T5/DestructibleDef/DestructibleDefInfoStringDumperT5.cpp new file mode 100644 index 000000000..5a2fc2685 --- /dev/null +++ b/src/ObjWriting/Game/T5/DestructibleDef/DestructibleDefInfoStringDumperT5.cpp @@ -0,0 +1,135 @@ +#include "DestructibleDefInfoStringDumperT5.h" + +#include "DestructibleDef/DestructibleDefCommon.h" +#include "Game/T5/DestructibleDef/DestructibleDefFields.h" +#include "Game/T5/InfoString/InfoStringFromStructConverter.h" +#include "Game/T5/ObjConstantsT5.h" + +#include +#include + +using namespace T5; + +namespace +{ + class InfoStringFromDestructiblePieceConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + assert(false); + } + + public: + InfoStringFromDestructiblePieceConverter(const DestructiblePiece* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + InfoString CreateDestructiblePieceInfoString(const DestructiblePiece* piece, const XAssetInfo& asset) + { + InfoStringFromDestructiblePieceConverter converter(piece, + destructible_piece_fields, + std::extent_v, + [&asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset.m_zone->m_script_strings.Count()); + if (scrStr >= asset.m_zone->m_script_strings.Count()) + return ""; + return asset.m_zone->m_script_strings[scrStr]; + }); + return converter.Convert(); + } + + class InfoStringFromDestructibleDefConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + assert(false); + } + + public: + InfoStringFromDestructibleDefConverter(const DestructibleDef* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + InfoString CreateInfoString(const XAssetInfo& asset) + { + InfoStringFromDestructibleDefConverter converter(asset.Asset(), + destructible_def_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset.m_zone->m_script_strings.Count()); + if (scrStr >= asset.m_zone->m_script_strings.Count()) + return ""; + + return asset.m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace destructible_def +{ + void InfoStringDumperT5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) + { + const auto* dDef = asset.Asset(); + + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset.m_name, GDF_FILENAME_DESTRUCTIBLE_DEF); + infoString.ToGdtProperties(INFO_STRING_PREFIX_DESTRUCTIBLE_DEF, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForDestructibleDef(asset.m_name)); + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(INFO_STRING_PREFIX_DESTRUCTIBLE_DEF); + stream.write(stringValue.c_str(), stringValue.size()); + } + + for (auto i = 0; i < dDef->numPieces; i++) + { + const auto* dPiece = &dDef->pieces[i]; + + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateDestructiblePieceInfoString(dPiece, asset); + GdtEntry gdtEntry(asset.m_name + "::piece_" + std::to_string(i), GDF_FILENAME_DESTRUCTIBLE_PIECE); + infoString.ToGdtProperties(INFO_STRING_PREFIX_DESTRUCTIBLE_PIECE, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForDestructiblePiece(asset.m_name, i)); + if (!assetFile) + continue; + + auto& stream = *assetFile; + const auto infoString = CreateDestructiblePieceInfoString(dPiece, asset); + const auto stringValue = infoString.ToString(INFO_STRING_PREFIX_DESTRUCTIBLE_PIECE); + stream.write(stringValue.c_str(), stringValue.size()); + } + } + } +} // namespace destructible_def diff --git a/src/ObjWriting/Game/T5/DestructibleDef/DestructibleDefInfoStringDumperT5.h b/src/ObjWriting/Game/T5/DestructibleDef/DestructibleDefInfoStringDumperT5.h new file mode 100644 index 000000000..856a74f8d --- /dev/null +++ b/src/ObjWriting/Game/T5/DestructibleDef/DestructibleDefInfoStringDumperT5.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T5/T5.h" + +namespace destructible_def +{ + class InfoStringDumperT5 final : public AbstractAssetDumper + { + protected: + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; + }; +} // namespace destructible_def diff --git a/src/ObjWriting/Game/T5/ObjWriterT5.cpp b/src/ObjWriting/Game/T5/ObjWriterT5.cpp index cd29a7fbb..6ccdf8524 100644 --- a/src/ObjWriting/Game/T5/ObjWriterT5.cpp +++ b/src/ObjWriting/Game/T5/ObjWriterT5.cpp @@ -1,5 +1,6 @@ #include "ObjWriterT5.h" +#include "DestructibleDef/DestructibleDefInfoStringDumperT5.h" #include "Game/T5/Font/FontDumperT5.h" #include "Game/T5/Image/ImageDumperT5.h" #include "Game/T5/Maps/MapEntsDumperT5.h" diff --git a/src/ObjWriting/Game/T6/DestructibleDef/DestructibleDefInfoStringDumperT6.cpp b/src/ObjWriting/Game/T6/DestructibleDef/DestructibleDefInfoStringDumperT6.cpp new file mode 100644 index 000000000..600393e7d --- /dev/null +++ b/src/ObjWriting/Game/T6/DestructibleDef/DestructibleDefInfoStringDumperT6.cpp @@ -0,0 +1,135 @@ +#include "DestructibleDefInfoStringDumperT6.h" + +#include "DestructibleDef/DestructibleDefCommon.h" +#include "Game/T6/DestructibleDef/DestructibleDefFields.h" +#include "Game/T6/InfoString/InfoStringFromStructConverter.h" +#include "Game/T6/ObjConstantsT6.h" + +#include +#include + +using namespace T6; + +namespace +{ + class InfoStringFromDestructiblePieceConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + assert(false); + } + + public: + InfoStringFromDestructiblePieceConverter(const DestructiblePiece* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + InfoString CreateDestructiblePieceInfoString(const DestructiblePiece* piece, const XAssetInfo& asset) + { + InfoStringFromDestructiblePieceConverter converter(piece, + destructible_piece_fields, + std::extent_v, + [&asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset.m_zone->m_script_strings.Count()); + if (scrStr >= asset.m_zone->m_script_strings.Count()) + return ""; + return asset.m_zone->m_script_strings[scrStr]; + }); + return converter.Convert(); + } + + class InfoStringFromDestructibleDefConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + assert(false); + } + + public: + InfoStringFromDestructibleDefConverter(const DestructibleDef* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + + InfoString CreateInfoString(const XAssetInfo& asset) + { + InfoStringFromDestructibleDefConverter converter(asset.Asset(), + destructible_def_fields, + std::extent_v, + [asset](const scr_string_t scrStr) -> std::string + { + assert(scrStr < asset.m_zone->m_script_strings.Count()); + if (scrStr >= asset.m_zone->m_script_strings.Count()) + return ""; + + return asset.m_zone->m_script_strings[scrStr]; + }); + + return converter.Convert(); + } +} // namespace + +namespace destructible_def +{ + void InfoStringDumperT6::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) + { + const auto* dest = asset.Asset(); + + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(asset); + GdtEntry gdtEntry(asset.m_name, GDF_FILENAME_DESTRUCTIBLE_DEF); + infoString.ToGdtProperties(INFO_STRING_PREFIX_DESTRUCTIBLE_DEF, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForDestructibleDef(asset.m_name)); + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(asset); + const auto stringValue = infoString.ToString(INFO_STRING_PREFIX_DESTRUCTIBLE_DEF); + stream.write(stringValue.c_str(), stringValue.size()); + } + + for (auto i = 0; i < dest->numPieces; i++) + { + const auto* piece = &dest->pieces[i]; + + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateDestructiblePieceInfoString(piece, asset); + GdtEntry gdtEntry(asset.m_name + "::piece_" + std::to_string(i), GDF_FILENAME_DESTRUCTIBLE_PIECE); + infoString.ToGdtProperties(INFO_STRING_PREFIX_DESTRUCTIBLE_PIECE, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForDestructiblePiece(asset.m_name, i)); + if (!assetFile) + continue; + + auto& stream = *assetFile; + const auto infoString = CreateDestructiblePieceInfoString(piece, asset); + const auto stringValue = infoString.ToString(INFO_STRING_PREFIX_DESTRUCTIBLE_PIECE); + stream.write(stringValue.c_str(), stringValue.size()); + } + } + } +} // namespace destructible_def diff --git a/src/ObjWriting/Game/T6/DestructibleDef/DestructibleDefInfoStringDumperT6.h b/src/ObjWriting/Game/T6/DestructibleDef/DestructibleDefInfoStringDumperT6.h new file mode 100644 index 000000000..1ef96e1e0 --- /dev/null +++ b/src/ObjWriting/Game/T6/DestructibleDef/DestructibleDefInfoStringDumperT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +namespace destructible_def +{ + class InfoStringDumperT6 final : public AbstractAssetDumper + { + protected: + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; + }; +} // namespace destructible_def diff --git a/src/ObjWriting/Game/T6/ObjWriterT6.cpp b/src/ObjWriting/Game/T6/ObjWriterT6.cpp index 05a30c1f1..e4415b30c 100644 --- a/src/ObjWriting/Game/T6/ObjWriterT6.cpp +++ b/src/ObjWriting/Game/T6/ObjWriterT6.cpp @@ -1,5 +1,6 @@ #include "ObjWriterT6.h" +#include "DestructibleDef/DestructibleDefInfoStringDumperT6.h" #include "FontIcon/FontIconDumperT6.h" #include "Game/T6/Font/FontDumperT6.h" #include "Game/T6/Image/ImageDumperT6.h" @@ -35,7 +36,7 @@ void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context) { RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); - // REGISTER_DUMPER(AssetDumperDestructibleDef, m_destructible_def) + RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique());