def test_from_image_8x8_1bpp(self): palette = EbPalette(1, 2) palette[0, 0].from_tuple((0xff, 0xff, 0xff)) palette[0, 1].from_tuple((0x0, 0x0, 0x0)) arrangement = EbTileArrangement(width=2, height=2) arrangement[0, 0].tile = 0 arrangement[1, 0].tile = 2 arrangement[0, 1].tile = 1 arrangement[1, 1].tile = 3 tileset = EbGraphicTileset(num_tiles=4, tile_width=8, tile_height=8) tileset.from_image(self.tile_8x8_2bpp_img, arrangement=arrangement, palette=palette) assert_list_equal(tileset[0], [[0] * 8] * 8) assert_list_equal(tileset[2], [[1] * 8] * 8) assert_list_equal(tileset[1], [[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]]) assert_list_equal(tileset[3], [[0, 1, 1, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1, 1, 1], [0, 1, 0, 0, 0, 0, 1, 1], [1, 1, 0, 0, 1, 0, 1, 1], [1, 1, 0, 1, 0, 0, 1, 1], [1, 1, 0, 0, 0, 0, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 0]])
class EbFont(object): def __init__(self, num_characters=96, tile_width=16, tile_height=8): self.num_characters = num_characters self.tileset = EbGraphicTileset(num_tiles=num_characters, tile_width=tile_width, tile_height=tile_height) self.character_widths = None def from_block(self, block, tileset_offset, character_widths_offset): self.tileset.from_block(block=block, offset=tileset_offset, bpp=1) self.character_widths = block[character_widths_offset:character_widths_offset + self.num_characters].to_list() def to_block(self, block, tileset_offset, character_widths_offset): self.tileset.to_block(block=block, offset=tileset_offset, bpp=1) block[character_widths_offset:character_widths_offset + self.num_characters] = self.character_widths def to_files(self, image_file, widths_file, image_format="png", widths_format="yml"): image = _FONT_IMAGE_ARRANGEMENT.image(self.tileset, _FONT_IMAGE_PALETTE) image.save(image_file, image_format) del image character_widths_dict = dict(enumerate(self.character_widths)) if widths_format == "yml": yml_dump(character_widths_dict, widths_file, default_flow_style=False) def from_files(self, image_file, widths_file, image_format="png", widths_format="yml"): image = open_indexed_image(image_file) self.tileset.from_image(image, _FONT_IMAGE_ARRANGEMENT, _FONT_IMAGE_PALETTE) del image if widths_format == "yml": widths_dict = yml_load(widths_file) self.character_widths = map(lambda i: widths_dict[i], range(self.tileset.num_tiles_maximum))
class EbCreditsFont(object): def __init__(self): self.tileset = EbGraphicTileset(num_tiles=192, tile_width=8, tile_height=8) self.palette = EbPalette(num_subpalettes=2, subpalette_length=4) 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 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 to_files(self, image_file, image_format="png"): image = _CREDITS_PREVIEW_ARRANGEMENT.image(self.tileset, self.palette) image.save(image_file, image_format) del image 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
class EbFont(object): def __init__(self, num_characters=96, tile_width=16, tile_height=8): self.num_characters = num_characters self.tileset = EbGraphicTileset(num_tiles=num_characters, tile_width=tile_width, tile_height=tile_height) self.character_widths = None def from_block(self, block, tileset_offset, character_widths_offset): self.tileset.from_block(block=block, offset=tileset_offset, bpp=1) for i in range(96, self.num_characters): self.tileset.clear_tile(i, color=1) self.character_widths = block[character_widths_offset:character_widths_offset + self.num_characters].to_list() def to_block(self, block): tileset_offset = block.allocate(size=self.tileset.block_size(bpp=1)) self.tileset.to_block(block=block, offset=tileset_offset, bpp=1) character_widths_offset = block.allocate(size=self.num_characters) block[character_widths_offset:character_widths_offset + self.num_characters] = self.character_widths return tileset_offset, character_widths_offset def to_files(self, image_file, widths_file, image_format="png", widths_format="yml"): if self.num_characters == 96: image = _FONT_IMAGE_ARRANGEMENT_96.image(self.tileset, FONT_IMAGE_PALETTE) elif self.num_characters == 128: image = _FONT_IMAGE_ARRANGEMENT_128.image(self.tileset, FONT_IMAGE_PALETTE) image.save(image_file, image_format) del image character_widths_dict = dict(enumerate(self.character_widths)) if widths_format == "yml": yml_dump(character_widths_dict, widths_file, default_flow_style=False) 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 image_size(self): if self.num_characters == 96: arr = _FONT_IMAGE_ARRANGEMENT_96 elif self.num_characters == 128: arr = _FONT_IMAGE_ARRANGEMENT_128 return arr.width * self.tileset.tile_width, arr.height * self.tileset.tile_height
class SoundStoneModule(EbModule): NAME = "Sound Stone" FREE_RANGES = [(0x0EDD5D, 0x0EF805)] # Sound stone graphics def __init__(self): super(SoundStoneModule, self).__init__() self.tileset = EbGraphicTileset(num_tiles=352, tile_width=8, tile_height=8) self.palette = EbPalette(num_subpalettes=6, subpalette_length=16) 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): 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 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 write_to_project(self, resource_open): image = SOUND_STONE_ARRANGEMENT.image(self.tileset, self.palette) with resource_open("Logos/SoundStone", "png") as image_file: image.save(image_file, "png") def upgrade_project(self, old_version, new_version, rom, resource_open_r, resource_open_w, resource_delete): if old_version < 8: self.read_from_rom(rom) self.write_to_project(resource_open_w)
def test_from_image_8x16_2bpp(self): palette = EbPalette(1, 4) palette[0, 0].from_tuple((0xff, 0xff, 0xff)) palette[0, 1].from_tuple((0x30, 0x00, 0xff)) palette[0, 2].from_tuple((0xff, 0x00, 0x00)) palette[0, 3].from_tuple((0x00, 0xff, 0x48)) arrangement = EbTileArrangement(width=2, height=3) arrangement[0, 0].tile = 1 arrangement[1, 0].tile = 1 arrangement[0, 1].tile = 3 arrangement[1, 1].tile = 2 arrangement[0, 2].tile = 0 arrangement[1, 2].tile = 4 tileset = EbGraphicTileset(num_tiles=5, tile_width=8, tile_height=16) tileset.from_image(self.tile_8x16_4bpp_img, arrangement=arrangement, palette=palette) assert_list_equal(tileset[1], [[2] * 8] * 16) assert_list_equal(tileset[3], [[3] * 8] * 16) assert_list_equal(tileset[2], [[3] * 8, [3] * 8, [3] * 8, [3] * 8, [3] * 8, [3, 3, 1, 3, 3, 3, 3, 3], [3, 3, 1, 3, 3, 1, 3, 3], [1] * 8, [1, 1, 2, 2, 1, 1, 1, 1], [1, 2, 2, 2, 2, 2, 1, 1], [1, 1, 1, 1, 1, 2, 1, 1], [1, 1, 1, 1, 2, 2, 1, 1], [1, 1, 2, 2, 2, 1, 1, 1], [1] * 8, [1] * 8, [1, 1, 1, 3, 1, 1, 1, 1]]) assert_list_equal(tileset[0], [[2, 1, 1, 1, 1, 1, 1, 1], [2, 3, 3, 3, 3, 3, 3, 1], [0, 2, 3, 3, 3, 3, 1, 3], [0, 2, 3, 3, 3, 3, 1, 3], [0, 0, 2, 3, 3, 1, 3, 3], [0, 0, 2, 3, 3, 1, 3, 3], [0, 0, 0, 2, 1, 3, 3, 3], [0, 0, 0, 2, 1, 3, 3, 3], [0, 0, 0, 1, 2, 3, 3, 3], [0, 0, 0, 1, 2, 3, 3, 3], [0, 0, 1, 0, 0, 2, 3, 3], [0, 0, 1, 0, 0, 2, 3, 3], [0, 1, 0, 0, 0, 0, 2, 3], [0, 1, 0, 0, 0, 0, 2, 3], [1, 0, 0, 0, 0, 0, 0, 2], [1, 0, 0, 0, 0, 0, 0, 2]]) assert_list_equal(tileset[4], [[3] * 8, [3] * 8, [3] * 8, [3] * 8, [3] * 8, [3, 2, 3, 3, 3, 2, 3, 3], [3] * 8, [3] * 8, [3, 3, 3, 3, 3, 3, 3, 2], [2, 3, 3, 3, 3, 3, 2, 3], [3, 2, 3, 3, 3, 2, 2, 3], [3, 2, 2, 2, 2, 2, 3, 3], [3] * 8, [3] * 8, [3] * 8, [3] * 8])
def test_from_image_8x8_1bpp(self): palette = EbPalette(1, 2) palette[0, 0].from_tuple((0xFF, 0xFF, 0xFF)) palette[0, 1].from_tuple((0x0, 0x0, 0x0)) arrangement = EbTileArrangement(width=2, height=2) arrangement[0, 0].tile = 0 arrangement[1, 0].tile = 2 arrangement[0, 1].tile = 1 arrangement[1, 1].tile = 3 tileset = EbGraphicTileset(num_tiles=4, tile_width=8, tile_height=8) tileset.from_image(self.tile_8x8_2bpp_img, arrangement=arrangement, palette=palette) assert_list_equal(tileset[0], [[0] * 8] * 8) assert_list_equal(tileset[2], [[1] * 8] * 8) assert_list_equal( tileset[1], [ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], ], ) assert_list_equal( tileset[3], [ [0, 1, 1, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1, 1, 1], [0, 1, 0, 0, 0, 0, 1, 1], [1, 1, 0, 0, 1, 0, 1, 1], [1, 1, 0, 1, 0, 0, 1, 1], [1, 1, 0, 0, 0, 0, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 0], ], )
class SoundStoneModule(EbModule): NAME = "Sound Stone" FREE_RANGES = [(0x0EDD5D, 0x0EF805)] # Sound stone graphics def __init__(self): super(SoundStoneModule, self).__init__() self.tileset = EbGraphicTileset(num_tiles=352, tile_width=8, tile_height=8) self.palette = EbPalette(num_subpalettes=6, subpalette_length=16) 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): 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 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 write_to_project(self, resource_open): image = SOUND_STONE_ARRANGEMENT.image(self.tileset, self.palette) with resource_open("Logos/SoundStone", "png") as image_file: image.save(image_file, "png") def upgrade_project(self, old_version, new_version, rom, resource_open_r, resource_open_w, resource_delete): if old_version < 8: self.read_from_rom(rom) self.write_to_project(resource_open_w)
class WindowGraphicsModule(EbModule): NAME = "Window Graphics" FREE_RANGES = [(0x200000, 0x20079f)] # Graphics def __init__(self): super(WindowGraphicsModule, self).__init__() self.graphics_1 = EbGraphicTileset(num_tiles=416, tile_width=8, tile_height=8) self.graphics_2 = EbGraphicTileset(num_tiles=7, tile_width=8, tile_height=8) self.flavor_palettes = [EbPalette(8, 4) for i in range(7)] self.flavor_names = dict() 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 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_to_project(self, resource_open): for i, palette in enumerate(self.flavor_palettes): with resource_open("WindowGraphics/Windows1_" + str(i), "png") as image_file: image = ARRANGEMENT_1.image(tileset=self.graphics_1, palette=palette) image.save(image_file, "png") with resource_open("WindowGraphics/Windows2_" + str(i), "png") as image_file: image = ARRANGEMENT_2.image(tileset=self.graphics_2, palette=palette.get_subpalette(7)) image.save(image_file, "png") # Write names with resource_open("WindowGraphics/flavor_names", "txt", True) as f: for asm_pointer_offset in FLAVOR_NAME_ASM_POINTER_OFFSETS: print(self.flavor_names[asm_pointer_offset], file=f) 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 test_from_image_8x16_2bpp(self): palette = EbPalette(1, 4) palette[0, 0].from_tuple((0xFF, 0xFF, 0xFF)) palette[0, 1].from_tuple((0x30, 0x00, 0xFF)) palette[0, 2].from_tuple((0xFF, 0x00, 0x00)) palette[0, 3].from_tuple((0x00, 0xFF, 0x48)) arrangement = EbTileArrangement(width=2, height=3) arrangement[0, 0].tile = 1 arrangement[1, 0].tile = 1 arrangement[0, 1].tile = 3 arrangement[1, 1].tile = 2 arrangement[0, 2].tile = 0 arrangement[1, 2].tile = 4 tileset = EbGraphicTileset(num_tiles=5, tile_width=8, tile_height=16) tileset.from_image(self.tile_8x16_4bpp_img, arrangement=arrangement, palette=palette) assert_list_equal(tileset[1], [[2] * 8] * 16) assert_list_equal(tileset[3], [[3] * 8] * 16) assert_list_equal( tileset[2], [ [3] * 8, [3] * 8, [3] * 8, [3] * 8, [3] * 8, [3, 3, 1, 3, 3, 3, 3, 3], [3, 3, 1, 3, 3, 1, 3, 3], [1] * 8, [1, 1, 2, 2, 1, 1, 1, 1], [1, 2, 2, 2, 2, 2, 1, 1], [1, 1, 1, 1, 1, 2, 1, 1], [1, 1, 1, 1, 2, 2, 1, 1], [1, 1, 2, 2, 2, 1, 1, 1], [1] * 8, [1] * 8, [1, 1, 1, 3, 1, 1, 1, 1], ], ) assert_list_equal( tileset[0], [ [2, 1, 1, 1, 1, 1, 1, 1], [2, 3, 3, 3, 3, 3, 3, 1], [0, 2, 3, 3, 3, 3, 1, 3], [0, 2, 3, 3, 3, 3, 1, 3], [0, 0, 2, 3, 3, 1, 3, 3], [0, 0, 2, 3, 3, 1, 3, 3], [0, 0, 0, 2, 1, 3, 3, 3], [0, 0, 0, 2, 1, 3, 3, 3], [0, 0, 0, 1, 2, 3, 3, 3], [0, 0, 0, 1, 2, 3, 3, 3], [0, 0, 1, 0, 0, 2, 3, 3], [0, 0, 1, 0, 0, 2, 3, 3], [0, 1, 0, 0, 0, 0, 2, 3], [0, 1, 0, 0, 0, 0, 2, 3], [1, 0, 0, 0, 0, 0, 0, 2], [1, 0, 0, 0, 0, 0, 0, 2], ], ) assert_list_equal( tileset[4], [ [3] * 8, [3] * 8, [3] * 8, [3] * 8, [3] * 8, [3, 2, 3, 3, 3, 2, 3, 3], [3] * 8, [3] * 8, [3, 3, 3, 3, 3, 3, 3, 2], [2, 3, 3, 3, 3, 3, 2, 3], [3, 2, 3, 3, 3, 2, 2, 3], [3, 2, 2, 2, 2, 2, 3, 3], [3] * 8, [3] * 8, [3] * 8, [3] * 8, ], )
class DeathScreenModule(EbModule): """Extracts the death screen data from EarthBound.""" NAME = "Death Screen" FREE_RANGES = [ (0x21cfaf, 0x21d4f3), # Tileset (0x21d4f4, 0x21d5e7), # Palette (0x21d5e8, 0x21d6e1) # Arrangement ] def __init__(self): super(DeathScreenModule, self).__init__() self.ness_tileset = EbGraphicTileset(num_tiles=NUM_TILES, tile_width=TILE_WIDTH, tile_height=TILE_HEIGHT) self.jeff_tileset = EbGraphicTileset(num_tiles=NUM_TILES, tile_width=TILE_WIDTH, tile_height=TILE_HEIGHT) self.arrangement = EbTileArrangement(width=ARRANGEMENT_WIDTH, height=ARRANGEMENT_HEIGHT) self.palette = EbPalette(num_subpalettes=NUM_SUBPALETTES, subpalette_length=SUBPALETTE_LENGTH) 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.ness_tileset.from_block(block=block, offset=NESS_OFFSET, bpp=TILESET_BPP) self.jeff_tileset.from_block(block=block, offset=JEFF_OFFSET, 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): # Write the tileset data block_size = self.ness_tileset.block_size( bpp=TILESET_BPP) + self.jeff_tileset.block_size(bpp=TILESET_BPP) with EbCompressibleBlock(block_size) as block: self.ness_tileset.to_block(block=block, offset=NESS_OFFSET, bpp=TILESET_BPP) self.jeff_tileset.to_block(block=block, offset=JEFF_OFFSET, bpp=TILESET_BPP) self._write_compressed_block(rom, block, TILESET_POINTER) # Write the tile arrangement data block_size = self.arrangement.block_size() with EbCompressibleBlock(block_size) as block: self.arrangement.to_block(block=block, offset=0) self._write_compressed_block(rom, block, ARRANGEMENT_POINTER) # Write the palette data block_size = self.palette.block_size() with EbCompressibleBlock(block_size) as block: self.palette.to_block(block=block, offset=0) self._write_compressed_block(rom, block, PALETTE_POINTER) 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 write_to_project(self, resource_open): with resource_open(NESS_DEATH_SCREEN_PATH, "png") as f: image = self.arrangement.image(self.ness_tileset, self.palette, True) image.save(f) with resource_open(JEFF_DEATH_SCREEN_PATH, "png") as f: image = self.arrangement.image(self.jeff_tileset, self.palette, True) image.save(f) with resource_open(DEATH_SCREEN_SUBPALETTES_PATH, "yml", True) as f: subpalettes = {} for x in range(ARRANGEMENT_WIDTH): for y in range(ARRANGEMENT_HEIGHT): subpalette = self.arrangement[x, y].subpalette if subpalette not in subpalettes: subpalettes[subpalette] = [] subpalettes[subpalette].append((x, y)) yml_dump(subpalettes, f, None) 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) @staticmethod 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))