aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Snover2017-05-10 00:33:43 -0500
committerColin Snover2017-05-10 10:55:00 -0500
commitdd13c3be43b2566d7ee6449be7918a86428bb4da (patch)
tree2bb300049aeb79eb787a1af0631af35636b8c56b
parent53b0b82e8754e5e74dda2dedd9f7da726b068df7 (diff)
downloadscummvm-rg350-dd13c3be43b2566d7ee6449be7918a86428bb4da.tar.gz
scummvm-rg350-dd13c3be43b2566d7ee6449be7918a86428bb4da.tar.bz2
scummvm-rg350-dd13c3be43b2566d7ee6449be7918a86428bb4da.zip
SCI: Fix support for ScummVM compressed audio volumes
The runtime code for this had previously relied on hot patching volume file offsets at the moment that a resource was loaded, instead of correcting file offsets when reading audio maps. The code added for sanity checking audio volumes started to report warnings because the offsets being received were for the original uncompressed audio volume, which (naturally) is larger than the compressed audio volume. This commit also deduplicates code between addResource and updateResource, and tweaks a validation-related error message for improved clarity. Fixes Trac#9764.
-rw-r--r--engines/sci/resource.cpp22
-rw-r--r--engines/sci/resource_audio.cpp124
-rw-r--r--engines/sci/resource_intern.h24
3 files changed, 75 insertions, 95 deletions
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 91cceb5968..2f34f8f433 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -2013,20 +2013,7 @@ bool ResourceManager::validateResource(const ResourceId &resourceId, const Commo
void ResourceManager::addResource(ResourceId resId, ResourceSource *src, uint32 offset, uint32 size, const Common::String &sourceMapLocation) {
// Adding new resource only if it does not exist
if (_resMap.contains(resId) == false) {
- Common::SeekableReadStream *volumeFile = getVolumeFile(src);
- if (volumeFile == nullptr) {
- error("Could not open %s for reading", src->getLocationName().c_str());
- }
-
- if (validateResource(resId, sourceMapLocation, src->getLocationName(), offset, size, volumeFile->size())) {
- Resource *res = new Resource(this, resId);
- _resMap.setVal(resId, res);
- res->_source = src;
- res->_fileOffset = offset;
- res->_size = size;
- } else {
- _hasBadResources = true;
- }
+ updateResource(resId, src, offset, size, sourceMapLocation);
}
}
@@ -2048,6 +2035,13 @@ Resource *ResourceManager::updateResource(ResourceId resId, ResourceSource *src,
error("Could not open %s for reading", src->getLocationName().c_str());
}
+ AudioVolumeResourceSource *avSrc = dynamic_cast<AudioVolumeResourceSource *>(src);
+ if (avSrc != nullptr && !avSrc->relocateMapOffset(offset, size)) {
+ warning("Compressed volume %s does not contain a valid entry for %s (map offset %u)", src->getLocationName().c_str(), resId.toString().c_str(), offset);
+ _hasBadResources = true;
+ return res;
+ }
+
if (validateResource(resId, sourceMapLocation, src->getLocationName(), offset, size, volumeFile->size())) {
if (res == nullptr) {
res = new Resource(this, resId);
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index a5501cdb84..2cd157a631 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -36,7 +36,6 @@ AudioVolumeResourceSource::AudioVolumeResourceSource(ResourceManager *resMan, co
: VolumeResourceSource(name, map, volNum, kSourceAudioVolume) {
_audioCompressionType = 0;
- _audioCompressionOffsetMapping = NULL;
/*
* Check if this audio volume got compressed by our tool. If that is the
@@ -49,36 +48,37 @@ AudioVolumeResourceSource::AudioVolumeResourceSource(ResourceManager *resMan, co
return;
fileStream->seek(0, SEEK_SET);
- uint32 compressionType = fileStream->readUint32BE();
+ const uint32 compressionType = fileStream->readUint32BE();
switch (compressionType) {
case MKTAG('M','P','3',' '):
case MKTAG('O','G','G',' '):
case MKTAG('F','L','A','C'):
- // Detected a compressed audio volume
_audioCompressionType = compressionType;
- // Now read the whole offset mapping table for later usage
- int32 recordCount = fileStream->readUint32LE();
- if (!recordCount)
- error("compressed audio volume doesn't contain any entries");
- int32 *offsetMapping = new int32[(recordCount + 1) * 2];
- _audioCompressionOffsetMapping = offsetMapping;
- for (int recordNo = 0; recordNo < recordCount; recordNo++) {
- *offsetMapping++ = fileStream->readUint32LE();
- *offsetMapping++ = fileStream->readUint32LE();
+ const uint32 numEntries = fileStream->readUint32LE();
+ if (!numEntries) {
+ error("Compressed audio volume %s has no relocation table entries", name.c_str());
}
- // Put ending zero
- *offsetMapping++ = 0;
- *offsetMapping++ = fileStream->size();
+
+ CompressedTableEntry *lastEntry = nullptr;
+ for (uint i = 0; i < numEntries; ++i) {
+ CompressedTableEntry nextEntry;
+ const uint32 sourceOffset = fileStream->readUint32LE();
+ nextEntry.offset = fileStream->readUint32LE();
+ if (lastEntry != nullptr) {
+ lastEntry->size = nextEntry.offset - lastEntry->offset;
+ }
+
+ _compressedOffsets.setVal(sourceOffset, nextEntry);
+ lastEntry = &_compressedOffsets.getVal(sourceOffset);
+ }
+
+ lastEntry->size = fileStream->size() - lastEntry->offset;
}
if (_resourceFile)
delete fileStream;
}
-AudioVolumeResourceSource::~AudioVolumeResourceSource() {
- delete[] _audioCompressionOffsetMapping;
-}
-
bool Resource::loadFromWaveFile(Common::SeekableReadStream *file) {
byte *ptr = new byte[_size];
_data = ptr;
@@ -315,7 +315,7 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
Common::SeekableReadStream *fileStream = getVolumeFile(src);
if (!fileStream) {
- warning("Failed to open file stream for %s", mapResId.toString().c_str());
+ warning("Failed to open file stream for %s", src->getLocationName().c_str());
return SCI_ERROR_NO_RESOURCE_FILES_FOUND;
}
@@ -383,17 +383,21 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
offset = ptr.getUint32LE();
ptr += 4;
- // The size is not stored in the map and the entries have no order.
- // We need to dig into the audio resource in the volume to get the size.
- stream->seek(offset + 1);
- byte headerSize = stream->readByte();
- if (headerSize != 11 && headerSize != 12) {
- error("Unexpected header size in %s: should be 11 or 12, got %d", audioResId.toString().c_str(), headerSize);
- }
-
- stream->skip(7);
- uint32 size = stream->readUint32LE() + headerSize + 2;
+ uint32 size;
+ if (src->getAudioCompressionType() == 0) {
+ // The size is not stored in the map and the entries have no order.
+ // We need to dig into the audio resource in the volume to get the size.
+ stream->seek(offset + 1);
+ byte headerSize = stream->readByte();
+ if (headerSize != 11 && headerSize != 12) {
+ error("Unexpected header size in %s: should be 11 or 12, got %d", audioResId.toString().c_str(), headerSize);
+ }
+ stream->skip(7);
+ size = stream->readUint32LE() + headerSize + 2;
+ } else {
+ size = 0;
+ }
addResource(audioResId, src, offset, size, map->getLocationName());
}
} else {
@@ -406,7 +410,7 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
while (ptr != mapRes->cend()) {
uint32 n = ptr.getUint32BE();
- int syncSize = 0;
+ uint32 syncSize = 0;
ptr += 4;
if (n == 0xffffffff)
@@ -436,7 +440,7 @@ int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
if (g_sci->getGameId() == GID_KQ6 && (n & 0x40)) {
// This seems to define the size of raw lipsync data (at least
// in KQ6 CD Windows).
- int kq6HiresSyncSize = ptr.getUint16LE();
+ uint32 kq6HiresSyncSize = ptr.getUint16LE();
ptr += 2;
if (kq6HiresSyncSize > 0) {
@@ -924,52 +928,16 @@ void AudioVolumeResourceSource::loadResource(ResourceManager *resMan, Resource *
if (!fileStream)
return;
- if (_audioCompressionType) {
- // this file is compressed, so lookup our offset in the offset-translation table and get the new offset
- // also calculate the compressed size by using the next offset
- int32 *mappingTable = _audioCompressionOffsetMapping;
- int32 compressedOffset = 0;
-
- do {
- if (*mappingTable == res->_fileOffset) {
- mappingTable++;
- compressedOffset = *mappingTable;
- // Go to next compressed offset and use that to calculate size of compressed sample
- switch (res->getType()) {
- case kResourceTypeSync:
- case kResourceTypeSync36:
- case kResourceTypeRave:
- // we should already have a (valid) size
- break;
- default:
- mappingTable += 2;
- res->_size = *mappingTable - compressedOffset;
- }
- break;
- }
- mappingTable += 2;
- } while (*mappingTable);
-
- if (!compressedOffset)
- error("could not translate offset to compressed offset in audio volume");
- fileStream->seek(compressedOffset, SEEK_SET);
-
- switch (res->getType()) {
- case kResourceTypeAudio:
- case kResourceTypeAudio36:
- // Directly read the stream, compressed audio wont have resource type id and header size for SCI1.1
- res->loadFromAudioVolumeSCI1(fileStream);
- if (_resourceFile)
- delete fileStream;
- return;
- default:
- break;
- }
- } else {
- // original file, directly seek to given offset and get SCI1/SCI1.1 audio resource
- fileStream->seek(res->_fileOffset, SEEK_SET);
- }
- if (getSciVersion() < SCI_VERSION_1_1)
+ fileStream->seek(res->_fileOffset, SEEK_SET);
+
+ // For compressed audio, using loadFromAudioVolumeSCI1 is a hack to bypass
+ // the resource type checking in loadFromAudioVolumeSCI11 (since
+ // loadFromAudioVolumeSCI1 does nothing more than read raw data)
+ if (_audioCompressionType != 0 &&
+ (res->getType() == kResourceTypeAudio ||
+ res->getType() == kResourceTypeAudio36)) {
+ res->loadFromAudioVolumeSCI1(fileStream);
+ } else if (getSciVersion() < SCI_VERSION_1_1)
res->loadFromAudioVolumeSCI1(fileStream);
else
res->loadFromAudioVolumeSCI11(fileStream);
diff --git a/engines/sci/resource_intern.h b/engines/sci/resource_intern.h
index f198eddeb3..ea3e05661f 100644
--- a/engines/sci/resource_intern.h
+++ b/engines/sci/resource_intern.h
@@ -144,17 +144,35 @@ public:
class AudioVolumeResourceSource : public VolumeResourceSource {
protected:
+ struct CompressedTableEntry {
+ uint32 offset;
+ uint32 size;
+ };
+
uint32 _audioCompressionType;
- int32 *_audioCompressionOffsetMapping;
+ Common::HashMap<uint32, CompressedTableEntry> _compressedOffsets;
public:
AudioVolumeResourceSource(ResourceManager *resMan, const Common::String &name, ResourceSource *map, int volNum);
- virtual ~AudioVolumeResourceSource();
-
virtual void loadResource(ResourceManager *resMan, Resource *res);
virtual uint32 getAudioCompressionType() const;
+
+ bool relocateMapOffset(uint32 &offset, uint32 &size) const {
+ if (_audioCompressionType == 0) {
+ return true;
+ }
+
+ if (!_compressedOffsets.contains(offset)) {
+ return false;
+ }
+
+ const CompressedTableEntry &entry = _compressedOffsets.getVal(offset);
+ offset = entry.offset;
+ size = entry.size;
+ return true;
+ }
};
class ExtAudioMapResourceSource : public ResourceSource {