]> git.draconx.ca Git - upkg.git/blob - src/libupkg.c
libupkg: Don't store upkg_priv pointer in struct upkg_file.
[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_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, const unsigned char *bytes, size_t n)
114 {
115         *val = 0;
116
117         for (size_t i = 0; i < MIN(n, 5); i++) {
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                         if (bytes[0] & 0x80)
130                                 *val = -*val;
131                         return i+1;
132                 }
133         }
134
135         /* Error */
136         return 0;
137 }
138
139 static struct upkg_priv *init_upkg(unsigned char hdr[static UPKG_HDR_SIZE])
140 {
141         struct upkg_priv *pkg;
142
143         pkg = malloc(sizeof *pkg);
144         if (!pkg)
145                 return NULL;
146
147         *pkg = (struct upkg_priv) {
148                 .pub = {
149                         .version      = unpack_16_le(hdr+4),
150                         .license      = unpack_16_le(hdr+6),
151                         .flags        = unpack_32_le(hdr+8),
152                         .name_count   = unpack_32_le(hdr+12),
153                         .export_count = unpack_32_le(hdr+20),
154                         .import_count = unpack_32_le(hdr+28),
155                 },
156
157                 .name_offset   = unpack_32_le(hdr+16),
158                 .export_offset = unpack_32_le(hdr+24),
159                 .import_offset = unpack_32_le(hdr+32),
160         };
161
162         return pkg;
163 }
164
165 static int pkg_init_guid(struct upkg_priv *pkg)
166 {
167         const struct upkg_file_ops *fops = pkg->fops;
168         size_t rc;
169
170         if (pkg->pub.version < 68) {
171                 unsigned long heritage_count, heritage_offset;
172                 unsigned char buf[8];
173
174                 rc = fops->read(buf, sizeof buf, pkg->f);
175                 if (rc < 8)
176                         return -1;
177
178                 heritage_count  = unpack_32_le(buf+0);
179                 heritage_offset = unpack_32_le(buf+4);
180
181                 if (heritage_count == 0)
182                         return -1;
183                 if (fops->seek(pkg->f, heritage_offset, SEEK_SET) != 0)
184                         return -1;
185         }
186
187         rc = fops->read(pkg->pub.guid, 16, pkg->f);
188         if (rc < 16)
189                 return -1;
190
191         return 0;
192 }
193
194 static int pkg_init_names(struct upkg_priv *pkg)
195 {
196         const struct upkg_file_ops *fops = pkg->fops;
197         void *f = pkg->f;
198
199         size_t rc, len, nbuf = 0;
200         unsigned long index = 0;
201         unsigned char buf[512];
202
203         if (fops->seek(f, pkg->name_offset, SEEK_SET) != 0)
204                 return -1;
205
206         pkg->names = malloc(pkg->pub.name_count * sizeof *pkg->names);
207         if (!pkg->names)
208                 return -1;
209
210         while (index < pkg->pub.name_count) {
211                 struct upkg_name *name = &pkg->names[index];
212
213                 /* Read some data into buffer. */
214                 if (!fops->eof(pkg->f)) {
215                         rc = fops->read(buf+nbuf, sizeof buf-nbuf, f);
216                         if (rc == 0 && nbuf == 0)
217                                 goto err;
218                         nbuf += rc;
219                 }
220
221                 if (pkg->pub.version >= 64) {
222                         len = buf[0];
223                         if (nbuf <= len + 4 || buf[len])
224                                 goto err;
225                         name->name = malloc(len);
226                         if (!name->name)
227                                 goto err;
228                         memcpy(name->name, buf+1, len);
229                         name->flags = unpack_32_le(buf+len+1);
230                         len += 4;
231
232                         nbuf -= len + 1;
233                         memmove(buf, buf+len+1, nbuf);
234                         index++;
235                 } else {
236                         unsigned char *c = memchr(buf, 0, nbuf);
237                         if (!c || nbuf <= c - buf + 5)
238                                 goto err;
239                         len = c - buf + 1;
240                         name->name = malloc(len);
241                         if (!name->name)
242                                 goto err;
243                         memcpy(name->name, buf, len);
244                         name->flags = unpack_32_le(buf+len);
245                         len += 4;
246
247                         nbuf -= len;
248                         memmove(buf, buf+len, nbuf);
249                         index++;
250                 }
251         }
252
253         return 0;
254 err:
255         for (unsigned i = 0; i < index; i++)
256                 free(pkg->names[i].name);
257         free(pkg->names);
258         return -1;
259 }
260
261 static int pkg_init_exports(struct upkg_priv *pkg)
262 {
263         const struct upkg_file_ops *fops = pkg->fops;
264         void *f = pkg->f;
265
266         size_t rc, len, nbuf = 0;
267         unsigned long index = 0;
268         unsigned char buf[512];
269
270         if (fops->seek(f, pkg->export_offset, SEEK_SET) != 0)
271                 return -1;
272
273         pkg->exports = malloc(pkg->pub.export_count * sizeof *pkg->exports);
274         if (!pkg->exports)
275                 return -1;
276
277         while (index < pkg->pub.export_count) {
278                 struct upkg_export_priv *export = &pkg->exports[index];
279                 unsigned long parent_index;
280                 long tmp;
281
282                 /* Read some data into buffer. */
283                 if (!fops->eof(pkg->f)) {
284                         rc = fops->read(buf+nbuf, sizeof buf-nbuf, f);
285                         if (rc == 0 && nbuf == 0)
286                                 goto err;
287                         nbuf += rc;
288                 }
289
290                 len = 0;
291                 rc = upkg_decode_index(&export->class, buf+len, nbuf-len);
292                 if (rc == 0) goto err;
293                 len += rc;
294
295                 rc = upkg_decode_index(&export->super, buf+len, nbuf-len);
296                 if (rc == 0) goto err;
297                 len += rc;
298
299                 if (nbuf-len < 4) goto err;
300                 parent_index = unpack_32_le(buf+len);
301                 len += 4;
302
303                 export->pub.parent = NULL;
304                 if (parent_index > 0) {
305                         parent_index--;
306                         if (parent_index >= pkg->pub.export_count)
307                                 goto err;
308                         export->pub.parent = &pkg->exports[parent_index].pub;
309                 }
310
311                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
312                 if (rc == 0 || tmp < 0 || tmp >= pkg->pub.name_count) goto err;
313                 export->pub.name = pkg->names[tmp].name;
314                 len += rc;
315
316                 if (nbuf-len < 4) goto err;
317                 export->pub.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->exports);
340         return -1;
341 }
342
343 static int pkg_init_imports(struct upkg_priv *pkg)
344 {
345         const struct upkg_file_ops *fops = pkg->fops;
346         void *f = pkg->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->import_offset, SEEK_SET) != 0)
353                 return -1;
354
355         pkg->imports = malloc(pkg->pub.import_count * sizeof *pkg->imports);
356         if (!pkg->imports)
357                 return -1;
358
359         while (index < pkg->pub.import_count) {
360                 struct upkg_import *import = &pkg->imports[index];
361                 long tmp;
362
363                 /* Read some data into buffer. */
364                 if (!fops->eof(pkg->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->pub.name_count) goto err;
374                 import->class_package = pkg->names[tmp].name;
375                 len += rc;
376
377                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
378                 if (rc == 0 || len >= pkg->pub.name_count) goto err;
379                 import->class_name = pkg->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->pub.name_count) goto err;
388                 import->object_name = pkg->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->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_priv *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->fops = fops;
421         pkg->dtor = destructor;
422         pkg->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->pub;
441 err3:
442         free(pkg->exports);
443 err2:
444         for (unsigned i = 0; i < pkg->pub.name_count; i++)
445                 free(pkg->names[i].name);
446         free(pkg->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 *pub)
471 {
472         struct upkg_priv *pkg = (struct upkg_priv *)pub;
473         int rc = 0;
474
475         if (pkg->dtor) {
476                 rc = pkg->dtor(pkg->f);
477         }
478
479         for (unsigned i = 0; i < pkg->pub.name_count; i++) {
480                 free(pkg->names[i].name);
481         }
482
483         free(pkg->imports);
484         free(pkg->exports);
485         free(pkg->names);
486         free(pkg);
487
488         return rc;
489 }
490
491 const char *upkg_get_name(struct upkg *pub, unsigned long idx)
492 {
493         struct upkg_priv *pkg = (struct upkg_priv *)pub;
494
495         if (idx >= pkg->pub.name_count)
496                 return 0;
497         return pkg->names[idx].name;
498 }
499
500 long upkg_export_find(struct upkg *pub, long parent_index, const char *name)
501 {
502         struct upkg_priv *pkg = (struct upkg_priv *)pub;
503         struct upkg_export *parent = NULL;
504
505         if (parent_index >= 0) {
506                 if (parent_index >= pkg->pub.export_count)
507                         return -1;
508                 parent = &pkg->exports[parent_index].pub;
509         }
510
511         for (unsigned long i = 0; i < pkg->pub.export_count; i++) {
512                 struct upkg_export_priv *e = &pkg->exports[i];
513
514                 if (e->pub.parent == parent && !strcmp(e->pub.name, name))
515                         return i;
516         }
517
518         return -1;
519 }
520
521 const struct upkg_export *upkg_get_export(struct upkg *pub, unsigned long idx)
522 {
523         struct upkg_priv *pkg = (struct upkg_priv *)pub;
524
525         if (idx < pkg->pub.export_count)
526                 return &pkg->exports[idx].pub;
527         return NULL;
528 }
529
530 const char *upkg_export_class(struct upkg *pub, unsigned long idx,
531                               const char **package)
532 {
533         struct upkg_priv *pkg = (struct upkg_priv *)pub;
534         struct upkg_export_priv *export;
535         struct upkg_import *iclass, *ipackage;
536         unsigned long pkg_idx;
537
538         if (idx >= pkg->pub.export_count)
539                 return NULL;
540
541         export = &pkg->exports[idx];
542
543         /* Assumption: class references are always imports. */
544         format_assert(export->class <= 0, return NULL);
545
546         /* Get the class. */
547         if (export->class == 0) {
548                 if (package) *package = "Core";
549                 return "Class";
550         }
551
552         pkg_idx = -(export->class + 1);
553         if (pkg_idx >= pkg->pub.import_count)
554                 return NULL;
555         iclass = &pkg->imports[pkg_idx];
556
557         /* Assumption: class references are always Core.Class. */
558         format_assert(!strcmp(iclass->class_package, "Core"), return NULL);
559         format_assert(!strcmp(iclass->class_name, "Class"), return NULL);
560
561         /* Assumption: package references are always imports. */
562         format_assert(iclass->package <= 0, return NULL);
563
564         /* Get the package. */
565         pkg_idx = -(iclass->package + 1);
566         if (pkg_idx >= pkg->pub.import_count)
567                 return NULL;
568         ipackage = &pkg->imports[pkg_idx];
569
570         /* Assumption: package references are always Core.Package. */
571         format_assert(!strcmp(ipackage->class_package, "Core"), return NULL);
572         format_assert(!strcmp(ipackage->class_name, "Package"), return NULL);
573
574         if (package) *package = ipackage->object_name;
575         return iclass->object_name;
576 }
577
578 struct upkg_file *upkg_export_open(struct upkg *pub, unsigned long idx)
579 {
580         struct upkg_priv *pkg = (struct upkg_priv *)pub;
581         struct upkg_file *f;
582
583         if (idx >= pkg->pub.export_count)
584                 return NULL;
585
586         f = malloc(sizeof *f);
587         if (f == NULL)
588                 return NULL;
589
590         *f = (struct upkg_file) {
591                 .pkg  = pub,
592                 .base = pkg->exports[idx].offset,
593                 .len  = pkg->exports[idx].size,
594                 .name = pkg->exports[idx].pub.name,
595         };
596
597         return f;
598 }
599
600 void upkg_export_close(struct upkg_file *f)
601 {
602         struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
603
604         if (pkg->last_file == f)
605                 pkg->last_file = NULL;
606         free(f);
607 }
608
609 long upkg_export_tell(struct upkg_file *f)
610 {
611         return f->offset;
612 }
613
614 int upkg_export_seek(struct upkg_file *f, long offset, int whence)
615 {
616         struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
617         const struct upkg_file_ops *fops = pkg->fops;
618         int rc = EOF;
619
620         switch (whence) {
621         case SEEK_CUR:
622                 offset = f->offset + offset;
623         case SEEK_SET:
624                 if (offset < 0 || offset > f->len)
625                         return EOF;
626                 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
627                 break;
628         case SEEK_END:
629                 offset = -offset;
630                 if (offset < 0 || offset > f->len)
631                         return EOF;
632                 offset = f->len - offset;
633                 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
634                 break;
635         }
636
637         if (rc == 0) {
638                 pkg->last_file = f;
639                 f->offset = offset;
640                 f->eof = 0;
641         } else if (pkg->last_file == f) {
642                 pkg->last_file = NULL;
643         }
644
645         return rc;
646 }
647
648 size_t upkg_export_read(struct upkg_file *f, void *buf, size_t n)
649 {
650         struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
651         const struct upkg_file_ops *fops = pkg->fops;
652         size_t want = MIN(n, f->len - f->offset);
653         size_t rc;
654
655         if (want == 0) {
656                 return 0;
657         }
658
659         if (f != pkg->last_file) {
660                 if (fops->seek(pkg->f, f->base + f->offset, SEEK_SET))
661                         return 0;
662         }
663
664         rc = fops->read(buf, want, pkg->f);
665         f->offset += rc;
666
667         if (want < n || (rc < want && fops->eof(pkg->f)))
668                 f->eof = 1;
669         return rc;
670 }