mirror of
https://github.com/libretro/scummvm.git
synced 2025-03-05 09:49:14 +00:00
Replaced the old code for compressed savegames (which was using the gzopen/gzread/etc. API, and thuse tied to FILE/fopen/fread/etc.) with a new wrapper approach, which allows reading/writing gzip data via arbitrary SaveFile implementations, and thus can be used with custom savefile implementations
svn-id: r25669
This commit is contained in:
parent
5fc65f2230
commit
7f07e6e48a
@ -19,6 +19,7 @@ MODULE_OBJS := \
|
||||
plugins/win32/win32-provider.o \
|
||||
saves/savefile.o \
|
||||
saves/default/default-saves.o \
|
||||
saves/compressed/compressed-saves.o \
|
||||
timer/default/default-timer.o
|
||||
|
||||
# Include common rules
|
||||
|
277
backends/saves/compressed/compressed-saves.cpp
Normal file
277
backends/saves/compressed/compressed-saves.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2002-2006 The ScummVM project
|
||||
*
|
||||
* This program 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.
|
||||
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/stdafx.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/util.h"
|
||||
#include "backends/saves/compressed/compressed-saves.h"
|
||||
|
||||
#if defined(USE_ZLIB)
|
||||
#include <zlib.h>
|
||||
|
||||
|
||||
/**
|
||||
* A simple wrapper class which can be used to wrap around an arbitrary
|
||||
* other InSaveFile and will then provide on-the-fly decompression support.
|
||||
* Assumes the compressed data to be in gzip format.
|
||||
*/
|
||||
class CompressedInSaveFile : public Common::InSaveFile {
|
||||
protected:
|
||||
enum {
|
||||
BUFSIZE = 16384 // 1 << MAX_WBITS
|
||||
};
|
||||
|
||||
byte _buf[BUFSIZE];
|
||||
|
||||
Common::InSaveFile *_wrapped;
|
||||
z_stream _stream;
|
||||
int _zlibErr;
|
||||
uint32 _pos;
|
||||
uint32 _origSize;
|
||||
|
||||
public:
|
||||
|
||||
CompressedInSaveFile(Common::InSaveFile *w) : _wrapped(w) {
|
||||
assert(w != 0);
|
||||
|
||||
_stream.zalloc = Z_NULL;
|
||||
_stream.zfree = Z_NULL;
|
||||
_stream.opaque = Z_NULL;
|
||||
|
||||
// Verify file header is correct once more
|
||||
w->seek(0, SEEK_SET);
|
||||
assert(w->readUint16BE() == 0x1F8B);
|
||||
|
||||
// Retrieve the original file size
|
||||
w->seek(-4, SEEK_END);
|
||||
_origSize = w->readUint32LE();
|
||||
_pos = 0;
|
||||
w->seek(0, SEEK_SET);
|
||||
|
||||
// Adding 32 to windowBits indicates to zlib that it is supposed to
|
||||
// automatically detect whether gzip or zlib headers are used for
|
||||
// the compressed file.
|
||||
_zlibErr = inflateInit2(&_stream, MAX_WBITS + 32);
|
||||
if (_zlibErr != Z_OK)
|
||||
return;
|
||||
|
||||
// Setup input buffer
|
||||
_stream.next_in = _buf;
|
||||
_stream.avail_in = 0;
|
||||
}
|
||||
|
||||
~CompressedInSaveFile() {
|
||||
inflateEnd(&_stream);
|
||||
delete _wrapped;
|
||||
}
|
||||
|
||||
bool ioFailed() const { return (_zlibErr != Z_OK); }
|
||||
void clearIOFailed() { /* errors here are not recoverable! */ }
|
||||
|
||||
uint32 read(void *dataPtr, uint32 dataSize) {
|
||||
_stream.next_out = (byte *)dataPtr;
|
||||
_stream.avail_out = dataSize;
|
||||
|
||||
// Keep going while we get no error
|
||||
while (_zlibErr == Z_OK && _stream.avail_out) {
|
||||
if (_stream.avail_in == 0 && !_wrapped->eos()) {
|
||||
// If we are out of input data: Read more data, if available.
|
||||
_stream.next_in = _buf;
|
||||
_stream.avail_in = _wrapped->read(_buf, BUFSIZE);
|
||||
}
|
||||
_zlibErr = inflate(&_stream, Z_NO_FLUSH);
|
||||
}
|
||||
|
||||
// Update the position counter
|
||||
_pos += dataSize - _stream.avail_out;
|
||||
|
||||
return dataSize - _stream.avail_out;
|
||||
}
|
||||
|
||||
bool eos() const {
|
||||
return (_zlibErr == Z_STREAM_END);
|
||||
//return _pos == _origSize;
|
||||
}
|
||||
uint32 pos() const {
|
||||
return _pos;
|
||||
}
|
||||
uint32 size() const {
|
||||
return _origSize;
|
||||
}
|
||||
void seek(int32 offset, int whence = SEEK_SET) {
|
||||
int32 newPos;
|
||||
switch(whence) {
|
||||
case SEEK_END:
|
||||
newPos = size() - offset;
|
||||
break;
|
||||
case SEEK_SET:
|
||||
newPos = offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
newPos = _pos + offset;
|
||||
}
|
||||
offset = newPos - _pos;
|
||||
|
||||
if (offset < 0)
|
||||
error("Backward seeking not supported in compressed savefiles");
|
||||
|
||||
// We could implement backward seeking, but it is tricky to do efficiently.
|
||||
// A simple solution would be to restart the whole decompression from the
|
||||
// start of the file. Or we could decompress the whole file in one go
|
||||
// in the constructor, and wrap it into a MemoryReadStream -- but that
|
||||
// would be rather wasteful. As long as we don't need it, I'd rather not
|
||||
// implement this at all. -- Fingolfin
|
||||
|
||||
// Skip the given amount of data (very inefficient if one tries to skip
|
||||
// huge amounts of data, but usually client code will only skip a few
|
||||
// bytes, so this should be fine.
|
||||
byte tmpBuf[1024];
|
||||
while (!ioFailed() && offset > 0) {
|
||||
offset -= read(tmpBuf, MIN((int32)sizeof(tmpBuf), offset));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A simple wrapper class which can be used to wrap around an arbitrary
|
||||
* other OutSaveFile and will then provide on-the-fly compression support.
|
||||
* The compressed data is written in the gzip format.
|
||||
*/
|
||||
class CompressedOutSaveFile : public Common::OutSaveFile {
|
||||
protected:
|
||||
enum {
|
||||
BUFSIZE = 16384 // 1 << MAX_WBITS
|
||||
};
|
||||
|
||||
byte _buf[BUFSIZE];
|
||||
Common::OutSaveFile *_wrapped;
|
||||
z_stream _stream;
|
||||
int _zlibErr;
|
||||
|
||||
void processData(int flushType) {
|
||||
// This function is called by both write() and finalize.
|
||||
while (_zlibErr == Z_OK && (_stream.avail_in || flushType == Z_FINISH)) {
|
||||
if (_stream.avail_out == 0) {
|
||||
if (_wrapped->write(_buf, BUFSIZE) != BUFSIZE) {
|
||||
_zlibErr = Z_ERRNO;
|
||||
break;
|
||||
}
|
||||
_stream.next_out = _buf;
|
||||
_stream.avail_out = BUFSIZE;
|
||||
}
|
||||
_zlibErr = deflate(&_stream, flushType);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CompressedOutSaveFile(Common::OutSaveFile *w) : _wrapped(w) {
|
||||
assert(w != 0);
|
||||
_stream.zalloc = Z_NULL;
|
||||
_stream.zfree = Z_NULL;
|
||||
_stream.opaque = Z_NULL;
|
||||
|
||||
// adding 16 to windowBits indicates to zlib that it is supposed to
|
||||
// write gzip headers
|
||||
_zlibErr = deflateInit2(&_stream,
|
||||
Z_DEFAULT_COMPRESSION,
|
||||
Z_DEFLATED,
|
||||
MAX_WBITS + 16,
|
||||
8,
|
||||
Z_DEFAULT_STRATEGY);
|
||||
assert(_zlibErr == Z_OK);
|
||||
|
||||
_stream.next_out = _buf;
|
||||
_stream.avail_out = BUFSIZE;
|
||||
}
|
||||
|
||||
~CompressedOutSaveFile() {
|
||||
finalize();
|
||||
deflateEnd(&_stream);
|
||||
delete _wrapped;
|
||||
}
|
||||
|
||||
bool ioFailed() const {
|
||||
return (_zlibErr != Z_OK && _zlibErr != Z_STREAM_END) || _wrapped->ioFailed();
|
||||
}
|
||||
|
||||
void clearIOFailed() {
|
||||
// Note: we don't reset the _zlibErr here, as it is not
|
||||
// clear in general ho
|
||||
_wrapped->clearIOFailed();
|
||||
}
|
||||
|
||||
void finalize() {
|
||||
if (_zlibErr != Z_OK)
|
||||
return;
|
||||
|
||||
// Process whatever remaining data there is.
|
||||
processData(Z_FINISH);
|
||||
|
||||
// Since processData only writes out blocks of size BUFSIZE,
|
||||
// we may have to flush some stragglers.
|
||||
uint remainder = BUFSIZE - _stream.avail_out;
|
||||
if (remainder > 0) {
|
||||
if (_wrapped->write(_buf, remainder) != remainder) {
|
||||
_zlibErr = Z_ERRNO;
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the wrapped savefile, too
|
||||
_wrapped->finalize();
|
||||
}
|
||||
|
||||
uint32 write(const void *dataPtr, uint32 dataSize) {
|
||||
if (ioFailed())
|
||||
return 0;
|
||||
|
||||
// Hook in the new data ...
|
||||
_stream.next_in = (Bytef*)dataPtr;
|
||||
_stream.avail_in = dataSize;
|
||||
|
||||
// ... and flush it to disk
|
||||
processData(Z_NO_FLUSH);
|
||||
|
||||
return dataSize - _stream.avail_in;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // USE_ZLIB
|
||||
|
||||
Common::InSaveFile *wrapInSaveFile(Common::InSaveFile *toBeWrapped) {
|
||||
#if defined(USE_ZLIB)
|
||||
if (toBeWrapped) {
|
||||
bool isCompressed = (toBeWrapped->readUint16BE() == 0x1F8B);
|
||||
toBeWrapped->seek(-2, SEEK_CUR);
|
||||
if (isCompressed)
|
||||
return new CompressedInSaveFile(toBeWrapped);
|
||||
}
|
||||
#endif
|
||||
return toBeWrapped;
|
||||
}
|
||||
|
||||
Common::OutSaveFile *wrapOutSaveFile(Common::OutSaveFile *toBeWrapped) {
|
||||
#if defined(USE_ZLIB)
|
||||
if (toBeWrapped)
|
||||
return new CompressedOutSaveFile(toBeWrapped);
|
||||
#endif
|
||||
return toBeWrapped;
|
||||
}
|
51
backends/saves/compressed/compressed-saves.h
Normal file
51
backends/saves/compressed/compressed-saves.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2002-2006 The ScummVM project
|
||||
*
|
||||
* This program 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.
|
||||
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKEND_SAVES_COMPRESSED_H
|
||||
#define BACKEND_SAVES_COMPRESSED_H
|
||||
|
||||
#include "common/stdafx.h"
|
||||
#include "common/savefile.h"
|
||||
|
||||
/**
|
||||
* Take an arbitrary InSaveFile and wrap it in a high level InSaveFile which
|
||||
* provides transparent on-the-fly decompression support.
|
||||
* Assumes the data it retrieves from the wrapped savefile to be either
|
||||
* uncompressed or in gzip format. In the former case, the original
|
||||
* savefile is returned unmodified (and in particular, not wrapped).
|
||||
*
|
||||
* It is safe to call this with a NULL parameter (in this case, NULL is
|
||||
* returned).
|
||||
*/
|
||||
Common::InSaveFile *wrapInSaveFile(Common::InSaveFile *toBeWrapped);
|
||||
|
||||
/**
|
||||
* Take an arbitrary OutSaveFile and wrap it in a high level OutSaveFile which
|
||||
* provides transparent on-the-fly compression support.
|
||||
* The compressed data is written in the gzip format.
|
||||
*
|
||||
* It is safe to call this with a NULL parameter (in this case, NULL is
|
||||
* returned).
|
||||
*/
|
||||
Common::OutSaveFile *wrapOutSaveFile(Common::OutSaveFile *toBeWrapped);
|
||||
|
||||
#endif
|
@ -24,14 +24,11 @@
|
||||
#include "common/savefile.h"
|
||||
#include "common/util.h"
|
||||
#include "backends/saves/default/default-saves.h"
|
||||
#include "backends/saves/compressed/compressed-saves.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#if defined(UNIX) || defined(__SYMBIAN32__)
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
@ -85,70 +82,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
class GzipSaveFile : public Common::InSaveFile, public Common::OutSaveFile {
|
||||
private:
|
||||
gzFile fh;
|
||||
bool _ioError;
|
||||
public:
|
||||
GzipSaveFile(const char *filename, bool saveOrLoad) {
|
||||
_ioError = false;
|
||||
fh = gzopen(filename, (saveOrLoad? "wb" : "rb"));
|
||||
}
|
||||
~GzipSaveFile() {
|
||||
if (fh)
|
||||
gzclose(fh);
|
||||
}
|
||||
|
||||
bool eos() const { return gzeof(fh) != 0; }
|
||||
bool ioFailed() const { return _ioError; }
|
||||
void clearIOFailed() { _ioError = false; }
|
||||
|
||||
bool isOpen() const { return fh != 0; }
|
||||
|
||||
uint32 read(void *dataPtr, uint32 dataSize) {
|
||||
assert(fh);
|
||||
int ret = gzread(fh, dataPtr, dataSize);
|
||||
if (ret <= -1)
|
||||
_ioError = true;
|
||||
return ret;
|
||||
}
|
||||
uint32 write(const void *dataPtr, uint32 dataSize) {
|
||||
assert(fh);
|
||||
// Due to a "bug" in the zlib headers (or maybe I should say,
|
||||
// a bug in the C++ spec? Whatever <g>) we have to be a bit
|
||||
// hackish here and remove the const qualifier.
|
||||
// Note that gzwrite's buf param is declared as "const voidp"
|
||||
// which you might think is the same as "const void *" but it
|
||||
// is not - rather it is equal to "void const *" which is the
|
||||
// same as "void *". Hrmpf
|
||||
int ret = gzwrite(fh, const_cast<void *>(dataPtr), dataSize);
|
||||
if (ret <= 0)
|
||||
_ioError = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32 pos() const {
|
||||
assert(fh);
|
||||
return gztell(fh);
|
||||
}
|
||||
uint32 size() const {
|
||||
assert(fh);
|
||||
uint32 oldPos = gztell(fh);
|
||||
gzseek(fh, 0, SEEK_END);
|
||||
uint32 length = gztell(fh);
|
||||
gzseek(fh, oldPos, SEEK_SET);
|
||||
return length;
|
||||
}
|
||||
|
||||
void seek(int32 offs, int whence = SEEK_SET) {
|
||||
assert(fh);
|
||||
gzseek(fh, offs, whence);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
static void join_paths(const char *filename, const char *directory,
|
||||
char *buf, int bufsize) {
|
||||
buf[bufsize-1] = '\0';
|
||||
@ -219,34 +152,26 @@ Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const char *filename)
|
||||
|
||||
join_paths(filename, savePath, buf, sizeof(buf));
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
GzipSaveFile *sf = new GzipSaveFile(buf, true);
|
||||
#else
|
||||
StdioSaveFile *sf = new StdioSaveFile(buf, true);
|
||||
#endif
|
||||
|
||||
if (!sf->isOpen()) {
|
||||
delete sf;
|
||||
sf = 0;
|
||||
}
|
||||
return sf;
|
||||
return wrapOutSaveFile(sf);
|
||||
}
|
||||
|
||||
Common::InSaveFile *DefaultSaveFileManager::openForLoading(const char *filename) {
|
||||
char buf[256];
|
||||
join_paths(filename, getSavePath(), buf, sizeof(buf));
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
GzipSaveFile *sf = new GzipSaveFile(buf, false);
|
||||
#else
|
||||
StdioSaveFile *sf = new StdioSaveFile(buf, false);
|
||||
#endif
|
||||
|
||||
if (!sf->isOpen()) {
|
||||
delete sf;
|
||||
sf = 0;
|
||||
}
|
||||
return sf;
|
||||
return wrapInSaveFile(sf);
|
||||
}
|
||||
|
||||
void DefaultSaveFileManager::listSavefiles(const char * /* prefix */, bool *marks, int num) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user