From e0e338ee449d4c9e741461676c4d618a1c33de86 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Mon, 3 Jul 2023 19:24:51 +0200 Subject: [PATCH 1/7] lego: add MxDSType enum, add it to all ctors, refactor `MxDSObject` (#73) * lego: add MxDSType enum, add to all ctors * refactor header * re-type members * add size assert for MxDSObject * fix Parse param * fix types --- LEGO1/mxatomid.h | 8 ++++---- LEGO1/mxdsaction.cpp | 3 ++- LEGO1/mxdsanim.cpp | 3 +-- LEGO1/mxdsevent.cpp | 7 +++---- LEGO1/mxdsmediaaction.cpp | 3 ++- LEGO1/mxdsmultiaction.cpp | 7 ++++--- LEGO1/mxdsobject.cpp | 16 ++++++++------- LEGO1/mxdsobject.h | 38 ++++++++++++++++++++---------------- LEGO1/mxdsobjectaction.cpp | 3 +-- LEGO1/mxdsparallelaction.cpp | 3 +-- LEGO1/mxdsselectaction.cpp | 7 ++++--- LEGO1/mxdsserialaction.cpp | 7 ++++--- LEGO1/mxdssound.cpp | 2 +- LEGO1/mxdsstill.cpp | 3 +-- LEGO1/mxdstypes.h | 20 +++++++++++++++++++ 15 files changed, 78 insertions(+), 52 deletions(-) create mode 100755 LEGO1/mxdstypes.h diff --git a/LEGO1/mxatomid.h b/LEGO1/mxatomid.h index 6b7dddbb..4a2082cf 100644 --- a/LEGO1/mxatomid.h +++ b/LEGO1/mxatomid.h @@ -5,10 +5,10 @@ enum LookupMode { - LookupMode_Exact = 0, - LookupMode_LowerCase = 1, - LookupMode_UpperCase = 2, - LookupMode_LowerCase2 = 3 + LookupMode_Exact = 0, + LookupMode_LowerCase = 1, + LookupMode_UpperCase = 2, + LookupMode_LowerCase2 = 3 }; class MxAtomId diff --git a/LEGO1/mxdsaction.cpp b/LEGO1/mxdsaction.cpp index a193ab21..e5162f89 100644 --- a/LEGO1/mxdsaction.cpp +++ b/LEGO1/mxdsaction.cpp @@ -4,9 +4,10 @@ MxDSAction::MxDSAction() { // TODO + this->SetType(MxDSType_Action); } -// OFFSET: LEGO1 0x100ada80 +// OFFSET: LEGO1 0x100ada80 STUB MxDSAction::~MxDSAction() { // TODO diff --git a/LEGO1/mxdsanim.cpp b/LEGO1/mxdsanim.cpp index 9a56d359..b9b887cd 100644 --- a/LEGO1/mxdsanim.cpp +++ b/LEGO1/mxdsanim.cpp @@ -3,11 +3,10 @@ // OFFSET: LEGO1 0x100c8ff0 MxDSAnim::MxDSAnim() { - // TODO + this->SetType(MxDSType_Anim); } // OFFSET: LEGO1 0x100c91a0 MxDSAnim::~MxDSAnim() { - // TODO } diff --git a/LEGO1/mxdsevent.cpp b/LEGO1/mxdsevent.cpp index a45ec93a..b6571075 100644 --- a/LEGO1/mxdsevent.cpp +++ b/LEGO1/mxdsevent.cpp @@ -1,13 +1,12 @@ #include "mxdsevent.h" -// OFFSET: LEGO1 0x100c95f0 STUB +// OFFSET: LEGO1 0x100c95f0 MxDSEvent::MxDSEvent() { - // TODO + this->SetType(MxDSType_Event); } -// OFFSET: LEGO1 0x100c97a0 STUB +// OFFSET: LEGO1 0x100c97a0 MxDSEvent::~MxDSEvent() { - // TODO } diff --git a/LEGO1/mxdsmediaaction.cpp b/LEGO1/mxdsmediaaction.cpp index 9587b152..5f9c1132 100644 --- a/LEGO1/mxdsmediaaction.cpp +++ b/LEGO1/mxdsmediaaction.cpp @@ -4,9 +4,10 @@ MxDSMediaAction::MxDSMediaAction() { // TODO + this->SetType(MxDSType_MediaAction); } -// OFFSET: LEGO1 0x100c8cf0 +// OFFSET: LEGO1 0x100c8cf0 STUB MxDSMediaAction::~MxDSMediaAction() { // TODO diff --git a/LEGO1/mxdsmultiaction.cpp b/LEGO1/mxdsmultiaction.cpp index 4b36f2ec..4188fb66 100644 --- a/LEGO1/mxdsmultiaction.cpp +++ b/LEGO1/mxdsmultiaction.cpp @@ -3,11 +3,12 @@ // OFFSET: LEGO1 0x100c9b90 MxDSMultiAction::MxDSMultiAction() { - + // TODO + this->SetType(MxDSType_MultiAction); } -// OFFSET: LEGO1 0x100ca060 +// OFFSET: LEGO1 0x100ca060 STUB MxDSMultiAction::~MxDSMultiAction() { - + // TODO } diff --git a/LEGO1/mxdsobject.cpp b/LEGO1/mxdsobject.cpp index abe8902f..2b478b88 100644 --- a/LEGO1/mxdsobject.cpp +++ b/LEGO1/mxdsobject.cpp @@ -3,10 +3,12 @@ #include #include +DECOMP_SIZE_ASSERT(MxDSObject, 0x2c); + // OFFSET: LEGO1 0x100bf6a0 MxDSObject::MxDSObject() { - this->m_unk0c = 0; + this->SetType(MxDSType_Object); this->m_sourceName = NULL; this->m_unk14 = 0; this->m_objectName = NULL; @@ -83,15 +85,15 @@ void MxDSObject::SetSourceName(const char *p_sourceName) } // OFFSET: LEGO1 0x100bf9c0 -int MxDSObject::unk14() +undefined4 MxDSObject::unk14() { return 10; } // OFFSET: LEGO1 0x100bf9d0 -unsigned int MxDSObject::CalculateUnk08() +MxU32 MxDSObject::CalculateUnk08() { - unsigned int unk08; + MxU32 unk08; if (this->m_sourceName) unk08 = strlen(this->m_sourceName) + 3; @@ -111,16 +113,16 @@ unsigned int MxDSObject::CalculateUnk08() } // OFFSET: LEGO1 0x100bfa20 -void MxDSObject::Parse(char **p_source, MxU16 p_unk24) +void MxDSObject::Parse(char **p_source, MxS16 p_unk24) { this->SetSourceName(*p_source); *p_source += strlen(this->m_sourceName) + 1; - this->m_unk14 = *(int*) *p_source; + this->m_unk14 = *(undefined4*) *p_source; *p_source += 4; this->SetObjectName(*p_source); *p_source += strlen(this->m_objectName) + 1; - this->m_unk1c = *(int*) *p_source; + this->m_unk1c = *(undefined4*) *p_source; *p_source += 4; this->m_unk24 = p_unk24; diff --git a/LEGO1/mxdsobject.h b/LEGO1/mxdsobject.h index ff7d4a02..a13f382f 100644 --- a/LEGO1/mxdsobject.h +++ b/LEGO1/mxdsobject.h @@ -1,55 +1,59 @@ #ifndef MXDSOBJECT_H #define MXDSOBJECT_H +#include "decomp.h" + #include "mxcore.h" #include "mxatomid.h" +#include "mxdstypes.h" // VTABLE 0x100dc868 // SIZE 0x2c class MxDSObject : public MxCore { public: - __declspec(dllexport) void SetObjectName(const char *p_objectName); - MxDSObject(); virtual ~MxDSObject() override; - MxDSObject &operator=(MxDSObject &p_dsObject); void CopyFrom(MxDSObject &p_dsObject); + MxDSObject &operator=(MxDSObject &p_dsObject); - // OFFSET: LEGO1 0x100bf730 + __declspec(dllexport) void SetObjectName(const char *p_objectName); + void SetSourceName(const char *p_sourceName); + + // OFFSET: LEGO1 0x100bf730 inline virtual const char *ClassName() const override { return "MxDSObject"; }; // vtable+0c // OFFSET: LEGO1 0x100bf740 inline virtual MxBool IsA(const char *name) const override { return !strcmp(name, MxDSObject::ClassName()) || MxCore::IsA(name); }; // vtable+10; - virtual int unk14(); // vtable+14; - virtual unsigned int CalculateUnk08(); // vtable+18; - virtual void Parse(char **p_source, MxU16 p_unk24); // vtable+1c; - - void SetSourceName(const char *p_sourceName); + virtual undefined4 unk14(); // vtable+14; + virtual MxU32 CalculateUnk08(); // vtable+18; + virtual void Parse(char **p_source, MxS16 p_unk24); // vtable+1c; inline const MxAtomId& GetAtomId() { return this->m_atomId; } - inline int GetUnknown1c() { return this->m_unk1c; } + inline undefined4 GetUnknown1c() { return this->m_unk1c; } - inline void SetUnknown1c(int p_unk1c) { this->m_unk1c = p_unk1c; } + inline void SetUnknown1c(undefined4 p_unk1c) { this->m_unk1c = p_unk1c; } inline void SetUnknown24(MxS16 p_unk24) { this->m_unk24 = p_unk24; } // OFFSET: ISLE 0x401c40 // OFFSET: LEGO1 0x10005530 inline void SetAtomId(MxAtomId p_atomId) { this->m_atomId = p_atomId; } +protected: + inline void SetType(MxDSType p_type) { this->m_type = p_type; } + private: - unsigned int m_unk08; - MxS16 m_unk0c; + MxU32 m_unk08; + MxU16 m_type; char* m_sourceName; - int m_unk14; + undefined4 m_unk14; char *m_objectName; - int m_unk1c; + undefined4 m_unk1c; MxAtomId m_atomId; MxS16 m_unk24; - MxU16 m_unk26; - int m_unk28; + undefined4 m_unk28; }; #endif // MXDSOBJECT_H diff --git a/LEGO1/mxdsobjectaction.cpp b/LEGO1/mxdsobjectaction.cpp index 990168da..d468f30a 100644 --- a/LEGO1/mxdsobjectaction.cpp +++ b/LEGO1/mxdsobjectaction.cpp @@ -3,11 +3,10 @@ // OFFSET: LEGO1 0x100c8870 MxDSObjectAction::MxDSObjectAction() { - // TODO + this->SetType(MxDSType_ObjectAction); } // OFFSET: LEGO1 0x100c8a20 MxDSObjectAction::~MxDSObjectAction() { - // TODO } diff --git a/LEGO1/mxdsparallelaction.cpp b/LEGO1/mxdsparallelaction.cpp index 78b9623a..97a8a9d3 100644 --- a/LEGO1/mxdsparallelaction.cpp +++ b/LEGO1/mxdsparallelaction.cpp @@ -3,11 +3,10 @@ // OFFSET: LEGO1 0x100cae80 MxDSParallelAction::MxDSParallelAction() { - + this->SetType(MxDSType_ParallelAction); } // OFFSET: LEGO1 0x100cb040 MxDSParallelAction::~MxDSParallelAction() { - } diff --git a/LEGO1/mxdsselectaction.cpp b/LEGO1/mxdsselectaction.cpp index 575aba29..5cae82fb 100644 --- a/LEGO1/mxdsselectaction.cpp +++ b/LEGO1/mxdsselectaction.cpp @@ -3,11 +3,12 @@ // OFFSET: LEGO1 0x100cb2b0 MxDSSelectAction::MxDSSelectAction() { - + // TODO + this->SetType(MxDSType_SelectAction); } -// OFFSET: LEGO1 0x100cb8d0 +// OFFSET: LEGO1 0x100cb8d0 STUB MxDSSelectAction::~MxDSSelectAction() { - + // TODO } diff --git a/LEGO1/mxdsserialaction.cpp b/LEGO1/mxdsserialaction.cpp index a5e8f623..46964208 100644 --- a/LEGO1/mxdsserialaction.cpp +++ b/LEGO1/mxdsserialaction.cpp @@ -3,11 +3,12 @@ // OFFSET: LEGO1 0x100ca9d0 MxDSSerialAction::MxDSSerialAction() { - + // TODO + this->SetType(MxDSType_SerialAction); } -// OFFSET: LEGO1 0x100cac10 +// OFFSET: LEGO1 0x100cac10 STUB MxDSSerialAction::~MxDSSerialAction() { - + // TODO } diff --git a/LEGO1/mxdssound.cpp b/LEGO1/mxdssound.cpp index 4f2b0d61..30894abc 100644 --- a/LEGO1/mxdssound.cpp +++ b/LEGO1/mxdssound.cpp @@ -4,10 +4,10 @@ MxDSSound::MxDSSound() { // TODO + this->SetType(MxDSType_Sound); } // OFFSET: LEGO1 0x100c9470 MxDSSound::~MxDSSound() { - // TODO } diff --git a/LEGO1/mxdsstill.cpp b/LEGO1/mxdsstill.cpp index 0a5434f1..938afe83 100644 --- a/LEGO1/mxdsstill.cpp +++ b/LEGO1/mxdsstill.cpp @@ -3,11 +3,10 @@ // OFFSET: LEGO1 0x100c98c0 MxDSStill::MxDSStill() { - // TODO + this->SetType(MxDSType_Still); } // OFFSET: LEGO1 0x100c9a70 MxDSStill::~MxDSStill() { - // TODO } diff --git a/LEGO1/mxdstypes.h b/LEGO1/mxdstypes.h new file mode 100755 index 00000000..6487bad5 --- /dev/null +++ b/LEGO1/mxdstypes.h @@ -0,0 +1,20 @@ +#ifndef MXDSTYPES_H +#define MXDSTYPES_H + +enum MxDSType +{ + MxDSType_Object = 0, + MxDSType_Action = 1, + MxDSType_MediaAction = 2, + MxDSType_Anim = 3, + MxDSType_Sound = 4, + MxDSType_MultiAction = 5, + MxDSType_SerialAction = 6, + MxDSType_ParallelAction = 7, + MxDSType_Event = 8, + MxDSType_SelectAction = 9, + MxDSType_Still = 10, + MxDSType_ObjectAction = 11, +}; + +#endif // MXDSTYPES_H From fb56735fbd2c3f54554741a643eb7df873b061a7 Mon Sep 17 00:00:00 2001 From: MishaProductions <106913236+MishaProductions@users.noreply.github.com> Date: Mon, 3 Jul 2023 20:25:37 +0300 Subject: [PATCH 2/7] [lego] Implement LegoGameState::SetSavepath (#76) * SetSavePath 100% match * fix naming convention --- LEGO1/legogamestate.cpp | 17 +++++++++++++++-- LEGO1/legogamestate.h | 3 +++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/LEGO1/legogamestate.cpp b/LEGO1/legogamestate.cpp index c12a5e06..344cf1ca 100644 --- a/LEGO1/legogamestate.cpp +++ b/LEGO1/legogamestate.cpp @@ -1,4 +1,5 @@ #include "legogamestate.h" +#include "legoomni.h" // OFFSET: LEGO1 0x10039550 LegoGameState::LegoGameState() @@ -39,7 +40,19 @@ void LegoGameState::SerializeScoreHistory(MxS16 p) } // OFFSET: LEGO1 0x10039f00 -void LegoGameState::SetSavePath(char *p) +void LegoGameState::SetSavePath(char *p_savePath) { - // TODO + if (m_savePath != NULL) + { + delete[] m_savePath; + } + if (p_savePath) + { + m_savePath = new char[strlen(p_savePath) + 1]; + strcpy(m_savePath, p_savePath); + } + else + { + m_savePath = NULL; + } } diff --git a/LEGO1/legogamestate.h b/LEGO1/legogamestate.h index 80437051..ef2eeb1e 100644 --- a/LEGO1/legogamestate.h +++ b/LEGO1/legogamestate.h @@ -13,6 +13,9 @@ class LegoGameState __declspec(dllexport) void SerializePlayersInfo(MxS16 p); __declspec(dllexport) void SerializeScoreHistory(MxS16 p); __declspec(dllexport) void SetSavePath(char *p); + +private: + char *m_savePath; }; #endif // LEGOGAMESTATE_H From 391ca9908f179a4b2a4b825e4fa7a96d42e68473 Mon Sep 17 00:00:00 2001 From: Mark Langen Date: Fri, 7 Jul 2023 09:20:51 -0700 Subject: [PATCH 3/7] LegoStream and implementations 100% match (#77) * MxFile and implementations 100% match * Add MxFile and it's implementations, MxSystemFile and MxMemoryFile. The names are chosen by me, we don't know their original naming. * These expose a Read/Write/Seek/Tell interface for reading and writing data, either from a file on disk or memory buffer. * 100% match all functions. * Change name to LegoStream * Use p_ convention * Assert size --- CMakeLists.txt | 1 + LEGO1/legostream.cpp | 149 +++++++++++++++++++++++++++++++++++++++++++ LEGO1/legostream.h | 73 +++++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 LEGO1/legostream.cpp create mode 100644 LEGO1/legostream.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cdfebd05..5c4e11d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ add_library(lego1 SHARED LEGO1/legoroi.cpp LEGO1/legosoundmanager.cpp LEGO1/legostate.cpp + LEGO1/legostream.cpp LEGO1/legotexturepresenter.cpp LEGO1/legoutil.cpp LEGO1/legovideomanager.cpp diff --git a/LEGO1/legostream.cpp b/LEGO1/legostream.cpp new file mode 100644 index 00000000..1fd2bf11 --- /dev/null +++ b/LEGO1/legostream.cpp @@ -0,0 +1,149 @@ + +#include "legostream.h" + +#include +#include + +// Very likely but not certain sizes. +// The classes are only used on the stack in functions we have not 100% matched +// yet, we can confirm the size once we have. +DECOMP_SIZE_ASSERT(LegoStream, 0x8); +DECOMP_SIZE_ASSERT(LegoFileStream, 0xC); +DECOMP_SIZE_ASSERT(LegoMemoryStream, 0x10); + +// OFFSET: LEGO1 0x10045ae0 +MxBool LegoStream::IsWriteMode() +{ + return m_mode == LEGOSTREAM_MODE_WRITE; +} + +// OFFSET: LEGO1 0x10045af0 +MxBool LegoStream::IsReadMode() +{ + return m_mode == LEGOSTREAM_MODE_READ; +} + +// OFFSET: LEGO1 0x100991c0 +LegoFileStream::LegoFileStream() + : LegoStream() +{ + m_hFile = NULL; +} + +// OFFSET: LEGO1 0x10099250 +LegoFileStream::~LegoFileStream() +{ + if (m_hFile != NULL) + fclose(m_hFile); +} + +// OFFSET: LEGO1 0x100992c0 +MxResult LegoFileStream::Read(char* p_buffer, MxU32 p_size) +{ + if (m_hFile == NULL) + return FAILURE; + + return (fread(p_buffer, 1, p_size, m_hFile) == p_size) ? SUCCESS : FAILURE; +} + +// OFFSET: LEGO1 0x10099300 +MxResult LegoFileStream::Write(char* p_buffer, MxU32 p_size) +{ + if (m_hFile == NULL) + return FAILURE; + + return (fwrite(p_buffer, 1, p_size, m_hFile) == p_size) ? SUCCESS : FAILURE; +} + +// OFFSET: LEGO1 0x10099340 +MxResult LegoFileStream::Tell(MxU32* p_offset) +{ + if (m_hFile == NULL) + return FAILURE; + + int got = ftell(m_hFile); + if (got == -1) + return FAILURE; + + *p_offset = got; + return SUCCESS; +} + +// OFFSET: LEGO1 0x10099370 +MxResult LegoFileStream::Seek(MxU32 p_offset) +{ + if (m_hFile == NULL) + return FAILURE; + + return (fseek(m_hFile, p_offset, 0) == 0) ? SUCCESS : FAILURE; +} + +// OFFSET: LEGO1 0x100993a0 +MxResult LegoFileStream::Open(const char* p_filename, OpenFlags p_mode) +{ + char modeString[4]; + + if (m_hFile != NULL) + fclose(m_hFile); + + modeString[0] = '\0'; + if (p_mode & ReadBit) + { + m_mode = LEGOSTREAM_MODE_READ; + strcat(modeString, "r"); + } + + if (p_mode & WriteBit) + { + if (m_mode != LEGOSTREAM_MODE_READ) + m_mode = LEGOSTREAM_MODE_WRITE; + strcat(modeString, "w"); + } + + if ((p_mode & 4) != 0) + strcat(modeString, "b"); + else + strcat(modeString, "t"); + + return (m_hFile = fopen(p_filename, modeString)) ? SUCCESS : FAILURE; +} + +// OFFSET: LEGO1 0x10099080 +LegoMemoryStream::LegoMemoryStream(char* p_buffer) + : LegoStream() +{ + m_buffer = p_buffer; + m_offset = 0; +} + +// OFFSET: LEGO1 0x10099160 +MxResult LegoMemoryStream::Read(char* p_buffer, MxU32 p_size) +{ + memcpy(p_buffer, m_buffer + m_offset, p_size); + m_offset += p_size; + return SUCCESS; +} + +// OFFSET: LEGO1 0x10099190 +MxResult LegoMemoryStream::Write(char* p_buffer, MxU32 p_size) +{ + memcpy(m_buffer + m_offset, p_buffer, p_size); + m_offset += p_size; + return SUCCESS; +} + +// OFFSET: LEGO1 0x100994a0 +MxResult LegoMemoryStream::Tell(MxU32* p_offset) +{ + *p_offset = m_offset; + return SUCCESS; +} + +// OFFSET: LEGO1 0x100994b0 +MxResult LegoMemoryStream::Seek(MxU32 p_offset) +{ + m_offset = p_offset; + return SUCCESS; +} + + diff --git a/LEGO1/legostream.h b/LEGO1/legostream.h new file mode 100644 index 00000000..a505cd54 --- /dev/null +++ b/LEGO1/legostream.h @@ -0,0 +1,73 @@ +#ifndef LEGOSTREAM_H +#define LEGOSTREAM_H + +#include "decomp.h" +#include "mxtypes.h" + +#include + +#define LEGOSTREAM_MODE_READ 1 +#define LEGOSTREAM_MODE_WRITE 2 + +// VTABLE 0x100d7d80 +class LegoStream +{ +public: + LegoStream() : m_mode(0) {} + inline virtual ~LegoStream() {}; + + virtual MxResult Read(char* p_buffer, MxU32 p_size) = 0; + virtual MxResult Write(char* p_buffer, MxU32 p_size) = 0; + virtual MxResult Tell(MxU32* p_offset) = 0; + virtual MxResult Seek(MxU32 p_offset) = 0; + + virtual MxBool IsWriteMode(); + virtual MxBool IsReadMode(); + + enum OpenFlags + { + ReadBit = 1, + WriteBit = 2, + BinaryBit = 4, + }; + +protected: + MxU8 m_mode; +}; + +// VTABLE 0x100db730 +class LegoFileStream : public LegoStream +{ +public: + LegoFileStream(); + virtual ~LegoFileStream(); + + MxResult Read(char* p_buffer, MxU32 p_size) override; + MxResult Write(char* p_buffer, MxU32 p_size) override; + MxResult Tell(MxU32* p_offset) override; + MxResult Seek(MxU32 p_offset) override; + + MxResult Open(const char* p_filename, OpenFlags p_mode); + +private: + FILE *m_hFile; +}; + +// VTABLE 0x100db710 +class LegoMemoryStream : public LegoStream +{ +public: + LegoMemoryStream(char* p_buffer); + ~LegoMemoryStream() {} + + MxResult Read(char* p_buffer, MxU32 p_size) override; + MxResult Write(char* p_buffer, MxU32 p_size) override; + MxResult Tell(MxU32* p_offset) override; + MxResult Seek(MxU32 p_offset) override; + +private: + char *m_buffer; + MxU32 m_offset; +}; + +#endif // LEGOSTREAM_H \ No newline at end of file From f8fe63524873b7d021373b4c7c1845c29566bd6c Mon Sep 17 00:00:00 2001 From: MattKC <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 7 Jul 2023 10:44:53 -0700 Subject: [PATCH 4/7] ci: don't show context lines when diffing progress, only show changed lines --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3bbc6d50..a65c8392 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -112,8 +112,8 @@ jobs: curl -fLSs -o ISLEPROGRESS-OLD.TXT https://github.com/isledecomp/isle/releases/download/continuous/ISLEPROGRESS.TXT curl -fLSs -o LEGO1PROGRESS-OLD.TXT https://github.com/isledecomp/isle/releases/download/continuous/LEGO1PROGRESS.TXT - diff -u ISLEPROGRESS-OLD.TXT ISLEPROGRESS.TXT || true - diff -u LEGO1PROGRESS-OLD.TXT LEGO1PROGRESS.TXT || true + diff -u0 ISLEPROGRESS-OLD.TXT ISLEPROGRESS.TXT || true + diff -u0 LEGO1PROGRESS-OLD.TXT LEGO1PROGRESS.TXT || true - name: Test Exports shell: bash From 889fd886f0fd43f47fc26c352d79917f1094949f Mon Sep 17 00:00:00 2001 From: Mark Langen Date: Fri, 7 Jul 2023 11:00:48 -0700 Subject: [PATCH 5/7] MxSemphore + MxThread + MxThread implementions (#80) * Add MxSemphore + MxThread and the two implementations I could find of MxThread (consumers extend it and override the Run method). * Implement a function in MxDiskStreamProvider which uses thread and semaphore to confirm correct layout / size of those classes. * All 100% match except two functions with a pair of registers swapped. --- CMakeLists.txt | 2 + LEGO1/mxdiskstreamprovider.cpp | 31 +++++++++++ LEGO1/mxdiskstreamprovider.h | 33 ++++++++++++ LEGO1/mxsemaphore.cpp | 29 ++++++++++ LEGO1/mxsemaphore.h | 27 ++++++++++ LEGO1/mxstreamprovider.h | 5 ++ LEGO1/mxthread.cpp | 99 ++++++++++++++++++++++++++++++++++ LEGO1/mxthread.h | 57 ++++++++++++++++++++ LEGO1/mxtimer.h | 2 +- 9 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 LEGO1/mxsemaphore.cpp create mode 100644 LEGO1/mxsemaphore.h create mode 100644 LEGO1/mxthread.cpp create mode 100644 LEGO1/mxthread.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c4e11d1..ca7f5679 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,7 @@ add_library(lego1 SHARED LEGO1/mxpalette.cpp LEGO1/mxpresenter.cpp LEGO1/mxscheduler.cpp + LEGO1/mxsemaphore.cpp LEGO1/mxsmkpresenter.cpp LEGO1/mxsoundmanager.cpp LEGO1/mxsoundpresenter.cpp @@ -143,6 +144,7 @@ add_library(lego1 SHARED LEGO1/mxstreamer.cpp LEGO1/mxstring.cpp LEGO1/mxstringvariable.cpp + LEGO1/mxthread.cpp LEGO1/mxtimer.cpp LEGO1/mxtransitionmanager.cpp LEGO1/mxunknown100dc6b0.cpp diff --git a/LEGO1/mxdiskstreamprovider.cpp b/LEGO1/mxdiskstreamprovider.cpp index 5e59efee..38d5cddc 100644 --- a/LEGO1/mxdiskstreamprovider.cpp +++ b/LEGO1/mxdiskstreamprovider.cpp @@ -1,5 +1,17 @@ #include "mxdiskstreamprovider.h" +#include "mxthread.h" + +// OFFSET: LEGO1 0x100d0f30 +MxResult MxDiskStreamProviderThread::Run() +{ + if (m_target != NULL) + m_target->WaitForWorkToComplete(); + MxThread::Run(); + // They should probably have writen "return MxThread::Run()" but they didn't. + return SUCCESS; +} + // OFFSET: LEGO1 0x100d0f70 MxDiskStreamProvider::MxDiskStreamProvider() { @@ -11,3 +23,22 @@ MxDiskStreamProvider::~MxDiskStreamProvider() { // TODO } + +// Matching but with esi / edi swapped +// OFFSET: LEGO1 0x100d1750 +MxResult MxDiskStreamProvider::WaitForWorkToComplete() +{ + while (m_remainingWork != 0) + { + m_busySemaphore.Wait(INFINITE); + if (m_unk1 != 0) + PerformWork(); + } + return SUCCESS; +} + +// OFFSET: LEGO1 0x100d1760 STUB +void MxDiskStreamProvider::PerformWork() +{ + // TODO +} \ No newline at end of file diff --git a/LEGO1/mxdiskstreamprovider.h b/LEGO1/mxdiskstreamprovider.h index 198b6062..58679055 100644 --- a/LEGO1/mxdiskstreamprovider.h +++ b/LEGO1/mxdiskstreamprovider.h @@ -2,6 +2,25 @@ #define MXDISKSTREAMPROVIDER_H #include "mxstreamprovider.h" +#include "mxthread.h" +#include "mxcriticalsection.h" + +class MxDiskStreamProvider; + +// VTABLE 0x100dd130 +class MxDiskStreamProviderThread : public MxThread +{ +public: + // Only inlined, no offset + inline MxDiskStreamProviderThread() + : MxThread() + , m_target(NULL) {} + + MxResult Run() override; + +private: + MxDiskStreamProvider *m_target; +}; // VTABLE 0x100dd138 class MxDiskStreamProvider : public MxStreamProvider @@ -23,6 +42,20 @@ class MxDiskStreamProvider : public MxStreamProvider { return !strcmp(name, MxDiskStreamProvider::ClassName()) || MxStreamProvider::IsA(name); } + + MxResult WaitForWorkToComplete(); + + void PerformWork(); + +private: + MxDiskStreamProviderThread m_thread; + MxSemaphore m_busySemaphore; + byte m_remainingWork; + byte m_unk1; + MxCriticalSection m_criticalSection; + byte unk2[4]; + void* unk3; + void *unk4; }; #endif // MXDISKSTREAMPROVIDER_H diff --git a/LEGO1/mxsemaphore.cpp b/LEGO1/mxsemaphore.cpp new file mode 100644 index 00000000..889a06e8 --- /dev/null +++ b/LEGO1/mxsemaphore.cpp @@ -0,0 +1,29 @@ + +#include "mxsemaphore.h" + +// OFFSET: LEGO1 0x100c87d0 +MxSemaphore::MxSemaphore() +{ + m_hSemaphore = NULL; +} + +// OFFSET: LEGO1 0x100c8800 +MxResult MxSemaphore::Init(MxU32 p_initialCount, MxU32 p_maxCount) +{ + MxResult result = FAILURE; + if (m_hSemaphore = CreateSemaphoreA(NULL, p_initialCount, p_maxCount, NULL)) + result = SUCCESS; + return result; +} + +// OFFSET: LEGO1 0x100c8830 +void MxSemaphore::Wait(MxU32 p_timeoutMS) +{ + WaitForSingleObject(m_hSemaphore, p_timeoutMS); +} + +// OFFSET: LEGO1 0x100c8850 +void MxSemaphore::Release(MxU32 p_releaseCount) +{ + ReleaseSemaphore(m_hSemaphore, p_releaseCount, NULL); +} \ No newline at end of file diff --git a/LEGO1/mxsemaphore.h b/LEGO1/mxsemaphore.h new file mode 100644 index 00000000..521678ac --- /dev/null +++ b/LEGO1/mxsemaphore.h @@ -0,0 +1,27 @@ +#ifndef MX_SEMAPHORE_H +#define MX_SEMAPHORE_H + +#include "mxtypes.h" +#include + +class MxSemaphore +{ +public: + MxSemaphore(); + + // Inlined only, no offset + ~MxSemaphore() + { + CloseHandle(m_hSemaphore); + } + + virtual MxResult Init(MxU32 p_initialCount, MxU32 p_maxCount); + + void Wait(MxU32 p_timeoutMS); + void Release(MxU32 p_releaseCount); + +private: + HANDLE m_hSemaphore; +}; + +#endif // MX_SEMAPHORE_H \ No newline at end of file diff --git a/LEGO1/mxstreamprovider.h b/LEGO1/mxstreamprovider.h index 796d0ed3..b70b6446 100644 --- a/LEGO1/mxstreamprovider.h +++ b/LEGO1/mxstreamprovider.h @@ -2,6 +2,7 @@ #define MXSTREAMPROVIDER_H #include "mxcore.h" +#include "mxdsfile.h" // VTABLE 0x100dd100 class MxStreamProvider : public MxCore @@ -18,6 +19,10 @@ class MxStreamProvider : public MxCore { return !strcmp(name, MxStreamProvider::ClassName()) || MxCore::IsA(name); } + +private: + void *m_pLookup; + MxDSFile* m_pFile; }; #endif // MXSTREAMPROVIDER_H diff --git a/LEGO1/mxthread.cpp b/LEGO1/mxthread.cpp new file mode 100644 index 00000000..b63019ed --- /dev/null +++ b/LEGO1/mxthread.cpp @@ -0,0 +1,99 @@ + +#include "mxthread.h" + +#include + +#include "mxomni.h" + +// OFFSET: LEGO1 0x100bf690 +MxResult MxThread::Run() +{ + m_semaphore.Release(1); + return SUCCESS; +} + +// OFFSET: LEGO1 0x100bf510 +MxThread::MxThread() +{ + m_hThread = NULL; + m_running = TRUE; + m_threadId = 0; +} + +// OFFSET: LEGO1 0x100bf5a0 +MxThread::~MxThread() +{ + if (m_hThread) + CloseHandle((HANDLE)m_hThread); +} + +typedef unsigned(__stdcall *ThreadFunc)(void *); + +// OFFSET: LEGO1 0x100bf610 +MxResult MxThread::Start(int p_stack, int p_flag) +{ + MxResult result = FAILURE; + if (m_semaphore.Init(0, 1) == SUCCESS) + { + if (m_hThread = _beginthreadex(NULL, p_stack << 2, (ThreadFunc)&MxThread::ThreadProc, this, p_flag, &m_threadId)) + result = SUCCESS; + } + return result; +} + +// OFFSET: LEGO1 0x100bf670 +void MxThread::Terminate() +{ + m_running = FALSE; + m_semaphore.Wait(INFINITE); +} + +// OFFSET: LEGO1 0x100bf680 +unsigned MxThread::ThreadProc(void *p_thread) +{ + return static_cast(p_thread)->Run(); +} + +// OFFSET: LEGO1 0x100bf660 +void MxThread::Sleep(MxS32 p_milliseconds) +{ + ::Sleep(p_milliseconds); +} + +// OFFSET: LEGO1 0x100b8bb0 +MxTickleThread::MxTickleThread(MxCore *p_target, int p_frequencyMS) +{ + m_target = p_target; + m_frequencyMS = p_frequencyMS; +} + +// OFFSET: LEGO1 0x100d0f50 +MxResult MxTickleThread::StartWithTarget(MxCore* p_target) +{ + m_target = p_target; + return Start(0x1000, 0); +} + +// Match except for register allocation +// OFFSET: LEGO1 0x100b8c90 +MxResult MxTickleThread::Run() +{ + MxTimer* timer = Timer(); + int lastTickled = -m_frequencyMS; + while (IsRunning()) + { + int currentTime = timer->GetTime(); + + if (currentTime < lastTickled) { + lastTickled = -m_frequencyMS; + } + int timeRemainingMS = (m_frequencyMS - currentTime) + lastTickled; + if (timeRemainingMS <= 0) { + m_target->Tickle(); + timeRemainingMS = 0; + lastTickled = currentTime; + } + Sleep(timeRemainingMS); + } + return MxThread::Run(); +} \ No newline at end of file diff --git a/LEGO1/mxthread.h b/LEGO1/mxthread.h new file mode 100644 index 00000000..6ac96b59 --- /dev/null +++ b/LEGO1/mxthread.h @@ -0,0 +1,57 @@ +#ifndef MXTHREAD_H +#define MXTHREAD_H + +#include "mxtypes.h" +#include "mxsemaphore.h" + +class MxCore; + +class MxThread +{ +public: + // Note: Comes before virtual destructor + virtual MxResult Run(); + + MxResult Start(int p_stack, int p_flag); + + void Terminate(); + + void Sleep(MxS32 p_milliseconds); + + // Inferred, not in DLL + inline MxBool IsRunning() { return m_running; } + +protected: + MxThread(); + virtual ~MxThread(); + +private: + static unsigned ThreadProc(void *p_thread); + + MxULong m_hThread; + MxU32 m_threadId; + MxBool m_running; + MxSemaphore m_semaphore; +}; + +class MxTickleThread : public MxThread +{ +public: + MxTickleThread(MxCore *p_target, int p_frequencyMS); + + // Unclear at this time whether this function and the m_target field are + // actually a general "userdata" pointer in the base MxThread, but it seems + // like the only usage is with an MxTickleThread. + MxResult StartWithTarget(MxCore* p_target); + + // Only inlined, no offset + virtual ~MxTickleThread() {} + + MxResult Run() override; + +private: + MxCore *m_target; + MxS32 m_frequencyMS; +}; + +#endif // MXTHREAD_H \ No newline at end of file diff --git a/LEGO1/mxtimer.h b/LEGO1/mxtimer.h index 1714e8e1..357cf410 100644 --- a/LEGO1/mxtimer.h +++ b/LEGO1/mxtimer.h @@ -18,7 +18,7 @@ class MxTimer : public MxCore inline MxLong GetTime() { if (this->m_isRunning) - return s_LastTimeCalculated; + return s_LastTimeTimerStarted; else return s_LastTimeCalculated - this->m_startTime; } From 67115e32d3da2b6ff982e3e4dde20eb571ccaa66 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 7 Jul 2023 12:22:42 -0700 Subject: [PATCH 6/7] cmake: bump minimum version --- .github/workflows/build.yml | 4 ++-- CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a65c8392..af1dac3b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,8 +45,8 @@ jobs: - name: Setup cmake uses: jwlawson/actions-setup-cmake@v1.13 with: - # Use 2.8 for maximum backwards compatibility - cmake-version: '2.8.x' + # Use minimum supported version + cmake-version: '3.0.x' - name: Build shell: cmd diff --git a/CMakeLists.txt b/CMakeLists.txt index ca7f5679..6067d1f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8...3.5 FATAL_ERROR) +cmake_minimum_required(VERSION 3.0...3.5 FATAL_ERROR) project(isle CXX) From f707af34dbc707d4cfc769a8ce59e65689a8b46a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 12 Jul 2023 14:12:03 -0700 Subject: [PATCH 7/7] move `override` macro to `compat.h` --- LEGO1/compat.h | 9 ++++++++- LEGO1/legostream.h | 3 ++- LEGO1/mxcore.h | 5 +++-- LEGO1/mxthread.h | 3 ++- LEGO1/mxtypes.h | 4 ---- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/LEGO1/compat.h b/LEGO1/compat.h index 496fa4db..7d76dfd0 100644 --- a/LEGO1/compat.h +++ b/LEGO1/compat.h @@ -11,4 +11,11 @@ #define COMPAT_CONST #endif -#endif // ISLECOMPAT_H \ No newline at end of file +// We use `override` so newer compilers can tell us our vtables are valid, +// however this keyword was added in C++11, so we define it as empty for +// compatibility with older compilers. +#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 corresponds to VC6.0 but "override" was probably added even later +#define override +#endif + +#endif // ISLECOMPAT_H diff --git a/LEGO1/legostream.h b/LEGO1/legostream.h index a505cd54..ca110f01 100644 --- a/LEGO1/legostream.h +++ b/LEGO1/legostream.h @@ -1,6 +1,7 @@ #ifndef LEGOSTREAM_H #define LEGOSTREAM_H +#include "compat.h" #include "decomp.h" #include "mxtypes.h" @@ -70,4 +71,4 @@ class LegoMemoryStream : public LegoStream MxU32 m_offset; }; -#endif // LEGOSTREAM_H \ No newline at end of file +#endif // LEGOSTREAM_H diff --git a/LEGO1/mxcore.h b/LEGO1/mxcore.h index 5e6586ba..9fb3e689 100644 --- a/LEGO1/mxcore.h +++ b/LEGO1/mxcore.h @@ -3,6 +3,7 @@ #include +#include "compat.h" #include "mxtypes.h" class MxParam; @@ -14,8 +15,8 @@ class MxCore public: __declspec(dllexport) MxCore(); __declspec(dllexport) virtual ~MxCore(); // vtable+00 - __declspec(dllexport) virtual MxLong Notify(MxParam &p); // vtable+04 - virtual MxLong Tickle(); // vtable+08 + __declspec(dllexport) virtual MxResult Notify(MxParam &p); // vtable+04 + virtual MxResult Tickle(); // vtable+08 // OFFSET: LEGO1 0x100144c0 inline virtual const char *ClassName() const // vtable+0c diff --git a/LEGO1/mxthread.h b/LEGO1/mxthread.h index 6ac96b59..4537dc14 100644 --- a/LEGO1/mxthread.h +++ b/LEGO1/mxthread.h @@ -1,6 +1,7 @@ #ifndef MXTHREAD_H #define MXTHREAD_H +#include "compat.h" #include "mxtypes.h" #include "mxsemaphore.h" @@ -54,4 +55,4 @@ class MxTickleThread : public MxThread MxS32 m_frequencyMS; }; -#endif // MXTHREAD_H \ No newline at end of file +#endif // MXTHREAD_H diff --git a/LEGO1/mxtypes.h b/LEGO1/mxtypes.h index 8a7d5026..d17ac09a 100644 --- a/LEGO1/mxtypes.h +++ b/LEGO1/mxtypes.h @@ -39,8 +39,4 @@ typedef MxU8 MxBool; #define FALSE 0 #endif -#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 corresponds to VC6.0 but "override" was probably added even later -#define override -#endif - #endif // MXTYPE_H