-
Notifications
You must be signed in to change notification settings - Fork 90
Expand file tree
/
Copy pathgame_sts2.py
More file actions
154 lines (135 loc) · 5.68 KB
/
Copy pathgame_sts2.py
File metadata and controls
154 lines (135 loc) · 5.68 KB
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
import json
import re
from pathlib import Path
from typing import cast
from PyQt6.QtCore import QDir, QFileInfo, qInfo, qWarning
import mobase
from ..basic_features.basic_save_game_info import BasicGameSaveGame
from ..basic_game import BasicGame
from ..steam_utils import find_steam_path
class SlayTheSpire2ModDataChecker(mobase.ModDataChecker):
_VALID_EXTENSIONS = (".pck", ".dll", ".json")
def _has_mod_files(self, filetree: mobase.IFileTree) -> bool:
return any(
entry.isFile() and entry.name().endswith(self._VALID_EXTENSIONS)
for entry in filetree
)
def dataLooksValid(
self, filetree: mobase.IFileTree
) -> mobase.ModDataChecker.CheckReturn:
if self._has_mod_files(filetree):
return mobase.ModDataChecker.VALID
for entry in filetree:
if entry.isDir() and self._has_mod_files(cast(mobase.IFileTree, entry)):
return mobase.ModDataChecker.FIXABLE
return mobase.ModDataChecker.INVALID
def fix(self, filetree: mobase.IFileTree) -> mobase.IFileTree:
for entry in list(filetree):
if entry.isDir():
tree = cast(mobase.IFileTree, entry)
if self._has_mod_files(tree):
filetree.merge(tree)
entry.detach()
return filetree
class SlayTheSpire2Game(BasicGame):
Name = "Slay the Spire 2 Support Plugin"
Author = "Azlle"
Version = "1.1.0"
GameName = "Slay the Spire 2"
GameShortName = "slaythespire2"
GameNexusName = "slaythespire2"
GameNexusId = 8916
GameSteamId = 2868840
GameBinary = "SlayTheSpire2.exe"
GameDataPath = "mods"
GameDocumentsDirectory = "%USERPROFILE%/AppData/Roaming/SlayTheSpire2"
_last_save_dir: str = ""
def init(self, organizer: mobase.IOrganizer) -> bool:
super().init(organizer)
self._register_feature(SlayTheSpire2ModDataChecker())
organizer.modList().onModInstalled(self._on_mod_installed)
return True
def initializeProfile(self, directory: QDir, settings: mobase.ProfileSetting):
mods_path = Path(self.dataDirectory().absolutePath())
if not mods_path.exists():
qInfo(f"Creating mods directory: {mods_path}")
mods_path.mkdir()
super().initializeProfile(directory, settings)
def _on_mod_installed(self, mod: mobase.IModInterface):
mod_name = mod.name()
self._organizer.onNextRefresh(
lambda: self._apply_version(self._organizer.modList().getMod(mod_name)),
True,
)
def _apply_version(self, mod: mobase.IModInterface | None):
if mod is None:
return
mod_path = Path(mod.absolutePath())
for json_file in mod_path.glob("*.json"):
try:
with open(json_file, encoding="utf-8-sig") as f:
data = json.load(f)
if version := data.get("version"):
version = version.lstrip("v")
meta_ini = mod_path / "meta.ini"
raw = meta_ini.read_bytes()
raw = re.sub(
rb"^\s*version\s*=\s*[^\r\n]*",
f"version={version}".encode(),
raw,
flags=re.MULTILINE,
)
meta_ini.write_bytes(raw)
qInfo(
f"Set version of {mod_path.name} to {version} using {json_file.name}"
)
self._organizer.modDataChanged(mod)
break
except (json.JSONDecodeError, OSError) as e:
qWarning(
f"Failed to apply version for {mod_path.name} via {json_file.name}: {e}"
)
continue
def savesDirectory(self) -> QDir:
steam_dir = Path(self.documentsDirectory().absolutePath()) / "steam"
candidates = []
is_fallback = False
steam_path = find_steam_path()
if steam_path is not None:
userdata = steam_path / "userdata"
if userdata.exists():
candidates = [
child / "2868840" / "remote"
for child in userdata.iterdir()
if child.is_dir() and (child / "2868840" / "remote").exists()
]
if not candidates:
is_fallback = True
if steam_dir.exists():
candidates = [child for child in steam_dir.iterdir() if child.is_dir()]
if candidates:
save_dir = max(candidates, key=lambda p: p.stat().st_mtime)
if (s := str(save_dir)) != self._last_save_dir:
status = "not found, using AppData" if is_fallback else "found"
qInfo(f"Steam save directory {status}: {save_dir}")
self.__class__._last_save_dir = s
return QDir(s)
return QDir(str(steam_dir))
def listSaves(self, folder: QDir) -> list[mobase.ISaveGame]:
base = Path(folder.absolutePath())
return [
BasicGameSaveGame(save)
for save in base.rglob("*")
if save.is_file() and save.suffix in (".save", ".run", ".backup")
]
def executables(self):
return [
mobase.ExecutableInfo(
self.GameName,
QFileInfo(self.gameDirectory().absoluteFilePath(self.binaryName())),
),
mobase.ExecutableInfo(
f"{self.GameName} (OpenGL)",
QFileInfo(self.gameDirectory().absoluteFilePath(self.binaryName())),
).withArgument("--rendering-driver opengl3"),
]