こんにちは。
2020年5月上旬から聖剣伝説3ToMのmod作成に夢中になり、色々と調査や検証を行ってきました。
以前とある方より「"すべての店に武器・防具を追加するmod"や"すべての店に秘薬やクラスチェンジアイテム追加するmod" ってどんなアプローチで作ったの?」という質問を頂きました。
英語での質問だったため、Google翻訳に頼りつつ、 uassetとuexpの関係や、自分自身のアプローチについてざっくりと書いて回答しました。 今回、回答した内容の日本語版をブログ記事として残しておきたいと思います。
目次
利用ツールについて
本記事では下記のツールを利用しています。
各種利用方法は割愛します。
Serializer : uassetとuexpを結合したファイル(bin)をJSON(キー:値)の形式に変換してくれるツール
Visual Studio Code: ソースコードエディタです。JSONの表示や編集に利用します
HxD: バイナリ・HEXエディタです。各ファイルの16進数値の表示・変更や、ファイルの結合(uasset+uexpなど)に使います
はじめに
- 通常の手順を試したができなかった
- uasset+uexpの結合とJSON化
- JSONの値変更と変更後のJSONのuasset化
まず、mod製作者の皆さんが使っているSerializerを用いて、uasset+uexpをJSONにしました。
しかしショップのファイルは、変更したJSONからbinに変換しても、内容が適用されず、通常の手順では不可能でした。 Serializerのみで編集完了するファイルは少ないようで、別のアプローチが必要になりました。
そこでnamesファイルとJSONファイルを元にuassetおよびuexpの16進数の並びを解析し推測しながら16進数を追加・変更することにしました。
(namesファイルはSerializerのuassetからJSONを出力するバッチ「1 - uasset to JSON--dumpnames.bat
」を利用することで取得できます。)
加えて、別のSerializerであるJohnWickParseのソースコード(※1)からuassetのHEX情報を得ました。
※1 JohnWickParse/blob/master/src/assets.rsの435行目~470行目付近
上記の情報をもとに、推測と検証を行った結果、以下のことを理解することができました。
[1]uassetのnamesとuexpの関係
[2]namesキーの変更方法
[3]namesキーの追加方法
[4]uexpの配列と構造体のHEX列
uassetとuexpの関係
それではuassetとuexpの関係について具体的に確認していきましょう。
uexpは、namesのインデックスとその値(整数や浮動小数点数、names)で構成されているようです。
(例外があるかもしれませんが、DataTableとよばれるオブジェクトに関しては、この構成のようです。)
ShopItemDataCsv.uassetを用いた解析アプローチ例
ShopItemDataCsv.uasset (Content\Game00\Data\Csv\ShopData)を例にします。 これをSerializerでJSONにすると、下記のようなデータが得られます。
上記画像の1219行目に注目してみます。
"ItemId_24_320FB4CD4865E046DDF62B80E83DE9FD": "EItemType::ITEM_ID_CHOCO",
と書かれているようです。
上記JSONの「キー:値」がバイナリエディタ上では、どのようになっているのか?
これを解析していきます。
1 - uasset to JSON--dumpnames.bat
で得られたShopItemDataCsvNames.names
ではItemId_24_320FB4CD4865E046DDF62B80E83DE9FD
は以下のとおりになっています。
79行目に注目してください。
この場合、ItemId_24_320FB4CD4865E046DDF62B80E83DE9FD
は79行目にありますが、インデックスは0始まりなので行数から1を引く必要があります。
79-1=78、つまり78がItemId_24_320FB4CD4865E046DDF62B80E83DE9FD
のインデックスということになります。
78を16進数(32bit)で表現すると、00 00 00 00 00 00 00 4E
です。
しかし、uexpの16進数はリトルエンディアンで表現されるため、 下記のとおりになります
4E 00 00 00 00 00 00 00
(リトルエンディアンについては、申し訳ありませんがここでは説明を割愛します。Web検索等ですぐに出てくるので、検索をおすすめします。)
試しに、HxDツールでShopItemDataCsv.uexp
を開き、この16進数の値4E 00 00 00 00 00 00 00
で検索してみると、160件のデータがマッチします。Serializerで出力したJSONで
ItemId_24_320FB4CD4865E046DDF62B80E83DE9FD
を検索すると同様に160件であり、個数が一致していることがわかります。
続いて、検索結果のうちの一つであるアドレス0x00000164
の16進数に着目してみましょう。
4E 00 00 00 00 00 00 00 46 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 0F 00 00 00 00 00 00 00 00 1B 00 00 00 00 00 00 00
これらの16進数を上記で説明した方法でnamesと照合していきます。
16進数 | 10進数 | namesキー(10進数に対応するインデックス) |
---|---|---|
4E 00 00 00 00 00 00 00 |
78 | ItemId_24_320FB4CD4865E046DDF62B80E83DE9FD |
08 00 00 00 00 00 00 00 |
8 | CameraId_26_4BE91328489B42F5B11C5E8894E1EF3A なんだか関係なさそうなキー。 |
0F 00 00 00 00 00 00 00 00 |
15 | EItemType |
1B 00 00 00 00 00 00 00 |
27 | EItemType::ITEM_ID_DROP |
さて、
08 00 00 00 00 00 00 00
をnamesとマッチングした結果、
CameraId_26_4BE91328489B42F5B11C5E8894E1EF3A
という関係なさそうなキーが出てきました。
このようにたまに突拍子もない値が登場することがあります。
これは、値の型を示す識別子の後に挿入されるケースが多いです。
(IntProperty
FloatProperty
などの型識別子, EItemType
EItemType::ITEM_ID_DROP
などのEnumProperty)
この場合、namesキー値ではなく、値そのものであると考えた方が良いです。
しかしこれで、
“ItemId_24_320FB4CD4865E046DDF62B80E83DE9FD”: “EItemType::ITEM_ID_DROP”
が示す16進数の値が判明しました。
“ItemId_24_320FB4CD4865E046DDF62B80E83DE9FD”: “EItemType::ITEM_ID_DROP”
が示す16進数の値46 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 0F 00 00 00 00 00 00 00 00 1B 00 00 00 00 00 00 00
では、目的のEItemType::ITEM_ID_CHOCO
を探すにはどうすれば良いでしょうか。
46 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 0F 00 00 00 00 00 00 00 00 1B 00 00 00 00 00 00 00
のうち、1B 00 00 00 00 00 00 00
の部分(10進数27
)が、
namesのEItemType::ITEM_ID_DROP
のインデックスと一致していました。
つまり、この部分をEItemType::ITEM_ID_CHOCO
のインデックスである
14 00 00 00 00 00 00 00
(10進数20
)に置換すれば良いのです。
“ItemId_24_320FB4CD4865E046DDF62B80E83DE9FD”: “EItemType::ITEM_ID_CHOCO”
が示す16進数の値46 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 0F 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00
この16進数で検索をしてみると、15件のデータが出てきました。
JSONでEItemType::ITEM_ID_CHOCO
を検索してみると、同様に15件であり一致していることがわかります。
このようにして、各種データの解析や変更を行うことができます。
nameキーの変更方法、追加方法や、uexpの配列と構造体のHEX列については、 下記の記事をご参照ください。
【聖剣伝説3-ToM_mod】uassetとuexpファイルのバイナリ変更方法まとめ - 気ままなタンス*プログラミングなどのノートブック