diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index 0d705375..0aa6ea69 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -142,29 +142,30 @@ namespace Assets replacementFound = false; // Find correct sortkey by comparing techsets - Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_MATERIAL, [](Game::XAssetHeader header, void* data) + Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset](Game::XAssetEntry* entry) { if (!replacementFound) { - Game::Material* material = reinterpret_cast(data); - const char* name = material->techniqueSet->name; + Game::XAssetHeader header = entry->asset.header; + + const char* name = asset->techniqueSet->name; if (name[0] == ',') ++name; if (std::string(name) == header.material->techniqueSet->name) { - material->sortKey = header.material->sortKey; + asset->sortKey = header.material->sortKey; // This is temp, as nobody has time to fix materials - material->stateBitsCount = header.material->stateBitsCount; - material->stateBitTable = header.material->stateBitTable; - std::memcpy(material->stateBitsEntry, header.material->stateBitsEntry, 48); - material->constantCount = header.material->constantCount; - material->constantTable = header.material->constantTable; + asset->stateBitsCount = header.material->stateBitsCount; + asset->stateBitTable = header.material->stateBitTable; + std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48); + asset->constantCount = header.material->constantCount; + asset->constantTable = header.material->constantTable; replacementFound = true; } } - }, asset, false); + }, false, false); if (!replacementFound && asset->techniqueSet) { diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index 036aee78..d30d382d 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -696,6 +696,16 @@ namespace Components Dvar::Var("isDlcInstalled_All").setRaw(hasAllDlcs ? 1 : 0); } + Game::XAssetEntry* Maps::GetAssetEntryPool() + { + if(Maps::EntryPool.empty()) + { + return reinterpret_cast(0x134CAD8); + } + + return Maps::EntryPool.data(); + } + void Maps::reallocateEntryPool() { AssertSize(Game::XAssetEntry, 16); diff --git a/src/Components/Modules/Maps.hpp b/src/Components/Modules/Maps.hpp index aa57dfb1..ffa039bd 100644 --- a/src/Components/Modules/Maps.hpp +++ b/src/Components/Modules/Maps.hpp @@ -67,6 +67,8 @@ namespace Components static UserMapContainer* GetUserMap(); static unsigned int GetUsermapHash(std::string map); + static Game::XAssetEntry* GetAssetEntryPool(); + private: class DLC { diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index c52cb2c3..e096d576 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -555,13 +555,13 @@ namespace Components if (type == Game::ASSET_TYPE_TECHNIQUE_SET) { Utils::IO::CreateDir("userraw/techsets"); - Utils::Stream* buffer = new Utils::Stream(0x1000); - Game::MaterialTechniqueSet* dest = buffer->dest(); - buffer->save(asset.techniqueSet); + Utils::Stream buffer(0x1000); + Game::MaterialTechniqueSet* dest = buffer.dest(); + buffer.save(asset.techniqueSet); if (asset.techniqueSet->name) { - buffer->saveString(asset.techniqueSet->name); + buffer.saveString(asset.techniqueSet->name); Utils::Stream::ClearPointer(&dest->name); } @@ -575,15 +575,15 @@ namespace Components if (!dest->techniques) { // Size-check is obsolete, as the structure is dynamic - buffer->align(Utils::Stream::ALIGN_4); + buffer.align(Utils::Stream::ALIGN_4); //storePointer(technique, buffer->); - Game::MaterialTechnique* destTechnique = buffer->dest(); - buffer->save(technique, 8); + Game::MaterialTechnique* destTechnique = buffer.dest(); + buffer.save(technique, 8); // Save_MaterialPassArray - Game::MaterialPass* destPasses = buffer->dest(); - buffer->saveArray(technique->passArray, technique->passCount); + Game::MaterialPass* destPasses = buffer.dest(); + buffer.saveArray(technique->passArray, technique->passCount); for (short j = 0; j < technique->passCount; ++j) { @@ -599,15 +599,15 @@ namespace Components if (pass->args) { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(pass->args, pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount); + buffer.align(Utils::Stream::ALIGN_4); + buffer.saveArray(pass->args, pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount); Utils::Stream::ClearPointer(&destPass->args); } } if (technique->name) { - buffer->saveString(technique->name); + buffer.saveString(technique->name); Utils::Stream::ClearPointer(&destTechnique->name); } diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 1fee5afd..56d91699 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -7,8 +7,6 @@ namespace Components std::string ZoneBuilder::TraceZone; std::vector> ZoneBuilder::TraceAssets; - std::vector> ZoneBuilder::CommonAssets; - ZoneBuilder::Zone::Zone(std::string name) : indexStart(0), externalSize(0), // Reserve 100MB by default. @@ -749,21 +747,25 @@ namespace Components if (type >= 0 && type < Game::XAssetType::ASSET_TYPE_COUNT) { - for (auto& asset : ZoneBuilder::CommonAssets) + int zoneIndex = Game::DB_GetZoneIndex("common_mp"); + + if (zoneIndex > 0) { - if (asset.first == type && asset.second == name) + Game::DB_EnumXAssetEntries(type, [&](Game::XAssetEntry* entry) { - // Allocate an empty asset (filled with zeros) - header.data = builder->getAllocator()->allocate(Game::DB_GetXAssetSizeHandlers[type]()); + if (!header.data && entry->zoneIndex == zoneIndex && Game::DB_GetXAssetName(&entry->asset) == name) + { + // Allocate an empty asset (filled with zeros) + header.data = builder->getAllocator()->allocate(Game::DB_GetXAssetSizeHandlers[type]()); - // Set the name to the original name, so it can be stored - Game::DB_SetXAssetNameHandlers[type](&header, name.data()); - AssetHandler::StoreTemporaryAsset(type, header); + // Set the name to the original name, so it can be stored + Game::DB_SetXAssetNameHandlers[type](&header, name.data()); + AssetHandler::StoreTemporaryAsset(type, header); - // Set the name to the empty name - Game::DB_SetXAssetNameHandlers[type](&header, builder->getAllocator()->duplicateString("," + name)); - break; - } + // Set the name to the empty name + Game::DB_SetXAssetNameHandlers[type](&header, builder->getAllocator()->duplicateString("," + name)); + } + }, true, true); } } @@ -853,12 +855,6 @@ namespace Components AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader /*asset*/, std::string name, bool* /*restrict*/) { - // This is used to track which assets can be stored as empty assets - if (FastFiles::Current() == "common_mp") - { - ZoneBuilder::CommonAssets.push_back({ type, name }); - } - if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current()) { ZoneBuilder::TraceAssets.push_back({ type, name }); @@ -998,7 +994,6 @@ namespace Components ZoneBuilder::~ZoneBuilder() { assert(ZoneBuilder::MemAllocator.empty()); - ZoneBuilder::CommonAssets.clear(); } #if defined(DEBUG) || defined(FORCE_UNIT_TESTS) diff --git a/src/Components/Modules/ZoneBuilder.hpp b/src/Components/Modules/ZoneBuilder.hpp index f1b0834b..a59edf35 100644 --- a/src/Components/Modules/ZoneBuilder.hpp +++ b/src/Components/Modules/ZoneBuilder.hpp @@ -105,8 +105,6 @@ namespace Components static std::string TraceZone; static std::vector> TraceAssets; - static std::vector> CommonAssets; - static void BeginAssetTrace(std::string zone); static std::vector> EndAssetTrace(); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index c292dde4..a4b5f112 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -363,6 +363,9 @@ namespace Game infoParm_t* infoParams = reinterpret_cast(0x79D260); // Count 0x1E + XZone* g_zones = reinterpret_cast(0x14C0F80); + unsigned short* db_hashTable = reinterpret_cast(0x12412B0); + XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize) { int elSize = DB_GetXAssetSizeHandlers[type](); @@ -476,6 +479,19 @@ namespace Game return ASSET_TYPE_INVALID; } + int DB_GetZoneIndex(std::string name) + { + for (int i = 0; i < 32; ++i) + { + if (Game::g_zones[i].name == name) + { + return i; + } + } + + return -1; + } + bool DB_IsZoneLoaded(const char* zone) { int zoneCount = Utils::Hook::Get(0x1261BCC); @@ -495,6 +511,48 @@ namespace Game return false; } + void DB_EnumXAssetEntries(XAssetType type, std::function callback, bool overrides, bool lock) + { + volatile long* lockVar = reinterpret_cast(0x16B8A54); + if (lock) InterlockedIncrement(lockVar); + + while (lock && *reinterpret_cast(0x16B8A58)) std::this_thread::sleep_for(1ms); + + unsigned int index = 0; + do + { + unsigned short hashIndex = db_hashTable[index]; + if (hashIndex) + { + do + { + XAssetEntry* asset = &Components::Maps::GetAssetEntryPool()[hashIndex]; + hashIndex = asset->nextHash; + if (asset->asset.type == type) + { + callback(asset); + if (overrides) + { + unsigned short overrideIndex = asset->nextOverride; + if (asset->nextOverride) + { + do + { + asset = &Components::Maps::GetAssetEntryPool()[overrideIndex]; + callback(asset); + overrideIndex = asset->nextOverride; + } while (overrideIndex); + } + } + } + } while (hashIndex); + } + ++index; + } while (index < 74000); + + if(lock) InterlockedDecrement(lockVar); + } + __declspec(naked) XAssetHeader DB_FindXAssetDefaultHeaderInternal(XAssetType /*type*/) { __asm diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 7b4f5d87..44e45867 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -758,6 +758,9 @@ namespace Game extern infoParm_t* infoParams; + extern XZone* g_zones; + extern unsigned short* db_hashTable; + XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize); void Menu_FreeItemMemory(Game::itemDef_t* item); const char* TableLookup(StringTable* stringtable, int row, int column); @@ -767,7 +770,9 @@ namespace Game const char *DB_GetXAssetName(XAsset *asset); XAssetType DB_GetXAssetNameType(const char* name); + int DB_GetZoneIndex(std::string name); bool DB_IsZoneLoaded(const char* zone); + void DB_EnumXAssetEntries(XAssetType type, std::function callback, bool overrides, bool lock); XAssetHeader DB_FindXAssetDefaultHeaderInternal(XAssetType type); XAssetEntry* DB_FindXAssetEntry(XAssetType type, const char* name); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 3f844f5a..83620e6d 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -3674,7 +3674,7 @@ namespace Game { XAsset asset; char zoneIndex; - bool inuse; + volatile char inuse; unsigned __int16 nextHash; unsigned __int16 nextOverride; unsigned __int16 usageFrame; @@ -3755,6 +3755,26 @@ namespace Game XAssetList assetList; }; + struct XZoneMemory + { + XBlock blocks[MAX_XFILE_COUNT]; + char *lockedVertexData; + char *lockedIndexData; + void *vertexBuffer; + void *indexBuffer; + }; + + struct XZone + { + int unk; + char name[64]; + int flags; + int allocType; + XZoneMemory mem; + int fileSize; + char modZone; + }; + struct XNKID { char ab[8];