summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pokemontools/labels.py14
-rwxr-xr-xpokemontools/pcm.py152
-rwxr-xr-xpokemontools/redsfxdisasm.py333
-rwxr-xr-xpokemontools/redsfxheaders.py335
-rw-r--r--pokemontools/sym.py35
5 files changed, 852 insertions, 17 deletions
diff --git a/pokemontools/labels.py b/pokemontools/labels.py
index 72700a5..59a6160 100644
--- a/pokemontools/labels.py
+++ b/pokemontools/labels.py
@@ -15,7 +15,7 @@ class Labels(object):
Store all labels.
"""
- def __init__(self, config, filename="pokecrystal.map"):
+ def __init__(self, config, filename="pokecrystal.sym"):
"""
Setup the instance.
"""
@@ -27,18 +27,18 @@ class Labels(object):
"""
Handle anything requiring file-loading and such.
"""
- # Look for a mapfile if it's not given
+ # Look for a symfile if it's not given
if not os.path.exists(self.path):
- self.filename = find_mapfile_in_dir(self.config.path)
+ self.filename = find_symfile_in_dir(self.config.path)
if self.filename == None:
- raise Exception, "Couldn't find any mapfiles. Run rgblink -m to create a mapfile."
+ raise Exception, "Couldn't find any .sym files. Run rgblink -n to create a .sym file."
self.path = os.path.join(self.config.path, self.filename)
- self.labels = sym.read_mapfile(self.path)
+ self.labels = sym.read_symfile(self.path)
-def find_mapfile_in_dir(path):
+def find_symfile_in_dir(path):
for filename in os.listdir(path):
- if os.path.splitext(filename)[1] == '.map':
+ if os.path.splitext(filename)[1] == '.sym':
return filename
return None
diff --git a/pokemontools/pcm.py b/pokemontools/pcm.py
new file mode 100755
index 0000000..c765a41
--- /dev/null
+++ b/pokemontools/pcm.py
@@ -0,0 +1,152 @@
+# pcm.py
+# Converts between .wav files and 1-bit pcm data. (pcm = pulse-code modulation)
+
+import argparse
+import os
+import struct
+import wave
+
+
+BASE_SAMPLE_RATE = 22050
+
+def convert_to_wav(filenames=[]):
+ """
+ Converts a file containing 1-bit pcm data into a .wav file.
+ """
+ for filename in filenames:
+ with open(filename, 'rb') as pcm_file:
+ # Generate array of on/off pcm values.
+ samples = []
+ byte = pcm_file.read(1)
+ while byte != "":
+ byte = struct.unpack('B', byte)[0]
+ for i in range(8):
+ bit_index = 7 - i
+ value = (byte >> bit_index) & 1
+ samples.append(value)
+ byte = pcm_file.read(1)
+
+ # Write a .wav file using the pcm data.
+ name, extension = os.path.splitext(filename)
+ wav_filename = name + '.wav'
+ wave_file = wave.open(wav_filename, 'w')
+ wave_file.setframerate(BASE_SAMPLE_RATE)
+ wave_file.setnchannels(1)
+ wave_file.setsampwidth(1)
+
+ for value in samples:
+ if value > 0:
+ value = 0xff
+
+ packed_value = struct.pack('B', value)
+ wave_file.writeframesraw(packed_value)
+
+ wave_file.close()
+
+
+def convert_to_pcm(filenames=[]):
+ """
+ Converts a .wav file into 1-bit pcm data.
+ Samples in the .wav file are simply clamped to on/off.
+
+ TODO: This currently only works correctly on .wav files with the following attributes:
+ 1. Sample Width = 1 or 2 bytes (Some wave files use 3 bytes per sample...)
+ 2. 1 Channel
+ It can be improved to account for these factors.
+ """
+ for filename in filenames:
+ samples, average_sample = get_wav_samples(filename)
+
+ # Generate a list of clamped samples
+ clamped_samples = []
+ for sample in samples:
+ # Clamp the raw sample to on/off
+ if sample < average_sample:
+ clamped_samples.append(0)
+ else:
+ clamped_samples.append(1)
+
+ # The pcm data must be a multiple of 8, so pad the clamped samples with 0.
+ while len(clamped_samples) % 8 != 0:
+ clamped_samples.append(0)
+
+ # Pack the 1-bit samples together.
+ packed_samples = bytearray()
+ for i in xrange(0, len(clamped_samples), 8):
+ # Read 8 pcm values to pack one byte.
+ packed_value = 0
+ for j in range(8):
+ packed_value <<= 1
+ packed_value += clamped_samples[i + j]
+ packed_samples.append(packed_value)
+
+ # Open the output .pcm file, and write all 1-bit samples.
+ name, extension = os.path.splitext(filename)
+ pcm_filename = name + '.pcm'
+ with open(pcm_filename, 'wb') as out_file:
+ out_file.write(packed_samples)
+
+
+def get_wav_samples(filename):
+ """
+ Reads the given .wav file and returns a list of its samples after re-sampling
+ to BASE_SAMPLE_RATE.
+ Also returns the average sample amplitude.
+ """
+ wav_file = wave.open(filename, 'r')
+ sample_width = wav_file.getsampwidth()
+ sample_count = wav_file.getnframes()
+ sample_rate = wav_file.getframerate()
+
+ samples = bytearray(wav_file.readframes(sample_count))
+
+ # Unpack the values based on the sample byte width.
+ unpacked_samples = []
+ for i in xrange(0, len(samples), sample_width):
+ if sample_width == 1:
+ fmt = 'B'
+ elif sample_width == 2:
+ fmt = 'h'
+ else:
+ # todo: support 3-byte sample width
+ raise Exception, "Unsupported sample width: " + str(sample_width)
+
+ value = struct.unpack(fmt, samples[i:i + sample_width])[0]
+ unpacked_samples.append(value)
+
+ # Approximate the BASE_SAMPLE_RATE.
+ # Also find the average amplitude of the samples.
+ resampled_samples = []
+ total_value = 0
+ interval = float(sample_rate) / BASE_SAMPLE_RATE
+ index = 0
+ while index < sample_count:
+ sample = unpacked_samples[int(index)]
+ total_value += sample
+
+ resampled_samples.append(sample)
+ index += interval
+
+ average_sample = float(total_value) / sample_count
+
+ return resampled_samples, average_sample
+
+
+def main():
+ ap = argparse.ArgumentParser()
+ ap.add_argument('mode')
+ ap.add_argument('filenames', nargs='*')
+ args = ap.parse_args()
+
+ method = {
+ 'wav': convert_to_wav,
+ 'pcm': convert_to_pcm,
+ }.get(args.mode, None)
+
+ if method == None:
+ raise Exception, "Unknown conversion method!"
+
+ method(args.filenames)
+
+if __name__ == "__main__":
+ main()
diff --git a/pokemontools/redsfxdisasm.py b/pokemontools/redsfxdisasm.py
index 327ee95..f1cfece 100755
--- a/pokemontools/redsfxdisasm.py
+++ b/pokemontools/redsfxdisasm.py
@@ -2,6 +2,326 @@ import configuration
config = configuration.Config()
rom = bytearray(open(config.rom_path, "r").read())
+sfx_names = [
+ "Snare1_2",
+ "Snare2_2",
+ "Snare3_2",
+ "Snare4_2",
+ "Snare5_2",
+ "Triangle1_2",
+ "Triangle2_2",
+ "Snare6_2",
+ "Snare7_2",
+ "Snare8_2",
+ "Snare9_2",
+ "Cymbal1_2",
+ "Cymbal2_2",
+ "Cymbal3_2",
+ "Muted_Snare1_2",
+ "Triangle3_2",
+ "Muted_Snare2_2",
+ "Muted_Snare3_2",
+ "Muted_Snare4_2",
+ "Cry00_2",
+ "Cry01_2",
+ "Cry02_2",
+ "Cry03_2",
+ "Cry04_2",
+ "Cry05_2",
+ "Cry06_2",
+ "Cry07_2",
+ "Cry08_2",
+ "Cry09_2",
+ "Cry0A_2",
+ "Cry0B_2",
+ "Cry0C_2",
+ "Cry0D_2",
+ "Cry0E_2",
+ "Cry0F_2",
+ "Cry10_2",
+ "Cry11_2",
+ "Cry12_2",
+ "Cry13_2",
+ "Cry14_2",
+ "Cry15_2",
+ "Cry16_2",
+ "Cry17_2",
+ "Cry18_2",
+ "Cry19_2",
+ "Cry1A_2",
+ "Cry1B_2",
+ "Cry1C_2",
+ "Cry1D_2",
+ "Cry1E_2",
+ "Cry1F_2",
+ "Cry20_2",
+ "Cry21_2",
+ "Cry22_2",
+ "Cry23_2",
+ "Cry24_2",
+ "Cry25_2",
+ "Level_Up",
+ "Get_Item2_2",
+ "Tink_2",
+ "Heal_HP_2",
+ "Heal_Ailment_2",
+ "Start_Menu_2",
+ "Press_AB_2",
+ "Ball_Toss",
+ "Ball_Poof",
+ "Faint_Thud",
+ "Run",
+ "Dex_Page_Added",
+ "Caught_Mon",
+ "Peck",
+ "Faint_Fall",
+ "Battle_09",
+ "Pound",
+ "Battle_0B",
+ "Battle_0C",
+ "Battle_0D",
+ "Battle_0E",
+ "Battle_0F",
+ "Damage",
+ "Not_Very_Effective",
+ "Battle_12",
+ "Battle_13",
+ "Battle_14",
+ "Vine_Whip",
+ "Battle_16",
+ "Battle_17",
+ "Battle_18",
+ "Battle_19",
+ "Super_Effective",
+ "Battle_1B",
+ "Battle_1C",
+ "Doubleslap",
+ "Battle_1E",
+ "Horn_Drill",
+ "Battle_20",
+ "Battle_21",
+ "Battle_22",
+ "Battle_23",
+ "Battle_24",
+ "Battle_25",
+ "Battle_26",
+ "Battle_27",
+ "Battle_28",
+ "Battle_29",
+ "Battle_2A",
+ "Battle_2B",
+ "Battle_2C",
+ "Psybeam",
+ "Battle_2E",
+ "Battle_2F",
+ "Psychic_M",
+ "Battle_31",
+ "Battle_32",
+ "Battle_33",
+ "Battle_34",
+ "Battle_35",
+ "Battle_36",
+ "Silph_Scope",
+ "Snare1_1",
+ "Snare2_1",
+ "Snare3_1",
+ "Snare4_1",
+ "Snare5_1",
+ "Triangle1_1",
+ "Triangle2_1",
+ "Snare6_1",
+ "Snare7_1",
+ "Snare8_1",
+ "Snare9_1",
+ "Cymbal1_1",
+ "Cymbal2_1",
+ "Cymbal3_1",
+ "Muted_Snare1_1",
+ "Triangle3_1",
+ "Muted_Snare2_1",
+ "Muted_Snare3_1",
+ "Muted_Snare4_1",
+ "Cry00_1",
+ "Cry01_1",
+ "Cry02_1",
+ "Cry03_1",
+ "Cry04_1",
+ "Cry05_1",
+ "Cry06_1",
+ "Cry07_1",
+ "Cry08_1",
+ "Cry09_1",
+ "Cry0A_1",
+ "Cry0B_1",
+ "Cry0C_1",
+ "Cry0D_1",
+ "Cry0E_1",
+ "Cry0F_1",
+ "Cry10_1",
+ "Cry11_1",
+ "Cry12_1",
+ "Cry13_1",
+ "Cry14_1",
+ "Cry15_1",
+ "Cry16_1",
+ "Cry17_1",
+ "Cry18_1",
+ "Cry19_1",
+ "Cry1A_1",
+ "Cry1B_1",
+ "Cry1C_1",
+ "Cry1D_1",
+ "Cry1E_1",
+ "Cry1F_1",
+ "Cry20_1",
+ "Cry21_1",
+ "Cry22_1",
+ "Cry23_1",
+ "Cry24_1",
+ "Cry25_1",
+ "Get_Item1_1",
+ "Get_Item2_1",
+ "Tink_1",
+ "Heal_HP_1",
+ "Heal_Ailment_1",
+ "Start_Menu_1",
+ "Press_AB_1",
+ "Pokedex_Rating_1",
+ "Get_Key_Item_1",
+ "Poisoned_1",
+ "Trade_Machine_1",
+ "Turn_On_PC_1",
+ "Turn_Off_PC_1",
+ "Enter_PC_1",
+ "Shrink_1",
+ "Switch_1",
+ "Healing_Machine_1",
+ "Teleport_Exit1_1",
+ "Teleport_Enter1_1",
+ "Teleport_Exit2_1",
+ "Ledge_1",
+ "Teleport_Enter2_1",
+ "Fly_1",
+ "Denied_1",
+ "Arrow_Tiles_1",
+ "Push_Boulder_1",
+ "SS_Anne_Horn_1",
+ "Withdraw_Deposit_1",
+ "Cut_1",
+ "Go_Inside_1",
+ "Swap_1",
+ "59_1",
+ "Purchase_1",
+ "Collision_1",
+ "Go_Outside_1",
+ "Save_1",
+ "Pokeflute",
+ "Safari_Zone_PA",
+ "Snare1_3",
+ "Snare2_3",
+ "Snare3_3",
+ "Snare4_3",
+ "Snare5_3",
+ "Triangle1_3",
+ "Triangle2_3",
+ "Snare6_3",
+ "Snare7_3",
+ "Snare8_3",
+ "Snare9_3",
+ "Cymbal1_3",
+ "Cymbal2_3",
+ "Cymbal3_3",
+ "Muted_Snare1_3",
+ "Triangle3_3",
+ "Muted_Snare2_3",
+ "Muted_Snare3_3",
+ "Muted_Snare4_3",
+ "Cry00_3",
+ "Cry01_3",
+ "Cry02_3",
+ "Cry03_3",
+ "Cry04_3",
+ "Cry05_3",
+ "Cry06_3",
+ "Cry07_3",
+ "Cry08_3",
+ "Cry09_3",
+ "Cry0A_3",
+ "Cry0B_3",
+ "Cry0C_3",
+ "Cry0D_3",
+ "Cry0E_3",
+ "Cry0F_3",
+ "Cry10_3",
+ "Cry11_3",
+ "Cry12_3",
+ "Cry13_3",
+ "Cry14_3",
+ "Cry15_3",
+ "Cry16_3",
+ "Cry17_3",
+ "Cry18_3",
+ "Cry19_3",
+ "Cry1A_3",
+ "Cry1B_3",
+ "Cry1C_3",
+ "Cry1D_3",
+ "Cry1E_3",
+ "Cry1F_3",
+ "Cry20_3",
+ "Cry21_3",
+ "Cry22_3",
+ "Cry23_3",
+ "Cry24_3",
+ "Cry25_3",
+ "Get_Item1_3",
+ "Get_Item2_3",
+ "Tink_3",
+ "Heal_HP_3",
+ "Heal_Ailment_3",
+ "Start_Menu_3",
+ "Press_AB_3",
+ "Pokedex_Rating_3",
+ "Get_Key_Item_3",
+ "Poisoned_3",
+ "Trade_Machine_3",
+ "Turn_On_PC_3",
+ "Turn_Off_PC_3",
+ "Enter_PC_3",
+ "Shrink_3",
+ "Switch_3",
+ "Healing_Machine_3",
+ "Teleport_Exit1_3",
+ "Teleport_Enter1_3",
+ "Teleport_Exit2_3",
+ "Ledge_3",
+ "Teleport_Enter2_3",
+ "Fly_3",
+ "Denied_3",
+ "Arrow_Tiles_3",
+ "Push_Boulder_3",
+ "SS_Anne_Horn_3",
+ "Withdraw_Deposit_3",
+ "Cut_3",
+ "Go_Inside_3",
+ "Swap_3",
+ "59_3",
+ "Purchase_3",
+ "Collision_3",
+ "Go_Outside_3",
+ "Save_3",
+ "Intro_Lunge",
+ "Intro_Hip",
+ "Intro_Hop",
+ "Intro_Raise",
+ "Intro_Crash",
+ "Intro_Whoosh",
+ "Slots_Stop_Wheel",
+ "Slots_Reward",
+ "Slots_New_Spin",
+ "Shooting_Star",
+ ]
+
banks = {
0x02: 0x60,
0x08: 0x78,
@@ -44,11 +364,15 @@ music_notes = {
0xb: "B_",
}
+sfxnum = 0
+
for bank in banks:
+ print bank
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", 'w')
+ sfxname = sfx_names[sfxnum]
+ sfxfile = open("music/sfx/" + sfx_names[sfxnum].lower() + ".asm", 'w')
+ sfxname = "SFX_" + sfxname
startingaddress = rom[header + 2] * 0x100 + rom[header + 1] + (0x4000 * (bank - 1))
end = 0
curchannel = 1
@@ -121,9 +445,10 @@ for bank in banks:
header += 3
channelnumber = rom[header]
if curchannel == lastchannel:
- output += "; {}".format(hex(address))
+ # output += "; {}".format(hex(address))
sfxfile.write(output)
break
output += "\n\n"
startingaddress = address
- curchannel += 1 \ No newline at end of file
+ curchannel += 1
+ sfxnum += 1 \ No newline at end of file
diff --git a/pokemontools/redsfxheaders.py b/pokemontools/redsfxheaders.py
index c854c20..223d82f 100755
--- a/pokemontools/redsfxheaders.py
+++ b/pokemontools/redsfxheaders.py
@@ -2,35 +2,358 @@ import configuration
config = configuration.Config()
rom = bytearray(open(config.rom_path, "r").read())
+sfx_names = [
+ "Snare1_1",
+ "Snare2_1",
+ "Snare3_1",
+ "Snare4_1",
+ "Snare5_1",
+ "Triangle1_1",
+ "Triangle2_1",
+ "Snare6_1",
+ "Snare7_1",
+ "Snare8_1",
+ "Snare9_1",
+ "Cymbal1_1",
+ "Cymbal2_1",
+ "Cymbal3_1",
+ "Muted_Snare1_1",
+ "Triangle3_1",
+ "Muted_Snare2_1",
+ "Muted_Snare3_1",
+ "Muted_Snare4_1",
+ "Cry00_1",
+ "Cry01_1",
+ "Cry02_1",
+ "Cry03_1",
+ "Cry04_1",
+ "Cry05_1",
+ "Cry06_1",
+ "Cry07_1",
+ "Cry08_1",
+ "Cry09_1",
+ "Cry0A_1",
+ "Cry0B_1",
+ "Cry0C_1",
+ "Cry0D_1",
+ "Cry0E_1",
+ "Cry0F_1",
+ "Cry10_1",
+ "Cry11_1",
+ "Cry12_1",
+ "Cry13_1",
+ "Cry14_1",
+ "Cry15_1",
+ "Cry16_1",
+ "Cry17_1",
+ "Cry18_1",
+ "Cry19_1",
+ "Cry1A_1",
+ "Cry1B_1",
+ "Cry1C_1",
+ "Cry1D_1",
+ "Cry1E_1",
+ "Cry1F_1",
+ "Cry20_1",
+ "Cry21_1",
+ "Cry22_1",
+ "Cry23_1",
+ "Cry24_1",
+ "Cry25_1",
+ "Get_Item1_1",
+ "Get_Item2_1",
+ "Tink_1",
+ "Heal_HP_1",
+ "Heal_Ailment_1",
+ "Start_Menu_1",
+ "Press_AB_1",
+ "Pokedex_Rating_1",
+ "Get_Key_Item_1",
+ "Poisoned_1",
+ "Trade_Machine_1",
+ "Turn_On_PC_1",
+ "Turn_Off_PC_1",
+ "Enter_PC_1",
+ "Shrink_1",
+ "Switch_1",
+ "Healing_Machine_1",
+ "Teleport_Exit1_1",
+ "Teleport_Enter1_1",
+ "Teleport_Exit2_1",
+ "Ledge_1",
+ "Teleport_Enter2_1",
+ "Fly_1",
+ "Denied_1",
+ "Arrow_Tiles_1",
+ "Push_Boulder_1",
+ "SS_Anne_Horn_1",
+ "Withdraw_Deposit_1",
+ "Cut_1",
+ "Go_Inside_1",
+ "Swap_1",
+ "59_1",
+ "Purchase_1",
+ "Collision_1",
+ "Go_Outside_1",
+ "Save_1",
+ "Pokeflute",
+ "Safari_Zone_PA",
+ "Snare1_2",
+ "Snare2_2",
+ "Snare3_2",
+ "Snare4_2",
+ "Snare5_2",
+ "Triangle1_2",
+ "Triangle2_2",
+ "Snare6_2",
+ "Snare7_2",
+ "Snare8_2",
+ "Snare9_2",
+ "Cymbal1_2",
+ "Cymbal2_2",
+ "Cymbal3_2",
+ "Muted_Snare1_2",
+ "Triangle3_2",
+ "Muted_Snare2_2",
+ "Muted_Snare3_2",
+ "Muted_Snare4_2",
+ "Cry00_2",
+ "Cry01_2",
+ "Cry02_2",
+ "Cry03_2",
+ "Cry04_2",
+ "Cry05_2",
+ "Cry06_2",
+ "Cry07_2",
+ "Cry08_2",
+ "Cry09_2",
+ "Cry0A_2",
+ "Cry0B_2",
+ "Cry0C_2",
+ "Cry0D_2",
+ "Cry0E_2",
+ "Cry0F_2",
+ "Cry10_2",
+ "Cry11_2",
+ "Cry12_2",
+ "Cry13_2",
+ "Cry14_2",
+ "Cry15_2",
+ "Cry16_2",
+ "Cry17_2",
+ "Cry18_2",
+ "Cry19_2",
+ "Cry1A_2",
+ "Cry1B_2",
+ "Cry1C_2",
+ "Cry1D_2",
+ "Cry1E_2",
+ "Cry1F_2",
+ "Cry20_2",
+ "Cry21_2",
+ "Cry22_2",
+ "Cry23_2",
+ "Cry24_2",
+ "Cry25_2",
+ "Level_Up",
+ "Get_Item2_2",
+ "Tink_2",
+ "Heal_HP_2",
+ "Heal_Ailment_2",
+ "Start_Menu_2",
+ "Press_AB_2",
+ "Ball_Toss",
+ "Ball_Poof",
+ "Faint_Thud",
+ "Run",
+ "Dex_Page_Added",
+ "Caught_Mon",
+ "Peck",
+ "Faint_Fall",
+ "Battle_09",
+ "Pound",
+ "Battle_0B",
+ "Battle_0C",
+ "Battle_0D",
+ "Battle_0E",
+ "Battle_0F",
+ "Damage",
+ "Not_Very_Effective",
+ "Battle_12",
+ "Battle_13",
+ "Battle_14",
+ "Vine_Whip",
+ "Battle_16",
+ "Battle_17",
+ "Battle_18",
+ "Battle_19",
+ "Super_Effective",
+ "Battle_1B",
+ "Battle_1C",
+ "Doubleslap",
+ "Battle_1E",
+ "Horn_Drill",
+ "Battle_20",
+ "Battle_21",
+ "Battle_22",
+ "Battle_23",
+ "Battle_24",
+ "Battle_25",
+ "Battle_26",
+ "Battle_27",
+ "Battle_28",
+ "Battle_29",
+ "Battle_2A",
+ "Battle_2B",
+ "Battle_2C",
+ "Psybeam",
+ "Battle_2E",
+ "Battle_2F",
+ "Psychic_M",
+ "Battle_31",
+ "Battle_32",
+ "Battle_33",
+ "Battle_34",
+ "Battle_35",
+ "Battle_36",
+ "Silph_Scope",
+ "Snare1_3",
+ "Snare2_3",
+ "Snare3_3",
+ "Snare4_3",
+ "Snare5_3",
+ "Triangle1_3",
+ "Triangle2_3",
+ "Snare6_3",
+ "Snare7_3",
+ "Snare8_3",
+ "Snare9_3",
+ "Cymbal1_3",
+ "Cymbal2_3",
+ "Cymbal3_3",
+ "Muted_Snare1_3",
+ "Triangle3_3",
+ "Muted_Snare2_3",
+ "Muted_Snare3_3",
+ "Muted_Snare4_3",
+ "Cry00_3",
+ "Cry01_3",
+ "Cry02_3",
+ "Cry03_3",
+ "Cry04_3",
+ "Cry05_3",
+ "Cry06_3",
+ "Cry07_3",
+ "Cry08_3",
+ "Cry09_3",
+ "Cry0A_3",
+ "Cry0B_3",
+ "Cry0C_3",
+ "Cry0D_3",
+ "Cry0E_3",
+ "Cry0F_3",
+ "Cry10_3",
+ "Cry11_3",
+ "Cry12_3",
+ "Cry13_3",
+ "Cry14_3",
+ "Cry15_3",
+ "Cry16_3",
+ "Cry17_3",
+ "Cry18_3",
+ "Cry19_3",
+ "Cry1A_3",
+ "Cry1B_3",
+ "Cry1C_3",
+ "Cry1D_3",
+ "Cry1E_3",
+ "Cry1F_3",
+ "Cry20_3",
+ "Cry21_3",
+ "Cry22_3",
+ "Cry23_3",
+ "Cry24_3",
+ "Cry25_3",
+ "Get_Item1_3",
+ "Get_Item2_3",
+ "Tink_3",
+ "Heal_HP_3",
+ "Heal_Ailment_3",
+ "Start_Menu_3",
+ "Press_AB_3",
+ "Pokedex_Rating_3",
+ "Get_Key_Item_3",
+ "Poisoned_3",
+ "Trade_Machine_3",
+ "Turn_On_PC_3",
+ "Turn_Off_PC_3",
+ "Enter_PC_3",
+ "Shrink_3",
+ "Switch_3",
+ "Healing_Machine_3",
+ "Teleport_Exit1_3",
+ "Teleport_Enter1_3",
+ "Teleport_Exit2_3",
+ "Ledge_3",
+ "Teleport_Enter2_3",
+ "Fly_3",
+ "Denied_3",
+ "Arrow_Tiles_3",
+ "Push_Boulder_3",
+ "SS_Anne_Horn_3",
+ "Withdraw_Deposit_3",
+ "Cut_3",
+ "Go_Inside_3",
+ "Swap_3",
+ "59_3",
+ "Purchase_3",
+ "Collision_3",
+ "Go_Outside_3",
+ "Save_3",
+ "Intro_Lunge",
+ "Intro_Hip",
+ "Intro_Hop",
+ "Intro_Raise",
+ "Intro_Crash",
+ "Intro_Whoosh",
+ "Slots_Stop_Wheel",
+ "Slots_Reward",
+ "Slots_New_Spin",
+ "Shooting_Star",
+ ]
+
headerlist = (
["sfxheaders02.asm", 0x8003, 0x822e],
["sfxheaders08.asm", 0x20003, 0x202be],
["sfxheaders1f.asm", 0x7c003, 0x7c249],
)
-def printsfxheaders(filename, address, end):
+def printsfxheaders(filename, address, end, sfxnum):
file = open(filename, 'w')
bank = address / 0x4000
byte = rom[address]
sfx = 1
channel = 1
- file.write("SFX_Headers_{:02x}:\n".format(bank))
+ file.write("SFX_Headers_{:02x}::\n".format(bank))
file.write("\tdb $ff, $ff, $ff ; padding\n")
while address != end:
left = (byte >> 6) + 1
- file.write("\nSFX_{:02x}_{:02x}: ; {:02x} ({:0x}:{:02x})\n".format(bank, sfx, address, bank, address % 0x4000 + 0x4000))
+ file.write("\nSFX_{}:: ; {:02x} ({:0x}:{:02x})\n".format(sfx_names[sfxnum], 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))
+ file.write("\tdw SFX_{}_Ch{}\n".format(sfx_names[sfxnum], channel))
address += 3
byte = rom[address]
channel += 1
left -= 1
channel = 1
sfx += 1
- file.write("\n; {}".format(hex(address)))
+ sfxnum += 1
+ #file.write("\n; {}".format(hex(address)))
+ return sfxnum
+sfxnum = 0
for header in headerlist:
- printsfxheaders(header[0], header[1], header[2]) \ No newline at end of file
+ sfxnum = printsfxheaders(header[0], header[1], header[2], sfxnum) \ No newline at end of file
diff --git a/pokemontools/sym.py b/pokemontools/sym.py
index b1e755f..3c9914d 100644
--- a/pokemontools/sym.py
+++ b/pokemontools/sym.py
@@ -1,6 +1,7 @@
# coding: utf-8
import os
+import re
import sys
import json
@@ -81,6 +82,40 @@ def make_sym_from_mapfile(filename = '../pokecrystal.sym', mapfile = '../mapfile
with open(filename, 'w') as sym:
sym.write(output)
+def read_symfile(filename='pokecrystal.sym'):
+ """
+ Scrape label addresses from an rgbds .sym file.
+ """
+ labels = []
+
+ with open(filename, 'r') as symfile:
+ lines = symfile.readlines()
+
+ # Example line from sym file: "06:5531 Func_19531"
+ label_regex = re.compile('([0-9A-Fa-f]+):([0-9A-Fa-f]+) (\S+)')
+
+ for line in lines:
+ match = label_regex.match(line)
+ if match:
+ bank = int(match.group(1), 16)
+ local_address = int(match.group(2), 16)
+ label = match.group(3)
+ absolute_address = local_address
+
+ if local_address < 0x8000 and bank > 0:
+ absolute_address += (bank - 1) * 0x4000
+
+ labels += [{
+ 'label': label,
+ 'bank': bank,
+ 'address': absolute_address,
+ 'offset': absolute_address,
+ 'local_address': local_address,
+ }]
+
+ return labels
+
+
if __name__ == "__main__":
#if os.path.exists('../pokecrystal.sym'):
# sys.exit()