support for compressed save-states

This commit is contained in:
Flyinghead 2020-12-26 09:58:53 +01:00
parent 544c55d848
commit 060650e0d8
6 changed files with 249 additions and 29 deletions

View File

@ -431,7 +431,9 @@ target_sources(${PROJECT_NAME} PRIVATE
core/archive/7zArchive.cpp
core/archive/7zArchive.h
core/archive/archive.cpp
core/archive/archive.h)
core/archive/archive.h
core/archive/rzip.cpp
core/archive/rzip.h)
if(NOT MSVC)
target_sources(${PROJECT_NAME} PRIVATE
core/archive/ZipArchive.cpp

132
core/archive/rzip.cpp Normal file
View File

@ -0,0 +1,132 @@
/*
Copyright 2020 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "rzip.h"
#include <zlib.h>
const u8 RZipHeader[8] = { '#', 'R', 'Z', 'I', 'P', 'v', 1, '#' };
bool RZipFile::Open(const std::string& path, bool write)
{
verify(file == nullptr);
file = fopen(path.c_str(), write ? "wb" : "rb");
if (file == nullptr)
return false;
if (!write)
{
u8 header[sizeof(RZipHeader)];
if (fread(header, sizeof(header), 1, file) != 1
|| memcmp(header, RZipHeader, sizeof(header))
|| fread(&maxChunkSize, sizeof(maxChunkSize), 1, file) != 1
|| fread(&size, sizeof(size), 1, file) != 1)
{
Close();
return false;
}
chunk = new u8[maxChunkSize];
chunkIndex = 0;
chunkSize = 0;
}
return true;
}
void RZipFile::Close()
{
if (file != nullptr)
{
fclose(file);
file = nullptr;
if (chunk != nullptr)
{
delete [] chunk;
chunk = nullptr;
}
}
}
size_t RZipFile::Read(void *data, size_t length)
{
verify(file != nullptr);
u8 *p = (u8 *)data;
size_t rv = 0;
while (rv < length)
{
if (chunkIndex == chunkSize)
{
chunkSize = 0;
chunkIndex = 0;
u32 zippedSize;
if (fread(&zippedSize, sizeof(zippedSize), 1, file) != 1)
break;
u8 *zipped = new u8[zippedSize];
if (fread(zipped, zippedSize, 1, file) != 1)
{
delete [] zipped;
break;
}
uLongf tl = maxChunkSize;
if (uncompress(chunk, &tl, zipped, zippedSize) != Z_OK)
{
delete [] zipped;
break;
}
delete [] zipped;
chunkSize = (u32)tl;
}
u32 l = std::min(chunkSize - chunkIndex, (u32)length);
memcpy(p, chunk + chunkIndex, l);
p += l;
chunkIndex += l;
rv += l;
}
return rv;
}
size_t RZipFile::Write(const void *data, size_t length)
{
verify(file != nullptr);
verify(ftell(file) == 0);
maxChunkSize = 1024 * 1024;
if (fwrite(RZipHeader, sizeof(RZipHeader), 1, file) != 1
|| fwrite(&maxChunkSize, sizeof(maxChunkSize), 1, file) != 1
|| fwrite(&length, sizeof(length), 1, file) != 1)
return 0;
const u8 *p = (const u8 *)data;
u8 *zipped = new u8[maxChunkSize];
size_t rv = 0;
while (rv < length)
{
uLongf zippedSize = maxChunkSize;
uLongf uncompressedSize = std::min(maxChunkSize, (u32)(length - rv));
if (compress(zipped, &zippedSize, p, uncompressedSize) != Z_OK)
break;
u32 sz = (u32)zippedSize;
if (fwrite(&sz, sizeof(sz), 1, file) != 1
|| fwrite(zipped, zippedSize, 1, file) != 1)
return 0;
p += uncompressedSize;
rv += uncompressedSize;
}
return rv;
}

43
core/archive/rzip.h Normal file
View File

@ -0,0 +1,43 @@
/*
Copyright 2020 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
// Implementation of the RZIP stream format as defined by libretro
// https://github.com/libretro/libretro-common/blob/master/include/streams/rzip_stream.h
#pragma once
#include "types.h"
class RZipFile
{
public:
~RZipFile() { Close(); }
bool Open(const std::string& path, bool write);
void Close();
size_t Size() const { return size; }
size_t Read(void *data, size_t length);
size_t Write(const void *data, size_t length);
private:
FILE *file = nullptr;
size_t size = 0;
u32 maxChunkSize = 0;
u8 *chunk = nullptr;
u32 chunkSize = 0;
u32 chunkIndex = 0;
};

View File

@ -35,6 +35,7 @@
#include "hw/maple/maple_devs.h"
#include "network/naomi_network.h"
#include "rend/mainui.h"
#include "archive/rzip.h"
void FlushCache();
static void LoadCustom();
@ -1106,11 +1107,8 @@ static std::string get_savestate_file_path(bool writable)
void dc_savestate()
{
std::string filename;
unsigned int total_size = 0 ;
void *data = NULL ;
void *data_ptr = NULL ;
FILE *f ;
dc_stop();
@ -1131,7 +1129,7 @@ void dc_savestate()
return;
}
data_ptr = data ;
void *data_ptr = data;
if ( ! dc_serialize(&data_ptr, &total_size) )
{
@ -1141,8 +1139,9 @@ void dc_savestate()
return;
}
filename = get_savestate_file_path(true);
f = fopen(filename.c_str(), "wb") ;
std::string filename = get_savestate_file_path(true);
#if 0
FILE *f = fopen(filename.c_str(), "wb") ;
if ( f == NULL )
{
@ -1154,6 +1153,25 @@ void dc_savestate()
fwrite(data, 1, total_size, f) ;
fclose(f);
#else
RZipFile zipFile;
if (!zipFile.Open(filename, true))
{
WARN_LOG(SAVESTATE, "Failed to save state - could not open %s for writing", filename.c_str());
gui_display_notification("Cannot open save file", 2000);
cleanup_serialize(data);
return;
}
if (zipFile.Write(data, total_size) != total_size)
{
WARN_LOG(SAVESTATE, "Failed to save state - error writing %s", filename.c_str());
gui_display_notification("Error saving state", 2000);
zipFile.Close();
cleanup_serialize(data);
return;
}
zipFile.Close();
#endif
cleanup_serialize(data) ;
INFO_LOG(SAVESTATE, "Saved state to %s size %d", filename.c_str(), total_size) ;
@ -1162,38 +1180,54 @@ void dc_savestate()
void dc_loadstate()
{
std::string filename;
unsigned int total_size = 0 ;
void *data = NULL ;
void *data_ptr = NULL ;
FILE *f ;
u32 total_size = 0;
FILE *f = nullptr;
dc_stop();
filename = get_savestate_file_path(false);
f = fopen(filename.c_str(), "rb") ;
if ( f == NULL )
std::string filename = get_savestate_file_path(false);
RZipFile zipFile;
if (zipFile.Open(filename, false))
{
WARN_LOG(SAVESTATE, "Failed to load state - could not open %s for reading", filename.c_str()) ;
gui_display_notification("Save state not found", 2000);
cleanup_serialize(data) ;
return;
total_size = (u32)zipFile.Size();
}
fseek(f, 0, SEEK_END);
total_size = ftell(f);
fseek(f, 0, SEEK_SET);
data = malloc(total_size) ;
else
{
f = fopen(filename.c_str(), "rb") ;
if ( f == NULL )
{
WARN_LOG(SAVESTATE, "Failed to load state - could not open %s for reading", filename.c_str()) ;
gui_display_notification("Save state not found", 2000);
return;
}
fseek(f, 0, SEEK_END);
total_size = (u32)ftell(f);
fseek(f, 0, SEEK_SET);
}
void *data = malloc(total_size);
if ( data == NULL )
{
WARN_LOG(SAVESTATE, "Failed to load state - could not malloc %d bytes", total_size) ;
gui_display_notification("Failed to load state - memory full", 2000);
cleanup_serialize(data) ;
if (f != nullptr)
fclose(f);
else
zipFile.Close();
return;
}
size_t read_size = fread(data, 1, total_size, f) ;
fclose(f);
size_t read_size;
if (f == nullptr)
{
read_size = zipFile.Read(data, total_size);
zipFile.Close();
}
else
{
read_size = fread(data, 1, total_size, f) ;
fclose(f);
}
if (read_size != total_size)
{
WARN_LOG(SAVESTATE, "Failed to load state - I/O error");
@ -1202,7 +1236,7 @@ void dc_loadstate()
return;
}
data_ptr = data ;
void *data_ptr = data;
custom_texture.Terminate();
#if FEAT_AREC == DYNAREC_JIT

View File

@ -64,8 +64,11 @@ void input_sdl_init()
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
#ifdef _WIN32
if (cfgLoadBool("input", "DisableXInput", false))
// Disable XInput for some old joytsicks
{
// Disable XInput for some old joysticks
NOTICE_LOG(INPUT, "Disabling XInput, using DirectInput");
SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "0");
}
#endif
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
die("SDL: error initializing Joystick subsystem");

View File

@ -315,6 +315,7 @@
AEF256512294060400348550 /* 7zArchive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEF2564C2294060300348550 /* 7zArchive.cpp */; };
AEF256522294060400348550 /* archive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEF2564E2294060300348550 /* archive.cpp */; };
AEF77FDB24AA76BA00171080 /* audiobackend_null.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEF77FDA24AA76B900171080 /* audiobackend_null.cpp */; };
AEF99730259682850038E0B8 /* rzip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEF9972F259682850038E0B8 /* rzip.cpp */; };
AEFF7ECC214AEC810068CE11 /* modem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEFF7EC7214AEC800068CE11 /* modem.cpp */; };
AEFF7F4D214D9D590068CE11 /* pico_arp.c in Sources */ = {isa = PBXBuildFile; fileRef = AEFF7EFA214D9D590068CE11 /* pico_arp.c */; };
AEFF7F4E214D9D590068CE11 /* pico_dev_ppp.c in Sources */ = {isa = PBXBuildFile; fileRef = AEFF7EFE214D9D590068CE11 /* pico_dev_ppp.c */; };
@ -967,6 +968,8 @@
AEF2564E2294060300348550 /* archive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = archive.cpp; sourceTree = "<group>"; };
AEF2564F2294060400348550 /* 7zArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 7zArchive.h; sourceTree = "<group>"; };
AEF77FDA24AA76B900171080 /* audiobackend_null.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audiobackend_null.cpp; sourceTree = "<group>"; };
AEF9972E259682850038E0B8 /* rzip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rzip.h; sourceTree = "<group>"; };
AEF9972F259682850038E0B8 /* rzip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rzip.cpp; sourceTree = "<group>"; };
AEFF7EC7214AEC800068CE11 /* modem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = modem.cpp; sourceTree = "<group>"; };
AEFF7EC8214AEC800068CE11 /* modem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modem.h; sourceTree = "<group>"; };
AEFF7EC9214AEC800068CE11 /* modem_regs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modem_regs.h; sourceTree = "<group>"; };
@ -1831,6 +1834,8 @@
AEF2564C2294060300348550 /* 7zArchive.cpp */,
AEF2564F2294060400348550 /* 7zArchive.h */,
AEF2564E2294060300348550 /* archive.cpp */,
AEF9972F259682850038E0B8 /* rzip.cpp */,
AEF9972E259682850038E0B8 /* rzip.h */,
AEF2564B2294060300348550 /* archive.h */,
AEF2564A2294060300348550 /* ZipArchive.cpp */,
AEF2564D2294060300348550 /* ZipArchive.h */,
@ -2630,6 +2635,7 @@
AED73E6F2348E45000ECDB64 /* PpAtom.cpp in Sources */,
84B7BF191B72720200F9733F /* deflate.c in Sources */,
84B7BF7E1B72720200F9733F /* TexCache.cpp in Sources */,
AEF99730259682850038E0B8 /* rzip.cpp in Sources */,
AE43537522C9420C005E19CE /* LogManager.cpp in Sources */,
AEFF7F4E214D9D590068CE11 /* pico_dev_ppp.c in Sources */,
84B7BEE41B72720200F9733F /* zip_delete.c in Sources */,