From 3c15f8ad7e1f937bf7bc1cb946701da56f21b083 Mon Sep 17 00:00:00 2001 From: njohnson Date: Thu, 30 Apr 2026 19:14:53 -0400 Subject: [PATCH] feat: add iw5 gfxlight dumper and loader --- docs/SupportedAssetTypes.md | 2 +- .../Game/IW5/LightDef/LightDefLoaderIW5.cpp | 103 ++++++++++++++++++ .../Game/IW5/LightDef/LightDefLoaderIW5.h | 13 +++ src/ObjLoading/Game/IW5/ObjLoaderIW5.cpp | 3 +- .../Game/IW5/LightDef/LightDefDumperIW5.cpp | 31 ++++++ .../Game/IW5/LightDef/LightDefDumperIW5.h | 13 +++ src/ObjWriting/Game/IW5/ObjWriterIW5.cpp | 3 +- 7 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 src/ObjLoading/Game/IW5/LightDef/LightDefLoaderIW5.cpp create mode 100644 src/ObjLoading/Game/IW5/LightDef/LightDefLoaderIW5.h create mode 100644 src/ObjWriting/Game/IW5/LightDef/LightDefDumperIW5.cpp create mode 100644 src/ObjWriting/Game/IW5/LightDef/LightDefDumperIW5.h diff --git a/docs/SupportedAssetTypes.md b/docs/SupportedAssetTypes.md index 86ae5e34..0ee0b896 100644 --- a/docs/SupportedAssetTypes.md +++ b/docs/SupportedAssetTypes.md @@ -103,7 +103,7 @@ The following section specify which assets are supported to be dumped to disk (u | MapEnts | ❌ | ❌ | | | FxWorld | ❌ | ❌ | | | GfxWorld | ❌ | ❌ | | -| GfxLightDef | ❌ | ❌ | | +| GfxLightDef | ✅ | ✅ | | | Font_s | ❌ | ❌ | | | MenuList | ✅ | ✅ | The output is decompiled. The result will not be the same as the input. | | menuDef_t | ✅ | ✅ | See menulist. | diff --git a/src/ObjLoading/Game/IW5/LightDef/LightDefLoaderIW5.cpp b/src/ObjLoading/Game/IW5/LightDef/LightDefLoaderIW5.cpp new file mode 100644 index 00000000..42f16b04 --- /dev/null +++ b/src/ObjLoading/Game/IW5/LightDef/LightDefLoaderIW5.cpp @@ -0,0 +1,103 @@ +#include "LightDefLoaderIW5.h" + +#include "Game/IW5/IW5.h" +#include "LightDef/LightDefCommon.h" +#include "Utils/Logging/Log.h" + +#include +#include +#include + +using namespace IW5; + +namespace +{ + constexpr auto MAX_IMAGE_NAME_SIZE = 0x800; + + class LoaderLightDef final : public AssetCreator + { + public: + LoaderLightDef(MemoryManager& memory, ISearchPath& searchPath) + : m_memory(memory), + m_search_path(searchPath) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto filename = light_def::GetFileNameForAsset(assetName); + const auto file = m_search_path.Open(filename); + if (!file.IsOpen()) + return AssetCreationResult::NoAction(); + + auto* lightDef = m_memory.Alloc(); + lightDef->name = m_memory.Dup(assetName.c_str()); + + AssetRegistration registration(assetName, lightDef); + + int8_t attenuationSamplerState; + file.m_stream->read(reinterpret_cast(&attenuationSamplerState), sizeof(int8_t)); + + std::string attenuationName = ""; + unsigned char letter; + file.m_stream->read(reinterpret_cast(&letter), sizeof(int8_t)); + while (letter != '\0') + { + attenuationName += letter; + + file.m_stream->read(reinterpret_cast(&letter), sizeof(int8_t)); + } + + auto* attenuationImageDependency = context.LoadDependency(attenuationName); + if (!attenuationImageDependency) + { + con::error("Could not load GfxLightDef \"{}\" due to missing attenuation image \"{}\"", assetName, attenuationName); + return AssetCreationResult::Failure(); + } + registration.AddDependency(attenuationImageDependency); + + int8_t cucolorisSamplerState; + file.m_stream->read(reinterpret_cast(&cucolorisSamplerState), sizeof(int8_t)); + + std::string cucolorisName = ""; + file.m_stream->read(reinterpret_cast(&letter), sizeof(int8_t)); + while (letter != '\0') + { + cucolorisName += letter; + + file.m_stream->read(reinterpret_cast(&cucolorisName), sizeof(int8_t)); + } + + auto* cucolorisImageDependency = context.LoadDependency(cucolorisName); + if (!cucolorisImageDependency) + { + con::error("Could not load GfxLightDef \"{}\" due to missing cucoloris image \"{}\"", assetName, cucolorisName); + return AssetCreationResult::Failure(); + } + registration.AddDependency(cucolorisImageDependency); + + int8_t lmapLookupStart; + file.m_stream->read(reinterpret_cast(&lmapLookupStart), sizeof(int8_t)); + + lightDef->attenuation.samplerState = attenuationSamplerState; + lightDef->attenuation.image = attenuationImageDependency->Asset(); + lightDef->cucoloris.samplerState = cucolorisSamplerState; + lightDef->cucoloris.image = cucolorisImageDependency->Asset(); + lightDef->lmapLookupStart = static_cast(static_cast(lmapLookupStart)); + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + }; +} // namespace + +namespace light_def +{ + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath) + { + return std::make_unique(memory, searchPath); + } +} // namespace light_def diff --git a/src/ObjLoading/Game/IW5/LightDef/LightDefLoaderIW5.h b/src/ObjLoading/Game/IW5/LightDef/LightDefLoaderIW5.h new file mode 100644 index 00000000..07f0454a --- /dev/null +++ b/src/ObjLoading/Game/IW5/LightDef/LightDefLoaderIW5.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/IW5/IW5.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace light_def +{ + std::unique_ptr> CreateLoaderIW5(MemoryManager& memory, ISearchPath& searchPath); +} // namespace light_def diff --git a/src/ObjLoading/Game/IW5/ObjLoaderIW5.cpp b/src/ObjLoading/Game/IW5/ObjLoaderIW5.cpp index a015ee7d..2b5ada65 100644 --- a/src/ObjLoading/Game/IW5/ObjLoaderIW5.cpp +++ b/src/ObjLoading/Game/IW5/ObjLoaderIW5.cpp @@ -10,6 +10,7 @@ #include "Game/IW5/Techset/VertexShaderLoaderIW5.h" #include "Game/IW5/XModel/LoaderXModelIW5.h" #include "Leaderboard/LoaderLeaderboardIW5.h" +#include "LightDef/LightDefLoaderIW5.h" #include "Localize/LoaderLocalizeIW5.h" #include "Material/LoaderMaterialIW5.h" #include "Menu/LoaderMenuListIW5.h" @@ -151,7 +152,7 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - // collection.AddAssetCreator(std::make_unique(memory)); + collection.AddAssetCreator(light_def::CreateLoaderIW5(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); collection.AddAssetCreator(menu::CreateMenuListLoaderIW5(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); diff --git a/src/ObjWriting/Game/IW5/LightDef/LightDefDumperIW5.cpp b/src/ObjWriting/Game/IW5/LightDef/LightDefDumperIW5.cpp new file mode 100644 index 00000000..4b0e912f --- /dev/null +++ b/src/ObjWriting/Game/IW5/LightDef/LightDefDumperIW5.cpp @@ -0,0 +1,31 @@ +#include "LightDefDumperIW5.h" + +#include "LightDef/LightDefCommon.h" + +using namespace IW5; + +namespace light_def +{ + void DumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) + { + const auto* lightDef = asset.Asset(); + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset.m_name)); + + if (!assetFile || lightDef->attenuation.image == nullptr || lightDef->attenuation.image->name == nullptr || lightDef->cucoloris.image == nullptr + || lightDef->cucoloris.image->name == nullptr) + return; + + auto& stream = *assetFile; + + const auto* attenuationImageName = lightDef->attenuation.image->name; + if (attenuationImageName[0] == ',') + attenuationImageName = &attenuationImageName[1]; + + const auto* cucolorisImageName = lightDef->cucoloris.image->name; + if (cucolorisImageName[0] == ',') + cucolorisImageName = &cucolorisImageName[1]; + + stream << lightDef->attenuation.samplerState << attenuationImageName << '\0' << lightDef->cucoloris.samplerState << cucolorisImageName + << static_cast(lightDef->lmapLookupStart); + } +} // namespace light_def diff --git a/src/ObjWriting/Game/IW5/LightDef/LightDefDumperIW5.h b/src/ObjWriting/Game/IW5/LightDef/LightDefDumperIW5.h new file mode 100644 index 00000000..bf325e76 --- /dev/null +++ b/src/ObjWriting/Game/IW5/LightDef/LightDefDumperIW5.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +namespace light_def +{ + class DumperIW5 final : public AbstractAssetDumper + { + protected: + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; + }; +} // namespace light_def diff --git a/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp b/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp index 92e33973..db308f2f 100644 --- a/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp +++ b/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp @@ -7,6 +7,7 @@ #include "Game/IW5/XModel/XModelDumperIW5.h" #include "Image/ImageDumperIW5.h" #include "Leaderboard/LeaderboardJsonDumperIW5.h" +#include "LightDef/LightDefDumperIW5.h" #include "Localize/LocalizeDumperIW5.h" #include "Maps/AddonMapEntsDumperIW5.h" #include "Menu/MenuDumperIW5.h" @@ -51,7 +52,7 @@ void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context) // REGISTER_DUMPER(AssetDumperMapEnts) // REGISTER_DUMPER(AssetDumperFxWorld) // REGISTER_DUMPER(AssetDumperGfxWorld) - // REGISTER_DUMPER(AssetDumperGfxLightDef) + RegisterAssetDumper(std::make_unique()); // REGISTER_DUMPER(AssetDumperFont_s) RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique());