Wii用ゲームのThe Last Storyは、テクスチャ、モデル、セリフ、パラメータデータ等がアーカイブファイルの中に格納されています。

アーカイブファイルはpk, pkh, pfsの3つのファイルで構成されており、pkにデータの実体、pkhにインデックス、pfsにファイル名やディレクトリ構造が記録されています。この3つのファイルフォーマットについて調べたので記しておきます。

なお、このアーカイブ形式は開発元のAQインタラクティブの後継会社のマーベラスAQLが開発したSoul Sacrifice Deltaというゲームにも採用されているそうですが、所持していないので同じ形式かどうかわかりません。


参考ページ[source]: http://forum.xentax.com/viewtopic.php?f=10&t=5938


展開プログラム書いたので、ほしい方はこちらから



初めに

Wiiはパワーアーキテクチャ系のCPUで、ビッグエンディアンが採用されています。ファイルに記録されている値はすべてビッグエンディアンなので注意してください。
1つのアーカイブにつき、3つのファイルがあります。(例:boot.pk , boot.pkh , boot.pfs)
以下に記すデータ型は、DWordを4バイト符号なし整数、Longを4バイト符号あり整数とします。型は適当なのでバイト数だけ注目してください。

PKファイル

一番大きなファイルです。複数のファイルが圧縮されて格納されています。  圧縮形式はLZ-11で、 DSDecmp(https://github.com/Barubary/dsdecmp)で1つのファイルを展開できます。
圧縮サイズが元サイズを超える場合、未圧縮で格納されています。
バイナリの先頭が0x11で始まっていたらLZ-11で圧縮されてると考えていいです。
LSPK-Extractorでは解凍に失敗し、かつPKHで圧縮サイズと展開サイズが一致していたら未圧縮と判定しています。

PKHファイル

このファイルにPKファイルのオフセット、圧縮サイズ、展開後サイズが記録されています。このファイルの情報を基にPKファイルを分割することができます。

PKHファイルヘッ ダ(4byte)
File Count
DWord (4byte)
ファイル総数です

ファイル情報テーブル (全16Byte×ファイル数)
Hash
DWord (4byte)
ファイルパスのCRC32です。詳しくは後述
Offset
DWord (4byte)
PKファイルの内部アドレスです。
Decompressed size
DWord (4Byte)
LZ11を展開した時のファイルサイズです。
Compressed size
DWord (4Byte)
PKファイルに記録されているサイズです。

PKHには、収録されているすべてのファイルのインデックス情報などが記録されています。
それらはすべて構造体の配列としてアクセスできるようになっています。

例えば、preload/boot.pkhだと、ファイル数が217なので、PKHファイルサイズは4+16*217=3476バイトです。
Hashは、ファイルパスの文字列のCRC32をとったものです。ファイル名でアクセスするには、このハッシュで構造体を検索してPKファイルから展開、といった形になります。


PFSファイル

ディレクトリ構造・ファイル名が記録されています。

PFSファイルヘッ ダ (16 bytes)
0x00
8byte
0x00で埋められています
Directory count
DWord (4byte)
全ディレクトリ数です。
File count
DWord (4byte)
全ファイル数です。



ディレクトリ情報 テーブル (24bytes * Directory Count)
Directory ID
Long (4byte) ディレクトリIDです。
Parent directory ID
Long (4byte) 親ディレクトリIDです。ルートディレクトリの場合は-1になります。
Start child directory Long (4byte) 子ディレクトリの開始IDです。
Child directory count Long (4byte) 子ディレクトリ数です。
Start child file Long (4byte) このディレクトリにあるファイルの開始ナンバーです。
Child file count Long (4byte) このディレクトリにあるファイルの数です。



ディレクトリ名オフ セットテーブル(4bytes * Directory count)
Offset in string table
DWord (4byte)
文字列テーブル中のディレクトリ名へのオフセット。



ファイル名オフセッ トテーブル(4bytes * File count)
Offset in string table DWord (4bytes)
文字列テーブル中のファイル名へのオフセット



文字列テーブル(EOF まで)
String table
EOFまで
文字列テーブルです。上の2つのテーブルを使ってアクセスします。


ディレクトリ構造の例

Boot.pfsの中身を例として以下に表します。
[親ディレクトリID,自分のディレクトリID]ディレクトリ名
[File,FileNum]ファイル名

Dir Index.0
 Name :
 dirID : 0
 ParentID : -1
 StartChildDir : 1
 ChildDirCount : 5
 StartChildFile : 0
 ChildFileCount : 0

Dir Index.1
 Name : boot
 dirID : 1
 ParentID : 0
 StartChildDir : 6
 ChildDirCount : 0
 StartChildFile : 0
 ChildFileCount : 56

Dir Index.2
 Name : data
 dirID : 2
 ParentID : 0
 StartChildDir : 6
 ChildDirCount : 3
 StartChildFile : 56
 ChildFileCount : 0

Dir Index.3
 Name : database
 dirID : 3
 ParentID : 0
 StartChildDir : 9
 ChildDirCount : 1
 StartChildFile : 56
 ChildFileCount : 0

Dir Index.4
 Name : font
 dirID : 4
 ParentID : 0
 StartChildDir : 13
 ChildDirCount : 0
 StartChildFile : 56
 ChildFileCount : 11


Dir Index.5
 Name : hbm
 dirID : 5
 ParentID : 0
 StartChildDir : 13
 ChildDirCount : 1
 StartChildFile : 67
 ChildFileCount : 0

Dir Index.6
 Name : d2anime
 dirID : 6
 ParentID : 2
 StartChildDir : 9
 ChildDirCount : 0
 StartChildFile : 67
 ChildFileCount : 16

Dir Index.7
 Name : shader
 dirID : 7
 ParentID : 2
 StartChildDir : 9
 ChildDirCount : 0
 StartChildFile : 83
 ChildFileCount : 76

Dir Index.8
 Name : texture
 dirID : 8
 ParentID : 2
 StartChildDir : 9
 ChildDirCount : 0
 StartChildFile : 159
 ChildFileCount : 36

Dir Index.9
 Name : cosedit
 dirID : 9
 ParentID : 3
 StartChildDir : 10
 ChildDirCount : 3
 StartChildFile : 195
 ChildFileCount : 0


Dir Index.10
 Name : option
 dirID : 10
 ParentID : 9
 StartChildDir : 13
 ChildDirCount : 0
 StartChildFile : 195
 ChildFileCount : 12

Dir Index.11
 Name : palette
 dirID : 11
 ParentID : 9
 StartChildDir : 13
 ChildDirCount : 0
 StartChildFile : 207
 ChildFileCount : 4

Dir Index.12
 Name : preset
 dirID : 12
 ParentID : 9
 StartChildDir : 13
 ChildDirCount : 0
 StartChildFile : 211
 ChildFileCount : 1

Dir Index.13
 Name : HomeButton2
 dirID : 13
 ParentID : 5
 StartChildDir : 14
 ChildDirCount : 0
 StartChildFile : 212
 ChildFileCount : 5


(Root)\ 
    [0,1]boot\
      [File,0]ai_table.csv
      [File,1]arrow_set.csv
            ~省略~
      [File,55]weapon_param_text_jp.u16
    [0,2]data\
      [2,6]d2anime\
        [File,67]camp_cursor.d2b
        [File,68]camp_info4.d2b
        [File,69]camp_info7.d2b
            ~省略~
        [File,80]tips_ud.d2b
        [File,81]ui_loading.d2b
        [File,82]ui_saving.d2b
      [2,7]shader\
        [File,83]color.shader
            ~省略~
        [File,158]water_uv_RF.shader
      [2,8]texture\
        [File,159]ban_home_button.texture
        [File,160]diffuse.texture
             ~省略~
        [File,194]wd_01_02.texture
    [0,3]database\
      [3,9]cosedit\
        [9,10]option\
          [File,195]option_parts.csv
          [File,196]pc_set_a_level.csv
          [File,197]pc_set_b_level.csv
          [File,198]pc_set_d_level.csv
          [File,199]pc_set_f_level.csv
          [File,200]pc_set_g_level.csv
          [File,201]pc_set_j_level.csv
          [File,202]pc_set_l_level.csv
          [File,203]pc_set_m_level.csv
          [File,204]pc_set_w_level.csv
          [File,205]pc_set_x_level.csv
          [File,206]pc_set_y_level.csv
        [9,11]palette\
          [File,207]db_eff_palette_id_table.csv
          [File,208]db_eff_palette_name_table.u16
          [File,209]db_palette_id_table.csv
          [File,210]db_palette_name_table.u16
        [9,12]preset\
          [File,211]edit_preset.csv
    [0,4]font\
      [File,56]cp932_unicode.tbl
      [File,57]font_01_01.gly
      [File,58]font_02_01.gly
      [File,59]font_02_01_s.gly
      [File,60]font_02_02.gly
      [File,61]font_02_03.gly
      [File,62]font_03_01.gly
      [File,63]font_05_01.gly
      [File,64]font_05_01_s.gly
      [File,65]font_06_01.gly
      [File,66]font_07_01.gly
    [0,5]hbm\
      [5,13]HomeButton2\
        [File,212]config.txt
        [File,213]home.csv
        [File,214]homeBtn.arc
        [File,215]HomeButtonSe.arc
        [File,216]SpeakerSe.arc


ファイルパスの形式とハッシュ

PKHのHashはファイルパスのCRC32をとったものです。
CRC32には複数の種類がありますが、広く用いられているCRC32bではなくノーマルなCRC32だそうです。
CRCテーブルは
0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, ...
となります。続きはググれば出ます。ちなみに、main.dolの0x00737AD0あたりにもこのCRCテーブルが記録されています。
ファイルパスの形式は、
ディレクトリ名/ファイル名
です。
例:boot/ai_table.csv (CRC32:1D9635ED)
File No.26
 CRC32  : 1D9635ED
 Offset : 00000000
 decompressed size : 0000596E
 compressed size   : 00001299

Windowsのパスのように'\'ではないので注意しましょう。
また、CRC32をとる時には、パスに含まれる大文字をすべて小文字に変換してからでないとPKHにあるハッシュと一致しません。
例:hbm/HomeButton2/HomeButtonSe.arc -> hbm/homebutton2/homebuttonse.arc (CRC32:6AC91931)

このハッシュをPKHから探すと
File No.88
 CRC32  : 6AC91931
 Offset : 0016F800
 decompressed size : 00059BB6
 compressed size   : 000560A1
が見つかります。そして、offsetとcompressed sizeからPKファイルにアクセスしてデータを得ます。


追記:
別のゲームでは、圧縮フォーマットにzlib,を使ったものやPKHの構造体サイズ16バイトから24バイトに変更されているものがあります。
' Last Storyなどで使用
Type PKH_INFO_BLOCK_16
	hash As DWord
	offset As DWord
	dec_size As DWord
	com_size As DWord
EndType

' PKのサイズが4GBを超えるものに使用
Type PKH_INFO_BLOCK_24
	hash As DWord
	dummy1 AS DWord
	offsetHigh AS DWord
	offsetLow As DWord
	dec_size As DWord
	com_size As DWord
EndType
2019/7/16:文章修正
以上です。


posted by RGBA_CRT at 00:08| Comment(0) | TrackBack(0) | プログラミング | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください

この記事へのトラックバック
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。