From 3eb96201209dfefef22dda515e78310624e11168 Mon Sep 17 00:00:00 2001 From: CCHyper <73803386+CCHyper@users.noreply.github.com> Date: Wed, 15 Sep 2021 21:10:42 +0100 Subject: [PATCH] Implements initial ScenarioClass extension. --- src/extensions/saveload/saveload.cpp | 46 ++++ src/extensions/scenario/scenarioext.cpp | 173 ++++++++++++ src/extensions/scenario/scenarioext.h | 63 +++++ src/extensions/scenario/scenarioext_hooks.cpp | 7 + src/extensions/scenario/scenarioext_init.cpp | 248 ++++++++++++++++++ src/extensions/scenario/scenarioext_init.h | 31 +++ 6 files changed, 568 insertions(+) create mode 100644 src/extensions/scenario/scenarioext.cpp create mode 100644 src/extensions/scenario/scenarioext.h create mode 100644 src/extensions/scenario/scenarioext_init.cpp create mode 100644 src/extensions/scenario/scenarioext_init.h diff --git a/src/extensions/saveload/saveload.cpp b/src/extensions/saveload/saveload.cpp index eaab0a5e5..4a2b897dc 100644 --- a/src/extensions/saveload/saveload.cpp +++ b/src/extensions/saveload/saveload.cpp @@ -36,6 +36,7 @@ #include "rulesext.h" #include "sessionext.h" +#include "scenarioext.h" #include "tacticalext.h" #include "objecttypeext.h" @@ -89,6 +90,7 @@ unsigned ViniferaSaveGameVersion = */ + sizeof(RulesClassExtension) + sizeof(SessionClassExtension) + + sizeof(ScenarioClassExtension) + sizeof(TacticalExtension) /** @@ -327,6 +329,38 @@ static bool Vinifera_Load_SessionExtension(IStream *pStm) } +static bool Vinifera_Save_ScenarioExtension(IStream *pStm) +{ + if (!pStm) { + return false; + } + + if (!ScenarioExtension) { + return false; + } + + ScenarioExtension->Save(pStm, true); + + return true; +} + + +static bool Vinifera_Load_ScenarioExtension(IStream *pStm) +{ + if (!pStm) { + return false; + } + + if (!ScenarioExtension) { + return false; + } + + ScenarioExtension->Load(pStm); + + return true; +} + + static bool Vinifera_Save_TacticalExtension(IStream *pStm) { if (!pStm) { @@ -477,6 +511,12 @@ bool Vinifera_Put_All(IStream *pStm) return false; } + DEBUG_INFO("Saving ScenarioExtension\n"); + if (!Vinifera_Save_ScenarioExtension(pStm)) { + DEBUG_INFO("\t***** FAILED!\n"); + return false; + } + DEBUG_INFO("Saving TacticalExtension\n"); if (!Vinifera_Save_TacticalExtension(pStm)) { DEBUG_INFO("\t***** FAILED!\n"); @@ -761,6 +801,12 @@ bool Vinifera_Load_All(IStream *pStm) return false; } + DEBUG_INFO("Loading ScenarioExtension\n"); + if (!Vinifera_Load_ScenarioExtension(pStm)) { + DEBUG_INFO("\t***** FAILED!\n"); + return false; + } + DEBUG_INFO("Loading TacticalExtension\n"); if (!Vinifera_Load_TacticalExtension(pStm)) { DEBUG_INFO("\t***** FAILED!\n"); diff --git a/src/extensions/scenario/scenarioext.cpp b/src/extensions/scenario/scenarioext.cpp new file mode 100644 index 000000000..292200d37 --- /dev/null +++ b/src/extensions/scenario/scenarioext.cpp @@ -0,0 +1,173 @@ +/******************************************************************************* +/* O P E N S O U R C E -- V I N I F E R A ** +/******************************************************************************* + * + * @project Vinifera + * + * @file SCENARIOEXT.CPP + * + * @author CCHyper + * + * @brief Extended ScenarioClass class. + * + * @license Vinifera is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version + * 3 of the License, or (at your option) any later version. + * + * Vinifera is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. + * If not, see . + * + ******************************************************************************/ +#include "scenarioext.h" +#include "asserthandler.h" +#include "debughandler.h" + + +ScenarioClassExtension *ScenarioExtension = nullptr; + + +/** + * Class constructor. + * + * @author: CCHyper + */ +ScenarioClassExtension::ScenarioClassExtension(ScenarioClass *this_ptr) : + Extension(this_ptr) +{ + ASSERT(ThisPtr != nullptr); + //DEV_DEBUG_TRACE("ScenarioClassExtension constructor - 0x%08X\n", (uintptr_t)(ThisPtr)); + //DEV_DEBUG_WARNING("ScenarioClassExtension constructor - 0x%08X\n", (uintptr_t)(ThisPtr)); + + /** + * This copies the behavior of the games ScenarioClass. + */ + Init_Clear(); + + IsInitialized = true; +} + + +/** + * Class no-init constructor. + * + * @author: CCHyper + */ +ScenarioClassExtension::ScenarioClassExtension(const NoInitClass &noinit) : + Extension(noinit) +{ + IsInitialized = false; +} + + +/** + * Class deconstructor. + * + * @author: CCHyper + */ +ScenarioClassExtension::~ScenarioClassExtension() +{ + //DEV_DEBUG_TRACE("ScenarioClassExtension deconstructor - 0x%08X\n", (uintptr_t)(ThisPtr)); + //DEV_DEBUG_WARNING("ScenarioClassExtension deconstructor - 0x%08X\n", (uintptr_t)(ThisPtr)); + + IsInitialized = false; +} + + +/** + * Initializes an object from the stream where it was saved previously. + * + * @author: CCHyper + */ +HRESULT ScenarioClassExtension::Load(IStream *pStm) +{ + ASSERT(ThisPtr != nullptr); + //DEV_DEBUG_TRACE("ScenarioClassExtension::Load - 0x%08X\n", (uintptr_t)(ThisPtr)); + + HRESULT hr = Extension::Load(pStm); + if (FAILED(hr)) { + return E_FAIL; + } + + new (this) ScenarioClassExtension(NoInitClass()); + + return hr; +} + + +/** + * Saves an object to the specified stream. + * + * @author: CCHyper + */ +HRESULT ScenarioClassExtension::Save(IStream *pStm, BOOL fClearDirty) +{ + ASSERT(ThisPtr != nullptr); + //DEV_DEBUG_TRACE("ScenarioClassExtension::Save - 0x%08X\n", (uintptr_t)(ThisPtr)); + + HRESULT hr = Extension::Save(pStm, fClearDirty); + if (FAILED(hr)) { + return hr; + } + + return hr; +} + + +/** + * Return the raw size of class data for save/load purposes. + * + * @author: CCHyper + */ +int ScenarioClassExtension::Size_Of() const +{ + ASSERT(ThisPtr != nullptr); + //DEV_DEBUG_TRACE("ScenarioClassExtension::Size_Of - 0x%08X\n", (uintptr_t)(ThisPtr)); + + return sizeof(*this); +} + + +/** + * Removes the specified target from any targeting and reference trackers. + * + * @author: CCHyper + */ +void ScenarioClassExtension::Detach(TARGET target, bool all) +{ + ASSERT(ThisPtr != nullptr); + //DEV_DEBUG_TRACE("ScenarioClassExtension::Detach - 0x%08X\n", (uintptr_t)(ThisPtr)); + +} + + +/** + * Compute a unique crc value for this instance. + * + * @author: CCHyper + */ +void ScenarioClassExtension::Compute_CRC(WWCRCEngine &crc) const +{ + ASSERT(ThisPtr != nullptr); + //DEV_DEBUG_TRACE("ScenarioClassExtension::Compute_CRC - 0x%08X\n", (uintptr_t)(ThisPtr)); + +} + + +/** + * Initialises any values for this instance. + * + * @author: CCHyper + */ +void ScenarioClassExtension::Init_Clear() +{ + ASSERT(ThisPtr != nullptr); + //DEV_DEBUG_TRACE("ScenarioClassExtension::Init - 0x%08X\n", (uintptr_t)(ThisPtr)); + +} diff --git a/src/extensions/scenario/scenarioext.h b/src/extensions/scenario/scenarioext.h new file mode 100644 index 000000000..4959eca50 --- /dev/null +++ b/src/extensions/scenario/scenarioext.h @@ -0,0 +1,63 @@ +/******************************************************************************* +/* O P E N S O U R C E -- V I N I F E R A ** +/******************************************************************************* + * + * @project Vinifera + * + * @file SCENARIOEXT.H + * + * @author CCHyper + * + * @brief Extended ScenarioClass class. + * + * @license Vinifera is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version + * 3 of the License, or (at your option) any later version. + * + * Vinifera is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. + * If not, see . + * + ******************************************************************************/ +#pragma once + +#include "extension.h" +#include "container.h" + +#include "noinit.h" + + +class ScenarioClass; + + +class ScenarioClassExtension final : public Extension +{ + public: + ScenarioClassExtension(ScenarioClass *this_ptr); + ScenarioClassExtension(const NoInitClass &noinit); + ~ScenarioClassExtension(); + + virtual HRESULT Load(IStream *pStm) override; + virtual HRESULT Save(IStream *pStm, BOOL fClearDirty) override; + virtual int Size_Of() const override; + + virtual void Detach(TARGET target, bool all = true) override; + virtual void Compute_CRC(WWCRCEngine &crc) const override; + + void Init_Clear(); + + public: + +}; + + +/** + * Global instance of the extended class. + */ +extern ScenarioClassExtension *ScenarioExtension; diff --git a/src/extensions/scenario/scenarioext_hooks.cpp b/src/extensions/scenario/scenarioext_hooks.cpp index 54072aff4..418084945 100644 --- a/src/extensions/scenario/scenarioext_hooks.cpp +++ b/src/extensions/scenario/scenarioext_hooks.cpp @@ -26,7 +26,9 @@ * ******************************************************************************/ #include "scenarioext_hooks.h" +#include "scenarioext_init.h" #include "scenarioext_functions.h" +#include "scenarioext.h" #include "tibsun_globals.h" #include "multiscore.h" #include "scenario.h" @@ -81,6 +83,11 @@ DECLARE_PATCH(_Do_Lose_Skip_MPlayer_Score_Screen_Patch) */ void ScenarioClassExtension_Hooks() { + /** + * Initialises the extended class. + */ + ScenarioClassExtension_Init(); + /** * Hooks in the new Assign_Houses() function. * diff --git a/src/extensions/scenario/scenarioext_init.cpp b/src/extensions/scenario/scenarioext_init.cpp new file mode 100644 index 000000000..4d6ee6670 --- /dev/null +++ b/src/extensions/scenario/scenarioext_init.cpp @@ -0,0 +1,248 @@ +/******************************************************************************* +/* O P E N S O U R C E -- V I N I F E R A ** +/******************************************************************************* + * + * @project Vinifera + * + * @file SCENARIOEXT_INIT.CPP + * + * @author CCHyper + * + * @brief Contains the hooks for initialising the extended ScenarioClass. + * + * @license Vinifera is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version + * 3 of the License, or (at your option) any later version. + * + * Vinifera is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. + * If not, see . + * + ******************************************************************************/ +#include "scenarioext_hooks.h" +#include "scenarioext.h" +#include "scenario.h" +#include "tibsun_globals.h" +#include "vinifera_util.h" +#include "fatal.h" +#include "debughandler.h" +#include "asserthandler.h" + + +/** + * "new" operations must be done within a new function for patched code. + * + * @author: CCHyper + */ +static void New_Scenario_Extension(ScenarioClass *this_ptr) +{ + /** + * Delete existing instance (should never be the case). + */ + delete ScenarioExtension; + + ScenarioExtension = new ScenarioClassExtension(this_ptr); +} + + +/** + * "delete" operations must be done within a new function for patched code. + * + * @author: CCHyper + */ +static void Delete_Scenario_Extension() +{ + delete ScenarioExtension; +} + + +/** + * Patch for including the extended class members in the creation process. + * + * @warning: Do not touch this unless you know what you are doing! + * + * @author: CCHyper + */ +DECLARE_PATCH(_ScenarioClass_Constructor_Patch) +{ + GET_REGISTER_STATIC(ScenarioClass *, this_ptr, ebp); // "this" pointer. + + /** + * Create the extended class instance. + */ + New_Scenario_Extension(this_ptr); + if (!ScenarioExtension) { + DEBUG_ERROR("Failed to create ScenarioExtension instance for 0x%08X!\n", (uintptr_t)this_ptr); + ShowCursor(TRUE); + MessageBoxA(MainWindow, "Failed to create ScenarioExtension instance!\n", "Vinifera", MB_OK|MB_ICONEXCLAMATION); + Vinifera_Generate_Mini_Dump(); + Fatal("Failed to create ScenarioExtension instance!\n"); + goto original_code; // Keep this for clean code analysis. + } + + /** + * Stolen bytes here. + */ +original_code: + + /** + * We can't assign to Views directly without trashing the stack, so clear the whole array. + */ + std::memset(&this_ptr->Views, 0, sizeof(this_ptr->Views)); + + _asm { mov eax, this_ptr } + _asm { pop edi } + _asm { pop esi } + _asm { pop ebp } + _asm { pop ebx } + _asm { ret } +} + + +/** + * Patch for including the extended class members in the noinit creation process. + * + * @warning: Do not touch this unless you know what you are doing! + * + * @author: CCHyper + */ +DECLARE_PATCH(_ScenarioClass_NoInit_Constructor_Patch) +{ + GET_REGISTER_STATIC(ScenarioClass *, this_ptr, esi); // "this" pointer. + GET_STACK_STATIC(const NoInitClass *, noinit, esp, 0x4); + + /** + * Create the extended class instance. + */ + New_Scenario_Extension(this_ptr); + if (!ScenarioExtension) { + DEBUG_ERROR("Failed to create ScenarioExtension instance for 0x%08X!\n", (uintptr_t)this_ptr); + ShowCursor(TRUE); + MessageBoxA(MainWindow, "Failed to create ScenarioExtension instance!\n", "Vinifera", MB_OK|MB_ICONEXCLAMATION); + Vinifera_Generate_Mini_Dump(); + Fatal("Failed to create ScenarioExtension instance!\n"); + goto original_code; // Keep this for clean code analysis. + } + + /** + * Stolen bytes here. + */ +original_code: + _asm { mov eax, this_ptr } + _asm { pop edi } + _asm { pop esi } + _asm { pop ebx } + _asm { ret 4 } +} + + +/** + * Patch for including the extended class members in the destruction process. + * + * @warning: Do not touch this unless you know what you are doing! + * + * @author: CCHyper + */ +DECLARE_PATCH(_ScenarioClass_Deconstructor_Patch) +{ + GET_REGISTER_STATIC(ScenarioClass *, this_ptr, esi); + + /** + * Remove the extended class instance. + */ + Delete_Scenario_Extension(); + + /** + * Stolen bytes here. + */ +original_code: + Scen = nullptr; + + JMP(0x006023D2); +} + + +/** + * Patch for including the extended class members when initialsing the scenario data. + * + * @warning: Do not touch this unless you know what you are doing! + * + * @author: CCHyper + */ +DECLARE_PATCH(_ScenarioClass_Init_Clear_Patch) +{ + GET_REGISTER_STATIC(ScenarioClass *, this_ptr, esi); + + /** + * Find the extension instance. + */ + if (!ScenarioExtension) { + goto original_code; + } + + ScenarioExtension->Init_Clear(); + + /** + * Stolen bytes here. + */ +original_code: + _asm { pop edi } + _asm { pop esi } + _asm { pop ebp } + _asm { pop ebx } + _asm { add esp, 0x0C } + _asm { ret } +} + + +/** + * Patch for including the extended class members when computing a unique crc value for this instance. + * + * @warning: Do not touch this unless you know what you are doing! + * + * @author: CCHyper + */ +DECLARE_PATCH(_ScenarioClass_Compute_CRC_Patch) +{ + GET_REGISTER_STATIC(OverlayTypeClass *, this_ptr, esi); + GET_STACK_STATIC(WWCRCEngine *, crc, esp, 0xC); + + /** + * Find the extension instance. + */ + if (!ScenarioExtension) { + goto original_code; + } + + /** + * Read type class compute crc. + */ + ScenarioExtension->Compute_CRC(*crc); + + /** + * Stolen bytes here. + */ +original_code: + _asm { pop edi } + _asm { pop esi } + _asm { ret 4 } +} + + +/** + * Main function for patching the hooks. + */ +void ScenarioClassExtension_Init() +{ + Patch_Jump(0x005DADDE, &_ScenarioClass_Constructor_Patch); + Patch_Jump(0x005DAE87, &_ScenarioClass_NoInit_Constructor_Patch); + Patch_Jump(0x006023CC, &_ScenarioClass_Deconstructor_Patch); // Inlined in game shutdown. + Patch_Jump(0x005DB166, &_ScenarioClass_Init_Clear_Patch); + Patch_Jump(0x005E1440, &_ScenarioClass_Compute_CRC_Patch); +} diff --git a/src/extensions/scenario/scenarioext_init.h b/src/extensions/scenario/scenarioext_init.h new file mode 100644 index 000000000..31cc662ca --- /dev/null +++ b/src/extensions/scenario/scenarioext_init.h @@ -0,0 +1,31 @@ +/******************************************************************************* +/* O P E N S O U R C E -- V I N I F E R A ** +/******************************************************************************* + * + * @project Vinifera + * + * @file SCENARIOEXT_INIT.H + * + * @author CCHyper + * + * @brief Contains the hooks for initialising the extended ScenarioClass. + * + * @license Vinifera is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version + * 3 of the License, or (at your option) any later version. + * + * Vinifera is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. + * If not, see . + * + ******************************************************************************/ +#pragma once + + +void ScenarioClassExtension_Init();