diff options
Diffstat (limited to 'engines/director/director.cpp')
-rw-r--r-- | engines/director/director.cpp | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/engines/director/director.cpp b/engines/director/director.cpp new file mode 100644 index 0000000000..cf66c851cd --- /dev/null +++ b/engines/director/director.cpp @@ -0,0 +1,367 @@ +/* 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 "common/config-manager.h" +#include "common/debug-channels.h" +#include "common/error.h" +#include "common/macresman.h" + +#include "graphics/macgui/macwindowmanager.h" + +#include "director/director.h" +#include "director/images.h" +#include "director/resource.h" +#include "director/score.h" +#include "director/sound.h" +#include "director/lingo/lingo.h" + +namespace Director { + +DirectorEngine::DirectorEngine(OSystem *syst, const DirectorGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc), + _rnd("director") { + DebugMan.addDebugChannel(kDebugLingoExec, "lingoexec", "Lingo Execution"); + DebugMan.addDebugChannel(kDebugLingoCompile, "lingocompile", "Lingo Compilation"); + DebugMan.addDebugChannel(kDebugLoading, "loading", "Loading"); + DebugMan.addDebugChannel(kDebugImages, "images", "Image drawing"); + + if (!_mixer->isReady()) + error("Sound initialization failed"); + + // Setup mixer + syncSoundSettings(); + + _sharedCasts = nullptr; + _sharedSound = nullptr; + _sharedBMP = nullptr; + _sharedSTXT = nullptr; + _sharedDIB = nullptr; + + _currentScore = nullptr; + _soundManager = nullptr; + _currentPalette = nullptr; + _currentPaletteLength = 0; + _lingo = nullptr; + + _mainArchive = nullptr; + _macBinary = nullptr; + + _movies = nullptr; + + _wm = nullptr; + + const Common::FSNode gameDataDir(ConfMan.get("path")); + SearchMan.addSubDirectoryMatching(gameDataDir, "data"); + SearchMan.addSubDirectoryMatching(gameDataDir, "install"); +} + +DirectorEngine::~DirectorEngine() { + delete _sharedCasts; + delete _sharedSound; + delete _sharedBMP; + delete _sharedSTXT; + delete _sharedDIB; + + delete _currentScore; + delete _mainArchive; + delete _macBinary; + delete _soundManager; + delete _lingo; +} + +Common::Error DirectorEngine::run() { + debug("Starting v%d Director game", getVersion()); + + //FIXME + _sharedMMM = "SHARDCST.MMM"; + + _currentPalette = nullptr; + + _macBinary = nullptr; + _soundManager = nullptr; + + _wm = new Graphics::MacWindowManager; + + _lingo = new Lingo(this); + _soundManager = new DirectorSound(); + + if (getGameID() == GID_TEST) { + _mainArchive = nullptr; + _currentScore = nullptr; + + _lingo->runTests(); + + return Common::kNoError; + } + + //FIXME + //_mainArchive = new RIFFArchive(); + //_mainArchive->openFile("bookshelf_example.mmm"); + + if (getPlatform() == Common::kPlatformWindows) + loadEXE(); + else + loadMac(); + + _currentScore = new Score(this); + debug(0, "Score name %s", _currentScore->getMacName().c_str()); + + _currentScore->loadArchive(); + _currentScore->startLoop(); + + return Common::kNoError; +} + +Common::HashMap<Common::String, Score *> DirectorEngine::loadMMMNames(Common::String folder) { + Common::FSNode directory(folder); + Common::FSList movies; + + Common::HashMap<Common::String, Score *> nameMap; + if (!directory.getChildren(movies, Common::FSNode::kListFilesOnly)) + return nameMap; + + if (!movies.empty()) { + for (Common::FSList::const_iterator i = movies.begin(); i != movies.end(); ++i) { + if (i->getName() == _sharedMMM) { + loadSharedCastsFrom(i->getPath()); + continue; + } + + RIFFArchive *arc = new RIFFArchive(); + arc->openFile(i->getPath()); + Score *sc = new Score(this); + nameMap[sc->getMacName()] = sc; + } + } + + return nameMap; +} + +void DirectorEngine::loadEXE() { + Common::SeekableReadStream *exeStream = SearchMan.createReadStreamForMember(getEXEName()); + if (!exeStream) + error("Failed to open EXE '%s'", getEXEName().c_str()); + + _lingo->processEvent(kEventStart, 0); + + exeStream->seek(-4, SEEK_END); + exeStream->seek(exeStream->readUint32LE()); + + switch (getVersion()) { + case 3: + loadEXEv3(exeStream); + break; + case 4: + loadEXEv4(exeStream); + break; + case 5: + loadEXEv5(exeStream); + break; + case 7: + loadEXEv7(exeStream); + break; + default: + error("Unhandled Windows EXE version %d", getVersion()); + } +} + +void DirectorEngine::loadEXEv3(Common::SeekableReadStream *stream) { + uint16 entryCount = stream->readUint16LE(); + if (entryCount != 1) + error("Unhandled multiple entry v3 EXE"); + + stream->skip(5); // unknown + + stream->readUint32LE(); // Main MMM size + Common::String mmmFileName = readPascalString(*stream); + Common::String directoryName = readPascalString(*stream); + + debug("Main MMM: '%s'", mmmFileName.c_str()); + debug("Directory Name: '%s'", directoryName.c_str()); + + _mainArchive = new RIFFArchive(); + + if (!_mainArchive->openFile(mmmFileName)) + error("Could not open '%s'", mmmFileName.c_str()); + + delete stream; +} + +void DirectorEngine::loadEXEv4(Common::SeekableReadStream *stream) { + if (stream->readUint32BE() != MKTAG('P', 'J', '9', '3')) + error("Invalid projector tag found in v4 EXE"); + + uint32 rifxOffset = stream->readUint32LE(); + /* uint32 fontMapOffset = */ stream->readUint32LE(); + /* uint32 resourceForkOffset1 = */ stream->readUint32LE(); + /* uint32 resourceForkOffset2 = */ stream->readUint32LE(); + stream->readUint32LE(); // graphics DLL offset + stream->readUint32LE(); // sound DLL offset + /* uint32 rifxOffsetAlt = */ stream->readUint32LE(); // equivalent to rifxOffset + + loadEXERIFX(stream, rifxOffset); +} + +void DirectorEngine::loadEXEv5(Common::SeekableReadStream *stream) { + if (stream->readUint32LE() != MKTAG('P', 'J', '9', '5')) + error("Invalid projector tag found in v5 EXE"); + + uint32 rifxOffset = stream->readUint32LE(); + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + /* uint16 screenWidth = */ stream->readUint16LE(); + /* uint16 screenHeight = */ stream->readUint16LE(); + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + /* uint32 fontMapOffset = */ stream->readUint32LE(); + + loadEXERIFX(stream, rifxOffset); +} + +void DirectorEngine::loadEXEv7(Common::SeekableReadStream *stream) { + if (stream->readUint32LE() != MKTAG('P', 'J', '0', '0')) + error("Invalid projector tag found in v7 EXE"); + + uint32 rifxOffset = stream->readUint32LE(); + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + stream->readUint32LE(); // unknown + stream->readUint32LE(); // some DLL offset + + loadEXERIFX(stream, rifxOffset); +} + +void DirectorEngine::loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset) { + _mainArchive = new RIFXArchive(); + + if (!_mainArchive->openStream(stream, offset)) + error("Failed to load RIFX from EXE"); +} + +void DirectorEngine::loadMac() { + if (getVersion() < 4) { + // The data is part of the resource fork of the executable + _mainArchive = new MacArchive(); + + if (!_mainArchive->openFile(getEXEName())) + error("Failed to open Mac binary '%s'", getEXEName().c_str()); + } else { + // The RIFX is located in the data fork of the executable + _macBinary = new Common::MacResManager(); + + if (!_macBinary->open(getEXEName()) || !_macBinary->hasDataFork()) + error("Failed to open Mac binary '%s'", getEXEName().c_str()); + + Common::SeekableReadStream *dataFork = _macBinary->getDataFork(); + _mainArchive = new RIFXArchive(); + + // First we need to detect PPC vs. 68k + + uint32 tag = dataFork->readUint32BE(); + uint32 startOffset; + + if (SWAP_BYTES_32(tag) == MKTAG('P', 'J', '9', '3') || tag == MKTAG('P', 'J', '9', '5') || tag == MKTAG('P', 'J', '0', '0')) { + // PPC: The RIFX shares the data fork with the binary + startOffset = dataFork->readUint32BE(); + } else { + // 68k: The RIFX is the only thing in the data fork + startOffset = 0; + } + + if (!_mainArchive->openStream(dataFork, startOffset)) + error("Failed to load RIFX from Mac binary"); + } +} + +Common::String DirectorEngine::readPascalString(Common::SeekableReadStream &stream) { + byte length = stream.readByte(); + Common::String x; + + while (length--) + x += (char)stream.readByte(); + + return x; +} + +void DirectorEngine::setPalette(byte *palette, uint16 count) { + _currentPalette = palette; + _currentPaletteLength = count; +} + +void DirectorEngine::loadSharedCastsFrom(Common::String filename) { + Archive *shardcst; + + if (getVersion() < 4) { + shardcst = new RIFFArchive(); + } else { + shardcst = new RIFXArchive(); + } + + shardcst->openFile(filename); + + Score *castScore = new Score(this); + Common::SeekableSubReadStreamEndian *castStream = shardcst->getResource(MKTAG('V','W','C','R'), 1024); + + castScore->loadCastData(*castStream); + *_sharedCasts = castScore->_casts; + + Common::Array<uint16> dib = shardcst->getResourceIDList(MKTAG('D','I','B',' ')); + + if (dib.size() != 0) { + Common::Array<uint16>::iterator iterator; + for (iterator = dib.begin(); iterator != dib.end(); ++iterator) { + debug(3, "Shared DIB %d", *iterator); + _sharedDIB->setVal(*iterator, shardcst->getResource(MKTAG('D','I','B',' '), *iterator)); + } + } + + Common::Array<uint16> stxt = shardcst->getResourceIDList(MKTAG('S','T','X','T')); + + if (stxt.size() != 0) { + Common::Array<uint16>::iterator iterator; + for (iterator = stxt.begin(); iterator != stxt.end(); ++iterator) { + debug(3, "Shared STXT %d", *iterator); + _sharedSTXT->setVal(*iterator, shardcst->getResource(MKTAG('S','T','X','T'), *iterator)); + } + } + + Common::Array<uint16> bmp = shardcst->getResourceIDList(MKTAG('B','I','T','D')); + + if (bmp.size() != 0) { + Common::Array<uint16>::iterator iterator; + for (iterator = bmp.begin(); iterator != bmp.end(); ++iterator) { + _sharedBMP->setVal(*iterator, shardcst->getResource(MKTAG('B','I','T','D'), *iterator)); + } + } + + Common::Array<uint16> sound = shardcst->getResourceIDList(MKTAG('S','N','D',' ')); + + if (stxt.size() != 0) { + Common::Array<uint16>::iterator iterator; + for (iterator = sound.begin(); iterator != sound.end(); ++iterator) { + _sharedSound->setVal(*iterator, shardcst->getResource(MKTAG('S','N','D',' '), *iterator)); + } + } +} + +} // End of namespace Director |