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