Repository: RetroAchievements/RAIntegration
Pull request #981 (2023-02-27 23:44:14)

use exposed memory range for arcade address validation as it varies by game
https://discord.com/channels/310192285306454017/962817919698501702/1079903962037239838


Patch:
diff --git a/src/data/models/TriggerValidation.cpp b/src/data/models/TriggerValidation.cpp
index 57f49830..6acacb19 100644
--- a/src/data/models/TriggerValidation.cpp
+++ b/src/data/models/TriggerValidation.cpp
@@ -3,6 +3,7 @@
 #include "RA_StringUtils.h"
 
 #include "data/context/ConsoleContext.hh"
+#include "data/context/EmulatorContext.hh"
 
 #include "services/ServiceLocator.hh"
 
@@ -103,6 +104,13 @@ bool TriggerValidation::Validate(const std::string& sTrigger, std::wstring& sErr
     {
         const auto& pConsoleContext = ra::services::ServiceLocator::Get<ra::data::context::ConsoleContext>();
         nMaxAddress = pConsoleContext.MaxAddress();
+
+        // if console definition doesn't specify the max address, see how much was exposed by the emulator
+        if (nMaxAddress == 0)
+        {
+            const auto& pEmulatorContext = ra::services::ServiceLocator::Get<ra::data::context::EmulatorContext>();
+            nMaxAddress = gsl::narrow_cast<unsigned>(pEmulatorContext.TotalMemorySize()) - 1;
+        }
     }
 
     char sErrorBuffer[256];
diff --git a/src/ui/viewmodels/OverlayManager.cpp b/src/ui/viewmodels/OverlayManager.cpp
index 3fc7ba30..4e530dca 100644
--- a/src/ui/viewmodels/OverlayManager.cpp
+++ b/src/ui/viewmodels/OverlayManager.cpp
@@ -484,7 +484,7 @@ void OverlayManager::UpdatePopup(ra::ui::drawing::ISurface& pSurface, const Popu
         if (nOldWidth != 0)
         {
             const int nHeight = pNewImage.GetHeight() + std::abs(nNewPos.Y - nOldY);
-            if (nNewPos.X > nOldX)
+            if (nNewPos.X > nOldX && nOldX + ra::to_signed(nOldWidth) >= 0)
                 pSurface.FillRectangle(nOldX - nFudge, nOldY, nNewPos.X - nOldX + nFudge, nHeight, ra::ui::Color::Transparent);
 
             const int nOldRightBound = nOldX + nOldWidth;
@@ -503,7 +503,7 @@ void OverlayManager::UpdatePopup(ra::ui::drawing::ISurface& pSurface, const Popu
 
         if (nOldHeight != 0)
         {
-            if (nNewPos.Y > nOldY)
+            if (nNewPos.Y > nOldY && nOldY + ra::to_signed(nOldHeight) >= 0)
                 pSurface.FillRectangle(nNewPos.X, nOldY - nFudge, pNewImage.GetWidth(), nNewPos.Y - nOldY + nFudge, ra::ui::Color::Transparent);
 
             const int nOldLowerBound = nOldY + nOldHeight;
@@ -605,11 +605,11 @@ void OverlayManager::UpdateScoreTrackers(ra::ui::drawing::ISurface& pSurface, Po
     {
         auto& vmTracker = **pIter;
 
-        if (bEnabled)
-            UpdatePopup(pSurface, pPopupLocations, fElapsed, vmTracker);
-
         if (vmTracker.IsDestroyPending())
         {
+            // call UpdatePopup to force it to "unpaint"
+            UpdatePopup(pSurface, pPopupLocations, fElapsed, vmTracker);
+
             pIter = m_vScoreTrackers.erase(pIter);
         }
         else
@@ -634,6 +634,7 @@ void OverlayManager::UpdateScoreTrackers(ra::ui::drawing::ISurface& pSurface, Po
                     }
                 }
 
+                UpdatePopup(pSurface, pPopupLocations, fElapsed, vmTracker);
                 AdjustLocationForPopup(pPopupLocations, vmTracker);
             }
 
@@ -690,7 +691,12 @@ void OverlayManager::UpdateChallengeIndicators(ra::ui::drawing::ISurface& pSurfa
 
 void OverlayManager::UpdateProgressTracker(ra::ui::drawing::ISurface& pSurface, PopupLocations& pPopupLocations, double fElapsed)
 {
-    assert(m_vmProgressTracker != nullptr);
+    Expects(m_vmProgressTracker != nullptr);
+
+    UpdatePopup(pSurface, pPopupLocations, fElapsed, *m_vmProgressTracker);
+
+    if (m_vmProgressTracker->IsAnimationComplete())
+        m_vmProgressTracker->SetDestroyPending();
 
     if (m_vmProgressTracker->IsDestroyPending())
     {
@@ -698,23 +704,14 @@ void OverlayManager::UpdateProgressTracker(ra::ui::drawing::ISurface& pSurface,
         UpdatePopup(pSurface, pPopupLocations, fElapsed, *m_vmProgressTracker);
 
         m_vmProgressTracker.reset();
-        return;
     }
-
-    const auto& pConfiguration = ra::services::ServiceLocator::Get<ra::services::IConfiguration>();
-    const auto bEnabled = (pConfiguration.GetPopupLocation(ra::ui::viewmodels::Popup::Progress) != ra::ui::viewmodels::PopupLocation::None);
-
-    if (bEnabled)
+    else
     {
-        UpdatePopup(pSurface, pPopupLocations, fElapsed, *m_vmProgressTracker);
-
-        if (m_vmProgressTracker->IsAnimationComplete())
-        {
-            m_vmProgressTracker->SetDestroyPending();
-            return;
-        }
+        const auto& pConfiguration = ra::services::ServiceLocator::Get<ra::services::IConfiguration>();
+        const auto bEnabled = (pConfiguration.GetPopupLocation(ra::ui::viewmodels::Popup::Progress) != ra::ui::viewmodels::PopupLocation::None);
 
-        AdjustLocationForPopup(pPopupLocations, *m_vmProgressTracker);
+        if (bEnabled)
+            AdjustLocationForPopup(pPopupLocations, *m_vmProgressTracker);
     }
 }
 


Test patch:
diff --git a/tests/data/models/TriggerValidation_Tests.cpp b/tests/data/models/TriggerValidation_Tests.cpp
index 7a0a42fc..9f5d70c4 100644
--- a/tests/data/models/TriggerValidation_Tests.cpp
+++ b/tests/data/models/TriggerValidation_Tests.cpp
@@ -3,6 +3,7 @@
 #include "data\models\TriggerValidation.hh"
 
 #include "tests\mocks\MockConsoleContext.hh"
+#include "tests\mocks\MockEmulatorContext.hh"
 
 using namespace Microsoft::VisualStudio::CppUnitTestFramework;
 
@@ -146,6 +147,29 @@ public:
         AssertValidation("I:0xX1234_0xH1234>5_0xHFFFFFF00>5", L"Condition 3: Address FFFFFF00 out of range (max FFFF)");
     }
 
+    TEST_METHOD(TestAddressRangeArcade)
+    {
+        // arcade doesn't provide a max address as each game can have a different amount of memory
+        // instead, max valid address is determined by how much memory the emulator/core exposes
+        ra::data::context::mocks::MockConsoleContext mockConsoleContext(Arcade, L"Arcade");
+        Assert::AreEqual(0U, mockConsoleContext.MaxAddress());
+        ra::data::context::mocks::MockEmulatorContext mockEmulatorContext;
+        unsigned char pMemory[256];
+        mockEmulatorContext.MockMemory(pMemory, sizeof(pMemory));
+        Assert::AreEqual({ 256U }, mockEmulatorContext.TotalMemorySize());
+
+        AssertValidation("0xH12>0xH34", L"");
+        AssertValidation("0xH123>0xH34", L"Condition 1: Address 0123 out of range (max 00FF)");
+        AssertValidation("0xH12>0xH345", L"Condition 1: Address 0345 out of range (max 00FF)");
+        AssertValidation("0xH123>0xH123", L"Condition 1: Address 0123 out of range (max 00FF)");
+        AssertValidation("0xX12>h123", L"");
+
+        // edge cases
+        AssertValidation("0xH00>5", L"");
+        AssertValidation("0xHFF>5", L"");
+        AssertValidation("0xH100>5", L"Condition 1: Address 0100 out of range (max 00FF)");
+    }
+
     TEST_METHOD(TestLeaderboardConditionTypes)
     {
         AssertLeaderboardValidation("0xH1234<99_0x2345<50", L"");