diff --git a/deps/iw4-open-formats b/deps/iw4-open-formats index 6d70c2ec..4fee3813 160000 --- a/deps/iw4-open-formats +++ b/deps/iw4-open-formats @@ -1 +1 @@ -Subproject commit 6d70c2ecfe3c34d9b3945fd87a90265e63763069 +Subproject commit 4fee3813071b11a95533ab8be975785ec6262358 diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index 76b21b2b..e753b934 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -40,6 +40,24 @@ namespace Assets } } + void Isnd_alias_list_t::dump(Game::XAssetHeader header) + { + Components::ZoneBuilder::GetExporter()->write(Game::XAssetType::ASSET_TYPE_SOUND, header.data); + } + + Isnd_alias_list_t::Isnd_alias_list_t() + { + Components::Command::Add("dumpSound", [this](const Components::Command::Params* param) + { + const auto header = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_SOUND, param->get(1)); + if (header.data) + { + Components::ZoneBuilder::RefreshExporterWorkDirectory(); + this->dump(header); + } + }); + } + void Isnd_alias_list_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { AssertSize(Game::snd_alias_list_t, 12); diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.hpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.hpp index d7f2337b..8a38cfa1 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.hpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.hpp @@ -10,5 +10,8 @@ namespace Assets void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; + void dump(Game::XAssetHeader header) override; + + Isnd_alias_list_t(); }; } diff --git a/src/Components/Modules/ModelSurfs.cpp b/src/Components/Modules/ModelSurfs.cpp index e7e888eb..55e30ba1 100644 --- a/src/Components/Modules/ModelSurfs.cpp +++ b/src/Components/Modules/ModelSurfs.cpp @@ -39,7 +39,8 @@ namespace Components Game::XModelSurfs* ModelSurfs::LoadXModelSurfaces(const std::string& name) { Utils::Memory::Allocator allocator; - FileSystem::FileReader model(std::format("models/{}", name)); + const auto path = std::format("models/{}", name); + FileSystem::FileReader model(path); if (!model.exists()) { @@ -57,7 +58,16 @@ namespace Components } #endif - Logger::Error(Game::ERR_FATAL, "Loading model {} failed!", name); + if (ZoneBuilder::IsEnabled()) + { + Logger::Print("Loading model surface {} at path \"{}\" failed!", name, path); + } + else + { + Logger::Error(Game::ERR_FATAL, "Loading model {} failed!", name); + } + + return nullptr; } Game::CModelHeader header; diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 6a26a07c..240c144a 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -18,6 +18,8 @@ namespace Components volatile bool ZoneBuilder::CommandThreadTerminate = false; std::thread ZoneBuilder::CommandThread; + iw4of::api ZoneBuilder::ExporterAPI(GetExporterAPIParams()); + std::string ZoneBuilder::DumpingZone{}; ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0), // Reserve 100MB by default. @@ -757,6 +759,23 @@ namespace Components return header; } + void ZoneBuilder::RefreshExporterWorkDirectory() + { + if (ZoneBuilder::DumpingZone.empty()) + { + ExporterAPI.set_work_path(std::format("userraw/dump/stray")); + } + else + { + ExporterAPI.set_work_path(std::format("userraw/dump/{}", ZoneBuilder::DumpingZone)); + } + } + + iw4of::api* ZoneBuilder::GetExporter() + { + return &ExporterAPI; + } + iw4of::params_t ZoneBuilder::Zone::getIW4OfApiParams() { iw4of::params_t params{}; @@ -815,7 +834,7 @@ namespace Components void* data = Utils::Memory::GetAllocator()->allocate(size); std::memcpy(data, *loadDef, size); - image->texture.loadDef = reinterpret_cast(data); + image->texture.loadDef = static_cast(data); return 0; } @@ -857,7 +876,7 @@ namespace Components return GetCurrentThreadId() == Utils::Hook::Get(0x1CDE7FC); } - static Game::XZoneInfo baseZones_old[] = + static Game::XZoneInfo baseZones[] = { { "code_pre_gfx_mp", Game::DB_ZONE_CODE, 0 }, { "localized_code_pre_gfx_mp", Game::DB_ZONE_CODE_LOC, 0 }, @@ -869,17 +888,6 @@ namespace Components { "localized_ui_mp", Game::DB_ZONE_GAME, 0 } }; - - static Game::XZoneInfo baseZones[] = - { - { "defaults", Game::DB_ZONE_CODE, 0 }, - { "techsets", Game::DB_ZONE_CODE, 0 }, - { "common_mp", Game::DB_ZONE_COMMON, 0 }, - { "localized_common_mp", Game::DB_ZONE_COMMON_LOC, 0 }, - { "ui_mp", Game::DB_ZONE_GAME, 0 }, - { "localized_ui_mp", Game::DB_ZONE_GAME, 0 } - }; - void ZoneBuilder::Com_Quitf_t() { ExitProcess(0); @@ -938,15 +946,7 @@ namespace Components Command::Add("quit", ZoneBuilder::Com_Quitf_t); // now load default assets and shaders - if (FastFiles::Exists("defaults") && FastFiles::Exists("techsets")) - { - Game::DB_LoadXAssets(baseZones, ARRAYSIZE(baseZones), 0); - } - else - { - Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing new init zones (defaults.ff & techsets.ff). You will need to load fastfiles to manually obtain techsets.\n"); - Game::DB_LoadXAssets(baseZones_old, ARRAYSIZE(baseZones_old), 0); - } + Game::DB_LoadXAssets(baseZones, ARRAYSIZE(baseZones), 0); Logger::Print("Waiting for fastiles to load...\n"); while (!Game::Sys_IsDatabaseReady()) @@ -983,6 +983,7 @@ namespace Components Logger::Print("\t-buildzone [zone]: builds a zone from a csv located in zone_source\n"); Logger::Print("\t-buildall: builds all zones in zone_source\n"); Logger::Print("\t-verifyzone [zone]: loads and verifies the specified zone\n"); + Logger::Print("\t-dumpzone [zone]: loads and dump the specified zone\n"); Logger::Print("\t-listassets [assettype]: lists all loaded assets of the specified type\n"); Logger::Print("\t-quit: quits the program\n"); Logger::Print(" --------------------------------------------------------------------------------\n"); @@ -1097,6 +1098,55 @@ namespace Components return file; } + iw4of::params_t ZoneBuilder::GetExporterAPIParams() + { + iw4of::params_t params{}; + + params.write_only_once = true; + + params.find_other_asset = [](int type, const std::string& name) -> void* + { + if (ZoneBuilder::DumpingZone.empty()) + { + return Game::DB_FindXAssetHeader(static_cast(type), name.data()).data; + } + + // Do not deadlock the DB + return nullptr; + }; + + params.fs_read_file = [](const std::string& filename) -> std::string + { + auto file = FileSystem::File(filename); + if (file.exists()) + { + return file.getBuffer(); + } + + return {}; + }; + + params.get_from_string_table = [](const unsigned int& id) -> std::string + { + return Game::SL_ConvertToString(static_cast(id)); + }; + + params.print = [](iw4of::params_t::print_type t, const std::string& message) -> void + { + switch (t) + { + case iw4of::params_t::P_ERR: + Logger::Error(Game::ERR_FATAL, "{}", message); + break; + case iw4of::params_t::P_WARN: + Logger::Print("{}", message); + break; + } + }; + + return params; + } + ZoneBuilder::ZoneBuilder() { // ReSharper disable CppStaticAssertFailure @@ -1200,7 +1250,7 @@ namespace Components // don't remap techsets Utils::Hook::Nop(0x5BC791, 5); - AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader /*asset*/, const std::string& name, bool* /*restrict*/) + AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, const std::string& name, bool* /*restrict*/) { if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current()) { @@ -1209,6 +1259,44 @@ namespace Components OutputDebugStringA(Utils::String::Format("%s\n", name)); #endif } + + if (!ZoneBuilder::DumpingZone.empty()) + { + if (ExporterAPI.is_type_supported(type) && name[0] != ',') + { + ExporterAPI.write(type, asset.data); + Components::Logger::Print("."); + } + } + }); + + Command::Add("dumpzone", [](const Command::Params* params) + { + if (params->size() < 2) return; + + std::string zone = params->get(1); + ZoneBuilder::DumpingZone = zone; + ZoneBuilder::RefreshExporterWorkDirectory(); + + Game::XZoneInfo info; + info.name = zone.data(); + info.allocFlags = Game::DB_ZONE_MOD; + info.freeFlags = 0; + + Logger::Print("Dumping zone '{}'...\n", zone); + + Game::DB_LoadXAssets(&info, 1, true); + AssetHandler::FindOriginalAsset(Game::ASSET_TYPE_RAWFILE, zone.data()); // Lock until zone is loaded + + Logger::Print("Unloading zone '{}'...\n", zone); + info.freeFlags = Game::DB_ZONE_MOD; + info.allocFlags = 0; + info.name = nullptr; + + Game::DB_LoadXAssets(&info, 1, true); + AssetHandler::FindOriginalAsset(Game::ASSET_TYPE_RAWFILE, "default"); // Lock until zone is unloaded + Logger::Print("Zone '{}' dumped", ZoneBuilder::DumpingZone); + ZoneBuilder::DumpingZone = std::string(); }); Command::Add("verifyzone", [](const Command::Params* params) @@ -1627,31 +1715,6 @@ namespace Components header.material->info.name, header.material->info.sortKey & 0xFF, header.material->info.gameFlags & 0xFF, header.material->stateFlags & 0xFF); }, nullptr, false); }); - - Command::Add("iwiDump", [](const Command::Params* params) - { - if (params->size() < 2) return; - - auto path = std::format("{}\\mods\\{}\\images", (*Game::fs_basepath)->current.string, params->get(1)); - auto images = FileSystem::GetSysFileList(path, "iwi", false); - - for (auto i = images.begin(); i != images.end();) - { - *i = std::format("images/{}", *i); - - if (FileSystem::File(*i).exists()) - { - i = images.erase(i); - continue; - } - - ++i; - } - - Logger::Print("------------------- BEGIN IWI DUMP -------------------\n"); - Logger::Print("{}\n", nlohmann::json(images).dump()); - Logger::Print("------------------- END IWI DUMP -------------------\n"); - }); } } diff --git a/src/Components/Modules/ZoneBuilder.hpp b/src/Components/Modules/ZoneBuilder.hpp index 2b5fe4de..c6fb46b6 100644 --- a/src/Components/Modules/ZoneBuilder.hpp +++ b/src/Components/Modules/ZoneBuilder.hpp @@ -137,6 +137,9 @@ namespace Components static std::vector> EndAssetTrace(); static Game::XAssetHeader GetEmptyAssetIfCommon(Game::XAssetType type, const std::string& name, Zone* builder); + static void RefreshExporterWorkDirectory(); + + static iw4of::api* GetExporter(); private: static int StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image); @@ -155,6 +158,8 @@ namespace Components static bool IsThreadMainThreadHook(); static Game::Sys_File Sys_CreateFile_Stub(const char* dir, const char* filename); + static iw4of::params_t GetExporterAPIParams(); + static void Com_Quitf_t(); static void CommandThreadCallback(); @@ -164,5 +169,7 @@ namespace Components static volatile bool CommandThreadTerminate; static std::thread CommandThread; + static iw4of::api ExporterAPI; + static std::string DumpingZone; }; } diff --git a/src/Components/Modules/Zones.cpp b/src/Components/Modules/Zones.cpp index 17226290..89bb812a 100644 --- a/src/Components/Modules/Zones.cpp +++ b/src/Components/Modules/Zones.cpp @@ -2947,6 +2947,9 @@ namespace Components iw4Map->mapEnts = &codolMapEnts; memcpy(&iw4Map->smodelNodeCount, &codolMap->smodelNodeCount, 48); + // unused on IW4 + iw4Map->numLeafSurfaces = 0; + AssetHandler::Relocate(&cancerMap->info.numCPlanes, &iw4Map->planeCount, 8); AssetHandler::Relocate(&cancerMap->numStaticModels, &iw4Map->numStaticModels, 8); AssetHandler::Relocate(&cancerMap->info.numMaterials, &iw4Map->numMaterials, 24);