+/*
+ * upkg: tool for manipulating Unreal Tournament packages.
+ * Copyright (C) 2009 Nick Bowler
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct upkg_private {
FILE *f;
+ struct upkg_file *last_file;
+
struct upkg_name *names;
struct upkg_export *exports;
struct upkg_import *imports;
* Stores the result in *val and returns the number of input bytes read (or 0
* if the input is invalid, in which case *val is undefined).
*/
-static size_t decode_index(long *val, unsigned char *bytes, size_t n)
+size_t upkg_decode_index(long *val, unsigned char *bytes, size_t n)
{
size_t i = 0;
static struct upkg *init_upkg(unsigned char hdr[static UPKG_HDR_SIZE])
{
- struct upkg *pkg;
+ struct {
+ struct upkg pkg;
+ struct upkg_private priv;
+ } *alloc;
- pkg = malloc(sizeof *pkg);
- if (!pkg) {
+ alloc = malloc(sizeof *alloc);
+ if (!alloc) {
return NULL;
}
- pkg->priv = malloc(sizeof *pkg->priv);
- if (!pkg->priv) {
- free(pkg);
- return NULL;
- }
+ alloc->pkg = (struct upkg) {
+ .version = unpack_16_le(hdr+4),
+ .license = unpack_16_le(hdr+6),
+ .flags = unpack_32_le(hdr+8),
+ .name_count = unpack_32_le(hdr+12),
+ .export_count = unpack_32_le(hdr+20),
+ .import_count = unpack_32_le(hdr+28),
+ .priv = &alloc->priv,
+ };
+
+ alloc->priv = (struct upkg_private) {
+ .name_offset = unpack_32_le(hdr+16),
+ .export_offset = unpack_32_le(hdr+24),
+ .import_offset = unpack_32_le(hdr+32),
+ };
+
+ return &alloc->pkg;
+}
+
+static int pkg_init_guid(struct upkg *pkg)
+{
+ size_t rc;
- pkg->version = unpack_16_le(hdr+4);
- pkg->license = unpack_16_le(hdr+6);
- pkg->flags = unpack_32_le(hdr+8);
- pkg->name_count = unpack_32_le(hdr+12);
- pkg->export_count = unpack_32_le(hdr+20);
- pkg->import_count = unpack_32_le(hdr+28);
+ if (pkg->version < 68) {
+ unsigned long heritage_count, heritage_offset;
+ unsigned char buf[8];
- pkg->priv->name_offset = unpack_32_le(hdr+16);
- pkg->priv->export_offset = unpack_32_le(hdr+24);
- pkg->priv->import_offset = unpack_32_le(hdr+32);
+ rc = fread(buf, 1, sizeof buf, pkg->priv->f);
+ if (rc < 8)
+ return -1;
- return pkg;
+ heritage_count = unpack_32_le(buf+0);
+ heritage_offset = unpack_32_le(buf+4);
+
+ if (heritage_count == 0)
+ return -1;
+ if (fseek(pkg->priv->f, heritage_offset, SEEK_SET) != 0)
+ return -1;
+ }
+
+ rc = fread(pkg->guid, 1, 16, pkg->priv->f);
+ if (rc < 16)
+ return -1;
+
+ return 0;
}
static int pkg_init_names(struct upkg *pkg)
}
len = 0;
- rc = decode_index(&export->class, buf+len, nbuf-len);
+ rc = upkg_decode_index(&export->class, buf+len, nbuf-len);
if (rc == 0) goto err;
len += rc;
- rc = decode_index(&export->super, buf+len, nbuf-len);
+ rc = upkg_decode_index(&export->super, buf+len, nbuf-len);
if (rc == 0) goto err;
len += rc;
export->package = unpack_32_le(buf+len);
len += 4;
- rc = decode_index(&tmp, buf+len, nbuf-len);
+ rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
if (rc == 0 || tmp < 0 || tmp >= pkg->name_count) goto err;
export->name = pkg->priv->names[tmp].name;
len += rc;
export->flags = unpack_32_le(buf+len);
len += 4;
- rc = decode_index(&export->size, buf+len, nbuf-len);
+ rc = upkg_decode_index(&export->size, buf+len, nbuf-len);
if (rc == 0) goto err;
len += rc;
if (export->size) {
- rc = decode_index(&export->offset, buf+len, nbuf-len);
+ rc = upkg_decode_index(&export->offset, buf+len, nbuf-len);
if (rc == 0) goto err;
len += rc;
}
}
len = 0;
- rc = decode_index(&tmp, buf+len, nbuf-len);
+ rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
import->class_package = pkg->priv->names[tmp].name;
len += rc;
- rc = decode_index(&tmp, buf+len, nbuf-len);
+ rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
import->class_name = pkg->priv->names[tmp].name;
len += rc;
import->package = unpack_32_le(buf+len);
len += 4;
- rc = decode_index(&tmp, buf+len, nbuf-len);
+ rc = upkg_decode_index(&tmp, buf+len, nbuf-len);
if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
import->object_name = pkg->priv->names[tmp].name;
len += rc;
}
pkg->priv->f = f;
+ if (pkg_init_guid(pkg) != 0) {
+ goto err2;
+ }
+
if (pkg_init_names(pkg) != 0) {
goto err2;
}
free(pkg->priv->names[i].name);
free(pkg->priv->names);
err2:
- free(pkg->priv);
free(pkg);
err1:
fclose(f);
free(pkg->priv->imports);
free(pkg->priv->exports);
free(pkg->priv->names);
- free(pkg->priv);
free(pkg);
return rc;
return 0;
return pkg->priv->names[idx].name;
}
+
+struct upkg_file *upkg_export_open(struct upkg *pkg, unsigned long idx)
+{
+ struct upkg_file *f;
+
+ if (idx >= pkg->export_count)
+ return NULL;
+
+ f = malloc(sizeof *f);
+ if (f == NULL)
+ return NULL;
+
+ *f = (struct upkg_file) {
+ .pkg = pkg,
+ .base = pkg->priv->exports[idx].offset,
+ .len = pkg->priv->exports[idx].size,
+ .name = pkg->priv->exports[idx].name,
+ };
+
+ return f;
+}
+
+void upkg_export_close(struct upkg_file *f)
+{
+ if (f->pkg->priv->last_file == f)
+ f->pkg->priv->last_file = NULL;
+ free(f);
+}
+
+long upkg_export_tell(struct upkg_file *f)
+{
+ return f->offset;
+}
+
+int upkg_export_seek(struct upkg_file *f, long offset, int whence)
+{
+ int rc = EOF;
+
+ switch (whence) {
+ case SEEK_CUR:
+ offset = f->offset + offset;
+ case SEEK_SET:
+ if (offset < 0 || offset > f->len)
+ return EOF;
+ rc = fseek(f->pkg->priv->f, f->base + offset, SEEK_SET);
+ break;
+ case SEEK_END:
+ offset = -offset;
+ if (offset < 0 || offset > f->len)
+ return EOF;
+ offset = f->len - offset;
+ rc = fseek(f->pkg->priv->f, f->base + offset, SEEK_SET);
+ break;
+ }
+
+ if (rc == 0)
+ f->pkg->priv->last_file = f;
+ return rc;
+}
+
+size_t upkg_export_read(struct upkg_file *f, void *buf, size_t n)
+{
+ size_t want = MIN(n, f->len - f->offset);
+ size_t rc;
+
+ if (want == 0) {
+ return 0;
+ }
+
+ if (f != f->pkg->priv->last_file) {
+ if (fseek(f->pkg->priv->f, f->base + f->offset, SEEK_SET))
+ return 0;
+ }
+
+ rc = fread(buf, 1, want, f->pkg->priv->f);
+ f->offset += rc;
+ return rc;
+}