diff --git a/CMakeLists.txt b/CMakeLists.txt index fa4de1e5..74d5a6bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,7 @@ add_library(lego1 SHARED LEGO1/mxaudiopresenter.cpp LEGO1/mxautolocker.cpp LEGO1/mxbackgroundaudiomanager.cpp + LEGO1/mxbinarytree.cpp LEGO1/mxbitmap.cpp LEGO1/mxcompositemediapresenter.cpp LEGO1/mxcompositepresenter.cpp diff --git a/LEGO1/mxatomid.cpp b/LEGO1/mxatomid.cpp index 007e2de3..31619427 100644 --- a/LEGO1/mxatomid.cpp +++ b/LEGO1/mxatomid.cpp @@ -1,9 +1,18 @@ #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 (!AtomIdTree()) + return; + + TreeValue *value = try_to_open(p_str, p_mode); + m_internal = value->m_str.GetData(); + value->RefCountInc(); } // OFFSET: LEGO1 0x100acfd0 @@ -18,3 +27,46 @@ MxAtomId &MxAtomId::operator=(const MxAtomId &id) // TODO return *this; } + +// OFFSET: LEGO1 0x100ad210 +TreeValue *MxAtomId::try_to_open(const char *p_str, LookupMode p_mode) +{ + TreeValue *value = new TreeValue(p_str); + TreeNode *node; + + switch (p_mode) { + case LookupMode_LowerCase: + case LookupMode_LowerCase2: + value->m_str.ToLowerCase(); + break; + case LookupMode_UpperCase: + value->m_str.ToUpperCase(); + break; + } + + MxBinaryTree *tree = AtomIdTree(); + // get the closest node that matches the given value + node = tree->Search(value); + + // pointer reuse??? + TreeNode *ptr_reuse = node; + + // is the node an exact match? + if (tree->m_root == node || + strcmp(value->m_str.GetData(), node->m_value->m_str.GetData()) > 0) { + ptr_reuse = tree->m_root; + } + + ptr_reuse = ptr_reuse->m_child0; + if (ptr_reuse == AtomIdTree()->m_root) { + delete value; + value = ptr_reuse->m_value; + } else { + + } + + // LAB_100ad42b + + + return value; +} diff --git a/LEGO1/mxatomid.h b/LEGO1/mxatomid.h index 4a2082cf..71c9f434 100644 --- a/LEGO1/mxatomid.h +++ b/LEGO1/mxatomid.h @@ -1,6 +1,8 @@ #ifndef MXATOMID_H #define MXATOMID_H +#include "mxbinarytree.h" +#include "mxstring.h" #include "mxtypes.h" enum LookupMode @@ -11,6 +13,7 @@ enum LookupMode LookupMode_LowerCase2 = 3 }; +// SIZE 0x4 class MxAtomId { public: @@ -28,8 +31,11 @@ class MxAtomId return this->m_internal == other.m_internal; } + // TODO: belongs here? + TreeValue *try_to_open(const char *, LookupMode); + private: - char *m_internal; + const char *m_internal; }; #endif // MXATOMID_H diff --git a/LEGO1/mxbinarytree.cpp b/LEGO1/mxbinarytree.cpp new file mode 100644 index 00000000..9e6eed81 --- /dev/null +++ b/LEGO1/mxbinarytree.cpp @@ -0,0 +1,240 @@ +#include "mxbinarytree.h" + +// 0x101013f0 +TreeNode *MxBinaryTree::g_Node_Nil = NULL; + +inline void MxBinaryTree::LeftRotate(TreeNode *x) +{ + TreeNode *y = x->m_child1; + x->m_child1 = y->m_child0; + + if (y->m_child0 != g_Node_Nil) + y->m_child0->m_parent = x; + + y->m_parent = x->m_parent; + + if (m_root->m_parent != x) { + if (x == x->m_parent->m_child0) { + x->m_parent->m_child0 = y; + y->m_child0 = x; //? + x->m_parent = y; //? + } else { + x->m_parent->m_child1 = y; + y->m_child1 = x; //? + x->m_parent = y; //? + } + } else { + // ? + y->m_child0 = x; + x->m_parent = y; + } +} + + +inline void MxBinaryTree::RightRotate(TreeNode *x) +{ + TreeNode *y = x->m_child0; + x->m_child0 = y->m_child1; + + if (y->m_child1 != g_Node_Nil) + y->m_child1->m_parent = x; + + y->m_parent = x->m_parent; + + if (m_root->m_parent != x) { + if (x == x->m_parent->m_child1) { + x->m_parent->m_child1 = y; + y->m_child1 = x; //? + x->m_parent = y; //? + } else { + x->m_parent->m_child0 = y; + y->m_child0 = x; //? + x->m_parent = y; //? + } + } else { + //? + y->m_child1 = x; + x->m_parent = y; + } +} + +// OFFSET: LEGO1 0x100ad480 +void mini_walk(TreeNode* &p_node) +{ + if (p_node->m_color == NODE_COLOR_RED + && p_node->m_parent->m_parent == p_node) { + p_node = p_node->m_child1; + return; + } + + TreeNode *t = p_node->m_child0; + if (t != MxBinaryTree::g_Node_Nil) { + + // wonky + while (1) { + if (t->m_child1 == MxBinaryTree::g_Node_Nil) + break; + t = t->m_child1; + } + + p_node = t; + return; + } + + TreeNode *u = p_node->m_parent; + TreeNode *v = p_node; + while (u != v) { + p_node = u; + v = u; + u = u->m_parent; + } + + p_node = u; +} + +// OFFSET: LEGO1 0x100ad4d0 +void MxBinaryTree::FUN_100ad4d0(TreeNode **p_output, TreeNode *p_leaf, TreeNode *p_parent, TreeValue *&p_value) +{ + TreeNode *node = new TreeNode(p_parent, NODE_COLOR_RED); + node->m_child0 = g_Node_Nil; + node->m_child1 = g_Node_Nil; + + // TODO: ??? + if (node->m_value) { + node->m_value = p_value; + } + + this->m_p3++; // node count? + + // if tree is NOT empty + // if param_2 is tree_nil (always true I think?) + // + if (this->m_root != p_parent + && p_leaf == MxBinaryTree::g_Node_Nil + && strcmp(p_value->m_str.GetData(), + p_parent->m_value->m_str.GetData()) <= 0) { + p_parent->m_child1 = node; + + if (m_root->m_child1 == p_parent) + m_root->m_child1 = node; + } else { + p_parent->m_child0 = node; + + if (m_root != p_parent && m_root->m_child0 == p_parent) { + m_root->m_child0 = p_parent; + } else { + m_root->m_parent = node; + m_root->m_child1 = node; + } + } + + // LAB_100ad593 + // rebalance?? + TreeNode *cur = node; + while (m_root->m_parent != cur) { + TreeNode *parent = cur->m_parent; + + if (parent->m_color != NODE_COLOR_RED) + break; + + TreeNode *grandparent = parent->m_parent; + TreeNode *uncle = grandparent->m_child0; + + if (uncle == parent) { + // wrong uncle + uncle = grandparent->m_child1; + + if (uncle->m_color != NODE_COLOR_RED) { + + // 100ad5d3 + if (parent->m_child1 == cur) { + cur = parent; + LeftRotate(cur); + // rotate. + /* + parent->m_child1 = parent->m_child1->m_child0; + + // 100ad5e2 + if (parent->m_child1 != g_Node_Nil) + parent->m_child1->m_parent = cur; + + parent->m_child1->m_parent = grandparent; + if (m_root->m_parent == cur || grandparent->m_child0 == cur) + grandparent->m_child1 = parent; + else + grandparent->m_child0 = parent; + + parent->m_child0 = cur; + grandparent->m_child0 = parent; + */ + } + + // LAB_100ad60f + cur->m_parent->m_color = NODE_COLOR_BLACK; + cur->m_parent->m_parent->m_color = NODE_COLOR_RED; + RightRotate(cur->m_parent->m_parent); + continue; + } + } else { + // LAB_100ad67f + if (uncle->m_color != NODE_COLOR_RED) { + if (parent->m_child0 == cur) { + cur = parent; + RightRotate(cur); + } + + // LAB_100ad60f + cur->m_parent->m_color = NODE_COLOR_BLACK; + cur->m_parent->m_parent->m_color = NODE_COLOR_RED; + LeftRotate(cur->m_parent->m_parent); + continue; + } + } + + // LAB_100ad72c + parent->m_color = NODE_COLOR_BLACK; + uncle->m_color = NODE_COLOR_BLACK; + parent->m_parent->m_color = NODE_COLOR_RED; + + cur = parent->m_parent; + } + + m_root->m_parent->m_color = NODE_COLOR_BLACK; + *p_output = node; +} + +// OFFSET: LEGO1 0x100ad780 +TreeNode *MxBinaryTree::Search(TreeValue*& p_value) +{ + TreeNode *node_match = m_root; + TreeNode *t_node = node_match->m_parent; + + while (t_node != g_Node_Nil) { + int result = strcmp(t_node->m_value->m_str.GetData(), p_value->m_str.GetData()); + + if (result <= 0) { + // if strings equal or less than search string + // closest match? + // it either does match or is where we will insert the new node. + node_match = t_node; + t_node = t_node->m_child0; + } else { + t_node = t_node->m_child1; + } + } + + return node_match; +} + +// OFFSET: LEGO1 0x100ad7f0 +void TreeValue::RefCountInc() +{ + m_t0++; +} + +// OFFSET: LEGO1 0x100ad800 +void TreeValue::RefCountDec() +{ + if (m_t0) + m_t0--; +} diff --git a/LEGO1/mxbinarytree.h b/LEGO1/mxbinarytree.h new file mode 100644 index 00000000..ac02c1e5 --- /dev/null +++ b/LEGO1/mxbinarytree.h @@ -0,0 +1,71 @@ +#ifndef MXBINARYTREE_H +#define MXBINARYTREE_H + +#include "mxstring.h" + +// TODO: enum instead? +#define NODE_COLOR_RED 0 +#define NODE_COLOR_BLACK 1 + +// SIZE 0x14 +class TreeValue { +public: + TreeValue(const char *p_str) + { + m_str = p_str; + m_t0 = 0; + } + + void RefCountInc(); + void RefCountDec(); + + MxString m_str; + MxU16 m_t0; + MxU16 m_t1; +}; + +// SIZE 0x14 +class TreeNode { +public: + TreeNode(TreeNode *p_parent, int p_color) + { + m_parent = p_parent; + m_color = p_color; + } + + TreeNode *m_child0; // +0 // string sorts after + TreeNode *m_parent; // +4 // parent node + TreeNode *m_child1; // +8 // string sorts before + TreeValue *m_value; // +c + int m_color; // +10 // BLACK or RED. +}; + +// SIZE 0x10 +class MxBinaryTree +{ +public: + // Dummy node to represent null value. + static TreeNode *g_Node_Nil; + MxBinaryTree() + { + if (!g_Node_Nil) { + g_Node_Nil = new TreeNode(NULL, NODE_COLOR_BLACK); + g_Node_Nil->m_child0 = NULL; + g_Node_Nil->m_child1 = NULL; + } + + m_root = new TreeNode(g_Node_Nil, NODE_COLOR_RED); + } + + void LeftRotate(TreeNode *); + void RightRotate(TreeNode *); + void FUN_100ad4d0(TreeNode **, TreeNode *, TreeNode *, TreeValue *&); + TreeNode *Search(TreeValue *&); + + int m_p0; // +0 + TreeNode *m_root; // +4 + int m_p2; // +8 + int m_p3; // +c +}; + +#endif //MXBINARYTREE_H \ No newline at end of file diff --git a/LEGO1/mxomni.cpp b/LEGO1/mxomni.cpp index 10e8eccb..fb4fcaa1 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_atomIdTree = NULL; m_unk64 = NULL; } @@ -104,6 +104,8 @@ void MxOmni::SetInstance(MxOmni *instance) // OFFSET: LEGO1 0x100af0c0 MxResult MxOmni::Create(MxOmniCreateParam &p) { + m_atomIdTree = new MxBinaryTree(); + if (p.CreateFlags().CreateTimer()) { MxTimer *timer = new MxTimer(); @@ -153,6 +155,12 @@ MxTimer *Timer() return MxOmni::GetInstance()->GetTimer(); } +// OFFSET: LEGO1 0x100acee0 +MxBinaryTree *AtomIdTree() +{ + return MxOmni::GetInstance()->GetAtomIdTree(); +} + // OFFSET: LEGO1 0x100acef0 MxStreamer* Streamer() { diff --git a/LEGO1/mxomni.h b/LEGO1/mxomni.h index b09d11ad..f757a569 100644 --- a/LEGO1/mxomni.h +++ b/LEGO1/mxomni.h @@ -14,6 +14,7 @@ #include "mxtimer.h" #include "mxvariabletable.h" #include "mxvideomanager.h" +#include "mxbinarytree.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; } + MxBinaryTree* GetAtomIdTree() const { return this->m_atomIdTree; } 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 + MxBinaryTree *m_atomIdTree; // 0x44 MxCriticalSection m_criticalsection; // 0x48 @@ -78,5 +80,6 @@ __declspec(dllexport) MxMusicManager * MusicManager(); __declspec(dllexport) MxEventManager * EventManager(); __declspec(dllexport) MxNotificationManager * NotificationManager(); MxVideoManager * MVideoManager(); +MxBinaryTree *AtomIdTree(); #endif // MXOMNI_H