1 /* ScummVM Tools
2  *
3  * ScummVM Tools is the legal property of its developers, whose
4  * names are too numerous to list here. Please refer to the
5  * COPYRIGHT file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #include <sciresource.h>
23 #include <console.h>
24 #include <script.h>
25 #include <vocabulary.h>
26 #include <old_objects.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <util.h>
30 #include <vm.h>
31 #include <assert.h>
32 
33 #ifdef SCI_CONSOLE
34 #define printf sciprintf
35 /* Yeah, I shouldn't be doing this ;-) [CJR] */
36 #endif
37 
38 FLEXARRAY_NOEXTRA(object*) fobjects;
39 
40 static int knames_count;
41 static char** knames;
42 static char** snames;
43 static opcode* opcodes;
44 
45 object **object_map, *object_root;
46 int max_object;
47 
48 const char* globals[] = {
49 	/*00*/
50 	"ego",
51 	"GAMEID",
52 	"roomXX",
53 	"speed",
54 	/*04*/
55 	"quitFlag",
56 	"cast",
57 	"regions",
58 	"timer",
59 	/*08*/
60 	"sounds",
61 	"inv",
62 	"eventHandler",
63 	"roomNumberExit",
64 	/*0C*/
65 	"previousRoomNumber",
66 	"roomNumber",
67 	"enterDebugModeOnRoomExit",
68 	"score",
69 	/*10*/
70 	"maximumScore",
71 	"11",
72 	"speed",
73 	"13",
74 	/*14*/
75 	"14",
76 	"loadCursor",
77 	"normalFont",
78 	"restoreSaveFont", /*dialogFont*/
79 	/*18*/
80 	"18",
81 	"19",
82 	"defaultFont",
83 	"1B",
84 	/*1C*/
85 	"pointerToVersionNumber",
86 	"locales",
87 	"pointerToSaveGameDirectory",
88 	"1F"
89 };
90 
add_object(object * obj)91 static int add_object(object* obj) {
92 	FLEXARRAY_APPEND(object*, fobjects, obj, return 1);
93 	return 0;
94 }
95 
dump(byte * data,int len)96 static void dump(byte* data, int len) {
97 	int i = 0;
98 	while (i < len) {
99 		printf("%02X ", data[i++]);
100 		if (i % 8 == 0) printf("   ");
101 		if (i % 16 == 0) printf("\n");
102 	}
103 	if (i % 16) printf("\n");
104 }
105 
printMethod(object * obj,int meth,int indent)106 static void printMethod(object* obj, int meth, int indent) {
107 	script_method* m = obj->methods[meth];
108 	int i, j;
109 
110 	for (j = 0; j < indent*2 - 1; j++) printf(" ");
111 	printf("Method %s\n", snames[m->number]);
112 
113 	for (i = 0; i < m->used; i++) {
114 		script_opcode op = m->data[i];
115 
116 		for (j = 0; j < indent; j++) printf("  ");
117 		printf("%s ", opcodes[op.opcode].name);
118 
119 		switch (op.opcode) {
120 		case 0x21: { /*callk*/
121 			if (op.arg1 > knames_count) printf("<no such kernel function %02X> ", op.arg1);
122 			else printf("%s ", knames[op.arg1]);
123 			printf("%02X", op.arg2);
124 		}
125 		break;
126 		case 0x28: { /*class*/
127 			if (op.arg1 > max_object) printf("<no such class %02X>", op.arg1);
128 			else {
129 				/* [DJ] op.arg1+1 adjusts for the <root> object */
130 				if (fobjects.data[op.arg1+1] == 0) printf("<null object>");
131 				else printf("%s", fobjects.data[op.arg1+1]->name);
132 			}
133 		}
134 		break;
135 		case 0x44: {
136 			if (op.arg1 > 0x20) printf("<no such global %02X> ", op.arg1);
137 			else printf("%s ", globals[op.arg1]);
138 		}
139 		break;
140 		default: {
141 			int args[3];
142 			args[0] = op.arg1;
143 			args[1] = op.arg2;
144 			args[2] = op.arg3;
145 			for (j = 0; j < 3; j++) {
146 				switch (formats[op.opcode][j]) {
147 				case Script_Invalid: {
148 					printf("<invalid> ");
149 				}
150 				break;
151 				case Script_None: {
152 					j = 3;
153 				}
154 				break;
155 				case Script_SByte:
156 				case Script_Byte: {
157 					printf("%02X ", args[j]);
158 				}
159 				break;
160 				case Script_Word:
161 				case Script_SVariable:
162 				case Script_Variable:
163 				case Script_SRelative:
164 				case Script_Property:
165 				case Script_Global:
166 				case Script_Local:
167 				case Script_Temp:
168 				case Script_Param: {
169 					printf("%04X ", args[j]);
170 				}
171 				break;
172 				case Script_SWord: {
173 					if (args[j] < 0) printf("-%04X", -args[j]);
174 					else printf("%04X", args[j]);
175 				}
176 				break;
177 				case Script_End: {
178 					printf("\n");
179 					return;
180 				}
181 				break;
182 				default: {
183 					printf("<unknown arg type %d> ", formats[op.opcode][j]);
184 				}
185 				}
186 			}
187 		}
188 		break;
189 		}
190 		printf("\n");
191 	}
192 }
193 
printObject_r(object * obj,int flags,int level)194 static void printObject_r(object* obj, int flags, int level) {
195 	int i;
196 	for (i = 0; i < level; i++) printf("  ");
197 	if (obj == 0) printf("(null)\n");
198 	else {
199 		printf("%s\n", obj->name);
200 		if (flags&SCRIPT_PRINT_METHODS) {
201 			for (i = 0; i < obj->method_count; i++) {
202 				printMethod(obj, i, level + 1);
203 			}
204 		}
205 		if (flags&SCRIPT_PRINT_CHILDREN) {
206 			for (i = 0; i < obj->children.used; i++) {
207 				printObject_r(obj->children.data[i], flags, level + 1);
208 			}
209 		}
210 	}
211 }
212 
printObject(object * obj,int flags)213 void printObject(object* obj, int flags) {
214 	printf("pO(%p, %d)\n", obj, flags);
215 	printObject_r(obj, flags, 0);
216 }
217 
object_new()218 static object* object_new() {
219 	object* obj = (object*)sci_malloc(sizeof(object));
220 	if (obj == 0) return 0;
221 
222 	obj->parent = 0;
223 	FLEXARRAY_INIT(object*, obj->children);
224 	obj->name = 0;
225 	obj->selector_count = 0;
226 	obj->selector_numbers = 0;
227 	obj->methods = 0;
228 	obj->method_count = 0;
229 
230 	return obj;
231 }
232 
add_child(object * parent,object * child)233 static int add_child(object* parent, object* child) {
234 	FLEXARRAY_APPEND(object*, parent->children, child, return 1);
235 	return 0;
236 }
237 
fake_object(const char * reason)238 static object* fake_object(const char* reason) {
239 	object* obj = object_new();
240 	if (obj == 0) {
241 #ifdef SCRIPT_DEBUG
242 		printf("object_new failed during fake for %s\n", reason);
243 #endif
244 		free(obj);
245 		return 0;
246 	}
247 	if (add_child(object_root, obj)) {
248 #ifdef SCRIPT_DEBUG
249 		printf("add_child failed during fake for %s\n", reason);
250 #endif
251 		free(obj);
252 		return 0;
253 	}
254 	obj->name = reason;
255 	if (add_object(obj)) {
256 #ifdef SCRIPT_DEBUG
257 		printf("add_object failed during fake for %s\n", reason);
258 #endif
259 		/*FIXME: clean up parent*/
260 		return 0;
261 	}
262 	return obj;
263 }
264 
decode_method(byte * data)265 static script_method* decode_method(byte* data) {
266 	script_method* m;
267 	int done = 0;
268 	int pos = 0;
269 	static int count = 0;
270 
271 	count++;
272 
273 	if ((m = (script_method*)sci_malloc(sizeof(script_method))) == 0) return 0;
274 	FLEXARRAY_INIT(script_opcode, *m);
275 
276 	while (!done) {
277 		int op = data[pos] >> 1;
278 		int size = 2 - (data[pos] & 1);
279 		int* args[3];
280 		int arg;
281 		int old_pos;
282 
283 		FLEXARRAY_ADD_SPACE(script_opcode, *m, 1, return 0);
284 		old_pos = pos;
285 		m->data[m->used-1].pos = pos;
286 		m->data[m->used-1].opcode = op;
287 
288 		/*Copy the adresses of the args to an array for convenience*/
289 		args[0] = &m->data[m->used-1].arg1;
290 		args[1] = &m->data[m->used-1].arg2;
291 		args[2] = &m->data[m->used-1].arg3;
292 
293 		/*Skip past the opcode*/
294 		pos++;
295 
296 		for (arg = 0; arg < 4; arg++) {
297 			switch (formats[op][arg]) {
298 			case Script_Invalid: { /*Can't happen(tm)*/
299 				int i;
300 				printf("Invalid opcode %02X at %04X in method %d\n", op, pos, count);
301 				for (i = m->used - 9; i < m->used - 1; i++) {
302 					printf("%s[%02X] ", opcodes[m->data[i].opcode].name, m->data[i].opcode);
303 					dump(data + m->data[i].pos, m->data[i].size);
304 				}
305 				printf("Dump from %04X-%04X\n", pos - 16, pos + 16);
306 				dump(data + pos - 16, 32);
307 			}
308 			break;
309 			case Script_None: { /*No more args*/
310 				arg = 4;
311 			}
312 			break;
313 			case Script_Byte: /*Just a one byte arg*/
314 			case Script_SByte: {
315 				*args[arg] = data[pos++];
316 			}
317 			break;
318 			case Script_Word: { /*A two byte arg*/
319 				*args[arg] = getInt16(data + pos);
320 				pos += 2;
321 			}
322 			break;
323 			case Script_SWord: { /*A signed two-byte arg*/
324 				int t = getInt16(data + pos);
325 				if (t&0x8000) *args[arg] = -(t & 0x7FFF);
326 				else *args[arg] = t;
327 				pos += 2;
328 			}
329 			break;
330 			case Script_Variable: /*Size of arg depends on LSB in opcode*/
331 			case Script_SVariable:
332 			case Script_SRelative:
333 			case Script_Property:
334 			case Script_Global:
335 			case Script_Local:
336 			case Script_Temp:
337 			case Script_Param: {
338 				if (size == 1) *args[arg] = data[pos++];
339 				else {
340 					*args[arg] = getInt16(data + pos);
341 					pos += 2;
342 				}
343 			}
344 			break;
345 			case Script_End: { /*Special tag for ret*/
346 				done = 1;
347 				arg = 4;
348 			}
349 			break;
350 			default: { /*Can't happen(tm)*/
351 				printf("Unknown argument format %d for op %02X\n", formats[op][arg], op);
352 			}
353 			break;
354 			}
355 		}
356 		fflush(stdout);
357 		if (m->used) m->data[m->used-1].size = pos - old_pos;
358 	}
359 
360 	return m;
361 }
362 
363 #ifdef SCRIPT_DEBUG
list_code_blocks(resource_t * r)364 void list_code_blocks(resource_t* r) {
365 	int pos = getInt16(r->data + 2);
366 	while (pos < r->size - 2) {
367 		int type = getInt16(r->data + pos);
368 		int len = getInt16(r->data + pos + 2);
369 		if (type == 2) printf("%X-%X\n", pos, pos + len);
370 		pos += len;
371 	}
372 }
373 #endif
374 
375 
376 /*These expect the frame, the whole frame, and, well, other stuff too,
377  *I guess, as long as it looks like a frame*/
get_type(unsigned char * obj)378 static int get_type(unsigned char* obj) {
379 	return getInt16(obj);
380 }
381 
get_length(unsigned char * obj)382 static int get_length(unsigned char* obj) {
383 	return getInt16(obj + 2);
384 }
385 
get_selector_count(unsigned char * obj)386 static int get_selector_count(unsigned char* obj) {
387 	return getInt16(obj + 10);
388 }
389 
get_selector_value(unsigned char * obj,int sel)390 static int get_selector_value(unsigned char* obj, int sel) {
391 	assert(sel < get_selector_count(obj));
392 	return getInt16(obj + 12 + sel*2);
393 }
394 
395 /*Bas things happen if the method offset value is wrong*/
get_method_area(unsigned char * obj)396 static unsigned char* get_method_area(unsigned char* obj) {
397 	return obj + getInt16(obj + 8) + 10;
398 }
399 
get_method_count(unsigned char * obj)400 static int get_method_count(unsigned char* obj) {
401 	return getInt16(get_method_area(obj));
402 }
403 
get_method_number(unsigned char * obj,int i)404 static int get_method_number(unsigned char* obj, int i) {
405 	assert(i < get_method_count(obj));
406 	return getInt16(get_method_area(obj) + 2 + 2*i);
407 }
408 
get_method_location(unsigned char * obj,int i)409 static int get_method_location(unsigned char* obj, int i) {
410 	assert(i < get_method_count(obj));
411 	return getInt16(get_method_area(obj) + 4 + 2*get_method_count(obj) + 2*i);
412 }
413 
414 
415 /*Returns the position of the first frame of type 'type' in resource 'r',
416  *starting from the frame starting at 'start', or -1 on failure.
417  */
find_frame(resource_t * r,int type,unsigned int start)418 static int find_frame(resource_t* r, int type, unsigned int start) {
419 	int t = -1;
420 	unsigned int pos = start;
421 	unsigned char* frame;
422 
423 	assert(start <= r->size - 4);
424 
425 #ifdef SCRIPT_DEBUG
426 	printf("Searching for frame of type %d in script %03d, starting at %#x\n", type, r->number, start);
427 	dump(r->data + start, 32);
428 #endif
429 
430 	/*Some times there's an extra byte at the beginning. Christoph?*/
431 #if 1
432 	if (pos == 0 && r->size >= 6 && \
433 	        !((0 < getInt16(r->data)) && (10 > getInt16(r->data)))) pos = 2;
434 #else
435 	if (pos == 0)
436 		pos = 2;
437 #endif
438 	frame = r->data + pos;
439 	while (1) {
440 #ifdef SCRIPT_DEBUG
441 		printf("offset = %#x\n", pos);
442 		dump(frame, 32);
443 #endif
444 		t = get_type(frame);
445 		if (t == type)
446 			break;
447 
448 		if (t == 0)
449 			return -1;
450 
451 		pos += get_length(frame);
452 		if (pos > (r->size - 2))
453 			return -1;
454 		frame += get_length(frame);
455 	}
456 
457 	return pos;
458 }
459 
460 
461 
462 /*FIXME: lots of things are identical to read_object and read_class. Some of
463  *these would benefit from being put in separate functions.*/
464 
read_object(resource_mgr_t * resmgr,int script,int positions[1000])465 static object* read_object(resource_mgr_t *resmgr, int script, int positions[1000]) {
466 	resource_t* r = scir_find_resource(resmgr, sci_script, script, 0);
467 	unsigned char* raw;
468 	int pos;
469 	object* obj;
470 
471 	printf("Searching for object in script %03d\n", script);
472 
473 	if (r == 0) return 0;
474 
475 	/*Skip to the next object*/
476 #ifdef SCRIPT_DEBUG
477 	printf("pre skip: pos=%#x\n", positions[script]);
478 #endif
479 	pos = find_frame(r, 1, positions[script]);
480 #ifdef SCRIPT_DEBUG
481 	printf("post skip: pos=%#x\n", pos);
482 #endif
483 	if (pos == -1) return 0;
484 	else positions[script] = pos + get_length(r->data + pos);
485 #ifdef SCRIPT_DEBUG
486 	printf("post post: pos=%#x (len=%#x)\n", positions[script], get_length(r->data + pos));
487 #endif
488 
489 	/*Construct the object*/
490 	obj = object_new();
491 	raw = r->data + pos;
492 
493 	/*Fill in the name*/
494 	if (get_selector_count(raw) < 4) obj->name = "<anonymous>";
495 	else {
496 		if (get_selector_value(raw, 3))
497 			obj->name = (char *) r->data + get_selector_value(raw, 3);
498 		else obj->name = "<null>";
499 	}
500 
501 	/*Fill in the class*/
502 	if (get_selector_count(raw) == 0) obj->parent = object_root;
503 	else {
504 		int parent_id = get_selector_value(raw, 1);
505 		if (parent_id >= fobjects.used) {
506 			free(obj);
507 			return 0;
508 		}
509 		if (parent_id < 1) obj->parent = object_root;
510 		else obj->parent = fobjects.data[parent_id];
511 	}
512 
513 	/*Add the object to the class*/
514 	if (!obj->parent) {
515 		free(obj);
516 		return 0;
517 	}
518 	if (add_child(obj->parent, obj)) {
519 		free(obj);
520 		return 0;
521 	}
522 	if (add_object(obj)) {
523 		free(obj);
524 		return 0;
525 	}
526 
527 	/*FIXME: decode selectors here*/
528 
529 	obj->method_count = get_method_count(raw);
530 	obj->methods = (script_method**)sci_malloc(obj->method_count * sizeof(script_method));
531 	if (obj->methods == 0) {
532 		free(obj);
533 		return 0;
534 	} else {
535 		int i;
536 		for (i = 0; i < obj->method_count; i++) {
537 			int number = get_method_number(raw, i);
538 			int position = get_method_location(raw, i);
539 
540 			if ((obj->methods[i] = decode_method(r->data + position)) == 0) {
541 				obj->method_count = i - 1;
542 				break;
543 			}
544 			obj->methods[i]->number = number;
545 		}
546 	}
547 
548 	return obj;
549 }
550 
read_class(resource_mgr_t * resmgr,int script,int positions[1000])551 static object* read_class(resource_mgr_t *resmgr, int script, int positions[1000]) {
552 	resource_t* r = scir_find_resource(resmgr, sci_script, script, 0);
553 	unsigned char* raw;
554 	int pos;
555 	object* obj;
556 
557 	printf("Searching for class in script %03d\n", script);
558 
559 	if (r == 0) return fake_object("<resource not found>");
560 
561 	/*Skip to the next class*/
562 #ifdef SCRIPT_DEBUG
563 	printf("pre skip: pos=%#x\n", positions[script]);
564 #endif
565 	pos = find_frame(r, 6, positions[script]);
566 #ifdef SCRIPT_DEBUG
567 	printf("post skip: pos=%#x\n", pos);
568 #endif
569 	if (pos == -1) return fake_object("<no more classes in script>");
570 	else positions[script] = pos + get_length(r->data + pos);
571 #ifdef SCRIPT_DEBUG
572 	printf("post post: pos=%#x (len=%#x)\n", positions[script], get_length(r->data + pos));
573 #endif
574 
575 	/*Construct the object*/
576 	obj = object_new();
577 	raw = r->data + pos;
578 
579 	/*Fill in the name*/
580 	if (get_selector_count(raw) < 4) obj->name = "<anonymous>";
581 	else {
582 		if (get_selector_value(raw, 3))
583 			obj->name = (char *) r->data + get_selector_value(raw, 3);
584 		else obj->name = "<null>";
585 	}
586 
587 	/*Fill in the parent*/
588 	if (get_selector_count(raw) == 0) obj->parent = object_root;
589 	else {
590 		int superclass_id = get_selector_value(raw, 1);
591 		printf("superclass==%d\n", superclass_id);
592 		if (superclass_id >= fobjects.used) {
593 			free(obj);
594 			return fake_object("<no such superclass>");
595 		}
596 		if (superclass_id < 1) obj->parent = object_root;
597 		else obj->parent = fobjects.data[superclass_id];
598 	}
599 
600 	/*Add the class to the hierarchy*/
601 	if (!obj->parent) {
602 		free(obj);
603 		return fake_object("<null parent>");
604 	}
605 	if (add_child(obj->parent, obj)) {
606 		free(obj);
607 		return fake_object("<add_child failed>");
608 	}
609 	if (add_object(obj)) {
610 		free(obj);
611 		return fake_object("<add_object failed>");
612 	}
613 
614 	/*FIXME: decode selectors and methods here*/
615 
616 	return obj;
617 }
618 
freeObject(object * obj)619 void freeObject(object* obj) {
620 	int i;
621 	for (i = 0; i < obj->children.used; i++) freeObject(obj->children.data[i]);
622 	free(obj);
623 }
624 
objects_init(resource_mgr_t * resmgr)625 static int objects_init(resource_mgr_t *resmgr) {
626 	FLEXARRAY_INIT(object*, fobjects);
627 	max_object = 0;
628 
629 	if ((object_root = object_new()) == 0) return 1;
630 	object_root->name = "<root>";
631 	add_object(object_root);
632 
633 	opcodes = vocabulary_get_opcodes(resmgr);
634 	knames = vocabulary_get_knames(resmgr, &knames_count);
635 	snames = vocabulary_get_snames(resmgr, NULL, 0);
636 
637 	return 0;
638 }
639 
loadObjects(resource_mgr_t * resmgr)640 int loadObjects(resource_mgr_t *resmgr) {
641 	int i;
642 	int *classes, class_count;
643 	int positions[1000];
644 
645 	if (objects_init(resmgr)) {
646 #ifdef SCRIPT_DEBUG
647 		perror("objects_init");
648 #endif
649 		return 1;
650 	}
651 	classes = vocabulary_get_classes(resmgr, &class_count);
652 
653 	for (i = 0; i < 1000; i++) positions[i] = 0;
654 	for (i = 0; i < class_count; i++) {
655 #ifdef SCRIPT_DEBUG
656 		printf("\n\nReading class 0x%02X\n", i);
657 #endif
658 		if (read_class(resmgr, classes[i], positions) == 0) {
659 #ifdef SCRIPT_DEBUG
660 			fprintf(stderr, "Failed to load class %d, which is a parent.\n", i);
661 #endif
662 			return 1;
663 		}
664 	}
665 
666 	for (i = 0; i < 1000; i++) positions[i] = 0;
667 	for (i = 0; i < 1000; i++) while (read_object(resmgr, i, positions));
668 
669 	object_map = fobjects.data;
670 	max_object = fobjects.used;
671 
672 	return 0;
673 }
674 
675 
676