diff options
Diffstat (limited to 'src/shared/directoryentry.cpp')
| -rw-r--r-- | src/shared/directoryentry.cpp | 1096 |
1 files changed, 545 insertions, 551 deletions
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<MOShared::FileRegister> fileRegister,
- boost::shared_ptr<MOShared::OriginConnection> originConnection) :
- m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path),
- m_Priority(priority), m_FileRegister(fileRegister),
- m_OriginConnection(originConnection)
+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<FileEntry::Ptr> FilesOrigin::getFiles() const
-{
- std::vector<FileEntry::Ptr> 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<FileEntry::Index> 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,19 +381,119 @@ 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;
+
+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)
+{
+ LEAK_TRACE;
+}
+
+FilesOrigin::FilesOrigin(
+ int ID, const std::wstring &name, const std::wstring &path, int priority,
+ boost::shared_ptr<MOShared::FileRegister> fileRegister,
+ boost::shared_ptr<MOShared::OriginConnection> originConnection) :
+ m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path),
+ m_Priority(priority), m_FileRegister(fileRegister),
+ m_OriginConnection(originConnection)
+{
+ LEAK_TRACE;
+}
+
+FilesOrigin::~FilesOrigin()
+{
+ 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<FileEntry::Ptr> FilesOrigin::getFiles() const
+{
+ std::vector<FileEntry::Ptr> 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<FileEntry::Index> 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;
+ }
}
}
@@ -497,6 +501,148 @@ bool FileEntry::isFromArchive(std::wstring archiveName) const }
+FileRegister::FileRegister(boost::shared_ptr<OriginConnection> 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<FileEntry::Index> indices, int originID, time_t notAfter)
+{
+ std::vector<FileEntry::Ptr> 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<DirectoryEntry*> 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<FileEntry::Index> 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),
@@ -524,11 +670,6 @@ DirectoryEntry::~DirectoryEntry() clear();
}
-const std::wstring &DirectoryEntry::getName() const
-{
- return m_Name;
-}
-
void DirectoryEntry::clear()
{
m_Files.clear();
@@ -542,19 +683,6 @@ void DirectoryEntry::clear() m_SubDirectoriesMap.clear();
}
-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 {
- return m_OriginConnection->createOrigin(
- originName, directory, priority, m_FileRegister, m_OriginConnection);
- }
-}
-
void DirectoryEntry::addFromOrigin(
const std::wstring &originName, const std::wstring &directory, int priority)
{
@@ -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<FileEntry::Ptr> 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<FileEntry::Ptr> 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"\\/");
+ SubDirectoriesMap::const_iterator itor;
- if (pos == std::string::npos) {
- return this->remove(filePath, origin);
+ if (alreadyLowerCase) {
+ itor = m_SubDirectoriesMap.find(name);
+ } else {
+ itor = m_SubDirectoriesMap.find(ToLowerCopy(name));
}
- 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;
+ if (itor == m_SubDirectoriesMap.end()) {
+ return nullptr;
}
+
+ return itor->second;
}
-void DirectoryEntry::removeDirRecursive()
+DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &path)
{
- while (!m_Files.empty()) {
- m_FileRegister->removeFile(m_Files.begin()->second);
- }
+ return getSubDirectoryRecursive(path, false, -1);
+}
- m_FilesLookup.clear();
+const FileEntry::Ptr DirectoryEntry::findFile(
+ const std::wstring &name, bool alreadyLowerCase) const
+{
+ FilesLookup::const_iterator iter;
- for (DirectoryEntry *entry : m_SubDirectories) {
- entry->removeDirRecursive();
- delete entry;
+ if (alreadyLowerCase) {
+ iter = m_FilesLookup.find(FileKey(name));
+ } else {
+ iter = m_FilesLookup.find(FileKey(ToLowerCopy(name)));
}
- m_SubDirectories.clear();
- m_SubDirectoriesMap.clear();
+ if (iter != m_FilesLookup.end()) {
+ return m_FileRegister->getFile(iter->second);
+ } else {
+ return FileEntry::Ptr();
+ }
}
-void DirectoryEntry::removeDir(const std::wstring &path)
+const FileEntry::Ptr DirectoryEntry::findFile(const FileKey& key) const
{
- 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;
- }
- }
-
- if (!found) {
- log::error("entry {} not in sub directories map", entry->getName());
- }
+ auto iter = m_FilesLookup.find(key);
- m_SubDirectories.erase(iter);
- delete entry;
- break;
- }
- }
+ if (iter != m_FilesLookup.end()) {
+ return m_FileRegister->getFile(iter->second);
} else {
- std::wstring dirName = path.substr(0, pos);
- std::wstring rest = path.substr(pos + 1);
- DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false);
-
- if (entry != nullptr) {
- entry->removeDir(rest);
- }
+ return FileEntry::Ptr();
}
}
-bool DirectoryEntry::remove(const std::wstring &fileName, int *origin)
+bool DirectoryEntry::hasFile(const std::wstring& name) const
{
- const auto lcFileName = ToLowerCopy(fileName);
-
- auto iter = m_Files.find(lcFileName);
- bool b = false;
+ return m_Files.contains(ToLowerCopy(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);
- }
+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<FileEntry::Index> &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<FileEntry::Ptr> DirectoryEntry::getFiles() const
-{
- std::vector<FileEntry::Ptr> 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 m_Origins.find(originID) != m_Origins.end();
+}
- return FileEntry::Ptr();
+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 {
+ return m_OriginConnection->createOrigin(
+ originName, directory, priority, m_FileRegister, m_OriginConnection);
}
+}
- 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;
- }
+void DirectoryEntry::removeFiles(const std::set<FileEntry::Index> &indices)
+{
+ for (auto iter = m_Files.begin(); iter != m_Files.end();) {
+ if (indices.find(iter->second) != indices.end()) {
+ iter = m_Files.erase(iter);
+ } else {
+ ++iter;
}
- } 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 temp->searchFile(path.substr(len + 1), directory);
+ for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) {
+ if (indices.find(iter->second) != indices.end()) {
+ iter = m_FilesLookup.erase(iter);
+ } else {
+ ++iter;
}
}
-
- return FileEntry::Ptr();
}
-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> originConnection)
- : m_OriginConnection(originConnection)
-{
- LEAK_TRACE;
-}
-
-FileRegister::~FileRegister()
-{
- LEAK_UNTRACE;
- m_Files.clear();
-}
-
-FileEntry::Index FileRegister::generateIndex()
-{
- static std::atomic<FileEntry::Index> 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<FileEntry::Index> indices, int originID, time_t notAfter)
+void DirectoryEntry::removeDirRecursive()
{
- std::vector<FileEntry::Ptr> 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<DirectoryEntry*> 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
|
