diff --git a/engines/prince/font.h b/engines/prince/font.h index 54e6b6b0a56c..629b5d61ebfb 100644 --- a/engines/prince/font.h +++ b/engines/prince/font.h @@ -49,6 +49,8 @@ class Font : public Graphics::Font { virtual void drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) const override; + virtual int getKerningOffset(byte left, byte right) const { return -2; } + private: struct ChrData { byte * _pixels; diff --git a/engines/prince/prince.cpp b/engines/prince/prince.cpp index 9ca7a62fad05..98bf4b71784f 100644 --- a/engines/prince/prince.cpp +++ b/engines/prince/prince.cpp @@ -86,10 +86,17 @@ PrinceEngine::PrinceEngine(OSystem *syst, const PrinceGameDescription *gameDesc) Engine(syst), _gameDescription(gameDesc), _graph(NULL), _script(NULL), _locationNr(0), _debugger(NULL), _objectList(NULL), _mobList(NULL), _midiPlayer(NULL), _cameraX(0), _newCameraX(0) { + + // Debug/console setup + DebugMan.addDebugChannel(DebugChannel::kScript, "script", "Prince Script debug channel"); + DebugMan.addDebugChannel(DebugChannel::kEngine, "engine", "Prince Engine debug channel"); + + DebugMan.enableDebugChannel("script"); + _rnd = new Common::RandomSource("prince"); _debugger = new Debugger(this); _midiPlayer = new MusicPlayer(this); - _textSlots[0] = ""; + } PrinceEngine::~PrinceEngine() { @@ -114,6 +121,7 @@ Common::Error PrinceEngine::run() { debug("Adding all path: %s", gameDataDir.getPath().c_str()); SearchMan.addSubDirectoryMatching(gameDataDir, "all", 0, 2); + SearchMan.addSubDirectoryMatching(gameDataDir, "data/voices/output", 0, 2); Common::SeekableReadStream *font1stream = SearchMan.createReadStreamForMember("font1.raw"); if (!font1stream) @@ -415,19 +423,49 @@ void PrinceEngine::hotspot() { } } -void PrinceEngine::printAt(const char *s, uint16 x, uint16 y) { - _textSlots[0] = s; +void PrinceEngine::printAt(uint32 slot, uint8 color, const char *s, uint16 x, uint16 y) { + + debugC(1, DebugChannel::kEngine, "PrinceEngine::printAt slot %d, color %d, x %02d, y %02d, str %s", slot, color, x, y, s); + + Text &text = _textSlots[slot]; + text._str = s; + text._x = x; + text._y = y; + text._color = color; } uint32 PrinceEngine::getTextWidth(const char *s) { uint16 textW = 0; - while (*s) { - textW += *s; - ++s; + while (*s) { + textW += _font.getCharWidth(*s) + _font.getKerningOffset(0, 0); + ++s; } return textW; } +void PrinceEngine::showTexts() { + for (uint32 slot = 0; slot < MAXTEXTS; ++slot) { + Text& text = _textSlots[slot]; + if (!text._str && !text._time) + continue; + + Common::Array lines; + _font.wordWrapText(text._str, _graph->_frontScreen->w, lines); + + for (int i = 0; i < lines.size(); ++i) { + _font.drawString( + _graph->_frontScreen, + lines[i], + text._x - getTextWidth(lines[i].c_str())/2, + text._y - (lines.size() - i) * (_font.getFontHeight()), + _graph->_frontScreen->w, + text._color + ); + } + + --text._time; + } +} void PrinceEngine::drawScreen() { const Graphics::Surface *roomSurface = _roomBmp.getSurface(); @@ -443,15 +481,7 @@ void PrinceEngine::drawScreen() { // _graph->drawTransparent(_objectList->getSurface()); hotspot(); - _font.drawString( - _graph->_frontScreen, - _textSlots[0], - 320 - getTextWidth(_textSlots[0])/2, - 470 - _font.getFontHeight(), - _graph->_frontScreen->w, - 216 - ); - + showTexts(); getDebugger()->onFrame(); diff --git a/engines/prince/prince.h b/engines/prince/prince.h index 080eca5ead90..43d94654c788 100644 --- a/engines/prince/prince.h +++ b/engines/prince/prince.h @@ -58,6 +58,25 @@ class MobList; class MusicPlayer; class VariaTxt; +struct Text { + const char *_str; + uint16 _x, _y; + uint16 _time; + uint32 _color; + + Text() : _str(NULL), _x(0), _y(0), _time(0), _color(255){ + } +}; + +struct DebugChannel { + +enum Type { + kScript, + kEngine +}; + +}; + class PrinceEngine : public Engine { protected: Common::Error run(); @@ -86,9 +105,10 @@ class PrinceEngine : public Engine { virtual GUI::Debugger *getDebugger(); void changeCursor(uint16 curId); - void printAt(const char *s, uint16 x, uint16 y); + void printAt(uint32 slot, uint8 color, const char *s, uint16 x, uint16 y); - const char * _textSlots[1000]; + static const uint8 MAXTEXTS = 32; + Text _textSlots[MAXTEXTS]; private: bool playNextFrame(); @@ -97,6 +117,7 @@ class PrinceEngine : public Engine { void scrollCameraRight(int16 delta); void scrollCameraLeft(int16 delta); void drawScreen(); + void showTexts(); uint32 getTextWidth(const char *s); diff --git a/engines/prince/script.cpp b/engines/prince/script.cpp index 7ab723a4fbdc..5e2b095b653e 100644 --- a/engines/prince/script.cpp +++ b/engines/prince/script.cpp @@ -29,6 +29,7 @@ #include "common/debug.h" #include "common/debug-channels.h" #include "common/stream.h" +#include "common/archive.h" namespace Prince { @@ -66,7 +67,7 @@ void Script::debugScript(const char *s, ...) { Common::String str = Common::String::format("@0x%04X: ", _lastInstruction); str += Common::String::format("op %02d: ", _lastOpcode); - debug("%s %s", str.c_str(), buf); + debugC(10, DebugChannel::kScript, "PrinceEngine::Script %s %s", str.c_str(), buf); } void Script::step() { @@ -85,7 +86,7 @@ void Script::step() { error("Trying to execute unknown opcode %s", dstr.c_str()); - debug("%s", dstr.c_str()); + debugScript("%s", dstr.c_str()); // Execute the current opcode OpcodeFunc op = _opcodes[_lastOpcode]; @@ -330,7 +331,7 @@ void Script::O_COMPARE() { value = val; } - debugScript("O_COMPARE flagId 0x%04X (%s), value %d", flagId, Flags::getFlagName(flagId), value); + debugScript("O_COMPARE flagId 0x%04X (%s), value %d ?= %d", flagId, Flags::getFlagName(flagId), value, _flags[flagId - 0x8000]); _result = (_flags[flagId - 0x8000] == value); } @@ -398,6 +399,7 @@ void Script::O_SUBFLAG() { void Script::O_SETSTRING() { int32 offset = readScript32bits(); + _currentString = offset; if (offset >= 80000) { debug("GetVaria %s", _vm->_variaTxt->getString(offset - 80000)); @@ -409,7 +411,7 @@ void Script::O_SETSTRING() { debug("TalkTxt %d %s", of, txt); } - debugScript("O_SETSTRING 0x%04X", offset); + debugScript("O_SETSTRING %04d", offset); } void Script::O_ANDFLAG() { @@ -545,8 +547,15 @@ void Script::O_TALKHERO() {} void Script::O_WAITTEXT() { uint16 slot = readScript16bits(); - debugScript("O_WAITTEXT slot %d", slot); - _opcodeNF = 1; + if (slot & 0x8000) { + slot = _flags[slot - 0x8000]; + } + //debugScript("O_WAITTEXT slot %d", slot); + Text &text = _vm->_textSlots[slot]; + if (text._time) { + _opcodeNF = 1; + _currentInstruction -= 4; + } } void Script::O_SETHEROANIM() {} @@ -568,10 +577,11 @@ void Script::O_LOADPATH() {} void Script::O_GETCHAR() { uint16 flagId = readScript16bits(); - debugScript("O_GETCHAR %04X (%s)", flagId, Flags::getFlagName(flagId)); _flags[flagId - 0x8000] = *_string; + debugScript("O_GETCHAR %04X (%s) %02x", flagId, Flags::getFlagName(flagId), _flags[flagId - 0x8000]); + _string++; } @@ -585,12 +595,15 @@ void Script::O_PRINTAT() { debugScript("O_PRINTAT slot %d, fr1 %d, fr2 %d", slot, fr1, fr2); - _vm->printAt((const char *)_string, 0, fr1); + uint8 color = _flags[Flags::KOLOR - 0x8000]; + + _vm->printAt(slot, color, (const char *)_string, fr1, fr2); while (*_string) { ++_string; } ++_string; + debug("O_PRINTAT %x", *_string); } void Script::O_ZOOMIN() {} @@ -732,7 +745,7 @@ void Script::O_CHECKFLCFRAME() { void Script::O_CHECKFLCEND() { - debugScript("O_CHECKFLCEND"); + //debugScript("O_CHECKFLCEND"); const Video::FlicDecoder &flicPlayer = _vm->_flicPlayer; @@ -795,24 +808,60 @@ void Script::O_SKIPTEXT() { debugScript("O_SKIPTEXT"); } +void Script::SetVoice(uint32 slot) { + const Common::String streamName = Common::String::format("%03d-01.WAV", _currentString); + debugScript("Loading wav %s slot %d", streamName.c_str(), slot); + + Common::SeekableReadStream *voiceStream = SearchMan.createReadStreamForMember(streamName); + if (!voiceStream) { + error("Can't open %s", streamName.c_str()); + } + uint32 id = voiceStream->readUint32LE(); + if (id != 0x46464952) { + error("It's not RIFF file %s", streamName.c_str()); + return; + } + + voiceStream->skip(0x20); + id = voiceStream->readUint32LE(); + if (id != 0x61746164) { + error("No data section in %s id %04x", streamName.c_str(), id); + return; + } + + id = voiceStream->readUint32LE(); + id <<= 3; + id /= 22050; + id += 2; + + _vm->_textSlots[slot]._time = voiceStream->readUint32LE(); + + debugScript("SetVoice slot %d time %04x", slot, _vm->_textSlots[slot]._time); + delete voiceStream; +} + void Script::O_SETVOICEH() { uint16 txn = readScript16bits(); debugScript("O_SETVOICEH txn %d", txn); + SetVoice(txn); } void Script::O_SETVOICEA() { uint16 txn = readScript16bits(); debugScript("O_SETVOICEA txn %d", txn); + SetVoice(txn); } void Script::O_SETVOICEB() { uint16 txn = readScript16bits(); debugScript("O_SETVOICEB txn %d", txn); + SetVoice(txn); } void Script::O_SETVOICEC() { uint16 txn = readScript16bits(); debugScript("O_SETVOICEC txn %d", txn); + SetVoice(txn); } void Script::O_VIEWFLCLOOP() { @@ -858,6 +907,7 @@ void Script::O_INPUTLINE() { void Script::O_SETVOICED() { uint16 txn = readScript16bits(); debugScript("O_SETVOICED txn %d", txn); + SetVoice(txn); } void Script::O_BREAK_POINT() { diff --git a/engines/prince/script.h b/engines/prince/script.h index dc050daeea28..68b44cb1e356 100644 --- a/engines/prince/script.h +++ b/engines/prince/script.h @@ -61,6 +61,7 @@ class Script { uint8 _savedStacktop; const byte * _string; + uint32 _currentString; // Helper functions void checkPC(uint32 address); @@ -70,6 +71,7 @@ class Script { uint32 readScript32bits(); uint16 readScript8or16bits(); void debugScript(const char *s, ...); + void SetVoice(uint32 slot); typedef void (Script::*OpcodeFunc)(); static OpcodeFunc _opcodes[];