mirror of
https://github.com/n64decomp/sm64
synced 2026-08-10 18:56:55 -04:00
init2
This commit is contained in:
Executable
+695
@@ -0,0 +1,695 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
commands = {}
|
||||
commands['seq'] = {
|
||||
# non-arg commands
|
||||
0xff: ['end'],
|
||||
0xfe: ['delay1'],
|
||||
0xfd: ['delay', 'var'],
|
||||
0xfc: ['call', 'addr'],
|
||||
0xfb: ['jump', 'addr'],
|
||||
0xfa: ['beqz', 'addr'],
|
||||
0xf9: ['bltz', 'addr'],
|
||||
0xf8: ['loop', 'u8'],
|
||||
0xf7: ['loopend'],
|
||||
0xf5: ['bgez', 'addr'],
|
||||
0xf2: ['reservenotes', 'u8'],
|
||||
0xf1: ['unreservenotes'],
|
||||
0xdf: ['transpose', 's8'],
|
||||
0xde: ['transposerel', 's8'],
|
||||
0xdd: ['settempo', 'u8'],
|
||||
0xdc: ['addtempo', 's8'],
|
||||
0xdb: ['setvol', 'u8'],
|
||||
0xda: ['changevol', 's8'],
|
||||
0xd7: ['initchannels', 'hex16'],
|
||||
0xd6: ['disablechannels', 'hex16'],
|
||||
0xd5: ['setmutescale', 's8'],
|
||||
0xd4: ['mute'],
|
||||
0xd3: ['setmutebhv', 'hex8'],
|
||||
0xd2: ['setshortnotevelocitytable', 'addr'],
|
||||
0xd1: ['setshortnotedurationtable', 'addr'],
|
||||
0xd0: ['setnoteallocationpolicy', 'u8'],
|
||||
0xcc: ['setval', 'u8'],
|
||||
0xc9: ['bitand', 'u8'],
|
||||
0xc8: ['subtract', 'u8'],
|
||||
# arg commands
|
||||
0x00: ['testchdisabled', 'arg'],
|
||||
0x50: ['subvariation', 'ign-arg'],
|
||||
0x70: ['setvariation', 'ign-arg'],
|
||||
0x80: ['getvariation', 'ign-arg'],
|
||||
0x90: ['startchannel', 'arg', 'addr'],
|
||||
}
|
||||
|
||||
commands['chan'] = {
|
||||
# non-arg commands
|
||||
0xff: ['end'],
|
||||
0xfe: ['delay1'],
|
||||
0xfd: ['delay', 'var'],
|
||||
0xfc: ['call', 'addr'],
|
||||
0xfb: ['jump', 'addr'],
|
||||
0xfa: ['beqz', 'addr'],
|
||||
0xf9: ['bltz', 'addr'],
|
||||
0xf8: ['loop', 'u8'],
|
||||
0xf7: ['loopend'],
|
||||
0xf6: ['break'],
|
||||
0xf5: ['bgez', 'addr'],
|
||||
0xf3: ['hang'],
|
||||
0xf2: ['reservenotes', 'u8'],
|
||||
0xf1: ['unreservenotes'],
|
||||
0xe4: ['dyncall'],
|
||||
0xe3: ['setvibratodelay', 'u8'],
|
||||
0xe2: ['setvibratoextentlinear', 'u8', 'u8', 'u8'],
|
||||
0xe1: ['setvibratoratelinear', 'u8', 'u8', 'u8'],
|
||||
0xe0: ['setvolscale', 'u8'],
|
||||
0xdf: ['setvol', 'u8'],
|
||||
0xde: ['freqscale', 'u16'],
|
||||
0xdd: ['setpan', 'u8'],
|
||||
0xdc: ['setpanmix', 'u8'],
|
||||
0xdb: ['transpose', 's8'],
|
||||
0xda: ['setenvelope', 'addr'],
|
||||
0xd9: ['setdecayrelease', 'u8'],
|
||||
0xd8: ['setvibratoextent', 'u8'],
|
||||
0xd7: ['setvibratorate', 'u8'],
|
||||
0xd6: ['setupdatesperframe_unimplemented', 'u8'],
|
||||
0xd4: ['setreverb', 'u8'],
|
||||
0xd3: ['pitchbend', 's8'],
|
||||
0xd2: ['setsustain', 'u8'],
|
||||
0xd1: ['setnoteallocationpolicy', 'u8'],
|
||||
0xd0: ['stereoheadseteffects', 'u8'],
|
||||
0xcc: ['setval', 'u8'],
|
||||
0xcb: ['readseq', 'addr'],
|
||||
0xca: ['setmutebhv', 'hex8'],
|
||||
0xc9: ['bitand', 'u8'],
|
||||
0xc8: ['subtract', 'u8'],
|
||||
0xc7: ['writeseq', 'u8', 'addr'],
|
||||
0xc6: ['setbank', 'u8'],
|
||||
0xc5: ['dynsetdyntable'],
|
||||
0xc4: ['largenoteson'],
|
||||
0xc3: ['largenotesoff'],
|
||||
0xc2: ['setdyntable', 'addr'],
|
||||
0xc1: ['setinstr', 'u8'],
|
||||
# arg commands
|
||||
0x00: ['testlayerfinished', 'arg'],
|
||||
0x10: ['startchannel', 'arg', 'addr'],
|
||||
0x20: ['disablechannel', 'arg'],
|
||||
0x30: ['iowriteval2', 'arg', 'u8'],
|
||||
0x40: ['ioreadval2', 'arg', 'u8'],
|
||||
0x50: ['ioreadvalsub', 'arg'],
|
||||
0x60: ['setnotepriority', 'arg'],
|
||||
0x70: ['iowriteval', 'arg'],
|
||||
0x80: ['ioreadval', 'arg'],
|
||||
0x90: ['setlayer', 'arg', 'addr'],
|
||||
0xa0: ['freelayer', 'arg'],
|
||||
0xb0: ['dynsetlayer', 'arg'],
|
||||
}
|
||||
|
||||
commands_layer_base = {
|
||||
# non-arg commands
|
||||
0xc0: ['delay', 'var'],
|
||||
0xc1: ['setshortnotevelocity', 'u8'],
|
||||
0xc2: ['transpose', 'u8'],
|
||||
0xc3: ['setshortnotedefaultplaypercentage', 'var'],
|
||||
0xc4: ['somethingon'], # ?? (something to do with decay behavior)
|
||||
0xc5: ['somethingoff'], # ??
|
||||
0xc6: ['setinstr', 'u8'],
|
||||
0xc7: ['portamento', 'hex8', 'u8', 'u8'],
|
||||
0xc8: ['disableportamento'],
|
||||
0xc9: ['setshortnoteduration', 'u8'],
|
||||
0xca: ['setpan', 'u8'],
|
||||
0xf7: ['loopend'],
|
||||
0xf8: ['loop', 'u8'],
|
||||
0xfb: ['jump', 'addr'],
|
||||
0xfc: ['call', 'addr'],
|
||||
0xff: ['end'],
|
||||
# arg commands
|
||||
0xd0: ['setshortnotevelocityfromtable', 'arg'],
|
||||
0xe0: ['setshortnotedurationfromtable', 'arg'],
|
||||
}
|
||||
|
||||
commands['layer_large'] = dict(list(commands_layer_base.items()) + list({
|
||||
0x00: ['note0', 'arg', 'var', 'u8', 'u8'],
|
||||
0x40: ['note1', 'arg', 'var', 'u8'],
|
||||
0x80: ['note2', 'arg', 'u8', 'u8'],
|
||||
}.items()))
|
||||
|
||||
commands['layer_small'] = dict(list(commands_layer_base.items()) + list({
|
||||
0x00: ['smallnote0', 'arg', 'var'],
|
||||
0x40: ['smallnote1', 'arg'],
|
||||
0x80: ['smallnote2', 'arg'],
|
||||
}.items()))
|
||||
|
||||
print_end_padding = False
|
||||
if "--print-end-padding" in sys.argv:
|
||||
print_end_padding = True
|
||||
sys.argv.remove("--print-end-padding")
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: {sys.argv[0]} (--emit-asm-macros | input.m64)")
|
||||
sys.exit(0)
|
||||
|
||||
if sys.argv[1] == "--emit-asm-macros":
|
||||
print("# Macros for disassembled sequence files. This file was automatically generated by seq_decoder.py.")
|
||||
print("# To regenerate it, run: ./tools/seq_decoder.py --emit-asm-macros >seq_macros.inc")
|
||||
print()
|
||||
def print_hword(x):
|
||||
print(f" .byte {x} >> 8, {x} & 0xff")
|
||||
|
||||
def emit_cmd(key, op, cmd):
|
||||
mn = cmd[0]
|
||||
args = cmd[1:]
|
||||
param_names = []
|
||||
param_list = []
|
||||
nibble_param_name = None
|
||||
for i, arg in enumerate(args):
|
||||
param_name = chr(97 + i)
|
||||
param_names.append(param_name)
|
||||
param_list.append(param_name + ("=0" if arg == "ign-arg" else ""))
|
||||
if arg == "ign-arg" or arg == "arg":
|
||||
nibble_param_name = param_name
|
||||
print(f".macro {key}_{mn} {', '.join(param_list)}".rstrip())
|
||||
if nibble_param_name is not None:
|
||||
print(f" .byte {hex(op)} + \\{nibble_param_name}")
|
||||
else:
|
||||
print(f" .byte {hex(op)}")
|
||||
for arg, param_name in zip(args, param_names):
|
||||
if arg in ['arg', 'ign-arg']:
|
||||
pass
|
||||
elif arg in ['s8', 'u8', 'hex8']:
|
||||
print(f" .byte \\{param_name}")
|
||||
elif arg in ['u16', 'hex16']:
|
||||
print_hword("\\" + param_name)
|
||||
elif arg == 'addr':
|
||||
print_hword(f"(\\{param_name} - sequence_start)")
|
||||
elif arg == 'var_long':
|
||||
print(f" var_long \\{param_name}")
|
||||
elif arg == 'var':
|
||||
print(f" var \\{param_name}")
|
||||
else:
|
||||
raise Exception("Unknown argument type " + arg)
|
||||
print(".endm")
|
||||
print()
|
||||
|
||||
def emit_env_cmd(op, cmd):
|
||||
mn = cmd[0]
|
||||
param_list = []
|
||||
for i, arg in enumerate(cmd[1:]):
|
||||
param_list.append(chr(97 + i))
|
||||
print(f".macro envelope_{mn} {', '.join(param_list)}".rstrip())
|
||||
if op is not None:
|
||||
print(f" .byte {hex(op >> 8)}, {hex(op & 0xff)}")
|
||||
for param in param_list:
|
||||
print_hword("\\" + param)
|
||||
print(".endm\n")
|
||||
|
||||
for key in ['seq', 'chan', 'layer']:
|
||||
print(f"# {key} commands\n")
|
||||
if key == 'layer':
|
||||
cmds = commands['layer_large']
|
||||
for op in sorted(commands['layer_small'].keys()):
|
||||
if op not in cmds:
|
||||
emit_cmd(key, op, commands['layer_small'][op])
|
||||
else:
|
||||
cmds = commands[key]
|
||||
eu = []
|
||||
non_eu = []
|
||||
for op in sorted(cmds.keys()):
|
||||
mn = cmds[op][0]
|
||||
if mn == 'setnotepriority':
|
||||
eu.append((0xe9, ['setnotepriority', 'u8']))
|
||||
non_eu.append((op, cmds[op]))
|
||||
elif mn in ['reservenotes', 'unreservenotes']:
|
||||
eu.append((op - 1, cmds[op]))
|
||||
non_eu.append((op, cmds[op]))
|
||||
elif mn not in ['portamento', 'writeseq']:
|
||||
emit_cmd(key, op, cmds[op])
|
||||
|
||||
if key == 'chan':
|
||||
print(".macro chan_writeseq val, pos, offset")
|
||||
print(" .byte 0xc7, \\val")
|
||||
print_hword("(\\pos - sequence_start + \\offset)")
|
||||
print(".endm\n")
|
||||
print(".macro chan_writeseq_nextinstr val, offset")
|
||||
print(" .byte 0xc7, \\val")
|
||||
print_hword("(writeseq\\@ - sequence_start + \\offset)")
|
||||
print(" writeseq\\@:")
|
||||
print(".endm\n")
|
||||
print(".macro layer_portamento a, b, c")
|
||||
print(" .byte 0xc7, \\a, \\b")
|
||||
print(" .if ((\\a & 0x80) == 0)")
|
||||
print(" var \\c")
|
||||
print(" .else")
|
||||
print(" .byte \\c")
|
||||
print(" .endif")
|
||||
print(".endm\n")
|
||||
emit_cmd(key, 0xfd, ['delay_long', 'var_long'])
|
||||
if key == 'layer':
|
||||
emit_cmd(key, 0xc0, ['delay_long', 'var_long'])
|
||||
emit_cmd(key, 0x40, ['note1_long', 'arg', 'var_long', 'u8'])
|
||||
if eu:
|
||||
print(".ifdef VERSION_EU\n")
|
||||
for (op, cmd) in eu:
|
||||
emit_cmd(key, op, cmd)
|
||||
print(".else\n")
|
||||
for (op, cmd) in non_eu:
|
||||
emit_cmd(key, op, cmd)
|
||||
print(".endif\n")
|
||||
|
||||
print("# envelope commands\n")
|
||||
emit_env_cmd(0, ['disable', 'u16'])
|
||||
emit_env_cmd(2**16-1, ['hang', 'u16'])
|
||||
emit_env_cmd(2**16-2, ['goto', 'u16'])
|
||||
emit_env_cmd(2**16-3, ['restart', 'u16'])
|
||||
emit_env_cmd(None, ['line', 'u16', 'u16'])
|
||||
|
||||
print("# other commands\n")
|
||||
print(".macro var_long x")
|
||||
print(" .byte (0x80 | (\\x & 0x7f00) >> 8), (\\x & 0xff)")
|
||||
print(".endm\n")
|
||||
print(".macro var x")
|
||||
print(" .if (\\x >= 0x80)")
|
||||
print(" var_long \\x")
|
||||
print(" .else")
|
||||
print(" .byte \\x")
|
||||
print(" .endif")
|
||||
print(".endm\n")
|
||||
print(".macro sound_ref a")
|
||||
print_hword("(\\a - sequence_start)")
|
||||
print(".endm")
|
||||
sys.exit(0)
|
||||
|
||||
filename = sys.argv[1]
|
||||
try:
|
||||
parts = filename.split('/seq_')
|
||||
lang = parts[0][-2:]
|
||||
seq_num = int(parts[1].split('.')[0], 16)
|
||||
except Exception:
|
||||
lang = ''
|
||||
seq_num = -1
|
||||
|
||||
try:
|
||||
with open(filename, 'rb') as f:
|
||||
data = f.read()
|
||||
except Exception:
|
||||
print("Error: could not open file {filename} for reading.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
output = [None] * len(data)
|
||||
output_instate = [None] * len(data)
|
||||
label_name = [None] * len(data)
|
||||
script_start = [False] * len(data)
|
||||
hit_eof = False
|
||||
errors = []
|
||||
seq_writes = []
|
||||
|
||||
# Our analysis of large notes mode doesn't persist through multiple channel activations
|
||||
# For simplicity, we force large notes always instead, which is valid for SM64.
|
||||
force_large_notes = True
|
||||
|
||||
if lang == 'eu':
|
||||
# unreservenotes moved to 0xf0 in EU, and reservenotes took its place
|
||||
commands['chan'][0xf0] = commands['chan'][0xf1]
|
||||
commands['chan'][0xf1] = commands['chan'][0xf2]
|
||||
del commands['chan'][0xf2]
|
||||
# total guess: the same is true for the 'seq'-type command
|
||||
commands['seq'][0xf0] = commands['seq'][0xf1]
|
||||
commands['seq'][0xf1] = commands['seq'][0xf2]
|
||||
del commands['seq'][0xf2]
|
||||
# setnotepriority moved to 0xe9, becoming a non-arg command
|
||||
commands['chan'][0xe9] = ['setnotepriority', 'u8']
|
||||
del commands['chan'][0x60]
|
||||
|
||||
def is_arg_command(cmd_args):
|
||||
return 'arg' in cmd_args or 'ign-arg' in cmd_args
|
||||
|
||||
def gen_label(ind, tp):
|
||||
nice_tp = tp.replace('_small', '').replace('_large', '')
|
||||
addr = hex(ind)[2:].upper()
|
||||
ret = f".{nice_tp}_{addr}"
|
||||
if ind >= len(data):
|
||||
errors.append(f"reference to oob label {ret}")
|
||||
return ret
|
||||
|
||||
if label_name[ind] is not None:
|
||||
return label_name[ind]
|
||||
label_name[ind] = ret
|
||||
return ret
|
||||
|
||||
def gen_mnemonic(tp, b):
|
||||
nice_tp = tp.split('_')[0]
|
||||
mn = commands[tp][b][0]
|
||||
if not mn:
|
||||
mn = f"{b:02X}"
|
||||
return f"{nice_tp}_{mn}"
|
||||
|
||||
decode_list = []
|
||||
|
||||
def decode_one(state):
|
||||
pos, tp, nesting, large = state
|
||||
orig_pos = pos
|
||||
|
||||
if pos >= len(data):
|
||||
global hit_eof
|
||||
hit_eof = True
|
||||
return
|
||||
|
||||
if output[pos] is not None:
|
||||
if output_instate[pos] != state:
|
||||
errors.append(f"got to {gen_label(orig_pos, tp)} with both state {state} and {output_instate[pos]}")
|
||||
return
|
||||
|
||||
def u8():
|
||||
nonlocal pos
|
||||
global hit_eof
|
||||
if pos == len(data):
|
||||
hit_eof = True
|
||||
return 0
|
||||
ret = data[pos]
|
||||
pos += 1
|
||||
return ret
|
||||
|
||||
def u16():
|
||||
hi = u8()
|
||||
lo = u8()
|
||||
return (hi << 8) | lo
|
||||
|
||||
def var():
|
||||
ret = u8()
|
||||
if ret & 0x80:
|
||||
ret = (ret << 8) & 0x7f00;
|
||||
ret |= u8()
|
||||
return (ret, ret < 0x80)
|
||||
return (ret, False)
|
||||
|
||||
if tp == 'soundref':
|
||||
sound = u16()
|
||||
decode_list.append((sound, 'chan', 0, True))
|
||||
if sound < len(data):
|
||||
script_start[sound] = True
|
||||
for p in range(orig_pos, pos):
|
||||
output[p] = ''
|
||||
output_instate[p] = state
|
||||
output[orig_pos] = 'sound_ref ' + gen_label(sound, 'chan')
|
||||
return
|
||||
|
||||
if tp == 'envelope':
|
||||
a = u16()
|
||||
b = u16()
|
||||
for p in range(orig_pos, pos):
|
||||
output[p] = ''
|
||||
output_instate[p] = state
|
||||
if a >= 2**16 - 3:
|
||||
a -= 2**16
|
||||
if a <= 0:
|
||||
mn = ['disable', 'hang', 'goto', 'restart'][-a]
|
||||
output[orig_pos] = f'envelope_{mn} {b}'
|
||||
# assume any goto is backwards and stop decoding
|
||||
else:
|
||||
output[orig_pos] = f'envelope_line {a} {b}'
|
||||
decode_list.append((pos, tp, nesting, large))
|
||||
return
|
||||
|
||||
ins_byte = u8()
|
||||
|
||||
cmds = commands[tp]
|
||||
if ins_byte in cmds and not is_arg_command(cmds[ins_byte]):
|
||||
used_b = ins_byte
|
||||
arg = None
|
||||
elif ins_byte & 0xf0 in cmds and is_arg_command(cmds[ins_byte & 0xf0]):
|
||||
used_b = ins_byte & 0xf0
|
||||
arg = ins_byte & 0xf
|
||||
elif ins_byte & 0xc0 in cmds and is_arg_command(cmds[ins_byte & 0xc0]) and tp.startswith('layer'):
|
||||
used_b = ins_byte & 0xc0
|
||||
arg = ins_byte & 0x3f
|
||||
else:
|
||||
errors.append(f"unrecognized instruction {hex(ins_byte)} for type {tp} at label {gen_label(orig_pos, tp)}")
|
||||
return
|
||||
|
||||
out_mn = gen_mnemonic(tp, used_b)
|
||||
out_args = []
|
||||
cmd_mn = cmds[used_b][0]
|
||||
cmd_args = cmds[used_b][1:]
|
||||
long_var = False
|
||||
|
||||
for a in cmd_args:
|
||||
if cmd_mn == 'portamento' and len(out_args) == 2 and (int(out_args[0], 0) & 0x80) == 0:
|
||||
a = 'var'
|
||||
if a == 'arg':
|
||||
out_args.append(str(arg))
|
||||
elif a == 'ign-arg' and arg != 0:
|
||||
out_args.append(str(arg))
|
||||
elif a == 'u8':
|
||||
out_args.append(str(u8()))
|
||||
elif a == 'hex8':
|
||||
out_args.append(hex(u8()))
|
||||
elif a == 's8':
|
||||
v = u8()
|
||||
out_args.append(str(v if v < 128 else v - 256))
|
||||
elif a == 'u16':
|
||||
out_args.append(str(u16()))
|
||||
elif a == 'hex16':
|
||||
out_args.append(hex(u16()))
|
||||
elif a == 'var':
|
||||
val, bad = var()
|
||||
out_args.append(hex(val))
|
||||
if bad:
|
||||
long_var = True
|
||||
elif a == 'addr':
|
||||
v = u16()
|
||||
|
||||
kind = 'addr'
|
||||
if cmd_mn == 'call':
|
||||
kind = tp + '_fn'
|
||||
elif cmd_mn in ['jump', 'beqz', 'bltz', 'bgez']:
|
||||
kind = tp
|
||||
elif cmd_mn == 'startchannel':
|
||||
kind = 'chan'
|
||||
elif cmd_mn == 'setlayer':
|
||||
kind = 'layer'
|
||||
elif cmd_mn == 'setdyntable':
|
||||
kind = 'table'
|
||||
elif cmd_mn == 'setenvelope':
|
||||
kind = 'envelope'
|
||||
|
||||
if v >= len(data):
|
||||
label = gen_label(v, kind)
|
||||
out_args.append(label)
|
||||
errors.append(f"reference to oob label {label}")
|
||||
elif cmd_mn == 'writeseq':
|
||||
out_args.append('<fixup>')
|
||||
seq_writes.append((orig_pos, v))
|
||||
else:
|
||||
out_args.append(gen_label(v, kind))
|
||||
if cmd_mn == 'call':
|
||||
decode_list.append((v, tp, 0, large))
|
||||
script_start[v] = True
|
||||
elif cmd_mn in ['jump', 'beqz', 'bltz', 'bgez']:
|
||||
decode_list.append((v, tp, nesting, large))
|
||||
elif cmd_mn == 'startchannel':
|
||||
decode_list.append((v, 'chan', 0, force_large_notes))
|
||||
script_start[v] = True
|
||||
elif cmd_mn == 'setlayer':
|
||||
if large:
|
||||
decode_list.append((v, 'layer_large', 0, True))
|
||||
else:
|
||||
decode_list.append((v, 'layer_small', 0, True))
|
||||
script_start[v] = True
|
||||
elif cmd_mn == 'setenvelope':
|
||||
decode_list.append((v, 'envelope', 0, True))
|
||||
script_start[v] = True
|
||||
else:
|
||||
script_start[v] = True
|
||||
|
||||
out_all = out_mn
|
||||
if long_var:
|
||||
out_all += "_long"
|
||||
if out_args:
|
||||
out_all += ' '
|
||||
out_all += ', '.join(out_args)
|
||||
for p in range(orig_pos, pos):
|
||||
output[p] = ''
|
||||
output_instate[p] = state
|
||||
output[orig_pos] = out_all
|
||||
|
||||
if cmd_mn in ['hang', 'jump']:
|
||||
return
|
||||
if cmd_mn in ['loop']:
|
||||
nesting += 1
|
||||
if cmd_mn == 'end':
|
||||
nesting -= 1
|
||||
if cmd_mn in ['break', 'loopend']:
|
||||
nesting -= 1
|
||||
if nesting < 0:
|
||||
# This is iffy, and actually happens in sequence 0. It will make us
|
||||
# return to the caller's caller at function end.
|
||||
nesting = 0
|
||||
if cmd_mn == 'largenoteson':
|
||||
large = True
|
||||
if cmd_mn == 'largenotesoff':
|
||||
large = False
|
||||
if nesting >= 0:
|
||||
decode_list.append((pos, tp, nesting, large))
|
||||
|
||||
def decode_rec(state, initial):
|
||||
if not initial:
|
||||
v = state[0]
|
||||
gen_label(v, state[1])
|
||||
script_start[v] = True
|
||||
decode_list.append(state)
|
||||
while decode_list:
|
||||
decode_one(decode_list.pop())
|
||||
|
||||
def main():
|
||||
decode_rec((0, 'seq', 0, False), initial=True)
|
||||
|
||||
if seq_num == 0:
|
||||
if lang == 'jp':
|
||||
sound_banks = [
|
||||
(0x14C, 0x70),
|
||||
(0x8A8, 0x38), # stated as 0x30
|
||||
(0xB66, 0x38), # stated as 0x30
|
||||
(0xE09, 0x80),
|
||||
(0x194B, 0x28), # stated as 0x20
|
||||
(0x1CA6, 0x80),
|
||||
(0x27C9, 0x20),
|
||||
(0x2975, 0x30),
|
||||
# same script as bank 3
|
||||
# same script as bank 5
|
||||
]
|
||||
unused = [
|
||||
(0x1FC4, 'layer_large'),
|
||||
(0x2149, 'layer_large'),
|
||||
(0x2223, 'layer_large'),
|
||||
(0x28C5, 'chan'),
|
||||
(0x3110, 'envelope'),
|
||||
(0x31EC, 'envelope'),
|
||||
]
|
||||
elif lang == 'us':
|
||||
sound_banks = [
|
||||
(0x14C, 0x70),
|
||||
(0x8F6, 0x38), # stated as 0x30
|
||||
(0xBB4, 0x40),
|
||||
(0xF8E, 0x80),
|
||||
(0x1AF3, 0x28), # stated as 0x20
|
||||
(0x1E4E, 0x80),
|
||||
(0x2971, 0x20),
|
||||
(0x2B1D, 0x40),
|
||||
# same script as bank 3
|
||||
# same script as bank 5
|
||||
]
|
||||
unused = [
|
||||
(0x216C, 'layer_large'),
|
||||
(0x22F1, 'layer_large'),
|
||||
(0x23CB, 'layer_large'),
|
||||
(0x2A6D, 'chan'),
|
||||
(0x339C, 'envelope'),
|
||||
(0x3478, 'envelope'),
|
||||
]
|
||||
elif lang == 'eu':
|
||||
sound_banks = [
|
||||
(0x154, 0x70),
|
||||
(0x8FE, 0x38), # stated as 0x30?
|
||||
(0xBBC, 0x40),
|
||||
(0xFA5, 0x80),
|
||||
(0x1B0C, 0x28), # stated as 0x20?
|
||||
(0x1E67, 0x80),
|
||||
(0x298A, 0x20),
|
||||
(0x2B36, 0x40),
|
||||
# same script as bank 3
|
||||
# same script as bank 5
|
||||
]
|
||||
unused = [
|
||||
(0xF9A, 'chan'),
|
||||
(0x2185, 'layer_large'),
|
||||
(0x230A, 'layer_large'),
|
||||
(0x23E4, 'layer_large'),
|
||||
(0x2A86, 'chan'),
|
||||
(0x33CC, 'envelope'),
|
||||
(0x34A8, 'envelope'),
|
||||
]
|
||||
|
||||
for (addr, count) in sound_banks:
|
||||
for i in range(count):
|
||||
decode_rec((addr + 2*i, 'soundref', 0, False), initial=True)
|
||||
|
||||
for (addr, tp) in unused:
|
||||
gen_label(addr, tp + '_unused')
|
||||
decode_rec((addr, tp, 0, force_large_notes), initial=False)
|
||||
|
||||
for (pos, write_to) in seq_writes:
|
||||
assert '<fixup>' in output[pos]
|
||||
delta = 0
|
||||
while output[write_to] == '':
|
||||
write_to -= 1
|
||||
delta += 1
|
||||
if write_to > pos and all(output[i] == '' for i in range(pos+1, write_to)):
|
||||
nice_target = str(delta)
|
||||
output[pos] = output[pos].replace('writeseq', 'writeseq_nextinstr')
|
||||
else:
|
||||
tp = output_instate[write_to][1] if output_instate[write_to] is not None else 'addr'
|
||||
nice_target = gen_label(write_to, tp) + ", " + str(delta)
|
||||
output[pos] = output[pos].replace('<fixup>', nice_target)
|
||||
|
||||
# Add unreachable 'end' markers
|
||||
for i in range(1, len(data)):
|
||||
if (data[i] == 0xff and output[i] is None and output[i - 1] is not None
|
||||
and label_name[i] is None):
|
||||
tp = output_instate[i - 1][1]
|
||||
if tp in ["seq", "chan", "layer_small", "layer_large"]:
|
||||
output[i] = gen_mnemonic(tp, 0xff)
|
||||
|
||||
# Add envelope padding
|
||||
for i in range(1, len(data) - 1):
|
||||
if (data[i] == 0 and output[i] is None and output[i - 1] is not None and
|
||||
output[i + 1] is not None and label_name[i] is None and
|
||||
output[i + 1].startswith('envelope')):
|
||||
script_start[i] = True
|
||||
output[i] = "# padding\n.byte 0"
|
||||
|
||||
# Add 'unused' marker labels
|
||||
for i in range(1, len(data)):
|
||||
if (output[i] is None and output[i - 1] is not None and label_name[i] is None):
|
||||
script_start[i] = True
|
||||
gen_label(i, 'unused')
|
||||
|
||||
# Remove up to 15 bytes of padding at the end
|
||||
end_padding = 0
|
||||
for i in range(len(data)-1, -1, -1):
|
||||
if output[i] is not None:
|
||||
break
|
||||
end_padding += 1
|
||||
if end_padding > 15:
|
||||
end_padding = 0
|
||||
|
||||
if print_end_padding:
|
||||
print(end_padding)
|
||||
sys.exit(0)
|
||||
|
||||
print(".include \"seq_macros.inc\"")
|
||||
print(".section .rodata")
|
||||
print(".align 0")
|
||||
print("sequence_start:")
|
||||
print()
|
||||
for i in range(len(data) - end_padding):
|
||||
if script_start[i] and i > 0:
|
||||
print()
|
||||
if label_name[i] is not None:
|
||||
print(f"{label_name[i]}:")
|
||||
o = output[i]
|
||||
if o is None:
|
||||
print(f".byte {hex(data[i])}")
|
||||
elif o:
|
||||
print(o)
|
||||
elif label_name[i] is not None:
|
||||
print("<mid-instruction>")
|
||||
errors.append(f"mid-instruction label {label_name[i]}")
|
||||
|
||||
if hit_eof:
|
||||
errors.append("hit eof!?")
|
||||
|
||||
if errors:
|
||||
print(f"[{filename}] errors:", file=sys.stderr)
|
||||
for w in errors:
|
||||
print(w, file=sys.stderr)
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user