diff --git a/docs/SupportedAssetTypes.md b/docs/SupportedAssetTypes.md index 3d51b23b..f203c775 100644 --- a/docs/SupportedAssetTypes.md +++ b/docs/SupportedAssetTypes.md @@ -127,7 +127,7 @@ using `Linker`): | Asset Type | Dumping Support | Loading Support | Notes | |----------------------|-----------------|-----------------|------------------------------------------------------------------------------| -| PhysPreset | ❌ | ❌ | | +| PhysPreset | ✅ | ✅ | | | PhysConstraints | ✅ | ❌ | | | DestructibleDef | ❌ | ❌ | | | XAnimParts | ✅ | ✅ | | diff --git a/src/ObjCommon/Game/T4/ObjConstantsT4.h b/src/ObjCommon/Game/T4/ObjConstantsT4.h index 5aff1828..7caa141b 100644 --- a/src/ObjCommon/Game/T4/ObjConstantsT4.h +++ b/src/ObjCommon/Game/T4/ObjConstantsT4.h @@ -4,8 +4,12 @@ namespace T4 { 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"; static constexpr auto INFO_STRING_PREFIX_WEAPON = "WEAPONFILE"; static constexpr auto GDF_FILENAME_PHYS_CONSTRAINTS = "physconstraints.gdf"; + static constexpr auto GDF_FILENAME_PHYS_PRESET = "physpreset.gdf"; static constexpr auto GDF_FILENAME_WEAPON = "weapon.gdf"; + + static constexpr float PHYS_PRESET_MAX_FRICTION = 1e+10f; } // namespace T4 diff --git a/src/ObjCommon/Game/T4/PhysPreset/PhysPresetFields.h b/src/ObjCommon/Game/T4/PhysPreset/PhysPresetFields.h new file mode 100644 index 00000000..7ac37d0e --- /dev/null +++ b/src/ObjCommon/Game/T4/PhysPreset/PhysPresetFields.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Game/T4/T4.h" + +namespace T4 +{ + inline cspField_t phys_preset_fields[]{ + {"mass", offsetof(PhysPresetInfo, mass), CSPFT_FLOAT }, + {"bounce", offsetof(PhysPresetInfo, bounce), CSPFT_FLOAT }, + {"friction", offsetof(PhysPresetInfo, friction), CSPFT_FLOAT }, + {"isFrictionInfinity", offsetof(PhysPresetInfo, isFrictionInfinity), CSPFT_BOOL }, + {"bulletForceScale", offsetof(PhysPresetInfo, bulletForceScale), CSPFT_FLOAT }, + {"explosiveForceScale", offsetof(PhysPresetInfo, explosiveForceScale), CSPFT_FLOAT }, + {"sndAliasPrefix", offsetof(PhysPresetInfo, sndAliasPrefix), CSPFT_STRING}, + {"piecesSpreadFraction", offsetof(PhysPresetInfo, piecesSpreadFraction), CSPFT_FLOAT }, + {"piecesUpwardVelocity", offsetof(PhysPresetInfo, piecesUpwardVelocity), CSPFT_FLOAT }, + {"canFloat", offsetof(PhysPresetInfo, canFloat), CSPFT_BOOL }, + {"gravityScale", offsetof(PhysPresetInfo, gravityScale), CSPFT_FLOAT }, + }; +} diff --git a/src/ObjLoading/Game/T4/ObjLoaderT4.cpp b/src/ObjLoading/Game/T4/ObjLoaderT4.cpp index 8740be9e..d666481b 100644 --- a/src/ObjLoading/Game/T4/ObjLoaderT4.cpp +++ b/src/ObjLoading/Game/T4/ObjLoaderT4.cpp @@ -11,6 +11,8 @@ #include "Localize/AssetLoaderLocalizeT4.h" #include "Maps/MapEntsLoaderT4.h" #include "Material/LoaderMaterialT4.h" +#include "PhysPreset/GdtLoaderPhysPresetT4.h" +#include "PhysPreset/RawLoaderPhysPresetT4.h" #include "RawFile/AssetLoaderRawFileT4.h" #include "Weapon/FlameTableLoaderT4.h" #include "Weapon/WeaponGdtLoaderT4.h" @@ -103,6 +105,8 @@ namespace collection.AddAssetCreator(font::CreateLoaderT4(memory, searchPath)); collection.AddAssetCreator(localize::CreateLoaderT4(memory, searchPath, zone)); collection.AddAssetCreator(map_ents::CreateLoaderT4(memory, searchPath)); + collection.AddAssetCreator(phys_preset::CreateRawLoaderT4(memory, searchPath, zone)); + collection.AddAssetCreator(phys_preset::CreateGdtLoaderT4(memory, gdt, zone)); collection.AddAssetCreator(raw_file::CreateLoaderT4(memory, searchPath)); collection.AddAssetCreator(weapon::CreateRawLoaderT4(memory, searchPath, zone)); collection.AddAssetCreator(weapon::CreateGdtLoaderT4(memory, searchPath, gdt, zone)); diff --git a/src/ObjLoading/Game/T4/PhysPreset/GdtLoaderPhysPresetT4.cpp b/src/ObjLoading/Game/T4/PhysPreset/GdtLoaderPhysPresetT4.cpp new file mode 100644 index 00000000..0a20dca0 --- /dev/null +++ b/src/ObjLoading/Game/T4/PhysPreset/GdtLoaderPhysPresetT4.cpp @@ -0,0 +1,50 @@ +#include "GdtLoaderPhysPresetT4.h" + +#include "Game/T4/ObjConstantsT4.h" +#include "Game/T4/T4.h" +#include "InfoString/InfoString.h" +#include "InfoStringLoaderPhysPresetT4.h" +#include "Utils/Logging/Log.h" + +using namespace T4; + +namespace +{ + class GdtLoaderPhysPreset final : public AssetCreator + { + public: + GdtLoaderPhysPreset(MemoryManager& memory, IGdtQueryable& gdt, Zone& zone) + : m_gdt(gdt), + m_info_string_loader(memory, zone) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto* gdtEntry = m_gdt.GetGdtEntryByGdfAndName(GDF_FILENAME_PHYS_PRESET, assetName); + if (gdtEntry == nullptr) + return AssetCreationResult::NoAction(); + + InfoString infoString; + if (!infoString.FromGdtProperties(*gdtEntry)) + { + con::error("Failed to read phys preset gdt entry: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + return m_info_string_loader.CreateAsset(assetName, infoString, context); + } + + private: + IGdtQueryable& m_gdt; + phys_preset::InfoStringLoaderT4 m_info_string_loader; + }; +} // namespace + +namespace phys_preset +{ + std::unique_ptr> CreateGdtLoaderT4(MemoryManager& memory, IGdtQueryable& gdt, Zone& zone) + { + return std::make_unique(memory, gdt, zone); + } +} // namespace phys_preset diff --git a/src/ObjLoading/Game/T4/PhysPreset/GdtLoaderPhysPresetT4.h b/src/ObjLoading/Game/T4/PhysPreset/GdtLoaderPhysPresetT4.h new file mode 100644 index 00000000..f344450b --- /dev/null +++ b/src/ObjLoading/Game/T4/PhysPreset/GdtLoaderPhysPresetT4.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T4/T4.h" +#include "Gdt/IGdtQueryable.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace phys_preset +{ + std::unique_ptr> CreateGdtLoaderT4(MemoryManager& memory, IGdtQueryable& gdt, Zone& zone); +} // namespace phys_preset diff --git a/src/ObjLoading/Game/T4/PhysPreset/InfoStringLoaderPhysPresetT4.cpp b/src/ObjLoading/Game/T4/PhysPreset/InfoStringLoaderPhysPresetT4.cpp new file mode 100644 index 00000000..283370bb --- /dev/null +++ b/src/ObjLoading/Game/T4/PhysPreset/InfoStringLoaderPhysPresetT4.cpp @@ -0,0 +1,94 @@ +#include "InfoStringLoaderPhysPresetT4.h" + +#include "Game/T4/InfoString/InfoStringToStructConverter.h" +#include "Game/T4/ObjConstantsT4.h" +#include "Game/T4/PhysPreset/PhysPresetFields.h" +#include "Game/T4/T4.h" +#include "Utils/Logging/Log.h" + +#include +#include +#include + +using namespace T4; + +namespace +{ + class InfoStringToPhysPresetConverter final : public InfoStringToStructConverter + { + protected: + bool ConvertExtensionField(const cspField_t& field, const std::string& value) override + { + assert(false); + return false; + } + + public: + InfoStringToPhysPresetConverter(const InfoString& infoString, + PhysPresetInfo& physPreset, + ZoneScriptStrings& zoneScriptStrings, + MemoryManager& memory, + AssetCreationContext& context, + AssetRegistration& registration, + const cspField_t* fields, + const size_t fieldCount) + : InfoStringToStructConverter(infoString, &physPreset, zoneScriptStrings, memory, context, registration, fields, fieldCount) + { + } + }; + + void CopyFromPhysPresetInfo(const PhysPresetInfo& physPresetInfo, PhysPreset& physPreset) + { + physPreset.mass = std::clamp(physPresetInfo.mass, 1.0f, 2000.0f) * 0.001f; + physPreset.bounce = physPresetInfo.bounce; + + if (physPresetInfo.isFrictionInfinity != 0) + physPreset.friction = PHYS_PRESET_MAX_FRICTION; + else + physPreset.friction = physPresetInfo.friction; + + physPreset.bulletForceScale = physPresetInfo.bulletForceScale; + physPreset.explosiveForceScale = physPresetInfo.explosiveForceScale; + physPreset.sndAliasPrefix = physPresetInfo.sndAliasPrefix; + physPreset.piecesSpreadFraction = physPresetInfo.piecesSpreadFraction; + physPreset.piecesUpwardVelocity = physPresetInfo.piecesUpwardVelocity; + physPreset.canFloat = physPresetInfo.canFloat; + physPreset.gravityScale = std::clamp(physPresetInfo.gravityScale, 0.01f, 10.0f); + } +} // namespace + +namespace phys_preset +{ + InfoStringLoaderT4::InfoStringLoaderT4(MemoryManager& memory, Zone& zone) + : m_memory(memory), + m_zone(zone) + { + } + + AssetCreationResult InfoStringLoaderT4::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) + { + auto* physPreset = m_memory.Alloc(); + physPreset->name = m_memory.Dup(assetName.c_str()); + + AssetRegistration registration(assetName, physPreset); + + PhysPresetInfo physPresetInfo{}; + InfoStringToPhysPresetConverter converter(infoString, + physPresetInfo, + m_zone.m_script_strings, + m_memory, + context, + registration, + phys_preset_fields, + std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse phys preset: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + CopyFromPhysPresetInfo(physPresetInfo, *physPreset); + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } +} // namespace phys_preset diff --git a/src/ObjLoading/Game/T4/PhysPreset/InfoStringLoaderPhysPresetT4.h b/src/ObjLoading/Game/T4/PhysPreset/InfoStringLoaderPhysPresetT4.h new file mode 100644 index 00000000..365824dd --- /dev/null +++ b/src/ObjLoading/Game/T4/PhysPreset/InfoStringLoaderPhysPresetT4.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Asset/AssetCreationContext.h" +#include "Asset/AssetCreationResult.h" +#include "InfoString/InfoString.h" + +namespace phys_preset +{ + class InfoStringLoaderT4 + { + public: + InfoStringLoaderT4(MemoryManager& memory, Zone& zone); + + AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context); + + private: + MemoryManager& m_memory; + Zone& m_zone; + }; +} // namespace phys_preset diff --git a/src/ObjLoading/Game/T4/PhysPreset/RawLoaderPhysPresetT4.cpp b/src/ObjLoading/Game/T4/PhysPreset/RawLoaderPhysPresetT4.cpp new file mode 100644 index 00000000..5aa56b4c --- /dev/null +++ b/src/ObjLoading/Game/T4/PhysPreset/RawLoaderPhysPresetT4.cpp @@ -0,0 +1,52 @@ +#include "RawLoaderPhysPresetT4.h" + +#include "Game/T4/ObjConstantsT4.h" +#include "Game/T4/T4.h" +#include "InfoString/InfoString.h" +#include "InfoStringLoaderPhysPresetT4.h" +#include "PhysPreset/PhysPresetCommon.h" +#include "Utils/Logging/Log.h" + +using namespace T4; + +namespace +{ + class RawLoaderPhysPreset final : public AssetCreator + { + public: + RawLoaderPhysPreset(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_search_path(searchPath), + m_info_string_loader(memory, zone) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto fileName = phys_preset::GetFileNameForAssetName(assetName); + const auto file = m_search_path.Open(fileName); + if (!file.IsOpen()) + return AssetCreationResult::NoAction(); + + InfoString infoString; + if (!infoString.FromStream(INFO_STRING_PREFIX_PHYS_PRESET, *file.m_stream)) + { + con::error("Could not parse as info string file: \"{}\"", fileName); + return AssetCreationResult::Failure(); + } + + return m_info_string_loader.CreateAsset(assetName, infoString, context); + } + + private: + ISearchPath& m_search_path; + phys_preset::InfoStringLoaderT4 m_info_string_loader; + }; +} // namespace + +namespace phys_preset +{ + std::unique_ptr> CreateRawLoaderT4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + { + return std::make_unique(memory, searchPath, zone); + } +} // namespace phys_preset diff --git a/src/ObjLoading/Game/T4/PhysPreset/RawLoaderPhysPresetT4.h b/src/ObjLoading/Game/T4/PhysPreset/RawLoaderPhysPresetT4.h new file mode 100644 index 00000000..1270e89d --- /dev/null +++ b/src/ObjLoading/Game/T4/PhysPreset/RawLoaderPhysPresetT4.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T4/T4.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace phys_preset +{ + std::unique_ptr> CreateRawLoaderT4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace phys_preset diff --git a/src/ObjWriting/Game/T4/ObjWriterT4.cpp b/src/ObjWriting/Game/T4/ObjWriterT4.cpp index a9c6a3cc..de5cd741 100644 --- a/src/ObjWriting/Game/T4/ObjWriterT4.cpp +++ b/src/ObjWriting/Game/T4/ObjWriterT4.cpp @@ -8,6 +8,7 @@ #include "Game/T4/XModel/XModelDumperT4.h" #include "Localize/LocalizeDumperT4.h" #include "PhysConstraints/PhysConstraintsInfoStringDumperT4.h" +#include "PhysPreset/PhysPresetInfoStringDumperT4.h" #include "RawFile/RawFileDumperT4.h" #include "Sound/LoadedSoundDumperT4.h" #include "StringTable/StringTableDumperT4.h" @@ -18,6 +19,7 @@ using namespace T4; void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context) { RegisterAssetDumper(std::make_unique()); + RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); diff --git a/src/ObjWriting/Game/T4/PhysPreset/PhysPresetInfoStringDumperT4.cpp b/src/ObjWriting/Game/T4/PhysPreset/PhysPresetInfoStringDumperT4.cpp new file mode 100644 index 00000000..29a43b12 --- /dev/null +++ b/src/ObjWriting/Game/T4/PhysPreset/PhysPresetInfoStringDumperT4.cpp @@ -0,0 +1,91 @@ +#include "PhysPresetInfoStringDumperT4.h" + +#include "Game/T4/InfoString/InfoStringFromStructConverter.h" +#include "Game/T4/ObjConstantsT4.h" +#include "Game/T4/PhysPreset/PhysPresetFields.h" +#include "PhysPreset/PhysPresetCommon.h" + +#include +#include +#include + +using namespace T4; + +namespace +{ + class InfoStringFromPhysPresetConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + assert(false); + } + + public: + InfoStringFromPhysPresetConverter(const PhysPresetInfo* structure, const cspField_t* fields, const size_t fieldCount) + : InfoStringFromStructConverter(structure, fields, fieldCount) + { + } + }; + + void CopyToPhysPresetInfo(const PhysPreset& physPreset, PhysPresetInfo& physPresetInfo) + { + physPresetInfo.mass = std::clamp(physPreset.mass * 1000.0f, 1.0f, 2000.0f); + physPresetInfo.bounce = physPreset.bounce; + + if (physPreset.friction >= PHYS_PRESET_MAX_FRICTION) + { + physPresetInfo.isFrictionInfinity = 1; + physPresetInfo.friction = 0; + } + else + { + physPresetInfo.isFrictionInfinity = 0; + physPresetInfo.friction = physPreset.friction; + } + + physPresetInfo.bulletForceScale = physPreset.bulletForceScale; + physPresetInfo.explosiveForceScale = physPreset.explosiveForceScale; + physPresetInfo.sndAliasPrefix = physPreset.sndAliasPrefix; + physPresetInfo.piecesSpreadFraction = physPreset.piecesSpreadFraction; + physPresetInfo.piecesUpwardVelocity = physPreset.piecesUpwardVelocity; + physPresetInfo.canFloat = physPreset.canFloat; + physPresetInfo.gravityScale = std::clamp(physPreset.gravityScale, 0.01f, 10.0f); + } + + InfoString CreateInfoString(const PhysPreset& physPreset) + { + PhysPresetInfo physPresetInfo{}; + CopyToPhysPresetInfo(physPreset, physPresetInfo); + + InfoStringFromPhysPresetConverter converter(&physPresetInfo, phys_preset_fields, std::extent_v); + return converter.Convert(); + } +} // namespace + +namespace phys_preset +{ + void InfoStringDumperT4::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) + { + // Only dump raw when no gdt available + if (context.m_gdt) + { + const auto infoString = CreateInfoString(*asset.Asset()); + GdtEntry gdtEntry(asset.m_name, GDF_FILENAME_PHYS_PRESET); + infoString.ToGdtProperties(INFO_STRING_PREFIX_PHYS_PRESET, gdtEntry); + context.m_gdt->WriteEntry(gdtEntry); + } + else + { + const auto assetFile = context.OpenAssetFile(GetFileNameForAssetName(asset.m_name)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + const auto infoString = CreateInfoString(*asset.Asset()); + const auto stringValue = infoString.ToString(INFO_STRING_PREFIX_PHYS_PRESET); + stream.write(stringValue.c_str(), stringValue.size()); + } + } +} // namespace phys_preset diff --git a/src/ObjWriting/Game/T4/PhysPreset/PhysPresetInfoStringDumperT4.h b/src/ObjWriting/Game/T4/PhysPreset/PhysPresetInfoStringDumperT4.h new file mode 100644 index 00000000..e7bd0870 --- /dev/null +++ b/src/ObjWriting/Game/T4/PhysPreset/PhysPresetInfoStringDumperT4.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T4/T4.h" + +namespace phys_preset +{ + class InfoStringDumperT4 final : public AbstractAssetDumper + { + protected: + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; + }; +} // namespace phys_preset diff --git a/test/SystemTests/Game/T4/PhysPresetT4.cpp b/test/SystemTests/Game/T4/PhysPresetT4.cpp new file mode 100644 index 00000000..eff3ee12 --- /dev/null +++ b/test/SystemTests/Game/T4/PhysPresetT4.cpp @@ -0,0 +1,112 @@ +#include "Game/T4/ObjConstantsT4.h" +#include "Game/T4/PhysPreset/PhysPresetInfoStringDumperT4.h" +#include "Game/T4/PhysPreset/RawLoaderPhysPresetT4.h" +#include "InfoString/InfoString.h" +#include "SearchPath/MockOutputPath.h" +#include "SearchPath/MockSearchPath.h" +#include "ZoneLoading.h" + +#include +#include +#include +#include + +using namespace T4; + +namespace +{ + constexpr auto PHYS_PRESET_NAME = "test_phys_preset"; + constexpr auto SOUND_ALIAS_PREFIX = "physics_metal"; + + std::unique_ptr CreateTestPhysPreset() + { + auto physPreset = std::make_unique(); + physPreset->name = PHYS_PRESET_NAME; + physPreset->mass = 1.25f; + physPreset->bounce = 0.25f; + physPreset->friction = PHYS_PRESET_MAX_FRICTION; + physPreset->bulletForceScale = 0.5f; + physPreset->explosiveForceScale = 0.75f; + physPreset->sndAliasPrefix = SOUND_ALIAS_PREFIX; + physPreset->piecesSpreadFraction = 0.125f; + physPreset->piecesUpwardVelocity = 64.0f; + physPreset->canFloat = 1; + physPreset->gravityScale = 2.0f; + return physPreset; + } + + std::string DumpPhysPreset(PhysPreset& physPreset) + { + Zone dumpingZone("DumpingZone", 0, GameId::T4, GamePlatform::PC); + dumpingZone.m_pools.AddAsset(std::make_unique>(ASSET_TYPE_PHYSPRESET, physPreset.name, &physPreset)); + + MockSearchPath dumpingObjPath; + MockOutputPath dumpingOutput; + AssetDumpingContext dumpingContext(dumpingZone, "", dumpingOutput, dumpingObjPath, std::nullopt); + phys_preset::InfoStringDumperT4 dumper; + dumper.Dump(dumpingContext); + + const auto* dumpedFile = dumpingOutput.GetMockedFile("physic/test_phys_preset"); + REQUIRE(dumpedFile != nullptr); + return dumpedFile->AsString(); + } + + TEST_CASE("PhysPreset dumper serializes raw fields (T4)", "[t4][physpreset][system]") + { + const auto testPhysPreset = CreateTestPhysPreset(); + std::istringstream dumpedStream(DumpPhysPreset(*testPhysPreset)); + + InfoString infoString; + REQUIRE(infoString.FromStream(INFO_STRING_PREFIX_PHYS_PRESET, dumpedStream)); + REQUIRE(infoString.GetValueForKey("mass") == "1250"); + REQUIRE(infoString.GetValueForKey("bounce") == "0.25"); + REQUIRE(infoString.GetValueForKey("friction") == "0"); + REQUIRE(infoString.GetValueForKey("isFrictionInfinity") == "1"); + REQUIRE(infoString.GetValueForKey("bulletForceScale") == "0.5"); + REQUIRE(infoString.GetValueForKey("explosiveForceScale") == "0.75"); + REQUIRE(infoString.GetValueForKey("sndAliasPrefix") == SOUND_ALIAS_PREFIX); + REQUIRE(infoString.GetValueForKey("piecesSpreadFraction") == "0.125"); + REQUIRE(infoString.GetValueForKey("piecesUpwardVelocity") == "64"); + REQUIRE(infoString.GetValueForKey("canFloat") == "1"); + REQUIRE(infoString.GetValueForKey("gravityScale") == "2"); + + auto foundObsoleteField = false; + infoString.GetValueForKey("tempDefaultToCylinder", &foundObsoleteField); + REQUIRE_FALSE(foundObsoleteField); + } + + TEST_CASE("PhysPreset loader restores stock raw fields (T4)", "[t4][physpreset][system]") + { + constexpr auto RAW_PHYS_PRESET = R"(PHYSIC\sndAliasPrefix\physics_metal)" + R"(\mass\1250\friction\0.5\isFrictionInfinity\1\bounce\0.25)" + R"(\bulletForceScale\0.5\explosiveForceScale\0.75)" + R"(\piecesSpreadFraction\0.125\piecesUpwardVelocity\64)" + R"(\tempDefaultToCylinder\1\canFloat\1\gravityScale\2)"; + + MockSearchPath loadingSearchPath; + loadingSearchPath.AddFileData("physic/test_phys_preset", RAW_PHYS_PRESET); + + Zone loadingZone("LoadingZone", 0, GameId::T4, GamePlatform::PC); + AssetCreatorCollection creatorCollection(loadingZone); + IgnoredAssetLookup ignoredAssetLookup; + AssetCreationContext loadingContext(loadingZone, &creatorCollection, &ignoredAssetLookup); + const auto loader = phys_preset::CreateRawLoaderT4(loadingZone.Memory(), loadingSearchPath, loadingZone); + const auto result = loader->CreateAsset(PHYS_PRESET_NAME, loadingContext); + + REQUIRE(result.HasBeenSuccessful()); + const auto* loadedAssetInfo = reinterpret_cast*>(result.GetAssetInfo()); + const auto* loadedPhysPreset = loadedAssetInfo->Asset(); + + REQUIRE(std::string(loadedPhysPreset->name) == PHYS_PRESET_NAME); + REQUIRE(loadedPhysPreset->mass == 1.25f); + REQUIRE(loadedPhysPreset->bounce == 0.25f); + REQUIRE(loadedPhysPreset->friction == PHYS_PRESET_MAX_FRICTION); + REQUIRE(loadedPhysPreset->bulletForceScale == 0.5f); + REQUIRE(loadedPhysPreset->explosiveForceScale == 0.75f); + REQUIRE(std::string(loadedPhysPreset->sndAliasPrefix) == SOUND_ALIAS_PREFIX); + REQUIRE(loadedPhysPreset->piecesSpreadFraction == 0.125f); + REQUIRE(loadedPhysPreset->piecesUpwardVelocity == 64.0f); + REQUIRE(loadedPhysPreset->canFloat == 1); + REQUIRE(loadedPhysPreset->gravityScale == 2.0f); + } +} // namespace