RGSS (RPGツクールXP/VX) のスクリプトデータを平文で得る
2004年発売のRPGツクールXPに搭載されたRGSS (Ruby Game Scripting System) のことを久しぶりに思い出して、懐かしんでいました。ゲームエンジンをRubyによってかなり自由度高く制御できます。これと出会わなければ私はこのIT業界にはいなかったでしょう。
このスクリプトデータは .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}_#{id}.rb", 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
だけ忘れていました。
RPGツクールVX, VX Aceは私は持っていなかったので、ほとんど似ていると聞いていますがもしかすると違うかもしれません。
Discussion
すみません。こちらの記事を読んで以前製作したゲームのScripts.rxdataを平文化することができましたが、rubyに詳しくないので他のrxdataファイルをmarshal.loadすることができませんでした。よろしければほかのファイルをmarshal.loadするプログラムについて教えていただけないでしょうか。
記事最後に示した参考サイトを当たると良いと思います。特に参考になるのはその記事中の
というわけでためしにActors.rvdata2をMarshal.loadしてみよう。
以下のところですね。示されているGistを使わせて頂けば、大概はうまくいくような気がします。この記事はRPGツクールVX Ace向けに書かれているのでもしかすると非互換性があり、そこは要注意です。たとえば
Actors.rxdata
なら以下コードのようにすれば読めるということです。省略している箇所はGistまたはRPGツクールのヘルプファイルを参照してコピペしてきてください。事前のこうしたクラス定義をせずに
Marshal.load
をすると、uninitialized constant RPG::BaseItem (NameError)
のように欠けているクラス定義がエラーメッセージに出るので、それを見て都度クラスを手前に足していく流れになります。おかげさまでVX(Scripts.rvdata)のファイルもテキスト化できました。
情報ありがとうございます。