1 /* 2ooM: The Master of Orion II Reverse Engineering Project
2 * Library for working with LBX archive files.
3 * Copyright (C) 2006-2008 Nick Bowler
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
33 #include "byteorder.h"
37 #define LBX_MAGIC 0x0000fead
53 struct lbx_state *lbx_fopen(FILE *f, const char *name)
55 struct lbx_state *new = NULL;
56 uint16_t nfiles, version;
59 if (fread(&nfiles, sizeof nfiles, 1, f) != 1) goto readerr;
60 if (fread(&magic, sizeof magic, 1, f) != 1) goto readerr;
61 if (fread(&version, sizeof version, 1, f) != 1) goto readerr;
63 nfiles = letohs(nfiles);
64 magic = letohl(magic);
65 version = letohs(version);
67 if (magic != LBX_MAGIC) {
68 lbx_errno = LBX_EMAGIC;
72 new = malloc(sizeof *new + (nfiles+1)*(sizeof *new->offsets));
78 *new = (struct lbx_state){
82 .foff = sizeof nfiles + sizeof magic + sizeof version,
85 if (fread(new->offsets, sizeof *new->offsets, nfiles+1, f) != nfiles+1)
87 new->foff += sizeof *new->offsets * (nfiles+1);
101 static int _lbx_memcpy(void *dest, struct lbx_state *src, size_t size)
103 if (src->foff + size > src->memsize)
105 memcpy(dest, src->mem + src->foff, size);
110 struct lbx_state *lbx_mopen(void *_mem, size_t size, const char *name)
112 struct lbx_state *new = NULL;
113 struct lbx_state tmp = { .mem = _mem, .memsize = size };
114 uint16_t nfiles, version;
117 if (_lbx_memcpy(&nfiles, &tmp, sizeof nfiles) == -1) goto eof;
118 if (_lbx_memcpy(&magic, &tmp, sizeof magic) == -1) goto eof;
119 if (_lbx_memcpy(&version, &tmp, sizeof version) == -1) goto eof;
121 nfiles = letohs(nfiles);
122 magic = letohl(magic);
123 version = letohs(version);
125 if (magic != LBX_MAGIC) {
126 lbx_errno = LBX_EMAGIC;
130 new = malloc(sizeof *new + (nfiles+1)*(sizeof *new->offsets));
136 *new = (struct lbx_state){
144 if (_lbx_memcpy(new->offsets, new, (nfiles+1)*(sizeof *new->offsets)))
150 lbx_errno = LBX_EEOF;
154 struct lbx_state *lbx_open(const char *path)
156 struct lbx_state *new = NULL;
159 if ((f = fopen(path, "rb"))) {
160 const char *name = strrchr(path, '/');
161 new = lbx_fopen(f, name ? name+1 : path);
169 size_t lbx_numfiles(struct lbx_state *lbx)
174 int lbx_stat(struct lbx_state *lbx, size_t index, struct lbx_statbuf *buf)
176 static char str[256]; /* FIXME */
178 if (index >= lbx->nfiles) {
180 lbx_errno = LBX_ERANGE;
184 snprintf(str, sizeof str, "%s.%03d", lbx->name, index);
186 buf->size = lbx->offsets[index+1] - lbx->offsets[index];
191 _lbx_mextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of)
196 assert(base + len <= lbx->memsize);
198 rc = fwrite(lbx->mem + base, 1, len, of);
206 _lbx_fextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of)
208 unsigned char buf[1024];
209 size_t rc, written = 0;
213 if (_lbx_fseek(lbx->f, &lbx->foff, base) == -1)
217 size_t amt = MIN(len, sizeof buf);
219 rc = fread(buf, 1, amt, lbx->f);
223 if (feof(lbx->f)) lbx_errno = LBX_EEOF;
224 else lbx_errno = -errno;
228 rc = fwrite(buf, 1, amt, of);
239 size_t lbx_extract(struct lbx_state *lbx, size_t index, FILE *of)
243 if (index >= lbx->nfiles) {
244 lbx_errno = LBX_ERANGE;
248 base = lbx->offsets[index];
249 len = lbx->offsets[index+1] - lbx->offsets[index];
252 return _lbx_mextract(lbx, base, len, of);
253 return _lbx_fextract(lbx, base, len, of);
256 void *lbx_mmap(struct lbx_state *lbx, size_t index, size_t *len)
258 unsigned char *mapping;
262 if (index >= lbx->nfiles) {
263 lbx_errno = LBX_ERANGE;
267 base = lbx->offsets[index];
268 *len = lbx->offsets[index+1] - lbx->offsets[index];
271 return lbx->mem + base;
273 if (fstat(fileno(lbx->f), &statbuf) == -1) {
278 mapping = mmap(NULL, statbuf.st_size, PROT_READ, 0, fileno(lbx->f), 0);
279 if (mapping == MAP_FAILED) {
285 lbx->memsize = statbuf.st_size;
286 return mapping + base;
289 void lbx_close(struct lbx_state *lbx)
296 munmap(lbx->mem, lbx->memsize);
303 const char *lbx_strerror(void)
306 return strerror(-lbx_errno);
309 case LBX_ESUCCESS: return "Success";
310 case LBX_EMAGIC: return "Bad magic number";
311 case LBX_EEOF: return "Unexpected end-of-file";
312 case LBX_ERANGE: return "Index out of range";
313 case LBX_EFORMAT: return "Invalid file format";
316 return "Unknown error";