久しぶりの日記、久しぶりのハック

日記を書かないのは、

  1. 日記を書いている暇がないほど忙しい
  2. 日記に書くほどのネタがない
  3. 日記を書くより面白いことがある

というのがだいたい主な理由だけど、ここ最近の状態は1と3が当てはまる。

日記より面白いことというのは色々で、その日によって違ったりするのでその全てを後から日記に書くこともなかなか難しいのだが、その中でも書けそうな事といったらWineのハックがある。

Wineとは Wine Is Not an Emulator の略で、Linux上でWindowsプログラムを動かすためのプログラム。昔少し使ってみたときは動作が遅すぎる上に使えない機能が多かったり起動すらしないものも結構あって、まだまだ未完成という印象を受けたが、最近のCVS HEADではだいぶ改善されていた。特にDirectX関係もちゃんと動いていて、これには驚いた。

こうなると最近のゲームを動かしたくなって、試してみたらIME関係で実装されていない関数のところで引っかかって動かない([wine-devel]Re: CVS - Notes Breakageと同じ問題に見える)。ゲームの動作にはそれほど影響しないので、中身のない関数を追加してやったらそこで止まることはなくなった(具体的には、[wine-patches]old ime function in user32 and otherのパッチを適用して、さらにWINNLSEnableIMEもstubではなく関数として定義する)。さらに続けると、日本語の取扱いがまだ不十分らしく、画面に表示される文字が化けてしまう。調べてみたら、どうもwine/objects/font.cのGetGlyphOutlineAという関数内でSHIFT-JISをUnicodeに変換しているところでおかしくなっているようだ。最初は漢字コードの問題かと思ったが、コードページに基づいてUnicodeに変換する機能自体は実装されていた。じゃあコードページの認識がおかしいのかと思ったけど、ANSIコードページ=CP932(SHIFT_JIS)が返っているようだし、CP932->Unicodeの変換テーブルもちゃんとしている。じゃあなんなんだとあきらめかけたときに、GetGlyphOutlineAのMBCS処理にバグを見付けた。

        p = FONT_mbtowc(hdc, (char*)&uChar, 1, NULL, NULL);

FONT_mbtowc(内部でMultiByteToCharを呼んでいる)に文字を渡すときに、バイト数を1に決め打ちしてるから、そりゃあ動くわけないよな。というわけで、ここを次のように直したらばっちり日本語も表示された。

        char str[4];
        int len=0;
        int i;
        for (i=0;i<4;i++) {
          str[len]=*1( len++;
        }
        p = FONT_mbtowc(hdc, str, len, NULL, NULL);

自分の持っている数本のゲームで試したところ、どれもこの修正でうまく動くようになった。DirectSoundで音も鳴るし、日本語の入力にはまだ難があるけど、これでゲームのためだけにWindowsを立ち上げる機会は減りそうだ。

*1:char*)&uChar)[3-i]; if )((len>0)||(str[len]!=0