From 71950cd40ac32ed0e788af12a3e3d5eeecf58c73 Mon Sep 17 00:00:00 2001 From: MS Date: Thu, 3 Aug 2023 20:13:41 -0400 Subject: [PATCH] lego1: MxVariableTable (#87) * MxStringVariable -> MxVariable * cursor test * GetVariable to 100 * meh * node insert inline * move to header file * use reference to match available code * variable table init in mxomni, some reshuffling --- CMakeLists.txt | 2 +- LEGO1/legobackgroundcolor.cpp | 14 +- LEGO1/legobackgroundcolor.h | 8 +- LEGO1/mxhashtable.h | 241 ++++++++++++++++++++++++++++++++++ LEGO1/mxomni.cpp | 9 ++ LEGO1/mxstringvariable.cpp | 22 ---- LEGO1/mxstringvariable.h | 20 --- LEGO1/mxvariable.cpp | 20 +++ LEGO1/mxvariable.h | 34 +++++ LEGO1/mxvariabletable.cpp | 60 ++++++++- LEGO1/mxvariabletable.h | 20 ++- 11 files changed, 385 insertions(+), 65 deletions(-) create mode 100644 LEGO1/mxhashtable.h delete mode 100644 LEGO1/mxstringvariable.cpp delete mode 100644 LEGO1/mxstringvariable.h create mode 100644 LEGO1/mxvariable.cpp create mode 100644 LEGO1/mxvariable.h diff --git a/CMakeLists.txt b/CMakeLists.txt index df71ccf0..3d1d0a1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,13 +149,13 @@ add_library(lego1 SHARED LEGO1/mxstillpresenter.cpp LEGO1/mxstreamer.cpp LEGO1/mxstring.cpp - LEGO1/mxstringvariable.cpp LEGO1/mxthread.cpp LEGO1/mxticklemanager.cpp LEGO1/mxtimer.cpp LEGO1/mxtransitionmanager.cpp LEGO1/mxunknown100dc6b0.cpp LEGO1/mxunknown100dc6e0.cpp + LEGO1/mxvariable.cpp LEGO1/mxvariabletable.cpp LEGO1/mxvector.cpp LEGO1/mxvideomanager.cpp diff --git a/LEGO1/legobackgroundcolor.cpp b/LEGO1/legobackgroundcolor.cpp index 2d8c5348..e1d6beca 100644 --- a/LEGO1/legobackgroundcolor.cpp +++ b/LEGO1/legobackgroundcolor.cpp @@ -9,18 +9,18 @@ const char *g_set = "set"; const char *g_reset = "reset"; // OFFSET: LEGO1 0x1003bfb0 -LegoBackgroundColor::LegoBackgroundColor(const char *p_name, const char *p_colorString) +LegoBackgroundColor::LegoBackgroundColor(const char *p_key, const char *p_value) { - m_name = p_name; - m_name.ToUpperCase(); - SetColorString(p_colorString); + m_key = p_key; + m_key.ToUpperCase(); + SetValue(p_value); } // OFFSET: LEGO1 0x1003c070 -void LegoBackgroundColor::SetColorString(const char *p_colorString) +void LegoBackgroundColor::SetValue(const char *p_colorString) { - m_string = p_colorString; - m_string.ToLowerCase(); + m_value = p_colorString; + m_value.ToLowerCase(); LegoVideoManager *videomanager = VideoManager(); if (!videomanager || !p_colorString) diff --git a/LEGO1/legobackgroundcolor.h b/LEGO1/legobackgroundcolor.h index 9345c394..aff62fe8 100644 --- a/LEGO1/legobackgroundcolor.h +++ b/LEGO1/legobackgroundcolor.h @@ -1,13 +1,13 @@ #ifndef LEGOBACKGROUNDCOLOR_H #define LEGOBACKGROUNDCOLOR_H -#include "mxstringvariable.h" +#include "mxvariable.h" -class LegoBackgroundColor : public MxStringVariable +class LegoBackgroundColor : public MxVariable { public: - __declspec(dllexport) LegoBackgroundColor(const char *p_name, const char *p_colorString); - void SetColorString(const char *p_colorString); + __declspec(dllexport) LegoBackgroundColor(const char *p_key, const char *p_value); + void SetValue(const char *p_colorString); private: float h; diff --git a/LEGO1/mxhashtable.h b/LEGO1/mxhashtable.h new file mode 100644 index 00000000..f9505bb5 --- /dev/null +++ b/LEGO1/mxhashtable.h @@ -0,0 +1,241 @@ +#ifndef MXHASHTABLE_H +#define MXHASHTABLE_H + +#include "mxtypes.h" +#include "mxcore.h" + +#define HASH_TABLE_INIT_SIZE 128 +#define HASH_TABLE_OPT_NO_EXPAND 0 +#define HASH_TABLE_OPT_EXPAND_ADD 1 +#define HASH_TABLE_OPT_EXPAND_MULTIPLY 2 + +template +class MxHashTableNode +{ +public: + MxHashTableNode() {} + MxHashTableNode(T *p_obj, MxU32 p_hash) + { + m_obj = p_obj; + m_hash = p_hash; + m_prev = NULL; + m_next = NULL; + } + +//private: + T* m_obj; + MxU32 m_hash; + MxHashTableNode *m_prev; + MxHashTableNode *m_next; +}; + +// See MxOmni::Create +// VTABLE 0x100dc1b0 +template +class HashTableParent : public MxCore +{ +public: + HashTableParent() { + m_numKeys = 0; + m_customDestructor = Destroy; + } + + // OFFSET: LEGO1 0x100afd30 + static void Destroy(T*) {}; + + // OFFSET: LEGO1 0x100afcd0 + virtual MxS8 Compare(T*, T*) = 0; + +protected: + MxU32 m_numKeys; // +0x8 + void (*m_customDestructor)(T*); // +0xc +}; + +// VTABLE 0x100dc1e8 +template +class MxHashTable : protected HashTableParent +{ +public: + MxHashTable() + { + m_numSlots = HASH_TABLE_INIT_SIZE; + m_slots = new MxHashTableNode*[HASH_TABLE_INIT_SIZE]; + memset(m_slots, 0, sizeof(MxHashTableNode *) * m_numSlots); + m_resizeOption = HASH_TABLE_OPT_NO_EXPAND; + } + + virtual ~MxHashTable(); + + void Resize(); + void MxHashTable::Add(T* ); + + virtual MxS8 Compare(T*, T*) = 0; + + // OFFSET: LEGO1 0x100afdc0 + virtual MxU32 Hash(T*) = 0; + + // FIXME: use of friend here? + friend class MxHashTableCursor; + +protected: + void _NodeInsert(MxHashTableNode *); + + MxHashTableNode **m_slots; // +0x10 + MxU32 m_numSlots; // +0x14 + MxU32 m_autoResizeRatio; + int m_resizeOption; // +0x1c + // FIXME: or FIXME? This qword is used as an integer or double depending + // on the value of m_resizeOption. Hard to say whether this is how the devs + // did it, but a simple cast in either direction doesn't match. + union { + MxU32 m_increaseAmount; + double m_increaseFactor; + }; +}; + +template +class MxHashTableCursor : public MxCore +{ +public: + MxHashTableCursor(MxHashTable *p_hashTable) + { + m_table = p_hashTable; + m_match = NULL; + } + + MxBool Find(T *p_obj) + { + MxU32 hash = m_table->Hash(p_obj); + int bucket = hash % m_table->m_numSlots; + + MxHashTableNode *t = m_table->m_slots[bucket]; + + while (t) { + if (t->m_hash == hash && !m_table->Compare(t->m_obj, p_obj)) + m_match = t; + t = t->m_next; + } + + return m_match != NULL; + } + + void GetMatch(T*& p_obj) + { + if (m_match) { + p_obj = m_match->m_obj; + } + } + + void DeleteMatch() + { + // Cut the matching node out of the linked list + // by updating pointer references. + + if (m_match->m_prev) { + m_match->m_prev->m_next = m_match->m_next; + } else { + // No "prev" node, so move "next" to the head of the list. + int bucket = m_match->m_hash % m_table->m_numSlots; + m_table->m_slots[bucket] = m_match->m_next; + } + + if (m_match->m_next) + m_match->m_next->m_prev = m_match->m_prev; + + m_table->m_customDestructor(m_match->m_obj); + delete m_match; + m_table->m_numKeys--; + } + +private: + MxHashTable *m_table; + MxHashTableNode *m_match; +}; + + +template +// OFFSET: LEGO1 0x100b0bd0 +MxHashTable::~MxHashTable() +{ + for (int i = 0; i < m_numSlots; i++) { + MxHashTableNode *t = m_slots[i]; + + while (t) { + MxHashTableNode *next = t->m_next; + m_customDestructor(t->m_obj); + delete t; + t = next; + } + } + + m_numKeys = 0; + memset(m_slots, 0, sizeof(MxHashTableNode *) * m_numSlots); + + delete[] m_slots; +} + +template +// OFFSET: LEGO1 0x100b7ab0 +inline void MxHashTable::Resize() +{ + // Save a reference to the current table + // so we can walk nodes and re-insert + MxU32 old_size = m_numSlots; + MxHashTableNode **old_table = m_slots; + + switch (m_resizeOption) { + case HASH_TABLE_OPT_EXPAND_ADD: + m_numSlots = old_size + m_increaseAmount; + break; + case HASH_TABLE_OPT_EXPAND_MULTIPLY: + m_numSlots = old_size * m_increaseFactor; + break; + } + + MxHashTableNode **new_table = new MxHashTableNode*[m_numSlots]; + // FIXME: order? m_numKeys set after `rep stosd` + m_slots = new_table; + memset(m_slots, 0, sizeof(MxHashTableNode *) * m_numSlots); + m_numKeys = 0; + + for (int i = 0; i != old_size; i++) { + MxHashTableNode *t = old_table[i]; + + while (t) { + MxHashTableNode *next = t->m_next; + _NodeInsert(t); + t = next; + } + } + + delete[] old_table; +} + +template +// OFFSET: LEGO1 0x100b7b80 +inline void MxHashTable::_NodeInsert(MxHashTableNode *p_node) +{ + int bucket = p_node->m_hash % m_numSlots; + + p_node->m_next = m_slots[bucket]; + + if (m_slots[bucket]) + m_slots[bucket]->m_prev = p_node; + + m_slots[bucket] = p_node; + m_numKeys++; +} + +template +inline void MxHashTable::Add(T* p_newobj) +{ + if (m_resizeOption && ((m_numKeys + 1) / m_numSlots) > m_autoResizeRatio) + MxHashTable::Resize(); + + MxU32 hash = Hash(p_newobj); + MxHashTableNode *node = new MxHashTableNode(p_newobj, hash); + + MxHashTable::_NodeInsert(node); +} + +#endif // MXHASHTABLE_H \ No newline at end of file diff --git a/LEGO1/mxomni.cpp b/LEGO1/mxomni.cpp index 10e8eccb..f5022b30 100644 --- a/LEGO1/mxomni.cpp +++ b/LEGO1/mxomni.cpp @@ -104,6 +104,15 @@ void MxOmni::SetInstance(MxOmni *instance) // OFFSET: LEGO1 0x100af0c0 MxResult MxOmni::Create(MxOmniCreateParam &p) { + if (p.CreateFlags().CreateVariableTable()) + { + MxVariableTable *variableTable = new MxVariableTable(); + this->m_variableTable = variableTable; + + if (variableTable == NULL) + return FAILURE; + } + if (p.CreateFlags().CreateTimer()) { MxTimer *timer = new MxTimer(); diff --git a/LEGO1/mxstringvariable.cpp b/LEGO1/mxstringvariable.cpp deleted file mode 100644 index e63329c1..00000000 --- a/LEGO1/mxstringvariable.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "mxstringvariable.h" -#include "mxstring.h" - - -// OFFSET: LEGO1 0x1003bea0 -MxString *MxStringVariable::GetString() -{ - return &m_string; -} - -// OFFSET: LEGO1 0x1003beb0 -void MxStringVariable::SetString(const char *colorString) -{ - m_string = colorString; -} - -//FIXME: Figure out what exactly this class is used for. It is used in LegoGameState::LegoGameState when loading the background color, and for loading the "fsmovie" variable -// OFFSET: LEGO1 0x1003bec0 -void MxStringVariable::Destroy() -{ - delete this; -} diff --git a/LEGO1/mxstringvariable.h b/LEGO1/mxstringvariable.h deleted file mode 100644 index dd623dfb..00000000 --- a/LEGO1/mxstringvariable.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef MXSTRINGVARIABLE_H -#define MXSTRINGVARIABLE_H -#include "mxstring.h" -#include "mxcore.h" -//VTABLE: 0x100d74a8 -class MxStringVariable -{ -public: - __declspec(dllexport) MxStringVariable(const char *, const char *); - MxStringVariable() {} - virtual MxString *GetString(); - virtual void SetString(const char *colorString); - virtual void Destroy(); - -protected: - MxString m_name; - MxString m_string; -}; - -#endif // MXSTRINGVARIABLE_H diff --git a/LEGO1/mxvariable.cpp b/LEGO1/mxvariable.cpp new file mode 100644 index 00000000..f18e6a94 --- /dev/null +++ b/LEGO1/mxvariable.cpp @@ -0,0 +1,20 @@ +#include "mxvariable.h" +#include "mxstring.h" + +// OFFSET: LEGO1 0x1003bea0 +MxString *MxVariable::GetValue() +{ + return &m_value; +} + +// OFFSET: LEGO1 0x1003beb0 +void MxVariable::SetValue(const char *value) +{ + m_value = value; +} + +// OFFSET: LEGO1 0x1003bec0 +void MxVariable::Destroy() +{ + delete this; +} diff --git a/LEGO1/mxvariable.h b/LEGO1/mxvariable.h new file mode 100644 index 00000000..6899dfac --- /dev/null +++ b/LEGO1/mxvariable.h @@ -0,0 +1,34 @@ +#ifndef MXVARIABLE_H +#define MXVARIABLE_H + +#include "mxstring.h" +#include "mxcore.h" + +//VTABLE: 0x100d74a8 +class MxVariable +{ +public: + MxVariable() {} + MxVariable(const char *p_key) + { + m_key = p_key; + m_key.ToUpperCase(); + } + MxVariable(const char *p_key, const char *p_value) + { + m_key = p_key; + m_key.ToUpperCase(); + m_value = p_value; + } + virtual MxString *GetValue(); + virtual void SetValue(const char *); + virtual void Destroy(); + + inline const MxString *GetKey() const { return &m_key; } + +protected: + MxString m_key; + MxString m_value; +}; + +#endif // MXVARIABLE_H diff --git a/LEGO1/mxvariabletable.cpp b/LEGO1/mxvariabletable.cpp index 8927bc32..81628993 100644 --- a/LEGO1/mxvariabletable.cpp +++ b/LEGO1/mxvariabletable.cpp @@ -1,20 +1,66 @@ #include "mxvariabletable.h" -// OFFSET: LEGO1 0x100b73a0 -void MxVariableTable::SetVariable(const char *key, const char *value) +// OFFSET: LEGO1 0x100b7330 +MxS8 MxVariableTable::Compare(MxVariable *p_var0, MxVariable *p_var1) { - // TODO + return strcmp(p_var0->GetKey()->GetData(), + p_var1->GetKey()->GetData()); +} + +// OFFSET: LEGO1 0x100b7370 +MxU32 MxVariableTable::Hash(MxVariable *p_var) +{ + const char *str = p_var->GetKey()->GetData(); + MxU32 value = 0; + + for (int i = 0; str[i]; i++) { + value += str[i]; + } + + return value; +} + +// OFFSET: LEGO1 0x100b73a0 +void MxVariableTable::SetVariable(const char *p_key, const char *p_value) +{ + MxHashTableCursor cursor(this); + MxVariable *var = new MxVariable(p_key, p_value); + + if (cursor.Find(var)) { + delete var; + cursor.GetMatch(var); + var->SetValue(p_value); + } else { + MxHashTable::Add(var); + } } // OFFSET: LEGO1 0x100b7740 void MxVariableTable::SetVariable(MxVariable *var) { - // TODO + MxHashTableCursor cursor(this); + MxBool found = cursor.Find(var); + + if (found) + cursor.DeleteMatch(); + + MxHashTable::Add(var); } // OFFSET: LEGO1 0x100b78f0 -const char *MxVariableTable::GetVariable(const char *key) +const char *MxVariableTable::GetVariable(const char *p_key) { - // TODO - return 0; + const char *value = ""; + MxHashTableCursor cursor(this); + MxVariable *var = new MxVariable(p_key); + + MxBool found = cursor.Find(var); + delete var; + + if (found) { + cursor.GetMatch(var); + value = var->GetValue()->GetData(); + } + + return value; } diff --git a/LEGO1/mxvariabletable.h b/LEGO1/mxvariabletable.h index 13c70658..b9ecf174 100644 --- a/LEGO1/mxvariabletable.h +++ b/LEGO1/mxvariabletable.h @@ -1,16 +1,28 @@ #ifndef MXVARIABLETABLE_H #define MXVARIABLETABLE_H -class MxVariable; +#include "mxtypes.h" +#include "mxhashtable.h" +#include "mxvariable.h" + // VTABLE 0x100dc1c8 // SIZE 0x28 -class MxVariableTable +class MxVariableTable : public MxHashTable { public: - __declspec(dllexport) const char * GetVariable(const char *key); - __declspec(dllexport) void SetVariable(MxVariable *var); + MxVariableTable() { + m_customDestructor = Destroy; + } __declspec(dllexport) void SetVariable(const char *key, const char *value); + __declspec(dllexport) void SetVariable(MxVariable *var); + __declspec(dllexport) const char * GetVariable(const char *key); + + // OFFSET: LEGO1 0x100afdb0 + static void Destroy(MxVariable *p_obj) { p_obj->Destroy(); } + + virtual MxS8 Compare(MxVariable *, MxVariable *); // +0x14 + virtual MxU32 Hash(MxVariable *); // +0x18 }; #endif // MXVARIABLETABLE_H