本章主要介绍我自己设计的一套文件系统,该系统的主要功能是方便用户使用统一的方式加载资源文件,不论资源文件的存储位置,是否进行压缩。在统一文件加载方式后,我们在开发过程中,可以直接进行系统目录下的文件操作,并方便的对其进行修改。而当项目完成并将资源打包后,程序也可以利用同一套API直接进行打包的资源文件的操作,不需要额外的修改代码,方便了开发、调试与发布。

文件树结构

file_system_structure
File System Strucutre

文件系统

文件系统对外提供了操作文件的统一接口,在使用之前需要通过挂载的方式来确定各个子文件系统的虚拟路径,为后续操作做准备。在完成文件系统初始化后,即可通过统一接口操作文件。

#pragma once
#include "FileSystem/Basic/File.h"
#include "FileSystem/Basic/FileSystem.h"
#include "FileSystem/Basic/VirtualNode.h"
#include "FileSystem/Basic/VirtualBlock.h"
#include "Pattern/Singleton.h"
 
#include <string>
#include <gsl/gsl>
 
namespace Rocket {
    // TODO : make it global static, singleton
    class VirtualFileSystem { // : _implements_ AbstractSingleton<VirtualFileSystem> {
    public:
        //using FileSystemMap = std::unordered_map<std::string, FileSystemPtr>;
        using FileSystemMap = std::vector<std::pair<std::string, FileSystemPtr>>;
    public:
        // Mount and Unmount
        [[nodiscard]] bool MountFileSystem(const FileSystemPtr& fs, const std::string& path);
        [[nodiscard]] bool UnmountFileSystem(const FileSystemPtr& fs);
        [[nodiscard]] bool UnmountFileSystem(const std::string& path);
        // VFS
        [[nodiscard]] inline VirtualBlockPtr RootBlock() const { return root; }
        // [[nodiscard]] inline const VNodeMap& NodeMap() const { return node_map; }
        // [[nodiscard]] inline const VBlockMap& BlockMap() const { return block_map; }
        [[nodiscard]] VNodeList VNodes(const std::string& dir) const;
        [[nodiscard]] VBlockList VBlocks(const std::string& dir) const;
        // Basic Judgement
        [[nodiscard]] bool IsFileExists(const std::string& file_path) const;
        [[nodiscard]] bool IsDirExists(const std::string& dir_path) const;
        [[nodiscard]] bool IsFile(const std::string& file_path) const;
        [[nodiscard]] bool IsDir(const std::string& file_path) const;
        // File Operation
        [[nodiscard]] FilePtr GetFilePointer(const std::string& file_path);
        void OpenFile(const FilePtr& file, int32_t mode);
        void CloseFile(const FilePtr& file);
        [[nodiscard]] std::size_t ReadFile(const FilePtr& file, FileBuffer* data);
        [[nodiscard]] std::size_t WriteFile(FilePtr& file, const FileBuffer& data);
        [[nodiscard]] bool CreateFile(const std::string& file_path);
        [[nodiscard]] bool RemoveFile(const std::string& file_path);
        [[nodiscard]] bool MoveFile(const std::string& src, const std::string& dst);
        [[nodiscard]] bool RenameFile(const std::string& src, const std::string& dst);
        [[nodiscard]] bool CopyFile(const std::string& src, const std::string& dst);
        [[nodiscard]] std::size_t FileSize(const FilePtr& file) const;
        // Dir Operation
        [[nodiscard]] bool CreateDir(const std::string& dir_path);
        [[nodiscard]] bool RemoveDir(const std::string& dir_path);
        [[nodiscard]] bool MoveDir(const std::string& src, const std::string& dst);
        [[nodiscard]] bool RenameDir(const std::string& src, const std::string& dst);
        [[nodiscard]] bool CopyDir(const std::string& src, const std::string& dst);
 
    private:
        void SetupBlockFileSystem(VirtualBlockPtr& root, const FileSystemPtr& fs);
 
    private:
        VirtualBlockPtr root = nullptr;
        FileSystemMap mounted_file_system;
    };
 
    CLASS_PTR(VirtualFileSystem);
}
VirtualFileSystem.h
#include "FileSystem/VirtualFileSystem.h"
#include "FileSystem/FileSystemFactory.h"
#include "FileSystem/Basic/VirtualUtils.h"
#include "Utils/StringUtils.h"
#include "Log/Instrumentor.h"
 
#include <vector>
#include <string>
 
namespace Rocket {
    void VirtualFileSystem::SetupBlockFileSystem(VirtualBlockPtr& root, const FileSystemPtr& fs) {
        root->file_system = fs;
        for(auto& block : root->block_map) {
            SetupBlockFileSystem(block.second, fs);
        }
    }
 
    bool VirtualFileSystem::MountFileSystem(const FileSystemPtr& fs, const std::string& path) {
        // 1. Generate Root Block
        if(root == nullptr) {
            root = std::make_shared<VirtualBlock>();
            root->path = "/";
        }
        // 2. Initialize fs
        if(!fs->IsInitialized()) {
            fs->Initialize();
        }
        auto fs_root = fs->RootBlock();
        if(fs_root == nullptr) {
            RK_WARN(File, "Empty Root Block")
            return false;
        }
        // 3. generate target block
        auto final_block = CreateVirtualBlock(root, path);
        if(final_block == nullptr) {
            RK_WARN(File, "Failed to mount file system");
            return false;
        }
        // 4. Setup fs for blocks
        SetupBlockFileSystem(fs_root, fs);
        // 5. insert block data
        if(final_block->parent != nullptr) {
            fs_root->parent = final_block->parent;
            final_block->parent->block_map[fs_root->name] = fs_root;
        } else {
            root = fs_root;
        }
        return true;
    }
 
    bool VirtualFileSystem::UnmountFileSystem(const FileSystemPtr& fs) {
        return UnmountFileSystem(fs->VirtualPath());
    }
 
    bool VirtualFileSystem::UnmountFileSystem(const std::string& path) {
        // 1. find target block
        auto block = FindVirtualBlock(root, path);
        if(block == nullptr) { return false; }
        // 2. finalize fs
        auto fs = block->file_system;
        fs->Finalize();
        // 3. correct other block's data 
        if(block->parent == nullptr) {
            root = std::make_shared<VirtualBlock>();
            root->path = "/";
        } else {
            auto found = block->parent->block_map.find(block->name);
            block->parent->block_map.erase(found);
        }
        return true;
    }
 
    VNodeList VirtualFileSystem::VNodes(const std::string& dir) const {
        auto block = FindVirtualBlock(root, dir);
        if(block == nullptr) return {};
        VNodeList nodes = {};
        for(auto item : block->node_map) {
            nodes.push_back(item.second);
        }
        return nodes;
    }
 
    VBlockList VirtualFileSystem::VBlocks(const std::string& dir) const {
        auto block = FindVirtualBlock(root, dir);
        if(block == nullptr) return {};
        VBlockList blocks = {};
        for(auto item : block->block_map) {
            blocks.push_back(item.second);
        }
        return blocks;
    }
 
    bool VirtualFileSystem::IsFileExists(const std::string& file_path) const {
        auto node = FindVirtualNode(root, file_path);
        if(node == nullptr) {
            return false;
        } else {
            return true;
        }
    }
 
    bool VirtualFileSystem::IsDirExists(const std::string& dir_path) const {
        auto block = FindVirtualBlock(root, dir_path);
        if(block == nullptr) {
            return false;
        } else {
            return true;
        }
    }
 
    bool VirtualFileSystem::IsFile(const std::string& file_path) const {
        return IsFileExists(file_path); 
    }
 
    bool VirtualFileSystem::IsDir(const std::string& dir_path) const { 
        return IsDirExists(dir_path); 
    }
 
    FilePtr VirtualFileSystem::GetFilePointer(const std::string& file_path) {
        RK_PROFILE_FUNCTION();
        auto node = FindVirtualNode(root, file_path);
        if(node == nullptr) {
            return nullptr;
        } else {
            return node->vblock->file_system->GetFilePointer(file_path);
        }
    }
 
    void VirtualFileSystem::OpenFile(const FilePtr& file, int32_t mode) {
        if(file == nullptr) return;
        file->Open(mode); 
    }
 
    void VirtualFileSystem::CloseFile(const FilePtr& file) { 
        if(file == nullptr) return;
        file->Close(); 
    }
 
    std::size_t VirtualFileSystem::ReadFile(const FilePtr& file, FileBuffer* data) { 
        if(file == nullptr) return std::size_t(0);
        return file->Read(data); 
    }
 
    std::size_t VirtualFileSystem::WriteFile(FilePtr& file, const FileBuffer& data) { 
        if(file == nullptr) return std::size_t(0);
        return file->Write(data); 
    }
 
    bool VirtualFileSystem::CreateFile(const std::string& file_path) {
        auto block = FindDeepestExistVirtualBlock(root, file_path);
        if(block == nullptr) {
            return false;
        } else {
            auto result = block->file_system->CreateFile(file_path);
            SetupBlockFileSystem(block, block->file_system);
            return result;
        }
    }
 
    bool VirtualFileSystem::RemoveFile(const std::string& file_path) {
        if(!IsFileExists(file_path)) {
            RK_WARN(File, "File Not Exist {}", file_path);
            return false;
        } else {
            auto node = FindVirtualNode(root, file_path);
            return node->vblock->file_system->RemoveFile(file_path);
        }
    }
 
    bool VirtualFileSystem::CreateDir(const std::string& dir_path) {
        auto block = FindDeepestExistVirtualBlock(root, dir_path);
        if(block == nullptr) {
            return false;
        } else {
            auto result = block->file_system->CreateDir(dir_path);
            SetupBlockFileSystem(block, block->file_system);
            return result;
        }
    }
 
    bool VirtualFileSystem::RemoveDir(const std::string& dir_path) {
        if(!IsDirExists(dir_path)) {
            RK_WARN(File, "Dir Not Exist {}", dir_path);
            return false;
        } else {
            auto block = FindVirtualBlock(root, dir_path);
            return block->file_system->RemoveDir(dir_path);
        }
    }
}
VirtualFileSystem.cpp

子文件系统

在Rocket Engine中,设计了三种子文件系统,包括普通文件系统、基于压缩文件的文件系统和基于内存的文件系统。普通文件系统用于调试过程文件修改后快速测试,内存文件系统用于存放临时文件,基于压缩文件的文件系统用于提高文件加载速度并减少硬盘占用空间,并用于最终发布的系统中。支持三种文件系统采用统一的方式操作可以提高开发效率,并使文件操作对开发人员透明。
所有的文件操作均通过VirtualFileSystem的static接口来使用。通过VirtualFileSystem来操作文件时,首先查找文件对应的内部挂载的文件系统,随后调用文件系统的的接口实现操作。

#pragma once
#include "Basic/FileSystem.h"
 
#include <unordered_map>
#include <functional>
 
namespace Rocket {
    enum class FileSystemType : int32_t {
        Native = 0,
        Memory,
        Zip,
    };
 
    using FileSystemCreator = std::function<FileSystemPtr(const std::string&, const std::string&)>;
 
    class FileSystemFactory {
    public:
        static void RegisterFileSystem(FileSystemType type, const FileSystemCreator& creator);
        static void UnregisterFileSystem(FileSystemType type);
        static FileSystemPtr CreateFileSystem(FileSystemType type, const std::string& rpath, const std::string& vpath);
    private:
        static std::unordered_map<FileSystemType, FileSystemCreator> file_systems;
    };
}
FileSystemFactory.h
#include "FileSystem/FileSystemFactory.h"
#include "FileSystem/MemoryFile/MemoryFileSystem.h"
#include "FileSystem/NativeFile/NativeFileSystem.h"
#include "FileSystem/ZipFile/ZipFileSystem.h"
 
namespace Rocket {
    std::unordered_map<FileSystemType, FileSystemCreator> FileSystemFactory::file_systems = {};
 
    void FileSystemFactory::RegisterFileSystem(FileSystemType type, const FileSystemCreator& creator) {
        file_systems[type] = creator;
    }
 
    void FileSystemFactory::UnregisterFileSystem(FileSystemType type) {
        auto found = file_systems.find(type);
        if(found != file_systems.end()) {
            file_systems.erase(found);
        }
    }
 
    FileSystemPtr FileSystemFactory::CreateFileSystem(FileSystemType type, const std::string& rpath, const std::string& vpath) {
        // build-in filesystem
        switch(type) {
            case FileSystemType::Memory: {
                return std::make_shared<MemoryFileSystem>(rpath, vpath);
            };
            case FileSystemType::Zip: {
                return std::make_shared<ZipFileSystem>(rpath, vpath);
            };
            case FileSystemType::Native: {
                return std::make_shared<NativeFileSystem>(rpath, vpath);
            };
        }
        // self-defined filesystem
        auto found = file_systems.find(type);
        if(found != file_systems.end()) {
            return found->second(rpath, vpath);
        } else {
            return nullptr;
        }
    }
}
FileSystemFactory.cpp

文件系统基本类型

文件系统内部是模仿Linux的文件系统,使用File Node的方式表示文件系统的树形结构。

#pragma once
#include "FileSystem/Basic/VirtualNode.h"
 
#include <gsl/gsl>
 
namespace Rocket {
    namespace FileEnum {
        enum Mode {
            READ_BINARY = 1 << 0,
            WRITE_BINARY = 1 << 1,
            READWRITE_BINARY = READ_BINARY | WRITE_BINARY,
            READ_TEXT = 1 << 2,
            WRITE_TEXT = 1 << 3,
            READWRITE_TEXT = READ_TEXT | WRITE_TEXT,
            APPEND = 1 << 4,
            TRUNCATE = 1 << 5,
        };
 
        enum Origin {
            BEGIN = 0,
            END = 1,
            SET = 2,
        };
    }
 
    using FileByte = std::byte;
    using FileBuffer = gsl::span<FileByte>;
    using FileBufferPtr = std::shared_ptr<FileBuffer>;
 
    // File Handle
    _Interface_ File {
    public:
        virtual ~File() = default;
 
        [[nodiscard]] virtual VirtualNodePtr VNode() const = 0;
        [[nodiscard]] virtual std::string VirtualPath() const = 0;
        [[nodiscard]] virtual std::string RealPath() const = 0;
        virtual void Open(int32_t mode) = 0;
        virtual void Close() = 0;
        [[nodiscard]] virtual bool IsOpened() const = 0;
        [[nodiscard]] virtual bool IsReadOnly() const = 0;
        [[nodiscard]] virtual std::size_t Size() const = 0;
        [[nodiscard]] virtual std::size_t Seek(std::size_t offset, FileEnum::Origin origin) = 0;
        [[nodiscard]] virtual std::size_t Tell() = 0;
        // Should Pre-Allocate Memory
        // If Read Text, read will read [size - 1] bytes,
        // because last will be ['\0'] for string
        [[nodiscard]] virtual std::size_t Read(FileBuffer* data) = 0;
        // Write will write all data to file, has no different
        [[nodiscard]] virtual std::size_t Write(const FileBuffer& data) = 0;
    };
 
    CLASS_PTR(File);
}
File.h
#pragma once
#include "Core/MemoryDefine.h"
#include "Utils/StringUtils.h"
#include "Utils/Hashing.h"
#include "Log/Log.h"
 
#include <memory>
#include <string>
#include <exception>
#include <stdexcept>
 
namespace Rocket {
    class VirtualBlock;
    // Contain Basic and Additional File Infos
    // Can be modified if needed
    struct VirtualNode {
        // std::vector<std::string> stack = {};
        // File Full Name will be path + name
        std::string path = "";  // file basic path in vfs
        std::string name = "";  // file name
        std::shared_ptr<VirtualBlock> vblock = nullptr;
        // int32_t crc = 0;
        // TODO : add support for link function
        // std::shared_ptr<VirtualNode> vlink = nullptr;
    };
 
    CLASS_PTR(VirtualNode);
}
VirtualNode.h
#pragma once
#include "Core/MemoryDefine.h"
#include "FileSystem/Basic/VirtualNode.h"
#include "Utils/StringUtils.h"
#include "Log/Log.h"
 
#include <unordered_map>
#include <memory>
#include <string>
 
namespace Rocket {
    class FileSystem;
    // Store File System Info, Just like linux block and inode
    // Should Use in VirtualFileSystem
    struct VirtualBlock {
        using SubBlockMap = std::unordered_map<std::string, std::shared_ptr<VirtualBlock> >;
        using NodeMap = std::unordered_map<std::string, std::shared_ptr<VirtualNode> >;
 
        // if file_system == null, should look to parent block
        std::shared_ptr<FileSystem> file_system = nullptr;
        // if parent == nullptr, means that this is root dir
        std::shared_ptr<VirtualBlock> parent = nullptr;
        // Parent Name to find file
        std::string name = {};
        // Alias Path in VFS
        std::string path = {};
        // Store data
        SubBlockMap block_map = {};
        NodeMap node_map = {};
        // int32_t crc = 0;
    };
 
    CLASS_PTR(VirtualBlock);
}
VirtualBlock.h
#pragma once
#include "Core/MemoryDefine.h"
#include "FileSystem/Basic/File.h"
#include "FileSystem/Basic/VirtualNode.h"
#include "FileSystem/Basic/VirtualBlock.h"
#include "Utils/CRC.h"
 
#include <vector>
#include <string>
#include <unordered_map>
#include <gsl/gsl>
 
namespace Rocket {
    using VNodeList = std::vector<VirtualNodePtr>;
    using VBlockList = std::vector<VirtualBlockPtr>;
    using VNodeMap = std::unordered_map<std::string, VirtualNodePtr>;
    using VBlockMap = std::unordered_map<std::string, VirtualBlockPtr>;
 
    // File System will generate file and dir index map
    // which will make searching faster
    // Further optimization will be use crc hash code as search key instead of string
    _Interface_ FileSystem {
    public:
        FileSystem(const std::string& real_path, const std::string& virtual_path);
        virtual ~FileSystem() = default;
        // Basic Operation
        inline bool IsInitialized() const { return is_initialized; }
        // For File System
        [[nodiscard]] inline const std::string& VirtualPath() const { return virtual_path; }
        [[nodiscard]] inline const std::string& RealPath() const { return real_path; }
        [[nodiscard]] inline VirtualBlockPtr RootBlock() const { return root; }
        // [[nodiscard]] inline const VNodeMap& NodeMap() const { return node_map; }
        // [[nodiscard]] inline const VBlockMap& BlockMap() const { return block_map; }
        [[nodiscard]] VNodeList VNodes(const std::string& dir) const;
        [[nodiscard]] VBlockList VBlocks(const std::string& dir) const;
        // Basic Judgement
        [[nodiscard]] bool IsFileExists(const std::string& file_path) const;
        [[nodiscard]] bool IsDirExists(const std::string& dir_path) const;
        [[nodiscard]] bool IsFile(const std::string& file_path) const;
        [[nodiscard]] bool IsDir(const std::string& file_path) const;
        // File Operation
        void OpenFile(const FilePtr& file, int32_t mode);
        void CloseFile(const FilePtr& file);
        [[nodiscard]] std::size_t ReadFile(const FilePtr& file, FileBuffer* data);
        [[nodiscard]] std::size_t WriteFile(FilePtr& file, const FileBuffer& data);
 
        // Virtual Functions --------------------------------------------------------------
         
        // Basic Operation
        virtual void Initialize() = 0;
        virtual void Finalize() = 0;
        // For File System
        virtual void SetVirtualPath(const std::string& basic) = 0;
        virtual void SetRealPath(const std::string& alias) = 0;
        [[nodiscard]] virtual bool IsReadOnly() const = 0;
        // TODO : File Operation -- Unfinished
        [[nodiscard]] virtual FilePtr GetFilePointer(const std::string& file_path) = 0;
        [[nodiscard]] virtual bool CreateFile(const std::string& file_path) = 0;
        [[nodiscard]] virtual bool RemoveFile(const std::string& file_path) = 0;
        [[nodiscard]] virtual bool MoveFile(const std::string& src, const std::string& dst);
        [[nodiscard]] virtual bool RenameFile(const std::string& src, const std::string& dst);
        [[nodiscard]] virtual bool CopyFile(const std::string& src, const std::string& dst);
        [[nodiscard]] virtual std::size_t FileSize(const FilePtr& file) const;
        // TODO : Dir Operation -- Unfinished
        [[nodiscard]] virtual bool CreateDir(const std::string& dir_path) = 0;
        [[nodiscard]] virtual bool RemoveDir(const std::string& dir_path) = 0;
        [[nodiscard]] virtual bool MoveDir(const std::string& src, const std::string& dst);
        [[nodiscard]] virtual bool RenameDir(const std::string& src, const std::string& dst);
        [[nodiscard]] virtual bool CopyDir(const std::string& src, const std::string& dst);
 
    protected:
        VirtualBlockPtr root = nullptr;
        std::string real_path = "";
        std::string virtual_path = "";
        bool is_initialized = false;
    };
 
    CLASS_PTR(FileSystem);
}
FileSystem.h
#include "FileSystem/Basic/FileSystem.h"
#include "FileSystem/Basic/VirtualUtils.h"
#include "Utils/StringUtils.h"
#include "Log/Instrumentor.h"
 
namespace Rocket {
    FileSystem::FileSystem(const std::string& real_path, const std::string& virtual_path)
        : real_path(real_path), virtual_path(virtual_path) {}
 
    VNodeList FileSystem::VNodes(const std::string& dir) const {
        auto block = FindVirtualBlock(root, dir);
        if(block == nullptr) return {};
        VNodeList nodes = {};
        for(auto item : block->node_map) {
            nodes.push_back(item.second);
        }
        return nodes;
    }
 
    VBlockList FileSystem::VBlocks(const std::string& dir) const {
        auto block = FindVirtualBlock(root, dir);
        if(block == nullptr) return {};
        VBlockList blocks = {};
        for(auto item : block->block_map) {
            blocks.push_back(item.second);
        }
        return blocks;
    }
 
    bool FileSystem::IsFileExists(const std::string& file_path) const {
        RK_PROFILE_FUNCTION();
        std::string temp_path = file_path.substr(virtual_path.size());
        auto node = FindVirtualNode(root, temp_path);
        if(node == nullptr)
            return false;
        else
            return true;
    }
 
    bool FileSystem::IsDirExists(const std::string& dir_path) const {
        RK_PROFILE_FUNCTION();
        std::string temp_path = dir_path.substr(virtual_path.size());
        auto block = FindVirtualBlock(root, temp_path);
        if(block == nullptr)
            return false;
        else
            return true;
    }
 
    bool FileSystem::IsFile(const std::string& file_path) const {
        return IsFileExists(file_path); 
    }
 
    bool FileSystem::IsDir(const std::string& dir_path) const { 
        return IsDirExists(dir_path); 
    }
 
    void FileSystem::OpenFile(const FilePtr& file, int32_t mode) { 
        if(file == nullptr) return;
        file->Open(mode); 
    }
 
    void FileSystem::CloseFile(const FilePtr& file) { 
        if(file == nullptr) return;
        file->Close(); 
    }
 
    std::size_t FileSystem::ReadFile(const FilePtr& file, FileBuffer* data) { 
        if(file == nullptr) return std::size_t(0);
        return file->Read(data); 
    }
 
    std::size_t FileSystem::WriteFile(FilePtr& file, const FileBuffer& data) { 
        if(file == nullptr) return std::size_t(0);
        return file->Write(data); 
    }
 
    bool FileSystem::MoveFile(const std::string& src, const std::string& dst) {
        RK_WARN(File, "Move File Not Supported");
        return false;
    }
 
    bool FileSystem::RenameFile(const std::string& src, const std::string& dst) {
        RK_WARN(File, "Rename File Not Supported");
        return false;
    }
 
    bool FileSystem::CopyFile(const std::string& src, const std::string& dst) {
        RK_WARN(File, "Copy File Not Supported");
        return false;
    }
 
    std::size_t FileSystem::FileSize(const FilePtr& file) const {
        return file->Size();
    }
 
    bool FileSystem::MoveDir(const std::string& src, const std::string& dst) {
        RK_WARN(File, "Move Dir Not Supported");
        return false;
    }
 
    bool FileSystem::RenameDir(const std::string& src, const std::string& dst) {
        RK_WARN(File, "Rename Dir Not Supported");
        return false;
    }
 
    bool FileSystem::CopyDir(const std::string& src, const std::string& dst) {
        RK_WARN(File, "Copy Dir Not Supported");
        return false;
    }
}
FileSystem.cpp

文件系统工具类

#pragma once
#include "FileSystem/Basic/VirtualNode.h"
#include "FileSystem/Basic/VirtualBlock.h"
 
namespace Rocket {
    std::string GetVirtualPath(const VirtualNodePtr& vnode);
    std::string GetRealPath(const VirtualNodePtr& vnode);
    VirtualBlockPtr CreateVirtualBlock(VirtualBlockPtr& root, const std::string& path);
    VirtualBlockPtr FindVirtualBlock(const VirtualBlockPtr& root, const std::string& dir);
    VirtualBlockPtr FindDeepestExistVirtualBlock(const VirtualBlockPtr& root, const std::string& dir);
    VirtualNodePtr CreateVirtualNode(VirtualBlockPtr& root, const std::string& dirs);
    VirtualNodePtr FindVirtualNode(const VirtualBlockPtr& root, const std::string& dirs, const std::string& name);
    VirtualNodePtr FindVirtualNode(const VirtualBlockPtr& root, const std::string& file_path);
}
VirtualUtils.h
\#include "FileSystem/Basic/VirtualUtils.h"
#include "FileSystem/Basic/FileSystem.h"
#include "Log/Instrumentor.h"
 
#include <cassert>
 
namespace Rocket {
    std::string GetVirtualPath(const VirtualNodePtr& vnode) {
        assert(vnode != nullptr);
        return vnode->path + vnode->name;
    }
 
    std::string GetRealPath(const VirtualNodePtr& vnode) {
        assert(vnode != nullptr);
        auto& fs_real_path = vnode->vblock->file_system->RealPath();
        auto& fs_virtual_path = vnode->vblock->file_system->VirtualPath();
        return fs_real_path + vnode->path.substr(fs_virtual_path.size()) + vnode->name;
    }
 
    VirtualBlockPtr CreateVirtualBlock(VirtualBlockPtr& root, const std::vector<std::string>& dirs, int32_t level) {
        RK_PROFILE_FUNCTION();
        if(root == nullptr) return nullptr;
        if(dirs.size() == 0 || level == dirs.size()) return root;
        auto found = root->block_map.find(dirs[level]);
        if(found == root->block_map.end()) {
            VirtualBlockPtr block = std::make_shared<VirtualBlock>();
            block->parent = root;
            block->name = dirs[level];
            block->path = root->path + block->name + "/";
            root->block_map[block->name] = block;
            RK_TRACE(File, "Block Path: {}", block->path);
            return CreateVirtualBlock(block, dirs, level + 1);
        } else {
            return CreateVirtualBlock(found->second, dirs, level + 1);
        }
    }
 
    VirtualBlockPtr CreateVirtualBlock(VirtualBlockPtr& root, const std::string& path) {
        RK_PROFILE_FUNCTION();
        auto dir_ = Replace(path, "\\", "/");
        std::vector<std::string> dir_stack;
        SplitSingleChar(dir_, &dir_stack, '/');
        auto final_block = CreateVirtualBlock(root, dir_stack, 0);
        return final_block;
    }
 
    VirtualNodePtr CreateVirtualNode(VirtualBlockPtr& root, const std::string& file_path) {
        RK_PROFILE_FUNCTION();
        auto dir_ = Replace(file_path, "\\", "/");
        std::string dir;
        std::string file_name;
        SplitLastSingleChar(dir_, &dir, &file_name, '/');
        auto block = CreateVirtualBlock(root, dir);
        if(block == nullptr) {
            return nullptr;
        } else {
            auto found = block->node_map.find(file_name);
            if(found == block->node_map.end()) {
                // Create Virtual Node
                auto node_name = block->path + file_name;
                auto node = std::make_shared<VirtualNode>();
                root->node_map[file_name] = node;
                node->name = node_name;
                node->vblock = block;
                block->node_map[file_name] = node;
                return node;
            } else {
                return found->second;
            }
        }
    }
 
    VirtualBlockPtr FindVirtualBlock(const VirtualBlockPtr& root, const std::vector<std::string>& dirs, int32_t level) {
        RK_PROFILE_FUNCTION();
        // check invalid status
        if(dirs.size() == 0) return root;
        if(root == nullptr) return nullptr;
        if(level == dirs.size()) return root;
        // find if dir exist
        auto found = root->block_map.find(dirs[level]);
        // if not exist, return null
        if(found == root->block_map.end()) { return nullptr; }
        // check return or go deeper
        VirtualBlockPtr sub_block = found->second;
        return FindVirtualBlock(sub_block, dirs, level + 1);
    }
 
    VirtualBlockPtr FindVirtualBlock(const VirtualBlockPtr& root, const std::string& dir) {
        RK_PROFILE_FUNCTION();
        auto dir_ = Replace(dir, "\\", "/");
        std::vector<std::string> dir_stack;
        SplitSingleChar(dir_, &dir_stack, '/');
        auto block = FindVirtualBlock(root, dir_stack, 0);
        return block;
    }
 
    VirtualBlockPtr FindDeepestExistVirtualBlock(const VirtualBlockPtr& root, const std::vector<std::string>& dirs, int32_t level) {
        RK_PROFILE_FUNCTION();
        // check invalid status
        if(dirs.size() == 0) return root;
        if(root == nullptr) return nullptr;
        if(level == dirs.size()) return root;
        // find if dir exist
        auto found = root->block_map.find(dirs[level]);
        // we reach deepest dir
        if(found == root->block_map.end()) { return root; }
        // check return or go deeper
        VirtualBlockPtr sub_block = found->second;
        return FindDeepestExistVirtualBlock(sub_block, dirs, level + 1);
    }
 
    VirtualBlockPtr FindDeepestExistVirtualBlock(const VirtualBlockPtr& root, const std::string& dir) {
        RK_PROFILE_FUNCTION();
        auto dir_ = Replace(dir, "\\", "/");
        std::vector<std::string> dir_stack;
        SplitSingleChar(dir_, &dir_stack, '/');
        auto block = FindDeepestExistVirtualBlock(root, dir_stack, 0);
        return block;
    }
 
    VirtualNodePtr FindVirtualNode(const VirtualBlockPtr& root, const std::vector<std::string>& dirs, const std::string& name) {
        RK_PROFILE_FUNCTION();
        // find file parent block
        auto block = FindVirtualBlock(root, dirs, 0);
        if(block == nullptr) return nullptr;
        // find target file
        auto found = block->node_map.find(name);
        if(found == block->node_map.end()) { 
            return nullptr; 
        } else {
            return found->second;
        }
    }
 
    VirtualNodePtr FindVirtualNode(const VirtualBlockPtr& root, const std::string& dir, const std::string& name) {
        RK_PROFILE_FUNCTION();
        auto dir_ = Replace(dir, "\\", "/");
        std::vector<std::string> dir_stack;
        SplitSingleChar(dir_, &dir_stack, '/');
        return FindVirtualNode(root, dir_stack, name);
    }
 
    VirtualNodePtr FindVirtualNode(const VirtualBlockPtr& root, const std::string& file_path) {
        RK_PROFILE_FUNCTION();
        auto dir_ = Replace(file_path, "\\", "/");
        std::string dir;
        std::string file_name;
        SplitLastSingleChar(dir_, &dir, &file_name, '/');
        return FindVirtualNode(root, dir, file_name);
    }
}
VirtualUtils.cpp

普通文件系统

在这个文件系统中,文件有三个基本类型,普通文件,压缩文件中的子文件和内存文件。

#pragma once
#include "Core/MemoryDefine.h"
#include "FileSystem/Basic/File.h"
 
#include <fstream>
 
namespace Rocket {
    class NativeFile : _implements_ File {
    public:
        // For File System
        explicit NativeFile(const VirtualNodePtr& vnode_);
        explicit NativeFile(const std::string& path_, const std::string& virtual_path_);
        explicit NativeFile(const std::string& path_);
        virtual ~NativeFile() = default;
 
        inline VirtualNodePtr VNode() const final { return vnode; }
        inline std::string VirtualPath() const final { return virtual_path; }
        inline std::string RealPath() const final { return real_path; }
        inline std::size_t Size() const final { return file_size; }
        inline bool IsOpened() const final { return stream.is_open(); }
        inline bool IsReadOnly() const final { return is_read_only; }
 
        // Should Check File Status Before These Operation
        // these functions don't promise correct behavior 
        // if you give a wrong file name input
        void Open(int32_t mode) final;
        void Close() final;
        std::size_t Seek(std::size_t offset, FileEnum::Origin origin) final;
        std::size_t Tell() final;
        std::size_t Read(FileBuffer* data) final;
        std::size_t Write(const FileBuffer& data) final;
    private:
        void UpdateSize();
    private:
        VirtualNodePtr vnode = nullptr;
        std::string virtual_path = "";
        std::string real_path = "";
        std::fstream stream;
        std::size_t file_size = 0;
        int32_t mode = 0;
        bool is_read_only = false;
    };
 
    CLASS_PTR(NativeFile);
}
NativeFile.h
#include "FileSystem/NativeFile/NativeFile.h"
#include "FileSystem/Basic/VirtualUtils.h"
#include "FileSystem/NativeFile/NativeUtils.h"
#include "Log/Instrumentor.h"
#include "Log/Log.h"
 
#include <filesystem>
#include <cassert>
 
namespace Rocket {
    NativeFile::NativeFile(const VirtualNodePtr& vnode_) 
        : vnode(vnode_), real_path(GetVirtualPath(vnode)), virtual_path(GetRealPath(vnode)) {}
    NativeFile::NativeFile(const std::string& path_, const std::string& virtual_path_)
        : real_path(path_), virtual_path(virtual_path_) {}
    NativeFile::NativeFile(const std::string& path_)
        : real_path(path_) {}
 
    void NativeFile::Open(int32_t mode) {
        if(IsOpened() && (this->mode & mode) != 0) {
            RK_WARN(File, "Reopen Native File {}, {}", virtual_path, real_path);
            Seek(0, FileEnum::BEGIN);
            return;
        }
        this->mode = mode;
        std::ios_base::openmode open_mode = (std::ios_base::openmode)0x00;
        if (mode & FileEnum::READ_BINARY) {
            is_read_only = true;
            open_mode |= std::fstream::in;
            open_mode |= std::fstream::binary;
        }
        if (mode & FileEnum::WRITE_BINARY) {
            is_read_only = false;
            open_mode |= std::fstream::out;
            open_mode |= std::fstream::binary;
        }
        if (mode & FileEnum::READ_TEXT) {
            is_read_only = true;
            open_mode |= std::fstream::in; 
        }
        if (mode & FileEnum::WRITE_TEXT) {
            is_read_only = false;
            open_mode |= std::fstream::out;
        }
        if (mode & FileEnum::APPEND) {
            is_read_only = false;
            open_mode |= std::fstream::app;
        }
        if (mode & FileEnum::TRUNCATE) {
            is_read_only = false;
            open_mode |= std::fstream::trunc; 
        }
        // Check in filesystem to assure is_read_only
        if(!is_read_only) {
            auto result = IsNativeReadOnly(real_path);
            if(result) {
                is_read_only = true;
            }
        }
        RK_TRACE(File, "Native Path: {}, {}", virtual_path, real_path);
        stream.open(real_path, open_mode);
        // Calculate File Size
        UpdateSize();
    }
 
    void NativeFile::Close() {
        stream.close();
        file_size = std::size_t(0);
    }
 
    void NativeFile::UpdateSize() {
        if (IsOpened()) {
#if 1
            auto cur_pos = Tell();
            Seek(0, FileEnum::END);
            file_size = Tell();
            Seek(cur_pos, FileEnum::BEGIN);
#else
            std::filesystem::path path = this->real_path;
            auto err = std::error_code{};
            auto filesize = std::filesystem::file_size(path, err);
            if (filesize != static_cast<uintmax_t>(-1))
                file_size = filesize;
            else
                file_size = 0;
#endif
        } else {
            file_size = std::size_t(0);
        }
    }
 
    std::size_t NativeFile::Seek(std::size_t offset, FileEnum::Origin origin) {
        if (!IsOpened()) {
            return std::size_t(0);
        }
        std::ios_base::seekdir way;
        if (origin == FileEnum::BEGIN) {
            way = std::ios_base::beg;
        } else if (origin == FileEnum::END) {
            way = std::ios_base::end;
        } else {
            way = std::ios_base::cur;
        }
        stream.seekg(offset, way);
        stream.seekp(offset, way);
        return Tell();
    }
 
    std::size_t NativeFile::Tell() {
        if(!IsOpened()) {
            return std::size_t(0);
        } else {
            return static_cast<std::size_t>(stream.tellg());
        }
    }
 
    std::size_t NativeFile::Read(FileBuffer* buffer) {
        RK_PROFILE_FUNCTION();
        assert(buffer != nullptr);
        if (!IsOpened()) { return std::size_t(0); }
        std::size_t buffer_size = Size() - Tell();
        std::size_t max_size = 0;
        if(mode & FileEnum::READ_TEXT) {
            max_size = std::min(buffer->size()-1, buffer_size);
        } else {
            max_size = std::min(buffer->size(), buffer_size);
        }
        stream.read(reinterpret_cast<char*>(buffer->data()), static_cast<std::streamsize>(max_size));
        std::size_t read_count = 0;
        if (stream) {
            if(mode & FileEnum::READ_TEXT) {
                read_count = buffer->size() - 1;
            } else {
                read_count = buffer->size(); 
            }
        } else {
            read_count = static_cast<std::size_t>(stream.gcount());
        }
        if(mode & FileEnum::READ_TEXT) {
            buffer->data()[read_count] = std::byte(0);
        }
        return read_count;
    }
 
    std::size_t NativeFile::Write(const FileBuffer& data) {
        RK_PROFILE_FUNCTION();
        if (!IsOpened() || IsReadOnly()) {
            RK_WARN(File, "Write to Read Only File {}, {}", virtual_path, real_path);
            return 0;
        }
        stream.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
        UpdateSize();
        if (stream) {
            return data.size();
        }
        return static_cast<std::size_t>(stream.gcount());
    }
}
NativeFile.cpp
#pragma once
#include "FileSystem/Basic/FileSystem.h"
#include "FileSystem/NativeFile/NativeFile.h"
 
#include <filesystem>
 
namespace Rocket {
    class NativeFileSystem : _implements_ FileSystem {
    public:
        NativeFileSystem(const std::string& real_path);
        NativeFileSystem(const std::string& real_path, const std::string& virtual_path);
        virtual ~NativeFileSystem() = default;
        // Basic Operation
        void Initialize() final;
        void Finalize() final;
        // For File System
        void SetVirtualPath(const std::string& vpath) final;    // Will Update File System
        void SetRealPath(const std::string& rpath) final;       // Will Update File System
        // Basic Judgement in Virtual Path
        bool IsReadOnly() const final;
        // File Operation
        FilePtr GetFilePointer(const std::string& file_path) final;
        bool CreateFile(const std::string& file_path) final;
        bool RemoveFile(const std::string& file_path) final;
        // virtual bool MoveFile(const std::string& src, const std::string& dst);
        // virtual bool RenameFile(const std::string& src, const std::string& dst);
        // virtual bool CopyFile(const std::string& src, const std::string& dst);
        // virtual std::size_t FileSize(const FilePtr& file) const;
        // Dir Operation
        bool CreateDir(const std::string& dir_path) final;
        bool RemoveDir(const std::string& dir_path) final;
        // virtual bool MoveDir(const std::string& src, const std::string& dst);
        // virtual bool RenameDir(const std::string& src, const std::string& dst);
        // virtual bool CopyDir(const std::string& src, const std::string& dst);
    private:
        void NormalizePath();
        void CheckFileSystem();
        void GetRootName();
        void BuildVirtualSystem(const std::filesystem::path& path, VirtualBlockPtr& root);
        void BuildVirtualSystem();
    };
 
    CLASS_PTR(NativeFileSystem);
}
NativeFileSystem.h
#include "FileSystem/NativeFile/NativeFileSystem.h"
#include "FileSystem/NativeFile/NativeUtils.h"
#include "FileSystem/Basic/VirtualUtils.h"
#include "Utils/StringUtils.h"
#include "Log/Instrumentor.h"
#include "Log/Log.h"
 
#include <exception>
#include <stdexcept>
#include <vector>
#include <fstream>
 
namespace Rocket {
    NativeFileSystem::NativeFileSystem(const std::string& real_path) 
        : FileSystem(real_path, "/") {}
    NativeFileSystem::NativeFileSystem(const std::string& real_path, const std::string& virtual_path)
        : FileSystem(real_path, virtual_path) {}
 
    void NativeFileSystem::Initialize() {
        RK_PROFILE_FUNCTION();
        if(IsInitialized()) {
            RK_INFO(File, "File System Already Initialized");
            return;
        }
        // Normalize Path
        NormalizePath();
        // Check File System
        CheckFileSystem();
        // Init Root Block
        root = std::make_shared<VirtualBlock>();
        root->path = virtual_path;
        // Get Root Name
        GetRootName();
        // Build up virtual blocks recurisively
        BuildVirtualSystem();
        // Finish
        is_initialized = true;
    }
 
    void NativeFileSystem::NormalizePath() {
        RK_PROFILE_FUNCTION();
        real_path = Replace(real_path, "\\", "/");
        if(!EndsWith(real_path, "/")) 
            real_path += "/";
        virtual_path = Replace(virtual_path, "\\", "/");
        if(!EndsWith(virtual_path, "/")) 
            virtual_path += "/";
        if(!StartsWith(virtual_path, "/"))
            virtual_path = "/" + virtual_path;
        RK_TRACE(File, "Current path: {}, {}", virtual_path, real_path);
    }
 
    void NativeFileSystem::CheckFileSystem() {
        RK_PROFILE_FUNCTION();
        std::filesystem::path basic = real_path;
        if(!std::filesystem::exists(basic)) {
            RK_TRACE(File, "Native File System {} Not Exist", real_path);
            throw std::invalid_argument("Native File System Not Exist");
        }
        if(!std::filesystem::is_directory(basic)) {
            RK_TRACE(File, "Native File System {} Not A Path", real_path);
            throw std::invalid_argument("Native File System Not A Path");
        }
    }
 
    void NativeFileSystem::GetRootName() {
        RK_PROFILE_FUNCTION();
        std::vector<std::string> dir_stack;
        SplitSingleChar(virtual_path, &dir_stack, '/');
        if(dir_stack.size() > 0) {
            root->name = dir_stack.at(dir_stack.size() - 1);
        } else {
            root->name = "/";
        }
    }
 
    void NativeFileSystem::BuildVirtualSystem() {
        RK_PROFILE_FUNCTION();
        std::filesystem::path basic = real_path;
        RK_INFO(File, "Build Up Virtual Blocks");
        BuildVirtualSystem(basic, root);
    }
 
    void NativeFileSystem::BuildVirtualSystem(const std::filesystem::path& path, VirtualBlockPtr& root) {
        RK_PROFILE_FUNCTION();
        for (const auto& entry : std::filesystem::directory_iterator(path)) {
            auto filename = entry.path().filename();
            auto filename_str = filename.u8string();
            if (std::filesystem::is_directory(entry.status())) {
                // Add Block
                auto block = std::make_shared<VirtualBlock>();
                root->block_map[filename_str] = block;
                block->parent = root;
                block->name = filename_str;
                block->path = root->path + filename_str + "/";
                // RK_TRACE(File, "Block Name: {}", filename_str);
                RK_TRACE(File, "Block Path: {}", block->path);
                BuildVirtualSystem(entry, block);
            } else if (std::filesystem::is_regular_file(entry.status())) {
                // Add Node
                auto node_name = root->path + filename_str;
                auto node = std::make_shared<VirtualNode>();
                root->node_map[filename_str] = node;
                node->path = root->path;
                node->name = filename_str;
                node->vblock = root;
                // RK_TRACE(File, "Node Name: {}", node->file_name);
                RK_TRACE(File, "Node Path: {}", node->path + node->name);
            } else {
                // Except Special File
                auto abs_path = std::filesystem::absolute(entry);
                RK_TRACE(File, "Not Regular File: {}", abs_path.u8string());
                continue;
            }
        }
    }
 
    void NativeFileSystem::Finalize() {
        root = nullptr;
        real_path = "";
        virtual_path = "";
        is_initialized = false;
    }
 
    void NativeFileSystem::SetVirtualPath(const std::string& vpath) {
        RK_PROFILE_FUNCTION();
        virtual_path = vpath;
        if(!IsInitialized()) { return; }
        // Normalize Path
        NormalizePath();
        // Init Root Block
        root->path = virtual_path;
        root->node_map.clear();
        root->block_map.clear();
        // Get Root Name
        GetRootName();
        // Build up virtual blocks recurisively
        BuildVirtualSystem();
    }
 
    void NativeFileSystem::SetRealPath(const std::string& rpath) {
        RK_PROFILE_FUNCTION();
        real_path = rpath;
        if(!IsInitialized()) { return; }
        // Normalize Path
        NormalizePath();
        // Check File System
        CheckFileSystem();
        // Init Root Block
        root->path = virtual_path;
        root->node_map.clear();
        root->block_map.clear();
        // Get Root Name
        GetRootName();
        // Build up virtual blocks recurisively
        BuildVirtualSystem();
    }
 
    bool NativeFileSystem::IsReadOnly() const {
        return IsNativeReadOnly(real_path);
    }
 
    FilePtr NativeFileSystem::GetFilePointer(const std::string& file_path) {
        RK_PROFILE_FUNCTION();
        auto temp_path = Replace(file_path, "\\", "/");
        if(!StartsWith(temp_path, "/"))
            temp_path = "/" + temp_path;
        if(!IsFileExists(temp_path)) {
            RK_WARN(File, "File Not Exist {}", temp_path);
            return nullptr;
        }
        auto temp = temp_path.substr(virtual_path.size());
        auto full_path = real_path + temp;
        auto file = std::make_shared<NativeFile>(full_path, temp_path);
        return file;
    }
 
    bool NativeFileSystem::CreateFile(const std::string& file_path) {
        if(IsFileExists(file_path)) {
            RK_WARN(File, "File Existed {}", file_path);
            return false;
        }
        // Get File Dir and Name
        std::string temp_path = Replace(file_path, "\\", "/");
        std::string sub_path = temp_path.substr(virtual_path.size());
        std::string dir;
        std::string file_name;
        SplitLastSingleChar(sub_path, &dir, &file_name, '/');
        // Create Dir in Real File System
        auto dir_path = real_path + dir;
        std::filesystem::create_directories(dir_path);
        // Add info to VFS
        auto final_block = CreateVirtualBlock(root, dir);
        // Create File in Real File System
        auto full_path = real_path + sub_path;
        std::fstream stream(full_path, std::fstream::out | std::fstream::binary);
        if(!stream.is_open()) {
            RK_ERROR(File, "Failed to create file: {}, {}", file_path, full_path);
            return false;
        }
        // Add info to VFS
        auto node = std::make_shared<VirtualNode>();
        node->name = file_name;
        node->path = final_block->path;
        node->vblock = final_block;
        final_block->node_map[file_name] = node;
        return true;
    }
 
    bool NativeFileSystem::RemoveFile(const std::string& file_path) {
        if(!IsFileExists(file_path)) {
            RK_WARN(File, "File Not Existed {}", file_path);
            return false;
        }
        // Get File Dir and Name
        std::string temp_path = Replace(file_path, "\\", "/");
        std::string sub_path = temp_path.substr(virtual_path.size());
        std::string dir;
        std::string file_name;
        SplitLastSingleChar(sub_path, &dir, &file_name, '/');
        // Remove File in Real File System
        std::string real_file_path = real_path + sub_path;
        std::filesystem::remove(real_file_path);
        // Remove File Info in VFS
        VirtualBlockPtr block = FindVirtualBlock(root, dir);
        if(block == nullptr) {
            RK_WARN(File, "Virtual Block Not Existed {}, MUST Have Something Wrong", file_path);
            return false;
        }
        auto found = block->node_map.find(file_name);
        if(found == block->node_map.end()) {
            RK_WARN(File, "Virtual Node Not Existed {}, MUST Have Something Wrong", file_path);
            return false;
        } else {
            block->node_map.erase(found);
            return true;
        }
    }
 
    bool NativeFileSystem::CreateDir(const std::string& dir_path) {
        if(IsDirExists(dir_path)) {
            RK_WARN(File, "Dir Existed {}", dir_path);
            return false;
        }
        // Get File Dir and Name
        std::string temp_path = Replace(dir_path, "\\", "/");
        std::string sub_path = temp_path.substr(virtual_path.size());
        std::string dir;
        std::string file_name;
        SplitLastSingleChar(sub_path, &dir, &file_name, '/');
        // Create Dir in Real File System
        auto full_path = real_path + dir;
        std::filesystem::create_directories(full_path);
        // Add info to VFS
        auto final_block = CreateVirtualBlock(root, dir);
        return true;
    }
 
    bool NativeFileSystem::RemoveDir(const std::string& dir_path) {
        if(!IsDirExists(dir_path)) {
            RK_WARN(File, "Dir Not Existed {}", dir_path);
            return false;
        }
        // Get File Dir and Name
        std::string temp_path = Replace(dir_path, "\\", "/");
        std::string sub_path = temp_path.substr(virtual_path.size());
        // Remove Dir in Real File System
        auto full_path = real_path + sub_path;
        std::filesystem::remove_all(full_path);
        // Remove Info
        VirtualBlockPtr block = FindVirtualBlock(root, sub_path);
        if(block->parent != nullptr) {
            auto found = block->parent->block_map.find(block->name);
            block->parent->block_map.erase(found);
        } else {
            block->block_map.clear();
            block->node_map.clear();
            block->file_system.reset();
        }
        return true;
    }
}
NativeFileSystem.cpp
#pragma once
#include <filesystem>
#include <string>
 
namespace Rocket {
    static bool IsNativeReadOnly(const std::string& real_path) {
        auto perm = std::filesystem::status(real_path).permissions();
        if((perm & std::filesystem::perms::owner_write) == std::filesystem::perms::none) {
            return true;
        }
        return false;
    }
}
NativeUtils.h

内存文件系统

内存文件系统用于存放临时文件,用于快速的内存读写。

#pragma once
#include "Core/MemoryDefine.h"
#include "FileSystem/Basic/File.h"
 
namespace Rocket {
    class MemoryFile : _implements_ File {
        friend class MemoryFileSystem;
    public:
        // For File System
        explicit MemoryFile(const VirtualNodePtr& vnode_);
        explicit MemoryFile(const std::string& path_, const std::string& virtual_path_);
        explicit MemoryFile(const std::string& path_);
        virtual ~MemoryFile() = default;
 
        inline VirtualNodePtr VNode() const final { return vnode; }
        inline std::string VirtualPath() const final { return virtual_path; }
        inline std::string RealPath() const final { return real_path; }
        inline std::size_t Size() const final { return file_data->size(); }
        inline bool IsOpened() const final { return is_opened; }
        inline bool IsReadOnly() const final { return is_read_only; }
 
        // Should Check File Status Before These Operation
        // these functions don't promise correct behavior 
        // if you give a wrong file name input
        void Open(int32_t mode) final;
        void Close() final;
        std::size_t Seek(std::size_t offset, FileEnum::Origin origin) final;
        std::size_t Tell() final;
        std::size_t Read(FileBuffer* data) final;
        std::size_t Write(const FileBuffer& data) final;
    private:
        void SetFileBuffer(const FileBufferPtr& buffer) { file_data = buffer; }
    private:
        VirtualNodePtr vnode = nullptr;
        std::string virtual_path = "";
        std::string real_path = "";
        FileBufferPtr file_data;
        std::size_t seek_pos = 0;
        int32_t mode = 0;
        bool is_read_only = false;
        bool is_opened = false;
    };
 
    CLASS_PTR(MemoryFile);
}
MemoryFile.h
#include "FileSystem/MemoryFile/MemoryFile.h"
#include "FileSystem/Basic/VirtualUtils.h"
#include "Log/Instrumentor.h"
#include "Log/Log.h"
 
#include <algorithm>
#include <cassert>
 
namespace Rocket {
    MemoryFile::MemoryFile(const VirtualNodePtr& vnode_) 
        : vnode(vnode_), real_path(GetVirtualPath(vnode)), virtual_path(GetRealPath(vnode)) {}
    MemoryFile::MemoryFile(const std::string& path_, const std::string& virtual_path_)
        : real_path(path_), virtual_path(virtual_path_) {}
    MemoryFile::MemoryFile(const std::string& path_)
        : real_path(path_) {}
 
    void MemoryFile::Open(int32_t mode) {
        // Check Re-open case
        if(IsOpened() && (this->mode & mode) != 0) {
            RK_WARN(File, "Reopen Memory File {}, {}", virtual_path, real_path);
            Seek(0, FileEnum::BEGIN);
            return;
        }
        this->mode = mode;
        this->seek_pos = 0;
        this->is_read_only = true;
        if (mode & FileEnum::WRITE_BINARY) {
            is_read_only = false;
        }
        if (mode & FileEnum::WRITE_TEXT) {
            is_read_only = false;
        }
        if (mode & FileEnum::APPEND) {
            is_read_only = false;
            seek_pos = Size() > 0 ? Size() - 1 : 0;
        }
        if (mode & FileEnum::TRUNCATE) {
            is_read_only = false;
            if(file_data->size() > 0) delete [] file_data->data();
            *file_data = {nullptr, std::size_t(0)};
        }
        is_opened = true;
    }
 
    void MemoryFile::Close() {
        file_data = nullptr;
        is_read_only = true;
        is_opened = false;
        seek_pos = 0;
    }
 
    std::size_t MemoryFile::Seek(std::size_t offset, FileEnum::Origin origin) {
        if (!IsOpened()) { return std::size_t(0); }
        if (origin == FileEnum::BEGIN) {
            seek_pos = offset;
        } else if (origin == FileEnum::END) {
            seek_pos = Size() - offset;
        } else {
            seek_pos += offset;
        }
        seek_pos = std::max(seek_pos, std::size_t(0));
        seek_pos = std::min(seek_pos, Size() - 1);
        return Tell();
    }
 
    std::size_t MemoryFile::Tell() {
        return seek_pos;
    }
 
    std::size_t MemoryFile::Read(FileBuffer* buffer) {
        RK_PROFILE_FUNCTION();
        assert(buffer != nullptr);
        if (!IsOpened()) { return std::size_t(0); }
        // Copy Specify Memory Area
        std::size_t buffer_size = Size() - Tell();
        std::size_t max_size = 0;
        if(mode & FileEnum::READ_TEXT) {
            max_size = std::min(buffer->size()-1, buffer_size);
        } else {
            max_size = std::min(buffer->size(), buffer_size);
        }
        if (max_size > 0) {
            std::memcpy(buffer->data(), file_data->data(), max_size);
            if(mode & FileEnum::READ_TEXT) {
                buffer->data()[max_size] = std::byte(0);
            }
            Seek(max_size, FileEnum::BEGIN);
            return max_size;
        } else {
            return std::size_t(0);
        }
    }
 
    std::size_t MemoryFile::Write(const FileBuffer& data) {
        RK_PROFILE_FUNCTION();
        if (!IsOpened() || IsReadOnly()) { 
            RK_WARN(File, "Write to Read Only File {}, {}", virtual_path, real_path);
            return 0; 
        }
        std::size_t buffer_size = Size() - Tell();
        if(data.size() > buffer_size) {
            // Resize Buffer Area, Make New one First
            FileByte* temp_data = new FileByte[file_data->size() + data.size() - buffer_size];
            FileBuffer temp{temp_data, static_cast<std::size_t>(file_data->size() + data.size() - buffer_size)};
            // Copy Data
            std::memcpy(temp.data(), file_data->data(), file_data->size());
            // Delete Old Buffer Area
            delete [] file_data->data();
            // Update Buffer
            *file_data = std::move(temp);
        }
        // Copy Needed Data
        std::memcpy(file_data->data() + Tell(), data.data(), data.size());
        return data.size();
    }
}
MemoryFile.cpp
#pragma once
#include "FileSystem/Basic/FileSystem.h"
#include "FileSystem/MemoryFile/MemoryFile.h"
 
#include <unordered_map>
 
namespace Rocket {
    class MemoryFileSystem : _implements_ FileSystem {
        using MemoryFileMap = std::unordered_map<std::string, FileBufferPtr>;
    public:
        MemoryFileSystem(const std::string& real_path);
        MemoryFileSystem(const std::string& real_path, const std::string& virtual_path);
        virtual ~MemoryFileSystem() = default;
        // Basic Operation
        void Initialize() final;
        void Finalize() final;
        // For File System
        void SetVirtualPath(const std::string& basic) final;
        void SetRealPath(const std::string& alias) final;
        // Basic Judgement
        bool IsReadOnly() const final { return false; }
        // File Operation
        FilePtr GetFilePointer(const std::string& file_path) final;
        bool CreateFile(const std::string& file_path) final;
        bool RemoveFile(const std::string& file_path) final;
        // virtual bool MoveFile(const std::string& src, const std::string& dst);
        // virtual bool RenameFile(const std::string& src, const std::string& dst);
        // virtual bool CopyFile(const std::string& src, const std::string& dst);
        // virtual std::size_t FileSize(const FilePtr& file) const;
        // Dir Operation
        bool CreateDir(const std::string& dir_path) final;
        bool RemoveDir(const std::string& dir_path) final;
        // virtual bool MoveDir(const std::string& src, const std::string& dst);
        // virtual bool RenameDir(const std::string& src, const std::string& dst);
        // virtual bool CopyDir(const std::string& src, const std::string& dst);
    private:
        void NormalizePath();
        void GetRootName();
        void RemoveFileRecursive(VirtualBlockPtr& block);
    private:
        MemoryFileMap file_map;
    };
 
    CLASS_PTR(MemoryFileSystem);
}
MemoryFileSystem.h
#include "FileSystem/MemoryFile/MemoryFileSystem.h"
#include "FileSystem/Basic/VirtualUtils.h"
#include "Utils/StringUtils.h"
#include "Log/Instrumentor.h"
#include "Log/Log.h"
 
namespace Rocket {
    MemoryFileSystem::MemoryFileSystem(const std::string& real_path) 
        : FileSystem(real_path, "/") {}
    MemoryFileSystem::MemoryFileSystem(const std::string& real_path, const std::string& virtual_path)
        : FileSystem(real_path, virtual_path) {}
 
    void MemoryFileSystem::NormalizePath() {
        RK_PROFILE_FUNCTION();
        real_path = Replace(real_path, "\\", "/");
        if(!EndsWith(real_path, "/")) 
            real_path += "/";
        virtual_path = Replace(virtual_path, "\\", "/");
        if(!EndsWith(virtual_path, "/")) 
            virtual_path += "/";
        if(!StartsWith(virtual_path, "/"))
            virtual_path = "/" + virtual_path;
        RK_TRACE(File, "Current path: {}, {}", virtual_path, real_path);
    }
 
    void MemoryFileSystem::GetRootName() {
        RK_PROFILE_FUNCTION();
        std::vector<std::string> dir_stack;
        SplitSingleChar(virtual_path, &dir_stack, '/');
        if(dir_stack.size() > 0) {
            root->name = dir_stack.at(dir_stack.size() - 1);
        } else {
            root->name = "/";
        }
    }
 
    void MemoryFileSystem::Initialize() {
        if(IsInitialized()) {
            RK_INFO(File, "File System Already Initialized");
            return;
        }
        NormalizePath();
        root = std::make_shared<VirtualBlock>();
        root->path = virtual_path;
        GetRootName();
        is_initialized = true;
    }
 
    void MemoryFileSystem::Finalize() {
        root = nullptr;
        real_path = "";
        virtual_path = "";
        is_initialized = false;
        for(auto file : file_map) {
            if(file.second->data() != nullptr) {
                delete [] file.second->data();
            }
        }
        file_map.clear();
    }
 
    void MemoryFileSystem::SetVirtualPath(const std::string& vpath) {
        virtual_path = vpath;
    }
 
    void MemoryFileSystem::SetRealPath(const std::string& rpath) {
        real_path = rpath;
    }
 
    FilePtr MemoryFileSystem::GetFilePointer(const std::string& file_path) {
        auto temp_path = Replace(file_path, "\\", "/");
        if(!StartsWith(temp_path, "/"))
            temp_path = "/" + temp_path;
        if(!IsFileExists(temp_path)) {
            RK_WARN(File, "File Not Exist {}", temp_path);
            return nullptr;
        }
        auto full_path = temp_path.substr(virtual_path.size());
        auto file = std::make_shared<MemoryFile>(full_path, temp_path);
        // Set File Buffer Data
        auto found = file_map.find(temp_path);
        if(found == file_map.end()) {
            RK_WARN(File, "Empty File Data: {}", temp_path);
            file->SetFileBuffer(nullptr);
        } else {
            file->SetFileBuffer(found->second);
        }
        return file;
    }
 
    bool MemoryFileSystem::CreateFile(const std::string& file_path) {
        if(IsFileExists(file_path)) {
            RK_WARN(File, "File Existed {}", file_path);
            return false;
        }
        // Get File Dir and Name
        std::string temp_path = Replace(file_path, "\\", "/");
        std::string sub_path = temp_path.substr(virtual_path.size());
        std::string dir;
        std::string file_name;
        SplitLastSingleChar(sub_path, &dir, &file_name, '/');
        // Create Virtual Block
        auto final_block = CreateVirtualBlock(root, dir);
        // Create Memory File
        auto file = std::make_shared<FileBuffer>(nullptr, std::size_t(0));
        file_map[file_path] = file;
        // Add info to VFS
        auto node = std::make_shared<VirtualNode>();
        node->name = file_name;
        node->path = final_block->path;
        node->vblock = final_block;
        final_block->node_map[file_name] = node;
        return true;
    }
 
    bool MemoryFileSystem::RemoveFile(const std::string& file_path) {
        if(IsFileExists(file_path)) {
            RK_WARN(File, "File Not Existed {}", file_path);
            return false;
        }
        // Get File Dir and Name
        std::string temp_path = Replace(file_path, "\\", "/");
        std::string sub_path = temp_path.substr(virtual_path.size());
        std::string dir;
        std::string file_name;
        SplitLastSingleChar(sub_path, &dir, &file_name, '/');
        // Remove file in vfs
        VirtualBlockPtr block = FindVirtualBlock(root, dir);
        if(block == nullptr) {
            RK_WARN(File, "Virtual Block Not Existed {}, MUST Have Something Wrong", file_path);
            return false;
        }
        auto found = block->node_map.find(file_name);
        if(found == block->node_map.end()) {
            RK_WARN(File, "Virtual Node Not Existed {}, MUST Have Something Wrong", file_path);
            return false;
        } else {
            block->node_map.erase(found);
            return true;
        }
        // remove memory file storage
        auto found_file = file_map.find(file_path);
        if(found_file->second->data() != nullptr) {
            delete [] found_file->second->data();
        }
        file_map.erase(found_file);
        return true;
    }
 
    bool MemoryFileSystem::CreateDir(const std::string& dir_path) {
        if(IsFileExists(dir_path)) {
            RK_WARN(File, "File Existed {}", dir_path);
            return false;
        }
        // Get File Dir and Name
        std::string temp_path = Replace(dir_path, "\\", "/");
        std::string sub_path = temp_path.substr(virtual_path.size());
        // Create Virtual Block
        auto final_block = CreateVirtualBlock(root, sub_path);
        return true;
    }
 
    void MemoryFileSystem::RemoveFileRecursive(VirtualBlockPtr& block) {
        for(auto file : block->node_map) {
            auto found = file_map.find(file.second->path + file.second->name);
            auto file_ptr = found->second;
            if(file_ptr->data() != nullptr) {
                delete [] file_ptr->data();
            }
            file_map.erase(found);
        }
        for(auto block : block->block_map) {
            RemoveFileRecursive(block.second);
        }
    }
 
    bool MemoryFileSystem::RemoveDir(const std::string& dir_path) {
        if(!IsDirExists(dir_path)) {
            RK_WARN(File, "Dir Not Existed {}", dir_path);
            return false;
        }
        // Get File Dir and Name
        std::string temp_path = Replace(dir_path, "\\", "/");
        std::string sub_path = temp_path.substr(virtual_path.size());
        // Remove File Storage
        VirtualBlockPtr block = FindVirtualBlock(root, sub_path);
        RemoveFileRecursive(block);
        // Remove Info
        if(block->parent != nullptr) {
            auto found = block->parent->block_map.find(block->name);
            block->parent->block_map.erase(found);
        } else {
            block->block_map.clear();
            block->node_map.clear();
            block->file_system.reset();
        }
        return true;
    }
}
MemoryFileSystem.cpp

基于压缩文件的文件系统

使用压缩文件可以减少硬盘空间占用,并且提高数据读取速度。不过由于zip格式文件在CPU中解压花费的时间较多,在实际使用时应该采用更加快速的压缩算法,比如十分常用的LZ4算法。

#pragma once
#include "Core/MemoryDefine.h"
#include "FileSystem/Basic/File.h"
 
#include <zip.h>
 
namespace Rocket {
    class ZipFile : _implements_ File {
    public:
        // For File System
        explicit ZipFile(const VirtualNodePtr& vnode_);
        explicit ZipFile(const std::string& path_, const std::string& virtual_path_, zip_t* zip_);
        explicit ZipFile(const std::string& path_, zip_t* zip_);
        virtual ~ZipFile() = default;
 
        inline VirtualNodePtr VNode() const final { return vnode; }
        inline std::string VirtualPath() const final { return virtual_path; }
        inline std::string RealPath() const final { return real_path; }
        inline std::size_t Size() const final { return zip_file_status.size; }
        inline bool IsOpened() const final { return is_opened; }
        inline bool IsReadOnly() const final { return is_read_only; }
 
        // Should Check File Status Before These Operation
        // these functions don't promise correct behavior 
        // if you give a wrong file name input
        void Open(int32_t mode) final;
        void Close() final;
        std::size_t Seek(std::size_t offset, FileEnum::Origin origin) final;
        std::size_t Tell() final;
        std::size_t Read(FileBuffer* data) final;
        std::size_t Write(const FileBuffer& data) final;
    private:
        VirtualNodePtr vnode = nullptr;
        std::string virtual_path = "";
        std::string real_path = "";
        zip_t* zip_ptr = nullptr;
        zip_file_t* zip_file_ptr = nullptr;
        zip_stat_t zip_file_status = {};
        int32_t mode = 0;
        bool is_read_only = false;
        bool is_opened = false;
    };
 
    CLASS_PTR(ZipFile);
}
ZipFile.h
#include "FileSystem/ZipFile/ZipFile.h"
#include "FileSystem/Basic/VirtualUtils.h"
#include "FileSystem/ZipFile/ZipUtils.h"
#include "Log/Instrumentor.h"
#include "Log/Log.h"
 
#include <exception>
#include <stdexcept>
#include <algorithm>
#include <filesystem>
#include <cassert>
 
namespace Rocket {
    ZipFile::ZipFile(const VirtualNodePtr& vnode_) 
        : vnode(vnode_), real_path(GetVirtualPath(vnode)), virtual_path(GetRealPath(vnode)) {}
    ZipFile::ZipFile(const std::string& path_, const std::string& virtual_path_, zip_t* zip_) 
        : real_path(path_), virtual_path(virtual_path_), zip_ptr(zip_) {}
    ZipFile::ZipFile(const std::string& path_, zip_t* zip_) 
        : real_path(path_), zip_ptr(zip_) {}
 
    void ZipFile::Open(int32_t mode) {
        if(IsOpened()) return;
        this->mode = mode;
        is_read_only = true;
        if(mode & FileEnum::APPEND) {
            is_read_only = false;
        }
        if(mode & FileEnum::WRITE_BINARY || mode & FileEnum::WRITE_TEXT) {
            is_read_only = false;
        }
        if(mode & FileEnum::TRUNCATE) {
            is_read_only = false;
        }
 
        if(!is_read_only) {
            auto perm = std::filesystem::status(real_path).permissions();
            // Check Owner's Permission
            if((perm & std::filesystem::perms::owner_write) == std::filesystem::perms::none) {
                is_read_only = true;
            }
        }
 
        zip_file_ptr = OpenZipFile(zip_ptr, real_path);
        if(zip_file_ptr == nullptr) {
            RK_ERROR(File, "Fail to open zip file: {}", real_path);
        } else {
            is_opened = true;
        }
 
        GetZipFileStatus(zip_ptr, &zip_file_status, real_path);
        RK_TRACE(File, "Name: {}", zip_file_status.name);
        RK_TRACE(File, "\tIndex: {}", zip_file_status.index);
        RK_TRACE(File, "\tCompressed Size: {}", zip_file_status.comp_size);
        RK_TRACE(File, "\tSize: {}", zip_file_status.size);
        RK_TRACE(File, "\tCRC: {}", zip_file_status.crc);
    }
 
    void ZipFile::Close() {
        if(!IsOpened()) return;
        CloseZipFile(zip_file_ptr);
        is_opened = false;
    }
 
    std::size_t ZipFile::Seek(std::size_t offset, FileEnum::Origin origin) {
        RK_WARN(File, "Unable to Seek in Zip File");
        return 0;
    }
 
    std::size_t ZipFile::Tell() {
        RK_WARN(File, "Unable to Tell in Zip File");
        return 0;
    }
 
    std::size_t ZipFile::Read(FileBuffer* data) {
        RK_PROFILE_FUNCTION();
        assert(data != nullptr);
        if(!IsOpened()) return std::size_t(0);
        std::size_t read_size = 0;
        if(mode & FileEnum::READ_TEXT) {
            read_size = std::min(Size(), data->size()-1);
        } else {
            read_size = std::min(Size(), data->size());
        }
        {
            RK_PROFILE_SCOPE("Zip Read Part");
            zip_fread(zip_file_ptr, data->data(), read_size);
        }
        if(mode & FileEnum::READ_TEXT) {
            data->data()[read_size] = std::byte(0);
        }
        return read_size;
    }
 
    std::size_t ZipFile::Write(const FileBuffer& data) {
        RK_PROFILE_FUNCTION();
        FileBuffer origin_data = {};
        if(mode & FileEnum::APPEND) {
            // TODO : support append to zip file
            FileBuffer append_data = {new FileByte[Size() + data.size()], Size() + data.size()};
            FileBuffer origin_data = {append_data.data(), Size()};
            Read(&origin_data);
            std::memcpy((void*)(append_data.data() + Size()), data.data(), data.size());
        } else {
            origin_data = data;
        }
 
        zip_source_t* source;
        source = zip_source_buffer(zip_ptr, origin_data.data(), origin_data.size(), 0);
        if(source == nullptr) {
            RK_WARN(File, "error adding in zip file: {}", zip_strerror(zip_ptr));
            return 0;
        }
        // ZIP_FL_ENC_UTF_8 : string is UTF-8 encoded
        // ZIP_FL_ENC_CP437 : string is CP437 encoded
        // ZIP_FL_OVERWRITE : zip_file_add: if file with name exists, overwrite (replace) it
        // this will replace file in zip
        auto result = zip_file_add(zip_ptr, zip_file_status.name, source, ZIP_FL_OVERWRITE);
        std::size_t real_size = 0;
        if (result < 0) {
            zip_source_free(source);
            RK_WARN(File, "error adding in zip file: {}", zip_strerror(zip_ptr));
        } else {
            if(mode & FileEnum::APPEND) {
                real_size = data.size();
                delete [] origin_data.data();
            } else {
                real_size = data.size();
            }
        }
        //zip_source_free(source);
        // Update File Info
        GetZipFileStatus(zip_ptr, &zip_file_status, real_path);
        return real_size;
    }
}
ZipFile.cpp
#pragma once
#include "FileSystem/Basic/FileSystem.h"
#include "FileSystem/ZipFile/ZipFile.h"
 
#include <zip.h>
 
namespace Rocket {
    class ZipFileSystem : _implements_ FileSystem {
    public:
        ZipFileSystem(const std::string& real_path);
        ZipFileSystem(const std::string& real_path, const std::string& virtual_path);
        virtual ~ZipFileSystem() = default;
        // Basic Operation
        void Initialize() final;
        void Finalize() final;
        // For File System
        void SetVirtualPath(const std::string& basic) final;
        void SetRealPath(const std::string& alias) final;
        // Basic Judgement
        bool IsReadOnly() const final;
        // File Operation
        FilePtr GetFilePointer(const std::string& file_path) final;
        bool CreateFile(const std::string& file_path) final;
        bool RemoveFile(const std::string& file_path) final;
        // virtual bool MoveFile(const std::string& src, const std::string& dst);
        // virtual bool RenameFile(const std::string& src, const std::string& dst);
        // virtual bool CopyFile(const std::string& src, const std::string& dst);
        // virtual std::size_t FileSize(const FilePtr& file) const;
        // Dir Operation
        bool CreateDir(const std::string& dir_path) final;
        bool RemoveDir(const std::string& dir_path) final;
        // virtual bool MoveDir(const std::string& src, const std::string& dst);
        // virtual bool RenameDir(const std::string& src, const std::string& dst);
        // virtual bool CopyDir(const std::string& src, const std::string& dst);
 
    private:
        void NormalizePath();
        void CheckFileSystem();
        void GetRootName();
        void BuildVirtualSystem();
        // VirtualBlockPtr CreateVirtualBlock(VirtualBlockPtr& root, const std::vector<std::string>& dirs, int32_t level);
        // VirtualNodePtr CreateVirtualNode(VirtualBlockPtr& root, const std::vector<std::string>& dirs);
    private:
        zip_t* zip_archive = nullptr;
    };
 
    CLASS_PTR(ZipFileSystem);
}
ZipFileSystem.h
#include "FileSystem/ZipFile/ZipFileSystem.h"
#include "FileSystem/Basic/VirtualUtils.h"
#include "FileSystem/NativeFile/NativeUtils.h"
#include "FileSystem/ZipFile/ZipUtils.h"
#include "Log/Instrumentor.h"
#include "Utils/StringUtils.h"
 
#include <filesystem>
#include <memory>
 
namespace Rocket {
    ZipFileSystem::ZipFileSystem(const std::string& real_path) 
        : FileSystem(real_path, "/") {}
    ZipFileSystem::ZipFileSystem(const std::string& real_path, const std::string& virtual_path)
        : FileSystem(real_path, virtual_path) {}
 
    void ZipFileSystem::Initialize() {
        RK_PROFILE_FUNCTION();
        if(IsInitialized()) {
            RK_INFO(File, "File System Already Initialized");
            return;
        }
        NormalizePath();
        CheckFileSystem();
        root = std::make_shared<VirtualBlock>();
        root->path = virtual_path;
        GetRootName();
        // Open Zip
        {
            zip_archive = OpenZip(real_path, ZIP_CREATE);
            if(zip_archive == nullptr) {
                throw std::runtime_error("Unable to Open Zip");
            }
        }
        BuildVirtualSystem();
        is_initialized = true;
    }
 
    void ZipFileSystem::NormalizePath() {
        RK_PROFILE_FUNCTION();
        real_path = Replace(real_path, "\\", "/");
        virtual_path = Replace(virtual_path, "\\", "/");
        if(!EndsWith(virtual_path, "/")) 
            virtual_path += "/";
        if(!StartsWith(virtual_path, "/"))
            virtual_path = "/" + virtual_path;
        RK_TRACE(File, "Current path: {}, {}", virtual_path, real_path);
    }
 
    void ZipFileSystem::CheckFileSystem() {
        RK_PROFILE_FUNCTION();
        std::filesystem::path basic = real_path;
        if(!std::filesystem::is_regular_file(basic)) {
            RK_TRACE(File, "Zip File System {} Not Exist", real_path);
            throw std::invalid_argument("Zip File System Not Exist");
        }
    }
 
    void ZipFileSystem::GetRootName() {
        std::string dir;
        RK_PROFILE_FUNCTION();
        std::string file_name;
        SplitLastSingleChar(virtual_path, &dir, &file_name, '/');
        SplitLastSingleChar(dir, nullptr, &file_name, '/');
        if(file_name.length() > 0) {
            root->name = file_name;
        } else {
            root->name = "/";
        }
    }
 
    void ZipFileSystem::BuildVirtualSystem() {
        RK_PROFILE_FUNCTION();
        // Iterate Through Zip Files
        zip_int64_t num_entries = zip_get_num_entries(zip_archive, 0);
        for(zip_int64_t i = 0; i < num_entries; ++i) {
            const char* name = zip_get_name(zip_archive, i, 0);
            if(EndsWith(name, "/")) { // Is Dir
                VirtualBlockPtr block = CreateVirtualBlock(root, name);
            } else { // Is File
                VirtualNodePtr node = CreateVirtualNode(root, name);
            }
        }
    }
 
    void ZipFileSystem::Finalize() {
        CloseZip(zip_archive);
        root = nullptr;
        real_path = "";
        virtual_path = "";
        is_initialized = false;
    }
 
    void ZipFileSystem::SetVirtualPath(const std::string& vpath) {
        RK_PROFILE_FUNCTION();
        virtual_path = vpath;
        if(!IsInitialized()) { return; }
        // Normalize Path
        NormalizePath();
        // Init Root Block
        root->path = virtual_path;
        root->node_map.clear();
        root->block_map.clear();
        // Get Root Name
        GetRootName();
        // Build up virtual blocks recurisively
        BuildVirtualSystem();
    }
 
    void ZipFileSystem::SetRealPath(const std::string& rpath) {
        RK_PROFILE_FUNCTION();
        if(IsInitialized()) {
            RK_WARN(File, "Zip File System Not Able To Change Real Path After Initialize");
        } else {
            real_path = rpath;
        }
    }
 
    bool ZipFileSystem::IsReadOnly() const {
        return IsNativeReadOnly(real_path);
    }
 
    FilePtr ZipFileSystem::GetFilePointer(const std::string& file_path) {
        RK_PROFILE_FUNCTION();
        auto temp_path = Replace(file_path, "\\", "/");
        if(!StartsWith(temp_path, "/"))
            temp_path = "/" + temp_path;
        if(!IsFileExists(temp_path)) {
            RK_WARN(File, "File Not Exist {}", temp_path);
            return nullptr;
        }
        auto full_path = temp_path.substr(virtual_path.size());
        auto file = std::make_shared<ZipFile>(full_path, temp_path, zip_archive);
        return file;
    }
 
    bool ZipFileSystem::CreateFile(const std::string& file_path) {
        // if(IsFileExists(file_path)) {
        //     RK_WARN(File, "File Existed {}", file_path);
        //     return false;
        // }
        // // Get File Dir and Name
        // std::string temp_path = Replace(file_path, "\\", "/");
        // std::string sub_path = temp_path.substr(virtual_path.size());
        // std::string dir;
        // std::string file_name;
        // SplitLastSingleChar(sub_path, &dir, &file_name, '/');
        // // Create File in Real File System
        // const char buf[]="test";
        // zip_source_t* source = zip_source_buffer(zip_archive, buf, sizeof(buf), 0);
        // if (source == nullptr) {
        //     RK_WARN(File, "failed to create file source buffer : {}", zip_strerror(zip_archive));
        //     return false;
        // }
        // auto result = zip_file_add(zip_archive, file_path.c_str(), source, ZIP_FL_ENC_UTF_8 | ZIP_FL_OVERWRITE);
        // if (result < 0) {
        //     zip_source_free(source);
        //     RK_WARN(File, "error adding file: {}", zip_strerror(zip_archive));
        //     return false;
        // }
        // // Write to file
        // CloseZip(zip_archive);
        // zip_archive = OpenZip(real_path, ZIP_CREATE);
        // // Add info to VFS
        // auto final_block = CreateVirtualBlock(root, dir);
        // // Add info to VFS
        // auto node = std::make_shared<VirtualNode>();
        // node->name = file_name;
        // node->path = final_block->path;
        // node->vblock = final_block;
        // final_block->node_map[file_name] = node;
        // return true;
        RK_WARN(File, "Create File Not Supported In Zip");
        return false;
    }
 
    bool ZipFileSystem::RemoveFile(const std::string& file_path) {
        // if(!IsFileExists(file_path)) {
        //     RK_WARN(File, "File Not Existed {}", file_path);
        //     return false;
        // }
        // // Get File Dir and Name
        // std::string temp_path = Replace(file_path, "\\", "/");
        // std::string sub_path = temp_path.substr(virtual_path.size());
        // std::string dir;
        // std::string file_name;
        // SplitLastSingleChar(sub_path, &dir, &file_name, '/');
        // // Remove File in Real File System
        // auto file_index = zip_name_locate(zip_archive, sub_path.c_str(), ZIP_FL_ENC_GUESS);
        // zip_delete(zip_archive, file_index);
        // // Write to file
        // CloseZip(zip_archive);
        // zip_archive = OpenZip(real_path, ZIP_CREATE);
        // // Remove File Info in VFS
        // VirtualBlockPtr block = FindVirtualBlock(root, dir);
        // if(block == nullptr) {
        //     RK_WARN(File, "Virtual Block Not Existed {}, MUST Have Something Wrong", file_path);
        //     return false;
        // }
        // auto found = block->node_map.find(file_name);
        // if(found == block->node_map.end()) {
        //     RK_WARN(File, "Virtual Node Not Existed {}, MUST Have Something Wrong", file_path);
        //     return false;
        // } else {
        //     block->node_map.erase(found);
        //     return true;
        // }
        // return true;
        RK_WARN(File, "Remove File Not Supported In Zip");
        return false;
    }
 
    bool ZipFileSystem::CreateDir(const std::string& dir_path) {
        // if(IsDirExists(dir_path)) {
        //     RK_WARN(File, "Dir Existed {}", dir_path);
        //     return false;
        // }
        // // Get File Dir and Name
        // std::string temp_path = Replace(dir_path, "\\", "/");
        // std::string sub_path = temp_path.substr(virtual_path.size());
        // std::string dir;
        // std::string file_name;
        // SplitLastSingleChar(sub_path, &dir, &file_name, '/');
        // // Create Dir in Real File System
        // zip_dir_add(zip_archive, sub_path.c_str(), ZIP_FL_ENC_UTF_8);
        // // Write to file
        // CloseZip(zip_archive);
        // zip_archive = OpenZip(real_path, ZIP_CREATE);
        // // Add info to VFS
        // auto final_block = CreateVirtualBlock(root, dir);
        RK_WARN(File, "Create Dir Not Supported In Zip");
        return true;
    }
 
    bool ZipFileSystem::RemoveDir(const std::string& dir_path) {
        // if(!IsDirExists(dir_path)) {
        //     RK_WARN(File, "Dir Not Existed {}", dir_path);
        //     return false;
        // }
        // // Get File Dir and Name
        // std::string temp_path = Replace(dir_path, "\\", "/");
        // std::string sub_path = temp_path.substr(virtual_path.size());
        // // Remove Dir in Real File System
        // auto dir_index = zip_name_locate(zip_archive, sub_path.c_str(), ZIP_FL_ENC_GUESS);
        // zip_delete(zip_archive, dir_index);
        // // Remove Info
        // VirtualBlockPtr block = FindVirtualBlock(root, sub_path);
        // if(block->parent != nullptr) {
        //     auto found = block->parent->block_map.find(block->name);
        //     block->parent->block_map.erase(found);
        // } else {
        //     block->block_map.clear();
        //     block->node_map.clear();
        //     block->file_system.reset();
        // }
        RK_WARN(File, "Remove Dir Not Supported In Zip");
        return true;
    }
}
ZipFileSystem.cpp
#pragma once
#include <zip.h>
#include <string>
 
namespace Rocket {
    zip_t* OpenZip(const std::string& file_name, uint32_t mode = ZIP_CREATE);
    void CloseZip(zip_t* zip);
    zip_file_t* OpenZipFile(zip_t* zip, const std::string& file_name, uint32_t mode = 0);
    void CloseZipFile(zip_file_t* zip_file);
    void GetZipFileStatus(zip_t* zip, zip_stat_t* status, const std::string& file_name, uint32_t mode = 0);
}
ZipUtils.h
#include "FileSystem/ZipFile/ZipUtils.h"
#include "Log/Log.h"
 
#include <cassert>
 
namespace Rocket {
    zip_t* OpenZip(const std::string& file_name, uint32_t mode) {
        auto zip_archive = zip_open(file_name.c_str(), ZIP_CREATE, nullptr);
        if(zip_archive == nullptr) {
            RK_TRACE(File, "Unable to Open Zip: {}, Error: {}", file_name, zip_strerror(zip_archive));
            return nullptr;
        }
        return zip_archive;
    }
 
    void CloseZip(zip_t* zip) {
        auto result = zip_close(zip);
        if(result < 0) {
            RK_WARN(File, "Zip Close Error: {}", zip_strerror(zip));
        }
    }
 
    zip_file_t* OpenZipFile(zip_t* zip, const std::string& file_name, uint32_t mode) {
        assert(zip != nullptr);
        auto file = zip_fopen(zip, file_name.c_str(), mode);
        if(file == nullptr) {
            RK_WARN(File, "Open File Not Exist in Zip: {}", file_name);
        }
        return file;
    }
 
    void CloseZipFile(zip_file_t* zip_file) {
        zip_fclose(zip_file);
    }
 
    void GetZipFileStatus(zip_t* zip, zip_stat_t* status, const std::string& file_name, uint32_t mode) {
        assert(zip != nullptr && status != nullptr);
        zip_stat_init(status);
        auto result = zip_stat(zip, file_name.c_str(), mode, status);
        if(result < 0) {
            RK_WARN(File, "Get File Info in Zip Error: {}", file_name);
        }
    }
}
ZipUtils.cpp

阅读材料

https://www.kernel.org/doc/html/latest/filesystems/vfs.html
https://simoncoenen.com/blog/programming/PakFiles