diff --git a/.gitignore b/.gitignore index 924c9cc1..5278875b 100644 --- a/.gitignore +++ b/.gitignore @@ -143,6 +143,7 @@ UpgradeLog*.htm *.til *.idb *.i64 +ida/* ### Custom user files # User scripts diff --git a/deps/json11 b/deps/json11 index 8d33613e..ec4e4521 160000 --- a/deps/json11 +++ b/deps/json11 @@ -1 +1 @@ -Subproject commit 8d33613ebde0e7756a026e3770330da11e741823 +Subproject commit ec4e45219af1d7cde3d58b49ed762376fccf1ace diff --git a/deps/libtommath b/deps/libtommath index 2d80a97a..13444a8a 160000 --- a/deps/libtommath +++ b/deps/libtommath @@ -1 +1 @@ -Subproject commit 2d80a97a2b48aa1ac6d8f0df29cc4dd6297b1fba +Subproject commit 13444a8af2d077eda8fd0be017cac2dad20465dc diff --git a/deps/mongoose b/deps/mongoose index c2a10601..e9a8e546 160000 --- a/deps/mongoose +++ b/deps/mongoose @@ -1 +1 @@ -Subproject commit c2a10601b9486f28b75307f003f5d9078acdce10 +Subproject commit e9a8e5468d9e6e37fd2813decfd49f0d5312e66e diff --git a/deps/pdcurses b/deps/pdcurses index 3f2336ad..2b9bb5d0 160000 --- a/deps/pdcurses +++ b/deps/pdcurses @@ -1 +1 @@ -Subproject commit 3f2336ad818b394110494ccb6d0dafed54a13950 +Subproject commit 2b9bb5d0c8fc563fec431cf24efb0f985e4b43b7 diff --git a/deps/zlib b/deps/zlib index 0d36ec47..7c0c75e9 160000 --- a/deps/zlib +++ b/deps/zlib @@ -1 +1 @@ -Subproject commit 0d36ec47f310478549c0864f215ab5c0114c49ba +Subproject commit 7c0c75e990ca5395139c148f120042048b0ce091 diff --git a/src/Components/Modules/AssetHandler.cpp b/src/Components/Modules/AssetHandler.cpp index 82aa25ec..6042048a 100644 --- a/src/Components/Modules/AssetHandler.cpp +++ b/src/Components/Modules/AssetHandler.cpp @@ -3,6 +3,7 @@ namespace Components { thread_local int AssetHandler::BypassState = 0; + bool AssetHandler::ShouldSearchTempAssets = false; std::map AssetHandler::AssetInterfaces; std::map> AssetHandler::TypeCallbacks; Utils::Signal AssetHandler::RestrictSignal; @@ -69,6 +70,21 @@ namespace Components return header; } + Game::XAssetHeader AssetHandler::FindTemporaryAsset(Game::XAssetType type, const char* filename) + { + Game::XAssetHeader header = { nullptr }; + if (type >= Game::XAssetType::ASSET_TYPE_COUNT) return header; + + auto tempPool = &AssetHandler::TemporaryAssets[type]; + auto entry = tempPool->find(filename); + if (entry != tempPool->end()) + { + header = { entry->second }; + } + + return header; + } + int AssetHandler::HasThreadBypass() { return AssetHandler::BypassState > 0; @@ -116,7 +132,7 @@ namespace Components test al, al - jnz finishOriginal + jnz checkTempAssets mov ecx, [esp + 18h] // Asset type mov ebx, [esp + 1Ch] // Filename @@ -139,9 +155,28 @@ namespace Components test eax, eax jnz finishFound + + checkTempAssets: + mov al, AssetHandler::ShouldSearchTempAssets // check to see if enabled + test eax, eax + jz finishOriginal - finishOriginal: - // Asset not found using custom handlers, redirect to DB_FindXAssetHeader + mov ecx, [esp + 18h] // Asset type + mov ebx, [esp + 1Ch] // Filename + + push ebx + push ecx + + call AssetHandler::FindTemporaryAsset + + add esp, 8h + + test eax, eax + jnz finishFound + + finishOriginal: + // Asset not found using custom handlers or in temp assets or bypasses were enabled + // redirect to DB_FindXAssetHeader mov ebx, ds:6D7190h // InterlockedDecrement mov eax, 40793Bh jmp eax @@ -454,6 +489,11 @@ namespace Components Utils::Hook::Set(0x5BAEA2, entryPool + 1); } + void AssetHandler::ExposeTemporaryAssets(bool expose) + { + AssetHandler::ShouldSearchTempAssets = expose; + } + AssetHandler::AssetHandler() { this->reallocateEntryPool(); @@ -525,6 +565,7 @@ namespace Components Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_RAWFILE, 2048); AssetHandler::RegisterInterface(new Assets::IFont_s()); + AssetHandler::RegisterInterface(new Assets::IWeapon()); AssetHandler::RegisterInterface(new Assets::IXModel()); AssetHandler::RegisterInterface(new Assets::IFxWorld()); AssetHandler::RegisterInterface(new Assets::IMapEnts()); @@ -535,8 +576,9 @@ namespace Components AssetHandler::RegisterInterface(new Assets::ISndCurve()); AssetHandler::RegisterInterface(new Assets::IMaterial()); AssetHandler::RegisterInterface(new Assets::IMenuList()); + AssetHandler::RegisterInterface(new Assets::IclipMap_t()); AssetHandler::RegisterInterface(new Assets::ImenuDef_t()); - AssetHandler::RegisterInterface(new Assets::IclipMap_t()); + AssetHandler::RegisterInterface(new Assets::ITracerDef()); AssetHandler::RegisterInterface(new Assets::IPhysPreset()); AssetHandler::RegisterInterface(new Assets::IXAnimParts()); AssetHandler::RegisterInterface(new Assets::IFxEffectDef()); diff --git a/src/Components/Modules/AssetHandler.hpp b/src/Components/Modules/AssetHandler.hpp index 71f9a5cf..38a253fe 100644 --- a/src/Components/Modules/AssetHandler.hpp +++ b/src/Components/Modules/AssetHandler.hpp @@ -39,8 +39,11 @@ namespace Components static void ResetBypassState(); + static void ExposeTemporaryAssets(bool expose); + private: static thread_local int BypassState; + static bool ShouldSearchTempAssets; static std::map TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT]; @@ -55,6 +58,7 @@ namespace Components static void RegisterInterface(IAsset* iAsset); static Game::XAssetHeader FindAsset(Game::XAssetType type, const char* filename); + static Game::XAssetHeader FindTemporaryAsset(Game::XAssetType type, const char* filename); static bool IsAssetEligible(Game::XAssetType type, Game::XAssetHeader* asset); static void FindAssetStub(); static void AddAssetStub(); @@ -76,6 +80,7 @@ namespace Components } #include "AssetInterfaces/IFont_s.hpp" +#include "AssetInterfaces/IWeapon.hpp" #include "AssetInterfaces/IXModel.hpp" #include "AssetInterfaces/IFxWorld.hpp" #include "AssetInterfaces/IMapEnts.hpp" @@ -86,8 +91,9 @@ namespace Components #include "AssetInterfaces/IMaterial.hpp" #include "AssetInterfaces/ISndCurve.hpp" #include "AssetInterfaces/IMenuList.hpp" -#include "AssetInterfaces/ImenuDef_t.hpp" #include "AssetInterfaces/IclipMap_t.hpp" +#include "AssetInterfaces/ImenuDef_t.hpp" +#include "AssetInterfaces/ITracerDef.hpp" #include "AssetInterfaces/IPhysPreset.hpp" #include "AssetInterfaces/IXAnimParts.hpp" #include "AssetInterfaces/IFxEffectDef.hpp" @@ -105,3 +111,4 @@ namespace Components #include "AssetInterfaces/IMaterialVertexShader.hpp" #include "AssetInterfaces/IStructuredDataDefSet.hpp" #include "AssetInterfaces/IMaterialVertexDeclaration.hpp" + diff --git a/src/Components/Modules/AssetInterfaces/ITracerDef.cpp b/src/Components/Modules/AssetInterfaces/ITracerDef.cpp new file mode 100644 index 00000000..92bcf361 --- /dev/null +++ b/src/Components/Modules/AssetInterfaces/ITracerDef.cpp @@ -0,0 +1,44 @@ +#include "STDInclude.hpp" + +namespace Assets +{ + void ITracerDef::load(Game::XAssetHeader* /*header*/, const std::string& /*name*/, Components::ZoneBuilder::Zone* /*builder*/) + { + return; // don't load from filesystem right now + } + + void ITracerDef::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::TracerDef* asset = header.tracerDef; + + if (asset->material) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->material); + } + } + + void ITracerDef::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::TracerDef, 0x70); + + Utils::Stream* buffer = builder->getBuffer(); + Game::TracerDef* asset = header.tracerDef; + Game::TracerDef* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->material) + { + dest->material = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->material).material; + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/ITracerDef.hpp b/src/Components/Modules/AssetInterfaces/ITracerDef.hpp new file mode 100644 index 00000000..cb78484b --- /dev/null +++ b/src/Components/Modules/AssetInterfaces/ITracerDef.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace Assets +{ + class ITracerDef : public Components::AssetHandler::IAsset + { + public: + virtual Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_TRACER; }; + + virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; + virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; + virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; + }; +} diff --git a/src/Components/Modules/AssetInterfaces/IWeapon.cpp b/src/Components/Modules/AssetInterfaces/IWeapon.cpp new file mode 100644 index 00000000..92135700 --- /dev/null +++ b/src/Components/Modules/AssetInterfaces/IWeapon.cpp @@ -0,0 +1,702 @@ +#include "STDInclude.hpp" + +namespace Assets +{ + void IWeapon::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) + { + // Try loading raw weapon + if (Components::FileSystem::File(Utils::String::VA("weapons/mp/%s", name.data())).exists()) + { + // let the function see temporary assets when calling DB_FindXAssetHeader during the loading function + // otherwise it fails to link things properly + Components::AssetHandler::ExposeTemporaryAssets(true); + header->data = Game::BG_LoadWeaponDef_LoadObj(name.data()); + Components::AssetHandler::ExposeTemporaryAssets(false); + } + } + + void IWeapon::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::WeaponCompleteDef* asset = header.weapon; + + // convert all script strings + if (asset->hideTags) + { + for (char i = 0; i < 32; ++i) + { + if (asset->hideTags[i] == NULL) break; // no more strings + builder->addScriptString(asset->hideTags[i]); + } + } + + if (asset->weapDef->notetrackSoundMapKeys) + { + for (char i = 0; i < 16; ++i) + { + if (asset->weapDef->notetrackSoundMapKeys[i] == NULL) break; // no more strings + builder->addScriptString(asset->weapDef->notetrackSoundMapKeys[i]); + } + } + + if (asset->weapDef->notetrackSoundMapValues) + { + for (char i = 0; i < 16; ++i) + { + if (asset->weapDef->notetrackSoundMapValues[i] == NULL) break; // no more strings + builder->addScriptString(asset->weapDef->notetrackSoundMapValues[i]); + } + } + + if (asset->weapDef->notetrackRumbleMapKeys) + { + for (char i = 0; i < 16; ++i) + { + if (asset->weapDef->notetrackRumbleMapKeys[i] == NULL) break; // no more strings + builder->addScriptString(asset->weapDef->notetrackRumbleMapKeys[i]); + } + } + + if (asset->weapDef->notetrackRumbleMapValues) + { + for (char i = 0; i < 16; ++i) + { + if (asset->weapDef->notetrackRumbleMapValues[i] == NULL) break; // no more strings + builder->addScriptString(asset->weapDef->notetrackRumbleMapValues[i]); + } + } + + + // now load all sub-assets properly + if (asset->killIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->killIcon); + if (asset->dpadIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->dpadIcon); + if (asset->weapDef->reticleCenter) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->reticleCenter); + if (asset->weapDef->reticleSide) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->reticleSide); + if (asset->weapDef->hudIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->hudIcon); + if (asset->weapDef->pickupIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->pickupIcon); + if (asset->weapDef->ammoCounterIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->ammoCounterIcon); + if (asset->weapDef->overlayMaterial) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->overlayMaterial); + if (asset->weapDef->overlayMaterialLowRes) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->overlayMaterialLowRes); + if (asset->weapDef->overlayMaterialEMP) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->overlayMaterialEMP); + if (asset->weapDef->overlayMaterialEMPLowRes) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->overlayMaterialEMPLowRes); + + if (asset->weapDef->gunXModel) + { + for (int i = 0; i < 16; i++) + { + if (asset->weapDef->gunXModel[i]) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->gunXModel[i]); + } + } + + if (asset->weapDef->handXModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->handXModel); + + if (asset->weapDef->worldModel) + { + for (int i = 0; i < 16; i++) + { + if (asset->weapDef->worldModel[i]) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->worldModel[i]); + } + } + + if (asset->weapDef->worldClipModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->worldClipModel); + if (asset->weapDef->rocketModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->rocketModel); + if (asset->weapDef->knifeModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->knifeModel); + if (asset->weapDef->worldKnifeModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->worldKnifeModel); + if (asset->weapDef->projectileModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->projectileModel); + + if (asset->weapDef->physCollmap) builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, asset->weapDef->physCollmap); + + if (asset->weapDef->tracerType) builder->loadAsset(Game::XAssetType::ASSET_TYPE_TRACER, asset->weapDef->tracerType); + + if (asset->weapDef->viewFlashEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->viewFlashEffect); + if (asset->weapDef->worldFlashEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->worldFlashEffect); + if (asset->weapDef->viewShellEjectEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->viewShellEjectEffect); + if (asset->weapDef->worldShellEjectEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->worldShellEjectEffect); + if (asset->weapDef->viewLastShotEjectEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->viewLastShotEjectEffect); + if (asset->weapDef->worldLastShotEjectEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->worldLastShotEjectEffect); + if (asset->weapDef->projExplosionEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projExplosionEffect); + if (asset->weapDef->projDudEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projDudEffect); + if (asset->weapDef->projTrailEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projTrailEffect); + if (asset->weapDef->projBeaconEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projBeaconEffect); + if (asset->weapDef->projIgnitionEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projIgnitionEffect); + if (asset->weapDef->turretOverheatEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->turretOverheatEffect); + } + + void IWeapon::writeWeaponDef(Game::WeaponDef* def, Components::ZoneBuilder::Zone* builder, Utils::Stream* buffer) + { + AssertSize(Game::WeaponDef, 0x684); + + Game::WeaponDef* dest = buffer->dest(); + buffer->save(def); + + if (def->szOverlayName) + { + buffer->saveString(def->szOverlayName); + Utils::Stream::ClearPointer(&dest->szOverlayName); + } + + if (def->gunXModel) + { + buffer->align(Utils::Stream::ALIGN_4); + Game::XModel** pointerTable = buffer->dest(); + buffer->saveMax(16 * sizeof(Game::XModel*)); + for (int i = 0; i < 16; i++) + { + if (!def->gunXModel[i]) + { + pointerTable[i] = NULL; + continue; + } + pointerTable[i] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->gunXModel[i]).model; + } + Utils::Stream::ClearPointer(&dest->gunXModel); + } + + if (def->handXModel) + { + dest->handXModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->handXModel).model; + } + + if (def->szXAnimsRightHanded) + { + buffer->align(Utils::Stream::ALIGN_4); + int* poinerTable = buffer->dest(); + buffer->saveMax(37 * sizeof(char*)); // array of 37 string pointers + for (int i = 0; i < 37; i++) + { + if (!def->szXAnimsRightHanded[i]) { + poinerTable[i] = 0; // clear poiner if there isn't a string here + continue; + } + + // save string if it is present + buffer->saveString(def->szXAnimsRightHanded[i]); + } + + Utils::Stream::ClearPointer(&dest->szXAnimsRightHanded); + } + + if (def->szXAnimsLeftHanded) + { + buffer->align(Utils::Stream::ALIGN_4); + int* poinerTable = buffer->dest(); + buffer->saveMax(37 * sizeof(char*)); // array of 37 string pointers + for (int i = 0; i < 37; i++) + { + if (!def->szXAnimsLeftHanded[i]) { + poinerTable[i] = 0; // clear poiner if there isn't a string here + continue; + } + + // save string if it is present + buffer->saveString(def->szXAnimsLeftHanded[i]); + } + + Utils::Stream::ClearPointer(&dest->szXAnimsLeftHanded); + } + + if (def->szModeName) + { + buffer->saveString(def->szModeName); + Utils::Stream::ClearPointer(&dest->szModeName); + } + + if (def->notetrackSoundMapKeys) + { + buffer->align(Utils::Stream::ALIGN_2); + unsigned short* scriptStringTable = buffer->dest(); + buffer->saveArray(def->notetrackSoundMapKeys, 16); + for (int i = 0; i < 16; i++) { + builder->mapScriptString(&scriptStringTable[i]); + } + + Utils::Stream::ClearPointer(&dest->notetrackSoundMapKeys); + } + + if (def->notetrackSoundMapValues) + { + buffer->align(Utils::Stream::ALIGN_2); + unsigned short* scriptStringTable = buffer->dest(); + buffer->saveArray(def->notetrackSoundMapValues, 16); + for (int i = 0; i < 16; i++) { + builder->mapScriptString(&scriptStringTable[i]); + } + + Utils::Stream::ClearPointer(&dest->notetrackSoundMapValues); + } + + if (def->notetrackRumbleMapKeys) + { + buffer->align(Utils::Stream::ALIGN_2); + unsigned short* scriptStringTable = buffer->dest(); + buffer->saveArray(def->notetrackRumbleMapKeys, 16); + for (int i = 0; i < 16; i++) { + builder->mapScriptString(&scriptStringTable[i]); + } + + Utils::Stream::ClearPointer(&dest->notetrackRumbleMapKeys); + } + + if (def->notetrackRumbleMapValues) + { + buffer->align(Utils::Stream::ALIGN_2); + unsigned short* scriptStringTable = buffer->dest(); + buffer->saveArray(def->notetrackRumbleMapValues, 16); + for (int i = 0; i < 16; i++) { + builder->mapScriptString(&scriptStringTable[i]); + } + + Utils::Stream::ClearPointer(&dest->notetrackRumbleMapValues); + } + + if (def->viewFlashEffect) + { + dest->viewFlashEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->viewFlashEffect).fx; + } + + if (def->worldFlashEffect) + { + dest->worldFlashEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->worldFlashEffect).fx; + } + + // This is compressed because I don't want to write the same piece of code 47 times + // TODO: verify that this is saving the aliases correctly because the old code looks wrong and this looks right but the old code worked so go figure + Game::snd_alias_list_t ** allSounds = &def->pickupSound; + Game::snd_alias_list_t ** allSoundsDest = &dest->pickupSound; + for (int i = 0; i < 47; i++) { + if (!allSounds[i]) continue; + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveMax(sizeof(Game::snd_alias_list_t*)); + buffer->saveString(allSounds[i]->aliasName); + Utils::Stream::ClearPointer(&allSoundsDest[i]); + } + + if (def->bounceSound) + { + buffer->align(Utils::Stream::ALIGN_4); + int* ptrs = buffer->dest(); + buffer->saveMax(37 * sizeof(Game::snd_alias_list_t*)); + + for (int i = 0; i < 37; i++) + { + if (!def->bounceSound[i]) + { + ptrs[i] = 0; + continue; + } + + buffer->saveMax(sizeof(Game::snd_alias_list_t*)); + buffer->saveString(def->bounceSound[i]->aliasName); + } + + Utils::Stream::ClearPointer(&dest->bounceSound); + } + + if (def->viewShellEjectEffect) + { + dest->viewShellEjectEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->viewShellEjectEffect).fx; + } + + if (def->worldShellEjectEffect) + { + dest->worldShellEjectEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->worldShellEjectEffect).fx; + } + + if (def->viewLastShotEjectEffect) + { + dest->viewLastShotEjectEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->viewLastShotEjectEffect).fx; + } + + if (def->worldLastShotEjectEffect) + { + dest->worldLastShotEjectEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->worldLastShotEjectEffect).fx; + } + + if (def->reticleCenter) + { + dest->reticleCenter = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->reticleCenter).material; + } + + if (def->reticleSide) + { + dest->reticleSide = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->reticleSide).material; + } + + if (def->worldModel) + { + buffer->align(Utils::Stream::ALIGN_4); + Game::XModel** pointerTable = buffer->dest(); + buffer->saveMax(16 * sizeof(Game::XModel*)); + for (int i = 0; i < 16; i++) + { + if (!def->worldModel[i]) + { + pointerTable[i] = NULL; + continue; + } + pointerTable[i] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->worldModel[i]).model; + } + Utils::Stream::ClearPointer(&dest->worldModel); + } + + if (def->worldClipModel) + { + dest->worldClipModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->worldClipModel).model; + } + + if (def->rocketModel) + { + dest->rocketModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->rocketModel).model; + } + + if (def->knifeModel) + { + dest->knifeModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->knifeModel).model; + } + + if (def->worldKnifeModel) + { + dest->worldKnifeModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->worldKnifeModel).model; + } + + if (def->hudIcon) + { + dest->hudIcon = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->hudIcon).material; + } + + if (def->pickupIcon) + { + dest->pickupIcon = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->pickupIcon).material; + } + + if (def->ammoCounterIcon) + { + dest->ammoCounterIcon = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->ammoCounterIcon).material; + } + + if (def->szAmmoName) + { + buffer->saveString(def->szAmmoName); + Utils::Stream::ClearPointer(&dest->szAmmoName); + } + + if (def->szClipName) + { + buffer->saveString(def->szClipName); + Utils::Stream::ClearPointer(&dest->szClipName); + } + + if (def->szSharedAmmoCapName) + { + buffer->saveString(def->szSharedAmmoCapName); + Utils::Stream::ClearPointer(&dest->szSharedAmmoCapName); + } + + if (def->overlayMaterial) + { + dest->overlayMaterial = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->overlayMaterial).material; + } + + if (def->overlayMaterialLowRes) + { + dest->overlayMaterialLowRes = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->overlayMaterialLowRes).material; + } + + if (def->overlayMaterialEMP) + { + dest->overlayMaterialEMP = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->overlayMaterialEMP).material; + } + + if (def->overlayMaterialEMPLowRes) + { + dest->overlayMaterialEMPLowRes = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->overlayMaterialEMPLowRes).material; + } + + if (def->physCollmap) + { + dest->physCollmap = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, def->overlayMaterialEMPLowRes).physCollmap; + } + + if (def->projectileModel) + { + dest->projectileModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->projectileModel).model; + } + + if (def->projExplosionEffect) + { + dest->projExplosionEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->projExplosionEffect).fx; + } + + if (def->projDudEffect) + { + dest->projDudEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->projDudEffect).fx; + } + + if (def->projExplosionSound) + { + buffer->saveMax(4); + buffer->saveString(def->projExplosionSound->aliasName); + Utils::Stream::ClearPointer(&dest->projExplosionSound); + } + + if (def->projDudSound) + { + buffer->saveMax(4); + buffer->saveString(def->projDudSound->aliasName); + Utils::Stream::ClearPointer(&dest->projDudSound); + } + + if (def->parallelBounce) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(def->parallelBounce, 31); + Utils::Stream::ClearPointer(&dest->parallelBounce); + } + + if (def->perpendicularBounce) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(def->perpendicularBounce, 31); + Utils::Stream::ClearPointer(&dest->perpendicularBounce); + } + + if (def->projTrailEffect) + { + dest->projTrailEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->projTrailEffect).fx; + } + + if (def->projBeaconEffect) + { + dest->projBeaconEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->projBeaconEffect).fx; + } + + if (def->projIgnitionEffect) + { + dest->projIgnitionEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->projIgnitionEffect).fx; + } + + if (def->projIgnitionSound) + { + buffer->saveMax(4); + buffer->saveString(def->projIgnitionSound->aliasName); + Utils::Stream::ClearPointer(&dest->projIgnitionSound); + } + + if (def->accuracyGraphName[0]) + { + buffer->saveString(def->accuracyGraphName[0]); + Utils::Stream::ClearPointer(&dest->accuracyGraphName[0]); + } + + if (def->originalAccuracyGraphKnots[0]) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(def->originalAccuracyGraphKnots[0], def->originalAccuracyGraphKnotCount[0]); + Utils::Stream::ClearPointer(&dest->originalAccuracyGraphKnots[0]); + } + + if (def->accuracyGraphName[1]) + { + buffer->saveString(def->accuracyGraphName[1]); + Utils::Stream::ClearPointer(&dest->accuracyGraphName[1]); + } + + if (def->originalAccuracyGraphKnots[1]) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(def->originalAccuracyGraphKnots[1], def->originalAccuracyGraphKnotCount[1]); + Utils::Stream::ClearPointer(&dest->originalAccuracyGraphKnots[1]); + } + + if (def->szUseHintString) + { + buffer->saveString(def->szUseHintString); + Utils::Stream::ClearPointer(&dest->szUseHintString); + } + + if (def->dropHintString) + { + buffer->saveString(def->dropHintString); + Utils::Stream::ClearPointer(&dest->dropHintString); + } + + if (def->szScript) + { + buffer->saveString(def->szScript); + Utils::Stream::ClearPointer(&dest->szScript); + } + + if (def->locationDamageMultipliers) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(def->locationDamageMultipliers, 20); + Utils::Stream::ClearPointer(&dest->locationDamageMultipliers); + } + + if (def->fireRumble) + { + buffer->saveString(def->fireRumble); + Utils::Stream::ClearPointer(&dest->fireRumble); + } + + if (def->meleeImpactRumble) + { + buffer->saveString(def->meleeImpactRumble); + Utils::Stream::ClearPointer(&dest->meleeImpactRumble); + } + + if (def->tracerType) + { + dest->tracerType = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_TRACER, def->tracerType).tracerDef; + } + + if (def->turretOverheatSound) + { + buffer->saveMax(4); + buffer->saveString(def->turretOverheatSound->aliasName); + Utils::Stream::ClearPointer(&dest->turretOverheatSound); + } + + if (def->turretOverheatEffect) + { + dest->turretOverheatEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->turretOverheatEffect).fx; + } + + if (def->turretBarrelSpinRumble) + { + buffer->saveString(def->turretBarrelSpinRumble); + Utils::Stream::ClearPointer(&dest->turretBarrelSpinRumble); + } + + if (def->turretBarrelSpinMaxSnd) + { + buffer->saveMax(4); + buffer->saveString(def->turretBarrelSpinMaxSnd->aliasName); + Utils::Stream::ClearPointer(&dest->turretBarrelSpinMaxSnd); + } + + for (int i = 0; i < 4; i++) { + if (!def->turretBarrelSpinUpSnd[i]) continue; + + buffer->saveMax(4); + buffer->saveString(def->turretBarrelSpinUpSnd[i]->aliasName); + Utils::Stream::ClearPointer(&dest->turretBarrelSpinUpSnd[i]); + } + + for (int i = 0; i < 4; i++) { + if (!def->turretBarrelSpinDownSnd[i]) continue; + + buffer->saveMax(4); + buffer->saveString(def->turretBarrelSpinDownSnd[i]->aliasName); + Utils::Stream::ClearPointer(&dest->turretBarrelSpinDownSnd[i]); + } + + if (def->missileConeSoundAlias) + { + buffer->saveMax(4); + buffer->saveString(def->missileConeSoundAlias->aliasName); + Utils::Stream::ClearPointer(&dest->missileConeSoundAlias); + } + + if (def->missileConeSoundAliasAtBase) + { + buffer->saveMax(4); + buffer->saveString(def->missileConeSoundAliasAtBase->aliasName); + Utils::Stream::ClearPointer(&dest->missileConeSoundAliasAtBase); + } + } + + void IWeapon::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::WeaponCompleteDef, 0x74); + + Utils::Stream* buffer = builder->getBuffer(); + Game::WeaponCompleteDef* asset = header.weapon; + Game::WeaponCompleteDef* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->szInternalName) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->szInternalName)); + Utils::Stream::ClearPointer(&dest->szInternalName); + } + + if (asset->weapDef) + { + buffer->align(Utils::Stream::ALIGN_4); + IWeapon::writeWeaponDef(asset->weapDef, builder, buffer); + + Utils::Stream::ClearPointer(&dest->weapDef); + } + + if (asset->szDisplayName) + { + buffer->saveString(asset->szDisplayName); + Utils::Stream::ClearPointer(&dest->szDisplayName); + } + + if (asset->hideTags) + { + buffer->align(Utils::Stream::ALIGN_2); + unsigned short* scriptStringTable = buffer->dest(); + buffer->saveArray(asset->hideTags, 32); + for (int i = 0; i < 32; i++) { + builder->mapScriptString(&scriptStringTable[i]); + } + + Utils::Stream::ClearPointer(&dest->hideTags); + } + + if (asset->szXAnims) + { + buffer->align(Utils::Stream::ALIGN_4); + int* poinerTable = buffer->dest(); + buffer->saveMax(37 * sizeof(char*)); // array of 37 string pointers + for (int i = 0; i < 37; i++) + { + if (!asset->szXAnims[i]) { + poinerTable[i] = 0; // clear poiner if there isn't a string here + continue; + } + + // save string if it is present + buffer->saveString(asset->szXAnims[i]); + } + + Utils::Stream::ClearPointer(&dest->szXAnims); + } + + if (asset->szAltWeaponName) + { + buffer->saveString(asset->szAltWeaponName); + Utils::Stream::ClearPointer(&dest->szAltWeaponName); + } + + if (asset->killIcon) + { + dest->killIcon = builder->saveSubAsset(Game::ASSET_TYPE_MATERIAL, asset->killIcon).material; + } + + if (asset->dpadIcon) + { + dest->dpadIcon = builder->saveSubAsset(Game::ASSET_TYPE_MATERIAL, asset->dpadIcon).material; + } + + if (asset->accuracyGraphKnots[0]) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->accuracyGraphKnots[0], asset->accuracyGraphKnotCount[0]); + Utils::Stream::ClearPointer(&dest->accuracyGraphKnots[0]); + } + + if (asset->accuracyGraphKnots[1]) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->accuracyGraphKnots[1], asset->accuracyGraphKnotCount[1]); + Utils::Stream::ClearPointer(&dest->accuracyGraphKnots[1]); + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IWeapon.hpp b/src/Components/Modules/AssetInterfaces/IWeapon.hpp new file mode 100644 index 00000000..82c38085 --- /dev/null +++ b/src/Components/Modules/AssetInterfaces/IWeapon.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace Assets +{ + class IWeapon : public Components::AssetHandler::IAsset + { + public: + virtual Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_WEAPON; }; + + virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; + virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; + virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; + + private: + void writeWeaponDef(Game::WeaponDef* def, Components::ZoneBuilder::Zone* builder, Utils::Stream* buffer); + }; +} diff --git a/src/Components/Modules/Exception.hpp b/src/Components/Modules/Exception.hpp index 2038842a..0b887af8 100644 --- a/src/Components/Modules/Exception.hpp +++ b/src/Components/Modules/Exception.hpp @@ -18,6 +18,7 @@ namespace Components static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter); static __declspec(noreturn) void ErrorLongJmp(jmp_buf _Buf, int _Value); static __declspec(noreturn) void LongJmp(jmp_buf _Buf, int _Value); + static void DebugMinidumpCommand(Command::Params*); static int MiniDumpType; static Utils::Hook SetFilterHook; diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index ece47840..6cd757c8 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -410,6 +410,8 @@ namespace Components } #endif + Utils::IO::WriteFile("uncompressed", zoneBuffer); + zoneBuffer = Utils::Compression::ZLib::Compress(zoneBuffer); outBuffer.append(zoneBuffer); @@ -704,21 +706,20 @@ namespace Components if (zoneIndex > 0) { - Game::DB_EnumXAssetEntries(type, [&](Game::XAssetEntry* entry) - { - 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]()); + Game::XAssetEntry* entry = Game::DB_FindXAssetEntry(type, name.data()); - // Set the name to the original name, so it can be stored - Game::DB_SetXAssetNameHandlers[type](&header, name.data()); - AssetHandler::StoreTemporaryAsset(type, header); + if (entry->zoneIndex == zoneIndex) + { + // Allocate an empty asset (filled with zeros) + header.data = builder->getAllocator()->allocate(Game::DB_GetXAssetSizeHandlers[type]()); - // Set the name to the empty name - Game::DB_SetXAssetNameHandlers[type](&header, builder->getAllocator()->duplicateString("," + name)); - } - }, true, true); + // 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)); + } } } @@ -1109,12 +1110,29 @@ namespace Components if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current()) { ZoneBuilder::TraceAssets.push_back({ type, name }); + OutputDebugStringA((name + "\n").data()); } }); Command::Add("verifyzone", [](Command::Params* params) { if (params->length() < 2) return; + /* + Utils::Hook(0x4AE9C2, [] { + Game::WeaponCompleteDef** varPtr = (Game::WeaponCompleteDef**)0x112A9F4; + Game::WeaponCompleteDef* var = *varPtr; + OutputDebugStringA(""); + Utils::Hook::Call(0x4D1D60)(); // DB_PopStreamPos + }, HOOK_JUMP).install()->quick(); + + + Utils::Hook(0x4AE9B4, [] { + Game::WeaponCompleteDef** varPtr = (Game::WeaponCompleteDef**)0x112A9F4; + Game::WeaponCompleteDef* var = *varPtr; + OutputDebugStringA(""); + Utils::Hook::Call(0x4D1D60)(); // DB_PopStreamPos + }, HOOK_JUMP).install()->quick(); + */ std::string zone = params->get(1);