COMMON: Add ignoreClashes flag to SearchSet and FSDirectory

This lets engine mute warnings about name clashes when there are useless
for its case. This will be used by Versailles as the tree has
directories with the same name at various places.
Files are duplicated too in different directories but are identical.
This commit is contained in:
Le Philousophe 2019-10-20 17:37:18 +02:00 committed by Eugene Sandulenko
parent b6132fec03
commit d62bb15d71
4 changed files with 57 additions and 25 deletions

View file

@ -115,7 +115,7 @@ void SearchSet::addDirectory(const String &name, const FSNode &dir, int priority
if (!dir.exists() || !dir.isDirectory()) if (!dir.exists() || !dir.isDirectory())
return; return;
add(name, new FSDirectory(dir, depth, flat), priority); add(name, new FSDirectory(dir, depth, flat, _ignoreClashes), priority);
} }
void SearchSet::addSubDirectoriesMatching(const FSNode &directory, String origPattern, bool ignoreCase, int priority, int depth, bool flat) { void SearchSet::addSubDirectoriesMatching(const FSNode &directory, String origPattern, bool ignoreCase, int priority, int depth, bool flat) {

View file

@ -152,7 +152,10 @@ class SearchSet : public Archive {
// Add an archive keeping the list sorted by descending priority. // Add an archive keeping the list sorted by descending priority.
void insert(const Node& node); void insert(const Node& node);
bool _ignoreClashes;
public: public:
SearchSet() : _ignoreClashes(false) { }
virtual ~SearchSet() { clear(); } virtual ~SearchSet() { clear(); }
/** /**
@ -247,6 +250,12 @@ public:
* opening the first file encountered that matches the name. * opening the first file encountered that matches the name.
*/ */
virtual SeekableReadStream *createReadStreamForMember(const String &name) const; virtual SeekableReadStream *createReadStreamForMember(const String &name) const;
/**
* Ignore clashes when adding directories. For more details see the corresponding parameter
* in FSDirectory documentation
*/
void setIgnoreClashes(bool ignoreClashes) { _ignoreClashes = ignoreClashes; }
}; };

View file

@ -168,22 +168,24 @@ bool FSNode::createDirectory() const {
return _realNode->createDirectory(); return _realNode->createDirectory();
} }
FSDirectory::FSDirectory(const FSNode &node, int depth, bool flat) FSDirectory::FSDirectory(const FSNode &node, int depth, bool flat, bool ignoreClashes)
: _node(node), _cached(false), _depth(depth), _flat(flat) { : _node(node), _cached(false), _depth(depth), _flat(flat), _ignoreClashes(ignoreClashes) {
} }
FSDirectory::FSDirectory(const String &prefix, const FSNode &node, int depth, bool flat) FSDirectory::FSDirectory(const String &prefix, const FSNode &node, int depth, bool flat,
: _node(node), _cached(false), _depth(depth), _flat(flat) { bool ignoreClashes)
: _node(node), _cached(false), _depth(depth), _flat(flat), _ignoreClashes(ignoreClashes) {
setPrefix(prefix); setPrefix(prefix);
} }
FSDirectory::FSDirectory(const String &name, int depth, bool flat) FSDirectory::FSDirectory(const String &name, int depth, bool flat, bool ignoreClashes)
: _node(name), _cached(false), _depth(depth), _flat(flat) { : _node(name), _cached(false), _depth(depth), _flat(flat), _ignoreClashes(ignoreClashes) {
} }
FSDirectory::FSDirectory(const String &prefix, const String &name, int depth, bool flat) FSDirectory::FSDirectory(const String &prefix, const String &name, int depth, bool flat,
: _node(name), _cached(false), _depth(depth), _flat(flat) { bool ignoreClashes)
: _node(name), _cached(false), _depth(depth), _flat(flat), _ignoreClashes(ignoreClashes) {
setPrefix(prefix); setPrefix(prefix);
} }
@ -253,11 +255,12 @@ SeekableReadStream *FSDirectory::createReadStreamForMember(const String &name) c
return stream; return stream;
} }
FSDirectory *FSDirectory::getSubDirectory(const String &name, int depth, bool flat) { FSDirectory *FSDirectory::getSubDirectory(const String &name, int depth, bool flat, bool ignoreClashes) {
return getSubDirectory(String(), name, depth, flat); return getSubDirectory(String(), name, depth, flat, ignoreClashes);
} }
FSDirectory *FSDirectory::getSubDirectory(const String &prefix, const String &name, int depth, bool flat) { FSDirectory *FSDirectory::getSubDirectory(const String &prefix, const String &name, int depth,
bool flat, bool ignoreClashes) {
if (name.empty() || !_node.isDirectory()) if (name.empty() || !_node.isDirectory())
return nullptr; return nullptr;
@ -265,7 +268,7 @@ FSDirectory *FSDirectory::getSubDirectory(const String &prefix, const String &na
if (!node) if (!node)
return nullptr; return nullptr;
return new FSDirectory(prefix, *node, depth, flat); return new FSDirectory(prefix, *node, depth, flat, ignoreClashes);
} }
void FSDirectory::cacheDirectoryRecursive(FSNode node, int depth, const String& prefix) const { void FSDirectory::cacheDirectoryRecursive(FSNode node, int depth, const String& prefix) const {
@ -286,17 +289,26 @@ void FSDirectory::cacheDirectoryRecursive(FSNode node, int depth, const String&
// since the hashmap is case insensitive, we need to check for clashes when caching // since the hashmap is case insensitive, we need to check for clashes when caching
if (it->isDirectory()) { if (it->isDirectory()) {
if (!_flat && _subDirCache.contains(lowercaseName)) { if (!_flat && _subDirCache.contains(lowercaseName)) {
warning("FSDirectory::cacheDirectory: name clash when building cache, ignoring sub-directory '%s'", name.c_str()); // 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'",
name.c_str());
} else { } else {
if (_subDirCache.contains(lowercaseName)) { if (_subDirCache.contains(lowercaseName)) {
warning("FSDirectory::cacheDirectory: name clash when building subDirCache with subdirectory '%s'", name.c_str()); if (!_ignoreClashes) {
warning("FSDirectory::cacheDirectory: name clash when building subDirCache with subdirectory '%s'",
name.c_str());
}
} }
cacheDirectoryRecursive(*it, depth - 1, _flat ? prefix : lowercaseName + "/"); cacheDirectoryRecursive(*it, depth - 1, _flat ? prefix : lowercaseName + "/");
_subDirCache[lowercaseName] = *it; _subDirCache[lowercaseName] = *it;
} }
} else { } else {
if (_fileCache.contains(lowercaseName)) { if (_fileCache.contains(lowercaseName)) {
warning("FSDirectory::cacheDirectory: name clash when building cache, ignoring file '%s'", name.c_str()); if (!_ignoreClashes) {
warning("FSDirectory::cacheDirectory: name clash when building cache, ignoring file '%s'",
name.c_str());
}
} else { } else {
_fileCache[lowercaseName] = *it; _fileCache[lowercaseName] = *it;
} }

View file

@ -267,6 +267,12 @@ public:
* are cached without the relative path, so in the example above * are cached without the relative path, so in the example above
* c:\my\data\file.ext would be cached as file.ext. * c:\my\data\file.ext would be cached as file.ext.
* *
* When the 'ignoreClashes' argument to the constructor is true, name clashes are
* expected by the engine. It means that files which clash should be identical and
* getSubDirectory shouldn't be used on clashing directories. This flag is useful
* in flat mode when there are directories with same name at different places in the
* tree whose name isn't relevant for the engine code.
*
* Client code can customize cache by using the constructors with the 'prefix' * Client code can customize cache by using the constructors with the 'prefix'
* parameter. In this case, the prefix is prepended to each entry in the cache, * parameter. In this case, the prefix is prepended to each entry in the cache,
* and effectively treated as a 'virtual' parent subdirectory. FSDirectory adds * and effectively treated as a 'virtual' parent subdirectory. FSDirectory adds
@ -277,6 +283,9 @@ public:
*/ */
class FSDirectory : public Archive { class FSDirectory : public Archive {
FSNode _node; FSNode _node;
int _depth;
bool _flat;
bool _ignoreClashes;
String _prefix; // string that is prepended to each cache item key String _prefix; // string that is prepended to each cache item key
void setPrefix(const String &prefix); void setPrefix(const String &prefix);
@ -286,8 +295,6 @@ class FSDirectory : public Archive {
typedef HashMap<String, FSNode, IgnoreCase_Hash, IgnoreCase_EqualTo> NodeCache; typedef HashMap<String, FSNode, IgnoreCase_Hash, IgnoreCase_EqualTo> NodeCache;
mutable NodeCache _fileCache, _subDirCache; mutable NodeCache _fileCache, _subDirCache;
mutable bool _cached; mutable bool _cached;
mutable int _depth;
mutable bool _flat;
// look for a match // look for a match
FSNode *lookupCache(NodeCache &cache, const String &name) const; FSNode *lookupCache(NodeCache &cache, const String &name) const;
@ -304,17 +311,19 @@ public:
* unbound FSDirectory if name is not found on the filesystem or if the node is not a * unbound FSDirectory if name is not found on the filesystem or if the node is not a
* valid directory. * valid directory.
*/ */
FSDirectory(const String &name, int depth = 1, bool flat = false); FSDirectory(const String &name, int depth = 1, bool flat = false,
FSDirectory(const FSNode &node, int depth = 1, bool flat = false); bool ignoreClashes = false);
FSDirectory(const FSNode &node, int depth = 1, bool flat = false,
bool ignoreClashes = false);
/** /**
* Create a FSDirectory representing a tree with the specified depth. The parameter * Create a FSDirectory representing a tree with the specified depth. The parameter
* prefix is prepended to the keys in the cache. See class comment. * prefix is prepended to the keys in the cache. See class comment.
*/ */
FSDirectory(const String &prefix, const String &name, int depth = 1, FSDirectory(const String &prefix, const String &name, int depth = 1,
bool flat = false); bool flat = false, bool ignoreClashes = false);
FSDirectory(const String &prefix, const FSNode &node, int depth = 1, FSDirectory(const String &prefix, const FSNode &node, int depth = 1,
bool flat = false); bool flat = false, bool ignoreClashes = false);
virtual ~FSDirectory(); virtual ~FSDirectory();
@ -328,8 +337,10 @@ public:
* for an explanation of the prefix parameter. * for an explanation of the prefix parameter.
* @return a new FSDirectory instance * @return a new FSDirectory instance
*/ */
FSDirectory *getSubDirectory(const String &name, int depth = 1, bool flat = false); FSDirectory *getSubDirectory(const String &name, int depth = 1, bool flat = false,
FSDirectory *getSubDirectory(const String &prefix, const String &name, int depth = 1, bool flat = false); bool ignoreClashes = false);
FSDirectory *getSubDirectory(const String &prefix, const String &name, int depth = 1,
bool flat = false, bool ignoreClashes = false);
/** /**
* Checks for existence in the cache. A full match of relative path and filename is needed * Checks for existence in the cache. A full match of relative path and filename is needed