]> git.draconx.ca Git - liblbx.git/blob - src/lbx.c
Initial implementation of list and extract operations.
[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
7 #include "byteorder.h"
8 #include "lbx.h"
9
10 #define LBX_MAGIC 0x0000fead
11 #define MIN(a,b) (((a)<(b))?(a):(b))
12
13 int lbx_errno = 0;
14
15 struct lbx_state {
16         const char *name;
17
18         FILE *f;
19         long foff;
20
21         uint16_t nfiles;
22         uint32_t offsets[];
23 };
24
25 struct lbx_state *lbx_fopen(FILE *f, const char *name)
26 {
27         struct lbx_state *new = NULL;
28         uint16_t nfiles, version;
29         uint32_t magic;
30
31         if (fread(&nfiles,  sizeof nfiles,  1, f) != 1) goto readerr;
32         if (fread(&magic,   sizeof magic,   1, f) != 1) goto readerr;
33         if (fread(&version, sizeof version, 1, f) != 1) goto readerr;
34
35         nfiles  = letohs(nfiles);
36         magic   = letohl(magic);
37         version = letohs(version);
38
39         if (magic != LBX_MAGIC) {
40                 lbx_errno = LBX_EMAGIC;
41                 return NULL;
42         }
43         
44         new = malloc(sizeof *new + (nfiles+1)*(sizeof *new->offsets));
45         if (!new) {
46                 lbx_errno = -errno;
47                 return NULL;
48         }
49         
50         *new = (struct lbx_state){
51                 .name   = name,
52                 .nfiles = nfiles,
53                 .f      = f,
54                 .foff   = sizeof nfiles + sizeof magic + sizeof version,
55         };
56         
57         if (fread(new->offsets, sizeof *new->offsets, nfiles+1, f) != nfiles+1)
58                 goto readerr;
59         new->foff += sizeof *new->offsets * (nfiles+1);
60
61         return new;
62 readerr:
63         if (feof(f)) {
64                 lbx_errno = LBX_EEOF;
65         } else {
66                 lbx_errno = -errno;
67         }
68
69         free(new);
70         return NULL;
71 }
72
73 struct lbx_state *lbx_open(const char *path)
74 {
75         struct lbx_state *new = NULL;
76         FILE *f;
77         
78         if ((f = fopen(path, "rb"))) {
79                 const char *name = strrchr(path, '/');
80                 new = lbx_fopen(f, name ? name+1 : path);
81         } else {
82                 lbx_errno = -errno;
83         }
84
85         return new;
86 }
87
88 size_t lbx_numfiles(struct lbx_state *lbx)
89 {
90         return lbx->nfiles;
91 }
92
93 int lbx_stat(struct lbx_state *lbx, size_t index, struct lbx_statbuf *buf)
94 {
95         static char str[256]; /* FIXME */
96
97         if (index >= lbx->nfiles) {
98                 buf->name = NULL;
99                 lbx_errno = LBX_ERANGE;
100                 return -1;
101         }
102
103         snprintf(str, sizeof str, "%s.%03d", lbx->name, index);
104         buf->name = str;
105         buf->size = lbx->offsets[index+1] - lbx->offsets[index];
106         return 0;
107 }
108
109 /*
110  * Read and discard len bytes from the file.  On success, returns 0.  On
111  * failure, returns -1 and sets lbx_errno.
112  */
113 static int skipdata(FILE *f, size_t len)
114 {
115         unsigned char buf[1024];
116         
117         while (len) {
118                 if (fread(buf, MIN(sizeof buf, len), 1, f) != 1) {
119                         if (feof(f)) {
120                                 lbx_errno = LBX_EEOF;
121                         } else {
122                                 lbx_errno = -errno;
123                         }
124                         return -1;
125                 }
126                 len -= MIN(sizeof buf, len);
127         }
128
129         return 0;
130 }
131
132 size_t lbx_extract(struct lbx_state *lbx, size_t index, FILE *of)
133 {
134         unsigned char buf[1024];
135         size_t rc, written = 0, base, len;
136
137         if (index >= lbx->nfiles) {
138                 lbx_errno = LBX_ERANGE;
139                 return 0;
140         }
141         
142         base = lbx->offsets[index];
143         len  = lbx->offsets[index+1] - lbx->offsets[index];
144
145         /* Seek to the beginning of the file. */
146         if (lbx->foff != base) {
147                 int seekrc;
148                 long dist = (lbx->foff < base) ?  (base - lbx->foff)
149                                                : -(lbx->foff - base);
150
151                 seekrc = fseek(lbx->f, dist, SEEK_CUR);
152                 if (seekrc == -1 && lbx->foff < base) {
153                         if (skipdata(lbx->f, dist) == -1) {
154                                 /* lbx_errno set by skipdata */
155                                 return 0;
156                         }
157                 } else if (seekrc == -1) {
158                         lbx_errno = -errno;
159                         return 0;
160                 }
161                 lbx->foff += dist; /* FIXME: failed skipdata breaks this. */
162         }
163
164         /* Copy file data */
165         while (len) {
166                 size_t amt = MIN(sizeof buf, len);
167
168                 rc = fread(buf, 1, amt, lbx->f);
169                 lbx->foff += rc;
170                 len -= rc;
171                 if (rc < amt) {
172                         if (feof(lbx->f)) {
173                                 lbx_errno = LBX_EEOF;
174                         } else {
175                                 lbx_errno = -errno;
176                         }
177                         break;
178                 }
179
180                 rc = fwrite(buf, 1, amt, of);
181                 written += rc;
182                 if (rc < amt) {
183                         lbx_errno = -errno;
184                         break;
185                 }
186         }
187
188         return written;
189 }
190
191 void lbx_close(struct lbx_state *lbx)
192 {
193         if (!lbx) return;
194
195         fclose(lbx->f);
196         free(lbx);
197 }
198
199 const char *lbx_strerror(void)
200 {
201         if (lbx_errno < 0)
202                 return strerror(-lbx_errno);
203
204         switch (lbx_errno) {
205         case LBX_ESUCCESS: return "Success";
206         case LBX_EMAGIC:   return "Bad magic number";
207         case LBX_EEOF:     return "Unexpected end-of-file";
208         case LBX_ERANGE:   return "Index out of range";
209         }
210
211         return "Unknown error";
212 }