From b8c7d6a72fd47ab90d43b81f671545f1c3fbc18f Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Tue, 27 Apr 2021 07:19:53 +0200 Subject: [PATCH] remove remaining sprite data --- data/default.apsprite | 7 ++ data/default.zspr | Bin 28882 -> 0 bytes data/sprites/custom/link.apsprite | 7 ++ worlds/alttp/Rom.py | 176 +++++++++++++++++++----------- 4 files changed, 129 insertions(+), 61 deletions(-) create mode 100644 data/default.apsprite delete mode 100644 data/default.zspr create mode 100644 data/sprites/custom/link.apsprite diff --git a/data/default.apsprite b/data/default.apsprite new file mode 100644 index 00000000..ea0e85c1 --- /dev/null +++ b/data/default.apsprite @@ -0,0 +1,7 @@ +author: Nintendo +data: null +game: A Link to the Past +min_format_version: 1 +name: Link +format_version: 1 +sprite_version: 1 \ No newline at end of file diff --git a/data/default.zspr b/data/default.zspr deleted file mode 100644 index fa0c9836a67c39ca00cb7515585e00ec36219982..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28882 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 MtLpA6_dMeIA2ZywzW@LL diff --git a/data/sprites/custom/link.apsprite b/data/sprites/custom/link.apsprite new file mode 100644 index 00000000..ea0e85c1 --- /dev/null +++ b/data/sprites/custom/link.apsprite @@ -0,0 +1,7 @@ +author: Nintendo +data: null +game: A Link to the Past +min_format_version: 1 +name: Link +format_version: 1 +sprite_version: 1 \ No newline at end of file diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py index 6be9d711..724ffaa3 100644 --- a/worlds/alttp/Rom.py +++ b/worlds/alttp/Rom.py @@ -14,6 +14,7 @@ import subprocess import threading import xxtea import concurrent.futures +import bsdiff4 from typing import Optional from BaseClasses import CollectionState, Region @@ -22,10 +23,12 @@ from worlds.alttp.Shops import ShopType from worlds.alttp.Dungeons import dungeon_music_addresses from worlds.alttp.Regions import location_table, old_location_address_to_new_location_address from worlds.alttp.Text import MultiByteTextMapper, text_addresses, Credits, TextTable -from worlds.alttp.Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, \ +from worlds.alttp.Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, \ + Blind_texts, \ BombShop2_texts, junk_texts -from worlds.alttp.Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, \ +from worlds.alttp.Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, \ + DeathMountain_texts, \ LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, \ SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc, is_bundled @@ -41,6 +44,7 @@ except: enemizer_logger = logging.getLogger("Enemizer") + class LocalRom(object): def __init__(self, file, patch=True, vanillaRom=None, name=None, hash=None): @@ -428,8 +432,11 @@ def patch_enemizer(world, team: int, player: int, rom: LocalRom, enemizercli): except OSError: pass + tile_list_lock = threading.Lock() _tile_collection_table = [] + + def _populate_tile_sets(): with tile_list_lock: if not _tile_collection_table: @@ -442,6 +449,7 @@ def _populate_tile_sets(): for file in os.listdir(dir): pool.submit(load_tileset_from_file, os.path.join(dir, file)) + class TileSet: def __init__(self, filename): with open(filename, 'rt', encoding='utf-8-sig') as file: @@ -497,25 +505,23 @@ def _populate_sprite_table(): for file in os.listdir(dir): pool.submit(load_sprite_from_file, os.path.join(dir, file)) -class Sprite(object): - palette = (255, 127, 126, 35, 183, 17, 158, 54, 165, 20, 255, 1, 120, 16, 157, - 89, 71, 54, 104, 59, 74, 10, 239, 18, 92, 42, 113, 21, 24, 122, - 255, 127, 126, 35, 183, 17, 158, 54, 165, 20, 255, 1, 120, 16, 157, - 89, 128, 105, 145, 118, 184, 38, 127, 67, 92, 42, 153, 17, 24, 122, - 255, 127, 126, 35, 183, 17, 158, 54, 165, 20, 255, 1, 120, 16, 157, - 89, 87, 16, 126, 69, 243, 109, 185, 126, 92, 42, 39, 34, 24, 122, - 255, 127, 126, 35, 218, 17, 158, 54, 165, 20, 255, 1, 120, 16, 151, - 61, 71, 54, 104, 59, 74, 10, 239, 18, 126, 86, 114, 24, 24, 122) - glove_palette = (246, 82, 118, 3) +class Sprite(): + sprite_size = 28672 + palette_size = 120 + glove_size = 4 author_name: Optional[str] = None def __init__(self, filename): + if not hasattr(Sprite, "palette"): + self.get_vanilla_sprite_data() with open(filename, 'rb') as file: - filedata = bytearray(file.read()) + filedata = file.read() self.name = os.path.basename(filename) self.valid = True - if len(filedata) == 0x7000: + if filename.endswith(".apsprite"): + self.from_ap_sprite(filedata) + elif len(filedata) == 0x7000: # sprite file with graphics and without palette data self.sprite = filedata[:0x7000] elif len(filedata) == 0x7078: @@ -534,26 +540,70 @@ class Sprite(object): self.palette = filedata[0xDD308:0xDD380] self.glove_palette = filedata[0xDEDF5:0xDEDF9] elif filedata.startswith(b'ZSPR'): - result = self.parse_zspr(filedata, 1) - if result is None: - self.valid = False - return - (sprite, palette, self.name, self.author_name) = result - if self.name == "": - self.name = os.path.split(filename)[1].split(".")[0] - if len(sprite) != 0x7000: - self.valid = False - return - self.sprite = sprite - if len(palette) == 0: - pass - elif len(palette) == 0x78: - self.palette = palette - elif len(palette) == 0x7C: - self.palette = palette[:0x78] - self.glove_palette = palette[0x78:] - else: - self.valid = False + self.from_zspr(filedata, filename) + else: + self.valid = False + + def get_vanilla_sprite_data(self): + from Patch import get_base_rom_path + file_name = get_base_rom_path() + base_rom_bytes = bytes(read_rom(open(file_name, "rb"))) + sprite = base_rom_bytes[0x80000:0x87000] + palette = base_rom_bytes[0xDD308:0xDD380] + glove_palette = base_rom_bytes[0xDEDF5:0xDEDF9] + Sprite.base_data = sprite + palette + glove_palette + + def from_ap_sprite(self, filedata): + filedata = filedata.decode("utf-8-sig") + import yaml + obj = yaml.safe_load(filedata) + if obj["min_format_version"] > 1: + raise Exception("Sprite file requires an updated reader.") + self.author_name = obj["author"] + self.name = obj["name"] + if obj["data"]: # skip patching for vanilla content + data = bsdiff4.patch(Sprite.base_data, obj["data"]) + self.sprite = data[:self.sprite_size] + self.palette = data[self.sprite_size:self.palette_size] + self.glove_palette = data[self.sprite_size + self.palette_size:] + + def to_ap_sprite(self, path): + from .. import Games + import yaml + payload = {"format_version": 1, + "min_format_version": 1, + "sprite_version": 1, + "name": self.name, + "author": self.author_name, + "game": Games.LTTP.value, + "data": self.get_delta()} + with open(path, "w") as f: + f.write(yaml.safe_dump(payload)) + + def get_delta(self): + modified_data = self.sprite + self.palette + self.glove_palette + return bsdiff4.diff(Sprite.base_data, modified_data) + + def from_zspr(self, filedata, filename): + result = self.parse_zspr(filedata, 1) + if result is None: + self.valid = False + return + (sprite, palette, self.name, self.author_name) = result + if self.name == "": + self.name = os.path.split(filename)[1].split(".")[0] + + if len(sprite) != 0x7000: + self.valid = False + return + self.sprite = sprite + if len(palette) == 0: + pass + elif len(palette) == 0x78: + self.palette = palette + elif len(palette) == 0x7C: + self.palette = palette[:0x78] + self.glove_palette = palette[0x78:] else: self.valid = False @@ -569,7 +619,7 @@ class Sprite(object): @staticmethod def default_link_sprite(): - return Sprite(local_path('../../data', 'default.zspr')) + return Sprite(local_path('data', 'default.apsprite')) def decode8(self, pos): arr = [[0 for _ in range(8)] for _ in range(8)] @@ -603,12 +653,12 @@ class Sprite(object): return arr def parse_zspr(self, filedata, expected_kind): - logger = logging.getLogger('') + logger = logging.getLogger('ZSPR') headerstr = "<4xBHHIHIHH6x" headersize = struct.calcsize(headerstr) if len(filedata) < headersize: return None - (version, csum, icsum, sprite_offset, sprite_size, palette_offset, palette_size, kind) = struct.unpack_from( + version, csum, icsum, sprite_offset, sprite_size, palette_offset, palette_size, kind = struct.unpack_from( headerstr, filedata) if version not in [1]: logger.error('Error parsing ZSPR file: Version %g not supported', version) @@ -657,7 +707,7 @@ class Sprite(object): return pair[1] << 8 | pair[0] def expand_color(i): - return ((i & 0x1F) * 8, (i >> 5 & 0x1F) * 8, (i >> 10 & 0x1F) * 8) + return (i & 0x1F) * 8, (i >> 5 & 0x1F) * 8, (i >> 10 & 0x1F) * 8 # turn palette data into a list of RGB tuples with 8 bit values palette_as_colors = [expand_color(make_int16(chnk)) for chnk in array_chunk(self.palette, 2)] @@ -689,6 +739,7 @@ bonk_addresses = [0x4CF6C, 0x4CFBA, 0x4CFE0, 0x4CFFB, 0x4D018, 0x4D01B, 0x4D028, 0x4D3F8, 0x4D416, 0x4D420, 0x4D423, 0x4D42D, 0x4D449, 0x4D48C, 0x4D4D9, 0x4D4DC, 0x4D4E3, 0x4D504, 0x4D507, 0x4D55E, 0x4D56A] + def patch_rom(world, rom, player, team, enemized): local_random = world.rom_seeds[player] @@ -751,7 +802,6 @@ def patch_rom(world, rom, player, team, enemized): for music_address in music_addresses: rom.write_byte(music_address, music) - if world.mapshuffle[player]: rom.write_byte(0x155C9, local_random.choice([0x11, 0x16])) # Randomize GT music too with map shuffle @@ -1070,7 +1120,6 @@ def patch_rom(world, rom, player, team, enemized): byte = int(rom.read_byte(address)) rom.write_byte(address, prize_replacements.get(byte, byte)) - # Fill in item substitutions table rom.write_bytes(0x184000, [ # original_item, limit, replacement_item, filler @@ -1146,9 +1195,12 @@ def patch_rom(world, rom, player, team, enemized): # Set up requested clock settings if world.clock_mode[player] in ['countdown-ohko', 'stopwatch', 'countdown']: - rom.write_int32(0x180200, world.red_clock_time[player] * 60 * 60) # red clock adjustment time (in frames, sint32) - rom.write_int32(0x180204, world.blue_clock_time[player] * 60 * 60) # blue clock adjustment time (in frames, sint32) - rom.write_int32(0x180208, world.green_clock_time[player] * 60 * 60) # green clock adjustment time (in frames, sint32) + rom.write_int32(0x180200, + world.red_clock_time[player] * 60 * 60) # red clock adjustment time (in frames, sint32) + rom.write_int32(0x180204, + world.blue_clock_time[player] * 60 * 60) # blue clock adjustment time (in frames, sint32) + rom.write_int32(0x180208, + world.green_clock_time[player] * 60 * 60) # green clock adjustment time (in frames, sint32) else: rom.write_int32(0x180200, 0) # red clock adjustment time (in frames, sint32) rom.write_int32(0x180204, 0) # blue clock adjustment time (in frames, sint32) @@ -1507,7 +1559,8 @@ def patch_rom(world, rom, player, team, enemized): rom.write_byte(0xEFD95, digging_game_rng) rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills rom.write_byte(0x1800A4, 0x01 if world.logic[player] != 'nologic' else 0x00) # enable POD EG fix - rom.write_byte(0x186383, 0x01 if world.glitch_triforce or world.logic[player] == 'nologic' else 0x00) # disable glitching to Triforce from Ganons Room + rom.write_byte(0x186383, 0x01 if world.glitch_triforce or world.logic[ + player] == 'nologic' else 0x00) # disable glitching to Triforce from Ganons Room rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill # remove shield from uncle @@ -1579,7 +1632,6 @@ def patch_rom(world, rom, player, team, enemized): rom.write_byte(0x4BA1D, tile_set.get_len()) rom.write_bytes(0x4BA2A, tile_set.get_bytes()) - write_strings(rom, world, player, team) rom.write_byte(0x18637C, 1 if world.remote_items[player] else 0) @@ -1650,7 +1702,7 @@ def write_custom_shops(rom, world, player): slot = 0 if shop.type == ShopType.TakeAny else index if item is None: break - if not item['item'] in item_table: # item not native to ALTTP + if not item['item'] in item_table: # item not native to ALTTP item_code = 0x09 # Hammer else: item_code = ItemFactory(item['item'], player).code @@ -1691,7 +1743,8 @@ def hud_format_text(text): def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite: str, palettes_options, - world=None, player=1, allow_random_on_event=False, reduceflashing=False, triforcehud:str = None): + world=None, player=1, allow_random_on_event=False, reduceflashing=False, + triforcehud: str = None): local_random = random if not world else world.rom_seeds[player] # enable instant item menu @@ -1716,22 +1769,22 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr else: rom.write_byte(0x180048, 0x08) - # Reduce flashing by nopping out instructions if reduceflashing: - rom.write_bytes(0x17E07, [0x06]) # reduce amount of colors changed, add this branch if we need to reduce more ""+ [0x80] + [(0x81-0x08)]"" - rom.write_bytes(0x17EAB, [0xD0, 0x03, 0xA9, 0x40, 0x29, 0x60]) # nullifies aga lightning, cutscene, vitreous, bat, ether + rom.write_bytes(0x17E07, [ + 0x06]) # reduce amount of colors changed, add this branch if we need to reduce more ""+ [0x80] + [(0x81-0x08)]"" + rom.write_bytes(0x17EAB, + [0xD0, 0x03, 0xA9, 0x40, 0x29, 0x60]) # nullifies aga lightning, cutscene, vitreous, bat, ether # ONLY write to black values with this low pale blue to indicate flashing, that's IT. ""BNE + : LDA #$2940 : + : RTS"" - rom.write_bytes(0x123FE, [0x72]) # set lightning flash in misery mire (and standard) to brightness 0x72 - rom.write_bytes(0x3FA7B, [0x80, 0xac-0x7b]) # branch from palette writing lightning on death mountain - rom.write_byte(0x10817F, 0x01) # internal rom option + rom.write_bytes(0x123FE, [0x72]) # set lightning flash in misery mire (and standard) to brightness 0x72 + rom.write_bytes(0x3FA7B, [0x80, 0xac - 0x7b]) # branch from palette writing lightning on death mountain + rom.write_byte(0x10817F, 0x01) # internal rom option else: - rom.write_bytes(0x17E07, [0x00]) + rom.write_bytes(0x17E07, [0x00]) rom.write_bytes(0x17EAB, [0x85, 0x00, 0x29, 0x1F, 0x00, 0x18]) - rom.write_bytes(0x123FE, [0x32]) # original weather flash value - rom.write_bytes(0x3FA7B, [0xc2, 0x20]) # rep #$20 - rom.write_byte(0x10817F, 0x00) # internal rom option - + rom.write_bytes(0x123FE, [0x32]) # original weather flash value + rom.write_bytes(0x3FA7B, [0xc2, 0x20]) # rep #$20 + rom.write_byte(0x10817F, 0x00) # internal rom option rom.write_byte(0x18004B, 0x01 if quickswap else 0x00) @@ -1766,7 +1819,8 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr if triforcehud: # set triforcehud - triforce_flag = (rom.read_byte(0x180167) & 0x80) | {'normal': 0x00, 'hide_goal': 0x01, 'hide_required': 0x02, 'hide_both': 0x03}[triforcehud] + triforce_flag = (rom.read_byte(0x180167) & 0x80) | \ + {'normal': 0x00, 'hide_goal': 0x01, 'hide_required': 0x02, 'hide_both': 0x03}[triforcehud] rom.write_byte(0x180167, triforce_flag) if z3pr: @@ -2045,7 +2099,7 @@ def write_strings(rom, world, player, team): f"\n ≥ Duh\n Oh carp\n{{CHOICE}}" # Bottle Vendor hint vendor_location = world.get_location("Bottle Merchant", player) - tt['bottle_vendor_choice'] = f"I gots {hint_text(vendor_location.item)}\nYous gots 100 rupees?"\ + tt['bottle_vendor_choice'] = f"I gots {hint_text(vendor_location.item)}\nYous gots 100 rupees?" \ f"\n ≥ I want\n no way!\n{{CHOICE}}" tt['sign_north_of_links_house'] = '> Randomizer The telepathic tiles can have hints!' @@ -2394,7 +2448,7 @@ def set_inverted_mode(world, player, rom): rom.write_byte(0xDC21D, 0x6B) # inverted mode flute activation (skip weathervane overlay) rom.write_bytes(0x48DB3, [0xF8, 0x01]) # inverted mode (bird X) rom.write_byte(0x48D5E, 0x01) # inverted mode (rock X) - rom.write_bytes(0x48CC1+36, bytes([0xF8]*12)) # (rock X) + rom.write_bytes(0x48CC1 + 36, bytes([0xF8] * 12)) # (rock X) rom.write_int16s(snes_to_pc(0x02E849), [0x0043, 0x0056, 0x0058, 0x006C, 0x006F, 0x0070, 0x007B, 0x007F, 0x001B]) # dw flute rom.write_int16(snes_to_pc(0x02E8D5), 0x07C8)