Skip to content

Commit

Permalink
export unknown game functionality for rc_client (#1070)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jamiras authored Feb 24, 2024
1 parent 8c66d82 commit f2e8239
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 14 deletions.
42 changes: 41 additions & 1 deletion src/services/AchievementRuntime.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "AchievementRuntime.hh"

#include "AchievementRuntimeExports.hh"
#include "Exports.hh"
#include "RA_Defs.h"
#include "RA_Log.h"
Expand Down Expand Up @@ -29,6 +30,7 @@
#include "ui\viewmodels\MessageBoxViewModel.hh"
#include "ui\viewmodels\OverlayManager.hh"
#include "ui\viewmodels\PopupMessageViewModel.hh"
#include "ui\viewmodels\UnknownGameViewModel.hh"
#include "ui\viewmodels\WindowManager.hh"

#include <rcheevos\include\rc_api_runtime.h>
Expand Down Expand Up @@ -147,13 +149,43 @@ static int RichPresenceOverride(rc_client_t*, char buffer[], size_t buffer_size)
return 0;
}


static uint32_t IdentifyUnknownHash(uint32_t console_id, const char* hash, rc_client_t*, void*)
{
RA_LOG_INFO("Could not identify game with hash %s", hash);

const auto& pEmulatorContext = ra::services::ServiceLocator::Get<ra::data::context::EmulatorContext>();
auto sEstimatedGameTitle = ra::Widen(pEmulatorContext.GetGameTitle());

ra::ui::viewmodels::UnknownGameViewModel vmUnknownGame;
vmUnknownGame.InitializeGameTitles(ra::itoe<ConsoleID>(console_id));
vmUnknownGame.SetSystemName(ra::Widen(rc_console_name(console_id)));
vmUnknownGame.SetChecksum(ra::Widen(hash));
vmUnknownGame.SetEstimatedGameName(sEstimatedGameTitle);
vmUnknownGame.SetNewGameName(sEstimatedGameTitle);

if (vmUnknownGame.ShowModal() == ra::ui::DialogResult::OK)
{
if (vmUnknownGame.GetTestMode())
{
ra::services::ServiceLocator::GetMutable<ra::data::context::GameContext>().SetMode(
ra::data::context::GameContext::Mode::CompatibilityTest);
}

return vmUnknownGame.GetSelectedGameId();
}

return 0;
}

AchievementRuntime::AchievementRuntime()
{
m_pClient.reset(rc_client_create(AchievementRuntime::ReadMemory, AchievementRuntime::ServerCallAsync));

m_pClient->callbacks.can_submit_achievement_unlock = CanSubmitAchievementUnlock;
m_pClient->callbacks.can_submit_leaderboard_entry = CanSubmitLeaderboardEntry;
m_pClient->callbacks.rich_presence_override = RichPresenceOverride;
m_pClient->callbacks.identify_unknown_hash = IdentifyUnknownHash;

#ifndef RA_UTEST
rc_client_enable_logging(m_pClient.get(), RC_CLIENT_LOG_LEVEL_VERBOSE, AchievementRuntime::LogMessage);
Expand Down Expand Up @@ -1138,13 +1170,21 @@ rc_client_async_handle_t* AchievementRuntime::BeginIdentifyAndLoadGame(uint32_t
}

GSL_SUPPRESS_CON3
void AchievementRuntime::LoadGameCallback(int nResult, const char* sErrorMessage, rc_client_t*, void* pUserdata)
void AchievementRuntime::LoadGameCallback(int nResult, const char* sErrorMessage, rc_client_t* pClient, void* pUserdata)
{
auto* wrapper = static_cast<LoadGameCallbackWrapper*>(pUserdata);
Expects(wrapper != nullptr);

if (nResult == RC_OK || nResult == RC_NO_GAME_LOADED)
{
// this has to be done before calling InitializeFromAchievementRuntime so the address validation
// doesn't flag every achievement as invalid.
if (IsExternalRcheevosClient() && pClient->game)
{
_RA_SetConsoleID(pClient->game->public_.console_id);
ResetEmulatorMemoryRegionsForRcheevosClient();
}

// initialize the game context
auto& pGameContext = ra::services::ServiceLocator::GetMutable<ra::data::context::GameContext>();
pGameContext.InitializeFromAchievementRuntime(wrapper->m_mAchievementDefinitions,
Expand Down
79 changes: 68 additions & 11 deletions src/services/AchievementRuntimeExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,23 @@ class AchievementRuntimeExports : private AchievementRuntime
ResetMemory();
}

static void ResetMemory()
{
const auto& pConsoleContext = ra::services::ServiceLocator::Get<ra::data::context::ConsoleContext>();
const auto nNumBytes = pConsoleContext.MaxAddress() + 1;

auto& pEmulatorContext = ra::services::ServiceLocator::GetMutable<ra::data::context::EmulatorContext>();
pEmulatorContext.ClearMemoryBlocks();

if (nNumBytes > 1)
{
pEmulatorContext.AddMemoryBlock(0, nNumBytes,
AchievementRuntimeExports::ReadMemoryByte,
AchievementRuntimeExports::WriteMemoryByte);
pEmulatorContext.AddMemoryBlockReader(0, AchievementRuntimeExports::ReadMemoryBlock);
}
}

static void set_get_time_millisecs(rc_client_t* client, rc_get_time_millisecs_func_t handler)
{
s_callbacks.get_time_millisecs_handler = handler;
Expand Down Expand Up @@ -457,6 +474,15 @@ class AchievementRuntimeExports : private AchievementRuntime
s_callbacks.write_memory_handler = handler;
}

static void set_raintegration_get_game_name_function(rc_client_t* client, rc_client_raintegration_get_game_name_func_t handler)
{
s_callbacks.get_game_name_client = client;
s_callbacks.get_game_name_handler = handler;

auto& pEmulatorContext = ra::services::ServiceLocator::GetMutable<ra::data::context::EmulatorContext>();
pEmulatorContext.SetGetGameTitleFunction(GetGameTitle);
}

static void set_raintegration_event_handler(rc_client_t* client, rc_client_raintegration_event_handler_t handler) noexcept
{
s_callbacks.raintegration_event_client = client;
Expand Down Expand Up @@ -690,6 +716,9 @@ class AchievementRuntimeExports : private AchievementRuntime
rc_client_raintegration_write_memory_func_t write_memory_handler;
rc_client_t* write_memory_client;

rc_client_raintegration_get_game_name_func_t get_game_name_handler;
rc_client_t* get_game_name_client;

rc_get_time_millisecs_func_t get_time_millisecs_handler;
rc_client_t* get_time_millisecs_client;

Expand Down Expand Up @@ -751,17 +780,6 @@ class AchievementRuntimeExports : private AchievementRuntime
return 0;
}

static void ResetMemory()
{
const auto& pConsoleContext = ra::services::ServiceLocator::Get<ra::data::context::ConsoleContext>();
const auto nNumBytes = pConsoleContext.MaxAddress() + 1;

auto& pEmulatorContext = ra::services::ServiceLocator::GetMutable<ra::data::context::EmulatorContext>();
pEmulatorContext.ClearMemoryBlocks();
pEmulatorContext.AddMemoryBlock(0, nNumBytes, AchievementRuntimeExports::ReadMemoryByte, AchievementRuntimeExports::WriteMemoryByte);
pEmulatorContext.AddMemoryBlockReader(0, AchievementRuntimeExports::ReadMemoryBlock);
}

static void RaisePauseEvent() noexcept
{
RaiseIntegrationEvent(RC_CLIENT_RAINTEGRATION_EVENT_PAUSE);
Expand All @@ -779,6 +797,15 @@ class AchievementRuntimeExports : private AchievementRuntime
}
}

static void GetGameTitle(char buffer[])
{
Expects(buffer != nullptr);
buffer[0] = '\0';

if (s_callbacks.get_game_name_handler)
s_callbacks.get_game_name_handler(buffer, 256, s_callbacks.raintegration_event_client);
}

static rc_clock_t GetTimeMillisecsExternal(const rc_client_t*) noexcept(false)
{
if (s_callbacks.get_time_millisecs_handler)
Expand Down Expand Up @@ -831,6 +858,11 @@ bool IsExternalRcheevosClient() noexcept
return ra::services::AchievementRuntimeExports::IsExternalRcheevosClient();
}

void ResetEmulatorMemoryRegionsForRcheevosClient()
{
ra::services::AchievementRuntimeExports::ResetMemory();
}

void SyncClientExternalRAIntegrationMenuItem(int nMenuItemId)
{
ra::services::AchievementRuntimeExports::SyncMenuItem(nMenuItemId);
Expand Down Expand Up @@ -947,11 +979,36 @@ API void CCONV _Rcheevos_SetRAIntegrationWriteMemoryFunction(rc_client_t* client
ra::services::AchievementRuntimeExports::set_raintegration_write_memory_function(client, handler);
}

API void CCONV _Rcheevos_SetRAIntegrationGetGameNameFunction(rc_client_t* client, rc_client_raintegration_get_game_name_func_t handler)
{
ra::services::AchievementRuntimeExports::set_raintegration_get_game_name_function(client, handler);
}

API void CCONV _Rcheevos_SetRAIntegrationEventHandler(rc_client_t* client, rc_client_raintegration_event_handler_t handler)
{
ra::services::AchievementRuntimeExports::set_raintegration_event_handler(client, handler);
}

API int CCONV _Rcheevos_HasModifications(void)
{
const auto& pGameContext = ra::services::ServiceLocator::Get<ra::data::context::GameContext>();
for (gsl::index nIndex = 0; nIndex < gsl::narrow_cast<gsl::index>(pGameContext.Assets().Count()); ++nIndex)
{
const auto* pAsset = pGameContext.Assets().GetItemAt(nIndex);
if (pAsset && pAsset->IsModified())
{
switch (pAsset->GetType())
{
case ra::data::models::AssetType::Achievement:
case ra::data::models::AssetType::Leaderboard:
return true;
}
}
}

return false;
}

#ifdef __cplusplus
} // extern "C"
#endif
1 change: 1 addition & 0 deletions src/services/AchievementRuntimeExports.hh
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ void SyncClientExternalRAIntegrationMenuItem(int nMenuItemId);
void SyncClientExternalHardcoreState();
bool IsExternalRcheevosClient() noexcept;
void ResetExternalRcheevosClient() noexcept;
void ResetEmulatorMemoryRegionsForRcheevosClient();

#endif // !RA_SERVICES_ACHIEVEMENT_RUNTIME_EXPORTS_HH
14 changes: 13 additions & 1 deletion src/ui/viewmodels/UnknownGameViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "data\context\ConsoleContext.hh"
#include "data\context\GameContext.hh"
#include "data\context\UserContext.hh"

#include "services\IClipboard.hh"
#include "services\ILocalStorage.hh"
Expand Down Expand Up @@ -34,14 +35,25 @@ UnknownGameViewModel::UnknownGameViewModel() noexcept
void UnknownGameViewModel::InitializeGameTitles()
{
const auto& pConsoleContext = ra::services::ServiceLocator::Get<ra::data::context::ConsoleContext>();
InitializeGameTitles(pConsoleContext.Id());
}

void UnknownGameViewModel::InitializeGameTitles(ConsoleID consoleId)
{
m_vGameTitles.Add(0U, L"<New Title>");

if (!ra::services::ServiceLocator::Get<ra::data::context::UserContext>().IsLoggedIn())
{
ra::ui::viewmodels::MessageBoxViewModel::ShowErrorMessage(*this, L"Could not retrieve list of existing games",
L"User is not logged in");
return;
}

SetValue(IsSelectedGameEnabledProperty, false);
SetValue(IsAssociateEnabledProperty, false);

ra::api::FetchGamesList::Request request;
request.ConsoleId = pConsoleContext.Id();
request.ConsoleId = consoleId;

request.CallAsync([this, pAsyncHandle = CreateAsyncHandle()](const ra::api::FetchGamesList::Response& response)
{
Expand Down
5 changes: 5 additions & 0 deletions src/ui/viewmodels/UnknownGameViewModel.hh
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ public:
/// <summary>
/// Initializes the <see cref="GameTitles"/> collection (asynchronously).
/// </summary>
void InitializeGameTitles(ConsoleID nConsoleId);

/// <summary>
/// Freezes the selected game.
/// </summary>
void InitializeTestCompatibilityMode();

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions tests/data/context/GameContext_Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ TEST_CLASS(GameContext_Tests)

game.LoadGame(1U, "0123456789abcdeffedcba987654321");

Assert::IsTrue(bDialogShown);
Assert::AreEqual(0U, game.GameId());
Assert::AreEqual(ra::data::context::GameContext::Mode::Normal, game.GetMode());
Assert::AreEqual(std::wstring(L""), game.GameTitle());
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/viewmodels/UnknownGameViewModel_Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "tests\mocks\MockLocalStorage.hh"
#include "tests\mocks\MockServer.hh"
#include "tests\mocks\MockThreadPool.hh"
#include "tests\mocks\MockUserContext.hh"

#include "tests\ui\UIAsserts.hh"

Expand All @@ -30,10 +31,16 @@ TEST_CLASS(UnknownGameViewModel_Tests)
ra::api::mocks::MockServer mockServer;
ra::data::context::mocks::MockConsoleContext mockConsoleContext;
ra::data::context::mocks::MockGameContext mockGameContext;
ra::data::context::mocks::MockUserContext mockUserContext;
ra::services::mocks::MockThreadPool mockThreadPool;
ra::services::mocks::MockLocalStorage mockLocalStorage;
ra::ui::mocks::MockDesktop mockDesktop;

UnknownGameViewModelHarness()
{
mockUserContext.Initialize("User", "ApiToken");
}

void MockGameTitles()
{
m_vGameTitles.Add(20, L"Game 20");
Expand Down

0 comments on commit f2e8239

Please sign in to comment.