Skip to content

Commit

Permalink
Migrate to VitePress
Browse files Browse the repository at this point in the history
  • Loading branch information
ikremniou committed Nov 12, 2023
1 parent b8188a5 commit 5253061
Show file tree
Hide file tree
Showing 6 changed files with 357 additions and 425 deletions.
14 changes: 14 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ export default defineConfig({
{ text: 'How to compile', link: '/how-to-compile' },
{ text: 'Plugin system', link: '/plugin-system' }
]
},
{
text: 'API',
items: [
{ text: 'Archiver API', link: '/plugin-api-def' },
]
},
{
text: 'Implementation',
items: [
{ text: 'Infrastructure', link: '/infrastructure' },
{ text: 'Implementing SZ', link: '/sz-impl' },
{ text: 'Implementing SZE', link: '/sze-impl' },
]
}
],

Expand Down
18 changes: 18 additions & 0 deletions docs/infrastructure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Infrastructure

Here we will review initial infrastructure to create handlers for `sz` and `sze` sample archives.

TBA

And finally plugin host will create InArchive object using [CreateObject](./plugin-api-def.md#createobject-const-guid-clsid-const-guid-iid-void-outobject) method.

``` C++
STDAPI_LIB CreateObject(const GUID* clsid, const GUID* iid, void** outObject) {
if (*clsid == SzHandlerGuid && *iid == IID_IInArchive) {
*outObject = new archive::SzInArchive();
}

static_cast<IUnknown*>(*outObject)->AddRef();
return S_OK;
}
```
147 changes: 147 additions & 0 deletions docs/plugin-api-def.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Plugin API

Once again let's look at the definition of the `Archive2.def` file from 7-zip sources:
```def
EXPORTS
CreateObject PRIVATE
GetHandlerProperty PRIVATE
GetNumberOfFormats PRIVATE
GetHandlerProperty2 PRIVATE
GetIsArc PRIVATE
GetNumberOfMethods PRIVATE
GetMethodProperty PRIVATE
CreateDecoder PRIVATE
CreateEncoder PRIVATE
GetHashers PRIVATE
SetCodecs PRIVATE
SetLargePageMode PRIVATE
SetCaseSensitive PRIVATE
GetModuleProp PRIVATE
```

This are the methods that can be implemented by the dynamic plugin. Some of them are not mandatory to implement. It strongly depends on the functionality of your dynamic plugin.

::: info
Methods marked with `*` are optional to implement.
:::

### `GetModuleProp(PROPID propID, PROPVARIANT* value)`*

1. `propID` - the ID of the property to retrieve. Can be one of the following values:
- *`kInterfaceType(0)`* - if VT_UI4 `0` is returned than the `IUnknown` inside of your module should not use the virtual destructor, otherwise it does (VT_UI4 `1` returned).
- *`kVersion(1)`* - the module version as VT_UI4. Used used internally by the plugin host.
2. `value` - the property variant value to return.

This function is called first to define if dll is compatible with the current version of the 7-Zip and to get the version of the module.

By default the 7-zip will expect that the module will return `0` for `kInterfaceType` on Windows and Linux(starting from `23.01`). The main idea is to have match between the plugin host destruction model and the module. If destination models does not match the plugin is considered not compatible with the plugin host.

::: info
If implementation is omitted the version of the plugin will be `0`, and the plugin will be considered compatible with the plugin host.
:::

### `GetNumberOfMethods(UInt32* numCodecs)`*

1. `numCodecs` - the number of codecs to return.

This function is called by the plugin host to get the number of supported codecs. Coders are not used when working with archives in File Manager, hance for FM plugins we can omit implementation.

::: info
If method is not implemented, the default codec index will be set to `0`.
:::
### `GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT* value)`*

1. *`codecIndex`* - is used to specify the index of the archiver coder. Top index is resolved by [`GetNumberOfMethods`](#getnumberofmethodsuint32-numcodecs).
2. *`propID`* - is the one of the following values:
- *`kID`*(0) - expect the *VT_UI8* as id of the archive coder(It used internally by the plugin host like checking the version of the "interface" where values like `kName` wont be queried if `kID` is not supported)
- *`kName`*(1) - expect the *VT_BSTR* as name of the archiver coder. For example, name is displayed in the `Options` dialog in File Manager.
- *`kDecoder`*(2) - expects the binary GUID of the decoder as *VT_BSTR*.
- *`kEncoder`*(3) - expects the binary GUID of the encoder as *VT_BSTR*.
- *`kPackStreams`*(4) - the *VT_UI4* number of pack streams(threads) that can be utilized to pack the data.
- *`kUnpackStreams`*(5) - the *VT_UI4* number of unpack streams(threads) that can be used to unpack the data.
- *`kDescription`*(6) - the description of the archive coder as *VT_BSTR*.
- *`kDecoderIsAssigned`*(7) - TBA
- *`kEncoderIsAssigned`*(8) - TBA
- *`kDigestSize`*(9) - TBA
- *`kIsFilter`*(10) - signals the plugin host that plugin supports filtering data streams. It can be used to pre-process data before compression. For example, you have `080204050201` sequence where you know that 0-s are always odd. You can filter them using special `-F` parameter and compress only even numbers. Same filter must be used to restore the original data.

3. Third parameter is used to report back the metadata about archive coder.

This function returns the metadata about specific archive coder.

::: info
Invoked only if `GetNumberOfMethods` is implemented and returned value that is greater then 0.
:::
### `GetNumberOfFormats(UINT32 *numFormats)`

1. `numFormats` - the number of archiver formats supported by the plugin.

It is considered that different formats will have different implementations, however, it is possible to have only one implementation for multiple formats, breaking the single responsibility principle. Later on the plugin host will refer to the formats using their index(starting from `0`).

### `GetHandlerProperty2(UInt32 formatIndex, PROPID propID, PROPVARIANT* value)`

1. `formatIndex` - the index of the format
1. `propID` - represents the ID of the property to retrieve. It can be one of the following values:
- *`kName`* - expects the name of the Handler as VT_BSTR
- *`kClassID`* - expects the class id of Handler as VT_BSTR binary GUID
- *`kExtension`* - expect the extension as VT_BSTR, that will be associated with the Handler.
- *`kAddExtension`* - allows to specify additional extensions supported by the Handler, separated by space symbol(` `)
- *`kUpdate`* - return variant bool `true` if it is possible to update the archive, otherwise `false`;
- *`kKeepName`* - TBA
- *`kSignature`* - if archive has a [signature](https://en.wikipedia.org/wiki/List_of_file_signatures), it will be verified by the plugin host before invoking plugins logic. The binary VT_BSTR is expected in return.
- *`kkMultiSignature`* - if archive has multiple signatures it expects VT_BSTR here. First byte should be the length of the sub-signature, then signature with specified length follows, and so on.
- *`kSignatureOffset`* - The offset of the file signature in bytes.
- *`kAltStreams`* - TBA
- *`kNtSecure`* - TBA
- *`kFlags`* - Flags that define the capabilities that are implemented by the handler. **TBA**
- *`kTimeFlags`* - TBA
2. `value` - represents the property value to set.

This function returns the metadata about archive format(handler).

### `CreateObject(const GUID *clsid, const GUID *iid, void **outObject)`

1. `clsid` - the format(handler) class id that is returned by the [GetHandlerProperty](#gethandlerproperty2uint32-formatindex-propid-propid-propvariant-value).
2. `iid` - the id of the interface of the object to create. For example, `IID_IInArchive` - the interface used to **read** the archives.
3. `outObject` - the COM-way to return the reference to the created object. Assign object reference to `*outObject`.

::: warning IMPORTANT
You should `AddRef` objects before you return them to the plugin host.
:::

### `GetHashers(IHashers** hashers)`*

1. `hashers` - the plugin should set this argument with the object implementing `IHashers` - the collection of hashers available in plugin. If will be queried on demand.

This function returns the object behind the `IHashers` interface. This interface is used to enumerate `IHasher` instances. The following 3 functions must be defined in order to implement `IHashers`:
1. `HashersImpl::CreateHasher(UInt32 index, IHasher** hasher) noexcept` - creates hasher with specified `index`.
2. `HashersImpl::GetHasherProp(UInt32 codecIndex, PROPID propID, PROPVARIANT* value) noexcept` - gets metadata about the Hasher using its index(`codecIndex`) and metadata property id(`propID`).
3. `HashersImpl::GetNumHashers() noexcept` - returns the number of Hashers supported by the plugin.

::: info
If none of your formats or coders supports hashing there is no reason to implement it.
:::
### `SetLargePageMode()`*
Called to notify if [Large Page Mode](https://learn.microsoft.com/en-us/windows/win32/memory/large-page-support) is enabled. Can be enabled by the CLI option, or special WinAPI call from plugin host.

### `SetCaseSensitive(Int32 caseSensitive)`*

1. `caseSensitive` - the 'bool' argument specifies whether case sensitivity is enabled. If it equals `0` then it is `false`, otherwise it is `true`.

Adjusts the [Case Sensitivity](https://learn.microsoft.com/en-us/windows/wsl/case-sensitivity). It looks like can only be set from the CLI option. Method will be ignored if CLI option is not set.

### `SetCodecs(ICompressCodecsInfo* compressCodecsInfo)`*

1. `compressCodecsInfo` - the interface to the compression codecs implemented by 7-zip.

Plugin host calls this method to set compression codecs for the plugin. This allows using build-in [In-Place](#in-place-plugins) compression algorithms.

### `CreateDecoder(UInt32 index, const GUID* iid, void** outObject)`*
::: info
The method is optional if you developing plugin for File Manager
:::

### `CreateEncoder(UInt32 index, const GUID* iid, void** outObject);`*
::: info
The method is optional if you developing plugin for File Manager
:::
170 changes: 170 additions & 0 deletions docs/sz-impl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Implementation of the SZ Archive

The implementation of the SZ sample archive can be found in [sz-archive.h](https://github.com/ikremniou/7z-assembly/blob/master/src/archive/sz-archive.h) and [sz-archive.cc](https://github.com/ikremniou/7z-assembly/blob/master/src/archive/sz-archive.cc). First we define the `SzInArchive` that will be created by the [CreateObject](./plugin-api-def.md#createobject-const-guid-clsid-const-guid-iid-void-outobject) function. Now, let's implement SZ sample archive.

### Definition of the SzInArchive

```C++
class SzInArchive : public CMyUnknownImp, public IInArchive {
public:
Z7_IFACES_IMP_UNK_1(IInArchive);
};
```
We implement the following interfaces:
1. The `IInArchive` interface.
2. The `IUnknown` interface.
I used the macro `Z7_IFACES_IMP_UNK_1` to create a declarations for `IUnknown` and `IInArchive` interfaces, and inherited from `CMyUnknownImpl` to provide definitions for `IUnknown`.
### Definition of the virtual files.
```C++
struct File {
const wchar_t* path;
bool is_dir;
const char* content;
};
std::array<File, 5> files = {
{
{L"sample.txt", false, "sample"},
{L"sample2.txt", false, "sample2"},
{L"someDir", true, nullptr},
{L"someDir/sample3.txt", false, "sample3"},
{L"child.sz", false, "any"},
}
};
```

### `IInArchive::Open` - opens archive and validates signature.

```C++
HRESULT SzInArchive::Open(IInStream* stream,
const UInt64* maxCheckStartPosition,
IArchiveOpenCallback* openCallback) noexcept {
char buffer[8];
UInt32 processed = 0;
stream->Read(buffer, sizeof(buffer), &processed);
if (buffer[0] != 'S' && buffer[0] != 'Z') {
return S_FALSE;
}
return S_OK;
}
```
### `IInArchive::GetNumberOfItems` - returns number of files in archive.
```C++
HRESULT SzInArchive::GetNumberOfItems(UInt32* numItems) noexcept {
*numItems = static_cast<UInt32>(std::size(files));
return S_OK;
}
```

### `IInArchive::GetProperty` - returns the file properties.

```C++
HRESULT SzInArchive::GetProperty(UInt32 index, PROPID propID,
PROPVARIANT* value) noexcept {
switch (propID) {
{
case kpidPath:
return utils::SetVariant(files[index].path, value);
case kpidIsDir:
return utils::SetVariant(files[index].is_dir, value);
case kpidNumSubFiles:
case kpidMTime:
case kpidIsAltStream:
case kpidEncrypted:
case kpidAttrib:
case kpidNumSubDirs:
case kpidSize:
case kpidPackSize:
case kpidCRC:
case kpidHardLink:
case kpidPosition:
case kpidSymLink:
case kpidPosixAttrib:
case kpidATime:
case kpidCTime:
case kpidIsAnti:
return S_OK;
default:
return S_OK;
}
}
}
```
All of the above properties are queried from by the plugin host with current setup. Most of them are defaulted or ignored by returning `S_OK` and not touching the `value` variant.
### `IInArchive::Extract` - extracts files.
```C++
HRESULT SzInArchive::Extract(
const UInt32* indices, UInt32 numItems, Int32 testMode,
IArchiveExtractCallback* extractCallback) noexcept {
CMyComPtr<ISequentialOutStream> outStream;
while (numItems-- > 0) {
RINOK(extractCallback->GetStream(*indices, &outStream, 0));
UInt32 size_processed;
const char* content = files[*indices].content;
if (content) {
outStream->Write(content, static_cast<UInt32>(std::strlen(content)),
&size_processed);
}
indices = indices + 1;
}
return S_OK;
}
```


### `IInArchive::GetArchiveProperty` - returns the properties of the archive file.

```C++
HRESULT SzInArchive::GetArchiveProperty(PROPID propID,
PROPVARIANT* value) noexcept {
switch (propID) {
case kpidWarningFlags:
case kpidWarning:
case kpidErrorFlags:
case kpidError:
case kpidOffset:
case kpidPhySize:
return S_OK;

case kpidIsTree:
case kpidIsDeleted:
case kpidIsAltStream:
case kpidIsAux:
case kpidINode:
return utils::SetVariant(false, value);
case kpidReadOnly:
return utils::SetVariant(true, value);

case kpidMainSubfile:
return utils::SetVariant(0u, value);

default:
return E_NOTIMPL;
}
}
```
All of the above properties are queried by the plugin host with current setup. Pay attention that we mark archive as **readonly**.
### `IInArchive::GetNumberOfProperties` - returns 0.
```C++
HRESULT SzInArchive::GetNumberOfProperties(UInt32* numProps) noexcept {
*numProps = 0;
return S_OK;
}
```
We return 0 to indicate that we do not display properties of the files **inside** of the archive.

::: tip
Rest of the `IInArchive` methods can safely return `S_OK` right away.
:::
5 changes: 5 additions & 0 deletions docs/sze-impl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Implementation of the SZE archive

The SZE archive emulates the real archive format. It is not readonly and user can add files to it. If user adds file with invalid symbols we should show error. As usual, lets start with the definitions of the classes required to implement the SZE archive.

TBA
Loading

0 comments on commit 5253061

Please sign in to comment.