mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-30 05:34:00 +00:00
e241843bec
- Mass rename .c to .cpp svn-id: r38227
717 lines
15 KiB
C++
717 lines
15 KiB
C++
#include <sciresource.h>
|
|
#include <console.h>
|
|
#include <script.h>
|
|
#include <vocabulary.h>
|
|
#include <old_objects.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <util.h>
|
|
#include <vm.h>
|
|
#include <assert.h>
|
|
|
|
#ifdef SCI_CONSOLE
|
|
#define printf sciprintf
|
|
/* Yeah, I shouldn't be doing this ;-) [CJR] */
|
|
#endif
|
|
|
|
FLEXARRAY_NOEXTRA(object*) fobjects;
|
|
|
|
static int knames_count;
|
|
static char** knames;
|
|
static char** snames;
|
|
static opcode* opcodes;
|
|
|
|
object **object_map, *object_root;
|
|
int max_object;
|
|
|
|
const char* globals[] = {
|
|
/*00*/
|
|
"ego",
|
|
"GAMEID",
|
|
"roomXX",
|
|
"speed",
|
|
/*04*/
|
|
"quitFlag",
|
|
"cast",
|
|
"regions",
|
|
"timer",
|
|
/*08*/
|
|
"sounds",
|
|
"inv",
|
|
"eventHandler",
|
|
"roomNumberExit",
|
|
/*0C*/
|
|
"previousRoomNumber",
|
|
"roomNumber",
|
|
"enterDebugModeOnRoomExit",
|
|
"score",
|
|
/*10*/
|
|
"maximumScore",
|
|
"11",
|
|
"speed",
|
|
"13",
|
|
/*14*/
|
|
"14",
|
|
"loadCursor",
|
|
"normalFont",
|
|
"restoreSaveFont", /*dialogFont*/
|
|
/*18*/
|
|
"18",
|
|
"19",
|
|
"defaultFont",
|
|
"1B",
|
|
/*1C*/
|
|
"pointerToVersionNumber",
|
|
"locales",
|
|
"pointerToSaveGameDirectory",
|
|
"1F"
|
|
};
|
|
|
|
static int add_object(object* obj)
|
|
{
|
|
FLEXARRAY_APPEND(object*, fobjects, obj, return 1);
|
|
return 0;
|
|
}
|
|
|
|
static void dump(byte* data, int len)
|
|
{
|
|
int i=0;
|
|
while(i<len)
|
|
{
|
|
printf("%02X ", data[i++]);
|
|
if(i%8==0) printf(" ");
|
|
if(i%16==0) printf("\n");
|
|
}
|
|
if(i%16) printf("\n");
|
|
}
|
|
|
|
static void printMethod(object* obj, int meth, int indent)
|
|
{
|
|
script_method* m=obj->methods[meth];
|
|
int i, j;
|
|
|
|
for(j=0; j<indent*2-1; j++) printf(" ");
|
|
printf("Method %s\n", snames[m->number]);
|
|
|
|
for(i=0; i<m->used; i++)
|
|
{
|
|
script_opcode op=m->data[i];
|
|
|
|
for(j=0; j<indent; j++) printf(" ");
|
|
printf("%s ", opcodes[op.opcode].name);
|
|
|
|
switch(op.opcode)
|
|
{
|
|
case 0x21: /*callk*/
|
|
{
|
|
if(op.arg1>knames_count) printf("<no such kernel function %02X> ", op.arg1);
|
|
else printf("%s ", knames[op.arg1]);
|
|
printf("%02X", op.arg2);
|
|
} break;
|
|
case 0x28: /*class*/
|
|
{
|
|
if(op.arg1>max_object) printf("<no such class %02X>", op.arg1);
|
|
else
|
|
{
|
|
/* [DJ] op.arg1+1 adjusts for the <root> object */
|
|
if(fobjects.data[op.arg1+1]==0) printf("<null object>");
|
|
else printf("%s", fobjects.data[op.arg1+1]->name);
|
|
}
|
|
} break;
|
|
case 0x44:
|
|
{
|
|
if(op.arg1>0x20) printf("<no such global %02X> ", op.arg1);
|
|
else printf("%s ", globals[op.arg1]);
|
|
} break;
|
|
default:
|
|
{
|
|
int args[3];
|
|
args[0]=op.arg1;
|
|
args[1]=op.arg2;
|
|
args[2]=op.arg3;
|
|
for(j=0; j<3; j++)
|
|
{
|
|
switch(formats[op.opcode][j])
|
|
{
|
|
case Script_Invalid:
|
|
{
|
|
printf("<invalid> ");
|
|
} break;
|
|
case Script_None:
|
|
{
|
|
j=3;
|
|
} break;
|
|
case Script_SByte:
|
|
case Script_Byte:
|
|
{
|
|
printf("%02X ", args[j]);
|
|
} break;
|
|
case Script_Word:
|
|
case Script_SVariable:
|
|
case Script_Variable:
|
|
case Script_SRelative:
|
|
case Script_Property:
|
|
case Script_Global:
|
|
case Script_Local:
|
|
case Script_Temp:
|
|
case Script_Param:
|
|
{
|
|
printf("%04X ", args[j]);
|
|
} break;
|
|
case Script_SWord:
|
|
{
|
|
if(args[j]<0) printf("-%04X", -args[j]);
|
|
else printf("%04X", args[j]);
|
|
} break;
|
|
case Script_End:
|
|
{
|
|
printf("\n");
|
|
return;
|
|
} break;
|
|
default:
|
|
{
|
|
printf("<unknown arg type %d> ", formats[op.opcode][j]);
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
static void printObject_r(object* obj, int flags, int level)
|
|
{
|
|
int i;
|
|
for(i=0; i<level; i++) printf(" ");
|
|
if(obj==0) printf("(null)\n");
|
|
else
|
|
{
|
|
printf("%s\n", obj->name);
|
|
if(flags&SCRIPT_PRINT_METHODS)
|
|
{
|
|
for(i=0; i<obj->method_count; i++)
|
|
{
|
|
printMethod(obj, i, level+1);
|
|
}
|
|
}
|
|
if(flags&SCRIPT_PRINT_CHILDREN)
|
|
{
|
|
for(i=0; i<obj->children.used; i++)
|
|
{
|
|
printObject_r(obj->children.data[i], flags, level+1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void printObject(object* obj, int flags)
|
|
{
|
|
printf("pO(%p, %d)\n", obj, flags);
|
|
printObject_r(obj, flags, 0);
|
|
}
|
|
|
|
static object* object_new()
|
|
{
|
|
object* obj= (object*)sci_malloc(sizeof(object));
|
|
if(obj==0) return 0;
|
|
|
|
obj->parent=0;
|
|
FLEXARRAY_INIT(object*, obj->children);
|
|
obj->name=0;
|
|
obj->selector_count=0;
|
|
obj->selector_numbers=0;
|
|
obj->methods=0;
|
|
obj->method_count=0;
|
|
|
|
return obj;
|
|
}
|
|
|
|
static int add_child(object* parent, object* child)
|
|
{
|
|
FLEXARRAY_APPEND(object*, parent->children, child, return 1);
|
|
return 0;
|
|
}
|
|
|
|
static object* fake_object(const char* reason)
|
|
{
|
|
object* obj=object_new();
|
|
if(obj==0)
|
|
{
|
|
#ifdef SCRIPT_DEBUG
|
|
printf("object_new failed during fake for %s\n", reason);
|
|
#endif
|
|
free(obj);
|
|
return 0;
|
|
}
|
|
if(add_child(object_root, obj))
|
|
{
|
|
#ifdef SCRIPT_DEBUG
|
|
printf("add_child failed during fake for %s\n", reason);
|
|
#endif
|
|
free(obj);
|
|
return 0;
|
|
}
|
|
obj->name=reason;
|
|
if(add_object(obj))
|
|
{
|
|
#ifdef SCRIPT_DEBUG
|
|
printf("add_object failed during fake for %s\n", reason);
|
|
#endif
|
|
/*FIXME: clean up parent*/
|
|
return 0;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
static script_method* decode_method(byte* data)
|
|
{
|
|
script_method* m;
|
|
int done=0;
|
|
int pos=0;
|
|
static int count=0;
|
|
|
|
count++;
|
|
|
|
if((m= (script_method*)sci_malloc(sizeof(script_method)))==0) return 0;
|
|
FLEXARRAY_INIT(script_opcode, *m);
|
|
|
|
while(!done)
|
|
{
|
|
int op=data[pos]>>1;
|
|
int size=2-(data[pos]&1);
|
|
int* args[3];
|
|
int arg;
|
|
int old_pos;
|
|
|
|
FLEXARRAY_ADD_SPACE(script_opcode, *m, 1, return 0);
|
|
old_pos=pos;
|
|
m->data[m->used-1].pos=pos;
|
|
m->data[m->used-1].opcode=op;
|
|
|
|
/*Copy the adresses of the args to an array for convenience*/
|
|
args[0]=&m->data[m->used-1].arg1;
|
|
args[1]=&m->data[m->used-1].arg2;
|
|
args[2]=&m->data[m->used-1].arg3;
|
|
|
|
/*Skip past the opcode*/
|
|
pos++;
|
|
|
|
for(arg=0; arg<4; arg++)
|
|
{
|
|
switch(formats[op][arg])
|
|
{
|
|
case Script_Invalid: /*Can't happen(tm)*/
|
|
{
|
|
int i;
|
|
printf("Invalid opcode %02X at %04X in method %d\n", op, pos, count);
|
|
for(i=m->used-9; i<m->used-1; i++)
|
|
{
|
|
printf("%s[%02X] ", opcodes[m->data[i].opcode].name, m->data[i].opcode);
|
|
dump(data+m->data[i].pos, m->data[i].size);
|
|
}
|
|
printf("Dump from %04X-%04X\n", pos-16, pos+16);
|
|
dump(data + pos - 16, 32);
|
|
} break;
|
|
case Script_None: /*No more args*/
|
|
{
|
|
arg=4;
|
|
} break;
|
|
case Script_Byte: /*Just a one byte arg*/
|
|
case Script_SByte:
|
|
{
|
|
*args[arg]=data[pos++];
|
|
} break;
|
|
case Script_Word: /*A two byte arg*/
|
|
{
|
|
*args[arg]=getInt16(data+pos);
|
|
pos+=2;
|
|
} break;
|
|
case Script_SWord: /*A signed two-byte arg*/
|
|
{
|
|
int t=getInt16(data+pos);
|
|
if(t&0x8000) *args[arg]=-(t&0x7FFF);
|
|
else *args[arg]=t;
|
|
pos+=2;
|
|
} break;
|
|
case Script_Variable: /*Size of arg depends on LSB in opcode*/
|
|
case Script_SVariable:
|
|
case Script_SRelative:
|
|
case Script_Property:
|
|
case Script_Global:
|
|
case Script_Local:
|
|
case Script_Temp:
|
|
case Script_Param:
|
|
{
|
|
if(size==1) *args[arg]=data[pos++];
|
|
else
|
|
{
|
|
*args[arg]=getInt16(data+pos);
|
|
pos+=2;
|
|
}
|
|
} break;
|
|
case Script_End: /*Special tag for ret*/
|
|
{
|
|
done=1;
|
|
arg=4;
|
|
} break;
|
|
default: /*Can't happen(tm)*/
|
|
{
|
|
printf("Unknown argument format %d for op %02X\n", formats[op][arg], op);
|
|
} break;
|
|
}
|
|
}
|
|
fflush(stdout);
|
|
if (m->used) m->data[m->used-1].size=pos-old_pos;
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
#ifdef SCRIPT_DEBUG
|
|
void list_code_blocks(resource_t* r)
|
|
{
|
|
int pos=getInt16(r->data+2);
|
|
while(pos<r->size-2)
|
|
{
|
|
int type=getInt16(r->data+pos);
|
|
int len=getInt16(r->data+pos+2);
|
|
if(type==2) printf("%X-%X\n", pos, pos+len);
|
|
pos+=len;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*These expect the frame, the whole frame, and, well, other stuff too,
|
|
*I guess, as long as it looks like a frame*/
|
|
static int get_type(unsigned char* obj)
|
|
{
|
|
return getInt16(obj);
|
|
}
|
|
|
|
static int get_length(unsigned char* obj)
|
|
{
|
|
return getInt16(obj+2);
|
|
}
|
|
|
|
static int get_selector_count(unsigned char* obj)
|
|
{
|
|
return getInt16(obj+10);
|
|
}
|
|
|
|
static int get_selector_value(unsigned char* obj, int sel)
|
|
{
|
|
assert(sel<get_selector_count(obj));
|
|
return getInt16(obj + 12 + sel*2);
|
|
}
|
|
|
|
/*Bas things happen if the method offset value is wrong*/
|
|
static unsigned char* get_method_area(unsigned char* obj)
|
|
{
|
|
return obj+getInt16(obj+8)+10;
|
|
}
|
|
|
|
static int get_method_count(unsigned char* obj)
|
|
{
|
|
return getInt16(get_method_area(obj));
|
|
}
|
|
|
|
static int get_method_number(unsigned char* obj, int i)
|
|
{
|
|
assert(i<get_method_count(obj));
|
|
return getInt16(get_method_area(obj)+2+2*i);
|
|
}
|
|
|
|
static int get_method_location(unsigned char* obj, int i)
|
|
{
|
|
assert(i<get_method_count(obj));
|
|
return getInt16(get_method_area(obj)+4+2*get_method_count(obj)+2*i);
|
|
}
|
|
|
|
|
|
/*Returns the position of the first frame of type 'type' in resource 'r',
|
|
*starting from the frame starting at 'start', or -1 on failure.
|
|
*/
|
|
static int find_frame(resource_t* r, int type, unsigned int start)
|
|
{
|
|
int t=-1;
|
|
unsigned int pos = start;
|
|
unsigned char* frame;
|
|
|
|
assert(start<=r->size-4);
|
|
|
|
#ifdef SCRIPT_DEBUG
|
|
printf("Searching for frame of type %d in script %03d, starting at %#x\n", type, r->number, start);
|
|
dump(r->data+start, 32);
|
|
#endif
|
|
|
|
/*Some times there's an extra byte at the beginning. Christoph?*/
|
|
#if 1
|
|
if(pos==0 && r->size>=6 && \
|
|
!((0<getInt16(r->data)) && (10>getInt16(r->data)))) pos=2;
|
|
#else
|
|
if(pos == 0)
|
|
pos = 2;
|
|
#endif
|
|
frame = r->data + pos;
|
|
while(1)
|
|
{
|
|
#ifdef SCRIPT_DEBUG
|
|
printf("offset = %#x\n", pos);
|
|
dump(frame, 32);
|
|
#endif
|
|
t = get_type(frame);
|
|
if(t == type)
|
|
break;
|
|
|
|
if(t == 0)
|
|
return -1;
|
|
|
|
pos+=get_length(frame);
|
|
if(pos > (r->size - 2))
|
|
return -1;
|
|
frame+=get_length(frame);
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
|
|
/*FIXME: lots of things are identical to read_object and read_class. Some of
|
|
*these would benefit from being put in separate functions.*/
|
|
|
|
static object* read_object(resource_mgr_t *resmgr, int script, int positions[1000])
|
|
{
|
|
resource_t* r = scir_find_resource(resmgr, sci_script, script, 0);
|
|
unsigned char* raw;
|
|
int pos;
|
|
object* obj;
|
|
|
|
printf("Searching for object in script %03d\n", script);
|
|
|
|
if(r==0) return 0;
|
|
|
|
/*Skip to the next object*/
|
|
#ifdef SCRIPT_DEBUG
|
|
printf("pre skip: pos=%#x\n", positions[script]);
|
|
#endif
|
|
pos=find_frame(r, 1, positions[script]);
|
|
#ifdef SCRIPT_DEBUG
|
|
printf("post skip: pos=%#x\n", pos);
|
|
#endif
|
|
if(pos==-1) return 0;
|
|
else positions[script]=pos+get_length(r->data+pos);
|
|
#ifdef SCRIPT_DEBUG
|
|
printf("post post: pos=%#x (len=%#x)\n", positions[script], get_length(r->data+pos));
|
|
#endif
|
|
|
|
/*Construct the object*/
|
|
obj=object_new();
|
|
raw=r->data+pos;
|
|
|
|
/*Fill in the name*/
|
|
if(get_selector_count(raw)<4) obj->name="<anonymous>";
|
|
else
|
|
{
|
|
if (get_selector_value(raw, 3))
|
|
obj->name = (char *) r->data + get_selector_value(raw, 3);
|
|
else obj->name="<null>";
|
|
}
|
|
|
|
/*Fill in the class*/
|
|
if(get_selector_count(raw)==0) obj->parent=object_root;
|
|
else
|
|
{
|
|
int parent_id=get_selector_value(raw, 1);
|
|
if(parent_id>=fobjects.used)
|
|
{
|
|
free(obj);
|
|
return 0;
|
|
}
|
|
if(parent_id<1) obj->parent=object_root;
|
|
else obj->parent=fobjects.data[parent_id];
|
|
}
|
|
|
|
/*Add the object to the class*/
|
|
if(!obj->parent)
|
|
{
|
|
free(obj);
|
|
return 0;
|
|
}
|
|
if(add_child(obj->parent, obj)){
|
|
free(obj);
|
|
return 0;
|
|
}
|
|
if(add_object(obj))
|
|
{
|
|
free(obj);
|
|
return 0;
|
|
}
|
|
|
|
/*FIXME: decode selectors here*/
|
|
|
|
obj->method_count=get_method_count(raw);
|
|
obj->methods= (script_method**)sci_malloc(obj->method_count*sizeof(script_method));
|
|
if(obj->methods==0)
|
|
{
|
|
free(obj);
|
|
return 0;
|
|
} else {
|
|
int i;
|
|
for(i=0; i<obj->method_count; i++)
|
|
{
|
|
int number=get_method_number(raw, i);
|
|
int position=get_method_location(raw, i);
|
|
|
|
if((obj->methods[i]=decode_method(r->data+position))==0)
|
|
{
|
|
obj->method_count=i-1;
|
|
break;
|
|
}
|
|
obj->methods[i]->number=number;
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
static object* read_class(resource_mgr_t *resmgr, int script, int positions[1000])
|
|
{
|
|
resource_t* r = scir_find_resource(resmgr, sci_script, script, 0);
|
|
unsigned char* raw;
|
|
int pos;
|
|
object* obj;
|
|
|
|
printf("Searching for class in script %03d\n", script);
|
|
|
|
if(r==0) return fake_object("<resource not found>");
|
|
|
|
/*Skip to the next class*/
|
|
#ifdef SCRIPT_DEBUG
|
|
printf("pre skip: pos=%#x\n", positions[script]);
|
|
#endif
|
|
pos=find_frame(r, 6, positions[script]);
|
|
#ifdef SCRIPT_DEBUG
|
|
printf("post skip: pos=%#x\n", pos);
|
|
#endif
|
|
if(pos==-1) return fake_object("<no more classes in script>");
|
|
else positions[script]=pos+get_length(r->data+pos);
|
|
#ifdef SCRIPT_DEBUG
|
|
printf("post post: pos=%#x (len=%#x)\n", positions[script], get_length(r->data+pos));
|
|
#endif
|
|
|
|
/*Construct the object*/
|
|
obj=object_new();
|
|
raw=r->data+pos;
|
|
|
|
/*Fill in the name*/
|
|
if(get_selector_count(raw)<4) obj->name="<anonymous>";
|
|
else
|
|
{
|
|
if (get_selector_value(raw, 3))
|
|
obj->name = (char *) r->data + get_selector_value(raw, 3);
|
|
else obj->name="<null>";
|
|
}
|
|
|
|
/*Fill in the parent*/
|
|
if(get_selector_count(raw)==0) obj->parent=object_root;
|
|
else
|
|
{
|
|
int superclass_id=get_selector_value(raw, 1);
|
|
printf("superclass==%d\n", superclass_id);
|
|
if(superclass_id>=fobjects.used)
|
|
{
|
|
free(obj);
|
|
return fake_object("<no such superclass>");
|
|
}
|
|
if(superclass_id<1) obj->parent=object_root;
|
|
else obj->parent=fobjects.data[superclass_id];
|
|
}
|
|
|
|
/*Add the class to the hierarchy*/
|
|
if(!obj->parent)
|
|
{
|
|
free(obj);
|
|
return fake_object("<null parent>");
|
|
}
|
|
if(add_child(obj->parent, obj)){
|
|
free(obj);
|
|
return fake_object("<add_child failed>");
|
|
}
|
|
if(add_object(obj))
|
|
{
|
|
free(obj);
|
|
return fake_object("<add_object failed>");
|
|
}
|
|
|
|
/*FIXME: decode selectors and methods here*/
|
|
|
|
return obj;
|
|
}
|
|
|
|
void freeObject(object* obj)
|
|
{
|
|
int i;
|
|
for(i=0; i<obj->children.used; i++) freeObject(obj->children.data[i]);
|
|
free(obj);
|
|
}
|
|
|
|
static int objects_init(resource_mgr_t *resmgr)
|
|
{
|
|
FLEXARRAY_INIT(object*, fobjects);
|
|
max_object=0;
|
|
|
|
if((object_root=object_new())==0) return 1;
|
|
object_root->name="<root>";
|
|
add_object(object_root);
|
|
|
|
opcodes=vocabulary_get_opcodes(resmgr);
|
|
knames=vocabulary_get_knames(resmgr, &knames_count);
|
|
snames=vocabulary_get_snames(resmgr, NULL, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int loadObjects(resource_mgr_t *resmgr)
|
|
{
|
|
int i;
|
|
int *classes, class_count;
|
|
int positions[1000];
|
|
|
|
if(objects_init(resmgr))
|
|
{
|
|
#ifdef SCRIPT_DEBUG
|
|
perror("objects_init");
|
|
#endif
|
|
return 1;
|
|
}
|
|
classes=vocabulary_get_classes(resmgr, &class_count);
|
|
|
|
for(i=0; i<1000; i++) positions[i]=0;
|
|
for(i=0; i<class_count; i++)
|
|
{
|
|
#ifdef SCRIPT_DEBUG
|
|
printf ("\n\nReading class 0x%02X\n", i);
|
|
#endif
|
|
if(read_class(resmgr, classes[i], positions)==0)
|
|
{
|
|
#ifdef SCRIPT_DEBUG
|
|
fprintf(stderr, "Failed to load class %d, which is a parent.\n", i);
|
|
#endif
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
for(i=0; i<1000; i++) positions[i]=0;
|
|
for(i=0; i<1000; i++) while(read_object(resmgr, i, positions));
|
|
|
|
object_map=fobjects.data;
|
|
max_object=fobjects.used;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|