Subversion Repositories Games.Descent

Rev

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

  1. /*
  2.  * High-level PhysicsFS archiver for simple unpacked file formats.
  3.  *
  4.  * This is a framework that basic archivers build on top of. It's for simple
  5.  *  formats that can just hand back a list of files and the offsets of their
  6.  *  uncompressed data. There are an alarming number of formats like this.
  7.  *
  8.  * RULES: Archive entries must be uncompressed. Dirs and files allowed, but no
  9.  *  symlinks, etc. We can relax some of these rules as necessary.
  10.  *
  11.  * Please see the file LICENSE.txt in the source's root directory.
  12.  *
  13.  *  This file written by Ryan C. Gordon.
  14.  */
  15.  
  16. #define __PHYSICSFS_INTERNAL__
  17. #include "physfs_internal.h"
  18.  
  19. typedef struct
  20. {
  21.     __PHYSFS_DirTree tree;
  22.     PHYSFS_Io *io;
  23. } UNPKinfo;
  24.  
  25. typedef struct
  26. {
  27.     __PHYSFS_DirTreeEntry tree;
  28.     PHYSFS_uint64 startPos;
  29.     PHYSFS_uint64 size;
  30.     PHYSFS_sint64 ctime;
  31.     PHYSFS_sint64 mtime;
  32. } UNPKentry;
  33.  
  34. typedef struct
  35. {
  36.     PHYSFS_Io *io;
  37.     UNPKentry *entry;
  38.     PHYSFS_uint32 curPos;
  39. } UNPKfileinfo;
  40.  
  41.  
  42. void UNPK_closeArchive(void *opaque)
  43. {
  44.     UNPKinfo *info = ((UNPKinfo *) opaque);
  45.     if (info)
  46.     {
  47.         __PHYSFS_DirTreeDeinit(&info->tree);
  48.  
  49.         if (info->io)
  50.             info->io->destroy(info->io);
  51.  
  52.         allocator.Free(info);
  53.     } /* if */
  54. } /* UNPK_closeArchive */
  55.  
  56. void UNPK_abandonArchive(void *opaque)
  57. {
  58.     UNPKinfo *info = ((UNPKinfo *) opaque);
  59.     if (info)
  60.     {
  61.         info->io = NULL;
  62.         UNPK_closeArchive(info);
  63.     } /* if */
  64. } /* UNPK_abandonArchive */
  65.  
  66. static PHYSFS_sint64 UNPK_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
  67. {
  68.     UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
  69.     const UNPKentry *entry = finfo->entry;
  70.     const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
  71.     PHYSFS_sint64 rc;
  72.  
  73.     if (bytesLeft < len)
  74.         len = bytesLeft;
  75.  
  76.     rc = finfo->io->read(finfo->io, buffer, len);
  77.     if (rc > 0)
  78.         finfo->curPos += (PHYSFS_uint32) rc;
  79.  
  80.     return rc;
  81. } /* UNPK_read */
  82.  
  83.  
  84. static PHYSFS_sint64 UNPK_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
  85. {
  86.     BAIL(PHYSFS_ERR_READ_ONLY, -1);
  87. } /* UNPK_write */
  88.  
  89.  
  90. static PHYSFS_sint64 UNPK_tell(PHYSFS_Io *io)
  91. {
  92.     return ((UNPKfileinfo *) io->opaque)->curPos;
  93. } /* UNPK_tell */
  94.  
  95.  
  96. static int UNPK_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
  97. {
  98.     UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
  99.     const UNPKentry *entry = finfo->entry;
  100.     int rc;
  101.  
  102.     BAIL_IF(offset >= entry->size, PHYSFS_ERR_PAST_EOF, 0);
  103.     rc = finfo->io->seek(finfo->io, entry->startPos + offset);
  104.     if (rc)
  105.         finfo->curPos = (PHYSFS_uint32) offset;
  106.  
  107.     return rc;
  108. } /* UNPK_seek */
  109.  
  110.  
  111. static PHYSFS_sint64 UNPK_length(PHYSFS_Io *io)
  112. {
  113.     const UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
  114.     return ((PHYSFS_sint64) finfo->entry->size);
  115. } /* UNPK_length */
  116.  
  117.  
  118. static PHYSFS_Io *UNPK_duplicate(PHYSFS_Io *_io)
  119. {
  120.     UNPKfileinfo *origfinfo = (UNPKfileinfo *) _io->opaque;
  121.     PHYSFS_Io *io = NULL;
  122.     PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
  123.     UNPKfileinfo *finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
  124.     GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
  125.     GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
  126.  
  127.     io = origfinfo->io->duplicate(origfinfo->io);
  128.     if (!io) goto UNPK_duplicate_failed;
  129.     finfo->io = io;
  130.     finfo->entry = origfinfo->entry;
  131.     finfo->curPos = 0;
  132.     memcpy(retval, _io, sizeof (PHYSFS_Io));
  133.     retval->opaque = finfo;
  134.     return retval;
  135.  
  136. UNPK_duplicate_failed:
  137.     if (finfo != NULL) allocator.Free(finfo);
  138.     if (retval != NULL) allocator.Free(retval);
  139.     if (io != NULL) io->destroy(io);
  140.     return NULL;
  141. } /* UNPK_duplicate */
  142.  
  143. static int UNPK_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
  144.  
  145. static void UNPK_destroy(PHYSFS_Io *io)
  146. {
  147.     UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
  148.     finfo->io->destroy(finfo->io);
  149.     allocator.Free(finfo);
  150.     allocator.Free(io);
  151. } /* UNPK_destroy */
  152.  
  153.  
  154. static const PHYSFS_Io UNPK_Io =
  155. {
  156.     CURRENT_PHYSFS_IO_API_VERSION, NULL,
  157.     UNPK_read,
  158.     UNPK_write,
  159.     UNPK_seek,
  160.     UNPK_tell,
  161.     UNPK_length,
  162.     UNPK_duplicate,
  163.     UNPK_flush,
  164.     UNPK_destroy
  165. };
  166.  
  167.  
  168. static inline UNPKentry *findEntry(UNPKinfo *info, const char *path)
  169. {
  170.     return (UNPKentry *) __PHYSFS_DirTreeFind(&info->tree, path);
  171. } /* findEntry */
  172.  
  173.  
  174. PHYSFS_Io *UNPK_openRead(void *opaque, const char *name)
  175. {
  176.     PHYSFS_Io *retval = NULL;
  177.     UNPKinfo *info = (UNPKinfo *) opaque;
  178.     UNPKfileinfo *finfo = NULL;
  179.     UNPKentry *entry = findEntry(info, name);
  180.  
  181.     BAIL_IF_ERRPASS(!entry, NULL);
  182.     BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL);
  183.  
  184.     retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
  185.     GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
  186.  
  187.     finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
  188.     GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
  189.  
  190.     finfo->io = info->io->duplicate(info->io);
  191.     GOTO_IF_ERRPASS(!finfo->io, UNPK_openRead_failed);
  192.  
  193.     if (!finfo->io->seek(finfo->io, entry->startPos))
  194.         goto UNPK_openRead_failed;
  195.  
  196.     finfo->curPos = 0;
  197.     finfo->entry = entry;
  198.  
  199.     memcpy(retval, &UNPK_Io, sizeof (*retval));
  200.     retval->opaque = finfo;
  201.     return retval;
  202.  
  203. UNPK_openRead_failed:
  204.     if (finfo != NULL)
  205.     {
  206.         if (finfo->io != NULL)
  207.             finfo->io->destroy(finfo->io);
  208.         allocator.Free(finfo);
  209.     } /* if */
  210.  
  211.     if (retval != NULL)
  212.         allocator.Free(retval);
  213.  
  214.     return NULL;
  215. } /* UNPK_openRead */
  216.  
  217.  
  218. PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name)
  219. {
  220.     BAIL(PHYSFS_ERR_READ_ONLY, NULL);
  221. } /* UNPK_openWrite */
  222.  
  223.  
  224. PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name)
  225. {
  226.     BAIL(PHYSFS_ERR_READ_ONLY, NULL);
  227. } /* UNPK_openAppend */
  228.  
  229.  
  230. int UNPK_remove(void *opaque, const char *name)
  231. {
  232.     BAIL(PHYSFS_ERR_READ_ONLY, 0);
  233. } /* UNPK_remove */
  234.  
  235.  
  236. int UNPK_mkdir(void *opaque, const char *name)
  237. {
  238.     BAIL(PHYSFS_ERR_READ_ONLY, 0);
  239. } /* UNPK_mkdir */
  240.  
  241.  
  242. int UNPK_stat(void *opaque, const char *path, PHYSFS_Stat *stat)
  243. {
  244.     UNPKinfo *info = (UNPKinfo *) opaque;
  245.     const UNPKentry *entry = findEntry(info, path);
  246.  
  247.     BAIL_IF_ERRPASS(!entry, 0);
  248.  
  249.     if (entry->tree.isdir)
  250.     {
  251.         stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
  252.         stat->filesize = 0;
  253.     } /* if */
  254.     else
  255.     {
  256.         stat->filetype = PHYSFS_FILETYPE_REGULAR;
  257.         stat->filesize = entry->size;
  258.     } /* else */
  259.  
  260.     stat->modtime = entry->mtime;
  261.     stat->createtime = entry->ctime;
  262.     stat->accesstime = -1;
  263.     stat->readonly = 1;
  264.  
  265.     return 1;
  266. } /* UNPK_stat */
  267.  
  268.  
  269. void *UNPK_addEntry(void *opaque, char *name, const int isdir,
  270.                     const PHYSFS_sint64 ctime, const PHYSFS_sint64 mtime,
  271.                     const PHYSFS_uint64 pos, const PHYSFS_uint64 len)
  272. {
  273.     UNPKinfo *info = (UNPKinfo *) opaque;
  274.     UNPKentry *entry;
  275.  
  276.     entry = (UNPKentry *) __PHYSFS_DirTreeAdd(&info->tree, name, isdir);
  277.     BAIL_IF_ERRPASS(!entry, NULL);
  278.  
  279.     entry->startPos = isdir ? 0 : pos;
  280.     entry->size = isdir ? 0 : len;
  281.     entry->ctime = ctime;
  282.     entry->mtime = mtime;
  283.  
  284.     return entry;
  285. } /* UNPK_addEntry */
  286.  
  287.  
  288. void *UNPK_openArchive(PHYSFS_Io *io)
  289. {
  290.     UNPKinfo *info = (UNPKinfo *) allocator.Malloc(sizeof (UNPKinfo));
  291.     BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  292.  
  293.     if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (UNPKentry)))
  294.     {
  295.         allocator.Free(info);
  296.         return NULL;
  297.     } /* if */
  298.  
  299.     info->io = io;
  300.  
  301.     return info;
  302. } /* UNPK_openArchive */
  303.  
  304. /* end of physfs_archiver_unpacked.c ... */
  305.  
  306.