Merge pull request #5 from IW4x/feature/codo-zones

Implement support for newer CoD:OL zones
This commit is contained in:
Maurice Heumann 2019-10-03 18:06:57 +02:00 committed by GitHub
commit 02b8305dfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 2280 additions and 130 deletions

View File

@ -112,5 +112,9 @@ namespace Components
Utils::Hook::Set<BYTE>(0x4A95F8, 32);
Utils::Hook::Set<int>(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<std::uint32_t>(0x630AA3, arenaCount);
}
}

View File

@ -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<json11::Json> 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<json11::Json> 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<json11::Json> techniques;
for (int technique = 0; technique < 48; technique++)
{
auto curTech = techset->techniques[technique];
if (curTech)
{
std::vector<json11::Json> passDataArray;
for (int pass = 0; pass < curTech->passCount; pass++)
{
auto curPass = &curTech->passArray[pass];
std::vector<json11::Json> 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<float> 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<int>(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<bool>() && 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());

View File

@ -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();

View File

@ -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<Game::XModelSurfs**>(0x0112A95C);

View File

@ -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<Game::XZoneInfo> 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<char*>(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<void(Game::gentity_s*, int, int)>(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

View File

@ -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);
};
}

File diff suppressed because it is too large Load Diff

View File

@ -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<int, Zones::FileData> 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();
};
}

View File

@ -2,6 +2,26 @@
namespace Game
{
std::vector<std::string> 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<std::string> 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);

View File

@ -2,6 +2,26 @@
namespace Game
{
template <typename T> static void DB_ConvertOffsetToPointer(T* pointer)
{
Utils::Hook::Call<void(T*)>(0x4A82B0)(pointer);
}
template <typename T> 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<std::string> 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);