goblib.cpp 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877
  1. /*****************************************
  2. *
  3. * GOB File System
  4. *
  5. * Here's what Merriam-Webster says about "gob": --Chuck
  6. * Entry: gob
  7. * Function: noun
  8. * Etymology: Middle English gobbe, from Middle French gobe large piece of food,
  9. * back-formation from gobet
  10. * Date: 14th century
  11. * 1 : LUMP
  12. * 2 : a large amount -- usually used in plural <gobs of money>
  13. *
  14. * Purpose: Provide fast, efficient disk access on a variety of platforms.
  15. *
  16. * Implementation:
  17. * The GOB system maintains two files -- GOB and GFC. The GOB file is actually
  18. * an archive of many files split into variable size, compressed blocks. The GFC,
  19. * GOB File Control, contains 3 tables -- a block table, basic file table, and
  20. * extended file table. The block table is analogous to a DOS FAT. The basic
  21. * file table contains a minimal set of file information to handle basic reading
  22. * tasks. The extended file table is optionally loaded and contains additional
  23. * file information. File names are case insensitive.
  24. *
  25. * Files can be read in a normal manner. Open, read, seek and close
  26. * operations are all provided. Files can only be written in a single
  27. * contiguous chunk of blocks at the end of an archive. Reads are processed
  28. * through a configurable number of read ahead buffers to in an effort to
  29. * minimize both reads and seeks. Other operations including delete, verify,
  30. * access, and get size are also supported on files inside an archive.
  31. *
  32. * The system supports read profiling. By supplying a file read callback
  33. * function, the library will output the block number of each read. This can
  34. * be used rearrange block in the archive to minimize seek times. The
  35. * GOBRearrange sorts files in an archive.
  36. *
  37. * Supports block based caching. Primarily aimed at caching files off a DVD/CD
  38. * to a faster hard disk.
  39. *
  40. * Future Work:
  41. *
  42. * Dependencies: vvInt, snprintf, zlib
  43. * Owner: Chris McEvoy
  44. * History:
  45. * 09/23/2001 Original version
  46. * 10/28/2002 Merged into vvtech
  47. *
  48. * Copyright (C) 2002, Vicarious Visions, Inc. All Rights Reserved.
  49. *
  50. * UNPUBLISHED -- Rights reserved under the copyright laws of the
  51. * United States. Use of a copyright notice is precautionary only and
  52. * does not imply publication or disclosure.
  53. *
  54. * THIS DOCUMENTATION CONTAINS CONFIDENTIAL AND PROPRIETARY INFORMATION
  55. * OF VICARIOUS VISIONS, INC. ANY DUPLICATION, MODIFICATION,
  56. * DISTRIBUTION, OR DISCLOSURE IS STRICTLY PROHIBITED WITHOUT THE PRIOR
  57. * EXPRESS WRITTEN PERMISSION OF VICARIOUS VISIONS, INC.
  58. *
  59. *****************************************/
  60. /*
  61. This is an unofficial branch of GOB, for Jedi Academy
  62. Maintainer: Brian Osman
  63. */
  64. #include "goblib.h"
  65. #include "../zlib/zlib.h"
  66. #include <stdio.h>
  67. #include <stdlib.h>
  68. #include <string.h>
  69. #include <ctype.h>
  70. #include <assert.h>
  71. #if (VV_PLATFORM == VV_PLATFORM_WIN) || (VV_PLATFORM == VV_PLATFORM_XBOX)
  72. # define CDECL __cdecl
  73. #else
  74. # define CDECL
  75. #endif
  76. // Profiling data
  77. static GOBProfileReadFunc ProfileReadCallback = NULL;
  78. static GOBBool ProfileEnabled = GOB_FALSE;
  79. // Indicates whether or not the library has been initialized
  80. static GOBBool LibraryInit = GOB_FALSE;
  81. // Callbacks for handling low-level compression/decompression
  82. static struct GOBCodecFuncSet CodecFuncs;
  83. // Callbacks for handling low-level memory alloc and free
  84. static struct GOBMemoryFuncSet MemFuncs;
  85. // Callbacks for handling low-level file access
  86. static struct GOBFileSysFuncSet FSFuncs;
  87. // Callbacks for handling block caching (ie Xbox temp space)
  88. static struct GOBCacheFileFuncSet CacheFileFuncs;
  89. static GOBBool CacheFileActive = GOB_FALSE;
  90. // Name of the GFC file
  91. static GOBChar ControlFileName[GOB_MAX_FILE_NAME_LEN];
  92. // Handle to the GOB archive
  93. static GOBFSHandle ArchiveHandle = (GOBFSHandle*)0xFFFFFFFF;
  94. // Size of the active GOB archive
  95. static GOBUInt32 ArchiveSize = 0;
  96. static GOBUInt32 ArchiveNumBlocks = 0;
  97. static GOBUInt32 ArchiveNumFiles = 0;
  98. // Cached blocks
  99. struct GOBBlockCache
  100. {
  101. GOBChar* data;
  102. GOBUInt32 block;
  103. GOBUInt32 time;
  104. GOBUInt32 size;
  105. };
  106. static struct GOBBlockCache* CacheBlocks = NULL;
  107. static GOBUInt32 NumCacheBlocks = 0;
  108. static GOBUInt32 CacheBlockCounter = 0;
  109. // Read ahead buffer
  110. struct GOBReadBuffer
  111. {
  112. GOBChar* data;
  113. GOBChar* dataStart;
  114. GOBUInt32 pos;
  115. GOBUInt32 size;
  116. };
  117. static struct GOBReadBuffer ReadBuffer;
  118. // Decompression buffer
  119. static GOBChar* DecompBuffer = NULL;
  120. // Stats gathering
  121. static struct GOBReadStats ReadStats;
  122. static GOBUInt32 CurrentArchivePos = 0;
  123. // File tables (from the GFC)
  124. static struct GOBFileTableBasicEntry* FileTableBasic = NULL;
  125. static struct GOBFileTableExtEntry* FileTableExt = NULL;
  126. // Block tables (from the GFC)
  127. static struct GOBBlockTableEntry* BlockTable = NULL;
  128. static GOBUInt32* BlockCRC = NULL;
  129. static GOBUInt32* CacheFileTable = NULL;
  130. // Do the tables need to be written?
  131. static GOBBool FileTableDirty = GOB_FALSE;
  132. // Information about open files
  133. struct OpenFileInfo
  134. {
  135. GOBBool valid;
  136. GOBUInt32 startBlock;
  137. GOBUInt32 block;
  138. GOBUInt32 offset;
  139. GOBUInt32 pos;
  140. GOBUInt32 size;
  141. };
  142. // Open file table -- indices in this array are passed
  143. // back to the caller as pseudo file handles.
  144. static struct OpenFileInfo OpenFiles[GOB_MAX_OPEN_FILES];
  145. // Converting text to lower case -- this isn't very
  146. // clean. A common buffer is used to store lower case
  147. // text. So its not thread safe... among other things. ;)
  148. static GOBChar LowerCaseBuffer[GOB_MAX_FILE_NAME_LEN];
  149. static GOBChar* LowerCase(const GOBChar* name)
  150. {
  151. GOBInt32 i;
  152. for (i = 0; name[i]; ++i) {
  153. LowerCaseBuffer[i] = (GOBChar)tolower(name[i]);
  154. }
  155. LowerCaseBuffer[i] = 0;
  156. return LowerCaseBuffer;
  157. }
  158. // Checks if a file handle is invalid
  159. static GOBBool InvalidHandle(GOBFSHandle h)
  160. {
  161. return (GOBUInt32)h == 0xFFFFFFFF ? GOB_TRUE : GOB_FALSE;
  162. }
  163. // Endian conversion
  164. #if VV_ENDIAN == VV_ENDIAN_LITTLE
  165. static GOBUInt32 SwapBytes(GOBUInt32 x)
  166. {
  167. return
  168. (x >> 24) |
  169. ((x >> 8) & 0xFF00) |
  170. ((x << 8) & 0xFF0000) |
  171. (x << 24);
  172. }
  173. #else
  174. static GOBUInt32 SwapBytes(GOBUInt32 x)
  175. {
  176. return x;
  177. }
  178. #endif
  179. // Given a file name, get its index in the FileTable
  180. static GOBInt32 GetFileTableEntry(const GOBChar* file)
  181. {
  182. GOBUInt32 entry;
  183. GOBUInt32 hash;
  184. // hash the file name
  185. hash = crc32(0L, Z_NULL, 0);
  186. hash = crc32(hash, (const unsigned char*)file, strlen(file));
  187. // linear search for matching a matching hash
  188. for (entry = 0; entry < ArchiveNumFiles; ++entry) {
  189. if (FileTableBasic[entry].block != GOB_INVALID_BLOCK &&
  190. FileTableBasic[entry].hash == hash)
  191. {
  192. return entry;
  193. }
  194. }
  195. return -1;
  196. }
  197. // Mark the contents of cache and read buffer invalid
  198. static GOBVoid InvalidateCache(GOBVoid)
  199. {
  200. GOBUInt32 i;
  201. for (i = 0; i < NumCacheBlocks; ++i) {
  202. CacheBlocks[i].block = 0xFFFFFFFF;
  203. }
  204. ReadBuffer.pos = 0xFFFFFFFF;
  205. }
  206. // Deallocate memory used by cache and read buffer
  207. static GOBVoid FreeCache(GOBVoid)
  208. {
  209. GOBUInt32 i;
  210. if (CacheBlocks) {
  211. for (i = 0; i < NumCacheBlocks; ++i) {
  212. if (CacheBlocks[i].data) MemFuncs.free(CacheBlocks[i].data);
  213. CacheBlocks[i].data = NULL;
  214. }
  215. MemFuncs.free(CacheBlocks);
  216. NumCacheBlocks = 0;
  217. CacheBlocks = NULL;
  218. }
  219. }
  220. // Write the file table to disk if the form of a GFC
  221. static GOBError CommitFileTable(GOBVoid)
  222. {
  223. GOBUInt32 num;
  224. struct GOBFileTableBasicEntry basic;
  225. struct GOBFileTableExtEntry ext;
  226. struct GOBBlockTableEntry block;
  227. // open the GFC
  228. GOBFSHandle handle = FSFuncs.open(ControlFileName, GOBACCESS_WRITE);
  229. if (InvalidHandle(handle)) return GOBERR_FILE_WRITE;
  230. // write the magic identifier
  231. num = SwapBytes(GOB_MAGIC_IDENTIFIER);
  232. if (!FSFuncs.write(handle, &num, sizeof(num))) return GOBERR_FILE_WRITE;
  233. // write the size of the GOB
  234. num = SwapBytes(ArchiveSize);
  235. if (!FSFuncs.write(handle, &num, sizeof(num))) return GOBERR_FILE_WRITE;
  236. // write number of blocks in archive
  237. num = SwapBytes(ArchiveNumBlocks);
  238. if (!FSFuncs.write(handle, &num, sizeof(num))) return GOBERR_FILE_WRITE;
  239. // write number of file in archive
  240. num = SwapBytes(ArchiveNumFiles);
  241. if (!FSFuncs.write(handle, &num, sizeof(num))) return GOBERR_FILE_WRITE;
  242. // write block table -- with endian conversion
  243. for (num = 0; num < ArchiveNumBlocks; ++num) {
  244. block.next = SwapBytes(BlockTable[num].next);
  245. block.offset = SwapBytes(BlockTable[num].offset);
  246. block.size = SwapBytes(BlockTable[num].size);
  247. if (!FSFuncs.write(handle, &block, sizeof(block))) return GOBERR_FILE_WRITE;
  248. }
  249. // write block CRCs -- with endian conversion
  250. for (num = 0; num < ArchiveNumBlocks; ++num) {
  251. BlockCRC[num] = SwapBytes(BlockCRC[num]);
  252. if (!FSFuncs.write(handle, &BlockCRC[num], sizeof(BlockCRC[num]))) {
  253. return GOBERR_FILE_WRITE;
  254. }
  255. }
  256. // write each basic table entry -- with endian conversion
  257. for (num = 0; num < ArchiveNumFiles; ++num) {
  258. basic.hash = SwapBytes(FileTableBasic[num].hash);
  259. basic.block = SwapBytes(FileTableBasic[num].block);
  260. basic.size = SwapBytes(FileTableBasic[num].size);
  261. if (!FSFuncs.write(handle, &basic, sizeof(basic))) return GOBERR_FILE_WRITE;
  262. }
  263. // write each extended table entry -- with endian conversion
  264. for (num = 0; num < ArchiveNumFiles; ++num) {
  265. strcpy(ext.name, FileTableExt[num].name);
  266. ext.crc = SwapBytes(FileTableExt[num].crc);
  267. ext.time = SwapBytes(FileTableExt[num].time);
  268. if (!FSFuncs.write(handle, &ext, sizeof(ext))) return GOBERR_FILE_WRITE;
  269. }
  270. // all done
  271. FSFuncs.close(&handle);
  272. FileTableDirty = GOB_FALSE;
  273. return GOBERR_OK;
  274. }
  275. static GOBVoid DeallocTables(GOBVoid)
  276. {
  277. if (BlockTable) {
  278. // free the block table
  279. MemFuncs.free(BlockTable);
  280. BlockTable = NULL;
  281. }
  282. if (BlockCRC) {
  283. // free the block crc table
  284. MemFuncs.free(BlockCRC);
  285. BlockCRC = NULL;
  286. }
  287. if (CacheFileTable)
  288. {
  289. // free the block cache table
  290. MemFuncs.free(CacheFileTable);
  291. CacheFileTable = NULL;
  292. }
  293. if (FileTableBasic) {
  294. // free the basic file table
  295. MemFuncs.free(FileTableBasic);
  296. FileTableBasic = NULL;
  297. }
  298. if (FileTableExt) {
  299. // free the extended file table
  300. MemFuncs.free(FileTableExt);
  301. FileTableExt = NULL;
  302. }
  303. }
  304. static GOBError AllocTables(GOBUInt32 num_blocks, GOBUInt32 num_files,
  305. GOBBool extended, GOBBool safe)
  306. {
  307. GOBUInt32 num;
  308. // dump any old tables
  309. DeallocTables();
  310. // allocate the block table
  311. BlockTable = (struct GOBBlockTableEntry*)
  312. MemFuncs.alloc(num_blocks * sizeof(struct GOBBlockTableEntry));
  313. if (!BlockTable) return GOBERR_NO_MEMORY;
  314. if (safe) {
  315. // allocate the block crc table for verifying data validity
  316. BlockCRC = (GOBUInt32*)MemFuncs.alloc(num_blocks * sizeof(GOBUInt32));
  317. if (!BlockCRC) return GOBERR_NO_MEMORY;
  318. }
  319. else {
  320. BlockCRC = NULL;
  321. }
  322. if (CacheFileActive)
  323. {
  324. // allocate the block cache bitfield
  325. CacheFileTable = (GOBUInt32*)
  326. MemFuncs.alloc((num_blocks / 32 + 1) * 4);
  327. if (!CacheFileTable) return GOBERR_NO_MEMORY;
  328. }
  329. // allocate the basic file table
  330. FileTableBasic = (struct GOBFileTableBasicEntry*)
  331. MemFuncs.alloc(num_files * sizeof(struct GOBFileTableBasicEntry));
  332. if (!FileTableBasic) return GOBERR_NO_MEMORY;
  333. if (extended) {
  334. // allocate the extended file table
  335. FileTableExt = (struct GOBFileTableExtEntry*)
  336. MemFuncs.alloc(num_files * sizeof(struct GOBFileTableExtEntry));
  337. if (!FileTableExt) return GOBERR_NO_MEMORY;
  338. }
  339. else {
  340. FileTableExt = NULL;
  341. }
  342. // clear the tables
  343. for (num = 0; num < num_files; ++num) {
  344. FileTableBasic[num].block = GOB_INVALID_BLOCK;
  345. if (FileTableExt) FileTableExt[num].name[0] = 0;
  346. }
  347. for (num = 0; num < num_blocks; ++num) {
  348. BlockTable[num].next = GOB_INVALID_BLOCK;
  349. BlockTable[num].size = GOB_INVALID_SIZE;
  350. }
  351. return GOBERR_OK;
  352. }
  353. // GOBInit
  354. // Public function. Initialize the library.
  355. GOBError GOBInit(struct GOBMemoryFuncSet* mem,
  356. struct GOBFileSysFuncSet* file,
  357. struct GOBCodecFuncSet* codec,
  358. struct GOBCacheFileFuncSet* cache)
  359. {
  360. GOBInt32 i;
  361. GOBError err;
  362. if (LibraryInit) return GOBERR_ALREADY_INIT;
  363. // setup the callbacks
  364. MemFuncs = *mem;
  365. FSFuncs = *file;
  366. CodecFuncs = *codec;
  367. if (cache) {
  368. CacheFileFuncs = *cache;
  369. CacheFileActive = GOB_TRUE;
  370. } else {
  371. CacheFileActive = GOB_FALSE;
  372. }
  373. // allocate decompression buffer
  374. DecompBuffer = (GOBChar*)MemFuncs.alloc(GOB_BLOCK_SIZE + GOB_COMPRESS_OVERHEAD);
  375. if (!DecompBuffer) return GOBERR_NO_MEMORY;
  376. // clear open table
  377. for (i = 0; i < GOB_MAX_OPEN_FILES; ++i) {
  378. OpenFiles[i].valid = GOB_FALSE;
  379. }
  380. LibraryInit = GOB_TRUE;
  381. err = GOBSetCacheSize(1);
  382. if (err != GOBERR_OK) {
  383. LibraryInit = GOB_FALSE;
  384. return err;
  385. }
  386. ReadBuffer.data = NULL;
  387. err = GOBSetReadBufferSize(128*1024);
  388. if (err != GOBERR_OK) {
  389. LibraryInit = GOB_FALSE;
  390. return err;
  391. }
  392. return GOBERR_OK;
  393. }
  394. // GOBShutdown
  395. // Public function. Close the library.
  396. GOBError GOBShutdown(GOBVoid)
  397. {
  398. if (!LibraryInit) return GOBERR_NOT_INIT;
  399. // if we have an open archive, close it
  400. if (!InvalidHandle(ArchiveHandle)) GOBArchiveClose();
  401. FreeCache();
  402. // free read ahead buffer
  403. if (ReadBuffer.data) {
  404. MemFuncs.free(ReadBuffer.data);
  405. ReadBuffer.data = NULL;
  406. }
  407. // free decompression buffer
  408. MemFuncs.free(DecompBuffer);
  409. // free the file and block tables
  410. DeallocTables();
  411. LibraryInit = GOB_FALSE;
  412. return GOBERR_OK;
  413. }
  414. // GOBArchiveCreate
  415. // Public function. Create an empty GFC and GOB.
  416. GOBError GOBArchiveCreate(const GOBChar* file)
  417. {
  418. GOBChar fname[GOB_MAX_FILE_NAME_LEN];
  419. GOBFSHandle handle;
  420. GOBError error;
  421. if (!LibraryInit) return GOBERR_NOT_INIT;
  422. if (!InvalidHandle(ArchiveHandle)) return GOBERR_ALREADY_OPEN;
  423. // Allocate the max space for tables
  424. error = AllocTables(GOB_MAX_BLOCKS, GOB_MAX_FILES, GOB_TRUE, GOB_TRUE);
  425. if (GOBERR_OK != error) {
  426. return error;
  427. }
  428. // create an empty GFC
  429. _snprintf(ControlFileName, GOB_MAX_FILE_NAME_LEN, "%s.gfc", file);
  430. ArchiveSize = 0;
  431. ArchiveNumBlocks = 0;
  432. ArchiveNumFiles = 0;
  433. CacheFileActive = GOB_FALSE;
  434. CommitFileTable();
  435. // create an empty GOB
  436. _snprintf(fname, GOB_MAX_FILE_NAME_LEN, "%s.gob", file);
  437. handle = FSFuncs.open(fname, GOBACCESS_WRITE);
  438. if (InvalidHandle(handle)) return GOBERR_CANNOT_CREATE;
  439. FSFuncs.close(&handle);
  440. return GOBERR_OK;
  441. }
  442. // GOBArchiveOpen
  443. // Public function. Open a GOB file and cache file tables.
  444. GOBError GOBArchiveOpen(const GOBChar* file, GOBAccessType atype,
  445. GOBBool extended, GOBBool safe)
  446. {
  447. GOBChar fname[GOB_MAX_FILE_NAME_LEN];
  448. GOBFSHandle handle;
  449. GOBUInt32 magic;
  450. GOBUInt32 i;
  451. GOBError error;
  452. if (!LibraryInit) return GOBERR_NOT_INIT;
  453. if (!InvalidHandle(ArchiveHandle)) return GOBERR_ALREADY_OPEN;
  454. // open the GFC
  455. _snprintf(ControlFileName, GOB_MAX_FILE_NAME_LEN, "%s.gfc", file);
  456. handle = FSFuncs.open(ControlFileName, atype);
  457. if (InvalidHandle(handle)) return GOBERR_FILE_NOT_FOUND;
  458. // read and check the magic
  459. if (!FSFuncs.read(handle, &magic, sizeof(magic))) return GOBERR_FILE_READ;
  460. if (SwapBytes(magic) != GOB_MAGIC_IDENTIFIER) return GOBERR_NOT_GOB_FILE;
  461. // read the GOB archive size
  462. if (!FSFuncs.read(handle, &ArchiveSize, sizeof(ArchiveSize))) return GOBERR_FILE_READ;
  463. ArchiveSize = SwapBytes(ArchiveSize);
  464. // read the number of blocks
  465. if (!FSFuncs.read(handle, &ArchiveNumBlocks, sizeof(ArchiveNumBlocks))) return GOBERR_FILE_READ;
  466. ArchiveNumBlocks = SwapBytes(ArchiveNumBlocks);
  467. // read the number of files
  468. if (!FSFuncs.read(handle, &ArchiveNumFiles, sizeof(ArchiveNumFiles))) return GOBERR_FILE_READ;
  469. ArchiveNumFiles = SwapBytes(ArchiveNumFiles);
  470. // Allocate the space for tables
  471. if (atype == GOBACCESS_READ) {
  472. error = AllocTables(ArchiveNumBlocks, ArchiveNumFiles, extended, safe);
  473. }
  474. else {
  475. error = AllocTables(GOB_MAX_BLOCKS, GOB_MAX_FILES, extended, safe);
  476. }
  477. if (GOBERR_OK != error) {
  478. return error;
  479. }
  480. // read the block table
  481. if (ArchiveNumBlocks &&
  482. !FSFuncs.read(handle, BlockTable,
  483. sizeof(struct GOBBlockTableEntry) * ArchiveNumBlocks))
  484. {
  485. return GOBERR_FILE_READ;
  486. }
  487. if (BlockCRC) {
  488. // read the block CRCs
  489. if (ArchiveNumBlocks &&
  490. !FSFuncs.read(handle, BlockCRC,
  491. sizeof(GOBUInt32) * ArchiveNumBlocks))
  492. {
  493. return GOBERR_FILE_READ;
  494. }
  495. }
  496. else {
  497. // skip block CRCs
  498. FSFuncs.seek(handle, sizeof(GOBUInt32) * ArchiveNumBlocks,
  499. GOBSEEK_CURRENT);
  500. }
  501. if (CacheFileActive)
  502. {
  503. // clear the block cache table
  504. for (i = 0; i < ArchiveNumBlocks / 32; ++i) {
  505. CacheFileTable[i] = 0;
  506. }
  507. }
  508. // open the cache file
  509. if (CacheFileActive && !CacheFileFuncs.open(ArchiveSize)) {
  510. CacheFileActive = GOB_FALSE;
  511. }
  512. // endian convert the table
  513. for (i = 0; i < ArchiveNumBlocks; ++i) {
  514. BlockTable[i].next = SwapBytes(BlockTable[i].next);
  515. BlockTable[i].offset = SwapBytes(BlockTable[i].offset);
  516. BlockTable[i].size = SwapBytes(BlockTable[i].size);
  517. if (BlockCRC) {
  518. BlockCRC[i] = SwapBytes(BlockCRC[i]);
  519. }
  520. }
  521. // read the basic file table
  522. if (ArchiveNumFiles &&
  523. !FSFuncs.read(handle, FileTableBasic,
  524. sizeof(struct GOBFileTableBasicEntry) * ArchiveNumFiles))
  525. {
  526. return GOBERR_FILE_READ;
  527. }
  528. // endian convert the table
  529. for (i = 0; i < ArchiveNumFiles; ++i) {
  530. FileTableBasic[i].hash = SwapBytes(FileTableBasic[i].hash);
  531. FileTableBasic[i].block = SwapBytes(FileTableBasic[i].block);
  532. FileTableBasic[i].size = SwapBytes(FileTableBasic[i].size);
  533. }
  534. // if we have memory for the extended file table
  535. if (FileTableExt) {
  536. // read the table
  537. if (ArchiveNumFiles &&
  538. !FSFuncs.read(handle, FileTableExt,
  539. sizeof(struct GOBFileTableExtEntry) * ArchiveNumFiles))
  540. {
  541. return GOBERR_FILE_READ;
  542. }
  543. // endian convert the table
  544. for (i = 0; i < ArchiveNumFiles; ++i) {
  545. FileTableExt[i].crc = SwapBytes(FileTableExt[i].crc);
  546. FileTableExt[i].time = SwapBytes(FileTableExt[i].time);
  547. }
  548. }
  549. FSFuncs.close(&handle);
  550. // open the GOB
  551. _snprintf(fname, GOB_MAX_FILE_NAME_LEN, "%s.gob", file);
  552. ArchiveHandle = FSFuncs.open(fname, atype);
  553. if (InvalidHandle(ArchiveHandle)) return GOBERR_FILE_NOT_FOUND;
  554. // initialize stats gathering
  555. CurrentArchivePos = 0;
  556. ReadStats.bufferUsed = 0;
  557. ReadStats.bytesRead = 0;
  558. ReadStats.cacheBytesRead = 0;
  559. ReadStats.cacheBytesWrite = 0;
  560. ReadStats.totalSeeks = 0;
  561. ReadStats.farSeeks = 0;
  562. ReadStats.filesOpened = 0;
  563. return GOBERR_OK;
  564. }
  565. // GOBArchiveClose
  566. // Public function. Close an open GOB archive.
  567. GOBError GOBArchiveClose(GOBVoid)
  568. {
  569. GOBInt32 i;
  570. if (!LibraryInit) return GOBERR_NOT_INIT;
  571. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  572. // close any open files
  573. for (i = 0; i < GOB_MAX_OPEN_FILES; ++i) {
  574. GOBClose(i);
  575. }
  576. // close the GOB
  577. FSFuncs.close(&ArchiveHandle);
  578. ArchiveHandle = (GOBFSHandle*)0xFFFFFFFF;
  579. // commit the file table if we're updated it
  580. if (FileTableDirty) {
  581. CommitFileTable();
  582. }
  583. // close the cache file
  584. if (CacheFileActive) {
  585. CacheFileFuncs.close();
  586. CacheFileActive = GOB_FALSE;
  587. }
  588. return GOBERR_OK;
  589. }
  590. static int CDECL SortBlockDescsCallback(const void* elem1, const void* elem2)
  591. {
  592. return (int)((struct GOBBlockTableEntry *)elem1)->offset -
  593. (int)((struct GOBBlockTableEntry *)elem2)->offset;
  594. }
  595. // GOBArchiveCheckMarkers
  596. // Public function. Check start/end markers to check approximate validity of GOB file
  597. GOBError GOBArchiveCheckMarkers(GOBVoid)
  598. {
  599. GOBUInt32 i;
  600. GOBUInt32 valid_blocks;
  601. struct GOBBlockTableEntry *blocks;
  602. GOBUInt32 block;
  603. GOBUInt32 start_marker;
  604. GOBUInt32 end_marker;
  605. GOBBool ok;
  606. if (!LibraryInit) return GOBERR_NOT_INIT;
  607. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  608. // count valid blocks
  609. valid_blocks = 0;
  610. for (i = 0; i < ArchiveNumBlocks; i++)
  611. {
  612. if (BlockTable[i].size != GOB_INVALID_SIZE &&
  613. BlockTable[i].next != GOB_INVALID_BLOCK)
  614. {
  615. valid_blocks++;
  616. }
  617. }
  618. // arcvive is empty
  619. if (valid_blocks == 0)
  620. {
  621. return GOBERR_OK;
  622. }
  623. // alloc mem for valid block list
  624. blocks = (GOBBlockTableEntry *) MemFuncs.alloc(sizeof(*blocks) * valid_blocks);
  625. if (blocks == NULL)
  626. {
  627. return GOBERR_NO_MEMORY;
  628. }
  629. // copy valid blocks descriptions
  630. block = 0;
  631. for (i = 0; i < ArchiveNumBlocks; ++i)
  632. {
  633. if (BlockTable[i].size != GOB_INVALID_SIZE &&
  634. BlockTable[i].next != GOB_INVALID_BLOCK)
  635. {
  636. blocks[block++] = BlockTable[i];
  637. }
  638. }
  639. assert(block == valid_blocks);
  640. // and sort 'em
  641. qsort(blocks, valid_blocks, sizeof(*blocks), SortBlockDescsCallback);
  642. // suppress some warnings
  643. start_marker = 0;
  644. end_marker = 0;
  645. // now scan entire archive for start-of-block and end-of-block markers
  646. for (i = 0; i < valid_blocks; i++)
  647. {
  648. ok = GOB_TRUE;
  649. ok = ok && !FSFuncs.seek(ArchiveHandle, blocks[i].offset, GOBSEEK_START);
  650. ok = ok && FSFuncs.read(ArchiveHandle, &start_marker, sizeof(GOBUInt32)) == sizeof(GOBUInt32);
  651. ok = ok && !FSFuncs.seek(ArchiveHandle, blocks[i].offset + blocks[i].size - sizeof(GOBUInt32), GOBSEEK_START);
  652. ok = ok && FSFuncs.read(ArchiveHandle, &end_marker, sizeof(GOBUInt32)) == sizeof(GOBUInt32);
  653. if (!ok ||
  654. SwapBytes(start_marker) != GOBMARKER_STARTBLOCK ||
  655. SwapBytes(end_marker) != GOBMARKER_ENDBLOCK)
  656. {
  657. MemFuncs.free(blocks);
  658. return GOBERR_NOT_GOB_FILE;
  659. }
  660. }
  661. MemFuncs.free(blocks);
  662. return GOBERR_OK;
  663. }
  664. // GOBArchiveCreate
  665. // Public function. Create an empty GFC and GOB.
  666. GOBUInt32 GOBGetSlack(GOBUInt32 x)
  667. {
  668. GOBUInt32 align = x % GOB_BLOCK_ALIGNMENT;
  669. if (align) return GOB_BLOCK_ALIGNMENT - align;
  670. return 0;
  671. }
  672. // GOBOpen
  673. // Public function. Open a file inside a GOB.
  674. GOBError GOBOpen(GOBChar* file, GOBHandle* handle)
  675. {
  676. GOBInt32 entry;
  677. GOBChar* lfile;
  678. if (!LibraryInit) return GOBERR_NOT_INIT;
  679. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  680. // find a free handle
  681. for (*handle = 0; *handle < GOB_MAX_OPEN_FILES; (*handle) += 1) {
  682. if (!OpenFiles[*handle].valid) break;
  683. }
  684. if (*handle >= GOB_MAX_OPEN_FILES) return GOBERR_TOO_MANY_OPEN;
  685. // find the file in the table
  686. lfile = LowerCase(file);
  687. entry = GetFileTableEntry(lfile);
  688. if (entry == -1) return GOBERR_FILE_NOT_FOUND;
  689. // setup the open file
  690. OpenFiles[*handle].startBlock = OpenFiles[*handle].block =
  691. FileTableBasic[entry].block;
  692. OpenFiles[*handle].size = FileTableBasic[entry].size;
  693. OpenFiles[*handle].offset = 0;
  694. OpenFiles[*handle].pos = 0;
  695. OpenFiles[*handle].valid = GOB_TRUE;
  696. ++ReadStats.filesOpened;
  697. return GOBERR_OK;
  698. }
  699. // GOBOpenCode
  700. // Public function. Open file with a code inside a GOB.
  701. GOBError GOBOpenCode(GOBInt32 code, GOBHandle* handle)
  702. {
  703. if (!LibraryInit) return GOBERR_NOT_INIT;
  704. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  705. // find a free handle
  706. for (*handle = 0; *handle < GOB_MAX_OPEN_FILES; (*handle) += 1) {
  707. if (!OpenFiles[*handle].valid) break;
  708. }
  709. if (*handle >= GOB_MAX_OPEN_FILES) return GOBERR_TOO_MANY_OPEN;
  710. // setup the open file
  711. OpenFiles[*handle].startBlock = OpenFiles[*handle].block =
  712. FileTableBasic[code].block;
  713. OpenFiles[*handle].size = FileTableBasic[code].size;
  714. OpenFiles[*handle].offset = 0;
  715. OpenFiles[*handle].pos = 0;
  716. OpenFiles[*handle].valid = GOB_TRUE;
  717. ++ReadStats.filesOpened;
  718. return GOBERR_OK;
  719. }
  720. // GOBClose
  721. // Public function. Close a file.
  722. GOBError GOBClose(GOBHandle handle)
  723. {
  724. if (!LibraryInit) return GOBERR_NOT_INIT;
  725. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  726. if (!OpenFiles[handle].valid) return GOBERR_NOT_OPEN;
  727. // close the file by simply invalidating the open
  728. // file table entry
  729. OpenFiles[handle].valid = GOB_FALSE;
  730. return GOBERR_OK;
  731. }
  732. static GOBUInt32 RawRead(GOBVoid* buffer, GOBUInt32 size, GOBUInt32 pos)
  733. {
  734. GOBUInt32 bytes;
  735. // Reads _must_ be aligned otherwise things get very slow
  736. if (pos % GOB_BLOCK_ALIGNMENT) {
  737. return 0;
  738. }
  739. if ((GOBUInt32)buffer % GOB_MEM_ALIGNMENT) {
  740. return 0;
  741. }
  742. // seek
  743. if (FSFuncs.seek(ArchiveHandle, pos, GOBSEEK_START)) return 0;
  744. if (CurrentArchivePos != pos) ++ReadStats.totalSeeks;
  745. if (pos > CurrentArchivePos + GOB_BLOCK_ALIGNMENT ||
  746. CurrentArchivePos > pos + GOB_BLOCK_ALIGNMENT)
  747. {
  748. ++ReadStats.farSeeks;
  749. }
  750. // read
  751. bytes = FSFuncs.read(ArchiveHandle, buffer, size);
  752. ReadStats.bytesRead += bytes;
  753. CurrentArchivePos = pos + bytes;
  754. return bytes;
  755. }
  756. static GOBUInt32 CacheRawRead(GOBVoid* buffer, GOBUInt32 size, GOBUInt32 pos)
  757. {
  758. GOBUInt32 bytes;
  759. // Reads _must_ be aligned otherwise things get very slow
  760. if (pos % GOB_BLOCK_ALIGNMENT) {
  761. return 0;
  762. }
  763. if ((GOBUInt32)buffer % GOB_MEM_ALIGNMENT) {
  764. return 0;
  765. }
  766. // seek
  767. if (CacheFileFuncs.seek(pos)) return 0;
  768. // read
  769. bytes = CacheFileFuncs.read(buffer, size);
  770. ReadStats.cacheBytesRead += bytes;
  771. return bytes;
  772. }
  773. static GOBUInt32 CacheRawWrite(GOBVoid* buffer, GOBUInt32 size, GOBUInt32 pos)
  774. {
  775. GOBUInt32 bytes;
  776. // Writes _must_ be aligned otherwise things get very slow
  777. if (pos % GOB_BLOCK_ALIGNMENT) {
  778. return 0;
  779. }
  780. if ((GOBUInt32)buffer % GOB_MEM_ALIGNMENT) {
  781. return 0;
  782. }
  783. // seek
  784. if (CacheFileFuncs.seek(pos)) return 0;
  785. // write
  786. bytes = CacheFileFuncs.write(buffer, size);
  787. ReadStats.cacheBytesWrite += bytes;
  788. return bytes;
  789. }
  790. static GOBInt32 BlockReadLow(GOBUInt32 block)
  791. {
  792. GOBUInt32 pos;
  793. GOBUInt32 bytes;
  794. GOBBool cache_read;
  795. GOBBool cache_write;
  796. GOBBool cache_fail;
  797. pos = 0;
  798. cache_read = GOB_FALSE;
  799. cache_write = GOB_FALSE;
  800. cache_fail = GOB_FALSE;
  801. for (;;) {
  802. // is the block in the read ahead buffer?
  803. if (ReadBuffer.pos <= BlockTable[block].offset + pos &&
  804. ReadBuffer.pos + ReadBuffer.size > BlockTable[block].offset + pos)
  805. {
  806. GOBUInt32 buffer_offset;
  807. GOBUInt32 buffer_size;
  808. // use data in the read buffer
  809. buffer_offset = BlockTable[block].offset + pos - ReadBuffer.pos;
  810. buffer_size = ReadBuffer.size - buffer_offset;
  811. // clamp size
  812. if (buffer_size > BlockTable[block].size - pos) {
  813. buffer_size = BlockTable[block].size - pos;
  814. }
  815. memcpy(&DecompBuffer[pos], &ReadBuffer.dataStart[buffer_offset], buffer_size);
  816. pos += buffer_size;
  817. }
  818. // got enough data
  819. if (pos == BlockTable[block].size) break;
  820. // refill read buffer
  821. ReadBuffer.pos = BlockTable[block].offset + pos;
  822. ReadBuffer.pos -= ReadBuffer.pos % GOB_BLOCK_ALIGNMENT;
  823. // check if block is in the external cache system
  824. if (CacheFileActive &&
  825. CacheFileTable[block / 32] & (1 << (block % 32)))
  826. {
  827. if (CacheRawRead(ReadBuffer.dataStart,
  828. ReadBuffer.size, ReadBuffer.pos))
  829. {
  830. cache_read = GOB_TRUE;
  831. continue;
  832. }
  833. }
  834. // read block from archive
  835. bytes = RawRead(ReadBuffer.dataStart, ReadBuffer.size, ReadBuffer.pos);
  836. if (bytes != ReadBuffer.size &&
  837. bytes != ArchiveSize - ReadBuffer.pos)
  838. {
  839. return -1; // Main read fail error code
  840. }
  841. // write block to cache file
  842. if (CacheFileActive)
  843. {
  844. if (CacheRawWrite(ReadBuffer.dataStart, bytes,
  845. ReadBuffer.pos) == bytes)
  846. {
  847. cache_write = GOB_TRUE;
  848. }
  849. else
  850. {
  851. cache_fail = GOB_TRUE;
  852. }
  853. }
  854. }
  855. if (cache_write) {
  856. if (!cache_fail) return 2;
  857. return 0;
  858. }
  859. if (cache_read) return 1;
  860. return 0;
  861. }
  862. static GOBBool BlockReadWithCache(GOBUInt32 block)
  863. {
  864. GOBInt32 i;
  865. for (i = 0; i < GOB_READ_RETRYS; ++i) {
  866. GOBInt32 result;
  867. // read the data
  868. result = BlockReadLow(block);
  869. if (result >= 0)
  870. {
  871. if (BlockCRC) {
  872. // crc check
  873. GOBUInt32 crc;
  874. crc = adler32(0L, Z_NULL, 0);
  875. crc = adler32(crc, (const unsigned char*)DecompBuffer,
  876. BlockTable[block].size);
  877. if (BlockCRC[block] != crc) {
  878. // crc mismatch, we must have got bad data --
  879. // try invalidating the cache and retrying...
  880. if (CacheFileActive) {
  881. CacheFileTable[block / 32] &= ~(1 << (block % 32));
  882. }
  883. ReadBuffer.pos = 0xFFFFFFFF;
  884. continue;
  885. }
  886. }
  887. // if cache write occurred -- mark block as cached
  888. if (result == 2) {
  889. CacheFileTable[block / 32] |= (1 << (block % 32));
  890. }
  891. // read success, crc success (or no check performed)
  892. return GOB_TRUE;
  893. }
  894. }
  895. // multiple read/crc failures
  896. return GOB_FALSE;
  897. }
  898. static GOBUInt32 BlockRead(GOBVoid* buffer, GOBUInt32 block)
  899. {
  900. GOBUInt32 size;
  901. GOBInt32 codec_index;
  902. GOBChar *compressed_data;
  903. // read block from cache or archive
  904. if (!BlockReadWithCache(block))
  905. {
  906. return GOB_INVALID_SIZE;
  907. }
  908. // decompress
  909. codec_index = 0;
  910. size = 0; // Initialize to satisfy compiler
  911. compressed_data = DecompBuffer + sizeof(GOBUInt32); // skip start-of-block marker
  912. while (codec_index < CodecFuncs.codecs) {
  913. // Check if codec matches
  914. if (*compressed_data == CodecFuncs.codec[codec_index].tag) {
  915. size = GOB_BLOCK_SIZE;
  916. if (CodecFuncs.codec[codec_index].decompress(compressed_data + 1,
  917. BlockTable[block].size - 1 - sizeof(GOBUInt32) * 2, buffer, &size)) {
  918. return GOB_INVALID_SIZE;
  919. }
  920. break;
  921. }
  922. codec_index++;
  923. }
  924. // If no suitable codecs were found, we're screwed
  925. if (codec_index == CodecFuncs.codecs) {
  926. return GOB_INVALID_SIZE;
  927. }
  928. if (ProfileReadCallback && ProfileEnabled) {
  929. // register current read command
  930. ProfileReadCallback(block);
  931. }
  932. return size;
  933. }
  934. static GOBVoid FillCacheBlock(GOBUInt32 block, GOBUInt32 index)
  935. {
  936. CacheBlocks[index].time = CacheBlockCounter++;
  937. CacheBlocks[index].block = block;
  938. CacheBlocks[index].size = BlockRead(CacheBlocks[index].data, block);
  939. }
  940. static GOBInt32 FindBestCacheBlock(GOBUInt32 block)
  941. {
  942. GOBInt32 i;
  943. GOBUInt32 oldest_time;
  944. GOBInt32 oldest_index;
  945. oldest_time = 0xFFFFFFFF;
  946. oldest_index = -1;
  947. for (i = 0; i < (signed)NumCacheBlocks; ++i) {
  948. if (CacheBlocks[i].block == block) {
  949. // if block is in this read buffer, use it
  950. return i;
  951. }
  952. // find the buffer that hasn't been accessed
  953. // for the longest time
  954. if (CacheBlocks[i].time < oldest_time) {
  955. oldest_time = CacheBlocks[i].time;
  956. oldest_index = i;
  957. }
  958. }
  959. // use the buffer that hasn't been accessed
  960. // in the longest time
  961. return oldest_index;
  962. }
  963. // GOBRead
  964. // Public function. Read from an open file using
  965. // a funky read-ahead buffer system.
  966. GOBUInt32 GOBRead(GOBVoid* buffer, GOBUInt32 size, GOBHandle handle)
  967. {
  968. GOBUInt32 pos;
  969. GOBInt32 cache_id;
  970. if (!LibraryInit) return 0;
  971. if (InvalidHandle(ArchiveHandle)) return 0;
  972. if (!OpenFiles[handle].valid) return 0;
  973. // make sure we're reading within the file
  974. if (OpenFiles[handle].pos + size > OpenFiles[handle].size) {
  975. size = OpenFiles[handle].size - OpenFiles[handle].pos;
  976. if (!size) return 0;
  977. }
  978. cache_id = FindBestCacheBlock(OpenFiles[handle].block);
  979. if (cache_id < 0) return GOB_INVALID_SIZE;
  980. pos = OpenFiles[handle].pos;
  981. for (;;) {
  982. // are looking for data inside the read buffer?
  983. if (CacheBlocks[cache_id].block == OpenFiles[handle].block) {
  984. // move any relevant data from the read buffer to the target buffer
  985. GOBUInt32 buffer_size;
  986. // calc size of data we want from current buffer
  987. buffer_size = CacheBlocks[cache_id].size - OpenFiles[handle].offset;
  988. if (buffer_size > size) buffer_size = size;
  989. // move from read buffer into output buffer
  990. memcpy(&((char*)buffer)[OpenFiles[handle].pos - pos],
  991. &CacheBlocks[cache_id].data[OpenFiles[handle].offset],
  992. buffer_size);
  993. // update file position
  994. OpenFiles[handle].pos += buffer_size;
  995. OpenFiles[handle].offset += buffer_size;
  996. // if we've completed this block -- move to next
  997. if (OpenFiles[handle].offset == CacheBlocks[cache_id].size) {
  998. OpenFiles[handle].block = BlockTable[OpenFiles[handle].block].next;
  999. OpenFiles[handle].offset = 0;
  1000. }
  1001. CacheBlocks[cache_id].time = CacheBlockCounter++;
  1002. ReadStats.bufferUsed += buffer_size;
  1003. size -= buffer_size;
  1004. if (size == 0) break;
  1005. }
  1006. // refill the buffer
  1007. FillCacheBlock(OpenFiles[handle].block, cache_id);
  1008. if (CacheBlocks[cache_id].size == GOB_INVALID_SIZE) {
  1009. CacheBlocks[cache_id].block = GOB_INVALID_BLOCK;
  1010. return GOB_INVALID_SIZE;
  1011. }
  1012. // reading off the end of the archive
  1013. if (CacheBlocks[cache_id].block != OpenFiles[handle].block) break;
  1014. }
  1015. return OpenFiles[handle].pos - pos;
  1016. }
  1017. // GOBSeek
  1018. // Public function. Seek to a position in an open file.
  1019. GOBError GOBSeek(GOBHandle handle, GOBUInt32 offset, GOBSeekType type, GOBUInt32* pos)
  1020. {
  1021. GOBUInt32 blocks;
  1022. if (!LibraryInit) return GOBERR_NOT_INIT;
  1023. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  1024. if (!OpenFiles[handle].valid) return GOBERR_NOT_OPEN;
  1025. // find a new position based on the seek type
  1026. switch (type) {
  1027. case GOBSEEK_START:
  1028. *pos = offset;
  1029. break;
  1030. case GOBSEEK_CURRENT:
  1031. *pos = OpenFiles[handle].pos + offset;
  1032. break;
  1033. case GOBSEEK_END:
  1034. *pos = OpenFiles[handle].size + offset;
  1035. break;
  1036. default:
  1037. return GOBERR_INVALID_SEEK;
  1038. }
  1039. // check to make sure we're still in the file
  1040. if (*pos > OpenFiles[handle].size) {
  1041. return GOBERR_INVALID_SEEK;
  1042. }
  1043. // update the file position
  1044. OpenFiles[handle].pos = *pos;
  1045. // update block
  1046. blocks = *pos / GOB_BLOCK_SIZE;
  1047. OpenFiles[handle].block = OpenFiles[handle].startBlock;
  1048. while (blocks--) {
  1049. OpenFiles[handle].block = BlockTable[OpenFiles[handle].block].next;
  1050. }
  1051. // update position inside block
  1052. OpenFiles[handle].offset = *pos % GOB_BLOCK_SIZE;
  1053. return GOBERR_OK;
  1054. }
  1055. static GOBUInt32 FindFreeBlock(GOBVoid)
  1056. {
  1057. GOBInt32 i;
  1058. for (i = 0; i < GOB_MAX_BLOCKS; ++i) {
  1059. if (BlockTable[i].next == GOB_INVALID_BLOCK) return i;
  1060. }
  1061. return GOB_MAX_BLOCKS;
  1062. }
  1063. // GOBWrite
  1064. // Public function. Write an entire file. The file should not be open!
  1065. GOBError GOBWrite(GOBVoid* buffer, GOBUInt32 size, GOBUInt32 mtime, const GOBChar* file, GOBUInt32 codec_mask)
  1066. {
  1067. GOBHandle handle;
  1068. GOBInt32 slack;
  1069. GOBChar* lfile;
  1070. GOBUInt32 hash;
  1071. GOBUInt32 crc;
  1072. GOBInt32 i;
  1073. GOBChar* out;
  1074. GOBUInt32 pos;
  1075. GOBUInt32 last_block;
  1076. GOBInt32 codec_index;
  1077. GOBInt32 compression_ratio;
  1078. GOBChar* out_data;
  1079. GOBUInt32 compressed_size;
  1080. if (!LibraryInit) return GOBERR_NOT_INIT;
  1081. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  1082. if (!FileTableExt) return GOBERR_NO_EXTENDED;
  1083. if (!BlockCRC) return GOBERR_NO_EXTENDED;
  1084. InvalidateCache();
  1085. // delete the file if it exists
  1086. GOBDelete(file);
  1087. // find a free entry in the file table
  1088. for (handle = 0; handle < GOB_MAX_FILES; ++handle) {
  1089. if (FileTableBasic[handle].block == GOB_INVALID_BLOCK) break;
  1090. }
  1091. if (handle >= GOB_MAX_FILES) return GOBERR_TOO_MANY_FILES;
  1092. if (handle >= (GOBInt32)ArchiveNumFiles) ArchiveNumFiles = handle + 1;
  1093. // move to the end of the GOB
  1094. if (FSFuncs.seek(ArchiveHandle, 0, GOBSEEK_END)) {
  1095. return GOBERR_FILE_WRITE;
  1096. }
  1097. // alloc compression buffer
  1098. out = (GOBChar*)MemFuncs.alloc(GOB_BLOCK_SIZE + GOB_COMPRESS_OVERHEAD);
  1099. last_block = GOB_MAX_BLOCKS - 1;
  1100. for (pos = 0; pos < size; pos += GOB_BLOCK_SIZE) {
  1101. GOBUInt32 block;
  1102. GOBUInt32 in_size;
  1103. // get a free block
  1104. block = FindFreeBlock();
  1105. if (block >= GOB_MAX_BLOCKS) return GOBERR_TOO_MANY_BLOCKS;
  1106. if (block >= ArchiveNumBlocks) ArchiveNumBlocks = block + 1;
  1107. // if this is not the first block, mark next block for the last block
  1108. // else assign the first block in file table
  1109. if (pos != 0) BlockTable[last_block].next = block;
  1110. else FileTableBasic[handle].block = block;
  1111. // invalidate the next block
  1112. BlockTable[block].next = GOB_MAX_BLOCKS;
  1113. // compute the decompressed block size
  1114. in_size = size - pos;
  1115. if (in_size > GOB_BLOCK_SIZE) in_size = GOB_BLOCK_SIZE;
  1116. // compress block
  1117. for (
  1118. codec_index = 0;
  1119. codec_index < CodecFuncs.codecs;
  1120. codec_index++)
  1121. {
  1122. if ( ! (GOB_CODEC_MASK(codec_index) & codec_mask) )
  1123. {
  1124. // skip if this codec is not listed as one of the allowed ones
  1125. continue;
  1126. }
  1127. BlockTable[block].size = GOB_BLOCK_SIZE + GOB_COMPRESS_OVERHEAD;
  1128. out_data = out;
  1129. *(GOBUInt32*)out_data = SwapBytes(GOBMARKER_STARTBLOCK);
  1130. out_data += sizeof(GOBUInt32);
  1131. *out_data = CodecFuncs.codec[codec_index].tag;
  1132. out_data++;
  1133. if (CodecFuncs.codec[codec_index].compress(&((GOBChar*)buffer)[pos],
  1134. in_size, out_data, &BlockTable[block].size))
  1135. {
  1136. return GOBERR_COMPRESS_FAIL;
  1137. }
  1138. out_data += BlockTable[block].size;
  1139. *(GOBUInt32*)out_data = SwapBytes(GOBMARKER_ENDBLOCK);
  1140. out_data += sizeof(GOBUInt32);
  1141. // Adjust for the prefixed start-of-block marker and codec tag and trailing end-of-block marker
  1142. compressed_size = BlockTable[block].size;
  1143. BlockTable[block].size += 1 + sizeof(GOBUInt32) * 2;
  1144. // Check compression result
  1145. compression_ratio = compressed_size * 100 / in_size;
  1146. if (compression_ratio <= CodecFuncs.codec[codec_index].max_ratio)
  1147. {
  1148. // Compressed result is under par. Let's go with it
  1149. break;
  1150. }
  1151. // Otherwise, try the next compressor
  1152. }
  1153. // If no suitable codecs were found, take our ball and go home
  1154. if (codec_index == CodecFuncs.codecs) return GOBERR_NO_SUITABLE_CODEC;
  1155. // compute and store the CRC
  1156. BlockCRC[block] = adler32(0L, Z_NULL, 0);
  1157. BlockCRC[block] = adler32(BlockCRC[block], (const unsigned char*)out,
  1158. BlockTable[block].size);
  1159. // write block
  1160. if (FSFuncs.write(ArchiveHandle, out, BlockTable[block].size) !=
  1161. (signed)BlockTable[block].size)
  1162. {
  1163. return GOBERR_FILE_WRITE;
  1164. }
  1165. // compute the slack (to keep alignment)
  1166. slack = GOBGetSlack(BlockTable[block].size);
  1167. // write the slack space
  1168. memset(out, 0, slack);
  1169. if (FSFuncs.write(ArchiveHandle, out, slack) != slack) {
  1170. return GOBERR_FILE_WRITE;
  1171. }
  1172. BlockTable[block].offset = ArchiveSize;
  1173. ArchiveSize += BlockTable[block].size + slack;
  1174. last_block = block;
  1175. }
  1176. MemFuncs.free(out);
  1177. lfile = LowerCase(file);
  1178. // calculate file name hash
  1179. hash = crc32(0L, Z_NULL, 0);
  1180. hash = crc32(hash, (const unsigned char*)lfile, strlen(lfile));
  1181. // make sure hash is unique
  1182. for (i = 0; i < GOB_MAX_FILES; ++i) {
  1183. if (i != handle &&
  1184. FileTableBasic[i].block != GOB_INVALID_BLOCK &&
  1185. FileTableBasic[i].hash == hash)
  1186. {
  1187. return GOBERR_DUP_HASH;
  1188. }
  1189. }
  1190. // update the file tables
  1191. FileTableBasic[handle].hash = hash;
  1192. FileTableBasic[handle].size = size;
  1193. strcpy(FileTableExt[handle].name, lfile);
  1194. crc = crc32(0L, Z_NULL, 0);
  1195. crc = crc32(crc, (const unsigned char*)buffer, size);
  1196. FileTableExt[handle].crc = crc;
  1197. FileTableExt[handle].time = mtime;
  1198. FileTableDirty = GOB_TRUE;
  1199. return GOBERR_OK;
  1200. }
  1201. // GOBDelete
  1202. // Public function. Delete a file from a GOB. The file should not be open!
  1203. GOBError GOBDelete(const GOBChar* file)
  1204. {
  1205. GOBInt32 entry;
  1206. GOBChar* lfile;
  1207. GOBUInt32 block;
  1208. if (!LibraryInit) return GOBERR_NOT_INIT;
  1209. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  1210. if (!FileTableExt) return GOBERR_NO_EXTENDED;
  1211. if (!BlockCRC) return GOBERR_NO_EXTENDED;
  1212. // find the file in the table
  1213. lfile = LowerCase(file);
  1214. entry = GetFileTableEntry(lfile);
  1215. if (entry == -1) return GOBERR_FILE_NOT_FOUND;
  1216. // invalidate blocks
  1217. block = FileTableBasic[entry].block;
  1218. do {
  1219. GOBUInt32 next;
  1220. next = BlockTable[block].next;
  1221. BlockTable[block].next = GOB_INVALID_BLOCK;
  1222. block = next;
  1223. } while(block != GOB_MAX_BLOCKS);
  1224. // invalidate the file
  1225. FileTableBasic[entry].block = GOB_INVALID_BLOCK;
  1226. FileTableDirty = GOB_TRUE;
  1227. return GOBERR_OK;
  1228. }
  1229. // GOBRearrange
  1230. // Public function. Sorts the blocks in an archive.
  1231. GOBError GOBRearrange(const GOBChar* file, const GOBUInt32* xlat, GOBFileSysRenameFunc _rename)
  1232. {
  1233. GOBError err;
  1234. GOBVoid* buffer;
  1235. GOBInt32 slack;
  1236. GOBVoid* slack_buf;
  1237. GOBUInt32 i;
  1238. GOBUInt32 size;
  1239. GOBFSHandle temp_handle;
  1240. GOBChar full_name[GOB_MAX_FILE_NAME_LEN];
  1241. if (!LibraryInit) return GOBERR_NOT_INIT;
  1242. if (!InvalidHandle(ArchiveHandle)) return GOBERR_ALREADY_OPEN;
  1243. if (!FileTableExt) return GOBERR_NO_EXTENDED;
  1244. if (!BlockCRC) return GOBERR_NO_EXTENDED;
  1245. // start things up
  1246. err = GOBArchiveOpen(file, GOBACCESS_READ, GOB_TRUE, GOB_TRUE);
  1247. if (err != GOBERR_OK) return err;
  1248. // create temporary file
  1249. temp_handle = FSFuncs.open("~temp.tmp", GOBACCESS_WRITE);
  1250. if (InvalidHandle(temp_handle)) return GOBERR_FILE_WRITE;
  1251. size = 0;
  1252. // create an empty buffer for slack
  1253. slack_buf = MemFuncs.alloc(GOB_BLOCK_ALIGNMENT);
  1254. if (!slack_buf) return GOBERR_NO_MEMORY;
  1255. memset(slack_buf, 0, GOB_BLOCK_ALIGNMENT);
  1256. // get memory for block
  1257. buffer = MemFuncs.alloc(GOB_BLOCK_SIZE + GOB_COMPRESS_OVERHEAD);
  1258. if (!buffer) return GOBERR_NO_MEMORY;
  1259. // copy files in new order to end of archive
  1260. for (i = 0; i < ArchiveNumBlocks; ++i) {
  1261. if (BlockTable[xlat[i]].next != GOB_INVALID_BLOCK) {
  1262. // seek to the block
  1263. if (FSFuncs.seek(ArchiveHandle,
  1264. BlockTable[xlat[i]].offset, GOBSEEK_START))
  1265. {
  1266. return GOBERR_FILE_READ;
  1267. }
  1268. // read the block
  1269. if (FSFuncs.read(ArchiveHandle, buffer, BlockTable[xlat[i]].size) !=
  1270. (signed)BlockTable[xlat[i]].size)
  1271. {
  1272. return GOBERR_FILE_READ;
  1273. }
  1274. // write block
  1275. if (FSFuncs.write(temp_handle, buffer, BlockTable[xlat[i]].size) !=
  1276. (signed)BlockTable[xlat[i]].size)
  1277. {
  1278. return GOBERR_FILE_WRITE;
  1279. }
  1280. // write the slack
  1281. slack = GOBGetSlack(BlockTable[xlat[i]].size);
  1282. if (FSFuncs.write(temp_handle, slack_buf, slack) != slack) {
  1283. return GOBERR_FILE_WRITE;
  1284. }
  1285. // update block pos
  1286. BlockTable[xlat[i]].offset = size;
  1287. size += BlockTable[xlat[i]].size + slack;
  1288. }
  1289. }
  1290. MemFuncs.free(buffer);
  1291. MemFuncs.free(slack_buf);
  1292. // close the archive
  1293. err = GOBArchiveClose();
  1294. if (err != GOBERR_OK) return err;
  1295. // close temp file
  1296. FSFuncs.close(&temp_handle);
  1297. // overrwrite archive with temp file
  1298. _snprintf(full_name, GOB_MAX_FILE_NAME_LEN, "%s.gob", file);
  1299. if (_rename("~temp.tmp", full_name)) return GOBERR_FILE_RENAME;
  1300. ArchiveSize = size;
  1301. CommitFileTable();
  1302. return GOBERR_OK;
  1303. }
  1304. // GOBVerify
  1305. // Public function. Verifies the integrity of a file.
  1306. GOBError GOBVerify(const GOBChar* file, GOBBool* status)
  1307. {
  1308. GOBHandle handle;
  1309. GOBError err;
  1310. GOBVoid* buffer;
  1311. GOBUInt32 size, junk;
  1312. GOBUInt32 crc;
  1313. GOBInt32 entry;
  1314. GOBChar* lfile;
  1315. if (!LibraryInit) return GOBERR_NOT_INIT;
  1316. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  1317. if (!FileTableExt) return GOBERR_NO_EXTENDED;
  1318. if (!BlockCRC) return GOBERR_NO_EXTENDED;
  1319. // get the file size
  1320. size = 0; // assign to avoid compiler warning
  1321. err = GOBGetSize(file, &size, &junk, &junk);
  1322. if (err != GOBERR_OK) return err;
  1323. // open the file
  1324. err = GOBOpen((GOBChar*)file, &handle);
  1325. if (err != GOBERR_OK) return err;
  1326. lfile = LowerCase(file);
  1327. entry = GetFileTableEntry(lfile);
  1328. // alloc space for the file
  1329. buffer = MemFuncs.alloc(size);
  1330. if (!buffer) return GOBERR_NO_MEMORY;
  1331. // read it into the buffer
  1332. crc = GOBRead(buffer, size, handle);
  1333. if (crc != size) return GOBERR_FILE_READ;
  1334. // calc the crc
  1335. crc = crc32(0L, Z_NULL, 0);
  1336. crc = crc32(crc, (const unsigned char*)buffer, size);
  1337. MemFuncs.free(buffer);
  1338. // verify the crc matches
  1339. if (crc != FileTableExt[entry].crc) *status = GOB_FALSE;
  1340. else *status = GOB_TRUE;
  1341. err = GOBClose(handle);
  1342. if (err != GOBERR_OK) return err;
  1343. return GOBERR_OK;
  1344. }
  1345. // GOBGetSize
  1346. // Public function. Get a file compressed, decompressed, slack sizes.
  1347. GOBError GOBGetSize(const GOBChar* file,
  1348. GOBUInt32* decomp, GOBUInt32* comp, GOBUInt32* slack)
  1349. {
  1350. GOBInt32 entry;
  1351. GOBChar* lfile;
  1352. GOBUInt32 block;
  1353. if (!LibraryInit) return GOBERR_NOT_INIT;
  1354. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  1355. // get file table entry
  1356. lfile = LowerCase(file);
  1357. entry = GetFileTableEntry(lfile);
  1358. if (entry == -1) return GOBERR_FILE_NOT_FOUND;
  1359. // decompressed size from file table
  1360. *decomp = FileTableBasic[entry].size;
  1361. // compressed size is sum of block sizes
  1362. *comp = 0;
  1363. *slack = 0;
  1364. block = FileTableBasic[entry].block;
  1365. while (block != GOB_MAX_BLOCKS) {
  1366. *comp += BlockTable[block].size;
  1367. *slack += GOBGetSlack(BlockTable[block].size);
  1368. block = BlockTable[block].next;
  1369. }
  1370. return GOBERR_OK;
  1371. }
  1372. // GOBGetTime
  1373. // Public function. Get a file modification time.
  1374. GOBError GOBGetTime(const GOBChar* file, GOBUInt32* time)
  1375. {
  1376. GOBInt32 entry;
  1377. GOBChar* lfile;
  1378. if (!LibraryInit) return GOBERR_NOT_INIT;
  1379. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  1380. if (!FileTableExt) return GOBERR_NO_EXTENDED;
  1381. if (!BlockCRC) return GOBERR_NO_EXTENDED;
  1382. lfile = LowerCase(file);
  1383. entry = GetFileTableEntry(lfile);
  1384. if (entry == -1) return GOBERR_FILE_NOT_FOUND;
  1385. *time = FileTableExt[entry].time;
  1386. return GOBERR_OK;
  1387. }
  1388. // GOBGetCRC
  1389. // Public function. Get a file CRC.
  1390. GOBError GOBGetCRC(const GOBChar* file, GOBUInt32* crc)
  1391. {
  1392. GOBInt32 entry;
  1393. GOBChar* lfile;
  1394. if (!LibraryInit) return GOBERR_NOT_INIT;
  1395. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  1396. if (!FileTableExt) return GOBERR_NO_EXTENDED;
  1397. if (!BlockCRC) return GOBERR_NO_EXTENDED;
  1398. lfile = LowerCase(file);
  1399. entry = GetFileTableEntry(lfile);
  1400. if (entry == -1) return GOBERR_FILE_NOT_FOUND;
  1401. *crc = FileTableExt[entry].crc;
  1402. return GOBERR_OK;
  1403. }
  1404. // GOBAccess
  1405. // Public function. Determine if a file exists in the archive.
  1406. GOBError GOBAccess(const GOBChar* file, GOBBool* status)
  1407. {
  1408. GOBInt32 entry;
  1409. GOBChar* lfile;
  1410. if (!LibraryInit) return GOBERR_NOT_INIT;
  1411. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  1412. lfile = LowerCase(file);
  1413. entry = GetFileTableEntry(lfile);
  1414. if (entry == -1) *status = GOB_FALSE;
  1415. else *status = GOB_TRUE;
  1416. return GOBERR_OK;
  1417. }
  1418. // GOBGetFileCode
  1419. // Public function. Find the index into the file table of a file.
  1420. GOBInt32 GOBGetFileCode(const GOBChar* file)
  1421. {
  1422. GOBInt32 entry;
  1423. GOBChar* lfile;
  1424. if (!LibraryInit) return -1;
  1425. if (InvalidHandle(ArchiveHandle)) return -1;
  1426. lfile = LowerCase(file);
  1427. entry = GetFileTableEntry(lfile);
  1428. return entry;
  1429. }
  1430. // GOBGetFileTables
  1431. // Public function. Return the active file tables.
  1432. GOBError GOBGetFileTables(struct GOBFileTableBasicEntry** basic,
  1433. struct GOBFileTableExtEntry** ext)
  1434. {
  1435. if (!LibraryInit) return GOBERR_NOT_INIT;
  1436. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  1437. *basic = FileTableBasic;
  1438. *ext = FileTableExt;
  1439. return GOBERR_OK;
  1440. }
  1441. // GOBGetBlockTable
  1442. // Public function. Return the active block table.
  1443. GOBError GOBGetBlockTable(struct GOBBlockTableEntry** table, GOBUInt32* num)
  1444. {
  1445. if (!LibraryInit) return GOBERR_NOT_INIT;
  1446. if (InvalidHandle(ArchiveHandle)) return GOBERR_NOT_OPEN;
  1447. *table = BlockTable;
  1448. *num = ArchiveNumBlocks;
  1449. return GOBERR_OK;
  1450. }
  1451. // GOBSetCacheSize
  1452. // Public function. Allocates buffers to cache blocks.
  1453. GOBError GOBSetCacheSize(GOBUInt32 num)
  1454. {
  1455. GOBUInt32 i;
  1456. if (!LibraryInit) return GOBERR_NOT_INIT;
  1457. // only continue if we actually need to resize
  1458. if (num == NumCacheBlocks) return GOBERR_OK;
  1459. // free old cache buffers
  1460. FreeCache();
  1461. NumCacheBlocks = 0;
  1462. CacheBlocks = (struct GOBBlockCache*)MemFuncs.alloc(
  1463. sizeof(struct GOBBlockCache) * num);
  1464. if (!CacheBlocks) return GOBERR_NO_MEMORY;
  1465. // allocate cache blocks and initialize
  1466. for (i = 0; i < num; ++i) {
  1467. CacheBlocks[i].data = (GOBChar*)MemFuncs.alloc(GOB_BLOCK_SIZE);
  1468. if (!CacheBlocks[i].data) return GOBERR_NO_MEMORY;
  1469. CacheBlocks[i].size = 0;
  1470. CacheBlocks[i].time = 0;
  1471. CacheBlocks[i].block = 0xFFFFFFFF;
  1472. ++NumCacheBlocks;
  1473. }
  1474. return GOBERR_OK;
  1475. }
  1476. // GOBSetReadBufferSize
  1477. // Public function. Allocate a read ahead buffer.
  1478. GOBError GOBSetReadBufferSize(GOBUInt32 size)
  1479. {
  1480. if (!LibraryInit) return GOBERR_NOT_INIT;
  1481. // only continue if we actually need to resize
  1482. if (size == ReadBuffer.size) return GOBERR_OK;
  1483. // remove old buffer
  1484. if (ReadBuffer.data) MemFuncs.free(ReadBuffer.data);
  1485. // allocate new buffer
  1486. ReadBuffer.data = (GOBChar*)MemFuncs.alloc(size + GOB_MEM_ALIGNMENT);
  1487. if (!ReadBuffer.data) return GOB_INVALID_SIZE;
  1488. // set aligned pointer
  1489. ReadBuffer.dataStart =
  1490. &ReadBuffer.data[GOB_MEM_ALIGNMENT -
  1491. ((GOBUInt32)(ReadBuffer.data) % GOB_MEM_ALIGNMENT)];
  1492. ReadBuffer.pos = 0xFFFFFFFF;
  1493. ReadBuffer.size = size;
  1494. return GOBERR_OK;
  1495. }
  1496. // GOBGetReadStats
  1497. // Public function. Get file read statistics (seeks, sizes).
  1498. struct GOBReadStats GOBGetReadStats(GOBVoid)
  1499. {
  1500. return ReadStats;
  1501. }
  1502. GOBVoid GOBSetProfileFuncs(struct GOBProfileFuncSet* fset)
  1503. {
  1504. ProfileReadCallback = fset->read;
  1505. }
  1506. GOBError GOBStartProfile(GOBVoid)
  1507. {
  1508. if (ProfileEnabled) return GOBERR_PROFILE_ON;
  1509. ProfileEnabled = GOB_TRUE;
  1510. return GOBERR_OK;
  1511. }
  1512. GOBError GOBStopProfile(GOBVoid)
  1513. {
  1514. if (!ProfileEnabled) return GOBERR_PROFILE_OFF;
  1515. ProfileEnabled = GOB_FALSE;
  1516. return GOBERR_OK;
  1517. }