Refactor rom patching now that jsonrom patches can safely be merged
This commit is contained in:
parent
6bafdfafe6
commit
77ae96cf1b
|
@ -984,7 +984,7 @@ class Spoiler(object):
|
||||||
self.medallions['Misery Mire (Player %d)' % player] = self.world.required_medallions[player][0]
|
self.medallions['Misery Mire (Player %d)' % player] = self.world.required_medallions[player][0]
|
||||||
self.medallions['Turtle Rock (Player %d)' % player] = self.world.required_medallions[player][1]
|
self.medallions['Turtle Rock (Player %d)' % player] = self.world.required_medallions[player][1]
|
||||||
|
|
||||||
self.startinventory = self.world.precollected_items.copy()
|
self.startinventory = list(map(str, self.world.precollected_items))
|
||||||
|
|
||||||
self.locations = OrderedDict()
|
self.locations = OrderedDict()
|
||||||
listed_locations = set()
|
listed_locations = set()
|
||||||
|
@ -1133,7 +1133,7 @@ class Spoiler(object):
|
||||||
outfile.write('\nMisery Mire Medallion (Player %d): %s' % (player, self.medallions['Misery Mire (Player %d)' % player]))
|
outfile.write('\nMisery Mire Medallion (Player %d): %s' % (player, self.medallions['Misery Mire (Player %d)' % player]))
|
||||||
outfile.write('\nTurtle Rock Medallion (Player %d): %s' % (player, self.medallions['Turtle Rock (Player %d)' % player]))
|
outfile.write('\nTurtle Rock Medallion (Player %d): %s' % (player, self.medallions['Turtle Rock (Player %d)' % player]))
|
||||||
outfile.write('\n\nStarting Inventory:\n\n')
|
outfile.write('\n\nStarting Inventory:\n\n')
|
||||||
outfile.write('\n'.join(map(str, self.startinventory)))
|
outfile.write('\n'.join(self.startinventory))
|
||||||
outfile.write('\n\nLocations:\n\n')
|
outfile.write('\n\nLocations:\n\n')
|
||||||
outfile.write('\n'.join(['%s: %s' % (location, item) for grouping in self.locations.values() for (location, item) in grouping.items()]))
|
outfile.write('\n'.join(['%s: %s' % (location, item) for grouping in self.locations.values() for (location, item) in grouping.items()]))
|
||||||
outfile.write('\n\nShops:\n\n')
|
outfile.write('\n\nShops:\n\n')
|
||||||
|
|
45
Main.py
45
Main.py
|
@ -13,7 +13,7 @@ from Items import ItemFactory
|
||||||
from Regions import create_regions, mark_light_world_regions
|
from Regions import create_regions, mark_light_world_regions
|
||||||
from InvertedRegions import create_inverted_regions, mark_dark_world_regions
|
from InvertedRegions import create_inverted_regions, mark_dark_world_regions
|
||||||
from EntranceShuffle import link_entrances, link_inverted_entrances
|
from EntranceShuffle import link_entrances, link_inverted_entrances
|
||||||
from Rom import patch_rom, get_race_rom_patches, get_enemizer_patch, apply_rom_settings, LocalRom, JsonRom
|
from Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, JsonRom
|
||||||
from Rules import set_rules
|
from Rules import set_rules
|
||||||
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
|
from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive
|
||||||
from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items, balance_multiworld_progression
|
from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items, balance_multiworld_progression
|
||||||
|
@ -148,32 +148,25 @@ def main(args, seed=None):
|
||||||
or args.shufflepots[player] or sprite_random_on_hit)
|
or args.shufflepots[player] or sprite_random_on_hit)
|
||||||
|
|
||||||
rom = JsonRom() if args.jsonout or use_enemizer else LocalRom(args.rom)
|
rom = JsonRom() if args.jsonout or use_enemizer else LocalRom(args.rom)
|
||||||
local_rom = LocalRom(args.rom) if not args.jsonout and use_enemizer else None
|
|
||||||
|
|
||||||
patch_rom(world, player, rom, use_enemizer)
|
patch_rom(world, player, rom, use_enemizer)
|
||||||
rom_names.append((player, list(rom.name)))
|
rom_names.append((player, list(rom.name)))
|
||||||
|
|
||||||
enemizer_patch = []
|
|
||||||
if use_enemizer and (args.enemizercli or not args.jsonout):
|
if use_enemizer and (args.enemizercli or not args.jsonout):
|
||||||
enemizer_patch = get_enemizer_patch(world, player, rom, args.rom, args.enemizercli, args.shufflepots[player], sprite_random_on_hit)
|
patch_enemizer(world, player, rom, args.rom, args.enemizercli, args.shufflepots[player], sprite_random_on_hit)
|
||||||
|
if not args.jsonout:
|
||||||
|
patches = rom.patches
|
||||||
|
rom = LocalRom(args.rom)
|
||||||
|
rom.merge_enemizer_patches(patches)
|
||||||
|
|
||||||
|
if args.race:
|
||||||
|
patch_race_rom(rom)
|
||||||
|
|
||||||
|
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player], args.fastmenu[player], args.disablemusic[player], args.sprite[player], args.ow_palettes[player], args.uw_palettes[player], player_names)
|
||||||
|
|
||||||
if args.jsonout:
|
if args.jsonout:
|
||||||
jsonout[f'patch{player}'] = rom.patches
|
jsonout[f'patch{player}'] = rom.patches
|
||||||
if use_enemizer:
|
|
||||||
jsonout[f'enemizer{player}'] = enemizer_patch
|
|
||||||
if args.race:
|
|
||||||
jsonout[f'race{player}'] = get_race_rom_patches(rom)
|
|
||||||
else:
|
else:
|
||||||
if use_enemizer:
|
|
||||||
local_rom.patch_enemizer(rom.patches, os.path.join(os.path.dirname(args.enemizercli), "enemizerBasePatch.json"), enemizer_patch)
|
|
||||||
rom = local_rom
|
|
||||||
|
|
||||||
if args.race:
|
|
||||||
for addr, values in get_race_rom_patches(rom).items():
|
|
||||||
rom.write_bytes(int(addr), values)
|
|
||||||
|
|
||||||
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player], args.fastmenu[player], args.disablemusic[player], args.sprite[player], args.ow_palettes[player], args.uw_palettes[player], player_names)
|
|
||||||
|
|
||||||
mcsb_name = ''
|
mcsb_name = ''
|
||||||
if all([world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player], world.bigkeyshuffle[player]]):
|
if all([world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player], world.bigkeyshuffle[player]]):
|
||||||
mcsb_name = '-keysanity'
|
mcsb_name = '-keysanity'
|
||||||
|
@ -194,11 +187,15 @@ def main(args, seed=None):
|
||||||
"-nohints" if not world.hints[player] else "")) if not args.outputname else ''
|
"-nohints" if not world.hints[player] else "")) if not args.outputname else ''
|
||||||
rom.write_to_file(output_path(f'{outfilebase}{playername}{outfilesuffix}.sfc'))
|
rom.write_to_file(output_path(f'{outfilebase}{playername}{outfilesuffix}.sfc'))
|
||||||
|
|
||||||
with open(output_path('%s_multidata' % outfilebase), 'wb') as f:
|
multidata = zlib.compress(json.dumps((world.players,
|
||||||
jsonstr = json.dumps((world.players,
|
rom_names,
|
||||||
rom_names,
|
[((location.address, location.player), (location.item.code, location.item.player)) for location in world.get_filled_locations() if type(location.address) is int])
|
||||||
[((location.address, location.player), (location.item.code, location.item.player)) for location in world.get_filled_locations() if type(location.address) is int]))
|
).encode("utf-8"))
|
||||||
f.write(zlib.compress(jsonstr.encode("utf-8")))
|
if args.jsonout:
|
||||||
|
jsonout["multidata"] = list(multidata)
|
||||||
|
else:
|
||||||
|
with open(output_path('%s_multidata' % outfilebase), 'wb') as f:
|
||||||
|
f.write(multidata)
|
||||||
|
|
||||||
if args.create_spoiler and not args.jsonout:
|
if args.create_spoiler and not args.jsonout:
|
||||||
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
|
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
|
||||||
|
@ -449,6 +446,6 @@ def create_playthrough(world):
|
||||||
old_world.spoiler.paths[str(world.get_region('Inverted Big Bomb Shop', player))] = get_path(state, world.get_region('Inverted Big Bomb Shop', player))
|
old_world.spoiler.paths[str(world.get_region('Inverted Big Bomb Shop', player))] = get_path(state, world.get_region('Inverted Big Bomb Shop', player))
|
||||||
|
|
||||||
# we can finally output our playthrough
|
# we can finally output our playthrough
|
||||||
old_world.spoiler.playthrough = OrderedDict([("0", [item for item in world.precollected_items if item.advancement])])
|
old_world.spoiler.playthrough = OrderedDict([("0", [str(item) for item in world.precollected_items if item.advancement])])
|
||||||
for i, sphere in enumerate(collection_spheres):
|
for i, sphere in enumerate(collection_spheres):
|
||||||
old_world.spoiler.playthrough[str(i + 1)] = {str(location): str(location.item) for location in sphere}
|
old_world.spoiler.playthrough[str(i + 1)] = {str(location): str(location.item) for location in sphere}
|
||||||
|
|
77
Rom.py
77
Rom.py
|
@ -114,24 +114,11 @@ class LocalRom(object):
|
||||||
# if RANDOMIZERBASEHASH != patchedmd5.hexdigest():
|
# if 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.')
|
# 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 patch_enemizer(self, rando_patch, base_enemizer_patch_path, enemizer_patch):
|
def merge_enemizer_patches(self, patches):
|
||||||
# extend to 4MB
|
|
||||||
self.buffer.extend(bytearray([0x00] * (0x400000 - len(self.buffer))))
|
self.buffer.extend(bytearray([0x00] * (0x400000 - len(self.buffer))))
|
||||||
|
for address, values in patches.items():
|
||||||
# apply randomizer patches
|
|
||||||
for address, values in rando_patch.items():
|
|
||||||
self.write_bytes(int(address), values)
|
self.write_bytes(int(address), values)
|
||||||
|
|
||||||
# load base enemizer patches
|
|
||||||
with open(base_enemizer_patch_path, 'r') as f:
|
|
||||||
base_enemizer_patch = json.load(f)
|
|
||||||
for patch in base_enemizer_patch:
|
|
||||||
self.write_bytes(patch["address"], patch["patchData"])
|
|
||||||
|
|
||||||
# apply enemizer patches
|
|
||||||
for patch in enemizer_patch:
|
|
||||||
self.write_bytes(patch["address"], patch["patchData"])
|
|
||||||
|
|
||||||
def write_crc(self):
|
def write_crc(self):
|
||||||
crc = (sum(self.buffer[:0x7FDC] + self.buffer[0x7FE0:]) + 0x01FE) & 0xFFFF
|
crc = (sum(self.buffer[:0x7FDC] + self.buffer[0x7FE0:]) + 0x01FE) & 0xFFFF
|
||||||
inv = crc ^ 0xFFFF
|
inv = crc ^ 0xFFFF
|
||||||
|
@ -163,9 +150,10 @@ def read_rom(stream):
|
||||||
buffer = buffer[0x200:]
|
buffer = buffer[0x200:]
|
||||||
return buffer
|
return buffer
|
||||||
|
|
||||||
def get_enemizer_patch(world, player, rom, baserom_path, enemizercli, shufflepots, random_sprite_on_hit):
|
def patch_enemizer(world, player, rom, baserom_path, enemizercli, shufflepots, random_sprite_on_hit):
|
||||||
baserom_path = os.path.abspath(baserom_path)
|
baserom_path = os.path.abspath(baserom_path)
|
||||||
basepatch_path = os.path.abspath(local_path('data/base2current.json'))
|
basepatch_path = os.path.abspath(local_path('data/base2current.json'))
|
||||||
|
enemizer_basepatch_path = os.path.join(os.path.dirname(enemizercli), "enemizerBasePatch.json")
|
||||||
randopatch_path = os.path.abspath(output_path('enemizer_randopatch.json'))
|
randopatch_path = os.path.abspath(output_path('enemizer_randopatch.json'))
|
||||||
options_path = os.path.abspath(output_path('enemizer_options.json'))
|
options_path = os.path.abspath(output_path('enemizer_options.json'))
|
||||||
enemizer_output_path = os.path.abspath(output_path('enemizer_output.json'))
|
enemizer_output_path = os.path.abspath(output_path('enemizer_output.json'))
|
||||||
|
@ -245,20 +233,14 @@ def get_enemizer_patch(world, player, rom, baserom_path, enemizercli, shufflepot
|
||||||
'IcePalace': world.get_dungeon("Ice Palace", player).boss.enemizer_name,
|
'IcePalace': world.get_dungeon("Ice Palace", player).boss.enemizer_name,
|
||||||
'MiseryMire': world.get_dungeon("Misery Mire", player).boss.enemizer_name,
|
'MiseryMire': world.get_dungeon("Misery Mire", player).boss.enemizer_name,
|
||||||
'TurtleRock': world.get_dungeon("Turtle Rock", player).boss.enemizer_name,
|
'TurtleRock': world.get_dungeon("Turtle Rock", player).boss.enemizer_name,
|
||||||
|
'GanonsTower1': world.get_dungeon('Ganons Tower' if world.mode[player] != 'inverted' else 'Inverted Ganons Tower', player).bosses['bottom'].enemizer_name,
|
||||||
|
'GanonsTower2': world.get_dungeon('Ganons Tower' if world.mode[player] != 'inverted' else 'Inverted Ganons Tower', player).bosses['middle'].enemizer_name,
|
||||||
|
'GanonsTower3': world.get_dungeon('Ganons Tower' if world.mode[player] != 'inverted' else 'Inverted Ganons Tower', player).bosses['top'].enemizer_name,
|
||||||
'GanonsTower4': 'Agahnim2',
|
'GanonsTower4': 'Agahnim2',
|
||||||
'Ganon': 'Ganon',
|
'Ganon': 'Ganon',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if world.mode[player] != 'inverted':
|
|
||||||
options['ManualBosses']['GanonsTower1'] = world.get_dungeon('Ganons Tower', player).bosses['bottom'].enemizer_name
|
|
||||||
options['ManualBosses']['GanonsTower2'] = world.get_dungeon('Ganons Tower', player).bosses['middle'].enemizer_name
|
|
||||||
options['ManualBosses']['GanonsTower3'] = world.get_dungeon('Ganons Tower', player).bosses['top'].enemizer_name
|
|
||||||
else:
|
|
||||||
options['ManualBosses']['GanonsTower1'] = world.get_dungeon('Inverted Ganons Tower', player).bosses['bottom'].enemizer_name
|
|
||||||
options['ManualBosses']['GanonsTower2'] = world.get_dungeon('Inverted Ganons Tower', player).bosses['middle'].enemizer_name
|
|
||||||
options['ManualBosses']['GanonsTower3'] = world.get_dungeon('Inverted Ganons Tower', player).bosses['top'].enemizer_name
|
|
||||||
|
|
||||||
rom.write_to_file(randopatch_path)
|
rom.write_to_file(randopatch_path)
|
||||||
|
|
||||||
with open(options_path, 'w') as f:
|
with open(options_path, 'w') as f:
|
||||||
|
@ -273,17 +255,13 @@ def get_enemizer_patch(world, player, rom, baserom_path, enemizercli, shufflepot
|
||||||
'--output', enemizer_output_path],
|
'--output', enemizer_output_path],
|
||||||
cwd=os.path.dirname(enemizercli), stdout=subprocess.DEVNULL)
|
cwd=os.path.dirname(enemizercli), stdout=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
with open(enemizer_basepatch_path, 'r') as f:
|
||||||
|
for patch in json.load(f):
|
||||||
|
rom.write_bytes(patch["address"], patch["patchData"])
|
||||||
|
|
||||||
with open(enemizer_output_path, 'r') as f:
|
with open(enemizer_output_path, 'r') as f:
|
||||||
ret = json.load(f)
|
for patch in json.load(f):
|
||||||
|
rom.write_bytes(patch["address"], patch["patchData"])
|
||||||
if os.path.exists(randopatch_path):
|
|
||||||
os.remove(randopatch_path)
|
|
||||||
|
|
||||||
if os.path.exists(options_path):
|
|
||||||
os.remove(options_path)
|
|
||||||
|
|
||||||
if os.path.exists(enemizer_output_path):
|
|
||||||
os.remove(enemizer_output_path)
|
|
||||||
|
|
||||||
if random_sprite_on_hit:
|
if random_sprite_on_hit:
|
||||||
_populate_sprite_table()
|
_populate_sprite_table()
|
||||||
|
@ -295,11 +273,24 @@ def get_enemizer_patch(world, player, rom, baserom_path, enemizercli, shufflepot
|
||||||
|
|
||||||
for i, path in enumerate(sprites[:32]):
|
for i, path in enumerate(sprites[:32]):
|
||||||
sprite = Sprite(path)
|
sprite = Sprite(path)
|
||||||
ret.append({"address": 0x300000 + (i * 0x8000), "patchData": list(sprite.sprite)})
|
rom.write_bytes(0x300000 + (i * 0x8000), sprite.sprite)
|
||||||
ret.append({"address": 0x307000 + (i * 0x8000), "patchData": list(sprite.palette)})
|
rom.write_bytes(0x307000 + (i * 0x8000), sprite.palette)
|
||||||
ret.append({"address": 0x307078 + (i * 0x8000), "patchData": list(sprite.glove_palette)})
|
rom.write_bytes(0x307078 + (i * 0x8000), sprite.glove_palette)
|
||||||
|
|
||||||
return ret
|
try:
|
||||||
|
os.remove(randopatch_path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.remove(options_path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.remove(enemizer_output_path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
_sprite_table = {}
|
_sprite_table = {}
|
||||||
def _populate_sprite_table():
|
def _populate_sprite_table():
|
||||||
|
@ -1255,13 +1246,11 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
RaceRom = None
|
RaceRom = None
|
||||||
|
|
||||||
def get_race_rom_patches(rom):
|
def patch_race_rom(rom):
|
||||||
patches = {str(0x180213): [0x01, 0x00]} # Tournament Seed
|
rom.write_bytes(0x180213, [0x01, 0x00]) # Tournament Seed
|
||||||
|
|
||||||
if 'RaceRom' in sys.modules:
|
if 'RaceRom' in sys.modules:
|
||||||
RaceRom.encrypt(rom, patches)
|
RaceRom.encrypt(rom)
|
||||||
|
|
||||||
return patches
|
|
||||||
|
|
||||||
def write_custom_shops(rom, world, player):
|
def write_custom_shops(rom, world, player):
|
||||||
shops = [shop for shop in world.shops if shop.replaceable and shop.active and shop.region.player == player]
|
shops = [shop for shop in world.shops if shop.replaceable and shop.active and shop.region.player == player]
|
||||||
|
|
Loading…
Reference in New Issue