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]])
Beispiel #2
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))
Beispiel #3
0
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
Beispiel #4
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)
        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
Beispiel #5
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)
    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])
Beispiel #7
0
    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)
Beispiel #9
0
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)
Beispiel #10
0
    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,
            ],
        )
Beispiel #11
0
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))