#******************************************************************************
#
# * 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: filenameの先頭にリンク
# タグ2: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 =~ //
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