diff --git a/CMakeLists.txt b/CMakeLists.txt
index a29ee3c06..26a13d786 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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
diff --git a/core/archive/rzip.cpp b/core/archive/rzip.cpp
new file mode 100644
index 000000000..f48bf7b43
--- /dev/null
+++ b/core/archive/rzip.cpp
@@ -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 .
+*/
+#include "rzip.h"
+#include
+
+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;
+}
diff --git a/core/archive/rzip.h b/core/archive/rzip.h
new file mode 100644
index 000000000..7a265e3c2
--- /dev/null
+++ b/core/archive/rzip.h
@@ -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 .
+*/
+// 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;
+};
diff --git a/core/nullDC.cpp b/core/nullDC.cpp
index 9c0589b94..bf2e41543 100644
--- a/core/nullDC.cpp
+++ b/core/nullDC.cpp
@@ -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
diff --git a/core/sdl/sdl.cpp b/core/sdl/sdl.cpp
index ca788716e..8de7d47b4 100644
--- a/core/sdl/sdl.cpp
+++ b/core/sdl/sdl.cpp
@@ -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");
diff --git a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj
index 36bac8b3c..55a788363 100644
--- a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj
+++ b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj
@@ -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 = ""; };
AEF2564F2294060400348550 /* 7zArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 7zArchive.h; sourceTree = ""; };
AEF77FDA24AA76B900171080 /* audiobackend_null.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audiobackend_null.cpp; sourceTree = ""; };
+ AEF9972E259682850038E0B8 /* rzip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rzip.h; sourceTree = ""; };
+ AEF9972F259682850038E0B8 /* rzip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rzip.cpp; sourceTree = ""; };
AEFF7EC7214AEC800068CE11 /* modem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = modem.cpp; sourceTree = ""; };
AEFF7EC8214AEC800068CE11 /* modem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modem.h; sourceTree = ""; };
AEFF7EC9214AEC800068CE11 /* modem_regs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modem_regs.h; sourceTree = ""; };
@@ -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 */,