490 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			490 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import binascii
 | 
						|
import importlib.util
 | 
						|
import importlib.machinery
 | 
						|
import os
 | 
						|
import random
 | 
						|
import pickle
 | 
						|
import Utils
 | 
						|
import settings
 | 
						|
from collections import defaultdict
 | 
						|
from typing import Dict
 | 
						|
 | 
						|
from .romTables import ROMWithTables
 | 
						|
from . import assembler
 | 
						|
from . import patches
 | 
						|
from .patches import overworld as _
 | 
						|
from .patches import dungeon as _
 | 
						|
from .patches import entrances as _
 | 
						|
from .patches import enemies as _
 | 
						|
from .patches import titleScreen as _
 | 
						|
from .patches import aesthetics as _
 | 
						|
from .patches import music as _
 | 
						|
from .patches import core as _
 | 
						|
from .patches import phone as _
 | 
						|
from .patches import photographer as _
 | 
						|
from .patches import owl as _
 | 
						|
from .patches import bank3e as _
 | 
						|
from .patches import bank3f as _
 | 
						|
from .patches import inventory as _
 | 
						|
from .patches import witch as _
 | 
						|
from .patches import tarin as _
 | 
						|
from .patches import fishingMinigame as _
 | 
						|
from .patches import softlock as _
 | 
						|
from .patches import maptweaks as _
 | 
						|
from .patches import chest as _
 | 
						|
from .patches import bomb as _
 | 
						|
from .patches import rooster as _
 | 
						|
from .patches import shop as _
 | 
						|
from .patches import trendy as _
 | 
						|
from .patches import goal as _
 | 
						|
from .patches import hardMode as _
 | 
						|
from .patches import weapons as _
 | 
						|
from .patches import health as _
 | 
						|
from .patches import heartPiece as _
 | 
						|
from .patches import droppedKey as _
 | 
						|
from .patches import goldenLeaf as _
 | 
						|
from .patches import songs as _
 | 
						|
from .patches import bowwow as _
 | 
						|
from .patches import desert as _
 | 
						|
from .patches import reduceRNG as _
 | 
						|
from .patches import madBatter as _
 | 
						|
from .patches import tunicFairy as _
 | 
						|
from .patches import seashell as _
 | 
						|
from .patches import instrument as _
 | 
						|
from .patches import endscreen as _
 | 
						|
from .patches import save as _
 | 
						|
from .patches import bingo as _
 | 
						|
from .patches import multiworld as _
 | 
						|
from .patches import tradeSequence as _
 | 
						|
from . import hints
 | 
						|
 | 
						|
from .patches import bank34
 | 
						|
from .roomEditor import RoomEditor, Object
 | 
						|
from .patches.aesthetics import rgb_to_bin, bin_to_rgb
 | 
						|
 | 
						|
from .. import Options
 | 
						|
 | 
						|
# Function to generate a final rom, this patches the rom with all required patches
 | 
						|
def generateRom(base_rom: bytes, args, patch_data: Dict):
 | 
						|
    random.seed(patch_data["seed"] + patch_data["player"])
 | 
						|
    multi_key = binascii.unhexlify(patch_data["multi_key"].encode())
 | 
						|
    item_list = pickle.loads(binascii.unhexlify(patch_data["item_list"].encode()))
 | 
						|
    options = patch_data["options"]
 | 
						|
    rom_patches = []
 | 
						|
    rom = ROMWithTables(base_rom, rom_patches)
 | 
						|
    rom.player_names = patch_data["other_player_names"]
 | 
						|
    pymods = []
 | 
						|
    if args.pymod:
 | 
						|
        for pymod in args.pymod:
 | 
						|
            spec = importlib.util.spec_from_loader(pymod, importlib.machinery.SourceFileLoader(pymod, pymod))
 | 
						|
            module = importlib.util.module_from_spec(spec)
 | 
						|
            spec.loader.exec_module(module)
 | 
						|
            pymods.append(module)
 | 
						|
    for pymod in pymods:
 | 
						|
        pymod.prePatch(rom)
 | 
						|
 | 
						|
    if options["gfxmod"]:
 | 
						|
        user_settings = settings.get_settings()
 | 
						|
        try:
 | 
						|
            gfx_mod_file = user_settings["ladx_options"]["gfx_mod_file"]
 | 
						|
            patches.aesthetics.gfxMod(rom, gfx_mod_file)
 | 
						|
        except FileNotFoundError:
 | 
						|
            pass # if user just doesnt provide gfxmod file, let patching continue
 | 
						|
 | 
						|
    assembler.resetConsts()
 | 
						|
    assembler.const("INV_SIZE", 16)
 | 
						|
    assembler.const("wHasFlippers", 0xDB3E)
 | 
						|
    assembler.const("wHasMedicine", 0xDB3F)
 | 
						|
    assembler.const("wTradeSequenceItem", 0xDB40)  # we use it to store flags of which trade items we have
 | 
						|
    assembler.const("wTradeSequenceItem2", 0xDB7F)  # Normally used to store that we have exchanged the trade item, we use it to store flags of which trade items we have
 | 
						|
    assembler.const("wSeashellsCount", 0xDB41)
 | 
						|
    assembler.const("wGoldenLeaves", 0xDB42)  # New memory location where to store the golden leaf counter
 | 
						|
    assembler.const("wCollectedTunics", 0xDB6D)  # Memory location where to store which tunic options are available (and boots)
 | 
						|
    assembler.const("wCustomMessage", 0xC0A0)
 | 
						|
    assembler.const("wOverworldRoomStatus", 0xD800)
 | 
						|
 | 
						|
    # We store the link info in unused color dungeon flags, so it gets preserved in the savegame.
 | 
						|
    assembler.const("wLinkSyncSequenceNumber", 0xDDF6)
 | 
						|
    assembler.const("wLinkStatusBits", 0xDDF7)
 | 
						|
    assembler.const("wLinkGiveItem", 0xDDF8)
 | 
						|
    assembler.const("wLinkGiveItemFrom", 0xDDF9)
 | 
						|
    assembler.const("wLinkSendItemRoomHigh", 0xDDFA)
 | 
						|
    assembler.const("wLinkSendItemRoomLow", 0xDDFB)
 | 
						|
    assembler.const("wLinkSendItemTarget", 0xDDFC)
 | 
						|
    assembler.const("wLinkSendItemItem", 0xDDFD)
 | 
						|
 | 
						|
    assembler.const("wZolSpawnCount", 0xDE10)
 | 
						|
    assembler.const("wCuccoSpawnCount", 0xDE11)
 | 
						|
    assembler.const("wDropBombSpawnCount", 0xDE12)
 | 
						|
    assembler.const("wLinkSpawnDelay", 0xDE13)
 | 
						|
 | 
						|
    #assembler.const("HARDWARE_LINK", 1)
 | 
						|
    assembler.const("HARD_MODE", 1 if options["hard_mode"] else 0)
 | 
						|
 | 
						|
    patches.core.cleanup(rom)
 | 
						|
    patches.save.singleSaveSlot(rom)
 | 
						|
    patches.phone.patchPhone(rom)
 | 
						|
    patches.photographer.fixPhotographer(rom)
 | 
						|
    patches.core.bugfixWrittingWrongRoomStatus(rom)
 | 
						|
    patches.core.bugfixBossroomTopPush(rom)
 | 
						|
    patches.core.bugfixPowderBagSprite(rom)
 | 
						|
    patches.core.fixEggDeathClearingItems(rom)
 | 
						|
    patches.core.disablePhotoPrint(rom)
 | 
						|
    patches.core.easyColorDungeonAccess(rom)
 | 
						|
    patches.owl.removeOwlEvents(rom)
 | 
						|
    patches.enemies.fixArmosKnightAsMiniboss(rom)
 | 
						|
    patches.bank3e.addBank3E(rom, multi_key, patch_data["player"], patch_data["other_player_names"])
 | 
						|
    patches.bank3f.addBank3F(rom)
 | 
						|
    patches.bank34.addBank34(rom, item_list)
 | 
						|
    patches.core.removeGhost(rom)
 | 
						|
    patches.core.fixMarinFollower(rom)
 | 
						|
    patches.core.fixWrongWarp(rom)
 | 
						|
    patches.core.alwaysAllowSecretBook(rom)
 | 
						|
    patches.core.injectMainLoop(rom)
 | 
						|
 | 
						|
    if options["shuffle_small_keys"] != Options.ShuffleSmallKeys.option_original_dungeon or\
 | 
						|
            options["shuffle_nightmare_keys"] != Options.ShuffleNightmareKeys.option_original_dungeon:
 | 
						|
        patches.inventory.advancedInventorySubscreen(rom)
 | 
						|
    patches.inventory.moreSlots(rom)
 | 
						|
    # if ladxr_settings["witch"]:
 | 
						|
    patches.witch.updateWitch(rom)
 | 
						|
    patches.softlock.fixAll(rom)
 | 
						|
    if not options["rooster"]:
 | 
						|
        patches.maptweaks.tweakMap(rom)
 | 
						|
        patches.maptweaks.tweakBirdKeyRoom(rom)
 | 
						|
    if options["overworld"] == Options.Overworld.option_open_mabe:
 | 
						|
        patches.maptweaks.openMabe(rom)
 | 
						|
    patches.chest.fixChests(rom)
 | 
						|
    patches.shop.fixShop(rom)
 | 
						|
    patches.rooster.patchRooster(rom)
 | 
						|
    patches.trendy.fixTrendy(rom)
 | 
						|
    patches.droppedKey.fixDroppedKey(rom)
 | 
						|
    patches.madBatter.upgradeMadBatter(rom)
 | 
						|
    patches.tunicFairy.upgradeTunicFairy(rom)
 | 
						|
    patches.tarin.updateTarin(rom)
 | 
						|
    patches.fishingMinigame.updateFinishingMinigame(rom)
 | 
						|
    patches.health.upgradeHealthContainers(rom)
 | 
						|
    # if ladxr_settings["owlstatues"] in ("dungeon", "both"):
 | 
						|
    #    patches.owl.upgradeDungeonOwlStatues(rom)
 | 
						|
    # if ladxr_settings["owlstatues"] in ("overworld", "both"):
 | 
						|
    #    patches.owl.upgradeOverworldOwlStatues(rom)
 | 
						|
    patches.goldenLeaf.fixGoldenLeaf(rom)
 | 
						|
    patches.heartPiece.fixHeartPiece(rom)
 | 
						|
    patches.seashell.fixSeashell(rom)
 | 
						|
    patches.instrument.fixInstruments(rom)
 | 
						|
    patches.seashell.upgradeMansion(rom)
 | 
						|
    patches.songs.upgradeMarin(rom)
 | 
						|
    patches.songs.upgradeManbo(rom)
 | 
						|
    patches.songs.upgradeMamu(rom)
 | 
						|
 | 
						|
    patches.tradeSequence.patchTradeSequence(rom, options)
 | 
						|
    patches.bowwow.fixBowwow(rom, everywhere=False)
 | 
						|
    # if ladxr_settings["bowwow"] != 'normal':
 | 
						|
    #    patches.bowwow.bowwowMapPatches(rom)
 | 
						|
    patches.desert.desertAccess(rom)
 | 
						|
    # if ladxr_settings["overworld"] == 'dungeondive':
 | 
						|
    #    patches.overworld.patchOverworldTilesets(rom)
 | 
						|
    #    patches.overworld.createDungeonOnlyOverworld(rom)
 | 
						|
    # elif ladxr_settings["overworld"] == 'nodungeons':
 | 
						|
    #    patches.dungeon.patchNoDungeons(rom)
 | 
						|
    #elif world.ladxr_settings["overworld"] == 'random':
 | 
						|
    #    patches.overworld.patchOverworldTilesets(rom)
 | 
						|
    #    mapgen.store_map(rom, world.ladxr_logic.world.map)
 | 
						|
    #if settings.dungeon_items == 'keysy':
 | 
						|
    #    patches.dungeon.removeKeyDoors(rom)
 | 
						|
    # patches.reduceRNG.slowdownThreeOfAKind(rom)
 | 
						|
    patches.reduceRNG.fixHorseHeads(rom)
 | 
						|
    patches.bomb.onlyDropBombsWhenHaveBombs(rom)
 | 
						|
    if options["music_change_condition"] == Options.MusicChangeCondition.option_always:
 | 
						|
        patches.aesthetics.noSwordMusic(rom)
 | 
						|
    patches.aesthetics.reduceMessageLengths(rom, random)
 | 
						|
    patches.aesthetics.allowColorDungeonSpritesEverywhere(rom)
 | 
						|
    if options["music"] == Options.Music.option_shuffled:
 | 
						|
        patches.music.randomizeMusic(rom, random)
 | 
						|
    elif options["music"] == Options.Music.option_off:
 | 
						|
        patches.music.noMusic(rom)
 | 
						|
    if options["no_flash"]:
 | 
						|
        patches.aesthetics.removeFlashingLights(rom)
 | 
						|
    if options["hard_mode"] == Options.HardMode.option_oracle:
 | 
						|
        patches.hardMode.oracleMode(rom)
 | 
						|
    elif options["hard_mode"] == Options.HardMode.option_hero:
 | 
						|
        patches.hardMode.heroMode(rom)
 | 
						|
    elif options["hard_mode"] == Options.HardMode.option_ohko:
 | 
						|
        patches.hardMode.oneHitKO(rom)
 | 
						|
    #if ladxr_settings["superweapons"]:
 | 
						|
    #    patches.weapons.patchSuperWeapons(rom)
 | 
						|
    if options["text_mode"] == Options.TextMode.option_fast:
 | 
						|
        patches.aesthetics.fastText(rom)
 | 
						|
    #if ladxr_settings["textmode"] == 'none':
 | 
						|
    #    patches.aesthetics.fastText(rom)
 | 
						|
    #    patches.aesthetics.noText(rom)
 | 
						|
    if not options["nag_messages"]:
 | 
						|
        patches.aesthetics.removeNagMessages(rom)
 | 
						|
    if options["low_hp_beep"] == Options.LowHpBeep.option_slow:
 | 
						|
        patches.aesthetics.slowLowHPBeep(rom)
 | 
						|
    if options["low_hp_beep"] == Options.LowHpBeep.option_none:
 | 
						|
        patches.aesthetics.removeLowHPBeep(rom)
 | 
						|
    if 0 <= options["link_palette"]:
 | 
						|
        patches.aesthetics.forceLinksPalette(rom, options["link_palette"])
 | 
						|
    if args.romdebugmode:
 | 
						|
        # The default rom has this build in, just need to set a flag and we get this save.
 | 
						|
        rom.patch(0, 0x0003, "00", "01")
 | 
						|
 | 
						|
    # Patch the sword check on the shopkeeper turning around.
 | 
						|
    #if ladxr_settings["steal"] == 'never':
 | 
						|
    #    rom.patch(4, 0x36F9, "FA4EDB", "3E0000")
 | 
						|
    #elif ladxr_settings["steal"] == 'always':
 | 
						|
    #    rom.patch(4, 0x36F9, "FA4EDB", "3E0100")
 | 
						|
 | 
						|
    #if ladxr_settings["hpmode"] == 'inverted':
 | 
						|
    #    patches.health.setStartHealth(rom, 9)
 | 
						|
    #elif ladxr_settings["hpmode"] == '1':
 | 
						|
    #    patches.health.setStartHealth(rom, 1)
 | 
						|
 | 
						|
    patches.inventory.songSelectAfterOcarinaSelect(rom)
 | 
						|
    if options["quickswap"] == 'a':
 | 
						|
        patches.core.quickswap(rom, 1)
 | 
						|
    elif options["quickswap"] == 'b':
 | 
						|
        patches.core.quickswap(rom, 0)
 | 
						|
 | 
						|
    patches.core.addBootsControls(rom, options["boots_controls"])
 | 
						|
 | 
						|
    random.seed(patch_data["seed"] + patch_data["player"])
 | 
						|
    hints.addHints(rom, random, patch_data["hint_texts"])
 | 
						|
 | 
						|
    if patch_data["world_setup"]["goal"] == "raft":
 | 
						|
        patches.goal.setRaftGoal(rom)
 | 
						|
    elif patch_data["world_setup"]["goal"] in ("bingo", "bingo-full"):
 | 
						|
        patches.bingo.setBingoGoal(rom, patch_data["world_setup"]["bingo_goals"], patch_data["world_setup"]["goal"])
 | 
						|
    elif patch_data["world_setup"]["goal"] == "seashells":
 | 
						|
        patches.goal.setSeashellGoal(rom, 20)
 | 
						|
    else:
 | 
						|
        patches.goal.setRequiredInstrumentCount(rom, patch_data["world_setup"]["goal"])
 | 
						|
 | 
						|
    # Patch the generated logic into the rom
 | 
						|
    patches.chest.setMultiChest(rom, patch_data["world_setup"]["multichest"])
 | 
						|
    #if ladxr_settings["overworld"] not in {"dungeondive", "random"}:
 | 
						|
    patches.entrances.changeEntrances(rom, patch_data["world_setup"]["entrance_mapping"])
 | 
						|
    for spot in item_list:
 | 
						|
        if spot.item and spot.item.startswith("*"):
 | 
						|
            spot.item = spot.item[1:]
 | 
						|
        mw = None
 | 
						|
        if spot.item_owner != spot.location_owner:
 | 
						|
            mw = spot.item_owner
 | 
						|
            if mw > 100:
 | 
						|
                # There are only 101 player name slots (99 + "The Server" + "another world"), so don't use more than that
 | 
						|
                mw = 100
 | 
						|
        spot.patch(rom, spot.item, multiworld=mw)
 | 
						|
    patches.enemies.changeBosses(rom, patch_data["world_setup"]["boss_mapping"])
 | 
						|
    patches.enemies.changeMiniBosses(rom, patch_data["world_setup"]["miniboss_mapping"])
 | 
						|
 | 
						|
    if not args.romdebugmode:
 | 
						|
        patches.core.addFrameCounter(rom, len(item_list))
 | 
						|
 | 
						|
    patches.core.warpHome(rom)  # Needs to be done after setting the start location.
 | 
						|
    patches.titleScreen.setRomInfo(rom, patch_data)
 | 
						|
    if options["ap_title_screen"]:
 | 
						|
        patches.titleScreen.setTitleGraphics(rom)
 | 
						|
    patches.endscreen.updateEndScreen(rom)
 | 
						|
    patches.aesthetics.updateSpriteData(rom)
 | 
						|
    if args.doubletrouble:
 | 
						|
        patches.enemies.doubleTrouble(rom)
 | 
						|
 | 
						|
    if options["text_shuffle"]:
 | 
						|
        excluded_ids = [
 | 
						|
            # Overworld owl statues
 | 
						|
            0x1B6, 0x1B7, 0x1B8, 0x1B9, 0x1BA, 0x1BB, 0x1BC, 0x1BD, 0x1BE, 0x22D,
 | 
						|
 | 
						|
            # Dungeon owls
 | 
						|
            0x288, 0x280,  # D1
 | 
						|
            0x28A, 0x289, 0x281,  # D2
 | 
						|
            0x282, 0x28C, 0x28B,  # D3
 | 
						|
            0x283,  # D4
 | 
						|
            0x28D, 0x284,  # D5
 | 
						|
            0x285, 0x28F, 0x28E,  # D6
 | 
						|
            0x291, 0x290, 0x286,  # D7
 | 
						|
            0x293, 0x287, 0x292,  # D8
 | 
						|
            0x263,  # D0
 | 
						|
 | 
						|
            # Hint books
 | 
						|
            0x267,  # color dungeon
 | 
						|
            0x200, 0x201,
 | 
						|
            0x202, 0x203,
 | 
						|
            0x204, 0x205,
 | 
						|
            0x206, 0x207,
 | 
						|
            0x208, 0x209,
 | 
						|
            0x20A, 0x20B,
 | 
						|
            0x20C,
 | 
						|
            0x20D, 0x20E,
 | 
						|
            0x217, 0x218, 0x219, 0x21A,
 | 
						|
 | 
						|
            # Goal sign
 | 
						|
            0x1A3,
 | 
						|
 | 
						|
            # Signpost maze
 | 
						|
            0x1A9, 0x1AA, 0x1AB, 0x1AC, 0x1AD,
 | 
						|
 | 
						|
            # Prices
 | 
						|
            0x02C, 0x02D, 0x030, 0x031, 0x032, 0x033, # Shop items
 | 
						|
            0x03B, # Trendy Game
 | 
						|
            0x045, # Fisherman
 | 
						|
            0x018, 0x019, # Crazy Tracy
 | 
						|
            0x0DC, # Mamu
 | 
						|
            0x0F0, # Raft ride
 | 
						|
        ]
 | 
						|
        excluded_texts = [ rom.texts[excluded_id] for excluded_id in excluded_ids]
 | 
						|
        buckets = defaultdict(list)
 | 
						|
        # For each ROM bank, shuffle text within the bank
 | 
						|
        random.seed(patch_data["seed"] + patch_data["player"])
 | 
						|
        for n, data in enumerate(rom.texts._PointerTable__data):
 | 
						|
            # Don't muck up which text boxes are questions and which are statements
 | 
						|
            if type(data) != int and data and data != b'\xFF' and data not in excluded_texts:
 | 
						|
                buckets[(rom.texts._PointerTable__banks[n], data[len(data) - 1] == 0xfe)].append((n, data))
 | 
						|
        for bucket in buckets.values():
 | 
						|
            # For each bucket, make a copy and shuffle
 | 
						|
            shuffled = bucket.copy()
 | 
						|
            random.shuffle(shuffled)
 | 
						|
            # Then put new text in
 | 
						|
            for bucket_idx, (orig_idx, data) in enumerate(bucket):
 | 
						|
                rom.texts[shuffled[bucket_idx][0]] = data
 | 
						|
 | 
						|
 | 
						|
    if options["trendy_game"] != Options.TrendyGame.option_normal:
 | 
						|
 | 
						|
        # TODO: if 0 or 4, 5, remove inaccurate conveyor tiles
 | 
						|
 | 
						|
 | 
						|
        room_editor = RoomEditor(rom, 0x2A0)
 | 
						|
 | 
						|
        if options["trendy_game"] == Options.TrendyGame.option_easy:
 | 
						|
            # Set physics flag on all objects
 | 
						|
            for i in range(0, 6):
 | 
						|
                rom.banks[0x4][0x6F1E + i -0x4000] = 0x4
 | 
						|
        else:
 | 
						|
            # All levels
 | 
						|
            # Set physics flag on yoshi
 | 
						|
            rom.banks[0x4][0x6F21-0x4000] = 0x3
 | 
						|
            # Add new conveyor to "push" yoshi (it's only a visual)
 | 
						|
            room_editor.objects.append(Object(5, 3, 0xD0))
 | 
						|
 | 
						|
            if options["trendy_game"] >= Options.TrendyGame.option_harder:
 | 
						|
                """
 | 
						|
                Data_004_76A0::
 | 
						|
                    db   $FC, $00, $04, $00, $00
 | 
						|
 | 
						|
                Data_004_76A5::
 | 
						|
                    db   $00, $04, $00, $FC, $00
 | 
						|
                """
 | 
						|
                speeds = {
 | 
						|
                    Options.TrendyGame.option_harder: (3, 8),
 | 
						|
                    Options.TrendyGame.option_hardest: (3, 8),
 | 
						|
                    Options.TrendyGame.option_impossible: (3, 16),
 | 
						|
                }
 | 
						|
                def speed():
 | 
						|
                    random.seed(patch_data["seed"] + patch_data["player"])
 | 
						|
                    return random.randint(*speeds[options["trendy_game"]])
 | 
						|
                rom.banks[0x4][0x76A0-0x4000] = 0xFF - speed()
 | 
						|
                rom.banks[0x4][0x76A2-0x4000] = speed()
 | 
						|
                rom.banks[0x4][0x76A6-0x4000] = speed()
 | 
						|
                rom.banks[0x4][0x76A8-0x4000] = 0xFF - speed()
 | 
						|
                if options["trendy_game"] >= Options.TrendyGame.option_hardest:
 | 
						|
                    rom.banks[0x4][0x76A1-0x4000] = 0xFF - speed()
 | 
						|
                    rom.banks[0x4][0x76A3-0x4000] = speed()
 | 
						|
                    rom.banks[0x4][0x76A5-0x4000] = speed()
 | 
						|
                    rom.banks[0x4][0x76A7-0x4000] = 0xFF - speed()
 | 
						|
 | 
						|
            room_editor.store(rom)
 | 
						|
            # This doesn't work, you can set random conveyors, but they aren't used
 | 
						|
            # for x in range(3, 9):
 | 
						|
            #     for y in range(1, 5):
 | 
						|
            #         room_editor.objects.append(Object(x, y, 0xCF + rnd.randint(0, 3)))
 | 
						|
 | 
						|
    # Attempt at imitating gb palette, fails
 | 
						|
    if False:
 | 
						|
        gb_colors = [
 | 
						|
            [0x0f, 0x38, 0x0f],
 | 
						|
            [0x30, 0x62, 0x30],
 | 
						|
            [0x8b, 0xac, 0x0f],
 | 
						|
            [0x9b, 0xbc, 0x0f],
 | 
						|
        ]
 | 
						|
        for color in gb_colors:
 | 
						|
            for channel in range(3):
 | 
						|
                color[channel] = color[channel] * 31 // 0xbc
 | 
						|
 | 
						|
    if options["warps"] != Options.Warps.option_vanilla:
 | 
						|
        patches.core.addWarpImprovements(rom, options["warps"] == Options.Warps.option_improved_additional)
 | 
						|
 | 
						|
    palette = options["palette"]
 | 
						|
    if palette != Options.Palette.option_normal:
 | 
						|
        ranges = {
 | 
						|
            # Object palettes
 | 
						|
            # Overworld palettes
 | 
						|
            # Dungeon palettes
 | 
						|
            # Interior palettes
 | 
						|
            "code/palettes.asm 1": (0x21, 0x1518, 0x34A0),
 | 
						|
            # Intro/outro(?)
 | 
						|
            # File select
 | 
						|
            # S+Q
 | 
						|
            # Map
 | 
						|
            "code/palettes.asm 2": (0x21, 0x3536, 0x3FFE),
 | 
						|
            # Used for transitioning in and out of forest
 | 
						|
            "backgrounds/palettes.asm": (0x24, 0x3478, 0x3578),
 | 
						|
            # Haven't yet found menu palette
 | 
						|
        }
 | 
						|
 | 
						|
        for name, (bank, start, end) in ranges.items():
 | 
						|
            def clamp(x, min, max):
 | 
						|
                if x < min:
 | 
						|
                    return min
 | 
						|
                if x > max:
 | 
						|
                    return max
 | 
						|
                return x
 | 
						|
 | 
						|
            for address in range(start, end, 2):
 | 
						|
                packed = (rom.banks[bank][address + 1] << 8) | rom.banks[bank][address]
 | 
						|
                r,g,b = bin_to_rgb(packed)
 | 
						|
 | 
						|
                # 1 bit
 | 
						|
                if palette == Options.Palette.option_1bit:
 | 
						|
                    r &= 0b10000
 | 
						|
                    g &= 0b10000
 | 
						|
                    b &= 0b10000
 | 
						|
                # 2 bit
 | 
						|
                elif palette == Options.Palette.option_1bit:
 | 
						|
                    r &= 0b11000
 | 
						|
                    g &= 0b11000
 | 
						|
                    b &= 0b11000
 | 
						|
                # Invert
 | 
						|
                elif palette == Options.Palette.option_inverted:
 | 
						|
                    r = 31 - r
 | 
						|
                    g = 31 - g
 | 
						|
                    b = 31 - b
 | 
						|
                # Pink
 | 
						|
                elif palette == Options.Palette.option_pink:
 | 
						|
                    r = r // 2
 | 
						|
                    r += 16
 | 
						|
                    r = int(r)
 | 
						|
                    r = clamp(r, 0, 0x1F)
 | 
						|
                    b = b // 2
 | 
						|
                    b += 16
 | 
						|
                    b = int(b)
 | 
						|
                    b = clamp(b, 0, 0x1F)
 | 
						|
                elif palette == Options.Palette.option_greyscale:
 | 
						|
                    # gray=int(0.299*r+0.587*g+0.114*b)
 | 
						|
                    gray = (r + g + b) // 3
 | 
						|
                    r = g = b = gray
 | 
						|
 | 
						|
                packed = rgb_to_bin(r, g, b)
 | 
						|
                rom.banks[bank][address] = packed & 0xFF
 | 
						|
                rom.banks[bank][address + 1] = packed >> 8
 | 
						|
 | 
						|
    SEED_LOCATION = 0x0134
 | 
						|
    # Patch over the title
 | 
						|
    assert(len(multi_key) == 12)
 | 
						|
    rom.patch(0x00, SEED_LOCATION, None, binascii.hexlify(multi_key))
 | 
						|
 | 
						|
    for pymod in pymods:
 | 
						|
        pymod.postPatch(rom)
 | 
						|
 | 
						|
    return rom.save()
 |