追加案: CardWirthNext 1.60の非圧縮シナリオへの読み書きの対応

Create issue
Issue #311 resolved
k4nagatsuki repo owner created an issue

CardWirthNext 1.60付属のWirthBuilderで作成・編集された(データバージョン7の)、圧縮されていないシナリオへの対応を考えます。

手許で行った調査によって、技術的には、対応は可能である事が分かっています。なぜ依然としてこの機能の実装をためらっているかというと、実装によって(提案だけでも)政治的な問題を惹き起す可能性があるからです(#71を参照してください)。

しかしながら、CWNext向けに作られたシナリオがWSN形式ないし1.50形式(データバージョン4)へ変換されて公開されるという事例がすでに複数件あります。これは明らかに、非常に手間のかかる地道な作業によって移植されたものです。

そのような手間は本来不要なはずであり、政治的な問題によって生じた不利益を無辜のユーザが被るというかねてよりの懸念が現実化してしまっています。そのような不利益はできるだけ減らすべきですから、そろそろそのためにリスクを取るべきであると判断しました。

シナリオ作者は、自分のシナリオを好きな形式に変換する簡単な手段を持っていてしかるべきです。

表題の機能を実装する事で、以下の事が容易に行えるようになります。

  • 非圧縮のCWNext 1.60形式(データバージョン7)のシナリオをWSN形式へ変換する。
  • WSN形式のシナリオを非圧縮のCWNext 1.60形式(データバージョン7)のシナリオへ変換する。
  • 非圧縮のCWNext 1.60形式(データバージョン7)のシナリオをCW 1.28~1.50(データバージョン4)へ変換する。

表題の機能は、以下のようにして実装したいと考えています。

  1. 非圧縮のCWNext 1.60形式(データバージョン7)のシナリオの読み込みを、cwxeditorで行えるようにする。
  2. その際、内部的にはWSN形式(Wsn.4)に変換する。保存の際にはWSN形式となる。
  3. WSNで対応していない機能は以下の通りに対応する。また、変換できていない事を示すために@@@NEXTのような文字列からはじまる説明コメントをつけ、検索で見つけられるようにする。

    • ミリ秒単位の空白時間 …… できるだけ近い値の従来の空白時間に変換。
    • 「荷物袋のみ」「選択メンバのバックパック」を対象とした配付・所持分岐・削除 …… 「荷物袋」「選択メンバ」に変換。
    • セーブ・キャンプの禁止 …… Wsn.4の状況設定コンテントに変換。
    • 埋め込みされた素材 …… 外部化する。
    • 数値ステップ …… データ量を節約するため、Wsn.4のコモンに変換する。
  4. なんらかの経路で、現在編集中のシナリオをデータバージョン7のシナリオとしてエクスポートできるようにする。

内部的に変換する理由は、データ形式間に完全な互換性が無いためです。そのまま保存できるようにすると、バックパック関係や埋め込みの効果音など、いくつかの情報が上書き保存しただけで失われてしまいます。

同じ理由で、1.60形式で保存する時にも、通常とは別の経路を用意する必要があります。今のところ、「名前を付けて保存」の出力タイプで「CardWirthNext 1.60形式のシナリオとしてエクスポート」のような選択肢を選んで出力する形を考えています。


1.50対応とされるシナリオの多くで、格納イメージが破損したビットマップになっている事が確認されています(詳細はcwx/editor/dwt/gui/dutils.dのloadImage周りのコードを読めばだいたい網羅できます)。それらのイメージは、CardWirthでは読めるものの、外部に書き出して他のアプリケーションで読もうとしても読み込めないか、壊れた内容で読み込まれてしまいます。

これはβ版など一部バージョンのWirthBuilderのイメージ形式変換処理のバグに由来するものではないかと私は考えていたのですが、調査したところ、最初から1.60をターゲットとして作られたシナリオの格納イメージの中にも、かなり多くの破損ビットマップがあるようです。とすると、発生条件は不明ながら、1.50系の正式版のWirthBuilderや、現行の1.60系のWirthBuilderにもビットマップを破損させるバグがある事はほぼ間違いないと考えなければなりません。

どうせシナリオを変換するのであればと、これらの格納イメージを自動的に修復する処理を入れる事も考えたのですが、勝手にイメージを修復するのはライセンス的に微妙かもしれません。これらは、とりあえずそのままにして、cwxeditor/CardWirthPyで読み込む時に内部的に修復しながらロードするという状態を当面は維持したいと考えています。

修復専用の別ツールを提供する方がいいかもしれません。


圧縮されたシナリオと宿の変換が行えるようになる見込みはありません。

それらの変換を行うのであれば、WDPではじまる圧縮データがどのようなアルゴリズムで生成されているかを知る必要があります。WirthBuilderで圧縮されているデータとされていないデータを作成し、内容を調べてみました。

内容を持たない2つのパッケージを作って比較してみます。Package1.widの元のデータは以下です。

07 00 00 00 01 31 01 00

圧縮されたデータは以下になります。

57 44 50 00 7B 95 0A CE 36 96 2C 4B 4B 8C 45 12 7A 8D 54 E7 D1 48

Package2.widの元データは以下です。Package1.widと異なるのはIDの部分(01->02)の1バイトだけです。

07 00 00 00 01 31 02 00

圧縮されると以下になります。

57 44 50 00 E2 3D ED FA C7 5B D3 8B FF D7 E1 3F 32 57 FA C4 2B 39

このように、ほとんど同じデータがまるで異なるバイト列に変換されています。これは圧縮処理のアルゴリズムが暗号化的なものである事を意味しています。

また、WirthBuilderで圧縮を有効にしたシナリオは、CAB等でアーカイブした際に、非圧縮のシナリオに対して1~2割ほどサイズの増加が見られます。比較的効率が悪いという事は、既成の圧縮アルゴリズムは使われていない可能性が高い事を示唆しているように思われます。

この未知のアルゴリズムを読み解く方法は、少なくとも2つ考えられます。

  • 圧縮前と圧縮後のデータを手懸りにして暗号解読の手法でアルゴリズムと鍵を推測する。ただしそれらが充分強力な場合は解読不能です。
  • CardWirthNextやWirthBuilderを逆コンパイルして該当処理を探し、コードを読み解く。

私にはどちらの技能もありませんし、身につけられる見込みも無く、今後このような技能を持つ協力者が現れる事も期待できません。

従って、WirthBuilderで圧縮を解除できるシナリオはともかく、宿の変換は将来も不可能である可能性が高いです。

(余談ですが、MP3やPNG等の圧縮済データの埋め込みが容量のほとんどを占める*.widファイルをアーカイブすると、圧縮無効側に対する圧縮有効側のサイズの増加はほんのわずかになります。これは元データが圧縮後のバイト列の傾向に影響する――完全にランダムに見えるような結果にはならない――事を意味しているように思えますが、私の知識レベルでは手懸りになりません)

Comments (7)

  1. k4nagatsuki reporter

    WSN形式のシナリオをデータバージョン7にエクスポートする際、エクスポート先で使用できない機能を簡単に検出できるようにしておく必要があります。

    内部的にはWSN形式を編集している形になるので、従来の警告機能をそのままで使用する事はできません。そこで、pull request #533で、誤り検索に判定基準となるデータ(エンジン)バージョンを指定できるようにする機能をつけました。ここに「CardWirthNext 1.60」のような選択肢を追加し、それを選択して誤り検索を行うと、エクスポート先で使えない機能が一覧できるようにしたいと考えています。

    ついでなので、近年発見された状態変数値の変更と判定コンテントが連続で置かれていると変数値の変更が判定に反映されない1.60固有のバグについても警告したいところです。手許で調べたところ、このバグは、フラグの反転や代入、ステップの加算・減算などの全ての値の変更と、フラグ判定・ステップ判定の全ての組み合わせで発生するようです。たぶんコンテント処理と分岐先判定の処理の順序が変わったせいでバグが発生したのだと思います。

  2. k4nagatsuki reporter

    効果コンテントに仕様上の齟齬があります。CWNext 1.60では効果コンテントによる死亡イベントの発火が起こりますが、キーコードイベントは発生しません。

    当初、Wsn.2のイベント発火機能をオンにしてキーコードが無い状態にすれば同じ結果になると考えていたのですが、実際にはうまくいかないケースがあります。キーコード不所持発火条件!キーコードがあるためです。

    例えばキーコード「遠距離攻撃」を持たない効果だけ防ぐ意図でエネミーカードに!遠距離攻撃を発火条件にしたイベントを配置しておくと、キーコードを持たない効果コンテントでも発火してしまいます。そのような場合に、効果コンテントのカード視覚効果によって当該エネミーカードを動かす演出を行ったりすると問題が発生します。

    これは微妙な齟齬ですが、充分にありえるケースです。とりあえず、死亡イベントを発火させうる効果「ダメージ」「吸収」「麻痺」「対象消去」を持つ効果コンテントだけイベント発火をオンにするようにすれば、「充分にありえる」から「ほとんど無い」くらいまで問題を軽減できると思います。

  3. k4nagatsuki reporter

    pull request #539

    ほぼ上記の通り実装しました。

    数値ステップのコモンへの変換については、CWNextでの通常ステップの最大の値数100を超えた場合のみ行うようにしています。

  4. k4nagatsuki reporter

    pull request #545

    ステップの値が0 .. 値数と完全に一致し、特殊文字も展開しない場合、数値ステップとして扱っても差し支えないので、エクスポート時に数値ステップ化するようにしました。

  5. Log in to comment