1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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)
|