Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial RAIntegration support through rc_client #19002

Merged
merged 3 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading