123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- /* GCSx
- ** LOAD.CPP
- **
- ** File loading only
- */
- /*****************************************************************************
- ** Copyright (C) 2003-2006 Janson
- **
- ** 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- *****************************************************************************/
- #include "all.h"
- // Read interface
- FileRead::FileRead(File* myFilePtr, int offset, int size, Uint32 myVersion, Uint32 myCompression) throw_File { start_func
- filePtr = myFilePtr;
- currentOffset = startOffset = offset;
- bytesRead = 0;
- totalSize = size;
- version = myVersion;
- compression = myCompression;
- zlibBufferIn = NULL;
- zlibStream = NULL;
- zlibStreamInit = 0;
-
- try {
- if (compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB) {
- // Read original size and checksum
- if (size < 4) {
- throw FileException("Unexpected end-of-block");
- }
- filePtr->seek(currentOffset);
- zlibOrigSize = filePtr->readInt();
- bytesRead += 4;
- currentOffset += 4;
- // Prepare objects
- zlibBufferIn = new Byte[ZLIB_BUFFER_SIZE];
- zlibStream = new z_stream;
-
- // Get first block NOW, so we are most likely to read zlib header here
- zlibStream->avail_in = 0;
- zlibNextBuffer();
-
- // Use default alloc and free for now
- zlibStream->zalloc = NULL;
- zlibStream->zfree = NULL;
- zlibStream->opaque = NULL;
- if (inflateInit(zlibStream) != Z_OK) {
- fatalCrash(0, "ZLIB Memory/decompression error: %s", zlibStream->msg);
- }
- zlibStreamInit = 1;
- }
- }
- catch (...) {
- delete[] zlibBufferIn;
- if (zlibStreamInit) inflateEnd(zlibStream);
- delete zlibStream;
- throw;
- }
- }
- FileRead::~FileRead() { start_func
- delete[] zlibBufferIn;
- if (zlibStreamInit) inflateEnd(zlibStream);
- delete zlibStream;
- }
- void FileRead::zlibNextBuffer() throw_File { start_func
- assert(compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB);
- assert(zlibStream);
- assert(zlibBufferIn);
- assert(zlibStream->avail_in == 0);
- int amount = min(totalSize - bytesRead, (int)ZLIB_BUFFER_SIZE);
- if (amount == 0) {
- throw FileException("Unexpected end-of-block");
- }
- filePtr->seek(currentOffset);
- filePtr->read(zlibBufferIn, amount);
- zlibStream->next_in = zlibBufferIn;
- zlibStream->avail_in = amount;
- bytesRead += amount;
- currentOffset += amount;
- }
- int FileRead::getSize() const { start_func
- if (compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB) {
- return zlibOrigSize;
- }
- else {
- return totalSize;
- }
- }
- int FileRead::moreData() const { start_func
- if (compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB) {
- return zlibOrigSize - zlibStream->total_out;
- }
- else {
- return totalSize - bytesRead;
- }
- }
- void FileRead::read(void* ptr, int size) throw_File { start_func
- if (compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB) {
- zlibStream->next_out = (Byte*)ptr;
- zlibStream->avail_out = size;
-
- // Inflate until error, reading more buffer as needed
- while (zlibStream->avail_out) {
- // Need more input buffer? (this also checks if we overflow)
- if (zlibStream->avail_in == 0) zlibNextBuffer();
- int result = inflate(zlibStream, 0);
- if (result == Z_STREAM_END) {
- // End of stream- we'd better be done!
- if (zlibStream->avail_out) {
- throw FileException("Unexpected end of compressed data");
- }
- // We are, at least with this read; mark us as done, unless we rewind
- bytesRead = totalSize;
- }
- // OK/BUF_ERROR means we need or may need to read more buffer
- else if ((result != Z_OK) && (result != Z_BUF_ERROR)) {
- throw FileException("Decompression error: %s", zlibStream->msg);
- }
- }
- }
- else {
- if (bytesRead + size > totalSize) {
- throw FileException("Unexpected end-of-block");
- }
-
- filePtr->seek(currentOffset);
- filePtr->read(ptr, size);
-
- bytesRead += size;
- currentOffset += size;
- }
- }
- char FileRead::copyBuffer[COPY_BUFFER_SIZE + 1];
- void FileRead::readStr(string& str) throw_File { start_func
- int size = readInt16();
- int first = 1;
-
- if (size) {
- while (size) {
- int amount = min(size, (int)COPY_BUFFER_SIZE);
- read(copyBuffer, amount);
- copyBuffer[amount] = 0;
- if (first) str = copyBuffer;
- else str += copyBuffer;
- size -= amount;
- }
- }
- else {
- str = blankString;
- }
- }
- void FileRead::readIntBulk(Uint32* ptr, int count) throw_File { start_func
- read(ptr, 4 * count);
- #if SDL_BYTEORDER != SDL_LIL_ENDIAN
- for (int pos = 0; pos < count; ++pos) {
- Uint8* bytes = ptr++;
- swap(bytes[0], bytes[3]);
- swap(bytes[1], bytes[2]);
- }
- #endif
- }
- Uint32 FileRead::readInt() throw_File { start_func
- Uint32 result;
- #if SDL_BYTEORDER == SDL_LIL_ENDIAN
- read(&result, 4);
- #else
- Uint8 bytes[4];
- read(bytes, 4);
- result = (Uint32)bytes[0] | ((Uint32)bytes[1] << 8) | ((Uint32)bytes[2] << 16) | ((Uint32)bytes[1] << 24);
- #endif
-
- return result;
- }
- Uint16 FileRead::readInt16() throw_File { start_func
- Uint16 result;
- #if SDL_BYTEORDER == SDL_LIL_ENDIAN
- read(&result, 2);
- #else
- Uint8 bytes[2];
- read(bytes, 2);
- result = (Uint16)bytes[0] | ((Uint16)bytes[1] << 8);
- #endif
-
- return result;
- }
- Uint8 FileRead::readInt8() throw_File { start_func
- Uint8 result;
- read(&result, 1);
- return result;
- }
- void FileRead::rewind() throw_File { start_func
- if (compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB) {
- // Optimization- don't do anything if we're already rewound
- if (bytesRead - zlibStream->avail_in != 4) {
- // End current stream
- inflateEnd(zlibStream);
- zlibStreamInit = 0;
-
- // Back to beginning, AFTER stored size
- currentOffset = startOffset + 4;
- bytesRead = 4;
-
- // Reget first block
- zlibStream->avail_in = 0;
- zlibNextBuffer();
-
- // Use default alloc and free for now
- zlibStream->zalloc = NULL;
- zlibStream->zfree = NULL;
- zlibStream->opaque = NULL;
- if (inflateInit(zlibStream) != Z_OK) {
- // Should never be Z_VERSION_ERROR
- // Really shouldn't memory error either, we just deleted
- // memory of an equal amount; therefore we throw a File
- // exception so we don't pollute the throw()
- throw FileException("Decompression error: %s", zlibStream->msg);
- }
- zlibStreamInit = 1;
- }
- }
- else {
- currentOffset = startOffset;
- bytesRead = 0;
- }
- }
- // World file class for loading only
- const char* WorldFileLoad::correctCookie = "GCSx";
- const Uint32 WorldFileLoad::currentVersion[4] = {
- VER_MAJOR, VER_MINOR, VER_RELEASE, VER_BUILD,
- };
- WorldFileLoad::WorldFileLoad() : blocks() { start_func
- // This will only be called by WorldFile when creating a new file
- filename = NULL;
- filePtr = NULL;
- numBlocks = 0;
- scanNextBlock = 0;
- }
- WorldFileLoad::WorldFileLoad(const string* openFile, int mode) throw_File : blocks() { start_func
- filename = openFile;
- filePtr = new File(filename->c_str(), mode);
- try {
- filePtr->seek(0);
-
- // Check cookie
- char cookie[5];
- filePtr->read(cookie, 4);
- cookie[4] = 0;
-
- if (strcmp(correctCookie, cookie)) {
- throw FileException("File %s is not a GCSx file", filename->c_str());
- }
-
- // Read version; skip reserved
- // @TODO: Check version and throw exception
- fileVersion[0] = filePtr->readInt();
- fileVersion[1] = filePtr->readInt();
- fileVersion[2] = filePtr->readInt();
- fileVersion[3] = filePtr->readInt();
- filePtr->skip(8);
-
- // Count of blocks
- numBlocks = filePtr->readInt();
- blocks.reserve(numBlocks);
- scanNextBlock = 0;
-
- // Block headers
- for (Uint32 pos = 0; pos < numBlocks; ++pos) {
- BlockInfo header;
-
- // Read header
- header.offset = filePtr->readInt();
- header.size = filePtr->readInt();
- header.type = filePtr->readInt();
- filePtr->skip(4);
-
- // Set other members and put in vector
- header.object = NULL;
- header.header.version = 0;
- header.header.headerSize = 0;
- header.header.contentSize = 0;
- header.header.compression = BLOCKCOMPRESSION_NONE;
- blocks.push_back(header);
- }
- }
- catch (...) {
- delete filePtr;
- throw;
- }
- }
- WorldFileLoad::~WorldFileLoad() { start_func
- if (filePtr) delete filePtr;
- }
- void WorldFileLoad::scanBlocks() { start_func
- scanNextBlock = 0;
- }
-
- int WorldFileLoad::nextBlock(Uint32* getType, Uint32* getID) { start_func
- while (scanNextBlock < numBlocks) {
- if (blocks[scanNextBlock].type != BLOCKTYPE_UNUSED) {
- *getType = blocks[scanNextBlock].type;
- *getID = scanNextBlock++;
- return 1;
- }
- ++scanNextBlock;
- }
-
- return 0;
- }
- void WorldFileLoad::claimBlock(Uint32 id, LoadOnly* claimingObject) throw_File { start_func
- assert(id >= 0);
- assert(id < numBlocks);
- assert(claimingObject);
- assert(blocks[id].object == NULL);
- assert(blocks[id].type != BLOCKTYPE_UNUSED);
- assert(filePtr);
-
- // Ensure header is read
- if (!blocks[id].header.version) {
- filePtr->seek(blocks[id].offset);
- blocks[id].header.version = filePtr->readInt();
- blocks[id].header.headerSize = filePtr->readInt();
- blocks[id].header.contentSize = filePtr->readInt();
- blocks[id].header.compression = filePtr->readInt();
- }
-
- blocks[id].object = claimingObject;
-
- // Tell object to load header (throws but with no problems)
- FileRead headerFile(filePtr, blocks[id].offset + 16, blocks[id].header.headerSize,
- blocks[id].header.version, BLOCKCOMPRESSION_NONE);
- claimingObject->loadHeader(&headerFile);
-
- // Tell object to load content (doesn't throw; but should delete ptr for us if it does)
- FileRead* contentFile = new FileRead(filePtr,
- blocks[id].offset + blocks[id].header.headerSize + 16,
- blocks[id].header.contentSize,
- blocks[id].header.version,
- blocks[id].header.compression);
- claimingObject->loadContent(contentFile);
- }
|