diff options
Diffstat (limited to 'engines/mads/conversations.cpp')
-rw-r--r-- | engines/mads/conversations.cpp | 255 |
1 files changed, 224 insertions, 31 deletions
diff --git a/engines/mads/conversations.cpp b/engines/mads/conversations.cpp index 10ce41d793..d45d49834a 100644 --- a/engines/mads/conversations.cpp +++ b/engines/mads/conversations.cpp @@ -24,27 +24,61 @@ #include "mads/mads.h" #include "mads/compression.h" #include "common/file.h" +#include "common/util.h" // for Common::hexdump namespace MADS { +#define MAX_SPEAKERS 5 + +enum DialogCommands { + cmdNodeEnd = 0, + // + cmdHide = 2, + cmdUnhide = 3, + cmdMessage = 4, + // + // + cmdGoto = 7, + // + cmdAssign = 9, + cmdDialogEnd = 255 +}; + +struct ConvDialog { + int16 textLineIndex; // 0-based + int16 speechIndex; // 1-based + uint16 nodeOffset; // offset in section 6 + uint16 nodeSize; // size in section 6 +}; + +struct ConvNode { + uint16 index; + uint16 dialogCount; + int16 unk1; + int16 unk2; + int16 unk3; + + Common::Array<ConvDialog> dialogs; +}; + struct ConvData { - uint16 nodes; - uint16 unk1; - uint16 messages; + uint16 nodeCount; // conversation nodes, each one containing several dialog options and messages + uint16 dialogCount; // messages (non-selectable) + texts (selectable) + uint16 messageCount; // messages (non-selectable) + uint16 textLineCount; uint16 unk2; - uint16 unk3; - uint16 imports; - uint16 speakers; - Common::List<Common::String> portraits; + uint16 importCount; + uint16 speakerCount; + Common::String portraits[MAX_SPEAKERS]; + bool speakerExists[MAX_SPEAKERS]; Common::String speechFile; + Common::Array<Common::String> textLines; + Common::Array<ConvNode> convNodes; }; -#define MAX_SPEAKERS 5 - GameConversation::GameConversation(MADSEngine *vm) : _vm(vm) { _running = _restoreRunning = 0; - _nextStartNode = nullptr; } GameConversation::~GameConversation() { @@ -53,6 +87,8 @@ GameConversation::~GameConversation() { void GameConversation::get(int id) { Common::File inFile; Common::String fileName = Common::String::format("CONV%03d.CNV", id); + // TODO: Also handle the .CND file + inFile.open(fileName); MadsPack convFileUnpacked(&inFile); Common::SeekableReadStream *convFile = convFileUnpacked.getItemStream(0); @@ -61,48 +97,205 @@ void GameConversation::get(int id) { ConvData conv; - // Section 0: Header - conv.nodes = convFile->readUint16LE(); - conv.unk1 = convFile->readUint16LE(); - conv.messages = convFile->readUint16LE(); + // **** Section 0: Header ************************************************* + conv.nodeCount = convFile->readUint16LE(); + conv.dialogCount = convFile->readUint16LE(); + conv.messageCount = convFile->readUint16LE(); + conv.textLineCount = convFile->readUint16LE(); conv.unk2 = convFile->readUint16LE(); - conv.unk3 = convFile->readUint16LE(); - conv.imports = convFile->readUint16LE(); - conv.speakers = convFile->readUint16LE(); + conv.importCount = convFile->readUint16LE(); + conv.speakerCount = convFile->readUint16LE(); - debug("Conv %d has %d nodes, %d messages, %d imports and %d speakers", id, conv.nodes, conv.messages, conv.imports, conv.speakers); + //debug("Conv %d has %d nodes, %d dialogs, %d messages, %d text lines, %d unk2, %d imports and %d speakers", + // id, conv.nodeCount, conv.dialogCount, conv.messageCount, conv.textLineCount, conv.unk2, conv.importCount, conv.speakerCount); for (uint16 i = 0; i < MAX_SPEAKERS; i++) { convFile->read(buffer, 16); - Common::String portrait = buffer; - debug("Speaker %d, portrait %s", i, portrait.c_str()); - conv.portraits.push_back(portrait); + conv.portraits[i] = buffer; + //debug("Speaker %d, portrait %s", i, conv.portraits[i].c_str()); } for (uint16 i = 0; i < MAX_SPEAKERS; i++) { - convFile->skip(2); + conv.speakerExists[i] = convFile->readUint16LE(); + //debug("Speaker %d exists: %d", i, conv.speakerExists[i]); } convFile->read(buffer, 14); conv.speechFile = Common::String(buffer); - debug("Speech file %s", conv.speechFile.c_str()); + //debug("Speech file %s", conv.speechFile.c_str()); - convFile->skip(32); // unknown bytes + uint16 textLength = convFile->readUint16LE(); // Total text length in section 5 + convFile->skip(2); // TODO: unknown + uint16 commandLength = convFile->readUint16LE(); // Total length of commands in section 6 + //debug("Node entry commands length: %d", commandLength); - // Section 1 + /*debug("Section 0 unknown bytes"); + byte *tmp0 = new byte[26]; + convFile->read(tmp0, 26); + Common::hexdump(tmp0, 26); + delete[] tmp0;*/ + + // **** Section 1: Nodes ************************************************** convFile = convFileUnpacked.getItemStream(1); - for (uint16 i = 0; i < conv.nodes; i++) { - uint16 nodeIndex = convFile->readUint16LE(); - debug("Node %d, index %d", i, nodeIndex); - convFile->skip(2); // 01 00 - convFile->skip(6); + for (uint16 i = 0; i < conv.nodeCount; i++) { + ConvNode node; + node.index = convFile->readUint16LE(); + node.dialogCount = convFile->readUint16LE(); + node.unk1 = convFile->readSint16LE(); // TODO + node.unk2 = convFile->readSint16LE(); // TODO + node.unk3 = convFile->readSint16LE(); // TODO + conv.convNodes.push_back(node); + //debug("Node %d, index %d, entries %d - %d, %d, %d", i, node.index, node.dialogCount, node.unk1, node.unk2, node.unk3); } - // TODO: Read the rest of the sections + // **** Section 2: Dialogs ************************************************ + convFile = convFileUnpacked.getItemStream(2); + assert(convFile->size() == conv.dialogCount * 8); + + for (uint16 i = 0; i < conv.nodeCount; i++) { + uint16 dialogCount = conv.convNodes[i].dialogCount; + + for (uint16 j = 0; j < dialogCount; j++) { + ConvDialog dialog; + dialog.textLineIndex = convFile->readSint16LE(); + dialog.speechIndex = convFile->readSint16LE(); + dialog.nodeOffset = convFile->readUint16LE(); + dialog.nodeSize = convFile->readUint16LE(); + conv.convNodes[i].dialogs.push_back(dialog); + //debug("Node %d, dialog %d: text line %d, speech index %d, node offset %d, node size %d", j, i, dialog.textLineIndex, dialog.speechIndex, dialog.nodeOffset, dialog.nodeSize); + } + } + + // **** Section 3: ???? *************************************************** + /*debug("Section 3"); + convFile = convFileUnpacked.getItemStream(3); + byte *tmp1 = new byte[convFile->size()]; + convFile->read(tmp1, convFile->size()); + Common::hexdump(tmp1, convFile->size()); + delete[] tmp1;*/ + // TODO + + // **** Section 4: Text line offsets ************************************** + convFile = convFileUnpacked.getItemStream(4); + assert(convFile->size() == conv.textLineCount * 2); + + uint16 *textLineOffsets = new uint16[conv.textLineCount]; // deleted below in section 5 + for (uint16 i = 0; i < conv.textLineCount; i++) + textLineOffsets[i] = convFile->readUint16LE(); + + // **** Section 5: Text lines ********************************************* + convFile = convFileUnpacked.getItemStream(5); + assert(convFile->size() == textLength); + + Common::String textLine; + conv.textLines.resize(conv.textLineCount); + char textLineBuffer[256]; + uint16 nextOffset; + for (uint16 i = 0; i < conv.textLineCount; i++) { + nextOffset = (i != conv.textLineCount - 1) ? textLineOffsets[i + 1] : convFile->size(); + convFile->read(textLineBuffer, nextOffset - textLineOffsets[i]); + conv.textLines[i] = Common::String(textLineBuffer); + //debug("Text line %d: %s", i, conv.textLines[i].c_str()); + } + + delete[] textLineOffsets; + + // **** Section 6: Node entry commands ************************************ + convFile = convFileUnpacked.getItemStream(6); + assert(convFile->size() == commandLength); + + for (uint16 i = 0; i < conv.nodeCount; i++) { + uint16 dialogCount = conv.convNodes[i].dialogCount; + + for (uint16 j = 0; j < dialogCount; j++) { + //ConvDialog dialog = conv.convNodes[i].dialogs[j]; + byte command; + uint16 chk; + + do { + command = convFile->readByte(); + chk = convFile->readUint16BE(); + if (chk != 0xFF00 && chk != 0x0000) { + warning("Error while reading conversation node entries - bailing out"); + break; + } + + switch (command) { + case cmdNodeEnd: + //debug("Node end"); + break; + case cmdDialogEnd: + //debug("Dialog end"); + break; + case cmdHide: { + byte count = convFile->readByte(); + for (byte k = 0; k < count; k++) { + /*uint16 nodeRef = */convFile->readUint16LE(); + //debug("Hide node %d", nodeRef); + } + + } + break; + case cmdUnhide: { + byte count = convFile->readByte(); + for (byte k = 0; k < count; k++) { + /*uint16 nodeRef = */convFile->readUint16LE(); + //debug("Unhide node %d", nodeRef); + } + + } + break; + case cmdMessage: + //debug("Message"); + convFile->skip(7); // TODO + break; + case cmdGoto: { + convFile->skip(3); // unused? + /*byte nodeRef = */convFile->readByte(); + //debug("Goto %d", nodeRef); + } + break; + case cmdAssign: { + convFile->skip(3); // unused? + /*uint16 value = */convFile->readUint16LE(); + /*uint16 variable = */convFile->readUint16LE(); + //debug("Variable %d = %d", variable, value); + } + break; + default: + error("Unknown conversation command %d", command); + break; + } + } while (command != cmdNodeEnd && command != cmdDialogEnd); + } + } inFile.close(); + /* + // DEBUG: Show the very first message, and play the very first speech + _vm->_audio->setSoundGroup(conv.speechFile); + uint16 firstText = 0, firstSpeech = 1; + + for (uint16 i = 0; i < conv.convNodes.size(); i++) { + for (uint16 k = 0; k < conv.convNodes[i].dialogs.size(); k++) { + if (conv.convNodes[i].dialogs[k].textLineIndex >= 0) { + firstText = conv.convNodes[i].dialogs[k].textLineIndex; + firstSpeech = conv.convNodes[i].dialogs[k].speechIndex; + break; + } + } + } + + _vm->_audio->playSound(firstSpeech - 1); + + TextDialog *dialog = new TextDialog(_vm, FONT_INTERFACE, Common::Point(0, 100), 30); + dialog->addLine(conv.textLines[firstText]); + dialog->show(); + delete dialog; + */ + warning("TODO GameConversation::get"); } |