]> git.draconx.ca Git - liblbx.git/blob - src/lbx.c
liblbx: Use unconditional includes of <config.h>.
[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-2010 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 #include <config.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <assert.h>
25
26 #include "pack.h"
27 #include "misc.h"
28 #include "error.h"
29 #include "lbx.h"
30
31 #define LBX_MAGIC    0x0000fead
32 #define LBX_HDR_SIZE 8
33
34 struct lbx_state {
35         char *name;
36
37         const struct lbx_file_ops *fops;
38         int (*dtor)(void *handle);
39         void *f;
40
41         struct lbx_file_state *last_file;
42
43         unsigned short nfiles;
44         unsigned long offsets[];
45 };
46
47 struct lbx_file_state {
48         unsigned long base, limit, offset;
49         struct lbx_state *lbx;
50         int eof;
51 };
52
53 static struct lbx_state *lbx_init(unsigned char hdr[static LBX_HDR_SIZE])
54 {
55         unsigned short nfiles  = unpack_16_le(hdr+0);
56         unsigned long  magic   = unpack_32_le(hdr+2);
57         unsigned short version = unpack_16_le(hdr+6);
58         struct lbx_state *lbx;
59
60         if (magic != LBX_MAGIC) {
61                 lbx_error_raise(LBX_EMAGIC);
62                 return NULL;
63         }
64
65         lbx = malloc(sizeof *lbx + sizeof lbx->offsets[0] * (nfiles+1));
66         if (!lbx) {
67                 lbx_error_raise(LBX_ENOMEM);
68                 return NULL;
69         }
70
71         *lbx = (struct lbx_state) {
72                 .nfiles = nfiles,
73         };
74
75         return lbx;
76 }
77
78 static char *str_dup(const char *s)
79 {
80         char *buf;
81
82         buf = malloc(strlen(s)+1);
83         if (buf)
84                 strcpy(buf, s);
85         return buf;
86 }
87
88 struct lbx_state *lbx_open(void *f, const struct lbx_file_ops *fops,
89                            int (*destructor)(void *), const char *name)
90 {
91         unsigned char hdr_buf[LBX_HDR_SIZE];
92         struct lbx_state *lbx = NULL;
93         char *dupname = NULL;
94
95         dupname = str_dup(name);
96         if (!dupname) {
97                 lbx_error_raise(LBX_ENOMEM);
98                 goto err;
99         }
100
101         if (fops->read(hdr_buf, sizeof hdr_buf, f) != sizeof hdr_buf) {
102                 if (fops->eof(f))
103                         lbx_error_raise(LBX_EEOF);
104                 goto err;
105         }
106
107         lbx = lbx_init(hdr_buf);
108         if (!lbx)
109                 goto err;
110
111         lbx->name = dupname;
112         lbx->dtor = destructor;
113         lbx->fops = fops;
114         lbx->f    = f;
115
116         for (unsigned i = 0; i <= lbx->nfiles; i++) {
117                 unsigned char buf[4];
118
119                 if (fops->read(buf, sizeof buf, f) != sizeof buf) {
120                         if (fops->eof(f))
121                                 lbx_error_raise(LBX_EEOF);
122                         goto err;
123                 }
124
125                 lbx->offsets[i] = unpack_32_le(buf);
126         }
127
128         return lbx;
129 err:
130         free(dupname);
131         free(lbx);
132         return NULL;
133 }
134
135 static int file_close(void *f)
136 {
137         return fclose((FILE *)f);
138 }
139
140 static int pipe_close(void *f)
141 {
142         struct lbx_pipe_state *p = f;
143         int rc;
144
145         rc = fclose(p->f);
146         free(p);
147         return rc;
148 }
149
150 static char *last_component(const char *name)
151 {
152         char *c;
153
154         /* TODO: Handle other path separators. */
155         c = strrchr(name, '/');
156         if (!c)
157                 return (char *)name;
158         return c+1;
159 }
160
161 struct lbx_state *lbx_fopen(const char *file)
162 {
163         const char *name = last_component(file);
164         struct lbx_pipe_state *p;
165         FILE *f;
166
167         f = fopen(file, "rb");
168         if (!f)
169                 return NULL;
170
171         if (fseek(f, 0, SEEK_CUR) == 0)
172                 return lbx_open(f, &lbx_default_fops, file_close, name);
173
174         p = malloc(sizeof *p);
175         if (!p) {
176                 fclose(f);
177                 return NULL;
178         }
179
180         *p = (struct lbx_pipe_state) { .f = f };
181         return lbx_open(p, &lbx_pipe_fops, pipe_close, name);
182 }
183
184 size_t lbx_numfiles(struct lbx_state *lbx)
185 {
186         return lbx->nfiles;
187 }
188
189 int
190 lbx_file_stat(struct lbx_state *lbx, unsigned fileno, struct lbx_statbuf *buf)
191 {
192         static char str[256]; /* FIXME */
193
194         if (fileno >= lbx->nfiles) {
195                 lbx_error_raise(LBX_ENOENT);
196                 buf->name = NULL;
197                 return -1;
198         }
199
200         snprintf(str, sizeof str, "%s.%03u", lbx->name, fileno);
201         buf->name = str;
202         buf->size = lbx->offsets[fileno+1] - lbx->offsets[fileno];
203         return 0;
204 }
205
206 int lbx_close(struct lbx_state *lbx)
207 {
208         int rc = 0;
209
210         if (lbx && lbx->dtor)
211                 rc = lbx->dtor(lbx->f);
212         free(lbx->name);
213         free(lbx);
214
215         return rc;
216 }
217
218 struct lbx_file_state *lbx_file_open(struct lbx_state *lbx, unsigned fileno)
219 {
220         struct lbx_file_state *state;
221
222         if (fileno >= lbx->nfiles) {
223                 lbx_error_raise(LBX_ENOENT);
224                 return NULL;
225         }
226
227         lbx->last_file = NULL;
228         if (lbx->fops->seek(lbx->f, lbx->offsets[fileno], SEEK_SET) != 0) {
229                 return NULL;
230         }
231
232         state = malloc(sizeof *state);
233         if (!state) {
234                 lbx_error_raise(LBX_ENOMEM);
235                 return NULL;
236         }
237
238         *state = (struct lbx_file_state) {
239                 .base  = lbx->offsets[fileno],
240                 .limit = lbx->offsets[fileno+1] - lbx->offsets[fileno],
241                 .lbx   = lbx,
242         };
243
244         lbx->last_file = state;
245         return state;
246 }
247
248 size_t lbx_file_read(struct lbx_file_state *f, void *buf, size_t n)
249 {
250         const struct lbx_file_ops *fops = f->lbx->fops;
251         size_t want = MIN(n, f->limit - f->offset);
252         size_t rc;
253
254         if (f != f->lbx->last_file) {
255                 f->lbx->last_file = NULL;
256                 if (fops->seek(f->lbx->f, f->base + f->limit, SEEK_SET) != 0)
257                         return 0;
258                 f->lbx->last_file = f;
259         }
260
261         rc = fops->read(buf, want, f->lbx->f);
262         f->offset += rc;
263
264         if (want < n || (rc < want && fops->eof(f->lbx->f)))
265                 f->eof = 1;
266         return rc;
267 }
268
269 int lbx_file_seek(struct lbx_file_state *f, long offset, int whence)
270 {
271         const struct lbx_file_ops *fops = f->lbx->fops;
272         unsigned long pos;
273
274         switch (whence) {
275         case SEEK_CUR:
276                 pos = f->offset + offset;
277                 break;
278         case SEEK_SET:
279                 pos = offset;
280                 break;
281         case SEEK_END:
282                 pos = f->limit + offset;
283                 break;
284         }
285
286         if (pos > f->limit)
287                 return -1;
288
289         f->lbx->last_file = NULL;
290         if (fops->seek(f->lbx->f, f->base + pos, SEEK_SET) != 0)
291                 return -1;
292
293         f->offset = pos;
294         f->lbx->last_file = f;
295         f->eof = 0;
296
297         return 0;
298 }
299
300 long lbx_file_tell(struct lbx_file_state *f)
301 {
302         return f->offset;
303 }
304
305 int lbx_file_eof(struct lbx_file_state *f)
306 {
307         return f->eof;
308 }
309
310 void lbx_file_close(struct lbx_file_state *f)
311 {
312         if (f->lbx->last_file == f)
313                 f->lbx->last_file = NULL;
314         free(f);
315 }