taisei/src/vfs/zippath.c
2021-03-25 20:54:22 +02:00

153 lines
4.2 KiB
C

/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "zipfile.h"
#include "zipfile_impl.h"
#include "syspath.h"
#include "rwops/all.h"
#define ZTLS(pdata) vfs_zipfile_get_tls((pdata)->zipnode, true)
static const char* vfs_zippath_name(VFSNode *node) {
VFSZipPathData *zdata = node->data1;
return zip_get_name(ZTLS(zdata)->zip, zdata->index, 0);
}
static void vfs_zippath_free(VFSNode *node) {
free(node->data1);
}
static char* vfs_zippath_repr(VFSNode *node) {
VFSZipPathData *zdata = node->data1;
char *ziprepr = vfs_node_repr(zdata->zipnode, false);
char *zpathrepr = strfmt("%s '%s' in %s",
zdata->info.is_dir ? "directory" : "file", vfs_zippath_name(node), ziprepr);
free(ziprepr);
return zpathrepr;
}
static char* vfs_zippath_syspath(VFSNode *node) {
VFSZipPathData *zdata = node->data1;
char *zippath = vfs_node_repr(zdata->zipnode, true);
char *subpath = strfmt("%s%c%s", zippath, vfs_syspath_separators[0], vfs_zippath_name(node));
free(zippath);
vfs_syspath_normalize_inplace(subpath);
return subpath;
}
static VFSInfo vfs_zippath_query(VFSNode *node) {
VFSZipPathData *zdata = node->data1;
return zdata->info;
}
static VFSNode* vfs_zippath_locate(VFSNode *node, const char *path) {
VFSZipPathData *zdata = node->data1;
const char *mypath = vfs_zippath_name(node);
char fullpath[strlen(mypath) + strlen(path) + 2];
snprintf(fullpath, sizeof(fullpath), "%s%c%s", mypath, VFS_PATH_SEPARATOR, path);
vfs_path_normalize_inplace(fullpath);
return vfs_locate(zdata->zipnode, fullpath);
}
static const char* vfs_zippath_iter(VFSNode *node, void **opaque) {
VFSZipPathData *zdata = node->data1;
VFSZipFileIterData *idata = *opaque;
if(!zdata->info.is_dir) {
return NULL;
}
if(!idata) {
idata = calloc(1, sizeof(VFSZipFileIterData));
idata->num = zip_get_num_entries(ZTLS(zdata)->zip, 0);
idata->idx = zdata->index;
idata->prefix = vfs_zippath_name(node);
idata->prefix_len = strlen(idata->prefix);
*opaque = idata;
}
return vfs_zipfile_iter_shared(node, zdata->zipnode->data1, idata, ZTLS(zdata));
}
#define vfs_zippath_iter_stop vfs_zipfile_iter_stop
static SDL_RWops* vfs_zippath_open(VFSNode *node, VFSOpenMode mode) {
if(mode & VFS_MODE_WRITE) {
vfs_set_error("ZIP archives are read-only");
return NULL;
}
VFSZipPathData *zdata = node->data1;
if(mode & VFS_MODE_SEEKABLE && zdata->compression != ZIP_CM_STORE) {
char *repr = vfs_node_repr(node, true);
log_warn("Opening compressed file '%s' in seekable mode, this is suboptimal. Consider storing this file without compression", repr);
free(repr);
}
return SDL_RWFromZipFile(node, zdata);
}
static VFSNodeFuncs vfs_funcs_zippath = {
.repr = vfs_zippath_repr,
.query = vfs_zippath_query,
.free = vfs_zippath_free,
.syspath = vfs_zippath_syspath,
//.mount = vfs_zippath_mount,
.locate = vfs_zippath_locate,
.iter = vfs_zippath_iter,
.iter_stop = vfs_zippath_iter_stop,
//.mkdir = vfs_zippath_mkdir,
.open = vfs_zippath_open,
};
void vfs_zippath_init(VFSNode *node, VFSNode *zipnode, zip_int64_t idx) {
VFSZipPathData *zdata = calloc(1, sizeof(VFSZipPathData));
zdata->zipnode = zipnode;
zdata->index = idx;
zdata->size = -1;
zdata->compressed_size = -1;
zdata->compression = ZIP_CM_STORE;
node->data1 = zdata;
zdata->info.exists = true;
zdata->info.is_readonly = true;
if('/' == *(strchr(vfs_zippath_name(node), 0) - 1)) {
zdata->info.is_dir = true;
}
zip_stat_t zstat;
if(zip_stat_index(ZTLS(zdata)->zip, zdata->index, 0, &zstat) < 0) {
log_warn("zip_stat_index(%"PRIi64") failed: %s", idx, zip_error_strerror(&ZTLS(zdata)->error));
} else {
if(zstat.valid & ZIP_STAT_SIZE) {
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;
}
}
node->funcs = &vfs_funcs_zippath;
const char *path = vfs_zippath_name(node);
char buf[strlen(path)+1], *base, *name;
strcpy(buf, path);
vfs_path_split_right(buf, &base, &name);
}