Introduce classes for Rom patching to enable json patch output for VT integration. Remove halfassed door frame overlay fixes (solved in randomizer rom now), make bonk prize shuffle optional.
This commit is contained in:
parent
c3854f4d2d
commit
83c448f14d
|
@ -34,7 +34,6 @@ class World(object):
|
||||||
self.clock_mode = 'off'
|
self.clock_mode = 'off'
|
||||||
self.aga_randomness = False
|
self.aga_randomness = False
|
||||||
self.lock_aga_door_in_escape = False
|
self.lock_aga_door_in_escape = False
|
||||||
self.fix_door_frames = self.shuffle not in ['vanilla', 'dungeonssimple', 'dungeonsfull']
|
|
||||||
self.fix_trock_doors = self.shuffle != 'vanilla'
|
self.fix_trock_doors = self.shuffle != 'vanilla'
|
||||||
self.save_and_quite_from_boss = False
|
self.save_and_quite_from_boss = False
|
||||||
self.check_beatable_only = check_beatable_only
|
self.check_beatable_only = check_beatable_only
|
||||||
|
|
13
Main.py
13
Main.py
|
@ -1,7 +1,7 @@
|
||||||
from BaseClasses import World, CollectionState, Item
|
from BaseClasses import World, CollectionState, Item
|
||||||
from Regions import create_regions
|
from Regions import create_regions
|
||||||
from EntranceShuffle import link_entrances
|
from EntranceShuffle import link_entrances
|
||||||
from Rom import patch_rom, patch_base_rom
|
from Rom import patch_rom, LocalRom, JsonRom
|
||||||
from Rules import set_rules
|
from Rules import set_rules
|
||||||
from Dungeons import fill_dungeons
|
from Dungeons import fill_dungeons
|
||||||
from Items import ItemFactory
|
from Items import ItemFactory
|
||||||
|
@ -87,11 +87,12 @@ def main(args, seed=None):
|
||||||
outfilebase = 'ER_%s_%s_%s_%s_%s_%s' % (world.mode, world.goal, world.shuffle, world.difficulty, world.algorithm, world.seed)
|
outfilebase = 'ER_%s_%s_%s_%s_%s_%s' % (world.mode, world.goal, world.shuffle, world.difficulty, world.algorithm, world.seed)
|
||||||
|
|
||||||
if not args.suppress_rom:
|
if not args.suppress_rom:
|
||||||
rom = bytearray(open(args.rom, 'rb').read())
|
if args.jsonout:
|
||||||
patch_base_rom(rom)
|
rom = JsonRom()
|
||||||
patched_rom = patch_rom(world, rom, bytearray(logic_hash), args.quickswap, args.heartbeep, sprite)
|
else:
|
||||||
with open('%s.sfc' % outfilebase, 'wb') as outfile:
|
rom = LocalRom(args.rom)
|
||||||
outfile.write(patched_rom)
|
patch_rom(world, rom, bytearray(logic_hash), args.quickswap, args.heartbeep, sprite)
|
||||||
|
rom.write_to_file(args.jsonout or '%s.sfc' % outfilebase)
|
||||||
|
|
||||||
if args.create_spoiler:
|
if args.create_spoiler:
|
||||||
with open('%s_Spoiler.txt' % outfilebase, 'w') as outfile:
|
with open('%s_Spoiler.txt' % outfilebase, 'w') as outfile:
|
||||||
|
|
13
Plando.py
13
Plando.py
|
@ -1,7 +1,7 @@
|
||||||
from BaseClasses import World
|
from BaseClasses import World
|
||||||
from Regions import create_regions
|
from Regions import create_regions
|
||||||
from EntranceShuffle import link_entrances, connect_entrance, connect_two_way, connect_exit
|
from EntranceShuffle import link_entrances, connect_entrance, connect_two_way, connect_exit
|
||||||
from Rom import patch_rom, patch_base_rom, write_string_to_rom, write_credits_string_to_rom
|
from Rom import patch_rom, LocalRom, write_string_to_rom, write_credits_string_to_rom
|
||||||
from Rules import set_rules
|
from Rules import set_rules
|
||||||
from Items import ItemFactory
|
from Items import ItemFactory
|
||||||
from Main import create_playthrough
|
from Main import create_playthrough
|
||||||
|
@ -79,9 +79,8 @@ def main(args, seed=None):
|
||||||
else:
|
else:
|
||||||
sprite = None
|
sprite = None
|
||||||
|
|
||||||
rom = bytearray(open(args.rom, 'rb').read())
|
rom = LocalRom(args.rom)
|
||||||
patch_base_rom(rom)
|
patch_rom(world, rom, logic_hash, args.quickswap, args.heartbeep, sprite)
|
||||||
patched_rom = patch_rom(world, rom, logic_hash, args.quickswap, args.heartbeep, sprite)
|
|
||||||
|
|
||||||
for textname, texttype, text in text_patches:
|
for textname, texttype, text in text_patches:
|
||||||
if texttype == 'text':
|
if texttype == 'text':
|
||||||
|
@ -91,8 +90,7 @@ def main(args, seed=None):
|
||||||
|
|
||||||
outfilebase = 'Plando_%s_%s' % (os.path.splitext(os.path.basename(args.plando))[0], world.seed)
|
outfilebase = 'Plando_%s_%s' % (os.path.splitext(os.path.basename(args.plando))[0], world.seed)
|
||||||
|
|
||||||
with open('%s.sfc' % outfilebase, 'wb') as outfile:
|
rom.write_to_file('%s.sfc' % outfilebase)
|
||||||
outfile.write(patched_rom)
|
|
||||||
if args.create_spoiler:
|
if args.create_spoiler:
|
||||||
with open('%s_Spoiler.txt' % outfilebase, 'w') as outfile:
|
with open('%s_Spoiler.txt' % outfilebase, 'w') as outfile:
|
||||||
outfile.write(world.spoiler)
|
outfile.write(world.spoiler)
|
||||||
|
@ -140,9 +138,6 @@ def fill_world(world, plando, text_patches):
|
||||||
elif line.startswith('!light_cone_dw'):
|
elif line.startswith('!light_cone_dw'):
|
||||||
_, dwconestr = line.split(':', 1)
|
_, dwconestr = line.split(':', 1)
|
||||||
world.dark_world_light_cone = dwconestr.strip().lower() == 'true'
|
world.dark_world_light_cone = dwconestr.strip().lower() == 'true'
|
||||||
elif line.startswith('!fix_door_frames'):
|
|
||||||
_, dfstring = line.split(':', 1)
|
|
||||||
world.fix_door_frames = dfstring.strip().lower() == 'true'
|
|
||||||
elif line.startswith('!fix_trock_doors'):
|
elif line.startswith('!fix_trock_doors'):
|
||||||
_, trdstr = line.split(':', 1)
|
_, trdstr = line.split(':', 1)
|
||||||
world.fix_trock_doors = trdstr.strip().lower() == 'true'
|
world.fix_trock_doors = trdstr.strip().lower() == 'true'
|
||||||
|
|
408
Rom.py
408
Rom.py
|
@ -11,6 +11,68 @@ JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = '89fcdb48446bd858878f14e8a994d0b8'
|
RANDOMIZERBASEHASH = '89fcdb48446bd858878f14e8a994d0b8'
|
||||||
|
|
||||||
|
|
||||||
|
class JsonRom(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.patches = {}
|
||||||
|
|
||||||
|
def write_byte(self, address, value):
|
||||||
|
self.patches[str(address)] = [value]
|
||||||
|
|
||||||
|
def write_bytes(self, startaddress, values):
|
||||||
|
self.patches[str(startaddress)] = list(values)
|
||||||
|
|
||||||
|
def write_to_file(self, file):
|
||||||
|
json.dump([self.patches], open(file, 'w'))
|
||||||
|
|
||||||
|
|
||||||
|
class LocalRom(object):
|
||||||
|
|
||||||
|
def __init__(self, file):
|
||||||
|
self.buffer = bytearray(open(file, 'rb').read())
|
||||||
|
self.patch_base_rom()
|
||||||
|
|
||||||
|
def write_byte(self, address, value):
|
||||||
|
self.buffer[address] = value
|
||||||
|
|
||||||
|
def write_bytes(self, startaddress, values):
|
||||||
|
for i, value in enumerate(values):
|
||||||
|
self.write_byte(startaddress + i, value)
|
||||||
|
|
||||||
|
def write_to_file(self, file):
|
||||||
|
with open(file, 'wb') as outfile:
|
||||||
|
outfile.write(self.buffer)
|
||||||
|
|
||||||
|
def patch_base_rom(self):
|
||||||
|
# verify correct checksum of baserom
|
||||||
|
basemd5 = hashlib.md5()
|
||||||
|
basemd5.update(self.buffer)
|
||||||
|
if not JAP10HASH == basemd5.hexdigest():
|
||||||
|
logging.getLogger('').warning('Supplied Base Rom does not match known MD5 for JAP(1.0) release. Will try to patch anyway.')
|
||||||
|
|
||||||
|
# extend to 2MB
|
||||||
|
self.buffer.extend(bytearray([0x00] * (2097152 - len(self.buffer))))
|
||||||
|
|
||||||
|
# load randomizer patches
|
||||||
|
patches = json.load(open('base2current.json', 'r'))
|
||||||
|
for patch in patches:
|
||||||
|
if isinstance(patch, dict):
|
||||||
|
for baseaddress, values in patch.items():
|
||||||
|
self.write_bytes(int(baseaddress), values)
|
||||||
|
|
||||||
|
# verify md5
|
||||||
|
patchedmd5 = hashlib.md5()
|
||||||
|
patchedmd5.update(self.buffer)
|
||||||
|
if not RANDOMIZERBASEHASH == patchedmd5.hexdigest():
|
||||||
|
raise RuntimeError('Provided Base Rom unsuitable for patching. Please provide a JAP(1.0) "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc" rom to use as a base.')
|
||||||
|
|
||||||
|
def write_crc(self):
|
||||||
|
# this does not seem to work
|
||||||
|
crc = sum(self.buffer[:0x7FEB] + self.buffer[0x7FF5:]) % 0xFFFF
|
||||||
|
inv = crc ^ 0xFFFF
|
||||||
|
self.write_bytes(0x7FDC, [inv & 0xFF, (inv >> 8) & 0xFF, crc & 0xFF, (crc >> 8) & 0xFF])
|
||||||
|
|
||||||
|
|
||||||
def patch_rom(world, rom, hashtable, quickswap=False, beep='normal', sprite=None):
|
def patch_rom(world, rom, hashtable, quickswap=False, beep='normal', sprite=None):
|
||||||
# patch items
|
# patch items
|
||||||
for location in world.get_locations():
|
for location in world.get_locations():
|
||||||
|
@ -22,20 +84,17 @@ def patch_rom(world, rom, hashtable, quickswap=False, beep='normal', sprite=None
|
||||||
locationaddress = location.address
|
locationaddress = location.address
|
||||||
if not location.crystal:
|
if not location.crystal:
|
||||||
# regular items
|
# regular items
|
||||||
write_byte(rom, locationaddress, itemid)
|
rom.write_byte(locationaddress, itemid)
|
||||||
else:
|
else:
|
||||||
# crystals
|
# crystals
|
||||||
for address, value in zip(locationaddress, itemid):
|
for address, value in zip(locationaddress, itemid):
|
||||||
write_byte(rom, address, value)
|
rom.write_byte(address, value)
|
||||||
|
|
||||||
# patch music
|
# patch music
|
||||||
music_addresses = dungeon_music_addresses[location.name]
|
music_addresses = dungeon_music_addresses[location.name]
|
||||||
music = 0x11 if 'Pendant' in location.item.name else 0x16
|
music = 0x11 if 'Pendant' in location.item.name else 0x16
|
||||||
for music_address in music_addresses:
|
for music_address in music_addresses:
|
||||||
write_byte(rom, music_address, music)
|
rom.write_byte(music_address, music)
|
||||||
|
|
||||||
# store old door overlay table
|
|
||||||
door_overlays = bytearray(rom[0x15488:0x15488+0x10A])
|
|
||||||
|
|
||||||
# patch entrances
|
# patch entrances
|
||||||
for region in world.regions:
|
for region in world.regions:
|
||||||
|
@ -43,87 +102,76 @@ def patch_rom(world, rom, hashtable, quickswap=False, beep='normal', sprite=None
|
||||||
if exit.target is not None:
|
if exit.target is not None:
|
||||||
addresses = [exit.addresses] if isinstance(exit.addresses, int) else exit.addresses
|
addresses = [exit.addresses] if isinstance(exit.addresses, int) else exit.addresses
|
||||||
for address in addresses:
|
for address in addresses:
|
||||||
write_byte(rom, address, exit.target)
|
rom.write_byte(address, exit.target)
|
||||||
|
|
||||||
# this does not yet seem to fix our door headaches ...
|
|
||||||
if world.fix_door_frames and exit.vanilla is not None:
|
|
||||||
# patch door overlay table. The value of where this now leads is patched into the location where this entrance would lead in vanilla
|
|
||||||
write_byte(rom, 0x15488 + (2*exit.vanilla), door_overlays[2*exit.target])
|
|
||||||
write_byte(rom, 0x15489 + (2 * exit.vanilla), door_overlays[(2 * exit.target) + 1])
|
|
||||||
|
|
||||||
# patch medallion requirements
|
# patch medallion requirements
|
||||||
if world.required_medallions[0] == 'Bombos':
|
if world.required_medallions[0] == 'Bombos':
|
||||||
write_byte(rom, 0x180022, 0x00) # requirement
|
rom.write_byte(0x180022, 0x00) # requirement
|
||||||
write_byte(rom, 0x4FF2, 0x31) # sprite
|
rom.write_byte(0x4FF2, 0x31) # sprite
|
||||||
write_byte(rom, 0x50D1, 0x80)
|
rom.write_byte(0x50D1, 0x80)
|
||||||
write_byte(rom, 0x51B0, 0x00)
|
rom.write_byte(0x51B0, 0x00)
|
||||||
elif world.required_medallions[0] == 'Quake':
|
elif world.required_medallions[0] == 'Quake':
|
||||||
write_byte(rom, 0x180022, 0x02) # requirement
|
rom.write_byte(0x180022, 0x02) # requirement
|
||||||
write_byte(rom, 0x4FF2, 0x31) # sprite
|
rom.write_byte(0x4FF2, 0x31) # sprite
|
||||||
write_byte(rom, 0x50D1, 0x88)
|
rom.write_byte(0x50D1, 0x88)
|
||||||
write_byte(rom, 0x51B0, 0x00)
|
rom.write_byte(0x51B0, 0x00)
|
||||||
if world.required_medallions[1] == 'Bombos':
|
if world.required_medallions[1] == 'Bombos':
|
||||||
write_byte(rom, 0x180023, 0x00) # requirement
|
rom.write_byte(0x180023, 0x00) # requirement
|
||||||
write_byte(rom, 0x5020, 0x31) # sprite
|
rom.write_byte(0x5020, 0x31) # sprite
|
||||||
write_byte(rom, 0x50FF, 0x90)
|
rom.write_byte(0x50FF, 0x90)
|
||||||
write_byte(rom, 0x51DE, 0x00)
|
rom.write_byte(0x51DE, 0x00)
|
||||||
elif world.required_medallions[1] == 'Ether':
|
elif world.required_medallions[1] == 'Ether':
|
||||||
write_byte(rom, 0x180023, 0x01) # requirement
|
rom.write_byte(0x180023, 0x01) # requirement
|
||||||
write_byte(rom, 0x5020, 0x31) # sprite
|
rom.write_byte(0x5020, 0x31) # sprite
|
||||||
write_byte(rom, 0x50FF, 0x98)
|
rom.write_byte(0x50FF, 0x98)
|
||||||
write_byte(rom, 0x51DE, 0x00)
|
rom.write_byte(0x51DE, 0x00)
|
||||||
|
|
||||||
# set open mode:
|
# set open mode:
|
||||||
if world.mode in ['open', 'swordless']:
|
if world.mode in ['open', 'swordless']:
|
||||||
write_byte(rom, 0x180032, 0x01) # open mode
|
rom.write_byte(0x180032, 0x01) # open mode
|
||||||
|
|
||||||
# disable sword sprite from uncle
|
# disable sword sprite from uncle
|
||||||
write_bytes(rom, 0x6D263, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
rom.write_bytes(0x6D263, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||||
write_bytes(rom, 0x6D26B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
rom.write_bytes(0x6D26B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||||
write_bytes(rom, 0x6D293, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
rom.write_bytes(0x6D293, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||||
write_bytes(rom, 0x6D29B, [0x00, 0x00, 0xf7, 0xff, 0x00, 0x0E])
|
rom.write_bytes(0x6D29B, [0x00, 0x00, 0xf7, 0xff, 0x00, 0x0E])
|
||||||
write_bytes(rom, 0x6D2B3, [0x00, 0x00, 0xf6, 0xff, 0x02, 0x0E])
|
rom.write_bytes(0x6D2B3, [0x00, 0x00, 0xf6, 0xff, 0x02, 0x0E])
|
||||||
write_bytes(rom, 0x6D2BB, [0x00, 0x00, 0xf6, 0xff, 0x02, 0x0E])
|
rom.write_bytes(0x6D2BB, [0x00, 0x00, 0xf6, 0xff, 0x02, 0x0E])
|
||||||
write_bytes(rom, 0x6D2E3, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
|
rom.write_bytes(0x6D2E3, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
|
||||||
write_bytes(rom, 0x6D2EB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
|
rom.write_bytes(0x6D2EB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
|
||||||
write_bytes(rom, 0x6D31B, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
|
rom.write_bytes(0x6D31B, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
|
||||||
write_bytes(rom, 0x6D323, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
|
rom.write_bytes(0x6D323, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
|
||||||
else:
|
else:
|
||||||
write_byte(rom, 0x180032, 0x00) # standard mode
|
rom.write_byte(0x180032, 0x00) # standard mode
|
||||||
|
|
||||||
# set light cones
|
# set light cones
|
||||||
write_byte(rom, 0x180038, 0x01 if world.sewer_light_cone else 0x00)
|
rom.write_byte(0x180038, 0x01 if world.sewer_light_cone else 0x00)
|
||||||
write_byte(rom, 0x180039, 0x01 if world.light_world_light_cone else 0x00)
|
rom.write_byte(0x180039, 0x01 if world.light_world_light_cone else 0x00)
|
||||||
write_byte(rom, 0x18003A, 0x01 if world.dark_world_light_cone else 0x00)
|
rom.write_byte(0x18003A, 0x01 if world.dark_world_light_cone else 0x00)
|
||||||
|
|
||||||
# disable light world cane in minor glitches
|
|
||||||
if world.logic == 'minorglitches':
|
|
||||||
write_byte(rom, 0x180039, 0x00) # light world light cone disable
|
|
||||||
write_byte(rom, 0x18003A, 0x00) # dark world light cone disable
|
|
||||||
|
|
||||||
# handle difficulty
|
# handle difficulty
|
||||||
if world.difficulty == 'hard':
|
if world.difficulty == 'hard':
|
||||||
# Spike Cave Damage
|
# Spike Cave Damage
|
||||||
write_byte(rom, 0x180168, 0x02)
|
rom.write_byte(0x180168, 0x02)
|
||||||
# Powdered Fairies Prize
|
# Powdered Fairies Prize
|
||||||
write_byte(rom, 0x36DD0, 0x79) # Bee
|
rom.write_byte(0x36DD0, 0x79) # Bee
|
||||||
# potion heal amount
|
# potion heal amount
|
||||||
write_byte(rom, 0x180084, 0x08) # One Heart
|
rom.write_byte(0x180084, 0x08) # One Heart
|
||||||
# potion magic restore amount
|
# potion magic restore amount
|
||||||
write_byte(rom, 0x180085, 0x20) # Quarter Magic
|
rom.write_byte(0x180085, 0x20) # Quarter Magic
|
||||||
else:
|
else:
|
||||||
# Spike Cave Damage
|
# Spike Cave Damage
|
||||||
write_byte(rom, 0x180168, 0x08)
|
rom.write_byte(0x180168, 0x08)
|
||||||
# Powdered Fairies Prize
|
# Powdered Fairies Prize
|
||||||
write_byte(rom, 0x36DD0, 0xE3) # fairy
|
rom.write_byte(0x36DD0, 0xE3) # fairy
|
||||||
# potion heal amount
|
# potion heal amount
|
||||||
write_byte(rom, 0x180084, 0xA0) # full
|
rom.write_byte(0x180084, 0xA0) # full
|
||||||
# potion magic restore amount
|
# potion magic restore amount
|
||||||
write_byte(rom, 0x180085, 0x80) # full
|
rom.write_byte(0x180085, 0x80) # full
|
||||||
|
|
||||||
# set up game internal RNG seed
|
# set up game internal RNG seed
|
||||||
for i in range(1024):
|
for i in range(1024):
|
||||||
write_byte(rom, 0x178000 + i, random.randint(0, 255))
|
rom.write_byte(0x178000 + i, random.randint(0, 255))
|
||||||
|
|
||||||
# shuffle prize packs
|
# shuffle prize packs
|
||||||
prizes = [0xD8, 0xD8, 0xD8, 0xD8, 0xD9, 0xD8, 0xD8, 0xD9, 0xDA, 0xD9, 0xDA, 0xDB, 0xDA, 0xD9, 0xDA, 0xDA, 0xE0, 0xDF, 0xDF, 0xDA, 0xE0, 0xDF, 0xD8, 0xDF,
|
prizes = [0xD8, 0xD8, 0xD8, 0xD8, 0xD9, 0xD8, 0xD8, 0xD9, 0xDA, 0xD9, 0xDA, 0xDB, 0xDA, 0xD9, 0xDA, 0xDA, 0xE0, 0xDF, 0xDF, 0xDA, 0xE0, 0xDF, 0xD8, 0xDF,
|
||||||
|
@ -132,22 +180,22 @@ def patch_rom(world, rom, hashtable, quickswap=False, beep='normal', sprite=None
|
||||||
random.shuffle(prizes)
|
random.shuffle(prizes)
|
||||||
|
|
||||||
# write tree pull prizes
|
# write tree pull prizes
|
||||||
write_byte(rom, 0xEFBD4, prizes.pop())
|
rom.write_byte(0xEFBD4, prizes.pop())
|
||||||
write_byte(rom, 0xEFBD5, prizes.pop())
|
rom.write_byte(0xEFBD5, prizes.pop())
|
||||||
write_byte(rom, 0xEFBD6, prizes.pop())
|
rom.write_byte(0xEFBD6, prizes.pop())
|
||||||
|
|
||||||
# rupee crab prizes
|
# rupee crab prizes
|
||||||
write_byte(rom, 0x329C8, prizes.pop()) # first prize
|
rom.write_byte(0x329C8, prizes.pop()) # first prize
|
||||||
write_byte(rom, 0x329C4, prizes.pop()) # final prize
|
rom.write_byte(0x329C4, prizes.pop()) # final prize
|
||||||
|
|
||||||
# stunned enemy prize
|
# stunned enemy prize
|
||||||
write_byte(rom, 0x37993, prizes.pop())
|
rom.write_byte(0x37993, prizes.pop())
|
||||||
|
|
||||||
# saved fish prize
|
# saved fish prize
|
||||||
write_byte(rom, 0xE82CC, prizes.pop())
|
rom.write_byte(0xE82CC, prizes.pop())
|
||||||
|
|
||||||
# fill enemy prize packs
|
# fill enemy prize packs
|
||||||
write_bytes(rom, 0x37A78, prizes)
|
rom.write_bytes(0x37A78, prizes)
|
||||||
|
|
||||||
# prize pack drop chances
|
# prize pack drop chances
|
||||||
if world.difficulty == 'hard':
|
if world.difficulty == 'hard':
|
||||||
|
@ -156,140 +204,138 @@ def patch_rom(world, rom, hashtable, quickswap=False, beep='normal', sprite=None
|
||||||
droprates = [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01] # 50%
|
droprates = [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01] # 50%
|
||||||
|
|
||||||
random.shuffle(droprates)
|
random.shuffle(droprates)
|
||||||
write_bytes(rom, 0x37A62, droprates)
|
rom.write_bytes(0x37A62, droprates)
|
||||||
|
|
||||||
|
vanilla_prize_pack_assignment = [131, 150, 132, 128, 128, 128, 128, 128, 2, 0, 2, 128, 160, 131, 151, 128, 128, 148, 145, 7, 0, 128, 0, 128, 146, 150, 128, 160, 0, 0, 0, 128, 4, 128,
|
||||||
|
130, 6, 6, 0, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 128, 128, 144, 128, 145, 145,
|
||||||
|
145, 151, 145, 149, 149, 147, 151, 20, 145, 146, 129, 130, 130, 128, 133, 128, 128, 128, 4, 4, 128, 145, 128, 128, 128, 128, 128, 128, 128, 128, 0, 128,
|
||||||
|
128, 130, 138, 128, 128, 128, 128, 146, 145, 128, 130, 129, 129, 128, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 151, 128, 128, 128, 128, 194,
|
||||||
|
128, 21, 21, 23, 6, 0, 128, 0, 192, 19, 64, 0, 2, 6, 16, 20, 0, 0, 64, 0, 0, 0, 0, 19, 70, 17, 128, 128, 0, 0, 0, 16, 0, 0, 0, 22, 22, 22, 129, 135, 130,
|
||||||
|
0, 128, 128, 0, 0, 0, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 23, 0, 18, 0, 0, 0, 0, 0, 16, 23, 0, 64, 1, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0]
|
||||||
|
|
||||||
# shuffle enemies to prize packs
|
# shuffle enemies to prize packs
|
||||||
for i in range(243):
|
for i in range(243):
|
||||||
if rom[0x6B632 + i] & 0x0F != 0x00:
|
if vanilla_prize_pack_assignment[i] & 0x0F != 0x00:
|
||||||
rom[0x6B632 + i] = (rom[0x6B632 + i] & 0xF0) | random.randint(1, 7)
|
rom.write_byte(0x6B632 + i, (vanilla_prize_pack_assignment[i] & 0xF0) | random.randint(1, 7))
|
||||||
|
|
||||||
# set bonk prizes
|
# set bonk prizes
|
||||||
bonk_prizes = [0x79, 0xE3, 0x79, 0xAC, 0xAC, 0xE0, 0xDC, 0xAC, 0xE3, 0xE3, 0xDA, 0xE3, 0xDA, 0xD8, 0xAC, 0xAC, 0xE3, 0xD8, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xDC, 0xDB, 0xE3, 0xDA, 0x79, 0x79, 0xE3, 0xE3,
|
if world.shuffle_bonk_prizes:
|
||||||
0xDA, 0x79, 0xAC, 0xAC, 0x79, 0xE3, 0x79, 0xAC, 0xAC, 0xE0, 0xDC, 0xE3, 0x79, 0xDE, 0xE3, 0xAC, 0xDB, 0x79, 0xE3, 0xD8, 0xAC, 0x79, 0xE3, 0xDB, 0xDB, 0xE3, 0xE3, 0x79, 0xD8, 0xDD]
|
bonk_prizes = [0x79, 0xE3, 0x79, 0xAC, 0xAC, 0xE0, 0xDC, 0xAC, 0xE3, 0xE3, 0xDA, 0xE3, 0xDA, 0xD8, 0xAC, 0xAC, 0xE3, 0xD8, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xDC, 0xDB, 0xE3, 0xDA, 0x79, 0x79, 0xE3, 0xE3,
|
||||||
bonk_addresses = [0x4CF6C, 0x4CFBA, 0x4CFE0, 0x4CFFB, 0x4D018, 0x4D01B, 0x4D028, 0x4D03C, 0x4D059, 0x4D07A, 0x4D09E, 0x4D0A8, 0x4D0AB, 0x4D0AE, 0x4D0BE, 0x4D0DD,
|
0xDA, 0x79, 0xAC, 0xAC, 0x79, 0xE3, 0x79, 0xAC, 0xAC, 0xE0, 0xDC, 0xE3, 0x79, 0xDE, 0xE3, 0xAC, 0xDB, 0x79, 0xE3, 0xD8, 0xAC, 0x79, 0xE3, 0xDB, 0xDB, 0xE3, 0xE3, 0x79, 0xD8, 0xDD]
|
||||||
0x4D16A, 0x4D1E5, 0x4D1EE, 0x4D20B, 0x4CBBF, 0x4CBBF, 0x4CC17, 0x4CC1A, 0x4CC4A, 0x4CC4D, 0x4CC53, 0x4CC69, 0x4CC6F, 0x4CC7C, 0x4CCEF, 0x4CD51,
|
bonk_addresses = [0x4CF6C, 0x4CFBA, 0x4CFE0, 0x4CFFB, 0x4D018, 0x4D01B, 0x4D028, 0x4D03C, 0x4D059, 0x4D07A, 0x4D09E, 0x4D0A8, 0x4D0AB, 0x4D0AE, 0x4D0BE, 0x4D0DD,
|
||||||
0x4CDC0, 0x4CDC3, 0x4CDC6, 0x4CE37, 0x4D2DE, 0x4D32F, 0x4D355, 0x4D367, 0x4D384, 0x4D387, 0x4D397, 0x4D39E, 0x4D3AB, 0x4D3AE, 0x4D3D1, 0x4D3D7,
|
0x4D16A, 0x4D1E5, 0x4D1EE, 0x4D20B, 0x4CBBF, 0x4CBBF, 0x4CC17, 0x4CC1A, 0x4CC4A, 0x4CC4D, 0x4CC53, 0x4CC69, 0x4CC6F, 0x4CC7C, 0x4CCEF, 0x4CD51,
|
||||||
0x4D3F8, 0x4D416, 0x4D420, 0x4D423, 0x4D42D, 0x4D449, 0x4D48C, 0x4D4D9, 0x4D4DC, 0x4D4E3, 0x4D504, 0x4D507, 0x4D55E, 0x4D56A]
|
0x4CDC0, 0x4CDC3, 0x4CDC6, 0x4CE37, 0x4D2DE, 0x4D32F, 0x4D355, 0x4D367, 0x4D384, 0x4D387, 0x4D397, 0x4D39E, 0x4D3AB, 0x4D3AE, 0x4D3D1, 0x4D3D7,
|
||||||
random.shuffle(bonk_prizes)
|
0x4D3F8, 0x4D416, 0x4D420, 0x4D423, 0x4D42D, 0x4D449, 0x4D48C, 0x4D4D9, 0x4D4DC, 0x4D4E3, 0x4D504, 0x4D507, 0x4D55E, 0x4D56A]
|
||||||
for prize, address in zip(bonk_prizes, bonk_addresses):
|
random.shuffle(bonk_prizes)
|
||||||
write_byte(rom, address, prize)
|
for prize, address in zip(bonk_prizes, bonk_addresses):
|
||||||
|
rom.write_byte(address, prize)
|
||||||
|
|
||||||
# set Fountain bottle exchange items
|
# set Fountain bottle exchange items
|
||||||
write_byte(rom, 0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][random.randint(0, 6)])
|
rom.write_byte(0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][random.randint(0, 6)])
|
||||||
write_byte(rom, 0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][random.randint(0, 6)])
|
rom.write_byte(0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][random.randint(0, 6)])
|
||||||
# set Fat Fairy Bow/Sword prizes to be disappointing
|
# set Fat Fairy Bow/Sword prizes to be disappointing
|
||||||
write_byte(rom, 0x34914, 0x3A) # Bow and Arrow
|
rom.write_byte(0x34914, 0x3A) # Bow and Arrow
|
||||||
write_byte(rom, 0x180028, 0x49) # Fighter Sword
|
rom.write_byte(0x180028, 0x49) # Fighter Sword
|
||||||
|
|
||||||
# set swordless mode settings
|
# set swordless mode settings
|
||||||
write_byte(rom, 0x18003F, 0x01 if world.mode == 'swordless' else 0x00) # hammer can harm ganon
|
rom.write_byte(0x18003F, 0x01 if world.mode == 'swordless' else 0x00) # hammer can harm ganon
|
||||||
write_byte(rom, 0x180040, 0x01 if world.mode == 'swordless' else 0x00) # open curtains
|
rom.write_byte(0x180040, 0x01 if world.mode == 'swordless' else 0x00) # open curtains
|
||||||
write_byte(rom, 0x180041, 0x01 if world.mode == 'swordless' else 0x00) # swordless medallions
|
rom.write_byte(0x180041, 0x01 if world.mode == 'swordless' else 0x00) # swordless medallions
|
||||||
write_byte(rom, 0x180042, 0xFF if world.mode == 'swordless' else 0x00) # starting sword for link
|
rom.write_byte(0x180042, 0xFF if world.mode == 'swordless' else 0x00) # starting sword for link
|
||||||
|
|
||||||
# set up clocks for timed modes
|
# set up clocks for timed modes
|
||||||
if world.clock_mode == 'off':
|
if world.clock_mode == 'off':
|
||||||
write_bytes(rom, 0x180190, [0x00, 0x00, 0x00]) # turn off clock mode
|
rom.write_bytes(0x180190, [0x00, 0x00, 0x00]) # turn off clock mode
|
||||||
write_bytes(rom, 0x180200, [0x00, 0x00, 0x00, 0x00]) # red clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180200, [0x00, 0x00, 0x00, 0x00]) # red clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x180204, [0x00, 0x00, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180204, [0x00, 0x00, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x180208, [0x00, 0x00, 0x00, 0x00]) # green clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180208, [0x00, 0x00, 0x00, 0x00]) # green clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x18020C, [0x00, 0x00, 0x00, 0x00]) # starting time (in frames, sint32)
|
rom.write_bytes(0x18020C, [0x00, 0x00, 0x00, 0x00]) # starting time (in frames, sint32)
|
||||||
elif world.clock_mode == 'ohko':
|
elif world.clock_mode == 'ohko':
|
||||||
write_bytes(rom, 0x180190, [0x01, 0x02, 0x01]) # ohko timer with resetable timer functionality
|
rom.write_bytes(0x180190, [0x01, 0x02, 0x01]) # ohko timer with resetable timer functionality
|
||||||
write_bytes(rom, 0x180200, [0x00, 0x00, 0x00, 0x00]) # red clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180200, [0x00, 0x00, 0x00, 0x00]) # red clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x180204, [0x00, 0x00, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180204, [0x00, 0x00, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x180208, [0x50, 0x46, 0x00, 0x00]) # green clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180208, [0x50, 0x46, 0x00, 0x00]) # green clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x18020C, [0xA0, 0x8C, 0x00, 0x00]) # starting time (in frames, sint32)
|
rom.write_bytes(0x18020C, [0xA0, 0x8C, 0x00, 0x00]) # starting time (in frames, sint32)
|
||||||
if world.clock_mode == 'stopwatch':
|
if world.clock_mode == 'stopwatch':
|
||||||
write_bytes(rom, 0x180190, [0x02, 0x01, 0x00]) # set stopwatch mode
|
rom.write_bytes(0x180190, [0x02, 0x01, 0x00]) # set stopwatch mode
|
||||||
write_bytes(rom, 0x180200, [0xE0, 0xE3, 0xFF, 0xFF]) # red clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180200, [0xE0, 0xE3, 0xFF, 0xFF]) # red clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x180204, [0x20, 0x1C, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180204, [0x20, 0x1C, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x180208, [0x40, 0x38, 0x00, 0x00]) # green clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180208, [0x40, 0x38, 0x00, 0x00]) # green clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x18020C, [0x00, 0x00, 0x00, 0x00]) # starting time (in frames, sint32)
|
rom.write_bytes(0x18020C, [0x00, 0x00, 0x00, 0x00]) # starting time (in frames, sint32)
|
||||||
if world.clock_mode == 'countdown':
|
if world.clock_mode == 'countdown':
|
||||||
write_bytes(rom, 0x180190, [0x01, 0x01, 0x00]) # set countdown, with no reset available
|
rom.write_bytes(0x180190, [0x01, 0x01, 0x00]) # set countdown, with no reset available
|
||||||
write_bytes(rom, 0x180200, [0xE0, 0xE3, 0xFF, 0xFF]) # red clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180200, [0xE0, 0xE3, 0xFF, 0xFF]) # red clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x180204, [0x20, 0x1C, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180204, [0x20, 0x1C, 0x00, 0x00]) # blue clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x180208, [0x40, 0x38, 0x00, 0x00]) # green clock adjustment time (in frames, sint32)
|
rom.write_bytes(0x180208, [0x40, 0x38, 0x00, 0x00]) # green clock adjustment time (in frames, sint32)
|
||||||
write_bytes(rom, 0x18020C, [0x80, 0x32, 0x02, 0x00]) # starting time (in frames, sint32)
|
rom.write_bytes(0x18020C, [0x80, 0x32, 0x02, 0x00]) # starting time (in frames, sint32)
|
||||||
|
|
||||||
# set up goals for treasure hunt
|
# set up goals for treasure hunt
|
||||||
write_bytes(rom, 0x180165, [0x0E, 0x28] if world.treasure_hunt_icon == 'Triforce Piece' else [0x0D, 0x28])
|
rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon == 'Triforce Piece' else [0x0D, 0x28])
|
||||||
write_byte(rom, 0x180167, world.treasure_hunt_count % 256)
|
rom.write_byte(0x180167, world.treasure_hunt_count % 256)
|
||||||
|
|
||||||
# assorted fixes
|
# assorted fixes
|
||||||
write_byte(rom, 0x180030, 0x00) # Disable SRAM trace
|
rom.write_byte(0x180030, 0x00) # Disable SRAM trace
|
||||||
write_byte(rom, 0x180036, 0x0A) # Rupoor negative value
|
rom.write_byte(0x180036, 0x0A) # Rupoor negative value
|
||||||
write_byte(rom, 0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence.
|
rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence.
|
||||||
write_byte(rom, 0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness
|
rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness
|
||||||
write_byte(rom, 0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp
|
rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp
|
||||||
if world.goal in ['pedestal', 'starhunt', 'triforcehunt']:
|
if world.goal in ['pedestal', 'starhunt', 'triforcehunt']:
|
||||||
write_byte(rom, 0x18003E, 0x01) # make ganon invincible
|
rom.write_byte(0x18003E, 0x01) # make ganon invincible
|
||||||
elif world.goal in ['dungeons']:
|
elif world.goal in ['dungeons']:
|
||||||
write_byte(rom, 0x18003E, 0x02) # make ganon invincible until all dungeons are beat
|
rom.write_byte(0x18003E, 0x02) # make ganon invincible until all dungeons are beat
|
||||||
write_byte(rom, 0x18016A, 0x00) # disable free roaming item text boxes
|
rom.write_byte(0x18016A, 0x00) # disable free roaming item text boxes
|
||||||
write_byte(rom, 0x18003B, 0x00) # disable maps showing crystals on overworld
|
rom.write_byte(0x18003B, 0x00) # disable maps showing crystals on overworld
|
||||||
write_byte(rom, 0x18003C, 0x00) # disable compasses showing dungeon count
|
rom.write_byte(0x18003C, 0x00) # disable compasses showing dungeon count
|
||||||
digging_game_rng = random.randint(1, 30) # set rng for digging game
|
digging_game_rng = random.randint(1, 30) # set rng for digging game
|
||||||
write_byte(rom, 0x180020, digging_game_rng)
|
rom.write_byte(0x180020, digging_game_rng)
|
||||||
write_byte(rom, 0xEFD95, digging_game_rng)
|
rom.write_byte(0xEFD95, digging_game_rng)
|
||||||
write_byte(rom, 0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills
|
rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills
|
||||||
write_byte(rom, 0x180042, 0x01 if world.save_and_quite_from_boss else 0x00) # Allow Save and Quite after boss kill
|
rom.write_byte(0x180042, 0x01 if world.save_and_quite_from_boss else 0x00) # Allow Save and Quite after boss kill
|
||||||
|
|
||||||
# remove shield from uncle
|
# remove shield from uncle
|
||||||
write_bytes(rom, 0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||||
write_bytes(rom, 0x6D25B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
rom.write_bytes(0x6D25B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||||
write_bytes(rom, 0x6D283, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
rom.write_bytes(0x6D283, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
|
||||||
write_bytes(rom, 0x6D28B, [0x00, 0x00, 0xf7, 0xff, 0x00, 0x0E])
|
rom.write_bytes(0x6D28B, [0x00, 0x00, 0xf7, 0xff, 0x00, 0x0E])
|
||||||
write_bytes(rom, 0x6D2CB, [0x00, 0x00, 0xf6, 0xff, 0x02, 0x0E])
|
rom.write_bytes(0x6D2CB, [0x00, 0x00, 0xf6, 0xff, 0x02, 0x0E])
|
||||||
write_bytes(rom, 0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
|
rom.write_bytes(0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
|
||||||
write_bytes(rom, 0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
|
rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
|
||||||
|
|
||||||
if world.swamp_patch_required:
|
if world.swamp_patch_required:
|
||||||
# patch swamp: Need to enable permanent drain of water as dam or swamp were moved
|
# patch swamp: Need to enable permanent drain of water as dam or swamp were moved
|
||||||
write_byte(rom, 0x18003D, 0x01)
|
rom.write_byte(0x18003D, 0x01)
|
||||||
|
|
||||||
# set correct flag for hera basement item
|
# set correct flag for hera basement item
|
||||||
if world.get_location('[dungeon-L3-1F] Tower of Hera - Freestanding Key').item is not None and world.get_location('[dungeon-L3-1F] Tower of Hera - Freestanding Key').item.name == 'Small Key (Tower of Hera)':
|
if world.get_location('[dungeon-L3-1F] Tower of Hera - Freestanding Key').item is not None and world.get_location('[dungeon-L3-1F] Tower of Hera - Freestanding Key').item.name == 'Small Key (Tower of Hera)':
|
||||||
write_byte(rom, 0x4E3BB, 0xE4)
|
rom.write_byte(0x4E3BB, 0xE4)
|
||||||
else:
|
else:
|
||||||
write_byte(rom, 0x4E3BB, 0xEB)
|
rom.write_byte(0x4E3BB, 0xEB)
|
||||||
|
|
||||||
# disable open door sprites when exiting caves
|
|
||||||
# this does not seem to work completely yet
|
|
||||||
if world.fix_door_frames:
|
|
||||||
for i in range(0x85):
|
|
||||||
write_byte(rom, 0x15274 + i, 0x00)
|
|
||||||
for i in range(0x86):
|
|
||||||
write_byte(rom, 0x15488 + i, 0x00)
|
|
||||||
# leave the entry marking tavern north a north facing exit
|
|
||||||
for i in range(0x82):
|
|
||||||
write_byte(rom, 0x15510 + i, 0x00)
|
|
||||||
|
|
||||||
# fix trock doors for reverse entrances
|
# fix trock doors for reverse entrances
|
||||||
if world.fix_trock_doors:
|
if world.fix_trock_doors:
|
||||||
write_byte(rom, 0xFED31, 0x0E) # preopen bombable exit
|
rom.write_byte(0xFED31, 0x0E) # preopen bombable exit
|
||||||
write_byte(rom, 0xFEE41, 0x0E) # preopen bombable exit
|
rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit
|
||||||
write_byte(rom, 0xFE465, 0x1E) # remove small key door on backside of big key door
|
rom.write_byte(0xFE465, 0x1E) # remove small key door on backside of big key door
|
||||||
|
|
||||||
# Thanks to Zarby89 for finding these values
|
# Thanks to Zarby89 for finding these values
|
||||||
# fix skull woods exit point
|
# fix skull woods exit point
|
||||||
if world.fix_skullwoods_exit:
|
if world.fix_skullwoods_exit:
|
||||||
write_byte(rom, 0x15E0D, 0xF8)
|
rom.write_byte(0x15E0D, 0xF8)
|
||||||
|
|
||||||
# fix palace of darkness exit point
|
# fix palace of darkness exit point
|
||||||
if world.fix_palaceofdarkness_exit:
|
if world.fix_palaceofdarkness_exit:
|
||||||
write_byte(rom, 0x15E03, 0x40)
|
rom.write_byte(0x15E03, 0x40)
|
||||||
|
|
||||||
# fix turtle rock exit point
|
# fix turtle rock exit point
|
||||||
if world.fix_trock_exit:
|
if world.fix_trock_exit:
|
||||||
write_byte(rom, 0x15E1D, 0x34)
|
rom.write_byte(0x15E1D, 0x34)
|
||||||
|
|
||||||
# enable quick item swapping with L and R (ported by Amazing Ampharos)
|
# enable quick item swapping with L and R (ported by Amazing Ampharos)
|
||||||
if quickswap:
|
if quickswap:
|
||||||
write_bytes(rom, 0x107fb, [0x22, 0x50, 0xFF, 0x1F])
|
rom.write_bytes(0x107fb, [0x22, 0x50, 0xFF, 0x1F])
|
||||||
write_bytes(rom, 0x12451, [0x22, 0x50, 0xFF, 0x1F])
|
rom.write_bytes(0x12451, [0x22, 0x50, 0xFF, 0x1F])
|
||||||
write_bytes(rom, 0xfff50, [0x20, 0x58, 0xFF, 0xA5, 0xF6, 0x29, 0x40, 0x6B, 0xA5, 0xF6, 0x89, 0x10, 0xF0, 0x03, 0x4C, 0x69,
|
rom.write_bytes(0xfff50, [0x20, 0x58, 0xFF, 0xA5, 0xF6, 0x29, 0x40, 0x6B, 0xA5, 0xF6, 0x89, 0x10, 0xF0, 0x03, 0x4C, 0x69,
|
||||||
0xFF, 0x89, 0x20, 0xF0, 0x03, 0x4C, 0xAA, 0xFF, 0x60, 0xAD, 0x02, 0x02, 0xF0, 0x3B, 0xDA, 0xAA,
|
0xFF, 0x89, 0x20, 0xF0, 0x03, 0x4C, 0xAA, 0xFF, 0x60, 0xAD, 0x02, 0x02, 0xF0, 0x3B, 0xDA, 0xAA,
|
||||||
0xE0, 0x0F, 0xF0, 0x14, 0xE0, 0x10, 0xF0, 0x14, 0xE0, 0x14, 0xD0, 0x02, 0xA2, 0x00, 0xE8, 0xBF,
|
0xE0, 0x0F, 0xF0, 0x14, 0xE0, 0x10, 0xF0, 0x14, 0xE0, 0x14, 0xD0, 0x02, 0xA2, 0x00, 0xE8, 0xBF,
|
||||||
0x3F, 0xF3, 0x7E, 0xF0, 0xEB, 0x4C, 0xEB, 0xFF, 0xA2, 0x01, 0x80, 0x0A, 0xAF, 0x4F, 0xF3, 0x7E,
|
0x3F, 0xF3, 0x7E, 0xF0, 0xEB, 0x4C, 0xEB, 0xFF, 0xA2, 0x01, 0x80, 0x0A, 0xAF, 0x4F, 0xF3, 0x7E,
|
||||||
|
@ -305,38 +351,46 @@ def patch_rom(world, rom, hashtable, quickswap=False, beep='normal', sprite=None
|
||||||
|
|
||||||
# set rom name
|
# set rom name
|
||||||
# 21 bytes
|
# 21 bytes
|
||||||
write_bytes(rom, 0x7FC0, bytearray('ER_041_%09d_' % world.seed, 'utf8') + world.option_identifier.to_bytes(4, 'big'))
|
rom.write_bytes(0x7FC0, bytearray('ER_041_%09d_' % world.seed, 'utf8') + world.option_identifier.to_bytes(4, 'big'))
|
||||||
|
|
||||||
# set heart beep rate
|
# set heart beep rate
|
||||||
write_byte(rom, 0x180033, {'off': 0x00, 'half': 0x40, 'quarter': 0x80, 'normal': 0x20}[beep])
|
rom.write_byte(0x180033, {'off': 0x00, 'half': 0x40, 'quarter': 0x80, 'normal': 0x20}[beep])
|
||||||
|
|
||||||
# store hash table for main menu hash
|
# store hash table for main menu hash
|
||||||
write_bytes(rom, 0x181000, hashtable)
|
rom.write_bytes(0x181000, hashtable)
|
||||||
|
|
||||||
# write link sprite if required
|
# write link sprite if required
|
||||||
if sprite is not None:
|
if sprite is not None:
|
||||||
write_bytes(rom, 0x80000, sprite)
|
write_sprite(rom, sprite)
|
||||||
|
|
||||||
|
if isinstance(rom, LocalRom):
|
||||||
|
rom.write_crc()
|
||||||
|
|
||||||
return rom
|
return rom
|
||||||
|
|
||||||
|
|
||||||
def write_byte(rom, address, value):
|
def write_sprite(rom, sprite):
|
||||||
rom[address] = value
|
if len(sprite) == 0x7000:
|
||||||
|
# sprite file with graphics and without palette data
|
||||||
|
rom.write_bytes(0x80000, sprite[:0x7000])
|
||||||
def write_bytes(rom, startaddress, values):
|
elif len(sprite) == 0x7078:
|
||||||
for i, value in enumerate(values):
|
# sprite file with graphics and palette data
|
||||||
write_byte(rom, startaddress + i, value)
|
rom.write_bytes(0x80000, sprite[:0x7000])
|
||||||
|
rom.write_bytes(0xDD308, sprite[0x7000:])
|
||||||
|
elif len(sprite) in [0x100000, 0x200000]:
|
||||||
|
# full rom with patched sprite, extract it
|
||||||
|
rom.write_bytes(0x80000, sprite[0x80000:0x87000])
|
||||||
|
rom.write_bytes(0xDD308, sprite[0xDD308:0xDD380])
|
||||||
|
|
||||||
|
|
||||||
def write_string_to_rom(rom, target, string):
|
def write_string_to_rom(rom, target, string):
|
||||||
address, maxbytes = text_addresses[target]
|
address, maxbytes = text_addresses[target]
|
||||||
write_bytes(rom, address, string_to_alttp_text(string, maxbytes))
|
rom.write_bytes(address, string_to_alttp_text(string, maxbytes))
|
||||||
|
|
||||||
|
|
||||||
def write_credits_string_to_rom(rom, target, string):
|
def write_credits_string_to_rom(rom, target, string):
|
||||||
address, length = credits_addresses[target]
|
address, length = credits_addresses[target]
|
||||||
write_bytes(rom, address, string_to_credits(string, length))
|
rom.write_bytes(address, string_to_credits(string, length))
|
||||||
|
|
||||||
|
|
||||||
def write_strings(rom, world):
|
def write_strings(rom, world):
|
||||||
|
@ -396,27 +450,3 @@ def write_strings(rom, world):
|
||||||
fluteboyitem = world.get_location('Flute Boy').item
|
fluteboyitem = world.get_location('Flute Boy').item
|
||||||
fluteboyitem_text = FluteBoy_texts[random.randint(0, len(FluteBoy_texts) - 1)] if fluteboyitem is None or fluteboyitem.fluteboy_credit_text is None else fluteboyitem.fluteboy_credit_text
|
fluteboyitem_text = FluteBoy_texts[random.randint(0, len(FluteBoy_texts) - 1)] if fluteboyitem is None or fluteboyitem.fluteboy_credit_text is None else fluteboyitem.fluteboy_credit_text
|
||||||
write_credits_string_to_rom(rom, 'FluteBoy', fluteboyitem_text)
|
write_credits_string_to_rom(rom, 'FluteBoy', fluteboyitem_text)
|
||||||
|
|
||||||
|
|
||||||
def patch_base_rom(rom):
|
|
||||||
# verify correct checksum of baserom
|
|
||||||
basemd5 = hashlib.md5()
|
|
||||||
basemd5.update(rom)
|
|
||||||
if not JAP10HASH == basemd5.hexdigest():
|
|
||||||
logging.getLogger('').warning('Supplied Base Rom does not match known MD5 for JAP(1.0) release. Will try to patch anyway.')
|
|
||||||
|
|
||||||
# extend to 2MB
|
|
||||||
rom.extend(bytearray([0x00]*(2097152 - len(rom))))
|
|
||||||
|
|
||||||
# load randomizer patches
|
|
||||||
patches = json.load(open('base2current.json', 'r'))
|
|
||||||
for patch in patches:
|
|
||||||
if isinstance(patch, dict):
|
|
||||||
for baseaddress, values in patch.items():
|
|
||||||
write_bytes(rom, int(baseaddress), values)
|
|
||||||
|
|
||||||
# verify md5
|
|
||||||
patchedmd5 = hashlib.md5()
|
|
||||||
patchedmd5.update(rom)
|
|
||||||
if not RANDOMIZERBASEHASH == patchedmd5.hexdigest():
|
|
||||||
raise RuntimeError('Provided Base Rom unsuitable for patching. Please provide a JAP(1.0) "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc" rom to use as a base.')
|
|
||||||
|
|
Loading…
Reference in New Issue