diff --git a/plugins/archivezip/GenericFileSystem.h b/plugins/archivezip/GenericFileSystem.h index 46abf811b8..af955710f6 100644 --- a/plugins/archivezip/GenericFileSystem.h +++ b/plugins/archivezip/GenericFileSystem.h @@ -1,5 +1,6 @@ #pragma once +#include "iarchive.h" #include "string/string.h" #include "os/path.h" @@ -9,103 +10,126 @@ namespace archive { -inline unsigned int path_get_depth(const char* path) +namespace { - unsigned int depth = 0; - while(path != 0 && path[0] != '\0') - { - path = strchr(path, '/'); - if(path != 0) - { - ++path; - } - ++depth; - } - return depth; + +// Returns the depth of a given path string, basically counting +// the number of parts in between forward slashes +// "" => 0 +// "dds/" => 1 +// "dds/textures/" => 2 +// "dds/textures/darkmod/" => 3 +// "dds/textures/darkmod/wood/" => 4 +// "dds/textures/darkmod/wood/boards/" => 5 +// "dds/textures/darkmod/wood/boards/dark_rough.dds" => 6 +inline unsigned int getPathDepth(const char* path) +{ + unsigned int depth = 0; + + while (path != 0 && path[0] != '\0') + { + path = strchr(path, '/'); + + if (path != 0) + { + ++path; + } + + ++depth; + } + + return depth; +} + } /// \brief A generic unix-style file-system which maps paths to files and directories. /// Provides average O(log n) find and insert methods. /// \param file_type The data type which represents a file. -template +template class GenericFileSystem { - class Path - { - std::string m_path; - unsigned int m_depth; - public: - Path(const std::string& path) : - m_path(path), - m_depth(path_get_depth(m_path.c_str())) - {} - - Path(const char* start, std::size_t length) : - m_path(start, length), - m_depth(path_get_depth(m_path.c_str())) - {} - - bool operator<(const Path& other) const - { - return string_less_nocase(c_str(), other.c_str()); - } - unsigned int depth() const - { - return m_depth; - } - const char* c_str() const - { - return m_path.c_str(); - } - - const std::string& string() const + class Path { - return m_path; - } - }; + private: + std::string _path; + unsigned int _depth; + public: + Path(const std::string& path) : + _path(path), + _depth(getPathDepth(_path.c_str())) + {} + + Path(const char* start, std::size_t length) : + Path(std::string(start, length)) + {} + + bool operator<(const Path& other) const + { + return string_less_nocase(c_str(), other.c_str()); + } + + unsigned int depth() const + { + return _depth; + } + + const char* c_str() const + { + return _path.c_str(); + } + + const std::string& string() const + { + return _path; + } + }; +public: class Entry { - std::shared_ptr _file; + std::shared_ptr _record; public: Entry() {} - Entry(const std::shared_ptr& file) : - _file(file) + Entry(const std::shared_ptr& record) : + _record(record) {} - std::shared_ptr& file() + std::shared_ptr& getRecord() { - return _file; + return _record; } - bool is_directory() const + bool isDirectory() const { - return !_file; + return !_record; } }; - typedef std::map Entries; - Entries m_entries; +private: + typedef std::map Entries; + Entries _entries; public: - typedef typename Entries::iterator iterator; - typedef typename Entries::value_type value_type; - typedef Entry entry_type; - - iterator begin() - { - return m_entries.begin(); - } - iterator end() - { - return m_entries.end(); - } + typedef typename Entries::iterator iterator; + typedef typename Entries::value_type value_type; + typedef Entry entry_type; + + iterator begin() + { + return _entries.begin(); + } + + iterator end() + { + return _entries.end(); + } void clear() { - m_entries.clear(); + _entries.clear(); } /// \brief Returns the file at \p path. @@ -121,63 +145,68 @@ class GenericFileSystem // greebo: Take the substring from start to end Path dir(start, end - start); - // And insert it as directory (NULL) - m_entries.insert(value_type(dir, Entry(NULL))); + // And insert it as directory + _entries.insert(value_type(dir, Entry())); end = path_remove_directory(end); } - return m_entries[path]; + return _entries[path]; } - /// \brief Returns the file at \p path or end() if not found. - iterator find(const Path& path) - { - return m_entries.find(path); - } - - iterator begin(const std::string& root) - { - if(root[0] == '\0') - { - return m_entries.begin(); - } - iterator i = m_entries.find(root); - if(i == m_entries.end()) - { - return i; - } - return ++i; - } - - /// \brief Performs a depth-first traversal of the file-system subtree rooted at \p root. - /// Traverses the entire tree if \p root is "". - /// Calls \p visitor.file() with the path to each file relative to the filesystem root. - /// Calls \p visitor.directory() with the path to each directory relative to the filesystem root. - template - void traverse(visitor_type visitor, const std::string& root) - { - unsigned int start_depth = path_get_depth(root.c_str()); - unsigned int skip_depth = 0; - for(iterator i = begin(root); i != end() && i->first.depth() > start_depth; ++i) - { - if(i->first.depth() == skip_depth) - { - skip_depth = 0; - } - if(skip_depth == 0) - { - if(!i->second.is_directory()) - { - visitor.file(i->first.string()); - } - else if(visitor.directory(i->first.string(), i->first.depth() - start_depth)) - { - skip_depth = i->first.depth(); - } - } - } - } + /// \brief Returns the file at \p path or end() if not found. + iterator find(const Path& path) + { + return _entries.find(path); + } + + /// \brief Performs a depth-first traversal of the file-system subtree rooted at \p root. + /// Traverses the entire tree if \p root is "". + /// Calls \p visitor.file() with the path to each file relative to the filesystem root. + /// Calls \p visitor.directory() with the path to each directory relative to the filesystem root. + void traverse(Archive::VisitorFunc visitor, const std::string& root) + { + unsigned int start_depth = getPathDepth(root.c_str()); + unsigned int skip_depth = 0; + + for (iterator i = begin(root); i != end() && i->first.depth() > start_depth; ++i) + { + if (i->first.depth() == skip_depth) + { + skip_depth = 0; + } + + if (skip_depth == 0) + { + if (!i->second.isDirectory()) + { + visitor.file(i->first.string()); + } + else if (visitor.directory(i->first.string(), i->first.depth() - start_depth)) + { + skip_depth = i->first.depth(); + } + } + } + } + +private: + iterator begin(const std::string& root) + { + if (root.empty()) + { + return _entries.begin(); + } + + iterator i = _entries.find(root); + + if (i == _entries.end()) + { + return i; + } + + return ++i; + } }; } diff --git a/plugins/archivezip/ZipArchive.cpp b/plugins/archivezip/ZipArchive.cpp index f3c5c2fb1b..d12c8c5b64 100644 --- a/plugins/archivezip/ZipArchive.cpp +++ b/plugins/archivezip/ZipArchive.cpp @@ -58,9 +58,9 @@ ArchiveFilePtr ZipArchive::openFile(const std::string& name) { ZipFileSystem::iterator i = _filesystem.find(name); - if (i != _filesystem.end() && !i->second.is_directory()) + if (i != _filesystem.end() && !i->second.isDirectory()) { - const std::shared_ptr& file = i->second.file(); + const std::shared_ptr& file = i->second.getRecord(); FileInputStream::size_type position = 0; @@ -98,9 +98,9 @@ ArchiveTextFilePtr ZipArchive::openTextFile(const std::string& name) { ZipFileSystem::iterator i = _filesystem.find(name); - if (i != _filesystem.end() && !i->second.is_directory()) + if (i != _filesystem.end() && !i->second.isDirectory()) { - const std::shared_ptr& file = i->second.file(); + const std::shared_ptr& file = i->second.getRecord(); { // Guard against concurrent access @@ -142,7 +142,7 @@ ArchiveTextFilePtr ZipArchive::openTextFile(const std::string& name) bool ZipArchive::containsFile(const std::string& name) { ZipFileSystem::iterator i = _filesystem.find(name); - return i != _filesystem.end() && !i->second.is_directory(); + return i != _filesystem.end() && !i->second.isDirectory(); } void ZipArchive::forEachFile(VisitorFunc visitor, const std::string& root) @@ -213,19 +213,19 @@ void ZipArchive::readZipRecord() if (os::isDirectory(path)) { - _filesystem[path].file().reset(); + _filesystem[path].getRecord().reset(); } else { - ZipFileSystem::entry_type& file = _filesystem[path]; + ZipFileSystem::entry_type& entry = _filesystem[path]; - if (!file.is_directory()) + if (!entry.isDirectory()) { rWarning() << "Zip archive " << _fullPath << " contains duplicated file: " << path << std::endl; } else { - file.file().reset(new ZipRecord(position, + entry.getRecord().reset(new ZipRecord(position, compressed_size, uncompressed_size, (compression_mode == Z_DEFLATED) ? ZipRecord::eDeflated : ZipRecord::eStored));