Achievements: Refactor state serialization

Fix load failures, and eliminate the buffer copy.
This commit is contained in:
Stenzek 2024-12-25 22:22:09 +10:00
parent d360564cef
commit 00eb54cd15
No known key found for this signature in database
3 changed files with 42 additions and 35 deletions

View File

@ -230,8 +230,6 @@ struct State
std::string game_icon; std::string game_icon;
std::string game_icon_url; std::string game_icon_url;
DynamicHeapArray<u8> state_buffer;
rc_client_async_handle_t* login_request = nullptr; rc_client_async_handle_t* login_request = nullptr;
rc_client_async_handle_t* load_game_request = nullptr; rc_client_async_handle_t* load_game_request = nullptr;
@ -1019,7 +1017,6 @@ void Achievements::IdentifyGame(const std::string& path, CDImage* image)
ClearGameHash(); ClearGameHash();
s_state.game_path = path; s_state.game_path = path;
s_state.game_hash = std::move(game_hash); s_state.game_hash = std::move(game_hash);
s_state.state_buffer.deallocate();
#ifdef ENABLE_RAINTEGRATION #ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration()) if (IsUsingRAIntegration())
@ -1106,7 +1103,6 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
const bool was_disc_change = (userdata != nullptr); const bool was_disc_change = (userdata != nullptr);
s_state.load_game_request = nullptr; s_state.load_game_request = nullptr;
s_state.state_buffer.deallocate();
if (result == RC_NO_GAME_LOADED) if (result == RC_NO_GAME_LOADED)
{ {
@ -1224,7 +1220,6 @@ void Achievements::ClearGameInfo()
s_state.game_title = {}; s_state.game_title = {};
s_state.game_icon = {}; s_state.game_icon = {};
s_state.game_icon_url = {}; s_state.game_icon_url = {};
s_state.state_buffer.deallocate();
s_state.has_achievements = false; s_state.has_achievements = false;
s_state.has_leaderboards = false; s_state.has_leaderboards = false;
s_state.has_rich_presence = false; s_state.has_rich_presence = false;
@ -1768,7 +1763,7 @@ bool Achievements::DoState(StateWrapper& sw)
if (data_size == 0) if (data_size == 0)
{ {
// reset runtime, no data (state might've been created without cheevos) // reset runtime, no data (state might've been created without cheevos)
DEV_LOG("State is missing cheevos data, resetting runtime"); WARNING_LOG("State is missing cheevos data, resetting runtime");
#ifdef ENABLE_RAINTEGRATION #ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration()) if (IsUsingRAIntegration())
RA_OnReset(); RA_OnReset();
@ -1781,21 +1776,18 @@ bool Achievements::DoState(StateWrapper& sw)
return !sw.HasError(); return !sw.HasError();
} }
if (data_size > s_state.state_buffer.size()) const std::span<u8> data = sw.GetDeferredBytes(data_size);
s_state.state_buffer.resize(data_size);
if (data_size > 0)
sw.DoBytes(s_state.state_buffer.data(), data_size);
if (sw.HasError()) if (sw.HasError())
return false; return false;
#ifdef ENABLE_RAINTEGRATION #ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration()) if (IsUsingRAIntegration())
{ {
RA_RestoreState(reinterpret_cast<const char*>(s_state.state_buffer.data())); RA_RestoreState(reinterpret_cast<const char*>(data.data()));
} }
else else
{ {
const int result = rc_client_deserialize_progress_sized(s_state.client, s_state.state_buffer.data(), data_size); const int result = rc_client_deserialize_progress_sized(s_state.client, data.data(), data_size);
if (result != RC_OK) if (result != RC_OK)
{ {
WARNING_LOG("Failed to deserialize cheevos state ({}), resetting", result); WARNING_LOG("Failed to deserialize cheevos state ({}), resetting", result);
@ -1808,49 +1800,51 @@ bool Achievements::DoState(StateWrapper& sw)
} }
else else
{ {
size_t data_size; const size_t size_pos = sw.GetPosition();
#ifdef ENABLE_RAINTEGRATION #ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration()) if (IsUsingRAIntegration())
{ {
const int size = RA_CaptureState(nullptr, 0); const int size = RA_CaptureState(nullptr, 0);
u32 write_size = static_cast<u32>(std::max(size, 0));
sw.Do(&write_size);
data_size = (size >= 0) ? static_cast<u32>(size) : 0; const std::span<u8> data = sw.GetDeferredBytes(write_size);
s_state.state_buffer.resize(data_size); if (!data.empty())
if (data_size > 0)
{ {
const int result = const int result = RA_CaptureState(reinterpret_cast<char*>(data.data()), size);
RA_CaptureState(reinterpret_cast<char*>(s_state.state_buffer.data()), static_cast<int>(data_size)); if (result != static_cast<int>(size))
if (result != static_cast<int>(data_size))
{ {
WARNING_LOG("Failed to serialize cheevos state from RAIntegration."); WARNING_LOG("Failed to serialize cheevos state from RAIntegration.");
data_size = 0; write_size = 0;
sw.SetPosition(size_pos);
sw.Do(&write_size);
} }
} }
} }
else else
#endif #endif
{ {
data_size = rc_client_progress_size(s_state.client); u32 data_size = static_cast<u32>(rc_client_progress_size(s_state.client));
sw.Do(&data_size);
if (data_size > 0) if (data_size > 0)
{ {
if (s_state.state_buffer.size() < data_size) const std::span<u8> data = sw.GetDeferredBytes(data_size);
s_state.state_buffer.resize(data_size); if (!sw.HasError()) [[likely]]
{
const int result = rc_client_serialize_progress_sized(s_state.client, s_state.state_buffer.data(), data_size); const int result = rc_client_serialize_progress_sized(s_state.client, data.data(), data_size);
if (result != RC_OK) if (result != RC_OK)
{ {
// set data to zero, effectively serializing nothing // set data to zero, effectively serializing nothing
WARNING_LOG("Failed to serialize cheevos state ({})", result); WARNING_LOG("Failed to serialize cheevos state ({})", result);
data_size = 0; data_size = 0;
} sw.SetPosition(size_pos);
}
}
sw.Do(&data_size); sw.Do(&data_size);
if (data_size > 0) }
sw.DoBytes(s_state.state_buffer.data(), data_size); }
}
}
return !sw.HasError(); return !sw.HasError();
} }

View File

@ -111,6 +111,16 @@ bool StateWrapper::DoMarker(const char* marker)
return false; return false;
} }
std::span<u8> StateWrapper::GetDeferredBytes(size_t size)
{
if ((m_error = (m_error || (m_pos + size) > m_size))) [[unlikely]]
return {};
const std::span<u8> ret(&m_data[m_pos], size);
m_pos += size;
return ret;
}
bool StateWrapper::ReadData(void* buf, size_t size) bool StateWrapper::ReadData(void* buf, size_t size)
{ {
if ((m_error = (m_error || (m_pos + size) > m_size))) [[unlikely]] if ((m_error = (m_error || (m_pos + size) > m_size))) [[unlikely]]

View File

@ -9,8 +9,8 @@
#include <cstring> #include <cstring>
#include <deque> #include <deque>
#include <string>
#include <span> #include <span>
#include <string>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
@ -206,6 +206,9 @@ public:
m_pos += count; m_pos += count;
} }
// spans don't copy immediately
std::span<u8> GetDeferredBytes(size_t size);
private: private:
bool ReadData(void* buf, size_t size); bool ReadData(void* buf, size_t size);
bool WriteData(const void* buf, size_t size); bool WriteData(const void* buf, size_t size);