scummvm/engines/sci/engine/message.cpp
Martin Kiewitz 06f5393ed4 SCI: fixed crash in sq5/german (umlauts)
svn-id: r44587
2009-10-04 06:39:07 +00:00

288 lines
6.7 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 "sci/engine/message.h"
#include "sci/tools.h"
namespace Sci {
MessageTuple MessageState::getTuple() {
MessageTuple t;
t.noun = _engineCursor.index_record[0];
t.verb = _engineCursor.index_record[1];
if (_offsetCondSeq == -1) {
t.cond = 0;
t.seq = 1;
} else {
t.cond = _engineCursor.index_record[_offsetCondSeq];
t.seq = _engineCursor.index_record[_offsetCondSeq + 1];
}
return t;
}
MessageTuple MessageState::getRefTuple() {
MessageTuple t;
if (_offsetRef == -1) {
t.noun = 0;
t.verb = 0;
t.cond = 0;
} else {
t.noun = _engineCursor.index_record[_offsetRef];
t.verb = _engineCursor.index_record[_offsetRef + 1];
t.cond = _engineCursor.index_record[_offsetRef + 2];
}
t.seq = 1;
return t;
}
void MessageState::initCursor() {
_engineCursor.index_record = _indexRecords;
_engineCursor.index = 0;
_engineCursor.nextSeq = 0;
}
void MessageState::advanceCursor(bool increaseSeq) {
_engineCursor.index_record += _recordSize;
_engineCursor.index++;
if (increaseSeq)
_engineCursor.nextSeq++;
}
int MessageState::findTuple(MessageTuple &t) {
if (_module == -1)
return 0;
// Reset the cursor
initCursor();
_engineCursor.nextSeq = t.seq;
// Do a linear search for the message
while (1) {
MessageTuple looking_at = getTuple();
if (t.noun == looking_at.noun &&
t.verb == looking_at.verb &&
t.cond == looking_at.cond &&
t.seq == looking_at.seq)
break;
advanceCursor(false);
// Message tuple is not present
if (_engineCursor.index == _recordCount)
return 0;
}
return 1;
}
int MessageState::getMessage() {
if (_module == -1)
return 0;
if (_engineCursor.index != _recordCount) {
MessageTuple mesg = getTuple();
MessageTuple ref = getRefTuple();
if (_engineCursor.nextSeq == mesg.seq) {
// We found the right sequence number, check for recursion
if (ref.noun != 0) {
// Recursion, advance the current cursor and load the reference
advanceCursor(true);
_cursorStack.push(_engineCursor);
if (findTuple(ref))
return getMessage();
else {
// Reference not found
return 0;
}
} else {
// No recursion, we are done
return 1;
}
}
}
// We either ran out of records, or found an incorrect sequence number. Go to previous stack frame.
if (!_cursorStack.empty()) {
_engineCursor = _cursorStack.pop();
return getMessage();
}
// Stack is empty, no message available
return 0;
}
int MessageState::getTalker() {
return (_offsetTalker == -1) ? -1 : _engineCursor.index_record[_offsetTalker];
}
MessageTuple &MessageState::getLastTuple() {
return _lastReturned;
}
int MessageState::getLastModule() {
return _lastReturnedModule;
}
Common::String MessageState::getText() {
char *str = (char *)_currentResource->data + READ_LE_UINT16(_engineCursor.index_record + _offsetText);
Common::String strippedStr;
Common::String skippedSubstr;
bool skipping = false;
for (uint i = 0; i < strlen(str); i++) {
if (skipping) {
// Skip stage direction
skippedSubstr += str[i];
// Hopefully these locale-dependant functions are good enough
if (islower((unsigned char)str[i]) || isdigit((unsigned char)str[i])) {
// Lowercase or digit found, this is not a stage direction
strippedStr += skippedSubstr;
skipping = false;
} else if (str[i] == ')') {
// End of stage direction, skip trailing white space
while ((i + 1 < strlen(str)) && isspace(str[i + 1]))
i++;
skipping = false;
}
} else {
if (str[i] == '(') {
// Start skipping stage direction
skippedSubstr = str[i];
skipping = true;
} else if (str[i] == '\\') {
// Escape sequence
if ((i + 2 < strlen(str)) && isdigit(str[i + 1]) && isdigit(str[i + 2])) {
// Hex escape sequence
char hexStr[3];
hexStr[0] = str[++i];
hexStr[1] = str[++i];
hexStr[2] = 0;
char *endptr;
int hexNr = strtol(hexStr, &endptr, 16);
if (*endptr == 0)
strippedStr += hexNr;
} else if (i + 1 < strlen(str)) {
// Literal escape sequence
strippedStr += str[++i];
}
} else {
strippedStr += str[i];
}
}
}
return strippedStr;
}
void MessageState::gotoNext() {
_lastReturned = getTuple();
_lastReturnedModule = _module;
advanceCursor(true);
}
int MessageState::getLength() {
int offset = READ_LE_UINT16(_engineCursor.index_record + _offsetText);
char *stringptr = (char *)_currentResource->data + offset;
return strlen(stringptr);
}
int MessageState::loadRes(ResourceManager *resMan, int module, bool lock) {
_cursorStack.clear();
if (_locked) {
// We already have a locked resource
if (_module == module) {
// If it's the same resource, we are done
return 1;
}
// Otherwise, free the old resource
resMan->unlockResource(_currentResource);
_locked = false;
}
_currentResource = resMan->findResource(ResourceId(kResourceTypeMessage, module), lock);
if (_currentResource == NULL || _currentResource->data == NULL) {
warning("Message: failed to load %d.msg", module);
return 0;
}
_module = module;
_locked = lock;
int version = READ_LE_UINT16(_currentResource->data);
debug(5, "Message: reading resource %d.msg, version %d.%03d", _module, version / 1000, version % 1000);
int offsetCount;
// FIXME: Correct/extend this data by examining more games
if (version < 3000) {
_offsetCondSeq = -1;
_offsetTalker = -1;
_offsetRef = -1;
_offsetText = 2;
_recordSize = 4;
offsetCount = 4;
} else if (version < 4000) {
_offsetCondSeq = 2;
_offsetTalker = 4;
_offsetRef = -1;
_offsetText = 5;
_recordSize = 10;
offsetCount = 6;
} else {
_offsetCondSeq = 2;
_offsetTalker = 4;
_offsetRef = 7;
_offsetText = 5;
_recordSize = 11;
offsetCount = 8;
}
_recordCount = READ_LE_UINT16(_currentResource->data + offsetCount);
_indexRecords = _currentResource->data + offsetCount + 2;
initCursor();
return 1;
}
} // End of namespace Sci