Achievements: Eliminate intermediate malloc on state save

This commit is contained in:
Stenzek 2024-06-24 21:00:17 +10:00 committed by Connor McLaughlin
parent 46931072c7
commit f909282973
3 changed files with 34 additions and 37 deletions

View File

@ -14,6 +14,7 @@
#include "IopMem.h" #include "IopMem.h"
#include "MTGS.h" #include "MTGS.h"
#include "Memory.h" #include "Memory.h"
#include "SaveState.h"
#include "VMManager.h" #include "VMManager.h"
#include "svnrev.h" #include "svnrev.h"
#include "vtlb.h" #include "vtlb.h"
@ -23,6 +24,7 @@
#include "common/Error.h" #include "common/Error.h"
#include "common/FileSystem.h" #include "common/FileSystem.h"
#include "common/HTTPDownloader.h" #include "common/HTTPDownloader.h"
#include "common/HeapArray.h"
#include "common/MD5Digest.h" #include "common/MD5Digest.h"
#include "common/Path.h" #include "common/Path.h"
#include "common/ScopedGuard.h" #include "common/ScopedGuard.h"
@ -1466,7 +1468,7 @@ void Achievements::SetHardcoreMode(bool enabled, bool force_display_message)
Host::OnAchievementsHardcoreModeChanged(enabled); Host::OnAchievementsHardcoreModeChanged(enabled);
} }
void Achievements::LoadState(const u8* state_data, u32 state_data_size) void Achievements::LoadState(std::span<const u8> data)
{ {
const auto lock = GetLock(); const auto lock = GetLock();
@ -1479,14 +1481,14 @@ void Achievements::LoadState(const u8* state_data, u32 state_data_size)
#ifdef ENABLE_RAINTEGRATION #ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration()) if (IsUsingRAIntegration())
{ {
if (state_data_size == 0) if (data.empty())
{ {
Console.Warning("State is missing cheevos data, resetting RAIntegration"); Console.Warning("State is missing cheevos data, resetting RAIntegration");
RA_OnReset(); RA_OnReset();
} }
else else
{ {
RA_RestoreState(reinterpret_cast<const char*>(state_data)); RA_RestoreState(reinterpret_cast<const char*>(data.data()));
} }
return; return;
@ -1503,7 +1505,7 @@ void Achievements::LoadState(const u8* state_data, u32 state_data_size)
EndLoadingScreen(was_running_idle); EndLoadingScreen(was_running_idle);
} }
if (state_data_size == 0) if (data.empty())
{ {
// reset runtime, no data (state might've been created without cheevos) // reset runtime, no data (state might've been created without cheevos)
Console.Warning("State is missing cheevos data, resetting runtime"); Console.Warning("State is missing cheevos data, resetting runtime");
@ -1513,7 +1515,7 @@ void Achievements::LoadState(const u8* state_data, u32 state_data_size)
// These routines scare me a bit.. the data isn't bounds checked. // These routines scare me a bit.. the data isn't bounds checked.
// Really hope that nobody puts any thing malicious in a save state... // Really hope that nobody puts any thing malicious in a save state...
const int result = rc_client_deserialize_progress(s_client, state_data); const int result = rc_client_deserialize_progress_sized(s_client, data.data(), data.size());
if (result != RC_OK) if (result != RC_OK)
{ {
Console.Warning("Failed to deserialize cheevos state (%d), resetting", result); Console.Warning("Failed to deserialize cheevos state (%d), resetting", result);
@ -1521,10 +1523,8 @@ void Achievements::LoadState(const u8* state_data, u32 state_data_size)
} }
} }
std::vector<u8> Achievements::SaveState() void Achievements::SaveState(SaveStateBase& writer)
{ {
std::vector<u8> ret;
const auto lock = GetLock(); const auto lock = GetLock();
#ifdef ENABLE_RAINTEGRATION #ifdef ENABLE_RAINTEGRATION
@ -1533,16 +1533,18 @@ std::vector<u8> Achievements::SaveState()
const int size = RA_CaptureState(nullptr, 0); const int size = RA_CaptureState(nullptr, 0);
const u32 data_size = (size >= 0) ? static_cast<u32>(size) : 0; const u32 data_size = (size >= 0) ? static_cast<u32>(size) : 0;
ret.resize(data_size); if (data_size > 0)
const int result = RA_CaptureState(reinterpret_cast<char*>(ret.data()), static_cast<int>(data_size));
if (result != static_cast<int>(data_size))
{ {
Console.Warning("Failed to serialize cheevos state from RAIntegration."); writer.PrepBlock(static_cast<int>(data_size));
ret.clear();
const int result = RA_CaptureState(reinterpret_cast<char*>(writer.GetBlockPtr()), static_cast<int>(data_size));
if (result != static_cast<int>(data_size))
Console.Warning("Failed to serialize cheevos state from RAIntegration.");
else
writer.CommitBlock(static_cast<int>(data_size));
} }
return ret; return;
} }
#endif #endif
@ -1550,18 +1552,17 @@ std::vector<u8> Achievements::SaveState()
{ {
// internally this happens twice.. not great. // internally this happens twice.. not great.
const size_t data_size = rc_client_progress_size(s_client); const size_t data_size = rc_client_progress_size(s_client);
ret.resize(data_size); if (data_size > 0)
const int result = rc_client_serialize_progress(s_client, ret.data());
if (result != RC_OK)
{ {
// set data to zero, effectively serializing nothing writer.PrepBlock(static_cast<int>(data_size));
Console.Warning("Failed to serialize cheevos state (%d)", result);
ret.clear(); const int result = rc_client_serialize_progress_sized(s_client, writer.GetBlockPtr(), data_size);
if (result != RC_OK)
Console.Warning("Failed to serialize cheevos state (%d)", result);
else
writer.CommitBlock(static_cast<int>(data_size));
} }
} }
return ret;
} }

View File

@ -9,12 +9,15 @@
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <span>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
class Error; class Error;
class SaveStateBase;
namespace Achievements namespace Achievements
{ {
enum class LoginRequestReason enum class LoginRequestReason
@ -51,8 +54,8 @@ namespace Achievements
void IdleUpdate(); void IdleUpdate();
/// Saves/loads state. /// Saves/loads state.
void LoadState(const u8* state_data, u32 state_data_size); void LoadState(std::span<const u8> data);
std::vector<u8> SaveState(); void SaveState(SaveStateBase& writer);
/// Attempts to log in to RetroAchievements using the specified credentials. /// Attempts to log in to RetroAchievements using the specified credentials.
/// If the login is successful, the token returned by the server will be saved. /// If the login is successful, the token returned by the server will be saved.

View File

@ -656,10 +656,10 @@ class SaveStateEntry_Achievements final : public BaseSavestateEntry
if (zf) if (zf)
data = ReadBinaryFileInZip(zf); data = ReadBinaryFileInZip(zf);
if (data.has_value() && !data->empty()) if (data.has_value())
Achievements::LoadState(data->data(), data->size()); Achievements::LoadState(data.value());
else else
Achievements::LoadState(nullptr, 0); Achievements::LoadState(std::span<const u8>());
return true; return true;
} }
@ -669,14 +669,7 @@ class SaveStateEntry_Achievements final : public BaseSavestateEntry
if (!Achievements::IsActive()) if (!Achievements::IsActive())
return true; return true;
std::vector<u8> data(Achievements::SaveState()); Achievements::SaveState(writer);
if (!data.empty())
{
writer.PrepBlock(static_cast<int>(data.size()));
std::memcpy(writer.GetBlockPtr(), data.data(), data.size());
writer.CommitBlock(static_cast<int>(data.size()));
}
return writer.IsOkay(); return writer.IsOkay();
} }