🎮

RGSS (RPGツクールXP/VX) のスクリプトデータを平文で得る

3 min read

2004年発売のRPGツクールXPに搭載されたRGSS (Ruby Game Scripting System) のことを久しぶりに思い出して、懐かしんでいました。ゲームエンジンをRubyによってかなり自由度高く制御できます。これと出会わなければ私はこのIT業界にはいなかったでしょう。

https://tkool.jp/products/rpgxp/index/index.html

このスクリプトデータは .rxdata (VXだと .rvdata) という独自バイナリ形式で固められています。久しぶりに中を見たくなりましたが、我が家のツクールはどっかに行ってしまったので、Rubyの力で解読します。

ソースコード (Scripts.rxdata) 以外にも.rxdataはありますが、今回は省略します。


環境

Ruby 1.8.1 以上ならなんでも (初代RGSSの内蔵Rubyバージョンが1.8.1)

形式

Scripts.rxdataファイルはバイナリファイルで、Marshal.dump で固められています。フォーマットはこんな感じです。

[[ID1, セクション名1, ソースコードのzlib圧縮バイナリ1], 
 [ID2, セクション名2, ソースコードのzlib圧縮バイナリ2], ...]

デコードする実装

デコード結果をセクションごとにテキストファイルに出してみます。ソースコード文字列については、そのままだと x\x9C\xDDX_S\x13W\x14\x7Fw\xC6\xEFp+>$... のような値になっており、 zlibの展開処理が必要です。

require 'zlib'

rxdata = IO.binread("Scripts.rxdata")
data = Marshal.load(rxdata)

data.each do |id, name, content|
    content_inf = Zlib::inflate(content)
        .force_encoding("UTF-8")
        .gsub("\r\n", "\n")    
    File.write("#{name}.txt", content_inf)
end

以下は出力される Main セクションの例です。死ぬほど懐かしい。

#==============================================================================
# ■ Main
#------------------------------------------------------------------------------
#  各クラスの定義が終わった後、ここから実際の処理が始まります。
#==============================================================================

begin
  # トランジション準備
  Graphics.freeze
  # シーンオブジェクト (タイトル画面) を作成
  $scene = Scene_Title.new
  # $scene が有効な限り main メソッドを呼び出す
  while $scene != nil
    $scene.main
  end
  # フェードアウト
  Graphics.transition(20)
rescue Errno::ENOENT
  # 例外 Errno::ENOENT を補足
  # ファイルがオープンできなかった場合、メッセージを表示して終了する
  filename = $!.message.sub("No such file or directory - ", "")
  print("ファイル #{filename} が見つかりません。")
end

またこちらはGame_Tempセクションの例です。

#==============================================================================
# ■ Game_Temp
#------------------------------------------------------------------------------
#  セーブデータに含まれない、一時的なデータを扱うクラスです。このクラスのイン
# スタンスは $game_temp で参照されます。
#==============================================================================

class Game_Temp
  #--------------------------------------------------------------------------
  # ● 公開インスタンス変数
  #--------------------------------------------------------------------------
  attr_accessor :map_bgm                  # マップ画面 BGM (バトル時記憶用)
  attr_accessor :message_text             # メッセージ 文章
  attr_accessor :message_proc             # メッセージ コールバック (Proc)
  attr_accessor :choice_start             # 選択肢 開始行
  attr_accessor :choice_max               # 選択肢 項目数
  attr_accessor :choice_cancel_type       # 選択肢 キャンセルの場合
  attr_accessor :choice_proc              # 選択肢 コールバック (Proc)
  attr_accessor :num_input_start          # 数値入力 開始行
  attr_accessor :num_input_variable_id    # 数値入力 変数 ID
  # (以下略)

おわりに

Marshal.load, Zlib::inflate といった処理の組み合わせで読み込むことができます。最新のRuby 3.0.0でも読み込むことができ、Marshalの仕様は変わりないようですね。

これと逆の変換をかければ .rxdata 形式を作れることも意味します。他のエディタ・IDEでコーディングしたのちに.rxdataとして固めるという使い方が可能です。

というのは今更で、16,7年前にやるべきでしたが。RPGツクールXPのスクリプトエディタは、タブ表示もできず補完も一切なしで、2004年当時としても使いづらいものでした。

参考文献

8割がた私の記憶だけ頼りに書いており、Zlib::Inflateだけ忘れていました。

https://blog.aotak.me/post/69162974987/git-for-rpgtkool

RPGツクールVXは私は持っていなかったので、ほとんど似ていると聞いていますがもしかすると違うかもしれません。

Discussion

ログインするとコメントできます