scummvm/engines/hugo/parser.cpp
Littleboy b694a78f62 ANALYSIS: Add static casts to is* functions
This fixes a potential problem with passing char values that would be sign-extended and yield unexpected results.
See http://msdn.microsoft.com/en-us/library/ms245348.aspx
2011-06-23 08:52:52 -04:00

490 lines
14 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.
*
*/
/*
* This code is based on original Hugo Trilogy source code
*
* Copyright (c) 1989-1995 David P. Gray
*
*/
#include "common/events.h"
#include "common/textconsole.h"
#include "hugo/hugo.h"
#include "hugo/display.h"
#include "hugo/parser.h"
#include "hugo/file.h"
#include "hugo/schedule.h"
#include "hugo/util.h"
#include "hugo/route.h"
#include "hugo/sound.h"
#include "hugo/object.h"
#include "hugo/text.h"
#include "hugo/inventory.h"
namespace Hugo {
Parser::Parser(HugoEngine *vm) : _vm(vm), _putIndex(0), _getIndex(0), _arrayReqs(0), _catchallList(0), _backgroundObjects(0), _cmdList(0) {
_cmdLineIndex = 0;
_cmdLineTick = 0;
_cmdLineCursor = '_';
_cmdLine[0] = '\0';
_cmdListSize = 0;
_checkDoubleF1Fl = false;
_backgroundObjectsSize = 0;
}
Parser::~Parser() {
}
uint16 Parser::getCmdDefaultVerbIdx(const uint16 index) const {
return _cmdList[index][0].verbIndex;
}
/**
* Read a cmd structure from Hugo.dat
*/
void Parser::readCmd(Common::ReadStream &in, cmd &curCmd) {
curCmd.verbIndex = in.readUint16BE();
curCmd.reqIndex = in.readUint16BE();
curCmd.textDataNoCarryIndex = in.readUint16BE();
curCmd.reqState = in.readByte();
curCmd.newState = in.readByte();
curCmd.textDataWrongIndex = in.readUint16BE();
curCmd.textDataDoneIndex = in.readUint16BE();
curCmd.actIndex = in.readUint16BE();
}
/**
* Load _cmdList from Hugo.dat
*/
void Parser::loadCmdList(Common::ReadStream &in) {
cmd tmpCmd;
memset(&tmpCmd, 0, sizeof(tmpCmd));
for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
uint16 numElem = in.readUint16BE();
if (varnt == _vm->_gameVariant) {
_cmdListSize = numElem;
_cmdList = (cmd **)malloc(sizeof(cmd *) * _cmdListSize);
}
for (int16 i = 0; i < numElem; i++) {
uint16 numSubElem = in.readUint16BE();
if (varnt == _vm->_gameVariant)
_cmdList[i] = (cmd *)malloc(sizeof(cmd) * numSubElem);
for (int16 j = 0; j < numSubElem; j++)
readCmd(in, (varnt == _vm->_gameVariant) ? _cmdList[i][j] : tmpCmd);
}
}
}
void Parser::readBG(Common::ReadStream &in, background_t &curBG) {
curBG.verbIndex = in.readUint16BE();
curBG.nounIndex = in.readUint16BE();
curBG.commentIndex = in.readSint16BE();
curBG.matchFl = (in.readByte() != 0);
curBG.roomState = in.readByte();
curBG.bonusIndex = in.readByte();
}
/**
* Read _backgrounObjects from Hugo.dat
*/
void Parser::loadBackgroundObjects(Common::ReadStream &in) {
background_t tmpBG;
memset(&tmpBG, 0, sizeof(tmpBG));
for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
uint16 numElem = in.readUint16BE();
if (varnt == _vm->_gameVariant) {
_backgroundObjectsSize = numElem;
_backgroundObjects = (background_t **)malloc(sizeof(background_t *) * numElem);
}
for (int i = 0; i < numElem; i++) {
uint16 numSubElem = in.readUint16BE();
if (varnt == _vm->_gameVariant)
_backgroundObjects[i] = (background_t *)malloc(sizeof(background_t) * numSubElem);
for (int j = 0; j < numSubElem; j++)
readBG(in, (varnt == _vm->_gameVariant) ? _backgroundObjects[i][j] : tmpBG);
}
}
}
/**
* Read _catchallList from Hugo.dat
*/
void Parser::loadCatchallList(Common::ReadStream &in) {
background_t *wrkCatchallList = 0;
background_t tmpBG;
memset(&tmpBG, 0, sizeof(tmpBG));
for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
uint16 numElem = in.readUint16BE();
if (varnt == _vm->_gameVariant)
_catchallList = wrkCatchallList = (background_t *)malloc(sizeof(background_t) * numElem);
for (int i = 0; i < numElem; i++)
readBG(in, (varnt == _vm->_gameVariant) ? wrkCatchallList[i] : tmpBG);
}
}
void Parser::loadArrayReqs(Common::SeekableReadStream &in) {
_arrayReqs = _vm->loadLongArray(in);
}
/**
* Search background command list for this screen for supplied object.
* Return first associated verb (not "look") or 0 if none found.
*/
const char *Parser::useBG(const char *name) {
debugC(1, kDebugEngine, "useBG(%s)", name);
objectList_t p = _backgroundObjects[*_vm->_screen_p];
for (int i = 0; p[i].verbIndex != 0; i++) {
if ((name == _vm->_text->getNoun(p[i].nounIndex, 0) &&
p[i].verbIndex != _vm->_look) &&
((p[i].roomState == kStateDontCare) || (p[i].roomState == _vm->_screenStates[*_vm->_screen_p])))
return _vm->_text->getVerb(p[i].verbIndex, 0);
}
return 0;
}
void Parser::freeParser() {
if (_arrayReqs) {
for (int i = 0; _arrayReqs[i] != 0; i++)
free(_arrayReqs[i]);
free(_arrayReqs);
}
free(_catchallList);
if (_backgroundObjects) {
for (int i = 0; i < _backgroundObjectsSize; i++)
free(_backgroundObjects[i]);
free(_backgroundObjects);
}
if (_cmdList) {
for (int i = 0; i < _cmdListSize; i++)
free(_cmdList[i]);
free(_cmdList);
}
}
void Parser::switchTurbo() {
_vm->_config.turboFl = !_vm->_config.turboFl;
}
/**
* Add any new chars to line buffer and display them.
* If CR pressed, pass line to LineHandler()
*/
void Parser::charHandler() {
debugC(4, kDebugParser, "charHandler");
status_t &gameStatus = _vm->getGameStatus();
// Check for one or more characters in ring buffer
while (_getIndex != _putIndex) {
char c = _ringBuffer[_getIndex++];
if (_getIndex >= sizeof(_ringBuffer))
_getIndex = 0;
switch (c) {
case Common::KEYCODE_BACKSPACE: // Rubout key
if (_cmdLineIndex)
_cmdLine[--_cmdLineIndex] = '\0';
break;
case Common::KEYCODE_RETURN: // EOL, pass line to line handler
if (_cmdLineIndex && (_vm->_hero->pathType != kPathQuiet)) {
// Remove inventory bar if active
if (_vm->_inventory->getInventoryState() == kInventoryActive)
_vm->_inventory->setInventoryState(kInventoryUp);
// Call Line handler and reset line
command(_cmdLine);
_cmdLine[_cmdLineIndex = 0] = '\0';
}
break;
default: // Normal text key, add to line
if (_cmdLineIndex >= kMaxLineSize) {
//MessageBeep(MB_ICONASTERISK);
warning("STUB: MessageBeep() - Command line too long");
} else if (isprint(static_cast<unsigned char>(c))) {
_cmdLine[_cmdLineIndex++] = c;
_cmdLine[_cmdLineIndex] = '\0';
}
break;
}
}
// See if time to blink cursor, set cursor character
if ((_cmdLineTick++ % (_vm->getTPS() / kBlinksPerSec)) == 0)
_cmdLineCursor = (_cmdLineCursor == '_') ? ' ' : '_';
// See if recall button pressed
if (gameStatus.recallFl) {
// Copy previous line to current cmdline
gameStatus.recallFl = false;
strcpy(_cmdLine, _vm->_line);
_cmdLineIndex = strlen(_cmdLine);
}
sprintf(_vm->_statusLine, ">%s%c", _cmdLine, _cmdLineCursor);
sprintf(_vm->_scoreLine, "F1-Help %s Score: %d of %d Sound %s", (_vm->_config.turboFl) ? "T" : " ", _vm->getScore(), _vm->getMaxScore(), (_vm->_config.soundFl) ? "On" : "Off");
// See if "look" button pressed
if (gameStatus.lookFl) {
command("look around");
gameStatus.lookFl = false;
}
}
void Parser::keyHandler(Common::Event event) {
debugC(1, kDebugParser, "keyHandler(%d)", event.kbd.keycode);
status_t &gameStatus = _vm->getGameStatus();
uint16 nChar = event.kbd.keycode;
if ((event.kbd.hasFlags(Common::KBD_ALT)) || (event.kbd.hasFlags(Common::KBD_SCRL)))
return;
if (event.kbd.hasFlags(Common::KBD_CTRL)) {
switch (nChar) {
case Common::KEYCODE_d:
_vm->getDebugger()->attach();
_vm->getDebugger()->onFrame();
break;
case Common::KEYCODE_l:
_vm->_file->restoreGame(-1);
break;
case Common::KEYCODE_n:
if (Utils::yesNoBox("Are you sure you want to start a new game?"))
_vm->_file->restoreGame(0);
break;
case Common::KEYCODE_s:
if (gameStatus.viewState == kViewPlay) {
if (gameStatus.gameOverFl)
_vm->gameOverMsg();
else
_vm->_file->saveGame(-1, Common::String());
}
break;
default:
break;
}
return;
}
// Process key down event - called from OnKeyDown()
switch (nChar) { // Set various toggle states
case Common::KEYCODE_ESCAPE: // Escape key, may want to QUIT
if (gameStatus.viewState == kViewIntro)
gameStatus.skipIntroFl = true;
else {
if (_vm->_inventory->getInventoryState() == kInventoryActive) // Remove inventory, if displayed
_vm->_inventory->setInventoryState(kInventoryUp);
_vm->_screen->resetInventoryObjId();
}
break;
case Common::KEYCODE_END:
case Common::KEYCODE_HOME:
case Common::KEYCODE_PAGEUP:
case Common::KEYCODE_PAGEDOWN:
case Common::KEYCODE_KP1:
case Common::KEYCODE_KP7:
case Common::KEYCODE_KP9:
case Common::KEYCODE_KP3:
case Common::KEYCODE_LEFT:
case Common::KEYCODE_RIGHT:
case Common::KEYCODE_UP:
case Common::KEYCODE_DOWN:
case Common::KEYCODE_KP4:
case Common::KEYCODE_KP6:
case Common::KEYCODE_KP8:
case Common::KEYCODE_KP2:
_vm->_route->resetRoute(); // Stop any automatic route
_vm->_route->setWalk(nChar); // Direction of hero travel
break;
case Common::KEYCODE_F1: // User Help (DOS)
if (_checkDoubleF1Fl)
gameStatus.helpFl = true;
else
_vm->_screen->userHelp();
_checkDoubleF1Fl = !_checkDoubleF1Fl;
break;
case Common::KEYCODE_F2: // Toggle sound
_vm->_sound->toggleSound();
_vm->_sound->toggleMusic();
break;
case Common::KEYCODE_F3: // Repeat last line
gameStatus.recallFl = true;
break;
case Common::KEYCODE_F4: // Save game
if (gameStatus.viewState == kViewPlay) {
if (gameStatus.gameOverFl)
_vm->gameOverMsg();
else
_vm->_file->saveGame(-1, Common::String());
}
break;
case Common::KEYCODE_F5: // Restore game
_vm->_file->restoreGame(-1);
break;
case Common::KEYCODE_F6: // Inventory
showInventory();
break;
case Common::KEYCODE_F8: // Turbo mode
switchTurbo();
break;
case Common::KEYCODE_F9: // Boss button
warning("STUB: F9 (DOS) - BossKey");
break;
default: // Any other key
if (!gameStatus.storyModeFl) { // Keyboard disabled
// Add printable keys to ring buffer
uint16 bnext = _putIndex + 1;
if (bnext >= sizeof(_ringBuffer))
bnext = 0;
if (bnext != _getIndex) {
_ringBuffer[_putIndex] = event.kbd.ascii;
_putIndex = bnext;
}
}
break;
}
if (_checkDoubleF1Fl && (nChar != Common::KEYCODE_F1))
_checkDoubleF1Fl = false;
}
/**
* Perform an immediate command. Takes parameters a la sprintf
* Assumes final string will not overrun line[] length
*/
void Parser::command(const char *format, ...) {
debugC(1, kDebugParser, "Command(%s, ...)", format);
va_list marker;
va_start(marker, format);
// TODO:
// _vm->_line = Common::String::vformat(format, marker);
vsprintf(_vm->_line, format, marker);
va_end(marker);
lineHandler();
}
/**
* Locate any member of object name list appearing in command line
*/
bool Parser::isWordPresent(char **wordArr) const {
debugC(1, kDebugParser, "isWordPresent(%s)", wordArr[0]);
if (wordArr != 0) {
for (int i = 0; strlen(wordArr[i]); i++) {
if (strstr(_vm->_line, wordArr[i]))
return true;
}
}
return false;
}
/**
* Locate word in list of nouns and return ptr to first string in noun list
*/
const char *Parser::findNoun() const {
debugC(1, kDebugParser, "findNoun()");
for (int i = 0; _vm->_text->getNounArray(i); i++) {
for (int j = 0; strlen(_vm->_text->getNoun(i, j)); j++) {
if (strstr(_vm->_line, _vm->_text->getNoun(i, j)))
return _vm->_text->getNoun(i, 0);
}
}
return 0;
}
/**
* Locate word in list of verbs and return ptr to first string in verb list
*/
const char *Parser::findVerb() const {
debugC(1, kDebugParser, "findVerb()");
for (int i = 0; _vm->_text->getVerbArray(i); i++) {
for (int j = 0; strlen(_vm->_text->getVerb(i, j)); j++) {
if (strstr(_vm->_line, _vm->_text->getVerb(i, j)))
return _vm->_text->getVerb(i, 0);
}
}
return 0;
}
/**
* Show user all objects being carried in a variable width 2 column format
*/
void Parser::showDosInventory() const {
debugC(1, kDebugParser, "showDosInventory()");
static const char *blanks = " ";
uint16 index = 0, len1 = 0, len2 = 0;
for (int i = 0; i < _vm->_object->_numObj; i++) { // Find widths of 2 columns
if (_vm->_object->isCarried(i)) {
uint16 len = strlen(_vm->_text->getNoun(_vm->_object->_objects[i].nounIndex, 2));
if (index++ & 1) // Right hand column
len2 = (len > len2) ? len : len2;
else
len1 = (len > len1) ? len : len1;
}
}
len1 += 1; // For gap between columns
if (len1 + len2 < (uint16)strlen(_vm->_text->getTextParser(kTBOutro)))
len1 = strlen(_vm->_text->getTextParser(kTBOutro));
Common::String buffer;
assert(len1 + len2 - strlen(_vm->_text->getTextParser(kTBIntro)) / 2 < strlen(blanks));
buffer = Common::String(blanks, (len1 + len2 - strlen(_vm->_text->getTextParser(kTBIntro))) / 2);
buffer += Common::String(_vm->_text->getTextParser(kTBIntro)) + "\n";
index = 0;
for (int i = 0; i < _vm->_object->_numObj; i++) { // Assign strings
if (_vm->_object->isCarried(i)) {
if (index++ & 1)
buffer += Common::String(_vm->_text->getNoun(_vm->_object->_objects[i].nounIndex, 2)) + "\n";
else
buffer += Common::String(_vm->_text->getNoun(_vm->_object->_objects[i].nounIndex, 2)) + Common::String(blanks, len1 - strlen(_vm->_text->getNoun(_vm->_object->_objects[i].nounIndex, 2)));
}
}
if (index & 1)
buffer += "\n";
buffer += Common::String(_vm->_text->getTextParser(kTBOutro));
Utils::notifyBox(buffer.c_str());
}
} // End of namespace Hugo