#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; } } // namespace umr