This commit is contained in:
2026-03-23 12:11:07 +01:00
commit e64eb40b38
4573 changed files with 3117439 additions and 0 deletions

486
external/unarr/zip/inflate.c vendored Normal file
View File

@@ -0,0 +1,486 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
#include "inflate.h"
#include "../common/allocator.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#ifdef _MSC_VER
#define UNARR_FORCE_INLINE __forceinline
#else
#define UNARR_FORCE_INLINE inline __attribute__((always_inline))
#endif
#define MAX_BITS 16
#define TREE_FAST_BITS 10
#define MAX_TREE_NODES 288
enum inflate_step {
STEP_NEXT_BLOCK = 0,
STEP_COPY_INIT, STEP_COPY,
STEP_INFLATE_STATIC_INIT, STEP_INFLATE_DYNAMIC_INIT, STEP_INFLATE_DYNAMIC_INIT_PRETREE, STEP_INFLATE_DYNAMIC_INIT_TREES,
STEP_INFLATE_CODE, STEP_INFLATE, STEP_INFLATE_DISTANCE_CODE, STEP_INFLATE_DISTANCE, STEP_INFLATE_REPEAT,
};
enum { RESULT_EOS = -1, RESULT_NOT_DONE = 0, RESULT_ERROR = 1 };
#if defined(_MSC_VER) || defined(__GNUC__)
#define RESULT_ERROR (RESULT_ERROR + __COUNTER__)
#endif
struct tree {
struct {
unsigned value : 11;
unsigned is_value : 1;
unsigned length : 4;
} nodes[(1 << TREE_FAST_BITS) + MAX_TREE_NODES * 2];
int next_node;
};
struct inflate_state_s {
enum inflate_step step;
struct {
int value;
int length;
int dist;
int tree_idx;
} state;
struct {
int hlit;
int hdist;
int hclen;
int idx;
int clens[288 + 32];
} prepare;
bool inflate64;
bool is_final_block;
struct tree tree_lengths;
struct tree tree_dists;
struct {
const uint8_t *data_in;
size_t *avail_in;
uint64_t bits;
int available;
} in;
struct {
uint8_t *data_out;
size_t *avail_out;
uint8_t window[1 << 16];
size_t offset;
} out;
};
static const struct {
int bits;
int length;
} table_lengths[30] = {
{ 0, 3 }, { 0, 4 }, { 0, 5 }, { 0, 6 }, { 0, 7 }, { 0, 8 }, { 0, 9 }, { 0, 10 },
{ 1, 11 }, { 1, 13 }, { 1, 15 }, { 1, 17 }, { 2, 19 }, { 2, 23 }, { 2, 27 }, { 2, 31 },
{ 3, 35 }, { 3, 43 }, { 3, 51 }, { 3, 59 }, { 4, 67 }, { 4, 83 }, { 4, 99 }, { 4, 115 },
{ 5, 131 }, { 5, 163 }, { 5, 195 }, { 5, 227 },
{ 0, 258 }, /* Deflate64 (replaces { 0, 258 }) */ { 16, 3 }
};
static const struct {
int bits;
int dist;
} table_dists[32] = {
{ 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 1, 5 }, { 1, 7 },
{ 2, 9 }, { 2, 13 }, { 3, 17 }, { 3, 25 }, { 4, 33 }, { 4, 49 },
{ 5, 65 }, { 5, 97 }, { 6, 129 }, { 6, 193 }, { 7, 257 }, { 7, 385 },
{ 8, 513 }, { 8, 769 }, { 9, 1025 }, { 9, 1537 }, { 10, 2049 }, { 10, 3073 },
{ 11, 4097 }, { 11, 6145 }, { 12, 8193 }, { 12, 12289 }, { 13, 16385 }, { 13, 24577 },
/* Deflate64 */ { 14, 32769 }, { 14, 49153 }
};
static const int table_code_length_idxs[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
static UNARR_FORCE_INLINE bool br_ensure(inflate_state *state, int bits)
{
while (state->in.available < bits) {
if (*state->in.avail_in == 0)
return false;
state->in.bits |= ((uint64_t)*state->in.data_in++ << state->in.available);
(*state->in.avail_in)--;
state->in.available += 8;
}
return true;
}
static UNARR_FORCE_INLINE uint64_t br_bits(inflate_state *state, int bits)
{
uint64_t res = state->in.bits & (((uint64_t)1 << bits) - 1);
state->in.available -= bits;
state->in.bits >>= bits;
return res;
}
static UNARR_FORCE_INLINE void output(inflate_state *state, uint8_t value)
{
*state->out.data_out++ = value;
(*state->out.avail_out)--;
state->out.window[state->out.offset++ & (sizeof(state->out.window) - 1)] = value;
}
static bool tree_add_value(struct tree *tree, int key, int bits, int value)
{
int rkey = 0, i;
for (i = 0; i < bits; i++)
rkey = (rkey << 1) | ((key >> i) & 1);
if (bits <= TREE_FAST_BITS) {
if (tree->nodes[rkey].length)
return false;
tree->nodes[rkey].length = bits;
tree->nodes[rkey].value = value;
tree->nodes[rkey].is_value = true;
for (i = 1; i < (1 << (TREE_FAST_BITS - bits)); i++) {
if (tree->nodes[rkey | (i << bits)].length)
return false;
tree->nodes[rkey | (i << bits)] = tree->nodes[rkey];
}
return true;
}
rkey &= (1 << TREE_FAST_BITS) - 1;
if (tree->nodes[rkey].is_value)
return false;
tree->nodes[rkey].length = TREE_FAST_BITS + 1;
if (!tree->nodes[rkey].value)
tree->nodes[rkey].value = (1 << TREE_FAST_BITS) + tree->next_node++ * 2;
i = tree->nodes[rkey].value;
bits -= TREE_FAST_BITS;
while (bits > 1) {
i |= (key >> (bits - 1)) & 1;
if (tree->nodes[i].is_value)
return false;
if (!tree->nodes[i].value) {
if (tree->next_node == MAX_TREE_NODES)
return false;
tree->nodes[i].value = (1 << TREE_FAST_BITS) + tree->next_node++ * 2;
}
i = tree->nodes[i].value;
bits--;
}
i |= key & 1;
if (tree->nodes[i].value || tree->nodes[i].is_value)
return false;
tree->nodes[i].value = value;
tree->nodes[i].is_value = true;
return true;
}
static UNARR_FORCE_INLINE int tree_get_value(inflate_state *state, const struct tree *tree, bool not_fast)
{
if (state->state.tree_idx == 0) {
int key = state->in.bits & ((1 << TREE_FAST_BITS) - 1);
while (not_fast && state->in.available < TREE_FAST_BITS && state->in.available < (int)tree->nodes[key].length) {
if (!br_ensure(state, tree->nodes[key].length))
return RESULT_NOT_DONE;
key = state->in.bits & ((1 << TREE_FAST_BITS) - 1);
}
if (tree->nodes[key].is_value) {
state->state.value = tree->nodes[key].value;
(void)br_bits(state, tree->nodes[key].length);
return RESULT_EOS;
}
if (tree->nodes[key].length == 0)
return RESULT_ERROR;
(void)br_bits(state, TREE_FAST_BITS);
state->state.tree_idx = tree->nodes[key].value;
}
while (state->state.value == -1) {
int idx;
if (not_fast && !br_ensure(state, 1))
return RESULT_NOT_DONE;
idx = state->state.tree_idx | (int)br_bits(state, 1);
if (tree->nodes[idx].is_value)
state->state.value = tree->nodes[idx].value;
else if (tree->nodes[idx].value)
state->state.tree_idx = tree->nodes[idx].value;
else
return RESULT_ERROR;
}
state->state.tree_idx = 0;
return RESULT_EOS;
}
static void setup_static_trees(inflate_state *state)
{
int i;
memset(&state->tree_lengths, 0, sizeof(state->tree_lengths));
for (i = 0; i < 144; i++)
tree_add_value(&state->tree_lengths, i + 48, 8, i);
for (i = 144; i < 256; i++)
tree_add_value(&state->tree_lengths, i + 256, 9, i);
for (i = 256; i < 280; i++)
tree_add_value(&state->tree_lengths, i - 256, 7, i);
for (i = 280; i < 288; i++)
tree_add_value(&state->tree_lengths, i - 88, 8, i);
memset(&state->tree_dists, 0, sizeof(state->tree_dists));
for (i = 0; i < 32; i++)
tree_add_value(&state->tree_dists, i, 5, i);
}
static bool setup_dynamic_tree(struct tree *tree, int *clens, int count)
{
int code, i;
int bl_count[MAX_BITS];
int next_code[MAX_BITS];
memset(bl_count, 0, sizeof(bl_count));
for (i = 0; i < count; i++)
bl_count[clens[i]]++;
bl_count[0] = 0;
code = 0;
for (i = 1; i < MAX_BITS; i++) {
code = (code + bl_count[i - 1]) << 1;
next_code[i] = code;
}
memset(tree, 0, sizeof(*tree));
for (i = 0; i < count; i++) {
if (clens[i] != 0) {
if (!tree_add_value(tree, next_code[clens[i]], clens[i], i))
return false;
next_code[clens[i]]++;
}
}
return true;
}
inflate_state *inflate_create(bool inflate64)
{
inflate_state *state = calloc(1, sizeof(inflate_state));
if (state)
state->inflate64 = inflate64;
return state;
}
void inflate_free(inflate_state *state)
{
free(state);
}
int inflate_process(inflate_state *state, const void *data_in, size_t *avail_in, void *data_out, size_t *avail_out)
{
bool not_fast = true;
int res;
if (!state || !data_in || !avail_in || !data_out || !avail_out)
return RESULT_ERROR;
state->in.data_in = data_in;
state->in.avail_in = avail_in;
state->out.data_out = data_out;
state->out.avail_out = avail_out;
for (;;) {
switch (state->step) {
case STEP_NEXT_BLOCK:
if (state->is_final_block)
return RESULT_EOS;
if (!br_ensure(state, 3))
return RESULT_NOT_DONE;
state->is_final_block = br_bits(state, 1) != 0;
switch (br_bits(state, 2)) {
case 0:
state->step = STEP_COPY_INIT;
break;
case 1:
state->step = STEP_INFLATE_STATIC_INIT;
break;
case 2:
state->step = STEP_INFLATE_DYNAMIC_INIT;
break;
default:
return RESULT_ERROR;
}
break;
case STEP_COPY_INIT:
if (!br_ensure(state, 32))
return RESULT_NOT_DONE;
(void)br_bits(state, state->in.available & 0x7);
state->state.length = (uint16_t)br_bits(state, 16);
if (state->state.length != 0xFFFF - (uint16_t)br_bits(state, 16))
return RESULT_ERROR;
state->step = STEP_COPY;
/* fall through */
case STEP_COPY:
while (state->state.length > 0) {
if (!br_ensure(state, 8) || *avail_out == 0)
return RESULT_NOT_DONE;
output(state, (uint8_t)br_bits(state, 8));
state->state.length--;
}
state->step = STEP_NEXT_BLOCK;
break;
case STEP_INFLATE_STATIC_INIT:
setup_static_trees(state);
/* fall through */
STEP_INFLATE_START:
not_fast = !br_ensure(state, state->inflate64 ? 49 : 48);
state->state.value = -1;
/* fall through */
case STEP_INFLATE_CODE:
res = tree_get_value(state, &state->tree_lengths, not_fast);
if (res != RESULT_EOS) {
state->step = STEP_INFLATE_CODE;
return res;
}
/* fall through */
case STEP_INFLATE:
if (state->state.value < 256) {
if (*avail_out == 0) {
state->step = STEP_INFLATE;
return RESULT_NOT_DONE;
}
output(state, (uint8_t)state->state.value);
goto STEP_INFLATE_START;
}
if (state->state.value == 256) {
state->step = STEP_NEXT_BLOCK;
break;
}
if (state->state.value > 285)
return RESULT_ERROR;
if (state->inflate64 && state->state.value == 285) {
not_fast = !br_ensure(state, 45);
state->state.value = 286;
}
if (not_fast && !br_ensure(state, table_lengths[state->state.value - 257].bits)) {
state->step = STEP_INFLATE;
return RESULT_NOT_DONE;
}
state->state.length = table_lengths[state->state.value - 257].length + (int)br_bits(state, table_lengths[state->state.value - 257].bits);
state->state.value = -1;
/* fall through */
case STEP_INFLATE_DISTANCE_CODE:
res = tree_get_value(state, &state->tree_dists, not_fast);
if (res != RESULT_EOS) {
state->step = STEP_INFLATE_DISTANCE_CODE;
return res;
}
/* fall through */
case STEP_INFLATE_DISTANCE:
if (not_fast && !br_ensure(state, table_dists[state->state.value].bits)) {
state->step = STEP_INFLATE_DISTANCE;
return RESULT_NOT_DONE;
}
state->state.dist = table_dists[state->state.value].dist + (int)br_bits(state, table_dists[state->state.value].bits);
if ((size_t)state->state.dist > state->out.offset || (state->state.value > 30 && !state->inflate64))
return RESULT_ERROR;
state->step = STEP_INFLATE_REPEAT;
/* fall through */
case STEP_INFLATE_REPEAT:
while (state->state.length > 0) {
if (*avail_out == 0)
return RESULT_NOT_DONE;
output(state, state->out.window[(state->out.offset - state->state.dist) & (sizeof(state->out.window) - 1)]);
state->state.length--;
}
goto STEP_INFLATE_START;
case STEP_INFLATE_DYNAMIC_INIT:
if (!br_ensure(state, 14))
return RESULT_NOT_DONE;
state->prepare.hlit = (int)br_bits(state, 5) + 257;
state->prepare.hdist = (int)br_bits(state, 5) + 1;
state->prepare.hclen = (int)br_bits(state, 4) + 4;
memset(state->prepare.clens, 0, sizeof(state->prepare.clens));
state->prepare.idx = 0;
state->step = STEP_INFLATE_DYNAMIC_INIT_PRETREE;
/* fall through */
case STEP_INFLATE_DYNAMIC_INIT_PRETREE:
while (state->prepare.idx < state->prepare.hclen) {
if (!br_ensure(state, 3))
return RESULT_NOT_DONE;
state->prepare.clens[table_code_length_idxs[state->prepare.idx]] = (int)br_bits(state, 3);
state->prepare.idx++;
}
if (!setup_dynamic_tree(&state->tree_lengths, state->prepare.clens, 19))
return RESULT_ERROR;
memset(state->prepare.clens, 0, sizeof(state->prepare.clens));
state->prepare.idx = 0;
state->state.value = -1;
state->step = STEP_INFLATE_DYNAMIC_INIT_TREES;
/* fall through */
case STEP_INFLATE_DYNAMIC_INIT_TREES:
while (state->prepare.idx < state->prepare.hlit + state->prepare.hdist) {
int value = 0, repeat = 0;
if (state->state.value == -1) {
res = tree_get_value(state, &state->tree_lengths, true);
if (res != RESULT_EOS)
return res;
}
if (state->state.value < 16) {
state->prepare.clens[state->prepare.idx++] = state->state.value;
}
else if (state->state.value == 16) {
if (state->prepare.idx == 0)
return RESULT_ERROR;
if (!br_ensure(state, 2))
return RESULT_NOT_DONE;
value = state->prepare.clens[state->prepare.idx - 1];
repeat = (int)br_bits(state, 2) + 3;
}
else if (state->state.value == 17) {
if (!br_ensure(state, 3))
return RESULT_NOT_DONE;
value = 0;
repeat = (int)br_bits(state, 3) + 3;
}
else {
if (!br_ensure(state, 7))
return RESULT_NOT_DONE;
value = 0;
repeat = (int)br_bits(state, 7) + 11;
}
if (repeat) {
if (state->prepare.idx + repeat > state->prepare.hlit + state->prepare.hdist)
return RESULT_ERROR;
while (repeat-- > 0)
state->prepare.clens[state->prepare.idx++] = value;
}
state->state.value = -1;
}
if (!setup_dynamic_tree(&state->tree_lengths, state->prepare.clens, state->prepare.hlit))
return RESULT_ERROR;
if (!setup_dynamic_tree(&state->tree_dists, state->prepare.clens + state->prepare.hlit, state->prepare.hdist))
return RESULT_ERROR;
goto STEP_INFLATE_START;
}
}
}
int inflate_flush(inflate_state *state, unsigned char data_in[8])
{
int count = 0;
int keep = state->in.available & 0x7;
while (count < state->in.available / 8) {
data_in[count] = (state->in.bits >> (count * 8 + keep)) & 0xFF;
count++;
}
state->in.available = keep;
return count;
}

19
external/unarr/zip/inflate.h vendored Normal file
View File

@@ -0,0 +1,19 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
#ifndef zip_inflate_h
#define zip_inflate_h
#include <stddef.h>
#include <stdbool.h>
typedef struct inflate_state_s inflate_state;
inflate_state *inflate_create(bool inflate64);
/* updates avail_in and avail_out and returns -1 on EOF or any other non-zero value on error */
int inflate_process(inflate_state *state, const void *data_in, size_t *avail_in, void *data_out, size_t *avail_out);
/* restores up to 8 bytes of data cached by inflate_process */
int inflate_flush(inflate_state *state, unsigned char data_in[8]);
void inflate_free(inflate_state *state);
#endif

327
external/unarr/zip/parse-zip.c vendored Normal file
View File

@@ -0,0 +1,327 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
#include "zip.h"
#if defined(_MSC_VER) && !defined(inline)
#define inline __inline
#endif
static inline uint16_t uint16le(unsigned char *data) { return data[0] | data[1] << 8; }
static inline uint32_t uint32le(unsigned char *data) { return data[0] | data[1] << 8 | data[2] << 16 | (uint32_t) data[3] << 24; }
static inline uint64_t uint64le(unsigned char *data) { return (uint64_t)uint32le(data) | (uint64_t)uint32le(data + 4) << 32; }
bool zip_seek_to_compressed_data(ar_archive_zip *zip)
{
struct zip_entry entry;
if (!ar_seek(zip->super.stream, zip->entry.offset, SEEK_SET))
return false;
if (!zip_parse_local_file_entry(zip, &entry))
return false;
if (zip->entry.method != entry.method) {
warn("Compression methods don't match: %d != %d", zip->entry.method, entry.method);
if (!zip->entry.method)
zip->entry.method = entry.method;
}
if (zip->entry.dosdate != entry.dosdate) {
warn("Timestamps don't match");
if (!zip->entry.dosdate) {
zip->entry.dosdate = entry.dosdate;
zip->super.entry_filetime = ar_conv_dosdate_to_filetime(zip->entry.dosdate);
}
}
return ar_seek(zip->super.stream, zip->entry.offset + ZIP_LOCAL_ENTRY_FIXED_SIZE + entry.namelen + entry.extralen, SEEK_SET);
}
static bool zip_parse_extra_fields(ar_archive_zip *zip, struct zip_entry *entry)
{
uint8_t *extra;
if (!entry->extralen)
return true;
/* read ZIP64 values where needed */
if (!ar_skip(zip->super.stream, entry->namelen))
return false;
extra = malloc(entry->extralen);
if (!extra || ar_read(zip->super.stream, extra, entry->extralen) != entry->extralen) {
free(extra);
return false;
}
for (uint32_t idx = 0; idx + 4 < entry->extralen; idx += 4 + uint16le(&extra[idx + 2])) {
if (uint16le(&extra[idx]) == 0x0001) {
uint16_t size = uint16le(&extra[idx + 2]);
if (size + idx + 1 > entry->extralen) {
free(extra);
return false;
}
uint16_t offset = 0;
if (entry->uncompressed == UINT32_MAX && offset + 8 <= size) {
entry->uncompressed = uint64le(&extra[idx + 4 + offset]);
offset += 8;
}
if (entry->datasize == UINT32_MAX && offset + 8 <= size) {
entry->datasize = uint64le(&extra[idx + 4 + offset]);
offset += 8;
}
if (entry->header_offset == UINT32_MAX && offset + 8 <= size) {
entry->header_offset = (off64_t)uint64le(&extra[idx + 4 + offset]);
offset += 8;
}
if (entry->disk == UINT16_MAX && offset + 4 <= size) {
entry->disk = uint32le(&extra[idx + 4 + offset]);
offset += 4;
}
break;
}
}
free(extra);
return true;
}
bool zip_parse_local_file_entry(ar_archive_zip *zip, struct zip_entry *entry)
{
uint8_t data[ZIP_LOCAL_ENTRY_FIXED_SIZE];
if (ar_read(zip->super.stream, data, sizeof(data)) != sizeof(data))
return false;
memset(entry, 0, sizeof(*entry));
entry->signature = uint32le(data + 0);
entry->version = uint16le(data + 4);
entry->flags = uint16le(data + 6);
entry->method = uint16le(data + 8);
entry->dosdate = uint32le(data + 10);
entry->crc = uint32le(data + 14);
entry->datasize = uint32le(data + 18);
entry->uncompressed = uint32le(data + 22);
entry->namelen = uint16le(data + 26);
entry->extralen = uint16le(data + 28);
if (entry->signature != SIG_LOCAL_FILE_HEADER)
return false;
return zip_parse_extra_fields(zip, entry);
}
off64_t zip_find_next_local_file_entry(ar_stream *stream, off64_t offset)
{
uint8_t data[512];
int count, i;
if (!ar_seek(stream, offset, SEEK_SET))
return -1;
count = (int)ar_read(stream, data, sizeof(data));
while (count >= ZIP_LOCAL_ENTRY_FIXED_SIZE) {
for (i = 0; i < count - 4; i++) {
if (uint32le(data + i) == SIG_LOCAL_FILE_HEADER)
return offset + i;
}
memmove(data, data + count - 4, 4);
offset += count - 4;
count = (int)ar_read(stream, data + 4, sizeof(data) - 4) + 4;
}
return -1;
}
bool zip_parse_directory_entry(ar_archive_zip *zip, struct zip_entry *entry)
{
uint8_t data[ZIP_DIR_ENTRY_FIXED_SIZE];
if (ar_read(zip->super.stream, data, sizeof(data)) != sizeof(data))
return false;
entry->signature = uint32le(data + 0);
entry->version = uint16le(data + 4);
entry->min_version = uint16le(data + 6);
entry->flags = uint16le(data + 8);
entry->method = uint16le(data + 10);
entry->dosdate = uint32le(data + 12);
entry->crc = uint32le(data + 16);
entry->datasize = uint32le(data + 20);
entry->uncompressed = uint32le(data + 24);
entry->namelen = uint16le(data + 28);
entry->extralen = uint16le(data + 30);
entry->commentlen = uint16le(data + 32);
entry->disk = uint16le(data + 34);
entry->attr_internal = uint16le(data + 36);
entry->attr_external = uint32le(data + 38);
entry->header_offset = uint32le(data + 42);
if (entry->signature != SIG_CENTRAL_DIRECTORY)
return false;
return zip_parse_extra_fields(zip, entry);
}
off64_t zip_find_end_of_last_directory_entry(ar_stream *stream, struct zip_eocd64 *eocd)
{
uint8_t data[ZIP_DIR_ENTRY_FIXED_SIZE];
uint64_t i;
if (!ar_seek(stream, eocd->dir_offset, SEEK_SET))
return -1;
for (i = 0; i < eocd->numentries; i++) {
if (ar_read(stream, data, sizeof(data)) != sizeof(data))
return -1;
if (uint32le(data + 0) != SIG_CENTRAL_DIRECTORY)
return -1;
if (!ar_skip(stream, uint16le(data + 28) + uint16le(data + 30) + uint16le(data + 32)))
return -1;
}
return ar_tell(stream);
}
bool zip_parse_end_of_central_directory(ar_stream *stream, struct zip_eocd64 *eocd)
{
uint8_t data[56];
if (ar_read(stream, data, ZIP_END_OF_CENTRAL_DIR_SIZE) != ZIP_END_OF_CENTRAL_DIR_SIZE)
return false;
eocd->signature = uint32le(data + 0);
eocd->diskno = uint16le(data + 4);
eocd->diskno_dir = uint16le(data + 6);
eocd->numentries_disk = uint16le(data + 8);
eocd->numentries = uint16le(data + 10);
eocd->dir_size = uint32le(data + 12);
eocd->dir_offset = uint32le(data + 16);
eocd->commentlen = uint16le(data + 20);
if (eocd->signature != SIG_END_OF_CENTRAL_DIRECTORY)
return false;
/* try to locate the ZIP64 end of central directory */
if (!ar_skip(stream, -42))
return eocd->dir_size < 20;
if (ar_read(stream, data, 20) != 20)
return false;
if (uint32le(data + 0) != SIG_END_OF_CENTRAL_DIRECTORY_64_LOCATOR)
return true;
if ((eocd->diskno != UINT16_MAX && uint32le(data + 4) != eocd->diskno) || uint32le(data + 16) != 1) {
warn("Archive spanning isn't supported");
return false;
}
if (!ar_seek(stream, (off64_t)uint64le(data + 8), SEEK_SET))
return false;
if (ar_read(stream, data, 56) != 56)
return false;
/* use data from ZIP64 end of central directory (when necessary) */
eocd->signature = uint32le(data + 0);
eocd->version = uint16le(data + 12);
eocd->min_version = uint16le(data + 14);
if (eocd->diskno == UINT16_MAX)
eocd->diskno = uint32le(data + 16);
if (eocd->diskno_dir == UINT16_MAX)
eocd->diskno_dir = uint32le(data + 20);
if (eocd->numentries_disk == UINT16_MAX)
eocd->numentries_disk = uint64le(data + 24);
if (eocd->numentries == UINT16_MAX)
eocd->numentries = uint64le(data + 32);
if (eocd->dir_size == UINT32_MAX)
eocd->dir_size = uint64le(data + 40);
if (eocd->dir_offset == UINT32_MAX)
eocd->dir_offset = (off64_t)uint64le(data + 48);
if (eocd->signature != SIG_END_OF_CENTRAL_DIRECTORY_64)
return false;
if (eocd->diskno != eocd->diskno_dir || eocd->numentries != eocd->numentries_disk) {
warn("Archive spanning isn't supported");
return false;
}
if (uint64le(data + 4) > 44)
log("ZIP64 extensible data sector present @" PRIi64, ar_tell(stream));
return true;
}
off64_t zip_find_end_of_central_directory(ar_stream *stream)
{
uint8_t data[512];
off64_t filesize;
int fromend = 0;
int count, i;
if (!ar_seek(stream, 0, SEEK_END))
return -1;
filesize = ar_tell(stream);
while (fromend < UINT16_MAX + ZIP_END_OF_CENTRAL_DIR_SIZE && fromend < filesize) {
count = (filesize - fromend < (int)sizeof(data) ? (int)(filesize - fromend) : (int)sizeof(data));
fromend += count;
if (count < ZIP_END_OF_CENTRAL_DIR_SIZE)
return -1;
if (!ar_seek(stream, -fromend, SEEK_END))
return -1;
if (ar_read(stream, data, count) != (size_t)count)
return -1;
for (i = count - ZIP_END_OF_CENTRAL_DIR_SIZE; i >= 0; i--) {
if (uint32le(data + i) == SIG_END_OF_CENTRAL_DIRECTORY)
return filesize - fromend + i;
}
fromend -= ZIP_END_OF_CENTRAL_DIR_SIZE - 1;
}
return -1;
}
const char *zip_get_name(ar_archive *ar, bool raw)
{
ar_archive_zip *zip = (ar_archive_zip *)ar;
if (!zip->entry.name) {
struct zip_entry entry;
char *name;
if (zip->dir.end_offset >= 0) {
if (!ar_seek(ar->stream, ar->entry_offset, SEEK_SET))
return NULL;
if (!zip_parse_directory_entry(zip, &entry))
return NULL;
if (!ar_seek(ar->stream, ar->entry_offset + ZIP_DIR_ENTRY_FIXED_SIZE, SEEK_SET))
return NULL;
}
else {
if (!ar_seek(ar->stream, zip->entry.offset, SEEK_SET))
return NULL;
if (!zip_parse_local_file_entry(zip, &entry))
return NULL;
if (!ar_seek(ar->stream, ar->entry_offset + ZIP_LOCAL_ENTRY_FIXED_SIZE, SEEK_SET))
return NULL;
}
name = malloc(entry.namelen + 1);
if (!name || ar_read(ar->stream, name, entry.namelen) != entry.namelen) {
free(name);
return NULL;
}
name[entry.namelen] = '\0';
zip->entry.raw_name = malloc(entry.namelen + 1);
if (zip->entry.raw_name) {
memcpy(zip->entry.raw_name, name, entry.namelen + 1);
}
if ((entry.flags & (1 << 11))) {
zip->entry.name = name;
}
else {
zip->entry.name = ar_conv_dos_to_utf8(name);
free(name);
}
/* normalize path separators */
if (zip->entry.name) {
char *p = zip->entry.name;
while ((p = strchr(p, '\\')) != NULL) {
*p = '/';
}
}
}
return raw ? zip->entry.raw_name : zip->entry.name;
}

540
external/unarr/zip/uncompress-zip.c vendored Normal file
View File

@@ -0,0 +1,540 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
#include "zip.h"
#define ERR_UNCOMP UINT32_MAX
static bool zip_fill_input_buffer(ar_archive_zip *zip)
{
struct ar_archive_zip_uncomp *uncomp = &zip->uncomp;
size_t count;
if (uncomp->input.offset) {
memmove(&uncomp->input.data[0], &uncomp->input.data[uncomp->input.offset], uncomp->input.bytes_left);
uncomp->input.offset = 0;
}
count = sizeof(uncomp->input.data) - uncomp->input.bytes_left;
if (count > zip->progress.data_left)
count = zip->progress.data_left;
if (ar_read(zip->super.stream, &uncomp->input.data[uncomp->input.bytes_left], count) != count) {
warn("Unexpected EOF during decompression (invalid data size?)");
return false;
}
zip->progress.data_left -= count;
uncomp->input.bytes_left += (uint16_t)count;
uncomp->input.at_eof = !zip->progress.data_left;
return true;
}
/***** Deflate compression *****/
#ifdef HAVE_ZLIB
static void *gZlib_Alloc(void *opaque, uInt count, uInt size) { (void)opaque; return calloc(count, size); }
static void gZlib_Free(void *opaque, void *ptr) { (void)opaque; free(ptr); }
static bool zip_init_uncompress_deflate(struct ar_archive_zip_uncomp *uncomp)
{
int err;
uncomp->state.zstream.zalloc = gZlib_Alloc;
uncomp->state.zstream.zfree = gZlib_Free;
uncomp->state.zstream.opaque = NULL;
err = inflateInit2(&uncomp->state.zstream, -15);
return err == Z_OK;
}
static uint32_t zip_uncompress_data_deflate(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
int err;
uncomp->state.zstream.next_in = &uncomp->input.data[uncomp->input.offset];
uncomp->state.zstream.avail_in = uncomp->input.bytes_left;
uncomp->state.zstream.next_out = buffer;
uncomp->state.zstream.avail_out = buffer_size;
err = inflate(&uncomp->state.zstream, Z_SYNC_FLUSH);
uncomp->input.offset += uncomp->input.bytes_left - (uint16_t)uncomp->state.zstream.avail_in;
uncomp->input.bytes_left = (uint16_t)uncomp->state.zstream.avail_in;
if (err != Z_OK && err != Z_STREAM_END) {
warn("Unexpected ZLIB error %d", err);
return ERR_UNCOMP;
}
if (err == Z_STREAM_END && (!is_last_chunk || uncomp->state.zstream.avail_out)) {
warn("Premature EOS in Deflate stream");
return ERR_UNCOMP;
}
return buffer_size - uncomp->state.zstream.avail_out;
}
static void zip_clear_uncompress_deflate(struct ar_archive_zip_uncomp *uncomp)
{
inflateEnd(&uncomp->state.zstream);
}
#endif
/***** Deflate(64) compression *****/
static bool zip_init_uncompress_deflate64(struct ar_archive_zip_uncomp *uncomp, bool deflate64)
{
uncomp->state.inflate = inflate_create(deflate64);
return uncomp->state.inflate != NULL;
}
static uint32_t zip_uncompress_data_deflate64(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
size_t avail_in = uncomp->input.bytes_left;
size_t avail_out = buffer_size;
int result = inflate_process(uncomp->state.inflate, &uncomp->input.data[uncomp->input.offset], &avail_in, buffer, &avail_out);
uncomp->input.offset += uncomp->input.bytes_left - (uint16_t)avail_in;
uncomp->input.bytes_left = (uint16_t)avail_in;
if (result && result != EOF) {
warn("Unexpected Inflate error %d", result);
return ERR_UNCOMP;
}
if (result == EOF && (!is_last_chunk || avail_out)) {
warn("Premature EOS in Deflate stream");
return ERR_UNCOMP;
}
return buffer_size - (uint32_t)avail_out;
}
static void zip_clear_uncompress_deflate64(struct ar_archive_zip_uncomp *uncomp)
{
inflate_free(uncomp->state.inflate);
}
/***** BZIP2 compression *****/
#ifdef HAVE_BZIP2
static void *gBzip2_Alloc(void *opaque, int count, int size) { (void)opaque; return calloc(count, size); }
static void gBzip2_Free(void *opaque, void *ptr) { (void)opaque; free(ptr); }
static bool zip_init_uncompress_bzip2(struct ar_archive_zip_uncomp *uncomp)
{
int err;
uncomp->state.bstream.bzalloc = gBzip2_Alloc;
uncomp->state.bstream.bzfree = gBzip2_Free;
uncomp->state.bstream.opaque = NULL;
err = BZ2_bzDecompressInit(&uncomp->state.bstream, 0, 0);
return err == BZ_OK;
}
static uint32_t zip_uncompress_data_bzip2(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
int err;
uncomp->state.bstream.next_in = (char *)&uncomp->input.data[uncomp->input.offset];
uncomp->state.bstream.avail_in = uncomp->input.bytes_left;
uncomp->state.bstream.next_out = (char *)buffer;
uncomp->state.bstream.avail_out = buffer_size;
err = BZ2_bzDecompress(&uncomp->state.bstream);
uncomp->input.offset += uncomp->input.bytes_left - (uint16_t)uncomp->state.bstream.avail_in;
uncomp->input.bytes_left = (uint16_t)uncomp->state.bstream.avail_in;
if (err != BZ_OK && err != BZ_STREAM_END) {
warn("Unexpected BZIP2 error %d", err);
return ERR_UNCOMP;
}
if (err == BZ_STREAM_END && (!is_last_chunk || uncomp->state.bstream.avail_out)) {
warn("Premature EOS in BZIP2 stream");
return ERR_UNCOMP;
}
return buffer_size - uncomp->state.bstream.avail_out;
}
static void zip_clear_uncompress_bzip2(struct ar_archive_zip_uncomp *uncomp)
{
BZ2_bzDecompressEnd(&uncomp->state.bstream);
}
#endif
/***** LZMA compression *****/
#ifdef HAVE_LIBLZMA
static void *gLzma_Alloc(void *opaque, size_t nmemb, size_t size)
{ (void)opaque; (void) nmemb; return malloc(size); }
static void gLzma_Free(void *opaque, void *ptr)
{ (void)opaque; free(ptr); }
static bool zip_init_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp)
{
lzma_stream strm = LZMA_STREAM_INIT;
uncomp->state.lzmastream = strm;
#if LZMA_VERSION_MAJOR > 5 || (LZMA_VERSION_MAJOR == 5 && LZMA_VERSION_MINOR >= 2)
static const lzma_allocator allocator = { gLzma_Alloc, gLzma_Free, NULL };
#else
static lzma_allocator allocator = { gLzma_Alloc, gLzma_Free, NULL };
#endif
uncomp->state.lzmastream.allocator = &allocator;
return true;
}
static uint32_t zip_uncompress_data_lzma1(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
int err;
if (uncomp->state.lzmastream.internal == NULL) {
uint8_t propsize;
propsize = uncomp->input.data[uncomp->input.offset + 2];
lzma_filter filters[2] = {{.id=LZMA_FILTER_LZMA1, .options=NULL},
{.id=LZMA_VLI_UNKNOWN, .options=NULL}};
err = lzma_properties_decode(
&filters[0], NULL,
&uncomp->input.data[uncomp->input.offset + 4], propsize);
if (err != LZMA_OK) {
warn("Properties error %d", err);
return ERR_UNCOMP;
}
err = lzma_raw_decoder(&uncomp->state.lzmastream, filters);
free(filters[0].options);
if (err != LZMA_OK) {
warn("Decoder init error %d", err);
return ERR_UNCOMP;
}
uncomp->input.offset += 4 + propsize;
uncomp->input.bytes_left -= 4 + propsize;
}
uncomp->state.lzmastream.next_in = &uncomp->input.data[uncomp->input.offset];
uncomp->state.lzmastream.avail_in = uncomp->input.bytes_left;
uncomp->state.lzmastream.next_out = buffer;
uncomp->state.lzmastream.avail_out = buffer_size;
err = lzma_code(&uncomp->state.lzmastream, LZMA_RUN);
uncomp->input.offset += (uint16_t)uncomp->input.bytes_left - (uint16_t)uncomp->state.lzmastream.avail_in;
uncomp->input.bytes_left = (uint16_t)uncomp->state.lzmastream.avail_in;
if (err != LZMA_OK && err != LZMA_STREAM_END) {
warn("Unexpected LZMA error %d", err);
warn("%d", buffer_size - uncomp->state.lzmastream.avail_out);
return ERR_UNCOMP;
}
if (err == LZMA_STREAM_END && (!is_last_chunk || uncomp->state.lzmastream.avail_out)) {
warn("Premature EOS in LZMA stream");
return ERR_UNCOMP;
}
return buffer_size - uncomp->state.lzmastream.avail_out;
}
static uint32_t zip_uncompress_data_xz(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
int err;
if (uncomp->state.lzmastream.internal == NULL) {
/* restrict decoder memory usage to 100 MB */
err = lzma_stream_decoder(&uncomp->state.lzmastream, 100 << 20, 0);
if (err != LZMA_OK) {
warn("Unexpected LZMA Decoder init error %d", err);
return ERR_UNCOMP;
}
}
uncomp->state.lzmastream.next_in = &uncomp->input.data[uncomp->input.offset];
uncomp->state.lzmastream.avail_in = uncomp->input.bytes_left;
uncomp->state.lzmastream.next_out = buffer;
uncomp->state.lzmastream.avail_out = buffer_size;
err = lzma_code(&uncomp->state.lzmastream, LZMA_RUN);
uncomp->input.offset += (uint16_t)uncomp->input.bytes_left - (uint16_t)uncomp->state.lzmastream.avail_in;
uncomp->input.bytes_left = (uint16_t)uncomp->state.lzmastream.avail_in;
if (err != LZMA_OK && err != LZMA_STREAM_END) {
warn("Unexpected XZ error %d", err);
warn("%d", buffer_size - uncomp->state.lzmastream.avail_out);
return ERR_UNCOMP;
}
if (err == LZMA_STREAM_END && (!is_last_chunk || uncomp->state.lzmastream.avail_out)) {
warn("Premature EOS in XZ stream");
return ERR_UNCOMP;
}
return buffer_size - uncomp->state.lzmastream.avail_out;
}
static void zip_clear_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp)
{
lzma_end(&uncomp->state.lzmastream);
}
#else //HAVE_LIBLZMA
static void *gLzma_Alloc(ISzAllocPtr self, size_t size) { (void)self; return malloc(size); }
static void gLzma_Free(ISzAllocPtr self, void *ptr) { (void)self; free(ptr); }
static bool zip_init_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp, uint16_t flags)
{
uncomp->state.lzma.alloc.Alloc = gLzma_Alloc;
uncomp->state.lzma.alloc.Free = gLzma_Free;
uncomp->state.lzma.finish = (flags & (1 << 1)) ? LZMA_FINISH_END : LZMA_FINISH_ANY;
LzmaDec_Construct(&uncomp->state.lzma.dec);
return true;
}
static uint32_t zip_uncompress_data_lzma(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
SizeT srclen, dstlen;
ELzmaStatus status;
ELzmaFinishMode finish;
SRes res;
if (!uncomp->state.lzma.dec.dic) {
uint8_t propsize;
if (uncomp->input.bytes_left < 9) {
warn("Insufficient data in compressed stream");
return ERR_UNCOMP;
}
propsize = uncomp->input.data[uncomp->input.offset + 2];
if (uncomp->input.data[uncomp->input.offset + 3] != 0 || uncomp->input.bytes_left < 4 + propsize) {
warn("Insufficient data in compressed stream");
return ERR_UNCOMP;
}
res = LzmaDec_Allocate(&uncomp->state.lzma.dec, &uncomp->input.data[uncomp->input.offset + 4], propsize, &uncomp->state.lzma.alloc);
uncomp->input.offset += 4 + propsize;
uncomp->input.bytes_left -= 4 + propsize;
if (res != SZ_OK)
return ERR_UNCOMP;
LzmaDec_Init(&uncomp->state.lzma.dec);
}
srclen = uncomp->input.bytes_left;
dstlen = buffer_size;
finish = uncomp->input.at_eof && is_last_chunk ? uncomp->state.lzma.finish : LZMA_FINISH_ANY;
res = LzmaDec_DecodeToBuf(&uncomp->state.lzma.dec, buffer, &dstlen, &uncomp->input.data[uncomp->input.offset], &srclen, finish, &status);
uncomp->input.offset += (uint16_t)srclen;
uncomp->input.bytes_left -= (uint16_t)srclen;
if (res != SZ_OK || (srclen == 0 && dstlen == 0)) {
warn("Unexpected LZMA error %d", res);
return ERR_UNCOMP;
}
if (status == LZMA_STATUS_FINISHED_WITH_MARK && (!is_last_chunk || dstlen != buffer_size)) {
warn("Premature EOS in LZMA stream");
return ERR_UNCOMP;
}
return (uint32_t)dstlen;
}
static void zip_clear_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp)
{
LzmaDec_Free(&uncomp->state.lzma.dec, &uncomp->state.lzma.alloc);
}
#endif //HAVE_LIBLZMA
/***** PPMd compression *****/
static void *gPpmd_Alloc(ISzAllocPtr self, size_t size) { (void)self; return malloc(size); }
static void gPpmd_Free(ISzAllocPtr self, void *ptr) { (void)self; free(ptr); }
static Byte gPpmd_ByteIn_Read(const IByteIn *p)
{
struct ByteReader *self = (struct ByteReader *) p;
if (!self->input->bytes_left && (!self->zip->progress.data_left || !zip_fill_input_buffer(self->zip)))
return 0xFF;
self->input->bytes_left--;
return self->input->data[self->input->offset++];
}
static bool zip_init_uncompress_ppmd(ar_archive_zip *zip)
{
struct ar_archive_zip_uncomp *uncomp = &zip->uncomp;
uncomp->state.ppmd8.alloc.Alloc = gPpmd_Alloc;
uncomp->state.ppmd8.alloc.Free = gPpmd_Free;
uncomp->state.ppmd8.bytein.super.Read = gPpmd_ByteIn_Read;
uncomp->state.ppmd8.bytein.input = &uncomp->input;
uncomp->state.ppmd8.bytein.zip = zip;
uncomp->state.ppmd8.ctx.Stream.In = &uncomp->state.ppmd8.bytein.super;
Ppmd8_Construct(&uncomp->state.ppmd8.ctx);
return true;
}
static uint32_t zip_uncompress_data_ppmd(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
uint32_t bytes_done = 0;
if (!uncomp->state.ppmd8.ctx.Base) {
uint8_t order, size, method;
if (uncomp->input.bytes_left < 2) {
warn("Insufficient data in compressed stream");
return ERR_UNCOMP;
}
order = (uncomp->input.data[uncomp->input.offset] & 0x0F) + 1;
size = ((uncomp->input.data[uncomp->input.offset] >> 4) | ((uncomp->input.data[uncomp->input.offset + 1] << 4) & 0xFF));
method = uncomp->input.data[uncomp->input.offset + 1] >> 4;
uncomp->input.bytes_left -= 2;
uncomp->input.offset += 2;
if (order < 2 || method > 2) {
warn("Invalid PPMd data stream");
return ERR_UNCOMP;
}
#ifndef PPMD8_FREEZE_SUPPORT
if (order == 2) {
warn("PPMd freeze method isn't supported");
return ERR_UNCOMP;
}
#endif
if (!Ppmd8_Alloc(&uncomp->state.ppmd8.ctx, (size + 1) << 20, &uncomp->state.ppmd8.alloc))
return ERR_UNCOMP;
if (!Ppmd8_Init_RangeDec(&uncomp->state.ppmd8.ctx))
return ERR_UNCOMP;
Ppmd8_Init(&uncomp->state.ppmd8.ctx, order, method);
}
while (bytes_done < buffer_size) {
int symbol = Ppmd8_DecodeSymbol(&uncomp->state.ppmd8.ctx);
if (symbol < 0) {
warn("Invalid PPMd data stream");
return ERR_UNCOMP;
}
((uint8_t *)buffer)[bytes_done++] = (uint8_t)symbol;
}
if (is_last_chunk) {
int symbol = Ppmd8_DecodeSymbol(&uncomp->state.ppmd8.ctx);
if (symbol != -1 || !Ppmd8_RangeDec_IsFinishedOK(&uncomp->state.ppmd8.ctx)) {
warn("Invalid PPMd data stream");
return ERR_UNCOMP;
}
}
return bytes_done;
}
static void zip_clear_uncompress_ppmd(struct ar_archive_zip_uncomp *uncomp)
{
Ppmd8_Free(&uncomp->state.ppmd8.ctx, &uncomp->state.ppmd8.alloc);
}
/***** common decompression handling *****/
static bool zip_init_uncompress(ar_archive_zip *zip)
{
struct ar_archive_zip_uncomp *uncomp = &zip->uncomp;
if (uncomp->initialized)
return true;
memset(uncomp, 0, sizeof(*uncomp));
if (zip->entry.method == METHOD_DEFLATE) {
#ifdef HAVE_ZLIB
if (zip_init_uncompress_deflate(uncomp)) {
uncomp->uncompress_data = zip_uncompress_data_deflate;
uncomp->clear_state = zip_clear_uncompress_deflate;
}
#else
if (zip_init_uncompress_deflate64(uncomp, false)) {
uncomp->uncompress_data = zip_uncompress_data_deflate64;
uncomp->clear_state = zip_clear_uncompress_deflate64;
}
#endif
}
else if (zip->entry.method == METHOD_DEFLATE64) {
if (zip_init_uncompress_deflate64(uncomp, true)) {
uncomp->uncompress_data = zip_uncompress_data_deflate64;
uncomp->clear_state = zip_clear_uncompress_deflate64;
}
}
else if (zip->entry.method == METHOD_BZIP2) {
#ifdef HAVE_BZIP2
if (zip_init_uncompress_bzip2(uncomp)) {
uncomp->uncompress_data = zip_uncompress_data_bzip2;
uncomp->clear_state = zip_clear_uncompress_bzip2;
}
#else
warn("BZIP2 support requires BZIP2 (define HAVE_BZIP2)");
#endif
}
#ifdef HAVE_LIBLZMA
else if (zip->entry.method == METHOD_LZMA) {
if (zip_init_uncompress_lzma(uncomp)) {
uncomp->uncompress_data = zip_uncompress_data_lzma1;
uncomp->clear_state = zip_clear_uncompress_lzma;
}
}
else if (zip->entry.method == METHOD_XZ) {
if (zip_init_uncompress_lzma(uncomp)) {
uncomp->uncompress_data = zip_uncompress_data_xz;
uncomp->clear_state = zip_clear_uncompress_lzma;
}
}
#else
else if (zip->entry.method == METHOD_LZMA) {
if (zip_init_uncompress_lzma(uncomp, zip->entry.flags)) {
uncomp->uncompress_data = zip_uncompress_data_lzma;
uncomp->clear_state = zip_clear_uncompress_lzma;
}
}
#endif // HAVE_LIBLZMA
else if (zip->entry.method == METHOD_PPMD) {
if (zip_init_uncompress_ppmd(zip)) {
uncomp->uncompress_data = zip_uncompress_data_ppmd;
uncomp->clear_state = zip_clear_uncompress_ppmd;
}
}
else
warn("Unsupported compression method %d", zip->entry.method);
uncomp->initialized = uncomp->uncompress_data != NULL && uncomp->clear_state != NULL;
return uncomp->initialized;
}
void zip_clear_uncompress(struct ar_archive_zip_uncomp *uncomp)
{
if (!uncomp->initialized)
return;
uncomp->clear_state(uncomp);
uncomp->initialized = false;
}
bool zip_uncompress_part(ar_archive_zip *zip, void *buffer, size_t buffer_size)
{
struct ar_archive_zip_uncomp *uncomp = &zip->uncomp;
uint32_t count;
if (!zip_init_uncompress(zip))
return false;
for (;;) {
if (buffer_size == 0)
return true;
if (uncomp->input.bytes_left < sizeof(uncomp->input.data) / 2 && zip->progress.data_left) {
if (!zip_fill_input_buffer(zip))
return false;
}
count = buffer_size >= UINT32_MAX ? UINT32_MAX - 1 : (uint32_t)buffer_size;
count = uncomp->uncompress_data(uncomp, buffer, count, zip->progress.bytes_done + count == zip->super.entry_size_uncompressed);
if (count == ERR_UNCOMP)
return false;
if (count == 0 && !zip->progress.data_left) {
warn("Insufficient data in compressed stream");
return false;
}
zip->progress.bytes_done += count;
buffer = (uint8_t *)buffer + count;
buffer_size -= count;
}
}

219
external/unarr/zip/zip.c vendored Normal file
View File

@@ -0,0 +1,219 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
#include "zip.h"
static void zip_close(ar_archive *ar)
{
ar_archive_zip *zip = (ar_archive_zip *)ar;
free(zip->entry.name);
free(zip->entry.raw_name);
zip_clear_uncompress(&zip->uncomp);
}
static bool zip_parse_local_entry(ar_archive *ar, off64_t offset)
{
ar_archive_zip *zip = (ar_archive_zip *)ar;
struct zip_entry entry;
offset = zip_find_next_local_file_entry(ar->stream, offset);
if (offset < 0) {
if (ar->entry_offset_next)
ar->at_eof = true;
else
warn("Work around failed, no entries found in this file");
return false;
}
if (!ar_seek(ar->stream, offset, SEEK_SET)) {
warn("Couldn't seek to offset %" PRIi64, offset);
return false;
}
if (!zip_parse_local_file_entry(zip, &entry))
return false;
ar->entry_offset = offset;
ar->entry_offset_next = offset + ZIP_LOCAL_ENTRY_FIXED_SIZE + entry.namelen + entry.extralen + (off64_t)entry.datasize;
if (ar->entry_offset_next <= ar->entry_offset) {
warn("Compressed size is too large (%" PRIu64 ")", entry.datasize);
return false;
}
ar->entry_size_uncompressed = (size_t)entry.uncompressed;
ar->entry_filetime = ar_conv_dosdate_to_filetime(entry.dosdate);
zip->entry.offset = offset;
zip->entry.method = entry.method;
zip->entry.flags = entry.flags;
zip->entry.crc = entry.crc;
free(zip->entry.name);
zip->entry.name = NULL;
free(zip->entry.raw_name);
zip->entry.raw_name = NULL;
zip->entry.dosdate = entry.dosdate;
zip->progress.crc = 0;
zip->progress.bytes_done = 0;
zip->progress.data_left = (size_t)entry.datasize;
zip_clear_uncompress(&zip->uncomp);
if (entry.datasize == 0 && ar_entry_get_name(ar) &&
zip->entry.name != NULL && *zip->entry.name &&
zip->entry.name[strlen(zip->entry.name) - 1] == '/') {
log("Skipping directory entry \"%s\"", zip->entry.name);
return zip_parse_local_entry(ar, ar->entry_offset_next);
}
if (entry.datasize == 0 && entry.uncompressed == 0 && (entry.flags & (1 << 3))) {
warn("Deferring sizes to data descriptor isn't supported");
ar->entry_size_uncompressed = 1;
}
return true;
}
static bool zip_parse_entry(ar_archive *ar, off64_t offset)
{
ar_archive_zip *zip = (ar_archive_zip *)ar;
struct zip_entry entry;
if (offset >= zip->dir.end_offset) {
ar->at_eof = true;
return false;
}
if (!ar_seek(ar->stream, offset, SEEK_SET)) {
warn("Couldn't seek to offset %" PRIi64, offset);
return false;
}
if (!zip_parse_directory_entry(zip, &entry)) {
warn("Couldn't read directory entry @%" PRIi64, offset);
return false;
}
ar->entry_offset = offset;
ar->entry_offset_next = offset + ZIP_DIR_ENTRY_FIXED_SIZE + entry.namelen + entry.extralen + entry.commentlen;
ar->entry_size_uncompressed = (size_t)entry.uncompressed;
ar->entry_filetime = ar_conv_dosdate_to_filetime(entry.dosdate);
zip->entry.offset = entry.header_offset;
zip->entry.method = entry.method;
zip->entry.flags = entry.flags;
zip->entry.crc = entry.crc;
free(zip->entry.name);
zip->entry.name = NULL;
free(zip->entry.raw_name);
zip->entry.raw_name = NULL;
zip->entry.dosdate = entry.dosdate;
zip->progress.crc = 0;
zip->progress.bytes_done = 0;
zip->progress.data_left = (size_t)entry.datasize;
zip_clear_uncompress(&zip->uncomp);
if (entry.datasize == 0 && ((entry.version >> 8) == 0 || (entry.version >> 8) == 3) && (entry.attr_external & 0x40000010)) {
log("Skipping directory entry \"%s\"", zip_get_name(ar, false));
return zip_parse_entry(ar, ar->entry_offset_next);
}
return true;
}
static bool zip_copy_stored(ar_archive_zip *zip, void *buffer, size_t count)
{
if (count > zip->progress.data_left) {
warn("Unexpected EOS in stored data");
return false;
}
if (ar_read(zip->super.stream, buffer, count) != count) {
warn("Unexpected EOF in stored data");
return false;
}
zip->progress.data_left -= count;
zip->progress.bytes_done += count;
return true;
}
static bool zip_uncompress(ar_archive *ar, void *buffer, size_t count)
{
ar_archive_zip *zip = (ar_archive_zip *)ar;
if (zip->progress.bytes_done == 0) {
if ((zip->entry.flags & ((1 << 0) | (1 << 6)))) {
warn("Encrypted archives aren't supported");
return false;
}
if (!zip_seek_to_compressed_data(zip)) {
warn("Couldn't find data for file");
return false;
}
}
if (count > ar->entry_size_uncompressed - zip->progress.bytes_done) {
warn("Requesting too much data (%" PRIuPTR " < %" PRIuPTR ")", ar->entry_size_uncompressed - zip->progress.bytes_done, count);
return false;
}
if (zip->entry.method == METHOD_STORE) {
if (!zip_copy_stored(zip, buffer, count))
return false;
}
else if (zip->deflatedonly && zip->entry.method != METHOD_DEFLATE) {
warn("Only store and deflate compression methods are allowed");
return false;
}
else {
if (!zip_uncompress_part(zip, buffer, count))
return false;
}
zip->progress.crc = ar_crc32(zip->progress.crc, buffer, count);
if (zip->progress.bytes_done < ar->entry_size_uncompressed)
return true;
if (zip->uncomp.initialized ? !zip->uncomp.input.at_eof || zip->uncomp.input.bytes_left : zip->progress.data_left)
log("Compressed block has more data than required");
if (zip->progress.crc != zip->entry.crc) {
warn("Checksum of extracted data doesn't match");
return false;
}
return true;
}
static size_t zip_get_global_comment(ar_archive *ar, void *buffer, size_t count)
{
ar_archive_zip *zip = (ar_archive_zip *)ar;
if (!zip->comment_size)
return 0;
if (!buffer)
return zip->comment_size;
if (!ar_seek(ar->stream, zip->comment_offset, SEEK_SET))
return 0;
if (count > zip->comment_size)
count = zip->comment_size;
return ar_read(ar->stream, buffer, count);
}
ar_archive *ar_open_zip_archive(ar_stream *stream, bool deflatedonly)
{
ar_archive *ar;
ar_archive_zip *zip;
struct zip_eocd64 eocd = { 0 };
off64_t offset = zip_find_end_of_central_directory(stream);
if (offset < 0)
return NULL;
if (!ar_seek(stream, offset, SEEK_SET))
return NULL;
if (!zip_parse_end_of_central_directory(stream, &eocd))
return NULL;
ar = ar_open_archive(stream, sizeof(ar_archive_zip), zip_close, zip_parse_entry, zip_get_name, zip_uncompress, zip_get_global_comment, eocd.dir_offset);
if (!ar)
return NULL;
zip = (ar_archive_zip *)ar;
zip->dir.end_offset = zip_find_end_of_last_directory_entry(stream, &eocd);
if (zip->dir.end_offset < 0) {
warn("Couldn't read central directory @%" PRIi64 ", trying to work around...", eocd.dir_offset);
ar->parse_entry = zip_parse_local_entry;
ar->entry_offset_first = ar->entry_offset_next = 0;
}
zip->deflatedonly = deflatedonly;
zip->comment_offset = offset + ZIP_END_OF_CENTRAL_DIR_SIZE;
zip->comment_size = eocd.commentlen;
return ar;
}

173
external/unarr/zip/zip.h vendored Normal file
View File

@@ -0,0 +1,173 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
#ifndef zip_zip_h
#define zip_zip_h
#include "../common/unarr-imp.h"
#ifdef HAVE_ZLIB
#include <zlib.h>
#endif
#include "inflate.h"
#ifdef HAVE_BZIP2
#include <bzlib.h>
#endif
#ifdef HAVE_LIBLZMA
#include <lzma.h>
#else
#include "../lzmasdk/LzmaDec.h"
#endif
#include "../lzmasdk/Ppmd8.h"
typedef struct ar_archive_zip_s ar_archive_zip;
/***** parse-zip *****/
enum zip_signatures {
SIG_LOCAL_FILE_HEADER = 0x04034B50,
SIG_CENTRAL_DIRECTORY = 0x02014B50,
SIG_END_OF_CENTRAL_DIRECTORY_64 = 0x06064B50,
SIG_END_OF_CENTRAL_DIRECTORY_64_LOCATOR = 0x07064B50,
SIG_END_OF_CENTRAL_DIRECTORY = 0x06054B50,
};
enum compression_method {
METHOD_STORE = 0, METHOD_DEFLATE = 8,
METHOD_DEFLATE64 = 9, METHOD_BZIP2 = 12, METHOD_LZMA = 14,
METHOD_XZ = 95, METHOD_PPMD = 98,
};
#define ZIP_LOCAL_ENTRY_FIXED_SIZE 30
#define ZIP_DIR_ENTRY_FIXED_SIZE 46
#define ZIP_END_OF_CENTRAL_DIR_SIZE 22
struct zip_entry {
uint32_t signature;
uint16_t version;
uint16_t min_version;
uint16_t flags;
uint16_t method;
uint32_t dosdate;
uint32_t crc;
uint64_t datasize;
uint64_t uncompressed;
uint16_t namelen;
uint16_t extralen;
uint16_t commentlen;
uint32_t disk;
uint16_t attr_internal;
uint32_t attr_external;
off64_t header_offset;
};
struct zip_eocd64 {
uint32_t signature;
uint16_t version;
uint16_t min_version;
uint32_t diskno;
uint32_t diskno_dir;
uint64_t numentries_disk;
uint64_t numentries;
uint64_t dir_size;
off64_t dir_offset;
uint16_t commentlen;
};
struct ar_archive_zip_entry {
off64_t offset;
uint16_t method;
uint16_t flags;
uint32_t crc;
char *name;
char *raw_name;
uint32_t dosdate;
};
bool zip_seek_to_compressed_data(ar_archive_zip *zip);
bool zip_parse_local_file_entry(ar_archive_zip *zip, struct zip_entry *entry);
off64_t zip_find_next_local_file_entry(ar_stream *stream, off64_t offset);
bool zip_parse_directory_entry(ar_archive_zip *zip, struct zip_entry *entry);
off64_t zip_find_end_of_last_directory_entry(ar_stream *stream, struct zip_eocd64 *eocd);
bool zip_parse_end_of_central_directory(ar_stream *stream, struct zip_eocd64 *eocd);
off64_t zip_find_end_of_central_directory(ar_stream *stream);
const char *zip_get_name(ar_archive *ar, bool raw);
/***** uncompress-zip *****/
struct ar_archive_zip_uncomp;
typedef uint32_t (* zip_uncomp_uncompress_data_fn)(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk);
typedef void (* zip_uncomp_clear_state_fn)(struct ar_archive_zip_uncomp *uncomp);
struct InputBuffer {
uint8_t data[4096];
uint16_t offset;
uint16_t bytes_left;
bool at_eof;
};
struct ByteReader {
IByteIn super;
struct InputBuffer *input;
ar_archive_zip *zip;
};
struct ar_archive_zip_uncomp {
bool initialized;
zip_uncomp_uncompress_data_fn uncompress_data;
zip_uncomp_clear_state_fn clear_state;
union {
#ifdef HAVE_ZLIB
z_stream zstream;
#endif
inflate_state *inflate;
#ifdef HAVE_BZIP2
bz_stream bstream;
#endif
#ifdef HAVE_LIBLZMA
lzma_stream lzmastream;
#else
struct {
CLzmaDec dec;
ELzmaFinishMode finish;
ISzAlloc alloc;
} lzma;
#endif //HAVE_LIBLZMA
struct {
CPpmd8 ctx;
struct ByteReader bytein;
ISzAlloc alloc;
} ppmd8;
} state;
struct InputBuffer input;
};
bool zip_uncompress_part(ar_archive_zip *zip, void *buffer, size_t buffer_size);
void zip_clear_uncompress(struct ar_archive_zip_uncomp *uncomp);
/***** zip *****/
struct ar_archive_zip_dir {
/* off64_t offset; // use ar_archive::entry_offset_first */
off64_t end_offset;
};
struct ar_archive_zip_progress {
size_t data_left;
size_t bytes_done;
uint32_t crc;
};
struct ar_archive_zip_s {
ar_archive super;
struct ar_archive_zip_dir dir;
struct ar_archive_zip_entry entry;
struct ar_archive_zip_uncomp uncomp;
struct ar_archive_zip_progress progress;
bool deflatedonly;
off64_t comment_offset;
uint16_t comment_size;
};
#endif