From 8b57c4b239da92f76491792b2b67f9763c505e6a Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Tue, 11 Apr 2023 00:04:08 -0400 Subject: [PATCH] Added LoadGameByFilenameAsync and CloseGame to AchievementManager LoadGameByFilenameAsync sets up a volume reader and hashes the volume, then uses that hash to make the three consecutive API requests to resolve hash, start session and load game data. CloseGame resets the m_is_game_loaded flag, wipes the queue, and destroys all the game data responses. --- Source/Core/Core/AchievementManager.cpp | 111 ++++++++++++++++++++++-- Source/Core/Core/AchievementManager.h | 13 +-- Source/Core/Core/BootManager.cpp | 11 +++ Source/Core/Core/Core.cpp | 5 ++ 4 files changed, 126 insertions(+), 14 deletions(-) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index ff93120d7c..a566ae6a63 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -12,6 +12,7 @@ #include "Common/WorkQueueThread.h" #include "Config/AchievementSettings.h" #include "Core/Core.h" +#include "DiscIO/Volume.h" AchievementManager* AchievementManager::GetInstance() { @@ -32,46 +33,140 @@ void AchievementManager::Init() AchievementManager::ResponseType AchievementManager::Login(const std::string& password) { + if (!m_is_runtime_initialized) + return AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED; return VerifyCredentials(password); } -void AchievementManager::LoginAsync(const std::string& password, const LoginCallback& callback) +void AchievementManager::LoginAsync(const std::string& password, const ResponseCallback& callback) { + if (!m_is_runtime_initialized) + { + callback(AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED); + return; + } m_queue.EmplaceItem([this, password, callback] { callback(VerifyCredentials(password)); }); } bool AchievementManager::IsLoggedIn() const { - return m_login_data.response.succeeded; + return !Config::Get(Config::RA_API_TOKEN).empty(); +} + +void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path, + const ResponseCallback& callback) +{ + if (!m_is_runtime_initialized) + { + callback(AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED); + return; + } + struct FilereaderState + { + int64_t position = 0; + std::unique_ptr volume; + }; + rc_hash_filereader volume_reader{ + .open = + [](const char* path_utf8) { + auto state = std::make_unique(); + state->volume = DiscIO::CreateVolume(path_utf8); + return reinterpret_cast(state.release()); + }, + .seek = + [](void* file_handle, int64_t offset, int origin) { + switch (origin) + { + case SEEK_SET: + reinterpret_cast(file_handle)->position = offset; + break; + case SEEK_CUR: + reinterpret_cast(file_handle)->position += offset; + break; + case SEEK_END: + // Unused + break; + } + }, + .tell = + [](void* file_handle) { + return reinterpret_cast(file_handle)->position; + }, + .read = + [](void* file_handle, void* buffer, size_t requested_bytes) { + FilereaderState* filereader_state = reinterpret_cast(file_handle); + bool success = (filereader_state->volume->Read( + filereader_state->position, requested_bytes, reinterpret_cast(buffer), + DiscIO::PARTITION_NONE)); + if (success) + { + filereader_state->position += requested_bytes; + return requested_bytes; + } + else + { + return static_cast(0); + } + }, + .close = [](void* file_handle) { delete reinterpret_cast(file_handle); }}; + rc_hash_init_custom_filereader(&volume_reader); + std::array game_hash; + rc_hash_generate_from_file(game_hash.data(), RC_CONSOLE_GAMECUBE, iso_path.c_str()); + m_queue.EmplaceItem([this, callback, game_hash] { + const auto resolve_hash_response = ResolveHash(game_hash); + if (resolve_hash_response != ResponseType::SUCCESS || m_game_id == 0) + { + callback(resolve_hash_response); + return; + } + + const auto start_session_response = StartRASession(); + if (start_session_response != ResponseType::SUCCESS) + { + callback(start_session_response); + return; + } + + const auto fetch_game_data_response = FetchGameData(); + m_is_game_loaded = fetch_game_data_response == ResponseType::SUCCESS; + callback(fetch_game_data_response); + }); +} + +void AchievementManager::CloseGame() +{ + m_is_game_loaded = false; + m_game_id = 0; + m_queue.Cancel(); } void AchievementManager::Logout() { + CloseGame(); Config::SetBaseOrCurrent(Config::RA_API_TOKEN, ""); - rc_api_destroy_login_response(&m_login_data); - m_login_data.response.succeeded = 0; } void AchievementManager::Shutdown() { + CloseGame(); m_is_runtime_initialized = false; m_queue.Shutdown(); // DON'T log out - keep those credentials for next run. - rc_api_destroy_login_response(&m_login_data); - m_login_data.response.succeeded = 0; rc_runtime_destroy(&m_runtime); } AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std::string& password) { + rc_api_login_response_t login_data{}; std::string username = Config::Get(Config::RA_USERNAME); std::string api_token = Config::Get(Config::RA_API_TOKEN); rc_api_login_request_t login_request = { .username = username.c_str(), .api_token = api_token.c_str(), .password = password.c_str()}; ResponseType r_type = Request( - login_request, &m_login_data, rc_api_init_login_request, rc_api_process_login_response); + login_request, &login_data, rc_api_init_login_request, rc_api_process_login_response); if (r_type == ResponseType::SUCCESS) - Config::SetBaseOrCurrent(Config::RA_API_TOKEN, m_login_data.api_token); + Config::SetBaseOrCurrent(Config::RA_API_TOKEN, login_data.api_token); + rc_api_destroy_login_response(&login_data); return r_type; } diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index 7e39990fd3..ecbf236d60 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -22,17 +22,21 @@ public: enum class ResponseType { SUCCESS, + MANAGER_NOT_INITIALIZED, INVALID_CREDENTIALS, CONNECTION_FAILED, UNKNOWN_FAILURE }; - using LoginCallback = std::function; + using ResponseCallback = std::function; static AchievementManager* GetInstance(); void Init(); ResponseType Login(const std::string& password); - void LoginAsync(const std::string& password, const LoginCallback& callback); + void LoginAsync(const std::string& password, const ResponseCallback& callback); bool IsLoggedIn() const; + void LoadGameByFilenameAsync(const std::string& iso_path, const ResponseCallback& callback); + + void CloseGame(); void Logout(); void Shutdown(); @@ -54,11 +58,8 @@ private: rc_runtime_t m_runtime{}; bool m_is_runtime_initialized = false; unsigned int m_game_id = 0; - rc_api_login_response_t m_login_data{}; - - - rc_api_fetch_game_data_response_t m_game_data{}; + bool m_is_game_loaded = false; Common::WorkQueueThread> m_queue; }; // class AchievementManager diff --git a/Source/Core/Core/BootManager.cpp b/Source/Core/Core/BootManager.cpp index d2c1483994..1c9e2f03d4 100644 --- a/Source/Core/Core/BootManager.cpp +++ b/Source/Core/Core/BootManager.cpp @@ -29,6 +29,7 @@ #include "Common/IniFile.h" #include "Common/Logging/Log.h" +#include "Core/AchievementManager.h" #include "Core/Boot/Boot.h" #include "Core/Config/MainSettings.h" #include "Core/Config/SYSCONFSettings.h" @@ -163,6 +164,16 @@ bool BootCore(std::unique_ptr boot, const WindowSystemInfo& wsi) } } +#ifdef USE_RETRO_ACHIEVEMENTS + std::string path = ""; + if (std::holds_alternative(boot->parameters)) + { + path = std::get(boot->parameters).path; + } + AchievementManager::GetInstance()->LoadGameByFilenameAsync( + path, [](AchievementManager::ResponseType r_type) {}); +#endif // USE_RETRO_ACHIEVEMENTS + const bool load_ipl = !StartUp.bWii && !Config::Get(Config::MAIN_SKIP_IPL) && std::holds_alternative(boot->parameters); if (load_ipl) diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 4294faaf45..435b26985d 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -38,6 +38,7 @@ #include "Common/Timer.h" #include "Common/Version.h" +#include "Core/AchievementManager.h" #include "Core/Boot/Boot.h" #include "Core/BootManager.h" #include "Core/Config/MainSettings.h" @@ -283,6 +284,10 @@ void Stop() // - Hammertime! if (GetState() == State::Stopping || GetState() == State::Uninitialized) return; +#ifdef USE_RETRO_ACHIEVEMENTS + AchievementManager::GetInstance()->CloseGame(); +#endif // USE_RETRO_ACHIEVEMENTS + s_is_stopping = true; CallOnStateChangedCallbacks(State::Stopping);