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.cpp185
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);