From 5f00634b9c617fb4fff414759c82cd6fc264fdc2 Mon Sep 17 00:00:00 2001 From: MS Date: Wed, 29 May 2024 10:47:25 -0400 Subject: [PATCH] Implement LegoMeterPresenter::DrawMeter (#964) * Implement LegoMeterPresenter::DrawMeter * New MxRect16 header, offsets and size annotations * Missing mxtypes include --- .../legoomni/include/legometerpresenter.h | 32 ++++-- .../src/control/legometerpresenter.cpp | 104 ++++++++++++++---- LEGO1/omni/include/mxbitmap.h | 29 ++--- LEGO1/omni/include/mxrect16.h | 47 ++++++++ 4 files changed, 167 insertions(+), 45 deletions(-) create mode 100644 LEGO1/omni/include/mxrect16.h diff --git a/LEGO1/lego/legoomni/include/legometerpresenter.h b/LEGO1/lego/legoomni/include/legometerpresenter.h index ad43944d..9a8b7acd 100644 --- a/LEGO1/lego/legoomni/include/legometerpresenter.h +++ b/LEGO1/lego/legoomni/include/legometerpresenter.h @@ -1,10 +1,18 @@ #ifndef LEGOMETERPRESENTER_H #define LEGOMETERPRESENTER_H +#include "mxrect16.h" #include "mxstillpresenter.h" #include "mxstring.h" +// SIZE 0x08 +struct MeterRect : public MxRect16 { + // FUNCTION: BETA10 0x10097eb0 + MeterRect() {} +}; + // VTABLE: LEGO1 0x100d7ac8 +// VTABLE: BETA10 0x101bca68 // SIZE 0x94 class LegoMeterPresenter : public MxStillPresenter { public: @@ -18,17 +26,21 @@ public: void ParseExtra() override; // vtable+0x30 private: - void FUN_10043a50(); + enum MeterLayout { + e_leftToRight = 0, + e_rightToLeft, + e_bottomToTop, + e_topToBottom + }; - MxU8* m_unk0x6c; // 0x6c - MxU16 m_type; // 0x70 - MxString m_variable; // 0x74 - MxFloat m_unk0x84; // 0x84 - MxU16 m_unk0x88; // 0x88 - MxU16 m_unk0x8a; // 0x8a - MxU16 m_unk0x8c; // 0x8c - MxU16 m_unk0x8e; // 0x8e - MxU16 m_layout; // 0x90 + void DrawMeter(); + + MxU8* m_meterPixels; // 0x6c + MxU16 m_fillColor; // 0x70 + MxString m_variable; // 0x74 + MxFloat m_curPercent; // 0x84 + MeterRect m_meterRect; // 0x88 + MxS16 m_layout; // 0x90 }; // SYNTHETIC: LEGO1 0x10043760 diff --git a/LEGO1/lego/legoomni/src/control/legometerpresenter.cpp b/LEGO1/lego/legoomni/src/control/legometerpresenter.cpp index d6dd5dd8..c6c8352a 100644 --- a/LEGO1/lego/legoomni/src/control/legometerpresenter.cpp +++ b/LEGO1/lego/legoomni/src/control/legometerpresenter.cpp @@ -4,27 +4,34 @@ #include "define.h" #include "mxbitmap.h" #include "mxdsaction.h" +#include "mxmisc.h" #include "mxutilities.h" +#include "mxvariabletable.h" + +#include DECOMP_SIZE_ASSERT(LegoMeterPresenter, 0x94) // FUNCTION: LEGO1 0x10043430 +// FUNCTION: BETA10 0x10097570 LegoMeterPresenter::LegoMeterPresenter() { - m_layout = 0; - m_unk0x6c = 0; - m_unk0x84 = 0; - m_type = 1; - SetBit1(FALSE); + m_meterPixels = NULL; + m_fillColor = 1; + m_curPercent = 0; + m_layout = e_leftToRight; + m_flags.m_bit1 = FALSE; } // FUNCTION: LEGO1 0x10043780 +// FUNCTION: BETA10 0x1009764a LegoMeterPresenter::~LegoMeterPresenter() { - delete m_unk0x6c; + delete m_meterPixels; } // FUNCTION: LEGO1 0x10043800 +// FUNCTION: BETA10 0x100976ec void LegoMeterPresenter::ParseExtra() { MxStillPresenter::ParseExtra(); @@ -41,27 +48,28 @@ void LegoMeterPresenter::ParseExtra() char output[256]; if (KeyValueStringParse(extraCopy, g_strTYPE, output)) { if (!strcmpi(output, g_strLEFT_TO_RIGHT)) { - m_layout = 0; + m_layout = e_leftToRight; } else if (!strcmpi(output, g_strRIGHT_TO_LEFT)) { - m_layout = 1; + m_layout = e_rightToLeft; } else if (!strcmpi(output, g_strBOTTOM_TO_TOP)) { - m_layout = 2; + m_layout = e_bottomToTop; } else if (!strcmpi(output, g_strTOP_TO_BOTTOM)) { - m_layout = 3; + m_layout = e_topToBottom; } } if (KeyValueStringParse(extraCopy, g_strFILLER_INDEX, output)) { - m_type = atoi(output); + m_fillColor = atoi(output); } if (KeyValueStringParse(extraCopy, g_strVARIABLE, output)) { m_variable = output; } else { + assert(0); EndAction(); } } @@ -71,30 +79,84 @@ void LegoMeterPresenter::ParseExtra() } // FUNCTION: LEGO1 0x10043990 +// FUNCTION: BETA10 0x10097917 void LegoMeterPresenter::StreamingTickle() { MxStillPresenter::StreamingTickle(); - m_unk0x6c = new MxU8[m_frameBitmap->GetBmiStride() * m_frameBitmap->GetBmiHeightAbs()]; - if (m_unk0x6c == NULL) { + m_meterPixels = new MxU8[m_frameBitmap->GetDataSize()]; + if (m_meterPixels == NULL) { + assert(0); EndAction(); } - memcpy(m_unk0x6c, m_frameBitmap->GetImage(), m_frameBitmap->GetBmiStride() * m_frameBitmap->GetBmiHeightAbs()); + memcpy(m_meterPixels, m_frameBitmap->GetImage(), m_frameBitmap->GetDataSize()); - m_unk0x88 = 0; - m_unk0x8a = 0; - m_unk0x8c = m_frameBitmap->GetBmiWidth() - 1; - m_unk0x8e = m_frameBitmap->GetBmiHeightAbs() - 1; + m_meterRect.SetLeft(0); + m_meterRect.SetTop(0); + m_meterRect.SetRight(m_frameBitmap->GetBmiWidth() - 1); + m_meterRect.SetBottom(m_frameBitmap->GetBmiHeightAbs() - 1); } // FUNCTION: LEGO1 0x10043a30 +// FUNCTION: BETA10 0x10097a1a void LegoMeterPresenter::RepeatingTickle() { - FUN_10043a50(); + DrawMeter(); MxStillPresenter::RepeatingTickle(); } -// STUB: LEGO1 0x10043a50 -void LegoMeterPresenter::FUN_10043a50() +// FUNCTION: LEGO1 0x10043a50 +// FUNCTION: BETA10 0x10097a40 +void LegoMeterPresenter::DrawMeter() { + const char* strval = VariableTable()->GetVariable(m_variable.GetData()); + MxFloat percent = atof(strval); + MxS16 row, leftRightCol, bottomTopCol, leftRightEnd, bottomTopEnd; + + if (strval != NULL && m_curPercent != percent) { + m_curPercent = percent; + + // DECOMP: This clamp is retail only + if (percent > 0.99) { + m_curPercent = 0.99f; + } + else if (percent < 0.0) { + m_curPercent = 0.0f; + } + + // Copy the previously drawn meter back into the bitmap + memcpy(m_frameBitmap->GetImage(), m_meterPixels, m_frameBitmap->GetDataSize()); + + switch (m_layout) { + case e_leftToRight: + leftRightEnd = m_meterRect.GetWidth() * m_curPercent; + + for (row = m_meterRect.GetTop(); row < m_meterRect.GetBottom(); row++) { + MxU8* line = m_frameBitmap->GetStart(m_meterRect.GetLeft(), row); + + for (leftRightCol = 0; leftRightCol < leftRightEnd; leftRightCol++, line++) { + if (*line) { + *line = m_fillColor; + } + } + } + break; + case e_bottomToTop: + bottomTopEnd = m_meterRect.GetBottom() - (MxS16) (m_meterRect.GetHeight() * m_curPercent); + + for (row = m_meterRect.GetBottom(); row < bottomTopEnd; row--) { + MxU8* line = m_frameBitmap->GetStart(m_meterRect.GetLeft(), row); + + for (bottomTopCol = 0; bottomTopCol < m_meterRect.GetWidth(); bottomTopCol++, line++) { + if (*line) { + *line = m_fillColor; + } + } + } + // break; + default: + // The other two fill options are not implemented. + break; + } + } } diff --git a/LEGO1/omni/include/mxbitmap.h b/LEGO1/omni/include/mxbitmap.h index c9c949be..e0056957 100644 --- a/LEGO1/omni/include/mxbitmap.h +++ b/LEGO1/omni/include/mxbitmap.h @@ -113,6 +113,17 @@ public: // FUNCTION: BETA10 0x100982b0 inline MxLong GetDataSize() const { return AlignToFourByte(m_bmiHeader->biWidth) * GetBmiHeightAbs(); } + // FUNCTION: BETA10 0x1002c4b0 + inline MxBool IsTopDown() + { + if (m_bmiHeader->biCompression == BI_RGB_TOPDOWN) { + return TRUE; + } + else { + return m_bmiHeader->biHeight < 0; + } + } + inline MxLong GetAdjustedStride() { if (m_bmiHeader->biCompression == BI_RGB_TOPDOWN || m_bmiHeader->biHeight < 0) { @@ -123,28 +134,18 @@ public: } } - inline MxLong GetLine(MxS32 p_top) - { - MxS32 height; - if (m_bmiHeader->biCompression == BI_RGB_TOPDOWN || m_bmiHeader->biHeight < 0) { - height = p_top; - } - else { - height = GetBmiHeightAbs() - p_top - 1; - } - return GetBmiStride() * height; - } - + // FUNCTION: BETA10 0x1002c320 inline MxU8* GetStart(MxS32 p_left, MxS32 p_top) { if (m_bmiHeader->biCompression == BI_RGB) { - return GetLine(p_top) + m_data + p_left; + return m_data + p_left + + AlignToFourByte(GetBmiWidth()) * (IsTopDown() ? p_top : (GetBmiHeightAbs() - 1) - p_top); } else if (m_bmiHeader->biCompression == BI_RGB_TOPDOWN) { return m_data; } else { - return GetLine(0) + m_data; + return m_data + AlignToFourByte(GetBmiWidth()) * (IsTopDown() ? 0 : (GetBmiHeightAbs() - 1)); } } diff --git a/LEGO1/omni/include/mxrect16.h b/LEGO1/omni/include/mxrect16.h new file mode 100644 index 00000000..1dd82dc5 --- /dev/null +++ b/LEGO1/omni/include/mxrect16.h @@ -0,0 +1,47 @@ +#ifndef MXRECT16_H +#define MXRECT16_H + +#include "mxtypes.h" + +// SIZE 0x08 +struct MxRect16 { + // FUNCTION: BETA10 0x10097ee0 + MxRect16() {} + + // FUNCTION: BETA10 0x100981f0 + inline void SetLeft(MxS16 p_left) { m_left = p_left; } + + // FUNCTION: BETA10 0x10098220 + inline void SetTop(MxS16 p_top) { m_top = p_top; } + + // FUNCTION: BETA10 0x10098250 + inline void SetRight(MxS16 p_right) { m_right = p_right; } + + // FUNCTION: BETA10 0x10098280 + inline void SetBottom(MxS16 p_bottom) { m_bottom = p_bottom; } + + // FUNCTION: BETA10 0x10098300 + inline MxS16 GetLeft() const { return m_left; } + + // FUNCTION: BETA10 0x10098330 + inline MxS16 GetTop() const { return m_top; } + + // There is no GetRight() + + // FUNCTION: BETA10 0x10098360 + inline MxS16 GetBottom() const { return m_bottom; } + + // FUNCTION: BETA10 0x10098390 + inline MxS16 GetWidth() const { return m_right - m_left + 1; } + + // FUNCTION: BETA10 0x100983c0 + inline MxS16 GetHeight() const { return m_bottom - m_top + 1; } + +private: + MxS16 m_left; // 0x00 + MxS16 m_top; // 0x02 + MxS16 m_right; // 0x04 + MxS16 m_bottom; // 0x06 +}; + +#endif // MXRECT16_H