aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/sci/module.mk1
-rw-r--r--engines/sci/resource.cpp679
-rw-r--r--engines/sci/resource.h16
-rw-r--r--engines/sci/resource_audio.cpp701
4 files changed, 716 insertions, 681 deletions
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 26ae1c16b1..a2cfd38f95 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS := \
detection.o \
event.o \
resource.o \
+ resource_audio.o \
sci.o \
util.o \
engine/features.o \
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 0b985ea67c..89b7a5b7e2 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -26,7 +26,6 @@
// Resource library
#include "common/file.h"
-#include "common/macresman.h"
#include "sci/resource.h"
#include "sci/util.h"
@@ -45,18 +44,6 @@ struct resource_index_t {
uint16 wSize;
};
-struct ResourceSource {
- ResSourceType source_type;
- bool scanned;
- Common::String location_name; // FIXME: Replace by FSNode ?
- const Common::FSNode *resourceFile;
- int volume_number;
- ResourceSource *associated_map;
- uint32 audioCompressionType;
- int32 *audioCompressionOffsetMapping;
- Common::MacResManager macResMan;
-};
-
//////////////////////////////////////////////////////////////////////
static SciVersion s_sciVersion = SCI_VERSION_NONE; // FIXME: Move this inside a suitable class, e.g. SciEngine
@@ -270,36 +257,6 @@ ResourceSource *ResourceManager::getVolume(ResourceSource *map, int volume_nr) {
// Resource manager constructors and operations
-void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) {
- Common::File *file = getVolumeFile(source->location_name.c_str());
- if (!file) {
- warning("Failed to open %s", source->location_name.c_str());
- return;
- }
- file->seek(0, SEEK_SET);
- uint32 compressionType = file->readUint32BE();
- switch (compressionType) {
- case MKID_BE('MP3 '):
- case MKID_BE('OGG '):
- case MKID_BE('FLAC'):
- // Detected a compressed audio volume
- source->audioCompressionType = compressionType;
- // Now read the whole offset mapping table for later usage
- int32 recordCount = file->readUint32LE();
- if (!recordCount)
- error("compressed audio volume doesn't contain any entries!");
- int32 *offsetMapping = new int32[(recordCount + 1) * 2];
- source->audioCompressionOffsetMapping = offsetMapping;
- for (int recordNo = 0; recordNo < recordCount; recordNo++) {
- *offsetMapping++ = file->readUint32LE();
- *offsetMapping++ = file->readUint32LE();
- }
- // Put ending zero
- *offsetMapping++ = 0;
- *offsetMapping++ = file->size();
- }
-}
-
bool ResourceManager::loadPatch(Resource *res, Common::File &file) {
// We assume that the resource type matches res->type
// We also assume that the current file position is right at the actual data (behind resourceid/headersize byte)
@@ -341,70 +298,6 @@ bool ResourceManager::loadFromPatchFile(Resource *res) {
return loadPatch(res, file);
}
-bool ResourceManager::loadFromWaveFile(Resource *res, Common::File &file) {
- res->data = new byte[res->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);
-
- res->_status = kResStatusAllocated;
- return true;
-}
-
-bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::File &file) {
- // Check for WAVE files here
- uint32 riffTag = file.readUint32BE();
- if (riffTag == MKID_BE('RIFF')) {
- res->_headerSize = 0;
- res->size = file.readUint32LE();
- file.seek(-8, SEEK_CUR);
- return loadFromWaveFile(res, 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 from %s", res->_id.toString().c_str(), file.getName());
- res->unalloc();
- return false;
- }
-
- res->_headerSize = file.readByte();
-
- if (type == kResourceTypeAudio) {
- if (res->_headerSize != 11 && res->_headerSize != 12) {
- warning("Unsupported audio header");
- res->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);
- }
-
- return loadPatch(res, file);
-}
-
-bool ResourceManager::loadFromAudioVolumeSCI1(Resource *res, Common::File &file) {
- res->data = new byte[res->size];
-
- if (res->data == NULL) {
- error("Can't allocate %d bytes needed for loading %s", res->size, res->_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);
-
- res->_status = kResStatusAllocated;
- return true;
-}
-
Common::File *ResourceManager::getVolumeFile(const char *filename) {
Common::List<Common::File *>::iterator it = _volumeFiles.begin();
Common::File *file;
@@ -695,36 +588,6 @@ int ResourceManager::addInternalSources() {
return 1;
}
-void ResourceManager::addNewGMPatch(const Common::String &gameId) {
- Common::String gmPatchFile;
-
- if (gameId == "ecoquest")
- gmPatchFile = "ECO1GM.PAT";
- else if (gameId == "hoyle3")
- gmPatchFile = "HOY3GM.PAT";
- else if (gameId == "hoyle3")
- gmPatchFile = "HOY3GM.PAT";
- else if (gameId == "lsl1sci")
- gmPatchFile = "LL1_GM.PAT";
- else if (gameId == "lsl5")
- gmPatchFile = "LL5_GM.PAT";
- else if (gameId == "longbow")
- gmPatchFile = "ROBNGM.PAT";
- else if (gameId == "sq1sci")
- gmPatchFile = "SQ1_GM.PAT";
- else if (gameId == "sq4")
- gmPatchFile = "SQ4_GM.PAT";
- else if (gameId == "fairytales")
- gmPatchFile = "TALEGM.PAT";
-
- if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) {
- ResourceSource *psrcPatch = new ResourceSource;
- psrcPatch->source_type = kSourcePatch;
- psrcPatch->location_name = gmPatchFile;
- processPatch(psrcPatch, kResourceTypePatch, 4);
- }
-}
-
void ResourceManager::scanNewSources() {
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
ResourceSource *source = *it;
@@ -1295,49 +1158,6 @@ void ResourceManager::readResourcePatches(ResourceSource *source) {
}
}
-void ResourceManager::readWaveAudioPatches() {
- // Here we do check for SCI1.1+ so we can patch wav files in as audio resources
- Common::ArchiveMemberList files;
- SearchMan.listMatchingMembers(files, "*.wav");
-
- 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->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());
- }
- }
-}
-
int ResourceManager::readResourceMapSCI0(ResourceSource *map) {
Common::File file;
Resource *res;
@@ -1558,261 +1378,6 @@ void ResourceManager::addResource(ResourceId resId, ResourceSource *src, uint32
}
}
-void ResourceManager::removeAudioResource(ResourceId resId) {
- // Remove resource, unless it was loaded from a patch
- if (_resMap.contains(resId)) {
- Resource *res = _resMap.getVal(resId);
-
- if (res->_source->source_type == kSourceAudioVolume) {
- if (res->_status == kResStatusLocked) {
- warning("Failed to remove resource %s (still in use)", resId.toString().c_str());
- } else {
- if (res->_status == kResStatusEnqueued)
- removeFromLRU(res);
-
- _resMap.erase(resId);
- delete res;
- }
- }
- }
-}
-
-// Early SCI1.1 65535.MAP structure (uses RESOURCE.AUD):
-// =========
-// 6-byte entries:
-// w nEntry
-// dw offset
-
-// Late SCI1.1 65535.MAP structure (uses RESOURCE.SFX):
-// =========
-// 5-byte entries:
-// w nEntry
-// tb offset (cumulative)
-
-// Early SCI1.1 MAP structure:
-// ===============
-// 10-byte entries:
-// b noun
-// b verb
-// b cond
-// b seq
-// dw offset
-// w syncSize + syncAscSize
-
-// Late SCI1.1 MAP structure:
-// ===============
-// Header:
-// dw baseOffset
-// Followed by 7 or 11-byte entries:
-// b noun
-// b verb
-// b cond
-// b seq
-// tb cOffset (cumulative offset)
-// w syncSize (iff seq has bit 7 set)
-// 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);
-
- if (!mapRes) {
- warning("Failed to open %i.MAP", map->volume_number);
- return SCI_ERROR_RESMAP_NOT_FOUND;
- }
-
- ResourceSource *src = getVolume(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;
-
- while (ptr < mapRes->data + mapRes->size) {
- uint16 n = READ_LE_UINT16(ptr);
- ptr += 2;
-
- if (n == 0xffff)
- break;
-
- if (isEarly) {
- offset = READ_LE_UINT32(ptr);
- ptr += 4;
- } else {
- offset += READ_LE_UINT24(ptr);
- ptr += 3;
- }
-
- addResource(ResourceId(kResourceTypeAudio, n), src, offset);
- }
- } else {
- // Heuristic to detect late SCI1.1 map format
- if ((mapRes->size >= 11) && (ptr[mapRes->size - 11] == 0xff))
- isEarly = false;
-
- if (!isEarly) {
- offset = READ_LE_UINT32(ptr);
- ptr += 4;
- }
-
- while (ptr < mapRes->data + mapRes->size) {
- uint32 n = READ_BE_UINT32(ptr);
- int syncSize = 0;
- ptr += 4;
-
- if (n == 0xffffffff)
- break;
-
- if (isEarly) {
- offset = READ_LE_UINT32(ptr);
- ptr += 4;
- } else {
- offset += READ_LE_UINT24(ptr);
- ptr += 3;
- }
-
- if (isEarly || (n & 0x80)) {
- syncSize = READ_LE_UINT16(ptr);
- ptr += 2;
-
- if (syncSize > 0)
- addResource(ResourceId(kResourceTypeSync36, map->volume_number, n & 0xffffff3f), src, offset, syncSize);
- }
-
- if (n & 0x40) {
- syncSize += READ_LE_UINT16(ptr);
- ptr += 2;
- }
-
- addResource(ResourceId(kResourceTypeAudio36, map->volume_number, n & 0xffffff3f), src, offset + syncSize);
- }
- }
-
- return 0;
-}
-
-// AUDIOnnn.MAP contains 10-byte entries:
-// Early format:
-// w 5 bits resource type and 11 bits resource number
-// dw 7 bits volume number and 25 bits offset
-// dw size
-// Later format:
-// w nEntry
-// dw offset+volume (as in resource.map)
-// dw size
-// ending with 10 0xFFs
-int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
- Common::File file;
-
- if (!file.open(map->location_name))
- return SCI_ERROR_RESMAP_NOT_FOUND;
-
- bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio;
- file.seek(0);
-
- while (1) {
- uint16 n = file.readUint16LE();
- uint32 offset = file.readUint32LE();
- uint32 size = file.readUint32LE();
-
- if (file.eos() || file.err()) {
- warning("Error while reading %s", map->location_name.c_str());
- return SCI_ERROR_RESMAP_NOT_FOUND;
- }
-
- if (n == 0xffff)
- break;
-
- byte volume_nr;
-
- if (oldFormat) {
- n &= 0x07ff; // Mask out resource type
- volume_nr = offset >> 25; // most significant 7 bits
- offset &= 0x01ffffff; // least significant 25 bits
- } else {
- volume_nr = offset >> 28; // most significant 4 bits
- offset &= 0x0fffffff; // least significant 28 bits
- }
-
- ResourceSource *src = getVolume(map, volume_nr);
-
- if (src) {
- if (unload)
- removeAudioResource(ResourceId(kResourceTypeAudio, n));
- else
- addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
- } else {
- warning("Failed to find audio volume %i", volume_nr);
- }
- }
-
- return 0;
-}
-
-void ResourceManager::setAudioLanguage(int language) {
- if (_audioMapSCI1) {
- if (_audioMapSCI1->volume_number == language) {
- // This language is already loaded
- return;
- }
-
- // We already have a map loaded, so we unload it first
- readAudioMapSCI1(_audioMapSCI1, true);
-
- // Remove all volumes that use this map from the source list
- Common::List<ResourceSource *>::iterator it = _sources.begin();
- while (it != _sources.end()) {
- ResourceSource *src = *it;
- if (src->associated_map == _audioMapSCI1) {
- it = _sources.erase(it);
- delete src;
- } else {
- ++it;
- }
- }
-
- // Remove the map itself from the source list
- _sources.remove(_audioMapSCI1);
- delete _audioMapSCI1;
-
- _audioMapSCI1 = NULL;
- }
-
- char filename[9];
- snprintf(filename, 9, "AUDIO%03d", language);
-
- Common::String fullname = Common::String(filename) + ".MAP";
- if (!Common::File::exists(fullname)) {
- warning("No audio map found for language %i", language);
- return;
- }
-
- _audioMapSCI1 = addSource(NULL, kSourceExtAudioMap, fullname.c_str(), language);
-
- // Search for audio volumes for this language and add them to the source list
- Common::ArchiveMemberList files;
- SearchMan.listMatchingMembers(files, Common::String(filename) + ".0??");
- for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
- const Common::String name = (*x)->getName();
- const char *dot = strrchr(name.c_str(), '.');
- int number = atoi(dot + 1);
-
- addSource(_audioMapSCI1, kSourceAudioVolume, name.c_str(), number);
- }
-
- scanNewSources();
-}
-
-int ResourceManager::getAudioLanguage() const {
- return (_audioMapSCI1 ? _audioMapSCI1->volume_number : 0);
-}
-
int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream *file,
uint32&szPacked, ResourceCompression &compression) {
// SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes
@@ -2367,248 +1932,4 @@ Common::String ResourceManager::findSierraGameId() {
#undef READ_UINT16
-SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) {
- Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resNumber), true);
- int trackNr, channelNr;
- if (!resource)
- return;
-
- _innerResource = resource;
-
- byte *data, *data2;
- byte *dataEnd;
- Channel *channel, *sampleChannel;
-
- switch (_soundVersion) {
- case SCI_VERSION_0_EARLY:
- case SCI_VERSION_0_LATE:
- // SCI0 only has a header of 0x11/0x21 byte length and the actual midi track follows afterwards
- _trackCount = 1;
- _tracks = new Track[_trackCount];
- _tracks->digitalChannelNr = -1;
- _tracks->type = 0; // Not used for SCI0
- _tracks->channelCount = 1;
- // Digital sample data included? -> Add an additional channel
- if (resource->data[0] == 2)
- _tracks->channelCount++;
- _tracks->channels = new Channel[_tracks->channelCount];
- memset(_tracks->channels, 0, sizeof(Channel) * _tracks->channelCount);
- channel = &_tracks->channels[0];
- if (_soundVersion == SCI_VERSION_0_EARLY) {
- channel->data = resource->data + 0x11;
- channel->size = resource->size - 0x11;
- } else {
- channel->data = resource->data + 0x21;
- channel->size = resource->size - 0x21;
- }
- if (_tracks->channelCount == 2) {
- // Digital sample data included
- _tracks->digitalChannelNr = 1;
- sampleChannel = &_tracks->channels[1];
- // we need to find 0xFC (channel terminator) within the data
- data = channel->data;
- dataEnd = channel->data + channel->size;
- while ((data < dataEnd) && (*data != 0xfc))
- data++;
- // Skip any following 0xFCs as well
- while ((data < dataEnd) && (*data == 0xfc))
- data++;
- // Now adjust channels accordingly
- sampleChannel->data = data;
- sampleChannel->size = channel->size - (data - channel->data);
- channel->size = data - channel->data;
- // Read sample header information
- //Offset 14 in the header contains the frequency as a short integer. Offset 32 contains the sample length, also as a short integer.
- _tracks->digitalSampleRate = READ_LE_UINT16(sampleChannel->data + 14);
- _tracks->digitalSampleSize = READ_LE_UINT16(sampleChannel->data + 32);
- _tracks->digitalSampleStart = 0;
- _tracks->digitalSampleEnd = 0;
- sampleChannel->data += 44; // Skip over header
- sampleChannel->size -= 44;
- }
- break;
-
- case SCI_VERSION_1_EARLY:
- case SCI_VERSION_1_LATE:
- data = resource->data;
- // Count # of tracks
- _trackCount = 0;
- while ((*data++) != 0xFF) {
- _trackCount++;
- while (*data != 0xFF)
- data += 6;
- data++;
- }
- _tracks = new Track[_trackCount];
- data = resource->data;
- 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
- // 0xFF:BYTE as terminator to end that track and begin with another track type
- // Track type 0xFF is the marker signifying the end of the tracks
-
- _tracks[trackNr].type = *data++;
- // Counting # of channels used
- data2 = data;
- _tracks[trackNr].channelCount = 0;
- while (*data2 != 0xFF) {
- data2 += 6;
- _tracks[trackNr].channelCount++;
- }
- _tracks[trackNr].channels = new Channel[_tracks[trackNr].channelCount];
- _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++) {
- 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);
- 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;
- }
- data += 6;
- }
- } else {
- // Skip over digital track
- data += 6;
- }
- data++; // Skipping 0xFF that closes channels list
- }
- break;
-
- default:
- error("SoundResource: SCI version %d is unsupported", _soundVersion);
- }
-}
-
-SoundResource::~SoundResource() {
- for (int trackNr = 0; trackNr < _trackCount; trackNr++)
- delete[] _tracks[trackNr].channels;
- delete[] _tracks;
-
- _resMan->unlockResource(_innerResource);
-}
-
-#if 0
-SoundResource::Track* SoundResource::getTrackByNumber(uint16 number) {
- if (_soundVersion <= SCI_VERSION_0_LATE)
- return &_tracks[0];
-
- if (/*number >= 0 &&*/number < _trackCount)
- return &_tracks[number];
- return NULL;
-}
-#endif
-
-SoundResource::Track *SoundResource::getTrackByType(byte type) {
- if (_soundVersion <= SCI_VERSION_0_LATE)
- return &_tracks[0];
-
- for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
- if (_tracks[trackNr].type == type)
- return &_tracks[trackNr];
- }
- return NULL;
-}
-
-SoundResource::Track *SoundResource::getDigitalTrack() {
- for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
- if (_tracks[trackNr].digitalChannelNr != -1)
- return &_tracks[trackNr];
- }
- return NULL;
-}
-
-// Gets the filter mask for SCI0 sound resources
-int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) {
- byte *data = _innerResource->data;
- int channelMask = 0;
-
- if (_soundVersion > SCI_VERSION_0_LATE)
- return 0;
-
- data++; // Skip over digital sample flag
-
- for (int channelNr = 0; channelNr < 16; channelNr++) {
- channelMask = channelMask >> 1;
-
- byte flags;
-
- if (_soundVersion == SCI_VERSION_0_EARLY) {
- // Each channel is specified by a single byte
- // Upper 4 bits of the byte is a voices count
- // Lower 4 bits -> bit 0 set: use for AdLib
- // bit 1 set: use for PCjr
- // bit 2 set: use for PC speaker
- // bit 3 set and bit 0 clear: control channel (15)
- // bit 3 set and bit 0 set: rhythm channel (9)
- // Note: control channel is dynamically assigned inside the drivers,
- // but seems to be fixed at 15 in the song data.
- flags = *data++;
-
- // Get device bits
- flags &= 0x7;
- } else {
- // Each channel is specified by 2 bytes
- // 1st byte is voices count
- // 2nd byte is play mask, which specifies if the channel is supposed to be played
- // by the corresponding hardware
-
- // Skip voice count
- data++;
-
- flags = *data++;
- }
-
- bool play;
- switch (channelNr) {
- case 15:
- // Always play control channel
- play = true;
- break;
- case 9:
- // Play rhythm channel when requested
- play = wantsRhythm;
- break;
- default:
- // Otherwise check for flag
- play = flags & hardwareMask;
- }
-
- if (play) {
- // This Channel is supposed to be played by the hardware
- channelMask |= 0x8000;
- }
- }
-
- return channelMask;
-}
-
-byte SoundResource::getInitialVoiceCount(byte channel) {
- byte *data = _innerResource->data;
-
- if (_soundVersion > SCI_VERSION_0_LATE)
- return 0; // TODO
-
- data++; // Skip over digital sample flag
-
- if (_soundVersion == SCI_VERSION_0_EARLY)
- return data[channel] >> 4;
- else
- return data[channel * 2];
-}
-
} // End of namespace Sci
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index 8e83ed7bf0..dbd99f633d 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -26,8 +26,9 @@
#ifndef SCI_SCICORE_RESOURCE_H
#define SCI_SCICORE_RESOURCE_H
-#include "common/str.h"
#include "common/fs.h"
+#include "common/macresman.h"
+#include "common/str.h"
#include "sci/graphics/helpers.h" // for ViewType
#include "sci/decompressor.h"
@@ -121,7 +122,18 @@ const char *getResourceTypeName(ResourceType restype);
class ResourceManager;
-struct ResourceSource;
+
+struct ResourceSource {
+ ResSourceType source_type;
+ bool scanned;
+ Common::String location_name; // FIXME: Replace by FSNode ?
+ const Common::FSNode *resourceFile;
+ int volume_number;
+ ResourceSource *associated_map;
+ uint32 audioCompressionType;
+ int32 *audioCompressionOffsetMapping;
+ Common::MacResManager macResMan;
+};
class ResourceId {
public:
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
new file mode 100644
index 0000000000..5dea36bb07
--- /dev/null
+++ b/engines/sci/resource_audio.cpp
@@ -0,0 +1,701 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Resource library
+
+#include "common/file.h"
+
+#include "sci/resource.h"
+#include "sci/util.h"
+
+namespace Sci {
+
+void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) {
+ Common::File *file = getVolumeFile(source->location_name.c_str());
+ if (!file) {
+ warning("Failed to open %s", source->location_name.c_str());
+ return;
+ }
+ file->seek(0, SEEK_SET);
+ uint32 compressionType = file->readUint32BE();
+ switch (compressionType) {
+ case MKID_BE('MP3 '):
+ case MKID_BE('OGG '):
+ case MKID_BE('FLAC'):
+ // Detected a compressed audio volume
+ source->audioCompressionType = compressionType;
+ // Now read the whole offset mapping table for later usage
+ int32 recordCount = file->readUint32LE();
+ if (!recordCount)
+ error("compressed audio volume doesn't contain any entries!");
+ int32 *offsetMapping = new int32[(recordCount + 1) * 2];
+ source->audioCompressionOffsetMapping = offsetMapping;
+ for (int recordNo = 0; recordNo < recordCount; recordNo++) {
+ *offsetMapping++ = file->readUint32LE();
+ *offsetMapping++ = file->readUint32LE();
+ }
+ // Put ending zero
+ *offsetMapping++ = 0;
+ *offsetMapping++ = file->size();
+ }
+}
+
+bool ResourceManager::loadFromWaveFile(Resource *res, Common::File &file) {
+ res->data = new byte[res->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);
+
+ res->_status = kResStatusAllocated;
+ return true;
+}
+
+bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::File &file) {
+ // Check for WAVE files here
+ uint32 riffTag = file.readUint32BE();
+ if (riffTag == MKID_BE('RIFF')) {
+ res->_headerSize = 0;
+ res->size = file.readUint32LE();
+ file.seek(-8, SEEK_CUR);
+ return loadFromWaveFile(res, 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 from %s", res->_id.toString().c_str(), file.getName());
+ res->unalloc();
+ return false;
+ }
+
+ res->_headerSize = file.readByte();
+
+ if (type == kResourceTypeAudio) {
+ if (res->_headerSize != 11 && res->_headerSize != 12) {
+ warning("Unsupported audio header");
+ res->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);
+ }
+
+ return loadPatch(res, file);
+}
+
+bool ResourceManager::loadFromAudioVolumeSCI1(Resource *res, Common::File &file) {
+ res->data = new byte[res->size];
+
+ if (res->data == NULL) {
+ error("Can't allocate %d bytes needed for loading %s", res->size, res->_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);
+
+ res->_status = kResStatusAllocated;
+ return true;
+}
+
+void ResourceManager::addNewGMPatch(const Common::String &gameId) {
+ Common::String gmPatchFile;
+
+ if (gameId == "ecoquest")
+ gmPatchFile = "ECO1GM.PAT";
+ else if (gameId == "hoyle3")
+ gmPatchFile = "HOY3GM.PAT";
+ else if (gameId == "hoyle3")
+ gmPatchFile = "HOY3GM.PAT";
+ else if (gameId == "lsl1sci")
+ gmPatchFile = "LL1_GM.PAT";
+ else if (gameId == "lsl5")
+ gmPatchFile = "LL5_GM.PAT";
+ else if (gameId == "longbow")
+ gmPatchFile = "ROBNGM.PAT";
+ else if (gameId == "sq1sci")
+ gmPatchFile = "SQ1_GM.PAT";
+ else if (gameId == "sq4")
+ gmPatchFile = "SQ4_GM.PAT";
+ else if (gameId == "fairytales")
+ gmPatchFile = "TALEGM.PAT";
+
+ if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) {
+ ResourceSource *psrcPatch = new ResourceSource;
+ psrcPatch->source_type = kSourcePatch;
+ psrcPatch->location_name = gmPatchFile;
+ processPatch(psrcPatch, kResourceTypePatch, 4);
+ }
+}
+
+void ResourceManager::readWaveAudioPatches() {
+ // Here we do check for SCI1.1+ so we can patch wav files in as audio resources
+ Common::ArchiveMemberList files;
+ SearchMan.listMatchingMembers(files, "*.wav");
+
+ 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->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());
+ }
+ }
+}
+
+void ResourceManager::removeAudioResource(ResourceId resId) {
+ // Remove resource, unless it was loaded from a patch
+ if (_resMap.contains(resId)) {
+ Resource *res = _resMap.getVal(resId);
+
+ if (res->_source->source_type == kSourceAudioVolume) {
+ if (res->_status == kResStatusLocked) {
+ warning("Failed to remove resource %s (still in use)", resId.toString().c_str());
+ } else {
+ if (res->_status == kResStatusEnqueued)
+ removeFromLRU(res);
+
+ _resMap.erase(resId);
+ delete res;
+ }
+ }
+ }
+}
+
+// Early SCI1.1 65535.MAP structure (uses RESOURCE.AUD):
+// =========
+// 6-byte entries:
+// w nEntry
+// dw offset
+
+// Late SCI1.1 65535.MAP structure (uses RESOURCE.SFX):
+// =========
+// 5-byte entries:
+// w nEntry
+// tb offset (cumulative)
+
+// Early SCI1.1 MAP structure:
+// ===============
+// 10-byte entries:
+// b noun
+// b verb
+// b cond
+// b seq
+// dw offset
+// w syncSize + syncAscSize
+
+// Late SCI1.1 MAP structure:
+// ===============
+// Header:
+// dw baseOffset
+// Followed by 7 or 11-byte entries:
+// b noun
+// b verb
+// b cond
+// b seq
+// tb cOffset (cumulative offset)
+// w syncSize (iff seq has bit 7 set)
+// 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);
+
+ if (!mapRes) {
+ warning("Failed to open %i.MAP", map->volume_number);
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ }
+
+ ResourceSource *src = getVolume(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;
+
+ while (ptr < mapRes->data + mapRes->size) {
+ uint16 n = READ_LE_UINT16(ptr);
+ ptr += 2;
+
+ if (n == 0xffff)
+ break;
+
+ if (isEarly) {
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ } else {
+ offset += READ_LE_UINT24(ptr);
+ ptr += 3;
+ }
+
+ addResource(ResourceId(kResourceTypeAudio, n), src, offset);
+ }
+ } else {
+ // Heuristic to detect late SCI1.1 map format
+ if ((mapRes->size >= 11) && (ptr[mapRes->size - 11] == 0xff))
+ isEarly = false;
+
+ if (!isEarly) {
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ }
+
+ while (ptr < mapRes->data + mapRes->size) {
+ uint32 n = READ_BE_UINT32(ptr);
+ int syncSize = 0;
+ ptr += 4;
+
+ if (n == 0xffffffff)
+ break;
+
+ if (isEarly) {
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ } else {
+ offset += READ_LE_UINT24(ptr);
+ ptr += 3;
+ }
+
+ if (isEarly || (n & 0x80)) {
+ syncSize = READ_LE_UINT16(ptr);
+ ptr += 2;
+
+ if (syncSize > 0)
+ addResource(ResourceId(kResourceTypeSync36, map->volume_number, n & 0xffffff3f), src, offset, syncSize);
+ }
+
+ if (n & 0x40) {
+ syncSize += READ_LE_UINT16(ptr);
+ ptr += 2;
+ }
+
+ addResource(ResourceId(kResourceTypeAudio36, map->volume_number, n & 0xffffff3f), src, offset + syncSize);
+ }
+ }
+
+ return 0;
+}
+
+// AUDIOnnn.MAP contains 10-byte entries:
+// Early format:
+// w 5 bits resource type and 11 bits resource number
+// dw 7 bits volume number and 25 bits offset
+// dw size
+// Later format:
+// w nEntry
+// dw offset+volume (as in resource.map)
+// dw size
+// ending with 10 0xFFs
+int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
+ Common::File file;
+
+ if (!file.open(map->location_name))
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+
+ bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio;
+ file.seek(0);
+
+ while (1) {
+ uint16 n = file.readUint16LE();
+ uint32 offset = file.readUint32LE();
+ uint32 size = file.readUint32LE();
+
+ if (file.eos() || file.err()) {
+ warning("Error while reading %s", map->location_name.c_str());
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ }
+
+ if (n == 0xffff)
+ break;
+
+ byte volume_nr;
+
+ if (oldFormat) {
+ n &= 0x07ff; // Mask out resource type
+ volume_nr = offset >> 25; // most significant 7 bits
+ offset &= 0x01ffffff; // least significant 25 bits
+ } else {
+ volume_nr = offset >> 28; // most significant 4 bits
+ offset &= 0x0fffffff; // least significant 28 bits
+ }
+
+ ResourceSource *src = getVolume(map, volume_nr);
+
+ if (src) {
+ if (unload)
+ removeAudioResource(ResourceId(kResourceTypeAudio, n));
+ else
+ addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
+ } else {
+ warning("Failed to find audio volume %i", volume_nr);
+ }
+ }
+
+ return 0;
+}
+
+void ResourceManager::setAudioLanguage(int language) {
+ if (_audioMapSCI1) {
+ if (_audioMapSCI1->volume_number == language) {
+ // This language is already loaded
+ return;
+ }
+
+ // We already have a map loaded, so we unload it first
+ readAudioMapSCI1(_audioMapSCI1, true);
+
+ // Remove all volumes that use this map from the source list
+ Common::List<ResourceSource *>::iterator it = _sources.begin();
+ while (it != _sources.end()) {
+ ResourceSource *src = *it;
+ if (src->associated_map == _audioMapSCI1) {
+ it = _sources.erase(it);
+ delete src;
+ } else {
+ ++it;
+ }
+ }
+
+ // Remove the map itself from the source list
+ _sources.remove(_audioMapSCI1);
+ delete _audioMapSCI1;
+
+ _audioMapSCI1 = NULL;
+ }
+
+ char filename[9];
+ snprintf(filename, 9, "AUDIO%03d", language);
+
+ Common::String fullname = Common::String(filename) + ".MAP";
+ if (!Common::File::exists(fullname)) {
+ warning("No audio map found for language %i", language);
+ return;
+ }
+
+ _audioMapSCI1 = addSource(NULL, kSourceExtAudioMap, fullname.c_str(), language);
+
+ // Search for audio volumes for this language and add them to the source list
+ Common::ArchiveMemberList files;
+ SearchMan.listMatchingMembers(files, Common::String(filename) + ".0??");
+ for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
+ const Common::String name = (*x)->getName();
+ const char *dot = strrchr(name.c_str(), '.');
+ int number = atoi(dot + 1);
+
+ addSource(_audioMapSCI1, kSourceAudioVolume, name.c_str(), number);
+ }
+
+ scanNewSources();
+}
+
+int ResourceManager::getAudioLanguage() const {
+ return (_audioMapSCI1 ? _audioMapSCI1->volume_number : 0);
+}
+
+SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) {
+ Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resNumber), true);
+ int trackNr, channelNr;
+ if (!resource)
+ return;
+
+ _innerResource = resource;
+
+ byte *data, *data2;
+ byte *dataEnd;
+ Channel *channel, *sampleChannel;
+
+ switch (_soundVersion) {
+ case SCI_VERSION_0_EARLY:
+ case SCI_VERSION_0_LATE:
+ // SCI0 only has a header of 0x11/0x21 byte length and the actual midi track follows afterwards
+ _trackCount = 1;
+ _tracks = new Track[_trackCount];
+ _tracks->digitalChannelNr = -1;
+ _tracks->type = 0; // Not used for SCI0
+ _tracks->channelCount = 1;
+ // Digital sample data included? -> Add an additional channel
+ if (resource->data[0] == 2)
+ _tracks->channelCount++;
+ _tracks->channels = new Channel[_tracks->channelCount];
+ memset(_tracks->channels, 0, sizeof(Channel) * _tracks->channelCount);
+ channel = &_tracks->channels[0];
+ if (_soundVersion == SCI_VERSION_0_EARLY) {
+ channel->data = resource->data + 0x11;
+ channel->size = resource->size - 0x11;
+ } else {
+ channel->data = resource->data + 0x21;
+ channel->size = resource->size - 0x21;
+ }
+ if (_tracks->channelCount == 2) {
+ // Digital sample data included
+ _tracks->digitalChannelNr = 1;
+ sampleChannel = &_tracks->channels[1];
+ // we need to find 0xFC (channel terminator) within the data
+ data = channel->data;
+ dataEnd = channel->data + channel->size;
+ while ((data < dataEnd) && (*data != 0xfc))
+ data++;
+ // Skip any following 0xFCs as well
+ while ((data < dataEnd) && (*data == 0xfc))
+ data++;
+ // Now adjust channels accordingly
+ sampleChannel->data = data;
+ sampleChannel->size = channel->size - (data - channel->data);
+ channel->size = data - channel->data;
+ // Read sample header information
+ //Offset 14 in the header contains the frequency as a short integer. Offset 32 contains the sample length, also as a short integer.
+ _tracks->digitalSampleRate = READ_LE_UINT16(sampleChannel->data + 14);
+ _tracks->digitalSampleSize = READ_LE_UINT16(sampleChannel->data + 32);
+ _tracks->digitalSampleStart = 0;
+ _tracks->digitalSampleEnd = 0;
+ sampleChannel->data += 44; // Skip over header
+ sampleChannel->size -= 44;
+ }
+ break;
+
+ case SCI_VERSION_1_EARLY:
+ case SCI_VERSION_1_LATE:
+ data = resource->data;
+ // Count # of tracks
+ _trackCount = 0;
+ while ((*data++) != 0xFF) {
+ _trackCount++;
+ while (*data != 0xFF)
+ data += 6;
+ data++;
+ }
+ _tracks = new Track[_trackCount];
+ data = resource->data;
+ 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
+ // 0xFF:BYTE as terminator to end that track and begin with another track type
+ // Track type 0xFF is the marker signifying the end of the tracks
+
+ _tracks[trackNr].type = *data++;
+ // Counting # of channels used
+ data2 = data;
+ _tracks[trackNr].channelCount = 0;
+ while (*data2 != 0xFF) {
+ data2 += 6;
+ _tracks[trackNr].channelCount++;
+ }
+ _tracks[trackNr].channels = new Channel[_tracks[trackNr].channelCount];
+ _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++) {
+ 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);
+ 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;
+ }
+ data += 6;
+ }
+ } else {
+ // Skip over digital track
+ data += 6;
+ }
+ data++; // Skipping 0xFF that closes channels list
+ }
+ break;
+
+ default:
+ error("SoundResource: SCI version %d is unsupported", _soundVersion);
+ }
+}
+
+SoundResource::~SoundResource() {
+ for (int trackNr = 0; trackNr < _trackCount; trackNr++)
+ delete[] _tracks[trackNr].channels;
+ delete[] _tracks;
+
+ _resMan->unlockResource(_innerResource);
+}
+
+#if 0
+SoundResource::Track* SoundResource::getTrackByNumber(uint16 number) {
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ return &_tracks[0];
+
+ if (/*number >= 0 &&*/number < _trackCount)
+ return &_tracks[number];
+ return NULL;
+}
+#endif
+
+SoundResource::Track *SoundResource::getTrackByType(byte type) {
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ return &_tracks[0];
+
+ for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
+ if (_tracks[trackNr].type == type)
+ return &_tracks[trackNr];
+ }
+ return NULL;
+}
+
+SoundResource::Track *SoundResource::getDigitalTrack() {
+ for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
+ if (_tracks[trackNr].digitalChannelNr != -1)
+ return &_tracks[trackNr];
+ }
+ return NULL;
+}
+
+// Gets the filter mask for SCI0 sound resources
+int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) {
+ byte *data = _innerResource->data;
+ int channelMask = 0;
+
+ if (_soundVersion > SCI_VERSION_0_LATE)
+ return 0;
+
+ data++; // Skip over digital sample flag
+
+ for (int channelNr = 0; channelNr < 16; channelNr++) {
+ channelMask = channelMask >> 1;
+
+ byte flags;
+
+ if (_soundVersion == SCI_VERSION_0_EARLY) {
+ // Each channel is specified by a single byte
+ // Upper 4 bits of the byte is a voices count
+ // Lower 4 bits -> bit 0 set: use for AdLib
+ // bit 1 set: use for PCjr
+ // bit 2 set: use for PC speaker
+ // bit 3 set and bit 0 clear: control channel (15)
+ // bit 3 set and bit 0 set: rhythm channel (9)
+ // Note: control channel is dynamically assigned inside the drivers,
+ // but seems to be fixed at 15 in the song data.
+ flags = *data++;
+
+ // Get device bits
+ flags &= 0x7;
+ } else {
+ // Each channel is specified by 2 bytes
+ // 1st byte is voices count
+ // 2nd byte is play mask, which specifies if the channel is supposed to be played
+ // by the corresponding hardware
+
+ // Skip voice count
+ data++;
+
+ flags = *data++;
+ }
+
+ bool play;
+ switch (channelNr) {
+ case 15:
+ // Always play control channel
+ play = true;
+ break;
+ case 9:
+ // Play rhythm channel when requested
+ play = wantsRhythm;
+ break;
+ default:
+ // Otherwise check for flag
+ play = flags & hardwareMask;
+ }
+
+ if (play) {
+ // This Channel is supposed to be played by the hardware
+ channelMask |= 0x8000;
+ }
+ }
+
+ return channelMask;
+}
+
+byte SoundResource::getInitialVoiceCount(byte channel) {
+ byte *data = _innerResource->data;
+
+ if (_soundVersion > SCI_VERSION_0_LATE)
+ return 0; // TODO
+
+ data++; // Skip over digital sample flag
+
+ if (_soundVersion == SCI_VERSION_0_EARLY)
+ return data[channel] >> 4;
+ else
+ return data[channel * 2];
+}
+
+} // End of namespace Sci