umu-commander 1.6.2__py3-none-any.whl → 1.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of umu-commander might be problematic. Click here for more details.
- umu_commander/__init__.py +1 -1
- umu_commander/__main__.py +42 -38
- umu_commander/classes.py +8 -71
- umu_commander/configuration.py +49 -29
- umu_commander/database.py +30 -16
- umu_commander/proton.py +25 -42
- umu_commander/tracking.py +43 -33
- umu_commander/umu_config.py +88 -93
- umu_commander/util.py +46 -105
- {umu_commander-1.6.2.dist-info → umu_commander-1.7.0.dist-info}/METADATA +17 -13
- umu_commander-1.7.0.dist-info/RECORD +14 -0
- umu_commander-1.6.2.dist-info/RECORD +0 -14
- {umu_commander-1.6.2.dist-info → umu_commander-1.7.0.dist-info}/WHEEL +0 -0
- {umu_commander-1.6.2.dist-info → umu_commander-1.7.0.dist-info}/entry_points.txt +0 -0
- {umu_commander-1.6.2.dist-info → umu_commander-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
umu_commander/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = "v1.
|
|
1
|
+
VERSION = "v1.7.0"
|
umu_commander/__main__.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import sys
|
|
3
|
-
from collections.abc import Callable
|
|
4
2
|
from json import JSONDecodeError
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from InquirerPy.exceptions import InvalidArgument
|
|
5
6
|
|
|
6
7
|
from umu_commander import configuration as config
|
|
7
8
|
from umu_commander import database as db
|
|
@@ -10,27 +11,22 @@ from umu_commander.classes import ExitCode
|
|
|
10
11
|
from umu_commander.configuration import CONFIG_DIR, CONFIG_NAME
|
|
11
12
|
from umu_commander.util import print_help
|
|
12
13
|
|
|
13
|
-
# TODO: Add related projects shoutout
|
|
14
|
-
# https://github.com/Faugus/faugus-launcher
|
|
15
|
-
# https://github.com/SeongGino/Nero-umu
|
|
16
|
-
# https://github.com/korewaChino/umu-wrapper
|
|
17
|
-
|
|
18
|
-
# TODO: https://inquirerpy.readthedocs.io/en/latest/
|
|
19
|
-
|
|
20
14
|
|
|
21
|
-
def
|
|
15
|
+
def init() -> ExitCode:
|
|
22
16
|
try:
|
|
23
17
|
config.load()
|
|
24
18
|
|
|
25
19
|
except (JSONDecodeError, KeyError):
|
|
26
|
-
config_path:
|
|
27
|
-
config_path_old:
|
|
20
|
+
config_path: Path = CONFIG_DIR / CONFIG_NAME
|
|
21
|
+
config_path_old: Path = CONFIG_DIR / (str(CONFIG_NAME) + ".old")
|
|
28
22
|
|
|
29
23
|
print(f"Config file at {config_path} could not be read.")
|
|
30
24
|
|
|
31
|
-
if not
|
|
25
|
+
if not config_path_old.exists():
|
|
32
26
|
print(f"Config file renamed to {config_path_old}.")
|
|
33
|
-
|
|
27
|
+
config_path.rename(config_path_old)
|
|
28
|
+
|
|
29
|
+
return ExitCode.DECODING_ERROR
|
|
34
30
|
|
|
35
31
|
except FileNotFoundError:
|
|
36
32
|
config.dump()
|
|
@@ -39,51 +35,59 @@ def main() -> ExitCode:
|
|
|
39
35
|
db.load()
|
|
40
36
|
|
|
41
37
|
except JSONDecodeError:
|
|
42
|
-
db_path:
|
|
43
|
-
db_path_old:
|
|
38
|
+
db_path: Path = config.DB_DIR / config.DB_NAME
|
|
39
|
+
db_path_old: Path = config.DB_DIR / (str(config.DB_NAME) + ".old")
|
|
44
40
|
|
|
45
41
|
print(f"Tracking file at {db_path} could not be read.")
|
|
46
42
|
|
|
47
|
-
if not
|
|
43
|
+
if not db_path_old.exists():
|
|
44
|
+
db_path.rename(db_path_old)
|
|
48
45
|
print(f"DB file renamed to {db_path_old}.")
|
|
49
|
-
os.rename(db_path, db_path_old)
|
|
50
46
|
|
|
51
47
|
except FileNotFoundError:
|
|
52
48
|
pass
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"run": umu_config.run,
|
|
61
|
-
}
|
|
50
|
+
return ExitCode.SUCCESS
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def main() -> int:
|
|
54
|
+
if (return_code := init()) != ExitCode.SUCCESS:
|
|
55
|
+
return return_code.value
|
|
62
56
|
|
|
63
57
|
try:
|
|
64
|
-
|
|
58
|
+
match sys.argv[1]:
|
|
59
|
+
case "track":
|
|
60
|
+
tracking.track(),
|
|
61
|
+
case "untrack":
|
|
62
|
+
tracking.untrack(),
|
|
63
|
+
case "users":
|
|
64
|
+
tracking.users(),
|
|
65
|
+
case "delete":
|
|
66
|
+
tracking.delete(),
|
|
67
|
+
case "create":
|
|
68
|
+
umu_config.create({"umu": {}, "env": {}}, True),
|
|
69
|
+
case "run":
|
|
70
|
+
umu_config.run(),
|
|
71
|
+
case _:
|
|
72
|
+
print("Unrecognised verb.")
|
|
73
|
+
print_help()
|
|
74
|
+
return ExitCode.INVALID_SELECTION.value
|
|
65
75
|
|
|
66
76
|
except IndexError:
|
|
67
77
|
print_help()
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
except KeyError:
|
|
71
|
-
print("Unrecognised verb.")
|
|
72
|
-
print_help()
|
|
73
|
-
return_val = ExitCode.INVALID_SELECTION
|
|
78
|
+
return ExitCode.SUCCESS.value
|
|
74
79
|
|
|
75
|
-
except
|
|
76
|
-
|
|
80
|
+
except InvalidArgument:
|
|
81
|
+
print("No choices to select from.")
|
|
82
|
+
return ExitCode.INVALID_SELECTION.value
|
|
77
83
|
|
|
78
84
|
else:
|
|
79
|
-
|
|
85
|
+
return ExitCode.SUCCESS.value
|
|
80
86
|
|
|
81
87
|
finally:
|
|
82
88
|
tracking.untrack_unlinked()
|
|
83
89
|
db.dump()
|
|
84
90
|
|
|
85
|
-
return return_val.value
|
|
86
|
-
|
|
87
91
|
|
|
88
92
|
if __name__ == "__main__":
|
|
89
93
|
exit(main())
|
umu_commander/classes.py
CHANGED
|
@@ -1,78 +1,15 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
1
|
from enum import Enum
|
|
3
2
|
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
class Element(str):
|
|
5
|
+
def __new__(cls, value: str, name: str = None):
|
|
6
|
+
instance = super().__new__(cls, value)
|
|
7
|
+
if name is None:
|
|
8
|
+
instance.name = value
|
|
9
|
+
else:
|
|
10
|
+
instance.name = name
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
return ProtonVer(self.group_id, self.value, self.info)
|
|
13
|
-
|
|
14
|
-
def as_dll_override(self) -> "DLLOverride":
|
|
15
|
-
return DLLOverride(self.info, self.value)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@dataclass
|
|
19
|
-
class ProtonVer(Element):
|
|
20
|
-
def __init__(self, dir: str = "", version_num: str = "", user_count: str = ""):
|
|
21
|
-
super().__init__(group_id=dir, value=version_num, info=user_count)
|
|
22
|
-
|
|
23
|
-
@property
|
|
24
|
-
def dir(self):
|
|
25
|
-
return self.group_id
|
|
26
|
-
|
|
27
|
-
@property
|
|
28
|
-
def version_num(self):
|
|
29
|
-
return self.value
|
|
30
|
-
|
|
31
|
-
@property
|
|
32
|
-
def user_count(self):
|
|
33
|
-
return self.info
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@dataclass
|
|
37
|
-
class DLLOverride(Element):
|
|
38
|
-
def __init__(self, label: str = "", override_str: str = ""):
|
|
39
|
-
super().__init__(group_id="", value=override_str, info=label)
|
|
40
|
-
|
|
41
|
-
@property
|
|
42
|
-
def override_str(self):
|
|
43
|
-
return self.value
|
|
44
|
-
|
|
45
|
-
@property
|
|
46
|
-
def label(self):
|
|
47
|
-
return self.info
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@dataclass
|
|
51
|
-
class Value(Element):
|
|
52
|
-
def __init__(self, value: str):
|
|
53
|
-
super().__init__(value=value)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
@dataclass
|
|
57
|
-
class Group:
|
|
58
|
-
identity: str = ""
|
|
59
|
-
label: str = ""
|
|
60
|
-
elements: list[Element] = list
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@dataclass
|
|
64
|
-
class ProtonDir(Group):
|
|
65
|
-
@property
|
|
66
|
-
def path(self):
|
|
67
|
-
return self.identity
|
|
68
|
-
|
|
69
|
-
@property
|
|
70
|
-
def versions(self) -> list[ProtonVer]:
|
|
71
|
-
return [e.as_proton_ver() for e in self.elements]
|
|
72
|
-
|
|
73
|
-
@versions.setter
|
|
74
|
-
def versions(self, value):
|
|
75
|
-
self.elements = value
|
|
12
|
+
return instance
|
|
76
13
|
|
|
77
14
|
|
|
78
15
|
class ExitCode(Enum):
|
umu_commander/configuration.py
CHANGED
|
@@ -6,48 +6,59 @@ from typing import Any
|
|
|
6
6
|
|
|
7
7
|
import tomli_w
|
|
8
8
|
|
|
9
|
-
from umu_commander.classes import
|
|
9
|
+
from umu_commander.classes import Element
|
|
10
10
|
|
|
11
|
-
CONFIG_DIR:
|
|
12
|
-
CONFIG_NAME:
|
|
11
|
+
CONFIG_DIR: Path = Path.home() / ".config"
|
|
12
|
+
CONFIG_NAME: Path = Path("umu-commander.toml")
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
PROTON_PATHS: tuple[
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
PROTON_PATHS: tuple[Path, ...] = (
|
|
16
|
+
Path.home() / ".local/share/Steam/compatibilitytools.d/",
|
|
17
|
+
Path.home() / ".local/share/umu/compatibilitytools",
|
|
18
18
|
)
|
|
19
|
-
UMU_PROTON_PATH:
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
DLLOverride("winhttp for BepInEx", "winhttp.dll=n;"),
|
|
19
|
+
UMU_PROTON_PATH: Path = Path(Path.home() / ".local/share/Steam/compatibilitytools.d")
|
|
20
|
+
|
|
21
|
+
DB_NAME: Path = Path("tracking.json")
|
|
22
|
+
DB_DIR: Path = Path.home() / ".local/share/umu/compatibilitytools"
|
|
23
|
+
UMU_CONFIG_NAME: Path = Path("umu-config.toml")
|
|
24
|
+
DEFAULT_PREFIX_DIR: Path = Path.home() / ".local/share/wineprefixes/"
|
|
25
|
+
DLL_OVERRIDES_OPTIONS: tuple[Element, ...] = (
|
|
26
|
+
Element("winhttp.dll=n,b;", "winhttp for BepInEx"),
|
|
28
27
|
)
|
|
28
|
+
LANG_OVERRIDES_OPTIONS: tuple[Element, ...] = (Element("ja_JP.UTF8", "Japanese"),)
|
|
29
|
+
|
|
30
|
+
module = importlib.import_module(__name__)
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
def load():
|
|
32
34
|
with open(os.path.join(CONFIG_DIR, CONFIG_NAME), "rb") as conf_file:
|
|
33
35
|
toml_conf = tomllib.load(conf_file)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
|
|
37
|
+
# Proton dirs translation
|
|
38
|
+
setattr(
|
|
39
|
+
module,
|
|
40
|
+
"PROTON_PATHS",
|
|
41
|
+
(Path(proton_dir) for proton_dir in toml_conf["PROTON_PATHS"]),
|
|
42
|
+
)
|
|
43
|
+
del toml_conf["PROTON_PATHS"]
|
|
44
|
+
|
|
45
|
+
# DLL/LANG Override translation
|
|
46
|
+
for key in ["DLL", "LANG"]:
|
|
47
|
+
setattr(
|
|
48
|
+
module,
|
|
49
|
+
f"{key}_OVERRIDES_OPTIONS",
|
|
50
|
+
(
|
|
51
|
+
Element(value, name)
|
|
52
|
+
for name, value in toml_conf[f"{key}_OVERRIDES_OPTIONS"].items()
|
|
53
|
+
),
|
|
42
54
|
)
|
|
55
|
+
del toml_conf[f"{key}_OVERRIDES_OPTIONS"]
|
|
43
56
|
|
|
44
|
-
module = importlib.import_module(__name__)
|
|
45
57
|
for key, value in toml_conf.items():
|
|
46
|
-
setattr(module, key, value)
|
|
58
|
+
setattr(module, key, Path(value))
|
|
47
59
|
|
|
48
60
|
|
|
49
61
|
def _get_attributes() -> dict[str, Any]:
|
|
50
|
-
module = importlib.import_module(__name__)
|
|
51
62
|
attributes: dict[str, Any] = {}
|
|
52
63
|
for key in dir(module):
|
|
53
64
|
value = getattr(module, key)
|
|
@@ -66,8 +77,17 @@ def dump():
|
|
|
66
77
|
del toml_conf["CONFIG_DIR"]
|
|
67
78
|
del toml_conf["CONFIG_NAME"]
|
|
68
79
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
80
|
+
for key, value in toml_conf.items():
|
|
81
|
+
match key:
|
|
82
|
+
case "PROTON_PATHS":
|
|
83
|
+
toml_conf[key] = [str(proton_dir) for proton_dir in PROTON_PATHS]
|
|
84
|
+
|
|
85
|
+
case "DLL_OVERRIDES_OPTIONS" | "LANG_OVERRIDES_OPTIONS":
|
|
86
|
+
toml_conf[key] = {
|
|
87
|
+
override.name: override for override in getattr(module, key)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
case _:
|
|
91
|
+
toml_conf[key] = str(value)
|
|
72
92
|
|
|
73
93
|
tomli_w.dump(toml_conf, conf_file)
|
umu_commander/database.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
|
-
import os
|
|
3
2
|
from collections import defaultdict
|
|
3
|
+
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
import umu_commander.configuration as config
|
|
6
6
|
|
|
7
|
-
_db: defaultdict[
|
|
7
|
+
_db: defaultdict[Path, defaultdict[Path, list[Path]]] = defaultdict(
|
|
8
8
|
lambda: defaultdict(list)
|
|
9
9
|
)
|
|
10
10
|
|
|
@@ -12,27 +12,41 @@ _db: defaultdict[str, defaultdict[str, list[str]]] = defaultdict(
|
|
|
12
12
|
def load():
|
|
13
13
|
global _db
|
|
14
14
|
|
|
15
|
-
if not
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
with open(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
if not config.DB_DIR.exists():
|
|
16
|
+
config.DB_DIR.mkdir()
|
|
17
|
+
|
|
18
|
+
with open(config.DB_DIR / config.DB_NAME, "rt") as db_file:
|
|
19
|
+
db: dict[Path, dict[Path, list[Path]]] = {}
|
|
20
|
+
for proton_dir, proton_vers in json.load(db_file).items():
|
|
21
|
+
proton_dir = Path(proton_dir)
|
|
22
|
+
db[proton_dir] = {}
|
|
23
|
+
for proton_ver, proton_users in proton_vers.items():
|
|
24
|
+
proton_ver = proton_dir / proton_ver
|
|
25
|
+
db[proton_dir][proton_ver] = [Path(user) for user in proton_users]
|
|
26
|
+
# noinspection PyTypeChecker
|
|
27
|
+
_db.update(db)
|
|
22
28
|
|
|
23
29
|
|
|
24
30
|
def dump():
|
|
25
|
-
if not
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
if not config.DB_DIR.exists():
|
|
32
|
+
config.DB_DIR.mkdir()
|
|
33
|
+
|
|
34
|
+
db: dict[str, dict[str, list[str]]] = {}
|
|
35
|
+
for proton_dir, proton_vers in _db.items():
|
|
36
|
+
proton_dir = str(proton_dir)
|
|
37
|
+
db[proton_dir] = {}
|
|
38
|
+
for proton_ver, proton_users in proton_vers.items():
|
|
39
|
+
proton_ver = proton_ver.name
|
|
40
|
+
db[proton_dir][proton_ver] = [str(user) for user in proton_users]
|
|
41
|
+
|
|
42
|
+
with open(config.DB_DIR / config.DB_NAME, "wt") as db_file:
|
|
29
43
|
# noinspection PyTypeChecker
|
|
30
|
-
json.dump(
|
|
44
|
+
json.dump(db, db_file, indent="\t")
|
|
31
45
|
|
|
32
46
|
|
|
33
47
|
def get(
|
|
34
|
-
proton_dir:
|
|
35
|
-
) -> dict[
|
|
48
|
+
proton_dir: Path = None, proton_ver: Path = None
|
|
49
|
+
) -> dict[Path, dict[Path, list[Path]]] | dict[Path, list[Path]] | list[Path]:
|
|
36
50
|
global _db
|
|
37
51
|
|
|
38
52
|
if proton_dir is None and proton_ver is None:
|
umu_commander/proton.py
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import re
|
|
3
2
|
import subprocess
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
import umu_commander.configuration as config
|
|
6
|
-
import umu_commander.database as db
|
|
7
|
-
from umu_commander.classes import ProtonDir, ProtonVer
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
def _natural_sort_proton_ver_key(p:
|
|
11
|
-
s: str = p.
|
|
9
|
+
def _natural_sort_proton_ver_key(p: Path, _nsre=re.compile(r"(\d+)")):
|
|
10
|
+
s: str = p.name
|
|
12
11
|
return [int(text) if text.isdigit() else text for text in _nsre.split(s)]
|
|
13
12
|
|
|
14
13
|
|
|
@@ -32,51 +31,35 @@ def refresh_proton_versions():
|
|
|
32
31
|
break
|
|
33
32
|
|
|
34
33
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def collect_proton_versions(
|
|
40
|
-
sort: bool = False, user_count: bool = False
|
|
41
|
-
) -> list[ProtonDir]:
|
|
42
|
-
def get_user_count(proton_dir: str, proton_ver) -> str:
|
|
43
|
-
return (
|
|
44
|
-
"(" + str(len(db.get(proton_dir, proton_ver))) + ")"
|
|
45
|
-
if proton_ver in db.get(proton_dir)
|
|
46
|
-
else "(-)"
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
proton_dirs: list[ProtonDir] = []
|
|
34
|
+
def collect_proton_versions(sort: bool = False) -> dict[Path, Iterable[Path]]:
|
|
35
|
+
versions: dict[Path, Iterable[Path]] = {}
|
|
50
36
|
for proton_dir in config.PROTON_PATHS:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
57
|
-
for version in os.listdir(proton_dir)
|
|
58
|
-
if os.path.isdir(os.path.join(proton_dir, version))
|
|
59
|
-
]
|
|
37
|
+
dir_versions = [version for version in proton_dir.iterdir() if version.is_dir()]
|
|
38
|
+
if len(dir_versions) == 0:
|
|
39
|
+
continue
|
|
40
|
+
else:
|
|
41
|
+
versions[proton_dir] = dir_versions
|
|
60
42
|
|
|
61
43
|
if sort:
|
|
62
|
-
versions = sorted(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
ProtonDir(proton_dir, f"Proton versions in {proton_dir}:", versions)
|
|
66
|
-
)
|
|
44
|
+
versions[proton_dir] = sorted(
|
|
45
|
+
versions[proton_dir], key=_natural_sort_proton_ver_key, reverse=True
|
|
46
|
+
)
|
|
67
47
|
|
|
68
|
-
return
|
|
48
|
+
return versions
|
|
69
49
|
|
|
70
50
|
|
|
71
|
-
def get_latest_umu_proton():
|
|
72
|
-
umu_proton_versions: list[
|
|
73
|
-
|
|
74
|
-
for
|
|
75
|
-
if "UMU" in
|
|
76
|
-
and os.path.isdir(os.path.join(config.UMU_PROTON_PATH, version))
|
|
51
|
+
def get_latest_umu_proton() -> Path | None:
|
|
52
|
+
umu_proton_versions: list[Path] = [
|
|
53
|
+
config.UMU_PROTON_PATH / proton_ver
|
|
54
|
+
for proton_ver in config.UMU_PROTON_PATH.iterdir()
|
|
55
|
+
if "UMU" in proton_ver.name and (config.UMU_PROTON_PATH / proton_ver).is_dir()
|
|
77
56
|
]
|
|
57
|
+
|
|
58
|
+
if len(umu_proton_versions) == 0:
|
|
59
|
+
return None
|
|
60
|
+
|
|
78
61
|
umu_proton_versions = sorted(
|
|
79
62
|
umu_proton_versions, key=_natural_sort_proton_ver_key, reverse=True
|
|
80
63
|
)
|
|
81
64
|
|
|
82
|
-
return umu_proton_versions[0]
|
|
65
|
+
return umu_proton_versions[0]
|
umu_commander/tracking.py
CHANGED
|
@@ -1,64 +1,74 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import shutil
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from InquirerPy import inquirer
|
|
3
5
|
|
|
4
6
|
import umu_commander.database as db
|
|
5
|
-
from umu_commander.classes import ProtonDir, ProtonVer
|
|
6
7
|
from umu_commander.proton import (
|
|
7
8
|
collect_proton_versions,
|
|
8
9
|
get_latest_umu_proton,
|
|
9
10
|
refresh_proton_versions,
|
|
10
11
|
)
|
|
11
12
|
from umu_commander.util import (
|
|
12
|
-
|
|
13
|
+
build_choices,
|
|
13
14
|
)
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
def untrack(quiet: bool = False):
|
|
17
|
-
|
|
17
|
+
def untrack(target_dir: Path = None, *, quiet: bool = False):
|
|
18
|
+
if target_dir is None:
|
|
19
|
+
target_dir = Path.cwd()
|
|
20
|
+
|
|
18
21
|
for proton_dir in db.get().keys():
|
|
19
22
|
for proton_ver in db.get(proton_dir):
|
|
20
|
-
if
|
|
21
|
-
db.get(proton_dir, proton_ver).remove(
|
|
23
|
+
if target_dir in db.get(proton_dir, proton_ver):
|
|
24
|
+
db.get(proton_dir, proton_ver).remove(target_dir)
|
|
22
25
|
|
|
23
26
|
if not quiet:
|
|
24
27
|
print("Directory removed from all tracking lists.")
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
def track(
|
|
28
|
-
proton_ver:
|
|
31
|
+
proton_ver: Path = None,
|
|
32
|
+
target_dir: Path = None,
|
|
33
|
+
*,
|
|
34
|
+
refresh_versions: bool = True,
|
|
35
|
+
quiet: bool = False,
|
|
29
36
|
):
|
|
30
|
-
if
|
|
37
|
+
if target_dir is None:
|
|
38
|
+
target_dir = Path.cwd()
|
|
39
|
+
|
|
40
|
+
if refresh_versions and proton_ver is None:
|
|
31
41
|
refresh_proton_versions()
|
|
32
42
|
|
|
33
43
|
if proton_ver is None:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
).
|
|
44
|
+
proton_dirs = collect_proton_versions(sort=True)
|
|
45
|
+
choices = build_choices(None, proton_dirs)
|
|
46
|
+
proton_ver: Path = inquirer.select(
|
|
47
|
+
"Select Proton version to track directory with:", choices
|
|
48
|
+
).execute()
|
|
39
49
|
|
|
40
50
|
untrack(quiet=True)
|
|
41
|
-
|
|
42
|
-
db.get(proton_ver.dir, proton_ver.version_num).append(current_directory)
|
|
51
|
+
db.get(proton_ver.parent, proton_ver).append(target_dir)
|
|
43
52
|
|
|
44
53
|
if not quiet:
|
|
45
54
|
print(
|
|
46
|
-
f"Directory {
|
|
55
|
+
f"Directory {target_dir} added to Proton version's {proton_ver.name} in {proton_ver.parent} tracking list."
|
|
47
56
|
)
|
|
48
57
|
|
|
49
58
|
|
|
50
|
-
def users():
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
def users(proton_ver: Path = None):
|
|
60
|
+
if proton_ver is None:
|
|
61
|
+
proton_dirs = collect_proton_versions(sort=True)
|
|
62
|
+
choices = build_choices(None, proton_dirs, count_elements=True)
|
|
63
|
+
proton_ver: Path = inquirer.select(
|
|
64
|
+
"Select Proton version to view user list:", choices
|
|
65
|
+
).execute()
|
|
66
|
+
|
|
67
|
+
if proton_ver.parent in db.get() and proton_ver in db.get(proton_ver.parent):
|
|
68
|
+
version_users: list[Path] = db.get(proton_ver.parent, proton_ver)
|
|
59
69
|
if len(version_users) > 0:
|
|
60
70
|
print(
|
|
61
|
-
f"Directories tracked by {proton_ver.
|
|
71
|
+
f"Directories tracked by {proton_ver.name} of {proton_ver.parent}:",
|
|
62
72
|
*version_users,
|
|
63
73
|
sep="\n\t",
|
|
64
74
|
)
|
|
@@ -77,12 +87,12 @@ def delete():
|
|
|
77
87
|
continue
|
|
78
88
|
|
|
79
89
|
if len(version_users) == 0:
|
|
80
|
-
|
|
81
|
-
f"Version {proton_ver} in {proton_dir} is tracking no directories, delete?
|
|
82
|
-
)
|
|
83
|
-
if
|
|
90
|
+
confirmed: bool = inquirer.confirm(
|
|
91
|
+
f"Version {proton_ver.name} in {proton_dir} is tracking no directories, delete?"
|
|
92
|
+
).execute()
|
|
93
|
+
if confirmed:
|
|
84
94
|
try:
|
|
85
|
-
shutil.rmtree(
|
|
95
|
+
shutil.rmtree(proton_dir / proton_ver)
|
|
86
96
|
except FileNotFoundError:
|
|
87
97
|
pass
|
|
88
98
|
del db.get(proton_dir)[proton_ver]
|
|
@@ -92,5 +102,5 @@ def untrack_unlinked():
|
|
|
92
102
|
for proton_dir in db.get().keys():
|
|
93
103
|
for proton_ver, version_users in db.get()[proton_dir].items():
|
|
94
104
|
for user in version_users:
|
|
95
|
-
if not
|
|
105
|
+
if not user.exists():
|
|
96
106
|
db.get(proton_dir, proton_ver).remove(user)
|
umu_commander/umu_config.py
CHANGED
|
@@ -1,117 +1,107 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import subprocess
|
|
3
3
|
import tomllib
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
from typing import Any
|
|
5
6
|
|
|
6
7
|
import tomli_w
|
|
8
|
+
from InquirerPy import inquirer
|
|
7
9
|
|
|
8
10
|
import umu_commander.configuration as config
|
|
9
11
|
from umu_commander import tracking
|
|
10
|
-
from umu_commander.classes import
|
|
11
|
-
from umu_commander.
|
|
12
|
-
from umu_commander.
|
|
12
|
+
from umu_commander.classes import Element
|
|
13
|
+
from umu_commander.configuration import DLL_OVERRIDES_OPTIONS, LANG_OVERRIDES_OPTIONS
|
|
14
|
+
from umu_commander.proton import (
|
|
15
|
+
collect_proton_versions,
|
|
16
|
+
get_latest_umu_proton,
|
|
17
|
+
refresh_proton_versions,
|
|
18
|
+
)
|
|
19
|
+
from umu_commander.util import build_choices
|
|
13
20
|
|
|
14
21
|
|
|
15
|
-
def
|
|
16
|
-
|
|
22
|
+
def select_prefix() -> str:
|
|
23
|
+
default = Element(str(Path.cwd() / "prefix"), "Current directory")
|
|
24
|
+
choices = build_choices([default, *config.DEFAULT_PREFIX_DIR.iterdir()], None)
|
|
25
|
+
return str(inquirer.select("Select wine prefix:", choices, default).execute())
|
|
17
26
|
|
|
18
|
-
params: dict[str, Any] = {"umu": {}, "env": {}}
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
if selection == "Current directory":
|
|
30
|
-
params["umu"]["prefix"] = os.path.join(os.getcwd(), "prefix")
|
|
31
|
-
else:
|
|
32
|
-
params["umu"]["prefix"] = os.path.join(config.DEFAULT_PREFIX_DIR, selection)
|
|
28
|
+
def select_proton() -> str:
|
|
29
|
+
default = Element(str(get_latest_umu_proton()), "Latest UMU-Proton")
|
|
30
|
+
choices = build_choices([default], collect_proton_versions(sort=True))
|
|
31
|
+
return str(
|
|
32
|
+
inquirer.select(
|
|
33
|
+
"Select Proton version:", choices, Path.cwd() / "prefix"
|
|
34
|
+
).execute()
|
|
35
|
+
)
|
|
33
36
|
|
|
34
|
-
# Proton selection
|
|
35
|
-
selected_umu_latest: bool = False
|
|
36
|
-
proton_ver: ProtonVer = get_selection(
|
|
37
|
-
"Select Proton version:",
|
|
38
|
-
None,
|
|
39
|
-
collect_proton_versions(sort=True),
|
|
40
|
-
).as_proton_ver()
|
|
41
|
-
params["umu"]["proton"] = os.path.join(proton_ver.dir, proton_ver.version_num)
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if idx in selected:
|
|
54
|
-
idx = "Y"
|
|
55
|
-
print(f"{idx}) {override.label}")
|
|
56
|
-
|
|
57
|
-
index: str = input("? ")
|
|
58
|
-
if index == "":
|
|
59
|
-
break
|
|
60
|
-
|
|
61
|
-
if index.isdecimal():
|
|
62
|
-
index: int = int(index)
|
|
63
|
-
else:
|
|
64
|
-
continue
|
|
38
|
+
def select_dll_override() -> str:
|
|
39
|
+
choices = build_choices(DLL_OVERRIDES_OPTIONS, None)
|
|
40
|
+
return "".join(
|
|
41
|
+
[
|
|
42
|
+
selection
|
|
43
|
+
for selection in inquirer.checkbox(
|
|
44
|
+
"Select DLLs to override:", choices
|
|
45
|
+
).execute()
|
|
46
|
+
]
|
|
47
|
+
)
|
|
65
48
|
|
|
66
|
-
# reset
|
|
67
|
-
if index == 0:
|
|
68
|
-
selected = set()
|
|
69
|
-
continue
|
|
70
49
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
50
|
+
def select_lang() -> str:
|
|
51
|
+
default = Element("", "No override")
|
|
52
|
+
choices = build_choices([default, *LANG_OVERRIDES_OPTIONS], None)
|
|
53
|
+
return inquirer.select("Select locale:", choices, default).execute()
|
|
74
54
|
|
|
75
|
-
if index - 1 < len(possible_overrides):
|
|
76
|
-
selected.add(index)
|
|
77
55
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
56
|
+
def set_launch_args() -> list[str]:
|
|
57
|
+
options: str = inquirer.text(
|
|
58
|
+
"Enter executable options, separated by space:"
|
|
59
|
+
).execute()
|
|
60
|
+
return [opt.strip() for opt in options.split(" ")]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def select_exe() -> str:
|
|
64
|
+
files = [file for file in Path.cwd().iterdir() if file.is_file()]
|
|
65
|
+
choices = build_choices(files, None)
|
|
66
|
+
return str(inquirer.select("Select game executable:", choices).execute())
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def create(params: dict[str, dict[str, Any]], interactive: bool):
|
|
70
|
+
refresh_proton_versions()
|
|
71
|
+
|
|
72
|
+
# Prefix selection
|
|
73
|
+
if params.get("umu").get("prefix") is None:
|
|
74
|
+
if interactive:
|
|
75
|
+
params["umu"]["prefix"] = select_prefix()
|
|
76
|
+
else:
|
|
77
|
+
params["umu"]["prefix"] = str(Path.cwd() / "prefix")
|
|
78
|
+
|
|
79
|
+
# Proton selection
|
|
80
|
+
if params.get("umu").get("proton") is None:
|
|
81
|
+
if interactive:
|
|
82
|
+
params["umu"]["proton"] = select_proton()
|
|
83
|
+
else:
|
|
84
|
+
params["umu"]["proton"] = str(get_latest_umu_proton())
|
|
85
|
+
|
|
86
|
+
selected_umu_latest: bool = params["umu"]["proton"] == str(get_latest_umu_proton())
|
|
87
|
+
|
|
88
|
+
# Select DLL overrides
|
|
89
|
+
if interactive:
|
|
90
|
+
params["env"]["WINEDLLOVERRIDES"] = select_dll_override()
|
|
91
|
+
print(params["env"]["WINEDLLOVERRIDES"])
|
|
85
92
|
|
|
86
93
|
# Set language locale
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
[lang_default, string_to_value("Japanese")],
|
|
91
|
-
None,
|
|
92
|
-
default_element=lang_default,
|
|
93
|
-
).value:
|
|
94
|
-
case "Default":
|
|
95
|
-
pass
|
|
96
|
-
case "Japanese":
|
|
97
|
-
params["env"]["LANG"] = "ja_JP.UTF8"
|
|
94
|
+
if interactive:
|
|
95
|
+
if (lang := select_lang()) != "":
|
|
96
|
+
params["env"]["LANG"] = lang
|
|
98
97
|
|
|
99
98
|
# Input executable launch args
|
|
100
|
-
|
|
101
|
-
"
|
|
102
|
-
).split()
|
|
103
|
-
params["umu"]["launch_args"] = launch_args
|
|
99
|
+
if interactive:
|
|
100
|
+
params["umu"]["launch_args"] = set_launch_args()
|
|
104
101
|
|
|
105
102
|
# Select executable name
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
for file in os.listdir(os.getcwd())
|
|
109
|
-
if os.path.isfile(os.path.join(os.getcwd(), file))
|
|
110
|
-
]
|
|
111
|
-
executable_name: str = get_selection(
|
|
112
|
-
"Select game executable:", strings_to_values(files), None
|
|
113
|
-
).value
|
|
114
|
-
params["umu"]["exe"] = executable_name
|
|
103
|
+
if params["umu"].get("exe") is None:
|
|
104
|
+
params["umu"]["exe"] = select_exe()
|
|
115
105
|
|
|
116
106
|
try:
|
|
117
107
|
with open(config.UMU_CONFIG_NAME, "wb") as file:
|
|
@@ -119,21 +109,26 @@ def create():
|
|
|
119
109
|
|
|
120
110
|
print(f"Configuration file {config.UMU_CONFIG_NAME} created at {os.getcwd()}.")
|
|
121
111
|
print(f"Use by running umu-commander run.")
|
|
122
|
-
|
|
112
|
+
if not selected_umu_latest:
|
|
113
|
+
tracking.track(
|
|
114
|
+
Path(params["umu"]["proton"], Path(params["umu"]["exe"])).parent,
|
|
115
|
+
refresh_versions=False,
|
|
116
|
+
)
|
|
123
117
|
except:
|
|
124
118
|
print("Could not create configuration file.")
|
|
125
119
|
|
|
126
120
|
|
|
127
121
|
def run():
|
|
128
|
-
if not
|
|
122
|
+
if not config.UMU_CONFIG_NAME.exists():
|
|
129
123
|
print("No umu config in current directory.")
|
|
130
124
|
return
|
|
131
125
|
|
|
132
126
|
with open(config.UMU_CONFIG_NAME, "rb") as toml_file:
|
|
133
127
|
toml_conf = tomllib.load(toml_file)
|
|
134
128
|
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
prefix_path = Path(toml_conf["umu"]["prefix"])
|
|
130
|
+
if not prefix_path.exists():
|
|
131
|
+
prefix_path.mkdir()
|
|
137
132
|
|
|
138
133
|
os.environ.update(toml_conf.get("env", {}))
|
|
139
134
|
subprocess.run(
|
umu_commander/util.py
CHANGED
|
@@ -1,117 +1,58 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def string_to_value(value: str) -> Value:
|
|
6
|
-
return Value(value)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def strings_to_values(values: list[str]) -> list[Value]:
|
|
10
|
-
return [string_to_value(value) for value in values]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _selection_set_valid(
|
|
14
|
-
selection_elements: list[Element] | None, selection_groups: list[Group] | None
|
|
15
|
-
):
|
|
16
|
-
if (selection_elements is None or len(selection_elements) == 0) and (
|
|
17
|
-
len(selection_groups) == 0
|
|
18
|
-
or all([len(group.elements) == 0 for group in selection_groups])
|
|
19
|
-
):
|
|
20
|
-
return False
|
|
21
|
-
else:
|
|
22
|
-
return True
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def _print_selection_group(
|
|
26
|
-
elements: list[Element], enum_start_idx: int, tab: bool = True
|
|
27
|
-
):
|
|
28
|
-
prefix: str = "\t" if tab else ""
|
|
29
|
-
print(
|
|
30
|
-
*[
|
|
31
|
-
f"{prefix}{idx}) {element.value} {element.info}"
|
|
32
|
-
for idx, element in enumerate(elements, start=enum_start_idx)
|
|
33
|
-
],
|
|
34
|
-
sep="\n",
|
|
35
|
-
)
|
|
36
|
-
print("")
|
|
37
|
-
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from pathlib import Path
|
|
38
3
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
selection_elements: list[Element],
|
|
42
|
-
selection_groups: list[Group],
|
|
43
|
-
) -> Element:
|
|
44
|
-
len_counter: int = 0
|
|
4
|
+
from InquirerPy.base.control import Choice
|
|
5
|
+
from InquirerPy.separator import Separator
|
|
45
6
|
|
|
46
|
-
|
|
47
|
-
|
|
7
|
+
from umu_commander import VERSION
|
|
8
|
+
from umu_commander import database as db
|
|
9
|
+
from umu_commander.classes import Element
|
|
48
10
|
|
|
49
|
-
for group in selection_groups:
|
|
50
|
-
len_counter += len(group.elements)
|
|
51
|
-
if len_counter > selection_index:
|
|
52
|
-
break
|
|
53
11
|
|
|
54
|
-
|
|
55
|
-
|
|
12
|
+
def count_users(proton_dir: Path, proton_ver: Path) -> str:
|
|
13
|
+
return (
|
|
14
|
+
f"({len(db.get(proton_dir, proton_ver))})"
|
|
15
|
+
if proton_ver in db.get(proton_dir)
|
|
16
|
+
else "(-)"
|
|
56
17
|
)
|
|
57
18
|
|
|
58
19
|
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
) ->
|
|
65
|
-
if
|
|
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
|
-
print("")
|
|
92
|
-
if selection == "":
|
|
93
|
-
if default_element is not None:
|
|
94
|
-
return default_element
|
|
95
|
-
|
|
96
|
-
# If only 1 choice
|
|
97
|
-
if len(selection_groups) == 0 and len(selection_elements) == 1:
|
|
98
|
-
return selection_elements[0]
|
|
99
|
-
|
|
100
|
-
# len(selection_groups) == 1 and len(selection_groups[0].elements) == 1
|
|
101
|
-
groups_with_one_element: list[Group] = [
|
|
102
|
-
group for group in selection_groups if len(group.elements) == 1
|
|
20
|
+
def build_choices(
|
|
21
|
+
elements: Iterable[Path | Element] | None,
|
|
22
|
+
groups: dict[Path | Element, Iterable[Path | Element]] | None,
|
|
23
|
+
*,
|
|
24
|
+
count_elements: bool = False,
|
|
25
|
+
) -> list[Separator | Choice | str]:
|
|
26
|
+
if elements is None:
|
|
27
|
+
elements = []
|
|
28
|
+
|
|
29
|
+
if groups is None:
|
|
30
|
+
groups = {}
|
|
31
|
+
|
|
32
|
+
choices: list[Choice | Separator] = [Choice(el, name=el.name) for el in elements]
|
|
33
|
+
if len(choices) > 0:
|
|
34
|
+
choices.append(Separator(""))
|
|
35
|
+
|
|
36
|
+
for group, elements in groups.items():
|
|
37
|
+
choices.extend(
|
|
38
|
+
[
|
|
39
|
+
Separator(f"In: {group}"),
|
|
40
|
+
*[
|
|
41
|
+
Choice(
|
|
42
|
+
el,
|
|
43
|
+
name=(
|
|
44
|
+
el.name
|
|
45
|
+
if not count_elements
|
|
46
|
+
else f"{el.name} {count_users(group, el)}"
|
|
47
|
+
),
|
|
48
|
+
)
|
|
49
|
+
for el in elements
|
|
50
|
+
],
|
|
51
|
+
Separator(""),
|
|
103
52
|
]
|
|
104
|
-
|
|
105
|
-
return groups_with_one_element[0].elements[0]
|
|
106
|
-
|
|
107
|
-
elif selection.isdecimal():
|
|
108
|
-
selection: int = int(selection) - 1
|
|
109
|
-
if enum_start_idx - 1 > selection >= 0:
|
|
110
|
-
break
|
|
53
|
+
)
|
|
111
54
|
|
|
112
|
-
return
|
|
113
|
-
selection, selection_elements, selection_groups
|
|
114
|
-
)
|
|
55
|
+
return choices
|
|
115
56
|
|
|
116
57
|
|
|
117
58
|
def print_help():
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: umu-commander
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
4
4
|
Summary: umu-commander is an interactive CLI tool to help you manage umu.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Mpaxlamitsounas/umu-commander
|
|
6
6
|
Project-URL: Issues, https://github.com/Mpaxlamitsounas/umu-commander/issues
|
|
@@ -11,11 +11,14 @@ Keywords: umu,umu-launcher
|
|
|
11
11
|
Classifier: Operating System :: POSIX :: Linux
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Requires-Python: >=3.12
|
|
14
|
+
Requires-Dist: inquirerpy
|
|
14
15
|
Requires-Dist: tomli-w
|
|
15
16
|
Description-Content-Type: text/markdown
|
|
16
17
|
|
|
17
18
|
## umu-commander
|
|
18
|
-
### umu-commander is
|
|
19
|
+
### umu-commander is a CLI tool to augment umu-launcher as well as help you manage its Proton versions.
|
|
20
|
+
|
|
21
|
+
This tool does not provide a centralised way of managing your games or utilise template umu-configs. See [faugus-launcher](https://github.com/Faugus/faugus-launcher) or [Nero-umu](https://github.com/SeongGino/Nero-umu) for something more akin to a games launcher, and [umu-wrapper](https://github.com/korewaChino/umu-wrapper) for templating functionality.
|
|
19
22
|
|
|
20
23
|
Proton versions can track and untrack directories, with the intention of safely removing them once no game depends on one.
|
|
21
24
|
|
|
@@ -26,17 +29,18 @@ The configuration file lives at `~/.config/umu-commander.toml`, which cannot be
|
|
|
26
29
|
|
|
27
30
|
The config schema is as follows:
|
|
28
31
|
|
|
29
|
-
| Name
|
|
30
|
-
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `UMU_CONFIG_NAME`
|
|
36
|
-
| `
|
|
37
|
-
| `[DLL_OVERRIDES_OPTIONS]`
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
| Name | Description |
|
|
33
|
+
|:---------------------------|:-------------------------------------------------------------------|
|
|
34
|
+
| `DB_DIR` | Directory where the Tracking DB is stored. |
|
|
35
|
+
| `DB_NAME` | Tracking DB filename. |
|
|
36
|
+
| `DEFAULT_PREFIX_DIR` | Directory where umu-commander will search for WINE prefixes. |
|
|
37
|
+
| `PROTON_PATHS` | List of directories umu-commander will search for Proton versions. |
|
|
38
|
+
| `UMU_CONFIG_NAME` | Name of the umu config created using umu-commander create. |
|
|
39
|
+
| `UMU_PROTON_PATH` | Directory where umu-launcher downloads its UMU-Proton versions. |
|
|
40
|
+
| `[DLL_OVERRIDES_OPTIONS]` | TOML table where all possible DLL overrides are listed. |
|
|
41
|
+
| `[LANG_OVERRIDES_OPTIONS]` | TOML table where all possible LANG overrides are listed. |
|
|
42
|
+
|
|
43
|
+
To add an extra DLL override option, add a line below the table in the form "`Label`" = "`WINE DLL override string`". Use the winhttp example as an example. You can add LANG overrides in a similar way.
|
|
40
44
|
|
|
41
45
|
### Verbs
|
|
42
46
|
umu-commander needs one of the following verbs specified after the executable name:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
umu_commander/__init__.py,sha256=89F2IQARHlHjXGiEH_BGITazcP0AvMuV3rjFPaIQHDE,19
|
|
2
|
+
umu_commander/__main__.py,sha256=RP6aH0JeYXb69P_gbFmlM_hsALUor4Sf4RENJ-oYs2I,2460
|
|
3
|
+
umu_commander/classes.py,sha256=k2ymgDkVdG0-yBLJsByfbgx4qi4fIOU0RXAQcSdlK-A,363
|
|
4
|
+
umu_commander/configuration.py,sha256=dUM4esIU8fj7Ts0pA8jM7bwhldsZbnRJdYlaNpfQuWg,2866
|
|
5
|
+
umu_commander/database.py,sha256=Ix5ISzUKWF0xs3BUfhB8-azmKiWgP3NiaviKMydI-0E,1890
|
|
6
|
+
umu_commander/proton.py,sha256=ZYew3Yn4m02tY0Y13E6loMSdxRSOIHA4t-1_BnxZa4g,1962
|
|
7
|
+
umu_commander/tracking.py,sha256=ugorxaI_g7pNYR5BfUowcRzdciMg0Fin6bqNxyBli2w,3302
|
|
8
|
+
umu_commander/umu_config.py,sha256=7U9888VP-rW61lFWfZsHMbCq_5_Q3-vqS4Y8trdMupU,4223
|
|
9
|
+
umu_commander/util.py,sha256=ZTi3yrvGHy0Rr8xKEzloT-xcwCSHgDqBsH36oftOKiE,1817
|
|
10
|
+
umu_commander-1.7.0.dist-info/METADATA,sha256=yCgJWJbntlR-HkurqJR5Dlutyajrqhjh1D6kseld0Uo,5920
|
|
11
|
+
umu_commander-1.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
12
|
+
umu_commander-1.7.0.dist-info/entry_points.txt,sha256=ljyUmDmgCCMm7mQgB1syoWbf_5AiemyrS6YN7eTn9CI,62
|
|
13
|
+
umu_commander-1.7.0.dist-info/licenses/LICENSE.txt,sha256=yipFXBRmVZ2Q44x1q18HccPUAECBQLXAOAr21aS57uY,1071
|
|
14
|
+
umu_commander-1.7.0.dist-info/RECORD,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
umu_commander/__init__.py,sha256=eKShG3zJqAmg0Iu_xbUNkhAa7HaW-1-IthmdvuW6whs,19
|
|
2
|
-
umu_commander/__main__.py,sha256=b5T4HnlLYreaLL9rbr0p7xT4roIoMJ8qJO3o_tcpK_M,2352
|
|
3
|
-
umu_commander/classes.py,sha256=3vEC7Iq3buEUaIIJKO1ze57hs9yQ_-9pnkazHkSfcLk,1655
|
|
4
|
-
umu_commander/configuration.py,sha256=bF_1n_8PQ04GmJZpC1scQ5OBZbsVXPstJ9RGfKE-D5k,2277
|
|
5
|
-
umu_commander/database.py,sha256=mkBX0e6YEv2dWU2hCMaZwhSD02f_XefW0qbErcVHHYo,1183
|
|
6
|
-
umu_commander/proton.py,sha256=K3KhaXxZawOZN84XKhXKP-HkIrW-sLSbMwoKBL2SrAU,2552
|
|
7
|
-
umu_commander/tracking.py,sha256=WfWeQWnNNK_q-1rFt0HPFNDD8z7VcTCYKI5A_W8xNIg,3113
|
|
8
|
-
umu_commander/umu_config.py,sha256=cePb3uKcf2WU4fwyQrqXJvlXmP7Riihc1guWfNAyT2c,4230
|
|
9
|
-
umu_commander/util.py,sha256=8zn0c3gyAuZ8uuiDga4amsOYGFRy2FwzADsyJ1au0LY,3671
|
|
10
|
-
umu_commander-1.6.2.dist-info/METADATA,sha256=nuaRiii5hRfKQ4toY-4w2Zq3aSIsDN0OpQmSm6Mo-5o,5391
|
|
11
|
-
umu_commander-1.6.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
12
|
-
umu_commander-1.6.2.dist-info/entry_points.txt,sha256=ljyUmDmgCCMm7mQgB1syoWbf_5AiemyrS6YN7eTn9CI,62
|
|
13
|
-
umu_commander-1.6.2.dist-info/licenses/LICENSE.txt,sha256=yipFXBRmVZ2Q44x1q18HccPUAECBQLXAOAr21aS57uY,1071
|
|
14
|
-
umu_commander-1.6.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|