From f64af166c8cfe3ada0d2f3c0ec83bf06a5cc35fa Mon Sep 17 00:00:00 2001 From: jonschz <17198703+jonschz@users.noreply.github.com> Date: Sat, 21 Jun 2025 07:52:24 +0200 Subject: [PATCH 1/5] Add BETA10 addresses for `LegoROI` and others (#1569) --------- Co-authored-by: jonschz --- LEGO1/lego/sources/anim/legoanim.cpp | 1 + LEGO1/lego/sources/roi/legoroi.cpp | 18 +++++++++++++- LEGO1/lego/sources/roi/legoroi.h | 11 +++++++-- LEGO1/realtime/orientableroi.cpp | 9 +++++++ LEGO1/realtime/orientableroi.h | 2 ++ LEGO1/realtime/roi.h | 20 ++++++++++++++++ LEGO1/viewmanager/viewroi.cpp | 35 ++++++++++++---------------- LEGO1/viewmanager/viewroi.h | 6 +++++ 8 files changed, 79 insertions(+), 23 deletions(-) diff --git a/LEGO1/lego/sources/anim/legoanim.cpp b/LEGO1/lego/sources/anim/legoanim.cpp index 3b8caafc..dc3c21da 100644 --- a/LEGO1/lego/sources/anim/legoanim.cpp +++ b/LEGO1/lego/sources/anim/legoanim.cpp @@ -725,6 +725,7 @@ void LegoAnimNodeData::SetName(LegoChar* p_name) } // FUNCTION: LEGO1 0x100a03c0 +// FUNCTION: BETA10 0x1017f254 LegoResult LegoAnimNodeData::CreateLocalTransform(LegoFloat p_time, Matrix4& p_matrix) { LegoU32 index; diff --git a/LEGO1/lego/sources/roi/legoroi.cpp b/LEGO1/lego/sources/roi/legoroi.cpp index dd72f7f0..97baaadf 100644 --- a/LEGO1/lego/sources/roi/legoroi.cpp +++ b/LEGO1/lego/sources/roi/legoroi.cpp @@ -58,8 +58,12 @@ ColorOverride g_colorOverride = NULL; TextureHandler g_textureHandler = NULL; // FUNCTION: LEGO1 0x100a81b0 -void LegoROI::FUN_100a81b0(const LegoChar* p_error, const LegoChar* p_name) +// FUNCTION: BETA10 0x101898c0 +// FUNCTION: ALPHA 0x100bb1c0 +void LegoROI::FUN_100a81b0(const LegoChar* p_error, ...) { + // Probably a printf-like debug function that was removed early. + // No known implementation in any of the binaries. } // FUNCTION: LEGO1 0x100a81c0 @@ -69,6 +73,7 @@ void LegoROI::configureLegoROI(int p_roiConfig) } // FUNCTION: LEGO1 0x100a81d0 +// FUNCTION: BETA10 0x101898e8 LegoROI::LegoROI(Tgl::Renderer* p_renderer) : ViewROI(p_renderer, NULL) { m_parentROI = NULL; @@ -77,6 +82,7 @@ LegoROI::LegoROI(Tgl::Renderer* p_renderer) : ViewROI(p_renderer, NULL) } // FUNCTION: LEGO1 0x100a82d0 +// FUNCTION: BETA10 0x10189994 LegoROI::LegoROI(Tgl::Renderer* p_renderer, ViewLODList* p_lodList) : ViewROI(p_renderer, p_lodList) { m_parentROI = NULL; @@ -85,6 +91,7 @@ LegoROI::LegoROI(Tgl::Renderer* p_renderer, ViewLODList* p_lodList) : ViewROI(p_ } // FUNCTION: LEGO1 0x100a83c0 +// FUNCTION: BETA10 0x10189a42 LegoROI::~LegoROI() { if (comp) { @@ -105,6 +112,7 @@ LegoROI::~LegoROI() } // FUNCTION: LEGO1 0x100a84a0 +// FUNCTION: BETA10 0x10189b99 LegoResult LegoROI::Read( OrientableROI* p_unk0xd4, Tgl::Renderer* p_renderer, @@ -337,6 +345,7 @@ LegoResult LegoROI::Read( } // FUNCTION: LEGO1 0x100a8cb0 +// FUNCTION: BETA10 0x1018a7e8 LegoResult LegoROI::CreateLocalTransform(LegoAnimNodeData* p_data, LegoTime p_time, Matrix4& p_matrix) { p_matrix.SetIdentity(); @@ -379,6 +388,7 @@ LegoROI* LegoROI::FindChildROI(const LegoChar* p_name, LegoROI* p_roi) } // FUNCTION: LEGO1 0x100a8da0 +// FUNCTION: BETA10 0x1018a9fb LegoResult LegoROI::ApplyAnimationTransformation( LegoTreeNode* p_node, const Matrix4& p_matrix, @@ -473,6 +483,7 @@ void LegoROI::FUN_100a8fd0(LegoTreeNode* p_node, Matrix4& p_matrix, LegoTime p_t } // FUNCTION: LEGO1 0x100a90f0 +// FUNCTION: BETA10 0x1018ada8 LegoResult LegoROI::SetFrame(LegoAnim* p_anim, LegoTime p_time) { LegoTreeNode* root = p_anim->GetRoot(); @@ -512,6 +523,7 @@ LegoResult LegoROI::SetLodColor(LegoFloat p_red, LegoFloat p_green, LegoFloat p_ } // FUNCTION: LEGO1 0x100a9210 +// FUNCTION: BETA10 0x1018af25 LegoResult LegoROI::SetTextureInfo(LegoTextureInfo* p_textureInfo) { LegoResult result = SUCCESS; @@ -735,6 +747,7 @@ LegoU32 LegoROI::FUN_100a9410( } // FUNCTION: LEGO1 0x100a9a50 +// FUNCTION: BETA10 0x1018bb6b TimeROI::TimeROI(Tgl::Renderer* p_renderer, ViewLODList* p_lodList, LegoTime p_time) : LegoROI(p_renderer, p_lodList) { m_time = p_time; @@ -760,6 +773,7 @@ void TimeROI::FUN_100a9b40(Matrix4& p_matrix, LegoTime p_time) } // FUNCTION: LEGO1 0x100a9bf0 +// FUNCTION: BETA10 0x1018bc93 LegoBool LegoROI::GetRGBAColor(const LegoChar* p_name, float& p_red, float& p_green, float& p_blue, float& p_alpha) { if (p_name == NULL) { @@ -850,12 +864,14 @@ void LegoROI::SetDisplayBB(int p_displayBB) } // FUNCTION: LEGO1 0x100aa340 +// FUNCTION: BETA10 0x1018cca0 float LegoROI::IntrinsicImportance() const { return .5; } // FUNCTION: LEGO1 0x100aa350 +// FUNCTION: BETA10 0x1018ccc0 void LegoROI::UpdateWorldBoundingVolumes() { CalcWorldBoundingVolumes(m_sphere, m_local2world, m_world_bounding_box, m_world_bounding_sphere); diff --git a/LEGO1/lego/sources/roi/legoroi.h b/LEGO1/lego/sources/roi/legoroi.h index 28119c72..216bb68d 100644 --- a/LEGO1/lego/sources/roi/legoroi.h +++ b/LEGO1/lego/sources/roi/legoroi.h @@ -17,6 +17,7 @@ class LegoTreeNode; struct LegoAnimActorEntry; // VTABLE: LEGO1 0x100dbe38 +// VTABLE: BETA10 0x101c3898 // SIZE 0x108 class LegoROI : public ViewROI { public: @@ -57,7 +58,7 @@ class LegoROI : public ViewROI { void SetDisplayBB(int p_displayBB); static LegoResult CreateLocalTransform(LegoAnimNodeData* p_data, LegoTime p_time, Matrix4& p_matrix); - static void FUN_100a81b0(const LegoChar* p_error, const LegoChar* p_name); + static void FUN_100a81b0(const LegoChar* p_error, ...); static void configureLegoROI(int p_roi); static void SetColorOverride(ColorOverride p_colorOverride); static LegoBool GetRGBAColor(const LegoChar* p_name, float& p_red, float& p_green, float& p_blue, float& p_alpha); @@ -86,6 +87,7 @@ class LegoROI : public ViewROI { void SetBoundingBox(const BoundingBox& p_box) { m_bounding_box = p_box; } // SYNTHETIC: LEGO1 0x100a82b0 + // SYNTHETIC: BETA10 0x1018c490 // LegoROI::`scalar deleting destructor' private: @@ -96,15 +98,20 @@ class LegoROI : public ViewROI { }; // VTABLE: LEGO1 0x100dbea8 +// VTABLE: BETA10 0x101c38d0 // SIZE 0x10c class TimeROI : public LegoROI { public: TimeROI(Tgl::Renderer* p_renderer, ViewLODList* p_lodList, LegoTime p_time); + void FUN_100a9b40(Matrix4& p_matrix, LegoTime p_time); + // SYNTHETIC: LEGO1 0x100a9ad0 + // SYNTHETIC: BETA10 0x1018c540 // TimeROI::`scalar deleting destructor' - void FUN_100a9b40(Matrix4& p_matrix, LegoTime p_time); + // SYNTHETIC: BETA10 0x1018c580 + // TimeROI::~TimeROI private: LegoTime m_time; // 0x108 diff --git a/LEGO1/realtime/orientableroi.cpp b/LEGO1/realtime/orientableroi.cpp index f479ca7d..92c9ac9e 100644 --- a/LEGO1/realtime/orientableroi.cpp +++ b/LEGO1/realtime/orientableroi.cpp @@ -108,6 +108,7 @@ void OrientableROI::SetLocal2World(const Matrix4& p_local2world) } // FUNCTION: LEGO1 0x100a5910 +// FUNCTION: BETA10 0x10167bac void OrientableROI::UpdateWorldData() { UpdateWorldBoundingVolumes(); @@ -115,6 +116,7 @@ void OrientableROI::UpdateWorldData() } // FUNCTION: LEGO1 0x100a5930 +// FUNCTION: BETA10 0x10167bd8 void OrientableROI::SetLocal2WorldWithWorldDataUpdate(const Matrix4& p_transform) { m_local2world = p_transform; @@ -123,6 +125,7 @@ void OrientableROI::SetLocal2WorldWithWorldDataUpdate(const Matrix4& p_transform } // FUNCTION: LEGO1 0x100a5960 +// FUNCTION: BETA10 0x10167c19 void OrientableROI::UpdateWorldDataWithTransform(const Matrix4& p_transform) { MxMatrix l_matrix(m_local2world); @@ -132,6 +135,7 @@ void OrientableROI::UpdateWorldDataWithTransform(const Matrix4& p_transform) } // FUNCTION: LEGO1 0x100a59b0 +// FUNCTION: BETA10 0x10167c6d void OrientableROI::UpdateWorldDataWithTransformAndChildren(const Matrix4& p_transform) { MxMatrix l_matrix(m_local2world); @@ -155,11 +159,13 @@ void OrientableROI::SetWorldVelocity(const Vector3& p_world_velocity) } // FUNCTION: LEGO1 0x100a5a50 +// FUNCTION: BETA10 0x10167d65 void OrientableROI::UpdateWorldVelocity() { } // FUNCTION: LEGO1 0x100a5a60 +// FUNCTION: BETA10 0x10167d7b void CalcWorldBoundingVolumes( const BoundingSphere& modelling_sphere, const Matrix4& local2world, @@ -186,18 +192,21 @@ void CalcWorldBoundingVolumes( } // FUNCTION: LEGO1 0x100a5d80 +// FUNCTION: BETA10 0x10168760 const float* OrientableROI::GetWorldVelocity() const { return m_world_velocity.GetData(); } // FUNCTION: LEGO1 0x100a5d90 +// FUNCTION: BETA10 0x10168790 const BoundingBox& OrientableROI::GetWorldBoundingBox() const { return m_world_bounding_box; } // FUNCTION: LEGO1 0x100a5da0 +// FUNCTION: BETA10 0x101687b0 const BoundingSphere& OrientableROI::GetWorldBoundingSphere() const { return m_world_bounding_sphere; diff --git a/LEGO1/realtime/orientableroi.h b/LEGO1/realtime/orientableroi.h index 031c5af7..c1354c50 100644 --- a/LEGO1/realtime/orientableroi.h +++ b/LEGO1/realtime/orientableroi.h @@ -23,6 +23,7 @@ class OrientableROI : public ROI { const BoundingSphere& GetWorldBoundingSphere() const override; // vtable+0x10 // FUNCTION: LEGO1 0x100a5db0 + // FUNCTION: BETA10 0x101687d0 virtual void WrappedUpdateWorldData() { UpdateWorldData(); } // vtable+0x14 virtual void UpdateWorldBoundingVolumes() = 0; // vtable+0x18 @@ -81,6 +82,7 @@ class OrientableROI : public ROI { // OrientableROI::`scalar deleting destructor' // SYNTHETIC: LEGO1 0x100aa2f0 +// SYNTHETIC: BETA10 0x10168600 // OrientableROI::~OrientableROI #endif // ORIENTABLEROI_H diff --git a/LEGO1/realtime/roi.h b/LEGO1/realtime/roi.h index 87cbe8f1..dd5b9993 100644 --- a/LEGO1/realtime/roi.h +++ b/LEGO1/realtime/roi.h @@ -15,9 +15,16 @@ // SIZE 0x28 class BoundingBox { public: + // The BETA10 matches may reference the wrong version + + // FUNCTION: BETA10 0x1004a7a0 const Vector3& Min() const { return min; } + Vector3& Min() { return min; } + + // FUNCTION: BETA10 0x1004a7c0 const Vector3& Max() const { return max; } + Vector3& Max() { return max; } private: @@ -31,14 +38,26 @@ class BoundingBox { // SIZE 0x18 class BoundingSphere { public: + // The BETA10 matches may reference the wrong version + + // FUNCTION: BETA10 0x1001fac0 const Vector3& Center() const { return center; } + + // FUNCTION: BETA10 0x100d55a0 Vector3& Center() { return center; } + + // FUNCTION: BETA10 0x1001fd30 const float& Radius() const { return radius; } + + // FUNCTION: BETA10 0x1001fae0 float& Radius() { return radius; } // SYNTHETIC: BETA10 0x1001fb90 // BoundingSphere::operator= + // SYNTHETIC: BETA10 0x1001fc50 + // BoundingSphere::BoundingSphere + private: Mx3DPointFloat center; // 0x00 float radius; // 0x14 @@ -136,6 +155,7 @@ class ROI { // list >::~list > // SYNTHETIC: LEGO1 0x100a5d50 +// SYNTHETIC: BETA10 0x101686a0 // ROI::~ROI #endif // ROI_H diff --git a/LEGO1/viewmanager/viewroi.cpp b/LEGO1/viewmanager/viewroi.cpp index 787e9078..05f636b6 100644 --- a/LEGO1/viewmanager/viewroi.cpp +++ b/LEGO1/viewmanager/viewroi.cpp @@ -16,65 +16,60 @@ float ViewROI::IntrinsicImportance() const } // for now // FUNCTION: LEGO1 0x100a9ec0 +// FUNCTION: BETA10 0x1018c740 Tgl::Group* ViewROI::GetGeometry() { return geometry; } // FUNCTION: LEGO1 0x100a9ed0 +// FUNCTION: BETA10 0x1018c760 const Tgl::Group* ViewROI::GetGeometry() const { return geometry; } // FUNCTION: LEGO1 0x100a9ee0 +// FUNCTION: BETA10 0x1018c780 void ViewROI::UpdateWorldDataWithTransformAndChildren(const Matrix4& parent2world) { OrientableROI::UpdateWorldDataWithTransformAndChildren(parent2world); + SetGeometryTransformation(); +} +// FUNCTION: BETA10 0x1018c7b0 +inline void ViewROI::SetGeometryTransformation() +{ if (geometry) { Tgl::FloatMatrix4 matrix; Matrix4 in(matrix); SETMAT4(in, m_local2world); - Tgl::Result result = geometry->SetTransformation(matrix); - // assert(Tgl::Succeeded(result)); + geometry->SetTransformation(matrix); } } // FUNCTION: LEGO1 0x100a9fc0 +// FUNCTION: BETA10 0x1018cad0 void ViewROI::UpdateWorldDataWithTransform(const Matrix4& p_transform) { OrientableROI::UpdateWorldDataWithTransform(p_transform); - if (geometry) { - Tgl::FloatMatrix4 matrix; - Matrix4 in(matrix); - SETMAT4(in, m_local2world); - geometry->SetTransformation(matrix); - } + SetGeometryTransformation(); } // FUNCTION: LEGO1 0x100aa0a0 +// FUNCTION: BETA10 0x1018cb00 void ViewROI::SetLocal2WorldWithWorldDataUpdate(const Matrix4& p_transform) { OrientableROI::SetLocal2WorldWithWorldDataUpdate(p_transform); - if (geometry) { - Tgl::FloatMatrix4 matrix; - Matrix4 in(matrix); - SETMAT4(in, m_local2world); - geometry->SetTransformation(matrix); - } + SetGeometryTransformation(); } // FUNCTION: LEGO1 0x100aa180 +// FUNCTION: BETA10 0x1018cb30 void ViewROI::UpdateWorldData() { OrientableROI::UpdateWorldData(); - if (geometry) { - Tgl::FloatMatrix4 matrix; - Matrix4 in(matrix); - SETMAT4(in, m_local2world); - geometry->SetTransformation(matrix); - } + SetGeometryTransformation(); } // FUNCTION: LEGO1 0x100aa500 diff --git a/LEGO1/viewmanager/viewroi.h b/LEGO1/viewmanager/viewroi.h index 63e82154..b7ba9564 100644 --- a/LEGO1/viewmanager/viewroi.h +++ b/LEGO1/viewmanager/viewroi.h @@ -13,6 +13,7 @@ */ // VTABLE: LEGO1 0x100dbe70 +// VTABLE: BETA10 0x101c3908 // SIZE 0xe4 class ViewROI : public OrientableROI { public: @@ -21,6 +22,7 @@ class ViewROI : public OrientableROI { c_lodLevelInvisible = -2, }; + // FUNCTION: BETA10 0x1018c5e0 ViewROI(Tgl::Renderer* pRenderer, ViewLODList* lodList) { SetLODList(lodList); @@ -29,6 +31,7 @@ class ViewROI : public OrientableROI { } // FUNCTION: LEGO1 0x100a9e20 + // FUNCTION: BETA10 0x1018c680 ~ViewROI() override { // SetLODList() will decrease refCount of LODList @@ -36,6 +39,7 @@ class ViewROI : public OrientableROI { delete geometry; } + // FUNCTION: BETA10 0x1007b540 void SetLODList(ViewLODList* lodList) { // ??? inherently type unsafe - kind of... because, now, ROI @@ -69,6 +73,8 @@ class ViewROI : public OrientableROI { protected: void UpdateWorldDataWithTransformAndChildren(const Matrix4& parent2world) override; // vtable+0x28 + void SetGeometryTransformation(); + Tgl::Group* geometry; // 0xdc int m_lodLevel; // 0xe0 }; From e3fc6fd13599082a8364d2e6c658e99813e41eba Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Sun, 22 Jun 2025 00:32:30 +0200 Subject: [PATCH 2/5] Use enum from `LegoActor` to map between string and id (#1577) --- LEGO1/lego/legoomni/src/common/legovariables.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/LEGO1/lego/legoomni/src/common/legovariables.cpp b/LEGO1/lego/legoomni/src/common/legovariables.cpp index 68768e99..2c701074 100644 --- a/LEGO1/lego/legoomni/src/common/legovariables.cpp +++ b/LEGO1/lego/legoomni/src/common/legovariables.cpp @@ -1,6 +1,7 @@ #include "legovariables.h" #include "3dmanager/lego3dmanager.h" +#include "legoactor.h" #include "legogamestate.h" #include "legonavcontroller.h" #include "legovideomanager.h" @@ -157,18 +158,18 @@ void WhoAmIVariable::SetValue(const char* p_value) MxVariable::SetValue(p_value); if (!strcmpi(p_value, g_papa)) { - GameState()->SetActorId(3); + GameState()->SetActorId(LegoActor::c_papa); } else if (!strcmpi(p_value, g_mama)) { - GameState()->SetActorId(2); + GameState()->SetActorId(LegoActor::c_mama); } else if (!strcmpi(p_value, g_pepper)) { - GameState()->SetActorId(1); + GameState()->SetActorId(LegoActor::c_pepper); } else if (!strcmpi(p_value, g_nick)) { - GameState()->SetActorId(4); + GameState()->SetActorId(LegoActor::c_nick); } else if (!strcmpi(p_value, g_laura)) { - GameState()->SetActorId(5); + GameState()->SetActorId(LegoActor::c_laura); } } From 16db496832d8532fa7f5a44807fb3330cd18f2a4 Mon Sep 17 00:00:00 2001 From: jonschz <17198703+jonschz@users.noreply.github.com> Date: Sun, 22 Jun 2025 08:22:25 +0200 Subject: [PATCH 3/5] Match `LegoGameState::History::WriteScoreHistory()`, clear unknowns (#1576) --------- Co-authored-by: jonschz --- LEGO1/lego/legoomni/include/legogamestate.h | 26 ++- .../legoomni/src/common/legogamestate.cpp | 181 +++++++++++------- 2 files changed, 134 insertions(+), 73 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legogamestate.h b/LEGO1/lego/legoomni/include/legogamestate.h index 1eec40a6..b81696fd 100644 --- a/LEGO1/lego/legoomni/include/legogamestate.h +++ b/LEGO1/lego/legoomni/include/legogamestate.h @@ -150,7 +150,18 @@ class LegoGameState { MxS16 m_totalScore; // 0x00 MxU8 m_scores[5][5]; // 0x02 Username m_name; // 0x1c - MxS16 m_unk0x2a; // 0x2a + MxS16 m_playerId; // 0x2a + + ScoreItem& operator=(const ScoreItem& p_other) + { + // MSVC auto-generates an operator=, but LegoGameState::WriteScoreHistory() has a much better match + // with a manual implementation. + m_totalScore = p_other.m_totalScore; + memcpy(m_scores, p_other.m_scores, sizeof(m_scores)); + m_name = p_other.m_name; + m_playerId = p_other.m_playerId; + return *this; + } }; // SIZE 0x372 @@ -158,7 +169,7 @@ class LegoGameState { History(); void WriteScoreHistory(); MxResult Serialize(LegoStorage* p_storage); - ScoreItem* FUN_1003cc90(Username* p_player, MxS16 p_unk0x24, MxS32& p_unk0x2c); + ScoreItem* FindPlayerInScoreHistory(Username* p_player, MxS16 p_unk0x24, MxS32& p_unk0x2c); // FUNCTION: BETA10 0x1002c2b0 MxS16 GetCount() { return m_count; } @@ -167,9 +178,12 @@ class LegoGameState { // FUNCTION: BETA10 0x1002c540 ScoreItem* GetScore(MxS32 p_index) { return p_index >= m_count ? NULL : &m_scores[p_index]; } - MxS16 m_count; // 0x00 - ScoreItem m_scores[20]; // 0x02 - MxS16 m_unk0x372; // 0x372 + MxS16 m_count; // 0x00 +#ifdef BETA10 + MxS16 m_indices[20]; // 0x02 +#endif + ScoreItem m_scores[20]; // 0x02 (0x22 for BETA10) + MxS16 m_nextPlayerId; // 0x372 (0x392 for BETA10) }; LegoGameState(); @@ -243,7 +257,7 @@ class LegoGameState { // TODO: Most likely getters/setters are not used according to BETA for the following members: public: - MxS16 m_unk0x24; // 0x24 + MxS16 m_currentPlayerId; // 0x24 MxS16 m_playerCount; // 0x26 Username m_players[9]; // 0x28 History m_history; // 0xa6 diff --git a/LEGO1/lego/legoomni/src/common/legogamestate.cpp b/LEGO1/lego/legoomni/src/common/legogamestate.cpp index 99648947..9c67340c 100644 --- a/LEGO1/lego/legoomni/src/common/legogamestate.cpp +++ b/LEGO1/lego/legoomni/src/common/legogamestate.cpp @@ -277,7 +277,7 @@ MxResult LegoGameState::Save(MxULong p_slot) } storage.WriteS32(0x1000c); - storage.WriteS16(m_unk0x24); + storage.WriteS16(m_currentPlayerId); storage.WriteU16(m_currentAct); storage.WriteU8(m_actorId); @@ -372,7 +372,7 @@ MxResult LegoGameState::Load(MxULong p_slot) goto done; } - storage.ReadS16(m_unk0x24); + storage.ReadS16(m_currentPlayerId); storage.ReadS16(actArea); SetCurrentAct((Act) actArea); @@ -615,8 +615,8 @@ MxResult LegoGameState::AddPlayer(Username& p_player) m_playerCount++; m_players[0].Set(p_player); - m_unk0x24 = m_history.m_unk0x372; - m_history.m_unk0x372 = m_unk0x24 + 1; + m_currentPlayerId = m_history.m_nextPlayerId; + m_history.m_nextPlayerId = m_currentPlayerId + 1; m_history.WriteScoreHistory(); SetCurrentAct(e_act1); @@ -1407,7 +1407,7 @@ MxResult LegoGameState::ScoreItem::Serialize(LegoStorage* p_storage) } m_name.Serialize(p_storage); - p_storage->ReadS16(m_unk0x2a); + p_storage->ReadS16(m_playerId); } else if (p_storage->IsWriteMode()) { p_storage->WriteS16(m_totalScore); @@ -1419,7 +1419,7 @@ MxResult LegoGameState::ScoreItem::Serialize(LegoStorage* p_storage) } m_name.Serialize(p_storage); - p_storage->WriteS16(m_unk0x2a); + p_storage->WriteS16(m_playerId); } return SUCCESS; @@ -1430,7 +1430,7 @@ MxResult LegoGameState::ScoreItem::Serialize(LegoStorage* p_storage) LegoGameState::History::History() { m_count = 0; - m_unk0x372 = 0; + m_nextPlayerId = 0; } // FUNCTION: LEGO1 0x1003c870 @@ -1441,83 +1441,130 @@ void LegoGameState::History::WriteScoreHistory() MxU8 scores[5][5]; InfocenterState* state = (InfocenterState*) GameState()->GetState("InfocenterState"); - if (state->m_letters[0]) { - JetskiRaceState* jetskiRaceState = (JetskiRaceState*) GameState()->GetState("JetskiRaceState"); - CarRaceState* carRaceState = (CarRaceState*) GameState()->GetState("CarRaceState"); - TowTrackMissionState* towTrackMissionState = - (TowTrackMissionState*) GameState()->GetState("TowTrackMissionState"); - PizzaMissionState* pizzaMissionState = (PizzaMissionState*) GameState()->GetState("PizzaMissionState"); - AmbulanceMissionState* ambulanceMissionState = - (AmbulanceMissionState*) GameState()->GetState("AmbulanceMissionState"); - for (MxS32 actor = 1; actor <= 5; actor++) { - scores[0][actor - 1] = carRaceState ? carRaceState->GetState(actor)->GetHighScore() : 0; - totalScore += scores[0][actor - 1]; + if (!state->m_letters[0]) { + return; + } - scores[1][actor - 1] = jetskiRaceState ? jetskiRaceState->GetState(actor)->GetHighScore() : 0; - totalScore += scores[1][actor - 1]; + JetskiRaceState* jetskiRaceState = (JetskiRaceState*) GameState()->GetState("JetskiRaceState"); + CarRaceState* carRaceState = (CarRaceState*) GameState()->GetState("CarRaceState"); + TowTrackMissionState* towTrackMissionState = (TowTrackMissionState*) GameState()->GetState("TowTrackMissionState"); + PizzaMissionState* pizzaMissionState = (PizzaMissionState*) GameState()->GetState("PizzaMissionState"); + AmbulanceMissionState* ambulanceMissionState = + (AmbulanceMissionState*) GameState()->GetState("AmbulanceMissionState"); - scores[2][actor - 1] = pizzaMissionState ? pizzaMissionState->GetHighScore(actor) : 0; - totalScore += scores[2][actor - 1]; + for (MxS32 actor = 1; actor <= 5; actor++) { + scores[0][actor - 1] = carRaceState ? carRaceState->GetState(actor)->GetHighScore() : 0; + totalScore += scores[0][actor - 1]; - scores[3][actor - 1] = towTrackMissionState ? towTrackMissionState->GetHighScore(actor) : 0; - totalScore += scores[3][actor - 1]; +#ifdef BETA10 + // likely a bug in BETA10 + scores[1][actor - 1] = carRaceState ? carRaceState->GetState(actor)->GetHighScore() : 0; +#else + scores[1][actor - 1] = jetskiRaceState ? jetskiRaceState->GetState(actor)->GetHighScore() : 0; +#endif + totalScore += scores[1][actor - 1]; - scores[4][actor - 1] = ambulanceMissionState ? ambulanceMissionState->GetHighScore(actor) : 0; - totalScore += scores[4][actor - 1]; - } + scores[2][actor - 1] = pizzaMissionState ? pizzaMissionState->GetHighScore(actor) : 0; + totalScore += scores[2][actor - 1]; - MxS32 unk0x2c; - ScoreItem* p_scorehist = FUN_1003cc90(&GameState()->m_players[0], GameState()->m_unk0x24, unk0x2c); + scores[3][actor - 1] = towTrackMissionState ? towTrackMissionState->GetHighScore(actor) : 0; + totalScore += scores[3][actor - 1]; - if (p_scorehist != NULL) { - p_scorehist->m_totalScore = totalScore; - memcpy(p_scorehist->m_scores, scores, sizeof(p_scorehist->m_scores)); - } - else { - if (m_count < (MxS16) sizeOfArray(m_scores)) { - m_scores[m_count].m_totalScore = totalScore; - memcpy(m_scores[m_count].m_scores, scores, sizeof(m_scores[m_count].m_scores)); - m_scores[m_count].m_name = GameState()->m_players[0]; - m_scores[m_count].m_unk0x2a = GameState()->m_unk0x24; - m_count++; - } - else if (m_scores[19].m_totalScore <= totalScore) { - m_scores[19].m_totalScore = totalScore; - memcpy(m_scores[19].m_scores, scores, sizeof(m_scores[19].m_scores)); - m_scores[19].m_name = GameState()->m_players[0]; - m_scores[19].m_unk0x2a = GameState()->m_unk0x24; + scores[4][actor - 1] = ambulanceMissionState ? ambulanceMissionState->GetHighScore(actor) : 0; + totalScore += scores[4][actor - 1]; + } + + MxS32 playerScoreHistoryIndex; + ScoreItem* p_scorehist = + FindPlayerInScoreHistory(GameState()->m_players, GameState()->m_currentPlayerId, playerScoreHistoryIndex); + +#ifdef BETA10 + if (!p_scorehist) { + MxS32 playerScoreRank; + // LINE: BETA10 0x100870ee + for (playerScoreRank = 0; playerScoreRank < m_count; playerScoreRank++) { + if (totalScore > m_scores[m_indices[playerScoreRank]].m_totalScore) { + break; } } + // LINE: BETA10 0x1008713f + if (playerScoreRank < m_count) { + if (m_count < 20) { + playerScoreHistoryIndex = m_count++; + } + else { + playerScoreHistoryIndex = m_indices[19]; + } - MxU8 tmpScores[5][5]; - Username tmpPlayer; - MxS16 tmpUnk0x2a; + MxS32 max = m_count - 1; + for (MxS32 j = max; playerScoreRank < j; j--) { + m_indices[j - 1] = m_indices[j - 2]; + } - // TODO: Match bubble sort loops - for (MxS32 i = m_count - 1; i > 0; i--) { - for (MxS32 j = 1; j <= i; j++) { - if (m_scores[j - 1].m_totalScore < m_scores[j].m_totalScore) { - memcpy(tmpScores, m_scores[j - 1].m_scores, sizeof(tmpScores)); - tmpPlayer = m_scores[j - 1].m_name; - tmpUnk0x2a = m_scores[j - 1].m_unk0x2a; + m_indices[playerScoreRank] = playerScoreHistoryIndex; + p_scorehist = &m_scores[playerScoreHistoryIndex]; + } + else if (playerScoreRank < 20) { + m_indices[m_count] = m_count; + p_scorehist = &m_scores[m_count++]; + } + } + else if (p_scorehist->m_totalScore != totalScore) { + assert(totalScore > p_scorehist->m_totalScore); - memcpy(m_scores[j - 1].m_scores, m_scores[j].m_scores, sizeof(m_scores[j - 1].m_scores)); - m_scores[j - 1].m_name = m_scores[j].m_name; - m_scores[j - 1].m_unk0x2a = m_scores[j].m_unk0x2a; + for (MxS32 i = playerScoreHistoryIndex; i > 0 && m_indices[i - 1] < m_indices[i]; i--) { + MxU8 tmp = m_indices[i - 1]; + m_indices[i - 1] = m_indices[i]; + m_indices[i] = tmp; + } + } + if (p_scorehist) { + p_scorehist->m_totalScore = totalScore; + memcpy(p_scorehist->m_scores[0], scores[0], sizeof(scores)); + p_scorehist->m_name = GameState()->m_players[0]; + p_scorehist->m_playerId = GameState()->m_currentPlayerId; + } +#else + if (p_scorehist != NULL) { + p_scorehist->m_totalScore = totalScore; + memcpy(p_scorehist->m_scores, scores, sizeof(p_scorehist->m_scores)); + } + else { + if (m_count < (MxS16) sizeOfArray(m_scores)) { + assert(totalScore > p_scorehist->m_totalScore); - memcpy(m_scores[j].m_scores, tmpScores, sizeof(m_scores[j].m_scores)); - m_scores[j].m_name = tmpPlayer; - m_scores[j].m_unk0x2a = tmpUnk0x2a; - } + m_scores[m_count].m_totalScore = totalScore; + memcpy(m_scores[m_count].m_scores, scores, sizeof(m_scores[m_count].m_scores)); + m_scores[m_count].m_name = GameState()->m_players[0]; + m_scores[m_count].m_playerId = GameState()->m_currentPlayerId; + m_count++; + } + else if (m_scores[19].m_totalScore <= totalScore) { + m_scores[19].m_totalScore = totalScore; + memcpy(m_scores[19].m_scores, scores, sizeof(m_scores[19].m_scores)); + m_scores[19].m_name = GameState()->m_players[0]; + m_scores[19].m_playerId = GameState()->m_currentPlayerId; + } + } + + ScoreItem tmpItem; + + for (MxS32 i = m_count - 1; i >= 0; i--) { + for (MxS32 j = 1; j <= i; j++) { + if (m_scores[j].m_totalScore > m_scores[j - 1].m_totalScore) { + tmpItem = m_scores[j - 1]; + m_scores[j - 1] = m_scores[j]; + m_scores[j] = tmpItem; } } } +#endif } // FUNCTION: LEGO1 0x1003cc90 // FUNCTION: BETA10 0x1008732a -LegoGameState::ScoreItem* LegoGameState::History::FUN_1003cc90( +LegoGameState::ScoreItem* LegoGameState::History::FindPlayerInScoreHistory( LegoGameState::Username* p_player, MxS16 p_unk0x24, MxS32& p_unk0x2c @@ -1525,7 +1572,7 @@ LegoGameState::ScoreItem* LegoGameState::History::FUN_1003cc90( { MxS32 i = 0; for (; i < m_count; i++) { - if (!memcmp(p_player, &m_scores[i].m_name, sizeof(*p_player)) && m_scores[i].m_unk0x2a == p_unk0x24) { + if (!memcmp(p_player, &m_scores[i].m_name, sizeof(*p_player)) && m_scores[i].m_playerId == p_unk0x24) { break; } } @@ -1544,7 +1591,7 @@ LegoGameState::ScoreItem* LegoGameState::History::FUN_1003cc90( MxResult LegoGameState::History::Serialize(LegoStorage* p_storage) { if (p_storage->IsReadMode()) { - p_storage->ReadS16(m_unk0x372); + p_storage->ReadS16(m_nextPlayerId); p_storage->ReadS16(m_count); for (MxS16 i = 0; i < m_count; i++) { @@ -1554,7 +1601,7 @@ MxResult LegoGameState::History::Serialize(LegoStorage* p_storage) } } else if (p_storage->IsWriteMode()) { - p_storage->WriteS16(m_unk0x372); + p_storage->WriteS16(m_nextPlayerId); p_storage->WriteS16(m_count); for (MxS16 i = 0; i < m_count; i++) { From 2595537c4cbdaff80786beac32d51d0e10763347 Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Sun, 22 Jun 2025 14:34:29 +0200 Subject: [PATCH 4/5] Clear unknowns in `LegoAnimActorEntry` and `LegoAnimKey` (#1580) --- .../src/build/legocarbuildpresenter.cpp | 4 ++-- .../legoomni/src/video/legoanimpresenter.cpp | 12 ++++++------ LEGO1/lego/sources/anim/legoanim.cpp | 8 ++++---- LEGO1/lego/sources/anim/legoanim.h | 18 +++++++++++++----- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp b/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp index 87c896dc..d6512c8b 100644 --- a/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp +++ b/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp @@ -577,10 +577,10 @@ void LegoCarBuildAnimPresenter::RotateAroundYAxis(MxFloat p_angle) newRotation.EqualsHamiltonProduct(currentRotation, additionalRotation); if (newRotation[3] < 0.9999) { - rotationKey->FUN_100739a0(TRUE); + rotationKey->SetActive(TRUE); } else { - rotationKey->FUN_100739a0(FALSE); + rotationKey->SetActive(FALSE); } m_platformAnimNodeData->GetRotationKey(0)->SetX(newRotation[0]); diff --git a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp index 54b1da70..852263fc 100644 --- a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp +++ b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp @@ -221,10 +221,10 @@ void LegoAnimPresenter::FUN_100692b0() for (LegoU32 i = 0; i < numActors; i++) { LegoChar* str = GetVariableOrIdentity(m_anim->GetActorName(i), NULL); - undefined4 unk0x04 = m_anim->GetActorUnknown0x04(i); + LegoU32 actorType = m_anim->GetActorType(i); LegoROI* roi = NULL; - if (unk0x04 == 2) { + if (actorType == LegoAnimActorEntry::e_actorType2) { LegoChar* src; if (str[0] == '*') { src = str + 1; @@ -239,7 +239,7 @@ void LegoAnimPresenter::FUN_100692b0() roi->SetVisibility(FALSE); } } - else if (unk0x04 == 4) { + else if (actorType == LegoAnimActorEntry::e_actorType4) { LegoChar* baseName = new LegoChar[strlen(str)]; strcpy(baseName, str + 1); strlwr(baseName); @@ -254,7 +254,7 @@ void LegoAnimPresenter::FUN_100692b0() delete[] baseName; delete[] und; } - else if (unk0x04 == 3) { + else if (actorType == LegoAnimActorEntry::e_actorType3) { LegoChar* lodName = new LegoChar[strlen(str)]; strcpy(lodName, str + 1); @@ -300,9 +300,9 @@ void LegoAnimPresenter::FUN_100695c0() for (LegoU32 i = 0; i < numActors; i++) { if (FUN_100698b0(rois, m_anim->GetActorName(i)) == FALSE) { - undefined4 unk0x04 = m_anim->GetActorUnknown0x04(i); + LegoU32 actorType = m_anim->GetActorType(i); - if (unk0x04 == 5 || unk0x04 == 6) { + if (actorType == LegoAnimActorEntry::e_actorType5 || actorType == LegoAnimActorEntry::e_actorType6) { LegoChar lodName[256]; const LegoChar* actorName = m_anim->GetActorName(i); diff --git a/LEGO1/lego/sources/anim/legoanim.cpp b/LEGO1/lego/sources/anim/legoanim.cpp index dc3c21da..8ec3ee2d 100644 --- a/LEGO1/lego/sources/anim/legoanim.cpp +++ b/LEGO1/lego/sources/anim/legoanim.cpp @@ -1063,7 +1063,7 @@ LegoResult LegoAnim::Read(LegoStorage* p_storage, LegoS32 p_parseScene) m_modelList[i].m_name[length] = '\0'; - if (p_storage->Read(&m_modelList[i].m_unk0x04, sizeof(undefined4)) != SUCCESS) { + if (p_storage->Read(&m_modelList[i].m_type, sizeof(LegoU32)) != SUCCESS) { goto done; } } @@ -1124,7 +1124,7 @@ LegoResult LegoAnim::Write(LegoStorage* p_storage) goto done; } - if (p_storage->Write(&m_modelList[i].m_unk0x04, sizeof(m_modelList[i].m_unk0x04)) != SUCCESS) { + if (p_storage->Write(&m_modelList[i].m_type, sizeof(m_modelList[i].m_type)) != SUCCESS) { goto done; } } @@ -1159,10 +1159,10 @@ const LegoChar* LegoAnim::GetActorName(LegoU32 p_index) // FUNCTION: LEGO1 0x100a0f40 // FUNCTION: BETA10 0x1018023c -undefined4 LegoAnim::GetActorUnknown0x04(LegoU32 p_index) +LegoU32 LegoAnim::GetActorType(LegoU32 p_index) { if (p_index < m_numActors) { - return m_modelList[p_index].m_unk0x04; + return m_modelList[p_index].m_type; } return 0; diff --git a/LEGO1/lego/sources/anim/legoanim.h b/LEGO1/lego/sources/anim/legoanim.h index 9b81b531..372128b5 100644 --- a/LEGO1/lego/sources/anim/legoanim.h +++ b/LEGO1/lego/sources/anim/legoanim.h @@ -30,9 +30,9 @@ class LegoAnimKey { LegoU32 ShouldSkipInterpolation() { return m_flags & c_skipInterpolation; } // FUNCTION: BETA10 0x100739a0 - void FUN_100739a0(MxS32 p_param) + void SetActive(MxS32 p_active) { - if (p_param) { + if (p_active) { m_flags |= c_active; } else { @@ -289,8 +289,16 @@ class LegoAnimNodeData : public LegoTreeNodeData { // SIZE 0x08 struct LegoAnimActorEntry { - LegoChar* m_name; // 0x00 - undefined4 m_unk0x04; // 0x04 + enum { + e_actorType2 = 2, + e_actorType3 = 3, + e_actorType4 = 4, + e_actorType5 = 5, + e_actorType6 = 6, + }; + + LegoChar* m_name; // 0x00 + LegoU32 m_type; // 0x04 }; // TODO: Possibly called `LegoCameraAnim(ation)`? @@ -338,7 +346,7 @@ class LegoAnim : public LegoTree { virtual LegoResult Read(LegoStorage* p_storage, LegoS32 p_parseScene); // vtable+0x10 const LegoChar* GetActorName(LegoU32 p_index); - undefined4 GetActorUnknown0x04(LegoU32 p_index); + LegoU32 GetActorType(LegoU32 p_index); // FUNCTION: BETA10 0x1005abf0 LegoAnimScene* GetCamAnim() { return m_camAnim; } From 0a5091531253edb05826fa12588d81e6219ee418 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sun, 22 Jun 2025 08:55:36 -0700 Subject: [PATCH 5/5] Fix implicit signed char-ness (#1581) --- LEGO1/omni/src/video/flic.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LEGO1/omni/src/video/flic.cpp b/LEGO1/omni/src/video/flic.cpp index afdebebc..d262e517 100644 --- a/LEGO1/omni/src/video/flic.cpp +++ b/LEGO1/omni/src/video/flic.cpp @@ -288,7 +288,7 @@ void DecodeBrun(LPBITMAPINFOHEADER p_bitmapHeader, BYTE* p_pixelData, BYTE* p_da while (--line >= 0) { short column = 0; data++; - char count = 0; + signed char count = 0; while ((column += count) < width2) { count = *data++; @@ -331,7 +331,7 @@ void DecodeLC(LPBITMAPINFOHEADER p_bitmapHeader, BYTE* p_pixelData, BYTE* p_data while (packets > 0) { column += *data++; // skip byte - char type = *((char*) data++); + signed char type = *((signed char*) data++); if (type < 0) { type = -type; @@ -417,7 +417,7 @@ void DecodeSS2(LPBITMAPINFOHEADER p_bitmapHeader, BYTE* p_pixelData, BYTE* p_dat // LINE: BETA10 0x1013e726 column += *data.byte++; // LINE: BETA10 0x1013e73a - short type = *(char*) data.byte++; + short type = *(signed char*) data.byte++; type += type; if (type >= 0) {