1278 lines
No EOL
46 KiB
Ruby
1278 lines
No EOL
46 KiB
Ruby
#******************************************************************************
|
||
#
|
||
# * TES本体 *
|
||
#
|
||
# for RGSS3
|
||
#
|
||
# Ver 1.00 2013.09.21
|
||
#
|
||
# ◆機能説明
|
||
# テキストファイルで作成したイベントを変換・実行するための素材です。
|
||
# 使い方等はTESドキュメントを参照して下さい。
|
||
#
|
||
# 提供者:睡工房 http://hime.be/
|
||
#
|
||
#******************************************************************************
|
||
module TES
|
||
# ---------- コンフィグ ---------------------------------------------------
|
||
|
||
#◆ 外部シナリオ(テキストファイル)の保管フォルダの場所
|
||
SOURCE_DIR = "../Scenario/"
|
||
|
||
#◆ 変換後のメインTESファイルの保管場所
|
||
# ※暗号化されるフォルダを指定して下さい。
|
||
MAIN_DIR = "Data/"
|
||
|
||
#◆ 配布用パッチTESファイルの保管場所
|
||
PATCH_DIR = "Patch/"
|
||
|
||
#◆ パッチを有効/無効
|
||
PATCH_ENABLE = false
|
||
|
||
#◆ TES変換モードの起動キー
|
||
# ※デバッグモードでのみ可能
|
||
CONVERT_MODE_BTN = :F8
|
||
|
||
#◆ 圧縮暗号化モード
|
||
# WHITE-FLUTE様の「Comp-SaveDataEX セーブデータ圧縮暗号化スクリプト」を
|
||
# 導入済みの場合、解析・改竄が困難な状態で変換が可能です。
|
||
# ※解析・改竄が困難にはなりますが、100%防止するものではありません。
|
||
# ※未導入の場合はtrueに設定しないで下さい。
|
||
WF_COMP_MODE = false
|
||
|
||
#◆ TES変換のロック
|
||
# ゲームやパッチの配布後等、TES変換したくないフォルダを指定して下さい。
|
||
TES_LOCK = []
|
||
end
|
||
|
||
#============================================================================
|
||
# 設定はここまで。以下変更禁止
|
||
#============================================================================
|
||
|
||
|
||
|
||
|
||
|
||
#----------------------------------------------------------------------------
|
||
# ◇ 例外の定義をいくつか
|
||
#----------------------------------------------------------------------------
|
||
# リンク失敗時に発生する例外
|
||
class TesLinkError < StandardError;end
|
||
|
||
# シナリオファイル読込エラー
|
||
class TesLoadError < StandardError;end
|
||
|
||
# TESコマンド分割エラー
|
||
class TesSplitError < StandardError;end
|
||
|
||
# TES文法エラー
|
||
class TesSyntaxError < StandardError;end
|
||
|
||
# TES検証エラー
|
||
class TesValidateError < StandardError;end
|
||
|
||
# TES変換失敗の例外
|
||
class TesConvertError < StandardError;end
|
||
|
||
# TES保存失敗の例外
|
||
class TesSaveError < StandardError;end
|
||
|
||
|
||
#----------------------------------------------------------------------------
|
||
# ◇ ツクール連携
|
||
#----------------------------------------------------------------------------
|
||
class << DataManager
|
||
#--------------------------------------------------------------------------
|
||
# ● モジュール初期化 TES管理もついでに初期化
|
||
#--------------------------------------------------------------------------
|
||
alias sui_tes_init init
|
||
def init
|
||
sui_tes_init
|
||
TesManager.init
|
||
end
|
||
end
|
||
|
||
class Game_Interpreter
|
||
#--------------------------------------------------------------------------
|
||
# ● 注釈からTESにリンクする。
|
||
# タグ1:<link filename> filenameの先頭にリンク
|
||
# タグ2:<link filename label>filenameにリンクしてlabelにジャンプ
|
||
#--------------------------------------------------------------------------
|
||
alias sui_tes_command_108 command_108
|
||
def command_108
|
||
sui_tes_command_108
|
||
link_tes(@comments)
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ TESにリンク
|
||
#--------------------------------------------------------------------------
|
||
def link_tes(comments)
|
||
comments.each do |comment|
|
||
if comment =~ /<link\s+(\S+)(\s+(\S+))?>/
|
||
begin
|
||
list = TesManager.link($1)
|
||
if list and list.size > 0
|
||
tes = Game_Interpreter.new(@depth + 1)
|
||
tes.setup(list, same_map? ? @event_id : 0)
|
||
tes.link_label($3)
|
||
tes.run
|
||
end
|
||
rescue TesLinkError => err
|
||
text = err.message + "\n\tScenario -> #{$1}"
|
||
text += ", Label -> *#{$3}" if $3
|
||
msgbox(text)
|
||
exit
|
||
end
|
||
end
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ ラベルのある行にインデックスを設定
|
||
#--------------------------------------------------------------------------
|
||
def link_label(label)
|
||
return unless label
|
||
@list.size.times do |i|
|
||
if @list[i].code == 118 and @list[i].parameters[0] == label
|
||
@index = i
|
||
return
|
||
end
|
||
end
|
||
raise(TesLinkError, "リンク先のラベルは存在しませんでした。")
|
||
end
|
||
end
|
||
|
||
|
||
#----------------------------------------------------------------------------
|
||
# ◇ TES管理
|
||
#----------------------------------------------------------------------------
|
||
module TesManager
|
||
#--------------------------------------------------------------------------
|
||
# ○ モジュールのインスタンス変数
|
||
#--------------------------------------------------------------------------
|
||
@tes = {} # TES変換後のイベントコマンドリスト
|
||
#--------------------------------------------------------------------------
|
||
# ○ オブジェクト初期化
|
||
#--------------------------------------------------------------------------
|
||
def self.init
|
||
@tes = load_tes(TES::MAIN_DIR, "main")
|
||
patch if TES::PATCH_ENABLE
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ TESへのリンク
|
||
#--------------------------------------------------------------------------
|
||
def self.link(filename)
|
||
unless @tes.include?(filename)
|
||
raise(TesLinkError, "リンク先のシナリオは存在しませんでした。")
|
||
end
|
||
return @tes[filename]
|
||
end
|
||
private
|
||
#--------------------------------------------------------------------------
|
||
# ○ TESファイル読み込み
|
||
#--------------------------------------------------------------------------
|
||
def self.load_tes(dir, filename)
|
||
begin
|
||
if TES::WF_COMP_MODE
|
||
name, events = load_tes_by_wf(dir, filename)
|
||
else
|
||
name, events = load_tes_by_tk(dir, filename)
|
||
end
|
||
unless x(name) == code + filename
|
||
raise(TesLoadError)
|
||
end
|
||
return events
|
||
rescue => err
|
||
msgbox("TESの読み込みに失敗。\n\s\sFilename -> #{filename}")
|
||
return {}
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ TESファイル読み込み(標準アーカイブ)
|
||
#--------------------------------------------------------------------------
|
||
def self.load_tes_by_tk(dir, filename)
|
||
temp = load_data(dir + filename + ".rvdata2")
|
||
return Marshal.load(Zlib::Inflate.inflate(x(temp)))
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ TESファイル読み込み(WHITE-FLUTE版)
|
||
#--------------------------------------------------------------------------
|
||
def self.load_tes_by_wf(dir, filename)
|
||
events = {}
|
||
File.open(dir + filename + ".rvdata2", "rb") do |file|
|
||
header = Marshal.load(file)
|
||
contents = Marshal.load(file)
|
||
events = extract_save_contents(header, contents, filename)
|
||
end
|
||
return events
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 標準アーカイブ用難読化
|
||
#--------------------------------------------------------------------------
|
||
def self.x(text)
|
||
text = text.unpack("C*")
|
||
key = keys.unpack("C*")
|
||
index = 0
|
||
while text.size > index
|
||
k = text.size - index > key.size ? key.size : text.size - index
|
||
text[index, k] = text[index, k].zip(key[0, k]).collect{|a, b| a ^ b}
|
||
index += k
|
||
end
|
||
text.pack("C*")
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ セーブ内容の展開
|
||
#--------------------------------------------------------------------------
|
||
def self.extract_save_contents(header, contents, filename)
|
||
if filename == "main"
|
||
index = 0
|
||
else
|
||
if filename =~ /^tespatch(\d+)$/
|
||
index = $1.to_i
|
||
else
|
||
raise(TesLoadError)
|
||
end
|
||
end
|
||
if header.checksum != Zlib.crc32(contents)
|
||
raise(RuntimeError)
|
||
end
|
||
APISet.crypt_keys(8, 8, DataManager.xkeys_up(index)) do
|
||
APISet.decrypt!(contents, header.iv)
|
||
end
|
||
contents = Zlib_xStreams.inflate(contents).force_encoding("ASCII-8BIT")
|
||
xcrc = contents.slice!(0, 32)
|
||
crc = Marshal.load(xcrc)
|
||
if crc != Zlib.crc32(contents)
|
||
raise(RuntimeError)
|
||
end
|
||
return Marshal.load(contents)
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ パッチ処理
|
||
#--------------------------------------------------------------------------
|
||
def self.patch
|
||
patches = []
|
||
Dir.entries(TES::PATCH_DIR).each do |file|
|
||
patches << $1.to_i if File.basename(file) =~ /^tespatch(\d+)\.rvdata2$/
|
||
end
|
||
patches.sort.each do |index|
|
||
data = load_tes(TES::PATCH_DIR, "tespatch#{index.to_s}")
|
||
if data
|
||
data.each do |filename, events|
|
||
@tes[filename] = events
|
||
end
|
||
end
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ keys
|
||
#--------------------------------------------------------------------------
|
||
def self.keys
|
||
"89793f0446bebb472abca7e1132546b52f4cd296fdd181dc417a2ffe5ec50d2378324b71932c1041a046deb1cb150e0ca5a303cc41e0d29052d6a3036c1251cc"
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ code
|
||
#--------------------------------------------------------------------------
|
||
def self.code
|
||
"b6e9d8bb924a6b31a73be8766a44c421a3cae44dc5d236ee1e21121266090f1ae0b5860df716399b8dd50ff16a92065d76a8772cae9d66d849da706fe6b63443"
|
||
end
|
||
end
|
||
|
||
|
||
if $TEST
|
||
class Scene_Map < Scene_Base
|
||
#--------------------------------------------------------------------------
|
||
# ● フレーム更新
|
||
#--------------------------------------------------------------------------
|
||
alias sui_tes_update update
|
||
def update
|
||
sui_tes_update
|
||
if Input.press?(TES::CONVERT_MODE_BTN)
|
||
SceneManager.call(Scene_TesConvertMode)
|
||
end
|
||
end
|
||
end
|
||
|
||
|
||
#----------------------------------------------------------------------------
|
||
# ◇ TES変換モード
|
||
#----------------------------------------------------------------------------
|
||
class Scene_TesConvertMode < Scene_MenuBase
|
||
#--------------------------------------------------------------------------
|
||
# ● 開始処理
|
||
#--------------------------------------------------------------------------
|
||
def start
|
||
super
|
||
create_top_window
|
||
create_bottom_window
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● 上ウィンドウの作成
|
||
#--------------------------------------------------------------------------
|
||
def create_top_window
|
||
@top_window = Window_TesTop.new
|
||
@top_window.set_handler(:ok, method(:on_top_ok))
|
||
@top_window.set_handler(:cancel, method(:return_scene))
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● 下ウィンドウの作成
|
||
#--------------------------------------------------------------------------
|
||
def create_bottom_window
|
||
@bottom_window = Window_TesBottom.new(@top_window.height + 20)
|
||
@bottom_window.set_handler(:cancel, method(:on_bottom_cancel))
|
||
@top_window.bottom_window = @bottom_window
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● 上[決定]
|
||
#--------------------------------------------------------------------------
|
||
def on_top_ok
|
||
@bottom_window.activate
|
||
@bottom_window.select(0)
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● 下[キャンセル]
|
||
#--------------------------------------------------------------------------
|
||
def on_bottom_cancel
|
||
@top_window.activate
|
||
@bottom_window.unselect
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● 呼び出し元のシーンへ戻る
|
||
#--------------------------------------------------------------------------
|
||
def return_scene
|
||
if @bottom_window.reload?
|
||
TesManager.init
|
||
print ("\n\n----- TES Reloaded --------------------\n")
|
||
end
|
||
super
|
||
end
|
||
end
|
||
|
||
|
||
# パッチフォルダ群を表示
|
||
class Window_TesTop < Window_Selectable
|
||
#--------------------------------------------------------------------------
|
||
# ● クラス変数
|
||
#--------------------------------------------------------------------------
|
||
@@last_top_row = 0 # 先頭の行 保存用
|
||
@@last_index = 0 # カーソル位置 保存用
|
||
#--------------------------------------------------------------------------
|
||
# ● 公開インスタンス変数
|
||
#--------------------------------------------------------------------------
|
||
attr_reader :bottom_window # 下ウィンドウ
|
||
#--------------------------------------------------------------------------
|
||
# ● オブジェクト初期化
|
||
#--------------------------------------------------------------------------
|
||
def initialize
|
||
super(0, 0, Graphics.width, 72)
|
||
refresh
|
||
self.top_row = @@last_top_row
|
||
select(@@last_index)
|
||
activate
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● 桁数の取得
|
||
#--------------------------------------------------------------------------
|
||
def col_max
|
||
return 4
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● フレーム更新
|
||
#--------------------------------------------------------------------------
|
||
def update
|
||
super
|
||
@bottom_window.target = @filelist[self.index] if @bottom_window
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● 項目数の取得
|
||
#--------------------------------------------------------------------------
|
||
def item_max
|
||
@filelist ? @filelist.size : 0
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● リフレッシュ
|
||
#--------------------------------------------------------------------------
|
||
def refresh
|
||
patches = []
|
||
@filelist = []
|
||
# フォルダのパッチ番号を抽出
|
||
Dir.entries(TES::SOURCE_DIR).each do |dir|
|
||
patches << $1.to_i if File.basename(dir) =~ /^tespatch(\d+)$/
|
||
end
|
||
# 数値でソートして代入
|
||
patches.sort.each do |patch|
|
||
@filelist << "tespatch#{patch}"
|
||
end
|
||
# メインを先頭に挿入
|
||
@filelist.unshift("main")
|
||
create_contents
|
||
draw_all_items
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● 項目の描画
|
||
#--------------------------------------------------------------------------
|
||
def draw_item(index)
|
||
draw_text(item_rect_for_text(index), @filelist[index])
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● キャンセルボタンが押されたときの処理
|
||
#--------------------------------------------------------------------------
|
||
def process_cancel
|
||
super
|
||
@@last_top_row = top_row
|
||
@@last_index = index
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 下ウィンドウの設定
|
||
#--------------------------------------------------------------------------
|
||
def bottom_window=(bottom_window)
|
||
@bottom_window = bottom_window
|
||
update
|
||
end
|
||
end
|
||
|
||
# パッチフォルダ内の詳細を表示。
|
||
class Window_TesBottom < Window_Selectable
|
||
#--------------------------------------------------------------------------
|
||
# ● オブジェクト初期化
|
||
#--------------------------------------------------------------------------
|
||
def initialize(top_height)
|
||
super(0, top_height, Graphics.width, Graphics.height - top_height)
|
||
@target = nil
|
||
@reload = false
|
||
self.opacity = 0
|
||
@update_info = TesManager.load_update_info
|
||
create_caption
|
||
refresh
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● 終了処理
|
||
#--------------------------------------------------------------------------
|
||
def terminate
|
||
super
|
||
@caption.bitmap.dispose
|
||
@caption.diopose
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● フレーム更新
|
||
#--------------------------------------------------------------------------
|
||
def update
|
||
super
|
||
if Input.press?(:SHIFT) and Input.trigger?(:C)
|
||
Sound.play_use_item
|
||
TesManager.convert(@target)
|
||
@update_info = TesManager.load_update_info
|
||
refresh
|
||
@reload = true
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ キャプション作成
|
||
#--------------------------------------------------------------------------
|
||
def create_caption
|
||
@caption = Sprite.new
|
||
@caption.x = padding
|
||
@caption.y = self.y - 10
|
||
@caption.bitmap = Bitmap.new(contents_width, 15)
|
||
@caption.bitmap.font.size = 16
|
||
@caption.bitmap.font.color = system_color
|
||
rect = @caption.bitmap.rect
|
||
@caption.bitmap.draw_text(rect, "ファイル名")
|
||
rect.x = 195
|
||
@caption.bitmap.draw_text(rect, "アーカイブ内更新日時")
|
||
rect.x = 365
|
||
@caption.bitmap.draw_text(rect, "ソースフォルダ内更新日時")
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● 項目数の取得
|
||
#--------------------------------------------------------------------------
|
||
def item_max
|
||
@info_list ? @info_list.size : 0
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ ターゲットの指定
|
||
#--------------------------------------------------------------------------
|
||
def target=(value)
|
||
if @target != value
|
||
@target = value
|
||
refresh
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● リフレッシュ
|
||
#--------------------------------------------------------------------------
|
||
def refresh
|
||
return unless @target
|
||
@info_list = []
|
||
list = {}
|
||
src = TES::SOURCE_DIR + @target + "/"
|
||
# 更新情報からリスト作成
|
||
if @update_info[@target]
|
||
@update_info[@target].each do |filename, mtime|
|
||
list[filename] = [mtime, nil]
|
||
end
|
||
end
|
||
# フォルダからリスト作成
|
||
Dir.entries(src, :encoding => "UTF-8").each do |file|
|
||
if File.extname(src + file) == ".txt" and file != "replace.txt"
|
||
list[file] = [nil, nil] unless list.include?(file)
|
||
list[file][1] = File.mtime(src + file)
|
||
end
|
||
end
|
||
# リストを配列に変換
|
||
list.each do |filename, mtimes|
|
||
@info_list << [filename, mtimes[0], mtimes[1]]
|
||
end
|
||
create_contents
|
||
contents.font.size = 20
|
||
draw_all_items
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● 項目の描画
|
||
#--------------------------------------------------------------------------
|
||
def draw_item(index)
|
||
info = @info_list[index]
|
||
change_color(info[1] == info[2] ? normal_color : crisis_color)
|
||
ctime = info[1] ? info[1].strftime("%Y.%m.%d %H:%M:%S") : "none"
|
||
mtime = info[2] ? info[2].strftime("%Y.%m.%d %H:%M:%S") : "none"
|
||
rect = item_rect_for_text(index)
|
||
rect.width = 195
|
||
draw_text(rect, info[0])
|
||
rect.x = 195
|
||
draw_text(rect, ctime)
|
||
rect.x = 365
|
||
draw_text(rect, mtime)
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ● TESのリロードが必要か?
|
||
#--------------------------------------------------------------------------
|
||
def reload?
|
||
@reload
|
||
end
|
||
end
|
||
|
||
|
||
#----------------------------------------------------------------------------
|
||
# ◇ TES管理
|
||
#----------------------------------------------------------------------------
|
||
module TesManager
|
||
#--------------------------------------------------------------------------
|
||
# ○ モジュールのインスタンス変数
|
||
#--------------------------------------------------------------------------
|
||
@output = [] # 出力情報
|
||
#--------------------------------------------------------------------------
|
||
# ○ TES変換
|
||
#--------------------------------------------------------------------------
|
||
def self.convert(target)
|
||
init_members
|
||
dir = TES::SOURCE_DIR + target + "/"
|
||
if TES::TES_LOCK.include?(target)
|
||
output("!!!!! このシナリオフォルダはロックされています !!!!!")
|
||
output("!!!!! 変換処理をキャンセルします。 !!!!!\n\n")
|
||
save_output
|
||
return
|
||
end
|
||
|
||
# 変換開始
|
||
begin
|
||
output("----- TES変換開始; Time : #{Time.now.to_s} -----")
|
||
output("----- Target -> #{target}\n")
|
||
Dir.entries(dir, :encoding => "UTF-8").each do |file|
|
||
convert_file(dir, file)
|
||
end
|
||
raise(TesConvertError) if @error
|
||
save_dir = target == "main" ? TES::MAIN_DIR : TES::PATCH_DIR
|
||
save_tes(@events, save_dir, target)
|
||
update_update_info(target)
|
||
output("----- 変換が完了しました -----\n\n\n")
|
||
rescue => err
|
||
p err.message
|
||
output("----- 変換に失敗しました -----\n\n\n")
|
||
end
|
||
save_output
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 変換情報出力
|
||
#--------------------------------------------------------------------------
|
||
def self.output(text)
|
||
@output << text
|
||
print text + "\n"
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ アップデート情報読み込み
|
||
#--------------------------------------------------------------------------
|
||
def self.load_update_info
|
||
begin
|
||
filelist = load_data(TES::SOURCE_DIR + "update.rvdata2")
|
||
rescue
|
||
filelist = {}
|
||
end
|
||
return filelist
|
||
end
|
||
private
|
||
#--------------------------------------------------------------------------
|
||
# ○ メンバ変数の初期化
|
||
#--------------------------------------------------------------------------
|
||
def self.init_members
|
||
@info = {}
|
||
@error = false
|
||
@output = []
|
||
@events = {}
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ TES変換(ファイル単位)
|
||
#--------------------------------------------------------------------------
|
||
def self.convert_file(dir, file)
|
||
return unless File.extname(dir + file) == ".txt"
|
||
return if file == "replace.txt" # 置換リストは処理しない
|
||
output("\n++++++++++ TesFile: #{file} ++++++++++\n")
|
||
loader = Tes_Loader.new
|
||
splitter = Tes_Splitter.new
|
||
validator = Tes_Validator.new
|
||
converter = Tes_Converter.new
|
||
|
||
loader.run(dir, file)
|
||
splitter.run(dir, loader.data) unless loader.error?
|
||
validator.run(splitter.data) unless splitter.error?
|
||
converter.run(validator.data) unless validator.error?
|
||
unless converter.error?
|
||
@events[File.basename(file, ".*")] = converter.data
|
||
@info[file] = File.mtime(dir + file)
|
||
else
|
||
@error = true
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ TESファイル書き込み
|
||
#--------------------------------------------------------------------------
|
||
def self.save_tes(events, dir, filename)
|
||
begin
|
||
name = x(code + filename)
|
||
if TES::WF_COMP_MODE
|
||
save_tes_by_wf(name, events, dir, filename)
|
||
else
|
||
save_tes_by_tk(name, events, dir, filename)
|
||
end
|
||
rescue
|
||
output("暗号化ファイルへの書き込みに失敗。\n\s\sFilename -> #{filename}")
|
||
raise
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ TESファイル書き込み(標準アーカイブ)
|
||
#--------------------------------------------------------------------------
|
||
def self.save_tes_by_tk(name, events, dir, filename)
|
||
temp = Marshal.dump([name, events])
|
||
temp = x(Zlib::Deflate.deflate(temp, Zlib::BEST_COMPRESSION))
|
||
save_data(temp, dir + filename + ".rvdata2")
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ TESファイル書き込み(WHITE-FLUTE版)
|
||
#--------------------------------------------------------------------------
|
||
def self.save_tes_by_wf(name, events, dir, filename)
|
||
File.open(dir + filename + ".rvdata2", "wb") do |file|
|
||
contents, header = make_save_contents([name, events], filename)
|
||
Marshal.dump(header, file)
|
||
Marshal.dump(contents, file)
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ セーブ内容の作成
|
||
#--------------------------------------------------------------------------
|
||
def self.make_save_contents(contents, filename)
|
||
if filename == "main"
|
||
index = 0
|
||
else
|
||
if filename =~ /^tespatch(\d+)$/
|
||
index = $1.to_i
|
||
else
|
||
raise(TesSaveError)
|
||
end
|
||
end
|
||
contents = Marshal.dump(contents)
|
||
xcrc = DataManager.make_crc32_internal(contents)
|
||
contents = xcrc + contents
|
||
compstr = Zlib_xStreams.deflate(contents)
|
||
header = Game_SaveComp.new
|
||
APISet.crypt_keys(8, 8, DataManager.xkeys_up(index)) do
|
||
APISet.encrypt!(compstr, header.iv)
|
||
end
|
||
header.checksum = Zlib.crc32(compstr)
|
||
[compstr, header]
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ アップデート情報読み込み
|
||
#--------------------------------------------------------------------------
|
||
def self.load_update_info
|
||
begin
|
||
filelist = load_data(TES::SOURCE_DIR + "update.rvdata2")
|
||
rescue
|
||
filelist = {}
|
||
end
|
||
return filelist
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ アップデート情報書き込み
|
||
#--------------------------------------------------------------------------
|
||
def self.save_update_info(info)
|
||
begin
|
||
save_data(info, TES::SOURCE_DIR + "update.rvdata2")
|
||
rescue
|
||
output("アップデート情報の書き込みに失敗しました。")
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ アップデート情報更新
|
||
#--------------------------------------------------------------------------
|
||
def self.update_update_info(target)
|
||
update_info = load_update_info
|
||
update_info[target] = @info
|
||
save_update_info(update_info)
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 変換情報テキストへ出力
|
||
#--------------------------------------------------------------------------
|
||
def self.save_output
|
||
begin
|
||
unless FileTest.exist?(TES::SOURCE_DIR + "tes_output.txt")
|
||
File.open(TES::SOURCE_DIR + "tes_output.txt", "w") {}
|
||
end
|
||
File.open(TES::SOURCE_DIR + "tes_output.txt", "r+") { |f|
|
||
old = f.read
|
||
f.pos = 0
|
||
@output.each do |text|
|
||
f.write(text + "\n")
|
||
end
|
||
f.write(old)
|
||
}
|
||
rescue
|
||
msgbox("変換情報の出力に失敗しました。")
|
||
end
|
||
end
|
||
end
|
||
|
||
|
||
#----------------------------------------------------------------------------
|
||
# ◇ TES変換
|
||
#----------------------------------------------------------------------------
|
||
class Tes_ConverterBase
|
||
#--------------------------------------------------------------------------
|
||
# ○ オブジェクト初期化
|
||
#--------------------------------------------------------------------------
|
||
def initialize
|
||
@error = false
|
||
@data = []
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 処理開始
|
||
#--------------------------------------------------------------------------
|
||
def run
|
||
pre_processing
|
||
processing
|
||
post_processing
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 前処理
|
||
#--------------------------------------------------------------------------
|
||
def pre_processing
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ メイン処理
|
||
#--------------------------------------------------------------------------
|
||
def processing
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 後処理
|
||
#--------------------------------------------------------------------------
|
||
def post_processing
|
||
@data.clear if error?
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ データ取得
|
||
#--------------------------------------------------------------------------
|
||
def data
|
||
return @data
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ エラーフラグ
|
||
#--------------------------------------------------------------------------
|
||
def error?
|
||
return (@data.empty? or @error)
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 行数取得
|
||
#--------------------------------------------------------------------------
|
||
def line_number
|
||
0
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ エラーメッセージの出力
|
||
#--------------------------------------------------------------------------
|
||
def err_message(err)
|
||
@error = true
|
||
message = "line " + sprintf("%04d",line_number) + " : #{err.message}"
|
||
TesManager.output(message)
|
||
end
|
||
end
|
||
|
||
class Tes_Loader < Tes_ConverterBase
|
||
#--------------------------------------------------------------------------
|
||
# ○ オブジェクト初期化
|
||
#--------------------------------------------------------------------------
|
||
def initialize
|
||
super
|
||
@line_number = 0
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 処理開始
|
||
#--------------------------------------------------------------------------
|
||
def run(dir, filename)
|
||
@dir = dir
|
||
@filename = filename
|
||
TesManager.output(">TESの読み込みを開始")
|
||
begin
|
||
super()
|
||
rescue => err
|
||
@error = true
|
||
@data.clear
|
||
TesManager.output("#{err.message}")
|
||
TesManager.output(">>TESの読み込みに失敗")
|
||
end
|
||
TesManager.output(">>>TESの読み込み処理が終了\n")
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ メイン処理
|
||
#--------------------------------------------------------------------------
|
||
def processing
|
||
super
|
||
File.open(@dir + @filename, "r", :encoding => "BOM|UTF-8") do |f|
|
||
f.each_line do |line|
|
||
@line_number += 1
|
||
line.gsub!(/\r\n|\r|\n/) { "" }
|
||
@data << [line, @line_number]
|
||
end
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 後処理
|
||
#--------------------------------------------------------------------------
|
||
def post_processing
|
||
super
|
||
if @data.empty?
|
||
raise(TesLoadError, "\n>>変換できるシナリオがありませんでした")
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 行数取得
|
||
#--------------------------------------------------------------------------
|
||
def line_number
|
||
@line_number
|
||
end
|
||
end
|
||
|
||
class Tes_Splitter < Tes_ConverterBase
|
||
#--------------------------------------------------------------------------
|
||
# ○ オブジェクト初期化
|
||
#--------------------------------------------------------------------------
|
||
def initialize
|
||
super
|
||
@index = 0
|
||
@replace_list = []
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 処理開始
|
||
#--------------------------------------------------------------------------
|
||
def run(dir, lines)
|
||
@dir = dir
|
||
@lines = lines
|
||
TesManager.output(">TESのコマンド分割を開始")
|
||
begin
|
||
super()
|
||
rescue => err
|
||
@error = true
|
||
@data.clear
|
||
TesManager.output("#{err.message}")
|
||
TesManager.output(">>TESのコマンド分割に失敗")
|
||
end
|
||
TesManager.output(">>>TESのコマンド分割処理が完了\n")
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 前処理
|
||
#--------------------------------------------------------------------------
|
||
def pre_processing
|
||
super
|
||
@replace_list.clear
|
||
return unless FileTest.exist?(@dir + "replace.txt")
|
||
File.open(@dir + "replace.txt", "r", :encoding => "BOM|UTF-8") do |f|
|
||
f.each_line do |line|
|
||
line.gsub!(/\r\n|\r|\n/) { "" }
|
||
before, after = line.split(/\t+/, 2)
|
||
@replace_list << [before, after] if !before.empty? and !after.empty?
|
||
end
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ メイン処理
|
||
#--------------------------------------------------------------------------
|
||
def processing
|
||
until eoc?
|
||
begin
|
||
allocate
|
||
rescue TesSyntaxError => err
|
||
err_message(err)
|
||
ensure
|
||
@index += 1
|
||
end
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 後処理
|
||
#--------------------------------------------------------------------------
|
||
def post_processing
|
||
super
|
||
if @data.empty?
|
||
raise(TesSplitError, "\n>>TESコマンドが検出されませんでした")
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 分割処理の振り分け
|
||
#--------------------------------------------------------------------------
|
||
def allocate
|
||
text = get_line
|
||
return if text.empty? # 空行は飛ばす
|
||
first = get_first_character(text)
|
||
if first == "*" # ラベル分割
|
||
split_label2
|
||
elsif first == "@" # タグ分割
|
||
tag = get_command_name(text)
|
||
if respond_to?("split_#{tag}") # 専用分割コマンドがある場合は処理
|
||
send("split_#{tag}")
|
||
else
|
||
split_normal # 一般タグの分割処理
|
||
end
|
||
else
|
||
split_message # それ以外はメッセージ行分割処理
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 分割処理(メッセージ)
|
||
#--------------------------------------------------------------------------
|
||
def split_message
|
||
if @data.empty? or @data[-1][0] != "message_h"
|
||
@data << ["message_h", {}, line_number] # デフォルト値ヘッダー設定
|
||
end
|
||
messages = []
|
||
until eoc?
|
||
temp = replace_message(get_line)
|
||
messages << ["message", {"value" => temp}, line_number]
|
||
break if messages.size >= 4 or !next_message? # 最大4行
|
||
@index += 1
|
||
end
|
||
@data += messages
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 分割処理(ラベル)
|
||
#--------------------------------------------------------------------------
|
||
def split_label2
|
||
label = get_line[1, 9999]
|
||
if label.empty?
|
||
raise(TesSyntaxError, "ラベルが無名です。")
|
||
end
|
||
@data << ["label", {"value" => label}, line_number]
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 分割処理(一般タグ)
|
||
#--------------------------------------------------------------------------
|
||
def split_normal
|
||
text = get_line
|
||
tag = get_command_name(text)
|
||
args = get_command_args(text)
|
||
@data << [tag, args, line_number]
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 行数取得
|
||
#--------------------------------------------------------------------------
|
||
def line_number
|
||
eoc? ? -1 : @lines[@index][1]
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コマンドの終端か?
|
||
#--------------------------------------------------------------------------
|
||
def eoc?
|
||
return @index >= @lines.size
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コメントと前後空白の削除
|
||
#--------------------------------------------------------------------------
|
||
def erace_comment(line)
|
||
return line ? line.gsub(/#.*/) { "" }.strip : ""
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ カレント行文字列取得
|
||
#--------------------------------------------------------------------------
|
||
def get_line
|
||
return erace_comment(@lines[@index][0])
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 次の行文字列取得
|
||
#--------------------------------------------------------------------------
|
||
def get_next_line
|
||
return "" if @index + 1 >= @lines.size
|
||
return erace_comment(@lines[@index + 1][0])
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 1文字目取得
|
||
#--------------------------------------------------------------------------
|
||
def get_first_character(text)
|
||
return text.lstrip[0, 1]
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コマンド名取得
|
||
#--------------------------------------------------------------------------
|
||
def get_command_name(text)
|
||
if get_first_character(text) == "@"
|
||
return text.slice(/^\S+/)[1, 9999].downcase
|
||
else
|
||
return nil
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コマンドの引数部を取得
|
||
#--------------------------------------------------------------------------
|
||
def get_command_args(text)
|
||
temp = {}
|
||
if cmd = get_command_name(text)
|
||
if args = text[cmd.size + 1, 9999].strip
|
||
args.gsub!(/\s*=\s*/) { "<=>" }
|
||
args.split(/\s+/).each do |arg|
|
||
key, param = arg.split("<=>", 2)
|
||
temp[key.downcase] = param
|
||
end
|
||
end
|
||
end
|
||
return temp
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 次の行がメッセージか?
|
||
#--------------------------------------------------------------------------
|
||
def next_message?
|
||
text = get_next_line
|
||
return false if text.empty? # 空行はfalse
|
||
first = get_first_character(text)
|
||
return !(first == "*" or first == "@") # 特殊文字行がきたらfalse
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ メッセージの置換処理
|
||
#--------------------------------------------------------------------------
|
||
def replace_message(text)
|
||
@replace_list.each do |before, after|
|
||
text.gsub!(/#{before}/) { after }
|
||
end
|
||
return text
|
||
end
|
||
end
|
||
|
||
class Tes_Validator < Tes_ConverterBase
|
||
#--------------------------------------------------------------------------
|
||
# ○ クラス変数
|
||
#--------------------------------------------------------------------------
|
||
@@validates = {}
|
||
#--------------------------------------------------------------------------
|
||
# ○ オブジェクト初期化
|
||
#--------------------------------------------------------------------------
|
||
def initialize
|
||
super
|
||
@index = 0
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 処理開始
|
||
#--------------------------------------------------------------------------
|
||
def run(data)
|
||
@data = data
|
||
TesManager.output(">TESのコマンド検証を開始")
|
||
begin
|
||
super()
|
||
rescue => err
|
||
@error = true
|
||
@data.clear
|
||
TesManager.output("#{err.message}")
|
||
TesManager.output(">>TESのコマンド検証に失敗")
|
||
end
|
||
TesManager.output(">>>TESのコマンド検証処理が完了\n")
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ メイン処理
|
||
#--------------------------------------------------------------------------
|
||
def processing
|
||
until eoc?
|
||
begin
|
||
validation
|
||
rescue TesValidateError => err
|
||
err_message(err)
|
||
ensure
|
||
@index += 1
|
||
end
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 後処理
|
||
#--------------------------------------------------------------------------
|
||
def post_processing
|
||
super
|
||
if @data.empty?
|
||
raise(TesValidateError, "\n>>TESコマンドの検証で問題が見つかりました")
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コマンドの検証
|
||
#--------------------------------------------------------------------------
|
||
def validation
|
||
command = get_command_name
|
||
args = get_command_args
|
||
validates = get_validate_funcs(command)
|
||
validates.each do |param_name, funcs|
|
||
funcs.each do |func, vargs|
|
||
begin
|
||
validate_func_exists?(func)
|
||
send("validate_#{func}", vargs, param_name, args)
|
||
rescue TesValidateError => err
|
||
err_message(err)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 検証コマンドの存在確認
|
||
#--------------------------------------------------------------------------
|
||
def validate_func_exists?(func)
|
||
unless respond_to?("validate_#{func}")
|
||
raise(TesValidateError,
|
||
"検証コマンド「validate_#{func}」がありません。")
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ バリデーションの取得
|
||
#--------------------------------------------------------------------------
|
||
def get_validate_funcs(tag)
|
||
return (@@validates[tag] or {})
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コマンド名の取得
|
||
#--------------------------------------------------------------------------
|
||
def get_command_name
|
||
@data[@index][0]
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コマンド名の取得
|
||
#--------------------------------------------------------------------------
|
||
def get_command_args
|
||
@data[@index][1]
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 行数取得
|
||
#--------------------------------------------------------------------------
|
||
def line_number
|
||
@data[@index][2]
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コマンドの終端か?
|
||
#--------------------------------------------------------------------------
|
||
def eoc?
|
||
return @index >= @data.size
|
||
end
|
||
end
|
||
|
||
class Tes_Converter < Tes_ConverterBase
|
||
#--------------------------------------------------------------------------
|
||
# ○ オブジェクト初期化
|
||
#--------------------------------------------------------------------------
|
||
def initialize
|
||
super
|
||
@index = 0
|
||
@indent = 0
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 処理開始
|
||
#--------------------------------------------------------------------------
|
||
def run(commands)
|
||
@commands = commands
|
||
TesManager.output(">ツクールイベントへの変換を開始")
|
||
begin
|
||
super()
|
||
rescue => err
|
||
@error = true
|
||
@data.clear
|
||
TesManager.output("#{err.message}")
|
||
TesManager.output(">>ツクールイベントへの変換に失敗")
|
||
end
|
||
TesManager.output(">>>ツクールイベントへの変換処理が完了\n")
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ メイン処理
|
||
#--------------------------------------------------------------------------
|
||
def processing
|
||
until eoc?
|
||
begin
|
||
convert
|
||
rescue TesConvertError => err
|
||
err_message(err)
|
||
ensure
|
||
@index += 1
|
||
end
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 後処理
|
||
#--------------------------------------------------------------------------
|
||
def post_processing
|
||
super
|
||
if @data.empty?
|
||
raise(TesConvertError, "\n>>変換されたイベントがありませんでした")
|
||
else
|
||
@data << RPG::EventCommand.new
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コマンドの変換
|
||
#--------------------------------------------------------------------------
|
||
def convert
|
||
command = get_command_name
|
||
args = get_command_args
|
||
convert_func_exists?(command)
|
||
temp = send("convert_#{command}", args)
|
||
@data << temp if temp
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 変換コマンドの存在確認
|
||
#--------------------------------------------------------------------------
|
||
def convert_func_exists?(func)
|
||
unless respond_to?("convert_#{func}")
|
||
raise(TesConvertError,
|
||
"変換コマンド「convert_#{func}」がありません。")
|
||
end
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コマンド名の取得
|
||
#--------------------------------------------------------------------------
|
||
def get_command_name
|
||
@commands[@index][0]
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コマンド名の取得
|
||
#--------------------------------------------------------------------------
|
||
def get_command_args
|
||
@commands[@index][1]
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ 行数取得
|
||
#--------------------------------------------------------------------------
|
||
def line_number
|
||
@commands[@index][2]
|
||
end
|
||
#--------------------------------------------------------------------------
|
||
# ○ コマンドの終端か?
|
||
#--------------------------------------------------------------------------
|
||
def eoc?
|
||
return @index >= @commands.size
|
||
end
|
||
end
|
||
end |