From a6ae4dd126e7b2b678e0db31b68bba51311282cc Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Wed, 3 Feb 2010 20:47:40 -0500 Subject: [PATCH] liblbx: Parameterise I/O functions to allow custom streams. This eliminates the need for lbx_mopen and friends, so remove them. We also now leave it to the caller to handle "seeking" on pipes, to eliminate yet another hack. Only the core LBX handling code is updated for now. --- src/Makefile.inc | 2 +- src/fops.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++ src/lbx.c | 108 +++++++++-------------------------------------- src/lbx.h | 20 +++++++++ src/lbxtool.c | 66 +++++++++++++---------------- 5 files changed, 176 insertions(+), 126 deletions(-) create mode 100644 src/fops.c diff --git a/src/Makefile.inc b/src/Makefile.inc index fd3d0a8..dee2958 100644 --- a/src/Makefile.inc +++ b/src/Makefile.inc @@ -10,7 +10,7 @@ lbx_HEADERS = src/lbx.h src/image.h noinst_HEADERS += src/byteorder.h src/misc.h src/tools.h src/pack.h lib_LTLIBRARIES += liblbx.la -liblbx_la_SOURCES = src/misc.c src/lbx.c src/image.c src/pack.c +liblbx_la_SOURCES = src/misc.c src/lbx.c src/fops.c src/image.c src/pack.c bin_PROGRAMS += lbxtool lbxtool_SOURCES = src/lbxtool.c diff --git a/src/fops.c b/src/fops.c new file mode 100644 index 0000000..2ae6c61 --- /dev/null +++ b/src/fops.c @@ -0,0 +1,106 @@ +/* + * 2ooM: The Master of Orion II Reverse Engineering Project + * Default file operations structures for liblbx. + * Copyright (C) 2010 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +#include "misc.h" +#include "lbx.h" + +/* Default I/O operations for ordinary files. */ +static size_t file_read(void *buf, size_t size, void *handle) +{ + return fread(buf, 1, size, (FILE *)handle); +} + +static int file_seek(void *handle, long offset, int whence) +{ + return fseek((FILE *)handle, offset, whence); +} + +static long file_tell(void *handle) +{ + return ftell((FILE *)handle); +} + +static int file_eof(void *handle) +{ + return feof((FILE *)handle); +} + +const struct lbx_file_ops lbx_default_fops = { + .read = file_read, + .seek = file_seek, + .tell = file_tell, + .eof = file_eof, +}; + +/* Read function for pipes that tracks the number of bytes read. */ +static size_t pipe_read(void *buf, size_t size, void *handle) +{ + struct lbx_pipe_state *state = handle; + size_t rc; + + rc = fread(buf, 1, size, state->f); + state->offset += rc; + return rc; +} + +/* Seek function for pipes that reads data into the void. */ +static int pipe_seek(void *handle, long offset, int whence) +{ + struct lbx_pipe_state *state = handle; + long distance; + + distance = offset - state->offset; + if (distance < 0) + return -1; + + while (distance > 0) { + static unsigned char oblivion[1024]; + size_t rc, amount = MIN(sizeof oblivion, distance); + + rc = pipe_read(oblivion, amount, handle); + distance -= rc; + + if (rc < amount) + return -1; + } + + return 0; +} + +static long pipe_tell(void *handle) +{ + struct lbx_pipe_state *state = handle; + + return state->offset; +} + +static int pipe_eof(void *handle) +{ + struct lbx_pipe_state *state = handle; + + return feof(state->f); +} + +const struct lbx_file_ops lbx_pipe_fops = { + .read = pipe_read, + .seek = pipe_seek, + .tell = pipe_tell, + .eof = pipe_eof, +}; diff --git a/src/lbx.c b/src/lbx.c index fc52680..4b5d7f3 100644 --- a/src/lbx.c +++ b/src/lbx.c @@ -42,10 +42,9 @@ int lbx_errno = 0; struct lbx_state { const char *name; - unsigned char *mem; - size_t memsize; - FILE *f; - long foff; + const struct lbx_file_ops *fops; + int (*dtor)(void *handle); + void *f; uint16_t nfiles; uint32_t offsets[]; @@ -76,12 +75,13 @@ static struct lbx_state *lbx_init(unsigned char hdr[static LBX_HDR_SIZE]) return lbx; } -struct lbx_state *lbx_fopen(FILE *f, const char *name) +struct lbx_state *lbx_open(void *f, const struct lbx_file_ops *fops, + int (*destructor)(void *), const char *name) { unsigned char hdr_buf[LBX_HDR_SIZE]; struct lbx_state *lbx; - if (fread(hdr_buf, 1, sizeof hdr_buf, f) != sizeof hdr_buf) { + if (fops->read(hdr_buf, sizeof hdr_buf, f) != sizeof hdr_buf) { lbx_errno = -errno; return NULL; } @@ -90,79 +90,31 @@ struct lbx_state *lbx_fopen(FILE *f, const char *name) if (!lbx) return NULL; + lbx->dtor = destructor; lbx->name = name; + lbx->fops = fops; lbx->f = f; - lbx->foff = sizeof hdr_buf; for (unsigned i = 0; i <= lbx->nfiles; i++) { unsigned char buf[4]; - if (fread(buf, 1, sizeof buf, f) != sizeof buf) { + if (fops->read(buf, sizeof buf, f) != sizeof buf) { lbx_errno = -errno; - if (feof(f)) + if (fops->eof(f)) lbx_errno = LBX_EEOF; free(lbx); return NULL; } lbx->offsets[i] = unpack_32_le(buf); - lbx->foff += sizeof buf; } return lbx; } -static int _lbx_memcpy(void *dest, struct lbx_state *src, size_t size) +struct lbx_state *lbx_fopen(FILE *f, const char *name) { - if (src->foff + size > src->memsize) - return -1; - memcpy(dest, src->mem + src->foff, size); - src->foff += size; - return 0; -} - -struct lbx_state *lbx_mopen(void *_mem, size_t size, const char *name) -{ - struct lbx_state *new = NULL; - struct lbx_state tmp = { .mem = _mem, .memsize = size }; - uint16_t nfiles, version; - uint32_t magic; - - if (_lbx_memcpy(&nfiles, &tmp, sizeof nfiles) == -1) goto eof; - if (_lbx_memcpy(&magic, &tmp, sizeof magic) == -1) goto eof; - if (_lbx_memcpy(&version, &tmp, sizeof version) == -1) goto eof; - - nfiles = letohs(nfiles); - magic = letohl(magic); - version = letohs(version); - - if (magic != LBX_MAGIC) { - lbx_errno = LBX_EMAGIC; - return NULL; - } - - new = malloc(sizeof *new + (nfiles+1)*(sizeof *new->offsets)); - if (!new) { - lbx_errno = -errno; - return NULL; - } - - *new = (struct lbx_state){ - .name = name, - .nfiles = nfiles, - .mem = _mem, - .memsize = size, - .foff = tmp.foff, - }; - - if (_lbx_memcpy(new->offsets, new, (nfiles+1)*(sizeof *new->offsets))) - goto eof; - - return new; -eof: - free(new); - lbx_errno = LBX_EEOF; - return NULL; + return lbx_open(f, &lbx_default_fops, NULL, name); } size_t lbx_numfiles(struct lbx_state *lbx) @@ -186,21 +138,6 @@ int lbx_stat(struct lbx_state *lbx, size_t index, struct lbx_statbuf *buf) return 0; } -static size_t -_lbx_mextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of) -{ - size_t rc; - - assert(lbx->mem); - assert(base + len <= lbx->memsize); - - rc = fwrite(lbx->mem + base, 1, len, of); - if (rc < len) - lbx_errno = -errno; - - return rc; -} - static size_t _lbx_fextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of) { @@ -209,17 +146,18 @@ _lbx_fextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of) assert(lbx->f); - if (_lbx_fseek(lbx->f, &lbx->foff, base) == -1) + if (lbx->fops->seek(lbx->f, base, SEEK_SET) != 0) { + lbx_errno = -errno; return 0; + } while (len) { size_t amt = MIN(len, sizeof buf); - rc = fread(buf, 1, amt, lbx->f); - lbx->foff += rc; + rc = lbx->fops->read(buf, amt, lbx->f); len -= rc; if (rc < amt) { - if (feof(lbx->f)) lbx_errno = LBX_EEOF; + if (lbx->fops->eof(lbx->f)) lbx_errno = LBX_EEOF; else lbx_errno = -errno; break; } @@ -246,9 +184,6 @@ size_t lbx_extract(struct lbx_state *lbx, size_t index, FILE *of) base = lbx->offsets[index]; len = lbx->offsets[index+1] - lbx->offsets[index]; - - if (lbx->mem) - return _lbx_mextract(lbx, base, len, of); return _lbx_fextract(lbx, base, len, of); } @@ -256,13 +191,10 @@ int lbx_close(struct lbx_state *lbx) { int rc = 0; - if (!lbx) - return 0; - - if (lbx->f) - rc = fclose(lbx->f); - + if (lbx && lbx->dtor) + rc = lbx->dtor(lbx->f); free(lbx); + return rc; } diff --git a/src/lbx.h b/src/lbx.h index 89d4bb4..a788318 100644 --- a/src/lbx.h +++ b/src/lbx.h @@ -13,6 +13,24 @@ enum { }; extern int lbx_errno; +struct lbx_file_ops { + size_t (*read)(void *buf, size_t size, void *handle); + int (*seek)(void *handle, long offset, int whence); + long (*tell)(void *handle); + int (*eof) (void *handle); +}; + +struct lbx_pipe_state { + FILE *f; + long offset; +}; + +/* Default I/O operations for ordinary files. */ +extern const struct lbx_file_ops lbx_default_fops; + +/* I/O operations for un-seekable files (e.g. pipes). */ +extern const struct lbx_file_ops lbx_pipe_fops; + /* Opaque */ typedef struct lbx_state LBX; @@ -22,6 +40,8 @@ struct lbx_statbuf { }; /* Archive operations */ +LBX *lbx_open(void *handle, const struct lbx_file_ops *fops, + int (*destructor)(void *handle), const char *name); LBX *lbx_fopen(FILE *, const char *); LBX *lbx_mopen(void *, size_t, const char *); int lbx_close(LBX *); diff --git a/src/lbxtool.c b/src/lbxtool.c index 47ee37b..d070e8f 100644 --- a/src/lbxtool.c +++ b/src/lbxtool.c @@ -26,6 +26,7 @@ #include "tools.h" #include "lbx.h" + static void printusage(void) { puts("usage: lbxtool [-l|-x] [-v] [-f path] [file ...]"); @@ -67,17 +68,10 @@ int filematch(char **argv, const char *name) return i ? -1: 0; } -int list(FILE *f, const char *name, int verbose, char **argv) { - LBX *lbx; +int list(LBX *lbx, const char *name, int verbose, char **argv) { size_t nfiles; unsigned int i; - lbx = lbx_fopen(f, name); - if (!lbx) { - errmsg("failed to open archive: %s.\n", lbx_strerror()); - return EXIT_FAILURE; - } - nfiles = lbx_numfiles(lbx); if (verbose) { printf("Files in archive: %zu\n", nfiles); @@ -91,7 +85,7 @@ int list(FILE *f, const char *name, int verbose, char **argv) { switch (filematch(argv, stat.name)) { case -1: continue; case 0: break; - default: goto err; + default: return EXIT_FAILURE; } printf("%s", stat.name); @@ -102,24 +96,13 @@ int list(FILE *f, const char *name, int verbose, char **argv) { putchar('\n'); } - lbx_close(lbx); return EXIT_SUCCESS; -err: - lbx_close(lbx); - return EXIT_FAILURE; } -int extract(FILE *f, const char *name, int verbose, char **argv) { - LBX *lbx; +int extract(LBX *lbx, const char *name, int verbose, char **argv) { size_t nfiles; unsigned int i; - lbx = lbx_fopen(f, name); - if (!lbx) { - errmsg("failed to open archive: %s.\n", lbx_strerror()); - return EXIT_FAILURE; - } - nfiles = lbx_numfiles(lbx); if (verbose) { printf("Files in archive: %zu\n", nfiles); @@ -136,14 +119,14 @@ int extract(FILE *f, const char *name, int verbose, char **argv) { switch (filematch(argv, stat.name)) { case -1: continue; case 0: break; - default: goto err; + default: return EXIT_FAILURE; } of = fopen(stat.name, "wbx"); if (!of) { errmsg("failed to create output file %s: %m.\n", stat.name); - goto err; + return EXIT_FAILURE; } if (verbose) printf("extracting %s...\n", stat.name); @@ -152,24 +135,20 @@ int extract(FILE *f, const char *name, int verbose, char **argv) { if (rc == 0) remove(stat.name); errmsg("error extracting %s: %s.\n", stat.name, lbx_strerror()); - goto err; + return EXIT_FAILURE; } if (verbose) printf("wrote %zu bytes.\n", rc); } - lbx_close(lbx); return EXIT_SUCCESS; -err: - lbx_close(lbx); - return EXIT_FAILURE; } int main(int argc, char **argv) { - int mode = MODE_NONE, verbose = 0; + int mode = MODE_NONE, verbose = 0, opt, rc = EXIT_FAILURE; + struct lbx_pipe_state state = { .f = stdin }; const char *name = "stdin"; - FILE *f = stdin; - int opt; + LBX *lbx; static const char *sopts = "lxf:i:vV"; static const struct option lopts[] = { @@ -204,8 +183,7 @@ int main(int argc, char **argv) name = strrchr(optarg, '/'); name = name ? name+1 : optarg; - f = fopen(optarg, "rb"); - if (!f) { + if (!freopen(optarg, "rb", state.f)) { errmsg("failed to open file %s: %m\n", optarg); return EXIT_FAILURE; } @@ -230,13 +208,27 @@ int main(int argc, char **argv) } } + if (fseek(state.f, 0, SEEK_CUR) == 0) + lbx = lbx_open(state.f, &lbx_default_fops, NULL, name); + else + lbx = lbx_open(&state, &lbx_pipe_fops, NULL, name); + + if (!lbx) { + errmsg("failed to open archive: %s.\n", lbx_strerror()); + return EXIT_FAILURE; + } + switch (mode) { case MODE_LIST: - return list(f, name, verbose, &argv[optind]); + rc = list(lbx, name, verbose, &argv[optind]); + break; case MODE_EXTRACT: - return extract(f, name, verbose, &argv[optind]); + rc = extract(lbx, name, verbose, &argv[optind]); + break; + default: + fprintf(stderr, "%s: you must specify a mode.\n", progname); } - fprintf(stderr, "%s: you must specify a mode.\n", progname); - return EXIT_FAILURE; + lbx_close(lbx); + return rc; } -- 2.43.2