From ee03c232907a0c340715ed8390d432cf27e13b27 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Dec 2019 23:40:43 -0500 Subject: refactoring: moved member functions in the same order as the header --- src/shared/directoryentry.cpp | 1176 ++++++++++++++++++++--------------------- 1 file changed, 585 insertions(+), 591 deletions(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 3b98b7ff..c93df665 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -65,6 +65,11 @@ static bool SupportOptimizedFind() return (::VerifyVersionInfo(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, mask) == TRUE); } +static bool DirCompareByName(const DirectoryEntry *lhs, const DirectoryEntry *rhs) +{ + return _wcsicmp(lhs->getName().c_str(), rhs->getName().c_str()) < 0; +} + class OriginConnection { @@ -157,112 +162,25 @@ private: }; -FilesOrigin::FilesOrigin() - : m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0) -{ - LEAK_TRACE; -} - -FilesOrigin::FilesOrigin(const FilesOrigin &reference) - : m_ID(reference.m_ID) - , m_Disabled(reference.m_Disabled) - , m_Name(reference.m_Name) - , m_Path(reference.m_Path) - , m_Priority(reference.m_Priority) - , m_FileRegister(reference.m_FileRegister) - , m_OriginConnection(reference.m_OriginConnection) +FileEntry::FileEntry() : + m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(nullptr), + m_LastAccessed(time(nullptr)) { LEAK_TRACE; } - -FilesOrigin::FilesOrigin( - int ID, const std::wstring &name, const std::wstring &path, int priority, - boost::shared_ptr fileRegister, - boost::shared_ptr originConnection) : - m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path), - m_Priority(priority), m_FileRegister(fileRegister), - m_OriginConnection(originConnection) +FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) : + m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), + m_Parent(parent), m_LastAccessed(time(nullptr)) { LEAK_TRACE; } -FilesOrigin::~FilesOrigin() +FileEntry::~FileEntry() { LEAK_UNTRACE; } -void FilesOrigin::setPriority(int priority) -{ - m_OriginConnection.lock()->changePriorityLookup(m_Priority, priority); - - m_Priority = priority; -} - -void FilesOrigin::setName(const std::wstring &name) -{ - m_OriginConnection.lock()->changeNameLookup(m_Name, name); - - // change path too - if (tail(m_Path, m_Name.length()) == m_Name) { - m_Path = m_Path.substr(0, m_Path.length() - m_Name.length()).append(name); - } - - m_Name = name; -} - -std::vector FilesOrigin::getFiles() const -{ - std::vector result; - - for (FileEntry::Index fileIdx : m_Files) { - if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) { - result.push_back(p); - } - } - - return result; -} - -FileEntry::Ptr FilesOrigin::findFile(FileEntry::Index index) const -{ - return m_FileRegister.lock()->getFile(index); -} - -void FilesOrigin::enable(bool enabled, time_t notAfter) -{ - if (!enabled) { - std::set copy = m_Files; - m_FileRegister.lock()->removeOriginMulti(copy, m_ID, notAfter); - m_Files.clear(); - } - - m_Disabled = !enabled; -} - -void FilesOrigin::removeFile(FileEntry::Index index) -{ - auto iter = m_Files.find(index); - - if (iter != m_Files.end()) { - m_Files.erase(iter); - } -} - -bool FilesOrigin::containsArchive(std::wstring archiveName) -{ - for (FileEntry::Index fileIdx : m_Files) { - if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) { - if (p->isFromArchive(archiveName)) { - return true; - } - } - } - - return false; -} - - void FileEntry::addOrigin( int origin, FILETIME fileTime, const std::wstring &archive, int order) { @@ -381,25 +299,6 @@ bool FileEntry::removeOrigin(int origin) return false; } -FileEntry::FileEntry() : - m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(nullptr), - m_LastAccessed(time(nullptr)) -{ - LEAK_TRACE; -} - -FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) : - m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), - m_Parent(parent), m_LastAccessed(time(nullptr)) -{ - LEAK_TRACE; -} - -FileEntry::~FileEntry() -{ - LEAK_UNTRACE; -} - void FileEntry::sortOrigins() { m_Alternatives.push_back({m_Origin, m_Archive}); @@ -440,18 +339,23 @@ void FileEntry::sortOrigins() } } -bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) const +bool FileEntry::isFromArchive(std::wstring archiveName) const { - if (parent == nullptr) { - return false; - } else { - // don't append the topmost parent because it is the virtual data-root - if (recurseParents(path, parent->getParent())) { - path.append(L"\\").append(parent->getName()); - } + if (archiveName.length() == 0) { + return m_Archive.first.length() != 0; + } + if (m_Archive.first.compare(archiveName) == 0) { return true; } + + for (auto alternative : m_Alternatives) { + if (alternative.second.first.compare(archiveName) == 0) { + return true; + } + } + + return false; } std::wstring FileEntry::getFullPath() const @@ -477,105 +381,329 @@ std::wstring FileEntry::getRelativePath() const return result + L"\\" + m_Name; } -bool FileEntry::isFromArchive(std::wstring archiveName) const +bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) const { - if (archiveName.length() == 0) { - return m_Archive.first.length() != 0; - } + if (parent == nullptr) { + return false; + } else { + // don't append the topmost parent because it is the virtual data-root + if (recurseParents(path, parent->getParent())) { + path.append(L"\\").append(parent->getName()); + } - if (m_Archive.first.compare(archiveName) == 0) { return true; } - - for (auto alternative : m_Alternatives) { - if (alternative.second.first.compare(archiveName) == 0) { - return true; - } - } - - return false; } -DirectoryEntry::DirectoryEntry( - const std::wstring &name, DirectoryEntry *parent, int originID) : - m_OriginConnection(new OriginConnection), - m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(true) +FilesOrigin::FilesOrigin() + : m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0) { - m_FileRegister.reset(new FileRegister(m_OriginConnection)); - m_Origins.insert(originID); LEAK_TRACE; } -DirectoryEntry::DirectoryEntry( - const std::wstring &name, DirectoryEntry *parent, int originID, - boost::shared_ptr fileRegister, - boost::shared_ptr originConnection) : - m_FileRegister(fileRegister), m_OriginConnection(originConnection), - m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(false) +FilesOrigin::FilesOrigin(const FilesOrigin &reference) + : m_ID(reference.m_ID) + , m_Disabled(reference.m_Disabled) + , m_Name(reference.m_Name) + , m_Path(reference.m_Path) + , m_Priority(reference.m_Priority) + , m_FileRegister(reference.m_FileRegister) + , m_OriginConnection(reference.m_OriginConnection) { LEAK_TRACE; - m_Origins.insert(originID); } -DirectoryEntry::~DirectoryEntry() +FilesOrigin::FilesOrigin( + int ID, const std::wstring &name, const std::wstring &path, int priority, + boost::shared_ptr fileRegister, + boost::shared_ptr originConnection) : + m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path), + m_Priority(priority), m_FileRegister(fileRegister), + m_OriginConnection(originConnection) { - LEAK_UNTRACE; - clear(); + LEAK_TRACE; } -const std::wstring &DirectoryEntry::getName() const +FilesOrigin::~FilesOrigin() { - return m_Name; + LEAK_UNTRACE; } -void DirectoryEntry::clear() +void FilesOrigin::setPriority(int priority) { - m_Files.clear(); - m_FilesLookup.clear(); - - for (DirectoryEntry *entry : m_SubDirectories) { - delete entry; - } + m_OriginConnection.lock()->changePriorityLookup(m_Priority, priority); - m_SubDirectories.clear(); - m_SubDirectoriesMap.clear(); + m_Priority = priority; } -FilesOrigin &DirectoryEntry::createOrigin( - const std::wstring &originName, const std::wstring &directory, int priority) +void FilesOrigin::setName(const std::wstring &name) { - if (m_OriginConnection->exists(originName)) { - FilesOrigin &origin = m_OriginConnection->getByName(originName); - origin.enable(true); - return origin; - } else { - return m_OriginConnection->createOrigin( - originName, directory, priority, m_FileRegister, m_OriginConnection); + m_OriginConnection.lock()->changeNameLookup(m_Name, name); + + // change path too + if (tail(m_Path, m_Name.length()) == m_Name) { + m_Path = m_Path.substr(0, m_Path.length() - m_Name.length()).append(name); } + + m_Name = name; } -void DirectoryEntry::addFromOrigin( - const std::wstring &originName, const std::wstring &directory, int priority) +std::vector FilesOrigin::getFiles() const { - FilesOrigin &origin = createOrigin(originName, directory, priority); + std::vector result; - if (directory.length() != 0) { - boost::scoped_array buffer(new wchar_t[MAXPATH_UNICODE + 1]); - memset(buffer.get(), L'\0', MAXPATH_UNICODE + 1); - int offset = _snwprintf(buffer.get(), MAXPATH_UNICODE, L"%ls", directory.c_str()); - buffer.get()[offset] = L'\0'; - addFiles(origin, buffer.get(), offset); + for (FileEntry::Index fileIdx : m_Files) { + if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) { + result.push_back(p); + } } - m_Populated = true; + return result; } -void DirectoryEntry::addFromBSA( - const std::wstring &originName, std::wstring &directory, - const std::wstring &fileName, int priority, int order) +FileEntry::Ptr FilesOrigin::findFile(FileEntry::Index index) const { - FilesOrigin &origin = createOrigin(originName, directory, priority); + return m_FileRegister.lock()->getFile(index); +} + +void FilesOrigin::enable(bool enabled, time_t notAfter) +{ + if (!enabled) { + std::set copy = m_Files; + m_FileRegister.lock()->removeOriginMulti(copy, m_ID, notAfter); + m_Files.clear(); + } + + m_Disabled = !enabled; +} + +void FilesOrigin::removeFile(FileEntry::Index index) +{ + auto iter = m_Files.find(index); + + if (iter != m_Files.end()) { + m_Files.erase(iter); + } +} + +bool FilesOrigin::containsArchive(std::wstring archiveName) +{ + for (FileEntry::Index fileIdx : m_Files) { + if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) { + if (p->isFromArchive(archiveName)) { + return true; + } + } + } + + return false; +} + + +FileRegister::FileRegister(boost::shared_ptr originConnection) + : m_OriginConnection(originConnection) +{ + LEAK_TRACE; +} + +FileRegister::~FileRegister() +{ + LEAK_UNTRACE; + m_Files.clear(); +} + +bool FileRegister::indexValid(FileEntry::Index index) const +{ + return (m_Files.find(index) != m_Files.end()); +} + +FileEntry::Ptr FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent) +{ + FileEntry::Index index = generateIndex(); + m_Files[index] = FileEntry::Ptr(new FileEntry(index, name, parent)); + + return m_Files[index]; +} + +FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const +{ + auto iter = m_Files.find(index); + + if (iter != m_Files.end()) { + return iter->second; + } else { + return FileEntry::Ptr(); + } +} + +bool FileRegister::removeFile(FileEntry::Index index) +{ + auto iter = m_Files.find(index); + + if (iter != m_Files.end()) { + unregisterFile(iter->second); + m_Files.erase(index); + return true; + } else { + log::error(QObject::tr("invalid file index for remove: {}").toStdString(), index); + return false; + } +} + +void FileRegister::removeOrigin(FileEntry::Index index, int originID) +{ + auto iter = m_Files.find(index); + + if (iter != m_Files.end()) { + if (iter->second->removeOrigin(originID)) { + unregisterFile(iter->second); + m_Files.erase(iter); + } + } else { + log::error(QObject::tr("invalid file index for remove (for origin): {}").toStdString(), index); + } +} + +void FileRegister::removeOriginMulti( + std::set indices, int originID, time_t notAfter) +{ + std::vector removedFiles; + + for (auto iter = indices.begin(); iter != indices.end(); ) { + auto pos = m_Files.find(*iter); + + if (pos != m_Files.end() + && (pos->second->lastAccessed() < notAfter) + && pos->second->removeOrigin(originID)) { + removedFiles.push_back(pos->second); + m_Files.erase(pos); + ++iter; + } else { + indices.erase(iter++); + } + } + + // optimization: this is only called when disabling an origin and in this case + // we don't have to remove the file from the origin + + // need to remove files from their parent directories. multiple ways to go + // about this: + // a) for each file, search its parents file-list (preferably by name) and + // remove what is found + // b) gather the parent directories, go through the file list for each once + // and remove all files that have been removed + // + // the latter should be faster when there are many files in few directories. + // since this is called only when disabling an origin that is probably + // frequently the case + + std::set parents; + for (const FileEntry::Ptr &file : removedFiles) { + if (file->getParent() != nullptr) { + parents.insert(file->getParent()); + } + } + + for (DirectoryEntry *parent : parents) { + parent->removeFiles(indices); + } +} + +void FileRegister::sortOrigins() +{ + for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { + iter->second->sortOrigins(); + } +} + +FileEntry::Index FileRegister::generateIndex() +{ + static std::atomic sIndex(0); + return sIndex++; +} + +void FileRegister::unregisterFile(FileEntry::Ptr file) +{ + bool ignore; + + // unregister from origin + int originID = file->getOrigin(ignore); + m_OriginConnection->getByID(originID).removeFile(file->getIndex()); + const auto& alternatives = file->getAlternatives(); + + for (auto iter = alternatives.begin(); iter != alternatives.end(); ++iter) { + m_OriginConnection->getByID(iter->first).removeFile(file->getIndex()); + } + + // unregister from directory + if (file->getParent() != nullptr) { + file->getParent()->removeFile(file->getIndex()); + } +} + + +DirectoryEntry::DirectoryEntry( + const std::wstring &name, DirectoryEntry *parent, int originID) : + m_OriginConnection(new OriginConnection), + m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(true) +{ + m_FileRegister.reset(new FileRegister(m_OriginConnection)); + m_Origins.insert(originID); + LEAK_TRACE; +} + +DirectoryEntry::DirectoryEntry( + const std::wstring &name, DirectoryEntry *parent, int originID, + boost::shared_ptr fileRegister, + boost::shared_ptr originConnection) : + m_FileRegister(fileRegister), m_OriginConnection(originConnection), + m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(false) +{ + LEAK_TRACE; + m_Origins.insert(originID); +} + +DirectoryEntry::~DirectoryEntry() +{ + LEAK_UNTRACE; + clear(); +} + +void DirectoryEntry::clear() +{ + m_Files.clear(); + m_FilesLookup.clear(); + + for (DirectoryEntry *entry : m_SubDirectories) { + delete entry; + } + + m_SubDirectories.clear(); + m_SubDirectoriesMap.clear(); +} + +void DirectoryEntry::addFromOrigin( + const std::wstring &originName, const std::wstring &directory, int priority) +{ + FilesOrigin &origin = createOrigin(originName, directory, priority); + + if (directory.length() != 0) { + boost::scoped_array buffer(new wchar_t[MAXPATH_UNICODE + 1]); + memset(buffer.get(), L'\0', MAXPATH_UNICODE + 1); + int offset = _snwprintf(buffer.get(), MAXPATH_UNICODE, L"%ls", directory.c_str()); + buffer.get()[offset] = L'\0'; + addFiles(origin, buffer.get(), offset); + } + + m_Populated = true; +} + +void DirectoryEntry::addFromBSA( + const std::wstring &originName, std::wstring &directory, + const std::wstring &fileName, int priority, int order) +{ + FilesOrigin &origin = createOrigin(originName, directory, priority); WIN32_FILE_ATTRIBUTE_DATA fileData; if (::GetFileAttributesExW(fileName.c_str(), GetFileExInfoStandard, &fileData) == 0) { @@ -608,6 +736,7 @@ void DirectoryEntry::addFromBSA( << QObject::tr("invalid bsa file: ").toStdString() << ToString(fileName, false) << " errorcode " << res << " - " << ::GetLastError(); + throw std::runtime_error(stream.str()); } @@ -625,201 +754,170 @@ void DirectoryEntry::propagateOrigin(int origin) } } - -static bool DirCompareByName(const DirectoryEntry *lhs, const DirectoryEntry *rhs) +bool DirectoryEntry::originExists(const std::wstring &name) const { - return _wcsicmp(lhs->getName().c_str(), rhs->getName().c_str()) < 0; + return m_OriginConnection->exists(name); } - -void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset) +FilesOrigin &DirectoryEntry::getOriginByID(int ID) const { - WIN32_FIND_DATAW findData; + return m_OriginConnection->getByID(ID); +} - _snwprintf_s(buffer + bufferOffset, MAXPATH_UNICODE - bufferOffset, _TRUNCATE, L"\\*"); +FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const +{ + return m_OriginConnection->getByName(name); +} - HANDLE searchHandle = nullptr; +int DirectoryEntry::anyOrigin() const +{ + bool ignore; - if (SupportOptimizedFind()) { - searchHandle = ::FindFirstFileExW( - buffer, FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, - FIND_FIRST_EX_LARGE_FETCH); - } else { - searchHandle = ::FindFirstFileExW( - buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, nullptr, 0); + for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { + FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); + if ((entry.get() != nullptr) && !entry->isFromArchive()) { + return entry->getOrigin(ignore); + } } - if (searchHandle != INVALID_HANDLE_VALUE) { - BOOL result = true; - - while (result) { - if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - if ((wcscmp(findData.cFileName, L".") != 0) && - (wcscmp(findData.cFileName, L"..") != 0)) { - int offset = _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE, L"\\%ls", findData.cFileName); - // recurse into subdirectories - getSubDirectory(findData.cFileName, true, origin.getID())->addFiles(origin, buffer, bufferOffset + offset); - } - } else { - insert(findData.cFileName, origin, findData.ftLastWriteTime, L"", -1); - } - - result = ::FindNextFileW(searchHandle, &findData); + // 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 != -1){ + return res; } } - std::sort(m_SubDirectories.begin(), m_SubDirectories.end(), &DirCompareByName); - ::FindClose(searchHandle); + return *(m_Origins.begin()); } -void DirectoryEntry::addFiles( - FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, - const std::wstring &archiveName, int order) +std::vector DirectoryEntry::getFiles() const { - // add files - for (unsigned int fileIdx = 0; fileIdx < archiveFolder->getNumFiles(); ++fileIdx) { - BSA::File::Ptr file = archiveFolder->getFile(fileIdx); - insert(ToWString(file->getName(), true), origin, fileTime, archiveName, order); - } - - // recurse into subdirectories - for (unsigned int folderIdx = 0; folderIdx < archiveFolder->getNumSubFolders(); ++folderIdx) { - BSA::Folder::Ptr folder = archiveFolder->getSubFolder(folderIdx); - DirectoryEntry *folderEntry = getSubDirectoryRecursive(ToWString(folder->getName(), true), true, origin.getID()); + std::vector result; - folderEntry->addFiles(origin, folder, fileTime, archiveName, order); + for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { + result.push_back(m_FileRegister->getFile(iter->second)); } + + return result; } -bool DirectoryEntry::removeFile(const std::wstring &filePath, int *origin) +DirectoryEntry *DirectoryEntry::findSubDirectory( + const std::wstring &name, bool alreadyLowerCase) const { - 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); - DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); + SubDirectoriesMap::const_iterator itor; - if (entry != nullptr) { - return entry->removeFile(rest, origin); + if (alreadyLowerCase) { + itor = m_SubDirectoriesMap.find(name); } else { - return false; - } -} - -void DirectoryEntry::removeDirRecursive() -{ - while (!m_Files.empty()) { - m_FileRegister->removeFile(m_Files.begin()->second); + itor = m_SubDirectoriesMap.find(ToLowerCopy(name)); } - m_FilesLookup.clear(); - - for (DirectoryEntry *entry : m_SubDirectories) { - entry->removeDirRecursive(); - delete entry; + if (itor == m_SubDirectoriesMap.end()) { + return nullptr; } - m_SubDirectories.clear(); - m_SubDirectoriesMap.clear(); + return itor->second; } -void DirectoryEntry::removeDir(const std::wstring &path) +DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(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(); - - bool found = false; - for (auto iter2=m_SubDirectoriesMap.begin(); iter2!=m_SubDirectoriesMap.end(); ++iter2) { - if (iter2->second == entry) { - m_SubDirectoriesMap.erase(iter2); - found = true; - break; - } - } + return getSubDirectoryRecursive(path, false, -1); +} - if (!found) { - log::error("entry {} not in sub directories map", entry->getName()); - } +const FileEntry::Ptr DirectoryEntry::findFile( + const std::wstring &name, bool alreadyLowerCase) const +{ + FilesLookup::const_iterator iter; - m_SubDirectories.erase(iter); - delete entry; - break; - } - } + if (alreadyLowerCase) { + iter = m_FilesLookup.find(FileKey(name)); } else { - std::wstring dirName = path.substr(0, pos); - std::wstring rest = path.substr(pos + 1); - DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); + iter = m_FilesLookup.find(FileKey(ToLowerCopy(name))); + } - if (entry != nullptr) { - entry->removeDir(rest); - } + if (iter != m_FilesLookup.end()) { + return m_FileRegister->getFile(iter->second); + } else { + return FileEntry::Ptr(); } } -bool DirectoryEntry::remove(const std::wstring &fileName, int *origin) +const FileEntry::Ptr DirectoryEntry::findFile(const FileKey& key) const { - const auto lcFileName = ToLowerCopy(fileName); + auto iter = m_FilesLookup.find(key); - auto iter = m_Files.find(lcFileName); - bool b = false; + if (iter != m_FilesLookup.end()) { + return m_FileRegister->getFile(iter->second); + } else { + return FileEntry::Ptr(); + } +} - if (iter != m_Files.end()) { - if (origin != nullptr) { - FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); - if (entry.get() != nullptr) { - bool ignore; - *origin = entry->getOrigin(ignore); - } +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) { + FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); + if (entry->isFromArchive(archiveName)) { + return true; } + } - b = m_FileRegister->removeFile(iter->second); + return false; +} + +const FileEntry::Ptr DirectoryEntry::searchFile( + const std::wstring &path, const DirectoryEntry **directory) const +{ + if (directory != nullptr) { + *directory = nullptr; } - if (m_Files.size() != m_FilesLookup.size()) { - DebugBreak(); + if ((path.length() == 0) || (path == L"*")) { + // no file name -> the path ended on a (back-)slash + if (directory != nullptr) { + *directory = this; + } + + return FileEntry::Ptr(); } - return b; -} + const size_t len = path.find_first_of(L"\\/"); -void DirectoryEntry::insert( - const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, - const std::wstring &archive, int order) -{ - std::wstring fileNameLower = ToLowerCopy(fileName); - auto iter = m_Files.find(fileNameLower); - FileEntry::Ptr file; + if (len == std::string::npos) { + // no more path components + auto iter = m_Files.find(ToLowerCopy(path)); - if (iter != m_Files.end()) { - file = m_FileRegister->getFile(iter->second); + 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 = m_FileRegister->createFile(fileName, this); - m_Files.emplace(fileNameLower, file->getIndex()); - m_FilesLookup.emplace(fileNameLower, file->getIndex()); - } + // file is in a subdirectory, recurse into the matching subdirectory + std::wstring pathComponent = path.substr(0, len); + DirectoryEntry *temp = findSubDirectory(pathComponent); - if (m_Files.size() != m_FilesLookup.size()) { - DebugBreak(); - } + if (temp != nullptr) { + if (len >= path.size()) { + log::error(QObject::tr("unexpected end of path")); + return FileEntry::Ptr(); + } - file->addOrigin(origin.getID(), fileTime, archive, order); - origin.addFile(file->getIndex()); -} + return temp->searchFile(path.substr(len + 1), directory); + } + } -bool DirectoryEntry::hasContentsFromOrigin(int originID) const -{ - return m_Origins.find(originID) != m_Origins.end(); + return FileEntry::Ptr(); } void DirectoryEntry::insertFile( @@ -848,12 +946,12 @@ void DirectoryEntry::removeFile(FileEntry::Index index) m_FilesLookup.erase(iter); } else { log::error( - "file \"{}\" not in directory for lookup \"{}\"", + QObject::tr("file \"{}\" not in directory for lookup \"{}\"").toStdString(), m_FileRegister->getFile(index)->getName(), this->getName()); } } else { log::error( - "file \"{}\" not in directory \"{}\" for lookup, directory empty", + QObject::tr("file \"{}\" not in directory \"{}\" for lookup, directory empty").toStdString(), m_FileRegister->getFile(index)->getName(), this->getName()); } @@ -882,188 +980,209 @@ void DirectoryEntry::removeFile(FileEntry::Index index) } } -void DirectoryEntry::removeFiles(const std::set &indices) +bool DirectoryEntry::removeFile(const std::wstring &filePath, int *origin) { - for (auto iter = m_Files.begin(); iter != m_Files.end();) { - if (indices.find(iter->second) != indices.end()) { - iter = m_Files.erase(iter); - } else { - ++iter; - } + size_t pos = filePath.find_first_of(L"\\/"); + + if (pos == std::string::npos) { + return this->remove(filePath, origin); } - for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) { - if (indices.find(iter->second) != indices.end()) { - iter = m_FilesLookup.erase(iter); - } else { - ++iter; - } + std::wstring dirName = filePath.substr(0, pos); + std::wstring rest = filePath.substr(pos + 1); + DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); + + if (entry != nullptr) { + return entry->removeFile(rest, origin); + } else { + return false; } } -bool DirectoryEntry::containsArchive(std::wstring archiveName) +void DirectoryEntry::removeDir(const std::wstring &path) { - for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { - FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); - if (entry->isFromArchive(archiveName)) { - return true; - } - } + size_t pos = path.find_first_of(L"\\/"); - return false; -} + if (pos == std::string::npos) { + for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) { + DirectoryEntry *entry = *iter; -int DirectoryEntry::anyOrigin() const -{ - bool ignore; + if (CaseInsensitiveEqual(entry->getName(), path)) { + entry->removeDirRecursive(); - for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { - FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); - if ((entry.get() != nullptr) && !entry->isFromArchive()) { - return entry->getOrigin(ignore); + bool found = false; + for (auto iter2=m_SubDirectoriesMap.begin(); iter2!=m_SubDirectoriesMap.end(); ++iter2) { + if (iter2->second == entry) { + m_SubDirectoriesMap.erase(iter2); + found = true; + break; + } + } + + if (!found) { + log::error("entry {} not in sub directories map", entry->getName()); + } + + m_SubDirectories.erase(iter); + delete entry; + break; + } } - } + } else { + std::wstring dirName = path.substr(0, pos); + std::wstring rest = path.substr(pos + 1); + DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); - // 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 != -1){ - return res; + if (entry != nullptr) { + entry->removeDir(rest); } } - - return *(m_Origins.begin()); } -bool DirectoryEntry::originExists(const std::wstring &name) const +bool DirectoryEntry::remove(const std::wstring &fileName, int *origin) { - return m_OriginConnection->exists(name); -} + const auto lcFileName = ToLowerCopy(fileName); -FilesOrigin &DirectoryEntry::getOriginByID(int ID) const -{ - return m_OriginConnection->getByID(ID); -} + auto iter = m_Files.find(lcFileName); + bool b = false; -FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const -{ - return m_OriginConnection->getByName(name); -} + if (iter != m_Files.end()) { + if (origin != nullptr) { + FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); + if (entry.get() != nullptr) { + bool ignore; + *origin = entry->getOrigin(ignore); + } + } -std::vector DirectoryEntry::getFiles() const -{ - std::vector result; + b = m_FileRegister->removeFile(iter->second); + } - for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { - result.push_back(m_FileRegister->getFile(iter->second)); + if (m_Files.size() != m_FilesLookup.size()) { + DebugBreak(); } - return result; + return b; } -const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const DirectoryEntry **directory) const +bool DirectoryEntry::hasContentsFromOrigin(int originID) 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 FileEntry::Ptr(); - } - - 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)); + return m_Origins.find(originID) != m_Origins.end(); +} - if (iter != m_Files.end()) { - return m_FileRegister->getFile(iter->second); - } else if (directory != nullptr) { - DirectoryEntry *temp = findSubDirectory(path); - if (temp != nullptr) { - *directory = temp; - } - } +FilesOrigin &DirectoryEntry::createOrigin( + const std::wstring &originName, const std::wstring &directory, int priority) +{ + if (m_OriginConnection->exists(originName)) { + FilesOrigin &origin = m_OriginConnection->getByName(originName); + origin.enable(true); + return origin; } 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 FileEntry::Ptr(); - } + return m_OriginConnection->createOrigin( + originName, directory, priority, m_FileRegister, m_OriginConnection); + } +} - return temp->searchFile(path.substr(len + 1), directory); +void DirectoryEntry::removeFiles(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; } } - return FileEntry::Ptr(); + for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) { + if (indices.find(iter->second) != indices.end()) { + iter = m_FilesLookup.erase(iter); + } else { + ++iter; + } + } } -DirectoryEntry *DirectoryEntry::findSubDirectory( - const std::wstring &name, bool alreadyLowerCase) const +void DirectoryEntry::insert( + const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, + const std::wstring &archive, int order) { - SubDirectoriesMap::const_iterator itor; + std::wstring fileNameLower = ToLowerCopy(fileName); + auto iter = m_Files.find(fileNameLower); + FileEntry::Ptr file; - if (alreadyLowerCase) { - itor = m_SubDirectoriesMap.find(name); + if (iter != m_Files.end()) { + file = m_FileRegister->getFile(iter->second); } else { - itor = m_SubDirectoriesMap.find(ToLowerCopy(name)); + file = m_FileRegister->createFile(fileName, this); + m_Files.emplace(fileNameLower, file->getIndex()); + m_FilesLookup.emplace(fileNameLower, file->getIndex()); } - if (itor == m_SubDirectoriesMap.end()) { - return nullptr; + if (m_Files.size() != m_FilesLookup.size()) { + DebugBreak(); } - return itor->second; + file->addOrigin(origin.getID(), fileTime, archive, order); + origin.addFile(file->getIndex()); } -DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &path) +void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset) { - return getSubDirectoryRecursive(path, false, -1); -} + WIN32_FIND_DATAW findData; -const FileEntry::Ptr DirectoryEntry::findFile( - const std::wstring &name, bool alreadyLowerCase) const -{ - FilesLookup::const_iterator iter; + _snwprintf_s(buffer + bufferOffset, MAXPATH_UNICODE - bufferOffset, _TRUNCATE, L"\\*"); - if (alreadyLowerCase) { - iter = m_FilesLookup.find(FileKey(name)); - } else { - iter = m_FilesLookup.find(FileKey(ToLowerCopy(name))); - } + HANDLE searchHandle = nullptr; - if (iter != m_FilesLookup.end()) { - return m_FileRegister->getFile(iter->second); + if (SupportOptimizedFind()) { + searchHandle = ::FindFirstFileExW( + buffer, FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, + FIND_FIRST_EX_LARGE_FETCH); } else { - return FileEntry::Ptr(); + searchHandle = ::FindFirstFileExW( + buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, nullptr, 0); } -} -const FileEntry::Ptr DirectoryEntry::findFile(const FileKey& key) const -{ - auto iter = m_FilesLookup.find(key); + if (searchHandle != INVALID_HANDLE_VALUE) { + BOOL result = true; - if (iter != m_FilesLookup.end()) { - return m_FileRegister->getFile(iter->second); - } else { - return FileEntry::Ptr(); + while (result) { + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if ((wcscmp(findData.cFileName, L".") != 0) && + (wcscmp(findData.cFileName, L"..") != 0)) { + int offset = _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE, L"\\%ls", findData.cFileName); + // recurse into subdirectories + getSubDirectory(findData.cFileName, true, origin.getID())->addFiles(origin, buffer, bufferOffset + offset); + } + } else { + insert(findData.cFileName, origin, findData.ftLastWriteTime, L"", -1); + } + + result = ::FindNextFileW(searchHandle, &findData); + } } + + std::sort(m_SubDirectories.begin(), m_SubDirectories.end(), &DirCompareByName); + ::FindClose(searchHandle); } -bool DirectoryEntry::hasFile(const std::wstring& name) const +void DirectoryEntry::addFiles( + FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, + const std::wstring &archiveName, int order) { - return m_Files.contains(ToLowerCopy(name)); + // add files + for (unsigned int fileIdx = 0; fileIdx < archiveFolder->getNumFiles(); ++fileIdx) { + BSA::File::Ptr file = archiveFolder->getFile(fileIdx); + insert(ToWString(file->getName(), true), origin, fileTime, archiveName, order); + } + + // recurse into subdirectories + for (unsigned int folderIdx = 0; folderIdx < archiveFolder->getNumSubFolders(); ++folderIdx) { + BSA::Folder::Ptr folder = archiveFolder->getSubFolder(folderIdx); + DirectoryEntry *folderEntry = getSubDirectoryRecursive(ToWString(folder->getName(), true), true, origin.getID()); + + folderEntry->addFiles(origin, folder, fileTime, archiveName, order); + } } DirectoryEntry *DirectoryEntry::getSubDirectory( @@ -1111,146 +1230,21 @@ DirectoryEntry *DirectoryEntry::getSubDirectoryRecursive( } } - -FileRegister::FileRegister(boost::shared_ptr originConnection) - : m_OriginConnection(originConnection) -{ - LEAK_TRACE; -} - -FileRegister::~FileRegister() -{ - LEAK_UNTRACE; - m_Files.clear(); -} - -FileEntry::Index FileRegister::generateIndex() -{ - static std::atomic sIndex(0); - return sIndex++; -} - -bool FileRegister::indexValid(FileEntry::Index index) const -{ - return (m_Files.find(index) != m_Files.end()); -} - -FileEntry::Ptr FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent) -{ - FileEntry::Index index = generateIndex(); - m_Files[index] = FileEntry::Ptr(new FileEntry(index, name, parent)); - - return m_Files[index]; -} - -FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const -{ - auto iter = m_Files.find(index); - - if (iter != m_Files.end()) { - return iter->second; - } else { - return FileEntry::Ptr(); - } -} - -void FileRegister::unregisterFile(FileEntry::Ptr file) -{ - bool ignore; - - // unregister from origin - int originID = file->getOrigin(ignore); - m_OriginConnection->getByID(originID).removeFile(file->getIndex()); - const auto& alternatives = file->getAlternatives(); - - for (auto iter = alternatives.begin(); iter != alternatives.end(); ++iter) { - m_OriginConnection->getByID(iter->first).removeFile(file->getIndex()); - } - - // unregister from directory - if (file->getParent() != nullptr) { - file->getParent()->removeFile(file->getIndex()); - } -} - -bool FileRegister::removeFile(FileEntry::Index index) -{ - auto iter = m_Files.find(index); - - if (iter != m_Files.end()) { - unregisterFile(iter->second); - m_Files.erase(index); - return true; - } else { - log::error(QObject::tr("invalid file index for remove: {}").toStdString(), index); - return false; - } -} - -void FileRegister::removeOrigin(FileEntry::Index index, int originID) -{ - auto iter = m_Files.find(index); - - if (iter != m_Files.end()) { - if (iter->second->removeOrigin(originID)) { - unregisterFile(iter->second); - m_Files.erase(iter); - } - } else { - log::error(QObject::tr("invalid file index for remove (for origin): {}").toStdString(), index); - } -} - -void FileRegister::removeOriginMulti( - std::set indices, int originID, time_t notAfter) +void DirectoryEntry::removeDirRecursive() { - std::vector removedFiles; - - for (auto iter = indices.begin(); iter != indices.end(); ) { - auto pos = m_Files.find(*iter); - - if (pos != m_Files.end() - && (pos->second->lastAccessed() < notAfter) - && pos->second->removeOrigin(originID)) { - removedFiles.push_back(pos->second); - m_Files.erase(pos); - ++iter; - } else { - indices.erase(iter++); - } + while (!m_Files.empty()) { + m_FileRegister->removeFile(m_Files.begin()->second); } - // optimization: this is only called when disabling an origin and in this case - // we don't have to remove the file from the origin - - // need to remove files from their parent directories. multiple ways to go - // about this: - // a) for each file, search its parents file-list (preferably by name) and - // remove what is found - // b) gather the parent directories, go through the file list for each once - // and remove all files that have been removed - // - // the latter should be faster when there are many files in few directories. - // since this is called only when disabling an origin that is probably - // frequently the case - - std::set parents; - for (const FileEntry::Ptr &file : removedFiles) { - if (file->getParent() != nullptr) { - parents.insert(file->getParent()); - } - } + m_FilesLookup.clear(); - for (DirectoryEntry *parent : parents) { - parent->removeFiles(indices); + for (DirectoryEntry *entry : m_SubDirectories) { + entry->removeDirRecursive(); + delete entry; } -} -void FileRegister::sortOrigins() -{ - for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { - iter->second->sortOrigins(); - } + m_SubDirectories.clear(); + m_SubDirectoriesMap.clear(); } } // namespace MOShared -- cgit v1.3.1