scummvm/engines/parallaction/parser_ns.cpp
Nicola Mettifogo 9906030312 Ensure that zones/animations always get the same index number.
When a zone/animation is skipped by the parser because already
loaded, the index is now increased. This does not affect NS, since
indexes are only used for scene sorting there, but is relevant for
BRA, where indexes need to be coherent across location changes.

Patch #3004008 by fuzzie.

svn-id: r49428
2010-06-04 13:48:09 +00:00

1448 lines
33 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "parallaction/parallaction.h"
#include "parallaction/parser.h"
#include "parallaction/sound.h"
namespace Parallaction {
#define CMD_SET 1
#define CMD_CLEAR 2
#define CMD_START 3
#define CMD_SPEAK 4
#define CMD_GET 5
#define CMD_LOCATION 6
#define CMD_OPEN 7
#define CMD_CLOSE 8
#define CMD_ON 9
#define CMD_OFF 10
#define CMD_CALL 11
#define CMD_TOGGLE 12
#define CMD_DROP 13
#define CMD_QUIT 14
#define CMD_MOVE 15
#define CMD_STOP 16
#define INST_ON 1
#define INST_OFF 2
#define INST_X 3
#define INST_Y 4
#define INST_Z 5
#define INST_F 6
#define INST_LOOP 7
#define INST_ENDLOOP 8
#define INST_SHOW 9
#define INST_INC 10
#define INST_DEC 11
#define INST_SET 12
#define INST_PUT 13
#define INST_CALL 14
#define INST_WAIT 15
#define INST_START 16
#define INST_SOUND 17
#define INST_MOVE 18
#define INST_END 19
const char *_zoneFlagNamesRes_ns[] = {
"closed",
"active",
"remove",
"acting",
"locked",
"fixed",
"noname",
"nomasked",
"looping",
"added",
"character",
"nowalk"
};
const char *_zoneTypeNamesRes_ns[] = {
"examine",
"door",
"get",
"merge",
"taste",
"hear",
"feel",
"speak",
"none",
"trap",
"yourself",
"Command"
};
const char *_commandsNamesRes_ns[] = {
"set",
"clear",
"start",
"speak",
"get",
"location",
"open",
"close",
"on",
"off",
"call",
"toggle",
"drop",
"quit",
"move",
"stop",
"endcommands",
"endzone"
};
const char *_locationStmtRes_ns[] = {
"endlocation",
"location",
"disk",
"nodes",
"zone",
"animation",
"localflags",
"commands",
"acommands",
"flags",
"comment",
"endcomment",
"sound",
"music"
};
const char *_locationZoneStmtRes_ns[] = {
"limits",
"moveto",
"type",
"commands",
"label",
"flags",
"endzone"
};
const char *_locationAnimStmtRes_ns[] = {
"script",
"commands",
"type",
"label",
"flags",
"file",
"position",
"moveto",
"endanimation"
};
const char *_instructionNamesRes_ns[] = {
"on",
"off",
"x",
"y",
"z",
"f",
"loop",
"endloop",
"show",
"inc",
"dec",
"set",
"put",
"call",
"wait",
"start",
"sound",
"move",
"endscript"
};
#define DECLARE_ZONE_PARSER(sig) void LocationParser_ns::locZoneParse_##sig()
#define DECLARE_ANIM_PARSER(sig) void LocationParser_ns::locAnimParse_##sig()
#define DECLARE_COMMAND_PARSER(sig) void LocationParser_ns::cmdParse_##sig()
#define DECLARE_LOCATION_PARSER(sig) void LocationParser_ns::locParse_##sig()
#define DECLARE_INSTRUCTION_PARSER(sig) void ProgramParser_ns::instParse_##sig()
void LocationParser_ns::warning_unexpected() {
debugC(1, kDebugParser, "unexpected keyword '%s' in line %i", _tokens[0], _script->getLine());
}
DECLARE_ANIM_PARSER(script) {
debugC(7, kDebugParser, "ANIM_PARSER(script) ");
ctxt.a->_scriptName = strdup(_tokens[1]);
}
DECLARE_ANIM_PARSER(commands) {
debugC(7, kDebugParser, "ANIM_PARSER(commands) ");
parseCommands(ctxt.a->_commands);
}
DECLARE_ANIM_PARSER(type) {
debugC(7, kDebugParser, "ANIM_PARSER(type) ");
ctxt.a->_type = buildZoneType(_tokens[1], _tokens[2]);
if ((ACTIONTYPE(ctxt.a) != 0) && (ACTIONTYPE(ctxt.a) != kZoneCommand)) {
parseZoneTypeBlock(ctxt.a);
}
ctxt.a->_flags |= 0x1000000;
_parser->popTables();
}
DECLARE_ANIM_PARSER(label) {
debugC(7, kDebugParser, "ANIM_PARSER(label) ");
ctxt.a->_label = _vm->_gfx->renderFloatingLabel(_vm->_labelFont, _tokens[1]);
ctxt.a->_flags &= ~kFlagsNoName;
}
DECLARE_ANIM_PARSER(flags) {
debugC(7, kDebugParser, "ANIM_PARSER(flags) ");
uint16 _si = 1;
do {
byte _al = _zoneFlagNames->lookup(_tokens[_si]);
_si++;
ctxt.a->_flags |= 1 << (_al - 1);
} while (!scumm_stricmp(_tokens[_si++], "|"));
}
DECLARE_ANIM_PARSER(file) {
debugC(7, kDebugParser, "ANIM_PARSER(file) ");
char vC8[200];
strcpy(vC8, _tokens[1]);
if (_engineFlags & kEngineTransformedDonna) {
if (!scumm_stricmp(_tokens[1], "donnap") || !scumm_stricmp(_tokens[1], "donnapa")) {
strcat(vC8, "tras");
}
}
ctxt.a->gfxobj = _vm->_gfx->loadAnim(vC8);
}
DECLARE_ANIM_PARSER(position) {
debugC(7, kDebugParser, "ANIM_PARSER(position) ");
ctxt.a->setX(atoi(_tokens[1]));
ctxt.a->setY(atoi(_tokens[2]));
ctxt.a->setZ(atoi(_tokens[3]));
}
DECLARE_ANIM_PARSER(moveto) {
debugC(7, kDebugParser, "ANIM_PARSER(moveto) ");
ctxt.a->_moveTo.x = atoi(_tokens[1]);
ctxt.a->_moveTo.y = atoi(_tokens[2]);
}
DECLARE_ANIM_PARSER(endanimation) {
debugC(7, kDebugParser, "ANIM_PARSER(endanimation) ");
ctxt.a->_flags |= 0x1000000;
_parser->popTables();
}
void LocationParser_ns::parseAnimation(AnimationList &list, char *name) {
debugC(5, kDebugParser, "parseAnimation(name: %s)", name);
if (_vm->_location.findAnimation(name)) {
_zoneProg++;
_script->skip("endanimation");
return;
}
AnimationPtr a(new Animation);
_zoneProg++;
strncpy(a->_name, name, ZONENAME_LENGTH);
a->_flags |= kFlagsIsAnimation;
list.push_front(AnimationPtr(a));
ctxt.a = a;
_parser->pushTables(&_locationAnimParsers, _locationAnimStmt);
}
void ProgramParser_ns::parseInstruction() {
_script->readLineToken(true);
if (_tokens[0][1] == '.') {
_tokens[0][1] = '\0';
ctxt.a = _vm->_location.findAnimation(&_tokens[0][2]);
} else
if (_tokens[1][1] == '.') {
_tokens[1][1] = '\0';
ctxt.a = _vm->_location.findAnimation(&_tokens[1][2]);
} else
ctxt.a = _program->_anim;
if (!ctxt.a) {
return;
}
InstructionPtr inst(new Instruction);
ctxt.inst = inst;
_currentInstruction = _program->_instructions.size();
_parser->parseStatement();
_program->_instructions.push_back(inst);
return;
}
void ProgramParser_ns::parse(Script *script, ProgramPtr program) {
_script = script;
_program = program;
ctxt.end = false;
ctxt.locals = program->_locals;
_parser->reset();
_parser->pushTables(&_instructionParsers, _instructionNames);
do {
parseInstruction();
} while (!ctxt.end);
_parser->popTables();
program->_ip = 0;
}
void Parallaction_ns::loadProgram(AnimationPtr a, const char *filename) {
debugC(1, kDebugParser, "loadProgram(Animation: %s, script: %s)", a->_name, filename);
Script *script = _disk->loadScript(filename);
ProgramPtr program(new Program);
program->_anim = a;
_programParser->parse(script, program);
delete script;
_location._programs.push_back(program);
debugC(1, kDebugParser, "loadProgram() done");
return;
}
DECLARE_INSTRUCTION_PARSER(animation) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(animation) ");
if (!scumm_stricmp(_tokens[1], ctxt.a->_name)) {
ctxt.inst->_a = ctxt.a;
} else {
ctxt.inst->_a = _vm->_location.findAnimation(_tokens[1]);
}
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(loop) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(loop) ");
parseRValue(ctxt.inst->_opB, _tokens[1]);
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(x) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(x) ");
parseLValue(ctxt.inst->_opA, "X");
parseRValue(ctxt.inst->_opB, _tokens[1]);
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(y) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(y) ");
parseLValue(ctxt.inst->_opA, "Y");
parseRValue(ctxt.inst->_opB, _tokens[1]);
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(z) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(z) ");
parseLValue(ctxt.inst->_opA, "Z");
parseRValue(ctxt.inst->_opB, _tokens[1]);
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(f) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(f) ");
parseLValue(ctxt.inst->_opA, "F");
parseRValue(ctxt.inst->_opB, _tokens[1]);
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(inc) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(inc) ");
parseLValue(ctxt.inst->_opA, _tokens[1]);
parseRValue(ctxt.inst->_opB, _tokens[2]);
if (!scumm_stricmp(_tokens[3], "mod")) {
ctxt.inst->_flags |= kInstMod;
}
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(set) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(set) ");
// WORKAROUND: At least one script (balzo.script) in Amiga versions didn't declare
// local variables before using them, thus leading to crashes. The line launching the
// script was commented out on Dos version. This workaround enables the engine
// to dynamically add a local variable when it is encountered the first time in
// the script, so should fix any other occurrence as well.
if (_program->findLocal(_tokens[1]) == -1) {
_program->addLocal(_tokens[1]);
}
parseLValue(ctxt.inst->_opA, _tokens[1]);
parseRValue(ctxt.inst->_opB, _tokens[2]);
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(move) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(move) ");
parseRValue(ctxt.inst->_opA, _tokens[1]);
parseRValue(ctxt.inst->_opB, _tokens[2]);
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(put) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(put) ");
if (!scumm_stricmp(_tokens[1], ctxt.a->_name)) {
ctxt.inst->_a = ctxt.a;
} else {
ctxt.inst->_a = _vm->_location.findAnimation(_tokens[1]);
}
parseRValue(ctxt.inst->_opA, _tokens[2]);
parseRValue(ctxt.inst->_opB, _tokens[3]);
if (!scumm_stricmp(_tokens[4], "masked")) {
ctxt.inst->_flags |= kInstMaskedPut;
}
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(call) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(call) ");
int index = _vm->_callableNames->lookup(_tokens[1]);
if (index == Table::notFound)
error("unknown callable '%s'", _tokens[1]);
ctxt.inst->_immediate = index - 1;
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(sound) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(sound) ");
ctxt.inst->_z = _vm->_location.findZone(_tokens[1]);
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(null) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(null) ");
ctxt.inst->_index = _parser->_lookup;
}
DECLARE_INSTRUCTION_PARSER(defLocal) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(defLocal) ");
int16 val = atoi(_tokens[2]);
int16 index;
if (_tokens[3][0] != '\0') {
index = _program->addLocal(_tokens[0], val, atoi(_tokens[3]), atoi(_tokens[4]));
} else {
index = _program->addLocal(_tokens[0], val);
}
ctxt.inst->_opA.setLocal(&ctxt.locals[index]);
ctxt.inst->_opB.setImmediate(ctxt.locals[index].getValue());
ctxt.inst->_index = INST_SET;
}
DECLARE_INSTRUCTION_PARSER(endscript) {
debugC(7, kDebugParser, "INSTRUCTION_PARSER(endscript) ");
ctxt.end = true;
ctxt.inst->_index = _parser->_lookup;
}
void ProgramParser_ns::parseRValue(ScriptVar &v, const char *str) {
if (isdigit(str[0]) || str[0] == '-') {
v.setImmediate(atoi(str));
return;
}
int index = _program->findLocal(str);
if (index != -1) {
v.setLocal(&ctxt.locals[index]);
return;
}
AnimationPtr a;
if (str[1] == '.') {
a = _vm->_location.findAnimation(&str[2]);
} else {
a = ctxt.a;
}
if (str[0] == 'X') {
v.setField(a.get(), &Animation::getX);
} else
if (str[0] == 'Y') {
v.setField(a.get(), &Animation::getY);
} else
if (str[0] == 'Z') {
v.setField(a.get(), &Animation::getZ);
} else
if (str[0] == 'F') {
v.setField(a.get(), &Animation::getF);
}
}
void ProgramParser_ns::parseLValue(ScriptVar &v, const char *str) {
int index = _program->findLocal(str);
if (index != -1) {
v.setLocal(&ctxt.locals[index]);
return;
}
AnimationPtr a;
if (str[1] == '.') {
a = _vm->_location.findAnimation(&str[2]);
} else {
a = ctxt.a;
}
if (str[0] == 'X') {
v.setField(a.get(), &Animation::getX, &Animation::setX);
} else
if (str[0] == 'Y') {
v.setField(a.get(), &Animation::getY, &Animation::setY);
} else
if (str[0] == 'Z') {
v.setField(a.get(), &Animation::getZ, &Animation::setZ);
} else
if (str[0] == 'F') {
v.setField(a.get(), &Animation::getF, &Animation::setF);
}
}
DECLARE_COMMAND_PARSER(flags) {
debugC(7, kDebugParser, "COMMAND_PARSER(flags) ");
createCommand(_parser->_lookup);
if (_vm->_globalFlagsNames->lookup(_tokens[1]) == Table::notFound) {
do {
char _al = _vm->_localFlagNames->lookup(_tokens[ctxt.nextToken]);
ctxt.nextToken++;
ctxt.cmd->_flags |= 1 << (_al - 1);
} while (!scumm_stricmp(_tokens[ctxt.nextToken++], "|"));
ctxt.nextToken--;
} else {
ctxt.cmd->_flags |= kFlagsGlobal;
do {
char _al = _vm->_globalFlagsNames->lookup(_tokens[1]);
ctxt.nextToken++;
ctxt.cmd->_flags |= 1 << (_al - 1);
} while (!scumm_stricmp(_tokens[ctxt.nextToken++], "|"));
ctxt.nextToken--;
}
parseCommandFlags();
addCommand();
}
DECLARE_COMMAND_PARSER(zone) {
debugC(7, kDebugParser, "COMMAND_PARSER(zone) ");
createCommand(_parser->_lookup);
ctxt.cmd->_zoneName = _tokens[ctxt.nextToken];
ctxt.nextToken++;
parseCommandFlags();
addCommand();
}
DECLARE_COMMAND_PARSER(location) {
debugC(7, kDebugParser, "COMMAND_PARSER(location) ");
createCommand(_parser->_lookup);
ctxt.cmd->_string = strdup(_tokens[ctxt.nextToken]);
ctxt.nextToken++;
parseCommandFlags();
addCommand();
}
DECLARE_COMMAND_PARSER(invObject) {
debugC(7, kDebugParser, "COMMAND_PARSER(drop) ");
createCommand(_parser->_lookup);
ctxt.cmd->_object = 4 + _vm->_objectsNames->lookup(_tokens[ctxt.nextToken]);
ctxt.nextToken++;
parseCommandFlags();
addCommand();
}
DECLARE_COMMAND_PARSER(call) {
debugC(7, kDebugParser, "COMMAND_PARSER(call) ");
createCommand(_parser->_lookup);
ctxt.cmd->_callable = _vm->_callableNames->lookup(_tokens[ctxt.nextToken]) - 1;
ctxt.nextToken++;
parseCommandFlags();
addCommand();
}
DECLARE_COMMAND_PARSER(simple) {
debugC(7, kDebugParser, "COMMAND_PARSER(simple) ");
createCommand(_parser->_lookup);
parseCommandFlags();
addCommand();
}
DECLARE_COMMAND_PARSER(move) {
debugC(7, kDebugParser, "COMMAND_PARSER(move) ");
createCommand(_parser->_lookup);
ctxt.cmd->_move.x = atoi(_tokens[ctxt.nextToken]);
ctxt.nextToken++;
ctxt.cmd->_move.y = atoi(_tokens[ctxt.nextToken]);
ctxt.nextToken++;
parseCommandFlags();
addCommand();
}
DECLARE_COMMAND_PARSER(endcommands) {
debugC(7, kDebugParser, "COMMAND_PARSER(endcommands) ");
_parser->popTables();
// temporary trick to handle dialogue commands
ctxt.endcommands = true;
}
void LocationParser_ns::parseCommandFlag(CommandPtr cmd, const char *flag, Table *table) {
if (!scumm_stricmp(flag, "exit")) {
cmd->_flagsOn |= kFlagsExit;
} else
if (!scumm_stricmp(flag, "exittrap")) {
cmd->_flagsOn |= kFlagsExit;
} else
if (!scumm_stricmp(flag, "enter")) {
cmd->_flagsOn |= kFlagsEnter;
} else
if (!scumm_stricmp(flag, "entertrap")) {
cmd->_flagsOn |= kFlagsEnter;
} else
if (!scumm_strnicmp(flag, "no", 2)) {
byte _al = table->lookup(flag+2);
if (_al != Table::notFound) {
cmd->_flagsOff |= 1 << (_al - 1);
} else {
warning("Flag '%s' not found", flag);
}
} else {
byte _al = table->lookup(flag);
if (_al != Table::notFound) {
cmd->_flagsOn |= 1 << (_al - 1);
} else {
warning("Flag '%s' not found", flag);
}
}
}
void LocationParser_ns::parseCommandFlags() {
int _si = ctxt.nextToken;
CommandPtr cmd = ctxt.cmd;
if (!scumm_stricmp(_tokens[_si], "flags")) {
do {
_si++;
parseCommandFlag(cmd, _tokens[_si], _vm->_localFlagNames);
_si++;
} while (!scumm_stricmp(_tokens[_si], "|"));
}
if (!scumm_stricmp(_tokens[_si], "gflags")) {
do {
_si++;
parseCommandFlag(cmd, _tokens[_si], _vm->_globalFlagsNames);
_si++;
} while (!scumm_stricmp(_tokens[_si], "|"));
cmd->_flagsOn |= kFlagsGlobal;
}
}
void LocationParser_ns::addCommand() {
ctxt.list->push_front(ctxt.cmd); // NOTE: command lists are written backwards in scripts
}
void LocationParser_ns::createCommand(uint id) {
ctxt.nextToken = 1;
ctxt.cmd = CommandPtr(new Command);
ctxt.cmd->_id = id;
ctxt.cmd->_valid = true;
}
void LocationParser_ns::parseCommands(CommandList& list) {
debugC(5, kDebugParser, "parseCommands()");
ctxt.list = &list;
ctxt.endcommands = false;
_parser->pushTables(&_commandParsers, _commandsNames);
}
Dialogue *LocationParser_ns::parseDialogue() {
debugC(7, kDebugParser, "parseDialogue()");
Dialogue *dialogue = new Dialogue;
assert(dialogue);
_script->readLineToken(true);
while (scumm_stricmp(_tokens[0], "enddialogue")) {
if (!scumm_stricmp(_tokens[0], "question")) {
Question *q = new Question(_tokens[1]);
assert(q);
parseQuestion(q);
dialogue->addQuestion(q);
}
_script->readLineToken(true);
}
debugC(7, kDebugParser, "parseDialogue() done");
return dialogue;
}
void LocationParser_ns::parseQuestion(Question *q) {
q->_text = parseDialogueString();
_script->readLineToken(true);
q->_mood = atoi(_tokens[0]);
uint16 numAnswers = 0;
_script->readLineToken(true);
while (scumm_stricmp(_tokens[0], "endquestion")) { // parse answers
q->_answers[numAnswers] = parseAnswer();
numAnswers++;
}
}
void LocationParser_ns::parseAnswerBody(Answer *answer) {
answer->_text = parseDialogueString();
_script->readLineToken(true);
answer->_mood = atoi(_tokens[0]);
answer->_followingName = parseDialogueString();
_script->readLineToken(true);
if (!scumm_stricmp(_tokens[0], "commands")) {
parseCommands(answer->_commands);
ctxt.endcommands = false;
do {
_script->readLineToken(true);
_parser->parseStatement();
} while (!ctxt.endcommands);
_script->readLineToken(true);
}
}
void LocationParser_ns::parseAnswerFlags(Answer *answer) {
if (!_tokens[1][0]) {
return;
}
Table* flagNames;
uint16 token;
if (!scumm_stricmp(_tokens[1], "global")) {
token = 2;
flagNames = _vm->_globalFlagsNames;
answer->_yesFlags |= kFlagsGlobal;
} else {
token = 1;
flagNames = _vm->_localFlagNames;
}
do {
if (!scumm_strnicmp(_tokens[token], "no", 2)) {
byte _al = flagNames->lookup(_tokens[token]+2);
answer->_noFlags |= 1 << (_al - 1);
} else {
byte _al = flagNames->lookup(_tokens[token]);
answer->_yesFlags |= 1 << (_al - 1);
}
token++;
} while (!scumm_stricmp(_tokens[token++], "|"));
}
Answer *LocationParser_ns::parseAnswer() {
Answer *answer = new Answer;
assert(answer);
parseAnswerFlags(answer);
parseAnswerBody(answer);
return answer;
}
Common::String LocationParser_ns::parseDialogueString() {
char buf[400];
char *line = _script->readLine(buf, 400);
if (line == 0) {
return 0;
}
return Common::String(line);
}
DECLARE_LOCATION_PARSER(endlocation) {
debugC(7, kDebugParser, "LOCATION_PARSER(endlocation) ");
ctxt.end = true;
}
DECLARE_LOCATION_PARSER(location) {
debugC(7, kDebugParser, "LOCATION_PARSER(location) ");
// The parameter for location is 'location.mask'.
// If mask is not present, then it is assumed
// that path & mask are encoded in the background
// bitmap, otherwise a separate .msk file exists.
char *mask = strchr(_tokens[1], '.');
if (mask) {
mask[0] = '\0';
mask++;
}
strcpy(_vm->_location._name, _tokens[1]);
_vm->changeBackground(_vm->_location._name, mask);
if (_tokens[2][0] != '\0') {
_vm->_char._ani->setX(atoi(_tokens[2]));
_vm->_char._ani->setY(atoi(_tokens[3]));
}
if (_tokens[4][0] != '\0') {
_vm->_char._ani->setF(atoi(_tokens[4]));
}
}
DECLARE_LOCATION_PARSER(disk) {
debugC(7, kDebugParser, "LOCATION_PARSER(disk) ");
_vm->_disk->selectArchive(_tokens[1]);
}
DECLARE_LOCATION_PARSER(nodes) {
debugC(7, kDebugParser, "LOCATION_PARSER(nodes) ");
parsePointList(_vm->_location._walkPoints);
}
DECLARE_LOCATION_PARSER(zone) {
debugC(7, kDebugParser, "LOCATION_PARSER(zone) ");
parseZone(_vm->_location._zones, _tokens[1]);
}
DECLARE_LOCATION_PARSER(animation) {
debugC(7, kDebugParser, "LOCATION_PARSER(animation) ");
parseAnimation(_vm->_location._animations, _tokens[1]);
}
DECLARE_LOCATION_PARSER(localflags) {
debugC(7, kDebugParser, "LOCATION_PARSER(localflags) ");
int _si = 1;
while (_tokens[_si][0] != '\0') {
_vm->_localFlagNames->addData(_tokens[_si]);
_si++;
}
}
DECLARE_LOCATION_PARSER(commands) {
debugC(7, kDebugParser, "LOCATION_PARSER(commands) ");
parseCommands(_vm->_location._commands);
}
DECLARE_LOCATION_PARSER(acommands) {
debugC(7, kDebugParser, "LOCATION_PARSER(acommands) ");
parseCommands(_vm->_location._aCommands);
}
DECLARE_LOCATION_PARSER(flags) {
debugC(7, kDebugParser, "LOCATION_PARSER(flags) ");
if ((_vm->getLocationFlags() & kFlagsVisited) == 0) {
// only for 1st visit
_vm->clearLocationFlags((uint32)kFlagsAll);
int _si = 1;
do {
byte _al = _vm->_localFlagNames->lookup(_tokens[_si]);
_vm->setLocationFlags(1 << (_al - 1));
_si++;
if (scumm_stricmp(_tokens[_si], "|")) break;
_si++;
} while (true);
}
}
DECLARE_LOCATION_PARSER(comment) {
debugC(7, kDebugParser, "LOCATION_PARSER(comment) ");
_vm->_location._comment = parseComment();
}
DECLARE_LOCATION_PARSER(endcomment) {
debugC(7, kDebugParser, "LOCATION_PARSER(endcomment) ");
_vm->_location._endComment = parseComment();
}
DECLARE_LOCATION_PARSER(sound) {
debugC(7, kDebugParser, "LOCATION_PARSER(sound) ");
if (_vm->getPlatform() == Common::kPlatformAmiga) {
strcpy(_vm->_location._soundFile, _tokens[1]);
_vm->_location._hasSound = true;
}
}
DECLARE_LOCATION_PARSER(music) {
debugC(7, kDebugParser, "LOCATION_PARSER(music) ");
if (_vm->getPlatform() == Common::kPlatformAmiga)
_vm->_soundMan->execute(SC_SETMUSICFILE, _tokens[1]);
}
void LocationParser_ns::parse(Script *script) {
_zoneProg = 0;
ctxt.end = false;
_script = script;
ctxt.filename = 0;//filename;
_parser->reset();
_parser->pushTables(&_locationParsers, _locationStmt);
do {
_script->readLineToken(true);
_parser->parseStatement();
} while (!ctxt.end);
_parser->popTables();
}
void LocationParser_ns::parsePointList(PointList &list) {
debugC(5, kDebugParser, "parsePointList()");
_script->readLineToken(true);
while (scumm_stricmp(_tokens[0], "ENDNODES")) {
if (!scumm_stricmp(_tokens[0], "COORD")) {
list.push_front(Common::Point(atoi(_tokens[1]), atoi(_tokens[2])));
}
_script->readLineToken(true);
}
debugC(5, kDebugParser, "parsePointList() done");
return;
}
/*
typedef OpcodeImpl<ProgramParser_ns> OpcodeV1;
#define INSTRUCTION_PARSER(sig) OpcodeV1(this, &ProgramParser_ns::instParse_##sig)
typedef OpcodeImpl<LocationParser_ns> OpcodeV2;
#define ZONE_PARSER(sig) OpcodeV2(this, &LocationParser_ns::locZoneParse_##sig)
#define ANIM_PARSER(sig) OpcodeV2(this, &LocationParser_ns::locAnimParse_##sig)
#define LOCATION_PARSER(sig) OpcodeV2(this, &LocationParser_ns::locParse_##sig)
#define COMMAND_PARSER(sig) OpcodeV2(this, &LocationParser_ns::cmdParse_##sig)
#define WARNING_PARSER(sig) OpcodeV2(this, &LocationParser_br::warning_##sig)
*/
#define SetOpcodeTable(x) table = &x;
typedef Common::Functor0Mem<void, ProgramParser_ns> OpcodeV1;
#define SetOpcodeTable(x) table = &x;
#define INSTRUCTION_PARSER(sig) table->push_back(new OpcodeV1(this, &ProgramParser_ns::instParse_##sig))
typedef Common::Functor0Mem<void, LocationParser_ns> OpcodeV2;
#define ZONE_PARSER(sig) table->push_back(new OpcodeV2(this, &LocationParser_ns::locZoneParse_##sig))
#define ANIM_PARSER(sig) table->push_back(new OpcodeV2(this, &LocationParser_ns::locAnimParse_##sig))
#define LOCATION_PARSER(sig) table->push_back(new OpcodeV2(this, &LocationParser_ns::locParse_##sig))
#define COMMAND_PARSER(sig) table->push_back(new OpcodeV2(this, &LocationParser_ns::cmdParse_##sig))
#define WARNING_PARSER(sig) table->push_back(new OpcodeV2(this, &LocationParser_br::warning_##sig))
void LocationParser_ns::init() {
_parser = new Parser;
_zoneFlagNames = new Table(ARRAYSIZE(_zoneFlagNamesRes_ns), _zoneFlagNamesRes_ns);
_zoneTypeNames = new Table(ARRAYSIZE(_zoneTypeNamesRes_ns), _zoneTypeNamesRes_ns);
_commandsNames = new Table(ARRAYSIZE(_commandsNamesRes_ns), _commandsNamesRes_ns);
_locationStmt = new Table(ARRAYSIZE(_locationStmtRes_ns), _locationStmtRes_ns);
_locationZoneStmt = new Table(ARRAYSIZE(_locationZoneStmtRes_ns), _locationZoneStmtRes_ns);
_locationAnimStmt = new Table(ARRAYSIZE(_locationAnimStmtRes_ns), _locationAnimStmtRes_ns);
Common::Array<const Opcode*> *table = 0;
SetOpcodeTable(_commandParsers);
WARNING_PARSER(unexpected);
COMMAND_PARSER(flags); // set
COMMAND_PARSER(flags); // clear
COMMAND_PARSER(zone); // start
COMMAND_PARSER(zone); // speak
COMMAND_PARSER(zone); // get
COMMAND_PARSER(location); // location
COMMAND_PARSER(zone); // open
COMMAND_PARSER(zone); // close
COMMAND_PARSER(zone); // on
COMMAND_PARSER(zone); // off
COMMAND_PARSER(call); // call
COMMAND_PARSER(flags); // toggle
COMMAND_PARSER(invObject); // drop
COMMAND_PARSER(simple); // quit
COMMAND_PARSER(move); // move
COMMAND_PARSER(zone); // stop
COMMAND_PARSER(endcommands); // endcommands
COMMAND_PARSER(endcommands); // endzone
SetOpcodeTable(_locationParsers);
WARNING_PARSER(unexpected);
LOCATION_PARSER(endlocation);
LOCATION_PARSER(location);
LOCATION_PARSER(disk);
LOCATION_PARSER(nodes);
LOCATION_PARSER(zone);
LOCATION_PARSER(animation);
LOCATION_PARSER(localflags);
LOCATION_PARSER(commands);
LOCATION_PARSER(acommands);
LOCATION_PARSER(flags);
LOCATION_PARSER(comment);
LOCATION_PARSER(endcomment);
LOCATION_PARSER(sound);
LOCATION_PARSER(music);
SetOpcodeTable(_locationZoneParsers);
WARNING_PARSER(unexpected);
ZONE_PARSER(limits);
ZONE_PARSER(moveto);
ZONE_PARSER(type);
ZONE_PARSER(commands);
ZONE_PARSER(label);
ZONE_PARSER(flags);
ZONE_PARSER(endzone);
SetOpcodeTable(_locationAnimParsers);
WARNING_PARSER(unexpected);
ANIM_PARSER(script);
ANIM_PARSER(commands);
ANIM_PARSER(type);
ANIM_PARSER(label);
ANIM_PARSER(flags);
ANIM_PARSER(file);
ANIM_PARSER(position);
ANIM_PARSER(moveto);
ANIM_PARSER(endanimation);
}
void ProgramParser_ns::init() {
_parser = new Parser;
_instructionNames = new Table(ARRAYSIZE(_instructionNamesRes_ns), _instructionNamesRes_ns);
Common::Array<const Opcode*> *table = 0;
SetOpcodeTable(_instructionParsers);
INSTRUCTION_PARSER(defLocal); // invalid opcode -> local definition
INSTRUCTION_PARSER(animation); // on
INSTRUCTION_PARSER(animation); // off
INSTRUCTION_PARSER(x);
INSTRUCTION_PARSER(y);
INSTRUCTION_PARSER(z);
INSTRUCTION_PARSER(f);
INSTRUCTION_PARSER(loop);
INSTRUCTION_PARSER(null); // endloop
INSTRUCTION_PARSER(null); // show
INSTRUCTION_PARSER(inc);
INSTRUCTION_PARSER(inc); // dec
INSTRUCTION_PARSER(set);
INSTRUCTION_PARSER(put);
INSTRUCTION_PARSER(call);
INSTRUCTION_PARSER(null); // wait
INSTRUCTION_PARSER(animation); // start
INSTRUCTION_PARSER(sound);
INSTRUCTION_PARSER(move);
INSTRUCTION_PARSER(endscript);
}
//
// a comment can appear both at location and Zone levels
// comments are displayed into rectangles on the screen
//
Common::String LocationParser_ns::parseComment() {
Common::String comment;
char buf[400];
do {
char *line = _script->readLine(buf, 400);
if (!scumm_stricmp(line, "endtext"))
break;
if (comment.size() > 0)
comment += " ";
comment += line;
} while (true);
if (comment.size() == 0) {
return 0;
}
return comment;
}
DECLARE_ZONE_PARSER(null) {
debugC(7, kDebugParser, "ZONE_PARSER(null) ");
}
DECLARE_ZONE_PARSER(endzone) {
debugC(7, kDebugParser, "ZONE_PARSER(endzone) ");
_parser->popTables();
}
DECLARE_ZONE_PARSER(limits) {
debugC(7, kDebugParser, "ZONE_PARSER(limits) ");
ctxt.z->setRect(atoi(_tokens[1]), atoi(_tokens[2]), atoi(_tokens[3]), atoi(_tokens[4]));
}
DECLARE_ZONE_PARSER(moveto) {
debugC(7, kDebugParser, "ZONE_PARSER(moveto) ");
ctxt.z->_moveTo.x = atoi(_tokens[1]);
ctxt.z->_moveTo.y = atoi(_tokens[2]);
}
uint32 LocationParser_ns::buildZoneType(const char *t0, const char* t1) {
uint16 it = 0;
if (t1[0] != '\0') {
it = 4 + _vm->_objectsNames->lookup(t1);
}
uint16 zt = _zoneTypeNames->lookup(t0);
return PACK_ZONETYPE(zt, it);
}
DECLARE_ZONE_PARSER(type) {
debugC(7, kDebugParser, "ZONE_PARSER(type) ");
ctxt.z->_type = buildZoneType(_tokens[1], _tokens[2]);
if (ACTIONTYPE(ctxt.z) != 0) {
parseZoneTypeBlock(ctxt.z);
}
_parser->popTables();
}
DECLARE_ZONE_PARSER(commands) {
debugC(7, kDebugParser, "ZONE_PARSER(commands) ");
parseCommands(ctxt.z->_commands);
}
DECLARE_ZONE_PARSER(label) {
debugC(7, kDebugParser, "ZONE_PARSER(label) ");
// printf("label: %s", _tokens[1]);
ctxt.z->_label = _vm->_gfx->renderFloatingLabel(_vm->_labelFont, _tokens[1]);
ctxt.z->_flags &= ~kFlagsNoName;
}
DECLARE_ZONE_PARSER(flags) {
debugC(7, kDebugParser, "ZONE_PARSER(flags) ");
uint16 _si = 1;
do {
char _al = _zoneFlagNames->lookup(_tokens[_si]);
_si++;
ctxt.z->_flags |= 1 << (_al - 1);
} while (!scumm_stricmp(_tokens[_si++], "|"));
}
void LocationParser_ns::parseZone(ZoneList &list, char *name) {
debugC(5, kDebugParser, "parseZone(name: %s)", name);
if (_vm->_location.findZone(name)) {
_zoneProg++;
_script->skip("endzone");
return;
}
ZonePtr z(new Zone);
_zoneProg++;
strncpy(z->_name, name, ZONENAME_LENGTH);
ctxt.z = z;
list.push_front(z);
_parser->pushTables(&_locationZoneParsers, _locationZoneStmt);
return;
}
void LocationParser_ns::parseGetData(ZonePtr z) {
TypeData *data = &z->u;
if (!scumm_stricmp(_tokens[0], "file")) {
GfxObj *obj = _vm->_gfx->loadGet(_tokens[1]);
obj->frame = 0;
obj->x = z->getX();
obj->y = z->getY();
obj->_prog = _zoneProg;
bool visible = (z->_flags & kFlagsRemove) == 0;
_vm->_gfx->showGfxObj(obj, visible);
data->_gfxobj = obj;
} else
if (!scumm_stricmp(_tokens[0], "icon")) {
data->_getIcon = 4 + _vm->_objectsNames->lookup(_tokens[1]);
}
}
void LocationParser_ns::parseExamineData(ZonePtr z) {
TypeData *data = &z->u;
if (!scumm_stricmp(_tokens[0], "file")) {
data->_filename = _tokens[1];
} else
if (!scumm_stricmp(_tokens[0], "desc")) {
data->_examineText = parseComment();
}
}
void LocationParser_ns::parseDoorData(ZonePtr z) {
TypeData *data = &z->u;
if (!scumm_stricmp(_tokens[0], "slidetext")) {
_vm->_location._slideText[0] = _tokens[1];
_vm->_location._slideText[1] = _tokens[2];
} else
if (!scumm_stricmp(_tokens[0], "location")) {
data->_doorLocation = _tokens[1];
} else
if (!scumm_stricmp(_tokens[0], "file")) {
GfxObj *obj = _vm->_gfx->loadDoor(_tokens[1]);
obj->frame = z->_flags & kFlagsClosed ? 0 : 1;
obj->x = z->getX();
obj->y = z->getY();
_vm->_gfx->showGfxObj(obj, true);
data->_gfxobj = obj;
} else
if (!scumm_stricmp(_tokens[0], "startpos")) {
data->_doorStartPos.x = atoi(_tokens[1]);
data->_doorStartPos.y = atoi(_tokens[2]);
data->_doorStartFrame = atoi(_tokens[3]);
}
}
void LocationParser_ns::parseMergeData(ZonePtr z) {
TypeData *data = &z->u;
if (!scumm_stricmp(_tokens[0], "obj1")) {
data->_mergeObj1 = 4 + _vm->_objectsNames->lookup(_tokens[1]);
} else
if (!scumm_stricmp(_tokens[0], "obj2")) {
data->_mergeObj2 = 4 + _vm->_objectsNames->lookup(_tokens[1]);
} else
if (!scumm_stricmp(_tokens[0], "newobj")) {
data->_mergeObj3 = 4 + _vm->_objectsNames->lookup(_tokens[1]);
}
}
void LocationParser_ns::parseHearData(ZonePtr z) {
TypeData *data = &z->u;
if (!scumm_stricmp(_tokens[0], "sound")) {
data->_filename = _tokens[1];
data->_hearChannel = atoi(_tokens[2]);
} else
if (!scumm_stricmp(_tokens[0], "freq")) {
data->_hearFreq = atoi(_tokens[1]);
}
}
void LocationParser_ns::parseSpeakData(ZonePtr z) {
TypeData *data = &z->u;
if (!scumm_stricmp(_tokens[0], "file")) {
data->_filename = _tokens[1];
} else
if (!scumm_stricmp(_tokens[0], "Dialogue")) {
data->_speakDialogue = parseDialogue();
}
}
typedef void (LocationParser_ns::*ZoneTypeParser)(ZonePtr);
static ZoneTypeParser parsers[] = {
0, // no type
&LocationParser_ns::parseExamineData,
&LocationParser_ns::parseDoorData,
&LocationParser_ns::parseGetData,
&LocationParser_ns::parseMergeData,
0, // taste
&LocationParser_ns::parseHearData,
0, // feel
&LocationParser_ns::parseSpeakData,
0, // none
0, // trap
0, // you
0 // command
};
void LocationParser_ns::parseZoneTypeBlock(ZonePtr z) {
debugC(7, kDebugParser, "parseZoneTypeBlock(name: %s, type: %x)", z->_name, z->_type);
ZoneTypeParser p = parsers[ACTIONTYPE(z)];
do {
if (p) {
(this->*p)(z);
}
_script->readLineToken(true);
} while (scumm_stricmp(_tokens[0], "endzone") && scumm_stricmp(_tokens[0], "endanimation"));
debugC(7, kDebugParser, "parseZoneTypeBlock() done");
}
} // namespace Parallaction