Skip to content

Instantly share code, notes, and snippets.

@asm256
Last active December 27, 2015 05:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save asm256/81d883c6b038387f7986 to your computer and use it in GitHub Desktop.
Save asm256/81d883c6b038387f7986 to your computer and use it in GitHub Desktop.
#unbox.rb
# AOIMY01形式の.box専用
# .boxファイルを引数に入力すると
# 中身のコンパイル済み.txtと簡易デコンパイルしたdec_*.txtを出力する
# .boxファイルはbox.vfsやobj.vfsを展開すると手に入る
# 以下に256byteのメモリダンプが必要
# 256 BYTE memory dump from 0x002F7640
ARGSNUM = [
]
#コンパイル済み.txtの書式
#1byteのコマンド指定
# コマンドによっては引数がある
# 引数の数はコマンドに依存する
#0x02(msg) : msgを表示する
#0x03(charname,voice=null,unk=null) : 発言者欄にcharnameを設定し、voiceを再生する
#0x05() : 改ページ
#0x07(addr,msg,unkA=null,unkB=null) : 選択肢を表示し0x05命令で止まる 選択された場合 goto addr
#0x0A(VAR,VAL) : 変数VARにVALをセットする
#0x0B(VAR,VAL,unkA=null) : 変数VARにVALを加算する
#0x15(addr) : goto addr
#0x16(addr) : call addr
#0x17 : return 0x16で飛んで0x17で返ってくる
#0x1A(cond,addr) : if(cond == false) goto addr
#0x2E(VAR,range_start,range_end) : VARにrange_start ~ range_end までの乱数を挿入する
#0x3C(msg) : システムメッセージmsgを表示する
#0x41(unk) : 不明 test.box(実験コーナー)みる限り画面エフェクトだと思われる unk = 2..20
#0x49(moviefile) : moviefile(.dat)を再生する
#0x4C(imgFile,num): num番レイヤーをimgFile(.agf)で初期化する?
#0x4D(imgFile,num): num番レイヤーをimgFile(.agf)で上書きする?
# 0m0_01.txtの場合
# x4c "bg001e0.agf" 10
# ...
# x4d "bg098a0.agf" 10
# x4d "bg011b0.agf" 33
# ...
# x4d "bg098a0.agf" 10
# x4d "bg008c0.agf" 33
#0x52(imgFile,unkA,unkB,unkC,unkD=null,unkE=null): 立ち絵imgFileを表示する
#0x56(unkA,unkB,unkC=null,unkD=null): 立ち絵を消去する
# 0m0_01.txtの場合
# x52 "ta00a00.agf" 4 4 11 null null
# ...
# x56 0xffffffff 11 null null
#0x6E(num,box) : boxのnum番シナリオを再生する
#0x76(VAR,1,0,num,box) : boxのnum番シナリオのローカル既読判定をVARに格納
#0x78() : シナリオ末尾
#
#引数指定
# 引数は直前のコマンドに引数をくっつける
#特殊
#0x00 : push NULL
#0x01 LL STRING[LL] : 式を計算して返り値をpush
#0x88 LL STRING[LL] : 文字列
#一般的
#0xXY
# X : 0 ゲーム変数 C/F/S
# X : 2 シナリオローカル変数CL/FL/SL
# X : 4 グローバル変数CG/FG/SG
# C/F/Sは
# Y=2の場合はC(数値)
# Y=4の場合はF(真偽値) 1でYES 0でNO
# Y=8の場合はS(文字列)
# X : 9 アドレスの指定
# X : 8 数値の直接指定
#
# Y : 1 特殊(ポインタ?)
# Y : 2 数値(DWORD)
# Y : 4 数値(BYTE)
# Y : 8 文字列
#作ってみたがほとんど使わなかった
def zread_ucs2(buf)
dst = ""
pos = 0
len = buf.length
pos += 2 until buf[pos,2] == "\0\0" || len <= pos
return buf[0,pos] , buf[pos+2..len]
end
def box_calc(num , pos , esi, val)
val2 = ((pos >> val) & 15)
left = num >> (val2 + 1) & 0x7fffffff
right= num << (31 - val2) & 0xffffffff
(left + right + esi) & 0xffffffff
end
def decrypt_box(buf,pos,size)
b = buf.unpack("C*")
size.times{|i|
esi = (pos - 0x5CC8E9D7) & 0xffffffff
num1 = (0xA3371629 >> ((pos & 15) + 1)) - ((0x5CC8E9D7 << (31 - (pos & 15))) & 0xffffffff) + esi
num1 = num1 & 0xffffffff
num2 = box_calc(num1 , pos , esi , 4)
num3 = box_calc(num2 , pos , esi , 8)
num4 = box_calc(num3 , pos , esi , 12)
num5 = box_calc(num4 , pos , esi , 16)
num6 = box_calc(num5 , pos , esi , 20)
num7 = box_calc(num6 , pos , esi , 24)
num8 = box_calc(num7 , pos , esi , 28)
key = num8 >> (pos & 15)
# puts <<EOS
#num1 = #{num1.to_s 16}
#num2 = #{num2.to_s 16}
#num3 = #{num3.to_s 16}
#num4 = #{num4.to_s 16}
#num5 = #{num5.to_s 16}
#num6 = #{num6.to_s 16}
#num7 = #{num7.to_s 16}
#num8 = #{num8.to_s 16}
#key = #{(key & 0xff).to_s 16}
#EOS
b[i] ^= key & 0xFF
pos += 1
}
b.pack "C*"
end
def print_number_type t
case t
when 9
"&"
when 8
""
when 4
"CG"
when 2
"CL"
when 1
"CP"
when 0
"C"
end
end
def print_number type , number
t = type >> 4
tt = (type & 4 == 4 ? "F" :
type & 2 == 2 ? "C" :
type & 8 == 8 ? "S" : "@")
case t
when 9
return "ERROR(F&#{number.to_s 16})" if type & 4 != 0
"&" + "000000#{number.to_s 16}"[-6,6] + "h"
when 8
number
when 4
"#{tt}G" + "000#{number}"[-3,3]
when 2
"#{tt}L" + "000#{number}"[-3,3]
when 1
"#{tt}P" + "000#{number}"[-3,3]
when 0
"#{tt}" + "000#{number}"[-3,3]
else
puts "[unknown_type: #{type},number:#{number}]"
return "[unknown_type: #{type},number:#{number}]"
end
end
def decompile_arg txtbuf , i , argLen
arg = []
argLen.times{
type = txtbuf[i]
#puts "#{i.to_s 16} : #{type.to_s 16}"
if type & 2 != 0 then
if type & 0x80 != 0 then
buf = txtbuf[i+1,4].pack "C*"
tmp = buf.unpack("L>").first
i += 5
if type & 0x01 != 0 then
puts "[unknown 3 @pos = 0x#{i.to_s 16} , @type = 0x#{type.to_s 16}]"
else
tmp = print_number type , tmp
end
else
# 2byte 読み込み?
buf = txtbuf[i+1,2].pack "C*"
i += 3
tmp = buf.unpack("S>").first
if type & 0x01 != 0 then
#0x23を確認
#puts "unknown 2 @pos = 0x#{i.to_s 16} , @type = 0x#{type.to_s 16}"
#CG(CL(005) + 03) + 100
vtype = type
begin
tmp2 = txtbuf[i+1,2].pack("C*").unpack("s>").first
tmp = "#{print_number_type(type >> 4)}(#{tmp})+#{tmp2}"
vtype = txtbuf[i]
i += 3
end while vtype & 1 != 0
#vtype & 1 != 0 の時再帰的に変数
else
tmp = print_number type , tmp
end
end
arg.push tmp
elsif type & 4 != 0
if type & 0x80 != 0
tmp = txtbuf[i+1]
i += 2
else
#2byte読み込み
tmp = txtbuf[i+1,2].pack("C*").unpack("s>").first
i += 3
end
tmp = print_number type , tmp
arg.push tmp
elsif type & 8 != 0
if type & 0x80 != 0
len = txtbuf[i+1]
tmp = '"' + txtbuf[i+2,len*2].pack("C*").unpack("v*").pack("U*") + '"'
i += 2 + len * 2
else
tmp = txtbuf[i+1,2].pack("C*").unpack("s>").first
i += 3
tmp = "#{print_number(type , tmp)}"
#puts "unknown 8 @pos = 0x#{i.to_s 16} , @type = 0x#{type.to_s 16}" if tmp != 0
#p txtbuf[i,16]
end
arg.push tmp
elsif type & 1 != 0
# 実際には計算して結果を引数に格納してる
len = txtbuf[i+1]
arg.push 'eval(`' + txtbuf[i+2,len*2].pack("C*").unpack("v*").pack("U*").strip + '`)'
i += 2 + len * 2
else
i += 1
arg.push nil
puts "unknown @pos = 0x#{i.to_s 16} , @type = 0x#{type.to_s 16}" if type != 0
end
}
return i , arg
end
def to_my_s obj
r = ""
if obj.nil? then
r += "null"
elsif obj.kind_of? String
r += obj
elsif obj.kind_of? Array
r += "["
array_sep = false
obj.each{|x|
r += " , " if array_sep
r += to_my_s x
}
r += "]"
elsif obj.kind_of? Integer
if obj > 256
r += "0x#{obj.to_s 16}"
else
r += obj.to_s
end
else
r += obj.to_s
end
r
end
def decompile_txt txt
len = txt.size
txtbuf = txt.unpack("C*")
buffer = ""
i = 0
begin
buffer += "00000000#{i.to_s 16}"[-6,6] + ":"
cmd = txtbuf[i]
i+=1
arg = []
argLen = ARGSNUM[cmd]
i,arg = decompile_arg txtbuf,i,argLen
if cmd == 0 then
p "cmd(0) have arg" unless arg.empty?
end
buffer += "x#{cmd.to_s 16}"
arg.each{|a|
buffer += " #{to_my_s a}"
}
buffer += "\n"
end while len > i
buffer
end
def unbox target
org_buf = File.binread target
header,buf = zread_ucs2(org_buf)
return unless header.unpack("v*").pack( "U*") == "AOIMY01"
file_num , unk_header, buf = buf.unpack("NNa*")
#puts "file_num = #{file_num.to_s 16}\n"
file_num.times{
#struct{
# UCHAR name[16];
# DWORD offset;
# DWORD size;
#}
vname = buf.unpack("v16")
pos,size,buf = buf.unpack("@32NNa*")
name_len = vname.find_index 0 || 16
name = vname[0,name_len].pack("U*")
puts "\t#{name}"
File.binwrite(name , decrypt_box(org_buf[pos,size] , pos,size))
File.binwrite("dec_#{name}" , decompile_txt(decrypt_box(org_buf[pos,size] , pos,size)))
}
end
ARGV.each{|t|
puts "input: #{t}"
unbox t
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment