プリントデバッグが面倒なときの ImHex

# ImHex の紹介

今回紹介するのは ImHex です。いわゆるバイナリエディタです。

https://imhex.werwolv.net/

バイナリエディタらしい機能に加え、エディタがデータに含まれる構造体のメンバとかを解釈して表示してくれます。これを実現するのが Pattern Language（以下パターン言語） です。このアプリケーションを使いこなすにはパターン言語を読み書きする必要があります。

https://docs.werwolv.net/pattern-language/

一般的なデータ構造についてはアプリケーションに内蔵されています。ただし SATO 製ラベルプリンタのページ記述言語については書かれていないようですね。

https://github.com/WerWolv/ImHex-Patterns

# プリントデバッグが面倒なときは？

一般的にプリントデバッグはとても便利です。特にラベル印刷において物理的な印刷ほど信頼できるデバッグ手法はありません。
ただしページ記述言語を意図通りに組み立てられているかを知りたい、ということなら大袈裟とも言えます。プリンタを起動して、ラベルをセットして、アプリケーションをつなげて…これらの物理的な作業はなかなか面倒です。

ということでパターン言語で SATO 製ラベルプリンタのページ記述言語、SBPL の一部の命令用に定義を書きました。プリントデバッグの代わりに SBPL の構造を読み解きましょう。

https://github.com/karronoli/imhex-sbpl

ImHex はデスクトップアプリの他に https://web.imhex.werwolv.net/ からの利用もできます。今回作った定義で解釈できる SBPL を読み込ませると次のような画面になります。

![web.imhex.werwolv.net_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4162/3b262148-2117-b51c-b003-7ed4922dbfec.png)

# パターン言語の入門

まずはパターン言語を書く対象について理解しましょう。SBPL の仕様は SATO America から確認するのがお手軽です。

https://www.satoamerica.com/resources/documentation/manuals

私としては今も昔も MB400i のドキュメントが使いやすいと思ってます。既にディスコンの製品ですが、汎用的な命令については現行品と変わってないようです。

- [I Programming Reference For MB200i MB400i MB410i]( https://d2n1rly8br52rx.cloudfront.net/resources/files/i_-Programming-Reference-for-MB200i-MB400i-MB410i.pdf)

ではドキュメントのページを部分的に引用して、命令開始の A コマンドがどのように表現できるか考えてみましょう。（ImageMagick で雑に変換してるので画質が悪いです。）

![i-031.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4162/e97e1fd6-1285-fe18-0139-78ca7c2bcb70.png)

命令のデータは &lt;ESC&gt;A と書かれています。このデータを bash で表現してみましょう。コマンドシーケンスは SBPL 標準のものを使います。

```sh
ESC=$(printf '\x1b')

printf "%b" \
 "${ESC}A"
```

```console
$ bash a.sh | hexdump -C
00000000  1b 41                                             |.A|
00000002
$
```

パターン言語を使った定義を考えてみます。

```cpp
enum EscapeSeq : u8 {
  ESC = 0x1b,
};

struct A {
  char command[1];
};

struct Instruction {
  EscapeSeq;
  A;
};

Instruction sbpl @ 0x0;
```

これらのデータを ImHex に読み込ませると次のような画面が出ると思います。

![a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4162/52cfd9ee-9c42-5cb1-ccf3-576905b31377.png)

u8 や str といった基本的なデータ型や、enum や struct を使った独自のデータ構造を作ることができます。
この定義の特徴としては…

- EscapeSeq: エスケープシーケンスを符号なしの 1 バイト整数
- 構造体 A: コマンドの実体
- Instruction: 命令全体

なんとなく読めそうな気がしてきませんか？雰囲気的にチューリングマシンが 1 本のテープを先頭から末尾に向かってシークしてるように見えてきませんか？

次はバーコード印字の BG コマンドを定義してみましょう。命令のデータは &lt;ESC&gt;BG です。

![i-058.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4162/5ef2d1cb-3646-8201-36e9-a4704df6a815.png)

A コマンドと違って BG コマンドには引数があります。

```sh
ESC=$(printf '\x1b')

printf "%b" \
 "${ESC}BG02080018"
```

```console
$ bash bg.sh | hexdump -C
00000000  1b 42 47 30 32 30 38 30  30 31 38                 |.BG02080018|
0000000b
$
```

このデータに意味付けするとしたら例えばこんな感じです。

```cpp
enum EscapeSeq : u8 {
  ESC = 0x1b,
};

struct BG {
  char command[2];
  char narrow_bar_width[2];
  char barcode_height[3];
  u8 barcode[3];
};

struct Instruction {
  EscapeSeq;
  BG;
};

Instruction sbpl @ 0x0;
```

![bg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4162/c3653460-9de6-fed7-cf3e-eca9bc1612ef.png)

BG コマンドは CODE128 のバーコードを印字できます。ただしこの定義には問題があります。

- バーコードが 3 バイトのデータしか入らない
- Instruction の定義が A コマンドのときと違う
- BG 構造体の各メンバにコマンド引数が適切に指定されてるか分からない

それぞれの問題を解決するために色々と抽象化してみましょう、といったように段階を踏んでデータ構造の理解を深めることができます。
パターン言語自体についてより詳しくはオフィシャルのドキュメントを参考にしてください。

# まとめ

ここまで読んでパターン言語なんてものを書きたくない、という反応も理解できます。ChatGPT が ImHex のソースコードを読んでくれれば生成できた類かもしれません。

でも ChatGPT が生成したものを利用しようとして、正しく定義できてるのかを判断するのは今のところ人頼みです。今後も色んな観点のデバッグ手法を考えていきたいですね。