diff options
Diffstat (limited to 'engines/sci/resource.cpp')
-rw-r--r-- | engines/sci/resource.cpp | 185 |
1 files changed, 121 insertions, 64 deletions
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 2a4cd95b91..364ea2a0ac 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -26,6 +26,9 @@ #include "common/fs.h" #include "common/macresman.h" #include "common/textconsole.h" +#ifdef ENABLE_SCI32 +#include "common/memstream.h" +#endif #include "sci/resource.h" #include "sci/resource_intern.h" @@ -156,7 +159,7 @@ static const ResourceType s_resTypeMapSci21[] = { kResourceTypeView, kResourceTypePic, kResourceTypeScript, kResourceTypeAnimation, // 0x00-0x03 kResourceTypeSound, kResourceTypeEtc, kResourceTypeVocab, kResourceTypeFont, // 0x04-0x07 kResourceTypeCursor, kResourceTypePatch, kResourceTypeBitmap, kResourceTypePalette, // 0x08-0x0B - kResourceTypeInvalid, kResourceTypeAudio, kResourceTypeSync, kResourceTypeMessage, // 0x0C-0x0F + kResourceTypeAudio, kResourceTypeAudio, kResourceTypeSync, kResourceTypeMessage, // 0x0C-0x0F kResourceTypeMap, kResourceTypeHeap, kResourceTypeChunk, kResourceTypeAudio36, // 0x10-0x13 kResourceTypeSync36, kResourceTypeTranslation, kResourceTypeRobot, kResourceTypeVMD, // 0x14-0x17 kResourceTypeDuck, kResourceTypeClut, kResourceTypeTGA, kResourceTypeZZZ // 0x18-0x1B @@ -221,6 +224,12 @@ void Resource::writeToStream(Common::WriteStream *stream) const { stream->write(data, size); } +#ifdef ENABLE_SCI32 +Common::SeekableReadStream *Resource::makeStream() const { + return new Common::MemoryReadStream(data, size, DisposeAfterUse::NO); +} +#endif + uint32 Resource::getAudioCompressionType() const { return _source->getAudioCompressionType(); } @@ -229,7 +238,6 @@ uint32 AudioVolumeResourceSource::getAudioCompressionType() const { return _audioCompressionType; } - ResourceSource::ResourceSource(ResSourceType type, const Common::String &name, int volNum, const Common::FSNode *resFile) : _sourceType(type), _name(name), _volumeNumber(volNum), _resourceFile(resFile) { _scanned = false; @@ -293,32 +301,30 @@ ResourceSource *ResourceManager::findVolume(ResourceSource *map, int volume_nr) // Resource manager constructors and operations bool Resource::loadPatch(Common::SeekableReadStream *file) { - Resource *res = this; - - // We assume that the resource type matches res->type + // We assume that the resource type matches `type` // We also assume that the current file position is right at the actual data (behind resourceid/headersize byte) - res->data = new byte[res->size]; + data = new byte[size]; - if (res->_headerSize > 0) - res->_header = new byte[res->_headerSize]; + if (_headerSize > 0) + _header = new byte[_headerSize]; - if ((res->data == NULL) || ((res->_headerSize > 0) && (res->_header == NULL))) { - error("Can't allocate %d bytes needed for loading %s", res->size + res->_headerSize, res->_id.toString().c_str()); + if (data == nullptr || (_headerSize > 0 && _header == nullptr)) { + error("Can't allocate %d bytes needed for loading %s", size + _headerSize, _id.toString().c_str()); } - unsigned int really_read; - if (res->_headerSize > 0) { - 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); + uint32 really_read; + if (_headerSize > 0) { + really_read = file->read(_header, _headerSize); + if (really_read != _headerSize) + error("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), _headerSize); } - 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); + really_read = file->read(data, size); + if (really_read != size) + error("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size); - res->_status = kResStatusAllocated; + _status = kResStatusAllocated; return true; } @@ -565,12 +571,14 @@ Resource *ResourceManager::testResource(ResourceId id) { } int ResourceManager::addAppropriateSources() { - Common::ArchiveMemberList files; - +#ifdef ENABLE_SCI32 + _multiDiscAudio = false; +#endif if (Common::File::exists("resource.map")) { // SCI0-SCI2 file naming scheme ResourceSource *map = addExternalMap("resource.map"); + Common::ArchiveMemberList files; SearchMan.listMatchingMembers(files, "resource.0??"); for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { @@ -587,20 +595,20 @@ int ResourceManager::addAppropriateSources() { #endif } else if (Common::MacResManager::exists("Data1")) { // Mac SCI1.1+ file naming scheme - SearchMan.listMatchingMembers(files, "Data?*"); + Common::StringArray files; + Common::MacResManager::listFiles(files, "Data?"); - for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { - Common::String filename = (*x)->getName(); - addSource(new MacResourceForkResourceSource(filename, atoi(filename.c_str() + 4))); + for (Common::StringArray::const_iterator x = files.begin(); x != files.end(); ++x) { + addSource(new MacResourceForkResourceSource(*x, atoi(x->c_str() + 4))); } #ifdef ENABLE_SCI32 // There can also be a "Patches" resource fork with patches - if (Common::File::exists("Patches")) + if (Common::MacResManager::exists("Patches")) addSource(new MacResourceForkResourceSource("Patches", 100)); } else { // SCI2.1-SCI3 file naming scheme - Common::ArchiveMemberList mapFiles; + Common::ArchiveMemberList mapFiles, files; SearchMan.listMatchingMembers(mapFiles, "resmap.0??"); SearchMan.listMatchingMembers(files, "ressci.0??"); @@ -608,6 +616,10 @@ int ResourceManager::addAppropriateSources() { if (mapFiles.empty() || files.empty() || mapFiles.size() != files.size()) return 0; + if (Common::File::exists("resaud.001")) { + _multiDiscAudio = true; + } + for (Common::ArchiveMemberList::const_iterator mapIterator = mapFiles.begin(); mapIterator != mapFiles.end(); ++mapIterator) { Common::String mapName = (*mapIterator)->getName(); int mapNumber = atoi(strrchr(mapName.c_str(), '.') + 1); @@ -652,6 +664,7 @@ int ResourceManager::addAppropriateSourcesForDetection(const Common::FSList &fsl #ifdef ENABLE_SCI32 ResourceSource *sci21PatchMap = 0; const Common::FSNode *sci21PatchRes = 0; + _multiDiscAudio = false; #endif // First, find resource.map @@ -852,6 +865,13 @@ void ResourceManager::addResourcesFromChunk(uint16 id) { scanNewSources(); } +void ResourceManager::findDisc(const int16 discNo) { + // Since all resources are expected to be copied from the original discs + // into a single game directory, this call just records the number of the CD + // that the game has requested + _currentDiscNo = discNo; +} + #endif void ResourceManager::freeResourceSources() { @@ -871,7 +891,9 @@ void ResourceManager::init() { _LRU.clear(); _resMap.clear(); _audioMapSCI1 = NULL; - +#ifdef ENABLE_SCI32 + _currentDiscNo = 1; +#endif // FIXME: put this in an Init() function, so that we can error out if detection fails completely _mapVersion = detectMapVersion(); @@ -924,7 +946,7 @@ void ResourceManager::init() { // cache, leading to constant decompression of picture resources // and making the renderer very slow. if (getSciVersion() >= SCI_VERSION_2) { - _maxMemoryLRU = 2048 * 1024; // 2MiB + _maxMemoryLRU = 4096 * 1024; // 4MiB } switch (_viewType) { @@ -1044,7 +1066,13 @@ Resource *ResourceManager::findResource(ResourceId id, bool lock) { if (retval->_status == kResStatusNoMalloc) loadResource(retval); else if (retval->_status == kResStatusEnqueued) + // The resource is removed from its current position + // in the LRU list because it has been requested + // again. Below, it will either be locked, or it + // will be added back to the LRU list at the 'most + // recent' position. removeFromLRU(retval); + // Unless an error occurred, the resource is now either // locked or allocated, but never queued or freed. @@ -1353,6 +1381,8 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType resource Common::File *file = new Common::File(); if (!file->open(source->getLocationName())) { warning("ResourceManager::processPatch(): failed to open %s", source->getLocationName().c_str()); + delete source; + delete file; return; } fileStream = file; @@ -1361,16 +1391,32 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType resource int fsize = fileStream->size(); if (fsize < 3) { debug("Patching %s failed - file too small", source->getLocationName().c_str()); + delete source; + delete fileStream; return; } byte patchType = convertResType(fileStream->readByte()); - byte patchDataOffset = fileStream->readByte(); + int32 patchDataOffset; + if (_volVersion < kResVersionSci2) { + patchDataOffset = fileStream->readByte(); + } else if (patchType == kResourceTypeView) { + fileStream->seek(3, SEEK_SET); + patchDataOffset = fileStream->readByte() + 22 + 2; + } else if (patchType == kResourceTypePic) { + patchDataOffset = 2; + } else if (patchType == kResourceTypePalette) { + fileStream->seek(3, SEEK_SET); + patchDataOffset = fileStream->readByte() + 2; + } else { + patchDataOffset = 0; + } delete fileStream; if (patchType != checkForType) { debug("Patching %s failed - resource type mismatch", source->getLocationName().c_str()); + delete source; return; } @@ -1395,6 +1441,7 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType resource if (patchDataOffset + 2 >= fsize) { debug("Patching %s failed - patch starting at offset %d can't be in file of size %d", source->getLocationName().c_str(), patchDataOffset + 2, fsize); + delete source; return; } @@ -1442,17 +1489,28 @@ void ResourceManager::readResourcePatchesBase36() { files.clear(); // audio36 resources start with a @, A, or B - // sync36 resources start with a # + // sync36 resources start with a #, S, or T if (i == kResourceTypeAudio36) { SearchMan.listMatchingMembers(files, "@???????.???"); SearchMan.listMatchingMembers(files, "A???????.???"); SearchMan.listMatchingMembers(files, "B???????.???"); - } else + } else { SearchMan.listMatchingMembers(files, "#???????.???"); +#ifdef ENABLE_SCI32 + SearchMan.listMatchingMembers(files, "S???????.???"); + SearchMan.listMatchingMembers(files, "T???????.???"); +#endif + } for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { name = (*x)->getName(); + // The S/T prefixes often conflict with non-patch files and generate + // spurious warnings about invalid patches + if (name.hasSuffix(".DLL") || name.hasSuffix(".EXE") || name.hasSuffix(".TXT")) { + continue; + } + ResourceId resource36 = convertPatchNameBase36((ResourceType)i, name); /* @@ -1714,11 +1772,42 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { // if we use the first entries in the resource file, half of the // game will be English and umlauts will also be missing :P if (resource->_source->getSourceType() == kSourceVolume) { + // Maps are read during the scanning process (below), so + // need to be treated as unallocated in order for the new + // data from this volume to be picked up and used + if (resId.getType() == kResourceTypeMap) { + resource->_status = kResStatusNoMalloc; + } resource->_source = source; resource->_fileOffset = fileOffset; resource->size = 0; } } + +#ifdef ENABLE_SCI32 + // Different CDs may have different audio maps on each disc. The + // ResourceManager does not know how to deal with this; it expects + // each resource ID to be unique across an entire game. To work + // around this problem, all audio maps from this disc must be + // processed immediately, since they will be replaced by the audio + // map from the next disc on the next call to readResourceMapSCI1 + if (_multiDiscAudio && resId.getType() == kResourceTypeMap) { + IntMapResourceSource *audioMap = static_cast<IntMapResourceSource *>(addSource(new IntMapResourceSource("MAP", mapVolumeNr, resId.getNumber()))); + Common::String volumeName; + if (resId.getNumber() == 65535) { + volumeName = Common::String::format("RESSFX.%03d", mapVolumeNr); + } else { + volumeName = Common::String::format("RESAUD.%03d", mapVolumeNr); + } + + ResourceSource *audioVolume = addSource(new AudioVolumeResourceSource(this, volumeName, audioMap, mapVolumeNr)); + if (!audioMap->_scanned) { + audioVolume->_scanned = true; + audioMap->_scanned = true; + audioMap->scanSource(this); + } + } +#endif } } @@ -2381,38 +2470,6 @@ void ResourceManager::detectSciVersion() { } } -bool ResourceManager::detectHires() { - // SCI 1.1 and prior is never hires - if (getSciVersion() <= SCI_VERSION_1_1) - return false; - -#ifdef ENABLE_SCI32 - for (int i = 0; i < 32768; i++) { - Resource *res = findResource(ResourceId(kResourceTypePic, i), 0); - - if (res) { - if (READ_SCI11ENDIAN_UINT16(res->data) == 0x0e) { - // SCI32 picture - uint16 width = READ_SCI11ENDIAN_UINT16(res->data + 10); - uint16 height = READ_SCI11ENDIAN_UINT16(res->data + 12); - // Surely lowres (e.g. QFG4CD) - if ((width == 320) && ((height == 190) || (height == 200))) - return false; - // Surely hires - if ((width >= 600) || (height >= 400)) - return true; - } - } - } - - // We haven't been able to find hires content - - return false; -#else - error("no sci32 support"); -#endif -} - bool ResourceManager::detectFontExtended() { Resource *res = findResource(ResourceId(kResourceTypeFont, 0), 0); |