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