]> git.draconx.ca Git - liblbx.git/blob - src/lbx.c
Use AC_HEADER_ASSERT to allow users to disable assertions.
[liblbx.git] / src / lbx.c
1 #ifdef HAVE_CONFIG_H
2 #       include "config.h"
3 #endif
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdint.h>
9 #include <errno.h>
10 #include <assert.h>
11
12 #include <sys/stat.h>
13 #include <sys/mman.h>
14
15 #include "byteorder.h"
16 #include "lbx.h"
17
18 #define LBX_MAGIC 0x0000fead
19 #define MIN(a,b) (((a)<(b))?(a):(b))
20
21 int lbx_errno = 0;
22
23 struct lbx_state {
24         const char *name;
25
26         unsigned char *mem;
27         size_t memsize;
28         FILE *f;
29         long foff;
30
31         uint16_t nfiles;
32         uint32_t offsets[];
33 };
34
35 struct lbx_state *lbx_fopen(FILE *f, const char *name)
36 {
37         struct lbx_state *new = NULL;
38         uint16_t nfiles, version;
39         uint32_t magic;
40
41         if (fread(&nfiles,  sizeof nfiles,  1, f) != 1) goto readerr;
42         if (fread(&magic,   sizeof magic,   1, f) != 1) goto readerr;
43         if (fread(&version, sizeof version, 1, f) != 1) goto readerr;
44
45         nfiles  = letohs(nfiles);
46         magic   = letohl(magic);
47         version = letohs(version);
48
49         if (magic != LBX_MAGIC) {
50                 lbx_errno = LBX_EMAGIC;
51                 return NULL;
52         }
53         
54         new = malloc(sizeof *new + (nfiles+1)*(sizeof *new->offsets));
55         if (!new) {
56                 lbx_errno = -errno;
57                 return NULL;
58         }
59         
60         *new = (struct lbx_state){
61                 .name   = name,
62                 .nfiles = nfiles,
63                 .f      = f,
64                 .foff   = sizeof nfiles + sizeof magic + sizeof version,
65         };
66         
67         if (fread(new->offsets, sizeof *new->offsets, nfiles+1, f) != nfiles+1)
68                 goto readerr;
69         new->foff += sizeof *new->offsets * (nfiles+1);
70
71         return new;
72 readerr:
73         if (feof(f)) {
74                 lbx_errno = LBX_EEOF;
75         } else {
76                 lbx_errno = -errno;
77         }
78
79         free(new);
80         return NULL;
81 }
82
83 static int _lbx_memcpy(void *dest, struct lbx_state *src, size_t size)
84 {
85         if (src->foff + size > src->memsize)
86                 return -1;
87         memcpy(dest, src->mem + src->foff, size);
88         src->foff += size;
89         return 0;
90 }
91
92 struct lbx_state *lbx_mopen(void *_mem, size_t size, const char *name)
93 {
94         struct lbx_state *new = NULL;
95         struct lbx_state tmp = { .mem = _mem, .memsize = size };
96         uint16_t nfiles, version;
97         uint32_t magic;
98
99         if (_lbx_memcpy(&nfiles,  &tmp, sizeof nfiles)  == -1) goto eof;
100         if (_lbx_memcpy(&magic,   &tmp, sizeof magic)   == -1) goto eof;
101         if (_lbx_memcpy(&version, &tmp, sizeof version) == -1) goto eof;
102
103         nfiles  = letohs(nfiles);
104         magic   = letohl(magic);
105         version = letohs(version);
106
107         if (magic != LBX_MAGIC) {
108                 lbx_errno = LBX_EMAGIC;
109                 return NULL;
110         }
111         
112         new = malloc(sizeof *new + (nfiles+1)*(sizeof *new->offsets));
113         if (!new) {
114                 lbx_errno = -errno;
115                 return NULL;
116         }
117         
118         *new = (struct lbx_state){
119                 .name    = name,
120                 .nfiles  = nfiles,
121                 .mem     = _mem,
122                 .memsize = size,
123                 .foff    = tmp.foff,
124         };
125         
126         if (_lbx_memcpy(new->offsets, new, (nfiles+1)*(sizeof *new->offsets)))
127                 goto eof;
128
129         return new;
130 eof:
131         free(new);
132         lbx_errno = LBX_EEOF;
133         return NULL;
134 }
135
136 struct lbx_state *lbx_open(const char *path)
137 {
138         struct lbx_state *new = NULL;
139         FILE *f;
140         
141         if ((f = fopen(path, "rb"))) {
142                 const char *name = strrchr(path, '/');
143                 new = lbx_fopen(f, name ? name+1 : path);
144         } else {
145                 lbx_errno = -errno;
146         }
147
148         return new;
149 }
150
151 size_t lbx_numfiles(struct lbx_state *lbx)
152 {
153         return lbx->nfiles;
154 }
155
156 int lbx_stat(struct lbx_state *lbx, size_t index, struct lbx_statbuf *buf)
157 {
158         static char str[256]; /* FIXME */
159
160         if (index >= lbx->nfiles) {
161                 buf->name = NULL;
162                 lbx_errno = LBX_ERANGE;
163                 return -1;
164         }
165
166         snprintf(str, sizeof str, "%s.%03d", lbx->name, index);
167         buf->name = str;
168         buf->size = lbx->offsets[index+1] - lbx->offsets[index];
169         return 0;
170 }
171
172 /* Advance to the beginning of the index'th file by either fseek or reading. */
173 static int _lbx_fseek(struct lbx_state *lbx, size_t base)
174 {
175         static unsigned char oblivion[1024];
176         long dist;
177
178         if (lbx->foff < base) {
179                 dist = base - lbx->foff;
180         } else if (lbx->foff > base) {
181                 dist = -(long)(lbx->foff - base);
182         } else {
183                 return 0;
184         }
185
186         if (fseek(lbx->f, dist, SEEK_CUR) == 0) {
187                 lbx->foff += dist;
188         } else if (lbx->foff < base) {
189                 while (dist) {
190                         size_t rc, amt = MIN(sizeof oblivion, dist);
191                         rc = fread(oblivion, 1, amt, lbx->f);
192                         lbx->foff += rc;
193                         dist -= rc;
194                         if (rc < amt) {
195                                 if (feof(lbx->f))
196                                         lbx_errno = LBX_EEOF;
197                                 else
198                                         lbx_errno = -errno;
199                                 return -1;
200                         }
201                 }
202         } else {
203                 lbx_errno = -errno;
204                 return -1;
205         }
206         return 0;
207 }
208
209 static size_t
210 _lbx_mextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of)
211 {
212         size_t rc;
213
214         assert(lbx->mem);
215         assert(base + len <= lbx->memsize);
216
217         rc = fwrite(lbx->mem + base, 1, len, of);
218         if (rc < len)
219                 lbx_errno = -errno;
220
221         return rc;
222 }
223
224 static size_t
225 _lbx_fextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of)
226 {
227         unsigned char buf[1024];
228         size_t rc, written = 0;
229
230         assert(lbx->f);
231
232         if (_lbx_fseek(lbx, base) == -1)
233                 return 0;
234         
235         while (len) {
236                 size_t amt = MIN(len, sizeof buf);
237
238                 rc = fread(buf, 1, amt, lbx->f);
239                 lbx->foff += rc;
240                 len -= rc;
241                 if (rc < amt) {
242                         if (feof(lbx->f)) lbx_errno = LBX_EEOF;
243                         else lbx_errno = -errno;
244                         break;
245                 }
246
247                 rc = fwrite(buf, 1, amt, of);
248                 written += rc;
249                 if (rc < amt) {
250                         lbx_errno = -errno;
251                         break;
252                 }
253         }
254
255         return written;
256 }
257
258 size_t lbx_extract(struct lbx_state *lbx, size_t index, FILE *of)
259 {
260         size_t base, len;
261
262         if (index >= lbx->nfiles) {
263                 lbx_errno = LBX_ERANGE;
264                 return 0;
265         }
266         
267         base = lbx->offsets[index];
268         len  = lbx->offsets[index+1] - lbx->offsets[index];
269
270         if (lbx->mem)
271                 return _lbx_mextract(lbx, base, len, of);
272         return _lbx_fextract(lbx, base, len, of);
273 }
274
275 void *lbx_mmap(struct lbx_state *lbx, size_t index, size_t *len)
276 {
277         unsigned char *mapping;
278         struct stat statbuf;
279         size_t base;
280
281         if (index >= lbx->nfiles) {
282                 lbx_errno = LBX_ERANGE;
283                 return NULL;
284         }
285         
286         base = lbx->offsets[index];
287         *len = lbx->offsets[index+1] - lbx->offsets[index];
288
289         if (lbx->mem)
290                 return lbx->mem + base;
291         
292         if (fstat(fileno(lbx->f), &statbuf) == -1) {
293                 lbx_errno = -errno;
294                 return NULL;
295         }
296
297         mapping = mmap(NULL, statbuf.st_size, PROT_READ, 0, fileno(lbx->f), 0);
298         if (mapping == MAP_FAILED) {
299                 lbx_errno = -errno;
300                 return NULL;
301         }
302
303         lbx->mem     = mapping;
304         lbx->memsize = statbuf.st_size;
305         return mapping + base;
306 }
307
308 void lbx_close(struct lbx_state *lbx)
309 {
310         if (!lbx) return;
311
312         if (lbx->f) {
313                 fclose(lbx->f);
314                 if (lbx->mem) {
315                         munmap(lbx->mem, lbx->memsize);
316                 }
317         }
318
319         free(lbx);
320 }
321
322 const char *lbx_strerror(void)
323 {
324         if (lbx_errno < 0)
325                 return strerror(-lbx_errno);
326
327         switch (lbx_errno) {
328         case LBX_ESUCCESS: return "Success";
329         case LBX_EMAGIC:   return "Bad magic number";
330         case LBX_EEOF:     return "Unexpected end-of-file";
331         case LBX_ERANGE:   return "Index out of range";
332         }
333
334         return "Unknown error";
335 }