]> git.draconx.ca Git - upkg.git/blob - src/libupkg.c
Export the decode_index function from the upkg library.
[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_file *last_file;
51
52         struct upkg_name   *names;
53         struct upkg_export *exports;
54         struct upkg_import *imports;
55
56         unsigned long name_offset, export_offset, import_offset;
57         unsigned char guid[16];
58 };
59
60 /*
61  * Decode the compact index format from the upkg.  This format is fucked.
62  * Stores the result in *val and returns the number of input bytes read (or 0
63  * if the input is invalid, in which case *val is undefined).
64  */
65 size_t upkg_decode_index(long *val, unsigned char *bytes, size_t n)
66 {
67         size_t i = 0;
68
69         *val = 0;
70         while (i < MIN(n, 5)) {
71                 /*
72                  * Least significant bytes are first, so we need to do this
73                  * nonsense.
74                  */
75                 long tmp = bytes[i] & (i == 0 ? 0x3f : 0x7f);
76
77                 if (i > 0) tmp <<= 6;
78                 if (i > 1) tmp <<= 7*(i-1);
79                 *val += tmp;
80
81                 if (!(bytes[i] & (i == 0 ? 0x40 : 0x80))) {
82                         i++;
83                         break;
84                 }
85
86                 i++;
87         }
88
89         if (i > MIN(n, 5) || n == 0)
90                 return 0;
91         if (bytes[0] & 0x80)
92                 *val = -*val;
93         return i;
94 }
95
96 static struct upkg *init_upkg(unsigned char hdr[static UPKG_HDR_SIZE])
97 {
98         struct {
99                 struct upkg pkg;
100                 struct upkg_private priv;
101         } *alloc;
102
103         alloc = malloc(sizeof *alloc);
104         if (!alloc) {
105                 return NULL;
106         }
107
108         alloc->pkg = (struct upkg) {
109                 .version      = unpack_16_le(hdr+4),
110                 .license      = unpack_16_le(hdr+6),
111                 .flags        = unpack_32_le(hdr+8),
112                 .name_count   = unpack_32_le(hdr+12),
113                 .export_count = unpack_32_le(hdr+20),
114                 .import_count = unpack_32_le(hdr+28),
115                 .priv         = &alloc->priv,
116         };
117
118         alloc->priv = (struct upkg_private) {
119                 .name_offset   = unpack_32_le(hdr+16),
120                 .export_offset = unpack_32_le(hdr+24),
121                 .import_offset = unpack_32_le(hdr+32),
122         };
123
124         return &alloc->pkg;
125 }
126
127 static int pkg_init_names(struct upkg *pkg)
128 {
129         size_t rc, len, nbuf = 0;
130         unsigned long index = 0;
131         char buf[512];
132
133         if (fseek(pkg->priv->f, pkg->priv->name_offset, SEEK_SET) != 0)
134                 return -1;
135
136         pkg->priv->names = malloc(pkg->name_count * sizeof *pkg->priv->names);
137         if (!pkg->priv->names)
138                 return -1;
139
140         while (index < pkg->name_count) {
141                 struct upkg_name *name = &pkg->priv->names[index];
142
143                 /* Read some data into buffer. */
144                 if (!feof(pkg->priv->f)) {
145                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
146                         if (rc == 0)
147                                 goto err;
148                         nbuf += rc;
149                 }
150
151                 if (pkg->version >= 64) {
152                         len = buf[0];
153                         if (nbuf <= len + 4 || buf[len])
154                                 goto err;
155                         name->name = malloc(len);
156                         if (!name->name)
157                                 goto err;
158                         memcpy(name->name, buf+1, len);
159                         name->flags = unpack_32_le(buf+len+1);
160                         len += 4;
161
162                         nbuf -= len + 1;
163                         memmove(buf, buf+len+1, nbuf);
164                         index++;
165                 } else {
166                         char *c = memchr(buf, 0, nbuf);
167                         if (!c || nbuf <= c - buf + 5)
168                                 goto err;
169                         len = c - buf + 1;
170                         name->name = malloc(len);
171                         if (!name->name)
172                                 goto err;
173                         memcpy(name->name, buf, len);
174                         name->flags = unpack_32_le(buf+len);
175                         len += 4;
176
177                         nbuf -= len;
178                         memmove(buf, buf+len, nbuf);
179                         index++;
180                 }
181         }
182
183         return 0;
184 err:
185         for (unsigned i = 0; i < index; i++)
186                 free(pkg->priv->names[i].name);
187         free(pkg->priv->names);
188         return -1;
189 }
190
191 static int pkg_init_exports(struct upkg *pkg)
192 {
193         size_t rc, len, nbuf = 0;
194         unsigned long index = 0;
195         char buf[512];
196
197         if (fseek(pkg->priv->f, pkg->priv->export_offset, SEEK_SET) != 0)
198                 return -1;
199
200         pkg->priv->exports = malloc(pkg->export_count * sizeof *pkg->priv->exports);
201         if (!pkg->priv->exports)
202                 return -1;
203
204         while (index < pkg->export_count) {
205                 struct upkg_export *export = &pkg->priv->exports[index];
206                 long tmp;
207
208                 /* Read some data into buffer. */
209                 if (!feof(pkg->priv->f)) {
210                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
211                         if (rc == 0)
212                                 goto err;
213                         nbuf += rc;
214                 }
215
216                 len = 0;
217                 rc = upkg_decode_index(&export->class, buf+len, nbuf-len);
218                 if (rc == 0) goto err;
219                 len += rc;
220
221                 rc = upkg_decode_index(&export->super, buf+len, nbuf-len);
222                 if (rc == 0) goto err;
223                 len += rc;
224
225                 if (nbuf-len < 4) goto err;
226                 export->package = unpack_32_le(buf+len);
227                 len += 4;
228
229                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
230                 if (rc == 0 || tmp < 0 || tmp >= pkg->name_count) goto err;
231                 export->name = pkg->priv->names[tmp].name;
232                 len += rc;
233
234                 if (nbuf-len < 4) goto err;
235                 export->flags = unpack_32_le(buf+len);
236                 len += 4;
237
238                 rc = upkg_decode_index(&export->size, buf+len, nbuf-len);
239                 if (rc == 0) goto err;
240                 len += rc;
241
242                 if (export->size) {
243                         rc = upkg_decode_index(&export->offset, buf+len, nbuf-len);
244                         if (rc == 0) goto err;
245                         len += rc;
246                 }
247
248                 nbuf -= len;
249                 memmove(buf, buf+len, nbuf);
250                 index++;
251         }
252
253         return 0;
254 err:
255         free(pkg->priv->exports);
256         return -1;
257 }
258
259 static int pkg_init_imports(struct upkg *pkg)
260 {
261         size_t rc, len, nbuf = 0;
262         unsigned long index = 0;
263         char buf[512];
264
265         if (fseek(pkg->priv->f, pkg->priv->import_offset, SEEK_SET) != 0)
266                 return -1;
267
268         pkg->priv->imports = malloc(pkg->import_count * sizeof *pkg->priv->imports);
269         if (!pkg->priv->imports)
270                 return -1;
271
272         while (index < pkg->import_count) {
273                 struct upkg_import *import = &pkg->priv->imports[index];
274                 long tmp;
275
276                 /* Read some data into buffer. */
277                 if (!feof(pkg->priv->f)) {
278                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
279                         if (rc == 0)
280                                 goto err;
281                         nbuf += rc;
282                 }
283
284                 len = 0;
285                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
286                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
287                 import->class_package = pkg->priv->names[tmp].name;
288                 len += rc;
289
290                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
291                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
292                 import->class_name = pkg->priv->names[tmp].name;
293                 len += rc;
294
295                 if (nbuf-len < 4) goto err;
296                 import->package = unpack_32_le(buf+len);
297                 len += 4;
298
299                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
300                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
301                 import->object_name = pkg->priv->names[tmp].name;
302                 len += rc;
303
304                 nbuf -= len;
305                 memmove(buf, buf+len, nbuf);
306                 index++;
307         }
308
309         return 0;
310 err:
311         free(pkg->priv->imports);
312         return -1;
313 }
314
315 struct upkg *upkg_fopen(const char *path)
316 {
317         unsigned char hdr_buf[UPKG_HDR_SIZE];
318         struct upkg *pkg;
319         FILE *f;
320
321         if (!(f = fopen(path, "rb"))) {
322                 return NULL;
323         }
324         if (fread(hdr_buf, sizeof hdr_buf, 1, f) != 1) {
325                 goto err1;
326         }
327         if (unpack_32_le(hdr_buf) != UPKG_HDR_MAGIC) {
328                 goto err1;
329         }
330
331         /* Initialize package structure. */
332         pkg = init_upkg(hdr_buf);
333         if (!pkg) {
334                 goto err1;
335         }
336         pkg->priv->f = f;
337
338         if (pkg_init_names(pkg) != 0) {
339                 goto err2;
340         }
341
342         if (pkg_init_exports(pkg) != 0) {
343                 goto err3;
344         }
345
346         if (pkg_init_imports(pkg) != 0) {
347                 goto err4;
348         }
349
350         return pkg;
351 err4:
352         free(pkg->priv->exports);
353 err3:
354         for (unsigned i = 0; i < pkg->name_count; i++)
355                 free(pkg->priv->names[i].name);
356         free(pkg->priv->names);
357 err2:
358         free(pkg);
359 err1:
360         fclose(f);
361         return NULL;
362 }
363
364 int upkg_close(struct upkg *pkg)
365 {
366         int rc = 0;
367
368         if (pkg->priv->f) {
369                 rc = fclose(pkg->priv->f);
370
371                 for (unsigned i = 0; i < pkg->name_count; i++) {
372                         free(pkg->priv->names[i].name);
373                 }
374         }
375
376         free(pkg->priv->imports);
377         free(pkg->priv->exports);
378         free(pkg->priv->names);
379         free(pkg);
380
381         return rc;
382 }
383
384 const char *upkg_get_name(struct upkg *pkg, unsigned long idx)
385 {
386         if (idx >= pkg->name_count)
387                 return 0;
388         return pkg->priv->names[idx].name;
389 }
390
391 struct upkg_file *upkg_export_open(struct upkg *pkg, unsigned long idx)
392 {
393         struct upkg_file *f;
394
395         if (idx >= pkg->export_count)
396                 return NULL;
397
398         f = malloc(sizeof *f);
399         if (f == NULL)
400                 return NULL;
401
402         *f = (struct upkg_file) {
403                 .pkg  = pkg,
404                 .base = pkg->priv->exports[idx].offset,
405                 .len  = pkg->priv->exports[idx].size,
406         };
407
408         return f;
409 }
410
411 void upkg_export_close(struct upkg_file *f)
412 {
413         if (f->pkg->priv->last_file == f)
414                 f->pkg->priv->last_file = NULL;
415         free(f);
416 }
417
418 size_t upkg_export_read(struct upkg_file *f, void *buf, size_t n)
419 {
420         size_t want = MIN(n, f->len - f->offset);
421         size_t rc;
422
423         if (want == 0) {
424                 return 0;
425         }
426
427         if (f != f->pkg->priv->last_file) {
428                 if (fseek(f->pkg->priv->f, f->base + f->offset, SEEK_SET))
429                         return 0;
430         }
431
432         rc = fread(buf, 1, want, f->pkg->priv->f);
433         f->offset += rc;
434         return rc;
435 }