archivers/mvl.c
changeset 553 4338d9c0bbcd
child 564 ce114ffdf485
equal deleted inserted replaced
552:24e99a1cbb98 553:4338d9c0bbcd
       
     1 /*
       
     2  * MVL support routines for PhysicsFS.
       
     3  *
       
     4  * This driver handles Descent II Movielib archives.
       
     5  *
       
     6  * The file format of MVL is quite easy...
       
     7  *
       
     8  *   //MVL File format - Written by Heiko Herrmann
       
     9  *   char sig[4] = {'D','M', 'V', 'L'}; // "DMVL"=Descent MoVie Library
       
    10  *
       
    11  *   int num_files; // the number of files in this MVL
       
    12  *
       
    13  *   struct {
       
    14  *    char file_name[13]; // Filename, padded to 13 bytes with 0s
       
    15  *    int file_size; // filesize in bytes
       
    16  *   }DIR_STRUCT[num_files];
       
    17  *
       
    18  *   struct {
       
    19  *    char data[file_size]; // The file data
       
    20  *   }FILE_STRUCT[num_files];
       
    21  *
       
    22  * (That info is from http://www.descent2.com/ddn/specs/mvl/)
       
    23  *
       
    24  * Please see the file LICENSE in the source's root directory.
       
    25  *
       
    26  *  This file written by Bradley Bell.
       
    27  *  Based on grp.c by Ryan C. Gordon.
       
    28  */
       
    29 
       
    30 #if HAVE_CONFIG_H
       
    31 #  include <config.h>
       
    32 #endif
       
    33 
       
    34 #if (defined PHYSFS_SUPPORTS_MVL)
       
    35 
       
    36 #include <stdio.h>
       
    37 #include <stdlib.h>
       
    38 #include <string.h>
       
    39 #include "physfs.h"
       
    40 
       
    41 #define __PHYSICSFS_INTERNAL__
       
    42 #include "physfs_internal.h"
       
    43 
       
    44 typedef struct
       
    45 {
       
    46     char name[13];
       
    47     PHYSFS_uint32 startPos;
       
    48     PHYSFS_uint32 size;
       
    49 } MVLentry;
       
    50 
       
    51 typedef struct
       
    52 {
       
    53     char *filename;
       
    54     PHYSFS_sint64 last_mod_time;
       
    55     PHYSFS_uint32 entryCount;
       
    56     MVLentry *entries;
       
    57 } MVLinfo;
       
    58 
       
    59 typedef struct
       
    60 {
       
    61     void *handle;
       
    62     MVLentry *entry;
       
    63     PHYSFS_uint32 curPos;
       
    64 } MVLfileinfo;
       
    65 
       
    66 
       
    67 static void MVL_dirClose(DirHandle *h);
       
    68 static PHYSFS_sint64 MVL_read(FileHandle *handle, void *buffer,
       
    69                               PHYSFS_uint32 objSize, PHYSFS_uint32 objCount);
       
    70 static PHYSFS_sint64 MVL_write(FileHandle *handle, const void *buffer,
       
    71                                PHYSFS_uint32 objSize, PHYSFS_uint32 objCount);
       
    72 static int MVL_eof(FileHandle *handle);
       
    73 static PHYSFS_sint64 MVL_tell(FileHandle *handle);
       
    74 static int MVL_seek(FileHandle *handle, PHYSFS_uint64 offset);
       
    75 static PHYSFS_sint64 MVL_fileLength(FileHandle *handle);
       
    76 static int MVL_fileClose(FileHandle *handle);
       
    77 static int MVL_isArchive(const char *filename, int forWriting);
       
    78 static DirHandle *MVL_openArchive(const char *name, int forWriting);
       
    79 static LinkedStringList *MVL_enumerateFiles(DirHandle *h,
       
    80                                             const char *dirname,
       
    81                                             int omitSymLinks);
       
    82 static int MVL_exists(DirHandle *h, const char *name);
       
    83 static int MVL_isDirectory(DirHandle *h, const char *name, int *fileExists);
       
    84 static int MVL_isSymLink(DirHandle *h, const char *name, int *fileExists);
       
    85 static PHYSFS_sint64 MVL_getLastModTime(DirHandle *h, const char *n, int *e);
       
    86 static FileHandle *MVL_openRead(DirHandle *h, const char *name, int *exist);
       
    87 static FileHandle *MVL_openWrite(DirHandle *h, const char *name);
       
    88 static FileHandle *MVL_openAppend(DirHandle *h, const char *name);
       
    89 static int MVL_remove(DirHandle *h, const char *name);
       
    90 static int MVL_mkdir(DirHandle *h, const char *name);
       
    91 
       
    92 const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL =
       
    93 {
       
    94     "MVL",
       
    95     MVL_ARCHIVE_DESCRIPTION,
       
    96     "Bradley Bell <btb@icculus.org>",
       
    97     "http://icculus.org/physfs/",
       
    98 };
       
    99 
       
   100 
       
   101 static const FileFunctions __PHYSFS_FileFunctions_MVL =
       
   102 {
       
   103     MVL_read,       /* read() method       */
       
   104     MVL_write,      /* write() method      */
       
   105     MVL_eof,        /* eof() method        */
       
   106     MVL_tell,       /* tell() method       */
       
   107     MVL_seek,       /* seek() method       */
       
   108     MVL_fileLength, /* fileLength() method */
       
   109     MVL_fileClose   /* fileClose() method  */
       
   110 };
       
   111 
       
   112 
       
   113 const DirFunctions __PHYSFS_DirFunctions_MVL =
       
   114 {
       
   115     &__PHYSFS_ArchiveInfo_MVL,
       
   116     MVL_isArchive,          /* isArchive() method      */
       
   117     MVL_openArchive,        /* openArchive() method    */
       
   118     MVL_enumerateFiles,     /* enumerateFiles() method */
       
   119     MVL_exists,             /* exists() method         */
       
   120     MVL_isDirectory,        /* isDirectory() method    */
       
   121     MVL_isSymLink,          /* isSymLink() method      */
       
   122     MVL_getLastModTime,     /* getLastModTime() method */
       
   123     MVL_openRead,           /* openRead() method       */
       
   124     MVL_openWrite,          /* openWrite() method      */
       
   125     MVL_openAppend,         /* openAppend() method     */
       
   126     MVL_remove,             /* remove() method         */
       
   127     MVL_mkdir,              /* mkdir() method          */
       
   128     MVL_dirClose            /* dirClose() method       */
       
   129 };
       
   130 
       
   131 
       
   132 
       
   133 static void MVL_dirClose(DirHandle *h)
       
   134 {
       
   135     MVLinfo *info = ((MVLinfo *) h->opaque);
       
   136     free(info->filename);
       
   137     free(info->entries);
       
   138     free(info);
       
   139     free(h);
       
   140 } /* MVL_dirClose */
       
   141 
       
   142 
       
   143 static PHYSFS_sint64 MVL_read(FileHandle *handle, void *buffer,
       
   144                               PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
       
   145 {
       
   146     MVLfileinfo *finfo = (MVLfileinfo *) (handle->opaque);
       
   147     MVLentry *entry = finfo->entry;
       
   148     PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
       
   149     PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
       
   150     PHYSFS_sint64 rc;
       
   151 
       
   152     if (objsLeft < objCount)
       
   153         objCount = objsLeft;
       
   154 
       
   155     rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
       
   156     if (rc > 0)
       
   157         finfo->curPos += (PHYSFS_uint32) (rc * objSize);
       
   158 
       
   159     return(rc);
       
   160 } /* MVL_read */
       
   161 
       
   162 
       
   163 static PHYSFS_sint64 MVL_write(FileHandle *handle, const void *buffer,
       
   164                                PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
       
   165 {
       
   166     BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
       
   167 } /* MVL_write */
       
   168 
       
   169 
       
   170 static int MVL_eof(FileHandle *handle)
       
   171 {
       
   172     MVLfileinfo *finfo = (MVLfileinfo *) (handle->opaque);
       
   173     MVLentry *entry = finfo->entry;
       
   174     return(finfo->curPos >= entry->size);
       
   175 } /* MVL_eof */
       
   176 
       
   177 
       
   178 static PHYSFS_sint64 MVL_tell(FileHandle *handle)
       
   179 {
       
   180     return(((MVLfileinfo *) (handle->opaque))->curPos);
       
   181 } /* MVL_tell */
       
   182 
       
   183 
       
   184 static int MVL_seek(FileHandle *handle, PHYSFS_uint64 offset)
       
   185 {
       
   186     MVLfileinfo *finfo = (MVLfileinfo *) (handle->opaque);
       
   187     MVLentry *entry = finfo->entry;
       
   188     int rc;
       
   189 
       
   190     BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
       
   191     BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
       
   192     rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
       
   193     if (rc)
       
   194         finfo->curPos = (PHYSFS_uint32) offset;
       
   195 
       
   196     return(rc);
       
   197 } /* MVL_seek */
       
   198 
       
   199 
       
   200 static PHYSFS_sint64 MVL_fileLength(FileHandle *handle)
       
   201 {
       
   202     MVLfileinfo *finfo = ((MVLfileinfo *) handle->opaque);
       
   203     return((PHYSFS_sint64) finfo->entry->size);
       
   204 } /* MVL_fileLength */
       
   205 
       
   206 
       
   207 static int MVL_fileClose(FileHandle *handle)
       
   208 {
       
   209     MVLfileinfo *finfo = ((MVLfileinfo *) handle->opaque);
       
   210     BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
       
   211     free(finfo);
       
   212     free(handle);
       
   213     return(1);
       
   214 } /* MVL_fileClose */
       
   215 
       
   216 
       
   217 static int mvl_open(const char *filename, int forWriting,
       
   218                     void **fh, PHYSFS_uint32 *count)
       
   219 {
       
   220     PHYSFS_uint8 buf[4];
       
   221 
       
   222     *fh = NULL;
       
   223     BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
       
   224 
       
   225     *fh = __PHYSFS_platformOpenRead(filename);
       
   226     BAIL_IF_MACRO(*fh == NULL, NULL, 0);
       
   227     
       
   228     if (__PHYSFS_platformRead(*fh, buf, 4, 1) != 1)
       
   229         goto openMvl_failed;
       
   230 
       
   231     if (memcmp(buf, "DMVL", 4) != 0)
       
   232     {
       
   233         __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
       
   234         goto openMvl_failed;
       
   235     } /* if */
       
   236 
       
   237     if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
       
   238         goto openMvl_failed;
       
   239 
       
   240     *count = PHYSFS_swapULE32(*count);
       
   241 
       
   242     return(1);
       
   243 
       
   244 openMvl_failed:
       
   245     if (*fh != NULL)
       
   246         __PHYSFS_platformClose(*fh);
       
   247 
       
   248     *count = -1;
       
   249     *fh = NULL;
       
   250     return(0);
       
   251 } /* mvl_open */
       
   252 
       
   253 
       
   254 static int MVL_isArchive(const char *filename, int forWriting)
       
   255 {
       
   256     void *fh;
       
   257     PHYSFS_uint32 fileCount;
       
   258     int retval = mvl_open(filename, forWriting, &fh, &fileCount);
       
   259 
       
   260     if (fh != NULL)
       
   261         __PHYSFS_platformClose(fh);
       
   262 
       
   263     return(retval);
       
   264 } /* MVL_isArchive */
       
   265 
       
   266 
       
   267 static int mvl_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
       
   268 {
       
   269     MVLentry *a = (MVLentry *) _a;
       
   270     return(strcmp(a[one].name, a[two].name));
       
   271 } /* mvl_entry_cmp */
       
   272 
       
   273 
       
   274 static void mvl_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
       
   275 {
       
   276     MVLentry tmp;
       
   277     MVLentry *first = &(((MVLentry *) _a)[one]);
       
   278     MVLentry *second = &(((MVLentry *) _a)[two]);
       
   279     memcpy(&tmp, first, sizeof (MVLentry));
       
   280     memcpy(first, second, sizeof (MVLentry));
       
   281     memcpy(second, &tmp, sizeof (MVLentry));
       
   282 } /* mvl_entry_swap */
       
   283 
       
   284 
       
   285 static int mvl_load_entries(const char *name, int forWriting, MVLinfo *info)
       
   286 {
       
   287     void *fh = NULL;
       
   288     PHYSFS_uint32 fileCount;
       
   289     PHYSFS_uint32 location = 8;  /* sizeof sig. */
       
   290     MVLentry *entry;
       
   291     char *ptr;
       
   292 
       
   293     BAIL_IF_MACRO(!mvl_open(name, forWriting, &fh, &fileCount), NULL, 0);
       
   294     info->entryCount = fileCount;
       
   295     info->entries = (MVLentry *) malloc(sizeof (MVLentry) * fileCount);
       
   296     if (info->entries == NULL)
       
   297     {
       
   298         __PHYSFS_platformClose(fh);
       
   299         BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
       
   300     } /* if */
       
   301 
       
   302     location += (17 * fileCount);
       
   303 
       
   304     for (entry = info->entries; fileCount > 0; fileCount--, entry++)
       
   305     {
       
   306         if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1)
       
   307         {
       
   308             __PHYSFS_platformClose(fh);
       
   309             return(0);
       
   310         } /* if */
       
   311 
       
   312         if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
       
   313         {
       
   314             __PHYSFS_platformClose(fh);
       
   315             return(0);
       
   316         } /* if */
       
   317 
       
   318         entry->size = PHYSFS_swapULE32(entry->size);
       
   319         entry->startPos = location;
       
   320         location += entry->size;
       
   321     } /* for */
       
   322 
       
   323     __PHYSFS_platformClose(fh);
       
   324 
       
   325     __PHYSFS_sort(info->entries, info->entryCount,
       
   326                   mvl_entry_cmp, mvl_entry_swap);
       
   327     return(1);
       
   328 } /* mvl_load_entries */
       
   329 
       
   330 
       
   331 static DirHandle *MVL_openArchive(const char *name, int forWriting)
       
   332 {
       
   333     MVLinfo *info;
       
   334     DirHandle *retval = malloc(sizeof (DirHandle));
       
   335     PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
       
   336 
       
   337     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
       
   338     info = retval->opaque = malloc(sizeof (MVLinfo));
       
   339     if (info == NULL)
       
   340     {
       
   341         __PHYSFS_setError(ERR_OUT_OF_MEMORY);
       
   342         goto MVL_openArchive_failed;
       
   343     } /* if */
       
   344 
       
   345     memset(info, '\0', sizeof (MVLinfo));
       
   346 
       
   347     info->filename = (char *) malloc(strlen(name) + 1);
       
   348     if (info->filename == NULL)
       
   349     {
       
   350         __PHYSFS_setError(ERR_OUT_OF_MEMORY);
       
   351         goto MVL_openArchive_failed;
       
   352     } /* if */
       
   353 
       
   354     if (!mvl_load_entries(name, forWriting, info))
       
   355         goto MVL_openArchive_failed;
       
   356 
       
   357     strcpy(info->filename, name);
       
   358     info->last_mod_time = modtime;
       
   359     retval->funcs = &__PHYSFS_DirFunctions_MVL;
       
   360     return(retval);
       
   361 
       
   362 MVL_openArchive_failed:
       
   363     if (retval != NULL)
       
   364     {
       
   365         if (retval->opaque != NULL)
       
   366         {
       
   367             if (info->filename != NULL)
       
   368                 free(info->filename);
       
   369             if (info->entries != NULL)
       
   370                 free(info->entries);
       
   371             free(info);
       
   372         } /* if */
       
   373         free(retval);
       
   374     } /* if */
       
   375 
       
   376     return(NULL);
       
   377 } /* MVL_openArchive */
       
   378 
       
   379 
       
   380 static LinkedStringList *MVL_enumerateFiles(DirHandle *h,
       
   381                                             const char *dirname,
       
   382                                             int omitSymLinks)
       
   383 {
       
   384     MVLinfo *info = ((MVLinfo *) h->opaque);
       
   385     MVLentry *entry = info->entries;
       
   386     LinkedStringList *retval = NULL, *p = NULL;
       
   387     PHYSFS_uint32 max = info->entryCount;
       
   388     PHYSFS_uint32 i;
       
   389 
       
   390     /* no directories in MVL files. */
       
   391     BAIL_IF_MACRO(*dirname != '\0', ERR_NOT_A_DIR, NULL);
       
   392 
       
   393     for (i = 0; i < max; i++, entry++)
       
   394         retval = __PHYSFS_addToLinkedStringList(retval, &p, entry->name, -1);
       
   395 
       
   396     return(retval);
       
   397 } /* MVL_enumerateFiles */
       
   398 
       
   399 
       
   400 static MVLentry *mvl_find_entry(MVLinfo *info, const char *name)
       
   401 {
       
   402     char *ptr = strchr(name, '.');
       
   403     MVLentry *a = info->entries;
       
   404     PHYSFS_sint32 lo = 0;
       
   405     PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
       
   406     PHYSFS_sint32 middle;
       
   407     int rc;
       
   408 
       
   409     /*
       
   410      * Rule out filenames to avoid unneeded processing...no dirs,
       
   411      *   big filenames, or extensions > 3 chars.
       
   412      */
       
   413     BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
       
   414     BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
       
   415     BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
       
   416 
       
   417     while (lo <= hi)
       
   418     {
       
   419         middle = lo + ((hi - lo) / 2);
       
   420         rc = __PHYSFS_platformStricmp(name, a[middle].name);
       
   421         if (rc == 0)  /* found it! */
       
   422             return(&a[middle]);
       
   423         else if (rc > 0)
       
   424             lo = middle + 1;
       
   425         else
       
   426             hi = middle - 1;
       
   427     } /* while */
       
   428 
       
   429     BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
       
   430 } /* mvl_find_entry */
       
   431 
       
   432 
       
   433 static int MVL_exists(DirHandle *h, const char *name)
       
   434 {
       
   435     return(mvl_find_entry(((MVLinfo *) h->opaque), name) != NULL);
       
   436 } /* MVL_exists */
       
   437 
       
   438 
       
   439 static int MVL_isDirectory(DirHandle *h, const char *name, int *fileExists)
       
   440 {
       
   441     *fileExists = MVL_exists(h, name);
       
   442     return(0);  /* never directories in a groupfile. */
       
   443 } /* MVL_isDirectory */
       
   444 
       
   445 
       
   446 static int MVL_isSymLink(DirHandle *h, const char *name, int *fileExists)
       
   447 {
       
   448     *fileExists = MVL_exists(h, name);
       
   449     return(0);  /* never symlinks in a groupfile. */
       
   450 } /* MVL_isSymLink */
       
   451 
       
   452 
       
   453 static PHYSFS_sint64 MVL_getLastModTime(DirHandle *h,
       
   454                                         const char *name,
       
   455                                         int *fileExists)
       
   456 {
       
   457     MVLinfo *info = ((MVLinfo *) h->opaque);
       
   458     PHYSFS_sint64 retval = -1;
       
   459 
       
   460     *fileExists = (mvl_find_entry(info, name) != NULL);
       
   461     if (*fileExists)  /* use time of MVL itself in the physical filesystem. */
       
   462         retval = ((MVLinfo *) h->opaque)->last_mod_time;
       
   463 
       
   464     return(retval);
       
   465 } /* MVL_getLastModTime */
       
   466 
       
   467 
       
   468 static FileHandle *MVL_openRead(DirHandle *h, const char *fnm, int *fileExists)
       
   469 {
       
   470     MVLinfo *info = ((MVLinfo *) h->opaque);
       
   471     FileHandle *retval;
       
   472     MVLfileinfo *finfo;
       
   473     MVLentry *entry;
       
   474 
       
   475     entry = mvl_find_entry(info, fnm);
       
   476     *fileExists = (entry != NULL);
       
   477     BAIL_IF_MACRO(entry == NULL, NULL, NULL);
       
   478 
       
   479     retval = (FileHandle *) malloc(sizeof (FileHandle));
       
   480     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
       
   481     finfo = (MVLfileinfo *) malloc(sizeof (MVLfileinfo));
       
   482     if (finfo == NULL)
       
   483     {
       
   484         free(retval);
       
   485         BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
       
   486     } /* if */
       
   487 
       
   488     finfo->handle = __PHYSFS_platformOpenRead(info->filename);
       
   489     if ( (finfo->handle == NULL) ||
       
   490          (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
       
   491     {
       
   492         free(finfo);
       
   493         free(retval);
       
   494         return(NULL);
       
   495     } /* if */
       
   496 
       
   497     finfo->curPos = 0;
       
   498     finfo->entry = entry;
       
   499     retval->opaque = (void *) finfo;
       
   500     retval->funcs = &__PHYSFS_FileFunctions_MVL;
       
   501     retval->dirHandle = h;
       
   502     return(retval);
       
   503 } /* MVL_openRead */
       
   504 
       
   505 
       
   506 static FileHandle *MVL_openWrite(DirHandle *h, const char *name)
       
   507 {
       
   508     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
       
   509 } /* MVL_openWrite */
       
   510 
       
   511 
       
   512 static FileHandle *MVL_openAppend(DirHandle *h, const char *name)
       
   513 {
       
   514     BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
       
   515 } /* MVL_openAppend */
       
   516 
       
   517 
       
   518 static int MVL_remove(DirHandle *h, const char *name)
       
   519 {
       
   520     BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
       
   521 } /* MVL_remove */
       
   522 
       
   523 
       
   524 static int MVL_mkdir(DirHandle *h, const char *name)
       
   525 {
       
   526     BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
       
   527 } /* MVL_mkdir */
       
   528 
       
   529 #endif  /* defined PHYSFS_SUPPORTS_MVL */
       
   530 
       
   531 /* end of mvl.c ... */
       
   532