mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 05:38:56 +00:00
Split all of the audio-related functions of the resource manager in a separate file
svn-id: r49260
This commit is contained in:
parent
739f551852
commit
e5eaf3ee55
@ -6,6 +6,7 @@ MODULE_OBJS := \
|
||||
detection.o \
|
||||
event.o \
|
||||
resource.o \
|
||||
resource_audio.o \
|
||||
sci.o \
|
||||
util.o \
|
||||
engine/features.o \
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
701
engines/sci/resource_audio.cpp
Normal file
701
engines/sci/resource_audio.cpp
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user