/*
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