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