diff options
| author | Alyssa Milburn | 2011-04-07 00:30:09 +0200 | 
|---|---|---|
| committer | Alyssa Milburn | 2011-04-07 00:30:09 +0200 | 
| commit | bbe2c437a86c6435012d5699dad8e9b4c88ca60c (patch) | |
| tree | 156e2d60269b2c784259a016b614b9fe4f405220 /engines | |
| parent | 3bbeee90c02e613ab2fcf870a26dbcfb3aad60e1 (diff) | |
| download | scummvm-rg350-bbe2c437a86c6435012d5699dad8e9b4c88ca60c.tar.gz scummvm-rg350-bbe2c437a86c6435012d5699dad8e9b4c88ca60c.tar.bz2 scummvm-rg350-bbe2c437a86c6435012d5699dad8e9b4c88ca60c.zip  | |
MOHAWK: Replace most of the LBCode interpreter.
Diffstat (limited to 'engines')
| -rw-r--r-- | engines/mohawk/livingbooks.cpp | 18 | ||||
| -rw-r--r-- | engines/mohawk/livingbooks_code.cpp | 904 | ||||
| -rw-r--r-- | engines/mohawk/livingbooks_code.h | 169 | 
3 files changed, 792 insertions, 299 deletions
diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp index 3f53eae4b8..3d22683739 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 a11ac1cfe9..c0718d941a 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> ¶ms); +	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> ¶ms) { +	warning("unimplemented command called"); +} + +void LBCode::cmdSetPlayParams(const Common::Array<LBValue> ¶ms) { +	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> ¶ms) { +	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> ¶ms) { +	if (params.size() > 2) +		error("incorrect number of parameters (%d) to setHitTest", params.size()); +	warning("ignoring setHitTest"); +} + +void LBCode::cmdKey(const Common::Array<LBValue> ¶ms) { +	_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> ¶ms) { +	// TODO +	warning("ignoring isPlaying"); +	_stack.push(0); +} + +void LBCode::itemSetParent(const Common::Array<LBValue> ¶ms) { +	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 7960e79035..71174cc09e 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> ¶ms); +	void cmdSetPlayParams(const Common::Array<LBValue> ¶ms); +	void cmdSetKeyEvent(const Common::Array<LBValue> ¶ms); +	void cmdSetHitTest(const Common::Array<LBValue> ¶ms); +	void cmdKey(const Common::Array<LBValue> ¶ms); + +	void itemSetParent(const Common::Array<LBValue> ¶ms); +	void itemIsPlaying(const Common::Array<LBValue> ¶ms);  };  } // End of namespace Mohawk  | 
