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