]> git.draconx.ca Git - upkg.git/blob - src/libupkg.c
Allocate upkg and private structs in one go.
[upkg.git] / src / libupkg.c
1 /*
2  *  upkg: tool for manipulating Unreal Tournament packages.
3  *  Copyright (C) 2009 Nick Bowler
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "upkg.h"
25 #include "pack.h"
26
27 #define MIN(a, b) ((a) < (b) ? (a) : (b))
28
29 struct upkg_name {
30         unsigned long flags;
31         char *name;
32 };
33
34 struct upkg_export {
35         const char *name;
36
37         long package, class, super;
38         unsigned long flags;
39         unsigned long size, offset;
40 };
41
42 struct upkg_import {
43         const char *class_package, *class_name, *object_name;
44         long package;
45 };
46
47 struct upkg_private {
48         FILE *f;
49
50         struct upkg_name   *names;
51         struct upkg_export *exports;
52         struct upkg_import *imports;
53
54         unsigned long name_offset, export_offset, import_offset;
55         unsigned char guid[16];
56 };
57
58 /*
59  * Decode the compact index format from the upkg.  This format is fucked.
60  * Stores the result in *val and returns the number of input bytes read (or 0
61  * if the input is invalid, in which case *val is undefined).
62  */
63 static size_t decode_index(long *val, unsigned char *bytes, size_t n)
64 {
65         size_t i = 0;
66
67         *val = 0;
68         while (i < MIN(n, 5)) {
69                 /*
70                  * Least significant bytes are first, so we need to do this
71                  * nonsense.
72                  */
73                 long tmp = bytes[i] & (i == 0 ? 0x3f : 0x7f);
74
75                 if (i > 0) tmp <<= 6;
76                 if (i > 1) tmp <<= 7*(i-1);
77                 *val += tmp;
78
79                 if (!(bytes[i] & (i == 0 ? 0x40 : 0x80))) {
80                         i++;
81                         break;
82                 }
83
84                 i++;
85         }
86
87         if (i > MIN(n, 5) || n == 0)
88                 return 0;
89         if (bytes[0] & 0x80)
90                 *val = -*val;
91         return i;
92 }
93
94 static struct upkg *init_upkg(unsigned char hdr[static UPKG_HDR_SIZE])
95 {
96         struct {
97                 struct upkg pkg;
98                 struct upkg_private priv;
99         } *alloc;
100
101         alloc = malloc(sizeof *alloc);
102         if (!alloc) {
103                 return NULL;
104         }
105
106         alloc->pkg = (struct upkg) {
107                 .version      = unpack_16_le(hdr+4),
108                 .license      = unpack_16_le(hdr+6),
109                 .flags        = unpack_32_le(hdr+8),
110                 .name_count   = unpack_32_le(hdr+12),
111                 .export_count = unpack_32_le(hdr+20),
112                 .import_count = unpack_32_le(hdr+28),
113                 .priv         = &alloc->priv,
114         };
115
116         alloc->priv = (struct upkg_private) {
117                 .name_offset   = unpack_32_le(hdr+16),
118                 .export_offset = unpack_32_le(hdr+24),
119                 .import_offset = unpack_32_le(hdr+32),
120         };
121
122         return &alloc->pkg;
123 }
124
125 static int pkg_init_names(struct upkg *pkg)
126 {
127         size_t rc, len, nbuf = 0;
128         unsigned long index = 0;
129         char buf[512];
130
131         if (fseek(pkg->priv->f, pkg->priv->name_offset, SEEK_SET) != 0)
132                 return -1;
133
134         pkg->priv->names = malloc(pkg->name_count * sizeof *pkg->priv->names);
135         if (!pkg->priv->names)
136                 return -1;
137
138         while (index < pkg->name_count) {
139                 struct upkg_name *name = &pkg->priv->names[index];
140
141                 /* Read some data into buffer. */
142                 if (!feof(pkg->priv->f)) {
143                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
144                         if (rc == 0)
145                                 goto err;
146                         nbuf += rc;
147                 }
148
149                 if (pkg->version >= 64) {
150                         len = buf[0];
151                         if (nbuf <= len + 4 || buf[len])
152                                 goto err;
153                         name->name = malloc(len);
154                         if (!name->name)
155                                 goto err;
156                         memcpy(name->name, buf+1, len);
157                         name->flags = unpack_32_le(buf+len+1);
158                         len += 4;
159
160                         nbuf -= len + 1;
161                         memmove(buf, buf+len+1, nbuf);
162                         index++;
163                 } else {
164                         char *c = memchr(buf, 0, nbuf);
165                         if (!c || nbuf <= c - buf + 5)
166                                 goto err;
167                         len = c - buf + 1;
168                         name->name = malloc(len);
169                         if (!name->name)
170                                 goto err;
171                         memcpy(name->name, buf, len);
172                         name->flags = unpack_32_le(buf+len);
173                         len += 4;
174
175                         nbuf -= len;
176                         memmove(buf, buf+len, nbuf);
177                         index++;
178                 }
179         }
180
181         return 0;
182 err:
183         for (unsigned i = 0; i < index; i++)
184                 free(pkg->priv->names[i].name);
185         free(pkg->priv->names);
186         return -1;
187 }
188
189 static int pkg_init_exports(struct upkg *pkg)
190 {
191         size_t rc, len, nbuf = 0;
192         unsigned long index = 0;
193         char buf[512];
194
195         if (fseek(pkg->priv->f, pkg->priv->export_offset, SEEK_SET) != 0)
196                 return -1;
197
198         pkg->priv->exports = malloc(pkg->export_count * sizeof *pkg->priv->exports);
199         if (!pkg->priv->exports)
200                 return -1;
201
202         while (index < pkg->export_count) {
203                 struct upkg_export *export = &pkg->priv->exports[index];
204                 long tmp;
205
206                 /* Read some data into buffer. */
207                 if (!feof(pkg->priv->f)) {
208                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
209                         if (rc == 0)
210                                 goto err;
211                         nbuf += rc;
212                 }
213
214                 len = 0;
215                 rc = decode_index(&export->class, buf+len, nbuf-len);
216                 if (rc == 0) goto err;
217                 len += rc;
218
219                 rc = decode_index(&export->super, buf+len, nbuf-len);
220                 if (rc == 0) goto err;
221                 len += rc;
222
223                 if (nbuf-len < 4) goto err;
224                 export->package = unpack_32_le(buf+len);
225                 len += 4;
226
227                 rc = decode_index(&tmp, buf+len, nbuf-len);
228                 if (rc == 0 || tmp < 0 || tmp >= pkg->name_count) goto err;
229                 export->name = pkg->priv->names[tmp].name;
230                 len += rc;
231
232                 if (nbuf-len < 4) goto err;
233                 export->flags = unpack_32_le(buf+len);
234                 len += 4;
235
236                 rc = decode_index(&export->size, buf+len, nbuf-len);
237                 if (rc == 0) goto err;
238                 len += rc;
239
240                 if (export->size) {
241                         rc = decode_index(&export->offset, buf+len, nbuf-len);
242                         if (rc == 0) goto err;
243                         len += rc;
244                 }
245
246                 nbuf -= len;
247                 memmove(buf, buf+len, nbuf);
248                 index++;
249         }
250
251         return 0;
252 err:
253         free(pkg->priv->exports);
254         return -1;
255 }
256
257 static int pkg_init_imports(struct upkg *pkg)
258 {
259         size_t rc, len, nbuf = 0;
260         unsigned long index = 0;
261         char buf[512];
262
263         if (fseek(pkg->priv->f, pkg->priv->import_offset, SEEK_SET) != 0)
264                 return -1;
265
266         pkg->priv->imports = malloc(pkg->import_count * sizeof *pkg->priv->imports);
267         if (!pkg->priv->imports)
268                 return -1;
269
270         while (index < pkg->import_count) {
271                 struct upkg_import *import = &pkg->priv->imports[index];
272                 long tmp;
273
274                 /* Read some data into buffer. */
275                 if (!feof(pkg->priv->f)) {
276                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
277                         if (rc == 0)
278                                 goto err;
279                         nbuf += rc;
280                 }
281
282                 len = 0;
283                 rc = decode_index(&tmp, buf+len, nbuf-len);
284                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
285                 import->class_package = pkg->priv->names[tmp].name;
286                 len += rc;
287
288                 rc = decode_index(&tmp, buf+len, nbuf-len);
289                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
290                 import->class_name = pkg->priv->names[tmp].name;
291                 len += rc;
292
293                 if (nbuf-len < 4) goto err;
294                 import->package = unpack_32_le(buf+len);
295                 len += 4;
296
297                 rc = decode_index(&tmp, buf+len, nbuf-len);
298                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
299                 import->object_name = pkg->priv->names[tmp].name;
300                 len += rc;
301
302                 nbuf -= len;
303                 memmove(buf, buf+len, nbuf);
304                 index++;
305         }
306
307         return 0;
308 err:
309         free(pkg->priv->imports);
310         return -1;
311 }
312
313 struct upkg *upkg_fopen(const char *path)
314 {
315         unsigned char hdr_buf[UPKG_HDR_SIZE];
316         struct upkg *pkg;
317         FILE *f;
318
319         if (!(f = fopen(path, "rb"))) {
320                 return NULL;
321         }
322         if (fread(hdr_buf, sizeof hdr_buf, 1, f) != 1) {
323                 goto err1;
324         }
325         if (unpack_32_le(hdr_buf) != UPKG_HDR_MAGIC) {
326                 goto err1;
327         }
328
329         /* Initialize package structure. */
330         pkg = init_upkg(hdr_buf);
331         if (!pkg) {
332                 goto err1;
333         }
334         pkg->priv->f = f;
335
336         if (pkg_init_names(pkg) != 0) {
337                 goto err2;
338         }
339
340         if (pkg_init_exports(pkg) != 0) {
341                 goto err3;
342         }
343
344         if (pkg_init_imports(pkg) != 0) {
345                 goto err4;
346         }
347
348         return pkg;
349 err4:
350         free(pkg->priv->exports);
351 err3:
352         for (unsigned i = 0; i < pkg->name_count; i++)
353                 free(pkg->priv->names[i].name);
354         free(pkg->priv->names);
355 err2:
356         free(pkg);
357 err1:
358         fclose(f);
359         return NULL;
360 }
361
362 int upkg_close(struct upkg *pkg)
363 {
364         int rc = 0;
365
366         if (pkg->priv->f) {
367                 rc = fclose(pkg->priv->f);
368
369                 for (unsigned i = 0; i < pkg->name_count; i++) {
370                         free(pkg->priv->names[i].name);
371                 }
372         }
373
374         free(pkg->priv->imports);
375         free(pkg->priv->exports);
376         free(pkg->priv->names);
377         free(pkg);
378
379         return rc;
380 }
381
382 const char *upkg_get_name(struct upkg *pkg, unsigned long idx)
383 {
384         if (idx >= pkg->name_count)
385                 return 0;
386         return pkg->priv->names[idx].name;
387 }