[Scummvm-cvs-logs] SF.net SVN: scummvm:[52364] scummvm/trunk/engines/m4

dreammaster at users.sourceforge.net dreammaster at users.sourceforge.net
Wed Aug 25 08:42:54 CEST 2010


Revision: 52364
          http://scummvm.svn.sourceforge.net/scummvm/?rev=52364&view=rev
Author:   dreammaster
Date:     2010-08-25 06:42:54 +0000 (Wed, 25 Aug 2010)

Log Message:
-----------
M4: Implementation of script engine

Modified Paths:
--------------
    scummvm/trunk/engines/m4/globals.h
    scummvm/trunk/engines/m4/m4.h
    scummvm/trunk/engines/m4/mads_logic.cpp
    scummvm/trunk/engines/m4/mads_logic.h
    scummvm/trunk/engines/m4/mads_scene.cpp
    scummvm/trunk/engines/m4/mads_scene.h
    scummvm/trunk/engines/m4/mads_views.cpp
    scummvm/trunk/engines/m4/scene.cpp
    scummvm/trunk/engines/m4/scene.h

Modified: scummvm/trunk/engines/m4/globals.h
===================================================================
--- scummvm/trunk/engines/m4/globals.h	2010-08-25 06:02:18 UTC (rev 52363)
+++ scummvm/trunk/engines/m4/globals.h	2010-08-25 06:42:54 UTC (rev 52364)
@@ -229,8 +229,69 @@
 #define SET_GLOBAL(x,y) _madsVm->globals()->_globals[x] = y
 #define SET_GLOBAL32(x,y) { _madsVm->globals()->_globals[x] = (y) & 0xffff; _madsVm->globals()->_globals[(x) + 1] = (y) >> 16; }
 
-typedef Common::HashMap<uint16, uint16> IntStorage;
+union DataMapEntry {
+	bool *boolValue;
+	uint16 *uint16Value;
+	int *intValue;
+};
 
+typedef Common::HashMap<uint16, uint16> DataMapHash;
+
+enum DataMapType {BOOL, UINT16, INT};
+
+class DataMapWrapper {
+	friend class DataMap;
+private:
+	DataMapEntry _value;
+	DataMapType _type;
+public:
+	DataMapWrapper(bool *v) { _value.boolValue = v; _type = BOOL; }
+	DataMapWrapper(uint16 *v) { _value.uint16Value = v; _type = UINT16; }
+	DataMapWrapper(int16 *v) { _value.uint16Value = (uint16 *)v; _type = UINT16; }
+	DataMapWrapper(int *v) { _value.intValue = v; _type = INT; }
+
+	uint16 getIntValue() {
+		if (_type == BOOL) return *_value.boolValue ? 0xffff : 0;
+		else if (_type == UINT16) return *_value.uint16Value;
+		else return *_value.intValue;
+	}
+	void setIntValue(uint16 v) {
+		if (_type == BOOL) *_value.boolValue = v != 0;
+		else if (_type == UINT16) *_value.uint16Value = v;
+		else *_value.intValue = v;
+	}
+};
+
+#define MAP_DATA(V) _madsVm->globals()->_dataMap.addMapping(new DataMapWrapper(V))
+
+class DataMap {
+private:
+	DataMapHash _data;
+	Common::Array<DataMapWrapper *> _mapList;
+public:
+	DataMap() {
+		_mapList.push_back(NULL);
+	}
+	~DataMap() {
+		for (uint i = 1; i < _mapList.size(); ++i)
+			delete _mapList[i];
+	}
+	
+	void addMapping(DataMapWrapper *v) { _mapList.push_back(v); }
+	uint16 get(uint16 index) {
+		if (index < _mapList.size()) 
+			return _mapList[index]->getIntValue();
+
+		return _data[index];
+	}
+	void set(uint16 index, uint16 v) {
+		if (index < _mapList.size()) 
+			_mapList[index]->setIntValue(v);
+		else
+			_data[index] = v;
+	}
+};
+
 class MadsGlobals : public Globals {
 private:
 	struct MessageItem {
@@ -259,7 +320,7 @@
 	int previousScene;
 	int16 _nextSceneId;
 	uint16 actionNouns[3];
-	IntStorage _dataMap;
+	DataMap _dataMap;
 	int _difficultyLevel;
 
 	void loadMadsVocab();

Modified: scummvm/trunk/engines/m4/m4.h
===================================================================
--- scummvm/trunk/engines/m4/m4.h	2010-08-25 06:02:18 UTC (rev 52363)
+++ scummvm/trunk/engines/m4/m4.h	2010-08-25 06:42:54 UTC (rev 52364)
@@ -96,10 +96,10 @@
 
 enum M4GameType {
 	GType_Riddle = 1,
-	GType_Burger,
-	GType_RexNebular,
-	GType_DragonSphere,
-	GType_Phantom
+	GType_Burger = 2,
+	GType_RexNebular = 3, 
+	GType_DragonSphere = 4,
+	GType_Phantom = 5
 };
 
 enum Features {
@@ -224,8 +224,10 @@
 	MadsGlobals *globals() { return (MadsGlobals *)_globals; }
 	MadsScene *scene() { return (MadsScene *)_scene; }
 	void startScene(int sceneNum) {
-		if (!_scene)
+		if (!_scene) {
 			_scene = new MadsScene(this);
+			((MadsScene *)_scene)->initialise();
+		}
 		_scene->show();
 		_scene->loadScene(101);
 	}

Modified: scummvm/trunk/engines/m4/mads_logic.cpp
===================================================================
--- scummvm/trunk/engines/m4/mads_logic.cpp	2010-08-25 06:02:18 UTC (rev 52363)
+++ scummvm/trunk/engines/m4/mads_logic.cpp	2010-08-25 06:42:54 UTC (rev 52364)
@@ -24,9 +24,12 @@
  */
 
 #include "m4/m4.h"
+#include "m4/dialogs.h"
 #include "m4/mads_logic.h"
 #include "m4/scene.h"
 
+#define MAX_CALL_PARAMS 10
+
 namespace M4 {
 
 void MadsGameLogic::initialiseGlobals() {
@@ -139,6 +142,52 @@
 
 /*--------------------------------------------------------------------------*/
 
+const char *MadsSceneLogic::subFormatList[] = {"scene%d_enter", "scene%d_step", "scene%d_preaction", "scene%d_actions"};
+
+#define	OPSIZE8		0x40	///< when this bit is set - the operand size is 8 bits
+#define	OPSIZE16	0x80	///< when this bit is set - the operand size is 16 bits
+#define	OPMASK		0x3F	///< mask to isolate the opcode
+
+enum Opcodes {
+	OP_HALT	= 0, OP_IMM	= 1, OP_ZERO =  2, OP_ONE = 3, OP_MINUSONE = 4, OP_STR = 5, OP_DLOAD = 6,
+	OP_DSTORE = 7, OP_PAL = 8, OP_LOAD = 9, OP_GLOAD = 10, OP_STORE = 11, OP_GSTORE = 12,
+	OP_CALL = 13, OP_LIBCALL = 14, OP_RET = 15, OP_ALLOC = 16, OP_JUMP = 17, OP_JMPFALSE = 18,
+	OP_JMPTRUE = 19, OP_EQUAL = 20, OP_LESS = 21, OP_LEQUAL = 22, OP_NEQUAL = 23, OP_GEQUAL = 24,
+	OP_GREAT = 25, OP_PLUS = 26, OP_MINUS = 27, OP_LOR = 28, OP_MULT = 29, OP_DIV = 30,
+	OP_MOD = 31, OP_AND = 32, OP_OR = 33, OP_EOR = 34, OP_LAND = 35, OP_NOT = 36, OP_COMP = 37,
+	OP_NEG = 38, OP_DUP = 39,
+	TOTAL_OPCODES = 40
+};
+
+const char *MadsSceneLogic::_opcodeStrings[] = {
+	"HALT", "IMM", "ZERO", "ONE", "MINUSONE", "STR", "DLOAD", "DSTORE", NULL, "LOAD", "GLOAD",
+	"STORE", "GSTORE", "CALL", "LIBCALL", "RET", "ALLOC", "JUMP", "JMPFALSE", "JMPTRUE", "EQUAL",
+	"LESS", "LEQUAL", "NEQUAL", "GEQUAL", "GREAT", "PLUS", "MINUS", "LOR", "MULT", "DIV",
+	"MOD", "AND", "OR", "EOR", "LAND", "NOT", "COMP", "NEG", "DUP"
+};
+
+/**
+ * This method sets up the data map with pointers to all the common game objects. This allows the script engine to
+ * convert game specific offsets for various fields in the original game's data segment into a generic data index
+ * that will be common across all the MADS games
+ */
+void MadsSceneLogic::initialiseDataMap() {
+	// The unique order of these items must be maintained
+	MAP_DATA((uint16 *)&_madsVm->scene()->_abortTimersMode2);
+	MAP_DATA(&_madsVm->scene()->_abortTimers);
+	MAP_DATA(&_madsVm->_player._stepEnabled);
+	MAP_DATA(&_madsVm->scene()->_nextScene);
+	MAP_DATA(&_madsVm->scene()->_previousScene);
+	MAP_DATA(&_madsVm->_player._playerPos.x);
+	MAP_DATA(&_madsVm->_player._playerPos.y);
+	MAP_DATA(&_madsVm->_player._direction);
+	MAP_DATA(&_madsVm->_player._visible);
+}
+
+DataMap &MadsSceneLogic::dataMap() {
+	return _madsVm->globals()->_dataMap;
+}
+
 const char *MadsSceneLogic::formAnimName(char sepChar, int16 suffixNum) {
 	return MADSResourceManager::getResourceName(sepChar, _sceneNumber, EXTTYPE_NONE, NULL, suffixNum);
 }
@@ -177,10 +226,6 @@
 	strcpy(_madsVm->scene()->_aaName, newName);
 }
 
-IntStorage &MadsSceneLogic::dataMap() {
-	return _madsVm->globals()->_dataMap;
-}
-
 /*--------------------------------------------------------------------------*/
 
 uint16 MadsSceneLogic::loadSpriteSet(uint16 suffixNum, uint16 sepChar) {
@@ -220,44 +265,6 @@
 	// TODO:
 }
 
-void MadsSceneLogic::lowRoomsEntrySound() {
-	if (!_madsVm->globals()->_config.musicFlag) {
-		_madsVm->_sound->playSound(2);
-	} else {
-		// Play different sounds for each of the rooms
-		switch (_madsVm->globals()->sceneNumber) {
-		case 101:
-			_madsVm->_sound->playSound(11);
-			break;
-		case 102:
-			_madsVm->_sound->playSound(12);
-			break;
-		case 103:
-			_madsVm->_sound->playSound(3);
-			_madsVm->_sound->playSound(25);
-			break;
-		case 104:
-			_madsVm->_sound->playSound(10);
-			break;
-		case 105:
-			if ((_madsVm->globals()->previousScene < 104) || (_madsVm->globals()->previousScene > 108))
-				_madsVm->_sound->playSound(10);
-			break;
-		case 106:
-			_madsVm->_sound->playSound(13);
-			break;
-		case 107:
-			_madsVm->_sound->playSound(3);
-			break;
-		case 108:
-			_madsVm->_sound->playSound(15);
-			break;
-		default:
-			break;
-		}
-	}
-}
-
 void MadsSceneLogic::getPlayerSpritesPrefix() {
 	_madsVm->_sound->playSound(5);
 
@@ -315,17 +322,107 @@
 /*--------------------------------------------------------------------------*/
 
 /**
- * FIXME:
- * Currently I'm only working at providing manual implementation of the first Rex Nebular scene.
- * It will make more sense to convert the remaining game logic from the games into some
- * kind of bytecode scripts
+ * Loads the MADS.DAT file and loads the script data for the correct game/language
  */
+void MadsSceneLogic::initialiseScripts() {
+	Common::File f;
+	if (!f.open("mads.dat")) {
+		warning("Could not locate mads.dat file");
+		return;
+	}
 
+	// Validate that the file being read is a valid mads.dat file
+	char header[4];
+	f.read(&header[0], 4);
+	if (strncmp(header, "MADS", 4) != 0) {
+		warning("Invalid mads.dat file");
+		return;
+	}
+
+	// Get a list of the offsets of game blocks
+	uint32 v;
+	Common::Array<uint32> offsets;
+	while ((v = f.readUint32LE()) != 0)
+		offsets.push_back(v);
+
+	// Check the header of each block in turn
+	_scriptsData = NULL;
+	_scriptsSize = 0;
+
+	for (uint i = 0; i < offsets.size(); ++i) {
+		// Get the block header
+		f.seek(offsets[i]);
+		byte gameId = f.readByte();
+		byte language = f.readByte();
+		f.readByte();			// Language currently unused
+
+		// If this block isn't for the current game, skip it
+		if (_madsVm->getGameType() != (gameId + 2))
+			continue;
+		if ((language != 1) || (_madsVm->getLanguage() != Common::EN_ANY))
+			continue;
+
+		// Found script block for the given game and language. 
+		_scriptsSize = (i < (offsets.size() - 1)) ? offsets[i + 1] - offsets[i] : f.size() - offsets[i];
+		break;
+	}
+
+	if (!_scriptsSize) {
+		warning("Could not find appropriate scripts block for game in mads.dat file");
+		f.close();
+		return;
+	}
+
+	// Load up the list of subroutines into a hash map
+	uint32 blockOffset = f.pos() - 3;
+	uint32 subsStart = 0;	
+	for (;;) {
+		// Get next entry
+		Common::String subName;
+		char c;
+		while ((c = (char)f.readByte()) != '\0')
+			subName += c;
+		if (subName.empty())
+			// Reached end of subroutine list
+			break;
+
+		// Read in the offset of the routine
+		uint32 offset = f.readUint32LE();
+		if (_subroutines.empty()) {
+			// The first subroutine offset is used to reduce the amount of data to later load in. In essence,
+			// the subroutine index will not be separately loaded, since it's contents will be in the hash map
+			subsStart = offset;
+			_scriptsSize -= offset;
+		}
+
+		_subroutines[subName] = offset - subsStart;
+		_subroutineOffsets.push_back(offset - subsStart);
+	}
+
+	// Read in the remaining data
+	f.seek(blockOffset + subsStart, SEEK_SET);
+	_scriptsData = (byte *)malloc(_scriptsSize);
+	f.read(_scriptsData, _scriptsSize);
+
+	f.close();
+}
+
 void MadsSceneLogic::selectScene(int sceneNum) {
 	assert(sceneNum == 101);
 	_sceneNumber = sceneNum;
 
 	Common::set_to(&_spriteIndexes[0], &_spriteIndexes[50], 0);
+
+	// If debugging is turned on, show a debug warning if any of the scene methods aren't present
+	if (gDebugLevel > 0) {
+		for (int i = 0; i < 4; ++i) {
+			char buffer[20];
+			sprintf(buffer, subFormatList[i], sceneNum);
+			Common::HashMap<Common::String, uint32>::iterator it = _subroutines.find(Common::String(buffer));
+			if (it == _subroutines.end())
+				debugC(1, kDebugScript, "Scene method %s not found", buffer);
+		}
+	}
 }
 
 void MadsSceneLogic::setupScene() {
@@ -343,149 +440,492 @@
 	getAnimName();
 }
 
-void MadsSceneLogic::enterScene() {
-	for (int i = 1; i <= 7; ++i)
-		_spriteIndexes[i - 1] = loadSpriteSet(i, 'x');
-	_spriteIndexes[7] = loadSpriteSet(0xFFFF, 'm');
-	_spriteIndexes[8] = loadSpriteSet(1, 'b');
-	_spriteIndexes[9] = loadSpriteSet(2, 'b');
-	_spriteIndexes[10] = loadSpriteSet(0, 'a');
-	_spriteIndexes[11] = loadSpriteSet(1, 'a');
-	_spriteIndexes[12] = loadSpriteSet(8, 'x');
-	_spriteIndexes[13] = loadSpriteSet(0, 'x');
+/**
+ * Handles the logic when a scene is entered
+ */
+void MadsSceneLogic::doEnterScene() {
+	char buffer[20];
+	sprintf(buffer, subFormatList[SUBFORMAT_ENTER], _sceneNumber);
+	execute(Common::String(buffer));
+}
 
-	_spriteIndexes[15] = startCycledSpriteSequence(_spriteIndexes[0], false, 5, 0, 0, 25);
-	_spriteIndexes[16] = startCycledSpriteSequence(_spriteIndexes[1], false, 4, 0, 1, 0);
-	_spriteIndexes[17] = startCycledSpriteSequence(_spriteIndexes[2], false, 4, 0, 1, 0);
+/**
+ * Handles the script execution which is called regularly every frame
+ */
+void MadsSceneLogic::doSceneStep() {
+	char buffer[20];
+	sprintf(buffer, subFormatList[SUBFORMAT_STEP], _sceneNumber);
+	execute(Common::String(buffer));
+}
 
-	_madsVm->scene()->_sequenceList.addSubEntry(_spriteIndexes[17], SM_FRAME_INDEX, 7, 70);
+/**
+ * Handles and preactions before an action is started
+ */
+void MadsSceneLogic::doPreactions() {
+	char buffer[20];
+	sprintf(buffer, subFormatList[SUBFORMAT_PREACTIONS], _sceneNumber);
+	execute(Common::String(buffer));
+}
 
-	_spriteIndexes[18] = startReversibleSpriteSequence(_spriteIndexes[3], false, 10, 0, 0, 60);
-	_spriteIndexes[19] = startCycledSpriteSequence(_spriteIndexes[4], false, 5, 0, 1, 0);
-	_spriteIndexes[20] = startCycledSpriteSequence(_spriteIndexes[5], false, 10, 0, 2, 0);
-	_spriteIndexes[21] = startCycledSpriteSequence(_spriteIndexes[6], false, 6, 0, 0, 0);
-	_spriteIndexes[23] = startCycledSpriteSequence(_spriteIndexes[8], false, 6, 0, 10, 4);
-	_spriteIndexes[24] = startCycledSpriteSequence(_spriteIndexes[9], false, 6, 0, 32, 47);
+/**
+ * Handles any action that has been selected
+ */
+void MadsSceneLogic::doAction() {
+	char buffer[20];
+	sprintf(buffer, subFormatList[SUBFORMAT_ACTIONS], _sceneNumber);
+	execute(Common::String(buffer));
+}
 
-	activateHotspot(0x137, false);		// SHIELD MODULATOR
-	// shield_panel_opened = 0;
+/**
+ * Executes the script with the specified name
+ */
+void MadsSceneLogic::execute(const Common::String &scriptName) {
+	Common::HashMap<Common::String, uint32>::iterator it = _subroutines.find(scriptName);
+	if (it != _subroutines.end())
+		execute(it->_value);
+}
 
-	if (_madsVm->globals()->previousScene != -1)
-		_madsVm->globals()->_globals[10] = 0;
-	if (_madsVm->globals()->previousScene != -2) {
-		_madsVm->_player._playerPos = Common::Point(100, 152);
-	}
-	
-	if ((_madsVm->globals()->previousScene == 112) || 
-		((_madsVm->globals()->previousScene != -2) && (_spriteIndexes[29] != 0))) {
-		// Returning from probe cutscene?
-		_spriteIndexes[29] = -1;
-		_madsVm->_player._playerPos = Common::Point(161, 123);
-		_madsVm->_player._direction = 9;
+#define UNUSED_VAL 0xEAEAEAEA
+/**
+ * Executes the script at the specified offset
+ */
+void MadsSceneLogic::execute(uint32 subOffset) {
+	Common::Array<ScriptVar> locals;
+	Common::Stack<ScriptVar> stack;
+	char opcodeBuffer[100];
+	uint32 scriptOffset = subOffset;
+	uint32 param;
 
-		// TODO: Extra flags setting
-		_spriteIndexes[25] = startCycledSpriteSequence(_spriteIndexes[10], false, 3, 0, 0, 0);
-		_madsVm->scene()->_sequenceList.setAnimRange(_spriteIndexes[25], 17, 17);
-		activateHotspot(0x47, false);	// CHAIR
-		/*timer_unk1 = */_madsVm->scene()->_dynamicHotspots.add(0x47, 0x13F /*SIT_IN*/, -1,
-			Common::Rect(159, 84, 159+33, 84+36));
+	debugC(1, kDebugScript, "executing script at %xh", subOffset);
+
+	bool done = false;
+	while (!done) {
+		param = UNUSED_VAL;
+		byte opcode = _scriptsData[scriptOffset++];
+		sprintf(opcodeBuffer, "%.4x[%.2d] - %s", scriptOffset - 1, stack.size(), _opcodeStrings[opcode & OPMASK]);
+
+		switch (opcode & OPMASK) {
+		case OP_HALT:			// end of program
+		case OP_RET:
+			done = true;
+			break;
+
+		case OP_IMM:			// Loads immediate value onto stack
+			param = getParam(scriptOffset, opcode);
+			stack.push(ScriptVar(param));
+			break;
+
+		case OP_ZERO:			// loads zero onto stack
+			stack.push(ScriptVar((uint32)0));
+			break;
+
+		case OP_ONE:			// loads one onto stack
+			stack.push(ScriptVar(1));
+			break;
+
+		case OP_MINUSONE:		// loads minus one (0xffff) onto stack
+			stack.push(ScriptVar(0xffff));
+			break;
+
+		case OP_DLOAD: {		// Gets data variable
+			param = getParam(scriptOffset, opcode);
+			uint16 v = dataMap().get(param);
+			stack.push(ScriptVar(v));
+			break;
+		}
+
+		case OP_DSTORE:	{		// Stores data variable
+			param = getParam(scriptOffset, opcode); 
+			ScriptVar v = stack.pop();
+			dataMap().set(param, v.isInt() ? v : 0);
+			break;
+		}
 		
-		//if (_madsVm->globals()->previousScene == 112)
-		//	room101Check();
-	} else {
-		_spriteIndexes[26] = startCycledSpriteSequence(_spriteIndexes[11], false, 6, 0, 0, 0);
-		_madsVm->scene()->_sequenceList.setDepth(_spriteIndexes[26], 4);
-	}
+		case OP_LOAD:			// loads local variable onto stack
+			param = getParam(scriptOffset, opcode);
+			stack.push(locals[param]);
+			break;
 
-	_madsVm->globals()->loadQuoteSet(0x31, 0x32, 0x37, 0x38, 0x39, -1);
+		case OP_STORE:			// Pops a value and stores it in a local
+			// Get the local index and expand the locals store if necessary
+			param = getParam(scriptOffset, opcode);
+			while (param >= locals.size())
+				locals.push_back(ScriptVar());
 
-	if (_madsVm->globals()->_globals[10]) {
-		const char *animName = MADSResourceManager::getResourceName('S', 'e', EXTTYPE_AA, NULL, -1);
-		_madsVm->scene()->loadAnimation(animName, 71);
+			locals[param] = stack.pop();
+			break;
 
-		_madsVm->_player._playerPos = Common::Point(68, 140);
-		_madsVm->_player._direction = 4;
-		_madsVm->_player._visible = false;
-		_madsVm->_player._stepEnabled = false;
+		case OP_GLOAD:				// loads global variable onto stack
+			param = getParam(scriptOffset, opcode);
+			assert(param < TOTAL_NUM_VARIABLES);
+			stack.push(_madsVm->globals()->_globals[param]);
+			break;
 
-		dataMap()[0x56FC] = 0;
-		dataMap()[0x5482] = 0;
-		dataMap()[0x5484] = 30;
+		case OP_GSTORE:				// pops stack and stores in global variable
+			param = getParam(scriptOffset, opcode);
+			assert(param < TOTAL_NUM_VARIABLES);			
+			_madsVm->globals()->_globals[param] = stack.pop().get();
+			break;
+
+		case OP_CALL:				// procedure call
+			param = getParam(scriptOffset, opcode);
+			assert(param < _subroutineOffsets.size());
+			execute(_subroutineOffsets[param]);
+			break;
+
+		case OP_LIBCALL:		// library procedure or function call
+			param = getParam(scriptOffset, opcode);
+			callSubroutine(param, stack);
+			break;
+
+		case OP_JUMP:	// unconditional jump
+			param = subOffset + getParam(scriptOffset, opcode);
+			scriptOffset = param;
+			break;
+
+		case OP_JMPFALSE:	// conditional jump
+			param = subOffset + getParam(scriptOffset, opcode);
+			if (stack.pop().get() == 0)
+				// Condition satisfied - do the jump
+				scriptOffset = param;
+			break;
+					   
+		case OP_JMPTRUE:	// conditional jump
+			param = subOffset + getParam(scriptOffset, opcode);
+			if (stack.pop().get() != 0)
+				// Condition satisfied - do the jump
+				scriptOffset = param;
+			break;
+
+		case OP_EQUAL:			// tests top two items on stack for equality
+		case OP_LESS:			// tests top two items on stack
+		case OP_LEQUAL:			// tests top two items on stack
+		case OP_NEQUAL:			// tests top two items on stack
+		case OP_GEQUAL:			// tests top two items on stack
+		case OP_GREAT:			// tests top two items on stack
+		case OP_LOR:			// logical or of top two items on stack and replaces with result
+		case OP_LAND:			// logical ands top two items on stack and replaces with result
+			{
+				uint32 param2 = stack.pop().get();
+				uint32 param1 = stack.pop().get();
+
+				// Do the comparison
+				uint32 tmp = 0;
+				switch (opcode) {
+				case OP_EQUAL:  tmp = (param1 == param2); break;
+				case OP_LESS:   tmp = (param1 <  param2); break;
+				case OP_LEQUAL: tmp = (param1 <= param2); break;
+				case OP_NEQUAL: tmp = (param1 != param2); break;
+				case OP_GEQUAL: tmp = (param1 >= param2); break;
+				case OP_GREAT:  tmp = (param1 >  param2); break;
+
+				case OP_LOR:    tmp = (param1 || param2); break;
+				case OP_LAND:   tmp = (param1 && param2); break;
+				}
+
+				stack.push(ScriptVar(tmp));
+			}
+			break;
+
+		case OP_PLUS:			// adds top two items on stack and replaces with result
+		case OP_MINUS:			// subs top two items on stack and replaces with result
+		case OP_MULT:			// multiplies top two items on stack and replaces with result
+		case OP_DIV:			// divides top two items on stack and replaces with result
+		case OP_MOD:			// divides top two items on stack and replaces with modulus
+		case OP_AND:			// bitwise ands top two items on stack and replaces with result
+		case OP_OR:				// bitwise ors top two items on stack and replaces with result
+		case OP_EOR:			// bitwise exclusive ors top two items on stack and replaces with result
+			{
+				uint32 param2 = stack.pop().get();
+				uint32 param1 = stack.pop().get();
+
+				// replace other operand with result of operation
+				switch (opcode) {
+				case OP_PLUS:   param1 += param2; break;
+				case OP_MINUS:  param1 -= param2; break;
+				case OP_MULT:   param1 *= param2; break;
+				case OP_DIV:    param1 /= param2; break;
+				case OP_MOD:    param1 %= param2; break;
+				case OP_AND:    param1 &= param2; break;
+				case OP_OR:     param1 |= param2; break;
+				case OP_EOR:    param1 ^= param2; break;
+				}
+				
+				stack.push(ScriptVar(param1));
+			}
+			break;
+
+		case OP_NOT:			// logical nots top item on stack
+			param = stack.pop().get();
+			stack.push(ScriptVar((uint32)!param & 0xffff));
+			break;
+
+		case OP_COMP:			// complements top item on stack
+			param = stack.pop().get();
+			stack.push(ScriptVar(~param & 0xffff));
+			break;
+
+		case OP_NEG:			// negates top item on stack
+			param = stack.pop().get();
+			stack.push(ScriptVar(((uint32)-(int32)param) & 0xffff));
+			break;
+
+		case OP_DUP:			// duplicates top item on stack
+			stack.push(stack.top());
+			break;
+
+		default:
+			error("execute() - Unknown opcode");
+		}
+
+		// check for stack size
+		assert(stack.size() < 100);
+
+		if (gDebugLevel > 0) {
+			if (param != UNUSED_VAL)
+				sprintf(opcodeBuffer + strlen(opcodeBuffer), "\t%d", param);
+			debugC(2, kDebugScript, opcodeBuffer);
+		}
 	}
 
-	_madsVm->globals()->_dataMap[0x5486] = 0;
-	lowRoomsEntrySound();
+	debugC(1, kDebugScript, "finished executing script");
+
+	// make sure stack is unwound
+	assert(stack.size() == 0);
 }
 
-void MadsSceneLogic::doPreactions() {
-	warning("Still to do preactions logic");
+uint32 MadsSceneLogic::getParam(uint32 &scriptOffset, int opcode) {
+	switch (opcode & (~OPMASK)) {
+	case OPSIZE8:
+		return _scriptsData[scriptOffset++];
+	case OPSIZE16: {
+		uint16 v = READ_LE_UINT16(&_scriptsData[scriptOffset]);
+		scriptOffset += sizeof(uint16);
+		return v;
+	}
+	default: {
+		uint32 v = READ_LE_UINT32(&_scriptsData[scriptOffset]);
+		scriptOffset += sizeof(uint32);
+		return v;
+	}
+	}
 }
 
-void MadsSceneLogic::doAction() {
-	warning("Still to do actions logic");
+/**
+ * Support method for extracting the required number of parameters needed for a library routine call
+ */
+void MadsSceneLogic::getCallParameters(int numParams, Common::Stack<ScriptVar> &stack, ScriptVar *callParams) {
+	assert(numParams <= MAX_CALL_PARAMS);
+	for (int i = 0; i < numParams; ++i, ++callParams) 
+		*callParams = stack.pop();
 }
 
-void MadsSceneLogic::doSceneStep() {
-	// TODO: Sound handling
-	
-	switch (_madsVm->scene()->_abortTimers) {
-	case 70:
-		_madsVm->_sound->playSound(9);
+#define EXTRACT_PARAMS(n) getCallParameters(n, stack, p)
+
+void MadsSceneLogic::callSubroutine(int subIndex, Common::Stack<ScriptVar> &stack) {
+	ScriptVar p[MAX_CALL_PARAMS];
+
+	switch (subIndex) {
+	case 1: {
+		// dialog_show
+		EXTRACT_PARAMS(1);
+		Dialog *dlg = new Dialog(_vm, p[0].getStr(), "TODO: Proper Title");
+		_vm->_viewManager->addView(dlg);
+		_vm->_viewManager->moveToFront(dlg);
 		break;
-	case 71:
-		_madsVm->globals()->_globals[10] = 0;
-		_madsVm->_player._visible = true;
-		_madsVm->_player._stepEnabled = true;
+	}
 
-		_madsVm->_player._priorTimer = _madsVm->_currentTimer - _madsVm->_player._ticksAmount;
+	case 2:
+		// SequenceList_remove
+		EXTRACT_PARAMS(1);
+		_madsVm->scene()->_sequenceList.remove(p[0]);
 		break;
-	case 72:
-	case 73:
-		// TODO: Method that should be scripted
+
+	case 3:
+	case 6:
+	case 20: {
+		// 3: start_reversible_sprite_sequence
+		// 6: start_cycled_sprite_sequence
+		// 20: start_sprite_sequence3
+		EXTRACT_PARAMS(6);
+		int idx;
+		if (subIndex == 3)
+			idx = startReversibleSpriteSequence(p[0], p[1] != 0, p[2], p[3], p[4], p[5]);
+		else if (subIndex == 6)
+			idx = startCycledSpriteSequence(p[0], p[1], p[2], p[3], p[4], p[5]);
+		else
+			idx = startSpriteSequence3(p[0], p[1] != 0, p[2], p[3], p[4], p[5]);
+		stack.push(ScriptVar(idx));
 		break;
+	}
 
-	default:
+	case 4:
+		// SequenceList_setAnimRange
+		EXTRACT_PARAMS(3);
+		_madsVm->scene()->_sequenceList.setAnimRange(p[0], p[1], p[2]);
 		break;
+
+	case 5:
+		// SequenceList_addSubEntry
+		EXTRACT_PARAMS(4);
+		stack.push(ScriptVar(_madsVm->scene()->_sequenceList.addSubEntry(p[0], (SequenceSubEntryMode)p[1].get(), p[2], p[3])));
+		break;
+
+	case 7: {
+		// quotes_get_pointer
+		EXTRACT_PARAMS(1);
+		const char *quoteStr = _madsVm->globals()->getQuote(p[0]);
+		stack.push(ScriptVar(quoteStr));
+		break;
 	}
 
-	// Wake up message sequence
-	Animation *anim = _madsVm->scene()->activeAnimation();
-	if (anim) {
-		if ((anim->getCurrentFrame() == 6) && (dataMap()[0x5482] == 0)) {
-			dataMap()[0x5482]++;
-			_madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]), 
-				0x1110, 0, 0, 240, _madsVm->globals()->getQuote(49));
-			dataMap()[0x5484] += 14;
-		}
+	case 8: {
+		// KernelMessageList_add
+		EXTRACT_PARAMS(8);
+		int msgIndex = _madsVm->scene()->_kernelMessages.add(Common::Point(p[0], p[1]), p[2],
+			p[3], p[4], p[5] | (p[6] << 16), p[7].getStr());
+		stack.push(ScriptVar(msgIndex));
+		break;
+	}
 
-		if ((anim->getCurrentFrame() == 7) && (dataMap()[0x5482] == 1)) {
-			dataMap()[0x5482]++;
-			_madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]), 
-				0x1110, 0, 0, 240, _madsVm->globals()->getQuote(54));
-			dataMap()[0x5484] += 14;
-		}
+	case 9:
+		// SequenceList_unk3
+		EXTRACT_PARAMS(1);
+		// TODO: Implement unk3 method
+//		stack.push(ScriptVar(_madsVm->scene()->_sequenceList.unk3(p[0])));
+		break;
 
-		if ((anim->getCurrentFrame() == 10) && (dataMap()[0x5482] == 2)) {
-			dataMap()[0x5482]++;
-			_madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]), 
-				0x1110, 0, 0, 240, _madsVm->globals()->getQuote(55));
-			dataMap()[0x5484] += 14;
-		}
+	case 10:
+		// start_sound
+		EXTRACT_PARAMS(1);
+		_madsVm->_sound->playSound(p[0]);
+		break;
 
-		if ((anim->getCurrentFrame() == 17) && (dataMap()[0x5482] == 3)) {
-			dataMap()[0x5482]++;
-			_madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]), 
-				0x1110, 0, 0, 240, _madsVm->globals()->getQuote(56));
-			dataMap()[0x5484] += 14;
+	case 11:
+		// SceneLogic_formAnimName
+		EXTRACT_PARAMS(2);
+		stack.push(ScriptVar(formAnimName((char)p[0], p[1])));
+		break;
+
+	case 12:
+		// SpriteList_addSprites
+		EXTRACT_PARAMS(2);
+		stack.push(ScriptVar(_madsVm->scene()->_spriteSlots.addSprites(p[0].getStr(), false, p[1])));
+		break;
+
+	case 13:
+		// hotspot_activate
+		EXTRACT_PARAMS(2);
+		// TODO: Implement setActive version that takes in a hotspot Id
+//		_madsVm->scene()->getSceneResources().hotspots->setActive(p[0], p[1] != 0);
+		break;
+
+	case 14: {
+		// DynamicHotspots_add
+		EXTRACT_PARAMS(7);
+		int idx = _madsVm->scene()->_dynamicHotspots.add(p[0], p[1], p[2], 
+			Common::Rect(p[6], p[5], p[6] + p[4], p[5] + p[3]));
+		stack.push(ScriptVar(idx));
+		break;
+	}
+
+	case 15:
+		// SequenceList_setDepth
+		EXTRACT_PARAMS(2);
+		_madsVm->scene()->_sequenceList.setDepth(p[0], p[1]);
+		break;
+
+	case 16: {
+		// quotes_load
+		// Quotes loading can take an arbitrary number of quote Ids, terminated by a 0
+		int firstId = -1;
+		int quoteId;
+		while ((quoteId = stack.pop()) != 0) {
+			if (firstId == -1)
+				firstId = quoteId;
+			_madsVm->globals()->loadQuote(quoteId);
 		}
 
-		if ((anim->getCurrentFrame() == 20) && (dataMap()[0x5482] == 4)) {
-			dataMap()[0x5482]++;
-			_madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]), 
-				0x1110, 0, 0, 240, _madsVm->globals()->getQuote(50));
-			dataMap()[0x5484] += 14;
-		}
-	}		
+		if (firstId != -1)
+			stack.push(ScriptVar(_madsVm->globals()->getQuote(firstId)));
+		break;
+	}
+
+	case 17: {
+		// form_resource_name
+		EXTRACT_PARAMS(4);
+		const char *suffix = NULL;
+		if (p[4].isInt()) {
+			// If integer provided for suffix, it must be a value of 0 (NULL)
+			uint32 vTemp = p[4] | stack.pop();
+			assert(!vTemp);
+		} else
+			suffix = p[4].getStr();
+
+		stack.push(ScriptVar(MADSResourceManager::getResourceName((char)p[1], p[0], (ExtensionType)p[3].get(),
+			suffix, (int16)p[2])));
+		break;
+	}
+
+	case 18:
+		// MadsScene_loadAnimation
+		EXTRACT_PARAMS(3);
+		_madsVm->scene()->loadAnimation(p[1].getStr(), p[0]);
+		break;
+
+	case 19: {
+		// Action_isAction
+		int verbId = stack.pop();
+		int objectNameId = (verbId == 0) ? 0 : stack.pop();
+		int indirectObjectId = (objectNameId == 0) ? 0 : stack.pop();
+
+		stack.push(ScriptVar(_madsVm->scene()->_action.isAction(verbId, objectNameId, indirectObjectId)));
+		break;
+	}
+
+	case 21:
+		// DynamicHotspots_remove
+		EXTRACT_PARAMS(1);
+		_madsVm->scene()->_dynamicHotspots.remove(p[0]);
+		break;
+
+	case 22: {
+		// object_is_present
+		EXTRACT_PARAMS(1);
+		const MadsObject *obj = _madsVm->globals()->getObject(p[0]);
+		stack.push(ScriptVar((obj->roomNumber == _madsVm->scene()->_currentScene)));
+		break;
+	}
+
+	case 23:
+		// inventory_add
+		EXTRACT_PARAMS(1);
+		_madsVm->scene()->getInterface()->addObjectToInventory(p[0]);
+		break;
+
+	case 24: {
+		// dialog_picture_show
+		EXTRACT_PARAMS(3);
+		int messageId = p[0] | (p[1] << 16);
+		int objectNum = p[2];
+		warning("TODO: Implement dialog with picture. MessageId=%d, objectNum=%d", messageId, objectNum);
+		break;
+	}
+
+	case 25: {
+		// object_is_in_inventory
+		EXTRACT_PARAMS(1);
+		const MadsObject *obj = _madsVm->globals()->getObject(p[0]);
+		stack.push(ScriptVar(obj->isInInventory()));
+		break;
+	}
+
+	default:
+		error("Unknown subroutine %d called", subIndex);
+		break;
+	}
 }
 
-}
+#undef EXTRACT_PARAMS
+
+}
\ No newline at end of file

Modified: scummvm/trunk/engines/m4/mads_logic.h
===================================================================
--- scummvm/trunk/engines/m4/mads_logic.h	2010-08-25 06:02:18 UTC (rev 52363)
+++ scummvm/trunk/engines/m4/mads_logic.h	2010-08-25 06:42:54 UTC (rev 52364)
@@ -29,10 +29,38 @@
 #ifndef M4_MADS_LOGIC_H
 #define M4_MADS_LOGIC_H
 
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "common/stack.h"
 #include "m4/mads_views.h"
 
 namespace M4 {
 
+union ScriptVarValue {
+	const char *strValue;
+	uint32 intValue;
+};
+
+/**
+ * Specifies a script variable that either be a 32-bit unsigned integer or a string pointer
+ */
+class ScriptVar {
+private:
+	ScriptVarValue _value;
+	bool _isInt;
+public:
+	ScriptVar(uint32 v = 0) { _value.intValue = v; _isInt = true; }
+	ScriptVar(const char *s) { _value.strValue = s; _isInt = false; }
+
+	void set(uint32 v) { _value.intValue = v; _isInt = true; }
+	void set(const char *s) { _value.strValue = s; _isInt = false; }
+	const char *getStr() const { assert(!_isInt); return _value.strValue; }
+	uint32 get() const { assert(_isInt); return _value.intValue; }
+	bool isInt() const { return _isInt; }
+
+	operator int() { return get(); }
+};
+
 class MadsSceneLogic {
 private:
 	// Library interface methods
@@ -41,27 +69,45 @@
 	uint16 startCycledSpriteSequence(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks);
 	uint16 startSpriteSequence3(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks);
 	void activateHotspot(int idx, bool active);
-	void lowRoomsEntrySound();
 	void getPlayerSpritesPrefix();
 	void getPlayerSpritesPrefix2();
 private:
 	int _sceneNumber;
 	int16 _spriteIndexes[50];
+	byte *_scriptsData;
+	int _scriptsSize;
+	Common::HashMap<Common::String, uint32> _subroutines;
+	Common::Array<uint32> _subroutineOffsets;
 
+	enum SubFormatIndex {SUBFORMAT_ENTER, SUBFORMAT_STEP, SUBFORMAT_PREACTIONS, SUBFORMAT_ACTIONS};
+	static const char *subFormatList[];
+	static const char *_opcodeStrings[];
+
 	// Support functions
 	const char *formAnimName(char sepChar, int16 suffixNum);
 	void getSceneSpriteSet();
 	void getAnimName();
 
-	IntStorage &dataMap();
+	DataMap &dataMap();
+	void getCallParameters(int numParams, Common::Stack<ScriptVar> &stack, ScriptVar *callParams);
 public:
+	MadsSceneLogic() { _scriptsData = NULL; }
+	~MadsSceneLogic() { delete _scriptsData; }
+
+	void initialiseScripts();
+	void initialiseDataMap();
 	void selectScene(int sceneNum);
 
 	void setupScene();
-	void enterScene();
+	void doEnterScene();
 	void doPreactions();
 	void doAction();
 	void doSceneStep();
+
+	void execute(const Common::String &scriptName);
+	void execute(uint32 scriptOffset);
+	uint32 getParam(uint32 &scriptOffset, int opcode);
+	void callSubroutine(int subIndex, Common::Stack<ScriptVar> &stack);
 };
 
 class MadsGameLogic {

Modified: scummvm/trunk/engines/m4/mads_scene.cpp
===================================================================
--- scummvm/trunk/engines/m4/mads_scene.cpp	2010-08-25 06:02:18 UTC (rev 52363)
+++ scummvm/trunk/engines/m4/mads_scene.cpp	2010-08-25 06:42:54 UTC (rev 52364)
@@ -68,6 +68,7 @@
 	_interfaceSurface = new MadsInterfaceView(vm);
 	_showMousePos = false;
 	_mouseMsgIndex = -1;
+	_previousScene = -1;
 }
 
 MadsScene::~MadsScene() {
@@ -174,7 +175,7 @@
 
 	// Do any scene specific setup
 	if (_vm->getGameType() == GType_RexNebular)
-		_sceneLogic.enterScene();
+		_sceneLogic.doEnterScene();
 
 	// Miscellaneous player setup
 	_madsVm->_player._destPos = _madsVm->_player._destPos;

Modified: scummvm/trunk/engines/m4/mads_scene.h
===================================================================
--- scummvm/trunk/engines/m4/mads_scene.h	2010-08-25 06:02:18 UTC (rev 52363)
+++ scummvm/trunk/engines/m4/mads_scene.h	2010-08-25 06:42:54 UTC (rev 52364)
@@ -111,6 +111,10 @@
 public:
 	MadsScene(MadsEngine *vm);
 	virtual ~MadsScene();
+	void initialise() {
+		_sceneLogic.initialiseScripts();
+		_sceneLogic.initialiseDataMap();
+	}
 
 	// Methods that differ between engines
 	virtual void loadScene(int sceneNumber);

Modified: scummvm/trunk/engines/m4/mads_views.cpp
===================================================================
--- scummvm/trunk/engines/m4/mads_views.cpp	2010-08-25 06:02:18 UTC (rev 52363)
+++ scummvm/trunk/engines/m4/mads_views.cpp	2010-08-25 06:42:54 UTC (rev 52364)
@@ -394,14 +394,20 @@
 			return -1;
 	}
 
+	// Append on a '.SS' suffix if the resource doesn't already have an extension
+	char buffer[100];
+	strncpy(buffer, resName, 95);
+	if (!strchr(buffer, '.'))
+		strcat(buffer, ".SS");
+
 	// Get the sprite set
-	Common::SeekableReadStream *data = _vm->res()->get(resName);
-	SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), resName, false, flags);
+	Common::SeekableReadStream *data = _vm->res()->get(buffer);
+	SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), buffer, false, flags);
 	spriteSet->translate(_madsVm->_palette);
 	assert(spriteSet != NULL);
 
 	_sprites.push_back(spriteSet);
-	_vm->res()->toss(resName);
+	_vm->res()->toss(buffer);
 
 	return _sprites.size() - 1;
 }

Modified: scummvm/trunk/engines/m4/scene.cpp
===================================================================
--- scummvm/trunk/engines/m4/scene.cpp	2010-08-25 06:02:18 UTC (rev 52363)
+++ scummvm/trunk/engines/m4/scene.cpp	2010-08-25 06:42:54 UTC (rev 52364)
@@ -51,6 +51,7 @@
 	_interfacePal = NULL;
 	_interfaceSurface = NULL;
 	_vm->_rails->setCodeSurface(_walkSurface);
+	_currentScene = -1;
 }
 
 Scene::~Scene() {

Modified: scummvm/trunk/engines/m4/scene.h
===================================================================
--- scummvm/trunk/engines/m4/scene.h	2010-08-25 06:02:18 UTC (rev 52363)
+++ scummvm/trunk/engines/m4/scene.h	2010-08-25 06:42:54 UTC (rev 52364)
@@ -77,9 +77,6 @@
 private:
 	HotSpotList _sceneHotspots;
 protected:
-	int _currentScene;
-	int _previousScene;
-	int _nextScene;
 	GameInterfaceView *_interfaceSurface;
 	M4Surface *_backgroundSurface;
 	M4Surface *_walkSurface;
@@ -87,6 +84,10 @@
 	RGBList *_interfacePal;
 	SceneResources *_sceneResources;
 public:
+	int _currentScene;
+	int _previousScene;
+	int _nextScene;
+public:
 	Scene(MadsM4Engine *vm, SceneResources *res);
 	virtual ~Scene();
 


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list