フォーマットがわからないデータファイルの解析

超初歩。


HLTVのデータファイルのデータ構造の解析を行いたいけど、
そんな経験全くないものだから、結構手探り状態。
モチベーション保つために、記事にしておく。


何を知るべきか

HLTVが持つデータ構造

データ構造から何を知るべきか

  • サーバ情報
    • マップ
    • ラウンド数
    • その他
  • キャラクター情報
    • 名前
    • 位置情報
    • frag/death
    • Health
    • 使用武器
    • その他
  • HLTV/POVが持つデータ情報
    • 経過時間(Demo Playerでシーンの移動などする場合に必要なはず)



データ構造解析

データ構造の解析には、違う状況で差分をとるのがよい。

  • 状況の変化が全てDemofileに反映されるので、最初の取っ掛かりには状態の変化が少ない状態が望ましい。
    • 記録の間隔を空けない
    • 誰も入っていない鯖に、HLTVを入れる。
  • 10秒ぐらいrecordしたファイルを2つ用意する
    • record→10秒待つ→停止→これを2回おこなう
    • 人間の操作なので、多少録画しすぎてしまうかもしれないが、それは問題ではない。
  • ヘキサエディタ(バイナリエディタ)で違いを眺める


差分をざっと見ると、先頭はほとんど変化がないことがわかる。
つまり、このあたりはデモファイルで共通の情報を記録していることがわかる。
(de_inferno,cstrikeなど人間の目で見てわかる文字列もある。これは当然mapの情報,ゲームの名称とわかるだろう。)
ちなみに、差分をとる方法は、ゲームのセーブデータをいじるときにも使えます。


変化がある部分を上から順に追ってみる。
まず最初に"0E C0"と"6F 35"とそれぞれ2byteが異なる値になっている部分が見つかる。
つまりこの情報は、同じ手順で記録操作を行った際に、変化を受けているわけである。


これは勘になるのだが、ファイルサイズではないか?と疑ってみる。
予備知識として、

  • 32bitOSではデータを4byte単位で扱うと都合が良い(アライメントあたりで検索すれば出るかも)
  • 次の"02 00"の2byteが共通で存在している
  • トルエンディアンで記録している可能性

などのことから、それぞれ2byteの後ろに続くデータをつなげてみる。
"0E C0 02 00", "6F 35 02 00"
さらに、このそれぞれ4byteをbyte毎に左右反転させる。(リトルエンディアンをビッグエンディアンに直す)
"00 02 C0 0E", "00 02 35 6F"
この情報を頭に入れて、ファイルの最下層を見てみる。



"0002C0C9", "0002362A"
"00 02 C0 C9", "00 02 36 2A"
この値がそれぞれのファイルの終端である。=ファイルサイズでもある。
完全に一致しないので、ファイルサイズではなかったようだが、
どちらも近い値を示しており、ファイルサイズより小さい関係を持っていることがわかる。
このことから、この情報はファイルが持つ何らかのデータを示しているのではないか?と疑う。


差分をそれぞれとってみよう。
windows xpなら、電卓→表示→関数電卓→16進を選べば簡単に16進数の演算が行える。

(ファイルサイズ) - (謎の4byteデータ(左右反転))
0002C0C9-0002C00E=BB
0002362A-0002356F=BB
どちらもBBとなる。
この二つのファイルの異なる点から共通点を見つけ出すことができたわけだ。
この"BB"を考えてみる。とりあえず10進数に直してみると、187byteである。
187byteというのは中途半端なデータである。この膨大のデータの中で187byteが持つ意味を探るのは少し難しい。


そこで、違う方向から考えてみる。
"00 02 C0 0E", "00 02 35 6F"
先ほど"ファイルサイズより小さい値"ということがわかっているので、
これはファイルの位置情報じゃないか?と考えてみる。
バイナリエディタのアドレスジャンプ機能を使えば、容易にその箇所に到達できる。
ファイルサイズの大きさに近いことから、一番最後のほうにジャンプするだろう。



そこから16Byteの値を見てみると、ともに
"02 00 00 00 00 00 00 00 4C 4F 41 44 49 4E 47 00"
で一致していることがわかる。
つまり、"ファイルの終端の位置にある情報の位置"を示す値とみてよさそうだ。


これはオフセット値と呼ばれており、データ構造では良く使われている。(WAD3にもね)
今回は"ファイルの先頭からの位置"でほぼ間違いないだろうが、
オフセット値には、そのオフセット値があったデータの位置から・・・だという場合も多々あるため、
他のファイルを解析する場合は注意が必要である。


ここでわかったデータ構造は

{
	map名
	ゲーム名
	終端の情報へのオフセット値

	〜謎〜

	終端データ{
		〜謎〜
	}
}

またまだ解析する内容は盛りだくさん。
実際はわけのわからないデータがたくさん出てくるので、
このわけのわからないデータをプログラムがどう処理しているのかをデバッグしていく必要がでる。


そのあたりの知識は本当に全くないので、がんばって付けます。
メモリ上にデモファイルが全部展開されれば、ある程度ラクなんだけど、
多分、ファイルから逐次読み出す感じだと思うし・・・

おまけ:ビットイメージで知るデータ構造


1byte毎にある規則にしたがって色分けして1ピクセルで表示して並べる機能があるので使ってみる。
見える範囲では上にたくさんのデータが詰まっており、それ以降は規則的なかんじで模様が並んでいるように見える。


参考: 目grep入門 +解説
http://www.slideshare.net/murachue/grep-8132856