misc/libphysfs/archiver_unpacked.c
branchphysfslayer
changeset 8524 a65e9bcf0a03
parent 8522 1853628ae285
child 10017 de822cd3df3a
equal deleted inserted replaced
8522:1853628ae285 8524:a65e9bcf0a03
       
     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, must not have separate subdir
       
     9  *  entries (but can have subdirs), must be case insensitive LOW ASCII
       
    10  *  filenames <= 56 bytes. No symlinks, etc. We can relax some of these rules
       
    11  *  as necessary.
       
    12  *
       
    13  * Please see the file LICENSE.txt in the source's root directory.
       
    14  *
       
    15  *  This file written by Ryan C. Gordon.
       
    16  */
       
    17 
       
    18 #define __PHYSICSFS_INTERNAL__
       
    19 #include "physfs_internal.h"
       
    20 
       
    21 typedef struct
       
    22 {
       
    23     PHYSFS_Io *io;
       
    24     PHYSFS_uint32 entryCount;
       
    25     UNPKentry *entries;
       
    26 } UNPKinfo;
       
    27 
       
    28 
       
    29 typedef struct
       
    30 {
       
    31     PHYSFS_Io *io;
       
    32     UNPKentry *entry;
       
    33     PHYSFS_uint32 curPos;
       
    34 } UNPKfileinfo;
       
    35 
       
    36 
       
    37 void UNPK_closeArchive(PHYSFS_Dir *opaque)
       
    38 {
       
    39     UNPKinfo *info = ((UNPKinfo *) opaque);
       
    40     info->io->destroy(info->io);
       
    41     allocator.Free(info->entries);
       
    42     allocator.Free(info);
       
    43 } /* UNPK_closeArchive */
       
    44 
       
    45 
       
    46 static PHYSFS_sint64 UNPK_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
       
    47 {
       
    48     UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
       
    49     const UNPKentry *entry = finfo->entry;
       
    50     const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
       
    51     PHYSFS_sint64 rc;
       
    52 
       
    53     if (bytesLeft < len)
       
    54         len = bytesLeft;
       
    55 
       
    56     rc = finfo->io->read(finfo->io, buffer, len);
       
    57     if (rc > 0)
       
    58         finfo->curPos += (PHYSFS_uint32) rc;
       
    59 
       
    60     return rc;
       
    61 } /* UNPK_read */
       
    62 
       
    63 
       
    64 static PHYSFS_sint64 UNPK_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
       
    65 {
       
    66     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, -1);
       
    67 } /* UNPK_write */
       
    68 
       
    69 
       
    70 static PHYSFS_sint64 UNPK_tell(PHYSFS_Io *io)
       
    71 {
       
    72     return ((UNPKfileinfo *) io->opaque)->curPos;
       
    73 } /* UNPK_tell */
       
    74 
       
    75 
       
    76 static int UNPK_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
       
    77 {
       
    78     UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
       
    79     const UNPKentry *entry = finfo->entry;
       
    80     int rc;
       
    81 
       
    82     BAIL_IF_MACRO(offset >= entry->size, PHYSFS_ERR_PAST_EOF, 0);
       
    83     rc = finfo->io->seek(finfo->io, entry->startPos + offset);
       
    84     if (rc)
       
    85         finfo->curPos = (PHYSFS_uint32) offset;
       
    86 
       
    87     return rc;
       
    88 } /* UNPK_seek */
       
    89 
       
    90 
       
    91 static PHYSFS_sint64 UNPK_length(PHYSFS_Io *io)
       
    92 {
       
    93     const UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
       
    94     return ((PHYSFS_sint64) finfo->entry->size);
       
    95 } /* UNPK_length */
       
    96 
       
    97 
       
    98 static PHYSFS_Io *UNPK_duplicate(PHYSFS_Io *_io)
       
    99 {
       
   100     UNPKfileinfo *origfinfo = (UNPKfileinfo *) _io->opaque;
       
   101     PHYSFS_Io *io = NULL;
       
   102     PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
       
   103     UNPKfileinfo *finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
       
   104     GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
       
   105     GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
       
   106 
       
   107     io = origfinfo->io->duplicate(origfinfo->io);
       
   108     if (!io) goto UNPK_duplicate_failed;
       
   109     finfo->io = io;
       
   110     finfo->entry = origfinfo->entry;
       
   111     finfo->curPos = 0;
       
   112     memcpy(retval, _io, sizeof (PHYSFS_Io));
       
   113     retval->opaque = finfo;
       
   114     return retval;
       
   115 
       
   116 UNPK_duplicate_failed:
       
   117     if (finfo != NULL) allocator.Free(finfo);
       
   118     if (retval != NULL) allocator.Free(retval);
       
   119     if (io != NULL) io->destroy(io);
       
   120     return NULL;
       
   121 } /* UNPK_duplicate */
       
   122 
       
   123 static int UNPK_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
       
   124 
       
   125 static void UNPK_destroy(PHYSFS_Io *io)
       
   126 {
       
   127     UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
       
   128     finfo->io->destroy(finfo->io);
       
   129     allocator.Free(finfo);
       
   130     allocator.Free(io);
       
   131 } /* UNPK_destroy */
       
   132 
       
   133 
       
   134 static const PHYSFS_Io UNPK_Io =
       
   135 {
       
   136     CURRENT_PHYSFS_IO_API_VERSION, NULL,
       
   137     UNPK_read,
       
   138     UNPK_write,
       
   139     UNPK_seek,
       
   140     UNPK_tell,
       
   141     UNPK_length,
       
   142     UNPK_duplicate,
       
   143     UNPK_flush,
       
   144     UNPK_destroy
       
   145 };
       
   146 
       
   147 
       
   148 static int entryCmp(void *_a, size_t one, size_t two)
       
   149 {
       
   150     if (one != two)
       
   151     {
       
   152         const UNPKentry *a = (const UNPKentry *) _a;
       
   153         return __PHYSFS_stricmpASCII(a[one].name, a[two].name);
       
   154     } /* if */
       
   155 
       
   156     return 0;
       
   157 } /* entryCmp */
       
   158 
       
   159 
       
   160 static void entrySwap(void *_a, size_t one, size_t two)
       
   161 {
       
   162     if (one != two)
       
   163     {
       
   164         UNPKentry tmp;
       
   165         UNPKentry *first = &(((UNPKentry *) _a)[one]);
       
   166         UNPKentry *second = &(((UNPKentry *) _a)[two]);
       
   167         memcpy(&tmp, first, sizeof (UNPKentry));
       
   168         memcpy(first, second, sizeof (UNPKentry));
       
   169         memcpy(second, &tmp, sizeof (UNPKentry));
       
   170     } /* if */
       
   171 } /* entrySwap */
       
   172 
       
   173 
       
   174 static PHYSFS_sint32 findStartOfDir(UNPKinfo *info, const char *path,
       
   175                                     int stop_on_first_find)
       
   176 {
       
   177     PHYSFS_sint32 lo = 0;
       
   178     PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
       
   179     PHYSFS_sint32 middle;
       
   180     PHYSFS_uint32 dlen = (PHYSFS_uint32) strlen(path);
       
   181     PHYSFS_sint32 retval = -1;
       
   182     const char *name;
       
   183     int rc;
       
   184 
       
   185     if (*path == '\0')  /* root dir? */
       
   186         return 0;
       
   187 
       
   188     if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */
       
   189         dlen--;
       
   190 
       
   191     while (lo <= hi)
       
   192     {
       
   193         middle = lo + ((hi - lo) / 2);
       
   194         name = info->entries[middle].name;
       
   195         rc = __PHYSFS_strnicmpASCII(path, name, dlen);
       
   196         if (rc == 0)
       
   197         {
       
   198             char ch = name[dlen];
       
   199             if (ch < '/') /* make sure this isn't just a substr match. */
       
   200                 rc = -1;
       
   201             else if (ch > '/')
       
   202                 rc = 1;
       
   203             else 
       
   204             {
       
   205                 if (stop_on_first_find) /* Just checking dir's existance? */
       
   206                     return middle;
       
   207 
       
   208                 if (name[dlen + 1] == '\0') /* Skip initial dir entry. */
       
   209                     return (middle + 1);
       
   210 
       
   211                 /* there might be more entries earlier in the list. */
       
   212                 retval = middle;
       
   213                 hi = middle - 1;
       
   214             } /* else */
       
   215         } /* if */
       
   216 
       
   217         if (rc > 0)
       
   218             lo = middle + 1;
       
   219         else
       
   220             hi = middle - 1;
       
   221     } /* while */
       
   222 
       
   223     return retval;
       
   224 } /* findStartOfDir */
       
   225 
       
   226 
       
   227 /*
       
   228  * Moved to seperate function so we can use alloca then immediately throw
       
   229  *  away the allocated stack space...
       
   230  */
       
   231 static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
       
   232                            const char *odir, const char *str, PHYSFS_sint32 ln)
       
   233 {
       
   234     char *newstr = __PHYSFS_smallAlloc(ln + 1);
       
   235     if (newstr == NULL)
       
   236         return;
       
   237 
       
   238     memcpy(newstr, str, ln);
       
   239     newstr[ln] = '\0';
       
   240     cb(callbackdata, odir, newstr);
       
   241     __PHYSFS_smallFree(newstr);
       
   242 } /* doEnumCallback */
       
   243 
       
   244 
       
   245 void UNPK_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
       
   246                          int omitSymLinks, PHYSFS_EnumFilesCallback cb,
       
   247                          const char *origdir, void *callbackdata)
       
   248 {
       
   249     UNPKinfo *info = ((UNPKinfo *) opaque);
       
   250     PHYSFS_sint32 dlen, dlen_inc, max, i;
       
   251 
       
   252     i = findStartOfDir(info, dname, 0);
       
   253     if (i == -1)  /* no such directory. */
       
   254         return;
       
   255 
       
   256     dlen = (PHYSFS_sint32) strlen(dname);
       
   257     if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */
       
   258         dlen--;
       
   259 
       
   260     dlen_inc = ((dlen > 0) ? 1 : 0) + dlen;
       
   261     max = (PHYSFS_sint32) info->entryCount;
       
   262     while (i < max)
       
   263     {
       
   264         char *add;
       
   265         char *ptr;
       
   266         PHYSFS_sint32 ln;
       
   267         char *e = info->entries[i].name;
       
   268         if ((dlen) &&
       
   269             ((__PHYSFS_strnicmpASCII(e, dname, dlen)) || (e[dlen] != '/')))
       
   270         {
       
   271             break;  /* past end of this dir; we're done. */
       
   272         } /* if */
       
   273 
       
   274         add = e + dlen_inc;
       
   275         ptr = strchr(add, '/');
       
   276         ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add));
       
   277         doEnumCallback(cb, callbackdata, origdir, add, ln);
       
   278         ln += dlen_inc;  /* point past entry to children... */
       
   279 
       
   280         /* increment counter and skip children of subdirs... */
       
   281         while ((++i < max) && (ptr != NULL))
       
   282         {
       
   283             char *e_new = info->entries[i].name;
       
   284             if ((__PHYSFS_strnicmpASCII(e, e_new, ln) != 0) ||
       
   285                 (e_new[ln] != '/'))
       
   286             {
       
   287                 break;
       
   288             } /* if */
       
   289         } /* while */
       
   290     } /* while */
       
   291 } /* UNPK_enumerateFiles */
       
   292 
       
   293 
       
   294 /*
       
   295  * This will find the UNPKentry associated with a path in platform-independent
       
   296  *  notation. Directories don't have UNPKentries associated with them, but 
       
   297  *  (*isDir) will be set to non-zero if a dir was hit.
       
   298  */
       
   299 static UNPKentry *findEntry(const UNPKinfo *info, const char *path, int *isDir)
       
   300 {
       
   301     UNPKentry *a = info->entries;
       
   302     PHYSFS_sint32 pathlen = (PHYSFS_sint32) strlen(path);
       
   303     PHYSFS_sint32 lo = 0;
       
   304     PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
       
   305     PHYSFS_sint32 middle;
       
   306     const char *thispath = NULL;
       
   307     int rc;
       
   308 
       
   309     while (lo <= hi)
       
   310     {
       
   311         middle = lo + ((hi - lo) / 2);
       
   312         thispath = a[middle].name;
       
   313         rc = __PHYSFS_strnicmpASCII(path, thispath, pathlen);
       
   314 
       
   315         if (rc > 0)
       
   316             lo = middle + 1;
       
   317 
       
   318         else if (rc < 0)
       
   319             hi = middle - 1;
       
   320 
       
   321         else /* substring match...might be dir or entry or nothing. */
       
   322         {
       
   323             if (isDir != NULL)
       
   324             {
       
   325                 *isDir = (thispath[pathlen] == '/');
       
   326                 if (*isDir)
       
   327                     return NULL;
       
   328             } /* if */
       
   329 
       
   330             if (thispath[pathlen] == '\0') /* found entry? */
       
   331                 return &a[middle];
       
   332             /* adjust search params, try again. */
       
   333             else if (thispath[pathlen] > '/')
       
   334                 hi = middle - 1;
       
   335             else
       
   336                 lo = middle + 1;
       
   337         } /* if */
       
   338     } /* while */
       
   339 
       
   340     if (isDir != NULL)
       
   341         *isDir = 0;
       
   342 
       
   343     BAIL_MACRO(PHYSFS_ERR_NO_SUCH_PATH, NULL);
       
   344 } /* findEntry */
       
   345 
       
   346 
       
   347 PHYSFS_Io *UNPK_openRead(PHYSFS_Dir *opaque, const char *fnm, int *fileExists)
       
   348 {
       
   349     PHYSFS_Io *retval = NULL;
       
   350     UNPKinfo *info = (UNPKinfo *) opaque;
       
   351     UNPKfileinfo *finfo = NULL;
       
   352     int isdir = 0;
       
   353     UNPKentry *entry = findEntry(info, fnm, &isdir);
       
   354 
       
   355     *fileExists = (entry != NULL);
       
   356     GOTO_IF_MACRO(isdir, PHYSFS_ERR_NOT_A_FILE, UNPK_openRead_failed);
       
   357     GOTO_IF_MACRO(!entry, ERRPASS, UNPK_openRead_failed);
       
   358 
       
   359     retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
       
   360     GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
       
   361 
       
   362     finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
       
   363     GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
       
   364 
       
   365     finfo->io = info->io->duplicate(info->io);
       
   366     GOTO_IF_MACRO(!finfo->io, ERRPASS, UNPK_openRead_failed);
       
   367 
       
   368     if (!finfo->io->seek(finfo->io, entry->startPos))
       
   369         goto UNPK_openRead_failed;
       
   370 
       
   371     finfo->curPos = 0;
       
   372     finfo->entry = entry;
       
   373 
       
   374     memcpy(retval, &UNPK_Io, sizeof (*retval));
       
   375     retval->opaque = finfo;
       
   376     return retval;
       
   377 
       
   378 UNPK_openRead_failed:
       
   379     if (finfo != NULL)
       
   380     {
       
   381         if (finfo->io != NULL)
       
   382             finfo->io->destroy(finfo->io);
       
   383         allocator.Free(finfo);
       
   384     } /* if */
       
   385 
       
   386     if (retval != NULL)
       
   387         allocator.Free(retval);
       
   388 
       
   389     return NULL;
       
   390 } /* UNPK_openRead */
       
   391 
       
   392 
       
   393 PHYSFS_Io *UNPK_openWrite(PHYSFS_Dir *opaque, const char *name)
       
   394 {
       
   395     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
       
   396 } /* UNPK_openWrite */
       
   397 
       
   398 
       
   399 PHYSFS_Io *UNPK_openAppend(PHYSFS_Dir *opaque, const char *name)
       
   400 {
       
   401     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
       
   402 } /* UNPK_openAppend */
       
   403 
       
   404 
       
   405 int UNPK_remove(PHYSFS_Dir *opaque, const char *name)
       
   406 {
       
   407     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
       
   408 } /* UNPK_remove */
       
   409 
       
   410 
       
   411 int UNPK_mkdir(PHYSFS_Dir *opaque, const char *name)
       
   412 {
       
   413     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
       
   414 } /* UNPK_mkdir */
       
   415 
       
   416 
       
   417 int UNPK_stat(PHYSFS_Dir *opaque, const char *filename,
       
   418               int *exists, PHYSFS_Stat *stat)
       
   419 {
       
   420     int isDir = 0;
       
   421     const UNPKinfo *info = (const UNPKinfo *) opaque;
       
   422     const UNPKentry *entry = findEntry(info, filename, &isDir);
       
   423 
       
   424     if (isDir)
       
   425     {
       
   426         *exists = 1;
       
   427         stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
       
   428         stat->filesize = 0;
       
   429     } /* if */
       
   430     else if (entry != NULL)
       
   431     {
       
   432         *exists = 1;
       
   433         stat->filetype = PHYSFS_FILETYPE_REGULAR;
       
   434         stat->filesize = entry->size;
       
   435     } /* else if */
       
   436     else
       
   437     {
       
   438         *exists = 0;
       
   439         return 0;
       
   440     } /* else */
       
   441 
       
   442     stat->modtime = -1;
       
   443     stat->createtime = -1;
       
   444     stat->accesstime = -1;
       
   445     stat->readonly = 1;
       
   446 
       
   447     return 1;
       
   448 } /* UNPK_stat */
       
   449 
       
   450 
       
   451 PHYSFS_Dir *UNPK_openArchive(PHYSFS_Io *io, UNPKentry *e,
       
   452                              const PHYSFS_uint32 num)
       
   453 {
       
   454     UNPKinfo *info = (UNPKinfo *) allocator.Malloc(sizeof (UNPKinfo));
       
   455     if (info == NULL)
       
   456     {
       
   457         allocator.Free(e);
       
   458         BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   459     } /* if */
       
   460 
       
   461     __PHYSFS_sort(e, (size_t) num, entryCmp, entrySwap);
       
   462     info->io = io;
       
   463     info->entryCount = num;
       
   464     info->entries = e;
       
   465 
       
   466     return info;
       
   467 } /* UNPK_openArchive */
       
   468 
       
   469 /* end of archiver_unpacked.c ... */
       
   470