def __init__(self, stream: InputStream): self.contents = stream.get_u8() self.shop_graphic = (self.contents >> 4) & 0x0f self.shop_data_length = self.contents & 0x0f self.unused = [] for unused in range(3): self.unused.append(stream.get_u8()) self.pointer = stream.get_u32()
def __init__(self, stream: InputStream = None): if stream is None: self.enemy_id = 0 self.min_count = 0 self.max_count = 0 self.unused = 0 else: self.enemy_id = stream.get_u8() self.min_count = stream.get_u8() self.max_count = stream.get_u8() self.unused = stream.get_u8()
def __init__(self, stream: InputStream = None): if stream is None: self.identifier = 0 self.event = 0 self.x_pos = 0 self.y_pos = 0 else: self.identifier = stream.get_u16() self.event = stream.get_u16() self.x_pos = stream.get_u16() self.y_pos = stream.get_u16()
def __init__(self, stream: InputStream = None): if stream is None: self.config = 0 self.unrunnable = 0 self.surprise_chance = 0 self.groups = [] else: self.config = stream.get_u8() self.unrunnable = stream.get_u8() self.surprise_chance = stream.get_u16() self.groups = [] for index in range(4): self.groups.append(EncounterGroup(stream))
def __init__(self, stream: InputStream = None): if stream is None: self.identifier = 0 self.low_x = 0 self.low_y = 0 self.high_x = 0 self.high_y = 0 else: self.identifier = stream.get_u16() self.low_x = stream.get_u16() self.low_y = stream.get_u16() self.high_x = stream.get_u16() self.high_y = stream.get_u16()
def build(self): stream = OutputStream() written_length = 0 if len(self._magic) > 0: for magic_id in self._magic: stream.put_u8(magic_id) written_length += 1 if len(self._armor) > 0: stream.put_u8(0xfc) for item_id in self._armor: stream.put_u8(item_id) written_length += 1 if len(self._weapons) > 0: stream.put_u8(0xfd) for item_id in self._weapons: stream.put_u8(item_id) written_length += 1 if len(self._items) > 0: stream.put_u8(0xfe) for item_id in self._items: stream.put_u8(item_id) written_length += 1 if written_length > 0xf: raise RuntimeError( f"Error: Too many items in shop: {written_length}") return ShopInventory(InputStream(stream.get_buffer()), written_length)
def _as_ascii(stream: InputStream, symbolic_names: bool = False): working = "" while not stream.is_eos(): char_code = stream.get_u8() if char_code > 0x80: char_code = (char_code << 8) | stream.get_u8() elif char_code == 0x25: char_code = (char_code << 8) | stream.get_u8() next_char = stream.get_u8() if char_code == 0x2532 and next_char == 0x64: char_code = 0x253264 else: stream.unget_u8() elif char_code in TextBlock.TEXT_TABLE: char_code = char_code else: print(f"Unknown code encountered in string: {hex(char_code)}") to_append = TextBlock.TEXT_TABLE[ char_code] if char_code in TextBlock.TEXT_TABLE else None if not symbolic_names: if to_append is None or (len(to_append) > 1 and not to_append.startswith("\\")): working += TextBlock._escape(char_code) else: working += to_append else: if to_append is not None: working += to_append else: working += TextBlock._escape(char_code) return working
def __init__(self, map_id: int, stream: InputStream): self.header = None self.tiles = [] self.npcs = [] self.chests = [] self.sprites = [] self.shops = [] self.dummy_chests = [] data_type = stream.peek_u16() while data_type != 0xffff: if data_type == 0x0: if self.header is not None: raise RuntimeError( f"Warning: Map {hex(map_id)} has more than one header?" ) self.header = MapHeader(stream) elif data_type == 0x1: self.tiles.append(Tile(stream)) elif data_type == 0x2: self.npcs.append(Npc(stream)) elif data_type == 0x3: self.chests.append(Chest(stream)) elif data_type == 0x4: self.sprites.append(Sprite(stream)) elif data_type == 0x5: self.shops.append(Shop(stream)) else: raise RuntimeError(f"Unknown type: {data_type}") data_type = stream.peek_u16() # Some treasure chests are basically placeholders for events. We want to # make a note of these chest IDs. if len(self.chests) > 0 and len(self.sprites) > 0: for index, chest in enumerate(self.chests): for sprite in self.sprites: if chest.x_pos == sprite.x_pos and chest.y_pos == sprite.y_pos: self.dummy_chests.append(chest.chest_id) break
def open_bytestream(self, offset: int, size: int = -1, check_alignment: bool = True) -> InputStream: """ Opens a InputStream to read over part of the ROM. :param offset: Offset of the start of the InputStream :param size: Number of bytes to include :param check_alignment: Whether non-byte reads should checked that they are word aligned :return: The InputStream """ if 0 <= offset < len(self.rom_data): data = self.rom_data[offset:] if size < 0 else self.rom_data[ offset:(offset + size)] return InputStream(data, check_alignment=check_alignment) raise RuntimeError( f"Index out of bounds {hex(offset)} vs {len(self.rom_data)}")
def get_event(self, offset: int) -> InputStream: """ Creates an InputStream from a simple event. Note: This does *not* follow jumps. Many events only include one "end of event" command at the end, and this method will work to read them. Some SoC events, however, include multiple end of event commands, and this method would only read to the first one. :param offset: Offset of the start of the event. :return: InputStream representing the event. """ end_offset = offset last_cmd = -1 while last_cmd != 0: cmd_len = self.rom_data[end_offset + 1] last_cmd = self.rom_data[end_offset] end_offset += cmd_len return InputStream(self.rom_data[offset:end_offset])
def __init__(self, stream: InputStream, length: int): self.magic = [] self.armor = [] self.weapons = [] self.items = [] # There's no byte code for magic, so start there and change based on the data active = self.magic read_length = 0 while read_length < length: data = stream.get_u8() if data == 0xfc: active = self.armor elif data == 0xfd: active = self.weapons elif data == 0xfe: active = self.items else: active.append(data) read_length += 1
def __init__(self, stream: InputStream = None): if stream is None: self.identifier = 0 self.event = 0 self.x_pos = 0 self.y_pos = 0 self.sprite_id = 0 self.move_speed = 0 self.facing = 0 self.in_room = 0 else: self.identifier = stream.get_u16() self.event = stream.get_u16() self.x_pos = stream.get_u16() self.y_pos = stream.get_u16() self.sprite_id = stream.get_u16() self.move_speed = stream.get_u16() self.facing = stream.get_u16() self.in_room = stream.get_u16()
def get_stream(self, offset: int, end_marker: bytearray) -> InputStream: """ Gets an InputStream from the ROM. This should primarily be used where the data is not null terminated (use `get_string` then), and is of variable length. Many events could be read using this method and a bytearray of [0x0, 0x4, 0xff, 0xff] for example, but `get_event` should be used in that case. :param offset: The offset of the start of the stream. :param end_marker: The end marker. :return: InputStream representing the data. """ end_offset = offset markers_found = 0 while markers_found < len(end_marker): if self.rom_data[end_offset] == end_marker[markers_found]: markers_found += 1 else: markers_found = 0 end_offset += 1 return InputStream(self.rom_data[offset:end_offset])
def read(stream: InputStream): chest_data = stream.get_u32() if chest_data & 0x80000000 == 0: return MoneyChest(chest_data) else: return ItemChest(chest_data)
def __getitem__(self, index): return TextBlock._as_ascii( InputStream(self.strings[index], check_alignment=False))
def __setitem__(self, index, value): self.strings[index] = TextBlock._encode_text( InputStream(value, check_alignment=False))
def encode_text(text: str) -> bytearray: data = bytearray() for char in text: data.append(ord(char)) return TextBlock._encode_text(InputStream(data))
def __init__(self, stream: InputStream = None): if stream is None: self.base_hp = 0 self.base_mp = 0 self.starting_spell_level = 0 self.base_strength = 0 self.base_agility = 0 self.base_intellect = 0 self.base_stamina = 0 self.base_luck = 0 self.base_accuracy = 0 self.base_evade = 0 self.base_mdef = 0 self.weapon_id = 0 self.armor_id = 0 self.unused = 0 else: self.base_hp = stream.get_u16() self.base_mp = stream.get_u16() self.starting_spell_level = stream.get_u8() self.base_strength = stream.get_u8() self.base_agility = stream.get_u8() self.base_intellect = stream.get_u8() self.base_stamina = stream.get_u8() self.base_luck = stream.get_u8() self.base_accuracy = stream.get_u8() self.base_evade = stream.get_u8() self.base_mdef = stream.get_u8() self.weapon_id = stream.get_u8() self.armor_id = stream.get_u8() self.unused = stream.get_u8()
def __init__(self, stream: InputStream = None): if stream is None: self.exp_reward = 0 self.gil_reward = 0 self.max_hp = 0 self.morale = 0 self.unused_ai = 0 self.evasion = 0 self.pdef = 0 self.hit_count = 0 self.acc = 0 self.atk = 0 self.agi = 0 self.intel = 0 self.crit_rate = 0 self.status_atk_elem = 0 self.status_atk_ailment = 0 self.family = 0 self.mdef = 0 self.unused = 0 self.elem_weakness = 0 self.elem_resists = 0 self.drop_type = 0 self.drop_id = 0 self.drop_chance = 0 self.padding = [] else: self.exp_reward = stream.get_u16() self.gil_reward = stream.get_u16() self.max_hp = stream.get_u16() self.morale = stream.get_u8() self.unused_ai = stream.get_u8() self.evasion = stream.get_u8() self.pdef = stream.get_u8() self.hit_count = stream.get_u8() self.acc = stream.get_u8() self.atk = stream.get_u8() self.agi = stream.get_u8() self.intel = stream.get_u8() self.crit_rate = stream.get_u8() self.status_atk_elem = stream.get_u16() self.status_atk_ailment = stream.get_u8() self.family = stream.get_u8() self.mdef = stream.get_u8() self.unused = stream.get_u8() self.elem_weakness = stream.get_u16() self.elem_resists = stream.get_u16() self.drop_type = stream.get_u8() self.drop_id = stream.get_u8() self.drop_chance = stream.get_u8() self.padding = [] for index in range(3): self.padding.append(stream.get_u8())