| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  | import tkinter as tk | 
					
						
							|  |  |  | import argparse | 
					
						
							|  |  |  | import logging | 
					
						
							|  |  |  | import random | 
					
						
							|  |  |  | import os | 
					
						
							| 
									
										
										
										
											2022-12-10 21:11:40 -06:00
										 |  |  | import zipfile | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  | from itertools import chain | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from BaseClasses import MultiWorld | 
					
						
							|  |  |  | from Options import Choice, Range, Toggle | 
					
						
							|  |  |  | from worlds.oot import OOTWorld | 
					
						
							| 
									
										
										
										
											2021-11-15 08:14:55 -06:00
										 |  |  | from worlds.oot.Cosmetics import patch_cosmetics | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  | from worlds.oot.Options import cosmetic_options, sfx_options | 
					
						
							|  |  |  | from worlds.oot.Rom import Rom, compress_rom_file | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  | from worlds.oot.N64Patch import apply_patch_file | 
					
						
							| 
									
										
										
										
											2022-01-04 10:16:09 -06:00
										 |  |  | from worlds.oot.Utils import data_path | 
					
						
							| 
									
										
										
										
											2021-11-15 08:14:55 -06:00
										 |  |  | from Utils import local_path | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | logger = logging.getLogger('OoTAdjuster') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							|  |  |  |     parser = argparse.ArgumentParser() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.add_argument('--rom', default='',  | 
					
						
							|  |  |  |         help='Path to an OoT randomized ROM to adjust.') | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  |     parser.add_argument('--vanilla_rom', default='', | 
					
						
							|  |  |  |         help='Path to a vanilla OoT ROM for patching.') | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  |     for name, option in chain(cosmetic_options.items(), sfx_options.items()): | 
					
						
							|  |  |  |         parser.add_argument('--'+name, default=None, | 
					
						
							|  |  |  |             help=option.__doc__) | 
					
						
							|  |  |  |     parser.add_argument('--is_glitched', default=False, action='store_true', | 
					
						
							|  |  |  |         help='Setting this to true will enable protection on kokiri tunic colors for weirdshot.') | 
					
						
							|  |  |  |     parser.add_argument('--deathlink', | 
					
						
							|  |  |  |         help='Enable DeathLink system', action='store_true') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     args = parser.parse_args() | 
					
						
							|  |  |  |     if not os.path.isfile(args.rom): | 
					
						
							|  |  |  |         adjustGUI() | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         adjust(args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def adjustGUI(): | 
					
						
							|  |  |  |     from tkinter import Tk, LEFT, BOTTOM, TOP, E, W, \ | 
					
						
							|  |  |  |         StringVar, IntVar, Checkbutton, Frame, Label, X, Entry, Button, \ | 
					
						
							|  |  |  |         OptionMenu, filedialog, messagebox, ttk | 
					
						
							|  |  |  |     from argparse import Namespace | 
					
						
							|  |  |  |     from Main import __version__ as MWVersion | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     window = tk.Tk() | 
					
						
							|  |  |  |     window.wm_title(f"Archipelago {MWVersion} OoT Adjuster") | 
					
						
							|  |  |  |     set_icon(window) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  |     opts = Namespace() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  |     # Select ROM | 
					
						
							|  |  |  |     romDialogFrame = Frame(window) | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  |     romLabel = Label(romDialogFrame, text='Rom/patch to adjust') | 
					
						
							|  |  |  |     vanillaLabel = Label(romDialogFrame, text='OoT Base Rom') | 
					
						
							|  |  |  |     opts.rom = StringVar() | 
					
						
							|  |  |  |     opts.vanilla_rom = StringVar(value="The Legend of Zelda - Ocarina of Time.z64") | 
					
						
							|  |  |  |     romEntry = Entry(romDialogFrame, textvariable=opts.rom) | 
					
						
							|  |  |  |     vanillaEntry = Entry(romDialogFrame, textvariable=opts.vanilla_rom) | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def RomSelect(): | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  |         rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".z64", ".n64", ".apz5")), ("All Files", "*")]) | 
					
						
							|  |  |  |         opts.rom.set(rom) | 
					
						
							|  |  |  |     def VanillaSelect(): | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  |         rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".z64", ".n64")), ("All Files", "*")]) | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  |         opts.vanilla_rom.set(rom) | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     romSelectButton = Button(romDialogFrame, text='Select Rom', command=RomSelect) | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  |     vanillaSelectButton = Button(romDialogFrame, text='Select Rom', command=VanillaSelect) | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  |     romDialogFrame.pack(side=TOP, expand=True, fill=X) | 
					
						
							|  |  |  |     romLabel.pack(side=LEFT) | 
					
						
							|  |  |  |     romEntry.pack(side=LEFT, expand=True, fill=X) | 
					
						
							|  |  |  |     romSelectButton.pack(side=LEFT) | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  |     vanillaLabel.pack(side=LEFT) | 
					
						
							|  |  |  |     vanillaEntry.pack(side=LEFT, expand=True, fill=X) | 
					
						
							|  |  |  |     vanillaSelectButton.pack(side=LEFT) | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Cosmetic options | 
					
						
							|  |  |  |     romSettingsFrame = Frame(window) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def dropdown_option(type, option_name, row, column): | 
					
						
							|  |  |  |         if type == 'cosmetic': | 
					
						
							|  |  |  |             option = cosmetic_options[option_name] | 
					
						
							|  |  |  |         elif type == 'sfx': | 
					
						
							|  |  |  |             option = sfx_options[option_name] | 
					
						
							|  |  |  |         optionFrame = Frame(romSettingsFrame) | 
					
						
							|  |  |  |         optionFrame.grid(row=row, column=column, sticky=E) | 
					
						
							| 
									
										
										
										
											2022-02-02 16:29:29 +01:00
										 |  |  |         optionLabel = Label(optionFrame, text=option.display_name) | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  |         optionLabel.pack(side=LEFT) | 
					
						
							|  |  |  |         setattr(opts, option_name, StringVar()) | 
					
						
							|  |  |  |         getattr(opts, option_name).set(option.name_lookup[option.default]) | 
					
						
							|  |  |  |         optionMenu = OptionMenu(optionFrame, getattr(opts, option_name), *option.name_lookup.values()) | 
					
						
							|  |  |  |         optionMenu.pack(side=LEFT) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'default_targeting', 0, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'display_dpad', 0, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'correct_model_colors', 0, 2) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'background_music', 1, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'fanfares', 1, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'ocarina_fanfares', 1, 2) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'kokiri_color', 2, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'goron_color', 2, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'zora_color', 2, 2) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'silver_gauntlets_color', 3, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'golden_gauntlets_color', 3, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'mirror_shield_frame_color', 3, 2) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'navi_color_default_inner', 4, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'navi_color_default_outer', 4, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'navi_color_enemy_inner', 5, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'navi_color_enemy_outer', 5, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'navi_color_npc_inner', 6, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'navi_color_npc_outer', 6, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'navi_color_prop_inner', 7, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'navi_color_prop_outer', 7, 1) | 
					
						
							|  |  |  |     # sword_trail_duration, 8, 2 | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'sword_trail_color_inner', 8, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'sword_trail_color_outer', 8, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'bombchu_trail_color_inner', 9, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'bombchu_trail_color_outer', 9, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'boomerang_trail_color_inner', 10, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'boomerang_trail_color_outer', 10, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'heart_color', 11, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'magic_color', 12, 0) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'a_button_color', 11, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'b_button_color', 11, 2) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'c_button_color', 12, 1) | 
					
						
							|  |  |  |     dropdown_option('cosmetic', 'start_button_color', 12, 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dropdown_option('sfx', 'sfx_navi_overworld', 14, 0) | 
					
						
							|  |  |  |     dropdown_option('sfx', 'sfx_navi_enemy', 14, 1) | 
					
						
							|  |  |  |     dropdown_option('sfx', 'sfx_low_hp', 14, 2) | 
					
						
							|  |  |  |     dropdown_option('sfx', 'sfx_menu_cursor', 15, 0) | 
					
						
							|  |  |  |     dropdown_option('sfx', 'sfx_menu_select', 15, 1) | 
					
						
							|  |  |  |     dropdown_option('sfx', 'sfx_nightfall', 15, 2) | 
					
						
							|  |  |  |     dropdown_option('sfx', 'sfx_horse_neigh', 16, 0) | 
					
						
							|  |  |  |     dropdown_option('sfx', 'sfx_hover_boots', 16, 1) | 
					
						
							|  |  |  |     dropdown_option('sfx', 'sfx_ocarina', 16, 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Special cases | 
					
						
							|  |  |  |     # Sword trail duration is a range | 
					
						
							|  |  |  |     option = cosmetic_options['sword_trail_duration'] | 
					
						
							|  |  |  |     optionFrame = Frame(romSettingsFrame) | 
					
						
							|  |  |  |     optionFrame.grid(row=8, column=2, sticky=E) | 
					
						
							| 
									
										
										
										
											2022-02-02 16:29:29 +01:00
										 |  |  |     optionLabel = Label(optionFrame, text=option.display_name) | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  |     optionLabel.pack(side=LEFT) | 
					
						
							|  |  |  |     setattr(opts, 'sword_trail_duration', StringVar()) | 
					
						
							|  |  |  |     getattr(opts, 'sword_trail_duration').set(option.default) | 
					
						
							|  |  |  |     optionMenu = OptionMenu(optionFrame, getattr(opts, 'sword_trail_duration'), *range(4, 21)) | 
					
						
							|  |  |  |     optionMenu.pack(side=LEFT) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Glitched is a checkbox | 
					
						
							|  |  |  |     opts.is_glitched = IntVar(value=0) | 
					
						
							|  |  |  |     glitched_checkbox = Checkbutton(romSettingsFrame, text="Glitched Logic?", variable=opts.is_glitched) | 
					
						
							|  |  |  |     glitched_checkbox.grid(row=17, column=0, sticky=W) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Deathlink is a checkbox | 
					
						
							|  |  |  |     opts.deathlink = IntVar(value=0) | 
					
						
							|  |  |  |     deathlink_checkbox = Checkbutton(romSettingsFrame, text="DeathLink (Team Deaths)", variable=opts.deathlink) | 
					
						
							|  |  |  |     deathlink_checkbox.grid(row=17, column=1, sticky=W) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     romSettingsFrame.pack(side=TOP) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def adjustRom(): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             guiargs = Namespace() | 
					
						
							|  |  |  |             options = vars(opts) | 
					
						
							|  |  |  |             for o in options: | 
					
						
							|  |  |  |                 result = options[o].get() | 
					
						
							|  |  |  |                 if result == 'true': | 
					
						
							|  |  |  |                     result = True | 
					
						
							|  |  |  |                 if result == 'false': | 
					
						
							|  |  |  |                     result = False | 
					
						
							|  |  |  |                 setattr(guiargs, o, result) | 
					
						
							|  |  |  |             guiargs.sword_trail_duration = int(guiargs.sword_trail_duration) | 
					
						
							|  |  |  |             path = adjust(guiargs) | 
					
						
							|  |  |  |         except Exception as e: | 
					
						
							|  |  |  |             logging.exception(e) | 
					
						
							|  |  |  |             messagebox.showerror(title="Error while adjusting Rom", message=str(e)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             messagebox.showinfo(title="Success", message=f"Rom patched successfully to {path}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Adjust button | 
					
						
							|  |  |  |     bottomFrame = Frame(window) | 
					
						
							|  |  |  |     adjustButton = Button(bottomFrame, text='Adjust Rom', command=adjustRom) | 
					
						
							|  |  |  |     adjustButton.pack(side=BOTTOM, padx=(5, 5)) | 
					
						
							|  |  |  |     bottomFrame.pack(side=BOTTOM, pady=(5, 5)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     window.mainloop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def set_icon(window): | 
					
						
							|  |  |  |     logo = tk.PhotoImage(file=local_path('data', 'icon.png')) | 
					
						
							|  |  |  |     window.tk.call('wm', 'iconphoto', window._w, logo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def adjust(args): | 
					
						
							|  |  |  |     # Create a fake world and OOTWorld to use as a base | 
					
						
							|  |  |  |     world = MultiWorld(1) | 
					
						
							| 
									
										
										
										
											2023-02-02 01:14:23 +01:00
										 |  |  |     world.per_slot_randoms = {1: random} | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  |     ootworld = OOTWorld(world, 1) | 
					
						
							|  |  |  |     # Set options in the fake OOTWorld | 
					
						
							|  |  |  |     for name, option in chain(cosmetic_options.items(), sfx_options.items()): | 
					
						
							|  |  |  |         result = getattr(args, name, None) | 
					
						
							|  |  |  |         if result is None: | 
					
						
							|  |  |  |             if issubclass(option, Choice): | 
					
						
							|  |  |  |                 result = option.name_lookup[option.default] | 
					
						
							|  |  |  |             elif issubclass(option, Range) or issubclass(option, Toggle): | 
					
						
							|  |  |  |                 result = option.default | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise Exception("Unsupported option type") | 
					
						
							|  |  |  |         setattr(ootworld, name, result) | 
					
						
							|  |  |  |     ootworld.logic_rules = 'glitched' if args.is_glitched else 'glitchless' | 
					
						
							|  |  |  |     ootworld.death_link = args.deathlink | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-04 10:16:09 -06:00
										 |  |  |     delete_zootdec = False | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  |     if os.path.splitext(args.rom)[-1] in ['.z64', '.n64']: | 
					
						
							|  |  |  |         # Load up the ROM | 
					
						
							|  |  |  |         rom = Rom(file=args.rom, force_use=True) | 
					
						
							| 
									
										
										
										
											2022-01-04 10:16:09 -06:00
										 |  |  |         delete_zootdec = True | 
					
						
							| 
									
										
										
										
											2022-12-10 21:11:40 -06:00
										 |  |  |     elif os.path.splitext(args.rom)[-1] in ['.apz5', '.zpf']: | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  |         # Load vanilla ROM | 
					
						
							|  |  |  |         rom = Rom(file=args.vanilla_rom, force_use=True) | 
					
						
							| 
									
										
										
										
											2022-12-10 21:11:40 -06:00
										 |  |  |         apz5_file = args.rom | 
					
						
							|  |  |  |         base_name = os.path.splitext(apz5_file)[0] | 
					
						
							| 
									
										
										
										
											2021-11-14 22:58:56 -06:00
										 |  |  |         # Patch file | 
					
						
							| 
									
										
										
										
											2022-12-10 21:11:40 -06:00
										 |  |  |         apply_patch_file(rom, apz5_file, | 
					
						
							|  |  |  |             sub_file=(os.path.basename(base_name) + '.zpf' | 
					
						
							|  |  |  |                 if zipfile.is_zipfile(apz5_file) | 
					
						
							|  |  |  |                 else None)) | 
					
						
							| 
									
										
										
										
											2021-11-15 08:14:55 -06:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2022-12-10 21:11:40 -06:00
										 |  |  |         raise Exception("Invalid file extension; requires .n64, .z64, .apz5, .zpf") | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  |     # Call patch_cosmetics | 
					
						
							| 
									
										
										
										
											2022-01-04 10:16:09 -06:00
										 |  |  |     try: | 
					
						
							|  |  |  |         patch_cosmetics(ootworld, rom) | 
					
						
							|  |  |  |         rom.write_byte(rom.sym('DEATH_LINK'), args.deathlink) | 
					
						
							|  |  |  |         # Output new file | 
					
						
							|  |  |  |         path_pieces = os.path.splitext(args.rom) | 
					
						
							|  |  |  |         decomp_path = path_pieces[0] + '-adjusted-decomp.n64' | 
					
						
							|  |  |  |         comp_path = path_pieces[0] + '-adjusted.n64' | 
					
						
							|  |  |  |         rom.write_to_file(decomp_path) | 
					
						
							|  |  |  |         os.chdir(data_path("Compress")) | 
					
						
							|  |  |  |         compress_rom_file(decomp_path, comp_path) | 
					
						
							|  |  |  |         os.remove(decomp_path) | 
					
						
							|  |  |  |     finally: | 
					
						
							|  |  |  |         if delete_zootdec: | 
					
						
							|  |  |  |             os.chdir(os.path.split(__file__)[0]) | 
					
						
							|  |  |  |             os.remove("ZOOTDEC.z64") | 
					
						
							| 
									
										
										
										
											2021-11-14 16:50:49 -06:00
										 |  |  |     return comp_path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     main() |