]> git.draconx.ca Git - upkg.git/blob - src/libupkg.c
Disable deprecation warnings from Glib.
[upkg.git] / src / libupkg.c
1 /*
2  * upkg: tool for manipulating Unreal Tournament packages.
3  * Copyright © 2009-2012, 2022 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 <https://www.gnu.org/licenses/>.
17  */
18
19 #include <config.h>
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 /*
30  * Print a message and execute some statement(s) if the expression evaluates
31  * to zero.  Intended to help verify that assumed constraints on the file
32  * format actually are not violated.
33  */
34 #define format_assert(expr, body) do { \
35         if (!(expr)) { \
36                 fprintf(stderr, "%s: %d: %s: format assertion failed: %s\n", \
37                                 __FILE__, __LINE__, __func__, #expr); \
38                 body; \
39         } \
40 } while (0)
41
42 struct upkg_name {
43         unsigned long flags;
44         char *name;
45 };
46
47 struct upkg_export_priv {
48         struct upkg_export pub;
49
50         long super;
51         unsigned long size, offset;
52 };
53
54 struct upkg_priv {
55         struct upkg pub;
56
57         const struct upkg_file_ops *fops;
58         int (*dtor)(void *handle);
59         void *f;
60
61         struct upkg_file *last_file;
62
63         struct upkg_name        *names;
64         struct upkg_export_priv *exports;
65         struct upkg_import      *imports;
66
67         unsigned long name_offset, export_offset, import_offset;
68         unsigned char guid[16];
69 };
70
71 /* Default I/O operations for ordinary files. */
72 static size_t file_read(void *buf, size_t size, void *handle)
73 {
74         return fread(buf, 1, size, (FILE *)handle);
75 }
76
77 static int file_seek(void *handle, long offset, int whence)
78 {
79         return fseek((FILE *)handle, offset, whence);
80 }
81
82 static long file_tell(void *handle)
83 {
84         return ftell((FILE *)handle);
85 }
86
87 static int file_eof(void *handle)
88 {
89         return feof((FILE *)handle);
90 }
91
92 static int file_close(void *handle)
93 {
94         return fclose((FILE *)handle);
95 }
96
97 const struct upkg_file_ops upkg_default_fops = {
98         .read = file_read,
99         .seek = file_seek,
100         .tell = file_tell,
101         .eof  = file_eof,
102 };
103
104 /*
105  * Decode the compact index format from the upkg.  This format is fucked.
106  * Stores the result in *val and returns the number of input bytes read (or 0
107  * if the input is invalid, in which case *val is undefined).
108  */
109 size_t upkg_decode_index(long *val, const unsigned char *bytes, size_t n)
110 {
111         *val = 0;
112
113         for (size_t i = 0; i < MIN(n, 5); i++) {
114                 /*
115                  * Least significant bytes are first, so we need to do this
116                  * nonsense.
117                  */
118                 long tmp = bytes[i] & (i == 0 ? 0x3f : 0x7f);
119
120                 if (i > 0) tmp <<= 6;
121                 if (i > 1) tmp <<= 7*(i-1);
122                 *val += tmp;
123
124                 if (!(bytes[i] & (i == 0 ? 0x40 : 0x80))) {
125                         if (bytes[0] & 0x80)
126                                 *val = -*val;
127                         return i+1;
128                 }
129         }
130
131         /* Error */
132         return 0;
133 }
134
135 static struct upkg_priv *init_upkg(unsigned char hdr[static UPKG_HDR_SIZE])
136 {
137         struct upkg_priv *pkg;
138
139         pkg = malloc(sizeof *pkg);
140         if (!pkg)
141                 return NULL;
142
143         *pkg = (struct upkg_priv) {
144                 .pub = {
145                         .version      = unpack_16_le(hdr+4),
146                         .license      = unpack_16_le(hdr+6),
147                         .flags        = unpack_32_le(hdr+8),
148                         .name_count   = unpack_32_le(hdr+12),
149                         .export_count = unpack_32_le(hdr+20),
150                         .import_count = unpack_32_le(hdr+28),
151                 },
152
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 pkg;
159 }
160
161 static int pkg_init_guid(struct upkg_priv *pkg)
162 {
163         const struct upkg_file_ops *fops = pkg->fops;
164         size_t rc;
165
166         if (pkg->pub.version < 68) {
167                 unsigned long heritage_count, heritage_offset;
168                 unsigned char buf[8];
169
170                 rc = fops->read(buf, sizeof buf, pkg->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->f, heritage_offset, SEEK_SET) != 0)
180                         return -1;
181         }
182
183         rc = fops->read(pkg->pub.guid, 16, pkg->f);
184         if (rc < 16)
185                 return -1;
186
187         return 0;
188 }
189
190 static int pkg_init_names(struct upkg_priv *pkg)
191 {
192         const struct upkg_file_ops *fops = pkg->fops;
193         void *f = pkg->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->name_offset, SEEK_SET) != 0)
200                 return -1;
201
202         pkg->names = malloc(pkg->pub.name_count * sizeof *pkg->names);
203         if (!pkg->names)
204                 return -1;
205
206         while (index < pkg->pub.name_count) {
207                 struct upkg_name *name = &pkg->names[index];
208
209                 /* Read some data into buffer. */
210                 if (!fops->eof(pkg->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->pub.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->names[i].name);
253         free(pkg->names);
254         return -1;
255 }
256
257 static int pkg_init_exports(struct upkg_priv *pkg)
258 {
259         const struct upkg_file_ops *fops = pkg->fops;
260         void *f = pkg->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->export_offset, SEEK_SET) != 0)
267                 return -1;
268
269         pkg->exports = malloc(pkg->pub.export_count * sizeof *pkg->exports);
270         if (!pkg->exports)
271                 return -1;
272
273         while (index < pkg->pub.export_count) {
274                 struct upkg_export_priv *export = &pkg->exports[index];
275                 long tmp;
276
277                 /* Read some data into buffer. */
278                 if (!fops->eof(pkg->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->pub.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                 tmp = unpack_s32_le(buf+len);
296                 if (tmp < 0)
297                         goto err;
298                 len += 4;
299
300                 export->pub.parent = NULL;
301                 if (tmp > 0) {
302                         tmp--;
303                         if (tmp >= pkg->pub.export_count)
304                                 goto err;
305                         export->pub.parent = &pkg->exports[tmp].pub;
306                 }
307
308                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
309                 if (rc == 0 || tmp < 0 || tmp >= pkg->pub.name_count) goto err;
310                 export->pub.name = pkg->names[tmp].name;
311                 len += rc;
312
313                 if (nbuf-len < 4) goto err;
314                 export->pub.flags = unpack_32_le(buf+len);
315                 len += 4;
316
317                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
318                 if (rc == 0 || tmp < 0) goto err;
319                 export->size = tmp;
320                 len += rc;
321
322                 if (export->size) {
323                         rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
324                         if (rc == 0 || tmp < 0) goto err;
325                         export->offset = tmp;
326                         len += rc;
327                 }
328
329                 nbuf -= len;
330                 memmove(buf, buf+len, nbuf);
331                 index++;
332         }
333
334         return 0;
335 err:
336         free(pkg->exports);
337         return -1;
338 }
339
340 static int pkg_init_imports(struct upkg_priv *pkg)
341 {
342         const struct upkg_file_ops *fops = pkg->fops;
343         void *f = pkg->f;
344
345         size_t rc, len, nbuf = 0;
346         unsigned long index = 0;
347         unsigned char buf[512];
348
349         if (fops->seek(f, pkg->import_offset, SEEK_SET) != 0)
350                 return -1;
351
352         pkg->imports = malloc(pkg->pub.import_count * sizeof *pkg->imports);
353         if (!pkg->imports)
354                 return -1;
355
356         while (index < pkg->pub.import_count) {
357                 struct upkg_import *import = &pkg->imports[index];
358                 long tmp;
359
360                 /* Read some data into buffer. */
361                 if (!fops->eof(pkg->f)) {
362                         rc = fops->read(buf+nbuf, sizeof buf-nbuf, f);
363                         if (rc == 0 && nbuf == 0)
364                                 goto err;
365                         nbuf += rc;
366                 }
367
368                 len = 0;
369                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
370                 if (rc == 0 || len >= pkg->pub.name_count) goto err;
371                 import->class_package = pkg->names[tmp].name;
372                 len += rc;
373
374                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
375                 if (rc == 0 || len >= pkg->pub.name_count) goto err;
376                 import->class_name = pkg->names[tmp].name;
377                 len += rc;
378
379                 if (nbuf-len < 4) goto err;
380                 tmp = unpack_s32_le(buf+len);
381                 if (tmp > 0)
382                         goto err;
383                 len += 4;
384
385                 import->parent = NULL;
386                 if (tmp < 0) {
387                         tmp = -(tmp + 1);
388                         if (tmp >= pkg->pub.import_count)
389                                 goto err;
390                         import->parent = &pkg->imports[tmp];
391                 }
392
393                 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
394                 if (rc == 0 || len >= pkg->pub.name_count) goto err;
395                 import->name = pkg->names[tmp].name;
396                 len += rc;
397
398                 nbuf -= len;
399                 memmove(buf, buf+len, nbuf);
400                 index++;
401         }
402
403         return 0;
404 err:
405         free(pkg->imports);
406         return -1;
407 }
408
409 struct upkg *upkg_open(void *f, const struct upkg_file_ops *fops,
410                        int (*destructor)(void *handle))
411 {
412         unsigned char hdr_buf[UPKG_HDR_SIZE];
413         struct upkg_priv *pkg;
414
415         if (fops->read(hdr_buf, sizeof hdr_buf, f) != sizeof hdr_buf) {
416                 return NULL;
417         }
418         if (unpack_32_le(hdr_buf) != UPKG_HDR_MAGIC) {
419                 return NULL;
420         }
421
422         /* Initialize package structure. */
423         pkg = init_upkg(hdr_buf);
424         if (!pkg) {
425                 return NULL;
426         }
427         pkg->fops = fops;
428         pkg->dtor = destructor;
429         pkg->f    = f;
430
431         if (pkg_init_guid(pkg) != 0) {
432                 goto err1;
433         }
434
435         if (pkg_init_names(pkg) != 0) {
436                 goto err1;
437         }
438
439         if (pkg_init_exports(pkg) != 0) {
440                 goto err2;
441         }
442
443         if (pkg_init_imports(pkg) != 0) {
444                 goto err3;
445         }
446
447         return &pkg->pub;
448 err3:
449         free(pkg->exports);
450 err2:
451         for (unsigned i = 0; i < pkg->pub.name_count; i++)
452                 free(pkg->names[i].name);
453         free(pkg->names);
454 err1:
455         free(pkg);
456         return NULL;
457 }
458
459 struct upkg *upkg_fopen(const char *path)
460 {
461         struct upkg *pkg;
462         FILE *f;
463
464         f = fopen(path, "rb");
465         if (!f) {
466                 return NULL;
467         }
468
469         pkg = upkg_open(f, &upkg_default_fops, file_close);
470         if (!pkg) {
471                 fclose(f);
472         }
473
474         return pkg;
475 }
476
477 int upkg_close(struct upkg *pub)
478 {
479         struct upkg_priv *pkg = (struct upkg_priv *)pub;
480         int rc = 0;
481
482         if (pkg->dtor) {
483                 rc = pkg->dtor(pkg->f);
484         }
485
486         for (unsigned i = 0; i < pkg->pub.name_count; i++) {
487                 free(pkg->names[i].name);
488         }
489
490         free(pkg->imports);
491         free(pkg->exports);
492         free(pkg->names);
493         free(pkg);
494
495         return rc;
496 }
497
498 const char *upkg_get_name(struct upkg *pub, unsigned long idx)
499 {
500         struct upkg_priv *pkg = (struct upkg_priv *)pub;
501
502         if (idx >= pkg->pub.name_count)
503                 return 0;
504         return pkg->names[idx].name;
505 }
506
507 long upkg_export_find(struct upkg *pub, long parent_index, const char *name)
508 {
509         struct upkg_priv *pkg = (struct upkg_priv *)pub;
510         struct upkg_export *parent = NULL;
511
512         if (parent_index >= 0) {
513                 if (parent_index >= pkg->pub.export_count)
514                         return -1;
515                 parent = &pkg->exports[parent_index].pub;
516         }
517
518         for (unsigned long i = 0; i < pkg->pub.export_count; i++) {
519                 struct upkg_export_priv *e = &pkg->exports[i];
520
521                 if (e->pub.parent == parent && !strcmp(e->pub.name, name))
522                         return i;
523         }
524
525         return -1;
526 }
527
528 const struct upkg_export *upkg_get_export(struct upkg *pub, unsigned long idx)
529 {
530         struct upkg_priv *pkg = (struct upkg_priv *)pub;
531
532         if (idx < pkg->pub.export_count)
533                 return &pkg->exports[idx].pub;
534         return NULL;
535 }
536
537 const struct upkg_import *upkg_get_import(struct upkg *pub, unsigned long idx)
538 {
539         struct upkg_priv *pkg = (struct upkg_priv *)pub;
540
541         if (idx < pkg->pub.import_count)
542                 return &pkg->imports[idx];
543         return NULL;
544 }
545
546 struct upkg_file *upkg_export_open(struct upkg *pub, unsigned long idx)
547 {
548         struct upkg_priv *pkg = (struct upkg_priv *)pub;
549         struct upkg_file *f;
550
551         if (idx >= pkg->pub.export_count)
552                 return NULL;
553
554         f = malloc(sizeof *f);
555         if (f == NULL)
556                 return NULL;
557
558         *f = (struct upkg_file) {
559                 .pkg  = pub,
560                 .base = pkg->exports[idx].offset,
561                 .len  = pkg->exports[idx].size,
562                 .name = pkg->exports[idx].pub.name,
563         };
564
565         return f;
566 }
567
568 void upkg_export_close(struct upkg_file *f)
569 {
570         struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
571
572         if (pkg->last_file == f)
573                 pkg->last_file = NULL;
574         free(f);
575 }
576
577 long upkg_export_tell(struct upkg_file *f)
578 {
579         return f->offset;
580 }
581
582 int upkg_export_seek(struct upkg_file *f, long offset, int whence)
583 {
584         struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
585         const struct upkg_file_ops *fops = pkg->fops;
586         int rc = EOF;
587
588         switch (whence) {
589         case SEEK_CUR:
590                 offset = f->offset + offset;
591         case SEEK_SET:
592                 if (offset < 0 || offset > f->len)
593                         return EOF;
594                 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
595                 break;
596         case SEEK_END:
597                 offset = -offset;
598                 if (offset < 0 || offset > f->len)
599                         return EOF;
600                 offset = f->len - offset;
601                 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
602                 break;
603         }
604
605         if (rc == 0) {
606                 pkg->last_file = f;
607                 f->offset = offset;
608                 f->eof = 0;
609         } else if (pkg->last_file == f) {
610                 pkg->last_file = NULL;
611         }
612
613         return rc;
614 }
615
616 size_t upkg_export_read(struct upkg_file *f, void *buf, size_t n)
617 {
618         struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
619         const struct upkg_file_ops *fops = pkg->fops;
620         size_t want = MIN(n, f->len - f->offset);
621         size_t rc;
622
623         if (want == 0) {
624                 return 0;
625         }
626
627         if (f != pkg->last_file) {
628                 if (fops->seek(pkg->f, f->base + f->offset, SEEK_SET))
629                         return 0;
630         }
631
632         rc = fops->read(buf, want, pkg->f);
633         f->offset += rc;
634
635         if (want < n || (rc < want && fops->eof(pkg->f)))
636                 f->eof = 1;
637         return rc;
638 }