[Scummvm-cvs-logs] SF.net SVN: scummvm: [25669] scummvm/trunk/backends

fingolfin at users.sourceforge.net fingolfin at users.sourceforge.net
Sun Feb 18 03:25:41 CET 2007


Revision: 25669
          http://scummvm.svn.sourceforge.net/scummvm/?rev=25669&view=rev
Author:   fingolfin
Date:     2007-02-17 18:25:39 -0800 (Sat, 17 Feb 2007)

Log Message:
-----------
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

Modified Paths:
--------------
    scummvm/trunk/backends/module.mk
    scummvm/trunk/backends/saves/default/default-saves.cpp

Added Paths:
-----------
    scummvm/trunk/backends/saves/compressed/
    scummvm/trunk/backends/saves/compressed/compressed-saves.cpp
    scummvm/trunk/backends/saves/compressed/compressed-saves.h

Modified: scummvm/trunk/backends/module.mk
===================================================================
--- scummvm/trunk/backends/module.mk	2007-02-18 01:05:27 UTC (rev 25668)
+++ scummvm/trunk/backends/module.mk	2007-02-18 02:25:39 UTC (rev 25669)
@@ -19,6 +19,7 @@
 	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 


Property changes on: scummvm/trunk/backends/saves/compressed
___________________________________________________________________
Name: svn:ignore
   + .deps
*.o
lib*.a


Added: scummvm/trunk/backends/saves/compressed/compressed-saves.cpp
===================================================================
--- scummvm/trunk/backends/saves/compressed/compressed-saves.cpp	                        (rev 0)
+++ scummvm/trunk/backends/saves/compressed/compressed-saves.cpp	2007-02-18 02:25:39 UTC (rev 25669)
@@ -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;
+}


Property changes on: scummvm/trunk/backends/saves/compressed/compressed-saves.cpp
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Date Rev Author URL Id
Name: svn:eol-style
   + native

Added: scummvm/trunk/backends/saves/compressed/compressed-saves.h
===================================================================
--- scummvm/trunk/backends/saves/compressed/compressed-saves.h	                        (rev 0)
+++ scummvm/trunk/backends/saves/compressed/compressed-saves.h	2007-02-18 02:25:39 UTC (rev 25669)
@@ -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


Property changes on: scummvm/trunk/backends/saves/compressed/compressed-saves.h
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Date Rev Author URL Id
Name: svn:eol-style
   + native

Modified: scummvm/trunk/backends/saves/default/default-saves.cpp
===================================================================
--- scummvm/trunk/backends/saves/default/default-saves.cpp	2007-02-18 01:05:27 UTC (rev 25668)
+++ scummvm/trunk/backends/saves/default/default-saves.cpp	2007-02-18 02:25:39 UTC (rev 25669)
@@ -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 @@
 };
 
 
-#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 @@
 
 	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) {


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list