System: Add XZ save state compression

For when space is really at a premium.

Also use CompressHelpers to reduce duplication.
pull/3528/head
Stenzek 2 months ago
parent bbd150823c
commit 1ef1a2d3f4
No known key found for this signature in database

@ -26,6 +26,7 @@ struct SAVE_STATE_HEADER
None = 0,
Deflate = 1,
Zstandard = 2,
XZ = 3,
};
u32 magic;

@ -2376,7 +2376,8 @@ const char* Settings::GetCDROMMechVersionDisplayName(CDROMMechaconVersion mode)
}
static constexpr const std::array s_save_state_compression_mode_names = {
"Uncompressed", "DeflateLow", "DeflateDefault", "DeflateHigh", "ZstLow", "ZstDefault", "ZstHigh",
"Uncompressed", "DeflateLow", "DeflateDefault", "DeflateHigh", "ZstLow",
"ZstDefault", "ZstHigh", "XZLow", "XZDefault", "XZHigh",
};
static constexpr const std::array s_save_state_compression_mode_display_names = {
TRANSLATE_DISAMBIG_NOOP("Settings", "Uncompressed", "SaveStateCompressionMode"),
@ -2386,6 +2387,9 @@ static constexpr const std::array s_save_state_compression_mode_display_names =
TRANSLATE_DISAMBIG_NOOP("Settings", "Zstandard (Low)", "SaveStateCompressionMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Zstandard (Default)", "SaveStateCompressionMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Zstandard (High)", "SaveStateCompressionMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "XZ (Low)", "SaveStateCompressionMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "XZ (Default)", "SaveStateCompressionMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "XZ (High)", "SaveStateCompressionMode"),
};
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() ==

@ -43,6 +43,7 @@
#include "util/audio_stream.h"
#include "util/cd_image.h"
#include "util/compress_helpers.h"
#include "util/gpu_device.h"
#include "util/imgui_manager.h"
#include "util/ini_settings_interface.h"
@ -84,9 +85,6 @@
#include <cstdio>
#include <limits>
#include <thread>
#include <zlib.h>
#include <zstd.h>
#include <zstd_errors.h>
LOG_CHANNEL(System);
@ -3145,49 +3143,32 @@ bool System::ReadAndDecompressStateData(std::FILE* fp, std::span<u8> dst, u32 fi
return false;
}
if (method == SAVE_STATE_HEADER::CompressionType::Deflate)
CompressHelpers::CompressType type;
switch (method)
{
uLong source_len = compressed_size;
uLong dest_len = static_cast<uLong>(dst.size());
const int err = uncompress2(dst.data(), &dest_len, compressed_data.data(), &source_len);
if (err != Z_OK) [[unlikely]]
{
Error::SetStringFmt(error, "uncompress2() failed: ", err);
return false;
}
else if (dest_len < dst.size()) [[unlikely]]
{
Error::SetStringFmt(error, "Only decompressed {} of {} bytes", dest_len, dst.size());
return false;
}
case SAVE_STATE_HEADER::CompressionType::Deflate:
type = CompressHelpers::CompressType::Deflate;
break;
if (source_len < compressed_size) [[unlikely]]
WARNING_LOG("Only consumed {} of {} compressed bytes", source_len, compressed_size);
case SAVE_STATE_HEADER::CompressionType::Zstandard:
type = CompressHelpers::CompressType::Zstandard;
break;
return true;
}
else if (method == SAVE_STATE_HEADER::CompressionType::Zstandard)
{
const size_t result = ZSTD_decompress(dst.data(), dst.size(), compressed_data.data(), compressed_size);
if (ZSTD_isError(result)) [[unlikely]]
{
const char* errstr = ZSTD_getErrorString(ZSTD_getErrorCode(result));
Error::SetStringFmt(error, "ZSTD_decompress() failed: {}", errstr ? errstr : "<unknown>");
return false;
}
else if (result < dst.size())
{
Error::SetStringFmt(error, "Only decompressed {} of {} bytes", result, dst.size());
return false;
}
case SAVE_STATE_HEADER::CompressionType::XZ:
type = CompressHelpers::CompressType::XZ;
break;
return true;
default:
Error::SetStringFmt(error, "Unknown compression method {}", static_cast<u32>(method));
return false;
}
else [[unlikely]]
{
Error::SetStringView(error, "Unknown method.");
const std::optional<size_t> decompressed_size =
CompressHelpers::DecompressBuffer(dst, type, compressed_data.cspan(), dst.size(), error);
if (!decompressed_size.has_value() || decompressed_size.value() != dst.size())
return false;
}
return true;
}
bool System::SaveState(std::string path, Error* error, bool backup_existing_save, bool ignore_memcard_busy)
@ -3443,59 +3424,48 @@ u32 System::CompressAndWriteStateData(std::FILE* fp, std::span<const u8> src, Sa
return static_cast<u32>(src.size());
}
DynamicHeapArray<u8> buffer;
u32 write_size;
CompressHelpers::CompressType ctype;
int clevel;
if (method >= SaveStateCompressionMode::DeflateLow && method <= SaveStateCompressionMode::DeflateHigh)
{
const size_t buffer_size = compressBound(static_cast<uLong>(src.size()));
buffer.resize(buffer_size);
uLongf compressed_size = static_cast<uLongf>(buffer_size);
const int level =
((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]]
{
Error::SetStringFmt(error, "compress2() failed: {}", err);
return 0;
}
ctype = CompressHelpers::CompressType::Deflate;
*header_type = static_cast<u32>(SAVE_STATE_HEADER::CompressionType::Deflate);
write_size = static_cast<u32>(compressed_size);
clevel =
((method == SaveStateCompressionMode::DeflateLow) ? 1 :
((method == SaveStateCompressionMode::DeflateHigh) ? 9 : -1));
}
else if (method >= SaveStateCompressionMode::ZstLow && method <= SaveStateCompressionMode::ZstHigh)
{
const size_t buffer_size = ZSTD_compressBound(src.size());
buffer.resize(buffer_size);
const int level =
((method == SaveStateCompressionMode::ZstLow) ? 1 : ((method == SaveStateCompressionMode::ZstHigh) ? 18 : 0));
const size_t compressed_size = ZSTD_compress(buffer.data(), buffer_size, src.data(), src.size(), level);
if (ZSTD_isError(compressed_size)) [[unlikely]]
{
const char* errstr = ZSTD_getErrorString(ZSTD_getErrorCode(compressed_size));
Error::SetStringFmt(error, "ZSTD_compress() failed: {}", errstr ? errstr : "<unknown>");
return 0;
}
ctype = CompressHelpers::CompressType::Zstandard;
*header_type = static_cast<u32>(SAVE_STATE_HEADER::CompressionType::Zstandard);
write_size = static_cast<u32>(compressed_size);
clevel =
((method == SaveStateCompressionMode::ZstLow) ? 1 : ((method == SaveStateCompressionMode::ZstHigh) ? 18 : 0));
}
else [[unlikely]]
else if (method >= SaveStateCompressionMode::XZLow && method <= SaveStateCompressionMode::XZHigh)
{
Error::SetStringView(error, "Unknown method.");
ctype = CompressHelpers::CompressType::XZ;
*header_type = static_cast<u32>(SAVE_STATE_HEADER::CompressionType::XZ);
clevel =
((method == SaveStateCompressionMode::XZLow) ? 1 : ((method == SaveStateCompressionMode::XZHigh) ? 9 : 5));
}
else
{
Error::SetStringFmt(error, "Unknown compression type {}", static_cast<u32>(method));
return 0;
}
if (std::fwrite(buffer.data(), write_size, 1, fp) != 1) [[unlikely]]
const CompressHelpers::OptionalByteBuffer compressed_data =
CompressHelpers::CompressToBuffer(ctype, src, clevel, error);
if (!compressed_data.has_value())
return 0;
if (std::fwrite(compressed_data->data(), compressed_data->size(), 1, fp) != 1) [[unlikely]]
{
Error::SetStringFmt(error, "fwrite() failed: {}", errno);
return 0;
}
return write_size;
return static_cast<u32>(compressed_data->size());
}
float System::GetTargetSpeed()

@ -318,6 +318,9 @@ enum class SaveStateCompressionMode : u8
ZstLow,
ZstDefault,
ZstHigh,
XZLow,
XZDefault,
XZHigh,
Count,
};

Loading…
Cancel
Save