Skip to content

Latest commit

 

History

History
703 lines (561 loc) · 20.5 KB

archiver_lzma.c

File metadata and controls

703 lines (561 loc) · 20.5 KB
 
4
* Please see the file lzma.txt in the lzma/ directory.
6
* This file was written by Dennis Schridde, with some peeking at "7zMain.c"
7
8
9
10
11
12
* by Igor Pavlov.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
15
16
17
#include "lzma/C/7zCrc.h"
#include "lzma/C/Archive/7z/7zIn.h"
#include "lzma/C/Archive/7z/7zExtract.h"
20
21
/* 7z internal from 7zIn.c */
extern int TestSignatureCandidate(Byte *testBytes);
24
25
26
#ifdef _LZMA_IN_CB
# define BUFFER_SIZE (1 << 12)
#endif /* _LZMA_IN_CB */
29
30
31
32
/*
* Carries filestream metadata through 7z
*/
typedef struct _FileInputStream
34
35
36
ISzAlloc allocImp; /* Allocation implementation, used by 7z */
ISzAlloc allocTempImp; /* Temporary allocation implementation, used by 7z */
ISzInStream inStream; /* Input stream with read callbacks, used by 7z */
37
PHYSFS_Io *io; /* Filehandle, used by read implementation */
38
39
40
41
#ifdef _LZMA_IN_CB
Byte buffer[BUFFER_SIZE]; /* Buffer, used by read implementation */
#endif /* _LZMA_IN_CB */
} FileInputStream;
44
* In the 7z format archives are splited into blocks, those are called folders
45
46
47
48
49
50
* Set by LZMA_read()
*/
typedef struct _LZMAfolder
{
PHYSFS_uint32 index; /* Index of folder in archive */
PHYSFS_uint32 references; /* Number of files using this block */
51
52
PHYSFS_uint8 *cache; /* Cached folder */
size_t size; /* Size of folder */
53
54
55
56
57
58
} LZMAfolder;
/*
* Set by LZMA_openArchive(), except folder which gets it's values
* in LZMA_read()
*/
61
62
struct _LZMAfile *files; /* Array of files, size == archive->db.Database.NumFiles */
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 */
67
68
/* Set by LZMA_openArchive(), except offset which is set by LZMA_read() */
typedef struct _LZMAfile
70
PHYSFS_uint32 index; /* Index of file in archive */
71
LZMAarchive *archive; /* Link to corresponding archive */
72
73
LZMAfolder *folder; /* Link to corresponding folder */
CFileItem *item; /* For 7z: File info, eg. name, size */
74
size_t offset; /* Offset in folder */
75
76
size_t position; /* Current "virtual" position in file */
} LZMAfile;
79
80
/* Memory management implementations to be passed to 7z */
81
82
83
84
85
86
87
88
89
90
91
92
93
static void *SzAllocPhysicsFS(size_t size)
{
return ((size == 0) ? NULL : allocator.Malloc(size));
} /* SzAllocPhysicsFS */
static void SzFreePhysicsFS(void *address)
{
if (address != NULL)
allocator.Free(address);
} /* SzFreePhysicsFS */
94
95
96
97
/* Filesystem implementations to be passed to 7z */
#ifdef _LZMA_IN_CB
98
99
100
101
/*
* Read implementation, to be passed to 7z
* WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
*/
102
103
SZ_RESULT SzFileReadImp(void *object, void **buffer, size_t maxReqSize,
size_t *processedSize)
105
FileInputStream *s = (FileInputStream *)(object - offsetof(FileInputStream, inStream)); /* HACK! */
106
107
108
109
PHYSFS_sint64 processedSizeLoc = 0;
if (maxReqSize > BUFFER_SIZE)
maxReqSize = BUFFER_SIZE;
110
processedSizeLoc = s->io->read(s->io, s->buffer, maxReqSize);
112
113
if (processedSize != NULL)
*processedSize = (size_t) processedSizeLoc;
115
116
117
118
return SZ_OK;
} /* SzFileReadImp */
#else
120
121
122
123
/*
* Read implementation, to be passed to 7z
* WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
*/
124
125
126
SZ_RESULT SzFileReadImp(void *object, void *buffer, size_t size,
size_t *processedSize)
{
127
FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
128
129
const size_t processedSizeLoc = s->io->read(s->io, buffer, size);
if (processedSize != NULL)
130
*processedSize = processedSizeLoc;
136
137
138
139
/*
* Seek implementation, to be passed to 7z
* WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly!
*/
140
141
SZ_RESULT SzFileSeekImp(void *object, CFileSize pos)
{
142
FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */
143
if (s->io->seek(s->io, (PHYSFS_uint64) pos))
144
145
146
147
148
return SZ_OK;
return SZE_FAIL;
} /* SzFileSeekImp */
150
* Translate Microsoft FILETIME (used by 7zip) into UNIX timestamp
152
static PHYSFS_sint64 lzma_filetime_to_unix_timestamp(CArchiveFileTime *ft)
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/* MS counts in nanoseconds ... */
const PHYSFS_uint64 FILETIME_NANOTICKS_PER_SECOND = __PHYSFS_UI64(10000000);
/* MS likes to count seconds since 01.01.1601 ... */
const PHYSFS_uint64 FILETIME_UNIX_DIFF = __PHYSFS_UI64(11644473600);
PHYSFS_uint64 filetime = ft->Low | ((PHYSFS_uint64)ft->High << 32);
return filetime/FILETIME_NANOTICKS_PER_SECOND - FILETIME_UNIX_DIFF;
} /* lzma_filetime_to_unix_timestamp */
/*
* Compare a file with a given name, C89 stdlib variant
* Used for sorting
*/
static int lzma_file_cmp_stdlib(const void *key, const void *object)
{
const char *name = (const char *) key;
LZMAfile *file = (LZMAfile *) object;
175
176
177
178
179
/*
* Compare two files with each other based on the name
* Used for sorting
*/
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
185
186
187
188
189
} /* lzma_file_cmp */
/*
* Swap two entries in the file array
*/
190
static void lzma_file_swap(void *_a, size_t one, size_t two)
191
192
193
194
195
196
197
198
199
200
201
202
203
{
LZMAfile tmp;
LZMAfile *first = &(((LZMAfile *) _a)[one]);
LZMAfile *second = &(((LZMAfile *) _a)[two]);
memcpy(&tmp, first, sizeof (LZMAfile));
memcpy(first, second, sizeof (LZMAfile));
memcpy(second, &tmp, sizeof (LZMAfile));
} /* lzma_file_swap */
/*
* Find entry 'name' in 'archive'
*/
204
static LZMAfile * lzma_find_file(const LZMAarchive *archive, const char *name)
206
LZMAfile *file = bsearch(name, archive->files, archive->db.Database.NumFiles, sizeof(*archive->files), lzma_file_cmp_stdlib); /* FIXME: Should become __PHYSFS_search!!! */
208
BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NOT_FOUND, NULL);
215
* Load metadata for the file at given index
217
static int lzma_file_init(LZMAarchive *archive, PHYSFS_uint32 fileIndex)
219
220
221
LZMAfile *file = &archive->files[fileIndex];
PHYSFS_uint32 folderIndex = archive->db.FileIndexToFolderIndexMap[fileIndex];
222
file->index = fileIndex; /* Store index into 7z array, since we sort our own. */
224
225
file->folder = (folderIndex != (PHYSFS_uint32)-1 ? &archive->folders[folderIndex] : NULL); /* Directories don't have a folder (they contain no own data...) */
file->item = &archive->db.Database.Files[fileIndex]; /* Holds crucial data and is often referenced -> Store link */
226
227
228
file->position = 0;
file->offset = 0; /* Offset will be set by LZMA_read() */
233
234
235
236
237
238
/*
* Load metadata for all files
*/
static int lzma_files_init(LZMAarchive *archive)
{
PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles;
240
for (fileIndex = 0; fileIndex < numFiles; fileIndex++ )
242
if (!lzma_file_init(archive, fileIndex))
244
return 0; /* FALSE on failure */
248
__PHYSFS_sort(archive->files, (size_t) numFiles, lzma_file_cmp, lzma_file_swap);
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/*
* Initialise specified archive
*/
static void lzma_archive_init(LZMAarchive *archive)
{
memset(archive, 0, sizeof(*archive));
/* Prepare callbacks for 7z */
archive->stream.inStream.Read = SzFileReadImp;
archive->stream.inStream.Seek = SzFileSeekImp;
archive->stream.allocImp.Alloc = SzAllocPhysicsFS;
archive->stream.allocImp.Free = SzFreePhysicsFS;
archive->stream.allocTempImp.Alloc = SzAllocPhysicsFS;
archive->stream.allocTempImp.Free = SzFreePhysicsFS;
}
/*
* Deinitialise archive
*/
static void lzma_archive_exit(LZMAarchive *archive)
{
/* Free arrays */
allocator.Free(archive->folders);
allocator.Free(archive->files);
allocator.Free(archive);
}
284
285
286
287
288
289
290
291
292
293
/*
* Wrap all 7z calls in this, so the physfs error state is set appropriately.
*/
static int lzma_err(SZ_RESULT rc)
{
switch (rc)
{
case SZ_OK: /* Same as LZMA_RESULT_OK */
break;
case SZE_DATA_ERROR: /* Same as LZMA_RESULT_DATA_ERROR */
294
__PHYSFS_setError(PHYSFS_ERR_CORRUPT); /*!!!FIXME: was "PHYSFS_ERR_DATA_ERROR" */
297
__PHYSFS_setError(PHYSFS_ERR_OUT_OF_MEMORY);
306
__PHYSFS_setError(PHYSFS_ERR_OTHER_ERROR); /* !!! FIXME: right? */
309
__PHYSFS_setError(PHYSFS_ERR_CORRUPT); /* !!! FIXME: right? */
319
static PHYSFS_sint64 LZMA_read(PHYSFS_Io *io, void *outBuf, PHYSFS_uint64 len)
321
LZMAfile *file = (LZMAfile *) io->opaque;
323
size_t wantedSize = (size_t) len;
324
const size_t remainingSize = file->item->Size - file->position;
327
328
BAIL_IF_MACRO(wantedSize == 0, ERRPASS, 0); /* quick rejection. */
BAIL_IF_MACRO(remainingSize == 0, PHYSFS_ERR_PAST_EOF, 0);
330
331
if (wantedSize > remainingSize)
wantedSize = remainingSize;
333
/* Only decompress the folder if it is not already cached */
334
if (file->folder->cache == NULL)
336
const int rc = lzma_err(SzExtract(
337
338
339
&file->archive->stream.inStream, /* compressed data */
&file->archive->db, /* 7z's database, containing everything */
file->index, /* Index into database arrays */
340
/* Index of cached folder, will be changed by SzExtract */
342
/* Cache for decompressed folder, allocated/freed by SzExtract */
344
/* Size of cache, will be changed by SzExtract */
346
/* Offset of this file inside the cache, set by SzExtract */
348
&fileSize, /* Size of this file */
349
350
&file->archive->stream.allocImp,
&file->archive->stream.allocTempImp));
351
352
if (rc != SZ_OK)
354
} /* if */
356
/* Copy wanted bytes over from cache to outBuf */
357
memcpy(outBuf, (file->folder->cache + file->offset + file->position),
358
359
360
wantedSize);
file->position += wantedSize; /* Increase virtual position */
365
static PHYSFS_sint64 LZMA_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
371
static PHYSFS_sint64 LZMA_tell(PHYSFS_Io *io)
373
374
LZMAfile *file = (LZMAfile *) io->opaque;
return file->position;
378
static int LZMA_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
380
LZMAfile *file = (LZMAfile *) io->opaque;
382
BAIL_IF_MACRO(offset > file->item->Size, PHYSFS_ERR_PAST_EOF, 0);
383
384
file->position = offset; /* We only use a virtual position... */
390
static PHYSFS_sint64 LZMA_length(PHYSFS_Io *io)
392
const LZMAfile *file = (LZMAfile *) io->opaque;
397
static PHYSFS_Io *LZMA_duplicate(PHYSFS_Io *_io)
399
400
/* !!! FIXME: this archiver needs to be reworked to allow multiple
* !!! FIXME: opens before we worry about duplication. */
405
static int LZMA_flush(PHYSFS_Io *io) { return 1; /* no write support. */ }
408
static void LZMA_destroy(PHYSFS_Io *io)
410
LZMAfile *file = (LZMAfile *) io->opaque;
414
415
416
417
418
419
420
421
422
423
424
425
/* Only decrease refcount if someone actually requested this file... Prevents from overflows and close-on-open... */
if (file->folder->references > 0)
file->folder->references--;
if (file->folder->references == 0)
{
/* Free the cache which might have been allocated by LZMA_read() */
allocator.Free(file->folder->cache);
file->folder->cache = NULL;
}
/* !!! FIXME: we don't free (file) or (file->folder)?! */
} /* if */
} /* LZMA_destroy */
428
429
static const PHYSFS_Io LZMA_Io =
{
430
CURRENT_PHYSFS_IO_API_VERSION, NULL,
431
432
433
434
435
436
437
LZMA_read,
LZMA_write,
LZMA_seek,
LZMA_tell,
LZMA_length,
LZMA_duplicate,
LZMA_flush,
442
static void *LZMA_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
444
PHYSFS_uint8 sig[k7zSignatureSize];
446
LZMAarchive *archive = NULL;
448
449
assert(io != NULL); /* shouldn't ever happen. */
450
BAIL_IF_MACRO(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
451
452
if (io->read(io, sig, k7zSignatureSize) != k7zSignatureSize)
453
454
455
return 0;
BAIL_IF_MACRO(!TestSignatureCandidate(sig), PHYSFS_ERR_UNSUPPORTED, NULL);
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);
460
lzma_archive_init(archive);
465
466
467
468
if (lzma_err(SzArchiveOpen(&archive->stream.inStream,
&archive->db,
&archive->stream.allocImp,
&archive->stream.allocTempImp)) != SZ_OK)
470
471
SzArDbExFree(&archive->db, SzFreePhysicsFS);
lzma_archive_exit(archive);
472
return NULL; /* Error is set by lzma_err! */
475
476
477
478
479
480
len = archive->db.Database.NumFiles * sizeof (LZMAfile);
archive->files = (LZMAfile *) allocator.Malloc(len);
if (archive->files == NULL)
{
SzArDbExFree(&archive->db, SzFreePhysicsFS);
lzma_archive_exit(archive);
482
483
484
485
486
487
488
489
}
/*
* Init with 0 so we know when a folder is already cached
* Values will be set by LZMA_openRead()
*/
memset(archive->files, 0, len);
490
len = archive->db.Database.NumFolders * sizeof (LZMAfolder);
491
492
493
494
495
archive->folders = (LZMAfolder *) allocator.Malloc(len);
if (archive->folders == NULL)
{
SzArDbExFree(&archive->db, SzFreePhysicsFS);
lzma_archive_exit(archive);
498
499
500
501
502
/*
* Init with 0 so we know when a folder is already cached
* Values will be set by LZMA_read()
*/
503
504
505
506
507
508
memset(archive->folders, 0, len);
if(!lzma_files_init(archive))
{
SzArDbExFree(&archive->db, SzFreePhysicsFS);
lzma_archive_exit(archive);
513
514
515
516
517
518
519
520
} /* LZMA_openArchive */
/*
* Moved to seperate function so we can use alloca then immediately throw
* away the allocated stack space...
*/
static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
521
const char *odir, const char *str, size_t flen)
523
char *newstr = __PHYSFS_smallAlloc(flen + 1);
527
528
memcpy(newstr, str, flen);
newstr[flen] = '\0';
534
static void LZMA_enumerateFiles(void *opaque, const char *dname,
536
537
const char *origdir, void *callbackdata)
{
538
539
size_t dlen = strlen(dname),
dlen_inc = dlen + ((dlen > 0) ? 1 : 0);
540
LZMAarchive *archive = (LZMAarchive *) opaque;
541
542
543
544
545
LZMAfile *file = NULL,
*lastFile = &archive->files[archive->db.Database.NumFiles];
if (dlen)
{
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
548
549
550
551
552
file += 1;
}
else
{
file = archive->files;
}
554
BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NOT_FOUND, );
558
559
560
561
562
563
564
const char * fname = file->item->Name;
const char * dirNameEnd = fname + dlen_inc;
if (strncmp(dname, fname, dlen) != 0) /* Stop after mismatch, archive->files is sorted */
break;
if (strchr(dirNameEnd, '/')) /* Skip subdirs */
566
567
568
569
570
571
572
573
574
file++;
continue;
}
/* Do the actual callback... */
doEnumCallback(cb, callbackdata, origdir, dirNameEnd, strlen(dirNameEnd));
file++;
}
578
static PHYSFS_Io *LZMA_openRead(void *opaque, const char *name,
579
int *fileExists)
580
581
{
LZMAarchive *archive = (LZMAarchive *) opaque;
582
LZMAfile *file = lzma_find_file(archive, name);
585
*fileExists = (file != NULL);
586
BAIL_IF_MACRO(file == NULL, PHYSFS_ERR_NOT_FOUND, NULL);
587
BAIL_IF_MACRO(file->folder == NULL, PHYSFS_ERR_NOT_A_FILE, NULL);
590
file->folder->references++; /* Increase refcount for automatic cleanup... */
592
io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
593
BAIL_IF_MACRO(io == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
594
595
596
597
memcpy(io, &LZMA_Io, sizeof (*io));
io->opaque = file;
return io;
601
static PHYSFS_Io *LZMA_openWrite(void *opaque, const char *filename)
607
static PHYSFS_Io *LZMA_openAppend(void *opaque, const char *filename)
613
static void LZMA_closeArchive(void *opaque)
615
616
LZMAarchive *archive = (LZMAarchive *) opaque;
617
618
#if 0 /* !!! FIXME: you shouldn't have to do this. */
PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles;
619
for (fileIndex = 0; fileIndex < numFiles; fileIndex++)
621
622
LZMA_fileClose(&archive->files[fileIndex]);
} /* for */
625
SzArDbExFree(&archive->db, SzFreePhysicsFS);
626
archive->stream.io->destroy(archive->stream.io);
627
lzma_archive_exit(archive);
631
static int LZMA_remove(void *opaque, const char *name)
637
static int LZMA_mkdir(void *opaque, const char *name)
642
static int LZMA_stat(void *opaque, const char *filename,
643
int *exists, PHYSFS_Stat *stat)
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
{
const LZMAarchive *archive = (const LZMAarchive *) opaque;
const LZMAfile *file = lzma_find_file(archive, filename);
*exists = (file != 0);
if (!file)
return 0;
if(file->item->IsDirectory)
{
stat->filesize = 0;
stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
} /* if */
else
{
stat->filesize = (PHYSFS_sint64) file->item->Size;
stat->filetype = PHYSFS_FILETYPE_REGULAR;
} /* else */
/* !!! FIXME: the 0's should be -1's? */
if (file->item->IsLastWriteTimeDefined)
stat->modtime = lzma_filetime_to_unix_timestamp(&file->item->LastWriteTime);
else
stat->modtime = 0;
/* real create and accesstype are currently not in the lzma SDK */
stat->createtime = stat->modtime;
stat->accesstime = 0;
stat->readonly = 1; /* 7zips are always read only */
678
679
680
const PHYSFS_Archiver __PHYSFS_Archiver_LZMA =
{
681
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
682
683
684
685
686
{
"7Z",
"LZMA (7zip) format",
"Dennis Schridde <devurandom@gmx.net>",
"http://icculus.org/physfs/",
689
690
691
692
693
694
695
LZMA_openArchive, /* openArchive() method */
LZMA_enumerateFiles, /* enumerateFiles() method */
LZMA_openRead, /* openRead() method */
LZMA_openWrite, /* openWrite() method */
LZMA_openAppend, /* openAppend() method */
LZMA_remove, /* remove() method */
LZMA_mkdir, /* mkdir() method */
696
LZMA_closeArchive, /* closeArchive() method */
697
LZMA_stat /* stat() method */
700
#endif /* defined PHYSFS_SUPPORTS_7Z */