rwops_zipfile: support manual zstd decompression

This commit is contained in:
Andrei Alexeyev 2021-03-25 20:54:22 +02:00
parent 3df55eb6ac
commit 12227f0348
No known key found for this signature in database
GPG key ID: 72D26128040B9690
4 changed files with 70 additions and 31 deletions

View file

@ -20,7 +20,7 @@ Dependencies
- freetype2
- libpng >= 1.5.0
- libwebpdecoder >= 0.5 or libwebp >= 0.5
- libzip >= 1.2
- libzip >= 1.7
- libzstd >= 1.3.0
- opusfile
- zlib

View file

@ -10,24 +10,37 @@
#include "rwops_zipfile.h"
#include "rwops_util.h"
#include "rwops_zstd.h"
#include "util.h"
#define FORCE_MANUAL_DECOMPRESSION (0)
#if FORCE_MANUAL_DECOMPRESSION
#include "rwops_zlib.h"
#endif
typedef struct ZipRWData {
VFSNode *node;
zip_file_t *file;
int64_t pos;
int64_t size;
zip_flags_t open_flags;
} ZipRWData;
#define ZTLS(pdata) vfs_zipfile_get_tls((pdata)->zipnode, true)
static zip_file_t *ziprw_open(VFSZipPathData *pdata) {
zip_file_t *zipfile = zip_fopen_index(ZTLS(pdata)->zip, pdata->index, 0);
static int ziprw_reopen(SDL_RWops *rw) {
ZipRWData *rwdata = rw->hidden.unknown.data1;
VFSZipPathData *pdata = rw->hidden.unknown.data2;
if(!zipfile) {
SDL_SetError("ZIP error: %s", zip_error_strerror(&ZTLS(pdata)->error));
if(rwdata->file) {
zip_fclose(rwdata->file);
}
return zipfile;
rwdata->file = zip_fopen_index(ZTLS(pdata)->zip, pdata->index, rwdata->open_flags);
rwdata->pos = 0;
return rwdata->file ? 0 : -1;
}
static zip_file_t *ziprw_get_zipfile(SDL_RWops *rw) {
@ -39,11 +52,9 @@ static zip_file_t *ziprw_get_zipfile(SDL_RWops *rw) {
if(!vfs_zipfile_get_tls((pdata)->zipnode, false) && rwdata->file) {
zip_fclose(rwdata->file);
rwdata->file = NULL;
rwdata->pos = 0;
}
if(!rwdata->file) {
rwdata->file = ziprw_open(pdata);
if(!rwdata->file && ziprw_reopen(rw) >= 0) {
SDL_RWseek(rw, pos, RW_SEEK_SET);
}
@ -82,19 +93,6 @@ static int64_t ziprw_seek(SDL_RWops *rw, int64_t offset, int whence) {
return -1;
}
static int rwzip_reopen(SDL_RWops *rw) {
ZipRWData *rwdata = rw->hidden.unknown.data1;
VFSZipPathData *pdata = rw->hidden.unknown.data2;
log_debug("Reopening %s", zip_get_name(ZTLS(pdata)->zip, pdata->index, ZIP_FL_ENC_RAW));
zip_fclose(rwdata->file);
rwdata->file = ziprw_open(pdata);
rwdata->pos = 0;
return rwdata->file ? 0 : -1;
}
static int64_t ziprw_seek_emulated(SDL_RWops *rw, int64_t offset, int whence) {
ZipRWData *rwdata = rw->hidden.unknown.data1;
@ -106,24 +104,22 @@ static int64_t ziprw_seek_emulated(SDL_RWops *rw, int64_t offset, int whence) {
return rwutil_seek_emulated(
rw, offset, whence,
&rwdata->pos, rwzip_reopen, sizeof(buf), buf
&rwdata->pos, ziprw_reopen, sizeof(buf), buf
);
}
static int64_t ziprw_size(SDL_RWops *rw) {
VFSZipPathData *pdata = rw->hidden.unknown.data2;
ZipRWData *rwdata = rw->hidden.unknown.data1;
if(pdata->size < 0) {
SDL_SetError("zip_stat_index() failed");
return -1;
if(rwdata->size < 0) {
return SDL_SetError("zip_stat_index() failed");
}
return pdata->size;
return rwdata->size;
}
static size_t ziprw_read(SDL_RWops *rw, void *ptr, size_t size, size_t maxnum) {
ZipRWData *rwdata = rw->hidden.unknown.data1;
VFSZipPathData *pdata = rw->hidden.unknown.data2;
libzip_sucks:
@ -146,7 +142,7 @@ libzip_sucks:
return 0;
}
if(read_size > 0 && bytes_read == 0 && rw->seek == ziprw_seek && zip_ftell(rwdata->file) < pdata->size) {
if(read_size > 0 && bytes_read == 0 && rw->seek == ziprw_seek && zip_ftell(rwdata->file) < rwdata->size) {
log_debug("libzip BUG: EOF flag not cleared after seek, reopening file");
zip_fclose(rwdata->file);
rwdata->file = NULL;
@ -168,6 +164,7 @@ SDL_RWops *SDL_RWFromZipFile(VFSNode *znode, VFSZipPathData *pdata) {
ZipRWData *rwdata = calloc(1, sizeof(*rwdata));
rwdata->node = znode;
rwdata->size = pdata->size;
vfs_incref(znode);
@ -180,11 +177,43 @@ SDL_RWops *SDL_RWFromZipFile(VFSNode *znode, VFSZipPathData *pdata) {
rw->read = ziprw_read;
rw->write = ziprw_write;
DIAGNOSTIC(push)
DIAGNOSTIC(ignored "-Wunreachable-code")
if(pdata->compression == ZIP_CM_STORE) {
rw->seek = ziprw_seek;
} else {
} else if(
!FORCE_MANUAL_DECOMPRESSION &&
zip_compression_method_supported(pdata->compression, false)
) {
rw->seek = ziprw_seek_emulated;
} else {
rw->seek = ziprw_seek;
rwdata->size = pdata->compressed_size;
rwdata->open_flags = ZIP_FL_COMPRESSED;
switch(pdata->compression) {
#if FORCE_MANUAL_DECOMPRESSION
case ZIP_CM_DEFLATE:
rw = SDL_RWWrapInflateReaderSeekable(rw, pdata->size, imin(4096, pdata->size), true);
break;
#endif
case ZIP_CM_ZSTD: {
rw = SDL_RWWrapZstdReaderSeekable(rw, pdata->size, true);
break;
}
default: {
char *fname = vfs_node_repr(znode, true);
SDL_SetError("%s: unsupported compression method: %i", fname, pdata->compression);
SDL_RWclose(rw);
return NULL;
}
}
}
DIAGNOSTIC(pop)
return rw;
}

View file

@ -17,6 +17,10 @@ DIAGNOSTIC_CLANG(ignored "-Wnullability-extension")
#include <zip.h>
DIAGNOSTIC_CLANG(pop)
#ifndef ZIP_CM_ZSTD
#define ZIP_CM_ZSTD 93
#endif
#include "private.h"
#include "hashtable.h"
@ -51,6 +55,7 @@ typedef struct VFSZipPathData {
VFSNode *zipnode;
uint64_t index;
ssize_t size;
ssize_t compressed_size;
VFSInfo info;
uint16_t compression;
} VFSZipPathData;

View file

@ -115,6 +115,7 @@ void vfs_zippath_init(VFSNode *node, VFSNode *zipnode, zip_int64_t idx) {
zdata->zipnode = zipnode;
zdata->index = idx;
zdata->size = -1;
zdata->compressed_size = -1;
zdata->compression = ZIP_CM_STORE;
node->data1 = zdata;
@ -134,6 +135,10 @@ void vfs_zippath_init(VFSNode *node, VFSNode *zipnode, zip_int64_t idx) {
zdata->size = zstat.size;
}
if(zstat.valid & ZIP_STAT_COMP_SIZE) {
zdata->compressed_size = zstat.comp_size;
}
if(zstat.valid & ZIP_STAT_COMP_METHOD) {
zdata->compression = zstat.comp_method;
}