]> git.draconx.ca Git - liblbx.git/blob - src/lbx.c
liblbx: Remove exact-width integer types from lbx_state.
[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 <errno.h>
27 #include <assert.h>
28
29 #include <sys/stat.h>
30 #include <sys/mman.h>
31
32 #include "pack.h"
33 #include "misc.h"
34 #include "lbx.h"
35
36 #define LBX_MAGIC    0x0000fead
37 #define LBX_HDR_SIZE 8
38
39 int lbx_errno = 0;
40
41 struct lbx_state {
42         const char *name;
43
44         const struct lbx_file_ops *fops;
45         int (*dtor)(void *handle);
46         void *f;
47
48         unsigned short nfiles;
49         unsigned long offsets[];
50 };
51
52 static struct lbx_state *lbx_init(unsigned char hdr[static LBX_HDR_SIZE])
53 {
54         unsigned short nfiles  = unpack_16_le(hdr+0);
55         unsigned long  magic   = unpack_32_le(hdr+2);
56         unsigned short version = unpack_16_le(hdr+6);
57         struct lbx_state *lbx;
58
59         if (magic != LBX_MAGIC) {
60                 lbx_errno = -LBX_EMAGIC;
61                 return NULL;
62         }
63
64         lbx = malloc(sizeof *lbx + sizeof lbx->offsets[0] * (nfiles+1));
65         if (!lbx) {
66                 lbx_errno = -errno;
67                 return NULL;
68         }
69
70         *lbx = (struct lbx_state) {
71                 .nfiles = nfiles,
72         };
73
74         return lbx;
75 }
76
77 struct lbx_state *lbx_open(void *f, const struct lbx_file_ops *fops,
78                            int (*destructor)(void *), const char *name)
79 {
80         unsigned char hdr_buf[LBX_HDR_SIZE];
81         struct lbx_state *lbx;
82
83         if (fops->read(hdr_buf, sizeof hdr_buf, f) != sizeof hdr_buf) {
84                 lbx_errno = -errno;
85                 return NULL;
86         }
87
88         lbx = lbx_init(hdr_buf);
89         if (!lbx)
90                 return NULL;
91
92         lbx->dtor = destructor;
93         lbx->name = name;
94         lbx->fops = fops;
95         lbx->f    = f;
96
97         for (unsigned i = 0; i <= lbx->nfiles; i++) {
98                 unsigned char buf[4];
99
100                 if (fops->read(buf, sizeof buf, f) != sizeof buf) {
101                         lbx_errno = -errno;
102                         if (fops->eof(f))
103                                 lbx_errno = LBX_EEOF;
104                         free(lbx);
105                         return NULL;
106                 }
107
108                 lbx->offsets[i] = unpack_32_le(buf);
109         }
110
111         return lbx;
112 }
113
114 struct lbx_state *lbx_fopen(FILE *f, const char *name)
115 {
116         return lbx_open(f, &lbx_default_fops, NULL, name);
117 }
118
119 size_t lbx_numfiles(struct lbx_state *lbx)
120 {
121         return lbx->nfiles;
122 }
123
124 int lbx_stat(struct lbx_state *lbx, size_t index, struct lbx_statbuf *buf)
125 {
126         static char str[256]; /* FIXME */
127
128         if (index >= lbx->nfiles) {
129                 buf->name = NULL;
130                 lbx_errno = LBX_ERANGE;
131                 return -1;
132         }
133
134         snprintf(str, sizeof str, "%s.%03zu", lbx->name, index);
135         buf->name = str;
136         buf->size = lbx->offsets[index+1] - lbx->offsets[index];
137         return 0;
138 }
139
140 static size_t
141 _lbx_fextract(struct lbx_state *lbx, size_t base, size_t len, FILE *of)
142 {
143         unsigned char buf[1024];
144         size_t rc, written = 0;
145
146         assert(lbx->f);
147
148         if (lbx->fops->seek(lbx->f, base, SEEK_SET) != 0) {
149                 lbx_errno = -errno;
150                 return 0;
151         }
152         
153         while (len) {
154                 size_t amt = MIN(len, sizeof buf);
155
156                 rc = lbx->fops->read(buf, amt, lbx->f);
157                 len -= rc;
158                 if (rc < amt) {
159                         if (lbx->fops->eof(lbx->f)) lbx_errno = LBX_EEOF;
160                         else lbx_errno = -errno;
161                         break;
162                 }
163
164                 rc = fwrite(buf, 1, amt, of);
165                 written += rc;
166                 if (rc < amt) {
167                         lbx_errno = -errno;
168                         break;
169                 }
170         }
171
172         return written;
173 }
174
175 size_t lbx_extract(struct lbx_state *lbx, size_t index, FILE *of)
176 {
177         size_t base, len;
178
179         if (index >= lbx->nfiles) {
180                 lbx_errno = LBX_ERANGE;
181                 return 0;
182         }
183         
184         base = lbx->offsets[index];
185         len  = lbx->offsets[index+1] - lbx->offsets[index];
186         return _lbx_fextract(lbx, base, len, of);
187 }
188
189 int lbx_close(struct lbx_state *lbx)
190 {
191         int rc = 0;
192
193         if (lbx && lbx->dtor)
194                 rc = lbx->dtor(lbx->f);
195         free(lbx);
196
197         return rc;
198 }
199
200 const char *lbx_strerror(void)
201 {
202         if (lbx_errno < 0)
203                 return strerror(-lbx_errno);
204
205         switch (lbx_errno) {
206         case LBX_ESUCCESS: return "Success";
207         case LBX_EMAGIC:   return "Bad magic number";
208         case LBX_EEOF:     return "Unexpected end-of-file";
209         case LBX_ERANGE:   return "Index out of range";
210         case LBX_EFORMAT:  return "Invalid file format";
211         }
212
213         return "Unknown error";
214 }