summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorisanae <14251494+isanae@users.noreply.github.com>2019-12-19 23:00:18 -0500
committerisanae <14251494+isanae@users.noreply.github.com>2020-02-04 03:33:19 -0500
commit2be531470d54fa56307e392ad5bdfbc02048a744 (patch)
tree4ab9e436d40b1650fdce9378bdef83b970cd2136 /src
parent0a908c49625fe0e54bc45e29fe8c4908d20b0dbe (diff)
renamed ToLower() to avoid confusion with in-place vs copy
pre-hashed file lookup in DirectoryEntry
Diffstat (limited to 'src')
-rw-r--r--src/filetree.cpp5
-rw-r--r--src/filetree.h1
-rw-r--r--src/filetreeitem.cpp26
-rw-r--r--src/filetreeitem.h31
-rw-r--r--src/filetreemodel.cpp40
-rw-r--r--src/filetreemodel.h13
-rw-r--r--src/organizercore.cpp10
-rw-r--r--src/organizercore.h12
-rw-r--r--src/shared/directoryentry.cpp125
-rw-r--r--src/shared/directoryentry.h88
-rw-r--r--src/shared/util.cpp12
-rw-r--r--src/shared/util.h8
12 files changed, 258 insertions, 113 deletions
diff --git a/src/filetree.cpp b/src/filetree.cpp
index 8fc9e908..71a49200 100644
--- a/src/filetree.cpp
+++ b/src/filetree.cpp
@@ -144,6 +144,11 @@ void FileTree::refresh()
m_model->refresh();
}
+void FileTree::clear()
+{
+ m_model->clear();
+}
+
FileTreeItem* FileTree::singleSelection()
{
const auto sel = m_tree->selectionModel()->selectedRows();
diff --git a/src/filetree.h b/src/filetree.h
index 77d5012c..1a17354f 100644
--- a/src/filetree.h
+++ b/src/filetree.h
@@ -20,6 +20,7 @@ public:
FileTreeModel* model();
void refresh();
+ void clear();
void open();
void openHooked();
diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp
index 0469dfcc..39cdd9c4 100644
--- a/src/filetreeitem.cpp
+++ b/src/filetreeitem.cpp
@@ -6,24 +6,22 @@
using namespace MOBase;
using namespace MOShared;
-FileTreeItem::FileTreeItem()
- : m_flags(NoFlags), m_loaded(false)
-{
-}
-
FileTreeItem::FileTreeItem(
FileTreeItem* parent, int originID,
std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags,
std::wstring file, std::wstring mod) :
- m_parent(parent), m_originID(originID),
- m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)),
- m_realPath(QString::fromStdWString(realPath)),
- m_flags(flags),
- m_wsFile(file), m_wsLcFile(ToLower(file)),
- m_file(QString::fromStdWString(file)),
- m_mod(QString::fromStdWString(mod)),
- m_loaded(false),
- m_expanded(false)
+ m_parent(parent),
+ m_originID(originID),
+ m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)),
+ m_realPath(QString::fromStdWString(realPath)),
+ m_flags(flags),
+ m_wsFile(file),
+ m_wsLcFile(ToLowerCopy(file)),
+ m_key(m_wsLcFile),
+ m_file(QString::fromStdWString(file)),
+ m_mod(QString::fromStdWString(mod)),
+ m_loaded(false),
+ m_expanded(false)
{
}
diff --git a/src/filetreeitem.h b/src/filetreeitem.h
index 1e820f3f..fb9eccce 100644
--- a/src/filetreeitem.h
+++ b/src/filetreeitem.h
@@ -1,6 +1,7 @@
#ifndef MODORGANIZER_FILETREEITEM_INCLUDED
#define MODORGANIZER_FILETREEITEM_INCLUDED
+#include "directoryentry.h"
#include <QFileIconProvider>
class FileTreeItem
@@ -16,7 +17,6 @@ public:
Q_DECLARE_FLAGS(Flags, Flag);
- FileTreeItem();
FileTreeItem(
FileTreeItem* parent, int originID,
std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags,
@@ -35,6 +35,12 @@ public:
void insert(std::unique_ptr<FileTreeItem> child, std::size_t at);
void remove(std::size_t i);
+ void clear()
+ {
+ m_children.clear();
+ m_loaded = false;
+ }
+
const std::vector<std::unique_ptr<FileTreeItem>>& children() const
{
return m_children;
@@ -57,6 +63,7 @@ public:
}
QString virtualPath() const;
+
const QString& filename() const
{
return m_file;
@@ -72,6 +79,11 @@ public:
return m_wsLcFile;
}
+ const MOShared::DirectoryEntry::FileKey& key() const
+ {
+ return m_key;
+ }
+
const QString& mod() const
{
return m_mod;
@@ -152,13 +164,16 @@ public:
private:
FileTreeItem* m_parent;
- int m_originID;
- QString m_virtualParentPath;
- QString m_realPath;
- Flags m_flags;
- std::wstring m_wsFile, m_wsLcFile;
- QString m_file;
- QString m_mod;
+
+ const int m_originID;
+ const QString m_virtualParentPath;
+ const QString m_realPath;
+ const Flags m_flags;
+ const std::wstring m_wsFile, m_wsLcFile;
+ const MOShared::DirectoryEntry::FileKey m_key;
+ const QString m_file;
+ const QString m_mod;
+
bool m_loaded;
bool m_expanded;
std::vector<std::unique_ptr<FileTreeItem>> m_children;
diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp
index 9332b354..ed62b8ae 100644
--- a/src/filetreemodel.cpp
+++ b/src/filetreemodel.cpp
@@ -17,9 +17,13 @@ void trace(F&&)
}
-FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent)
- : QAbstractItemModel(parent), m_core(core), m_flags(NoFlags)
+FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) :
+ QAbstractItemModel(parent), m_core(core),
+ m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L"<root>"),
+ m_flags(NoFlags)
{
+ m_root.setExpanded(true);
+
connect(&m_iconPendingTimer, &QTimer::timeout, [&]{ updatePendingIcons(); });
connect(
@@ -33,21 +37,6 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent)
});
}
-void FileTreeModel::setFlags(Flags f)
-{
- m_flags = f;
-}
-
-bool FileTreeModel::showConflicts() const
-{
- return (m_flags & Conflicts);
-}
-
-bool FileTreeModel::showArchives() const
-{
- return (m_flags & Archives) && m_core.getArchiveParsing();
-}
-
void FileTreeModel::refresh()
{
if (m_root.hasChildren()) {
@@ -56,13 +45,24 @@ void FileTreeModel::refresh()
} else {
TimeThis tt("FileTreeModel::fill()");
beginResetModel();
- m_root = {nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L"<root>"};
- m_root.setExpanded(true);
+ m_root.clear();
fill(m_root, *m_core.directoryStructure(), L"");
endResetModel();
}
}
+void FileTreeModel::clear()
+{
+ beginResetModel();
+ m_root.clear();
+ endResetModel();
+}
+
+bool FileTreeModel::showArchives() const
+{
+ return (m_flags & Archives) && m_core.getArchiveParsing();
+}
+
void FileTreeModel::ensureLoaded(FileTreeItem* item) const
{
if (!item) {
@@ -426,7 +426,7 @@ void FileTreeModel::updateFiles(
continue;
}
- if (auto f=parentEntry.findFile(item->filenameWsLowerCase(), true)) {
+ if (auto f=parentEntry.findFile(item->key())) {
if (shouldShowFile(*f)) {
// file still exists
trace([&]{ log::debug("{} still exists", item->debugName()); });
diff --git a/src/filetreemodel.h b/src/filetreemodel.h
index 0cfe19c7..8a1738b3 100644
--- a/src/filetreemodel.h
+++ b/src/filetreemodel.h
@@ -23,8 +23,13 @@ public:
FileTreeModel(OrganizerCore& core, QObject* parent=nullptr);
- void setFlags(Flags f);
+ void setFlags(Flags f)
+ {
+ m_flags = f;
+ }
+
void refresh();
+ void clear();
QModelIndex index(int row, int col, const QModelIndex& parent={}) const override;
QModelIndex parent(const QModelIndex& index) const override;
@@ -53,7 +58,11 @@ private:
mutable std::vector<QModelIndex> m_iconPending;
mutable QTimer m_iconPendingTimer;
- bool showConflicts() const;
+ bool showConflicts() const
+ {
+ return (m_flags & Conflicts);
+ }
+
bool showArchives() const;
void fill(
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 336be37d..c4f9e081 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -500,16 +500,6 @@ std::wstring OrganizerCore::crashDumpsPath() {
).toStdWString();
}
-bool OrganizerCore::getArchiveParsing() const
-{
- return m_ArchiveParsing;
-}
-
-void OrganizerCore::setArchiveParsing(const bool archiveParsing)
-{
- m_ArchiveParsing = archiveParsing;
-}
-
void OrganizerCore::setCurrentProfile(const QString &profileName)
{
if ((m_CurrentProfile != nullptr)
diff --git a/src/organizercore.h b/src/organizercore.h
index 6c9edb9f..4ee6ddc5 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -218,8 +218,16 @@ public:
bool onFinishedRun(const std::function<void (const QString &, unsigned int)> &func);
void refreshModList(bool saveChanges = true);
QStringList modsSortedByProfilePriority() const;
- bool getArchiveParsing() const;
- void setArchiveParsing(bool archiveParsing);
+
+ bool getArchiveParsing() const
+ {
+ return m_ArchiveParsing;
+ }
+
+ void setArchiveParsing(bool archiveParsing)
+ {
+ m_ArchiveParsing = archiveParsing;
+ }
public: // IPluginDiagnose interface
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index 97da1061..639d6cac 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -482,6 +482,8 @@ const std::wstring &DirectoryEntry::getName() const
void DirectoryEntry::clear()
{
m_Files.clear();
+ m_FilesLookup.clear();
+
for (DirectoryEntry *entry : m_SubDirectories) {
delete entry;
}
@@ -663,6 +665,8 @@ void DirectoryEntry::removeDirRecursive()
m_FileRegister->removeFile(m_Files.begin()->second);
}
+ m_FilesLookup.clear();
+
for (DirectoryEntry *entry : m_SubDirectories) {
entry->removeDirRecursive();
delete entry;
@@ -709,6 +713,56 @@ void DirectoryEntry::removeDir(const std::wstring &path)
}
}
+bool DirectoryEntry::remove(const std::wstring &fileName, int *origin)
+{
+ const auto lcFileName = ToLowerCopy(fileName);
+
+ auto iter = m_Files.find(lcFileName);
+ bool b = false;
+
+ if (iter != m_Files.end()) {
+ if (origin != nullptr) {
+ FileEntry::Ptr entry = m_FileRegister->getFile(iter->second);
+ if (entry.get() != nullptr) {
+ bool ignore;
+ *origin = entry->getOrigin(ignore);
+ }
+ }
+
+ b = m_FileRegister->removeFile(iter->second);
+ }
+
+ if (m_Files.size() != m_FilesLookup.size()) {
+ DebugBreak();
+ }
+
+ return b;
+}
+
+void DirectoryEntry::insert(
+ const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime,
+ const std::wstring &archive, int order)
+{
+ std::wstring fileNameLower = ToLowerCopy(fileName);
+ auto iter = m_Files.find(fileNameLower);
+ FileEntry::Ptr file;
+
+ if (iter != m_Files.end()) {
+ file = m_FileRegister->getFile(iter->second);
+ } else {
+ file = m_FileRegister->createFile(fileName, this);
+ m_Files.emplace(fileNameLower, file->getIndex());
+ m_FilesLookup.emplace(fileNameLower, file->getIndex());
+ }
+
+ if (m_Files.size() != m_FilesLookup.size()) {
+ DebugBreak();
+ }
+
+ file->addOrigin(origin.getID(), fileTime, archive, order);
+ origin.addFile(file->getIndex());
+}
+
bool DirectoryEntry::hasContentsFromOrigin(int originID) const
{
return m_Origins.find(originID) != m_Origins.end();
@@ -726,13 +780,34 @@ void DirectoryEntry::insertFile(const std::wstring &filePath, FilesOrigin &origi
}
}
-
void DirectoryEntry::removeFile(FileEntry::Index index)
{
+ if (!m_FilesLookup.empty()) {
+ auto iter = std::find_if(
+ m_FilesLookup.begin(), m_FilesLookup.end(),
+ [&index](auto&& pair) { return (pair.second == index); }
+ );
+
+ if (iter != m_FilesLookup.end()) {
+ m_FilesLookup.erase(iter);
+ } else {
+ log::error(
+ "file \"{}\" not in directory for lookup \"{}\"",
+ m_FileRegister->getFile(index)->getName(), this->getName());
+ }
+ } else {
+ log::error(
+ "file \"{}\" not in directory \"{}\" for lookup, directory empty",
+ m_FileRegister->getFile(index)->getName(), this->getName());
+ }
+
if (!m_Files.empty()) {
- auto iter = std::find_if(m_Files.begin(), m_Files.end(),
- [&index](const std::pair<std::wstring, FileEntry::Index> &iter) -> bool {
- return iter.second == index; } );
+ auto iter = std::find_if(
+ m_Files.begin(), m_Files.end(),
+ [&index](const std::pair<std::wstring, FileEntry::Index> &iter) -> bool {
+ return iter.second == index; }
+ );
+
if (iter != m_Files.end()) {
m_Files.erase(iter);
} else {
@@ -745,14 +820,25 @@ void DirectoryEntry::removeFile(FileEntry::Index index)
QObject::tr("file \"{}\" not in directory \"{}\", directory empty").toStdString(),
m_FileRegister->getFile(index)->getName(), this->getName());
}
-}
+ if (m_Files.size() != m_FilesLookup.size()) {
+ DebugBreak();
+ }
+}
void DirectoryEntry::removeFiles(const std::set<FileEntry::Index> &indices)
{
for (auto iter = m_Files.begin(); iter != m_Files.end();) {
if (indices.find(iter->second) != indices.end()) {
- m_Files.erase(iter++);
+ iter = m_Files.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+
+ for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) {
+ if (indices.find(iter->second) != indices.end()) {
+ iter = m_FilesLookup.erase(iter);
} else {
++iter;
}
@@ -851,7 +937,7 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const
if (len == std::string::npos) {
// no more path components
- auto iter = m_Files.find(ToLower(path));
+ auto iter = m_Files.find(ToLowerCopy(path));
if (iter != m_Files.end()) {
return m_FileRegister->getFile(iter->second);
} else if (directory != nullptr) {
@@ -884,7 +970,7 @@ DirectoryEntry *DirectoryEntry::findSubDirectory(
if (alreadyLowerCase) {
itor = m_SubDirectoriesMap.find(name);
} else {
- itor = m_SubDirectoriesMap.find(ToLower(name));
+ itor = m_SubDirectoriesMap.find(ToLowerCopy(name));
}
if (itor == m_SubDirectoriesMap.end()) {
@@ -904,15 +990,26 @@ DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &pa
const FileEntry::Ptr DirectoryEntry::findFile(
const std::wstring &name, bool alreadyLowerCase) const
{
- std::map<std::wstring, FileEntry::Index>::const_iterator iter;
+ FilesLookup::const_iterator iter;
if (alreadyLowerCase) {
- iter = m_Files.find(name);
+ iter = m_FilesLookup.find(FileKey(name));
} else {
- iter = m_Files.find(ToLower(name));
+ iter = m_FilesLookup.find(FileKey(ToLowerCopy(name)));
}
- if (iter != m_Files.end()) {
+ if (iter != m_FilesLookup.end()) {
+ return m_FileRegister->getFile(iter->second);
+ } else {
+ return FileEntry::Ptr();
+ }
+}
+
+const FileEntry::Ptr DirectoryEntry::findFile(const FileKey& key) const
+{
+ auto iter = m_FilesLookup.find(key);
+
+ if (iter != m_FilesLookup.end()) {
return m_FileRegister->getFile(iter->second);
} else {
return FileEntry::Ptr();
@@ -921,7 +1018,7 @@ const FileEntry::Ptr DirectoryEntry::findFile(
bool DirectoryEntry::hasFile(const std::wstring& name) const
{
- return m_Files.contains(ToLower(name));
+ return m_Files.contains(ToLowerCopy(name));
}
DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool create, int originID)
@@ -936,7 +1033,7 @@ DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool c
name, this, originID, m_FileRegister, m_OriginConnection);
m_SubDirectories.push_back(entry);
- m_SubDirectoriesMap.emplace(ToLower(name), entry);
+ m_SubDirectoriesMap.emplace(ToLowerCopy(name), entry);
return entry;
} else {
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index 79bc5cf2..fc68cae7 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -35,6 +35,20 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#endif
#include "util.h"
+namespace MOShared { struct DirectoryEntryFileKey; }
+
+namespace std
+{
+ template <>
+ struct hash<MOShared::DirectoryEntryFileKey>
+ {
+ using argument_type = MOShared::DirectoryEntryFileKey;
+ using result_type = std::size_t;
+
+ inline result_type operator()(const argument_type& key) const;
+ };
+}
+
namespace MOShared {
@@ -203,9 +217,32 @@ private:
};
+struct DirectoryEntryFileKey
+{
+ DirectoryEntryFileKey(std::wstring v)
+ : value(std::move(v)), hash(getHash(value))
+ {
+ }
+
+ bool operator==(const DirectoryEntryFileKey& o) const
+ {
+ return (value == o.value);
+ }
+
+ static std::size_t getHash(const std::wstring& value)
+ {
+ return std::hash<std::wstring>()(value);
+ }
+
+ const std::wstring value;
+ const std::size_t hash;
+};
+
+
class DirectoryEntry
{
public:
+ using FileKey = DirectoryEntryFileKey;
DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID);
@@ -294,6 +331,7 @@ public:
* @return fileentry object for the file or nullptr if no file matches
*/
const FileEntry::Ptr findFile(const std::wstring &name, bool alreadyLowerCase=false) const;
+ const FileEntry::Ptr findFile(const FileKey& key) const;
bool hasFile(const std::wstring& name) const;
bool containsArchive(std::wstring archiveName);
@@ -315,21 +353,7 @@ public:
*/
void removeDir(const std::wstring &path);
- bool remove(const std::wstring &fileName, int *origin) {
- auto iter = m_Files.find(ToLower(fileName));
- if (iter != m_Files.end()) {
- if (origin != nullptr) {
- FileEntry::Ptr entry = m_FileRegister->getFile(iter->second);
- if (entry.get() != nullptr) {
- bool ignore;
- *origin = entry->getOrigin(ignore);
- }
- }
- return m_FileRegister->removeFile(iter->second);
- } else {
- return false;
- }
- }
+ bool remove(const std::wstring &fileName, int *origin);
bool hasContentsFromOrigin(int originID) const;
@@ -342,20 +366,9 @@ private:
DirectoryEntry(const DirectoryEntry &reference);
DirectoryEntry &operator=(const DirectoryEntry &reference);
- void insert(const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order) {
- std::wstring fileNameLower = ToLower(fileName);
- auto iter = m_Files.find(fileNameLower);
- FileEntry::Ptr file;
- if (iter != m_Files.end()) {
- file = m_FileRegister->getFile(iter->second);
- } else {
- file = m_FileRegister->createFile(fileName, this);
- // TODO this has been observed to cause a crash, no clue why
- m_Files[fileNameLower] = file->getIndex();
- }
- file->addOrigin(origin.getID(), fileTime, archive, order);
- origin.addFile(file->getIndex());
- }
+ void insert(
+ const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime,
+ const std::wstring &archive, int order);
void addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset);
void addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName, int order);
@@ -367,13 +380,16 @@ private:
void removeDirRecursive();
private:
+ using FilesMap = std::map<std::wstring, FileEntry::Index>;
+ using FilesLookup = std::unordered_map<FileKey, FileEntry::Index>;
using SubDirectoriesMap = std::unordered_map<std::wstring, DirectoryEntry*>;
boost::shared_ptr<FileRegister> m_FileRegister;
boost::shared_ptr<OriginConnection> m_OriginConnection;
std::wstring m_Name;
- std::map<std::wstring, FileEntry::Index> m_Files;
+ FilesMap m_Files;
+ FilesLookup m_FilesLookup;
std::vector<DirectoryEntry*> m_SubDirectories;
SubDirectoriesMap m_SubDirectoriesMap;
@@ -386,7 +402,17 @@ private:
};
-
} // namespace MOShared
+
+namespace std
+{
+ hash<MOShared::DirectoryEntryFileKey>::result_type
+ hash<MOShared::DirectoryEntryFileKey>::operator()(
+ const argument_type& key) const
+ {
+ return key.hash;
+ }
+}
+
#endif // DIRECTORYENTRY_H
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 4ac95465..009aad70 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -102,32 +102,28 @@ static auto locToLower = [] (char in) -> char {
return std::tolower(in, loc);
};
-std::string &ToLower(std::string &text)
+std::string& ToLowerInPlace(std::string& text)
{
- //std::transform(text.begin(), text.end(), text.begin(), locToLower);
CharLowerBuffA(const_cast<CHAR *>(text.c_str()), static_cast<DWORD>(text.size()));
return text;
}
-std::string ToLower(const std::string &text)
+std::string ToLowerCopy(const std::string& text)
{
std::string result(text);
- //std::transform(result.begin(), result.end(), result.begin(), locToLower);
CharLowerBuffA(const_cast<CHAR *>(result.c_str()), static_cast<DWORD>(result.size()));
return result;
}
-std::wstring &ToLower(std::wstring &text)
+std::wstring& ToLowerInPlace(std::wstring& text)
{
- //std::transform(text.begin(), text.end(), text.begin(), locToLowerW);
CharLowerBuffW(const_cast<WCHAR *>(text.c_str()), static_cast<DWORD>(text.size()));
return text;
}
-std::wstring ToLower(const std::wstring &text)
+std::wstring ToLowerCopy(const std::wstring& text)
{
std::wstring result(text);
- //std::transform(result.begin(), result.end(), result.begin(), locToLowerW);
CharLowerBuffW(const_cast<WCHAR *>(result.c_str()), static_cast<DWORD>(result.size()));
return result;
}
diff --git a/src/shared/util.h b/src/shared/util.h
index 788d2444..79cadf71 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -36,11 +36,11 @@ bool FileExists(const std::wstring &searchPath, const std::wstring &filename);
std::string ToString(const std::wstring &source, bool utf8);
std::wstring ToWString(const std::string &source, bool utf8);
-std::string &ToLower(std::string &text);
-std::string ToLower(const std::string &text);
+std::string& ToLowerInPlace(std::string& text);
+std::string ToLowerCopy(const std::string& text);
-std::wstring &ToLower(std::wstring &text);
-std::wstring ToLower(const std::wstring &text);
+std::wstring& ToLowerInPlace(std::wstring& text);
+std::wstring ToLowerCopy(const std::wstring& text);
bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs);