import argparse import copy import json import os import logging import random import textwrap import shlex import sys from Main import main import source.classes.constants as CONST from source.classes.BabelFish import BabelFish class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter): def _get_help_string(self, action): return textwrap.dedent(action.help) def parse_arguments(argv, no_defaults=False): def defval(value): return value if not no_defaults else None # get settings settings = get_settings() fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") # we need to know how many players we have first parser = argparse.ArgumentParser(add_help=False) parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255)) multiargs, _ = parser.parse_known_args(argv) parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) # get args args = [] with open(os.path.join("resources","app","cli","args.json")) as argsFile: args = json.load(argsFile) for arg in args: argdata = args[arg] argname = "--" + arg argatts = {} argatts["help"] = "(default: %(default)s)" if "action" in argdata: argatts["action"] = argdata["action"] if "choices" in argdata: argatts["choices"] = argdata["choices"] argatts["const"] = argdata["choices"][0] argatts["default"] = argdata["choices"][0] argatts["nargs"] = "?" elif arg in settings: argatts["default"] = defval(settings[arg] != 0) if "type" in argdata and argdata["type"] == "bool" else defval(settings[arg]) arghelp = fish.translate("cli","help",arg) if "help" in argdata and argdata["help"] == "suppress": argatts["help"] = argparse.SUPPRESS elif not isinstance(arghelp,str): argatts["help"] = '\n'.join(arghelp).replace("\\'","'") parser.add_argument(argname,**argatts) parser.add_argument('--seed', default=defval(int(settings["seed"]) if settings["seed"] != "" and settings["seed"] is not None else None), help="\n".join(fish.translate("cli","help","seed")), type=int) parser.add_argument('--count', default=defval(int(settings["count"]) if settings["count"] != "" and settings["count"] is not None else 1), help="\n".join(fish.translate("cli","help","count")), type=int) parser.add_argument('--customitemarray', default={}, help=argparse.SUPPRESS) # included for backwards compatibility parser.add_argument('--beemizer', default=defval(settings["beemizer"]), type=lambda value: min(max(int(value), 0), 4)) parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255)) parser.add_argument('--teams', default=defval(1), type=lambda value: max(int(value), 1)) if multiargs.multi: for player in range(1, multiargs.multi + 1): parser.add_argument(f'--p{player}', default=defval(''), help=argparse.SUPPRESS) ret = parser.parse_args(argv) if ret.keysanity: ret.mapshuffle, ret.compassshuffle, ret.keyshuffle, ret.bigkeyshuffle = [True] * 4 if multiargs.multi: defaults = copy.deepcopy(ret) for player in range(1, multiargs.multi + 1): playerargs = parse_arguments(shlex.split(getattr(ret,f"p{player}")), True) for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'shuffle', 'door_shuffle', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', 'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots', 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep', 'remote_items']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) if player == 1: setattr(ret, name, {1: value}) else: getattr(ret, name)[player] = value return ret def get_settings(): # set default settings settings = { "lang": "en", "retro": False, "mode": "open", "logic": "noglitches", "goal": "ganon", "crystals_gt": "7", "crystals_ganon": "7", "swords": "random", "difficulty": "normal", "item_functionality": "normal", "timer": "none", "progressive": "on", "accessibility": "items", "algorithm": "balanced", "openpyramid": False, "shuffleganon": False, "shuffle": "vanilla", "shufflepots": False, "shuffleenemies": "none", "shufflebosses": "none", "enemy_damage": "default", "enemy_health": "default", "enemizercli": os.path.join(".", "EnemizerCLI", "EnemizerCLI.Core"), "mapshuffle": False, "compassshuffle": False, "keyshuffle": False, "bigkeyshuffle": False, "keysanity": False, "door_shuffle": "basic", "experimental": 0, "dungeon_counters": "default", "multi": 1, "names": "", "hints": True, "disablemusic": False, "quickswap": False, "heartcolor": "red", "heartbeep": "normal", "sprite": None, "fastmenu": "normal", "ow_palettes": "default", "uw_palettes": "default", "create_spoiler": False, "skip_playthrough": False, "suppress_rom": False, "usestartinventory": False, "custom": False, "rom": os.path.join(".", "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"), "seed": None, "count": None, "startinventory": "", "beemizer": 0, "remote_items": False, "race": False, "customitemarray": { "bow": 0, "progressivebow": 2, "boomerang": 1, "redmerang": 1, "hookshot": 1, "mushroom": 1, "powder": 1, "firerod": 1, "icerod": 1, "bombos": 1, "ether": 1, "quake": 1, "lamp": 1, "hammer": 1, "shovel": 1, "flute": 1, "bugnet": 1, "book": 1, "bottle": 4, "somaria": 1, "byrna": 1, "cape": 1, "mirror": 1, "boots": 1, "powerglove": 0, "titansmitt": 0, "progressiveglove": 2, "flippers": 1, "pearl": 1, "heartpiece": 24, "heartcontainer": 10, "sancheart": 1, "sword1": 0, "sword2": 0, "sword3": 0, "sword4": 0, "progressivesword": 4, "shield1": 0, "shield2": 0, "shield3": 0, "progressiveshield": 3, "mail2": 0, "mail3": 0, "progressivemail": 2, "halfmagic": 1, "quartermagic": 0, "bombsplus5": 0, "bombsplus10": 0, "arrowsplus5": 0, "arrowsplus10": 0, "arrow1": 1, "arrow10": 12, "bomb1": 0, "bomb3": 16, "bomb10": 1, "rupee1": 2, "rupee5": 4, "rupee20": 28, "rupee50": 7, "rupee100": 1, "rupee300": 5, "blueclock": 0, "greenclock": 0, "redclock": 0, "silversupgrade": 0, "generickeys": 0, "triforcepieces": 0, "triforcepiecesgoal": 0, "triforce": 0, "rupoor": 0, "rupoorcost": 10 }, "randomSprite": False, "outputpath": os.path.join("."), "saveonexit": "ask", "outputname": "", "startinventoryarray": {} } if sys.platform.lower().find("windows"): settings["enemizercli"] += ".exe" # read saved settings file if it exists and set these settings_path = os.path.join(".", "resources", "user", "settings.json") if os.path.exists(settings_path): with(open(settings_path)) as json_file: data = json.load(json_file) for k, v in data.items(): settings[k] = v return settings # Priority fallback is: # 1: CLI # 2: Settings file # 3: Canned defaults def get_args_priority(settings_args, gui_args, cli_args): args = {} args["settings"] = get_settings() if settings_args is None else settings_args args["gui"] = {} if gui_args is None else gui_args args["cli"] = cli_args args["load"] = args["settings"] if args["gui"] is not None: for k in args["gui"]: if k not in args["load"] or args["load"][k] != args["gui"]: args["load"][k] = args["gui"][k] if args["cli"] is None: args["cli"] = {} cli = vars(parse_arguments(None)) for k, v in cli.items(): if isinstance(v, dict) and 1 in v: args["cli"][k] = v[1] else: args["cli"][k] = v load_doesnt_have_key = k not in args["load"] different_val = (k in args["load"] and k in args["cli"]) and (args["load"][k] != args["cli"][k]) cli_has_empty_dict = k in args["cli"] and isinstance(args["cli"][k], dict) and len(args["cli"][k]) == 0 if load_doesnt_have_key or different_val: if not cli_has_empty_dict: args["load"][k] = args["cli"][k] return args