From bb0d5be921c243970d7a45db59a37dfc21657968 Mon Sep 17 00:00:00 2001 From: MS Date: Wed, 16 Aug 2023 13:09:44 -0400 Subject: [PATCH] Implement MxAtomId and reference counter (stl set) (#109) * Implement MxAtomId and reference counter (stl set) * Partial success in hiding 4786 warning spam. * Build out most of MxOmni::Destroy since it also touches the set * Add some size asserts --- CMakeLists.txt | 1 + LEGO1/compat.h | 6 +++ LEGO1/mxatomid.cpp | 87 ++++++++++++++++++++++++++++++++++++--- LEGO1/mxatomid.h | 7 +++- LEGO1/mxatomidcounter.cpp | 18 ++++++++ LEGO1/mxatomidcounter.h | 49 ++++++++++++++++++++++ LEGO1/mxomni.cpp | 44 +++++++++++++++++++- LEGO1/mxomni.h | 5 ++- 8 files changed, 209 insertions(+), 8 deletions(-) create mode 100644 LEGO1/mxatomidcounter.cpp create mode 100644 LEGO1/mxatomidcounter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3645e122..3428ef6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,7 @@ add_library(lego1 SHARED LEGO1/legoworldpresenter.cpp LEGO1/motorcycle.cpp LEGO1/mxatomid.cpp + LEGO1/mxatomidcounter.cpp LEGO1/mxaudiopresenter.cpp LEGO1/mxautolocker.cpp LEGO1/mxbackgroundaudiomanager.cpp diff --git a/LEGO1/compat.h b/LEGO1/compat.h index a182b0fc..33b97d5a 100644 --- a/LEGO1/compat.h +++ b/LEGO1/compat.h @@ -14,6 +14,11 @@ // DIsable "nonstandard extension used : 'bool'" warning spam #pragma warning( disable : 4237 ) +// Disable "identifier was truncated to '255' characters" warning. +// Impossible to avoid this if using STL map or set. +// This removes most (but not all) occurrences of the warning. +#pragma warning( disable : 4786 ) + #define MSVC420_VERSION 1020 // STL compatibility. @@ -22,6 +27,7 @@ #else #include #include +#include using namespace std; #endif diff --git a/LEGO1/mxatomid.cpp b/LEGO1/mxatomid.cpp index 007e2de3..092f5deb 100644 --- a/LEGO1/mxatomid.cpp +++ b/LEGO1/mxatomid.cpp @@ -1,20 +1,97 @@ #include "mxatomid.h" +#include "mxomni.h" // OFFSET: LEGO1 0x100acf90 -MxAtomId::MxAtomId(const char *, LookupMode) +MxAtomId::MxAtomId(const char *p_str, LookupMode p_mode) { - // TODO + if (!MxOmni::GetInstance()) + return; + + if (!AtomIdCounterSet()) + return; + + MxAtomIdCounter *counter = GetCounter(p_str, p_mode); + m_internal = counter->GetKey()->GetData(); + counter->Inc(); } // OFFSET: LEGO1 0x100acfd0 MxAtomId::~MxAtomId() { - // TODO + Destroy(); +} + +// OFFSET: LEGO1 0x100acfe0 +void MxAtomId::Destroy() +{ + if (!m_internal) + return; + + if (!MxOmni::GetInstance()) + return; + + if (!AtomIdCounterSet()) + return; + + // The dtor is called on the counter object immediately, + // so this syntax should be correct. + MxAtomIdCounterSet::iterator it = AtomIdCounterSet()->find( + &MxAtomIdCounter(m_internal) + ); + + MxAtomIdCounter *counter = (MxAtomIdCounter*)(*it); + counter->Dec(); } // OFFSET: LEGO1 0x100ad1c0 -MxAtomId &MxAtomId::operator=(const MxAtomId &id) +MxAtomId &MxAtomId::operator=(const MxAtomId &p_atomId) { - // TODO + if (m_internal) + Destroy(); + + if (p_atomId.m_internal && MxOmni::GetInstance() && AtomIdCounterSet()) { + MxAtomIdCounter *counter = GetCounter(p_atomId.m_internal, LookupMode_Exact); + counter->Inc(); + } + + m_internal = p_atomId.m_internal; + return *this; } + +// OFFSET: LEGO1 0x100ad210 +MxAtomIdCounter* MxAtomId::GetCounter(const char *p_str, LookupMode p_mode) +{ + MxAtomId _unused; + MxAtomIdCounter *counter = new MxAtomIdCounter(p_str); + + switch (p_mode) { + case LookupMode_LowerCase: + case LookupMode_LowerCase2: + counter->GetKey()->ToLowerCase(); + break; + case LookupMode_UpperCase: + counter->GetKey()->ToUpperCase(); + break; + } + + MxAtomIdCounterSet::iterator it = AtomIdCounterSet()->find(counter); + if (it != AtomIdCounterSet()->end()) { + // Counter already in the set. Delete temp value and return it. + delete counter; + counter = *it; + } else { + // Counter is not in the set. Add it. + AtomIdCounterSet()->insert(counter); + } + + return counter; +} + +// OFFSET: LEGO1 0x100ad7e0 +void MxAtomId::Clear() +{ + // Reset but do not delete MxAtomId object. + Destroy(); + m_internal = NULL; +} diff --git a/LEGO1/mxatomid.h b/LEGO1/mxatomid.h index 4a2082cf..8efef679 100644 --- a/LEGO1/mxatomid.h +++ b/LEGO1/mxatomid.h @@ -2,6 +2,7 @@ #define MXATOMID_H #include "mxtypes.h" +#include "mxatomidcounter.h" enum LookupMode { @@ -27,9 +28,13 @@ class MxAtomId { return this->m_internal == other.m_internal; } + void Clear(); private: - char *m_internal; + MxAtomIdCounter* GetCounter(const char *, LookupMode); + void Destroy(); + + const char *m_internal; }; #endif // MXATOMID_H diff --git a/LEGO1/mxatomidcounter.cpp b/LEGO1/mxatomidcounter.cpp new file mode 100644 index 00000000..a38dbe6f --- /dev/null +++ b/LEGO1/mxatomidcounter.cpp @@ -0,0 +1,18 @@ +#include "mxatomidcounter.h" +#include "decomp.h" + +DECOMP_SIZE_ASSERT(MxAtomIdCounter, 0x14); +DECOMP_SIZE_ASSERT(MxAtomIdCounterSet, 0x10); + +// OFFSET: LEGO1 0x100ad7f0 +void MxAtomIdCounter::Inc() +{ + m_value++; +} + +// OFFSET: LEGO1 0x100ad800 +void MxAtomIdCounter::Dec() +{ + if (m_value) + m_value--; +} diff --git a/LEGO1/mxatomidcounter.h b/LEGO1/mxatomidcounter.h new file mode 100644 index 00000000..79e7c7f5 --- /dev/null +++ b/LEGO1/mxatomidcounter.h @@ -0,0 +1,49 @@ +#ifndef MXATOMIDCOUNTER_H +#define MXATOMIDCOUNTER_H + +#include "mxstring.h" +#include "compat.h" // STL + +// Counts the number of existing MxAtomId objects based +// on the matching char* string. A seems fit for purpose here: +// We have an MxString as a key and MxU16 as the value. +// And yet a is the best match. The malloc in MxOmni::Create +// for the _Nil node asks for more bytes than a regular node if a +// is used, but all nodes are 20 bytes wide with a . +// Also: the increment/decrement methods suggest a custom type was used +// for the combined key_value_pair, which doesn't seem possible with . + +// SIZE: 0x14 (including padding) +class MxAtomIdCounter +{ +public: + // always inlined + MxAtomIdCounter(const char *p_str) + { + m_key = p_str; + m_value = 0; + } + + void Inc(); + void Dec(); + inline MxString* GetKey() { return &m_key; }; + inline MxU16 GetValue() { return m_value; }; + +private: + MxString m_key; + MxU16 m_value; +}; + +struct MxAtomIdCounterCompare +{ + // OFFSET: LEGO1 0x100ad120 + int operator()(MxAtomIdCounter* const & p_val0, MxAtomIdCounter* const & p_val1) const + { + return strcmp(p_val0->GetKey()->GetData(), p_val1->GetKey()->GetData()) > 0; + } +}; + +class MxAtomIdCounterSet : public set +{}; + +#endif //MXATOMIDCOUNTER_H \ No newline at end of file diff --git a/LEGO1/mxomni.cpp b/LEGO1/mxomni.cpp index f5022b30..4048a0b5 100644 --- a/LEGO1/mxomni.cpp +++ b/LEGO1/mxomni.cpp @@ -38,7 +38,7 @@ void MxOmni::Init() m_eventManager = NULL; m_timer = NULL; m_streamer = NULL; - m_unk44 = NULL; + m_atomIdCounterSet = NULL; m_unk64 = NULL; } @@ -104,6 +104,8 @@ void MxOmni::SetInstance(MxOmni *instance) // OFFSET: LEGO1 0x100af0c0 MxResult MxOmni::Create(MxOmniCreateParam &p) { + m_atomIdCounterSet = new MxAtomIdCounterSet(); + if (p.CreateFlags().CreateVariableTable()) { MxVariableTable *variableTable = new MxVariableTable(); @@ -129,6 +131,40 @@ MxResult MxOmni::Create(MxOmniCreateParam &p) void MxOmni::Destroy() { // FIXME: Stub + + /* + // TODO: private members + if (m_notificationManager) { + while (m_notificationManager->m_queue->size()) { + m_notificationManager->Tickle(); + } + } + + m_notificationManager->m_active = 0; + */ + + delete m_eventManager; + delete m_soundManager; + delete m_musicManager; + delete m_videoManager; + delete m_streamer; + delete m_timer; + delete m_objectFactory; + delete m_variableTable; + delete m_notificationManager; + delete m_tickleManager; + + // There could be a tree/iterator function that does this inline + if (m_atomIdCounterSet) { + while (!m_atomIdCounterSet->empty()) { + // Pop each node and delete its value + MxAtomIdCounterSet::iterator begin = m_atomIdCounterSet->begin(); + MxAtomIdCounter *value = *begin; + m_atomIdCounterSet->erase(begin); + delete value; + } + delete m_atomIdCounterSet; + } } // OFFSET: LEGO1 0x100b07f0 @@ -162,6 +198,12 @@ MxTimer *Timer() return MxOmni::GetInstance()->GetTimer(); } +// OFFSET: LEGO1 0x100acee0 +MxAtomIdCounterSet *AtomIdCounterSet() +{ + return MxOmni::GetInstance()->GetAtomIdCounterSet(); +} + // OFFSET: LEGO1 0x100acef0 MxStreamer* Streamer() { diff --git a/LEGO1/mxomni.h b/LEGO1/mxomni.h index b09d11ad..fb0a9f3e 100644 --- a/LEGO1/mxomni.h +++ b/LEGO1/mxomni.h @@ -14,6 +14,7 @@ #include "mxtimer.h" #include "mxvariabletable.h" #include "mxvideomanager.h" +#include "mxatomidcounter.h" // VTABLE 0x100dc168 // SIZE 0x68 @@ -47,6 +48,7 @@ class MxOmni : public MxCore MxVariableTable* GetVariableTable() const { return this->m_variableTable; } MxMusicManager* GetMusicManager() const { return this->m_musicManager; } MxEventManager* GetEventManager() const { return this->m_eventManager; } + MxAtomIdCounterSet* GetAtomIdCounterSet() const { return this->m_atomIdCounterSet; } protected: static MxOmni* g_instance; @@ -63,7 +65,7 @@ class MxOmni : public MxCore MxTimer* m_timer; //0x3C MxStreamer* m_streamer; //0x40 - int m_unk44; // 0x44 + MxAtomIdCounterSet* m_atomIdCounterSet; // 0x44 MxCriticalSection m_criticalsection; // 0x48 @@ -78,5 +80,6 @@ __declspec(dllexport) MxMusicManager * MusicManager(); __declspec(dllexport) MxEventManager * EventManager(); __declspec(dllexport) MxNotificationManager * NotificationManager(); MxVideoManager * MVideoManager(); +MxAtomIdCounterSet* AtomIdCounterSet(); #endif // MXOMNI_H