aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/resource_audio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/resource_audio.cpp')
-rw-r--r--engines/sci/resource_audio.cpp336
1 files changed, 204 insertions, 132 deletions
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index 57efbdcb38..a25505fe47 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -25,20 +25,30 @@
// Resource library
+#include "common/archive.h"
#include "common/file.h"
#include "sci/resource.h"
+#include "sci/resource_intern.h"
#include "sci/util.h"
namespace Sci {
-void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) {
- Common::SeekableReadStream *fileStream = getVolumeFile(source);
+AudioVolumeResourceSource::AudioVolumeResourceSource(ResourceManager *resMan, const Common::String &name, ResourceSource *map, int volNum)
+ : VolumeResourceSource(name, map, volNum, kSourceAudioVolume) {
- if (!fileStream) {
- warning("Failed to open %s", source->location_name.c_str());
+ _audioCompressionType = 0;
+ _audioCompressionOffsetMapping = NULL;
+
+ /*
+ * Check if this audio volume got compressed by our tool. If that is the
+ * case, set _audioCompressionType and read in the offset translation
+ * table for later usage.
+ */
+
+ Common::SeekableReadStream *fileStream = getVolumeFile(resMan, 0);
+ if (!fileStream)
return;
- }
fileStream->seek(0, SEEK_SET);
uint32 compressionType = fileStream->readUint32BE();
@@ -47,13 +57,13 @@ void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) {
case MKID_BE('OGG '):
case MKID_BE('FLAC'):
// Detected a compressed audio volume
- source->audioCompressionType = compressionType;
+ _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];
- source->audioCompressionOffsetMapping = offsetMapping;
+ _audioCompressionOffsetMapping = offsetMapping;
for (int recordNo = 0; recordNo < recordCount; recordNo++) {
*offsetMapping++ = fileStream->readUint32LE();
*offsetMapping++ = fileStream->readUint32LE();
@@ -63,105 +73,124 @@ void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) {
*offsetMapping++ = fileStream->size();
}
- if (source->resourceFile)
+ if (_resourceFile)
delete fileStream;
}
-bool ResourceManager::loadFromWaveFile(Resource *res, Common::SeekableReadStream *file) {
- res->data = new byte[res->size];
+bool Resource::loadFromWaveFile(Common::SeekableReadStream *file) {
+ data = new byte[size];
- uint32 really_read = file->read(res->data, res->size);
- if (really_read != res->size)
- error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size);
+ uint32 really_read = file->read(data, size);
+ if (really_read != size)
+ error("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size);
- res->_status = kResStatusAllocated;
+ _status = kResStatusAllocated;
return true;
}
-bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::SeekableReadStream *file) {
+bool Resource::loadFromAudioVolumeSCI11(Common::SeekableReadStream *file) {
// Check for WAVE files here
uint32 riffTag = file->readUint32BE();
if (riffTag == MKID_BE('RIFF')) {
- res->_headerSize = 0;
- res->size = file->readUint32LE();
+ _headerSize = 0;
+ size = file->readUint32LE() + 8;
file->seek(-8, SEEK_CUR);
- return loadFromWaveFile(res, file);
+ return loadFromWaveFile(file);
}
file->seek(-4, SEEK_CUR);
- ResourceType type = (ResourceType)(file->readByte() & 0x7f);
- if (((res->_id.type == kResourceTypeAudio || res->_id.type == kResourceTypeAudio36) && (type != kResourceTypeAudio))
- || ((res->_id.type == kResourceTypeSync || res->_id.type == kResourceTypeSync36) && (type != kResourceTypeSync))) {
- warning("Resource type mismatch loading %s", res->_id.toString().c_str());
- res->unalloc();
+ ResourceType type = _resMan->convertResType(file->readByte());
+ if (((getType() == kResourceTypeAudio || getType() == kResourceTypeAudio36) && (type != kResourceTypeAudio))
+ || ((getType() == kResourceTypeSync || getType() == kResourceTypeSync36) && (type != kResourceTypeSync))) {
+ warning("Resource type mismatch loading %s", _id.toString().c_str());
+ unalloc();
return false;
}
- res->_headerSize = file->readByte();
+ _headerSize = file->readByte();
if (type == kResourceTypeAudio) {
- if (res->_headerSize != 11 && res->_headerSize != 12) {
+ if (_headerSize != 7 && _headerSize != 11 && _headerSize != 12) {
warning("Unsupported audio header");
- res->unalloc();
+ unalloc();
return false;
}
- // Load sample size
- file->seek(7, SEEK_CUR);
- res->size = file->readUint32LE();
- // Adjust offset to point at the header data again
- file->seek(-11, SEEK_CUR);
+ if (_headerSize != 7) { // Size is defined already from the map
+ // Load sample size
+ file->seek(7, SEEK_CUR);
+ size = file->readUint32LE();
+ // Adjust offset to point at the header data again
+ file->seek(-11, SEEK_CUR);
+ }
}
- return loadPatch(res, file);
+ return loadPatch(file);
}
-bool ResourceManager::loadFromAudioVolumeSCI1(Resource *res, Common::SeekableReadStream *file) {
- res->data = new byte[res->size];
+bool Resource::loadFromAudioVolumeSCI1(Common::SeekableReadStream *file) {
+ data = new byte[size];
- if (res->data == NULL) {
- error("Can't allocate %d bytes needed for loading %s", res->size, res->_id.toString().c_str());
+ if (data == NULL) {
+ error("Can't allocate %d bytes needed for loading %s", size, _id.toString().c_str());
}
- unsigned int really_read = file->read(res->data, res->size);
- if (really_read != res->size)
- warning("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size);
+ unsigned int really_read = file->read(data, size);
+ if (really_read != size)
+ warning("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size);
- res->_status = kResStatusAllocated;
+ _status = kResStatusAllocated;
return true;
}
-void ResourceManager::addNewGMPatch(const Common::String &gameId) {
+void ResourceManager::addNewGMPatch(SciGameId gameId) {
Common::String gmPatchFile;
- if (gameId == "ecoquest")
+ switch (gameId) {
+ case GID_ECOQUEST:
gmPatchFile = "ECO1GM.PAT";
- else if (gameId == "hoyle3")
- gmPatchFile = "HOY3GM.PAT";
- else if (gameId == "hoyle3")
+ break;
+ case GID_HOYLE3:
gmPatchFile = "HOY3GM.PAT";
- else if (gameId == "lsl1sci")
+ break;
+ case GID_LSL1:
gmPatchFile = "LL1_GM.PAT";
- else if (gameId == "lsl5")
+ break;
+ case GID_LSL5:
gmPatchFile = "LL5_GM.PAT";
- else if (gameId == "longbow")
+ break;
+ case GID_LONGBOW:
gmPatchFile = "ROBNGM.PAT";
- else if (gameId == "sq1sci")
+ break;
+ case GID_SQ1:
gmPatchFile = "SQ1_GM.PAT";
- else if (gameId == "sq4")
+ break;
+ case GID_SQ4:
gmPatchFile = "SQ4_GM.PAT";
- else if (gameId == "fairytales")
+ break;
+ case GID_FAIRYTALES:
gmPatchFile = "TALEGM.PAT";
+ break;
+ default:
+ break;
+ }
if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) {
- ResourceSource *psrcPatch = new ResourceSource;
- psrcPatch->source_type = kSourcePatch;
- psrcPatch->resourceFile = 0;
- psrcPatch->location_name = gmPatchFile;
+ ResourceSource *psrcPatch = new PatchResourceSource(gmPatchFile);
processPatch(psrcPatch, kResourceTypePatch, 4);
}
}
+void ResourceManager::processWavePatch(ResourceId resourceId, Common::String name) {
+ ResourceSource *resSrc = new WaveResourceSource(name);
+ Common::File file;
+ file.open(name);
+
+ updateResource(resourceId, resSrc, file.size());
+
+ debugC(1, kDebugLevelResMan, "Patching %s - OK", name.c_str());
+}
+
void ResourceManager::readWaveAudioPatches() {
// Here we do check for SCI1.1+ so we can patch wav files in as audio resources
Common::ArchiveMemberList files;
@@ -170,39 +199,8 @@ void ResourceManager::readWaveAudioPatches() {
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
Common::String name = (*x)->getName();
- if (isdigit(name[0])) {
- int number = atoi(name.c_str());
- ResourceSource *psrcPatch = new ResourceSource;
- psrcPatch->source_type = kSourceWave;
- psrcPatch->resourceFile = 0;
- psrcPatch->location_name = name;
- psrcPatch->volume_number = 0;
- psrcPatch->audioCompressionType = 0;
-
- ResourceId resId = ResourceId(kResourceTypeAudio, number);
-
- Resource *newrsc = NULL;
-
- // Prepare destination, if neccessary
- if (_resMap.contains(resId) == false) {
- newrsc = new Resource;
- _resMap.setVal(resId, newrsc);
- } else
- newrsc = _resMap.getVal(resId);
-
- // Get the size of the file
- Common::SeekableReadStream *stream = (*x)->createReadStream();
- uint32 fileSize = stream->size();
- delete stream;
-
- // Overwrite everything, because we're patching
- newrsc->_id = resId;
- newrsc->_status = kResStatusNoMalloc;
- newrsc->_source = psrcPatch;
- newrsc->size = fileSize;
- newrsc->_headerSize = 0;
- debugC(1, kDebugLevelResMan, "Patching %s - OK", psrcPatch->location_name.c_str());
- }
+ if (isdigit(name[0]))
+ processWavePatch(ResourceId(kResourceTypeAudio, atoi(name.c_str())), name);
}
}
@@ -211,7 +209,7 @@ void ResourceManager::removeAudioResource(ResourceId resId) {
if (_resMap.contains(resId)) {
Resource *res = _resMap.getVal(resId);
- if (res->_source->source_type == kSourceAudioVolume) {
+ if (res->_source->getSourceType() == kSourceAudioVolume) {
if (res->_status == kResStatusLocked) {
warning("Failed to remove resource %s (still in use)", resId.toString().c_str());
} else {
@@ -237,6 +235,20 @@ void ResourceManager::removeAudioResource(ResourceId resId) {
// w nEntry
// tb offset (cumulative)
+// QFG3 Demo 0.MAP structure:
+// =========
+// 10-byte entries:
+// w nEntry
+// dw offset
+// dw size
+
+// LB2 Floppy/Mother Goose SCI1.1 0.MAP structure:
+// =========
+// 8-byte entries:
+// w nEntry
+// w 0xffff
+// dw offset
+
// Early SCI1.1 MAP structure:
// ===============
// 10-byte entries:
@@ -261,27 +273,31 @@ void ResourceManager::removeAudioResource(ResourceId resId) {
// w syncAscSize (iff seq has bit 6 set)
int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
- bool isEarly = true;
uint32 offset = 0;
- Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->volume_number), false);
+ Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->_volumeNumber), false);
if (!mapRes) {
- warning("Failed to open %i.MAP", map->volume_number);
+ warning("Failed to open %i.MAP", map->_volumeNumber);
return SCI_ERROR_RESMAP_NOT_FOUND;
}
- ResourceSource *src = getVolume(map, 0);
+ ResourceSource *src = findVolume(map, 0);
if (!src)
return SCI_ERROR_NO_RESOURCE_FILES_FOUND;
byte *ptr = mapRes->data;
- if (map->volume_number == 65535) {
- // Heuristic to detect late SCI1.1 map format
- if ((mapRes->size >= 6) && (ptr[mapRes->size - 6] != 0xff))
- isEarly = false;
+ // Heuristic to detect entry size
+ uint32 entrySize = 0;
+ for (int i = mapRes->size - 1; i >= 0; --i) {
+ if (ptr[i] == 0xff)
+ entrySize++;
+ else
+ break;
+ }
+ if (map->_volumeNumber == 65535) {
while (ptr < mapRes->data + mapRes->size) {
uint16 n = READ_LE_UINT16(ptr);
ptr += 2;
@@ -289,7 +305,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
if (n == 0xffff)
break;
- if (isEarly) {
+ if (entrySize == 6) {
offset = READ_LE_UINT32(ptr);
ptr += 4;
} else {
@@ -299,10 +315,50 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
addResource(ResourceId(kResourceTypeAudio, n), src, offset);
}
+ } else if (map->_volumeNumber == 0 && entrySize == 10 && ptr[3] == 0) {
+ // QFG3 demo format
+ // ptr[3] would be 'seq' in the normal format and cannot possibly be 0
+ while (ptr < mapRes->data + mapRes->size) {
+ uint16 n = READ_BE_UINT16(ptr);
+ ptr += 2;
+
+ if (n == 0xffff)
+ break;
+
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ uint32 size = READ_LE_UINT32(ptr);
+ ptr += 4;
+
+ addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
+ }
+ } else if (map->_volumeNumber == 0 && entrySize == 8 && READ_LE_UINT16(ptr + 2) == 0xffff) {
+ // LB2 Floppy/Mother Goose SCI1.1 format
+ Common::SeekableReadStream *stream = getVolumeFile(src);
+
+ while (ptr < mapRes->data + mapRes->size) {
+ uint16 n = READ_LE_UINT16(ptr);
+ ptr += 4;
+
+ if (n == 0xffff)
+ break;
+
+ offset = READ_LE_UINT32(ptr);
+ 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();
+ assert(headerSize == 11 || headerSize == 12);
+
+ stream->skip(5);
+ uint32 size = stream->readUint32LE() + headerSize + 2;
+
+ addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
+ }
} else {
- // Heuristic to detect late SCI1.1 map format
- if ((mapRes->size >= 11) && (ptr[mapRes->size - 11] == 0xff))
- isEarly = false;
+ bool isEarly = (entrySize != 11);
if (!isEarly) {
offset = READ_LE_UINT32(ptr);
@@ -330,15 +386,17 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
ptr += 2;
if (syncSize > 0)
- addResource(ResourceId(kResourceTypeSync36, map->volume_number, n & 0xffffff3f), src, offset, syncSize);
+ addResource(ResourceId(kResourceTypeSync36, map->_volumeNumber, n & 0xffffff3f), src, offset, syncSize);
}
if (n & 0x40) {
+ // This seems to define the size of raw lipsync data (at least
+ // in kq6), may also just be general appended data.
syncSize += READ_LE_UINT16(ptr);
ptr += 2;
}
- addResource(ResourceId(kResourceTypeAudio36, map->volume_number, n & 0xffffff3f), src, offset + syncSize);
+ addResource(ResourceId(kResourceTypeAudio36, map->_volumeNumber, n & 0xffffff3f), src, offset + syncSize);
}
}
@@ -358,7 +416,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
Common::File file;
- if (!file.open(map->location_name))
+ if (!file.open(map->getLocationName()))
return SCI_ERROR_RESMAP_NOT_FOUND;
bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio;
@@ -370,7 +428,7 @@ int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
uint32 size = file.readUint32LE();
if (file.eos() || file.err()) {
- warning("Error while reading %s", map->location_name.c_str());
+ warning("Error while reading %s", map->getLocationName().c_str());
return SCI_ERROR_RESMAP_NOT_FOUND;
}
@@ -388,7 +446,7 @@ int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
offset &= 0x0fffffff; // least significant 28 bits
}
- ResourceSource *src = getVolume(map, volume_nr);
+ ResourceSource *src = findVolume(map, volume_nr);
if (src) {
if (unload)
@@ -405,7 +463,7 @@ int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
void ResourceManager::setAudioLanguage(int language) {
if (_audioMapSCI1) {
- if (_audioMapSCI1->volume_number == language) {
+ if (_audioMapSCI1->_volumeNumber == language) {
// This language is already loaded
return;
}
@@ -417,7 +475,7 @@ void ResourceManager::setAudioLanguage(int language) {
Common::List<ResourceSource *>::iterator it = _sources.begin();
while (it != _sources.end()) {
ResourceSource *src = *it;
- if (src->associated_map == _audioMapSCI1) {
+ if (src->findVolume(_audioMapSCI1, src->_volumeNumber)) {
it = _sources.erase(it);
delete src;
} else {
@@ -441,7 +499,7 @@ void ResourceManager::setAudioLanguage(int language) {
return;
}
- _audioMapSCI1 = addSource(NULL, kSourceExtAudioMap, fullname.c_str(), language);
+ _audioMapSCI1 = addSource(new ExtAudioMapResourceSource(fullname, language));
// Search for audio volumes for this language and add them to the source list
Common::ArchiveMemberList files;
@@ -451,18 +509,18 @@ void ResourceManager::setAudioLanguage(int language) {
const char *dot = strrchr(name.c_str(), '.');
int number = atoi(dot + 1);
- addSource(_audioMapSCI1, kSourceAudioVolume, name.c_str(), number);
+ addSource(new AudioVolumeResourceSource(this, name, _audioMapSCI1, number));
}
scanNewSources();
}
int ResourceManager::getAudioLanguage() const {
- return (_audioMapSCI1 ? _audioMapSCI1->volume_number : 0);
+ return (_audioMapSCI1 ? _audioMapSCI1->_volumeNumber : 0);
}
-SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) {
- Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resNumber), true);
+SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) {
+ Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resourceNr), true);
int trackNr, channelNr;
if (!resource)
return;
@@ -473,8 +531,6 @@ SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersi
byte *dataEnd;
Channel *channel, *sampleChannel;
- _channelsUsed = 0;
-
switch (_soundVersion) {
case SCI_VERSION_0_EARLY:
case SCI_VERSION_0_LATE:
@@ -526,6 +582,7 @@ SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersi
case SCI_VERSION_1_EARLY:
case SCI_VERSION_1_LATE:
+ case SCI_VERSION_2_1:
data = resource->data;
// Count # of tracks
_trackCount = 0;
@@ -537,6 +594,9 @@ SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersi
}
_tracks = new Track[_trackCount];
data = resource->data;
+
+ byte channelCount;
+
for (trackNr = 0; trackNr < _trackCount; trackNr++) {
// Track info starts with track type:BYTE
// Then the channel information gets appended Unknown:WORD, ChannelOffset:WORD, ChannelSize:WORD
@@ -546,35 +606,47 @@ SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersi
_tracks[trackNr].type = *data++;
// Counting # of channels used
data2 = data;
- _tracks[trackNr].channelCount = 0;
+ channelCount = 0;
while (*data2 != 0xFF) {
data2 += 6;
+ channelCount++;
_tracks[trackNr].channelCount++;
}
- _tracks[trackNr].channels = new Channel[_tracks[trackNr].channelCount];
+ _tracks[trackNr].channels = new Channel[channelCount];
+ _tracks[trackNr].channelCount = 0;
_tracks[trackNr].digitalChannelNr = -1; // No digital sound associated
_tracks[trackNr].digitalSampleRate = 0;
_tracks[trackNr].digitalSampleSize = 0;
_tracks[trackNr].digitalSampleStart = 0;
_tracks[trackNr].digitalSampleEnd = 0;
if (_tracks[trackNr].type != 0xF0) { // Digital track marker - not supported currently
- for (channelNr = 0; channelNr < _tracks[trackNr].channelCount; channelNr++) {
+ channelNr = 0;
+ while (channelCount--) {
channel = &_tracks[trackNr].channels[channelNr];
channel->prio = READ_LE_UINT16(data);
- channel->data = resource->data + READ_LE_UINT16(data + 2) + 2;
- channel->size = READ_LE_UINT16(data + 4) - 2; // Not counting channel header
- channel->number = *(channel->data - 2);
- setChannelUsed(channel->number);
- channel->poly = *(channel->data - 1);
- channel->time = channel->prev = 0;
- if (channel->number == 0xFE) { // Digital channel
- _tracks[trackNr].digitalChannelNr = channelNr;
- _tracks[trackNr].digitalSampleRate = READ_LE_UINT16(channel->data);
- _tracks[trackNr].digitalSampleSize = READ_LE_UINT16(channel->data + 2);
- _tracks[trackNr].digitalSampleStart = READ_LE_UINT16(channel->data + 4);
- _tracks[trackNr].digitalSampleEnd = READ_LE_UINT16(channel->data + 6);
- channel->data += 8; // Skip over header
- channel->size -= 8;
+ uint dataOffset = READ_LE_UINT16(data + 2);
+ if (dataOffset < resource->size) {
+ channel->data = resource->data + dataOffset;
+ channel->size = READ_LE_UINT16(data + 4);
+ channel->curPos = 0;
+ channel->number = *channel->data;
+ channel->poly = *(channel->data + 1);
+ channel->time = channel->prev = 0;
+ channel->data += 2; // skip over header
+ channel->size -= 2; // remove header size
+ if (channel->number == 0xFE) { // Digital channel
+ _tracks[trackNr].digitalChannelNr = channelNr;
+ _tracks[trackNr].digitalSampleRate = READ_LE_UINT16(channel->data);
+ _tracks[trackNr].digitalSampleSize = READ_LE_UINT16(channel->data + 2);
+ _tracks[trackNr].digitalSampleStart = READ_LE_UINT16(channel->data + 4);
+ _tracks[trackNr].digitalSampleEnd = READ_LE_UINT16(channel->data + 6);
+ channel->data += 8; // Skip over header
+ channel->size -= 8;
+ }
+ _tracks[trackNr].channelCount++;
+ channelNr++;
+ } else {
+ warning("Invalid offset inside sound resource %d: track %d, channel %d", resourceNr, trackNr, channelNr);
}
data += 6;
}