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