From 780899fb0e0be87bd87386fc5f6aeffb4bc30c42 Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Tue, 26 May 2009 10:02:25 +0000 Subject: Rewrote the Audio stream parser. The introduction of KQ6 should work more correctly now (apart from Cassima's speech) svn-id: r40904 --- engines/sci/engine/ksound.cpp | 25 +--- engines/sci/resource.cpp | 261 ++++++++++++++++++++++++------------------ engines/sci/resource.h | 18 ++- 3 files changed, 161 insertions(+), 143 deletions(-) (limited to 'engines') diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp index 1f7ece33c3..b6ac628a6b 100644 --- a/engines/sci/engine/ksound.cpp +++ b/engines/sci/engine/ksound.cpp @@ -1001,37 +1001,18 @@ reg_t kDoAudio(EngineState *s, int funct_nr, int argc, reg_t *argv) { int sampleLen = 0; if (!s->sound.audioResource) - s->sound.audioResource = new AudioResource(); + s->sound.audioResource = new AudioResource(s->resmgr, s->version); switch (UKPV(0)) { case kSci1AudioWPlay: case kSci1AudioPlay: { s->sound.audioResource->stop(); - Audio::AudioStream *audioStream = 0; - - // Try to load from an external patch file first - Sci::Resource* audioRes = s->resmgr->findResource(kResourceTypeAudio, UKPV(1), 1); - - if (s->_gameName == "KQ5") { - if (audioRes) { - audioStream = s->sound.audioResource->getAudioStreamKQ5CD(audioRes, &sampleLen); - } else { - // No patch file found, read it from the audio volume - audioStream = s->sound.audioResource->getAudioStreamKQ5CD(UKPV(1), &sampleLen); - } - } else if (s->_gameName == "Kq6") { - if (audioRes) { - audioStream = s->sound.audioResource->getAudioStreamKQ6Floppy(audioRes, &sampleLen); - } else { - // No patch file found, read it from the audio volume - audioStream = s->sound.audioResource->getAudioStreamKQ6Floppy(UKPV(1), &sampleLen); - } - } + Audio::AudioStream *audioStream = + audioStream = s->sound.audioResource->getAudioStream(UKPV(1), &sampleLen); if (audioStream) mixer->playInputStream(Audio::Mixer::kSpeechSoundType, s->sound.audioResource->getAudioHandle(), audioStream); - } return make_reg(0, sampleLen); // return sample length in ticks case kSci1AudioStop: diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 73f4bf7bd2..63c3ee4e8c 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -56,6 +56,13 @@ const char *sci_version_types[] = { const int sci_max_resource_nr[] = {65536, 1000, 2048, 2048, 2048, 65536, 65536, 65536}; +enum SolFlags { + kSolFlagCompressed = 1 << 0, + kSolFlagUnknown = 1 << 1, + kSolFlag16Bit = 1 << 2, + kSolFlagIsSigned = 1 << 3 +}; + const char *sci_error_types[] = { "No error", "I/O error", @@ -1168,19 +1175,28 @@ void ResourceSync::stopSync() { } -AudioResource::AudioResource() { +AudioResource::AudioResource(ResourceManager *resMgr, int sciVersion) { + _resMgr = resMgr; + _sciVersion = sciVersion; _audioRate = 0; _lang = 0; - _audioMap = 0; + _audioMapSCI1 = 0; + _audioMapSCI11 = 0; } -AudioResource::~AudioResource() { - delete[] _audioMap; - _audioMap = 0; +AudioResource::~AudioResource() { + if (_sciVersion < SCI_VERSION_1_1) { + if (_audioMapSCI1) { + delete[] _audioMapSCI1; + _audioMapSCI1 = 0; + } + } else { + _resMgr->unlockResource(_audioMapSCI11, 65535, kResourceTypeMap); + } } +// Used in SCI1 games void AudioResource::setAudioLang(int16 lang) { - // TODO: CD Audio (used for example in the end credits of KQ6CD) if (lang != -1) { _lang = lang; @@ -1190,12 +1206,12 @@ void AudioResource::setAudioLang(int16 lang) { Common::File* audioMapFile = new Common::File(); if (audioMapFile->open(filename)) { // The audio map is freed in the destructor - _audioMap = new byte[audioMapFile->size()]; - audioMapFile->read(_audioMap, audioMapFile->size()); + _audioMapSCI1 = new byte[audioMapFile->size()]; + audioMapFile->read(_audioMapSCI1, audioMapFile->size()); audioMapFile->close(); delete audioMapFile; } else { - _audioMap = 0; + _audioMapSCI1 = 0; } } } @@ -1208,7 +1224,7 @@ int AudioResource::getAudioPosition() { } } -bool AudioResource::findAudEntryKQ5CD(uint16 audioNumber, byte& volume, uint32& offset, uint32& size) { +bool AudioResource::findAudEntrySCI1(uint16 audioNumber, byte &volume, uint32 &offset, uint32 &size) { // AUDIO00X.MAP contains 10-byte entries: // w nEntry // dw offset+volume (as in resource.map) @@ -1217,10 +1233,10 @@ bool AudioResource::findAudEntryKQ5CD(uint16 audioNumber, byte& volume, uint32& uint16 n; uint32 off; - if (_audioMap == 0) + if (_audioMapSCI1 == 0) return false; - byte *ptr = _audioMap; + byte *ptr = _audioMapSCI1; while ((n = READ_UINT16(ptr)) != 0xFFFF) { if (n == audioNumber) { off = READ_LE_UINT32(ptr + 2); @@ -1235,131 +1251,154 @@ bool AudioResource::findAudEntryKQ5CD(uint16 audioNumber, byte& volume, uint32& return false; } -Audio::AudioStream* AudioResource::getAudioStreamKQ5CD(uint16 audioNumber, int* sampleLen) { - Audio::AudioStream *audioStream = 0; - byte volume; - uint32 offset; - uint32 size; +bool AudioResource::findAudEntrySCI11(uint16 audioNumber, uint32 &offset) { + // 65535.MAP contains 6-byte entries: + // w nEntry + // dw offset + // ending with 6 0xFFs + uint16 n; + offset = 0; + uint16 cur = 0; - if (findAudEntryKQ5CD(audioNumber, volume, offset, size)) { - uint32 start = offset * 1000 / _audioRate; - uint32 duration = size * 1000 / _audioRate; + if (!_audioMapSCI11) + _audioMapSCI11 = _resMgr->findResource(kResourceTypeMap, 65535, 1); - char filename[40]; - sprintf(filename, "AUDIO%03d.%03d", _lang, volume); + byte *ptr = _audioMapSCI11->data; + while (cur < _audioMapSCI11->size) { + n = READ_UINT16(ptr); + offset = READ_UINT32(ptr + 2); - // Try to load compressed - audioStream = Audio::AudioStream::openStreamFile(filename, start, duration); - if (!audioStream) { - // Compressed file load failed, try to load original raw data - byte *soundbuff = (byte *)malloc(size); - Common::File* audioFile = new Common::File(); - if (audioFile->open(filename)) { - audioFile->seek(offset); - audioFile->read(soundbuff, size); - audioFile->close(); - delete audioFile; + // Check if we reached the end + if (n == 0xFF && offset == 0xFFFF) + return false; - audioStream = Audio::makeLinearInputStream(soundbuff, size, _audioRate, - Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED, 0, 0); - } - } + if (n == audioNumber) + return true; - *sampleLen = size * 60 / _audioRate; + ptr += 6; + cur += 6; } - return audioStream; + return false; } -bool AudioResource::findAudEntryKQ6Floppy(uint16 audioNumber, uint32& offset) { - // 65535.MAP contains 8-byte entries: - // w nEntry - // dw offset - // w unknown - uint16 n; - offset = 0; - int cur = 0; - int fileSize = 0; - - // Load audio map - Common::File* audioMapFile = new Common::File(); - if (audioMapFile->open("65535.map")) { - _audioMap = new byte[audioMapFile->size()]; - audioMapFile->read(_audioMap, audioMapFile->size()); - fileSize = audioMapFile->size(); - audioMapFile->close(); - delete audioMapFile; - } else { - _audioMap = 0; - return false; +// Sierra SOL audio file reader +// Check here for more info: http://wiki.multimedia.cx/index.php?title=Sierra_Audio +// TODO: improve this for later versions of the format, as this currently only reads +// raw PCM encoded files (not ADPCM compressed ones) +byte* readSOLAudio(Common::SeekableReadStream *audioStream, uint32 *size, uint16 *audioRate, byte *flags) { + byte audioFlags; + byte version = audioStream->readByte(); + + if (version != 0x8D) { + warning("SOL audio version is newer than the expected one"); + return NULL; } - byte *ptr = _audioMap; - while (cur < fileSize) { - n = READ_UINT16(ptr); - if (n == audioNumber) { - offset = READ_LE_UINT32(ptr + 2); - delete[] _audioMap; - _audioMap = 0; - return true; - } - ptr += 8; - cur += 8; + audioStream->readByte(); // skip header size (1 byte), usually 0B (11 bytes) + audioStream->readUint32LE(); // skip "SOL" + 0 (4 bytes) + *audioRate = audioStream->readUint16LE(); + audioFlags = audioStream->readByte(); + + if (audioFlags & kSolFlagCompressed) { + warning("ADPCM compressed SOL files are not supported yet"); + return NULL; } - delete[] _audioMap; - _audioMap = 0; - return false; + // Convert the SOL stream flags to our own format + *flags = 0; + if (audioFlags & kSolFlag16Bit) + *flags |= Audio::Mixer::FLAG_16BITS; + if (!(audioFlags & kSolFlagIsSigned)) + *flags |= Audio::Mixer::FLAG_UNSIGNED; + + *size = audioStream->readUint16LE(); + + // We assume that the sound data is raw PCM + byte *buffer = new byte[*size]; + audioStream->read(buffer, *size); + return buffer; } -Audio::AudioStream* AudioResource::getAudioStreamKQ6Floppy(uint16 audioNumber, int* sampleLen) { +Audio::AudioStream* AudioResource::getAudioStream(uint16 audioNumber, int *sampleLen) { Audio::AudioStream *audioStream = 0; + byte volume; uint32 offset; uint32 size; + bool found = false; + byte *data = 0; + char filename[40]; + byte flags; + + // Try to load from an external patch file first + Sci::Resource* audioRes = _resMgr->findResource(kResourceTypeAudio, audioNumber, 1); + if (audioRes) { + if (_sciVersion < SCI_VERSION_1_1) { + size = audioRes->size; + data = audioRes->data; + } else { + // TODO: this is disabled for now, as for some reason the resource manager returns + // only the data chunk of the audio file, and not its headers + /* + Common::MemoryReadStream *memStream = + new Common::MemoryReadStream(audioRes->data, audioRes->size, true); + data = readSOLAudio(memStream, &size, &_audioRate, &flags); + delete memStream; + */ + } - if (findAudEntryKQ6Floppy(audioNumber, offset)) { - Common::File* audioFile = new Common::File(); - if (audioFile->open("resource.aud")) { - audioFile->seek(offset); - // Read audio file info - // Audio files are actually Sierra Audio files. - // Check here for more info: http://wiki.multimedia.cx/index.php?title=Sierra_Audio - audioFile->readByte(); // skip version - audioFile->readByte(); // skip header size - audioFile->readUint32LE(); // skip "SOL" + 0 - _audioRate = audioFile->readUint16LE(); - audioFile->readByte(); // skip flags - size = audioFile->readUint16LE(); - byte *soundbuff = (byte *)malloc(size); - audioFile->read(soundbuff, size); - audioFile->close(); - delete audioFile; - - audioStream = Audio::makeLinearInputStream(soundbuff, size, _audioRate, - Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED, 0, 0); + if (data) { + *sampleLen = size * 60 / _audioRate; + return Audio::makeLinearInputStream(data, size, _audioRate, + flags | Audio::Mixer::FLAG_AUTOFREE, 0, 0); } + } - *sampleLen = size * 60 / _audioRate; + // Patch file not found, load it from the audio file + if (_sciVersion < SCI_VERSION_1_1) { + found = findAudEntrySCI1(audioNumber, volume, offset, size); + sprintf(filename, "AUDIO%03d.%03d", _lang, volume); + } else { + found = findAudEntrySCI11(audioNumber, offset); + strcpy(filename, "RESOURCE.AUD"); } - return audioStream; -} + if (found) { + if (_sciVersion < SCI_VERSION_1_1) { + uint32 start = offset * 1000 / _audioRate; + uint32 duration = size * 1000 / _audioRate; + + // Try to load compressed + audioStream = Audio::AudioStream::openStreamFile(filename, start, duration); + } -Audio::AudioStream* AudioResource::getAudioStreamKQ5CD(Resource* audioRes, int* sampleLen) { - *sampleLen = audioRes->size * 60 / _audioRate; - return Audio::makeLinearInputStream(audioRes->data, audioRes->size, _audioRate, Audio::Mixer::FLAG_UNSIGNED, 0, 0); -} + if (!audioStream) { + // Compressed file load failed, try to load original raw data + Common::File* audioFile = new Common::File(); + if (audioFile->open(filename)) { + audioFile->seek(offset); -Audio::AudioStream* AudioResource::getAudioStreamKQ6Floppy(Resource* audioRes, int* sampleLen) { - // Read audio file info - // Audio files are actually Sierra Audio files. - // Check here for more info: http://wiki.multimedia.cx/index.php?title=Sierra_Audio - _audioRate = READ_UINT16(audioRes->data + 6); - uint32 size = READ_UINT16(audioRes->data + 9); + if (_sciVersion < SCI_VERSION_1_1) { + data = (byte *)malloc(size); + audioFile->read(data, size); + } else { + data = readSOLAudio(audioFile, &size, &_audioRate, &flags); + } - *sampleLen = size * 60 / _audioRate; - return Audio::makeLinearInputStream(audioRes->data + 11, size, _audioRate, Audio::Mixer::FLAG_UNSIGNED, 0, 0); -} + audioFile->close(); + delete audioFile; + if (data) { + audioStream = Audio::makeLinearInputStream(data, size, _audioRate, + flags | Audio::Mixer::FLAG_AUTOFREE, 0, 0); + } + } + } + + *sampleLen = size * 60 / _audioRate; + } + + return audioStream; +} } // End of namespace Sci diff --git a/engines/sci/resource.h b/engines/sci/resource.h index 6f527645a7..60f3ea1d26 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -304,7 +304,7 @@ protected: class AudioResource { public: - AudioResource(); + AudioResource(ResourceManager *resMgr, int sciVersion); ~AudioResource(); void setAudioRate(uint16 audioRate) { _audioRate = audioRate; } @@ -313,11 +313,7 @@ public: Audio::SoundHandle* getAudioHandle() { return &_audioHandle; } int getAudioPosition(); - // TODO: these need better names - Audio::AudioStream* getAudioStreamKQ5CD(uint16 audioNumber, int* sampleLen); - Audio::AudioStream* getAudioStreamKQ5CD(Resource* audioRes, int* sampleLen); - Audio::AudioStream* getAudioStreamKQ6Floppy(uint16 audioNumber, int* sampleLen); - Audio::AudioStream* getAudioStreamKQ6Floppy(Resource* audioRes, int* sampleLen); + Audio::AudioStream* getAudioStream(uint16 audioNumber, int *sampleLen); void stop() { g_system->getMixer()->stopHandle(_audioHandle); } void pause() { g_system->getMixer()->pauseHandle(_audioHandle, true); } @@ -327,11 +323,13 @@ private: Audio::SoundHandle _audioHandle; uint16 _audioRate; int16 _lang; - byte *_audioMap; + byte *_audioMapSCI1; + Resource *_audioMapSCI11; + ResourceManager *_resMgr; + int _sciVersion; - // TODO: these need better names - bool findAudEntryKQ5CD(uint16 audioNumber, byte& volume, uint32& offset, uint32& size); - bool findAudEntryKQ6Floppy(uint16 audioNumber, uint32& offset); + bool findAudEntrySCI1(uint16 audioNumber, byte &volume, uint32 &offset, uint32 &size); + bool findAudEntrySCI11(uint16 audioNumber, uint32 &offset); }; } // End of namespace Sci -- cgit v1.2.3