[Scummvm-git-logs] scummvm master -> be0ee20b4aec6fefb6fcb26042488dd75131a2a2

sev- noreply at scummvm.org
Tue Aug 29 22:31:04 UTC 2023


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
9892bedc61 COMMON: Add createReadStreamForAltStream to open Mac resource fork and metadata streams
be0ee20b4a MTROPOLIS: Fix SPQR Mac not booting


Commit: 9892bedc61d8fee94587f80a589960ef6876ce02
    https://github.com/scummvm/scummvm/commit/9892bedc61d8fee94587f80a589960ef6876ce02
Author: elasota (ejlasota at gmail.com)
Date: 2023-08-30T00:31:00+02:00

Commit Message:
COMMON: Add createReadStreamForAltStream to open Mac resource fork and metadata streams

Changed paths:
    backends/fs/abstract-fs.cpp
    backends/fs/abstract-fs.h
    backends/fs/posix/posix-fs.cpp
    backends/fs/posix/posix-fs.h
    common/archive.cpp
    common/archive.h
    common/compression/gentee_installer.cpp
    common/compression/stuffit.cpp
    common/compression/vise.cpp
    common/formats/prodos.cpp
    common/formats/prodos.h
    common/fs.cpp
    common/fs.h
    common/macresman.cpp
    engines/grim/lab.h
    engines/mm/shared/utils/engine_data.cpp
    engines/stark/formats/xarc.cpp
    engines/ultima/shared/engine/data_archive.cpp
    engines/wintermute/base/file/base_file_entry.cpp
    engines/wintermute/base/file/base_file_entry.h


diff --git a/backends/fs/abstract-fs.cpp b/backends/fs/abstract-fs.cpp
index 3feb04a1553..b5b8c44542a 100644
--- a/backends/fs/abstract-fs.cpp
+++ b/backends/fs/abstract-fs.cpp
@@ -35,3 +35,7 @@ const char *AbstractFSNode::lastPathComponent(const Common::String &str, const c
 
 	return cur + 1;
 }
+
+Common::SeekableReadStream *AbstractFSNode::createReadStreamForAltStream(Common::AltStreamType altStreamType) {
+	return nullptr;
+}
diff --git a/backends/fs/abstract-fs.h b/backends/fs/abstract-fs.h
index 3458a61fc69..54831079e89 100644
--- a/backends/fs/abstract-fs.h
+++ b/backends/fs/abstract-fs.h
@@ -182,6 +182,16 @@ public:
 	 */
 	virtual Common::SeekableReadStream *createReadStream() = 0;
 
+	/**
+	 * Creates a SeekableReadStream instance corresponding to an alternate
+	 * stream of the file referred by this node. This assumes that the node
+	 * actually refers to a readable file and the alt stream exists.
+	 * If either is not the case, 0 is returned.
+	 *
+	 * @return pointer to the stream object, 0 in case of a failure
+	 */
+	virtual Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType);
+
 	/**
 	 * Creates a WriteStream instance corresponding to the file
 	 * referred by this node. This assumes that the node actually refers
diff --git a/backends/fs/posix/posix-fs.cpp b/backends/fs/posix/posix-fs.cpp
index 4db92eb7e96..d63af64a5fd 100644
--- a/backends/fs/posix/posix-fs.cpp
+++ b/backends/fs/posix/posix-fs.cpp
@@ -269,6 +269,17 @@ Common::SeekableReadStream *POSIXFilesystemNode::createReadStream() {
 	return PosixIoStream::makeFromPath(getPath(), false);
 }
 
+Common::SeekableReadStream *POSIXFilesystemNode::createReadStreamForAltStream(Common::AltStreamType altStreamType) {
+#ifdef MACOSX
+	if (altStreamType == Common::AltStreamType::MacResourceFork) {
+		// Check the actual fork on a Mac computer
+		return PosixIoStream::makeFromPath(getPath() + "/..namedfork/rsrc", false);
+	}
+#endif
+
+	return nullptr;
+}
+
 Common::SeekableWriteStream *POSIXFilesystemNode::createWriteStream() {
 	return PosixIoStream::makeFromPath(getPath(), true);
 }
diff --git a/backends/fs/posix/posix-fs.h b/backends/fs/posix/posix-fs.h
index 3f70a233229..f1ed9607e84 100644
--- a/backends/fs/posix/posix-fs.h
+++ b/backends/fs/posix/posix-fs.h
@@ -66,6 +66,7 @@ public:
 	AbstractFSNode *getParent() const override;
 
 	Common::SeekableReadStream *createReadStream() override;
+	Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) override;
 	Common::SeekableWriteStream *createWriteStream() override;
 	bool createDirectory() override;
 
diff --git a/common/archive.cpp b/common/archive.cpp
index ae50f6492ae..e882cd88d4a 100644
--- a/common/archive.cpp
+++ b/common/archive.cpp
@@ -54,6 +54,9 @@ SeekableReadStream *GenericArchiveMember::createReadStream() const {
 	return _parent.createReadStreamForMember(_path);
 }
 
+SeekableReadStream *GenericArchiveMember::createReadStreamForAltStream(AltStreamType altStreamType) const {
+	return _parent.createReadStreamForMemberAltStream(_path, altStreamType);
+}
 
 int Archive::listMatchingMembers(ArchiveMemberList &list, const Path &pattern, bool matchPathComponents) const {
 	// Get all "names" (TODO: "files" ?)
@@ -80,6 +83,10 @@ int Archive::listMatchingMembers(ArchiveMemberList &list, const Path &pattern, b
 	return matches;
 }
 
+SeekableReadStream *Archive::createReadStreamForMemberAltStream(const Path &path, AltStreamType altStreamType) const {
+	return nullptr;
+}
+
 Common::Error Archive::dumpArchive(String destPath) {
 	Common::ArchiveMemberList files;
 
@@ -135,17 +142,34 @@ char Archive::getPathSeparator() const {
 }
 
 SeekableReadStream *MemcachingCaseInsensitiveArchive::createReadStreamForMember(const Path &path) const {
-	String translated = translatePath(path);
+	return createReadStreamForMemberImpl(path, false, Common::AltStreamType::Invalid);
+}
+
+SeekableReadStream *MemcachingCaseInsensitiveArchive::createReadStreamForMemberAltStream(const Path &path, Common::AltStreamType altStreamType) const {
+	// There is no situation where an invalid alt stream should be returning anything unless the implementation
+	// of readContentsForPathAltStream is broken, and attempting that will break the cache keying since we used Invalid
+	// for keying the primary stream.
+	if (altStreamType == Common::AltStreamType::Invalid)
+		return nullptr;
+
+	return createReadStreamForMemberImpl(path, true, altStreamType);
+}
+
+SeekableReadStream *MemcachingCaseInsensitiveArchive::createReadStreamForMemberImpl(const Path &path, bool isAltStream, Common::AltStreamType altStreamType) const {
+	CacheKey cacheKey;
+	cacheKey.path = translatePath(path);
+	cacheKey.altStreamType = isAltStream ? altStreamType : AltStreamType::Invalid;
+
 	bool isNew = false;
-	if (!_cache.contains(translated)) {
-		SharedArchiveContents readResult = readContentsForPath(translated);
+	if (!_cache.contains(cacheKey)) {
+		SharedArchiveContents readResult = isAltStream ? readContentsForPathAltStream(cacheKey.path, altStreamType) : readContentsForPath(cacheKey.path);
 		if (readResult._bypass)
 			return readResult._bypass;
-		_cache[translated] = readResult;
+		_cache[cacheKey] = readResult;
 		isNew = true;
 	}
 
-	SharedArchiveContents* entry = &_cache[translated];
+	SharedArchiveContents* entry = &_cache[cacheKey];
 
 	// Errors and missing files. Just return nullptr,
 	// no need to create stream.
@@ -155,11 +179,11 @@ SeekableReadStream *MemcachingCaseInsensitiveArchive::createReadStreamForMember(
 	// Check whether the entry is still valid as WeakPtr might have expired.
 	if (!entry->makeStrong()) {
 		// If it's expired, recreate the entry.
-		SharedArchiveContents readResult = readContentsForPath(translated);
+		SharedArchiveContents readResult = isAltStream ? readContentsForPathAltStream(cacheKey.path, altStreamType) : readContentsForPath(cacheKey.path);
 		if (readResult._bypass)
 			return readResult._bypass;
-		_cache[translated] = readResult;
-		entry = &_cache[translated];
+		_cache[cacheKey] = readResult;
+		entry = &_cache[cacheKey];
 		isNew = true;
 	}
 
@@ -180,6 +204,20 @@ SeekableReadStream *MemcachingCaseInsensitiveArchive::createReadStreamForMember(
 	return memStream;
 }
 
+SharedArchiveContents MemcachingCaseInsensitiveArchive::readContentsForPathAltStream(const String &translatedPath, AltStreamType altStreamType) const {
+	return SharedArchiveContents();
+}
+
+MemcachingCaseInsensitiveArchive::CacheKey::CacheKey() : altStreamType(AltStreamType::Invalid) {
+}
+
+bool MemcachingCaseInsensitiveArchive::CacheKey_EqualTo::operator()(const CacheKey &x, const CacheKey &y) const {
+	return (x.altStreamType == y.altStreamType) && x.path.equalsIgnoreCase(y.path);
+}
+
+uint MemcachingCaseInsensitiveArchive::CacheKey_Hash::operator()(const CacheKey &x) const {
+	return static_cast<uint>(hashit_lower(x.path) * 1000003u) ^ static_cast<uint>(x.altStreamType);
+};
 
 SearchSet::ArchiveNodeList::iterator SearchSet::find(const String &name) {
 	ArchiveNodeList::iterator it = _list.begin();
@@ -415,6 +453,20 @@ SeekableReadStream *SearchSet::createReadStreamForMember(const Path &path) const
 	return nullptr;
 }
 
+SeekableReadStream *SearchSet::createReadStreamForMemberAltStream(const Path &path, AltStreamType altStreamType) const {
+	if (path.empty())
+		return nullptr;
+
+	ArchiveNodeList::const_iterator it = _list.begin();
+	for (; it != _list.end(); ++it) {
+		SeekableReadStream *stream = it->_arc->createReadStreamForMemberAltStream(path, altStreamType);
+		if (stream)
+			return stream;
+	}
+
+	return nullptr;
+}
+
 SeekableReadStream *SearchSet::createReadStreamForMemberNext(const Path &path, const Archive *starting) const {
 	if (path.empty())
 		return nullptr;
diff --git a/common/archive.h b/common/archive.h
index 96f07d79ddd..6624c73ad71 100644
--- a/common/archive.h
+++ b/common/archive.h
@@ -46,6 +46,13 @@ namespace Common {
 class FSNode;
 class SeekableReadStream;
 
+enum class AltStreamType {
+	Invalid,
+
+	MacFinderInfo,
+	MacResourceFork,
+};
+
 
 /**
  * The ArchiveMember class is an abstract interface to represent elements inside
@@ -59,6 +66,7 @@ class ArchiveMember {
 public:
 	virtual ~ArchiveMember() { }
 	virtual SeekableReadStream *createReadStream() const = 0; /*!< Create a read stream. */
+	virtual SeekableReadStream *createReadStreamForAltStream(AltStreamType altStreamType) const = 0; /*!< Create a read stream of an alternate stream. */
 
 	/**
 	* @deprecated Get the name of the archive member.  This may be a file name or a full path depending on archive type.
@@ -112,6 +120,7 @@ public:
 	Path getPathInArchive() const override;       /*!< Get the full path of the archive member relative to the containing archive root. */
 	String getFileName() const override; /*!< Get the file name of the archive member relative to its containing directory within the archive. */
 	SeekableReadStream *createReadStream() const override; /*!< Create a read stream. */
+	SeekableReadStream *createReadStreamForAltStream(AltStreamType altStreamType) const override; /*!< Create a read stream of an alternate stream. */
 
 private:
 	const Archive &_parent;
@@ -167,6 +176,14 @@ public:
 	 */
 	virtual SeekableReadStream *createReadStreamForMember(const Path &path) const = 0;
 
+	/**
+	 * Create a stream bound to an alternate stream of a member with the specified
+	 * name in the archive. If no member with this name exists, 0 is returned.
+	 *
+	 * @return The newly created input stream.
+	 */
+	virtual SeekableReadStream *createReadStreamForMemberAltStream(const Path &path, AltStreamType altStreamType) const;
+
 	/**
 	 * For most archives: same as previous. For SearchSet see SearchSet
 	 * documentation.
@@ -243,6 +260,7 @@ class MemcachingCaseInsensitiveArchive : public Archive {
 public:
 	MemcachingCaseInsensitiveArchive(uint32 maxStronglyCachedSize = 512) : _maxStronglyCachedSize(maxStronglyCachedSize) {}
 	SeekableReadStream *createReadStreamForMember(const Path &path) const;
+	SeekableReadStream *createReadStreamForMemberAltStream(const Path &path, Common::AltStreamType altStreamType) const;
 
 	virtual String translatePath(const Path &path) const {
 		// Most of users of this class implement DOS-like archives.
@@ -250,10 +268,28 @@ public:
 		return normalizePath(path.toString('\\'), '\\');
 	}
 
-	virtual SharedArchiveContents readContentsForPath(const String& translatedPath) const = 0;
+	virtual SharedArchiveContents readContentsForPath(const String &translatedPath) const = 0;
+	virtual SharedArchiveContents readContentsForPathAltStream(const String &translatedPath, AltStreamType altStreamType) const;
 
 private:
-	mutable HashMap<String, SharedArchiveContents, IgnoreCase_Hash, IgnoreCase_EqualTo> _cache;
+	struct CacheKey {
+		CacheKey();
+
+		String path;
+		AltStreamType altStreamType;
+	};
+
+	struct CacheKey_EqualTo {
+		bool operator()(const CacheKey &x, const CacheKey &y) const;
+	};
+
+	struct CacheKey_Hash {
+		uint operator()(const CacheKey &x) const;
+	};
+
+	SeekableReadStream *createReadStreamForMemberImpl(const Path &path, bool isAltStream, Common::AltStreamType altStreamType) const;
+
+	mutable HashMap<CacheKey, SharedArchiveContents, CacheKey_Hash, CacheKey_EqualTo> _cache;
 	uint32 _maxStronglyCachedSize;
 };
 
@@ -388,6 +424,12 @@ public:
 	 */
 	SeekableReadStream *createReadStreamForMember(const Path &path) const override;
 
+	/**
+	 * Implement createReadStreamForMemberAltStream from the Archive base class. The current policy is
+	 * opening the first file encountered that matches the name.
+	 */
+	SeekableReadStream *createReadStreamForMemberAltStream(const Path &path, AltStreamType altStreamType) const override;
+
 	/**
 	 * Similar to above but exclude matches from archives before starting and starting itself.
 	 */
diff --git a/common/compression/gentee_installer.cpp b/common/compression/gentee_installer.cpp
index ab0d71f5e80..7abda6a1a09 100644
--- a/common/compression/gentee_installer.cpp
+++ b/common/compression/gentee_installer.cpp
@@ -585,6 +585,7 @@ public:
 	ArchiveItem(Common::SeekableReadStream *stream, Common::Mutex *guardMutex, const Common::String &path, const Common::String &name, int64 filePos, uint compressedSize, uint decompressedSize, bool isCompressed);
 
 	Common::SeekableReadStream *createReadStream() const override;
+	Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override;
 	Common::String getName() const override;
 	Common::Path getPathInArchive() const override { return getName(); }
 	Common::String getFileName() const override { return getName(); }
@@ -623,6 +624,10 @@ Common::SeekableReadStream *ArchiveItem::createReadStream() const {
 		return sliceSubstream;
 }
 
+Common::SeekableReadStream *ArchiveItem::createReadStreamForAltStream(Common::AltStreamType altStreamType) const {
+	return nullptr;
+}
+
 Common::String ArchiveItem::getName() const {
 	return _name;
 }
diff --git a/common/compression/stuffit.cpp b/common/compression/stuffit.cpp
index 529fa07c928..217f0397b1b 100644
--- a/common/compression/stuffit.cpp
+++ b/common/compression/stuffit.cpp
@@ -52,17 +52,25 @@ public:
 	bool hasFile(const Common::Path &path) const override;
 	int listMembers(Common::ArchiveMemberList &list) const override;
 	const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
-	Common::SharedArchiveContents readContentsForPath(const Common::String& name) const override;
+	Common::SharedArchiveContents readContentsForPath(const Common::String &name) const override;
+	Common::SharedArchiveContents readContentsForPathAltStream(const String &translatedPath, Common::AltStreamType altStreamType) const override;
 	Common::String translatePath(const Common::Path &path) const override;
 	char getPathSeparator() const override;
 
 private:
-	struct FileEntry {
-		byte compression;
+	struct FileEntryFork {
+		FileEntryFork();
+
 		uint32 uncompressedSize;
 		uint32 compressedSize;
 		uint32 offset;
 		uint16 crc;
+		byte compression;
+	};
+
+	struct FileEntry {
+		FileEntryFork dataFork;
+		FileEntryFork resFork;
 	};
 
 	Common::SeekableReadStream *_stream;
@@ -82,6 +90,8 @@ private:
 	// Decompression Helpers
 	void update14(uint16 first, uint16 last, byte *code, uint16 *freq) const;
 	void readTree14(Common::BitStream8LSB *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const;
+
+	Common::SharedArchiveContents readContentsForPathFork(const String &translatedPath, bool isResFork) const;
 };
 
 StuffItArchive::StuffItArchive() : Common::MemcachingCaseInsensitiveArchive(), _flattenTree(false) {
@@ -220,37 +230,32 @@ bool StuffItArchive::open(Common::SeekableReadStream *stream, bool flattenTree)
 		if (!flattenTree)
 			name = dirPrefix + name;
 
-		_metadataMap[name + ".finf"] = finfo.toData();
+		_metadataMap[name] = finfo.toData();
 
 		if (dataForkUncompressedSize != 0) {
 			// We have a data fork
 
-			FileEntry entry;
-			entry.compression = dataForkCompression;
-			entry.uncompressedSize = dataForkUncompressedSize;
-			entry.compressedSize = dataForkCompressedSize;
-			entry.offset = _stream->pos() + resForkCompressedSize;
-			entry.crc = dataForkCRC;
-			_map[name] = entry;
+			FileEntryFork &entryFork = _map[name].dataFork;
+			entryFork.compression = dataForkCompression;
+			entryFork.uncompressedSize = dataForkUncompressedSize;
+			entryFork.compressedSize = dataForkCompressedSize;
+			entryFork.offset = _stream->pos() + resForkCompressedSize;
+			entryFork.crc = dataForkCRC;
 
-			debug(0, "StuffIt file '%s', Compression = %d", name.c_str(), entry.compression);
+			debug(0, "StuffIt file '%s' data fork, Compression = %d", name.c_str(), entryFork.compression);
 		}
 
 		if (resForkUncompressedSize != 0) {
 			// We have a resource fork
 
-			// Add a .rsrc extension so we know it's the resource fork
-			name += ".rsrc";
-
-			FileEntry entry;
-			entry.compression = resForkCompression;
-			entry.uncompressedSize = resForkUncompressedSize;
-			entry.compressedSize = resForkCompressedSize;
-			entry.offset = _stream->pos();
-			entry.crc = resForkCRC;
-			_map[name] = entry;
+			FileEntryFork &entryFork = _map[name].resFork;
+			entryFork.compression = resForkCompression;
+			entryFork.uncompressedSize = resForkUncompressedSize;
+			entryFork.compressedSize = resForkCompressedSize;
+			entryFork.offset = _stream->pos();
+			entryFork.crc = resForkCRC;
 
-			debug(0, "StuffIt file '%s', Compression = %d", name.c_str(), entry.compression);
+			debug(0, "StuffIt file '%s' res fork, Compression = %d", name.c_str(), entryFork.compression);
 		}
 
 		// Go to the next entry
@@ -282,10 +287,14 @@ const Common::ArchiveMemberPtr StuffItArchive::getMember(const Common::Path &pat
 	return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
 }
 
-Common::SharedArchiveContents StuffItArchive::readContentsForPath(const Common::String& name) const {
-	if (!_stream || !_map.contains(name)) {
-		if (_metadataMap.contains(name)) {
-			const Common::MacFinderInfoData &metadata = _metadataMap[name];
+Common::SharedArchiveContents StuffItArchive::readContentsForPath(const Common::String &name) const {
+	return readContentsForPathFork(name, false);
+}
+
+Common::SharedArchiveContents StuffItArchive::readContentsForPathAltStream(const String &translatedPath, Common::AltStreamType altStreamType) const {
+	if (altStreamType == Common::AltStreamType::MacFinderInfo) {
+		if (_metadataMap.contains(translatedPath)) {
+			const Common::MacFinderInfoData &metadata = _metadataMap[translatedPath];
 			byte *copy = new byte[sizeof(Common::MacFinderInfoData)];
 			memcpy(copy, reinterpret_cast<const byte *>(&metadata), sizeof(Common::MacFinderInfoData));
 			return Common::SharedArchiveContents(copy, sizeof(Common::MacFinderInfoData));
@@ -293,39 +302,59 @@ Common::SharedArchiveContents StuffItArchive::readContentsForPath(const Common::
 		return Common::SharedArchiveContents();
 	}
 
+	if (altStreamType == Common::AltStreamType::MacResourceFork)
+		return readContentsForPathFork(translatedPath, true);
+
+	return Common::SharedArchiveContents();
+}
+
+Common::SharedArchiveContents StuffItArchive::readContentsForPathFork(const Common::String &name, bool isResFork) const {
+	FileMap::const_iterator entryIt = _map.find(name);
+
+	if (entryIt == _map.end())
+		return Common::SharedArchiveContents();
+
 	const FileEntry &entry = _map[name];
+	const FileEntryFork &entryFork = isResFork ? entry.resFork : entry.dataFork;
 
-	if (entry.compression & 0xF0)
+	if (entryFork.uncompressedSize == 0) {
+		if (isResFork)
+			return Common::SharedArchiveContents();
+		else
+			return Common::SharedArchiveContents(nullptr, 0);	// Treat no data fork as an empty stream
+	}
+
+	if (entryFork.compression & 0xF0)
 		error("Unhandled StuffIt encryption");
 
-	Common::SeekableSubReadStream subStream(_stream, entry.offset, entry.offset + entry.compressedSize);
+	Common::SeekableSubReadStream subStream(_stream, entryFork.offset, entryFork.offset + entryFork.compressedSize);
 
-	byte *uncompressedBlock = new byte[entry.uncompressedSize];
+	byte *uncompressedBlock = new byte[entryFork.uncompressedSize];
 
 	// We currently only support type 14 compression
-	switch (entry.compression) {
+	switch (entryFork.compression) {
 	case 0: // Uncompressed
-		subStream.read(uncompressedBlock, entry.uncompressedSize);
+		subStream.read(uncompressedBlock, entryFork.uncompressedSize);
 		break;
 	case 13: // TableHuff
-		if (!decompress13(&subStream, uncompressedBlock, entry.uncompressedSize))
+		if (!decompress13(&subStream, uncompressedBlock, entryFork.uncompressedSize))
 			error("SIT-13 decompression failed");
 		break;
 	case 14: // Installer
-		decompress14(&subStream, uncompressedBlock, entry.uncompressedSize);
+		decompress14(&subStream, uncompressedBlock, entryFork.uncompressedSize);
 		break;
 	default:
-		error("Unhandled StuffIt compression %d", entry.compression);
+		error("Unhandled StuffIt compression %d", entryFork.compression);
 		return Common::SharedArchiveContents();
 	}
 
-	uint16 actualCRC = Common::CRC16().crcFast(uncompressedBlock, entry.uncompressedSize);
+	uint16 actualCRC = Common::CRC16().crcFast(uncompressedBlock, entryFork.uncompressedSize);
 
-	if (actualCRC != entry.crc) {
-		error("StuffItArchive::readContentsForPath(): CRC mismatch: %04x vs %04x for file %s", actualCRC, entry.crc, name.c_str());
+	if (actualCRC != entryFork.crc) {
+		error("StuffItArchive::readContentsForPath(): CRC mismatch: %04x vs %04x for file %s %s fork", actualCRC, entryFork.crc, name.c_str(), (isResFork ? "res" : "data"));
 	}
 
-	return Common::SharedArchiveContents(uncompressedBlock, entry.uncompressedSize);
+	return Common::SharedArchiveContents(uncompressedBlock, entryFork.uncompressedSize);
 }
 
 Common::String StuffItArchive::translatePath(const Common::Path &path) const {
@@ -1085,6 +1114,10 @@ void StuffItArchive::decompress14(Common::SeekableReadStream *src, byte *dst, ui
 #undef OUTPUT_VAL
 #undef ALIGN_BITS
 
+
+StuffItArchive::FileEntryFork::FileEntryFork() : uncompressedSize(0), compressedSize(0), offset(0), crc(0), compression(0) {
+}
+
 Common::Archive *createStuffItArchive(const Common::String &fileName, bool flattenTree) {
 	StuffItArchive *archive = new StuffItArchive();
 
diff --git a/common/compression/vise.cpp b/common/compression/vise.cpp
index 649c1fd81c7..f21631c56f6 100644
--- a/common/compression/vise.cpp
+++ b/common/compression/vise.cpp
@@ -66,23 +66,19 @@ private:
 
 	class ArchiveMember : public Common::ArchiveMember {
 	public:
-		enum SubstreamType {
-			kSubstreamTypeData,
-			kSubstreamTypeResource,
-			kSubstreamTypeFinderInfo,
-		};
-
-		ArchiveMember(Common::SeekableReadStream *archiveStream, const FileDesc *fileDesc, SubstreamType substreamType);
+		ArchiveMember(Common::SeekableReadStream *archiveStream, const FileDesc *fileDesc);
 
 		Common::SeekableReadStream *createReadStream() const override;
+		Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override;
 		Common::String getName() const override;
 		Common::Path getPathInArchive() const override;
 		Common::String getFileName() const override;
 
 	private:
+		Common::SeekableReadStream *createReadStreamForDataStream(bool isResFork) const;
+
 		Common::SeekableReadStream *_archiveStream;
 		const FileDesc *_fileDesc;
-		SubstreamType _substreamType;
 	};
 
 public:
@@ -96,23 +92,28 @@ public:
 	int listMembers(Common::ArchiveMemberList &list) const override;
 	const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
 	Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
+	Common::SeekableReadStream *createReadStreamForMemberAltStream(const Common::Path &path, Common::AltStreamType altStreamType) const override;
 	char getPathSeparator() const override;
 
 private:
-	bool getFileDescIndex(const Common::Path &path, uint &outIndex, ArchiveMember::SubstreamType &outSubstreamType) const;
+	bool getFileDescIndex(const Common::Path &path, uint &outIndex) const;
 
 	Common::SeekableReadStream *_archiveStream;
 	Common::Array<FileDesc> _fileDescs;
 	Common::Array<DirectoryDesc> _directoryDescs;
 };
 
-MacVISEArchive::ArchiveMember::ArchiveMember(Common::SeekableReadStream *archiveStream, const FileDesc *fileDesc, SubstreamType substreamType)
-	: _archiveStream(archiveStream), _fileDesc(fileDesc), _substreamType(substreamType) {
+MacVISEArchive::ArchiveMember::ArchiveMember(Common::SeekableReadStream *archiveStream, const FileDesc *fileDesc)
+	: _archiveStream(archiveStream), _fileDesc(fileDesc) {
 }
 
 Common::SeekableReadStream *MacVISEArchive::ArchiveMember::createReadStream() const {
-	if (_substreamType == kSubstreamTypeFinderInfo) {
-		Common::MacFinderInfoData *finfoData = static_cast<Common::MacFinderInfoData*>(malloc(sizeof(Common::MacFinderInfoData)));
+	return createReadStreamForDataStream(false);
+}
+
+Common::SeekableReadStream *MacVISEArchive::ArchiveMember::createReadStreamForAltStream(Common::AltStreamType altStreamType) const {
+	if (altStreamType == AltStreamType::MacFinderInfo) {
+		Common::MacFinderInfoData *finfoData = static_cast<Common::MacFinderInfoData *>(malloc(sizeof(Common::MacFinderInfoData)));
 
 		if (!finfoData)
 			return nullptr;
@@ -126,6 +127,13 @@ Common::SeekableReadStream *MacVISEArchive::ArchiveMember::createReadStream() co
 		return new Common::MemoryReadStream(reinterpret_cast<const byte *>(finfoData), sizeof(Common::MacFinderInfoData), DisposeAfterUse::YES);
 	}
 
+	if (altStreamType == AltStreamType::MacResourceFork)
+		return createReadStreamForDataStream(true);
+
+	return nullptr;
+}
+
+Common::SeekableReadStream *MacVISEArchive::ArchiveMember::createReadStreamForDataStream(bool isResFork) const {
 	static const uint8 vl3DeobfuscationTable[] = {
 		0x6a, 0xb7, 0x36, 0xec, 0x15, 0xd9, 0xc8, 0x73, 0xe8, 0x38, 0x9a, 0xdf, 0x21, 0x25, 0xd0, 0xcc,
 		0xfd, 0xdc, 0x16, 0xd7, 0xe3, 0x43, 0x05, 0xc5, 0x8f, 0x48, 0xda, 0xf2, 0x3f, 0x10, 0x23, 0x6c,
@@ -145,12 +153,15 @@ Common::SeekableReadStream *MacVISEArchive::ArchiveMember::createReadStream() co
 		0x86, 0xdd, 0x5f, 0x42, 0xd3, 0x02, 0x61, 0x95, 0x0c, 0x5c, 0xa5, 0xcd, 0xc0, 0x07, 0xe2, 0xf3,
 	};
 
-	const bool isResFork = (_substreamType == kSubstreamTypeResource);
-
 	uint32 uncompressedSize = isResFork ? _fileDesc->uncompressedResSize : _fileDesc->uncompressedDataSize;
 	uint32 compressedSize = isResFork ? _fileDesc->compressedResSize : _fileDesc->compressedDataSize;
 	uint32 filePosition = _fileDesc->positionInArchive;
 
+	if (uncompressedSize == 0 && !isResFork) {
+		// Always return a stream for the data fork, even if it's empty
+		return new Common::MemoryReadStream(nullptr, 0, DisposeAfterUse::NO);
+	}
+
 	if (isResFork)
 		filePosition += _fileDesc->compressedDataSize;
 
@@ -201,21 +212,11 @@ Common::String MacVISEArchive::ArchiveMember::getName() const {
 }
 
 Common::Path MacVISEArchive::ArchiveMember::getPathInArchive() const {
-	if (_substreamType == kSubstreamTypeFinderInfo)
-		return _fileDesc->fullPath.append(".finf", ':');
-	else if (_substreamType == kSubstreamTypeResource)
-		return _fileDesc->fullPath.append(".rsrc", ':');
-	else
-		return _fileDesc->fullPath;
+	return _fileDesc->fullPath;
 }
 
 Common::String MacVISEArchive::ArchiveMember::getFileName() const {
-	if (_substreamType == kSubstreamTypeFinderInfo)
-		return _fileDesc->name + ".finf";
-	else if (_substreamType == kSubstreamTypeResource)
-		return _fileDesc->name + ".rsrc";
-	else
-		return _fileDesc->name;
+	return _fileDesc->name;
 }
 
 MacVISEArchive::FileDesc::FileDesc() : type{0, 0, 0, 0}, creator{0, 0, 0, 0}, compressedDataSize(0), uncompressedDataSize(0), compressedResSize(0), uncompressedResSize(0), positionInArchive(0) {
@@ -360,8 +361,7 @@ const MacVISEArchive::FileDesc *MacVISEArchive::getFileDesc(const Common::Path &
 
 bool MacVISEArchive::hasFile(const Common::Path &path) const {
 	uint index = 0;
-	ArchiveMember::SubstreamType substreamType = ArchiveMember::kSubstreamTypeData;
-	return getFileDescIndex(path, index, substreamType);
+	return getFileDescIndex(path, index);
 }
 
 int MacVISEArchive::listMembers(Common::ArchiveMemberList &list) const {
@@ -369,16 +369,7 @@ int MacVISEArchive::listMembers(Common::ArchiveMemberList &list) const {
 	for (uint fileIndex = 0; fileIndex < _fileDescs.size(); fileIndex++) {
 		const FileDesc &desc = _fileDescs[fileIndex];
 
-		if (desc.uncompressedDataSize) {
-			list.push_back(Common::ArchiveMemberPtr(new ArchiveMember(_archiveStream, &desc, ArchiveMember::kSubstreamTypeData)));
-			numMembers++;
-		}
-		if (desc.uncompressedResSize) {
-			list.push_back(Common::ArchiveMemberPtr(new ArchiveMember(_archiveStream, &desc, ArchiveMember::kSubstreamTypeResource)));
-			numMembers++;
-		}
-
-		list.push_back(Common::ArchiveMemberPtr(new ArchiveMember(nullptr, &desc, ArchiveMember::kSubstreamTypeFinderInfo)));
+		list.push_back(Common::ArchiveMemberPtr(new ArchiveMember(_archiveStream, &desc)));
 		numMembers++;
 	}
 	return numMembers;
@@ -386,11 +377,10 @@ int MacVISEArchive::listMembers(Common::ArchiveMemberList &list) const {
 
 const Common::ArchiveMemberPtr MacVISEArchive::getMember(const Common::Path &path) const {
 	uint descIndex = 0;
-	ArchiveMember::SubstreamType substreamType = ArchiveMember::kSubstreamTypeData;
-	if (!getFileDescIndex(path, descIndex, substreamType))
+	if (!getFileDescIndex(path, descIndex))
 		return nullptr;
 
-	return Common::ArchiveMemberPtr(new ArchiveMember(_archiveStream, &_fileDescs[descIndex], substreamType));
+	return Common::ArchiveMemberPtr(new ArchiveMember(_archiveStream, &_fileDescs[descIndex]));
 }
 
 Common::SeekableReadStream *MacVISEArchive::createReadStreamForMember(const Common::Path &path) const {
@@ -401,34 +391,23 @@ Common::SeekableReadStream *MacVISEArchive::createReadStreamForMember(const Comm
 	return archiveMember->createReadStream();
 }
 
+Common::SeekableReadStream *MacVISEArchive::createReadStreamForMemberAltStream(const Common::Path &path, Common::AltStreamType altStreamType) const {
+	Common::ArchiveMemberPtr archiveMember = getMember(path);
+	if (!archiveMember)
+		return nullptr;
+
+	return archiveMember->createReadStreamForAltStream(altStreamType);
+}
+
 char MacVISEArchive::getPathSeparator() const {
 	return ':';
 }
 
-bool MacVISEArchive::getFileDescIndex(const Common::Path &path, uint &outIndex, ArchiveMember::SubstreamType &outSubstreamType) const {
-	Common::String convertedPath = path.toString(getPathSeparator());
-
-	ArchiveMember::SubstreamType substreamType = ArchiveMember::kSubstreamTypeData;
-	if (convertedPath.hasSuffix(".rsrc")) {
-		substreamType = ArchiveMember::kSubstreamTypeResource;
-		convertedPath = convertedPath.substr(0, convertedPath.size() - 5);
-	} else if (convertedPath.hasSuffix(".finf")) {
-		substreamType = ArchiveMember::kSubstreamTypeFinderInfo;
-		convertedPath = convertedPath.substr(0, convertedPath.size() - 5);
-	}
-
-	Common::Path filePath(convertedPath, getPathSeparator());
-
+bool MacVISEArchive::getFileDescIndex(const Common::Path &path, uint &outIndex) const {
 	for (uint descIndex = 0; descIndex < _fileDescs.size(); descIndex++) {
 		const FileDesc &desc = _fileDescs[descIndex];
 
-		if (desc.fullPath == filePath) {
-			if (substreamType == ArchiveMember::SubstreamType::kSubstreamTypeData && desc.uncompressedDataSize == 0)
-				return false;
-			if (substreamType == ArchiveMember::SubstreamType::kSubstreamTypeResource && desc.uncompressedResSize == 0)
-				return false;
-
-			outSubstreamType = substreamType;
+		if (desc.fullPath == path) {
 			outIndex = descIndex;
 			return true;
 		}
diff --git a/common/formats/prodos.cpp b/common/formats/prodos.cpp
index 1fa5e498eb0..78b7c0ad2c0 100644
--- a/common/formats/prodos.cpp
+++ b/common/formats/prodos.cpp
@@ -172,6 +172,10 @@ Common::SeekableReadStream *ProDOSFile::createReadStream() const {
 	return new Common::MemoryReadStream(finalData, _eof, DisposeAfterUse::YES);
 }
 
+Common::SeekableReadStream *ProDOSFile::createReadStreamForAltStream(Common::AltStreamType altStreamType) const {
+	return nullptr;
+}
+
 // --- ProDOSDisk methods ---
 
 /* The time and date are compressed into 16bit words, so to make them useable
diff --git a/common/formats/prodos.h b/common/formats/prodos.h
index 085e0ec2c91..f71db7ebfea 100644
--- a/common/formats/prodos.h
+++ b/common/formats/prodos.h
@@ -90,6 +90,7 @@ public:
 	Common::Path getPathInArchive() const override;                       // Returns _name
 	Common::String getFileName() const override;                          // Returns _name
 	Common::SeekableReadStream *createReadStream() const override;        // This is what the archive needs to create a file
+	Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override;
 	void getDataBlock(byte *memOffset, int offset, int size) const;       // Gets data up to the size of a single data block (512 bytes)
 	int parseIndexBlock(byte *memOffset, int blockNum, int cSize) const;  // Uses getDataBlock() on every pointer in the index file, adding them to byte * memory block
 
diff --git a/common/fs.cpp b/common/fs.cpp
index e794adbe6bc..42ab5f7b1be 100644
--- a/common/fs.cpp
+++ b/common/fs.cpp
@@ -33,6 +33,7 @@ public:
 	FSDirectoryFile(const Common::Path &pathInDirectory, const FSNode &fsNode);
 
 	SeekableReadStream *createReadStream() const override;
+	SeekableReadStream *createReadStreamForAltStream(AltStreamType altStreamType) const override;
 	String getName() const override;
 	Path getPathInArchive() const override;
 	String getFileName() const override;
@@ -50,6 +51,10 @@ SeekableReadStream *FSDirectoryFile::createReadStream() const {
 	return _fsNode.createReadStream();
 }
 
+SeekableReadStream *FSDirectoryFile::createReadStreamForAltStream(AltStreamType altStreamType) const {
+	return _fsNode.createReadStreamForAltStream(altStreamType);
+}
+
 String FSDirectoryFile::getName() const {
 	return _fsNode.getName();
 }
@@ -194,6 +199,21 @@ SeekableReadStream *FSNode::createReadStream() const {
 	return _realNode->createReadStream();
 }
 
+SeekableReadStream *FSNode::createReadStreamForAltStream(AltStreamType altStreamType) const {
+	if (_realNode == nullptr)
+		return nullptr;
+
+	if (!_realNode->exists()) {
+		warning("FSNode::createReadStream: '%s' does not exist", getName().c_str());
+		return nullptr;
+	} else if (_realNode->isDirectory()) {
+		warning("FSNode::createReadStream: '%s' is a directory", getName().c_str());
+		return nullptr;
+	}
+
+	return _realNode->createReadStreamForAltStream(altStreamType);
+}
+
 SeekableWriteStream *FSNode::createWriteStream() const {
 	if (_realNode == nullptr)
 		return nullptr;
@@ -360,21 +380,8 @@ void FSDirectory::cacheDirectoryRecursive(FSNode node, int depth, const Path& pr
 					warning("FSDirectory::cacheDirectory: name clash when building cache, ignoring file '%s'",
 					        Common::toPrintable(name.toString('/')).c_str());
 				}
-			} else {
+			} else
 				_fileCache[name] = *it;
-
-#ifdef MACOSX
-				// On Mac, check for native resource fork
-				String rsrcName = it->getPath() + "/..namedfork/rsrc";
-				FSNode rsrc = FSNode(rsrcName);
-
-				Path cacheName = prefix.join(it->getRealName() + "/..namedfork/rsrc");
-
-				if (rsrc.exists()) {
-					_fileCache[cacheName] = rsrc;
-				}
-#endif
-			}
 		}
 	}
 
diff --git a/common/fs.h b/common/fs.h
index eb3e4ba03ee..6fa54266f2a 100644
--- a/common/fs.h
+++ b/common/fs.h
@@ -256,10 +256,20 @@ public:
 	 * referred by this node. This assumes that the node actually refers
 	 * to a readable file. If this is not the case, 0 is returned.
 	 *
-	 * @return Pointer to the stream object, 0 in case of a failure.
+	 * @return Pointer to the stream object, nullptr in case of a failure.
 	 */
 	SeekableReadStream *createReadStream() const override;
 
+	/**
+	 * Create a SeekableReadStream instance corresponding to an alternate stream
+	 * of the file referred by this node. This assumes that the node actually
+	 * refers to a readable file and the alternate stream exists.  If either is
+	 * not the case, nullptr is returned.
+	 *
+	 * @return Pointer to the stream object, nullptr in case of a failure.
+	 */
+	SeekableReadStream *createReadStreamForAltStream(AltStreamType altStreamType) const override;
+
 	/**
 	 * Create a WriteStream instance corresponding to the file
 	 * referred by this node. This assumes that the node actually refers
diff --git a/common/macresman.cpp b/common/macresman.cpp
index 3601ce364c8..fa819dd5b16 100644
--- a/common/macresman.cpp
+++ b/common/macresman.cpp
@@ -252,15 +252,19 @@ bool MacResManager::open(const Path &fileName, Archive &archive) {
 
 	// Maybe file is in MacBinary but without .bin extension?
 	// Check it here
-	SeekableReadStream *rawStream = archive.createReadStreamForMember(fileName);
-	if (rawStream && isMacBinary(*rawStream)) {
-		rawStream->seek(0);
-		if (loadFromMacBinary(rawStream)) {
+	stream = archive.createReadStreamForMember(fileName);
+	if (stream && isMacBinary(*stream)) {
+		stream->seek(0);
+		if (loadFromMacBinary(stream)) {
 			_baseFileName = fileName;
 			return true;
 		}
 	}
 
+	bool fileExists = (stream != nullptr);
+
+	delete stream;
+
 	// Then try for AppleDouble using Apple's naming
 	// As they are created silently from plain files (e.g. from a macbinary) they are pretty low quality often.
 	stream = openAppleDoubleWithAppleOrOSXNaming(archive, fileName);
@@ -270,6 +274,15 @@ bool MacResManager::open(const Path &fileName, Archive &archive) {
 	}
 	delete stream;
 
+	// Try alternate stream
+	stream = archive.createReadStreamForMemberAltStream(fileName, AltStreamType::MacResourceFork);
+	if (stream && loadFromRawFork(stream)) {
+		_baseFileName = fileName;
+		return true;
+	}
+	delete stream;
+
+
 #ifdef MACOSX
 	// Check the actual fork on a Mac computer. It's even worse than __MACOSX as
 	// it's present on any HFS(+) and appears even after copying macbin on HFS(+).
@@ -296,9 +309,8 @@ bool MacResManager::open(const Path &fileName, Archive &archive) {
 	}
 #endif
 
-	if (rawStream) { // No non-empty resource fork found.
+	if (fileExists) { // No non-empty resource fork found, but the file still exists
 		_baseFileName = fileName;
-		delete rawStream;
 		_stream = nullptr;
 		return true;
 	}
@@ -390,15 +402,15 @@ bool MacResManager::getFileFinderInfo(const Path &fileName, Archive &archive, Ma
 
 bool MacResManager::getFileFinderInfo(const Path &fileName, Archive &archive, MacFinderInfo &outFinderInfo, MacFinderExtendedInfo &outFinderExtendedInfo) {
 	// Our preference is as following:
-	// .finf -> AppleDouble in .rsrc -> MacBinary with .bin -> MacBinary without .bin -> AppleDouble in ._
+	// Alt stream -> AppleDouble in .rsrc -> MacBinary with .bin -> MacBinary without .bin -> AppleDouble in ._
 	// -> AppleDouble in __MACOSX -> No finder info
 	// If you compare with open there are following differences:
 	// * We add .finf. It has only finder info
 	// * We skip raw .rsrc as it lack finder info
 	// * Actual finder info on OSX isn't implemented yet
 
-	// Prefer standalone .finf files first (especially since this can avoid decompressing entire files from slow archive formats like StuffIt Installer)
-	Common::ScopedPtr<SeekableReadStream> stream(archive.createReadStreamForMember(fileName.append(".finf")));
+	// Prefer alt stream first (especially since this can avoid decompressing entire files from slow archive formats like StuffIt Installer)
+	Common::ScopedPtr<SeekableReadStream> stream(archive.createReadStreamForMemberAltStream(fileName, AltStreamType::MacFinderInfo));
 	if (stream) {
 		MacFinderInfoData finfoData;
 		MacFinderExtendedInfoData fxinfoData;
diff --git a/engines/grim/lab.h b/engines/grim/lab.h
index 6573ff43c03..621bba394d0 100644
--- a/engines/grim/lab.h
+++ b/engines/grim/lab.h
@@ -42,6 +42,7 @@ public:
 	Common::String getFileName() const override { return _name; }
 	Common::Path getPathInArchive() const override { return _name; }
 	Common::SeekableReadStream *createReadStream() const override;
+	Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override { return nullptr; }
 	friend class Lab;
 };
 
diff --git a/engines/mm/shared/utils/engine_data.cpp b/engines/mm/shared/utils/engine_data.cpp
index b37230e81ff..87a8e4155b1 100644
--- a/engines/mm/shared/utils/engine_data.cpp
+++ b/engines/mm/shared/utils/engine_data.cpp
@@ -45,6 +45,9 @@ public:
 	Common::SeekableReadStream *createReadStream() const override {
 		return _member->createReadStream();
 	}
+	Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override {
+		return nullptr;
+	}
 	Common::String getName() const override {
 		Common::String name = _member->getName();
 		assert(name.hasPrefixIgnoreCase(_innerfolder));
diff --git a/engines/stark/formats/xarc.cpp b/engines/stark/formats/xarc.cpp
index be96aa84767..16cb78b30f0 100644
--- a/engines/stark/formats/xarc.cpp
+++ b/engines/stark/formats/xarc.cpp
@@ -39,6 +39,7 @@ public:
 	XARCMember(const XARCArchive *xarc, Common::ReadStream &stream, uint32 offset);
 
 	Common::SeekableReadStream *createReadStream() const override;
+	Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override;
 	Common::String getName() const override { return _name; }
 	Common::Path getPathInArchive() const { return _name; }
 	Common::String getFileName() const { return _name; }
@@ -76,6 +77,10 @@ Common::SeekableReadStream *XARCMember::createReadStream() const {
 	return _xarc->createReadStreamForMember(this);
 }
 
+Common::SeekableReadStream *XARCMember::createReadStreamForAltStream(Common::AltStreamType altStreamType) const {
+	return nullptr;
+}
+
 Common::String XARCMember::readString(Common::ReadStream &stream) {
 	Common::String str;
 
diff --git a/engines/ultima/shared/engine/data_archive.cpp b/engines/ultima/shared/engine/data_archive.cpp
index 6cbcfb04634..b2c89163525 100644
--- a/engines/ultima/shared/engine/data_archive.cpp
+++ b/engines/ultima/shared/engine/data_archive.cpp
@@ -44,6 +44,9 @@ public:
 	Common::SeekableReadStream *createReadStream() const override {
 		return _member->createReadStream();
 	}
+	Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override {
+		return _member->createReadStreamForAltStream(altStreamType);
+	}
 	Common::String getName() const override {
 		Common::String name = _member->getName();
 		assert(name.hasPrefixIgnoreCase(_innerfolder));
diff --git a/engines/wintermute/base/file/base_file_entry.cpp b/engines/wintermute/base/file/base_file_entry.cpp
index 162a7f8ee4c..f6f0c06ec59 100644
--- a/engines/wintermute/base/file/base_file_entry.cpp
+++ b/engines/wintermute/base/file/base_file_entry.cpp
@@ -52,6 +52,10 @@ Common::SeekableReadStream *BaseFileEntry::createReadStream() const {
 	return file;
 }
 
+Common::SeekableReadStream *BaseFileEntry::createReadStreamForAltStream(Common::AltStreamType altStreamType) const {
+	return nullptr;
+}
+
 //////////////////////////////////////////////////////////////////////////
 BaseFileEntry::BaseFileEntry() {
 	_package = nullptr;
diff --git a/engines/wintermute/base/file/base_file_entry.h b/engines/wintermute/base/file/base_file_entry.h
index f7e3d66b7df..9f53377d8dd 100644
--- a/engines/wintermute/base/file/base_file_entry.h
+++ b/engines/wintermute/base/file/base_file_entry.h
@@ -39,6 +39,7 @@ class BasePackage;
 class BaseFileEntry : public Common::ArchiveMember {
 public:
 	Common::SeekableReadStream *createReadStream() const override;
+	Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override;
 	Common::String getName() const override { return _filename; }
 	Common::Path getPathInArchive() const override { return _filename; }
 	Common::String getFileName() const override { return _filename; }


Commit: be0ee20b4aec6fefb6fcb26042488dd75131a2a2
    https://github.com/scummvm/scummvm/commit/be0ee20b4aec6fefb6fcb26042488dd75131a2a2
Author: elasota (ejlasota at gmail.com)
Date: 2023-08-30T00:31:00+02:00

Commit Message:
MTROPOLIS: Fix SPQR Mac not booting

Changed paths:
    engines/mtropolis/boot.cpp


diff --git a/engines/mtropolis/boot.cpp b/engines/mtropolis/boot.cpp
index 2a7b99f1751..e9220f17a8a 100644
--- a/engines/mtropolis/boot.cpp
+++ b/engines/mtropolis/boot.cpp
@@ -506,8 +506,10 @@ void SPQRGameDataHandler::unpackAdditionalFiles(Common::Array<Common::SharedPtr<
 		debug(1, "Unpacking files...");
 
 		for (const MacVISE3InstallerUnpackRequest &request : unpackRequests) {
+			Common::Path requestPath(request.fileName, ':');
+
 			Common::MacFinderInfo finfo;
-			if (!Common::MacResManager::getFileFinderInfo(request.fileName, *archive, finfo))
+			if (!Common::MacResManager::getFileFinderInfo(requestPath, *archive, finfo))
 				error("Couldn't get Finder info for file '%s'", request.fileName);
 
 			FileIdentification ident;
@@ -518,14 +520,14 @@ void SPQRGameDataHandler::unpackAdditionalFiles(Common::Array<Common::SharedPtr<
 
 			if (request.extractResources) {
 				Common::SharedPtr<Common::MacResManager> resMan(new Common::MacResManager());
-				if (!resMan->open(request.fileName, *archive))
+				if (!resMan->open(requestPath, *archive))
 					error("Failed to open Mac res manager for file '%s'", request.fileName);
 
 				ident.resMan = resMan;
 			}
 
 			if (request.extractData)
-				ident.stream.reset(archive->createReadStreamForMember(request.fileName));
+				ident.stream.reset(archive->createReadStreamForMember(requestPath));
 
 			files.push_back(ident);
 		}




More information about the Scummvm-git-logs mailing list