[Scummvm-cvs-logs] scummvm master -> 40dd4f89baed798e431b671954e28755ca1aa73b

fuzzie fuzzie at fuzzie.org
Thu Apr 7 00:37:42 CEST 2011


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
3bbeee90c0 MOHAWK: Handle alignment byte for some targeting modes.
bbe2c437a8 MOHAWK: Replace most of the LBCode interpreter.
40dd4f89ba MOHAWK: Support LB script flow control.


Commit: 3bbeee90c02e613ab2fcf870a26dbcfb3aad60e1
    https://github.com/scummvm/scummvm/commit/3bbeee90c02e613ab2fcf870a26dbcfb3aad60e1
Author: Alyssa Milburn (fuzzie at fuzzie.org)
Date: 2011-04-06T15:26:27-07:00

Commit Message:
MOHAWK: Handle alignment byte for some targeting modes.

Changed paths:
    engines/mohawk/livingbooks.cpp



diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index 522d437..3f53eae 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -1931,6 +1931,11 @@ LBScriptEntry *LBItem::parseScriptEntry(uint16 type, uint16 &size, Common::Seeka
 				warning("ignoring target '%s' in script entry", target.c_str());
 				size -= target.size() + 1;
 			}
+
+			if (size % 2 == 1) {
+				stream->skip(1);
+				size--;
+			}
 		}
 
 		if (entry->argc) {


Commit: bbe2c437a86c6435012d5699dad8e9b4c88ca60c
    https://github.com/scummvm/scummvm/commit/bbe2c437a86c6435012d5699dad8e9b4c88ca60c
Author: Alyssa Milburn (fuzzie at fuzzie.org)
Date: 2011-04-06T15:30:09-07:00

Commit Message:
MOHAWK: Replace most of the LBCode interpreter.

Changed paths:
    engines/mohawk/livingbooks.cpp
    engines/mohawk/livingbooks_code.cpp
    engines/mohawk/livingbooks_code.h



diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index 3f53eae..3d22683 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -2690,24 +2690,6 @@ void LBItem::setNextTime(uint16 min, uint16 max, uint32 start) {
 	debug(9, "nextTime is now %d frames away", _nextTime - (uint)(_vm->_system->getMillis() / 16));
 }
 
-bool LBValue::operator==(const LBValue &x) const {
-	if (type != x.type) return false;
-
-	switch (type) {
-	case kLBValueString:
-		return string == x.string;
-
-	case kLBValueInteger:
-		return integer == x.integer;
-	default:
-		error("Unknown type when testing for equality");
-	}
-}
-
-bool LBValue::operator!=(const LBValue &x) const {
-	return !(*this == x);
-}
-
 enum LBTokenType {
 	kLBNoToken,
 	kLBNameToken,
diff --git a/engines/mohawk/livingbooks_code.cpp b/engines/mohawk/livingbooks_code.cpp
index a11ac1c..c0718d9 100644
--- a/engines/mohawk/livingbooks_code.cpp
+++ b/engines/mohawk/livingbooks_code.cpp
@@ -28,18 +28,76 @@
 
 namespace Mohawk {
 
+bool LBValue::operator==(const LBValue &x) const {
+	if (type != x.type) {
+		if (isNumeric() && x.isNumeric())
+			return toDouble() == x.toDouble();
+		else
+			return false;
+	}
+
+	switch (type) {
+	case kLBValueString:
+		return string == x.string;
+	case kLBValueInteger:
+		return integer == x.integer;
+	case kLBValueReal:
+		return real == x.real;
+	case kLBValuePoint:
+		return point == x.point;
+	case kLBValueRect:
+		return rect == x.rect;
+	case kLBValueItemPtr:
+		return item == x.item;
+	default:
+		error("Unknown type when testing for equality");
+	}
+}
+
+bool LBValue::operator!=(const LBValue &x) const {
+	return !(*this == x);
+}
+
+bool LBValue::isNumeric() const {
+	if (type == kLBValueInteger || type == kLBValueReal)
+		return true;
+
+	// TODO: string checks
+
+	return false;
+}
+
+bool LBValue::isZero() const {
+	return toInt() == 0; // FIXME
+}
+
+int LBValue::toInt() const {
+	return integer; // FIXME
+}
+
+double LBValue::toDouble() const {
+	return real; // FIXME
+}
+
 LBCode::LBCode(MohawkEngine_LivingBooks *vm) : _vm(vm) {
 	Common::SeekableSubReadStreamEndian *bcodStream = _vm->wrapStreamEndian(ID_BCOD, 1000);
+
 	uint32 totalSize = bcodStream->readUint32();
 	if (totalSize != (uint32)bcodStream->size())
 		error("BCOD had size %d, but claimed to be of size %d", bcodStream->size(), totalSize);
-	size = bcodStream->readUint32();
-	if (size + 8 > totalSize)
-		error("BCOD code was of size %d, beyond size %d", size, totalSize);
-	data = new byte[size];
-	bcodStream->read(data, size);
+	_size = bcodStream->readUint32();
+	if (_size + 8 > totalSize)
+		error("BCOD code was of size %d, beyond size %d", _size, totalSize);
+
+	_data = new byte[_size];
+	bcodStream->read(_data, _size);
+
 	uint16 pos = 0;
 	while (bcodStream->pos() < bcodStream->size()) {
+		if (bcodStream->pos() + 1 == bcodStream->size()) {
+			warning("ran out of bytes while reading strings");
+			break;
+		}
 		uint16 unknown = bcodStream->readUint16();
 		if (unknown != 0) {
 			warning("unknown was %04x, not zero, while reading strings", unknown);
@@ -48,287 +106,661 @@ LBCode::LBCode(MohawkEngine_LivingBooks *vm) : _vm(vm) {
 			break;
 		}
 		Common::String string = _vm->readString(bcodStream);
-		strings[pos] = string;
+		_strings[pos] = string;
 		debug(2, "read '%s' from BCOD at 0x%04x", string.c_str(), pos);
 		pos += 2 + string.size() + 1;
 	}
 }
 
 LBCode::~LBCode() {
-	delete[] data;
+	delete[] _data;
 }
 
-Common::Array<LBValue> LBCode::readParams(LBItem *src, uint32 &offset) {
-	Common::Array<LBValue> params;
-
-	if (offset + 1 >= size)
-		error("went off the end of code");
+LBValue LBCode::runCode(LBItem *src, uint32 offset) {
+	// TODO: re-entrancy issues?
+	_currSource = src;
+	_currOffset = offset;
 
-	byte numParams = data[offset];
-	offset++;
+	return runCode(kTokenEndOfFile);
+}
 
-	if (!numParams) {
-		debugN("()\n");
-		return params;
+void LBCode::nextToken() {
+	if (_currOffset + 1 >= _size) {
+		// TODO
+		warning("went off the end of code");
+		_currToken = kTokenEndOfFile;
+		_currValue = LBValue();
+		return;
 	}
 
-	byte nextToken = data[offset];
-	offset++;
-	if (nextToken != kLBCodeTokenOpenBracket)
-		error("missing ( before code parameter list (got %02x)", nextToken);
-	debugN("(");
+	_currToken = _data[_currOffset++];
 
-	for (uint i = 0; i < numParams; i++) {
-		if (i != 0) {
-			nextToken = data[offset];
-			offset++;
-			if (nextToken != ',')
-				error("missing , between code parameters (got %02x)", nextToken);
-			debugN(", ");
+	// We slurp any value associated with the parameter here too, to simplify things.
+	switch (_currToken) {
+	case kTokenIdentifier:
+		{
+		uint16 offset = READ_BE_UINT16(_data + _currOffset);
+		// TODO: check string exists
+		_currValue = _strings[offset];
+		_currOffset += 2;
 		}
+		break;
 
-		nextToken = data[offset];
-		offset++;
-
-		LBValue nextValue;
-
-		switch (nextToken) {
-		case kLBCodeTokenLiteral:
-			{
-			byte literalType = data[offset];
-			offset++;
-			if (literalType == kLBCodeLiteralInteger) {
-				uint16 intValue = READ_BE_UINT16(data + offset);
-				offset += 2;
-				nextValue.type = kLBValueInteger;
-				nextValue.integer = intValue;
-				debugN("%d", nextValue.integer);
-			} else
-				error("unknown literal type %02x in code", literalType);
-			}
+	case kTokenLiteral:
+		{
+		byte literalType = _data[_currOffset++];
+		switch (literalType) {
+		case kLBCodeLiteralInteger:
+			_currValue = READ_BE_UINT16(_data + _currOffset);
+			_currOffset += 2;
 			break;
+		default:
+			error("unknown kTokenLiteral type %02x", literalType);
+		}
+		}
+		break;
 
-		case kLBCodeTokenString:
-			{
-			uint16 stringOffset = READ_BE_UINT16(data + offset);
-			offset += 2;
-			// TODO: check string exists
-			nextValue.type = kLBValueString;
-			nextValue.string = strings[stringOffset];
-			debugN("\"%s\"", nextValue.string.c_str());
-			}
-			break;
+	case kTokenConstMode:
+	case kTokenConstEventId:
+	case 0x5e: // TODO: ??
+	case kTokenKeycode:
+		_currValue = READ_BE_UINT16(_data + _currOffset);
+		_currOffset += 2;
+		break;
 
-		case kLBCodeTokenChar:
-			{
-			uint16 stringOffset = READ_BE_UINT16(data + offset);
-			offset += 2;
-			// TODO: check string exists
-			nextValue.type = kLBValueString;
-			nextValue.string = strings[stringOffset];
-			debugN("'%s'", nextValue.string.c_str());
-			}
-			break;
+	case kTokenGeneralCommand:
+	case kTokenItemCommand:
+	case kTokenNotifyCommand:
+	case kTokenPropListCommand:
+	case kTokenRectCommand:
+		_currValue = _data[_currOffset++];
+		//_currValue = READ_BE_UINT16(_data + _currOffset);
+		//_currOffset += 2;
+		break;
 
-		case kLBCodeTokenLong: // FIXME: wrong?
-			{
-			uint32 intValue = READ_BE_UINT32(data + offset);
-			offset += 4;
-			nextValue.type = kLBValueInteger;
-			nextValue.integer = intValue;
-			debugN("%d", nextValue.integer);
-			}
-			break;
+	case kTokenString:
+		{
+		uint16 offset = READ_BE_UINT16(_data + _currOffset);
+		// TODO: check string exists
+		_currValue = _strings[offset];
+		_currOffset += 2;
+		}
+		break;
 
-		case 0x31:
-			{
-			// TODO
-			uint16 intValue = READ_BE_UINT16(data + offset);
-			offset += 2;
-			nextValue.type = kLBValueInteger;
-			nextValue.integer = intValue;
-			debugN("%d", nextValue.integer);
-			}
-			break;
+	default:
+		_currValue = LBValue();
+		break;
+	}
+}
 
-		case 0x4d:
-			// TODO
-			runCodeCommand(src, offset);
-			break;
+LBValue LBCode::runCode(byte terminator) {
+	LBValue result;
 
-		case 0x5f:
-			// keycode
-			nextValue.type = kLBValueInteger;
-			nextValue.integer = data[offset];
-			debugN("%d", nextValue.integer);
-			offset++;
-			offset++; // TODO
+	while (true) {
+		nextToken();
+		if (_currToken == kTokenEndOfFile)
 			break;
+		parseStatement();
+		if (_stack.size())
+			result = _stack.pop();
+		if (_currToken == terminator || _currToken == kTokenEndOfFile)
+			break;
+		if (_currToken != kTokenEndOfStatement && _currToken != kTokenEndOfFile)
+			error("missing EOS");
+		debugN("\n");
+	}
 
-		default:
-			error("unknown token %02x in code parameter", nextToken);
-		}
+	return result;
+}
 
-		params.push_back(nextValue);
+void LBCode::parseStatement() {
+	// FIXME: logical operators
+	parseComparisons();
+}
+
+void LBCode::parseComparisons() {
+	parseConcat();
+
+	if (_currToken != kTokenEquals && _currToken != kTokenLessThan && _currToken != kTokenGreaterThan &&
+		_currToken != kTokenLessThanEq && _currToken != kTokenGreaterThanEq && _currToken != kTokenNotEq)
+		return;
+	byte comparison = _currToken;
+	switch (comparison) {
+	case kTokenEquals:
+		debugN(" == ");
+		break;
+	case kTokenLessThan:
+		debugN(" < ");
+		break;
+	case kTokenGreaterThan:
+		debugN(" > ");
+		break;
+	case kTokenLessThanEq:
+		debugN(" <= ");
+		break;
+	case kTokenGreaterThanEq:
+		debugN(" >= ");
+		break;
+	case kTokenNotEq:
+		debugN(" != ");
+		break;
 	}
 
-	nextToken = data[offset];
-	offset++;
-	if (nextToken != kLBCodeTokenCloseBracket)
-		error("missing ) after code parameter list (got %02x)", nextToken);
-	debugN(")");
+	nextToken();
+	parseConcat();
+
+	if (_stack.size() < 2)
+		error("comparison didn't get enough values");
+	LBValue val2 = _stack.pop();
+	LBValue val1 = _stack.pop();
+	bool result;
+	// FIXME: should work for non-integers!!
+	switch (comparison) {
+	case kTokenEquals:
+		result = (val1 == val2);
+		break;
+	case kTokenLessThan:
+		result = (val1.integer < val2.integer);
+		break;
+	case kTokenGreaterThan:
+		result = (val1.integer > val2.integer);
+		break;
+	case kTokenLessThanEq:
+		result = (val1.integer <= val2.integer);
+		break;
+	case kTokenGreaterThanEq:
+		result = (val1.integer >= val2.integer);
+		break;
+	case kTokenNotEq:
+		result = (val1 != val2);
+		break;
+	}
 
-	return params;
+	debugN(" [--> %s]", result ? "true" : "false");
+	_stack.push(result ? 1 : 0);
 }
 
-void LBCode::runCodeCommand(LBItem *src, uint32 &offset) {
-	if (offset + 1 >= size)
-		error("went off the end of code");
+void LBCode::parseConcat() {
+	parseArithmetic1();
+	// FIXME: string concat
+}
 
-	byte commandType = data[offset];
-	offset++;
+void LBCode::parseArithmetic1() {
+	parseArithmetic2();
+	// FIXME: -/+ math operators
+}
 
-	switch (commandType) {
-	case 0x23:
-		{
-		debugN("setViewOrigin");
-		Common::Array<LBValue> params = readParams(src, offset);
-		// TODO
-		}
-		break;
+void LBCode::parseArithmetic2() {
+	// FIXME: other math operators
+	parseMain();
+}
 
-	case 0x36:
-		{
-		debugN("setWorld");
-		Common::Array<LBValue> params = readParams(src, offset);
-		// TODO
-		}
-		break;
+void LBCode::parseMain() {
+	byte prefix = 0;
+	if (_currToken == kTokenMinus || _currToken == kTokenPlus) {
+		debugN("%s", _currToken == kTokenMinus ? "-" : "+");
+		prefix = _currToken;
+		nextToken();
+	}
 
-	case 0x42:
+	switch (_currToken) {
+	case kTokenIdentifier:
+		assert(_currValue.type == kLBValueString);
 		{
-		debugN("setPlayParams");
-		Common::Array<LBValue> params = readParams(src, offset);
-		if (params.size() > 8)
-			error("too many parameters (%d) to setPlayParams", params.size());
-		if (!params.size())
-			error("no target for setPlayParams");
-		LBItem *target;
-		if (params[0].string.equalsIgnoreCase("self")) {
-			target = src;
+		Common::String varname = _currValue.string;
+		debugN("%s", varname.c_str());
+		nextToken();
+		if (varname == "self") {
+			_stack.push(LBValue(_currSource));
+			if (_currToken == kTokenAssign)
+				error("attempted assignment to self");
+		} else if (_currToken == kTokenAssign) {
+			debugN(" = ");
+			nextToken();
+			parseStatement();
+			if (!_stack.size())
+				error("assignment failed");
+			LBValue *val = &_vm->_variables[varname];
+			*val = _stack.pop();
+			_stack.push(*val);
 		} else {
-			error("didn't understand target '%s'", params[0].string.c_str());
-		}
-		// TODO: type-checking
-		switch (params.size()) {
-		case 8:
-			target->_soundMode = params[7].integer;
-		case 7:
-			target->_controlMode = params[6].integer;
-		case 6:
-			// TODO: _relocPoint?
-		case 5:
-			// TODO: _periodMin/Max
-		case 4:
-			target->_timingMode = params[3].integer;
-		case 3:
-			// TODO: _delayMin/Max
-		case 2:
-			target->_loopMode = params[1].integer;
+			_stack.push(_vm->_variables[varname]);
 		}
+		// FIXME: pre/postincrement
 		}
 		break;
 
-	case 0x50:
-		{
-		debugN("setKeyEvent");
-		Common::Array<LBValue> params = readParams(src, offset);
-		if (params.size() != 2)
-			error("incorrect number of parameters (%d) to setKeyEvent", params.size());
-		// FIXME: params[0] is key, params[1] is opcode id
-		}
+	case kTokenLiteral:
+	case kTokenConstMode:
+	case kTokenConstEventId:
+	case 0x5e: // TODO: ??
+	case kTokenKeycode:
+		assert(_currValue.type == kLBValueInteger);
+		debugN("%d", _currValue.integer);
+		_stack.push(_currValue);
+		nextToken();
 		break;
 
-	case 0x51:
-		{
-		debugN("setHitTest");
-		Common::Array<LBValue> params = readParams(src, offset);
-		if (params.size() > 2)
-			error("incorrect number of parameters (%d) to setHitTest", params.size());
-		// TODO
-		}
+	case kTokenString:
+		assert(_currValue.type == kLBValueString);
+		debugN("\"%s\"", _currValue.string.c_str());
+		_stack.push(_currValue);
+		nextToken();
 		break;
 
-	case 0x52:
-		{
-		debugN("key");
-		Common::Array<LBValue> params = readParams(src, offset);
-		// TODO
-		}
+	case kTokenOpenBracket:
+		debugN("(");
+		nextToken();
+		parseStatement();
+		if (_currToken != kTokenCloseBracket)
+			error("no kTokenCloseBracket (%02x), multiple entries?", _currToken);
+		debugN(")");
+		nextToken();
 		break;
 
-	case 0x5E:
-		{
-		debugN("setPageFade");
-		Common::Array<LBValue> params = readParams(src, offset);
-		// TODO
-		}
+	case kTokenNot:
+		debugN("!");
+		nextToken();
+		// not parseStatement, ! takes predecence over logical ops
+		parseComparisons();
+		if (!_stack.size())
+			error("not op failed");
+		_stack.push(_stack.pop().isZero() ? 1 : 0);
+		break;
+
+	case kTokenGeneralCommand:
+		runGeneralCommand();
+		break;
+
+	case kTokenItemCommand:
+		runItemCommand();
+		break;
+
+	case kTokenNotifyCommand:
+		runNotifyCommand();
 		break;
 
 	default:
-		error("unknown command %02x in code", commandType);
+		error("unknown token %02x in code", _currToken);
+	}
+
+	if (prefix) {
+		if (!_stack.size())
+			error("+/- prefix failed");
+		LBValue val = _stack.pop();
+		assert(val.isNumeric());
+		// FIXME
+		if (prefix == kTokenMinus)
+			val.integer--;
+		else
+			val.integer++;
+		_stack.push(val);
 	}
 }
 
-void LBCode::runCodeItemCommand(LBItem *src, uint32 &offset) {
-	if (offset + 1 >= size)
+Common::Array<LBValue> LBCode::readParams() {
+	Common::Array<LBValue> params;
+
+	if (_currOffset + 1 >= _size)
 		error("went off the end of code");
 
-	byte commandType = data[offset];
-	offset++;
+	byte numParams = _data[_currOffset++];
 
-	switch (commandType) {
-	case 0x1d:
-		{
-		debugN("setParent");
-		Common::Array<LBValue> params = readParams(src, offset);
-		if (params.size() > 2)
-			error("incorrect number of parameters (%d) to setParent", params.size());
-		// TODO
+	if (!numParams) {
+		debugN("()\n");
+		nextToken();
+		return params;
+	}
+
+	nextToken();
+	if (_currToken != kTokenOpenBracket)
+		error("missing ( before code parameter list (got %02x)", _currToken);
+	nextToken();
+	debugN("(");
+
+	for (uint i = 0; i < numParams; i++) {
+		if (i != 0) {
+			if (_currToken != ',')
+				error("missing , between code parameters (got %02x)", _currToken);
+			debugN(", ");
+			nextToken();
 		}
-		break;
 
-	default:
-		error("unknown item command %02x in code", commandType);
+		parseStatement();
+		if (!_stack.size())
+			error("stack empty");
+		LBValue nextValue = _stack.pop();
+
+		params.push_back(nextValue);
 	}
+
+	if (_currToken != kTokenCloseBracket)
+		error("missing ) after code parameter list (got %02x)", _currToken);
+	nextToken();
+	debugN(")");
+
+	return params;
 }
 
-void LBCode::runCodeNotifyCommand(LBItem *src, uint32 &offset) {
-	if (offset + 1 >= size)
-		error("went off the end of code");
+struct CodeCommandInfo {
+	const char *name;
+	typedef void (LBCode::*CommandFunc)(const Common::Array<LBValue> &params);
+	CommandFunc func;
+};
+
+#define NUM_GENERAL_COMMANDS 129
+CodeCommandInfo generalCommandInfo[NUM_GENERAL_COMMANDS] = {
+	{ "eval", 0 },
+	{ "random", 0 },
+	{ "stringLen", 0 },
+	{ "substring", 0 },
+	{ "max", 0 },
+	{ "min", 0 },
+	{ "abs", 0 },
+	{ "getRect", 0 }, // also "makeRect"
+	{ "makePt", 0 }, // also "makePair"
+	{ "topleft", 0 },
+	{ "bottomright", 0 },
+	{ "mousePos", 0 },
+	{ "top", 0 },
+	{ "left", 0 },
+	{ "bottom", 0 },
+	// 0x10
+	{ "right", 0 },
+	{ "xpos", 0 },
+	{ "ypos", 0 },
+	{ "playFrom", 0 },
+	{ "move", 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ "setDragParams", 0 },
+	{ "resetDragParams", 0 },
+	{ "enableRollover", &LBCode::cmdUnimplemented /* FIXME */ },
+	{ "setCursor", 0 },
+	{ "width", 0 },
+	{ "height", 0 },
+	{ "getFrameBounds", 0 }, // also "getFrameRect"
+	{ "traceRect", 0 },
+	{ "sqrt", 0 },
+	// 0x20
+	{ "deleteVar", 0 },
+	{ "saveVars", 0 },
+	{ "scriptLink", 0 },
+	{ "setViewOrigin", &LBCode::cmdUnimplemented },
+	{ "rectSect", 0 },
+	{ "getViewOrigin", 0 },
+	{ "getViewRect", 0 },
+	{ "getPage", 0 },
+	{ "getWorldRect", 0 },
+	{ "isWorldWrap", 0 },
+	{ "newList", 0 },
+	{ "deleteList", 0 },
+	{ "add", 0 },
+	{ 0, 0 },
+	{ "addAt", 0 },
+	{ "getAt", 0 },
+	// 0x30
+	{ 0, 0 },
+	{ "getIndex", 0 },
+	{ "setAt", 0 },
+	{ "listLen", 0 },
+	{ "deleteAt", 0 },
+	{ "clearList", 0 },
+	{ "setWorld", 0 },
+	{ "setProperty", 0 },
+	{ "getProperty", 0 },
+	{ "copyList", 0 },
+	{ "invoke", 0 },
+	{ "exec", 0 },
+	{ "return", 0 },
+	{ "sendSync", 0 },
+	{ "moveViewOrigin", 0 },
+	{ "addToGroup", 0 },
+	// 0x40
+	{ "removeFromGroup", 0 },
+	{ "clearGroup", 0 },
+	{ "setPlayParams", &LBCode::cmdSetPlayParams },
+	{ "autoEvent", 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ "getID", 0 },
+	{ "setCursorPosition", 0 },
+	{ "getTime", 0 },
+	{ "logWriteLn", 0 },
+	{ "logWrite", 0 },
+	{ "getLanguage", 0 },
+	{ "setLanguage", 0 },
+	{ "getSequence", 0 },
+	{ "setSequence", 0 },
+	{ "getFileSpec", 0 },
+	// 0x50
+	{ "setKeyEvent", &LBCode::cmdSetKeyEvent },
+	{ "setHitTest", &LBCode::cmdSetHitTest },
+	{ "key", &LBCode::cmdKey },
+	{ "deleteKeyEvent", 0 },
+	{ "setDisplay", &LBCode::cmdUnimplemented },
+	{ "getDisplay", 0 },
+	{ 0, 0 },
+	{ "lbxCreate", 0 },
+	{ "lbxFunc", 0 },
+	{ "waitCursor", 0 },
+	{ "debugBreak", 0 },
+	{ "menuItemEnable", 0 },
+	{ "showChannel", 0 },
+	{ "hideChannel", 0 },
+	{ "setPageFade", 0 },
+	{ "normalize", 0 },
+	// 0x60 (v5+)
+	{ "addEvent", 0 },
+	{ "setCueEvent", 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ "getName", 0 },
+	{ "getProperties", 0 },
+	{ "createItem", 0 },
+	{ "setProperties", 0 },
+	{ "alert", 0 },
+	{ "getUniqueID", 0 },
+	{ "isNumeric", 0 },
+	{ "setKeyFocus", 0 },
+	{ "getKeyFocus", 0 },
+	{ "isItem", 0 },
+	{ "itemHit", 0 },
+	{ "getItem ", 0 },
+	// 0x70
+	{ 0, 0 },
+	{ "setCascade", 0 },
+	{ "getCascade", 0 },
+	{ "getRes", 0 },
+	{ "setRes", 0 },
+	{ "getFilename", 0 },
+	{ "resEnumNames", 0 },
+	{ "isList", 0 },
+	{ "resetRect", 0 },
+	{ "setVolume", 0 },
+	{ "getVolume", 0 },
+	{ "pause", 0 },
+	{ "getTextWidth", 0 },
+	{ "setItemVolume", 0 },
+	{ "setSoundLoop", 0 },
+	// 0x80
+	{ "setClipboard", 0 },
+	{ "getResDuration", 0 }
+};
+
+void LBCode::runGeneralCommand() {
+	byte commandType = _currValue.integer;
+
+	if (commandType == 0 || commandType > NUM_GENERAL_COMMANDS)
+		error("bad command type 0x%02x in runGeneralCommand", commandType);
+
+	CodeCommandInfo &info = generalCommandInfo[commandType - 1];
+	debugN("%s", info.name);
+	Common::Array<LBValue> params = readParams();
+
+	if (!info.func)
+		error("general command '%s' (0x%02x) unimplemented", info.name, commandType);
+	(this->*(info.func))(params);
+}
+
+void LBCode::cmdUnimplemented(const Common::Array<LBValue> &params) {
+	warning("unimplemented command called");
+}
+
+void LBCode::cmdSetPlayParams(const Common::Array<LBValue> &params) {
+	if (params.size() > 8)
+		error("too many parameters (%d) to setPlayParams", params.size());
+	if (!params.size())
+		error("no target for setPlayParams");
+
+	if (params[0].type != kLBValueItemPtr)
+		error("first param to setPlayParams wasn't item");
+	LBItem *target = params[0].item;
+
+	// TODO: type-checking
+	switch (params.size()) {
+	case 8:
+		target->_soundMode = params[7].integer;
+	case 7:
+		target->_controlMode = params[6].integer;
+	case 6:
+		// TODO: _relocPoint?
+	case 5:
+		// TODO: _periodMin/Max
+	case 4:
+		target->_timingMode = params[3].integer;
+	case 3:
+		// TODO: _delayMin/Max
+	case 2:
+		target->_loopMode = params[1].integer;
+	}
+}
+
+void LBCode::cmdSetKeyEvent(const Common::Array<LBValue> &params) {
+	if (params.size() != 2)
+		error("incorrect number of parameters (%d) to setKeyEvent", params.size());
+
+	// FIXME: params[0] is key, params[1] is opcode id
+	warning("ignoring setKeyEvent");
+}
+
+void LBCode::cmdSetHitTest(const Common::Array<LBValue> &params) {
+	if (params.size() > 2)
+		error("incorrect number of parameters (%d) to setHitTest", params.size());
+	warning("ignoring setHitTest");
+}
+
+void LBCode::cmdKey(const Common::Array<LBValue> &params) {
+	_stack.push(0); // FIXME
+	warning("ignoring Key");
+}
+
+#define NUM_ITEM_COMMANDS 34
+CodeCommandInfo itemCommandInfo[NUM_ITEM_COMMANDS] = {
+	{ "clone", 0 },
+	{ "destroy", 0 },
+	{ "dragBeginFrom", 0 },
+	{ "dragEnd", 0 },
+	{ "enableLocal", 0 },
+	{ "enable", 0 },
+	{ "showLocal", 0 },
+	{ "show", 0 },
+	{ "getFrame", 0 },
+	{ "getParent", 0 },
+	{ "getPosition" , 0 },
+	{ "getText", 0 },
+	{ "getZNext", 0 },
+	{ "getZPrev", 0 },
+	{ "hitTest", 0 },
+	// 0x10
+	{ "isAmbient", 0 },
+	{ "isEnabled", 0 },
+	{ "isMuted", 0 },
+	{ "isPlaying", &LBCode::itemIsPlaying },
+	{ "isVisible", 0 },
+	{ "isLoaded", 0 },
+	{ "isDragging", 0 },
+	{ "load", 0 },
+	{ "moveTo", 0 },
+	{ "mute", 0 },
+	{ "play", 0 },
+	{ "seek", 0 },
+	{ "seekToFrame", 0 },
+	{ "setParent", &LBCode::itemSetParent },
+	{ "setZOrder", 0 },
+	{ "setText", 0 },
+	// 0x20
+	{ "stop", 0 },
+	{ "unload", 0 },
+	{ "unloadSync", 0}
+};
+
+void LBCode::runItemCommand() {
+	byte commandType = _currValue.integer;
+
+	if (commandType == 0 || commandType > NUM_ITEM_COMMANDS)
+		error("bad command type 0x%02x in runItemCommand", commandType);
+
+	CodeCommandInfo &info = itemCommandInfo[commandType - 1];
+	debugN("%s", info.name);
+	Common::Array<LBValue> params = readParams();
+
+	if (!info.func)
+		error("item command '%s' (0x%02x) unimplemented", info.name, commandType);
+	(this->*(info.func))(params);
+}
+
+void LBCode::itemIsPlaying(const Common::Array<LBValue> &params) {
+	// TODO
+	warning("ignoring isPlaying");
+	_stack.push(0);
+}
+
+void LBCode::itemSetParent(const Common::Array<LBValue> &params) {
+	if (params.size() > 2)
+		error("incorrect number of parameters (%d) to setParent", params.size());
+	// TODO
+	warning("ignoring setParent");
+}
 
-	byte commandType = data[offset];
-	offset++;
+void LBCode::runNotifyCommand() {
+	byte commandType = _currValue.integer;
 
 	switch (commandType) {
 	case kLBNotifyChangePage:
 		{
 		debugN("goto");
-		Common::Array<LBValue> params = readParams(src, offset);
+		Common::Array<LBValue> params = readParams();
 		// TODO: type-checking
+		NotifyEvent notifyEvent(kLBNotifyChangePage, 0);
 		switch (params.size()) {
-		case 1:
-			_vm->addNotifyEvent(NotifyEvent(kLBNotifyChangePage, params[0].integer));
+		case 4:
+			notifyEvent.type = kLBNotifyChangeMode; // FIXME: type 8?
+			notifyEvent.newUnknown = params[0].integer; // FIXME: this is newLanguage
+			notifyEvent.newMode = params[1].integer;
+			notifyEvent.newPage = params[2].integer;
+			notifyEvent.newSubpage = params[3].integer;
 			break;
 
 		case 2:
-			// FIXME
-		case 4:
-			// FIXME
+			notifyEvent.type = kLBNotifyChangeMode;
+			// FIXME: newPage and newSubpage?
+			error("can't handle goto with 2 params");
+			break;
+
+		case 1:
+			notifyEvent.param = params[0].integer;
+			break;
+
+		case 0:
+			// FIXME: use cur page?
+			error("can't handle goto with 0 params");
+			break;
 
 		default:
 			error("incorrect number of parameters (%d) to goto", params.size());
 		}
+		_vm->addNotifyEvent(notifyEvent);
 		}
 		break;
 
@@ -336,7 +768,7 @@ void LBCode::runCodeNotifyCommand(LBItem *src, uint32 &offset) {
 	case kLBNotifyGotoQuit:
 		{
 		debugN(commandType == kLBNotifyGoToControls ? "gotocontrol" : "gotoquit");
-		Common::Array<LBValue> params = readParams(src, offset);
+		Common::Array<LBValue> params = readParams();
 		if (params.size() != 0)
 			error("incorrect number of parameters (%d) to notify", params.size());
 		_vm->addNotifyEvent(NotifyEvent(commandType, 0));
@@ -346,7 +778,7 @@ void LBCode::runCodeNotifyCommand(LBItem *src, uint32 &offset) {
 	case kLBNotifyIntroDone:
 		{
 		debugN("startphasemain");
-		Common::Array<LBValue> params = readParams(src, offset);
+		Common::Array<LBValue> params = readParams();
 		if (params.size() != 0)
 			error("incorrect number of parameters (%d) to startphasemain", params.size());
 		_vm->addNotifyEvent(NotifyEvent(kLBNotifyIntroDone, 1));
@@ -358,50 +790,4 @@ void LBCode::runCodeNotifyCommand(LBItem *src, uint32 &offset) {
 	}
 }
 
-void LBCode::runCode(LBItem *src, uint32 offset) {
-	while (true) {
-		if (offset + 1 >= size) {
-			warning("went off the end of code");
-			return;
-		}
-
-		byte tokenType = data[offset];
-		offset++;
-
-		switch (tokenType) {
-		case 0x01: // FIXME
-		case kLBCodeTokenEndOfFile:
-			return;
-
-		case 0x4D:
-			runCodeCommand(src, offset);
-			break;
-
-		case 0x4E:
-			runCodeItemCommand(src, offset);
-			break;
-
-		case 0x4F:
-			runCodeNotifyCommand(src, offset);
-			break;
-
-		default:
-			debugN("at %04x: %02x ", offset - 1, tokenType);
-			for (uint i = 0; i < size; i++)
-				debugN("%02x ", data[offset++]);
-			debugN("\n");
-			error("unknown token %02x in code", tokenType);
-		}
-
-		byte nextToken = data[offset];
-		offset++;
-		if (nextToken != kLBCodeTokenEndOfStatement)
-			warning("missing EndOfStatement after code statement (got %04x)", nextToken);
-		if (nextToken == kLBCodeTokenEndOfFile)
-			return;
-
-		debugN("\n");
-	}
-}
-
 } // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks_code.h b/engines/mohawk/livingbooks_code.h
index 7960e79..71174cc 100644
--- a/engines/mohawk/livingbooks_code.h
+++ b/engines/mohawk/livingbooks_code.h
@@ -26,6 +26,8 @@
 #ifndef MOHAWK_LIVINGBOOKS_CODE_H
 #define MOHAWK_LIVINGBOOKS_CODE_H
 
+#include "common/rect.h"
+#include "common/stack.h"
 #include "common/substream.h"
 
 namespace Mohawk {
@@ -35,18 +37,70 @@ class LBItem;
 
 enum LBValueType {
 	kLBValueString,
-	kLBValueInteger
+	kLBValueInteger,
+	kLBValueReal,
+	kLBValuePoint,
+	kLBValueRect,
+	kLBValueItemPtr
 };
 
 struct LBValue {
-	LBValue() { type = kLBValueInteger; integer = 0; }
+	LBValue() {
+		type = kLBValueInteger;
+		integer = 0;
+	}
+	LBValue(int val) {
+		type = kLBValueInteger;
+		integer = val;
+	}
+	LBValue(const Common::String &str) {
+		type = kLBValueString;
+		string = str;
+	}
+	LBValue(LBItem *itm) {
+		type = kLBValueItemPtr;
+		item = itm;
+	}
+	LBValue(const LBValue &val) {
+		type = val.type;
+		switch (type) {
+		case kLBValueString:
+			string = val.string;
+			break;
+		case kLBValueInteger:
+			integer = val.integer;
+			break;
+		case kLBValueReal:
+			real = val.real;
+			break;
+		case kLBValuePoint:
+			point = val.point;
+			break;
+		case kLBValueRect:
+			rect = val.rect;
+			break;
+		case kLBValueItemPtr:
+			item = val.item;
+			break;
+		}
+	}
 
 	LBValueType type;
 	Common::String string;
 	int integer;
+	double real;
+	Common::Point point;
+	Common::Rect rect;
+	LBItem *item;
 
 	bool operator==(const LBValue &x) const;
 	bool operator!=(const LBValue &x) const;
+
+	bool isNumeric() const;
+	bool isZero() const;
+
+	int toInt() const;
+	double toDouble() const;
 };
 
 enum {
@@ -54,18 +108,63 @@ enum {
 };
 
 enum {
-	kLBCodeTokenString = 0x1,
-	kLBCodeTokenLiteral = 0x5,
-	kLBCodeTokenChar = 0x6,
-	kLBCodeTokenEndOfStatement = 0x7,
-	kLBCodeTokenEndOfFile = 0x8,
-	kLBCodeTokenOpenBracket = 0xf,
-	kLBCodeTokenCloseBracket = 0x10,
-	kLBCodeTokenLong = 0x11,
-
-	kLBCodeTokenEquals = 0x22, // TODO: maybe..
-	kLBCodeTokenQuote = 0x27, // "'"
-	kLBCodeTokenComma = 0x2c // ","
+	kTokenIdentifier = 0x1,
+	kTokenLiteral = 0x5,
+	kTokenString = 0x6,
+	kTokenEndOfStatement = 0x7,
+	kTokenEndOfFile = 0x8,
+	kTokenConcat = 0xb,
+	kTokenSingleQuote = 0xc, // ??
+	kTokenDoubleQuote = 0xd, // ??
+	kTokenMultiply = 0xe,
+	kTokenOpenBracket = 0xf,
+	kTokenCloseBracket = 0x10,
+	kTokenMinus = 0x11,
+	kTokenMinusMinus = 0x12,
+	kTokenPlusEquals = 0x13,
+	kTokenPlus = 0x14,
+	kTokenPlusPlus = 0x15,
+	kTokenEquals = 0x16,
+	kTokenMinusEquals = 0x17,
+	kTokenMultiplyEquals = 0x18,
+	kTokenDivideEquals = 0x19,
+	kTokenListStart = 0x1a,
+	kTokenListEnd = 0x1b,
+	kTokenColon = 0x1c, // ??
+	kTokenLessThan = 0x1d,
+	kTokenGreaterThan = 0x1e,
+	kTokenAndEquals = 0x1f,
+	kTokenDotOperator = 0x20,
+	kTokenDivide = 0x21,
+	kTokenAssign = 0x22,
+	kTokenLessThanEq = 0x23,
+	kTokenGreaterThanEq = 0x24,
+	kTokenNotEq = 0x25,
+	kTokenQuote = 0x27, // ??
+	kTokenAnd = 0x2a,
+	kTokenComma = 0x2c,
+	kTokenConstMode = 0x31,
+	kTokenIntDivide = 0x32,
+	kTokenModulo = 0x34,
+	kTokenNot = 0x35,
+	kTokenOr = 0x37,
+	kTokenTrue = 0x39,
+	kTokenFalse = 0x3a,
+	kTokenConstDataType = 0x3b, // ??
+	kTokenConstItemType = 0x3c, // ??
+	kTokenConstEventId = 0x42,
+	kTokenConstScriptOpcode = 0x43, // ??
+	kTokenConstScriptParam = 0x44, // ??
+	kTokenGeneralCommand = 0x4d,
+	kTokenItemCommand = 0x4e,
+	kTokenNotifyCommand = 0x4f,
+	// 0x5e?!
+	kTokenKeycode = 0x5f,
+
+	// v5 only:
+	kTokenLocal = 0x61,
+	kTokenPropListCommand = 0x70,
+	kTokenRectCommand = 0x71
 };
 
 class LBCode {
@@ -73,20 +172,46 @@ public:
 	LBCode(MohawkEngine_LivingBooks *vm);
 	~LBCode();
 
-	void runCode(LBItem *src, uint32 offset);
+	LBValue runCode(LBItem *src, uint32 offset);
 
 protected:
 	MohawkEngine_LivingBooks *_vm;
 
-	uint32 size;
-	byte *data;
+	uint32 _size;
+	byte *_data;
+	Common::HashMap<uint16, Common::String> _strings;
+
+	uint32 _currOffset;
+	LBItem *_currSource;
+
+	Common::Stack<LBValue> _stack;
+	byte _currToken;
+	LBValue _currValue;
 
-	Common::HashMap<uint16, Common::String> strings;
+	void nextToken();
 
-	Common::Array<LBValue> readParams(LBItem *src, uint32 &offset);
-	void runCodeCommand(LBItem *src, uint32 &offset);
-	void runCodeItemCommand(LBItem *src, uint32 &offset);
-	void runCodeNotifyCommand(LBItem *src, uint32 &offset);
+	LBValue runCode(byte terminator);
+	void parseStatement();
+	void parseComparisons();
+	void parseConcat();
+	void parseArithmetic1();
+	void parseArithmetic2();
+	void parseMain();
+
+	Common::Array<LBValue> readParams();
+	void runGeneralCommand();
+	void runItemCommand();
+	void runNotifyCommand();
+
+public:
+	void cmdUnimplemented(const Common::Array<LBValue> &params);
+	void cmdSetPlayParams(const Common::Array<LBValue> &params);
+	void cmdSetKeyEvent(const Common::Array<LBValue> &params);
+	void cmdSetHitTest(const Common::Array<LBValue> &params);
+	void cmdKey(const Common::Array<LBValue> &params);
+
+	void itemSetParent(const Common::Array<LBValue> &params);
+	void itemIsPlaying(const Common::Array<LBValue> &params);
 };
 
 } // End of namespace Mohawk


Commit: 40dd4f89baed798e431b671954e28755ca1aa73b
    https://github.com/scummvm/scummvm/commit/40dd4f89baed798e431b671954e28755ca1aa73b
Author: Alyssa Milburn (fuzzie at fuzzie.org)
Date: 2011-04-06T15:33:11-07:00

Commit Message:
MOHAWK: Support LB script flow control.

Changed paths:
    engines/mohawk/livingbooks.cpp
    engines/mohawk/livingbooks.h



diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index 3d22683..542cc96 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -1956,17 +1956,19 @@ LBScriptEntry *LBItem::parseScriptEntry(uint16 type, uint16 &size, Common::Seeka
 		}
 	}
 
-	if (type == kLBMsgListScript && entry->opcode == 0xfffb) {
-		uint16 u1 = stream->readUint16();
-		uint16 u2 = stream->readUint16();
-		uint16 u3 = stream->readUint16();
-		warning("unknown 0xfffb: %04x, %04x, %04x", u1, u2, u3);
+	if (type == kLBMsgListScript && entry->opcode == kLBOpJumpUnlessExpression) {
+		if (size < 6)
+			error("not enough bytes (%d) in kLBOpJumpUnlessExpression, event 0x%04x", size, entry->event);
+		entry->offset = stream->readUint32();
+		entry->target = stream->readUint16();
+		debug(4, "kLBOpJumpUnlessExpression: offset %08x, target %d", entry->offset, entry->target);
 		size -= 6;
 	}
-	if (type == kLBMsgListScript && entry->opcode == 0xfffd) {
-		uint16 u1 = stream->readUint16();
-		uint16 u2 = stream->readUint16();
-		warning("unknown 0xfffd: %04x, %04x", u1, u2);
+	if (type == kLBMsgListScript && entry->opcode == kLBOpJumpToExpression) {
+		if (size < 4)
+			error("not enough bytes (%d) in kLBOpJumpToExpression, event 0x%04x", size, entry->event);
+		entry->offset = stream->readUint32();
+		debug(4, "kLBOpJumpToExpression: offset %08x", entry->offset);
 		size -= 4;
 	}
 
@@ -2469,9 +2471,9 @@ void LBItem::runScript(uint event, uint16 data, uint16 from) {
 	}
 }
 
-void LBItem::runScriptEntry(LBScriptEntry *entry) {
+int LBItem::runScriptEntry(LBScriptEntry *entry) {
 	if (entry->state == 0xffff)
-		return;
+		return 0;
 
 	uint start = 0;
 	uint count = entry->argc;
@@ -2479,7 +2481,7 @@ void LBItem::runScriptEntry(LBScriptEntry *entry) {
 	if (!count)
 		count = 1;
 
-	switch (entry->param) {
+	if (entry->opcode != kLBOpRunSubentries) switch (entry->param) {
 	case 0xfffe:
 		// Run once (disable self after run).
 		entry->state = 0xffff;
@@ -2496,7 +2498,7 @@ void LBItem::runScriptEntry(LBScriptEntry *entry) {
 			case 0:
 				// Disable..
 				entry->state = 0xffff;
-				return;
+				return 0;
 			case 1:
 				// Stay at the end.
 				entry->state = count - 1;
@@ -2666,7 +2668,22 @@ void LBItem::runScriptEntry(LBScriptEntry *entry) {
 			for (uint i = 0; i < entry->subentries.size(); i++) {
 				LBScriptEntry *subentry = entry->subentries[i];
 
-				runScriptEntry(subentry);
+				int e = runScriptEntry(subentry);
+
+				switch (subentry->opcode) {
+				case kLBOpJumpUnlessExpression:
+					debug(2, "JumpUnless got %d (to %d, on %d, of %d)", e, subentry->target, i, entry->subentries.size());
+					if (!e)
+						i = subentry->target - 1;
+					break;
+				case kLBOpBreakExpression:
+					debug(2, "BreakExpression");
+					i = entry->subentries.size();
+				case kLBOpJumpToExpression:
+					debug(2, "JumpToExpression got %d (on %d, of %d)", e, i, entry->subentries.size());
+					i = e - 1;
+					break;
+				}
 			}
 			break;
 
@@ -2674,11 +2691,24 @@ void LBItem::runScriptEntry(LBScriptEntry *entry) {
 			runCommand(entry->command);
 			break;
 
+		case kLBOpJumpUnlessExpression:
+		case kLBOpBreakExpression:
+		case kLBOpJumpToExpression:
+			if (!_vm->_code)
+				error("no BCOD?");
+			{
+			LBValue r = _vm->_code->runCode(this, entry->offset);
+			// FIXME
+			return r.integer;
+			}
+
 		default:
 			error("Unknown script opcode (type 0x%04x, event 0x%04x, opcode 0x%04x, param 0x%04x, target '%s')",
 				entry->type, entry->event, entry->opcode, entry->param, target->_desc.c_str());
 		}
 	}
+
+	return 0;
 }
 
 void LBItem::setNextTime(uint16 min, uint16 max) {
diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h
index e55f14b..3eff039 100644
--- a/engines/mohawk/livingbooks.h
+++ b/engines/mohawk/livingbooks.h
@@ -217,6 +217,9 @@ enum {
 	kLBOpScriptEnable = 0x1b,
 	kLBOpUnknown1C = 0x1c,
 	kLBOpSendExpression = 0x1d,
+	kLBOpJumpUnlessExpression = 0xfffb,
+	kLBOpBreakExpression = 0xfffc,
+	kLBOpJumpToExpression = 0xfffd,
 	kLBOpRunSubentries = 0xfffe,
 	kLBOpRunCommand = 0xffff
 };
@@ -264,6 +267,8 @@ struct LBScriptEntry {
 
 	// kLBOpSendExpression
 	uint32 offset;
+	// kLBOpJumpUnlessExpression
+	uint16 target;
 
 	Common::String command;
 	Common::Array<Common::String> conditions;
@@ -409,7 +414,7 @@ protected:
 
 	Common::Array<LBScriptEntry *> _scriptEntries;
 	void runScript(uint event, uint16 data = 0, uint16 from = 0);
-	void runScriptEntry(LBScriptEntry *entry);
+	int runScriptEntry(LBScriptEntry *entry);
 
 	LBValue parseValue(const Common::String &command, uint &pos);
 	void runCommand(const Common::String &command);






More information about the Scummvm-git-logs mailing list