大概瞭解了存檔包的處理流程,我們就可以開始搞事了。一步一步來,讓我們先來寫個虛擬存檔包,也就是在代碼中寫死一些文件項目,每次打開該類型的存檔包都固定返回這些項目。作爲存檔格式的識別依據,我們就只規定一條,只要後綴名是 .zzz
,就認爲它是我們要的虛擬存檔包。
大概寫一下結構:
namespace NArchive {
namespace NZzz {
static const Byte kProps[] =
{
kpidPath,
kpidSize,
kpidPackSize
};
struct MyVirtualItem
{
const wchar_t* name;
const char* text;
} items[] =
{
{L"文件1.txt", "內容1"},
{L"文件2.txt", "內容2"},
{L"遞歸調用測試.zzz", "111"}
};
class CHandler :
public IInArchive,
public IInArchiveGetStream,
public CMyUnknownImp
{
public:
MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
INTERFACE_IInArchive(;)
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream** stream);
};
IMP_IInArchive_Props
IMP_IInArchive_ArcProps_NO_Table
// 省略 CHandler 類實現
REGISTER_ARC_I_NO_SIG(
"Zzz", "zzz", 0, 0xAA,
0,
0,
NULL)
}}
這裏爲了省事,使用了 IMP_IInArchive_ArcProps_NO_Table
宏,即該存檔包不含任何額外的屬性。
最後註冊時,有一個格式 ID 參數,該參數理論上應該是唯一的,但。。你應該也猜到了,官方同樣沒有文檔!所以隨便寫一個吧。我在最後附錄中整理出了當前官方代碼中已使用的所有 ID,不跟它們撞在一起就行。
虛擬文件項目數組中,最後一項我故意寫了一個後綴名爲 .zzz
的文件,目的就是爲了測試它能否被遞歸調用。
STDMETHODIMP CHandler::Open(IInStream* stream, const UInt64*, IArchiveOpenCallback* callback)
{
CMyComPtr<IArchiveOpenVolumeCallback> volumeCallback;
callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void**)& volumeCallback);
UString name;
{
NCOM::CPropVariant prop;
RINOK(volumeCallback->GetProperty(kpidName, &prop));
name = prop.bstrVal;
}
int dotPos = name.ReverseFind_Dot();
const UString ext = name.Ptr(dotPos + 1);
return StringsAreEqualNoCase_Ascii(ext, "zzz") ? S_OK : S_FALSE;
}
Open
方法,我刪掉了所有錯誤處理代碼,剩下就是這些,通過 callback->QueryInterface()
查詢到 IArchiveOpenVolumeCallback
接口,然後獲取當前打開存檔包的文件名,看後綴名是不是 .zzz
。
STDMETHODIMP CHandler::Close()
{
return S_OK;
}
STDMETHODIMP CHandler::GetNumberOfItems(UInt32* numItems)
{
*numItems = _countof(items);
return S_OK;
}
這兩個方法沒什麼好說的,我們沒有實際打開任何文件,也不需要釋放資源,Close
直接返回就行。
STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
Int32 testMode, IArchiveExtractCallback* extractCallback)
{
bool allFilesMode = (numItems == (UInt32)(Int32)-1);
if (allFilesMode)
numItems = _countof(items);
if (numItems == 0)
return S_OK;
for (UINT i = 0; i < numItems; i++)
{
CMyComPtr<ISequentialOutStream> realOutStream;
Int32 askMode = testMode ?
NExtract::NAskMode::kTest :
NExtract::NAskMode::kExtract;
UINT32 index = allFilesMode ? i : indices[i];
auto& item = items[index];
RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
if (!testMode && !realOutStream)
continue;
RINOK(extractCallback->PrepareOperation(askMode));
RINOK(realOutStream->Write(item.text, (UINT32)strlen(item.text), NULL));
realOutStream.Release();
RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
}
return S_OK;
}
同樣,這裏展示的 Extract
我也刪掉了錯誤處理,還刪掉了進度回調,只說明一下核心邏輯。
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT* value)
{
NCOM::CPropVariant prop;
switch (propID)
{
case kpidPhySize:
prop = (UInt64)1;
break;
}
prop.Detach(value);
return S_OK;
}
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT* value)
{
NCOM::CPropVariant prop;
switch (propID)
{
case kpidPath:
prop = items[index].name;
break;
case kpidSize:
prop = strlen(items[index].text);
break;
case kpidPackSize:
prop = strlen(items[index].text) * 2;
break;
}
prop.Detach(value);
return S_OK;
}
隨便返回一些屬性信息,注意文件項目屬性中,爲了與解包大小區分,打包後的大小我故意寫成了實際大小的兩倍,哈哈,如果有這樣的打包格式,估計作者會被錘死。
到此爲止,我們已經實現出了一個能穩定工作的虛擬存檔包格式。只要創建任意文件,把它的後綴名改爲 .zzz
,然後用 7z 打開這個文件,就能顯示出我們寫死的這些文件項目。
注意,創建的 .zzz
文件大小一定不能爲 0,否則 7z 不會打開。
最後,完整代碼在這裏:https://github.com/wzv5/7z-formatzzz
附錄 - 格式 ID
格式 | ID |
---|---|
zip | 0x01 |
bzip2 | 0x02 |
Rar | 0x03 |
Arj | 0x04 |
Z | 0x05 |
Lzh | 0x06 |
7z | 0x07 |
Cab | 0x08 |
Nsis | 0x09 |
lzma | 0x0A |
lzma86 | 0x0B |
xz | 0x0C |
Ppmd | 0x0D |
COFF | 0xC6 |
Ext | 0xC7 |
VMDK | 0xC8 |
VDI | 0xC9 |
QCOW | 0xCA |
GPT | 0xCB |
Rar5 | 0xCC |
IHex | 0xCD |
Hxs | 0xCE |
TE | 0xCF |
UEFIc | 0xD0 |
UEFIf | 0xD1 |
SquashFS | 0xD2 |
CramFS | 0xD3 |
APM | 0xD4 |
MsLZ | 0xD5 |
FLV | 0xD6 |
SWF | 0xD7 |
SWFc | 0xD8 |
NTFS | 0xD9 |
FAT | 0xDA |
MBR | 0xDB |
VHD | 0xDC |
PE | 0xDD |
ELF | 0xDE |
MachO | 0xDF |
Udf | 0xE0 |
Xar | 0xE1 |
Mub | 0xE2 |
HFS | 0xE3 |
Dmg | 0xE4 |
Compound | 0xE5 |
wim | 0xE6 |
Iso | 0xE7 |
Chm | 0xE9 |
Split | 0xEA |
Rpm | 0xEB |
Ar | 0xEC |
Cpio | 0xED |
tar | 0xEE |
gzip | 0xEF |