Skip to content

Commit

Permalink
Merge pull request #19002 from hrydgard/initial-ra-integration
Browse files Browse the repository at this point in the history
Add initial RAIntegration support through rc_client
  • Loading branch information
hrydgard authored Apr 5, 2024
2 parents 32620ff + 6f825e4 commit c9515ce
Show file tree
Hide file tree
Showing 25 changed files with 198 additions and 53 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ build
libretro/obj/local

ppsspp_retroachievements.dat
RACache
RAPrefs_PPSSPP.cfg

# For CLion
cmake-build-*/
4 changes: 3 additions & 1 deletion Common/System/System.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ enum SystemProperty {
SYSPROP_USER_DOCUMENTS_DIR,

SYSPROP_OK_BUTTON_LEFT,

SYSPROP_MAIN_WINDOW_HANDLE,
};

enum class SystemNotification {
Expand Down Expand Up @@ -252,7 +254,7 @@ enum class UIMessage {

std::string System_GetProperty(SystemProperty prop);
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop);
int System_GetPropertyInt(SystemProperty prop);
int64_t System_GetPropertyInt(SystemProperty prop);
float System_GetPropertyFloat(SystemProperty prop);
bool System_GetPropertyBool(SystemProperty prop);

Expand Down
1 change: 1 addition & 0 deletions Core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ static bool DefaultSasThread() {
static const ConfigSetting achievementSettings[] = {
// Core settings
ConfigSetting("AchievementsEnable", &g_Config.bAchievementsEnable, true, CfgFlag::DEFAULT),
ConfigSetting("AchievementsEnableRAIntegration", &g_Config.bAchievementsEnableRAIntegration, false, CfgFlag::DEFAULT),
ConfigSetting("AchievementsChallengeMode", &g_Config.bAchievementsChallengeMode, true, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
ConfigSetting("AchievementsEncoreMode", &g_Config.bAchievementsEncoreMode, false, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
ConfigSetting("AchievementsUnofficial", &g_Config.bAchievementsUnofficial, false, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
Expand Down
1 change: 1 addition & 0 deletions Core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ struct Config {
bool bAchievementsSoundEffects;
bool bAchievementsLogBadMemReads;
bool bAchievementsSaveStateInHardcoreMode;
bool bAchievementsEnableRAIntegration;

// Positioning of the various notifications
int iAchievementsLeaderboardTrackerPos;
Expand Down
8 changes: 4 additions & 4 deletions Core/Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ext\libchdr\include;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>RC_CLIENT_SUPPORTS_RAINTEGRATION;_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
Expand All @@ -166,7 +166,7 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ext\libchdr\include;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib;../ext/zstd/lib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>RC_CLIENT_SUPPORTS_RAINTEGRATION;_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
Expand Down Expand Up @@ -257,7 +257,7 @@
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>RC_CLIENT_SUPPORTS_RAINTEGRATION;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
Expand Down Expand Up @@ -294,7 +294,7 @@
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<WholeProgramOptimization>false</WholeProgramOptimization>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<PreprocessorDefinitions>USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>RC_CLIENT_SUPPORTS_RAINTEGRATION;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_LIB;NDEBUG;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StringPooling>true</StringPooling>
Expand Down
126 changes: 119 additions & 7 deletions Core/RetroAchievements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include "ext/rcheevos/include/rcheevos.h"
#include "ext/rcheevos/include/rc_client.h"
#include "ext/rcheevos/include/rc_client_raintegration.h"
#include "ext/rcheevos/include/rc_api_user.h"
#include "ext/rcheevos/include/rc_api_info.h"
#include "ext/rcheevos/include/rc_api_request.h"
Expand All @@ -38,8 +39,6 @@
#include "Common/Log.h"
#include "Common/File/Path.h"
#include "Common/File/FileUtil.h"
#include "Core/FileLoaders/LocalFileLoader.h"
#include "Core/FileSystems/BlockDevices.h"
#include "Common/Net/HTTPClient.h"
#include "Common/System/OSD.h"
#include "Common/System/System.h"
Expand All @@ -55,19 +54,28 @@
#include "Core/MemMap.h"
#include "Core/Config.h"
#include "Core/CoreParameter.h"
#include "Core/ELF/ParamSFO.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "Core/FileLoaders/LocalFileLoader.h"
#include "Core/FileSystems/BlockDevices.h"
#include "Core/ELF/ParamSFO.h"
#include "Core/FileSystems/MetaFileSystem.h"
#include "Core/FileSystems/ISOFileSystem.h"
#include "Core/RetroAchievements.h"

#if RC_CLIENT_SUPPORTS_RAINTEGRATION

#include "Windows/MainWindow.h"

#endif

static bool HashISOFile(ISOFileSystem *fs, const std::string filename, md5_context *md5) {
int handle = fs->OpenFile(filename, FILEACCESS_READ);
if (handle < 0) {
return false;
}

uint32_t sz = fs->SeekFile(handle, 0, FILEMOVE_END);
uint32_t sz = (uint32_t)fs->SeekFile(handle, 0, FILEMOVE_END);
fs->SeekFile(handle, 0, FILEMOVE_BEGIN);
if (!sz) {
return false;
Expand Down Expand Up @@ -131,7 +139,7 @@ static Achievements::Statistics g_stats;
const std::string g_gameIconCachePrefix = "game:";
const std::string g_iconCachePrefix = "badge:";

Path s_game_path;
Path g_gamePath;
std::string s_game_hash;

std::set<uint32_t> g_activeChallenges;
Expand Down Expand Up @@ -461,6 +469,90 @@ static void login_token_callback(int result, const char *error_message, rc_clien
g_isLoggingIn = false;
}

bool RAIntegrationDirty() {
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
return rc_client_raintegration_has_modifications(g_rcClient);
#else
return false;
#endif
}

#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION

static void raintegration_get_game_name_handler(char *buffer, uint32_t buffer_size, rc_client_t *client) {
snprintf(buffer, buffer_size, "%s", g_gamePath.GetFilename().c_str());
}

static void raintegration_write_memory_handler(uint32_t address, uint8_t *buffer, uint32_t num_bytes, rc_client_t *client) {
// convert_retroachievements_address_to_real_address
uint32_t realAddress = address + PSP_MEMORY_OFFSET;
uint8_t *writePtr = Memory::GetPointerWriteRange(address, num_bytes);
if (writePtr) {
memcpy(writePtr, buffer, num_bytes);
}
}

static void raintegration_event_handler(const rc_client_raintegration_event_t *event, rc_client_t *client) {
switch (event->type) {
case RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED:
// The checked state of one of the menu items has changed and should be reflected in the UI.
// Call the handy helper function if the menu was created by rc_client_raintegration_rebuild_submenu.
rc_client_raintegration_update_menu_item(client, event->menu_item);
break;
case RC_CLIENT_RAINTEGRATION_EVENT_PAUSE:
// The toolkit has hit a breakpoint and wants to pause the emulator. Do so.
Core_EnableStepping(true, "ra_breakpoint");
break;
case RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED:
// Hardcore mode has been changed (either directly by the user, or disabled through the use of the tools).
// The frontend doesn't necessarily need to know that this value changed, they can still query it whenever
// it's appropriate, but the event lets the frontend do things like enable/disable rewind or cheats.
// handle_hardcore_changed();
break;
default:
ERROR_LOG(ACHIEVEMENTS, "Unsupported raintegration event %u\n", event->type);
break;
}
}

static void load_integration_callback(int result, const char *error_message, rc_client_t *client, void *userdata) {
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);

// If DLL not present, do nothing. User can still play without the toolkit.
switch (result) {
case RC_OK:
{
// DLL was loaded correctly.
g_OSD.Show(OSDType::MESSAGE_SUCCESS, ac->T("RAIntegration DLL loaded."));

rc_client_raintegration_set_event_handler(g_rcClient, &raintegration_event_handler);
rc_client_raintegration_set_write_memory_function(g_rcClient, &raintegration_write_memory_handler);
rc_client_raintegration_set_get_game_name_function(g_rcClient, &raintegration_get_game_name_handler);
HWND hWnd = (HWND)userdata;
rc_client_raintegration_rebuild_submenu(g_rcClient, GetMenu(hWnd));
break;
}
case RC_MISSING_VALUE:
// This is fine, proceeding to login.
g_OSD.Show(OSDType::MESSAGE_WARNING, ac->T("RAIntegration is enabled, but RAIntegration-x64.dll was not found."));
break;
case RC_ABORTED:
// This is fine, proceeding to login.
g_OSD.Show(OSDType::MESSAGE_WARNING, ac->T("Wrong version of RAIntegration-x64.dll?"));
break;
default:
g_OSD.Show(OSDType::MESSAGE_ERROR, StringFromFormat("RAIntegration init failed: %s", error_message));
// Bailing.
return;
}

// Things are ready to load a game. If the DLL was initialized, calling rc_client_begin_load_game will be redirected
// through the DLL so the toolkit has access to the game data. Similarly, things like rc_create_leaderboard_list will
// be redirected through the DLL to reflect any local changes made by the user.
TryLoginByToken(true);
}
#endif

void Initialize() {
if (!g_Config.bAchievementsEnable) {
_dbg_assert_(!g_rcClient);
Expand All @@ -477,14 +569,28 @@ void Initialize() {

// Provide a logging function to simplify debugging
rc_client_enable_logging(g_rcClient, RC_CLIENT_LOG_LEVEL_VERBOSE, log_message_callback);

if (!System_GetPropertyBool(SYSPROP_SUPPORTS_HTTPS)) {
// Disable SSL if not supported by our platform implementation.
rc_client_set_host(g_rcClient, "http://retroachievements.org");
}

rc_client_set_event_handler(g_rcClient, event_handler_callback);

#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
if (g_Config.bAchievementsEnableRAIntegration) {
wchar_t szFilePath[MAX_PATH];
GetModuleFileNameW(NULL, szFilePath, MAX_PATH);
for (int64_t i = wcslen(szFilePath) - 1; i > 0; i--) {
if (szFilePath[i] == '\\') {
szFilePath[i] = '\0';
break;
}
}
HWND hWnd = (HWND)System_GetPropertyInt(SYSPROP_MAIN_WINDOW_HANDLE);
rc_client_begin_load_raintegration(g_rcClient, szFilePath, hWnd, "PPSSPP", PPSSPP_GIT_VERSION, &load_integration_callback, hWnd);
return;
}
#endif
TryLoginByToken(true);
}

Expand Down Expand Up @@ -551,7 +657,7 @@ static void login_password_callback(int result, const char *error_message, rc_cl

bool LoginAsync(const char *username, const char *password) {
auto di = GetI18NCategory(I18NCat::DIALOG);
if (IsLoggedIn() || std::strlen(username) == 0 || std::strlen(password) == 0 || IsUsingRAIntegration())
if (IsLoggedIn() || std::strlen(username) == 0 || std::strlen(password) == 0)
return false;

g_OSD.SetProgressBar("cheevos_async_login", di->T("Logging in..."), 0, 0, 0, 0.0f);
Expand Down Expand Up @@ -587,6 +693,9 @@ void UpdateSettings() {

bool Shutdown() {
g_activeChallenges.clear();
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
rc_client_unload_raintegration(g_rcClient);
#endif
rc_client_destroy(g_rcClient);
g_rcClient = nullptr;
INFO_LOG(ACHIEVEMENTS, "Achievements shut down.");
Expand Down Expand Up @@ -832,6 +941,7 @@ void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoad
}

// The caller should hold off on executing game code until this turns false, checking with IsBlockingExecution()
g_gamePath = path;
g_isIdentifying = true;

// TODO: Fish the block device out of the loading process somewhere else. Though, probably easier to just do it here.
Expand Down Expand Up @@ -861,6 +971,8 @@ void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoad
void UnloadGame() {
if (g_rcClient) {
rc_client_unload_game(g_rcClient);
g_gamePath.clear();
s_game_hash.clear();
}
}

Expand Down
17 changes: 3 additions & 14 deletions Core/RetroAchievements.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,6 @@ struct Statistics {
int badMemoryAccessCount;
};

// RAIntegration only exists for Windows, so no point checking it on other platforms.
#ifdef WITH_RAINTEGRATION

bool IsUsingRAIntegration();

#else

static inline bool IsUsingRAIntegration()
{
return false;
}

#endif

// Returns true if the user is logged in properly, and everything is set up for playing games with achievements.
bool IsLoggedIn();

Expand Down Expand Up @@ -80,6 +66,9 @@ bool WarnUserIfHardcoreModeActive(bool isSaveStateAction, std::string_view messa
// Returns the length of the string. If (size_t)-1, there's no message.
size_t GetRichPresenceMessage(char *buffer, size_t bufSize);

// Returns true if the user has unsaved RAIntegration changes. Should prompt the user to be sure they want to exit.
bool RAIntegrationDirty();

// The new API is so much nicer that we can use it directly instead of wrapping it. So let's expose the client.
// Will of course return nullptr if not active.
rc_client_t *GetClient();
Expand Down
2 changes: 1 addition & 1 deletion Qt/QtMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
}
}

int System_GetPropertyInt(SystemProperty prop) {
int64_t System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
#if defined(SDL)
case SYSPROP_AUDIO_SAMPLE_RATE:
Expand Down
2 changes: 1 addition & 1 deletion SDL/SDLMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
}
}

int System_GetPropertyInt(SystemProperty prop) {
int64_t System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
case SYSPROP_AUDIO_SAMPLE_RATE:
return g_retFmt.freq;
Expand Down
4 changes: 3 additions & 1 deletion UI/MiscScreens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,9 @@ UI::EventReturn PromptScreen::OnNo(UI::EventParams &e) {
}

void PromptScreen::TriggerFinish(DialogResult result) {
callback_(result == DR_OK || result == DR_YES);
if (callback_) {
callback_(result == DR_OK || result == DR_YES);
}
UIDialogScreenWithBackground::TriggerFinish(result);
}

Expand Down
24 changes: 20 additions & 4 deletions UI/PauseScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ void GamePauseScreen::update() {
UIScreen::update();

if (finishNextFrame_) {
TriggerFinish(DR_CANCEL);
TriggerFinish(finishNextFrameResult_);
finishNextFrame_ = false;
}

Expand Down Expand Up @@ -494,10 +494,26 @@ UI::EventReturn GamePauseScreen::OnScreenshotClicked(UI::EventParams &e) {
}

UI::EventReturn GamePauseScreen::OnExitToMenu(UI::EventParams &e) {
if (g_Config.bPauseMenuExitsEmulator) {
System_ExitApp();
// If RAIntegration has dirty info, ask for confirmation.
if (Achievements::RAIntegrationDirty()) {
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
auto di = GetI18NCategory(I18NCat::DIALOG);
screenManager()->push(new PromptScreen(gamePath_, ac->T("You have unsaved RAIntegration changes. Exit?"), di->T("Yes"), di->T("No"), [=](bool result) {
if (result) {
if (g_Config.bPauseMenuExitsEmulator) {
System_ExitApp();
} else {
finishNextFrameResult_ = DR_OK; // exit game
finishNextFrame_ = true;
}
}
}));
} else {
TriggerFinish(DR_OK);
if (g_Config.bPauseMenuExitsEmulator) {
System_ExitApp();
} else {
TriggerFinish(DR_OK);
}
}
return UI::EVENT_DONE;
}
Expand Down
Loading

0 comments on commit c9515ce

Please sign in to comment.