diff --git a/.gitmodules b/.gitmodules index c53bfe41..95e898f6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,43 +1,46 @@ -[submodule "deps/zlib"] - path = deps/zlib - url = https://github.com/madler/zlib.git - branch = develop -[submodule "deps/libtommath"] - path = deps/libtommath - url = https://github.com/libtom/libtommath.git - branch = develop -[submodule "deps/libtomcrypt"] - path = deps/libtomcrypt - url = https://github.com/libtom/libtomcrypt.git - branch = develop -[submodule "deps/pdcurses"] - path = deps/pdcurses - url = https://github.com/wmcbrine/PDCurses.git - branch = master -[submodule "deps/mongoose"] - path = deps/mongoose - url = https://github.com/cesanta/mongoose.git - branch = 7.9 -[submodule "deps/protobuf"] - path = deps/protobuf - url = https://github.com/google/protobuf.git - branch = 3.20.x -[submodule "deps/udis86"] - path = deps/udis86 - url = https://github.com/vmt/udis86.git -[submodule "deps/dxsdk"] - path = deps/dxsdk - url = https://github.com/devKlausS/dxsdk.git -[submodule "deps/GSL"] - path = deps/GSL - url = https://github.com/microsoft/GSL.git -[submodule "deps/nlohmannjson"] - path = deps/json - url = https://github.com/nlohmann/json.git - branch = v3.11.2 -[submodule "deps/discord-rpc"] - path = deps/discord-rpc - url = https://github.com/discord/discord-rpc.git -[submodule "deps/rapidjson"] - path = deps/rapidjson - url = https://github.com/Tencent/rapidjson.git +[submodule "deps/zlib"] + path = deps/zlib + url = https://github.com/madler/zlib.git + branch = develop +[submodule "deps/libtommath"] + path = deps/libtommath + url = https://github.com/libtom/libtommath.git + branch = develop +[submodule "deps/libtomcrypt"] + path = deps/libtomcrypt + url = https://github.com/libtom/libtomcrypt.git + branch = develop +[submodule "deps/pdcurses"] + path = deps/pdcurses + url = https://github.com/wmcbrine/PDCurses.git + branch = master +[submodule "deps/mongoose"] + path = deps/mongoose + url = https://github.com/cesanta/mongoose.git + branch = 7.9 +[submodule "deps/protobuf"] + path = deps/protobuf + url = https://github.com/google/protobuf.git + branch = 3.20.x +[submodule "deps/udis86"] + path = deps/udis86 + url = https://github.com/vmt/udis86.git +[submodule "deps/dxsdk"] + path = deps/dxsdk + url = https://github.com/devKlausS/dxsdk.git +[submodule "deps/GSL"] + path = deps/GSL + url = https://github.com/microsoft/GSL.git +[submodule "deps/nlohmannjson"] + path = deps/json + url = https://github.com/nlohmann/json.git + branch = v3.11.2 +[submodule "deps/discord-rpc"] + path = deps/discord-rpc + url = https://github.com/discord/discord-rpc.git +[submodule "deps/rapidjson"] + path = deps/rapidjson + url = https://github.com/Tencent/rapidjson.git +[submodule "deps/iw4-open-formats"] + path = deps/iw4-open-formats + url = https://github.com/XLabsProject/iw4-open-formats.git diff --git a/deps/iw4-open-formats b/deps/iw4-open-formats new file mode 160000 index 00000000..619302b5 --- /dev/null +++ b/deps/iw4-open-formats @@ -0,0 +1 @@ +Subproject commit 619302b5c30f372f3b22576d4553f5af637a6b10 diff --git a/deps/premake/iw4-open-formats.lua b/deps/premake/iw4-open-formats.lua new file mode 100644 index 00000000..ebf18c32 --- /dev/null +++ b/deps/premake/iw4-open-formats.lua @@ -0,0 +1,44 @@ +iw4_open_formats = { + source = path.join(dependencies.basePath, "iw4-open-formats"), +} + +function iw4_open_formats.import() + links "iw4-open-formats" + + iw4_open_formats.includes() +end + +function iw4_open_formats.includes() + includedirs { + path.join(iw4_open_formats.source, "include") + } +end + +function iw4_open_formats.project() + project "iw4-open-formats" + language "C++" + + iw4_open_formats.includes() + + pchheader "std_include.hpp" + pchsource (path.join(iw4_open_formats.source, "src/iw4-of/std_include.cpp")) + + files { + path.join(iw4_open_formats.source, "src/iw4-of/**.hpp"), + path.join(iw4_open_formats.source, "src/iw4-of/**.cpp"), + } + + includedirs { + path.join(iw4_open_formats.source, "src/iw4-of"), + path.join(iw4_open_formats.source, "include"), + } + + libtomcrypt.includes() + libtommath.includes() + rapidjson.includes() + zlib.includes() + + kind "StaticLib" +end + +table.insert(dependencies, iw4_open_formats) diff --git a/src/Components/Modules/AssetInterfaces/IComWorld.cpp b/src/Components/Modules/AssetInterfaces/IComWorld.cpp index ae7aff94..c6584170 100644 --- a/src/Components/Modules/AssetInterfaces/IComWorld.cpp +++ b/src/Components/Modules/AssetInterfaces/IComWorld.cpp @@ -7,52 +7,7 @@ namespace Assets { void IComWorld::load(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder) { - std::string name = _name; - Utils::String::Replace(name, "maps/mp/", ""); - Utils::String::Replace(name, ".d3dbsp", ""); - - Components::FileSystem::File mapFile(std::format("comworld/{}.iw4xComWorld", name)); - - if (mapFile.exists()) - { - Utils::Stream::Reader reader(builder->getAllocator(), mapFile.getBuffer()); - - __int64 magic = reader.read<__int64>(); - if (std::memcmp(&magic, "IW4xComW", 8)) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading comworld '{}' failed, header is invalid!", name); - } - - int version = reader.read(); - if (version != IW4X_COMMAP_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading comworld '{}' failed, expected version is {}, but it was {}!", name, IW4X_COMMAP_VERSION, version); - } - - Game::ComWorld* asset = reader.readObject(); - header->comWorld = asset; - - if (asset->name) - { - asset->name = reader.readCString(); - } - - if (asset->primaryLights) - { - asset->primaryLights = reader.readArray(asset->primaryLightCount); - - for (unsigned int i = 0; i < asset->primaryLightCount; ++i) - { - Game::ComPrimaryLight* light = &asset->primaryLights[i]; - - if (light->defName) - { - light->defName = reader.readCString(); - Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LIGHT_DEF, light->defName, builder); - } - } - } - } + header->comWorld = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_COMWORLD, _name); } void IComWorld::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) diff --git a/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp b/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp index 94535b56..978934b6 100644 --- a/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp +++ b/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp @@ -1,14 +1,12 @@ #include #include "IFxEffectDef.hpp" -#define IW4X_FX_VERSION 1 - namespace Assets { void IFxEffectDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { if (!header->data) this->loadEfx(header, name, builder); // Check if we have an editor fx - if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game + if (!header->data) this->loadFromIW4OF(header, name, builder); // Check if we need to import a new one into the game if (!header->data /*&& !builder->isPrimaryAsset()*/) this->loadNative(header, name, builder); // Check if there is a native one } @@ -62,139 +60,9 @@ namespace Assets } } - void IFxEffectDef::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) + void IFxEffectDef::loadFromIW4OF(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File fxFile(std::format("fx/{}.iw4xFx", name)); - - if (fxFile.exists()) - { - Utils::Stream::Reader buffer(builder->getAllocator(), fxFile.getBuffer()); - - __int64 magic = buffer.read<__int64>(); - if (std::memcmp(&magic, "IW4xFx ", 8)) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading fx '{}' failed, header is invalid!", name); - } - - int version = buffer.read(); - if (version != IW4X_FX_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading fx '{}' failed, expected version is {}, but it was {}!", name, IW4X_FX_VERSION, version); - } - - Game::FxEffectDef* asset = buffer.readObject(); - header->fx = asset; - - if (asset->name) - { - asset->name = buffer.readCString(); - } - - if (asset->elemDefs) - { - asset->elemDefs = buffer.readArray(asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot); - - for (int i = 0; i < (asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot); ++i) - { - Game::FxElemDef* elemDef = &asset->elemDefs[i]; - - if (elemDef->velSamples) - { - elemDef->velSamples = buffer.readArray(elemDef->velIntervalCount + 1); - } - - if (elemDef->visSamples) - { - elemDef->visSamples = buffer.readArray(elemDef->visStateIntervalCount + 1); - } - - // Save_FxElemDefVisuals - { - if (elemDef->elemType == Game::FX_ELEM_TYPE_DECAL) - { - if (elemDef->visuals.markArray) - { - elemDef->visuals.markArray = buffer.readArray(elemDef->visualCount); - - for (char j = 0; j < elemDef->visualCount; ++j) - { - if (elemDef->visuals.markArray[j].materials[0]) - { - elemDef->visuals.markArray[j].materials[0] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, buffer.readString(), builder).material; - } - - if (elemDef->visuals.markArray[j].materials[1]) - { - elemDef->visuals.markArray[j].materials[1] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, buffer.readString(), builder).material; - } - } - } - } - else if (elemDef->visualCount > 1) - { - if (elemDef->visuals.array) - { - elemDef->visuals.array = buffer.readArray(elemDef->visualCount); - - for (char j = 0; j < elemDef->visualCount; ++j) - { - this->loadFxElemVisuals(&elemDef->visuals.array[j], elemDef->elemType, builder, &buffer); - } - } - } - else - { - this->loadFxElemVisuals(&elemDef->visuals.instance, elemDef->elemType, builder, &buffer); - } - } - - if (elemDef->effectOnImpact.handle) - { - elemDef->effectOnImpact.handle = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, buffer.readString(), builder).fx; - } - - if (elemDef->effectOnDeath.handle) - { - elemDef->effectOnDeath.handle = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, buffer.readString(), builder).fx; - } - - if (elemDef->effectEmitted.handle) - { - elemDef->effectEmitted.handle = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, buffer.readString(), builder).fx; - } - - // Save_FxElemExtendedDefPtr - { - - if (elemDef->elemType == Game::FX_ELEM_TYPE_TRAIL) - { - // Save_FxTrailDef - { - if (elemDef->extended.trailDef) - { - Game::FxTrailDef* trailDef = buffer.readObject(); - elemDef->extended.trailDef = trailDef; - - if (trailDef->verts) - { - trailDef->verts = buffer.readArray(trailDef->vertCount); - } - - if (trailDef->inds) - { - trailDef->inds = buffer.readArray(trailDef->indCount); - } - } - } - } - else if (elemDef->extended.trailDef) - { - Components::Logger::Error(Game::ERR_FATAL, "Fx element of type {} has traildef, that's impossible?\n", elemDef->elemType); - } - } - } - } - } + header->fx = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_FX, name); } void IFxEffectDef::loadEfx(Game::XAssetHeader* /*header*/, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) @@ -270,7 +138,7 @@ namespace Assets // TODO: Convert editor fx to real fx } #else - (name); + (void)name; #endif } diff --git a/src/Components/Modules/AssetInterfaces/IFxEffectDef.hpp b/src/Components/Modules/AssetInterfaces/IFxEffectDef.hpp index 9a877050..0ad259ed 100644 --- a/src/Components/Modules/AssetInterfaces/IFxEffectDef.hpp +++ b/src/Components/Modules/AssetInterfaces/IFxEffectDef.hpp @@ -17,7 +17,7 @@ namespace Assets void loadEfx(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); - void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); + void loadFromIW4OF(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); void loadFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader); }; diff --git a/src/Components/Modules/AssetInterfaces/IFxWorld.cpp b/src/Components/Modules/AssetInterfaces/IFxWorld.cpp index 6988543f..4b35cc40 100644 --- a/src/Components/Modules/AssetInterfaces/IFxWorld.cpp +++ b/src/Components/Modules/AssetInterfaces/IFxWorld.cpp @@ -187,6 +187,19 @@ namespace Assets } void IFxWorld::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) + { + if (!header->fxWorld) loadFromDisk(header, name, builder); + if (!header->fxWorld) generate(header, name, builder); + + assert(header->fxWorld); + } + + void IFxWorld::loadFromDisk(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder) + { + header->fxWorld = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_FXWORLD, _name); + } + + void IFxWorld::generate(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { Game::FxWorld* map = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FXWORLD, name.data()).fxWorld; if (map) return; @@ -194,7 +207,7 @@ namespace Assets // Generate map = builder->getAllocator()->allocate(); map->name = builder->getAllocator()->duplicateString(name); - + // No glass for you! ZeroMemory(&map->glassSys, sizeof(map->glassSys)); diff --git a/src/Components/Modules/AssetInterfaces/IFxWorld.hpp b/src/Components/Modules/AssetInterfaces/IFxWorld.hpp index a19ddf56..4f1590c1 100644 --- a/src/Components/Modules/AssetInterfaces/IFxWorld.hpp +++ b/src/Components/Modules/AssetInterfaces/IFxWorld.hpp @@ -10,5 +10,7 @@ namespace Assets void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; + void loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); + void generate(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); }; } diff --git a/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp b/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp index 1656d480..47444470 100644 --- a/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp +++ b/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp @@ -80,115 +80,6 @@ namespace Assets void IGameWorldMp::load(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder) { - std::string name = _name; - Utils::String::Replace(name, "maps/mp/", ""); - Utils::String::Replace(name, ".d3dbsp", ""); - - Components::FileSystem::File gameWorld(std::format("gameworld/{}.iw4x.json", name)); - - if (gameWorld.exists()) - { - nlohmann::json gameWorldJson; - - try - { - - gameWorldJson = nlohmann::json::parse(gameWorld.getBuffer()); - } - catch (const std::exception& e) - { - Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid JSON for gameworld {}! {}", name, e.what()); - return; - } - - auto* asset = builder->getAllocator()->allocate(); - - if (!gameWorldJson.is_object()) - { - Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid GameWorldMp json for {}\n", name); - return; - } - - auto version = gameWorldJson["version"].is_number() ? gameWorldJson["version"].get() : 0; - if (version != IW4X_GAMEWORLD_VERSION) - { - Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid GameWorld json version for {}, expected {} and got {}\n", name, IW4X_GAMEWORLD_VERSION, version); - return; - } - - if (!gameWorldJson["name"].is_string()) - { - Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Missing gameworld name! on {}\n", name); - return; - } - - asset->name = builder->getAllocator()->duplicateString(gameWorldJson["name"].get()); - auto glassData = builder->getAllocator()->allocate(); - - if (gameWorldJson["glassData"].is_object()) - { - auto jsonGlassData = gameWorldJson["glassData"]; - - try - { - glassData->damageToDestroy = jsonGlassData["damageToDestroy"].get(); - glassData->damageToWeaken = jsonGlassData["damageToWeaken"].get(); - - if (jsonGlassData["glassNames"].is_array()) - { - nlohmann::json::array_t glassNames = jsonGlassData["glassNames"]; - glassData->glassNameCount = glassNames.size(); - glassData->glassNames = builder->getAllocator()->allocateArray(glassData->glassNameCount); - - for (size_t i = 0; i < glassData->glassNameCount; i++) - { - auto jsonGlassName = glassNames[i]; - glassData->glassNames[i].nameStr = builder->getAllocator()->duplicateString(jsonGlassName["nameStr"]); - - glassData->glassNames[i].name = jsonGlassName["name"].get(); - - if (jsonGlassName["piecesIndices"].is_array()) - { - nlohmann::json::array_t jsonPiecesIndices = jsonGlassName["piecesIndices"]; - glassData->glassNames[i].pieceCount = static_cast(jsonPiecesIndices.size()); - - for (size_t j = 0; j < glassData->glassNames[i].pieceCount; j++) - { - glassData->glassNames[i].pieceIndices[j] = jsonPiecesIndices[j].get(); - } - } - } - } - - if (gameWorldJson["glassPieces"].is_array()) - { - nlohmann::json::array_t glassPieces = gameWorldJson["glassPieces"]; - glassData->pieceCount = glassPieces.size(); - glassData->glassPieces = builder->getAllocator()->allocateArray(glassData->pieceCount); - - for (size_t i = 0; i < glassData->pieceCount; i++) - { - glassData->glassPieces[i].collapseTime = glassPieces[i]["collapseTime"].get(); - glassData->glassPieces[i].damageTaken = glassPieces[i]["damageTaken"].get(); - glassData->glassPieces[i].lastStateChangeTime = glassPieces[i]["lastStateChangeTime"].get(); - glassData->glassPieces[i].impactDir = glassPieces[i]["impactDir"].get(); - - nlohmann::json::array_t jsonPos = glassPieces[i]["impactPos"]; - glassData->glassPieces[i].impactPos[0] = jsonPos[0].get(); - glassData->glassPieces[i].impactPos[1] = jsonPos[1].get(); - } - } - } - catch (const nlohmann::json::exception& e) - { - Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Malformed GameWorldMp json for {} ({})\n", name, e.what()); - return; - } - } - - asset->g_glassData = glassData; - - header->gameWorldMp = asset; - } + header->gameWorldMp = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP, _name); } } diff --git a/src/Components/Modules/AssetInterfaces/IGfxImage.cpp b/src/Components/Modules/AssetInterfaces/IGfxImage.cpp index 4ff564c6..4744e3ee 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxImage.cpp +++ b/src/Components/Modules/AssetInterfaces/IGfxImage.cpp @@ -1,150 +1,11 @@ #include #include "IGfxImage.hpp" -#define IW4X_IMG_VERSION "0" - namespace Assets { void IGfxImage::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Game::GfxImage* image = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_IMAGE, name.data()).image; - if (image && name[0] != '*') return; - - image = builder->getAllocator()->allocate(); - if (!image) - { - Components::Logger::Error(Game::ERR_FATAL, "Failed to allocate GfxImage structure!"); - return; - } - - image->name = builder->getAllocator()->duplicateString(name); - image->semantic = Game::TextureSemantic::TS_COLOR_MAP; - - const char* tempName = image->name; - if (tempName[0] == '*') tempName++; - - Components::FileSystem::File imageFile(std::format("images/{}.iw4xImage", tempName)); - if (imageFile.exists()) - { - Utils::Stream::Reader reader(builder->getAllocator(), imageFile.getBuffer()); - - __int64 magic = reader.read<__int64>(); - if (std::memcmp(&magic, "IW4xImg" IW4X_IMG_VERSION, 8)) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading image '{}' failed, header is invalid!", name); - } - - image->mapType = reader.read(); - image->semantic = reader.read(); - image->category = reader.read(); - - int dataLength = reader.read(); - image->cardMemory.platform[0] = dataLength; - image->cardMemory.platform[1] = dataLength; - - Game::GfxImageLoadDefIW3 loadDef; - image->texture.loadDef = reinterpret_cast(reader.readArray(dataLength + 16)); - std::memcpy(&loadDef, image->texture.loadDef, sizeof(loadDef)); - - image->texture.loadDef->levelCount = loadDef.levelCount; - image->texture.loadDef->flags = loadDef.flags; - image->texture.loadDef->format = loadDef.format; - image->texture.loadDef->resourceSize = loadDef.resourceSize; - ZeroMemory(image->texture.loadDef->pad, 3); - - if (image->texture.loadDef->resourceSize != dataLength) - { - Components::Logger::Error(Game::ERR_FATAL, "Resource size doesn't match the data length ({})!\n", name); - } - - image->width = loadDef.dimensions[0]; - image->height = loadDef.dimensions[1]; - image->depth = loadDef.dimensions[2]; - - image->delayLoadPixels = true; - - header->image = image; - } - else if (name[0] != '*') - { - char nameBuffer[MAX_PATH]{}; - Components::Materials::FormatImagePath(nameBuffer, sizeof(nameBuffer), 0, 0, name.data()); - Components::FileSystem::File iwi(nameBuffer); - - if (!iwi.exists()) - { - Components::Logger::Error(Game::ERR_FATAL, "Loading image '{}' failed!", iwi.getName()); - return; - } - - auto iwiBuffer = iwi.getBuffer(); - - const Game::GfxImageFileHeader* iwiHeader = reinterpret_cast(iwiBuffer.data()); - - if (std::memcmp(iwiHeader->tag, "IWi", 3) && iwiHeader->version == 8) - { - Components::Logger::Error(Game::ERR_FATAL, "Image is not a valid IWi!"); - return; - } - - image->mapType = Game::MAPTYPE_2D; - image->cardMemory.platform[0] = iwiHeader->fileSizeForPicmip[0] - 32; - image->cardMemory.platform[1] = iwiHeader->fileSizeForPicmip[0] - 32; - - image->texture.loadDef = builder->getAllocator()->allocate(); - if (!image->texture.loadDef) - { - Components::Logger::Error(Game::ERR_FATAL, "Failed to allocate GfxImageLoadDef structure!"); - return; - } - - image->texture.loadDef->flags = iwiHeader->flags; - image->texture.loadDef->levelCount = 0; - - image->width = iwiHeader->dimensions[0]; - image->height = iwiHeader->dimensions[1]; - image->depth = iwiHeader->dimensions[2]; - - switch (iwiHeader->format) - { - case Game::IMG_FORMAT_BITMAP_RGBA: - { - image->texture.loadDef->format = 21; - break; - } - - case Game::IMG_FORMAT_BITMAP_RGB: - { - image->texture.loadDef->format = 20; - break; - } - - case Game::IMG_FORMAT_DXT1: - { - image->texture.loadDef->format = 0x31545844; - break; - } - - case Game::IMG_FORMAT_DXT3: - { - image->texture.loadDef->format = 0x33545844; - break; - } - - case Game::IMG_FORMAT_DXT5: - { - image->texture.loadDef->format = 0x35545844; - break; - } - - default: - { - break; - } - } - - header->image = image; - } + header->image = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_IMAGE, name); } void IGfxImage::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) diff --git a/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp b/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp index ca029d05..6849e701 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp +++ b/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp @@ -1,44 +1,11 @@ #include #include "IGfxLightDef.hpp" -#define IW4X_LIGHT_VERSION "0" - namespace Assets { void IGfxLightDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File mapFile(std::format("lights/{}.iw4xLight", name)); - - if (mapFile.exists()) - { - Utils::Stream::Reader reader(builder->getAllocator(), mapFile.getBuffer()); - - char* magic = reader.readArray(7); - if (std::memcmp(magic, "IW4xLit", 7)) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading light '{}' failed, header is invalid!", name); - } - - std::string version; - version.push_back(reader.read()); - if (version != IW4X_LIGHT_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading light '{}' failed, expected version is {}, but it was {}!", name, IW4X_LIGHT_VERSION, version); - } - - Game::GfxLightDef* asset = reader.readObject(); - header->lightDef = asset; - - if (asset->name) - { - asset->name = reader.readCString(); - } - - if (asset->attenuation.image) - { - asset->attenuation.image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString(), builder).image; - } - } + header->lightDef = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_LIGHT_DEF, name); } void IGfxLightDef::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) diff --git a/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp b/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp index 44eab247..6334fad7 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp +++ b/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp @@ -1,450 +1,16 @@ #include #include "IGfxWorld.hpp" -#define IW4X_GFXMAP_VERSION 1 - -// The xmodel vehicle_small_hatch_green_destructible_mp causes EXTREME lag -// when placed in the world, for reasons unknown. -// -// Something happens with the SModelSurfIterator which makes it load garbage -// as an XSurface in the middle of otherwise valid surfaces. This bug is very -// easy to reproduce with an empty map and just this car in the middle -// -// As of know we do not know why the iterator corruption occurs or what causes -// it. It doesn't seem linked to the SModel, nor to the materials or techsets, -// nor to the sortkeys, nor to the tilemode, boneinfo, and so on. So for now -// and to make it work for majority of users, we just swap the car. (no, using -// the identical car from iw4's favela_escape doesn't work either!) -// -// Two other models have this problem: ch_apartment_9story_noentry_02 and -// ch_apartment_5story_noentry_01 -// But these exist in mp_vacant in slightly different versions, and can be -// swapped safely by deleting the two .iw4XModel files and requiring mp_vacant -// or a minimal zone containing just these two models. -// -#define SWAP_GREEN_VEHICLE_XMODEL 1 - namespace Assets { - void IGfxWorld::loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader) - { - if (asset->sortedSurfIndex) - { - asset->sortedSurfIndex = reader->readArray(asset->staticSurfaceCount + asset->staticSurfaceCountNoDecal); - } - - if (asset->smodelInsts) - { - asset->smodelInsts = reader->readArray(asset->smodelCount); - } - - if (asset->surfaces) - { - asset->surfaces = reader->readArray(world->surfaceCount); - - for (unsigned int i = 0; i < world->surfaceCount; ++i) - { - Game::GfxSurface* surface = &asset->surfaces[i]; - - if (surface->material) - { - world->dpvs.surfaces[i].material = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader->readString(), builder).material; - } - } - } - - if (asset->surfacesBounds) - { - asset->surfacesBounds = reader->readArray(world->surfaceCount); - } - - if (asset->smodelDrawInsts) - { - asset->smodelDrawInsts = reader->readArray(asset->smodelCount); - - for (unsigned int i = 0; i < asset->smodelCount; ++i) - { - Game::GfxStaticModelDrawInst* model = &asset->smodelDrawInsts[i]; - - if (model->model) - { - auto name = reader->readString(); - - model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, name, builder).model; - - assert(model->model); - } - } - } - } - - void IGfxWorld::loadGfxWorldDraw(Game::GfxWorldDraw* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader) - { - if (asset->reflectionProbes) - { - asset->reflectionProbes = reader->readArray(asset->reflectionProbeCount); - - for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i) - { - asset->reflectionProbes[i] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString(), builder).image; - } - } - - if (asset->reflectionProbeOrigins) - { - asset->reflectionProbeOrigins = reader->readArray(asset->reflectionProbeCount); - } - - if (asset->lightmaps) - { - asset->lightmaps = reader->readArray(asset->lightmapCount); - - for (int i = 0; i < asset->lightmapCount; ++i) - { - Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i]; - - if (lightmapArray->primary) - { - lightmapArray->primary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString(), builder).image; - } - - if (lightmapArray->secondary) - { - lightmapArray->secondary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString(), builder).image; - } - } - } - - if (asset->lightmapOverridePrimary) - { - asset->lightmapOverridePrimary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString(), builder).image; - } - - if (asset->lightmapOverrideSecondary) - { - asset->lightmapOverrideSecondary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString(), builder).image; - } - - // saveGfxWorldVertexData - { - if (asset->vd.vertices) - { - asset->vd.vertices = reader->readArray(asset->vertexCount); - } - } - - // saveGfxWorldVertexLayerData - { - if (asset->vld.data) - { - // no align for char - asset->vld.data = reader->readArray(asset->vertexLayerDataSize); - } - } - - if (asset->indices) - { - asset->indices = reader->readArray(asset->indexCount); - } - } - void IGfxWorld::load(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder) { - std::string name = _name; - Utils::String::Replace(name, "maps/mp/", ""); - Utils::String::Replace(name, ".d3dbsp", ""); - - Components::FileSystem::File mapFile(std::format("gfxworld/{}.iw4xGfxWorld", name)); - - if (mapFile.exists()) - { - Utils::Stream::Reader reader(builder->getAllocator(), mapFile.getBuffer()); - - __int64 magic = reader.read<__int64>(); - if (std::memcmp(&magic, "IW4xGfxW", 8)) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading gfxworld '{}' failed, header is invalid!", name); - } - - int version = reader.read(); - if (version != IW4X_GFXMAP_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading gfxworld '{}' failed, expected version is {}, but it was {}!", name, IW4X_GFXMAP_VERSION, version); - } - - Game::GfxWorld* asset = reader.readObject(); - header->gfxWorld = asset; - - if (asset->name) - { - asset->name = reader.readCString(); - } - - if (asset->baseName) - { - asset->baseName = reader.readCString(); - } - - if (asset->skies) - { - asset->skies = reader.readArray(asset->skyCount); - - for (int i = 0; i < asset->skyCount; ++i) - { - Game::GfxSky* sky = &asset->skies[i]; - - if (sky->skyStartSurfs) - { - sky->skyStartSurfs = reader.readArray(sky->skySurfCount); - } - - if (sky->skyImage) - { - sky->skyImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString(), builder).image; - } - } - } - - // GfxWorldDpvsPlanes - { - if (asset->dpvsPlanes.planes) - { - void* oldPtr = asset->dpvsPlanes.planes; - asset->dpvsPlanes.planes = reader.readArray(asset->planeCount); - - if (builder->getAllocator()->isPointerMapped(oldPtr)) - { - asset->dpvsPlanes.planes = builder->getAllocator()->getPointer(oldPtr); - } - else - { - builder->getAllocator()->mapPointer(oldPtr, asset->dpvsPlanes.planes); - Components::Logger::Print("GfxWorld dpvs planes not mapped. This shouldn't happen. Make sure to load the ClipMap first!\n"); - } - } - - if (asset->dpvsPlanes.nodes) - { - asset->dpvsPlanes.nodes = reader.readArray(asset->nodeCount); - } - } - - - int cellCount = asset->dpvsPlanes.cellCount; - - if (asset->aabbTreeCounts) - { - asset->aabbTreeCounts = reader.readArray(cellCount); - } - - if (asset->aabbTrees) - { - asset->aabbTrees = reader.readArray(cellCount); - - for (int i = 0; i < cellCount; ++i) - { - Game::GfxCellTree* cellTree = &asset->aabbTrees[i]; - - if (cellTree->aabbTree) - { - cellTree->aabbTree = reader.readArray(asset->aabbTreeCounts[i].aabbTreeCount); - - for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j) - { - Game::GfxAabbTree* aabbTree = &cellTree->aabbTree[j]; - - if (aabbTree->smodelIndexes) - { - unsigned short* oldPointer = aabbTree->smodelIndexes; - if(builder->getAllocator()->isPointerMapped(oldPointer)) - { - // We still have to read it - reader.readArray(aabbTree->smodelIndexCount); - - aabbTree->smodelIndexes = builder->getAllocator()->getPointer(oldPointer); - } - else - { - aabbTree->smodelIndexes = reader.readArray(aabbTree->smodelIndexCount); - - for (unsigned short k = 0; k < aabbTree->smodelIndexCount; ++k) - { - builder->getAllocator()->mapPointer(&oldPointer[k], &aabbTree->smodelIndexes[k]); - } - } - } - } - } - } - } - - if (asset->cells) - { - asset->cells = reader.readArray(cellCount); - - for (int i = 0; i < cellCount; ++i) - { - Game::GfxCell* cell = &asset->cells[i]; - - if (cell->portals) - { - cell->portals = reader.readArray(cell->portalCount); - - for (int j = 0; j < cell->portalCount; ++j) - { - Game::GfxPortal* portal = &cell->portals[j]; - - if (portal->vertices) - { - portal->vertices = reader.readArray(portal->vertexCount); - } - } - } - - if (cell->reflectionProbes) - { - cell->reflectionProbes = reader.readArray(cell->reflectionProbeCount); - } - } - } - - this->loadGfxWorldDraw(&asset->draw, builder, &reader); - - // GfxLightGrid - { - if (asset->lightGrid.rowDataStart) - { - asset->lightGrid.rowDataStart = reader.readArray((asset->lightGrid.maxs[asset->lightGrid.rowAxis] - asset->lightGrid.mins[asset->lightGrid.rowAxis]) + 1); - } - - if (asset->lightGrid.rawRowData) - { - asset->lightGrid.rawRowData = reader.readArray(asset->lightGrid.rawRowDataSize); - } - - if (asset->lightGrid.entries) - { - asset->lightGrid.entries = reader.readArray(asset->lightGrid.entryCount); - } - - if (asset->lightGrid.colors) - { - asset->lightGrid.colors = reader.readArray(asset->lightGrid.colorCount); - } - } - - if (asset->models) - { - asset->models = reader.readArray(asset->modelCount); - } - - if (asset->materialMemory) - { - asset->materialMemory = reader.readArray(asset->materialMemoryCount); - - for (int i = 0; i < asset->materialMemoryCount; ++i) - { - Game::MaterialMemory* materialMemory = &asset->materialMemory[i]; - - if (materialMemory->material) - { - materialMemory->material = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material; - } - } - } - - if (asset->sun.spriteMaterial) - { - asset->sun.spriteMaterial = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material; - } - - if (asset->sun.flareMaterial) - { - asset->sun.flareMaterial = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material; - } - - if (asset->outdoorImage) - { - asset->outdoorImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString(), builder).image; - } - - if (asset->primaryLightCount > 0) - { - Utils::Stream::ClearPointer(&asset->primaryLightEntityShadowVis); - } - - if (asset->dpvsDyn.dynEntClientCount[0] > 0) - { - Utils::Stream::ClearPointer(&asset->sceneDynModel); - Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[0]); - Utils::Stream::ClearPointer(&asset->nonSunPrimaryLightForModelDynEnt); - } - - if (asset->dpvsDyn.dynEntClientCount[1] > 0) - { - Utils::Stream::ClearPointer(&asset->sceneDynBrush); - Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[1]); - } - - if (asset->shadowGeom) - { - asset->shadowGeom = reader.readArray(asset->primaryLightCount); - - for (unsigned int i = 0; i < asset->primaryLightCount; ++i) - { - Game::GfxShadowGeometry* shadowGeometry = &asset->shadowGeom[i]; - - if (shadowGeometry->sortedSurfIndex) - { - shadowGeometry->sortedSurfIndex = reader.readArray(shadowGeometry->surfaceCount); - } - - if (shadowGeometry->smodelIndex) - { - shadowGeometry->smodelIndex = reader.readArray(shadowGeometry->smodelCount); - } - } - } - - if (asset->lightRegion) - { - asset->lightRegion = reader.readArray(asset->primaryLightCount); - - for (unsigned int i = 0; i < asset->primaryLightCount; ++i) - { - Game::GfxLightRegion* lightRegion = &asset->lightRegion[i]; - - if (lightRegion->hulls) - { - lightRegion->hulls = reader.readArray(lightRegion->hullCount); - - for (unsigned int j = 0; j < lightRegion->hullCount; ++j) - { - Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j]; - - if (lightRegionHull->axis) - { - lightRegionHull->axis = reader.readArray(lightRegionHull->axisCount); - } - } - } - } - } - - this->loadGfxWorldDpvsStatic(asset, &asset->dpvs, builder, &reader); - - // Obsolete, IW3 has no support for that - if (asset->heroOnlyLights) - { - asset->heroOnlyLights = reader.readArray(asset->heroOnlyLightCount); - } - } + header->gfxWorld = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_GFXWORLD, _name); } void IGfxWorld::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { - Game::GfxWorld* asset = header.gfxWorld; - + auto* asset = header.gfxWorld; if (asset->draw.reflectionProbes) { for (unsigned int i = 0; i < asset->draw.reflectionProbeCount; ++i) @@ -455,7 +21,7 @@ namespace Assets if (asset->draw.lightmaps) { - for (int i = 0; i < asset->draw.lightmapCount; ++i) + for (auto i = 0; i < asset->draw.lightmapCount; ++i) { if (asset->draw.lightmaps[i].primary) { @@ -599,7 +165,7 @@ namespace Assets { buffer->align(Utils::Stream::ALIGN_4); - Game::GfxImage** imageDest = buffer->dest(); + auto** imageDest = buffer->dest(); buffer->saveArray(asset->reflectionProbes, asset->reflectionProbeCount); for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i) @@ -648,13 +214,13 @@ namespace Assets buffer->align(Utils::Stream::ALIGN_4); - Game::GfxLightmapArray* lightmapArrayDestTable = buffer->dest(); + auto* lightmapArrayDestTable = buffer->dest(); buffer->saveArray(asset->lightmaps, asset->lightmapCount); for (int i = 0; i < asset->lightmapCount; ++i) { - Game::GfxLightmapArray* lightmapArrayDest = &lightmapArrayDestTable[i]; - Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i]; + auto* lightmapArrayDest = &lightmapArrayDestTable[i]; + auto* lightmapArray = &asset->lightmaps[i]; if (lightmapArray->primary) { @@ -854,13 +420,13 @@ namespace Assets SaveLogEnter("GfxSurface"); buffer->align(Utils::Stream::ALIGN_4); - Game::GfxSurface* destSurfaceTable = buffer->dest(); + auto* destSurfaceTable = buffer->dest(); buffer->saveArray(asset->surfaces, world->surfaceCount); for (unsigned int i = 0; i < world->surfaceCount; ++i) { - Game::GfxSurface* surface = &asset->surfaces[i]; - Game::GfxSurface* destSurface = &destSurfaceTable[i]; + auto* surface = &asset->surfaces[i]; + auto* destSurface = &destSurfaceTable[i]; if (surface->material) { @@ -890,13 +456,13 @@ namespace Assets SaveLogEnter("GfxStaticModelDrawInst"); buffer->align(Utils::Stream::ALIGN_4); - Game::GfxStaticModelDrawInst* destModelTable = buffer->dest(); + auto* destModelTable = buffer->dest(); buffer->saveArray(asset->smodelDrawInsts, asset->smodelCount); for (unsigned int i = 0; i < asset->smodelCount; ++i) { - Game::GfxStaticModelDrawInst* model = &asset->smodelDrawInsts[i]; - Game::GfxStaticModelDrawInst* destModel = &destModelTable[i]; + auto* model = &asset->smodelDrawInsts[i]; + auto* destModel = &destModelTable[i]; if (model->model) { @@ -986,8 +552,8 @@ namespace Assets Utils::Stream* buffer = builder->getBuffer(); SaveLogEnter("GfxWorld"); - Game::GfxWorld* asset = header.gfxWorld; - Game::GfxWorld* dest = buffer->dest(); + auto* asset = header.gfxWorld; + auto* dest = buffer->dest(); buffer->save(asset); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); @@ -1012,13 +578,13 @@ namespace Assets SaveLogEnter("GfxSky"); buffer->align(Utils::Stream::ALIGN_4); - Game::GfxSky* destSkyTable = buffer->dest(); + auto* destSkyTable = buffer->dest(); buffer->saveArray(asset->skies, asset->skyCount); for (int i = 0; i < asset->skyCount; ++i) { - Game::GfxSky* destSky = &destSkyTable[i]; - Game::GfxSky* sky = &asset->skies[i]; + auto* destSky = &destSkyTable[i]; + auto* sky = &asset->skies[i]; if (sky->skyStartSurfs) { @@ -1059,13 +625,13 @@ namespace Assets SaveLogEnter("GfxCellTree"); buffer->align(Utils::Stream::ALIGN_128); - Game::GfxCellTree* destCellTreeTable = buffer->dest(); + auto* destCellTreeTable = buffer->dest(); buffer->saveArray(asset->aabbTrees, cellCount); for (int i = 0; i < cellCount; ++i) { - Game::GfxCellTree* destCellTree = &destCellTreeTable[i]; - Game::GfxCellTree* cellTree = &asset->aabbTrees[i]; + auto* destCellTree = &destCellTreeTable[i]; + auto* cellTree = &asset->aabbTrees[i]; if (cellTree->aabbTree) { @@ -1073,7 +639,7 @@ namespace Assets SaveLogEnter("GfxAabbTree"); buffer->align(Utils::Stream::ALIGN_4); - Game::GfxAabbTree* destAabbTreeTable = buffer->dest(); + auto* destAabbTreeTable = buffer->dest(); buffer->saveArray(cellTree->aabbTree, asset->aabbTreeCounts[i].aabbTreeCount); // ok this one is based on some assumptions because the actual count is this @@ -1083,8 +649,8 @@ namespace Assets for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j) { - Game::GfxAabbTree* destAabbTree = &destAabbTreeTable[j]; - Game::GfxAabbTree* aabbTree = &cellTree->aabbTree[j]; + auto* destAabbTree = &destAabbTreeTable[j]; + auto* aabbTree = &cellTree->aabbTree[j]; if (aabbTree->smodelIndexes) { @@ -1122,13 +688,13 @@ namespace Assets SaveLogEnter("GfxCell"); buffer->align(Utils::Stream::ALIGN_4); - Game::GfxCell* destCellTable = buffer->dest(); + auto* destCellTable = buffer->dest(); buffer->saveArray(asset->cells, cellCount); for (int i = 0; i < cellCount; ++i) { - Game::GfxCell* destCell = &destCellTable[i]; - Game::GfxCell* cell = &asset->cells[i]; + auto* destCell = &destCellTable[i]; + auto* cell = &asset->cells[i]; if (cell->portals) { @@ -1136,13 +702,13 @@ namespace Assets SaveLogEnter("GfxPortal"); buffer->align(Utils::Stream::ALIGN_4); - Game::GfxPortal* destPortalTable = buffer->dest(); + auto* destPortalTable = buffer->dest(); buffer->saveArray(cell->portals, cell->portalCount); for (int j = 0; j < cell->portalCount; ++j) { - Game::GfxPortal* destPortal = &destPortalTable[j]; - Game::GfxPortal* portal = &cell->portals[j]; + auto* destPortal = &destPortalTable[j]; + auto* portal = &cell->portals[j]; if (portal->vertices) { @@ -1189,13 +755,13 @@ namespace Assets SaveLogEnter("MaterialMemory"); buffer->align(Utils::Stream::ALIGN_4); - Game::MaterialMemory* destMaterialMemoryTable = buffer->dest(); + auto* destMaterialMemoryTable = buffer->dest(); buffer->saveArray(asset->materialMemory, asset->materialMemoryCount); for (int i = 0; i < asset->materialMemoryCount; ++i) { - Game::MaterialMemory* destMaterialMemory = &destMaterialMemoryTable[i]; - Game::MaterialMemory* materialMemory = &asset->materialMemory[i]; + auto* destMaterialMemory = &destMaterialMemoryTable[i]; + auto* materialMemory = &asset->materialMemory[i]; if (materialMemory->material) { @@ -1284,13 +850,13 @@ namespace Assets SaveLogEnter("GfxShadowGeometry"); buffer->align(Utils::Stream::ALIGN_4); - Game::GfxShadowGeometry* destShadowGeometryTable = buffer->dest(); + auto* destShadowGeometryTable = buffer->dest(); buffer->saveArray(asset->shadowGeom, asset->primaryLightCount); for (unsigned int i = 0; i < asset->primaryLightCount; ++i) { - Game::GfxShadowGeometry* destShadowGeometry = &destShadowGeometryTable[i]; - Game::GfxShadowGeometry* shadowGeometry = &asset->shadowGeom[i]; + auto* destShadowGeometry = &destShadowGeometryTable[i]; + auto* shadowGeometry = &asset->shadowGeom[i]; if (shadowGeometry->sortedSurfIndex) { @@ -1317,13 +883,13 @@ namespace Assets SaveLogEnter("GfxLightRegion"); buffer->align(Utils::Stream::ALIGN_4); - Game::GfxLightRegion* destLightRegionTable = buffer->dest(); + auto* destLightRegionTable = buffer->dest(); buffer->saveArray(asset->lightRegion, asset->primaryLightCount); for (unsigned int i = 0; i < asset->primaryLightCount; ++i) { - Game::GfxLightRegion* destLightRegion = &destLightRegionTable[i]; - Game::GfxLightRegion* lightRegion = &asset->lightRegion[i]; + auto* destLightRegion = &destLightRegionTable[i]; + auto* lightRegion = &asset->lightRegion[i]; if (lightRegion->hulls) { @@ -1331,13 +897,13 @@ namespace Assets SaveLogEnter("GfxLightRegionHull"); buffer->align(Utils::Stream::ALIGN_4); - Game::GfxLightRegionHull* destLightRegionHullTable = buffer->dest(); + auto* destLightRegionHullTable = buffer->dest(); buffer->saveArray(lightRegion->hulls, lightRegion->hullCount); for (unsigned int j = 0; j < lightRegion->hullCount; ++j) { - Game::GfxLightRegionHull* destLightRegionHull = &destLightRegionHullTable[j]; - Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j]; + auto* destLightRegionHull = &destLightRegionHullTable[j]; + auto* lightRegionHull = &lightRegion->hulls[j]; if (lightRegionHull->axis) { @@ -1373,7 +939,6 @@ namespace Assets Utils::Stream::ClearPointer(&dest->heroOnlyLights); } - //buffer->setPointerAssertion(false); buffer->popBlock(); SaveLogExit(); } diff --git a/src/Components/Modules/AssetInterfaces/IGfxWorld.hpp b/src/Components/Modules/AssetInterfaces/IGfxWorld.hpp index 24e3a71d..74f1786e 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxWorld.hpp +++ b/src/Components/Modules/AssetInterfaces/IGfxWorld.hpp @@ -18,8 +18,5 @@ namespace Assets void savesunflare_t(Game::sunflare_t* asset, Game::sunflare_t* dest, Components::ZoneBuilder::Zone* builder); void saveGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Game::GfxWorldDpvsStatic* dest, int planeCount, Components::ZoneBuilder::Zone* builder); void saveGfxWorldDpvsDynamic(Game::GfxWorldDpvsDynamic* asset, Game::GfxWorldDpvsDynamic* dest, int cellCount, Components::ZoneBuilder::Zone* builder); - - void loadGfxWorldDraw(Game::GfxWorldDraw* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader); - void loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader); }; } diff --git a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp index 5f22deea..803bc4e6 100644 --- a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp +++ b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp @@ -5,106 +5,16 @@ namespace Assets { void ILoadedSound::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File soundFile(std::format("loaded_sound/{}", name)); - if (!soundFile.exists()) - { - header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd; - return; - } - - Game::LoadedSound* sound = builder->getAllocator()->allocate(); - if (!sound) - { - Components::Logger::Print("Error allocating memory for sound structure!\n"); - return; - } - - Game::LoadedSound* reference = nullptr; - if (!reference) reference = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_LOADED_SOUND, "weapons/c4_detpack/c4_drop_dirt1.wav").loadSnd; - - std::memcpy(sound, reference, sizeof(Game::LoadedSound)); - sound->sound.data = nullptr; - - Utils::Stream::Reader reader(builder->getAllocator(), soundFile.getBuffer()); - - unsigned int chunkIDBuffer = reader.read(); - if (chunkIDBuffer != 0x46464952) // RIFF - { - Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, header is invalid!", name); - return; - } - - unsigned int chunkSize = reader.read(); - - unsigned int format = reader.read(); - if (format != 0x45564157) // WAVE - { - Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, header is invalid!", name); - return; - } - - while (!sound->sound.data && !reader.end()) - { - chunkIDBuffer = reader.read(); - chunkSize = reader.read(); - - switch (chunkIDBuffer) - { - case 0x20746D66: // fmt - if (chunkSize >= 16) - { - sound->sound.info.format = reader.read(); - if (sound->sound.info.format != 1 && sound->sound.info.format != 17) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, invalid format!", name); - return; - } - - sound->sound.info.channels = reader.read(); - sound->sound.info.rate = reader.read(); - sound->sound.info.samples = reader.read(); - sound->sound.info.block_size = reader.read(); - sound->sound.info.bits = reader.read(); - - // skip any extra parameters - if (chunkSize > 16) - { - reader.seekRelative(chunkSize - 16); - } - } - break; - - case 0x61746164: // data - sound->sound.info.data_len = chunkSize; - sound->sound.data = reader.readArray(chunkSize); - break; - - default: - if (chunkSize > 0) - { - reader.seekRelative(chunkSize); - } - break; - } - } - - if (!sound->sound.info.data_ptr) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, invalid format!", name); - return; - } - - sound->name = builder->getAllocator()->duplicateString(name); - header->loadSnd = sound; + header->loadSnd = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, name); } void ILoadedSound::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { AssertSize(Game::LoadedSound, 44); - Utils::Stream* buffer = builder->getBuffer(); - Game::LoadedSound* asset = header.loadSnd; - Game::LoadedSound* dest = buffer->dest(); + auto* buffer = builder->getBuffer(); + auto* asset = header.loadSnd; + auto* dest = buffer->dest(); buffer->save(asset); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); diff --git a/src/Components/Modules/AssetInterfaces/IMapEnts.cpp b/src/Components/Modules/AssetInterfaces/IMapEnts.cpp index 11f4ac0a..48ed5c93 100644 --- a/src/Components/Modules/AssetInterfaces/IMapEnts.cpp +++ b/src/Components/Modules/AssetInterfaces/IMapEnts.cpp @@ -5,55 +5,7 @@ namespace Assets { void IMapEnts::load(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder) { - std::string name = _name; - Utils::String::Replace(name, "maps/", ""); - Utils::String::Replace(name, "mp/", ""); - Utils::String::Replace(name, ".d3dbsp", ""); - - Components::FileSystem::File ents(std::format("mapents/{}.ents", name)); - if (ents.exists()) - { - Game::MapEnts* entites = builder->getAllocator()->allocate(); - Game::MapEnts* orgEnts = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).mapEnts; - - // TODO: Get rid of that - if (!orgEnts) - { - orgEnts = Components::AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MAP_ENTS, "maps/iw4_credits.d3dbsp").mapEnts; - - if (!orgEnts) - { - Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_MAP_ENTS, [](Game::XAssetHeader header, void* mapEnts) - { - if (!*reinterpret_cast(mapEnts)) - { - *reinterpret_cast(mapEnts) = header.mapEnts; - } - }, &orgEnts, false); - } - } - - if (orgEnts) - { - std::memcpy(entites, orgEnts, sizeof Game::MapEnts); - } - else - { - entites->stageCount = 1; - entites->stages = builder->getAllocator()->allocate(); - entites->stages[0].name = "stage 0"; - entites->stages[0].triggerIndex = 0x400; - entites->stages[0].sunPrimaryLightIndex = 0x1; - } - - std::string entityString = ents.getBuffer(); - - entites->name = builder->getAllocator()->duplicateString(std::format("maps/mp/{}.d3dbsp", name)); - entites->entityString = builder->getAllocator()->duplicateString(entityString); - entites->numEntityChars = entityString.size() + 1; - - header->mapEnts = entites; - } + header->mapEnts = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_MAP_ENTS, _name); } void IMapEnts::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index 44116037..a30bfcf7 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -1,730 +1,18 @@ #include #include "IMaterial.hpp" -#include - -#define IW4X_MAT_BIN_VERSION "1" -#define IW4X_MAT_JSON_VERSION 1 - namespace Assets { - const std::unordered_map techSetCorrespondance = - { - {"effect", "effect_blend"}, - {"effect", "effect_blend"}, - {"effect_nofog", "effect_blend_nofog"}, - {"effect_zfeather", "effect_zfeather_blend"}, - {"effect_zfeather_falloff", "effect_zfeather_falloff_add"}, - {"effect_zfeather_nofog", "effect_zfeather_add_nofog"}, - - {"wc_unlit_add", "wc_unlit_add_lin"}, - {"wc_unlit_distfalloff", "wc_unlit_distfalloff_replace"}, - {"wc_unlit_multiply", "wc_unlit_multiply_lin"}, - {"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin"}, - {"wc_unlit", "wc_unlit_replace_lin"}, - {"wc_unlit_alphatest", "wc_unlit_blend_lin"}, - {"wc_unlit_blend", "wc_unlit_blend_lin"}, - {"wc_unlit_replace", "wc_unlit_replace_lin"}, - {"wc_unlit_nofog", "wc_unlit_replace_lin_nofog_nocast" }, - - {"mc_unlit_replace", "mc_unlit_replace_lin"}, - {"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"}, - {"mc_unlit", "mc_unlit_replace_lin_nocast"}, - {"mc_unlit_alphatest", "mc_unlit_blend_lin"}, - {"mc_effect_nofog", "mc_effect_blend_nofog"}, - {"mc_effect_falloff_add_nofog", "mc_effect_falloff_add_nofog_eyeoffset"}, - }; - void IMaterial::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - if (!header->data) this->loadJson(header, name, builder); // Check if we want to load a material from disk - if (!header->data) this->loadBinary(header, name, builder); // Check if we want to load a material from disk (binary format) + if (!header->data) this->loadFromDisk(header, name, builder); // Check if we want to load a material from disk if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one + assert(header->data); } - - void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder) + void IMaterial::loadFromDisk(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File materialInfo(std::format("materials/{}.iw4x.json", name)); - - if (!materialInfo.exists()) return; - - Game::Material* asset = builder->getAllocator()->allocate(); - - - nlohmann::json materialJson; - try - { - materialJson = nlohmann::json::parse(materialInfo.getBuffer()); - } - catch (const std::exception& e) - { - Components::Logger::Print("Invalid material json for {} (broken json {})\n", name, e.what()); - } - - if (!materialJson.is_object()) - { - Components::Logger::Print("Invalid material json for {} (Is it zonebuilder format?)\n", name); - return; - } - - if (materialJson["version"].get() != IW4X_MAT_JSON_VERSION) - { - Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid material json version for {}, expected {} and got {}\n", name, IW4X_MAT_JSON_VERSION, materialJson["version"].get()); - return; - } - - try - { - asset->info.name = builder->getAllocator()->duplicateString(materialJson["name"].get()); - asset->info.gameFlags = static_cast(Utils::Json::ReadFlags(materialJson["gameFlags"].get(), sizeof(char))); - - asset->info.sortKey = materialJson["sortKey"].get(); - // * We do techset later * // - asset->info.textureAtlasRowCount = materialJson["textureAtlasRowCount"].get(); - asset->info.textureAtlasColumnCount = materialJson["textureAtlasColumnCount"].get(); - asset->info.surfaceTypeBits = static_cast(Utils::Json::ReadFlags(materialJson["surfaceTypeBits"].get(), sizeof(int))); - asset->info.hashIndex = materialJson["hashIndex"].get(); - asset->cameraRegion = materialJson["cameraRegion"].get(); - } - catch (const nlohmann::json::exception& e) - { - Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid material json for {} (broken json {})\n", name, e.what()); - return; - } - - if (materialJson["gfxDrawSurface"].is_object()) - { - asset->info.drawSurf.fields.customIndex = materialJson["gfxDrawSurface"]["customIndex"].get(); - asset->info.drawSurf.fields.hasGfxEntIndex = materialJson["gfxDrawSurface"]["hasGfxEntIndex"].get(); - asset->info.drawSurf.fields.materialSortedIndex = materialJson["gfxDrawSurface"]["materialSortedIndex"].get(); - asset->info.drawSurf.fields.objectId = materialJson["gfxDrawSurface"]["objectId"].get(); - asset->info.drawSurf.fields.prepass = materialJson["gfxDrawSurface"]["prepass"].get(); - asset->info.drawSurf.fields.primarySortKey = materialJson["gfxDrawSurface"]["primarySortKey"].get(); - asset->info.drawSurf.fields.reflectionProbeIndex = materialJson["gfxDrawSurface"]["reflectionProbeIndex"].get(); - asset->info.drawSurf.fields.sceneLightIndex = materialJson["gfxDrawSurface"]["sceneLightIndex"].get(); - asset->info.drawSurf.fields.surfType = materialJson["gfxDrawSurface"]["surfType"].get(); - asset->info.drawSurf.fields.unused = materialJson["gfxDrawSurface"]["unused"].get(); - asset->info.drawSurf.fields.useHeroLighting = materialJson["gfxDrawSurface"]["useHeroLighting"].get(); - } - - asset->stateFlags = static_cast(Utils::Json::ReadFlags(materialJson["stateFlags"].get(), sizeof(char))); - - if (materialJson["textureTable"].is_array()) - { - nlohmann::json::array_t textureTable = materialJson["textureTable"]; - asset->textureCount = static_cast(textureTable.size()); - asset->textureTable = builder->getAllocator()->allocateArray(asset->textureCount); - - for (size_t i = 0; i < textureTable.size(); i++) - { - auto& textureJson = textureTable[i]; - if (textureJson.is_object()) - { - Game::MaterialTextureDef* textureDef = &asset->textureTable[i]; - textureDef->semantic = textureJson["semantic"].get(); - textureDef->samplerState = textureJson["samplerState"].get(); - textureDef->nameStart = textureJson["nameStart"].get(); - textureDef->nameEnd = textureJson["nameEnd"].get(); - textureDef->nameHash = textureJson["nameHash"].get(); - - if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP) - { - Game::water_t* water = builder->getAllocator()->allocate(); - - if (textureJson["water"].is_object()) - { - auto& waterJson = textureJson["water"]; - - if (waterJson["image"].is_string()) - { - auto imageName = waterJson["image"].get(); - - water->image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, imageName, builder).image; - } - - water->amplitude = waterJson["amplitude"].get(); - water->M = waterJson["M"].get(); - water->N = waterJson["N"].get(); - water->Lx = waterJson["Lx"].get(); - water->Lz = waterJson["Lz"].get(); - water->gravity = waterJson["gravity"].get(); - water->windvel = waterJson["windvel"].get(); - - auto winddir = waterJson["winddir"].get>(); - if (winddir.size() == 2) - { - std::copy(winddir.begin(), winddir.end(), water->winddir); - } - - auto codeConstant = waterJson["codeConstant"].get>(); - - if (codeConstant.size() == 4) - { - std::copy(codeConstant.begin(), codeConstant.end(), water->codeConstant); - } - - /// H0 - [[maybe_unused]] auto idealSize = water->M * water->N * sizeof(Game::complex_s); - auto h064 = waterJson["H0"].get(); - auto predictedSize = static_cast(std::ceilf((h064.size() / 4.f) * 3.f)); - assert(predictedSize >= idealSize); - - auto h0 = reinterpret_cast(builder->getAllocator()->allocate(predictedSize)); - - [[maybe_unused]] auto h0Result = base64_decode( - h064.data(), - h064.size(), - reinterpret_cast(h0), - &predictedSize - ); - - assert(h0Result == CRYPT_OK); - water->H0 = h0; - - /// WTerm - auto wTerm64 = waterJson["wTerm"].get(); - auto predictedWTermSize = static_cast(std::ceilf((wTerm64.size() / 4.f) * 3.f)); - - auto wTerm = reinterpret_cast(builder->getAllocator()->allocate(predictedWTermSize)); - - [[maybe_unused]] auto wTermResult = base64_decode( - wTerm64.data(), - wTerm64.size(), - reinterpret_cast(wTerm), - &predictedWTermSize - ); - - assert(wTermResult == CRYPT_OK); - water->wTerm = wTerm; - } - - textureDef->u.water = water; - } - else - { - textureDef->u.image = nullptr; - if (textureJson["image"].is_string()) - { - textureDef->u.image = Components::AssetHandler::FindAssetForZone - ( - Game::XAssetType::ASSET_TYPE_IMAGE, - textureJson["image"].get(), - builder - ).image; - } - } - } - } - } - - // Statebits - if (materialJson["stateBitsEntry"].is_array()) - { - nlohmann::json::array_t stateBitsEntry = materialJson["stateBitsEntry"]; - - for (size_t i = 0; i < std::min(stateBitsEntry.size(), 48u); i++) - { - asset->stateBitsEntry[i] = stateBitsEntry[i].get(); - } - } - - if (materialJson["stateBitsTable"].is_array()) - { - nlohmann::json::array_t array = materialJson["stateBitsTable"]; - asset->stateBitsCount = static_cast(array.size()); - - asset->stateBitsTable = builder->getAllocator()->allocateArray(array.size()); - - size_t statebitTableIndex = 0; - for (auto& jsonStateBitEntry : array) - { - auto stateBit = &asset->stateBitsTable[statebitTableIndex++]; - - unsigned int loadbits0 = 0; - unsigned int loadbits1 = 0; - -#define READ_INT_LB_FROM_JSON(x) unsigned int x = jsonStateBitEntry[#x].get() -#define READ_BOOL_LB_FROM_JSON(x) bool x = jsonStateBitEntry[#x].get() - - READ_INT_LB_FROM_JSON(srcBlendRgb); - READ_INT_LB_FROM_JSON(dstBlendRgb); - READ_INT_LB_FROM_JSON(blendOpRgb); - READ_INT_LB_FROM_JSON(srcBlendAlpha); - READ_INT_LB_FROM_JSON(dstBlendAlpha); - READ_INT_LB_FROM_JSON(blendOpAlpha); - READ_INT_LB_FROM_JSON(depthTest); - READ_INT_LB_FROM_JSON(polygonOffset); - - const auto alphaTest = jsonStateBitEntry["alphaTest"].get(); - const auto cullFace = jsonStateBitEntry["cullFace"].get(); - - READ_BOOL_LB_FROM_JSON(colorWriteRgb); - READ_BOOL_LB_FROM_JSON(colorWriteAlpha); - READ_BOOL_LB_FROM_JSON(polymodeLine); - - READ_BOOL_LB_FROM_JSON(gammaWrite); - READ_BOOL_LB_FROM_JSON(depthWrite); - READ_BOOL_LB_FROM_JSON(stencilFrontEnabled); - READ_BOOL_LB_FROM_JSON(stencilBackEnabled); - - READ_INT_LB_FROM_JSON(stencilFrontPass); - READ_INT_LB_FROM_JSON(stencilFrontFail); - READ_INT_LB_FROM_JSON(stencilFrontZFail); - READ_INT_LB_FROM_JSON(stencilFrontFunc); - READ_INT_LB_FROM_JSON(stencilBackPass); - READ_INT_LB_FROM_JSON(stencilBackFail); - READ_INT_LB_FROM_JSON(stencilBackZFail); - READ_INT_LB_FROM_JSON(stencilBackFunc); - - loadbits0 |= srcBlendRgb << Game::GFXS0_SRCBLEND_RGB_SHIFT; - loadbits0 |= dstBlendRgb << Game::GFXS0_DSTBLEND_RGB_SHIFT; - loadbits0 |= blendOpRgb << Game::GFXS0_BLENDOP_RGB_SHIFT; - loadbits0 |= srcBlendAlpha << Game::GFXS0_SRCBLEND_ALPHA_SHIFT; - loadbits0 |= dstBlendAlpha << Game::GFXS0_DSTBLEND_ALPHA_SHIFT; - loadbits0 |= blendOpAlpha << Game::GFXS0_BLENDOP_ALPHA_SHIFT; - - if (depthTest == -1) - { - loadbits1 |= Game::GFXS1_DEPTHTEST_DISABLE; - } - else - { - loadbits1 |= depthTest << Game::GFXS1_DEPTHTEST_SHIFT; - } - - loadbits1 |= polygonOffset << Game::GFXS1_POLYGON_OFFSET_SHIFT; - - if (alphaTest == "disable") - { - loadbits0 |= Game::GFXS0_ATEST_DISABLE; - } - else if (alphaTest == ">0") - { - loadbits0 |= Game::GFXS0_ATEST_GT_0; - } - else if (alphaTest == "<128") - { - loadbits0 |= Game::GFXS0_ATEST_LT_128; - } - else if (alphaTest == ">=128") - { - loadbits0 |= Game::GFXS0_ATEST_GE_128; - } - else - { - Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid alphatest loadbit0 '{}' in material {}\n", alphaTest, name); - return; - } - - if (cullFace == "none") - { - loadbits0 |= Game::GFXS0_CULL_NONE; - } - else if (cullFace == "back") - { - loadbits0 |= Game::GFXS0_CULL_BACK; - } - else if (cullFace == "front") - { - loadbits0 |= Game::GFXS0_CULL_FRONT; - } - else - { - Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid cullFace loadbit0 '{}' in material {}\n", cullFace, name); - return; - } - - if (gammaWrite) - { - loadbits0 |= Game::GFXS0_GAMMAWRITE; - } - - if (colorWriteAlpha) - { - loadbits0 |= Game::GFXS0_COLORWRITE_ALPHA; - } - - if (colorWriteRgb) - { - loadbits0 |= Game::GFXS0_COLORWRITE_RGB; - } - - if (polymodeLine) - { - loadbits0 |= Game::GFXS0_POLYMODE_LINE; - } - - if (depthWrite) - { - loadbits1 |= Game::GFXS1_DEPTHWRITE; - } - if (stencilFrontEnabled) - { - loadbits1 |= Game::GFXS1_STENCIL_FRONT_ENABLE; - } - if (stencilBackEnabled) - { - loadbits1 |= Game::GFXS1_STENCIL_BACK_ENABLE; - } - - loadbits1 |= stencilFrontPass << Game::GFXS1_STENCIL_FRONT_PASS_SHIFT; - loadbits1 |= stencilFrontFail << Game::GFXS1_STENCIL_FRONT_FAIL_SHIFT; - loadbits1 |= stencilFrontZFail << Game::GFXS1_STENCIL_FRONT_ZFAIL_SHIFT; - loadbits1 |= stencilFrontFunc << Game::GFXS1_STENCIL_FRONT_FUNC_SHIFT; - loadbits1 |= stencilBackPass << Game::GFXS1_STENCIL_BACK_PASS_SHIFT; - loadbits1 |= stencilBackFail << Game::GFXS1_STENCIL_BACK_FAIL_SHIFT; - loadbits1 |= stencilBackZFail << Game::GFXS1_STENCIL_BACK_ZFAIL_SHIFT; - loadbits1 |= stencilBackFunc << Game::GFXS1_STENCIL_BACK_FUNC_SHIFT; - - stateBit->loadBits[0] = loadbits0; - stateBit->loadBits[1] = loadbits1; - } - - // Constant table - if (materialJson["constantTable"].is_array()) - { - - nlohmann::json::array_t constants = materialJson["constantTable"]; - asset->constantCount = static_cast(constants.size()); - auto table = builder->getAllocator()->allocateArray(asset->constantCount); - - for (size_t constantIndex = 0; constantIndex < asset->constantCount; constantIndex++) - { - auto& constant = constants[constantIndex]; - auto entry = &table[constantIndex]; - - auto litVec = constant["literal"].get>(); - std::copy(litVec.begin(), litVec.end(), entry->literal); - - auto constantName = constant["name"].get(); - std::copy(constantName.begin(), constantName.end(), entry->name); - - entry->nameHash = constant["nameHash"].get(); - } - - asset->constantTable = table; - } - - if (materialJson["techniqueSet"].is_string()) - { - const std::string techsetName = materialJson["techniqueSet"].get(); - asset->techniqueSet = findWorkingTechset(techsetName, asset, builder); - - if (asset->techniqueSet == nullptr) - { - assert(false); - } - } - - header->material = asset; - } - } - - Game::MaterialTechniqueSet* IMaterial::findWorkingTechset(std::string techsetName, [[maybe_unused]] Game::Material* material, Components::ZoneBuilder::Zone* builder) const - { - Game::MaterialTechniqueSet* techset; - - // Pass 1: Identical techset (1:1) - techset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techsetName, builder).techniqueSet; - if (techset != nullptr) - { - return techset; - } - - // We do no more cause we use CoD4 techset and they should always be present - // If one day we want to go back to mw2 fallback we can add extra steps here! - - return nullptr; - } - - void IMaterial::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) - { - static const char* techsetSuffix[] = - { - "_lin", - "_add_lin", - "_replace", - "_eyeoffset", - - "_blend", - "_blend_nofog", - "_add", - "_nofog", - "_nocast", - - "_add_lin_nofog", - }; - - Components::FileSystem::File materialFile(std::format("materials/{}.iw4xMaterial", name)); - if (!materialFile.exists()) return; - - Utils::Stream::Reader reader(builder->getAllocator(), materialFile.getBuffer()); - - char* magic = reader.readArray(7); - if (std::memcmp(magic, "IW4xMat", 7) != 0) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading material '{}' failed, header is invalid!", name); - } - - std::string version; - version.push_back(reader.read()); - if (version != IW4X_MAT_BIN_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading material '{}' failed, expected version is {}, but it was {}!", name, IW4X_MAT_BIN_VERSION, version); - } - - auto* asset = reader.readObject(); - - if (asset->info.name) - { - asset->info.name = reader.readCString(); - } - - if (asset->techniqueSet) - { - std::string techsetName = reader.readString(); - if (!techsetName.empty() && techsetName.front() == ',') techsetName.erase(techsetName.begin()); - asset->techniqueSet = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techsetName, builder).techniqueSet; - - if (!asset->techniqueSet) - { - // Workaround for effect techsets having _nofog suffix - std::string suffix; - if (Utils::String::StartsWith(techsetName, "effect_") && Utils::String::EndsWith(techsetName, "_nofog")) - { - suffix = "_nofog"; - Utils::String::Replace(techsetName, suffix, ""); - } - - for (int i = 0; i < ARRAYSIZE(techsetSuffix); ++i) - { - Game::MaterialTechniqueSet* techsetPtr = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, (techsetName + techsetSuffix[i] + suffix), builder).techniqueSet; - - if (techsetPtr) - { - asset->techniqueSet = techsetPtr; - - if (asset->techniqueSet->name[0] == ',') continue; // Try to find a better one - Components::Logger::Print("Techset '{}' has been mapped to '{}'\n", techsetName, asset->techniqueSet->name); - break; - } - } - } - else - { - Components::Logger::Print("Techset {} exists with the same name in iw4, and was mapped 1:1 with {}\n", techsetName, asset->techniqueSet->name); - } - - if (!asset->techniqueSet) - { - Components::Logger::Error(Game::ERR_FATAL, "Missing techset: '{}' not found", techsetName); - } - } - - if (asset->textureTable) - { - asset->textureTable = reader.readArray(asset->textureCount); - - for (char i = 0; i < asset->textureCount; ++i) - { - Game::MaterialTextureDef* textureDef = &asset->textureTable[i]; - - if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP) - { - if (textureDef->u.water) - { - Game::water_t* water = reader.readObject(); - textureDef->u.water = water; - - // Save_water_t - if (water->H0) - { - water->H0 = reader.readArray(water->M * water->N); - } - - if (water->wTerm) - { - water->wTerm = reader.readArray(water->M * water->N); - } - - if (water->image) - { - water->image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString(), builder).image; - } - } - } - else if (textureDef->u.image) - { - textureDef->u.image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString(), builder).image; - } - } - } - - if (asset->constantTable) - { - asset->constantTable = reader.readArray(asset->constantCount); - } - - if (asset->stateBitsTable) - { - asset->stateBitsTable = reader.readArray(asset->stateBitsCount); - } - - header->material = asset; - - static thread_local bool replacementFound; - replacementFound = false; - - // Find correct sortkey by comparing techsets - Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset](Game::XAssetEntry* entry) - { - if (!replacementFound) - { - Game::XAssetHeader header = entry->asset.header; - - const char* name = asset->techniqueSet->name; - if (name[0] == ',') ++name; - - if (std::string(name) == header.material->techniqueSet->name) - { - asset->info.sortKey = header.material->info.sortKey; - - // This is temp, as nobody has time to fix materials - asset->stateBitsCount = header.material->stateBitsCount; - asset->stateBitsTable = header.material->stateBitsTable; - std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, ARRAYSIZE(asset->stateBitsEntry)); - asset->constantCount = header.material->constantCount; - asset->constantTable = header.material->constantTable; - Components::Logger::Print("For {}, copied constants & statebits from {}\n", asset->info.name, header.material->info.name); - replacementFound = true; - } - } - }, false); - - if (!replacementFound) - { - auto techsetMatches = [](Game::Material* m1, Game::Material* m2) - { - Game::MaterialTechniqueSet* t1 = m1->techniqueSet; - Game::MaterialTechniqueSet* t2 = m2->techniqueSet; - if (!t1 || !t2) return false; - if (t1->remappedTechniqueSet && t2->remappedTechniqueSet && std::string(t1->remappedTechniqueSet->name) == t2->remappedTechniqueSet->name) return true; - - for (int i = 0; i < ARRAYSIZE(t1->techniques); ++i) - { - if (!t1->techniques[i] && !t2->techniques[i]) continue;; - if (!t1->techniques[i] || !t2->techniques[i]) return false; - - // Apparently, this is really not that important - //if (t1->techniques[i]->flags != t2->techniques[i]->flags) return false; - } - - return true; - }; - - - Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, techsetMatches](Game::XAssetEntry* entry) - { - if (!replacementFound) - { - Game::XAssetHeader header = entry->asset.header; - - if (techsetMatches(header.material, asset)) - { - Components::Logger::Print("Material {} with techset {} has been mapped to {}\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name); - asset->info.sortKey = header.material->info.sortKey; - replacementFound = true; - } - } - }, false); - } - - if (!replacementFound && asset->techniqueSet) - { - Components::Logger::Print("No replacement found for material {} with techset {}\n", asset->info.name, asset->techniqueSet->name); - std::string techName = asset->techniqueSet->name; - if (const auto itr = techSetCorrespondance.find(techName); itr != techSetCorrespondance.end()) - { - auto& iw4TechSetName = itr->second; - auto* iw4TechSet = Game::DB_FindXAssetEntry(Game::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data()); - - if (iw4TechSet) - { - Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, iw4TechSet](Game::XAssetEntry* entry) - { - if (!replacementFound) - { - Game::XAssetHeader header = entry->asset.header; - Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\n", - asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name); - - // Yeah this has a tendency to fuck up a LOT of transparent materials - if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet && std::string(header.material->info.name).find("icon") != std::string::npos) - { - Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\n", - asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name); - - asset->info.sortKey = header.material->info.sortKey; - asset->techniqueSet = iw4TechSet->asset.header.techniqueSet; - - // this is terrible! - asset->stateBitsCount = header.material->stateBitsCount; - asset->stateBitsTable = header.material->stateBitsTable; - std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48); - asset->constantCount = header.material->constantCount; - asset->constantTable = header.material->constantTable; - - replacementFound = true; - } - } - }, false); - - if (!replacementFound) - { - Components::Logger::Print("Could not find any loaded material with techset {} (in replacement of {}), so I cannot set the sortkey for material {}\n", iw4TechSetName, asset->techniqueSet->name, asset->info.name); - } - } - else - { - Components::Logger::Print("Could not find any loaded techset with iw4 name {} for iw3 techset {}\n", iw4TechSetName, asset->techniqueSet->name); - } - } - else - { - Components::Logger::Print("Could not match iw3 techset {} with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable.\n", techName); - } - } - - if (!reader.end()) - { - Components::Logger::Error(Game::ERR_FATAL, "Material data left!"); - } - - /*char baseIndex = 0; - for (char i = 0; i < asset->stateBitsCount; ++i) - { - auto stateBits = asset->stateBitsTable[i]; - if (stateBits.loadBits[0] == 0x18128812 && - stateBits.loadBits[1] == 0xD) // Seems to be like a default stateBit causing a 'generic' initialization - { - baseIndex = i; - break; - } - } - - for (int i = 0; i < 48; ++i) - { - if (!asset->techniqueSet->techniques[i] && asset->stateBitsEntry[i] != -1) - { - asset->stateBitsEntry[i] = -1; - } - - if (asset->techniqueSet->techniques[i] && asset->stateBitsEntry[i] == -1) - { - asset->stateBitsEntry[i] = baseIndex; - } - }*/ + header->material = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_MATERIAL, name); } void IMaterial::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.hpp b/src/Components/Modules/AssetInterfaces/IMaterial.hpp index 2aa645b7..adec2e19 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.hpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.hpp @@ -10,11 +10,7 @@ namespace Assets void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; - void loadJson(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); + void loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); - void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); - - private: - Game::MaterialTechniqueSet* findWorkingTechset(const std::string techsetName, Game::Material* material, Components::ZoneBuilder::Zone* builder) const; }; } diff --git a/src/Components/Modules/AssetInterfaces/IMaterialPixelShader.cpp b/src/Components/Modules/AssetInterfaces/IMaterialPixelShader.cpp index 7c3737e4..99d56b85 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterialPixelShader.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterialPixelShader.cpp @@ -1,8 +1,6 @@ #include #include "IMaterialPixelShader.hpp" -#define GFX_RENDERER_SHADER_SM3 0 - namespace Assets { @@ -19,21 +17,7 @@ namespace Assets void IMaterialPixelShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File psFile(std::format("ps/{}.cso", name)); - if (!psFile.exists()) return; - - auto buff = psFile.getBuffer(); - auto programSize = buff.size() / 4; - Game::MaterialPixelShader* asset = builder->getAllocator()->allocate(); - - asset->name = builder->getAllocator()->duplicateString(name); - asset->prog.loadDef.loadForRenderer = GFX_RENDERER_SHADER_SM3; - asset->prog.loadDef.programSize = static_cast(programSize); - asset->prog.loadDef.program = builder->getAllocator()->allocateArray(programSize); - memcpy_s(asset->prog.loadDef.program, buff.size(), buff.data(), buff.size()); - - - header->pixelShader = asset; + header->pixelShader = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_PIXELSHADER, name); } void IMaterialPixelShader::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) diff --git a/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.cpp b/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.cpp index a7519a12..3329ac78 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.cpp @@ -11,6 +11,8 @@ namespace Assets { if (!header->data) this->loadFromDisk(header, name, builder); // Check if we need to import a new one into the game if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one + + assert(header->data); } void IMaterialTechniqueSet::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) @@ -18,198 +20,9 @@ namespace Assets header->techniqueSet = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).techniqueSet; } - void IMaterialTechniqueSet::loadTechniqueFromDisk(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::MaterialPass, 20); - - Components::FileSystem::File techFile(std::format("techniques/{}.iw4x.json", name)); - if (!techFile.exists()) - { - *tech = nullptr; - - Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing technique '{}'\n", name); - return; - } - - nlohmann::json technique; - - try - { - technique = nlohmann::json::parse(techFile.getBuffer()); - } - catch (std::exception& e) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what()); - } - - int version = technique["version"].get(); - - if (version != IW4X_TECHSET_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, - "Reading technique '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version); - } - - unsigned short flags = static_cast(Utils::Json::ReadFlags(technique["flags"].get(), sizeof(short))); - - if (technique["passArray"].is_array()) - { - nlohmann::json::array_t passArray = technique["passArray"]; - - Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passArray.size() - 1))); - - asset->name = builder->getAllocator()->duplicateString(name); - asset->flags = flags; - asset->passCount = static_cast(passArray.size()); - - Game::MaterialPass* passes = builder->getAllocator()->allocateArray(asset->passCount); - std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * asset->passCount); - - for (unsigned short i = 0; i < asset->passCount; i++) - { - Game::MaterialPass* pass = &asset->passArray[i]; - auto jsonPass = passArray[i]; - - if (jsonPass["vertexDeclaration"].is_string()) - { - auto declName = jsonPass["vertexDeclaration"].get(); - pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl; - } - - if (jsonPass["vertexShader"].is_string()) - { - auto vsName = jsonPass["vertexShader"].get(); - pass->vertexShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, vsName, builder).vertexShader; - } - - if (jsonPass["pixelShader"].is_string()) - { - auto psName = jsonPass["pixelShader"].get(); - pass->pixelShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PIXELSHADER, psName, builder).pixelShader; - } - - pass->perPrimArgCount = jsonPass["perPrimArgCount"].get(); - pass->perObjArgCount = jsonPass["perObjArgCount"].get(); - pass->stableArgCount = jsonPass["stableArgCount"].get(); - pass->customSamplerFlags = jsonPass["customSamplerFlags"].get(); - - - if (jsonPass["arguments"].is_array()) - { - nlohmann::json::array_t jsonAguments = jsonPass["arguments"]; - - pass->args = builder->getAllocator()->allocateArray(jsonAguments.size()); - - for (size_t j = 0; j < jsonAguments.size(); j++) - { - auto jsonArgument = jsonAguments[j]; - Game::MaterialShaderArgument* argument = &pass->args[j]; - - argument->type = jsonArgument["type"].get(); - argument->dest = jsonArgument["dest"].get(); - - if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_VERTEX_CONST || - argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_PIXEL_CONST) - { - argument->u.literalConst = builder->getAllocator()->allocateArray(4); - - auto literals = jsonArgument["literals"].get>(); - std::copy(literals.begin(), literals.end(), argument->u.literalConst); - } - else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_VERTEX_CONST || - argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_CONST) - { - if (jsonArgument["codeConst"].is_object()) - { - auto codeConst = jsonArgument["codeConst"]; - - argument->u.codeConst.index = codeConst["index"].get(); - argument->u.codeConst.firstRow = codeConst["firstRow"].get(); - argument->u.codeConst.rowCount = codeConst["rowCount"].get(); - } - } - else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_SAMPLER || - argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_VERTEX_CONST || - argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_CONST) - { - argument->u.nameHash = jsonArgument["nameHash"].get(); - } - else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_SAMPLER) - { - argument->u.codeSampler = jsonArgument["codeSampler"].get(); - } - } - } - } - - *tech = asset; - } - } - void IMaterialTechniqueSet::loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File tsFile(std::format("techsets/{}.iw4x.json", name)); - if (!tsFile.exists()) return; - - nlohmann::json techset; - - try - { - techset = nlohmann::json::parse(tsFile.getBuffer()); - } - catch (std::exception& e) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what()); - } - - auto version = techset["version"].get(); - if (version != IW4X_TECHSET_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, expected version is {}, but it was {}!", - name, IW4X_TECHSET_VERSION, version); - } - - Game::MaterialTechniqueSet* asset = builder->getAllocator()->allocate(); - - if (asset == nullptr) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, allocation failed!", name); - return; - } - - if (techset["name"].is_string()) - { - asset->name = builder->getAllocator()->duplicateString(techset["name"].get()); - } - - asset->hasBeenUploaded = techset["hasBeenUploaded"].get(); - asset->worldVertFormat = techset["worldVertFormat"].get(); - - - if (techset["remappedTechniqueSet"].is_string()) - { - auto remapped = techset["remappedTechniqueSet"].get(); - - if (remapped != asset->name) - { - builder->loadAssetByName(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, remapped, false); - } - } - - if (techset["techniques"].is_object()) - { - for (int i = 0; i < Game::TECHNIQUE_COUNT; i++) - { - auto technique = techset["techniques"].at(std::to_string(i)); - - if (technique.is_string()) - { - this->loadTechniqueFromDisk(&asset->techniques[i], technique.get(), builder); - } - } - } - - header->techniqueSet = asset; + header->techniqueSet = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, name); } void IMaterialTechniqueSet::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) diff --git a/src/Components/Modules/AssetInterfaces/IMaterialVertexDeclaration.cpp b/src/Components/Modules/AssetInterfaces/IMaterialVertexDeclaration.cpp index 26b16d11..af4894de 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterialVertexDeclaration.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterialVertexDeclaration.cpp @@ -18,32 +18,7 @@ namespace Assets void IMaterialVertexDeclaration::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File declFile(std::format("decl/{}.iw4xDECL", name)); - if (!declFile.exists()) return; - - Utils::Stream::Reader reader(builder->getAllocator(), declFile.getBuffer()); - - char* magic = reader.readArray(8); - if (std::memcmp(magic, "IW4xDECL", 8)) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, header is invalid!", name); - } - - auto version = reader.read(); - if (version != IW4X_TECHSET_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, expected version is {}, but it was {:d}!", - name, IW4X_TECHSET_VERSION, version); - } - - Game::MaterialVertexDeclaration* asset = reader.readObject(); - - if (asset->name) - { - asset->name = reader.readCString(); - } - - header->vertexDecl = asset; + header->vertexDecl = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_VERTEXDECL, name); } void IMaterialVertexDeclaration::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) diff --git a/src/Components/Modules/AssetInterfaces/IMaterialVertexShader.cpp b/src/Components/Modules/AssetInterfaces/IMaterialVertexShader.cpp index e4d7c9b5..bfa0c9b5 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterialVertexShader.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterialVertexShader.cpp @@ -18,20 +18,7 @@ namespace Assets void IMaterialVertexShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File vsFile(std::format("vs/{}.cso", name)); - if (!vsFile.exists()) return; - - auto buff = vsFile.getBuffer(); - auto programSize = buff.size() / 4; - Game::MaterialVertexShader* asset = builder->getAllocator()->allocate(); - - asset->name = builder->getAllocator()->duplicateString(name); - asset->prog.loadDef.loadForRenderer = GFX_RENDERER_SHADER_SM3; - asset->prog.loadDef.programSize = static_cast(programSize); - asset->prog.loadDef.program = builder->getAllocator()->allocateArray(programSize); - memcpy_s(asset->prog.loadDef.program, buff.size(), buff.data(), buff.size()); - - header->vertexShader = asset; + header->vertexShader = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, name); } void IMaterialVertexShader::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) diff --git a/src/Components/Modules/AssetInterfaces/IPhysPreset.cpp b/src/Components/Modules/AssetInterfaces/IPhysPreset.cpp index ad753a5c..d485e230 100644 --- a/src/Components/Modules/AssetInterfaces/IPhysPreset.cpp +++ b/src/Components/Modules/AssetInterfaces/IPhysPreset.cpp @@ -7,9 +7,9 @@ namespace Assets { AssertSize(Game::PhysPreset, 44); - Utils::Stream* buffer = builder->getBuffer(); - Game::PhysPreset* asset = header.physPreset; - Game::PhysPreset* dest = buffer->dest(); + auto* buffer = builder->getBuffer(); + auto* asset = header.physPreset; + auto* dest = buffer->dest(); buffer->save(asset); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); @@ -28,4 +28,14 @@ namespace Assets buffer->popBlock(); } + + void IPhysPreset::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) + { + loadFromDisk(header, name, builder); + } + + void IPhysPreset::loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) + { + header->physPreset = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_PHYSPRESET, name); + } } diff --git a/src/Components/Modules/AssetInterfaces/IPhysPreset.hpp b/src/Components/Modules/AssetInterfaces/IPhysPreset.hpp index f3939a08..47fb85e8 100644 --- a/src/Components/Modules/AssetInterfaces/IPhysPreset.hpp +++ b/src/Components/Modules/AssetInterfaces/IPhysPreset.hpp @@ -8,5 +8,7 @@ namespace Assets Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_PHYSPRESET; } void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; + void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; + void loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); }; } diff --git a/src/Components/Modules/AssetInterfaces/IRawFile.cpp b/src/Components/Modules/AssetInterfaces/IRawFile.cpp index a206c3f7..15782c93 100644 --- a/src/Components/Modules/AssetInterfaces/IRawFile.cpp +++ b/src/Components/Modules/AssetInterfaces/IRawFile.cpp @@ -7,38 +7,7 @@ namespace Assets { void IRawFile::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File rawFile(name); - - if (!rawFile.exists()) - { - return; - } - - auto* asset = builder->getAllocator()->allocate(); - if (!asset) - { - return; - } - - asset->name = builder->getAllocator()->duplicateString(name); - asset->len = static_cast(rawFile.getBuffer().size()); - - const auto compressedData = Utils::Compression::ZLib::Compress(rawFile.getBuffer()); - // Only save the compressed buffer if we gained space - if (compressedData.size() < rawFile.getBuffer().size()) - { - asset->buffer = builder->getAllocator()->allocateArray(compressedData.size()); - std::memcpy(const_cast(asset->buffer), compressedData.data(), compressedData.size()); - asset->compressedLen = static_cast(compressedData.size()); - } - else - { - asset->buffer = builder->getAllocator()->allocateArray(rawFile.getBuffer().size() + 1); - std::memcpy(const_cast(asset->buffer), rawFile.getBuffer().data(), rawFile.getBuffer().size()); - asset->compressedLen = 0; - } - - header->rawfile = asset; + header->rawfile = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_RAWFILE, name); } void IRawFile::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) diff --git a/src/Components/Modules/AssetInterfaces/ISndCurve.cpp b/src/Components/Modules/AssetInterfaces/ISndCurve.cpp index 1a7d0f01..a4228e67 100644 --- a/src/Components/Modules/AssetInterfaces/ISndCurve.cpp +++ b/src/Components/Modules/AssetInterfaces/ISndCurve.cpp @@ -7,9 +7,9 @@ namespace Assets { AssertSize(Game::SndCurve, 136); - Utils::Stream* buffer = builder->getBuffer(); - Game::SndCurve* asset = header.sndCurve; - Game::SndCurve* dest = buffer->dest(); + auto* buffer = builder->getBuffer(); + auto* asset = header.sndCurve; + auto* dest = buffer->dest(); buffer->save(asset); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); @@ -22,4 +22,9 @@ namespace Assets buffer->popBlock(); } + + void ISndCurve::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) + { + header->sndCurve = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, name); + } } diff --git a/src/Components/Modules/AssetInterfaces/ISndCurve.hpp b/src/Components/Modules/AssetInterfaces/ISndCurve.hpp index 0b0d7564..0d2cc831 100644 --- a/src/Components/Modules/AssetInterfaces/ISndCurve.hpp +++ b/src/Components/Modules/AssetInterfaces/ISndCurve.hpp @@ -8,5 +8,6 @@ namespace Assets Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_SOUND_CURVE; } void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; + void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; }; } diff --git a/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp b/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp index 0bd5e2ce..e8b9860c 100644 --- a/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp +++ b/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp @@ -1,108 +1,11 @@ #include #include "IXAnimParts.hpp" -#define IW4X_ANIM_VERSION 1 - namespace Assets { void IXAnimParts::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File animFile(std::format("xanim/{}.iw4xAnim", name)); - - if (animFile.exists()) - { - Utils::Stream::Reader reader(builder->getAllocator(), animFile.getBuffer()); - - __int64 magic = reader.read<__int64>(); - if (std::memcmp(&magic, "IW4xAnim", 8)) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, header is invalid!", name); - } - - int version = reader.read(); - if (version != IW4X_ANIM_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, expected version is {}, but it was {}!", name, IW4X_ANIM_VERSION, version); - } - - Game::XAnimParts* xanim = reader.readArray(); - - if (xanim) - { - if (xanim->name) - { - xanim->name = reader.readCString(); - } - - if (xanim->names) - { - xanim->names = builder->getAllocator()->allocateArray(xanim->boneCount[Game::PART_TYPE_ALL]); - for (int i = 0; i < xanim->boneCount[Game::PART_TYPE_ALL]; ++i) - { - xanim->names[i] = static_cast(Game::SL_GetString(reader.readCString(), 0)); - } - } - - if (xanim->notify) - { - xanim->notify = reader.readArray(xanim->notifyCount); - - for (int i = 0; i < xanim->notifyCount; ++i) - { - xanim->notify[i].name = static_cast(Game::SL_GetString(reader.readCString(), 0)); - } - } - - if (xanim->dataByte) - { - xanim->dataByte = reader.readArray(xanim->dataByteCount); - } - - if (xanim->dataShort) - { - xanim->dataShort = reader.readArray(xanim->dataShortCount); - } - - if (xanim->dataInt) - { - xanim->dataInt = reader.readArray(xanim->dataIntCount); - } - - if (xanim->randomDataByte) - { - xanim->randomDataByte = reader.readArray(xanim->randomDataByteCount); - } - - if (xanim->randomDataShort) - { - xanim->randomDataShort = reader.readArray(xanim->randomDataShortCount); - } - - if (xanim->randomDataInt) - { - xanim->randomDataInt = reader.readArray(xanim->randomDataIntCount); - } - - if (xanim->indices.data) - { - if (xanim->numframes < 256) - { - xanim->indices._1 = reader.readArray(xanim->indexCount); - } - else - { - xanim->indices._2 = reader.readArray(xanim->indexCount); - } - } - - if (!reader.end()) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, remaining raw data found!", name); - } - - header->parts = xanim; - } - } + header->parts = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_XANIMPARTS, name); } void IXAnimParts::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) diff --git a/src/Components/Modules/AssetInterfaces/IXModel.cpp b/src/Components/Modules/AssetInterfaces/IXModel.cpp index c8173537..d4444712 100644 --- a/src/Components/Modules/AssetInterfaces/IXModel.cpp +++ b/src/Components/Modules/AssetInterfaces/IXModel.cpp @@ -1,298 +1,30 @@ #include #include "IXModel.hpp" -#define IW4X_MODEL_VERSION 8 - namespace Assets { - void IXModel::loadXSurfaceCollisionTree(Game::XSurfaceCollisionTree* entry, Utils::Stream::Reader* reader) - { - if (entry->nodes) - { - entry->nodes = reader->readArrayOnce(entry->nodeCount); - } - - if (entry->leafs) - { - entry->leafs = reader->readArrayOnce(entry->leafCount); - } - } - - void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, [[maybe_unused]] Components::ZoneBuilder::Zone* builder) - { - if (surf->vertInfo.vertsBlend) - { - surf->vertInfo.vertsBlend = reader->readArrayOnce(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7)); - } - - // Access vertex block - if (surf->verts0) - { - surf->verts0 = reader->readArrayOnce(surf->vertCount); - } - - // Save_XRigidVertListArray - if (surf->vertList) - { - surf->vertList = reader->readArrayOnce(surf->vertListCount); - - for (unsigned int i = 0; i < surf->vertListCount; ++i) - { - Game::XRigidVertList* rigidVertList = &surf->vertList[i]; - - if (rigidVertList->collisionTree) - { - rigidVertList->collisionTree = reader->readObject(); - this->loadXSurfaceCollisionTree(rigidVertList->collisionTree, reader); - } - } - } - - // Access index block - if (surf->triIndices) - { - surf->triIndices = reader->readArrayOnce(surf->triCount * 3); - } - } - - void IXModel::loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder) - { - if (asset->name) - { - asset->name = reader->readCString(); - } - - if (asset->surfs) - { - asset->surfs = reader->readArrayOnce(asset->numsurfs); - - for (int i = 0; i < asset->numsurfs; ++i) - { - this->loadXSurface(&asset->surfs[i], reader, builder); - } - } - } - void IXModel::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File modelFile(std::format("xmodel/{}.iw4xModel", name)); + header->model = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_XMODEL, name); - if (!builder->isPrimaryAsset() && (!Components::ZoneBuilder::ZBPreferDiskAssets.get() || !modelFile.exists())) + if (header->model) { - header->model = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).model; - if (header->model) return; - } - - - if (modelFile.exists()) - { - Utils::Stream::Reader reader(builder->getAllocator(), modelFile.getBuffer()); - - __int64 magic = reader.read<__int64>(); - if (std::memcmp(&magic, "IW4xModl", 8)) + // ??? + if (header->model->physCollmap) { - Components::Logger::Error(Game::ERR_FATAL, "Reading model '{}' failed, header is invalid!", name); + Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, { header->model->physCollmap }); } - int version = reader.read(); - if (version != IW4X_MODEL_VERSION) + if (header->model->physPreset) { - Components::Logger::Error(Game::ERR_FATAL, "Reading model '{}' failed, expected version is {}, but it was {}!", name, IW4X_MODEL_VERSION, version); + Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, { header->model->physPreset }); } - Game::XModel* asset = reader.readObject(); - - if (asset->name) + for (size_t i = 0; i < header->model->numLods; i++) { - asset->name = reader.readCString(); + const auto& info = header->model->lodInfo[i]; + Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, { info.modelSurfs }); } - - if (asset->boneNames) - { - asset->boneNames = builder->getAllocator()->allocateArray(asset->numBones); - - for (char i = 0; i < asset->numBones; ++i) - { - asset->boneNames[i] = static_cast(Game::SL_GetString(reader.readCString(), 0)); - } - } - - if (asset->parentList) - { - asset->parentList = reader.readArrayOnce(asset->numBones - asset->numRootBones); - } - - if (asset->quats) - { - asset->quats = reader.readArrayOnce((asset->numBones - asset->numRootBones) * 4); - } - - if (asset->trans) - { - asset->trans = reader.readArrayOnce((asset->numBones - asset->numRootBones) * 3); - } - - if (asset->partClassification) - { - asset->partClassification = reader.readArrayOnce(asset->numBones); - } - - if (asset->baseMat) - { - asset->baseMat = reader.readArrayOnce(asset->numBones); - } - - if (asset->materialHandles) - { - asset->materialHandles = reader.readArray(asset->numsurfs); - - for (unsigned char i = 0; i < asset->numsurfs; ++i) - { - if (asset->materialHandles[i]) - { - asset->materialHandles[i] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material; - } - } - } - - // Save_XModelLodInfoArray - { - for (unsigned int i = 0; i < 4; ++i) - { - if (asset->lodInfo[i].modelSurfs) - { - asset->lodInfo[i].modelSurfs = reader.readObject(); - this->loadXModelSurfs(asset->lodInfo[i].modelSurfs, &reader, builder); - Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, { asset->lodInfo[i].modelSurfs }); - - asset->lodInfo[i].surfs = asset->lodInfo[i].modelSurfs->surfs; - - // Zero that for now, it breaks the models. - // TODO: Figure out how that can be converted - asset->lodInfo[i].smcBaseIndexPlusOne = 0; - asset->lodInfo[i].smcSubIndexMask = 0; - asset->lodInfo[i].smcBucket = 0; - } - } - } - - // Save_XModelCollSurfArray - if (asset->collSurfs) - { - asset->collSurfs = reader.readArray(asset->numCollSurfs); - - for (int i = 0; i < asset->numCollSurfs; ++i) - { - Game::XModelCollSurf_s* collSurf = &asset->collSurfs[i]; - - if (collSurf->collTris) - { - collSurf->collTris = reader.readArray(collSurf->numCollTris); - } - } - } - - if (asset->boneInfo) - { - asset->boneInfo = reader.readArray(asset->numBones); - } - - if (asset->physPreset) - { - asset->physPreset = reader.readObject(); - - if (asset->physPreset->name) - { - asset->physPreset->name = reader.readCString(); - } - - if (asset->physPreset->sndAliasPrefix) - { - asset->physPreset->sndAliasPrefix = reader.readCString(); - } - - // This is an experiment, ak74 fails though - if (asset->name == "weapon_ak74u"s) - { - asset->physPreset = nullptr; - } - else - { - Game::PhysPreset* preset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PHYSPRESET, asset->physPreset->name, builder).physPreset; - if (preset) - { - asset->physPreset = preset; - } - else - { - Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, { asset->physPreset }); - } - } - } - - if (asset->physCollmap) - { - Game::PhysCollmap* collmap = reader.readObject(); - asset->physCollmap = collmap; - - if (collmap->name) - { - collmap->name = reader.readCString(); - } - - if (collmap->geoms) - { - collmap->geoms = reader.readArray(collmap->count); - - for (unsigned int i = 0; i < collmap->count; ++i) - { - Game::PhysGeomInfo* geom = &collmap->geoms[i]; - - if (geom->brushWrapper) - { - Game::BrushWrapper* brush = reader.readObject(); - geom->brushWrapper = brush; - { - if (brush->brush.sides) - { - brush->brush.sides = reader.readArray(brush->brush.numsides); - for (unsigned short j = 0; j < brush->brush.numsides; ++j) - { - Game::cbrushside_t* side = &brush->brush.sides[j]; - - // TODO: Add pointer support - if (side->plane) - { - side->plane = reader.readObject(); - } - } - } - - if (brush->brush.baseAdjacentSide) - { - brush->brush.baseAdjacentSide = reader.readArray(brush->totalEdgeCount); - } - } - - // TODO: Add pointer support - if (brush->planes) - { - brush->planes = reader.readArray(brush->brush.numsides); - } - } - } - - Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, { asset->physCollmap }); - // asset->physCollmap = nullptr; - } - } - - if (!reader.end()) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading model '{}' failed, remaining raw data found!", name); - } - - header->model = asset; } } diff --git a/src/Components/Modules/AssetInterfaces/IXModel.hpp b/src/Components/Modules/AssetInterfaces/IXModel.hpp index 44388f3d..c43ffec2 100644 --- a/src/Components/Modules/AssetInterfaces/IXModel.hpp +++ b/src/Components/Modules/AssetInterfaces/IXModel.hpp @@ -10,11 +10,5 @@ namespace Assets void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; - - private: - std::map triIndicies; - void loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder); - void loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder); - void loadXSurfaceCollisionTree(Game::XSurfaceCollisionTree* entry, Utils::Stream::Reader* reader); }; } diff --git a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp index f0524227..5d3ed66e 100644 --- a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp +++ b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp @@ -1,8 +1,6 @@ #include #include "IclipMap_t.hpp" -#define IW4X_CLIPMAP_VERSION 2 - namespace Assets { void IclipMap_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) @@ -37,7 +35,7 @@ namespace Assets buffer->align(Utils::Stream::ALIGN_4); // not sure if this is needed but both brushside and brushedge need it and it can't hurt - for (int i = 0; i < asset->planeCount; ++i) + for (size_t i = 0; i < asset->planeCount; ++i) { builder->storePointer(&asset->planes[i]); buffer->save(&asset->planes[i]); @@ -192,7 +190,13 @@ namespace Assets SaveLogEnter("cLeafBrush_t"); buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->leafbrushes, asset->numLeafBrushes); + + for (size_t i = 0; i < asset->numLeafBrushes; i++) + { + builder->storePointer(&asset->leafbrushes[i]); + buffer->saveObject(asset->leafbrushes[i]); + } + Utils::Stream::ClearPointer(&dest->leafbrushes); SaveLogExit(); @@ -213,6 +217,9 @@ namespace Assets { if (node[i].data.leaf.brushes) { + assert(node[i].data.leaf.brushes >= asset->leafbrushes); + assert(node[i].data.leaf.brushes < asset->leafbrushes + sizeof(unsigned short) * asset->numLeafBrushes); + if (builder->hasPointer(node[i].data.leaf.brushes)) { node[i].data.leaf.brushes = builder->getPointer(node[i].data.leaf.brushes); @@ -278,7 +285,7 @@ namespace Assets buffer->align(Utils::Stream::ALIGN_4); - for (int i = 0; i < asset->borderCount; ++i) + for (size_t i = 0; i < asset->borderCount; ++i) { builder->storePointer(&asset->borders[i]); buffer->save(&asset->borders[i]); @@ -564,381 +571,13 @@ namespace Assets } } } + builder->loadAsset(Game::XAssetType::ASSET_TYPE_MAP_ENTS, asset); } void IclipMap_t::load(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder) { - std::string name = _name; - Utils::String::Replace(name, "maps/mp/", ""); - Utils::String::Replace(name, ".d3dbsp", ""); - - Components::FileSystem::File clipFile(std::format("clipmap/{}.iw4xClipMap", name)); - if (!clipFile.exists()) - { - return; - } - - Game::clipMap_t* clipMap = builder->getAllocator()->allocate(); - if (!clipMap) - { - Components::Logger::Print("Error allocating memory for clipMap_t structure!\n"); - return; - } - - Game::clipMap_t* orgClipMap = nullptr; - Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_CLIPMAP_MP, [](Game::XAssetHeader header, void* clipMap) - { - if (!*reinterpret_cast(clipMap)) - { - *reinterpret_cast(clipMap) = header.clipMap; - } - }, &orgClipMap, false); - - if (orgClipMap) std::memcpy(clipMap, orgClipMap, sizeof Game::clipMap_t); - - Utils::Stream::Reader reader(builder->getAllocator(), clipFile.getBuffer()); - - __int64 magic = reader.read<__int64>(); - if (std::memcmp(&magic, "IW4xClip", 8)) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading clipMap_t '{}' failed, header is invalid!", name); - } - - int version = reader.read(); - if (version > IW4X_CLIPMAP_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading clipmap '{}' failed, expected version is {}, but it was {}!", name, IW4X_CLIPMAP_VERSION, version); - } - - clipMap->name = reader.readCString(); - - clipMap->planeCount = reader.read(); - clipMap->numStaticModels = reader.read(); - clipMap->numMaterials = reader.read(); - clipMap->numBrushSides = reader.read(); - clipMap->numBrushEdges = reader.read(); - clipMap->numNodes = reader.read(); - clipMap->numLeafs = reader.read(); - clipMap->leafbrushNodesCount = reader.read(); - clipMap->numLeafBrushes = reader.read(); - clipMap->numLeafSurfaces = reader.read(); - clipMap->vertCount = reader.read(); - clipMap->triCount = reader.read(); - clipMap->borderCount = reader.read(); - clipMap->partitionCount = reader.read(); - clipMap->aabbTreeCount = reader.read(); - clipMap->numSubModels = reader.read(); - clipMap->numBrushes = reader.read(); - clipMap->dynEntCount[0] = reader.read(); - clipMap->dynEntCount[1] = reader.read(); - - if (clipMap->planeCount) - { - void* oldPtr = reader.read(); - clipMap->planes = reader.readArray(clipMap->planeCount); - - if (builder->getAllocator()->isPointerMapped(oldPtr)) - { - clipMap->planes = builder->getAllocator()->getPointer(oldPtr); - Components::Logger::Print("ClipMap dpvs planes already mapped. This shouldn't happen. Make sure to load the ClipMap before the GfxWorld!\n"); - } - else - { - builder->getAllocator()->mapPointer(oldPtr, clipMap->planes); - } - } - - if (clipMap->numStaticModels) - { - clipMap->staticModelList = builder->getAllocator()->allocateArray(clipMap->numStaticModels); - for (unsigned int i = 0; i < clipMap->numStaticModels; ++i) - { - std::string modelName = reader.readString(); - if (modelName != "NONE"s) - { - clipMap->staticModelList[i].xmodel = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, modelName, builder).model; - } - float* buf = reader.readArray(18); - memcpy(&clipMap->staticModelList[i].origin, buf, sizeof(float) * 18); - } - } - - if (clipMap->numMaterials) - { - clipMap->materials = builder->getAllocator()->allocateArray(clipMap->numMaterials); - for (unsigned int j = 0; j < clipMap->numMaterials; ++j) - { - clipMap->materials[j].name = reader.readArray(64); - clipMap->materials[j].surfaceFlags = reader.read(); - clipMap->materials[j].contents = reader.read(); - } - } - - if (clipMap->numBrushSides) - { - clipMap->brushsides = builder->getAllocator()->allocateArray(clipMap->numBrushSides); - for (unsigned int i = 0; i < clipMap->numBrushSides; ++i) - { - int planeIndex = reader.read(); - if (planeIndex < 0 || planeIndex >= clipMap->planeCount) - { - Components::Logger::Error(Game::ERR_FATAL, "invalid plane index"); - return; - } - clipMap->brushsides[i].plane = &clipMap->planes[planeIndex]; - clipMap->brushsides[i].materialNum = static_cast(reader.read()); // materialNum - clipMap->brushsides[i].firstAdjacentSideOffset = static_cast(reader.read()); // firstAdjacentSide - clipMap->brushsides[i].edgeCount = reader.read(); // edgeCount - } - } - - if (clipMap->numBrushEdges) - { - clipMap->brushEdges = reader.readArray(clipMap->numBrushEdges); - } - - if (clipMap->numNodes) - { - clipMap->nodes = builder->getAllocator()->allocateArray(clipMap->numNodes); - for (unsigned int i = 0; i < clipMap->numNodes; ++i) - { - int planeIndex = reader.read(); - if (planeIndex < 0 || planeIndex >= clipMap->planeCount) - { - Components::Logger::Error(Game::ERR_FATAL, "invalid plane index\n"); - return; - } - clipMap->nodes[i].plane = &clipMap->planes[planeIndex]; - clipMap->nodes[i].children[0] = reader.read(); - clipMap->nodes[i].children[1] = reader.read(); - } - } - - if (clipMap->numLeafs) - { - clipMap->leafs = reader.readArray(clipMap->numLeafs); - } - - if (clipMap->leafbrushNodesCount) - { - clipMap->leafbrushNodes = builder->getAllocator()->allocateArray(clipMap->leafbrushNodesCount); - for (unsigned int i = 0; i < clipMap->leafbrushNodesCount; ++i) - { - clipMap->leafbrushNodes[i] = reader.read(); - - if (clipMap->leafbrushNodes[i].leafBrushCount > 0) - { - clipMap->leafbrushNodes[i].data.leaf.brushes = reader.readArray(clipMap->leafbrushNodes[i].leafBrushCount); - } - } - } - - if (clipMap->numLeafBrushes) - { - clipMap->leafbrushes = reader.readArray(clipMap->numLeafBrushes); - } - - if (clipMap->numLeafSurfaces) - { - clipMap->leafsurfaces = reader.readArray(clipMap->numLeafSurfaces); - } - - if (clipMap->vertCount) - { - clipMap->verts = reader.readArray(clipMap->vertCount); - } - - if (clipMap->triCount) - { - clipMap->triIndices = reader.readArray(clipMap->triCount * 3); - clipMap->triEdgeIsWalkable = reader.readArray(4 * ((3 * clipMap->triCount + 31) >> 5)); - } - - if (clipMap->borderCount) - { - clipMap->borders = reader.readArray(clipMap->borderCount); - } - - if (clipMap->partitionCount) - { - clipMap->partitions = builder->getAllocator()->allocateArray(clipMap->partitionCount); - for (int i = 0; i < clipMap->partitionCount; ++i) - { - clipMap->partitions[i].triCount = reader.read(); - clipMap->partitions[i].borderCount = reader.read(); - clipMap->partitions[i].firstTri = reader.read(); - - if (clipMap->partitions[i].borderCount > 0) - { - int index = reader.read(); - if (index < 0 || index > clipMap->borderCount) - { - Components::Logger::Error(Game::ERR_FATAL, "invalid border index\n"); - return; - } - clipMap->partitions[i].borders = &clipMap->borders[index]; - } - } - } - - if (clipMap->aabbTreeCount) - { - clipMap->aabbTrees = reader.readArray(clipMap->aabbTreeCount); - } - - if (clipMap->numSubModels) - { - clipMap->cmodels = reader.readArray(clipMap->numSubModels); - } - - if (clipMap->numBrushes) - { - clipMap->brushes = builder->getAllocator()->allocateArray(clipMap->numBrushes); - memset(clipMap->brushes, 0, sizeof(Game::cbrush_t) * clipMap->numBrushes); - for (int i = 0; i < clipMap->numBrushes; ++i) - { - clipMap->brushes[i].numsides = reader.read() & 0xFFFF; // todo: check for overflow here - if (clipMap->brushes[i].numsides > 0) - { - auto index = reader.read(); - if (index < 0 || index > clipMap->numBrushSides) - { - Components::Logger::Error(Game::ERR_FATAL, "invalid side index\n"); - return; - } - clipMap->brushes[i].sides = &clipMap->brushsides[index]; - } - else - { - clipMap->brushes[i].sides = nullptr; - } - - auto index = reader.read(); - if (index > clipMap->numBrushEdges) - { - Components::Logger::Error(Game::ERR_FATAL, "invalid edge index\n"); - return; - } - clipMap->brushes[i].baseAdjacentSide = &clipMap->brushEdges[index]; - - char* tmp = reader.readArray(12); - memcpy(&clipMap->brushes[i].axialMaterialNum, tmp, 12); - - //todo check for overflow - for (int r = 0; r < 2; ++r) - { - for (int c = 0; c < 3; ++c) - { - clipMap->brushes[i].firstAdjacentSideOffsets[r][c] = reader.read() & 0xFF; - } - } - - tmp = reader.readArray(6); - memcpy(&clipMap->brushes[i].edgeCount, tmp, 6); - } - - clipMap->brushBounds = reader.readArray(clipMap->numBrushes); - clipMap->brushContents = reader.readArray(clipMap->numBrushes); - } - - for (int x = 0; x < 2; ++x) - { - if (clipMap->dynEntCount[x]) - { - clipMap->dynEntDefList[x] = builder->getAllocator()->allocateArray(clipMap->dynEntCount[x]); - for (int i = 0; i < clipMap->dynEntCount[x]; ++i) - { - clipMap->dynEntDefList[x][i].type = reader.read(); - clipMap->dynEntDefList[x][i].pose = reader.read(); - std::string tempName = reader.readString(); - if (tempName != "NONE"s) - { - clipMap->dynEntDefList[x][i].xModel = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, tempName, builder).model; - } - - clipMap->dynEntDefList[x][i].brushModel = reader.read(); - clipMap->dynEntDefList[x][i].physicsBrushModel = reader.read(); - - tempName = reader.readString(); - if (tempName != "NONE"s) - { - clipMap->dynEntDefList[x][i].destroyFx = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, tempName, builder).fx; - } - - tempName = reader.readString(); - if (tempName != "NONE"s) - { - clipMap->dynEntDefList[x][i].physPreset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PHYSPRESET, tempName, builder).physPreset; - } - - clipMap->dynEntDefList[x][i].health = reader.read(); - clipMap->dynEntDefList[x][i].mass = reader.read(); - clipMap->dynEntDefList[x][i].contents = reader.read(); - } - } - } - - clipMap->smodelNodeCount = reader.read(); - clipMap->smodelNodes = reader.readArray(clipMap->smodelNodeCount); - - clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, std::format("maps/mp/{}.d3dbsp", name), builder).mapEnts; - - // add triggers to mapEnts - if (version >= 2) { - if (clipMap->numSubModels > 0) { - clipMap->mapEnts->trigger.count = clipMap->numSubModels; - clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels; - - Game::TriggerHull* hulls = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.hullCount); - Game::TriggerModel* models = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.count); - - for (unsigned int i = 0; i < clipMap->numSubModels; ++i) - { - models[i] = reader.read(); - hulls[i] = reader.read(); - } - - size_t slabCount = reader.read(); - clipMap->mapEnts->trigger.slabCount = slabCount; - Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.slabCount); - for (unsigned int i = 0; i < clipMap->mapEnts->trigger.slabCount; i++) { - slabs[i] = reader.read(); - } - - - clipMap->mapEnts->trigger.models = &models[0]; - clipMap->mapEnts->trigger.hulls = &hulls[0]; - clipMap->mapEnts->trigger.slabs = &slabs[0]; - } - } - - clipMap->checksum = reader.read(); - - // This mustn't be null and has to have at least 1 'valid' entry. - if (!clipMap->smodelNodeCount || !clipMap->smodelNodes) - { - clipMap->smodelNodeCount = 1; - clipMap->smodelNodes = builder->getAllocator()->allocateArray(clipMap->smodelNodeCount); - - clipMap->smodelNodes[0].bounds.halfSize[0] = -131072.000f; - clipMap->smodelNodes[0].bounds.halfSize[1] = -131072.000f; - clipMap->smodelNodes[0].bounds.halfSize[2] = -131072.000f; - } - - // These mustn't be null, but they don't need to be valid. - for (int i = 0; i < 2 && clipMap->dynEntCount[i]; ++i) - { - Utils::Stream::ClearPointer(&clipMap->dynEntPoseList[i]); - Utils::Stream::ClearPointer(&clipMap->dynEntClientList[i]); - Utils::Stream::ClearPointer(&clipMap->dynEntCollList[i]); - } - - if (!reader.end()) - { - Components::Logger::Error(Game::ERR_FATAL, "Clipmap data left!"); - } - - header->clipMap = clipMap; + header->clipMap = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_CLIPMAP_MP, _name); + assert(header->data); } } diff --git a/src/Components/Modules/AssetInterfaces/IclipMap_t.hpp b/src/Components/Modules/AssetInterfaces/IclipMap_t.hpp index fbd5e307..2edb3089 100644 --- a/src/Components/Modules/AssetInterfaces/IclipMap_t.hpp +++ b/src/Components/Modules/AssetInterfaces/IclipMap_t.hpp @@ -10,6 +10,7 @@ namespace Assets void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; + static void dump(Game::XAssetHeader /*header*/); private: class SModelQuadtree diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index 545acc75..9d321160 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -7,351 +7,7 @@ namespace Assets { void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File aliasFile(std::format("sounds/{}.json", name)); - if (!aliasFile.exists()) - { - header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).sound; - return; - } - - auto* aliasList = builder->getAllocator()->allocate(); - if (!aliasList) - { - Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Failed to allocate memory for sound alias structure!\n"); - return; - } - - nlohmann::json infoData; - try - { - infoData = nlohmann::json::parse(aliasFile.getBuffer()); - } - catch (const nlohmann::json::parse_error& ex) - { - Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}\n", ex.what()); - return; - } - - - nlohmann::json aliasesContainer = infoData["head"]; - nlohmann::json::array_t aliases = aliasesContainer; - - aliasList->count = aliases.size(); - - // Allocate - aliasList->head = builder->getAllocator()->allocateArray(aliasList->count); - if (!aliasList->head) - { - Components::Logger::Print("Error allocating memory for sound alias structure!\n"); - return; - } - - aliasList->aliasName = builder->getAllocator()->duplicateString(name); - - for (size_t i = 0; i < aliasList->count; i++) - { - nlohmann::json head = aliasesContainer[i]; - - if (!infoData.is_object()) - { - Components::Logger::Error(Game::ERR_FATAL, "Failed to load sound {}!", name); - return; - } - - aliasList->head->soundFile = builder->getAllocator()->allocate(); - if (!aliasList->head->soundFile) - { - Components::Logger::Print("Error allocating memory for sound alias structure!\n"); - return; - } - - Game::snd_alias_t* alias = aliasList->head; - - // try and parse everything and if it fails then fail for the whole file - auto type = head["type"]; - auto subtitle = head["subtitle"]; - auto secondaryAliasName = head["secondaryAliasName"]; - auto chainAliasName = head["chainAliasName"]; - auto soundFile = head["soundFile"]; - auto sequence = head["sequence"]; - auto volMin = head["volMin"]; - auto volMax = head["volMax"]; - auto pitchMin = head["pitchMin"]; - auto pitchMax = head["pitchMax"]; - auto distMin = head["distMin"]; - auto distMax = head["distMax"]; - auto flags = head["flags"]; - auto slavePercentage = head["slavePercentage"]; - auto probability = head["probability"]; - auto lfePercentage = head["lfePercentage"]; - auto centerPercentage = head["centerPercentage"]; - auto startDelay = head["startDelay"]; - auto volumeFalloffCurve = head["volumeFalloffCurve"]; - auto envelopMin = head["envelopMin"]; - auto envelopMax = head["envelopMax"]; - auto envelopPercentage = head["envelopPercentage"]; - auto speakerMap = head["speakerMap"]; - auto aliasName = head["aliasName"]; - - // Fix casing - if (soundFile.is_null()) - { - soundFile = head["soundfile"]; - - Components::Logger::Print("Fixed casing on {}\n", name); - } - - if (type.is_null() || soundFile.is_null()) - { - Components::Logger::Print("Type is {}\n", type.dump()); - Components::Logger::Print("SoundFile is {}\n", soundFile.dump()); - Components::Logger::Error(Game::ERR_FATAL, "Failed to parse sound {}! Each alias must have at least a type and a soundFile\n", name); - return; - } - -#define CHECK(x, type) (x.is_##type##() || x.is_null()) - - // TODO: actually support all of those properties - if (!CHECK(type, number)) - { - Components::Logger::Print("{} is not number but {} ({})\n", "type", Utils::Json::TypeToString(type.type()), type.dump()); - } - - if (!CHECK(subtitle, string)) - { - Components::Logger::Print("{} is not string but {} ({})\n", "subtitle", Utils::Json::TypeToString(subtitle.type()), subtitle.dump()); - } - - if (!CHECK(aliasName, string)) - { - Components::Logger::Print("{} is not string but {} ({})\n", "aliasName", Utils::Json::TypeToString(aliasName.type()), aliasName.dump()); - } - - if (!CHECK(secondaryAliasName, string)) - { - Components::Logger::Print("{} is not string but {} ({})\n", "secondaryAliasName", Utils::Json::TypeToString(secondaryAliasName.type()), secondaryAliasName.dump()); - } - - if (!CHECK(chainAliasName, string)) - { - Components::Logger::Print("{} is not string but {} ({})\n", "chainAliasName", Utils::Json::TypeToString(chainAliasName.type()), chainAliasName.dump()); - } - - if (!CHECK(soundFile, string)) - { - Components::Logger::Print("{} is not string but {} ({})\n", "soundFile", Utils::Json::TypeToString(soundFile.type()), soundFile.dump()); - } - - if (!CHECK(sequence, number)) - { - Components::Logger::Print("{} is not number but {} ({})\n", "sequence", Utils::Json::TypeToString(sequence.type()), sequence.dump()); - } - - if (!CHECK(volMin, number)) - { - Components::Logger::Print("{} is not number but {} ({})\n", "volMin", Utils::Json::TypeToString(volMin.type()), volMin.dump()); - } - - if (!CHECK(volMax, number)) - { - Components::Logger::Print("{} is not number but {} ({})\n", "volMax", Utils::Json::TypeToString(volMax.type()), volMax.dump()); - } - - if (!CHECK(pitchMin, number)) - { - Components::Logger::Print("{} is not number but {} ({})\n", "pitchMin", Utils::Json::TypeToString(pitchMin.type()), pitchMin.dump()); - } - - if (!CHECK(pitchMax, number)) - { - Components::Logger::Print("{} is not number but {} ()\n", "pitchMax", Utils::Json::TypeToString(pitchMax.type()), pitchMax.dump()); - } - - if (!CHECK(probability, number)) - { - Components::Logger::Print("{} is not number but {} ({}))\n", "probability", Utils::Json::TypeToString(probability.type()), probability.dump()); - } - - if (!CHECK(lfePercentage, number)) - { - Components::Logger::Print("{} is not number but {} ({})\n", "lfePercentage", Utils::Json::TypeToString(lfePercentage.type()), lfePercentage.dump()); - } - - if (!CHECK(centerPercentage, number)) - { - Components::Logger::Print("{} is not number but {} ({})\n", "centerPercentage", Utils::Json::TypeToString(centerPercentage.type()), centerPercentage.dump()); - } - - if (!CHECK(startDelay, number)) - { - Components::Logger::Print("{} is not number but {} ({})\n", "startDelay", Utils::Json::TypeToString(startDelay.type()), startDelay.dump()); - } - - if (!CHECK(volumeFalloffCurve, string)) - { - Components::Logger::Print("{}s is not string but {} ({})\n", "volumeFalloffCurve", Utils::Json::TypeToString(volumeFalloffCurve.type()), volumeFalloffCurve.dump()); - } - - if (!CHECK(envelopMin, number)) - { - Components::Logger::Print("{} is not number but {} ({})\n", "envelopMin", Utils::Json::TypeToString(envelopMin.type()), envelopMin.dump()); - } - - if (!CHECK(envelopMax, number)) - { - Components::Logger::Print("{} is not number but {} ({})\n", "envelopMax", Utils::Json::TypeToString(envelopMax.type()), envelopMax.dump()); - } - - if (!CHECK(envelopPercentage, number)) - { - Components::Logger::Print("{} is not number but {} ({})\n", "envelopPercentage", Utils::Json::TypeToString(envelopPercentage.type()), envelopPercentage.dump()); - } - - if (!CHECK(speakerMap, object)) - { - Components::Logger::Print("{} is not object but {} ({})\n", "speakerMap", Utils::Json::TypeToString(speakerMap.type()), speakerMap.dump()); - } - - - if (CHECK(type, number) && CHECK(aliasName, string) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) && - CHECK(soundFile, string) && CHECK(sequence, number) && CHECK(volMin, number) && CHECK(volMax, number) && CHECK(pitchMin, number) && - CHECK(pitchMax, number) && CHECK(distMin, number) && CHECK(distMax, number) && CHECK(flags, number) && CHECK(slavePercentage, number) && - CHECK(probability, number) && CHECK(lfePercentage, number) && CHECK(centerPercentage, number) && CHECK(startDelay, number) && - CHECK(volumeFalloffCurve, string) && CHECK(envelopMin, number) && CHECK(envelopMax, number) && CHECK(envelopPercentage, number) && - CHECK(speakerMap, object)) - { - - alias->soundFile->exists = true; - alias->aliasName = builder->getAllocator()->duplicateString(aliasName.get()); - - if (subtitle.is_string()) - { - alias->subtitle = builder->getAllocator()->duplicateString(subtitle.get()); - } - if (secondaryAliasName.is_string()) - { - alias->secondaryAliasName = builder->getAllocator()->duplicateString(secondaryAliasName.get()); - } - if (chainAliasName.is_string()) - { - alias->chainAliasName = builder->getAllocator()->duplicateString(chainAliasName.get()); - } - - alias->sequence = sequence.get(); - alias->volMin = volMin.get(); - alias->volMax = volMax.get(); - alias->pitchMin = pitchMin.get(); - alias->pitchMax = pitchMax.get(); - alias->distMin = distMin.get(); - alias->distMax = distMax.get(); - alias->flags = flags.get(); - alias->___u15.slavePercentage = slavePercentage.get(); - alias->probability = probability.get(); - alias->lfePercentage = lfePercentage.get(); - alias->centerPercentage = centerPercentage.get(); - alias->startDelay = startDelay.get(); - alias->envelopMin = envelopMin.get(); - alias->envelopMax = envelopMax.get(); - alias->envelopPercentage = envelopPercentage.get(); - - // Speaker map object - if (!speakerMap.is_null()) - { - alias->speakerMap = builder->getAllocator()->allocate(); - if (!alias->speakerMap) - { - Components::Logger::Print("Error allocating memory for speakermap in sound alias{}!\n", alias->aliasName); - return; - } - - alias->speakerMap->name = builder->getAllocator()->duplicateString(speakerMap["name"].get()); - alias->speakerMap->isDefault = speakerMap["isDefault"].get(); - - if (speakerMap["channelMaps"].is_array()) - { - nlohmann::json::array_t channelMaps = speakerMap["channelMaps"]; - - assert(channelMaps.size() <= 4); - - // channelMapIndex should never exceed 1 - for (size_t channelMapIndex = 0; channelMapIndex < 2; channelMapIndex++) - { - // subChannelIndex should never exceed 1 - for (size_t subChannelIndex = 0; subChannelIndex < 2; subChannelIndex++) - { - nlohmann::json channelMap = channelMaps[channelMapIndex * 2 + subChannelIndex]; // 0-3 - - nlohmann::json::array_t speakers = channelMap["speakers"]; - - alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount = speakers.size(); - - for (size_t speakerIndex = 0; speakerIndex < alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount; speakerIndex++) - { - auto speaker = speakers[speakerIndex]; - alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[0] = speaker["levels0"].get(); - alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[1] = speaker["levels1"].get(); - alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].numLevels = speaker["numLevels"].get(); - alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].speaker = speaker["speaker"].get(); - } - } - } - } - } - - if (volumeFalloffCurve.is_string()) - { - auto fallOffCurve = volumeFalloffCurve.get(); - - if (fallOffCurve.empty()) - { - fallOffCurve = "$default"; - } - - auto curve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, fallOffCurve, builder).sndCurve; - alias->volumeFalloffCurve = curve; - } - - if (static_cast(type.get()) == Game::snd_alias_type_t::SAT_LOADED) // Loaded - { - alias->soundFile->type = Game::SAT_LOADED; - alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.get(), builder).loadSnd; - } - else if (static_cast(type.get()) == Game::snd_alias_type_t::SAT_STREAMED) // Streamed - { - alias->soundFile->type = Game::SAT_STREAMED; - - std::string streamedFile = soundFile.get(); - std::string directory = ""s; - int split = streamedFile.find_last_of('/'); - - if (split >= 0) - { - directory = streamedFile.substr(0, split); - streamedFile = streamedFile.substr(split+1); - } - - alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(directory.c_str()); - alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.c_str()); - } - else - { - Components::Logger::Error(Game::ERR_FATAL, "Failed to parse sound {}! Invalid sound type {}\n", name, type.get()); - return; - } - - aliasList->head[i] = *alias; - } - else - { - Components::Logger::Error(Game::ERR_FATAL, "Failed to parse sound {}!\n", name); - return; - } - } - - header->sound = aliasList; - -#undef CHECK - + header->sound = builder->getIW4OfApi()->read(Game::XAssetType::ASSET_TYPE_SOUND, name); } void Isnd_alias_list_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) @@ -392,8 +48,16 @@ namespace Assets if (asset->aliasName) { - buffer->saveString(builder->getAssetName(this->getType(), asset->aliasName)); - Utils::Stream::ClearPointer(&dest->aliasName); + if (builder->hasPointer(asset->aliasName)) + { + dest->aliasName = builder->getPointer(asset->aliasName); + } + else + { + builder->storePointer(asset->aliasName); + buffer->saveString(asset->aliasName); + Utils::Stream::ClearPointer(&dest->aliasName); + } } if (asset->head) @@ -419,8 +83,16 @@ namespace Assets if (alias->aliasName) { - buffer->saveString(alias->aliasName); - Utils::Stream::ClearPointer(&destAlias->aliasName); + if (builder->hasPointer(alias->aliasName)) + { + destAlias->aliasName = builder->getPointer(alias->aliasName); + } + else + { + builder->storePointer(alias->aliasName); + buffer->saveString(alias->aliasName); + Utils::Stream::ClearPointer(&destAlias->aliasName); + } } if (alias->subtitle) @@ -467,7 +139,7 @@ namespace Assets { if (alias->soundFile->type == Game::snd_alias_type_t::SAT_LOADED) { - destSoundFile->u.loadSnd = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, alias->soundFile->u.loadSnd).loadSnd; + destSoundFile->u.loadSnd = builder->saveSubAsset(Game::ASSET_TYPE_LOADED_SOUND, alias->soundFile->u.loadSnd).loadSnd; } else { @@ -494,7 +166,7 @@ namespace Assets if (alias->volumeFalloffCurve) { - destAlias->volumeFalloffCurve = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve).sndCurve; + destAlias->volumeFalloffCurve = builder->saveSubAsset(Game::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve).sndCurve; } if (alias->speakerMap) diff --git a/src/Components/Modules/Renderer.cpp b/src/Components/Modules/Renderer.cpp index cd0a75de..796cb2c0 100644 --- a/src/Components/Modules/Renderer.cpp +++ b/src/Components/Modules/Renderer.cpp @@ -159,10 +159,11 @@ namespace Components // show error pushad + push eax push ebx push edx call R_TextureFromCodeError - add esp, 8 + add esp, 0xC popad // go back diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 675e0b30..db46346f 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -19,8 +19,6 @@ namespace Components volatile bool ZoneBuilder::CommandThreadTerminate = false; std::thread ZoneBuilder::CommandThread; - Dvar::Var ZoneBuilder::ZBPreferDiskAssets; - ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0), // Reserve 100MB by default. // That's totally fine, as the dedi doesn't load images and therefore doesn't need much memory. @@ -28,8 +26,12 @@ namespace Components // Side note: if you need a fastfile larger than 100MB, you're doing it wrong- // Well, decompressed maps can get way larger than 100MB, so let's increase that. buffer(0xC800000), - zoneName(name), dataMap("zone_source/" + name + ".csv"), branding{nullptr}, assetDepth(0) + zoneName(name), + dataMap("zone_source/" + name + ".csv"), + branding{nullptr}, + assetDepth(0) { + this->initializeIW4OfApi(); } ZoneBuilder::Zone::~Zone() @@ -98,6 +100,11 @@ namespace Components return &this->memAllocator; } + iw4of::api* ZoneBuilder::Zone::getIW4OfApi() + { + return &iw4ofApi; + } + void ZoneBuilder::Zone::Zone::build() { if (!this->dataMap.isValid()) @@ -750,6 +757,49 @@ namespace Components return header; } + void ZoneBuilder::Zone::initializeIW4OfApi() + { + iw4of::params_t params; + + params.find_other_asset = [this](int type, const std::string& name) -> void* + { + return AssetHandler::FindAssetForZone(static_cast(type), name, this).data; + }; + + params.fs_read_file = [](const std::string& filename) -> std::string + { + auto file = FileSystem::File(filename); + if (file.exists()) + { + return file.getBuffer(); + } + + return {}; + }; + + params.store_in_string_table = [](const std::string& text) -> unsigned int + { + return Game::SL_GetString(text.data(), 0); + }; + + params.print = [](iw4of::params_t::print_type t, const std::string& message) -> void + { + switch (t) + { + case iw4of::params_t::P_ERR: + Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}", message); + break; + case iw4of::params_t::P_WARN: + Logger::Print("{}", message); + break; + } + }; + + params.work_directory = (*Game::fs_basepath)->current.string; + + this->iw4ofApi = iw4of::api{ params }; + } + int ZoneBuilder::StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image) { size_t size = 16 + (*loadDef)->resourceSize; @@ -1590,9 +1640,6 @@ namespace Components Logger::Print("{}\n", nlohmann::json(images).dump()); Logger::Print("------------------- END IWI DUMP -------------------\n"); }); - - // True by default, but can be put to zero for backward compatibility if needed - ZoneBuilder::ZBPreferDiskAssets = Dvar::Register("zb_prefer_disk_assets", true, Game::DVAR_NONE, "Should ZoneBuilder prefer in-memory assets (requirements) or disk assets when both are present?"); } } diff --git a/src/Components/Modules/ZoneBuilder.hpp b/src/Components/Modules/ZoneBuilder.hpp index 46cd387a..5bbc5359 100644 --- a/src/Components/Modules/ZoneBuilder.hpp +++ b/src/Components/Modules/ZoneBuilder.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #define XFILE_MAGIC_UNSIGNED 0x3030317566665749 #define XFILE_VERSION 276 @@ -38,6 +40,7 @@ namespace Components Utils::Stream* getBuffer(); Utils::Memory::Allocator* getAllocator(); + iw4of::api* getIW4OfApi(); bool hasPointer(const void* pointer); void storePointer(const void* pointer); @@ -87,11 +90,14 @@ namespace Components void addBranding(); + void initializeIW4OfApi(); + uint32_t safeGetPointer(const void* pointer); int indexStart; unsigned int externalSize; Utils::Stream buffer; + iw4of::api iw4ofApi; std::string zoneName; Utils::CSV dataMap; @@ -131,7 +137,6 @@ namespace Components static std::vector> EndAssetTrace(); static Game::XAssetHeader GetEmptyAssetIfCommon(Game::XAssetType type, const std::string& name, Zone* builder); - static Dvar::Var ZBPreferDiskAssets; private: static int StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 4ba05281..15f0cc33 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -882,8 +882,8 @@ namespace Game { float normal[3]; float dist; - char type; - char pad[3]; + unsigned char type; + unsigned char pad[3]; }; struct cbrushside_t @@ -896,13 +896,13 @@ namespace Game struct cbrush_t { - unsigned __int16 numsides; - unsigned __int16 glassPieceIndex; + unsigned short numsides; + unsigned short glassPieceIndex; cbrushside_t* sides; - char* baseAdjacentSide; - __int16 axialMaterialNum[2][3]; - char firstAdjacentSideOffsets[2][3]; - char edgeCount[2][3]; + unsigned char* baseAdjacentSide; + unsigned short axialMaterialNum[2][3]; + unsigned char firstAdjacentSideOffsets[2][3]; + unsigned char edgeCount[2][3]; }; struct BrushWrapper @@ -2614,6 +2614,13 @@ namespace Game SASYS_COUNT = 0x3, }; + struct SoundFile + { + char type; + char exists; + SoundFileRef u; + }; + struct MSSSpeakerLevels { int speaker; @@ -2634,11 +2641,24 @@ namespace Game MSSChannelMap channelMaps[2][2]; }; - struct SoundFile + union SoundAliasFlags { - char type; - char exists; - SoundFileRef u; +#pragma warning(push) +#pragma warning(disable: 4201) + struct + { + unsigned int looping : 1; // & 1 / 0x1 / 0000 0000 0000 0001 + unsigned int isMaster : 1; // & 2 / 0x2 / 0000 0000 0000 0010 + unsigned int isSlave : 1; // & 4 / 0x4 / 0000 0000 0000 0100 + unsigned int fullDryLevel : 1; // & 8 / 0x8 / 0000 0000 0000 1000 + unsigned int noWetLevel : 1; // & 16 / 0x10 / 0000 0000 0001 0000 + unsigned int unknown : 1; // & 32 / 0x20 / 0000 0000 0010 0000 + unsigned int unk_is3D : 1; // & 64 / 0x40 / 0000 0000 0100 0000 // CONFIRMED IW4 IW5 + unsigned int type : 2; // & 384 / 0x180 / 0000 0001 1000 0000 // CONFIRMED IW4 IW5 + unsigned int channel : 6; // & 32256 / 0x7E00 / 0111 1110 0000 0000 // CONFIRMED IW4 IW5 + }; +#pragma warning(pop) + unsigned int intValue; }; struct SndCurve @@ -2664,7 +2684,7 @@ namespace Game float distMin; float distMax; float velocityMin; - int flags; + SoundAliasFlags flags; union { float slavePercentage; @@ -2817,7 +2837,7 @@ namespace Game struct cLeafBrushNode_s { - char axis; + unsigned char axis; __int16 leafBrushCount; int contents; cLeafBrushNodeData_t data; @@ -2834,9 +2854,9 @@ namespace Game struct CollisionPartition { - char triCount; - char borderCount; - char firstVertSegment; + unsigned char triCount; + unsigned char borderCount; + unsigned char firstVertSegment; int firstTri; CollisionBorder* borders; }; @@ -3173,7 +3193,7 @@ namespace Game { const char* name; int isInUse; - int planeCount; + unsigned int planeCount; cplane_s* planes; unsigned int numStaticModels; cStaticModel_s* staticModelList; @@ -3182,7 +3202,7 @@ namespace Game unsigned int numBrushSides; cbrushside_t* brushsides; unsigned int numBrushEdges; - char* brushEdges; + unsigned char* brushEdges; unsigned int numNodes; cNode_t* nodes; unsigned int numLeafs; @@ -3194,15 +3214,15 @@ namespace Game unsigned int numLeafSurfaces; unsigned int* leafsurfaces; unsigned int vertCount; - float(*verts)[3]; - int triCount; + vec3_t* verts; + unsigned int triCount; unsigned __int16* triIndices; - char* triEdgeIsWalkable; - int borderCount; + unsigned char* triEdgeIsWalkable; + unsigned int borderCount; CollisionBorder* borders; int partitionCount; CollisionPartition* partitions; - int aabbTreeCount; + unsigned int aabbTreeCount; CollisionAabbTree* aabbTrees; unsigned int numSubModels; cmodel_t* cmodels; @@ -3574,9 +3594,9 @@ namespace Game float texCoordOrigin[2]; unsigned int supportMask; float areaX2; - char defIndex; - char vertCount; - char fanDataCount; + unsigned char defIndex; + unsigned char vertCount; + unsigned char fanDataCount; char pad[1]; }; diff --git a/src/Utils/Json.cpp b/src/Utils/Json.cpp index ff401f86..02961a27 100644 --- a/src/Utils/Json.cpp +++ b/src/Utils/Json.cpp @@ -62,4 +62,12 @@ namespace Utils::Json return input.to_ulong(); } + Game::Bounds ReadBounds(const nlohmann::json& value) + { + Game::Bounds bounds{}; + CopyArray(bounds.midPoint, value["midPoint"]); + CopyArray(bounds.halfSize, value["halfSize"]); + + return bounds; + } } diff --git a/src/Utils/Json.hpp b/src/Utils/Json.hpp index 463e24b0..bf208ea9 100644 --- a/src/Utils/Json.hpp +++ b/src/Utils/Json.hpp @@ -1,8 +1,25 @@ #pragma once +#include + namespace Utils::Json { std::string TypeToString(nlohmann::json::value_t type); - unsigned long ReadFlags(std::string binaryFlags, size_t size); + unsigned long ReadFlags(const std::string binaryFlags, size_t size); + + Game::Bounds ReadBounds(const nlohmann::json& value); + + template void CopyArray(T* destination, const nlohmann::json& json_member, size_t count = 0) + { + if (count == 0) + { + count = json_member.size(); + } + + for (size_t i = 0; i < count; i++) + { + destination[i] = json_member[i].get(); + } + } } diff --git a/src/Utils/Maths.cpp b/src/Utils/Maths.cpp index 2b840002..35f12ff3 100644 --- a/src/Utils/Maths.cpp +++ b/src/Utils/Maths.cpp @@ -7,7 +7,7 @@ namespace Utils::Maths return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } - void VectorSubtract(float va[3], float vb[3], float out[3]) + void VectorSubtract(const float va[3], const float vb[3], float out[3]) { out[0] = va[0] - vb[0]; out[1] = va[1] - vb[1]; @@ -35,7 +35,7 @@ namespace Utils::Maths out[2] = v[2] * scale; } - float Vec3SqrDistance(float v1[3], float v2[3]) + float Vec3SqrDistance(const float v1[3], const float v2[3]) { float out[3]; diff --git a/src/Utils/Maths.hpp b/src/Utils/Maths.hpp index 47f37ed5..7d2111df 100644 --- a/src/Utils/Maths.hpp +++ b/src/Utils/Maths.hpp @@ -7,9 +7,9 @@ namespace Utils::Maths constexpr auto VectorNegate(float x[3]) { x[0] = -x[0]; x[1] = -x[1]; x[2] = -x[2]; } float DotProduct(float v1[3], float v2[3]); - void VectorSubtract(float va[3], float vb[3], float out[3]); + void VectorSubtract(const float va[3], const float vb[3], float out[3]); void VectorAdd(float va[3], float vb[3], float out[3]); void VectorCopy(float in[3], float out[3]); void VectorScale(float v[3], float scale, float out[3]); - float Vec3SqrDistance(float v1[3], float v2[3]); + float Vec3SqrDistance(const float v1[3], const float v2[3]); } diff --git a/src/Utils/Stream.cpp b/src/Utils/Stream.cpp index 7aa0d189..43b3aa98 100644 --- a/src/Utils/Stream.cpp +++ b/src/Utils/Stream.cpp @@ -31,12 +31,11 @@ namespace Utils void* Stream::Reader::read(size_t size, std::size_t count) { - size_t bytes = size * count; + auto bytes = size * count; if ((this->position_ + bytes) <= this->buffer_.size()) { - void* buffer = this->allocator_->allocate(bytes); - + auto* buffer = this->allocator_->allocate(bytes); std::memcpy(buffer, this->buffer_.data() + this->position_, bytes); this->position_ += bytes; @@ -84,7 +83,7 @@ namespace Utils bool Stream::Reader::hasPointer(void* pointer) const { - return this->pointerMap_.find(pointer) != this->pointerMap_.end(); + return this->pointerMap_.contains(pointer); } Stream::Stream() : ptrAssertion(false), criticalSectionState(0) @@ -159,7 +158,7 @@ namespace Utils return this->at(); } - auto data = this->data(); + auto* data = this->data(); if (this->isCriticalSection() && this->length() + (size * count) > this->capacity()) { diff --git a/src/Utils/Stream.hpp b/src/Utils/Stream.hpp index 18970c58..1ef8d1c9 100644 --- a/src/Utils/Stream.hpp +++ b/src/Utils/Stream.hpp @@ -12,6 +12,9 @@ namespace Utils { + constexpr auto POINTER = 255; + constexpr auto FOLLOWING = 254; + class Stream { private: @@ -42,9 +45,6 @@ namespace Utils template T* readArrayOnce(std::size_t count = 1) { - constexpr auto POINTER = 255; - constexpr auto FOLLOWING = 254; - auto b = static_cast(readByte()); switch (b) { @@ -65,6 +65,7 @@ namespace Utils auto filePosition = position_; auto data = readArray(count); allocator_->mapPointer(reinterpret_cast(filePosition), data); + return data; } default: @@ -123,11 +124,13 @@ namespace Utils Stream(size_t size); ~Stream(); + std::unordered_map dataPointers; + [[nodiscard]] std::size_t length() const; [[nodiscard]] std::size_t capacity() const; - char* save(const void * _str, std::size_t size, std::size_t count = 1); - char* save(Game::XFILE_BLOCK_TYPES stream, const void * _str, std::size_t size, std::size_t count); + char* save(const void * str, std::size_t size, std::size_t count = 1); + char* save(Game::XFILE_BLOCK_TYPES stream, const void * str, std::size_t size, std::size_t count); char* save(Game::XFILE_BLOCK_TYPES stream, int value, std::size_t count); template char* save(T* object) @@ -135,6 +138,38 @@ namespace Utils return saveArray(object, 1); } + template char* saveObject(T value) + { + return saveArray(&value, 1); + } + + template void saveArrayIfNotExisting(T* data, size_t count) + { + if (const auto itr = dataPointers.find(data); itr != dataPointers.end()) + { + saveByte(POINTER); + saveObject(itr->second); + } + else + { + saveByte(FOLLOWING); + dataPointers.insert_or_assign(reinterpret_cast(data), length()); + saveArray(data, count); + } + } + + char* save(int value, size_t count = 1) + { + auto ret = this->length(); + + for (size_t i = 0; i < count; ++i) + { + this->save(&value, 4, 1); + } + + return this->data() + ret; + } + template char* saveArray(T* array, std::size_t count) { return save(array, sizeof(T), count);