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))
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.
34 #define format_assert(expr, body) do { \
36 fprintf(stderr, "%s: %d: %s: format assertion failed: %s\n", \
37 __FILE__, __LINE__, __func__, #expr); \
47 struct upkg_export_priv {
48 struct upkg_export pub;
51 unsigned long size, offset;
57 const struct upkg_file_ops *fops;
58 int (*dtor)(void *handle);
61 struct upkg_file *last_file;
63 struct upkg_name *names;
64 struct upkg_export_priv *exports;
65 struct upkg_import *imports;
67 unsigned long name_offset, export_offset, import_offset;
68 unsigned char guid[16];
71 /* Default I/O operations for ordinary files. */
72 static size_t file_read(void *buf, size_t size, void *handle)
74 return fread(buf, 1, size, (FILE *)handle);
77 static int file_seek(void *handle, long offset, int whence)
79 return fseek((FILE *)handle, offset, whence);
82 static long file_tell(void *handle)
84 return ftell((FILE *)handle);
87 static int file_eof(void *handle)
89 return feof((FILE *)handle);
92 static int file_close(void *handle)
94 return fclose((FILE *)handle);
97 const struct upkg_file_ops upkg_default_fops = {
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).
109 size_t upkg_decode_index(long *val, const unsigned char *bytes, size_t n)
113 for (size_t i = 0; i < MIN(n, 5); i++) {
115 * Least significant bytes are first, so we need to do this
118 long tmp = bytes[i] & (i == 0 ? 0x3f : 0x7f);
120 if (i > 0) tmp <<= 6;
121 if (i > 1) tmp <<= 7*(i-1);
124 if (!(bytes[i] & (i == 0 ? 0x40 : 0x80))) {
135 static struct upkg_priv *init_upkg(unsigned char hdr[static UPKG_HDR_SIZE])
137 struct upkg_priv *pkg;
139 pkg = malloc(sizeof *pkg);
143 *pkg = (struct upkg_priv) {
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),
153 .name_offset = unpack_32_le(hdr+16),
154 .export_offset = unpack_32_le(hdr+24),
155 .import_offset = unpack_32_le(hdr+32),
161 static int pkg_init_guid(struct upkg_priv *pkg)
163 const struct upkg_file_ops *fops = pkg->fops;
166 if (pkg->pub.version < 68) {
167 unsigned long heritage_count, heritage_offset;
168 unsigned char buf[8];
170 rc = fops->read(buf, sizeof buf, pkg->f);
174 heritage_count = unpack_32_le(buf+0);
175 heritage_offset = unpack_32_le(buf+4);
177 if (heritage_count == 0)
179 if (fops->seek(pkg->f, heritage_offset, SEEK_SET) != 0)
183 rc = fops->read(pkg->pub.guid, 16, pkg->f);
190 static int pkg_init_names(struct upkg_priv *pkg)
192 const struct upkg_file_ops *fops = pkg->fops;
195 size_t rc, len, nbuf = 0;
196 unsigned long index = 0;
197 unsigned char buf[512];
199 if (fops->seek(f, pkg->name_offset, SEEK_SET) != 0)
202 pkg->names = malloc(pkg->pub.name_count * sizeof *pkg->names);
206 while (index < pkg->pub.name_count) {
207 struct upkg_name *name = &pkg->names[index];
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)
217 if (pkg->pub.version >= 64) {
219 if (nbuf <= len + 4 || buf[len])
221 name->name = malloc(len);
224 memcpy(name->name, buf+1, len);
225 name->flags = unpack_32_le(buf+len+1);
229 memmove(buf, buf+len+1, nbuf);
232 unsigned char *c = memchr(buf, 0, nbuf);
233 if (!c || nbuf <= c - buf + 5)
236 name->name = malloc(len);
239 memcpy(name->name, buf, len);
240 name->flags = unpack_32_le(buf+len);
244 memmove(buf, buf+len, nbuf);
251 for (unsigned i = 0; i < index; i++)
252 free(pkg->names[i].name);
257 static int pkg_init_exports(struct upkg_priv *pkg)
259 const struct upkg_file_ops *fops = pkg->fops;
262 size_t rc, len, nbuf = 0;
263 unsigned long index = 0;
264 unsigned char buf[512];
266 if (fops->seek(f, pkg->export_offset, SEEK_SET) != 0)
269 pkg->exports = malloc(pkg->pub.export_count * sizeof *pkg->exports);
273 while (index < pkg->pub.export_count) {
274 struct upkg_export_priv *export = &pkg->exports[index];
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)
286 rc = upkg_decode_index(&export->pub.class, buf+len, nbuf-len);
287 if (rc == 0) goto err;
290 rc = upkg_decode_index(&export->super, buf+len, nbuf-len);
291 if (rc == 0) goto err;
294 if (nbuf-len < 4) goto err;
295 tmp = unpack_s32_le(buf+len);
300 export->pub.parent = NULL;
303 if (tmp >= pkg->pub.export_count)
305 export->pub.parent = &pkg->exports[tmp].pub;
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;
313 if (nbuf-len < 4) goto err;
314 export->pub.flags = unpack_32_le(buf+len);
317 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
318 if (rc == 0 || tmp < 0) goto err;
323 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
324 if (rc == 0 || tmp < 0) goto err;
325 export->offset = tmp;
330 memmove(buf, buf+len, nbuf);
340 static int pkg_init_imports(struct upkg_priv *pkg)
342 const struct upkg_file_ops *fops = pkg->fops;
345 size_t rc, len, nbuf = 0;
346 unsigned long index = 0;
347 unsigned char buf[512];
349 if (fops->seek(f, pkg->import_offset, SEEK_SET) != 0)
352 pkg->imports = malloc(pkg->pub.import_count * sizeof *pkg->imports);
356 while (index < pkg->pub.import_count) {
357 struct upkg_import *import = &pkg->imports[index];
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)
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;
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;
379 if (nbuf-len < 4) goto err;
380 tmp = unpack_s32_le(buf+len);
385 import->parent = NULL;
388 if (tmp >= pkg->pub.import_count)
390 import->parent = &pkg->imports[tmp];
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;
399 memmove(buf, buf+len, nbuf);
409 struct upkg *upkg_open(void *f, const struct upkg_file_ops *fops,
410 int (*destructor)(void *handle))
412 unsigned char hdr_buf[UPKG_HDR_SIZE];
413 struct upkg_priv *pkg;
415 if (fops->read(hdr_buf, sizeof hdr_buf, f) != sizeof hdr_buf) {
418 if (unpack_32_le(hdr_buf) != UPKG_HDR_MAGIC) {
422 /* Initialize package structure. */
423 pkg = init_upkg(hdr_buf);
428 pkg->dtor = destructor;
431 if (pkg_init_guid(pkg) != 0) {
435 if (pkg_init_names(pkg) != 0) {
439 if (pkg_init_exports(pkg) != 0) {
443 if (pkg_init_imports(pkg) != 0) {
451 for (unsigned i = 0; i < pkg->pub.name_count; i++)
452 free(pkg->names[i].name);
459 struct upkg *upkg_fopen(const char *path)
464 f = fopen(path, "rb");
469 pkg = upkg_open(f, &upkg_default_fops, file_close);
477 int upkg_close(struct upkg *pub)
479 struct upkg_priv *pkg = (struct upkg_priv *)pub;
483 rc = pkg->dtor(pkg->f);
486 for (unsigned i = 0; i < pkg->pub.name_count; i++) {
487 free(pkg->names[i].name);
498 const char *upkg_get_name(struct upkg *pub, unsigned long idx)
500 struct upkg_priv *pkg = (struct upkg_priv *)pub;
502 if (idx >= pkg->pub.name_count)
504 return pkg->names[idx].name;
507 long upkg_export_find(struct upkg *pub, long parent_index, const char *name)
509 struct upkg_priv *pkg = (struct upkg_priv *)pub;
510 struct upkg_export *parent = NULL;
512 if (parent_index >= 0) {
513 if (parent_index >= pkg->pub.export_count)
515 parent = &pkg->exports[parent_index].pub;
518 for (unsigned long i = 0; i < pkg->pub.export_count; i++) {
519 struct upkg_export_priv *e = &pkg->exports[i];
521 if (e->pub.parent == parent && !strcmp(e->pub.name, name))
528 const struct upkg_export *upkg_get_export(struct upkg *pub, unsigned long idx)
530 struct upkg_priv *pkg = (struct upkg_priv *)pub;
532 if (idx < pkg->pub.export_count)
533 return &pkg->exports[idx].pub;
537 const struct upkg_import *upkg_get_import(struct upkg *pub, unsigned long idx)
539 struct upkg_priv *pkg = (struct upkg_priv *)pub;
541 if (idx < pkg->pub.import_count)
542 return &pkg->imports[idx];
546 struct upkg_file *upkg_export_open(struct upkg *pub, unsigned long idx)
548 struct upkg_priv *pkg = (struct upkg_priv *)pub;
551 if (idx >= pkg->pub.export_count)
554 f = malloc(sizeof *f);
558 *f = (struct upkg_file) {
560 .base = pkg->exports[idx].offset,
561 .len = pkg->exports[idx].size,
562 .name = pkg->exports[idx].pub.name,
568 void upkg_export_close(struct upkg_file *f)
570 struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
572 if (pkg->last_file == f)
573 pkg->last_file = NULL;
577 long upkg_export_tell(struct upkg_file *f)
582 int upkg_export_seek(struct upkg_file *f, long offset, int whence)
584 struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
585 const struct upkg_file_ops *fops = pkg->fops;
590 offset = f->offset + offset;
592 if (offset < 0 || offset > f->len)
594 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
598 if (offset < 0 || offset > f->len)
600 offset = f->len - offset;
601 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
609 } else if (pkg->last_file == f) {
610 pkg->last_file = NULL;
616 size_t upkg_export_read(struct upkg_file *f, void *buf, size_t n)
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);
627 if (f != pkg->last_file) {
628 if (fops->seek(pkg->f, f->base + f->offset, SEEK_SET))
632 rc = fops->read(buf, want, pkg->f);
635 if (want < n || (rc < want && fops->eof(pkg->f)))