2 * upkg: tool for manipulating Unreal Tournament packages.
3 * Copyright © 2009-2012, 2022 Nick Bowler
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.
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.
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/>.
27 #define MIN(a, b) ((a) < (b) ? (a) : (b))
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
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.
44 #define format_assert(expr, body) do { \
46 fprintf(stderr, "%s: %d: %s: format assertion failed: %s\n", \
47 __FILE__, __LINE__, __func__, #expr); \
57 struct upkg_export_priv {
58 struct upkg_export pub;
61 unsigned long size, offset;
67 const struct upkg_file_ops *fops;
68 int (*dtor)(void *handle);
71 struct upkg_file *last_file;
73 struct upkg_name *names;
74 struct upkg_export_priv *exports;
75 struct upkg_import *imports;
77 unsigned long name_offset, export_offset, import_offset;
78 unsigned char guid[16];
81 /* Default I/O operations for ordinary files. */
82 static size_t file_read(void *buf, size_t size, void *handle)
84 return fread(buf, 1, size, (FILE *)handle);
87 static int file_seek(void *handle, long offset, int whence)
89 return fseek((FILE *)handle, offset, whence);
92 static long file_tell(void *handle)
94 return ftell((FILE *)handle);
97 static int file_eof(void *handle)
99 return feof((FILE *)handle);
102 static int file_close(void *handle)
104 return fclose((FILE *)handle);
107 const struct upkg_file_ops upkg_default_fops = {
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).
119 size_t upkg_decode_index(long *val, const unsigned char *bytes, size_t n)
123 for (size_t i = 0; i < MIN(n, 5); i++) {
125 * Least significant bytes are first, so we need to do this
128 long tmp = bytes[i] & (i == 0 ? 0x3f : 0x7f);
130 if (i > 0) tmp <<= 6;
131 if (i > 1) tmp <<= 7*(i-1);
134 if (!(bytes[i] & (i == 0 ? 0x40 : 0x80))) {
145 static struct upkg_priv *init_upkg(const unsigned char *hdr)
147 struct upkg_priv *pkg, tmp = {0};
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);
159 pkg = malloc(sizeof *pkg);
166 static int pkg_init_guid(struct upkg_priv *pkg)
168 const struct upkg_file_ops *fops = pkg->fops;
171 if (pkg->pub.version < 68) {
172 unsigned long heritage_count, heritage_offset;
173 unsigned char buf[8];
175 rc = fops->read(buf, sizeof buf, pkg->f);
179 heritage_count = unpack_32_le(buf+0);
180 heritage_offset = unpack_32_le(buf+4);
182 if (heritage_count == 0)
184 if (fops->seek(pkg->f, heritage_offset, SEEK_SET) != 0)
188 rc = fops->read(pkg->pub.guid, 16, pkg->f);
195 static int pkg_init_names(struct upkg_priv *pkg)
197 const struct upkg_file_ops *fops = pkg->fops;
200 size_t rc, len, nbuf = 0;
201 unsigned long index = 0;
202 unsigned char buf[512];
204 if (fops->seek(f, pkg->name_offset, SEEK_SET) != 0)
207 pkg->names = malloc(pkg->pub.name_count * sizeof *pkg->names);
211 while (index < pkg->pub.name_count) {
212 struct upkg_name *name = &pkg->names[index];
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)
222 if (pkg->pub.version >= 64) {
224 if (nbuf <= len + 4 || buf[len])
226 name->name = malloc(len);
229 memcpy(name->name, buf+1, len);
230 name->flags = unpack_32_le(buf+len+1);
234 memmove(buf, buf+len+1, nbuf);
237 unsigned char *c = memchr(buf, 0, nbuf);
238 if (!c || nbuf <= c - buf + 5)
241 name->name = malloc(len);
244 memcpy(name->name, buf, len);
245 name->flags = unpack_32_le(buf+len);
249 memmove(buf, buf+len, nbuf);
256 for (unsigned i = 0; i < index; i++)
257 free(pkg->names[i].name);
262 static int pkg_init_exports(struct upkg_priv *pkg)
264 const struct upkg_file_ops *fops = pkg->fops;
267 size_t rc, len, nbuf = 0;
268 unsigned long index = 0;
269 unsigned char buf[512];
271 if (fops->seek(f, pkg->export_offset, SEEK_SET) != 0)
274 pkg->exports = malloc(pkg->pub.export_count * sizeof *pkg->exports);
278 while (index < pkg->pub.export_count) {
279 struct upkg_export_priv *export = &pkg->exports[index];
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)
291 rc = upkg_decode_index(&export->pub.class, buf+len, nbuf-len);
292 if (rc == 0) goto err;
295 rc = upkg_decode_index(&export->super, buf+len, nbuf-len);
296 if (rc == 0) goto err;
299 if (nbuf-len < 4) goto err;
300 tmp = unpack_s32_le(buf+len);
305 export->pub.parent = NULL;
308 if (tmp >= pkg->pub.export_count)
310 export->pub.parent = &pkg->exports[tmp].pub;
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;
318 if (nbuf-len < 4) goto err;
319 export->pub.flags = unpack_32_le(buf+len);
322 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
323 if (rc == 0 || tmp < 0) goto err;
328 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
329 if (rc == 0 || tmp < 0) goto err;
330 export->offset = tmp;
335 memmove(buf, buf+len, nbuf);
345 static int pkg_init_imports(struct upkg_priv *pkg)
347 const struct upkg_file_ops *fops = pkg->fops;
350 size_t rc, len, nbuf = 0;
351 unsigned long index = 0;
352 unsigned char buf[512];
354 if (fops->seek(f, pkg->import_offset, SEEK_SET) != 0)
357 pkg->imports = malloc(pkg->pub.import_count * sizeof *pkg->imports);
361 while (index < pkg->pub.import_count) {
362 struct upkg_import *import = &pkg->imports[index];
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)
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;
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;
384 if (nbuf-len < 4) goto err;
385 tmp = unpack_s32_le(buf+len);
390 import->parent = NULL;
393 if (tmp >= pkg->pub.import_count)
395 import->parent = &pkg->imports[tmp];
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;
404 memmove(buf, buf+len, nbuf);
414 struct upkg *upkg_open(void *f, const struct upkg_file_ops *fops,
415 int (*destructor)(void *handle))
417 unsigned char hdr_buf[UPKG_HDR_SIZE];
418 struct upkg_priv *pkg;
420 if (fops->read(hdr_buf, sizeof hdr_buf, f) != sizeof hdr_buf) {
423 if (unpack_32_le(hdr_buf) != UPKG_HDR_MAGIC) {
427 /* Initialize package structure. */
428 pkg = init_upkg(hdr_buf);
433 pkg->dtor = destructor;
436 if (pkg_init_guid(pkg) != 0) {
440 if (pkg_init_names(pkg) != 0) {
444 if (pkg_init_exports(pkg) != 0) {
448 if (pkg_init_imports(pkg) != 0) {
456 for (unsigned i = 0; i < pkg->pub.name_count; i++)
457 free(pkg->names[i].name);
464 struct upkg *upkg_fopen(const char *path)
469 f = fopen(path, "rb");
474 pkg = upkg_open(f, &upkg_default_fops, file_close);
482 int upkg_close(struct upkg *pub)
484 struct upkg_priv *pkg = (struct upkg_priv *)pub;
488 rc = pkg->dtor(pkg->f);
491 for (unsigned i = 0; i < pkg->pub.name_count; i++) {
492 free(pkg->names[i].name);
503 const char *upkg_get_name(struct upkg *pub, unsigned long idx)
505 struct upkg_priv *pkg = (struct upkg_priv *)pub;
507 if (idx >= pkg->pub.name_count)
509 return pkg->names[idx].name;
512 long upkg_export_find(struct upkg *pub, long parent_index, const char *name)
514 struct upkg_priv *pkg = (struct upkg_priv *)pub;
515 struct upkg_export *parent = NULL;
517 if (parent_index >= 0) {
518 if (parent_index >= pkg->pub.export_count)
520 parent = &pkg->exports[parent_index].pub;
523 for (unsigned long i = 0; i < pkg->pub.export_count; i++) {
524 struct upkg_export_priv *e = &pkg->exports[i];
526 if (e->pub.parent == parent && !strcmp(e->pub.name, name))
533 const struct upkg_export *upkg_get_export(struct upkg *pub, unsigned long idx)
535 struct upkg_priv *pkg = (struct upkg_priv *)pub;
537 if (idx < pkg->pub.export_count)
538 return &pkg->exports[idx].pub;
542 const struct upkg_import *upkg_get_import(struct upkg *pub, unsigned long idx)
544 struct upkg_priv *pkg = (struct upkg_priv *)pub;
546 if (idx < pkg->pub.import_count)
547 return &pkg->imports[idx];
551 struct upkg_file *upkg_export_open(struct upkg *pub, unsigned long idx)
553 struct upkg_priv *pkg = (struct upkg_priv *)pub;
556 if (idx >= pkg->pub.export_count)
559 f = malloc(sizeof *f);
563 *f = (struct upkg_file) {
565 .base = pkg->exports[idx].offset,
566 .len = pkg->exports[idx].size,
567 .name = pkg->exports[idx].pub.name,
573 void upkg_export_close(struct upkg_file *f)
575 struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
577 if (pkg->last_file == f)
578 pkg->last_file = NULL;
582 long upkg_export_tell(struct upkg_file *f)
587 int upkg_export_seek(struct upkg_file *f, long offset, int whence)
589 struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
590 const struct upkg_file_ops *fops = pkg->fops;
595 offset = f->offset + offset;
597 if (offset < 0 || offset > f->len)
599 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
603 if (offset < 0 || offset > f->len)
605 offset = f->len - offset;
606 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
614 } else if (pkg->last_file == f) {
615 pkg->last_file = NULL;
621 size_t upkg_export_read(struct upkg_file *f, void *buf, size_t n)
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);
632 if (f != pkg->last_file) {
633 if (fops->seek(pkg->f, f->base + f->offset, SEEK_SET))
637 rc = fops->read(buf, want, pkg->f);
640 if (want < n || (rc < want && fops->eof(pkg->f)))