TECHNICAL CASE NOTE CatSystem2 · 設定メニュー · Wine / VMR-7 採用運用あり・根本修正は未実装 実機確認: 2026-06-23

動画OFFで設定メニューが白くなる

動画ON/OFFの一変数比較で、描画処理と画面更新を切り分けた

§1

読めないメニュー

設定画面を開くと、上端にある「システム/表示/設定/ヘルプ」の文字が見えず、メニューバーは白い帯として表示された。項目自体は存在するが、位置を判別できない状態だった。

これは、macOS 上のWineで、CatSystem2を使う32bitのビジュアルノベルを動かしたときの問題だ。同じタイトルでは、OP/ED動画が黒画面のまま停止する別の問題も発生していた。

当初は「動画が黒い」と「メニューが白い」を同じ描画経路の問題として調べた。しかし、二つの症状は別に切り分ける必要があった。

§2

動画経路を先に修正した

実行ファイル内にはVMR9の文字列があったが、実行時のtraceで使われていたのはVMR7経路だった。静的な文字列だけでは実際の動画経路を判定できなかった。

Orrery同梱WineのVMR7経路で、0×0だった表示矩形、誤った表示先ウィンドウ、再生終了時のdeadlockを修正した。これにより、動画を有効にしたまま最後まで再生し、本編へ進める状態になった。

調査時間を短縮するため、prefix内のsetup.xmldisablemovie=1に変更した。この設定変更後に、メニューの白化が発生していた。

§3

最初に検証した仮説

テーマ、描画面の重なり、Wine側のflushを同じ実機条件で比較した。

テーマが文字を白く塗っている棄却

Wineの色設定ではメニュー色が(255,255,255)で、Windows風テーマも有効だった。テーマを無効化して再起動したが、表示は変わらなかった。

OpenGLの描画面がメニューを覆っている棄却

ゲーム本体のOpenGL描画面がメニューを覆っている可能性を調べた。OpenGLSurfaceMode=behindへ変更しても表示は変わらず、設定をbaselineへ戻した。

描画は呼ばれているのに画面へ届いていない観測のみ

白いbaselineでもNtUserDrawMenuBarTempと4項目分のdraw_menu_itemは発火していた。描画関数が呼ばれた事実だけでは、画面へ反映されたことを確認できない。

転送経路にパッチを当てる無変化

DC解放時に即時flushするWine patchを適用した。実験runではメニュー帯のdirty領域(0,0)-(1280,19)を含むflushを21回観測したが、表示は変わらなかったためpatchを撤回した。

§4

最後に表示できていた状態との差を取る

作業記録を確認すると、メニューが最後に表示されていたのは、動画を無効化する前だった。runtime、prefix、ゲームデータを固定し、disablemovieだけを変更して比較した。

disablemovie=1ではメニューが白くなり、disablemovie=0へ戻すと4項目の表示と操作が復活した。症状の有無を分けた最小差分は動画設定だった。

§5

動画のPresentが画面更新を駆動していた

観測と最も整合する説明は、メニュー描画と最終的な画面更新が別の段階だというものだった。動画を有効にすると、動画側のPresentが継続的に画面更新を駆動し、GDIで描いたメニュー帯もmacOS側のウィンドウへ届く。

動画を無効にすると継続的な更新が止まり、メニュー項目は論理上描かれても、ユーザーが見ている画面へ反映する駆動が不足する。この説明は動画ON/OFFの反転観測とWine traceに整合する。

ただし、動画OFF用の独立した更新処理を実装して機構を証明したわけではない。現在の採用運用は、修正済みの動画経路を有効に戻し、disablemovie=0で使用することだ。

変更したのはOrrery同梱Wineとprefix内のユーザー設定であり、ゲーム本体のexe、配布データ、セーブデータにはpatchを当てていない。

§6

再利用できる調査手順

深いtraceやruntime patchへ進む前に、成立条件の最小差分を確認する。

「動く状態」と「壊れた状態」の最小差分を先に取る

runtime、prefix、ゲームデータを固定し、症状の有無を分ける設定だけを変更する。本件ではdisablemovieが比較変数だった。

「二つの症状は同根」と決めつけない

動画の黒画面とメニューの白化は、見た目が似ていても別の処理段階に属する。観測項目と仮説を症状ごとに分ける。

「描画が呼ばれた」を「画面に出た」と混同しない

traceは描画関数の呼び出しを示すが、最終画面への反映までは保証しない。関数呼び出し、dirty領域、実機表示を別の観測項目として記録する。

表示を変えないpatchは撤回する

即時flush patchは観測値を変えたが、表示を変えなかった。無変化のpatchを成果として残さず、baselineへ戻して次の比較へ進む。

成立していた条件を調べる

失敗条件だけでなく、最後に表示できた条件を確認する。両条件の差が、別経路への依存を示すことがある。

§7

再利用できる知見

機能間の依存を分けて観測する

ある表示が成立する条件を、別機能の更新ループが支えている場合がある。各機能の処理と最終表示を別々に記録する。

最小差分は、深いトレースより速い

症状の有無を分ける最小の変数を先に比較する。深いtraceは、その差で原因範囲を絞ってから使う。

実行経路はtraceで確認する

実行ファイルにVMR9の文字列があっても、実際に使われたのはVMR7だった。静的な参照と実行時の経路を区別する。

呼び出しと可視結果を分ける

描画関数やflushが記録されても、画面へ反映されるとは限らない。実機表示を独立した検証項目にする。

§8

現在の状態と制約

採用運用
  • Orrery同梱WineのVMR7修正を適用し、disablemovie=0で使用する。
  • 動画の最後までの再生、メニュー4項目、ファイル選択画面、設定画面の全タブを実機で確認した。
  • ゲーム本体のexe・配布データ・セーブデータは変更していない。
未実装・制約
  • 動画OFFでもメニューを独立して更新する根本修正は未実装。
  • Melammuは、既存prefixに残ったdisablemovie=1を現在は自動で修正しない。
  • 一つのCatSystem2タイトルで確認した結果であり、他のエンジンやタイトルへ一般化しない。
  • 一部ダイアログの項目見切れは、フォントとレイアウトに属する別件。