diff --git a/src/Components/Modules/ArenaLength.cpp b/src/Components/Modules/ArenaLength.cpp index 391cf24b..c2efd804 100644 --- a/src/Components/Modules/ArenaLength.cpp +++ b/src/Components/Modules/ArenaLength.cpp @@ -112,5 +112,9 @@ namespace Components Utils::Hook::Set(0x4A95F8, 32); Utils::Hook::Set(0x42F22B, offsetof(Game::newMapArena_t, mapName) - offsetof(Game::newMapArena_t, other)); + + // patch max arena count + constexpr auto arenaCount = sizeof(ArenaLength::NewArenas) / sizeof(Game::newMapArena_t); + Utils::Hook::Set(0x630AA3, arenaCount); } } diff --git a/src/Components/Modules/AssetHandler.cpp b/src/Components/Modules/AssetHandler.cpp index 6042048a..aa3d2faf 100644 --- a/src/Components/Modules/AssetHandler.cpp +++ b/src/Components/Modules/AssetHandler.cpp @@ -216,7 +216,7 @@ namespace Components } // Fix shader const stuff - if (type == Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET && Zones::Version() >= 359) + if (type == Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET && Zones::Version() >= 359 && Zones::Version() < 448) { for (int i = 0; i < 48; ++i) { @@ -533,6 +533,210 @@ namespace Components AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool*) { +#ifdef DEBUG +// #define DUMP_TECHSETS +#ifdef DUMP_TECHSETS + if (type == Game::XAssetType::ASSET_TYPE_VERTEXDECL && !name.empty() && name[0] != ',') + { + std::filesystem::create_directories("techsets/vertexdecl"); + + auto vertexdecl = asset.vertexDecl; + + std::vector routingData; + for (int i = 0; i < vertexdecl->streamCount; i++) + { + routingData.push_back(json11::Json::object + { + { "source", (int)vertexdecl->routing.data[i].source }, + { "dest", (int)vertexdecl->routing.data[i].dest }, + }); + } + + std::vector declData; + for (int i = 0; i < 16; i++) + { + if (vertexdecl->routing.decl[i]) + { + routingData.push_back(int(vertexdecl->routing.decl[i])); + } + else + { + routingData.push_back(nullptr); + } + } + + json11::Json vertexData = json11::Json::object + { + { "name", vertexdecl->name }, + { "streamCount", vertexdecl->streamCount }, + { "hasOptionalSource", vertexdecl->hasOptionalSource }, + { "routing", routingData }, + { "decl", declData }, + }; + + auto stringData = vertexData.dump(); + + auto fp = fopen(Utils::String::VA("techsets/vertexdecl/%s.%s.json", vertexdecl->name, Zones::Version() > 276 ? "codo" : "iw4"), "wb"); + fwrite(&stringData[0], stringData.size(), 1, fp); + fclose(fp); + } + + if (type == Game::ASSET_TYPE_TECHNIQUE_SET && !name.empty() && name[0] != ',') + { + std::filesystem::create_directory("techsets"); + std::filesystem::create_directories("techsets/techniques"); + + auto techset = asset.techniqueSet; + + std::vector techniques; + for (int technique = 0; technique < 48; technique++) + { + auto curTech = techset->techniques[technique]; + if (curTech) + { + std::vector passDataArray; + for (int pass = 0; pass < curTech->passCount; pass++) + { + auto curPass = &curTech->passArray[pass]; + + std::vector argDataArray; + for (int arg = 0; arg < curPass->perObjArgCount + curPass->perPrimArgCount + curPass->stableArgCount; arg++) + { + auto curArg = &curPass->args[arg]; + + if (curArg->type == 1 || curArg->type == 7) + { + std::vector literalConsts; + if (curArg->u.literalConst != 0) + { + literalConsts.push_back(curArg->u.literalConst[0]); + literalConsts.push_back(curArg->u.literalConst[1]); + literalConsts.push_back(curArg->u.literalConst[2]); + literalConsts.push_back(curArg->u.literalConst[3]); + } + + json11::Json argData = json11::Json::object + { + { "type", curArg->type }, + { "value", literalConsts }, + }; + argDataArray.push_back(argData); + } + else if (curArg->type == 3 || curArg->type == 5) + { + json11::Json argData = json11::Json::object + { + { "type", curArg->type }, + { "firstRow", curArg->u.codeConst.firstRow }, + { "rowCount", curArg->u.codeConst.rowCount }, + { "index", curArg->u.codeConst.index }, + }; + argDataArray.push_back(argData); + } + else + { + json11::Json argData = json11::Json::object + { + { "type", curArg->type }, + { "value", static_cast(curArg->u.codeSampler) }, + }; + argDataArray.push_back(argData); + } + } + + json11::Json passData = json11::Json::object + { + { "perObjArgCount", curPass->perObjArgCount }, + { "perPrimArgCount", curPass->perPrimArgCount }, + { "stableArgCount", curPass->stableArgCount }, + { "args", argDataArray }, + { "pixelShader", curPass->pixelShader ? curPass->pixelShader->name : "" }, + { "vertexShader", curPass->vertexShader ? curPass->vertexShader->name : "" }, + { "vertexDecl", curPass->vertexDecl ? curPass->vertexDecl->name : "" }, + }; + passDataArray.push_back(passData); + } + + json11::Json techData = json11::Json::object + { + { "name", curTech->name }, + { "index", technique }, + { "flags", curTech->flags }, + { "numPasses", curTech->passCount }, + { "pass", passDataArray }, + }; + + auto stringData = techData.dump(); + + auto fp = fopen(Utils::String::VA("techsets/techniques/%s.%s.json", curTech->name, Zones::Version() > 276 ? "codo" : "iw4"), "wb"); + fwrite(&stringData[0], stringData.size(), 1, fp); + fclose(fp); + + json11::Json techsetTechnique = json11::Json::object + { + { "name", curTech->name }, + { "index", technique }, + }; + techniques.push_back(techsetTechnique); + } + else + { + techniques.push_back(nullptr); + } + } + + json11::Json techsetData = json11::Json::object + { + { "name", techset->name }, + { "techniques", techniques }, + }; + + auto stringData = techsetData.dump(); + + auto fp = fopen(Utils::String::VA("techsets/%s.%s.json", techset->name, Zones::Version() > 276 ? "codo" : "iw4"), "wb"); + fwrite(&stringData[0], stringData.size(), 1, fp); + fclose(fp); + } +#endif + + if (type == Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET && Zones::Version() >= 460) + { + auto techset = asset.techniqueSet; + if (techset) + { + for (int t = 0; t < 48; t++) + { + if (techset->techniques[t]) + { + for (int p = 0; p < techset->techniques[t]->passCount; p++) + { + for (int a = 0; a < techset->techniques[t]->passArray[p].perObjArgCount + + techset->techniques[t]->passArray[p].perPrimArgCount + + techset->techniques[t]->passArray[p].stableArgCount; a++) + { + auto arg = &techset->techniques[t]->passArray[p].args[a]; + if (arg->type == 3 || arg->type == 5) + { + if (arg->u.codeConst.index > 140) + { + OutputDebugStringA(Utils::String::VA("codeConst %i is out of range for %s::%s[%i]\n", arg->u.codeConst.index, + techset->name, techset->techniques[t]->name, p)); + + if (!ZoneBuilder::IsEnabled()) + { + __debugbreak(); + } + } + } + } + } + } + } + } + } + +#endif + if (Dvar::Var("r_noVoid").get() && type == Game::XAssetType::ASSET_TYPE_XMODEL && name == "void") { asset.model->numLods = 0; @@ -540,16 +744,16 @@ namespace Components }); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP, 1); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMAGE, ZoneBuilder::IsEnabled() ? 14336 : 7168); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, 2700); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FX, 1200); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMAGE, ZoneBuilder::IsEnabled() ? 14336 * 2 : 7168); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, 2700 * 2); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FX, 1200 * 2); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, 14000); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XANIMPARTS, 8192); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL, 5125); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XANIMPARTS, 8192 * 2); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL, 5125 * 2); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PHYSPRESET, 128); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PIXELSHADER, ZoneBuilder::IsEnabled() ? 0x4000 : 10000); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, ZoneBuilder::IsEnabled() ? 0x2000 : 3072); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MATERIAL, 8192); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MATERIAL, 8192 * 2); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXDECL, ZoneBuilder::IsEnabled() ? 0x400 : 196); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_WEAPON, WEAPON_LIMIT); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_STRINGTABLE, 800); @@ -559,10 +763,11 @@ namespace Components if (ZoneBuilder::IsEnabled()) { Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MAP_ENTS, 10); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, 8192); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, 8192 * 2); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, 0x2000); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FONT, 32); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_RAWFILE, 2048); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LEADERBOARD, 500); AssetHandler::RegisterInterface(new Assets::IFont_s()); AssetHandler::RegisterInterface(new Assets::IWeapon()); diff --git a/src/Components/Modules/AssetHandler.hpp b/src/Components/Modules/AssetHandler.hpp index 38a253fe..5207a0f7 100644 --- a/src/Components/Modules/AssetHandler.hpp +++ b/src/Components/Modules/AssetHandler.hpp @@ -41,6 +41,8 @@ namespace Components static void ExposeTemporaryAssets(bool expose); + static void OffsetToAlias(Utils::Stream::Offset* offset); + private: static thread_local int BypassState; static bool ShouldSearchTempAssets; @@ -63,8 +65,6 @@ namespace Components static void FindAssetStub(); static void AddAssetStub(); - static void OffsetToAlias(Utils::Stream::Offset* offset); - static void StoreEmptyAsset(Game::XAssetType type, const char* name); static void StoreEmptyAssetStub(); diff --git a/src/Components/Modules/FastFiles.cpp b/src/Components/Modules/FastFiles.cpp index 67187114..fb7c8939 100644 --- a/src/Components/Modules/FastFiles.cpp +++ b/src/Components/Modules/FastFiles.cpp @@ -320,7 +320,7 @@ namespace Components Zones::SetVersion(*version); // Allow loading of codo versions - if (*version >= VERSION_ALPHA2 && *version <= 360) + if ((*version >= VERSION_ALPHA2 && *version <= 360) || (*version >= 423 && *version <= 461)) { *version = XFILE_VERSION; } @@ -369,7 +369,7 @@ namespace Components unsigned long outLen = sizeof(FastFiles::CurrentKey); rsa_import(FastFiles::ZoneKey, sizeof(FastFiles::ZoneKey), &key); - rsa_decrypt_key_ex(encKey, 256, FastFiles::CurrentKey.data, &outLen, nullptr, NULL, hash, (Zones::Version() >= 359 ? 1 : 2), &stat, &key); + rsa_decrypt_key_ex(encKey, 256, FastFiles::CurrentKey.data, &outLen, nullptr, NULL, hash, Zones::Version() >= 359 ? 1 : 2, &stat, &key); rsa_free(&key); ctr_start(aes, FastFiles::CurrentKey.iv, FastFiles::CurrentKey.key, sizeof(FastFiles::CurrentKey.key), 0, 0, &FastFiles::CurrentCTR); @@ -484,7 +484,7 @@ namespace Components } #endif - void FastFiles::Load_XSurfaceArray(int atStreamStart, int /*count*/) + void FastFiles::Load_XSurfaceArray(int atStreamStart, [[maybe_unused]] int count) { // read the actual count from the varXModelSurfs ptr auto surface = *reinterpret_cast(0x0112A95C); diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index 64bc3d40..0b1aa2e5 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -128,7 +128,7 @@ namespace Components Maps::SPMap = false; Maps::CurrentMainZone = zoneInfo->name; - + Maps::CurrentDependencies.clear(); for (auto i = Maps::DependencyList.begin(); i != Maps::DependencyList.end(); ++i) { @@ -149,7 +149,7 @@ namespace Components std::vector data; Utils::Merge(&data, zoneInfo, zoneCount); - + Game::XZoneInfo team; team.allocFlags = zoneInfo->allocFlags; team.freeFlags = zoneInfo->freeFlags; @@ -252,12 +252,8 @@ namespace Components asset.mapEnts->entityString = const_cast(mapEntities.data()); asset.mapEnts->numEntityChars = mapEntities.size() + 1; } - - // Apply new mapEnts - // This doesn't work, entities are spawned before the patch file is loaded - //Maps::OverrideMapEnts(asset.mapEnts); } - + // This is broken if ((type == Game::XAssetType::ASSET_TYPE_MENU || type == Game::XAssetType::ASSET_TYPE_MENULIST) && Zones::Version() >= 359) { @@ -271,14 +267,13 @@ namespace Components Logger::Print("Waiting for database...\n"); while (!Game::Sys_IsDatabaseReady()) std::this_thread::sleep_for(10ms); - if (!Utils::String::StartsWith(Maps::CurrentMainZone, "mp_") || Maps::SPMap) + if (!Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp || !Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp->name || + !Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp->g_glassData || Maps::SPMap) { - return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP].gameWorldSp[0].g_glassData; - } - else - { - return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp[0].g_glassData; + return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP].gameWorldSp->g_glassData; } + + return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp->g_glassData; } __declspec(naked) void Maps::GetWorldDataStub() @@ -864,6 +859,17 @@ namespace Components } } + void Maps::G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2) + { + if (Maps::CurrentMainZone == "mp_backlot_sh"s || Maps::CurrentMainZone == "mp_con_spring"s || + Maps::CurrentMainZone == "mp_mogadishu_sh"s || Maps::CurrentMainZone == "mp_nightshift_sh"s) + { + return; + } + + Utils::Hook::Call(0x408910)(ent, unk, unk2); + } + Maps::Maps() { Dvar::OnInit([]() @@ -874,8 +880,8 @@ namespace Components Maps::AddDlc({ 1, "Stimulus Pack", {"mp_complex", "mp_compact", "mp_storm", "mp_overgrown", "mp_crash"} }); Maps::AddDlc({ 2, "Resurgence Pack", {"mp_abandon", "mp_vacant", "mp_trailerpark", "mp_strike", "mp_fuel2"} }); Maps::AddDlc({ 3, "Nuketown", {"mp_nuked"} }); - Maps::AddDlc({ 4, "Classics Pack", {"mp_cross_fire", "mp_cargoship", "mp_bloc"} }); - Maps::AddDlc({ 5, "Classics Pack", {"mp_killhouse", "mp_bog_sh"} }); + Maps::AddDlc({ 4, "Classics Pack #1", {"mp_cross_fire", "mp_cargoship", "mp_bloc"} }); + Maps::AddDlc({ 5, "Classics Pack #2", {"mp_killhouse", "mp_bog_sh"} }); Maps::AddDlc({ 6, "Freighter", {"mp_cargoship_sh"} }); Maps::AddDlc({ 7, "Resurrection Pack", {"mp_shipment_long", "mp_rust_long", "mp_firingrange"} }); Maps::AddDlc({ 8, "Recycled Pack", {"mp_bloc_sh", "mp_crash_tropical", "mp_estate_tropical", "mp_fav_tropical", "mp_storm_spring"} }); @@ -900,6 +906,10 @@ namespace Components }); }); + // disable turrets on CoD:OL 448+ maps for now + Utils::Hook(0x5EE577, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick(); + Utils::Hook(0x44A4D5, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick(); + //#define SORT_SMODELS #if !defined(DEBUG) || !defined(SORT_SMODELS) // Don't sort static models diff --git a/src/Components/Modules/Maps.hpp b/src/Components/Modules/Maps.hpp index 205255cd..c1cabedd 100644 --- a/src/Components/Modules/Maps.hpp +++ b/src/Components/Modules/Maps.hpp @@ -123,5 +123,6 @@ namespace Components static Game::dvar_t* GetSpecularDvar(); static void SetSpecularStub1(); static void SetSpecularStub2(); + static void G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2); }; } diff --git a/src/Components/Modules/Zones.cpp b/src/Components/Modules/Zones.cpp index 9e989d01..d5188da4 100644 --- a/src/Components/Modules/Zones.cpp +++ b/src/Components/Modules/Zones.cpp @@ -1,5 +1,6 @@ #include "STDInclude.hpp" +#pragma optimize( "", off ) namespace Components { int Zones::ZoneVersion; @@ -7,6 +8,9 @@ namespace Components int Zones::FxEffectIndex; char* Zones::FxEffectStrings[64]; + Game::XAssetType currentAssetType = Game::XAssetType::ASSET_TYPE_INVALID; + Game::XAssetType previousAssetType = Game::XAssetType::ASSET_TYPE_INVALID; + bool Zones::LoadFxEffectDef(bool atStreamStart, char* buffer, int size) { int count = 0; @@ -83,7 +87,7 @@ namespace Components } bool result = Game::Load_Stream(atStreamStart, xmodel, size); - + if (Zones::Version() >= VERSION_ALPHA2) { Game::XModel model[2]; // Allocate 2 models, as we exceed the buffer @@ -234,6 +238,11 @@ namespace Components if (Zones::ZoneVersion >= 365) { size = 3124; + + if (Zones::ZoneVersion >= 460) + { + size = 4120; + } } } } @@ -261,7 +270,8 @@ namespace Components if (Zones::ZoneVersion >= 359) { - for (int offset = 20; offset <= 56; offset += 4) + auto count = (Zones::Version() == 460) ? 52 : 56; + for (int offset = 20; offset <= count; offset += 4) { *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + offset); Game::Load_XModelPtr(false); @@ -289,17 +299,20 @@ namespace Components if (Zones::ZoneVersion >= 359) { + auto stringCount = (Zones::Version() == 460) ? 62 : 52; + auto arraySize = stringCount * 4; + // 236 *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 124); - Game::Load_XStringArray(false, 52); + Game::Load_XStringArray(false, stringCount); // 428 - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 332); - Game::Load_XStringArray(false, 52); + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 124 + arraySize); + Game::Load_XStringArray(false, stringCount); // 620 - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 540); - Game::Load_XStringArray(false, 52); + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 124 + (arraySize * 2)); + Game::Load_XStringArray(false, stringCount); } else { @@ -319,7 +332,39 @@ namespace Components // 812 // 16 * 4 scriptstrings - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + for (int i = 0; i < 16; i++) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 1028 + (i * 4)); + Game::Load_FxEffectDefHandle(false); + } + + for (int i = 0; i < 16; i++) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 1124 + (i * 4)); + Game::Load_FxEffectDefHandle(false); + } + + for (int i = 0; i < 16; i++) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 1188 + (i * 4)); + Game::Load_FxEffectDefHandle(false); + } + + for (int i = 0; i < 16; i++) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 1316 + (i * 4)); + Game::Load_FxEffectDefHandle(false); + } + + for (int i = 0; i < 5; i++) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 1444 + (i * 4)); + Game::Load_FxEffectDefHandle(false); + } + } + else if (Zones::ZoneVersion >= 359) { // 972 *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 908); @@ -338,11 +383,23 @@ namespace Components Game::Load_FxEffectDefHandle(false); } + if (Zones::Version() >= 460) + { + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1464); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1472); + Game::Load_XString(false); + } + // 980 if (Zones::ZoneVersion >= 359) { + auto offset = (Zones::Version() == 460) ? 1476 : 916; + auto count = (Zones::Version() == 460) ? 57 : 52; + // 53 soundalias name references; up to and including 1124 - for (int i = 0, offset = 916; i < 52; ++i, offset += 4) + for (int i = 0; i < count; ++i, offset += 4) { *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); @@ -369,7 +426,39 @@ namespace Components } } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + if (*reinterpret_cast(varWeaponCompleteDef + 1708)) + { + if (*reinterpret_cast(varWeaponCompleteDef + 1708) == -1) + { + *reinterpret_cast(varWeaponCompleteDef + 1708) = Game::DB_AllocStreamPos(3); + *Game::varsnd_alias_list_name = *reinterpret_cast(varWeaponCompleteDef + 1708); + + Game::Load_snd_alias_list_nameArray(true, 31); + } + else + { + // full usability requires ConvertOffsetToPointer here + } + } + + if (*reinterpret_cast(varWeaponCompleteDef + 1712)) + { + if (*reinterpret_cast(varWeaponCompleteDef + 1712) == -1) + { + *reinterpret_cast(varWeaponCompleteDef + 1712) = Game::DB_AllocStreamPos(3); + *Game::varsnd_alias_list_name = *reinterpret_cast(varWeaponCompleteDef + 1712); + + Game::Load_snd_alias_list_nameArray(true, 31); + } + else + { + // full usability requires ConvertOffsetToPointer here + } + } + } + else if (Zones::ZoneVersion >= 359) { if (*reinterpret_cast(varWeaponCompleteDef + 1128)) { @@ -434,7 +523,16 @@ namespace Components } } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + // 1192 + for (int offset = 1716; offset <= 1728; offset += 4) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_FxEffectDefHandle(false); + } + } + else if (Zones::ZoneVersion >= 359) { // 1192 for (int offset = 1136; offset <= 1148; offset += 4) @@ -453,7 +551,17 @@ namespace Components } } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + // 1208 + static int matOffsets1[] = { 1732, 1736, 1952, 1956, 1960, 1964, 1968, 1972, 1980, 1988, 2000, 2004, 2008, 2012 }; + for (int i = 0; i < ARRAYSIZE(matOffsets1); ++i) + { + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + matOffsets1[i]); + Game::Load_MaterialHandle(false); + } + } + else if (Zones::ZoneVersion >= 359) { // 1208 static int matOffsets1[] = { 1152, 1156, 1372,1376,1380, 1384, 1388, 1392, 1400, 1408 }; @@ -473,7 +581,18 @@ namespace Components } } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2024); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2032); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2048); + Game::Load_XString(false); + } + else if (Zones::ZoneVersion >= 359) { *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1428); Game::Load_XString(false); @@ -496,7 +615,15 @@ namespace Components Game::Load_XString(false); } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + for (int offset = 2332; offset <= 2344; offset += 4) + { + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_MaterialHandle(false); + } + } + else if (Zones::ZoneVersion >= 359) { for (int offset = 1716; offset <= 1728; offset += 4) { @@ -513,7 +640,15 @@ namespace Components } } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + *Game::varPhysCollmapPtr = reinterpret_cast(varWeaponCompleteDef + 2544); + Game::Load_PhysCollmapPtr(false); + + *Game::varPhysPresetPtr = reinterpret_cast(varWeaponCompleteDef + 2548); + Game::Load_PhysPresetPtr(false); + } + else if (Zones::ZoneVersion >= 359) { *Game::varPhysCollmapPtr = reinterpret_cast(varWeaponCompleteDef + 1928); Game::Load_PhysCollmapPtr(false); @@ -527,7 +662,12 @@ namespace Components Game::Load_PhysCollmapPtr(false); } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + 2656); + Game::Load_XModelPtr(false); + } + else if (Zones::ZoneVersion >= 359) { *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + 2020); Game::Load_XModelPtr(false); @@ -538,7 +678,15 @@ namespace Components Game::Load_XModelPtr(false); } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2664); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2668); + Game::Load_FxEffectDefHandle(false); + } + else if (Zones::ZoneVersion >= 359) { *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2028); Game::Load_FxEffectDefHandle(false); @@ -555,7 +703,15 @@ namespace Components Game::Load_FxEffectDefHandle(false); } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2672); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2676); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + else if (Zones::ZoneVersion >= 359) { *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2036); Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); @@ -572,7 +728,18 @@ namespace Components Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2952); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2956); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2984); + Game::Load_FxEffectDefHandle(false); + } + else if (Zones::ZoneVersion >= 359) { *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2304); Game::Load_FxEffectDefHandle(false); @@ -595,7 +762,30 @@ namespace Components Game::Load_FxEffectDefHandle(false); } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2988); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2992); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2996); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 3000); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 3004); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 3008); + Game::Load_FxEffectDefHandle(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3196); + Game::Load_XString(false); + } + else if (Zones::ZoneVersion >= 359) { *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2340); Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); @@ -612,7 +802,28 @@ namespace Components Game::Load_XString(false); } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + if (*reinterpret_cast(varWeaponCompleteDef + 3204) == -1) + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 3204) = vec2; + + Game::Load_Stream(true, vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3776)); + } + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3200); + Game::Load_XString(false); + + if (*reinterpret_cast(varWeaponCompleteDef + 3208) == -1) + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 3208) = vec2; + + Game::Load_Stream(true, vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3778)); + } + } + else if (Zones::ZoneVersion >= 359) { if (*reinterpret_cast(varWeaponCompleteDef + 2524) == -1) { @@ -655,7 +866,27 @@ namespace Components } } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3288); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3292); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3324); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3328); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3484); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3488); + Game::Load_XString(false); + } + else if (Zones::ZoneVersion >= 359) { *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2608); Game::Load_XString(false); @@ -696,7 +927,42 @@ namespace Components Game::Load_XString(false); } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + *Game::varTracerDefPtr = reinterpret_cast(varWeaponCompleteDef + 3492); + Game::Load_TracerDefPtr(false); + + *Game::varTracerDefPtr = reinterpret_cast(varWeaponCompleteDef + 3496); + Game::Load_TracerDefPtr(false); + + *Game::varTracerDefPtr = reinterpret_cast(varWeaponCompleteDef + 3500); + Game::Load_TracerDefPtr(false); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 3528); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); // 2848 + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 3532); + Game::Load_FxEffectDefHandle(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3536); + Game::Load_XString(false); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 3552); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 3556); + Game::Load_snd_alias_list_nameArray(false, 4); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 3572); + Game::Load_snd_alias_list_nameArray(false, 4); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 3588); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 3592); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + else if (Zones::ZoneVersion >= 359) { *Game::varTracerDefPtr = reinterpret_cast(varWeaponCompleteDef + 2780); Game::Load_TracerDefPtr(false); @@ -755,7 +1021,15 @@ namespace Components Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + for (int i = 0, offset = 3660; i < 6; ++i, offset += 4) + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + } + else if (Zones::ZoneVersion >= 359) { for (int i = 0, offset = 2940; i < 6; ++i, offset += 4) { @@ -782,7 +1056,30 @@ namespace Components } } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3712); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3728); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3732); + Game::Load_XString(false); + + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3740); + Game::Load_MaterialHandle(false); + + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3744); + Game::Load_MaterialHandle(false); + + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3748); + Game::Load_MaterialHandle(false); + + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3752); + Game::Load_MaterialHandle(false); + } + else if (Zones::ZoneVersion >= 359) { *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2988); Game::Load_XString(false); @@ -828,7 +1125,40 @@ namespace Components Game::Load_MaterialHandle(false); } - if (Zones::ZoneVersion >= 359) + if (Zones::Version() >= 460) + { + if (*reinterpret_cast(varWeaponCompleteDef + 3780) == -1) + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 3780) = vec2; + + Game::Load_Stream(true, vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3776)); + } + + if (*reinterpret_cast(varWeaponCompleteDef + 3784) == -1) + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 3784) = vec2; + + Game::Load_Stream(true, vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3778)); + } + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3876); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3880); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3884); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3996); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 4012); + Game::Load_XString(false); + } + else if (Zones::ZoneVersion >= 359) { if (*reinterpret_cast(varWeaponCompleteDef + 3048) == -1) { @@ -1051,73 +1381,190 @@ namespace Components char* varWeaponAttach = *reinterpret_cast(0x112ADE0); // varAddonMapEnts // and do the stuff - Game::Load_Stream(true, varWeaponAttach, 12); + if (Zones::Version() >= 448) + { + Game::Load_Stream(true, varWeaponAttach, 20); - Game::DB_PushStreamPos(3); + Game::DB_PushStreamPos(3); - *Game::varXString = reinterpret_cast(varWeaponAttach); - Game::Load_XString(false); + *Game::varXString = reinterpret_cast(varWeaponAttach); + Game::Load_XString(false); - *reinterpret_cast(varWeaponAttach + 8) = Game::DB_AllocStreamPos(3); - Zones::LoadWeaponAttachStuff(*reinterpret_cast(varWeaponAttach + 8), *reinterpret_cast(varWeaponAttach + 4)); + // load string array + if (*reinterpret_cast(varWeaponAttach + 16) != nullptr) + { + if (*reinterpret_cast(varWeaponAttach + 16) == -1) + { + *reinterpret_cast(varWeaponAttach + 16) = Game::DB_AllocStreamPos(3); + *Game::varXString = reinterpret_cast(varWeaponAttach + 16); + Game::Load_XStringArray(true, *reinterpret_cast(varWeaponAttach + 12)); + } + else + { + Utils::Hook::Call(0x4A82B0)(reinterpret_cast(varWeaponAttach + 16)); + } + } - Game::DB_PopStreamPos(); + Game::DB_PopStreamPos(); + } + else + { + Game::Load_Stream(true, varWeaponAttach, 12); + + Game::DB_PushStreamPos(3); + + *Game::varXString = reinterpret_cast(varWeaponAttach); + Game::Load_XString(false); + + *reinterpret_cast(varWeaponAttach + 8) = Game::DB_AllocStreamPos(3); + Zones::LoadWeaponAttachStuff(*reinterpret_cast(varWeaponAttach + 8), *reinterpret_cast(varWeaponAttach + 4)); + + Game::DB_PopStreamPos(); + } + } bool Zones::LoadMaterialShaderArgumentArray(bool atStreamStart, Game::MaterialShaderArgument* argument, int size) { + // if (Zones::ZoneVersion >= 448 && currentAssetType == Game::XAssetType::ASSET_TYPE_FX) __debugbreak(); bool result = Game::Load_Stream(atStreamStart, argument, size); Game::MaterialPass* curPass = *Game::varMaterialPass; int count = curPass->perPrimArgCount + curPass->perObjArgCount + curPass->stableArgCount; - + for (int i = 0; i < count && (Zones::ZoneVersion >= VERSION_ALPHA2); ++i) { Game::MaterialShaderArgument* arg = &argument[i]; - if (arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_TEXTURE && arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_ATTROUT) + if (Zones::Version() < 448) { - continue; - } - - // should be min 68 currently - // >= 58 fixes foliage without bad side effects - // >= 53 still has broken shadow mapping - // >= 23 is still broken somehow - if (arg->u.codeConst.index >= 58 && arg->u.codeConst.index <= 135) // >= 34 would be 31 in iw4 terms - { - arg->u.codeConst.index -= 3; - - if (Zones::Version() >= 359/* && arg->paramID <= 113*/) + if (arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_TEXTURE && arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_ATTROUT) { - arg->u.codeConst.index -= 7; + continue; + } + + // should be min 68 currently + // >= 58 fixes foliage without bad side effects + // >= 53 still has broken shadow mapping + // >= 23 is still broken somehow + if (arg->u.codeConst.index >= 58 && arg->u.codeConst.index <= 135) // >= 34 would be 31 in iw4 terms + { + arg->u.codeConst.index -= 3; - if (arg->u.codeConst.index <= 53) + if (Zones::Version() >= 359/* && arg->paramID <= 113*/) { - arg->u.codeConst.index += 1; + arg->u.codeConst.index -= 7; + + if (arg->u.codeConst.index <= 53) + { + arg->u.codeConst.index += 1; + } + } + } + // >= 21 works fine for specular, but breaks trees + // >= 4 is too low, breaks specular + else if (arg->u.codeConst.index >= 11 && arg->u.codeConst.index < 58) + { + arg->u.codeConst.index -= 2; + + if (Zones::Version() >= 359) + { + if (arg->u.codeConst.index > 15 && arg->u.codeConst.index < 30) + { + arg->u.codeConst.index -= 1; + + if (arg->u.codeConst.index == 19) + { + arg->u.codeConst.index = 21; + } + } + else if (arg->u.codeConst.index >= 50) + { + arg->u.codeConst.index += 6; + } } } } - // >= 21 works fine for specular, but breaks trees - // >= 4 is too low, breaks specular - else if (arg->u.codeConst.index >= 11 && arg->u.codeConst.index < 58) + else { - arg->u.codeConst.index -= 2; - - if (Zones::Version() >= 359) + if (arg->type == 3 || arg->type == 5) { - if (arg->u.codeConst.index > 15 && arg->u.codeConst.index < 30) + if (Zones::Version() == 460 /*|| Zones::Version() == 448*/) // 448 is no longer compatible, needs correct mappings { - arg->u.codeConst.index -= 1; + static std::unordered_map mapped_constants = { + { 22, 21 }, + { 33, 31 }, + { 34, 32 }, + { 36, 34 }, + { 37, 35 }, + { 38, 36 }, + { 39, 37 }, + { 40, 38 }, + { 41, 39 }, + { 42, 40 }, + { 43, 41 }, + { 44, 42 }, + { 45, 43 }, + { 62, 52 }, + { 63, 53 }, - if (arg->u.codeConst.index == 19) + // these might need fixes? + { 197, 58 }, + { 198, 59 }, + { 202, 63 }, + { 203, 64 }, + { 207, 68 }, + { 252, 81 }, + { 253, 82 }, + + // these seem fine + { 261, 90 }, + { 265, 94 }, + { 269, 98 }, + { 272, 101 }, + { 273, 102 }, + { 274, 103 }, + { 277, 106 }, + }; + + const auto itr = mapped_constants.find(arg->u.codeConst.index); + if (itr != mapped_constants.end()) { - arg->u.codeConst.index = 21; + arg->u.codeConst.index = itr->second; + } + else + { + if (arg->u.codeConst.index == 257) + { + if (FastFiles::Current() != "mp_conflict" && FastFiles::Current() != "mp_derail_sh" && FastFiles::Current() != "mp_overwatch_sh" && + FastFiles::Current() != "mp_con_spring" && FastFiles::Current() != "mp_resistance_sh" && FastFiles::Current() != "mp_lookout_sh") + { + const auto varMaterialTechniqueSet = *reinterpret_cast(0x112AE8C); + if (varMaterialTechniqueSet->name && !strncmp(varMaterialTechniqueSet->name, "mc_", 3)) + { + // fixes trees + arg->u.codeConst.index = 86; + } + else + { + // fixes black spots in the maps + arg->u.codeConst.index = 128; + } + } + //else + /*{ + arg->u.codeConst.index = 134; + }*/ + } + else if (arg->u.codeConst.index >= 259) + { + arg->u.codeConst.index -= 171; + } + else if (arg->u.codeConst.index >= 197) + { + arg->u.codeConst.index -= 139; + } } - } - else if (arg->u.codeConst.index >= 50) - { - arg->u.codeConst.index += 6; } } } @@ -1224,7 +1671,7 @@ namespace Components { int count = 0; - if (Zones::ZoneVersion >= 359) + if (Zones::ZoneVersion >= 334) { size /= 8; count = size; @@ -1233,23 +1680,28 @@ namespace Components bool result = Game::Load_Stream(atStreamStart, buffer, size); - if (Zones::ZoneVersion >= 359) + if (Zones::ZoneVersion >= 334) { - Utils::Memory::Allocator allocator; - Game::XAsset* tempAssets = allocator.allocateArray(count); - for (int i = 0; i < count; ++i) { - char* src = &buffer[i * 16]; + std::memmove(buffer + (i * 8), buffer + (i * 16), 4); + std::memmove(buffer + (i * 8) + 4, buffer + (i * 16) + 8, 4); - std::memcpy(&tempAssets[i].type, src + 0, 4); - std::memcpy(&tempAssets[i].header, src + 8, 4); - - AssetHandler::Relocate(src + 0, buffer + (i * 8) + 0, 4); - AssetHandler::Relocate(src + 8, buffer + (i * 8) + 4, 4); + if (Zones::Version() >= 423) + { + // don't read assets that are unused by codol, for some retarded reason their header is written in the FF anyway + if (*reinterpret_cast(buffer + (i * 8)) == Game::XAssetType::ASSET_TYPE_CLIPMAP_SP || + *reinterpret_cast(buffer + (i * 8)) == Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP || + *reinterpret_cast(buffer + (i * 8)) == Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP) + { + *reinterpret_cast(buffer + (i * 8)) = Game::XAssetType::ASSET_TYPE_UI_MAP; + *reinterpret_cast(buffer + (i * 8) + 4) = nullptr; + } + } + + AssetHandler::Relocate(buffer + (i * 16), buffer + (i * 8) + 0, 4); + AssetHandler::Relocate(buffer + (i * 16) + 8, buffer + (i * 8) + 4, 4); } - - std::memcpy(buffer, tempAssets, sizeof(Game::XAsset) * count); } return result; @@ -1257,11 +1709,11 @@ namespace Components bool Zones::LoadMaterialTechnique(bool atStreamStart, char* buffer, int size) { - if (Zones::ZoneVersion >= 359) - { - size += 4; - } - + // 359 and above adds an extra remapped techset ptr + if (Zones::ZoneVersion >= 359) size += 4; + // 448 amd above adds an additional technique + if (Zones::ZoneVersion >= 448) size += 4; + bool result = Game::Load_Stream(atStreamStart, buffer, size); if (Zones::ZoneVersion >= 359) @@ -1272,19 +1724,69 @@ namespace Components // As MW2 flags are only 1 byte large, this won't be possible anyways int shiftTest = 4; - std::memmove(buffer + 8 + shiftTest, buffer + 12 + shiftTest, 196 - shiftTest); - AssetHandler::Relocate(buffer + 12 + shiftTest, buffer + 8 + shiftTest, 196 - shiftTest); + std::memmove(buffer + 8 + shiftTest, buffer + 12 + shiftTest, (Zones::Version() >= 448) ? 200 : 196 - shiftTest); + AssetHandler::Relocate(buffer + 12 + shiftTest, buffer + 8 + shiftTest, (Zones::Version() >= 448) ? 200 : 196 - shiftTest); } return result; } + int Zones::LoadMaterialTechniqueArray(bool atStreamStart, int count) + { + if (Zones::Version() >= 448) + { + count += 1; + } + + auto retval = Utils::Hook::Call(0x497020)(atStreamStart, count); + + if (Zones::Version() >= 448) + { + auto lastTechnique = **reinterpret_cast(0x112AEDC); + auto varMaterialTechniqueSet = **reinterpret_cast(0x112B070); + + // patch last technique to match iw4 + varMaterialTechniqueSet->techniques[47] = lastTechnique; + } + + return retval; + } bool Zones::LoadMaterial(bool atStreamStart, char* buffer, int size) { - bool result = Game::Load_Stream(atStreamStart, buffer, size); + // if (Zones::ZoneVersion >= 448 && currentAssetType == Game::ASSET_TYPE_XMODEL) __debugbreak(); - if (Zones::ZoneVersion >= 359) + bool result = Game::Load_Stream(atStreamStart, buffer, (Zones::Version() >= 448) ? 104 : size); + + if (Zones::Version() >= 448) { + char codol_material[104]; + memcpy(codol_material, buffer, 104); + + // move material data + std::memmove(buffer, codol_material + 0x10, 4); + std::memmove(buffer + 0x11, codol_material + 0x14, 6); + std::memmove(buffer + 4, codol_material + 0x1A, 1); + std::memmove(buffer + 5, codol_material + 0x1C, 3); + std::memmove(buffer + 8, codol_material, 16); + std::memmove(buffer + 0x18, codol_material + 0x20, 0x30); + std::memmove(buffer + 0x48, codol_material + 0x51, 5); + std::memmove(buffer + 0x50, codol_material + 0x58, 0x10); + + std::memset(buffer + 0x48 + 5, 0, 3); + + // relocate pointers + AssetHandler::Relocate(buffer + 10, buffer, 4); + AssetHandler::Relocate(buffer + 0x1B, buffer + 4, 4); + AssetHandler::Relocate(buffer, buffer + 8, 16); + AssetHandler::Relocate(buffer + 0x20, buffer + 0x18, 0x30); + AssetHandler::Relocate(buffer + 0x51, buffer + 0x48, 5); + AssetHandler::Relocate(buffer + 0x58, buffer + 0x50, 0x10); + + // fix statebit + reinterpret_cast(buffer)->stateBitsEntry[47] = codol_material[0x50]; + } + else if (Zones::ZoneVersion >= 359) + { struct material339_s { char drawSurfBegin[8]; // 4 @@ -1340,18 +1842,44 @@ namespace Components return result; } + int gfxLightMapExtraCount = 0; + int* gfxLightMapExtraPtr1 = nullptr; + int* gfxLightMapExtraPtr2 = nullptr; + bool Zones::LoadGfxWorld(bool atStreamStart, char* buffer, int size) { - if (Zones::ZoneVersion >= 359) - { - size += 968; - } + gfxLightMapExtraPtr1 = nullptr; + gfxLightMapExtraPtr2 = nullptr; + if (Zones::Version() >= 460) size += 984; + else if (Zones::Version() >= 423) size += 980; + else if (Zones::Version() >= 359) size += 968; + bool result = Game::Load_Stream(atStreamStart, buffer, size); - if (Zones::ZoneVersion >= 359) + // fix 4 byte difference in FF ver 460+ this turns 460+ into 423 + if (Zones::Version() >= 460) { - int sunDiff = 8; // Stuff that is part of the sunflare we would overwrite + std::memmove(buffer + 0x34, buffer + 0x38, size - 0x38); + AssetHandler::Relocate(buffer + 0x38, buffer + 0x34, size - 0x38); + } + + // fix 12 byte difference in FF ver 423+, this turns 423+ into 359 + if (Zones::Version() >= 423) + { + // store extra pointers we just yeeted out of the structure + gfxLightMapExtraCount = *reinterpret_cast(buffer + 0x50 + 0x10); + gfxLightMapExtraPtr1 = *reinterpret_cast(buffer + 0x50 + 0x14); + gfxLightMapExtraPtr2 = *reinterpret_cast(buffer + 0x50 + 0x18); + + // move gfxworld into 359 format + std::memmove(buffer + 0x50 + 0x10, buffer + 0x50 + 0x1C, size - 0x6C); + AssetHandler::Relocate(buffer + 0x50 + 0x1C, buffer + 0x50 + 0x10, size - 0x6C); + } + + if (Zones::Version() >= 359) + { + int sunDiff = 8; // Stuff that is part of the sunflare we would overwrite std::memmove(buffer + 348 + sunDiff, buffer + 1316 + sunDiff, 280 - sunDiff); AssetHandler::Relocate(buffer + 1316, buffer + 348, 280); } @@ -1431,16 +1959,29 @@ namespace Components void Zones::LoadImpactFx(bool atStreamStart, char* buffer, int size) { - if (Zones::Version() >= VERSION_ALPHA2) - { - size = 0x8C0; - } + if (Zones::Version() >= 460) size = 0xB94; + else if (Zones::Version() >= 448) size = 0xA64; + else if (Zones::Version() >= VERSION_ALPHA2) size = 0x8C0; Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::Version() >= 460) + { + for (auto i = 0; i < Zones::ImpactFxArrayCount(); i++) + { + std::memmove(buffer + (i * 140), buffer + (i * 156), 140); + AssetHandler::Relocate(buffer + (i * 156), buffer + (i * 140), 140); + } + } } int Zones::ImpactFxArrayCount() { + if (Zones::Version() >= 448) + { + return 19; + } + if (Zones::Version() >= VERSION_ALPHA2) { return 16; @@ -1513,7 +2054,7 @@ namespace Components return true; } - if (Zones::Version() >= VERSION_ALPHA2 && type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP) + if (Zones::Version() >= VERSION_ALPHA2 && Zones::Version() < 423 && type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP) { Maps::HandleAsSPMap(); return true; @@ -1574,11 +2115,1300 @@ namespace Components retn } } + + __declspec(naked) void Zones::GetCurrentAssetTypeStub() + { + __asm pushad; + previousAssetType = currentAssetType; + __asm popad; + __asm + { + // get asset type + mov eax, ds:0x112aa54 + mov eax, [eax]; + + // log asset type + // mov previousAssetType, currentAssetType; + mov currentAssetType, eax; + + // go back + push 0x418847; + retn; + } + } + int Zones::LoadRandomFxGarbage(bool atStreamStart, char* buffer, int size) + { + int count = 0; + if (Zones::Version() >= 448) + { + size /= 48; + count = size; + size *= 64; + } + + const auto retval = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::Version() >= 448) + { + for (auto i = 0; i < count; i++) + { + std::memcpy(buffer + (48 * i) + 0, buffer + (64 * i) + 0, 24); + std::memcpy(buffer + (48 * i) + 24, buffer + (64 * i) + 32, 24); + + AssetHandler::Relocate(buffer + (64 * i) + 0, buffer + (48 * i) + 0, 24); + AssetHandler::Relocate(buffer + (64 * i) + 32, buffer + (48 * i) + 24, 24); + } + } + + return retval; + } + + int currentGfxSurfaceIndex = 0; + std::vector> gfxSurfaceExtraData; + + int Zones::LoadGfxXSurfaceArray(bool atStreamStart, char* buffer, int size) + { + currentGfxSurfaceIndex = 0; + gfxSurfaceExtraData.clear(); + + int count = 0; + + if (Zones::Version() >= 423) + { + size /= 40; + count = size; + size *= 48; + } + + const auto retval = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::Version() >= 423) + { + // fix structure + for (auto i = 0; i < count; i++) + { + auto read_count = *reinterpret_cast(buffer + (48 * i) + 40); + auto read_ptr = *reinterpret_cast(buffer + (48 * i) + 44); + + // extra data stuff we need to load + gfxSurfaceExtraData.push_back({ + read_count, + read_ptr + }); + + // fix structure + std::memmove(buffer + (40 * i), buffer + (48 * i), 40); + AssetHandler::Relocate(buffer + (48 * i), buffer + (40 * i), 40); + } + } + + return retval; + } + + int Zones::LoadGfxXSurfaceExtraData(bool atStreamStart) + { + const auto retval = Utils::Hook::Call(0x4516B0)(atStreamStart); + + if (Zones::Version() >= 423) + { + const auto currentData = &gfxSurfaceExtraData[currentGfxSurfaceIndex]; + if (currentData->second) + { + currentData->second = reinterpret_cast(Game::DB_AllocStreamPos(0)); + Game::Load_Stream(true, currentData->second, currentData->first); + } + currentGfxSurfaceIndex++; + } + + return retval; + } + + int Zones::LoadGfxReflectionProbes(bool atStreamStart, char* buffer, int size) + { + int count = 0; + + if (Zones::Version() >= 448) + { + size /= 12; + count = size; + size *= 20; + } + + auto retval = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::Version() >= 448) + { + for (int i = 0; i < count; i++) + { + auto garbage = *reinterpret_cast(buffer + (20 * i) + 12); + auto garbage_count = *reinterpret_cast(buffer + (20 * i) + 16); + + if (garbage != nullptr) + { + garbage = Game::DB_AllocStreamPos(3); + Game::Load_Stream(true, garbage, 8 * garbage_count); + + for (int j = 0; j < garbage_count; j++) + { + auto garbage_2 = *reinterpret_cast(garbage + (8 * j) + 0); + auto garbage_2_count = *reinterpret_cast(garbage + (8 * j) + 4); + + if (garbage_2) + { + garbage_2 = Game::DB_AllocStreamPos(1); + Game::Load_Stream(true, garbage_2, 2 * garbage_2_count); + } + } + } + + std::memmove(buffer + (12 * i), buffer + (20 * i), 12); + AssetHandler::Relocate(buffer + (20 * i), buffer + (12 * i), 12); + } + } + + return retval; + } + + void Zones::LoadGfxLightMapExtraData() + { + Game::DB_PopStreamPos(); + + if (Zones::Version() >= 423) + { + if (gfxLightMapExtraPtr1) + { + gfxLightMapExtraPtr1 = reinterpret_cast(Game::DB_AllocStreamPos(3)); + Game::Load_Stream(true, gfxLightMapExtraPtr1, 12 * gfxLightMapExtraCount); + } + + if (gfxLightMapExtraPtr2) + { + gfxLightMapExtraPtr2 = reinterpret_cast(Game::DB_AllocStreamPos(0)); + Game::Load_Stream(true, gfxLightMapExtraPtr2, gfxLightMapExtraCount); + } + } + } + + __declspec(naked) void Zones::LoadXModelColSurfPtr() + { + static auto DB_ConvertOffsetToPointer_Address = 0x4A82B0; + + __asm + { + cmp dword ptr[eax], 0; + je dontLoadAssetData; + + cmp dword ptr[eax], 0xFFFFFFFF; + je loadAssetData; + + // check if FF is below 448, still load data in that case + cmp Zones::ZoneVersion, 448; + jl loadAssetData; + + // offset to pointer magic + pushad; + push eax; + call DB_ConvertOffsetToPointer_Address; + add esp, 4; + popad; + + dontLoadAssetData: + push 0x4C870E; + retn; + + loadAssetData: + push 0x4C86DD; + retn; + } + } + + struct ClipInfo + { + int numCPlanes; + Game::cplane_s* cPlanes; + int numMaterials; + Game::ClipMaterial* materials; + int numCBrushSides; + Game::cbrushside_t* cBrushSides; + int numCBrushEdges; + char* cBrushEdges; + int numCLeafBrushNodes; + Game::cLeafBrushNode_s* cLeafBrushNodes; // cmodels use this? + int numLeafBrushes; + short* leafBrushes; + unsigned short numBrushes; + Game::cbrush_t* brushes; + Game::Bounds* brushBounds; + int* brushContents; + }; + struct codolCmodel_t + { + Game::Bounds bounds; + float radius; + ClipInfo* infoPtr; + Game::cLeaf_t leaf; + }; + struct codolClipMap_t + { + char* name; + bool isInUse; + char pad1[3]; + ClipInfo info; + ClipInfo* pInfo; + int numStaticModels; + Game::cStaticModel_s* staticModelList; + int numCNodes; + Game::cNode_t* cNodes; + int numCLeaf; + Game::cLeaf_t* cLeaf; + int numVerts; + float(*verts)[3]; + int numTriIndices; + short* triIndices; + char* triEdgeIsWalkable; //Size = ((triCount << 1) + triCount + 0x1F) >> 3 << 2 + int numCollisionBorders; + Game::CollisionBorder* collisionBorders; + int numCollisionPartitions; + Game::CollisionPartition* collisionPartitions; + int numCollisionAABBTrees; + Game::CollisionAabbTree* collisionAABBTrees; + int numCModels; + codolCmodel_t* cModels; + Game::MapEnts* mapEnts; + Game::Stage* stages; + unsigned char stageCount; + char pad2[3]; + Game::MapTriggers trigger; + short smodelNodeCount; + Game::SModelAabbNode* smodelNodes; + unsigned short dynEntCount[2]; + Game::DynEntityDef* dynEntDefList[2]; + Game::DynEntityPose* dynEntPoseList[2]; + Game::DynEntityClient* dynEntClientList[2]; + Game::DynEntityColl* dynEntCollList[2]; + char pad3[20]; + std::uint32_t isPlutoniumMap; + }; + + static Game::MapEnts codolMapEnts; + static Game::MapEnts* codolMapEntsPtr; + + int Zones::LoadMapEnts(bool atStreamStart, Game::MapEnts* buffer, int size) + { + if (Zones::Version() >= 448) + { + size /= 44; + size *= 36; + + memset(&codolMapEnts, 0, sizeof Game::MapEnts); + + buffer = &codolMapEnts; + codolMapEntsPtr = &codolMapEnts; + + *reinterpret_cast(0x112B3E8) = &codolMapEnts; // varMapEnts + *reinterpret_cast(0x112B388) = &codolMapEntsPtr; // varMapEntsPtr + + AssetHandler::Relocate(&codolMapEnts, buffer, size); + } + + return Game::Load_Stream(atStreamStart, buffer, size); + } + + ClipInfo* varClipInfoPtr; + void Zones::Load_ClipInfo(bool atStreamStart) + { + AssertSize(ClipInfo, 64); + AssertSize(Game::cplane_s, 20); + AssertSize(Game::Bounds, 24); + + Game::Load_Stream(atStreamStart, varClipInfoPtr, sizeof ClipInfo); + + if (varClipInfoPtr->cPlanes) + { + if (varClipInfoPtr->cPlanes == reinterpret_cast(0xFFFFFFFF)) + { + varClipInfoPtr->cPlanes = reinterpret_cast(Game::DB_AllocStreamPos(3)); + Game::Load_Stream(true, varClipInfoPtr->cPlanes, varClipInfoPtr->numCPlanes * sizeof(Game::cplane_s)); + } + else + { + Game::DB_ConvertOffsetToPointer(&varClipInfoPtr->cPlanes); + } + } + + if (varClipInfoPtr->materials) + { + if (varClipInfoPtr->materials == reinterpret_cast(0xFFFFFFFF)) + { + varClipInfoPtr->materials = reinterpret_cast(Game::DB_AllocStreamPos(3)); + *reinterpret_cast(0x112A958) = varClipInfoPtr->materials; + Utils::Hook::Call(0x4895F0)(true, varClipInfoPtr->numMaterials); + } + else + { + Game::DB_ConvertOffsetToPointer(&varClipInfoPtr->materials); + } + } + + if (varClipInfoPtr->cBrushSides) + { + if (varClipInfoPtr->cBrushSides == reinterpret_cast(0xFFFFFFFF)) + { + varClipInfoPtr->cBrushSides = reinterpret_cast(Game::DB_AllocStreamPos(3)); + *reinterpret_cast(0x112B33C) = varClipInfoPtr->cBrushSides; + Utils::Hook::Call(0x420790)(true, varClipInfoPtr->numCBrushSides); + } + else + { + Game::DB_ConvertOffsetToPointer(&varClipInfoPtr->cBrushSides); + } + } + + if (varClipInfoPtr->cBrushEdges) + { + if (varClipInfoPtr->cBrushEdges == reinterpret_cast(0xFFFFFFFF)) + { + varClipInfoPtr->cBrushEdges = reinterpret_cast(Game::DB_AllocStreamPos(0)); + Game::Load_Stream(true, varClipInfoPtr->cBrushEdges, varClipInfoPtr->numCBrushEdges); + } + else + { + Game::DB_ConvertOffsetToPointer(&varClipInfoPtr->cBrushEdges); + } + } + + if (varClipInfoPtr->cLeafBrushNodes) + { + if (varClipInfoPtr->cLeafBrushNodes == reinterpret_cast(0xFFFFFFFF)) + { + varClipInfoPtr->cLeafBrushNodes = reinterpret_cast(Game::DB_AllocStreamPos(3)); + *reinterpret_cast(0x112B130) = varClipInfoPtr->cLeafBrushNodes; + Utils::Hook::Call(0x4C29D0)(true, varClipInfoPtr->numCLeafBrushNodes); + } + else + { + Game::DB_ConvertOffsetToPointer(&varClipInfoPtr->cLeafBrushNodes); + } + } + + if (varClipInfoPtr->leafBrushes) + { + if (varClipInfoPtr->leafBrushes == reinterpret_cast(0xFFFFFFFF)) + { + varClipInfoPtr->leafBrushes = reinterpret_cast(Game::DB_AllocStreamPos(1)); + Game::Load_Stream(true, varClipInfoPtr->leafBrushes, varClipInfoPtr->numLeafBrushes * sizeof(short)); + } + else + { + Game::DB_ConvertOffsetToPointer(&varClipInfoPtr->leafBrushes); + } + } + + AssertOffset(ClipInfo, numBrushes, 48); + AssertOffset(ClipInfo, brushes, 52); + AssertSize(Game::cbrush_t, 36); + if (varClipInfoPtr->brushes) + { + if (varClipInfoPtr->brushes == reinterpret_cast(0xFFFFFFFF)) + { + varClipInfoPtr->brushes = reinterpret_cast(Game::DB_AllocStreamPos(127)); + *reinterpret_cast(0x112B2B8) = varClipInfoPtr->brushes; + Utils::Hook::Call(0x4B4160)(true, varClipInfoPtr->numBrushes); + } + else + { + Game::DB_ConvertOffsetToPointer(&varClipInfoPtr->brushes); + } + } + + AssertOffset(ClipInfo, brushBounds, 56); + if (varClipInfoPtr->brushBounds) + { + if (varClipInfoPtr->brushBounds == reinterpret_cast(0xFFFFFFFF)) + { + varClipInfoPtr->brushBounds = reinterpret_cast(Game::DB_AllocStreamPos(127)); + Game::Load_Stream(true, varClipInfoPtr->brushBounds, varClipInfoPtr->numBrushes * sizeof(Game::Bounds)); + } + else + { + Game::DB_ConvertOffsetToPointer(&varClipInfoPtr->brushBounds); + } + } + + AssertOffset(ClipInfo, brushContents, 60); + if (varClipInfoPtr->brushContents) + { + if (varClipInfoPtr->brushContents == reinterpret_cast(0xFFFFFFFF)) + { + varClipInfoPtr->brushContents = reinterpret_cast(Game::DB_AllocStreamPos(3)); + Game::Load_Stream(true, varClipInfoPtr->brushContents, 4 * varClipInfoPtr->numBrushes); + } + else + { + Game::DB_ConvertOffsetToPointer(&varClipInfoPtr->brushContents); + } + } + } + + int Zones::LoadClipMap(bool atStreamStart) + { + if (Zones::Version() >= 448) + { + AssertOffset(codolClipMap_t, pInfo, 72); + + AssertSize(Game::cStaticModel_s, 76); + AssertOffset(codolClipMap_t, numStaticModels, 76); + AssertOffset(codolClipMap_t, staticModelList, 80); + + auto varClipMap = *reinterpret_cast(0x112A758); + Game::Load_Stream(atStreamStart, varClipMap, 256); + + Game::DB_PushStreamPos(3); + + *Game::varXString = &varClipMap->name; + Game::Load_XString(false); + + varClipInfoPtr = &varClipMap->info; + Load_ClipInfo(false); + + Game::DB_PushStreamPos(0); + + varClipInfoPtr = varClipMap->pInfo; + ClipInfo** assetPointer = nullptr; + if (varClipMap->pInfo) + { + if (varClipMap->pInfo == reinterpret_cast(0xFFFFFFFF) || + varClipMap->pInfo == reinterpret_cast(0xFFFFFFFE)) + { + const auto needsToAllocPointer = varClipMap->pInfo == reinterpret_cast(0xFFFFFFFE); + + varClipMap->pInfo = reinterpret_cast(Game::DB_AllocStreamPos(3)); + + if (needsToAllocPointer) // 0xFFFFFFFE + { + assetPointer = Game::DB_InsertPointer(); + } + + varClipInfoPtr = varClipMap->pInfo; + Load_ClipInfo(true); + + // varClipMap->pInfo = &varClipMap->info; + if (assetPointer) + { + *assetPointer = varClipMap->pInfo; + } + } + } + Game::DB_PopStreamPos(); + + if (varClipMap->staticModelList) + { + varClipMap->staticModelList = reinterpret_cast(Game::DB_AllocStreamPos(3)); + *reinterpret_cast(0x112B0E4) = varClipMap->staticModelList; + Utils::Hook::Call(0x4B7440)(true, varClipMap->numStaticModels); + } + + if (varClipMap->cNodes) + { + varClipMap->cNodes = reinterpret_cast(Game::DB_AllocStreamPos(3)); + *reinterpret_cast(0x112B1D0) = varClipMap->cNodes; + Utils::Hook::Call(0x4E65A0)(true, varClipMap->numCNodes); + } + + if (varClipMap->cLeaf) + { + varClipMap->cLeaf = reinterpret_cast(Game::DB_AllocStreamPos(3)); + Game::Load_Stream(true, varClipMap->cLeaf, sizeof(Game::cLeaf_t) * varClipMap->numCLeaf); + } + + if (varClipMap->verts) + { + varClipMap->verts = reinterpret_cast(Game::DB_AllocStreamPos(3)); + Game::Load_Stream(true, varClipMap->verts, 12 * varClipMap->numVerts); + } + + if (varClipMap->triIndices) + { + varClipMap->triIndices = reinterpret_cast(Game::DB_AllocStreamPos(1)); + Game::Load_Stream(true, varClipMap->triIndices, sizeof(short) * varClipMap->numTriIndices * 3); + } + + if (varClipMap->triEdgeIsWalkable) + { + varClipMap->triEdgeIsWalkable = static_cast(Game::DB_AllocStreamPos(0)); +#pragma warning(push) +#pragma warning(disable: 4554) + Game::Load_Stream(true, varClipMap->triEdgeIsWalkable, 0x1F + varClipMap->numTriIndices * 3 >> 3 & 0xFFFFFFFC); +#pragma warning(pop) + } + + if (varClipMap->collisionBorders) + { + varClipMap->collisionBorders = reinterpret_cast(Game::DB_AllocStreamPos(3)); + Game::Load_Stream(true, varClipMap->collisionBorders, varClipMap->numCollisionBorders * sizeof(Game::CollisionBorder)); + } + + if (varClipMap->collisionPartitions) + { + varClipMap->collisionPartitions = reinterpret_cast(Game::DB_AllocStreamPos(3)); + *reinterpret_cast(0x112AA38) = varClipMap->collisionPartitions; + Utils::Hook::Call(0x444AF0)(true, varClipMap->numCollisionPartitions); + } + + AssertSize(Game::CollisionAabbTree, 32); + if (varClipMap->collisionAABBTrees) + { + varClipMap->collisionAABBTrees = reinterpret_cast(Game::DB_AllocStreamPos(15)); + Game::Load_Stream(true, varClipMap->collisionAABBTrees, varClipMap->numCollisionAABBTrees * sizeof(Game::CollisionAabbTree)); + } + + AssertSize(Game::cmodel_t, 68); + AssertSize(codolCmodel_t, 72); + AssertOffset(codolCmodel_t, infoPtr, 28); + if (varClipMap->cModels) + { + varClipMap->cModels = reinterpret_cast(Game::DB_AllocStreamPos(3)); + Game::Load_Stream(true, varClipMap->cModels, sizeof(codolCmodel_t) * varClipMap->numCModels); + + // read garbage iw5 data we don't need in iw4 + for (auto i = 0; i < varClipMap->numCModels; i++) + { + Game::DB_PushStreamPos(0); + + varClipInfoPtr = varClipMap->cModels[i].infoPtr; + if (varClipInfoPtr) + { + if (varClipInfoPtr == reinterpret_cast(0xFFFFFFFF) || + varClipInfoPtr == reinterpret_cast(0xFFFFFFFE)) + { + const auto needsToAllocPointer = varClipMap->pInfo == reinterpret_cast(0xFFFFFFFE); + ClipInfo** info = nullptr; + + varClipInfoPtr = reinterpret_cast(Game::DB_AllocStreamPos(3)); + + if (needsToAllocPointer) // 0xFFFFFFFE + { + info = Game::DB_InsertPointer(); + } + + Load_ClipInfo(true); + + if (info) + { + *info = varClipInfoPtr; + } + } + } + + Game::DB_PopStreamPos(); + } + + // fix cmodels + std::vector iw4Models; + iw4Models.resize(varClipMap->numCModels); + + for (int i = 0; i < varClipMap->numCModels; i++) + { + std::memcpy(&iw4Models[i], &varClipMap->cModels[i], 28); + std::memcpy(&iw4Models[i].leaf, &varClipMap->cModels[i].leaf, 40); + } + + std::memcpy(varClipMap->cModels, iw4Models.data(), iw4Models.size() * sizeof Game::cmodel_t); + } + + if (varClipMap->smodelNodes) + { + varClipMap->smodelNodes = reinterpret_cast(Game::DB_AllocStreamPos(3)); + Game::Load_Stream(true, varClipMap->smodelNodes, varClipMap->smodelNodeCount * sizeof(Game::SModelAabbNode)); + } + + // load mapents + *reinterpret_cast(0x112B388) = &varClipMap->mapEnts; + Utils::Hook::Call(0x5B9E10)(false); + + // load stages + if (varClipMap->stages) + { + varClipMap->stages = (Game::Stage*)Game::DB_AllocStreamPos(3); + *reinterpret_cast(0x112A818) = varClipMap->stages; + Utils::Hook::Call(0x4AC760)(true, varClipMap->stageCount); + + // IW4 expects stages in mapents instaed of clipmap + codolMapEnts.stageCount = varClipMap->stageCount; + codolMapEnts.stages = varClipMap->stages; + } + + // load map triggers + *reinterpret_cast(0x112AB3C) = &varClipMap->trigger; + Utils::Hook::Call(0x43CBA0)(false); + + AssertOffset(codolClipMap_t, dynEntCount[0], 196); + AssertOffset(codolClipMap_t, dynEntCount[1], 198); + + // dynamic entity shit + for (int i = 0; i < 2; i++) + { + if (varClipMap->dynEntDefList[i]) + { + varClipMap->dynEntDefList[i] = reinterpret_cast(Game::DB_AllocStreamPos(3)); + *reinterpret_cast(0x112AF3C) = varClipMap->dynEntDefList[i]; + Utils::Hook::Call(0x47CE10)(true, varClipMap->dynEntCount[i]); + } + } + + Game::DB_PushStreamPos(2); + + for (int i = 0; i < 2; i++) + { + if (varClipMap->dynEntPoseList[i]) + { + varClipMap->dynEntPoseList[i] = reinterpret_cast(Game::DB_AllocStreamPos(3)); + Game::Load_Stream(true, varClipMap->dynEntPoseList[i], varClipMap->dynEntCount[i] * sizeof(Game::DynEntityPose)); + } + } + for (int i = 0; i < 2; i++) + { + if (varClipMap->dynEntClientList[i]) + { + varClipMap->dynEntClientList[i] = reinterpret_cast(Game::DB_AllocStreamPos(3)); + Game::Load_Stream(true, varClipMap->dynEntClientList[i], varClipMap->dynEntCount[i] * sizeof(Game::DynEntityClient)); + } + } + for (int i = 0; i < 2; i++) + { + if (varClipMap->dynEntCollList[i]) + { + varClipMap->dynEntCollList[i] = reinterpret_cast(Game::DB_AllocStreamPos(3)); + Game::Load_Stream(true, varClipMap->dynEntCollList[i], varClipMap->dynEntCount[i] * sizeof(Game::DynEntityColl)); + } + } + + Game::DB_PopStreamPos(); + + Game::DB_PopStreamPos(); + + auto codolMap = new codolClipMap_t; + memcpy(codolMap, varClipMap, sizeof codolClipMap_t); + + auto cancerMap = reinterpret_cast(varClipMap); + auto iw4Map = reinterpret_cast(varClipMap); + + memcpy(&iw4Map->planeCount, &codolMap->info.numCPlanes, 8); + memcpy(&iw4Map->numStaticModels, &codolMap->numStaticModels, 8); + memcpy(&iw4Map->numMaterials, &codolMap->info.numMaterials, 24); + memcpy(&iw4Map->numNodes, &codolMap->numCNodes, 16); + memcpy(&iw4Map->leafbrushNodesCount, &codolMap->info.numCLeafBrushNodes, 16); + memcpy(&iw4Map->vertCount, &codolMap->numVerts, 52); + memcpy(&iw4Map->numBrushes, &codolMap->info.numBrushes, 16); + iw4Map->mapEnts = &codolMapEnts; + memcpy(&iw4Map->smodelNodeCount, &codolMap->smodelNodeCount, 48); + + AssetHandler::Relocate(&cancerMap->info.numCPlanes, &iw4Map->planeCount, 8); + AssetHandler::Relocate(&cancerMap->numStaticModels, &iw4Map->numStaticModels, 8); + AssetHandler::Relocate(&cancerMap->info.numMaterials, &iw4Map->numMaterials, 24); + AssetHandler::Relocate(&cancerMap->numCNodes, &iw4Map->numNodes, 16); + AssetHandler::Relocate(&cancerMap->info.numCLeafBrushNodes, &iw4Map->leafbrushNodesCount, 16); + AssetHandler::Relocate(&cancerMap->numVerts, &iw4Map->vertCount, 52); + AssetHandler::Relocate(&cancerMap->info.numBrushes, &iw4Map->numBrushes, 16); + AssetHandler::Relocate(&cancerMap->smodelNodeCount, &iw4Map->smodelNodeCount, 48); + + delete codolMap; + + return 1; + } + else + { + return Utils::Hook::Call(0x46C390)(atStreamStart); + } + } + + static const unsigned int crcTable[] = + { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + uint32_t Zones::HashCRC32StringInt(const std::string& string, uint32_t initialCrc) + { + auto curPtr = reinterpret_cast(const_cast(string.data())); + auto remaining = string.size(); + auto crc = ~initialCrc; + + for (; remaining--; ++curPtr) + { + crc = (crc >> 8) ^ crcTable[(crc ^ *curPtr) & 0xFF]; + } + + return (~crc); + } + + std::unordered_map Zones::fileDataMap; + std::mutex Zones::fileDataMutex; + + __declspec(naked) int Zones::FS_FOpenFileReadForThreadOriginal(const char*, int*, int) + { + __asm + { + sub esp, 0x33C; + + push 0x643276; + retn; + } + } + int Zones::FS_FOpenFileReadForThreadHook(const char* file, int* filePointer, int thread) + { + const auto retval = FS_FOpenFileReadForThreadOriginal(file, filePointer, thread); + + if (file != nullptr && filePointer != nullptr && strlen(file) >= 4 && retval > 0) + { + std::string fileBuffer; + fileBuffer.resize(retval); + auto readSize = Game::FS_Read(&fileBuffer[0], retval, *filePointer); + + // check if file should be skipped + auto skipFile = false; + if (!strncmp(&file[strlen(file) - 4], ".iwi", 4)) + { + if (readSize > 3 && !memcmp(&fileBuffer[0], "IWi", 3)) + { + skipFile = true; + } + } + else if (strstr(file, "weapons")) + { + skipFile = true; + } + else + { + if (readSize > 8 && *reinterpret_cast(&fileBuffer[4]) == 0xe9c9c447) + { + skipFile = true; + } + } + + // if the header seems encrypted... + if (fileBuffer.size() > 4 && readSize == retval && !skipFile) + { + auto packedSize = fileBuffer.size() - 4; + auto unpackedSize = *reinterpret_cast(&fileBuffer[fileBuffer.size() - 4]); + + // calc encrypted buffer size + auto encryptedBufferSize = fileBuffer.size(); + encryptedBufferSize -= 4; + encryptedBufferSize += 16 - (encryptedBufferSize % 16); + + // prepare encryptedData buffer + std::string encryptedData; + encryptedData.resize(encryptedBufferSize); + memcpy(&encryptedData[0], &fileBuffer[0], packedSize); + + // prepare decryptedData buffer + std::string decryptedData; + decryptedData.resize(encryptedBufferSize); + + register_cipher(&aes_desc); + + auto aes = find_cipher("aes"); + + // attempt to decrypt the IWI + symmetric_CTR ctr_state; + memset(&ctr_state, 0, sizeof(symmetric_CTR)); + + // decryption keys + std::uint8_t aesKey[24] = { 0x15, 0x9a, 0x03, 0x25, 0xe0, 0x75, 0x2e, 0x80, 0xc6, 0xc0, 0x94, 0x2a, 0x50, 0x5c, 0x1c, 0x68, 0x8c, 0x17, 0xef, 0x53, 0x99, 0xf8, 0x68, 0x3c }; + std::uint32_t aesIV[4] = { 0x1010101, 0x1010101, 0x1010101, 0x1010101 }; + + auto strippedFileName = std::filesystem::path(file).filename().string(); + auto nonce = HashCRC32StringInt(strippedFileName, strippedFileName.size()); + + std::uint8_t iv[16]; + memset(iv, 0, sizeof iv); + memcpy(iv, &nonce, 4); + memcpy(iv + 4, &unpackedSize, 4); + + ctr_start(aes, reinterpret_cast(&aesIV[0]), &aesKey[0], sizeof aesKey, 0, CTR_COUNTER_BIG_ENDIAN, &ctr_state); + + // decrypt image + auto readDataSize = 0u; + while (readDataSize < packedSize) + { + auto left = (packedSize - readDataSize); + auto blockSize = (left > 0x8000) ? 0x8000 : left; + + memcpy(iv + 8, &readDataSize, 4); + memcpy(iv + 12, &blockSize, 4); + + ctr_setiv(iv, sizeof iv, &ctr_state); + ctr_decrypt(reinterpret_cast(&encryptedData[readDataSize]), reinterpret_cast(&decryptedData[readDataSize]), blockSize, &ctr_state); + + readDataSize += blockSize; + } + + ctr_done(&ctr_state); + + if (static_cast(decryptedData[0]) == 0x78) + { + FileData data = {}; + data.readPos = 0; + data.len = unpackedSize; + data.fileContents.resize(unpackedSize); + + // decompress the buffer + auto result = uncompress(reinterpret_cast(&data.fileContents[0]), + reinterpret_cast(&data.len), reinterpret_cast(&decryptedData[0]), packedSize); + + // insert file data + if (result == Z_OK) + { + std::lock_guard $(fileDataMutex); + fileDataMap[*filePointer] = data; + return unpackedSize; + } + } + } + + // un-read data, file is apparently not encrypted + Game::FS_Seek(*filePointer, 0, Game::FS_SEEK_SET); + } + + return retval; + } + + __declspec(naked) int Zones::FS_ReadOriginal(void*, size_t, int) + { + __asm + { + push ecx; + mov eax, [esp + 0x10]; + + push 0x4A04C5; + retn; + } + } + int Zones::FS_ReadHook(void* buffer, size_t size, int filePointer) + { + std::lock_guard $(fileDataMutex); + + auto itr = fileDataMap.find(filePointer); + if (itr != fileDataMap.end()) + { + if (!itr->second.fileContents.empty()) + { + const auto readSize = std::min(size, itr->second.fileContents.size() - itr->second.readPos); + memcpy(buffer, &itr->second.fileContents[itr->second.readPos], readSize); + itr->second.readPos += readSize; + return readSize; + } + } + + return FS_ReadOriginal(buffer, size, filePointer); + } + __declspec(naked) void Zones::FS_FCloseFileOriginal(int) + { + __asm + { + mov eax, [esp + 4]; + push esi; + + push 0x462005; + retn; + } + } + void Zones::FS_FCloseFileHook(int filePointer) + { + std::lock_guard $(fileDataMutex); + + FS_FCloseFileOriginal(filePointer); + + const auto itr = fileDataMap.find(filePointer); + if (itr != fileDataMap.end()) + { + fileDataMap.erase(itr); + } + } + __declspec(naked) std::uint32_t Zones::FS_SeekOriginal(int, int, int) + { + __asm + { + push esi; + mov esi, [esp + 8]; + + push 0x4A63D5; + retn; + } + } + std::uint32_t Zones::FS_SeekHook(int fileHandle, int seekPosition, int seekOrigin) + { + std::lock_guard $(fileDataMutex); + + const auto itr = fileDataMap.find(fileHandle); + if (itr != fileDataMap.end()) + { + if (seekOrigin == Game::FS_SEEK_SET) + { + itr->second.readPos = seekPosition; + } + else if (seekOrigin == Game::FS_SEEK_CUR) + { + itr->second.readPos += seekPosition; + } + else if (seekOrigin == Game::FS_SEEK_END) + { + itr->second.readPos = itr->second.fileContents.size() - seekPosition; + } + + return itr->second.readPos; + } + else + { + return FS_SeekOriginal(fileHandle, seekPosition, seekOrigin); + } + } + + __declspec(naked) void Zones::LoadMapTriggersModelPointer() + { + static auto DB_ConvertOffsetToPointer_Address = 0x4A82B0; + + __asm + { + cmp dword ptr[edx + 4], 0; + je dontLoadAssetData; + + cmp dword ptr[edx + 4], 0xFFFFFFFF; + je loadAssetData; + + // check if FF is below 448, still load data in that case + cmp Zones::ZoneVersion, 448; + jl loadAssetData; + + // offset to pointer magic + pushad; + push eax; + call DB_ConvertOffsetToPointer_Address; + add esp, 4; + popad; + + dontLoadAssetData: + push 0x43CBF3; + retn; + + loadAssetData: + push 0x43CBC1; + retn; + } + } + __declspec(naked) void Zones::LoadMapTriggersHullPointer() + { + static auto DB_ConvertOffsetToPointer_Address = 0x4A82B0; + + __asm + { + cmp dword ptr[eax + 0Ch], 0; + je dontLoadAssetData; + + cmp dword ptr[eax + 0Ch], 0xFFFFFFFF; + je loadAssetData; + + // check if FF is below 448, still load data in that case + cmp Zones::ZoneVersion, 448; + jl loadAssetData; + + // offset to pointer magic + pushad; + push eax; + call DB_ConvertOffsetToPointer_Address; + add esp, 4; + popad; + + dontLoadAssetData: + push 0x43CC2E; + retn; + + loadAssetData: + push 0x43CBFE; + retn; + } + } + __declspec(naked) void Zones::LoadMapTriggersSlabPointer() + { + static auto DB_ConvertOffsetToPointer_Address = 0x4A82B0; + + __asm + { + cmp dword ptr[eax + 14h], 0; + je dontLoadAssetData; + + cmp dword ptr[eax + 14h], 0xFFFFFFFF; + je loadAssetData; + + // check if FF is below 448, still load data in that case + cmp Zones::ZoneVersion, 448; + jl loadAssetData; + + // offset to pointer magic + pushad; + push eax; + call DB_ConvertOffsetToPointer_Address; + add esp, 4; + popad; + + dontLoadAssetData: + push 0x43CC6D; + retn; + + loadAssetData: + push 0x43CC39; + retn; + } + } + + void Zones::LoadFxWorldAsset(Game::FxWorld** asset) + { + Utils::Hook::Call(0x4857F0)(asset); + + if (Zones::Version() >= 423 && asset && *asset) + { + // allocate glass data structures + static Game::GameWorldMp glassMap; + static Game::GameWorldMp* glassMapPtr; + static Game::G_GlassData glassData; + static std::vector glassPieces; + + // clear previous glass data + memset(&glassMap, 0, sizeof Game::GameWorldMp); + memset(&glassData, 0, sizeof Game::G_GlassData); + glassPieces.clear(); + + // generate glassPieces array + const auto pieceCount = (*asset)->glassSys.initPieceCount; + if (pieceCount > 0) + { + glassPieces.resize(pieceCount); + memset(&glassPieces[0], 0, sizeof(Game::G_GlassPiece) * pieceCount); + + // generate glassData array + glassData.glassPieces = glassPieces.data(); + glassData.pieceCount = glassPieces.size(); + } + else + { + // game seems to do an array lookup on the first index even if there's no glass pieces? + static Game::G_GlassPiece emptyPiece; + glassData.glassPieces = &emptyPiece; + } + + // build glass asset + glassMap.g_glassData = &glassData; + glassMap.name = (*asset)->name; + + // set glass map ptr + glassMapPtr = &glassMap; + + // add glass to DB + Utils::Hook::Call(0x4A6240)(&glassMapPtr); + } + } + + void Zones::LoadXModelAsset(Game::XModel** asset) + { + if (Zones::Version() >= 448) + { + for (int i = 0; i < (*asset)->numLods; i++) + { + if ((*asset)->lodInfo[i].surfs == nullptr && Zones::Version() >= 448) + { + const auto name = (*asset)->name; + const auto fx_model = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_XMODEL, "void").model; + memcpy(*asset, fx_model, sizeof Game::XModel); + (*asset)->name = name; + break; + } + } + } + + return Utils::Hook::Call(0x47A690)(asset); + } + + // patch max file amount returned by Sys_ListFiles + constexpr auto fileCountMultiplier = 8; + constexpr auto maxFileCount = 8191 * fileCountMultiplier; + + int RelocateFileCount(std::uint32_t dwSize, int unk1, int unk2, int unk3) + { + dwSize *= fileCountMultiplier; + return Utils::Hook::Call(0x430E90)(dwSize, unk1, unk2, unk3); + } + + void Zones::LoadMaterialAsset(Game::Material** asset) + { + if (asset && *asset && Zones::Version() >= 448) + { + static std::vector broken_materials = { + "gfx_fxt_debris_wind_ash_z10", + "gfx_fxt_smk_light_z3" + }; + + // replace broken materials with the default one as restricting them does not seem to work. + const auto itr = std::find(broken_materials.begin(), broken_materials.end(), (*asset)->info.name); + if (itr != broken_materials.end()) + { + const auto name = (*asset)->info.name; + const auto default_material = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "$default").material; + memcpy(*asset, default_material, sizeof Game::Material); + (*asset)->info.name = name; + } + } + + return Utils::Hook::Call(0x476750)(asset); + } + + void Zones::LoadTracerDef(bool atStreamStart, Game::TracerDef* tracer, int size) + { + if (Zones::Version() >= 460) + { + size = 116; + } + + Game::Load_Stream(atStreamStart, tracer, size); + *Game::varFxEffectDefHandle = nullptr; + + if (Zones::Version() >= 460) + { + *Game::varFxEffectDefHandle = reinterpret_cast(tracer + 8); + + std::memmove(tracer + 8, tracer + 12, size - 12); + AssetHandler::Relocate(tracer + 12, tracer + 8, size - 12); + } + } + + void Zones::LoadTracerDefFxEffect() + { + if (Zones::Version() >= 460) + { + Game::Load_FxEffectDefHandle(false); + } + + Game::DB_PopStreamPos(); + } + Zones::Zones() { Zones::ZoneVersion = 0; + Command::Add("decryptImages", [](Command::Params*) + { + auto images = Game::Sys_ListFilesWrapper("iw4x/images", "iwi"); + Logger::Print("decrypting %u images...\n", images.size()); + + for (auto& image : images) + { + char* buffer = nullptr; + auto fileLength = Game::FS_ReadFile(Utils::String::VA("images/%s", image.data()), &buffer); + + if (fileLength && buffer) + { + if (!std::filesystem::exists("raw/images")) + { + std::filesystem::create_directories("raw/images"); + } + + if (!std::filesystem::exists(Utils::String::VA("raw/images/%s", image.data()))) + { + const auto fp = fopen(Utils::String::VA("raw/images/%s", image.data()), "wb"); + if (fp) + { + fwrite(buffer, fileLength, 1, fp); + fclose(fp); + } + } + + Game::FS_FreeFile(buffer); + } + } + + Logger::Print("decrypted %u images!\n", images.size()); + }); + Command::Add("decryptSounds", [](Command::Params*) + { + auto sounds = Game::Sys_ListFilesWrapper("iw4x/sound", "iwi"); + Logger::Print("decrypting %u sounds...\n", sounds.size()); + + for (auto& sound : sounds) + { + char* buffer = nullptr; + auto fileLength = Game::FS_ReadFile(Utils::String::VA("sound/%s", sound.data()), &buffer); + + if (fileLength && buffer) + { + auto path = std::filesystem::path(sound.data()); + std::filesystem::create_directories("raw/sound" / path.parent_path()); + + if (!std::filesystem::exists(Utils::String::VA("raw/sound/%s", sound.data()))) + { + const auto fp = fopen(Utils::String::VA("raw/sound/%s", sound.data()), "wb"); + if (fp) + { + fwrite(buffer, fileLength, 1, fp); + fclose(fp); + } + } + + Game::FS_FreeFile(buffer); + } + } + + Logger::Print("decrypted %u sounds!\n", sounds.size()); + }); + + // patch max filecount Sys_ListFiles can return + Utils::Hook::Set(0x45A66B, (maxFileCount + fileCountMultiplier) * 4); + Utils::Hook::Set(0x64AF78, maxFileCount); + Utils::Hook::Set(0x64B04F, maxFileCount); + Utils::Hook::Set(0x45A8CE, maxFileCount); + Utils::Hook(0x45A806, RelocateFileCount, HOOK_CALL).install()->quick(); + Utils::Hook(0x45A6A0, RelocateFileCount, HOOK_CALL).install()->quick(); + // Ignore missing soundaliases for now // TODO: Include them in the dependency zone! Utils::Hook::Nop(0x644207, 5); @@ -1589,6 +3419,29 @@ namespace Components // addon_map_ents asset type (we reuse it for weaponattach) Utils::Hook::Set(0x418B31, 0x72); + // encrypted images hooks + Utils::Hook(0x462000, Zones::FS_FCloseFileHook, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4A04C0, Zones::FS_ReadHook, HOOK_JUMP).install()->quick(); + Utils::Hook(0x643270, Zones::FS_FOpenFileReadForThreadHook, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4A63D0, Zones::FS_SeekHook, HOOK_JUMP).install()->quick(); + + // asset hooks + Utils::Hook(0x47146D, Zones::LoadTracerDef, HOOK_CALL).install()->quick(); + Utils::Hook(0x4714A3, Zones::LoadTracerDefFxEffect, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4039DE, Zones::LoadMaterialAsset, HOOK_CALL).install()->quick(); + Utils::Hook(0x4FCAEE, Zones::LoadXModelAsset, HOOK_CALL).install()->quick(); + Utils::Hook(0x5BA01E, Zones::LoadFxWorldAsset, HOOK_CALL).install()->quick(); + Utils::Hook(0x43CBBB, Zones::LoadMapTriggersModelPointer, HOOK_JUMP).install()->quick(); + Utils::Hook(0x43CBF8, Zones::LoadMapTriggersHullPointer, HOOK_JUMP).install()->quick(); + Utils::Hook(0x43CC33, Zones::LoadMapTriggersSlabPointer, HOOK_JUMP).install()->quick(); + Utils::Hook(0x47E1DD, Zones::LoadMapEnts, HOOK_CALL).install()->quick(); + Utils::Hook(0x4BF992, Zones::LoadClipMap, HOOK_CALL).install()->quick(); + Utils::Hook(0x4C86D8, Zones::LoadXModelColSurfPtr, HOOK_JUMP).install()->quick(); + Utils::Hook(0x44EB21, Zones::LoadGfxLightMapExtraData, HOOK_CALL).install()->quick(); + Utils::Hook(0x44EAD3, Zones::LoadGfxReflectionProbes, HOOK_CALL).install()->quick(); + Utils::Hook(0x4C08F8, Zones::LoadGfxXSurfaceExtraData, HOOK_CALL).install()->quick(); + Utils::Hook(0x4C08DC, Zones::LoadGfxXSurfaceArray, HOOK_CALL).install()->quick(); + Utils::Hook(0x45AE3D, Zones::LoadRandomFxGarbage, HOOK_CALL).install()->quick(); Utils::Hook(0x495938, Zones::LoadFxElemDefArrayStub, HOOK_CALL).install()->quick(); Utils::Hook(0x45ADA0, Zones::LoadFxElemDefStub, HOOK_CALL).install()->quick(); Utils::Hook(0x4EA6FE, Zones::LoadXModelLodInfoStub, HOOK_CALL).install()->quick(); @@ -1611,20 +3464,18 @@ namespace Components Utils::Hook(0x4D6A0B, Zones::LoadPathNodeArray, HOOK_CALL).install()->quick(); Utils::Hook(0x4D6A47, Zones::LoadPathDataConstant, HOOK_JUMP).install()->quick(); Utils::Hook(0x463D6E, Zones::LoadPathnodeConstantTail, HOOK_CALL).install()->quick(); - Utils::Hook(0x4471AD, Zones::LoadGfxImage, HOOK_CALL).install()->quick(); - Utils::Hook(0x41A590, Zones::LoadExpressionSupportingDataPtr, HOOK_CALL).install()->quick(); Utils::Hook(0x459833, Zones::LoadExpressionSupportingDataPtr, HOOK_JUMP).install()->quick(); - Utils::Hook(0x5B9AA5, Zones::LoadXAsset, HOOK_CALL).install()->quick(); + Utils::Hook(0x461740, Zones::LoadMaterialTechniqueArray, HOOK_CALL).install()->quick(); Utils::Hook(0x461710, Zones::LoadMaterialTechnique, HOOK_CALL).install()->quick(); Utils::Hook(0x40330D, Zones::LoadMaterial, HOOK_CALL).install()->quick(); Utils::Hook(0x4B8DC0, Zones::LoadGfxWorld, HOOK_CALL).install()->quick(); Utils::Hook(0x4B8FF5, Zones::Loadsunflare_t, HOOK_CALL).install()->quick(); - Utils::Hook(0x418998, Zones::GameMapSpPatchStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x427A1B, Zones::LoadPathDataTail, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4F4D3B, [] () { if (Zones::ZoneVersion >= VERSION_ALPHA3) @@ -1660,3 +3511,4 @@ namespace Components } } +#pragma optimize( "", on ) diff --git a/src/Components/Modules/Zones.hpp b/src/Components/Modules/Zones.hpp index faa2d17f..f97fba5e 100644 --- a/src/Components/Modules/Zones.hpp +++ b/src/Components/Modules/Zones.hpp @@ -9,6 +9,13 @@ namespace Components class Zones : public Component { public: + struct FileData + { + std::uint32_t readPos; + std::uint32_t len; + std::string fileContents; + }; + Zones(); ~Zones(); @@ -17,6 +24,7 @@ namespace Components static int Version() { return Zones::ZoneVersion; }; private: + static int ZoneVersion; static int FxEffectIndex; @@ -61,6 +69,36 @@ namespace Components static void LoadImpactFxArray(); static int ImpactFxArrayCount(); static void LoadPathDataConstant(); + static void GetCurrentAssetTypeStub(); + static int LoadRandomFxGarbage(bool atStreamStart, char* buffer, int size); + static int LoadGfxXSurfaceArray(bool atStreamStart, char* buffer, int size); + static int LoadGfxXSurfaceExtraData(bool atStreamStart); + static int LoadGfxReflectionProbes(bool atStreamStart, char* buffer, int size); + static void LoadGfxLightMapExtraData(); + static void LoadXModelColSurfPtr(); static int PathDataSize(); + static int LoadMaterialTechniqueArray(bool atStreamStart, int count); + static int LoadMapEnts(bool atStreamStart, Game::MapEnts* buffer, int size); + static void Load_ClipInfo(bool atStreamStart); + static int LoadClipMap(bool atStreamStart); + static uint32_t HashCRC32StringInt(const std::string& Value, uint32_t Initial); + static std::unordered_map fileDataMap; + static std::mutex fileDataMutex; + static int FS_FOpenFileReadForThreadOriginal(const char*, int*, int); + static int FS_FOpenFileReadForThreadHook(const char* file, int* filePointer, int thread); + static int FS_ReadOriginal(void*, size_t, int); + static int FS_ReadHook(void* buffer, size_t size, int filePointer); + static void FS_FCloseFileOriginal(int); + static void FS_FCloseFileHook(int filePointer); + static std::uint32_t FS_SeekOriginal(int, int, int); + static std::uint32_t FS_SeekHook(int fileHandle, int seekPosition, int seekOrigin); + static void LoadMapTriggersModelPointer(); + static void LoadMapTriggersHullPointer(); + static void LoadMapTriggersSlabPointer(); + static void LoadFxWorldAsset(Game::FxWorld** asset); + static void LoadXModelAsset(Game::XModel** asset); + static void LoadMaterialAsset(Game::Material** asset); + static void LoadTracerDef(bool atStreamStart, Game::TracerDef* tracer, int size); + static void LoadTracerDefFxEffect(); }; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 701f1412..672687ec 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -2,6 +2,26 @@ namespace Game { + std::vector Sys_ListFilesWrapper(const std::string& directory, const std::string& extension) + { + auto fileCount = 0; + auto files = Game::Sys_ListFiles(directory.data(), extension.data(), 0, &fileCount, 0); + + std::vector result; + + for (auto i = 0; i < fileCount; i++) + { + if (files[i]) + { + result.push_back(files[i]); + } + } + + Game::FS_FreeFileList(files); + + return result; + } + AddRefToObject_t AddRefToObject = AddRefToObject_t(0x61C360); AllocObject_t AllocObject = AllocObject_t(0x434320); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index aa4f1bf8..7b8eedf4 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -2,6 +2,26 @@ namespace Game { + template static void DB_ConvertOffsetToPointer(T* pointer) + { + Utils::Hook::Call(0x4A82B0)(pointer); + } + template static T** DB_InsertPointer() + { + static auto DB_InsertPointer_Address = 0x43B290; + T** retval = nullptr; + + __asm + { + call DB_InsertPointer_Address; + mov retval, eax; + } + + return retval; + } + + std::vector Sys_ListFilesWrapper(const std::string& directory, const std::string& extension); + typedef void(__cdecl * AddRefToObject_t)(unsigned int id); extern AddRefToObject_t AddRefToObject; @@ -319,7 +339,7 @@ namespace Game typedef void(__cdecl * LargeLocalInit_t)(); extern LargeLocalInit_t LargeLocalInit; - typedef bool(__cdecl * Load_Stream_t)(bool atStreamStart, const void *ptr, int size); + typedef bool(__cdecl * Load_Stream_t)(bool atStreamStart, const void *ptr, unsigned int size); extern Load_Stream_t Load_Stream; typedef void(__cdecl * Load_XString_t)(bool atStreamStart);