mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-10 16:34:36 +00:00
System: Add XZ save state compression
For when space is really at a premium. Also use CompressHelpers to reduce duplication.
This commit is contained in:
@@ -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;
|
||||
case SAVE_STATE_HEADER::CompressionType::XZ:
|
||||
type = CompressHelpers::CompressType::XZ;
|
||||
break;
|
||||
|
||||
default:
|
||||
Error::SetStringFmt(error, "Unknown compression method {}", static_cast<u32>(method));
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user