2 * upkg: tool for manipulating Unreal Tournament packages.
3 * Copyright © 2009-2011 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 <http://www.gnu.org/licenses/>.
26 #define MIN(a, b) ((a) < (b) ? (a) : (b))
29 * Print a message and execute some statement(s) if the expression evaluates
30 * to zero. Intended to help verify that assumed constraints on the file
31 * format actually are not violated.
33 #define format_assert(expr, body) do { \
35 fprintf(stderr, "%s: %d: %s: format assertion failed: %s\n", \
36 __FILE__, __LINE__, __func__, #expr); \
46 struct upkg_export_priv {
47 struct upkg_export pub;
50 unsigned long size, offset;
56 const struct upkg_file_ops *fops;
57 int (*dtor)(void *handle);
60 struct upkg_file *last_file;
62 struct upkg_name *names;
63 struct upkg_export_priv *exports;
64 struct upkg_import *imports;
66 unsigned long name_offset, export_offset, import_offset;
67 unsigned char guid[16];
70 /* Default I/O operations for ordinary files. */
71 static size_t file_read(void *buf, size_t size, void *handle)
73 return fread(buf, 1, size, (FILE *)handle);
76 static int file_seek(void *handle, long offset, int whence)
78 return fseek((FILE *)handle, offset, whence);
81 static long file_tell(void *handle)
83 return ftell((FILE *)handle);
86 static int file_eof(void *handle)
88 return feof((FILE *)handle);
91 static int file_close(void *handle)
93 return fclose((FILE *)handle);
96 const struct upkg_file_ops upkg_default_fops = {
104 * Decode the compact index format from the upkg. This format is fucked.
105 * Stores the result in *val and returns the number of input bytes read (or 0
106 * if the input is invalid, in which case *val is undefined).
108 size_t upkg_decode_index(long *val, const unsigned char *bytes, size_t n)
112 for (size_t i = 0; i < MIN(n, 5); i++) {
114 * Least significant bytes are first, so we need to do this
117 long tmp = bytes[i] & (i == 0 ? 0x3f : 0x7f);
119 if (i > 0) tmp <<= 6;
120 if (i > 1) tmp <<= 7*(i-1);
123 if (!(bytes[i] & (i == 0 ? 0x40 : 0x80))) {
134 static struct upkg_priv *init_upkg(unsigned char hdr[static UPKG_HDR_SIZE])
136 struct upkg_priv *pkg;
138 pkg = malloc(sizeof *pkg);
142 *pkg = (struct upkg_priv) {
144 .version = unpack_16_le(hdr+4),
145 .license = unpack_16_le(hdr+6),
146 .flags = unpack_32_le(hdr+8),
147 .name_count = unpack_32_le(hdr+12),
148 .export_count = unpack_32_le(hdr+20),
149 .import_count = unpack_32_le(hdr+28),
152 .name_offset = unpack_32_le(hdr+16),
153 .export_offset = unpack_32_le(hdr+24),
154 .import_offset = unpack_32_le(hdr+32),
160 static int pkg_init_guid(struct upkg_priv *pkg)
162 const struct upkg_file_ops *fops = pkg->fops;
165 if (pkg->pub.version < 68) {
166 unsigned long heritage_count, heritage_offset;
167 unsigned char buf[8];
169 rc = fops->read(buf, sizeof buf, pkg->f);
173 heritage_count = unpack_32_le(buf+0);
174 heritage_offset = unpack_32_le(buf+4);
176 if (heritage_count == 0)
178 if (fops->seek(pkg->f, heritage_offset, SEEK_SET) != 0)
182 rc = fops->read(pkg->pub.guid, 16, pkg->f);
189 static int pkg_init_names(struct upkg_priv *pkg)
191 const struct upkg_file_ops *fops = pkg->fops;
194 size_t rc, len, nbuf = 0;
195 unsigned long index = 0;
196 unsigned char buf[512];
198 if (fops->seek(f, pkg->name_offset, SEEK_SET) != 0)
201 pkg->names = malloc(pkg->pub.name_count * sizeof *pkg->names);
205 while (index < pkg->pub.name_count) {
206 struct upkg_name *name = &pkg->names[index];
208 /* Read some data into buffer. */
209 if (!fops->eof(pkg->f)) {
210 rc = fops->read(buf+nbuf, sizeof buf-nbuf, f);
211 if (rc == 0 && nbuf == 0)
216 if (pkg->pub.version >= 64) {
218 if (nbuf <= len + 4 || buf[len])
220 name->name = malloc(len);
223 memcpy(name->name, buf+1, len);
224 name->flags = unpack_32_le(buf+len+1);
228 memmove(buf, buf+len+1, nbuf);
231 unsigned char *c = memchr(buf, 0, nbuf);
232 if (!c || nbuf <= c - buf + 5)
235 name->name = malloc(len);
238 memcpy(name->name, buf, len);
239 name->flags = unpack_32_le(buf+len);
243 memmove(buf, buf+len, nbuf);
250 for (unsigned i = 0; i < index; i++)
251 free(pkg->names[i].name);
256 static int pkg_init_exports(struct upkg_priv *pkg)
258 const struct upkg_file_ops *fops = pkg->fops;
261 size_t rc, len, nbuf = 0;
262 unsigned long index = 0;
263 unsigned char buf[512];
265 if (fops->seek(f, pkg->export_offset, SEEK_SET) != 0)
268 pkg->exports = malloc(pkg->pub.export_count * sizeof *pkg->exports);
272 while (index < pkg->pub.export_count) {
273 struct upkg_export_priv *export = &pkg->exports[index];
276 /* Read some data into buffer. */
277 if (!fops->eof(pkg->f)) {
278 rc = fops->read(buf+nbuf, sizeof buf-nbuf, f);
279 if (rc == 0 && nbuf == 0)
285 rc = upkg_decode_index(&export->pub.class, buf+len, nbuf-len);
286 if (rc == 0) goto err;
289 rc = upkg_decode_index(&export->super, buf+len, nbuf-len);
290 if (rc == 0) goto err;
293 if (nbuf-len < 4) goto err;
294 tmp = unpack_s32_le(buf+len);
299 export->pub.parent = NULL;
302 if (tmp >= pkg->pub.export_count)
304 export->pub.parent = &pkg->exports[tmp].pub;
307 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
308 if (rc == 0 || tmp < 0 || tmp >= pkg->pub.name_count) goto err;
309 export->pub.name = pkg->names[tmp].name;
312 if (nbuf-len < 4) goto err;
313 export->pub.flags = unpack_32_le(buf+len);
316 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
317 if (rc == 0 || tmp < 0) goto err;
322 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
323 if (rc == 0 || tmp < 0) goto err;
324 export->offset = tmp;
329 memmove(buf, buf+len, nbuf);
339 static int pkg_init_imports(struct upkg_priv *pkg)
341 const struct upkg_file_ops *fops = pkg->fops;
344 size_t rc, len, nbuf = 0;
345 unsigned long index = 0;
346 unsigned char buf[512];
348 if (fops->seek(f, pkg->import_offset, SEEK_SET) != 0)
351 pkg->imports = malloc(pkg->pub.import_count * sizeof *pkg->imports);
355 while (index < pkg->pub.import_count) {
356 struct upkg_import *import = &pkg->imports[index];
359 /* Read some data into buffer. */
360 if (!fops->eof(pkg->f)) {
361 rc = fops->read(buf+nbuf, sizeof buf-nbuf, f);
362 if (rc == 0 && nbuf == 0)
368 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
369 if (rc == 0 || len >= pkg->pub.name_count) goto err;
370 import->class_package = pkg->names[tmp].name;
373 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
374 if (rc == 0 || len >= pkg->pub.name_count) goto err;
375 import->class_name = pkg->names[tmp].name;
378 if (nbuf-len < 4) goto err;
379 tmp = unpack_s32_le(buf+len);
384 import->parent = NULL;
387 if (tmp >= pkg->pub.import_count)
389 import->parent = &pkg->imports[tmp];
392 rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
393 if (rc == 0 || len >= pkg->pub.name_count) goto err;
394 import->name = pkg->names[tmp].name;
398 memmove(buf, buf+len, nbuf);
408 struct upkg *upkg_open(void *f, const struct upkg_file_ops *fops,
409 int (*destructor)(void *handle))
411 unsigned char hdr_buf[UPKG_HDR_SIZE];
412 struct upkg_priv *pkg;
414 if (fops->read(hdr_buf, sizeof hdr_buf, f) != sizeof hdr_buf) {
417 if (unpack_32_le(hdr_buf) != UPKG_HDR_MAGIC) {
421 /* Initialize package structure. */
422 pkg = init_upkg(hdr_buf);
427 pkg->dtor = destructor;
430 if (pkg_init_guid(pkg) != 0) {
434 if (pkg_init_names(pkg) != 0) {
438 if (pkg_init_exports(pkg) != 0) {
442 if (pkg_init_imports(pkg) != 0) {
450 for (unsigned i = 0; i < pkg->pub.name_count; i++)
451 free(pkg->names[i].name);
458 struct upkg *upkg_fopen(const char *path)
463 f = fopen(path, "rb");
468 pkg = upkg_open(f, &upkg_default_fops, file_close);
476 int upkg_close(struct upkg *pub)
478 struct upkg_priv *pkg = (struct upkg_priv *)pub;
482 rc = pkg->dtor(pkg->f);
485 for (unsigned i = 0; i < pkg->pub.name_count; i++) {
486 free(pkg->names[i].name);
497 const char *upkg_get_name(struct upkg *pub, unsigned long idx)
499 struct upkg_priv *pkg = (struct upkg_priv *)pub;
501 if (idx >= pkg->pub.name_count)
503 return pkg->names[idx].name;
506 long upkg_export_find(struct upkg *pub, long parent_index, const char *name)
508 struct upkg_priv *pkg = (struct upkg_priv *)pub;
509 struct upkg_export *parent = NULL;
511 if (parent_index >= 0) {
512 if (parent_index >= pkg->pub.export_count)
514 parent = &pkg->exports[parent_index].pub;
517 for (unsigned long i = 0; i < pkg->pub.export_count; i++) {
518 struct upkg_export_priv *e = &pkg->exports[i];
520 if (e->pub.parent == parent && !strcmp(e->pub.name, name))
527 const struct upkg_export *upkg_get_export(struct upkg *pub, unsigned long idx)
529 struct upkg_priv *pkg = (struct upkg_priv *)pub;
531 if (idx < pkg->pub.export_count)
532 return &pkg->exports[idx].pub;
536 const struct upkg_import *upkg_get_import(struct upkg *pub, unsigned long idx)
538 struct upkg_priv *pkg = (struct upkg_priv *)pub;
540 if (idx < pkg->pub.import_count)
541 return &pkg->imports[idx];
545 struct upkg_file *upkg_export_open(struct upkg *pub, unsigned long idx)
547 struct upkg_priv *pkg = (struct upkg_priv *)pub;
550 if (idx >= pkg->pub.export_count)
553 f = malloc(sizeof *f);
557 *f = (struct upkg_file) {
559 .base = pkg->exports[idx].offset,
560 .len = pkg->exports[idx].size,
561 .name = pkg->exports[idx].pub.name,
567 void upkg_export_close(struct upkg_file *f)
569 struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
571 if (pkg->last_file == f)
572 pkg->last_file = NULL;
576 long upkg_export_tell(struct upkg_file *f)
581 int upkg_export_seek(struct upkg_file *f, long offset, int whence)
583 struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
584 const struct upkg_file_ops *fops = pkg->fops;
589 offset = f->offset + offset;
591 if (offset < 0 || offset > f->len)
593 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
597 if (offset < 0 || offset > f->len)
599 offset = f->len - offset;
600 rc = fops->seek(pkg->f, f->base + offset, SEEK_SET);
608 } else if (pkg->last_file == f) {
609 pkg->last_file = NULL;
615 size_t upkg_export_read(struct upkg_file *f, void *buf, size_t n)
617 struct upkg_priv *pkg = (struct upkg_priv *)f->pkg;
618 const struct upkg_file_ops *fops = pkg->fops;
619 size_t want = MIN(n, f->len - f->offset);
626 if (f != pkg->last_file) {
627 if (fops->seek(pkg->f, f->base + f->offset, SEEK_SET))
631 rc = fops->read(buf, want, pkg->f);
634 if (want < n || (rc < want && fops->eof(pkg->f)))