]> git.draconx.ca Git - liblbx.git/blob - src/lbx.c
liblbx: Convert lbx_fopen to use the packing routines from libupkg.
[liblbx.git] / src / lbx.c
1 /*
2  *  2ooM: The Master of Orion II Reverse Engineering Project
3  *  Library for working with LBX archive files.
4  *  Copyright (C) 2006-2008 Nick Bowler
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #ifdef HAVE_CONFIG_H
20 #       include "config.h"
21 #endif
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdint.h>
27 #include <errno.h>
28 #include <assert.h>
29
30 #include <sys/stat.h>
31 #include <sys/mman.h>
32
33 #include "pack.h"
34 #include "misc.h"
35 #include "lbx.h"
36
37 #define LBX_MAGIC    0x0000fead
38 #define LBX_HDR_SIZE 8
39
40 int lbx_errno = 0;
41
42 struct lbx_state {
43         const char *name;
44
45         unsigned char *mem;
46         size_t memsize;
47         FILE *f;
48         long foff;
49
50         uint16_t nfiles;
51         uint32_t offsets[];
52 };
53
54 static struct lbx_state *lbx_init(unsigned char hdr[static LBX_HDR_SIZE])
55 {
56         unsigned short nfiles  = unpack_16_le(hdr+0);
57         unsigned long  magic   = unpack_32_le(hdr+2);
58         unsigned short version = unpack_16_le(hdr+6);
59         struct lbx_state *lbx;
60
61         if (magic != LBX_MAGIC) {
62                 lbx_errno = -LBX_EMAGIC;
63                 return NULL;
64         }
65
66         lbx = malloc(sizeof *lbx + sizeof lbx->offsets[0] * (nfiles+1));
67         if (!lbx) {
68                 lbx_errno = -errno;
69                 return NULL;
70         }
71
72         *lbx = (struct lbx_state) {
73                 .nfiles = nfiles,
74         };
75
76         return lbx;
77 }
78
79 struct lbx_state *lbx_fopen(FILE *f, const char *name)
80 {
81         unsigned char hdr_buf[LBX_HDR_SIZE];
82         struct lbx_state *lbx;
83
84         if (fread(hdr_buf, 1, sizeof hdr_buf, f) != sizeof hdr_buf) {
85                 lbx_errno = -errno;
86                 return NULL;
87         }
88
89         lbx = lbx_init(hdr_buf);
90         if (!lbx)
91                 return NULL;
92
93         lbx->name = name;
94         lbx->f    = f;
95         lbx->foff = sizeof hdr_buf;
96
97         for (unsigned i = 0; i <= lbx->nfiles; i++) {
98                 unsigned char buf[4];
99
100                 if (fread(buf, 1, sizeof buf, f) != sizeof buf) {
101                         lbx_errno = -errno;
102                         if (feof(f))
103                                 lbx_errno = LBX_EEOF;
104                         free(lbx);
105                         return NULL;
106                 }
107
108                 lbx->offsets[i] = unpack_32_le(buf);
109                 lbx->foff += sizeof buf;
110         }
111
112         return lbx;
113 }
114
115 static int _lbx_memcpy(void *dest, struct lbx_state *src, size_t size)
116 {
117         if (src->foff + size > src->memsize)
118                 return -1;
119         memcpy(dest, src->mem + src->foff, size);
120         src->foff += size;
121         return 0;
122 }
123
124 struct lbx_state *lbx_mopen(void *_mem, size_t size, const char *name)
125 {
126         struct lbx_state *new = NULL;
127         struct lbx_state tmp = { .mem = _mem, .memsize = size };
128         uint16_t nfiles, version;
129         uint32_t magic;
130
131         if (_lbx_memcpy(&nfiles,  &tmp, sizeof nfiles)  == -1) goto eof;
132         if (_lbx_memcpy(&magic,   &tmp, sizeof magic)   == -1) goto eof;
133         if (_lbx_memcpy(&version, &tmp, sizeof version) == -1) goto eof;
134
135         nfiles  = letohs(nfiles);
136         magic   = letohl(magic);
137         version = letohs(version);
138
139         if (magic != LBX_MAGIC) {
140                 lbx_errno = LBX_EMAGIC;
141                 return NULL;
142         }
143         
144         new = malloc(sizeof *new + (nfiles+1)*(sizeof *new->offsets));
145         if (!new) {
146                 lbx_errno = -errno;
147                 return NULL;
148         }
149         
150         *new = (struct lbx_state){
151                 .name    = name,
152                 .nfiles  = nfiles,
153                 .mem     = _mem,
154                 .memsize = size,
155                 .foff    = tmp.foff,
156         };
157         
158         if (_lbx_memcpy(new->offsets, new, (nfiles+1)*(sizeof *new->offsets)))
159                 goto eof;
160
161         return new;
162 eof:
163         free(new);
164         lbx_errno = LBX_EEOF;
165         return NULL;
166 }
167
168 struct lbx_state *lbx_open(const char *path)
169 {
170         struct lbx_state *new = NULL;
171         FILE *f;
172         
173         if ((f = fopen(path, "rb"))) {
174                 const char *name = strrchr(path, '/');
175                 new = lbx_fopen(f, name ? name+1 : path);
176         } else {
177                 lbx_errno = -errno;
178         }
179
180         return new;
181 }
182
183 size_t lbx_numfiles(struct lbx_state *lbx)
184 {
185         return lbx->nfiles;
186 }
187
188 int lbx_stat(struct lbx_state *lbx, size_t index, struct lbx_statbuf *buf)
189 {
190         static char str[256]; /* FIXME */
191
192         if (index >= lbx->nfiles) {
193                 buf->name = NULL;
194                 lbx_errno = LBX_ERANGE;
195                 return -1;
196         }
197
198         snprintf(str, sizeof str, "%s.%03zu", lbx->name, index);
199         buf->name = str;
200         buf->size = lbx->offsets[index+1] - lbx->offsets[index];
201         return 0;
202 }
203
204 static size_t
205 _lbx_mextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of)
206 {
207         size_t rc;
208
209         assert(lbx->mem);
210         assert(base + len <= lbx->memsize);
211
212         rc = fwrite(lbx->mem + base, 1, len, of);
213         if (rc < len)
214                 lbx_errno = -errno;
215
216         return rc;
217 }
218
219 static size_t
220 _lbx_fextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of)
221 {
222         unsigned char buf[1024];
223         size_t rc, written = 0;
224
225         assert(lbx->f);
226
227         if (_lbx_fseek(lbx->f, &lbx->foff, base) == -1)
228                 return 0;
229         
230         while (len) {
231                 size_t amt = MIN(len, sizeof buf);
232
233                 rc = fread(buf, 1, amt, lbx->f);
234                 lbx->foff += rc;
235                 len -= rc;
236                 if (rc < amt) {
237                         if (feof(lbx->f)) lbx_errno = LBX_EEOF;
238                         else lbx_errno = -errno;
239                         break;
240                 }
241
242                 rc = fwrite(buf, 1, amt, of);
243                 written += rc;
244                 if (rc < amt) {
245                         lbx_errno = -errno;
246                         break;
247                 }
248         }
249
250         return written;
251 }
252
253 size_t lbx_extract(struct lbx_state *lbx, size_t index, FILE *of)
254 {
255         size_t base, len;
256
257         if (index >= lbx->nfiles) {
258                 lbx_errno = LBX_ERANGE;
259                 return 0;
260         }
261         
262         base = lbx->offsets[index];
263         len  = lbx->offsets[index+1] - lbx->offsets[index];
264
265         if (lbx->mem)
266                 return _lbx_mextract(lbx, base, len, of);
267         return _lbx_fextract(lbx, base, len, of);
268 }
269
270 void *lbx_mmap(struct lbx_state *lbx, size_t index, size_t *len)
271 {
272         unsigned char *mapping;
273         struct stat statbuf;
274         size_t base;
275
276         if (index >= lbx->nfiles) {
277                 lbx_errno = LBX_ERANGE;
278                 return NULL;
279         }
280         
281         base = lbx->offsets[index];
282         *len = lbx->offsets[index+1] - lbx->offsets[index];
283
284         if (lbx->mem)
285                 return lbx->mem + base;
286         
287         if (fstat(fileno(lbx->f), &statbuf) == -1) {
288                 lbx_errno = -errno;
289                 return NULL;
290         }
291
292         mapping = mmap(NULL, statbuf.st_size, PROT_READ, 0, fileno(lbx->f), 0);
293         if (mapping == MAP_FAILED) {
294                 lbx_errno = -errno;
295                 return NULL;
296         }
297
298         lbx->mem     = mapping;
299         lbx->memsize = statbuf.st_size;
300         return mapping + base;
301 }
302
303 void lbx_close(struct lbx_state *lbx)
304 {
305         if (!lbx) return;
306
307         if (lbx->f) {
308                 fclose(lbx->f);
309                 if (lbx->mem) {
310                         munmap(lbx->mem, lbx->memsize);
311                 }
312         }
313
314         free(lbx);
315 }
316
317 const char *lbx_strerror(void)
318 {
319         if (lbx_errno < 0)
320                 return strerror(-lbx_errno);
321
322         switch (lbx_errno) {
323         case LBX_ESUCCESS: return "Success";
324         case LBX_EMAGIC:   return "Bad magic number";
325         case LBX_EEOF:     return "Unexpected end-of-file";
326         case LBX_ERANGE:   return "Index out of range";
327         case LBX_EFORMAT:  return "Invalid file format";
328         }
329
330         return "Unknown error";
331 }