def write(self, rom: Rom) -> Rom: # Since there's a LUT and the data that it points to, create two output streams. # This should work because both are continuous. data_lut_stream = OutputStream() shop_inventory = OutputStream() next_shop_addr = self.shop_data_pointers[0].pointer start_size = 0 for index in range(51): new_shop_length = self.shop_inventories[index].write( shop_inventory) sdp = self.shop_data_pointers[index] sdp.contents = ( (sdp.shop_graphic << 4) & 0xf0) | (new_shop_length & 0x0f) sdp.pointer = next_shop_addr sdp.write(data_lut_stream) # Because the size of the data varies by shop, we have to keep track of how # large the output buffer was and move the pointer up by the difference. next_shop_addr += (shop_inventory.size() - start_size) start_size = shop_inventory.size() # Make a dictionary for the two parts so we only have to write the new Rom once. patches = { 0x1E070C: data_lut_stream.get_buffer(), Rom.pointer_to_offset(self.shop_data_pointers[0].pointer): shop_inventory.get_buffer() } return rom.apply_patches(patches)
def add_credits(rom: Rom) -> Rom: credits_lut = rom.get_lut(0x1D871C, 128) base_addr = credits_lut[0] new_lut = OutputStream() data_stream = OutputStream() for index, line in enumerate(CREDITS_TEXT.splitlines()[1:]): line = line.strip() if len(line) > 0: encoded = TextBlock.encode_text(line) new_lut.put_u32(base_addr + data_stream.size()) data_stream.put_bytes(encoded) else: new_lut.put_u32(0x0) # And EOF marker new_lut.put_u32(0xffffffff) # Change the duration so it doesn't take so long to scroll duration = OutputStream() duration.put_u16(60 * 60) return rom.apply_patches({ 0x016848: duration.get_buffer(), 0x1D871C: new_lut.get_buffer(), Rom.pointer_to_offset(base_addr): data_stream.get_buffer() })
def _do_placement(self, key_item_locations: tuple): source_headers = self._prepare_header(key_item_locations) patches = {} for event_id, source in EVENT_SOURCE_MAP.items(): event_source = pparse(f"{source_headers}\n\n{source}") event_addr = self.events.get_addr(event_id) event_space = self.rom.get_event( Rom.pointer_to_offset(event_addr)).size() # See if the event fits into it's vanilla location. event = easm.parse(event_source, event_addr) if len(event) > event_space: # Didn't fit. Move it to our space. event_addr = self.our_events.current_addr() self.events.set_addr(event_id, event_addr) # We'll write all of our events together at the end event = easm.parse(event_source, event_addr) self.our_events.put_bytes(event) else: # Add the event to the vanilla patches. patches[Rom.pointer_to_offset(event_addr)] = event self._update_npcs(key_item_locations) self._unite_mystic_key_doors() self._better_earth_plate() self._rewrite_give_texts() self._save_chests() # Append our new (relocated) events in the patch data. patches[0x223F4C] = self.our_events.get_buffer() # And then get all the patch data for the LUTs for offset, patch in self.events.get_patches().items(): patches[offset] = patch self.rom = self.rom.apply_patches(patches) self.rom = self.maps.write(self.rom)
def write(self, rom: Rom) -> Rom: patches = {} for index, map in enumerate(self._maps): # TODO: Figure out what breaks the Caravan. if index == 0x73: continue data = OutputStream() map.write(data) patches[Rom.pointer_to_offset( self._map_lut[index])] = data.get_buffer() return rom.apply_patches(patches)
def __init__(self, rom: Rom): self._maps = [] self.dummy_chests = [] self._map_lut = rom.get_lut(0x1E4F40, 124) for map_id, map_addr in enumerate(self._map_lut): map_stream = rom.get_stream(Rom.pointer_to_offset(map_addr), bytearray.fromhex("ffff")) map = MapFeatures(map_id, map_stream) self._maps.append(map) # Collect the dummy chests together self.dummy_chests += map.dummy_chests
def __init__(self, rom: Rom): data_lut_stream = rom.open_bytestream(0x1DFB04, 0x198) self.shop_data_pointers = [] self.shop_inventories = [] for index in range(51): shop = ShopDataPointer(data_lut_stream) self.shop_data_pointers.append(shop) # This is overkill for vanilla, but is still small. Since code bytes don't count, the # value in shop.shop_data_length isn't quite as useful as it could be. inventory = ShopInventory( rom.open_bytestream(Rom.pointer_to_offset(shop.pointer), 0x20), shop.shop_data_length) self.shop_inventories.append(inventory)
def pack(self, rom: Rom) -> Rom: text_block = OutputStream() text_lut = OutputStream() next_addr = self.lut[0] text_block_offset = Rom.pointer_to_offset(next_addr) for index, data in enumerate(self.strings): if data is not None: text_lut.put_u32(next_addr) text_block.put_bytes(data) next_addr += len(data) else: text_lut.put_u32(self.lut[0]) patches = { self.lut_offset: text_lut.get_buffer(), text_block_offset: text_block.get_buffer() } return rom.apply_patches(patches)
def __init__(self, rom: Rom, lut_offset: int, count: int): self.lut_offset = lut_offset self.lut = list(rom.get_lut(lut_offset, count)) self.strings = [] for addr in self.lut: self.strings.append(rom.get_string(Rom.pointer_to_offset(addr)))
def get_map_offset(self, map_id: int) -> int: return Rom.pointer_to_offset(self._map_lut[map_id])