首页 > Personal > Game > 游戏引擎设计系列7-虚拟文件系统
2018
09-25

游戏引擎设计系列7-虚拟文件系统

为了更好的管理引擎中的文件路径,引擎中设计了虚拟文件系统VFS。比如同一路径根据不同访问权限或不同优先级指向不同目录,同时引擎中设计了打包文件形式的文件系统,用于打包零散的资源文件,同样可以绑定到虚拟路径上。
打包文件类似于一个独立的文件系统,实现借鉴了Windows的exfat格式,在文件系统之,可以添加相应的加密和压缩机制。每个文件就是一个Block
class Block {
int sector_size; // Sector最小的存储单元,分配在Cluster中,一般为4个字节可以存放一个整型,作为Cluster的信息头记录下一个Cluster位置
int sector_count; // 所有的Cluster一共包含多少个Sector
int sector_per_cluster; // 每个Cluster有多少个Sector,如果信息头记录在Cluster头的话,还需要加上包含信息头的Sector个数
int cluster_data_size; // 去除信息头的Cluster大小
int cluster_size; // 包含文件的头的Cluster大小
int cluster_count; // 这个Block一共包含多少个Cluster
int cluster_sector_start; // 在Cluster中实际数据从哪个Sector开始,前面为信息头
uint use_encrypt; // 是否加密
};
根据这个结构,很容易的通过Cluster的序号和Sector的需要来定位到实际的文件位置。

在这个文件系统中,从根开始,所有的文件和目录都是Node,同时在文件系统的头还需要一个CMap,用1位标识Cluster的使用情况,
还可以用4位来标识下一个Cluster地址,可以省略掉Cluster的头信息,但是Pack头会增大3倍
class Node{
Node parent; // 父节点
Node child; // 子节点链表
Node next; // 下一个兄弟节点
Node prev; // 上一个兄弟节点

uint fptr_index; // 记录访问指针从Node的Cluster开始位置移动了多少
uint fptr_cluster; // 记录移动后指向的Cluster
uint entry_cluster;
uint entry_offset;
int flags; // 文件属性,文件或者文件夹、读写权限、是否已缓存等
uint start_cluster; // 从哪个cluser开始
int size; // 文件大小,如果是文件夹大小为cluster_data_size
long mtime; //文件修改时间
string name; // 文件名
uint name_hash; // 文件名的哈希值
};

struct CMap {
uint start_cluster; // 开始的Cluster
int size; // 需要标识的Cluster数量
byte[] chunk; // 数据
int chunk_size; // 数据大小,(size + 7) / 8
bool dirty; // 是否被修改
};

不管是文件还是文件夹,都是以Entry作为信息头写入的,不管读取还是写入,都是先操作Entry,根据类型不同EntryInfo包含文件的基本信息,如Cluster地址等,
EntryName用来存储文件名
struct EntryInfo {
byte type; // 标识EntryInfo或EntryName或CMap
byte continuations; // 文件是否是连续的,保证连续可以直接读取,否则就需要按Cluster读取
byte name_length; // 文件名长度
ushort checksum; // 检查和
ushort flags; // 文件属性
uint name_hash; // 文件名Hash
uint start_cluster; //文件开始Cluster地址
int size; // 文件大小
long mtime; // 文件修改时间
};

struct EntryName{
byte type;
byte name[EntryNameSize];
};
这个文件系统的基本设计思路就是这样,每次操作Pack,如果是新建会重新生成Root节点和CMap,如果是读取,则从文件读取CMap和Root(root信息在CMap之后),同时为了加速访问,会遍历Root下的所有文件夹,并缓存所有Node,方便之后访问。文件会根据文件名中的‘/’分为不同文件夹,并建立父子关系,查找也是按文件夹逐级查找或创建。

/// 虚拟路径的绑定节点,同一个虚拟路劲可以有多个节点,根据权限和优先级获取
struct MountPoint : public Linkable {
CStrBuf VirtualPath; // 虚拟路径,以’/’开头
CStrBuf PhysicalPath; // 物理路径
CStrBuf VirtualPathBase; // 虚拟路径的上级目录,’/’为根目录,上级目录还是’/’,其他目录直接加上’../’
Path::VFSAccess AccessMode; // 读写权限
S32 Priority; // 优先级
int m_PackFd; // 如果绑定的虚拟文件,则这个值大于-1,

bool SetVirtualPath(const CRefStr & path) // 设置虚拟路径,像path转换为合法格式
bool SetPhysicalPath(const CRefStr & path) // 设置物理路径
}

/// 虚拟文件系统
struct VirtualFileSystem{
LinkList MountTable; // 绑定节点链表

/// 初始化绑定根目录
VirtualFileSystem() {
char buff[MAX_PATH];
GetCurrentDirectoryA(MAX_PATH, buff);
MountVFS(“/”, buff, Path::kVirtualReadWrite, 0);
}

/// 绑定虚拟路径
bool MountVFS(const char* virtualPath, const char* physicalPath, Path::VFSAccess accessMode, S32 priority) {
// 创建MountPoint
// 将MountPoint根据优先级插入MountTable中
}

/// 解绑虚拟路径
bool UnmountVFS(const char* virtualPath)
}

struct Path {
enum VFSAccess {
kVirtualRead = 1 << 0,
kVirtualWrite = 1 << 1,
kVirtualReadWrite = 0x03,
};
/// 根据访问权限、优先级,绑定虚拟路径的物理路径
static bool MountVFS(const char* virtualPath,
const char* physicalPath, VFSAccess accessMode, S32 priority);
/// 解绑虚拟路径
static bool UnmountVFS(const char* virtualPath);
/// 通过虚拟路径和访问权限获取物理路径
static void GetPhysicalPath(CRefStr & physicalPath,
const CRefStr & virtualPath, VFSAccess accessMode) {
// 遍历VirtualFileSystem的MountTable,比较虚拟路径是否以MountPoint虚拟路径开始,
// 并根据访问权限找到相应MountPoint,返回物理路径
}
};

最后编辑:
作者:wy182000
这个作者貌似有点懒,什么都没有留下。