diff --git a/.gitmodules b/.gitmodules
index 18c5cc79a..ff3fb68e6 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -106,3 +106,6 @@
[submodule "externals/libretro-common"]
path = externals/libretro-common/libretro-common
url = https://github.com/libretro/libretro-common.git
+[submodule "externals/rcheevos/rcheevos"]
+ path = externals/rcheevos/rcheevos
+ url = https://github.com/RetroAchievements/rcheevos.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 54eb90e90..7712727a2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -136,6 +136,8 @@ option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON)
option(ENABLE_DEVELOPER_OPTIONS "Enable functionality targeted at emulator developers" OFF)
+option(ENABLE_RETROACHIEVEMENTS "Build with RetroAchievements support" OFF)
+
# Compile options
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO})
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 2ee7018a3..07d747c1e 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -519,4 +519,9 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|ARM64|armv8")
else()
target_compile_definitions(xxhash PRIVATE XXH_VECTOR=XXH_SCALAR)
message(STATUS "Disabling SIMD for xxHash")
-endif()
\ No newline at end of file
+endif()
+
+# rcheevos
+if (ENABLE_RETROACHIEVEMENTS)
+ add_subdirectory(rcheevos)
+endif()
diff --git a/externals/rcheevos/CMakeLists.txt b/externals/rcheevos/CMakeLists.txt
new file mode 100644
index 000000000..53a36194c
--- /dev/null
+++ b/externals/rcheevos/CMakeLists.txt
@@ -0,0 +1,30 @@
+add_library(rcheevos STATIC
+ rcheevos/src/rc_compat.c
+ rcheevos/src/rc_client.c
+ rcheevos/src/rc_util.c
+ rcheevos/src/rc_version.c
+ rcheevos/src/rcheevos/alloc.c
+ rcheevos/src/rcheevos/condition.c
+ rcheevos/src/rcheevos/condset.c
+ rcheevos/src/rcheevos/consoleinfo.c
+ rcheevos/src/rcheevos/format.c
+ rcheevos/src/rcheevos/lboard.c
+ rcheevos/src/rcheevos/memref.c
+ rcheevos/src/rcheevos/operand.c
+ rcheevos/src/rcheevos/rc_validate.c
+ rcheevos/src/rcheevos/richpresence.c
+ rcheevos/src/rcheevos/runtime.c
+ rcheevos/src/rcheevos/runtime_progress.c
+ rcheevos/src/rcheevos/trigger.c
+ rcheevos/src/rcheevos/value.c
+ rcheevos/src/rhash/md5.c
+ rcheevos/src/rapi/rc_api_common.c
+ rcheevos/src/rapi/rc_api_editor.c
+ rcheevos/src/rapi/rc_api_info.c
+ rcheevos/src/rapi/rc_api_runtime.c
+ rcheevos/src/rapi/rc_api_user.c
+)
+
+target_include_directories(rcheevos PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/rcheevos/include
+)
diff --git a/externals/rcheevos/rcheevos b/externals/rcheevos/rcheevos
new file mode 160000
index 000000000..e9ca3694c
--- /dev/null
+++ b/externals/rcheevos/rcheevos
@@ -0,0 +1 @@
+Subproject commit e9ca3694c862b61235595176dac4b22677848c93
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f0da8bbe3..9ef6252a3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -184,6 +184,11 @@ if(ENABLE_DEVELOPER_OPTIONS)
add_compile_definitions(ENABLE_DEVELOPER_OPTIONS)
endif()
+if (ENABLE_RETROACHIEVEMENTS)
+ add_compile_definitions(ENABLE_RETROACHIEVEMENTS)
+ add_subdirectory(retroachievements)
+endif()
+
add_subdirectory(common)
add_subdirectory(core)
add_subdirectory(video_core)
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 20971828a..57ba3efca 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -334,3 +334,7 @@ endif()
if (CITRA_USE_PRECOMPILED_HEADERS)
target_precompile_headers(citra_qt PRIVATE precompiled_headers.h)
endif()
+
+if (ENABLE_RETROACHIEVEMENTS)
+ target_link_libraries(citra_qt PRIVATE retroachievements)
+endif()
diff --git a/src/citra_qt/configuration/configure_general.cpp b/src/citra_qt/configuration/configure_general.cpp
index ca548b21b..e770ea233 100644
--- a/src/citra_qt/configuration/configure_general.cpp
+++ b/src/citra_qt/configuration/configure_general.cpp
@@ -11,6 +11,10 @@
#include "citra_qt/uisettings.h"
#include "common/file_util.h"
#include "common/settings.h"
+#include "core/core.h"
+#ifdef ENABLE_RETROACHIEVEMENTS
+#include "retroachievements/client.h"
+#endif
#include "ui_configure_general.h"
// The QSlider doesn't have an easy way to set a custom step amount,
@@ -47,6 +51,10 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
ui->updates_group->setVisible(false);
#endif
+#ifndef ENABLE_RETROACHIEVEMENTS
+ ui->retroachievements_group->setVisible(false);
+#endif
+
SetupPerGameUI();
SetConfiguration();
@@ -74,6 +82,9 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
}
ui->change_screenshot_dir->setEnabled(true);
});
+
+ connect(ui->retroachievements_log_in_button, &QPushButton::clicked, this,
+ &ConfigureGeneral::RetroAchievementsLogIn);
}
ConfigureGeneral::~ConfigureGeneral() = default;
@@ -196,6 +207,16 @@ void ConfigureGeneral::RetranslateUI() {
ui->retranslateUi(this);
}
+void ConfigureGeneral::RetroAchievementsLogIn() {
+#ifdef ENABLE_RETROACHIEVEMENTS
+ std::string username = ui->retroachievements_username_input->text().toStdString(),
+ password = ui->retroachievements_password_input->text().toStdString();
+
+ Core::System::GetInstance().RetroAchievementsClient().LogInUser(username.c_str(),
+ password.c_str());
+#endif
+}
+
void ConfigureGeneral::SetupPerGameUI() {
if (Settings::IsConfiguringGlobal()) {
ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
diff --git a/src/citra_qt/configuration/configure_general.h b/src/citra_qt/configuration/configure_general.h
index 93a855387..6817506b4 100644
--- a/src/citra_qt/configuration/configure_general.h
+++ b/src/citra_qt/configuration/configure_general.h
@@ -24,6 +24,7 @@ public:
void ApplyConfiguration();
void RetranslateUI();
void SetConfiguration();
+ void RetroAchievementsLogIn();
void SetupPerGameUI();
diff --git a/src/citra_qt/configuration/configure_general.ui b/src/citra_qt/configuration/configure_general.ui
index 1c4a05631..9468d0c35 100644
--- a/src/citra_qt/configuration/configure_general.ui
+++ b/src/citra_qt/configuration/configure_general.ui
@@ -295,7 +295,7 @@
-
-
+
Save Screenshots To
@@ -317,6 +317,82 @@
+ -
+
+
+ RetroAchievements
+
+
+
-
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Username
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Password
+
+
+
+ -
+
+
+ QLineEdit::PasswordEchoOnEdit
+
+
+
+
+
+
+ -
+
+
+ Log In
+
+
+
+
+
+
-
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index fe905dfe7..c951a5b1c 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -137,7 +137,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
CLS(Movie) \
CLS(Loader) \
CLS(WebService) \
- CLS(RPC_Server)
+ CLS(RPC_Server) \
+ CLS(RetroAchievements)
// GetClassName is a macro defined by Windows.h, grrr...
const char* GetLogClassName(Class log_class) {
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 215bdbb3f..9a72cc5a4 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -52,60 +52,61 @@ enum class Class : u8 {
Applet_SWKBD, ///< The Software Keyboard applet
Service, ///< HLE implementation of system services. Each major service
///< should have its own subclass.
- Service_SRV, ///< The SRV (Service Directory) implementation
- Service_FRD, ///< The FRD (Friends) service
- Service_FS, ///< The FS (Filesystem) service implementation
- Service_ERR, ///< The ERR (Error) port implementation
- Service_ACT, ///< The ACT (Account) service
- Service_APT, ///< The APT (Applets) service
- Service_BOSS, ///< The BOSS (SpotPass) service
- Service_GSP, ///< The GSP (GPU control) service
- Service_AC, ///< The AC (WiFi status) service
- Service_AM, ///< The AM (Application manager) service
- Service_PTM, ///< The PTM (Power status & misc.) service
- Service_LDR, ///< The LDR (3ds dll loader) service
- Service_MIC, ///< The MIC (Microphone) service
- Service_NDM, ///< The NDM (Network daemon manager) service
- Service_NFC, ///< The NFC service
- Service_NIM, ///< The NIM (Network interface manager) service
- Service_NS, ///< The NS (Nintendo User Interface Shell) service
- Service_NWM, ///< The NWM (Network wlan manager) service
- Service_CAM, ///< The CAM (Camera) service
- Service_CECD, ///< The CECD (StreetPass) service
- Service_CFG, ///< The CFG (Configuration) service
- Service_CSND, ///< The CSND (CWAV format process) service
- Service_DSP, ///< The DSP (DSP control) service
- Service_DLP, ///< The DLP (Download Play) service
- Service_HID, ///< The HID (Human interface device) service
- Service_HTTP, ///< The HTTP service
- Service_SOC, ///< The SOC (Socket) service
- Service_IR, ///< The IR service
- Service_Y2R, ///< The Y2R (YUV to RGB conversion) service
- Service_PS, ///< The PS (Process) service
- Service_PLGLDR, ///< The PLGLDR (plugin loader) service
- Service_NEWS, ///< The NEWS (Notifications) service
- HW, ///< Low-level hardware emulation
- HW_Memory, ///< Memory-map and address translation
- HW_LCD, ///< LCD register emulation
- HW_GPU, ///< GPU control emulation
- HW_AES, ///< AES engine emulation
- HW_RSA, ///< RSA engine emulation
- HW_ECC, ///< ECC engine emulation
- Frontend, ///< Emulator UI
- Render, ///< Emulator video output and hardware acceleration
- Render_Software, ///< Software renderer backend
- Render_OpenGL, ///< OpenGL backend
- Render_Vulkan, ///< Vulkan backend
- Audio, ///< Audio emulation
- Audio_DSP, ///< The HLE and LLE implementations of the DSP
- Audio_Sink, ///< Emulator audio output backend
- Loader, ///< ROM loader
- Input, ///< Input emulation
- Network, ///< Network emulation
- Movie, ///< Movie (Input Recording) Playback
- WebService, ///< Interface to Citra Web Services
- RPC_Server, ///< RPC server
- Count, ///< Total number of logging classes
+ Service_SRV, ///< The SRV (Service Directory) implementation
+ Service_FRD, ///< The FRD (Friends) service
+ Service_FS, ///< The FS (Filesystem) service implementation
+ Service_ERR, ///< The ERR (Error) port implementation
+ Service_ACT, ///< The ACT (Account) service
+ Service_APT, ///< The APT (Applets) service
+ Service_BOSS, ///< The BOSS (SpotPass) service
+ Service_GSP, ///< The GSP (GPU control) service
+ Service_AC, ///< The AC (WiFi status) service
+ Service_AM, ///< The AM (Application manager) service
+ Service_PTM, ///< The PTM (Power status & misc.) service
+ Service_LDR, ///< The LDR (3ds dll loader) service
+ Service_MIC, ///< The MIC (Microphone) service
+ Service_NDM, ///< The NDM (Network daemon manager) service
+ Service_NFC, ///< The NFC service
+ Service_NIM, ///< The NIM (Network interface manager) service
+ Service_NS, ///< The NS (Nintendo User Interface Shell) service
+ Service_NWM, ///< The NWM (Network wlan manager) service
+ Service_CAM, ///< The CAM (Camera) service
+ Service_CECD, ///< The CECD (StreetPass) service
+ Service_CFG, ///< The CFG (Configuration) service
+ Service_CSND, ///< The CSND (CWAV format process) service
+ Service_DSP, ///< The DSP (DSP control) service
+ Service_DLP, ///< The DLP (Download Play) service
+ Service_HID, ///< The HID (Human interface device) service
+ Service_HTTP, ///< The HTTP service
+ Service_SOC, ///< The SOC (Socket) service
+ Service_IR, ///< The IR service
+ Service_Y2R, ///< The Y2R (YUV to RGB conversion) service
+ Service_PS, ///< The PS (Process) service
+ Service_PLGLDR, ///< The PLGLDR (plugin loader) service
+ Service_NEWS, ///< The NEWS (Notifications) service
+ HW, ///< Low-level hardware emulation
+ HW_Memory, ///< Memory-map and address translation
+ HW_LCD, ///< LCD register emulation
+ HW_GPU, ///< GPU control emulation
+ HW_AES, ///< AES engine emulation
+ HW_RSA, ///< RSA engine emulation
+ HW_ECC, ///< ECC engine emulation
+ Frontend, ///< Emulator UI
+ Render, ///< Emulator video output and hardware acceleration
+ Render_Software, ///< Software renderer backend
+ Render_OpenGL, ///< OpenGL backend
+ Render_Vulkan, ///< Vulkan backend
+ Audio, ///< Audio emulation
+ Audio_DSP, ///< The HLE and LLE implementations of the DSP
+ Audio_Sink, ///< Emulator audio output backend
+ Loader, ///< ROM loader
+ Input, ///< Input emulation
+ Network, ///< Network emulation
+ Movie, ///< Movie (Input Recording) Playback
+ WebService, ///< Interface to Citra Web Services
+ RPC_Server, ///< RPC server
+ RetroAchievements, ///< RetroAchievements
+ Count, ///< Total number of logging classes
};
} // namespace Common::Log
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index fd8212dcf..e10318ca9 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -506,6 +506,11 @@ target_link_libraries(citra_core PUBLIC citra_common PRIVATE audio_core network
target_link_libraries(citra_core PRIVATE Boost::boost Boost::serialization Boost::iostreams httplib)
target_link_libraries(citra_core PUBLIC dds-ktx PRIVATE cryptopp fmt lodepng open_source_archives)
+if (ENABLE_RETROACHIEVEMENTS)
+ target_link_libraries(citra_core PUBLIC retroachievements)
+ target_compile_definitions(citra_core PUBLIC ENABLE_RETROACHIEVEMENTS)
+endif()
+
if (ENABLE_WEB_SERVICE)
target_link_libraries(citra_core PRIVATE web_service)
endif()
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 42cba3160..89aa6b568 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -50,6 +50,9 @@
#include "core/rpc/server.h"
#endif
#include "network/network.h"
+#ifdef ENABLE_RETROACHIEVEMENTS
+#include "retroachievements/client.h"
+#endif
#include "video_core/custom_textures/custom_tex_manager.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
@@ -73,7 +76,12 @@ Core::Timing& Global() {
return System::GetInstance().CoreTiming();
}
-System::System() : movie{*this}, cheat_engine{*this} {}
+System::System() : movie{*this}, cheat_engine{*this} {
+#ifdef ENABLE_RETROACHIEVEMENTS
+ retroachievements_client = std::make_unique(*this);
+ retroachievements_client->Initialize();
+#endif
+}
System::~System() = default;
@@ -653,6 +661,16 @@ const Cheats::CheatEngine& System::CheatEngine() const {
return cheat_engine;
}
+#ifdef ENABLE_RETROACHIEVEMENTS
+RetroAchievements::Client& System::RetroAchievementsClient() {
+ return *retroachievements_client;
+}
+
+const RetroAchievements::Client& System::RetroAchievementsClient() const {
+ return *retroachievements_client;
+}
+#endif
+
void System::RegisterVideoDumper(std::shared_ptr dumper) {
video_dumper = std::move(dumper);
}
diff --git a/src/core/core.h b/src/core/core.h
index ca61e8262..a3eb793f2 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -70,6 +70,12 @@ namespace Loader {
class AppLoader;
}
+#ifdef ENABLE_RETROACHIEVEMENTS
+namespace RetroAchievements {
+class Client;
+} // namespace RetroAchievements
+#endif
+
namespace Core {
class ARM_Interface;
@@ -277,6 +283,14 @@ public:
/// Gets a const reference to the cheat engine
[[nodiscard]] const Cheats::CheatEngine& CheatEngine() const;
+#ifdef ENABLE_RETROACHIEVEMENTS
+ // Gets a reference to the RetroAchievements client
+ [[nodiscard]] RetroAchievements::Client& RetroAchievementsClient();
+
+ // Gets a const reference to the RetroAchievements client
+ [[nodiscard]] const RetroAchievements::Client& RetroAchievementsClient() const;
+#endif
+
/// Gets a reference to the custom texture cache system
[[nodiscard]] VideoCore::CustomTexManager& CustomTexManager();
@@ -430,6 +444,11 @@ private:
/// Cheats manager
Cheats::CheatEngine cheat_engine;
+#ifdef ENABLE_RETROACHIEVEMENTS
+ /// RetroAchievements
+ std::unique_ptr retroachievements_client;
+#endif
+
/// Video dumper backend
std::shared_ptr video_dumper;
diff --git a/src/retroachievements/CMakeLists.txt b/src/retroachievements/CMakeLists.txt
new file mode 100644
index 000000000..86b97b2d2
--- /dev/null
+++ b/src/retroachievements/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_library(retroachievements STATIC
+ client.cpp
+ client.h
+)
+
+target_link_libraries(retroachievements PRIVATE citra_common httplib rcheevos)
diff --git a/src/retroachievements/client.cpp b/src/retroachievements/client.cpp
new file mode 100644
index 000000000..836006de4
--- /dev/null
+++ b/src/retroachievements/client.cpp
@@ -0,0 +1,113 @@
+// Copyright Citra Emulator Project / Azahar Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "client.h"
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include "common/logging/log.h"
+#include "common/scm_rev.h"
+
+namespace RetroAchievements {
+static const char base_url[] = "https://retroachievements.org";
+
+// TODO: Make this use a numeric version as per
+// https://github.com/RetroAchievements/rcheevos/wiki/rc_client-integration#user-agent-header
+static const std::string user_agent = std::string("Azahar/") + Common::g_build_fullname;
+static const httplib::Headers headers = httplib::Headers({{"User-Agent", user_agent}});
+
+namespace Callbacks {
+// This is the function the rc_client will use to read memory for the emulator. we don't need it
+// yet, so just provide a dummy function that returns "no memory read".
+static uint32_t read_memory(uint32_t address, uint8_t* buffer, uint32_t num_bytes,
+ rc_client_t* client) {
+ // TODO: implement later
+ LOG_DEBUG(RetroAchievements, "Attempting to read memory.");
+
+ return 0;
+}
+
+static void server_call(const rc_api_request_t* request, rc_client_server_callback_t callback,
+ void* callback_data, rc_client_t* rc_client) {
+ LOG_DEBUG(RetroAchievements, "Attempting to call server.");
+
+ if (std::strstr(request->url, base_url) == nullptr) {
+ LOG_ERROR(RetroAchievements, "Invalid URL: \"{}\"", request->url);
+ return;
+ }
+
+ httplib::Client client(base_url);
+ const char* path = &request->url[sizeof(base_url) - 1];
+
+ // TODO: Should make this async?
+ httplib::Result result;
+ if (request->post_data) {
+ result = client.Post(path, headers, request->post_data, std::strlen(request->post_data),
+ request->content_type);
+ } else {
+ result = client.Get(path, headers);
+ }
+
+ if (result) {
+ LOG_DEBUG(RetroAchievements, "Status: {}", result->status);
+ LOG_DEBUG(RetroAchievements, "Body: {}", result->body);
+
+ rc_api_server_response_t server_response = {.body = result->body.c_str(),
+ .body_length = result->body.length(),
+ .http_status_code = result->status};
+ callback(&server_response, callback_data);
+ } else {
+ LOG_DEBUG(RetroAchievements, "HTTP error {}", result.error());
+ }
+}
+
+// Write log messages to the console
+static void log_message(const char* message, const rc_client_t* client) {
+ LOG_DEBUG(RetroAchievements, "RetroAchievements internal message: \"{}\"", message);
+}
+} // namespace Callbacks
+
+Client::Client(const Core::System& _system) : system{_system} {}
+
+Client::~Client() {
+ if (rc_client) {
+ rc_client_destroy(rc_client);
+ rc_client = NULL;
+ }
+}
+
+void Client::Initialize() {
+ LOG_DEBUG(RetroAchievements, "Initializing RetroAchievements client.");
+
+ rc_client = rc_client_create(Callbacks::read_memory, Callbacks::server_call);
+ rc_client_enable_logging(rc_client, RC_CLIENT_LOG_LEVEL_VERBOSE, Callbacks::log_message);
+ rc_client_set_hardcore_enabled(rc_client, 0);
+}
+
+static void login_callback(int result, const char* error_message, rc_client_t* client,
+ void* userdata) {
+ // If not successful, just report the error and bail.
+ if (result != RC_OK) {
+ LOG_ERROR(RetroAchievements, "Login failed.");
+ return;
+ }
+
+ // Login was successful. Capture the token for future logins so we don't have to store the
+ // password anywhere.
+ const rc_client_user_t* user = rc_client_get_user_info(client);
+ // store_retroachievements_credentials(user->username, user->token);
+
+ // Inform user of successful login
+ LOG_INFO(RetroAchievements, "Logged in as {} ({} points)", user->display_name, user->score);
+}
+
+void Client::LogInUser(const char* username, const char* password) {
+ rc_client_begin_login_with_password(rc_client, username, password, login_callback, NULL);
+}
+} // namespace RetroAchievements
diff --git a/src/retroachievements/client.h b/src/retroachievements/client.h
new file mode 100644
index 000000000..2ea1799ad
--- /dev/null
+++ b/src/retroachievements/client.h
@@ -0,0 +1,27 @@
+// Copyright Citra Emulator Project / Azahar Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Core {
+class System;
+}
+
+struct rc_client_t;
+
+namespace RetroAchievements {
+class Client {
+public:
+ explicit Client(const Core::System& system);
+ ~Client();
+
+ void Initialize();
+
+ void LogInUser(const char* username, const char* password);
+
+private:
+ const Core::System& system;
+ rc_client_t* rc_client = nullptr;
+};
+} // namespace RetroAchievements