665 lines
13 KiB
C++
665 lines
13 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
#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 = 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 = get_u32(&hdr->name_offset);
|
|
|
|
for (i = 0, j = get_u32(&hdr->name_count); i < j; i++) {
|
|
if (get_u32(&hdr->file_version) >= 64) {
|
|
get_string(&header[index + 1],
|
|
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 = 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 = 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 = get_u32(&hdr->export_count); i < j; i++) {
|
|
exports[i].class_index = get_fci(&readbuf[index]);
|
|
index += data_size;
|
|
|
|
exports[i].package_index = get_s32(&readbuf[index]);
|
|
index += data_size;
|
|
|
|
exports[i].super_index = get_fci(&readbuf[index]);
|
|
index += data_size;
|
|
|
|
exports[i].object_name = get_fci(&readbuf[index]);
|
|
index += data_size;
|
|
|
|
exports[i].object_flags = get_s32(&readbuf[index]);
|
|
index += data_size;
|
|
|
|
exports[i].serial_size = get_fci(&readbuf[index]);
|
|
index += data_size;
|
|
|
|
if (exports[i].serial_size > 0) {
|
|
exports[i].serial_offset =
|
|
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 = get_u32(&hdr->import_count); i < j; i++) {
|
|
imports[i].class_package = get_fci(&readbuf[index]);
|
|
index += data_size;
|
|
|
|
imports[i].class_name = get_fci(&readbuf[index]);
|
|
index += data_size;
|
|
|
|
imports[i].package_index = get_s32(&readbuf[index]);
|
|
index += data_size;
|
|
|
|
imports[i].object_name = 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;
|
|
char *chtmp;
|
|
|
|
index = 0;
|
|
|
|
for (i = 0, j = 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],
|
|
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 = tmp;
|
|
break;
|
|
case UPKG_EXP_SIZE: // maybe we'll do something later on
|
|
break;
|
|
case UPKG_OBJ_SIZE:
|
|
exports[e].object_size = 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 = 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 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 =
|
|
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 =
|
|
reader->read(buffer, ((count > 4096) ? 4096 : count));
|
|
writer->write(buffer, diff);
|
|
count -= diff;
|
|
} while (count > 0);
|
|
|
|
free(buffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
}
|