Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

interpreter.cpp

/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * Additional copyright for this file:
 * Copyright (C) 1994-1998 Revolution Software Ltd.
 *
 * 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-11-1/engines/sword2/interpreter.cpp $
 * $Id: interpreter.cpp 30944 2008-02-23 22:50:18Z sev $
 */


#include "common/util.h"

#include "sword2/sword2.h"
#include "sword2/header.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

void Logic::setupOpcodes() {
      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)
      };

      _numOpcodes = ARRAYSIZE(opcodes);
      _opcodes = opcodes;
}

#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;
      bool checkPearlBug = 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;
            else if (strcmp((char *)header.name, "pearl_31") == 0)
                  checkPearlBug = 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 < _numOpcodes);
                  // 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:
                        if (checkPearlBug && readVar(1290) == 0) {
                              // Pearl's interaction script will wait
                              // until global(1290) is no longer 0
                              // before doing anything. But if the
                              // script was terminated prematurely,
                              // that never happens.
                              warning("Working around Pearl bug: Resetting Pearl's state");
                              writeVar(1290, 1);
                        }
                        // 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

Generated by  Doxygen 1.6.0   Back to index