Cheevos: Add RAIntergration support

This commit is contained in:
Connor McLaughlin 2022-04-18 15:09:21 +10:00
parent 8b61fb8b58
commit 9a5ef2d0a2
17 changed files with 1997 additions and 22 deletions

1
dep/rainterface/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.o

21
dep/rainterface/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 RetroAchievements.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,83 @@
#ifndef RA_CONSOLES_H
#define RA_CONSOLES_H
/* this list should match the list in rcheevos/include/rconsoles.h */
enum ConsoleID
{
UnknownConsoleID = 0,
MegaDrive = 1,
N64 = 2,
SNES = 3,
GB = 4,
GBA = 5,
GBC = 6,
NES = 7,
PCEngine = 8,
SegaCD = 9,
Sega32X = 10,
MasterSystem = 11,
PlayStation = 12,
Lynx = 13,
NeoGeoPocket = 14,
GameGear = 15,
GameCube = 16,
Jaguar = 17,
DS = 18,
WII = 19,
WIIU = 20,
PlayStation2 = 21,
Xbox = 22,
MagnavoxOdyssey = 23,
PokemonMini = 24,
Atari2600 = 25,
MSDOS = 26,
Arcade = 27,
VirtualBoy = 28,
MSX = 29,
C64 = 30,
ZX81 = 31,
Oric = 32,
SG1000 = 33,
VIC20 = 34,
Amiga = 35,
AtariST = 36,
AmstradCPC = 37,
AppleII = 38,
Saturn = 39,
Dreamcast = 40,
PSP = 41,
CDi = 42,
ThreeDO = 43,
Colecovision = 44,
Intellivision = 45,
Vectrex = 46,
PC8800 = 47,
PC9800 = 48,
PCFX = 49,
Atari5200 = 50,
Atari7800 = 51,
X68K = 52,
WonderSwan = 53,
CassetteVision = 54,
SuperCassetteVision = 55,
NeoGeoCD = 56,
FairchildChannelF = 57,
FMTowns = 58,
ZXSpectrum = 59,
GameAndWatch = 60,
NokiaNGage = 61,
Nintendo3DS = 62,
Supervision = 63,
SharpX1 = 64,
Tic80 = 65,
ThomsonTO8 = 66,
PC6000 = 67,
Pico = 68,
MegaDuck = 69,
Zeebo = 70,
Arduboy = 71,
NumConsoleIDs
};
#endif /* !RA_CONSOLES_H */

View File

@ -0,0 +1,23 @@
#ifndef RA_EMULATORS_H
#define RA_EMULATORS_H
enum EmulatorID
{
RA_Gens = 0,
RA_Project64 = 1,
RA_Snes9x = 2,
RA_VisualboyAdvance = 3,
RA_Nester = 4,
RA_FCEUX = 5,
RA_PCE = 6,
RA_Libretro = 7,
RA_Meka = 8,
RA_QUASI88 = 9,
RA_AppleWin = 10,
RA_Oricutron = 11,
NumEmulatorIDs,
UnknownEmulator = NumEmulatorIDs
};
#endif /* !RA_EMULATORS_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,368 @@
#ifndef RA_INTERFACE_H
#define RA_INTERFACE_H
#include <wtypes.h> /* HWND */
#ifdef __cplusplus
extern "C" {
#endif
/******************************
* Initialization *
******************************/
/**
* Loads and initializes the DLL.
*
* Must be called before using any of the other functions. Will automatically download the DLL
* if not found, or prompt the user to upgrade if a newer version is available
*
* @param hMainHWND the handle of the main window
* @param nEmulatorID the unique idenfier of the emulator
* @param sClientVersion the current version of the emulator (will be validated against the minimum version for the specified emulator ID)
*/
extern void RA_Init(HWND hMainHWND, int nEmulatorID, const char* sClientVersion);
/**
* Loads and initializes the DLL.
*
* Must be called before using any of the other functions. Will automatically download the DLL
* if not found, or prompt the user to upgrade if a newer version is available
*
* @param hMainHWND the handle of the main window
* @param sClientName the name of the client, displayed in the title bar and included in the User-Agent for API calls
* @param sClientVersion the current version of the client
*/
extern void RA_InitClient(HWND hMainHWND, const char* sClientName, const char* sClientVersion);
/**
* Defines callbacks that the DLL can use to interact with the client.
*
* @param fpUnusedIsActive [no longer used] returns non-zero if a game is running
* @param fpCauseUnpause unpauses the emulator
* @param fpCausePause pauses the emulator
* @param fpRebuildMenu notifies the client that the popup menu has changed (@see RA_CreatePopupMenu)
* @param fpEstimateTitle gets a short description for the game being loaded (parameter is a 256-byte buffer to fill)
* @param fpResetEmulator resets the emulator
* @param fpLoadROM [currently unused] tells the emulator to load a specific game
*/
extern void RA_InstallSharedFunctions(int (*fpUnusedIsActive)(void),
void (*fpCauseUnpause)(void), void (*fpCausePause)(void), void (*fpRebuildMenu)(void),
void (*fpEstimateTitle)(char*), void (*fpResetEmulator)(void), void (*fpLoadROM)(const char*));
/**
* Tells the DLL to use UpdateWindow instead of InvalidateRect when the UI needs to be repainted. This is primarily
* necessary when integrating with an emulator using the SDL library as it keeps the message queue flooded so the
* InvalidateRect messages never get turned into WM_PAINT messages.
*
* @param bEnable non-zero if InvalidateRect calls should be replaced with UpdateWindow
*/
extern void RA_SetForceRepaint(int bEnable);
/**
* Creates a popup menu that can be appended to the main menu of the emulator.
*
* @return handle to the menu. if not attached to the program menu, caller must destroy it themselves.
*/
extern HMENU RA_CreatePopupMenu(void);
/* Resource values for menu items - needed by MFC ON_COMMAND_RANGE macros or WM_COMMAND WndProc handlers
* they're not all currently used, allowing additional items without forcing recompilation of the emulators
*/
#define IDM_RA_MENUSTART 1700
#define IDM_RA_MENUEND 1739
/**
* Called when a menu item in the popup menu is selected.
*
* @param nID the ID of the menu item (will be between IDM_RA_MENUSTART and IDM_RA_MENUEND)
*/
extern void RA_InvokeDialog(LPARAM nID);
/**
* Provides additional information to include in the User-Agent string for API calls.
*
* This is primarily used to identify dependencies or configurations (such as libretro core versions)
*
* @param sDetail the additional information to include
*/
extern void RA_SetUserAgentDetail(const char* sDetail);
/**
* Attempts to log in to the retroachievements.org site.
*
* Prompts the user for their login credentials and performs the login. If they've previously logged in and have
* chosen to store their credentials, the login occurs without prompting.
*
* @param bBlocking if zero, control is returned to the calling process while the login request is processed by the server.
*/
extern void RA_AttemptLogin(int bBlocking);
/**
* Specifies the console associated to the emulator.
*
* May be called just before loading a game if the emulator supports multiple consoles.
*
* @param nConsoleID the unique identifier of the console associated to the game being loaded.
*/
extern void RA_SetConsoleID(unsigned int nConsoleID);
/**
* Resets memory references created by previous calls to RA_InstallMemoryBank.
*/
extern void RA_ClearMemoryBanks(void);
typedef unsigned char (RA_ReadMemoryFunc)(unsigned int nAddress);
typedef void (RA_WriteMemoryFunc)(unsigned int nAddress, unsigned char nValue);
/**
* Exposes a block of memory to the DLL.
*
* The blocks of memory expected by the DLL are unique per console ID. To identify the correct map for a console,
* view the consoleinfo.c file in the rcheevos repository.
*
* @param nBankID the index of the bank to update. will replace any existing bank at that index.
* @param pReader a function to read from the bank. parameter is the offset within the bank to read from.
* @param pWriter a function to write to the bank. parameters are the offset within the bank to write to and an 8-bit value to write.
* @param nBankSize the size of the bank.
*/
extern void RA_InstallMemoryBank(int nBankID, RA_ReadMemoryFunc pReader, RA_WriteMemoryFunc pWriter, int nBankSize);
/**
* Deinitializes and unloads the DLL.
*/
extern void RA_Shutdown(void);
/******************************
* Overlay *
******************************/
/**
* Determines if the overlay is fully visible.
*
* Precursor check before calling RA_NavigateOverlay
*
* @return non-zero if the overlay is fully visible, zero if it is not.
*/
extern int RA_IsOverlayFullyVisible(void);
/**
* Called to show or hide the overlay.
*
* @param bIsPaused true to show the overlay, false to hide it.
*/
extern void RA_SetPaused(bool bIsPaused);
struct ControllerInput
{
int m_bUpPressed;
int m_bDownPressed;
int m_bLeftPressed;
int m_bRightPressed;
int m_bConfirmPressed; /* Usually C or A */
int m_bCancelPressed; /* Usually B */
int m_bQuitPressed; /* Usually Start */
};
/**
* Passes controller input to the overlay.
*
* Does nothing if the overlay is not fully visible.
*
* @param pInput pointer to a ControllerInput structure indicating which inputs are active.
*/
extern void RA_NavigateOverlay(struct ControllerInput* pInput);
/**
* [deprecated] Updates the overlay for a single frame.
*
* This function just calls RA_NavigateOverlay. Updating and rendering is now handled internally to the DLL.
*/
extern void RA_UpdateRenderOverlay(HDC, struct ControllerInput* pInput, float, RECT*, bool, bool);
/**
* Updates the handle to the main window.
*
* The main window handle is used to anchor the overlay. If the client recreates the handle as the result of switching
* from windowed mode to full screen, or for any other reason, it should call this to reattach the overlay.
*
* @param hMainHWND the new handle of the main window
*/
extern void RA_UpdateHWnd(HWND hMainHWND);
/******************************
* Game Management *
******************************/
/**
* Identifies the game associated to a block of memory.
*
* The block of memory is the fully buffered game file. If more complicated identification is required, the caller
* needs to link against rcheevos/rhash directly to generate the hash, and call RA_IdentifyHash with the result.
* Can be called when switching discs to ensure the additional discs are still associated to the loaded game.
*
* @param pROMData the contents of the game file
* @param nROMSize the size of the game file
* @return the unique identifier of the game, 0 if no association available.
*/
extern unsigned int RA_IdentifyRom(BYTE* pROMData, unsigned int nROMSize);
/**
* Identifies the game associated to a pre-generated hash.
*
* Used when the hash algorithm is something other than full file.
* Can be called when switching discs to ensure the additional discs are still associated to the loaded game.
*
* @param sHash the hash generated by rcheevos/rhash
* @return the unique identifier of the game, 0 if no association available.
*/
extern unsigned int RA_IdentifyHash(const char* sHash);
/**
* Fetches all retroachievements related data for the specified game.
*
* @param nGameId the unique identifier of the game to activate
*/
extern void RA_ActivateGame(unsigned int nGameId);
/**
* Identifies and activates the game associated to a block of memory.
*
* Functions as a call to RA_IdentifyRom followed by a call to RA_ActivateGame.
*
* @param pROMData the contents of the game file
* @param nROMSize the size of the game file
*/
extern void RA_OnLoadNewRom(BYTE* pROMData, unsigned int nROMSize);
/**
* Called before unloading the game to allow the user to save any changes they might have.
*
* @param bIsQuitting non-zero to change the messaging to indicate the emulator is closing.
* @return zero to abort the unload. non-zero to continue.
*/
extern int RA_ConfirmLoadNewRom(int bIsQuitting);
/******************************
* Runtime Functionality *
******************************/
/**
* Does all achievement-related processing for a single frame.
*/
extern void RA_DoAchievementsFrame(void);
/**
* Temporarily disables forced updating of tool windows.
*
* Primarily used while fast-forwarding.
*/
extern void RA_SuspendRepaint(void);
/**
* Resumes forced updating of tool windows.
*/
extern void RA_ResumeRepaint(void);
/**
* [deprecated] Used to be used to ensure the asynchronous server calls are processed on the UI thread.
* That's all managed within the DLL now. Calling this function does nothing.
*/
extern void RA_HandleHTTPResults(void);
/**
* Adds flavor text to the application title bar.
*
* Application title bar is managed by the DLL. Value will be "ClientName - Version - Flavor Text - Username"
*
* @param sCustomMessage the flavor text to include in the title bar.
*/
extern void RA_UpdateAppTitle(const char* sCustomMessage);
/**
* Get the user name of the currently logged in user.
*
* @return user name of the currently logged in user, empty if no user is logged in.
*/
const char* RA_UserName(void);
/**
* Determines if the user is currently playing with hardcore enabled.
*
* The client should disable any features that would give the player an unfair advantage if this returns non-zero.
* Things like loading states, using cheats, modifying RAM, disabling rendering layers, viewing decoded tilemaps, etc.
*
* @return non-zero if hardcore mode is currently active.
*/
extern int RA_HardcoreModeIsActive(void);
/**
* Warns the user they're about to do something that will disable hardcore mode.
*
* @param sActivity what the user is about to do (i.e. "load a state").
* @return non-zero if the user disabled hardcore and the activity is allowed.
* zero if the user declined to disable hardcore and the activity should be aborted.
*/
extern int RA_WarnDisableHardcore(const char* sActivity);
/**
* Disables hardcore mode without prompting or notifying the user.
*
* Should only be called if the client does its own prompting/notification.
* Typically used when an activity cannot be aborted.
*/
extern void RA_DisableHardcore(void);
/**
* Notifies the DLL that the game has been reset.
*
* Disables active leaderboards and resets hit counts on all active achievements.
*/
extern void RA_OnReset(void);
/**
* Notifies the DLL that a save state has been created.
*
* Creates a .rap file next to the state file that contains achievement-related information for the save state.
*
* @param sFilename full path to the save state file.
*/
extern void RA_OnSaveState(const char* sFilename);
/**
* Notifies the DLL that a save state has been loaded.
*
* Loads the .rap file next to the state file that contains achievement-related information for the save state being loaded.
*
* @param sFilename full path to the save state file.
*/
extern void RA_OnLoadState(const char* sFilename);
/**
* Captures the current state of the achievement runtime for inclusion in a save state.
*
* @param pBuffer buffer to write achievement state information to
* @param nBufferSize the size of the buffer
* @return the number of bytes needed to capture the achievement state. if less than nBufferSize, pBuffer
* will not be populated. the function should be called again with a larger buffer.
*/
extern int RA_CaptureState(char* pBuffer, int nBufferSize);
/**
* Restores the state of the achievement runtime from a previously captured state.
*
* @param pBuffer buffer containing previously serialized achievement state information
*/
extern void RA_RestoreState(const char* pBuffer);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif // !RA_INTERFACE_H

View File

@ -0,0 +1,5 @@
# RAInterface
This code is intended to be loaded into another repository as a submodule.
An emulator should include RA_Interface.cpp in its build and link against winhttp.lib. Then, the emulator can be modified to call the hooks provided in RA_Interface.cpp at appropriate times to integrate with the RetroAchievements server via the RA_Integration.dll. See wiki for more details.

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\msvc\vsprops\Configurations.props" />
<PropertyGroup Label="Globals">
<ProjectGuid>{E4357877-D459-45C7-B8F6-DCBB587BB528}</ProjectGuid>
</PropertyGroup>
<ItemGroup>
<ClInclude Include="RA_Consoles.h" />
<ClInclude Include="RA_Emulators.h" />
<ClInclude Include="RA_Interface.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="RA_Interface.cpp" />
</ItemGroup>
<Import Project="..\msvc\vsprops\StaticLibrary.props" />
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="..\msvc\vsprops\Targets.props" />
</Project>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClInclude Include="RA_Emulators.h" />
<ClInclude Include="RA_Interface.h" />
<ClInclude Include="RA_Consoles.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="RA_Interface.cpp" />
</ItemGroup>
</Project>

View File

@ -26,10 +26,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "src\core\core.vcxpr
{EE054E08-3799-4A59-A422-18259C105FFD} = {EE054E08-3799-4A59-A422-18259C105FFD}
{BB08260F-6FBC-46AF-8924-090EE71360C6} = {BB08260F-6FBC-46AF-8924-090EE71360C6}
{8906836E-F06E-46E8-B11A-74E5E8C7B8FB} = {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}
{E4357877-D459-45C7-B8F6-DCBB587BB528} = {E4357877-D459-45C7-B8F6-DCBB587BB528}
{ED601289-AC1A-46B8-A8ED-17DB9EB73423} = {ED601289-AC1A-46B8-A8ED-17DB9EB73423}
{09553C96-9F39-49BF-8AE6-7ACBD07C410C} = {09553C96-9F39-49BF-8AE6-7ACBD07C410C}
{9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035} = {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}
{7FF9FDB9-D504-47DB-A16A-B08071999620} = {7FF9FDB9-D504-47DB-A16A-B08071999620}
{4BA0A6D4-3AE1-42B2-9347-096FD023FF64} = {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stb", "dep\stb\stb.vcxproj", "{ED601289-AC1A-46B8-A8ED-17DB9EB73423}"
@ -68,7 +70,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "frontend-common", "src\fron
{933118A9-68C5-47B4-B151-B03C93961623} = {933118A9-68C5-47B4-B151-B03C93961623}
{868B98C8-65A1-494B-8346-250A73A48C0A} = {868B98C8-65A1-494B-8346-250A73A48C0A}
{3773F4CC-614E-4028-8595-22E08CA649E3} = {3773F4CC-614E-4028-8595-22E08CA649E3}
{4BA0A6D4-3AE1-42B2-9347-096FD023FF64} = {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xxhash", "dep\xxhash\xxhash.vcxproj", "{09553C96-9F39-49BF-8AE6-7ACBD07C410C}"
@ -100,6 +101,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rcheevos", "dep\rcheevos\rc
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duckstation-regtest", "src\duckstation-regtest\duckstation-regtest.vcxproj", "{3029310E-4211-4C87-801A-72E130A648EF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rainterface", "dep\rainterface\rainterface.vcxproj", "{E4357877-D459-45C7-B8F6-DCBB587BB528}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@ -912,6 +915,42 @@ Global
{3029310E-4211-4C87-801A-72E130A648EF}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64
{3029310E-4211-4C87-801A-72E130A648EF}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64
{3029310E-4211-4C87-801A-72E130A648EF}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Debug|ARM64.Build.0 = Debug|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Debug|x64.ActiveCfg = Debug|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Debug|x64.Build.0 = Debug|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Debug|x86.ActiveCfg = Debug|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Debug|x86.Build.0 = Debug|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugFast|ARM64.ActiveCfg = DebugFast|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugFast|ARM64.Build.0 = DebugFast|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugFast|x64.ActiveCfg = DebugFast|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugFast|x64.Build.0 = DebugFast|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugFast|x86.ActiveCfg = DebugFast|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugFast|x86.Build.0 = DebugFast|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugUWP|x64.ActiveCfg = DebugUWP|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugUWP|x64.Build.0 = DebugUWP|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.DebugUWP|x86.Build.0 = DebugUWP|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Release|ARM64.ActiveCfg = Release|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Release|ARM64.Build.0 = Release|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Release|x64.ActiveCfg = Release|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Release|x64.Build.0 = Release|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Release|x86.ActiveCfg = Release|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.Release|x86.Build.0 = Release|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseLTCG|ARM64.ActiveCfg = ReleaseLTCG|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseLTCG|ARM64.Build.0 = ReleaseLTCG|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseLTCG|x64.Build.0 = ReleaseLTCG|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32
{E4357877-D459-45C7-B8F6-DCBB587BB528}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -935,6 +974,7 @@ Global
{8906836E-F06E-46E8-B11A-74E5E8C7B8FB} = {BA490C0E-497D-4634-A21E-E65012006385}
{39F0ADFF-3A84-470D-9CF0-CA49E164F2F3} = {BA490C0E-497D-4634-A21E-E65012006385}
{4BA0A6D4-3AE1-42B2-9347-096FD023FF64} = {BA490C0E-497D-4634-A21E-E65012006385}
{E4357877-D459-45C7-B8F6-DCBB587BB528} = {BA490C0E-497D-4634-A21E-E65012006385}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {26E40B32-7C1D-48D0-95F4-1A500E054028}

View File

@ -22,11 +22,16 @@
#include "scmversion/scmversion.h"
#include <algorithm>
#include <cstdarg>
#include <cstdlib>
#include <functional>
#include <string>
#include <vector>
Log_SetChannel(Cheevos);
#ifdef WITH_RAINTEGRATION
#include "RA_Interface.h"
#endif
namespace Cheevos {
enum : s32
@ -60,6 +65,11 @@ static bool s_unofficial_test_mode = false;
static bool s_use_first_disc_from_playlist = true;
static bool s_rich_presence_enabled = false;
#ifdef WITH_RAINTEGRATION
bool g_using_raintegration = false;
bool g_raintegration_initialized = false;
#endif
static rc_runtime_t s_rcheevos_runtime;
static std::unique_ptr<FrontendCommon::HTTPDownloader> s_http_downloader;
@ -244,6 +254,10 @@ bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_r
g_active = true;
g_challenge_mode = challenge_mode;
#ifdef WITH_RAINTEGRATION
g_using_raintegration = false;
#endif
s_test_mode = test_mode;
s_unofficial_test_mode = include_unofficial;
s_use_first_disc_from_playlist = use_first_disc_from_playlist;
@ -266,6 +280,14 @@ void Reset()
if (!g_active)
return;
#ifdef WITH_RAINTEGRATION
if (IsUsingRAIntegration())
{
RA_OnReset();
return;
}
#endif
Log_DevPrint("Resetting rcheevos state...");
rc_runtime_reset(&s_rcheevos_runtime);
}
@ -298,6 +320,16 @@ void Update()
if (HasActiveGame())
{
#ifdef WITH_RAINTEGRATION
if (IsUsingRAIntegration())
{
if (g_raintegration_initialized)
RA_DoAchievementsFrame();
return;
}
#endif
rc_runtime_do_frame(&s_rcheevos_runtime, &CheevosEventHandler, &CheevosPeek, nullptr, nullptr);
UpdateRichPresence();
@ -332,7 +364,20 @@ bool DoState(StateWrapper& sw)
{
// reset runtime, no data (state might've been created without cheevos)
Log_DevPrintf("State is missing cheevos data, resetting runtime");
#ifdef WITH_RAINTEGRATION
if (IsUsingRAIntegration())
{
if (g_raintegration_initialized)
RA_OnReset();
}
else
{
rc_runtime_reset(&s_rcheevos_runtime);
}
#else
rc_runtime_reset(&s_rcheevos_runtime);
#endif
return !sw.HasError();
}
@ -341,30 +386,64 @@ bool DoState(StateWrapper& sw)
if (sw.HasError())
return false;
const int result = rc_runtime_deserialize_progress(&s_rcheevos_runtime, data.get(), nullptr);
if (result != RC_OK)
#ifdef WITH_RAINTEGRATION
if (IsUsingRAIntegration() && g_raintegration_initialized)
{
Log_WarningPrintf("Failed to deserialize cheevos state (%d), resetting", result);
rc_runtime_reset(&s_rcheevos_runtime);
RA_RestoreState(reinterpret_cast<const char*>(data.get()));
}
else
{
const int result = rc_runtime_deserialize_progress(&s_rcheevos_runtime, data.get(), nullptr);
if (result != RC_OK)
{
Log_WarningPrintf("Failed to deserialize cheevos state (%d), resetting", result);
rc_runtime_reset(&s_rcheevos_runtime);
}
}
#endif
return true;
}
else
{
// internally this happens twice.. not great.
const int size = rc_runtime_progress_size(&s_rcheevos_runtime, nullptr);
u32 data_size;
std::unique_ptr<u8[]> data;
u32 data_size = (size >= 0) ? static_cast<u32>(size) : 0;
std::unique_ptr<u8[]> data(new u8[data_size]);
const int result = rc_runtime_serialize_progress(data.get(), &s_rcheevos_runtime, nullptr);
if (result != RC_OK)
#ifdef WITH_RAINTEGRATION
if (IsUsingRAIntegration())
{
// set data to zero, effectively serializing nothing
Log_WarningPrintf("Failed to serialize cheevos state (%d)", result);
data_size = 0;
if (g_raintegration_initialized)
{
const int size = RA_CaptureState(nullptr, 0);
data_size = (size >= 0) ? static_cast<u32>(size) : 0;
data = std::unique_ptr<u8[]>(new u8[data_size]);
const int result = RA_CaptureState(reinterpret_cast<char*>(data.get()), static_cast<int>(data_size));
if (result != static_cast<int>(data_size))
{
Log_WarningPrint("Failed to serialize cheevos state from RAIntegration.");
data_size = 0;
}
}
}
else
{
// internally this happens twice.. not great.
const int size = rc_runtime_progress_size(&s_rcheevos_runtime, nullptr);
data_size = (size >= 0) ? static_cast<u32>(size) : 0;
data = std::unique_ptr<u8[]>(new u8[data_size]);
const int result = rc_runtime_serialize_progress(data.get(), &s_rcheevos_runtime, nullptr);
if (result != RC_OK)
{
// set data to zero, effectively serializing nothing
Log_WarningPrintf("Failed to serialize cheevos state (%d)", result);
data_size = 0;
}
}
#endif
sw.Do(&data_size);
if (data_size > 0)
@ -468,7 +547,7 @@ bool LoginAsync(const char* username, const char* password)
{
s_http_downloader->WaitForAllRequests();
if (s_logged_in || std::strlen(username) == 0 || std::strlen(password) == 0)
if (s_logged_in || std::strlen(username) == 0 || std::strlen(password) == 0 || IsUsingRAIntegration())
return false;
if (ImGuiFullscreen::IsInitialized())
@ -487,7 +566,7 @@ bool Login(const char* username, const char* password)
if (g_active)
s_http_downloader->WaitForAllRequests();
if (s_logged_in || std::strlen(username) == 0 || std::strlen(password) == 0)
if (s_logged_in || std::strlen(username) == 0 || std::strlen(password) == 0 || IsUsingRAIntegration())
return false;
if (g_active)
@ -1016,8 +1095,10 @@ static void GetGameIdCallback(s32 status_code, const FrontendCommon::HTTPDownloa
const u32 game_id = (doc.HasMember("GameID") && doc["GameID"].IsUint()) ? doc["GameID"].GetUint() : 0;
Log_InfoPrintf("Server returned GameID %u", game_id);
if (game_id != 0)
GetPatches(game_id);
if (game_id == 0)
return;
GetPatches(game_id);
}
void GameChanged()
@ -1089,6 +1170,14 @@ void GameChanged(const std::string& path, CDImage* image)
return;
}
#ifdef WITH_RAINTEGRATION
if (IsUsingRAIntegration())
{
RAIntegration::GameChanged();
return;
}
#endif
char url[256];
int res = rc_url_get_gameid(url, sizeof(url), s_game_hash.c_str());
Assert(res == 0);
@ -1492,4 +1581,184 @@ unsigned CheevosPeek(unsigned address, unsigned num_bytes, void* ud)
}
}
#ifdef WITH_RAINTEGRATION
#include "RA_Consoles.h"
static int RACallbackIsActive();
static void RACallbackCauseUnpause();
static void RACallbackCausePause();
static void RACallbackRebuildMenu();
static void RACallbackEstimateTitle(char* buf);
static void RACallbackResetEmulator();
static void RACallbackLoadROM(const char* unused);
static unsigned char RACallbackReadMemory(unsigned int address);
static void RACallbackWriteMemory(unsigned int address, unsigned char value);
void SwitchToRAIntegration()
{
g_using_raintegration = true;
g_raintegration_initialized = false;
g_active = true;
s_logged_in = true;
}
static void InitializeRAIntegration(void* main_window_handle)
{
RA_InitClient((HWND)main_window_handle, "DuckStation", g_scm_tag_str);
RA_SetUserAgentDetail(Cheevos::GetUserAgent().c_str());
RA_InstallSharedFunctions(RACallbackIsActive, RACallbackCauseUnpause, RACallbackCausePause, RACallbackRebuildMenu,
RACallbackEstimateTitle, RACallbackResetEmulator, RACallbackLoadROM);
RA_SetConsoleID(PlayStation);
// Apparently this has to be done early, or the memory inspector doesn't work.
// That's a bit unfortunate, because the RAM size can vary between games, and depending on the option.
RA_InstallMemoryBank(0, RACallbackReadMemory, RACallbackWriteMemory, Bus::RAM_2MB_SIZE);
// Fire off a login anyway. Saves going into the menu and doing it.
RA_AttemptLogin(0);
g_challenge_mode = RA_HardcoreModeIsActive() != 0;
g_raintegration_initialized = true;
// this is pretty lame, but we may as well persist until we exit anyway
std::atexit(RA_Shutdown);
}
void RAIntegration::MainWindowChanged(void* new_handle)
{
if (g_raintegration_initialized)
{
RA_UpdateHWnd((HWND)new_handle);
return;
}
InitializeRAIntegration(new_handle);
}
void RAIntegration::GameChanged()
{
g_game_id = RA_IdentifyHash(s_game_hash.c_str());
RA_ActivateGame(g_game_id);
}
std::vector<std::pair<int, const char*>> RAIntegration::GetMenuItems()
{
// NOTE: I *really* don't like doing this. But sadly it's the only way we can integrate with Qt.
static constexpr int IDM_RA_RETROACHIEVEMENTS = 1700;
static constexpr int IDM_RA_OVERLAYSETTINGS = 1701;
static constexpr int IDM_RA_FILES_MEMORYBOOKMARKS = 1703;
static constexpr int IDM_RA_FILES_ACHIEVEMENTS = 1704;
static constexpr int IDM_RA_FILES_MEMORYFINDER = 1705;
static constexpr int IDM_RA_FILES_LOGIN = 1706;
static constexpr int IDM_RA_FILES_LOGOUT = 1707;
static constexpr int IDM_RA_FILES_ACHIEVEMENTEDITOR = 1708;
static constexpr int IDM_RA_HARDCORE_MODE = 1710;
static constexpr int IDM_RA_REPORTBROKENACHIEVEMENTS = 1711;
static constexpr int IDM_RA_GETROMCHECKSUM = 1712;
static constexpr int IDM_RA_OPENUSERPAGE = 1713;
static constexpr int IDM_RA_OPENGAMEPAGE = 1714;
static constexpr int IDM_RA_PARSERICHPRESENCE = 1716;
static constexpr int IDM_RA_TOGGLELEADERBOARDS = 1717;
static constexpr int IDM_RA_NON_HARDCORE_WARNING = 1718;
std::vector<std::pair<int, const char*>> ret;
const char* username = RA_UserName();
if (!username || std::strlen(username) == 0)
{
ret.emplace_back(IDM_RA_FILES_LOGIN, "&Login");
}
else
{
ret.emplace_back(IDM_RA_FILES_LOGOUT, "Log&out");
ret.emplace_back(0, nullptr);
ret.emplace_back(IDM_RA_OPENUSERPAGE, "Open my &User Page");
ret.emplace_back(IDM_RA_OPENGAMEPAGE, "Open this &Game's Page");
ret.emplace_back(0, nullptr);
ret.emplace_back(IDM_RA_HARDCORE_MODE, "&Hardcore Mode");
ret.emplace_back(IDM_RA_NON_HARDCORE_WARNING, "Non-Hardcore &Warning");
ret.emplace_back(0, nullptr);
ret.emplace_back(IDM_RA_TOGGLELEADERBOARDS, "Enable &Leaderboards");
ret.emplace_back(IDM_RA_OVERLAYSETTINGS, "O&verlay Settings");
ret.emplace_back(0, nullptr);
ret.emplace_back(IDM_RA_FILES_ACHIEVEMENTS, "Assets Li&st");
ret.emplace_back(IDM_RA_FILES_ACHIEVEMENTEDITOR, "Assets &Editor");
ret.emplace_back(IDM_RA_FILES_MEMORYFINDER, "&Memory Inspector");
ret.emplace_back(IDM_RA_FILES_MEMORYBOOKMARKS, "Memory &Bookmarks");
ret.emplace_back(IDM_RA_PARSERICHPRESENCE, "Rich &Presence Monitor");
ret.emplace_back(0, nullptr);
ret.emplace_back(IDM_RA_REPORTBROKENACHIEVEMENTS, "&Report Achievement Problem");
ret.emplace_back(IDM_RA_GETROMCHECKSUM, "View Game H&ash");
}
return ret;
}
void RAIntegration::ActivateMenuItem(int item)
{
RA_InvokeDialog(item);
}
int RACallbackIsActive()
{
return static_cast<int>(HasActiveGame());
}
void RACallbackCauseUnpause()
{
if (System::IsValid())
g_host_interface->PauseSystem(false);
}
void RACallbackCausePause()
{
if (System::IsValid())
g_host_interface->PauseSystem(true);
}
void RACallbackRebuildMenu()
{
// unused, we build the menu on demand
}
void RACallbackEstimateTitle(char* buf)
{
StringUtil::Strlcpy(buf, System::GetRunningTitle(), 256);
}
void RACallbackResetEmulator()
{
g_challenge_mode = RA_HardcoreModeIsActive() != 0;
if (System::IsValid())
g_host_interface->ResetSystem();
}
void RACallbackLoadROM(const char* unused)
{
// unused
UNREFERENCED_PARAMETER(unused);
}
unsigned char RACallbackReadMemory(unsigned int address)
{
if (!System::IsValid())
return 0;
u8 value = 0;
CPU::SafeReadMemoryByte(address, &value);
return value;
}
void RACallbackWriteMemory(unsigned int address, unsigned char value)
{
if (!System::IsValid())
return;
CPU::SafeWriteMemoryByte(address, value);
}
#endif
} // namespace Cheevos

View File

@ -4,6 +4,8 @@
#include <functional>
#include <optional>
#include <string>
#include <utility>
#include <vector>
class CDImage;
class StateWrapper;
@ -51,6 +53,25 @@ extern bool g_active;
extern bool g_challenge_mode;
extern u32 g_game_id;
// RAIntegration only exists for Windows, so no point checking it on other platforms.
#ifdef WITH_RAINTEGRATION
extern bool g_using_raintegration;
static ALWAYS_INLINE bool IsUsingRAIntegration()
{
return g_using_raintegration;
}
#else
static ALWAYS_INLINE bool IsUsingRAIntegration()
{
return false;
}
#endif
ALWAYS_INLINE bool IsActive()
{
return g_active;
@ -123,4 +144,15 @@ TinyString GetAchievementProgressText(const Achievement& achievement);
void UnlockAchievement(u32 achievement_id, bool add_notification = true);
void SubmitLeaderboard(u32 leaderboard_id, int value);
#ifdef WITH_RAINTEGRATION
void SwitchToRAIntegration();
namespace RAIntegration {
void MainWindowChanged(void* new_handle);
void GameChanged();
std::vector<std::pair<int, const char*>> GetMenuItems();
void ActivateMenuItem(int item);
} // namespace RAIntegration
#endif
} // namespace Cheevos

View File

@ -5,10 +5,12 @@
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>WITH_CHEEVOS=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='x64' Or '$(Platform)'=='ARM' Or '$(Platform)'=='ARM64'">WITH_RECOMPILER=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="('$(BuildingForUWP)'!='true' And '$(Platform)'!='ARM64')">WITH_RAINTEGRATION=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM' Or '$(Platform)'=='ARM64')">WITH_RECOMPILER=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM64') And ('$(BuildingForUWP)'!='true')">WITH_MMAP_FASTMEM=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\rcheevos\include;$(SolutionDir)dep\rapidjson\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="('$(BuildingForUWP)'!='true' And '$(Platform)'!='ARM64')">$(SolutionDir)dep\rainterface;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Platform)'=='x64'">$(SolutionDir)dep\xbyak\xbyak;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Platform)'=='ARM' Or '$(Platform)'=='ARM64'">$(SolutionDir)dep\vixl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@ -18,6 +20,7 @@
<ItemDefinitionGroup>
<Lib>
<AdditionalDependencies>$(RootBuildDir)rcheevos\rcheevos.lib;$(RootBuildDir)imgui\imgui.lib;$(RootBuildDir)stb\stb.lib;$(RootBuildDir)vulkan-loader\vulkan-loader.lib;$(RootBuildDir)xxhash\xxhash.lib;$(RootBuildDir)zlib\zlib.lib;$(RootBuildDir)common\common.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="('$(BuildingForUWP)'!='true' And '$(Platform)'!='ARM64')">$(RootBuildDir)rainterface\rainterface.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Platform)'=='ARM64'">$(RootBuildDir)vixl\vixl.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>

View File

@ -20,6 +20,11 @@
#include "scmversion/scmversion.h"
#include "settingsdialog.h"
#include "settingwidgetbinder.h"
#ifdef WITH_CHEEVOS
#include "core/cheevos.h"
#endif
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
@ -101,6 +106,11 @@ void MainWindow::initializeAndShow()
switchToGameListView();
show();
#ifdef WITH_RAINTEGRATION
if (Cheevos::IsUsingRAIntegration())
Cheevos::RAIntegration::MainWindowChanged((void*)winId());
#endif
}
void MainWindow::reportError(const QString& message)
@ -991,6 +1001,33 @@ void MainWindow::setupAdditionalUi()
connect(action, &QAction::triggered,
[scale]() { QtHostInterface::GetInstance()->requestRenderWindowScale(scale); });
}
#ifdef WITH_RAINTEGRATION
if (Cheevos::IsUsingRAIntegration())
{
QMenu* raMenu = new QMenu(QStringLiteral("RAIntegration"), m_ui.menuDebug);
connect(raMenu, &QMenu::aboutToShow, this, [this, raMenu]() {
raMenu->clear();
const auto items = Cheevos::RAIntegration::GetMenuItems();
for (const auto& [id, title] : items)
{
if (id == 0)
{
raMenu->addSeparator();
continue;
}
QAction* raAction = raMenu->addAction(QString::fromUtf8(title));
connect(raAction, &QAction::triggered, this, [id]() {
QtHostInterface::GetInstance()->executeOnEmulationThread(
[id]() { Cheevos::RAIntegration::ActivateMenuItem(id); });
});
}
});
m_ui.menuDebug->insertMenu(m_ui.menuCPUExecutionMode->menuAction(), raMenu);
}
#endif
}
void MainWindow::updateEmulationActions(bool starting, bool running, bool cheevos_challenge_mode)

View File

@ -18,6 +18,7 @@
#ifdef WITH_CHEEVOS
#include "achievementsettingswidget.h"
#include "core/cheevos.h"
#endif
static constexpr char DEFAULT_SETTING_HELP_TEXT[] = "";
@ -45,7 +46,8 @@ SettingsDialog::SettingsDialog(QtHostInterface* host_interface, QWidget* parent
m_advanced_settings = new AdvancedSettingsWidget(host_interface, m_ui.settingsContainer, this);
#ifdef WITH_CHEEVOS
m_achievement_settings = new AchievementSettingsWidget(host_interface, m_ui.settingsContainer, this);
if (!Cheevos::IsUsingRAIntegration())
m_achievement_settings = new AchievementSettingsWidget(host_interface, m_ui.settingsContainer, this);
#endif
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::GeneralSettings), m_general_settings);
@ -62,7 +64,18 @@ SettingsDialog::SettingsDialog(QtHostInterface* host_interface, QWidget* parent
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::AudioSettings), m_audio_settings);
#ifdef WITH_CHEEVOS
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::AchievementSettings), m_achievement_settings);
if (Cheevos::IsUsingRAIntegration())
{
QLabel* placeholder_label =
new QLabel(QStringLiteral("RAIntegration is being used, built-in RetroAchievements support is disabled."),
m_ui.settingsContainer);
placeholder_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::AchievementSettings), placeholder_label);
}
else
{
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::AchievementSettings), m_achievement_settings);
}
#else
QLabel* placeholder_label =
new QLabel(tr("This DuckStation build was not compiled with RetroAchievements support."), m_ui.settingsContainer);

View File

@ -113,6 +113,11 @@ bool CommonHostInterface::Initialize()
CreateImGuiContext();
#ifdef WITH_CHEEVOS
#ifdef WITH_RAINTEGRATION
if (GetBoolSettingValue("Cheevos", "UseRAIntegration", false))
Cheevos::SwitchToRAIntegration();
#endif
UpdateCheevosActive();
#endif
@ -3287,6 +3292,10 @@ void CommonHostInterface::SetDefaultSettings(SettingsInterface& si)
si.SetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", true);
si.DeleteValue("Cheevos", "Username");
si.DeleteValue("Cheevos", "Token");
#ifdef WITH_RAINTEGRATION
si.SetBoolValue("Cheevos", "UseRAIntegration", false);
#endif
#endif
}
@ -4377,6 +4386,11 @@ void CommonHostInterface::UpdateCheevosActive()
const bool cheevos_rich_presence = GetBoolSettingValue("Cheevos", "RichPresence", true);
const bool cheevos_hardcore = GetBoolSettingValue("Cheevos", "ChallengeMode", false);
#ifdef WITH_RAINTEGRATION
if (Cheevos::IsUsingRAIntegration())
return;
#endif
if (cheevos_enabled != Cheevos::IsActive() || cheevos_test_mode != Cheevos::IsTestModeActive() ||
cheevos_unofficial_test_mode != Cheevos::IsUnofficialTestModeActive() ||
cheevos_use_first_disc_from_playlist != Cheevos::IsUsingFirstDiscFromPlaylist() ||

View File

@ -2328,6 +2328,17 @@ void DrawSettingsWindow()
case SettingsPage::AchievementsSetings:
{
#ifdef WITH_RAINTEGRATION
if (Cheevos::IsUsingRAIntegration())
{
BeginMenuButtons();
ActiveButton(ICON_FA_BAN " RAIntegration is being used instead of the built-in cheevos implementation.",
false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
EndMenuButtons();
break;
}
#endif
#ifdef WITH_CHEEVOS
BeginMenuButtons();