From e097f04958d5b5ed836240a5de741e99b578e2d5 Mon Sep 17 00:00:00 2001 From: FynnTW Date: Thu, 10 Oct 2024 12:45:16 +0300 Subject: [PATCH] handle model loading and unloading --- M2TWEOP Code/M2TWEOP library/Injects.cpp | 53 ++++++++++++ M2TWEOP Code/M2TWEOP library/Injects.h | 24 ++++++ M2TWEOP Code/M2TWEOP library/dataOffsets.cpp | 2 + M2TWEOP Code/M2TWEOP library/dataOffsets.h | 1 + .../M2TWEOP library/functionsOffsets.cpp | 2 + .../M2TWEOP library/functionsOffsets.h | 1 + M2TWEOP Code/M2TWEOP library/managerF.cpp | 13 +++ .../M2TWEOP library/patchesForGame.cpp | 10 +++ M2TWEOP Code/M2TWEOP library/patchesForGame.h | 2 + .../M2TWEOP library/stratModelsChange.cpp | 80 ++++++++++++------- .../M2TWEOP library/stratModelsChange.h | 4 +- 11 files changed, 164 insertions(+), 28 deletions(-) diff --git a/M2TWEOP Code/M2TWEOP library/Injects.cpp b/M2TWEOP Code/M2TWEOP library/Injects.cpp index 49ea059c..aa2e0682 100644 --- a/M2TWEOP Code/M2TWEOP library/Injects.cpp +++ b/M2TWEOP Code/M2TWEOP library/Injects.cpp @@ -4906,6 +4906,59 @@ void onLoadBuilding::SetNewCode() delete a; } +onLoadModelRigid::onLoadModelRigid(MemWork* mem, LPVOID addr, int ver) + :AATemplate(mem), funcAddress(addr) +{ + if (ver == 2)//steam + m_adress = 0xA04FEE; + + else if (ver == 1)//kingdoms + m_adress = 0xA0446E; +} + +void onLoadModelRigid::SetNewCode() +{ + const auto a = new Assembler(); + a->push(eax); + a->push(ecx); + a->push(edx); + a->mov(ecx, esi); + a->mov(eax, reinterpret_cast(funcAddress)); + a->call(eax); + a->pop(edx); + a->pop(ecx); + a->pop(eax); + a->ret(); + m_cheatBytes = static_cast(a->make()); + delete a; +} + +onUnloadModels::onUnloadModels(MemWork* mem, LPVOID addr, int ver) + :AATemplate(mem), funcAddress(addr) +{ + if (ver == 2)//steam + m_adress = 0xA0506C; + + else if (ver == 1)//kingdoms + m_adress = 0xA044EC; +} + +void onUnloadModels::SetNewCode() +{ + const auto a = new Assembler(); + a->push(ecx); + a->push(edx); + a->mov(eax, reinterpret_cast(funcAddress)); + a->call(eax); + a->mov(eax, 0); + a->test(eax, eax); + a->pop(edx); + a->pop(ecx); + a->ret(); + m_cheatBytes = static_cast(a->make()); + delete a; +} + onOffMapModelThing::onOffMapModelThing(MemWork* mem, LPVOID addr, int ver) :AATemplate(mem), funcAddress(addr) { diff --git a/M2TWEOP Code/M2TWEOP library/Injects.h b/M2TWEOP Code/M2TWEOP library/Injects.h index 9eb0f22f..7abf963b 100644 --- a/M2TWEOP Code/M2TWEOP library/Injects.h +++ b/M2TWEOP Code/M2TWEOP library/Injects.h @@ -2093,6 +2093,30 @@ class onHiddenResourceCheck LPVOID funcAddress; }; +class onLoadModelRigid + :public AATemplate +{ +public: + onLoadModelRigid(MemWork* mem, LPVOID addr, int ver); + ~onLoadModelRigid() = default; + + void SetNewCode(); +private: + LPVOID funcAddress; +}; + +class onUnloadModels + :public AATemplate +{ +public: + onUnloadModels(MemWork* mem, LPVOID addr, int ver); + ~onUnloadModels() = default; + + void SetNewCode(); +private: + LPVOID funcAddress; +}; + class onLoadBuilding :public AATemplate { diff --git a/M2TWEOP Code/M2TWEOP library/dataOffsets.cpp b/M2TWEOP Code/M2TWEOP library/dataOffsets.cpp index 92c55027..32bfd372 100644 --- a/M2TWEOP Code/M2TWEOP library/dataOffsets.cpp +++ b/M2TWEOP Code/M2TWEOP library/dataOffsets.cpp @@ -98,6 +98,7 @@ void dataOffsets::initDataOffsets(int gameVer) offsets.currentRegion = 0x18FD970; offsets.traitDb = 0x1666F90; offsets.ancillaryDb = 0x1666F40; + offsets.modelRigidCounts = 0x1B5EFE4; offsets.fortVtbl = 0x13362F4; @@ -200,6 +201,7 @@ void dataOffsets::initDataOffsets(int gameVer) offsets.currentRegion = 0x18B4800; offsets.uiStratUiV2 = 0x2C6D1B0; offsets.isDLC = 0x016A284C; + offsets.modelRigidCounts = 0x1B15EB4; offsets.audioEnable = reinterpret_cast(0x01639f1d); offsets.audioMaster_vol = reinterpret_cast(0x01639f60); diff --git a/M2TWEOP Code/M2TWEOP library/dataOffsets.h b/M2TWEOP Code/M2TWEOP library/dataOffsets.h index ddaa6b3b..5b85a0b7 100644 --- a/M2TWEOP Code/M2TWEOP library/dataOffsets.h +++ b/M2TWEOP Code/M2TWEOP library/dataOffsets.h @@ -38,6 +38,7 @@ class dataOffsets DWORD buildingChainLimit = NULL; DWORD guildCooldown = NULL; DWORD isDLC = NULL; + DWORD modelRigidCounts = NULL; DWORD unitTypesStart = NULL; diff --git a/M2TWEOP Code/M2TWEOP library/functionsOffsets.cpp b/M2TWEOP Code/M2TWEOP library/functionsOffsets.cpp index 0abb39f5..b2ca972c 100644 --- a/M2TWEOP Code/M2TWEOP library/functionsOffsets.cpp +++ b/M2TWEOP Code/M2TWEOP library/functionsOffsets.cpp @@ -330,6 +330,7 @@ void codes::initCodes(int gameVer) offsets.buildQueueConflictTest = 0x5EB100; offsets.removeBuildingSelection = 0x5FA3A0; offsets.getNextWord = 0xD4F060; + offsets.modelRigidDestructor = 0x933640; } @@ -662,5 +663,6 @@ void codes::initCodes(int gameVer) offsets.buildQueueConflictTest = 0x005EB560; offsets.removeBuildingSelection = 0x005FA790; offsets.getNextWord = 0xD493C0; + offsets.modelRigidDestructor = 0x934110; } } \ No newline at end of file diff --git a/M2TWEOP Code/M2TWEOP library/functionsOffsets.h b/M2TWEOP Code/M2TWEOP library/functionsOffsets.h index c38e828d..ef39d663 100644 --- a/M2TWEOP Code/M2TWEOP library/functionsOffsets.h +++ b/M2TWEOP Code/M2TWEOP library/functionsOffsets.h @@ -207,6 +207,7 @@ class codes DWORD switchCharacterFaction = NULL; DWORD switchNamedCharacterFaction = NULL; DWORD switchArmyFaction = NULL; + DWORD modelRigidDestructor = NULL; DWORD changeCharacterTileStuff = NULL; DWORD initPlaceCharacter = NULL; DWORD getNextWord = NULL; diff --git a/M2TWEOP Code/M2TWEOP library/managerF.cpp b/M2TWEOP Code/M2TWEOP library/managerF.cpp index 12bbd57b..a874be29 100644 --- a/M2TWEOP Code/M2TWEOP library/managerF.cpp +++ b/M2TWEOP Code/M2TWEOP library/managerF.cpp @@ -711,6 +711,19 @@ void managerF::execPatches() toLoadBuilding->Enable(); f1 << "Done" << '\n'; + f1 << "Start applying onLoadModelRigid patch" << '\n'; + onLoadModelRigid* toLoadModelRigid = new onLoadModelRigid(mem, (LPVOID)patchesForGame::onLoadModelRigid, globals::dataS.gameVersion); + toLoadModelRigid->SetNewCode(); + toLoadModelRigid->Enable(); + f1 << "Done" << '\n'; + + + f1 << "Start applying onUnloadModels patch" << '\n'; + onUnloadModels* toUnloadModels = new onUnloadModels(mem, (LPVOID)patchesForGame::onUnloadModels, globals::dataS.gameVersion); + toUnloadModels->SetNewCode(); + toUnloadModels->Enable(); + f1 << "Done" << '\n'; + f1 << "Start applying onOffMapModelThing patch" << '\n'; onOffMapModelThing* toOffMapModelThing = new onOffMapModelThing(mem, (LPVOID)patchesForGame::onOffMapModelThing, globals::dataS.gameVersion); toOffMapModelThing->SetNewCode(); diff --git a/M2TWEOP Code/M2TWEOP library/patchesForGame.cpp b/M2TWEOP Code/M2TWEOP library/patchesForGame.cpp index fab8b632..3772b500 100644 --- a/M2TWEOP Code/M2TWEOP library/patchesForGame.cpp +++ b/M2TWEOP Code/M2TWEOP library/patchesForGame.cpp @@ -914,6 +914,16 @@ void patchesForGame::onRemoveFromUnitQueue(const unitRQ* queue, const int index) gameEvents::onRemoveFromUnitQueue(item); } +void patchesForGame::onLoadModelRigid(model_Rigid* model) +{ + stratModelsChange::pushGameModel(model); +} + +void patchesForGame::onUnloadModels() +{ + stratModelsChange::clearModels(); +} + building* patchesForGame::onGetBuildingById(const settlementBuildings* buildings, const int index) { if (index < 0) diff --git a/M2TWEOP Code/M2TWEOP library/patchesForGame.h b/M2TWEOP Code/M2TWEOP library/patchesForGame.h index 6c4c9f6f..3bd480ce 100644 --- a/M2TWEOP Code/M2TWEOP library/patchesForGame.h +++ b/M2TWEOP Code/M2TWEOP library/patchesForGame.h @@ -55,6 +55,8 @@ class patchesForGame static int __fastcall onCanWithdrawPreBattle(const settlementStruct* settlement); static int __fastcall onCalculateCommand(const characterRecord* general); static void __fastcall onRemoveFromUnitQueue(const unitRQ* queue, int index); + static void __fastcall onLoadModelRigid(model_Rigid* model); + static void __fastcall onUnloadModels(); static building* __fastcall onGetBuildingById(const settlementBuildings* buildings, int index); static int __fastcall onCheckSettHasBuilding(const settlementBuildings* buildings, int index); static void __fastcall getPossibleConstructions(exportDescrBuildings* edb, settlementStruct* sett, void* data, void* caps, void* bonus, bool checkQueue, bool forceTemple); diff --git a/M2TWEOP Code/M2TWEOP library/stratModelsChange.cpp b/M2TWEOP Code/M2TWEOP library/stratModelsChange.cpp index b9a835d7..84b7c04a 100644 --- a/M2TWEOP Code/M2TWEOP library/stratModelsChange.cpp +++ b/M2TWEOP Code/M2TWEOP library/stratModelsChange.cpp @@ -15,21 +15,28 @@ namespace stratModelsChange needFixHiding = 1, needChange = 2 }; - modelsChangeStatus changeModelsNeededNow = modelsChangeStatus::changed; + modelsChangeStatus CHANGE_MODELS_NEEDED_NOW = modelsChangeStatus::changed; struct stratModelCharacterRecord { const char* modelId; - const char* skeletonname; - const char* caspath; - const char* shadowcaspath; - const char* texturepath; + const char* skeletonName; + const char* casPath; + const char* shadowCasPath; + const char* texturePath; float scale; stratModelArrayEntry* entry; }; - mapstratModels; - vectorcharacterStratModels; + mapSTRAT_MODELS; + vectorCHARACTER_STRAT_MODELS; + + std::vector GAME_MODELS{}; + + void pushGameModel(model_Rigid* model) + { + GAME_MODELS.push_back(model); + } void addModelToGame(const std::string& path, const UINT32 modelId, const bool isSettlement) { @@ -37,7 +44,7 @@ namespace stratModelsChange modRec->path = path; modRec->modelId = modelId; modRec->isSettlement = isSettlement; - stratModels[modelId] = modRec; + STRAT_MODELS[modelId] = modRec; } void addModelToGameNoBool(const std::string& path, const UINT32 modelId) @@ -68,7 +75,7 @@ namespace stratModelsChange void setModel(const int x, const int y, const UINT32 modelId, const UINT32 modelId2) { - if (stratModels.find(modelId) == stratModels.end()) + if (STRAT_MODELS.find(modelId) == STRAT_MODELS.end()) return; for (size_t i = 0; i < stratModelChangeList.size(); i++) { @@ -97,7 +104,7 @@ namespace stratModelsChange eopSettlementDataDb::get()->getSettlementData(tile->regionId, sett->minorSettlementIndex)->modelId = modelId; } stratModelChangeList.push_back(rec); - changeModelsNeededNow = modelsChangeStatus::needChange; + CHANGE_MODELS_NEEDED_NOW = modelsChangeStatus::needChange; } void setModelOneVar(int x, int y, UINT32 modelId) @@ -109,7 +116,7 @@ namespace stratModelsChange { try { - return stratModels.at(modelId); + return STRAT_MODELS.at(modelId); } catch (...) { @@ -122,7 +129,7 @@ namespace stratModelsChange { try { - return stratModels.at(modelId)->modelP; + return STRAT_MODELS.at(modelId)->modelP; } catch (...) { @@ -145,10 +152,10 @@ namespace stratModelsChange void checkAndChangeStratModels() { - if (changeModelsNeededNow == modelsChangeStatus::changed) + if (CHANGE_MODELS_NEEDED_NOW == modelsChangeStatus::changed) return; - changeModelsNeededNow = modelsChangeStatus::changed; + CHANGE_MODELS_NEEDED_NOW = modelsChangeStatus::changed; for (const stratModelChangeRecord* changeMod : stratModelChangeList) //static models { const stratModelRecord* mod1 = findStratModel(changeMod->modelId); @@ -190,19 +197,19 @@ namespace stratModelsChange // return; // ReSharper disable once CppUseElementsView - for (const auto& [index, record] : stratModels) + for (const auto& [index, record] : STRAT_MODELS) { record->modelP = loadModel(record->path, record->isSettlement); } MODELS_LOADED = true; - changeModelsNeededNow = modelsChangeStatus::needChange; + CHANGE_MODELS_NEEDED_NOW = modelsChangeStatus::needChange; } void loadCharModels() //rebuild character CAS entries to be sure no pointers were cleaned up { - for (const stratModelCharacterRecord* modRec : characterStratModels) + for (const stratModelCharacterRecord* modRec : CHARACTER_STRAT_MODELS) { - *modRec->entry = *buildCharacterCas(modRec->skeletonname, modRec->caspath, modRec->shadowcaspath, modRec->modelId, modRec->texturepath, modRec->scale); + *modRec->entry = *buildCharacterCas(modRec->skeletonName, modRec->casPath, modRec->shadowCasPath, modRec->modelId, modRec->texturePath, modRec->scale); } } @@ -225,11 +232,30 @@ namespace stratModelsChange const uint32_t flags = isSettlement ? 2428 : 2332; GAME_FUNC(char(__thiscall*)(model_Rigid*, DWORD, char*, uint32_t, int ,int, int, bool) , loadModelRigid)(modelRigid, casModel, textureName, flags, 2, 1, 1, true); + pushGameModel(modelRigid); GAME_FUNC(void(__thiscall*)(simpleCas*), closeCas)(&cas); GAME_FUNC(void(__thiscall*)(simpleCas*), simpleCasDestructor)(&cas); return modelRigid; } + void closeModel(model_Rigid* model) + { + if (!model) + return; + callClassFunc(model, 0x0); + GAME_FUNC(void(__thiscall*)(model_Rigid*), modelRigidDestructor)(model); + } + + void clearModels() + { + for (model_Rigid* model : GAME_MODELS) + { + closeModel(model); + } + GAME_MODELS.clear(); + *reinterpret_cast(dataOffsets::offsets.modelRigidCounts) = 0; + } + void setCharacterModel(character* gen, const char* model) //add character to be changed to the queue { if (!gen || !model) @@ -253,7 +279,7 @@ namespace stratModelsChange const auto charData = eopCharacterDataDb::get()->getOrCreateData(gen->characterRecord->giveValidLabel(), gen->getTypeID()); charData->model = model; - changeModelsNeededNow = modelsChangeStatus::needChange; + CHANGE_MODELS_NEEDED_NOW = modelsChangeStatus::needChange; } void changeStratModel(character* gen, const char* model) @@ -290,13 +316,13 @@ namespace stratModelsChange gen->genType = characterFacEntry; //assign new array to general - changeModelsNeededNow = modelsChangeStatus::needChange; + CHANGE_MODELS_NEEDED_NOW = modelsChangeStatus::needChange; } stratModelArrayEntry* findCharacterStratModel(const char* modelId) //find eop model from vector { - for (const stratModelCharacterRecord* newRec : characterStratModels) + for (const stratModelCharacterRecord* newRec : CHARACTER_STRAT_MODELS) { if (strcmp(modelId, newRec->modelId) == 0) { return newRec->entry; @@ -431,19 +457,19 @@ namespace stratModelsChange newRec->modelId = typeNameCopy2; const auto skeletonCopy = new char[strlen(skeletonname)]; strcpy(skeletonCopy, skeletonname); - newRec->skeletonname = skeletonCopy; + newRec->skeletonName = skeletonCopy; const auto caspathCopy = new char[strlen(caspath)]; strcpy(caspathCopy, caspath); - newRec->caspath = caspathCopy; + newRec->casPath = caspathCopy; const auto shadowpathCopy = new char[strlen(shadowcaspath)]; strcpy(shadowpathCopy, shadowcaspath); - newRec->shadowcaspath = shadowpathCopy; + newRec->shadowCasPath = shadowpathCopy; const auto textureCopy = new char[strlen(texturepath)]; strcpy(textureCopy, texturepath); - newRec->texturepath = textureCopy; + newRec->texturePath = textureCopy; newRec->scale = scale; - newRec->entry = buildCharacterCas(newRec->skeletonname, newRec->caspath, newRec->shadowcaspath, newRec->modelId, newRec->texturepath, newRec->scale); - characterStratModels.push_back(newRec); + newRec->entry = buildCharacterCas(newRec->skeletonName, newRec->casPath, newRec->shadowCasPath, newRec->modelId, newRec->texturePath, newRec->scale); + CHARACTER_STRAT_MODELS.push_back(newRec); } diff --git a/M2TWEOP Code/M2TWEOP library/stratModelsChange.h b/M2TWEOP Code/M2TWEOP library/stratModelsChange.h index 4ac5f8e2..64d7f661 100644 --- a/M2TWEOP Code/M2TWEOP library/stratModelsChange.h +++ b/M2TWEOP Code/M2TWEOP library/stratModelsChange.h @@ -24,7 +24,9 @@ namespace stratModelsChange void update(); void loadModels(); model_Rigid* loadModel(const std::string& path, bool isSettlement = false); - + void pushGameModel(model_Rigid* model); + void closeModel(model_Rigid* model); + void clearModels(); //add model to a game //pass path to model and its id(used for change models) //example of path - eopData/models_strat/northern_european_large_castle.CAS