2021年7月30日金曜日

Unreal Engine 4 でPakをLZ4圧縮

はじめに

LZ4に正式対応し, Pak File Compression Format(s)で指定できるように思えますが, そんなことはないです.

原因

FCompression::IsFormatValidが原因と思われます. コメントからすると, 不具合のように見えます.

    bool FCompression::IsFormatValid(FName FormatName)
    {
        // build in formats are always valid
        if (FormatName == NAME_Zlib || FormatName == NAME_Gzip)
        {
            return true;
        }

        // otherwise, if we can get the format class, we are good!
        return GetCompressionFormat(FormatName, false) != nullptr;
    }

圧縮形式

zlibのチェックサム計算も軽くはないことに注意です.

  • zlib
    • 圧縮率と速度のバランスが良いが, 今では旧い, zstandardを代わりに使うべき
    • checksumは必ず計算する
  • ZStandard
    • 圧縮率と速度のバランスが良い
    • checksumはオプション
  • LZ4
    • 圧縮・伸張速度に重点を置いたもの
      • LZSSだけなので上記2つより圧縮率は低い
    • 圧縮に時間をかけても, 伸張速度はほとんど変わらない
    • checksumはオプション
  • Lizard
    • LZ5を名乗る, LZ4を改良したと主張している

PakのLZ4圧縮

FCompressionを書き換えることは影響範囲を考えてやりたくないです. エンジンのプラグインを書きます. UnrealPakは独立したプログラムのため, 単純なエディタ拡張ではUnrealPakから見えません.

LZ4CompressionFormat.cpp
#include "LZ4Compression.h"
#include "CoreMinimal.h"
#include "Misc/Compression.h"
#include "Misc/ICompressionFormat.h"
#define LOCTEXT_NAMESPACE "FLZ4CompressionModule"

namespace
{
DEFINE_LOG_CATEGORY_STATIC(LogLZ4CompressionFormat, Log, All);
struct LZ4CompressionFormat : public ICompressionFormat
{
    FName GetCompressionFormatName() override;
    bool Compress(void* CompressedBuffer, int32& CompressedSize, const void* UncompressedBuffer, int32 UncompressedSize, int32 CompressionData) override;
    bool Uncompress(void* UncompressedBuffer, int32& UncompressedSize, const void* CompressedBuffer, int32 CompressedSize, int32 CompressionData) override;
    int32 GetCompressedBufferSize(int32 UncompressedSize, int32 CompressionData) override;
    uint32 GetVersion() override;
    FString GetDDCKeySuffix() override;
};

FName LZ4CompressionFormat::GetCompressionFormatName()
{
    return "lz4";
}

bool LZ4CompressionFormat::Compress(void* CompressedBuffer, int32& CompressedSize, const void* UncompressedBuffer, int32 UncompressedSize, int32 CompressionData)
{
    UE_LOG(LogLZ4CompressionFormat, Warning, TEXT("Compress"));
    if(!FCompression::CompressMemory(NAME_LZ4, CompressedBuffer, CompressedSize, UncompressedBuffer, UncompressedSize, COMPRESS_NoFlags, CompressionData)) {
        UE_LOG(LogLZ4CompressionFormat, Warning, TEXT("Faile to compress"));
        return false;
    }
    return true;
}

bool LZ4CompressionFormat::Uncompress(void* UncompressedBuffer, int32& UncompressedSize, const void* CompressedBuffer, int32 CompressedSize, int32 CompressionData)
{
    UE_LOG(LogLZ4CompressionFormat, Warning, TEXT("Uncompress"));
    if(!FCompression::UncompressMemory(NAME_LZ4, UncompressedBuffer, UncompressedSize, CompressedBuffer, CompressedSize, COMPRESS_NoFlags, CompressionData)) {
        UE_LOG(LogLZ4CompressionFormat, Warning, TEXT("Faile to uncompress"));
        return false;
    }
    return true;
}

int32 LZ4CompressionFormat::GetCompressedBufferSize(int32 UncompressedSize, int32 CompressionData)
{
    return FCompression::CompressMemoryBound(NAME_LZ4, UncompressedSize, COMPRESS_NoFlags, CompressionData);
}

uint32 LZ4CompressionFormat::GetVersion()
{
    return FCompression::GetCompressorVersion(NAME_LZ4);
}

FString LZ4CompressionFormat::GetDDCKeySuffix()
{
    static const FString suffix = "2AEE7CBB0BD24E71B0D516ECE2AB68C1";
    return suffix;
}
} // namespace

FLZ4CompressionModule::FLZ4CompressionModule()
    : compressionFormat_(nullptr)
{
}

FLZ4CompressionModule::~FLZ4CompressionModule()
{
    delete compressionFormat_;
}

void FLZ4CompressionModule::StartupModule()
{
    compressionFormat_ = new LZ4CompressionFormat();
    IModularFeatures::Get().RegisterModularFeature(COMPRESSION_FORMAT_FEATURE_NAME, compressionFormat_);
}

void FLZ4CompressionModule::ShutdownModule()
{
    IModularFeatures::Get().UnregisterModularFeature(COMPRESSION_FORMAT_FEATURE_NAME, compressionFormat_);
    delete compressionFormat_;
    compressionFormat_ = nullptr;
}

#undef LOCTEXT_NAMESPACE

IMPLEMENT_MODULE(FLZ4CompressionModule, LZ4Compression)

まとめ

今回は, Pak File Compression Format(s)lz4を書けばLZ4圧縮になるはずです. これを応用すれば好きなフォーマットにできるはずです.

0 件のコメント:

コメントを投稿