diff options
Diffstat (limited to 'pokemontools/crystal.py')
-rw-r--r-- | pokemontools/crystal.py | 172 |
1 files changed, 126 insertions, 46 deletions
diff --git a/pokemontools/crystal.py b/pokemontools/crystal.py index 5d602c9..cdab01f 100644 --- a/pokemontools/crystal.py +++ b/pokemontools/crystal.py @@ -174,7 +174,7 @@ def how_many_until(byte, starting, rom): def load_map_group_offsets(map_group_pointer_table, map_group_count, rom=None): """reads the map group table for the list of pointers""" map_group_offsets = [] # otherwise this method can only be used once - data = rom_interval(map_group_pointer_table, map_group_count*2, strings=False, rom=rom) + data = rom.interval(map_group_pointer_table, map_group_count*2, strings=False, rom=rom) data = helpers.grouper(data) for pointer_parts in data: pointer = pointer_parts[0] + (pointer_parts[1] << 8) @@ -548,7 +548,7 @@ def parse_text_from_bytes(bytes, debug=True, japanese=False): def parse_text_at(address, count=10, debug=True): """returns a list of bytes from an address see parse_text_at2 for pretty printing""" - return parse_text_from_bytes(rom_interval(address, count, strings=False), debug=debug) + return parse_text_from_bytes(rom.interval(address, count, strings=False), debug=debug) def parse_text_at2(address, count=10, debug=True, japanese=False): """returns a string of text from an address @@ -569,7 +569,7 @@ def parse_text_at3(address, map_group=None, map_id=None, debug=False): def rom_text_at(address, count=10): """prints out raw text from the ROM like for 0x112110""" - return "".join([chr(x) for x in rom_interval(address, count, strings=False)]) + return "".join([chr(x) for x in rom.interval(address, count, strings=False)]) def get_map_constant_label(map_group=None, map_id=None, map_internal_ids=None): """returns PALLET_TOWN for some map group/id pair""" @@ -764,6 +764,10 @@ class SingleByteParam(): else: return str(self.byte) + @staticmethod + def from_asm(value): + return value + class DollarSignByte(SingleByteParam): def to_asm(self): return hex(self.byte).replace("0x", "$") @@ -803,7 +807,7 @@ class MultiByteParam(): self.parse() def parse(self): - self.bytes = rom_interval(self.address, self.size, strings=False) + self.bytes = rom.interval(self.address, self.size, strings=False) self.parsed_number = self.bytes[0] + (self.bytes[1] << 8) if hasattr(self, "bank"): self.parsed_address = calculate_pointer_from_bytes_at(self.address, bank=self.bank) @@ -822,6 +826,10 @@ class MultiByteParam(): decimal = int("0x"+"".join([("%.2x")%x for x in reversed(self.bytes)]), 16) return str(decimal) + @staticmethod + def from_asm(value): + return value + class PointerLabelParam(MultiByteParam): # default size is 2 bytes @@ -1000,6 +1008,7 @@ class RAMAddressParam(MultiByteParam): class MoneyByteParam(MultiByteParam): size = 3 + byte_type = "db" max_value = 0x0F423F should_be_decimal = True def parse(self): @@ -1236,6 +1245,7 @@ class Command: # use this when the "byte id" doesn't matter # .. for example, a non-script command doesn't use the "byte id" override_byte_check = False + is_rgbasm_macro = False base_label = "UnseenLabel_" def __init__(self, address=None, *pargs, **kwargs): @@ -1723,6 +1733,7 @@ class TextCommand(Command): #def get_dependencies(self, recompute=False, global_dependencies=set()): # return [] + # this is a regular command in a TextScript for writing text # but unlike other macros that preprocessor.py handles, # the preprocessor-parser is custom and MainText is not @@ -1769,7 +1780,7 @@ class MainText(TextCommand): # read the text bytes into a structure # skip the first offset byte because that's the command byte - self.bytes = rom_interval(offset, jump, strings=False) + self.bytes = rom.interval(offset, jump, strings=False) # include the original command in the size calculation self.size = jump @@ -2372,41 +2383,70 @@ def create_command_classes(debug=False): command_classes = create_command_classes() +class BigEndianParam: + """big-endian word""" + size = 2 + should_be_decimal = False + byte_type = "bigdw" + + def __init__(self, *args, **kwargs): + self.prefix = '$' + for (key, value) in kwargs.items(): + setattr(self, key, value) + self.parse() + + def parse(self): + self.bytes = rom.interval(self.address, 2, strings=False) + self.parsed_number = self.bytes[0] * 0x100 + self.bytes[1] + + def to_asm(self): + if not self.should_be_decimal: + return self.prefix+"".join([("%.2x")%x for x in self.bytes]) + elif self.should_be_decimal: + decimal = int("0x"+"".join([("%.2x")%x for x in self.bytes]), 16) + return str(decimal) + + @staticmethod + def from_asm(value): + return value + +class DecimalBigEndianParam(BigEndianParam): + should_be_decimal = True -music_commands_new = { - 0xD0: ["octave8"], - 0xD1: ["octave7"], - 0xD2: ["octave6"], - 0xD3: ["octave5"], - 0xD4: ["octave4"], - 0xD5: ["octave3"], - 0xD6: ["octave2"], - 0xD7: ["octave1"], - 0xD8: ["notetype", ["note_length", SingleByteParam], ["intensity", SingleByteParam]], # only 1 param on ch3 +music_commands = { + 0xD0: ["octave 8"], + 0xD1: ["octave 7"], + 0xD2: ["octave 6"], + 0xD3: ["octave 5"], + 0xD4: ["octave 4"], + 0xD5: ["octave 3"], + 0xD6: ["octave 2"], + 0xD7: ["octave 1"], + 0xD8: ["notetype", ["note_length", SingleByteParam], ["intensity", SingleByteParam]], # no intensity on channel 4/8 0xD9: ["forceoctave", ["octave", SingleByteParam]], - 0xDA: ["tempo", ["tempo", MultiByteParam]], + 0xDA: ["tempo", ["tempo", DecimalBigEndianParam]], 0xDB: ["dutycycle", ["duty_cycle", SingleByteParam]], 0xDC: ["intensity", ["intensity", SingleByteParam]], 0xDD: ["soundinput", ["input", SingleByteParam]], - 0xDE: ["unknownmusic0xde", ["unknown", SingleByteParam]], # also updates duty cycle + 0xDE: ["unknownmusic0xde", ["unknown", SingleByteParam]], 0xDF: ["unknownmusic0xdf"], 0xE0: ["unknownmusic0xe0", ["unknown", SingleByteParam], ["unknown", SingleByteParam]], 0xE1: ["vibrato", ["delay", SingleByteParam], ["extent", SingleByteParam]], 0xE2: ["unknownmusic0xe2", ["unknown", SingleByteParam]], - 0xE3: ["togglenoise", ["id", SingleByteParam]], # this can have 0-1 params! + 0xE3: ["togglenoise", ["id", SingleByteParam]], # no parameters on toggle off 0xE4: ["panning", ["tracks", SingleByteParam]], 0xE5: ["volume", ["volume", SingleByteParam]], - 0xE6: ["tone", ["tone", MultiByteParam]], # big endian + 0xE6: ["tone", ["tone", BigEndianParam]], 0xE7: ["unknownmusic0xe7", ["unknown", SingleByteParam]], 0xE8: ["unknownmusic0xe8", ["unknown", SingleByteParam]], - 0xE9: ["globaltempo", ["value", MultiByteParam]], + 0xE9: ["globaltempo", ["value", DecimalBigEndianParam]], 0xEA: ["restartchannel", ["address", PointerLabelParam]], - 0xEB: ["newsong", ["id", MultiByteParam]], + 0xEB: ["newsong", ["id", DecimalBigEndianParam]], 0xEC: ["sfxpriorityon"], 0xED: ["sfxpriorityoff"], 0xEE: ["unknownmusic0xee", ["address", PointerLabelParam]], 0xEF: ["stereopanning", ["tracks", SingleByteParam]], - 0xF0: ["sfxtogglenoise", ["id", SingleByteParam]], # 0-1 params + 0xF0: ["sfxtogglenoise", ["id", SingleByteParam]], # no parameters on toggle off 0xF1: ["music0xf1"], # nothing 0xF2: ["music0xf2"], # nothing 0xF3: ["music0xf3"], # nothing @@ -2419,19 +2459,29 @@ music_commands_new = { 0xFA: ["setcondition", ["condition", SingleByteParam]], 0xFB: ["jumpif", ["condition", SingleByteParam], ["address", PointerLabelParam]], 0xFC: ["jumpchannel", ["address", PointerLabelParam]], - 0xFD: ["loopchannel", ["count", SingleByteParam], ["address", PointerLabelParam]], + 0xFD: ["loopchannel", ["count", DecimalParam], ["address", PointerLabelParam]], 0xFE: ["callchannel", ["address", PointerLabelParam]], 0xFF: ["endchannel"], } -music_command_enders = [0xEA, 0xEB, 0xEE, 0xFC, 0xFF,] -# special case for 0xFD (if loopchannel.count = 0, break) +music_command_enders = [ + "restartchannel", + "newsong", + "unknownmusic0xee", + "jumpchannel", + "endchannel", +] def create_music_command_classes(debug=False): klasses = [] - for (byte, cmd) in music_commands_new.items(): + for (byte, cmd) in music_commands.items(): cmd_name = cmd[0].replace(" ", "_") - params = {"id": byte, "size": 1, "end": byte in music_command_enders, "macro_name": cmd_name} + params = { + "id": byte, + "size": 1, + "end": cmd[0] in music_command_enders, + "macro_name": cmd[0] + } params["param_types"] = {} if len(cmd) > 1: param_types = cmd[1:] @@ -2451,24 +2501,54 @@ def create_music_command_classes(debug=False): klasses.append(klass) # later an individual klass will be instantiated to handle something return klasses + music_classes = create_music_command_classes() +class OctaveParam(DecimalParam): + @staticmethod + def from_asm(value): + value = int(value) + return hex(0xd8 - value).replace("0x", "$") + +class OctaveCommand(Command): + macro_name = "octave" + size = 0 + end = False + param_types = { + 0: {"name": "octave", "class": OctaveParam}, + } + allowed_lengths = [1] + override_byte_check = True + +class ChannelCommand(Command): + macro_name = "channel" + size = 3 + override_byte_check = True + param_types = { + 0: {"name": "id", "class": DecimalParam}, + 1: {"name": "address", "class": PointerLabelParam}, + } + + +# pokered + class callchannel(Command): - id = 0xFD - macro_name = "callchannel" - size = 3 - param_types = { - 0: {"name": "address", "class": PointerLabelParam}, - } + id = 0xFD + macro_name = "callchannel" + size = 3 + param_types = { + 0: {"name": "address", "class": PointerLabelParam}, + } class loopchannel(Command): - id = 0xFE - macro_name = "loopchannel" - size = 4 - param_types = { - 0: {"name": "count", "class": SingleByteParam}, - 1: {"name": "address", "class": PointerLabelParam}, - } + id = 0xFE + macro_name = "loopchannel" + size = 4 + param_types = { + 0: {"name": "count", "class": SingleByteParam}, + 1: {"name": "address", "class": PointerLabelParam}, + } + effect_commands = { 0x1: ['checkturn'], @@ -4272,7 +4352,7 @@ class Signpost(Command): address = self.address bank = self.bank self.last_address = self.address + self.size - bytes = rom_interval(self.address, self.size) #, signpost_byte_size) + bytes = rom.interval(self.address, self.size) #, signpost_byte_size) self.y = int(bytes[0], 16) self.x = int(bytes[1], 16) @@ -4526,7 +4606,7 @@ class SecondMapHeader: def parse(self): address = self.address - bytes = rom_interval(address, second_map_header_byte_size, strings=False) + bytes = rom.interval(address, second_map_header_byte_size, strings=False) size = second_map_header_byte_size # for later @@ -5339,7 +5419,7 @@ class MapBlockData: os.mkdir(self.maps_path) if not os.path.exists(map_path): # dump to file - #bytes = rom_interval(self.address, self.width.byte*self.height.byte, strings=True) + #bytes = rom.interval(self.address, self.width.byte*self.height.byte, strings=True) bytes = rom[self.address : self.address + self.width.byte*self.height.byte] file_handler = open(map_path, "w") file_handler.write(bytes) @@ -5407,7 +5487,7 @@ class MapEventHeader: # signposts signpost_count = ord(rom[after_triggers]) signpost_byte_count = signpost_byte_size * signpost_count - # signposts = rom_interval(after_triggers+1, signpost_byte_count) + # signposts = rom.interval(after_triggers+1, signpost_byte_count) signposts = parse_signposts(after_triggers+1, signpost_count, bank=bank, map_group=map_group, map_id=map_id, debug=debug) after_signposts = after_triggers + 1 + signpost_byte_count self.signpost_count = signpost_count @@ -5416,7 +5496,7 @@ class MapEventHeader: # people events people_event_count = ord(rom[after_signposts]) people_event_byte_count = people_event_byte_size * people_event_count - # people_events_bytes = rom_interval(after_signposts+1, people_event_byte_count) + # people_events_bytes = rom.interval(after_signposts+1, people_event_byte_count) # people_events = parse_people_event_bytes(people_events_bytes, address=after_signposts+1, map_group=map_group, map_id=map_id) people_events = parse_people_events(after_signposts+1, people_event_count, bank=pointers.calculate_bank(after_signposts+2), map_group=map_group, map_id=map_id, debug=debug) self.people_event_count = people_event_count @@ -5577,7 +5657,7 @@ class MapScriptHeader: self.trigger_count = ord(rom[address]) self.triggers = [] ptr_line_size = 4 - groups = helpers.grouper(rom_interval(address+1, self.trigger_count * ptr_line_size, strings=False), count=ptr_line_size) + groups = helpers.grouper(rom.interval(address+1, self.trigger_count * ptr_line_size, strings=False), count=ptr_line_size) current_address = address+1 for (index, trigger_bytes) in enumerate(groups): logging.debug( |