diff options
author | Johannes Schickel | 2014-07-05 13:35:34 +0200 |
---|---|---|
committer | Johannes Schickel | 2014-07-05 13:35:34 +0200 |
commit | b77412a9a07e58a21dd430e28cf34210d4648fd0 (patch) | |
tree | a0ca26a89ddcfb81e8da4e8361352e1e62e926e6 /engines/scumm | |
parent | 48564efc8812aef644144cb368d65a8d14a4397f (diff) | |
parent | 53d3ee07df6eea863e93620ab4a406eb24fbfef1 (diff) | |
download | scummvm-rg350-b77412a9a07e58a21dd430e28cf34210d4648fd0.tar.gz scummvm-rg350-b77412a9a07e58a21dd430e28cf34210d4648fd0.tar.bz2 scummvm-rg350-b77412a9a07e58a21dd430e28cf34210d4648fd0.zip |
Merge pull request #470 from bluegr/Lucasarts_Steam
SCUMM: Add support for Steam versions of Indy 3, Indy 4, Loom and Dig
Diffstat (limited to 'engines/scumm')
-rw-r--r-- | engines/scumm/cdda.cpp | 120 | ||||
-rw-r--r-- | engines/scumm/cdda.h | 58 | ||||
-rw-r--r-- | engines/scumm/detection.cpp | 29 | ||||
-rw-r--r-- | engines/scumm/detection.h | 2 | ||||
-rw-r--r-- | engines/scumm/detection_tables.h | 20 | ||||
-rw-r--r-- | engines/scumm/file.cpp | 39 | ||||
-rw-r--r-- | engines/scumm/file.h | 28 | ||||
-rw-r--r-- | engines/scumm/module.mk | 1 | ||||
-rw-r--r-- | engines/scumm/saveload.cpp | 2 | ||||
-rw-r--r-- | engines/scumm/scumm-md5.h | 10 | ||||
-rw-r--r-- | engines/scumm/scumm.cpp | 47 | ||||
-rw-r--r-- | engines/scumm/sound.cpp | 66 | ||||
-rw-r--r-- | engines/scumm/sound.h | 7 |
13 files changed, 406 insertions, 23 deletions
diff --git a/engines/scumm/cdda.cpp b/engines/scumm/cdda.cpp new file mode 100644 index 0000000000..adb414ecce --- /dev/null +++ b/engines/scumm/cdda.cpp @@ -0,0 +1,120 @@ +/* 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 "scumm/cdda.h" +#include "common/stream.h" +#include "audio/audiostream.h" + +namespace Scumm { + + +#pragma mark - +#pragma mark --- CDDA stream --- +#pragma mark - + +#define START_OF_CDDA_DATA 800 +#define BLOCK_SIZE 1177 + +class CDDAStream : public Audio::SeekableAudioStream { +private: + Common::SeekableReadStream *_stream; + DisposeAfterUse::Flag _disposeAfterUse; + byte _shiftLeft; + byte _shiftRight; + uint32 _pos; + Audio::Timestamp _length; + +public: + CDDAStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse); + virtual ~CDDAStream(); + + int readBuffer(int16 *buffer, const int numSamples); + bool isStereo() const { return true; } + int getRate() const { return 44100; } + bool endOfData() const { return _stream->eos(); } + bool seek(const Audio::Timestamp &where); + Audio::Timestamp getLength() const { return _length; } +}; + +CDDAStream::CDDAStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) : + _stream(stream), _disposeAfterUse(disposeAfterUse), _pos(START_OF_CDDA_DATA) { + _stream->seek(START_OF_CDDA_DATA, SEEK_SET); + // The total size of CDDA.SOU is 289,808,802 bytes or (289808802 - 800) / 1177 = 246226 blocks + // We also deduct the shift values to return the correct length + uint32 blocks = (_stream->size() - START_OF_CDDA_DATA) / BLOCK_SIZE; + _length = Audio::Timestamp(0, (_stream->size() - START_OF_CDDA_DATA - blocks) / (isStereo() ? 2 : 1), getRate()); +} + +CDDAStream::~CDDAStream() { + if (_disposeAfterUse == DisposeAfterUse::YES) + delete _stream; +} + +bool CDDAStream::seek(const Audio::Timestamp &where) { + const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames(); + uint32 blocks = seekSample / 1176; + + // Before seeking, read the shift values from the beginning of that block + _stream->seek(START_OF_CDDA_DATA + blocks * BLOCK_SIZE, SEEK_SET); + byte shiftVal = _stream->readByte(); + _shiftLeft = shiftVal >> 4; + _shiftRight = shiftVal & 0x0F; + + _pos = START_OF_CDDA_DATA + blocks + seekSample; + return _stream->seek(_pos, SEEK_SET); +} + +int CDDAStream::readBuffer(int16 *buffer, const int numSamples) { + int samples; + + for (samples = 0 ; samples < numSamples && !_stream->eos() ; ) { + if (!((_pos - START_OF_CDDA_DATA) % BLOCK_SIZE)) { + byte shiftVal = _stream->readByte(); + _shiftLeft = shiftVal >> 4; + _shiftRight = shiftVal & 0x0F; + _pos++; + } + buffer[samples++] = _stream->readSByte() << _shiftLeft; + buffer[samples++] = _stream->readSByte() << _shiftRight; + _pos += 2; + } + return samples; +} + +#pragma mark - +#pragma mark --- CDDA factory functions --- +#pragma mark - + +Audio::SeekableAudioStream *makeCDDAStream( + Common::SeekableReadStream *stream, + DisposeAfterUse::Flag disposeAfterUse) { + Audio::SeekableAudioStream *s = new CDDAStream(stream, disposeAfterUse); + if (s && s->endOfData()) { + delete s; + return 0; + } else { + return s; + } + return 0; +} + +} // End of namespace Scumm diff --git a/engines/scumm/cdda.h b/engines/scumm/cdda.h new file mode 100644 index 0000000000..2146777497 --- /dev/null +++ b/engines/scumm/cdda.h @@ -0,0 +1,58 @@ + +/* 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. + * + */ + +/** + * @file + * CD audio decoder used in the Steam versions of Loom + */ + +#ifndef SCUMM_CDDA_H +#define SCUMM_CDDA_H + +#include "common/types.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Audio { +class SeekableAudioStream; +} + +namespace Scumm { + +/** + * Create a new SeekableAudioStream from the CDDA data in the given stream. + * Allows for seeking (which is why we require a SeekableReadStream). + * + * @param stream the SeekableReadStream from which to read the CDDA data + * @param disposeAfterUse whether to delete the stream after use + * @return a new SeekableAudioStream, or NULL, if an error occurred + */ +Audio::SeekableAudioStream *makeCDDAStream( + Common::SeekableReadStream *stream, + DisposeAfterUse::Flag disposeAfterUse); + +} // End of namespace Audio + +#endif // #ifndef SCUMM_CDDA_H diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index b7a25808a5..55df5926fc 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -79,10 +79,12 @@ Common::String ScummEngine::generateFilename(const int room) const { } else { switch (_filenamePattern.genMethod) { case kGenDiskNum: + case kGenDiskNumSteam: result = Common::String::format(_filenamePattern.pattern, diskNumber); break; case kGenRoomNum: + case kGenRoomNumSteam: result = Common::String::format(_filenamePattern.pattern, room); break; @@ -209,14 +211,30 @@ Common::String ScummEngine_v70he::generateFilename(const int room) const { return result; } -static Common::String generateFilenameForDetection(const char *pattern, FilenameGenMethod genMethod) { +static const char *getSteamExeNameFromPattern(Common::String pattern, Common::Platform platform) { + for (const SteamIndexFile *indexFile = steamIndexFiles; indexFile->len; ++indexFile) { + if (platform == indexFile->platform && pattern.equalsIgnoreCase(indexFile->pattern)) + return indexFile->executableName; + } + + error("Unable to find Steam executable from detection pattern"); + return ""; +} + +static Common::String generateFilenameForDetection(const char *pattern, FilenameGenMethod genMethod, Common::Platform platform) { Common::String result; + Common::String patternStr = pattern; switch (genMethod) { case kGenDiskNum: case kGenRoomNum: result = Common::String::format(pattern, 0); break; + + case kGenDiskNumSteam: + case kGenRoomNumSteam: + result = getSteamExeNameFromPattern(pattern, platform); + break; case kGenHEPC: case kGenHEIOS: @@ -528,7 +546,8 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul DetectorResult dr; // Dive one level down since mac indy3/loom has its files split into directories. See Bug #1438631 - composeFileHashMap(fileMD5Map, fslist, 2, directoryGlobs); + // Dive two levels down for Mac Steam games + composeFileHashMap(fileMD5Map, fslist, 3, directoryGlobs); // Iterate over all filename patterns. for (const GameFilenamePattern *gfp = gameFilenamesTable; gfp->gameid; ++gfp) { @@ -540,7 +559,7 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul // Generate the detectname corresponding to the gfp. If the file doesn't // exist in the directory we are looking at, we can skip to the next // one immediately. - Common::String file(generateFilenameForDetection(gfp->pattern, gfp->genMethod)); + Common::String file(generateFilenameForDetection(gfp->pattern, gfp->genMethod, gfp->platform)); if (!fileMD5Map.contains(file)) continue; @@ -1025,7 +1044,7 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) co Common::FSNode dir(ConfMan.get("path")); if (!dir.isDirectory()) return Common::kPathNotDirectory; - if (!dir.getChildren(fslist, Common::FSNode::kListFilesOnly)) + if (!dir.getChildren(fslist, Common::FSNode::kListAll)) return Common::kNoGameDataFoundError; // Invoke the detector, but fixed to the specified gameid. @@ -1081,7 +1100,7 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) co md5Warning += Common::String::format(" SCUMM gameid '%s', file '%s', MD5 '%s'\n\n", res.game.gameid, - generateFilenameForDetection(res.fp.pattern, res.fp.genMethod).c_str(), + generateFilenameForDetection(res.fp.pattern, res.fp.genMethod, Common::kPlatformUnknown).c_str(), res.md5.c_str()); g_system->logMessage(LogMessageType::kWarning, md5Warning.c_str()); diff --git a/engines/scumm/detection.h b/engines/scumm/detection.h index f714812a9c..0587c3fab1 100644 --- a/engines/scumm/detection.h +++ b/engines/scumm/detection.h @@ -95,7 +95,9 @@ struct GameSettings { enum FilenameGenMethod { kGenDiskNum, + kGenDiskNumSteam, kGenRoomNum, + kGenRoomNumSteam, kGenHEMac, kGenHEMacNoParens, kGenHEPC, diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index c876af1256..1dccbd32a3 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -52,6 +52,8 @@ namespace Scumm { */ static const char *const directoryGlobs[] = { "rooms *", // Mac version of indy3/loom + "Contents", // Mac Steam versions + "MacOS", // Mac Steam versions 0 }; @@ -221,6 +223,7 @@ static const GameSettings gameVariantsTable[] = { {"indy3", "EGA", "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, 0, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"indy3", "No AdLib", "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"indy3", "VGA", "vga", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, GF_OLD256 | GF_FEW_LOCALS, Common::kPlatformDOS, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, + {"indy3", "Steam", "steam", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, GF_OLD256 | GF_FEW_LOCALS, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"indy3", "FM-TOWNS", 0, GID_INDY3, 3, 0, MDT_TOWNS, GF_OLD256 | GF_FEW_LOCALS | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GUIO_MIDITOWNS, GUIO_NOASPECT)}, {"loom", "EGA", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO1(GUIO_NOSPEECH)}, @@ -230,6 +233,7 @@ static const GameSettings gameVariantsTable[] = { #endif {"loom", "FM-TOWNS", 0, GID_LOOM, 3, 0, MDT_TOWNS, GF_AUDIOTRACKS | GF_OLD256, Common::kPlatformFMTowns, GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GUIO_MIDITOWNS, GUIO_NOASPECT)}, {"loom", "VGA", "vga", GID_LOOM, 4, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformDOS, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, + {"loom", "Steam", "steam", GID_LOOM, 4, 0, MDT_NONE, GF_AUDIOTRACKS, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"pass", 0, 0, GID_PASS, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, GF_16COLOR, Common::kPlatformDOS, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, @@ -245,6 +249,7 @@ static const GameSettings gameVariantsTable[] = { {"monkey2", "FM-TOWNS", 0, GID_MONKEY2, 5, 0, MDT_PCSPK | MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO5(GUIO_NOSPEECH, GUIO_MIDITOWNS, GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_NOASPECT)}, {"atlantis", "", 0, GID_INDY4, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO0()}, + {"atlantis", "Steam", "steam", GID_INDY4, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO0()}, {"atlantis", "Floppy", 0, GID_INDY4, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO1(GUIO_NOSPEECH)}, {"atlantis", "FM-TOWNS", 0, GID_INDY4, 5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO4(GUIO_MIDITOWNS, GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_NOASPECT)}, @@ -257,7 +262,8 @@ static const GameSettings gameVariantsTable[] = { #ifdef ENABLE_SCUMM_7_8 {"ft", 0, 0, GID_FT, 7, 0, MDT_NONE, 0, UNK, GUIO1(GUIO_NOMIDI)}, - {"dig", 0, 0, GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO1(GUIO_NOMIDI)}, + {"dig", "", 0, GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO1(GUIO_NOMIDI)}, + {"dig", "Steam", "steam", GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO1(GUIO_NOMIDI)}, {"comi", 0, 0, GID_CMI, 8, 0, MDT_NONE, 0, Common::kPlatformWindows, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)}, #endif @@ -422,10 +428,10 @@ static const GameSettings gameVariantsTable[] = { using Common::UNK_LANG; // The following describes how Fingolfin thinks this table might be used one day; -// this is work in progress, so read this with a salt of grain... +// this is work in progress, so read this with a grain of salt... // // The following table maps gameids to possible filename variants for that game. -// This information is used by the detector to determin possible "detect files". +// This information is used by the detector to determine possible "detect files". // It is also later used by the engine creation code to verify the game to be // launched is present. Finally, the correct GameFilenamePattern entry is passed on // to the engine which uses it to locate the files for the game. @@ -451,6 +457,8 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "zak", "zak1.d64", kGenUnchanged, UNK_LANG, Common::kPlatformC64, "V1" }, // ... and zak2.d64 { "indy3", "%02d.LFL", kGenRoomNum, UNK_LANG, UNK, 0 }, + { "indy3", "%02d.LFL", kGenRoomNumSteam, UNK_LANG, Common::kPlatformWindows, "Steam" }, + { "indy3", "%02d.LFL", kGenRoomNumSteam, UNK_LANG, Common::kPlatformMacintosh, "Steam" }, { "indyloom", "%02d.LFL", kGenRoomNum, UNK_LANG, UNK, 0 }, { "indyzak", "%02d.LFL", kGenRoomNum, UNK_LANG, UNK, 0 }, @@ -458,6 +466,8 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "loom", "%02d.LFL", kGenRoomNum, UNK_LANG, UNK, 0 }, { "loom", "%03d.LFL", kGenRoomNum, UNK_LANG, UNK, "VGA" }, // Loom CD + { "loom", "%03d.LFL", kGenRoomNumSteam, UNK_LANG, Common::kPlatformWindows, "Steam" }, + { "loom", "%03d.LFL", kGenRoomNumSteam, UNK_LANG, Common::kPlatformMacintosh, "Steam" }, { "pass", "%03d.LFL", kGenRoomNum, UNK_LANG, UNK, 0 }, @@ -471,6 +481,8 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "monkey2", "mi2demo.%03d", kGenDiskNum, UNK_LANG, UNK, 0 }, { "atlantis", "atlantis.%03d", kGenDiskNum, UNK_LANG, UNK, 0 }, + { "atlantis", "atlantis.%03d", kGenDiskNumSteam, UNK_LANG, Common::kPlatformWindows, "Steam" }, + { "atlantis", "atlantis.%03d", kGenDiskNumSteam, UNK_LANG, Common::kPlatformMacintosh, "Steam" }, { "atlantis", "fate.%03d", kGenDiskNum, UNK_LANG, UNK, 0 }, { "atlantis", "playfate.%03d", kGenDiskNum, UNK_LANG, UNK, 0 }, { "atlantis", "indy4.%03d", kGenDiskNum, Common::JA_JPN, Common::kPlatformFMTowns, "FM-TOWNS" }, @@ -494,6 +506,8 @@ static const GameFilenamePattern gameFilenamesTable[] = { #ifdef ENABLE_SCUMM_7_8 { "dig", "dig.la%d", kGenDiskNum, UNK_LANG, UNK, 0 }, + { "dig", "dig.la%d", kGenDiskNumSteam, UNK_LANG, Common::kPlatformWindows, "Steam" }, + { "dig", "dig.la%d", kGenDiskNumSteam, UNK_LANG, Common::kPlatformMacintosh, "Steam" }, { "dig", "thedig.la%d", kGenDiskNum, UNK_LANG, UNK, "Demo" }, // Used by an alternate version of the demo { "dig", "The Dig Data", kGenUnchanged, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "dig", "The Dig Demo Data", kGenUnchanged, UNK_LANG, Common::kPlatformMacintosh, "Demo" }, diff --git a/engines/scumm/file.cpp b/engines/scumm/file.cpp index 9c161ff1cc..21b0e594bf 100644 --- a/engines/scumm/file.cpp +++ b/engines/scumm/file.cpp @@ -29,6 +29,22 @@ namespace Scumm { +// The following table includes all the index files, which are embedded in the +// main game executables in Steam versions. +const SteamIndexFile steamIndexFiles[] = { + { GID_INDY3, Common::kPlatformWindows, "%02d.LFL", "00.LFL", "Indiana Jones and the Last Crusade.exe", 162056, 6295 }, + { GID_INDY3, Common::kPlatformMacintosh, "%02d.LFL", "00.LFL", "The Last Crusade", 150368, 6295 }, + { GID_INDY4, Common::kPlatformWindows, "atlantis.%03d", "ATLANTIS.000", "Indiana Jones and the Fate of Atlantis.exe", 224336, 12035 }, + { GID_INDY4, Common::kPlatformMacintosh, "atlantis.%03d", "ATLANTIS.000", "The Fate of Atlantis", 260224, 12035 }, + { GID_LOOM, Common::kPlatformWindows, "%03d.LFL", "000.LFL", "Loom.exe", 187248, 8307 }, + { GID_LOOM, Common::kPlatformMacintosh, "%03d.LFL", "000.LFL", "Loom", 170464, 8307 }, +#ifdef ENABLE_SCUMM_7_8 + { GID_DIG, Common::kPlatformWindows, "dig.la%d", "DIG.LA0", "The Dig.exe", 340632, 16304 }, + { GID_DIG, Common::kPlatformMacintosh, "dig.la%d", "DIG.LA0", "The Dig", 339744, 16304 }, +#endif + { 0, Common::kPlatformUnknown, "", "", "", 0, 0 } +}; + #pragma mark - #pragma mark --- ScummFile --- #pragma mark - @@ -185,6 +201,29 @@ uint32 ScummFile::read(void *dataPtr, uint32 dataSize) { } #pragma mark - +#pragma mark --- ScummSteamFile --- +#pragma mark - +bool ScummSteamFile::open(const Common::String &filename) { + for (const SteamIndexFile *indexFile = steamIndexFiles; indexFile->len; ++indexFile) { + if (indexFile->id == _steamGame.id && indexFile->platform == _steamGame.platform && filename.equalsIgnoreCase(indexFile->indexFileName)) + return openWithSubRange(indexFile->executableName, indexFile->start, indexFile->len); + } + + // Regular non-bundled file + return ScummFile::open(filename); +} + +bool ScummSteamFile::openWithSubRange(const Common::String &filename, int32 subFileStart, int32 subFileLen) { + if (ScummFile::open(filename)) { + _subFileStart = subFileStart; + _subFileLen = subFileLen; + seek(0, SEEK_SET); + return true; + } else + return false; +} + +#pragma mark - #pragma mark --- ScummDiskImage --- #pragma mark - diff --git a/engines/scumm/file.h b/engines/scumm/file.h index d6dbc06ddc..f3eaac5d32 100644 --- a/engines/scumm/file.h +++ b/engines/scumm/file.h @@ -53,7 +53,7 @@ public: }; class ScummFile : public BaseScummFile { -private: +protected: int32 _subFileStart; int32 _subFileLen; bool _myEos; // Have we read past the end of the subfile? @@ -64,7 +64,7 @@ private: public: ScummFile(); - bool open(const Common::String &filename); + virtual bool open(const Common::String &filename); bool openSubFile(const Common::String &filename); void clearErr() { _myEos = false; BaseScummFile::clearErr(); } @@ -76,6 +76,18 @@ public: uint32 read(void *dataPtr, uint32 dataSize); }; +class ScummSteamFile : public ScummFile { +private: + GameSettings _steamGame; + + bool openWithSubRange(const Common::String &filename, int32 subFileStart, int32 subFileLen); + +public: + ScummSteamFile(GameSettings game) : ScummFile(), _steamGame(game) {} + + bool open(const Common::String &filename); +}; + class ScummDiskImage : public BaseScummFile { private: Common::SeekableReadStream *_stream; @@ -120,6 +132,18 @@ public: uint32 read(void *dataPtr, uint32 dataSize); }; +struct SteamIndexFile { + byte id; + Common::Platform platform; + const char *pattern; + const char *indexFileName; + const char *executableName; + int32 start; + int32 len; +}; + +extern const SteamIndexFile steamIndexFiles[]; + } // End of namespace Scumm #endif diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index d43db1e5f1..416a8f7ef9 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -7,6 +7,7 @@ MODULE_OBJS := \ bomp.o \ boxes.o \ camera.o \ + cdda.o \ charset.o \ charset-fontdata.o \ costume.o \ diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index 0aaff4c094..7eadb042fb 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -1453,7 +1453,7 @@ void ScummEngine::saveOrLoad(Serializer *s) { // forever, then resume playing it. This helps a lot when the audio CD // is used to provide ambient music (see bug #788195). if (s->isLoading() && info.playing && info.numLoops < 0) - _system->getAudioCDManager()->play(info.track, info.numLoops, info.start, info.duration); + _sound->playCDTrackInternal(info.track, info.numLoops, info.start, info.duration); } diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index 8accf39129..0eeff57ff7 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@ /* - This file was generated by the md5table tool on Wed Jun 25 09:07:50 2014 + This file was generated by the md5table tool on Wed Jun 25 10:34:07 2014 DO NOT EDIT MANUALLY! */ @@ -17,6 +17,7 @@ static const MD5Table md5table[] = { { "008e76ec3ae58d0add637ea7aa299a2c", "freddi3", "", "", -1, Common::FR_FRA, Common::kPlatformMacintosh }, { "02cae0e7ff8504f73618391873d5781a", "freddi3", "HE 98.5", "", -1, Common::DE_DEU, Common::kPlatformWindows }, { "0305e850382b812fec6e5998ef88a966", "pajama", "", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, + { "0354ee0d14cde1264ec762261c04c14a", "loom", "Steam", "Steam", 585728, Common::EN_ANY, Common::kPlatformWindows }, { "035deab53b47bc43abc763560d0f8d4b", "atlantis", "Floppy", "Demo", -1, Common::EN_ANY, Common::kPlatformDOS }, { "037385a953789190298494d92b89b3d0", "catalog", "HE 72", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows }, { "03d3b18ee3fd68114e2a687c871e38d5", "freddi4", "HE 99", "Mini Game", -1, Common::EN_USA, Common::kPlatformWindows }, @@ -299,6 +300,7 @@ static const MD5Table md5table[] = { { "69ffe29185b8d71f09f6199f8b2a87cb", "lost", "HE 100", "", -1, Common::RU_RUS, Common::kPlatformWindows }, { "6a30a07f353a75cdc602db27d73e1b42", "puttputt", "HE 70", "", -1, Common::EN_ANY, Common::kPlatformWindows }, { "6a60d395b78b205c93a956100b1bf5ae", "pajama2", "HE 98.5", "", -1, Common::DE_DEU, Common::kPlatformUnknown }, + { "6a8133b63d46f6663fbcbb49d5a2edb1", "atlantis", "Steam", "Steam", 520548, Common::EN_ANY, Common::kPlatformMacintosh }, { "6af2419fe3db5c2fdb091ae4e5833770", "puttrace", "HE 98.5", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "6b19d0e25cbf720d05822379b8b90ed9", "PuttTime", "HE 90", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "6b257bb2827dd894b8109a50a1a18b5a", "freddicove", "HE 100", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, @@ -357,6 +359,7 @@ static const MD5Table md5table[] = { { "7edd665bbede7ea8b7233f8e650be6f8", "samnmax", "", "CD", -1, Common::FR_FRA, Common::kPlatformUnknown }, { "7f45ddd6dbfbf8f80c0c0efea4c295bc", "maniac", "V1", "V1", 1972, Common::EN_ANY, Common::kPlatformDOS }, { "7f945525abcd48015adf1632637a44a1", "pajama", "", "Demo", -1, Common::FR_FRA, Common::kPlatformUnknown }, + { "7fbcff27c323499beaedd605e1ebd47d", "indy3", "Steam", "Steam", 561152, Common::EN_ANY, Common::kPlatformWindows }, { "7fc6cdb46b4c9d384c52327f4bca6416", "football", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown }, { "810a9da887aefa597b0cf3c77d262897", "BluesABCTime", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown }, { "822807c3cd3b43a925cab2767ca6b453", "BluesTreasureHunt", "", "Disc 1", -1, Common::EN_ANY, Common::kPlatformUnknown }, @@ -439,6 +442,7 @@ static const MD5Table md5table[] = { { "a095616d2d23ccf43b8e257711202cba", "football2002", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown }, { "a095e33061606d231ff37dca4c64c8ac", "pajama", "HE 99", "", -1, Common::DE_DEU, Common::kPlatformUnknown }, { "a0a7dea72003933b8b3f8b99b9f7ddeb", "loom", "No AdLib", "EGA", -1, Common::EN_ANY, Common::kPlatformAtariST }, + { "a15d6e1e2c52bbd0ff7fa6b63ab7f796", "indy3", "Steam", "Steam", 680340, Common::EN_ANY, Common::kPlatformMacintosh }, { "a194f15f51ee62badab74b9e7da97693", "baseball2001", "", "Demo", 20507, Common::EN_ANY, Common::kPlatformUnknown }, { "a197a87ae77f3b3333f09a7a2c448fe2", "freddi", "HE 99", "Updated", -1, Common::EN_ANY, Common::kPlatformWindows }, { "a22af0ad0e3126d19d22707b0267a37d", "balloon", "HE 80", "", -1, Common::NL_NLD, Common::kPlatformWindows }, @@ -467,6 +471,7 @@ static const MD5Table md5table[] = { { "aa8a0cb65f3afbbe2c14c3f9f92775a3", "monkey", "CD", "CD", 8955, Common::FR_FRA, Common::kPlatformDOS }, { "aaa587701cde7e74692c68c1024b85eb", "puttrace", "HE 99", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "aaa7f36a253f277dd29dd1c051b0e4b9", "indy3", "No AdLib", "EGA", -1, Common::DE_DEU, Common::kPlatformAtariST }, + { "aad201302286c1cfee92321cd406e427", "dig", "Steam", "Steam", 811008, Common::EN_ANY, Common::kPlatformWindows }, { "ab0693e9324cfcf498fdcbb12acf8bb4", "puttcircus", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown }, { "ac1642b6edfb8521ca03760126f1c250", "tentacle", "", "Demo", -1, Common::DE_DEU, Common::kPlatformDOS }, { "ac62d50e39492ee3738b4e83a5ac780f", "freddi2", "HE 80", "", -1, Common::NL_NLD, Common::kPlatformWindows }, @@ -480,6 +485,7 @@ static const MD5Table md5table[] = { { "b250d0f9cc83f80ced56fe11a4fb057c", "maniac", "V2", "V2", 1988, Common::EN_ANY, Common::kPlatformDOS }, { "b289a2a8cbedbf45786e0b4ad2f510f1", "samnmax", "Floppy", "Floppy", -1, Common::IT_ITA, Common::kPlatformDOS }, { "b47be81e39a9710f6f595f7b527b60f8", "puttrace", "HE 99", "", -1, Common::EN_GRB, Common::kPlatformWindows }, + { "b4a677bf27c010a747975705108ff1e6", "loom", "Steam", "Steam", 393572, Common::EN_ANY, Common::kPlatformMacintosh }, { "b5298a5c15ffbe8b381d51ea4e26d35c", "freddi4", "HE 99", "", -1, Common::DE_DEU, Common::kPlatformUnknown }, { "b597e0403cc0002f69170e6caba7edd9", "indy3", "EGA", "EGA Demo", 5361, Common::EN_ANY, Common::kPlatformDOS }, { "b628506f7def772e40de0aa5440fb8e1", "activity", "HE 70", "", -1, Common::EN_ANY, Common::kPlatformWindows }, @@ -569,6 +575,7 @@ static const MD5Table md5table[] = { { "d7b247c26bf1f01f8f7daf142be84de3", "balloon", "HE 99", "Updated", -1, Common::EN_ANY, Common::kPlatformWindows }, { "d8323015ecb8b10bf53474f6e6b0ae33", "dig", "", "", 16304, Common::UNK_LANG, Common::kPlatformUnknown }, { "d917f311a448e3cc7239c31bddb00dd2", "samnmax", "", "CD", 9080, Common::EN_ANY, Common::kPlatformUnknown }, + { "d93cc8be628ed5d3b3a29188fc7105d3", "dig", "Steam", "Steam", 1061296, Common::EN_ANY, Common::kPlatformMacintosh }, { "d9d0dd93d16ab4dec55cabc2b86bbd17", "samnmax", "", "Demo", 6478, Common::EN_ANY, Common::kPlatformDOS }, { "da09e666fc8f5b78d7b0ac65d1a3b56e", "monkey2", "FM-TOWNS", "", 11135, Common::EN_ANY, Common::kPlatformFMTowns }, { "da6269b18fcb08189c0aa9c95533cce2", "monkey", "CD", "CD", 8955, Common::IT_ITA, Common::kPlatformDOS }, @@ -629,6 +636,7 @@ static const MD5Table md5table[] = { { "f237bf8a5ef9af78b2a6a4f3901da341", "pajama", "", "Demo", 18354, Common::EN_ANY, Common::kPlatformUnknown }, { "f27b1ba0eadaf2a6617b2b58192d1dbf", "samnmax", "Floppy", "Floppy", -1, Common::DE_DEU, Common::kPlatformDOS }, { "f2ec78e50bdc63b70044e9758be10914", "spyfox", "HE 98.5", "Demo", -1, Common::NL_NLD, Common::kPlatformMacintosh }, + { "f3c5d9bf3f091bd1f18dc1013fba5396", "atlantis", "Steam", "Steam", 638976, Common::EN_ANY, Common::kPlatformWindows }, { "f3d55aea441e260e9e9c7d2a187097e0", "puttzoo", "", "Demo", 14337, Common::EN_ANY, Common::kPlatformWindows }, { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "HE 60", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh }, { "f5228b0cc1c19e6ea8268ba2eeb61f60", "freddi", "HE 73", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows }, diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 34c231e5d4..8c0325934d 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -1026,6 +1026,35 @@ Common::Error ScummEngine::init() { } #endif + // Extra directories needed for the Steam versions + if (_filenamePattern.genMethod == kGenDiskNumSteam || _filenamePattern.genMethod == kGenRoomNumSteam) { + if (_game.platform == Common::kPlatformWindows) { + switch (_game.id) { + case GID_INDY3 : + SearchMan.addSubDirectoryMatching(gameDataDir, "indy3"); + break; + case GID_INDY4 : + SearchMan.addSubDirectoryMatching(gameDataDir, "atlantis"); + break; + case GID_LOOM : + SearchMan.addSubDirectoryMatching(gameDataDir, "loom"); + break; +#ifdef ENABLE_SCUMM_7_8 + case GID_DIG : + SearchMan.addSubDirectoryMatching(gameDataDir, "dig"); + SearchMan.addSubDirectoryMatching(gameDataDir, "dig/video"); + break; +#endif + default: + break; + } + } else { + SearchMan.addSubDirectoryMatching(gameDataDir, "Contents"); + SearchMan.addSubDirectoryMatching(gameDataDir, "Contents/MacOS"); + SearchMan.addSubDirectoryMatching(gameDataDir, "Contents/Resources"); + SearchMan.addSubDirectoryMatching(gameDataDir, "Contents/Resources/video"); + } + } // The kGenUnchanged method is only used for 'container files', i.e. files // that contain the real game files bundled together in an archive format. @@ -1126,15 +1155,25 @@ Common::Error ScummEngine::init() { error("Couldn't find known subfile inside container file '%s'", _containerFile.c_str()); _fileHandle->close(); - } else { error("kGenUnchanged used with unsupported platform"); } } else { - // Regular access, no container file involved - _fileHandle = new ScummFile(); + if (_filenamePattern.genMethod == kGenDiskNumSteam || _filenamePattern.genMethod == kGenRoomNumSteam) { + // Steam game versions have the index file embedded in the main executable + _fileHandle = new ScummSteamFile(_game); + } else { + // Regular access, no container file involved + _fileHandle = new ScummFile(); + } } + // Steam Win and Mac versions share the same DOS data files. We show Windows or Mac + // for the platform the detector, but internally we force the platform to DOS, so that + // the code for handling the original DOS data files is used. + if (_filenamePattern.genMethod == kGenDiskNumSteam || _filenamePattern.genMethod == kGenRoomNumSteam) + _game.platform = Common::kPlatformDOS; + // Load CJK font, if present // Load it earlier so _useCJKMode variable could be set loadCJKFont(); @@ -1218,7 +1257,7 @@ Common::Error ScummEngine::init() { void ScummEngine::setupScumm() { // On some systems it's not safe to run CD audio games from the CD. - if (_game.features & GF_AUDIOTRACKS) { + if (_game.features & GF_AUDIOTRACKS && !Common::File::exists("CDDA.SOU")) { checkCD(); int cd_num = ConfMan.getInt("cdrom"); diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index 01bdefc7c0..21bf565a4e 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -27,6 +27,7 @@ #include "common/substream.h" #include "scumm/actor.h" +#include "scumm/cdda.h" #include "scumm/file.h" #include "scumm/imuse/imuse.h" #include "scumm/imuse_digi/dimuse.h" @@ -36,8 +37,6 @@ #include "scumm/sound.h" #include "scumm/util.h" -#include "backends/audiocd/audiocd.h" - #include "audio/decoders/adpcm.h" #include "audio/decoders/flac.h" #include "audio/mididrv.h" @@ -89,11 +88,21 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer) memset(_mouthSyncTimes, 0, sizeof(_mouthSyncTimes)); _musicType = MDT_NONE; + + _loomSteamCD.playing = false; + _loomSteamCD.track = 0; + _loomSteamCD.start = 0; + _loomSteamCD.duration = 0; + _loomSteamCD.numLoops = 0; + _loomSteamCD.volume = Audio::Mixer::kMaxChannelVolume; + _loomSteamCD.balance = 0; + + _isLoomSteam = _vm->_game.id == GID_LOOM && Common::File::exists("CDDA.SOU"); } Sound::~Sound() { stopCDTimer(); - g_system->getAudioCDManager()->stop(); + stopCD(); free(_offsetTable); } @@ -1033,7 +1042,7 @@ void Sound::playCDTrack(int track, int numLoops, int startFrame, int duration) { // Play it if (!_soundsPaused) - g_system->getAudioCDManager()->play(track, numLoops, startFrame, duration); + playCDTrackInternal(track, numLoops, startFrame, duration); // Start the timer after starting the track. Starting an MP3 track is // almost instantaneous, but a CD player may take some time. Hopefully @@ -1041,16 +1050,59 @@ void Sound::playCDTrack(int track, int numLoops, int startFrame, int duration) { startCDTimer(); } +void Sound::playCDTrackInternal(int track, int numLoops, int startFrame, int duration) { + _loomSteamCD.track = track; + _loomSteamCD.numLoops = numLoops; + _loomSteamCD.start = startFrame; + _loomSteamCD.duration = duration; + + if (!_isLoomSteam) { + g_system->getAudioCDManager()->play(track, numLoops, startFrame, duration); + } else { + // Stop any currently playing track + _mixer->stopHandle(_loomSteamCDAudioHandle); + + Common::File *cddaFile = new Common::File(); + if (cddaFile->open("CDDA.SOU")) { + Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75); + Audio::Timestamp end = Audio::Timestamp(0, startFrame + duration, 75); + Audio::SeekableAudioStream *stream = makeCDDAStream(cddaFile, DisposeAfterUse::YES); + + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_loomSteamCDAudioHandle, + Audio::makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops)); + } else { + delete cddaFile; + } + } +} + void Sound::stopCD() { - g_system->getAudioCDManager()->stop(); + if (!_isLoomSteam) + g_system->getAudioCDManager()->stop(); + else + _mixer->stopHandle(_loomSteamCDAudioHandle); } int Sound::pollCD() const { - return g_system->getAudioCDManager()->isPlaying(); + if (!_isLoomSteam) + return g_system->getAudioCDManager()->isPlaying(); + else + return _mixer->isSoundHandleActive(_loomSteamCDAudioHandle); } void Sound::updateCD() { - g_system->getAudioCDManager()->updateCD(); + if (!_isLoomSteam) + g_system->getAudioCDManager()->updateCD(); +} + +AudioCDManager::Status Sound::getCDStatus() { + if (!_isLoomSteam) + return g_system->getAudioCDManager()->getStatus(); + else { + AudioCDManager::Status info = _loomSteamCD; + _loomSteamCD.playing = _mixer->isSoundHandleActive(_loomSteamCDAudioHandle); + return info; + } } void Sound::saveLoadWithSerializer(Serializer *ser) { diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h index a96d2b8ed5..a479ad5731 100644 --- a/engines/scumm/sound.h +++ b/engines/scumm/sound.h @@ -27,6 +27,7 @@ #include "audio/audiostream.h" #include "audio/mididrv.h" #include "audio/mixer.h" +#include "backends/audiocd/audiocd.h" #include "scumm/saveload.h" namespace Audio { @@ -86,6 +87,10 @@ protected: int16 _currentCDSound; int16 _currentMusic; + Audio::SoundHandle _loomSteamCDAudioHandle; + bool _isLoomSteam; + AudioCDManager::Status _loomSteamCD; + public: Audio::SoundHandle _talkChannelHandle; // Handle of mixer channel actor is talking on @@ -119,9 +124,11 @@ public: void stopCDTimer(); void playCDTrack(int track, int numLoops, int startFrame, int duration); + void playCDTrackInternal(int track, int numLoops, int startFrame, int duration); void stopCD(); int pollCD() const; void updateCD(); + AudioCDManager::Status getCDStatus(); int getCurrentCDSound() const { return _currentCDSound; } // Used by the save/load system: |