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 /* Script disassembler for Broken Sword II */
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 
27 #include "common/file.h"
28 
29 enum {
30 	GAME_OBJECT = 3,
31 	SCREEN_MANAGER = 9
32 };
33 
34 enum {
35 	CP_END_SCRIPT			= 0,
36 	CP_PUSH_LOCAL_VAR32		= 1,
37 	CP_PUSH_GLOBAL_VAR32		= 2,
38 	CP_POP_LOCAL_VAR32		= 3,
39 	CP_CALL_MCODE			= 4,
40 	CP_PUSH_LOCAL_ADDR		= 5,
41 	CP_PUSH_INT32			= 6,
42 	CP_SKIPONFALSE			= 7,
43 	CP_SKIPALWAYS			= 8,
44 	CP_SWITCH			= 9,
45 	CP_ADDNPOP_LOCAL_VAR32		= 10,
46 	CP_SUBNPOP_LOCAL_VAR32		= 11,
47 	CP_SKIPONTRUE			= 12,
48 	CP_POP_GLOBAL_VAR32		= 13,
49 	CP_ADDNPOP_GLOBAL_VAR32		= 14,
50 	CP_SUBNPOP_GLOBAL_VAR32		= 15,
51 	CP_DEBUGON			= 16,
52 	CP_DEBUGOFF			= 17,
53 	CP_QUIT				= 18,
54 	CP_TERMINATE			= 19,
55 	OP_ISEQUAL			= 20,
56 	OP_PLUS				= 21,
57 	OP_MINUS			= 22,
58 	OP_TIMES			= 23,
59 	OP_DIVIDE			= 24,
60 	OP_NOTEQUAL			= 25,
61 	OP_ANDAND			= 26,
62 	OP_GTTHAN			= 27,
63 	OP_LSTHAN			= 28,
64 	CP_JUMP_ON_RETURNED		= 29,
65 	CP_TEMP_TEXT_PROCESS		= 30,
66 	CP_SAVE_MCODE_START		= 31,
67 	CP_RESTART_SCRIPT		= 32,
68 	CP_PUSH_STRING			= 33,
69 	CP_PUSH_DEREFERENCED_STRUCTURE	= 34,
70 	OP_GTTHANE			= 35,
71 	OP_LSTHANE			= 36,
72 	OP_OROR				= 37
73 };
74 
75 const char *opcodes[] = {
76 	/* 00 */
77 	"fnTestFunction",
78 	"fnTestFlags",
79 	"fnRegisterStartPoint",
80 	"fnInitBackground",
81 	/* 04 */
82 	"fnSetSession",
83 	"fnBackSprite",
84 	"fnSortSprite",
85 	"fnForeSprite",
86 	/* 08 */
87 	"fnRegisterMouse",
88 	"fnAnim",
89 	"fnRandom",
90 	"fnPreLoad",
91 	/* 0C */
92 	"fnAddSubject",
93 	"fnInteract",
94 	"fnChoose",
95 	"fnWalk",
96 	/* 10 */
97 	"fnWalkToAnim",
98 	"fnTurn",
99 	"fnStandAt",
100 	"fnStand",
101 	/* 14 */
102 	"fnStandAfterAnim",
103 	"fnPause",
104 	"fnMegaTableAnim",
105 	"fnAddMenuObject",
106 	/* 18 */
107 	"fnStartConversation",
108 	"fnEndConversation",
109 	"fnSetFrame",
110 	"fnRandomPause",
111 	/* 1C */
112 	"fnRegisterFrame",
113 	"fnNoSprite",
114 	"fnSendSync",
115 	"fnUpdatePlayerStats",
116 	/* 20 */
117 	"fnPassGraph",
118 	"fnInitFloorMouse",
119 	"fnPassMega",
120 	"fnFaceXY",
121 	/* 24 */
122 	"fnEndSession",
123 	"fnNoHuman",
124 	"fnAddHuman",
125 	"fnWeWait",
126 	/* 28 */
127 	"fnTheyDoWeWait",
128 	"fnTheyDo",
129 	"fnWalkToTalkToMega",
130 	"fnFadeDown",
131 	/* 2C */
132 	"fnISpeak",
133 	"fnTotalRestart",
134 	"fnSetWalkGrid",
135 	"fnSpeechProcess",
136 	/* 30 */
137 	"fnSetScaling",
138 	"fnStartEvent",
139 	"fnCheckEventWaiting",
140 	"fnRequestSpeech",
141 	/* 34 */
142 	"fnGosub",
143 	"fnTimedWait",
144 	"fnPlayFx",
145 	"fnStopFx",
146 	/* 38 */
147 	"fnPlayMusic",
148 	"fnStopMusic",
149 	"fnSetValue",
150 	"fnNewScript",
151 	/* 3C */
152 	"fnGetSync",
153 	"fnWaitSync",
154 	"fnRegisterWalkGrid",
155 	"fnReverseMegaTableAnim",
156 	/* 40 */
157 	"fnReverseAnim",
158 	"fnAddToKillList",
159 	"fnSetStandbyCoords",
160 	"fnBackPar0Sprite",
161 	/* 44 */
162 	"fnBackPar1Sprite",
163 	"fnForePar0Sprite",
164 	"fnForePar1Sprite",
165 	"fnSetPlayerActionEvent",
166 	/* 48 */
167 	"fnSetScrollCoordinate",
168 	"fnStandAtAnim",
169 	"fnSetScrollLeftMouse",
170 	"fnSetScrollRightMouse",
171 	/* 4C */
172 	"fnColour",
173 	"fnFlash",
174 	"fnPreFetch",
175 	"fnGetPlayerSaveData",
176 	/* 50 */
177 	"fnPassPlayerSaveData",
178 	"fnSendEvent",
179 	"fnAddWalkGrid",
180 	"fnRemoveWalkGrid",
181 	/* 54 */
182 	"fnCheckForEvent",
183 	"fnPauseForEvent",
184 	"fnClearEvent",
185 	"fnFaceMega",
186 	/* 58 */
187 	"fnPlaySequence",
188 	"fnShadedSprite",
189 	"fnUnshadedSprite",
190 	"fnFadeUp",
191 	/* 60 */
192 	"fnDisplayMsg",
193 	"fnSetObjectHeld",
194 	"fnAddSequenceText",
195 	"fnResetGlobals",
196 	/* 64 */
197 	"fnSetPalette",
198 	"fnRegisterPointerText",
199 	"fnFetchWait",
200 	"fnRelease",
201 	/* 68 */
202 	"fnPrepareMusic",
203 	"fnSoundFetch",
204 	"fnPrepareMusic",
205 	"fnSmackerLeadIn",
206 	/* 6C */
207 	"fnSmackerLeadOut",
208 	"fnStopAllFx",
209 	"fnCheckPlayerActivity",
210 	"fnResetPlayerActivityDelay",
211 	/* 70 */
212 	"fnCheckMusicPlaying",
213 	"fnPlayCredits",
214 	"fnSetScrollSpeedNormal",
215 	"fnSetScrollSpeedSlow",
216 	/* 74 */
217 	"fnRemoveChooser",
218 	"fnSetFxVolAndPan",
219 	"fnSetFxVol",
220 	"fnRestoreGame",
221 	/* 78 */
222 	"fnRefreshInventory",
223 	"fnChangeShadows"
224 };
225 
globalVar(int num)226 const char *globalVar(int num) {
227 	static char buf[40];
228 
229 	switch (num) {
230 	case 0:		return "ID";
231 	case 1:		return "RESULT";
232 	case 2:		return "PLAYER_ACTION";
233 	case 305:	return "PLAYER_ID";
234 	case 13:	return "TALK_FLAG";
235 	case 4:		return "MOUSE_X";
236 	case 5:		return "MOUSE_Y";
237 	case 109:	return "LEFT_BUTTON";
238 	case 110:	return "RIGHT_BUTTON";
239 	case 178:	return "CLICKED_ID";
240 	case 6:		return "IN_SUBJECT";
241 	case 7:		return "COMBINE_BASE";
242 	case 14:	return "OBJECT_HELD";
243 	case 9:		return "SPEECH_ID";
244 	case 10:	return "INS1";
245 	case 11:	return "INS2";
246 	case 12:	return "INS3";
247 	case 60:	return "INS4";
248 	case 61:	return "INS5";
249 	case 59:	return "INS_COMMAND";
250 	case 141:	return "PLAYER_FEET_X";
251 	case 142:	return "PLAYER_FEET_Y";
252 	case 937:	return "PLAYER_CUR_DIR";
253 	case 62:	return "LOCATION";
254 	case 345:	return "SCROLL_X";
255 	case 346:	return "SCROLL_Y";
256 	case 710:	return "EXIT_CLICK_ID";
257 	case 713:	return "EXIT_FADING";
258 	case 912:	return "SYSTEM_TESTING_ANIMS";
259 	case 1230:	return "SYSTEM_TESTING_TEXT";
260 	case 1245:	return "SYSTEM_WANT_PREVIOUS_LINE";
261 	case 686:	return "MOUSE_AVAILABLE";
262 	case 1115:	return "AUTO_SELECTED";
263 	case 15:	return "CHOOSER_COUNT_FLAG";
264 	case 1153:	return "DEMO";
265 	case 1173:	return "PSXFLAG";
266 	case 1256:	return "DEAD";
267 	case 1278:	return "SPEECHANIMFLAG";
268 	case 1314:	return "SCROLL_OFFSET_X";
269 	case 111:	return "GAME_LANGUAGE";
270 	default:
271 		sprintf(buf, "global(%d)", num);
272 		return buf;
273 	}
274 }
275 
entry(int argc,char * argv[])276 int entry(int argc, char *argv[]) {
277 	Common::File in;
278 	uint8 type;
279 	char name[34];
280 	int32 size;
281 	int32 numScripts;
282 	long scriptBase;
283 	int32 scriptOffsets[100];	/* Way more than enough */
284 	int i, j;
285 
286 	if (argc != 2) {
287 		printf("Usage: desword2 file\n");
288 		return EXIT_FAILURE;
289 	}
290 
291 	in.open(argv[1], "rb");
292 
293 	/* Standard header
294 	 *
295 	 *  1 byte file type
296 	 *  1 byte compression type
297 	 *  4 bytes compressed size
298 	 *  4 bytes decompressed size
299 	 * 34 bytes name of object
300 	 */
301 
302 	type = in.readByte();
303 	if (type != GAME_OBJECT && type != SCREEN_MANAGER) {
304 		printf("This resource type does not include any scripts\n");
305 		return EXIT_FAILURE;
306 	}
307 
308 	in.seek(9, SEEK_CUR);
309 	in.read_throwsOnError(name, sizeof(name));
310 
311 	printf("\"%s\"\n", name);
312 
313 	/* Object hub
314 	 *
315 	 *  4 bytes object type
316 	 *  4 bytes logic level
317 	 * 12 bytes unused
318 	 * 12 bytes script_id
319 	 * 12 bytes script_pc
320 	 */
321 
322 	in.seek(44, SEEK_CUR);
323 
324 	/* Script data
325 	 *
326 	 * 4 bytes variable space size
327 	 * variable space
328 	 * 4 bytes number of scripts
329 	 * offsets for each script (4 bytes each)
330 	 */
331 
332 	size = (int32) in.readUint32LE();
333 	in.seek(size, SEEK_CUR);
334 
335 	numScripts = (int32) in.readUint32LE();
336 
337 	printf("numScripts = %d\n", numScripts);
338 
339 	for (i = 0; i < numScripts; i++)
340 		scriptOffsets[i] = (int32) in.readUint32LE();
341 
342 	/* Checksum block
343 	 *
344 	 * 4 bytes "magic number" (12345678)
345 	 * 4 bytes code length
346 	 * 4 bytes checksum
347 	 */
348 
349 	in.seek(12, SEEK_CUR);
350 
351 	scriptBase = in.pos();
352 
353 	for (i = 0; i < numScripts; i++) {
354 		int done = 0;
355 		int pc = 0;
356 
357 		printf("\nScript #%d\n\n", i + 1);
358 
359 		in.seek(scriptBase + scriptOffsets[i], SEEK_SET);
360 
361 		while (!done) {
362 			uint32 curCommand;
363 			int32 caseCount;
364 			int32 parameter;
365 			int32 value;
366 
367 			printf("%04x: ", pc);
368 
369 			curCommand = in.readByte();
370 			pc++;
371 
372 			switch (curCommand) {
373 			case CP_END_SCRIPT:
374 				printf("END\n");
375 				done = 1;
376 				break;
377 			case CP_PUSH_LOCAL_VAR32:
378 				parameter = (int16) in.readUint16LE();
379 				pc += 2;
380 				printf("PUSH local(%d)\n", parameter);
381 				break;
382 			case CP_PUSH_GLOBAL_VAR32:
383 				parameter = (int16) in.readUint16LE();
384 				pc += 2;
385 				printf("PUSH %s\n", globalVar(parameter));
386 				break;
387 			case CP_POP_LOCAL_VAR32:
388 				parameter = (int16) in.readUint16LE();
389 				pc += 2;
390 				printf("POP -> local(%d)\n", parameter);
391 				break;
392 			case CP_CALL_MCODE:
393 				parameter = (int16) in.readUint16LE();
394 				value = (int8) in.readByte();
395 				pc += 3;
396 				printf("CALL %d, %s\n", value, opcodes[parameter]);
397 				break;
398 			case CP_PUSH_LOCAL_ADDR:
399 				parameter = (int16) in.readUint16LE();
400 				pc += 2;
401 				printf("PUSH &local(%d)\n", parameter);
402 				break;
403 			case CP_PUSH_INT32:
404 				parameter = (int32) in.readUint32LE();
405 				pc += 4;
406 				printf("PUSH %d\n", parameter);
407 				break;
408 			case CP_SKIPONFALSE:
409 				parameter = (int32) in.readUint32LE();
410 				printf("IFNOT POP THEN JUMP %04x\n", pc + parameter);
411 				pc += 4;
412 				break;
413 			case CP_SKIPALWAYS:
414 				parameter = (int32) in.readUint32LE();
415 				printf("JUMP %04x\n", pc + parameter);
416 				pc += 4;
417 				break;
418 			case CP_SWITCH:
419 				caseCount = (int32) in.readUint32LE();
420 				pc += 4;
421 				printf("SWITCH POP\n");
422 				for (j = 0; j < caseCount; j++) {
423 					int32 to;
424 
425 					value = (int32) in.readUint32LE();
426 					to = (int32) in.readUint32LE();
427 
428 					printf("----:     %-7d -> JUMP %04x\n", value, pc + to);
429 
430 					pc += 8;
431 				}
432 
433 				printf("----:     default -> JUMP %04x\n", pc + (int32) in.readUint32LE());
434 				pc += 4;
435 				break;
436 			case CP_ADDNPOP_LOCAL_VAR32:
437 				parameter = (int16) in.readUint16LE();
438 				pc += 2;
439 				printf("local(%d) += POP\n", parameter);
440 				break;
441 			case CP_SUBNPOP_LOCAL_VAR32:
442 				parameter = (int16) in.readUint16LE();
443 				pc += 2;
444 				printf("local(%d) -= POP\n", parameter);
445 				break;
446 			case CP_SKIPONTRUE:
447 				parameter = (int32) in.readUint32LE();
448 				printf("IF POP THEN JUMP %04x\n", pc + parameter);
449 				pc += 4;
450 				break;
451 			case CP_POP_GLOBAL_VAR32:
452 				parameter = (int16) in.readUint16LE();
453 				pc += 2;
454 				printf("%s = POP\n", globalVar(parameter));
455 				break;
456 			case CP_ADDNPOP_GLOBAL_VAR32:
457 				parameter = (int16) in.readUint16LE();
458 				pc += 2;
459 				printf("%s += POP\n", globalVar(parameter));
460 				break;
461 			case CP_SUBNPOP_GLOBAL_VAR32:
462 				parameter = (int16) in.readUint16LE();
463 				pc += 2;
464 				printf("%s -= POP\n", globalVar(parameter));
465 				break;
466 			case CP_DEBUGON:
467 				printf("DEBUG ON\n");
468 				break;
469 			case CP_DEBUGOFF:
470 				printf("DEBUG OFF\n");
471 				break;
472 			case CP_QUIT:
473 				printf("QUIT\n");	/* For a cycle only */
474 				break;
475 			case CP_TERMINATE:
476 				printf("TERMINATE\n");
477 				break;
478 			case OP_ISEQUAL:
479 				printf("PUSH POP == POP\n");
480 				break;
481 			case OP_PLUS:
482 				printf("PUSH POP + POP\n");
483 				break;
484 			case OP_MINUS:
485 				printf("PUSH POP - POP\n");
486 				break;
487 			case OP_TIMES:
488 				printf("PUSH POP * POP\n");
489 				break;
490 			case OP_DIVIDE:
491 				printf("PUSH POP / POP\n");
492 				break;
493 			case OP_NOTEQUAL:
494 				printf("PUSH POP != POP\n");
495 				break;
496 			case OP_ANDAND:
497 				printf("PUSH POP && POP\n");
498 				break;
499 			case OP_GTTHAN:
500 				printf("PUSH POP > POP\n");
501 				break;
502 			case OP_LSTHAN:
503 				printf("PUSH POP < POP\n");
504 				break;
505 			case CP_JUMP_ON_RETURNED:
506 				in.readByte();
507 				printf("JUMP_ON_RETURNED\n");
508 				pc++;
509 				break;
510 			case CP_TEMP_TEXT_PROCESS:
511 				in.readUint32LE();
512 				pc += 4;
513 				printf("TEMP_TEXT_PROCESS\n");
514 				break;
515 			case CP_SAVE_MCODE_START:
516 				printf("SAVE_MCODE_START\n");
517 				break;
518 			case CP_RESTART_SCRIPT:
519 				printf("RESTART_SCRIPT\n");
520 				break;
521 			case CP_PUSH_STRING:
522 				parameter = (int8) in.readByte();
523 				printf("PUSH \"");
524 				for (j = 0; j < parameter; j++) {
525 					byte c = in.readByte();
526 					fputc(c, stdout);
527 					pc++;
528 				}
529 				printf("\"\n");
530 				in.readByte();
531 				pc += 2;
532 				break;
533 			case CP_PUSH_DEREFERENCED_STRUCTURE:
534 				parameter = (int32) in.readUint32LE();
535 				pc += 4;
536 				printf("PUSH far(%d)\n", parameter);
537 				break;
538 			case OP_GTTHANE:
539 				printf("PUSH POP >= POP\n");
540 				break;
541 			case OP_LSTHANE:
542 				printf("PUSH POP <= POP\n");
543 				break;
544 			case OP_OROR:
545 				printf("PUSH POP || POP\n");
546 				break;
547 			default:
548 				printf("<unknown>\n");
549 				break;
550 			}
551 		}
552 	}
553 
554 	return EXIT_SUCCESS;
555 }
556 
main(int argc,char * argv[])557 int main(int argc, char *argv[]) {
558 	// Catch exceptions
559 	try {
560 		return entry(argc, argv);
561 	} catch(std::exception &e) {
562 		printf("Fatal Error: %s\n", e.what());
563 	}
564 	return -1;
565 }
566 
567 
568