From 86bb01ba9eac879d3685c439ac9da0028bc4bc80 Mon Sep 17 00:00:00 2001 From: Mikaƫl Capelle Date: Tue, 17 May 2022 11:37:19 +0200 Subject: Convert everything to CRLF. --- src/shared/appconfig.cpp | 66 +- src/shared/appconfig.h | 80 +- src/shared/directoryentry.cpp | 1956 ++++++++++++++++++++--------------------- src/shared/directoryentry.h | 642 +++++++------- src/shared/util.cpp | 888 +++++++++---------- src/shared/util.h | 178 ++-- src/shared/windows_error.cpp | 98 +-- src/shared/windows_error.h | 88 +- 8 files changed, 1998 insertions(+), 1998 deletions(-) (limited to 'src/shared') diff --git a/src/shared/appconfig.cpp b/src/shared/appconfig.cpp index 49edcbcc..d2fc386c 100644 --- a/src/shared/appconfig.cpp +++ b/src/shared/appconfig.cpp @@ -1,33 +1,33 @@ -/* -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 . -*/ - -#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 . +*/ + +#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 diff --git a/src/shared/appconfig.h b/src/shared/appconfig.h index b32043e5..cca5b0f5 100644 --- a/src/shared/appconfig.h +++ b/src/shared/appconfig.h @@ -1,40 +1,40 @@ -/* -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 . -*/ - -#ifndef APPCONFIG_H -#define APPCONFIG_H - -#include - -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 . +*/ + +#ifndef APPCONFIG_H +#define APPCONFIG_H + +#include + +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 diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index badb5636..48ff7aa3 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -1,978 +1,978 @@ -/* -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 . -*/ - -#include "directoryentry.h" -#include "originconnection.h" -#include "filesorigin.h" -#include "fileentry.h" -#include "../envfs.h" -#include "util.h" -#include "windows_error.h" -#include -#include - -namespace MOShared -{ - -using namespace MOBase; -const int MAXPATH_UNICODE = 32767; - -template -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, - boost::shared_ptr 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& archives, - const std::set& enabledArchives, - const std::vector& 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 DirectoryEntry::getFiles() const -{ - std::vector 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 &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 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; igetFile(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; igetSubFolder(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& 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 . +*/ + +#include "directoryentry.h" +#include "originconnection.h" +#include "filesorigin.h" +#include "fileentry.h" +#include "../envfs.h" +#include "util.h" +#include "windows_error.h" +#include +#include + +namespace MOShared +{ + +using namespace MOBase; +const int MAXPATH_UNICODE = 32767; + +template +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, + boost::shared_ptr 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& archives, + const std::set& enabledArchives, + const std::vector& 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 DirectoryEntry::getFiles() const +{ + std::vector 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 &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 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; igetFile(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; igetSubFolder(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& 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..f7ab2b90 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -1,321 +1,321 @@ -/* -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 . -*/ - -#ifndef MO_REGISTER_DIRECTORYENTRY_INCLUDED -#define MO_REGISTER_DIRECTORYENTRY_INCLUDED - -#include "fileregister.h" -#include - -namespace env -{ - class DirectoryWalker; - struct Directory; - struct File; -} - -namespace std -{ - template <> - struct hash - { - 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( - std::wstring name, DirectoryEntry* parent, OriginID originID); - - DirectoryEntry( - std::wstring name, DirectoryEntry* parent, OriginID originID, - boost::shared_ptr fileRegister, - boost::shared_ptr 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& archives, - const std::set& enabledArchives, - const std::vector& 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 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 getFiles() const; - - const SubDirectories& getSubDirectories() const - { - return m_SubDirectories; - } - - template - void forEachDirectory(F&& f) const - { - for (auto&& d : m_SubDirectories) { - if (!f(*d)) { - break; - } - } - } - - template - void forEachFile(F&& f) const - { - for (auto&& p : m_Files) { - if (auto file=m_FileRegister->getFile(p.second)) { - if (!f(*file)) { - break; - } - } - } - } - - template - 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& indices); - - void dump(const std::wstring& file) const; - -private: - using FilesMap = std::map; - using FilesLookup = std::unordered_map; - using SubDirectoriesLookup = std::unordered_map; - - boost::shared_ptr m_FileRegister; - boost::shared_ptr m_OriginConnection; - - std::wstring m_Name; - FilesMap m_Files; - FilesLookup m_FilesLookup; - SubDirectories m_SubDirectories; - SubDirectoriesLookup m_SubDirectoriesLookup; - - DirectoryEntry* m_Parent; - std::set 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& 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::result_type - hash::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 . +*/ + +#ifndef MO_REGISTER_DIRECTORYENTRY_INCLUDED +#define MO_REGISTER_DIRECTORYENTRY_INCLUDED + +#include "fileregister.h" +#include + +namespace env +{ + class DirectoryWalker; + struct Directory; + struct File; +} + +namespace std +{ + template <> + struct hash + { + 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( + std::wstring name, DirectoryEntry* parent, OriginID originID); + + DirectoryEntry( + std::wstring name, DirectoryEntry* parent, OriginID originID, + boost::shared_ptr fileRegister, + boost::shared_ptr 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& archives, + const std::set& enabledArchives, + const std::vector& 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 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 getFiles() const; + + const SubDirectories& getSubDirectories() const + { + return m_SubDirectories; + } + + template + void forEachDirectory(F&& f) const + { + for (auto&& d : m_SubDirectories) { + if (!f(*d)) { + break; + } + } + } + + template + void forEachFile(F&& f) const + { + for (auto&& p : m_Files) { + if (auto file=m_FileRegister->getFile(p.second)) { + if (!f(*file)) { + break; + } + } + } + } + + template + 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& indices); + + void dump(const std::wstring& file) const; + +private: + using FilesMap = std::map; + using FilesLookup = std::unordered_map; + using SubDirectoriesLookup = std::unordered_map; + + boost::shared_ptr m_FileRegister; + boost::shared_ptr m_OriginConnection; + + std::wstring m_Name; + FilesMap m_Files; + FilesLookup m_FilesLookup; + SubDirectories m_SubDirectories; + SubDirectoriesLookup m_SubDirectoriesLookup; + + DirectoryEntry* m_Parent; + std::set 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& 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::result_type + hash::operator()( + const argument_type& key) const + { + return key.hash; + } +} + +#endif // MO_REGISTER_DIRECTORYENTRY_INCLUDED diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 54c6b841..e2faaec5 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -1,444 +1,444 @@ -/* -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 . -*/ - -#include "util.h" -#include "windows_error.h" -#include "../mainwindow.h" -#include "../env.h" -#include -#include -#include - -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(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(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(text.c_str()), static_cast(text.size())); - return text; -} - -std::string ToLowerCopy(const std::string& text) -{ - std::string result(text); - CharLowerBuffA(const_cast(result.c_str()), static_cast(result.size())); - return result; -} - -std::wstring& ToLowerInPlace(std::wstring& text) -{ - CharLowerBuffW(const_cast(text.c_str()), static_cast(text.size())); - return text; -} - -std::wstring ToLowerCopy(const std::wstring& text) -{ - std::wstring result(text); - CharLowerBuffW(const_cast(result.c_str()), static_cast(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 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 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( - ::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( - 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() - 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; iisSeparator()) { - continue; - } - - const char shortcut1 = shortcutChar(action1); - if (shortcut1 == 0) { - continue; - } - - for (int j=i+1; jisSeparator()) { - 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(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 . +*/ + +#include "util.h" +#include "windows_error.h" +#include "../mainwindow.h" +#include "../env.h" +#include +#include +#include + +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(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(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(text.c_str()), static_cast(text.size())); + return text; +} + +std::string ToLowerCopy(const std::string& text) +{ + std::string result(text); + CharLowerBuffA(const_cast(result.c_str()), static_cast(result.size())); + return result; +} + +std::wstring& ToLowerInPlace(std::wstring& text) +{ + CharLowerBuffW(const_cast(text.c_str()), static_cast(text.size())); + return text; +} + +std::wstring ToLowerCopy(const std::wstring& text) +{ + std::wstring result(text); + CharLowerBuffW(const_cast(result.c_str()), static_cast(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 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 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( + ::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( + 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() - 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; iisSeparator()) { + continue; + } + + const char shortcut1 = shortcutChar(action1); + if (shortcut1 == 0) { + continue; + } + + for (int j=i+1; jisSeparator()) { + 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(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..87ec5bf2 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 . -*/ - -#ifndef UTIL_H -#define UTIL_H - -#include -#include -#include -#include - -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; -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 . +*/ + +#ifndef UTIL_H +#define UTIL_H + +#include +#include +#include +#include + +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; +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..49fb9bc7 100644 --- a/src/shared/windows_error.cpp +++ b/src/shared/windows_error.cpp @@ -1,49 +1,49 @@ -/* -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 . -*/ - -#include "windows_error.h" -#include - -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 . +*/ + +#include "windows_error.h" +#include + +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..ea506dc1 100644 --- a/src/shared/windows_error.h +++ b/src/shared/windows_error.h @@ -1,44 +1,44 @@ -/* -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 . -*/ - -#ifndef WINDOWS_ERROR_H -#define WINDOWS_ERROR_H - - -#include -#define WIN32_LEAN_AND_MEAN -#include - -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 . +*/ + +#ifndef WINDOWS_ERROR_H +#define WINDOWS_ERROR_H + + +#include +#define WIN32_LEAN_AND_MEAN +#include + +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 -- cgit v1.3.1