]> git.draconx.ca Git - liblbx.git/blob - src/lbx.c
liblbx: Parameterise I/O functions to allow custom streams.
[liblbx.git] / src / lbx.c
1 /*
2  *  2ooM: The Master of Orion II Reverse Engineering Project
3  *  Library for working with LBX archive files.
4  *  Copyright (C) 2006-2008 Nick Bowler
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #ifdef HAVE_CONFIG_H
20 #       include "config.h"
21 #endif
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdint.h>
27 #include <errno.h>
28 #include <assert.h>
29
30 #include <sys/stat.h>
31 #include <sys/mman.h>
32
33 #include "pack.h"
34 #include "misc.h"
35 #include "lbx.h"
36
37 #define LBX_MAGIC    0x0000fead
38 #define LBX_HDR_SIZE 8
39
40 int lbx_errno = 0;
41
42 struct lbx_state {
43         const char *name;
44
45         const struct lbx_file_ops *fops;
46         int (*dtor)(void *handle);
47         void *f;
48
49         uint16_t nfiles;
50         uint32_t offsets[];
51 };
52
53 static struct lbx_state *lbx_init(unsigned char hdr[static LBX_HDR_SIZE])
54 {
55         unsigned short nfiles  = unpack_16_le(hdr+0);
56         unsigned long  magic   = unpack_32_le(hdr+2);
57         unsigned short version = unpack_16_le(hdr+6);
58         struct lbx_state *lbx;
59
60         if (magic != LBX_MAGIC) {
61                 lbx_errno = -LBX_EMAGIC;
62                 return NULL;
63         }
64
65         lbx = malloc(sizeof *lbx + sizeof lbx->offsets[0] * (nfiles+1));
66         if (!lbx) {
67                 lbx_errno = -errno;
68                 return NULL;
69         }
70
71         *lbx = (struct lbx_state) {
72                 .nfiles = nfiles,
73         };
74
75         return lbx;
76 }
77
78 struct lbx_state *lbx_open(void *f, const struct lbx_file_ops *fops,
79                            int (*destructor)(void *), const char *name)
80 {
81         unsigned char hdr_buf[LBX_HDR_SIZE];
82         struct lbx_state *lbx;
83
84         if (fops->read(hdr_buf, sizeof hdr_buf, f) != sizeof hdr_buf) {
85                 lbx_errno = -errno;
86                 return NULL;
87         }
88
89         lbx = lbx_init(hdr_buf);
90         if (!lbx)
91                 return NULL;
92
93         lbx->dtor = destructor;
94         lbx->name = name;
95         lbx->fops = fops;
96         lbx->f    = f;
97
98         for (unsigned i = 0; i <= lbx->nfiles; i++) {
99                 unsigned char buf[4];
100
101                 if (fops->read(buf, sizeof buf, f) != sizeof buf) {
102                         lbx_errno = -errno;
103                         if (fops->eof(f))
104                                 lbx_errno = LBX_EEOF;
105                         free(lbx);
106                         return NULL;
107                 }
108
109                 lbx->offsets[i] = unpack_32_le(buf);
110         }
111
112         return lbx;
113 }
114
115 struct lbx_state *lbx_fopen(FILE *f, const char *name)
116 {
117         return lbx_open(f, &lbx_default_fops, NULL, name);
118 }
119
120 size_t lbx_numfiles(struct lbx_state *lbx)
121 {
122         return lbx->nfiles;
123 }
124
125 int lbx_stat(struct lbx_state *lbx, size_t index, struct lbx_statbuf *buf)
126 {
127         static char str[256]; /* FIXME */
128
129         if (index >= lbx->nfiles) {
130                 buf->name = NULL;
131                 lbx_errno = LBX_ERANGE;
132                 return -1;
133         }
134
135         snprintf(str, sizeof str, "%s.%03zu", lbx->name, index);
136         buf->name = str;
137         buf->size = lbx->offsets[index+1] - lbx->offsets[index];
138         return 0;
139 }
140
141 static size_t
142 _lbx_fextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of)
143 {
144         unsigned char buf[1024];
145         size_t rc, written = 0;
146
147         assert(lbx->f);
148
149         if (lbx->fops->seek(lbx->f, base, SEEK_SET) != 0) {
150                 lbx_errno = -errno;
151                 return 0;
152         }
153         
154         while (len) {
155                 size_t amt = MIN(len, sizeof buf);
156
157                 rc = lbx->fops->read(buf, amt, lbx->f);
158                 len -= rc;
159                 if (rc < amt) {
160                         if (lbx->fops->eof(lbx->f)) lbx_errno = LBX_EEOF;
161                         else lbx_errno = -errno;
162                         break;
163                 }
164
165                 rc = fwrite(buf, 1, amt, of);
166                 written += rc;
167                 if (rc < amt) {
168                         lbx_errno = -errno;
169                         break;
170                 }
171         }
172
173         return written;
174 }
175
176 size_t lbx_extract(struct lbx_state *lbx, size_t index, FILE *of)
177 {
178         size_t base, len;
179
180         if (index >= lbx->nfiles) {
181                 lbx_errno = LBX_ERANGE;
182                 return 0;
183         }
184         
185         base = lbx->offsets[index];
186         len  = lbx->offsets[index+1] - lbx->offsets[index];
187         return _lbx_fextract(lbx, base, len, of);
188 }
189
190 int lbx_close(struct lbx_state *lbx)
191 {
192         int rc = 0;
193
194         if (lbx && lbx->dtor)
195                 rc = lbx->dtor(lbx->f);
196         free(lbx);
197
198         return rc;
199 }
200
201 const char *lbx_strerror(void)
202 {
203         if (lbx_errno < 0)
204                 return strerror(-lbx_errno);
205
206         switch (lbx_errno) {
207         case LBX_ESUCCESS: return "Success";
208         case LBX_EMAGIC:   return "Bad magic number";
209         case LBX_EEOF:     return "Unexpected end-of-file";
210         case LBX_ERANGE:   return "Index out of range";
211         case LBX_EFORMAT:  return "Invalid file format";
212         }
213
214         return "Unknown error";
215 }