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();