diff options
-rw-r--r-- | engines/sci/engine/game.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/kernel.cpp | 4 | ||||
-rw-r--r-- | engines/sci/engine/kernel.h | 1 | ||||
-rw-r--r-- | engines/sci/engine/kstring.cpp | 109 | ||||
-rw-r--r-- | engines/sci/engine/message.cpp | 495 | ||||
-rw-r--r-- | engines/sci/engine/message.h | 77 | ||||
-rw-r--r-- | engines/sci/engine/savegame.cpp | 3 | ||||
-rw-r--r-- | engines/sci/engine/state.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/state.h | 4 | ||||
-rw-r--r-- | engines/sci/gui/gui_gfx.cpp | 1 | ||||
-rw-r--r-- | engines/sci/gui/gui_picture.cpp | 1 |
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" |