COMMON: Add a way to check if an archive member is in a Mac archive so we can avoid decompressing the data fork to check for MacBinary when the resource data is always in the alt stream.

This commit is contained in:
elasota 2023-12-15 19:02:06 -05:00 committed by Eugene Sandulenko
parent 237e6231dd
commit 6ed3c946ff
7 changed files with 92 additions and 29 deletions

View File

@ -37,6 +37,10 @@ U32String ArchiveMember::getDisplayName() const {
return getName();
}
bool ArchiveMember::isInMacArchive() const {
return false;
}
bool ArchiveMember::isDirectory() const {
return false;
}

View File

@ -82,6 +82,7 @@ public:
virtual bool isDirectory() const; /*!< Checks if the ArchiveMember is a directory. */
virtual void listChildren(ArchiveMemberList &childList, const char *pattern = nullptr) const; /*!< Adds the immediate children of this archive member to childList, optionally matching a pattern. */
virtual U32String getDisplayName() const; /*!< Get the display name of the archive member. */
virtual bool isInMacArchive() const; /*!< Checks if the ArchiveMember is in a Mac archive, in which case resource forks and Finder info can only be loaded via alt streams. */
};
struct ArchiveMemberDetails {

View File

@ -73,6 +73,13 @@ private:
FileEntryFork resFork;
};
class StuffItArchiveMember : public Common::GenericArchiveMember {
public:
StuffItArchiveMember(const Common::Path &path, const Common::Archive &archive);
bool isInMacArchive() const override;
};
Common::SeekableReadStream *_stream;
typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
@ -284,7 +291,7 @@ int StuffItArchive::listMembers(Common::ArchiveMemberList &list) const {
}
const Common::ArchiveMemberPtr StuffItArchive::getMember(const Common::Path &path) const {
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
return Common::ArchiveMemberPtr(new StuffItArchiveMember(path, *this));
}
Common::SharedArchiveContents StuffItArchive::readContentsForPath(const Common::String &name) const {
@ -1118,6 +1125,14 @@ void StuffItArchive::decompress14(Common::SeekableReadStream *src, byte *dst, ui
StuffItArchive::FileEntryFork::FileEntryFork() : uncompressedSize(0), compressedSize(0), offset(0), crc(0), compression(0) {
}
StuffItArchive::StuffItArchiveMember::StuffItArchiveMember(const Common::Path &path, const Common::Archive &archive)
: Common::GenericArchiveMember(path, archive) {
}
bool StuffItArchive::StuffItArchiveMember::isInMacArchive() const {
return true;
}
Common::Archive *createStuffItArchive(const Common::String &fileName, bool flattenTree) {
StuffItArchive *archive = new StuffItArchive();

View File

@ -73,6 +73,7 @@ private:
Common::String getName() const override;
Common::Path getPathInArchive() const override;
Common::String getFileName() const override;
bool isInMacArchive() const override;
private:
Common::SeekableReadStream *createReadStreamForDataStream(bool isResFork) const;
@ -219,6 +220,10 @@ Common::String MacVISEArchive::ArchiveMember::getFileName() const {
return _fileDesc->name;
}
bool MacVISEArchive::ArchiveMember::isInMacArchive() const {
return true;
}
MacVISEArchive::FileDesc::FileDesc() : type{0, 0, 0, 0}, creator{0, 0, 0, 0}, compressedDataSize(0), uncompressedDataSize(0), compressedResSize(0), uncompressedResSize(0), positionInArchive(0) {
}

View File

@ -217,12 +217,28 @@ SeekableReadStream *MacResManager::openAppleDoubleWithAppleOrOSXNaming(Archive&
bool MacResManager::open(const Path &fileName, Archive &archive) {
close();
SeekableReadStream *stream = nullptr;
// Our preference is as following:
// AppleDouble in .rsrc -> Raw .rsrc -> MacBinary with .bin -> MacBinary without .bin -> AppleDouble in ._
// -> AppleDouble in __MACOSX -> Actual resource fork -> No resource fork
Common::ArchiveMemberPtr archiveMember = archive.getMember(fileName);
// If this is in a Mac archive, then the resource fork will always be in the alt stream
if (archiveMember && archiveMember->isInMacArchive()) {
_baseFileName = fileName;
stream = archive.createReadStreamForMemberAltStream(fileName, AltStreamType::MacResourceFork);
if (stream && !loadFromRawFork(stream))
_stream = nullptr;
// If the archive member exists, then the file exists, but has no res fork, so we should return true
return true;
}
// Prefer standalone files first, starting with raw forks
SeekableReadStream *stream = archive.createReadStreamForMember(fileName.append(".rsrc"));
stream = archive.createReadStreamForMember(fileName.append(".rsrc"));
if (stream) {
// Some programs actually store AppleDouble there. Check it
@ -251,14 +267,17 @@ bool MacResManager::open(const Path &fileName, Archive &archive) {
// Maybe file is in MacBinary but without .bin extension?
// Check it here
stream = archive.createReadStreamForMember(fileName);
if (stream && isMacBinary(*stream)) {
stream->seek(0);
if (loadFromMacBinary(stream)) {
_baseFileName = fileName;
return true;
if (archiveMember) {
stream = archiveMember->createReadStream();
if (stream && isMacBinary(*stream)) {
stream->seek(0);
if (loadFromMacBinary(stream)) {
_baseFileName = fileName;
return true;
}
}
}
} else
stream = nullptr;
bool fileExists = (stream != nullptr);
@ -285,7 +304,6 @@ bool MacResManager::open(const Path &fileName, Archive &archive) {
#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(+).
const ArchiveMemberPtr archiveMember = archive.getMember(fileName);
if (archiveMember.get()) {
// This could be a MacBinary file that still has a
// resource fork; if it is, it needs to get opened as MacBinary
@ -332,7 +350,12 @@ SeekableReadStream * MacResManager::openDataForkFromMacBinary(SeekableReadStream
}
SeekableReadStream * MacResManager::openFileOrDataFork(const Path &fileName, Archive &archive) {
SeekableReadStream *stream = archive.createReadStreamForMember(fileName);
SeekableReadStream *stream = nullptr;
Common::ArchiveMemberPtr archiveMember = archive.getMember(fileName);
bool mayBeMacBinary = true;
// Our preference is as following:
// File itself as macbinary -> File itself as raw -> .bin as macbinary
// Compared to open:
@ -346,27 +369,37 @@ SeekableReadStream * MacResManager::openFileOrDataFork(const Path &fileName, Arc
// right levels of onion. Fortunately no game so far does it. But someday...
// Hopefully not.
// Check the basename for Macbinary
if (stream && isMacBinary(*stream)) {
stream->seek(MBI_DFLEN);
uint32 dataSize = stream->readUint32BE();
return new SeekableSubReadStream(stream, MBI_INFOHDR, MBI_INFOHDR + dataSize, DisposeAfterUse::YES);
}
// All formats other than Macbinary and AppleSingle (not supported) use
// basename-named file as data fork holder.
if (stream) {
stream->seek(0);
return stream;
if (archiveMember && archiveMember->isInMacArchive())
mayBeMacBinary = false;
if (archiveMember) {
stream = archiveMember->createReadStream();
// Check the basename for Macbinary
if (mayBeMacBinary && stream && isMacBinary(*stream)) {
stream->seek(MBI_DFLEN);
uint32 dataSize = stream->readUint32BE();
return new SeekableSubReadStream(stream, MBI_INFOHDR, MBI_INFOHDR + dataSize, DisposeAfterUse::YES);
}
// All formats other than Macbinary and AppleSingle (not supported) use
// basename-named file as data fork holder.
if (stream) {
stream->seek(0);
return stream;
}
}
// Check .bin for MacBinary next
stream = archive.createReadStreamForMember(fileName.append(".bin"));
if (stream && isMacBinary(*stream)) {
stream->seek(MBI_DFLEN);
uint32 dataSize = stream->readUint32BE();
return new SeekableSubReadStream(stream, MBI_INFOHDR, MBI_INFOHDR + dataSize, DisposeAfterUse::YES);
if (mayBeMacBinary) {
// Check .bin for MacBinary next
stream = archive.createReadStreamForMember(fileName.append(".bin"));
if (stream && isMacBinary(*stream)) {
stream->seek(MBI_DFLEN);
uint32 dataSize = stream->readUint32BE();
return new SeekableSubReadStream(stream, MBI_INFOHDR, MBI_INFOHDR + dataSize, DisposeAfterUse::YES);
}
delete stream;
}
delete stream;
// The file doesn't exist
return nullptr;

View File

@ -235,4 +235,8 @@ Common::U32String VirtualFileSystem::VFSArchiveMember::getDisplayName() const {
return _virtualFile->_archiveMember->getDisplayName();
}
bool VirtualFileSystem::VFSArchiveMember::isInMacArchive() const {
return _virtualFile->_archiveMember->isInMacArchive();
}
} // End of namespace MTropolis

View File

@ -92,6 +92,7 @@ private:
bool isDirectory() const override;
void listChildren(Common::ArchiveMemberList &childList, const char *pattern) const override;
Common::U32String getDisplayName() const override;
bool isInMacArchive() const override;
private:
const VirtualFile *_virtualFile;