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

script.cpp

/* ScummVM - Scumm Interpreter
 * Copyright (C) 2004-2005 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.
 *
 * $Header: /cvsroot/scummvm/scummvm/kyra/script.cpp,v 1.17.2.1 2005/10/18 02:11:19 sev Exp $
 *
 */

#include "common/stdafx.h"
#include "common/stream.h"
#include "common/util.h"
#include "common/system.h"
#include "kyra/kyra.h"
#include "kyra/resource.h"
#include "kyra/script.h"

#define FORM_CHUNK 0x4D524F46
#define TEXT_CHUNK 0x54584554
#define DATA_CHUNK 0x41544144
#define ORDR_CHUNK 0x5244524F

namespace Kyra {
ScriptHelper::ScriptHelper(KyraEngine *vm) : _vm(vm) {
#define COMMAND(x) { &ScriptHelper::x, #x }
      // now we create a list of all Command/Opcode procs and so
      static CommandEntry commandProcs[] = {
            // 0x00
            COMMAND(c1_jmpTo),
            COMMAND(c1_setRetValue),
            COMMAND(c1_pushRetOrPos),
            COMMAND(c1_push),
            // 0x04
            COMMAND(c1_push),
            COMMAND(c1_pushVar),
            COMMAND(c1_pushBPNeg),
            COMMAND(c1_pushBPAdd),
            // 0x08
            COMMAND(c1_popRetOrPos),
            COMMAND(c1_popVar),
            COMMAND(c1_popBPNeg),
            COMMAND(c1_popBPAdd),
            // 0x0C
            COMMAND(c1_addSP),
            COMMAND(c1_subSP),
            COMMAND(c1_execOpcode),
            COMMAND(c1_ifNotJmp),
            // 0x10
            COMMAND(c1_negate),
            COMMAND(c1_eval),
            COMMAND(c1_setRetAndJmp)
      };
      _commands = commandProcs;
#undef COMMAND
}

ScriptHelper::~ScriptHelper() {
}

bool ScriptHelper::loadScript(const char *filename, ScriptData *scriptData, byte *specialPtr) {
      uint32 size = 0;
      uint8 *data = _vm->resource()->fileData(filename, &size);   
      byte *curData = data;
      
      uint32 formBlockSize = getFORMBlockSize(curData);
      if (formBlockSize == (uint32)-1) {
            delete [] data;
            error("No FORM chunk found in file: '%s'", filename);
            return false;
      }
      
      uint32 chunkSize = getIFFBlockSize(data, curData, size, TEXT_CHUNK);
      if (chunkSize != (uint32)-1) {
            if (specialPtr) {
                  scriptData->mustBeFreed = 0;
                  scriptData->text = specialPtr;
                  specialPtr += chunkSize;
            } else {
                  scriptData->mustBeFreed = 1;
                  scriptData->text = new byte[chunkSize];
            }
            if (!loadIFFBlock(data, curData, size, TEXT_CHUNK, scriptData->text, chunkSize)) {
                  delete [] data;
                  unloadScript(scriptData);
                  error("Couldn't load TEXT chunk from file: '%s'", filename);
                  return false;
            }
      }
      
      chunkSize = getIFFBlockSize(data, curData, size, ORDR_CHUNK);
      if (chunkSize == (uint32)-1) {
            delete [] data;
            unloadScript(scriptData);
            error("No ORDR chunk found in file: '%s'", filename);
            return false;
      }
      if (specialPtr) {
            scriptData->mustBeFreed = 0;
            scriptData->ordr = specialPtr;
            specialPtr += chunkSize;
      } else {
            scriptData->mustBeFreed = 1;
            scriptData->ordr = new byte[chunkSize];
      }
      if (!loadIFFBlock(data, curData, size, ORDR_CHUNK, scriptData->ordr, chunkSize)) {
            delete [] data;
            unloadScript(scriptData);
            error("Couldn't load ORDR chunk from file: '%s'", filename);
            return false;
      }
      chunkSize = chunkSize / 2;
      while (chunkSize--) {
            ((uint16*)scriptData->ordr)[chunkSize] = READ_BE_UINT16(&((uint16*)scriptData->ordr)[chunkSize]);
      }
      
      chunkSize = getIFFBlockSize(data, curData, size, DATA_CHUNK);
      if (chunkSize == (uint32)-1) {
            delete [] data;
            unloadScript(scriptData);
            error("No DATA chunk found in file: '%s'", filename);
            return false;
      }
      if (specialPtr) {
            scriptData->mustBeFreed = 0;
            scriptData->data = specialPtr;
            specialPtr += chunkSize;
      } else {
            scriptData->mustBeFreed = 1;
            scriptData->data = new byte[chunkSize];
      }
      if (!loadIFFBlock(data, curData, size, DATA_CHUNK, scriptData->data, chunkSize)) {
            delete [] data;
            unloadScript(scriptData);
            error("Couldn't load DATA chunk from file: '%s'", filename);
            return false;
      }
      scriptData->dataSize = chunkSize / 2;
      
      delete [] data;
      return true;
}

void ScriptHelper::unloadScript(ScriptData *data) {
      if (data->mustBeFreed) {
            delete [] data->text;
            delete [] data->ordr;
            delete [] data->data;
      }
      
      data->mustBeFreed = 0;
      data->text = data->ordr = data->data = 0;
}

void ScriptHelper::initScript(ScriptState *scriptStat, ScriptData *data) {
      scriptStat->dataPtr = data;
      scriptStat->ip = 0;
      scriptStat->stack[60] = 0;
      scriptStat->bp = 62;
      scriptStat->sp = 60;
}

bool ScriptHelper::startScript(ScriptState *script, int function) {
      if (!script->dataPtr) {
            return false;
      }
      uint16 functionOffset = ((uint16*)script->dataPtr->ordr)[function];
      if (functionOffset == (uint16)-1) {
            return false;
      }
      script->ip = &script->dataPtr->data[functionOffset*2+2];
      return true;
}

bool ScriptHelper::validScript(ScriptState *script) {
      if (!script->ip || !script->dataPtr)
            return false;
      return true;
}

bool ScriptHelper::runScript(ScriptState *script) {
      _curScript = script;
      _parameter = 0;
      _continue = true;
      
      if (!_curScript->ip) {
            return false;
      }
      
      int16 code = READ_BE_UINT16(_curScript->ip); _curScript->ip += 2;
      int16 opcode = (code >> 8) & 0x1F;

      if (code & 0x8000) {
            opcode = 0;
            _parameter = code & 0x7FFF;
      } else if (code & 0x4000) {
            _parameter = code & 0xFF;
      } else if (code & 0x2000) {
            _parameter = READ_BE_UINT16(_curScript->ip); _curScript->ip += 2;
      } else {
            _parameter = 0;
      }
      
      if (opcode > 18) {
            error("Script unknown command: %d", opcode);
      } else {
            debug(5, "%s(%d)", _commands[opcode].desc, _parameter);
            (this->*(_commands[opcode].proc))();
      }
      
      _curScript = 0;
      return _continue;
}

uint32 ScriptHelper::getFORMBlockSize(byte *&data) const {
      static const uint32 chunkName = FORM_CHUNK;
      if (READ_LE_UINT32(data) != chunkName) {
            return (uint32)-1;
      }
      data += 4;
      uint32 retValue = READ_BE_UINT32(data); data += 4;
      return retValue;
}

uint32 ScriptHelper::getIFFBlockSize(byte *start, byte *&data, uint32 maxSize, const uint32 chunkName) const {
      uint32 size = (uint32)-1;
      bool special = false;
      
      if (data == (start + maxSize)) {
            data = start + 0x0C;
      }
      while (data < (start + maxSize)) {
            uint32 chunk = READ_LE_UINT32(data); data += 4;
            uint32 size_temp = READ_BE_UINT32(data); data += 4;
            if (chunk != chunkName) {
                  if (special) {
                        data += (size_temp + 1) & 0xFFFFFFFE;
                  } else {
                        data = start + 0x0C;
                        special = true;
                  }
            } else {
                  // kill our data
                  data = start;
                  size = size_temp;
                  break;
            }
      }
      return size;
}

bool ScriptHelper::loadIFFBlock(byte *start, byte *&data, uint32 maxSize, const uint32 chunkName, byte *loadTo, uint32 ptrSize) const {
      bool special = false;
      
      if (data == (start + maxSize)) {
            data = start + 0x0C;
      }
      while (data < (start + maxSize)) {
            uint32 chunk = READ_LE_UINT32(data); data += 4;
            uint32 chunkSize = READ_BE_UINT32(data); data += 4;
            if (chunk != chunkName) {
                  if (special) {
                        data += (chunkSize + 1) & 0xFFFFFFFE;
                  } else {
                        data = start + 0x0C;
                        special = true;
                  }
            } else {
                  uint32 loadSize = 0;
                  if (chunkSize < ptrSize)
                        loadSize = chunkSize;
                  else
                        loadSize = ptrSize;
                  memcpy(loadTo, data, loadSize);
                  chunkSize = (chunkSize + 1) & 0xFFFFFFFE;
                  if (chunkSize > loadSize) {
                        data += (chunkSize - loadSize);
                  }
                  return true;
            }
      }
      return false;
}
} // end of namespace Kyra

Generated by  Doxygen 1.6.0   Back to index