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