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