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