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