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
This commit is contained in:
MS 2023-08-16 13:09:44 -04:00 committed by GitHub
parent 75c1276292
commit bb0d5be921
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 209 additions and 8 deletions

View file

@ -92,6 +92,7 @@ add_library(lego1 SHARED
LEGO1/legoworldpresenter.cpp LEGO1/legoworldpresenter.cpp
LEGO1/motorcycle.cpp LEGO1/motorcycle.cpp
LEGO1/mxatomid.cpp LEGO1/mxatomid.cpp
LEGO1/mxatomidcounter.cpp
LEGO1/mxaudiopresenter.cpp LEGO1/mxaudiopresenter.cpp
LEGO1/mxautolocker.cpp LEGO1/mxautolocker.cpp
LEGO1/mxbackgroundaudiomanager.cpp LEGO1/mxbackgroundaudiomanager.cpp

View file

@ -14,6 +14,11 @@
// DIsable "nonstandard extension used : 'bool'" warning spam // DIsable "nonstandard extension used : 'bool'" warning spam
#pragma warning( disable : 4237 ) #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 #define MSVC420_VERSION 1020
// STL compatibility. // STL compatibility.
@ -22,6 +27,7 @@
#else #else
#include <algorithm> #include <algorithm>
#include <list> #include <list>
#include <set>
using namespace std; using namespace std;
#endif #endif

View file

@ -1,20 +1,97 @@
#include "mxatomid.h" #include "mxatomid.h"
#include "mxomni.h"
// OFFSET: LEGO1 0x100acf90 // 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 // OFFSET: LEGO1 0x100acfd0
MxAtomId::~MxAtomId() 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 // 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; 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;
}

View file

@ -2,6 +2,7 @@
#define MXATOMID_H #define MXATOMID_H
#include "mxtypes.h" #include "mxtypes.h"
#include "mxatomidcounter.h"
enum LookupMode enum LookupMode
{ {
@ -27,9 +28,13 @@ class MxAtomId
{ {
return this->m_internal == other.m_internal; return this->m_internal == other.m_internal;
} }
void Clear();
private: private:
char *m_internal; MxAtomIdCounter* GetCounter(const char *, LookupMode);
void Destroy();
const char *m_internal;
}; };
#endif // MXATOMID_H #endif // MXATOMID_H

18
LEGO1/mxatomidcounter.cpp Normal file
View file

@ -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--;
}

49
LEGO1/mxatomidcounter.h Normal file
View file

@ -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 <map> seems fit for purpose here:
// We have an MxString as a key and MxU16 as the value.
// And yet a <set> is the best match. The malloc in MxOmni::Create
// for the _Nil node asks for more bytes than a regular node if a <map>
// is used, but all nodes are 20 bytes wide with a <set>.
// Also: the increment/decrement methods suggest a custom type was used
// for the combined key_value_pair, which doesn't seem possible with <map>.
// 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<MxAtomIdCounter*, MxAtomIdCounterCompare>
{};
#endif //MXATOMIDCOUNTER_H

View file

@ -38,7 +38,7 @@ void MxOmni::Init()
m_eventManager = NULL; m_eventManager = NULL;
m_timer = NULL; m_timer = NULL;
m_streamer = NULL; m_streamer = NULL;
m_unk44 = NULL; m_atomIdCounterSet = NULL;
m_unk64 = NULL; m_unk64 = NULL;
} }
@ -104,6 +104,8 @@ void MxOmni::SetInstance(MxOmni *instance)
// OFFSET: LEGO1 0x100af0c0 // OFFSET: LEGO1 0x100af0c0
MxResult MxOmni::Create(MxOmniCreateParam &p) MxResult MxOmni::Create(MxOmniCreateParam &p)
{ {
m_atomIdCounterSet = new MxAtomIdCounterSet();
if (p.CreateFlags().CreateVariableTable()) if (p.CreateFlags().CreateVariableTable())
{ {
MxVariableTable *variableTable = new MxVariableTable(); MxVariableTable *variableTable = new MxVariableTable();
@ -129,6 +131,40 @@ MxResult MxOmni::Create(MxOmniCreateParam &p)
void MxOmni::Destroy() void MxOmni::Destroy()
{ {
// FIXME: Stub // 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 // OFFSET: LEGO1 0x100b07f0
@ -162,6 +198,12 @@ MxTimer *Timer()
return MxOmni::GetInstance()->GetTimer(); return MxOmni::GetInstance()->GetTimer();
} }
// OFFSET: LEGO1 0x100acee0
MxAtomIdCounterSet *AtomIdCounterSet()
{
return MxOmni::GetInstance()->GetAtomIdCounterSet();
}
// OFFSET: LEGO1 0x100acef0 // OFFSET: LEGO1 0x100acef0
MxStreamer* Streamer() MxStreamer* Streamer()
{ {

View file

@ -14,6 +14,7 @@
#include "mxtimer.h" #include "mxtimer.h"
#include "mxvariabletable.h" #include "mxvariabletable.h"
#include "mxvideomanager.h" #include "mxvideomanager.h"
#include "mxatomidcounter.h"
// VTABLE 0x100dc168 // VTABLE 0x100dc168
// SIZE 0x68 // SIZE 0x68
@ -47,6 +48,7 @@ class MxOmni : public MxCore
MxVariableTable* GetVariableTable() const { return this->m_variableTable; } MxVariableTable* GetVariableTable() const { return this->m_variableTable; }
MxMusicManager* GetMusicManager() const { return this->m_musicManager; } MxMusicManager* GetMusicManager() const { return this->m_musicManager; }
MxEventManager* GetEventManager() const { return this->m_eventManager; } MxEventManager* GetEventManager() const { return this->m_eventManager; }
MxAtomIdCounterSet* GetAtomIdCounterSet() const { return this->m_atomIdCounterSet; }
protected: protected:
static MxOmni* g_instance; static MxOmni* g_instance;
@ -63,7 +65,7 @@ class MxOmni : public MxCore
MxTimer* m_timer; //0x3C MxTimer* m_timer; //0x3C
MxStreamer* m_streamer; //0x40 MxStreamer* m_streamer; //0x40
int m_unk44; // 0x44 MxAtomIdCounterSet* m_atomIdCounterSet; // 0x44
MxCriticalSection m_criticalsection; // 0x48 MxCriticalSection m_criticalsection; // 0x48
@ -78,5 +80,6 @@ __declspec(dllexport) MxMusicManager * MusicManager();
__declspec(dllexport) MxEventManager * EventManager(); __declspec(dllexport) MxEventManager * EventManager();
__declspec(dllexport) MxNotificationManager * NotificationManager(); __declspec(dllexport) MxNotificationManager * NotificationManager();
MxVideoManager * MVideoManager(); MxVideoManager * MVideoManager();
MxAtomIdCounterSet* AtomIdCounterSet();
#endif // MXOMNI_H #endif // MXOMNI_H