]> git.draconx.ca Git - liblbx.git/blob - src/lbx.c
Split off _lbx_fseek() into a shared component of the library.
[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 "misc.h"
17 #include "lbx.h"
18
19 #define LBX_MAGIC 0x0000fead
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 static size_t
173 _lbx_mextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of)
174 {
175         size_t rc;
176
177         assert(lbx->mem);
178         assert(base + len <= lbx->memsize);
179
180         rc = fwrite(lbx->mem + base, 1, len, of);
181         if (rc < len)
182                 lbx_errno = -errno;
183
184         return rc;
185 }
186
187 static size_t
188 _lbx_fextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of)
189 {
190         unsigned char buf[1024];
191         size_t rc, written = 0;
192
193         assert(lbx->f);
194
195         if (_lbx_fseek(lbx->f, &lbx->foff, base) == -1)
196                 return 0;
197         
198         while (len) {
199                 size_t amt = MIN(len, sizeof buf);
200
201                 rc = fread(buf, 1, amt, lbx->f);
202                 lbx->foff += rc;
203                 len -= rc;
204                 if (rc < amt) {
205                         if (feof(lbx->f)) lbx_errno = LBX_EEOF;
206                         else lbx_errno = -errno;
207                         break;
208                 }
209
210                 rc = fwrite(buf, 1, amt, of);
211                 written += rc;
212                 if (rc < amt) {
213                         lbx_errno = -errno;
214                         break;
215                 }
216         }
217
218         return written;
219 }
220
221 size_t lbx_extract(struct lbx_state *lbx, size_t index, FILE *of)
222 {
223         size_t base, len;
224
225         if (index >= lbx->nfiles) {
226                 lbx_errno = LBX_ERANGE;
227                 return 0;
228         }
229         
230         base = lbx->offsets[index];
231         len  = lbx->offsets[index+1] - lbx->offsets[index];
232
233         if (lbx->mem)
234                 return _lbx_mextract(lbx, base, len, of);
235         return _lbx_fextract(lbx, base, len, of);
236 }
237
238 void *lbx_mmap(struct lbx_state *lbx, size_t index, size_t *len)
239 {
240         unsigned char *mapping;
241         struct stat statbuf;
242         size_t base;
243
244         if (index >= lbx->nfiles) {
245                 lbx_errno = LBX_ERANGE;
246                 return NULL;
247         }
248         
249         base = lbx->offsets[index];
250         *len = lbx->offsets[index+1] - lbx->offsets[index];
251
252         if (lbx->mem)
253                 return lbx->mem + base;
254         
255         if (fstat(fileno(lbx->f), &statbuf) == -1) {
256                 lbx_errno = -errno;
257                 return NULL;
258         }
259
260         mapping = mmap(NULL, statbuf.st_size, PROT_READ, 0, fileno(lbx->f), 0);
261         if (mapping == MAP_FAILED) {
262                 lbx_errno = -errno;
263                 return NULL;
264         }
265
266         lbx->mem     = mapping;
267         lbx->memsize = statbuf.st_size;
268         return mapping + base;
269 }
270
271 void lbx_close(struct lbx_state *lbx)
272 {
273         if (!lbx) return;
274
275         if (lbx->f) {
276                 fclose(lbx->f);
277                 if (lbx->mem) {
278                         munmap(lbx->mem, lbx->memsize);
279                 }
280         }
281
282         free(lbx);
283 }
284
285 const char *lbx_strerror(void)
286 {
287         if (lbx_errno < 0)
288                 return strerror(-lbx_errno);
289
290         switch (lbx_errno) {
291         case LBX_ESUCCESS: return "Success";
292         case LBX_EMAGIC:   return "Bad magic number";
293         case LBX_EEOF:     return "Unexpected end-of-file";
294         case LBX_ERANGE:   return "Index out of range";
295         }
296
297         return "Unknown error";
298 }