#include #include #include #include #include "umr.h" namespace umr { #include "urf.h" /* upkg_hdr *hdr; // read the urf.h for these 4... upkg_exports *exports; upkg_imports *imports; upkg_names *names; FILE *file; // we store the file pointer globally around here int data_size, // a way to standardize some freaky parts of the format pkg_opened = 0; // sanity check int indent_level; char header[4096], // we load the header into this buffer buf[256]; // temp buf for get_string() */ // this function decodes the encoded indices in the upkg files signed long upkg::get_fci(char *in) { signed long a; int size; a = 0; size = 1; a = in[0] & 0x3f; if (in[0] & 0x40) { size++; a |= (in[1] & 0x7f) << 6; if (in[1] & 0x80) { size++; a |= (in[2] & 0x7f) << 13; if (in[2] & 0x80) { size++; a |= (in[3] & 0x7f) << 20; if (in[3] & 0x80) { size++; a |= (in[4] & 0x3f) << 27; } } } } if (in[0] & 0x80) a = -a; data_size = size; return a; } unsigned long upkg::get_u32(void *addr) { const uint8_t * addr8 = (const uint8_t *) addr; uint32_t rval = addr8[0] | (addr8[1] << 8) | (addr8[2] << 16) | (addr8[3] << 24); data_size = sizeof(uint32_t); return rval; } signed long upkg::get_s32(void *addr) { const uint8_t * addr8 = (const uint8_t *) addr; int32_t rval = addr8[0] | (addr8[1] << 8) | (addr8[2] << 16) | (addr8[3] << 24); data_size = sizeof(int32_t); return rval; } signed long upkg::get_s16(void *addr) { const uint8_t * addr8 = (const uint8_t *) addr; int16_t rval = addr8[0] | (addr8[1] << 8); data_size = sizeof(int16_t); return rval; } signed long upkg::get_s8(void *addr) { data_size = sizeof(int8_t); return *(int8_t *) addr; } char * upkg::get_string(char *addr, int count) { if (count > UPKG_MAX_NAME_SIZE || count == UPKG_NAME_NOCOUNT) count = UPKG_MAX_NAME_SIZE; strncpy(buf, addr, count); // the string stops at count chars, or is ASCIIZ data_size = (unsigned int)(strlen(buf) + 1); return buf; } signed int export_index(signed int i) { if (i > 0) { return i - 1; } return -1; } signed int import_index(signed int i) { if (i < 0) { return -i - 1; } return -1; } // idx == exports[idx], c_idx == index to the next element from idx int upkg::set_classname(int idx, int c_idx) { int i, next; i = c_idx; do { if (i < 0) { i = import_index(i); if (!strcmp(names[imports[i].class_name].name, "Class")) { exports[idx].class_name = imports[i].object_name; return imports[i].package_index; } next = imports[i].package_index; } if (i > 0) { i = export_index(i); next = exports[i].class_index; } else { break; } i = next; } while (i >= -hdr->import_count && i < hdr->export_count); exports[idx].class_name = hdr->name_count; return c_idx; } int upkg::set_pkgname(int idx, int c_idx) { int i, next; i = c_idx; do { if (i < 0) { i = import_index(i); if (!strcmp(names[imports[i].class_name].name, "Package")) { exports[idx].package_name = imports[i].object_name; return imports[i].package_index; } next = imports[i].package_index; } if (i > 0) { i = export_index(i); next = exports[i].class_index; } else { break; } i = next; } while (i >= -hdr->import_count && i < hdr->export_count); exports[idx].package_name = hdr->name_count; return c_idx; } // load in the header, AWA allocating the needed memory for the tables int upkg::load_upkg(void) { int index, i; index = 0; hdr = (upkg_hdr *) header; if (get_u32(&hdr->tag) != UPKG_HDR_TAG) return -1; for (i = 0; export_desc[i].version; i++) { if (get_u32(&hdr->file_version) == export_desc[i].version) { break; } } if (export_desc[i].version == 0) return -1; names = (upkg_names *) malloc(sizeof(upkg_names) * (hdr->name_count + 1)); if (names == NULL) return -1; exports = (upkg_exports *) malloc(sizeof(upkg_exports) * hdr->export_count); if (exports == NULL) { free(names); return -1; } imports = (upkg_imports *) malloc(sizeof(upkg_imports) * hdr->import_count); if (imports == NULL) { free(exports); free(names); return -1; } return 0; } // load the name table void upkg::get_names(void) { int i, j, index; index = (int) get_u32(&hdr->name_offset); for (i = 0, j = (int) get_u32(&hdr->name_count); i < j; i++) { if (get_u32(&hdr->file_version) >= 64) { get_string(&header[index + 1], (int) get_s8(&header[index])); index++; } else { get_string(&header[index], UPKG_NAME_NOCOUNT); } index += data_size; strncpy(names[i].name, buf, UPKG_MAX_NAME_SIZE); names[i].flags = (int32_t) get_s32(&header[index]); index += data_size; } // hdr->name_count + 1 names total, this one's last strncpy(names[i].name, "(NULL)", UPKG_MAX_NAME_SIZE); names[i].flags = 0; } // load the export table (which is at the end of the file... go figure) void upkg::get_exports_cpnames(int idx) { int x; if (idx < 0 || idx >= get_u32(&hdr->export_count)) return; x = (int) get_u32(&exports[idx].class_index); x = set_classname(idx, x); set_pkgname(idx, x); } void upkg::get_exports(void) { int i, j, index; char readbuf[1024]; reader->seek(hdr->export_offset); reader->read(readbuf, 1024); index = 0; for (i = 0, j = (int) get_u32(&hdr->export_count); i < j; i++) { exports[i].class_index = (int32_t) get_fci(&readbuf[index]); index += data_size; exports[i].package_index = (int32_t) get_s32(&readbuf[index]); index += data_size; exports[i].super_index = (int32_t) get_fci(&readbuf[index]); index += data_size; exports[i].object_name = (int32_t) get_fci(&readbuf[index]); index += data_size; exports[i].object_flags = (int32_t) get_s32(&readbuf[index]); index += data_size; exports[i].serial_size = (int32_t) get_fci(&readbuf[index]); index += data_size; if (exports[i].serial_size > 0) { exports[i].serial_offset = (int32_t) get_fci(&readbuf[index]); index += data_size; } else { exports[i].serial_offset = -1; } get_exports_cpnames(i); // go grab the class & package names } } // load the import table (notice a trend?). same story as get_exports() void upkg::get_imports(void) { int i, j, index; char readbuf[1024]; reader->seek(hdr->import_offset); reader->read(readbuf, 1024); index = 0; for (i = 0, j = (int) get_u32(&hdr->import_count); i < j; i++) { imports[i].class_package = (int32_t) get_fci(&readbuf[index]); index += data_size; imports[i].class_name = (int32_t) get_fci(&readbuf[index]); index += data_size; imports[i].package_index = (int32_t) get_s32(&readbuf[index]); index += data_size; imports[i].object_name = (int32_t) get_fci(&readbuf[index]); index += data_size; } } // load the type_names void upkg::get_type(char *buf, int e, int d) { int i, j, index; signed long tmp = 0; char *chtmp; index = 0; for (i = 0, j = (int) strlen(export_desc[d].order); i < j; i++) { switch (export_desc[d].order[i]) { case UPKG_DATA_FCI: tmp = get_fci(&buf[index]); index += data_size; break; case UPKG_DATA_32: tmp = get_s32(&buf[index]); index += data_size; break; case UPKG_DATA_16: tmp = get_s16(&buf[index]); index += data_size; break; case UPKG_DATA_8: tmp = get_s8(&buf[index]); index += data_size; break; case UPKG_DATA_ASCIC: chtmp = get_string(&buf[index + 1], (int) get_s8(&buf[index])); index += data_size + 1; break; case UPKG_DATA_ASCIZ: chtmp = get_string(&buf[index], UPKG_NAME_NOCOUNT); index += data_size; break; case UPKG_OBJ_JUNK: // do nothing!!! break; case UPKG_OBJ_NAME: exports[e].type_name = (int32_t) tmp; break; case UPKG_EXP_SIZE: // maybe we'll do something later on break; case UPKG_OBJ_SIZE: exports[e].object_size = (int32_t) tmp; break; default: exports[e].type_name = -1; return; } } exports[e].object_offset = exports[e].serial_offset + index; } int upkg::get_types_isgood(int idx) { int i; for (i = 0; export_desc[i].version; i++) { if (export_desc[i].version == get_u32(&hdr->file_version)) { if (strcmp(export_desc[i].class_name, names[exports[idx].class_name].name ) == 0) { return i; } } } return -1; } void upkg::check_type(int e, int d) { int i; char readbuf[101], s, l; reader->seek(exports[e].object_offset); reader->read(readbuf, 100); for (i = 0; object_desc[i].sig_offset != -1; i++) { s = object_desc[i].sig_offset; l = strlen(object_desc[i].object_sig); readbuf[100] = readbuf[s + l]; readbuf[s + l] = 0; if (!strcmp(&readbuf[s], object_desc[i].object_sig)) { return; } readbuf[s + l] = readbuf[100]; } exports[e].type_name = -1; } void upkg::get_types(void) { int i, j, k; char readbuf[UPKG_MAX_ORDERS * 4]; for (i = 0, k = (int) get_u32(&hdr->export_count); i < k; i++) { if ((j = get_types_isgood(i)) != -1) { reader->seek(exports[i].serial_offset); reader->read(readbuf, 4 * UPKG_MAX_ORDERS); get_type(readbuf, i, j); check_type(i, j); } else { exports[i].type_name = -1; } } } //************** GLOBALS // open that puppy!!! gets the file opened and the data structs read for use bool upkg::open(file_reader * p_reader) { if (pkg_opened) // is there a pkg opened already? return false; // if so, don't try to open another one! if (p_reader == NULL) return false; reader = p_reader; if (reader->read(header, 4096) < 4096) { return false; } if (load_upkg() != 0) { return false; } pkg_opened = 1; get_names(); // this order is important. get_imports(); get_exports(); get_types(); return true; } // close everything out void upkg::close(void) { if (pkg_opened == 0) return; free(imports); free(exports); free(names); hdr = (upkg_hdr *) 0; pkg_opened = 0; } // API stuff... should be self-explainatory (upkg_o* == unreal package object *) signed int upkg::ocount(void) { if (pkg_opened == 0) return -1; return hdr->export_count; } char * upkg::oname(signed int idx) { idx = export_index(idx); if (idx == -1 || pkg_opened == 0) return NULL; return names[exports[idx].object_name].name; } char * upkg::oclassname(signed int idx) { idx = export_index(idx); if (idx == -1 || pkg_opened == 0) return NULL; return names[exports[idx].class_name].name; } char * upkg::opackagename(signed int idx) { idx = export_index(idx); if (idx == -1 || pkg_opened == 0) return NULL; return names[exports[idx].package_name].name; } char * upkg::otype(signed int idx) { idx = export_index(idx); if (idx == -1 || pkg_opened == 0) return NULL; if (exports[idx].type_name == -1) return NULL; return names[exports[idx].type_name].name; } signed int upkg::export_size(signed int idx) { idx = export_index(idx); if (idx == -1 || pkg_opened == 0) return 0; return exports[idx].serial_size; } signed int upkg::object_size(signed int idx) { idx = export_index(idx); if (idx == -1 || pkg_opened == 0) return 0; return exports[idx].object_size; } signed int upkg::export_offset(signed int idx) { idx = export_index(idx); if (idx == -1 || pkg_opened == 0) return 0; return exports[idx].serial_offset; } signed int upkg::object_offset(signed int idx) { idx = export_index(idx); if (idx == -1 || pkg_opened == 0) return 0; return exports[idx].object_offset; } int upkg::read(void *readbuf, signed int bytes, signed int offset) { if (pkg_opened == 0) return -1; reader->seek(offset); return (int) reader->read(readbuf, bytes); } int upkg::export_dump(file_writer * writer, signed int idx) { int count, diff; void *buffer; idx = export_index(idx); if (idx == -1 || pkg_opened == 0) return -1; buffer = malloc(4096); if (buffer == NULL) return -1; reader->seek(exports[idx].serial_offset); count = exports[idx].serial_size; do { diff = (int) reader->read(buffer, ((count > 4096) ? 4096 : count)); writer->write(buffer, diff); count -= diff; } while (count > 0); free(buffer); return 0; } int upkg::object_dump(file_writer * writer, signed int idx) { int count, diff; void *buffer; idx = export_index(idx); if (idx == -1 || pkg_opened == 0) return -1; buffer = malloc(4096); if (buffer == NULL) return -1; reader->seek(exports[idx].object_offset); count = exports[idx].object_size; do { diff = (int) reader->read(buffer, ((count > 4096) ? 4096 : count)); writer->write(buffer, diff); count -= diff; } while (count > 0); free(buffer); return 0; } }