雑用担当の備忘録

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

locres 編集

翻訳すべきデータは EthanCarter\Content\Localization\Game\en\Game.locres にある。

locres の構造
struct Locres {
    namespace_count: i32,
    namespaces: Vec<Namespace>,
}
struct Namespace {
    namespace: FString,
    key_count: i32,
    entry: Vec<Entry>,
}
struct Entry {
    key: FString,
    source_string_hash: i32,    // crc32
    localized_string: FString,
}

01 00 00 00
namespace_count = 0x1

FB FF FF FF 47 00 61 00 6D 00 65 00 00 00
FString
0xFFFFFFFB = -5 なので UTF-16-LE で5文字つまり10バイトで末尾に Null 付加されている。
47 00 61 00 6D 00 65 00 00 00
したがって
namespace = "Game"

A3 02 00 00
key_count = 0x2A3

0x2A3ぶん entry を読み込む
entry[0]
FB FF FF FF 4E 00 6F 00 6E 00 65 00 00 00
FString だから key = "None"

C5 0D 18 71
source_string_hash = 0x71180DC5

F0 FF FF FF 3D 00 3D 00 3D 00 3D 00 20 00 4D 00
45 00 4E 00 55 00 20 00 3D 00 3D 00 3D 00 3D 00
3D 00 00 00
FString だから localized_string = "==== MENU ====="

こんな感じで読み込めるから、これを元にエクスポート処理を実装すればよい。

インポートはオフセット値とか気にする必要はないので、上記の処理の反対を頭から繰り返すだけで可能

アンパック

pak ファイルにデータはまとめられている。展開した状態でゲーム起動は可能。

pak ファイルは 4G 超えていますので、大容量のデータを取り扱えるバイナリエディタでないとデータは見れません注意してください。

struct PakInfo {
    magic: u32,
    version: i32,
    index_offset: u64,
    index_size: u64,
    index_hash: [u8; 20],
}
まず PakInfo なんですがこれは pak の末尾にあります。
offset, size は u64(4G超可能) なんで注意 8 バイトです。
magic は末尾から seek(-44) です。

index_offset ここに index があるのでそこへ seek
3F 89 CF 4F 01 00 00 00 = 0x14FCF893F

struct Pak {
    mount_point: FString,
    num_entries: i32,
    entries: Vec<PakEntry>,
}
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,
}
PakCompressedBlock は今回の non commpres では取り扱いません。comppresed のゲームの時に書きます。

まず FString について
struct FString {
    legth: i32,
    string: Vec<u8>,
}
length の値が < 0 の場合 string は UTF-16LE で > 0 の場合は ANSI
String は null 終端
path は ANSI なので length は正の値

0A 00 00 00 2E 2E 2F 2E 2E 2F 2E 2E 2F 00
length = 10, String = "../../../"

mount_point = "../../../"

となります。後は num_entries 分 PakEntry を読み込むcompression_blocksは無いものとして考えてください。

後は実際のアンパック処理なんですが
file_name, offset, size がわかっているので
offset へシークして size 分読み込んだものを file_name として出力するだけ。

追記:
忘れていました。offserへシークした場所に PakEntry の file_name を除いた他の要素が付加されていますので。それを読み込んでその次のデータから取り扱うが正解です。

TVECR 解析

概要:
  • フレームワークは UE4、ローカライズは UE4 の機能を使用。つまり locres バイナリデータの編集が必要。
  • pak は non compress、展開した状態でゲームが起動できるためリパックの必要はない。
  • Font は EFontCacheType::Runtime のため uasset のフォントに無い文字コードの場合は slate の font が使われる。
UE4の基本形といった所でしょうか。
まぁ、UEは内部のバージョンによって細々としたところが変わっていくので、今後色んな派生形が出てくると思います。

locres 編集した状態
EthanJP

フォントは置換していないが Runtime なので豆腐では無く日本語が表示される。
ただし、この場合若干の不具合も発生します。
最の字を見てみると、若干上の方が欠けています。
あとこのフォントでは CJK 互換の部分で C つまり中国のグリフが表示されます。

追記:移転後、フォント置換後の画像を表示していました。フォント置換前の画像に戻しました。

↑このページのトップヘ