爲 7-Zip 寫一個存檔格式插件 (5):實戰

大概瞭解了存檔包的處理流程,我們就可以開始搞事了。一步一步來,讓我們先來寫個虛擬存檔包,也就是在代碼中寫死一些文件項目,每次打開該類型的存檔包都固定返回這些項目。作爲存檔格式的識別依據,我們就只規定一條,只要後綴名是 .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 打開這個文件,就能顯示出我們寫死的這些文件項目。

clipboard.png

注意,創建的 .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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章