king-exit/Scripts/TES_.rb
2024-08-31 14:17:23 -05:00

1278 lines
No EOL
46 KiB
Ruby
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#******************************************************************************
#
# 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