aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/sci/engine/game.cpp2
-rw-r--r--engines/sci/engine/kernel.cpp4
-rw-r--r--engines/sci/engine/kernel.h1
-rw-r--r--engines/sci/engine/kstring.cpp109
-rw-r--r--engines/sci/engine/message.cpp495
-rw-r--r--engines/sci/engine/message.h77
-rw-r--r--engines/sci/engine/savegame.cpp3
-rw-r--r--engines/sci/engine/state.cpp2
-rw-r--r--engines/sci/engine/state.h4
-rw-r--r--engines/sci/gui/gui_gfx.cpp1
-rw-r--r--engines/sci/gui/gui_picture.cpp1
11 files changed, 381 insertions, 318 deletions
diff --git a/engines/sci/engine/game.cpp b/engines/sci/engine/game.cpp
index ba0b8e4823..75cafd7a43 100644
--- a/engines/sci/engine/game.cpp
+++ b/engines/sci/engine/game.cpp
@@ -32,6 +32,7 @@
#include "sci/engine/kernel.h"
#include "sci/engine/kernel_types.h"
#include "sci/gui/gui.h"
+#include "sci/engine/message.h"
#include "sci/gfx/gfx_widgets.h"
#include "sci/gfx/gfx_state_internal.h" // required for GfxPort, GfxVisual
#include "sci/gfx/menubar.h"
@@ -318,6 +319,7 @@ int game_init_sound(EngineState *s, int sound_flags) {
// Architectural stuff: Init/Unintialize engine
int script_init_engine(EngineState *s) {
s->_segMan = new SegManager(s->resMan);
+ s->_msgState = new MessageState(s->_segMan);
s->gc_countdown = GC_INTERVAL - 1;
SegmentId script_000_segment = s->_segMan->getScriptSegment(0, SCRIPT_GET_LOCK);
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 81a40b51e1..01e42df13d 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -160,7 +160,7 @@ static const char *sci_default_knames[SCI_KNAMES_DEFAULT_ENTRIES_NR] = {
/*0x79*/ "ATan",
/*0x7a*/ "Lock",
/*0x7b*/ "StrSplit",
- /*0x7c*/ "Message",
+ /*0x7c*/ "GetMessage",
/*0x7d*/ "IsItSkip",
/*0x7e*/ "MergePoly",
/*0x7f*/ "ResCheck",
@@ -325,6 +325,7 @@ SciKernelFunction kfunct_mappers[] = {
/*(?)*/ DEFUN("TimesCot", kTimesCot, "ii"),
/*(?)*/ DEFUN("TimesTan", kTimesTan, "ii"),
DEFUN("Message", kMessage, ".*"),
+ DEFUN("GetMessage", kGetMessage, "iiir"),
DEFUN("DoAudio", kDoAudio, ".*"),
DEFUN("DoSync", kDoSync, ".*"),
DEFUN("ResCheck", kResCheck, "iii*"),
@@ -700,6 +701,7 @@ void Kernel::setDefaultKernelNames() {
// KQ6CD calls unimplemented function 0x26
_kernelNames[0x26] = "Dummy";
_kernelNames[0x71] = "PalVary";
+ _kernelNames[0x7c] = "Message";
break;
default:
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index e82017e7a3..12003c650a 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -414,6 +414,7 @@ reg_t kMemoryInfo(EngineState *s, int argc, reg_t *argv);
reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv);
reg_t kTextSize(EngineState *s, int argc, reg_t *argv);
reg_t kIsItSkip(EngineState *s, int argc, reg_t *argv);
+reg_t kGetMessage(EngineState *s, int argc, reg_t *argv);
reg_t kMessage(EngineState *s, int argc, reg_t *argv);
reg_t kDoAudio(EngineState *s, int argc, reg_t *argv);
reg_t kDoSync(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 1e26450066..77c8310159 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -618,92 +618,40 @@ enum kMessageFunc {
K_MESSAGE_LASTMESSAGE
};
-reg_t kMessage(EngineState *s, int argc, reg_t *argv) {
- MessageTuple tuple;
- int func;
- // For earlier version of of this function (GetMessage)
- bool isGetMessage = argc == 4;
-
- if (isGetMessage) {
- func = K_MESSAGE_GET;
-
- tuple.noun = argv[0].toUint16();
- tuple.verb = argv[2].toUint16();
- tuple.cond = 0;
- tuple.seq = 1;
- } else {
- func = argv[0].toUint16();
+reg_t kGetMessage(EngineState *s, int argc, reg_t *argv) {
+ MessageTuple tuple = MessageTuple(argv[0].toUint16(), argv[2].toUint16());
- if (argc >= 6) {
- tuple.noun = argv[2].toUint16();
- tuple.verb = argv[3].toUint16();
- tuple.cond = argv[4].toUint16();
- tuple.seq = argv[5].toUint16();
- }
- }
+ s->_msgState->getMessage(argv[1].toUint16(), tuple, argv[3]);
- switch (func) {
- case K_MESSAGE_GET:
- case K_MESSAGE_NEXT: {
- reg_t bufferReg;
- Common::String str;
- reg_t retval;
-
- if (func == K_MESSAGE_GET) {
- s->_msgState.loadRes(s->resMan, argv[1].toUint16(), true);
- s->_msgState.findTuple(tuple);
-
- if (isGetMessage)
- bufferReg = (argc == 4 ? argv[3] : NULL_REG);
- else
- bufferReg = (argc == 7 ? argv[6] : NULL_REG);
- } else {
- bufferReg = (argc == 2 ? argv[1] : NULL_REG);
- }
-
- if (s->_msgState.getMessage()) {
- str = s->_msgState.getText();
- if (isGetMessage)
- retval = bufferReg;
- else
- retval = make_reg(0, s->_msgState.getTalker());
- } else {
- str = Common::String(DUMMY_MESSAGE);
- retval = NULL_REG;
- }
+ return argv[3];
+}
- if (!bufferReg.isNull()) {
- int len = str.size() + 1;
- SegmentRef buffer_r = s->_segMan->dereference(bufferReg);
- if (buffer_r.maxSize < len) {
- warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(bufferReg), len, str.c_str());
+reg_t kMessage(EngineState *s, int argc, reg_t *argv) {
+ uint func = argv[0].toUint16();
- // Set buffer to empty string if possible
- if (buffer_r.maxSize > 0)
- s->_segMan->strcpy(bufferReg, "");
- } else
- s->_segMan->strcpy(bufferReg, str.c_str());
+ if ((func != K_MESSAGE_NEXT) && (argc < 2)) {
+ warning("Message: not enough arguments passed to subfunction %d", func);
+ return NULL_REG;
+ }
- s->_msgState.gotoNext();
- }
+ MessageTuple tuple;
- return retval;
- }
- case K_MESSAGE_SIZE: {
- MessageState tempState;
+ if (argc >= 6)
+ tuple = MessageTuple(argv[2].toUint16(), argv[3].toUint16(), argv[4].toUint16(), argv[5].toUint16());
- if (tempState.loadRes(s->resMan, argv[1].toUint16(), false) && tempState.findTuple(tuple) && tempState.getMessage())
- return make_reg(0, tempState.getText().size() + 1);
- else
- return NULL_REG;
- }
+ switch (func) {
+ case K_MESSAGE_GET:
+ return make_reg(0, s->_msgState->getMessage(argv[1].toUint16(), tuple, (argc == 7 ? argv[6] : NULL_REG)));
+ case K_MESSAGE_NEXT:
+ return make_reg(0, s->_msgState->nextMessage((argc == 2 ? argv[1] : NULL_REG)));
+ case K_MESSAGE_SIZE:
+ return make_reg(0, s->_msgState->messageSize(argv[1].toUint16(), tuple));
case K_MESSAGE_REFCOND:
case K_MESSAGE_REFVERB:
case K_MESSAGE_REFNOUN: {
- MessageState tempState;
+ MessageTuple t;
- if (tempState.loadRes(s->resMan, argv[1].toUint16(), false) && tempState.findTuple(tuple)) {
- MessageTuple t = tempState.getRefTuple();
+ if (s->_msgState->messageRef(argv[1].toUint16(), tuple, t)) {
switch (func) {
case K_MESSAGE_REFCOND:
return make_reg(0, t.cond);
@@ -714,16 +662,17 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) {
}
}
- return NULL_REG;
+ return make_reg(0, -1);
}
case K_MESSAGE_LASTMESSAGE: {
- MessageTuple msg = s->_msgState.getLastTuple();
- int module = s->_msgState.getLastModule();
+ MessageTuple msg;
+ int module;
+
+ s->_msgState->lastQuery(module, msg);
+
byte *buffer = s->_segMan->derefBulkPtr(argv[1], 10);
if (buffer) {
- // FIXME: Is this correct? I.e., do we really write into a "raw" segment
- // here? Or maybe we want to write 4 reg_t instead?
assert(s->_segMan->dereference(argv[1]).isRaw);
WRITE_LE_UINT16(buffer, module);
diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp
index 7cab337199..45bddf890a 100644
--- a/engines/sci/engine/message.cpp
+++ b/engines/sci/engine/message.cpp
@@ -24,264 +24,361 @@
*/
#include "sci/engine/message.h"
+#include "sci/engine/kernel.h"
+#include "sci/engine/seg_manager.h"
#include "sci/tools.h"
namespace Sci {
-MessageTuple MessageState::getTuple() {
- MessageTuple t;
+struct MessageRecord {
+ MessageTuple tuple;
+ MessageTuple refTuple;
+ const char *string;
+ byte talker;
+};
- 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];
- }
+class MessageReader {
+public:
+ bool init() {
+ if (_headerSize > _size)
+ return false;
- return t;
-}
+ // Read message count from last word in header
+ _messageCount = READ_LE_UINT16(_data + _headerSize - 2);
-MessageTuple MessageState::getRefTuple() {
- MessageTuple t;
+ if (_messageCount * _recordSize + _headerSize > _size)
+ return false;
- 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];
+ return true;
}
- t.seq = 1;
- return t;
-}
+ virtual bool findRecord(const MessageTuple &tuple, MessageRecord &record) = 0;
-void MessageState::initCursor() {
- _engineCursor.index_record = _indexRecords;
- _engineCursor.index = 0;
- _engineCursor.nextSeq = 0;
-}
+ virtual ~MessageReader() { };
-void MessageState::advanceCursor(bool increaseSeq) {
- _engineCursor.index_record += _recordSize;
- _engineCursor.index++;
+protected:
+ MessageReader(const byte *data, uint size, uint headerSize, uint recordSize)
+ : _data(data), _size(size), _headerSize(headerSize), _recordSize(recordSize) { }
- if (increaseSeq)
- _engineCursor.nextSeq++;
-}
+ const byte *_data;
+ const uint _size;
+ const uint _headerSize;
+ const uint _recordSize;
+ uint _messageCount;
+};
-int MessageState::findTuple(MessageTuple &t) {
- if (_module == -1)
- return 0;
+class MessageReaderV2 : public MessageReader {
+public:
+ MessageReaderV2(byte *data, uint size) : MessageReader(data, size, 6, 4) { }
- // Reset the cursor
- initCursor();
- _engineCursor.nextSeq = t.seq;
+ bool findRecord(const MessageTuple &tuple, MessageRecord &record) {
+ const byte *recordPtr = _data + _headerSize;
- // Do a linear search for the message
- while (1) {
- MessageTuple looking_at = getTuple();
+ for (uint i = 0; i < _messageCount; i++) {
+ if ((recordPtr[0] == tuple.noun) && (recordPtr[1] == tuple.verb)) {
+ record.tuple = tuple;
+ record.refTuple = MessageTuple();
+ record.talker = 0;
+ record.string = (const char *)_data + READ_LE_UINT16(recordPtr + 2);
+ return true;
+ }
+ recordPtr += _recordSize;
+ }
- if (t.noun == looking_at.noun &&
- t.verb == looking_at.verb &&
- t.cond == looking_at.cond &&
- t.seq == looking_at.seq)
- break;
+ return false;
+ }
+};
+
+class MessageReaderV3 : public MessageReader {
+public:
+ MessageReaderV3(byte *data, uint size) : MessageReader(data, size, 8, 10) { }
+
+ bool findRecord(const MessageTuple &tuple, MessageRecord &record) {
+ const byte *recordPtr = _data + _headerSize;
+
+ for (uint i = 0; i < _messageCount; i++) {
+ if ((recordPtr[0] == tuple.noun) && (recordPtr[1] == tuple.verb)
+ && (recordPtr[2] == tuple.cond) && (recordPtr[3] == tuple.seq)) {
+ record.tuple = tuple;
+ record.refTuple = MessageTuple();
+ record.talker = recordPtr[4];
+ record.string = (const char *)_data + READ_LE_UINT16(recordPtr + 5);
+ return true;
+ }
+ recordPtr += _recordSize;
+ }
- advanceCursor(false);
+ return false;
+ }
+};
+
+class MessageReaderV4 : public MessageReader {
+public:
+ MessageReaderV4(byte *data, uint size) : MessageReader(data, size, 10, 11) { }
+
+ bool findRecord(const MessageTuple &tuple, MessageRecord &record) {
+ const byte *recordPtr = _data + _headerSize;
+
+ for (uint i = 0; i < _messageCount; i++) {
+ if ((recordPtr[0] == tuple.noun) && (recordPtr[1] == tuple.verb)
+ && (recordPtr[2] == tuple.cond) && (recordPtr[3] == tuple.seq)) {
+ record.tuple = tuple;
+ record.refTuple = MessageTuple(recordPtr[7], recordPtr[8], recordPtr[9]);
+ record.talker = recordPtr[4];
+ record.string = (const char *)_data + READ_LE_UINT16(recordPtr + 5);
+ return true;
+ }
+ recordPtr += _recordSize;
+ }
- // Message tuple is not present
- if (_engineCursor.index == _recordCount)
- return 0;
+ return false;
}
+};
- return 1;
-}
+bool MessageState::getRecord(CursorStack &stack, bool recurse, MessageRecord &record) {
+ Resource *res = ((SciEngine *)g_engine)->getResourceManager()->findResource(ResourceId(kResourceTypeMessage, stack.getModule()), 0);
-int MessageState::getMessage() {
- if (_module == -1)
- return 0;
+ if (!res) {
+ warning("Failed to open message resource %d", stack.getModule());
+ return NULL;
+ }
+
+ MessageReader *reader;
+ int version = READ_LE_UINT16(res->data) / 1000;
+
+ switch (version) {
+ case 2:
+ reader = new MessageReaderV2(res->data, res->size);
+ break;
+ case 3:
+ reader = new MessageReaderV3(res->data, res->size);
+ break;
+ case 4:
+ reader = new MessageReaderV4(res->data, res->size);
+ break;
+ default:
+ warning("Message: unsupported resource version");
+ return NULL;
+ }
+
+ if (!reader->init()) {
+ warning("Message: failed to read resource header");
+ return NULL;
+ }
- if (_engineCursor.index != _recordCount) {
- MessageTuple mesg = getTuple();
- MessageTuple ref = getRefTuple();
+ while (1) {
+ MessageTuple &t = stack.top();
- if (_engineCursor.nextSeq == mesg.seq) {
- // We found the right sequence number, check for recursion
+ if (!reader->findRecord(t, record)) {
+ // Tuple not found
+ if (recurse && (stack.size() > 1)) {
+ stack.pop();
+ continue;
+ }
- if (ref.noun != 0) {
- // Recursion, advance the current cursor and load the reference
- advanceCursor(true);
+ return false;
+ }
- _cursorStack.push(_engineCursor);
+ if (recurse) {
+ MessageTuple &ref = record.refTuple;
- if (findTuple(ref))
- return getMessage();
- else {
- // Reference not found
- return 0;
- }
- } else {
- // No recursion, we are done
- return 1;
+ if ((ref.noun != 0) && (ref.verb != 0) && (ref.cond != 0)) {
+ t.seq++;
+ stack.push(MessageTuple(ref.noun, ref.verb, ref.cond));
+ continue;
}
}
+
+ return true;
}
+}
+
+int MessageState::getMessage(int module, MessageTuple &t, reg_t buf) {
+ _cursorStack.init(module, t);
+ return nextMessage(buf);
+}
+
+int MessageState::nextMessage(reg_t buf) {
+ MessageRecord record;
+
+ if (!buf.isNull()) {
+ MessageTuple &t = _cursorStack.top();
+ Common::String finalStr;
+
+ if (getRecord(_cursorStack, true, record)) {
+ outputString(buf, processString(record.string));
+ _lastReturned = record.tuple;
+ _lastReturnedModule = _cursorStack.getModule();
+ t.seq++;
+ return record.talker;
+ } else {
+ outputString(buf, Common::String::printf("Msg %d: %d %d %d %d not found", _cursorStack.getModule(), t.noun, t.verb, t.cond, t.seq));
+ return 0;
+ }
+ } else {
+ CursorStack stack = _cursorStack;
- // 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();
+ if (getRecord(stack, true, record))
+ return record.talker;
+ else
+ return 0;
}
+}
+
+int MessageState::messageSize(int module, MessageTuple &t) {
+ CursorStack stack;
+ MessageRecord record;
- // Stack is empty, no message available
- return 0;
+ stack.init(module, t);
+ if (getRecord(stack, true, record))
+ return strlen(record.string);
+ else
+ return 0;
}
-int MessageState::getTalker() {
- return (_offsetTalker == -1) ? -1 : _engineCursor.index_record[_offsetTalker];
+bool MessageState::messageRef(int module, const MessageTuple &t, MessageTuple &ref) {
+ CursorStack stack;
+ MessageRecord record;
+
+ stack.init(module, t);
+ if (getRecord(stack, false, record)) {
+ ref = record.refTuple;
+ return true;
+ }
+
+ return false;
}
-MessageTuple &MessageState::getLastTuple() {
- return _lastReturned;
+void MessageState::pushCursorStack() {
+ _cursorStackStack.push(_cursorStack);
}
-int MessageState::getLastModule() {
- return _lastReturnedModule;
+void MessageState::popCursorStack() {
+ if (!_cursorStackStack.empty())
+ _cursorStack = _cursorStackStack.pop();
+ else
+ warning("Message: attempt to pop from empty stack");
}
-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];
- }
- }
- }
+int MessageState::hexDigitToInt(char h) {
+ if ((h >= 'A') && (h <= 'F'))
+ return h - 'A' + 10;
+
+ if ((h >= 'a') && (h <= 'f'))
+ return h - 'a' + 10;
- return strippedStr;
+ if ((h >= '0') && (h <= '9'))
+ return h - '0';
+
+ return -1;
}
-void MessageState::gotoNext() {
- _lastReturned = getTuple();
- _lastReturnedModule = _module;
- advanceCursor(true);
+bool MessageState::stringHex(Common::String &outStr, const Common::String &inStr, uint &index) {
+ // Hex escape sequences of the form \nn, where n is a hex digit
+ if (inStr[index] != '\\')
+ return false;
+
+ // Check for enough room for a hex escape sequence
+ if (index + 2 >= inStr.size())
+ return false;
+
+ int digit1 = hexDigitToInt(inStr[index + 1]);
+ int digit2 = hexDigitToInt(inStr[index + 2]);
+
+ // Check for hex
+ if ((digit1 == -1) || (digit2 == -1))
+ return false;
+
+ outStr += digit1 * 16 + digit2;
+ index += 3;
+
+ return true;
}
-int MessageState::getLength() {
- int offset = READ_LE_UINT16(_engineCursor.index_record + _offsetText);
- char *stringptr = (char *)_currentResource->data + offset;
- return strlen(stringptr);
+bool MessageState::stringLit(Common::String &outStr, const Common::String &inStr, uint &index) {
+ // Literal escape sequences of the form \n
+ if (inStr[index] != '\\')
+ return false;
+
+ // Check for enough room for a literal escape sequence
+ if (index + 1 >= inStr.size())
+ return false;
+
+ outStr += inStr[index + 1];
+ index += 2;
+
+ return true;
}
-int MessageState::loadRes(ResourceManager *resMan, int module, bool lock) {
- _cursorStack.clear();
+bool MessageState::stringStage(Common::String &outstr, const Common::String &inStr, uint &index) {
+ // Stage directions of the form (n*), where n is anything but a digit or a lowercase character
+ if (inStr[index] != '(')
+ return false;
+
+ for (uint i = index + 1; i < inStr.size(); i++) {
+ if (inStr[i] == ')') {
+ // Stage direction found, skip it
+ index = i + 1;
+
+ // Skip trailing white space
+ while ((index < inStr.size()) && ((inStr[index] == '\n') || (inStr[index] == '\r') || (inStr[index] == ' ')))
+ index++;
- if (_locked) {
- // We already have a locked resource
- if (_module == module) {
- // If it's the same resource, we are done
- return 1;
+ return true;
}
- // Otherwise, free the old resource
- resMan->unlockResource(_currentResource);
- _locked = false;
+ // If we find a lowercase character or a digit, it's not a stage direction
+ if (((inStr[i] >= 'a') && (inStr[i] <= 'z')) || ((inStr[i] >= '0') && (inStr[i] <= '9')))
+ return false;
}
- _currentResource = resMan->findResource(ResourceId(kResourceTypeMessage, module), lock);
+ // We ran into the end of the string without finding a closing bracket
+ return false;
+}
- if (_currentResource == NULL || _currentResource->data == NULL) {
- warning("Message: failed to load %d.msg", module);
- return 0;
- }
+Common::String MessageState::processString(const char *s) {
+ Common::String outStr;
+ Common::String inStr = Common::String(s);
- _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;
+ uint index = 0;
+
+ while (index < inStr.size()) {
+ // Check for hex escape sequence
+ if (stringHex(outStr, inStr, index))
+ continue;
+
+ // Check for literal escape sequence
+ if (stringLit(outStr, inStr, index))
+ continue;
+
+ // Check for stage direction
+ if (stringStage(outStr, inStr, index))
+ continue;
+
+ // None of the above, copy char
+ outStr += inStr[index++];
}
- _recordCount = READ_LE_UINT16(_currentResource->data + offsetCount);
- _indexRecords = _currentResource->data + offsetCount + 2;
+ return outStr;
+}
+
+void MessageState::outputString(reg_t buf, const Common::String &str) {
+ SegmentRef buffer_r = _segMan->dereference(buf);
- initCursor();
+ if ((unsigned)buffer_r.maxSize >= str.size()) {
+ _segMan->strcpy(buf, str.c_str());
+ } else {
+ warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(buf), str.size() + 1, str.c_str());
+
+ // Set buffer to empty string if possible
+ if (buffer_r.maxSize > 0)
+ _segMan->strcpy(buf, "");
+ }
+}
- return 1;
+void MessageState::lastQuery(int &module, MessageTuple &tuple) {
+ module = _lastReturnedModule;
+ tuple = _lastReturned;
}
} // End of namespace Sci
diff --git a/engines/sci/engine/message.h b/engines/sci/engine/message.h
index 0a1d1e909a..c07a937201 100644
--- a/engines/sci/engine/message.h
+++ b/engines/sci/engine/message.h
@@ -27,59 +27,66 @@
#define SCI_ENGINE_MESSAGE_H
#include "sci/resource.h"
+#include "sci/engine/vm_types.h"
#include "common/stack.h"
namespace Sci {
+class SegManager;
+class MessageRecord;
+
struct MessageTuple {
- int noun;
- int verb;
- int cond;
- int seq;
+ byte noun;
+ byte verb;
+ byte cond;
+ byte seq;
+
+ MessageTuple(byte noun_ = 0, byte verb_ = 0, byte cond_ = 0, byte seq_ = 1)
+ : noun(noun_), verb(verb_), cond(cond_), seq(seq_) { }
};
-struct IndexRecordCursor {
- byte *index_record;
- int index;
- int nextSeq;
+class CursorStack : public Common::Stack<MessageTuple> {
+public:
+ void init(int module, MessageTuple t) {
+ clear();
+ push(t);
+ _module = module;
+ }
+
+ int getModule() const { return _module; }
+
+private:
+ int _module;
};
-typedef Common::Stack<IndexRecordCursor> CursorStack;
+typedef Common::Stack<CursorStack> CursorStackStack;
-// FIXME: Documentation
class MessageState {
public:
- MessageState() : _module(-1), _locked(false) { }
- int findTuple(MessageTuple &t);
- MessageTuple getTuple();
- MessageTuple getRefTuple();
- int getMessage();
- void gotoNext();
- Common::String getText();
- int getTalker();
- int getLength();
- MessageTuple &getLastTuple();
- int getLastModule();
- int loadRes(ResourceManager *resMan, int module, bool lock);
+ MessageState(SegManager *segMan) : _segMan(segMan) { }
+ int getMessage(int module, MessageTuple &t, reg_t buf);
+ int nextMessage(reg_t buf);
+ int messageSize(int module, MessageTuple &t);
+ bool messageRef(int module, const MessageTuple &t, MessageTuple &ref);
+ void lastQuery(int &module, MessageTuple &tuple);
+ void pushCursorStack();
+ void popCursorStack();
private:
- void initCursor();
- void advanceCursor(bool increaseSeq);
+ bool getRecord(CursorStack &stack, bool recurse, MessageRecord &record);
+ void outputString(reg_t buf, const Common::String &str);
+ Common::String processString(const char *s);
+ int hexDigitToInt(char h);
+ bool stringHex(Common::String &outStr, const Common::String &inStr, uint &index);
+ bool stringLit(Common::String &outStr, const Common::String &inStr, uint &index);
+ bool stringStage(Common::String &outStr, const Common::String &inStr, uint &index);
- Resource *_currentResource;
- int _module;
- bool _locked;
- int _recordCount;
- byte *_indexRecords;
CursorStack _cursorStack;
- IndexRecordCursor _engineCursor;
+ CursorStackStack _cursorStackStack;
MessageTuple _lastReturned;
int _lastReturnedModule;
- int _offsetCondSeq;
- int _offsetRef;
- int _offsetTalker;
- int _offsetText;
- int _recordSize;
+
+ SegManager *_segMan;
};
} // End of namespace Sci
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 9f644dcd8e..8c48790935 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -35,6 +35,7 @@
#include "sci/sfx/core.h"
#include "sci/sfx/iterator.h"
#include "sci/engine/state.h"
+#include "sci/engine/message.h"
#include "sci/engine/savegame.h"
#include "sci/gui/gui.h"
@@ -775,7 +776,7 @@ EngineState *gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
reconstruct_sounds(retval);
// Message state:
- retval->_msgState = s->_msgState;
+ retval->_msgState = new MessageState(retval->_segMan);
retval->_gui->resetEngineState(retval);
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index 04dc6ccc83..328cdd4971 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -26,6 +26,7 @@
#include "sci/engine/state.h"
#include "sci/engine/vm.h"
#include "sci/engine/script.h"
+#include "sci/engine/message.h"
namespace Sci {
@@ -116,6 +117,7 @@ EngineState::EngineState(ResourceManager *res, Kernel *kernel, Vocabulary *voc,
EngineState::~EngineState() {
delete speedThrottler;
+ delete _msgState;
}
uint16 EngineState::currentRoomNumber() const {
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index 9ee8d271e6..0dacd1a23c 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -39,7 +39,6 @@ namespace Common {
#include "sci/vocabulary.h"
#include "sci/resource.h"
#include "sci/engine/kernel.h" // for kfunct_sig_pair_t
-#include "sci/engine/message.h" // for MessageState
#include "sci/engine/script.h"
#include "sci/engine/seg_manager.h"
#include "sci/gfx/gfx_system.h"
@@ -50,6 +49,7 @@ namespace Sci {
class Menubar;
class SciGui;
class SciGuiCursor;
+class MessageState;
struct GfxState;
struct GfxPort;
@@ -320,7 +320,7 @@ public:
SegManager *_segMan;
int gc_countdown; /**< Number of kernel calls until next gc */
- MessageState _msgState;
+ MessageState *_msgState;
SpeedThrottler *speedThrottler;
diff --git a/engines/sci/gui/gui_gfx.cpp b/engines/sci/gui/gui_gfx.cpp
index 9bf0784887..32b3d0f0e9 100644
--- a/engines/sci/gui/gui_gfx.cpp
+++ b/engines/sci/gui/gui_gfx.cpp
@@ -24,6 +24,7 @@
*/
#include "common/util.h"
+#include "common/stack.h"
#include "graphics/primitives.h"
#include "sci/sci.h"
diff --git a/engines/sci/gui/gui_picture.cpp b/engines/sci/gui/gui_picture.cpp
index 80f1df1c00..95914cd129 100644
--- a/engines/sci/gui/gui_picture.cpp
+++ b/engines/sci/gui/gui_picture.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "common/stack.h"
#include "sci/sci.h"
#include "sci/engine/state.h"
#include "sci/tools.h"