雑用担当の備忘録

技術的な話のみになります。翻訳データそのものはありません。 カテゴリ>その他>注意事項を必ず参照してください

カテゴリ:解析 > UE4

前回の記事 のコメントより補足が必要みたいなので

UE4でフォントを作成した後、パッケージを作成する必要があります。

フォントをインポートした時点で作業フォルダに uasset ファイルが作成されます。
このファイルはあくまでも中間ファイルです。これをリリース状態にするにはパッケージ化が必要です。ビルドとかデプロイとかコンパイルに相当するものと考えてください。
UE4_pkg01
パッケージ化は上の画像を参考に
ただこのままだと生成したファイルが pak ファイルにまとめられてしまいますので
パッケージング設定
UE4_pkg02
Use Pak File のチェックを外してください。この状態だと pak にまとめないのでアンパックの必要性が無くなります。

ここからは余談
中間ファイルの中身
UE4_pkg03
ポイントは FString の None の後の dword (0x25 からの4byte) と緑で囲っているバージョン文字列。dword は PackageFlags になるんですが中間ファイルは PackageFlags = 0x00000000
パッケージ後は
UE4_pkg04
PackageFlags = 0x80000000 となっています。またバージョン文字列もありません。
中間ファイルの PackageFlags を 0x80000000 に置き換えると実行ファイルから uasset が読み込まれるようになります。これがコメントで書いていた 0x28:00 -> 0x28:80 の意味なんですが
バージョン文字列の関係もあって普通にパッケージ化した方が良いです。

フォント

uasset 置換は難度が高めなので slate 置換で問題が出ないならそちらをお勧めします。

uasset 置換
メニュー画面のみでの確認なので、置換対象フォントが複数フォルダに分かれている可能性もあります注意してください。
Kholat/Content/Kholat/UI/Fonts/MenuFOnt.uasset

uasset にカスタムヘッダで調整されている?のかUE4で生成しただけではうまく読み込まれません。EFontCacheType::Runtime の場合 ttf ファイルは FontBulkData という形で格納されています。ヘッダ部分はオリジナルのままで、UE4で日本語フォントをインポートして生成した uasset から FontBulkData の部分をオリジナルの FontBulkData に上書きするということをします。

FontBulkData の位置は bulk_data_start_offset という場所にあるのですが
この位置の探し方
uasset01
まずこの FString を探す。パス文字列の前の i32 の部分 1F 00 00 00 も含みます。
次に i32(4バイト) * 2 戻ります 00のピンクの部分ですね。
そこから u64(8バイト) 戻る D2 05 00 00 00 00 00 00 ここが
bulk_data_start_offset = 0x5D2

0x5D2 の部分を見ます
uasset03
C1 83 2A 9E というのがありますね、ここからファイルの最後までが FontBulkData となります。
自作した日本語フォントをインポートした uasset から同じように検索した FontBulkData をコピーして上記の部分に上書きで貼り付ければ良いです。

このやり方は難度が高いのですが、TVECR のように単純に作成して uasset コピーするだけで読み込むものは少ないかもしれないので、多少応用が利くかもしれません。

locres の注意点

TVECR と同じなんですが、実装時に躓いた部分があるのでそこの補足
TVECR は namespace_count = 1 だったんですが

Khl の開始は
08 00 00 00 00 00 00 00

問題は FString の実装にあったんですがパッと見 locres のバージョンが違うのか?と判断してしまったのが間違いでした。単純に FString.length = 0x0 だったら String の部分は出力されないというだけだったんですが
ちょっと変な方向に走りました…

結論として Khl は namespace_count = 8 で Namespaces[0].namespace= ""    // 空き文字
ということです。

FString の実装としては length > 0 なら ANSI で length < 0 は UTF-16-LE
length == 0 は空き文字として実装すると良いです。

アンパック

解析

const COMPRESS_NONE:i32 = 0;
const COMPRESS_ZLIB:i32 = 1;
const COMPRESS_GZIP:i32 = 2;

struct PakEntry {
    file_name: FString,
    offset: u64,
    size: u64,
    uncompressed_size: u64,
    compression_method: i32,
    hash: [u8; 20],
    compression_blocks: Vec<PakCompressedBlock>,
    bencrypted: u8,
    compression_block_size: u32,
}

struct PakCompressedBlock {
    compressed_start: u64,
    compressed_end: u64,
}

entry[0]

2A 00 00 00
45 6E 67 69 6E 65 2F 50 6C 75 67 69 6E 73 2F 32
44 2F 50 61 70 65 72 32 44 2F 50 61 70 65 72 32
44 2E 75 70 6C 75 67 69 6E 00
FileName = "Engine/Plugins/2D/Paper2D/Paper2D.uplugin"

00 00 00 00 00 00 00 00
Offset = 0x0

7E 01 00 00 00 00 00 00
Size = 0x17E

72 03 00 00 00 00 00 00
UncompressedSize = 0x372

01 00 00 00
CompressionMethod = 0x1 (COMPRESS_ZLIB)

D3 1C F0 F0 1B 02 64 78 9C B5 D6 B8 35 45 82 86 CE 4E 38 D4
Hash

01 00 00 00
TArray.ArrayNum = 1
49 00 00 00 00 00 00 00
PakCompressedBlock.CompressedStart = 0x49
C7 01 00 00 00 00 00 00
PakCompressedBlock.CompressedEnd = 0x1C7

00
bEncrypted=0x0

72 03 00 00
CompressionBlockSize=0x372

まず CompressionMethod をみると 0x1 これは COMPRESS_ZLIB なので ZLIB 圧縮されている。
シー ク CompressedStart から CompressedEnd - CompressedStart これがサイズになるのでこの分読み込む。読み込んだものを ZLIB で復元しファイルに出力する。これを ArrayNum 分繰り返す。出力ファイルは FileName

以上でアンパックできます。

このフォーマットを見る限り pak は
bEncrypted = 0x0 (false) つまり非暗号で圧縮無し・ZLIB圧縮・GZIP圧縮
bEncrypted = 0x1 (true) 暗号化されている(AES)で圧縮無し・ZLIB圧縮・GZIP圧縮
のパターンが出てくるかもしれません。
(UE4.9.1時の情報)
AES暗号済みのも出現しました。

概要
  • UE4
  • データは locres
  • pak は zlib compress リパック必要なし
  • Font は EFontCacheType::Runtime
  • uasset はカスタマイズヘッダー
locres は標準形なので TVECR を参考にすると良い。
今回取り扱うのは zlib compress とフォントの uasset カスタマイズヘッダー対応について。

アンパックしてlocresを編集した状態
良さそうに見えますが CJK 互換では問題が出るかも
KholatEN
上記に uasset へ日本語フォントを組み込んだ状態
KholatJP

↑このページのトップヘ