archivers/qpak.c
changeset 450 b60e00958aad
child 456 ecaab6f9e19f
equal deleted inserted replaced
449:eb054e541f67 450:b60e00958aad
       
     1 /*
       
     2  * Quake PAK support routines for PhysicsFS.
       
     3  *
       
     4  * This driver handles id Software Quake PAK files.
       
     5  *
       
     6  * Please see the file LICENSE in the source's root directory.
       
     7  *
       
     8  *  This file written by Ed Sinjiashvili.
       
     9  */
       
    10 
       
    11 #if HAVE_CONFIG_H
       
    12 #  include <config.h>
       
    13 #endif
       
    14 
       
    15 #if (defined PHYSFS_SUPPORTS_QPAK)
       
    16 #include <stdio.h>
       
    17 #include <stdlib.h>
       
    18 #include <string.h>
       
    19 #include <errno.h>
       
    20 #include <fcntl.h>
       
    21 #include <assert.h>
       
    22 #include "physfs.h"
       
    23 
       
    24 #define __PHYSICSFS_INTERNAL__
       
    25 #include "physfs_internal.h"
       
    26 
       
    27 #define QPAK_MAXDIRLEN 60
       
    28 
       
    29 typedef struct
       
    30 {
       
    31     char          name[56];
       
    32     PHYSFS_uint32 offset;
       
    33     PHYSFS_uint32 size;
       
    34 } QPAKentry;
       
    35 
       
    36 typedef struct tagQPAKdirentry
       
    37 {
       
    38     char                   *name;
       
    39     QPAKentry              *entry;
       
    40     struct tagQPAKdirentry *next;
       
    41 } QPAKdirentry;
       
    42 
       
    43 typedef struct QPAKDirectory
       
    44 {
       
    45     char name[QPAK_MAXDIRLEN];
       
    46 
       
    47     struct QPAKDirectory *dirs, *next;
       
    48 
       
    49     QPAKdirentry *files;
       
    50 } QPAKdirectory;
       
    51 
       
    52 typedef struct
       
    53 {
       
    54     void               *handle;
       
    55     char               *filename;
       
    56     PHYSFS_uint32       dirOffset;
       
    57     PHYSFS_uint32       totalEntries;
       
    58     QPAKentry          *entries;
       
    59     QPAKdirectory      *root;
       
    60 } QPAKinfo;
       
    61 
       
    62 typedef struct
       
    63 {
       
    64     void         *handle;
       
    65     QPAKentry    *entry;
       
    66     PHYSFS_sint64 curPos;
       
    67 } QPAKfileinfo;
       
    68 
       
    69 
       
    70 static int           QPAK_isArchive(const char *filename, int forWriting);
       
    71 static DirHandle    *QPAK_openArchive(const char *name, int forWriting);
       
    72 static void          QPAK_dirClose(DirHandle *h);
       
    73 static LinkedStringList *QPAK_enumerateFiles(DirHandle *h, const char *dirname,
       
    74                                              int omitSymLinks);
       
    75 static int           QPAK_exists(DirHandle *h, const char *name);
       
    76 static int           QPAK_isDirectory(DirHandle *h, const char *name);
       
    77 static int           QPAK_isSymLink(DirHandle *h, const char *name);
       
    78 static PHYSFS_sint64 QPAK_getLastModTime(DirHandle *h, const char *name);
       
    79 static FileHandle   *QPAK_openRead(DirHandle *h, const char *name);
       
    80 
       
    81 
       
    82 static PHYSFS_sint64 QPAK_read(FileHandle *handle, void *buffer,
       
    83                               PHYSFS_uint32 objSize, PHYSFS_uint32 objCount);
       
    84 static int           QPAK_eof(FileHandle *handle);
       
    85 static PHYSFS_sint64 QPAK_tell(FileHandle *handle);
       
    86 static int           QPAK_seek(FileHandle *handle, PHYSFS_uint64 offset);
       
    87 static PHYSFS_sint64 QPAK_fileLength(FileHandle *handle);
       
    88 static int           QPAK_fileClose(FileHandle *handle);
       
    89 
       
    90 
       
    91 const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK =
       
    92 {
       
    93     "PAK",
       
    94     "Quake PAK file format",
       
    95     "Ed Sinjiashvili <slimb@swes.saren.ru>",
       
    96     "http://icculus.org/physfs/",
       
    97 };
       
    98 
       
    99 static const FileFunctions __PHYSFS_FileFunctions_QPAK =
       
   100 {
       
   101     QPAK_read,               /* read() method       */
       
   102     NULL,                    /* write() method      */
       
   103     QPAK_eof,                /* eof() method        */
       
   104     QPAK_tell,               /* tell() method       */
       
   105     QPAK_seek,               /* seek() method       */
       
   106     QPAK_fileLength,         /* fileLength() method */
       
   107     QPAK_fileClose           /* fileClose() method  */
       
   108 };
       
   109 
       
   110 const DirFunctions __PHYSFS_DirFunctions_QPAK =
       
   111 {
       
   112     &__PHYSFS_ArchiveInfo_QPAK,
       
   113     QPAK_isArchive,          /* isArchive() method      */
       
   114     QPAK_openArchive,        /* openArchive() method    */
       
   115     QPAK_enumerateFiles,     /* enumerateFiles() method */
       
   116     QPAK_exists,             /* exists() method         */
       
   117     QPAK_isDirectory,        /* isDirectory() method    */
       
   118     QPAK_isSymLink,          /* isSymLink() method      */
       
   119     QPAK_getLastModTime,     /* getLastModTime() method */
       
   120     QPAK_openRead,           /* openRead() method       */
       
   121     NULL,                    /* openWrite() method      */
       
   122     NULL,                    /* openAppend() method     */
       
   123     NULL,                    /* remove() method         */
       
   124     NULL,                    /* mkdir() method          */
       
   125     QPAK_dirClose            /* dirClose() method       */
       
   126 };
       
   127 
       
   128 
       
   129 #define QPAK_MAGIC 0x4B434150  /* look like "PACK" in ascii. */
       
   130 
       
   131 
       
   132 /*
       
   133  * Read an unsigned 32-bit int and swap to native byte order.
       
   134  */
       
   135 static int readui32(void *in, PHYSFS_uint32 *val)
       
   136 {
       
   137     PHYSFS_uint32 v;
       
   138     BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0);
       
   139     *val = PHYSFS_swapULE32(v);
       
   140     return(1);
       
   141 } /* readui32 */
       
   142 
       
   143 
       
   144 static int openQPak(const char *filename, int forWriting, void **fileHandle)
       
   145 {
       
   146     PHYSFS_uint32 sig;
       
   147 
       
   148     *fileHandle = NULL;
       
   149     BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
       
   150 
       
   151     *fileHandle = __PHYSFS_platformOpenRead(filename);
       
   152     BAIL_IF_MACRO(*fileHandle == NULL, NULL, 0);
       
   153     
       
   154     if (!readui32(*fileHandle, &sig))
       
   155         goto openPak_failed;
       
   156     
       
   157     if (sig == QPAK_MAGIC)
       
   158     {
       
   159         __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
       
   160         goto openPak_failed;
       
   161     } /* if */
       
   162 
       
   163     return(1);
       
   164 
       
   165 openPak_failed:
       
   166     if (*fileHandle != NULL)
       
   167         __PHYSFS_platformClose(*fileHandle);
       
   168 
       
   169     *fileHandle = NULL;
       
   170     return(0);
       
   171 } /* openQPak */
       
   172 
       
   173 
       
   174 static int QPAK_isArchive(const char *filename, int forWriting)
       
   175 {
       
   176     void *fileHandle;
       
   177     int retval = openQPak(filename, forWriting, &fileHandle);
       
   178 
       
   179     if (fileHandle != NULL)
       
   180         __PHYSFS_platformClose(fileHandle);
       
   181 
       
   182     return(retval);
       
   183 } /* QPAK_isArchive */
       
   184 
       
   185 
       
   186 static void qpak_insertion_sort(QPAKentry *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi)
       
   187 {
       
   188     PHYSFS_uint32 i;
       
   189     PHYSFS_uint32 j;
       
   190     QPAKentry tmp;
       
   191 
       
   192     for (i = lo + 1; i <= hi; i++)
       
   193     {
       
   194         memcpy(&tmp, &a[i], sizeof (QPAKentry));
       
   195         j = i;
       
   196         while ((j > lo) && (strcmp(a[j - 1].name, tmp.name) > 0))
       
   197         {
       
   198             memcpy(&a[j], &a[j - 1], sizeof (QPAKentry));
       
   199             j--;
       
   200         } /* while */
       
   201         memcpy(&a[j], &tmp, sizeof (QPAKentry));
       
   202     } /* for */
       
   203 } /* qpak_insertion_sort */
       
   204 
       
   205 
       
   206 static void qpak_sort_entries(QPAKentry *entries, PHYSFS_uint32 max)
       
   207 {
       
   208     qpak_insertion_sort(entries, 0, max - 1);
       
   209 } /* qpak_sort_entries */
       
   210 
       
   211 
       
   212 static int qpak_loadEntries(void *fh, int dirOffset, int numEntries,
       
   213                             QPAKentry *entries)
       
   214 {
       
   215     PHYSFS_uint32 i;
       
   216 
       
   217     BAIL_IF_MACRO(__PHYSFS_platformSeek(fh, dirOffset) == 0, NULL, 0);
       
   218 
       
   219     for (i = 0; i < numEntries; i++, entries++)
       
   220     {
       
   221         PHYSFS_sint64 r = __PHYSFS_platformRead(fh, entries->name, 56, 1);
       
   222         BAIL_IF_MACRO(r == 0, NULL, 0);
       
   223         BAIL_IF_MACRO(!readui32(fh, &entries->offset), NULL, 0);
       
   224         BAIL_IF_MACRO(!readui32(fh, &entries->size), NULL, 0);
       
   225     } /* for */
       
   226 
       
   227     return(1);
       
   228 } /* qpak_loadEntries */
       
   229 
       
   230 
       
   231 static QPAKdirentry *qpak_newDirentry(char *name, QPAKentry *entry)
       
   232 {
       
   233     QPAKdirentry *retval = (QPAKdirentry *) malloc(sizeof (QPAKdirentry));
       
   234     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, 0);
       
   235 
       
   236     retval->name  = name;
       
   237     retval->entry = entry;
       
   238     retval->next  = NULL;
       
   239 
       
   240     return(retval);
       
   241 } /* qpak_newDirentry */
       
   242 
       
   243 
       
   244 static void qpak_deleteDirentry(QPAKdirentry *e)
       
   245 {
       
   246     while (e != NULL)
       
   247     {
       
   248         QPAKdirentry *next = e->next;
       
   249         free(e);
       
   250         e = next;
       
   251     } /* while */
       
   252 } /* qpak_deleteDirentry */
       
   253 
       
   254 
       
   255 static QPAKdirectory *qpak_newDirectory(char *name)
       
   256 {
       
   257     QPAKdirectory *dir = (QPAKdirectory *) malloc(sizeof (QPAKdirectory));
       
   258     BAIL_IF_MACRO(dir == NULL, ERR_OUT_OF_MEMORY, 0);
       
   259 
       
   260     strcpy(dir->name, name);
       
   261     dir->dirs = NULL;
       
   262     dir->next = NULL;
       
   263     dir->files = NULL;
       
   264 
       
   265     return dir;
       
   266 } /* qpak_newDirectory */
       
   267 
       
   268 
       
   269 static void qpak_deleteDirectory(QPAKdirectory *d)
       
   270 {
       
   271     while (d != NULL)
       
   272     {
       
   273         QPAKdirectory *next = d->next;
       
   274         qpak_deleteDirentry(d->files);
       
   275         qpak_deleteDirectory(d->dirs);
       
   276         free(d);
       
   277         d = next;
       
   278     } /* while */
       
   279 } /* qpak_deleteDirectory */
       
   280 
       
   281 
       
   282 static int qpak_addFile(QPAKdirectory *dir, char *name, QPAKentry *entry)
       
   283 {
       
   284     QPAKdirentry *file = qpak_newDirentry(name, entry);
       
   285     if (file == NULL)
       
   286         return(0);
       
   287 
       
   288     /* !!! FIXME: Traversing a linkedlist gets slower with each added file. */
       
   289     if (dir->files == NULL)
       
   290     {
       
   291         dir->files = file;
       
   292     } /* if */
       
   293     else
       
   294     {
       
   295         QPAKdirentry *tail = dir->files;
       
   296         while (tail->next != NULL)
       
   297             tail = tail->next;
       
   298 
       
   299         tail->next = file;
       
   300     } /* else */
       
   301 
       
   302     return(1);
       
   303 } /* qpak_addFile */
       
   304 
       
   305 
       
   306 static QPAKdirectory *qpak_findDirectory(QPAKdirectory *root, const char *name)
       
   307 {
       
   308     char *p = strchr(name, '/');
       
   309 
       
   310     if (p == NULL)
       
   311     {
       
   312         QPAKdirectory *thisDir = root->dirs;
       
   313         while (thisDir != NULL)
       
   314         {
       
   315             if (strcmp(thisDir->name, name) == 0)
       
   316                 return(thisDir);
       
   317             thisDir = thisDir->next;
       
   318         } /* while */
       
   319     } /* if */
       
   320 
       
   321     else
       
   322     {
       
   323         char temp[QPAK_MAXDIRLEN];
       
   324         QPAKdirectory *thisDir = root->dirs;
       
   325 
       
   326         strncpy (temp, name, p - name);
       
   327         temp[p - name] = '\0';
       
   328 
       
   329         while (thisDir != NULL)
       
   330         {
       
   331             if (strcmp(thisDir->name, temp) == 0)
       
   332                 return(qpak_findDirectory(thisDir, p + 1));
       
   333             thisDir = thisDir->next;
       
   334         } /* while */
       
   335     } /* else */
       
   336 
       
   337     return(0);
       
   338 } /* qpak_findDirectory */
       
   339 
       
   340 
       
   341 static QPAKdirectory *qpak_addDir(QPAKdirectory *dir, char *name)
       
   342 {
       
   343     QPAKdirectory *newDir = qpak_findDirectory(dir, name);
       
   344     if (newDir != 0)
       
   345         return(newDir);
       
   346 
       
   347     newDir = qpak_newDirectory(name);
       
   348     if (newDir == 0)
       
   349         return 0;
       
   350 
       
   351     if (dir->dirs == NULL)
       
   352     {
       
   353         dir->dirs = newDir;
       
   354     } /* if */
       
   355 
       
   356     else
       
   357     {
       
   358         QPAKdirectory *tail = dir->dirs;
       
   359         while (tail->next != NULL)
       
   360             tail = tail->next;
       
   361 
       
   362         tail->next = newDir;
       
   363     } /* else */
       
   364 
       
   365     return(newDir);
       
   366 } /* qpak_addDir */
       
   367 
       
   368 
       
   369 static int qpak_addEntry(QPAKdirectory *dir, char *name, QPAKentry *entry)
       
   370 {
       
   371     char tempName[QPAK_MAXDIRLEN];
       
   372     QPAKdirectory *child;
       
   373     char *p = strchr(name, '/');
       
   374     if (p == NULL)
       
   375         return(qpak_addFile(dir, name, entry));
       
   376 
       
   377     strncpy(tempName, name, p - name);
       
   378     tempName[p - name] = '\0';
       
   379 
       
   380     child = qpak_addDir(dir, tempName);
       
   381     return(qpak_addEntry(child, p + 1, entry));
       
   382 } /* qpak_addEntry */
       
   383 
       
   384 
       
   385 static QPAKentry *qpak_findEntry(QPAKdirectory *root, const char *name)
       
   386 {
       
   387     QPAKdirectory *dir = NULL;
       
   388     QPAKdirentry *thisFile = NULL;
       
   389     const char *t = strrchr (name, '/');
       
   390 
       
   391     if (t == NULL)
       
   392     {
       
   393         dir = root;
       
   394         t = name;
       
   395     } /* if */
       
   396 
       
   397     else
       
   398     {
       
   399         char temp[QPAK_MAXDIRLEN];
       
   400         strncpy(temp, name, t - name);
       
   401         temp[t - name] = '\0';
       
   402         dir = qpak_findDirectory(root, temp);
       
   403         t++;
       
   404     } /* else */
       
   405 
       
   406     if (dir == NULL)
       
   407         return(0);
       
   408 
       
   409     thisFile = dir->files;
       
   410 
       
   411     while (thisFile != NULL)
       
   412     {
       
   413         if (strcmp(thisFile->name, t) == 0)
       
   414             return(thisFile->entry);
       
   415 
       
   416         thisFile = thisFile->next;
       
   417     } /* while */
       
   418 
       
   419     return(0);
       
   420 } /* qpak_findEntry */
       
   421 
       
   422 
       
   423 static int qpak_populateDirectories(QPAKentry *entries, int numEntries,
       
   424                                     QPAKdirectory *root)
       
   425 {
       
   426     PHYSFS_uint32 i;
       
   427     QPAKentry *entry = entries;
       
   428 
       
   429     for (i = 0; i < numEntries; i++, entry++)
       
   430     {
       
   431         if (qpak_addEntry(root, entry->name, entry) == 0)
       
   432             return(0);
       
   433     } /* for */
       
   434 
       
   435     return(1);
       
   436 } /* qpak_populateDirectories */
       
   437 
       
   438 
       
   439 static void qpak_deletePakInfo (QPAKinfo *pakInfo)
       
   440 {
       
   441     if (pakInfo->filename != NULL)
       
   442         free(pakInfo->filename);
       
   443 
       
   444     if (pakInfo->entries != NULL)
       
   445         free(pakInfo->entries);
       
   446 
       
   447     qpak_deleteDirectory(pakInfo->root);
       
   448 
       
   449     free(pakInfo);
       
   450 } /* qpak_deletePakInfo */
       
   451 
       
   452 
       
   453 static DirHandle *QPAK_openArchive(const char *name, int forWriting)
       
   454 {
       
   455     void *fh = NULL;
       
   456     PHYSFS_uint32 dirOffset, dirLength;
       
   457     QPAKinfo *pi;
       
   458     DirHandle *retval;
       
   459 
       
   460     retval = (DirHandle *) malloc(sizeof (DirHandle));
       
   461     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
       
   462 
       
   463     pi = (QPAKinfo *) malloc(sizeof (QPAKinfo));
       
   464     if (pi == NULL)
       
   465     {
       
   466         free(retval);
       
   467         BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
       
   468     } /* if */
       
   469 
       
   470     retval->opaque = pi;
       
   471 
       
   472     pi->filename = (char *) malloc(strlen(name) + 1);
       
   473     if (pi->filename == NULL)
       
   474     {
       
   475         __PHYSFS_setError(ERR_OUT_OF_MEMORY);
       
   476         goto QPAK_openArchive_failed;
       
   477     } /* if */
       
   478 
       
   479     if (!openQPak(name, forWriting, &fh))
       
   480         goto QPAK_openArchive_failed;
       
   481 
       
   482     if (!readui32(fh, &dirOffset))
       
   483         goto QPAK_openArchive_failed;
       
   484 
       
   485     if (!readui32(fh, &dirLength))
       
   486         goto QPAK_openArchive_failed;
       
   487 
       
   488     if (__PHYSFS_platformFileLength(fh) < dirOffset + dirLength)
       
   489         goto QPAK_openArchive_failed;
       
   490 
       
   491     strcpy(pi->filename, name);
       
   492     pi->handle = fh;
       
   493     pi->dirOffset = dirOffset;
       
   494     pi->totalEntries = dirLength / 64;
       
   495 
       
   496     pi->entries = (QPAKentry *) malloc(pi->totalEntries * sizeof (QPAKentry));
       
   497     if (pi->entries == NULL)
       
   498     {
       
   499         __PHYSFS_setError(ERR_OUT_OF_MEMORY);
       
   500         goto QPAK_openArchive_failed;
       
   501     } /* if */
       
   502 
       
   503     if (qpak_loadEntries(fh, dirOffset, pi->totalEntries, pi->entries) == 0)
       
   504         goto QPAK_openArchive_failed;
       
   505 
       
   506     qpak_sort_entries(pi->entries, pi->totalEntries);
       
   507 
       
   508     pi->root = qpak_newDirectory("");
       
   509     if (pi->root == NULL)
       
   510         goto QPAK_openArchive_failed;
       
   511 
       
   512     if (qpak_populateDirectories(pi->entries, pi->totalEntries, pi->root) == 0)
       
   513         goto QPAK_openArchive_failed;
       
   514 
       
   515     retval->funcs = &__PHYSFS_DirFunctions_QPAK;
       
   516     return(retval);
       
   517 
       
   518 QPAK_openArchive_failed:
       
   519     if (retval != NULL)
       
   520     {
       
   521         if (retval->opaque != NULL)
       
   522             qpak_deletePakInfo((QPAKinfo *) retval->opaque);
       
   523 
       
   524         free(retval);
       
   525     } /* if */
       
   526 
       
   527     if (fh != NULL)
       
   528         __PHYSFS_platformClose(fh);
       
   529 
       
   530     return(0);
       
   531 } /* QPAK_openArchive */
       
   532 
       
   533 
       
   534 static void QPAK_dirClose(DirHandle *dirHandle)
       
   535 {
       
   536     QPAKinfo *info = (QPAKinfo *) dirHandle->opaque;
       
   537     __PHYSFS_platformClose(info->handle);
       
   538     free(info->filename);
       
   539     free(info);
       
   540     free(dirHandle);
       
   541 } /* QPAK_dirClose */
       
   542 
       
   543 
       
   544 static LinkedStringList *QPAK_enumerateFiles(DirHandle *h, const char *dirname,
       
   545                                              int omitSymLinks)
       
   546 {
       
   547     LinkedStringList *retval = NULL, *p = NULL;
       
   548     QPAKdirectory *dir;
       
   549     QPAKinfo *info = (QPAKinfo *) h->opaque;
       
   550 
       
   551     if ((dirname == NULL) || (*dirname == '\0'))
       
   552         dir = info->root;
       
   553     else
       
   554         dir = qpak_findDirectory(info->root, dirname);
       
   555 
       
   556     if (dir != NULL)
       
   557     {
       
   558         QPAKdirectory *child = dir->dirs;
       
   559         QPAKdirentry *file = dir->files;
       
   560 
       
   561         while (child != NULL)
       
   562         {
       
   563             retval = __PHYSFS_addToLinkedStringList(retval, &p, child->name, -1);
       
   564             child = child->next;
       
   565         } /* while */
       
   566 
       
   567         while (file != NULL)
       
   568         {
       
   569             retval = __PHYSFS_addToLinkedStringList(retval, &p, file->name, -1);
       
   570             file = file->next;
       
   571         } /* while */
       
   572     } /* if */
       
   573 
       
   574     return(retval);
       
   575 } /* QPAK_enumerateFiles */
       
   576 
       
   577 
       
   578 static int QPAK_exists(DirHandle *h, const char *name)
       
   579 {
       
   580     QPAKinfo *driver = (QPAKinfo *) h->opaque;
       
   581 
       
   582     if ((name == NULL) || (*name == '\0'))
       
   583         return(0);
       
   584     
       
   585     if (qpak_findDirectory(driver->root, name) != 0)
       
   586         return(1);
       
   587 
       
   588     if (qpak_findEntry(driver->root, name) != 0)
       
   589         return(1);
       
   590 
       
   591     return(0);
       
   592 } /* QPAK_exists */
       
   593 
       
   594 
       
   595 static int QPAK_isDirectory(DirHandle *h, const char *name)
       
   596 {
       
   597     QPAKinfo *info = (QPAKinfo *) h->opaque;
       
   598     return(qpak_findDirectory(info->root, name) != 0);
       
   599 } /* QPAK_isDirectory */
       
   600 
       
   601 
       
   602 static int QPAK_isSymLink(DirHandle *h, const char *name)
       
   603 {
       
   604     return(0); /* we don't support symlinks for now */
       
   605 } /* QPAK_isSymlink */
       
   606 
       
   607 
       
   608 static PHYSFS_sint64 QPAK_getLastModTime(DirHandle *h, const char *name)
       
   609 {
       
   610     return(__PHYSFS_platformGetLastModTime(((QPAKinfo *) h->opaque)->filename));
       
   611 } /* QPAK_getLastModTime */
       
   612 
       
   613 
       
   614 static void *qpak_getFileHandle(const char *name, QPAKentry *entry)
       
   615 {
       
   616     void *retval = __PHYSFS_platformOpenRead(name);
       
   617     if (retval == NULL)
       
   618         return(NULL);
       
   619 
       
   620     if (!__PHYSFS_platformSeek(retval, entry->offset))
       
   621     {
       
   622         __PHYSFS_platformClose(retval);
       
   623         return(NULL);
       
   624     } /* if */
       
   625 
       
   626     return(retval);
       
   627 } /* qpak_getFileHandle */
       
   628 
       
   629 
       
   630 static FileHandle *QPAK_openRead(DirHandle *h, const char *name)
       
   631 {
       
   632     QPAKinfo *driver = (QPAKinfo *) h->opaque;
       
   633     QPAKentry *entry = qpak_findEntry(driver->root, name);
       
   634     QPAKfileinfo *fileDriver = 0;
       
   635     FileHandle *result = 0;
       
   636 
       
   637     if (entry == NULL)
       
   638         return(NULL);
       
   639  
       
   640     fileDriver = (QPAKfileinfo *) malloc(sizeof (QPAKfileinfo));
       
   641     BAIL_IF_MACRO(fileDriver == NULL, ERR_OUT_OF_MEMORY, NULL);
       
   642     
       
   643     fileDriver->handle = qpak_getFileHandle(driver->filename, entry);
       
   644     if (fileDriver->handle == NULL)
       
   645     {
       
   646         free(fileDriver);
       
   647         return(NULL);
       
   648     } /* if */
       
   649 
       
   650     fileDriver->entry = entry;
       
   651     fileDriver->curPos = 0;
       
   652 
       
   653     result = (FileHandle *)malloc(sizeof (FileHandle));
       
   654     if (result == NULL)
       
   655     {
       
   656         __PHYSFS_platformClose(fileDriver->handle);
       
   657         free(fileDriver);
       
   658         BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
       
   659     } /* if */
       
   660 
       
   661     result->opaque = fileDriver;
       
   662     result->dirHandle = h;
       
   663     result->funcs = &__PHYSFS_FileFunctions_QPAK;
       
   664     return(result);
       
   665 } /* QPAK_openRead */
       
   666 
       
   667 
       
   668 static PHYSFS_sint64 QPAK_read(FileHandle *handle, void *buffer,
       
   669                                PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
       
   670 {
       
   671     QPAKfileinfo *finfo = (QPAKfileinfo *) (handle->opaque);
       
   672     QPAKentry *entry = finfo->entry;
       
   673     PHYSFS_uint64 bytesLeft = entry->size - finfo->curPos;
       
   674     PHYSFS_uint64 objsLeft = (bytesLeft / objSize);
       
   675     PHYSFS_sint64 rc;
       
   676 
       
   677     if (objsLeft < objCount)
       
   678         objCount = (PHYSFS_uint32) objsLeft;
       
   679 
       
   680     rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
       
   681     if (rc > 0)
       
   682         finfo->curPos += (rc * objSize);
       
   683 
       
   684     return(rc);
       
   685 } /* QPAK_read */
       
   686 
       
   687 
       
   688 static int QPAK_eof(FileHandle *handle)
       
   689 {
       
   690     QPAKfileinfo *finfo = (QPAKfileinfo *) (handle->opaque);
       
   691     QPAKentry *entry = finfo->entry;
       
   692     
       
   693     return(finfo->curPos >= (PHYSFS_sint64) entry->size);
       
   694 } /* QPAK_eof */
       
   695 
       
   696 
       
   697 static PHYSFS_sint64 QPAK_tell(FileHandle *handle)
       
   698 {
       
   699     return(((QPAKfileinfo *) handle->opaque)->curPos);
       
   700 } /* QPAK_tell */
       
   701 
       
   702 
       
   703 static int QPAK_seek(FileHandle *handle, PHYSFS_uint64 offset)
       
   704 {
       
   705     QPAKfileinfo *finfo = (QPAKfileinfo *) handle->opaque;
       
   706     QPAKentry *entry = finfo->entry;
       
   707     PHYSFS_uint64 newPos = entry->offset + offset;
       
   708     int rc;
       
   709 
       
   710     BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
       
   711     BAIL_IF_MACRO(newPos > entry->offset + entry->size, ERR_PAST_EOF, 0);
       
   712     rc = __PHYSFS_platformSeek(finfo->handle, newPos);
       
   713     if (rc)
       
   714         finfo->curPos = offset;
       
   715 
       
   716     return(rc);
       
   717 }  /* QPAK_seek */
       
   718 
       
   719 
       
   720 static PHYSFS_sint64 QPAK_fileLength(FileHandle *handle)
       
   721 {
       
   722     return ((QPAKfileinfo *) handle->opaque)->entry->size;
       
   723 } /* QPAK_fileLength */
       
   724 
       
   725 
       
   726 static int QPAK_fileClose(FileHandle *handle)
       
   727 {
       
   728     QPAKfileinfo *finfo = (QPAKfileinfo *) handle->opaque;
       
   729     BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
       
   730     free(finfo);
       
   731     free(handle);
       
   732     return(1);
       
   733 } /* QPAK_fileClose */
       
   734 
       
   735 #endif  /* defined PHYSFS_SUPPORTS_QPAK */
       
   736 
       
   737 /* end of qpak.c ... */
       
   738 
       
   739