aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/resource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/resource.cpp')
-rw-r--r--engines/sci/resource.cpp1065
1 files changed, 306 insertions, 759 deletions
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 4888dbd4cb..4818428663 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
@@ -196,7 +183,7 @@ ResourceSource *ResourceManager::addExternalMap(const char *file_name, int volum
return newsrc;
}
-ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile) {
+ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile, int volume_nr) {
ResourceSource *newsrc = new ResourceSource();
newsrc->source_type = kSourceExtMap;
@@ -204,7 +191,7 @@ ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile) {
newsrc->resourceFile = mapFile;
newsrc->scanned = false;
newsrc->associated_map = NULL;
- newsrc->volume_number = 0;
+ newsrc->volume_number = volume_nr;
_sources.push_back(newsrc);
return newsrc;
@@ -250,6 +237,7 @@ ResourceSource *ResourceManager::addPatchDir(const char *dirname) {
ResourceSource *newsrc = new ResourceSource();
newsrc->source_type = kSourceDirectory;
+ newsrc->resourceFile = 0;
newsrc->scanned = false;
newsrc->location_name = dirname;
@@ -270,37 +258,7 @@ 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) {
+bool ResourceManager::loadPatch(Resource *res, Common::SeekableReadStream *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)
@@ -315,12 +273,12 @@ bool ResourceManager::loadPatch(Resource *res, Common::File &file) {
unsigned int really_read;
if (res->_headerSize > 0) {
- really_read = file.read(res->_header, res->_headerSize);
+ really_read = file->read(res->_header, res->_headerSize);
if (really_read != res->_headerSize)
error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->_headerSize);
}
- really_read = file.read(res->data, res->size);
+ 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);
@@ -338,77 +296,18 @@ bool ResourceManager::loadFromPatchFile(Resource *res) {
}
// Skip resourceid and header size byte
file.seek(2, SEEK_SET);
- 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;
+ return loadPatch(res, &file);
}
-Common::File *ResourceManager::getVolumeFile(const char *filename) {
+Common::SeekableReadStream *ResourceManager::getVolumeFile(ResourceSource *source) {
Common::List<Common::File *>::iterator it = _volumeFiles.begin();
Common::File *file;
+ if (source->resourceFile)
+ return source->resourceFile->createReadStream();
+
+ const char *filename = source->location_name.c_str();
+
// check if file is already opened
while (it != _volumeFiles.end()) {
file = *it;
@@ -445,11 +344,10 @@ void ResourceManager::loadResource(Resource *res) {
return;
if (res->_source->source_type == kSourceMacResourceFork) {
- //error("ResourceManager::loadResource(): TODO: Mac resource fork ;)");
Common::SeekableReadStream *stream = res->_source->macResMan.getResource(resTypeToMacTag(res->_id.type), res->_id.number);
if (!stream)
- error("Could not get Mac resource fork resource");
+ error("Could not get Mac resource fork resource: %d %d", res->_id.type, res->_id.number);
int error = decompress(res, stream);
if (error) {
@@ -460,10 +358,9 @@ void ResourceManager::loadResource(Resource *res) {
return;
}
- Common::File *file;
- // Either loading from volume or patch loading failed
- file = getVolumeFile(res->_source->location_name.c_str());
- if (!file) {
+ Common::SeekableReadStream *fileStream = getVolumeFile(res->_source);
+
+ if (!fileStream) {
warning("Failed to open %s", res->_source->location_name.c_str());
res->unalloc();
return;
@@ -471,8 +368,10 @@ void ResourceManager::loadResource(Resource *res) {
switch(res->_source->source_type) {
case kSourceWave:
- file->seek(res->_fileOffset, SEEK_SET);
- loadFromWaveFile(res, *file);
+ fileStream->seek(res->_fileOffset, SEEK_SET);
+ loadFromWaveFile(res, fileStream);
+ if (res->_source->resourceFile)
+ delete fileStream;
return;
case kSourceAudioVolume:
@@ -503,30 +402,39 @@ void ResourceManager::loadResource(Resource *res) {
if (!compressedOffset)
error("could not translate offset to compressed offset in audio volume");
- file->seek(compressedOffset, SEEK_SET);
+ fileStream->seek(compressedOffset, SEEK_SET);
switch (res->_id.type) {
case kResourceTypeAudio:
case kResourceTypeAudio36:
// Directly read the stream, compressed audio wont have resource type id and header size for SCI1.1
- loadFromAudioVolumeSCI1(res, *file);
+ loadFromAudioVolumeSCI1(res, fileStream);
+ if (res->_source->resourceFile)
+ delete fileStream;
return;
default:
break;
}
} else {
// original file, directly seek to given offset and get SCI1/SCI1.1 audio resource
- file->seek(res->_fileOffset, SEEK_SET);
+ fileStream->seek(res->_fileOffset, SEEK_SET);
}
if (getSciVersion() < SCI_VERSION_1_1)
- loadFromAudioVolumeSCI1(res, *file);
+ loadFromAudioVolumeSCI1(res, fileStream);
else
- loadFromAudioVolumeSCI11(res, *file);
+ loadFromAudioVolumeSCI11(res, fileStream);
+
+ if (res->_source->resourceFile)
+ delete fileStream;
return;
default:
- file->seek(res->_fileOffset, SEEK_SET);
- int error = decompress(res, file);
+ fileStream->seek(res->_fileOffset, SEEK_SET);
+ int error = decompress(res, fileStream);
+
+ if (res->_source->resourceFile)
+ delete fileStream;
+
if (error) {
warning("Error %d occured while reading %s from resource file: %s",
error, res->_id.toString().c_str(), sci_error_types[error]);
@@ -539,27 +447,14 @@ Resource *ResourceManager::testResource(ResourceId id) {
return _resMap.getVal(id, NULL);
}
-int sci0_get_compression_method(Common::ReadStream &stream) {
- uint16 compressionMethod;
-
- stream.readUint16LE();
- stream.readUint16LE();
- stream.readUint16LE();
- compressionMethod = stream.readUint16LE();
- if (stream.err())
- return SCI_ERROR_IO_ERROR;
-
- return compressionMethod;
-}
-
int ResourceManager::addAppropriateSources() {
Common::ArchiveMemberList files;
- if (Common::File::exists("RESOURCE.MAP")) {
+ if (Common::File::exists("resource.map")) {
// SCI0-SCI2 file naming scheme
- ResourceSource *map = addExternalMap("RESOURCE.MAP");
+ ResourceSource *map = addExternalMap("resource.map");
- SearchMan.listMatchingMembers(files, "RESOURCE.0??");
+ SearchMan.listMatchingMembers(files, "resource.0??");
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
const Common::String name = (*x)->getName();
@@ -570,12 +465,12 @@ int ResourceManager::addAppropriateSources() {
}
#ifdef ENABLE_SCI32
// GK1CD hires content
- if (Common::File::exists("ALT.MAP") && Common::File::exists("RESOURCE.ALT"))
- addSource(addExternalMap("ALT.MAP", 10), kSourceVolume, "RESOURCE.ALT", 10);
+ if (Common::File::exists("alt.map") && Common::File::exists("resource.alt"))
+ addSource(addExternalMap("alt.map", 10), kSourceVolume, "resource.alt", 10);
#endif
} else if (Common::File::exists("Data1")) {
// Mac SCI1.1+ file naming scheme
- SearchMan.listMatchingMembers(files, "Data?");
+ SearchMan.listMatchingMembers(files, "Data?*");
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
Common::String filename = (*x)->getName();
@@ -594,8 +489,8 @@ int ResourceManager::addAppropriateSources() {
} else {
// SCI2.1-SCI3 file naming scheme
Common::ArchiveMemberList mapFiles;
- SearchMan.listMatchingMembers(mapFiles, "RESMAP.0??");
- SearchMan.listMatchingMembers(files, "RESSCI.0??");
+ SearchMan.listMatchingMembers(mapFiles, "resmap.0??");
+ SearchMan.listMatchingMembers(files, "ressci.0??");
// We need to have the same number of maps as resource archives
if (mapFiles.empty() || files.empty() || mapFiles.size() != files.size())
@@ -617,9 +512,9 @@ int ResourceManager::addAppropriateSources() {
}
// SCI2.1 resource patches
- if (Common::File::exists("RESMAP.PAT") && Common::File::exists("RESSCI.PAT")) {
+ if (Common::File::exists("resmap.pat") && Common::File::exists("ressci.pat")) {
// We add this resource with a map which surely won't exist
- addSource(addExternalMap("RESMAP.PAT", 100), kSourceVolume, "RESSCI.PAT", 100);
+ addSource(addExternalMap("resmap.pat", 100), kSourceVolume, "ressci.pat", 100);
}
}
#else
@@ -628,14 +523,18 @@ int ResourceManager::addAppropriateSources() {
#endif
addPatchDir(".");
- if (Common::File::exists("MESSAGE.MAP"))
- addSource(addExternalMap("MESSAGE.MAP"), kSourceVolume, "RESOURCE.MSG", 0);
+ if (Common::File::exists("message.map"))
+ addSource(addExternalMap("message.map"), kSourceVolume, "resource.msg", 0);
return 1;
}
int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
ResourceSource *map = 0;
+#ifdef ENABLE_SCI32
+ ResourceSource *sci21PatchMap = 0;
+ const Common::FSNode *sci21PatchRes = 0;
+#endif
// First, find resource.map
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
@@ -645,17 +544,33 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
Common::String filename = file->getName();
filename.toLowercase();
- // TODO: Load the SCI2.1+ maps (resmap.*) in concurrence with the volumes to
- // get the proper volume numbers from the maps.
- if (filename.contains("resource.map") || filename.contains("resmap.000")) {
+ if (filename.contains("resource.map"))
map = addExternalMap(file);
- break;
+
+ if (filename.contains("resmap.0")) {
+ const char *dot = strrchr(file->getName().c_str(), '.');
+ int number = atoi(dot + 1);
+ map = addExternalMap(file, number);
}
+
+#ifdef ENABLE_SCI32
+ // SCI2.1 resource patches
+ if (filename.contains("resmap.pat"))
+ sci21PatchMap = addExternalMap(file, 100);
+
+ if (filename.contains("ressci.pat"))
+ sci21PatchRes = file;
+#endif
}
if (!map)
return 0;
+#ifdef ENABLE_SCI32
+ if (sci21PatchMap && sci21PatchRes)
+ addSource(sci21PatchMap, kSourceVolume, sci21PatchRes, 100);
+#endif
+
// Now find all the resource.0?? files
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
if (file->isDirectory())
@@ -696,36 +611,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;
@@ -735,6 +620,9 @@ void ResourceManager::scanNewSources() {
switch (source->source_type) {
case kSourceDirectory:
readResourcePatches(source);
+#ifdef ENABLE_SCI32
+ readResourcePatchesBase36(source);
+#endif
readWaveAudioPatches();
break;
case kSourceExtMap:
@@ -990,7 +878,6 @@ const char *ResourceManager::versionDescription(ResVersion version) const {
ResourceManager::ResVersion ResourceManager::detectMapVersion() {
Common::SeekableReadStream *fileStream = 0;
- Common::File *file = 0;
byte buff[6];
ResourceSource *rsrc= 0;
@@ -1001,7 +888,7 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() {
if (rsrc->resourceFile) {
fileStream = rsrc->resourceFile->createReadStream();
} else {
- file = new Common::File();
+ Common::File *file = new Common::File();
file->open(rsrc->location_name);
if (file->isOpen())
fileStream = file;
@@ -1081,7 +968,6 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() {
ResourceManager::ResVersion ResourceManager::detectVolVersion() {
Common::SeekableReadStream *fileStream = 0;
- Common::File *file = 0;
ResourceSource *rsrc;
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
@@ -1091,7 +977,7 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() {
if (rsrc->resourceFile) {
fileStream = rsrc->resourceFile->createReadStream();
} else {
- file = new Common::File();
+ Common::File *file = new Common::File();
file->open(rsrc->location_name);
if (file->isOpen())
fileStream = file;
@@ -1180,27 +1066,36 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() {
}
// version-agnostic patch application
-void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, int resnumber) {
- Common::File file;
+void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, uint16 resnumber, uint32 tuple) {
+ Common::SeekableReadStream *fileStream = 0;
Resource *newrsc;
- ResourceId resId = ResourceId(restype, resnumber);
+ ResourceId resId = ResourceId(restype, resnumber, tuple);
byte patchtype, patch_data_offset;
int fsize;
- if (resnumber == -1)
- return;
- if (!file.open(source->location_name)) {
- warning("ResourceManager::processPatch(): failed to open %s", source->location_name.c_str());
+ if (resnumber == 0xFFFF)
return;
+
+ if (source->resourceFile) {
+ fileStream = source->resourceFile->createReadStream();
+ } else {
+ Common::File *file = new Common::File();
+ if (!file->open(source->location_name)) {
+ warning("ResourceManager::processPatch(): failed to open %s", source->location_name.c_str());
+ return;
+ }
+ fileStream = file;
}
- fsize = file.size();
+ fsize = fileStream->size();
if (fsize < 3) {
debug("Patching %s failed - file too small", source->location_name.c_str());
return;
}
- patchtype = file.readByte() & 0x7F;
- patch_data_offset = file.readByte();
+ patchtype = fileStream->readByte() & 0x7F;
+ patch_data_offset = fileStream->readByte();
+
+ delete fileStream;
if (patchtype != restype) {
debug("Patching %s failed - resource type mismatch", source->location_name.c_str());
@@ -1215,8 +1110,12 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType restype,
case 1:
patch_data_offset = 2;
break;
+ case 4:
+ patch_data_offset = 8;
+ break;
default:
- warning("Resource patch unsupported special case %X", patch_data_offset);
+ warning("Resource patch unsupported special case %X", patch_data_offset & 0x7F);
+ return;
}
}
@@ -1241,11 +1140,70 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType restype,
debugC(1, kDebugLevelResMan, "Patching %s - OK", source->location_name.c_str());
}
+#ifdef ENABLE_SCI32
+
+void ResourceManager::readResourcePatchesBase36(ResourceSource *source) {
+ // The base36 encoded audio36 and sync36 resources use a different naming scheme, because they
+ // cannot be described with a single resource number, but are a result of a
+ // <number, noun, verb, cond, seq> tuple. Please don't be confused with the normal audio patches
+ // (*.aud) and normal sync patches (*.syn). audio36 patches can be seen for example in the AUD
+ // folder of GK1CD, and are like this file: @0CS0M00.0X1. GK1CD is the first game where these
+ // have been observed. The actual audio36 and sync36 resources exist in SCI1.1 as well, but the
+ // first game where external patch files for them have been found is GK1CD. The names of these
+ // files are base36 encoded, and we handle their decoding here. audio36 files start with a '@',
+ // whereas sync36 start with a '#'. Mac versions begin with 'A' (probably meaning AIFF). Torin
+ // has several that begin with 'B'.
+
+ Common::String name, inputName;
+ Common::ArchiveMemberList files;
+ //ResourceSource *psrcPatch;
+
+ for (int i = kResourceTypeAudio36; i <= kResourceTypeSync36; ++i) {
+ // audio36 resources start with a @, A, or B
+ // sync36 resources start with a #
+ if (i == kResourceTypeAudio36) {
+ SearchMan.listMatchingMembers(files, "@???????.???");
+ SearchMan.listMatchingMembers(files, "A???????.???");
+ SearchMan.listMatchingMembers(files, "B???????.???");
+ } else
+ SearchMan.listMatchingMembers(files, "#???????.???");
+
+ for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
+ name = (*x)->getName();
+ inputName = (*x)->getName();
+ inputName.toUppercase();
+ inputName.deleteChar(0); // delete the first character (type)
+ inputName.deleteChar(7); // delete the dot
+
+ // The base36 encoded resource contains the following:
+ // uint16 number, byte noun, byte verb, byte cond, byte seq
+ // TODO: this is still not right (especially the tuple part, seems to be overflowing?)
+ uint16 number = strtol(Common::String(inputName.c_str(), 2).c_str(), 0, 36);
+ uint32 tuple = strtol(inputName.c_str() + 2, 0, 36);
+ ResourceId resource36((ResourceType)i, number, tuple);
+
+ if (i == kResourceTypeAudio36)
+ debug("audio36 patch: %s => %s. tuple:%d, %s\n", name.c_str(), inputName.c_str(), tuple, resource36.toString().c_str());
+ else
+ debug("sync36 patch: %s => %s. tuple:%d, %s\n", name.c_str(), inputName.c_str(), tuple, resource36.toString().c_str());
+
+ /*
+ psrcPatch = new ResourceSource;
+ psrcPatch->source_type = kSourcePatch;
+ psrcPatch->location_name = name;
+ psrcPatch->resourceFile = 0;
+ processPatch(psrcPatch, (ResourceType)i, number, tuple);
+ */
+ }
+ }
+}
+
+#endif
void ResourceManager::readResourcePatches(ResourceSource *source) {
-// Note: since some SCI1 games(KQ5 floppy, SQ4) might use SCI0 naming scheme for patch files
-// this function tries to read patch file with any supported naming scheme,
-// regardless of s_sciVersion value
+ // Note: since some SCI1 games(KQ5 floppy, SQ4) might use SCI0 naming scheme for patch files
+ // this function tries to read patch file with any supported naming scheme,
+ // regardless of s_sciVersion value
Common::String mask, name;
Common::ArchiveMemberList files;
@@ -1253,11 +1211,7 @@ void ResourceManager::readResourcePatches(ResourceSource *source) {
const char *szResType;
ResourceSource *psrcPatch;
- for (int i = kResourceTypeView; i <= kResourceTypeRobot; ++i) {
- // TODO: add support for audio36 and sync36 files
- if (i == kResourceTypeAudio36 || i == kResourceTypeSync36)
- continue;
-
+ for (int i = kResourceTypeView; i <= kResourceTypeHeap; ++i) {
files.clear();
szResType = getResourceTypeName((ResourceType)i);
// SCI0 naming - type.nnn
@@ -1268,6 +1222,7 @@ void ResourceManager::readResourcePatches(ResourceSource *source) {
mask = "*.";
mask += resourceTypeSuffixes[i];
SearchMan.listMatchingMembers(files, mask);
+
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
bool bAdd = false;
name = (*x)->getName();
@@ -1289,75 +1244,42 @@ void ResourceManager::readResourcePatches(ResourceSource *source) {
psrcPatch = new ResourceSource;
psrcPatch->source_type = kSourcePatch;
psrcPatch->location_name = name;
+ psrcPatch->resourceFile = 0;
processPatch(psrcPatch, (ResourceType)i, number);
}
}
}
}
-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;
+ Common::SeekableReadStream *fileStream = 0;
Resource *res;
ResourceType type;
uint16 number, id;
uint32 offset;
- if (!file.open(map->location_name))
- return SCI_ERROR_RESMAP_NOT_FOUND;
+ if (map->resourceFile) {
+ fileStream = map->resourceFile->createReadStream();
+ if (!fileStream)
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ } else {
+ Common::File *file = new Common::File();
+ if (!file->open(map->location_name))
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ fileStream = file;
+ }
- file.seek(0, SEEK_SET);
+ fileStream->seek(0, SEEK_SET);
byte bMask = (_mapVersion == kResVersionSci1Middle) ? 0xF0 : 0xFC;
byte bShift = (_mapVersion == kResVersionSci1Middle) ? 28 : 26;
do {
- id = file.readUint16LE();
- offset = file.readUint32LE();
+ id = fileStream->readUint16LE();
+ offset = fileStream->readUint32LE();
- if (file.eos() || file.err()) {
+ if (fileStream->eos() || fileStream->err()) {
+ delete fileStream;
warning("Error while reading %s", map->location_name.c_str());
return SCI_ERROR_RESMAP_NOT_FOUND;
}
@@ -1386,15 +1308,26 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map) {
res->_id = resId;
_resMap.setVal(resId, res);
}
- } while (!file.eos());
+ } while (!fileStream->eos());
+
+ delete fileStream;
return 0;
}
int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
- Common::File file;
+ Common::SeekableReadStream *fileStream = 0;
Resource *res;
- if (!file.open(map->location_name))
- return SCI_ERROR_RESMAP_NOT_FOUND;
+
+ if (map->resourceFile) {
+ fileStream = map->resourceFile->createReadStream();
+ if (!fileStream)
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ } else {
+ Common::File *file = new Common::File();
+ if (!file->open(map->location_name))
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+ fileStream = file;
+ }
resource_index_t resMap[32];
memset(resMap, 0, sizeof(resource_index_t) * 32);
@@ -1405,8 +1338,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
// Read resource type and offsets to resource offsets block from .MAP file
// The last entry has type=0xFF (0x1F) and offset equals to map file length
do {
- type = file.readByte() & 0x1F;
- resMap[type].wOffset = file.readUint16LE();
+ type = fileStream->readByte() & 0x1F;
+ resMap[type].wOffset = fileStream->readUint16LE();
resMap[prevtype].wSize = (resMap[type].wOffset
- resMap[prevtype].wOffset) / nEntrySize;
prevtype = type;
@@ -1417,18 +1350,18 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
for (type = 0; type < 32; type++) {
if (resMap[type].wOffset == 0) // this resource does not exist in map
continue;
- file.seek(resMap[type].wOffset);
+ fileStream->seek(resMap[type].wOffset);
for (int i = 0; i < resMap[type].wSize; i++) {
- uint16 number = file.readUint16LE();
+ uint16 number = fileStream->readUint16LE();
int volume_nr = 0;
if (_mapVersion == kResVersionSci11) {
// offset stored in 3 bytes
- off = file.readUint16LE();
- off |= file.readByte() << 16;
+ off = fileStream->readUint16LE();
+ off |= fileStream->readByte() << 16;
off <<= 1;
} else {
// offset/volume stored in 4 bytes
- off = file.readUint32LE();
+ off = fileStream->readUint32LE();
if (_mapVersion < kResVersionSci11) {
volume_nr = off >> 28; // most significant 4 bits
off &= 0x0FFFFFFF; // least significant 28 bits
@@ -1436,7 +1369,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
// in SCI32 it's a plain offset
}
}
- if (file.eos() || file.err()) {
+ if (fileStream->eos() || fileStream->err()) {
+ delete fileStream;
warning("Error while reading %s", map->location_name.c_str());
return SCI_ERROR_RESMAP_NOT_FOUND;
}
@@ -1448,7 +1382,7 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
res->_id = resId;
// NOTE: We add the map's volume number here to the specified volume number
- // for SCI2.1 and SCI3 maps that are not RESMAP.000. The RESMAP.* files' numbers
+ // for SCI2.1 and SCI3 maps that are not resmap.000. The resmap.* files' numbers
// need to be used in concurrence with the volume specified in the map to get
// the actual resource file.
res->_source = getVolume(map, volume_nr + map->volume_number);
@@ -1456,6 +1390,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
}
}
}
+
+ delete fileStream;
return 0;
}
@@ -1476,7 +1412,11 @@ struct {
{ MKID_BE('PAL '), kResourceTypePalette },
{ MKID_BE('snd '), kResourceTypeAudio },
{ MKID_BE('MSG '), kResourceTypeMessage },
- { MKID_BE('HEP '), kResourceTypeHeap }
+ { MKID_BE('HEP '), kResourceTypeHeap },
+ { MKID_BE('IBIN'), kResourceTypeMacIconBarPictN },
+ { MKID_BE('IBIS'), kResourceTypeMacIconBarPictS },
+ { MKID_BE('PICT'), kResourceTypeMacPict },
+ { MKID_BE('SYN '), kResourceTypeSync }
};
static uint32 resTypeToMacTag(ResourceType type) {
@@ -1509,6 +1449,16 @@ int ResourceManager::readMacResourceFork(ResourceSource *source) {
Common::MacResIDArray idArray = source->macResMan.getResIDArray(tagArray[i]);
for (uint32 j = 0; j < idArray.size(); j++) {
+ // Get the size of the file
+ Common::SeekableReadStream *stream = source->macResMan.getResource(tagArray[i], idArray[j]);
+
+ // Some IBIS resources have a size of 0, so we skip them
+ if (!stream)
+ continue;
+
+ uint32 fileSize = stream->size();
+ delete stream;
+
ResourceId resId = ResourceId(type, idArray[j]);
Resource *newrsc = NULL;
@@ -1520,11 +1470,6 @@ int ResourceManager::readMacResourceFork(ResourceSource *source) {
} else
newrsc = _resMap.getVal(resId);
- // Get the size of the file
- Common::SeekableReadStream *stream = source->macResMan.getResource(tagArray[i], idArray[j]);
- uint32 fileSize = stream->size();
- delete stream;
-
// Overwrite everything
newrsc->_id = resId;
newrsc->_status = kResStatusNoMalloc;
@@ -1549,261 +1494,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
@@ -1954,7 +1644,7 @@ ResourceCompression ResourceManager::getViewCompression() {
// Test 10 views to see if any are compressed
for (int i = 0; i < 1000; i++) {
- Common::File *file;
+ Common::SeekableReadStream *fileStream = 0;
Resource *res = testResource(ResourceId(kResourceTypeView, i));
if (!res)
@@ -1963,16 +1653,23 @@ ResourceCompression ResourceManager::getViewCompression() {
if (res->_source->source_type != kSourceVolume)
continue;
- file = getVolumeFile(res->_source->location_name.c_str());
- if (!file)
+ fileStream = getVolumeFile(res->_source);
+
+ if (!fileStream)
continue;
- file->seek(res->_fileOffset, SEEK_SET);
+ fileStream->seek(res->_fileOffset, SEEK_SET);
uint32 szPacked;
ResourceCompression compression;
- if (readResourceInfo(res, file, szPacked, compression))
+ if (readResourceInfo(res, fileStream, szPacked, compression)) {
+ if (res->_source->resourceFile)
+ delete fileStream;
continue;
+ }
+
+ if (res->_source->resourceFile)
+ delete fileStream;
if (compression != kCompNone)
return compression;
@@ -2329,248 +2026,98 @@ bool ResourceManager::hasSci1Voc900() {
return offset == res->size;
}
-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;
+// Same function as Script::findBlock(). Slight code
+// duplication here, but this has been done to keep the resource
+// manager independent from the rest of the engine
+static byte *findSci0ExportsBlock(byte *buffer) {
+ byte *buf = buffer;
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
- _resMan->unlockResource(_innerResource);
-}
+ if (oldScriptHeader)
+ buf += 2;
-#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
+ do {
+ int seekerType = READ_LE_UINT16(buf);
-SoundResource::Track *SoundResource::getTrackByType(byte type) {
- if (_soundVersion <= SCI_VERSION_0_LATE)
- return &_tracks[0];
+ if (seekerType == 0)
+ break;
+ if (seekerType == 7) // exports
+ return buf;
- for (int trackNr = 0; trackNr < _trackCount; trackNr++) {
- if (_tracks[trackNr].type == type)
- return &_tracks[trackNr];
- }
- return NULL;
-}
+ int seekerSize = READ_LE_UINT16(buf + 2);
+ assert(seekerSize > 0);
+ buf += seekerSize;
+ } while (1);
-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;
+reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) {
+ Resource *script = findResource(ResourceId(kResourceTypeScript, 0), false);
- data++; // Skip over digital sample flag
+ if (!script)
+ return NULL_REG;
- for (int channelNr = 0; channelNr < 16; channelNr++) {
- channelMask = channelMask >> 1;
+ byte *offsetPtr = 0;
- byte flags;
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ byte *buf = (getSciVersion() == SCI_VERSION_0_EARLY) ? script->data + 2 : script->data;
- 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;
+ // Check if the first block is the exports block (in most cases, it is)
+ bool exportsIsFirst = (READ_LE_UINT16(buf + 4) == 7);
+ if (exportsIsFirst) {
+ offsetPtr = buf + 4 + 2;
} 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++;
+ offsetPtr = findSci0ExportsBlock(script->data);
+ if (!offsetPtr)
+ error("Unable to find exports block from script 0");
+ offsetPtr += 4 + 2;
}
+ } else {
+ offsetPtr = script->data + 4 + 2 + 2;
+ }
+
+ int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr);
- 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;
- }
+ // In SCI1.1 and newer, the heap is appended at the end of the script,
+ // so adjust the offset accordingly
+ if (getSciVersion() >= SCI_VERSION_1_1 && addSci11ScriptOffset) {
+ offset += script->size;
- if (play) {
- // This Channel is supposed to be played by the hardware
- channelMask |= 0x8000;
- }
+ // Ensure that the start of the heap is word-aligned - same as in Script::init()
+ if (script->size & 2)
+ offset++;
}
- return channelMask;
+ return make_reg(1, offset);
}
-byte SoundResource::getInitialVoiceCount(byte channel) {
- byte *data = _innerResource->data;
+Common::String ResourceManager::findSierraGameId() {
+ // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1+, it's separated
+ Resource *heap = 0;
+ int nameSelector = 3;
- if (_soundVersion > SCI_VERSION_0_LATE)
- return 0; // TODO
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ heap = findResource(ResourceId(kResourceTypeScript, 0), false);
+ } else {
+ heap = findResource(ResourceId(kResourceTypeHeap, 0), false);
+ nameSelector += 5;
+ }
- data++; // Skip over digital sample flag
+ if (!heap)
+ return "";
- if (_soundVersion == SCI_VERSION_0_EARLY)
- return data[channel] >> 4;
- else
- return data[channel * 2];
+ int16 gameObjectOffset = findGameObject(false).offset;
+
+ if (!gameObjectOffset)
+ return "";
+
+ // Seek to the name selector of the first export
+ byte *seeker = heap->data + READ_UINT16(heap->data + gameObjectOffset + nameSelector * 2);
+ Common::String sierraId;
+ sierraId += (const char *)seeker;
+
+ return sierraId;
}
} // End of namespace Sci