mirror of
https://github.com/SimoneN64/Kaizen.git
synced 2025-04-02 10:41:53 -04:00
205 lines
6.2 KiB
C
205 lines
6.2 KiB
C
/* Copyright 2018 the unarr project authors (see AUTHORS file).
|
|
License: LGPLv3 */
|
|
|
|
#include "_7z.h"
|
|
|
|
#ifdef HAVE_7Z
|
|
|
|
static void *gSzAlloc_Alloc(ISzAllocPtr self, size_t size) { (void)self; return malloc(size); }
|
|
static void gSzAlloc_Free(ISzAllocPtr self, void *ptr) { (void)self; free(ptr); }
|
|
static ISzAlloc gSzAlloc = { gSzAlloc_Alloc, gSzAlloc_Free };
|
|
|
|
static SRes CSeekStream_Read(const ISeekInStream *p, void *data, size_t *size)
|
|
{
|
|
struct CSeekStream *stm = (struct CSeekStream *) p;
|
|
*size = ar_read(stm->stream, data, *size);
|
|
return SZ_OK;
|
|
}
|
|
|
|
static SRes CSeekStream_Seek(const ISeekInStream *p, Int64 *pos, ESzSeek origin)
|
|
{
|
|
struct CSeekStream *stm = (struct CSeekStream *) p;
|
|
if (!ar_seek(stm->stream, *pos, (int)origin))
|
|
return SZ_ERROR_FAIL;
|
|
*pos = ar_tell(stm->stream);
|
|
return SZ_OK;
|
|
}
|
|
|
|
static void CSeekStream_CreateVTable(struct CSeekStream *in_stream, ar_stream *stream)
|
|
{
|
|
in_stream->super.Read = CSeekStream_Read;
|
|
in_stream->super.Seek = CSeekStream_Seek;
|
|
in_stream->stream = stream;
|
|
}
|
|
|
|
#ifndef USE_7Z_CRC32
|
|
UInt32 Z7_FASTCALL CrcCalc(const void *data, size_t size)
|
|
{
|
|
return ar_crc32(0, data, size);
|
|
}
|
|
#endif
|
|
|
|
static void _7z_close(ar_archive *ar)
|
|
{
|
|
ar_archive_7z *_7z = (ar_archive_7z *)ar;
|
|
free(_7z->entry_name);
|
|
SzArEx_Free(&_7z->data, &gSzAlloc);
|
|
IAlloc_Free(&gSzAlloc, _7z->uncomp.buffer);
|
|
IAlloc_Free(&gSzAlloc, _7z->look_stream.buf);
|
|
}
|
|
|
|
static const char *_7z_get_name(ar_archive *ar, bool raw);
|
|
|
|
static bool _7z_parse_entry(ar_archive *ar, off64_t offset)
|
|
{
|
|
ar_archive_7z *_7z = (ar_archive_7z *)ar;
|
|
//const CSzFileItem *item = _7z->data.db.PackPositions + offset;
|
|
|
|
if (offset < 0 || offset > _7z->data.NumFiles) {
|
|
warn("Offsets must be between 0 and %u", _7z->data.NumFiles);
|
|
return false;
|
|
}
|
|
if (offset == _7z->data.NumFiles) {
|
|
ar->at_eof = true;
|
|
return false;
|
|
}
|
|
|
|
ar->entry_offset = offset;
|
|
ar->entry_offset_next = offset + 1;
|
|
ar->entry_size_uncompressed = (size_t)SzArEx_GetFileSize(&_7z->data, offset);
|
|
ar->entry_filetime = SzBitWithVals_Check(&_7z->data.MTime, offset) ?
|
|
(time64_t)(_7z->data.MTime.Vals[offset].Low |
|
|
((time64_t)_7z->data.MTime.Vals[offset].High << 32))
|
|
: 0;
|
|
free(_7z->entry_name);
|
|
_7z->entry_name = NULL;
|
|
_7z->uncomp.initialized = false;
|
|
|
|
if (SzArEx_IsDir(&_7z->data, offset)) {
|
|
log("Skipping directory entry \"%s\"", _7z_get_name(ar, false));
|
|
return _7z_parse_entry(ar, offset + 1);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static char *SzArEx_GetFileNameUtf8(const CSzArEx *p, UInt32 fileIndex)
|
|
{
|
|
size_t len = p->FileNameOffsets[fileIndex + 1] - p->FileNameOffsets[fileIndex];
|
|
const Byte *src = p->FileNames + p->FileNameOffsets[fileIndex] * 2;
|
|
const Byte *srcEnd = src + len * 2;
|
|
size_t size = len * 3;
|
|
char *str, *out;
|
|
|
|
if (size == (size_t)-1)
|
|
return NULL;
|
|
str = malloc(size + 1);
|
|
if (!str)
|
|
return NULL;
|
|
|
|
for (out = str; src < srcEnd - 1; src += 2) {
|
|
out += ar_conv_rune_to_utf8(src[0] | src[1] << 8, out, str + size - out);
|
|
}
|
|
*out = '\0';
|
|
|
|
return str;
|
|
}
|
|
|
|
static const char *_7z_get_name(ar_archive *ar, bool raw)
|
|
{
|
|
if (raw)
|
|
return NULL;
|
|
|
|
ar_archive_7z *_7z = (ar_archive_7z *)ar;
|
|
if (!_7z->entry_name && ar->entry_offset_next && !ar->at_eof) {
|
|
_7z->entry_name = SzArEx_GetFileNameUtf8(&_7z->data, (UInt32)ar->entry_offset);
|
|
/* normalize path separators */
|
|
if (_7z->entry_name) {
|
|
char *p = _7z->entry_name;
|
|
while ((p = strchr(p, '\\')) != NULL) {
|
|
*p = '/';
|
|
}
|
|
}
|
|
}
|
|
return _7z->entry_name;
|
|
}
|
|
|
|
static bool _7z_uncompress(ar_archive *ar, void *buffer, size_t buffer_size)
|
|
{
|
|
ar_archive_7z *_7z = (ar_archive_7z *)ar;
|
|
struct ar_archive_7z_uncomp *uncomp = &_7z->uncomp;
|
|
|
|
if (!uncomp->initialized) {
|
|
/* TODO: this uncompresses all data for solid compressions */
|
|
SRes res = SzArEx_Extract(&_7z->data, &_7z->look_stream.vt, (UInt32)ar->entry_offset, &uncomp->folder_index, &uncomp->buffer, &uncomp->buffer_size, &uncomp->offset, &uncomp->bytes_left, &gSzAlloc, &gSzAlloc);
|
|
if (res != SZ_OK) {
|
|
warn("Failed to extract file at index %" PRIi64 " (failed with error %d)", ar->entry_offset, res);
|
|
return false;
|
|
}
|
|
if (uncomp->bytes_left != ar->entry_size_uncompressed) {
|
|
warn("Uncompressed sizes don't match (%" PRIuPTR " != %" PRIuPTR ")", uncomp->bytes_left, ar->entry_size_uncompressed);
|
|
return false;
|
|
}
|
|
uncomp->initialized = true;
|
|
}
|
|
|
|
if (buffer_size > uncomp->bytes_left) {
|
|
warn("Requesting too much data (%" PRIuPTR " < %" PRIuPTR ")", uncomp->bytes_left, buffer_size);
|
|
return false;
|
|
}
|
|
|
|
memcpy(buffer, uncomp->buffer + uncomp->offset + ar->entry_size_uncompressed - uncomp->bytes_left, buffer_size);
|
|
uncomp->bytes_left -= buffer_size;
|
|
|
|
return true;
|
|
}
|
|
|
|
ar_archive *ar_open_7z_archive(ar_stream *stream)
|
|
{
|
|
ar_archive *ar;
|
|
ar_archive_7z *_7z;
|
|
SRes res;
|
|
|
|
if (!ar_seek(stream, 0, SEEK_SET))
|
|
return NULL;
|
|
|
|
ar = ar_open_archive(stream, sizeof(ar_archive_7z), _7z_close, _7z_parse_entry, _7z_get_name, _7z_uncompress, NULL, 0);
|
|
if (!ar)
|
|
return NULL;
|
|
|
|
_7z = (ar_archive_7z *)ar;
|
|
CSeekStream_CreateVTable(&_7z->in_stream, stream);
|
|
LookToRead2_CreateVTable(&_7z->look_stream, False);
|
|
_7z->look_stream.realStream = &_7z->in_stream.super;
|
|
_7z->look_stream.buf = ISzAlloc_Alloc(&gSzAlloc, 1 << 18);
|
|
_7z->look_stream.bufSize = 1 << 18;
|
|
LookToRead2_INIT(&_7z->look_stream);
|
|
|
|
|
|
#ifdef USE_7Z_CRC32
|
|
CrcGenerateTable();
|
|
#endif
|
|
|
|
SzArEx_Init(&_7z->data);
|
|
res = SzArEx_Open(&_7z->data, &_7z->look_stream.vt, &gSzAlloc, &gSzAlloc);
|
|
if (res != SZ_OK) {
|
|
if (res != SZ_ERROR_NO_ARCHIVE)
|
|
warn("Invalid 7z archive (failed with error %d)", res);
|
|
ISzAlloc_Free(&gSzAlloc, _7z->look_stream.buf);
|
|
free(ar);
|
|
return NULL;
|
|
}
|
|
|
|
return ar;
|
|
}
|
|
|
|
#else
|
|
|
|
ar_archive *ar_open_7z_archive(ar_stream *stream)
|
|
{
|
|
(void)stream;
|
|
warn("7z support requires 7z SDK (define HAVE_7Z)");
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|