def write_to_rom(self, rom): # Write animations to rom animation_offsets = [] for animation in self.animations: offset = animation.to_block(rom) animation_offsets.append(offset) # Build up animation table new_num_rows = len( self.animations ) + 1 # Add 1 because the table is prefixed with an empty entry if self.table.num_rows != new_num_rows: self.table.recreate(new_num_rows) self.table[0] = [0, 0, 0, 0] # The first entry should be empty for i, animation in enumerate(self.animations): self.table[i + 1] = [ to_snes_address(animation_offsets[i]), animation.graphics_data_size, animation.frames, animation.unknown ] # Relocate animation table so we can store more than six animations new_table_offset = rom.allocate(size=self.table.size) write_animation_table_address(rom, to_snes_address(new_table_offset)) self.table.to_block(rom, offset=new_table_offset)
def write_to_rom(self, rom): apply_relocation_patch(rom) # Write frames and populate swirl table frame_hashes = {} animation_pointers = [] animation_pointers_index = 0 for i, swirl in enumerate(self.swirls): num_frames = len(swirl.frames) self.swirl_table[i] = [swirl.speed, animation_pointers_index, num_frames] frame_offsets = write_swirl_frames(rom, swirl, frame_hashes) animation_pointers += frame_offsets animation_pointers_index += num_frames # Allocate animation pointer table animation_pointer_table_offset = rom.allocate(size=4 * len(animation_pointers)) for offset, pointer_delta in RELOCATED_SWIRL_ANIMATION_POINTER_TABLE_POINTERS: rom.write_multi(offset, to_snes_address(animation_pointer_table_offset + pointer_delta), 3) # Write animation pointer table for animation_pointer in animation_pointers: rom.write_multi(animation_pointer_table_offset, to_snes_address(animation_pointer), 4) animation_pointer_table_offset += 4 # Write swirls table self.swirl_table.to_block( rom, offset=from_snes_address(SWIRL_TABLE_DEFAULT_OFFSET))
def write_to_rom(self, rom): self.enemy_config_table.to_block( block=rom, offset=from_snes_address(ENEMY_CONFIGURATION_TABLE_DEFAULT_OFFSET)) self.enemy_group_bg_table.to_block( block=rom, offset=from_snes_address( ENEMY_GROUP_BACKGROUND_TABLE_DEFAULT_OFFSET)) # Write the sprites self.graphics_pointer_table.recreate(num_rows=len(self.battle_sprites)) for i, battle_sprite in enumerate(self.battle_sprites): self.graphics_pointer_table[i] = [None, battle_sprite.size()] with EbCompressibleBlock( size=battle_sprite.block_size()) as compressed_block: battle_sprite.to_block(block=compressed_block, offset=0) compressed_block.compress() graphics_offset = rom.allocate(data=compressed_block) self.graphics_pointer_table[i][0] = to_snes_address( graphics_offset) graphics_pointer_table_offset = rom.allocate( size=self.graphics_pointer_table.size) self.graphics_pointer_table.to_block( block=rom, offset=graphics_pointer_table_offset) write_asm_pointer( block=rom, offset=GRAPHICS_POINTER_TABLE_ASM_POINTER_OFFSET, pointer=to_snes_address(graphics_pointer_table_offset)) for pointer_offset in GRAPHICS_POINTER_TABLE_POINTER_OFFSETS: rom.write_multi( pointer_offset, item=to_snes_address(graphics_pointer_table_offset), size=3) # Write the palettes if self.palettes: palettes_offset = rom.allocate(size=self.palettes[0].block_size() * len(self.palettes)) write_asm_pointer(block=rom, offset=PALETTES_ASM_POINTER_OFFSET, pointer=to_snes_address(palettes_offset)) for palette in self.palettes: palette.to_block(block=rom, offset=palettes_offset) palettes_offset += palette.block_size() # Write the groups for i, group in enumerate(self.enemy_groups): offset = rom.allocate( size=(len(group) * EnemyGroupTableEntry.size + 1)) self.enemy_group_table[i][0] = to_snes_address(offset) for group_entry in group: EnemyGroupTableEntry.to_block(block=rom, offset=offset, value=group_entry) offset += EnemyGroupTableEntry.size rom[offset] = 0xff self.enemy_group_table.to_block( block=rom, offset=from_snes_address(ENEMY_GROUP_TABLE_DEFAULT_OFFSET))
def write_to_rom(self, rom): graphics_1_block_size = self.graphics_1.block_size(bpp=2) with EbCompressibleBlock(graphics_1_block_size) as compressed_block: self.graphics_1.to_block(block=compressed_block, offset=0, bpp=2) compressed_block.compress() graphics_1_offset = rom.allocate(data=compressed_block) write_asm_pointer(block=rom, offset=GRAPHICS_1_ASM_POINTER_OFFSET, pointer=to_snes_address(graphics_1_offset)) graphics_2_block_size = self.graphics_2.block_size(bpp=2) with EbCompressibleBlock(graphics_2_block_size) as compressed_block: self.graphics_2.to_block(block=compressed_block, offset=0, bpp=2) compressed_block.compress() graphics_2_offset = rom.allocate(data=compressed_block) write_asm_pointer(block=rom, offset=GRAPHICS_2_ASM_POINTER_OFFSET, pointer=to_snes_address(graphics_2_offset)) # Write palettes offset = FLAVOR_PALETTES_OFFSET for palette in self.flavor_palettes: palette.to_block(block=rom, offset=offset) offset += 64 # Write names for asm_pointer_offset in FLAVOR_NAME_ASM_POINTER_OFFSETS: name = self.flavor_names[asm_pointer_offset] offset = rom.allocate(size=FLAVOR_NAME_ENTRY.size) FLAVOR_NAME_ENTRY.to_block(block=rom, offset=offset, value=name) write_asm_pointer(block=rom, offset=asm_pointer_offset, pointer=to_snes_address(offset))
def write_town_map_icons_to_rom(self, rom): log.debug("Writing town map icons") graphics_offset, arrangement_offset, palette_offsets = self.town_map_icons.to_block(rom) write_asm_pointer(block=rom, offset=TOWN_MAP_ICON_GRAPHICS_ASM_POINTER_OFFSET, pointer=to_snes_address(graphics_offset)) write_asm_pointer(block=rom, offset=TOWN_MAP_ICON_PALETTE_ASM_POINTER_OFFSET, pointer=to_snes_address(palette_offsets[0]))
def write_town_map_icons_to_rom(self, rom): log.debug("Writing town map icons") graphics_offset, arrangement_offset, palette_offsets = self.town_map_icons.to_block(rom) write_asm_pointer(block=rom, offset=TOWN_MAP_ICON_GRAPHICS_ASM_POINTER_OFFSET, pointer=to_snes_address(graphics_offset)) write_asm_pointer(block=rom, offset=TOWN_MAP_ICON_PALETTE_ASM_POINTER_OFFSET, pointer=to_snes_address(palette_offsets[0]))
def write_logos_to_rom(self, rom, logos, infos): for info, logo in zip(infos, logos): graphics_offset, arrangement_offset, palette_offsets = logo.to_block(rom) for asm_pointer_offset in info.graphics_asm_pointer_offsets: write_asm_pointer(block=rom, offset=asm_pointer_offset, pointer=to_snes_address(graphics_offset)) for asm_pointer_offset in info.arrangement_asm_pointer_offsets: write_asm_pointer(block=rom, offset=asm_pointer_offset, pointer=to_snes_address(arrangement_offset)) for offset, asm_pointer_offset in zip(palette_offsets, info.palette_asm_pointer_offsets): write_asm_pointer(block=rom, offset=asm_pointer_offset, pointer=to_snes_address(offset))
def write_logos_to_rom(self, rom, logos, infos): for info, logo in zip(infos, logos): graphics_offset, arrangement_offset, palette_offsets = logo.to_block(rom) for asm_pointer_offset in info.graphics_asm_pointer_offsets: write_asm_pointer(block=rom, offset=asm_pointer_offset, pointer=to_snes_address(graphics_offset)) for asm_pointer_offset in info.arrangement_asm_pointer_offsets: write_asm_pointer(block=rom, offset=asm_pointer_offset, pointer=to_snes_address(arrangement_offset)) for offset, asm_pointer_offset in zip(palette_offsets, info.palette_asm_pointer_offsets): write_asm_pointer(block=rom, offset=asm_pointer_offset, pointer=to_snes_address(offset))
def write_to_rom(self, rom): self.font_pointer_table.from_block(block=rom, offset=from_snes_address(FONT_POINTER_TABLE_OFFSET)) for i, font in enumerate(self.fonts): log.debug("Writing font #{} to the ROM".format(FONT_FILENAMES[i])) graphics_offset, widths_offset = font.to_block(block=rom) self.font_pointer_table[i][0] = to_snes_address(widths_offset) self.font_pointer_table[i][1] = to_snes_address(graphics_offset) self.font_pointer_table.to_block(block=rom, offset=from_snes_address(FONT_POINTER_TABLE_OFFSET)) self.write_credits_font_to_rom(rom)
def write_to_rom(self, rom): self.font_pointer_table.from_block(block=rom, offset=from_snes_address(FONT_POINTER_TABLE_OFFSET)) for i, font in enumerate(self.fonts): log.debug("Writing font #{} to the ROM".format(FONT_FILENAMES[i])) graphics_offset, widths_offset = font.to_block(block=rom) self.font_pointer_table[i][0] = to_snes_address(widths_offset) self.font_pointer_table[i][1] = to_snes_address(graphics_offset) self.font_pointer_table.to_block(block=rom, offset=from_snes_address(FONT_POINTER_TABLE_OFFSET)) self.write_credits_font_to_rom(rom)
def write_to_rom(self, rom): rom.deallocate((0xf61e7, 0xf8984)) pointer_table_offset = rom.allocate(size=self.table.size, can_write_to=partial(not_in_bank, 0x0f)) self.table.to_block(rom, pointer_table_offset) rom.write_multi(self.POINTER_TABLE_POINTER_OFFSET, to_snes_address(pointer_table_offset), 3)
def write_to_rom(self, rom): # Deallocate the range of the ROM in which we will write the door destinations. # We deallocate it here instead of specifying it in FREE_RANGES because we want to be sure that this module # get first dibs at writing to this range. This is because door destinations needs to be written to the 0x0F # bank of the EB ROM, and this is one of the few ranges available in that bank. rom.deallocate((0x0F0000, 0x0F58EE)) destination_offsets = dict() empty_area_offset = from_snes_address( rom.allocate(data=[0, 0], can_write_to=not_in_destination_bank)) i = 0 for door_area in self.door_areas: if (door_area is None) or (not door_area): self.pointer_table[i] = [empty_area_offset] else: num_doors = len(door_area) area_offset = rom.allocate( size=(2 + num_doors * 5), can_write_to=not_in_destination_bank) self.pointer_table[i] = [to_snes_address(area_offset)] rom.write_multi(area_offset, num_doors, 2) area_offset += 2 for door in door_area: door.write_to_block(rom, area_offset, destination_offsets) area_offset += 5 i += 1 self.pointer_table.to_block(rom, from_snes_address(0xD00000))
def write_sprites_to_free(self, rom): if self.num_sprites == 0: self.sprite_pointers = [] return unique_sprites, unique_sprite_usages = self.calculate_unique_sprites() # Find a free area offset = rom.allocate(size=sum( [x.block_size() for x in unique_sprites.values()]), can_write_to=(lambda y: (y & 0xf) == 0)) self.bank = to_snes_address(offset) >> 16 offset_start = offset & 0xffff # Write each sprite sprite_offsets = dict() for i, (sprite_hash, sprite) in enumerate(sorted(unique_sprites.items())): sprite.to_block(rom, offset) sprite_offsets[sprite_hash] = offset offset += sprite.block_size() # Get the pointers for each sprite in the group self.sprite_pointers = [None] * self.num_sprites for i, (sprite_hash, is_mirrored) in enumerate(unique_sprite_usages): self.sprite_pointers[i] = (sprite_offsets[sprite_hash] & 0xffff) | is_mirrored | ( self.sprites[i][1] << 1)
def _write_compressed_block(rom, compressed_block, pointer): compressed_block.compress() new_offset = rom.allocate(data=compressed_block) write_asm_pointer( block=rom, offset=pointer, pointer=to_snes_address(new_offset) ) return new_offset
def write_to_rom(self, rom): for offset, table in self.tables.iteritems(): new_table_offset = rom.allocate(size=table.size) table.to_block(rom, new_table_offset) log.info("Writing table @ " + hex(new_table_offset)) for pointer in self.TABLE_OFFSETS[offset]: pointer.write(rom, to_snes_address(new_table_offset))
def write_background_data_to_rom(self, rom): # Write the background tileset data block_size = self.bg_tileset.block_size(bpp=BG_TILESET_BPP) with EbCompressibleBlock(block_size) as block: self.bg_tileset.to_block(block=block, offset=0, bpp=BG_TILESET_BPP) self._write_compressed_block(rom, block, BG_TILESET_POINTER) # Write the background tile arrangement data block_size = self.bg_arrangement.block_size() with EbCompressibleBlock(block_size) as block: self.bg_arrangement.to_block(block=block, offset=0) self._write_compressed_block(rom, block, BG_ARRANGEMENT_POINTER) # Write the background palette data # There is an additional pointer to this location, so change that one # too block_size = self.bg_palette.block_size() with EbCompressibleBlock(block_size) as block: self.bg_palette.to_block(block=block, offset=0) new_offset = self._write_compressed_block(rom, block, BG_PALETTE_POINTER) write_asm_pointer(block=rom, offset=BG_PALETTE_POINTER_SECONDARY, pointer=to_snes_address(new_offset)) # Write the background animated palette data block_size = self.bg_anim_palette.block_size() with EbCompressibleBlock(block_size) as block: self.bg_anim_palette.to_block(block=block, offset=0) self._write_compressed_block(rom, block, BG_ANIM_PALETTE_POINTER)
def write_background_data_to_rom(self, rom): # Write the background tileset data block_size = self.bg_tileset.block_size(bpp=BG_TILESET_BPP) with EbCompressibleBlock(block_size) as block: self.bg_tileset.to_block(block=block, offset=0, bpp=BG_TILESET_BPP) self._write_compressed_block(rom, block, BG_TILESET_POINTER) # Write the background tile arrangement data block_size = self.bg_arrangement.block_size() with EbCompressibleBlock(block_size) as block: self.bg_arrangement.to_block(block=block, offset=0) self._write_compressed_block(rom, block, BG_ARRANGEMENT_POINTER) # Write the background palette data # There is an additional pointer to this location, so change that one # too block_size = self.bg_palette.block_size() with EbCompressibleBlock(block_size) as block: self.bg_palette.to_block(block=block, offset=0) new_offset = self._write_compressed_block( rom, block, BG_PALETTE_POINTER ) write_asm_pointer( block=rom, offset=BG_PALETTE_POINTER_SECONDARY, pointer=to_snes_address(new_offset) ) # Write the background animated palette data block_size = self.bg_anim_palette.block_size() with EbCompressibleBlock(block_size) as block: self.bg_anim_palette.to_block(block=block, offset=0) self._write_compressed_block(rom, block, BG_ANIM_PALETTE_POINTER)
def _write_compressed_block(rom, compressed_block, pointer): compressed_block.compress() new_offset = rom.allocate(data=compressed_block) write_asm_pointer(block=rom, offset=pointer, pointer=to_snes_address(new_offset)) return new_offset
def write_to_rom(self, rom): rom.deallocate((0xf61e7, 0xf8984)) pointer_table_offset = rom.allocate(size=self.table.size, can_write_to=partial(not_in_bank, 0x0f)) self.table.to_block(rom, pointer_table_offset) rom.write_multi(self.POINTER_TABLE_POINTER_OFFSET, to_snes_address(pointer_table_offset), 3)
def write_gfx_to_rom(self, rom, offset, gfx): graphics_offset, arrangement_offset, palette_offsets = gfx.to_block( block=rom) write_asm_pointer(block=rom, offset=offset, pointer=to_snes_address(graphics_offset)) gfx.palettes[0].to_block(block=rom, offset=CAST_GRAPHICS_PALETTE_OFFSET)
def write_to_rom(self, rom): tileset_block_size = self.tileset.block_size(bpp=4) with EbCompressibleBlock(tileset_block_size) as compressed_block: self.tileset.to_block(block=compressed_block, offset=0, bpp=4) compressed_block.compress() tileset_offset = rom.allocate(data=compressed_block) write_asm_pointer(block=rom, offset=GRAPHICS_ASM_POINTER_OFFSET, pointer=to_snes_address(tileset_offset)) self.palette.to_block(block=rom, offset=PALETTE_OFFSET)
def write_to_rom(self, rom): offset = rom.allocate(size=len(self.data)) self.write_pointer_to_rom(rom, to_snes_address(offset)) log.debug('Writing staff text') rom[offset:offset + len(self.data)] = self.data for length_offset in LENGTH_OFFSETS: rom.write_multi(length_offset, self.height, 2)
def to_block(cls, block, offset, value): data_size = cls.data_table_entry.to_block_size(value) data_offset = block.allocate(size=data_size, can_write_to=partial( is_in_bank, cls.bank)) cls.pointer_table_entry.to_block(block, offset, to_snes_address(data_offset)) cls.data_table_entry.to_block(block, data_offset, value)
def write_to_rom(self, rom): rom.deallocate((0xf58ef, 0xf61e5)) pointer_table_offset = rom.allocate(size=self.pointer_table.size, can_write_to=partial( not_in_bank, 0x0f)) self.pointer_table.to_block(rom, pointer_table_offset) rom.write_multi(MAP_MUSIC_ASM_POINTER_OFFSET, to_snes_address(pointer_table_offset), 3)
def to_block(self, block, value): if self.pointer: loc = block.allocate(size=self.table_entry.size) self.pointer.write(block, to_snes_address(loc)) else: loc = self.default_offset value = self.table_entry.from_yml_rep(value) self.table_entry.to_block(block, loc, value)
def to_block(self, block, value): if self.pointer: loc = block.allocate(size=self.table_entry.size) self.pointer.write(block, to_snes_address(loc)) else: loc = self.default_offset value = self.table_entry.from_yml_rep(value) self.table_entry.to_block(block, loc, value)
def test_to_snes_address(): assert_equal(to_snes_address(0), 0xc00000) assert_equal(to_snes_address(0xf1234), 0xcf1234) assert_equal(to_snes_address(0x2abc4f), 0xeabc4f) assert_equal(to_snes_address(0x3fffff), 0xffffff) assert_equal(to_snes_address(0x400000), 0x400000) assert_equal(to_snes_address(0x4daa34), 0x4daa34) assert_equal(to_snes_address(0x5fffff), 0x5fffff)
def test_to_snes_address(): assert_equal(to_snes_address(0), 0xc00000) assert_equal(to_snes_address(0xf1234), 0xcf1234) assert_equal(to_snes_address(0x2abc4f), 0xeabc4f) assert_equal(to_snes_address(0x3fffff), 0xffffff) assert_equal(to_snes_address(0x400000), 0x400000) assert_equal(to_snes_address(0x4daa34), 0x4daa34) assert_equal(to_snes_address(0x5fffff), 0x5fffff)
def to_block(self, block, tileset_asm_pointer_offset, palette_offset): tileset_block_size = self.tileset.block_size(bpp=2) with EbCompressibleBlock(tileset_block_size) as compressed_block: self.tileset.to_block(block=compressed_block, offset=0, bpp=2) compressed_block.compress() tileset_offset = block.allocate(data=compressed_block) write_asm_pointer(block=block, offset=tileset_asm_pointer_offset, pointer=to_snes_address(tileset_offset)) self.palette.to_block(block=block, offset=palette_offset)
def write_to_rom(self, rom): tileset_block_size = self.tileset.block_size(bpp=4) with EbCompressibleBlock(tileset_block_size) as compressed_block: self.tileset.to_block(block=compressed_block, offset=0, bpp=4) compressed_block.compress() tileset_offset = rom.allocate(data=compressed_block) write_asm_pointer(block=rom, offset=GRAPHICS_ASM_POINTER_OFFSET, pointer=to_snes_address(tileset_offset)) self.palette.to_block(block=rom, offset=PALETTE_OFFSET)
def write_to_rom(self, rom): pointer_table_offset = rom.allocate(size=self.pointer_table.size) bank = rom.get_largest_unallocated_range()[0] >> 16 log.debug("Writing data to {:#x} bank".format(bank)) self.pointer_table_entry_class.bank = bank rom[POINTER_TABLE_BANK_OFFSET] = bank + 0xc0 log.debug("Writing pointer table to {:#x}".format(pointer_table_offset)) rom.write_multi(POINTER_TABLE_POINTER_OFFSET, to_snes_address(pointer_table_offset), 3) self.pointer_table.to_block(rom, pointer_table_offset)
def write_to_rom(self, rom): self.enemy_config_table.to_block(block=rom, offset=from_snes_address(ENEMY_CONFIGURATION_TABLE_DEFAULT_OFFSET)) self.enemy_group_bg_table.to_block(block=rom, offset=from_snes_address(ENEMY_GROUP_BACKGROUND_TABLE_DEFAULT_OFFSET)) # Write the sprites self.graphics_pointer_table.recreate(num_rows=len(self.battle_sprites)) for i, battle_sprite in enumerate(self.battle_sprites): self.graphics_pointer_table[i] = [None, battle_sprite.size()] with EbCompressibleBlock(size=battle_sprite.block_size()) as compressed_block: battle_sprite.to_block(block=compressed_block, offset=0) compressed_block.compress() graphics_offset = rom.allocate(data=compressed_block) self.graphics_pointer_table[i][0] = to_snes_address(graphics_offset) graphics_pointer_table_offset = rom.allocate(size=self.graphics_pointer_table.size) self.graphics_pointer_table.to_block(block=rom, offset=graphics_pointer_table_offset) write_asm_pointer(block=rom, offset=GRAPHICS_POINTER_TABLE_ASM_POINTER_OFFSET, pointer=to_snes_address(graphics_pointer_table_offset)) for pointer_offset in GRAPHICS_POINTER_TABLE_POINTER_OFFSETS: rom.write_multi(pointer_offset, item=to_snes_address(graphics_pointer_table_offset), size=3) # Write the palettes if self.palettes: palettes_offset = rom.allocate(size=self.palettes[0].block_size() * len(self.palettes)) write_asm_pointer(block=rom, offset=PALETTES_ASM_POINTER_OFFSET, pointer=to_snes_address(palettes_offset)) for palette in self.palettes: palette.to_block(block=rom, offset=palettes_offset) palettes_offset += palette.block_size() # Write the groups for i, group in enumerate(self.enemy_groups): offset = rom.allocate(size=(len(group) * EnemyGroupTableEntry.size + 1)) self.enemy_group_table[i] = [to_snes_address(offset)] for group_entry in group: EnemyGroupTableEntry.to_block(block=rom, offset=offset, value=group_entry) offset += EnemyGroupTableEntry.size rom[offset] = 0xff self.enemy_group_table.to_block(block=rom, offset=from_snes_address(ENEMY_GROUP_TABLE_DEFAULT_OFFSET))
def write_to_rom(self, rom): pointer_table_offset = rom.allocate(size=self.pointer_table.size) bank = rom.get_largest_unallocated_range()[0] >> 16 log.debug("Writing data to {:#x} bank".format(bank)) self.pointer_table_entry_class.bank = bank rom[POINTER_TABLE_BANK_OFFSET] = bank + 0xc0 log.debug( "Writing pointer table to {:#x}".format(pointer_table_offset)) rom.write_multi(POINTER_TABLE_POINTER_OFFSET, to_snes_address(pointer_table_offset), 3) self.pointer_table.to_block(rom, pointer_table_offset)
def to_block(cls, block, offset, value): if not value: super(TownMapIconPlacementPointerTableEntry, cls).to_block(block, offset, 0) else: pointer = block.allocate( size=sum([cls.table_entry_class.size for x in value]) + 1) super(TownMapIconPlacementPointerTableEntry, cls).to_block(block, offset, to_snes_address(pointer)) for icon_placement in value: cls.table_entry_class.to_block(block, pointer, icon_placement) pointer += cls.table_entry_class.size block[pointer] = 0xff
def write_to_rom(self, rom): if not test_swirl_relocated(rom): apply_relocation_patch(rom) # Write frames and populate swirl table frame_hashes = {} animation_pointers = [] animation_pointers_index = 0 for i, swirl in enumerate(self.swirls): num_frames = len(swirl.frames) self.swirl_table[i] = [ swirl.speed, animation_pointers_index, num_frames ] frame_offsets = write_swirl_frames(rom, swirl, frame_hashes) animation_pointers += frame_offsets animation_pointers_index += num_frames # Allocate animation pointer table animation_pointer_table_offset = rom.allocate(size=4 * len(animation_pointers)) for offset, pointer_delta in RELOCATED_SWIRL_ANIMATION_POINTER_TABLE_POINTERS: rom.write_multi( offset, to_snes_address(animation_pointer_table_offset + pointer_delta), 3) # Write animation pointer table for animation_pointer in animation_pointers: rom.write_multi(animation_pointer_table_offset, to_snes_address(animation_pointer), 4) animation_pointer_table_offset += 4 # Write swirls table self.swirl_table.to_block( rom, offset=from_snes_address(SWIRL_TABLE_DEFAULT_OFFSET))
def write_to_rom(self, rom): with Block(size=sum(x.block_size() for x in self.groups)) as block: offset = 0 # Write all the groups to the block, and sprites to rom for i, group in enumerate(self.groups): group.write_sprites_to_free(rom) group.to_block(block, offset) self.group_pointer_table[i] = [offset] offset += group.block_size() # Write the block to rom and correct the group pointers address = to_snes_address(rom.allocate(data=block)) for i in range(self.group_pointer_table.num_rows): self.group_pointer_table[i][0] += address self.group_pointer_table.to_block(block=rom, offset=from_snes_address(GROUP_POINTER_TABLE_OFFSET)) self.palette_table.to_block(block=rom, offset=from_snes_address(PALETTE_TABLE_OFFSET))
def write_to_rom(self, rom): if self.mode != 'prefix': self.get_patch(self.mode, rom.type).apply(rom) loc = rom.allocate(size=self.table_entry.to_block_size(self.text)) addr = to_snes_address(loc) write_asm_pointer(block=rom, offset=self.asm_pointer_offset, pointer=addr) self.table_entry.to_block(rom, loc, self.text) if self.mode == 'prefix': self.get_patch(self.mode, rom.type).apply(rom) rom.write_multi(self.custom_asm_offset + 9, addr & 0xFFFF, 2) rom.write_multi(self.custom_asm_offset + 14, (addr >> 16) & 0xFF, 2) rom.write_multi(self.custom_asm_offset + 26, len(self.text), 2)
def write_chars_layouts_to_rom(self, rom): block_size = sum( TitleScreenLayoutEntry.block_size()*len(c) for c in self.chars_layouts ) # Ensure the new data is located in only one bank # Spreading it across two banks might make part of it inaccessible. def can_write_to(begin): return begin >> 16 == (begin + block_size) >> 16 with Block(block_size) as block: # Write the character animation data to the ROM offset = 0 for layout in self.chars_layouts: for entry in layout: entry.to_block(block=block, offset=offset) offset += entry.block_size() new_offset = to_snes_address(rom.allocate( data=block, size=block_size, can_write_to=can_write_to )) # Write the offsets to the layouts to the ROM new_bank = new_offset >> 16 new_data_start = new_offset & 0xFFFF data_offset = new_data_start for c, layout in enumerate(self.chars_layouts): rom[CHARS_LAYOUT_TABLE + c*2:CHARS_LAYOUT_TABLE + c*2 + 2] = [ data_offset & 0xFF, data_offset >> 8 ] data_offset += len(layout)*TitleScreenLayoutEntry.block_size() # Change the offset for the character layouts # The way this normally works is that EarthBound stores the address # of the bank holding the data (0xE1 by default, hence the 0x210000 # offset); the offsets in the table are then prefixed with that # address. However, reallocating the data may have changed its # bank, so we need to manually set it to the new bank address. # In order to change the offset, we are replacing a LDA instruction # which addresses a direct page (0xA5) with a LDA instruction # that treats its operand as the constant to load (0xA9) # See https://wiki.superfamicom.org/snes/show/65816+Reference#instructions. rom[CHARS_LAYOUT_BANK:CHARS_LAYOUT_BANK + 2] = [0xA9, new_bank]
def write_to_rom(self, rom): with Block(size=sum(x.block_size() for x in self.groups)) as block: offset = 0 # Write all the groups to the block, and sprites to rom for i, group in enumerate(self.groups): group.write_sprites_to_free(rom) group.to_block(block, offset) self.group_pointer_table[i] = [offset] offset += group.block_size() # Write the block to rom and correct the group pointers address = to_snes_address(rom.allocate(data=block)) for i in range(self.group_pointer_table.num_rows): self.group_pointer_table[i][0] += address self.group_pointer_table.to_block( block=rom, offset=from_snes_address(GROUP_POINTER_TABLE_OFFSET)) self.palette_table.to_block( block=rom, offset=from_snes_address(PALETTE_TABLE_OFFSET))
def write_to_rom(self, rom): # Write the data table self.bg_table.to_block(block=rom, offset=from_snes_address(BACKGROUND_TABLE_OFFSET)) self.scroll_table.to_block(block=rom, offset=from_snes_address(SCROLL_TABLE_OFFSET)) self.distortion_table.to_block(block=rom, offset=from_snes_address(DISTORTION_TABLE_OFFSET)) # Write graphics and arrangements self.graphics_pointer_table.recreate(num_rows=len(self.backgrounds)) self.arrangement_pointer_table.recreate(num_rows=len(self.backgrounds)) for i, (tileset, color_depth, arrangement) in enumerate(self.backgrounds): with EbCompressibleBlock(size=tileset.block_size(bpp=color_depth)) as compressed_block: tileset.to_block(block=compressed_block, offset=0, bpp=color_depth) compressed_block.compress() tileset_offset = rom.allocate(data=compressed_block) self.graphics_pointer_table[i] = [to_snes_address(tileset_offset)] with EbCompressibleBlock(size=arrangement.block_size()) as compressed_block: arrangement.to_block(block=compressed_block, offset=0) compressed_block.compress() arrangement_offset = rom.allocate(data=compressed_block) self.arrangement_pointer_table[i] = [to_snes_address(arrangement_offset)] graphics_pointer_table_offset = rom.allocate(size=self.graphics_pointer_table.size) self.graphics_pointer_table.to_block(block=rom, offset=graphics_pointer_table_offset) for asm_pointer_offset in GRAPHICS_POINTER_TABLE_ASM_POINTER_OFFSETS: write_asm_pointer(block=rom, offset=asm_pointer_offset, pointer=to_snes_address(graphics_pointer_table_offset)) arrangement_pointer_table_offset = rom.allocate(size=self.arrangement_pointer_table.size) self.arrangement_pointer_table.to_block(block=rom, offset=arrangement_pointer_table_offset) for asm_pointer_offset in ARRANGEMENT_POINTER_TABLE_ASM_POINTER_OFFSETS: write_asm_pointer(block=rom, offset=asm_pointer_offset, pointer=to_snes_address(arrangement_pointer_table_offset)) # Write pals self.palette_pointer_table.recreate(num_rows=len(self.palettes)) for i, palette in enumerate(self.palettes): with Block(32) as block: palette.to_block(block=block, offset=0) palette_offset = rom.allocate(data=block) self.palette_pointer_table[i] = [to_snes_address(palette_offset)] palette_pointer_table_offset = rom.allocate(size=self.palette_pointer_table.size) self.palette_pointer_table.to_block(block=rom, offset=palette_pointer_table_offset) for asm_pointer_offset in PALETTE_POINTER_TABLE_ASM_POINTER_OFFSETS: write_asm_pointer(block=rom, offset=asm_pointer_offset, pointer=to_snes_address(palette_pointer_table_offset))
def write_to_rom(self, rom): if self.data["Enable Skip"]: rom[0x1faae] = 0x5c offset = rom.allocate(size=(10 + 4 * 5 * 5 + 3 * 6 * 5)) rom.write_multi(0x1faaf, to_snes_address(offset), 3) rom[offset:offset+4] = [0x48, 0x08, 0xe2, 0x20] offset += 4 offset = self.write_loader_asm(rom, offset, self.data["Name1"], 5, 0xce, 0x99) offset = self.write_loader_asm(rom, offset, self.data["Name2"], 5, 0x2d, 0x9a) offset = self.write_loader_asm(rom, offset, self.data["Name3"], 5, 0x8c, 0x9a) offset = self.write_loader_asm(rom, offset, self.data["Name4"], 5, 0xeb, 0x9a) offset = self.write_loader_asm(rom, offset, self.data["Pet"], 6, 0x19, 0x98) offset = self.write_loader_asm(rom, offset, self.data["Food"], 6, 0x1f, 0x98) offset = self.write_loader_asm(rom, offset, self.data["Thing"], 6, 0x29, 0x98) if self.data["Enable Summary"]: rom[offset:offset+6] = [0x28, 0x68, 0x5c, 0xc0, 0xfa, 0xc1] else: rom[offset:offset+6] = [0x28, 0x68, 0x5c, 0x05, 0xfd, 0xc1]
def write_to_rom(self, rom): # Deallocate the range of the ROM in which we will write the door destinations. # We deallocate it here instead of specifying it in FREE_RANGES because we want to be sure that this module # get first dibs at writing to this range. This is because door destinations needs to be written to the 0x0F # bank of the EB ROM, and this is one of the few ranges available in that bank. rom.deallocate((0x0F0000, 0x0F58EE)) destination_offsets = dict() empty_area_offset = from_snes_address(rom.allocate(data=[0, 0], can_write_to=not_in_destination_bank)) i = 0 for door_area in self.door_areas: if (door_area is None) or (not door_area): self.pointer_table[i] = [empty_area_offset] else: num_doors = len(door_area) area_offset = rom.allocate(size=(2 + num_doors * 5), can_write_to=not_in_destination_bank) self.pointer_table[i] = [to_snes_address(area_offset)] rom.write_multi(area_offset, num_doors, 2) area_offset += 2 for door in door_area: door.write_to_block(rom, area_offset, destination_offsets) area_offset += 5 i += 1 self.pointer_table.to_block(rom, from_snes_address(0xD00000))
def write_sprites_to_free(self, rom): if self.num_sprites == 0: self.sprite_pointers = [] return unique_sprites, unique_sprite_usages = self.calculate_unique_sprites() # Find a free area offset = rom.allocate(size=sum([x.block_size() for x in unique_sprites.itervalues()]), can_write_to=(lambda y: (y & 0xf) == 0)) self.bank = to_snes_address(offset) >> 16 offset_start = offset & 0xffff # Write each sprite sprite_offsets = dict() for i, (sprite_hash, sprite) in enumerate(unique_sprites.iteritems()): sprite.to_block(rom, offset) sprite_offsets[sprite_hash] = offset offset += sprite.block_size() # Get the pointers for each sprite in the group self.sprite_pointers = [None] * self.num_sprites for i, (sprite_hash, is_mirrored) in enumerate(unique_sprite_usages): self.sprite_pointers[i] = (sprite_offsets[sprite_hash] & 0xffff) | is_mirrored | (self.sprites[i][1] << 1)
def write_to_rom(self, rom): pointer_table_offset = rom.allocate(size=self.table.size) self.table.to_block(rom, pointer_table_offset) write_asm_pointer(rom, self.POINTER_TABLE_ASM_POINTER_OFFSET, to_snes_address(pointer_table_offset))
def write_to_rom(self, rom): rom.deallocate((0xf58ef, 0xf61e5)) pointer_table_offset = rom.allocate(size=self.pointer_table.size, can_write_to=partial(not_in_bank, 0x0f)) self.pointer_table.to_block(rom, pointer_table_offset) rom.write_multi(MAP_MUSIC_ASM_POINTER_OFFSET, to_snes_address(pointer_table_offset), 3)
def to_block(self, block, tileset_asm_pointer_offset, palette_offset): tileset_block_size = self.tileset.block_size(bpp=2) with EbCompressibleBlock(tileset_block_size) as compressed_block: self.tileset.to_block(block=compressed_block, offset=0, bpp=2) compressed_block.compress() tileset_offset = block.allocate(data=compressed_block) write_asm_pointer(block=block, offset=tileset_asm_pointer_offset, pointer=to_snes_address(tileset_offset)) self.palette.to_block(block=block, offset=palette_offset)
def write_to_rom(self, rom): pointer_table_offset = rom.allocate(size=self.table.size) self.table.to_block(rom, pointer_table_offset) write_asm_pointer(rom, self.POINTER_TABLE_ASM_POINTER_OFFSET, to_snes_address(pointer_table_offset))
def to_block(cls, block, offset, value): data_size = cls.data_table_entry.to_block_size(value) data_offset = block.allocate(size=data_size, can_write_to=partial(is_in_bank, cls.bank)) cls.pointer_table_entry.to_block(block, offset, to_snes_address(data_offset)) cls.data_table_entry.to_block(block, data_offset, value)
def to_block(cls, block, offset, value): data_size = cls.data_table_entry.to_block_size(value) data_offset = block.allocate(size=data_size) cls.pointer_table_entry.to_block(block, offset, to_snes_address(data_offset)) cls.data_table_entry.to_block(block, data_offset, value)
def write_town_maps_to_rom(self, rom): log.debug("Writing town maps") for pointer_offset, town_map in zip(TOWN_MAP_POINTER_OFFSETS, self.town_maps): offset = town_map.to_block(rom) rom.write_multi(pointer_offset, to_snes_address(offset), size=4)
def to_block(cls, block, offset, value): if not value: super(TownMapIconPlacementPointerTableEntry, cls).to_block(block, offset, 0) else: pointer = block.allocate(size=sum([cls.table_entry_class.size for x in value]) + 1) super(TownMapIconPlacementPointerTableEntry, cls).to_block(block, offset, to_snes_address(pointer)) for icon_placement in value: cls.table_entry_class.to_block(block, pointer, icon_placement) pointer += cls.table_entry_class.size block[pointer] = 0xff
def write_town_maps_to_rom(self, rom): log.debug("Writing town maps") for pointer_offset, town_map in zip(TOWN_MAP_POINTER_OFFSETS, self.town_maps): offset = town_map.to_block(rom) rom.write_multi(pointer_offset, to_snes_address(offset), size=4)
def write_to_rom(self, rom): # Collisions need to be in the 0xD8 bank rom.deallocate((0x180000, 0x18f05d)) collision_offsets = dict() collision_array = array('B', [0] * 16) log.debug("Writing collisions") for tileset_id, tileset in enumerate(self.tilesets): with Block(len(tileset.collisions) * 2) as collision_table: j = 0 for collision in tileset.collisions: for i, collision_item in enumerate(collision): collision_array[i] = collision_item collision_hash = crc32(collision_array) try: collision_offset = collision_offsets[collision_hash] except KeyError: collision_offset = rom.allocate(data=collision, can_write_to=partial(is_in_bank, 0x18)) & 0xffff collision_offsets[collision_hash] = collision_offset collision_table.write_multi(key=j, item=collision_offset, size=2) j += 2 collision_table_offset = rom.allocate(data=collision_table, can_write_to=partial(not_in_bank, 0x18)) self.collisions_pointer_table[tileset_id] = [to_snes_address(collision_table_offset)] self.collisions_pointer_table.to_block(block=rom, offset=from_snes_address(COLLISIONS_POINTER_TABLE_OFFSET)) # Palettes need to be in the 0xDA bank rom.deallocate((0x1a0000, 0x1afaa6)) # Not sure if this can go farther # Write maps/drawing tilesets associations and map tset pals log.debug("Writing palettes") for map_tileset_id in range(32): # For each map tileset # Find the drawing tileset number for this map tileset for i, tileset in enumerate(self.tilesets): if tileset.has_map_tileset(map_tileset_id): tileset_id = i break else: # TODO Error, this drawing tileset isn't associated tileset_id = 0 self.map_tileset_table[map_tileset_id] = [tileset_id] palettes = tileset.get_palettes_by_map_tileset(map_tileset_id) palettes.sort() # Write the event palettes for map_palette_id, palette in palettes: palette.flag_palette_to_block(rom) # Write the standard palettes palette_offset = rom.allocate(size=0xc0 * len(palettes), can_write_to=partial(is_in_bank, 0x1a)) self.palette_pointer_table[map_tileset_id] = [to_snes_address(palette_offset)] for map_palette_id, palette in palettes: palette.to_block(block=rom, offset=palette_offset) palette_offset += palette.block_size() self.map_tileset_table.to_block(block=rom, offset=from_snes_address(MAP_TILESET_TABLE_OFFSET)) self.palette_pointer_table.to_block(block=rom, offset=from_snes_address(PALETTE_POINTER_TABLE_OFFSET)) for tileset_id, tileset in enumerate(self.tilesets): log.debug("Writing tileset #{}".format(tileset_id)) self.graphics_pointer_table[tileset_id] = [to_snes_address(tileset.minitiles_to_block(rom))] self.arrangements_pointer_table[tileset_id] = [to_snes_address(tileset.arrangements_to_block(rom))] self.graphics_pointer_table.to_block(block=rom, offset=from_snes_address(GRAPHICS_POINTER_TABLE_OFFSET)) self.arrangements_pointer_table.to_block(block=rom, offset=from_snes_address(ARRANGEMENTS_POINTER_TABLE_OFFSET))