diff options
Diffstat (limited to 'libs/installer_wizard/src/utils.py')
| -rw-r--r-- | libs/installer_wizard/src/utils.py | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/libs/installer_wizard/src/utils.py b/libs/installer_wizard/src/utils.py new file mode 100644 index 0000000..da85c48 --- /dev/null +++ b/libs/installer_wizard/src/utils.py @@ -0,0 +1,170 @@ +from __future__ import annotations + +import re +from collections.abc import Sequence +from pathlib import Path +from typing import cast + +from wizard.tweaks import WizardINISetting, WizardINISettingEdit + + +def make_obscript_ini_tweaks(tweaks: Sequence[WizardINISetting]) -> str: + lines: list[str] = [] + + for tweak in tweaks: + if not isinstance(tweak, WizardINISettingEdit): + continue + line = f"{tweak.section} {tweak.setting} to {tweak.value}" + if tweak.comment: + line += f" ; {tweak.comment}" + + lines.append(line) + + return "\n".join(["; Generated by Mod Organizer 2 via Wizard"] + sorted(lines)) + + +def make_standard_ini_tweaks(tweaks: Sequence[WizardINISetting]) -> str: + # group Tweaks per section + sections: dict[str, list[WizardINISetting]] = {m.section: [] for m in tweaks} + for m in tweaks: + sections[m.section].append(m) + + lines: list[str] = [] + + for k in sorted(sections): + s_tweaks = sorted(sections[k], key=lambda m: m.setting) + lines.append(f"[{k}]") + for tw in s_tweaks: + if isinstance(tw, WizardINISettingEdit): + line = f"{tw.setting} = {tw.value}" + if tw.comment: + line += f" # {tw.comment}" + else: + line = f"# {tw.setting} - disabled" + lines.append(line) + lines.append("\n") + + return "\n".join(lines[:-1]) + + +def merge_standard_ini_tweaks(tweaks: Sequence[WizardINISetting], file: Path) -> str: + import sys + + print(f"Cannot merge INI Tweaks for {file.name}.", file=sys.stderr) + return make_standard_ini_tweaks(tweaks) + + +def merge_obscript_ini_tweaks(tweaks: Sequence[WizardINISetting], file: Path) -> str: + # this is from Wrye Bash (and the function is inspired from Wrye Bash) + reComment = re.compile(";.*", re.U) + reDeleted = re.compile("" r";-(\w.*?)$", re.U) + reSet = re.compile("" r"\s*set\s+(.+?)\s+to\s+(.*)", re.I | re.U) + reSetGS = re.compile("" r"\s*setGS\s+(.+?)\s+(.*)", re.I | re.U) + reSetNGS = re.compile("" r"\s*SetNumericGameSetting\s+(.+?)\s+(.*)", re.I | re.U) + + _regex_tuples = ( + (reSet, "set", "set {} to {}"), + (reSetGS, "setgs", "setGS {} {}"), + (reSetNGS, "setnumericgamesetting", "SetNumericGameSetting {} {}"), + ) + + def _parse_obse_line(line: str): + for regex, sectionKey, format_string in _regex_tuples: + match = regex.match(line) + if match: + return match, sectionKey, format_string + return None, None, None + + # read the original file + with open(file, "r") as fp: + olines = fp.readlines() + + # map setting name to value + settings: dict[str, dict[str, WizardINISettingEdit]] = {} + deleted: dict[str, dict[str, WizardINISetting]] = {} + for tweak in tweaks: + if isinstance(tweak, WizardINISettingEdit): + if tweak.section not in settings: + settings[tweak.section.lower()] = {} + settings[tweak.section.lower()][tweak.setting] = tweak + else: + if tweak.section not in deleted: + deleted[tweak.section.lower()] = {} + deleted[tweak.section.lower()][tweak.setting] = tweak + + # create the lines + lines: list[str] = [] + for line in olines: + line = line.rstrip() + maDeleted = reDeleted.match(line) + if maDeleted: + stripped = maDeleted.group(1) + else: + stripped = line + stripped = reComment.sub("", stripped).strip() + + match, section_key, format_string = _parse_obse_line(stripped) + + if match: + assert format_string is not None + setting = match.group(1) + if section_key in settings and setting in settings[section_key]: + value = settings[section_key][setting].value + line = format_string.format(setting, value) + comment = "" + if settings[section_key][setting].comment: + comment = cast(str, settings[section_key][setting].comment) + comment += " " + comment += f"(set by MO2 via Wizard, was {match.group(2)})" + line = f"{line} ; {comment}" + del settings[section_key][setting] + elif ( + not maDeleted + and section_key in deleted + and setting in deleted[section_key] + ): + line = f";-{line}" + + lines.append(line) + + for section in settings.values(): + line = "" + for setting in section.values(): + if setting.section.lower() == "set": + line = f"{setting.section} {setting.setting} to {setting.value}" + else: + line = f"{setting.section} {setting.setting} {setting.value}" + + if setting.comment: + comment = setting.comment + " (set by MO2 via Wizard)" + else: + comment = "(set by MO2 via Wizard)" + line = f"{line} ; {comment}" + + lines.append(line) + + return "\n".join(lines) + + +def make_ini_tweaks(tweaks: Sequence[WizardINISetting]) -> str: + is_set = [ + tw.section.lower() in ("set", "setgs", "setnumericgamesetting") for tw in tweaks + ] + + # assume there is no mix + if all(is_set): + return make_obscript_ini_tweaks(tweaks) + else: + return make_standard_ini_tweaks(tweaks) + + +def merge_ini_tweaks(tweaks: Sequence[WizardINISetting], file: Path) -> str: + is_set = [ + tw.section.lower() in ("set", "setgs", "setnumericgamesetting") for tw in tweaks + ] + + # assume there is no mix + if all(is_set): + return merge_obscript_ini_tweaks(tweaks, file) + else: + return merge_standard_ini_tweaks(tweaks, file) |
