diff options
Diffstat (limited to 'sword2/interpreter.cpp')
| -rw-r--r-- | sword2/interpreter.cpp | 753 |
1 files changed, 0 insertions, 753 deletions
diff --git a/sword2/interpreter.cpp b/sword2/interpreter.cpp deleted file mode 100644 index 1a6e7080b8..0000000000 --- a/sword2/interpreter.cpp +++ /dev/null @@ -1,753 +0,0 @@ -/* Copyright (C) 1994-1998 Revolution Software Ltd. - * Copyright (C) 2003-2006 The ScummVM project - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * $URL$ - * $Id$ - */ - -#include "common/stdafx.h" -#include "common/util.h" -#include "sword2/sword2.h" -#include "sword2/defs.h" -#include "sword2/interpreter.h" -#include "sword2/logic.h" -#include "sword2/memory.h" -#include "sword2/resman.h" - -namespace Sword2 { - -#define STACK_SIZE 10 - -// The machine code table - -#ifndef REDUCE_MEMORY_USAGE -# define OPCODE(x) { &Logic::x, #x } -#else -# define OPCODE(x) { &Logic::x, "" } -#endif - -typedef int32 (Logic::*OpcodeProc)(int32 *); -struct OpcodeEntry { - OpcodeProc proc; - const char *desc; -}; - -static const OpcodeEntry opcodes[] = { - /* 00 */ - OPCODE(fnTestFunction), - OPCODE(fnTestFlags), - OPCODE(fnRegisterStartPoint), - OPCODE(fnInitBackground), - /* 04 */ - OPCODE(fnSetSession), - OPCODE(fnBackSprite), - OPCODE(fnSortSprite), - OPCODE(fnForeSprite), - /* 08 */ - OPCODE(fnRegisterMouse), - OPCODE(fnAnim), - OPCODE(fnRandom), - OPCODE(fnPreLoad), - /* 0C */ - OPCODE(fnAddSubject), - OPCODE(fnInteract), - OPCODE(fnChoose), - OPCODE(fnWalk), - /* 10 */ - OPCODE(fnWalkToAnim), - OPCODE(fnTurn), - OPCODE(fnStandAt), - OPCODE(fnStand), - /* 14 */ - OPCODE(fnStandAfterAnim), - OPCODE(fnPause), - OPCODE(fnMegaTableAnim), - OPCODE(fnAddMenuObject), - /* 18 */ - OPCODE(fnStartConversation), - OPCODE(fnEndConversation), - OPCODE(fnSetFrame), - OPCODE(fnRandomPause), - /* 1C */ - OPCODE(fnRegisterFrame), - OPCODE(fnNoSprite), - OPCODE(fnSendSync), - OPCODE(fnUpdatePlayerStats), - /* 20 */ - OPCODE(fnPassGraph), - OPCODE(fnInitFloorMouse), - OPCODE(fnPassMega), - OPCODE(fnFaceXY), - /* 24 */ - OPCODE(fnEndSession), - OPCODE(fnNoHuman), - OPCODE(fnAddHuman), - OPCODE(fnWeWait), - /* 28 */ - OPCODE(fnTheyDoWeWait), - OPCODE(fnTheyDo), - OPCODE(fnWalkToTalkToMega), - OPCODE(fnFadeDown), - /* 2C */ - OPCODE(fnISpeak), - OPCODE(fnTotalRestart), - OPCODE(fnSetWalkGrid), - OPCODE(fnSpeechProcess), - /* 30 */ - OPCODE(fnSetScaling), - OPCODE(fnStartEvent), - OPCODE(fnCheckEventWaiting), - OPCODE(fnRequestSpeech), - /* 34 */ - OPCODE(fnGosub), - OPCODE(fnTimedWait), - OPCODE(fnPlayFx), - OPCODE(fnStopFx), - /* 38 */ - OPCODE(fnPlayMusic), - OPCODE(fnStopMusic), - OPCODE(fnSetValue), - OPCODE(fnNewScript), - /* 3C */ - OPCODE(fnGetSync), - OPCODE(fnWaitSync), - OPCODE(fnRegisterWalkGrid), - OPCODE(fnReverseMegaTableAnim), - /* 40 */ - OPCODE(fnReverseAnim), - OPCODE(fnAddToKillList), - OPCODE(fnSetStandbyCoords), - OPCODE(fnBackPar0Sprite), - /* 44 */ - OPCODE(fnBackPar1Sprite), - OPCODE(fnForePar0Sprite), - OPCODE(fnForePar1Sprite), - OPCODE(fnSetPlayerActionEvent), - /* 48 */ - OPCODE(fnSetScrollCoordinate), - OPCODE(fnStandAtAnim), - OPCODE(fnSetScrollLeftMouse), - OPCODE(fnSetScrollRightMouse), - /* 4C */ - OPCODE(fnColour), - OPCODE(fnFlash), - OPCODE(fnPreFetch), - OPCODE(fnGetPlayerSaveData), - /* 50 */ - OPCODE(fnPassPlayerSaveData), - OPCODE(fnSendEvent), - OPCODE(fnAddWalkGrid), - OPCODE(fnRemoveWalkGrid), - /* 54 */ - OPCODE(fnCheckForEvent), - OPCODE(fnPauseForEvent), - OPCODE(fnClearEvent), - OPCODE(fnFaceMega), - /* 58 */ - OPCODE(fnPlaySequence), - OPCODE(fnShadedSprite), - OPCODE(fnUnshadedSprite), - OPCODE(fnFadeUp), - /* 5C */ - OPCODE(fnDisplayMsg), - OPCODE(fnSetObjectHeld), - OPCODE(fnAddSequenceText), - OPCODE(fnResetGlobals), - /* 60 */ - OPCODE(fnSetPalette), - OPCODE(fnRegisterPointerText), - OPCODE(fnFetchWait), - OPCODE(fnRelease), - /* 64 */ - OPCODE(fnPrepareMusic), - OPCODE(fnSoundFetch), - OPCODE(fnPrepareMusic), // Again, apparently - OPCODE(fnSmackerLeadIn), - /* 68 */ - OPCODE(fnSmackerLeadOut), - OPCODE(fnStopAllFx), - OPCODE(fnCheckPlayerActivity), - OPCODE(fnResetPlayerActivityDelay), - /* 6C */ - OPCODE(fnCheckMusicPlaying), - OPCODE(fnPlayCredits), - OPCODE(fnSetScrollSpeedNormal), - OPCODE(fnSetScrollSpeedSlow), - /* 70 */ - OPCODE(fnRemoveChooser), - OPCODE(fnSetFxVolAndPan), - OPCODE(fnSetFxVol), - OPCODE(fnRestoreGame), - /* 74 */ - OPCODE(fnRefreshInventory), - OPCODE(fnChangeShadows) -}; - -#define push(value) \ -do { \ - assert(stackPtr < ARRAYSIZE(stack)); \ - stack[stackPtr++] = (value); \ -} while (false) - -#define push_ptr(ptr) push(_vm->_memory->encodePtr(ptr)) - -#define pop() (assert(stackPtr < ARRAYSIZE(stack)), stack[--stackPtr]) - -int Logic::runResScript(uint32 scriptRes, uint32 offset) { - byte *scriptAddr; - int result; - - scriptAddr = _vm->_resman->openResource(scriptRes); - result = runScript(scriptAddr, scriptAddr, offset); - _vm->_resman->closeResource(scriptRes); - - return result; -} - -int Logic::runResObjScript(uint32 scriptRes, uint32 objRes, uint32 offset) { - byte *scriptAddr, *objAddr; - int result; - - scriptAddr = _vm->_resman->openResource(scriptRes); - objAddr = _vm->_resman->openResource(objRes); - result = runScript(scriptAddr, objAddr, offset); - _vm->_resman->closeResource(objRes); - _vm->_resman->closeResource(scriptRes); - - return result; -} - -int Logic::runScript(byte *scriptData, byte *objectData, uint32 offset) { - byte pc[4]; - - WRITE_LE_UINT32(pc, offset); - return runScript2(scriptData, objectData, pc); -} - -// This form of the runScript function is only called directly from -// the processSession() function, which uses it to update the script PC in the -// current object hub. For reasons which I do not understand, I couldn't get it -// to work if I called the function first with a dummy offset variable, and -// and then updated the object hub myself. - -int Logic::runScript2(byte *scriptData, byte *objectData, byte *offsetPtr) { - // Interestingly, unlike our BASS engine the stack is a local variable. - // I don't know whether or not this is relevant to the working of the - // BS2 engine. - - int32 stack[STACK_SIZE]; - int32 stackPtr = 0; - - uint32 offset = READ_LE_UINT32(offsetPtr); - - ResHeader header; - - header.read(scriptData); - - scriptData += ResHeader::size() + ObjectHub::size(); - - // The script data format: - // int32_TYPE 1 Size of variable space in bytes - // ... The variable space - // int32_TYPE 1 numberOfScripts - // int32_TYPE numberOfScripts The offsets for each script - - // Initialise some stuff - - uint32 ip = 0; // Code pointer - int scriptNumber; - - // Get the start of variables and start of code - - byte *localVars = scriptData + 4; - byte *code = scriptData + READ_LE_UINT32(scriptData) + 4; - uint32 noScripts = READ_LE_UINT32(code); - - code += 4; - - byte *offsetTable = code; - - if (offset < noScripts) { - ip = READ_LE_UINT32(offsetTable + offset * 4); - scriptNumber = offset; - debug(8, "Starting script %d from %d", scriptNumber, ip); - } else { - uint i; - - ip = offset; - - for (i = 1; i < noScripts; i++) { - if (READ_LE_UINT32(offsetTable + 4 * i) >= ip) - break; - } - - scriptNumber = i - 1; - debug(8, "Resuming script %d from %d", scriptNumber, ip); - } - - // There are a couple of known script bugs related to interacting with - // certain objects. We try to work around a few of them. - - bool checkMopBug = false; - bool checkPyramidBug = false; - bool checkElevatorBug = false; - - if (scriptNumber == 2) { - if (strcmp((char *)header.name, "mop_73") == 0) - checkMopBug = true; - else if (strcmp((char *)header.name, "titipoco_81") == 0) - checkPyramidBug = true; - else if (strcmp((char *)header.name, "lift_82") == 0) - checkElevatorBug = true; - } - - code += noScripts * 4; - - // Code should now be pointing at an identifier and a checksum - byte *checksumBlock = code; - - code += 4 * 3; - - if (READ_LE_UINT32(checksumBlock) != 12345678) { - error("Invalid script in object %s", header.name); - return 0; - } - - int32 codeLen = READ_LE_UINT32(checksumBlock + 4); - int32 checksum = 0; - - for (int i = 0; i < codeLen; i++) - checksum += (unsigned char) code[i]; - - if (checksum != (int32)READ_LE_UINT32(checksumBlock + 8)) { - debug(1, "Checksum error in object %s", header.name); - // This could be bad, but there has been a report about someone - // who had problems running the German version because of - // checksum errors. Could there be a version where checksums - // weren't properly calculated? - } - - bool runningScript = true; - - int parameterReturnedFromMcodeFunction = 0; // Allow scripts to return things - int savedStartOfMcode = 0; // For saving start of mcode commands - - while (runningScript) { - int i; - int32 a, b; - int curCommand, parameter, value; // Command and parameter variables - int retVal; - int caseCount; - bool foundCase; - byte *ptr; - - curCommand = code[ip++]; - - switch (curCommand) { - - // Script-related opcodes - - case CP_END_SCRIPT: - // End the script - runningScript = false; - - // WORKAROUND: The dreaded pyramid bug makes the torch - // untakeable when you speak to Titipoco. This is - // because one of the conditions for the torch to be - // takeable is that Titipoco isn't doing anything out - // of the ordinary. Global variable 913 has to be 0 to - // signify that he is in his "idle" state. - // - // Unfortunately, simply the act of speaking to him - // sets variable 913 to 1 (probably to stop him from - // turning around every now and then). The script may - // then go on to set the variable to different values - // to trigger various behaviours in him, but if you - // have run out of these cases the script won't ever - // set it back to 0 again. - // - // So if his click hander finishes, and variable 913 is - // 1, we set it back to 0 manually. - - if (checkPyramidBug && readVar(913) == 1) { - warning("Working around pyramid bug: Resetting Titipoco's state"); - writeVar(913, 0); - } - - // WORKAROUND: The not-so-known-but-should-be-dreaded - // elevator bug. - // - // The click handler for the top of the elevator only - // handles using the elevator, not examining it. When - // examining it, the mouse cursor is removed but never - // restored. - - if (checkElevatorBug && readVar(RIGHT_BUTTON)) { - warning("Working around elevator bug: Restoring mouse pointer"); - fnAddHuman(NULL); - } - - debug(9, "CP_END_SCRIPT"); - break; - case CP_QUIT: - // Quit out for a cycle - WRITE_LE_UINT32(offsetPtr, ip); - debug(9, "CP_QUIT"); - return 0; - case CP_TERMINATE: - // Quit out immediately without affecting the offset - // pointer - debug(9, "CP_TERMINATE"); - return 3; - case CP_RESTART_SCRIPT: - // Start the script again - ip = FROM_LE_32(offsetTable[scriptNumber]); - debug(9, "CP_RESTART_SCRIPT"); - break; - - // Stack-related opcodes - - case CP_PUSH_INT32: - // Push a long word value on to the stack - Read32ip(parameter); - push(parameter); - debug(9, "CP_PUSH_INT32: %d", parameter); - break; - case CP_PUSH_LOCAL_VAR32: - // Push the contents of a local variable - Read16ip(parameter); - push(READ_LE_UINT32(localVars + parameter)); - debug(9, "CP_PUSH_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, READ_LE_UINT32(localVars + parameter)); - break; - case CP_PUSH_GLOBAL_VAR32: - // Push a global variable - Read16ip(parameter); - push(readVar(parameter)); - debug(9, "CP_PUSH_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, readVar(parameter)); - break; - case CP_PUSH_LOCAL_ADDR: - // Push the address of a local variable - - // From what I understand, some scripts store data - // (e.g. mouse pointers) in their local variable space - // from the very beginning, and use this mechanism to - // pass that data to the opcode function. I don't yet - // know the conceptual difference between this and the - // CP_PUSH_DEREFERENCED_STRUCTURE opcode. - - Read16ip(parameter); - push_ptr(localVars + parameter); - debug(9, "CP_PUSH_LOCAL_ADDR: &localVars[%d] => %p", parameter / 4, localVars + parameter); - break; - case CP_PUSH_STRING: - // Push the address of a string on to the stack - // Get the string size - Read8ip(parameter); - - // ip now points to the string - ptr = code + ip; - push_ptr(ptr); - debug(9, "CP_PUSH_STRING: \"%s\"", ptr); - ip += (parameter + 1); - break; - case CP_PUSH_DEREFERENCED_STRUCTURE: - // Push the address of a dereferenced structure - Read32ip(parameter); - ptr = objectData + 4 + ResHeader::size() + ObjectHub::size() + parameter; - push_ptr(ptr); - debug(9, "CP_PUSH_DEREFERENCED_STRUCTURE: %d => %p", parameter, ptr); - break; - case CP_POP_LOCAL_VAR32: - // Pop a value into a local word variable - Read16ip(parameter); - value = pop(); - WRITE_LE_UINT32(localVars + parameter, value); - debug(9, "CP_POP_LOCAL_VAR32: localVars[%d] = %d", parameter / 4, value); - break; - case CP_POP_GLOBAL_VAR32: - // Pop a global variable - Read16ip(parameter); - value = pop(); - - // WORKAROUND for bug #1214168: The not-at-all dreaded - // mop bug. - // - // At the London Docks, global variable 1003 keeps - // track of Nico: - // - // 0: Hiding behind the first crate. - // 1: Hiding behind the second crate. - // 2: Standing in plain view on the deck. - // 3: Hiding on the roof. - // - // The bug happens when trying to pick up the mop while - // hiding on the roof. Nico climbs down, the mop is - // picked up, but the variable remains set to 3. - // Visually, everything looks ok. But as far as the - // scripts are concerned, she's still hiding up on the - // roof. This is not fatal, but leads to a number of - // glitches until the state is corrected. E.g. trying - // to climb back up the ladder will cause Nico to climb - // down again. - // - // Global variable 1017 keeps track of the mop. Setting - // it to 2 means that the mop has been picked up. We - // use that as the signal that Nico's state needs to be - // updated as well. - - if (checkMopBug && parameter == 1017 && readVar(1003) != 2) { - warning("Working around mop bug: Setting Nico's state"); - writeVar(1003, 2); - } - - writeVar(parameter, value); - debug(9, "CP_POP_GLOBAL_VAR32: scriptsVars[%d] = %d", parameter, value); - break; - case CP_ADDNPOP_LOCAL_VAR32: - Read16ip(parameter); - value = READ_LE_UINT32(localVars + parameter) + pop(); - WRITE_LE_UINT32(localVars + parameter, value); - debug(9, "CP_ADDNPOP_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, value); - break; - case CP_SUBNPOP_LOCAL_VAR32: - Read16ip(parameter); - value = READ_LE_UINT32(localVars + parameter) - pop(); - WRITE_LE_UINT32(localVars + parameter, value); - debug(9, "CP_SUBNPOP_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, value); - break; - case CP_ADDNPOP_GLOBAL_VAR32: - // Add and pop a global variable - Read16ip(parameter); - value = readVar(parameter) + pop(); - writeVar(parameter, value); - debug(9, "CP_ADDNPOP_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, value); - break; - case CP_SUBNPOP_GLOBAL_VAR32: - // Sub and pop a global variable - Read16ip(parameter); - value = readVar(parameter) - pop(); - writeVar(parameter, value); - debug(9, "CP_SUBNPOP_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, value); - break; - - // Jump opcodes - - case CP_SKIPONTRUE: - // Skip if the value on the stack is true - Read32ipLeaveip(parameter); - value = pop(); - if (!value) { - ip += 4; - debug(9, "CP_SKIPONTRUE: %d (IS FALSE (NOT SKIPPED))", parameter); - } else { - ip += parameter; - debug(9, "CP_SKIPONTRUE: %d (IS TRUE (SKIPPED))", parameter); - } - break; - case CP_SKIPONFALSE: - // Skip if the value on the stack is false - Read32ipLeaveip(parameter); - value = pop(); - if (value) { - ip += 4; - debug(9, "CP_SKIPONFALSE: %d (IS TRUE (NOT SKIPPED))", parameter); - } else { - ip += parameter; - debug(9, "CP_SKIPONFALSE: %d (IS FALSE (SKIPPED))", parameter); - } - break; - case CP_SKIPALWAYS: - // skip a block - Read32ipLeaveip(parameter); - ip += parameter; - debug(9, "CP_SKIPALWAYS: %d", parameter); - break; - case CP_SWITCH: - // switch - value = pop(); - Read32ip(caseCount); - - // Search the cases - foundCase = false; - for (i = 0; i < caseCount && !foundCase; i++) { - if (value == (int32)READ_LE_UINT32(code + ip)) { - // We have found the case, so lets - // jump to it - foundCase = true; - ip += READ_LE_UINT32(code + ip + 4); - } else - ip += 4 * 2; - } - - // If we found no matching case then use the default - - if (!foundCase) - ip += READ_LE_UINT32(code + ip); - - debug(9, "CP_SWITCH: [SORRY, NO DEBUG INFO]"); - break; - case CP_SAVE_MCODE_START: - // Save the start position on an mcode instruction in - // case we need to restart it again - savedStartOfMcode = ip - 1; - debug(9, "CP_SAVE_MCODE_START"); - break; - case CP_CALL_MCODE: - // Call an mcode routine - Read16ip(parameter); - assert(parameter < ARRAYSIZE(opcodes)); - // amount to adjust stack by (no of parameters) - Read8ip(value); - debug(9, "CP_CALL_MCODE: '%s', %d", opcodes[parameter].desc, value); - stackPtr -= value; - assert(stackPtr >= 0); - retVal = (this->*opcodes[parameter].proc)(&stack[stackPtr]); - - switch (retVal & 7) { - case IR_STOP: - // Quit out for a cycle - WRITE_LE_UINT32(offsetPtr, ip); - return 0; - case IR_CONT: - // Continue as normal - break; - case IR_TERMINATE: - // Return without updating the offset - return 2; - case IR_REPEAT: - // Return setting offset to start of this - // function call - WRITE_LE_UINT32(offsetPtr, savedStartOfMcode); - return 0; - case IR_GOSUB: - // that's really neat - WRITE_LE_UINT32(offsetPtr, ip); - return 2; - default: - error("Bad return code (%d) from '%s'", retVal & 7, opcodes[parameter].desc); - } - parameterReturnedFromMcodeFunction = retVal >> 3; - break; - case CP_JUMP_ON_RETURNED: - // Jump to a part of the script depending on - // the return value from an mcode routine - - // Get the maximum value - Read8ip(parameter); - debug(9, "CP_JUMP_ON_RETURNED: %d => %d", - parameterReturnedFromMcodeFunction, - READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4)); - ip += READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4); - break; - - // Operators - - case OP_ISEQUAL: - b = pop(); - a = pop(); - push(a == b); - debug(9, "OP_ISEQUAL: RESULT = %d", a == b); - break; - case OP_NOTEQUAL: - b = pop(); - a = pop(); - push(a != b); - debug(9, "OP_NOTEQUAL: RESULT = %d", a != b); - break; - case OP_GTTHAN: - b = pop(); - a = pop(); - push(a > b); - debug(9, "OP_GTTHAN: RESULT = %d", a > b); - break; - case OP_LSTHAN: - b = pop(); - a = pop(); - push(a < b); - debug(9, "OP_LSTHAN: RESULT = %d", a < b); - break; - case OP_GTTHANE: - b = pop(); - a = pop(); - push(a >= b); - debug(9, "OP_GTTHANE: RESULT = %d", a >= b); - break; - case OP_LSTHANE: - b = pop(); - a = pop(); - push(a <= b); - debug(9, "OP_LSTHANE: RESULT = %d", a <= b); - break; - case OP_PLUS: - b = pop(); - a = pop(); - push(a + b); - debug(9, "OP_PLUS: RESULT = %d", a + b); - break; - case OP_MINUS: - b = pop(); - a = pop(); - push(a - b); - debug(9, "OP_MINUS: RESULT = %d", a - b); - break; - case OP_TIMES: - b = pop(); - a = pop(); - push(a * b); - debug(9, "OP_TIMES: RESULT = %d", a * b); - break; - case OP_DIVIDE: - b = pop(); - a = pop(); - push(a / b); - debug(9, "OP_DIVIDE: RESULT = %d", a / b); - break; - case OP_ANDAND: - b = pop(); - a = pop(); - push(a && b); - debug(9, "OP_ANDAND: RESULT = %d", a && b); - break; - case OP_OROR: - b = pop(); - a = pop(); - push(a || b); - debug(9, "OP_OROR: RESULT = %d", a || b); - break; - - // Debugging opcodes, I think - - case CP_DEBUGON: - debug(9, "CP_DEBUGON"); - break; - case CP_DEBUGOFF: - debug(9, "CP_DEBUGOFF"); - break; - case CP_TEMP_TEXT_PROCESS: - Read32ip(parameter); - debug(9, "CP_TEMP_TEXT_PROCESS: %d", parameter); - break; - default: - error("Invalid script command %d", curCommand); - return 3; - } - } - - return 1; -} - -} // End of namespace Sword2 |
