2023-01-22 00:18:00 -05:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
2023-01-22 18:46:23 -05:00
|
|
|
# seven_mods.py
|
2023-01-22 00:18:00 -05:00
|
|
|
#
|
|
|
|
# Copyright 2023 Patrick Marsee <patrickm@patrick-b550mc>
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
# MA 02110-1301, USA.
|
|
|
|
#
|
|
|
|
#
|
|
|
|
|
|
|
|
import os
|
|
|
|
import os.path
|
|
|
|
import json
|
|
|
|
import re
|
2023-01-22 14:35:02 -05:00
|
|
|
import sys
|
2023-01-30 23:47:19 -05:00
|
|
|
import xml.etree.ElementTree as et # I don't need anything powerful for this
|
2023-01-22 00:18:00 -05:00
|
|
|
|
|
|
|
PROFILE_DB = "profile_db.json"
|
|
|
|
HOME = os.getenv("HOME", os.getcwd()) # Use relative path in attmpt to salvage things
|
|
|
|
MODS_DIR = os.getcwd()
|
|
|
|
STEAM_LIBRARIES_DIR = os.path.join(HOME, ".local/share/Steam/config/libraryfolders.vdf")
|
|
|
|
SEVEN_DIR = os.path.join(HOME, "/.steam/steam/steamapps/common/7 Days To Die")
|
|
|
|
CONFIG_FILE = "seven-mods.cfg"
|
|
|
|
|
2023-01-22 14:35:02 -05:00
|
|
|
HELP_TEXT="""Usage: seven-mods.py <command> <args ...>
|
|
|
|
Valid commands are:
|
|
|
|
- help : usage guide.
|
|
|
|
- list : show a listing of all installed mods. Disabled mods appear in red.
|
|
|
|
Enabled ones appear in green, and are followed by an asterisk (`*`).
|
|
|
|
`list` also accepts an optional argument, `profiles`, which tells it
|
|
|
|
to list all saved profiles instead of mods.
|
|
|
|
- enable : enable mods. A list of mod names can be given, separated by spaces.
|
|
|
|
The mod names are the names of their respective folders, *not* the
|
|
|
|
names listed in their respective `ModInfo.xml` files. Alternatively,
|
|
|
|
to enable all mods, simply type `-a` rather than listing them all
|
|
|
|
out.
|
|
|
|
- disable : disable mods. Arguments are the same as for `enable`, including `-a`
|
|
|
|
to disable all mods.
|
|
|
|
- toggle : toggles mods on or off. Does *not* support `-a`.
|
|
|
|
- save : saves the currently enabled mods to a profile. The only argument
|
|
|
|
that should be given is the name of the profile to save. If the
|
|
|
|
profile already exists, then it will be overwritten.
|
|
|
|
- load : sets the enabled mods to those defined by the given profile.
|
|
|
|
- config : reconfigure paths to your mod and 7 Days to Die directories."""
|
|
|
|
|
2023-01-22 00:18:00 -05:00
|
|
|
class Config:
|
|
|
|
|
2023-02-02 22:44:08 -05:00
|
|
|
defaults = {"mods_dir": MODS_DIR,
|
|
|
|
"seven_dir": "",
|
|
|
|
"warn_load": True,
|
|
|
|
"warn_save": True,
|
|
|
|
"warn_delete": True}
|
2023-01-22 00:18:00 -05:00
|
|
|
def __init__(self, **kwargs):
|
2023-02-02 22:44:08 -05:00
|
|
|
self.settings = Config.defaults.copy()
|
|
|
|
for key in kwargs:
|
|
|
|
self.set_val(key, kwargs[key])
|
|
|
|
|
|
|
|
def __getattr__(self, name: str):
|
|
|
|
if name in self.settings:
|
|
|
|
return self.settings[name]
|
2023-01-22 00:18:00 -05:00
|
|
|
else:
|
2023-02-02 22:44:08 -05:00
|
|
|
raise AttributeError(f"Config has no attribute named '{name}'.")
|
|
|
|
|
2023-02-07 20:09:54 -05:00
|
|
|
def set_val(self, name: str, val):
|
2023-02-02 22:44:08 -05:00
|
|
|
if name in ("warn_load", "warn_save", "warn_delete"):
|
|
|
|
# boolean
|
2023-02-07 20:09:54 -05:00
|
|
|
if isinstance(val, bool):
|
|
|
|
self.settings[name] = val
|
|
|
|
elif isinstance(val, str):
|
|
|
|
if val.casefold() in ("on", "true", "yes", "y"):
|
|
|
|
self.settings[name] = True
|
|
|
|
elif val.casefold() in ("off", "false", "no", "n"):
|
|
|
|
self.settings[name] = False
|
|
|
|
else:
|
|
|
|
raise ValueError(f"Input cannot be interpreted as boolean: '{val}'")
|
2023-02-02 22:44:08 -05:00
|
|
|
else:
|
2023-02-07 20:09:54 -05:00
|
|
|
raise TypeError(f"Input must be a bool or string.")
|
2023-02-02 22:44:08 -05:00
|
|
|
elif name in Config.defaults:
|
|
|
|
# siletly remove newlines, replace with spaces
|
|
|
|
# This is to prevent weird things.
|
|
|
|
self.settings[name] = val.replace("\n", " ")
|
2023-01-22 00:18:00 -05:00
|
|
|
else:
|
2023-02-02 22:44:08 -05:00
|
|
|
raise ValueError(f"No configuration setting: '{name}'")
|
2023-01-22 00:18:00 -05:00
|
|
|
|
|
|
|
class ModProfiles:
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.profiles = {}
|
|
|
|
|
|
|
|
def load_mod_profiles(self):
|
|
|
|
try:
|
|
|
|
with open(PROFILE_DB, 'r') as pro_file:
|
|
|
|
self.profiles = json.load(pro_file)
|
|
|
|
except:
|
|
|
|
# If loading failed, then assume that there are no profiles currently.
|
|
|
|
# We don't need to do anything special here.
|
|
|
|
pass
|
|
|
|
|
|
|
|
def save_mod_profiles(self):
|
|
|
|
try:
|
|
|
|
with open(PROFILE_DB, 'w') as pro_file:
|
|
|
|
json.dump(self.profiles, pro_file)
|
|
|
|
except:
|
|
|
|
print("Could not save profiles.")
|
|
|
|
|
|
|
|
VAL_RE = re.compile(r'\s*"([^"]*)"', re.MULTILINE)
|
|
|
|
DICT_START_RE = re.compile(r'\s*{', re.MULTILINE)
|
|
|
|
DICT_END_RE = re.compile(r'\s*}', re.MULTILINE)
|
|
|
|
|
2023-01-22 14:35:02 -05:00
|
|
|
class SevenModsError(RuntimeError):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class VDF_Error(SevenModsError):
|
2023-01-22 00:18:00 -05:00
|
|
|
pass
|
|
|
|
|
|
|
|
def parse_vdf_val(text: str) -> tuple:
|
|
|
|
ret = VAL_RE.match(text)
|
|
|
|
if ret:
|
|
|
|
return ret[1], ret.end()
|
|
|
|
text_sample = text[:min(32, len(text))]
|
|
|
|
if len(text) > 32:
|
|
|
|
text_sample += "..."
|
|
|
|
return None, 0
|
|
|
|
|
|
|
|
def parse_vdf_dict(text: str):
|
|
|
|
start_match = DICT_START_RE.match(text)
|
|
|
|
if start_match:
|
|
|
|
start_cursor = start_match.end()
|
|
|
|
text = text[start_cursor:]
|
|
|
|
ret = {}
|
|
|
|
end_match = DICT_END_RE.match(text)
|
|
|
|
while not end_match:
|
|
|
|
var, var_cursor = parse_vdf_var(text)
|
|
|
|
if var != None:
|
|
|
|
ret[var[0]] = var[1]
|
|
|
|
text = text[var_cursor:]
|
|
|
|
start_cursor += var_cursor
|
|
|
|
else:
|
|
|
|
# if it's neither the end of a dictionary or the start of a var,
|
|
|
|
# then it's a syntax error.
|
|
|
|
text_sample = text[:min(32, len(text))]
|
|
|
|
if len(text) > 32:
|
|
|
|
text_sample += "..."
|
|
|
|
raise VDF_Error(f"Syntax error: dict failed to parse.\nValue: {ret}\nText: {text_sample}")
|
|
|
|
end_match = DICT_END_RE.match(text)
|
|
|
|
return ret, start_cursor + end_match.end()
|
|
|
|
else:
|
|
|
|
return None, 0
|
|
|
|
|
|
|
|
def parse_vdf_var(text:str) -> tuple:
|
|
|
|
var_name, name_cursor = parse_vdf_val(text)
|
|
|
|
if var_name == None:
|
|
|
|
text_sample = text[:min(32, len(text))]
|
|
|
|
if len(text) > 32:
|
|
|
|
text_sample += "..."
|
|
|
|
raise VDF_Error(f"Syntax error: var failed to parse name. Text: {text_sample}")
|
|
|
|
text = text[name_cursor:]
|
|
|
|
var_val, val_cursor = parse_vdf_val(text)
|
|
|
|
if var_val == None: # it didn't match, so it's a dict
|
|
|
|
var_val, val_cursor = parse_vdf_dict(text)
|
|
|
|
if var_val == None:
|
|
|
|
text_sample = text[:min(32, len(text))]
|
|
|
|
if len(text) > 32:
|
|
|
|
text_sample += "..."
|
|
|
|
raise VDF_Error(f"Syntax error: var failed to parse value. Text: {text_sample}")
|
|
|
|
return (var_name, var_val), val_cursor + name_cursor
|
|
|
|
|
|
|
|
def parse_vdf(text: str) -> dict:
|
|
|
|
# val : "[^"]"
|
|
|
|
#
|
|
|
|
# var : val dict
|
|
|
|
# | val val
|
|
|
|
#
|
|
|
|
# dict : { var ... }
|
|
|
|
|
|
|
|
return parse_vdf_var(text)[0]
|
|
|
|
|
|
|
|
def get_seven_days_install_path() -> str:
|
|
|
|
if os.path.isfile(STEAM_LIBRARIES_DIR):
|
|
|
|
text = None
|
|
|
|
with open(STEAM_LIBRARIES_DIR, 'r') as library_file:
|
|
|
|
text = library_file.read()
|
|
|
|
vdf_data = parse_vdf(text)
|
|
|
|
for folder in vdf_data[1]:
|
|
|
|
if "251570" in vdf_data[1][folder]["apps"]:
|
|
|
|
return os.path.join(vdf_data[1][folder]["path"], "steamapps/common/7 Days To Die")
|
|
|
|
else:
|
|
|
|
return ""
|
|
|
|
|
|
|
|
def get_internal_mods_path(cfg: Config) -> str:
|
|
|
|
ret = os.path.join(cfg.seven_dir, "Mods")
|
|
|
|
if not os.path.exists(ret):
|
|
|
|
try:
|
2023-01-22 14:35:02 -05:00
|
|
|
os.mkdir(ret)
|
2023-01-22 00:18:00 -05:00
|
|
|
except:
|
2023-01-22 14:35:02 -05:00
|
|
|
raise SevenModsError(f"Could not create directory at {ret}.")
|
2023-01-22 00:18:00 -05:00
|
|
|
elif not os.path.isdir(ret):
|
|
|
|
os.remove(ret)
|
|
|
|
os.makedirs(ret)
|
|
|
|
return ret
|
|
|
|
|
|
|
|
def prompt_configuration_cli():
|
|
|
|
SEVEN_DIR = get_seven_days_install_path()
|
|
|
|
mods_dir = input(f"Mod directory (default = {MODS_DIR}): ")
|
|
|
|
if mods_dir == "":
|
|
|
|
mods_dir = MODS_DIR
|
|
|
|
seven_dir = input(f"Seven Days to Die installation (default = {SEVEN_DIR}): ")
|
|
|
|
if seven_dir == "":
|
|
|
|
seven_dir = SEVEN_DIR
|
|
|
|
cfg = Config(mods_dir=mods_dir, seven_dir=seven_dir)
|
|
|
|
if not save_config(cfg):
|
2023-01-22 14:35:02 -05:00
|
|
|
raise SevenModsError("There was a problem writing the configuration file.")
|
2023-01-22 00:18:00 -05:00
|
|
|
return cfg
|
|
|
|
|
|
|
|
def save_config(cfg: Config) -> bool:
|
|
|
|
try:
|
|
|
|
with open(CONFIG_FILE, 'w') as config_file:
|
2023-02-02 22:44:08 -05:00
|
|
|
config_file.write("\n".join([f"{key}={cfg.settings[key]}" for key in cfg.settings]) + "\n")
|
2023-01-22 00:18:00 -05:00
|
|
|
return True
|
2023-01-22 14:35:02 -05:00
|
|
|
except RuntimeError:
|
2023-01-22 00:18:00 -05:00
|
|
|
return False
|
|
|
|
|
|
|
|
def load_config():
|
2023-01-22 18:46:23 -05:00
|
|
|
lines = []
|
|
|
|
with open(CONFIG_FILE, 'r') as config_file:
|
|
|
|
lines = config_file.readlines()
|
|
|
|
cfg_dict = {}
|
|
|
|
for line in lines:
|
|
|
|
if not "=" in line:
|
|
|
|
continue
|
|
|
|
var, val = line.split("=")
|
|
|
|
cfg_dict[var.strip()] = val.strip()
|
|
|
|
return Config(**cfg_dict)
|
2023-01-22 00:18:00 -05:00
|
|
|
|
|
|
|
def get_loaded_mods(cfg: Config) -> list:
|
|
|
|
internal_mods_path = get_internal_mods_path(cfg)
|
|
|
|
ret = []
|
|
|
|
with os.scandir(internal_mods_path) as it:
|
|
|
|
for entry in it:
|
2024-07-20 11:53:05 -04:00
|
|
|
if entry.is_symlink() and entry.is_dir() and not entry.name.startswith('.'):
|
2023-01-22 00:18:00 -05:00
|
|
|
# only
|
|
|
|
ret.append(entry.name)
|
2023-01-30 23:11:27 -05:00
|
|
|
return sorted(ret, key = lambda x: x.casefold())
|
2023-01-22 00:18:00 -05:00
|
|
|
|
|
|
|
def get_available_mods(cfg: Config) -> list:
|
|
|
|
ret = []
|
|
|
|
with os.scandir(cfg.mods_dir) as it:
|
|
|
|
for entry in it:
|
2024-07-20 11:53:05 -04:00
|
|
|
if entry.is_dir() and not entry.name.startswith('.') and entry.name != "__pycache__":
|
2023-01-22 00:18:00 -05:00
|
|
|
ret.append(entry.name)
|
2023-01-30 23:11:27 -05:00
|
|
|
return sorted(ret, key = lambda x: x.casefold())
|
2023-01-22 00:18:00 -05:00
|
|
|
|
|
|
|
def enable_mod(cfg: Config, mod_name: str):
|
|
|
|
link_source = os.path.join(cfg.mods_dir, mod_name)
|
|
|
|
link_dest = os.path.join(get_internal_mods_path(cfg), mod_name)
|
|
|
|
if not os.path.exists(link_source):
|
2023-01-22 14:35:02 -05:00
|
|
|
raise SevenModsError(f"Could not enable {mod_name}: mod not available.")
|
2023-01-22 00:18:00 -05:00
|
|
|
if os.path.exists(link_dest):
|
2023-01-22 14:35:02 -05:00
|
|
|
raise SevenModsError(f"Could not enable {mod_name}: already enabled.")
|
2023-01-22 00:18:00 -05:00
|
|
|
os.symlink(link_source, link_dest)
|
|
|
|
|
|
|
|
def disable_mod(cfg: Config, mod_name: str):
|
|
|
|
link_dest = os.path.join(get_internal_mods_path(cfg), mod_name)
|
|
|
|
if not os.path.exists(link_dest):
|
2023-01-22 14:35:02 -05:00
|
|
|
raise SevenModsError(f"Could not disable {mod_name}: already disabled.")
|
2023-01-22 00:18:00 -05:00
|
|
|
os.remove(link_dest)
|
|
|
|
|
|
|
|
def toggle_mod(cfg: Config, mod_name: str):
|
|
|
|
link_dest = os.path.join(get_internal_mods_path(cfg), mod_name)
|
|
|
|
if os.path.exists(link_dest):
|
|
|
|
os.remove(link_dest)
|
|
|
|
else:
|
|
|
|
link_source = os.path.join(cfg.mods_dir, mod_name)
|
|
|
|
if not os.path.exists(link_source):
|
2023-01-22 14:35:02 -05:00
|
|
|
raise SevenModsError(f"Could not enable {mod_name}: mod not available.")
|
2023-01-22 00:18:00 -05:00
|
|
|
os.symlink(link_source, link_dest)
|
|
|
|
|
2023-01-30 23:47:19 -05:00
|
|
|
def get_mod_data(mod: str, cfg: Config) -> tuple:
|
|
|
|
mod_info_path = os.path.join(cfg.mods_dir, mod, "ModInfo.xml")
|
|
|
|
try:
|
|
|
|
tree = et.parse(mod_info_path)
|
|
|
|
except:
|
|
|
|
return "", "", ""
|
|
|
|
root = tree.getroot()
|
|
|
|
name = ""
|
|
|
|
author = ""
|
|
|
|
version = ""
|
|
|
|
for node in root[0]:
|
|
|
|
if node.tag == "Name":
|
|
|
|
name = node.attrib["value"]
|
|
|
|
elif node.tag == "Author":
|
|
|
|
author = node.attrib["value"]
|
|
|
|
elif node.tag == "Version":
|
|
|
|
version = node.attrib["value"]
|
|
|
|
return name, version, author
|
|
|
|
|
2023-01-22 00:18:00 -05:00
|
|
|
def command_list(args: list, cfg: Config, profiles: ModProfiles):
|
2023-01-30 23:47:19 -05:00
|
|
|
if len(args) == 3 and args[2] == "profiles":
|
2023-01-22 00:18:00 -05:00
|
|
|
for profile in profiles.profiles:
|
|
|
|
print(profile)
|
2023-01-30 23:47:19 -05:00
|
|
|
elif len(args) == 2 or len(args) == 3:
|
|
|
|
available_mods = get_available_mods(cfg)
|
|
|
|
loaded_mods = get_loaded_mods(cfg)
|
|
|
|
if len(args) == 3 and args[2] == "-l":
|
|
|
|
mod_info = [get_mod_data(i, cfg) for i in available_mods]
|
|
|
|
ts1 = len(max(available_mods, key = lambda x: len(x), default = 3)) + 1
|
|
|
|
pretty_names = [i[0] for i in mod_info]
|
|
|
|
ts2 = len(max(pretty_names, key = lambda x: len(x), default = 4)) + 1
|
|
|
|
versions = [i[1] for i in mod_info]
|
|
|
|
ts3 = len(max(versions, key = lambda x: len(x), default = 7)) + 1
|
|
|
|
authors = [i[2] for i in mod_info]
|
|
|
|
ts4 = len(max(authors, key = lambda x: len(x), default = 6)) + 1
|
|
|
|
print("".join(["mod".ljust(ts1), "name".ljust(ts2), "version".ljust(ts3), "author".ljust(ts4)]))
|
|
|
|
for mod, name, version, author in zip(available_mods, pretty_names, versions, authors):
|
|
|
|
if mod in loaded_mods:
|
|
|
|
print(f"\x1b[32m{mod.ljust(ts1)}{name.ljust(ts2)}{version.ljust(ts3)}{author.ljust(ts4)}\x1b[0m *")
|
|
|
|
else:
|
|
|
|
print(f"\x1b[31m{mod.ljust(ts1)}{name.ljust(ts2)}{version.ljust(ts3)}{author.ljust(ts4)}\x1b[0m")
|
|
|
|
else:
|
|
|
|
for mod in available_mods:
|
|
|
|
if mod in loaded_mods:
|
|
|
|
print(f"\x1b[32m{mod}\x1b[0m *")
|
|
|
|
else:
|
|
|
|
print(f"\x1b[31m{mod}\x1b[0m")
|
2023-01-22 00:18:00 -05:00
|
|
|
else:
|
2023-01-22 14:35:02 -05:00
|
|
|
print(f"Usage: {args[0]} {args[1]} [profiles]")
|
2023-01-22 00:18:00 -05:00
|
|
|
|
|
|
|
def command_enable(args: list, cfg: Config, profiles: ModProfiles):
|
2023-01-30 23:56:49 -05:00
|
|
|
args = [a.rstrip("/") for a in args]
|
2023-01-22 00:18:00 -05:00
|
|
|
for mod in args[2:]:
|
|
|
|
if not mod in ("-a", "-A", "*"):
|
|
|
|
enable_mod(cfg, mod)
|
|
|
|
else:
|
|
|
|
available_mods = get_available_mods(cfg)
|
|
|
|
loaded_mods = get_loaded_mods(cfg)
|
|
|
|
for mod in available_mods:
|
|
|
|
if mod not in loaded_mods:
|
|
|
|
enable_mod(cfg, mod)
|
|
|
|
break
|
|
|
|
|
|
|
|
def command_disable(args: list, cfg: Config, profiles: ModProfiles):
|
2023-01-30 23:56:49 -05:00
|
|
|
args = [a.rstrip("/") for a in args]
|
2023-01-22 00:18:00 -05:00
|
|
|
for mod in args[2:]:
|
|
|
|
if not mod in ("-a", "-A", "*"):
|
|
|
|
disable_mod(cfg, mod)
|
|
|
|
else:
|
|
|
|
loaded_mods = get_loaded_mods(cfg)
|
|
|
|
for mod in loaded_mods:
|
|
|
|
disable_mod(cfg, mod)
|
|
|
|
break
|
|
|
|
|
|
|
|
def command_toggle(args: list, cfg: Config, profiles: ModProfiles):
|
2023-01-30 23:56:49 -05:00
|
|
|
args = [a.rstrip("/") for a in args]
|
2023-01-22 00:18:00 -05:00
|
|
|
for mod in args[2:]:
|
|
|
|
toggle_mod(cfg, mod)
|
|
|
|
|
|
|
|
def command_save(args: list, cfg: Config, profiles: ModProfiles):
|
|
|
|
if len(args) > 2:
|
|
|
|
for prof in args[2:]:
|
2023-02-02 22:44:08 -05:00
|
|
|
if len(prof) == 0 or prof.isspace():
|
|
|
|
raise seven_mods.SevenModsError("Profile must have a name.")
|
|
|
|
if not cfg.warn_save or prof not in profiles.profiles or input("Overwrite? y/N").casefold() == "y":
|
|
|
|
profiles.profiles[prof] = get_loaded_mods(cfg)
|
|
|
|
profiles.save_mod_profiles()
|
2023-01-22 00:18:00 -05:00
|
|
|
else:
|
2023-01-22 14:35:02 -05:00
|
|
|
print(f"Usage: {args[0]} {args[1]} <profile-name>")
|
2023-01-22 00:18:00 -05:00
|
|
|
|
|
|
|
def command_load(args: list, cfg: Config, profiles: ModProfiles):
|
|
|
|
if len(args) == 3 and args[2] in profiles.profiles:
|
2023-02-02 22:44:08 -05:00
|
|
|
if not cfg.warn_load or input("Load? y/N").casefold() == "y":
|
|
|
|
command_disable(args[:2] + ["*"], cfg, profiles)
|
|
|
|
command_enable(args[:2] + profiles.profiles[args[2]], cfg, profiles)
|
2023-01-22 00:18:00 -05:00
|
|
|
else:
|
2023-01-22 14:35:02 -05:00
|
|
|
print(f"Usage: {args[0]} {args[1]} <profile-name>")
|
|
|
|
|
2023-01-30 23:11:27 -05:00
|
|
|
def command_delete(args: list, cfg: Config, profiles: ModProfiles):
|
|
|
|
if len(args) > 2:
|
|
|
|
for prof in args[2:]:
|
2023-02-02 22:44:08 -05:00
|
|
|
if prof in profiles.profiles and (not cfg.warn_delete or input("Delete? y/N").casefold() == "y"):
|
2023-01-30 23:11:27 -05:00
|
|
|
del profiles.profiles[prof]
|
|
|
|
profiles.save_mod_profiles()
|
|
|
|
else:
|
|
|
|
print(f"Cannot delete non-existant profile: {prof}")
|
|
|
|
else:
|
|
|
|
print(f"Usage: {args[0]} {args[1]} <profile-name>")
|
|
|
|
|
2023-01-22 14:35:02 -05:00
|
|
|
def command_configure(args: list, cfg: Config, profiles: ModProfiles):
|
2023-02-02 22:44:08 -05:00
|
|
|
if len(args) == 2:
|
|
|
|
prompt_configuration_cli()
|
|
|
|
elif len(args) == 4:
|
|
|
|
if args[2] in Config.defaults:
|
|
|
|
cfg.set_val(args[2], args[3])
|
|
|
|
save_config(cfg)
|
|
|
|
else:
|
|
|
|
print(f"Usage: {args[0]} {args[1]} [name value]")
|
2023-01-22 14:35:02 -05:00
|
|
|
|
|
|
|
def command_help(args: list, cfg: Config, profiles: ModProfiles):
|
|
|
|
print(HELP_TEXT)
|
2023-01-22 00:18:00 -05:00
|
|
|
|
|
|
|
def parse_input(args: list, cfg: Config, profiles: ModProfiles):
|
|
|
|
if len(args) > 1:
|
|
|
|
commands = {"ls" : command_list,
|
|
|
|
"list" : command_list,
|
|
|
|
"enable" : command_enable,
|
|
|
|
"disable" : command_disable,
|
|
|
|
"toggle" : command_toggle,
|
|
|
|
"save" : command_save,
|
2023-01-22 14:35:02 -05:00
|
|
|
"load" : command_load,
|
2023-01-30 23:11:27 -05:00
|
|
|
"delete" : command_delete,
|
2023-01-22 14:35:02 -05:00
|
|
|
"config" : command_configure,
|
|
|
|
"help" : command_help,
|
|
|
|
"--help" : command_help}
|
2023-01-22 00:18:00 -05:00
|
|
|
if args[1] in commands:
|
|
|
|
commands[args[1]](args, cfg, profiles)
|
|
|
|
else:
|
|
|
|
print(f"Command {args[1]} not recogised. Valid commands are:")
|
|
|
|
for command in commands:
|
|
|
|
print(command)
|
|
|
|
|
|
|
|
def main(args):
|
2023-01-22 18:46:23 -05:00
|
|
|
cfg = None
|
|
|
|
try:
|
|
|
|
cfg = load_config()
|
|
|
|
except:
|
|
|
|
# no config or invalid config
|
|
|
|
cfg = prompt_configuration_cli()
|
2023-01-22 00:18:00 -05:00
|
|
|
profiles = ModProfiles()
|
|
|
|
profiles.load_mod_profiles()
|
2023-01-22 14:35:02 -05:00
|
|
|
try:
|
|
|
|
parse_input(args, cfg, profiles)
|
|
|
|
except SevenModsError as err:
|
|
|
|
print(err, file = sys.stderr)
|
|
|
|
return 1
|
2023-01-22 00:18:00 -05:00
|
|
|
return 0
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
sys.exit(main(sys.argv))
|