]> git.draconx.ca Git - upkg.git/blob - src/libupkg.c
upkg: Add support for listing package imports.
[upkg.git] / src / libupkg.c
1 /*
2  *  upkg: tool for manipulating Unreal Tournament packages.
3  *  Copyright © 2009-2011 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 3 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, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "upkg.h"
24 #include "pack.h"
25
26 #define MIN(a, b) ((a) < (b) ? (a) : (b))
27
28 /*
29  * Print a message and execute some statement(s) if the expression evaluates
30  * to zero.  Intended to help verify that assumed constraints on the file
31  * format actually are not violated.
32  */
33 #define format_assert(expr, body) do { \
34         if (!(expr)) { \
35                 fprintf(stderr, "%s: %d: %s: format assertion failed: %s\n", \
36                                 __FILE__, __LINE__, __func__, #expr); \
37                 body; \
38         } \
39 } while (0)
40
41 struct upkg_name {
42         unsigned long flags;
43         char *name;
44 };
45
46 struct upkg_export_priv {
47         struct upkg_export pub;
48
49         long class, super;
50         unsigned long size, offset;
51 };
52
53 struct upkg_priv {
54         struct upkg pub;
55
56         const struct upkg_file_ops *fops;
57         int (*dtor)(void *handle);
58         void *f;
59
60         struct upkg_file *last_file;
61
62         struct upkg_name        *names;
63         struct upkg_export_priv *exports;
64         struct upkg_import      *imports;
65
66         unsigned long name_offset, export_offset, import_offset;
67         unsigned char guid[16];
68 };
69
70 /* Default I/O operations for ordinary files. */
71 static size_t file_read(void *buf, size_t size, void *handle)
72 {
73         return fread(buf, 1, size, (FILE *)handle);
74 }
75
76 static int file_seek(void *handle, long offset, int whence)
77 {
78         return fseek((FILE *)handle, offset, whence);
79 }
80
81 static long file_tell(void *handle)
82 {
83         return ftell((FILE *)handle);
84 }
85
86 static int file_eof(void *handle)
87 {
88         return feof((FILE *)handle);
89 }
90
91 static int file_close(void *handle)
92 {
93         return fclose((FILE *)handle);
94 }
95
96 const struct upkg_file_ops upkg_default_fops = {
97         .read = file_read,
98         .seek = file_seek,
99         .tell = file_tell,
100         .eof  = file_eof,
101 };
102
103 /*
104  * Decode the compact index format from the upkg.  This format is fucked.
105  * Stores the result in *val and returns the number of input bytes read (or 0
106  * if the input is invalid, in which case *val is undefined).
107  */
108 size_t upkg_decode_index(long *val, const unsigned char *bytes, size_t n)
109 {
110         *val = 0;
111
112         for (size_t i = 0; i < MIN(n, 5); i++) {
113                 /*
114                  * Least significant bytes are first, so we need to do this
115                  * nonsense.
116                  */
117                 long tmp = bytes[i] & (i == 0 ? 0x3f : 0x7f);
118
119                 if (i > 0) tmp <<= 6;
120                 if (i > 1) tmp <<= 7*(i-1);
121                 *val += tmp;
122
123                 if (!(bytes[i] & (i == 0 ? 0x40 : 0x80))) {
124                         if (bytes[0] & 0x80)
125                                 *val = -*val;
126                         return i+1;
127                 }
128         }
129
130         /* Error */
131         return 0;
132 }
133
134 static struct upkg_priv *init_upkg(unsigned char hdr[static UPKG_HDR_SIZE])
135 {
136         struct upkg_priv *pkg;
137
138         pkg = malloc(sizeof *pkg);
139         if (!pkg)
140                 return NULL;
141
142         *pkg = (struct upkg_priv) {
143                 .pub = {
144                         .version      = unpack_16_le(hdr+4),
145                         .license      = unpack_16_le(hdr+6),
146                         .flags        = unpack_32_le(hdr+8),
147                         .name_count   = unpack_32_le(hdr+12),
148                         .export_count = unpack_32_le(hdr+20),
149                         .import_count = unpack_32_le(hdr+28),
150                 },
151
152                 .name_offset   = unpack_32_le(hdr+16),
153                 .export_offset = unpack_32_le(hdr+24),
154                 .import_offset = unpack_32_le(hdr+32),
155         };
156
157         return pkg;
158 }
159
160 static int pkg_init_guid(struct upkg_priv *pkg)
161 {
162         const struct upkg_file_ops *fops = pkg->fops;
163         size_t rc;
164
165         if (pkg->pub.version < 68) {
166                 unsigned long heritage_count, heritage_offset;
167                 unsigned char buf[8];
168
169                 rc = fops->read(buf, sizeof buf, pkg->f);
170                 if (rc < 8)
171                         return -1;
172
173                 heritage_count  = unpack_32_le(buf+0);
174                 heritage_offset = unpack_32_le(buf+4);
175
176                 if (heritage_count == 0)
177                         return -1;
178                 if (fops->seek(pkg->f, heritage_offset, SEEK_SET) != 0)
179                         return -1;
180         }
181
182         rc = fops->read(pkg->pub.guid, 16, pkg->f);
183         if (rc < 16)
184                 return -1;
185
186         return 0;
187 }
188
189 static int pkg_init_names(struct upkg_priv *pkg)
190 {
191         const struct upkg_file_ops *fops = pkg->fops;
192         void *f = pkg->f;
193
194         size_t rc, len, nbuf = 0;
195         unsigned long index = 0;
196         unsigned char buf[512];
197
198         if (fops->seek(f, pkg->name_offset, SEEK_SET) != 0)
199                 return -1;
200
201         pkg->names = malloc(pkg->pub.name_count * sizeof *pkg->names);
202         if (!pkg->names)
203                 return -1;
204
205         while (index < pkg->pub.name_count) {
206                 struct upkg_name *name = &pkg->names[index];
207
208                 /* Read some data into buffer. */
209                 if (!fops->eof(pkg->f)) {
210                         rc = fops->read(buf+nbuf, sizeof buf-nbuf, f);
211                         if (rc == 0 && nbuf == 0)
212                                 goto err;
213                         nbuf += rc;
214                 }
215
216                 if (pkg->pub.version >= 64) {
217                         len = buf[0];
218                         if (nbuf <= len + 4 || buf[len])
219                                 goto err;
220                         name->name = malloc(len);
221                         if (!name->name)
222                                 goto err;
223                         memcpy(name->name, buf+1, len);
224                         name->flags = unpack_32_le(buf+len+1);
225                         len += 4;
226
227                         nbuf -= len + 1;
228                         memmove(buf, buf+len+1, nbuf);
229                         index++;
230                 } else {
231                         unsigned char *c = memchr(buf, 0, nbuf);
232                         if (!c || nbuf <= c - buf + 5)
233                                 goto err;
234                         len = c - buf + 1;
235                         name->name = malloc(len);
236                         if (!name->name)
237                                 goto err;
238                         memcpy(name->name, buf, len);
239                         name->flags = unpack_32_le(buf+len);
240                         len += 4;
241
242                         nbuf -= len;
243                         memmove(buf, buf+len, nbuf);
244                         index++;
245                 }
246         }
247
248         return 0;
249 err:
250         for (unsigned i = 0; i < index; i++)
251                 free(pkg->names[i].name);
252         free(pkg->names);
253         return -1;
254 }
255
256 static int pkg_init_exports(struct upkg_priv *pkg)
257 {
258         const struct upkg_file_ops *fops = pkg->fops;
259         void *f = pkg->f;
260
261         size_t rc, len, nbuf = 0;
262         unsigned long index = 0;
263         unsigned char buf[512];
264
265         if (fops->seek(f, pkg->export_offset, SEEK_SET) != 0)
266                 return -1;
267
268         pkg->exports = malloc(pkg->pub.export_count * sizeof *pkg->exports);
269         if (!pkg->exports)
270                 return -1;
271
272         while (index < pkg->pub.export_count) {
273                 struct upkg_export_priv *export = &pkg->exports[index];
274                 long tmp;
275
276                 /* Read some data into buffer. */
277                 if (!fops->eof(pkg->f)) {
278                         rc = fops->read(buf+nbuf, sizeof buf-nbuf, f);
279                         if (rc == 0 && nbuf == 0)
280                                 goto err;
281                         nbuf += rc;
282                 }
283
284                 len = 0;
285                 rc = upkg_decode_index(&export->class, buf+len, nbuf-len);
286                 if (rc == 0) goto err;
287                 len += rc;
288
289                 rc = upkg_decode_index(&export->super, buf+len, nbuf-len);
290                 if (rc == 0) goto err;
291                 len += rc;
292
293                 if (nbuf-len < 4) goto err;
294                 tmp = unpack_s32_le(buf+len);
295                 if (tmp < 0)
296                         goto err;
297                 len += 4;
298
299                 export->pub.parent = NULL;
300                 if (tmp > 0) {
301                         tmp--;
302                         if (tmp >= pkg->pub.export_count)
303                                 goto err;
304                         export->pub.parent = &pkg->exports[tmp].pub;
305                 }
306
307                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
308                 if (rc == 0 || tmp < 0 || tmp >= pkg->pub.name_count) goto err;
309                 export->pub.name = pkg->names[tmp].name;
310                 len += rc;
311
312                 if (nbuf-len < 4) goto err;
313                 export->pub.flags = unpack_32_le(buf+len);
314                 len += 4;
315
316                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
317                 if (rc == 0 || tmp < 0) goto err;
318                 export->size = tmp;
319                 len += rc;
320
321                 if (export->size) {
322                         rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
323                         if (rc == 0 || tmp < 0) goto err;
324                         export->offset = tmp;
325                         len += rc;
326                 }
327
328                 nbuf -= len;
329                 memmove(buf, buf+len, nbuf);
330                 index++;
331         }
332
333         return 0;
334 err:
335         free(pkg->exports);
336         return -1;
337 }
338
339 static int pkg_init_imports(struct upkg_priv *pkg)
340 {
341         const struct upkg_file_ops *fops = pkg->fops;
342         void *f = pkg->f;
343
344         size_t rc, len, nbuf = 0;
345         unsigned long index = 0;
346         unsigned char buf[512];
347
348         if (fops->seek(f, pkg->import_offset, SEEK_SET) != 0)
349                 return -1;
350
351         pkg->imports = malloc(pkg->pub.import_count * sizeof *pkg->imports);
352         if (!pkg->imports)
353                 return -1;
354
355         while (index < pkg->pub.import_count) {
356                 struct upkg_import *import = &pkg->imports[index];
357                 long tmp;
358
359                 /* Read some data into buffer. */
360                 if (!fops->eof(pkg->f)) {
361                         rc = fops->read(buf+nbuf, sizeof buf-nbuf, f);
362                         if (rc == 0 && nbuf == 0)
363                                 goto err;
364                         nbuf += rc;
365                 }
366
367                 len = 0;
368                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
369                 if (rc == 0 || len >= pkg->pub.name_count) goto err;
370                 import->class_package = pkg->names[tmp].name;
371                 len += rc;
372
373                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
374                 if (rc == 0 || len >= pkg->pub.name_count) goto err;
375                 import->class_name = pkg->names[tmp].name;
376                 len += rc;
377
378                 if (nbuf-len < 4) goto err;
379                 tmp = unpack_s32_le(buf+len);
380                 if (tmp > 0)
381                         goto err;
382                 len += 4;
383
384                 import->parent = NULL;
385                 if (tmp < 0) {
386                         tmp = -(tmp + 1);
387                         if (tmp >= pkg->pub.import_count)
388                                 goto err;
389                         import->parent = &pkg->imports[tmp];
390                 }
391
392                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
393                 if (rc == 0 || len >= pkg->pub.name_count) goto err;
394                 import->name = pkg->names[tmp].name;
395                 len += rc;
396
397                 nbuf -= len;
398                 memmove(buf, buf+len, nbuf);
399                 index++;
400         }
401
402         return 0;
403 err:
404         free(pkg->imports);
405         return -1;
406 }
407
408 struct upkg *upkg_open(void *f, const struct upkg_file_ops *fops,
409                        int (*destructor)(void *handle))
410 {
411         unsigned char hdr_buf[UPKG_HDR_SIZE];
412         struct upkg_priv *pkg;
413
414         if (fops->read(hdr_buf, sizeof hdr_buf, f) != sizeof hdr_buf) {
415                 return NULL;
416         }
417         if (unpack_32_le(hdr_buf) != UPKG_HDR_MAGIC) {
418                 return NULL;
419         }
420
421         /* Initialize package structure. */
422         pkg = init_upkg(hdr_buf);
423         if (!pkg) {
424                 return NULL;
425         }
426         pkg->fops = fops;
427         pkg->dtor = destructor;
428         pkg->f    = f;
429
430         if (pkg_init_guid(pkg) != 0) {
431                 goto err1;
432         }
433
434         if (pkg_init_names(pkg) != 0) {
435                 goto err1;
436         }
437
438         if (pkg_init_exports(pkg) != 0) {
439                 goto err2;
440         }
441
442         if (pkg_init_imports(pkg) != 0) {
443                 goto err3;
444         }
445
446         return &pkg->pub;
447 err3:
448         free(pkg->exports);
449 err2:
450         for (unsigned i = 0; i < pkg->pub.name_count; i++)
451                 free(pkg->names[i].name);
452         free(pkg->names);
453 err1:
454         free(pkg);
455         return NULL;
456 }
457
458 struct upkg *upkg_fopen(const char *path)
459 {
460         struct upkg *pkg;
461         FILE *f;
462
463         f = fopen(path, "rb");
464         if (!f) {
465                 return NULL;
466         }
467
468         pkg = upkg_open(f, &upkg_default_fops, file_close);
469         if (!pkg) {
470                 fclose(f);
471         }
472
473         return pkg;
474 }
475
476 int upkg_close(struct upkg *pub)
477 {
478         struct upkg_priv *pkg = (struct upkg_priv *)pub;
479         int rc = 0;
480
481         if (pkg->dtor) {
482                 rc = pkg->dtor(pkg->f);
483         }
484
485         for (unsigned i = 0; i < pkg->pub.name_count; i++) {
486                 free(pkg->names[i].name);
487         }
488
489         free(pkg->imports);
490         free(pkg->exports);
491         free(pkg->names);
492         free(pkg);
493
494         return rc;
495 }
496
497 const char *upkg_get_name(struct upkg *pub, unsigned long idx)
498 {
499         struct upkg_priv *pkg = (struct upkg_priv *)pub;
500
501         if (idx >= pkg->pub.name_count)
502                 return 0;
503         return pkg->names[idx].name;
504 }
505
506 long upkg_export_find(struct upkg *pub, long parent_index, const char *name)
507 {
508         struct upkg_priv *pkg = (struct upkg_priv *)pub;
509         struct upkg_export *parent = NULL;
510
511         if (parent_index >= 0) {
512                 if (parent_index >= pkg->pub.export_count)
513                         return -1;
514                 parent = &pkg->exports[parent_index].pub;
515         }
516
517         for (unsigned long i = 0; i < pkg->pub.export_count; i++) {
518                 struct upkg_export_priv *e = &pkg->exports[i];
519
520                 if (e->pub.parent == parent && !strcmp(e->pub.name, name))
521                         return i;
522         }
523
524         return -1;
525 }
526
527 const struct upkg_export *upkg_get_export(struct upkg *pub, unsigned long idx)
528 {
529         struct upkg_priv *pkg = (struct upkg_priv *)pub;
530
531         if (idx < pkg->pub.export_count)
532                 return &pkg->exports[idx].pub;
533         return NULL;
534 }
535
536 const struct upkg_import *upkg_get_import(struct upkg *pub, unsigned long idx)
537 {
538         struct upkg_priv *pkg = (struct upkg_priv *)pub;
539
540         if (idx < pkg->pub.import_count)
541                 return &pkg->imports[idx];
542         return NULL;
543 }
544
545 const char *upkg_export_class(struct upkg *pub, unsigned long idx,
546                               const char **package)
547 {
548         struct upkg_priv *pkg = (struct upkg_priv *)pub;
549         const struct upkg_import *iclass, *ipackage;
550         struct upkg_export_priv *export;
551         unsigned long pkg_idx;
552
553         if (idx >= pkg->pub.export_count)
554                 return NULL;
555
556         export = &pkg->exports[idx];
557
558         /* Assumption: class references are always imports. */
559         format_assert(export->class <= 0, return NULL);
560
561         /* Get the class. */
562         if (export->class == 0) {
563                 if (package) *package = "Core";
564                 return "Class";
565         }
566
567         pkg_idx = -(export->class + 1);
568         if (pkg_idx >= pkg->pub.import_count)
569                 return NULL;
570         iclass = &pkg->imports[pkg_idx];
571
572         /* Assumption: class references are always Core.Class. */
573         format_assert(!strcmp(iclass->class_package, "Core"), return NULL);
574         format_assert(!strcmp(iclass->class_name, "Class"), return NULL);
575
576         ipackage = iclass->parent;
577         if (!ipackage)
578                 return NULL;
579
580         /* Assumption: package references are always Core.Package. */
581         format_assert(!strcmp(ipackage->class_package, "Core"), return NULL);
582         format_assert(!strcmp(ipackage->class_name, "Package"), return NULL);
583
584         if (package) *package = ipackage->name;
585         return iclass->name;
586 }
587
588 struct upkg_file *upkg_export_open(struct upkg *pub, unsigned long idx)
589 {
590         struct upkg_priv *pkg = (struct upkg_priv *)pub;
591         struct upkg_file *f;
592
593         if (idx >= pkg->pub.export_count)
594                 return NULL;
595
596         f = malloc(sizeof *f);
597         if (f == NULL)
598                 return NULL;
599
600         *f = (struct upkg_file) {
601                 .pkg  = pub,
602                 .base = pkg->exports[idx].offset,
603                 .len  = pkg->exports[idx].size,
604                 .name = pkg->exports[idx].pub.name,
605         };
606
607         return f;
608 }
609
610 void upkg_export_close(struct upkg_file *f)
611 {
612         struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
613
614         if (pkg->last_file == f)
615                 pkg->last_file = NULL;
616         free(f);
617 }
618
619 long upkg_export_tell(struct upkg_file *f)
620 {
621         return f->offset;
622 }
623
624 int upkg_export_seek(struct upkg_file *f, long offset, int whence)
625 {
626         struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
627         const struct upkg_file_ops *fops = pkg->fops;
628         int rc = EOF;
629
630         switch (whence) {
631         case SEEK_CUR:
632                 offset = f->offset + offset;
633         case SEEK_SET:
634                 if (offset < 0 || offset > f->len)
635                         return EOF;
636                 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
637                 break;
638         case SEEK_END:
639                 offset = -offset;
640                 if (offset < 0 || offset > f->len)
641                         return EOF;
642                 offset = f->len - offset;
643                 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
644                 break;
645         }
646
647         if (rc == 0) {
648                 pkg->last_file = f;
649                 f->offset = offset;
650                 f->eof = 0;
651         } else if (pkg->last_file == f) {
652                 pkg->last_file = NULL;
653         }
654
655         return rc;
656 }
657
658 size_t upkg_export_read(struct upkg_file *f, void *buf, size_t n)
659 {
660         struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
661         const struct upkg_file_ops *fops = pkg->fops;
662         size_t want = MIN(n, f->len - f->offset);
663         size_t rc;
664
665         if (want == 0) {
666                 return 0;
667         }
668
669         if (f != pkg->last_file) {
670                 if (fops->seek(pkg->f, f->base + f->offset, SEEK_SET))
671                         return 0;
672         }
673
674         rc = fops->read(buf, want, pkg->f);
675         f->offset += rc;
676
677         if (want < n || (rc < want && fops->eof(pkg->f)))
678                 f->eof = 1;
679         return rc;
680 }