diff options
Diffstat (limited to 'engines/mads/conversations.cpp')
-rw-r--r-- | engines/mads/conversations.cpp | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/engines/mads/conversations.cpp b/engines/mads/conversations.cpp new file mode 100644 index 0000000000..86f38b29dc --- /dev/null +++ b/engines/mads/conversations.cpp @@ -0,0 +1,348 @@ +/* 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. + * + */ + +#include "mads/conversations.h" +#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 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 importCount; + uint16 speakerCount; + Common::String portraits[MAX_SPEAKERS]; + bool speakerExists[MAX_SPEAKERS]; + Common::String speechFile; + Common::Array<Common::String> textLines; + Common::Array<ConvNode> convNodes; +}; + +GameConversation::GameConversation(MADSEngine *vm) + : _vm(vm) { + _running = _restoreRunning = 0; +} + +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); + + char buffer[16]; + + ConvData conv; + + // **** Section 0: Header ************************************************* + conv.nodeCount = convFile->readUint16LE(); + conv.dialogCount = convFile->readUint16LE(); + conv.messageCount = convFile->readUint16LE(); + conv.textLineCount = convFile->readUint16LE(); + conv.unk2 = convFile->readUint16LE(); + conv.importCount = convFile->readUint16LE(); + conv.speakerCount = convFile->readUint16LE(); + + //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); + conv.portraits[i] = buffer; + //debug("Speaker %d, portrait %s", i, conv.portraits[i].c_str()); + } + + for (uint16 i = 0; i < MAX_SPEAKERS; i++) { + 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()); + + 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); + + /*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.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); + } + + // **** 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"); +} + +void GameConversation::run(int id) { + warning("TODO GameConversation::run"); +} + +void GameConversation::stop() { + warning("TODO GameConversation::stop"); +} + +void GameConversation::exportPointer(int *val) { + warning("TODO GameConversation::exportPointer"); +} + +void GameConversation::exportValue(int val) { + warning("TODO GameConversation::exportValue"); +} + +void GameConversation::setHeroTrigger(int val) { + _vm->_game->_trigger = val; // HACK + _running = -1; // HACK + warning("TODO: GameConversation::setHeroTrigger"); +} + +void GameConversation::setInterlocutorTrigger(int val) { + warning("TODO: GameConversation::setInterlocutorTrigger"); +} + +int* GameConversation::getVariable(int idx) { + warning("TODO: GameConversation::getVariable"); + return nullptr; +} + +void GameConversation::hold() { + warning("TODO: GameConversation::hold"); +} + +void GameConversation::release() { + warning("TODO: GameConversation::release"); +} + +void GameConversation::reset(int id) { + warning("TODO: GameConversation::reset"); +} + +void GameConversation::abortConv() { + warning("TODO: GameConversation::abort"); +} +} // End of namespace MADS |