SCI: Moved SCI1 audio map handling into the resource manager.

svn-id: r41408
This commit is contained in:
Walter van Niftrik 2009-06-09 19:18:48 +00:00
parent 6f49d1576b
commit 5e68fbfe87
3 changed files with 210 additions and 202 deletions

View File

@ -1027,7 +1027,7 @@ reg_t kDoAudio(EngineState *s, int funct_nr, int argc, reg_t *argv) {
// In SCI1.1: tests for digital audio support
return make_reg(0, 1);
} else {
s->_sound._audioResource->setAudioLang(argv[1].toSint16());
s->resmgr->setAudioLanguage(argv[1].toSint16());
}
break;
default:

View File

@ -217,7 +217,7 @@ bool ResourceManager::loadFromPatchFile(Resource *res) {
return loadPatch(res, file);
}
bool ResourceManager::loadFromAudioVolume(Resource *res, Common::File &file) {
bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::File &file) {
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))) {
@ -243,6 +243,21 @@ bool ResourceManager::loadFromAudioVolume(Resource *res, Common::File &file) {
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;
@ -292,7 +307,10 @@ void ResourceManager::loadResource(Resource *res) {
file->seek(res->file_offset, SEEK_SET);
if (res->source->source_type == kSourceAudioVolume) {
loadFromAudioVolume(res, *file);
if (_sciVersion < SCI_VERSION_1_1)
loadFromAudioVolumeSCI1(res, *file);
else
loadFromAudioVolumeSCI11(res, *file);
} else {
int error = decompress(res, file);
if (error) {
@ -436,8 +454,11 @@ void ResourceManager::scanNewSources() {
else
readResourceMapSCI1(source);
break;
case kSourceExtAudioMap:
readAudioMapSCI1(source);
break;
case kSourceIntMap:
readMap(source);
readAudioMapSCI11(source);
break;
default:
break;
@ -460,6 +481,7 @@ ResourceManager::ResourceManager(int version, int maxMemory) {
_LRU.clear();
_resMap.clear();
_sciVersion = version;
_audioMapSCI1 = NULL;
addAppropriateSources();
@ -607,15 +629,10 @@ void ResourceManager::printLRU() {
debug("Total: %d entries, %d bytes (mgr says %d)", entries, mem, _memoryLRU);
}
void ResourceManager::freeOldResources(int last_invulnerable) {
while (_maxMemory < _memoryLRU && (!last_invulnerable || !_LRU.empty())) {
void ResourceManager::freeOldResources() {
while (_maxMemory < _memoryLRU) {
assert(!_LRU.empty());
Resource *goner = *_LRU.reverse_begin();
if (!goner) {
debug("Internal error: mgr->lru_last is NULL!");
debug("LRU-mem= %d", _memoryLRU);
debug("lru_first = %p", (void *)*_LRU.begin());
printLRU();
}
removeFromLRU(goner);
goner->unalloc();
#ifdef SCI_VERBOSE_RESMGR
@ -659,6 +676,8 @@ Resource *ResourceManager::findResource(ResourceId id, bool lock) {
// Unless an error occured, the resource is now either
// locked or allocated, but never queued or freed.
freeOldResources();
if (lock) {
if (retval->status == kResStatusAllocated) {
retval->status = kResStatusLocked;
@ -671,8 +690,6 @@ Resource *ResourceManager::findResource(ResourceId id, bool lock) {
addToLRU(retval);
}
freeOldResources(retval->status == kResStatusAllocated);
if (retval->data)
return retval;
else {
@ -695,7 +712,7 @@ void ResourceManager::unlockResource(Resource *res) {
addToLRU(res);
}
freeOldResources(0);
freeOldResources();
}
int ResourceManager::detectMapVersion() {
@ -1077,6 +1094,22 @@ 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->lockers == 0) {
_resMap.erase(resId);
delete res;
} else {
warning("Failed to remove resource %s (still in use)", resId.toString().c_str());
}
}
}
}
// Early SCI1.1 65535.MAP structure (uses RESOURCE.AUD):
// =========
// 6-byte entries:
@ -1112,7 +1145,7 @@ void ResourceManager::addResource(ResourceId resId, ResourceSource *src, uint32
// w syncSize (iff seq has bit 7 set)
// w syncAscSize (iff seq has bit 6 set)
int ResourceManager::readMap(ResourceSource *map) {
int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
bool isEarly = true;
uint32 offset = 0;
Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->volume_number), false);
@ -1197,6 +1230,103 @@ int ResourceManager::readMap(ResourceSource *map) {
return 0;
}
// AUDIOnnn.MAP contains 10-byte entries:
// 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;
while (1) {
uint16 n = file.readUint16LE();
uint32 offset = file.readUint32LE();
uint32 size = file.readUint32LE();
if (file.ioFailed()) {
warning("Error while reading %s", map->location_name.c_str());
return SCI_ERROR_RESMAP_NOT_FOUND;
}
if (n == 0xffff)
break;
byte 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::readResourceInfo(Resource *res, Common::File *file,
uint32&szPacked, ResourceCompression &compression) {
// SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes
@ -1360,42 +1490,6 @@ AudioResource::AudioResource(ResourceManager *resMgr, int sciVersion) {
_resMgr = resMgr;
_sciVersion = sciVersion;
_audioRate = 11025;
_lang = 0;
_audioMapSCI1 = 0;
_audioMapSCI11 = 0;
}
AudioResource::~AudioResource() {
if (_sciVersion < SCI_VERSION_1_1) {
if (_audioMapSCI1) {
delete[] _audioMapSCI1;
_audioMapSCI1 = 0;
}
} else {
if (_audioMapSCI11)
_resMgr->unlockResource(_audioMapSCI11);
}
}
// Used in SCI1 games
void AudioResource::setAudioLang(int16 lang) {
if (lang != -1) {
_lang = lang;
char filename[40];
sprintf(filename, "AUDIO%03d.MAP", _lang);
Common::File* audioMapFile = new Common::File();
if (audioMapFile->open(filename)) {
// The audio map is freed in the destructor
_audioMapSCI1 = new byte[audioMapFile->size()];
audioMapFile->read(_audioMapSCI1, audioMapFile->size());
audioMapFile->close();
delete audioMapFile;
} else {
_audioMapSCI1 = 0;
}
}
}
int AudioResource::getAudioPosition() {
@ -1406,33 +1500,6 @@ int AudioResource::getAudioPosition() {
}
}
bool AudioResource::findAudEntrySCI1(uint16 audioNumber, byte &volume, uint32 &offset, uint32 &size) {
// AUDIO00X.MAP contains 10-byte entries:
// w nEntry
// dw offset+volume (as in resource.map)
// dw size
// ending with 10 0xFFs
uint16 n;
uint32 off;
if (_audioMapSCI1 == 0)
return false;
byte *ptr = _audioMapSCI1;
while ((n = READ_LE_UINT16(ptr)) != 0xFFFF) {
if (n == audioNumber) {
off = READ_LE_UINT32(ptr + 2);
size = READ_LE_UINT32(ptr + 6);
volume = off >> 28;
offset = off & 0x0FFFFFFF;
return true;
}
ptr += 10;
}
return false;
}
// FIXME: Move this to sound/adpcm.cpp?
// Note that the 16-bit version is also used in coktelvideo.cpp
static const uint16 tableDPCM16[128] = {
@ -1535,115 +1602,56 @@ static byte* readSOLAudio(Common::SeekableReadStream *audioStream, uint32 &size,
Audio::AudioStream* AudioResource::getAudioStream(uint32 audioNumber, uint32 volume, int *sampleLen) {
Audio::AudioStream *audioStream = 0;
uint32 offset;
uint32 size;
bool found = false;
byte *data = 0;
char filename[40];
byte flags = 0;
Sci::Resource* audioRes = NULL;
Sci::Resource* audioRes;
// Try to load from resource manager
if (volume == 65535)
if (volume == 65535) {
audioRes = _resMgr->findResource(ResourceId(kResourceTypeAudio, audioNumber), false);
else
audioRes = _resMgr->findResource(ResourceId(kResourceTypeAudio36, volume, audioNumber), false);
if (audioRes) {
if (_sciVersion < SCI_VERSION_1_1) {
size = audioRes->size;
data = audioRes->data;
} else {
byte audioFlags;
Common::MemoryReadStream *headerStream =
new Common::MemoryReadStream(audioRes->header, audioRes->headerSize, false);
if (readSOLHeader(headerStream, audioRes->headerSize, size, _audioRate, audioFlags)) {
Common::MemoryReadStream *dataStream =
new Common::MemoryReadStream(audioRes->data, audioRes->size, false);
data = readSOLAudio(dataStream, size, audioFlags, flags);
delete dataStream;
}
delete headerStream;
}
if (data) {
audioStream = Audio::makeLinearInputStream(data, size, _audioRate,
flags | Audio::Mixer::FLAG_AUTOFREE, 0, 0);
if (!audioRes) {
warning("Failed to find audio entry %i", audioNumber);
return NULL;
}
} else {
// Load it from the audio file
if (_sciVersion < SCI_VERSION_1_1) {
byte sci1Volume;
found = findAudEntrySCI1(audioNumber, sci1Volume, offset, size);
sprintf(filename, "AUDIO%03d.%03d", _lang, sci1Volume);
flags |= Audio::Mixer::FLAG_UNSIGNED;
}
if (found) {
#if 0
// TODO: This tries to load directly from the KQ5CD audio file with MP3/OGG/FLAC
// compression. Once we got a tool to compress this file AND update the map file
// at the same time, we can use this code to play compressed audio.
if (_sciVersion < SCI_VERSION_1_1) {
uint32 start = offset * 1000 / _audioRate;
uint32 duration = size * 1000 / _audioRate;
// Try to load compressed
audioStream = Audio::AudioStream::openStreamFile(filename, start, duration);
}
#endif
if (!audioStream) {
// Compressed file load failed, try to load original raw data
Common::File* audioFile = new Common::File();
if (audioFile->open(filename)) {
audioFile->seek(offset);
if (_sciVersion < SCI_VERSION_1_1) {
data = (byte *)malloc(size);
audioFile->read(data, size);
} else {
byte type = audioFile->readByte() & 0x7f;
byte audioFlags;
if (type != kResourceTypeAudio) {
warning("Resource type mismatch");
delete audioFile;
return NULL;
}
byte headerSize = audioFile->readByte();
if (readSOLHeader(audioFile, headerSize, size, _audioRate, audioFlags))
data = readSOLAudio(audioFile, size, audioFlags, flags);
if (!data) {
delete audioFile;
return NULL;
}
}
audioFile->close();
if (data) {
audioStream = Audio::makeLinearInputStream(data, size, _audioRate,
flags | Audio::Mixer::FLAG_AUTOFREE, 0, 0);
}
}
delete audioFile;
}
} else {
audioRes = _resMgr->findResource(ResourceId(kResourceTypeAudio36, volume, audioNumber), false);
if (!audioRes) {
warning("Failed to find audio entry (%i, %i, %i, %i, %i)", volume, (audioNumber >> 24) & 0xff,
(audioNumber >> 16) & 0xff, (audioNumber >> 8) & 0xff, audioNumber & 0xff);
return NULL;
}
}
if (audioStream) {
*sampleLen = (flags & Audio::Mixer::FLAG_16BITS ? size >> 1 : size) * 60 / _audioRate;
return audioStream;
byte audioFlags;
if (audioRes->headerSize > 0) {
// SCI1.1
Common::MemoryReadStream *headerStream =
new Common::MemoryReadStream(audioRes->header, audioRes->headerSize, false);
if (readSOLHeader(headerStream, audioRes->headerSize, size, _audioRate, audioFlags)) {
Common::MemoryReadStream *dataStream =
new Common::MemoryReadStream(audioRes->data, audioRes->size, false);
data = readSOLAudio(dataStream, size, audioFlags, flags);
delete dataStream;
}
delete headerStream;
} else {
// SCI1
size = audioRes->size;
data = (byte *)malloc(size);
assert(data);
memcpy(data, audioRes->data, size);
flags = Audio::Mixer::FLAG_UNSIGNED;
}
if (data) {
audioStream = Audio::makeLinearInputStream(data, size, _audioRate,
flags | Audio::Mixer::FLAG_AUTOFREE, 0, 0);
if (audioStream) {
*sampleLen = (flags & Audio::Mixer::FLAG_16BITS ? size >> 1 : size) * 60 / _audioRate;
return audioStream;
}
}
return NULL;

View File

@ -72,19 +72,14 @@ enum {
enum ResSourceType {
kSourceDirectory = 0,
kSourcePatch = 1,
kSourceVolume = 2,
kSourceExtMap = 3,
kSourceIntMap = 4,
kSourceAudioVolume = 5,
kSourceMask = 127
kSourcePatch,
kSourceVolume,
kSourceExtMap,
kSourceIntMap,
kSourceAudioVolume,
kSourceExtAudioMap
};
#define RESSOURCE_ADDRESSING_BASIC 0
#define RESSOURCE_ADDRESSING_EXTENDED 128
#define RESSOURCE_ADDRESSING_MASK 128
#define RESOURCE_HASH(type, number) (uint32)((type<<16) | number)
#define SCI0_RESMAP_ENTRIES_SIZE 6
#define SCI1_RESMAP_ENTRIES_SIZE 6
#define SCI11_RESMAP_ENTRIES_SIZE 5
@ -136,7 +131,6 @@ struct ResourceSource {
Common::String location_name; // FIXME: Replace by FSNode ?
int volume_number;
ResourceSource *associated_map;
ResourceSource *next;
};
class ResourceManager;
@ -270,6 +264,8 @@ public:
*/
Common::List<ResourceId> *listResources(ResourceType type, int mapNumber = -1);
void setAudioLanguage(int language);
protected:
int _maxMemory; //!< Config option: Maximum total byte number allocated
Common::List<ResourceSource *> _sources;
@ -278,6 +274,7 @@ protected:
Common::List<Resource *> _LRU; //!< Last Resource Used list
ResourceMap _resMap;
Common::List<Common::File *> _volumeFiles; //!< list of opened volume files
ResourceSource *_audioMapSCI1; //!< Currently loaded audio map for SCI1
/**
* Add a path to the resource manager's list of sources.
@ -325,11 +322,13 @@ protected:
void loadResource(Resource *res);
bool loadPatch(Resource *res, Common::File &file);
bool loadFromPatchFile(Resource *res);
bool loadFromAudioVolume(Resource *res, Common::File &file);
void freeOldResources(int last_invulnerable);
bool loadFromAudioVolumeSCI1(Resource *res, Common::File &file);
bool loadFromAudioVolumeSCI11(Resource *res, Common::File &file);
void freeOldResources();
int decompress(Resource *res, Common::File *file);
int readResourceInfo(Resource *res, Common::File *file, uint32&szPacked, ResourceCompression &compression);
void addResource(ResourceId resId, ResourceSource *src, uint32 offset, uint32 size = 0);
void removeAudioResource(ResourceId resId);
/**--- Resource map decoding functions ---*/
int detectMapVersion();
@ -337,21 +336,32 @@ protected:
/**
* Reads the SCI0 resource.map file from a local directory.
* @param map The map
* @return 0 on success, an SCI_ERROR_* code otherwise
*/
int readResourceMapSCI0(ResourceSource *map);
/**
* Reads the SCI1 resource.map file from a local directory.
* @param map The map
* @return 0 on success, an SCI_ERROR_* code otherwise
*/
int readResourceMapSCI1(ResourceSource *map);
/**
* Reads SCI1.1 MAP resources
* Reads SCI1.1 audio map resources
* @param map The map
* @return 0 on success, an SCI_ERROR_* code otherwise
*/
int readMap(ResourceSource *map);
int readAudioMapSCI11(ResourceSource *map);
/**
* Reads SCI1 audio map files
* @param map The map
* @param unload Unload the map instead of loading it
* @return 0 on success, an SCI_ERROR_* code otherwise
*/
int readAudioMapSCI1(ResourceSource *map, bool unload = false);
/**--- Patch management functions ---*/
@ -393,10 +403,8 @@ protected:
class AudioResource {
public:
AudioResource(ResourceManager *resMgr, int sciVersion);
~AudioResource();
void setAudioRate(uint16 audioRate) { _audioRate = audioRate; }
void setAudioLang(int16 lang);
Audio::SoundHandle* getAudioHandle() { return &_audioHandle; }
int getAudioPosition();
@ -410,16 +418,8 @@ public:
private:
Audio::SoundHandle _audioHandle;
uint16 _audioRate;
int16 _lang;
byte *_audioMapSCI1;
Resource *_audioMapSCI11;
ResourceManager *_resMgr;
int _sciVersion;
bool findAudEntrySCI1(uint16 audioNumber, byte &volume, uint32 &offset, uint32 &size);
bool findAudEntrySCI11(uint32 audioNumber, uint32 volume, uint32 &offset, bool getSync = false, uint32 *size = NULL);
bool findAudEntrySCI11Late(uint32 audioNumber, uint32 &offset, bool getSync, uint32 *size);
bool findAudEntrySCI11Early(uint32 audioNumber, uint32 &offset, bool getSync, uint32 *size);
};
} // End of namespace Sci