Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * 7zip support routines for PhysicsFS.
  3.  *
  4.  * Please see the file LICENSE.txt in the source's root directory.
  5.  *
  6.  *  This file was written by Ryan C. Gordon.
  7.  */
  8.  
  9. #define __PHYSICSFS_INTERNAL__
  10. #include "physfs_internal.h"
  11.  
  12. #if PHYSFS_SUPPORTS_7Z
  13.  
  14. #include "physfs_lzmasdk.h"
  15.  
  16. typedef struct
  17. {
  18.     ISeekInStream seekStream; /* lzma sdk i/o interface (lower level).  */
  19.     PHYSFS_Io *io;            /* physfs i/o interface for this archive. */
  20.     CLookToRead lookStream;   /* lzma sdk i/o interface (higher level). */
  21. } SZIPLookToRead;
  22.  
  23. /* One SZIPentry is kept for each file in an open 7zip archive. */
  24. typedef struct
  25. {
  26.     __PHYSFS_DirTreeEntry tree;   /* manages directory tree         */
  27.     PHYSFS_uint32 dbidx;          /* index into lzma sdk database   */
  28. } SZIPentry;
  29.  
  30. /* One SZIPinfo is kept for each open 7zip archive. */
  31. typedef struct
  32. {
  33.     __PHYSFS_DirTree tree;    /* manages directory tree.           */
  34.     PHYSFS_Io *io;            /* physfs i/o interface for this archive. */
  35.     CSzArEx db;               /* lzma sdk archive database object. */
  36. } SZIPinfo;
  37.  
  38.  
  39. static PHYSFS_ErrorCode szipErrorCode(const SRes rc)
  40. {
  41.     switch (rc)
  42.     {
  43.         case SZ_OK: return PHYSFS_ERR_OK;
  44.         case SZ_ERROR_DATA: return PHYSFS_ERR_CORRUPT;
  45.         case SZ_ERROR_MEM: return PHYSFS_ERR_OUT_OF_MEMORY;
  46.         case SZ_ERROR_CRC: return PHYSFS_ERR_CORRUPT;
  47.         case SZ_ERROR_UNSUPPORTED: return PHYSFS_ERR_UNSUPPORTED;
  48.         case SZ_ERROR_INPUT_EOF: return PHYSFS_ERR_CORRUPT;
  49.         case SZ_ERROR_OUTPUT_EOF: return PHYSFS_ERR_IO;
  50.         case SZ_ERROR_READ: return PHYSFS_ERR_IO;
  51.         case SZ_ERROR_WRITE: return PHYSFS_ERR_IO;
  52.         case SZ_ERROR_ARCHIVE: return PHYSFS_ERR_CORRUPT;
  53.         case SZ_ERROR_NO_ARCHIVE: return PHYSFS_ERR_UNSUPPORTED;
  54.         default: break;
  55.     } /* switch */
  56.  
  57.     return PHYSFS_ERR_OTHER_ERROR;
  58. } /* szipErrorCode */
  59.  
  60.  
  61. /* LZMA SDK's ISzAlloc interface ... */
  62.  
  63. static void *SZIP_ISzAlloc_Alloc(void *p, size_t size)
  64. {
  65.     return allocator.Malloc(size ? size : 1);
  66. } /* SZIP_ISzAlloc_Alloc */
  67.  
  68. static void SZIP_ISzAlloc_Free(void *p, void *address)
  69. {
  70.     if (address)
  71.         allocator.Free(address);
  72. } /* SZIP_ISzAlloc_Free */
  73.  
  74. static ISzAlloc SZIP_SzAlloc = {
  75.     SZIP_ISzAlloc_Alloc, SZIP_ISzAlloc_Free
  76. };
  77.  
  78.  
  79. /* we implement ISeekInStream, and then wrap that in LZMA SDK's CLookToRead,
  80.    which implements the higher-level ILookInStream on top of that, handling
  81.    buffering and such for us. */
  82.  
  83. /* LZMA SDK's ISeekInStream interface ... */
  84.  
  85. static SRes SZIP_ISeekInStream_Read(void *p, void *buf, size_t *size)
  86. {
  87.     SZIPLookToRead *stream = (SZIPLookToRead *) p;
  88.     PHYSFS_Io *io = stream->io;
  89.     const PHYSFS_uint64 len = (PHYSFS_uint64) *size;
  90.     const PHYSFS_sint64 rc = (len == 0) ? 0 : io->read(io, buf, len);
  91.  
  92.     if (rc < 0)
  93.     {
  94.         *size = 0;
  95.         return SZ_ERROR_READ;
  96.     } /* if */
  97.  
  98.     *size = (size_t) rc;
  99.     return SZ_OK;
  100. } /* SZIP_ISeekInStream_Read */
  101.  
  102. static SRes SZIP_ISeekInStream_Seek(void *p, Int64 *pos, ESzSeek origin)
  103. {
  104.     SZIPLookToRead *stream = (SZIPLookToRead *) p;
  105.     PHYSFS_Io *io = stream->io;
  106.     PHYSFS_sint64 base;
  107.     PHYSFS_uint64 newpos;
  108.  
  109.     switch (origin)
  110.     {
  111.         case SZ_SEEK_SET:
  112.             base = 0;
  113.             break;
  114.  
  115.         case SZ_SEEK_CUR:
  116.             base = io->tell(io);
  117.             break;
  118.  
  119.         case SZ_SEEK_END:
  120.             base = io->length(io);
  121.             break;
  122.  
  123.         default:
  124.             return SZ_ERROR_FAIL;
  125.     } /* switch */
  126.  
  127.     if (base < 0)
  128.         return SZ_ERROR_FAIL;
  129.     else if ((*pos < 0) && (((Int64) base) < -*pos))
  130.         return SZ_ERROR_FAIL;
  131.  
  132.     newpos = (PHYSFS_uint64) (((Int64) base) + *pos);
  133.     if (!io->seek(io, newpos))
  134.         return SZ_ERROR_FAIL;
  135.  
  136.     *pos = (Int64) newpos;
  137.     return SZ_OK;
  138. } /* SZIP_ISeekInStream_Seek */
  139.  
  140.  
  141. static void szipInitStream(SZIPLookToRead *stream, PHYSFS_Io *io)
  142. {
  143.     stream->seekStream.Read = SZIP_ISeekInStream_Read;
  144.     stream->seekStream.Seek = SZIP_ISeekInStream_Seek;
  145.  
  146.     stream->io = io;
  147.  
  148.     /* !!! FIXME: can we use lookahead? Is there value to it? */
  149.     LookToRead_Init(&stream->lookStream);
  150.     LookToRead_CreateVTable(&stream->lookStream, False);
  151.     stream->lookStream.realStream = &stream->seekStream;
  152. } /* szipInitStream */
  153.  
  154.  
  155. /* Do this in a separate function so we can smallAlloc without looping. */
  156. static int szipLoadEntry(SZIPinfo *info, const PHYSFS_uint32 idx)
  157. {
  158.     const size_t utf16len = SzArEx_GetFileNameUtf16(&info->db, idx, NULL);
  159.     const size_t utf16buflen = utf16len * 2;
  160.     PHYSFS_uint16 *utf16 = (PHYSFS_uint16 *) __PHYSFS_smallAlloc(utf16buflen);
  161.     const size_t utf8buflen = utf16len * 4;
  162.     char *utf8 = (char *) __PHYSFS_smallAlloc(utf8buflen);
  163.     int retval = 0;
  164.  
  165.     if (utf16 && utf8)
  166.     {
  167.         const int isdir = SzArEx_IsDir(&info->db, idx) != 0;
  168.         SZIPentry *entry;
  169.         SzArEx_GetFileNameUtf16(&info->db, idx, (UInt16 *) utf16);
  170.         PHYSFS_utf8FromUtf16(utf16, utf8, utf8buflen);
  171.         entry = (SZIPentry*) __PHYSFS_DirTreeAdd(&info->tree, utf8, isdir);
  172.         retval = (entry != NULL);
  173.         if (retval)
  174.             entry->dbidx = idx;
  175.     } /* if */
  176.  
  177.     __PHYSFS_smallFree(utf8);
  178.     __PHYSFS_smallFree(utf16);
  179.  
  180.     return retval;
  181. } /* szipLoadEntry */
  182.  
  183.  
  184. static int szipLoadEntries(SZIPinfo *info)
  185. {
  186.     int retval = 0;
  187.  
  188.     if (__PHYSFS_DirTreeInit(&info->tree, sizeof (SZIPentry)))
  189.     {
  190.         const PHYSFS_uint32 count = info->db.NumFiles;
  191.         PHYSFS_uint32 i;
  192.         for (i = 0; i < count; i++)
  193.             BAIL_IF_ERRPASS(!szipLoadEntry(info, i), 0);
  194.         retval = 1;
  195.     } /* if */
  196.  
  197.     return retval;
  198. } /* szipLoadEntries */
  199.  
  200.  
  201. static void SZIP_closeArchive(void *opaque)
  202. {
  203.     SZIPinfo *info = (SZIPinfo *) opaque;
  204.     if (info)
  205.     {
  206.         if (info->io)
  207.             info->io->destroy(info->io);
  208.         SzArEx_Free(&info->db, &SZIP_SzAlloc);
  209.         __PHYSFS_DirTreeDeinit(&info->tree);
  210.         allocator.Free(info);
  211.     } /* if */
  212. } /* SZIP_closeArchive */
  213.  
  214.  
  215. static void *SZIP_openArchive(PHYSFS_Io *io, const char *name,
  216.                               int forWriting, int *claimed)
  217. {
  218.     static const PHYSFS_uint8 wantedsig[] = { '7','z',0xBC,0xAF,0x27,0x1C };
  219.     SZIPLookToRead stream;
  220.     ISzAlloc *alloc = &SZIP_SzAlloc;
  221.     SZIPinfo *info = NULL;
  222.     SRes rc;
  223.     PHYSFS_uint8 sig[6];
  224.     PHYSFS_sint64 pos;
  225.  
  226.     BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
  227.     pos = io->tell(io);
  228.     BAIL_IF_ERRPASS(pos == -1, NULL);
  229.     BAIL_IF_ERRPASS(io->read(io, sig, 6) != 6, NULL);
  230.     *claimed = (memcmp(sig, wantedsig, 6) == 0);
  231.     BAIL_IF_ERRPASS(!io->seek(io, pos), NULL);
  232.  
  233.     info = (SZIPinfo *) allocator.Malloc(sizeof (SZIPinfo));
  234.     BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  235.     memset(info, '\0', sizeof (*info));
  236.  
  237.     SzArEx_Init(&info->db);
  238.  
  239.     info->io = io;
  240.  
  241.     szipInitStream(&stream, io);
  242.     rc = SzArEx_Open(&info->db, &stream.lookStream.s, alloc, alloc);
  243.     GOTO_IF(rc != SZ_OK, szipErrorCode(rc), failed);
  244.  
  245.     GOTO_IF_ERRPASS(!szipLoadEntries(info), failed);
  246.  
  247.     return info;
  248.  
  249. failed:
  250.     info->io = NULL;  /* don't let cleanup destroy the PHYSFS_Io. */
  251.     SZIP_closeArchive(info);
  252.     return NULL;
  253. } /* SZIP_openArchive */
  254.  
  255.  
  256. static PHYSFS_Io *SZIP_openRead(void *opaque, const char *path)
  257. {
  258.     /* !!! FIXME: the current lzma sdk C API only allows you to decompress
  259.        !!! FIXME:  the entire file at once, which isn't ideal. Fix this in the
  260.        !!! FIXME:  SDK and then convert this all to a streaming interface. */
  261.  
  262.     SZIPinfo *info = (SZIPinfo *) opaque;
  263.     SZIPentry *entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path);
  264.     ISzAlloc *alloc = &SZIP_SzAlloc;
  265.     SZIPLookToRead stream;
  266.     PHYSFS_Io *retval = NULL;
  267.     PHYSFS_Io *io = NULL;
  268.     UInt32 blockIndex = 0xFFFFFFFF;
  269.     Byte *outBuffer = NULL;
  270.     size_t outBufferSize = 0;
  271.     size_t offset = 0;
  272.     size_t outSizeProcessed = 0;
  273.     void *buf = NULL;
  274.     SRes rc;
  275.  
  276.     BAIL_IF_ERRPASS(!entry, NULL);
  277.     BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL);
  278.  
  279.     io = info->io->duplicate(info->io);
  280.     GOTO_IF_ERRPASS(!io, SZIP_openRead_failed);
  281.  
  282.     szipInitStream(&stream, io);
  283.  
  284.     rc = SzArEx_Extract(&info->db, &stream.lookStream.s, entry->dbidx,
  285.                         &blockIndex, &outBuffer, &outBufferSize, &offset,
  286.                         &outSizeProcessed, alloc, alloc);
  287.     GOTO_IF(rc != SZ_OK, szipErrorCode(rc), SZIP_openRead_failed);
  288.  
  289.     io->destroy(io);
  290.     io = NULL;
  291.  
  292.     buf = allocator.Malloc(outSizeProcessed);
  293.     GOTO_IF(rc != SZ_OK, PHYSFS_ERR_OUT_OF_MEMORY, SZIP_openRead_failed);
  294.     memcpy(buf, outBuffer + offset, outSizeProcessed);
  295.  
  296.     alloc->Free(alloc, outBuffer);
  297.     outBuffer = NULL;
  298.  
  299.     retval = __PHYSFS_createMemoryIo(buf, outSizeProcessed, allocator.Free);
  300.     GOTO_IF_ERRPASS(!retval, SZIP_openRead_failed);
  301.  
  302.     return retval;
  303.  
  304. SZIP_openRead_failed:
  305.     if (io != NULL)
  306.         io->destroy(io);
  307.  
  308.     if (buf)
  309.         allocator.Free(buf);
  310.  
  311.     if (outBuffer)
  312.         alloc->Free(alloc, outBuffer);
  313.  
  314.     return NULL;
  315. } /* SZIP_openRead */
  316.  
  317.  
  318. static PHYSFS_Io *SZIP_openWrite(void *opaque, const char *filename)
  319. {
  320.     BAIL(PHYSFS_ERR_READ_ONLY, NULL);
  321. } /* SZIP_openWrite */
  322.  
  323.  
  324. static PHYSFS_Io *SZIP_openAppend(void *opaque, const char *filename)
  325. {
  326.     BAIL(PHYSFS_ERR_READ_ONLY, NULL);
  327. } /* SZIP_openAppend */
  328.  
  329.  
  330. static int SZIP_remove(void *opaque, const char *name)
  331. {
  332.     BAIL(PHYSFS_ERR_READ_ONLY, 0);
  333. } /* SZIP_remove */
  334.  
  335.  
  336. static int SZIP_mkdir(void *opaque, const char *name)
  337. {
  338.     BAIL(PHYSFS_ERR_READ_ONLY, 0);
  339. } /* SZIP_mkdir */
  340.  
  341.  
  342. static inline PHYSFS_uint64 lzmasdkTimeToPhysfsTime(const CNtfsFileTime *t)
  343. {
  344.     const PHYSFS_uint64 winEpochToUnixEpoch = __PHYSFS_UI64(0x019DB1DED53E8000);
  345.     const PHYSFS_uint64 nanosecToMillisec = __PHYSFS_UI64(10000000);
  346.     const PHYSFS_uint64 quad = (((PHYSFS_uint64) t->High) << 32) | t->Low;
  347.     return (quad - winEpochToUnixEpoch) / nanosecToMillisec;
  348. } /* lzmasdkTimeToPhysfsTime */
  349.  
  350.  
  351. static int SZIP_stat(void *opaque, const char *path, PHYSFS_Stat *stat)
  352. {
  353.     SZIPinfo *info = (SZIPinfo *) opaque;
  354.     SZIPentry *entry;
  355.     PHYSFS_uint32 idx;
  356.  
  357.     entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path);
  358.     BAIL_IF_ERRPASS(!entry, 0);
  359.     idx = entry->dbidx;
  360.  
  361.     if (entry->tree.isdir)
  362.     {
  363.         stat->filesize = -1;
  364.             stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
  365.     } /* if */
  366.     else
  367.     {
  368.         stat->filesize = (PHYSFS_sint64) SzArEx_GetFileSize(&info->db, idx);
  369.             stat->filetype = PHYSFS_FILETYPE_REGULAR;
  370.     } /* else */
  371.  
  372.     if (info->db.MTime.Vals != NULL)
  373.             stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]);
  374.     else if (info->db.CTime.Vals != NULL)
  375.             stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]);
  376.     else
  377.             stat->modtime = -1;
  378.  
  379.     if (info->db.CTime.Vals != NULL)
  380.             stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]);
  381.     else if (info->db.MTime.Vals != NULL)
  382.             stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]);
  383.     else
  384.             stat->createtime = -1;
  385.  
  386.         stat->accesstime = -1;
  387.         stat->readonly = 1;
  388.  
  389.     return 1;
  390. } /* SZIP_stat */
  391.  
  392.  
  393. void SZIP_global_init(void)
  394. {
  395.     /* this just needs to calculate some things, so it only ever
  396.        has to run once, even after a deinit. */
  397.     static int generatedTable = 0;
  398.     if (!generatedTable)
  399.     {
  400.         generatedTable = 1;
  401.         CrcGenerateTable();
  402.     } /* if */
  403. } /* SZIP_global_init */
  404.  
  405.  
  406. const PHYSFS_Archiver __PHYSFS_Archiver_7Z =
  407. {
  408.     CURRENT_PHYSFS_ARCHIVER_API_VERSION,
  409.     {
  410.         "7Z",
  411.         "7zip archives",
  412.         "Ryan C. Gordon <icculus@icculus.org>",
  413.         "https://icculus.org/physfs/",
  414.         0,  /* supportsSymlinks */
  415.     },
  416.     SZIP_openArchive,
  417.     __PHYSFS_DirTreeEnumerate,
  418.     SZIP_openRead,
  419.     SZIP_openWrite,
  420.     SZIP_openAppend,
  421.     SZIP_remove,
  422.     SZIP_mkdir,
  423.     SZIP_stat,
  424.     SZIP_closeArchive
  425. };
  426.  
  427. #endif  /* defined PHYSFS_SUPPORTS_7Z */
  428.  
  429. /* end of physfs_archiver_7z.c ... */
  430.  
  431.