2017-05-25 15:47:15 +00:00
from Dungeons import dungeon_music_addresses
2017-05-25 13:58:35 +00:00
from Text import string_to_alttp_text , text_addresses , credits_addresses , string_to_credits
from Text import Uncle_texts , Ganon1_texts , PyramidFairy_texts , TavernMan_texts , Sahasrahla2_texts , Triforce_texts , Blind_texts , BombShop2_texts
from 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
2017-05-25 10:09:50 +00:00
import random
2017-06-03 12:20:39 +00:00
import json
import hashlib
import logging
JAP10HASH = ' 03a63945398191337e896e5771f77173 '
2017-06-04 18:01:09 +00:00
RANDOMIZERBASEHASH = ' d5b61947feef1972e0f546ba43180e62 '
2017-05-25 10:09:50 +00:00
2017-05-26 16:39:32 +00:00
def patch_rom ( world , rom , hashtable , quickswap = False , beep = ' normal ' , sprite = None ) :
2017-05-25 10:09:50 +00:00
# patch items
for location in world . get_locations ( ) :
itemid = location . item . code if location . item is not None else 0x5A
2017-06-17 12:40:37 +00:00
if itemid is None or location . address is None :
continue
2017-05-25 15:47:15 +00:00
locationaddress = location . address
if not location . crystal :
2017-05-25 10:09:50 +00:00
# regular items
write_byte ( rom , locationaddress , itemid )
2017-05-25 15:47:15 +00:00
else :
2017-05-25 10:09:50 +00:00
# crystals
for address , value in zip ( locationaddress , itemid ) :
write_byte ( rom , address , value )
# patch music
music_addresses = dungeon_music_addresses [ location . name ]
music = 0x11 if ' Pendant ' in location . item . name else 0x16
for music_address in music_addresses :
write_byte ( rom , music_address , music )
2017-06-17 11:16:13 +00:00
# store old door overlay table
door_overlays = bytearray ( rom [ 0x15488 : 0x15488 + 0x10A ] )
2017-05-25 10:09:50 +00:00
# patch entrances
for region in world . regions :
for exit in region . exits :
if exit . target is not None :
2017-06-03 13:33:11 +00:00
addresses = [ exit . addresses ] if isinstance ( exit . addresses , int ) else exit . addresses
for address in addresses :
write_byte ( rom , address , exit . target )
2017-05-25 10:09:50 +00:00
2017-06-17 11:16:13 +00:00
# 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 ] )
2017-05-25 10:09:50 +00:00
# patch medallion requirements
if world . required_medallions [ 0 ] == ' Bombos ' :
write_byte ( rom , 0x180022 , 0x00 ) # requirement
write_byte ( rom , 0x4FF2 , 0x31 ) # sprite
write_byte ( rom , 0x50D1 , 0x80 )
write_byte ( rom , 0x51B0 , 0x00 )
elif world . required_medallions [ 0 ] == ' Quake ' :
write_byte ( rom , 0x180022 , 0x02 ) # requirement
write_byte ( rom , 0x4FF2 , 0x31 ) # sprite
write_byte ( rom , 0x50D1 , 0x88 )
write_byte ( rom , 0x51B0 , 0x00 )
if world . required_medallions [ 1 ] == ' Bombos ' :
write_byte ( rom , 0x180023 , 0x00 ) # requirement
write_byte ( rom , 0x5020 , 0x31 ) # sprite
write_byte ( rom , 0x50FF , 0x90 )
write_byte ( rom , 0x51DE , 0x00 )
elif world . required_medallions [ 1 ] == ' Ether ' :
write_byte ( rom , 0x180023 , 0x01 ) # requirement
write_byte ( rom , 0x5020 , 0x31 ) # sprite
write_byte ( rom , 0x50FF , 0x98 )
write_byte ( rom , 0x51DE , 0x00 )
# set open mode:
if world . mode == ' open ' :
write_byte ( rom , 0x180032 , 0x01 ) # open mode
# disable sword sprite from uncle
write_bytes ( rom , 0x6D263 , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
write_bytes ( rom , 0x6D26B , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
write_bytes ( rom , 0x6D293 , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
write_bytes ( rom , 0x6D29B , [ 0x00 , 0x00 , 0xf7 , 0xff , 0x00 , 0x0E ] )
write_bytes ( rom , 0x6D2B3 , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x02 , 0x0E ] )
write_bytes ( rom , 0x6D2BB , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x02 , 0x0E ] )
write_bytes ( rom , 0x6D2E3 , [ 0x00 , 0x00 , 0xf7 , 0xff , 0x02 , 0x0E ] )
write_bytes ( rom , 0x6D2EB , [ 0x00 , 0x00 , 0xf7 , 0xff , 0x02 , 0x0E ] )
write_bytes ( rom , 0x6D31B , [ 0x00 , 0x00 , 0xe4 , 0xff , 0x08 , 0x0E ] )
write_bytes ( rom , 0x6D323 , [ 0x00 , 0x00 , 0xe4 , 0xff , 0x08 , 0x0E ] )
else :
write_byte ( rom , 0x180032 , 0x00 ) # standard mode
2017-06-03 13:46:05 +00:00
# set light cones
write_byte ( rom , 0x180038 , 0x01 if world . sewer_light_cone else 0x00 )
write_byte ( rom , 0x180039 , 0x01 if world . light_world_light_cone else 0x00 )
write_byte ( rom , 0x18003A , 0x01 if world . dark_world_light_cone else 0x00 )
2017-05-25 10:09:50 +00:00
# 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
2017-06-04 12:44:23 +00:00
if world . difficulty == ' hard ' :
2017-05-25 10:09:50 +00:00
# Spike Cave Damage
write_byte ( rom , 0x180168 , 0x02 )
# Powdered Fairies Prize
write_byte ( rom , 0x36DD0 , 0x79 ) # Bee
# potion heal amount
write_byte ( rom , 0x180084 , 0x08 ) # One Heart
# potion magic restore amount
write_byte ( rom , 0x180085 , 0x20 ) # Quarter Magic
2017-06-04 12:44:23 +00:00
else :
# Spike Cave Damage
write_byte ( rom , 0x180168 , 0x08 )
# Powdered Fairies Prize
write_byte ( rom , 0x36DD0 , 0xE3 ) # fairy
# potion heal amount
write_byte ( rom , 0x180084 , 0xA0 ) # full
# potion magic restore amount
write_byte ( rom , 0x180085 , 0x80 ) # full
2017-05-25 10:09:50 +00:00
# set up game internal RNG seed
for i in range ( 1024 ) :
write_byte ( rom , 0x178000 + i , random . randint ( 0 , 255 ) )
# 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 ,
0xDC , 0xDC , 0xDC , 0xDD , 0xDC , 0xDC , 0xDE , 0xDC , 0xE1 , 0xD8 , 0xE1 , 0xE2 , 0xE1 , 0xD8 , 0xE1 , 0xE2 , 0xDF , 0xD9 , 0xD8 , 0xE1 , 0xDF , 0xDC , 0xD9 , 0xD8 ,
0xD8 , 0xE3 , 0xE0 , 0xDB , 0xDE , 0xD8 , 0xDB , 0xE2 , 0xD9 , 0xDA , 0xDB , 0xD9 , 0xDB , 0xD9 , 0xDB ]
random . shuffle ( prizes )
# write tree pull prizes
write_byte ( rom , 0xEFBD4 , prizes . pop ( ) )
write_byte ( rom , 0xEFBD5 , prizes . pop ( ) )
write_byte ( rom , 0xEFBD6 , prizes . pop ( ) )
# in open mode with shuffled caves, cannot guarantee access to rupees or a shop. Make 4 kill tree pull single bombs always to give guaranteed access
2017-06-03 14:02:42 +00:00
if world . shuffle not in [ ' vanilla ' , ' dungeonsfull ' , ' dungeonssimple ' ] :
2017-05-25 10:09:50 +00:00
write_byte ( rom , 0xEFBD6 , 0xDC )
# rupee crab prizes
write_byte ( rom , 0x329C8 , prizes . pop ( ) ) # first prize
write_byte ( rom , 0x329C4 , prizes . pop ( ) ) # final prize
# stunned enemy prize
write_byte ( rom , 0x37993 , prizes . pop ( ) )
# saved fish prize
write_byte ( rom , 0xE82CC , prizes . pop ( ) )
# fill enemy prize packs
write_bytes ( rom , 0x37A78 , prizes )
# prize pack drop chances
2017-06-04 12:44:23 +00:00
if world . difficulty == ' hard ' :
2017-05-25 10:09:50 +00:00
droprates = [ 0x01 , 0x02 , 0x03 , 0x03 , 0x03 , 0x04 , 0x04 ] # 50%, 25%, 3* 12.5%, 2* 6.25%
2017-06-04 12:44:23 +00:00
else :
droprates = [ 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ] # 50%
2017-05-25 10:09:50 +00:00
random . shuffle ( droprates )
write_bytes ( rom , 0x37A62 , droprates )
2017-05-25 13:58:35 +00:00
# shuffle enemies to prize packs
2017-05-25 10:09:50 +00:00
for i in range ( 243 ) :
if rom [ 0x6B632 + i ] & 0x0F != 0x00 :
rom [ 0x6B632 + i ] = ( rom [ 0x6B632 + i ] & 0xF0 ) | random . randint ( 1 , 7 )
# 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 ,
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_addresses = [ 0x4CF6C , 0x4CFBA , 0x4CFE0 , 0x4CFFB , 0x4D018 , 0x4D01B , 0x4D028 , 0x4D03C , 0x4D059 , 0x4D07A , 0x4D09E , 0x4D0A8 , 0x4D0AB , 0x4D0AE , 0x4D0BE , 0x4D0DD ,
0x4D16A , 0x4D1E5 , 0x4D1EE , 0x4D20B , 0x4CBBF , 0x4CBBF , 0x4CC17 , 0x4CC1A , 0x4CC4A , 0x4CC4D , 0x4CC53 , 0x4CC69 , 0x4CC6F , 0x4CC7C , 0x4CCEF , 0x4CD51 ,
0x4CDC0 , 0x4CDC3 , 0x4CDC6 , 0x4CE37 , 0x4D2DE , 0x4D32F , 0x4D355 , 0x4D367 , 0x4D384 , 0x4D387 , 0x4D397 , 0x4D39E , 0x4D3AB , 0x4D3AE , 0x4D3D1 , 0x4D3D7 ,
0x4D3F8 , 0x4D416 , 0x4D420 , 0x4D423 , 0x4D42D , 0x4D449 , 0x4D48C , 0x4D4D9 , 0x4D4DC , 0x4D4E3 , 0x4D504 , 0x4D507 , 0x4D55E , 0x4D56A ]
random . shuffle ( bonk_prizes )
for prize , address in zip ( bonk_prizes , bonk_addresses ) :
write_byte ( rom , address , prize )
2017-05-25 15:52:31 +00:00
# set Fountain bottle exchange items
write_byte ( rom , 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 ) ] )
2017-06-03 12:20:39 +00:00
# set Fat Fairy Bow/Sword prizes to be disappointing
write_byte ( rom , 0x34914 , 0x3A ) # Bow and Arrow
write_byte ( rom , 0x180028 , 0x49 ) # Fighter Sword
2017-06-04 11:10:22 +00:00
# set up clocks for timed modes
if world . clock_mode == ' off ' :
write_bytes ( rom , 0x180190 , [ 0x00 , 0x00 , 0x00 ] ) # turn off clock mode
2017-06-04 12:44:23 +00:00
write_bytes ( rom , 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)
write_bytes ( rom , 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)
2017-06-04 11:10:22 +00:00
elif world . clock_mode == ' ohko ' :
write_bytes ( rom , 0x180190 , [ 0x01 , 0x02 , 0x01 ] ) # ohko timer with resetable timer functionality
2017-06-04 12:44:23 +00:00
write_bytes ( rom , 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)
write_bytes ( rom , 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)
2017-06-04 11:10:22 +00:00
if world . clock_mode == ' stopwatch ' :
write_bytes ( rom , 0x180190 , [ 0x02 , 0x01 , 0x00 ] ) # set stopwatch mode
2017-06-04 12:44:23 +00:00
write_bytes ( rom , 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)
write_bytes ( rom , 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)
2017-06-04 11:10:22 +00:00
if world . clock_mode == ' countdown ' :
write_bytes ( rom , 0x180190 , [ 0x01 , 0x01 , 0x00 ] ) # set countdown, with no reset available
2017-06-04 12:44:23 +00:00
write_bytes ( rom , 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)
write_bytes ( rom , 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)
2017-06-04 11:10:22 +00:00
# set up goals for treasure hunt
2017-06-04 12:44:23 +00:00
write_bytes ( rom , 0x180165 , [ 0x0E , 0x28 ] if world . treasure_hunt_icon == ' Triforce Piece ' else [ 0x0D , 0x28 ] )
2017-06-04 11:10:22 +00:00
write_byte ( rom , 0x180167 , world . treasure_hunt_count % 256 )
2017-06-03 12:20:39 +00:00
# assorted fixes
write_byte ( rom , 0x180030 , 0x00 ) # Disable SRAM trace
write_byte ( rom , 0x180036 , 0x0A ) # Rupoor negative value
2017-06-04 11:10:22 +00:00
write_byte ( rom , 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 == ' vanilla ' else 0x02 if world . aga_randomness == ' table ' else 0x01 ) # set blue ball and ganon warp randomness
2017-06-05 00:12:08 +00:00
write_byte ( rom , 0x1800A1 , 0x01 ) # enable overworld screen transition draining for water level inside swamp
2017-06-04 12:44:23 +00:00
if world . goal in [ ' pedestal ' , ' starhunt ' , ' triforcehunt ' ] :
2017-06-03 19:27:34 +00:00
write_byte ( rom , 0x18003E , 0x01 ) # make ganon invincible
2017-06-03 12:20:39 +00:00
# remove shield from uncle
write_bytes ( rom , 0x6D253 , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
write_bytes ( rom , 0x6D25B , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
write_bytes ( rom , 0x6D283 , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
write_bytes ( rom , 0x6D28B , [ 0x00 , 0x00 , 0xf7 , 0xff , 0x00 , 0x0E ] )
write_bytes ( rom , 0x6D2CB , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x02 , 0x0E ] )
write_bytes ( rom , 0x6D2FB , [ 0x00 , 0x00 , 0xf7 , 0xff , 0x02 , 0x0E ] )
write_bytes ( rom , 0x6D313 , [ 0x00 , 0x00 , 0xe4 , 0xff , 0x08 , 0x0E ] )
2017-05-25 15:52:31 +00:00
2017-05-25 10:09:50 +00:00
if world . swamp_patch_required :
# patch swamp: Need to enable permanent drain of water as dam or swamp were moved
2017-06-05 00:12:08 +00:00
write_byte ( rom , 0x18003D , 0x01 )
2017-05-25 10:09:50 +00:00
# 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) ' :
write_byte ( rom , 0x4E3BB , 0xE4 )
else :
write_byte ( rom , 0x4E3BB , 0xEB )
2017-05-25 10:50:42 +00:00
# disable open door sprites when exiting caves
2017-06-03 12:20:39 +00:00
# this does not seem to work completely yet
2017-06-17 11:16:13 +00:00
if world . fix_door_frames :
2017-05-25 10:50:42 +00:00
for i in range ( 0x85 ) :
write_byte ( rom , 0x15274 + i , 0x00 )
2017-06-17 11:16:13 +00:00
for i in range ( 0x86 ) :
write_byte ( rom , 0x15488 + i , 0x02 )
# leave the entry marking tavern north a north facing exit
for i in range ( 0x82 ) :
write_byte ( rom , 0x15510 + i , 0x02 )
2017-05-25 10:50:42 +00:00
2017-05-28 13:40:59 +00:00
# fix trock doors for reverse entrances
2017-06-17 11:16:13 +00:00
if world . fix_trock_doors :
2017-05-28 13:40:59 +00:00
write_byte ( rom , 0xFED31 , 0x0E ) # preopen bombable exit
write_byte ( rom , 0xFEE41 , 0x0E ) # preopen bombable exit
write_byte ( rom , 0xFE465 , 0x1E ) # remove small key door on backside of big key door
2017-05-25 10:50:42 +00:00
# enable quick item swapping with L and R (ported by Amazing Ampharos)
if quickswap :
write_bytes ( rom , 0x107fb , [ 0x22 , 0x50 , 0xFF , 0x1F ] )
write_bytes ( rom , 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 ,
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 ,
0x3F , 0xF3 , 0x7E , 0xF0 , 0xEB , 0x4C , 0xEB , 0xFF , 0xA2 , 0x01 , 0x80 , 0x0A , 0xAF , 0x4F , 0xF3 , 0x7E ,
0xAA , 0xE0 , 0x04 , 0xF0 , 0x10 , 0xE8 , 0xBF , 0x5B , 0xF3 , 0x7E , 0xF0 , 0xF5 , 0x8A , 0x8F , 0x4F , 0xF3 ,
0x7E , 0xA2 , 0x10 , 0x80 , 0xE0 , 0xA2 , 0x11 , 0x80 , 0xD6 , 0x60 , 0xAD , 0x02 , 0x02 , 0xF0 , 0x3B , 0xDA ,
0xAA , 0xE0 , 0x11 , 0xF0 , 0x14 , 0xE0 , 0x10 , 0xF0 , 0x14 , 0xE0 , 0x01 , 0xD0 , 0x02 , 0xA2 , 0x15 , 0xCA ,
0xBF , 0x3F , 0xF3 , 0x7E , 0xF0 , 0xEB , 0x4C , 0xEB , 0xFF , 0xA2 , 0x04 , 0x80 , 0x0A , 0xAF , 0x4F , 0xF3 ,
0x7E , 0xAA , 0xE0 , 0x01 , 0xF0 , 0x10 , 0xCA , 0xBF , 0x5B , 0xF3 , 0x7E , 0xF0 , 0xF5 , 0x8A , 0x8F , 0x4F ,
0xF3 , 0x7E , 0xA2 , 0x10 , 0x80 , 0xE0 , 0xA2 , 0x0F , 0x80 , 0xD6 , 0x60 , 0xA9 , 0x20 , 0x8D , 0x2F , 0x01 ,
0x8E , 0x02 , 0x02 , 0x22 , 0x7F , 0xDB , 0x0D , 0xFA , 0x60 , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF ] )
2017-05-25 13:58:35 +00:00
write_strings ( rom , world )
2017-05-25 10:09:50 +00:00
2017-05-25 15:47:15 +00:00
# set rom name
2017-05-30 05:33:23 +00:00
# 21 bytes
2017-06-04 13:02:27 +00:00
write_bytes ( rom , 0x7FC0 , bytearray ( ' ER_030_ %09d _ ' % world . seed , ' utf8 ' ) + world . option_identifier . to_bytes ( 4 , ' big ' ) )
2017-05-25 15:47:15 +00:00
# set heart beep rate
write_byte ( rom , 0x180033 , { ' off ' : 0x00 , ' half ' : 0x40 , ' quarter ' : 0x80 , ' normal ' : 0x20 } [ beep ] )
2017-05-25 19:41:37 +00:00
# store hash table for main menu hash
write_bytes ( rom , 0x181000 , hashtable )
2017-05-26 16:39:32 +00:00
# write link sprite if required
if sprite is not None :
write_bytes ( rom , 0x80000 , sprite )
2017-05-25 10:09:50 +00:00
return rom
def write_byte ( rom , address , value ) :
rom [ address ] = value
def write_bytes ( rom , startaddress , values ) :
for i , value in enumerate ( values ) :
write_byte ( rom , startaddress + i , value )
def write_string_to_rom ( rom , target , string ) :
address , maxbytes = text_addresses [ target ]
2017-05-25 13:58:35 +00:00
write_bytes ( rom , address , string_to_alttp_text ( string , maxbytes ) )
def write_credits_string_to_rom ( rom , target , string ) :
address , length = credits_addresses [ target ]
write_bytes ( rom , address , string_to_credits ( string , length ) )
def write_strings ( rom , world ) :
2017-05-25 15:47:15 +00:00
silverarrows = world . find_items ( ' Silver Arrows ' )
2017-06-03 12:20:39 +00:00
silverarrow_hint = ( ' %s ? ' % silverarrows [ 0 ] . hint_text ) if silverarrows else ' ? \n I think not! '
write_string_to_rom ( rom , ' Ganon2 ' , ' Did you find the silver arrows %s ' % silverarrow_hint )
2017-05-25 15:47:15 +00:00
crystal5 = world . find_items ( ' Crystal 5 ' ) [ 0 ]
crystal6 = world . find_items ( ' Crystal 6 ' ) [ 0 ]
write_string_to_rom ( rom , ' BombShop1 ' , ' Big Bomb? \n My supply is blocked until you clear %s and %s . ' % ( crystal5 . hint_text , crystal6 . hint_text ) )
greenpendant = world . find_items ( ' Green Pendant ' ) [ 0 ]
write_string_to_rom ( rom , ' Sahasrahla1 ' , ' I lost my family heirloom in %s ' % greenpendant . hint_text )
2017-05-25 13:58:35 +00:00
write_string_to_rom ( rom , ' Uncle ' , Uncle_texts [ random . randint ( 0 , len ( Uncle_texts ) - 1 ) ] )
write_string_to_rom ( rom , ' Triforce ' , Triforce_texts [ random . randint ( 0 , len ( Triforce_texts ) - 1 ) ] )
write_string_to_rom ( rom , ' BombShop2 ' , BombShop2_texts [ random . randint ( 0 , len ( BombShop2_texts ) - 1 ) ] )
write_string_to_rom ( rom , ' PyramidFairy ' , PyramidFairy_texts [ random . randint ( 0 , len ( PyramidFairy_texts ) - 1 ) ] )
write_string_to_rom ( rom , ' Sahasrahla2 ' , Sahasrahla2_texts [ random . randint ( 0 , len ( Sahasrahla2_texts ) - 1 ) ] )
write_string_to_rom ( rom , ' Blind ' , Blind_texts [ random . randint ( 0 , len ( Blind_texts ) - 1 ) ] )
2017-06-04 12:44:23 +00:00
if world . goal in [ ' pedestal ' , ' starhunt ' , ' triforcehunt ' ] :
2017-06-03 19:27:34 +00:00
write_string_to_rom ( rom , ' Ganon1 ' , ' Why are you even here? \n You can \' t even hurt me! ' )
else :
write_string_to_rom ( rom , ' Ganon1 ' , Ganon1_texts [ random . randint ( 0 , len ( Ganon1_texts ) - 1 ) ] )
2017-05-25 13:58:35 +00:00
write_string_to_rom ( rom , ' TavernMan ' , TavernMan_texts [ random . randint ( 0 , len ( TavernMan_texts ) - 1 ) ] )
altaritem = world . get_location ( ' Altar ' ) . item
altar_text = ' Some Hot Air ' if altaritem is None else altaritem . altar_hint_text if altaritem . altar_hint_text is not None else ' Unknown Item. '
write_string_to_rom ( rom , ' Altar ' , altar_text )
altar_credit_text = ' and the Hot Air ' if altaritem is None else altaritem . altar_credit_text if altaritem . altar_credit_text is not None else ' and the Unknown Item. '
write_credits_string_to_rom ( rom , ' Altar ' , altar_credit_text )
write_credits_string_to_rom ( rom , ' KingsReturn ' , KingsReturn_texts [ random . randint ( 0 , len ( KingsReturn_texts ) - 1 ) ] )
write_credits_string_to_rom ( rom , ' Sanctuary ' , Sanctuary_texts [ random . randint ( 0 , len ( Sanctuary_texts ) - 1 ) ] )
write_credits_string_to_rom ( rom , ' Kakariko ' , Kakariko_texts [ random . randint ( 0 , len ( Kakariko_texts ) - 1 ) ] )
write_credits_string_to_rom ( rom , ' Blacksmiths ' , Blacksmiths_texts [ random . randint ( 0 , len ( Blacksmiths_texts ) - 1 ) ] )
write_credits_string_to_rom ( rom , ' DeathMountain ' , DeathMountain_texts [ random . randint ( 0 , len ( DeathMountain_texts ) - 1 ) ] )
write_credits_string_to_rom ( rom , ' LostWoods ' , LostWoods_texts [ random . randint ( 0 , len ( LostWoods_texts ) - 1 ) ] )
write_credits_string_to_rom ( rom , ' WishingWell ' , WishingWell_texts [ random . randint ( 0 , len ( WishingWell_texts ) - 1 ) ] )
write_credits_string_to_rom ( rom , ' DesertPalace ' , DesertPalace_texts [ random . randint ( 0 , len ( DesertPalace_texts ) - 1 ) ] )
write_credits_string_to_rom ( rom , ' MountainTower ' , MountainTower_texts [ random . randint ( 0 , len ( MountainTower_texts ) - 1 ) ] )
write_credits_string_to_rom ( rom , ' LinksHouse ' , LinksHouse_texts [ random . randint ( 0 , len ( LinksHouse_texts ) - 1 ) ] )
write_credits_string_to_rom ( rom , ' Lumberjacks ' , Lumberjacks_texts [ random . randint ( 0 , len ( Lumberjacks_texts ) - 1 ) ] )
sickkiditem = world . get_location ( ' Sick Kid ' ) . item
sickkiditem_text = SickKid_texts [ random . randint ( 0 , len ( SickKid_texts ) - 1 ) ] if sickkiditem is None or sickkiditem . sickkid_credit_text is None else sickkiditem . sickkid_credit_text
write_credits_string_to_rom ( rom , ' SickKid ' , sickkiditem_text )
zoraitem = world . get_location ( ' King Zora ' ) . item
zoraitem_text = Zora_texts [ random . randint ( 0 , len ( Zora_texts ) - 1 ) ] if zoraitem is None or zoraitem . zora_credit_text is None else zoraitem . zora_credit_text
write_credits_string_to_rom ( rom , ' Zora ' , zoraitem_text )
magicshopitem = world . get_location ( ' Witch ' ) . item
magicshopitem_text = MagicShop_texts [ random . randint ( 0 , len ( MagicShop_texts ) - 1 ) ] if magicshopitem is None or magicshopitem . magicshop_credit_text is None else magicshopitem . magicshop_credit_text
write_credits_string_to_rom ( rom , ' MagicShop ' , magicshopitem_text )
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
write_credits_string_to_rom ( rom , ' FluteBoy ' , fluteboyitem_text )
2017-06-03 12:20:39 +00:00
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
2017-06-03 19:27:34 +00:00
patches = json . load ( open ( ' base2current.json ' , ' r ' ) )
2017-06-03 12:20:39 +00:00
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. ' )