Merge pull request #10454 from jdgleaver/save-state-compression
Add optional save state compression
This commit is contained in:
commit
5303ecee22
|
@ -1681,7 +1681,8 @@ endif
|
||||||
|
|
||||||
ifeq ($(HAVE_ZLIB_COMMON), 1)
|
ifeq ($(HAVE_ZLIB_COMMON), 1)
|
||||||
OBJ += $(LIBRETRO_COMM_DIR)/file/archive_file_zlib.o \
|
OBJ += $(LIBRETRO_COMM_DIR)/file/archive_file_zlib.o \
|
||||||
$(LIBRETRO_COMM_DIR)/streams/trans_stream_zlib.o
|
$(LIBRETRO_COMM_DIR)/streams/trans_stream_zlib.o \
|
||||||
|
$(LIBRETRO_COMM_DIR)/streams/rzip_stream.o
|
||||||
DEFINES += -DHAVE_ZLIB
|
DEFINES += -DHAVE_ZLIB
|
||||||
HAVE_COMPRESSION = 1
|
HAVE_COMPRESSION = 1
|
||||||
|
|
||||||
|
|
|
@ -911,6 +911,10 @@ static const bool savestate_auto_load = false;
|
||||||
|
|
||||||
static const bool savestate_thumbnail_enable = false;
|
static const bool savestate_thumbnail_enable = false;
|
||||||
|
|
||||||
|
/* When creating save state files, compress
|
||||||
|
* written data */
|
||||||
|
#define DEFAULT_SAVESTATE_FILE_COMPRESSION false
|
||||||
|
|
||||||
/* Slowmotion ratio. */
|
/* Slowmotion ratio. */
|
||||||
#define DEFAULT_SLOWMOTION_RATIO 3.0
|
#define DEFAULT_SLOWMOTION_RATIO 3.0
|
||||||
|
|
||||||
|
|
|
@ -1614,6 +1614,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
||||||
SETTING_BOOL("savestate_auto_save", &settings->bools.savestate_auto_save, true, savestate_auto_save, false);
|
SETTING_BOOL("savestate_auto_save", &settings->bools.savestate_auto_save, true, savestate_auto_save, false);
|
||||||
SETTING_BOOL("savestate_auto_load", &settings->bools.savestate_auto_load, true, savestate_auto_load, false);
|
SETTING_BOOL("savestate_auto_load", &settings->bools.savestate_auto_load, true, savestate_auto_load, false);
|
||||||
SETTING_BOOL("savestate_thumbnail_enable", &settings->bools.savestate_thumbnail_enable, true, savestate_thumbnail_enable, false);
|
SETTING_BOOL("savestate_thumbnail_enable", &settings->bools.savestate_thumbnail_enable, true, savestate_thumbnail_enable, false);
|
||||||
|
SETTING_BOOL("savestate_file_compression", &settings->bools.savestate_file_compression, true, DEFAULT_SAVESTATE_FILE_COMPRESSION, false);
|
||||||
SETTING_BOOL("history_list_enable", &settings->bools.history_list_enable, true, DEFAULT_HISTORY_LIST_ENABLE, false);
|
SETTING_BOOL("history_list_enable", &settings->bools.history_list_enable, true, DEFAULT_HISTORY_LIST_ENABLE, false);
|
||||||
SETTING_BOOL("playlist_entry_rename", &settings->bools.playlist_entry_rename, true, DEFAULT_PLAYLIST_ENTRY_RENAME, false);
|
SETTING_BOOL("playlist_entry_rename", &settings->bools.playlist_entry_rename, true, DEFAULT_PLAYLIST_ENTRY_RENAME, false);
|
||||||
SETTING_BOOL("game_specific_options", &settings->bools.game_specific_options, true, default_game_specific_options, false);
|
SETTING_BOOL("game_specific_options", &settings->bools.game_specific_options, true, default_game_specific_options, false);
|
||||||
|
|
|
@ -344,6 +344,7 @@ typedef struct settings
|
||||||
bool savestate_auto_save;
|
bool savestate_auto_save;
|
||||||
bool savestate_auto_load;
|
bool savestate_auto_load;
|
||||||
bool savestate_thumbnail_enable;
|
bool savestate_thumbnail_enable;
|
||||||
|
bool savestate_file_compression;
|
||||||
bool network_cmd_enable;
|
bool network_cmd_enable;
|
||||||
bool stdin_cmd_enable;
|
bool stdin_cmd_enable;
|
||||||
bool keymapper_enable;
|
bool keymapper_enable;
|
||||||
|
|
|
@ -128,6 +128,7 @@ COMPRESSION
|
||||||
|
|
||||||
#ifdef HAVE_ZLIB
|
#ifdef HAVE_ZLIB
|
||||||
#include "../libretro-common/streams/trans_stream_zlib.c"
|
#include "../libretro-common/streams/trans_stream_zlib.c"
|
||||||
|
#include "../libretro-common/streams/rzip_stream.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*============================================================
|
/*============================================================
|
||||||
|
|
|
@ -1200,6 +1200,8 @@ MSG_HASH(MENU_ENUM_LABEL_SAVESTATE_AUTO_LOAD,
|
||||||
"savestate_auto_load")
|
"savestate_auto_load")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_SAVESTATE_THUMBNAIL_ENABLE,
|
MSG_HASH(MENU_ENUM_LABEL_SAVESTATE_THUMBNAIL_ENABLE,
|
||||||
"savestate_thumbnails")
|
"savestate_thumbnails")
|
||||||
|
MSG_HASH(MENU_ENUM_LABEL_SAVESTATE_FILE_COMPRESSION,
|
||||||
|
"savestate_file_compression")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_SAVESTATE_AUTO_SAVE,
|
MSG_HASH(MENU_ENUM_LABEL_SAVESTATE_AUTO_SAVE,
|
||||||
"savestate_auto_save")
|
"savestate_auto_save")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_SAVESTATE_DIRECTORY,
|
MSG_HASH(MENU_ENUM_LABEL_SAVESTATE_DIRECTORY,
|
||||||
|
|
|
@ -2996,6 +2996,14 @@ MSG_HASH(
|
||||||
MENU_ENUM_LABEL_VALUE_SAVESTATE_THUMBNAIL_ENABLE,
|
MENU_ENUM_LABEL_VALUE_SAVESTATE_THUMBNAIL_ENABLE,
|
||||||
"Savestate Thumbnails"
|
"Savestate Thumbnails"
|
||||||
)
|
)
|
||||||
|
MSG_HASH(
|
||||||
|
MENU_ENUM_LABEL_VALUE_SAVESTATE_FILE_COMPRESSION,
|
||||||
|
"Savestate Compression"
|
||||||
|
)
|
||||||
|
MSG_HASH(
|
||||||
|
MENU_ENUM_SUBLABEL_SAVESTATE_FILE_COMPRESSION,
|
||||||
|
"Write save state files in an archived format. Dramatically reduces file size at the expense of increased saving/loading times."
|
||||||
|
)
|
||||||
MSG_HASH(
|
MSG_HASH(
|
||||||
MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG,
|
MENU_ENUM_LABEL_VALUE_SAVE_CURRENT_CONFIG,
|
||||||
"Save Current Configuration"
|
"Save Current Configuration"
|
||||||
|
|
|
@ -36,7 +36,8 @@ enum intfstream_type
|
||||||
{
|
{
|
||||||
INTFSTREAM_FILE = 0,
|
INTFSTREAM_FILE = 0,
|
||||||
INTFSTREAM_MEMORY,
|
INTFSTREAM_MEMORY,
|
||||||
INTFSTREAM_CHD
|
INTFSTREAM_CHD,
|
||||||
|
INTFSTREAM_RZIP
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct intfstream_internal intfstream_internal_t, intfstream_t;
|
typedef struct intfstream_internal intfstream_internal_t, intfstream_t;
|
||||||
|
@ -112,6 +113,9 @@ intfstream_t *intfstream_open_writable_memory(void *data,
|
||||||
intfstream_t *intfstream_open_chd_track(const char *path,
|
intfstream_t *intfstream_open_chd_track(const char *path,
|
||||||
unsigned mode, unsigned hints, int32_t track);
|
unsigned mode, unsigned hints, int32_t track);
|
||||||
|
|
||||||
|
intfstream_t* intfstream_open_rzip_file(const char *path,
|
||||||
|
unsigned mode);
|
||||||
|
|
||||||
RETRO_END_DECLS
|
RETRO_END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
/* Copyright (C) 2010-2020 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (rzip_stream.h).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge,
|
||||||
|
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LIBRETRO_SDK_FILE_RZIP_STREAM_H
|
||||||
|
#define _LIBRETRO_SDK_FILE_RZIP_STREAM_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <retro_common_api.h>
|
||||||
|
|
||||||
|
RETRO_BEGIN_DECLS
|
||||||
|
|
||||||
|
/* Rudimentary interface for streaming data to/from a
|
||||||
|
* zlib-compressed chunk-based RZIP archive file.
|
||||||
|
*
|
||||||
|
* This is somewhat less efficient than using regular
|
||||||
|
* gzip code, but this is by design - the intention here
|
||||||
|
* is to create an interface that integrates seamlessly
|
||||||
|
* with normal RetroArch functionality, using only
|
||||||
|
* standard/existing libretro-common routines.
|
||||||
|
* (Actual efficiency is pretty good, regardless:
|
||||||
|
* archived file size is almost identical to a solid
|
||||||
|
* zip file, and compression/decompression speed is
|
||||||
|
* not substantially worse than external archiving tools;
|
||||||
|
* it is certainly acceptable for use in real-time
|
||||||
|
* frontend applications)
|
||||||
|
*
|
||||||
|
* When reading existing files, uncompressed content
|
||||||
|
* is handled automatically. File type (compressed/
|
||||||
|
* uncompressed) is detected via the RZIP header.
|
||||||
|
*
|
||||||
|
* ## RZIP file format:
|
||||||
|
*
|
||||||
|
* <file id header>: 8 bytes
|
||||||
|
* - [#][R][Z][I][P][v][file format version][#]
|
||||||
|
* <uncompressed chunk size>: 4 bytes, little endian order
|
||||||
|
* - nominal (maximum) size of each uncompressed
|
||||||
|
* chunk, in bytes
|
||||||
|
* <total uncompressed data size>: 8 bytes, little endian order
|
||||||
|
* <size of next compressed chunk>: 4 bytes, little endian order
|
||||||
|
* - size on-disk of next compressed data
|
||||||
|
* chunk, in bytes
|
||||||
|
* <next compressed chunk>: n bytes of zlib compressed data
|
||||||
|
* ...
|
||||||
|
* <size of next compressed chunk> : repeated until end of file
|
||||||
|
* <next compressed chunk> :
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Prevent direct access to rzipstream_t members */
|
||||||
|
typedef struct rzipstream rzipstream_t;
|
||||||
|
|
||||||
|
/* File Open */
|
||||||
|
|
||||||
|
/* Opens a new or existing RZIP file
|
||||||
|
* > Supported 'mode' values are:
|
||||||
|
* - RETRO_VFS_FILE_ACCESS_READ
|
||||||
|
* - RETRO_VFS_FILE_ACCESS_WRITE
|
||||||
|
* > When reading, 'path' may reference compressed
|
||||||
|
* or uncompressed data
|
||||||
|
* Returns NULL if arguments are invalid, file
|
||||||
|
* is invalid or an IO error occurs */
|
||||||
|
rzipstream_t* rzipstream_open(const char *path, unsigned mode);
|
||||||
|
|
||||||
|
/* File Read */
|
||||||
|
|
||||||
|
/* Reads (a maximum of) 'len' bytes from an RZIP file.
|
||||||
|
* Returns actual number of bytes read, or -1 in
|
||||||
|
* the event of an error */
|
||||||
|
int64_t rzipstream_read(rzipstream_t *stream, void *data, int64_t len);
|
||||||
|
|
||||||
|
/* File Write */
|
||||||
|
|
||||||
|
/* Writes 'len' bytes to an RZIP file.
|
||||||
|
* Returns actual number of bytes written, or -1
|
||||||
|
* in the event of an error */
|
||||||
|
int64_t rzipstream_write(rzipstream_t *stream, const void *data, int64_t len);
|
||||||
|
|
||||||
|
/* File Status */
|
||||||
|
|
||||||
|
/* Returns total size (in bytes) of the *uncompressed*
|
||||||
|
* data in an RZIP file.
|
||||||
|
* (If reading an uncompressed file, this corresponds
|
||||||
|
* to the 'physical' file size in bytes)
|
||||||
|
* Returns -1 in the event of a error. */
|
||||||
|
int64_t rzipstream_get_size(rzipstream_t *stream);
|
||||||
|
|
||||||
|
/* Returns EOF when no further *uncompressed* data
|
||||||
|
* can be read from an RZIP file. */
|
||||||
|
int rzipstream_eof(rzipstream_t *stream);
|
||||||
|
|
||||||
|
/* File Close */
|
||||||
|
|
||||||
|
/* Closes RZIP file. If file is open for writing,
|
||||||
|
* flushes any remaining buffered data to disk.
|
||||||
|
* Returns -1 in the event of a error. */
|
||||||
|
int rzipstream_close(rzipstream_t *stream);
|
||||||
|
|
||||||
|
RETRO_END_DECLS
|
||||||
|
|
||||||
|
#endif
|
|
@ -28,6 +28,9 @@
|
||||||
#ifdef HAVE_CHD
|
#ifdef HAVE_CHD
|
||||||
#include <streams/chd_stream.h>
|
#include <streams/chd_stream.h>
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
#include <streams/rzip_stream.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
struct intfstream_internal
|
struct intfstream_internal
|
||||||
{
|
{
|
||||||
|
@ -55,6 +58,12 @@ struct intfstream_internal
|
||||||
chdstream_t *fp;
|
chdstream_t *fp;
|
||||||
} chd;
|
} chd;
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
rzipstream_t *fp;
|
||||||
|
} rzip;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
int64_t intfstream_get_size(intfstream_internal_t *intf)
|
int64_t intfstream_get_size(intfstream_internal_t *intf)
|
||||||
|
@ -73,6 +82,12 @@ int64_t intfstream_get_size(intfstream_internal_t *intf)
|
||||||
return chdstream_get_size(intf->chd.fp);
|
return chdstream_get_size(intf->chd.fp);
|
||||||
#else
|
#else
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
return rzipstream_get_size(intf->rzip.fp);
|
||||||
|
#else
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +114,9 @@ bool intfstream_resize(intfstream_internal_t *intf, intfstream_info_t *info)
|
||||||
#ifdef HAVE_CHD
|
#ifdef HAVE_CHD
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
/* Unsupported */
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -130,6 +148,15 @@ bool intfstream_open(intfstream_internal_t *intf, const char *path,
|
||||||
break;
|
break;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
|
#endif
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
intf->rzip.fp = rzipstream_open(path, mode);
|
||||||
|
if (!intf->rzip.fp)
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +174,7 @@ int intfstream_flush(intfstream_internal_t *intf)
|
||||||
return filestream_flush(intf->file.fp);
|
return filestream_flush(intf->file.fp);
|
||||||
case INTFSTREAM_MEMORY:
|
case INTFSTREAM_MEMORY:
|
||||||
case INTFSTREAM_CHD:
|
case INTFSTREAM_CHD:
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
/* Should we stub this for these interfaces? */
|
/* Should we stub this for these interfaces? */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -173,6 +201,12 @@ int intfstream_close(intfstream_internal_t *intf)
|
||||||
#ifdef HAVE_CHD
|
#ifdef HAVE_CHD
|
||||||
if (intf->chd.fp)
|
if (intf->chd.fp)
|
||||||
chdstream_close(intf->chd.fp);
|
chdstream_close(intf->chd.fp);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
if (intf->rzip.fp)
|
||||||
|
return rzipstream_close(intf->rzip.fp);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -209,6 +243,8 @@ void *intfstream_init(intfstream_info_t *info)
|
||||||
#else
|
#else
|
||||||
goto error;
|
goto error;
|
||||||
#endif
|
#endif
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return intf;
|
return intf;
|
||||||
|
@ -252,6 +288,9 @@ int64_t intfstream_seek(intfstream_internal_t *intf, int64_t offset, int whence)
|
||||||
#else
|
#else
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
/* Unsupported */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -273,6 +312,12 @@ int64_t intfstream_read(intfstream_internal_t *intf, void *s, uint64_t len)
|
||||||
return chdstream_read(intf->chd.fp, s, len);
|
return chdstream_read(intf->chd.fp, s, len);
|
||||||
#else
|
#else
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
return rzipstream_read(intf->rzip.fp, s, len);
|
||||||
|
#else
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,6 +338,12 @@ int64_t intfstream_write(intfstream_internal_t *intf,
|
||||||
return memstream_write(intf->memory.fp, s, len);
|
return memstream_write(intf->memory.fp, s, len);
|
||||||
case INTFSTREAM_CHD:
|
case INTFSTREAM_CHD:
|
||||||
return -1;
|
return -1;
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
return rzipstream_write(intf->rzip.fp, s, len);
|
||||||
|
#else
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -311,6 +362,8 @@ int64_t intfstream_get_ptr(intfstream_internal_t* intf)
|
||||||
return memstream_get_ptr(intf->memory.fp);
|
return memstream_get_ptr(intf->memory.fp);
|
||||||
case INTFSTREAM_CHD:
|
case INTFSTREAM_CHD:
|
||||||
return -1;
|
return -1;
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -336,6 +389,9 @@ char *intfstream_gets(intfstream_internal_t *intf,
|
||||||
#else
|
#else
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
/* Unsupported */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -358,6 +414,9 @@ int intfstream_getc(intfstream_internal_t *intf)
|
||||||
#else
|
#else
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
/* Unsupported */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -380,6 +439,9 @@ int64_t intfstream_tell(intfstream_internal_t *intf)
|
||||||
#else
|
#else
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
/* Unsupported */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -400,6 +462,9 @@ void intfstream_rewind(intfstream_internal_t *intf)
|
||||||
chdstream_rewind(intf->chd.fp);
|
chdstream_rewind(intf->chd.fp);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
/* Unsupported */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,6 +483,9 @@ void intfstream_putc(intfstream_internal_t *intf, int c)
|
||||||
break;
|
break;
|
||||||
case INTFSTREAM_CHD:
|
case INTFSTREAM_CHD:
|
||||||
break;
|
break;
|
||||||
|
case INTFSTREAM_RZIP:
|
||||||
|
/* Unsupported */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,8 +599,6 @@ error:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
intfstream_t *intfstream_open_chd_track(const char *path,
|
intfstream_t *intfstream_open_chd_track(const char *path,
|
||||||
unsigned mode, unsigned hints, int32_t track)
|
unsigned mode, unsigned hints, int32_t track)
|
||||||
{
|
{
|
||||||
|
@ -560,3 +626,29 @@ error:
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
intfstream_t* intfstream_open_rzip_file(const char *path,
|
||||||
|
unsigned mode)
|
||||||
|
{
|
||||||
|
intfstream_info_t info;
|
||||||
|
intfstream_t *fd = NULL;
|
||||||
|
|
||||||
|
info.type = INTFSTREAM_RZIP;
|
||||||
|
fd = (intfstream_t*)intfstream_init(&info);
|
||||||
|
|
||||||
|
if (!fd)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!intfstream_open(fd, path, mode, RETRO_VFS_FILE_ACCESS_HINT_NONE))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (fd)
|
||||||
|
{
|
||||||
|
intfstream_close(fd);
|
||||||
|
free(fd);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,742 @@
|
||||||
|
/* Copyright (C) 2010-2020 The RetroArch team
|
||||||
|
*
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
* The following license statement only applies to this file (rzip_stream.c).
|
||||||
|
* ---------------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge,
|
||||||
|
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string/stdstring.h>
|
||||||
|
#include <file/file_path.h>
|
||||||
|
|
||||||
|
#include <streams/file_stream.h>
|
||||||
|
#include <streams/trans_stream.h>
|
||||||
|
|
||||||
|
#include <streams/rzip_stream.h>
|
||||||
|
|
||||||
|
/* Current RZIP file format version */
|
||||||
|
#define RZIP_VERSION 1
|
||||||
|
|
||||||
|
/* Compression level
|
||||||
|
* > zlib default of 6 provides the best
|
||||||
|
* balance between file size and
|
||||||
|
* compression speed */
|
||||||
|
#define RZIP_COMPRESSION_LEVEL 6
|
||||||
|
|
||||||
|
/* Default chunk size: 128kb */
|
||||||
|
#define RZIP_DEFAULT_CHUNK_SIZE 131072
|
||||||
|
|
||||||
|
/* Header sizes (in bytes) */
|
||||||
|
#define RZIP_HEADER_SIZE 20
|
||||||
|
#define RZIP_CHUNK_HEADER_SIZE 4
|
||||||
|
|
||||||
|
/* Holds all metadata for an RZIP file stream */
|
||||||
|
struct rzipstream
|
||||||
|
{
|
||||||
|
bool is_compressed;
|
||||||
|
bool is_writing;
|
||||||
|
uint64_t size;
|
||||||
|
uint32_t chunk_size;
|
||||||
|
/* virtual_ptr: Used to track how much
|
||||||
|
* uncompressed data has been read */
|
||||||
|
uint64_t virtual_ptr;
|
||||||
|
RFILE* file;
|
||||||
|
const struct trans_stream_backend *deflate_backend;
|
||||||
|
void *deflate_stream;
|
||||||
|
const struct trans_stream_backend *inflate_backend;
|
||||||
|
void *inflate_stream;
|
||||||
|
uint8_t *in_buf;
|
||||||
|
uint32_t in_buf_size;
|
||||||
|
uint32_t in_buf_ptr;
|
||||||
|
uint8_t *out_buf;
|
||||||
|
uint32_t out_buf_size;
|
||||||
|
uint32_t out_buf_ptr;
|
||||||
|
uint32_t out_buf_occupancy;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Header Functions */
|
||||||
|
|
||||||
|
/* Reads header information from RZIP file
|
||||||
|
* > Detects whether file is compressed or
|
||||||
|
* uncompressed data
|
||||||
|
* > If compressed, extracts uncompressed
|
||||||
|
* file/chunk sizes */
|
||||||
|
static bool rzipstream_read_file_header(rzipstream_t *stream)
|
||||||
|
{
|
||||||
|
uint8_t header_bytes[RZIP_HEADER_SIZE] = {0};
|
||||||
|
int64_t length;
|
||||||
|
|
||||||
|
if (!stream)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Attempt to read header bytes */
|
||||||
|
length = filestream_read(stream->file, header_bytes, sizeof(header_bytes));
|
||||||
|
if (length <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* If file length is less than header size
|
||||||
|
* then assume this is uncompressed data */
|
||||||
|
if (length < RZIP_HEADER_SIZE)
|
||||||
|
goto file_uncompressed;
|
||||||
|
|
||||||
|
/* Check 'magic numbers' - first 8 bytes
|
||||||
|
* of header */
|
||||||
|
if ((header_bytes[0] != 35) || /* # */
|
||||||
|
(header_bytes[1] != 82) || /* R */
|
||||||
|
(header_bytes[2] != 90) || /* Z */
|
||||||
|
(header_bytes[3] != 73) || /* I */
|
||||||
|
(header_bytes[4] != 80) || /* P */
|
||||||
|
(header_bytes[5] != 118) || /* v */
|
||||||
|
(header_bytes[6] != RZIP_VERSION) || /* file format version number */
|
||||||
|
(header_bytes[7] != 35)) /* # */
|
||||||
|
goto file_uncompressed;
|
||||||
|
|
||||||
|
/* Get uncompressed chunk size - next 4 bytes */
|
||||||
|
stream->chunk_size = ((uint32_t)header_bytes[11] << 24) |
|
||||||
|
((uint32_t)header_bytes[10] << 16) |
|
||||||
|
((uint32_t)header_bytes[9] << 8) |
|
||||||
|
(uint32_t)header_bytes[8];
|
||||||
|
if (stream->chunk_size == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Get total uncompressed data size - next 8 bytes */
|
||||||
|
stream->size = ((uint64_t)header_bytes[19] << 56) |
|
||||||
|
((uint64_t)header_bytes[18] << 48) |
|
||||||
|
((uint64_t)header_bytes[17] << 40) |
|
||||||
|
((uint64_t)header_bytes[16] << 32) |
|
||||||
|
((uint64_t)header_bytes[15] << 24) |
|
||||||
|
((uint64_t)header_bytes[14] << 16) |
|
||||||
|
((uint64_t)header_bytes[13] << 8) |
|
||||||
|
(uint64_t)header_bytes[12];
|
||||||
|
if (stream->size == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stream->is_compressed = true;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
file_uncompressed:
|
||||||
|
|
||||||
|
/* Reset file to start */
|
||||||
|
filestream_seek(stream->file, 0, SEEK_SET);
|
||||||
|
|
||||||
|
/* Get 'raw' file size */
|
||||||
|
stream->size = filestream_get_size(stream->file);
|
||||||
|
|
||||||
|
stream->is_compressed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Writes header information to RZIP file
|
||||||
|
* > ID 'magic numbers' + uncompressed
|
||||||
|
* file/chunk sizes */
|
||||||
|
static bool rzipstream_write_file_header(rzipstream_t *stream)
|
||||||
|
{
|
||||||
|
uint8_t header_bytes[RZIP_HEADER_SIZE] = {0};
|
||||||
|
int64_t length;
|
||||||
|
|
||||||
|
if (!stream)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Populate header array */
|
||||||
|
|
||||||
|
/* > 'Magic numbers' - first 8 bytes */
|
||||||
|
header_bytes[0] = 35; /* # */
|
||||||
|
header_bytes[1] = 82; /* R */
|
||||||
|
header_bytes[2] = 90; /* Z */
|
||||||
|
header_bytes[3] = 73; /* I */
|
||||||
|
header_bytes[4] = 80; /* P */
|
||||||
|
header_bytes[5] = 118; /* v */
|
||||||
|
header_bytes[6] = RZIP_VERSION; /* file format version number */
|
||||||
|
header_bytes[7] = 35; /* # */
|
||||||
|
|
||||||
|
/* > Uncompressed chunk size - next 4 bytes */
|
||||||
|
header_bytes[11] = (stream->chunk_size >> 24) & 0xFF;
|
||||||
|
header_bytes[10] = (stream->chunk_size >> 16) & 0xFF;
|
||||||
|
header_bytes[9] = (stream->chunk_size >> 8) & 0xFF;
|
||||||
|
header_bytes[8] = stream->chunk_size & 0xFF;
|
||||||
|
|
||||||
|
/* > Total uncompressed data size - next 8 bytes */
|
||||||
|
header_bytes[19] = (stream->size >> 56) & 0xFF;
|
||||||
|
header_bytes[18] = (stream->size >> 48) & 0xFF;
|
||||||
|
header_bytes[17] = (stream->size >> 40) & 0xFF;
|
||||||
|
header_bytes[16] = (stream->size >> 32) & 0xFF;
|
||||||
|
header_bytes[15] = (stream->size >> 24) & 0xFF;
|
||||||
|
header_bytes[14] = (stream->size >> 16) & 0xFF;
|
||||||
|
header_bytes[13] = (stream->size >> 8) & 0xFF;
|
||||||
|
header_bytes[12] = stream->size & 0xFF;
|
||||||
|
|
||||||
|
/* Reset file to start */
|
||||||
|
filestream_seek(stream->file, 0, SEEK_SET);
|
||||||
|
|
||||||
|
/* Write header bytes */
|
||||||
|
length = filestream_write(stream->file, header_bytes, sizeof(header_bytes));
|
||||||
|
if (length != RZIP_HEADER_SIZE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stream Initialisation/De-initialisation */
|
||||||
|
|
||||||
|
/* Initialises all members of an rzipstream_t struct,
|
||||||
|
* reading config from existing file header if available */
|
||||||
|
static bool rzipstream_init_stream(
|
||||||
|
rzipstream_t *stream, const char *path, bool is_writing)
|
||||||
|
{
|
||||||
|
unsigned file_mode;
|
||||||
|
|
||||||
|
if (!stream)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Ensure stream has valid initial values */
|
||||||
|
stream->size = 0;
|
||||||
|
stream->chunk_size = RZIP_DEFAULT_CHUNK_SIZE;
|
||||||
|
stream->file = NULL;
|
||||||
|
stream->deflate_backend = NULL;
|
||||||
|
stream->deflate_stream = NULL;
|
||||||
|
stream->inflate_backend = NULL;
|
||||||
|
stream->inflate_stream = NULL;
|
||||||
|
stream->in_buf = NULL;
|
||||||
|
stream->in_buf_size = 0;
|
||||||
|
stream->in_buf_ptr = 0;
|
||||||
|
stream->out_buf = NULL;
|
||||||
|
stream->out_buf_size = 0;
|
||||||
|
stream->out_buf_ptr = 0;
|
||||||
|
stream->out_buf_occupancy = 0;
|
||||||
|
|
||||||
|
/* Check whether this is a read or write stream */
|
||||||
|
stream->is_writing = is_writing;
|
||||||
|
if (stream->is_writing)
|
||||||
|
{
|
||||||
|
/* Written files are always compressed */
|
||||||
|
stream->is_compressed = true;
|
||||||
|
file_mode = RETRO_VFS_FILE_ACCESS_WRITE;
|
||||||
|
}
|
||||||
|
/* For read files, must get compression status
|
||||||
|
* from file itself... */
|
||||||
|
else
|
||||||
|
file_mode = RETRO_VFS_FILE_ACCESS_READ;
|
||||||
|
|
||||||
|
/* Open file */
|
||||||
|
stream->file = filestream_open(
|
||||||
|
path, file_mode, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||||
|
if (!stream->file)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* If file is open for writing, output header
|
||||||
|
* (Size component cannot be written until
|
||||||
|
* file is closed...) */
|
||||||
|
if (stream->is_writing)
|
||||||
|
{
|
||||||
|
/* Note: could just write zeros here, but
|
||||||
|
* still want to identify this as an RZIP
|
||||||
|
* file if writing fails partway through */
|
||||||
|
if (!rzipstream_write_file_header(stream))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* If file is open for reading, parse any existing
|
||||||
|
* header */
|
||||||
|
else if (!rzipstream_read_file_header(stream))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Initialise appropriate transform stream
|
||||||
|
* and determine associated buffer sizes */
|
||||||
|
if (stream->is_writing)
|
||||||
|
{
|
||||||
|
/* Compression */
|
||||||
|
stream->deflate_backend = trans_stream_get_zlib_deflate_backend();
|
||||||
|
if (!stream->deflate_backend)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stream->deflate_stream = stream->deflate_backend->stream_new();
|
||||||
|
if (!stream->deflate_stream)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Set compression level */
|
||||||
|
if (!stream->deflate_backend->define(
|
||||||
|
stream->deflate_stream, "level", RZIP_COMPRESSION_LEVEL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Buffers
|
||||||
|
* > Input: uncompressed
|
||||||
|
* > Output: compressed */
|
||||||
|
stream->in_buf_size = stream->chunk_size;
|
||||||
|
stream->out_buf_size = stream->chunk_size * 2;
|
||||||
|
/* > Account for minimum zlib overhead
|
||||||
|
* of 11 bytes... */
|
||||||
|
stream->out_buf_size =
|
||||||
|
(stream->out_buf_size < (stream->in_buf_size + 11)) ?
|
||||||
|
stream->out_buf_size + 11 :
|
||||||
|
stream->out_buf_size;
|
||||||
|
|
||||||
|
/* Redundant safety check */
|
||||||
|
if ((stream->in_buf_size == 0) ||
|
||||||
|
(stream->out_buf_size == 0))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* When reading, don't need an inflate transform
|
||||||
|
* stream (or buffers) if source file is uncompressed */
|
||||||
|
else if (stream->is_compressed)
|
||||||
|
{
|
||||||
|
/* Decompression */
|
||||||
|
stream->inflate_backend = trans_stream_get_zlib_inflate_backend();
|
||||||
|
if (!stream->inflate_backend)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stream->inflate_stream = stream->inflate_backend->stream_new();
|
||||||
|
if (!stream->inflate_stream)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Buffers
|
||||||
|
* > Input: compressed
|
||||||
|
* > Output: uncompressed
|
||||||
|
* Note 1: Actual compressed chunk sizes are read
|
||||||
|
* from the file - just allocate a sensible
|
||||||
|
* default to minimise memory reallocations
|
||||||
|
* Note 2: If file header is valid, output buffer
|
||||||
|
* should have a size of exactly stream->chunk_size.
|
||||||
|
* Allocate some additional space, just for
|
||||||
|
* redundant safety... */
|
||||||
|
stream->in_buf_size = stream->chunk_size * 2;
|
||||||
|
stream->out_buf_size = stream->chunk_size + (stream->chunk_size >> 2);
|
||||||
|
|
||||||
|
/* Redundant safety check */
|
||||||
|
if ((stream->in_buf_size == 0) ||
|
||||||
|
(stream->out_buf_size == 0))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate buffers */
|
||||||
|
if (stream->in_buf_size > 0)
|
||||||
|
{
|
||||||
|
stream->in_buf = (uint8_t *)calloc(stream->in_buf_size, 1);
|
||||||
|
if (!stream->in_buf)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->out_buf_size > 0)
|
||||||
|
{
|
||||||
|
stream->out_buf = (uint8_t *)calloc(stream->out_buf_size, 1);
|
||||||
|
if (!stream->out_buf)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free()'s all members of an rzipstream_t struct
|
||||||
|
* > Also closes associated file, if currently open */
|
||||||
|
static int rzipstream_free_stream(rzipstream_t *stream)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!stream)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Free transform streams */
|
||||||
|
if (stream->deflate_stream && stream->deflate_backend)
|
||||||
|
stream->deflate_backend->stream_free(stream->deflate_stream);
|
||||||
|
|
||||||
|
stream->deflate_stream = NULL;
|
||||||
|
stream->deflate_backend = NULL;
|
||||||
|
|
||||||
|
if (stream->inflate_stream && stream->inflate_backend)
|
||||||
|
stream->inflate_backend->stream_free(stream->inflate_stream);
|
||||||
|
|
||||||
|
stream->inflate_stream = NULL;
|
||||||
|
stream->inflate_backend = NULL;
|
||||||
|
|
||||||
|
/* Free buffers */
|
||||||
|
if (stream->in_buf)
|
||||||
|
free(stream->in_buf);
|
||||||
|
stream->in_buf = NULL;
|
||||||
|
|
||||||
|
if (stream->out_buf)
|
||||||
|
free(stream->out_buf);
|
||||||
|
stream->out_buf = NULL;
|
||||||
|
|
||||||
|
/* Close file */
|
||||||
|
if (stream->file)
|
||||||
|
ret = filestream_close(stream->file);
|
||||||
|
stream->file = NULL;
|
||||||
|
|
||||||
|
free(stream);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File Open */
|
||||||
|
|
||||||
|
/* Opens a new or existing RZIP file
|
||||||
|
* > Supported 'mode' values are:
|
||||||
|
* - RETRO_VFS_FILE_ACCESS_READ
|
||||||
|
* - RETRO_VFS_FILE_ACCESS_WRITE
|
||||||
|
* > When reading, 'path' may reference compressed
|
||||||
|
* or uncompressed data
|
||||||
|
* Returns NULL if arguments are invalid, file
|
||||||
|
* is invalid or an IO error occurs */
|
||||||
|
rzipstream_t* rzipstream_open(const char *path, unsigned mode)
|
||||||
|
{
|
||||||
|
rzipstream_t *stream = NULL;
|
||||||
|
|
||||||
|
/* Sanity check
|
||||||
|
* > Only RETRO_VFS_FILE_ACCESS_READ and
|
||||||
|
* RETRO_VFS_FILE_ACCESS_WRITE are supported */
|
||||||
|
if (string_is_empty(path) ||
|
||||||
|
((mode != RETRO_VFS_FILE_ACCESS_READ) &&
|
||||||
|
(mode != RETRO_VFS_FILE_ACCESS_WRITE)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* If opening in read mode, ensure file exists */
|
||||||
|
if ((mode == RETRO_VFS_FILE_ACCESS_READ) &&
|
||||||
|
!path_is_valid(path))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Allocate stream object */
|
||||||
|
stream = (rzipstream_t*)calloc(1, sizeof(*stream));
|
||||||
|
if (!stream)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Initialise stream */
|
||||||
|
if (!rzipstream_init_stream(
|
||||||
|
stream, path,
|
||||||
|
(mode == RETRO_VFS_FILE_ACCESS_WRITE)))
|
||||||
|
{
|
||||||
|
rzipstream_free_stream(stream);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File Read */
|
||||||
|
|
||||||
|
/* Reads and decompresses the next chunk of data
|
||||||
|
* in the RZIP file */
|
||||||
|
static bool rzipstream_read_chunk(rzipstream_t *stream)
|
||||||
|
{
|
||||||
|
uint8_t chunk_header_bytes[RZIP_CHUNK_HEADER_SIZE] = {0};
|
||||||
|
uint32_t compressed_chunk_size;
|
||||||
|
uint32_t inflate_read;
|
||||||
|
uint32_t inflate_written;
|
||||||
|
int64_t length;
|
||||||
|
|
||||||
|
if (!stream || !stream->inflate_backend || !stream->inflate_stream)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Attempt to read chunk header bytes */
|
||||||
|
length = filestream_read(
|
||||||
|
stream->file, chunk_header_bytes, sizeof(chunk_header_bytes));
|
||||||
|
if (length != RZIP_CHUNK_HEADER_SIZE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Get size of next compressed chunk */
|
||||||
|
compressed_chunk_size = ((uint32_t)chunk_header_bytes[3] << 24) |
|
||||||
|
((uint32_t)chunk_header_bytes[2] << 16) |
|
||||||
|
((uint32_t)chunk_header_bytes[1] << 8) |
|
||||||
|
(uint32_t)chunk_header_bytes[0];
|
||||||
|
if (compressed_chunk_size == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Resize input buffer, if required */
|
||||||
|
if (compressed_chunk_size > stream->in_buf_size)
|
||||||
|
{
|
||||||
|
free(stream->in_buf);
|
||||||
|
stream->in_buf = NULL;
|
||||||
|
|
||||||
|
stream->in_buf_size = compressed_chunk_size;
|
||||||
|
stream->in_buf = (uint8_t *)calloc(stream->in_buf_size, 1);
|
||||||
|
if (!stream->in_buf)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Note: Uncompressed data size is fixed, and read
|
||||||
|
* from the file header - we therefore don't attempt
|
||||||
|
* to resize the output buffer (if it's too small, then
|
||||||
|
* that's an error condition) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read compressed chunk from file */
|
||||||
|
length = filestream_read(
|
||||||
|
stream->file, stream->in_buf, compressed_chunk_size);
|
||||||
|
if (length != compressed_chunk_size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Decompress chunk data */
|
||||||
|
stream->inflate_backend->set_in(
|
||||||
|
stream->inflate_stream,
|
||||||
|
stream->in_buf, compressed_chunk_size);
|
||||||
|
|
||||||
|
stream->inflate_backend->set_out(
|
||||||
|
stream->inflate_stream,
|
||||||
|
stream->out_buf, stream->out_buf_size);
|
||||||
|
|
||||||
|
/* Note: We have to set 'flush == true' here, otherwise we
|
||||||
|
* can't guarantee that the entire chunk will be written
|
||||||
|
* to the output buffer - this is inefficient, but not
|
||||||
|
* much we can do... */
|
||||||
|
if (!stream->inflate_backend->trans(
|
||||||
|
stream->inflate_stream, true,
|
||||||
|
&inflate_read, &inflate_written, NULL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Error checking */
|
||||||
|
if (inflate_read != compressed_chunk_size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((inflate_written == 0) ||
|
||||||
|
(inflate_written > stream->out_buf_size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Record current output buffer occupancy
|
||||||
|
* and reset pointer */
|
||||||
|
stream->out_buf_occupancy = inflate_written;
|
||||||
|
stream->out_buf_ptr = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reads (a maximum of) 'len' bytes from an RZIP file.
|
||||||
|
* Returns actual number of bytes read, or -1 in
|
||||||
|
* the event of an error */
|
||||||
|
int64_t rzipstream_read(rzipstream_t *stream, void *data, int64_t len)
|
||||||
|
{
|
||||||
|
int64_t data_len = len;
|
||||||
|
uint8_t *data_ptr = (uint8_t *)data;
|
||||||
|
int64_t data_read = 0;
|
||||||
|
|
||||||
|
if (!stream || stream->is_writing)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* If we are reading uncompressed data, simply
|
||||||
|
* 'pass on' the direct file access request */
|
||||||
|
if (!stream->is_compressed)
|
||||||
|
return filestream_read(stream->file, data, len);
|
||||||
|
|
||||||
|
/* Process input data */
|
||||||
|
while (data_len > 0)
|
||||||
|
{
|
||||||
|
uint32_t read_size = 0;
|
||||||
|
|
||||||
|
/* Check whether we have reached the end
|
||||||
|
* of the file */
|
||||||
|
if (stream->virtual_ptr >= stream->size)
|
||||||
|
return data_read;
|
||||||
|
|
||||||
|
/* If everything in the output buffer has already
|
||||||
|
* been read, grab and extract the next chunk
|
||||||
|
* from disk */
|
||||||
|
if (stream->out_buf_ptr >= stream->out_buf_occupancy)
|
||||||
|
if (!rzipstream_read_chunk(stream))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Get amount of data to 'read out' this loop
|
||||||
|
* > i.e. minimum of remaining output buffer
|
||||||
|
* occupancy and remaining 'read data' size */
|
||||||
|
read_size = stream->out_buf_occupancy - stream->out_buf_ptr;
|
||||||
|
read_size = (read_size > data_len) ? data_len : read_size;
|
||||||
|
|
||||||
|
/* Copy as much cached data as possible into
|
||||||
|
* the read buffer */
|
||||||
|
memcpy(data_ptr, stream->out_buf + stream->out_buf_ptr, read_size);
|
||||||
|
|
||||||
|
/* Increment pointers and remaining length */
|
||||||
|
stream->out_buf_ptr += read_size;
|
||||||
|
data_ptr += read_size;
|
||||||
|
data_len -= read_size;
|
||||||
|
|
||||||
|
stream->virtual_ptr += read_size;
|
||||||
|
|
||||||
|
data_read += read_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File Write */
|
||||||
|
|
||||||
|
/* Compresses currently cached data and writes it
|
||||||
|
* as the next RZIP file chunk */
|
||||||
|
static bool rzipstream_write_chunk(rzipstream_t *stream)
|
||||||
|
{
|
||||||
|
uint8_t chunk_header_bytes[RZIP_CHUNK_HEADER_SIZE] = {0};
|
||||||
|
uint32_t deflate_read;
|
||||||
|
uint32_t deflate_written;
|
||||||
|
int64_t length;
|
||||||
|
|
||||||
|
if (!stream || !stream->deflate_backend || !stream->deflate_stream)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Compress data currently held in input buffer */
|
||||||
|
stream->deflate_backend->set_in(
|
||||||
|
stream->deflate_stream,
|
||||||
|
stream->in_buf, stream->in_buf_ptr);
|
||||||
|
|
||||||
|
stream->deflate_backend->set_out(
|
||||||
|
stream->deflate_stream,
|
||||||
|
stream->out_buf, stream->out_buf_size);
|
||||||
|
|
||||||
|
/* Note: We have to set 'flush == true' here, otherwise we
|
||||||
|
* can't guarantee that the entire chunk will be written
|
||||||
|
* to the output buffer - this is inefficient, but not
|
||||||
|
* much we can do... */
|
||||||
|
if (!stream->deflate_backend->trans(
|
||||||
|
stream->deflate_stream, true,
|
||||||
|
&deflate_read, &deflate_written, NULL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Error checking */
|
||||||
|
if (deflate_read != stream->in_buf_ptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((deflate_written == 0) ||
|
||||||
|
(deflate_written > stream->out_buf_size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Write compressed chunk size to file */
|
||||||
|
chunk_header_bytes[3] = (deflate_written >> 24) & 0xFF;
|
||||||
|
chunk_header_bytes[2] = (deflate_written >> 16) & 0xFF;
|
||||||
|
chunk_header_bytes[1] = (deflate_written >> 8) & 0xFF;
|
||||||
|
chunk_header_bytes[0] = deflate_written & 0xFF;
|
||||||
|
|
||||||
|
length = filestream_write(
|
||||||
|
stream->file, chunk_header_bytes, sizeof(chunk_header_bytes));
|
||||||
|
if (length != RZIP_CHUNK_HEADER_SIZE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Write compressed data to file */
|
||||||
|
length = filestream_write(
|
||||||
|
stream->file, stream->out_buf, deflate_written);
|
||||||
|
|
||||||
|
if (length != deflate_written)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Reset input buffer pointer */
|
||||||
|
stream->in_buf_ptr = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Writes 'len' bytes to an RZIP file.
|
||||||
|
* Returns actual number of bytes written, or -1
|
||||||
|
* in the event of an error */
|
||||||
|
int64_t rzipstream_write(rzipstream_t *stream, const void *data, int64_t len)
|
||||||
|
{
|
||||||
|
int64_t data_len = len;
|
||||||
|
const uint8_t *data_ptr = (const uint8_t *)data;
|
||||||
|
|
||||||
|
if (!stream || !stream->is_writing)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Process input data */
|
||||||
|
while (data_len > 0)
|
||||||
|
{
|
||||||
|
uint32_t cache_size = 0;
|
||||||
|
|
||||||
|
/* If input buffer is full, compress and write to disk */
|
||||||
|
if (stream->in_buf_ptr >= stream->in_buf_size)
|
||||||
|
if (!rzipstream_write_chunk(stream))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Get amount of data to cache during this loop
|
||||||
|
* > i.e. minimum of space remaining in input buffer
|
||||||
|
* and remaining 'write data' size */
|
||||||
|
cache_size = stream->in_buf_size - stream->in_buf_ptr;
|
||||||
|
cache_size = (cache_size > data_len) ? data_len : cache_size;
|
||||||
|
|
||||||
|
/* Copy as much data as possible into
|
||||||
|
* the input buffer */
|
||||||
|
memcpy(stream->in_buf + stream->in_buf_ptr, data_ptr, cache_size);
|
||||||
|
|
||||||
|
/* Increment pointers and remaining length */
|
||||||
|
stream->in_buf_ptr += cache_size;
|
||||||
|
data_ptr += cache_size;
|
||||||
|
data_len -= cache_size;
|
||||||
|
|
||||||
|
stream->size += cache_size;
|
||||||
|
stream->virtual_ptr += cache_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We always write the specified number of bytes
|
||||||
|
* (unless rzipstream_write_chunk() fails, in
|
||||||
|
* which we register a complete failure...) */
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File Status */
|
||||||
|
|
||||||
|
/* Returns total size (in bytes) of the *uncompressed*
|
||||||
|
* data in an RZIP file.
|
||||||
|
* (If reading an uncompressed file, this corresponds
|
||||||
|
* to the 'physical' file size in bytes)
|
||||||
|
* Returns -1 in the event of a error. */
|
||||||
|
int64_t rzipstream_get_size(rzipstream_t *stream)
|
||||||
|
{
|
||||||
|
if (!stream)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (stream->is_compressed)
|
||||||
|
return stream->size;
|
||||||
|
else
|
||||||
|
return filestream_get_size(stream->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns EOF when no further *uncompressed* data
|
||||||
|
* can be read from an RZIP file. */
|
||||||
|
int rzipstream_eof(rzipstream_t *stream)
|
||||||
|
{
|
||||||
|
if (!stream)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (stream->is_compressed)
|
||||||
|
return (stream->virtual_ptr >= stream->size) ?
|
||||||
|
EOF : 0;
|
||||||
|
else
|
||||||
|
return filestream_eof(stream->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File Close */
|
||||||
|
|
||||||
|
/* Closes RZIP file. If file is open for writing,
|
||||||
|
* flushes any remaining buffered data to disk.
|
||||||
|
* Returns -1 in the event of a error. */
|
||||||
|
int rzipstream_close(rzipstream_t *stream)
|
||||||
|
{
|
||||||
|
if (!stream)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* If we are writing, ensure that any
|
||||||
|
* remaining uncompressed data is flushed to
|
||||||
|
* disk and update file header */
|
||||||
|
if (stream->is_writing)
|
||||||
|
{
|
||||||
|
if (stream->in_buf_ptr > 0)
|
||||||
|
if (!rzipstream_write_chunk(stream))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (!rzipstream_write_file_header(stream))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free stream
|
||||||
|
* > This also closes the file */
|
||||||
|
return rzipstream_free_stream(stream);
|
||||||
|
|
||||||
|
error:
|
||||||
|
/* Stream must be free()'d regardless */
|
||||||
|
rzipstream_free_stream(stream);
|
||||||
|
return -1;
|
||||||
|
}
|
|
@ -381,6 +381,7 @@ default_sublabel_macro(action_bind_sublabel_perfcnt_enable, MENU_
|
||||||
default_sublabel_macro(action_bind_sublabel_savestate_auto_save, MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_SAVE)
|
default_sublabel_macro(action_bind_sublabel_savestate_auto_save, MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_SAVE)
|
||||||
default_sublabel_macro(action_bind_sublabel_savestate_auto_load, MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_LOAD)
|
default_sublabel_macro(action_bind_sublabel_savestate_auto_load, MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_LOAD)
|
||||||
default_sublabel_macro(action_bind_sublabel_savestate_thumbnail_enable, MENU_ENUM_SUBLABEL_SAVESTATE_THUMBNAIL_ENABLE)
|
default_sublabel_macro(action_bind_sublabel_savestate_thumbnail_enable, MENU_ENUM_SUBLABEL_SAVESTATE_THUMBNAIL_ENABLE)
|
||||||
|
default_sublabel_macro(action_bind_sublabel_savestate_file_compression, MENU_ENUM_SUBLABEL_SAVESTATE_FILE_COMPRESSION)
|
||||||
default_sublabel_macro(action_bind_sublabel_autosave_interval, MENU_ENUM_SUBLABEL_AUTOSAVE_INTERVAL)
|
default_sublabel_macro(action_bind_sublabel_autosave_interval, MENU_ENUM_SUBLABEL_AUTOSAVE_INTERVAL)
|
||||||
default_sublabel_macro(action_bind_sublabel_input_remap_binds_enable, MENU_ENUM_SUBLABEL_INPUT_REMAP_BINDS_ENABLE)
|
default_sublabel_macro(action_bind_sublabel_input_remap_binds_enable, MENU_ENUM_SUBLABEL_INPUT_REMAP_BINDS_ENABLE)
|
||||||
default_sublabel_macro(action_bind_sublabel_input_autodetect_enable, MENU_ENUM_SUBLABEL_INPUT_AUTODETECT_ENABLE)
|
default_sublabel_macro(action_bind_sublabel_input_autodetect_enable, MENU_ENUM_SUBLABEL_INPUT_AUTODETECT_ENABLE)
|
||||||
|
@ -2327,6 +2328,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||||
case MENU_ENUM_LABEL_SAVESTATE_THUMBNAIL_ENABLE:
|
case MENU_ENUM_LABEL_SAVESTATE_THUMBNAIL_ENABLE:
|
||||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_savestate_thumbnail_enable);
|
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_savestate_thumbnail_enable);
|
||||||
break;
|
break;
|
||||||
|
case MENU_ENUM_LABEL_SAVESTATE_FILE_COMPRESSION:
|
||||||
|
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_savestate_file_compression);
|
||||||
|
break;
|
||||||
case MENU_ENUM_LABEL_SAVESTATE_AUTO_SAVE:
|
case MENU_ENUM_LABEL_SAVESTATE_AUTO_SAVE:
|
||||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_savestate_auto_save);
|
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_savestate_auto_save);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -6832,6 +6832,7 @@ unsigned menu_displaylist_build_list(
|
||||||
{MENU_ENUM_LABEL_SAVESTATE_AUTO_SAVE, PARSE_ONLY_BOOL},
|
{MENU_ENUM_LABEL_SAVESTATE_AUTO_SAVE, PARSE_ONLY_BOOL},
|
||||||
{MENU_ENUM_LABEL_SAVESTATE_AUTO_LOAD, PARSE_ONLY_BOOL},
|
{MENU_ENUM_LABEL_SAVESTATE_AUTO_LOAD, PARSE_ONLY_BOOL},
|
||||||
{MENU_ENUM_LABEL_SAVESTATE_THUMBNAIL_ENABLE, PARSE_ONLY_BOOL},
|
{MENU_ENUM_LABEL_SAVESTATE_THUMBNAIL_ENABLE, PARSE_ONLY_BOOL},
|
||||||
|
{MENU_ENUM_LABEL_SAVESTATE_FILE_COMPRESSION, PARSE_ONLY_BOOL},
|
||||||
{MENU_ENUM_LABEL_SAVEFILES_IN_CONTENT_DIR_ENABLE, PARSE_ONLY_BOOL},
|
{MENU_ENUM_LABEL_SAVEFILES_IN_CONTENT_DIR_ENABLE, PARSE_ONLY_BOOL},
|
||||||
{MENU_ENUM_LABEL_SAVESTATES_IN_CONTENT_DIR_ENABLE, PARSE_ONLY_BOOL},
|
{MENU_ENUM_LABEL_SAVESTATES_IN_CONTENT_DIR_ENABLE, PARSE_ONLY_BOOL},
|
||||||
{MENU_ENUM_LABEL_SYSTEMFILES_IN_CONTENT_DIR_ENABLE, PARSE_ONLY_BOOL},
|
{MENU_ENUM_LABEL_SYSTEMFILES_IN_CONTENT_DIR_ENABLE, PARSE_ONLY_BOOL},
|
||||||
|
|
|
@ -8950,6 +8950,24 @@ static bool setting_append_list(
|
||||||
general_read_handler,
|
general_read_handler,
|
||||||
SD_FLAG_NONE);
|
SD_FLAG_NONE);
|
||||||
|
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
CONFIG_BOOL(
|
||||||
|
list, list_info,
|
||||||
|
&settings->bools.savestate_file_compression,
|
||||||
|
MENU_ENUM_LABEL_SAVESTATE_FILE_COMPRESSION,
|
||||||
|
MENU_ENUM_LABEL_VALUE_SAVESTATE_FILE_COMPRESSION,
|
||||||
|
DEFAULT_SAVESTATE_FILE_COMPRESSION,
|
||||||
|
MENU_ENUM_LABEL_VALUE_OFF,
|
||||||
|
MENU_ENUM_LABEL_VALUE_ON,
|
||||||
|
&group_info,
|
||||||
|
&subgroup_info,
|
||||||
|
parent_group,
|
||||||
|
general_write_handler,
|
||||||
|
general_read_handler,
|
||||||
|
SD_FLAG_NONE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* TODO/FIXME: This is in the wrong group... */
|
||||||
CONFIG_BOOL(
|
CONFIG_BOOL(
|
||||||
list, list_info,
|
list, list_info,
|
||||||
&settings->bools.scan_without_core_match,
|
&settings->bools.scan_without_core_match,
|
||||||
|
|
|
@ -1607,6 +1607,7 @@ enum msg_hash_enums
|
||||||
MENU_LABEL(SAVESTATE_AUTO_SAVE),
|
MENU_LABEL(SAVESTATE_AUTO_SAVE),
|
||||||
MENU_LABEL(SAVESTATE_AUTO_LOAD),
|
MENU_LABEL(SAVESTATE_AUTO_LOAD),
|
||||||
MENU_LABEL(SAVESTATE_THUMBNAIL_ENABLE),
|
MENU_LABEL(SAVESTATE_THUMBNAIL_ENABLE),
|
||||||
|
MENU_LABEL(SAVESTATE_FILE_COMPRESSION),
|
||||||
|
|
||||||
MENU_LABEL(SUSPEND_SCREENSAVER_ENABLE),
|
MENU_LABEL(SUSPEND_SCREENSAVER_ENABLE),
|
||||||
MENU_ENUM_LABEL_VOLUME_UP,
|
MENU_ENUM_LABEL_VOLUME_UP,
|
||||||
|
|
|
@ -7818,7 +7818,14 @@ static void command_event_undo_save_state(char *s, size_t len)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!content_undo_save_state())
|
if (!content_undo_save_state())
|
||||||
|
{
|
||||||
|
strlcpy(s,
|
||||||
|
msg_hash_to_str(MSG_FAILED_TO_UNDO_SAVE_STATE), len);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(s,
|
||||||
|
msg_hash_to_str(MSG_UNDOING_SAVE_STATE), len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void command_event_undo_load_state(char *s, size_t len)
|
static void command_event_undo_load_state(char *s, size_t len)
|
||||||
|
|
|
@ -101,6 +101,7 @@ typedef struct
|
||||||
int state_slot;
|
int state_slot;
|
||||||
bool thumbnail_enable;
|
bool thumbnail_enable;
|
||||||
bool has_valid_framebuffer;
|
bool has_valid_framebuffer;
|
||||||
|
bool compress_files;
|
||||||
} save_task_state_t;
|
} save_task_state_t;
|
||||||
|
|
||||||
typedef save_task_state_t load_task_data_t;
|
typedef save_task_state_t load_task_data_t;
|
||||||
|
@ -560,7 +561,12 @@ static void *get_serialized_data(const char *path, size_t serial_size)
|
||||||
if (!serial_size)
|
if (!serial_size)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
data = malloc(serial_size);
|
/* Ensure buffer is initialised to zero
|
||||||
|
* > Prevents inconsistent compressed state file
|
||||||
|
* sizes when core requests a larger buffer
|
||||||
|
* than it needs (and leaves the excess
|
||||||
|
* as uninitialised garbage) */
|
||||||
|
data = calloc(serial_size, 1);
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -597,9 +603,13 @@ static void task_save_handler(retro_task_t *task)
|
||||||
|
|
||||||
if (!state->file)
|
if (!state->file)
|
||||||
{
|
{
|
||||||
state->file = intfstream_open_file(
|
if (state->compress_files)
|
||||||
state->path, RETRO_VFS_FILE_ACCESS_WRITE,
|
state->file = intfstream_open_rzip_file(
|
||||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
state->path, RETRO_VFS_FILE_ACCESS_WRITE);
|
||||||
|
else
|
||||||
|
state->file = intfstream_open_file(
|
||||||
|
state->path, RETRO_VFS_FILE_ACCESS_WRITE,
|
||||||
|
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||||
|
|
||||||
if (!state->file)
|
if (!state->file)
|
||||||
return;
|
return;
|
||||||
|
@ -694,6 +704,11 @@ static bool task_push_undo_save_state(const char *path, void *data, size_t size)
|
||||||
retro_task_t *task = task_init();
|
retro_task_t *task = task_init();
|
||||||
save_task_state_t *state = (save_task_state_t*)calloc(1, sizeof(*state));
|
save_task_state_t *state = (save_task_state_t*)calloc(1, sizeof(*state));
|
||||||
settings_t *settings = config_get_ptr();
|
settings_t *settings = config_get_ptr();
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
bool compress_files = settings->bools.savestate_file_compression;
|
||||||
|
#else
|
||||||
|
bool compress_files = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!task || !state)
|
if (!task || !state)
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -704,6 +719,7 @@ static bool task_push_undo_save_state(const char *path, void *data, size_t size)
|
||||||
state->undo_save = true;
|
state->undo_save = true;
|
||||||
state->state_slot = settings->ints.state_slot;
|
state->state_slot = settings->ints.state_slot;
|
||||||
state->has_valid_framebuffer = video_driver_cached_frame_has_valid_framebuffer();
|
state->has_valid_framebuffer = video_driver_cached_frame_has_valid_framebuffer();
|
||||||
|
state->compress_files = compress_files;
|
||||||
|
|
||||||
task->type = TASK_TYPE_BLOCKING;
|
task->type = TASK_TYPE_BLOCKING;
|
||||||
task->state = state;
|
task->state = state;
|
||||||
|
@ -787,23 +803,26 @@ static void task_load_handler(retro_task_t *task)
|
||||||
|
|
||||||
if (!state->file)
|
if (!state->file)
|
||||||
{
|
{
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
/* Always use RZIP interface when reading state
|
||||||
|
* files - this will automatically handle uncompressed
|
||||||
|
* data */
|
||||||
|
state->file = intfstream_open_rzip_file(state->path,
|
||||||
|
RETRO_VFS_FILE_ACCESS_READ);
|
||||||
|
#else
|
||||||
state->file = intfstream_open_file(state->path,
|
state->file = intfstream_open_file(state->path,
|
||||||
RETRO_VFS_FILE_ACCESS_READ,
|
RETRO_VFS_FILE_ACCESS_READ,
|
||||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!state->file)
|
if (!state->file)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (intfstream_seek(state->file, 0, SEEK_END) != 0)
|
state->size = intfstream_get_size(state->file);
|
||||||
goto error;
|
|
||||||
|
|
||||||
state->size = intfstream_tell(state->file);
|
|
||||||
|
|
||||||
if (state->size < 0)
|
if (state->size < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
intfstream_rewind(state->file);
|
|
||||||
|
|
||||||
state->data = malloc(state->size + 1);
|
state->data = malloc(state->size + 1);
|
||||||
|
|
||||||
if (!state->data)
|
if (!state->data)
|
||||||
|
@ -1076,6 +1095,11 @@ static void task_push_save_state(const char *path, void *data, size_t size, bool
|
||||||
settings_t *settings = config_get_ptr();
|
settings_t *settings = config_get_ptr();
|
||||||
bool savestate_thumbnail_enable = settings->bools.savestate_thumbnail_enable;
|
bool savestate_thumbnail_enable = settings->bools.savestate_thumbnail_enable;
|
||||||
int state_slot = settings->ints.state_slot;
|
int state_slot = settings->ints.state_slot;
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
bool compress_files = settings->bools.savestate_file_compression;
|
||||||
|
#else
|
||||||
|
bool compress_files = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!task || !state)
|
if (!task || !state)
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1088,6 +1112,7 @@ static void task_push_save_state(const char *path, void *data, size_t size, bool
|
||||||
state->thumbnail_enable = savestate_thumbnail_enable;
|
state->thumbnail_enable = savestate_thumbnail_enable;
|
||||||
state->state_slot = state_slot;
|
state->state_slot = state_slot;
|
||||||
state->has_valid_framebuffer = video_driver_cached_frame_has_valid_framebuffer();
|
state->has_valid_framebuffer = video_driver_cached_frame_has_valid_framebuffer();
|
||||||
|
state->compress_files = compress_files;
|
||||||
|
|
||||||
task->type = TASK_TYPE_BLOCKING;
|
task->type = TASK_TYPE_BLOCKING;
|
||||||
task->state = state;
|
task->state = state;
|
||||||
|
@ -1161,6 +1186,11 @@ static void task_push_load_and_save_state(const char *path, void *data,
|
||||||
retro_task_t *task = NULL;
|
retro_task_t *task = NULL;
|
||||||
settings_t *settings = config_get_ptr();
|
settings_t *settings = config_get_ptr();
|
||||||
int state_slot = settings->ints.state_slot;
|
int state_slot = settings->ints.state_slot;
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
bool compress_files = settings->bools.savestate_file_compression;
|
||||||
|
#else
|
||||||
|
bool compress_files = false;
|
||||||
|
#endif
|
||||||
save_task_state_t *state = (save_task_state_t*)
|
save_task_state_t *state = (save_task_state_t*)
|
||||||
calloc(1, sizeof(*state));
|
calloc(1, sizeof(*state));
|
||||||
|
|
||||||
|
@ -1188,6 +1218,7 @@ static void task_push_load_and_save_state(const char *path, void *data,
|
||||||
state->state_slot = state_slot;
|
state->state_slot = state_slot;
|
||||||
state->has_valid_framebuffer =
|
state->has_valid_framebuffer =
|
||||||
video_driver_cached_frame_has_valid_framebuffer();
|
video_driver_cached_frame_has_valid_framebuffer();
|
||||||
|
state->compress_files = compress_files;
|
||||||
|
|
||||||
task->state = state;
|
task->state = state;
|
||||||
task->type = TASK_TYPE_BLOCKING;
|
task->type = TASK_TYPE_BLOCKING;
|
||||||
|
@ -1318,6 +1349,11 @@ bool content_load_state(const char *path,
|
||||||
save_task_state_t *state = (save_task_state_t*)calloc(1, sizeof(*state));
|
save_task_state_t *state = (save_task_state_t*)calloc(1, sizeof(*state));
|
||||||
settings_t *settings = config_get_ptr();
|
settings_t *settings = config_get_ptr();
|
||||||
int state_slot = settings->ints.state_slot;
|
int state_slot = settings->ints.state_slot;
|
||||||
|
#if defined(HAVE_ZLIB)
|
||||||
|
bool compress_files = settings->bools.savestate_file_compression;
|
||||||
|
#else
|
||||||
|
bool compress_files = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!task || !state)
|
if (!task || !state)
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1328,6 +1364,7 @@ bool content_load_state(const char *path,
|
||||||
state->state_slot = state_slot;
|
state->state_slot = state_slot;
|
||||||
state->has_valid_framebuffer =
|
state->has_valid_framebuffer =
|
||||||
video_driver_cached_frame_has_valid_framebuffer();
|
video_driver_cached_frame_has_valid_framebuffer();
|
||||||
|
state->compress_files = compress_files;
|
||||||
|
|
||||||
task->type = TASK_TYPE_BLOCKING;
|
task->type = TASK_TYPE_BLOCKING;
|
||||||
task->state = state;
|
task->state = state;
|
||||||
|
|
Loading…
Reference in New Issue