archiver_lzma.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. /*
  2. * LZMA support routines for PhysicsFS.
  3. *
  4. * Please see the file lzma.txt in the lzma/ directory.
  5. *
  6. * This file was written by Dennis Schridde, with some peeking at "7zMain.c"
  7. * by Igor Pavlov.
  8. */
  9. #if (defined PHYSFS_SUPPORTS_7Z)
  10. #define __PHYSICSFS_INTERNAL__
  11. #include "physfs_internal.h"
  12. #include "lzma/C/7zCrc.h"
  13. #include "lzma/C/Archive/7z/7zIn.h"
  14. #include "lzma/C/Archive/7z/7zExtract.h"
  15. /* 7z internal from 7zIn.c */
  16. extern int TestSignatureCandidate(Byte *testBytes);
  17. #ifdef _LZMA_IN_CB
  18. # define BUFFER_SIZE (1 << 12)
  19. #endif /* _LZMA_IN_CB */
  20. /*
  21. * Carries filestream metadata through 7z
  22. */
  23. typedef struct _FileInputStream
  24. {
  25. ISzAlloc allocImp; /* Allocation implementation, used by 7z */
  26. ISzAlloc allocTempImp; /* Temporary allocation implementation, used by 7z */
  27. ISzInStream inStream; /* Input stream with read callbacks, used by 7z */
  28. PHYSFS_Io *io; /* Filehandle, used by read implementation */
  29. #ifdef _LZMA_IN_CB
  30. Byte buffer[BUFFER_SIZE]; /* Buffer, used by read implementation */
  31. #endif /* _LZMA_IN_CB */
  32. } FileInputStream;
  33. /*
  34. * In the 7z format archives are splited into blocks, those are called folders
  35. * Set by LZMA_read()
  36. */
  37. typedef struct _LZMAfolder
  38. {
  39. PHYSFS_uint32 index; /* Index of folder in archive */
  40. PHYSFS_uint32 references; /* Number of files using this block */
  41. PHYSFS_uint8 *cache; /* Cached folder */
  42. size_t size; /* Size of folder */
  43. } LZMAfolder;
  44. /*
  45. * Set by LZMA_openArchive(), except folder which gets it's values
  46. * in LZMA_read()
  47. */
  48. typedef struct _LZMAarchive
  49. {
  50. struct _LZMAfile *files; /* Array of files, size == archive->db.Database.NumFiles */
  51. LZMAfolder *folders; /* Array of folders, size == archive->db.Database.NumFolders */
  52. CArchiveDatabaseEx db; /* For 7z: Database */
  53. FileInputStream stream; /* For 7z: Input file incl. read and seek callbacks */
  54. } LZMAarchive;
  55. /* Set by LZMA_openArchive(), except offset which is set by LZMA_read() */
  56. typedef struct _LZMAfile
  57. {
  58. PHYSFS_uint32 index; /* Index of file in archive */
  59. LZMAarchive *archive; /* Link to corresponding archive */
  60. LZMAfolder *folder; /* Link to corresponding folder */
  61. CFileItem *item; /* For 7z: File info, eg. name, size */
  62. size_t offset; /* Offset in folder */
  63. size_t position; /* Current "virtual" position in file */
  64. } LZMAfile;
  65. /* Memory management implementations to be passed to 7z */
  66. static void *SzAllocPhysicsFS(size_t size)
  67. {
  68. return ((size == 0) ? NULL : allocator.Malloc(size));
  69. } /* SzAllocPhysicsFS */
  70. static void SzFreePhysicsFS(void *address)
  71. {
  72. if (address != NULL)
  73. allocator.Free(address);
  74. } /* SzFreePhysicsFS */
  75. /* Filesystem implementations to be passed to 7z */
  76. #ifdef _LZMA_IN_CB
  77. /*
  78. * Read implementation, to be passed to 7z
  79. * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
  80. */
  81. SZ_RESULT SzFileReadImp(void *object, void **buffer, size_t maxReqSize,
  82. size_t *processedSize)
  83. {
  84. FileInputStream *s = (FileInputStream *)(object - offsetof(FileInputStream, inStream)); /* HACK! */
  85. PHYSFS_sint64 processedSizeLoc = 0;
  86. if (maxReqSize > BUFFER_SIZE)
  87. maxReqSize = BUFFER_SIZE;
  88. processedSizeLoc = s->io->read(s->io, s->buffer, maxReqSize);
  89. *buffer = s->buffer;
  90. if (processedSize != NULL)
  91. *processedSize = (size_t) processedSizeLoc;
  92. return SZ_OK;
  93. } /* SzFileReadImp */
  94. #else
  95. /*
  96. * Read implementation, to be passed to 7z
  97. * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
  98. */
  99. SZ_RESULT SzFileReadImp(void *object, void *buffer, size_t size,
  100. size_t *processedSize)
  101. {
  102. FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
  103. const size_t processedSizeLoc = s->io->read(s->io, buffer, size);
  104. if (processedSize != NULL)
  105. *processedSize = processedSizeLoc;
  106. return SZ_OK;
  107. } /* SzFileReadImp */
  108. #endif
  109. /*
  110. * Seek implementation, to be passed to 7z
  111. * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
  112. */
  113. SZ_RESULT SzFileSeekImp(void *object, CFileSize pos)
  114. {
  115. FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
  116. if (s->io->seek(s->io, (PHYSFS_uint64) pos))
  117. return SZ_OK;
  118. return SZE_FAIL;
  119. } /* SzFileSeekImp */
  120. /*
  121. * Translate Microsoft FILETIME (used by 7zip) into UNIX timestamp
  122. */
  123. static PHYSFS_sint64 lzma_filetime_to_unix_timestamp(CArchiveFileTime *ft)
  124. {
  125. /* MS counts in nanoseconds ... */
  126. const PHYSFS_uint64 FILETIME_NANOTICKS_PER_SECOND = __PHYSFS_UI64(10000000);
  127. /* MS likes to count seconds since 01.01.1601 ... */
  128. const PHYSFS_uint64 FILETIME_UNIX_DIFF = __PHYSFS_UI64(11644473600);
  129. PHYSFS_uint64 filetime = ft->Low | ((PHYSFS_uint64)ft->High << 32);
  130. return filetime/FILETIME_NANOTICKS_PER_SECOND - FILETIME_UNIX_DIFF;
  131. } /* lzma_filetime_to_unix_timestamp */
  132. /*
  133. * Compare a file with a given name, C89 stdlib variant
  134. * Used for sorting
  135. */
  136. static int lzma_file_cmp_stdlib(const void *key, const void *object)
  137. {
  138. const char *name = (const char *) key;
  139. LZMAfile *file = (LZMAfile *) object;
  140. return strcmp(name, file->item->Name);
  141. } /* lzma_file_cmp_posix */
  142. /*
  143. * Compare two files with each other based on the name
  144. * Used for sorting
  145. */
  146. static int lzma_file_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
  147. {
  148. LZMAfile *files = (LZMAfile *) _a;
  149. return strcmp(files[one].item->Name, files[two].item->Name);
  150. } /* lzma_file_cmp */
  151. /*
  152. * Swap two entries in the file array
  153. */
  154. static void lzma_file_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
  155. {
  156. LZMAfile tmp;
  157. LZMAfile *first = &(((LZMAfile *) _a)[one]);
  158. LZMAfile *second = &(((LZMAfile *) _a)[two]);
  159. memcpy(&tmp, first, sizeof (LZMAfile));
  160. memcpy(first, second, sizeof (LZMAfile));
  161. memcpy(second, &tmp, sizeof (LZMAfile));
  162. } /* lzma_file_swap */
  163. /*
  164. * Find entry 'name' in 'archive'
  165. */
  166. static LZMAfile * lzma_find_file(const LZMAarchive *archive, const char *name)
  167. {
  168. LZMAfile *file = bsearch(name, archive->files, archive->db.Database.NumFiles, sizeof(*archive->files), lzma_file_cmp_stdlib); /* FIXME: Should become __PHYSFS_search!!! */
  169. BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, NULL);
  170. return file;
  171. } /* lzma_find_file */
  172. /*
  173. * Load metadata for the file at given index
  174. */
  175. static int lzma_file_init(LZMAarchive *archive, PHYSFS_uint32 fileIndex)
  176. {
  177. LZMAfile *file = &archive->files[fileIndex];
  178. PHYSFS_uint32 folderIndex = archive->db.FileIndexToFolderIndexMap[fileIndex];
  179. file->index = fileIndex; /* Store index into 7z array, since we sort our own. */
  180. file->archive = archive;
  181. file->folder = (folderIndex != (PHYSFS_uint32)-1 ? &archive->folders[folderIndex] : NULL); /* Directories don't have a folder (they contain no own data...) */
  182. file->item = &archive->db.Database.Files[fileIndex]; /* Holds crucial data and is often referenced -> Store link */
  183. file->position = 0;
  184. file->offset = 0; /* Offset will be set by LZMA_read() */
  185. return 1;
  186. } /* lzma_load_file */
  187. /*
  188. * Load metadata for all files
  189. */
  190. static int lzma_files_init(LZMAarchive *archive)
  191. {
  192. PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles;
  193. for (fileIndex = 0; fileIndex < numFiles; fileIndex++ )
  194. {
  195. if (!lzma_file_init(archive, fileIndex))
  196. {
  197. return 0; /* FALSE on failure */
  198. }
  199. } /* for */
  200. __PHYSFS_sort(archive->files, numFiles, lzma_file_cmp, lzma_file_swap);
  201. return 1;
  202. } /* lzma_load_files */
  203. /*
  204. * Initialise specified archive
  205. */
  206. static void lzma_archive_init(LZMAarchive *archive)
  207. {
  208. memset(archive, 0, sizeof(*archive));
  209. /* Prepare callbacks for 7z */
  210. archive->stream.inStream.Read = SzFileReadImp;
  211. archive->stream.inStream.Seek = SzFileSeekImp;
  212. archive->stream.allocImp.Alloc = SzAllocPhysicsFS;
  213. archive->stream.allocImp.Free = SzFreePhysicsFS;
  214. archive->stream.allocTempImp.Alloc = SzAllocPhysicsFS;
  215. archive->stream.allocTempImp.Free = SzFreePhysicsFS;
  216. }
  217. /*
  218. * Deinitialise archive
  219. */
  220. static void lzma_archive_exit(LZMAarchive *archive)
  221. {
  222. /* Free arrays */
  223. allocator.Free(archive->folders);
  224. allocator.Free(archive->files);
  225. allocator.Free(archive);
  226. }
  227. /*
  228. * Wrap all 7z calls in this, so the physfs error state is set appropriately.
  229. */
  230. static int lzma_err(SZ_RESULT rc)
  231. {
  232. switch (rc)
  233. {
  234. case SZ_OK: /* Same as LZMA_RESULT_OK */
  235. break;
  236. case SZE_DATA_ERROR: /* Same as LZMA_RESULT_DATA_ERROR */
  237. __PHYSFS_setError(ERR_DATA_ERROR);
  238. break;
  239. case SZE_OUTOFMEMORY:
  240. __PHYSFS_setError(ERR_OUT_OF_MEMORY);
  241. break;
  242. case SZE_CRC_ERROR:
  243. __PHYSFS_setError(ERR_CORRUPTED);
  244. break;
  245. case SZE_NOTIMPL:
  246. __PHYSFS_setError(ERR_NOT_IMPLEMENTED);
  247. break;
  248. case SZE_FAIL:
  249. __PHYSFS_setError(ERR_UNKNOWN_ERROR); /* !!! FIXME: right? */
  250. break;
  251. case SZE_ARCHIVE_ERROR:
  252. __PHYSFS_setError(ERR_CORRUPTED); /* !!! FIXME: right? */
  253. break;
  254. default:
  255. __PHYSFS_setError(ERR_UNKNOWN_ERROR);
  256. } /* switch */
  257. return rc;
  258. } /* lzma_err */
  259. static PHYSFS_sint64 LZMA_read(PHYSFS_Io *io, void *outBuf, PHYSFS_uint64 len)
  260. {
  261. LZMAfile *file = (LZMAfile *) io->opaque;
  262. size_t wantedSize = (size_t) len;
  263. const size_t remainingSize = file->item->Size - file->position;
  264. size_t fileSize = 0;
  265. BAIL_IF_MACRO(wantedSize == 0, NULL, 0); /* quick rejection. */
  266. BAIL_IF_MACRO(remainingSize == 0, ERR_PAST_EOF, 0);
  267. if (wantedSize > remainingSize)
  268. wantedSize = remainingSize;
  269. /* Only decompress the folder if it is not already cached */
  270. if (file->folder->cache == NULL)
  271. {
  272. const int rc = lzma_err(SzExtract(
  273. &file->archive->stream.inStream, /* compressed data */
  274. &file->archive->db, /* 7z's database, containing everything */
  275. file->index, /* Index into database arrays */
  276. /* Index of cached folder, will be changed by SzExtract */
  277. &file->folder->index,
  278. /* Cache for decompressed folder, allocated/freed by SzExtract */
  279. &file->folder->cache,
  280. /* Size of cache, will be changed by SzExtract */
  281. &file->folder->size,
  282. /* Offset of this file inside the cache, set by SzExtract */
  283. &file->offset,
  284. &fileSize, /* Size of this file */
  285. &file->archive->stream.allocImp,
  286. &file->archive->stream.allocTempImp));
  287. if (rc != SZ_OK)
  288. return -1;
  289. } /* if */
  290. /* Copy wanted bytes over from cache to outBuf */
  291. memcpy(outBuf, (file->folder->cache + file->offset + file->position),
  292. wantedSize);
  293. file->position += wantedSize; /* Increase virtual position */
  294. return wantedSize;
  295. } /* LZMA_read */
  296. static PHYSFS_sint64 LZMA_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
  297. {
  298. BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
  299. } /* LZMA_write */
  300. static PHYSFS_sint64 LZMA_tell(PHYSFS_Io *io)
  301. {
  302. LZMAfile *file = (LZMAfile *) io->opaque;
  303. return file->position;
  304. } /* LZMA_tell */
  305. static int LZMA_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
  306. {
  307. LZMAfile *file = (LZMAfile *) io->opaque;
  308. BAIL_IF_MACRO(offset > file->item->Size, ERR_PAST_EOF, 0);
  309. file->position = offset; /* We only use a virtual position... */
  310. return 1;
  311. } /* LZMA_seek */
  312. static PHYSFS_sint64 LZMA_length(PHYSFS_Io *io)
  313. {
  314. const LZMAfile *file = (LZMAfile *) io->opaque;
  315. return (file->item->Size);
  316. } /* LZMA_length */
  317. static PHYSFS_Io *LZMA_duplicate(PHYSFS_Io *_io)
  318. {
  319. /* !!! FIXME: this archiver needs to be reworked to allow multiple
  320. * !!! FIXME: opens before we worry about duplication. */
  321. BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
  322. } /* LZMA_duplicate */
  323. static int LZMA_flush(PHYSFS_Io *io) { return 1; /* no write support. */ }
  324. static void LZMA_destroy(PHYSFS_Io *io)
  325. {
  326. LZMAfile *file = (LZMAfile *) io->opaque;
  327. if (file->folder != NULL)
  328. {
  329. /* Only decrease refcount if someone actually requested this file... Prevents from overflows and close-on-open... */
  330. if (file->folder->references > 0)
  331. file->folder->references--;
  332. if (file->folder->references == 0)
  333. {
  334. /* Free the cache which might have been allocated by LZMA_read() */
  335. allocator.Free(file->folder->cache);
  336. file->folder->cache = NULL;
  337. }
  338. /* !!! FIXME: we don't free (file) or (file->folder)?! */
  339. } /* if */
  340. } /* LZMA_destroy */
  341. static const PHYSFS_Io LZMA_Io =
  342. {
  343. LZMA_read,
  344. LZMA_write,
  345. LZMA_seek,
  346. LZMA_tell,
  347. LZMA_length,
  348. LZMA_duplicate,
  349. LZMA_flush,
  350. LZMA_destroy,
  351. NULL
  352. };
  353. static void *LZMA_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
  354. {
  355. PHYSFS_uint8 sig[k7zSignatureSize];
  356. size_t len = 0;
  357. LZMAarchive *archive = NULL;
  358. assert(io != NULL); /* shouldn't ever happen. */
  359. BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
  360. if (io->read(io, sig, k7zSignatureSize) != k7zSignatureSize)
  361. BAIL_MACRO(NULL, 0);
  362. BAIL_IF_MACRO(!TestSignatureCandidate(sig), ERR_NOT_AN_ARCHIVE, NULL);
  363. BAIL_IF_MACRO(!io->seek(io, 0), NULL, NULL);
  364. archive = (LZMAarchive *) allocator.Malloc(sizeof (LZMAarchive));
  365. BAIL_IF_MACRO(archive == NULL, ERR_OUT_OF_MEMORY, NULL);
  366. lzma_archive_init(archive);
  367. archive->stream.io = io;
  368. CrcGenerateTable();
  369. SzArDbExInit(&archive->db);
  370. if (lzma_err(SzArchiveOpen(&archive->stream.inStream,
  371. &archive->db,
  372. &archive->stream.allocImp,
  373. &archive->stream.allocTempImp)) != SZ_OK)
  374. {
  375. SzArDbExFree(&archive->db, SzFreePhysicsFS);
  376. lzma_archive_exit(archive);
  377. return NULL; /* Error is set by lzma_err! */
  378. } /* if */
  379. len = archive->db.Database.NumFiles * sizeof (LZMAfile);
  380. archive->files = (LZMAfile *) allocator.Malloc(len);
  381. if (archive->files == NULL)
  382. {
  383. SzArDbExFree(&archive->db, SzFreePhysicsFS);
  384. lzma_archive_exit(archive);
  385. BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
  386. }
  387. /*
  388. * Init with 0 so we know when a folder is already cached
  389. * Values will be set by LZMA_openRead()
  390. */
  391. memset(archive->files, 0, len);
  392. len = archive->db.Database.NumFolders * sizeof (LZMAfolder);
  393. archive->folders = (LZMAfolder *) allocator.Malloc(len);
  394. if (archive->folders == NULL)
  395. {
  396. SzArDbExFree(&archive->db, SzFreePhysicsFS);
  397. lzma_archive_exit(archive);
  398. BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
  399. }
  400. /*
  401. * Init with 0 so we know when a folder is already cached
  402. * Values will be set by LZMA_read()
  403. */
  404. memset(archive->folders, 0, len);
  405. if(!lzma_files_init(archive))
  406. {
  407. SzArDbExFree(&archive->db, SzFreePhysicsFS);
  408. lzma_archive_exit(archive);
  409. BAIL_MACRO(ERR_UNKNOWN_ERROR, NULL);
  410. }
  411. return archive;
  412. } /* LZMA_openArchive */
  413. /*
  414. * Moved to seperate function so we can use alloca then immediately throw
  415. * away the allocated stack space...
  416. */
  417. static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
  418. const char *odir, const char *str, size_t flen)
  419. {
  420. char *newstr = __PHYSFS_smallAlloc(flen + 1);
  421. if (newstr == NULL)
  422. return;
  423. memcpy(newstr, str, flen);
  424. newstr[flen] = '\0';
  425. cb(callbackdata, odir, newstr);
  426. __PHYSFS_smallFree(newstr);
  427. } /* doEnumCallback */
  428. static void LZMA_enumerateFiles(dvoid *opaque, const char *dname,
  429. int omitSymLinks, PHYSFS_EnumFilesCallback cb,
  430. const char *origdir, void *callbackdata)
  431. {
  432. size_t dlen = strlen(dname),
  433. dlen_inc = dlen + ((dlen > 0) ? 1 : 0);
  434. LZMAarchive *archive = (LZMAarchive *) opaque;
  435. LZMAfile *file = NULL,
  436. *lastFile = &archive->files[archive->db.Database.NumFiles];
  437. if (dlen)
  438. {
  439. file = lzma_find_file(archive, dname);
  440. if (file != NULL) /* if 'file' is NULL it should stay so, otherwise errors will not be handled */
  441. file += 1;
  442. }
  443. else
  444. {
  445. file = archive->files;
  446. }
  447. BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, );
  448. while (file < lastFile)
  449. {
  450. const char * fname = file->item->Name;
  451. const char * dirNameEnd = fname + dlen_inc;
  452. if (strncmp(dname, fname, dlen) != 0) /* Stop after mismatch, archive->files is sorted */
  453. break;
  454. if (strchr(dirNameEnd, '/')) /* Skip subdirs */
  455. {
  456. file++;
  457. continue;
  458. }
  459. /* Do the actual callback... */
  460. doEnumCallback(cb, callbackdata, origdir, dirNameEnd, strlen(dirNameEnd));
  461. file++;
  462. }
  463. } /* LZMA_enumerateFiles */
  464. static PHYSFS_Io *LZMA_openRead(dvoid *opaque, const char *name, int *fileExists)
  465. {
  466. LZMAarchive *archive = (LZMAarchive *) opaque;
  467. LZMAfile *file = lzma_find_file(archive, name);
  468. PHYSFS_Io *io = NULL;
  469. *fileExists = (file != NULL);
  470. BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, NULL);
  471. BAIL_IF_MACRO(file->folder == NULL, ERR_NOT_A_FILE, NULL);
  472. file->position = 0;
  473. file->folder->references++; /* Increase refcount for automatic cleanup... */
  474. io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
  475. BAIL_IF_MACRO(io == NULL, ERR_OUT_OF_MEMORY, NULL);
  476. memcpy(io, &LZMA_Io, sizeof (*io));
  477. io->opaque = file;
  478. return io;
  479. } /* LZMA_openRead */
  480. static PHYSFS_Io *LZMA_openWrite(dvoid *opaque, const char *filename)
  481. {
  482. BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
  483. } /* LZMA_openWrite */
  484. static PHYSFS_Io *LZMA_openAppend(dvoid *opaque, const char *filename)
  485. {
  486. BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
  487. } /* LZMA_openAppend */
  488. static void LZMA_dirClose(dvoid *opaque)
  489. {
  490. LZMAarchive *archive = (LZMAarchive *) opaque;
  491. #if 0 /* !!! FIXME: you shouldn't have to do this. */
  492. PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles;
  493. for (fileIndex = 0; fileIndex < numFiles; fileIndex++)
  494. {
  495. LZMA_fileClose(&archive->files[fileIndex]);
  496. } /* for */
  497. #endif
  498. SzArDbExFree(&archive->db, SzFreePhysicsFS);
  499. archive->stream.io->destroy(archive->stream.io);
  500. lzma_archive_exit(archive);
  501. } /* LZMA_dirClose */
  502. static int LZMA_remove(dvoid *opaque, const char *name)
  503. {
  504. BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
  505. } /* LZMA_remove */
  506. static int LZMA_mkdir(dvoid *opaque, const char *name)
  507. {
  508. BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
  509. } /* LZMA_mkdir */
  510. static int LZMA_stat(dvoid *opaque, const char *filename, int *exists,
  511. PHYSFS_Stat *stat)
  512. {
  513. const LZMAarchive *archive = (const LZMAarchive *) opaque;
  514. const LZMAfile *file = lzma_find_file(archive, filename);
  515. *exists = (file != 0);
  516. if (!file)
  517. return 0;
  518. if(file->item->IsDirectory)
  519. {
  520. stat->filesize = 0;
  521. stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
  522. } /* if */
  523. else
  524. {
  525. stat->filesize = (PHYSFS_sint64) file->item->Size;
  526. stat->filetype = PHYSFS_FILETYPE_REGULAR;
  527. } /* else */
  528. /* !!! FIXME: the 0's should be -1's? */
  529. if (file->item->IsLastWriteTimeDefined)
  530. stat->modtime = lzma_filetime_to_unix_timestamp(&file->item->LastWriteTime);
  531. else
  532. stat->modtime = 0;
  533. /* real create and accesstype are currently not in the lzma SDK */
  534. stat->createtime = stat->modtime;
  535. stat->accesstime = 0;
  536. stat->readonly = 1; /* 7zips are always read only */
  537. return 1;
  538. } /* LZMA_stat */
  539. const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA =
  540. {
  541. "7Z",
  542. LZMA_ARCHIVE_DESCRIPTION,
  543. "Dennis Schridde <devurandom@gmx.net>",
  544. "http://icculus.org/physfs/",
  545. };
  546. const PHYSFS_Archiver __PHYSFS_Archiver_LZMA =
  547. {
  548. &__PHYSFS_ArchiveInfo_LZMA,
  549. LZMA_openArchive, /* openArchive() method */
  550. LZMA_enumerateFiles, /* enumerateFiles() method */
  551. LZMA_openRead, /* openRead() method */
  552. LZMA_openWrite, /* openWrite() method */
  553. LZMA_openAppend, /* openAppend() method */
  554. LZMA_remove, /* remove() method */
  555. LZMA_mkdir, /* mkdir() method */
  556. LZMA_dirClose, /* dirClose() method */
  557. LZMA_stat /* stat() method */
  558. };
  559. #endif /* defined PHYSFS_SUPPORTS_7Z */
  560. /* end of lzma.c ... */