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.
This commit is contained in:
parent
bd75ce6e6d
commit
8b57c4b239
|
@ -12,6 +12,7 @@
|
||||||
#include "Common/WorkQueueThread.h"
|
#include "Common/WorkQueueThread.h"
|
||||||
#include "Config/AchievementSettings.h"
|
#include "Config/AchievementSettings.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
|
#include "DiscIO/Volume.h"
|
||||||
|
|
||||||
AchievementManager* AchievementManager::GetInstance()
|
AchievementManager* AchievementManager::GetInstance()
|
||||||
{
|
{
|
||||||
|
@ -32,46 +33,140 @@ void AchievementManager::Init()
|
||||||
|
|
||||||
AchievementManager::ResponseType AchievementManager::Login(const std::string& password)
|
AchievementManager::ResponseType AchievementManager::Login(const std::string& password)
|
||||||
{
|
{
|
||||||
|
if (!m_is_runtime_initialized)
|
||||||
|
return AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED;
|
||||||
return VerifyCredentials(password);
|
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)); });
|
m_queue.EmplaceItem([this, password, callback] { callback(VerifyCredentials(password)); });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AchievementManager::IsLoggedIn() const
|
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<DiscIO::Volume> volume;
|
||||||
|
};
|
||||||
|
rc_hash_filereader volume_reader{
|
||||||
|
.open =
|
||||||
|
[](const char* path_utf8) {
|
||||||
|
auto state = std::make_unique<FilereaderState>();
|
||||||
|
state->volume = DiscIO::CreateVolume(path_utf8);
|
||||||
|
return reinterpret_cast<void*>(state.release());
|
||||||
|
},
|
||||||
|
.seek =
|
||||||
|
[](void* file_handle, int64_t offset, int origin) {
|
||||||
|
switch (origin)
|
||||||
|
{
|
||||||
|
case SEEK_SET:
|
||||||
|
reinterpret_cast<FilereaderState*>(file_handle)->position = offset;
|
||||||
|
break;
|
||||||
|
case SEEK_CUR:
|
||||||
|
reinterpret_cast<FilereaderState*>(file_handle)->position += offset;
|
||||||
|
break;
|
||||||
|
case SEEK_END:
|
||||||
|
// Unused
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.tell =
|
||||||
|
[](void* file_handle) {
|
||||||
|
return reinterpret_cast<FilereaderState*>(file_handle)->position;
|
||||||
|
},
|
||||||
|
.read =
|
||||||
|
[](void* file_handle, void* buffer, size_t requested_bytes) {
|
||||||
|
FilereaderState* filereader_state = reinterpret_cast<FilereaderState*>(file_handle);
|
||||||
|
bool success = (filereader_state->volume->Read(
|
||||||
|
filereader_state->position, requested_bytes, reinterpret_cast<u8*>(buffer),
|
||||||
|
DiscIO::PARTITION_NONE));
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
filereader_state->position += requested_bytes;
|
||||||
|
return requested_bytes;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.close = [](void* file_handle) { delete reinterpret_cast<FilereaderState*>(file_handle); }};
|
||||||
|
rc_hash_init_custom_filereader(&volume_reader);
|
||||||
|
std::array<char, HASH_LENGTH> 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()
|
void AchievementManager::Logout()
|
||||||
{
|
{
|
||||||
|
CloseGame();
|
||||||
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, "");
|
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, "");
|
||||||
rc_api_destroy_login_response(&m_login_data);
|
|
||||||
m_login_data.response.succeeded = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementManager::Shutdown()
|
void AchievementManager::Shutdown()
|
||||||
{
|
{
|
||||||
|
CloseGame();
|
||||||
m_is_runtime_initialized = false;
|
m_is_runtime_initialized = false;
|
||||||
m_queue.Shutdown();
|
m_queue.Shutdown();
|
||||||
// DON'T log out - keep those credentials for next run.
|
// 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);
|
rc_runtime_destroy(&m_runtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std::string& password)
|
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 username = Config::Get(Config::RA_USERNAME);
|
||||||
std::string api_token = Config::Get(Config::RA_API_TOKEN);
|
std::string api_token = Config::Get(Config::RA_API_TOKEN);
|
||||||
rc_api_login_request_t login_request = {
|
rc_api_login_request_t login_request = {
|
||||||
.username = username.c_str(), .api_token = api_token.c_str(), .password = password.c_str()};
|
.username = username.c_str(), .api_token = api_token.c_str(), .password = password.c_str()};
|
||||||
ResponseType r_type = Request<rc_api_login_request_t, rc_api_login_response_t>(
|
ResponseType r_type = Request<rc_api_login_request_t, rc_api_login_response_t>(
|
||||||
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)
|
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;
|
return r_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,17 +22,21 @@ public:
|
||||||
enum class ResponseType
|
enum class ResponseType
|
||||||
{
|
{
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
|
MANAGER_NOT_INITIALIZED,
|
||||||
INVALID_CREDENTIALS,
|
INVALID_CREDENTIALS,
|
||||||
CONNECTION_FAILED,
|
CONNECTION_FAILED,
|
||||||
UNKNOWN_FAILURE
|
UNKNOWN_FAILURE
|
||||||
};
|
};
|
||||||
using LoginCallback = std::function<void(ResponseType)>;
|
using ResponseCallback = std::function<void(ResponseType)>;
|
||||||
|
|
||||||
static AchievementManager* GetInstance();
|
static AchievementManager* GetInstance();
|
||||||
void Init();
|
void Init();
|
||||||
ResponseType Login(const std::string& password);
|
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;
|
bool IsLoggedIn() const;
|
||||||
|
void LoadGameByFilenameAsync(const std::string& iso_path, const ResponseCallback& callback);
|
||||||
|
|
||||||
|
void CloseGame();
|
||||||
void Logout();
|
void Logout();
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
|
@ -54,11 +58,8 @@ private:
|
||||||
rc_runtime_t m_runtime{};
|
rc_runtime_t m_runtime{};
|
||||||
bool m_is_runtime_initialized = false;
|
bool m_is_runtime_initialized = false;
|
||||||
unsigned int m_game_id = 0;
|
unsigned int m_game_id = 0;
|
||||||
rc_api_login_response_t m_login_data{};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
rc_api_fetch_game_data_response_t m_game_data{};
|
rc_api_fetch_game_data_response_t m_game_data{};
|
||||||
|
bool m_is_game_loaded = false;
|
||||||
|
|
||||||
Common::WorkQueueThread<std::function<void()>> m_queue;
|
Common::WorkQueueThread<std::function<void()>> m_queue;
|
||||||
}; // class AchievementManager
|
}; // class AchievementManager
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "Common/IniFile.h"
|
#include "Common/IniFile.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
|
|
||||||
|
#include "Core/AchievementManager.h"
|
||||||
#include "Core/Boot/Boot.h"
|
#include "Core/Boot/Boot.h"
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
#include "Core/Config/SYSCONFSettings.h"
|
#include "Core/Config/SYSCONFSettings.h"
|
||||||
|
@ -163,6 +164,16 @@ bool BootCore(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
|
std::string path = "";
|
||||||
|
if (std::holds_alternative<BootParameters::Disc>(boot->parameters))
|
||||||
|
{
|
||||||
|
path = std::get<BootParameters::Disc>(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) &&
|
const bool load_ipl = !StartUp.bWii && !Config::Get(Config::MAIN_SKIP_IPL) &&
|
||||||
std::holds_alternative<BootParameters::Disc>(boot->parameters);
|
std::holds_alternative<BootParameters::Disc>(boot->parameters);
|
||||||
if (load_ipl)
|
if (load_ipl)
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "Common/Timer.h"
|
#include "Common/Timer.h"
|
||||||
#include "Common/Version.h"
|
#include "Common/Version.h"
|
||||||
|
|
||||||
|
#include "Core/AchievementManager.h"
|
||||||
#include "Core/Boot/Boot.h"
|
#include "Core/Boot/Boot.h"
|
||||||
#include "Core/BootManager.h"
|
#include "Core/BootManager.h"
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
|
@ -283,6 +284,10 @@ void Stop() // - Hammertime!
|
||||||
if (GetState() == State::Stopping || GetState() == State::Uninitialized)
|
if (GetState() == State::Stopping || GetState() == State::Uninitialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
|
AchievementManager::GetInstance()->CloseGame();
|
||||||
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
|
|
||||||
s_is_stopping = true;
|
s_is_stopping = true;
|
||||||
|
|
||||||
CallOnStateChangedCallbacks(State::Stopping);
|
CallOnStateChangedCallbacks(State::Stopping);
|
||||||
|
|
Loading…
Reference in New Issue