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)
|