做为入门,个人选择是从 SplitHandler 下手,由于这里不牵扯任何压缩和哈希算法逻辑,也没有任何文件结构,仅仅是把 .00一、.00二、... 这样的序列文件合并为一个文件,从实现逻辑上来说是最简单的。git
整个文件代码太长,这里不贴出来了,能够本身到 https://github.com/wzv5/7z-formatzzz/blob/master/CPP/7zip/Archive/SplitHandler.cpp 查看。github
namespace NArchive { namespace NSplit { static const Byte kProps[] = { ... } static const Byte kArcProps[] = { ... } class CHandler { ... }; REGISTER_ARC_I_NO_SIG(...) }}
全部代码位于 NArchive::NSplit
命名空间中,分为如下 4 部份内容。算法
首先最重要的是 CHandler
类,这个类是存档包处理的核心。数组
跳过中间的类实现代码,看文件最后,这是第 2 部分,使用 REGISTER_ARC_I_NO_SIG
宏来注册该存档包格式。函数
回过头来看文件开头,第 3 部分是全局的 static const Byte kProps[]
数组,第 4 部分是全局的 static const Byte kArcProps[]
数组,这些数组的含义以后再说。spa
class CHandler: public IInArchive, public IInArchiveGetStream, public CMyUnknownImp { public: MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) INTERFACE_IInArchive(;) STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); };
按照习惯,类名必须为 CHandler
,必须 public 继承自 CMyUnknownImp
。code
若是支持读取此存档格式,则 public 继承 IInArchive
。orm
若是此存档格式的文件项目可以提供顺序流读取,则 public 继承 IInArchiveGetStream
。对象
因为 CMyUnknownImp
类仅仅定义了一个引用计数成员,并无实现任何方法,因此须要本身在类内显式实现,这有现成的宏来实现。继承
这里的 CHandler
类实现了 2 个接口,IInArchive
和 IInArchiveGetStream
,就调用 MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
。
官方已定义 MY_UNKNOWN_IMP1
~ MY_UNKNOWN_IMP7
宏,实现了几个接口就调用相应的宏,并把全部接口传入宏。
若是实现了 IInArchive
接口,调用 INTERFACE_IInArchive(;)
,宏括号内必须写分号。这个宏括号中参数的含义实际上是用以区分当前究竟是为了定义接口,仍是子类为了实现接口。在 IArchive.h
文件中能够看到,若是是要定义接口,那么宏括号内传入的是 PURE
,即 = 0
,纯虚函数。
对于其余接口方法,都经过 STDMETHOD
宏定义出来,能够直接从 IArchive.h
文件中复制定义。
对于类成员变量,没有任何要求,这里定义的几个成员变量都是 Split 处理中须要的,而不是接口规范要求的。
能够看到,这里额外定义了一个 Open2()
方法,这一样不是接口规范要求的,能够按本身须要来写。
IMP_IInArchive_Props IMP_IInArchive_ArcProps
类定义下方,紧接着就是这 2 个宏,它们的含义在上一篇文中有说到,IMP_IInArchive_Props
是用来实现获取包内项目属性的相关方法,IMP_IInArchive_ArcProps
用来实现获取存档包总体属性的相关方法。
有了这 2 个宏,咱们就不须要手动实现 IInArchive
接口中的 GetNumberOfProperties
、GetPropertyInfo
、GetNumberOfArchiveProperties
、GetArchivePropertyInfo
这些方法了。
能够看到,与属性相关的方法仅剩下 GetProperty
、GetArchiveProperty
,前者用来返回文件项目的属性,后者用来返回存档包总体的属性。
注意,重点来了,这 2 个宏有什么魔力,可以自动实现这些方法呢?答案是开头所说的 kProps
和 kArcProps
这 2 个全局数组。数组中定义了该存档格式支持的全部属性,数组成员可用值在 PropID.h
文件中。数组的名字必须为这样,这与宏定义相匹配。
REGISTER_ARC_I_NO_SIG( "Split", "001", 0, 0xEA, 0, 0, NULL)
经过 RegisterArc.h
文件中定义的宏来注册文件格式。
// 只建立存档信息结构 REGISTER_ARC_V(n, e, ae, id, sigSize, sig, offs, flags, crIn, crOut, isArc) // 建立存档信息结构,并注册 REGISTER_ARC_R(n, e, ae, id, sigSize, sig, offs, flags, crIn, crOut, isArc) // 只读,指定实现了读取接口的类名 REGISTER_ARC_I_CLS(cls, n, e, ae, id, sig, offs, flags, isArc) // 只读,指定实现了读取接口的类名,没有签名 REGISTER_ARC_I_CLS_NO_SIG(cls, n, e, ae, id, offs, flags, isArc) // 只读,读取接口为全局的 CHandler 类 REGISTER_ARC_I(n, e, ae, id, sig, offs, flags, isArc) // 只读,读取接口为全局的 CHandler 类,没有签名 REGISTER_ARC_I_NO_SIG(n, e, ae, id, offs, flags, isArc) // 读写,读取和写入接口均为全局的 CHandler 类 REGISTER_ARC_IO(n, e, ae, id, sig, offs, flags, isArc) // 读写,读取和写入接口均为全局的 CHandler 类,且对签名首字符减1,只有 7z 格式使用 REGISTER_ARC_IO_DECREMENT_SIG(n, e, ae, id, sig, offs, flags, isArc)
以上宏中参数的含义:
n
:char*
,格式名字e
:char*
,后缀名,不带点
ae
:char*
,以空格分割的额外的后缀名
e
的成员数一致,分别用以描述每一个 e
的额外后缀名.tgz
这样的后缀名,它实际为 .tar.gz
的简写形式,经过 e
判断该文件为 gzip 格式,但其内部又有 tar,故 ae
应该写为 .tar
.tar
e
没有额外后缀名,则要填入 *
e
都没有额外后缀名,能够传入 NULL
* * .tar .tar
id
:Byte
,格式独立 IDsigSize
:Byte
,签名的长度sig
:Byte[]
,签名,即文件头,用来识别文件格式offs
:UInt16
,签名偏移,若是签名不在文件头部须要手动指定flags
:UInt16
,该格式的一些参数,可用值在 IArchive.h
文件的 NArcInfoFlags
命名空间中crIn
:指定一个函数,返回 IInArchive
接口的对象crOut
:指定一个函数,返回 IOutArchive
接口的对象isArc
,指定一个函数,用来检测传入文件是不是该格式
API_FUNC_static_IsArc IsArc_XXX(const Byte *p, size_t size)
k_IsArc_Res_YES
或 k_IsArc_Res_NO
size
太小,不足以准确判断,能够返回 k_IsArc_Res_NEED_MORE
sig
就能准确判断,该参数能够传 NULL
cls
:指定处理类的类名,一般为 CHandler
new cls;
的形式建立对象,若是处理类的构造函数须要传入额外的参数,能够直接在类名后添加,如 CMyHandler(...)