vimでバイナリを表示し、値を変更したい

バイナリファイルを表示させることなら、

$ hexdump -C picture.png
00000000  89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|
00000010  00 00 00 0a 00 00 00 0f  08 02 00 00 00 52 9d c9  |.............R.?|

とか、

$ xxd picture.png
0000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
0000010: 0000 000a 0000 000f 0802 0000 0052 9dc9  .............R..

みたいに、hexdumpコマンドとかxxdコマンド(この記事を書いてて知った)を使えばできる。
でも、値を変えたいときは、どうすれば良いのか。
それ、vimで、できるよ。

vimはスゴい

http://www.ac.cyberhome.ne.jp/~yakahaira/vimdoc/usr_23.html#23.4

本物のバイナリエディタは、テキスト表示と 16 進表示の二通りの方法でファイルを表
示します。"xxd" を使ってファイルを変換すれば Vim でも同じように表示できます。
"xxd" は Vim に付属しています。
まず、ファイルをバイナリモードで開いてください:

        vim -b datafile

そして、xxd を使って16進数ダンプ形式に変換します:

        :%!xxd

テキストは次のように変換されます:

        0000000: 1f8b 0808 39d7 173b 0203 7474 002b 4e49  ....9..;..tt.+NI
        0000010: 4b2c 8660 eb9c ecac c462 eb94 345e 2e30  K,.`.....b..4^.0
        0000020: 373b 2731 0b22 0ca6 c1a2 d669 1035 39d9  7;'1.".....i.59.

あとは、好きなようにテキストを編集してください。普通のテキストを編集するのと同
じです。16 進数部分を変更しても、テキスト部分は更新されません。逆も同様です。
編集が済んだら変換し、テキストに戻します:

        :%!xxd -r

16 進数部分への変更だけが反映されます。右側のテキスト部分への変更は無視されま
す。

以下の手順を踏めばできるよ、ってことです。

      • > バイナリモードでファイルを開く(オプション -b)
      • > コマンド%!xxdにより現在開いているバッファをバイナリ表示する
      • > 任意の箇所を変更。
      • > コマンド%!xxd -rによりテキスト表示に戻す

でも、こんなに手順を踏むのは面倒ですよね。
面倒から解放されたい。
それ、vimで、できるよ。

vimは賢い

http://www.ac.cyberhome.ne.jp/~yakahaira/vimdoc/tips.html

もしそのファイルがバイナリファイル特有の拡張子(exe、binなど)をしているときは、
あなたの<.vimrc>に次のオートコマンドを加えることで、変換の過程を自動化しておく
こともできます。"*.bin"をあなたの編集したいファイルの拡張子の、カンマで区切ら
れたリストに変えてください:

        " vim -b : edit binary using xxd-format!
        augroup Binary
          au!
          au BufReadPre  *.bin let &bin=1
          au BufReadPost *.bin if &bin | %!xxd
          au BufReadPost *.bin set ft=xxd | endif
          au BufWritePre *.bin if &bin | %!xxd -r
          au BufWritePre *.bin endif
          au BufWritePost *.bin if &bin | %!xxd
          au BufWritePost *.bin set nomod | endif
        augroup END

ただ、このままの設定では、拡張子binにしか適応されません。
目標は拡張子がなんであっても、バイナリモードでファイルを開いたらバイナリで表示してくれること。
それ、vimで、できるよ。

vimが好きです

http://www.kawaz.jp/pukiwiki/?vim#notefoot_1

"バイナリ編集(xxd)モード(vim -b での起動、もしくは *.bin ファイルを開くと発動します)
augroup BinaryXXD
  autocmd!
  autocmd BufReadPre  *.bin let &binary =1
  autocmd BufReadPost * if &binary | silent %!xxd -g 1
  autocmd BufReadPost * set ft=xxd | endif
  autocmd BufWritePre * if &binary | %!xxd -r | endif
  autocmd BufWritePost * if &binary | silent %!xxd -g 1
  autocmd BufWritePost * set nomod | endif
augroup END

ちょっと説明してみる。

augroup BinaryXXD

コマンドのグループ化をしています。グループ名はBinaryXXDになってます。
グループ名は任意に設定できる。

autocmd!

このグループのautocmdをすべて削除しています。初期化ですね。

autocmd BufReadPre  *.bin let &binary =1

vim -b での起動、もしくは *.bin ファイルを開くと
ファイルを読み込む前に変数binaryに1をセットする。

autocmd BufReadPost * if &binary | silent %!xxd -g 1

ファイルが読み込まれた後に変数binaryに1がセットされているならば、
コマンド%!xxd -g 1を実行。(オプション-g 1によって1バイト表示になる。defaultは2バイト)

 はコマンドの追記。

silent は"続けるにはENTERを押すかコマンドを入力してください"みたいなメッセージを表示されコマンドが止まるのを防ぐために書かれています。

autocmd BufReadPost * set ft=xxd | endif

ft(filetype)をxxdに、つまりバイナリに設定して、if文を終了します。
これにより視覚的に見やすくなります。

set ft=xxdを適用後↓

  autocmd BufWritePre * if &binary | %!xxd -r | endif
  autocmd BufWritePost * if &binary | silent %!xxd -g 1
  autocmd BufWritePost * set nomod | endif

ここは、:wとコマンドを実行して保存する前にテキストファイルに戻してやって、保存したらバイナリ表示にするという
設定です。

vimは何でもできる。

.vimrcに以上のautocmdを記述しておくことにより、バイナリの編集が非常に便利になりました。
例えば、pngファイルをバイナリで開いて値を変更したい場合は以下のようにします。

$ vim -b picture.png

すると、以下のようにバイナリで表示してくれるから。
任意の箇所の値を変更して、保存して、終了すればいい。
[
以下のように新しいバッファを開くと、それもバイナリで表示される。
(vimをオプション-b、つまりバイナリモードで起動しているため)

もし、バイナリで表示したくなくなりテキスト表示をしたいならば、
以下のようにバイナリ表示用のaugroupを無効にしてやればいい。

これによって、新しく開いたバッファはテキストファイルとして表示される。