mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
Merge pull request #12216 from LillyJadeKatrin/retroachievements-rehash
RetroAchievements - Improved Game Loading
This commit is contained in:
commit
b4276049fc
@ -20,7 +20,7 @@
|
||||
#include "Core/Core.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
#include "Core/System.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
#include "VideoCommon/VideoEvents.h"
|
||||
|
||||
@ -91,8 +91,7 @@ bool AchievementManager::IsLoggedIn() const
|
||||
return !Config::Get(Config::RA_API_TOKEN).empty();
|
||||
}
|
||||
|
||||
void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
|
||||
const ResponseCallback& callback)
|
||||
void AchievementManager::HashGame(const std::string& file_path, const ResponseCallback& callback)
|
||||
{
|
||||
if (!m_is_runtime_initialized)
|
||||
{
|
||||
@ -101,117 +100,215 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
|
||||
callback(AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED);
|
||||
return;
|
||||
}
|
||||
if (m_disabled)
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager is disabled until core is rebooted.");
|
||||
OSD::AddMessage("Achievements are disabled until you restart emulation.",
|
||||
OSD::Duration::VERY_LONG, OSD::Color::RED);
|
||||
return;
|
||||
}
|
||||
m_system = &Core::System::GetInstance();
|
||||
m_queue.EmplaceItem([this, callback, file_path] {
|
||||
Hash new_hash;
|
||||
{
|
||||
std::lock_guard lg{m_filereader_lock};
|
||||
rc_hash_filereader volume_reader{
|
||||
.open = &AchievementManager::FilereaderOpenByFilepath,
|
||||
.seek = &AchievementManager::FilereaderSeek,
|
||||
.tell = &AchievementManager::FilereaderTell,
|
||||
.read = &AchievementManager::FilereaderRead,
|
||||
.close = &AchievementManager::FilereaderClose,
|
||||
};
|
||||
rc_hash_init_custom_filereader(&volume_reader);
|
||||
if (!rc_hash_generate_from_file(new_hash.data(), RC_CONSOLE_GAMECUBE, file_path.c_str()))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Unable to generate achievement hash from game file {}.",
|
||||
file_path);
|
||||
callback(AchievementManager::ResponseType::MALFORMED_OBJECT);
|
||||
}
|
||||
}
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
if (m_disabled)
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Achievements disabled while hash was resolving.");
|
||||
callback(AchievementManager::ResponseType::EXPIRED_CONTEXT);
|
||||
return;
|
||||
}
|
||||
m_game_hash = std::move(new_hash);
|
||||
}
|
||||
LoadGameSync(callback);
|
||||
});
|
||||
}
|
||||
|
||||
void AchievementManager::HashGame(const DiscIO::Volume* volume, const ResponseCallback& callback)
|
||||
{
|
||||
if (!m_is_runtime_initialized)
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS,
|
||||
"Attempted to load game achievements without Achievement Manager initialized.");
|
||||
callback(AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED);
|
||||
return;
|
||||
}
|
||||
if (volume == nullptr)
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "New volume is empty.");
|
||||
return;
|
||||
}
|
||||
if (m_disabled)
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager is disabled until core is rebooted.");
|
||||
OSD::AddMessage("Achievements are disabled until core is rebooted.", OSD::Duration::VERY_LONG,
|
||||
OSD::Color::RED);
|
||||
return;
|
||||
}
|
||||
// Need to SetDisabled outside a lock because it uses m_lock internally.
|
||||
bool disable = false;
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
if (m_loading_volume.get() != nullptr)
|
||||
{
|
||||
disable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_loading_volume = DiscIO::CreateVolume(volume->GetBlobReader().CopyReader());
|
||||
}
|
||||
}
|
||||
if (disable)
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Disabling Achievement Manager due to hash spam.");
|
||||
SetDisabled(true);
|
||||
callback(AchievementManager::ResponseType::EXPIRED_CONTEXT);
|
||||
return;
|
||||
}
|
||||
m_system = &Core::System::GetInstance();
|
||||
struct FilereaderState
|
||||
{
|
||||
int64_t position = 0;
|
||||
std::unique_ptr<DiscIO::Volume> volume;
|
||||
};
|
||||
rc_hash_filereader volume_reader{
|
||||
.open = [](const char* path_utf8) -> void* {
|
||||
auto state = std::make_unique<FilereaderState>();
|
||||
state->volume = DiscIO::CreateVolume(path_utf8);
|
||||
if (!state->volume)
|
||||
return nullptr;
|
||||
return 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);
|
||||
if (!rc_hash_generate_from_file(m_game_hash.data(), RC_CONSOLE_GAMECUBE, iso_path.c_str()))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Unable to generate achievement hash from game file.");
|
||||
return;
|
||||
}
|
||||
m_queue.EmplaceItem([this, callback] {
|
||||
const auto resolve_hash_response = ResolveHash(this->m_game_hash);
|
||||
if (resolve_hash_response != ResponseType::SUCCESS || m_game_id == 0)
|
||||
Hash new_hash;
|
||||
{
|
||||
callback(resolve_hash_response);
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "No RetroAchievements data found for this game.");
|
||||
OSD::AddMessage("No RetroAchievements data found for this game.", OSD::Duration::VERY_LONG,
|
||||
OSD::Color::RED);
|
||||
return;
|
||||
std::lock_guard lg{m_filereader_lock};
|
||||
rc_hash_filereader volume_reader{
|
||||
.open = &AchievementManager::FilereaderOpenByVolume,
|
||||
.seek = &AchievementManager::FilereaderSeek,
|
||||
.tell = &AchievementManager::FilereaderTell,
|
||||
.read = &AchievementManager::FilereaderRead,
|
||||
.close = &AchievementManager::FilereaderClose,
|
||||
};
|
||||
rc_hash_init_custom_filereader(&volume_reader);
|
||||
if (!rc_hash_generate_from_file(new_hash.data(), RC_CONSOLE_GAMECUBE, ""))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Unable to generate achievement hash from volume.");
|
||||
callback(AchievementManager::ResponseType::MALFORMED_OBJECT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto start_session_response = StartRASession();
|
||||
if (start_session_response != ResponseType::SUCCESS)
|
||||
{
|
||||
callback(start_session_response);
|
||||
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to connect to RetroAchievements server.");
|
||||
OSD::AddMessage("Failed to connect to RetroAchievements server.", OSD::Duration::VERY_LONG,
|
||||
OSD::Color::RED);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto fetch_game_data_response = FetchGameData();
|
||||
if (fetch_game_data_response != ResponseType::SUCCESS)
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Unable to retrieve data from RetroAchievements server.");
|
||||
OSD::AddMessage("Unable to retrieve data from RetroAchievements server.",
|
||||
OSD::Duration::VERY_LONG, OSD::Color::RED);
|
||||
return;
|
||||
}
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Loading achievements for {}.", m_game_data.title);
|
||||
|
||||
// Claim the lock, then queue the fetch unlock data calls, then initialize the unlock map in
|
||||
// ActivateDeactiveAchievements. This allows the calls to process while initializing the
|
||||
// unlock map but then forces them to wait until it's initialized before making modifications to
|
||||
// it.
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
m_is_game_loaded = true;
|
||||
m_framecount = 0;
|
||||
LoadUnlockData([](ResponseType r_type) {});
|
||||
ActivateDeactivateAchievements();
|
||||
ActivateDeactivateLeaderboards();
|
||||
ActivateDeactivateRichPresence();
|
||||
if (m_disabled)
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Achievements disabled while hash was resolving.");
|
||||
callback(AchievementManager::ResponseType::EXPIRED_CONTEXT);
|
||||
return;
|
||||
}
|
||||
m_game_hash = std::move(new_hash);
|
||||
m_loading_volume.reset();
|
||||
}
|
||||
FetchBadges();
|
||||
// Reset this to zero so that RP immediately triggers on the first frame
|
||||
m_last_ping_time = 0;
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "RetroAchievements successfully loaded for {}.", m_game_data.title);
|
||||
|
||||
if (m_update_callback)
|
||||
m_update_callback();
|
||||
callback(fetch_game_data_response);
|
||||
LoadGameSync(callback);
|
||||
});
|
||||
}
|
||||
|
||||
void AchievementManager::LoadGameSync(const ResponseCallback& callback)
|
||||
{
|
||||
u32 new_game_id = 0;
|
||||
Hash current_hash;
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
current_hash = m_game_hash;
|
||||
}
|
||||
const auto resolve_hash_response = ResolveHash(current_hash, &new_game_id);
|
||||
if (resolve_hash_response != ResponseType::SUCCESS || new_game_id == 0)
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "No RetroAchievements data found for this game.");
|
||||
OSD::AddMessage("No RetroAchievements data found for this game.", OSD::Duration::VERY_LONG,
|
||||
OSD::Color::RED);
|
||||
SetDisabled(true);
|
||||
callback(resolve_hash_response);
|
||||
return;
|
||||
}
|
||||
u32 old_game_id;
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
old_game_id = m_game_id;
|
||||
}
|
||||
if (new_game_id == old_game_id)
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Alternate hash resolved for current game {}.", old_game_id);
|
||||
callback(ResponseType::SUCCESS);
|
||||
return;
|
||||
}
|
||||
else if (old_game_id != 0)
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Swapping game {} for game {}; achievements disabled.", old_game_id,
|
||||
new_game_id);
|
||||
OSD::AddMessage("Achievements are now disabled. Please close emulation to re-enable.",
|
||||
OSD::Duration::VERY_LONG, OSD::Color::RED);
|
||||
SetDisabled(true);
|
||||
callback(ResponseType::EXPIRED_CONTEXT);
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
m_game_id = new_game_id;
|
||||
}
|
||||
|
||||
const auto start_session_response = StartRASession();
|
||||
if (start_session_response != ResponseType::SUCCESS)
|
||||
{
|
||||
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to connect to RetroAchievements server.");
|
||||
OSD::AddMessage("Failed to connect to RetroAchievements server.", OSD::Duration::VERY_LONG,
|
||||
OSD::Color::RED);
|
||||
callback(start_session_response);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto fetch_game_data_response = FetchGameData();
|
||||
if (fetch_game_data_response != ResponseType::SUCCESS)
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Unable to retrieve data from RetroAchievements server.");
|
||||
OSD::AddMessage("Unable to retrieve data from RetroAchievements server.",
|
||||
OSD::Duration::VERY_LONG, OSD::Color::RED);
|
||||
return;
|
||||
}
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Loading achievements for {}.", m_game_data.title);
|
||||
|
||||
// Claim the lock, then queue the fetch unlock data calls, then initialize the unlock map in
|
||||
// ActivateDeactiveAchievements. This allows the calls to process while initializing the
|
||||
// unlock map but then forces them to wait until it's initialized before making modifications to
|
||||
// it.
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
m_is_game_loaded = true;
|
||||
m_framecount = 0;
|
||||
LoadUnlockData([](ResponseType r_type) {});
|
||||
ActivateDeactivateAchievements();
|
||||
ActivateDeactivateLeaderboards();
|
||||
ActivateDeactivateRichPresence();
|
||||
}
|
||||
FetchBadges();
|
||||
// Reset this to zero so that RP immediately triggers on the first frame
|
||||
m_last_ping_time = 0;
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "RetroAchievements successfully loaded for {}.", m_game_data.title);
|
||||
|
||||
if (m_update_callback)
|
||||
m_update_callback();
|
||||
callback(fetch_game_data_response);
|
||||
}
|
||||
|
||||
bool AchievementManager::IsGameLoaded() const
|
||||
{
|
||||
return m_is_game_loaded;
|
||||
@ -741,6 +838,25 @@ AchievementManager::RichPresence AchievementManager::GetRichPresence()
|
||||
return rich_presence;
|
||||
}
|
||||
|
||||
void AchievementManager::SetDisabled(bool disable)
|
||||
{
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
m_disabled = disable;
|
||||
}
|
||||
if (disable)
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager has been disabled.");
|
||||
OSD::AddMessage("Please close all games to re-enable achievements.", OSD::Duration::VERY_LONG,
|
||||
OSD::Color::RED);
|
||||
CloseGame();
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager has been re-enabled.");
|
||||
}
|
||||
};
|
||||
|
||||
void AchievementManager::CloseGame()
|
||||
{
|
||||
{
|
||||
@ -790,6 +906,69 @@ void AchievementManager::Shutdown()
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager shut down.");
|
||||
}
|
||||
|
||||
void* AchievementManager::FilereaderOpenByFilepath(const char* path_utf8)
|
||||
{
|
||||
auto state = std::make_unique<FilereaderState>();
|
||||
state->volume = DiscIO::CreateVolume(path_utf8);
|
||||
if (!state->volume)
|
||||
return nullptr;
|
||||
return state.release();
|
||||
}
|
||||
|
||||
void* AchievementManager::FilereaderOpenByVolume(const char* path_utf8)
|
||||
{
|
||||
auto state = std::make_unique<FilereaderState>();
|
||||
{
|
||||
std::lock_guard lg{*AchievementManager::GetInstance()->GetLock()};
|
||||
state->volume = std::move(AchievementManager::GetInstance()->GetLoadingVolume());
|
||||
}
|
||||
if (!state->volume)
|
||||
return nullptr;
|
||||
return state.release();
|
||||
}
|
||||
|
||||
void AchievementManager::FilereaderSeek(void* file_handle, int64_t offset, int origin)
|
||||
{
|
||||
switch (origin)
|
||||
{
|
||||
case SEEK_SET:
|
||||
static_cast<FilereaderState*>(file_handle)->position = offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
static_cast<FilereaderState*>(file_handle)->position += offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
// Unused
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t AchievementManager::FilereaderTell(void* file_handle)
|
||||
{
|
||||
return static_cast<FilereaderState*>(file_handle)->position;
|
||||
}
|
||||
|
||||
size_t AchievementManager::FilereaderRead(void* file_handle, void* buffer, size_t requested_bytes)
|
||||
{
|
||||
FilereaderState* filereader_state = static_cast<FilereaderState*>(file_handle);
|
||||
bool success = (filereader_state->volume->Read(filereader_state->position, requested_bytes,
|
||||
static_cast<u8*>(buffer), DiscIO::PARTITION_NONE));
|
||||
if (success)
|
||||
{
|
||||
filereader_state->position += requested_bytes;
|
||||
return requested_bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void AchievementManager::FilereaderClose(void* file_handle)
|
||||
{
|
||||
delete static_cast<FilereaderState*>(file_handle);
|
||||
}
|
||||
|
||||
AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std::string& password)
|
||||
{
|
||||
rc_api_login_response_t login_data{};
|
||||
@ -826,8 +1005,8 @@ AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std
|
||||
return r_type;
|
||||
}
|
||||
|
||||
AchievementManager::ResponseType
|
||||
AchievementManager::ResolveHash(std::array<char, HASH_LENGTH> game_hash)
|
||||
AchievementManager::ResponseType AchievementManager::ResolveHash(const Hash& game_hash,
|
||||
u32* game_id)
|
||||
{
|
||||
rc_api_resolve_hash_response_t hash_data{};
|
||||
std::string username, api_token;
|
||||
@ -843,9 +1022,8 @@ AchievementManager::ResolveHash(std::array<char, HASH_LENGTH> game_hash)
|
||||
rc_api_process_resolve_hash_response);
|
||||
if (r_type == ResponseType::SUCCESS)
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
m_game_id = hash_data.game_id;
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Hashed game ID {} for RetroAchievements.", m_game_id);
|
||||
*game_id = hash_data.game_id;
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Hashed game ID {} for RetroAchievements.", *game_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "Common/Event.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
@ -51,6 +52,8 @@ public:
|
||||
u32 soft_points;
|
||||
};
|
||||
|
||||
static constexpr size_t HASH_SIZE = 33;
|
||||
using Hash = std::array<char, HASH_SIZE>;
|
||||
using AchievementId = u32;
|
||||
static constexpr size_t FORMAT_SIZE = 24;
|
||||
using FormattedValue = std::array<char, FORMAT_SIZE>;
|
||||
@ -105,7 +108,8 @@ public:
|
||||
ResponseType Login(const std::string& password);
|
||||
void LoginAsync(const std::string& password, const ResponseCallback& callback);
|
||||
bool IsLoggedIn() const;
|
||||
void LoadGameByFilenameAsync(const std::string& iso_path, const ResponseCallback& callback);
|
||||
void HashGame(const std::string& file_path, const ResponseCallback& callback);
|
||||
void HashGame(const DiscIO::Volume* volume, const ResponseCallback& callback);
|
||||
bool IsGameLoaded() const;
|
||||
|
||||
void LoadUnlockData(const ResponseCallback& callback);
|
||||
@ -132,6 +136,8 @@ public:
|
||||
u32* target);
|
||||
const std::unordered_map<AchievementId, LeaderboardStatus>& GetLeaderboardsInfo() const;
|
||||
RichPresence GetRichPresence();
|
||||
bool IsDisabled() const { return m_disabled; };
|
||||
void SetDisabled(bool disabled);
|
||||
|
||||
void CloseGame();
|
||||
void Logout();
|
||||
@ -140,15 +146,29 @@ public:
|
||||
private:
|
||||
AchievementManager() = default;
|
||||
|
||||
static constexpr int HASH_LENGTH = 33;
|
||||
struct FilereaderState
|
||||
{
|
||||
int64_t position = 0;
|
||||
std::unique_ptr<DiscIO::Volume> volume;
|
||||
};
|
||||
|
||||
static void* FilereaderOpenByFilepath(const char* path_utf8);
|
||||
static void* FilereaderOpenByVolume(const char* path_utf8);
|
||||
static void FilereaderSeek(void* file_handle, int64_t offset, int origin);
|
||||
static int64_t FilereaderTell(void* file_handle);
|
||||
static size_t FilereaderRead(void* file_handle, void* buffer, size_t requested_bytes);
|
||||
static void FilereaderClose(void* file_handle);
|
||||
|
||||
ResponseType VerifyCredentials(const std::string& password);
|
||||
ResponseType ResolveHash(std::array<char, HASH_LENGTH> game_hash);
|
||||
ResponseType ResolveHash(const Hash& game_hash, u32* game_id);
|
||||
void LoadGameSync(const ResponseCallback& callback);
|
||||
ResponseType StartRASession();
|
||||
ResponseType FetchGameData();
|
||||
ResponseType FetchUnlockData(bool hardcore);
|
||||
ResponseType FetchBoardInfo(AchievementId leaderboard_id);
|
||||
|
||||
std::unique_ptr<DiscIO::Volume>& GetLoadingVolume() { return m_loading_volume; };
|
||||
|
||||
void ActivateDeactivateAchievement(AchievementId id, bool enabled, bool unofficial, bool encore);
|
||||
void GenerateRichPresence();
|
||||
|
||||
@ -174,10 +194,12 @@ private:
|
||||
Core::System* m_system{};
|
||||
bool m_is_runtime_initialized = false;
|
||||
UpdateCallback m_update_callback;
|
||||
std::unique_ptr<DiscIO::Volume> m_loading_volume;
|
||||
bool m_disabled = false;
|
||||
std::string m_display_name;
|
||||
u32 m_player_score = 0;
|
||||
BadgeStatus m_player_badge;
|
||||
std::array<char, HASH_LENGTH> m_game_hash{};
|
||||
Hash m_game_hash{};
|
||||
u32 m_game_id = 0;
|
||||
rc_api_fetch_game_data_response_t m_game_data{};
|
||||
bool m_is_game_loaded = false;
|
||||
@ -192,6 +214,7 @@ private:
|
||||
Common::WorkQueueThread<std::function<void()>> m_queue;
|
||||
Common::WorkQueueThread<std::function<void()>> m_image_queue;
|
||||
mutable std::recursive_mutex m_lock;
|
||||
std::recursive_mutex m_filereader_lock;
|
||||
}; // class AchievementManager
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Boot/DolReader.h"
|
||||
#include "Core/Boot/ElfReader.h"
|
||||
#include "Core/CommonTitles.h"
|
||||
@ -558,6 +559,11 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
|
||||
SetupGCMemory(system, guard);
|
||||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance()->HashGame(executable.path,
|
||||
[](AchievementManager::ResponseType r_type) {});
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
if (!executable.reader->LoadIntoMemory(system))
|
||||
{
|
||||
PanicAlertFmtT("Failed to load the executable to memory.");
|
||||
|
@ -165,13 +165,7 @@ 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) {});
|
||||
AchievementManager::GetInstance()->SetDisabled(false);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
const bool load_ipl = !StartUp.bWii && !Config::Get(Config::MAIN_SKIP_IPL) &&
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Version.h"
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Boot/Boot.h"
|
||||
#include "Core/CommonTitles.h"
|
||||
#include "Core/Config/DefaultLocale.h"
|
||||
@ -168,6 +169,10 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
|
||||
if (!was_changed)
|
||||
return;
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance()->SetDisabled(true);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
if (game_id == "00000000")
|
||||
{
|
||||
m_title_name.clear();
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "Common/Config/Config.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/SessionSettings.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
@ -396,6 +397,11 @@ void DVDInterface::SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
|
||||
m_auto_disc_change_index = 0;
|
||||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance()->HashGame(disc.get(),
|
||||
[](AchievementManager::ResponseType r_type) {});
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
// Assume that inserting a disc requires having an empty disc before
|
||||
if (had_disc != has_disc)
|
||||
ExpansionInterface::g_rtc_flags[ExpansionInterface::RTCFlag::DiscChanged] = true;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "Common/NandPaths.h"
|
||||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/CommonTitles.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
@ -476,6 +477,12 @@ bool ESDevice::LaunchPPCTitle(u64 title_id)
|
||||
if (!Core::IsRunningAndStarted())
|
||||
return BootstrapPPC();
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
INFO_LOG_FMT(ACHIEVEMENTS,
|
||||
"WAD and NAND formats not currently supported by Achievement Manager.");
|
||||
AchievementManager::GetInstance()->SetDisabled(true);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
core_timing.RemoveEvent(s_bootstrap_ppc_for_launch_event);
|
||||
core_timing.ScheduleEvent(ticks, s_bootstrap_ppc_for_launch_event);
|
||||
return true;
|
||||
|
@ -37,6 +37,11 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
|
||||
m_game_progress_hard = new QProgressBar();
|
||||
m_game_progress_soft = new QProgressBar();
|
||||
m_rich_presence = new QLabel();
|
||||
m_locked_warning = new QLabel();
|
||||
|
||||
m_locked_warning->setText(tr("Achievements have been disabled.<br>Please close all running "
|
||||
"games to re-enable achievements."));
|
||||
m_locked_warning->setStyleSheet(QStringLiteral("QLabel { color : red; }"));
|
||||
|
||||
QSizePolicy sp_retain = m_game_progress_hard->sizePolicy();
|
||||
sp_retain.setRetainSizeWhenHidden(true);
|
||||
@ -54,6 +59,7 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
|
||||
text_col->addWidget(m_game_progress_hard);
|
||||
text_col->addWidget(m_game_progress_soft);
|
||||
text_col->addWidget(m_rich_presence);
|
||||
text_col->addWidget(m_locked_warning);
|
||||
QHBoxLayout* header_layout = new QHBoxLayout();
|
||||
header_layout->addLayout(icon_col);
|
||||
header_layout->addLayout(text_col);
|
||||
@ -148,6 +154,7 @@ void AchievementHeaderWidget::UpdateData()
|
||||
QString::fromUtf8(AchievementManager::GetInstance()->GetRichPresence().data()));
|
||||
if (!m_rich_presence->isVisible())
|
||||
m_rich_presence->setVisible(Config::Get(Config::RA_RICH_PRESENCE_ENABLED));
|
||||
m_locked_warning->setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -157,6 +164,10 @@ void AchievementHeaderWidget::UpdateData()
|
||||
m_game_progress_hard->setVisible(false);
|
||||
m_game_progress_soft->setVisible(false);
|
||||
m_rich_presence->setVisible(false);
|
||||
if (AchievementManager::GetInstance()->IsDisabled())
|
||||
{
|
||||
m_locked_warning->setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ private:
|
||||
QProgressBar* m_game_progress_hard;
|
||||
QProgressBar* m_game_progress_soft;
|
||||
QLabel* m_rich_presence;
|
||||
QLabel* m_locked_warning;
|
||||
QGroupBox* m_header_box;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user