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