summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
authorMikaƫl Capelle <capelle.mikael@gmail.com>2023-07-09 17:36:03 +0200
committerGitHub <noreply@github.com>2023-07-09 17:36:03 +0200
commitef94aee28464039672b277243a0181ae93550d6c (patch)
tree0575a68af5f154e9d6d0738edbaefb16a11d6f5c /src/shared
parent7d6cb8528d20e36a4cee822263865ee2f7f32481 (diff)
parent3de050e9c03e553f7ae3f780f6bd080a30ae123e (diff)
Merge pull request #1839 from Holt59/ci/initial-gh-action-for-build
Clang-Format + Github Workflow
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/appconfig.cpp72
-rw-r--r--src/shared/appconfig.h81
-rw-r--r--src/shared/directoryentry.cpp1938
-rw-r--r--src/shared/directoryentry.h605
-rw-r--r--src/shared/fileentry.cpp104
-rw-r--r--src/shared/fileentry.h75
-rw-r--r--src/shared/fileregister.cpp30
-rw-r--r--src/shared/fileregister.h12
-rw-r--r--src/shared/fileregisterfwd.h45
-rw-r--r--src/shared/filesorigin.cpp32
-rw-r--r--src/shared/filesorigin.h41
-rw-r--r--src/shared/originconnection.cpp62
-rw-r--r--src/shared/originconnection.h40
-rw-r--r--src/shared/util.cpp873
-rw-r--r--src/shared/util.h178
-rw-r--r--src/shared/windows_error.cpp101
-rw-r--r--src/shared/windows_error.h91
17 files changed, 2131 insertions, 2249 deletions
diff --git a/src/shared/appconfig.cpp b/src/shared/appconfig.cpp
index 49edcbcc..221732de 100644
--- a/src/shared/appconfig.cpp
+++ b/src/shared/appconfig.cpp
@@ -1,33 +1,39 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "appconfig.h"
-
-namespace AppConfig {
-
-#define PARWSTRING wstring
-#define APPPARAM(partype, parid, value) partype parid () { return value; }
-#include "appconfig.inc"
-
-namespace MOShared {
-#undef PARWSTRING
-#undef APPPARAM
-
-}
-} // namespace MOShared
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "appconfig.h"
+
+namespace AppConfig
+{
+
+#define PARWSTRING wstring
+#define APPPARAM(partype, parid, value) \
+ partype parid() \
+ { \
+ return value; \
+ }
+#include "appconfig.inc"
+
+namespace MOShared
+{
+#undef PARWSTRING
+#undef APPPARAM
+
+} // namespace MOShared
+} // namespace AppConfig
diff --git a/src/shared/appconfig.h b/src/shared/appconfig.h
index b32043e5..598ee19c 100644
--- a/src/shared/appconfig.h
+++ b/src/shared/appconfig.h
@@ -1,40 +1,41 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef APPCONFIG_H
-#define APPCONFIG_H
-
-#include <string>
-
-namespace AppConfig {
-
-
-#define PARWSTRING wstring
-#define APPPARAM(partype, parid, value) partype parid ();
-#include "appconfig.inc"
-
-namespace MOShared {
-#undef PARWSTRING
-#undef APPPARAM
-
-}
-
-} // namespace MOShared
-
-#endif // APPCONFIG_H
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef APPCONFIG_H
+#define APPCONFIG_H
+
+#include <string>
+
+namespace AppConfig
+{
+
+#define PARWSTRING wstring
+#define APPPARAM(partype, parid, value) partype parid();
+#include "appconfig.inc"
+
+namespace MOShared
+{
+#undef PARWSTRING
+#undef APPPARAM
+
+} // namespace MOShared
+
+} // namespace AppConfig
+
+#endif // APPCONFIG_H
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index badb5636..84da66e2 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -1,978 +1,960 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "directoryentry.h"
-#include "originconnection.h"
-#include "filesorigin.h"
-#include "fileentry.h"
-#include "../envfs.h"
-#include "util.h"
-#include "windows_error.h"
-#include <log.h>
-#include <utility.h>
-
-namespace MOShared
-{
-
-using namespace MOBase;
-const int MAXPATH_UNICODE = 32767;
-
-template <class F>
-void elapsedImpl(std::chrono::nanoseconds& out, F&& f)
-{
- if constexpr (DirectoryStats::EnableInstrumentation) {
- const auto start = std::chrono::high_resolution_clock::now();
- f();
- const auto end = std::chrono::high_resolution_clock::now();
- out += (end - start);
- } else {
- f();
- }
-}
-
-// elapsed() is not optimized out when EnableInstrumentation is false even
-// though it's equivalent that this macro
-#define elapsed(OUT, F) (F)();
-//#define elapsed(OUT, F) elapsedImpl(OUT, F);
-
-static bool SupportOptimizedFind()
-{
- // large fetch and basic info for FindFirstFileEx is supported on win server 2008 r2, win 7 and newer
-
- OSVERSIONINFOEX versionInfo;
- versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
- versionInfo.dwMajorVersion = 6;
- versionInfo.dwMinorVersion = 1;
-
- ULONGLONG mask = ::VerSetConditionMask(
- ::VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL),
- VER_MINORVERSION, VER_GREATER_EQUAL);
-
- return (::VerifyVersionInfo(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, mask) == TRUE);
-}
-
-bool DirCompareByName::operator()(
- const DirectoryEntry* lhs, const DirectoryEntry* rhs) const
-{
- return _wcsicmp(lhs->getName().c_str(), rhs->getName().c_str()) < 0;
-}
-
-
-DirectoryEntry::DirectoryEntry(
- std::wstring name, DirectoryEntry* parent, int originID) :
- m_OriginConnection(new OriginConnection),
- m_Name(std::move(name)), m_Parent(parent), m_Populated(false), m_TopLevel(true)
-{
- m_FileRegister.reset(new FileRegister(m_OriginConnection));
- m_Origins.insert(originID);
-}
-
-DirectoryEntry::DirectoryEntry(
- std::wstring name, DirectoryEntry* parent, int originID,
- boost::shared_ptr<FileRegister> fileRegister,
- boost::shared_ptr<OriginConnection> originConnection) :
- m_FileRegister(fileRegister), m_OriginConnection(originConnection),
- m_Name(std::move(name)), m_Parent(parent), m_Populated(false), m_TopLevel(false)
-{
- m_Origins.insert(originID);
-}
-
-DirectoryEntry::~DirectoryEntry()
-{
- clear();
-}
-
-void DirectoryEntry::clear()
-{
- for (auto itor=m_SubDirectories.rbegin(); itor!=m_SubDirectories.rend(); ++itor) {
- delete *itor;
- }
-
- m_Files.clear();
- m_FilesLookup.clear();
- m_SubDirectories.clear();
- m_SubDirectoriesLookup.clear();
-}
-
-void DirectoryEntry::addFromOrigin(
- const std::wstring &originName, const std::wstring &directory, int priority,
- DirectoryStats& stats)
-{
- env::DirectoryWalker walker;
- addFromOrigin(walker, originName, directory, priority, stats);
-}
-
-void DirectoryEntry::addFromOrigin(
- env::DirectoryWalker& walker, const std::wstring &originName,
- const std::wstring &directory, int priority, DirectoryStats& stats)
-{
- FilesOrigin &origin = createOrigin(originName, directory, priority, stats);
-
- if (!directory.empty()) {
- addFiles(walker, origin, directory, stats);
- }
-
- m_Populated = true;
-}
-
-void DirectoryEntry::addFromList(
- const std::wstring &originName, const std::wstring &directory,
- env::Directory& root, int priority, DirectoryStats& stats)
-{
- stats = {};
-
- FilesOrigin &origin = createOrigin(originName, directory, priority, stats);
- addDir(origin, root, stats);
-}
-
-void DirectoryEntry::addDir(
- FilesOrigin& origin, env::Directory& d, DirectoryStats& stats)
-{
- elapsed(stats.dirTimes, [&]{
- for (auto& sd : d.dirs) {
- auto* sdirEntry = getSubDirectory(sd, true, stats, origin.getID());
- sdirEntry->addDir(origin, sd, stats);
- }
- });
-
- elapsed(stats.fileTimes, [&]{
- for (auto& f : d.files) {
- insert(f, origin, L"", -1, stats);
- }
- });
-
- m_Populated = true;
-}
-
-void DirectoryEntry::addFromAllBSAs(
- const std::wstring& originName, const std::wstring& directory,
- int priority, const std::vector<std::wstring>& archives,
- const std::set<std::wstring>& enabledArchives,
- const std::vector<std::wstring>& loadOrder,
- DirectoryStats& stats)
-{
- for (const auto& archive : archives) {
- const std::filesystem::path archivePath(archive);
- const auto filename = archivePath.filename().native();
-
- if (!enabledArchives.contains(filename)) {
- continue;
- }
-
- const auto filenameLc = ToLowerCopy(filename);
-
- int order = -1;
-
- for (auto plugin : loadOrder)
- {
- const auto pluginNameLc =
- ToLowerCopy(std::filesystem::path(plugin).stem().native());
-
- if (filenameLc.starts_with(pluginNameLc + L" - ") ||
- filenameLc.starts_with(pluginNameLc + L".")) {
- auto itor = std::find(loadOrder.begin(), loadOrder.end(), plugin);
- if (itor != loadOrder.end()) {
- order = std::distance(loadOrder.begin(), itor);
- }
- }
- }
-
- addFromBSA(
- originName, directory, archivePath.native(),
- priority, order, stats);
- }
-}
-
-void DirectoryEntry::addFromBSA(
- const std::wstring& originName, const std::wstring& directory,
- const std::wstring& archivePath, int priority, int order, DirectoryStats& stats)
-{
- FilesOrigin& origin = createOrigin(originName, directory, priority, stats);
- const auto archiveName = std::filesystem::path(archivePath).filename().native();
-
- if (containsArchive(archiveName)) {
- return;
- }
-
- BSA::Archive archive;
- BSA::EErrorCode res = BSA::ERROR_NONE;
-
- try
- {
- // read() can return an error, but it can also throw if the file is not a
- // valid bsa
- res = archive.read(ToString(archivePath, false).c_str(), false);
- }
- catch(std::exception& e)
- {
- log::error("invalid bsa '{}', error {}", archivePath, e.what());
- return;
- }
-
- if ((res != BSA::ERROR_NONE) && (res != BSA::ERROR_INVALIDHASHES)) {
- log::error("invalid bsa '{}', error {}", archivePath, res);
- return;
- }
-
- std::error_code ec;
- const auto lwt = std::filesystem::last_write_time(archivePath, ec);
- FILETIME ft = {};
-
- if (ec) {
- log::warn(
- "failed to get last modified date for '{}', {}",
- archivePath, ec.message());
- } else {
- ft = ToFILETIME(lwt);
- }
-
- addFiles(origin, archive.getRoot(), ft, archiveName, order, stats);
-
- m_Populated = true;
-}
-
-void DirectoryEntry::propagateOrigin(int origin)
-{
- {
- std::scoped_lock lock(m_OriginsMutex);
- m_Origins.insert(origin);
- }
-
- if (m_Parent != nullptr) {
- m_Parent->propagateOrigin(origin);
- }
-}
-
-bool DirectoryEntry::originExists(const std::wstring &name) const
-{
- return m_OriginConnection->exists(name);
-}
-
-FilesOrigin &DirectoryEntry::getOriginByID(int ID) const
-{
- return m_OriginConnection->getByID(ID);
-}
-
-FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const
-{
- return m_OriginConnection->getByName(name);
-}
-
-const FilesOrigin* DirectoryEntry::findOriginByID(int ID) const
-{
- return m_OriginConnection->findByID(ID);
-}
-
-int DirectoryEntry::anyOrigin() const
-{
- bool ignore;
-
- for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
- FileEntryPtr entry = m_FileRegister->getFile(iter->second);
- if ((entry.get() != nullptr) && !entry->isFromArchive()) {
- return entry->getOrigin(ignore);
- }
- }
-
- // if we got here, no file directly within this directory is a valid indicator for a mod, thus
- // we continue looking in subdirectories
- for (DirectoryEntry* entry : m_SubDirectories) {
- int res = entry->anyOrigin();
- if (res != InvalidOriginID){
- return res;
- }
- }
-
- return *(m_Origins.begin());
-}
-
-std::vector<FileEntryPtr> DirectoryEntry::getFiles() const
-{
- std::vector<FileEntryPtr> result;
-
- for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
- result.push_back(m_FileRegister->getFile(iter->second));
- }
-
- return result;
-}
-
-DirectoryEntry* DirectoryEntry::findSubDirectory(
- const std::wstring &name, bool alreadyLowerCase) const
-{
- SubDirectoriesLookup::const_iterator itor;
-
- if (alreadyLowerCase) {
- itor = m_SubDirectoriesLookup.find(name);
- } else {
- itor = m_SubDirectoriesLookup.find(ToLowerCopy(name));
- }
-
- if (itor == m_SubDirectoriesLookup.end()) {
- return nullptr;
- }
-
- return itor->second;
-}
-
-DirectoryEntry* DirectoryEntry::findSubDirectoryRecursive(const std::wstring &path)
-{
- DirectoryStats dummy;
- return getSubDirectoryRecursive(path, false, dummy, InvalidOriginID);
-}
-
-const FileEntryPtr DirectoryEntry::findFile(
- const std::wstring &name, bool alreadyLowerCase) const
-{
- FilesLookup::const_iterator iter;
-
- if (alreadyLowerCase) {
- iter = m_FilesLookup.find(DirectoryEntryFileKey(name));
- } else {
- iter = m_FilesLookup.find(DirectoryEntryFileKey(ToLowerCopy(name)));
- }
-
- if (iter != m_FilesLookup.end()) {
- return m_FileRegister->getFile(iter->second);
- } else {
- return FileEntryPtr();
- }
-}
-
-const FileEntryPtr DirectoryEntry::findFile(const DirectoryEntryFileKey& key) const
-{
- auto iter = m_FilesLookup.find(key);
-
- if (iter != m_FilesLookup.end()) {
- return m_FileRegister->getFile(iter->second);
- } else {
- return FileEntryPtr();
- }
-}
-
-bool DirectoryEntry::hasFile(const std::wstring& name) const
-{
- return m_Files.contains(ToLowerCopy(name));
-}
-
-bool DirectoryEntry::containsArchive(std::wstring archiveName)
-{
- for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
- FileEntryPtr entry = m_FileRegister->getFile(iter->second);
- if (entry->isFromArchive(archiveName)) {
- return true;
- }
- }
-
- return false;
-}
-
-const FileEntryPtr DirectoryEntry::searchFile(
- const std::wstring &path, const DirectoryEntry** directory) const
-{
- if (directory != nullptr) {
- *directory = nullptr;
- }
-
- if ((path.length() == 0) || (path == L"*")) {
- // no file name -> the path ended on a (back-)slash
- if (directory != nullptr) {
- *directory = this;
- }
-
- return FileEntryPtr();
- }
-
- const size_t len = path.find_first_of(L"\\/");
-
- if (len == std::string::npos) {
- // no more path components
- auto iter = m_Files.find(ToLowerCopy(path));
-
- if (iter != m_Files.end()) {
- return m_FileRegister->getFile(iter->second);
- } else if (directory != nullptr) {
- DirectoryEntry* temp = findSubDirectory(path);
- if (temp != nullptr) {
- *directory = temp;
- }
- }
- } else {
- // file is in a subdirectory, recurse into the matching subdirectory
- std::wstring pathComponent = path.substr(0, len);
- DirectoryEntry* temp = findSubDirectory(pathComponent);
-
- if (temp != nullptr) {
- if (len >= path.size()) {
- log::error(QObject::tr("unexpected end of path").toStdString());
- return FileEntryPtr();
- }
-
- return temp->searchFile(path.substr(len + 1), directory);
- }
- }
-
- return FileEntryPtr();
-}
-
-void DirectoryEntry::removeFile(FileIndex index)
-{
- removeFileFromList(index);
-}
-
-bool DirectoryEntry::removeFile(const std::wstring &filePath, int* origin)
-{
- size_t pos = filePath.find_first_of(L"\\/");
-
- if (pos == std::string::npos) {
- return this->remove(filePath, origin);
- }
-
- std::wstring dirName = filePath.substr(0, pos);
- std::wstring rest = filePath.substr(pos + 1);
-
- DirectoryStats dummy;
- DirectoryEntry* entry = getSubDirectoryRecursive(dirName, false, dummy);
-
- if (entry != nullptr) {
- return entry->removeFile(rest, origin);
- } else {
- return false;
- }
-}
-
-void DirectoryEntry::removeDir(const std::wstring &path)
-{
- size_t pos = path.find_first_of(L"\\/");
-
- if (pos == std::string::npos) {
- for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
- DirectoryEntry* entry = *iter;
-
- if (CaseInsensitiveEqual(entry->getName(), path)) {
- entry->removeDirRecursive();
- removeDirectoryFromList(iter);
- delete entry;
- break;
- }
- }
- } else {
- std::wstring dirName = path.substr(0, pos);
- std::wstring rest = path.substr(pos + 1);
-
- DirectoryStats dummy;
- DirectoryEntry* entry = getSubDirectoryRecursive(dirName, false, dummy);
-
- if (entry != nullptr) {
- entry->removeDir(rest);
- }
- }
-}
-
-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) {
- FileEntryPtr entry = m_FileRegister->getFile(iter->second);
- if (entry.get() != nullptr) {
- bool ignore;
- *origin = entry->getOrigin(ignore);
- }
- }
-
- b = m_FileRegister->removeFile(iter->second);
- }
-
- return b;
-}
-
-bool DirectoryEntry::hasContentsFromOrigin(int originID) const
-{
- return m_Origins.find(originID) != m_Origins.end();
-}
-
-FilesOrigin &DirectoryEntry::createOrigin(
- const std::wstring &originName, const std::wstring &directory, int priority,
- DirectoryStats& stats)
-{
- auto r = m_OriginConnection->getOrCreate(
- originName, directory, priority,
- m_FileRegister, m_OriginConnection, stats);
-
- if (r.second) {
- ++stats.originCreate;
- } else {
- ++stats.originExists;
- }
-
- return r.first;
-}
-
-void DirectoryEntry::removeFiles(const std::set<FileIndex> &indices)
-{
- removeFilesFromList(indices);
-}
-
-FileEntryPtr DirectoryEntry::insert(
- std::wstring_view fileName, FilesOrigin &origin, FILETIME fileTime,
- std::wstring_view archive, int order, DirectoryStats& stats)
-{
- std::wstring fileNameLower = ToLowerCopy(fileName);
- FileEntryPtr fe;
-
- DirectoryEntryFileKey key(std::move(fileNameLower));
-
- {
- std::unique_lock lock(m_FilesMutex);
-
- FilesLookup::iterator itor;
-
- elapsed(stats.filesLookupTimes, [&]{
- itor = m_FilesLookup.find(key);
- });
-
- if (itor != m_FilesLookup.end()) {
- lock.unlock();
- ++stats.fileExists;
- fe = m_FileRegister->getFile(itor->second);
- } else {
- ++stats.fileCreate;
- fe = m_FileRegister->createFile(
- std::wstring(fileName.begin(), fileName.end()), this, stats);
-
- elapsed(stats.addFileTimes, [&] {
- addFileToList(std::move(key.value), fe->getIndex());
- });
-
- // fileNameLower has moved from this point
- }
- }
-
- elapsed(stats.addOriginToFileTimes, [&]{
- fe->addOrigin(origin.getID(), fileTime, archive, order);
- });
-
- elapsed(stats.addFileToOriginTimes, [&]{
- origin.addFile(fe->getIndex());
- });
-
- return fe;
-}
-
-FileEntryPtr DirectoryEntry::insert(
- env::File& file, FilesOrigin &origin, std::wstring_view archive, int order,
- DirectoryStats& stats)
-{
- FileEntryPtr fe;
-
- {
- std::unique_lock lock(m_FilesMutex);
-
- FilesMap::iterator itor;
-
- elapsed(stats.filesLookupTimes, [&]{
- itor = m_Files.find(file.lcname);
- });
-
- if (itor != m_Files.end()) {
- lock.unlock();
- ++stats.fileExists;
- fe = m_FileRegister->getFile(itor->second);
- } else {
- ++stats.fileCreate;
- fe = m_FileRegister->createFile(std::move(file.name), this, stats);
- // file.name has been moved from this point
-
- elapsed(stats.addFileTimes, [&]{
- addFileToList(std::move(file.lcname), fe->getIndex());
- });
-
- // file.lcname has been moved from this point
- }
- }
-
- elapsed(stats.addOriginToFileTimes, [&]{
- fe->addOrigin(origin.getID(), file.lastModified, archive, order);
- });
-
- elapsed(stats.addFileToOriginTimes, [&]{
- origin.addFile(fe->getIndex());
- });
-
- return fe;
-}
-
-struct DirectoryEntry::Context
-{
- FilesOrigin& origin;
- DirectoryStats& stats;
- std::stack<DirectoryEntry*> current;
-};
-
-void DirectoryEntry::addFiles(
- env::DirectoryWalker& walker, FilesOrigin &origin,
- const std::wstring& path, DirectoryStats& stats)
-{
- Context cx = {origin, stats};
- cx.current.push(this);
-
- walker.forEachEntry(path, &cx,
- [](void* pcx, std::wstring_view path)
- {
- onDirectoryStart((Context*)pcx, path);
- },
-
- [](void* pcx, std::wstring_view path)
- {
- onDirectoryEnd((Context*)pcx, path);
- },
-
- [](void* pcx, std::wstring_view path, FILETIME ft, uint64_t)
- {
- onFile((Context*)pcx, path, ft);
- }
- );
-}
-
-void DirectoryEntry::onDirectoryStart(Context* cx, std::wstring_view path)
-{
- elapsed(cx->stats.dirTimes, [&] {
- auto* sd = cx->current.top()->getSubDirectory(
- path, true, cx->stats, cx->origin.getID());
-
- cx->current.push(sd);
- });
-}
-
-void DirectoryEntry::onDirectoryEnd(Context* cx, std::wstring_view path)
-{
- elapsed(cx->stats.dirTimes, [&] {
- cx->current.pop();
- });
-}
-
-void DirectoryEntry::onFile(Context* cx, std::wstring_view path, FILETIME ft)
-{
- elapsed(cx->stats.fileTimes, [&]{
- cx->current.top()->insert(path, cx->origin, ft, L"", -1, cx->stats);
- });
-}
-
-void DirectoryEntry::addFiles(
- FilesOrigin& origin, const BSA::Folder::Ptr archiveFolder, FILETIME fileTime,
- const std::wstring& archiveName, int order, DirectoryStats& stats)
-{
- // add files
- const auto fileCount = archiveFolder->getNumFiles();
- for (unsigned int i=0; i<fileCount; ++i) {
- const BSA::File::Ptr file = archiveFolder->getFile(i);
-
- auto f = insert(
- ToWString(file->getName(), true), origin, fileTime,
- archiveName, order, stats);
-
- if (f) {
- if (file->getUncompressedFileSize() > 0) {
- f->setFileSize(file->getFileSize(), file->getUncompressedFileSize());
- } else {
- f->setFileSize(file->getFileSize(), FileEntry::NoFileSize);
- }
- }
- }
-
- // recurse into subdirectories
- const auto dirCount = archiveFolder->getNumSubFolders();
- for (unsigned int i=0; i<dirCount; ++i) {
- const BSA::Folder::Ptr folder = archiveFolder->getSubFolder(i);
-
- DirectoryEntry* folderEntry = getSubDirectoryRecursive(
- ToWString(folder->getName(), true), true, stats, origin.getID());
-
- folderEntry->addFiles(origin, folder, fileTime, archiveName, order, stats);
- }
-}
-
-DirectoryEntry* DirectoryEntry::getSubDirectory(
- std::wstring_view name, bool create, DirectoryStats& stats, int originID)
-{
- std::wstring nameLc = ToLowerCopy(name);
-
- std::scoped_lock lock(m_SubDirMutex);
-
- SubDirectoriesLookup::iterator itor;
- elapsed(stats.subdirLookupTimes, [&] {
- itor = m_SubDirectoriesLookup.find(nameLc);
- });
-
- if (itor != m_SubDirectoriesLookup.end()) {
- ++stats.subdirExists;
- return itor->second;
- }
-
- if (create) {
- ++stats.subdirCreate;
-
- auto* entry = new DirectoryEntry(
- std::wstring(name.begin(), name.end()), this, originID,
- m_FileRegister, m_OriginConnection);
-
- elapsed(stats.addDirectoryTimes, [&] {
- addDirectoryToList(entry, std::move(nameLc));
- // nameLc is moved from this point
- });
-
- return entry;
- } else {
- return nullptr;
- }
-}
-
-DirectoryEntry* DirectoryEntry::getSubDirectory(
- env::Directory& dir, bool create, DirectoryStats& stats, int originID)
-{
- SubDirectoriesLookup::iterator itor;
-
- std::scoped_lock lock(m_SubDirMutex);
-
- elapsed(stats.subdirLookupTimes, [&] {
- itor = m_SubDirectoriesLookup.find(dir.lcname);
- });
-
- if (itor != m_SubDirectoriesLookup.end()) {
- ++stats.subdirExists;
- return itor->second;
- }
-
- if (create) {
- ++stats.subdirCreate;
-
- auto* entry = new DirectoryEntry(
- std::move(dir.name), this, originID,
- m_FileRegister, m_OriginConnection);
- // dir.name is moved from this point
-
- elapsed(stats.addDirectoryTimes, [&]{
- addDirectoryToList(entry, std::move(dir.lcname));
- });
-
- // dir.lcname is moved from this point
-
- return entry;
- } else {
- return nullptr;
- }
-}
-
-DirectoryEntry* DirectoryEntry::getSubDirectoryRecursive(
- const std::wstring& path, bool create, DirectoryStats& stats, int originID)
-{
- if (path.length() == 0) {
- // path ended with a backslash?
- return this;
- }
-
- const size_t pos = path.find_first_of(L"\\/");
-
- if (pos == std::wstring::npos) {
- return getSubDirectory(path, create, stats);
- } else {
- DirectoryEntry* nextChild = getSubDirectory(
- path.substr(0, pos), create, stats, originID);
-
- if (nextChild == nullptr) {
- return nullptr;
- } else {
- return nextChild->getSubDirectoryRecursive(
- path.substr(pos + 1), create, stats, originID);
- }
- }
-}
-
-void DirectoryEntry::removeDirRecursive()
-{
- while (!m_Files.empty()) {
- m_FileRegister->removeFile(m_Files.begin()->second);
- }
-
- m_FilesLookup.clear();
-
- for (DirectoryEntry* entry : m_SubDirectories) {
- entry->removeDirRecursive();
- delete entry;
- }
-
- m_SubDirectories.clear();
- m_SubDirectoriesLookup.clear();
-}
-
-void DirectoryEntry::addDirectoryToList(DirectoryEntry* e, std::wstring nameLc)
-{
- m_SubDirectories.insert(e);
- m_SubDirectoriesLookup.emplace(std::move(nameLc), e);
-}
-
-void DirectoryEntry::removeDirectoryFromList(SubDirectories::iterator itor)
-{
- const auto* entry = *itor;
-
- {
- auto itor2 = std::find_if(
- m_SubDirectoriesLookup.begin(), m_SubDirectoriesLookup.end(),
- [&](auto&& d) { return (d.second == entry); });
-
- if (itor2 == m_SubDirectoriesLookup.end()) {
- log::error("entry {} not in sub directories map", entry->getName());
- } else {
- m_SubDirectoriesLookup.erase(itor2);
- }
- }
-
- m_SubDirectories.erase(itor);
-}
-
-void DirectoryEntry::removeFileFromList(FileIndex index)
-{
- auto removeFrom = [&](auto& list) {
- auto iter = std::find_if(
- list.begin(), list.end(),
- [&index](auto&& pair) { return (pair.second == index); }
- );
-
- if (iter == list.end()) {
- auto f = m_FileRegister->getFile(index);
-
- if (f) {
- log::error(
- "can't remove file '{}', not in directory entry '{}'",
- f->getName(), getName());
- } else {
- log::error(
- "can't remove file with index {}, not in directory entry '{}' and "
- "not in register",
- index, getName());
- }
- } else {
- list.erase(iter);
- }
- };
-
- removeFrom(m_FilesLookup);
- removeFrom(m_Files);
-}
-
-void DirectoryEntry::removeFilesFromList(const std::set<FileIndex>& indices)
-{
- for (auto iter = m_Files.begin(); iter != m_Files.end();) {
- if (indices.find(iter->second) != indices.end()) {
- 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;
- }
- }
-}
-
-void DirectoryEntry::addFileToList(std::wstring fileNameLower, FileIndex index)
-{
- m_FilesLookup.emplace(fileNameLower, index);
- m_Files.emplace(std::move(fileNameLower), index);
- // fileNameLower has been moved from this point
-}
-
-struct DumpFailed : public std::runtime_error
-{
- using runtime_error::runtime_error;
-};
-
-void DirectoryEntry::dump(const std::wstring& file) const
-{
- try
- {
- std::FILE* f = nullptr;
- auto e = _wfopen_s(&f, file.c_str(), L"wb");
-
- if (e != 0 || !f) {
- throw DumpFailed(fmt::format(
- "failed to open, {} ({})", std::strerror(e), e));
- }
-
- Guard g([&]{ std::fclose(f); });
-
- dump(f, L"Data");
- }
- catch(DumpFailed& e)
- {
- log::error(
- "failed to write list to '{}': {}",
- QString::fromStdWString(file).toStdString(), e.what());
- }
-}
-
-void DirectoryEntry::dump(std::FILE* f, const std::wstring& parentPath) const
-{
- {
- std::scoped_lock lock(m_FilesMutex);
-
- for (auto&& index : m_Files) {
- const auto file = m_FileRegister->getFile(index.second);
- if (!file) {
- continue;
- }
-
- if (file->isFromArchive()) {
- // TODO: don't list files from archives. maybe make this an option?
- continue;
- }
-
- const auto& o = m_OriginConnection->getByID(file->getOrigin());
- const auto path = parentPath + L"\\" + file->getName();
- const auto line = path + L"\t(" + o.getName() + L")\r\n";
-
- const auto lineu8 = MOShared::ToString(line, true);
-
- if (std::fwrite(lineu8.data(), lineu8.size(), 1, f) != 1) {
- const auto e = errno;
- throw DumpFailed(fmt::format(
- "failed to write, {} ({})", std::strerror(e), e));
- }
- }
- }
-
- {
- std::scoped_lock lock(m_SubDirMutex);
- for (auto&& d : m_SubDirectories) {
- const auto path = parentPath + L"\\" + d->m_Name;
- d->dump(f, path);
- }
- }
-}
-
-} // namespace MOShared
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "directoryentry.h"
+#include "../envfs.h"
+#include "fileentry.h"
+#include "filesorigin.h"
+#include "originconnection.h"
+#include "util.h"
+#include "windows_error.h"
+#include <log.h>
+#include <utility.h>
+
+namespace MOShared
+{
+
+using namespace MOBase;
+const int MAXPATH_UNICODE = 32767;
+
+template <class F>
+void elapsedImpl(std::chrono::nanoseconds& out, F&& f)
+{
+ if constexpr (DirectoryStats::EnableInstrumentation) {
+ const auto start = std::chrono::high_resolution_clock::now();
+ f();
+ const auto end = std::chrono::high_resolution_clock::now();
+ out += (end - start);
+ } else {
+ f();
+ }
+}
+
+// elapsed() is not optimized out when EnableInstrumentation is false even
+// though it's equivalent that this macro
+#define elapsed(OUT, F) (F)();
+// #define elapsed(OUT, F) elapsedImpl(OUT, F);
+
+static bool SupportOptimizedFind()
+{
+ // large fetch and basic info for FindFirstFileEx is supported on win server 2008 r2,
+ // win 7 and newer
+
+ OSVERSIONINFOEX versionInfo;
+ versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ versionInfo.dwMajorVersion = 6;
+ versionInfo.dwMinorVersion = 1;
+
+ ULONGLONG mask = ::VerSetConditionMask(
+ ::VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION,
+ VER_GREATER_EQUAL);
+
+ return (::VerifyVersionInfo(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION,
+ mask) == TRUE);
+}
+
+bool DirCompareByName::operator()(const DirectoryEntry* lhs,
+ const DirectoryEntry* rhs) const
+{
+ return _wcsicmp(lhs->getName().c_str(), rhs->getName().c_str()) < 0;
+}
+
+DirectoryEntry::DirectoryEntry(std::wstring name, DirectoryEntry* parent, int originID)
+ : m_OriginConnection(new OriginConnection), m_Name(std::move(name)),
+ m_Parent(parent), m_Populated(false), m_TopLevel(true)
+{
+ m_FileRegister.reset(new FileRegister(m_OriginConnection));
+ m_Origins.insert(originID);
+}
+
+DirectoryEntry::DirectoryEntry(std::wstring name, DirectoryEntry* parent, int originID,
+ boost::shared_ptr<FileRegister> fileRegister,
+ boost::shared_ptr<OriginConnection> originConnection)
+ : m_FileRegister(fileRegister), m_OriginConnection(originConnection),
+ m_Name(std::move(name)), m_Parent(parent), m_Populated(false), m_TopLevel(false)
+{
+ m_Origins.insert(originID);
+}
+
+DirectoryEntry::~DirectoryEntry()
+{
+ clear();
+}
+
+void DirectoryEntry::clear()
+{
+ for (auto itor = m_SubDirectories.rbegin(); itor != m_SubDirectories.rend(); ++itor) {
+ delete *itor;
+ }
+
+ m_Files.clear();
+ m_FilesLookup.clear();
+ m_SubDirectories.clear();
+ m_SubDirectoriesLookup.clear();
+}
+
+void DirectoryEntry::addFromOrigin(const std::wstring& originName,
+ const std::wstring& directory, int priority,
+ DirectoryStats& stats)
+{
+ env::DirectoryWalker walker;
+ addFromOrigin(walker, originName, directory, priority, stats);
+}
+
+void DirectoryEntry::addFromOrigin(env::DirectoryWalker& walker,
+ const std::wstring& originName,
+ const std::wstring& directory, int priority,
+ DirectoryStats& stats)
+{
+ FilesOrigin& origin = createOrigin(originName, directory, priority, stats);
+
+ if (!directory.empty()) {
+ addFiles(walker, origin, directory, stats);
+ }
+
+ m_Populated = true;
+}
+
+void DirectoryEntry::addFromList(const std::wstring& originName,
+ const std::wstring& directory, env::Directory& root,
+ int priority, DirectoryStats& stats)
+{
+ stats = {};
+
+ FilesOrigin& origin = createOrigin(originName, directory, priority, stats);
+ addDir(origin, root, stats);
+}
+
+void DirectoryEntry::addDir(FilesOrigin& origin, env::Directory& d,
+ DirectoryStats& stats)
+{
+ elapsed(stats.dirTimes, [&] {
+ for (auto& sd : d.dirs) {
+ auto* sdirEntry = getSubDirectory(sd, true, stats, origin.getID());
+ sdirEntry->addDir(origin, sd, stats);
+ }
+ });
+
+ elapsed(stats.fileTimes, [&] {
+ for (auto& f : d.files) {
+ insert(f, origin, L"", -1, stats);
+ }
+ });
+
+ m_Populated = true;
+}
+
+void DirectoryEntry::addFromAllBSAs(const std::wstring& originName,
+ const std::wstring& directory, int priority,
+ const std::vector<std::wstring>& archives,
+ const std::set<std::wstring>& enabledArchives,
+ const std::vector<std::wstring>& loadOrder,
+ DirectoryStats& stats)
+{
+ for (const auto& archive : archives) {
+ const std::filesystem::path archivePath(archive);
+ const auto filename = archivePath.filename().native();
+
+ if (!enabledArchives.contains(filename)) {
+ continue;
+ }
+
+ const auto filenameLc = ToLowerCopy(filename);
+
+ int order = -1;
+
+ for (auto plugin : loadOrder) {
+ const auto pluginNameLc =
+ ToLowerCopy(std::filesystem::path(plugin).stem().native());
+
+ if (filenameLc.starts_with(pluginNameLc + L" - ") ||
+ filenameLc.starts_with(pluginNameLc + L".")) {
+ auto itor = std::find(loadOrder.begin(), loadOrder.end(), plugin);
+ if (itor != loadOrder.end()) {
+ order = std::distance(loadOrder.begin(), itor);
+ }
+ }
+ }
+
+ addFromBSA(originName, directory, archivePath.native(), priority, order, stats);
+ }
+}
+
+void DirectoryEntry::addFromBSA(const std::wstring& originName,
+ const std::wstring& directory,
+ const std::wstring& archivePath, int priority,
+ int order, DirectoryStats& stats)
+{
+ FilesOrigin& origin = createOrigin(originName, directory, priority, stats);
+ const auto archiveName = std::filesystem::path(archivePath).filename().native();
+
+ if (containsArchive(archiveName)) {
+ return;
+ }
+
+ BSA::Archive archive;
+ BSA::EErrorCode res = BSA::ERROR_NONE;
+
+ try {
+ // read() can return an error, but it can also throw if the file is not a
+ // valid bsa
+ res = archive.read(ToString(archivePath, false).c_str(), false);
+ } catch (std::exception& e) {
+ log::error("invalid bsa '{}', error {}", archivePath, e.what());
+ return;
+ }
+
+ if ((res != BSA::ERROR_NONE) && (res != BSA::ERROR_INVALIDHASHES)) {
+ log::error("invalid bsa '{}', error {}", archivePath, res);
+ return;
+ }
+
+ std::error_code ec;
+ const auto lwt = std::filesystem::last_write_time(archivePath, ec);
+ FILETIME ft = {};
+
+ if (ec) {
+ log::warn("failed to get last modified date for '{}', {}", archivePath,
+ ec.message());
+ } else {
+ ft = ToFILETIME(lwt);
+ }
+
+ addFiles(origin, archive.getRoot(), ft, archiveName, order, stats);
+
+ m_Populated = true;
+}
+
+void DirectoryEntry::propagateOrigin(int origin)
+{
+ {
+ std::scoped_lock lock(m_OriginsMutex);
+ m_Origins.insert(origin);
+ }
+
+ if (m_Parent != nullptr) {
+ m_Parent->propagateOrigin(origin);
+ }
+}
+
+bool DirectoryEntry::originExists(const std::wstring& name) const
+{
+ return m_OriginConnection->exists(name);
+}
+
+FilesOrigin& DirectoryEntry::getOriginByID(int ID) const
+{
+ return m_OriginConnection->getByID(ID);
+}
+
+FilesOrigin& DirectoryEntry::getOriginByName(const std::wstring& name) const
+{
+ return m_OriginConnection->getByName(name);
+}
+
+const FilesOrigin* DirectoryEntry::findOriginByID(int ID) const
+{
+ return m_OriginConnection->findByID(ID);
+}
+
+int DirectoryEntry::anyOrigin() const
+{
+ bool ignore;
+
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ FileEntryPtr entry = m_FileRegister->getFile(iter->second);
+ if ((entry.get() != nullptr) && !entry->isFromArchive()) {
+ return entry->getOrigin(ignore);
+ }
+ }
+
+ // if we got here, no file directly within this directory is a valid indicator for a
+ // mod, thus we continue looking in subdirectories
+ for (DirectoryEntry* entry : m_SubDirectories) {
+ int res = entry->anyOrigin();
+ if (res != InvalidOriginID) {
+ return res;
+ }
+ }
+
+ return *(m_Origins.begin());
+}
+
+std::vector<FileEntryPtr> DirectoryEntry::getFiles() const
+{
+ std::vector<FileEntryPtr> result;
+
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ result.push_back(m_FileRegister->getFile(iter->second));
+ }
+
+ return result;
+}
+
+DirectoryEntry* DirectoryEntry::findSubDirectory(const std::wstring& name,
+ bool alreadyLowerCase) const
+{
+ SubDirectoriesLookup::const_iterator itor;
+
+ if (alreadyLowerCase) {
+ itor = m_SubDirectoriesLookup.find(name);
+ } else {
+ itor = m_SubDirectoriesLookup.find(ToLowerCopy(name));
+ }
+
+ if (itor == m_SubDirectoriesLookup.end()) {
+ return nullptr;
+ }
+
+ return itor->second;
+}
+
+DirectoryEntry* DirectoryEntry::findSubDirectoryRecursive(const std::wstring& path)
+{
+ DirectoryStats dummy;
+ return getSubDirectoryRecursive(path, false, dummy, InvalidOriginID);
+}
+
+const FileEntryPtr DirectoryEntry::findFile(const std::wstring& name,
+ bool alreadyLowerCase) const
+{
+ FilesLookup::const_iterator iter;
+
+ if (alreadyLowerCase) {
+ iter = m_FilesLookup.find(DirectoryEntryFileKey(name));
+ } else {
+ iter = m_FilesLookup.find(DirectoryEntryFileKey(ToLowerCopy(name)));
+ }
+
+ if (iter != m_FilesLookup.end()) {
+ return m_FileRegister->getFile(iter->second);
+ } else {
+ return FileEntryPtr();
+ }
+}
+
+const FileEntryPtr DirectoryEntry::findFile(const DirectoryEntryFileKey& key) const
+{
+ auto iter = m_FilesLookup.find(key);
+
+ if (iter != m_FilesLookup.end()) {
+ return m_FileRegister->getFile(iter->second);
+ } else {
+ return FileEntryPtr();
+ }
+}
+
+bool DirectoryEntry::hasFile(const std::wstring& name) const
+{
+ return m_Files.contains(ToLowerCopy(name));
+}
+
+bool DirectoryEntry::containsArchive(std::wstring archiveName)
+{
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ FileEntryPtr entry = m_FileRegister->getFile(iter->second);
+ if (entry->isFromArchive(archiveName)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const FileEntryPtr DirectoryEntry::searchFile(const std::wstring& path,
+ const DirectoryEntry** directory) const
+{
+ if (directory != nullptr) {
+ *directory = nullptr;
+ }
+
+ if ((path.length() == 0) || (path == L"*")) {
+ // no file name -> the path ended on a (back-)slash
+ if (directory != nullptr) {
+ *directory = this;
+ }
+
+ return FileEntryPtr();
+ }
+
+ const size_t len = path.find_first_of(L"\\/");
+
+ if (len == std::string::npos) {
+ // no more path components
+ auto iter = m_Files.find(ToLowerCopy(path));
+
+ if (iter != m_Files.end()) {
+ return m_FileRegister->getFile(iter->second);
+ } else if (directory != nullptr) {
+ DirectoryEntry* temp = findSubDirectory(path);
+ if (temp != nullptr) {
+ *directory = temp;
+ }
+ }
+ } else {
+ // file is in a subdirectory, recurse into the matching subdirectory
+ std::wstring pathComponent = path.substr(0, len);
+ DirectoryEntry* temp = findSubDirectory(pathComponent);
+
+ if (temp != nullptr) {
+ if (len >= path.size()) {
+ log::error(QObject::tr("unexpected end of path").toStdString());
+ return FileEntryPtr();
+ }
+
+ return temp->searchFile(path.substr(len + 1), directory);
+ }
+ }
+
+ return FileEntryPtr();
+}
+
+void DirectoryEntry::removeFile(FileIndex index)
+{
+ removeFileFromList(index);
+}
+
+bool DirectoryEntry::removeFile(const std::wstring& filePath, int* origin)
+{
+ size_t pos = filePath.find_first_of(L"\\/");
+
+ if (pos == std::string::npos) {
+ return this->remove(filePath, origin);
+ }
+
+ std::wstring dirName = filePath.substr(0, pos);
+ std::wstring rest = filePath.substr(pos + 1);
+
+ DirectoryStats dummy;
+ DirectoryEntry* entry = getSubDirectoryRecursive(dirName, false, dummy);
+
+ if (entry != nullptr) {
+ return entry->removeFile(rest, origin);
+ } else {
+ return false;
+ }
+}
+
+void DirectoryEntry::removeDir(const std::wstring& path)
+{
+ size_t pos = path.find_first_of(L"\\/");
+
+ if (pos == std::string::npos) {
+ for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ DirectoryEntry* entry = *iter;
+
+ if (CaseInsensitiveEqual(entry->getName(), path)) {
+ entry->removeDirRecursive();
+ removeDirectoryFromList(iter);
+ delete entry;
+ break;
+ }
+ }
+ } else {
+ std::wstring dirName = path.substr(0, pos);
+ std::wstring rest = path.substr(pos + 1);
+
+ DirectoryStats dummy;
+ DirectoryEntry* entry = getSubDirectoryRecursive(dirName, false, dummy);
+
+ if (entry != nullptr) {
+ entry->removeDir(rest);
+ }
+ }
+}
+
+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) {
+ FileEntryPtr entry = m_FileRegister->getFile(iter->second);
+ if (entry.get() != nullptr) {
+ bool ignore;
+ *origin = entry->getOrigin(ignore);
+ }
+ }
+
+ b = m_FileRegister->removeFile(iter->second);
+ }
+
+ return b;
+}
+
+bool DirectoryEntry::hasContentsFromOrigin(int originID) const
+{
+ return m_Origins.find(originID) != m_Origins.end();
+}
+
+FilesOrigin& DirectoryEntry::createOrigin(const std::wstring& originName,
+ const std::wstring& directory, int priority,
+ DirectoryStats& stats)
+{
+ auto r = m_OriginConnection->getOrCreate(originName, directory, priority,
+ m_FileRegister, m_OriginConnection, stats);
+
+ if (r.second) {
+ ++stats.originCreate;
+ } else {
+ ++stats.originExists;
+ }
+
+ return r.first;
+}
+
+void DirectoryEntry::removeFiles(const std::set<FileIndex>& indices)
+{
+ removeFilesFromList(indices);
+}
+
+FileEntryPtr DirectoryEntry::insert(std::wstring_view fileName, FilesOrigin& origin,
+ FILETIME fileTime, std::wstring_view archive,
+ int order, DirectoryStats& stats)
+{
+ std::wstring fileNameLower = ToLowerCopy(fileName);
+ FileEntryPtr fe;
+
+ DirectoryEntryFileKey key(std::move(fileNameLower));
+
+ {
+ std::unique_lock lock(m_FilesMutex);
+
+ FilesLookup::iterator itor;
+
+ elapsed(stats.filesLookupTimes, [&] {
+ itor = m_FilesLookup.find(key);
+ });
+
+ if (itor != m_FilesLookup.end()) {
+ lock.unlock();
+ ++stats.fileExists;
+ fe = m_FileRegister->getFile(itor->second);
+ } else {
+ ++stats.fileCreate;
+ fe = m_FileRegister->createFile(std::wstring(fileName.begin(), fileName.end()),
+ this, stats);
+
+ elapsed(stats.addFileTimes, [&] {
+ addFileToList(std::move(key.value), fe->getIndex());
+ });
+
+ // fileNameLower has moved from this point
+ }
+ }
+
+ elapsed(stats.addOriginToFileTimes, [&] {
+ fe->addOrigin(origin.getID(), fileTime, archive, order);
+ });
+
+ elapsed(stats.addFileToOriginTimes, [&] {
+ origin.addFile(fe->getIndex());
+ });
+
+ return fe;
+}
+
+FileEntryPtr DirectoryEntry::insert(env::File& file, FilesOrigin& origin,
+ std::wstring_view archive, int order,
+ DirectoryStats& stats)
+{
+ FileEntryPtr fe;
+
+ {
+ std::unique_lock lock(m_FilesMutex);
+
+ FilesMap::iterator itor;
+
+ elapsed(stats.filesLookupTimes, [&] {
+ itor = m_Files.find(file.lcname);
+ });
+
+ if (itor != m_Files.end()) {
+ lock.unlock();
+ ++stats.fileExists;
+ fe = m_FileRegister->getFile(itor->second);
+ } else {
+ ++stats.fileCreate;
+ fe = m_FileRegister->createFile(std::move(file.name), this, stats);
+ // file.name has been moved from this point
+
+ elapsed(stats.addFileTimes, [&] {
+ addFileToList(std::move(file.lcname), fe->getIndex());
+ });
+
+ // file.lcname has been moved from this point
+ }
+ }
+
+ elapsed(stats.addOriginToFileTimes, [&] {
+ fe->addOrigin(origin.getID(), file.lastModified, archive, order);
+ });
+
+ elapsed(stats.addFileToOriginTimes, [&] {
+ origin.addFile(fe->getIndex());
+ });
+
+ return fe;
+}
+
+struct DirectoryEntry::Context
+{
+ FilesOrigin& origin;
+ DirectoryStats& stats;
+ std::stack<DirectoryEntry*> current;
+};
+
+void DirectoryEntry::addFiles(env::DirectoryWalker& walker, FilesOrigin& origin,
+ const std::wstring& path, DirectoryStats& stats)
+{
+ Context cx = {origin, stats};
+ cx.current.push(this);
+
+ walker.forEachEntry(
+ path, &cx,
+ [](void* pcx, std::wstring_view path) {
+ onDirectoryStart((Context*)pcx, path);
+ },
+
+ [](void* pcx, std::wstring_view path) {
+ onDirectoryEnd((Context*)pcx, path);
+ },
+
+ [](void* pcx, std::wstring_view path, FILETIME ft, uint64_t) {
+ onFile((Context*)pcx, path, ft);
+ });
+}
+
+void DirectoryEntry::onDirectoryStart(Context* cx, std::wstring_view path)
+{
+ elapsed(cx->stats.dirTimes, [&] {
+ auto* sd =
+ cx->current.top()->getSubDirectory(path, true, cx->stats, cx->origin.getID());
+
+ cx->current.push(sd);
+ });
+}
+
+void DirectoryEntry::onDirectoryEnd(Context* cx, std::wstring_view path)
+{
+ elapsed(cx->stats.dirTimes, [&] {
+ cx->current.pop();
+ });
+}
+
+void DirectoryEntry::onFile(Context* cx, std::wstring_view path, FILETIME ft)
+{
+ elapsed(cx->stats.fileTimes, [&] {
+ cx->current.top()->insert(path, cx->origin, ft, L"", -1, cx->stats);
+ });
+}
+
+void DirectoryEntry::addFiles(FilesOrigin& origin, const BSA::Folder::Ptr archiveFolder,
+ FILETIME fileTime, const std::wstring& archiveName,
+ int order, DirectoryStats& stats)
+{
+ // add files
+ const auto fileCount = archiveFolder->getNumFiles();
+ for (unsigned int i = 0; i < fileCount; ++i) {
+ const BSA::File::Ptr file = archiveFolder->getFile(i);
+
+ auto f = insert(ToWString(file->getName(), true), origin, fileTime, archiveName,
+ order, stats);
+
+ if (f) {
+ if (file->getUncompressedFileSize() > 0) {
+ f->setFileSize(file->getFileSize(), file->getUncompressedFileSize());
+ } else {
+ f->setFileSize(file->getFileSize(), FileEntry::NoFileSize);
+ }
+ }
+ }
+
+ // recurse into subdirectories
+ const auto dirCount = archiveFolder->getNumSubFolders();
+ for (unsigned int i = 0; i < dirCount; ++i) {
+ const BSA::Folder::Ptr folder = archiveFolder->getSubFolder(i);
+
+ DirectoryEntry* folderEntry = getSubDirectoryRecursive(
+ ToWString(folder->getName(), true), true, stats, origin.getID());
+
+ folderEntry->addFiles(origin, folder, fileTime, archiveName, order, stats);
+ }
+}
+
+DirectoryEntry* DirectoryEntry::getSubDirectory(std::wstring_view name, bool create,
+ DirectoryStats& stats, int originID)
+{
+ std::wstring nameLc = ToLowerCopy(name);
+
+ std::scoped_lock lock(m_SubDirMutex);
+
+ SubDirectoriesLookup::iterator itor;
+ elapsed(stats.subdirLookupTimes, [&] {
+ itor = m_SubDirectoriesLookup.find(nameLc);
+ });
+
+ if (itor != m_SubDirectoriesLookup.end()) {
+ ++stats.subdirExists;
+ return itor->second;
+ }
+
+ if (create) {
+ ++stats.subdirCreate;
+
+ auto* entry = new DirectoryEntry(std::wstring(name.begin(), name.end()), this,
+ originID, m_FileRegister, m_OriginConnection);
+
+ elapsed(stats.addDirectoryTimes, [&] {
+ addDirectoryToList(entry, std::move(nameLc));
+ // nameLc is moved from this point
+ });
+
+ return entry;
+ } else {
+ return nullptr;
+ }
+}
+
+DirectoryEntry* DirectoryEntry::getSubDirectory(env::Directory& dir, bool create,
+ DirectoryStats& stats, int originID)
+{
+ SubDirectoriesLookup::iterator itor;
+
+ std::scoped_lock lock(m_SubDirMutex);
+
+ elapsed(stats.subdirLookupTimes, [&] {
+ itor = m_SubDirectoriesLookup.find(dir.lcname);
+ });
+
+ if (itor != m_SubDirectoriesLookup.end()) {
+ ++stats.subdirExists;
+ return itor->second;
+ }
+
+ if (create) {
+ ++stats.subdirCreate;
+
+ auto* entry = new DirectoryEntry(std::move(dir.name), this, originID,
+ m_FileRegister, m_OriginConnection);
+ // dir.name is moved from this point
+
+ elapsed(stats.addDirectoryTimes, [&] {
+ addDirectoryToList(entry, std::move(dir.lcname));
+ });
+
+ // dir.lcname is moved from this point
+
+ return entry;
+ } else {
+ return nullptr;
+ }
+}
+
+DirectoryEntry* DirectoryEntry::getSubDirectoryRecursive(const std::wstring& path,
+ bool create,
+ DirectoryStats& stats,
+ int originID)
+{
+ if (path.length() == 0) {
+ // path ended with a backslash?
+ return this;
+ }
+
+ const size_t pos = path.find_first_of(L"\\/");
+
+ if (pos == std::wstring::npos) {
+ return getSubDirectory(path, create, stats);
+ } else {
+ DirectoryEntry* nextChild =
+ getSubDirectory(path.substr(0, pos), create, stats, originID);
+
+ if (nextChild == nullptr) {
+ return nullptr;
+ } else {
+ return nextChild->getSubDirectoryRecursive(path.substr(pos + 1), create, stats,
+ originID);
+ }
+ }
+}
+
+void DirectoryEntry::removeDirRecursive()
+{
+ while (!m_Files.empty()) {
+ m_FileRegister->removeFile(m_Files.begin()->second);
+ }
+
+ m_FilesLookup.clear();
+
+ for (DirectoryEntry* entry : m_SubDirectories) {
+ entry->removeDirRecursive();
+ delete entry;
+ }
+
+ m_SubDirectories.clear();
+ m_SubDirectoriesLookup.clear();
+}
+
+void DirectoryEntry::addDirectoryToList(DirectoryEntry* e, std::wstring nameLc)
+{
+ m_SubDirectories.insert(e);
+ m_SubDirectoriesLookup.emplace(std::move(nameLc), e);
+}
+
+void DirectoryEntry::removeDirectoryFromList(SubDirectories::iterator itor)
+{
+ const auto* entry = *itor;
+
+ {
+ auto itor2 = std::find_if(m_SubDirectoriesLookup.begin(),
+ m_SubDirectoriesLookup.end(), [&](auto&& d) {
+ return (d.second == entry);
+ });
+
+ if (itor2 == m_SubDirectoriesLookup.end()) {
+ log::error("entry {} not in sub directories map", entry->getName());
+ } else {
+ m_SubDirectoriesLookup.erase(itor2);
+ }
+ }
+
+ m_SubDirectories.erase(itor);
+}
+
+void DirectoryEntry::removeFileFromList(FileIndex index)
+{
+ auto removeFrom = [&](auto& list) {
+ auto iter = std::find_if(list.begin(), list.end(), [&index](auto&& pair) {
+ return (pair.second == index);
+ });
+
+ if (iter == list.end()) {
+ auto f = m_FileRegister->getFile(index);
+
+ if (f) {
+ log::error("can't remove file '{}', not in directory entry '{}'", f->getName(),
+ getName());
+ } else {
+ log::error("can't remove file with index {}, not in directory entry '{}' and "
+ "not in register",
+ index, getName());
+ }
+ } else {
+ list.erase(iter);
+ }
+ };
+
+ removeFrom(m_FilesLookup);
+ removeFrom(m_Files);
+}
+
+void DirectoryEntry::removeFilesFromList(const std::set<FileIndex>& indices)
+{
+ for (auto iter = m_Files.begin(); iter != m_Files.end();) {
+ if (indices.find(iter->second) != indices.end()) {
+ 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;
+ }
+ }
+}
+
+void DirectoryEntry::addFileToList(std::wstring fileNameLower, FileIndex index)
+{
+ m_FilesLookup.emplace(fileNameLower, index);
+ m_Files.emplace(std::move(fileNameLower), index);
+ // fileNameLower has been moved from this point
+}
+
+struct DumpFailed : public std::runtime_error
+{
+ using runtime_error::runtime_error;
+};
+
+void DirectoryEntry::dump(const std::wstring& file) const
+{
+ try {
+ std::FILE* f = nullptr;
+ auto e = _wfopen_s(&f, file.c_str(), L"wb");
+
+ if (e != 0 || !f) {
+ throw DumpFailed(fmt::format("failed to open, {} ({})", std::strerror(e), e));
+ }
+
+ Guard g([&] {
+ std::fclose(f);
+ });
+
+ dump(f, L"Data");
+ } catch (DumpFailed& e) {
+ log::error("failed to write list to '{}': {}",
+ QString::fromStdWString(file).toStdString(), e.what());
+ }
+}
+
+void DirectoryEntry::dump(std::FILE* f, const std::wstring& parentPath) const
+{
+ {
+ std::scoped_lock lock(m_FilesMutex);
+
+ for (auto&& index : m_Files) {
+ const auto file = m_FileRegister->getFile(index.second);
+ if (!file) {
+ continue;
+ }
+
+ if (file->isFromArchive()) {
+ // TODO: don't list files from archives. maybe make this an option?
+ continue;
+ }
+
+ const auto& o = m_OriginConnection->getByID(file->getOrigin());
+ const auto path = parentPath + L"\\" + file->getName();
+ const auto line = path + L"\t(" + o.getName() + L")\r\n";
+
+ const auto lineu8 = MOShared::ToString(line, true);
+
+ if (std::fwrite(lineu8.data(), lineu8.size(), 1, f) != 1) {
+ const auto e = errno;
+ throw DumpFailed(fmt::format("failed to write, {} ({})", std::strerror(e), e));
+ }
+ }
+ }
+
+ {
+ std::scoped_lock lock(m_SubDirMutex);
+ for (auto&& d : m_SubDirectories) {
+ const auto path = parentPath + L"\\" + d->m_Name;
+ d->dump(f, path);
+ }
+ }
+}
+
+} // namespace MOShared
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index 0ee3c919..42aef8c7 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -1,321 +1,284 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef MO_REGISTER_DIRECTORYENTRY_INCLUDED
-#define MO_REGISTER_DIRECTORYENTRY_INCLUDED
-
-#include "fileregister.h"
-#include <bsatk.h>
-
-namespace env
-{
- class DirectoryWalker;
- struct Directory;
- struct File;
-}
-
-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
-{
-
-struct DirCompareByName
-{
- bool operator()(const DirectoryEntry* a, const DirectoryEntry* b) const;
-};
-
-
-class DirectoryEntry
-{
-public:
- using SubDirectories = std::set<DirectoryEntry*, DirCompareByName>;
-
- DirectoryEntry(
- std::wstring name, DirectoryEntry* parent, OriginID originID);
-
- DirectoryEntry(
- std::wstring name, DirectoryEntry* parent, OriginID originID,
- boost::shared_ptr<FileRegister> fileRegister,
- boost::shared_ptr<OriginConnection> originConnection);
-
- ~DirectoryEntry();
-
- // noncopyable
- DirectoryEntry(const DirectoryEntry&) = delete;
- DirectoryEntry& operator=(const DirectoryEntry&) = delete;
-
- void clear();
-
- bool isPopulated() const
- {
- return m_Populated;
- }
-
- bool isTopLevel() const
- {
- return m_TopLevel;
- }
-
- bool isEmpty() const
- {
- return m_Files.empty() && m_SubDirectories.empty();
- }
-
- bool hasFiles() const
- {
- return !m_Files.empty();
- }
-
- const DirectoryEntry* getParent() const
- {
- return m_Parent;
- }
-
- // add files to this directory (and subdirectories) from the specified origin.
- // That origin may exist or not
- void addFromOrigin(
- const std::wstring& originName,
- const std::wstring& directory, int priority, DirectoryStats& stats);
-
- void addFromOrigin(
- env::DirectoryWalker& walker, const std::wstring& originName,
- const std::wstring& directory, int priority, DirectoryStats& stats);
-
- void addFromAllBSAs(
- const std::wstring& originName, const std::wstring& directory,
- int priority, const std::vector<std::wstring>& archives,
- const std::set<std::wstring>& enabledArchives,
- const std::vector<std::wstring>& loadOrder,
- DirectoryStats& stats);
-
- void addFromBSA(
- const std::wstring& originName, const std::wstring& directory,
- const std::wstring& archivePath, int priority, int order,
- DirectoryStats& stats);
-
- void addFromList(
- const std::wstring& originName, const std::wstring& directory,
- env::Directory& root, int priority, DirectoryStats& stats);
-
- void propagateOrigin(OriginID origin);
-
- const std::wstring& getName() const
- {
- return m_Name;
- }
-
- boost::shared_ptr<FileRegister> getFileRegister()
- {
- return m_FileRegister;
- }
-
- bool originExists(const std::wstring& name) const;
- FilesOrigin& getOriginByID(OriginID ID) const;
- FilesOrigin& getOriginByName(const std::wstring& name) const;
- const FilesOrigin* findOriginByID(OriginID ID) const;
-
- OriginID anyOrigin() const;
-
- std::vector<FileEntryPtr> getFiles() const;
-
- const SubDirectories& getSubDirectories() const
- {
- return m_SubDirectories;
- }
-
- template <class F>
- void forEachDirectory(F&& f) const
- {
- for (auto&& d : m_SubDirectories) {
- if (!f(*d)) {
- break;
- }
- }
- }
-
- template <class F>
- void forEachFile(F&& f) const
- {
- for (auto&& p : m_Files) {
- if (auto file=m_FileRegister->getFile(p.second)) {
- if (!f(*file)) {
- break;
- }
- }
- }
- }
-
- template <class F>
- void forEachFileIndex(F&& f) const
- {
- for (auto&& p : m_Files) {
- if (!f(p.second)) {
- break;
- }
- }
- }
-
- FileEntryPtr getFileByIndex(FileIndex index) const
- {
- return m_FileRegister->getFile(index);
- }
-
- DirectoryEntry* findSubDirectory(
- const std::wstring& name, bool alreadyLowerCase=false) const;
-
- DirectoryEntry* findSubDirectoryRecursive(const std::wstring& path);
-
- /** retrieve a file in this directory by name.
- * @param name name of the file
- * @return fileentry object for the file or nullptr if no file matches
- */
- const FileEntryPtr findFile(const std::wstring& name, bool alreadyLowerCase=false) const;
- const FileEntryPtr findFile(const DirectoryEntryFileKey& key) const;
-
- bool hasFile(const std::wstring& name) const;
- bool containsArchive(std::wstring archiveName);
-
- // search through this directory and all subdirectories for a file by the
- // specified name (relative path).
- //
- // if directory is not nullptr, the referenced variable will be set to the
- // path containing the file
- //
- const FileEntryPtr searchFile(
- const std::wstring& path, const DirectoryEntry** directory=nullptr) const;
-
- void removeFile(FileIndex index);
-
- // remove the specified file from the tree. This can be a path leading to a
- // file in a subdirectory
- bool removeFile(const std::wstring& filePath, OriginID* origin = nullptr);
-
- /**
- * @brief remove the specified directory
- * @param path directory to remove
- */
- void removeDir(const std::wstring& path);
-
- bool remove(const std::wstring& fileName, OriginID* origin);
-
- bool hasContentsFromOrigin(OriginID originID) const;
-
- FilesOrigin& createOrigin(
- const std::wstring& originName,
- const std::wstring& directory, int priority, DirectoryStats& stats);
-
- void removeFiles(const std::set<FileIndex>& indices);
-
- void dump(const std::wstring& file) const;
-
-private:
- using FilesMap = std::map<std::wstring, FileIndex>;
- using FilesLookup = std::unordered_map<DirectoryEntryFileKey, FileIndex>;
- using SubDirectoriesLookup = std::unordered_map<std::wstring, DirectoryEntry*>;
-
- boost::shared_ptr<FileRegister> m_FileRegister;
- boost::shared_ptr<OriginConnection> m_OriginConnection;
-
- std::wstring m_Name;
- FilesMap m_Files;
- FilesLookup m_FilesLookup;
- SubDirectories m_SubDirectories;
- SubDirectoriesLookup m_SubDirectoriesLookup;
-
- DirectoryEntry* m_Parent;
- std::set<OriginID> m_Origins;
- bool m_Populated;
- bool m_TopLevel;
- mutable std::mutex m_SubDirMutex;
- mutable std::mutex m_FilesMutex;
- mutable std::mutex m_OriginsMutex;
-
-
- FileEntryPtr insert(
- std::wstring_view fileName, FilesOrigin& origin, FILETIME fileTime,
- std::wstring_view archive, int order, DirectoryStats& stats);
-
- FileEntryPtr insert(
- env::File& file, FilesOrigin& origin,
- std::wstring_view archive, int order, DirectoryStats& stats);
-
- void addFiles(
- env::DirectoryWalker& walker, FilesOrigin& origin,
- const std::wstring& path, DirectoryStats& stats);
-
- void addFiles(
- FilesOrigin& origin, BSA::Folder::Ptr archiveFolder, FILETIME fileTime,
- const std::wstring& archiveName, int order, DirectoryStats& stats);
-
- void addDir(FilesOrigin& origin, env::Directory& d, DirectoryStats& stats);
-
- DirectoryEntry* getSubDirectory(
- std::wstring_view name, bool create, DirectoryStats& stats,
- OriginID originID = InvalidOriginID);
-
- DirectoryEntry* getSubDirectory(
- env::Directory& dir, bool create, DirectoryStats& stats,
- OriginID originID = InvalidOriginID);
-
- DirectoryEntry* getSubDirectoryRecursive(
- const std::wstring& path, bool create, DirectoryStats& stats,
- OriginID originID = InvalidOriginID);
-
- void removeDirRecursive();
-
- void addDirectoryToList(DirectoryEntry* e, std::wstring nameLc);
- void removeDirectoryFromList(SubDirectories::iterator itor);
-
- void addFileToList(std::wstring fileNameLower, FileIndex index);
- void removeFileFromList(FileIndex index);
- void removeFilesFromList(const std::set<FileIndex>& indices);
-
- struct Context;
- static void onDirectoryStart(Context* cx, std::wstring_view path);
- static void onDirectoryEnd(Context* cx, std::wstring_view path);
- static void onFile(Context* cx, std::wstring_view path, FILETIME ft);
-
- void dump(std::FILE* f, const std::wstring& parentPath) const;
-};
-
-} // namespace MOShared
-
-
-namespace std
-{
- hash<MOShared::DirectoryEntryFileKey>::result_type
- hash<MOShared::DirectoryEntryFileKey>::operator()(
- const argument_type& key) const
- {
- return key.hash;
- }
-}
-
-#endif // MO_REGISTER_DIRECTORYENTRY_INCLUDED
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MO_REGISTER_DIRECTORYENTRY_INCLUDED
+#define MO_REGISTER_DIRECTORYENTRY_INCLUDED
+
+#include "fileregister.h"
+#include <bsatk.h>
+
+namespace env
+{
+class DirectoryWalker;
+struct Directory;
+struct File;
+} // namespace env
+
+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 std
+
+namespace MOShared
+{
+
+struct DirCompareByName
+{
+ bool operator()(const DirectoryEntry* a, const DirectoryEntry* b) const;
+};
+
+class DirectoryEntry
+{
+public:
+ using SubDirectories = std::set<DirectoryEntry*, DirCompareByName>;
+
+ DirectoryEntry(std::wstring name, DirectoryEntry* parent, OriginID originID);
+
+ DirectoryEntry(std::wstring name, DirectoryEntry* parent, OriginID originID,
+ boost::shared_ptr<FileRegister> fileRegister,
+ boost::shared_ptr<OriginConnection> originConnection);
+
+ ~DirectoryEntry();
+
+ // noncopyable
+ DirectoryEntry(const DirectoryEntry&) = delete;
+ DirectoryEntry& operator=(const DirectoryEntry&) = delete;
+
+ void clear();
+
+ bool isPopulated() const { return m_Populated; }
+
+ bool isTopLevel() const { return m_TopLevel; }
+
+ bool isEmpty() const { return m_Files.empty() && m_SubDirectories.empty(); }
+
+ bool hasFiles() const { return !m_Files.empty(); }
+
+ const DirectoryEntry* getParent() const { return m_Parent; }
+
+ // add files to this directory (and subdirectories) from the specified origin.
+ // That origin may exist or not
+ void addFromOrigin(const std::wstring& originName, const std::wstring& directory,
+ int priority, DirectoryStats& stats);
+
+ void addFromOrigin(env::DirectoryWalker& walker, const std::wstring& originName,
+ const std::wstring& directory, int priority,
+ DirectoryStats& stats);
+
+ void addFromAllBSAs(const std::wstring& originName, const std::wstring& directory,
+ int priority, const std::vector<std::wstring>& archives,
+ const std::set<std::wstring>& enabledArchives,
+ const std::vector<std::wstring>& loadOrder,
+ DirectoryStats& stats);
+
+ void addFromBSA(const std::wstring& originName, const std::wstring& directory,
+ const std::wstring& archivePath, int priority, int order,
+ DirectoryStats& stats);
+
+ void addFromList(const std::wstring& originName, const std::wstring& directory,
+ env::Directory& root, int priority, DirectoryStats& stats);
+
+ void propagateOrigin(OriginID origin);
+
+ const std::wstring& getName() const { return m_Name; }
+
+ boost::shared_ptr<FileRegister> getFileRegister() { return m_FileRegister; }
+
+ bool originExists(const std::wstring& name) const;
+ FilesOrigin& getOriginByID(OriginID ID) const;
+ FilesOrigin& getOriginByName(const std::wstring& name) const;
+ const FilesOrigin* findOriginByID(OriginID ID) const;
+
+ OriginID anyOrigin() const;
+
+ std::vector<FileEntryPtr> getFiles() const;
+
+ const SubDirectories& getSubDirectories() const { return m_SubDirectories; }
+
+ template <class F>
+ void forEachDirectory(F&& f) const
+ {
+ for (auto&& d : m_SubDirectories) {
+ if (!f(*d)) {
+ break;
+ }
+ }
+ }
+
+ template <class F>
+ void forEachFile(F&& f) const
+ {
+ for (auto&& p : m_Files) {
+ if (auto file = m_FileRegister->getFile(p.second)) {
+ if (!f(*file)) {
+ break;
+ }
+ }
+ }
+ }
+
+ template <class F>
+ void forEachFileIndex(F&& f) const
+ {
+ for (auto&& p : m_Files) {
+ if (!f(p.second)) {
+ break;
+ }
+ }
+ }
+
+ FileEntryPtr getFileByIndex(FileIndex index) const
+ {
+ return m_FileRegister->getFile(index);
+ }
+
+ DirectoryEntry* findSubDirectory(const std::wstring& name,
+ bool alreadyLowerCase = false) const;
+
+ DirectoryEntry* findSubDirectoryRecursive(const std::wstring& path);
+
+ /** retrieve a file in this directory by name.
+ * @param name name of the file
+ * @return fileentry object for the file or nullptr if no file matches
+ */
+ const FileEntryPtr findFile(const std::wstring& name,
+ bool alreadyLowerCase = false) const;
+ const FileEntryPtr findFile(const DirectoryEntryFileKey& key) const;
+
+ bool hasFile(const std::wstring& name) const;
+ bool containsArchive(std::wstring archiveName);
+
+ // search through this directory and all subdirectories for a file by the
+ // specified name (relative path).
+ //
+ // if directory is not nullptr, the referenced variable will be set to the
+ // path containing the file
+ //
+ const FileEntryPtr searchFile(const std::wstring& path,
+ const DirectoryEntry** directory = nullptr) const;
+
+ void removeFile(FileIndex index);
+
+ // remove the specified file from the tree. This can be a path leading to a
+ // file in a subdirectory
+ bool removeFile(const std::wstring& filePath, OriginID* origin = nullptr);
+
+ /**
+ * @brief remove the specified directory
+ * @param path directory to remove
+ */
+ void removeDir(const std::wstring& path);
+
+ bool remove(const std::wstring& fileName, OriginID* origin);
+
+ bool hasContentsFromOrigin(OriginID originID) const;
+
+ FilesOrigin& createOrigin(const std::wstring& originName,
+ const std::wstring& directory, int priority,
+ DirectoryStats& stats);
+
+ void removeFiles(const std::set<FileIndex>& indices);
+
+ void dump(const std::wstring& file) const;
+
+private:
+ using FilesMap = std::map<std::wstring, FileIndex>;
+ using FilesLookup = std::unordered_map<DirectoryEntryFileKey, FileIndex>;
+ using SubDirectoriesLookup = std::unordered_map<std::wstring, DirectoryEntry*>;
+
+ boost::shared_ptr<FileRegister> m_FileRegister;
+ boost::shared_ptr<OriginConnection> m_OriginConnection;
+
+ std::wstring m_Name;
+ FilesMap m_Files;
+ FilesLookup m_FilesLookup;
+ SubDirectories m_SubDirectories;
+ SubDirectoriesLookup m_SubDirectoriesLookup;
+
+ DirectoryEntry* m_Parent;
+ std::set<OriginID> m_Origins;
+ bool m_Populated;
+ bool m_TopLevel;
+ mutable std::mutex m_SubDirMutex;
+ mutable std::mutex m_FilesMutex;
+ mutable std::mutex m_OriginsMutex;
+
+ FileEntryPtr insert(std::wstring_view fileName, FilesOrigin& origin,
+ FILETIME fileTime, std::wstring_view archive, int order,
+ DirectoryStats& stats);
+
+ FileEntryPtr insert(env::File& file, FilesOrigin& origin, std::wstring_view archive,
+ int order, DirectoryStats& stats);
+
+ void addFiles(env::DirectoryWalker& walker, FilesOrigin& origin,
+ const std::wstring& path, DirectoryStats& stats);
+
+ void addFiles(FilesOrigin& origin, BSA::Folder::Ptr archiveFolder, FILETIME fileTime,
+ const std::wstring& archiveName, int order, DirectoryStats& stats);
+
+ void addDir(FilesOrigin& origin, env::Directory& d, DirectoryStats& stats);
+
+ DirectoryEntry* getSubDirectory(std::wstring_view name, bool create,
+ DirectoryStats& stats,
+ OriginID originID = InvalidOriginID);
+
+ DirectoryEntry* getSubDirectory(env::Directory& dir, bool create,
+ DirectoryStats& stats,
+ OriginID originID = InvalidOriginID);
+
+ DirectoryEntry* getSubDirectoryRecursive(const std::wstring& path, bool create,
+ DirectoryStats& stats,
+ OriginID originID = InvalidOriginID);
+
+ void removeDirRecursive();
+
+ void addDirectoryToList(DirectoryEntry* e, std::wstring nameLc);
+ void removeDirectoryFromList(SubDirectories::iterator itor);
+
+ void addFileToList(std::wstring fileNameLower, FileIndex index);
+ void removeFileFromList(FileIndex index);
+ void removeFilesFromList(const std::set<FileIndex>& indices);
+
+ struct Context;
+ static void onDirectoryStart(Context* cx, std::wstring_view path);
+ static void onDirectoryEnd(Context* cx, std::wstring_view path);
+ static void onFile(Context* cx, std::wstring_view path, FILETIME ft);
+
+ void dump(std::FILE* f, const std::wstring& parentPath) const;
+};
+
+} // namespace MOShared
+
+namespace std
+{
+hash<MOShared::DirectoryEntryFileKey>::result_type
+hash<MOShared::DirectoryEntryFileKey>::operator()(const argument_type& key) const
+{
+ return key.hash;
+}
+} // namespace std
+
+#endif // MO_REGISTER_DIRECTORYENTRY_INCLUDED
diff --git a/src/shared/fileentry.cpp b/src/shared/fileentry.cpp
index 5be9a9dc..48670617 100644
--- a/src/shared/fileentry.cpp
+++ b/src/shared/fileentry.cpp
@@ -5,20 +5,18 @@
namespace MOShared
{
-FileEntry::FileEntry() :
- m_Index(InvalidFileIndex), m_Name(), m_Origin(-1), m_Parent(nullptr),
- m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize)
-{
-}
+FileEntry::FileEntry()
+ : m_Index(InvalidFileIndex), m_Name(), m_Origin(-1), m_Parent(nullptr),
+ m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize)
+{}
-FileEntry::FileEntry(FileIndex index, std::wstring name, DirectoryEntry *parent) :
- m_Index(index), m_Name(std::move(name)), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent),
- m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize)
-{
-}
+FileEntry::FileEntry(FileIndex index, std::wstring name, DirectoryEntry* parent)
+ : m_Index(index), m_Name(std::move(name)), m_Origin(-1), m_Archive(L"", -1),
+ m_Parent(parent), m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize)
+{}
-void FileEntry::addOrigin(
- OriginID origin, FILETIME fileTime, std::wstring_view archive, int order)
+void FileEntry::addOrigin(OriginID origin, FILETIME fileTime, std::wstring_view archive,
+ int order)
{
std::scoped_lock lock(m_OriginsMutex);
@@ -29,32 +27,30 @@ void FileEntry::addOrigin(
if (m_Origin == -1) {
// If this file has no previous origin, this mod is now the origin with no
// alternatives
- m_Origin = origin;
+ m_Origin = origin;
m_FileTime = fileTime;
- m_Archive = DataArchiveOrigin(std::wstring(archive.begin(), archive.end()), order);
- }
- else if (
- (m_Parent != nullptr) && (
- (m_Parent->getOriginByID(origin).getPriority() > m_Parent->getOriginByID(m_Origin).getPriority()) ||
- (archive.size() == 0 && m_Archive.isValid()))
- ) {
+ m_Archive = DataArchiveOrigin(std::wstring(archive.begin(), archive.end()), order);
+ } else if ((m_Parent != nullptr) &&
+ ((m_Parent->getOriginByID(origin).getPriority() >
+ m_Parent->getOriginByID(m_Origin).getPriority()) ||
+ (archive.size() == 0 && m_Archive.isValid()))) {
// If this mod has a higher priority than the origin mod OR
// this mod has a loose file and the origin mod has an archived file,
// this mod is now the origin and the previous origin is the first alternative
- auto itor = std::find_if(
- m_Alternatives.begin(), m_Alternatives.end(),
- [&](auto&& i) { return i.originID() == m_Origin; });
+ auto itor =
+ std::find_if(m_Alternatives.begin(), m_Alternatives.end(), [&](auto&& i) {
+ return i.originID() == m_Origin;
+ });
if (itor == m_Alternatives.end()) {
m_Alternatives.push_back({m_Origin, m_Archive});
}
- m_Origin = origin;
+ m_Origin = origin;
m_FileTime = fileTime;
- m_Archive = DataArchiveOrigin(std::wstring(archive.begin(), archive.end()), order);
- }
- else {
+ m_Archive = DataArchiveOrigin(std::wstring(archive.begin(), archive.end()), order);
+ } else {
// This mod is just an alternative
bool found = false;
@@ -70,15 +66,18 @@ void FileEntry::addOrigin(
}
if ((m_Parent != nullptr) &&
- (m_Parent->getOriginByID(iter->originID()).getPriority() < m_Parent->getOriginByID(origin).getPriority())) {
- m_Alternatives.insert(iter, {origin, {std::wstring(archive.begin(), archive.end()), order}});
+ (m_Parent->getOriginByID(iter->originID()).getPriority() <
+ m_Parent->getOriginByID(origin).getPriority())) {
+ m_Alternatives.insert(
+ iter, {origin, {std::wstring(archive.begin(), archive.end()), order}});
found = true;
break;
}
}
if (!found) {
- m_Alternatives.push_back({origin, {std::wstring(archive.begin(), archive.end()), order}});
+ m_Alternatives.push_back(
+ {origin, {std::wstring(archive.begin(), archive.end()), order}});
}
}
}
@@ -93,21 +92,21 @@ bool FileEntry::removeOrigin(OriginID origin)
auto currentIter = m_Alternatives.begin();
for (auto iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) {
if (iter->originID() != origin) {
- //Both files are not from archives.
+ // Both files are not from archives.
if (!iter->isFromArchive() && !currentIter->isFromArchive()) {
- if ((m_Parent->getOriginByID(iter->originID()).getPriority() > m_Parent->getOriginByID(currentIter->originID()).getPriority())) {
+ if ((m_Parent->getOriginByID(iter->originID()).getPriority() >
+ m_Parent->getOriginByID(currentIter->originID()).getPriority())) {
currentIter = iter;
}
- }
- else {
- //Both files are from archives
+ } else {
+ // Both files are from archives
if (iter->isFromArchive() && currentIter->isFromArchive()) {
if (iter->archive().order() > currentIter->archive().order()) {
currentIter = iter;
}
- }
- else {
- //Only one of the two is an archive, so we change currentIter only if he is the archive one.
+ } else {
+ // Only one of the two is an archive, so we change currentIter only if he
+ // is the archive one.
if (currentIter->isFromArchive()) {
currentIter = iter;
}
@@ -117,19 +116,20 @@ bool FileEntry::removeOrigin(OriginID origin)
}
OriginID currentID = currentIter->originID();
- m_Archive = currentIter->archive();
+ m_Archive = currentIter->archive();
m_Alternatives.erase(currentIter);
m_Origin = currentID;
} else {
- m_Origin = -1;
+ m_Origin = -1;
m_Archive = DataArchiveOrigin(L"", -1);
return true;
}
} else {
- auto newEnd = std::remove_if(
- m_Alternatives.begin(), m_Alternatives.end(),
- [&](auto &i) { return i.originID() == origin; });
+ auto newEnd =
+ std::remove_if(m_Alternatives.begin(), m_Alternatives.end(), [&](auto& i) {
+ return i.originID() == origin;
+ });
if (newEnd != m_Alternatives.end()) {
m_Alternatives.erase(newEnd, m_Alternatives.end());
@@ -160,8 +160,12 @@ void FileEntry::sortOrigins()
}
if (LHS.isFromArchive() && RHS.isFromArchive()) {
- int l = LHS.archive().order(); if (l < 0) l = INT_MAX;
- int r = RHS.archive().order(); if (r < 0) r = INT_MAX;
+ int l = LHS.archive().order();
+ if (l < 0)
+ l = INT_MAX;
+ int r = RHS.archive().order();
+ if (r < 0)
+ r = INT_MAX;
return l < r;
}
@@ -171,10 +175,10 @@ void FileEntry::sortOrigins()
}
return true;
- });
+ });
if (!m_Alternatives.empty()) {
- m_Origin = m_Alternatives.back().originID();
+ m_Origin = m_Alternatives.back().originID();
m_Archive = m_Alternatives.back().archive();
m_Alternatives.pop_back();
}
@@ -207,7 +211,7 @@ std::wstring FileEntry::getFullPath(OriginID originID) const
if (originID == InvalidOriginID) {
bool ignore = false;
- originID = getOrigin(ignore);
+ originID = getOrigin(ignore);
}
// base directory for origin
@@ -234,7 +238,7 @@ std::wstring FileEntry::getRelativePath() const
return result + L"\\" + m_Name;
}
-bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) const
+bool FileEntry::recurseParents(std::wstring& path, const DirectoryEntry* parent) const
{
if (parent == nullptr) {
return false;
@@ -248,4 +252,4 @@ bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent)
}
}
-} // namespace
+} // namespace MOShared
diff --git a/src/shared/fileentry.h b/src/shared/fileentry.h
index 31655bb1..80d8b292 100644
--- a/src/shared/fileentry.h
+++ b/src/shared/fileentry.h
@@ -9,23 +9,19 @@ namespace MOShared
class FileEntry
{
public:
- static constexpr uint64_t NoFileSize =
- std::numeric_limits<uint64_t>::max();
+ static constexpr uint64_t NoFileSize = std::numeric_limits<uint64_t>::max();
FileEntry();
- FileEntry(FileIndex index, std::wstring name, DirectoryEntry *parent);
+ FileEntry(FileIndex index, std::wstring name, DirectoryEntry* parent);
// noncopyable
- FileEntry(const FileEntry&) = delete;
+ FileEntry(const FileEntry&) = delete;
FileEntry& operator=(const FileEntry&) = delete;
- FileIndex getIndex() const
- {
- return m_Index;
- }
+ FileIndex getIndex() const { return m_Index; }
- void addOrigin(
- OriginID origin, FILETIME fileTime, std::wstring_view archive, int order);
+ void addOrigin(OriginID origin, FILETIME fileTime, std::wstring_view archive,
+ int order);
// remove the specified origin from the list of origins that contain this
// file. if no origin is left, the file is effectively deleted and true is
@@ -37,71 +33,44 @@ public:
// gets the list of alternative origins (origins with lower priority than
// the primary one). if sortOrigins has been called, it is sorted by priority
// (ascending)
- const AlternativesVector &getAlternatives() const
- {
- return m_Alternatives;
- }
+ const AlternativesVector& getAlternatives() const { return m_Alternatives; }
- const std::wstring &getName() const
- {
- return m_Name;
- }
+ const std::wstring& getName() const { return m_Name; }
- OriginID getOrigin() const
- {
- return m_Origin;
- }
+ OriginID getOrigin() const { return m_Origin; }
- OriginID getOrigin(bool &archive) const
+ OriginID getOrigin(bool& archive) const
{
archive = m_Archive.isValid();
return m_Origin;
}
- const DataArchiveOrigin &getArchive() const
- {
- return m_Archive;
- }
+ const DataArchiveOrigin& getArchive() const { return m_Archive; }
bool isFromArchive(std::wstring archiveName = L"") const;
// if originID is -1, uses the main origin; if this file doesn't exist in the
// given origin, returns an empty string
//
- std::wstring getFullPath(OriginID originID=InvalidOriginID) const;
+ std::wstring getFullPath(OriginID originID = InvalidOriginID) const;
std::wstring getRelativePath() const;
- DirectoryEntry *getParent()
- {
- return m_Parent;
- }
+ DirectoryEntry* getParent() { return m_Parent; }
- void setFileTime(FILETIME fileTime) const
- {
- m_FileTime = fileTime;
- }
+ void setFileTime(FILETIME fileTime) const { m_FileTime = fileTime; }
- FILETIME getFileTime() const
- {
- return m_FileTime;
- }
+ FILETIME getFileTime() const { return m_FileTime; }
void setFileSize(uint64_t size, uint64_t compressedSize)
{
- m_FileSize = size;
+ m_FileSize = size;
m_CompressedFileSize = compressedSize;
}
- uint64_t getFileSize() const
- {
- return m_FileSize;
- }
+ uint64_t getFileSize() const { return m_FileSize; }
- uint64_t getCompressedFileSize() const
- {
- return m_CompressedFileSize;
- }
+ uint64_t getCompressedFileSize() const { return m_CompressedFileSize; }
private:
FileIndex m_Index;
@@ -109,14 +78,14 @@ private:
OriginID m_Origin;
DataArchiveOrigin m_Archive;
AlternativesVector m_Alternatives;
- DirectoryEntry *m_Parent;
+ DirectoryEntry* m_Parent;
mutable FILETIME m_FileTime;
uint64_t m_FileSize, m_CompressedFileSize;
mutable std::mutex m_OriginsMutex;
- bool recurseParents(std::wstring &path, const DirectoryEntry *parent) const;
+ bool recurseParents(std::wstring& path, const DirectoryEntry* parent) const;
};
-} // namespace
+} // namespace MOShared
-#endif // MO_REGISTER_FILEENTRY_INCLUDED
+#endif // MO_REGISTER_FILEENTRY_INCLUDED
diff --git a/src/shared/fileregister.cpp b/src/shared/fileregister.cpp
index 98e49452..ab94367f 100644
--- a/src/shared/fileregister.cpp
+++ b/src/shared/fileregister.cpp
@@ -1,8 +1,8 @@
#include "fileregister.h"
-#include "fileentry.h"
#include "directoryentry.h"
-#include "originconnection.h"
+#include "fileentry.h"
#include "filesorigin.h"
+#include "originconnection.h"
#include <log.h>
namespace MOShared
@@ -11,9 +11,8 @@ namespace MOShared
using namespace MOBase;
FileRegister::FileRegister(boost::shared_ptr<OriginConnection> originConnection)
- : m_OriginConnection(originConnection), m_NextIndex(0)
-{
-}
+ : m_OriginConnection(originConnection), m_NextIndex(0)
+{}
bool FileRegister::indexValid(FileIndex index) const
{
@@ -26,11 +25,11 @@ bool FileRegister::indexValid(FileIndex index) const
return false;
}
-FileEntryPtr FileRegister::createFile(
- std::wstring name, DirectoryEntry *parent, DirectoryStats& stats)
+FileEntryPtr FileRegister::createFile(std::wstring name, DirectoryEntry* parent,
+ DirectoryStats& stats)
{
const auto index = generateIndex();
- auto p = FileEntryPtr(new FileEntry(index, std::move(name), parent));
+ auto p = FileEntryPtr(new FileEntry(index, std::move(name), parent));
{
std::scoped_lock lock(m_Mutex);
@@ -96,18 +95,19 @@ void FileRegister::removeOrigin(FileIndex index, OriginID originID)
}
}
- log::error(QObject::tr("invalid file index for remove (for origin): {}").toStdString(), index);
+ log::error(
+ QObject::tr("invalid file index for remove (for origin): {}").toStdString(),
+ index);
}
-void FileRegister::removeOriginMulti(
- std::set<FileIndex> indices, OriginID originID)
+void FileRegister::removeOriginMulti(std::set<FileIndex> indices, OriginID originID)
{
std::vector<FileEntryPtr> removedFiles;
{
std::scoped_lock lock(m_Mutex);
- for (auto iter = indices.begin(); iter != indices.end(); ) {
+ for (auto iter = indices.begin(); iter != indices.end();) {
const auto index = *iter;
if (index < m_Files.size()) {
@@ -140,13 +140,13 @@ void FileRegister::removeOriginMulti(
// frequently the case
std::set<DirectoryEntry*> parents;
- for (const FileEntryPtr &file : removedFiles) {
+ for (const FileEntryPtr& file : removedFiles) {
if (file->getParent() != nullptr) {
parents.insert(file->getParent());
}
}
- for (DirectoryEntry *parent : parents) {
+ for (DirectoryEntry* parent : parents) {
parent->removeFiles(indices);
}
}
@@ -181,4 +181,4 @@ void FileRegister::unregisterFile(FileEntryPtr file)
}
}
-} // namespace
+} // namespace MOShared
diff --git a/src/shared/fileregister.h b/src/shared/fileregister.h
index c27dc93b..aeb2c5b7 100644
--- a/src/shared/fileregister.h
+++ b/src/shared/fileregister.h
@@ -2,8 +2,8 @@
#define MO_REGISTER_FILESREGISTER_INCLUDED
#include "fileregisterfwd.h"
-#include <mutex>
#include <boost/shared_ptr.hpp>
+#include <mutex>
namespace MOShared
{
@@ -14,13 +14,13 @@ public:
FileRegister(boost::shared_ptr<OriginConnection> originConnection);
// noncopyable
- FileRegister(const FileRegister&) = delete;
+ FileRegister(const FileRegister&) = delete;
FileRegister& operator=(const FileRegister&) = delete;
bool indexValid(FileIndex index) const;
- FileEntryPtr createFile(
- std::wstring name, DirectoryEntry *parent, DirectoryStats& stats);
+ FileEntryPtr createFile(std::wstring name, DirectoryEntry* parent,
+ DirectoryStats& stats);
FileEntryPtr getFile(FileIndex index) const;
@@ -48,6 +48,6 @@ private:
FileIndex generateIndex();
};
-} // namespace
+} // namespace MOShared
-#endif // MO_REGISTER_FILESREGISTER_INCLUDED
+#endif // MO_REGISTER_FILESREGISTER_INCLUDED
diff --git a/src/shared/fileregisterfwd.h b/src/shared/fileregisterfwd.h
index 57b3715a..dce40ab5 100644
--- a/src/shared/fileregisterfwd.h
+++ b/src/shared/fileregisterfwd.h
@@ -8,15 +8,9 @@ namespace MOShared
struct DirectoryEntryFileKey
{
- DirectoryEntryFileKey(std::wstring v)
- : value(std::move(v)), hash(getHash(value))
- {
- }
+ DirectoryEntryFileKey(std::wstring v) : value(std::move(v)), hash(getHash(value)) {}
- bool operator==(const DirectoryEntryFileKey& o) const
- {
- return (value == o.value);
- }
+ bool operator==(const DirectoryEntryFileKey& o) const { return (value == o.value); }
static std::size_t getHash(const std::wstring& value)
{
@@ -27,7 +21,6 @@ struct DirectoryEntryFileKey
const std::size_t hash;
};
-
class DirectoryEntry;
class OriginConnection;
class FileRegister;
@@ -36,11 +29,11 @@ class FileEntry;
struct DirectoryStats;
using FileEntryPtr = boost::shared_ptr<FileEntry>;
-using FileIndex = unsigned int;
-using OriginID = int;
+using FileIndex = unsigned int;
+using OriginID = int;
constexpr FileIndex InvalidFileIndex = UINT_MAX;
-constexpr OriginID InvalidOriginID = -1;
+constexpr OriginID InvalidOriginID = -1;
// if a file is in an archive, name is the name of the bsa and order
// is the order of the associated plugin in the plugins list
@@ -49,19 +42,17 @@ constexpr OriginID InvalidOriginID = -1;
class DataArchiveOrigin
{
std::wstring name_ = L"";
- int order_ = -1;
-
-public:
+ int order_ = -1;
+public:
int order() const { return order_; }
const std::wstring& name() const { return name_; }
-
- bool isValid() const {
- return name_.size() > 0;
- }
+
+ bool isValid() const { return name_.size() > 0; }
DataArchiveOrigin(std::wstring name, int order)
- : name_(std::move(name)), order_(order) {}
+ : name_(std::move(name)), order_(order)
+ {}
DataArchiveOrigin() = default;
};
@@ -72,18 +63,16 @@ class FileAlternative
DataArchiveOrigin archive_;
public:
-
OriginID originID() const { return originID_; }
const DataArchiveOrigin& archive() const { return archive_; }
- bool isFromArchive() const {
- return archive_.isValid();
- }
+ bool isFromArchive() const { return archive_.isValid(); }
FileAlternative() = default;
- FileAlternative(OriginID originID, DataArchiveOrigin archive)
- : originID_(originID), archive_(std::move(archive)) {}
+ FileAlternative(OriginID originID, DataArchiveOrigin archive)
+ : originID_(originID), archive_(std::move(archive))
+ {}
};
using AlternativesVector = std::vector<FileAlternative>;
@@ -127,6 +116,6 @@ struct DirectoryStats
std::string toCsv() const;
};
-} // namespace
+} // namespace MOShared
-#endif // MO_REGISTER_FILEREGISTERFWD_INCLUDED
+#endif // MO_REGISTER_FILEREGISTERFWD_INCLUDED
diff --git a/src/shared/filesorigin.cpp b/src/shared/filesorigin.cpp
index 049e4258..dd1aec61 100644
--- a/src/shared/filesorigin.cpp
+++ b/src/shared/filesorigin.cpp
@@ -1,12 +1,12 @@
#include "filesorigin.h"
-#include "originconnection.h"
-#include "fileregister.h"
#include "fileentry.h"
+#include "fileregister.h"
+#include "originconnection.h"
namespace MOShared
{
-std::wstring tail(const std::wstring &source, const size_t count)
+std::wstring tail(const std::wstring& source, const size_t count)
{
if (count >= source.length()) {
return source;
@@ -15,28 +15,24 @@ std::wstring tail(const std::wstring &source, const size_t count)
return source.substr(source.length() - count);
}
-
FilesOrigin::FilesOrigin()
- : m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0)
-{
-}
+ : m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0)
+{}
-FilesOrigin::FilesOrigin(
- OriginID ID, const std::wstring &name, const std::wstring &path, int priority,
- boost::shared_ptr<MOShared::FileRegister> fileRegister,
- boost::shared_ptr<MOShared::OriginConnection> originConnection) :
- m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path),
- m_Priority(priority), m_FileRegister(fileRegister),
- m_OriginConnection(originConnection)
-{
-}
+FilesOrigin::FilesOrigin(OriginID ID, const std::wstring& name,
+ const std::wstring& path, int priority,
+ boost::shared_ptr<MOShared::FileRegister> fileRegister,
+ boost::shared_ptr<MOShared::OriginConnection> originConnection)
+ : m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path), m_Priority(priority),
+ m_FileRegister(fileRegister), m_OriginConnection(originConnection)
+{}
void FilesOrigin::setPriority(int priority)
{
m_Priority = priority;
}
-void FilesOrigin::setName(const std::wstring &name)
+void FilesOrigin::setName(const std::wstring& name)
{
m_OriginConnection.lock()->changeNameLookup(m_Name, name);
@@ -121,4 +117,4 @@ bool FilesOrigin::containsArchive(std::wstring archiveName)
return false;
}
-} // namespace
+} // namespace MOShared
diff --git a/src/shared/filesorigin.h b/src/shared/filesorigin.h
index 1aa4c645..92fe12ef 100644
--- a/src/shared/filesorigin.h
+++ b/src/shared/filesorigin.h
@@ -12,40 +12,26 @@ class FilesOrigin
public:
FilesOrigin();
- FilesOrigin(
- OriginID ID, const std::wstring &name, const std::wstring &path,
- int priority,
- boost::shared_ptr<FileRegister> fileRegister,
- boost::shared_ptr<OriginConnection> originConnection);
+ FilesOrigin(OriginID ID, const std::wstring& name, const std::wstring& path,
+ int priority, boost::shared_ptr<FileRegister> fileRegister,
+ boost::shared_ptr<OriginConnection> originConnection);
// noncopyable
- FilesOrigin(const FilesOrigin&) = delete;
+ FilesOrigin(const FilesOrigin&) = delete;
FilesOrigin& operator=(const FilesOrigin&) = delete;
// sets priority for this origin (does not automatically refresh
// the structure)
void setPriority(int priority);
- int getPriority() const
- {
- return m_Priority;
- }
+ int getPriority() const { return m_Priority; }
- void setName(const std::wstring &name);
- const std::wstring &getName() const
- {
- return m_Name;
- }
+ void setName(const std::wstring& name);
+ const std::wstring& getName() const { return m_Name; }
- OriginID getID() const
- {
- return m_ID;
- }
+ OriginID getID() const { return m_ID; }
- const std::wstring &getPath() const
- {
- return m_Path;
- }
+ const std::wstring& getPath() const { return m_Path; }
std::vector<FileEntryPtr> getFiles() const;
FileEntryPtr findFile(FileIndex index) const;
@@ -53,10 +39,7 @@ public:
void enable(bool enabled, DirectoryStats& stats);
void enable(bool enabled);
- bool isDisabled() const
- {
- return m_Disabled;
- }
+ bool isDisabled() const { return m_Disabled; }
void addFile(FileIndex index)
{
@@ -80,6 +63,6 @@ private:
mutable std::mutex m_Mutex;
};
-} // namespace
+} // namespace MOShared
-#endif // MO_REGISTER_FILESORIGIN_INCLUDED
+#endif // MO_REGISTER_FILESORIGIN_INCLUDED
diff --git a/src/shared/originconnection.cpp b/src/shared/originconnection.cpp
index 2830e8e3..e433891c 100644
--- a/src/shared/originconnection.cpp
+++ b/src/shared/originconnection.cpp
@@ -8,24 +8,20 @@ namespace MOShared
using namespace MOBase;
-OriginConnection::OriginConnection()
- : m_NextID(0)
-{
-}
+OriginConnection::OriginConnection() : m_NextID(0) {}
std::pair<FilesOrigin&, bool> OriginConnection::getOrCreate(
- const std::wstring &originName, const std::wstring &directory, int priority,
- const boost::shared_ptr<FileRegister>& fileRegister,
- const boost::shared_ptr<OriginConnection>& originConnection,
- DirectoryStats& stats)
+ const std::wstring& originName, const std::wstring& directory, int priority,
+ const boost::shared_ptr<FileRegister>& fileRegister,
+ const boost::shared_ptr<OriginConnection>& originConnection, DirectoryStats& stats)
{
std::unique_lock lock(m_Mutex);
auto itor = m_OriginsNameMap.find(originName);
if (itor == m_OriginsNameMap.end()) {
- FilesOrigin& origin = createOriginNoLock(
- originName, directory, priority, fileRegister, originConnection);
+ FilesOrigin& origin = createOriginNoLock(originName, directory, priority,
+ fileRegister, originConnection);
return {origin, true};
} else {
@@ -37,18 +33,19 @@ std::pair<FilesOrigin&, bool> OriginConnection::getOrCreate(
}
}
-FilesOrigin& OriginConnection::createOrigin(
- const std::wstring &originName, const std::wstring &directory, int priority,
- boost::shared_ptr<FileRegister> fileRegister,
- boost::shared_ptr<OriginConnection> originConnection)
+FilesOrigin&
+OriginConnection::createOrigin(const std::wstring& originName,
+ const std::wstring& directory, int priority,
+ boost::shared_ptr<FileRegister> fileRegister,
+ boost::shared_ptr<OriginConnection> originConnection)
{
std::scoped_lock lock(m_Mutex);
- return createOriginNoLock(
- originName, directory, priority, fileRegister, originConnection);
+ return createOriginNoLock(originName, directory, priority, fileRegister,
+ originConnection);
}
-bool OriginConnection::exists(const std::wstring &name)
+bool OriginConnection::exists(const std::wstring& name)
{
std::scoped_lock lock(m_Mutex);
return m_OriginsNameMap.find(name) != m_OriginsNameMap.end();
@@ -73,7 +70,7 @@ const FilesOrigin* OriginConnection::findByID(OriginID ID) const
}
}
-FilesOrigin& OriginConnection::getByName(const std::wstring &name)
+FilesOrigin& OriginConnection::getByName(const std::wstring& name)
{
std::scoped_lock lock(m_Mutex);
@@ -83,12 +80,14 @@ FilesOrigin& OriginConnection::getByName(const std::wstring &name)
return m_Origins[iter->second];
} else {
std::ostringstream stream;
- stream << QObject::tr("invalid origin name: ").toStdString() << ToString(name, true);
+ stream << QObject::tr("invalid origin name: ").toStdString()
+ << ToString(name, true);
throw std::runtime_error(stream.str());
}
}
-void OriginConnection::changeNameLookup(const std::wstring &oldName, const std::wstring &newName)
+void OriginConnection::changeNameLookup(const std::wstring& oldName,
+ const std::wstring& newName)
{
std::scoped_lock lock(m_Mutex);
@@ -99,7 +98,8 @@ void OriginConnection::changeNameLookup(const std::wstring &oldName, const std::
m_OriginsNameMap.erase(iter);
m_OriginsNameMap[newName] = idx;
} else {
- log::error(QObject::tr("failed to change name lookup from {} to {}").toStdString(), oldName, newName);
+ log::error(QObject::tr("failed to change name lookup from {} to {}").toStdString(),
+ oldName, newName);
}
}
@@ -109,23 +109,21 @@ OriginID OriginConnection::createID()
}
FilesOrigin& OriginConnection::createOriginNoLock(
- const std::wstring &originName, const std::wstring &directory, int priority,
- boost::shared_ptr<FileRegister> fileRegister,
- boost::shared_ptr<OriginConnection> originConnection)
+ const std::wstring& originName, const std::wstring& directory, int priority,
+ boost::shared_ptr<FileRegister> fileRegister,
+ boost::shared_ptr<OriginConnection> originConnection)
{
OriginID newID = createID();
- auto itor = m_Origins.emplace(
- std::piecewise_construct,
- std::forward_as_tuple(newID),
- std::forward_as_tuple(
- newID, originName, directory, priority,
- fileRegister, originConnection))
- .first;
+ auto itor = m_Origins
+ .emplace(std::piecewise_construct, std::forward_as_tuple(newID),
+ std::forward_as_tuple(newID, originName, directory, priority,
+ fileRegister, originConnection))
+ .first;
m_OriginsNameMap.insert({originName, newID});
return itor->second;
}
-} // namespace
+} // namespace MOShared
diff --git a/src/shared/originconnection.h b/src/shared/originconnection.h
index ef8692bb..d8a67648 100644
--- a/src/shared/originconnection.h
+++ b/src/shared/originconnection.h
@@ -12,29 +12,29 @@ public:
OriginConnection();
// noncopyable
- OriginConnection(const OriginConnection&) = delete;
+ OriginConnection(const OriginConnection&) = delete;
OriginConnection& operator=(const OriginConnection&) = delete;
- std::pair<FilesOrigin&, bool> getOrCreate(
- const std::wstring &originName, const std::wstring &directory, int priority,
- const boost::shared_ptr<FileRegister>& fileRegister,
- const boost::shared_ptr<OriginConnection>& originConnection,
- DirectoryStats& stats);
+ std::pair<FilesOrigin&, bool>
+ getOrCreate(const std::wstring& originName, const std::wstring& directory,
+ int priority, const boost::shared_ptr<FileRegister>& fileRegister,
+ const boost::shared_ptr<OriginConnection>& originConnection,
+ DirectoryStats& stats);
- FilesOrigin& createOrigin(
- const std::wstring &originName, const std::wstring &directory, int priority,
- boost::shared_ptr<FileRegister> fileRegister,
- boost::shared_ptr<OriginConnection> originConnection);
+ FilesOrigin& createOrigin(const std::wstring& originName,
+ const std::wstring& directory, int priority,
+ boost::shared_ptr<FileRegister> fileRegister,
+ boost::shared_ptr<OriginConnection> originConnection);
- bool exists(const std::wstring &name);
+ bool exists(const std::wstring& name);
- FilesOrigin &getByID(OriginID ID);
+ FilesOrigin& getByID(OriginID ID);
const FilesOrigin* findByID(OriginID ID) const;
- FilesOrigin &getByName(const std::wstring &name);
+ FilesOrigin& getByName(const std::wstring& name);
void changePriorityLookup(int oldPriority, int newPriority);
- void changeNameLookup(const std::wstring &oldName, const std::wstring &newName);
+ void changeNameLookup(const std::wstring& oldName, const std::wstring& newName);
private:
std::atomic<OriginID> m_NextID;
@@ -44,12 +44,12 @@ private:
OriginID createID();
- FilesOrigin& createOriginNoLock(
- const std::wstring &originName, const std::wstring &directory, int priority,
- boost::shared_ptr<FileRegister> fileRegister,
- boost::shared_ptr<OriginConnection> originConnection);
+ FilesOrigin& createOriginNoLock(const std::wstring& originName,
+ const std::wstring& directory, int priority,
+ boost::shared_ptr<FileRegister> fileRegister,
+ boost::shared_ptr<OriginConnection> originConnection);
};
-} // namespace
+} // namespace MOShared
-#endif // MO_REGISTER_ORIGINCONNECTION_INCLUDED
+#endif // MO_REGISTER_ORIGINCONNECTION_INCLUDED
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 54c6b841..c3b4d7aa 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -1,444 +1,429 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "util.h"
-#include "windows_error.h"
-#include "../mainwindow.h"
-#include "../env.h"
-#include <log.h>
-#include <usvfs.h>
-#include <usvfs_version.h>
-
-using namespace MOBase;
-
-namespace MOShared
-{
-
-bool FileExists(const std::string &filename)
-{
- DWORD dwAttrib = ::GetFileAttributesA(filename.c_str());
-
- return (dwAttrib != INVALID_FILE_ATTRIBUTES);
-}
-
-bool FileExists(const std::wstring &filename)
-{
- DWORD dwAttrib = ::GetFileAttributesW(filename.c_str());
-
- return (dwAttrib != INVALID_FILE_ATTRIBUTES);
-}
-
-bool FileExists(const std::wstring &searchPath, const std::wstring &filename)
-{
- std::wstringstream stream;
- stream << searchPath << "\\" << filename;
- return FileExists(stream.str());
-}
-
-std::string ToString(const std::wstring &source, bool utf8)
-{
- std::string result;
- if (source.length() > 0) {
- UINT codepage = CP_UTF8;
- if (!utf8) {
- codepage = AreFileApisANSI() ? GetACP() : GetOEMCP();
- }
- int sizeRequired = ::WideCharToMultiByte(codepage, 0, &source[0], -1, nullptr, 0, nullptr, nullptr);
- if (sizeRequired == 0) {
- throw windows_error("failed to convert string to multibyte");
- }
- // the size returned by WideCharToMultiByte contains zero termination IF -1 is specified for the length.
- // we don't want that \0 in the string because then the length field would be wrong. Because madness
- result.resize(sizeRequired - 1, '\0');
- ::WideCharToMultiByte(codepage, 0, &source[0], (int)source.size(), &result[0], sizeRequired, nullptr, nullptr);
- }
-
- return result;
-}
-
-std::wstring ToWString(const std::string &source, bool utf8)
-{
- std::wstring result;
- if (source.length() > 0) {
- UINT codepage = CP_UTF8;
- if (!utf8) {
- codepage = AreFileApisANSI() ? GetACP() : GetOEMCP();
- }
- int sizeRequired
- = ::MultiByteToWideChar(codepage, 0, source.c_str(),
- static_cast<int>(source.length()), nullptr, 0);
- if (sizeRequired == 0) {
- throw windows_error("failed to convert string to wide character");
- }
- result.resize(sizeRequired, L'\0');
- ::MultiByteToWideChar(codepage, 0, source.c_str(),
- static_cast<int>(source.length()), &result[0],
- sizeRequired);
- }
-
- return result;
-}
-
-static std::locale loc("");
-static auto locToLowerW = [] (wchar_t in) -> wchar_t {
- return std::tolower(in, loc);
-};
-
-static auto locToLower = [] (char in) -> char {
- return std::tolower(in, loc);
-};
-
-std::string& ToLowerInPlace(std::string& text)
-{
- CharLowerBuffA(const_cast<CHAR *>(text.c_str()), static_cast<DWORD>(text.size()));
- return text;
-}
-
-std::string ToLowerCopy(const std::string& text)
-{
- std::string result(text);
- CharLowerBuffA(const_cast<CHAR *>(result.c_str()), static_cast<DWORD>(result.size()));
- return result;
-}
-
-std::wstring& ToLowerInPlace(std::wstring& text)
-{
- CharLowerBuffW(const_cast<WCHAR *>(text.c_str()), static_cast<DWORD>(text.size()));
- return text;
-}
-
-std::wstring ToLowerCopy(const std::wstring& text)
-{
- std::wstring result(text);
- CharLowerBuffW(const_cast<WCHAR *>(result.c_str()), static_cast<DWORD>(result.size()));
- return result;
-}
-
-std::wstring ToLowerCopy(std::wstring_view text)
-{
- std::wstring result(text.begin(), text.end());
- ToLowerInPlace(result);
- return result;
-}
-
-bool CaseInsenstiveComparePred(wchar_t lhs, wchar_t rhs)
-{
- return std::tolower(lhs, loc) == std::tolower(rhs, loc);
-}
-
-bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs)
-{
- return (lhs.length() == rhs.length())
- && std::equal(lhs.begin(), lhs.end(),
- rhs.begin(),
- [] (wchar_t lhs, wchar_t rhs) -> bool {
- return std::tolower(lhs, loc) == std::tolower(rhs, loc);
- });
-}
-
-VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName)
-{
- DWORD handle = 0UL;
- DWORD size = ::GetFileVersionInfoSizeW(fileName.c_str(), &handle);
- if (size == 0) {
- throw windows_error("failed to determine file version info size");
- }
-
- boost::scoped_array<char> buffer(new char[size]);
- try {
- handle = 0UL;
- if (!::GetFileVersionInfoW(fileName.c_str(), handle, size, buffer.get())) {
- throw windows_error("failed to determine file version info");
- }
-
- void *versionInfoPtr = nullptr;
- UINT versionInfoLength = 0;
- if (!::VerQueryValue(buffer.get(), L"\\", &versionInfoPtr, &versionInfoLength)) {
- throw windows_error("failed to determine file version");
- }
-
- VS_FIXEDFILEINFO result = *(VS_FIXEDFILEINFO*)versionInfoPtr;
- return result;
- } catch (...) {
- throw;
- }
-}
-
-std::wstring GetFileVersionString(const std::wstring &fileName)
-{
- DWORD handle = 0UL;
- DWORD size = ::GetFileVersionInfoSizeW(fileName.c_str(), &handle);
- if (size == 0) {
- throw windows_error("failed to determine file version info size");
- }
-
- boost::scoped_array<char> buffer(new char[size]);
- try {
- handle = 0UL;
- if (!::GetFileVersionInfoW(fileName.c_str(), handle, size, buffer.get())) {
- throw windows_error("failed to determine file version info");
- }
-
- LPVOID strBuffer = nullptr;
- UINT strLength = 0;
- if (!::VerQueryValue(buffer.get(), L"\\StringFileInfo\\040904B0\\ProductVersion", &strBuffer, &strLength)) {
- throw windows_error("failed to determine file version");
- }
-
- return std::wstring((LPCTSTR)strBuffer);
- }
- catch (...) {
- throw;
- }
-}
-
-VersionInfo createVersionInfo()
-{
- VS_FIXEDFILEINFO version = GetFileVersion(env::thisProcessPath().native());
-
- if (version.dwFileFlags & VS_FF_PRERELEASE)
- {
- // Pre-release builds need annotating
- QString versionString = QString::fromStdWString(
- GetFileVersionString(env::thisProcessPath().native()));
-
- // The pre-release flag can be set without the string specifying what type of pre-release
- bool noLetters = true;
- for (QChar character : versionString)
- {
- if (character.isLetter())
- {
- noLetters = false;
- break;
- }
- }
-
- if (noLetters)
- {
- // Default to pre-alpha when release type is unspecified
- return VersionInfo(version.dwFileVersionMS >> 16,
- version.dwFileVersionMS & 0xFFFF,
- version.dwFileVersionLS >> 16,
- version.dwFileVersionLS & 0xFFFF,
- VersionInfo::RELEASE_PREALPHA);
- }
- else
- {
- // Trust the string to make sense
- return VersionInfo(versionString);
- }
- }
- else
- {
- // Non-pre-release builds just need their version numbers reading
- return VersionInfo(version.dwFileVersionMS >> 16,
- version.dwFileVersionMS & 0xFFFF,
- version.dwFileVersionLS >> 16,
- version.dwFileVersionLS & 0xFFFF);
- }
-}
-
-QString getUsvfsDLLVersion()
-{
- // once 2.2.2 is released, this can be changed to call USVFSVersionString()
- // directly; until then, using GetProcAddress() allows for mixing up devbuilds
- // and usvfs dlls
-
- using USVFSVersionStringType = const char* WINAPI ();
-
- QString s;
-
- const auto m = ::LoadLibraryW(L"usvfs_x64.dll");
-
- if (m) {
- auto* f = reinterpret_cast<USVFSVersionStringType*>(
- ::GetProcAddress(m, "USVFSVersionString"));
-
- if (f) {
- s = f();
- }
-
- ::FreeLibrary(m);
- }
-
- if (s.isEmpty()) {
- s = "?";
- }
-
- return s;
-}
-
-QString getUsvfsVersionString()
-{
- const QString dll = getUsvfsDLLVersion();
- const QString header = USVFS_VERSION_STRING;
-
- QString usvfsVersion;
-
- if (dll == header) {
- return dll;
- } else {
- return "dll is " + dll + ", compiled against " + header;
- }
-}
-
-void SetThisThreadName(const QString& s)
-{
- using SetThreadDescriptionType = HRESULT (
- HANDLE hThread,
- PCWSTR lpThreadDescription
- );
-
- static SetThreadDescriptionType* SetThreadDescription = [] {
- SetThreadDescriptionType* p = nullptr;
-
- env::LibraryPtr kernel32(LoadLibraryW(L"kernel32.dll"));
- if (!kernel32) {
- return p;
- }
-
- p = reinterpret_cast<SetThreadDescriptionType*>(
- GetProcAddress(kernel32.get(), "SetThreadDescription"));
-
- return p;
- }();
-
- if (SetThreadDescription) {
- SetThreadDescription(GetCurrentThread(), s.toStdWString().c_str());
- }
-}
-
-
-char shortcutChar(const QAction* a)
-{
- const auto text = a->text();
- char shortcut = 0;
-
- for (int i=0; i<text.size(); ++i) {
- const auto c = text[i];
- if (c == '&') {
- if (i >= (text.size() - 1)) {
- log::error("ampersand at the end");
- return 0;
- }
-
- return text[i + 1].toLatin1();
- }
- }
-
- log::error("action {} has no shortcut", text);
- return 0;
-}
-
-void checkDuplicateShortcuts(const QMenu& m)
-{
- const auto actions = m.actions();
-
- for (int i=0; i<actions.size(); ++i) {
- const auto* action1 = actions[i];
- if (action1->isSeparator()) {
- continue;
- }
-
- const char shortcut1 = shortcutChar(action1);
- if (shortcut1 == 0) {
- continue;
- }
-
- for (int j=i+1; j<actions.size(); ++j) {
- const auto* action2 = actions[j];
- if (action2->isSeparator()) {
- continue;
- }
-
- const char shortcut2 = shortcutChar(action2);
-
- if (shortcut1 == shortcut2) {
- log::error(
- "duplicate shortcut {} for {} and {}",
- shortcut1, action1->text(), action2->text());
-
- break;
- }
- }
- }
-}
-
-} // namespace MOShared
-
-
-static bool g_exiting = false;
-static bool g_canClose = false;
-
-MainWindow* findMainWindow()
-{
- for (auto* tl : qApp->topLevelWidgets()) {
- if (auto* mw=dynamic_cast<MainWindow*>(tl)) {
- return mw;
- }
- }
-
- return nullptr;
-}
-
-bool ExitModOrganizer(ExitFlags e)
-{
- if (g_exiting) {
- return true;
- }
-
- g_exiting = true;
- Guard g([&]{ g_exiting = false; });
-
- if (!e.testFlag(Exit::Force)) {
- if (auto* mw=findMainWindow()) {
- if (!mw->canExit()) {
- return false;
- }
- }
- }
-
- g_canClose = true;
-
- const int code = (e.testFlag(Exit::Restart) ? RestartExitCode : 0);
- qApp->exit(code);
-
- return true;
-}
-
-bool ModOrganizerCanCloseNow()
-{
- return g_canClose;
-}
-
-bool ModOrganizerExiting()
-{
- return g_exiting;
-}
-
-void ResetExitFlag()
-{
- g_exiting = false;
-}
-
-
-bool isNxmLink(const QString& link)
-{
- return link.startsWith("nxm://", Qt::CaseInsensitive);
-}
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util.h"
+#include "../env.h"
+#include "../mainwindow.h"
+#include "windows_error.h"
+#include <log.h>
+#include <usvfs.h>
+#include <usvfs_version.h>
+
+using namespace MOBase;
+
+namespace MOShared
+{
+
+bool FileExists(const std::string& filename)
+{
+ DWORD dwAttrib = ::GetFileAttributesA(filename.c_str());
+
+ return (dwAttrib != INVALID_FILE_ATTRIBUTES);
+}
+
+bool FileExists(const std::wstring& filename)
+{
+ DWORD dwAttrib = ::GetFileAttributesW(filename.c_str());
+
+ return (dwAttrib != INVALID_FILE_ATTRIBUTES);
+}
+
+bool FileExists(const std::wstring& searchPath, const std::wstring& filename)
+{
+ std::wstringstream stream;
+ stream << searchPath << "\\" << filename;
+ return FileExists(stream.str());
+}
+
+std::string ToString(const std::wstring& source, bool utf8)
+{
+ std::string result;
+ if (source.length() > 0) {
+ UINT codepage = CP_UTF8;
+ if (!utf8) {
+ codepage = AreFileApisANSI() ? GetACP() : GetOEMCP();
+ }
+ int sizeRequired = ::WideCharToMultiByte(codepage, 0, &source[0], -1, nullptr, 0,
+ nullptr, nullptr);
+ if (sizeRequired == 0) {
+ throw windows_error("failed to convert string to multibyte");
+ }
+ // the size returned by WideCharToMultiByte contains zero termination IF -1 is
+ // specified for the length. we don't want that \0 in the string because then the
+ // length field would be wrong. Because madness
+ result.resize(sizeRequired - 1, '\0');
+ ::WideCharToMultiByte(codepage, 0, &source[0], (int)source.size(), &result[0],
+ sizeRequired, nullptr, nullptr);
+ }
+
+ return result;
+}
+
+std::wstring ToWString(const std::string& source, bool utf8)
+{
+ std::wstring result;
+ if (source.length() > 0) {
+ UINT codepage = CP_UTF8;
+ if (!utf8) {
+ codepage = AreFileApisANSI() ? GetACP() : GetOEMCP();
+ }
+ int sizeRequired = ::MultiByteToWideChar(
+ codepage, 0, source.c_str(), static_cast<int>(source.length()), nullptr, 0);
+ if (sizeRequired == 0) {
+ throw windows_error("failed to convert string to wide character");
+ }
+ result.resize(sizeRequired, L'\0');
+ ::MultiByteToWideChar(codepage, 0, source.c_str(),
+ static_cast<int>(source.length()), &result[0], sizeRequired);
+ }
+
+ return result;
+}
+
+static std::locale loc("");
+static auto locToLowerW = [](wchar_t in) -> wchar_t {
+ return std::tolower(in, loc);
+};
+
+static auto locToLower = [](char in) -> char {
+ return std::tolower(in, loc);
+};
+
+std::string& ToLowerInPlace(std::string& text)
+{
+ CharLowerBuffA(const_cast<CHAR*>(text.c_str()), static_cast<DWORD>(text.size()));
+ return text;
+}
+
+std::string ToLowerCopy(const std::string& text)
+{
+ std::string result(text);
+ CharLowerBuffA(const_cast<CHAR*>(result.c_str()), static_cast<DWORD>(result.size()));
+ return result;
+}
+
+std::wstring& ToLowerInPlace(std::wstring& text)
+{
+ CharLowerBuffW(const_cast<WCHAR*>(text.c_str()), static_cast<DWORD>(text.size()));
+ return text;
+}
+
+std::wstring ToLowerCopy(const std::wstring& text)
+{
+ std::wstring result(text);
+ CharLowerBuffW(const_cast<WCHAR*>(result.c_str()), static_cast<DWORD>(result.size()));
+ return result;
+}
+
+std::wstring ToLowerCopy(std::wstring_view text)
+{
+ std::wstring result(text.begin(), text.end());
+ ToLowerInPlace(result);
+ return result;
+}
+
+bool CaseInsenstiveComparePred(wchar_t lhs, wchar_t rhs)
+{
+ return std::tolower(lhs, loc) == std::tolower(rhs, loc);
+}
+
+bool CaseInsensitiveEqual(const std::wstring& lhs, const std::wstring& rhs)
+{
+ return (lhs.length() == rhs.length()) &&
+ std::equal(lhs.begin(), lhs.end(), rhs.begin(),
+ [](wchar_t lhs, wchar_t rhs) -> bool {
+ return std::tolower(lhs, loc) == std::tolower(rhs, loc);
+ });
+}
+
+VS_FIXEDFILEINFO GetFileVersion(const std::wstring& fileName)
+{
+ DWORD handle = 0UL;
+ DWORD size = ::GetFileVersionInfoSizeW(fileName.c_str(), &handle);
+ if (size == 0) {
+ throw windows_error("failed to determine file version info size");
+ }
+
+ boost::scoped_array<char> buffer(new char[size]);
+ try {
+ handle = 0UL;
+ if (!::GetFileVersionInfoW(fileName.c_str(), handle, size, buffer.get())) {
+ throw windows_error("failed to determine file version info");
+ }
+
+ void* versionInfoPtr = nullptr;
+ UINT versionInfoLength = 0;
+ if (!::VerQueryValue(buffer.get(), L"\\", &versionInfoPtr, &versionInfoLength)) {
+ throw windows_error("failed to determine file version");
+ }
+
+ VS_FIXEDFILEINFO result = *(VS_FIXEDFILEINFO*)versionInfoPtr;
+ return result;
+ } catch (...) {
+ throw;
+ }
+}
+
+std::wstring GetFileVersionString(const std::wstring& fileName)
+{
+ DWORD handle = 0UL;
+ DWORD size = ::GetFileVersionInfoSizeW(fileName.c_str(), &handle);
+ if (size == 0) {
+ throw windows_error("failed to determine file version info size");
+ }
+
+ boost::scoped_array<char> buffer(new char[size]);
+ try {
+ handle = 0UL;
+ if (!::GetFileVersionInfoW(fileName.c_str(), handle, size, buffer.get())) {
+ throw windows_error("failed to determine file version info");
+ }
+
+ LPVOID strBuffer = nullptr;
+ UINT strLength = 0;
+ if (!::VerQueryValue(buffer.get(), L"\\StringFileInfo\\040904B0\\ProductVersion",
+ &strBuffer, &strLength)) {
+ throw windows_error("failed to determine file version");
+ }
+
+ return std::wstring((LPCTSTR)strBuffer);
+ } catch (...) {
+ throw;
+ }
+}
+
+VersionInfo createVersionInfo()
+{
+ VS_FIXEDFILEINFO version = GetFileVersion(env::thisProcessPath().native());
+
+ if (version.dwFileFlags & VS_FF_PRERELEASE) {
+ // Pre-release builds need annotating
+ QString versionString =
+ QString::fromStdWString(GetFileVersionString(env::thisProcessPath().native()));
+
+ // The pre-release flag can be set without the string specifying what type of
+ // pre-release
+ bool noLetters = true;
+ for (QChar character : versionString) {
+ if (character.isLetter()) {
+ noLetters = false;
+ break;
+ }
+ }
+
+ if (noLetters) {
+ // Default to pre-alpha when release type is unspecified
+ return VersionInfo(
+ version.dwFileVersionMS >> 16, version.dwFileVersionMS & 0xFFFF,
+ version.dwFileVersionLS >> 16, version.dwFileVersionLS & 0xFFFF,
+ VersionInfo::RELEASE_PREALPHA);
+ } else {
+ // Trust the string to make sense
+ return VersionInfo(versionString);
+ }
+ } else {
+ // Non-pre-release builds just need their version numbers reading
+ return VersionInfo(version.dwFileVersionMS >> 16, version.dwFileVersionMS & 0xFFFF,
+ version.dwFileVersionLS >> 16, version.dwFileVersionLS & 0xFFFF);
+ }
+}
+
+QString getUsvfsDLLVersion()
+{
+ // once 2.2.2 is released, this can be changed to call USVFSVersionString()
+ // directly; until then, using GetProcAddress() allows for mixing up devbuilds
+ // and usvfs dlls
+
+ using USVFSVersionStringType = const char* WINAPI();
+
+ QString s;
+
+ const auto m = ::LoadLibraryW(L"usvfs_x64.dll");
+
+ if (m) {
+ auto* f = reinterpret_cast<USVFSVersionStringType*>(
+ ::GetProcAddress(m, "USVFSVersionString"));
+
+ if (f) {
+ s = f();
+ }
+
+ ::FreeLibrary(m);
+ }
+
+ if (s.isEmpty()) {
+ s = "?";
+ }
+
+ return s;
+}
+
+QString getUsvfsVersionString()
+{
+ const QString dll = getUsvfsDLLVersion();
+ const QString header = USVFS_VERSION_STRING;
+
+ QString usvfsVersion;
+
+ if (dll == header) {
+ return dll;
+ } else {
+ return "dll is " + dll + ", compiled against " + header;
+ }
+}
+
+void SetThisThreadName(const QString& s)
+{
+ using SetThreadDescriptionType = HRESULT(HANDLE hThread, PCWSTR lpThreadDescription);
+
+ static SetThreadDescriptionType* SetThreadDescription = [] {
+ SetThreadDescriptionType* p = nullptr;
+
+ env::LibraryPtr kernel32(LoadLibraryW(L"kernel32.dll"));
+ if (!kernel32) {
+ return p;
+ }
+
+ p = reinterpret_cast<SetThreadDescriptionType*>(
+ GetProcAddress(kernel32.get(), "SetThreadDescription"));
+
+ return p;
+ }();
+
+ if (SetThreadDescription) {
+ SetThreadDescription(GetCurrentThread(), s.toStdWString().c_str());
+ }
+}
+
+char shortcutChar(const QAction* a)
+{
+ const auto text = a->text();
+ char shortcut = 0;
+
+ for (int i = 0; i < text.size(); ++i) {
+ const auto c = text[i];
+ if (c == '&') {
+ if (i >= (text.size() - 1)) {
+ log::error("ampersand at the end");
+ return 0;
+ }
+
+ return text[i + 1].toLatin1();
+ }
+ }
+
+ log::error("action {} has no shortcut", text);
+ return 0;
+}
+
+void checkDuplicateShortcuts(const QMenu& m)
+{
+ const auto actions = m.actions();
+
+ for (int i = 0; i < actions.size(); ++i) {
+ const auto* action1 = actions[i];
+ if (action1->isSeparator()) {
+ continue;
+ }
+
+ const char shortcut1 = shortcutChar(action1);
+ if (shortcut1 == 0) {
+ continue;
+ }
+
+ for (int j = i + 1; j < actions.size(); ++j) {
+ const auto* action2 = actions[j];
+ if (action2->isSeparator()) {
+ continue;
+ }
+
+ const char shortcut2 = shortcutChar(action2);
+
+ if (shortcut1 == shortcut2) {
+ log::error("duplicate shortcut {} for {} and {}", shortcut1, action1->text(),
+ action2->text());
+
+ break;
+ }
+ }
+ }
+}
+
+} // namespace MOShared
+
+static bool g_exiting = false;
+static bool g_canClose = false;
+
+MainWindow* findMainWindow()
+{
+ for (auto* tl : qApp->topLevelWidgets()) {
+ if (auto* mw = dynamic_cast<MainWindow*>(tl)) {
+ return mw;
+ }
+ }
+
+ return nullptr;
+}
+
+bool ExitModOrganizer(ExitFlags e)
+{
+ if (g_exiting) {
+ return true;
+ }
+
+ g_exiting = true;
+ Guard g([&] {
+ g_exiting = false;
+ });
+
+ if (!e.testFlag(Exit::Force)) {
+ if (auto* mw = findMainWindow()) {
+ if (!mw->canExit()) {
+ return false;
+ }
+ }
+ }
+
+ g_canClose = true;
+
+ const int code = (e.testFlag(Exit::Restart) ? RestartExitCode : 0);
+ qApp->exit(code);
+
+ return true;
+}
+
+bool ModOrganizerCanCloseNow()
+{
+ return g_canClose;
+}
+
+bool ModOrganizerExiting()
+{
+ return g_exiting;
+}
+
+void ResetExitFlag()
+{
+ g_exiting = false;
+}
+
+bool isNxmLink(const QString& link)
+{
+ return link.startsWith("nxm://", Qt::CaseInsensitive);
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index d8fdb3a3..1bc6bd47 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -1,89 +1,89 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef UTIL_H
-#define UTIL_H
-
-#include <log.h>
-#include <string>
-#include <filesystem>
-#include <versioninfo.h>
-
-class Executable;
-
-namespace MOShared {
-
-/// Test if a file (or directory) by the specified name exists
-bool FileExists(const std::string &filename);
-bool FileExists(const std::wstring &filename);
-
-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& ToLowerInPlace(std::string& text);
-std::string ToLowerCopy(const std::string& text);
-
-std::wstring& ToLowerInPlace(std::wstring& text);
-std::wstring ToLowerCopy(const std::wstring& text);
-std::wstring ToLowerCopy(std::wstring_view text);
-
-bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs);
-
-MOBase::VersionInfo createVersionInfo();
-QString getUsvfsVersionString();
-
-void SetThisThreadName(const QString& s);
-void checkDuplicateShortcuts(const QMenu& m);
-
-inline FILETIME ToFILETIME(std::filesystem::file_time_type t)
-{
- FILETIME ft;
- static_assert(sizeof(t) == sizeof(ft));
-
- std::memcpy(&ft, &t, sizeof(FILETIME));
- return ft;
-}
-
-} // namespace MOShared
-
-
-enum class Exit
-{
- None = 0x00,
- Normal = 0x01,
- Restart = 0x02,
- Force = 0x04
-};
-
-const int RestartExitCode = INT_MAX;
-const int ReselectExitCode = INT_MAX - 1;
-
-using ExitFlags = QFlags<Exit>;
-Q_DECLARE_OPERATORS_FOR_FLAGS(ExitFlags);
-
-bool ExitModOrganizer(ExitFlags e=Exit::Normal);
-bool ModOrganizerExiting();
-bool ModOrganizerCanCloseNow();
-void ResetExitFlag();
-
-bool isNxmLink(const QString& link);
-
-#endif // UTIL_H
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <filesystem>
+#include <log.h>
+#include <string>
+#include <versioninfo.h>
+
+class Executable;
+
+namespace MOShared
+{
+
+/// Test if a file (or directory) by the specified name exists
+bool FileExists(const std::string& filename);
+bool FileExists(const std::wstring& filename);
+
+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& ToLowerInPlace(std::string& text);
+std::string ToLowerCopy(const std::string& text);
+
+std::wstring& ToLowerInPlace(std::wstring& text);
+std::wstring ToLowerCopy(const std::wstring& text);
+std::wstring ToLowerCopy(std::wstring_view text);
+
+bool CaseInsensitiveEqual(const std::wstring& lhs, const std::wstring& rhs);
+
+MOBase::VersionInfo createVersionInfo();
+QString getUsvfsVersionString();
+
+void SetThisThreadName(const QString& s);
+void checkDuplicateShortcuts(const QMenu& m);
+
+inline FILETIME ToFILETIME(std::filesystem::file_time_type t)
+{
+ FILETIME ft;
+ static_assert(sizeof(t) == sizeof(ft));
+
+ std::memcpy(&ft, &t, sizeof(FILETIME));
+ return ft;
+}
+
+} // namespace MOShared
+
+enum class Exit
+{
+ None = 0x00,
+ Normal = 0x01,
+ Restart = 0x02,
+ Force = 0x04
+};
+
+const int RestartExitCode = INT_MAX;
+const int ReselectExitCode = INT_MAX - 1;
+
+using ExitFlags = QFlags<Exit>;
+Q_DECLARE_OPERATORS_FOR_FLAGS(ExitFlags);
+
+bool ExitModOrganizer(ExitFlags e = Exit::Normal);
+bool ModOrganizerExiting();
+bool ModOrganizerCanCloseNow();
+void ResetExitFlag();
+
+bool isNxmLink(const QString& link);
+
+#endif // UTIL_H
diff --git a/src/shared/windows_error.cpp b/src/shared/windows_error.cpp
index 97a58a20..f0add1da 100644
--- a/src/shared/windows_error.cpp
+++ b/src/shared/windows_error.cpp
@@ -1,49 +1,52 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "windows_error.h"
-#include <sstream>
-
-namespace MOShared {
-
-std::string windows_error::constructMessage(const std::string& input, int inErrorCode)
-{
- std::ostringstream finalMessage;
- finalMessage << input;
-
- LPSTR buffer = nullptr;
-
- DWORD errorCode = inErrorCode != -1 ? inErrorCode : ::GetLastError();
-
- // TODO: the message is not english?
- if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, nullptr) == 0) {
- finalMessage << " (errorcode " << errorCode << ")";
- } else {
- LPSTR lastChar = buffer + strlen(buffer) - 2;
- *lastChar = '\0';
- finalMessage << " (" << buffer << " [" << errorCode << "])";
- LocalFree(buffer); // allocated by FormatMessage
- }
-
- ::SetLastError(errorCode); // restore error code because FormatMessage might have modified it
- return finalMessage.str();
-}
-
-} // namespace MOShared
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "windows_error.h"
+#include <sstream>
+
+namespace MOShared
+{
+
+std::string windows_error::constructMessage(const std::string& input, int inErrorCode)
+{
+ std::ostringstream finalMessage;
+ finalMessage << input;
+
+ LPSTR buffer = nullptr;
+
+ DWORD errorCode = inErrorCode != -1 ? inErrorCode : ::GetLastError();
+
+ // TODO: the message is not english?
+ if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR)&buffer, 0, nullptr) == 0) {
+ finalMessage << " (errorcode " << errorCode << ")";
+ } else {
+ LPSTR lastChar = buffer + strlen(buffer) - 2;
+ *lastChar = '\0';
+ finalMessage << " (" << buffer << " [" << errorCode << "])";
+ LocalFree(buffer); // allocated by FormatMessage
+ }
+
+ ::SetLastError(
+ errorCode); // restore error code because FormatMessage might have modified it
+ return finalMessage.str();
+}
+
+} // namespace MOShared
diff --git a/src/shared/windows_error.h b/src/shared/windows_error.h
index 03ce0abd..2bdd6392 100644
--- a/src/shared/windows_error.h
+++ b/src/shared/windows_error.h
@@ -1,44 +1,47 @@
-/*
-Copyright (C) 2012 Sebastian Herbord. All rights reserved.
-
-This file is part of Mod Organizer.
-
-Mod Organizer is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Mod Organizer is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef WINDOWS_ERROR_H
-#define WINDOWS_ERROR_H
-
-
-#include <stdexcept>
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-namespace MOShared {
-
-class windows_error : public std::runtime_error {
-public:
- windows_error(const std::string& message, int errorcode = ::GetLastError())
- : runtime_error(constructMessage(message, errorcode)), m_ErrorCode(errorcode)
- {}
- int getErrorCode() const { return m_ErrorCode; }
-private:
- std::string constructMessage(const std::string& input, int errorcode);
-private:
- int m_ErrorCode;
-};
-
-} // namespace MOShared
-
-#endif // WINDOWS_ERROR_H
+/*
+Copyright (C) 2012 Sebastian Herbord. All rights reserved.
+
+This file is part of Mod Organizer.
+
+Mod Organizer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Mod Organizer is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef WINDOWS_ERROR_H
+#define WINDOWS_ERROR_H
+
+#include <stdexcept>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+namespace MOShared
+{
+
+class windows_error : public std::runtime_error
+{
+public:
+ windows_error(const std::string& message, int errorcode = ::GetLastError())
+ : runtime_error(constructMessage(message, errorcode)), m_ErrorCode(errorcode)
+ {}
+ int getErrorCode() const { return m_ErrorCode; }
+
+private:
+ std::string constructMessage(const std::string& input, int errorcode);
+
+private:
+ int m_ErrorCode;
+};
+
+} // namespace MOShared
+
+#endif // WINDOWS_ERROR_H