diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 6691067b..2565bac6 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -470,6 +470,181 @@ namespace Components QuickPatch::CompareMaterialStateBits(); }); + Command::Add("dumptechsets", [](Command::Params* param) + { + if (param->length() != 2) + { + Logger::Print("usage: dumptechsets | all\n"); + return; + } + std::vector fastfiles; + + if (param->get(1) == "all"s) + { + for (std::string f : Utils::IO::ListFiles("zone/english")) + fastfiles.push_back(f.substr(7, f.length() - 10)); + for (std::string f : Utils::IO::ListFiles("zone/dlc")) + fastfiles.push_back(f.substr(3, f.length() - 6)); + for (std::string f : Utils::IO::ListFiles("zone/patch")) + fastfiles.push_back(f.substr(5, f.length() - 8)); + } + else + { + fastfiles.push_back(param->get(1)); + } + + int count = 0; + + AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool* /*restrict*/) + { + // they're basically the same right? + if (type == Game::ASSET_TYPE_PIXELSHADER | type == Game::ASSET_TYPE_VERTEXSHADER) + { + Utils::IO::CreateDir("userraw/shader_bin"); + + const char* formatString; + if (type == Game::ASSET_TYPE_PIXELSHADER) + { + formatString = "userraw/shader_bin/%.ps"; + } + else + { + formatString = "userraw/shader_bin/%.vs"; + } + + if (Utils::IO::FileExists(Utils::String::VA(formatString, name))) return; + + Utils::Stream* buffer = new Utils::Stream(0x1000); + Game::MaterialPixelShader* dest = buffer->dest(); + buffer->save(asset.pixelShader); + + if (asset.pixelShader->loadDef.physicalPart) + { + buffer->save(asset.pixelShader->loadDef.physicalPart, 4, asset.pixelShader->loadDef.cachedPartSize & 0xFFFF); + Utils::Stream::ClearPointer(&dest->loadDef.physicalPart); + } + + Utils::IO::WriteFile(Utils::String::VA(formatString, name), buffer->toBuffer()); + } + + static std::map pointerMap; + + // Check if the given pointer has already been mapped + std::function hasPointer = [](const void* pointer) + { + return (pointerMap.find(pointer) != pointerMap.end()); + }; + + // Get stored offset for given file pointer + std::function getPointer = [hasPointer](const void* pointer) + { + if (hasPointer(pointer)) + { + return pointerMap[pointer]; + } + + return 0U; + }; + + std::function storePointer = [hasPointer](const void* ptr, unsigned int offset) + { + if (hasPointer(ptr)) return; + pointerMap[ptr] = offset; + }; + + 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); + + if (asset.techniqueSet->name) + { + buffer->saveString(asset.techniqueSet->name); + Utils::Stream::ClearPointer(&dest->name); + } + + for (int i = 0; i < ARRAYSIZE(Game::MaterialTechniqueSet::techniques); ++i) + { + Game::MaterialTechnique* technique = asset.techniqueSet->techniques[i]; + + if (technique) + { + dest->techniques[i] = reinterpret_cast(getPointer(technique)); + if (!dest->techniques) + { + // Size-check is obsolete, as the structure is dynamic + buffer->align(Utils::Stream::ALIGN_4); + //storePointer(technique, buffer->); + + Game::MaterialTechnique* destTechnique = buffer->dest(); + buffer->save(technique, 8); + + // Save_MaterialPassArray + Game::MaterialPass* destPasses = buffer->dest(); + buffer->saveArray(technique->passes, technique->numPasses); + + for (short j = 0; j < technique->numPasses; ++j) + { + AssertSize(Game::MaterialPass, 20); + + Game::MaterialPass* destPass = &destPasses[j]; + Game::MaterialPass* pass = &technique->passes[j]; + + if (pass->vertexDecl) + { + + } + + + if (pass->argumentDef) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(pass->argumentDef, pass->argCount1 + pass->argCount2 + pass->argCount3); + Utils::Stream::ClearPointer(&destPass->argumentDef); + } + } + + if (technique->name) + { + buffer->saveString(technique->name); + Utils::Stream::ClearPointer(&destTechnique->name); + } + + Utils::Stream::ClearPointer(&dest->techniques[i]); + } + } + } + } + }); + + for (std::string fastfile : fastfiles) + { + if (!Game::DB_IsZoneLoaded(fastfile.data())) + { + Game::XZoneInfo info; + info.name = fastfile.data(); + info.allocFlags = 0x20; + info.freeFlags = 0; + + Game::DB_LoadXAssets(&info, 1, true); + } + + // unload the fastfiles so we don't run out of memory or asset pools + if (count % 5) + { + Game::XZoneInfo info; + info.name = nullptr; + info.allocFlags = 0x0; + info.freeFlags = 0x20; + Game::DB_LoadXAssets(&info, 1, true); + } + + count++; + } + }); + // Dvars Dvar::Register("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI");