misc/libphysfs/src/archiver_lzma.c
branchphysfslayer
changeset 8522 1853628ae285
parent 7768 13e2037ebc79
equal deleted inserted replaced
8520:1dedcc37bfe8 8522:1853628ae285
       
     1 /*
       
     2  * LZMA support routines for PhysicsFS.
       
     3  *
       
     4  * Please see the file lzma.txt in the lzma/ directory.
       
     5  *
       
     6  *  This file was written by Dennis Schridde, with some peeking at "7zMain.c"
       
     7  *   by Igor Pavlov.
       
     8  */
       
     9 
       
    10 #define __PHYSICSFS_INTERNAL__
       
    11 #include "physfs_internal.h"
       
    12 
       
    13 #if PHYSFS_SUPPORTS_7Z
       
    14 
       
    15 #include "lzma/C/7zCrc.h"
       
    16 #include "lzma/C/Archive/7z/7zIn.h"
       
    17 #include "lzma/C/Archive/7z/7zExtract.h"
       
    18 
       
    19 
       
    20 /* 7z internal from 7zIn.c */
       
    21 extern int TestSignatureCandidate(Byte *testBytes);
       
    22 
       
    23 
       
    24 #ifdef _LZMA_IN_CB
       
    25 # define BUFFER_SIZE (1 << 12)
       
    26 #endif /* _LZMA_IN_CB */
       
    27 
       
    28 
       
    29 /*
       
    30  * Carries filestream metadata through 7z
       
    31  */
       
    32 typedef struct _FileInputStream
       
    33 {
       
    34     ISzAlloc allocImp; /* Allocation implementation, used by 7z */
       
    35     ISzAlloc allocTempImp; /* Temporary allocation implementation, used by 7z */
       
    36     ISzInStream inStream; /* Input stream with read callbacks, used by 7z */
       
    37     PHYSFS_Io *io;  /* Filehandle, used by read implementation */
       
    38 #ifdef _LZMA_IN_CB
       
    39     Byte buffer[BUFFER_SIZE]; /* Buffer, used by read implementation */
       
    40 #endif /* _LZMA_IN_CB */
       
    41 } FileInputStream;
       
    42 
       
    43 /*
       
    44  * In the 7z format archives are splited into blocks, those are called folders
       
    45  * Set by LZMA_read()
       
    46 */
       
    47 typedef struct _LZMAfolder
       
    48 {
       
    49     PHYSFS_uint32 index; /* Index of folder in archive */
       
    50     PHYSFS_uint32 references; /* Number of files using this block */
       
    51     PHYSFS_uint8 *cache; /* Cached folder */
       
    52     size_t size; /* Size of folder */
       
    53 } LZMAfolder;
       
    54 
       
    55 /*
       
    56  * Set by LZMA_openArchive(), except folder which gets it's values
       
    57  *  in LZMA_read()
       
    58  */
       
    59 typedef struct _LZMAarchive
       
    60 {
       
    61     struct _LZMAfile *files; /* Array of files, size == archive->db.Database.NumFiles */
       
    62     LZMAfolder *folders; /* Array of folders, size == archive->db.Database.NumFolders */
       
    63     CArchiveDatabaseEx db; /* For 7z: Database */
       
    64     FileInputStream stream; /* For 7z: Input file incl. read and seek callbacks */
       
    65 } LZMAarchive;
       
    66 
       
    67 /* Set by LZMA_openArchive(), except offset which is set by LZMA_read() */
       
    68 typedef struct _LZMAfile
       
    69 {
       
    70     PHYSFS_uint32 index; /* Index of file in archive */
       
    71     LZMAarchive *archive; /* Link to corresponding archive */
       
    72     LZMAfolder *folder; /* Link to corresponding folder */
       
    73     CFileItem *item; /* For 7z: File info, eg. name, size */
       
    74     size_t offset; /* Offset in folder */
       
    75     size_t position; /* Current "virtual" position in file */
       
    76 } LZMAfile;
       
    77 
       
    78 
       
    79 /* Memory management implementations to be passed to 7z */
       
    80 
       
    81 static void *SzAllocPhysicsFS(size_t size)
       
    82 {
       
    83     return ((size == 0) ? NULL : allocator.Malloc(size));
       
    84 } /* SzAllocPhysicsFS */
       
    85 
       
    86 
       
    87 static void SzFreePhysicsFS(void *address)
       
    88 {
       
    89     if (address != NULL)
       
    90         allocator.Free(address);
       
    91 } /* SzFreePhysicsFS */
       
    92 
       
    93 
       
    94 /* Filesystem implementations to be passed to 7z */
       
    95 
       
    96 #ifdef _LZMA_IN_CB
       
    97 
       
    98 /*
       
    99  * Read implementation, to be passed to 7z
       
   100  * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
       
   101  */
       
   102 SZ_RESULT SzFileReadImp(void *object, void **buffer, size_t maxReqSize,
       
   103                         size_t *processedSize)
       
   104 {
       
   105     FileInputStream *s = (FileInputStream *)(object - offsetof(FileInputStream, inStream)); /* HACK! */
       
   106     PHYSFS_sint64 processedSizeLoc = 0;
       
   107 
       
   108     if (maxReqSize > BUFFER_SIZE)
       
   109         maxReqSize = BUFFER_SIZE;
       
   110     processedSizeLoc = s->io->read(s->io, s->buffer, maxReqSize);
       
   111     *buffer = s->buffer;
       
   112     if (processedSize != NULL)
       
   113         *processedSize = (size_t) processedSizeLoc;
       
   114 
       
   115     return SZ_OK;
       
   116 } /* SzFileReadImp */
       
   117 
       
   118 #else
       
   119 
       
   120 /*
       
   121  * Read implementation, to be passed to 7z
       
   122  * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
       
   123  */
       
   124 SZ_RESULT SzFileReadImp(void *object, void *buffer, size_t size,
       
   125                         size_t *processedSize)
       
   126 {
       
   127     FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
       
   128     const size_t processedSizeLoc = s->io->read(s->io, buffer, size);
       
   129     if (processedSize != NULL)
       
   130         *processedSize = processedSizeLoc;
       
   131     return SZ_OK;
       
   132 } /* SzFileReadImp */
       
   133 
       
   134 #endif
       
   135 
       
   136 /*
       
   137  * Seek implementation, to be passed to 7z
       
   138  * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
       
   139  */
       
   140 SZ_RESULT SzFileSeekImp(void *object, CFileSize pos)
       
   141 {
       
   142     FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
       
   143     if (s->io->seek(s->io, (PHYSFS_uint64) pos))
       
   144         return SZ_OK;
       
   145     return SZE_FAIL;
       
   146 } /* SzFileSeekImp */
       
   147 
       
   148 
       
   149 /*
       
   150  * Translate Microsoft FILETIME (used by 7zip) into UNIX timestamp
       
   151  */
       
   152 static PHYSFS_sint64 lzma_filetime_to_unix_timestamp(CArchiveFileTime *ft)
       
   153 {
       
   154     /* MS counts in nanoseconds ... */
       
   155     const PHYSFS_uint64 FILETIME_NANOTICKS_PER_SECOND = __PHYSFS_UI64(10000000);
       
   156     /* MS likes to count seconds since 01.01.1601 ... */
       
   157     const PHYSFS_uint64 FILETIME_UNIX_DIFF = __PHYSFS_UI64(11644473600);
       
   158 
       
   159     PHYSFS_uint64 filetime = ft->Low | ((PHYSFS_uint64)ft->High << 32);
       
   160     return filetime/FILETIME_NANOTICKS_PER_SECOND - FILETIME_UNIX_DIFF;
       
   161 } /* lzma_filetime_to_unix_timestamp */
       
   162 
       
   163 
       
   164 /*
       
   165  * Compare a file with a given name, C89 stdlib variant
       
   166  * Used for sorting
       
   167  */
       
   168 static int lzma_file_cmp_stdlib(const void *key, const void *object)
       
   169 {
       
   170     const char *name = (const char *) key;
       
   171     LZMAfile *file = (LZMAfile *) object;
       
   172     return strcmp(name, file->item->Name);
       
   173 } /* lzma_file_cmp_posix */
       
   174 
       
   175 
       
   176 /*
       
   177  * Compare two files with each other based on the name
       
   178  * Used for sorting
       
   179  */
       
   180 static int lzma_file_cmp(void *_a, size_t one, size_t two)
       
   181 {
       
   182     LZMAfile *files = (LZMAfile *) _a;
       
   183     return strcmp(files[one].item->Name, files[two].item->Name);
       
   184 } /* lzma_file_cmp */
       
   185 
       
   186 
       
   187 /*
       
   188  * Swap two entries in the file array
       
   189  */
       
   190 static void lzma_file_swap(void *_a, size_t one, size_t two)
       
   191 {
       
   192     LZMAfile tmp;
       
   193     LZMAfile *first = &(((LZMAfile *) _a)[one]);
       
   194     LZMAfile *second = &(((LZMAfile *) _a)[two]);
       
   195     memcpy(&tmp, first, sizeof (LZMAfile));
       
   196     memcpy(first, second, sizeof (LZMAfile));
       
   197     memcpy(second, &tmp, sizeof (LZMAfile));
       
   198 } /* lzma_file_swap */
       
   199 
       
   200 
       
   201 /*
       
   202  * Find entry 'name' in 'archive'
       
   203  */
       
   204 static LZMAfile * lzma_find_file(const LZMAarchive *archive, const char *name)
       
   205 {
       
   206     LZMAfile *file = bsearch(name, archive->files, archive->db.Database.NumFiles, sizeof(*archive->files), lzma_file_cmp_stdlib); /* FIXME: Should become __PHYSFS_search!!! */
       
   207 
       
   208     BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NO_SUCH_PATH, NULL);
       
   209 
       
   210     return file;
       
   211 } /* lzma_find_file */
       
   212 
       
   213 
       
   214 /*
       
   215  * Load metadata for the file at given index
       
   216  */
       
   217 static int lzma_file_init(LZMAarchive *archive, PHYSFS_uint32 fileIndex)
       
   218 {
       
   219     LZMAfile *file = &archive->files[fileIndex];
       
   220     PHYSFS_uint32 folderIndex = archive->db.FileIndexToFolderIndexMap[fileIndex];
       
   221 
       
   222     file->index = fileIndex; /* Store index into 7z array, since we sort our own. */
       
   223     file->archive = archive;
       
   224     file->folder = (folderIndex != (PHYSFS_uint32)-1 ? &archive->folders[folderIndex] : NULL); /* Directories don't have a folder (they contain no own data...) */
       
   225     file->item = &archive->db.Database.Files[fileIndex]; /* Holds crucial data and is often referenced -> Store link */
       
   226     file->position = 0;
       
   227     file->offset = 0; /* Offset will be set by LZMA_read() */
       
   228 
       
   229     return 1;
       
   230 } /* lzma_load_file */
       
   231 
       
   232 
       
   233 /*
       
   234  * Load metadata for all files
       
   235  */
       
   236 static int lzma_files_init(LZMAarchive *archive)
       
   237 {
       
   238     PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles;
       
   239 
       
   240     for (fileIndex = 0; fileIndex < numFiles; fileIndex++ )
       
   241     {
       
   242         if (!lzma_file_init(archive, fileIndex))
       
   243         {
       
   244             return 0; /* FALSE on failure */
       
   245         }
       
   246     } /* for */
       
   247 
       
   248    __PHYSFS_sort(archive->files, (size_t) numFiles, lzma_file_cmp, lzma_file_swap);
       
   249 
       
   250     return 1;
       
   251 } /* lzma_load_files */
       
   252 
       
   253 
       
   254 /*
       
   255  * Initialise specified archive
       
   256  */
       
   257 static void lzma_archive_init(LZMAarchive *archive)
       
   258 {
       
   259     memset(archive, 0, sizeof(*archive));
       
   260 
       
   261     /* Prepare callbacks for 7z */
       
   262     archive->stream.inStream.Read = SzFileReadImp;
       
   263     archive->stream.inStream.Seek = SzFileSeekImp;
       
   264 
       
   265     archive->stream.allocImp.Alloc = SzAllocPhysicsFS;
       
   266     archive->stream.allocImp.Free = SzFreePhysicsFS;
       
   267 
       
   268     archive->stream.allocTempImp.Alloc = SzAllocPhysicsFS;
       
   269     archive->stream.allocTempImp.Free = SzFreePhysicsFS;
       
   270 }
       
   271 
       
   272 
       
   273 /*
       
   274  * Deinitialise archive
       
   275  */
       
   276 static void lzma_archive_exit(LZMAarchive *archive)
       
   277 {
       
   278     /* Free arrays */
       
   279     allocator.Free(archive->folders);
       
   280     allocator.Free(archive->files);
       
   281     allocator.Free(archive);
       
   282 }
       
   283 
       
   284 /*
       
   285  * Wrap all 7z calls in this, so the physfs error state is set appropriately.
       
   286  */
       
   287 static int lzma_err(SZ_RESULT rc)
       
   288 {
       
   289     switch (rc)
       
   290     {
       
   291         case SZ_OK: /* Same as LZMA_RESULT_OK */
       
   292             break;
       
   293         case SZE_DATA_ERROR: /* Same as LZMA_RESULT_DATA_ERROR */
       
   294             __PHYSFS_setError(PHYSFS_ERR_CORRUPT); /*!!!FIXME: was "PHYSFS_ERR_DATA_ERROR" */
       
   295             break;
       
   296         case SZE_OUTOFMEMORY:
       
   297             __PHYSFS_setError(PHYSFS_ERR_OUT_OF_MEMORY);
       
   298             break;
       
   299         case SZE_CRC_ERROR:
       
   300             __PHYSFS_setError(PHYSFS_ERR_CORRUPT);
       
   301             break;
       
   302         case SZE_NOTIMPL:
       
   303             __PHYSFS_setError(PHYSFS_ERR_UNSUPPORTED);
       
   304             break;
       
   305         case SZE_FAIL:
       
   306             __PHYSFS_setError(PHYSFS_ERR_OTHER_ERROR);  /* !!! FIXME: right? */
       
   307             break;
       
   308         case SZE_ARCHIVE_ERROR:
       
   309             __PHYSFS_setError(PHYSFS_ERR_CORRUPT);  /* !!! FIXME: right? */
       
   310             break;
       
   311         default:
       
   312             __PHYSFS_setError(PHYSFS_ERR_OTHER_ERROR);
       
   313     } /* switch */
       
   314 
       
   315     return rc;
       
   316 } /* lzma_err */
       
   317 
       
   318 
       
   319 static PHYSFS_sint64 LZMA_read(PHYSFS_Io *io, void *outBuf, PHYSFS_uint64 len)
       
   320 {
       
   321     LZMAfile *file = (LZMAfile *) io->opaque;
       
   322 
       
   323     size_t wantedSize = (size_t) len;
       
   324     const size_t remainingSize = file->item->Size - file->position;
       
   325     size_t fileSize = 0;
       
   326 
       
   327     BAIL_IF_MACRO(wantedSize == 0, ERRPASS, 0); /* quick rejection. */
       
   328     BAIL_IF_MACRO(remainingSize == 0, PHYSFS_ERR_PAST_EOF, 0);
       
   329 
       
   330     if (wantedSize > remainingSize)
       
   331         wantedSize = remainingSize;
       
   332 
       
   333     /* Only decompress the folder if it is not already cached */
       
   334     if (file->folder->cache == NULL)
       
   335     {
       
   336         const int rc = lzma_err(SzExtract(
       
   337             &file->archive->stream.inStream, /* compressed data */
       
   338             &file->archive->db, /* 7z's database, containing everything */
       
   339             file->index, /* Index into database arrays */
       
   340             /* Index of cached folder, will be changed by SzExtract */
       
   341             &file->folder->index,
       
   342             /* Cache for decompressed folder, allocated/freed by SzExtract */
       
   343             &file->folder->cache,
       
   344             /* Size of cache, will be changed by SzExtract */
       
   345             &file->folder->size,
       
   346             /* Offset of this file inside the cache, set by SzExtract */
       
   347             &file->offset,
       
   348             &fileSize, /* Size of this file */
       
   349             &file->archive->stream.allocImp,
       
   350             &file->archive->stream.allocTempImp));
       
   351 
       
   352         if (rc != SZ_OK)
       
   353             return -1;
       
   354     } /* if */
       
   355 
       
   356     /* Copy wanted bytes over from cache to outBuf */
       
   357     memcpy(outBuf, (file->folder->cache + file->offset + file->position),
       
   358             wantedSize);
       
   359     file->position += wantedSize; /* Increase virtual position */
       
   360 
       
   361     return wantedSize;
       
   362 } /* LZMA_read */
       
   363 
       
   364 
       
   365 static PHYSFS_sint64 LZMA_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
       
   366 {
       
   367     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, -1);
       
   368 } /* LZMA_write */
       
   369 
       
   370 
       
   371 static PHYSFS_sint64 LZMA_tell(PHYSFS_Io *io)
       
   372 {
       
   373     LZMAfile *file = (LZMAfile *) io->opaque;
       
   374     return file->position;
       
   375 } /* LZMA_tell */
       
   376 
       
   377 
       
   378 static int LZMA_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
       
   379 {
       
   380     LZMAfile *file = (LZMAfile *) io->opaque;
       
   381 
       
   382     BAIL_IF_MACRO(offset > file->item->Size, PHYSFS_ERR_PAST_EOF, 0);
       
   383 
       
   384     file->position = offset; /* We only use a virtual position... */
       
   385 
       
   386     return 1;
       
   387 } /* LZMA_seek */
       
   388 
       
   389 
       
   390 static PHYSFS_sint64 LZMA_length(PHYSFS_Io *io)
       
   391 {
       
   392     const LZMAfile *file = (LZMAfile *) io->opaque;
       
   393     return (file->item->Size);
       
   394 } /* LZMA_length */
       
   395 
       
   396 
       
   397 static PHYSFS_Io *LZMA_duplicate(PHYSFS_Io *_io)
       
   398 {
       
   399     /* !!! FIXME: this archiver needs to be reworked to allow multiple
       
   400      * !!! FIXME:  opens before we worry about duplication. */
       
   401     BAIL_MACRO(PHYSFS_ERR_UNSUPPORTED, NULL);
       
   402 } /* LZMA_duplicate */
       
   403 
       
   404 
       
   405 static int LZMA_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
       
   406 
       
   407 
       
   408 static void LZMA_destroy(PHYSFS_Io *io)
       
   409 {
       
   410     LZMAfile *file = (LZMAfile *) io->opaque;
       
   411 
       
   412     if (file->folder != NULL)
       
   413     {
       
   414         /* Only decrease refcount if someone actually requested this file... Prevents from overflows and close-on-open... */
       
   415         if (file->folder->references > 0)
       
   416             file->folder->references--;
       
   417         if (file->folder->references == 0)
       
   418         {
       
   419             /* Free the cache which might have been allocated by LZMA_read() */
       
   420             allocator.Free(file->folder->cache);
       
   421             file->folder->cache = NULL;
       
   422         }
       
   423         /* !!! FIXME: we don't free (file) or (file->folder)?! */
       
   424     } /* if */
       
   425 } /* LZMA_destroy */
       
   426 
       
   427 
       
   428 static const PHYSFS_Io LZMA_Io =
       
   429 {
       
   430     CURRENT_PHYSFS_IO_API_VERSION, NULL,
       
   431     LZMA_read,
       
   432     LZMA_write,
       
   433     LZMA_seek,
       
   434     LZMA_tell,
       
   435     LZMA_length,
       
   436     LZMA_duplicate,
       
   437     LZMA_flush,
       
   438     LZMA_destroy
       
   439 };
       
   440 
       
   441 
       
   442 static void *LZMA_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
       
   443 {
       
   444     PHYSFS_uint8 sig[k7zSignatureSize];
       
   445     size_t len = 0;
       
   446     LZMAarchive *archive = NULL;
       
   447 
       
   448     assert(io != NULL);  /* shouldn't ever happen. */
       
   449 
       
   450     BAIL_IF_MACRO(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
       
   451 
       
   452     if (io->read(io, sig, k7zSignatureSize) != k7zSignatureSize)
       
   453         return 0;
       
   454     BAIL_IF_MACRO(!TestSignatureCandidate(sig), PHYSFS_ERR_UNSUPPORTED, NULL);
       
   455     BAIL_IF_MACRO(!io->seek(io, 0), ERRPASS, NULL);
       
   456 
       
   457     archive = (LZMAarchive *) allocator.Malloc(sizeof (LZMAarchive));
       
   458     BAIL_IF_MACRO(archive == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   459 
       
   460     lzma_archive_init(archive);
       
   461     archive->stream.io = io;
       
   462 
       
   463     CrcGenerateTable();
       
   464     SzArDbExInit(&archive->db);
       
   465     if (lzma_err(SzArchiveOpen(&archive->stream.inStream,
       
   466                                &archive->db,
       
   467                                &archive->stream.allocImp,
       
   468                                &archive->stream.allocTempImp)) != SZ_OK)
       
   469     {
       
   470         SzArDbExFree(&archive->db, SzFreePhysicsFS);
       
   471         lzma_archive_exit(archive);
       
   472         return NULL; /* Error is set by lzma_err! */
       
   473     } /* if */
       
   474 
       
   475     len = archive->db.Database.NumFiles * sizeof (LZMAfile);
       
   476     archive->files = (LZMAfile *) allocator.Malloc(len);
       
   477     if (archive->files == NULL)
       
   478     {
       
   479         SzArDbExFree(&archive->db, SzFreePhysicsFS);
       
   480         lzma_archive_exit(archive);
       
   481         BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   482     }
       
   483 
       
   484     /*
       
   485      * Init with 0 so we know when a folder is already cached
       
   486      * Values will be set by LZMA_openRead()
       
   487      */
       
   488     memset(archive->files, 0, len);
       
   489 
       
   490     len = archive->db.Database.NumFolders * sizeof (LZMAfolder);
       
   491     archive->folders = (LZMAfolder *) allocator.Malloc(len);
       
   492     if (archive->folders == NULL)
       
   493     {
       
   494         SzArDbExFree(&archive->db, SzFreePhysicsFS);
       
   495         lzma_archive_exit(archive);
       
   496         BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   497     }
       
   498 
       
   499     /*
       
   500      * Init with 0 so we know when a folder is already cached
       
   501      * Values will be set by LZMA_read()
       
   502      */
       
   503     memset(archive->folders, 0, len);
       
   504 
       
   505     if(!lzma_files_init(archive))
       
   506     {
       
   507         SzArDbExFree(&archive->db, SzFreePhysicsFS);
       
   508         lzma_archive_exit(archive);
       
   509         BAIL_MACRO(PHYSFS_ERR_OTHER_ERROR, NULL);
       
   510     }
       
   511 
       
   512     return archive;
       
   513 } /* LZMA_openArchive */
       
   514 
       
   515 
       
   516 /*
       
   517  * Moved to seperate function so we can use alloca then immediately throw
       
   518  *  away the allocated stack space...
       
   519  */
       
   520 static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
       
   521                            const char *odir, const char *str, size_t flen)
       
   522 {
       
   523     char *newstr = __PHYSFS_smallAlloc(flen + 1);
       
   524     if (newstr == NULL)
       
   525         return;
       
   526 
       
   527     memcpy(newstr, str, flen);
       
   528     newstr[flen] = '\0';
       
   529     cb(callbackdata, odir, newstr);
       
   530     __PHYSFS_smallFree(newstr);
       
   531 } /* doEnumCallback */
       
   532 
       
   533 
       
   534 static void LZMA_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
       
   535                                 int omitSymLinks, PHYSFS_EnumFilesCallback cb,
       
   536                                 const char *origdir, void *callbackdata)
       
   537 {
       
   538     size_t dlen = strlen(dname),
       
   539            dlen_inc = dlen + ((dlen > 0) ? 1 : 0);
       
   540     LZMAarchive *archive = (LZMAarchive *) opaque;
       
   541     LZMAfile *file = NULL,
       
   542             *lastFile = &archive->files[archive->db.Database.NumFiles];
       
   543         if (dlen)
       
   544         {
       
   545             file = lzma_find_file(archive, dname);
       
   546             if (file != NULL) /* if 'file' is NULL it should stay so, otherwise errors will not be handled */
       
   547                 file += 1;
       
   548         }
       
   549         else
       
   550         {
       
   551             file = archive->files;
       
   552         }
       
   553 
       
   554     BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NO_SUCH_PATH, );
       
   555 
       
   556     while (file < lastFile)
       
   557     {
       
   558         const char * fname = file->item->Name;
       
   559         const char * dirNameEnd = fname + dlen_inc;
       
   560 
       
   561         if (strncmp(dname, fname, dlen) != 0) /* Stop after mismatch, archive->files is sorted */
       
   562             break;
       
   563 
       
   564         if (strchr(dirNameEnd, '/')) /* Skip subdirs */
       
   565         {
       
   566             file++;
       
   567             continue;
       
   568         }
       
   569 
       
   570         /* Do the actual callback... */
       
   571         doEnumCallback(cb, callbackdata, origdir, dirNameEnd, strlen(dirNameEnd));
       
   572 
       
   573         file++;
       
   574     }
       
   575 } /* LZMA_enumerateFiles */
       
   576 
       
   577 
       
   578 static PHYSFS_Io *LZMA_openRead(PHYSFS_Dir *opaque, const char *name,
       
   579                                 int *fileExists)
       
   580 {
       
   581     LZMAarchive *archive = (LZMAarchive *) opaque;
       
   582     LZMAfile *file = lzma_find_file(archive, name);
       
   583     PHYSFS_Io *io = NULL;
       
   584 
       
   585     *fileExists = (file != NULL);
       
   586     BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NO_SUCH_PATH, NULL);
       
   587     BAIL_IF_MACRO(file->folder == NULL, PHYSFS_ERR_NOT_A_FILE, NULL);
       
   588 
       
   589     file->position = 0;
       
   590     file->folder->references++; /* Increase refcount for automatic cleanup... */
       
   591 
       
   592     io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
       
   593     BAIL_IF_MACRO(io == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   594     memcpy(io, &LZMA_Io, sizeof (*io));
       
   595     io->opaque = file;
       
   596 
       
   597     return io;
       
   598 } /* LZMA_openRead */
       
   599 
       
   600 
       
   601 static PHYSFS_Io *LZMA_openWrite(PHYSFS_Dir *opaque, const char *filename)
       
   602 {
       
   603     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
       
   604 } /* LZMA_openWrite */
       
   605 
       
   606 
       
   607 static PHYSFS_Io *LZMA_openAppend(PHYSFS_Dir *opaque, const char *filename)
       
   608 {
       
   609     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
       
   610 } /* LZMA_openAppend */
       
   611 
       
   612 
       
   613 static void LZMA_closeArchive(PHYSFS_Dir *opaque)
       
   614 {
       
   615     LZMAarchive *archive = (LZMAarchive *) opaque;
       
   616 
       
   617 #if 0  /* !!! FIXME: you shouldn't have to do this. */
       
   618     PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles;
       
   619     for (fileIndex = 0; fileIndex < numFiles; fileIndex++)
       
   620     {
       
   621         LZMA_fileClose(&archive->files[fileIndex]);
       
   622     } /* for */
       
   623 #endif
       
   624 
       
   625     SzArDbExFree(&archive->db, SzFreePhysicsFS);
       
   626     archive->stream.io->destroy(archive->stream.io);
       
   627     lzma_archive_exit(archive);
       
   628 } /* LZMA_closeArchive */
       
   629 
       
   630 
       
   631 static int LZMA_remove(PHYSFS_Dir *opaque, const char *name)
       
   632 {
       
   633     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
       
   634 } /* LZMA_remove */
       
   635 
       
   636 
       
   637 static int LZMA_mkdir(PHYSFS_Dir *opaque, const char *name)
       
   638 {
       
   639     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
       
   640 } /* LZMA_mkdir */
       
   641 
       
   642 static int LZMA_stat(PHYSFS_Dir *opaque, const char *filename,
       
   643                      int *exists, PHYSFS_Stat *stat)
       
   644 {
       
   645     const LZMAarchive *archive = (const LZMAarchive *) opaque;
       
   646     const LZMAfile *file = lzma_find_file(archive, filename);
       
   647 
       
   648     *exists = (file != 0);
       
   649     if (!file)
       
   650         return 0;
       
   651 
       
   652     if(file->item->IsDirectory)
       
   653     {
       
   654         stat->filesize = 0;
       
   655         stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
       
   656     } /* if */
       
   657     else
       
   658     {
       
   659         stat->filesize = (PHYSFS_sint64) file->item->Size;
       
   660         stat->filetype = PHYSFS_FILETYPE_REGULAR;
       
   661     } /* else */
       
   662 
       
   663     /* !!! FIXME: the 0's should be -1's? */
       
   664     if (file->item->IsLastWriteTimeDefined)
       
   665         stat->modtime = lzma_filetime_to_unix_timestamp(&file->item->LastWriteTime);
       
   666     else
       
   667         stat->modtime = 0;
       
   668 
       
   669     /* real create and accesstype are currently not in the lzma SDK */
       
   670     stat->createtime = stat->modtime;
       
   671     stat->accesstime = 0;
       
   672 
       
   673     stat->readonly = 1;  /* 7zips are always read only */
       
   674 
       
   675     return 1;
       
   676 } /* LZMA_stat */
       
   677 
       
   678 
       
   679 const PHYSFS_Archiver __PHYSFS_Archiver_LZMA =
       
   680 {
       
   681     {
       
   682         "7Z",
       
   683         "LZMA (7zip) format",
       
   684         "Dennis Schridde <devurandom@gmx.net>",
       
   685         "http://icculus.org/physfs/",
       
   686     },
       
   687     LZMA_openArchive,        /* openArchive() method    */
       
   688     LZMA_enumerateFiles,     /* enumerateFiles() method */
       
   689     LZMA_openRead,           /* openRead() method       */
       
   690     LZMA_openWrite,          /* openWrite() method      */
       
   691     LZMA_openAppend,         /* openAppend() method     */
       
   692     LZMA_remove,             /* remove() method         */
       
   693     LZMA_mkdir,              /* mkdir() method          */
       
   694     LZMA_closeArchive,       /* closeArchive() method   */
       
   695     LZMA_stat                /* stat() method           */
       
   696 };
       
   697 
       
   698 #endif  /* defined PHYSFS_SUPPORTS_7Z */
       
   699 
       
   700 /* end of lzma.c ... */
       
   701