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