X-Git-Url: https://git.draconx.ca/gitweb/upkg.git/blobdiff_plain/fdfd7c7a49a780dedf2d8f0225d710b521ae87e3..a196fc048fe63587123f918791f0ce1da8b9ff55:/src/libupkg.c diff --git a/src/libupkg.c b/src/libupkg.c index e2bcf29..503e3f5 100644 --- a/src/libupkg.c +++ b/src/libupkg.c @@ -2,9 +2,9 @@ * upkg: tool for manipulating Unreal Tournament packages. * Copyright (C) 2009 Nick Bowler * - * This program is free software; you can redistribute it and/or modify + * 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 + * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -13,8 +13,7 @@ * 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 + * along with this program. If not, see . */ #include @@ -62,7 +61,7 @@ struct upkg_private { * 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; @@ -124,11 +123,39 @@ static struct upkg *init_upkg(unsigned char hdr[static UPKG_HDR_SIZE]) return &alloc->pkg; } +static int pkg_init_guid(struct upkg *pkg) +{ + size_t rc; + + if (pkg->version < 68) { + unsigned long heritage_count, heritage_offset; + unsigned char buf[8]; + + rc = fread(buf, 1, sizeof buf, pkg->priv->f); + if (rc < 8) + return -1; + + 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) { size_t rc, len, nbuf = 0; unsigned long index = 0; - char buf[512]; + unsigned char buf[512]; if (fseek(pkg->priv->f, pkg->priv->name_offset, SEEK_SET) != 0) return -1; @@ -143,7 +170,7 @@ static int pkg_init_names(struct upkg *pkg) /* Read some data into buffer. */ if (!feof(pkg->priv->f)) { rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f); - if (rc == 0) + if (rc == 0 && nbuf == 0) goto err; nbuf += rc; } @@ -163,7 +190,7 @@ static int pkg_init_names(struct upkg *pkg) memmove(buf, buf+len+1, nbuf); index++; } else { - char *c = memchr(buf, 0, nbuf); + unsigned char *c = memchr(buf, 0, nbuf); if (!c || nbuf <= c - buf + 5) goto err; len = c - buf + 1; @@ -192,7 +219,7 @@ static int pkg_init_exports(struct upkg *pkg) { size_t rc, len, nbuf = 0; unsigned long index = 0; - char buf[512]; + unsigned char buf[512]; if (fseek(pkg->priv->f, pkg->priv->export_offset, SEEK_SET) != 0) return -1; @@ -208,25 +235,25 @@ static int pkg_init_exports(struct upkg *pkg) /* Read some data into buffer. */ if (!feof(pkg->priv->f)) { rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f); - if (rc == 0) + if (rc == 0 && nbuf == 0) goto err; nbuf += rc; } 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; if (nbuf-len < 4) goto err; - export->package = unpack_32_le(buf+len); + export->package = unpack_s32_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; @@ -235,13 +262,15 @@ static int pkg_init_exports(struct upkg *pkg) export->flags = unpack_32_le(buf+len); len += 4; - rc = decode_index(&export->size, buf+len, nbuf-len); - if (rc == 0) goto err; + rc = upkg_decode_index(&tmp, buf+len, nbuf-len); + if (rc == 0 || tmp < 0) goto err; + export->size = tmp; len += rc; if (export->size) { - rc = decode_index(&export->offset, buf+len, nbuf-len); - if (rc == 0) goto err; + rc = upkg_decode_index(&tmp, buf+len, nbuf-len); + if (rc == 0 || tmp < 0) goto err; + export->offset = tmp; len += rc; } @@ -260,7 +289,7 @@ static int pkg_init_imports(struct upkg *pkg) { size_t rc, len, nbuf = 0; unsigned long index = 0; - char buf[512]; + unsigned char buf[512]; if (fseek(pkg->priv->f, pkg->priv->import_offset, SEEK_SET) != 0) return -1; @@ -276,27 +305,27 @@ static int pkg_init_imports(struct upkg *pkg) /* Read some data into buffer. */ if (!feof(pkg->priv->f)) { rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f); - if (rc == 0) + if (rc == 0 && nbuf == 0) goto err; nbuf += 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; if (nbuf-len < 4) goto err; - import->package = unpack_32_le(buf+len); + import->package = unpack_s32_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; @@ -335,6 +364,10 @@ struct upkg *upkg_fopen(const char *path) } pkg->priv->f = f; + if (pkg_init_guid(pkg) != 0) { + goto err2; + } + if (pkg_init_names(pkg) != 0) { goto err2; } @@ -388,6 +421,91 @@ const char *upkg_get_name(struct upkg *pkg, unsigned long idx) return pkg->priv->names[idx].name; } +long upkg_export_find(struct upkg *pkg, const char *name) +{ + /* This is wrong. + * Export names are not necessarily unique within a package. */ + for (unsigned i = 0; i < pkg->export_count; i++) { + struct upkg_export *export = &pkg->priv->exports[i]; + if (strcmp(export->name, name) == 0) { + return i; + } + } + + return -1; +} + +unsigned long upkg_export_flags(struct upkg *pkg, unsigned long idx) +{ + if (idx < pkg->export_count) + return pkg->priv->exports[idx].flags; + return 0; +} + +const char *upkg_export_name(struct upkg *pkg, unsigned long idx) +{ + if (idx < pkg->export_count) + return pkg->priv->exports[idx].name; + return NULL; +} + +const char *upkg_export_class(struct upkg *pkg, unsigned long idx, + const char **package) +{ + struct upkg_export *export; + struct upkg_import *iclass, *ipackage; + unsigned long pkg_idx; + + if (idx >= pkg->export_count) + return NULL; + + export = &pkg->priv->exports[idx]; + + /* ASSUMPTION: class references are always imports */ + if (export->class > 0) { + fprintf(stderr, "Assumption Violated: class not import\n"); + return NULL; + } + + /* Get the class. */ + if (export->class == 0) { + if (package) *package = "Core"; + return "Class"; + } + + pkg_idx = -(export->class + 1); + if (pkg_idx >= pkg->import_count) + return NULL; + iclass = &pkg->priv->imports[pkg_idx]; + + /* ASSUMPTION: Class references are always Core.Class */ + if (strcmp(iclass->class_name, "Class") || strcmp(iclass->class_package, "Core")) { + fprintf(stderr, "Assumption Violated: class not Core.Class\n"); + return NULL; + } + + /* ASSUMPTION: Package references are always imports */ + if (iclass->package >= 0) { + fprintf(stderr, "Assumption Violated: package not import\n"); + return NULL; + } + + /* Get the package. */ + pkg_idx = -(iclass->package + 1); + if (pkg_idx >= pkg->import_count) + return NULL; + ipackage = &pkg->priv->imports[pkg_idx]; + + /* ASSUMPTION: Package references are always Core.Package */ + if (strcmp(ipackage->class_name, "Package") || strcmp(ipackage->class_package, "Core")) { + fprintf(stderr, "Assumption Violated: package not Core.Package\n"); + return NULL; + } + + if (package) *package = ipackage->object_name; + return iclass->object_name; +} + struct upkg_file *upkg_export_open(struct upkg *pkg, unsigned long idx) { struct upkg_file *f; @@ -403,6 +521,7 @@ struct upkg_file *upkg_export_open(struct upkg *pkg, unsigned long idx) .pkg = pkg, .base = pkg->priv->exports[idx].offset, .len = pkg->priv->exports[idx].size, + .name = pkg->priv->exports[idx].name, }; return f; @@ -415,6 +534,43 @@ void upkg_export_close(struct upkg_file *f) 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; + f->offset = offset; + f->eof = 0; + } else if (f->pkg->priv->last_file == f) { + f->pkg->priv->last_file = NULL; + } + + 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); @@ -431,5 +587,8 @@ size_t upkg_export_read(struct upkg_file *f, void *buf, size_t n) rc = fread(buf, 1, want, f->pkg->priv->f); f->offset += rc; + + if (want < n || (rc < want && feof(f->pkg->priv->f))) + f->eof = 1; return rc; }