gcsx_load.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /* GCSx
  2. ** LOAD.CPP
  3. **
  4. ** File loading only
  5. */
  6. /*****************************************************************************
  7. ** Copyright (C) 2003-2006 Janson
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ** GNU General Public License for more details.
  18. **
  19. ** You should have received a copy of the GNU General Public License
  20. ** along with this program; if not, write to the Free Software
  21. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  22. *****************************************************************************/
  23. #include "all.h"
  24. // Read interface
  25. FileRead::FileRead(File* myFilePtr, int offset, int size, Uint32 myVersion, Uint32 myCompression) throw_File { start_func
  26. filePtr = myFilePtr;
  27. currentOffset = startOffset = offset;
  28. bytesRead = 0;
  29. totalSize = size;
  30. version = myVersion;
  31. compression = myCompression;
  32. zlibBufferIn = NULL;
  33. zlibStream = NULL;
  34. zlibStreamInit = 0;
  35. try {
  36. if (compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB) {
  37. // Read original size and checksum
  38. if (size < 4) {
  39. throw FileException("Unexpected end-of-block");
  40. }
  41. filePtr->seek(currentOffset);
  42. zlibOrigSize = filePtr->readInt();
  43. bytesRead += 4;
  44. currentOffset += 4;
  45. // Prepare objects
  46. zlibBufferIn = new Byte[ZLIB_BUFFER_SIZE];
  47. zlibStream = new z_stream;
  48. // Get first block NOW, so we are most likely to read zlib header here
  49. zlibStream->avail_in = 0;
  50. zlibNextBuffer();
  51. // Use default alloc and free for now
  52. zlibStream->zalloc = NULL;
  53. zlibStream->zfree = NULL;
  54. zlibStream->opaque = NULL;
  55. if (inflateInit(zlibStream) != Z_OK) {
  56. fatalCrash(0, "ZLIB Memory/decompression error: %s", zlibStream->msg);
  57. }
  58. zlibStreamInit = 1;
  59. }
  60. }
  61. catch (...) {
  62. delete[] zlibBufferIn;
  63. if (zlibStreamInit) inflateEnd(zlibStream);
  64. delete zlibStream;
  65. throw;
  66. }
  67. }
  68. FileRead::~FileRead() { start_func
  69. delete[] zlibBufferIn;
  70. if (zlibStreamInit) inflateEnd(zlibStream);
  71. delete zlibStream;
  72. }
  73. void FileRead::zlibNextBuffer() throw_File { start_func
  74. assert(compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB);
  75. assert(zlibStream);
  76. assert(zlibBufferIn);
  77. assert(zlibStream->avail_in == 0);
  78. int amount = min(totalSize - bytesRead, (int)ZLIB_BUFFER_SIZE);
  79. if (amount == 0) {
  80. throw FileException("Unexpected end-of-block");
  81. }
  82. filePtr->seek(currentOffset);
  83. filePtr->read(zlibBufferIn, amount);
  84. zlibStream->next_in = zlibBufferIn;
  85. zlibStream->avail_in = amount;
  86. bytesRead += amount;
  87. currentOffset += amount;
  88. }
  89. int FileRead::getSize() const { start_func
  90. if (compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB) {
  91. return zlibOrigSize;
  92. }
  93. else {
  94. return totalSize;
  95. }
  96. }
  97. int FileRead::moreData() const { start_func
  98. if (compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB) {
  99. return zlibOrigSize - zlibStream->total_out;
  100. }
  101. else {
  102. return totalSize - bytesRead;
  103. }
  104. }
  105. void FileRead::read(void* ptr, int size) throw_File { start_func
  106. if (compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB) {
  107. zlibStream->next_out = (Byte*)ptr;
  108. zlibStream->avail_out = size;
  109. // Inflate until error, reading more buffer as needed
  110. while (zlibStream->avail_out) {
  111. // Need more input buffer? (this also checks if we overflow)
  112. if (zlibStream->avail_in == 0) zlibNextBuffer();
  113. int result = inflate(zlibStream, 0);
  114. if (result == Z_STREAM_END) {
  115. // End of stream- we'd better be done!
  116. if (zlibStream->avail_out) {
  117. throw FileException("Unexpected end of compressed data");
  118. }
  119. // We are, at least with this read; mark us as done, unless we rewind
  120. bytesRead = totalSize;
  121. }
  122. // OK/BUF_ERROR means we need or may need to read more buffer
  123. else if ((result != Z_OK) && (result != Z_BUF_ERROR)) {
  124. throw FileException("Decompression error: %s", zlibStream->msg);
  125. }
  126. }
  127. }
  128. else {
  129. if (bytesRead + size > totalSize) {
  130. throw FileException("Unexpected end-of-block");
  131. }
  132. filePtr->seek(currentOffset);
  133. filePtr->read(ptr, size);
  134. bytesRead += size;
  135. currentOffset += size;
  136. }
  137. }
  138. char FileRead::copyBuffer[COPY_BUFFER_SIZE + 1];
  139. void FileRead::readStr(string& str) throw_File { start_func
  140. int size = readInt16();
  141. int first = 1;
  142. if (size) {
  143. while (size) {
  144. int amount = min(size, (int)COPY_BUFFER_SIZE);
  145. read(copyBuffer, amount);
  146. copyBuffer[amount] = 0;
  147. if (first) str = copyBuffer;
  148. else str += copyBuffer;
  149. size -= amount;
  150. }
  151. }
  152. else {
  153. str = blankString;
  154. }
  155. }
  156. void FileRead::readIntBulk(Uint32* ptr, int count) throw_File { start_func
  157. read(ptr, 4 * count);
  158. #if SDL_BYTEORDER != SDL_LIL_ENDIAN
  159. for (int pos = 0; pos < count; ++pos) {
  160. Uint8* bytes = ptr++;
  161. swap(bytes[0], bytes[3]);
  162. swap(bytes[1], bytes[2]);
  163. }
  164. #endif
  165. }
  166. Uint32 FileRead::readInt() throw_File { start_func
  167. Uint32 result;
  168. #if SDL_BYTEORDER == SDL_LIL_ENDIAN
  169. read(&result, 4);
  170. #else
  171. Uint8 bytes[4];
  172. read(bytes, 4);
  173. result = (Uint32)bytes[0] | ((Uint32)bytes[1] << 8) | ((Uint32)bytes[2] << 16) | ((Uint32)bytes[1] << 24);
  174. #endif
  175. return result;
  176. }
  177. Uint16 FileRead::readInt16() throw_File { start_func
  178. Uint16 result;
  179. #if SDL_BYTEORDER == SDL_LIL_ENDIAN
  180. read(&result, 2);
  181. #else
  182. Uint8 bytes[2];
  183. read(bytes, 2);
  184. result = (Uint16)bytes[0] | ((Uint16)bytes[1] << 8);
  185. #endif
  186. return result;
  187. }
  188. Uint8 FileRead::readInt8() throw_File { start_func
  189. Uint8 result;
  190. read(&result, 1);
  191. return result;
  192. }
  193. void FileRead::rewind() throw_File { start_func
  194. if (compression == WorldFileLoad::BLOCKCOMPRESSION_ZLIB) {
  195. // Optimization- don't do anything if we're already rewound
  196. if (bytesRead - zlibStream->avail_in != 4) {
  197. // End current stream
  198. inflateEnd(zlibStream);
  199. zlibStreamInit = 0;
  200. // Back to beginning, AFTER stored size
  201. currentOffset = startOffset + 4;
  202. bytesRead = 4;
  203. // Reget first block
  204. zlibStream->avail_in = 0;
  205. zlibNextBuffer();
  206. // Use default alloc and free for now
  207. zlibStream->zalloc = NULL;
  208. zlibStream->zfree = NULL;
  209. zlibStream->opaque = NULL;
  210. if (inflateInit(zlibStream) != Z_OK) {
  211. // Should never be Z_VERSION_ERROR
  212. // Really shouldn't memory error either, we just deleted
  213. // memory of an equal amount; therefore we throw a File
  214. // exception so we don't pollute the throw()
  215. throw FileException("Decompression error: %s", zlibStream->msg);
  216. }
  217. zlibStreamInit = 1;
  218. }
  219. }
  220. else {
  221. currentOffset = startOffset;
  222. bytesRead = 0;
  223. }
  224. }
  225. // World file class for loading only
  226. const char* WorldFileLoad::correctCookie = "GCSx";
  227. const Uint32 WorldFileLoad::currentVersion[4] = {
  228. VER_MAJOR, VER_MINOR, VER_RELEASE, VER_BUILD,
  229. };
  230. WorldFileLoad::WorldFileLoad() : blocks() { start_func
  231. // This will only be called by WorldFile when creating a new file
  232. filename = NULL;
  233. filePtr = NULL;
  234. numBlocks = 0;
  235. scanNextBlock = 0;
  236. }
  237. WorldFileLoad::WorldFileLoad(const string* openFile, int mode) throw_File : blocks() { start_func
  238. filename = openFile;
  239. filePtr = new File(filename->c_str(), mode);
  240. try {
  241. filePtr->seek(0);
  242. // Check cookie
  243. char cookie[5];
  244. filePtr->read(cookie, 4);
  245. cookie[4] = 0;
  246. if (strcmp(correctCookie, cookie)) {
  247. throw FileException("File %s is not a GCSx file", filename->c_str());
  248. }
  249. // Read version; skip reserved
  250. // @TODO: Check version and throw exception
  251. fileVersion[0] = filePtr->readInt();
  252. fileVersion[1] = filePtr->readInt();
  253. fileVersion[2] = filePtr->readInt();
  254. fileVersion[3] = filePtr->readInt();
  255. filePtr->skip(8);
  256. // Count of blocks
  257. numBlocks = filePtr->readInt();
  258. blocks.reserve(numBlocks);
  259. scanNextBlock = 0;
  260. // Block headers
  261. for (Uint32 pos = 0; pos < numBlocks; ++pos) {
  262. BlockInfo header;
  263. // Read header
  264. header.offset = filePtr->readInt();
  265. header.size = filePtr->readInt();
  266. header.type = filePtr->readInt();
  267. filePtr->skip(4);
  268. // Set other members and put in vector
  269. header.object = NULL;
  270. header.header.version = 0;
  271. header.header.headerSize = 0;
  272. header.header.contentSize = 0;
  273. header.header.compression = BLOCKCOMPRESSION_NONE;
  274. blocks.push_back(header);
  275. }
  276. }
  277. catch (...) {
  278. delete filePtr;
  279. throw;
  280. }
  281. }
  282. WorldFileLoad::~WorldFileLoad() { start_func
  283. if (filePtr) delete filePtr;
  284. }
  285. void WorldFileLoad::scanBlocks() { start_func
  286. scanNextBlock = 0;
  287. }
  288. int WorldFileLoad::nextBlock(Uint32* getType, Uint32* getID) { start_func
  289. while (scanNextBlock < numBlocks) {
  290. if (blocks[scanNextBlock].type != BLOCKTYPE_UNUSED) {
  291. *getType = blocks[scanNextBlock].type;
  292. *getID = scanNextBlock++;
  293. return 1;
  294. }
  295. ++scanNextBlock;
  296. }
  297. return 0;
  298. }
  299. void WorldFileLoad::claimBlock(Uint32 id, LoadOnly* claimingObject) throw_File { start_func
  300. assert(id >= 0);
  301. assert(id < numBlocks);
  302. assert(claimingObject);
  303. assert(blocks[id].object == NULL);
  304. assert(blocks[id].type != BLOCKTYPE_UNUSED);
  305. assert(filePtr);
  306. // Ensure header is read
  307. if (!blocks[id].header.version) {
  308. filePtr->seek(blocks[id].offset);
  309. blocks[id].header.version = filePtr->readInt();
  310. blocks[id].header.headerSize = filePtr->readInt();
  311. blocks[id].header.contentSize = filePtr->readInt();
  312. blocks[id].header.compression = filePtr->readInt();
  313. }
  314. blocks[id].object = claimingObject;
  315. // Tell object to load header (throws but with no problems)
  316. FileRead headerFile(filePtr, blocks[id].offset + 16, blocks[id].header.headerSize,
  317. blocks[id].header.version, BLOCKCOMPRESSION_NONE);
  318. claimingObject->loadHeader(&headerFile);
  319. // Tell object to load content (doesn't throw; but should delete ptr for us if it does)
  320. FileRead* contentFile = new FileRead(filePtr,
  321. blocks[id].offset + blocks[id].header.headerSize + 16,
  322. blocks[id].header.contentSize,
  323. blocks[id].header.version,
  324. blocks[id].header.compression);
  325. claimingObject->loadContent(contentFile);
  326. }