[Scummvm-git-logs] scummvm master -> 75a89a0e51644ab7fb6b983c008f175ffba45c56
bluegr
noreply at scummvm.org
Sat Dec 21 17:01:16 UTC 2024
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
fe8380fc65 SCUMM: Separate the PAK index reading code
75a89a0e51 SCUMM: Handle the FMOD FSB audio files used in DOTT SE and FT SE
Commit: fe8380fc6501db27734ea2aaf176d9524efdb7f0
https://github.com/scummvm/scummvm/commit/fe8380fc6501db27734ea2aaf176d9524efdb7f0
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2024-12-21T18:55:18+02:00
Commit Message:
SCUMM: Separate the PAK index reading code
Changed paths:
engines/scumm/file.cpp
engines/scumm/file.h
diff --git a/engines/scumm/file.cpp b/engines/scumm/file.cpp
index 9a79adb32cb..7e3fd5b8cad 100644
--- a/engines/scumm/file.cpp
+++ b/engines/scumm/file.cpp
@@ -223,13 +223,15 @@ bool ScummSteamFile::openWithSubRange(const Common::Path &filename, int32 subFil
#pragma mark -
ScummPAKFile::ScummPAKFile(const ScummEngine *vm, bool indexFiles) : ScummFile(vm) {
- if (!indexFiles)
- return;
+ if (indexFiles)
+ readIndex(vm->_containerFile, vm->_game.id == GID_FT);
+}
- ScummFile::open(vm->_containerFile);
+void ScummPAKFile::readIndex(const Common::Path &containerFile, bool isFT) {
+ // Based off DoubleFine Explorer: https://github.com/bgbennyboy/DoubleFine-Explorer/blob/master/uDFExplorer_LPAKManager.pas
+ ScummFile::open(containerFile);
const uint32 magic = _baseStream->readUint32BE();
- const bool isFT = vm->_game.id == GID_FT;
const byte recordSize = isFT ? 24 : 20;
if (magic != MKTAG('K', 'A', 'P', 'L')) {
diff --git a/engines/scumm/file.h b/engines/scumm/file.h
index ddd895a9066..5dd0a15251f 100644
--- a/engines/scumm/file.h
+++ b/engines/scumm/file.h
@@ -161,6 +161,8 @@ class ScummPAKFile : public ScummFile {
private:
PAKFileHashMap _pakIndex;
+ void readIndex(const Common::Path &containerFile, bool isFT);
+
public:
ScummPAKFile(const ScummEngine *vm, bool indexFiles = true);
~ScummPAKFile() override { _pakIndex.clear(); }
Commit: 75a89a0e51644ab7fb6b983c008f175ffba45c56
https://github.com/scummvm/scummvm/commit/75a89a0e51644ab7fb6b983c008f175ffba45c56
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2024-12-21T18:55:19+02:00
Commit Message:
SCUMM: Handle the FMOD FSB audio files used in DOTT SE and FT SE
Changed paths:
engines/scumm/file.cpp
engines/scumm/sound.cpp
engines/scumm/soundse.cpp
engines/scumm/soundse.h
diff --git a/engines/scumm/file.cpp b/engines/scumm/file.cpp
index 7e3fd5b8cad..9403b572b51 100644
--- a/engines/scumm/file.cpp
+++ b/engines/scumm/file.cpp
@@ -276,6 +276,7 @@ void ScummPAKFile::readIndex(const Common::Path &containerFile, bool isFT) {
fileName.hasPrefixIgnoreCase("maniac/") || // DOTT MM easter egg
fileName.hasPrefixIgnoreCase("data/") || // FT data folder
fileName.hasPrefixIgnoreCase("video/") || // FT video folder
+ fileName.hasPrefixIgnoreCase("audio/") || // DOTT and FT SE audio folder
fileName.hasPrefixIgnoreCase("en/data/") || // TODO: Support non-English versions
fileName.hasPrefixIgnoreCase("en/video/")) { // TODO: Support non-English versions
// Remove the directory prefix
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index be5e66757ec..c81ff371b65 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -534,7 +534,7 @@ void Sound::triggerSound(int soundID) {
// TODO: If called from MI2SE, this will play the music
// multiple times
//if (_soundSE) {
- // _soundSE->startMusic(soundID);
+ // _soundSE->startSound(soundID);
// return;
//}
diff --git a/engines/scumm/soundse.cpp b/engines/scumm/soundse.cpp
index 9d2e0c97bde..54417e93cec 100644
--- a/engines/scumm/soundse.cpp
+++ b/engines/scumm/soundse.cpp
@@ -46,20 +46,35 @@ void SoundSE::initSoundFiles() {
switch (_vm->_game.id) {
case GID_MONKEY:
case GID_MONKEY2:
- _xwbMusicFilename = "MusicOriginal.xwb";
- //_xwbMusicFilename = "MusicNew.xwb"; // TODO: allow toggle between original and new music
- indexXWBFile(_xwbMusicFilename, &_xwbMusicEntries);
- _xwbSfxFilename = "SFXOriginal.xwb";
- //_xwbSfxFilename = "SFXNew.xwb"; // TODO: allow toggle between original and new SFX
- indexXWBFile(_xwbSfxFilename, &_xwbSfxEntries);
- _xwbSpeechFilename = "Speech.xwb";
- indexXWBFile(_xwbSpeechFilename, &_xwbSpeechEntries);
+ _musicFilename = "MusicOriginal.xwb";
+ //_musicFilename = "MusicNew.xwb"; // TODO: allow toggle between original and new music
+ indexXWBFile(_musicFilename, &_musicEntries);
+ _sfxFilename = "SFXOriginal.xwb";
+ //_sfxFilename = "SFXNew.xwb"; // TODO: allow toggle between original and new SFX
+ indexXWBFile(_sfxFilename, &_sfxEntries);
+ _speechFilename = "Speech.xwb";
+ indexXWBFile(_speechFilename, &_speechEntries);
+ // TODO: iMUSEClient_Commentary.fsb
break;
case GID_TENTACLE:
- // TODO
+ _musicFilename = "iMUSEClient_Music.fsb";
+ indexFSBFile(_musicFilename, &_musicEntries);
+ _sfxFilename = "iMUSEClient_SFX.fsb";
+ indexFSBFile(_sfxFilename, &_sfxEntries);
+ _speechFilename = "iMUSEClient_VO.fsb";
+ indexFSBFile(_speechFilename, &_speechEntries);
+ // TODO: iMUSEClient_Commentary.fsb
break;
case GID_FT:
- // TODO
+ _musicFilename = "iMUSEClient_Music.fsb";
+ indexFSBFile(_musicFilename, &_musicEntries);
+ _sfxFilename = "iMUSEClient_SFX_INMEMORY.fsb";
+ indexFSBFile(_sfxFilename, &_sfxEntries);
+ _speechFilename = "iMUSEClient_SPEECH.fsb";
+ indexFSBFile(_speechFilename, &_speechEntries);
+ // TODO: iMUSEClient_SFX_STREAMING.fsb
+ // TODO: iMUSEClient_UI.fsb
+ // TODO: iMUSEClient_Commentary.fsb
break;
default:
error("initSoundFiles: unhandled game");
@@ -69,12 +84,12 @@ void SoundSE::initSoundFiles() {
Audio::SeekableAudioStream *SoundSE::getXWBTrack(int track) {
Common::File *cdAudioFile = new Common::File();
- if (!cdAudioFile->open(Common::Path(_xwbMusicFilename))) {
+ if (!cdAudioFile->open(Common::Path(_musicFilename))) {
delete cdAudioFile;
return nullptr;
}
- XWBEntry entry = _xwbMusicEntries[track];
+ AudioEntry entry = _musicEntries[track];
auto subStream = new Common::SeekableSubReadStream(
cdAudioFile,
@@ -83,7 +98,7 @@ Audio::SeekableAudioStream *SoundSE::getXWBTrack(int track) {
DisposeAfterUse::YES
);
- return createXWBStream(subStream, entry);
+ return createSoundStream(subStream, entry);
}
#define WARN_AND_RETURN_XWB(message) \
@@ -94,7 +109,7 @@ Audio::SeekableAudioStream *SoundSE::getXWBTrack(int track) {
return; \
}
-void SoundSE::indexXWBFile(const Common::String &filename, XWBIndex *xwbIndex) {
+void SoundSE::indexXWBFile(const Common::String &filename, AudioIndex *audioIndex) {
// This implementation is based off unxwb: https://github.com/mariodon/unxwb/
// as well as xwbdump: https://raw.githubusercontent.com/wiki/Microsoft/DirectXTK/xwbdump.cpp
// Only the parts that apply to the Doublefine releases of
@@ -138,7 +153,7 @@ void SoundSE::indexXWBFile(const Common::String &filename, XWBIndex *xwbIndex) {
f->seek(segments[kXWBSegmentEntryMetaData].offset);
for (uint32 i = 0; i < entryCount; i++) {
- XWBEntry entry;
+ AudioEntry entry;
/*uint32 flagsAndDuration = */ f->readUint32LE();
uint32 format = f->readUint32LE();
entry.offset = f->readUint32LE() + segments[kXWBSegmentEntryWaveData].offset;
@@ -146,13 +161,13 @@ void SoundSE::indexXWBFile(const Common::String &filename, XWBIndex *xwbIndex) {
/*uint32 loopOffset = */ f->readUint32LE();
/*uint32 loopLength = */ f->readUint32LE();
- entry.codec = static_cast<XWBCodec>(format & ((1 << 2) - 1));
+ entry.codec = static_cast<AudioCodec>(format & ((1 << 2) - 1));
entry.channels = (format >> (2)) & ((1 << 3) - 1);
entry.rate = (format >> (2 + 3)) & ((1 << 18) - 1);
entry.align = (format >> (2 + 3 + 18)) & ((1 << 8) - 1);
entry.bits = (format >> (2 + 3 + 18 + 8)) & ((1 << 1) - 1);
- xwbIndex->push_back(entry);
+ audioIndex->push_back(entry);
}
f->close();
@@ -161,7 +176,90 @@ void SoundSE::indexXWBFile(const Common::String &filename, XWBIndex *xwbIndex) {
#undef WARN_AND_RETURN_XWB
-Audio::SeekableAudioStream *SoundSE::createXWBStream(Common::SeekableSubReadStream *stream, XWBEntry entry) {
+#define WARN_AND_RETURN_FSB(message) \
+ { \
+ warning("indexFSBFile: %s", message); \
+ f->close(); \
+ delete f; \
+ return; \
+ }
+
+#define GET_FSB5_OFFSET(X) ((((X) >> (uint64)7) << (uint64)5) & (((uint64)1 << (uint64)32) - 1))
+
+void SoundSE::indexFSBFile(const Common::String &filename, AudioIndex *audioIndex) {
+ // Based off DoubleFine Explorer: https://github.com/bgbennyboy/DoubleFine-Explorer/blob/master/uDFExplorer_FSBManager.pas
+ // and fsbext: https://aluigi.altervista.org/search.php?src=fsbext
+ ScummPAKFile *f = new ScummPAKFile(_vm);
+ _vm->openFile(*f, Common::Path(filename));
+
+ const uint32 headerSize = 60; // 4 * 7 + 8 + 16 + 8
+ const uint32 magic = f->readUint32BE();
+ if (magic != MKTAG('F', 'S', 'B', '5'))
+ WARN_AND_RETURN_FSB("Invalid FSB file")
+
+ /*const uint32 version = */f->readUint32LE();
+ const uint32 sampleCount = f->readUint32LE();
+ const uint32 sampleHeaderSize = f->readUint32LE();
+ const uint32 nameSize = f->readUint32LE();
+ const uint32 dataSize = f->readUint32LE();
+ /*const uint32 mode = */f->readUint32LE();
+ f->skip(8); // skip zero
+ f->skip(16); // skip hash
+ f->skip(8); // skip dummy
+ const uint32 nameOffset = sampleHeaderSize + headerSize;
+ const uint32 baseOffset = headerSize + sampleHeaderSize + nameSize;
+
+ for (uint32 i = 0; i < sampleCount; i++) {
+ const uint32 origOffset = f->readUint32LE();
+ f->skip(4); // samples, used in XMA
+ uint32 type = origOffset & ((1 << 7) - 1);
+ const uint32 fileOffset = nameOffset + nameSize + GET_FSB5_OFFSET(origOffset);
+ uint32 size;
+
+ // Meta data, skip it
+ while (type & 1) {
+ const uint32 t = f->readUint32LE();
+ type = t & 1;
+ const uint32 metaDataSize = (t & 0xffffff) >> 1;
+ f->skip(metaDataSize);
+ }
+
+ if (f->pos() < nameOffset) {
+ size = f->readUint32LE();
+ f->seek(-4, SEEK_CUR);
+ if (!size) {
+ size = dataSize + baseOffset;
+ } else {
+ size = GET_FSB5_OFFSET(size) + baseOffset;
+ }
+ } else {
+ size = dataSize + baseOffset;
+ }
+
+ size -= fileOffset;
+
+ AudioEntry entry;
+ entry.length = size;
+ entry.offset = fileOffset;
+ // The following are all unused - they'll
+ // be read from the MP3 streams
+ entry.rate = 48000;
+ entry.channels = 2;
+ entry.codec = kFSBCodecMP3;
+ entry.align = 0;
+ entry.bits = 16;
+
+ audioIndex->push_back(entry);
+ }
+
+ f->close();
+ delete f;
+}
+
+#undef GET_FSB5_OFFSET
+#undef WARN_AND_RETURN_FSB
+
+Audio::SeekableAudioStream *SoundSE::createSoundStream(Common::SeekableSubReadStream *stream, AudioEntry entry) {
switch (entry.codec) {
case kXWBCodecPCM: {
byte flags = Audio::FLAG_LITTLE_ENDIAN;
@@ -173,7 +271,7 @@ Audio::SeekableAudioStream *SoundSE::createXWBStream(Common::SeekableSubReadStre
}
case kXWBCodecXMA:
// Unused in MI1SE and MI2SE
- error("createXWBStream: XMA codec not supported");
+ error("createSoundStream: XMA codec not supported");
case kXWBCodecADPCM: {
const uint32 blockAlign = (entry.align + 22) * entry.channels;
return Audio::makeADPCMStream(
@@ -196,50 +294,76 @@ Audio::SeekableAudioStream *SoundSE::createXWBStream(Common::SeekableSubReadStre
entry.align,
stream
);*/
- warning("createXWBStream: WMA codec not implemented");
+ warning("createSoundStream: WMA codec not implemented");
delete stream;
return nullptr;
+ case kFSBCodecMP3:
+ return Audio::makeMP3Stream(
+ stream,
+ DisposeAfterUse::YES
+ );
}
- error("createXWBStream: Unknown XWB codec %d", entry.codec);
+ error("createSoundStream: Unknown XWB codec %d", entry.codec);
}
-#if 0
-void SoundSE::startMusic(int soundID) {
- int entry = -1;
-
- // HACK: Find the first entry with offset 8192 (MI2 theme)
- // TODO: Map soundID to entry (*.xsb files)
- for (int i = 0; i < _xwbMusicEntries.size(); i++) {
- if (_xwbMusicEntries[i].offset == 8192) {
- entry = i;
- break;
- }
+void SoundSE::startSoundEntry(int soundIndex, SoundSEType type) {
+ Common::SeekableReadStream *stream = nullptr;
+ Audio::SoundHandle *handle = nullptr;
+ Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType;
+ Common::String audioFileName;
+ AudioIndex &audioEntries = _musicEntries;
+
+ switch (type) {
+ case kSoundSETypeMusic:
+ handle = &_musicHandle;
+ soundType = Audio::Mixer::kMusicSoundType;
+ audioFileName = _musicFilename;
+ audioEntries = _musicEntries;
+ break;
+ case kSoundSETypeSpeech:
+ handle = &_speechHandle;
+ soundType = Audio::Mixer::kSpeechSoundType;
+ audioFileName = _speechFilename;
+ audioEntries = _speechEntries;
+ break;
+ case kSoundSETypeSFX:
+ handle = &_sfxHandle;
+ soundType = Audio::Mixer::kSFXSoundType;
+ audioFileName = _sfxFilename;
+ audioEntries = _sfxEntries;
+ break;
}
- if (entry == -1)
- return;
-
- Common::File *musicFile = new Common::File();
-
- if (!musicFile->open(Common::Path(_xwbMusicFilename))) {
- delete musicFile;
- return;
+ if (_vm->_game.id == GID_MONKEY || _vm->_game.id == GID_MONKEY2) {
+ Common::File *audioFile = new Common::File();
+ stream = audioFile;
+ if (!audioFile->open(Common::Path(audioFileName))) {
+ delete audioFile;
+ return;
+ }
+ } else {
+ ScummPAKFile *audioFile = new ScummPAKFile(_vm);
+ stream = audioFile;
+ if (!_vm->openFile(*audioFile, Common::Path(audioFileName))) {
+ delete audioFile;
+ return;
+ }
}
- XWBEntry xwbEntry = _xwbMusicEntries[entry];
- Common::SeekableSubReadStream *stream = new Common::SeekableSubReadStream(
- musicFile,
- xwbEntry.offset,
- xwbEntry.offset + xwbEntry.length,
+ AudioEntry audioEntry = audioEntries[soundIndex];
+ Common::SeekableSubReadStream *subStream = new Common::SeekableSubReadStream(
+ stream,
+ audioEntry.offset,
+ audioEntry.offset + audioEntry.length,
DisposeAfterUse::YES
);
_mixer->playStream(
- Audio::Mixer::kMusicSoundType,
- &_musicHandle, createXWBStream(stream, xwbEntry)
+ soundType,
+ handle,
+ createSoundStream(subStream, audioEntry)
);
}
-#endif
} // End of namespace Scumm
diff --git a/engines/scumm/soundse.h b/engines/scumm/soundse.h
index 4540577c072..9bc48e6d60d 100644
--- a/engines/scumm/soundse.h
+++ b/engines/scumm/soundse.h
@@ -38,6 +38,12 @@ namespace Scumm {
class ScummEngine;
+enum SoundSEType {
+ kSoundSETypeMusic,
+ kSoundSETypeSpeech,
+ kSoundSETypeSFX
+};
+
class SoundSE {
protected:
@@ -50,14 +56,15 @@ public:
Audio::SeekableAudioStream *getXWBTrack(int track);
- //void startMusic(int soundID);
+ void startSoundEntry(int soundIndex, SoundSEType type);
private:
- enum XWBCodec {
+ enum AudioCodec {
kXWBCodecPCM = 0,
kXWBCodecXMA = 1,
kXWBCodecADPCM = 2,
- kXWBCodecWMA = 3
+ kXWBCodecWMA = 3,
+ kFSBCodecMP3 = 4
};
enum XWBSegmentType {
@@ -68,33 +75,34 @@ private:
kXWBSegmentEntryWaveData = 4
};
- struct XWBEntry {
- uint32 offset;
+ struct AudioEntry {
+ uint64 offset;
uint32 length;
- XWBCodec codec;
+ AudioCodec codec;
byte channels;
uint16 rate;
uint16 align;
byte bits;
};
- typedef Common::Array<XWBEntry> XWBIndex;
+ typedef Common::Array<AudioEntry> AudioIndex;
- XWBIndex _xwbMusicEntries;
- Common::String _xwbMusicFilename;
+ AudioIndex _musicEntries;
+ Common::String _musicFilename;
Audio::SoundHandle _musicHandle;
- XWBIndex _xwbSpeechEntries;
- Common::String _xwbSpeechFilename;
+ AudioIndex _speechEntries;
+ Common::String _speechFilename;
Audio::SoundHandle _speechHandle;
- XWBIndex _xwbSfxEntries;
- Common::String _xwbSfxFilename;
+ AudioIndex _sfxEntries;
+ Common::String _sfxFilename;
Audio::SoundHandle _sfxHandle;
void initSoundFiles();
- void indexXWBFile(const Common::String &filename, XWBIndex *xwbIndex);
- Audio::SeekableAudioStream *createXWBStream(Common::SeekableSubReadStream *stream, XWBEntry entry);
+ void indexXWBFile(const Common::String &filename, AudioIndex *audioIndex);
+ Audio::SeekableAudioStream *createSoundStream(Common::SeekableSubReadStream *stream, AudioEntry entry);
+ void indexFSBFile(const Common::String &filename, AudioIndex *audioIndex);
};
More information about the Scummvm-git-logs
mailing list