試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

WOLF RPG エディター 2.10作品が3Dモード時にWineで文字化けする問題の邪道な対処

(2015/12/28)本記事は「WineでWOLF RPG エディター 2.10作品が3Dモード時に文字化けする問題と対処」へ移動した。

WOLF RPG エディターのバージョン2.10で作成された作品をWineで3Dモードで動かしたときに文字描画が崩れる現象について(2014年2月末時点)」のその後として、3Dモード時に発生する文字化けの対処についてを扱う。

  1. 問題のおさらい
  2. 修正の要領
  3. パッチ
  4. パッチ適用済みのバイナリ
  5. 設定方法

問題のおさらい

  • Sagawa氏曰く、IDirect3DDevice9::UpdateTexture()Wine(wined3d)における実装では、(バージョン1.7系時点では)「ダーティ領域」として記録された領域だけでなくテクスチャの全体がコピーされる処理になっている
  • 同氏曰く、バージョン3.11dより古い幾つかのバージョンのDXライブラリには、IDirect3DTexture9::LockRect()で矩形範囲をロック処理*1した後でIDirect3DDevice9::UpdateTexture()を呼び出した際に、先ほどロックした領域=ダーティ領域のみがコピーされるという動作を期待した処理がフォントキャッシュの扱いの中で存在し、Wineの実装では上の挙動の関係で特定の流れによってキャッシュの破損が確実に起こり、文字化けの原因となる
  • WOLF RPG エディター (ウディタ)バージョン2.10に含まれるGame.exeはこの現象が発生するバージョン(3.10f)のDXライブラリを組み込んでいる(静的リンクしている)ため影響を受ける
  • 上記動作に依存した部分は同ライブラリのバージョン3.11d以上では改善されて文字化けは起こらないが、その新しいバージョンのDXライブラリを組み込んだWOLF RPG エディターの新しいバージョンは2015年1月末時点では公開されておらず、それで既存のGame.exeを置き換えるという形の対処はまだ行えず、できるようになっても文字化けしない以外にバージョン2.10のGame.exeと完全に同じ動作になるかは不明
  • WineのBugzillaで2013年の年末にバグ報告をしたが、その後DXライブラリ側が修正されたことは分かったものの、Wine側の変更による対処には前向きではなく、パッチも提供されていない
  • 2014年2月にWOLF RPG エディターのバグ報告の掲示板にこの件についてを書いたものの、新しいバージョンが(ベータ版含め)出ていないどころか、作者によるコメントも(2013年8月より後はずっと)見られない
  • Wineのバージョン1.7.34*2時点では改善はされておらず、最近はMacユーザによる文字化け事例も複数見られるようになっている

修正の要領

WOLF RPG エディターのバージョン2.10で作成された作品をWineで3Dモードで動かしたときに文字描画が崩れる現象について(2014年2月末時点)」でも書いたような、レジストリ設定によって切り替え可能(他のプログラムの実行に影響しない形)な回避策がWine側の変更によって実現できないか試してみたところ、最終的にそのような形で動作するものが作成できた。
修正の要領としては以下のようになる。Wineのバージョンが変わって一部の修正の仕方が変わることがあっても考え方は同じ。

  • IDirect3DTexture9::LockRect()Wine実装からはテクスチャのサブリソースであるサーフェイス*3の処理としてIDirect3DSurface9::LockRect()Wine実装が中で呼ばれるが、この中において、ロックされた領域を示すRECT構造体(左/上/右/下の4つの座標の値から成る)を、サーフェイスの構造体内に別途追加した別のRECT型のメンバにコピーしておく
  • IDirect3DDevice9::UpdateTexture()*4Wine実装からはサブリソースの処理としてIDirect3DDevice9::UpdateSurface()Wine実装が中で呼ばれるが、2Dテクスチャを扱う場合の呼び出しについて、転送元サーフェイスの構造体のメンバを参照して、過去にロックされたことのあるものについては先ほどコピーした座標情報を利用して、その範囲のみが転送先サーフェイスの同一座標(左上基準・つまりコピー先のRECT構造体の左と上の座標値をPOINT構造体で用いる)へコピーされるように引数を指定する
  • 常にこの挙動で動作するようにすると他のプログラムで意図しない動作をする恐れがあるため、Wineにあるレジストリ設定の仕組みを利用して設定項目を追加(設定保持用構造体のメンバ追加と初期値指定,レジストリ設定取得処理の追加)*5し、上2つの動作はこの独自動作(以下hack)の有効時にのみ行われるようにする

というものとなった。
本来はサーフェイスの構造体内にダーティ領域を示す構造体のメンバを用意して、ダーティ領域を変化させる操作(ロック以外に複数存在する)それぞれについて扱いを正しく記述してIDirect3DDevice9::UpdateTexture()側でそれを用いてコピー範囲を決める必要がある*6のだが、WineDirect3Dでダーティ領域の情報が内部的にどのように管理されているのかがよく分からず、処理によっては関数から関数へと指定領域が引数で渡されているにも関わらず関数内でそれが一切使われていないところもあったため、結局は該当するバージョンのDXライブラリで正しく動くことだけを優先して「レジストリにより切り替え可能なhack」という形で実装した。
将来のバージョンでこのあたりの動作が改善されて特に何もせずに正しく動作するようになる可能性もなくはないが、あまり期待はできない。

パッチ

パブリックドメインとして公開する。
1.6.2向け: wine-1.6.2-dxlib-font-hack.patch
1.7.34向け*7: wine-1.7.34-dxlib-font-hack.patch
(2015/2/5)hack無効時の一部の不必要な条件分岐を省くように修正

手動でビルド*8する場合、ソースとバージョンが同一な(32bitの)ディストリのバイナリパッケージがあれば、dlls/wined3d/内のビルドが終わった後はビルドを中止して(32bitの)wined3d.dll.soのみを既存のファイル([32bitライブラリのディレクトリ]/wine/wined3d.dll.so)に対して管理者権限で上書きするだけで済む。

パッチ適用済みのバイナリ

ppa:kakurasan/unstable」のPPAリポジトリで公開した(Ubuntu 14.10向け)。バージョンは安定版系統の1.6.2のみ用意した。
PPAリポジトリの追加手順については以前AMDのlegacy版グラフィックドライバのインストールに関して書いた記事(内容自体は古いので注意)を参照。パッケージ情報の再読み込み後は「wine1.6」パッケージをインストール(インストール済みの場合はアップグレード)する。
他のディストリなどでは別途バージョン1.6.2のバイナリをインストールした上でwine1.6-i386_[バージョン]_i386.debのみ上のページから手動でダウンロードして展開し、wined3d.dll.soのみを既存のファイルに対して管理者権限で上書きする方法もあるが、正しく動作する保証はない。

設定方法

文字化け回避のモードで動かす(hackを有効にする)にはレジストリの設定が必要。
全てのプログラムに対してこのhackを有効にする場合レジストリのキー(フォルダ)「HKEY_CURRENT_USER\Software\Wine\Direct3D」の中に「DxLibFontHack」という名前の文字列を作成して値を「enabled」とする。
下はレジストリファイルとして取り込む場合の内容。
[任意]ファイル名: enable-dxlib-font-hack.reg

REGEDIT4

[HKEY_CURRENT_USER\Software\Wine\Direct3D]
"DxLibFontHack"="enabled"

無効化するファイルは「enabled」部分を他の「disabled」などの文字列に変更して別名で保存すると作れる。
特定のファイル名のプログラムに対してこのhackを有効にする*9場合は「HKEY_CURRENT_USER\Software\Wine\AppDefaults\[ファイル名]\Direct3D」の中に同様の項目を作成する。
下はGame.exeというファイル名でのみ有効になるような設定。
[任意]ファイル名: enable-dxlib-font-hack-game.reg

REGEDIT4

[HKEY_CURRENT_USER\Software\Wine\AppDefaults\Game.exe\Direct3D]
"DxLibFontHack"="enabled"

上はWOLF RPG エディター作品の動作に便利だが、より確実にWOLF RPG エディター作品だけを自動的にhackの対象にするには上記設定のファイル名部分と実際のファイル名の両方をWolfRpgGame.exeなど他とかぶりにくい名前にして使うとよい。
下は設定を自動で有効化するスクリプト例。環境に応じて一部変更して用いる。これはGame.exeWolfRpgGame.exeに名前変更して使う場合の例で、他のプログラムを動かす際にレジストリ設定の切り替えを行う場面はほとんどなくなる。
無効化するスクリプトは「enabled」部分を他の「disabled」などの文字列に変更して別名で保存すると作れる。
[任意]ファイル名: enable-dxlib-font-hack-wolfrpggame.sh

#! /bin/sh

### 標準ではない場所にWineを配置している場合
### libwine.so*のあるディレクトリを指定
#export LD_LIBRARY_PATH=/opt/wine-1.6.2/lib/i386-linux-gnu

### 標準ではない場所のWine環境を使用している場合に指定
#export WINEPREFIX=/home/user/dotwine-wolf

### wineコマンドの場所
#WINE=/opt/wine-1.6.2/bin/wine
WINE=wine

cat <<EOF | ${WINE} regedit -
REGEDIT4

[HKEY_CURRENT_USER\Software\Wine\AppDefaults\WolfRpgGame.exe\Direct3D]
"DxLibFontHack"="enabled"
EOF

使用したバージョン:

  • Wine 1.6.2, 1.7.34

*1:仕様上はここで同領域が「ダーティ領域」として記録される

*2:1.7.35については実際の動作は未確認だが、ソースの該当箇所を見る限りは同様

*3:少なくともWineDirect3Dではテクスチャという型はサーフェイス(画像データを保持するメモリ領域)という型を中に含む形になり、テクスチャからサーフェイスはサブリソースとして参照する・IDirect3DDevice9::UpdateTexture()についても、中でサブリソースのサーフェイスに対するIDirect3DDevice9::UpdateSurface()に相当する内部処理が呼ばれる形の実装になっている

*4:説明として「テクスチャのダーティー部分を更新する。」とあり、「転送元テクスチャにダーティー領域がある場合は、コピーをその領域だけに限定することで、コピー処理を最適化できる。」とも書かれているが「ダーティーとしてマークされているバイトだけがコピーされるという保証はない。」ともある

*5:これらは既存の設定項目に関係した記述を参考にしてコードを追加することで簡単に実装できる

*6:本家のソースツリーに対する修正としてはその形が望ましく、今回のような形の修正は取り込まれるべきではないと考える

*7:1.7.35については、パッチが正常に当たることは確認したが動作は未確認

*8:32bit向け・基本的には32bit環境や32bitのchroot環境でビルドする

*9:指定した以外のファイル名のプログラムでは無効になり、設定の切り替え機会を少なくできる