summaryrefslogtreecommitdiff
path: root/pokemontools/crystal.py
diff options
context:
space:
mode:
Diffstat (limited to 'pokemontools/crystal.py')
-rw-r--r--pokemontools/crystal.py172
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(