diff --git a/Aquaria/Continuity.cpp b/Aquaria/Continuity.cpp index 5ebb39b..e28e5fc 100644 --- a/Aquaria/Continuity.cpp +++ b/Aquaria/Continuity.cpp @@ -2468,6 +2468,9 @@ void Continuity::saveFile(int slot, Vector position, unsigned char *scrShotData, doc.InsertEndChild(startData); + + // FIXME: Patch TinyXML to write out a string and compress in-memory + doc.SaveFile(dsq->getSaveDirectory() + "/poot.tmp"); packFile(dsq->getSaveDirectory() + "/poot.tmp", getSaveFileName(slot, "aqs"), 9); @@ -2483,40 +2486,22 @@ std::string Continuity::getSaveFileName(int slot, const std::string &pfix) void Continuity::loadFileData(int slot, TiXmlDocument &doc) { - bool tmp = false; - std::string teh_file = dsq->continuity.getSaveFileName(slot, "aqs"); - - if (!exists(teh_file, false)) + if (exists(teh_file)) { - teh_file = dsq->continuity.getSaveFileName(slot, "sav"); - - if (!exists(teh_file, false)) - { - teh_file = dsq->continuity.getSaveFileName(slot, "xml"); - } - else - { - uncrunchFile(teh_file, dsq->getSaveDirectory() + "/poot2.tmp"); - unpackFile(dsq->getSaveDirectory() + "/poot2.tmp", dsq->getSaveDirectory() + "/poot.tmp"); - remove((dsq->getSaveDirectory() + "/poot2.tmp").c_str()); - - teh_file = dsq->getSaveDirectory() + "/poot.tmp"; - tmp = true; - } - } - else - { - unpackFile(teh_file, dsq->getSaveDirectory() + "/poot.tmp"); - - teh_file = dsq->getSaveDirectory() + "/poot.tmp"; - tmp = true; + unsigned long size = 0; + char *buf = readCompressedFile(teh_file, &size); + if (!doc.LoadMem(buf, size)) + errorLog("Failed to load save data: " + teh_file); + return; } - doc.LoadFile(teh_file); - - if (tmp) - remove(teh_file.c_str()); + teh_file = dsq->continuity.getSaveFileName(slot, "xml"); + if (exists(teh_file)) + { + if (!doc.LoadFile(teh_file)) + errorLog("Failed to load save data: " + teh_file); + } } void Continuity::loadFile(int slot) diff --git a/Aquaria/DSQ.cpp b/Aquaria/DSQ.cpp index 036b234..83cbbdf 100644 --- a/Aquaria/DSQ.cpp +++ b/Aquaria/DSQ.cpp @@ -3212,6 +3212,7 @@ void DSQ::doSaveSlotMenu(SaveSlotMode ssm, const Vector &position) tgaSave(tempfile.c_str(), scrShotWidth, scrShotHeight, 32, scrShotData); scrShotData = 0; // deleted by tgaSave() + // FIXME: Get rid of tempfile and compress in-memory packFile(dsq->getSaveDirectory() + "/poot-s.tmp", os.str(),9); remove((dsq->getSaveDirectory() + "/poot-s.tmp").c_str()); } diff --git a/BBGE/Base.cpp b/BBGE/Base.cpp index 714d820..ebe3257 100644 --- a/BBGE/Base.cpp +++ b/BBGE/Base.cpp @@ -1001,86 +1001,6 @@ int unpackFile(const std::string &sourcef, const std::string &destf) return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; } -int encode[8] = {16, 32, 8, 4, 2, 1, 3, 5 }; - -void crunchFile(const std::string &file, const std::string &out, bool deleteOriginal) -{ - FILE *f = fopen(core->adjustFilenameCase(file).c_str(), "rb"), *o = fopen(core->adjustFilenameCase(out).c_str(), "wb"); - - if (f && o) - { - char buf=0; - int rot = 0, add = 0; - - while (true) - { - if (fread(&buf, sizeof(char), 1, f) != 1) - break; - - buf += encode[rot] + add; - - if (fwrite(&buf, sizeof(char), 1, o) != 1) - { - errorLog("Failed to write to " + out); - break; - } - - rot++; - if (rot>=8) - { rot=0; add++; } - } - - fclose(f); - f=0; - - fclose(o); - o=0; - } - - if (f) fclose(f); - if (o) fclose(o); - - if (deleteOriginal) - remove(file.c_str()); -} - -void uncrunchFile(const std::string &file, const std::string &out) -{ - FILE *f = fopen(core->adjustFilenameCase(file).c_str(), "rb"), *o = fopen(core->adjustFilenameCase(out).c_str(), "wb"); - - if (f && o) - { - char buf=0; - int rot=0, add=0; - while (true) - { - if (fread(&buf, sizeof(char), 1, f) != 1) - break; - - buf -= encode[rot] + add; - - if (fwrite(&buf, sizeof(char), 1, o) != 1) - { - errorLog("Failed to write to " + out); - break; - } - - rot++; - if (rot>=8) - { rot=0; add++; } - } - - fclose(f); - f=0; - - fclose(o); - o=0; - } - - if (f) fclose(f); - if (o) fclose(o); -} - void openURL(const std::string &url) { #ifdef BBGE_BUILD_WINDOWS @@ -1138,3 +1058,28 @@ std::string spacesToUnderscores(const std::string &str) if (s[i] == ' ') s[i] = '_'; return s; } + + + +#include "DeflateCompressor.h" + +char *readCompressedFile(std::string path, unsigned long *size_ret) +{ + unsigned long size = 0; + char *buf = readFile(path, &size); + ZlibCompressor z; // allocates with new[] by default + z.init(buf, size, ByteBuffer::TAKE_OVER); + z.Compressed(true); + z.Decompress(); + if(!z.Compressed()) + { + if (size_ret) + *size_ret = z.size(); + z.wpos(z.size()); + z << '\0'; // be sure the buffer is null-terminated + buf = (char*)z.ptr(); + z._setPtr(NULL); + return buf; + } + return NULL; +} diff --git a/BBGE/Base.h b/BBGE/Base.h index 4e2e3c9..a0c7140 100644 --- a/BBGE/Base.h +++ b/BBGE/Base.h @@ -197,6 +197,7 @@ bool exists(const std::string &f, bool makeFatal = false); void errorLog(const std::string &s); void debugLog(const std::string &s); char *readFile(std::string path, unsigned long *size_ret = 0); +char *readCompressedFile(std::string path, unsigned long *size_ret = 0); void forEachFile(std::string path, std::string type, void callback(const std::string &filename, intptr_t param), intptr_t param); std::string stripEndlineForUnix(const std::string &in); std::vector getFileList(std::string path, std::string type, int param); @@ -277,10 +278,6 @@ enum LerpType float lerp(const float &v1, const float &v2, float dt, int lerpType); - -void crunchFile(const std::string &file, const std::string &out, bool deleteOriginal=false); -void uncrunchFile(const std::string &file, const std::string &out); - int packFile(const std::string &sourcef, const std::string &destf, int level); int unpackFile(const std::string &sourcef, const std::string &destf); diff --git a/BBGE/ByteBuffer.h b/BBGE/ByteBuffer.h new file mode 100644 index 0000000..2fbd2db --- /dev/null +++ b/BBGE/ByteBuffer.h @@ -0,0 +1,476 @@ +#ifndef BYTEBUFFER_H +#define BYTEBUFFER_H + +#include // for memcpy + + +// ** compatibility stuff for BBGE .... ** + +#include "Base.h" + +#define BYTEBUFFER_NO_EXCEPTIONS + +#if (defined(BBGE_BUILD_SDL) && (SDL_BYTEORDER == SDL_BIG_ENDIAN)) +# define BB_IS_BIG_ENDIAN +#endif + +// **** + + +#define BB_MAKE_WRITE_OP(T) inline ByteBuffer& operator<<(T val) { append(val); return *this; } +#define BB_MAKE_READ_OP(T) inline ByteBuffer& operator>>(T &val) { val = read(); return *this; } + +class ByteBuffer +{ +public: + typedef void (*delete_func)(void*); + typedef void *(*allocator_func)(size_t); + + enum Mode // for creation with existing pointers + { + COPY, //- Make a copy of the buffer (default action). + REUSE, //- Use the passed-in buffer as is. Requires the pointer + // to remain valid over the life of this object. + TAKE_OVER, //- Take over the passed-in buffer; it will be deleted on object destruction. + }; + +#ifdef _MSC_VER + typedef __int64 int64; + typedef long int32; + typedef short int16; + typedef char int8; + typedef unsigned __int64 uint64; + typedef unsigned long uint32; + typedef unsigned short uint16; + typedef unsigned char uint8; +#else + typedef long long int64; + typedef int int32; + typedef short int16; + typedef char int8; + typedef unsigned long long uint64; + typedef unsigned int uint32; + typedef unsigned short uint16; + typedef unsigned char uint8; +#endif + + class Exception + { + public: + Exception(const ByteBuffer *bb, const char *act, uint32 sp = 0) + { + action = act; + rpos = bb->rpos(); + wpos = bb->wpos(); + sizeparam = sp; + cursize = bb->size(); + } + uint32 rpos, wpos, sizeparam, cursize; + const char *action; + }; + +#ifdef BYTEBUFFER_NO_EXCEPTIONS +#define BYTEBUFFER_EXCEPT(bb, desc, sz) { Exception __e(bb, desc, sz); char errbuf[256]; \ + sprintf(errbuf, "Exception in ByteBuffer: '%s', rpos: %u, wpos: %u, cursize: %u, sizeparam: %u", \ + __e.action, __e.rpos, __e.wpos, __e.cursize, __e.sizeparam); errorLog(errbuf); abort(); } +#else +#define BYTEBUFFER_EXCEPT(bb, desc, sz) throw ByteBufferException(bb, desc, sz) +#endif + +protected: + + uint8 *_buf; // the ptr to the buffer that holds all the bytes + uint32 _rpos, // read position, [0 ... _size] + _wpos, // write position, [0 ... _size] + _res, // reserved buffer size, [0 ... _size ... _res] + _size; // used buffer size + delete_func _delfunc; + allocator_func _allocfunc; + bool _mybuf; // if true, destructor deletes buffer + bool _growable; // default true, if false, buffer will not re-allocate more space + +public: + + + ByteBuffer() + : _rpos(0), _wpos(0), _buf(NULL), _size(0), _growable(true), _res(0), _mybuf(false), _delfunc(NULL), + _allocfunc(NULL) + { + } + ByteBuffer(uint32 res) + : _rpos(0), _wpos(0), _buf(NULL), _size(0), _growable(true), _res(0), _mybuf(false), _delfunc(NULL), + _allocfunc(NULL) + { + _allocate(res); + } + ByteBuffer(ByteBuffer &buf, Mode mode = COPY, uint32 extra = 0) + : _rpos(0), _wpos(0), _buf(NULL), _size(0), _growable(true), _res(0), _mybuf(false), _delfunc(NULL), + _allocfunc(NULL) + { + init(buf, mode, extra); + } + // del param only used with TAKE_OVER, extra only used with COPY + ByteBuffer(void *buf, uint32 size, Mode mode = COPY, delete_func del = NULL, uint32 extra = 0) + : _rpos(0), _wpos(0), _size(size), _buf(NULL), _growable(true), _delfunc(del), + _mybuf(false), _allocfunc(NULL) // for mode == REUSE + { + init(buf, size, mode, del, extra); + } + + void init(void *buf, uint32 size, Mode mode = COPY, delete_func del = NULL, uint32 extra = 0) + { + _mybuf = false; + switch(mode) + { + case COPY: + _allocate(size + extra); + append(buf, size); + break; + + case TAKE_OVER: + _mybuf = true; // fallthrough + case REUSE: + _buf = (uint8*)buf; + _res = size; + _size = size; + } + } + + void init(ByteBuffer& bb, Mode mode = COPY, uint32 extra = 0) + { + _allocfunc = bb._allocfunc; + + switch(mode) + { + case COPY: + reserve(bb.size() + extra); + append(bb); + break; + + case TAKE_OVER: + case REUSE: + _mybuf = bb._mybuf; + _delfunc = bb._delfunc; + _buf = bb._buf; + _res = bb._res; + _size = bb._size; + _growable = bb._growable; + break; + } + + if(mode == TAKE_OVER) + { + bb._buf = NULL; + bb._size = 0; + bb._res = 0; + } + } + + virtual ~ByteBuffer() + { + clear(); + } + + void clear(void) + { + _delete(); + reset(); + } + + inline void reset(void) + { + _rpos = _wpos = _size = 0; + } + + void resize(uint32 newsize) + { + reserve(newsize); + _rpos = 0; + _wpos = newsize; + _size = newsize; + } + + void reserve(uint32 newsize) + { + if(_res < newsize) + _allocate(newsize); + } + + // ---------------------- Write methods ----------------------- + + BB_MAKE_WRITE_OP(char); + BB_MAKE_WRITE_OP(uint8); + BB_MAKE_WRITE_OP(uint16); + BB_MAKE_WRITE_OP(uint32); + BB_MAKE_WRITE_OP(uint64); + BB_MAKE_WRITE_OP(float); + BB_MAKE_WRITE_OP(double); + BB_MAKE_WRITE_OP(int); + + ByteBuffer &operator<<(bool value) + { + append((char)value); + return *this; + } + + ByteBuffer &operator<<(const char *str) + { + append((uint8 *)str, str ? strlen(str) : 0); + append((uint8)0); + return *this; + } + + ByteBuffer &operator<<(const std::string &value) + { + append((uint8 *)value.c_str(), value.length()); + append((uint8)0); + return *this; + } + + // -------------------- Read methods -------------------- + + BB_MAKE_READ_OP(char); + BB_MAKE_READ_OP(uint8); + BB_MAKE_READ_OP(uint16); + BB_MAKE_READ_OP(uint32); + BB_MAKE_READ_OP(uint64); + BB_MAKE_READ_OP(float); + BB_MAKE_READ_OP(double); + BB_MAKE_READ_OP(int); + + ByteBuffer &operator>>(bool &value) + { + value = read() > 0 ? true : false; + return *this; + } + + uint8 operator[](uint32 pos) + { + return read(pos); + } + + ByteBuffer &operator>>(std::string& value) + { + value.clear(); + char c; + while(readable() && (c = read())) + value += c; + return *this; + } + + // -------------------------------------------------- + + uint32 rpos() const { return _rpos; } + uint32 rpos(uint32 rpos) + { + _rpos = rpos < size() ? rpos : size(); + return _rpos; + } + + uint32 wpos() const { return _wpos; } + uint32 wpos(uint32 wpos) + { + _wpos = wpos < size() ? wpos : size(); + return _wpos; + } + + template T read() + { + T r = read(_rpos); + _rpos += sizeof(T); + return r; + } + template T read(uint32 pos) const + { + if(pos + sizeof(T) > size()) + BYTEBUFFER_EXCEPT(this, "read", sizeof(T)); + T val = *((T const*)(_buf + pos)); + ToLittleEndian(val); + return val; + } + + void read(void *dest, uint32 len) + { + if (_rpos + len <= size()) + memcpy(dest, &_buf[_rpos], len); + else + BYTEBUFFER_EXCEPT(this, "read-into", len); + _rpos += len; + } + + void skipRead(uint32 len) + { + _rpos += len; + } + + inline const uint8 *contents() const { return _buf; } + inline uint8 *contents() { return _buf; } + + inline const void *ptr() const { return _buf; } + inline void *ptr() { return _buf; } + + inline uint32 size() const { return _size; } + + inline uint32 bytes() const { return size(); } + inline uint32 bits() const { return bytes() * 8; } + + inline uint32 capacity() const { return _res; } + + inline uint32 readable(void) const { return size() - rpos(); } + inline uint32 writable(void) const { return size() - wpos(); } // free space left before realloc will occur + + template void append(T value) + { + ToLittleEndian(value); + _enlargeIfReq(_wpos + sizeof(T)); + *((T*)(_buf + _wpos)) = value; + _wpos += sizeof(T); + if(_size < _wpos) + _size = _wpos; + } + + void append(const void *src, uint32 bytes) + { + if (!bytes) return; + _enlargeIfReq(_wpos + bytes); + memcpy(_buf + _wpos, src, bytes); + _wpos += bytes; + if(_size < _wpos) + _size = _wpos; + } + void append(const ByteBuffer& buffer) + { + if(buffer.size()) + append(buffer.contents(), buffer.size()); + } + + void put(uint32 pos, const void *src, uint32 bytes) + { + memcpy(_buf + pos, src, bytes); + } + + template void put(uint32 pos, T value) + { + if(pos >= size()) + BYTEBUFFER_EXCEPT(this, "put", sizeof(T)); + + ToLittleEndian(value); + *((T*)(_buf + pos)) = value; + } + + inline bool growable(void) { return _growable; } + inline void growable(bool b) { _growable = b; } + + // dangerous functions + + void _setPtr(void *p) + { + _buf = (uint8*)p; + } + + void _setAllocFunc(allocator_func f) + { + _allocfunc = f; + } + + void _setDelFunc(delete_func f) + { + _delfunc = f; + } + + void _setSize(uint32 s) + { + _size = s; + } + + void _setReserved(uint32 s) + { + _res = s; + } + +protected: + + void _delete(void) + { + if(_mybuf) + { + if(_delfunc) + _delfunc(_buf); + else + delete [] _buf; + _buf = NULL; + _res = 0; + } + } + + // allocate larger buffer and copy contents. if we own the current buffer, delete old, otherwise, leave it as it is. + void _allocate(uint32 s) + { + if(!_growable && _buf) // only throw if we already have a buf + BYTEBUFFER_EXCEPT(this, "_alloc+locked", s); + + // dangerous: It's up to the user to be sure that _allocfunc and _delfunc are matching + uint8 *newbuf = (uint8*)(_allocfunc ? _allocfunc(s) : new char[s]); + if(_buf) + { + memcpy(newbuf, _buf, _size); + _delete(); + } + _buf = newbuf; + _res = s; + _mybuf = true; + + if (!_allocfunc) + _delfunc = NULL; + } + + void _enlargeIfReq(uint32 minSize) + { + if(_res < minSize) + { + uint32 a = _res * 2; + if(a < minSize) // fallback if doubling the space was not enough + a += minSize; + _allocate(a); + } + } + + template inline static void convert(char *val) + { + std::swap(*val, *(val + T - 1)); + convert(val + 1); + } + template<> inline static void convert<0>(char *) {} + template<> inline static void convert<1>(char *) {} + + template inline static void EndianConvert(T *val) + { + convert((char *)(val)); + } + +#if BB_IS_BIG_ENDIAN + template inline static void ToLittleEndian(T& val) { EndianConvert(&val); } + template inline static void ToBigEndian(T&) { } +#else + template inline static void ToLittleEndian(T&) { } + template inline static void ToBigEndian(T& val) { EndianConvert(&val); } +#endif + + template static void ToLittleEndian(T*); // will generate link error + template static void ToBigEndian(T*); // will generate link error + + inline static void ToLittleEndian(uint8&) { } + inline static void ToLittleEndian(int8&) { } + inline static void ToBigEndian(uint8&) { } + inline static void ToBigEndian( int8&) { } + +}; + + +#undef BB_MAKE_WRITE_OP +#undef BB_MAKE_READ_OP +#undef BB_IS_BIG_ENDIAN + + + +#endif diff --git a/BBGE/DeflateCompressor.cpp b/BBGE/DeflateCompressor.cpp new file mode 100644 index 0000000..302b957 --- /dev/null +++ b/BBGE/DeflateCompressor.cpp @@ -0,0 +1,259 @@ +#include "Base.h" + +#include +#include + +#include "DeflateCompressor.h" + +// for weird gcc/mingw hackfix below +#include + + +DeflateCompressor::DeflateCompressor() +: _windowBits(-MAX_WBITS), // negative, because we want a raw deflate stream, and not zlib-wrapped + _forceCompress(false), + _iscompressed(false), + _real_size(0) +{ +} + +ZlibCompressor::ZlibCompressor() +: DeflateCompressor() +{ + _windowBits = MAX_WBITS; // positive, means we use a zlib-wrapped deflate stream +} + +GzipCompressor::GzipCompressor() +: DeflateCompressor() +{ + _windowBits = MAX_WBITS + 16; // this makes zlib wrap a minimal gzip header around the stream + _forceCompress = true; // we want this for gzip +} + +void DeflateCompressor::compress(void* dst, uint32 *dst_size, const void* src, uint32 src_size, + uint8 level, int wbits) +{ + z_stream c_stream; + + c_stream.zalloc = (alloc_func)Z_NULL; + c_stream.zfree = (free_func)Z_NULL; + c_stream.opaque = (voidpf)Z_NULL; + + if (Z_OK != deflateInit2(&c_stream, level, Z_DEFLATED, wbits, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY)) + { + *dst_size = 0; + return; + } + + c_stream.next_out = (Bytef*)dst; + c_stream.avail_out = *dst_size; + c_stream.next_in = (Bytef*)src; + c_stream.avail_in = (uInt)src_size; + + if (Z_OK != deflate(&c_stream, Z_NO_FLUSH)) + { + errorLog("ZLIB: Can't compress (zlib: deflate)"); + *dst_size = 0; + return; + } + + if (c_stream.avail_in != 0) + { + errorLog("Can't compress (zlib: deflate not greedy)"); + *dst_size = 0; + return; + } + + if (Z_STREAM_END != deflate(&c_stream, Z_FINISH)) + { + errorLog("Can't compress (zlib: deflate, finish)"); + *dst_size = 0; + return; + } + + if (Z_OK != deflateEnd(&c_stream)) + { + errorLog("Can't compress (zlib: deflateEnd)"); + *dst_size = 0; + return; + } + + *dst_size = c_stream.total_out; +} + +void DeflateCompressor::decompress(void *dst, uint32 *origsize, const void *src, uint32 size, int wbits) +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)src; + stream.avail_in = (uInt)size; + stream.next_out = (Bytef*)dst; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.avail_out = *origsize; + stream.total_out = 0; + + err = inflateInit2(&stream, wbits); + if (err != Z_OK) + { + *origsize = 0; + return; + } + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) + { + inflateEnd(&stream); + *origsize = 0; + return; + } + *origsize = (uint32)stream.total_out; + + err = inflateEnd(&stream); + if(err != Z_OK) + *origsize = 0; +} + + +void DeflateCompressor::Compress(uint8 level) +{ + if(!_forceCompress && (!level || _iscompressed || (!size()))) + return; + + char *buf; + + + uint32 oldsize = size(); + uint32 newsize = compressBound(oldsize) + 30; // for optional gzip header + + buf = new char[newsize]; + + compress((void*)buf, &newsize, (void*)contents(), oldsize, level, _windowBits); + if(!newsize || (!_forceCompress && newsize > oldsize)) // only allow more data if compression is forced (which is the case for gzip) + { + delete [] buf; + return; + } + + resize(newsize); + rpos(0); + wpos(0); + append(buf,newsize); + delete [] buf; + + _iscompressed = true; + + _real_size = oldsize; +} + +void DeflateCompressor::Decompress(void) +{ + if( (!_iscompressed) || (!size())) + return; + + if(!_real_size) + { + if(decompressBlockwise() == Z_OK) + _iscompressed = false; + } + else + { + uint32 rs = (uint32)_real_size; + uint32 origsize = rs; + uint8 *target = new uint8[rs]; + wpos(0); + rpos(0); + decompress((void*)target, &origsize, (const void*)contents(), size(), _windowBits); + if(origsize != rs) + { + char errbuf[256]; + sprintf(errbuf, "DeflateCompressor: Inflate error! result=%d cursize=%u origsize=%u realsize=%u",size(),origsize,rs); + errorLog(errbuf); + delete [] target; + return; + } + clear(); + append(target, origsize); + delete [] target; + _real_size = 0; + _iscompressed = false; + } + +} + +#define CHUNK 16384 + +int DeflateCompressor::decompressBlockwise() +{ + int ret; + unsigned have; + z_stream strm; + unsigned char out[CHUNK]; + + /* allocate inflate state */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, _windowBits); + if (ret != Z_OK) + return ret; + + ByteBuffer bb; + + strm.avail_in = size(); + strm.next_in = contents(); + + /* decompress until deflate stream ends or end of file */ + do { + /* run inflate() on input until output buffer not full */ + do { + strm.avail_out = CHUNK; + strm.next_out = out; + ret = inflate(&strm, Z_NO_FLUSH); + switch (ret) { + case Z_NEED_DICT: + case Z_STREAM_ERROR: + ret = Z_DATA_ERROR; /* and fall through */ + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + return ret; + } + have = CHUNK - strm.avail_out; + bb.append(out, have); + } while (strm.avail_out == 0); + /* done when inflate() says it's done */ + } while (ret != Z_STREAM_END); + + /* clean up and return */ + (void)inflateEnd(&strm); + + if (ret != Z_STREAM_END) + return Z_DATA_ERROR; + + // exchange pointer + clear(); + init(bb, TAKE_OVER); + + return Z_OK; +} + +void GzipCompressor::Decompress(void) +{ + uint32 t = 0; + rpos(size() - sizeof(uint32)); // according to RFC 1952, input size are the last 4 bytes at the end of the file, in little endian + *this >> t; + _real_size = t; + + // !! NOTE: this fixes a gcc/mingw bug where _real_size would be set incorrectly +#if __GNUC__ + char xx[20]; + sprintf(xx, "%u", t); +#endif + + DeflateCompressor::Decompress(); // will set rpos back anyway +} diff --git a/BBGE/DeflateCompressor.h b/BBGE/DeflateCompressor.h new file mode 100644 index 0000000..1b97d14 --- /dev/null +++ b/BBGE/DeflateCompressor.h @@ -0,0 +1,59 @@ +#ifndef DEFLATE_COMPRESSOR_H +#define DEFLATE_COMPRESSOR_H + +#include "ByteBuffer.h" + +// implements a raw deflate stream, not zlib wrapped, and not checksummed. +class DeflateCompressor : public ByteBuffer +{ +public: + DeflateCompressor(); + DeflateCompressor(void *buf, uint32 size, Mode mode = COPY, delete_func del = NULL, uint32 extra = 0); + virtual ~DeflateCompressor() {} + virtual void Compress(uint8 level = 1); + virtual void Decompress(void); + + bool Compressed(void) const { return _iscompressed; } + void Compressed(bool b) { _iscompressed = b; } + uint32 RealSize(void) const { return _iscompressed ? _real_size : size(); } + void RealSize(uint32 realsize) { _real_size = realsize; } + void clear(void) // not required to be strictly virtual; be careful not to mess up static types! + { + ByteBuffer::clear(); + _real_size = 0; + _iscompressed = false; + } + +protected: + int _windowBits; // read zlib docs to know what this means + unsigned int _real_size; + bool _forceCompress; + bool _iscompressed; + +private: + static void decompress(void *dst, uint32 *origsize, const void *src, uint32 size, int wbits); + static void compress(void* dst, uint32 *dst_size, const void* src, uint32 src_size, + uint8 level, int wbits); + + int decompressBlockwise(); +}; + +// implements deflate stream, zlib wrapped +class ZlibCompressor : public DeflateCompressor +{ +public: + ZlibCompressor(); + virtual ~ZlibCompressor() {} +}; + +// the output produced by this stream contains a minimal gzip header, +// and can be directly written to a .gz file. +class GzipCompressor : public DeflateCompressor +{ +public: + GzipCompressor(); + virtual ~GzipCompressor() {} + virtual void Decompress(void); +}; + +#endif diff --git a/BBGE/Texture.cpp b/BBGE/Texture.cpp index e25b192..c9c5461 100644 --- a/BBGE/Texture.cpp +++ b/BBGE/Texture.cpp @@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "Texture.h" #include "Core.h" #include "../ExternalLibs/glpng.h" +#include "ByteBuffer.h" #include @@ -69,7 +70,6 @@ Texture::Texture() : Resource() repeat = false; pngSetStandardOrientation(0); imageData = 0; - layer = 0; ow = oh = -1; } @@ -388,19 +388,7 @@ void Texture::load(std::string file) } else if (post == "zga") { - if (core->getUserDataFolder().empty()) - { - unpackFile(file, "poot.tmp"); - loadTGA("poot.tmp"); - remove("poot.tmp"); - } - else - { - unpackFile(file, core->getUserDataFolder() + "/poot.tmp"); - loadTGA(core->getUserDataFolder() + "/poot.tmp"); - remove((core->getUserDataFolder() + "/poot.tmp").c_str()); - } - + loadZGA(file); } else if (post == "tga") { @@ -450,11 +438,6 @@ void Texture::unbind() { } -void Texture::setLayer(int layer) -{ - this->layer = layer; -} - #ifdef BBGE_BUILD_OPENGL void Texture::setID(int id) @@ -523,59 +506,43 @@ void Texture::loadPNG(const std::string &file) // internal load functions void Texture::loadTGA(const std::string &file) { -#ifdef BBGE_BUILD_GLFW - GLFWimage image; - glfwReadImage(file.c_str(), &image, 0); - width = image.Width; - height = image.Height; - glfwFreeImage(&image); + loadTGA(TGAload(file.c_str())); +} +void Texture::loadZGA(const std::string &file) +{ + unsigned long size = 0; + char *buf = readCompressedFile(file, &size); + ImageTGA *tga = TGAloadMem(buf, size); + if (!tga) + { + debugLog("Can't load ZGA File: " + file); + return; + } + loadTGA(tga); +} - glGenTextures(1, &id); - glBindTexture(GL_TEXTURE_2D, id); - - glfwLoadTexture2D(file.c_str(), 0); +void Texture::loadTGA(ImageTGA *imageTGA) +{ + if (!imageTGA) + return; + glGenTextures(1, &textures[0]); + glBindTexture(GL_TEXTURE_2D, textures[0]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,filter); // Linear Filtering glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,filter); // Linear Filtering -#endif + if (imageTGA->channels==3) + glTexImage2D(GL_TEXTURE_2D, 0, 3, imageTGA->sizeX, imageTGA->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, imageTGA->data); + else if (imageTGA->channels==4) + glTexImage2D(GL_TEXTURE_2D, 0, 4,imageTGA->sizeX, imageTGA->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageTGA->data); - /* - glfwLoadTexture2D(file.c_str(), 0); - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,filter); // Linear Filtering - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,filter); // Linear Filtering width = imageTGA->sizeX; height = imageTGA->sizeY; - */ -#ifdef BBGE_BUILD_SDL - ImageTGA *imageTGA; - - if ((imageTGA = TGAload(file.c_str())) != 0) - { - glGenTextures(1, &textures[0]); - glBindTexture(GL_TEXTURE_2D, textures[0]); - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,filter); // Linear Filtering - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,filter); // Linear Filtering - - if (imageTGA->channels==3) - glTexImage2D(GL_TEXTURE_2D, 0, 3, imageTGA->sizeX, imageTGA->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, imageTGA->data); - else if (imageTGA->channels==4) - { - //errorLog("4 channels"); - glTexImage2D(GL_TEXTURE_2D, 0, 4,imageTGA->sizeX, imageTGA->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageTGA->data); - } - width = imageTGA->sizeX; - height = imageTGA->sizeY; - } - if (imageTGA) - { - if (imageTGA->data) - delete[] (imageTGA->data); - free (imageTGA); - } -#endif + if (imageTGA->data) + delete[] (imageTGA->data); + free (imageTGA); } @@ -589,36 +556,37 @@ typedef uint16_t WORD; #endif -static int fread_int(FILE *file, int size) -{ - int buffer; - - //input.read((char *)&buffer, 4); - if (fread(&buffer, size, 1, file) != 1) - return 0; -#ifdef BBGE_BUILD_SDL - return SDL_SwapLE32(buffer); -#else - return buffer; -#endif -} - #ifdef BBGE_BUILD_WINDOWS #define byte char #endif ImageTGA *Texture::TGAload(const char *filename) { -/* - //HACK: function isn't macosx friendly - return 0; -*/ + unsigned long size = 0; + char *rawbuf = readFile(filename, &size); + ImageTGA *tga = TGAloadMem(rawbuf, size); + if (rawbuf) + delete [] rawbuf; + if (!tga) + { + debugLog("Can't load TGA File!"); + return NULL; + } + return tga; +} + +ImageTGA *Texture::TGAloadMem(void *mem, int size) +{ + if (!mem || size < 20) + return NULL; + + ByteBuffer bb(mem, size, ByteBuffer::REUSE); + ImageTGA *pImageData = NULL; // This stores our important image data WORD width = 0, height = 0; // The dimensions of the image byte length = 0; // The length in bytes to the pixels byte imageType = 0; // The image type (RLE, RGB, Alpha...) byte bits = 0; // The bits per pixel for the image (16, 24, 32) - FILE *pFile = NULL; // The file pointer int channels = 0; // The channels of the image (3 = RGA : 4 = RGBA) int stride = 0; // The stride (channels * width) int i = 0; // A counter @@ -636,41 +604,25 @@ ImageTGA *Texture::TGAload(const char *filename) // 32-bit textures are very similar, so there's no need to do anything special. // We do, however, read in an extra bit for each color. - // Open a file pointer to the targa file and check if it was found and opened - - if((pFile = fopen(core->adjustFilenameCase(filename).c_str(), "rb")) == NULL) //, "rb" // openRead(fn) - { - // Display an error message saying the file was not found, then return NULL - debugLog("Unable to load TGA File!"); - return NULL; - } // Allocate the structure that will hold our eventual image data (must free it!) pImageData = (ImageTGA*)malloc(sizeof(ImageTGA)); // Read in the length in bytes from the header to the pixel data - //fread(&length, sizeof(byte), 1, pFile); - length = fread_int(pFile, sizeof(byte)); + bb >> length; // Jump over one byte - fseek(pFile,1,SEEK_CUR); + bb.skipRead(1); // Read in the imageType (RLE, RGB, etc...) //fread(&imageType, sizeof(byte), 1, pFile); - imageType = fread_int(pFile, sizeof(byte)); + bb >> imageType; // Skip past general information we don't care about - fseek(pFile, 9, SEEK_CUR); + bb.skipRead(9); // Read the width, height and bits per pixel (16, 24 or 32) - /* - fread(&width, sizeof(WORD), 1, pFile); - fread(&height, sizeof(WORD), 1, pFile); - fread(&bits, sizeof(byte), 1, pFile); - */ - width = fread_int(pFile, sizeof(WORD)); - height = fread_int(pFile, sizeof(WORD)); - bits = fread_int(pFile, sizeof(byte)); + bb >> width >> height >> bits; /* std::ostringstream os; @@ -679,7 +631,7 @@ ImageTGA *Texture::TGAload(const char *filename) */ // Now we move the file pointer to the pixel data - fseek(pFile, length + 1, SEEK_CUR); + bb.skipRead(length + 1); // Check if the image is RLE compressed or not if(imageType != TGA_RLE) @@ -700,8 +652,9 @@ ImageTGA *Texture::TGAload(const char *filename) unsigned char *pLine = &(pImageData->data[stride * y]); // Read in the current line of pixels - if (fread(pLine, stride, 1, pFile) != 1) + if (bb.readable() < stride) break; + bb.read(pLine, stride); // Go through all of the pixels and swap the B and R values since TGA // files are stored as BGR instead of RGB (or use GL_BGR_EXT verses GL_RGB) @@ -729,8 +682,9 @@ ImageTGA *Texture::TGAload(const char *filename) for(int i = 0; i < width*height; i++) { // Read in the current pixel - if (fread(&pixels, sizeof(unsigned short), 1, pFile) != 1) + if (bb.readable() < sizeof(unsigned char)) break; + bb >> pixels; // To convert a 16-bit pixel into an R, G, B, we need to // do some masking and such to isolate each color value. @@ -787,8 +741,7 @@ ImageTGA *Texture::TGAload(const char *filename) while(i < width*height) { // Read in the current color count + 1 - if (fread(&rleID, sizeof(byte), 1, pFile) != 1) - break; + bb >> rleID; // Check if we don't have an encoded string of colors if(rleID < 128) @@ -800,8 +753,9 @@ ImageTGA *Texture::TGAload(const char *filename) while(rleID) { // Read in the current color - if (fread(pColors, sizeof(byte) * channels, 1, pFile) != 1) + if (bb.readable() < channels) break; + bb.read(pColors, channels); // Store the current pixel in our image array pImageData->data[colorsRead + 0] = pColors[2]; @@ -826,8 +780,9 @@ ImageTGA *Texture::TGAload(const char *filename) rleID -= 127; // Read in the current color, which is the same for a while - if (fread(pColors, sizeof(byte) * channels, 1, pFile) != 1) + if (bb.readable() < channels) break; + bb.read(pColors, channels); // Go and read as many pixels as are the same while(rleID) @@ -856,9 +811,6 @@ ImageTGA *Texture::TGAload(const char *filename) delete[] pColors; } - // Close the file pointer that opened the file - fclose(pFile); - // Fill in our tImageTGA structure to pass back pImageData->channels = channels; pImageData->sizeX = width; diff --git a/BBGE/Texture.h b/BBGE/Texture.h index e7923d5..936a381 100644 --- a/BBGE/Texture.h +++ b/BBGE/Texture.h @@ -59,6 +59,7 @@ public: int width, height; static ImageTGA *TGAload(const char* filename); + static ImageTGA *TGAloadMem(void *mem, int size); static bool useMipMaps; bool repeat; @@ -84,10 +85,12 @@ public: void read(int tx, int ty, int w, int h, unsigned char *pixels); protected: std::string loadName; - int layer; + // internal load functions void loadPNG(const std::string &file); void loadTGA(const std::string &file); + void loadZGA(const std::string &file); + void loadTGA(ImageTGA *tga); int ow, oh; diff --git a/ExternalLibs/tinyxml.cpp b/ExternalLibs/tinyxml.cpp index 9be6c6a..a4a6056 100644 --- a/ExternalLibs/tinyxml.cpp +++ b/ExternalLibs/tinyxml.cpp @@ -993,6 +993,11 @@ bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) return false; } + return LoadMem(buf, length, encoding); +} + +bool TiXmlDocument::LoadMem( char* buf, long length, TiXmlEncoding encoding ) +{ // Process the buffer in place to normalize new lines. (See comment above.) // Copies from the 'p' to 'q' pointer, where p can advance faster if // a newline-carriage return is hit. diff --git a/ExternalLibs/tinyxml.h b/ExternalLibs/tinyxml.h index 0182291..dca5f15 100644 --- a/ExternalLibs/tinyxml.h +++ b/ExternalLibs/tinyxml.h @@ -22,6 +22,9 @@ must not be misrepresented as being the original software. distribution. */ +// EDIT: +// - added LoadMem() function + #ifndef TINYXML_INCLUDED #define TINYXML_INCLUDED @@ -1421,6 +1424,8 @@ public: /// Save a file using the given FILE*. Returns true if successful. bool SaveFile( FILE* ) const; + bool LoadMem( char *buf, long length, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + #ifdef TIXML_USE_STL bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. {