def read_from_rom(self, rom): with EbCompressibleBlock() as block: # Read the tileset data block.from_compressed_block( block=rom, offset=from_snes_address( read_asm_pointer(rom, TILESET_POINTER) ) ) self.tileset.from_block(block=block, offset=0, bpp=TILESET_BPP) # Read the arrangement data block.from_compressed_block( block=rom, offset=from_snes_address( read_asm_pointer(rom, ARRANGEMENT_POINTER) ) ) self.arrangement.from_block(block=block, offset=0) # Read the palette data block.from_compressed_block( block=rom, offset=from_snes_address( read_asm_pointer(rom, PALETTE_POINTER) ) ) self.palette.from_block(block=block, offset=0)
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): # 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 read_from_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("Reading font #{} from the ROM".format(FONT_FILENAMES[i])) font.from_block(block=rom, tileset_offset=from_snes_address(self.font_pointer_table[i][1]), character_widths_offset=from_snes_address(self.font_pointer_table[i][0])) self.read_credits_font_from_rom(rom)
def read_town_map_icons_from_rom(self, rom): log.debug("Reading town map icons") graphics_offset = from_snes_address(read_asm_pointer(block=rom, offset=TOWN_MAP_ICON_GRAPHICS_ASM_POINTER_OFFSET)) palette_offset = from_snes_address(read_asm_pointer(block=rom, offset=TOWN_MAP_ICON_PALETTE_ASM_POINTER_OFFSET)) self.town_map_icons.from_block(block=rom, graphics_offset=graphics_offset, arrangement_offset=0, palette_offsets=[palette_offset])
def read_logos_from_rom(self, rom, logos, infos): for info, logo in zip(infos, logos): graphics_offset = from_snes_address(read_asm_pointer(rom, info.graphics_asm_pointer_offsets[0])) arrangement_offset = from_snes_address(read_asm_pointer(rom, info.arrangement_asm_pointer_offsets[0])) palette_offsets = [from_snes_address(read_asm_pointer(rom, x)) for x in info.palette_asm_pointer_offsets] logo.from_block(block=rom, graphics_offset=graphics_offset, arrangement_offset=arrangement_offset, palette_offsets=palette_offsets)
def read_from_rom(self, rom): log.debug("Reading pointer tables") self.graphics_pointer_table.from_block(rom, from_snes_address(GRAPHICS_POINTER_TABLE_OFFSET)) self.arrangements_pointer_table.from_block(rom, from_snes_address(ARRANGEMENTS_POINTER_TABLE_OFFSET)) self.collisions_pointer_table.from_block(rom, from_snes_address(COLLISIONS_POINTER_TABLE_OFFSET)) self.map_tileset_table.from_block(rom, from_snes_address(MAP_TILESET_TABLE_OFFSET)) self.palette_pointer_table.from_block(rom, from_snes_address(PALETTE_POINTER_TABLE_OFFSET)) for i, tileset in enumerate(self.tilesets): log.debug("Reading tileset #{}".format(i)) tileset.minitiles_from_block(rom, from_snes_address(self.graphics_pointer_table[i][0])) tileset.arrangements_from_block(rom, from_snes_address(self.arrangements_pointer_table[i][0])) tileset.collisions_from_block(rom, from_snes_address(self.collisions_pointer_table[i][0])) # Read palettes log.debug("Reading palettes") for i in range(self.map_tileset_table.num_rows): draw_tileset = self.map_tileset_table[i][0] # Estimate the number of palettes for this map tileset, assuming that the palettes are stored contiguously if i == 31: k = 8 else: k = self.palette_pointer_table[i + 1][0] - self.palette_pointer_table[i][0] k /= 0xc0 # Add the palettes to the tileset palette_offset = from_snes_address(self.palette_pointer_table[i][0]) for j in range(k): palette = EbMapPalette() palette.from_block(block=rom, offset=palette_offset) self.tilesets[draw_tileset].add_palette(i, j, palette) palette_offset += 0xc0
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): # 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): 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 read_from_rom(self, rom): self.group_pointer_table.from_block(rom, from_snes_address(GROUP_POINTER_TABLE_OFFSET)) self.palette_table.from_block(rom, from_snes_address(PALETTE_TABLE_OFFSET)) # Load the sprite groups self.groups = [] for i in range(self.group_pointer_table.num_rows): # Note: this assumes that the SPT is written contiguously num_sprites = 8 # Assume that the last group only has 8 sprites if i < self.group_pointer_table.num_rows - 1: num_sprites = (self.group_pointer_table[i + 1][0] - self.group_pointer_table[i][0] - 9) // 2 group = SpriteGroup(num_sprites) group.from_block(rom, from_snes_address(self.group_pointer_table[i][0])) self.groups.append(group)
def test_read_from_project(self): with open(os.path.join(TEST_DATA_DIR, 'summary.txt'), 'r') as summary_file: def resource_open(a, b): return summary_file self.module.read_from_project(resource_open) assert_equal((from_snes_address(0xf10000), from_snes_address(0xf19430)), self.module.used_range) assert_dict_equal( { 'file1.test1': 0xc23456, 'file1.test2': 0xf18cfb, 'file1.label_with_a_very_very_very_very_long_name': 0xf18e4f, 'short_module.entry1': 0xf00d13 }, EbPointer.label_address_map)
def read_from_rom(self, rom): graphics_offset = from_snes_address(read_asm_pointer( block=rom, offset=GRAPHICS_ASM_POINTER_OFFSET)) with EbCompressibleBlock() as compressed_block: compressed_block.from_compressed_block(block=rom, offset=graphics_offset) self.tileset.from_block(block=compressed_block, bpp=4) self.palette.from_block(block=rom, offset=PALETTE_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 read_chars_layouts_from_rom(self, rom): lda_instruction = rom[CHARS_LAYOUT_BANK] chars_layout_pointer_offset = CHARS_LAYOUT_POINTER_OFFSET_DEFAULT # Check if we are dealing with the modified Rom, # If we are, we need to recalculate the offset to the # character layouts if lda_instruction == 0xA9: bank = rom[CHARS_LAYOUT_BANK + 1] chars_layout_pointer_offset = from_snes_address(bank << 16) self.chars_layouts = [[] for _ in range(NUM_CHARS)] for char in range(NUM_CHARS): # Get the location of a character's data offset = chars_layout_pointer_offset + rom.read_multi( CHARS_LAYOUT_TABLE + char*2, 2 ) # Read entries until a final entry is encountered while True: entry = TitleScreenLayoutEntry() entry.from_block(rom, offset) self.chars_layouts[char].append(entry) offset += 5 if entry.is_final(): break
def read_chars_layouts_from_rom(self, rom): lda_instruction = rom[CHARS_LAYOUT_BANK] chars_layout_pointer_offset = CHARS_LAYOUT_POINTER_OFFSET_DEFAULT # Check if we are dealing with the modified Rom, # If we are, we need to recalculate the offset to the # character layouts if lda_instruction == 0xA9: bank = rom[CHARS_LAYOUT_BANK + 1] chars_layout_pointer_offset = from_snes_address(bank << 16) self.chars_layouts = [[] for _ in range(NUM_CHARS)] for char in range(NUM_CHARS): # Get the location of a character's data offset = chars_layout_pointer_offset + rom.read_multi( CHARS_LAYOUT_TABLE + char * 2, 2) # Read entries until a final entry is encountered while True: entry = TitleScreenLayoutEntry() entry.from_block(rom, offset) self.chars_layouts[char].append(entry) offset += 5 if entry.is_final(): break
def read_from_rom(self, rom): self.pointer_table.from_block(rom, from_snes_address(0xD00000)) self.door_areas = [] for i in range(self.pointer_table.num_rows): offset = from_snes_address(self.pointer_table[i][0]) door_area = [] num_doors = rom.read_multi(offset, 2) offset += 2 for j in range(num_doors): door = door_from_block(rom, offset) if door is None: # If we've found an invalid door, stop reading from this door group. # This is expected, since a clean ROM contains some invalid doors. break door_area.append(door) offset += 5 self.door_areas.append(door_area)
def read_from_rom(self, rom): bank = rom[POINTER_TABLE_BANK_OFFSET] - 0xc0 log.debug("Reading data from {:#x} bank".format(bank)) self.pointer_table_entry_class.bank = bank pointer_table_offset = from_snes_address(rom.read_multi(POINTER_TABLE_POINTER_OFFSET, 3)) log.debug("Reading pointer table from {:#x}".format(pointer_table_offset)) self.pointer_table.from_block(rom, pointer_table_offset)
def read_from_rom(self, rom): graphics_offset = from_snes_address( read_asm_pointer(block=rom, offset=GRAPHICS_ASM_POINTER_OFFSET)) with EbCompressibleBlock() as compressed_block: compressed_block.from_compressed_block(block=rom, offset=graphics_offset) self.tileset.from_block(block=compressed_block, bpp=4) self.palette.from_block(block=rom, offset=PALETTE_OFFSET)
def from_block(self, block, tileset_asm_pointer_offset, palette_offset): with EbCompressibleBlock() as compressed_block: compressed_block.from_compressed_block( block=block, offset=from_snes_address( read_asm_pointer(block=block, offset=tileset_asm_pointer_offset))) self.tileset.from_block(block=compressed_block, bpp=2) self.palette.from_block(block=block, offset=palette_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 test_from_snes_address(): assert_equal(from_snes_address(0xc00000), 0) assert_equal(from_snes_address(0xcf1234), 0xf1234) assert_equal(from_snes_address(0xeabc4f), 0x2abc4f) assert_equal(from_snes_address(0xffffff), 0x3fffff) assert_equal(from_snes_address(0x400000), 0x400000) assert_equal(from_snes_address(0x4daa34), 0x4daa34) assert_equal(from_snes_address(0x5fffff), 0x5fffff)
def read_from_rom(self, rom): self.text = self.table_entry.from_block( rom, from_snes_address( read_asm_pointer(block=rom, offset=self.asm_pointer_offset))) for mode in DYNAMIC_CAST_NAME_MODES: if self.get_patch(mode, rom.type).is_applied(rom): self.mode = mode break
def from_block(cls, block, offset): data_offset = from_snes_address(super(TownMapIconPlacementPointerTableEntry, cls).from_block(block, offset)) if not data_offset: return [] value = [] while block[data_offset] != 0xff: value.append(cls.table_entry_class.from_block(block, data_offset)) data_offset += cls.table_entry_class.size return value
def read_from_rom(self, rom): self.table.from_block(rom, offset=from_snes_address( read_animation_table_address(rom))) for index in range(self.table.num_rows): row = self.table[index] offset = row[0] graphics_data_size = row[1] frames = row[2] unknown = row[ 3] # This field appears to be related somewhat to animation speed if graphics_data_size > 0: # Only add animations that have data animation = Animation(graphics_data_size=graphics_data_size, frames=frames, unknown=unknown) animation.from_block(rom, from_snes_address(offset)) self.animations.append(animation)
def test_read_from_project(self): with open(os.path.join(TEST_DATA_DIR, 'summary.txt'), 'r') as summary_file: def resource_open(a, b): return summary_file self.module.read_from_project(resource_open) assert_equal( (from_snes_address(0xf10000), from_snes_address(0xf19430)), self.module.used_range) assert_dict_equal( { 'file1.test1': 0xc23456, 'file1.test2': 0xf18cfb, 'file1.label_with_a_very_very_very_very_long_name': 0xf18e4f, 'short_module.entry1': 0xf00d13 }, EbPointer.label_address_map)
def read_from_rom(self, rom): bank = rom[POINTER_TABLE_BANK_OFFSET] - 0xc0 log.debug("Reading data from {:#x} bank".format(bank)) self.pointer_table_entry_class.bank = bank pointer_table_offset = from_snes_address( rom.read_multi(POINTER_TABLE_POINTER_OFFSET, 3)) log.debug( "Reading pointer table from {:#x}".format(pointer_table_offset)) self.pointer_table.from_block(rom, pointer_table_offset)
def read_from_rom(self, rom): self.swirl_table.from_block( rom, offset=from_snes_address(SWIRL_TABLE_DEFAULT_OFFSET)) if test_swirl_relocated(rom): # Calculate total number of animations from the swirl table total_animations = 0 for i in range(self.swirl_table.num_rows): total_animations += self.swirl_table[i][2] # Read in the offset of the relocated animation table offset = rom.read_multi( RELOCATED_SWIRL_ANIMATION_POINTER_TABLE_POINTERS[0][0], 3) # Pointer size is now 4, so read in 4 bytes at a time all_animation_pointers = [ from_snes_address( rom.read_multi(from_snes_address(offset + (i * 4)), 4)) for i in range(total_animations) ] else: self.pointer_table.from_block( rom, offset=from_snes_address( SWIRL_ANIMATION_POINTER_TABLE_DEFAULT_OFFSET)) all_animation_pointers = [ from_snes_address(self.pointer_table[i][0] | SWIRL_ANIMATION_POINTER_TABLE_BASE) for i in range(self.pointer_table.num_rows) ] self.swirls = [None] * self.swirl_table.num_rows for i in range(self.swirl_table.num_rows): speed = self.swirl_table[i][0] first_animation = self.swirl_table[i][1] number_of_animations = self.swirl_table[i][2] animation_pointers = all_animation_pointers[ first_animation:first_animation + number_of_animations] self.swirls[i] = Swirl(speed=speed) self.swirls[i].frames_from_block(rom, animation_pointers)
def read_from_project(self, resource_open): EbPointer.label_address_map.clear() # Read and parse the summary file with resource_open(CccInterfaceModule.SUMMARY_RESOURCE_NAME, CccInterfaceModule.SUMMARY_RESOURCE_NAME) as \ summary_file: summary_file_lines = summary_file.readlines() if summary_file_lines: compilation_start_address = int(summary_file_lines[7][30:], 16) compilation_end_address = int(summary_file_lines[8][30:], 16) if compilation_start_address != 0xffffffff and compilation_end_address != 0xffffffff: self.used_range = ( from_snes_address(compilation_start_address), from_snes_address(compilation_end_address)) log.debug( "Found range[({:#x},{:#x})] used during compilation". format(self.used_range[0], self.used_range[1])) else: log.debug("Found no space used during compilation") module_name = None in_module_section = False # False = before section, True = in section for line in summary_file_lines: line = line.rstrip() if in_module_section: if line.startswith("-"): in_module_section = False else: label_key = module_name + "." + line.split(' ', 1)[0] label_val = int(line[-6:], 16) EbPointer.label_address_map[label_key] = label_val log.debug( "Adding CCScript label[{}] with address[{:#x}] in module[{}]" .format(label_key, label_val, module_name)) elif line.startswith("-") and module_name is not None: in_module_section = True elif line.startswith("Labels in module "): module_name = line[17:] log.debug( "Found CCScript module[{}]".format(module_name)) log.debug("Found a total of {} CCScript labels".format( len(EbPointer.label_address_map)))
def read_from_rom(self, rom): self.swirl_table.from_block( rom, offset=from_snes_address(SWIRL_TABLE_DEFAULT_OFFSET)) self.pointer_table.from_block( rom, offset=from_snes_address(SWIRL_ANIMATION_POINTER_TABLE_DEFAULT_OFFSET)) all_animation_pointers = [ from_snes_address(self.pointer_table[i][0] | SWIRL_ANIMATION_POINTER_TABLE_BASE) for i in xrange(self.pointer_table.num_rows) ] self.swirls = [None] * self.swirl_table.num_rows for i in xrange(self.swirl_table.num_rows): speed = self.swirl_table[i][0] first_animation = self.swirl_table[i][1] number_of_animations = self.swirl_table[i][2] animation_pointers = all_animation_pointers[first_animation:first_animation+number_of_animations] self.swirls[i] = Swirl(speed=speed) self.swirls[i].frames_from_block(rom, animation_pointers)
def read_from_rom(self, rom): staff_text_offset = from_snes_address(self.read_pointer_from_rom(rom)) self.read_staff_chars_from_assets() log.debug('Reading staff text') i = 0 byte = 0x00 while byte != CONTROL_END_OF_STAFF: byte = rom[staff_text_offset + i] self.data.append(byte) i += 1
def read_from_rom(self, rom): self.group_pointer_table.from_block( rom, from_snes_address(GROUP_POINTER_TABLE_OFFSET)) self.palette_table.from_block(rom, from_snes_address(PALETTE_TABLE_OFFSET)) # Load the sprite groups self.groups = [] for i in range(self.group_pointer_table.num_rows): # Note: this assumes that the SPT is written contiguously num_sprites = 8 # Assume that the last group only has 8 sprites if i < self.group_pointer_table.num_rows - 1: num_sprites = (self.group_pointer_table[i + 1][0] - self.group_pointer_table[i][0] - 9) / 2 group = SpriteGroup(num_sprites) group.from_block(rom, from_snes_address(self.group_pointer_table[i][0])) self.groups.append(group)
def from_block(cls, block, offset): data_offset = from_snes_address( super(TownMapIconPlacementPointerTableEntry, cls).from_block(block, offset)) if not data_offset: return [] value = [] while block[data_offset] != 0xff: value.append(cls.table_entry_class.from_block(block, data_offset)) data_offset += cls.table_entry_class.size return value
def read_from_rom(self, rom): # Read map data map_ptrs_addr = from_snes_address(rom.read_multi(MAP_POINTERS_OFFSET, 3)) map_addrs = [from_snes_address(rom.read_multi(map_ptrs_addr + x * 4, 4)) for x in range(8)] def read_row_data(row_number): offset = map_addrs[row_number % 8] + ((row_number >> 3) << 8) return rom[offset:offset + MAP_WIDTH].to_list() self.tiles = map(read_row_data, range(MAP_HEIGHT)) k = LOCAL_TILESETS_OFFSET for i in range(MAP_HEIGHT >> 3): for j in range(MAP_WIDTH): self.tiles[i << 3][j] |= (rom[k] & 3) << 8 self.tiles[(i << 3) | 1][j] |= ((rom[k] >> 2) & 3) << 8 self.tiles[(i << 3) | 2][j] |= ((rom[k] >> 4) & 3) << 8 self.tiles[(i << 3) | 3][j] |= ((rom[k] >> 6) & 3) << 8 self.tiles[(i << 3) | 4][j] |= (rom[k + 0x3000] & 3) << 8 self.tiles[(i << 3) | 5][j] |= ((rom[k + 0x3000] >> 2) & 3) << 8 self.tiles[(i << 3) | 6][j] |= ((rom[k + 0x3000] >> 4) & 3) << 8 self.tiles[(i << 3) | 7][j] |= ((rom[k + 0x3000] >> 6) & 3) << 8 k += 1 # Read sector data self.sector_tilesets_palettes_table.from_block(rom, from_snes_address(SECTOR_TILESETS_PALETTES_TABLE_OFFSET)) self.sector_music_table.from_block(rom, from_snes_address(SECTOR_MUSIC_TABLE_OFFSET)) self.sector_misc_table.from_block(rom, from_snes_address(SECTOR_MISC_TABLE_OFFSET)) self.sector_town_map_table.from_block(rom, from_snes_address(SECTOR_TOWN_MAP_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): # Write map data map_ptrs_addr = from_snes_address(rom.read_multi(MAP_POINTERS_OFFSET, 3)) map_addrs = [from_snes_address(rom.read_multi(map_ptrs_addr + x * 4, 4)) for x in range(8)] for i in range(MAP_HEIGHT): offset = map_addrs[i % 8] + ((i >> 3) << 8) rom[offset:offset + MAP_WIDTH] = [x & 0xff for x in self.tiles[i]] k = LOCAL_TILESETS_OFFSET for i in range(MAP_HEIGHT >> 3): for j in range(MAP_WIDTH): c = ((self.tiles[i << 3][j] >> 8) | ((self.tiles[(i << 3) | 1][j] >> 8) << 2) | ((self.tiles[(i << 3) | 2][j] >> 8) << 4) | ((self.tiles[(i << 3) | 3][j] >> 8) << 6)) rom[k] = c c = ((self.tiles[(i << 3) | 4][j] >> 8) | ((self.tiles[(i << 3) | 5][j] >> 8) << 2) | ((self.tiles[(i << 3) | 6][j] >> 8) << 4) | ((self.tiles[(i << 3) | 7][j] >> 8) << 6)) rom[k + 0x3000] = c k += 1 # Write sector data self.sector_tilesets_palettes_table.to_block(rom, from_snes_address(SECTOR_TILESETS_PALETTES_TABLE_OFFSET)) self.sector_music_table.to_block(rom, from_snes_address(SECTOR_MUSIC_TABLE_OFFSET)) self.sector_misc_table.to_block(rom, from_snes_address(SECTOR_MISC_TABLE_OFFSET)) self.sector_town_map_table.to_block(rom, from_snes_address(SECTOR_TOWN_MAP_TABLE_OFFSET))
def read_from_rom(self, rom): self.bg_table.from_block(block=rom, offset=from_snes_address(BACKGROUND_TABLE_OFFSET)) self.scroll_table.from_block(block=rom, offset=from_snes_address(SCROLL_TABLE_OFFSET)) self.distortion_table.from_block(block=rom, offset=from_snes_address(DISTORTION_TABLE_OFFSET)) self.graphics_pointer_table.from_block( block=rom, offset=from_snes_address(read_asm_pointer(block=rom, offset=GRAPHICS_POINTER_TABLE_ASM_POINTER_OFFSETS[0]))) self.arrangement_pointer_table.from_block( block=rom, offset=from_snes_address(read_asm_pointer(block=rom, offset=ARRANGEMENT_POINTER_TABLE_ASM_POINTER_OFFSETS[0]))) self.palette_pointer_table.from_block( block=rom, offset=from_snes_address(read_asm_pointer(block=rom, offset=PALETTE_POINTER_TABLE_ASM_POINTER_OFFSETS[0]))) self.backgrounds = [None for i in range(self.graphics_pointer_table.num_rows)] self.palettes = [None for i in range(self.palette_pointer_table.num_rows)] for i in range(self.bg_table.num_rows): graphics_id = self.bg_table[i][0] color_depth = self.bg_table[i][2] if self.backgrounds[graphics_id] is None: # Max tiles used in rom: 421 (2bpp) 442 (4bpp) tileset = EbGraphicTileset(num_tiles=512, tile_width=8, tile_height=8) with EbCompressibleBlock() as compressed_block: compressed_block.from_compressed_block( block=rom, offset=from_snes_address(self.graphics_pointer_table[graphics_id][0])) tileset.from_block(compressed_block, offset=0, bpp=color_depth) arrangement = EbTileArrangement(width=32, height=32) with EbCompressibleBlock() as compressed_block: compressed_block.from_compressed_block( block=rom, offset=from_snes_address(self.arrangement_pointer_table[graphics_id][0])) arrangement.from_block(block=compressed_block, offset=0) self.backgrounds[graphics_id] = (tileset, color_depth, arrangement) palette_id = self.bg_table[i][1] if self.palettes[palette_id] is None: palette = EbPalette(num_subpalettes=1, subpalette_length=16) palette.from_block(block=rom, offset=from_snes_address(self.palette_pointer_table[palette_id][0])) self.palettes[palette_id] = palette
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 read_from_rom(self, rom): self.swirl_table.from_block( rom, offset=from_snes_address(SWIRL_TABLE_DEFAULT_OFFSET)) self.pointer_table.from_block( rom, offset=from_snes_address( SWIRL_ANIMATION_POINTER_TABLE_DEFAULT_OFFSET)) all_animation_pointers = [ from_snes_address(self.pointer_table[i][0] | SWIRL_ANIMATION_POINTER_TABLE_BASE) for i in xrange(self.pointer_table.num_rows) ] self.swirls = [None] * self.swirl_table.num_rows for i in xrange(self.swirl_table.num_rows): speed = self.swirl_table[i][0] first_animation = self.swirl_table[i][1] number_of_animations = self.swirl_table[i][2] animation_pointers = all_animation_pointers[ first_animation:first_animation + number_of_animations] self.swirls[i] = Swirl(speed=speed) self.swirls[i].frames_from_block(rom, animation_pointers)
def read_from_rom(self, rom): self.swirl_table.from_block( rom, offset=from_snes_address(SWIRL_TABLE_DEFAULT_OFFSET)) if test_swirl_relocated(rom): # Calculate total number of animations from the swirl table total_animations = 0 for i in range(self.swirl_table.num_rows): total_animations += self.swirl_table[i][2] # Read in the offset of the relocated animation table offset = rom.read_multi(RELOCATED_SWIRL_ANIMATION_POINTER_TABLE_POINTERS[0][0], 3) # Pointer size is now 4, so read in 4 bytes at a time all_animation_pointers = [ from_snes_address(rom.read_multi(from_snes_address(offset + (i * 4)), 4)) for i in range(total_animations) ] else: self.pointer_table.from_block( rom, offset=from_snes_address(SWIRL_ANIMATION_POINTER_TABLE_DEFAULT_OFFSET)) all_animation_pointers = [ from_snes_address(self.pointer_table[i][0] | SWIRL_ANIMATION_POINTER_TABLE_BASE) for i in range(self.pointer_table.num_rows) ] self.swirls = [None] * self.swirl_table.num_rows for i in range(self.swirl_table.num_rows): speed = self.swirl_table[i][0] first_animation = self.swirl_table[i][1] number_of_animations = self.swirl_table[i][2] animation_pointers = all_animation_pointers[first_animation:first_animation+number_of_animations] self.swirls[i] = Swirl(speed=speed) self.swirls[i].frames_from_block(rom, animation_pointers)
def read_from_project(self, resource_open): EbPointer.label_address_map.clear() # Read and parse the summary file with resource_open(CccInterfaceModule.SUMMARY_RESOURCE_NAME, CccInterfaceModule.SUMMARY_RESOURCE_NAME, True) as \ summary_file: summary_file_lines = summary_file.readlines() if summary_file_lines: compilation_start_address = int(summary_file_lines[7][30:], 16) compilation_end_address = int(summary_file_lines[8][30:], 16) if compilation_start_address != 0xffffffff and compilation_end_address != 0xffffffff: self.used_range = (from_snes_address(compilation_start_address), from_snes_address(compilation_end_address)) log.debug("Found range[({:#x},{:#x})] used during compilation".format(self.used_range[0], self.used_range[1])) else: log.debug("Found no space used during compilation") module_name = None in_module_section = False # False = before section, True = in section for line in summary_file_lines: line = line.rstrip() if in_module_section: if line.startswith("-"): in_module_section = False else: label_key = module_name + "." + line.split(' ', 1)[0] label_val = int(line[-6:], 16) EbPointer.label_address_map[label_key] = label_val log.debug("Adding CCScript label[{}] with address[{:#x}] in module[{}]".format( label_key, label_val, module_name)) elif line.startswith("-") and module_name is not None: in_module_section = True elif line.startswith("Labels in module "): module_name = line[17:] log.debug("Found CCScript module[{}]".format(module_name)) log.debug("Found a total of {} CCScript labels".format(len(EbPointer.label_address_map)))
def read_from_rom(self, rom): with EbCompressibleBlock() as compressed_block: compressed_block.from_compressed_block( block=rom, offset=from_snes_address(read_asm_pointer(rom, GRAPHICS_1_ASM_POINTER_OFFSET))) self.graphics_1.from_block(block=compressed_block, bpp=2) with EbCompressibleBlock() as compressed_block: compressed_block.from_compressed_block( block=rom, offset=from_snes_address(read_asm_pointer(rom, GRAPHICS_2_ASM_POINTER_OFFSET))) self.graphics_2.from_block(block=compressed_block, bpp=2) # Read palettes offset = FLAVOR_PALETTES_OFFSET for palette in self.flavor_palettes: palette.from_block(block=rom, offset=offset) offset += 64 # Read names for asm_pointer_offset in FLAVOR_NAME_ASM_POINTER_OFFSETS: self.flavor_names[asm_pointer_offset] = FLAVOR_NAME_ENTRY.from_block( block=rom, offset=from_snes_address(read_asm_pointer(block=rom, offset=asm_pointer_offset)))
def read_from_rom(self, rom): self.enemy_config_table.from_block(block=rom, offset=from_snes_address(ENEMY_CONFIGURATION_TABLE_DEFAULT_OFFSET)) self.enemy_group_bg_table.from_block(block=rom, offset=from_snes_address(ENEMY_GROUP_BACKGROUND_TABLE_DEFAULT_OFFSET)) # Read the sprites log.debug("Reading battle sprites") self.graphics_pointer_table.from_block( rom, from_snes_address(read_asm_pointer(block=rom, offset=GRAPHICS_POINTER_TABLE_ASM_POINTER_OFFSET))) self.battle_sprites = [] for i in range(self.graphics_pointer_table.num_rows): with EbCompressibleBlock() as compressed_block: compressed_block.from_compressed_block( block=rom, offset=from_snes_address(self.graphics_pointer_table[i][0])) sprite = EbBattleSprite() sprite.from_block(block=compressed_block, offset=0, size=self.graphics_pointer_table[i][1]) self.battle_sprites.append(sprite) # Determine how many palettes there are num_palettes = 0 for i in range(self.enemy_config_table.num_rows): num_palettes = max(num_palettes, self.enemy_config_table[i][14]) num_palettes += 1 # Read the palettes log.debug("Reading palettes") palettes_offset = from_snes_address(read_asm_pointer(block=rom, offset=PALETTES_ASM_POINTER_OFFSET)) self.palettes = [] for i in range(num_palettes): palette = EbPalette(num_subpalettes=1, subpalette_length=16) palette.from_block(block=rom, offset=palettes_offset) self.palettes.append(palette) palettes_offset += palette.block_size() # Read the groups log.debug("Reading enemy groups") self.enemy_group_table.from_block(rom, from_snes_address(ENEMY_GROUP_TABLE_DEFAULT_OFFSET)) self.enemy_groups = [] for i in range(self.enemy_group_table.num_rows): group = [] group_offset = from_snes_address(self.enemy_group_table[i][0]) while rom[group_offset] != 0xff: group.append(EnemyGroupTableEntry.from_block(block=rom, offset=group_offset)) group_offset += EnemyGroupTableEntry.size self.enemy_groups.append(group)
def from_block(self, rom, offset): header_data = SpriteGroupHeaderTableEntry.from_block(rom, offset) self.height = header_data[0] self.width = header_data[1] >> 4 self.size = header_data[2] self.palette = (header_data[3] >> 1) & 0x7 self.collision_ns_w = header_data[4] self.collision_ns_h = header_data[5] self.collision_ew_w = header_data[6] self.collision_ew_h = header_data[7] bank = header_data[8] << 16 self.sprites = [[EbRegularSprite(), False] for i in range(self.num_sprites)] i = offset + 9 for spff in self.sprites: ptr = bank | rom.read_multi(i, 2) spff[1] = (ptr & 2) != 0 spff[0].from_block(rom, self.width * 8, self.height * 8, from_snes_address(ptr & 0xfffffc)) if (ptr & 1) != 0: spff[0].flip_horizontally() i += 2
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 read_from_rom(self, rom): for offset, table in self.tables.iteritems(): table.from_block(rom, from_snes_address(offset))
def write_to_rom(self, rom): self.group_pointer_table.to_block(rom, from_snes_address(GROUP_POINTER_TABLE_OFFSET)) self.group_placement_table.to_block(rom, from_snes_address(GROUP_PLACEMENT_TABLE_OFFSET))