225 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			225 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
from .rom import ROM
 | 
						|
from .pointerTable import PointerTable
 | 
						|
from .assembler import ASM
 | 
						|
 | 
						|
 | 
						|
class Texts(PointerTable):
 | 
						|
    END_OF_DATA = (0xfe, 0xff)
 | 
						|
 | 
						|
    def __init__(self, rom):
 | 
						|
        super().__init__(rom, {
 | 
						|
            "count": 0x2B0,
 | 
						|
            "pointers_addr": 1,
 | 
						|
            "pointers_bank": 0x1C,
 | 
						|
            "banks_addr": 0x741,
 | 
						|
            "banks_bank": 0x1C,
 | 
						|
            "expand_to_end_of_bank": {0x09}
 | 
						|
        })
 | 
						|
 | 
						|
 | 
						|
class Entities(PointerTable):
 | 
						|
    def __init__(self, rom):
 | 
						|
        super().__init__(rom, {
 | 
						|
            "count": 0x320,
 | 
						|
            "pointers_addr": 0,
 | 
						|
            "pointers_bank": 0x16,
 | 
						|
            "data_bank": 0x16,
 | 
						|
        })
 | 
						|
 | 
						|
class RoomsTable(PointerTable):
 | 
						|
    HEADER = 2
 | 
						|
 | 
						|
    def _readData(self, rom, bank_nr, pointer):
 | 
						|
        bank = rom.banks[bank_nr]
 | 
						|
        start = pointer
 | 
						|
        pointer += self.HEADER
 | 
						|
        while bank[pointer] != 0xFE:
 | 
						|
            obj_type = (bank[pointer] & 0xF0)
 | 
						|
            if obj_type == 0xE0:
 | 
						|
                pointer += 5
 | 
						|
            elif obj_type == 0xC0 or obj_type == 0x80:
 | 
						|
                pointer += 3
 | 
						|
            else:
 | 
						|
                pointer += 2
 | 
						|
        pointer += 1
 | 
						|
        self._addStorage(bank_nr, start, pointer)
 | 
						|
        return bank[start:pointer]
 | 
						|
 | 
						|
 | 
						|
class RoomsOverworldTop(RoomsTable):
 | 
						|
    def __init__(self, rom):
 | 
						|
        super().__init__(rom, {
 | 
						|
            "count": 0x080,
 | 
						|
            "pointers_addr": 0x000,
 | 
						|
            "pointers_bank": 0x09,
 | 
						|
            "data_bank": 0x09,
 | 
						|
            "alt_pointers": {
 | 
						|
                "Alt06": (0x00, 0x31FD),
 | 
						|
                "Alt0E": (0x00, 0x31CD),
 | 
						|
                "Alt1B": (0x00, 0x320D),
 | 
						|
                "Alt2B": (0x00, 0x321D),
 | 
						|
                "Alt79": (0x00, 0x31ED),
 | 
						|
            }
 | 
						|
        })
 | 
						|
 | 
						|
 | 
						|
class RoomsOverworldBottom(RoomsTable):
 | 
						|
    def __init__(self, rom):
 | 
						|
        super().__init__(rom, {
 | 
						|
            "count": 0x080,
 | 
						|
            "pointers_addr": 0x100,
 | 
						|
            "pointers_bank": 0x09,
 | 
						|
            "data_bank": 0x1A,
 | 
						|
            "alt_pointers": {
 | 
						|
                "Alt8C": (0x00, 0x31DD),
 | 
						|
            }
 | 
						|
        })
 | 
						|
 | 
						|
 | 
						|
class RoomsIndoorA(RoomsTable):
 | 
						|
    # TODO: The color dungeon tables are in the same bank, but the pointer table is after the room data.
 | 
						|
    def __init__(self, rom):
 | 
						|
        super().__init__(rom, {
 | 
						|
            "count": 0x100,
 | 
						|
            "pointers_addr": 0x000,
 | 
						|
            "pointers_bank": 0x0A,
 | 
						|
            "data_bank": 0x0A,
 | 
						|
            "alt_pointers": {
 | 
						|
                "Alt1F5": (0x00, 0x31A1),
 | 
						|
            }
 | 
						|
        })
 | 
						|
 | 
						|
 | 
						|
class RoomsIndoorB(RoomsTable):
 | 
						|
    # Most likely, this table can be expanded all the way to the end of the bank,
 | 
						|
    # giving a few 100 extra bytes to work with.
 | 
						|
    def __init__(self, rom):
 | 
						|
        super().__init__(rom, {
 | 
						|
            "count": 0x0FF,
 | 
						|
            "pointers_addr": 0x000,
 | 
						|
            "pointers_bank": 0x0B,
 | 
						|
            "data_bank": 0x0B,
 | 
						|
        })
 | 
						|
 | 
						|
 | 
						|
class RoomsColorDungeon(RoomsTable):
 | 
						|
    def __init__(self, rom):
 | 
						|
        super().__init__(rom, {
 | 
						|
            "count": 0x016,
 | 
						|
            "pointers_addr": 0x3B77,
 | 
						|
            "pointers_bank": 0x0A,
 | 
						|
            "data_bank": 0x0A,
 | 
						|
            "expand_to_end_of_bank": True
 | 
						|
        })
 | 
						|
 | 
						|
 | 
						|
class BackgroundTable(PointerTable):
 | 
						|
    def _readData(self, rom, bank_nr, pointer):
 | 
						|
        bank = rom.banks[bank_nr]
 | 
						|
        start = pointer
 | 
						|
        while bank[pointer] != 0x00:
 | 
						|
            addr = bank[pointer] << 8 | bank[pointer + 1]
 | 
						|
            amount = (bank[pointer + 2] & 0x3F) + 1
 | 
						|
            repeat = (bank[pointer + 2] & 0x40) == 0x40
 | 
						|
            vertical = (bank[pointer + 2] & 0x80) == 0x80
 | 
						|
            pointer += 3
 | 
						|
            if not repeat:
 | 
						|
                pointer += amount
 | 
						|
            if repeat:
 | 
						|
                pointer += 1
 | 
						|
        pointer += 1
 | 
						|
        self._addStorage(bank_nr, start, pointer)
 | 
						|
        return bank[start:pointer]
 | 
						|
 | 
						|
 | 
						|
class BackgroundTilesTable(BackgroundTable):
 | 
						|
    def __init__(self, rom):
 | 
						|
        super().__init__(rom, {
 | 
						|
            "count": 0x26,
 | 
						|
            "pointers_addr": 0x052B,
 | 
						|
            "pointers_bank": 0x20,
 | 
						|
            "data_bank": 0x08,
 | 
						|
            "expand_to_end_of_bank": True
 | 
						|
        })
 | 
						|
 | 
						|
 | 
						|
class BackgroundAttributeTable(BackgroundTable):
 | 
						|
    def __init__(self, rom):
 | 
						|
        super().__init__(rom, {
 | 
						|
            "count": 0x26,
 | 
						|
            "pointers_addr": 0x1C4B,
 | 
						|
            "pointers_bank": 0x24,
 | 
						|
            "data_bank": 0x24,
 | 
						|
            "expand_to_end_of_bank": True
 | 
						|
        })
 | 
						|
 | 
						|
 | 
						|
class OverworldRoomSpriteData(PointerTable):
 | 
						|
    def __init__(self, rom):
 | 
						|
        super().__init__(rom, {
 | 
						|
            "count": 0x100,
 | 
						|
            "pointers_addr": 0x30D3,
 | 
						|
            "pointers_bank": 0x20,
 | 
						|
            "data_bank": 0x20,
 | 
						|
            "data_addr": 0x33F3,
 | 
						|
            "data_size": 4,
 | 
						|
            "claim_storage_gaps": True,
 | 
						|
        })
 | 
						|
 | 
						|
 | 
						|
class IndoorRoomSpriteData(PointerTable):
 | 
						|
    def __init__(self, rom):
 | 
						|
        super().__init__(rom, {
 | 
						|
            "count": 0x220,
 | 
						|
            "pointers_addr": 0x31D3,
 | 
						|
            "pointers_bank": 0x20,
 | 
						|
            "data_bank": 0x20,
 | 
						|
            "data_addr": 0x363B,
 | 
						|
            "data_size": 4,
 | 
						|
            "claim_storage_gaps": True,
 | 
						|
        })
 | 
						|
 | 
						|
 | 
						|
class ROMWithTables(ROM):
 | 
						|
    def __init__(self, filename, patches=None):
 | 
						|
        super().__init__(filename, patches)
 | 
						|
 | 
						|
        # Ability to patch any text in the game with different text
 | 
						|
        self.texts = Texts(self)
 | 
						|
 | 
						|
        # Ability to modify rooms
 | 
						|
        self.entities = Entities(self)
 | 
						|
        self.rooms_overworld_top = RoomsOverworldTop(self)
 | 
						|
        self.rooms_overworld_bottom = RoomsOverworldBottom(self)
 | 
						|
        self.rooms_indoor_a = RoomsIndoorA(self)
 | 
						|
        self.rooms_indoor_b = RoomsIndoorB(self)
 | 
						|
        self.rooms_color_dungeon = RoomsColorDungeon(self)
 | 
						|
        self.room_sprite_data_overworld = OverworldRoomSpriteData(self)
 | 
						|
        self.room_sprite_data_indoor = IndoorRoomSpriteData(self)
 | 
						|
 | 
						|
        # Backgrounds for things like the title screen.
 | 
						|
        self.background_tiles = BackgroundTilesTable(self)
 | 
						|
        self.background_attributes = BackgroundAttributeTable(self)
 | 
						|
 | 
						|
        self.itemNames = {}
 | 
						|
 | 
						|
    def save(self, filename, *, name=None):
 | 
						|
        # Assert special handling of bank 9 expansion is fine
 | 
						|
        for i in range(0x3d42, 0x4000):
 | 
						|
            assert self.banks[9][i] == 0, self.banks[9][i]
 | 
						|
        self.texts.store(self)
 | 
						|
        self.entities.store(self)
 | 
						|
        self.rooms_overworld_top.store(self)
 | 
						|
        self.rooms_overworld_bottom.store(self)
 | 
						|
        self.rooms_indoor_a.store(self)
 | 
						|
        self.rooms_indoor_b.store(self)
 | 
						|
        self.rooms_color_dungeon.store(self)
 | 
						|
        leftover_storage = self.room_sprite_data_overworld.store(self)
 | 
						|
        self.room_sprite_data_indoor.addStorage(leftover_storage)
 | 
						|
        self.patch(0x00, 0x0DFA, ASM("ld hl, $763B"), ASM("ld hl, $%04x" % (leftover_storage[0]["start"] | 0x4000)))
 | 
						|
        self.room_sprite_data_indoor.adjustDataStart(leftover_storage[0]["start"])
 | 
						|
        self.room_sprite_data_indoor.store(self)
 | 
						|
        self.background_tiles.store(self)
 | 
						|
        self.background_attributes.store(self)
 | 
						|
        super().save(filename, name=name)
 |