Remove some legacy sm64ex code
This commit is contained in:
parent
940a05a2d1
commit
c91b390d80
142
src/pc/fs/fs.c
142
src/pc/fs/fs.c
|
@ -17,7 +17,6 @@
|
|||
#include "../platform.h"
|
||||
#include "fs.h"
|
||||
|
||||
char fs_gamedir[SYS_MAX_PATH] = "";
|
||||
char fs_writepath[SYS_MAX_PATH] = "";
|
||||
|
||||
struct fs_dir_s {
|
||||
|
@ -28,11 +27,9 @@ struct fs_dir_s {
|
|||
};
|
||||
|
||||
extern fs_packtype_t fs_packtype_dir;
|
||||
extern fs_packtype_t fs_packtype_zip;
|
||||
|
||||
static fs_packtype_t *fs_packers[] = {
|
||||
&fs_packtype_dir,
|
||||
&fs_packtype_zip,
|
||||
&fs_packtype_dir
|
||||
};
|
||||
|
||||
static fs_dir_t *fs_searchpaths = NULL;
|
||||
|
@ -44,45 +41,7 @@ static inline fs_dir_t *fs_find_dir(const char *realpath) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int mount_cmp(const void *p1, const void *p2) {
|
||||
const char *s1 = sys_file_name(*(const char **)p1);
|
||||
const char *s2 = sys_file_name(*(const char **)p2);
|
||||
|
||||
// check if one or both of these are basepacks
|
||||
const int plen = strlen(FS_BASEPACK_PREFIX);
|
||||
const bool is_base1 = !strncmp(s1, FS_BASEPACK_PREFIX, plen);
|
||||
const bool is_base2 = !strncmp(s2, FS_BASEPACK_PREFIX, plen);
|
||||
|
||||
// if both are basepacks, compare the postfixes only
|
||||
if (is_base1 && is_base2) return strcmp(s1 + plen, s2 + plen);
|
||||
// if only one is a basepack, it goes first
|
||||
if (is_base1) return -1;
|
||||
if (is_base2) return 1;
|
||||
// otherwise strcmp order
|
||||
return strcmp(s1, s2);
|
||||
}
|
||||
|
||||
static void scan_path_dir(const char *ropath, const char *dir) {
|
||||
char dirpath[SYS_MAX_PATH];
|
||||
snprintf(dirpath, sizeof(dirpath), "%s/%s", ropath, dir);
|
||||
|
||||
if (!fs_sys_dir_exists(dirpath)) return;
|
||||
|
||||
// since filename order in readdir() isn't guaranteed, collect paths and sort them in strcmp() order
|
||||
// (but with basepacks first)
|
||||
fs_pathlist_t plist = fs_sys_enumerate(dirpath, false);
|
||||
if (plist.paths) {
|
||||
qsort(plist.paths, plist.numpaths, sizeof(char *), mount_cmp);
|
||||
for (int32_t i = 0; i < plist.numpaths; ++i)
|
||||
fs_mount(plist.paths[i]);
|
||||
fs_pathlist_free(&plist);
|
||||
}
|
||||
|
||||
// mount the directory itself
|
||||
fs_mount(dirpath);
|
||||
}
|
||||
|
||||
bool fs_init(const char **rodirs, const char *gamedir, const char *writepath) {
|
||||
bool fs_init(const char *writepath) {
|
||||
char buf[SYS_MAX_PATH];
|
||||
|
||||
// expand and remember the write path
|
||||
|
@ -92,26 +51,6 @@ bool fs_init(const char **rodirs, const char *gamedir, const char *writepath) {
|
|||
printf("fs: writepath set to `%s`\n", fs_writepath);
|
||||
#endif
|
||||
|
||||
// remember the game directory name
|
||||
strncpy(fs_gamedir, gamedir, sizeof(fs_gamedir));
|
||||
fs_gamedir[sizeof(fs_gamedir)-1] = 0;
|
||||
#ifdef DEVELOPMENT
|
||||
printf("fs: gamedir set to `%s`\n", fs_gamedir);
|
||||
#endif
|
||||
|
||||
// first, scan all possible paths and mount all basedirs in them
|
||||
for (const char **p = rodirs; p && *p; ++p)
|
||||
scan_path_dir(fs_convert_path(buf, sizeof(buf), *p), FS_BASEDIR);
|
||||
scan_path_dir(fs_writepath, FS_BASEDIR);
|
||||
|
||||
// then mount all the gamedirs in them, if the game dir isn't the same
|
||||
if (sys_strcasecmp(FS_BASEDIR, fs_gamedir)) {
|
||||
for (const char **p = rodirs; p && *p; ++p)
|
||||
scan_path_dir(fs_convert_path(buf, sizeof(buf), *p), fs_gamedir);
|
||||
scan_path_dir(fs_writepath, fs_gamedir);
|
||||
}
|
||||
|
||||
// as a special case, mount writepath itself
|
||||
fs_mount(fs_writepath);
|
||||
|
||||
return true;
|
||||
|
@ -164,20 +103,6 @@ bool fs_mount(const char *realpath) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool fs_unmount(const char *realpath) {
|
||||
fs_dir_t *dir = fs_find_dir(realpath);
|
||||
if (dir) {
|
||||
dir->packer->unmount(dir->pack);
|
||||
free((void *)dir->realpath);
|
||||
if (dir->prev) dir->prev->next = dir->next;
|
||||
if (dir->next) dir->next->prev = dir->prev;
|
||||
if (dir == fs_searchpaths) fs_searchpaths = dir->next;
|
||||
free(dir);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fs_walk_result_t fs_walk(const char *base, walk_fn_t walkfn, void *user, const bool recur) {
|
||||
bool found = false;
|
||||
for (fs_dir_t *dir = fs_searchpaths; dir; dir = dir->next) {
|
||||
|
@ -190,22 +115,6 @@ fs_walk_result_t fs_walk(const char *base, walk_fn_t walkfn, void *user, const b
|
|||
return found ? FS_WALK_SUCCESS : FS_WALK_NOTFOUND;
|
||||
}
|
||||
|
||||
bool fs_is_file(const char *fname) {
|
||||
for (fs_dir_t *dir = fs_searchpaths; dir; dir = dir->next) {
|
||||
if (dir->packer->is_file(dir->pack, fname))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fs_is_dir(const char *fname) {
|
||||
for (fs_dir_t *dir = fs_searchpaths; dir; dir = dir->next) {
|
||||
if (dir->packer->is_dir(dir->pack, fname))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fs_file_t *fs_open(const char *vpath) {
|
||||
for (fs_dir_t *dir = fs_searchpaths; dir; dir = dir->next) {
|
||||
fs_file_t *f = dir->packer->open(dir->pack, vpath);
|
||||
|
@ -227,16 +136,6 @@ int64_t fs_read(fs_file_t *file, void *buf, const uint64_t size) {
|
|||
return file->parent->packer->read(file->parent->pack, file, buf, size);
|
||||
}
|
||||
|
||||
bool fs_seek(fs_file_t *file, const int64_t ofs) {
|
||||
if (!file) return -1;
|
||||
return file->parent->packer->seek(file->parent->pack, file, ofs);
|
||||
}
|
||||
|
||||
int64_t fs_tell(fs_file_t *file) {
|
||||
if (!file) return -1;
|
||||
return file->parent->packer->tell(file->parent->pack, file);
|
||||
}
|
||||
|
||||
int64_t fs_size(fs_file_t *file) {
|
||||
if (!file) return -1;
|
||||
return file->parent->packer->size(file->parent->pack, file);
|
||||
|
@ -254,31 +153,6 @@ struct matchdata_s {
|
|||
size_t dst_len;
|
||||
};
|
||||
|
||||
static bool match_walk(void *user, const char *path) {
|
||||
struct matchdata_s *data = (struct matchdata_s *)user;
|
||||
if (!strncmp(path, data->prefix, data->prefix_len)) {
|
||||
// found our lad, copy path to destination and terminate
|
||||
strncpy(data->dst, path, data->dst_len);
|
||||
data->dst[data->dst_len - 1] = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *fs_match(char *outname, const size_t outlen, const char *prefix) {
|
||||
struct matchdata_s data = {
|
||||
.prefix = prefix,
|
||||
.prefix_len = strlen(prefix),
|
||||
.dst = outname,
|
||||
.dst_len = outlen,
|
||||
};
|
||||
|
||||
if (fs_walk("", match_walk, &data, true) == FS_WALK_INTERRUPTED)
|
||||
return outname;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool enumerate_walk(void *user, const char *path) {
|
||||
fs_pathlist_t *data = (fs_pathlist_t *)user;
|
||||
|
||||
|
@ -440,18 +314,6 @@ bool fs_sys_walk(const char *base, walk_fn_t walk, void *user, const bool recur)
|
|||
#endif
|
||||
}
|
||||
|
||||
fs_pathlist_t fs_sys_enumerate(const char *base, const bool recur) {
|
||||
char **paths = malloc(sizeof(char *) * 32);
|
||||
fs_pathlist_t pathlist = { paths, 0, 32 };
|
||||
|
||||
if (!paths) return pathlist;
|
||||
|
||||
if (!fs_sys_walk(base, enumerate_walk, &pathlist, recur))
|
||||
fs_pathlist_free(&pathlist);
|
||||
|
||||
return pathlist;
|
||||
}
|
||||
|
||||
bool fs_sys_mkdir(const char *name) {
|
||||
#ifdef _WIN32
|
||||
return _mkdir(name) == 0;
|
||||
|
|
|
@ -8,21 +8,8 @@
|
|||
|
||||
#include "../platform.h"
|
||||
|
||||
// FS_BASEDIR is usually defined in the build script
|
||||
#ifndef FS_BASEDIR
|
||||
# define FS_BASEDIR "res"
|
||||
#endif
|
||||
|
||||
#ifndef FS_BASEPACK_PREFIX
|
||||
# define FS_BASEPACK_PREFIX "base"
|
||||
#endif
|
||||
|
||||
#define FS_TEXTUREDIR "gfx"
|
||||
#define FS_SOUNDDIR "sound"
|
||||
|
||||
#define SAVE_FILENAME "sm64_save_file.bin"
|
||||
|
||||
extern char fs_gamedir[];
|
||||
extern char fs_writepath[];
|
||||
|
||||
// receives the full path
|
||||
|
@ -81,16 +68,12 @@ typedef struct {
|
|||
// takes the supplied NULL-terminated list of read-only directories and mounts all the packs in them,
|
||||
// then mounts the directories themselves, then mounts all the packs in `gamedir`, then mounts `gamedir` itself,
|
||||
// then does the same with `userdir`
|
||||
// initializes the `fs_gamedir` and `fs_userdir` variables
|
||||
bool fs_init(const char **rodirs, const char *gamedir, const char *userdir);
|
||||
|
||||
// initializes the `fs_userdir` variable
|
||||
bool fs_init(const char *userdir);
|
||||
// mounts the pack at physical path `realpath` to the root of the filesystem
|
||||
// packs mounted later take priority over packs mounted earlier
|
||||
bool fs_mount(const char *realpath);
|
||||
|
||||
// removes the pack at physical path from the virtual filesystem
|
||||
bool fs_unmount(const char *realpath);
|
||||
|
||||
/* generalized filesystem functions that call matching packtype functions for each pack in the searchpath */
|
||||
|
||||
// FIXME: this can walk in unorthodox patterns, since it goes through mountpoints linearly
|
||||
|
@ -101,25 +84,16 @@ fs_pathlist_t fs_enumerate(const char *base, const bool recur);
|
|||
// call this on a list returned by fs_enumerate() to free it
|
||||
void fs_pathlist_free(fs_pathlist_t *pathlist);
|
||||
|
||||
bool fs_is_file(const char *fname);
|
||||
bool fs_is_dir(const char *fname);
|
||||
|
||||
fs_file_t *fs_open(const char *vpath);
|
||||
void fs_close(fs_file_t *file);
|
||||
int64_t fs_read(fs_file_t *file, void *buf, const uint64_t size);
|
||||
const char *fs_readline(fs_file_t *file, char *dst, const uint64_t size);
|
||||
bool fs_seek(fs_file_t *file, const int64_t ofs);
|
||||
int64_t fs_tell(fs_file_t *file);
|
||||
int64_t fs_size(fs_file_t *file);
|
||||
bool fs_eof(fs_file_t *file);
|
||||
|
||||
void *fs_load_file(const char *vpath, uint64_t *outsize);
|
||||
const char *fs_readline(fs_file_t *file, char *dst, uint64_t size);
|
||||
|
||||
// tries to find the first file with the filename that starts with `prefix`
|
||||
// puts full filename into `outname` and returns it or returns NULL if nothing matches
|
||||
const char *fs_match(char *outname, const size_t outlen, const char *prefix);
|
||||
|
||||
// takes a virtual path and prepends the write path to it
|
||||
const char *fs_get_write_path(const char *vpath);
|
||||
|
||||
|
@ -129,7 +103,6 @@ const char *fs_convert_path(char *buf, const size_t bufsiz, const char *path);
|
|||
/* these operate on the real filesystem and are used by fs_packtype_dir */
|
||||
|
||||
bool fs_sys_walk(const char *base, walk_fn_t walk, void *user, const bool recur);
|
||||
fs_pathlist_t fs_sys_enumerate(const char *base, const bool recur);
|
||||
bool fs_sys_file_exists(const char *name);
|
||||
bool fs_sys_dir_exists(const char *name);
|
||||
bool fs_sys_mkdir(const char *name); // creates with 0777 by default
|
||||
|
|
|
@ -1,490 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <tinfl.h>
|
||||
|
||||
#include "macros.h"
|
||||
#include "../platform.h"
|
||||
#include "fs.h"
|
||||
#include "dirtree.h"
|
||||
|
||||
#define ZIP_BUFSIZE 16384
|
||||
#define ZIP_EOCD_BUFSIZE 65578
|
||||
|
||||
#define ZIP_LFH_SIG 0x04034b50
|
||||
#define ZIP_CDH_SIG 0x02014b50
|
||||
#define ZIP_EOCD_SIG 0x06054b50
|
||||
|
||||
typedef struct {
|
||||
fs_dirtree_t tree; // this should always be first, so this could be used as a dirtree root
|
||||
const char *realpath; // physical path to the zip file
|
||||
FILE *zipf; // open zip file handle, if any
|
||||
} zip_pack_t;
|
||||
|
||||
typedef struct {
|
||||
fs_dirtree_entry_t tree; // this should always be first, so this could be used as a dirtree entry
|
||||
uint64_t ofs; // offset to compressed data in zip
|
||||
uint16_t bits; // general purpose zip flags
|
||||
uint16_t comptype; // compression method
|
||||
uint32_t crc; // CRC-32
|
||||
uint64_t comp_size; // size of compressed data in zip
|
||||
uint64_t uncomp_size; // size of decompressed data
|
||||
uint16_t attr_int; // internal attributes
|
||||
uint32_t attr_ext; // external attributes
|
||||
bool ofs_fixed; // if true, `ofs` points to the file data, otherwise to LFH
|
||||
} zip_entry_t;
|
||||
|
||||
typedef struct {
|
||||
zip_entry_t *entry; // the dirtree entry of this file
|
||||
uint32_t comp_pos; // read position in compressed data
|
||||
uint32_t uncomp_pos; // read position in uncompressed data
|
||||
uint8_t *buffer; // decompression buffer (if compressed)
|
||||
z_stream zstream; // tinfl zlib stream
|
||||
FILE *fstream; // duplicate of zipf of the parent zip file
|
||||
} zip_file_t;
|
||||
|
||||
static int64_t zip_find_eocd(FILE *f, int64_t *outlen) {
|
||||
// the EOCD is somewhere in the last 65557 bytes of the file
|
||||
// get the total file size
|
||||
fseek(f, 0, SEEK_END);
|
||||
const int64_t fsize = ftell(f);
|
||||
if (fsize <= 16) return -1; // probably not a zip
|
||||
|
||||
const int64_t rx = (fsize < ZIP_EOCD_BUFSIZE ? fsize : ZIP_EOCD_BUFSIZE);
|
||||
uint8_t *buf = malloc(rx);
|
||||
if (!buf) return -1;
|
||||
|
||||
// read that entire chunk and search for EOCD backwards from the end
|
||||
fseek(f, fsize - rx, SEEK_SET);
|
||||
if (fread(buf, rx, 1, f)) {
|
||||
for (int64_t i = rx - 8; i >= 0; --i) {
|
||||
if ((buf[i + 0] == 0x50) && (buf[i + 1] == 0x4B) &&
|
||||
(buf[i + 2] == 0x05) && (buf[i + 3] == 0x06)) {
|
||||
// gotem
|
||||
free(buf);
|
||||
if (outlen) *outlen = fsize;
|
||||
return fsize - rx + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool zip_parse_eocd(FILE *f, uint64_t *cdir_ofs, uint64_t *data_ofs, uint64_t *count) {
|
||||
int64_t fsize = 0;
|
||||
|
||||
// EOCD record struct
|
||||
struct eocd_s {
|
||||
uint32_t sig;
|
||||
uint16_t this_disk;
|
||||
uint16_t cdir_disk;
|
||||
uint16_t disk_entry_count;
|
||||
uint16_t total_entry_count;
|
||||
uint32_t cdir_size;
|
||||
uint32_t cdir_ofs;
|
||||
uint16_t comment_len;
|
||||
// zip comment follows
|
||||
} __attribute__((__packed__));
|
||||
struct eocd_s eocd;
|
||||
|
||||
// find the EOCD and seek to it
|
||||
int64_t pos = zip_find_eocd(f, &fsize);
|
||||
if (pos < 0) return false;
|
||||
fseek(f, pos, SEEK_SET);
|
||||
|
||||
// read it
|
||||
if (!fread(&eocd, sizeof(eocd), 1, f)) return false;
|
||||
|
||||
// double check the sig
|
||||
if (LE_TO_HOST32(eocd.sig) != ZIP_EOCD_SIG) return false;
|
||||
|
||||
// disks should all be 0
|
||||
if (eocd.this_disk || eocd.cdir_disk) return false;
|
||||
|
||||
// total entry count should be the same as disk entry count
|
||||
if (eocd.disk_entry_count != eocd.total_entry_count) return false;
|
||||
|
||||
*count = LE_TO_HOST16(eocd.total_entry_count);
|
||||
*cdir_ofs = LE_TO_HOST32(eocd.cdir_ofs);
|
||||
eocd.cdir_size = LE_TO_HOST32(eocd.cdir_size);
|
||||
|
||||
// end of central dir can't be before central dir
|
||||
if ((uint64_t)pos < *cdir_ofs + eocd.cdir_size) return false;
|
||||
|
||||
*data_ofs = (uint64_t)(pos - (*cdir_ofs + eocd.cdir_size));
|
||||
*cdir_ofs += *data_ofs;
|
||||
|
||||
// make sure end of comment matches end of file
|
||||
eocd.comment_len = LE_TO_HOST16(eocd.comment_len);
|
||||
return ((pos + 22 + eocd.comment_len) == fsize);
|
||||
}
|
||||
|
||||
static bool zip_fixup_offset(zip_file_t *zipfile) {
|
||||
// LFH record struct
|
||||
struct lfh_s {
|
||||
uint32_t sig;
|
||||
uint16_t version_required;
|
||||
uint16_t bits;
|
||||
uint16_t comptype;
|
||||
uint16_t mod_time;
|
||||
uint16_t mod_date;
|
||||
uint32_t crc;
|
||||
uint32_t comp_size;
|
||||
uint32_t uncomp_size;
|
||||
uint16_t fname_len;
|
||||
uint16_t extra_len;
|
||||
// file name, extra field and data follow
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct lfh_s lfh;
|
||||
|
||||
zip_entry_t *ent = zipfile->entry;
|
||||
|
||||
fseek(zipfile->fstream, ent->ofs, SEEK_SET);
|
||||
if (!fread(&lfh, sizeof(lfh), 1, zipfile->fstream)) return false;
|
||||
|
||||
// we only need these two
|
||||
lfh.fname_len = LE_TO_HOST16(lfh.fname_len);
|
||||
lfh.extra_len = LE_TO_HOST16(lfh.extra_len);
|
||||
|
||||
// ofs will now point to actual data
|
||||
ent->ofs += sizeof(lfh) + lfh.fname_len + lfh.extra_len;
|
||||
ent->ofs_fixed = true; // only need to do this once
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static zip_entry_t *zip_load_entry(FILE *f, fs_dirtree_t *tree, const uint64_t data_ofs) {
|
||||
// CDH record struct
|
||||
struct cdh_s {
|
||||
uint32_t sig;
|
||||
uint16_t version_used;
|
||||
uint16_t version_required;
|
||||
uint16_t bits;
|
||||
uint16_t comptype;
|
||||
uint16_t mod_time;
|
||||
uint16_t mod_date;
|
||||
uint32_t crc;
|
||||
uint32_t comp_size;
|
||||
uint32_t uncomp_size;
|
||||
uint16_t fname_len;
|
||||
uint16_t extra_len;
|
||||
uint16_t comment_len;
|
||||
uint16_t start_disk;
|
||||
uint16_t attr_int;
|
||||
uint32_t attr_ext;
|
||||
uint32_t lfh_ofs;
|
||||
// file name, extra field and comment follow
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct cdh_s cdh;
|
||||
zip_entry_t zipent;
|
||||
|
||||
memset(&zipent, 0, sizeof(zipent));
|
||||
|
||||
if (!fread(&cdh, sizeof(cdh), 1, f)) return NULL;
|
||||
|
||||
// check cdir entry header signature
|
||||
if (LE_TO_HOST32(cdh.sig) != ZIP_CDH_SIG) return NULL;
|
||||
|
||||
// byteswap and copy some important fields
|
||||
zipent.bits = LE_TO_HOST16(cdh.bits);
|
||||
zipent.comptype = LE_TO_HOST16(cdh.comptype);
|
||||
zipent.crc = LE_TO_HOST32(cdh.crc);
|
||||
zipent.comp_size = LE_TO_HOST32(cdh.comp_size);
|
||||
zipent.uncomp_size = LE_TO_HOST32(cdh.uncomp_size);
|
||||
zipent.ofs = LE_TO_HOST32(cdh.lfh_ofs);
|
||||
zipent.attr_int = LE_TO_HOST16(cdh.attr_int);
|
||||
zipent.attr_ext = LE_TO_HOST32(cdh.attr_ext);
|
||||
cdh.fname_len = LE_TO_HOST16(cdh.fname_len);
|
||||
cdh.comment_len = LE_TO_HOST16(cdh.comment_len);
|
||||
cdh.extra_len = LE_TO_HOST16(cdh.extra_len);
|
||||
|
||||
// read the name
|
||||
char *name = calloc(1, cdh.fname_len + 1);
|
||||
if (!name) return NULL;
|
||||
if (!fread(name, cdh.fname_len, 1, f)) { free(name); return NULL; }
|
||||
|
||||
// this is a directory if the name ends in a path separator
|
||||
bool is_dir = false;
|
||||
if (name[cdh.fname_len - 1] == '/') {
|
||||
is_dir = true;
|
||||
name[cdh.fname_len - 1] = 0;
|
||||
}
|
||||
name[cdh.fname_len] = 0;
|
||||
|
||||
// add to directory tree
|
||||
zip_entry_t *retent = (zip_entry_t *)fs_dirtree_add(tree, name, is_dir);
|
||||
free(name);
|
||||
if (!retent) return NULL;
|
||||
|
||||
// copy the data we read into the new entry
|
||||
zipent.tree = retent->tree;
|
||||
memcpy(retent, &zipent, sizeof(zipent));
|
||||
|
||||
// this points to the LFH now; will be fixed up on file open
|
||||
// while the CDH includes an "extra field length" field, it's usually different
|
||||
retent->ofs += data_ofs;
|
||||
|
||||
// skip to the next CDH
|
||||
fseek(f, cdh.extra_len + cdh.comment_len, SEEK_CUR);
|
||||
|
||||
return retent;
|
||||
}
|
||||
|
||||
static inline bool zip_load_entries(FILE *f, fs_dirtree_t *tree, const uint64_t cdir_ofs, const uint64_t data_ofs, const uint64_t count) {
|
||||
fseek(f, cdir_ofs, SEEK_SET);
|
||||
for (uint64_t i = 0; i < count; ++i) {
|
||||
if (!zip_load_entry(f, tree, data_ofs))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool is_zip(FILE *f) {
|
||||
uint32_t sig = 0;
|
||||
if (fread(&sig, sizeof(sig), 1, f)) {
|
||||
// the first LFH might be at the start of the zip
|
||||
if (LE_TO_HOST32(sig) == ZIP_LFH_SIG)
|
||||
return true;
|
||||
// no signature, might still be a zip because fuck you
|
||||
// the only way now is to try and find the end of central directory
|
||||
return zip_find_eocd(f, NULL) >= 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void *pack_zip_mount(const char *realpath) {
|
||||
uint64_t cdir_ofs, data_ofs, count;
|
||||
zip_pack_t *pack = NULL;
|
||||
FILE *f = NULL;
|
||||
|
||||
f = fopen(realpath, "rb");
|
||||
if (!f) goto _fail;
|
||||
|
||||
if (!is_zip(f)) goto _fail;
|
||||
|
||||
pack = calloc(1, sizeof(zip_pack_t));
|
||||
if (!pack) goto _fail;
|
||||
|
||||
if (!zip_parse_eocd(f, &cdir_ofs, &data_ofs, &count))
|
||||
goto _fail;
|
||||
|
||||
if (!fs_dirtree_init(&pack->tree, sizeof(zip_entry_t)))
|
||||
goto _fail;
|
||||
|
||||
if (!zip_load_entries(f, &pack->tree, cdir_ofs, data_ofs, count))
|
||||
goto _fail;
|
||||
|
||||
pack->realpath = sys_strdup(realpath);
|
||||
pack->zipf = f;
|
||||
|
||||
return pack;
|
||||
|
||||
_fail:
|
||||
if (f) fclose(f);
|
||||
if (pack) free(pack);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void pack_zip_unmount(void *pack) {
|
||||
zip_pack_t *zip = (zip_pack_t *)pack;
|
||||
fs_dirtree_free(&zip->tree);
|
||||
if (zip->realpath) free((void *)zip->realpath);
|
||||
if (zip->zipf) fclose(zip->zipf);
|
||||
free(zip);
|
||||
}
|
||||
|
||||
static bool pack_zip_is_file(void *pack, const char *fname) {
|
||||
zip_entry_t *ent = (zip_entry_t *)fs_dirtree_find((fs_dirtree_t *)pack, fname);
|
||||
return ent && !ent->tree.is_dir;
|
||||
}
|
||||
|
||||
static bool pack_zip_is_dir(void *pack, const char *fname) {
|
||||
zip_entry_t *ent = (zip_entry_t *)fs_dirtree_find((fs_dirtree_t *)pack, fname);
|
||||
return ent && ent->tree.is_dir;
|
||||
}
|
||||
|
||||
static inline void pack_zip_close_zipfile(zip_file_t *zipfile) {
|
||||
if (zipfile->buffer) {
|
||||
inflateEnd(&zipfile->zstream);
|
||||
free(zipfile->buffer);
|
||||
}
|
||||
if (zipfile->fstream) fclose(zipfile->fstream);
|
||||
free(zipfile);
|
||||
}
|
||||
|
||||
static fs_file_t *pack_zip_open(void *pack, const char *vpath) {
|
||||
fs_file_t *fsfile = NULL;
|
||||
zip_file_t *zipfile = NULL;
|
||||
zip_pack_t *zip = (zip_pack_t *)pack;
|
||||
zip_entry_t *ent = (zip_entry_t *)fs_dirtree_find((fs_dirtree_t *)zip, vpath);
|
||||
if (!ent || ent->tree.is_dir) goto _fail; // we're expecting a fucking file here
|
||||
|
||||
zipfile = calloc(1, sizeof(zip_file_t));
|
||||
if (!zipfile) goto _fail;
|
||||
zipfile->entry = ent;
|
||||
|
||||
// obtain an additional file descriptor
|
||||
// fdopen(dup(fileno())) is not very portable and might not create separate state
|
||||
zipfile->fstream = fopen(zip->realpath, "rb");
|
||||
if (!zipfile->fstream) goto _fail;
|
||||
|
||||
// make ent->ofs point to the actual file data if it doesn't already
|
||||
if (!ent->ofs_fixed)
|
||||
if (!zip_fixup_offset(zipfile))
|
||||
goto _fail; // this shouldn't generally happen but oh well
|
||||
|
||||
// if there's compression, assume it's zlib
|
||||
if (ent->comptype != 0) {
|
||||
zipfile->buffer = malloc(ZIP_BUFSIZE);
|
||||
if (!zipfile->buffer)
|
||||
goto _fail;
|
||||
if (inflateInit2(&zipfile->zstream, -MAX_WBITS) != Z_OK)
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
fsfile = malloc(sizeof(fs_file_t));
|
||||
if (!fsfile) goto _fail;
|
||||
fsfile->handle = zipfile;
|
||||
fsfile->parent = NULL;
|
||||
|
||||
// point to the start of the file data
|
||||
fseek(zipfile->fstream, ent->ofs, SEEK_SET);
|
||||
|
||||
return fsfile;
|
||||
|
||||
_fail:
|
||||
if (zipfile) pack_zip_close_zipfile(zipfile);
|
||||
if (fsfile) free(fsfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void pack_zip_close(UNUSED void *pack, fs_file_t *file) {
|
||||
if (!file) return;
|
||||
|
||||
zip_file_t *zipfile = (zip_file_t *)file->handle;
|
||||
if (zipfile) pack_zip_close_zipfile(zipfile);
|
||||
|
||||
free(file);
|
||||
}
|
||||
|
||||
static int64_t pack_zip_read(UNUSED void *pack, fs_file_t *file, void *buf, const uint64_t size) {
|
||||
zip_file_t *zipfile = (zip_file_t *)file->handle;
|
||||
zip_entry_t *ent = zipfile->entry;
|
||||
|
||||
int64_t avail = ent->uncomp_size - zipfile->uncomp_pos;
|
||||
int64_t max_read = ((int64_t)size > avail) ? avail : (int64_t)size;
|
||||
int64_t rx = 0;
|
||||
int err = 0;
|
||||
|
||||
if (max_read == 0) return 0;
|
||||
|
||||
if (ent->comptype == 0) {
|
||||
// no compression, just read
|
||||
rx = fread(buf, 1, size, zipfile->fstream);
|
||||
} else {
|
||||
zipfile->zstream.next_out = buf;
|
||||
zipfile->zstream.avail_out = (unsigned int)max_read;
|
||||
while (rx < max_read) {
|
||||
const uint32_t before = (uint32_t)zipfile->zstream.total_out;
|
||||
// check if we ran out of compressed bytes and read more if we did
|
||||
if (zipfile->zstream.avail_in == 0) {
|
||||
int32_t comp_rx = ent->comp_size - zipfile->comp_pos;
|
||||
if (comp_rx > 0) {
|
||||
if (comp_rx > ZIP_BUFSIZE) comp_rx = ZIP_BUFSIZE;
|
||||
comp_rx = fread(zipfile->buffer, 1, comp_rx, zipfile->fstream);
|
||||
if (comp_rx == 0) break;
|
||||
zipfile->comp_pos += (uint32_t)comp_rx;
|
||||
zipfile->zstream.next_in = zipfile->buffer;
|
||||
zipfile->zstream.avail_in = (unsigned int)comp_rx;
|
||||
}
|
||||
}
|
||||
// inflate
|
||||
err = inflate(&zipfile->zstream, Z_SYNC_FLUSH);
|
||||
rx += zipfile->zstream.total_out - before;
|
||||
if (err != Z_OK) break;
|
||||
}
|
||||
}
|
||||
|
||||
zipfile->uncomp_pos += rx;
|
||||
return rx;
|
||||
}
|
||||
|
||||
static bool pack_zip_seek(UNUSED void *pack, fs_file_t *file, const int64_t ofs) {
|
||||
zip_file_t *zipfile = (zip_file_t *)file->handle;
|
||||
zip_entry_t *ent = zipfile->entry;
|
||||
uint8_t buf[512];
|
||||
|
||||
if (ofs > (int64_t)ent->uncomp_size) return false;
|
||||
|
||||
if (ent->comptype == 0) {
|
||||
if (fseek(zipfile->fstream, ofs + ent->ofs, SEEK_SET) == 0)
|
||||
zipfile->uncomp_pos = ofs;
|
||||
} else {
|
||||
// if seeking backwards, gotta redecode the stream from the start until that point
|
||||
// so we make a copy of the zstream and clear it with a new one
|
||||
if (ofs < zipfile->uncomp_pos) {
|
||||
z_stream zstream;
|
||||
memset(&zstream, 0, sizeof(zstream));
|
||||
if (inflateInit2(&zstream, -MAX_WBITS) != Z_OK)
|
||||
return false;
|
||||
// reset the underlying file handle back to the start
|
||||
if (fseek(zipfile->fstream, ent->ofs, SEEK_SET) != 0) {
|
||||
if (zstream.zfree) {
|
||||
zstream.zfree(zstream.opaque, zstream.state);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// free and replace the old one
|
||||
inflateEnd(&zipfile->zstream);
|
||||
memcpy(&zipfile->zstream, &zstream, sizeof(zstream));
|
||||
zipfile->uncomp_pos = zipfile->comp_pos = 0;
|
||||
}
|
||||
// continue decoding the stream until we hit the new offset
|
||||
while (zipfile->uncomp_pos != ofs) {
|
||||
uint32_t max_read = (uint32_t)(ofs - zipfile->uncomp_pos);
|
||||
if (max_read > sizeof(buf)) max_read = sizeof(buf);
|
||||
if (pack_zip_read(pack, file, buf, max_read) != max_read)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int64_t pack_zip_tell(UNUSED void *pack, fs_file_t *file) {
|
||||
return ((zip_file_t *)file->handle)->uncomp_pos;
|
||||
}
|
||||
|
||||
static int64_t pack_zip_size(UNUSED void *pack, fs_file_t *file) {
|
||||
zip_file_t *zipfile = (zip_file_t *)file->handle;
|
||||
return zipfile->entry->uncomp_size;
|
||||
}
|
||||
|
||||
static bool pack_zip_eof(UNUSED void *pack, fs_file_t *file) {
|
||||
zip_file_t *zipfile = (zip_file_t *)file->handle;
|
||||
return zipfile->uncomp_pos >= zipfile->entry->uncomp_size;
|
||||
}
|
||||
|
||||
fs_packtype_t fs_packtype_zip = {
|
||||
"zip",
|
||||
pack_zip_mount,
|
||||
pack_zip_unmount,
|
||||
fs_dirtree_walk,
|
||||
pack_zip_is_file,
|
||||
pack_zip_is_dir,
|
||||
pack_zip_open,
|
||||
pack_zip_read,
|
||||
pack_zip_seek,
|
||||
pack_zip_tell,
|
||||
pack_zip_size,
|
||||
pack_zip_eof,
|
||||
pack_zip_close,
|
||||
};
|
|
@ -369,7 +369,7 @@ int main(int argc, char *argv[]) {
|
|||
#endif
|
||||
|
||||
const char *userpath = gCLIOpts.savePath[0] ? gCLIOpts.savePath : sys_user_path();
|
||||
fs_init(sys_ropaths, FS_BASEDIR, userpath);
|
||||
fs_init(userpath);
|
||||
configfile_load();
|
||||
|
||||
// Create the window straight away
|
||||
|
|
|
@ -9,20 +9,6 @@
|
|||
#include "fs/fs.h"
|
||||
#include "configfile.h"
|
||||
|
||||
/* NULL terminated list of platform specific read-only data paths */
|
||||
/* priority is top first */
|
||||
const char *sys_ropaths[] = {
|
||||
".", // working directory
|
||||
"!", // executable directory
|
||||
#if defined(__linux__) || defined(__unix__)
|
||||
// some common UNIX directories for read only stuff
|
||||
"/usr/local/share/sm64pc",
|
||||
"/usr/share/sm64pc",
|
||||
"/opt/sm64pc",
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* these are not available on some platforms, so might as well */
|
||||
|
||||
char *sys_strlwr(char *src) {
|
||||
|
@ -95,35 +81,6 @@ void sys_fatal(const char *fmt, ...) {
|
|||
// we can just ask SDL for most of this shit if we have it
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
// TEMPORARY: check the old save folder and copy contents to the new path
|
||||
// this will be removed after a while
|
||||
static inline bool copy_userdata(const char *userdir) {
|
||||
char oldpath[SYS_MAX_PATH] = { 0 };
|
||||
char path[SYS_MAX_PATH] = { 0 };
|
||||
|
||||
// check if a save already exists in the new folder
|
||||
snprintf(path, sizeof(path), "%s/" SAVE_FILENAME, userdir);
|
||||
if (fs_sys_file_exists(path)) return false;
|
||||
|
||||
// check if a save exists in the old folder ('pc' instead of 'ex')
|
||||
strncpy(oldpath, path, sizeof(oldpath));
|
||||
const unsigned int len = strlen(userdir);
|
||||
oldpath[len - 2] = 'p'; oldpath[len - 1] = 'c';
|
||||
if (!fs_sys_file_exists(oldpath)) return false;
|
||||
|
||||
printf("old save detected at '%s', copying to '%s'\n", oldpath, path);
|
||||
|
||||
bool ret = fs_sys_copy_file(oldpath, path);
|
||||
|
||||
// also try to copy the config
|
||||
path[len] = oldpath[len] = 0;
|
||||
strncat(path, "/" CONFIGFILE_DEFAULT, SYS_MAX_PATH - 1);
|
||||
strncat(oldpath, "/" CONFIGFILE_DEFAULT, SYS_MAX_PATH - 1);
|
||||
fs_sys_copy_file(oldpath, path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *sys_user_path(void) {
|
||||
static char path[SYS_MAX_PATH] = { 0 };
|
||||
|
||||
|
@ -141,8 +98,6 @@ const char *sys_user_path(void) {
|
|||
|
||||
if (!fs_sys_dir_exists(path) && !fs_sys_mkdir(path))
|
||||
path[0] = 0; // somehow failed, we got no user path
|
||||
else
|
||||
copy_userdata(path); // TEMPORARY: try to copy old saves, if any
|
||||
}
|
||||
|
||||
return path;
|
||||
|
|
|
@ -5,13 +5,10 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Platform-specific functions and whatnot */
|
||||
/* platform-specific functions and whatnot */
|
||||
|
||||
#define SYS_MAX_PATH 4096 // FIXME: define this on different platforms
|
||||
|
||||
// NULL terminated list of platform specific read-only data paths
|
||||
extern const char *sys_ropaths[];
|
||||
|
||||
// crossplatform impls of misc stuff
|
||||
char *sys_strdup(const char *src);
|
||||
char *sys_strlwr(char *src);
|
||||
|
|
Loading…
Reference in New Issue