Add pokered music tools

This commit is contained in:
U-Fish-PC\Daniel 2013-10-13 10:50:09 -04:00
parent 1b56d95aa0
commit c57e0f0706
3 changed files with 498 additions and 0 deletions

306
pokemontools/redmusicdisasm.py Executable file
View File

@ -0,0 +1,306 @@
import config
config = config.Config()
rom = bytearray(open(config.rom_path, "r").read())
songs = [
"PalletTown",
"Pokecenter",
"Gym",
"Cities1",
"Cities2",
"Celadon",
"Cinnabar",
"Vermilion",
"Lavender",
"SSAnne",
"MeetProfOak",
"MeetRival",
"MuseumGuy",
"SafariZone",
"PkmnHealed",
"Routes1",
"Routes2",
"Routes3",
"Routes4",
"IndigoPlateau",
"GymLeaderBattle",
"TrainerBattle",
"WildBattle",
"FinalBattle",
"DefeatedTrainer",
"DefeatedWildMon",
"DefeatedGymLeader",
"TitleScreen",
"Credits",
"HallOfFame",
"OaksLab",
"JigglypuffSong",
"BikeRiding",
"Surfing",
"GameCorner",
"IntroBattle",
"Dungeon1",
"Dungeon2",
"Dungeon3",
"CinnabarMansion",
"PokemonTower",
"SilphCo",
"MeetEvilTrainer",
"MeetFemaleTrainer",
"MeetMaleTrainer",
"UnusedSong",
#"SurfingPikachu",
#"MeetJessieJames",
#"YellowUnusedSong",
]
music_commands = {
0xd0: ["notetype", {"type": "nibble"}, 2],
0xe0: ["octave", 1],
0xe8: ["unknownmusic0xe8", 1],
0xea: ["vibrato", {"type": "byte"}, {"type": "nibble"}, 3],
0xeb: ["pitchbend", {"type": "byte"}, {"type": "byte"}, 3],
0xec: ["duty", {"type": "byte"}, 2],
0xed: ["tempo", {"type": "byte"}, {"type": "byte"}, 3],
0xee: ["unknownmusic0xee", {"type": "byte"}, 2],
0xf0: ["stereopanning", {"type": "byte"}, 2],
0xf8: ["unknownmusic0xf8", 1],
0xfc: ["dutycycle", {"type": "byte"}, 2],
0xfd: ["callchannel", {"type": "label"}, 3],
0xfe: ["loopchannel", {"type": "byte"}, {"type": "label"}, 4],
0xff: ["endchannel", 1],
}
param_lengths = {
"nibble": 1,
"byte": 1,
"label": 2,
}
music_notes = {
0x0: "C_",
0x1: "C#",
0x2: "D_",
0x3: "D#",
0x4: "E_",
0x5: "F_",
0x6: "F#",
0x7: "G_",
0x8: "G#",
0x9: "A_",
0xa: "A#",
0xb: "B_",
}
def printnoisechannel(songname, songfile, startingaddress, bank, output):
noise_commands = {
0xfd: ["callchannel", {"type": "label"}, 3],
0xfe: ["loopchannel", {"type": "byte"}, {"type": "label"}, 4],
0xff: ["endchannel", 1],
}
noise_instruments = {
0x01: "snare1",
0x02: "snare2",
0x03: "snare3",
0x04: "snare4",
0x05: "snare5",
0x06: "triangle1",
0x07: "triangle2",
0x08: "snare6",
0x09: "snare7",
0x0a: "snare8",
0x0b: "snare9",
0x0c: "cymbal1",
0x0d: "cymbal2",
0x0e: "cymbal3",
0x0f: "mutedsnare1",
0x10: "triangle3",
0x11: "mutedsnare2",
0x12: "mutedsnare3",
0x13: "mutedsnare4",
}
# pass 1, build a list of all addresses pointed to by calls and loops
address = startingaddress
labels = []
labelsleft= []
while 1:
byte = rom[address]
if byte < 0xc0:
command_length = 2
elif byte < 0xe0:
command_length = 1
else:
command_length = noise_commands[byte][-1]
if byte == 0xfd or byte == 0xfe:
label = rom[address + command_length - 1] * 0x100 + rom[address + command_length - 2]
labels.append(label)
if label > address % 0x4000 + 0x4000: labelsleft.append(label)
address += command_length
if len(labelsleft) == 0 and (byte == 0xfe and rom[address - command_length + 1] == 0 and rom[address - 1] * 0x100 + rom[address - 2] < address % 0x4000 + 0x4000 or byte == 0xff): break
while address % 0x4000 + 0x4000 in labelsleft: labelsleft.remove(address % 0x4000 + 0x4000)
# once the loop ends, start over from first address
if rom[address] == 0xff: address += 1
end = address
address = startingaddress
byte = rom[address]
output += "Music_{}_Ch4: ; {:02x} ({:0x}:{:02x})\n".format(songname, address, bank, address % 0x4000 + 0x4000)
# pass 2, print commands and labels for addresses that are in labels
while address != end:
if address % 0x4000 + 0x4000 in labels and address != startingaddress:
output += "\nMusic_{}_branch_{:02x}:\n".format(songname, address)
if byte < 0xc0:
output += "\tdnote {}, {}".format(byte % 0x10 + 1, noise_instruments[rom[address + 1]])
command_length = 2
elif byte < 0xd0:
output += "\trest {}".format(byte % 0x10 + 1)
command_length = 1
elif byte < 0xe0:
output += "\tdspeed {}".format(byte % 0x10)
command_length = 1
else:
command = noise_commands[byte]
output += "\t{}".format(command[0])
command_length = 1
params = 1
# print all params for current command
while params != len(noise_commands[byte]) - 1:
param_type = noise_commands[byte][params]["type"]
address += command_length
command_length = param_lengths[param_type]
param = rom[address]
if param_type == "byte":
output += " {}".format(param)
else:
param += rom[address + 1] * 0x100 - 0x4000 + (bank * 0x4000)
if param == startingaddress: output += " Music_{}_Ch4".format(songname)
else: output += " Music_{}_branch_{:02x}".format(songname, param)
params += 1
if params != len(noise_commands[byte]) - 1: output += ","
output += "\n"
address += command_length
byte = rom[address]
output += "; {}".format(hex(address))
songfile.write(output)
for i, songname in enumerate(songs):
songfile = open("music/" + songname.lower() + ".asm", 'a')
if songname == "PalletTown": header = 0x822e
if songname == "GymLeaderBattle": header = 0x202be
if songname == "TitleScreen": header = 0x7c249
if songname == "SurfingPikachu": header = 0x801cb
bank = header / 0x4000
startingaddress = rom[header + 2] * 0x100 + rom[header + 1] - 0x4000 + (0x4000 * bank)
curchannel = 1
lastchannel = (rom[header] >> 6) + 1
exception = False
if songname == "MeetRival" or songname == "Cities1":
startingaddress -= 7
exception = True
if songname == "UnusedSong":
bank = 2
startingaddress = 0xa913
lastchannel = 2
output = ''
while 1:
# pass 1, build a list of all addresses pointed to by calls and loops
address = startingaddress
labels = []
labelsleft = []
if songname == "MeetRival":
if curchannel == 1:
labels.append(0x719b)
labelsleft.append(0x719b)
if curchannel == 2:
labels.append(0x721d)
labelsleft.append(0x721d)
if curchannel == 3:
labels.append(0x72b5)
labelsleft.append(0x72b5)
while 1:
byte = rom[address]
if byte < 0xd0:
command_length = 1
elif byte < 0xe0:
command_length = 2
elif byte < 0xe8:
command_length = 1
else:
command_length = music_commands[byte][-1]
if byte == 0xfd or byte == 0xfe:
label = rom[address + command_length - 1] * 0x100 + rom[address + command_length - 2]
labels.append(label)
if label > address % 0x4000 + 0x4000: labelsleft.append(label)
address += command_length
if len(labelsleft) == 0 and (exception == False or address > startingaddress + 7) and (byte == 0xfe and rom[address - command_length + 1] == 0 and rom[address - 1] * 0x100 + rom[address - 2] < address % 0x4000 + 0x4000 or byte == 0xff): break
while address % 0x4000 + 0x4000 in labelsleft: labelsleft.remove(address % 0x4000 + 0x4000)
# once the loop breaks, start over from first address
if rom[address] == 0xff: address += 1
end = address
if curchannel != lastchannel and songname != "UnusedSong": end = rom[header + 5] * 0x100 + rom[header + 4] + (0x4000 * (bank - 1))
address = startingaddress
byte = rom[address]
# if song has an alternate start to channel 1, print a label and set startingaddress to true channel start
if exception:
output += "Music_{}_branch_{:02x}:\n".format(songname, address)
startingaddress += 7
# pass 2, print commands and labels for addresses that are in labels
while address != end:
if address == startingaddress:
if exception: output += "\n"
output += "Music_{}_Ch{}: ; {:02x} ({:0x}:{:02x})\n".format(songname, curchannel, address, bank, address % 0x4000 + 0x4000)
elif address % 0x4000 + 0x4000 in labels:
output += "\nMusic_{}_branch_{:02x}:\n".format(songname, address)
if byte < 0xc0:
output += "\tnote {}, {}".format(music_notes[byte >> 4], byte % 0x10 + 1)
command_length = 1
elif byte < 0xd0:
output += "\trest {}".format(byte % 0x10 + 1)
command_length = 1
else:
if byte < 0xe0:
command = music_commands[0xd0]
output += "\t{} {},".format(command[0], byte % 0x10)
byte = 0xd0
elif byte < 0xe8:
command = music_commands[0xe0]
output += "\t{} {}".format(command[0], 0xe8 - byte)
byte = 0xe0
else:
command = music_commands[byte]
output += "\t{}".format(command[0])
command_length = 1
params = 1
# print all params for current command
while params != len(music_commands[byte]) - 1:
param_type = music_commands[byte][params]["type"]
address += command_length
command_length = param_lengths[param_type]
param = rom[address]
if param_type == "nibble":
output += " {}, {}".format(param >> 4, param % 0x10)
elif param_type == "byte":
output += " {}".format(param)
else:
param += rom[address + 1] * 0x100 - 0x4000 + (bank * 0x4000)
if param == startingaddress: output += " Music_{}_Ch{}".format(songname, curchannel)
else: output += " Music_{}_branch_{:02x}".format(songname, param)
params += 1
if params != len(music_commands[byte]) - 1: output += ","
output += "\n"
address += command_length
byte = rom[address]
header += 3
if curchannel == lastchannel:
output += "; {}".format(hex(address))
songfile.write(output)
break
curchannel += 1
output += "\n\n"
startingaddress = end
exception = False
if curchannel == 4:
printnoisechannel(songname, songfile, startingaddress, bank, output)
header += 3
break

149
pokemontools/redsfxdisasm.py Executable file
View File

@ -0,0 +1,149 @@
import config
config = config.Config()
rom = bytearray(open(config.rom_path, "r").read())
banks = {
0x02: 0x60,
0x08: 0x78,
0x1f: 0x68,
}
music_commands = {
0xd0: ["notetype", {"type": "nibble"}, 2],
0xe0: ["octave", 1],
0xe8: ["unknownmusic0xe8", 1],
0xe9: ["unknownmusic0xe9", 1],
0xea: ["vibrato", {"type": "byte"}, {"type": "nibble"}, 3],
0xeb: ["pitchbend", {"type": "byte"}, {"type": "byte"}, 3],
0xec: ["duty", {"type": "byte"}, 2],
0xed: ["tempo", {"type": "byte"}, {"type": "byte"}, 3],
0xee: ["unknownmusic0xee", {"type": "byte"}, 2],
0xef: ["unknownmusic0xef", 1],
0xf0: ["stereopanning", {"type": "byte"}, 2],
0xf1: ["unknownmusic0xf1", 1],
0xf2: ["unknownmusic0xf2", 1],
0xf3: ["unknownmusic0xf3", 1],
0xf4: ["unknownmusic0xf4", 1],
0xf5: ["unknownmusic0xf5", 1],
0xf6: ["unknownmusic0xf6", 1],
0xf7: ["unknownmusic0xf7", 1],
0xf8: ["unknownmusic0xf8", 1],
0xf9: ["unknownmusic0xf9", 1],
0xfa: ["unknownmusic0xfa", 1],
#0xfb: ["unknownmusic0xfb", 1],
0xfc: ["dutycycle", {"type": "byte"}, 2],
0xfd: ["callchannel", {"type": "label"}, 3],
0xfe: ["loopchannel", {"type": "byte"}, {"type": "label"}, 4],
0xff: ["endchannel", 1],
}
param_lengths = {
"nibble": 1,
"byte": 1,
"label": 2,
}
music_notes = {
0x0: "C_",
0x1: "C#",
0x2: "D_",
0x3: "D#",
0x4: "E_",
0x5: "F_",
0x6: "F#",
0x7: "G_",
0x8: "G#",
0x9: "A_",
0xa: "A#",
0xb: "B_",
}
for bank in banks:
header = bank * 0x4000 + 3
for sfx in range(1,banks[bank]):
sfxname = "SFX_{:02x}_{:02x}".format(bank, sfx)
sfxfile = open("music/sfx/" + sfxname.lower() + ".asm", 'a')
startingaddress = rom[header + 2] * 0x100 + rom[header + 1] + (0x4000 * (bank - 1))
curchannel = 1
lastchannel = (rom[header] >> 6) + 1
output = ''
while 1:
# pass 1, build a list of all addresses pointed to by calls and loops
address = startingaddress
labels = []
labelsleft = []
while 1:
byte = rom[address]
if byte < 0xd0:
command_length = 1
elif byte < 0xe0:
command_length = 2
elif byte < 0xe8:
command_length = 1
else:
command_length = music_commands[byte][-1]
if byte == 0xfd or byte == 0xfe:
label = rom[address + command_length - 1] * 0x100 + rom[address + command_length - 2]
labels.append(label)
if label > address % 0x4000 + 0x4000: labelsleft.append(label)
address += command_length
if len(labelsleft) == 0 and (byte == 0xfe and rom[address - command_length + 1] == 0 and rom[address - 1] * 0x100 + rom[address - 2] < address % 0x4000 + 0x4000 or byte == 0xff): break
while address % 0x4000 + 0x4000 in labelsleft: labelsleft.remove(address % 0x4000 + 0x4000)
# once the loop breaks, start over from first address
end = address
if curchannel != lastchannel: end = rom[header + 5] * 0x100 + rom[header + 4] + (0x4000 * (bank - 1))
address = startingaddress
byte = rom[address]
# pass 2, print commands and labels for addresses that are in labels
while address != end:
if address == startingaddress:
output += "{}_Ch{}: ; {:02x} ({:0x}:{:02x})\n".format(sfxname, curchannel, address, bank, address % 0x4000 + 0x4000)
elif address % 0x4000 + 0x4000 in labels:
output += "\n{}_branch_{:02x}:\n".format(sfxname, address)
if byte < 0xc0:
output += "\tnote {}, {}".format(music_notes[byte >> 4], byte % 0x10 + 1)
command_length = 1
elif byte < 0xd0:
output += "\trest {}".format(byte % 0x10 + 1)
command_length = 1
else:
if byte < 0xe0:
command = music_commands[0xd0]
output += "\t{} {},".format(command[0], byte % 0x10)
byte = 0xd0
elif byte < 0xe8:
command = music_commands[0xe0]
output += "\t{} {}".format(command[0], 0xe8 - byte)
byte = 0xe0
else:
command = music_commands[byte]
output += "\t{}".format(command[0])
command_length = 1
params = 1
# print all params for current command
while params != len(music_commands[byte]) - 1:
param_type = music_commands[byte][params]["type"]
address += command_length
command_length = param_lengths[param_type]
param = rom[address]
if param_type == "nibble":
output += " {}, {}".format(param >> 4, param % 0x10)
elif param_type == "byte":
output += " {}".format(param)
else:
param += rom[address + 1] * 0x100 - 0x4000 + (bank * 0x4000)
if param == startingaddress: output += " {}_Ch{}".format(sfxname, curchannel)
else: output += " {}_branch_{:02x}".format(sfxname, param)
params += 1
if params != len(music_commands[byte]) - 1: output += ","
output += "\n"
address += command_length
byte = rom[address]
header += 3
if curchannel == lastchannel:
output += "; {}".format(hex(address))
sfxfile.write(output)
break
output += "\n\n"
startingaddress = end
curchannel += 1

43
pokemontools/redsfxheaders.py Executable file
View File

@ -0,0 +1,43 @@
import config
config = config.Config()
rom = bytearray(open(config.rom_path, "r").read())
headerlist = (
["sfxheaders02.asm", 0x8003, 0x822e],
["sfxheaders08.asm", 0x20003, 0x202be],
["sfxheaders1f.asm", 0x7c003, 0x7c249],
)
numberofchannels = {
0x0: 1,
0x4: 2,
0x8: 3,
0xC: 4,
}
def printsfxheaders(filename, address, end):
file = open(filename, 'w')
bank = address / 0x4000
byte = rom[address]
sfx = 1
channel = 1
file.write("SFX_Headers_{:02x}:\n".format(bank))
file.write("\tdb $ff, $ff, $ff ; padding\n")
while address != end:
left = numberofchannels[byte >> 4]
file.write("\nSFX_{:02x}_{:02x}: ; {:02x} ({:0x}:{:02x})\n".format(bank, sfx, address, bank, address % 0x4000 + 0x4000))
while left != 0:
pointer = rom[address + 2] * 0x100 + rom[address + 1]
if byte >> 4 != 0: file.write(" db ( ${:0x}0 | CH{:0x} )\n".format(byte >> 4, byte % 0x10))
else: file.write("\tdb CH{:0x}\n".format(byte))
file.write("\tdw SFX_{:02x}_{:02x}_Ch{}\n".format(bank, sfx, channel))
address += 3
byte = rom[address]
channel += 1
left -= 1
channel = 1
sfx += 1
file.write("\n; {}".format(hex(address)))
for header in headerlist:
printsfxheaders(header[0], header[1], header[2])