mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-16 14:50:17 +00:00
033ade4f5c
svn-id: r38331
1280 lines
32 KiB
C++
1280 lines
32 KiB
C++
/***************************************************************************
|
|
console.c Copyright (C) 1999..2002 Christoph Reichenbach, TU Darmstadt
|
|
|
|
|
|
This program may be modified and copied freely according to the terms of
|
|
the GNU general public license (GPL), as long as the above copyright
|
|
notice and the licensing information contained herein are preserved.
|
|
|
|
Please refer to www.gnu.org for licensing details.
|
|
|
|
This work is provided AS IS, without warranty of any kind, expressed or
|
|
implied, including but not limited to the warranties of merchantibility,
|
|
noninfringement, and fitness for a specific purpose. The author will not
|
|
be held liable for any damage caused by this work or derivatives of it.
|
|
|
|
By using this source code, you agree to the licensing terms as stated
|
|
above.
|
|
|
|
|
|
Please contact the maintainer for bug reports or inquiries.
|
|
|
|
Current Maintainer:
|
|
|
|
Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de]
|
|
|
|
***************************************************************************/
|
|
/* Second half of the console implementation: VM dependent stuff */
|
|
/* Remember, it doesn't have to be fast. */
|
|
|
|
#include "sci/include/sci_memory.h"
|
|
#include "sci/include/engine.h"
|
|
#ifdef SCI_CONSOLE
|
|
|
|
state_t *con_gamestate = NULL;
|
|
|
|
/***************************************************************************/
|
|
/* console commands */
|
|
|
|
static int c_version(struct _state *s); /* displays the package and version number */
|
|
static int c_list(struct _state *s); /* lists various types of things */
|
|
static int c_man(struct _state *s); /* 'manual page' */
|
|
static int c_set(struct _state *s); /* sets an int variable */
|
|
static int c_print(struct _state *s); /* prints a variable */
|
|
static int c_size(struct _state *s); /* displays the size of a resource */
|
|
static int c_dump(struct _state *s); /* gives a hex dump of a resource */
|
|
//static int c_objinfo(struct _state *s); /* shows some info about one class */
|
|
//static int c_objmethods(struct _state *s); /* Disassembles all methods of a class */
|
|
static int c_hexgrep(struct _state *s); /* Searches a string in one resource or resource class */
|
|
static int c_selectornames(struct _state *s); /* Displays all selector names */
|
|
static int c_kernelnames(struct _state *s); /* Displays all kernel function names */
|
|
static int c_dissectscript(struct _state *s); /* Splits a script into objects and explains them */
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
const char *description;
|
|
} cmd_mm_entry_t; /* All later structures must "extend" this */
|
|
|
|
typedef cmd_mm_entry_t cmd_page_t; /* Simple info page */
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
const char *description;
|
|
int (*command)(state_t *);
|
|
const char *param;
|
|
} cmd_command_t;
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
const char *description;
|
|
union {
|
|
int *intp;
|
|
char **charpp;
|
|
reg_t *reg;
|
|
} var;
|
|
} cmd_var_t;
|
|
|
|
|
|
typedef void printfunc_t(cmd_mm_entry_t *data, int full);
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
void *data; /* cmd_mm_entry_t */
|
|
size_t size_per_entry;
|
|
printfunc_t *print;
|
|
int entries; /* Number of used entries */
|
|
int allocated; /* Number of allocated entries */
|
|
} cmd_mm_struct_t;
|
|
|
|
#define CMD_MM_ENTRIES 3 /* command console memory and manual page manager */
|
|
#define CMD_MM_DEFAULT_ALLOC 4 /* Number of table entries to allocate per default */
|
|
|
|
#define CMD_MM_CMD 0 /* Commands */
|
|
#define CMD_MM_VAR 1 /* Variables */
|
|
#define CMD_MM_DOC 2 /* Misc. documentation */
|
|
|
|
static const char *cmd_mm_names[CMD_MM_ENTRIES] = {
|
|
"Commands",
|
|
"Variables",
|
|
"Documentation"
|
|
};
|
|
static size_t cmd_mm_sizes_per_entry[CMD_MM_ENTRIES] = {
|
|
sizeof(cmd_command_t),
|
|
sizeof(cmd_var_t),
|
|
sizeof(cmd_page_t)
|
|
};
|
|
|
|
|
|
static void _cmd_print_command(cmd_mm_entry_t *data, int full);
|
|
static void _cmd_print_var(cmd_mm_entry_t *data, int full);
|
|
static void _cmd_print_page(cmd_mm_entry_t *data, int full);
|
|
|
|
static printfunc_t *cmd_mm_printers[CMD_MM_ENTRIES] = {
|
|
_cmd_print_command,
|
|
_cmd_print_var,
|
|
_cmd_print_page
|
|
};
|
|
|
|
static cmd_mm_struct_t cmd_mm[CMD_MM_ENTRIES];
|
|
|
|
static int _cmd_initialized = 0;
|
|
static int _lists_need_sorting = 0;
|
|
|
|
unsigned int cmd_paramlength;
|
|
cmd_param_t *cmd_params;
|
|
|
|
|
|
/********** dmalloc functions **********/
|
|
|
|
#ifdef WITH_DMALLOC
|
|
|
|
int
|
|
c_dm_stats(state_t * s) {
|
|
dmalloc_log_stats();
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
c_dm_log_unfreed(state_t * s) {
|
|
dmalloc_log_unfreed();
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
c_dm_verify(state_t * s) {
|
|
unsigned long pointer_var;
|
|
void *ptr;
|
|
|
|
pointer_var = strtoul(cmd_params[0].str, NULL, 0);
|
|
ptr = (void *) pointer_var;
|
|
|
|
dmalloc_verify(ptr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
c_dm_debug(state_t * s) {
|
|
if (cmd_paramlength) {
|
|
long newval = strtol(cmd_params[0].str, NULL, 0);
|
|
|
|
sciprintf("Setting dmalloc_debug(%ld)\n", newval);
|
|
dmalloc_debug(newval);
|
|
} else
|
|
sciprintf("dmalloc_debug is at 0x%lx\n", dmalloc_debug_current());
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
c_dm_mark(state_t * s) {
|
|
unsigned long mark = dmalloc_mark();
|
|
|
|
dmalloc_message("------------- MARK 0x%lx ---------------\n", mark);
|
|
sciprintf("mark 0x%lx\n", mark);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
c_dm_chmark(state_t * s) {
|
|
unsigned long mark = strtoul(cmd_params[0].str, NULL, 0);
|
|
sciprintf("Checking mark 0x%lx\n", mark);
|
|
dmalloc_message("--- Mark 0x%lx:\n", mark);
|
|
dmalloc_log_changed(mark, 1, 1, 1);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
c_dm_print(state_t * s) {
|
|
int i;
|
|
for (i = 0; i < cmd_paramlength; i++)
|
|
dmalloc_message("%s\n", cmd_params[i].str);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
con_init_dmalloc() {
|
|
con_hook_command(c_dm_stats, "dm_stats", "",
|
|
"Prints memory usage stats\n to the dmalloc output file\n\n dm_stats");
|
|
con_hook_command(c_dm_log_unfreed, "dm_log_unfreed", "",
|
|
"Prints unfreed pointer\n information to the dmalloc\n output file\n\n"
|
|
"USAGE\n\n dm_log_unfreed");
|
|
con_hook_command(c_dm_verify, "dm_verify", "s",
|
|
"Verifies one pointer,\n prints output to dmalloc file\n\nUSAGE\n\n"
|
|
" dm_verify <ptr>\n dm_verify 0\n\n 'dm_verify 0' will verify\n ALL current pointers.\n");
|
|
con_hook_command(c_dm_debug, "dm_debug", "s*",
|
|
"Sets the dmalloc debug\n state or displays it\n\nUSAGE\n\n dm_debug <mode>\n dm_debug");
|
|
con_hook_command(c_dm_mark, "dm_mark", "",
|
|
"Gets a mark describing\n the current heap state\n\nUSAGE\n\n dm_mark\n\n"
|
|
" The mark is written to the\n dmalloc output file and\n to sci output.\n\nSEE ALSO\n\n cm_chmark");
|
|
con_hook_command(c_dm_chmark, "dm_chmark", "s",
|
|
"Checks changes in the\n heap state since a certain\n mark was retrieved\n\n"
|
|
"USAGE\n\n c_dm_chmark <mark>\n\n Output is written to the\n dmalloc output file.\n\n Use dm_mark to retrieve a\n"
|
|
" mark.\n\nSEE ALSO\n\n c_dm_mark");
|
|
con_hook_command(c_dm_print, "dm_print", "s*",
|
|
"Prints something to the\n dmalloc output.\n\nUSAGE\n\n dm_print <text>");
|
|
}
|
|
#else /* WITH_DMALLOC */
|
|
|
|
void
|
|
con_init_dmalloc(void) {
|
|
}
|
|
|
|
#endif /* WITH_DMALLOC */
|
|
|
|
|
|
void
|
|
_cmd_exit(void) {
|
|
int t;
|
|
|
|
for (t = 0; t < CMD_MM_ENTRIES; t++)
|
|
free(cmd_mm[t].data);
|
|
}
|
|
|
|
static cmd_mm_entry_t *
|
|
cmd_mm_find(char *name, int type) {
|
|
int i;
|
|
|
|
for (i = 0; i < cmd_mm[type].entries; i++)
|
|
if (!strcmp(((cmd_mm_entry_t *)((byte *)cmd_mm[type].data + i * cmd_mm[type].size_per_entry))->name, name))
|
|
return ((cmd_mm_entry_t *)((byte *)cmd_mm[type].data + i * cmd_mm[type].size_per_entry));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
_cmd_mm_comp(const void *a, const void *b) {
|
|
return strcmp(((cmd_mm_entry_t *) a)->name, ((cmd_mm_entry_t *) b)->name);
|
|
}
|
|
|
|
void
|
|
con_sort_all(void) {
|
|
int i;
|
|
|
|
for (i = 0; i < CMD_MM_ENTRIES; i++)
|
|
if (cmd_mm[i].entries && _lists_need_sorting & (1 << i))
|
|
qsort(cmd_mm[i].data, cmd_mm[i].entries, cmd_mm[i].size_per_entry,
|
|
_cmd_mm_comp);
|
|
|
|
_lists_need_sorting = 0;
|
|
}
|
|
|
|
void
|
|
con_init(void) {
|
|
if (!_cmd_initialized) {
|
|
int i;
|
|
|
|
_cmd_initialized = 1;
|
|
for (i = 0; i < CMD_MM_ENTRIES; i++) {
|
|
cmd_mm[i].name = cmd_mm_names[i];
|
|
cmd_mm[i].size_per_entry = cmd_mm_sizes_per_entry[i];
|
|
cmd_mm[i].entries = 0;
|
|
cmd_mm[i].allocated = CMD_MM_DEFAULT_ALLOC;
|
|
cmd_mm[i].data = sci_calloc(cmd_mm[i].allocated, cmd_mm[i].size_per_entry);
|
|
cmd_mm[i].print = cmd_mm_printers[i];
|
|
}
|
|
|
|
atexit(_cmd_exit);
|
|
|
|
/* Hook up some commands */
|
|
con_hook_command(&c_version, "version", "",
|
|
"Displays the version number");
|
|
con_hook_command(&c_list, "list", "s*",
|
|
"Lists various things (try 'list')");
|
|
con_hook_command(&c_man, "man", "s",
|
|
"Gives a short description of something");
|
|
con_hook_command(&c_print, "print", "s", "Prints an int variable");
|
|
con_hook_command(&c_set, "set", "si", "Sets an int variable");
|
|
con_hook_command(&c_size, "size", "si",
|
|
"Displays the size of a resource");
|
|
con_hook_command(&c_dump, "dump", "si", "HexDumps a resource");
|
|
con_hook_command(&c_hexgrep, "hexgrep", "shh*",
|
|
"Searches some resources for a\n"
|
|
" particular sequence of bytes, re-\n presented"
|
|
" as hexadecimal numbers.\n\n"
|
|
"EXAMPLES:\n hexgrep script e8 03 c8 00\n"
|
|
" hexgrep pic.042 fe");
|
|
con_hook_command(&c_dissectscript, "dissectscript", "i",
|
|
"Examines a script.");
|
|
|
|
con_hook_page("addresses",
|
|
"Passing address parameters\n\n"
|
|
" Address parameters may be passed in one of\n"
|
|
" three forms:\n"
|
|
" - ssss:oooo -- where 'ssss' denotes a\n"
|
|
" segment and 'oooo' an offset. Example:\n"
|
|
" \"a:c5\" would address something in seg-\n"
|
|
" ment 0xa at offset 0xc5.\n"
|
|
" - &scr:oooo -- where 'scr' is a script number\n"
|
|
" and oooo an offset within that script; will\n"
|
|
" fail if the script is not currently loaded\n"
|
|
" - $REG -- where 'REG' is one of 'PC', 'ACC',\n"
|
|
" 'PREV' or 'OBJ': References the address\n"
|
|
" indicated by the register of this name.\n"
|
|
" - $REG+n (or -n) -- Like $REG, but modifies\n"
|
|
" the offset part by a specific amount (which\n"
|
|
" is specified in hexadecimal).\n"
|
|
" - ?obj -- Looks up an object with the specified\n"
|
|
" name, uses its address. This will abort if\n"
|
|
" the object name is ambiguous; in that case,\n"
|
|
" a list of addresses and indices is provided.\n"
|
|
" ?obj.idx may be used to disambiguate 'obj'\n"
|
|
" by the index 'idx'.\n");
|
|
|
|
con_init_dmalloc();
|
|
|
|
con_hook_int(&con_passthrough, "con_passthrough",
|
|
"scicon->stdout passthrough");
|
|
}
|
|
}
|
|
|
|
static inline int
|
|
clone_is_used(clone_table_t *t, int idx) {
|
|
return ENTRY_IS_VALID(t, idx);
|
|
}
|
|
|
|
int
|
|
parse_reg_t(state_t *s, const char *str, reg_t *dest) { /* Returns 0 on success */
|
|
int rel_offsetting = 0;
|
|
const char *offsetting = NULL;
|
|
/* Non-NULL: Parse end of string for relative offsets */
|
|
char *endptr;
|
|
|
|
if (!s) {
|
|
sciprintf("Addresses can only be parsed if a global state is present");
|
|
return 1; /* Requires a valid state */
|
|
}
|
|
|
|
if (*str == '$') { /* Register */
|
|
rel_offsetting = 1;
|
|
|
|
if (!strncasecmp(str + 1, "PC", 2)) {
|
|
*dest = s->execution_stack[s->execution_stack_pos].addr.pc;
|
|
offsetting = str + 3;
|
|
} else if (!strncasecmp(str + 1, "P", 1)) {
|
|
*dest = s->execution_stack[s->execution_stack_pos].addr.pc;
|
|
offsetting = str + 2;
|
|
} else if (!strncasecmp(str + 1, "PREV", 4)) {
|
|
*dest = s->r_prev;
|
|
offsetting = str + 5;
|
|
} else if (!strncasecmp(str + 1, "ACC", 3)) {
|
|
*dest = s->r_acc;
|
|
offsetting = str + 4;
|
|
} else if (!strncasecmp(str + 1, "A", 1)) {
|
|
*dest = s->r_acc;
|
|
offsetting = str + 2;
|
|
} else if (!strncasecmp(str + 1, "OBJ", 3)) {
|
|
*dest = s->execution_stack[s->execution_stack_pos].objp;
|
|
offsetting = str + 4;
|
|
} else if (!strncasecmp(str + 1, "O", 1)) {
|
|
*dest = s->execution_stack[s->execution_stack_pos].objp;
|
|
offsetting = str + 2;
|
|
} else return 1; /* No matching register */
|
|
|
|
if (!*offsetting)
|
|
offsetting = NULL;
|
|
else if (*offsetting != '+' && *offsetting != '-')
|
|
return 1;
|
|
} else if (*str == '&') {
|
|
int script_nr;
|
|
/* Look up by script ID */
|
|
char *colon = (char *)strchr(str, ':');
|
|
|
|
if (!colon)
|
|
return 1;
|
|
*colon = 0;
|
|
offsetting = colon + 1;
|
|
|
|
script_nr = strtol(str + 1, &endptr, 10);
|
|
|
|
if (*endptr)
|
|
return 1;
|
|
|
|
dest->segment = sm_seg_get(&s->seg_manager, script_nr);
|
|
|
|
if (!dest->segment) {
|
|
return 1;
|
|
}
|
|
} else if (*str == '?') {
|
|
int index = -1;
|
|
int times_found = 0;
|
|
char *tmp;
|
|
const char *str_objname;
|
|
char *str_suffix;
|
|
char suffchar = 0; /* Supress spurious -Wall warning */
|
|
int i;
|
|
/* Parse obj by name */
|
|
|
|
tmp = (char *)strchr(str, '+');
|
|
str_suffix = (char *)strchr(str, '-');
|
|
if (tmp < str_suffix)
|
|
str_suffix = tmp;
|
|
if (str_suffix) {
|
|
suffchar = (*str_suffix);
|
|
*str_suffix = 0;
|
|
}
|
|
|
|
tmp = (char *)strchr(str, '.');
|
|
|
|
if (tmp) {
|
|
*tmp = 0;
|
|
index = strtol(tmp + 1, &endptr, 16);
|
|
if (*endptr)
|
|
return -1;
|
|
}
|
|
|
|
str_objname = str + 1;
|
|
|
|
/* Now all values are available; iterate over all objects. */
|
|
for (i = 0; i < s->seg_manager.heap_size; i++) {
|
|
mem_obj_t *mobj = s->seg_manager.heap[i];
|
|
int idx = 0;
|
|
int max_index = 0;
|
|
|
|
if (mobj) {
|
|
if (mobj->type == MEM_OBJ_SCRIPT)
|
|
max_index = mobj->data.script.objects_nr;
|
|
else if (mobj->type == MEM_OBJ_CLONES)
|
|
max_index = mobj->data.clones.max_entry;
|
|
}
|
|
|
|
while (idx < max_index) {
|
|
int valid = 1;
|
|
object_t *obj = NULL; /* Surpress spurious warning */
|
|
reg_t objpos;
|
|
objpos.offset = 0;
|
|
objpos.segment = i;
|
|
|
|
if (mobj->type == MEM_OBJ_SCRIPT) {
|
|
obj = mobj->data.script.objects + idx;
|
|
objpos.offset = obj->pos.offset;
|
|
} else if (mobj->type == MEM_OBJ_CLONES) {
|
|
obj = &(mobj->data.clones.table[idx].entry);
|
|
objpos.offset = idx;
|
|
valid = clone_is_used(&mobj->data.clones, idx);
|
|
}
|
|
|
|
if (valid) {
|
|
char *objname = (char *) obj->base
|
|
+ obj->variables[SCRIPT_NAME_SELECTOR].offset;
|
|
if (!strcmp(objname, str_objname)) {
|
|
/* Found a match! */
|
|
if (index < 0 ||
|
|
times_found == index)
|
|
*dest = objpos;
|
|
else if (times_found < 0 && index) {
|
|
|
|
if (index == 1) {
|
|
/* First time we realized
|
|
** the ambiguity */
|
|
sciprintf("Ambiguous:\n");
|
|
sciprintf(" %3x: ["PREG"] %s\n", 0, PRINT_REG(*dest), str_objname);
|
|
}
|
|
sciprintf(" %3x: ["PREG"] %s\n", index, PRINT_REG(objpos), str_objname);
|
|
}
|
|
++times_found;
|
|
}
|
|
}
|
|
|
|
++idx;
|
|
}
|
|
|
|
}
|
|
|
|
if (!times_found)
|
|
return 1;
|
|
|
|
if (times_found > 1
|
|
&& index < 0) {
|
|
sciprintf("Ambiguous: Aborting.\n");
|
|
return 1; /* Ambiguous */
|
|
}
|
|
|
|
if (times_found <= index)
|
|
return 1; /* Not found */
|
|
|
|
offsetting = str_suffix;
|
|
if (offsetting)
|
|
*str_suffix = suffchar;
|
|
rel_offsetting = 1;
|
|
} else {
|
|
char *colon = (char *)strchr(str, ':');
|
|
|
|
if (!colon) {
|
|
offsetting = str;
|
|
dest->segment = 0;
|
|
} else {
|
|
*colon = 0;
|
|
offsetting = colon + 1;
|
|
|
|
dest->segment = strtol(str, &endptr, 16);
|
|
if (*endptr)
|
|
return 1;
|
|
}
|
|
}
|
|
if (offsetting) {
|
|
int val = strtol(offsetting, &endptr, 16);
|
|
|
|
if (rel_offsetting)
|
|
dest->offset += val;
|
|
else
|
|
dest->offset = val;
|
|
|
|
if (*endptr)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
con_parse(state_t *s, const char *command) {
|
|
int quote = 0; /* quoting? */
|
|
int done = 0; /* are we done yet? */
|
|
int cdone = 0; /* Done with the current command? */
|
|
const char *paramt; /* parameter types */
|
|
char *cmd = (command && command[0]) ? (char *) sci_strdup(command) :
|
|
(char *) sci_strdup(" ");
|
|
char *_cmd = cmd;
|
|
int pos = 0;
|
|
|
|
if (!_cmd_initialized)
|
|
con_init();
|
|
|
|
while (!done) {
|
|
cmd_command_t *command_todo;
|
|
int onvar = 1; /* currently working on a variable? */
|
|
unsigned int parammem = 0;
|
|
unsigned int i;
|
|
cdone = 0;
|
|
pos = 0;
|
|
|
|
/* cmd_params = sci_realloc(cmd_params, parammem); */
|
|
cmd_paramlength = 0;
|
|
|
|
while (*cmd == ' ')
|
|
cmd++;
|
|
|
|
while (!cdone) {
|
|
switch (cmd[pos]) {
|
|
case 0:
|
|
done = 1;
|
|
case ';':
|
|
if (!quote)
|
|
cdone = 1;
|
|
case ' ':
|
|
if (!quote)
|
|
cmd[pos] = onvar = 0;
|
|
break;
|
|
case '\\': /* don't check next char for special meaning */
|
|
memmove(cmd + pos, cmd + pos + 1, strlen(cmd + pos) - 1);
|
|
break;
|
|
case '"':
|
|
quote ^= 1;
|
|
memmove(cmd + pos, cmd + pos + 1, strlen(cmd + pos));
|
|
pos--;
|
|
break;
|
|
default:
|
|
if (!onvar) {
|
|
onvar = 1;
|
|
if (cmd_paramlength == parammem)
|
|
cmd_params = (cmd_param_t*)sci_realloc(cmd_params,
|
|
sizeof(cmd_param_t)
|
|
* (parammem += 8));
|
|
|
|
cmd_params[cmd_paramlength].str = cmd + pos;
|
|
|
|
cmd_paramlength++;
|
|
}
|
|
break;
|
|
}
|
|
pos++;
|
|
}
|
|
|
|
if (quote)
|
|
sciprintf("unbalanced quotes\n");
|
|
else if (strcmp(cmd, "") != 0) {
|
|
|
|
command_todo = (cmd_command_t *) cmd_mm_find(cmd, CMD_MM_CMD);
|
|
|
|
if (!command_todo)
|
|
sciprintf("%s: not found\n", cmd);
|
|
else {
|
|
unsigned int minparams;
|
|
int need_state = 0;
|
|
|
|
paramt = command_todo->param;
|
|
if (command_todo->param[0] == '!') {
|
|
need_state = 1;
|
|
paramt++;
|
|
}
|
|
|
|
minparams = strlen(paramt);
|
|
|
|
if ((paramt[0] != 0) && (paramt[strlen(paramt) - 1] == '*'))
|
|
minparams -= 2;
|
|
|
|
if (cmd_paramlength < minparams)
|
|
sciprintf("%s: needs more than %d parameters\n",
|
|
cmd, cmd_paramlength);
|
|
|
|
else if ((cmd_paramlength > strlen(paramt))
|
|
&& ((strlen(paramt) == 0)
|
|
|| paramt[strlen(paramt) - 1] != '*'))
|
|
sciprintf("%s: too many parameters", cmd);
|
|
else {
|
|
int do_execute = !need_state || s; /* /me wants an
|
|
** implication arrow */
|
|
char paramtype;
|
|
int paramtypepos = 0;
|
|
char *endptr;
|
|
|
|
for (i = 0; i < cmd_paramlength; i++) {
|
|
paramtype = paramt[paramtypepos];
|
|
|
|
if ((paramt[paramtypepos + 1])
|
|
&& (paramt[paramtypepos + 1] != '*'))
|
|
paramtypepos++;
|
|
/* seek next param type unless end of string or '* ' */
|
|
|
|
switch (paramtype) {
|
|
/* Now turn the parameters into variables of the appropriate types,
|
|
** unless they're strings, and store them in the global cmd_params[]
|
|
** structure */
|
|
|
|
case 'a': {
|
|
char *oldname = cmd_params[i].str;
|
|
if (parse_reg_t(s, oldname,
|
|
&(cmd_params[i].reg))) {
|
|
sciprintf("%s: '%s' is not an address or object\n", cmd, oldname);
|
|
do_execute = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'i': {
|
|
char *orgstr = cmd_params[i].str;
|
|
|
|
cmd_params[i].val = strtol(orgstr, &endptr, 0);
|
|
if (*endptr != '\0') {
|
|
do_execute = 0;
|
|
sciprintf("%s: '%s' is not an int\n", cmd, orgstr);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'h': {
|
|
char *orgstr = cmd_params[i].str;
|
|
|
|
cmd_params[i].val = strtol(orgstr, &endptr, 16);
|
|
|
|
if (*endptr != '\0') {
|
|
do_execute = 0;
|
|
sciprintf("%s: '%s' is not a hex number\n", cmd, orgstr);
|
|
}
|
|
|
|
cmd_params[i].val &= 0xff; /* Clip hex numbers to 0x00 ... 0xff */
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "Internal error: Heap corruption or prior assertion failed:\n"
|
|
"Unknown parameter type '%c' for funtion\n", paramtype);
|
|
|
|
}
|
|
}
|
|
|
|
if (do_execute) {
|
|
command_todo->command(s);
|
|
} else fprintf(stderr, "Skipping command...\n");
|
|
}
|
|
}
|
|
}
|
|
cmd += pos;
|
|
}
|
|
|
|
free(_cmd);
|
|
if (cmd_params)
|
|
free(cmd_params);
|
|
cmd_params = NULL;
|
|
|
|
}
|
|
|
|
/* (unused)
|
|
static cmd_mm_entry_t *
|
|
con_iterate_entry(int ID, int *counter)
|
|
{
|
|
byte *retval;
|
|
con_init();
|
|
|
|
if (*counter >= cmd_mm[ID].entries)
|
|
return 0;
|
|
retval = cmd_mm[ID].data;
|
|
retval += (*counter) * cmd_mm[ID].size_per_entry;
|
|
|
|
(*counter)++;
|
|
|
|
return (cmd_mm_entry_t *) retval;
|
|
}
|
|
*/
|
|
|
|
static cmd_mm_entry_t *
|
|
con_alloc_page_entry(int ID) {
|
|
int entry;
|
|
con_init();
|
|
|
|
if (cmd_mm[ID].entries >= cmd_mm[ID].allocated) {
|
|
int nextsize = cmd_mm[ID].allocated;
|
|
if (nextsize >= 64)
|
|
nextsize += 16;
|
|
else
|
|
nextsize <<= 1;
|
|
|
|
cmd_mm[ID].data = sci_realloc(cmd_mm[ID].data,
|
|
nextsize * cmd_mm[ID].size_per_entry);
|
|
cmd_mm[ID].allocated = nextsize;
|
|
}
|
|
|
|
_lists_need_sorting |= (1 << ID);
|
|
|
|
entry = cmd_mm[ID].entries++;
|
|
return (cmd_mm_entry_t *)(((byte *)cmd_mm[ID].data)
|
|
+ entry * cmd_mm[ID].size_per_entry);
|
|
}
|
|
|
|
int
|
|
con_hook_page(const char *name, const char *body) {
|
|
cmd_page_t *page = (cmd_page_t *) con_alloc_page_entry(CMD_MM_DOC);
|
|
|
|
page->name = name;
|
|
page->description = body;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
con_hook_command(int command(state_t *), const char *name, const char *param,
|
|
const char *description) {
|
|
cmd_command_t *cmd = NULL;
|
|
unsigned int i;
|
|
|
|
|
|
if (NULL == name) {
|
|
sciprintf("console.c: con_hook_command(): NULL passed for name\n");
|
|
return -1;
|
|
}
|
|
|
|
if (command == NULL)
|
|
return 1;
|
|
|
|
if (param == NULL)
|
|
param = "";
|
|
|
|
if (description == NULL)
|
|
description = "";
|
|
|
|
i = 0;
|
|
while (param[i] != 0) {
|
|
switch (param[i]) {
|
|
case '*':
|
|
if (param[i + 1] != 0)
|
|
return 1;
|
|
if (i == 0)
|
|
return 1;
|
|
case 'h':
|
|
case '!':
|
|
case 'i':
|
|
case 'a':
|
|
case 's':
|
|
case 'r':
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
i++;
|
|
}
|
|
cmd = (cmd_command_t *) con_alloc_page_entry(CMD_MM_CMD);
|
|
|
|
cmd->command = command;
|
|
cmd->name = name;
|
|
cmd->param = param;
|
|
cmd->description = description;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
con_hook_int(int *pointer, const char *name, const char *description) {
|
|
cmd_var_t *var;
|
|
|
|
if (pointer == NULL)
|
|
return 1;
|
|
|
|
if (description == NULL)
|
|
description = "";
|
|
|
|
var = (cmd_var_t *) con_alloc_page_entry(CMD_MM_VAR);
|
|
|
|
var->var.intp = pointer;
|
|
var->name = name;
|
|
var->description = description;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* Console commands and support functions
|
|
***************************************************************************/
|
|
|
|
|
|
static int
|
|
get_resource_number(char *resid)
|
|
/* Gets the resource number of a resource string, or returns -1 */
|
|
{
|
|
int i, res = -1;
|
|
|
|
for (i = 0; i < sci_invalid_resource; i++)
|
|
if (strcmp(sci_resource_types[i], resid) == 0)
|
|
res = i;
|
|
return res;
|
|
}
|
|
|
|
static int
|
|
c_version(state_t * s) {
|
|
if (NULL == s) {
|
|
sciprintf("console.c: c_version: NULL passed for parameter s\n");
|
|
return -1;
|
|
}
|
|
|
|
sciprintf("FreeSCI, version " VERSION "\n");
|
|
sciprintf("Resource file version: %s\n", sci_version_types[s->resmgr->sci_version]);
|
|
sciprintf("Emulated interpreter version: %d.%03d.%03d\n",
|
|
SCI_VERSION_MAJOR(s->version),
|
|
SCI_VERSION_MINOR(s->version),
|
|
SCI_VERSION_PATCHLEVEL(s->version));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
c_list_words(state_t *s) {
|
|
word_t **words;
|
|
int words_nr;
|
|
int i;
|
|
|
|
words = vocab_get_words(s->resmgr, &words_nr);
|
|
|
|
if (!words) {
|
|
sciprintf("No vocabulary.\n");
|
|
return 1;
|
|
}
|
|
|
|
for (i = 0; i < words_nr; i++)
|
|
sciprintf("%4d: %03x [%03x] %s\n",
|
|
i,
|
|
words[i]->w_class,
|
|
words[i]->group,
|
|
words[i]->word);
|
|
|
|
vocab_free_words(words, words_nr);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
c_list_suffices(state_t *s) {
|
|
suffix_t **suffices;
|
|
int suffices_nr;
|
|
int i;
|
|
char word_buf[256], alt_buf[256];
|
|
|
|
suffices = vocab_get_suffices(s->resmgr, &suffices_nr);
|
|
|
|
if (!suffices) {
|
|
sciprintf("No suffix vocabulary.\n");
|
|
return 1;
|
|
}
|
|
|
|
for (i = 0; i < suffices_nr; i++) {
|
|
suffix_t *suf = suffices[i];
|
|
|
|
strncpy(word_buf, suf->word_suffix,
|
|
suf->word_suffix_length);
|
|
word_buf[suf->word_suffix_length] = 0;
|
|
strncpy(alt_buf, suf->alt_suffix,
|
|
suf->alt_suffix_length);
|
|
alt_buf[suf->alt_suffix_length] = 0;
|
|
|
|
sciprintf("%4d: (%03x) -%12s => -%12s (%03x)\n",
|
|
i, suf->class_mask, word_buf,
|
|
alt_buf, suf->result_class);
|
|
}
|
|
|
|
vocab_free_suffices(s->resmgr, suffices, suffices_nr);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
_cmd_print_command(cmd_mm_entry_t *data, int full) {
|
|
const char *paramseeker = ((cmd_command_t *) data)->param;
|
|
|
|
if (full) {
|
|
sciprintf("SYNOPSIS\n\n %s ", data->name, paramseeker);
|
|
|
|
while (*paramseeker) {
|
|
switch (*paramseeker) {
|
|
case '!':
|
|
break;
|
|
case 'i':
|
|
sciprintf(" (int)");
|
|
break;
|
|
case 'a':
|
|
sciprintf(" (addr)");
|
|
break;
|
|
case 's':
|
|
sciprintf(" (string)");
|
|
break;
|
|
case 'h':
|
|
sciprintf(" (hexbyte)");
|
|
break;
|
|
case '*':
|
|
sciprintf("*");
|
|
break;
|
|
default:
|
|
sciprintf(" (Unknown(%c))", *paramseeker);
|
|
}
|
|
paramseeker++;
|
|
}
|
|
|
|
sciprintf("\n\nDESCRIPTION\n\n %s",
|
|
data->description);
|
|
} else
|
|
sciprintf(" %s", data->name);
|
|
}
|
|
|
|
static void
|
|
_cmd_print_var(cmd_mm_entry_t *data, int full) {
|
|
cmd_var_t *var = (cmd_var_t *) data;
|
|
if (full)
|
|
sciprintf("VALUE\n\n");
|
|
sciprintf(" %s = %d\n", var->name, *(var->var.intp));
|
|
|
|
if (full)
|
|
sciprintf("\n\nDESCRIPTION\n\n %s",
|
|
data->description);
|
|
}
|
|
|
|
static void
|
|
_cmd_print_page(cmd_mm_entry_t *data, int full) {
|
|
if (full)
|
|
sciprintf("\n\nDESCRIPTION\n\n %s\n",
|
|
data->description);
|
|
else sciprintf("%s\n", data->name);
|
|
}
|
|
|
|
static int
|
|
c_list(state_t * s) {
|
|
if (_lists_need_sorting)
|
|
con_sort_all();
|
|
|
|
if (cmd_paramlength == 0) {
|
|
sciprintf("usage: list [type]\nwhere type is one of the following:\n"
|
|
"cmds - lists all commands\n"
|
|
"vars - lists all variables\n"
|
|
"docs - lists all misc. documentation\n"
|
|
"\n"
|
|
"restypes - lists all resource types\n"
|
|
"selectors - lists all selectors\n"
|
|
"syscalls - lists all kernel functions\n"
|
|
"words - lists all kernel words\n"
|
|
"suffixes - lists all suffix replacements\n"
|
|
"[resource] - lists all [resource]s");
|
|
} else if (cmd_paramlength == 1) {
|
|
const char *mm_subsects[3] = {"cmds", "vars", "docs"};
|
|
int mm_found = -1;
|
|
int i;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
if (mm_subsects[i] && !strcmp(mm_subsects[i], cmd_params[0].str))
|
|
mm_found = i;
|
|
|
|
if (mm_found >= 0)
|
|
for (i = 0; i < cmd_mm[mm_found].entries; i++)
|
|
cmd_mm[mm_found].print((cmd_mm_entry_t *)
|
|
(((byte *)cmd_mm[mm_found].data)
|
|
+ i * cmd_mm[mm_found].size_per_entry), 0);
|
|
else {
|
|
if (!s) {
|
|
sciprintf("You need a state to do that!\n");
|
|
return 1;
|
|
}
|
|
|
|
if (!strcmp("selectors", cmd_params[0].str))
|
|
return c_selectornames(s);
|
|
else if (!strcmp("syscalls", cmd_params[0].str))
|
|
return c_kernelnames(s);
|
|
else if (!strcmp("suffixes", cmd_params[0].str)
|
|
|| !strcmp("suffices", cmd_params[0].str)
|
|
|| !strcmp("sufficos", cmd_params[0].str))
|
|
/* sufficos: Accusative Plural of 'suffix' */
|
|
return c_list_suffices(s);
|
|
else if (!strcmp("words", cmd_params[0].str))
|
|
return c_list_words(s);
|
|
else if (strcmp("restypes", cmd_params[0].str) == 0) {
|
|
for (i = 0; i < sci_invalid_resource; i++)
|
|
sciprintf("%s\n", sci_resource_types[i]);
|
|
} else {
|
|
int res = get_resource_number(cmd_params[0].str);
|
|
if (res == -1)
|
|
sciprintf("Unknown resource type: '%s'\n", cmd_params[0].str);
|
|
else {
|
|
for (i = 0; i < sci_max_resource_nr[s->resmgr->sci_version]; i++)
|
|
if (scir_test_resource(s->resmgr, res, i))
|
|
sciprintf("%s.%03d\n", sci_resource_types[res], i);
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
sciprintf("list can only be used with one argument");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
c_man(state_t * s) {
|
|
int section = 0;
|
|
unsigned int i;
|
|
char *name = cmd_params[0].str;
|
|
char *c = strchr(name, '.');
|
|
cmd_mm_entry_t *entry = 0;
|
|
|
|
if (c) {
|
|
*c = 0;
|
|
section = atoi(c + 1);
|
|
}
|
|
|
|
if (section < 0 || section >= CMD_MM_ENTRIES) {
|
|
sciprintf("Invalid section %d\n",
|
|
section);
|
|
return 1;
|
|
}
|
|
|
|
sciprintf("section:%d\n", section);
|
|
if (section)
|
|
entry = cmd_mm_find(name, section - 1);
|
|
else
|
|
for (i = 0; i < CMD_MM_ENTRIES && !section; i++) {
|
|
if ((entry = cmd_mm_find(name, i)))
|
|
section = i + 1;
|
|
}
|
|
|
|
if (!entry) {
|
|
sciprintf("No manual entry\n");
|
|
return 1;
|
|
}
|
|
|
|
sciprintf("-- %s: %s.%d\n", cmd_mm[section - 1].name, name, section);
|
|
cmd_mm[section - 1].print(entry, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
c_set(state_t * s) {
|
|
cmd_var_t *var = (cmd_var_t *) cmd_mm_find(cmd_params[0].str, CMD_MM_VAR);
|
|
|
|
if (var)
|
|
*(var->var.intp) = cmd_params[1].val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
c_print(state_t * s) {
|
|
cmd_var_t *var = (cmd_var_t *) cmd_mm_find(cmd_params[0].str, CMD_MM_VAR);
|
|
|
|
if (var)
|
|
sciprintf("%d", *(var->var.intp));
|
|
else
|
|
sciprintf("Not defined.");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
c_size(state_t * s) {
|
|
int res = get_resource_number(cmd_params[0].str);
|
|
if (res == -1)
|
|
sciprintf("Resource type '%s' is not valid\n", cmd_params[0].str);
|
|
else {
|
|
resource_t *resource = scir_find_resource(s->resmgr, res, cmd_params[1].val, 0);
|
|
if (resource) {
|
|
sciprintf("Size: %d\n", resource->size);
|
|
} else
|
|
sciprintf("Resource %s.%03d not found\n", cmd_params[0].str,
|
|
cmd_params[1].val);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
c_dump(state_t * s) {
|
|
int res = get_resource_number(cmd_params[0].str);
|
|
|
|
if (res == -1)
|
|
sciprintf("Resource type '%s' is not valid\n", cmd_params[0].str);
|
|
else {
|
|
resource_t *resource = scir_find_resource(s->resmgr, res, cmd_params[1].val, 0);
|
|
if (resource)
|
|
sci_hexdump(resource->data, resource->size, 0);
|
|
else
|
|
sciprintf("Resource %s.%03d not found\n", cmd_params[0].str,
|
|
cmd_params[1].val);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
c_hexgrep(state_t * s) {
|
|
int i, seeklen, resnr, restype, resmax;
|
|
unsigned char *seekstr = NULL;
|
|
resource_t *script = NULL;
|
|
char *dot = strchr(cmd_params[0].str, '.');
|
|
|
|
if (NULL == s) {
|
|
fprintf(stderr, "console.c: c_hexgrep(): NULL passed for s\r\n");
|
|
return(-1);
|
|
}
|
|
|
|
seekstr = (unsigned char*)sci_malloc(seeklen = (cmd_paramlength - 1));
|
|
|
|
if (NULL == seekstr) {
|
|
fprintf(stderr, "console.c: c_hexgrep(): malloc failed for seekstr\r\n");
|
|
return(-1);
|
|
}
|
|
|
|
for (i = 0; i < seeklen; i++)
|
|
seekstr[i] = (byte)cmd_params[i + 1].val;
|
|
|
|
if (dot) {
|
|
*dot = 0;
|
|
resmax = resnr = atoi(dot + 1);
|
|
} else {
|
|
resnr = 0;
|
|
resmax = 999;
|
|
}
|
|
|
|
if ((restype = get_resource_number(cmd_params[0].str)) == -1) {
|
|
sciprintf("Unknown resource type \"%s\"\n", cmd_params[0].str);
|
|
free(seekstr);
|
|
return 1;
|
|
}
|
|
|
|
for (; resnr <= resmax; resnr++)
|
|
if ((script = scir_find_resource(s->resmgr, restype, resnr, 0))) {
|
|
unsigned int seeker = 0, seekerold = 0;
|
|
int comppos = 0;
|
|
int output_script_name = 0;
|
|
|
|
while (seeker < script->size) {
|
|
|
|
if (script->data[seeker] == seekstr[comppos]) {
|
|
if (comppos == 0)
|
|
seekerold = seeker;
|
|
|
|
comppos++;
|
|
|
|
if (comppos == seeklen) {
|
|
comppos = 0;
|
|
seeker = seekerold + 1;
|
|
|
|
if (!output_script_name) {
|
|
sciprintf("\nIn %s.%03d:\n", sci_resource_types[restype], resnr);
|
|
output_script_name = 1;
|
|
}
|
|
sciprintf(" 0x%04x\n", seekerold);
|
|
}
|
|
} else
|
|
comppos = 0;
|
|
|
|
seeker++;
|
|
}
|
|
}
|
|
|
|
free(seekstr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
c_selectornames(state_t * s) {
|
|
int namectr;
|
|
char **snames = NULL;
|
|
int seeker = 0;
|
|
|
|
if (NULL == s) {
|
|
sciprintf("console.c: c_selectornames(): NULL passed for parameter s\n");
|
|
return -1;
|
|
}
|
|
|
|
snames = vocabulary_get_snames(s->resmgr, &namectr, s ? s->version : 0);
|
|
|
|
if (!snames) {
|
|
sciprintf("No selector name table found!\n");
|
|
return 1;
|
|
}
|
|
|
|
sciprintf("Selector names in numeric order:\n");
|
|
while (snames[seeker]) {
|
|
sciprintf("%03x: %s\n", seeker, snames[seeker]);
|
|
seeker++;
|
|
}
|
|
vocabulary_free_snames(snames);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
c_kernelnames(state_t * s) {
|
|
int knamectr;
|
|
char **knames = vocabulary_get_knames(s->resmgr, &knamectr);
|
|
int seeker = 0;
|
|
|
|
if (NULL == s) {
|
|
sciprintf("console.c: c_kernelnames NULL passed for parameter s\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!knames) {
|
|
sciprintf("No kernel name table found!\n");
|
|
return 1;
|
|
}
|
|
|
|
sciprintf("Syscalls in numeric order:\n");
|
|
for (seeker = 0; seeker < knamectr; seeker++)
|
|
sciprintf("%03x: %s\n", seeker, knames[seeker]);
|
|
|
|
vocabulary_free_knames(knames);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
c_dissectscript(state_t * s) {
|
|
if (NULL == s) {
|
|
sciprintf("console.c: c_dissectscript(): NULL passed for parameter s\n");
|
|
return -1;
|
|
}
|
|
|
|
script_dissect(s->resmgr, cmd_params[0].val, s->selector_names, s->selector_names_nr);
|
|
return 0;
|
|
}
|
|
#endif /* SCI_CONSOLE */
|
|
|
|
|