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

sev- noreply at scummvm.org
Sun Sep 17 17:17:38 UTC 2023


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

Summary:
79e06f899a COMMON: Fix String::matchString matching excluded characters with trailing asterisk wildcards
1155eb7894 COMMON: Add isPathDirectory to Archive, and isDirectory and listChildren to ArchiveMember
b93d803f3e COMMON: Add isDirectory and listChildren to FSNode and FSDirectoryFile
63b01f5e85 COMMON: Strip trailing slashes from ZipArchive directories and improve directory detection.  Add support for listChildre
e0b8e6685e SWORD25: Fix extracted files support and update ZipArchive handling to process directories


Commit: 79e06f899ad26bf66a6902a41212fefafa73d379
    https://github.com/scummvm/scummvm/commit/79e06f899ad26bf66a6902a41212fefafa73d379
Author: elasota (ejlasota at gmail.com)
Date: 2023-09-17T19:17:33+02:00

Commit Message:
COMMON: Fix String::matchString matching excluded characters with trailing asterisk wildcards

Changed paths:
    common/str.cpp


diff --git a/common/str.cpp b/common/str.cpp
index 9994f209b32..193e5b3ed16 100644
--- a/common/str.cpp
+++ b/common/str.cpp
@@ -638,6 +638,7 @@ bool matchString(const char *str, const char *pat, bool ignoreCase, const char *
 	const char *p = nullptr;
 	const char *q = nullptr;
 	bool escaped = false;
+	bool noExclusions = (wildcardExclusions == nullptr || !*wildcardExclusions);
 
 	for (;;) {
 		if (wildcardExclusions && strchr(wildcardExclusions, *str)) {
@@ -662,8 +663,8 @@ bool matchString(const char *str, const char *pat, bool ignoreCase, const char *
 				p = nullptr;
 				q = nullptr;
 			}
-			// If pattern ended with * -> match
-			if (!*pat)
+			// If pattern ended with * and there are no exclusions -> match
+			if (!*pat && noExclusions)
 				return true;
 			break;
 


Commit: 1155eb789430f68b84f92c615422bf7b74ba7cd8
    https://github.com/scummvm/scummvm/commit/1155eb789430f68b84f92c615422bf7b74ba7cd8
Author: elasota (ejlasota at gmail.com)
Date: 2023-09-17T19:17:33+02:00

Commit Message:
COMMON: Add isPathDirectory to Archive, and isDirectory and listChildren to ArchiveMember

Changed paths:
    common/archive.cpp
    common/archive.h


diff --git a/common/archive.cpp b/common/archive.cpp
index e882cd88d4a..db189daf2a6 100644
--- a/common/archive.cpp
+++ b/common/archive.cpp
@@ -30,6 +30,20 @@
 
 namespace Common {
 
+ArchiveMember::~ArchiveMember() {
+}
+
+U32String ArchiveMember::getDisplayName() const {
+	return getName();
+}
+
+bool ArchiveMember::isDirectory() const {
+	return false;
+}
+
+void ArchiveMember::listChildren(ArchiveMemberList &childList, const char *pattern) const {
+}
+
 GenericArchiveMember::GenericArchiveMember(const String &pathStr, const Archive &parent)
 	: _parent(parent), _path(pathStr, parent.getPathSeparator()) {
 }
@@ -58,6 +72,23 @@ SeekableReadStream *GenericArchiveMember::createReadStreamForAltStream(AltStream
 	return _parent.createReadStreamForMemberAltStream(_path, altStreamType);
 }
 
+bool GenericArchiveMember::isDirectory() const {
+	return _parent.isPathDirectory(_path);
+}
+
+void GenericArchiveMember::listChildren(ArchiveMemberList &childList, const char *pattern) const {
+	if (!pattern)
+		pattern = "*";
+
+	Common::Path searchPath = _path.appendComponent(pattern);
+
+	_parent.listMatchingMembers(childList, searchPath);
+}
+
+bool Archive::isPathDirectory(const Path &path) const {
+	return false;
+}
+
 int Archive::listMatchingMembers(ArchiveMemberList &list, const Path &pattern, bool matchPathComponents) const {
 	// Get all "names" (TODO: "files" ?)
 	ArchiveMemberList allNames;
@@ -384,6 +415,29 @@ bool SearchSet::hasFile(const Path &path) const {
 	return false;
 }
 
+bool SearchSet::isPathDirectory(const Path &path) const {
+	if (path.empty())
+		return false;
+
+	ArchiveNodeList::const_iterator it = _list.begin();
+	for (; it != _list.end(); ++it) {
+		if (it->_arc->isPathDirectory(path)) {
+			// See if an earlier archive contains the same path as a non-directory file.
+			// If this is the case, then we want to return false here because getMember will return
+			// that file.  This is a bit faster than hasFile for each archive first.
+			while (it != _list.begin()) {
+				--it;
+				if (it->_arc->hasFile(path))
+					return false;
+			}
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
 int SearchSet::listMatchingMembers(ArchiveMemberList &list, const Path &pattern, bool matchPathComponents) const {
 	int matches = 0;
 
diff --git a/common/archive.h b/common/archive.h
index 6624c73ad71..aeae4e98265 100644
--- a/common/archive.h
+++ b/common/archive.h
@@ -43,6 +43,7 @@ namespace Common {
  * @{
  */
 
+class ArchiveMember;
 class FSNode;
 class SeekableReadStream;
 
@@ -53,6 +54,8 @@ enum class AltStreamType {
 	MacResourceFork,
 };
 
+typedef SharedPtr<ArchiveMember> ArchiveMemberPtr; /*!< Shared pointer to an archive member. */
+typedef List<ArchiveMemberPtr> ArchiveMemberList;  /*!< List of archive members. */
 
 /**
  * The ArchiveMember class is an abstract interface to represent elements inside
@@ -64,7 +67,7 @@ enum class AltStreamType {
  */
 class ArchiveMember {
 public:
-	virtual ~ArchiveMember() { }
+	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. */
 
@@ -76,12 +79,11 @@ public:
 
 	virtual Path getPathInArchive() const = 0; /*!< Get the full path of the archive member relative to the containing archive root. */
 	virtual String getFileName() const = 0; /*!< Get the file name of the archive member relative to its containing directory within the archive. */
-	virtual U32String getDisplayName() const { return getName(); } /*!< Get the display name of the archive member. */
+	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. */
 };
 
-typedef SharedPtr<ArchiveMember> ArchiveMemberPtr; /*!< Shared pointer to an archive member. */
-typedef List<ArchiveMemberPtr> ArchiveMemberList; /*!< List of archive members. */
-
 struct ArchiveMemberDetails {
 	ArchiveMemberPtr arcMember;
 	Common::String arcName;
@@ -121,6 +123,8 @@ public:
 	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. */
+	bool isDirectory() const override;
+	void listChildren(ArchiveMemberList &childList, const char *pattern) const override;
 
 private:
 	const Archive &_parent;
@@ -140,10 +144,15 @@ public:
 	/**
 	 * Check if a member with the given @p name is present in the Archive.
 	 * Patterns are not allowed, as this is meant to be a quick File::exists()
-	 * replacement.
+	 * replacement.  This returns "true" for both files and directories.
 	 */
 	virtual bool hasFile(const Path &path) const = 0;
 
+	/**
+	 * Check if a member with the given @p name exists and is a directory.
+	 */
+	virtual bool isPathDirectory(const Path &path) const;
+
 	/**
 	 * Add all members of the Archive matching the specified pattern to the list.
 	 * Must only append to list, and not remove elements from it.
@@ -410,6 +419,7 @@ public:
 	void setPriority(const String& name, int priority);
 
 	bool hasFile(const Path &path) const override;
+	bool isPathDirectory(const Path &path) const override;
 	int listMatchingMembers(ArchiveMemberList &list, const Path &pattern, bool matchPathComponents = false) const override;
 	int listMatchingMembers(ArchiveMemberDetailsList &list, const Path &pattern, bool matchPathComponents = false) const;
 	int listMembers(ArchiveMemberList &list) const override;


Commit: b93d803f3e64e05bc07c52abd3e7de264272e918
    https://github.com/scummvm/scummvm/commit/b93d803f3e64e05bc07c52abd3e7de264272e918
Author: elasota (ejlasota at gmail.com)
Date: 2023-09-17T19:17:33+02:00

Commit Message:
COMMON: Add isDirectory and listChildren to FSNode and FSDirectoryFile

Changed paths:
    common/fs.cpp
    common/fs.h


diff --git a/common/fs.cpp b/common/fs.cpp
index 42ab5f7b1be..936d5b46324 100644
--- a/common/fs.cpp
+++ b/common/fs.cpp
@@ -38,13 +38,16 @@ public:
 	Path getPathInArchive() const override;
 	String getFileName() const override;
 	U32String getDisplayName() const override;
+	bool isDirectory() const override;
+	void listChildren(ArchiveMemberList &list, const char *pattern) const override;
 
 private:
 	Common::Path _pathInDirectory;
 	FSNode _fsNode;
 };
 
-FSDirectoryFile::FSDirectoryFile(const Common::Path &pathInDirectory, const FSNode &fsNode) : _pathInDirectory(pathInDirectory), _fsNode(fsNode) {
+FSDirectoryFile::FSDirectoryFile(const Common::Path &pathInDirectory, const FSNode &fsNode)
+	: _pathInDirectory(pathInDirectory), _fsNode(fsNode) {
 }
 
 SeekableReadStream *FSDirectoryFile::createReadStream() const {
@@ -71,6 +74,30 @@ U32String FSDirectoryFile::getDisplayName() const {
 	return _fsNode.getDisplayName();
 }
 
+bool FSDirectoryFile::isDirectory() const {
+	return _fsNode.isDirectory();
+}
+
+void FSDirectoryFile::listChildren(ArchiveMemberList &list, const char *pattern) const {
+	// We don't check for includeDirectories in the parent archive to determine the list mode here because it is implicit,
+	// i.e. if includeDirectories was set false, then this file isn't a directory in the first place.
+
+	FSList fsList;
+	if (!_fsNode.getChildren(fsList, FSNode::kListAll))
+		return;
+
+	for (const FSNode &fsNode : fsList) {
+		Common::String fileName = fsNode.getName();
+
+		if (pattern != nullptr && !fileName.matchString(pattern, true))
+			continue;
+
+		Common::Path subPath = _pathInDirectory.appendComponent(fileName);
+
+		list.push_back(ArchiveMemberPtr(new FSDirectoryFile(subPath, fsNode)));
+	}
+}
+
 
 FSNode::FSNode() {
 }
@@ -79,6 +106,9 @@ FSNode::FSNode(AbstractFSNode *realNode)
 	: _realNode(realNode) {
 }
 
+FSNode::~FSNode() {
+}
+
 FSNode::FSNode(const Path &p) {
 	assert(g_system);
 	FilesystemFactory *factory = g_system->getFilesystemFactory();
@@ -176,6 +206,19 @@ bool FSNode::isDirectory() const {
 	return _realNode && _realNode->isDirectory();
 }
 
+void FSNode::listChildren(ArchiveMemberList &childList, const char *pattern) const {
+	Common::FSList fsList;
+	if (!getChildren(fsList, Common::FSNode::kListAll))
+		return;
+
+	for (const Common::FSNode &fsNode : fsList) {
+		if (pattern != nullptr && !fsNode.getName().matchString(pattern))
+			continue;
+
+		childList.push_back(ArchiveMemberPtr(new FSNode(fsNode)));
+	}
+}
+
 bool FSNode::isReadable() const {
 	return _realNode && _realNode->isReadable();
 }
@@ -299,6 +342,14 @@ bool FSDirectory::hasFile(const Path &path) const {
 	return node && node->exists();
 }
 
+bool FSDirectory::isPathDirectory(const Path &path) const {
+	if (path.toString().empty() || !_node.isDirectory())
+		return false;
+
+	FSNode *node = lookupCache(_fileCache, path);
+	return node && node->isDirectory();
+}
+
 const ArchiveMemberPtr FSDirectory::getMember(const Path &path) const {
 	if (path.toString().empty() || !_node.isDirectory())
 		return ArchiveMemberPtr();
diff --git a/common/fs.h b/common/fs.h
index 6fa54266f2a..8581230e032 100644
--- a/common/fs.h
+++ b/common/fs.h
@@ -106,8 +106,7 @@ public:
 	 * used (usually the root directory).
 	 */
 	explicit FSNode(const Path &path);
-
-	virtual ~FSNode() {}
+	~FSNode();
 
 	/**
 	 * Compare the name of this node to the name of another. Directories
@@ -223,7 +222,13 @@ public:
 	 * Or even replace isDirectory by a getType() method that can return values like
 	 * kDirNodeType, kFileNodeType, kInvalidNodeType.
 	 */
-	bool isDirectory() const;
+	bool isDirectory() const override;
+
+	/**
+	 * Adds the immediate children of this FSNode to a list, optionally matching a pattern.
+	 * Has no effect if this FSNode is not a directory.
+	 */
+	void listChildren(Common::ArchiveMemberList &childList, const char *pattern = nullptr) const override;
 
 	/**
 	 * Indicate whether the object referred by this node can be read from or not.
@@ -258,7 +263,7 @@ public:
 	 *
 	 * @return Pointer to the stream object, nullptr in case of a failure.
 	 */
-	SeekableReadStream *createReadStream() const override;
+	SeekableReadStream *createReadStream() const;
 
 	/**
 	 * Create a SeekableReadStream instance corresponding to an alternate stream
@@ -268,7 +273,7 @@ public:
 	 *
 	 * @return Pointer to the stream object, nullptr in case of a failure.
 	 */
-	SeekableReadStream *createReadStreamForAltStream(AltStreamType altStreamType) const override;
+	SeekableReadStream *createReadStreamForAltStream(AltStreamType altStreamType) const;
 
 	/**
 	 * Create a WriteStream instance corresponding to the file
@@ -410,6 +415,11 @@ public:
 	 */
 	bool hasFile(const Path &path) const override;
 
+	/**
+	 * Check if a specified subpath is a directory.
+	 */
+	bool isPathDirectory(const Path &path) const override;
+
 	/**
 	 * Return a list of matching file names. Pattern can use GLOB wildcards.
 	 */


Commit: 63b01f5e85771909a9b3144ed5723945854bde76
    https://github.com/scummvm/scummvm/commit/63b01f5e85771909a9b3144ed5723945854bde76
Author: elasota (ejlasota at gmail.com)
Date: 2023-09-17T19:17:33+02:00

Commit Message:
COMMON: Strip trailing slashes from ZipArchive directories and improve directory detection.  Add support for listChildren and isDirectory.

Changed paths:
    common/compression/unzip.cpp


diff --git a/common/compression/unzip.cpp b/common/compression/unzip.cpp
index cc5027a4dd3..df49edcca57 100644
--- a/common/compression/unzip.cpp
+++ b/common/compression/unzip.cpp
@@ -535,14 +535,49 @@ unzFile unzOpen(Common::SeekableReadStream *stream, bool flattenTree) {
 		fe.cur_file_info = us->cur_file_info;
 		fe.cur_file_info_internal = us->cur_file_info_internal;
 
-		const char *name = szCurrentFileName;
+		Common::String name(szCurrentFileName);
 
-		if (flattenTree)
-			for (const char *p = szCurrentFileName; *p; p++)
-				if (*p == '\\' || *p == '/')
-					name = p + 1;
+		bool isDirectory = false;
+		if (name.hasSuffix("/") || name.hasSuffix("\\")) {
+			isDirectory = true;
+			name = name.substr(0, name.size() - 1);
+		}
+
+		// If platform is specified as MS-DOS or Unix, check the directory flag
+		if (!isDirectory) {
+			int platform = (us->cur_file_info.version >> 8) & 0xff;
+			switch (platform) {
+			case 1: // Amiga
+				isDirectory = ((us->cur_file_info.external_fa & 0xc000000u) == 0x8000000u); // ((external_fa >> 16) & IFMT) == IFDIR
+				break;
+			case 0: // FAT (MS-DOS)
+			case 6: // HPFS (OS/2)
+			case 11: // NTFS
+			case 14: // VFAT
+				isDirectory = ((us->cur_file_info.external_fa & 0x10) == 0x10); // external_fa & FILE_ATTRIBUTE_DIRECTORY
+				break;
+			case 3: // Unix
+				isDirectory = ((us->cur_file_info.external_fa & 0xf0000000u) == 0x40000000u); // S_ISDIR(external_fa >> 16)
+				break;
+			default:
+				break;
+			}
+		}
 
-		us->_hash[Common::String(name)] = fe;
+		if (flattenTree) {
+			if (isDirectory)
+				continue;
+
+			size_t slashPos = name.findLastOf('\\');
+			if (slashPos != Common::String::npos)
+				name = name.substr(slashPos + 1);
+
+			slashPos = name.findLastOf('/');
+			if (slashPos != Common::String::npos)
+				name = name.substr(slashPos + 1);
+		}
+
+		us->_hash[name] = fe;
 
 		// Move to the next file
 		err = unzGoToNextFile((unzFile)us);
@@ -1001,6 +1036,7 @@ public:
 	~ZipArchive();
 
 	bool hasFile(const Path &path) const override;
+	bool isPathDirectory(const Path &path) const override;
 	int listMembers(ArchiveMemberList &list) const override;
 	const ArchiveMemberPtr getMember(const Path &path) const override;
 	Common::SharedArchiveContents readContentsForPath(const Common::String& translated) const override;
@@ -1040,6 +1076,19 @@ bool ZipArchive::hasFile(const Path &path) const {
 	return (unzLocateFile(_zipFile, name.c_str(), 2) == UNZ_OK);
 }
 
+bool ZipArchive::isPathDirectory(const Path &path) const {
+	String name = path.toString();
+
+	if (unzLocateFile(_zipFile, name.c_str(), 2) != UNZ_OK)
+		return false;
+
+	unz_file_info fi;
+	if (unzGetCurrentFileInfo(_zipFile, &fi, nullptr, 0, nullptr, 0, nullptr, 0) != UNZ_OK)
+		return false;
+
+	return (fi.external_fa & 0x10) != 0;
+}
+
 int ZipArchive::listMembers(ArchiveMemberList &list) const {
 	int members = 0;
 


Commit: e0b8e6685e1e317bcced3678db7160dda763f636
    https://github.com/scummvm/scummvm/commit/e0b8e6685e1e317bcced3678db7160dda763f636
Author: elasota (ejlasota at gmail.com)
Date: 2023-09-17T19:17:33+02:00

Commit Message:
SWORD25: Fix extracted files support and update ZipArchive handling to process directories

Changed paths:
    engines/sword25/package/packagemanager.cpp


diff --git a/engines/sword25/package/packagemanager.cpp b/engines/sword25/package/packagemanager.cpp
index ddf6c3ba211..ec287bc10cf 100644
--- a/engines/sword25/package/packagemanager.cpp
+++ b/engines/sword25/package/packagemanager.cpp
@@ -272,36 +272,17 @@ int PackageManager::doSearch(Common::ArchiveMemberList &list, const Common::Stri
 
 		// Create a list of the matching names
 		for (Common::ArchiveMemberList::iterator it = memberList.begin(); it != memberList.end(); ++it) {
-			Common::String name;
-			bool matchType;
-
-			// FSNode->getName() returns only name of the file, without the directory
-			// getPath() returns full path in the FS. Thus, we're getting it and
-			// removing the root from it.
-			if (_extractedFiles) {
-				Common::FSNode *node = (Common::FSNode *)(it->get());
-
-				name = node->getPath().substr(_directoryName.size());
-
-				for (uint c = 0; c < name.size(); c++) {
-					if (name[c] == '\\')
-						name.replace(c, 1, "/");
-				}
-
-				matchType = (((typeFilter & PackageManager::FT_DIRECTORY) && node->isDirectory()) ||
-					((typeFilter & PackageManager::FT_FILE) && !node->isDirectory()));
-			} else {
-				name = (*it)->getName();
-				matchType = ((typeFilter & PackageManager::FT_DIRECTORY) && name.hasSuffix("/")) ||
-				((typeFilter & PackageManager::FT_FILE) && !name.hasSuffix("/"));
-			}
+			Common::Path name = (*it)->getPathInArchive();
+			bool isDirectory = (*it)->isDirectory();
+			bool matchType = (((typeFilter & PackageManager::FT_DIRECTORY) && isDirectory) ||
+							  ((typeFilter & PackageManager::FT_FILE) && !isDirectory));
 
 			if (matchType) {
 
 				// Do not add duplicate files
 				bool found = false;
 				for (Common::ArchiveMemberList::iterator it1 = list.begin(); it1 != list.end(); ++it1) {
-					if ((*it1)->getName() == name) {
+					if ((*it1)->getPathInArchive() == name) {
 						found = true;
 						break;
 					}
@@ -309,7 +290,7 @@ int PackageManager::doSearch(Common::ArchiveMemberList &list, const Common::Stri
 
 				if (!found) {
 					list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(name, *(*i)->archive)));
-					debug(9, "> %s", name.c_str());
+					debug(9, "> %s", name.toString().c_str());
 				}
 				num++;
 			}




More information about the Scummvm-git-logs mailing list