做爲入門,個人選擇是從 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(...)