From 91c3ed3e7032e623bbd6dfff48a223bb3e57f8b6 Mon Sep 17 00:00:00 2001 From: Mark Langen Date: Thu, 12 Oct 2023 09:18:24 -0700 Subject: [PATCH] 100% match of many functions in the LegoGameState::Save codepath (#86) * Only include decomps * One more function * Add offset to endOfVariables * Remove leftover header * Use undefined where applicable * Fixes, refactorings * Fixes * Fix calling convention * Added offset comment --------- Co-authored-by: Christian Semmler --- CMakeLists.txt | 2 + LEGO1/infocenterstate.cpp | 2 + LEGO1/infocenterstate.h | 36 ++++++ LEGO1/legogamestate.cpp | 190 +++++++++++++++++++++++++++++--- LEGO1/legogamestate.h | 29 ++++- LEGO1/legoobjectfactory.cpp | 35 ++++++ LEGO1/legoobjectfactory.h | 22 ++++ LEGO1/legoomni.cpp | 8 +- LEGO1/legoomni.h | 7 +- LEGO1/legostream.cpp | 63 ++++++++++- LEGO1/legostream.h | 17 ++- LEGO1/legounksavedatawriter.cpp | 48 ++++++++ LEGO1/legounksavedatawriter.h | 40 +++++++ LEGO1/mxobjectfactory.cpp | 6 +- LEGO1/mxobjectfactory.h | 18 ++- LEGO1/mxomni.h | 6 +- 16 files changed, 489 insertions(+), 40 deletions(-) create mode 100644 LEGO1/legoobjectfactory.cpp create mode 100644 LEGO1/legoobjectfactory.h create mode 100644 LEGO1/legounksavedatawriter.cpp create mode 100644 LEGO1/legounksavedatawriter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cedd429..8d83e879 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,7 @@ add_library(lego1 SHARED LEGO1/legolocomotionanimpresenter.cpp LEGO1/legomodelpresenter.cpp LEGO1/legonavcontroller.cpp + LEGO1/legoobjectfactory.cpp LEGO1/legoomni.cpp LEGO1/legopalettepresenter.cpp LEGO1/legopartpresenter.cpp @@ -89,6 +90,7 @@ add_library(lego1 SHARED LEGO1/legostream.cpp LEGO1/legotexturepresenter.cpp LEGO1/legoutil.cpp + LEGO1/legounksavedatawriter.cpp LEGO1/legovideomanager.cpp LEGO1/legoworld.cpp LEGO1/legoworldpresenter.cpp diff --git a/LEGO1/infocenterstate.cpp b/LEGO1/infocenterstate.cpp index d680eb43..fcd300ee 100644 --- a/LEGO1/infocenterstate.cpp +++ b/LEGO1/infocenterstate.cpp @@ -1,5 +1,7 @@ #include "infocenterstate.h" +DECOMP_SIZE_ASSERT(InfocenterState, 0x94); + // OFFSET: LEGO1 0x10071600 STUB InfocenterState::InfocenterState() { diff --git a/LEGO1/infocenterstate.h b/LEGO1/infocenterstate.h index 4f09d496..309dccb3 100644 --- a/LEGO1/infocenterstate.h +++ b/LEGO1/infocenterstate.h @@ -3,6 +3,8 @@ #include "legostate.h" +#include "decomp.h" + // VTABLE 0x100d93a8 // SIZE 0x94 class InfocenterState : public LegoState @@ -23,6 +25,40 @@ class InfocenterState : public LegoState { return !strcmp(name, InfocenterState::ClassName()) || LegoState::IsA(name); } + + inline MxU32 GetInfocenterBufferElement(MxS32 p_index) { return m_buffer[p_index]; } + +private: + // Members should be renamed with their offsets before use + /* + struct SomeStruct + { + undefined4 unk1; + undefined2 unk2; + undefined2 unk3; + undefined2 unk4; + }; + + undefined2 unk1; + undefined2 unk2; + undefined4 unk3; + undefined4 padding1; + void *unk4; + undefined2 unk5; + undefined2 unk6; + undefined2 unk7; + undefined2 padding2; + void *unk8; + undefined2 unk9; + undefined2 unk10; + undefined2 unk11; + undefined2 padding3; + SomeStruct unk12[6]; + undefined4 unk13; + */ + + undefined pad[0x70]; + MxU32 m_buffer[7]; // 0x78 }; #endif // INFOCENTERSTATE_H diff --git a/LEGO1/legogamestate.cpp b/LEGO1/legogamestate.cpp index a08ce8e2..77cca7e6 100644 --- a/LEGO1/legogamestate.cpp +++ b/LEGO1/legogamestate.cpp @@ -1,17 +1,77 @@ #include "legogamestate.h" #include "legoomni.h" +#include "legostate.h" +#include "infocenterstate.h" +#include "legostream.h" +#include "mxobjectfactory.h" #include "mxvariabletable.h" -#include "decomp.h" +#include "mxstring.h" // Based on the highest dword offset (0x42c) referenced in the constructor. // There may be other members that come after. DECOMP_SIZE_ASSERT(LegoGameState, 0x430) +// GLOBAL OFFSET: LEGO1 0x100f3e40 +const char *g_fileExtensionGS = ".GS"; + +// GLOBAL OFFSET: LEGO1 0x100f3e58 +ColorStringStruct g_colorSaveData[43] = { + {"c_dbbkfny0", "lego red"}, + {"c_dbbkxly0", "lego white"}, + {"c_chbasey0", "lego black"}, + {"c_chbacky0", "lego black"}, + {"c_chdishy0", "lego white"}, + {"c_chhorny0", "lego black"}, + {"c_chljety1", "lego black"}, + {"c_chrjety1", "lego black"}, + {"c_chmidly0", "lego black"}, + {"c_chmotry0", "lego blue"}, + {"c_chsidly0", "lego black"}, + {"c_chsidry0", "lego black"}, + {"c_chstuty0", "lego black"}, + {"c_chtaily0", "lego black"}, + {"c_chwindy1", "lego black"}, + {"c_dbfbrdy0", "lego red"}, + {"c_dbflagy0", "lego yellow"}, + {"c_dbfrfny4", "lego red"}, + {"c_dbfrxly0", "lego white"}, + {"c_dbhndly0", "lego white"}, + {"c_dbltbry0", "lego white"}, + {"c_jsdashy0", "lego white"}, + {"c_jsexhy0", "lego black"}, + {"c_jsfrnty5", "lego black"}, + {"c_jshndly0", "lego red"}, + {"c_jslsidy0", "lego black"}, + {"c_jsrsidy0", "lego black"}, + {"c_jsskiby0", "lego red"}, + {"c_jswnshy5", "lego white"}, + {"c_rcbacky6", "lego green"}, + {"c_rcedgey0", "lego green"}, + {"c_rcfrmey0", "lego red"}, + {"c_rcfrnty6", "lego green"}, + {"c_rcmotry0", "lego white"}, + {"c_rcsidey0", "lego green"}, + {"c_rcstery0", "lego white"}, + {"c_rcstrpy0", "lego yellow"}, + {"c_rctailya", "lego white"}, + {"c_rcwhl1y0", "lego white"}, + {"c_rcwhl2y0", "lego white"}, + {"c_jsbasey0", "lego white"}, + {"c_chblady0", "lego black"}, + {"c_chseaty0", "lego white"}, +}; + +// NOTE: This offset = the end of the variables table, the last entry +// in that table is a special entry, the string "END_OF_VARIABLES" +// GLOBAL OFFSET: LEGO1 0x100f3e50 +extern const char *s_endOfVariables; + // OFFSET: LEGO1 0x10039550 LegoGameState::LegoGameState() { // TODO + m_stateCount = 0; m_backgroundColor = new LegoBackgroundColor("backgroundcolor", "set 56 54 68"); VariableTable()->SetVariable(m_backgroundColor); @@ -25,33 +85,93 @@ LegoGameState::LegoGameState() SerializeScoreHistory(1); } -// OFFSET: LEGO1 0x10039720 +// OFFSET: LEGO1 0x10039720 STUB LegoGameState::~LegoGameState() { // TODO } -// OFFSET: LEGO1 0x10039c60 +// OFFSET: LEGO1 0x10039c60 STUB MxResult LegoGameState::Load(MxULong) { // TODO return 0; } -// OFFSET: LEGO1 0x10039980 -MxResult LegoGameState::Save(MxULong p) +// OFFSET: LEGO1 0x1003a170 +void LegoGameState::GetFileSavePath(MxString *p_outPath, MxULong p_slotn) { - // TODO - return 0; + char baseForSlot[2] = "0"; + char path[1024] = ""; + + // Save path base + if (m_savePath != NULL) + strcpy(path, m_savePath); + + // Slot: "G0", "G1", ... + strcat(path, "G"); + baseForSlot[0] += p_slotn; + strcat(path, baseForSlot); + + // Extension: ".GS" + strcat(path, g_fileExtensionGS); + *p_outPath = MxString(path); } -// OFFSET: LEGO1 0x1003a2e0 +// OFFSET: LEGO1 0x1003a020 +MxResult LegoGameState::WriteEndOfVariables(LegoStream *p_stream) +{ + MxU8 len = strlen(s_endOfVariables); + if (p_stream->Write(&len, 1) == SUCCESS) + return p_stream->Write(s_endOfVariables, len); + return FAILURE; +} + +// OFFSET: LEGO1 0x10039980 +MxResult LegoGameState::Save(MxULong p_slot) +{ + MxResult result; + InfocenterState *infocenterState = (InfocenterState *)GameState()->GetState("InfocenterState"); + if (!infocenterState || infocenterState->GetInfocenterBufferElement(0) == 0) + result = SUCCESS; + else { + result = FAILURE; + MxVariableTable *variableTable = VariableTable(); + MxString savePath; + GetFileSavePath(&savePath, p_slot); + LegoFileStream fileStream; + if (fileStream.Open(savePath.GetData(), LegoStream::WriteBit) != FAILURE) { + MxU32 maybeVersion = 0x1000C; + fileStream.Write(&maybeVersion, 4); + fileStream.Write(&m_secondThingWritten, 2); + fileStream.Write(&m_someEnumState, 2); + fileStream.Write(&m_someModeSwitch, 1); + + for (MxS32 i = 0; i < sizeof(g_colorSaveData) / sizeof(g_colorSaveData[0]); ++i) { + if (LegoStream::WriteVariable(&fileStream, variableTable, g_colorSaveData[i].m_targetName) == FAILURE) + return result; + } + + if (LegoStream::WriteVariable(&fileStream, variableTable, "backgroundcolor") != FAILURE) { + if (LegoStream::WriteVariable(&fileStream, variableTable, "lightposition") != FAILURE) { + WriteEndOfVariables(&fileStream); + + // TODO: Calls down to more aggregate writing functions + return SUCCESS; + } + } + } + } + return result; +} + +// OFFSET: LEGO1 0x1003a2e0 STUB void LegoGameState::SerializePlayersInfo(MxS16 p) { // TODO } -// OFFSET: LEGO1 0x1003cdd0 +// OFFSET: LEGO1 0x1003cdd0 STUB void LegoGameState::SerializeScoreHistory(MxS16 p) { // TODO @@ -61,16 +181,56 @@ void LegoGameState::SerializeScoreHistory(MxS16 p) void LegoGameState::SetSavePath(char *p_savePath) { if (m_savePath != NULL) - { delete[] m_savePath; - } - if (p_savePath) - { + + if (p_savePath) { m_savePath = new char[strlen(p_savePath) + 1]; strcpy(m_savePath, p_savePath); } else - { m_savePath = NULL; - } } + +// OFFSET: LEGO1 0x1003bbb0 +LegoState *LegoGameState::GetState(char *p_stateName) +{ + for (MxS32 i = 0; i < m_stateCount; ++i) + if (m_stateArray[i]->IsA(p_stateName)) + return m_stateArray[i]; + return NULL; +} + +// OFFSET: LEGO1 0x1003bc00 +LegoState *LegoGameState::CreateState(char *p_stateName) +{ + LegoState* newState = (LegoState*)ObjectFactory()->Create(p_stateName); + RegisterState(newState); + + return newState; +} + +// OFFSET: LEGO1 0x1003bc30 +void LegoGameState::RegisterState(LegoState *p_state) +{ + MxS32 targetIndex; + for (targetIndex = 0; targetIndex < m_stateCount; ++targetIndex) + if (m_stateArray[targetIndex]->IsA(p_state->ClassName())) + break; + + if (targetIndex == m_stateCount) { + LegoState **newBuffer = new LegoState*[m_stateCount + 1]; + + if (m_stateCount != 0) { + memcpy(newBuffer, m_stateArray, m_stateCount * sizeof(LegoState*)); + delete[] m_stateArray; + } + + newBuffer[m_stateCount++] = p_state; + m_stateArray = newBuffer; + return; + } + + if (m_stateArray[targetIndex]) + delete m_stateArray[targetIndex]; + m_stateArray[targetIndex] = p_state; +} \ No newline at end of file diff --git a/LEGO1/legogamestate.h b/LEGO1/legogamestate.h index b1cd8b60..cec74785 100644 --- a/LEGO1/legogamestate.h +++ b/LEGO1/legogamestate.h @@ -6,6 +6,17 @@ #include "legobackgroundcolor.h" #include "legofullscreenmovie.h" +class LegoState; +class LegoStream; +class MxVariable; +class MxString; + +struct ColorStringStruct +{ + const char *m_targetName; + const char *m_colorName; +}; + // SIZE 0x430 (at least) class LegoGameState { @@ -18,13 +29,27 @@ class LegoGameState __declspec(dllexport) void SerializeScoreHistory(MxS16 p); __declspec(dllexport) void SetSavePath(char *p); + LegoState *GetState(char *p_stateName); + LegoState *CreateState(char *p_stateName); + + void GetFileSavePath(MxString *p_outPath, MxULong p_slotn); + +private: + void RegisterState(LegoState *p_state); + MxResult WriteEndOfVariables(LegoStream *p_stream); + private: char *m_savePath; // 0x0 - undefined m_unk04[20]; + MxS16 m_stateCount; + LegoState **m_stateArray; + MxU8 m_someModeSwitch; + MxU32 m_someEnumState; + undefined4 m_unk0x14; LegoBackgroundColor *m_backgroundColor; // 0x18 LegoBackgroundColor *m_tempBackgroundColor; // 0x1c LegoFullScreenMovie *m_fullScreenMovie; // 0x20 - undefined m_unk24[1036]; + MxU16 m_secondThingWritten; + undefined m_unk24[1032]; }; #endif // LEGOGAMESTATE_H diff --git a/LEGO1/legoobjectfactory.cpp b/LEGO1/legoobjectfactory.cpp new file mode 100644 index 00000000..c5747482 --- /dev/null +++ b/LEGO1/legoobjectfactory.cpp @@ -0,0 +1,35 @@ +#include "legoobjectfactory.h" + +#include "infocenterstate.h" +#include "decomp.h" + +// TODO: Uncomment once we have all the relevant types ready +// DECOMP_SIZE_ASSERT(LegoObjectFactory, 0x1c8); + +// OFFSET: LEGO1 0x10006e40 +LegoObjectFactory::LegoObjectFactory() +{ +#define X(V) this->m_id##V = MxAtomId(#V, LookupMode_Exact); + FOR_LEGOOBJECTFACTORY_OBJECTS(X) +#undef X +} + +// OFFSET: LEGO1 0x10009a90 +MxCore *LegoObjectFactory::Create(const char *p_name) +{ + MxAtomId atom(p_name, LookupMode_Exact); + + if (0) { +#define X(V) } else if (this->m_id##V == atom) { return new V; + FOR_LEGOOBJECTFACTORY_OBJECTS(X) +#undef X + } else { + return MxObjectFactory::Create(p_name); + } +} + +// OFFSET: LEGO1 0x1000fb30 STUB +void LegoObjectFactory::Destroy(void *p_object) +{ + // TODO +} diff --git a/LEGO1/legoobjectfactory.h b/LEGO1/legoobjectfactory.h new file mode 100644 index 00000000..d01a1196 --- /dev/null +++ b/LEGO1/legoobjectfactory.h @@ -0,0 +1,22 @@ +#ifndef LEGOOBJECTFACTORY_H +#define LEGOOBJECTFACTORY_H + +#include "mxobjectfactory.h" + +#define FOR_LEGOOBJECTFACTORY_OBJECTS(X) \ + X(InfocenterState) + +// VTABLE 0x100d4768 +class LegoObjectFactory : public MxObjectFactory +{ +public: + LegoObjectFactory(); + virtual MxCore *Create(const char *p_name) override; // vtable 0x14 + virtual void Destroy(void *p_object) override; // vtable 0x18 +private: +#define X(V) MxAtomId m_id##V; + FOR_LEGOOBJECTFACTORY_OBJECTS(X) +#undef X +}; + +#endif // LEGOOBJECTFACTORY_H diff --git a/LEGO1/legoomni.cpp b/LEGO1/legoomni.cpp index 33f88a58..7e306af1 100644 --- a/LEGO1/legoomni.cpp +++ b/LEGO1/legoomni.cpp @@ -4,6 +4,7 @@ #include "mxdsfile.h" #include "legogamestate.h" #include "legoutil.h" +#include "legoobjectfactory.h" // 0x100f4588 MxAtomId *g_nocdSourceName = NULL; @@ -261,7 +262,7 @@ void LegoOmni::Init() m_currentWorld = NULL; m_unk80 = FALSE; m_isle = NULL; - m_unk8c = 0; + m_unkLegoSaveDataWriter = NULL; m_plantManager = NULL; m_gameState = NULL; m_animationManager = NULL; @@ -271,11 +272,12 @@ void LegoOmni::Init() m_transitionManager = NULL; } -// OFFSET: LEGO1 0x10058e70 +// OFFSET: LEGO1 0x10058e70 STUB MxResult LegoOmni::Create(COMPAT_CONST MxOmniCreateParam &p) { - // FIXME: Stub MxOmni::Create(p); + + m_objectFactory = new LegoObjectFactory(); m_gameState = new LegoGameState(); m_bkgAudioManager = new MxBackgroundAudioManager(); diff --git a/LEGO1/legoomni.h b/LEGO1/legoomni.h index e4074463..37d3ef4b 100644 --- a/LEGO1/legoomni.h +++ b/LEGO1/legoomni.h @@ -16,6 +16,7 @@ class LegoPathBoundary; class LegoPlantManager; class LegoROI; class LegoSoundManager; +class LegoUnkSaveDataWriter; class LegoVideoManager; class LegoWorld; class MxAtomId; @@ -77,8 +78,8 @@ class LegoOmni : public MxOmni LegoWorld *GetCurrentWorld() { return m_currentWorld; } private: - int m_unk68; - int m_unk6c; + undefined4 m_unk68; + undefined4 m_unk6c; LegoInputManager *m_inputMgr; // 0x70 undefined4 m_unk74; undefined4 m_unk78; @@ -86,7 +87,7 @@ class LegoOmni : public MxOmni MxBool m_unk80; LegoNavController *m_navController; // 0x84 Isle* m_isle; // 0x88 - undefined4 m_unk8c; + LegoUnkSaveDataWriter* m_unkLegoSaveDataWriter; LegoPlantManager* m_plantManager; // 0x90 LegoAnimationManager* m_animationManager; LegoBuildingManager* m_buildingManager; // 0x98 diff --git a/LEGO1/legostream.cpp b/LEGO1/legostream.cpp index 1fd2bf11..0bc24db9 100644 --- a/LEGO1/legostream.cpp +++ b/LEGO1/legostream.cpp @@ -4,6 +4,14 @@ #include #include +#include "mxvariabletable.h" + +// This is a pointer to the end of the global variable name table, which has +// the text "END_OF_VARIABLES" in it. +// TODO: make s_endOfVariables reference the actual end of the variable array. +// GLOBAL OFFSET: LEGO1 0x100f3e50 +const char *s_endOfVariables = "END_OF_VARIABLES"; + // 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. @@ -38,7 +46,7 @@ LegoFileStream::~LegoFileStream() } // OFFSET: LEGO1 0x100992c0 -MxResult LegoFileStream::Read(char* p_buffer, MxU32 p_size) +MxResult LegoFileStream::Read(void* p_buffer, MxU32 p_size) { if (m_hFile == NULL) return FAILURE; @@ -47,7 +55,7 @@ MxResult LegoFileStream::Read(char* p_buffer, MxU32 p_size) } // OFFSET: LEGO1 0x10099300 -MxResult LegoFileStream::Write(char* p_buffer, MxU32 p_size) +MxResult LegoFileStream::Write(const void* p_buffer, MxU32 p_size) { if (m_hFile == NULL) return FAILURE; @@ -117,7 +125,7 @@ LegoMemoryStream::LegoMemoryStream(char* p_buffer) } // OFFSET: LEGO1 0x10099160 -MxResult LegoMemoryStream::Read(char* p_buffer, MxU32 p_size) +MxResult LegoMemoryStream::Read(void* p_buffer, MxU32 p_size) { memcpy(p_buffer, m_buffer + m_offset, p_size); m_offset += p_size; @@ -125,7 +133,7 @@ MxResult LegoMemoryStream::Read(char* p_buffer, MxU32 p_size) } // OFFSET: LEGO1 0x10099190 -MxResult LegoMemoryStream::Write(char* p_buffer, MxU32 p_size) +MxResult LegoMemoryStream::Write(const void* p_buffer, MxU32 p_size) { memcpy(m_buffer + m_offset, p_buffer, p_size); m_offset += p_size; @@ -146,4 +154,51 @@ MxResult LegoMemoryStream::Seek(MxU32 p_offset) return SUCCESS; } +// OFFSET: LEGO1 0x10039f70 +MxResult LegoStream::WriteVariable(LegoStream* p_stream, MxVariableTable* p_from, const char* p_variableName) +{ + MxResult result = FAILURE; + const char *variableValue = p_from->GetVariable(p_variableName); + if (variableValue) { + MxU8 length = strlen(p_variableName); + if (p_stream->Write((char*)&length, 1) == SUCCESS) { + if (p_stream->Write(p_variableName, length) == SUCCESS) { + length = strlen(variableValue); + if (p_stream->Write((char*)&length, 1) == SUCCESS) + result = p_stream->Write((char *)variableValue, length); + } + } + } + return result; +} + +// 95% match, just some instruction ordering differences on the call to +// MxVariableTable::SetVariable at the end. +// OFFSET: LEGO1 0x1003a080 +MxS32 LegoStream::ReadVariable(LegoStream* p_stream, MxVariableTable* p_to) +{ + MxS32 result = 1; + MxU8 length; + + if (p_stream->Read((char*)&length, 1) == SUCCESS) { + char nameBuffer[256]; + if (p_stream->Read(nameBuffer, length) == SUCCESS) { + nameBuffer[length] = '\0'; + if (strcmp(nameBuffer, s_endOfVariables) == 0) + // 2 -> "This was the last entry, done reading." + result = 2; + else { + if (p_stream->Read((char*)&length, 1) == SUCCESS) { + char valueBuffer[256]; + if (p_stream->Read(valueBuffer, length) == SUCCESS) { + result = 0; + valueBuffer[length] = '\0'; + p_to->SetVariable(nameBuffer, valueBuffer); + } + } + } + } + } + return result; +} diff --git a/LEGO1/legostream.h b/LEGO1/legostream.h index ca110f01..220d1b6d 100644 --- a/LEGO1/legostream.h +++ b/LEGO1/legostream.h @@ -10,6 +10,8 @@ #define LEGOSTREAM_MODE_READ 1 #define LEGOSTREAM_MODE_WRITE 2 +class MxVariableTable; + // VTABLE 0x100d7d80 class LegoStream { @@ -17,8 +19,8 @@ class LegoStream 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 Read(void* p_buffer, MxU32 p_size) = 0; + virtual MxResult Write(const void* p_buffer, MxU32 p_size) = 0; virtual MxResult Tell(MxU32* p_offset) = 0; virtual MxResult Seek(MxU32 p_offset) = 0; @@ -32,6 +34,9 @@ class LegoStream BinaryBit = 4, }; + static MxResult __stdcall WriteVariable(LegoStream* p_stream, MxVariableTable* p_from, const char* p_variableName); + static MxS32 __stdcall ReadVariable(LegoStream* p_stream, MxVariableTable* p_to); + protected: MxU8 m_mode; }; @@ -43,8 +48,8 @@ class LegoFileStream : public LegoStream LegoFileStream(); virtual ~LegoFileStream(); - MxResult Read(char* p_buffer, MxU32 p_size) override; - MxResult Write(char* p_buffer, MxU32 p_size) override; + MxResult Read(void* p_buffer, MxU32 p_size) override; + MxResult Write(const void* p_buffer, MxU32 p_size) override; MxResult Tell(MxU32* p_offset) override; MxResult Seek(MxU32 p_offset) override; @@ -61,8 +66,8 @@ class LegoMemoryStream : public LegoStream LegoMemoryStream(char* p_buffer); ~LegoMemoryStream() {} - MxResult Read(char* p_buffer, MxU32 p_size) override; - MxResult Write(char* p_buffer, MxU32 p_size) override; + MxResult Read(void* p_buffer, MxU32 p_size) override; + MxResult Write(const void* p_buffer, MxU32 p_size) override; MxResult Tell(MxU32* p_offset) override; MxResult Seek(MxU32 p_offset) override; diff --git a/LEGO1/legounksavedatawriter.cpp b/LEGO1/legounksavedatawriter.cpp new file mode 100644 index 00000000..61b41f28 --- /dev/null +++ b/LEGO1/legounksavedatawriter.cpp @@ -0,0 +1,48 @@ + +#include "legounksavedatawriter.h" +#include "legogamestate.h" +#include "legostream.h" + +DECOMP_SIZE_ASSERT(LegoSaveDataEntry3, 0x108); + +// GLOBAL OFFSET: LEGO1 0x10104f20 +LegoSaveDataEntry3 g_saveData3[66]; + +// OFFSET: LEGO1 0x10083310 +MxResult LegoUnkSaveDataWriter::WriteSaveData3(LegoStream *p_stream) +{ + MxResult result = FAILURE; + + // This should probably be a for loop but I can't figure out how to + // make it match as a for loop. + LegoSaveDataEntry3 *entry = g_saveData3; + const LegoSaveDataEntry3 *end = &g_saveData3[66]; + + while (TRUE) { + if (p_stream->Write(&entry->m_savePart1, 4) != SUCCESS) + break; + if (p_stream->Write(&entry->m_savePart2, 4) != SUCCESS) + break; + if (p_stream->Write(&entry->m_savePart3, 1) != SUCCESS) + break; + if (p_stream->Write(&entry->m_currentFrame, 1) != SUCCESS) + break; + if (p_stream->Write(&entry->m_savePart5, 1) != SUCCESS) + break; + if (p_stream->Write(&entry->m_savePart6, 1) != SUCCESS) + break; + if (p_stream->Write(&entry->m_savePart7, 1) != SUCCESS) + break; + if (p_stream->Write(&entry->m_savePart8, 1) != SUCCESS) + break; + if (p_stream->Write(&entry->m_savePart9, 1) != SUCCESS) + break; + if (p_stream->Write(&entry->m_savePart10, 1) != SUCCESS) + break; + if (++entry >= end) { + result = SUCCESS; + break; + } + } + return result; +} diff --git a/LEGO1/legounksavedatawriter.h b/LEGO1/legounksavedatawriter.h new file mode 100644 index 00000000..0280077a --- /dev/null +++ b/LEGO1/legounksavedatawriter.h @@ -0,0 +1,40 @@ +#ifndef LEGOUNKSAVEDATAWRITER_H +#define LEGOUNKSAVEDATAWRITER_H + +#include "mxtypes.h" +#include "decomp.h" + +class LegoStream; + +struct LegoSaveDataEntry3 +{ + char *m_name; + void *m_unk0x04; + void *m_unk0x08; + MxS32 m_savePart1; + MxS32 m_savePart2; + MxU8 m_savePart3; + undefined4 m_unk0x18[6]; + MxU8 m_frameOffsetInDwords; // 0x30 + MxS32 *m_pFrameData; + MxU8 m_currentFrame; + undefined4 m_unk0x3c[2]; + MxU8 m_savePart5; // 0x44 + undefined4 m_unk0x48[5]; + MxU8 m_savePart6; // 0x5c + undefined4 m_unk0x60[11]; + MxU8 m_savePart7; // 0x8c + undefined4 m_unk0x90[5]; + MxU8 m_savePart8; // 0xa4 + undefined4 m_unk0xa8[17]; + MxU8 m_savePart9; // 0xec + undefined4 m_unk0xf0[5]; + MxU8 m_savePart10; // 0x104 +}; + +class LegoUnkSaveDataWriter +{ + MxResult WriteSaveData3(LegoStream *p_stream); +}; + +#endif // LEGOUNKSAVEDATAWRITER_H diff --git a/LEGO1/mxobjectfactory.cpp b/LEGO1/mxobjectfactory.cpp index d52e4dac..78615f55 100644 --- a/LEGO1/mxobjectfactory.cpp +++ b/LEGO1/mxobjectfactory.cpp @@ -26,9 +26,9 @@ MxObjectFactory::MxObjectFactory() } // OFFSET: LEGO1 0x100b12c0 -MxCore *MxObjectFactory::Create(const char *name) +MxCore *MxObjectFactory::Create(const char *p_name) { - MxAtomId atom(name, LookupMode_Exact); + MxAtomId atom(p_name, LookupMode_Exact); if (0) { #define X(V) } else if (this->m_id##V == atom) { return new V; @@ -40,6 +40,6 @@ MxCore *MxObjectFactory::Create(const char *name) } // OFFSET: LEGO1 0x100b1a30 STUB -void MxObjectFactory::vtable18(void *) { +void MxObjectFactory::Destroy(void *p_object) { // FIXME } diff --git a/LEGO1/mxobjectfactory.h b/LEGO1/mxobjectfactory.h index 53aee2cb..b7e0be07 100644 --- a/LEGO1/mxobjectfactory.h +++ b/LEGO1/mxobjectfactory.h @@ -23,8 +23,22 @@ class MxObjectFactory : public MxCore { public: MxObjectFactory(); - virtual MxCore *Create(const char *name); // vtable 0x14 - virtual void vtable18(void *); // vtable 0x18 + + // OFFSET: LEGO1 0x10008f70 + inline virtual const char *ClassName() const override // vtable+0xc + { + // 0x100f0730 + return "MxObjectFactory"; + } + + // OFFSET: LEGO1 0x10008f80 + inline virtual MxBool IsA(const char *name) const override // vtable+0x10 + { + return !strcmp(name, MxObjectFactory::ClassName()) || MxCore::IsA(name); + } + + virtual MxCore *Create(const char *p_name); // vtable 0x14 + virtual void Destroy(void *p_object); // vtable 0x18 private: #define X(V) MxAtomId m_id##V; FOR_MXOBJECTFACTORY_OBJECTS(X) diff --git a/LEGO1/mxomni.h b/LEGO1/mxomni.h index 265db29b..ab07d2bd 100644 --- a/LEGO1/mxomni.h +++ b/LEGO1/mxomni.h @@ -93,7 +93,9 @@ __declspec(dllexport) MxVariableTable * VariableTable(); __declspec(dllexport) MxMusicManager * MusicManager(); __declspec(dllexport) MxEventManager * EventManager(); __declspec(dllexport) MxNotificationManager * NotificationManager(); -MxVideoManager * MVideoManager(); -MxAtomIdCounterSet* AtomIdCounterSet(); + +MxVideoManager *MVideoManager(); +MxAtomIdCounterSet *AtomIdCounterSet(); +MxObjectFactory *ObjectFactory(); #endif // MXOMNI_H