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