220 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			220 lines
		
	
	
		
			6.6 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, | ||
|  |         }) | ||
|  | 
 | ||
|  | 
 | ||
|  | 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): | ||
|  |         super().__init__(filename) | ||
|  | 
 | ||
|  |         # 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): | ||
|  |         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) |