diff --git a/src/data/context/GameContext.hh b/src/data/context/GameContext.hh
index 766e16e2..374501b2 100644
--- a/src/data/context/GameContext.hh
+++ b/src/data/context/GameContext.hh
@@ -72,7 +72,13 @@ public:
///
/// Sets the game play mode.
///
- void SetMode(Mode nMode) noexcept { m_nMode = nMode; }
+ void SetMode(Mode nMode)
+ {
+ m_nMode = nMode;
+
+ if (m_nGameId != 0)
+ OnActiveGameChanged();
+ }
///
/// Gets the assets for the current game.
diff --git a/src/ui/viewmodels/IntegrationMenuViewModel.cpp b/src/ui/viewmodels/IntegrationMenuViewModel.cpp
index eac8dba5..023f3512 100644
--- a/src/ui/viewmodels/IntegrationMenuViewModel.cpp
+++ b/src/ui/viewmodels/IntegrationMenuViewModel.cpp
@@ -3,6 +3,8 @@
#include "RA_Log.h"
#include "RA_Resource.h"
+#include "data/context/ConsoleContext.hh"
+#include "data/context/GameContext.hh"
#include "data/context/UserContext.hh"
#include "services/IConfiguration.hh"
@@ -15,6 +17,7 @@
#include "ui/viewmodels/LoginViewModel.hh"
#include "ui/viewmodels/MessageBoxViewModel.hh"
#include "ui/viewmodels/OverlaySettingsViewModel.hh"
+#include "ui/viewmodels/UnknownGameViewModel.hh"
#include "ui/viewmodels/WindowManager.hh"
namespace ra {
@@ -289,8 +292,30 @@ void IntegrationMenuViewModel::ReportBrokenAchievements()
void IntegrationMenuViewModel::ShowGameHash()
{
- ra::ui::viewmodels::GameChecksumViewModel vmGameChecksum;
- vmGameChecksum.ShowModal();
+ auto& pGameContext = ra::services::ServiceLocator::GetMutable();
+ if (pGameContext.GetMode() == ra::data::context::GameContext::Mode::CompatibilityTest)
+ {
+ ra::ui::viewmodels::UnknownGameViewModel vmUnknownGame;
+ vmUnknownGame.InitializeTestCompatibilityMode();
+
+ auto sEstimatedGameTitle = ra::services::ServiceLocator::Get().GetGameTitle();
+ vmUnknownGame.SetEstimatedGameName(ra::Widen(sEstimatedGameTitle));
+ vmUnknownGame.SetSystemName(ra::services::ServiceLocator::Get().Name());
+
+ if (vmUnknownGame.ShowModal() == ra::ui::DialogResult::OK)
+ {
+ // Test button is disabled, can only get here by clicking Link
+ Expects(!vmUnknownGame.GetTestMode());
+
+ pGameContext.SetMode(ra::data::context::GameContext::Mode::Normal);
+ pGameContext.RefreshUnlocks();
+ }
+ }
+ else
+ {
+ ra::ui::viewmodels::GameChecksumViewModel vmGameChecksum;
+ vmGameChecksum.ShowModal();
+ }
}
} // namespace viewmodels
diff --git a/src/ui/viewmodels/UnknownGameViewModel.cpp b/src/ui/viewmodels/UnknownGameViewModel.cpp
index e7de830b..432bb308 100644
--- a/src/ui/viewmodels/UnknownGameViewModel.cpp
+++ b/src/ui/viewmodels/UnknownGameViewModel.cpp
@@ -6,6 +6,7 @@
#include "api\SubmitNewTitle.hh"
#include "data\context\ConsoleContext.hh"
+#include "data\context\GameContext.hh"
#include "services\IClipboard.hh"
@@ -16,6 +17,8 @@ namespace ui {
namespace viewmodels {
const IntModelProperty UnknownGameViewModel::SelectedGameIdProperty("UnknownGameViewModel", "SelectedGameId", 0);
+const BoolModelProperty UnknownGameViewModel::IsSelectedGameEnabledProperty("UnknownGameViewModel", "IsSelectedGameEnabled", true);
+const BoolModelProperty UnknownGameViewModel::IsAssociateEnabledProperty("UnknownGameViewModel", "IsAssociateEnabled", true);
const StringModelProperty UnknownGameViewModel::NewGameNameProperty("UnknownGameViewModel", "NewGameName", L"");
const StringModelProperty UnknownGameViewModel::ChecksumProperty("UnknownGameViewModel", "Checksum", L"");
const StringModelProperty UnknownGameViewModel::EstimatedGameNameProperty("UnknownGameViewModel", "EstimatedGameName", L"");
@@ -33,6 +36,9 @@ void UnknownGameViewModel::InitializeGameTitles()
m_vGameTitles.Add(0U, L"");
+ SetValue(IsSelectedGameEnabledProperty, false);
+ SetValue(IsAssociateEnabledProperty, false);
+
ra::api::FetchGamesList::Request request;
request.ConsoleId = pConsoleContext.Id();
@@ -49,14 +55,30 @@ void UnknownGameViewModel::InitializeGameTitles()
}
else
{
+ m_vGameTitles.BeginUpdate();
for (const auto& pGame : response.Games)
m_vGameTitles.Add(pGame.Id, pGame.Name);
+ m_vGameTitles.EndUpdate();
}
m_vGameTitles.Freeze();
+
+ SetValue(IsAssociateEnabledProperty, true);
+ SetValue(IsSelectedGameEnabledProperty, true);
});
}
+void UnknownGameViewModel::InitializeTestCompatibilityMode()
+{
+ const auto& pGameContext = ra::services::ServiceLocator::Get();
+ m_vGameTitles.Add(pGameContext.GameId(), pGameContext.GameTitle());
+ m_vGameTitles.Freeze();
+ SetSelectedGameId(pGameContext.GameId());
+ SetChecksum(ra::Widen(pGameContext.GameHash()));
+
+ SetValue(IsSelectedGameEnabledProperty, false);
+}
+
bool UnknownGameViewModel::Associate()
{
ra::api::SubmitNewTitle::Request request;
diff --git a/src/ui/viewmodels/UnknownGameViewModel.hh b/src/ui/viewmodels/UnknownGameViewModel.hh
index 29a32eef..94e8f54f 100644
--- a/src/ui/viewmodels/UnknownGameViewModel.hh
+++ b/src/ui/viewmodels/UnknownGameViewModel.hh
@@ -54,6 +54,21 @@ public:
///
void InitializeGameTitles();
+ ///
+ /// Initializes the collection (asynchronously).
+ ///
+ void InitializeTestCompatibilityMode();
+
+ ///
+ /// The for whether or not a game can be selected.
+ ///
+ static const BoolModelProperty IsSelectedGameEnabledProperty;
+
+ ///
+ /// Gets whether or not a game can be selected.
+ ///
+ bool IsSelectedGameEnabled() const { return GetValue(IsSelectedGameEnabledProperty); }
+
///
/// The for the new game name.
///
@@ -134,6 +149,16 @@ public:
///
void SetTestMode(bool bValue) { SetValue(TestModeProperty, bValue); }
+ ///
+ /// The for whether or not the link button is enabled.
+ ///
+ static const BoolModelProperty IsAssociateEnabledProperty;
+
+ ///
+ /// Gets whether or not the link button is enabled.
+ ///
+ bool IsAssociateEnabled() const { return GetValue(IsAssociateEnabledProperty); }
+
///
/// Command handler for Associate button.
///
diff --git a/src/ui/win32/UnknownGameDialog.cpp b/src/ui/win32/UnknownGameDialog.cpp
index edb0db5b..4bdbf914 100644
--- a/src/ui/win32/UnknownGameDialog.cpp
+++ b/src/ui/win32/UnknownGameDialog.cpp
@@ -34,13 +34,18 @@ UnknownGameDialog::UnknownGameDialog(ra::ui::viewmodels::UnknownGameViewModel& v
m_bindExistingTitle.BindItems(vmUnknownGame.GameTitles());
m_bindExistingTitle.BindSelectedItem(ra::ui::viewmodels::UnknownGameViewModel::SelectedGameIdProperty);
+ m_bindWindow.BindEnabled(IDC_RA_KNOWNGAMES, ra::ui::viewmodels::UnknownGameViewModel::IsSelectedGameEnabledProperty);
m_bindNewTitle.BindText(ra::ui::viewmodels::UnknownGameViewModel::NewGameNameProperty,
ra::ui::win32::bindings::TextBoxBinding::UpdateMode::KeyPress);
+ m_bindWindow.BindEnabled(IDC_RA_GAMETITLE, ra::ui::viewmodels::UnknownGameViewModel::IsSelectedGameEnabledProperty);
m_bindWindow.BindLabel(IDC_RA_GAMENAME, ra::ui::viewmodels::UnknownGameViewModel::EstimatedGameNameProperty);
m_bindWindow.BindLabel(IDC_RA_SYSTEMNAME, ra::ui::viewmodels::UnknownGameViewModel::SystemNameProperty);
m_bindWindow.BindLabel(IDC_RA_CHECKSUM, ra::ui::viewmodels::UnknownGameViewModel::ChecksumProperty);
+
+ m_bindWindow.BindEnabled(IDC_RA_LINK, ra::ui::viewmodels::UnknownGameViewModel::IsAssociateEnabledProperty);
+ m_bindWindow.BindEnabled(IDOK, ra::ui::viewmodels::UnknownGameViewModel::IsSelectedGameEnabledProperty);
}
BOOL UnknownGameDialog::OnInitDialog()
diff --git a/tests/data/context/GameContext_Tests.cpp b/tests/data/context/GameContext_Tests.cpp
index 7d7910d2..67acc734 100644
--- a/tests/data/context/GameContext_Tests.cpp
+++ b/tests/data/context/GameContext_Tests.cpp
@@ -2667,6 +2667,49 @@ TEST_CLASS(GameContext_Tests)
const auto* pNote1 = game.FindCodeNote(1234U);
Assert::IsNull(pNote1);
}
+
+ TEST_METHOD(TestSetModeNotify)
+ {
+ class NotifyHarness : public GameContext::NotifyTarget
+ {
+ public:
+ bool m_bNotified = false;
+
+ protected:
+ void OnActiveGameChanged() noexcept override { m_bNotified = true; }
+ };
+ NotifyHarness notifyHarness;
+
+ GameContextHarness game;
+ game.mockServer.HandleRequest([](const ra::api::FetchGameData::Request&, ra::api::FetchGameData::Response& response)
+ {
+ response.Title = L"GameTitle";
+ response.ImageIcon = "9743";
+ return true;
+ });
+
+ Assert::AreEqual(GameContext::Mode::Normal, game.GetMode());
+
+ game.AddNotifyTarget(notifyHarness);
+ game.LoadGame(0U);
+
+ Assert::AreEqual(0U, game.GameId());
+ Assert::IsFalse(notifyHarness.m_bNotified);
+
+ game.SetMode(GameContext::Mode::CompatibilityTest);
+ Assert::AreEqual(GameContext::Mode::CompatibilityTest, game.GetMode());
+ Assert::IsFalse(notifyHarness.m_bNotified);
+
+ game.LoadGame(1U, GameContext::Mode::CompatibilityTest);
+ Assert::AreEqual(1U, game.GameId());
+ Assert::AreEqual(GameContext::Mode::CompatibilityTest, game.GetMode());
+ Assert::IsTrue(notifyHarness.m_bNotified);
+
+ notifyHarness.m_bNotified = false;
+ game.SetMode(GameContext::Mode::Normal);
+ Assert::AreEqual(GameContext::Mode::Normal, game.GetMode());
+ Assert::IsTrue(notifyHarness.m_bNotified);
+ }
};
} // namespace tests
diff --git a/tests/ui/viewmodels/IntegrationMenuViewModel_Tests.cpp b/tests/ui/viewmodels/IntegrationMenuViewModel_Tests.cpp
index 3dd42a70..8dd397d8 100644
--- a/tests/ui/viewmodels/IntegrationMenuViewModel_Tests.cpp
+++ b/tests/ui/viewmodels/IntegrationMenuViewModel_Tests.cpp
@@ -13,10 +13,13 @@
#include "ui\viewmodels\MemoryInspectorViewModel.hh"
#include "ui\viewmodels\OverlaySettingsViewModel.hh"
#include "ui\viewmodels\RichPresenceMonitorViewModel.hh"
+#include "ui\viewmodels\UnknownGameViewModel.hh"
+#include "tests\data\DataAsserts.hh"
#include "tests\ui\UIAsserts.hh"
#include "tests\mocks\MockAchievementRuntime.hh"
#include "tests\mocks\MockConfiguration.hh"
+#include "tests\mocks\MockConsoleContext.hh"
#include "tests\mocks\MockDesktop.hh"
#include "tests\mocks\MockEmulatorContext.hh"
#include "tests\mocks\MockGameContext.hh"
@@ -40,6 +43,7 @@ TEST_CLASS(IntegrationMenuViewModel_Tests)
{
public:
ra::api::mocks::MockServer mockServer;
+ ra::data::context::mocks::MockConsoleContext mockConsoleContext;
ra::data::context::mocks::MockEmulatorContext mockEmulatorContext;
ra::data::context::mocks::MockGameContext mockGameContext;
ra::data::context::mocks::MockUserContext mockUserContext;
@@ -530,6 +534,47 @@ TEST_CLASS(IntegrationMenuViewModel_Tests)
menu.AssertShowWindow(IDM_RA_GETROMCHECKSUM, true, "", DialogResult::None);
}
+ TEST_METHOD(TestShowGameHashTestCompatibilityModeCancel)
+ {
+ IntegrationMenuViewModelHarness menu;
+
+ bool bDialogShown = false;
+ menu.mockDesktop.ExpectWindow([&bDialogShown](ra::ui::viewmodels::UnknownGameViewModel& vmUnknown)
+ {
+ bDialogShown = true;
+
+ Assert::IsFalse(vmUnknown.IsSelectedGameEnabled());
+
+ return DialogResult::Cancel;
+ });
+
+ menu.mockGameContext.SetMode(ra::data::context::GameContext::Mode::CompatibilityTest);
+ menu.ActivateMenuItem(IDM_RA_GETROMCHECKSUM);
+
+ Assert::IsTrue(bDialogShown);
+ Assert::AreEqual(ra::data::context::GameContext::Mode::CompatibilityTest, menu.mockGameContext.GetMode());
+ }
+
+ TEST_METHOD(TestShowGameHashTestCompatibilityModeAssociate)
+ {
+ IntegrationMenuViewModelHarness menu;
+
+ bool bDialogShown = false;
+ menu.mockDesktop.ExpectWindow([&bDialogShown](ra::ui::viewmodels::UnknownGameViewModel& vmUnknown)
+ {
+ bDialogShown = true;
+
+ Assert::IsFalse(vmUnknown.IsSelectedGameEnabled());
+
+ return DialogResult::OK;
+ });
+
+ menu.mockGameContext.SetMode(ra::data::context::GameContext::Mode::CompatibilityTest);
+ menu.ActivateMenuItem(IDM_RA_GETROMCHECKSUM);
+
+ Assert::IsTrue(bDialogShown);
+ Assert::AreEqual(ra::data::context::GameContext::Mode::Normal, menu.mockGameContext.GetMode());
+ }
};
} // namespace tests
diff --git a/tests/ui/viewmodels/UnknownGameViewModel_Tests.cpp b/tests/ui/viewmodels/UnknownGameViewModel_Tests.cpp
index d5be01c6..03bc6dec 100644
--- a/tests/ui/viewmodels/UnknownGameViewModel_Tests.cpp
+++ b/tests/ui/viewmodels/UnknownGameViewModel_Tests.cpp
@@ -7,6 +7,7 @@
#include "tests\mocks\MockClipboard.hh"
#include "tests\mocks\MockConsoleContext.hh"
#include "tests\mocks\MockDesktop.hh"
+#include "tests\mocks\MockGameContext.hh"
#include "tests\mocks\MockServer.hh"
#include "tests\mocks\MockThreadPool.hh"
@@ -27,6 +28,7 @@ TEST_CLASS(UnknownGameViewModel_Tests)
public:
ra::api::mocks::MockServer mockServer;
ra::data::context::mocks::MockConsoleContext mockConsoleContext;
+ ra::data::context::mocks::MockGameContext mockGameContext;
ra::services::mocks::MockThreadPool mockThreadPool;
ra::ui::mocks::MockDesktop mockDesktop;
@@ -58,6 +60,7 @@ TEST_CLASS(UnknownGameViewModel_Tests)
Assert::AreEqual(std::wstring(L""), vmUnknownGame.GetNewGameName());
Assert::AreEqual(std::wstring(L""), vmUnknownGame.GetChecksum());
Assert::AreEqual({ 0U }, vmUnknownGame.GameTitles().Count());
+ Assert::IsTrue(vmUnknownGame.IsSelectedGameEnabled());
}
TEST_METHOD(TestInitializeGameTitles)
@@ -79,18 +82,22 @@ TEST_CLASS(UnknownGameViewModel_Tests)
vmUnknownGame.InitializeGameTitles();
- // item should be loaded immediately
+ // item should be loaded immediately, but linking should be disabled
Assert::AreEqual({ 1U }, vmUnknownGame.GameTitles().Count());
Assert::AreEqual(std::wstring(L""), vmUnknownGame.GameTitles().GetLabelForId(0));
Assert::IsFalse(vmUnknownGame.GameTitles().IsFrozen());
+ Assert::IsFalse(vmUnknownGame.IsAssociateEnabled());
+ Assert::IsFalse(vmUnknownGame.IsSelectedGameEnabled());
- // after server response, collection should have three items
+ // after server response, collection should have three items and be frozen. linking should be enabled
vmUnknownGame.mockThreadPool.ExecuteNextTask();
Assert::AreEqual({ 3U }, vmUnknownGame.GameTitles().Count());
Assert::AreEqual(std::wstring(L""), vmUnknownGame.GameTitles().GetLabelForId(0));
Assert::AreEqual(std::wstring(L"Game 33"), vmUnknownGame.GameTitles().GetLabelForId(33));
Assert::AreEqual(std::wstring(L"Game 37"), vmUnknownGame.GameTitles().GetLabelForId(37));
Assert::IsTrue(vmUnknownGame.GameTitles().IsFrozen());
+ Assert::IsTrue(vmUnknownGame.IsAssociateEnabled());
+ Assert::IsTrue(vmUnknownGame.IsSelectedGameEnabled());
}
TEST_METHOD(TestNameAndSelectionInteraction)
@@ -388,7 +395,62 @@ TEST_CLASS(UnknownGameViewModel_Tests)
Assert::IsFalse(vmUnknownGame.BeginTest());
}
-};
+
+ TEST_METHOD(TestInitializeTestCompatibilityMode)
+ {
+ UnknownGameViewModelHarness vmUnknownGame;
+ vmUnknownGame.mockConsoleContext.SetId(ConsoleID::C64);
+ vmUnknownGame.mockGameContext.SetGameId(33U);
+ vmUnknownGame.mockGameContext.SetGameTitle(L"Game 33");
+ vmUnknownGame.mockGameContext.SetGameHash("CHECKSUM");
+
+ vmUnknownGame.InitializeTestCompatibilityMode();
+
+ Assert::AreEqual({ 1U }, vmUnknownGame.GameTitles().Count());
+ Assert::AreEqual(33, vmUnknownGame.GameTitles().GetItemAt(0)->GetId());
+ Assert::AreEqual(std::wstring(L"Game 33"), vmUnknownGame.GameTitles().GetItemAt(0)->GetLabel());
+ Assert::AreEqual(33, vmUnknownGame.GetSelectedGameId());
+ Assert::AreEqual(std::wstring(L"CHECKSUM"), vmUnknownGame.GetChecksum());
+ Assert::IsTrue(vmUnknownGame.GameTitles().IsFrozen());
+
+ Assert::IsFalse(vmUnknownGame.IsSelectedGameEnabled());
+ }
+
+ TEST_METHOD(TestAssociateExistingTestCompatibilityMode)
+ {
+ UnknownGameViewModelHarness vmUnknownGame;
+ vmUnknownGame.mockConsoleContext.SetId(ConsoleID::PSP);
+ vmUnknownGame.mockGameContext.SetGameId(40U);
+ vmUnknownGame.mockGameContext.SetGameTitle(L"Game 40");
+ vmUnknownGame.mockGameContext.SetGameHash("CHECKSUM");
+ vmUnknownGame.SetEstimatedGameName(L"GAME40");
+ vmUnknownGame.InitializeTestCompatibilityMode();
+
+ vmUnknownGame.mockDesktop.ExpectWindow([](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox)
+ {
+ Assert::AreEqual(std::wstring(L"Are you sure you want to add a new hash to 'Game 40'?"), vmMessageBox.GetHeader());
+ return ra::ui::DialogResult::Yes;
+ });
+
+ vmUnknownGame.mockServer.HandleRequest([]
+ (const ra::api::SubmitNewTitle::Request& request, ra::api::SubmitNewTitle::Response& response)
+ {
+ Assert::AreEqual(41U, request.ConsoleId);
+ Assert::AreEqual(std::string("CHECKSUM"), request.Hash);
+ Assert::AreEqual(std::wstring(L"Game 40"), request.GameName);
+ Assert::AreEqual(std::wstring(L"GAME40"), request.Description);
+ Assert::AreEqual(40U, request.GameId);
+
+ response.Result = ra::api::ApiResult::Success;
+ response.GameId = 40U;
+
+ return true;
+ });
+
+ Assert::IsTrue(vmUnknownGame.Associate());
+ Assert::AreEqual(40, vmUnknownGame.GetSelectedGameId());
+ Assert::IsFalse(vmUnknownGame.GetTestMode());
+ }};
} // namespace tests
} // namespace viewmodels