mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-25 15:15:40 +00:00
System: Add save state compression mode options
This commit is contained in:
parent
70411783cd
commit
aeb9d383ce
|
@ -5184,9 +5184,14 @@ void FullscreenUI::DrawAdvancedSettingsPage()
|
||||||
FSUI_CSTR("Renames existing save states when saving to a backup file."), "Main",
|
FSUI_CSTR("Renames existing save states when saving to a backup file."), "Main",
|
||||||
"CreateSaveStateBackups", false);
|
"CreateSaveStateBackups", false);
|
||||||
DrawToggleSetting(
|
DrawToggleSetting(
|
||||||
bsi, FSUI_ICONSTR(ICON_FA_GAMEPAD, "Load Devices From Save States"),
|
bsi, FSUI_CSTR("Load Devices From Save States"),
|
||||||
FSUI_CSTR("When enabled, memory cards and controllers will be overwritten when save states are loaded."), "Main",
|
FSUI_CSTR("When enabled, memory cards and controllers will be overwritten when save states are loaded."), "Main",
|
||||||
"LoadDevicesFromSaveStates", false);
|
"LoadDevicesFromSaveStates", false);
|
||||||
|
DrawEnumSetting(bsi, FSUI_CSTR("Save State Compression"),
|
||||||
|
FSUI_CSTR("Reduces the size of save states by compressing the data before saving."), "Main",
|
||||||
|
"SaveStateCompression", Settings::DEFAULT_SAVE_STATE_COMPRESSION_MODE,
|
||||||
|
&Settings::ParseSaveStateCompressionModeName, &Settings::GetSaveStateCompressionModeName,
|
||||||
|
&Settings::GetSaveStateCompressionModeDisplayName, SaveStateCompressionMode::Count);
|
||||||
|
|
||||||
MenuHeading(FSUI_CSTR("Display Settings"));
|
MenuHeading(FSUI_CSTR("Display Settings"));
|
||||||
DrawToggleSetting(bsi, FSUI_CSTR("Show Status Indicators"),
|
DrawToggleSetting(bsi, FSUI_CSTR("Show Status Indicators"),
|
||||||
|
@ -7618,6 +7623,7 @@ TRANSLATE_NOOP("FullscreenUI", "Reduces \"wobbly\" polygons by attempting to pre
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Reduces hitches in emulation by reading/decompressing CD data asynchronously on a worker thread.");
|
TRANSLATE_NOOP("FullscreenUI", "Reduces hitches in emulation by reading/decompressing CD data asynchronously on a worker thread.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Reduces input latency by delaying the start of frame until closer to the presentation time.");
|
TRANSLATE_NOOP("FullscreenUI", "Reduces input latency by delaying the start of frame until closer to the presentation time.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Reduces polygon Z-fighting through depth testing. Low compatibility with games.");
|
TRANSLATE_NOOP("FullscreenUI", "Reduces polygon Z-fighting through depth testing. Low compatibility with games.");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Reduces the size of save states by compressing the data before saving.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Region");
|
TRANSLATE_NOOP("FullscreenUI", "Region");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Region: ");
|
TRANSLATE_NOOP("FullscreenUI", "Region: ");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Release Date: %s");
|
TRANSLATE_NOOP("FullscreenUI", "Release Date: %s");
|
||||||
|
@ -7661,6 +7667,7 @@ TRANSLATE_NOOP("FullscreenUI", "SDL DualShock 4 / DualSense Enhanced Mode");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Save Profile");
|
TRANSLATE_NOOP("FullscreenUI", "Save Profile");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Save Screenshot");
|
TRANSLATE_NOOP("FullscreenUI", "Save Screenshot");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Save State");
|
TRANSLATE_NOOP("FullscreenUI", "Save State");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Save State Compression");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Save State On Exit");
|
TRANSLATE_NOOP("FullscreenUI", "Save State On Exit");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Saved {:%c}");
|
TRANSLATE_NOOP("FullscreenUI", "Saved {:%c}");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Saves state periodically so you can rewind any mistakes while playing.");
|
TRANSLATE_NOOP("FullscreenUI", "Saves state periodically so you can rewind any mistakes while playing.");
|
||||||
|
|
|
@ -11,13 +11,6 @@ static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42;
|
||||||
|
|
||||||
static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION);
|
static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION);
|
||||||
|
|
||||||
enum class SaveStateCompression : u32
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
ZLib = 1,
|
|
||||||
ZStd = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma pack(push, 4)
|
#pragma pack(push, 4)
|
||||||
struct SAVE_STATE_HEADER
|
struct SAVE_STATE_HEADER
|
||||||
{
|
{
|
||||||
|
@ -28,6 +21,13 @@ struct SAVE_STATE_HEADER
|
||||||
MAX_SAVE_STATE_SIZE = 32 * 1024 * 1024,
|
MAX_SAVE_STATE_SIZE = 32 * 1024 * 1024,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class CompressionType : u32
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Deflate = 1,
|
||||||
|
Zstandard = 2,
|
||||||
|
};
|
||||||
|
|
||||||
u32 magic;
|
u32 magic;
|
||||||
u32 version;
|
u32 version;
|
||||||
char title[MAX_TITLE_LENGTH];
|
char title[MAX_TITLE_LENGTH];
|
||||||
|
|
|
@ -147,7 +147,6 @@ void Settings::Load(SettingsInterface& si)
|
||||||
pause_on_controller_disconnection = si.GetBoolValue("Main", "PauseOnControllerDisconnection", false);
|
pause_on_controller_disconnection = si.GetBoolValue("Main", "PauseOnControllerDisconnection", false);
|
||||||
save_state_on_exit = si.GetBoolValue("Main", "SaveStateOnExit", true);
|
save_state_on_exit = si.GetBoolValue("Main", "SaveStateOnExit", true);
|
||||||
create_save_state_backups = si.GetBoolValue("Main", "CreateSaveStateBackups", DEFAULT_SAVE_STATE_BACKUPS);
|
create_save_state_backups = si.GetBoolValue("Main", "CreateSaveStateBackups", DEFAULT_SAVE_STATE_BACKUPS);
|
||||||
compress_save_states = si.GetBoolValue("Main", "CompressSaveStates", DEFAULT_SAVE_STATE_COMPRESSION);
|
|
||||||
confim_power_off = si.GetBoolValue("Main", "ConfirmPowerOff", true);
|
confim_power_off = si.GetBoolValue("Main", "ConfirmPowerOff", true);
|
||||||
load_devices_from_save_states = si.GetBoolValue("Main", "LoadDevicesFromSaveStates", false);
|
load_devices_from_save_states = si.GetBoolValue("Main", "LoadDevicesFromSaveStates", false);
|
||||||
apply_compatibility_settings = si.GetBoolValue("Main", "ApplyCompatibilitySettings", true);
|
apply_compatibility_settings = si.GetBoolValue("Main", "ApplyCompatibilitySettings", true);
|
||||||
|
@ -311,6 +310,12 @@ void Settings::Load(SettingsInterface& si)
|
||||||
display_stretch_vertically = si.GetBoolValue("Display", "StretchVertically", false);
|
display_stretch_vertically = si.GetBoolValue("Display", "StretchVertically", false);
|
||||||
display_osd_scale = si.GetFloatValue("Display", "OSDScale", DEFAULT_OSD_SCALE);
|
display_osd_scale = si.GetFloatValue("Display", "OSDScale", DEFAULT_OSD_SCALE);
|
||||||
|
|
||||||
|
save_state_compression = ParseSaveStateCompressionModeName(
|
||||||
|
si.GetStringValue("Main", "SaveStateCompression",
|
||||||
|
GetSaveStateCompressionModeName(DEFAULT_SAVE_STATE_COMPRESSION_MODE))
|
||||||
|
.c_str())
|
||||||
|
.value_or(DEFAULT_SAVE_STATE_COMPRESSION_MODE);
|
||||||
|
|
||||||
cdrom_readahead_sectors =
|
cdrom_readahead_sectors =
|
||||||
static_cast<u8>(si.GetIntValue("CDROM", "ReadaheadSectors", DEFAULT_CDROM_READAHEAD_SECTORS));
|
static_cast<u8>(si.GetIntValue("CDROM", "ReadaheadSectors", DEFAULT_CDROM_READAHEAD_SECTORS));
|
||||||
cdrom_mechacon_version =
|
cdrom_mechacon_version =
|
||||||
|
@ -462,7 +467,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
|
||||||
si.SetBoolValue("Main", "PauseOnControllerDisconnection", pause_on_controller_disconnection);
|
si.SetBoolValue("Main", "PauseOnControllerDisconnection", pause_on_controller_disconnection);
|
||||||
si.SetBoolValue("Main", "SaveStateOnExit", save_state_on_exit);
|
si.SetBoolValue("Main", "SaveStateOnExit", save_state_on_exit);
|
||||||
si.SetBoolValue("Main", "CreateSaveStateBackups", create_save_state_backups);
|
si.SetBoolValue("Main", "CreateSaveStateBackups", create_save_state_backups);
|
||||||
si.SetBoolValue("Main", "CompressSaveStates", compress_save_states);
|
si.SetStringValue("Main", "SaveStateCompression", GetSaveStateCompressionModeName(save_state_compression));
|
||||||
si.SetBoolValue("Main", "ConfirmPowerOff", confim_power_off);
|
si.SetBoolValue("Main", "ConfirmPowerOff", confim_power_off);
|
||||||
si.SetBoolValue("Main", "EnableDiscordPresence", enable_discord_presence);
|
si.SetBoolValue("Main", "EnableDiscordPresence", enable_discord_presence);
|
||||||
}
|
}
|
||||||
|
@ -1764,6 +1769,43 @@ const char* Settings::GetCDROMMechVersionDisplayName(CDROMMechaconVersion mode)
|
||||||
return s_mechacon_version_display_names[static_cast<size_t>(mode)];
|
return s_mechacon_version_display_names[static_cast<size_t>(mode)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr const std::array s_save_state_compression_mode_names = {
|
||||||
|
"Uncompressed", "DeflateLow", "DeflateDefault", "DeflateHigh", "ZstLow", "ZstDefault", "ZstHigh",
|
||||||
|
};
|
||||||
|
static constexpr const std::array s_save_state_compression_mode_display_names = {
|
||||||
|
TRANSLATE_NOOP("Settings", "Uncompressed"), TRANSLATE_NOOP("Settings", "Deflate (Low)"),
|
||||||
|
TRANSLATE_NOOP("Settings", "Deflate (Default)"), TRANSLATE_NOOP("Settings", "Deflate (High)"),
|
||||||
|
TRANSLATE_NOOP("Settings", "Zstandard (Low)"), TRANSLATE_NOOP("Settings", "Zstandard (Default)"),
|
||||||
|
TRANSLATE_NOOP("Settings", "Zstandard (High)"),
|
||||||
|
};
|
||||||
|
static_assert(s_save_state_compression_mode_names.size() == static_cast<size_t>(SaveStateCompressionMode::Count));
|
||||||
|
static_assert(s_save_state_compression_mode_display_names.size() ==
|
||||||
|
static_cast<size_t>(SaveStateCompressionMode::Count));
|
||||||
|
|
||||||
|
std::optional<SaveStateCompressionMode> Settings::ParseSaveStateCompressionModeName(const char* str)
|
||||||
|
{
|
||||||
|
u32 index = 0;
|
||||||
|
for (const char* name : s_save_state_compression_mode_names)
|
||||||
|
{
|
||||||
|
if (StringUtil::Strcasecmp(name, str) == 0)
|
||||||
|
return static_cast<SaveStateCompressionMode>(index);
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Settings::GetSaveStateCompressionModeName(SaveStateCompressionMode mode)
|
||||||
|
{
|
||||||
|
return s_save_state_compression_mode_names[static_cast<size_t>(mode)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Settings::GetSaveStateCompressionModeDisplayName(SaveStateCompressionMode mode)
|
||||||
|
{
|
||||||
|
return Host::TranslateToCString("Settings", s_save_state_compression_mode_display_names[static_cast<size_t>(mode)]);
|
||||||
|
}
|
||||||
|
|
||||||
std::string EmuFolders::AppRoot;
|
std::string EmuFolders::AppRoot;
|
||||||
std::string EmuFolders::DataRoot;
|
std::string EmuFolders::DataRoot;
|
||||||
std::string EmuFolders::Bios;
|
std::string EmuFolders::Bios;
|
||||||
|
|
|
@ -63,14 +63,14 @@ struct Settings
|
||||||
ConsoleRegion region = DEFAULT_CONSOLE_REGION;
|
ConsoleRegion region = DEFAULT_CONSOLE_REGION;
|
||||||
|
|
||||||
CPUExecutionMode cpu_execution_mode = DEFAULT_CPU_EXECUTION_MODE;
|
CPUExecutionMode cpu_execution_mode = DEFAULT_CPU_EXECUTION_MODE;
|
||||||
u32 cpu_overclock_numerator = 1;
|
CPUFastmemMode cpu_fastmem_mode = DEFAULT_CPU_FASTMEM_MODE;
|
||||||
u32 cpu_overclock_denominator = 1;
|
|
||||||
bool cpu_overclock_enable : 1 = false;
|
bool cpu_overclock_enable : 1 = false;
|
||||||
bool cpu_overclock_active : 1 = false;
|
bool cpu_overclock_active : 1 = false;
|
||||||
bool cpu_recompiler_memory_exceptions : 1 = false;
|
bool cpu_recompiler_memory_exceptions : 1 = false;
|
||||||
bool cpu_recompiler_block_linking : 1 = true;
|
bool cpu_recompiler_block_linking : 1 = true;
|
||||||
bool cpu_recompiler_icache : 1 = false;
|
bool cpu_recompiler_icache : 1 = false;
|
||||||
CPUFastmemMode cpu_fastmem_mode = DEFAULT_CPU_FASTMEM_MODE;
|
u32 cpu_overclock_numerator = 1;
|
||||||
|
u32 cpu_overclock_denominator = 1;
|
||||||
|
|
||||||
float emulation_speed = 1.0f;
|
float emulation_speed = 1.0f;
|
||||||
float fast_forward_speed = 0.0f;
|
float fast_forward_speed = 0.0f;
|
||||||
|
@ -84,7 +84,6 @@ struct Settings
|
||||||
bool pause_on_controller_disconnection : 1 = false;
|
bool pause_on_controller_disconnection : 1 = false;
|
||||||
bool save_state_on_exit : 1 = true;
|
bool save_state_on_exit : 1 = true;
|
||||||
bool create_save_state_backups : 1 = DEFAULT_SAVE_STATE_BACKUPS;
|
bool create_save_state_backups : 1 = DEFAULT_SAVE_STATE_BACKUPS;
|
||||||
bool compress_save_states : 1 = DEFAULT_SAVE_STATE_COMPRESSION;
|
|
||||||
bool confim_power_off : 1 = true;
|
bool confim_power_off : 1 = true;
|
||||||
bool load_devices_from_save_states : 1 = false;
|
bool load_devices_from_save_states : 1 = false;
|
||||||
bool apply_compatibility_settings : 1 = true;
|
bool apply_compatibility_settings : 1 = true;
|
||||||
|
@ -180,6 +179,8 @@ struct Settings
|
||||||
float gpu_pgxp_tolerance = -1.0f;
|
float gpu_pgxp_tolerance = -1.0f;
|
||||||
float gpu_pgxp_depth_clear_threshold = DEFAULT_GPU_PGXP_DEPTH_THRESHOLD / GPU_PGXP_DEPTH_THRESHOLD_SCALE;
|
float gpu_pgxp_depth_clear_threshold = DEFAULT_GPU_PGXP_DEPTH_THRESHOLD / GPU_PGXP_DEPTH_THRESHOLD_SCALE;
|
||||||
|
|
||||||
|
SaveStateCompressionMode save_state_compression = DEFAULT_SAVE_STATE_COMPRESSION_MODE;
|
||||||
|
|
||||||
u8 cdrom_readahead_sectors = DEFAULT_CDROM_READAHEAD_SECTORS;
|
u8 cdrom_readahead_sectors = DEFAULT_CDROM_READAHEAD_SECTORS;
|
||||||
CDROMMechaconVersion cdrom_mechacon_version = DEFAULT_CDROM_MECHACON_VERSION;
|
CDROMMechaconVersion cdrom_mechacon_version = DEFAULT_CDROM_MECHACON_VERSION;
|
||||||
bool cdrom_region_check : 1 = false;
|
bool cdrom_region_check : 1 = false;
|
||||||
|
@ -459,6 +460,10 @@ struct Settings
|
||||||
static const char* GetCDROMMechVersionName(CDROMMechaconVersion mode);
|
static const char* GetCDROMMechVersionName(CDROMMechaconVersion mode);
|
||||||
static const char* GetCDROMMechVersionDisplayName(CDROMMechaconVersion mode);
|
static const char* GetCDROMMechVersionDisplayName(CDROMMechaconVersion mode);
|
||||||
|
|
||||||
|
static std::optional<SaveStateCompressionMode> ParseSaveStateCompressionModeName(const char* str);
|
||||||
|
static const char* GetSaveStateCompressionModeName(SaveStateCompressionMode mode);
|
||||||
|
static const char* GetSaveStateCompressionModeDisplayName(SaveStateCompressionMode mode);
|
||||||
|
|
||||||
static constexpr GPURenderer DEFAULT_GPU_RENDERER = GPURenderer::Automatic;
|
static constexpr GPURenderer DEFAULT_GPU_RENDERER = GPURenderer::Automatic;
|
||||||
static constexpr GPUTextureFilter DEFAULT_GPU_TEXTURE_FILTER = GPUTextureFilter::Nearest;
|
static constexpr GPUTextureFilter DEFAULT_GPU_TEXTURE_FILTER = GPUTextureFilter::Nearest;
|
||||||
static constexpr GPULineDetectMode DEFAULT_GPU_LINE_DETECT_MODE = GPULineDetectMode::Disabled;
|
static constexpr GPULineDetectMode DEFAULT_GPU_LINE_DETECT_MODE = GPULineDetectMode::Disabled;
|
||||||
|
@ -510,7 +515,7 @@ struct Settings
|
||||||
|
|
||||||
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
|
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
|
||||||
|
|
||||||
static constexpr bool DEFAULT_SAVE_STATE_COMPRESSION = true;
|
static constexpr SaveStateCompressionMode DEFAULT_SAVE_STATE_COMPRESSION_MODE = SaveStateCompressionMode::ZstDefault;
|
||||||
|
|
||||||
// Enable console logging by default on Linux platforms.
|
// Enable console logging by default on Linux platforms.
|
||||||
#if defined(__linux__) && !defined(__ANDROID__)
|
#if defined(__linux__) && !defined(__ANDROID__)
|
||||||
|
|
|
@ -190,12 +190,12 @@ static bool LoadStateFromBuffer(const SaveStateBuffer& buffer, Error* error, boo
|
||||||
static bool LoadStateBufferFromFile(SaveStateBuffer* buffer, std::FILE* fp, Error* error, bool read_title,
|
static bool LoadStateBufferFromFile(SaveStateBuffer* buffer, std::FILE* fp, Error* error, bool read_title,
|
||||||
bool read_media_path, bool read_screenshot, bool read_data);
|
bool read_media_path, bool read_screenshot, bool read_data);
|
||||||
static bool ReadAndDecompressStateData(std::FILE* fp, std::span<u8> dst, u32 file_offset, u32 compressed_size,
|
static bool ReadAndDecompressStateData(std::FILE* fp, std::span<u8> dst, u32 file_offset, u32 compressed_size,
|
||||||
SaveStateCompression method, Error* error);
|
SAVE_STATE_HEADER::CompressionType method, Error* error);
|
||||||
static bool SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screenshot_size = 256);
|
static bool SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screenshot_size = 256);
|
||||||
static bool SaveStateBufferToFile(const SaveStateBuffer& buffer, std::FILE* fp, Error* error,
|
static bool SaveStateBufferToFile(const SaveStateBuffer& buffer, std::FILE* fp, Error* error,
|
||||||
SaveStateCompression screenshot_compression = SaveStateCompression::ZLib,
|
SaveStateCompressionMode compression_mode);
|
||||||
SaveStateCompression data_compression = SaveStateCompression::ZStd);
|
static u32 CompressAndWriteStateData(std::FILE* fp, std::span<const u8> src, SaveStateCompressionMode method,
|
||||||
static u32 CompressAndWriteStateData(std::FILE* fp, std::span<const u8> src, SaveStateCompression method, Error* error);
|
u32* header_type, Error* error);
|
||||||
static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state);
|
static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state);
|
||||||
|
|
||||||
static void SetRewinding(bool enabled);
|
static void SetRewinding(bool enabled);
|
||||||
|
@ -2699,9 +2699,9 @@ bool System::LoadStateBufferFromFile(SaveStateBuffer* buffer, std::FILE* fp, Err
|
||||||
buffer->screenshot.SetSize(header.screenshot_width, header.screenshot_height);
|
buffer->screenshot.SetSize(header.screenshot_width, header.screenshot_height);
|
||||||
const u32 uncompressed_size = buffer->screenshot.GetPitch() * buffer->screenshot.GetHeight();
|
const u32 uncompressed_size = buffer->screenshot.GetPitch() * buffer->screenshot.GetHeight();
|
||||||
const u32 compressed_size = (header.version >= 69) ? header.screenshot_compressed_size : uncompressed_size;
|
const u32 compressed_size = (header.version >= 69) ? header.screenshot_compressed_size : uncompressed_size;
|
||||||
const SaveStateCompression compression_type =
|
const SAVE_STATE_HEADER::CompressionType compression_type =
|
||||||
(header.version >= 69) ? static_cast<SaveStateCompression>(header.screenshot_compression_type) :
|
(header.version >= 69) ? static_cast<SAVE_STATE_HEADER::CompressionType>(header.screenshot_compression_type) :
|
||||||
SaveStateCompression::None;
|
SAVE_STATE_HEADER::CompressionType::None;
|
||||||
if (!ReadAndDecompressStateData(
|
if (!ReadAndDecompressStateData(
|
||||||
fp, std::span<u8>(reinterpret_cast<u8*>(buffer->screenshot.GetPixels()), uncompressed_size),
|
fp, std::span<u8>(reinterpret_cast<u8*>(buffer->screenshot.GetPixels()), uncompressed_size),
|
||||||
header.offset_to_screenshot, compressed_size, compression_type, error)) [[unlikely]]
|
header.offset_to_screenshot, compressed_size, compression_type, error)) [[unlikely]]
|
||||||
|
@ -2716,8 +2716,8 @@ bool System::LoadStateBufferFromFile(SaveStateBuffer* buffer, std::FILE* fp, Err
|
||||||
buffer->state_data.resize(header.data_uncompressed_size);
|
buffer->state_data.resize(header.data_uncompressed_size);
|
||||||
buffer->state_size = header.data_uncompressed_size;
|
buffer->state_size = header.data_uncompressed_size;
|
||||||
if (!ReadAndDecompressStateData(fp, buffer->state_data.span(), header.offset_to_data, header.data_compressed_size,
|
if (!ReadAndDecompressStateData(fp, buffer->state_data.span(), header.offset_to_data, header.data_compressed_size,
|
||||||
static_cast<SaveStateCompression>(header.data_compression_type), error))
|
static_cast<SAVE_STATE_HEADER::CompressionType>(header.data_compression_type),
|
||||||
[[unlikely]]
|
error)) [[unlikely]]
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2727,12 +2727,12 @@ bool System::LoadStateBufferFromFile(SaveStateBuffer* buffer, std::FILE* fp, Err
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::ReadAndDecompressStateData(std::FILE* fp, std::span<u8> dst, u32 file_offset, u32 compressed_size,
|
bool System::ReadAndDecompressStateData(std::FILE* fp, std::span<u8> dst, u32 file_offset, u32 compressed_size,
|
||||||
SaveStateCompression method, Error* error)
|
SAVE_STATE_HEADER::CompressionType method, Error* error)
|
||||||
{
|
{
|
||||||
if (!FileSystem::FSeek64(fp, file_offset, SEEK_SET, error))
|
if (!FileSystem::FSeek64(fp, file_offset, SEEK_SET, error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (method == SaveStateCompression::None)
|
if (method == SAVE_STATE_HEADER::CompressionType::None)
|
||||||
{
|
{
|
||||||
// Feed through.
|
// Feed through.
|
||||||
if (std::fread(dst.data(), dst.size(), 1, fp) != 1) [[unlikely]]
|
if (std::fread(dst.data(), dst.size(), 1, fp) != 1) [[unlikely]]
|
||||||
|
@ -2751,7 +2751,7 @@ bool System::ReadAndDecompressStateData(std::FILE* fp, std::span<u8> dst, u32 fi
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method == SaveStateCompression::ZLib)
|
if (method == SAVE_STATE_HEADER::CompressionType::Deflate)
|
||||||
{
|
{
|
||||||
uLong source_len = compressed_size;
|
uLong source_len = compressed_size;
|
||||||
uLong dest_len = static_cast<uLong>(dst.size());
|
uLong dest_len = static_cast<uLong>(dst.size());
|
||||||
|
@ -2772,7 +2772,7 @@ bool System::ReadAndDecompressStateData(std::FILE* fp, std::span<u8> dst, u32 fi
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (method == SaveStateCompression::ZStd)
|
else if (method == SAVE_STATE_HEADER::CompressionType::Zstandard)
|
||||||
{
|
{
|
||||||
const size_t result = ZSTD_decompress(dst.data(), dst.size(), compressed_data.data(), compressed_size);
|
const size_t result = ZSTD_decompress(dst.data(), dst.size(), compressed_data.data(), compressed_size);
|
||||||
if (ZSTD_isError(result)) [[unlikely]]
|
if (ZSTD_isError(result)) [[unlikely]]
|
||||||
|
@ -2832,9 +2832,7 @@ bool System::SaveState(const char* path, Error* error, bool backup_existing_save
|
||||||
|
|
||||||
INFO_LOG("Saving state to '{}'...", path);
|
INFO_LOG("Saving state to '{}'...", path);
|
||||||
|
|
||||||
const SaveStateCompression compression =
|
if (!SaveStateBufferToFile(buffer, fp.get(), error, g_settings.save_state_compression))
|
||||||
g_settings.compress_save_states ? SaveStateCompression::ZStd : SaveStateCompression::None;
|
|
||||||
if (!SaveStateBufferToFile(buffer, fp.get(), error, compression, compression))
|
|
||||||
{
|
{
|
||||||
FileSystem::DiscardAtomicRenamedFile(fp);
|
FileSystem::DiscardAtomicRenamedFile(fp);
|
||||||
return false;
|
return false;
|
||||||
|
@ -2927,7 +2925,7 @@ bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screen
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::SaveStateBufferToFile(const SaveStateBuffer& buffer, std::FILE* fp, Error* error,
|
bool System::SaveStateBufferToFile(const SaveStateBuffer& buffer, std::FILE* fp, Error* error,
|
||||||
SaveStateCompression screenshot_compression, SaveStateCompression data_compression)
|
SaveStateCompressionMode compression)
|
||||||
{
|
{
|
||||||
// Header gets rewritten below.
|
// Header gets rewritten below.
|
||||||
SAVE_STATE_HEADER header = {};
|
SAVE_STATE_HEADER header = {};
|
||||||
|
@ -2961,7 +2959,6 @@ bool System::SaveStateBufferToFile(const SaveStateBuffer& buffer, std::FILE* fp,
|
||||||
if (buffer.screenshot.IsValid())
|
if (buffer.screenshot.IsValid())
|
||||||
{
|
{
|
||||||
DebugAssert(FileSystem::FTell64(fp) == static_cast<s64>(file_position));
|
DebugAssert(FileSystem::FTell64(fp) == static_cast<s64>(file_position));
|
||||||
header.screenshot_compression_type = static_cast<u32>(screenshot_compression);
|
|
||||||
header.screenshot_width = buffer.screenshot.GetWidth();
|
header.screenshot_width = buffer.screenshot.GetWidth();
|
||||||
header.screenshot_height = buffer.screenshot.GetHeight();
|
header.screenshot_height = buffer.screenshot.GetHeight();
|
||||||
header.offset_to_screenshot = file_position;
|
header.offset_to_screenshot = file_position;
|
||||||
|
@ -2969,7 +2966,7 @@ bool System::SaveStateBufferToFile(const SaveStateBuffer& buffer, std::FILE* fp,
|
||||||
CompressAndWriteStateData(fp,
|
CompressAndWriteStateData(fp,
|
||||||
std::span<const u8>(reinterpret_cast<const u8*>(buffer.screenshot.GetPixels()),
|
std::span<const u8>(reinterpret_cast<const u8*>(buffer.screenshot.GetPixels()),
|
||||||
buffer.screenshot.GetPitch() * buffer.screenshot.GetHeight()),
|
buffer.screenshot.GetPitch() * buffer.screenshot.GetHeight()),
|
||||||
screenshot_compression, error);
|
compression, &header.screenshot_compression_type, error);
|
||||||
if (header.screenshot_compressed_size == 0)
|
if (header.screenshot_compressed_size == 0)
|
||||||
return false;
|
return false;
|
||||||
file_position += header.screenshot_compressed_size;
|
file_position += header.screenshot_compressed_size;
|
||||||
|
@ -2977,10 +2974,9 @@ bool System::SaveStateBufferToFile(const SaveStateBuffer& buffer, std::FILE* fp,
|
||||||
|
|
||||||
DebugAssert(buffer.state_size > 0);
|
DebugAssert(buffer.state_size > 0);
|
||||||
header.offset_to_data = file_position;
|
header.offset_to_data = file_position;
|
||||||
header.data_compression_type = static_cast<u32>(data_compression);
|
|
||||||
header.data_uncompressed_size = static_cast<u32>(buffer.state_size);
|
header.data_uncompressed_size = static_cast<u32>(buffer.state_size);
|
||||||
header.data_compressed_size =
|
header.data_compressed_size = CompressAndWriteStateData(fp, buffer.state_data.cspan(0, buffer.state_size),
|
||||||
CompressAndWriteStateData(fp, buffer.state_data.cspan(0, buffer.state_size), data_compression, error);
|
compression, &header.data_compression_type, error);
|
||||||
if (header.data_compressed_size == 0)
|
if (header.data_compressed_size == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -3001,9 +2997,10 @@ bool System::SaveStateBufferToFile(const SaveStateBuffer& buffer, std::FILE* fp,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 System::CompressAndWriteStateData(std::FILE* fp, std::span<const u8> src, SaveStateCompression method, Error* error)
|
u32 System::CompressAndWriteStateData(std::FILE* fp, std::span<const u8> src, SaveStateCompressionMode method,
|
||||||
|
u32* header_type, Error* error)
|
||||||
{
|
{
|
||||||
if (method == SaveStateCompression::None)
|
if (method == SaveStateCompressionMode::Uncompressed)
|
||||||
{
|
{
|
||||||
if (std::fwrite(src.data(), src.size(), 1, fp) != 1) [[unlikely]]
|
if (std::fwrite(src.data(), src.size(), 1, fp) != 1) [[unlikely]]
|
||||||
{
|
{
|
||||||
|
@ -3011,33 +3008,40 @@ u32 System::CompressAndWriteStateData(std::FILE* fp, std::span<const u8> src, Sa
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*header_type = static_cast<u32>(SAVE_STATE_HEADER::CompressionType::None);
|
||||||
return static_cast<u32>(src.size());
|
return static_cast<u32>(src.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicHeapArray<u8> buffer;
|
DynamicHeapArray<u8> buffer;
|
||||||
u32 write_size;
|
u32 write_size;
|
||||||
if (method == SaveStateCompression::ZLib)
|
if (method >= SaveStateCompressionMode::DeflateLow && method <= SaveStateCompressionMode::DeflateHigh)
|
||||||
{
|
{
|
||||||
const size_t buffer_size = compressBound(static_cast<uLong>(src.size()));
|
const size_t buffer_size = compressBound(static_cast<uLong>(src.size()));
|
||||||
buffer.resize(buffer_size);
|
buffer.resize(buffer_size);
|
||||||
|
|
||||||
uLongf compressed_size = static_cast<uLongf>(buffer_size);
|
uLongf compressed_size = static_cast<uLongf>(buffer_size);
|
||||||
const int err =
|
const int level =
|
||||||
compress2(buffer.data(), &compressed_size, src.data(), static_cast<uLong>(src.size()), Z_DEFAULT_COMPRESSION);
|
((method == SaveStateCompressionMode::DeflateLow) ?
|
||||||
|
Z_BEST_SPEED :
|
||||||
|
((method == SaveStateCompressionMode::DeflateHigh) ? Z_BEST_COMPRESSION : Z_DEFAULT_COMPRESSION));
|
||||||
|
const int err = compress2(buffer.data(), &compressed_size, src.data(), static_cast<uLong>(src.size()), level);
|
||||||
if (err != Z_OK) [[unlikely]]
|
if (err != Z_OK) [[unlikely]]
|
||||||
{
|
{
|
||||||
Error::SetStringFmt(error, "compress2() failed: {}", err);
|
Error::SetStringFmt(error, "compress2() failed: {}", err);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*header_type = static_cast<u32>(SAVE_STATE_HEADER::CompressionType::Deflate);
|
||||||
write_size = static_cast<u32>(compressed_size);
|
write_size = static_cast<u32>(compressed_size);
|
||||||
}
|
}
|
||||||
else if (method == SaveStateCompression::ZStd)
|
else if (method >= SaveStateCompressionMode::ZstLow && method <= SaveStateCompressionMode::ZstHigh)
|
||||||
{
|
{
|
||||||
const size_t buffer_size = ZSTD_compressBound(src.size());
|
const size_t buffer_size = ZSTD_compressBound(src.size());
|
||||||
buffer.resize(buffer_size);
|
buffer.resize(buffer_size);
|
||||||
|
|
||||||
const size_t compressed_size = ZSTD_compress(buffer.data(), buffer_size, src.data(), src.size(), 0);
|
const int level =
|
||||||
|
((method == SaveStateCompressionMode::ZstLow) ? 1 : ((method == SaveStateCompressionMode::ZstHigh) ? 19 : 0));
|
||||||
|
const size_t compressed_size = ZSTD_compress(buffer.data(), buffer_size, src.data(), src.size(), level);
|
||||||
if (ZSTD_isError(compressed_size)) [[unlikely]]
|
if (ZSTD_isError(compressed_size)) [[unlikely]]
|
||||||
{
|
{
|
||||||
const char* errstr = ZSTD_getErrorString(ZSTD_getErrorCode(compressed_size));
|
const char* errstr = ZSTD_getErrorString(ZSTD_getErrorCode(compressed_size));
|
||||||
|
@ -3045,6 +3049,7 @@ u32 System::CompressAndWriteStateData(std::FILE* fp, std::span<const u8> src, Sa
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*header_type = static_cast<u32>(SAVE_STATE_HEADER::CompressionType::Zstandard);
|
||||||
write_size = static_cast<u32>(compressed_size);
|
write_size = static_cast<u32>(compressed_size);
|
||||||
}
|
}
|
||||||
else [[unlikely]]
|
else [[unlikely]]
|
||||||
|
|
|
@ -235,7 +235,7 @@ enum : u32
|
||||||
NUM_MULTITAPS = 2
|
NUM_MULTITAPS = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class CPUFastmemMode
|
enum class CPUFastmemMode : u8
|
||||||
{
|
{
|
||||||
Disabled,
|
Disabled,
|
||||||
MMap,
|
MMap,
|
||||||
|
@ -261,3 +261,16 @@ enum class CDROMMechaconVersion : u8
|
||||||
|
|
||||||
Count,
|
Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class SaveStateCompressionMode : u8
|
||||||
|
{
|
||||||
|
Uncompressed,
|
||||||
|
DeflateLow,
|
||||||
|
DeflateDefault,
|
||||||
|
DeflateHigh,
|
||||||
|
ZstLow,
|
||||||
|
ZstDefault,
|
||||||
|
ZstHigh,
|
||||||
|
|
||||||
|
Count,
|
||||||
|
};
|
||||||
|
|
|
@ -222,8 +222,11 @@ void AdvancedSettingsWidget::addTweakOptions()
|
||||||
"IncreaseTimerResolution", true);
|
"IncreaseTimerResolution", true);
|
||||||
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Load Devices From Save States"), "Main",
|
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Load Devices From Save States"), "Main",
|
||||||
"LoadDevicesFromSaveStates", false);
|
"LoadDevicesFromSaveStates", false);
|
||||||
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Compress Save States"), "Main", "CompressSaveStates",
|
addChoiceTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Save State Compression"), "Main", "SaveStateCompression",
|
||||||
Settings::DEFAULT_SAVE_STATE_COMPRESSION);
|
&Settings::ParseSaveStateCompressionModeName, &Settings::GetSaveStateCompressionModeName,
|
||||||
|
&Settings::GetSaveStateCompressionModeDisplayName,
|
||||||
|
static_cast<u32>(SaveStateCompressionMode::Count),
|
||||||
|
Settings::DEFAULT_SAVE_STATE_COMPRESSION_MODE);
|
||||||
|
|
||||||
if (m_dialog->isPerGameSettings())
|
if (m_dialog->isPerGameSettings())
|
||||||
{
|
{
|
||||||
|
@ -284,7 +287,8 @@ void AdvancedSettingsWidget::onResetToDefaultClicked()
|
||||||
setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Apply compatibility settings
|
setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Apply compatibility settings
|
||||||
setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Increase Timer Resolution
|
setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Increase Timer Resolution
|
||||||
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Load Devices From Save States
|
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Load Devices From Save States
|
||||||
setBooleanTweakOption(m_ui.tweakOptionTable, i++, Settings::DEFAULT_SAVE_STATE_COMPRESSION); // Compress Save States
|
setChoiceTweakOption(m_ui.tweakOptionTable, i++,
|
||||||
|
Settings::DEFAULT_SAVE_STATE_COMPRESSION_MODE); // Save State Compression
|
||||||
setIntRangeTweakOption(m_ui.tweakOptionTable, i++,
|
setIntRangeTweakOption(m_ui.tweakOptionTable, i++,
|
||||||
static_cast<int>(Settings::DEFAULT_DMA_MAX_SLICE_TICKS)); // DMA max slice ticks
|
static_cast<int>(Settings::DEFAULT_DMA_MAX_SLICE_TICKS)); // DMA max slice ticks
|
||||||
setIntRangeTweakOption(m_ui.tweakOptionTable, i++,
|
setIntRangeTweakOption(m_ui.tweakOptionTable, i++,
|
||||||
|
|
Loading…
Reference in a new issue