mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 13:50:13 +00:00
985 lines
25 KiB
C++
985 lines
25 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif /* HAVE_CONFIG_H */
|
|
|
|
#define MALLOC_DEBUG
|
|
|
|
#include <sciresource.h>
|
|
#include <engine.h>
|
|
#include <console.h>
|
|
#include <versions.h>
|
|
|
|
#ifdef HAVE_GETOPT_H
|
|
#include <getopt.h>
|
|
#endif /* HAVE_GETOPT_H */
|
|
|
|
static int hexdump = 0;
|
|
static int opcode_size = 0;
|
|
static int verbose = 0;
|
|
|
|
static resource_mgr_t *resmgr;
|
|
|
|
#ifdef HAVE_GETOPT_LONG
|
|
static struct option options[] = {
|
|
{"version", no_argument, 0, 256},
|
|
{"help", no_argument, 0, 'h'},
|
|
{"hexdump", no_argument, &hexdump, 1},
|
|
{"opcode-size", no_argument, &opcode_size, 1},
|
|
{"verbose", no_argument, &verbose, 1},
|
|
{"gamedir", required_argument, 0, 'd'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
#endif /* HAVE_GETOPT_LONG */
|
|
|
|
#define SCI_ASSUME_VERSION SCI_VERSION_FTU_NEW_SCRIPT_HEADER
|
|
|
|
typedef struct name_s {
|
|
int offset;
|
|
char *name;
|
|
int class_no;
|
|
struct name_s *next;
|
|
} name_t;
|
|
|
|
typedef struct area_s {
|
|
int start_offset;
|
|
int end_offset;
|
|
void *data;
|
|
struct area_s *next;
|
|
} area_t;
|
|
|
|
enum area_type { area_said, area_string, area_object, area_last };
|
|
|
|
typedef struct script_state_s {
|
|
int script_no;
|
|
name_t *names;
|
|
area_t *areas [area_last];
|
|
|
|
struct script_state_s *next;
|
|
} script_state_t;
|
|
|
|
typedef struct disasm_state_s {
|
|
char **snames;
|
|
int selector_count;
|
|
opcode *opcodes;
|
|
int kernel_names_nr;
|
|
char **kernel_names;
|
|
word_t **words;
|
|
int word_count;
|
|
|
|
char **class_names;
|
|
int *class_selector_count;
|
|
short **class_selectors;
|
|
int class_count;
|
|
int old_header;
|
|
|
|
script_state_t *scripts;
|
|
} disasm_state_t;
|
|
|
|
void
|
|
disassemble_script(disasm_state_t *d, int res_no, int pass_no);
|
|
|
|
script_state_t *
|
|
find_script_state(disasm_state_t *d, int script_no);
|
|
|
|
void
|
|
script_free_names(script_state_t *s);
|
|
|
|
void
|
|
script_add_name(script_state_t *s, int aoffset, char *aname, int aclass_no);
|
|
|
|
char *
|
|
script_find_name(script_state_t *s, int offset, int *class_no);
|
|
|
|
void
|
|
script_add_area(script_state_t *s, int start_offset, int end_offset, int type, void *data);
|
|
|
|
void
|
|
script_free_areas(script_state_t *s);
|
|
|
|
int
|
|
script_get_area_type(script_state_t *s, int offset, void **pdata);
|
|
|
|
void
|
|
disasm_init(disasm_state_t *d);
|
|
|
|
void
|
|
disasm_free_state(disasm_state_t *d);
|
|
|
|
int main(int argc, char** argv) {
|
|
int i;
|
|
char outfilename [256];
|
|
int optindex = 0;
|
|
int c;
|
|
disasm_state_t disasm_state;
|
|
char *gamedir = NULL;
|
|
int res_version = SCI_VERSION_AUTODETECT;
|
|
|
|
#ifdef HAVE_GETOPT_LONG
|
|
while ((c = getopt_long(argc, argv, "vhxr:d:", options, &optindex)) > -1) {
|
|
#else /* !HAVE_GETOPT_H */
|
|
while ((c = getopt(argc, argv, "vhxr:d:")) > -1) {
|
|
#endif /* !HAVE_GETOPT_H */
|
|
|
|
switch (c) {
|
|
case 256:
|
|
printf("scidisasm ("PACKAGE") "VERSION"\n");
|
|
printf("This program is copyright (C) 1999 Christoph Reichenbach.\n"
|
|
"It comes WITHOUT WARRANTY of any kind.\n"
|
|
"This is free software, released under the GNU General Public License.\n");
|
|
exit(0);
|
|
|
|
case 'h':
|
|
printf("Usage: scidisasm\n"
|
|
"\nAvailable options:\n"
|
|
" --version Prints the version number\n"
|
|
" --help -h Displays this help message\n"
|
|
" --gamedir <dir> -d<dir> Read game resources from dir\n"
|
|
" --hexdump -x Hex dump all script resources\n"
|
|
" --verbose Print additional disassembly information\n"
|
|
" --opcode-size Print opcode size postfixes\n");
|
|
exit(0);
|
|
|
|
case 'd':
|
|
if (gamedir) sci_free(gamedir);
|
|
gamedir = sci_strdup(optarg);
|
|
break;
|
|
|
|
case 'r':
|
|
res_version = atoi(optarg);
|
|
break;
|
|
|
|
case 0: /* getopt_long already did this for us */
|
|
case '?':
|
|
/* getopt_long already printed an error message. */
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (gamedir)
|
|
if (chdir(gamedir)) {
|
|
printf("Error changing to game directory '%s'\n", gamedir);
|
|
exit(1);
|
|
}
|
|
|
|
printf("Loading resources...\n");
|
|
if (!(resmgr = scir_new_resource_manager(sci_getcwd(), res_version,
|
|
1, 1024 * 128))) {
|
|
fprintf(stderr, "Could not find any resources; quitting.\n");
|
|
exit(1);
|
|
}
|
|
|
|
disasm_init(&disasm_state);
|
|
|
|
script_adjust_opcode_formats(resmgr->sci_version);
|
|
|
|
printf("Performing first pass...\n");
|
|
for (i = 0; i < resmgr->resources_nr; i++)
|
|
if (resmgr->resources[i].type == sci_script)
|
|
disassemble_script(&disasm_state,
|
|
resmgr->resources[i].number, 1);
|
|
|
|
printf("Performing second pass...\n");
|
|
for (i = 0; i < resmgr->resources_nr; i++)
|
|
if (resmgr->resources[i].type == sci_script) {
|
|
sprintf(outfilename, "%03d.script",
|
|
resmgr->resources[i].number);
|
|
open_console_file(outfilename);
|
|
disassemble_script(&disasm_state,
|
|
resmgr->resources[i].number, 2);
|
|
}
|
|
|
|
close_console_file();
|
|
disasm_free_state(&disasm_state);
|
|
|
|
free(resmgr->resource_path);
|
|
scir_free_resource_manager(resmgr);
|
|
return 0;
|
|
}
|
|
|
|
/* -- General operations on disasm_state_t ------------------------------- */
|
|
|
|
void
|
|
disasm_init(disasm_state_t *d) {
|
|
d->snames = vocabulary_get_snames(resmgr, &d->selector_count, SCI_ASSUME_VERSION);
|
|
d->opcodes = vocabulary_get_opcodes(resmgr);
|
|
d->kernel_names = vocabulary_get_knames(resmgr, &d->kernel_names_nr);
|
|
d->words = vocab_get_words(resmgr, &d->word_count);
|
|
d->scripts = NULL;
|
|
d->old_header = 0;
|
|
|
|
d->class_count = vocabulary_get_class_count(resmgr);
|
|
d->class_names = (char **) sci_malloc(d->class_count * sizeof(char *));
|
|
memset(d->class_names, 0, d->class_count * sizeof(char *));
|
|
d->class_selector_count = (int *) sci_malloc(d->class_count * sizeof(int));
|
|
memset(d->class_selector_count, 0, d->class_count * sizeof(int));
|
|
d->class_selectors = (short **) sci_malloc(d->class_count * sizeof(short *));
|
|
memset(d->class_selectors, 0, d->class_count * sizeof(short *));
|
|
}
|
|
|
|
void
|
|
disasm_free_state(disasm_state_t *d) {
|
|
script_state_t *s, *next_script;
|
|
int i;
|
|
|
|
s = d->scripts;
|
|
while (s) {
|
|
next_script = s->next;
|
|
script_free_names(s);
|
|
script_free_areas(s);
|
|
s = next_script;
|
|
}
|
|
|
|
for (i = 0; i < d->class_count; i++) {
|
|
if (d->class_names [i]) sci_free(d->class_names [i]);
|
|
if (d->class_selectors [i]) sci_free(d->class_selectors [i]);
|
|
}
|
|
|
|
free(d->class_names);
|
|
free(d->class_selectors);
|
|
free(d->class_selector_count);
|
|
|
|
vocabulary_free_snames(d->snames);
|
|
vocabulary_free_opcodes(d->opcodes);
|
|
vocabulary_free_knames(d->kernel_names);
|
|
vocab_free_words(d->words, d->word_count);
|
|
}
|
|
|
|
script_state_t *
|
|
find_script_state(disasm_state_t *d, int script_no) {
|
|
script_state_t *s;
|
|
|
|
for (s = d->scripts; s; s = s->next)
|
|
if (s->script_no == script_no) return s;
|
|
|
|
s = (script_state_t *) sci_malloc(sizeof(script_state_t));
|
|
memset(s, 0, sizeof(script_state_t));
|
|
s->script_no = script_no;
|
|
s->next = d->scripts;
|
|
|
|
d->scripts = s;
|
|
return s;
|
|
}
|
|
|
|
/* -- Name table operations ---------------------------------------------- */
|
|
|
|
void
|
|
script_free_names(script_state_t *s) {
|
|
name_t *p = s->names, *next_name;
|
|
|
|
while (p) {
|
|
next_name = p->next;
|
|
free(p->name);
|
|
free(p);
|
|
p = next_name;
|
|
}
|
|
|
|
s->names = NULL;
|
|
}
|
|
|
|
void
|
|
script_add_name(script_state_t *s, int aoffset, char *aname, int aclass_no) {
|
|
name_t *p;
|
|
char *name = script_find_name(s, aoffset, NULL);
|
|
if (name) return;
|
|
|
|
p = (name_t *) sci_malloc(sizeof(name_t));
|
|
p->offset = aoffset;
|
|
p->name = sci_strdup(aname);
|
|
p->class_no = aclass_no;
|
|
p->next = s->names;
|
|
s->names = p;
|
|
}
|
|
|
|
char *
|
|
script_find_name(script_state_t *s, int offset, int *aclass_no) {
|
|
name_t *p;
|
|
|
|
for (p = s->names; p; p = p->next)
|
|
if (p->offset == offset) {
|
|
if (aclass_no && p->class_no != -2) *aclass_no = p->class_no;
|
|
return p->name;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* -- Area table operations ---------------------------------------------- */
|
|
|
|
void
|
|
script_add_area(script_state_t *s, int start_offset, int end_offset, int type, void *data) {
|
|
area_t *area;
|
|
|
|
area = (area_t *) sci_malloc(sizeof(area_t));
|
|
area->start_offset = start_offset;
|
|
area->end_offset = end_offset;
|
|
area->data = data;
|
|
area->next = s->areas [type];
|
|
|
|
s->areas [type] = area;
|
|
}
|
|
|
|
void
|
|
script_free_areas(script_state_t *s) {
|
|
int i;
|
|
|
|
for (i = 0; i < area_last; i++) {
|
|
area_t *area = s->areas [i], *next_area;
|
|
while (area) {
|
|
next_area = area->next;
|
|
free(area);
|
|
area = next_area;
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
script_get_area_type(script_state_t *s, int offset, void **pdata) {
|
|
int i;
|
|
|
|
for (i = 0; i < area_last; i++) {
|
|
area_t *area = s->areas [i];
|
|
while (area) {
|
|
if (area->start_offset <= offset && area->end_offset >= offset) {
|
|
if (pdata != NULL) *pdata = area->data;
|
|
return i;
|
|
}
|
|
area = area->next;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
char *
|
|
get_selector_name(disasm_state_t *d, int selector) {
|
|
static char selector_name [256];
|
|
|
|
if (d->snames && selector >= 0 && selector < d->selector_count)
|
|
return d->snames [selector];
|
|
else {
|
|
sprintf(selector_name, "unknown_sel_%X", selector);
|
|
return selector_name;
|
|
}
|
|
}
|
|
|
|
const char *
|
|
get_class_name(disasm_state_t *d, int class_no) {
|
|
static char class_name [256];
|
|
|
|
if (class_no == -1)
|
|
return "<none>";
|
|
else if (class_no >= 0 && class_no < d->class_count && d->class_names [class_no])
|
|
return d->class_names [class_no];
|
|
else {
|
|
sprintf(class_name, "class_%d", class_no);
|
|
return class_name;
|
|
}
|
|
}
|
|
|
|
/* -- Code to dump individual script block types ------------------------- */
|
|
|
|
static void
|
|
script_dump_object(disasm_state_t *d, script_state_t *s,
|
|
unsigned char *data, int seeker, int objsize, int pass_no) {
|
|
int selectors, overloads, selectorsize;
|
|
int species = getInt16(data + 8 + seeker);
|
|
int superclass = getInt16(data + 10 + seeker);
|
|
int namepos = getInt16(data + 14 + seeker);
|
|
int i = 0;
|
|
short sel;
|
|
const char *name;
|
|
char buf [256];
|
|
short *sels;
|
|
|
|
selectors = (selectorsize = getInt16(data + seeker + 6));
|
|
name = namepos ? ((const char *)data + namepos) : "<unknown>";
|
|
|
|
if (pass_no == 1)
|
|
script_add_area(s, seeker, seeker + objsize - 1, area_object, strdup(name));
|
|
|
|
if (pass_no == 2) {
|
|
sciprintf(".object\n");
|
|
sciprintf("Name: %s\n", name);
|
|
sciprintf("Superclass: %s [%x]\n", get_class_name(d, superclass), superclass);
|
|
sciprintf("Species: %s [%x]\n", get_class_name(d, species), species);
|
|
|
|
sciprintf("-info-:%x\n", getInt16(data + 12 + seeker) & 0xffff);
|
|
|
|
sciprintf("Function area offset: %x\n", getInt16(data + seeker + 4));
|
|
sciprintf("Selectors [%x]:\n", selectors);
|
|
}
|
|
|
|
seeker += 8;
|
|
|
|
if (species < d->class_count)
|
|
sels = d->class_selectors [species];
|
|
else
|
|
sels = NULL;
|
|
|
|
while (selectors--) {
|
|
if (pass_no == 2) {
|
|
sel = getInt16(data + seeker) & 0xffff;
|
|
if (sels && (sels [i] >= 0) && (sels[i] < d->selector_count)) {
|
|
sciprintf(" [#%03x] %s = 0x%x\n", i, d->snames [sels [i]], sel);
|
|
i++;
|
|
} else
|
|
sciprintf(" [#%03x] <unknown> = 0x%x\n", i++, sel);
|
|
}
|
|
|
|
seeker += 2;
|
|
}
|
|
|
|
selectors = overloads = getInt16(data + seeker);
|
|
|
|
if (pass_no == 2)
|
|
sciprintf("Overloaded functions: %x\n", overloads);
|
|
|
|
seeker += 2;
|
|
|
|
while (overloads--) {
|
|
word selector = getInt16(data + (seeker)) & 0xffff;
|
|
if (d->old_header) selector >>= 1;
|
|
|
|
if (pass_no == 1) {
|
|
sprintf(buf, "%s::%s", name, get_selector_name(d, selector));
|
|
script_add_name(s, getInt16(data + seeker + selectors*2 + 2), buf, species);
|
|
} else {
|
|
sciprintf(" [%03x] %s: @", selector, get_selector_name(d, selector));
|
|
sciprintf("%04x\n", getInt16(data + seeker + selectors*2 + 2));
|
|
}
|
|
|
|
seeker += 2;
|
|
}
|
|
}
|
|
|
|
static void
|
|
script_dump_class(disasm_state_t *d, script_state_t *s,
|
|
unsigned char *data, int seeker, int objsize, int pass_no) {
|
|
word selectors, overloads, selectorsize;
|
|
int species = getInt16(data + 8 + seeker);
|
|
int superclass = getInt16(data + 10 + seeker);
|
|
int namepos = getInt16(data + 14 + seeker);
|
|
const char *name;
|
|
char buf [256];
|
|
int i;
|
|
|
|
name = namepos ? ((const char *)data + namepos) : "<unknown>";
|
|
selectors = (selectorsize = getInt16(data + seeker + 6));
|
|
|
|
if (pass_no == 1) {
|
|
if (species >= 0 && species < d->class_count) {
|
|
if (!namepos) {
|
|
sprintf(buf, "class_%d", species);
|
|
d->class_names [species] = sci_strdup(buf);
|
|
} else
|
|
d->class_names [species] = sci_strdup(name);
|
|
|
|
d->class_selector_count [species] = selectors;
|
|
d->class_selectors [species] = (short *) sci_malloc(sizeof(short) * selectors);
|
|
}
|
|
}
|
|
|
|
if (pass_no == 2) {
|
|
sciprintf(".class\n");
|
|
sciprintf("Name: %s\n", name);
|
|
sciprintf("Superclass: %s [%x]\n", get_class_name(d, superclass), superclass);
|
|
sciprintf("Species: %x\n", species);
|
|
sciprintf("-info-:%x\n", getInt16(data + 12 + seeker) & 0xffff);
|
|
|
|
sciprintf("Function area offset: %x\n", getInt16(data + seeker + 4));
|
|
sciprintf("Selectors [%x]:\n", selectors);
|
|
}
|
|
|
|
seeker += 8;
|
|
selectorsize <<= 1;
|
|
|
|
for (i = 0; i < selectors; i++) {
|
|
word selector = 0xffff & getInt16(data + (seeker) + selectorsize);
|
|
if (d->old_header) selector >>= 1;
|
|
|
|
if (pass_no == 1) {
|
|
if (species >= 0 && species < d->class_count)
|
|
d->class_selectors [species][i] = selector;
|
|
} else
|
|
sciprintf(" [%03x] %s = 0x%x\n", selector, get_selector_name(d, selector),
|
|
getInt16(data + seeker) & 0xffff);
|
|
|
|
seeker += 2;
|
|
}
|
|
|
|
seeker += selectorsize;
|
|
|
|
selectors = overloads = getInt16(data + seeker);
|
|
|
|
sciprintf("Overloaded functions: %x\n", overloads);
|
|
|
|
seeker += 2;
|
|
|
|
while (overloads--) {
|
|
word selector = getInt16(data + (seeker)) & 0xffff;
|
|
if (d->old_header) selector >>= 1;
|
|
|
|
if (pass_no == 1) {
|
|
sprintf(buf, "%s::%s", name, get_selector_name(d, selector));
|
|
script_add_name(s, getInt16(data + seeker + selectors*2 + 2) & 0xffff, buf, species);
|
|
} else {
|
|
sciprintf(" [%03x] %s: @", selector & 0xffff, get_selector_name(d, selector));
|
|
sciprintf("%04x\n", getInt16(data + seeker + selectors*2 + 2) & 0xffff);
|
|
}
|
|
|
|
seeker += 2;
|
|
}
|
|
}
|
|
|
|
static int
|
|
script_dump_said_string(disasm_state_t *d, unsigned char *data, int seeker) {
|
|
while (1) {
|
|
unsigned short nextitem = (unsigned char) data [seeker++];
|
|
if (nextitem == 0xFF) return seeker;
|
|
|
|
if (nextitem >= 0xF0) {
|
|
switch (nextitem) {
|
|
case 0xf0:
|
|
sciprintf(", ");
|
|
break;
|
|
case 0xf1:
|
|
sciprintf("& ");
|
|
break;
|
|
case 0xf2:
|
|
sciprintf("/ ");
|
|
break;
|
|
case 0xf3:
|
|
sciprintf("( ");
|
|
break;
|
|
case 0xf4:
|
|
sciprintf(") ");
|
|
break;
|
|
case 0xf5:
|
|
sciprintf("[ ");
|
|
break;
|
|
case 0xf6:
|
|
sciprintf("] ");
|
|
break;
|
|
case 0xf7:
|
|
sciprintf("# ");
|
|
break;
|
|
case 0xf8:
|
|
sciprintf("< ");
|
|
break;
|
|
case 0xf9:
|
|
sciprintf("> ");
|
|
break;
|
|
}
|
|
} else {
|
|
nextitem = nextitem << 8 | (unsigned char) data [seeker++];
|
|
sciprintf("%s ", vocab_get_any_group_word(nextitem, d->words, d->word_count));
|
|
if (verbose)
|
|
sciprintf("[%03x] ", nextitem);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
script_dump_said(disasm_state_t *d, script_state_t *s,
|
|
unsigned char *data, int seeker, int objsize, int pass_no) {
|
|
int _seeker = seeker + objsize - 4;
|
|
|
|
if (pass_no == 1) {
|
|
script_add_area(s, seeker, seeker + objsize - 1, area_said, NULL);
|
|
return;
|
|
}
|
|
|
|
sciprintf(".said\n");
|
|
|
|
while (seeker < _seeker - 1) {
|
|
sciprintf("%04x: ", seeker);
|
|
seeker = script_dump_said_string(d, data, seeker);
|
|
sciprintf("\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
script_dump_synonyms(disasm_state_t *d, script_state_t *s,
|
|
unsigned char *data, int seeker, int objsize, int pass_no) {
|
|
int _seeker = seeker + objsize - 4;
|
|
|
|
sciprintf("Synonyms:\n");
|
|
while (seeker < _seeker) {
|
|
int search = getInt16(data + seeker);
|
|
int replace = getInt16(data + seeker + 2);
|
|
seeker += 4;
|
|
if (search < 0) break;
|
|
sciprintf("%s[%03x] ==> %s[%03x]\n",
|
|
vocab_get_any_group_word(search, d->words, d->word_count), search,
|
|
vocab_get_any_group_word(replace, d->words, d->word_count), replace);
|
|
}
|
|
}
|
|
|
|
static void
|
|
script_dump_strings(disasm_state_t *d, script_state_t *s,
|
|
unsigned char *data, int seeker, int objsize, int pass_no) {
|
|
int endptr = seeker + objsize - 4;
|
|
|
|
if (pass_no == 1) {
|
|
script_add_area(s, seeker, seeker + objsize - 1, area_string, NULL);
|
|
return;
|
|
}
|
|
|
|
sciprintf(".strings\n");
|
|
while (data [seeker] && seeker < endptr) {
|
|
sciprintf("%04x: %s\n", seeker, data + seeker);
|
|
seeker += strlen((char *) data + seeker) + 1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
script_dump_exports(disasm_state_t *d, script_state_t *s,
|
|
unsigned char *data, int seeker, int objsize, int pass_no) {
|
|
byte *pexport = (byte *)(data + seeker);
|
|
word export_count = getUInt16(pexport);
|
|
int i;
|
|
char buf [256];
|
|
|
|
pexport += 2;
|
|
|
|
if (pass_no == 2) sciprintf(".exports\n");
|
|
|
|
for (i = 0; i < export_count; i++) {
|
|
if (pass_no == 1) {
|
|
guint16 offset = getUInt16(pexport);
|
|
sprintf(buf, "exp_%02X", i);
|
|
script_add_name(s, offset, buf, -1);
|
|
} else
|
|
sciprintf("%02X: %04X\n", i, *pexport);
|
|
pexport += 2;
|
|
}
|
|
}
|
|
|
|
/* -- The disassembly code ----------------------------------------------- */
|
|
|
|
static void
|
|
script_disassemble_code(disasm_state_t *d, script_state_t *s,
|
|
unsigned char *data, int seeker, int objsize, int pass_no) {
|
|
int endptr = seeker + objsize - 4;
|
|
int i = 0;
|
|
int cur_class = -1;
|
|
word dest;
|
|
void *area_data;
|
|
char buf [256];
|
|
char *dest_name;
|
|
|
|
if (pass_no == 2) sciprintf(".code\n");
|
|
|
|
while (seeker < endptr - 1) {
|
|
unsigned char opsize = data [seeker];
|
|
unsigned char opcode = opsize >> 1;
|
|
word param_value;
|
|
char *name;
|
|
|
|
opsize &= 1; /* byte if true, word if false */
|
|
|
|
if (pass_no == 2) {
|
|
name = script_find_name(s, seeker, &cur_class);
|
|
if (name) sciprintf(" %s:\n", name);
|
|
sciprintf("%04X: ", seeker);
|
|
sciprintf("%s", d->opcodes[opcode].name);
|
|
if (opcode_size && formats[opcode][0])
|
|
sciprintf(".%c", opsize ? 'b' : 'w');
|
|
sciprintf("\t");
|
|
}
|
|
|
|
seeker++;
|
|
|
|
for (i = 0; formats[opcode][i]; i++)
|
|
|
|
switch (formats[opcode][i]) {
|
|
|
|
case Script_Invalid:
|
|
if (pass_no == 2) sciprintf("-Invalid operation-");
|
|
break;
|
|
|
|
case Script_SByte:
|
|
case Script_Byte:
|
|
if (pass_no == 2) sciprintf(" %02x", data[seeker]);
|
|
seeker++;
|
|
break;
|
|
|
|
case Script_Word:
|
|
case Script_SWord:
|
|
if (pass_no == 2)
|
|
sciprintf(" %04x", 0xffff & (data[seeker] | (data[seeker+1] << 8)));
|
|
seeker += 2;
|
|
break;
|
|
|
|
case Script_SVariable:
|
|
case Script_Variable:
|
|
case Script_Global:
|
|
case Script_Local:
|
|
case Script_Temp:
|
|
case Script_Param:
|
|
case Script_SRelative:
|
|
case Script_Property:
|
|
case Script_Offset:
|
|
if (opsize)
|
|
param_value = data [seeker++];
|
|
else {
|
|
param_value = 0xffff & (data[seeker] | (data[seeker+1] << 8));
|
|
seeker += 2;
|
|
}
|
|
|
|
if (pass_no == 1) {
|
|
if (opcode == op_jmp || opcode == op_bt || opcode == op_bnt) {
|
|
dest = seeker + (short) param_value;
|
|
sprintf(buf, "lbl_%04X", dest);
|
|
script_add_name(s, dest, buf, -2);
|
|
}
|
|
} else if (pass_no == 2)
|
|
switch (formats[opcode][i]) {
|
|
|
|
case Script_SVariable:
|
|
case Script_Variable:
|
|
if (opcode == op_callk) {
|
|
sciprintf(" #%s", (param_value < d->kernel_names_nr)
|
|
? d->kernel_names[param_value] : "<invalid>");
|
|
if (verbose) sciprintf("[%x]", param_value);
|
|
} else if (opcode == op_class || (opcode == op_super && i == 0)) {
|
|
sciprintf(" %s", (d->class_names && param_value < d->class_count)
|
|
? d->class_names[param_value] : "<invalid>");
|
|
if (verbose) sciprintf("[%x]", param_value);
|
|
} else sciprintf(opsize ? " %02x" : " %04x", param_value);
|
|
|
|
if (opcode == op_pushi && param_value > 0 && param_value < d->selector_count)
|
|
sciprintf("\t\t; selector <%s>", d->snames [param_value]);
|
|
|
|
break;
|
|
|
|
case Script_Global:
|
|
sciprintf(" global_%d", param_value);
|
|
break;
|
|
|
|
case Script_Local:
|
|
sciprintf(" local_%d", param_value);
|
|
break;
|
|
|
|
case Script_Temp:
|
|
sciprintf(" temp_%d", param_value);
|
|
break;
|
|
|
|
case Script_Param:
|
|
sciprintf(" param_%d", param_value);
|
|
break;
|
|
|
|
case Script_Offset:
|
|
dest = (short) param_value;
|
|
dest_name = script_find_name(s, dest, NULL);
|
|
if (dest_name)
|
|
sciprintf(" %s", dest_name);
|
|
else
|
|
sciprintf(" %04x", dest);
|
|
|
|
if (verbose)
|
|
sciprintf(opsize ? " [%02x] " : " [%04x] ", param_value);
|
|
|
|
if (opcode == op_lofsa || opcode == op_lofss) {
|
|
int atype = script_get_area_type(s, dest, &area_data);
|
|
if (atype == area_string) {
|
|
strncpy(buf, (char *) &data [dest], sizeof(buf) - 1);
|
|
buf [sizeof(buf)-1] = 0;
|
|
if (strlen(buf) > 40) {
|
|
buf [40] = 0;
|
|
strcat(buf, "...");
|
|
}
|
|
sciprintf("\t\t; \"%s\"", buf);
|
|
} else if (atype == area_said) {
|
|
sciprintf("\t\t; said \"");
|
|
script_dump_said_string(d, data, dest);
|
|
sciprintf("\"\n");
|
|
} else if (atype == area_object)
|
|
sciprintf("\t\t; object <%s>", area_data);
|
|
}
|
|
break;
|
|
|
|
case Script_SRelative:
|
|
dest = seeker + (short) param_value;
|
|
dest_name = script_find_name(s, dest, NULL);
|
|
if (dest_name)
|
|
sciprintf(" %s", dest_name);
|
|
else
|
|
sciprintf(" %04x", dest);
|
|
|
|
if (verbose)
|
|
sciprintf(opsize ? " [%02x] " : " [%04x] ", param_value);
|
|
|
|
if (opcode == op_lofsa || opcode == op_lofss) {
|
|
int atype = script_get_area_type(s, dest, &area_data);
|
|
if (atype == area_string) {
|
|
strncpy(buf, (char *) &data [dest], sizeof(buf) - 1);
|
|
buf [sizeof(buf)-1] = 0;
|
|
if (strlen(buf) > 40) {
|
|
buf [40] = 0;
|
|
strcat(buf, "...");
|
|
}
|
|
sciprintf("\t\t; \"%s\"", buf);
|
|
} else if (atype == area_said) {
|
|
sciprintf("\t\t; said \"");
|
|
script_dump_said_string(d, data, dest);
|
|
sciprintf("\"\n");
|
|
} else if (atype == area_object)
|
|
sciprintf("\t\t; object <%s>", area_data);
|
|
}
|
|
break;
|
|
|
|
case Script_Property:
|
|
if (cur_class != -1 && param_value / 2 < d->class_selector_count [cur_class]) {
|
|
sciprintf(" %s", get_selector_name(d, d->class_selectors [cur_class][param_value/2]));
|
|
if (verbose) sciprintf("[%x]", param_value);
|
|
} else
|
|
sciprintf(opsize ? " %02x" : " %04x", param_value);
|
|
|
|
break;
|
|
|
|
case Script_End:
|
|
if (pass_no == 2) sciprintf("\n");
|
|
break;
|
|
|
|
default:
|
|
sciprintf("Unexpected opcode format %d\n", (formats[opcode][i]));
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (pass_no == 2) sciprintf("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
disassemble_script_pass(disasm_state_t *d, script_state_t *s,
|
|
resource_t *script, int pass_no) {
|
|
int _seeker = 0;
|
|
word id = getInt16(script->data);
|
|
|
|
if (id > 15) {
|
|
if (pass_no == 2) sciprintf("; Old script header detected\n");
|
|
d->old_header = 1;
|
|
}
|
|
|
|
if (d->old_header) _seeker = 2;
|
|
|
|
while (_seeker < script->size) {
|
|
int objtype = getInt16(script->data + _seeker);
|
|
int objsize;
|
|
int seeker = _seeker + 4;
|
|
|
|
if (!objtype) return;
|
|
|
|
if (pass_no == 2)
|
|
sciprintf("\n");
|
|
|
|
objsize = getInt16(script->data + _seeker + 2);
|
|
|
|
if (pass_no == 2) {
|
|
sciprintf("; Obj type #%x, offset 0x%x, size 0x%x:\n", objtype, _seeker, objsize);
|
|
if (hexdump) sci_hexdump(script->data + seeker, objsize - 4, seeker);
|
|
}
|
|
|
|
_seeker += objsize;
|
|
|
|
switch (objtype) {
|
|
case sci_obj_object:
|
|
script_dump_object(d, s, script->data, seeker, objsize, pass_no);
|
|
break;
|
|
|
|
case sci_obj_code:
|
|
script_disassemble_code(d, s, script->data, seeker, objsize, pass_no);
|
|
break;
|
|
|
|
case sci_obj_synonyms:
|
|
script_dump_synonyms(d, s, script->data, seeker, objsize, pass_no);
|
|
break;
|
|
|
|
case sci_obj_said:
|
|
script_dump_said(d, s, script->data, seeker, objsize, pass_no);
|
|
break;
|
|
|
|
case sci_obj_strings:
|
|
script_dump_strings(d, s, script->data, seeker, objsize, pass_no);
|
|
break;
|
|
|
|
case sci_obj_class:
|
|
script_dump_class(d, s, script->data, seeker, objsize, pass_no);
|
|
break;
|
|
|
|
case sci_obj_exports:
|
|
script_dump_exports(d, s, script->data, seeker, objsize, pass_no);
|
|
break;
|
|
|
|
case sci_obj_pointers:
|
|
if (pass_no == 2) {
|
|
sciprintf("Pointers\n");
|
|
sci_hexdump(script->data + seeker, objsize - 4, seeker);
|
|
};
|
|
break;
|
|
|
|
case sci_obj_preload_text:
|
|
if (pass_no == 2) {
|
|
sciprintf("The script has a preloaded text resource\n");
|
|
};
|
|
break;
|
|
|
|
case sci_obj_localvars:
|
|
if (pass_no == 2) {
|
|
sciprintf("Local vars\n");
|
|
sci_hexdump(script->data + seeker, objsize - 4, seeker);
|
|
};
|
|
break;
|
|
|
|
default:
|
|
sciprintf("Unsupported %d!\n", objtype);
|
|
return;
|
|
}
|
|
}
|
|
|
|
sciprintf("Script ends without terminator\n");
|
|
}
|
|
|
|
void
|
|
disassemble_script(disasm_state_t *d, int res_no, int pass_no) {
|
|
resource_t *script = scir_find_resource(resmgr, sci_script, res_no, 0);
|
|
script_state_t *s = find_script_state(d, res_no);
|
|
|
|
if (!script) {
|
|
sciprintf("Script not found!\n");
|
|
return;
|
|
}
|
|
|
|
disassemble_script_pass(d, s, script, pass_no);
|
|
}
|