From 9ac9fe276186efea2a002abdcbb8ed1b466b16fe Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Tue, 31 Oct 2023 11:30:13 -0400 Subject: [PATCH] MxRegion: initial implementation (#218) * MxRegion structures * MxRegion structures * Remove junk * Refactor * WIP * Use MxRect32 ctor * Refactor * Add MxSize32, match MxRegion ctor * Add two template annotations * Fix missing instructions * Fix another bug * Refactor * Add GetPoint * Implement/match MxRegionTopBottom::MxRegionTopBottom * Implement/match more functions * More implementation * Don't expose internal match * Fix indent * Add template annotations * Implement remaining functions * Fix comment * Match loops * Simplify function * Merge * Remove junk * Format * Format * match MxRegion::vtable1c * revert vtable1c match-hack This reverts commit 7b886259882e2d04444546c1323734ec3c243cf6. --------- Co-authored-by: Ramen2X <64166386+Ramen2X@users.noreply.github.com> --- CMakeLists.txt | 1 + LEGO1/mxlist.h | 36 +++++++ LEGO1/mxrect32.h | 32 +++++- LEGO1/mxregion.cpp | 232 +++++++++++++++++++++++++++++++++++------ LEGO1/mxregion.h | 35 ++++++- LEGO1/mxregionlist.cpp | 19 ++++ LEGO1/mxregionlist.h | 56 ++++++++++ LEGO1/mxsize32.h | 19 ++++ 8 files changed, 393 insertions(+), 37 deletions(-) create mode 100644 LEGO1/mxregionlist.cpp create mode 100644 LEGO1/mxregionlist.h create mode 100644 LEGO1/mxsize32.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 14882a1e..f63e39e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,7 @@ add_library(lego1 SHARED LEGO1/mxramstreamcontroller.cpp LEGO1/mxramstreamprovider.cpp LEGO1/mxregion.cpp + LEGO1/mxregionlist.cpp LEGO1/mxscheduler.cpp LEGO1/mxsemaphore.cpp LEGO1/mxsmkpresenter.cpp diff --git a/LEGO1/mxlist.h b/LEGO1/mxlist.h index f6093f60..ed66f9fc 100644 --- a/LEGO1/mxlist.h +++ b/LEGO1/mxlist.h @@ -97,10 +97,15 @@ class MxListCursor : public MxCore { MxBool Find(T p_obj); void Detach(); + void Destroy(); MxBool Next(T& p_obj); + MxBool Current(T& p_obj); + void Advance(); + MxBool HasMatch() { return m_match != NULL; } void SetValue(T p_obj); void Head() { m_match = m_list->m_first; } void Reset() { m_match = NULL; } + void Prepend(T p_newobj); private: MxList* m_list; @@ -215,6 +220,12 @@ inline void MxListCursor::Detach() m_match = NULL; } +template +inline void MxListCursor::Destroy() +{ + m_list->m_customDestructor(m_match->GetValue()); +} + template inline MxBool MxListCursor::Next(T& p_obj) { @@ -229,6 +240,24 @@ inline MxBool MxListCursor::Next(T& p_obj) return m_match != NULL; } +template +inline MxBool MxListCursor::Current(T& p_obj) +{ + if (m_match) + p_obj = m_match->GetValue(); + + return m_match != NULL; +} + +template +inline void MxListCursor::Advance() +{ + if (!m_match) + m_match = m_list->m_first; + else + m_match = m_match->m_next; +} + template inline void MxListCursor::SetValue(T p_obj) { @@ -236,4 +265,11 @@ inline void MxListCursor::SetValue(T p_obj) m_match->m_obj = p_obj; } +template +inline void MxListCursor::Prepend(T p_newobj) +{ + if (m_match) + m_list->_InsertEntry(p_newobj, m_match->m_prev, m_match); +} + #endif // MXLIST_H diff --git a/LEGO1/mxrect32.h b/LEGO1/mxrect32.h index c3381d46..0ee1bc9c 100644 --- a/LEGO1/mxrect32.h +++ b/LEGO1/mxrect32.h @@ -1,6 +1,9 @@ #ifndef MXRECT32_H #define MXRECT32_H +#include "mxpoint32.h" +#include "mxsize32.h" + class MxRect32 { public: MxRect32() {} @@ -12,14 +15,35 @@ class MxRect32 { this->m_bottom = p_bottom; } + MxRect32(const MxPoint32& p_point, const MxSize32& p_size) + { + this->m_left = p_point.m_x; + this->m_top = p_point.m_y; + this->m_right = p_size.m_width; + this->m_bottom = p_size.m_height; + } + + inline void SetPoint(const MxPoint32& p_point) + { + this->m_left = p_point.m_x; + this->m_top = p_point.m_y; + } + + inline void SetSize(const MxSize32& p_size) + { + this->m_right = p_size.m_width; + this->m_bottom = p_size.m_height; + } + + inline MxS32 GetWidth() { return (m_right - m_left) + 1; } + inline MxS32 GetHeight() { return (m_bottom - m_top) + 1; } + + inline MxPoint32 GetPoint() { return MxPoint32(this->m_left, this->m_top); } + MxS32 m_left; MxS32 m_top; MxS32 m_right; MxS32 m_bottom; - - inline MxS32 GetWidth() { return (m_right - m_left) + 1; } - - inline MxS32 GetHeight() { return (m_bottom - m_top) + 1; } }; #endif // MXRECT32_H diff --git a/LEGO1/mxregion.cpp b/LEGO1/mxregion.cpp index 5b7237cb..bd2a5f5a 100644 --- a/LEGO1/mxregion.cpp +++ b/LEGO1/mxregion.cpp @@ -1,40 +1,214 @@ #include "mxregion.h" -DECOMP_SIZE_ASSERT(MxRegion, 0x1c); +#include -// OFFSET: LEGO1 0x100c31c0 STUB +DECOMP_SIZE_ASSERT(MxRegion, 0x1c); +DECOMP_SIZE_ASSERT(MxRegionTopBottom, 0x0c); +DECOMP_SIZE_ASSERT(MxRegionLeftRight, 0x08); + +// OFFSET: LEGO1 0x100c31c0 MxRegion::MxRegion() { - // TODO + m_list = new MxRegionList; + m_rect.SetPoint(MxPoint32(INT_MAX, INT_MAX)); + m_rect.SetSize(MxSize32(-1, -1)); } -// OFFSET: LEGO1 0x100c3690 STUB -MxRegion::~MxRegion() -{ - // TODO -} - -// OFFSET: LEGO1 0x100c3700 STUB -void MxRegion::Reset() -{ - // TODO -} - -// OFFSET: LEGO1 0x100c3750 STUB -void MxRegion::vtable18(MxRect32& p_rect) -{ - // TODO -} - -// OFFSET: LEGO1 0x100c3e20 STUB -void MxRegion::vtable1c() -{ - // TODO -} - -// OFFSET: LEGO1 0x100c3660 STUB +// OFFSET: LEGO1 0x100c3660 MxBool MxRegion::vtable20() { - // TODO + return m_list->GetCount() == 0; +} + +// OFFSET: LEGO1 0x100c3690 +MxRegion::~MxRegion() +{ + if (m_list) + delete m_list; +} + +// OFFSET: LEGO1 0x100c3700 +void MxRegion::Reset() +{ + m_list->DeleteAll(); + m_rect.SetPoint(MxPoint32(INT_MAX, INT_MAX)); + m_rect.SetSize(MxSize32(-1, -1)); +} + +// OFFSET: LEGO1 0x100c3750 +void MxRegion::vtable18(MxRect32& p_rect) +{ + MxRect32 rectCopy(p_rect.GetPoint(), MxSize32(p_rect.m_right, p_rect.m_bottom)); + MxRegionListCursor cursor(m_list); + + if (rectCopy.m_left < rectCopy.m_right) { + while (rectCopy.m_top < rectCopy.m_bottom) { + MxRegionTopBottom* topBottom; + if (!cursor.Next(topBottom)) + break; + + if (topBottom->m_top >= rectCopy.m_bottom) { + cursor.Prepend(new MxRegionTopBottom(rectCopy)); + rectCopy.m_top = rectCopy.m_bottom; + } + else if (rectCopy.m_top < topBottom->m_bottom) { + if (rectCopy.m_top < topBottom->m_top) { + MxRect32 topBottomRect(rectCopy.GetPoint(), MxSize32(rectCopy.m_right, topBottom->m_top)); + + cursor.Prepend(new MxRegionTopBottom(topBottomRect)); + rectCopy.m_top = topBottom->m_top; + } + else if (topBottom->m_top < rectCopy.m_top) { + MxRegionTopBottom* newTopBottom = topBottom->Clone(); + newTopBottom->m_bottom = rectCopy.m_top; + topBottom->m_top = rectCopy.m_top; + cursor.Prepend(newTopBottom); + } + + if (rectCopy.m_bottom < topBottom->m_bottom) { + MxRegionTopBottom* newTopBottom = topBottom->Clone(); + newTopBottom->m_bottom = rectCopy.m_bottom; + topBottom->m_top = rectCopy.m_bottom; + newTopBottom->FUN_100c5280(rectCopy.m_left, rectCopy.m_right); + // TODO: _InsertEntry currently inlined, shouldn't be + cursor.Prepend(newTopBottom); + rectCopy.m_top = rectCopy.m_bottom; + } + else { + topBottom->FUN_100c5280(rectCopy.m_left, rectCopy.m_right); + rectCopy.m_top = topBottom->m_bottom; + } + } + + if (rectCopy.m_right <= rectCopy.m_left) + break; + } + } + + if (rectCopy.m_left < rectCopy.m_right && rectCopy.m_top < rectCopy.m_bottom) { + MxRegionTopBottom* newTopBottom = new MxRegionTopBottom(rectCopy); + m_list->OtherAppend(newTopBottom); + } + + m_rect.m_left = m_rect.m_left <= p_rect.m_left ? m_rect.m_left : p_rect.m_left; + m_rect.m_top = m_rect.m_top <= p_rect.m_top ? m_rect.m_top : p_rect.m_top; + m_rect.m_right = m_rect.m_right <= p_rect.m_right ? p_rect.m_right : m_rect.m_right; + m_rect.m_bottom = m_rect.m_bottom <= p_rect.m_bottom ? p_rect.m_bottom : m_rect.m_bottom; +} + +// OFFSET: LEGO1 0x100c3e20 +MxBool MxRegion::vtable1c(MxRect32& p_rect) +{ + if (m_rect.m_left >= p_rect.m_right || p_rect.m_left >= m_rect.m_right || m_rect.m_top >= p_rect.m_bottom || + p_rect.m_top >= m_rect.m_bottom) + return FALSE; + + MxRegionListCursor cursor(m_list); + MxRegionTopBottom* topBottom; + + while (cursor.Next(topBottom)) { + if (topBottom->m_top >= p_rect.m_bottom) + return FALSE; + if (topBottom->m_bottom > p_rect.m_top && topBottom->FUN_100c57b0(p_rect)) + return TRUE; + } + + return FALSE; +} + +// OFFSET: LEGO1 0x100c4c90 +MxRegionTopBottom::MxRegionTopBottom(MxS32 p_top, MxS32 p_bottom) +{ + m_top = p_top; + m_bottom = p_bottom; + m_leftRightList = new MxRegionLeftRightList; +} + +// OFFSET: LEGO1 0x100c50e0 +MxRegionTopBottom::MxRegionTopBottom(MxRect32& p_rect) +{ + m_top = p_rect.m_top; + m_bottom = p_rect.m_bottom; + m_leftRightList = new MxRegionLeftRightList; + + MxRegionLeftRight* leftRight = new MxRegionLeftRight(p_rect.m_left, p_rect.m_right); + m_leftRightList->Append(leftRight); +} + +// OFFSET: LEGO1 0x100c5280 +void MxRegionTopBottom::FUN_100c5280(MxS32 p_left, MxS32 p_right) +{ + MxRegionLeftRightListCursor a(m_leftRightList); + MxRegionLeftRightListCursor b(m_leftRightList); + + MxRegionLeftRight* leftRight; + while (a.Next(leftRight) && leftRight->m_right < p_left) + ; + + if (!a.HasMatch()) { + MxRegionLeftRight* copy = new MxRegionLeftRight(p_left, p_right); + m_leftRightList->OtherAppend(copy); + } + else { + if (p_left > leftRight->m_left) + p_left = leftRight->m_left; + + while (leftRight->m_left < p_right) { + if (p_right < leftRight->m_right) + p_right = leftRight->m_right; + + // TODO: Currently inlined, shouldn't be + b = a; + b.Advance(); + + if (a.HasMatch()) { + a.Destroy(); + a.Detach(); + } + + if (!b.Current(leftRight)) + break; + + a = b; + } + + if (a.HasMatch()) { + MxRegionLeftRight* copy = new MxRegionLeftRight(p_left, p_right); + a.Prepend(copy); + } + else { + MxRegionLeftRight* copy = new MxRegionLeftRight(p_left, p_right); + m_leftRightList->OtherAppend(copy); + } + } +} + +// OFFSET: LEGO1 0x100c55d0 +MxRegionTopBottom* MxRegionTopBottom::Clone() +{ + MxRegionTopBottom* clone = new MxRegionTopBottom(m_top, m_bottom); + + MxRegionLeftRightListCursor cursor(m_leftRightList); + MxRegionLeftRight* leftRight; + + while (cursor.Next(leftRight)) + clone->m_leftRightList->Append(leftRight->Clone()); + + return clone; +} + +// OFFSET: LEGO1 0x100c57b0 +MxBool MxRegionTopBottom::FUN_100c57b0(MxRect32& p_rect) +{ + MxRegionLeftRightListCursor cursor(m_leftRightList); + MxRegionLeftRight* leftRight; + + while (cursor.Next(leftRight)) { + if (p_rect.m_right <= leftRight->m_left) + return FALSE; + if (leftRight->m_right > p_rect.m_left) + return TRUE; + } + return FALSE; } diff --git a/LEGO1/mxregion.h b/LEGO1/mxregion.h index 25482207..4317d836 100644 --- a/LEGO1/mxregion.h +++ b/LEGO1/mxregion.h @@ -4,6 +4,35 @@ #include "decomp.h" #include "mxcore.h" #include "mxrect32.h" +#include "mxregionlist.h" + +// SIZE 0x0c +struct MxRegionTopBottom { + MxRegionTopBottom(MxRect32& p_rect); + MxRegionTopBottom(MxS32 m_top, MxS32 m_bottom); + + MxRegionTopBottom* Clone(); + void FUN_100c5280(MxS32 p_left, MxS32 p_right); + MxBool FUN_100c57b0(MxRect32& p_rect); + + MxS32 m_top; + MxS32 m_bottom; + MxRegionLeftRightList* m_leftRightList; +}; + +// SIZE 0x08 +struct MxRegionLeftRight { + MxRegionLeftRight(MxS32 p_left, MxS32 p_right) + { + m_left = p_left; + m_right = p_right; + } + + MxRegionLeftRight* Clone() { return new MxRegionLeftRight(m_left, m_right); } + + MxS32 m_left; + MxS32 m_right; +}; // VTABLE 0x100dcae8 // SIZE 0x1c @@ -14,15 +43,13 @@ class MxRegion : public MxCore { virtual void Reset(); virtual void vtable18(MxRect32& p_rect); - virtual void vtable1c(); + virtual MxBool vtable1c(MxRect32& p_rect); virtual MxBool vtable20(); inline MxRect32& GetRect() { return this->m_rect; } private: - // A container (probably MxList) holding MxRect32 - // MxList *m_rects; - undefined4 m_unk08; + MxRegionList* m_list; MxRect32 m_rect; }; diff --git a/LEGO1/mxregionlist.cpp b/LEGO1/mxregionlist.cpp new file mode 100644 index 00000000..a33d78b7 --- /dev/null +++ b/LEGO1/mxregionlist.cpp @@ -0,0 +1,19 @@ +#include "mxregionlist.h" + +#include "mxregion.h" + +// OFFSET: LEGO1 0x100c33e0 +void MxRegionListParent::Destroy(MxRegionTopBottom* p_topBottom) +{ + if (p_topBottom) { + if (p_topBottom->m_leftRightList) + delete p_topBottom->m_leftRightList; + delete p_topBottom; + } +} + +// OFFSET: LEGO1 0x100c4e80 +void MxRegionLeftRightListParent::Destroy(MxRegionLeftRight* p_leftRight) +{ + delete p_leftRight; +} diff --git a/LEGO1/mxregionlist.h b/LEGO1/mxregionlist.h new file mode 100644 index 00000000..52336d38 --- /dev/null +++ b/LEGO1/mxregionlist.h @@ -0,0 +1,56 @@ +#ifndef MXREGIONLIST_H +#define MXREGIONLIST_H + +#include "mxlist.h" + +struct MxRegionTopBottom; +struct MxRegionLeftRight; + +// VTABLE 0x100dcb40 +// SIZE 0x18 +class MxRegionListParent : public MxList { +public: + static void Destroy(MxRegionTopBottom* p_topBottom); + + MxRegionListParent() { m_customDestructor = Destroy; } +}; + +// VTABLE 0x100dcb58 +// SIZE 0x18 +class MxRegionList : public MxRegionListParent {}; + +// VTABLE 0x100dcb88 +typedef MxListCursorChildChild MxRegionListCursor; + +// OFFSET: LEGO1 0x100c5970 TEMPLATE +// MxList::_InsertEntry + +// OFFSET: LEGO1 0x100c5a20 TEMPLATE +// MxListEntry::MxListEntry + +// VTABLE 0x100dcc70 +// SIZE 0x18 +class MxRegionLeftRightListParent : public MxList { +public: + static void Destroy(MxRegionLeftRight* p_leftRight); + + MxRegionLeftRightListParent() { m_customDestructor = Destroy; } +}; + +// VTABLE 0x100dcc88 +// SIZE 0x18 +class MxRegionLeftRightList : public MxRegionLeftRightListParent {}; + +// VTABLE 0x100dcc10 +typedef MxListCursorChildChild MxRegionLeftRightListCursor; + +// OFFSET: LEGO1 0x100c54f0 TEMPLATE +// MxListCursor::MxListCursor + +// OFFSET: LEGO1 0x100c58c0 TEMPLATE +// MxList::_InsertEntry + +// OFFSET: LEGO1 0x100c5a40 TEMPLATE +// MxList::_DeleteEntry + +#endif // MXREGIONLIST_H diff --git a/LEGO1/mxsize32.h b/LEGO1/mxsize32.h new file mode 100644 index 00000000..a55326cc --- /dev/null +++ b/LEGO1/mxsize32.h @@ -0,0 +1,19 @@ +#ifndef MXSIZE32_H +#define MXSIZE32_H + +#include "mxtypes.h" + +class MxSize32 { +public: + MxSize32() {} + MxSize32(MxS32 p_width, MxS32 p_height) + { + this->m_width = p_width; + this->m_height = p_height; + } + + MxS32 m_width; + MxS32 m_height; +}; + +#endif // MXSIZE32_H