文件扩展名 *.dfs 为梦幻岛引擎的资源文件系统 Dream File System的英文首字母简写
一、dfs的trunk结构
一个trunk的结构为 id + size + data
1. Trunk id
Trunk id 大小为1字节,取值范围为0-255。
2. trunk size
Trunk size 大小为4字节,trunk data的大小不能超过4G,利用该size 可直接跳过不需要的trunk进行查找,该size不包括trunk id 和自身占用的空间
3.trunk data
存储该trunk的数据内容
二、dfs的文件结构
Id | Type | Name | explanation | ||
1 | string | 主入口 | 标识该文件格式的入口 , 有该格式的简要说明 | ||
2 | uint | 版本号 | 4字节从1开始 | ||
3 | uint | 当前文件数量 | 4字节,记录当前文件系统内存放的文件总数 | ||
4 | uint | 最大文件数量 | 4字节,记录当前文件系统内可以存放的最大文件总数 | ||
50 | data | 文件索引区使用状态表 | 记录文件索引区中每一个块有无使用的状态标识,每个标识占1字节 0为未使用,1为已使用,该标识有助于解决哈希冲突,当冲突发生时,将通过该trunk查找未使用的索引块,将冲突索引数据放入。 通过Trunk size 可计算出一共预留了多少个文件索引 | ||
60 | data | 文件索引区 | 每个文件索引分为 四部分 | ||
name | Size | explanation | |||
文件全路径的哈希值 | 4字节 | 文件的全路径字符串计算出的哈希值 | |||
文件全路径字符串 | 255字节 | 记录文件全路径字符串,目的是在哈希值冲突的情况下,通过该字符串来判断那个才是真正的文件索引 | |||
指向与该索引哈希冲突的其它索引的存放地址 | 4字节 | 如果没有哈希冲突,则为0,有冲突为指向与该索引哈希冲突的其它索引的存放地址。通过链表的方式,可访问所有有冲突的索引区 | |||
指向文件真正数据所在的地址 | 4字节 | 该文件在数据区的启始地址 | |||
255 | data | 文件数据 | 这里id为255 16进制就是FF,原因是在碎片较多时重建数据区时方便查找,删过的数据区为0,当查找到FF时,将该文件移动到上个文件结尾,前4字节为索引区地址,方便反向找到索引区修改移动后的位置 |
三、dfs文件操作流程
1.预分配空间
最初文件索引区和状态区空间大小 = 打包文件的总个数 * 1.5
1000个文件,就预先分配1500个文件索引区和索引状态区
2.动态增长
当文件索引区已满的时候添加文件,首先对文件进行重建,每次都按原大小的1.5倍增长。
3.添加文件
首先计算文件全路径的哈希值。
文件索引区的下标 = 文件全路径的哈希值 % 当前文件系统最大文件数
访问该下标的状态区,如果未使用,则添加,索引区链表指针为0,如果已使用,说明哈希值冲突,在状态区查找第一个还未使用的索引下标添加新索引,并沿着冲突索引的链表,将新索引的地址填进链表末端。
4.读取文件
首先计算要读取文件的全路径的哈希值。
找到索引区,如果该索引哈希冲突标识为不冲突,则按数据区地址读取文件,如果标识为冲突,则判断全路径字符串是否匹配,如果不匹配,则遍例该链表所有的索引,直到找到全路径匹配的索引后读取文件。
5.删除文件
删除文件要把索引区链表删除,修改状态区状态为未使用,并修正前一索引指向后一索引的地址,数据区以0将数据覆盖
6.重建文件
当删除文件过多导致大量碎片,或者索引区已满时需要对文件进行重建,重建方法待定。临时方案:可新建一文件,数据拷贝后并将原文件删除,将新文件改名为原文件名,但是如果文件较大,并且当前分区空间不足时就比较难办了。
四、命令行操作
Import 文件路径
Delete 文件系统内部文件全路径名
Export 文件系统内部文件全路径名 文件保存路径
Rebuild
其它的用到时在增加
五、Ogre自定义文件格式相关的插件类
实现一个DFSArchive类继承自Ogre::Archive ,其中getModifiedTime是本文件格式不支持的,直接返回0,官方那个zipArchive也不支持,呵呵所以估计没什么作用吧。另外find函数Ogre传进来的是带通配符的文件路径,比如*.*什么的,这个不用自己写解析Ogre有写好的函数 Ogre::StringUtil::match
实现一个DFSDataStream类继承自Ogre::DataStream,需要注意的是初始化时一定要手动自已调下seekg(0),因为ogre是不会在读之前调用seekg(0),上来直接tell()了(太s b了~~~)。
实现一个DFSArchiveFactory类继承自Ogre::ArchiveFactory
添加一个工厂类,没什么特别的和zipArchiveFactory一样改个名就行了,注意在使用时要把这个类注册给管理器,才能用。
Ogre文件系统是不支持目录结构的,即使你写了也用不上,Ogre全部认为整个带目录的路径为这个文件的文件名,所以在材质脚本或者其它脚本里也得把带目录的全路径名写上。比如 bloom.hlsl文件,如果有目录,你得写上 xxx/xxx/bloom.hlsl。
六、存在的问题和缺陷
1.没有专门的哈希冲突索引区,这样在冲突后转储的地址很有可能是其它还未添加的文件正常计算的哈希值所在位置,这样其它哈希值不冲突的文件索引也只能按冲突处理,又会影响其它正常的文件索引哈希下标。当第一个冲突之前,文件系统是可以保持最高效率的,当发生一次冲突之后,冲突发生率将会以几何倍增。最后添加的文件的读取速度将会降为普通链表方式遍例的速度。
2.因为不是树状结构,所以无法遍例某一文件夹中所有文件,只能单一读取文件。
3.删除文件造成的文件碎片未被利用,只能每次更新时重建一下文件系统。
4.大文件的重建时间有可能过长。
以下是Ogre相关自定义文件格式扩展类的代码
[OgreDFS.h]
#ifndef __OgreDFS_H__
#define __OgreDFS_H__
#include "OgrePrerequisites.h"
#include "OgreArchive.h"
#include "OgreArchiveFactory.h"
#ifdef OGREDREAMFILESYSTEM_EXPORTS
#define _OGREDFS_DllExport __declspec(dllexport)
#else
#define _OGREDFS_DllExport __declspec(dllimport)
#endif
namespace Ogre {
/*---------------------------------------------------------------------------------------------
Ogre版DFS自定义格式类
@remarks
DreamfileSystem也不支持返回单独文件的修改时间,所以getModifiedTime方法也未实现,直接返回0
Author: 苗琳
Date: 2009-12-8
UPDATE:
-----------------------------------------------------------------------------------------------**/
class _OGREDFS_DllExport DFSArchive : public Archive
{
public:
DFSArchive(const String& name, const String& archType );
~DFSArchive();
/// @copydoc Archive::isCaseSensitive
bool isCaseSensitive(void) const;
/// @copydoc Archive::load
void load();
/// @copydoc Archive::unload
void unload();
/// @copydoc Archive::open
DataStreamPtr open(const String& filename) const;
/// @copydoc Archive::list
StringVectorPtr list(bool recursive = true, bool dirs = false);
/// @copydoc Archive::listFileInfo
FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false);
/// @copydoc Archive::find
StringVectorPtr find(const String& pattern, bool recursive = true,
bool dirs = false);
/// @copydoc Archive::findFileInfo
FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
bool dirs = false);
/// @copydoc Archive::exists
bool exists(const String& filename);
/// @copydoc Archive::getModifiedTime
time_t getModifiedTime(const String& filename);
DFS::FileSystem* mDreamFileSystem; //Dream文件系统
};
/*---------------------------------------------------------------------------------------------
Ogre版DFS自定义格式的工厂类
@remarks
Ogre自定义格式必写类,没什么好说的~~~~~
Author: 苗琳
Date: 2009-12-8
UPDATE:
-----------------------------------------------------------------------------------------------**/
class _OGREDFS_DllExport DFSArchiveFactory : public ArchiveFactory
{
public:
virtual ~DFSArchiveFactory() {}
/// @copydoc FactoryObj::getType
const String& getType(void) const;
/// @copydoc FactoryObj::createInstance
Archive *createInstance( const String& name )
{
return OGRE_NEW DFSArchive(name, "DFS");
}
/// @copydoc FactoryObj::destroyInstance
void destroyInstance( Archive* arch) { delete arch; }
};
/*---------------------------------------------------------------------------------------------
Ogre版DFS数据操作流
@remarks
构造时传入的DataTrunk指针必须是new出来的,并且不用delete,该类释放时会自动释放该指针
Author: 苗琳
Date: 2009-12-8
UPDATE:
-----------------------------------------------------------------------------------------------**/
class DFSDataStream : public DataStream
{
protected:
DFS::DataTrunk* mDataTrunk;
public:
/// Constructor for creating named streams
DFSDataStream(const String& name, DFS::DataTrunk* pDataTrunk);
~DFSDataStream();
/// @copydoc DataStream::read
size_t read(void* buf, size_t count);
/// @copydoc DataStream::skip
void skip(long count);
/// @copydoc DataStream::seek
void seek( size_t pos );
/// @copydoc DataStream::seek
size_t tell(void) const;
/// @copydoc DataStream::eof
bool eof(void) const;
/// @copydoc DataStream::close
void close(void);
};
}
#endif
[OgreDFS.cpp]
#include "stdafx.h"
#include "OgreDFS.h"
namespace Ogre
{
DFSArchive::DFSArchive(const String& name, const String& archType )
: Archive(name, archType) , mDreamFileSystem(NULL)
{
}
DFSArchive::~DFSArchive()
{
unload();
}
bool DFSArchive::isCaseSensitive(void) const
{
return false;
}
void DFSArchive::load()
{
if (!mDreamFileSystem)
{
mDreamFileSystem = new DFS::FileSystem();
if (!mDreamFileSystem->open(mName))
{
OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR ,
mName + " - 档案文件打开失败" , "DFSArchive::load");
return;
}
}
}
void DFSArchive::unload()
{
if (mDreamFileSystem)
{
delete mDreamFileSystem;
mDreamFileSystem = NULL;
}
}
DataStreamPtr DFSArchive::open(const String& filename) const
{
unsigned int iStartPos = mDreamFileSystem->findFileDataStartPos(filename);
//如果文件存在
if (iStartPos)
{
DFS::DataTrunk* pDataTrunk = new DFS::DataTrunk(
mDreamFileSystem->getFileStreamPtr() , iStartPos);
pDataTrunk->readTrunkHeader();
// Construct & return stream
return DataStreamPtr(OGRE_NEW DFSDataStream(filename, pDataTrunk));
}
else
{
LogManager::getSingleton().logMessage(
mName + " - Unable to open file " + filename + ", error was '" + "文件(DFS)不存在" + "'");
// return null pointer
return DataStreamPtr();
}
return DataStreamPtr();
}
StringVectorPtr DFSArchive::list(bool recursive, bool dirs)
{
StringVectorPtr ret = StringVectorPtr(OGRE_NEW_T(StringVector, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);
DFS::FileSystem::FileInfoListPtr pFileList = mDreamFileSystem->getFileInfoList();
DFS::FileSystem::FileInfoList::iterator i, iend;
i = pFileList->begin();
iend = pFileList->end();
while (i != iend)
{
ret->push_back(i->mFileName);
++i;
}
return ret;
}
//-----------------------------------------------------------------------
FileInfoListPtr DFSArchive::listFileInfo(bool recursive, bool dirs)
{
FileInfoListPtr ret(OGRE_NEW_T(FileInfoList, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);
DFS::FileSystem::FileInfoListPtr pFileList = mDreamFileSystem->getFileInfoList();
DFS::FileSystem::FileInfoList::iterator i, iend;
i = pFileList->begin();
iend = pFileList->end();
while (i != iend)
{
FileInfo info;
info.archive = this;
info.filename = i->mFileName;
Ogre::StringUtil::splitFilename(info.filename , info.basename , info.path);
info.compressedSize = i->mSize;
info.uncompressedSize = i->mSize;
ret->push_back(info);
++i;
}
return ret;
}
//-----------------------------------------------------------------------
StringVectorPtr DFSArchive::find(const String& pattern, bool recursive, bool dirs)
{
StringVectorPtr ret = StringVectorPtr(OGRE_NEW_T(StringVector, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);
DFS::FileSystem::FileInfoListPtr pFileList = mDreamFileSystem->getFileInfoList();
DFS::FileSystem::FileInfoList::iterator i, iend;
i = pFileList->begin();
iend = pFileList->end();
while (i != iend)
{
if (StringUtil::match(i->mFileName, pattern))
{
ret->push_back(i->mFileName);
}
++i;
}
return ret;
}
//-----------------------------------------------------------------------
FileInfoListPtr DFSArchive::findFileInfo(const String& pattern,
bool recursive, bool dirs)
{
FileInfoListPtr ret(OGRE_NEW_T(FileInfoList, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);
DFS::FileSystem::FileInfoListPtr pFileList = mDreamFileSystem->getFileInfoList();
DFS::FileSystem::FileInfoList::iterator i, iend;
i = pFileList->begin();
iend = pFileList->end();
while (i != iend)
{
if (StringUtil::match(i->mFileName, pattern))
{
FileInfo info;
info.archive = this;
info.filename = i->mFileName;
Ogre::StringUtil::splitFilename(info.filename , info.basename , info.path);
info.compressedSize = i->mSize;
info.uncompressedSize = i->mSize;
ret->push_back(info);
}
++i;
}
return ret;
}
//-----------------------------------------------------------------------
bool DFSArchive::exists(const String& filename)
{
return mDreamFileSystem->find(filename);
}
//---------------------------------------------------------------------
time_t DFSArchive::getModifiedTime(const String& filename)
{
return 0;
}
DFSDataStream::DFSDataStream(const String& name, DFS::DataTrunk* pDataTrunk)
:DataStream(name) , mDataTrunk(pDataTrunk)
{
//获得大小
mSize = pDataTrunk->mSize;
}
//-----------------------------------------------------------------------
DFSDataStream::~DFSDataStream()
{
close();
}
//-----------------------------------------------------------------------
size_t DFSDataStream::read(void* buf, size_t count)
{
unsigned int r = mDataTrunk->read((char*)buf , count);
return (size_t) r;
}
//-----------------------------------------------------------------------
void DFSDataStream::skip(long count)
{
mDataTrunk->skip(count);
}
//-----------------------------------------------------------------------
void DFSDataStream::seek( size_t pos )
{
mDataTrunk->seek(pos);
}
//-----------------------------------------------------------------------
size_t DFSDataStream::tell(void) const
{
return (size_t)mDataTrunk->tell();
}
//-----------------------------------------------------------------------
bool DFSDataStream::eof(void) const
{
return mDataTrunk->eof();
}
//-----------------------------------------------------------------------
void DFSDataStream::close(void)
{
if (mDataTrunk)
{
delete mDataTrunk;
mDataTrunk = NULL;
}
}
//-----------------------------------------------------------------------
const String& DFSArchiveFactory::getType(void) const
{
static String name = "DFS";
return name;
}
}
最后发张dfs文件系统管理器的截图留念一下下~~~~