def read_from_project(self, resource_open): # Read graphics. Just use the first of each image. with resource_open("WindowGraphics/Windows1_0", "png") as image_file: image = open_indexed_image(image_file) self.graphics_1.from_image(image=image, arrangement=ARRANGEMENT_1, palette=self.flavor_palettes[0]) with resource_open("WindowGraphics/Windows2_0", "png") as image_file: image = open_indexed_image(image_file) self.graphics_2.from_image(image=image, arrangement=ARRANGEMENT_2, palette=self.flavor_palettes[0].get_subpalette(7)) # Read pals from Windows1 of each flavor. # Read subpal 7 from Windows2 of each flavor. for i, palette in enumerate(self.flavor_palettes): # Read all the palette data from Windows1 with resource_open("WindowGraphics/Windows1_" + str(i), "png") as image_file: image = open_indexed_image(image_file) palette.from_image(image=image) with resource_open("WindowGraphics/Windows2_" + str(i), "png") as image_file: image = open_indexed_image(image_file) palette_data = image.getpalette() m = 0 for k in range(4): palette[7, k].from_tuple((palette_data[m], palette_data[m + 1], palette_data[m + 2])) m += 3 # Read names with resource_open("WindowGraphics/flavor_names", "txt", True) as f: for asm_pointer_offset in FLAVOR_NAME_ASM_POINTER_OFFSETS: name = f.readline()[:-1] self.flavor_names[asm_pointer_offset] = FLAVOR_NAME_ENTRY.from_yml_rep(name)
def read_background_data_from_project(self, resource_open): # Load the background reference image # The image's arrangement, tileset and palette will be used for the # animation frames with resource_open(BG_REFERENCE_PATH, "png") as f: image = open_indexed_image(f) self.bg_arrangement.from_image( image, self.bg_tileset, self.bg_palette ) # Read the background animated frames for frame in range(NUM_ANIM_FRAMES): # Create temporary structures used to check consistency between # frames tileset = EbGraphicTileset(BG_NUM_TILES, TILE_WIDTH, TILE_HEIGHT) arrangement = EbTileArrangement( BG_ARRANGEMENT_WIDTH, BG_ARRANGEMENT_HEIGHT ) palette = EbPalette(NUM_SUBPALETTES, BG_SUBPALETTE_LENGTH) # Read one frame's image data with resource_open(BG_FRAMES_PATH.format(frame), "png") as f: image = open_indexed_image(f) arrangement.from_image(image, tileset, palette) # Make sure each frame's tileset and arrangement is identical # The background palette is checked only if it isn't the fake # palette used for the first few frames if frame >= CHARS_NUM_ANIM_SUBPALETTES: # Get the background animated subpalette from the background # palette colors = palette[0, BG_ANIM_SLICE] self.bg_anim_palette.subpalettes[ frame - CHARS_NUM_ANIM_SUBPALETTES ] = colors palette[0, BG_ANIM_SLICE] = self.bg_palette[ 0, BG_ANIM_SLICE ] if self.bg_palette != palette: log.warn( "Palette from background frame {} does not match " "reference.".format(frame) ) if self.bg_tileset != tileset: log.warn( "Tileset from background frame {} does not match " "reference.".format(frame) ) if self.bg_arrangement != arrangement: log.warn( "Arrangement from background frame {} does not match " "reference.".format(frame) )
def read_from_project(self, resource_open): with resource_open(NESS_DEATH_SCREEN_PATH, "png") as f: image = open_indexed_image(f) self.arrangement.from_image(image, self.ness_tileset, self.palette, True, False) with resource_open(JEFF_DEATH_SCREEN_PATH, "png") as f: image = open_indexed_image(f) self.jeff_tileset.from_image(image, self.arrangement, self.palette) with resource_open(DEATH_SCREEN_SUBPALETTES_PATH, "yml", True) as f: subpalettes = yml_load(f) for subpalette, tiles in subpalettes.items(): for x, y in tiles: self.arrangement[x, y].subpalette = subpalette
def read_gfx_from_project(self, obj, resource_open): with resource_open(obj.path(), 'png') as image_file: image = open_indexed_image(image_file) palette = EbPalette(num_subpalettes=1, subpalette_length=4) palette.from_image(image) obj.palettes[0] = palette obj.from_image(image, obj.cast_arrangement())
def read_from_project(self, resource_open): with resource_open("sprite_group_palettes", "yml") as f: self.palette_table.from_yml_file(f) with resource_open("sprite_groups", "yml") as f: input = yml_load(f) num_groups = len(input) self.groups = [] for i in range(num_groups): group = SpriteGroup(16) group.from_yml_rep(input[i]) palette = EbPalette(1, 16) with resource_open("SpriteGroups/" + str(i).zfill(3), "png") as f2: image = open_indexed_image(f2) group.from_image(image) palette.from_image(image) del image self.groups.append(group) # Assign the palette number to the sprite for j in range(8): if palette.list()[3:] == self.palette_table[j][0].list( )[3:]: group.palette = j break else: raise CoilSnakeError("Sprite Group #" + str(i).zfill(3) + " uses an invalid palette")
def read_town_maps_from_project(self, resource_open): for resource_name, town_map in zip(TOWN_MAP_RESOURCE_NAMES, self.town_maps): log.info("- Reading {}".format(resource_name)) with resource_open(resource_name, "png") as image_file: image = open_indexed_image(image_file) town_map.from_image(image)
def from_files(self, image_file, widths_file, image_format="png", widths_format="yml"): image = open_indexed_image(image_file) if self.num_characters == 96: self.tileset.from_image(image, _FONT_IMAGE_ARRANGEMENT_96, FONT_IMAGE_PALETTE) elif self.num_characters == 128: self.tileset.from_image(image, _FONT_IMAGE_ARRANGEMENT_128, FONT_IMAGE_PALETTE) del image if widths_format == "yml": widths_dict = yml_load(widths_file) self.character_widths = [widths_dict[i] for i in range(self.tileset.num_tiles_maximum)]
def upgrade_project(self, old_version, new_version, rom, resource_open_r, resource_open_w, resource_delete): if old_version == new_version: return self.read_from_rom(rom) self.write_to_project(resource_open_w) if old_version == 9: with resource_open_r(OLD_DEATH_SCREEN_PATH, "png") as old: with resource_open_w(NESS_DEATH_SCREEN_PATH, "png") as new: image = open_indexed_image(old) image.save(new) resource_delete(OLD_DEATH_SCREEN_PATH)
def upgrade_project(self, old_version, new_version, rom, resource_open_r, resource_open_w, resource_delete): if old_version == new_version: return elif old_version == 5: # Expand all the fonts from 96 characters to 128 characters for i, font in enumerate(self.fonts): log.debug("Expanding font #{}".format(FONT_FILENAMES[i])) image_resource_name = "Fonts/" + FONT_FILENAMES[i] widths_resource_name = "Fonts/" + FONT_FILENAMES[i] + "_widths" new_image_w, new_image_h = font.image_size() # Expand the image with resource_open_r(image_resource_name, 'png') as image_file: image = open_indexed_image(image_file) expanded_image = Image.new("P", (new_image_w, new_image_h), None) for y in xrange(new_image_h): for x in xrange(new_image_w): expanded_image.putpixel((x, y), 1) FONT_IMAGE_PALETTE.to_image(expanded_image) expanded_image.paste(image, (0, 0)) with resource_open_w(image_resource_name, 'png') as image_file2: expanded_image.save(image_file2, "png") # Expand the widths with resource_open_r(widths_resource_name, "yml") as widths_file: widths_dict = yml_load(widths_file) for character_id in xrange(96, 128): if character_id not in widths_dict: widths_dict[character_id] = 0 with resource_open_w(widths_resource_name, "yml") as widths_file: yml_dump(widths_dict, widths_file, default_flow_style=False) self.upgrade_project(6, new_version, rom, resource_open_r, resource_open_w, resource_delete) elif old_version <= 2: # The credits font was a new feature in version 3 self.read_credits_font_from_rom(rom) self.write_credits_font_to_project(resource_open_w) self.upgrade_project(3, new_version, rom, resource_open_r, resource_open_w, resource_delete) else: self.upgrade_project(old_version + 1, new_version, rom, resource_open_r, resource_open_w, resource_delete)
def read_from_project(self, resource_open): with resource_open("bg_data_table", "yml", True) as f: self.bg_table.from_yml_file(f) with resource_open("bg_scrolling_table", "yml", True) as f: self.scroll_table.from_yml_file(f) with resource_open("bg_distortion_table", "yml", True) as f: self.distortion_table.from_yml_file(f) self.backgrounds = [] self.palettes = [] for i in range(self.bg_table.num_rows): new_color_depth = self.bg_table[i][2] with resource_open("BattleBGs/" + str(i).zfill(3), "png") as f: image = open_indexed_image(f) new_palette = EbPalette(num_subpalettes=1, subpalette_length=16) new_tileset = EbGraphicTileset(num_tiles=512, tile_width=8, tile_height=8) new_arrangement = EbTileArrangement(width=32, height=32) new_arrangement.from_image(image, new_tileset, new_palette) for j, (tileset, color_depth, arrangement) in enumerate(self.backgrounds): if (color_depth == new_color_depth) \ and (tileset == new_tileset) \ and (arrangement == new_arrangement): self.bg_table[i][0] = j break else: self.bg_table[i][0] = len(self.backgrounds) self.backgrounds.append( (new_tileset, new_color_depth, new_arrangement)) for j, palette in enumerate(self.palettes): if palette == new_palette: self.bg_table[i][1] = j break else: self.bg_table[i][1] = len(self.palettes) self.palettes.append(new_palette)
def read_from_project(self, resource_open): with resource_open('Animations/animations', 'yml', True) as f: animation_data = yml_load(f) for animation_id in animation_data: frames = animation_data[animation_id]['frames'] unknown = animation_data[animation_id]['unknown'] animation = Animation(frames=frames, unknown=unknown) self.animations.append(animation) for frame_id in range(frames): with resource_open( 'Animations/{}/{}'.format(animation_id, str(frame_id).zfill(3)), 'png') as f: image = open_indexed_image(f) try: animation.add_frame_from_image(image, frame_id) except Exception as e: message = 'Encountered error while reading frame #{} of Animation #{}'.format( frame_id, animation_id) raise CoilSnakeTraceableError(message, e)
def read_from_project(self, resource_open): with resource_open("Swirls/swirls", "yml") as f: swirl_data = yml_load(f) self.swirls = [Swirl() for i in xrange(self.swirl_table.num_rows)] for swirl_id, swirl in enumerate(self.swirls): log.debug("Reading Swirl #{}".format(swirl_id)) speed = swirl_data[swirl_id]["speed"] num_frames = swirl_data[swirl_id]["frames"] swirl.speed = speed for frame_id in range(num_frames): with resource_open( "Swirls/{}/{}".format(swirl_id, str(frame_id).zfill(3)), "png") as f: image = open_indexed_image(f) try: swirl.add_frame_from_image(image) except Exception as e: message = "Encountered error while reading frame #{} of swirl #{}".format( frame_id, swirl_id) raise CoilSnakeTraceableError(message, e)
def read_chars_data_from_project(self, resource_open): # Read the characters positions with resource_open(CHARS_POSITIONS_PATH, "yml") as f: chars_positions = yml_load(f) # Read the characters animated frames self.chars_tileset = None self.chars_anim_palette = EbPalette(CHARS_NUM_ANIM_SUBPALETTES, ANIM_SUBPALETTE_LENGTH) original_tileset = None for p in xrange(CHARS_NUM_ANIM_SUBPALETTES): # Read one of the animation frames with resource_open(CHARS_FRAMES_PATH.format(p), "png") as f: # Create temporary structures to hold the data image = open_indexed_image(f) arrangement = EbTileArrangement(image.width / TILE_WIDTH, image.height / TILE_HEIGHT) tileset = EbGraphicTileset(CHARS_NUM_TILES, TILE_WIDTH, TILE_HEIGHT) anim_subpalette = EbPalette(NUM_SUBPALETTES, ANIM_SUBPALETTE_LENGTH) arrangement.from_image(image, tileset, anim_subpalette, True) # Add the characters animation subpalette for i in xrange(ANIM_SUBPALETTE_LENGTH): self.chars_anim_palette[p, i] = anim_subpalette[0, i] # Add the characters tileset if not already set, otherwise # ensure that it the current tileset is identical if not self.chars_tileset: original_tileset = tileset self.chars_tileset = EbGraphicTileset(CHARS_NUM_TILES, TILE_WIDTH, TILE_HEIGHT) self.chars_tileset.tiles = [[[0 for _ in xrange(TILE_HEIGHT)] for _ in xrange(TILE_WIDTH)] for _ in xrange(CHARS_NUM_TILES)] unused_tiles = set(xrange(CHARS_NUM_TILES)) # Set the new character layouts self.chars_layouts = [[] for _ in xrange(NUM_CHARS)] for c, data in chars_positions.items(): # Get the data from the YAML file x = int(data['x'] / TILE_WIDTH) y = int(data['y'] / TILE_HEIGHT) width = int(data['width'] / TILE_WIDTH) height = int(data['height'] / TILE_HEIGHT) x_offset = data['top_left_offset']['x'] y_offset = data['top_left_offset']['y'] unknown = data['unknown'] # Generate a list of all tiles must be visited # Where possible, we try to generate a multi tile (4 tiles # stored as one); otherwise, bordering tiles that are # visited will all be single tiles. l = [(i, j) for i in xrange(0, width, 2) for j in xrange(0, height, 2)] if width % 2 == 1: l.extend([(width - 1, j) for j in xrange(1, height, 2)]) if height % 2 == 1: l.extend([(i, height - 1) for i in xrange(1, width, 2)]) # Generate the new reduced tileset for i, j in l: # Put the tile in the new tileset o_tile = arrangement[x + i, y + j].tile n_tile = unused_tiles.pop() self.chars_tileset.tiles[n_tile] = tileset[o_tile] entry = TitleScreenLayoutEntry(i * 8 + x_offset, j * 8 + y_offset, n_tile, 0, unknown) # Create a multi entry if possible to save space if i < width - 1 and j < height - 1: entry.set_single(True) o_tile_r = arrangement[x + i + 1, y + j].tile o_tile_d = arrangement[x + i, y + j + 1].tile o_tile_dr = arrangement[x + i + 1, y + j + 1].tile n_tile_r = n_tile + 1 n_tile_d = n_tile + 16 n_tile_dr = n_tile + 17 unused_tiles.difference_update( (n_tile_r, n_tile_d, n_tile_dr)) self.chars_tileset.tiles[n_tile_r] = \ tileset[o_tile_r] self.chars_tileset.tiles[n_tile_d] = \ tileset[o_tile_d] self.chars_tileset.tiles[n_tile_dr] = \ tileset[o_tile_dr] self.chars_layouts[c].append(entry) self.chars_layouts[c][-1].set_final(True) elif original_tileset != tileset: log.warn("Tileset from characters frame {} does not match " "tileset from characters frame 0.".format(p)) # Read the initial characters palette with resource_open(CHARS_INITIAL_PATH, "png") as f: image = open_indexed_image(f) arrangement = EbTileArrangement(image.width / TILE_WIDTH, image.height / TILE_HEIGHT) tileset = EbGraphicTileset(CHARS_NUM_TILES, TILE_WIDTH, TILE_HEIGHT) self.chars_palette = EbPalette(NUM_SUBPALETTES, ANIM_SUBPALETTE_LENGTH) arrangement.from_image(image, tileset, self.chars_palette)
def from_files(self, image_file, image_format="png"): image = open_indexed_image(image_file) self.palette.from_image(image) self.tileset.from_image(image, _CREDITS_PREVIEW_ARRANGEMENT, self.palette) del image
def read_logos_from_project(self, resource_open, logos, infos): for info, logo in zip(infos, logos): log.info("- Reading " + info.name) with resource_open(info.name, "png") as image_file: image = open_indexed_image(image_file) logo.from_image(image)
def read_town_map_icons_from_project(self, resource_open): log.info("- Reading town map icons") with resource_open("TownMaps/icons", "png") as image_file: image = open_indexed_image(image_file) self.town_map_icons.from_image( image=image, arrangement=TOWN_MAP_ICON_PREVIEW_ARRANGEMENT)
def read_from_project(self, resource_open): with resource_open("Logos/SoundStone", "png") as image_file: image = open_indexed_image(image_file) self.palette.from_image(image) self.tileset.from_image(image, SOUND_STONE_ARRANGEMENT, self.palette)
def read_from_project(self, resource_open): with resource_open("enemy_configuration_table", "yml") as f: self.enemy_config_table.from_yml_file(f) # Read the sprites and palettes self.battle_sprites = [] self.palettes = [] sprite_hashes = dict() num_sprites = 0 palette_hashes = dict() num_palettes = 0 for i in range(self.enemy_config_table.num_rows): battle_sprite = EbBattleSprite() palette = EbPalette(num_subpalettes=1, subpalette_length=16) try: with resource_open("BattleSprites/" + str(i).zfill(3), "png") as f: image = open_indexed_image(f) battle_sprite.from_image(image) palette.from_image(image) del image except IOError: # No battle sprite self.enemy_config_table[i][4] = 0 self.enemy_config_table[i][14] = 0 continue sprite_hash = battle_sprite.hash() try: self.enemy_config_table[i][4] = sprite_hashes[sprite_hash] + 1 except KeyError: self.enemy_config_table[i][4] = num_sprites + 1 sprite_hashes[sprite_hash] = num_sprites self.battle_sprites.append(battle_sprite) num_sprites += 1 palette_hash = palette.hash() try: self.enemy_config_table[i][14] = palette_hashes[palette_hash] except KeyError: self.enemy_config_table[i][14] = num_palettes palette_hashes[palette_hash] = num_palettes self.palettes.append(palette) num_palettes += 1 # Read the groups with resource_open("enemy_groups", "yml") as f: self.enemy_group_table.from_yml_file(f) with resource_open("enemy_groups", "yml") as f: self.enemy_group_bg_table.from_yml_file(f) with resource_open("enemy_groups", "yml") as f: self.enemy_groups = [] enemy_groups_yml_rep = yml_load(f) for entry in enemy_groups_yml_rep.itervalues(): enemy_group = entry["Enemies"] if type(enemy_group) == dict: enemy_group = [enemy_group[x] for x in sorted(enemy_group.keys())] group = [EnemyGroupTableEntry.from_yml_rep(x) for x in enemy_group] self.enemy_groups.append(group)