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

phcoder noreply at scummvm.org
Wed Dec 14 04:08:54 UTC 2022


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

Summary:
af7d9cb850 COMMON: Move punycode_*codepath to path.cpp
d02a25230d COMMON: Add appendComponent to Common::Path for convenience
ce55430f1c COMMON: Don't access DIR_SEPARATOR in macresman
43d7259ff9 COMMON: Add matchPattern method for Common::Path
ab517cc577 COMMON: Add case-insensitive comparator and hash for Common::Path
78b6e60057 COMMON: Change common/fs to use Common::Path as key
4a2e69f990 COMMON: conflate : and / in mac names.
7ded5c2de0 COMMON: Move DIR_SEPARATOR into path.cpp
97bc2d843a SCI: Don't use rawString
f3daf65116 GOB: Don't use rawString.
673e5e025b COMMON: Remove rawString member for Common::Path
a690a034cf COMMON: Remove reliance that \x1f is never part of filename


Commit: af7d9cb850b67194351a53468619ead3fa632023
    https://github.com/scummvm/scummvm/commit/af7d9cb850b67194351a53468619ead3fa632023
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
COMMON: Move punycode_*codepath to path.cpp

This allows to reduce access to DIR_SEPARATOR from outside of Common::Path

Changed paths:
    common/path.cpp
    common/path.h
    common/punycode.cpp
    common/punycode.h
    engines/director/director.cpp
    engines/director/util.cpp
    engines/wage/wage.cpp


diff --git a/common/path.cpp b/common/path.cpp
index e152fa3f80b..459fea5389b 100644
--- a/common/path.cpp
+++ b/common/path.cpp
@@ -20,6 +20,8 @@
  */
 
 #include "common/path.h"
+#include "common/tokenizer.h"
+#include "common/punycode.h"
 
 namespace Common {
 
@@ -53,7 +55,7 @@ Path Path::getParent() const {
 	if (_str.size() < 2)
 		return Path();
 	size_t separatorPos = _str.findLastOf(DIR_SEPARATOR, _str.size() - 2);
-	if (separatorPos == Common::String::npos)
+	if (separatorPos == String::npos)
 		return Path();
 	return Path(_str.substr(0, separatorPos + 1), DIR_SEPARATOR);
 }
@@ -62,7 +64,7 @@ Path Path::getLastComponent() const {
 	if (_str.size() < 2)
 		return *this;
 	size_t separatorPos = _str.findLastOf(DIR_SEPARATOR, _str.size() - 2);
-	if (separatorPos == Common::String::npos)
+	if (separatorPos == String::npos)
 		return *this;
 	return Path(_str.substr(separatorPos + 1), DIR_SEPARATOR);
 }
@@ -182,4 +184,35 @@ Path Path::join(const char *str, char separator) const {
 	return temp;
 }
 
+Path Path::punycodeDecode() const {
+	StringTokenizer tok(rawString(), String(DIR_SEPARATOR));
+	String res;
+
+	while (!tok.empty()) {
+		res += punycode_decodefilename(tok.nextToken());
+		if (!tok.empty())
+			res += DIR_SEPARATOR;
+	}
+
+	return Path(res, DIR_SEPARATOR);
+}
+
+Path Path::punycodeEncode() const {
+	StringTokenizer tok(rawString(), String(DIR_SEPARATOR));
+	String res;
+
+	while (!tok.empty()) {
+		String part = tok.nextToken();
+		if (punycode_needEncode(part))
+			res += punycode_encodefilename(part);
+		else
+			res += part;
+
+		if (!tok.empty())
+			res += DIR_SEPARATOR;
+	}
+
+	return Path(res, DIR_SEPARATOR);
+}
+
 } // End of namespace Common
diff --git a/common/path.h b/common/path.h
index cc5b214d394..1c9c2941c09 100644
--- a/common/path.h
+++ b/common/path.h
@@ -175,6 +175,16 @@ public:
 
 	/** @overload */
 	Path join(const char *str, char separator = '/') const;
+
+	/**
+	 * Convert path from Punycode
+	 */
+	Path punycodeDecode() const;
+
+        /**
+	 * Convert path to Punycode
+	 */
+	Path punycodeEncode() const;
 };
 
 /** @} */
diff --git a/common/punycode.cpp b/common/punycode.cpp
index 76ff100b0fa..c894db4c002 100644
--- a/common/punycode.cpp
+++ b/common/punycode.cpp
@@ -43,7 +43,6 @@
 
 #include "common/punycode.h"
 #include "common/debug.h"
-#include "common/tokenizer.h"
 #include "common/util.h"
 
 namespace Common {
@@ -412,35 +411,4 @@ U32String punycode_decodefilename(const String &src1) {
 	return dst;
 }
 
-Path punycode_decodepath(const Path &src) {
-	StringTokenizer tok(src.rawString(), Common::String(DIR_SEPARATOR));
-	String res;
-
-	while (!tok.empty()) {
-		res += punycode_decodefilename(tok.nextToken());
-		if (!tok.empty())
-			res += DIR_SEPARATOR;
-	}
-
-	return Path(res, DIR_SEPARATOR);
-}
-
-Path punycode_encodepath(const Path &src) {
-	StringTokenizer tok(src.rawString(), Common::String(DIR_SEPARATOR));
-	String res;
-
-	while (!tok.empty()) {
-		String part = tok.nextToken();
-		if (punycode_needEncode(part))
-			res += punycode_encodefilename(part);
-		else
-			res += part;
-
-		if (!tok.empty())
-			res += DIR_SEPARATOR;
-	}
-
-	return Path(res, DIR_SEPARATOR);
-}
-
 } // end of namespace Common
diff --git a/common/punycode.h b/common/punycode.h
index 822a11b5a54..cad4f32408b 100644
--- a/common/punycode.h
+++ b/common/punycode.h
@@ -65,16 +65,6 @@ String punycode_encodefilename(const U32String &src1);
  */
 U32String punycode_decodefilename(const String &src1);
 
-/**
- * Convert path from Punycode
- */
-Path punycode_decodepath(const Path &src);
-
-/**
- * Convert path to Punycode
- */
-Path punycode_encodepath(const Path &src);
-
 bool punycode_hasprefix(const String &src);
 
 bool punycode_needEncode(const String &src);
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
index 2f4f74a6caa..9c3c7ce8273 100644
--- a/engines/director/director.cpp
+++ b/engines/director/director.cpp
@@ -323,7 +323,7 @@ void DirectorEngine::parseOptions() {
 					_options.startMovie.startFrame = atoi(tail.c_str());
 			}
 
-			_options.startMovie.startMovie = Common::punycode_decodepath(_options.startMovie.startMovie).toString(_dirSeparator);
+			_options.startMovie.startMovie = Common::Path(_options.startMovie.startMovie).punycodeDecode().toString(_dirSeparator);
 
 			debug(2, "parseOptions(): Movie is: %s, frame is: %d", _options.startMovie.startMovie.c_str(), _options.startMovie.startFrame);
 		} else if (key == "startup") {
diff --git a/engines/director/util.cpp b/engines/director/util.cpp
index 464b963681a..acb4d0376f6 100644
--- a/engines/director/util.cpp
+++ b/engines/director/util.cpp
@@ -1015,7 +1015,7 @@ Common::u32char_type_t numToChar(int num) {
 }
 
 Common::String encodePathForDump(const Common::String &path) {
-	return punycode_encodepath(Common::Path(path, g_director->_dirSeparator)).toString();
+	return Common::Path(path, g_director->_dirSeparator).punycodeEncode().toString();
 }
 
 Common::String utf8ToPrintable(const Common::String &str) {
diff --git a/engines/wage/wage.cpp b/engines/wage/wage.cpp
index 13837a1f53e..5fec12182c8 100644
--- a/engines/wage/wage.cpp
+++ b/engines/wage/wage.cpp
@@ -124,7 +124,7 @@ Common::Error WageEngine::run() {
 
 	// Your main event loop should be (invoked from) here.
 	_resManager = new Common::MacResManager();
-	if (!_resManager->open(Common::punycode_decodepath(getGameFile()).toString('/')))
+	if (!_resManager->open(Common::Path(getGameFile()).punycodeDecode().toString('/')))
 		error("Could not open %s as a resource fork", getGameFile());
 
 	_world = new World(this);


Commit: d02a25230d5c98afa2a24b4bada1b2796c030d18
    https://github.com/scummvm/scummvm/commit/d02a25230d5c98afa2a24b4bada1b2796c030d18
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
COMMON: Add appendComponent to Common::Path for convenience

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


diff --git a/common/path.cpp b/common/path.cpp
index 459fea5389b..736a8972fb8 100644
--- a/common/path.cpp
+++ b/common/path.cpp
@@ -69,6 +69,16 @@ Path Path::getLastComponent() const {
 	return Path(_str.substr(separatorPos + 1), DIR_SEPARATOR);
 }
 
+Path Path::appendComponent(const String &x) const {
+	if (x.empty())
+		return *this;
+	String str = _str;
+	if (!str.empty() && str.lastChar() != DIR_SEPARATOR)
+		str += DIR_SEPARATOR;
+	str += x;
+	return Path(str, DIR_SEPARATOR);
+}
+
 bool Path::operator==(const Path &x) const {
 	return _str == x.rawString();
 }
diff --git a/common/path.h b/common/path.h
index 1c9c2941c09..6ce0418b3bb 100644
--- a/common/path.h
+++ b/common/path.h
@@ -151,6 +151,11 @@ public:
 	/** @overload */
 	Path append(const char *str, char separator = '/') const;
 
+	/**
+	 * Appends exactly one component, without any separators
+	 * and prepends a separator if necessarry
+	 */
+	Path appendComponent(const String &x) const;
 
 	/**
 	 * Joins the given path to this path (in-place).


Commit: ce55430f1c2d68dedb7ca1cfb99eeebe54ee427e
    https://github.com/scummvm/scummvm/commit/ce55430f1c2d68dedb7ca1cfb99eeebe54ee427e
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
COMMON: Don't access DIR_SEPARATOR in macresman

DIR_SEPARATOR is an implementation detail of Common::Path, it should not
be accessed outside of it. Use public functions that do the same instead

Changed paths:
    common/macresman.cpp


diff --git a/common/macresman.cpp b/common/macresman.cpp
index b4556dd02f3..4cf244d9d8e 100644
--- a/common/macresman.cpp
+++ b/common/macresman.cpp
@@ -875,19 +875,9 @@ void MacResManager::readMap() {
 
 Path MacResManager::constructAppleDoubleName(Path name) {
 	// Insert "._" before the last portion of a path name
-	String rawName = name.rawString();
-	for (int i = rawName.size() - 1; i >= 0; i--) {
-		if (i == 0) {
-			rawName.insertChar('_', 0);
-			rawName.insertChar('.', 0);
-		} else if (rawName[i] == DIR_SEPARATOR) {
-			rawName.insertChar('_', i + 1);
-			rawName.insertChar('.', i + 1);
-			break;
-		}
-	}
-
-	return Path(rawName, DIR_SEPARATOR);
+	Path parent = name.getParent();
+	Path lastComponent = name.getLastComponent();
+	return parent.append("._").append(lastComponent);
 }
 
 Path MacResManager::disassembleAppleDoubleName(Path name, bool *isAppleDouble) {
@@ -896,27 +886,12 @@ Path MacResManager::disassembleAppleDoubleName(Path name, bool *isAppleDouble) {
 	}
 
 	// Remove "._" before the last portion of a path name.
-	String rawName = name.rawString();
-	for (int i = rawName.size() - 1; i >= 0; --i) {
-		if (i == 0) {
-			if (rawName.size() > 2 && rawName[0] == '.' && rawName[1] == '_') {
-				rawName.erase(0, 2);
-				if (isAppleDouble) {
-					*isAppleDouble = true;
-				}
-			}
-		} else if (rawName[i] == DIR_SEPARATOR) {
-			if ((uint)(i + 2) < rawName.size() && rawName[i + 1] == '.' && rawName[i + 2] == '_') {
-				rawName.erase(i + 1, 2);
-				if (isAppleDouble) {
-					*isAppleDouble = true;
-				}
-			}
-			break;
-		}
-	}
-
-	return Path(rawName, DIR_SEPARATOR);
+	Path parent = name.getParent();
+	Path lastComponent = name.getLastComponent();
+	String lastComponentString = lastComponent.toString();
+	if (!lastComponentString.hasPrefix("._"))
+		return name;
+	return parent.appendComponent(lastComponentString.substr(2));
 }
 
 void MacResManager::dumpRaw() {


Commit: 43d7259ff9456b812980f303809997975caef2fb
    https://github.com/scummvm/scummvm/commit/43d7259ff9456b812980f303809997975caef2fb
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
COMMON: Add matchPattern method for Common::Path

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


diff --git a/common/path.cpp b/common/path.cpp
index 736a8972fb8..97c72e74441 100644
--- a/common/path.cpp
+++ b/common/path.cpp
@@ -225,4 +225,11 @@ Path Path::punycodeEncode() const {
 	return Path(res, DIR_SEPARATOR);
 }
 
+bool Path::matchPattern(const Path& pattern) const {
+	// Prevent wildcards from matching the directory separator.
+	const char wildcardExclusions[] = { DIR_SEPARATOR, '\0' };
+
+	return punycodeDecode()._str.matchString(pattern.punycodeDecode()._str, true, wildcardExclusions);
+}
+
 } // End of namespace Common
diff --git a/common/path.h b/common/path.h
index 6ce0418b3bb..6d333285b12 100644
--- a/common/path.h
+++ b/common/path.h
@@ -190,6 +190,11 @@ public:
 	 * Convert path to Punycode
 	 */
 	Path punycodeEncode() const;
+
+        /**
+	 * Check pattern match similar matchString
+	 */
+	bool matchPattern(const Path& pattern) const;
 };
 
 /** @} */


Commit: ab517cc577d4eba17d5b79d29132924800cc11a3
    https://github.com/scummvm/scummvm/commit/ab517cc577d4eba17d5b79d29132924800cc11a3
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
COMMON: Add case-insensitive comparator and hash for Common::Path

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


diff --git a/common/path.cpp b/common/path.cpp
index 97c72e74441..82a596f6eb5 100644
--- a/common/path.cpp
+++ b/common/path.cpp
@@ -22,6 +22,7 @@
 #include "common/path.h"
 #include "common/tokenizer.h"
 #include "common/punycode.h"
+#include "common/hash-str.h"
 
 namespace Common {
 
@@ -232,4 +233,12 @@ bool Path::matchPattern(const Path& pattern) const {
 	return punycodeDecode()._str.matchString(pattern.punycodeDecode()._str, true, wildcardExclusions);
 }
 
+bool Path::IgnoreCaseAndMac_EqualsTo::operator()(const Path& x, const Path& y) const {
+	return x.punycodeDecode()._str.equalsIgnoreCase(y.punycodeDecode()._str);
+}
+
+uint Path::IgnoreCaseAndMac_Hash::operator()(const Path& x) const {
+	return hashit_lower(x.punycodeDecode()._str.c_str());
+}
+
 } // End of namespace Common
diff --git a/common/path.h b/common/path.h
index 6d333285b12..1e0f1a6addf 100644
--- a/common/path.h
+++ b/common/path.h
@@ -52,6 +52,23 @@ private:
 	String _str;
 
 public:
+	/**
+	 * Hash and comparator for Path with following changes:
+	 * * case-insensitive
+	 * * decoding of punycode
+	 * * Matching ':' and '/' inside path components to
+	 * This allows a path "Sound Manager 3.1 / SoundLib<separator>Sound"
+	 * to match both "xn--Sound Manager 3.1  SoundLib-lba84k/Sound"
+	 * and "Sound Manager 3.1 : SoundLib/Sound"
+	 */
+	struct IgnoreCaseAndMac_EqualsTo {
+		bool operator()(const Path& x, const Path& y) const;
+	};
+
+	struct IgnoreCaseAndMac_Hash {
+		uint operator()(const Path& x) const;
+	};
+
 	/** Construct a new empty path. */
 	Path() {}
 


Commit: 78b6e60057ad5ac7043d7710286fe5428de278e5
    https://github.com/scummvm/scummvm/commit/78b6e60057ad5ac7043d7710286fe5428de278e5
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
COMMON: Change common/fs to use Common::Path as key

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


diff --git a/common/fs.cpp b/common/fs.cpp
index cb67c45b12f..74a7c53cf1f 100644
--- a/common/fs.cpp
+++ b/common/fs.cpp
@@ -97,6 +97,11 @@ String FSNode::getName() const {
 	return punycode_decodefilename(_realNode->getName());
 }
 
+String FSNode::getRealName() const {
+	assert(_realNode);
+	return _realNode->getName();
+}
+
 FSNode FSNode::getParent() const {
 	if (_realNode == nullptr)
 		return *this;
@@ -179,7 +184,7 @@ FSDirectory::FSDirectory(const Path &prefix, const FSNode &node, int depth, bool
   : _node(node), _cached(false), _depth(depth), _flat(flat), _ignoreClashes(ignoreClashes),
 	_includeDirectories(includeDirectories) {
 
-	setPrefix(prefix.rawString());
+	setPrefix(prefix);
 }
 
 FSDirectory::FSDirectory(const Path &name, int depth, bool flat, bool ignoreClashes, bool includeDirectories)
@@ -192,24 +197,21 @@ FSDirectory::FSDirectory(const Path &prefix, const Path &name, int depth, bool f
   : _node(name), _cached(false), _depth(depth), _flat(flat), _ignoreClashes(ignoreClashes),
 	_includeDirectories(includeDirectories) {
 
-	setPrefix(prefix.rawString());
+	setPrefix(prefix);
 }
 
 FSDirectory::~FSDirectory() {
 }
 
-void FSDirectory::setPrefix(const String &prefix) {
+void FSDirectory::setPrefix(const Path &prefix) {
 	_prefix = prefix;
-
-	if (!_prefix.empty() && _prefix.lastChar() != DIR_SEPARATOR)
-		_prefix += DIR_SEPARATOR;
 }
 
 FSNode FSDirectory::getFSNode() const {
 	return _node;
 }
 
-FSNode *FSDirectory::lookupCache(NodeCache &cache, const String &name) const {
+FSNode *FSDirectory::lookupCache(NodeCache &cache, const Path &name) const {
 	// make caching as lazy as possible
 	if (!name.empty()) {
 		ensureCached();
@@ -222,26 +224,24 @@ FSNode *FSDirectory::lookupCache(NodeCache &cache, const String &name) const {
 }
 
 bool FSDirectory::hasFile(const Path &path) const {
-	String name = path.rawString();
-	if (name.empty() || !_node.isDirectory())
+	if (path.toString().empty() || !_node.isDirectory())
 		return false;
 
-	FSNode *node = lookupCache(_fileCache, name);
+	FSNode *node = lookupCache(_fileCache, path);
 	return node && node->exists();
 }
 
 const ArchiveMemberPtr FSDirectory::getMember(const Path &path) const {
-	String name = path.rawString();
-	if (name.empty() || !_node.isDirectory())
+	if (path.toString().empty() || !_node.isDirectory())
 		return ArchiveMemberPtr();
 
-	FSNode *node = lookupCache(_fileCache, name);
+	FSNode *node = lookupCache(_fileCache, path);
 
 	if (!node || !node->exists()) {
-		warning("FSDirectory::getMember: '%s' does not exist", Common::toPrintable(name).c_str());
+		warning("FSDirectory::getMember: '%s' does not exist", Common::toPrintable(path.toString()).c_str());
 		return ArchiveMemberPtr();
 	} else if (node->isDirectory()) {
-		warning("FSDirectory::getMember: '%s' is a directory", Common::toPrintable(name).c_str());
+		warning("FSDirectory::getMember: '%s' is a directory", Common::toPrintable(path.toString()).c_str());
 		return ArchiveMemberPtr();
 	}
 
@@ -249,16 +249,15 @@ const ArchiveMemberPtr FSDirectory::getMember(const Path &path) const {
 }
 
 SeekableReadStream *FSDirectory::createReadStreamForMember(const Path &path) const {
-	String name = path.rawString();
-	if (name.empty() || !_node.isDirectory())
+	if (path.toString().empty() || !_node.isDirectory())
 		return nullptr;
 
-	FSNode *node = lookupCache(_fileCache, name);
+	FSNode *node = lookupCache(_fileCache, path);
 	if (!node)
 		return nullptr;
 	SeekableReadStream *stream = node->createReadStream();
 	if (!stream)
-		warning("FSDirectory::createReadStreamForMember: Can't create stream for file '%s'", Common::toPrintable(name).c_str());
+		warning("FSDirectory::createReadStreamForMember: Can't create stream for file '%s'", Common::toPrintable(path.toString()).c_str());
 
 	return stream;
 }
@@ -269,11 +268,10 @@ FSDirectory *FSDirectory::getSubDirectory(const Path &name, int depth, bool flat
 
 FSDirectory *FSDirectory::getSubDirectory(const Path &prefix, const Path &name, int depth,
 		bool flat, bool ignoreClashes) {
-	String rawName = name.rawString();
-	if (rawName.empty() || !_node.isDirectory())
+	if (name.toString().empty() || !_node.isDirectory())
 		return nullptr;
 
-	FSNode *node = lookupCache(_subDirCache, rawName);
+	FSNode *node = lookupCache(_subDirCache, name);
 	if (!node)
 		return nullptr;
 
@@ -289,37 +287,33 @@ void FSDirectory::cacheDirectoryRecursive(FSNode node, int depth, const Path& pr
 
 	FSList::iterator it = list.begin();
 	for ( ; it != list.end(); ++it) {
-		String name = prefix.rawString() + it->getName();
-
-		// don't touch name as it might be used for warning messages
-		String lowercaseName = name;
-		lowercaseName.toLowercase();
+		Path name = prefix.appendComponent(it->getRealName());
 
 		// since the hashmap is case insensitive, we need to check for clashes when caching
 		if (it->isDirectory()) {
-			if (!_flat && _subDirCache.contains(lowercaseName)) {
+			if (!_flat && _subDirCache.contains(name)) {
 				// Always warn in this case as it's when there are 2 directories at the same place with different case
 				// That means a problem in user installation as lookups are always done case insensitive
 				warning("FSDirectory::cacheDirectory: name clash when building cache, ignoring sub-directory '%s'",
-				        Common::toPrintable(name).c_str());
+				        Common::toPrintable(name.toString('/')).c_str());
 			} else {
-				if (_subDirCache.contains(lowercaseName)) {
+				if (_subDirCache.contains(name)) {
 					if (!_ignoreClashes) {
 						warning("FSDirectory::cacheDirectory: name clash when building subDirCache with subdirectory '%s'",
-						        Common::toPrintable(name).c_str());
+						        Common::toPrintable(name.toString('/')).c_str());
 					}
 				}
-				cacheDirectoryRecursive(*it, depth - 1, _flat ? prefix : Common::Path(lowercaseName + DIR_SEPARATOR, DIR_SEPARATOR));
-				_subDirCache[lowercaseName] = *it;
+				cacheDirectoryRecursive(*it, depth - 1, _flat ? prefix : name);
+				_subDirCache[name] = *it;
 			}
 		} else {
-			if (_fileCache.contains(lowercaseName)) {
+			if (_fileCache.contains(name)) {
 				if (!_ignoreClashes) {
 					warning("FSDirectory::cacheDirectory: name clash when building cache, ignoring file '%s'",
-					        Common::toPrintable(name).c_str());
+					        Common::toPrintable(name.toString('/')).c_str());
 				}
 			} else {
-				_fileCache[lowercaseName] = *it;
+				_fileCache[name] = *it;
 			}
 		}
 	}
@@ -340,24 +334,16 @@ int FSDirectory::listMatchingMembers(ArchiveMemberList &list, const Path &patter
 	// Cache dir data
 	ensureCached();
 
-	// need to match lowercase key, since all entries in our file cache are
-	// stored as lowercase.
-	String lowercasePattern(pattern.rawString());
-	lowercasePattern.toLowercase();
-
-	// Prevent wildcards from matching the directory separator.
-	const char wildcardExclusions[] = { DIR_SEPARATOR, '\0' };
-
 	int matches = 0;
 	for (NodeCache::const_iterator it = _fileCache.begin(); it != _fileCache.end(); ++it) {
-		if (it->_key.matchString(lowercasePattern, false, wildcardExclusions)) {
+		if (it->_key.matchPattern(pattern)) {
 			list.push_back(ArchiveMemberPtr(new FSNode(it->_value)));
 			matches++;
 		}
 	}
 	if (_includeDirectories) {
 		for (NodeCache::const_iterator it = _subDirCache.begin(); it != _subDirCache.end(); ++it) {
-			if (it->_key.matchString(lowercasePattern, false, wildcardExclusions)) {
+			if (it->_key.matchPattern(pattern)) {
 				list.push_back(ArchiveMemberPtr(new FSNode(it->_value)));
 				matches++;
 			}
diff --git a/common/fs.h b/common/fs.h
index 57521cbb53f..384a84a106e 100644
--- a/common/fs.h
+++ b/common/fs.h
@@ -44,6 +44,7 @@ namespace Common {
  */
 
 class FSNode;
+class FSDirectory;
 class SeekableReadStream;
 class WriteStream;
 class SeekableWriteStream;
@@ -68,6 +69,7 @@ class FSList : public Array<FSNode> {};
 class FSNode : public ArchiveMember {
 private:
 	friend class ::AbstractFSNode;
+	friend class FSDirectory;
 	SharedPtr<AbstractFSNode>	_realNode;
 	/**
 	 * Construct an FSNode from a backend's AbstractFSNode implementation.
@@ -77,6 +79,7 @@ private:
 	 */
 	FSNode(AbstractFSNode *realNode);
 
+	String getRealName() const;
 public:
 	/**
 	 * Flag to tell listDir() which kind of files to list.
@@ -301,17 +304,17 @@ class FSDirectory : public Archive {
 	bool _ignoreClashes;
 	bool _includeDirectories;
 
-	String	_prefix; // string that is prepended to each cache item key
-	void setPrefix(const String &prefix);
+	Path	_prefix; // string that is prepended to each cache item key
+	void setPrefix(const Path &prefix);
 
 	// Caches are case insensitive, clashes are dealt with when creating
 	// Key is stored in lowercase.
-	typedef HashMap<String, FSNode, IgnoreCase_Hash, IgnoreCase_EqualTo> NodeCache;
+	typedef HashMap<Path, FSNode, Path::IgnoreCaseAndMac_Hash, Path::IgnoreCaseAndMac_EqualsTo> NodeCache;
 	mutable NodeCache	_fileCache, _subDirCache;
 	mutable bool _cached;
 
 	// look for a match
-	FSNode *lookupCache(NodeCache &cache, const String &name) const;
+	FSNode *lookupCache(NodeCache &cache, const Path &name) const;
 
 	// cache management
 	void cacheDirectoryRecursive(FSNode node, int depth, const Path& prefix) const;


Commit: 4a2e69f99094f5c95c827ce65cfa07fe6c161c0c
    https://github.com/scummvm/scummvm/commit/4a2e69f99094f5c95c827ce65cfa07fe6c161c0c
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
COMMON: conflate : and / in mac names.

When using mounted images on OSX or with fusehfs : and / are swapped.
When using dumper-companion it encodes / in punycode and preserves it.

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


diff --git a/common/path.cpp b/common/path.cpp
index 82a596f6eb5..2c2e7b2a646 100644
--- a/common/path.cpp
+++ b/common/path.cpp
@@ -208,6 +208,24 @@ Path Path::punycodeDecode() const {
 	return Path(res, DIR_SEPARATOR);
 }
 
+String Path::getIdentifierString() const {
+	StringTokenizer tok(rawString(), String(DIR_SEPARATOR));
+	String res;
+
+	while (!tok.empty()) {
+		String part = punycode_decodefilename(tok.nextToken());
+		for (uint i = 0; i < part.size(); i++)
+			if (part[i] == '/')
+				res += ':';
+			else
+				res += part[i];
+		if (!tok.empty())
+			res += DIR_SEPARATOR;
+	}
+
+	return res;
+}
+
 Path Path::punycodeEncode() const {
 	StringTokenizer tok(rawString(), String(DIR_SEPARATOR));
 	String res;
@@ -230,15 +248,15 @@ bool Path::matchPattern(const Path& pattern) const {
 	// Prevent wildcards from matching the directory separator.
 	const char wildcardExclusions[] = { DIR_SEPARATOR, '\0' };
 
-	return punycodeDecode()._str.matchString(pattern.punycodeDecode()._str, true, wildcardExclusions);
+	return getIdentifierString().matchString(pattern.getIdentifierString(), true, wildcardExclusions);
 }
 
 bool Path::IgnoreCaseAndMac_EqualsTo::operator()(const Path& x, const Path& y) const {
-	return x.punycodeDecode()._str.equalsIgnoreCase(y.punycodeDecode()._str);
+	return x.getIdentifierString().equalsIgnoreCase(y.getIdentifierString());
 }
 
 uint Path::IgnoreCaseAndMac_Hash::operator()(const Path& x) const {
-	return hashit_lower(x.punycodeDecode()._str.c_str());
+	return hashit_lower(x.getIdentifierString().c_str());
 }
 
 } // End of namespace Common
diff --git a/common/path.h b/common/path.h
index 1e0f1a6addf..db98ba6a10e 100644
--- a/common/path.h
+++ b/common/path.h
@@ -51,6 +51,7 @@ class Path {
 private:
 	String _str;
 
+	String getIdentifierString() const;
 public:
 	/**
 	 * Hash and comparator for Path with following changes:


Commit: 7ded5c2de0f3808487016c70568bdff60495bc44
    https://github.com/scummvm/scummvm/commit/7ded5c2de0f3808487016c70568bdff60495bc44
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
COMMON: Move DIR_SEPARATOR into path.cpp

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


diff --git a/common/path.cpp b/common/path.cpp
index 2c2e7b2a646..1fbf09d4b8d 100644
--- a/common/path.cpp
+++ b/common/path.cpp
@@ -24,6 +24,8 @@
 #include "common/punycode.h"
 #include "common/hash-str.h"
 
+const char DIR_SEPARATOR = '\x1f'; // unit separator
+
 namespace Common {
 
 Path::Path(const Path &path) {
diff --git a/common/path.h b/common/path.h
index db98ba6a10e..1123cf41e59 100644
--- a/common/path.h
+++ b/common/path.h
@@ -36,8 +36,6 @@ namespace Common {
  * @{
  */
 
-const char DIR_SEPARATOR = '\x1f'; // unit separator
-
 /**
  * Simple path class. Allows simple conversion to/from path strings with
  * arbitrary directory separators, providing a common representation.


Commit: 97bc2d843a5fe73174695094bad132abe78c8e5c
    https://github.com/scummvm/scummvm/commit/97bc2d843a5fe73174695094bad132abe78c8e5c
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
SCI: Don't use rawString

Changed paths:
    engines/sci/graphics/macfont.cpp


diff --git a/engines/sci/graphics/macfont.cpp b/engines/sci/graphics/macfont.cpp
index 12da36e83e9..206aeb95207 100644
--- a/engines/sci/graphics/macfont.cpp
+++ b/engines/sci/graphics/macfont.cpp
@@ -81,7 +81,7 @@ GfxMacFontManager::~GfxMacFontManager() {
 bool GfxMacFontManager::initFromFontTable(Common::MacResManager *macExecutable) {
 	Common::SeekableReadStream *table = macExecutable->getResource(MKTAG('f', 't', 'b', 'l'), 128);
 	if (table == nullptr) {
-		warning("Mac font table not found in \"%s\"", macExecutable->getBaseFileName().rawString().c_str());
+		warning("Mac font table not found in \"%s\"", macExecutable->getBaseFileName().toString(':').c_str());
 		return false;
 	}
 
@@ -89,14 +89,14 @@ bool GfxMacFontManager::initFromFontTable(Common::MacResManager *macExecutable)
 	uint16 defaultFontIndex = table->readUint16BE();
 	uint16 numberOfFonts = table->readUint16BE();
 	if (table->eos() || table->size() < 4 + numberOfFonts * 10) {
-		warning("Invalid mac font table in \"%s\"", macExecutable->getBaseFileName().rawString().c_str());
+		warning("Invalid mac font table in \"%s\"", macExecutable->getBaseFileName().toString(':').c_str());
 		return false;
 	}
 
 	for (uint16 i = 0; i < numberOfFonts; ++i) {
 		uint16 sciFontId = table->readUint16BE();
 		if (_macFonts.contains(sciFontId)) {
-			warning("Duplicate Mac font table entry for %d in \"%s\"", sciFontId, macExecutable->getBaseFileName().rawString().c_str());
+			warning("Duplicate Mac font table entry for %d in \"%s\"", sciFontId, macExecutable->getBaseFileName().toString(':').c_str());
 			return false;
 		}
 		uint16 macFontId = table->readUint16BE();
@@ -107,7 +107,7 @@ bool GfxMacFontManager::initFromFontTable(Common::MacResManager *macExecutable)
 		const Graphics::Font *smallFont = getMacFont(macFontId, smallFontSize);
 		const Graphics::Font *largeFont = getMacFont(macFontId, MAX(mediumFontSize, largeFontSize));
 		if (smallFont == nullptr || largeFont == nullptr) {
-			warning("Mac font %d not found in \"%s\"", macFontId, macExecutable->getBaseFileName().rawString().c_str());
+			warning("Mac font %d not found in \"%s\"", macFontId, macExecutable->getBaseFileName().toString(':').c_str());
 			return false;
 		}
 


Commit: f3daf651169dc0d479600b62d114e17e2aaa8b6b
    https://github.com/scummvm/scummvm/commit/f3daf651169dc0d479600b62d114e17e2aaa8b6b
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
GOB: Don't use rawString.

The result is being fed to File::open so \x1f and / get conflated later anyway,
so toString('/') has exactly the same effect.

Changed paths:
    engines/gob/inter_v1.cpp


diff --git a/engines/gob/inter_v1.cpp b/engines/gob/inter_v1.cpp
index 9d89998e734..5151581e709 100644
--- a/engines/gob/inter_v1.cpp
+++ b/engines/gob/inter_v1.cpp
@@ -1835,7 +1835,7 @@ void Inter_v1::o1_manageDataFile(OpFuncParams &params) {
 	Common::String file = _vm->_game->_script->evalString();
 
 	if (!file.empty()) {
-		_vm->_dataIO->openArchive(Common::Path(file, '\\').rawString(), true);
+		_vm->_dataIO->openArchive(Common::Path(file, '\\').toString('/'), true);
 	} else {
 		_vm->_dataIO->closeArchive(true);
 


Commit: 673e5e025b707fc7259fc35d1442e605f5fb9366
    https://github.com/scummvm/scummvm/commit/673e5e025b707fc7259fc35d1442e605f5fb9366
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
COMMON: Remove rawString member for Common::Path

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


diff --git a/common/path.cpp b/common/path.cpp
index 1fbf09d4b8d..98432c0ab22 100644
--- a/common/path.cpp
+++ b/common/path.cpp
@@ -29,7 +29,7 @@ const char DIR_SEPARATOR = '\x1f'; // unit separator
 namespace Common {
 
 Path::Path(const Path &path) {
-	_str = path.rawString();
+	_str = path._str;
 }
 
 Path::Path(const char *str, char separator) {
@@ -83,11 +83,11 @@ Path Path::appendComponent(const String &x) const {
 }
 
 bool Path::operator==(const Path &x) const {
-	return _str == x.rawString();
+	return _str == x._str;
 }
 
 bool Path::operator!=(const Path &x) const {
-	return _str != x.rawString();
+	return _str != x._str;
 }
 
 bool Path::empty() const {
@@ -95,7 +95,7 @@ bool Path::empty() const {
 }
 
 Path &Path::operator=(const Path &path) {
-	_str = path.rawString();
+	_str = path._str;
 	return *this;
 }
 
@@ -115,7 +115,7 @@ void Path::set(const char *str, char separator) {
 }
 
 Path &Path::appendInPlace(const Path &x) {
-	_str += x.rawString();
+	_str += x._str;
 	return *this;
 }
 
@@ -157,10 +157,10 @@ Path &Path::joinInPlace(const Path &x) {
 	if (x.empty())
 		return *this;
 
-	if (!_str.empty() && _str.lastChar() != DIR_SEPARATOR && x.rawString().firstChar() != DIR_SEPARATOR)
+	if (!_str.empty() && _str.lastChar() != DIR_SEPARATOR && x._str.firstChar() != DIR_SEPARATOR)
 		_str += DIR_SEPARATOR;
 
-	_str += x.rawString();
+	_str += x._str;
 
 	return *this;
 }
@@ -198,7 +198,7 @@ Path Path::join(const char *str, char separator) const {
 }
 
 Path Path::punycodeDecode() const {
-	StringTokenizer tok(rawString(), String(DIR_SEPARATOR));
+	StringTokenizer tok(_str, String(DIR_SEPARATOR));
 	String res;
 
 	while (!tok.empty()) {
@@ -211,7 +211,7 @@ Path Path::punycodeDecode() const {
 }
 
 String Path::getIdentifierString() const {
-	StringTokenizer tok(rawString(), String(DIR_SEPARATOR));
+	StringTokenizer tok(_str, String(DIR_SEPARATOR));
 	String res;
 
 	while (!tok.empty()) {
@@ -229,7 +229,7 @@ String Path::getIdentifierString() const {
 }
 
 Path Path::punycodeEncode() const {
-	StringTokenizer tok(rawString(), String(DIR_SEPARATOR));
+	StringTokenizer tok(_str, String(DIR_SEPARATOR));
 	String res;
 
 	while (!tok.empty()) {
diff --git a/common/path.h b/common/path.h
index 1123cf41e59..bbae406ee51 100644
--- a/common/path.h
+++ b/common/path.h
@@ -93,12 +93,6 @@ public:
 	 */
 	Path(const String &str, char separator = '/');
 
-	/**
-	 * Returns the unmodified, internal representation of the path,
-	 * using '\x1f' as a directory separator.
-	 */
-	const String &rawString() const { return _str; }
-
 	/**
 	 * Converts a path to a string using the given directory separator.
 	 * 


Commit: a690a034cf105bd16d5c74f208b5496a77997a68
    https://github.com/scummvm/scummvm/commit/a690a034cf105bd16d5c74f208b5496a77997a68
Author: Vladimir Serbinenko (phcoder at gmail.com)
Date: 2022-12-14T05:08:46+01:00

Commit Message:
COMMON: Remove reliance that \x1f is never part of filename

So far we never encountered such a case but mac disks are notorious for
having non-printable characters in names

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


diff --git a/common/path.cpp b/common/path.cpp
index 98432c0ab22..daaac85f3c6 100644
--- a/common/path.cpp
+++ b/common/path.cpp
@@ -20,11 +20,14 @@
  */
 
 #include "common/path.h"
-#include "common/tokenizer.h"
 #include "common/punycode.h"
 #include "common/hash-str.h"
 
-const char DIR_SEPARATOR = '\x1f'; // unit separator
+const char ESCAPER = '/';
+const char ESCAPE_SLASH = '+';
+const char ESCAPE_SEPARATOR = '/';
+const char *DIR_SEPARATOR = "//";
+const char *SLASH_ESCAPED = "/+";
 
 namespace Common {
 
@@ -37,49 +40,86 @@ Path::Path(const char *str, char separator) {
 }
 
 Path::Path(const String &str, char separator) {
-	if (separator == DIR_SEPARATOR)
-		_str = str;
-	else
-		set(str.c_str(), separator);
+	set(str.c_str(), separator);
 }
 
 String Path::toString(char separator) const {
 	String res;
-	for (const char *ptr = _str.c_str(); *ptr; ptr++) {
-		if (*ptr == DIR_SEPARATOR)
-			res += separator;
-		else
-			res += *ptr;
+	for (uint i = 0; i < _str.size(); i++) {
+		if (_str[i] == ESCAPER) {
+			i++;
+			if (_str[i] == ESCAPE_SLASH)
+				res += '/';
+			else if (_str[i] == ESCAPE_SEPARATOR)
+				res += separator;
+			else
+				error("Path::toString(): Malformed Common::Path. '%c' unexpected after '/'", _str[i]);
+		} else {
+			res += _str[i];
+		}
+	}
+	return res;
+}
+
+size_t Path::findLastSeparator() const {
+	size_t res = String::npos;
+	for (uint i = 0; i + 2 < _str.size(); i++) {
+		if (_str[i] == ESCAPER) {
+			i++;
+			if (_str[i] == ESCAPE_SEPARATOR)
+				res = i - 1;
+		}
 	}
+
 	return res;
 }
 
 Path Path::getParent() const {
 	if (_str.size() < 2)
 		return Path();
-	size_t separatorPos = _str.findLastOf(DIR_SEPARATOR, _str.size() - 2);
+	size_t separatorPos = findLastSeparator();
 	if (separatorPos == String::npos)
 		return Path();
-	return Path(_str.substr(0, separatorPos + 1), DIR_SEPARATOR);
+	Path ret;
+	ret._str = _str.substr(0, separatorPos + 2);
+	return ret;
 }
 
 Path Path::getLastComponent() const {
 	if (_str.size() < 2)
 		return *this;
-	size_t separatorPos = _str.findLastOf(DIR_SEPARATOR, _str.size() - 2);
+	size_t separatorPos = findLastSeparator();
 	if (separatorPos == String::npos)
 		return *this;
-	return Path(_str.substr(separatorPos + 1), DIR_SEPARATOR);
+	Path ret;
+	ret._str = _str.substr(separatorPos + 2);
+	return ret;
+}
+
+static String escapePath(const String& in) {
+	String ret;
+	for (uint i = 0; i < in.size(); i++) {
+		if (in[i] == '/')
+			ret += SLASH_ESCAPED;
+		else
+			ret += in[i];
+	}
+	return ret;
 }
 
 Path Path::appendComponent(const String &x) const {
 	if (x.empty())
 		return *this;
 	String str = _str;
-	if (!str.empty() && str.lastChar() != DIR_SEPARATOR)
+	size_t lastSep = findLastSeparator();
+	if (!str.empty() && (lastSep == String::npos || lastSep != str.size() - 2))
 		str += DIR_SEPARATOR;
-	str += x;
-	return Path(str, DIR_SEPARATOR);
+
+	str += escapePath(x);
+
+	Path ret;
+	ret._str = str;
+	return ret;
 }
 
 bool Path::operator==(const Path &x) const {
@@ -120,10 +160,7 @@ Path &Path::appendInPlace(const Path &x) {
 }
 
 Path &Path::appendInPlace(const String &str, char separator) {
-	if (separator == DIR_SEPARATOR)
-		_str += str;
-	else
-		appendInPlace(str.c_str(), separator);
+	appendInPlace(str.c_str(), separator);
 	return *this;
 }
 
@@ -131,6 +168,8 @@ Path &Path::appendInPlace(const char *str, char separator) {
 	for (; *str; str++) {
 		if (*str == separator)
 			_str += DIR_SEPARATOR;
+		else if (*str == '/') // Order matters as / may be the separator and often is.
+			_str += SLASH_ESCAPED;
 		else
 			_str += *str;
 	}
@@ -157,7 +196,8 @@ Path &Path::joinInPlace(const Path &x) {
 	if (x.empty())
 		return *this;
 
-	if (!_str.empty() && _str.lastChar() != DIR_SEPARATOR && x._str.firstChar() != DIR_SEPARATOR)
+	size_t lastSep = findLastSeparator();
+	if (!_str.empty() && (lastSep == String::npos || lastSep != _str.size() - 2) && x._str.hasPrefix(DIR_SEPARATOR))
 		_str += DIR_SEPARATOR;
 
 	_str += x._str;
@@ -173,7 +213,8 @@ Path &Path::joinInPlace(const char *str, char separator) {
 	if (*str == '\0')
 		return *this;
 
-	if (!_str.empty() && _str.lastChar() != DIR_SEPARATOR && *str != separator)
+	size_t lastSep = findLastSeparator();
+	if (!_str.empty() && (lastSep == String::npos || lastSep != _str.size() - 2) && *str != separator)
 		_str += DIR_SEPARATOR;
 
 	appendInPlace(str, separator);
@@ -193,35 +234,78 @@ Path Path::join(const String &str, char separator) const {
 
 Path Path::join(const char *str, char separator) const {
 	Path temp(*this);
-	temp.joinInPlace(str, DIR_SEPARATOR);
+	temp.joinInPlace(str, separator);
 	return temp;
 }
 
+StringArray Path::splitComponents() const {
+	StringArray res;
+	String cur;
+	for (uint i = 0; i < _str.size(); i++) {
+		if (_str[i] == ESCAPER) {
+			i++;
+			if (_str[i] == ESCAPE_SLASH)
+				cur += '/';
+			else if (_str[i] == ESCAPE_SEPARATOR) {
+				res.push_back(cur);
+				cur = "";
+			} else {
+				error("Path::splitComponents(): Malformed Common::Path. '%c' unexpected after '/'", _str[i]);
+			}
+		} else
+			cur += _str[i];
+	}
+
+	res.push_back(cur);
+
+	return res;
+}
+
 Path Path::punycodeDecode() const {
-	StringTokenizer tok(_str, String(DIR_SEPARATOR));
+	StringArray c = splitComponents();
 	String res;
 
-	while (!tok.empty()) {
-		res += punycode_decodefilename(tok.nextToken());
-		if (!tok.empty())
+	for (uint i = 0; i < c.size(); i++) {
+		res += escapePath(punycode_decodefilename(c[i]));
+		if (i + 1 < c.size())
 			res += DIR_SEPARATOR;
 	}
 
-	return Path(res, DIR_SEPARATOR);
+	Path ret;
+	ret._str = res;
+	return ret;
+}
+
+// See getIdentifierString() for more details.
+// This does the same but for a single path component and is used by
+// getIdentifierString().
+static String getIdentifierComponent(const String& in) {
+	String part = punycode_decodefilename(in);
+	String res = "";
+	for (uint j = 0; j < part.size(); j++)
+		if (part[j] == '/')
+			res += ':';
+		else
+			res += part[j];
+	return res;
 }
 
+// For a path creates a string with following property:
+// if 2 files have the same case-insensitive
+// identifier string then and only then we treat them as
+// effectively the same file. For this there are 2
+// transformations we need to do:
+// * decode punycode
+// * Replace / with : in path components so a path from
+// HFS(+) image will end up with : independently of how
+// it was dumped or copied from
 String Path::getIdentifierString() const {
-	StringTokenizer tok(_str, String(DIR_SEPARATOR));
+	StringArray c = splitComponents();
 	String res;
 
-	while (!tok.empty()) {
-		String part = punycode_decodefilename(tok.nextToken());
-		for (uint i = 0; i < part.size(); i++)
-			if (part[i] == '/')
-				res += ':';
-			else
-				res += part[i];
-		if (!tok.empty())
+	for (uint i = 0; i < c.size(); i++) {
+		res += getIdentifierComponent(c[i]);
+		if (i + 1 < c.size())
 			res += DIR_SEPARATOR;
 	}
 
@@ -229,28 +313,34 @@ String Path::getIdentifierString() const {
 }
 
 Path Path::punycodeEncode() const {
-	StringTokenizer tok(_str, String(DIR_SEPARATOR));
+	StringArray c = splitComponents();
 	String res;
 
-	while (!tok.empty()) {
-		String part = tok.nextToken();
-		if (punycode_needEncode(part))
-			res += punycode_encodefilename(part);
-		else
-			res += part;
-
-		if (!tok.empty())
+	for (uint i = 0; i < c.size(); i++) {
+		res += escapePath(punycode_encodefilename(c[i]));
+		if (i + 1 < c.size())
 			res += DIR_SEPARATOR;
 	}
 
-	return Path(res, DIR_SEPARATOR);
+	Path ret;
+	ret._str = res;
+	return ret;
 }
 
 bool Path::matchPattern(const Path& pattern) const {
+	StringArray c = splitComponents();
+	StringArray cpat = pattern.splitComponents();
+
 	// Prevent wildcards from matching the directory separator.
-	const char wildcardExclusions[] = { DIR_SEPARATOR, '\0' };
+	if (c.size() != cpat.size())
+		return false;
+
+	for (uint i = 0; i < c.size(); i++) {
+		if (!getIdentifierComponent(c[i]).matchString(getIdentifierComponent(cpat[i]), true))
+			return false;
+	}
 
-	return getIdentifierString().matchString(pattern.getIdentifierString(), true, wildcardExclusions);
+	return true;
 }
 
 bool Path::IgnoreCaseAndMac_EqualsTo::operator()(const Path& x, const Path& y) const {
diff --git a/common/path.h b/common/path.h
index bbae406ee51..8145bd2d6ac 100644
--- a/common/path.h
+++ b/common/path.h
@@ -24,6 +24,7 @@
 
 #include "common/scummsys.h"
 #include "common/str.h"
+#include "common/str-array.h"
 
 namespace Common {
 
@@ -41,15 +42,15 @@ namespace Common {
  * arbitrary directory separators, providing a common representation.
  * 
  * Internally, this is just a simple wrapper around a String, using
- * '\x1f' (unit separator) as a directory separator. As this is not
- * a printable character, it should not appear in file names, unlike
- * '/', '\', or ':', which are allowed on certain platforms.
+ * "//" (unit separator) as a directory separator and "/+" as "/".
  */
 class Path {
 private:
 	String _str;
 
 	String getIdentifierString() const;
+	size_t findLastSeparator() const;
+
 public:
 	/**
 	 * Hash and comparator for Path with following changes:
@@ -205,6 +206,15 @@ public:
 	 * Check pattern match similar matchString
 	 */
 	bool matchPattern(const Path& pattern) const;
+
+	/**
+	 * Splits into path components. After every component except
+	 * last there is an implied separator. First component is empty
+	 * if path starts with a separator. Last component is empty if
+	 * the path ends with a separator. Other components may be empty if
+	 * 2 separots follow each other
+	 */
+	StringArray splitComponents() const;
 };
 
 /** @} */




More information about the Scummvm-git-logs mailing list