mirror of
https://github.com/libretro/scummvm.git
synced 2025-03-04 01:07:22 +00:00
MOHAWK: Replace most of the LBCode interpreter.
This commit is contained in:
parent
3bbeee90c0
commit
bbe2c437a8
@ -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,
|
||||
|
@ -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,177 +106,517 @@ 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) {
|
||||
LBValue LBCode::runCode(LBItem *src, uint32 offset) {
|
||||
// TODO: re-entrancy issues?
|
||||
_currSource = src;
|
||||
_currOffset = offset;
|
||||
|
||||
return runCode(kTokenEndOfFile);
|
||||
}
|
||||
|
||||
void LBCode::nextToken() {
|
||||
if (_currOffset + 1 >= _size) {
|
||||
// TODO
|
||||
warning("went off the end of code");
|
||||
_currToken = kTokenEndOfFile;
|
||||
_currValue = LBValue();
|
||||
return;
|
||||
}
|
||||
|
||||
_currToken = _data[_currOffset++];
|
||||
|
||||
// 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;
|
||||
|
||||
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 kTokenConstMode:
|
||||
case kTokenConstEventId:
|
||||
case 0x5e: // TODO: ??
|
||||
case kTokenKeycode:
|
||||
_currValue = READ_BE_UINT16(_data + _currOffset);
|
||||
_currOffset += 2;
|
||||
break;
|
||||
|
||||
case kTokenGeneralCommand:
|
||||
case kTokenItemCommand:
|
||||
case kTokenNotifyCommand:
|
||||
case kTokenPropListCommand:
|
||||
case kTokenRectCommand:
|
||||
_currValue = _data[_currOffset++];
|
||||
//_currValue = READ_BE_UINT16(_data + _currOffset);
|
||||
//_currOffset += 2;
|
||||
break;
|
||||
|
||||
case kTokenString:
|
||||
{
|
||||
uint16 offset = READ_BE_UINT16(_data + _currOffset);
|
||||
// TODO: check string exists
|
||||
_currValue = _strings[offset];
|
||||
_currOffset += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
_currValue = LBValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LBValue LBCode::runCode(byte terminator) {
|
||||
LBValue result;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
debugN(" [--> %s]", result ? "true" : "false");
|
||||
_stack.push(result ? 1 : 0);
|
||||
}
|
||||
|
||||
void LBCode::parseConcat() {
|
||||
parseArithmetic1();
|
||||
// FIXME: string concat
|
||||
}
|
||||
|
||||
void LBCode::parseArithmetic1() {
|
||||
parseArithmetic2();
|
||||
// FIXME: -/+ math operators
|
||||
}
|
||||
|
||||
void LBCode::parseArithmetic2() {
|
||||
// FIXME: other math operators
|
||||
parseMain();
|
||||
}
|
||||
|
||||
void LBCode::parseMain() {
|
||||
byte prefix = 0;
|
||||
if (_currToken == kTokenMinus || _currToken == kTokenPlus) {
|
||||
debugN("%s", _currToken == kTokenMinus ? "-" : "+");
|
||||
prefix = _currToken;
|
||||
nextToken();
|
||||
}
|
||||
|
||||
switch (_currToken) {
|
||||
case kTokenIdentifier:
|
||||
assert(_currValue.type == kLBValueString);
|
||||
{
|
||||
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 {
|
||||
_stack.push(_vm->_variables[varname]);
|
||||
}
|
||||
// FIXME: pre/postincrement
|
||||
}
|
||||
break;
|
||||
|
||||
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 kTokenString:
|
||||
assert(_currValue.type == kLBValueString);
|
||||
debugN("\"%s\"", _currValue.string.c_str());
|
||||
_stack.push(_currValue);
|
||||
nextToken();
|
||||
break;
|
||||
|
||||
case kTokenOpenBracket:
|
||||
debugN("(");
|
||||
nextToken();
|
||||
parseStatement();
|
||||
if (_currToken != kTokenCloseBracket)
|
||||
error("no kTokenCloseBracket (%02x), multiple entries?", _currToken);
|
||||
debugN(")");
|
||||
nextToken();
|
||||
break;
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
Common::Array<LBValue> LBCode::readParams() {
|
||||
Common::Array<LBValue> params;
|
||||
|
||||
if (offset + 1 >= size)
|
||||
if (_currOffset + 1 >= _size)
|
||||
error("went off the end of code");
|
||||
|
||||
byte numParams = data[offset];
|
||||
offset++;
|
||||
byte numParams = _data[_currOffset++];
|
||||
|
||||
if (!numParams) {
|
||||
debugN("()\n");
|
||||
nextToken();
|
||||
return params;
|
||||
}
|
||||
|
||||
byte nextToken = data[offset];
|
||||
offset++;
|
||||
if (nextToken != kLBCodeTokenOpenBracket)
|
||||
error("missing ( before code parameter list (got %02x)", nextToken);
|
||||
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) {
|
||||
nextToken = data[offset];
|
||||
offset++;
|
||||
if (nextToken != ',')
|
||||
error("missing , between code parameters (got %02x)", nextToken);
|
||||
if (_currToken != ',')
|
||||
error("missing , between code parameters (got %02x)", _currToken);
|
||||
debugN(", ");
|
||||
nextToken();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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 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 kLBCodeTokenLong: // FIXME: wrong?
|
||||
{
|
||||
uint32 intValue = READ_BE_UINT32(data + offset);
|
||||
offset += 4;
|
||||
nextValue.type = kLBValueInteger;
|
||||
nextValue.integer = intValue;
|
||||
debugN("%d", nextValue.integer);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x31:
|
||||
{
|
||||
// TODO
|
||||
uint16 intValue = READ_BE_UINT16(data + offset);
|
||||
offset += 2;
|
||||
nextValue.type = kLBValueInteger;
|
||||
nextValue.integer = intValue;
|
||||
debugN("%d", nextValue.integer);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x4d:
|
||||
// TODO
|
||||
runCodeCommand(src, offset);
|
||||
break;
|
||||
|
||||
case 0x5f:
|
||||
// keycode
|
||||
nextValue.type = kLBValueInteger;
|
||||
nextValue.integer = data[offset];
|
||||
debugN("%d", nextValue.integer);
|
||||
offset++;
|
||||
offset++; // TODO
|
||||
break;
|
||||
|
||||
default:
|
||||
error("unknown token %02x in code parameter", nextToken);
|
||||
}
|
||||
parseStatement();
|
||||
if (!_stack.size())
|
||||
error("stack empty");
|
||||
LBValue nextValue = _stack.pop();
|
||||
|
||||
params.push_back(nextValue);
|
||||
}
|
||||
|
||||
nextToken = data[offset];
|
||||
offset++;
|
||||
if (nextToken != kLBCodeTokenCloseBracket)
|
||||
error("missing ) after code parameter list (got %02x)", nextToken);
|
||||
if (_currToken != kTokenCloseBracket)
|
||||
error("missing ) after code parameter list (got %02x)", _currToken);
|
||||
nextToken();
|
||||
debugN(")");
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
void LBCode::runCodeCommand(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;
|
||||
};
|
||||
|
||||
byte commandType = data[offset];
|
||||
offset++;
|
||||
#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 }
|
||||
};
|
||||
|
||||
switch (commandType) {
|
||||
case 0x23:
|
||||
{
|
||||
debugN("setViewOrigin");
|
||||
Common::Array<LBValue> params = readParams(src, offset);
|
||||
// TODO
|
||||
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);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x36:
|
||||
{
|
||||
debugN("setWorld");
|
||||
Common::Array<LBValue> params = readParams(src, offset);
|
||||
// TODO
|
||||
void LBCode::cmdUnimplemented(const Common::Array<LBValue> ¶ms) {
|
||||
warning("unimplemented command called");
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x42:
|
||||
{
|
||||
debugN("setPlayParams");
|
||||
Common::Array<LBValue> params = readParams(src, offset);
|
||||
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");
|
||||
LBItem *target;
|
||||
if (params[0].string.equalsIgnoreCase("self")) {
|
||||
target = src;
|
||||
} else {
|
||||
error("didn't understand target '%s'", params[0].string.c_str());
|
||||
}
|
||||
|
||||
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:
|
||||
@ -237,98 +635,132 @@ void LBCode::runCodeCommand(LBItem *src, uint32 &offset) {
|
||||
target->_loopMode = params[1].integer;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x50:
|
||||
{
|
||||
debugN("setKeyEvent");
|
||||
Common::Array<LBValue> params = readParams(src, offset);
|
||||
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
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x51:
|
||||
{
|
||||
debugN("setHitTest");
|
||||
Common::Array<LBValue> params = readParams(src, offset);
|
||||
// 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
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x52:
|
||||
{
|
||||
debugN("key");
|
||||
Common::Array<LBValue> params = readParams(src, offset);
|
||||
// TODO
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x5E:
|
||||
{
|
||||
debugN("setPageFade");
|
||||
Common::Array<LBValue> params = readParams(src, offset);
|
||||
// TODO
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
error("unknown command %02x in code", commandType);
|
||||
}
|
||||
warning("ignoring isPlaying");
|
||||
_stack.push(0);
|
||||
}
|
||||
|
||||
void LBCode::runCodeItemCommand(LBItem *src, uint32 &offset) {
|
||||
if (offset + 1 >= size)
|
||||
error("went off the end of code");
|
||||
|
||||
byte commandType = data[offset];
|
||||
offset++;
|
||||
|
||||
switch (commandType) {
|
||||
case 0x1d:
|
||||
{
|
||||
debugN("setParent");
|
||||
Common::Array<LBValue> params = readParams(src, offset);
|
||||
void LBCode::itemSetParent(const Common::Array<LBValue> ¶ms) {
|
||||
if (params.size() > 2)
|
||||
error("incorrect number of parameters (%d) to setParent", params.size());
|
||||
// TODO
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
error("unknown item command %02x in code", commandType);
|
||||
}
|
||||
warning("ignoring setParent");
|
||||
}
|
||||
|
||||
void LBCode::runCodeNotifyCommand(LBItem *src, uint32 &offset) {
|
||||
if (offset + 1 >= size)
|
||||
error("went off the end of code");
|
||||
|
||||
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
|
||||
|
@ -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,
|
||||
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,
|
||||
|
||||
kLBCodeTokenEquals = 0x22, // TODO: maybe..
|
||||
kLBCodeTokenQuote = 0x27, // "'"
|
||||
kLBCodeTokenComma = 0x2c // ","
|
||||
// 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;
|
||||
|
||||
Common::HashMap<uint16, Common::String> strings;
|
||||
uint32 _currOffset;
|
||||
LBItem *_currSource;
|
||||
|
||||
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);
|
||||
Common::Stack<LBValue> _stack;
|
||||
byte _currToken;
|
||||
LBValue _currValue;
|
||||
|
||||
void nextToken();
|
||||
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user