Skip to content

Commit 1892325

Browse files
committed
updated env variables and fix cassette beasts again
1 parent 5b4aa6e commit 1892325

7 files changed

Lines changed: 140 additions & 16 deletions

games/game_cassettebeasts.py

Lines changed: 131 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
1+
from collections.abc import Mapping, Sequence
2+
from datetime import datetime
3+
from functools import cached_property
4+
from io import BytesIO
5+
from typing import Any, Optional
6+
from pathlib import Path
7+
import json
8+
import math
19
import os
210
import shutil
3-
import mobase
11+
import struct
12+
import zlib
413

5-
from pathlib import Path
6-
from functools import cached_property
14+
import mobase
15+
from PyQt6.QtCore import QDateTime, QDir, QFile, QFileInfo
716

17+
from ..basic_features import BasicLocalSavegames
18+
from ..basic_features.basic_save_game_info import (BasicGameSaveGame,BasicGameSaveGameInfo)
819
from ..basic_game import BasicGame
920

10-
from PyQt6.QtCore import QDir, QFileInfo
1121

22+
def json_get_me(value: Any, path: Sequence[str | int], /, default: Any) -> Any:
23+
for part in path:
24+
if type(part) not in (str, int) or type(value) not in (dict, list):
25+
return default
26+
value = value[part]
27+
return value
1228

1329
class CassetteBeastsModDataChecker(mobase.ModDataChecker):
1430
def __init__(self, organizer: mobase.IOrganizer):
@@ -47,26 +63,125 @@ def fix(self, filetree: mobase.IFileTree) -> mobase.IFileTree:
4763
return None
4864
return filetree
4965

66+
class CassetteBlock:
67+
def __init__(self):
68+
compressed_size: str = "(unknown)"
69+
data: str = "(unknown)"
70+
71+
class CassetteBeastsSaveGame(BasicGameSaveGame):
72+
def __init__(self, filepath: Path):
73+
super().__init__(filepath)
74+
self.name: str = "(unknown)"
75+
self.cheated: str = "(unknown)"
76+
self.lastsave: str = "(unknown)"
77+
self.elapsed: str = "(unknown)"
78+
# This doesn't state wether the game would load it,
79+
# only if the data was properly parsed.
80+
self.errorMessage: str = ""
81+
82+
save_data = None
83+
try:
84+
info = bytearray()
85+
data = bytes()
86+
with open(filepath, 'rb') as infile:
87+
infile.read(4)
88+
89+
compression_mode, blocksize, raw_size = struct.unpack("III", infile.read(12))
90+
91+
num_blocks = math.ceil(raw_size / blocksize)
92+
93+
blocks = []
94+
95+
for _bnum in range(num_blocks):
96+
block = CassetteBlock()
97+
block.compressed_size = struct.unpack("I", infile.read(4))[0]
98+
blocks.append(block)
99+
100+
for block in blocks:
101+
block.data = infile.read(block.compressed_size)
102+
103+
infile.read(4)
104+
infile.close()
105+
for block in blocks:
106+
data = zlib.decompress(block.data, wbits=40, bufsize=blocksize)
107+
info = info + data
108+
save_data = json.load(BytesIO(info))
109+
except (OSError, struct.error, ValueError) as err:
110+
s = str(err)
111+
self.errorMessage = ('{0}: {1}' if s else '{0}').format(
112+
err.__class__.__name__, s
113+
)
114+
return
115+
x = json_get_me(save_data, ["party", "player", "custom", "name"], None)
116+
if type(x) is str:
117+
self.name = x
118+
x = json_get_me(save_data, ["saved_datetime"], None)
119+
if type(x) in (int, float):
120+
try:
121+
dt = datetime.fromtimestamp(float(x))
122+
except OSError:
123+
pass
124+
else:
125+
self.lastsave = "{0:d}-{1:02d}-{2:02d} at {3:02d}:{4:02d}:{5:02d}".format(
126+
dt.year, dt.month, dt.day,
127+
dt.hour, dt.minute, dt.second
128+
)
129+
x = json_get_me(save_data, ["play_time"], None)
130+
if type(x) in (int, float):
131+
a = [ 0, 0, 0, int(x * 10) ]
132+
a[2:4] = divmod(a[3], 10)
133+
a[1:3] = divmod(a[2], 60)
134+
a[0:2] = divmod(a[1], 60)
135+
self.elapsed = "{0:02d}:{1:02d}:{2:02d}.{3:01d}".format(*a)
136+
x = json_get_me(save_data, ["has_cheated"], None)
137+
if type(x) is bool:
138+
self.cheated = "Yes" if x else "No"
139+
140+
def getName(self) -> str:
141+
return self.name
142+
143+
def getCheated(self) -> str:
144+
return self.cheated
145+
146+
def getLastSaved(self) -> str:
147+
return self.lastsave
148+
149+
def getPlayTime(self) -> str:
150+
return self.elapsed
151+
152+
def getMetadata(p: Path, save: mobase.ISaveGame) -> Mapping[str, str]:
153+
if not save.errorMessage:
154+
return {
155+
"Character": save.getName(),
156+
"Last Saved": save.getLastSaved(),
157+
"Play Time": save.getPlayTime(),
158+
"Cheated": save.getCheated()
159+
}
160+
return {
161+
"Error loading file:": save.errorMessage
162+
}
50163

51164
class CassetteBeastsGame(BasicGame):
52-
appdataenv = os.getenv("APPDATA")
53-
54165
Name = "Cassette Beasts Support Plugin"
55166
Author = "modworkshop"
56167
Version = "1"
57168
GameName = "Cassette Beasts"
58169
GameShortName = "cassette-beasts"
59170
GameSteamId = 1321440
60171
GameBinary = "CassetteBeasts.exe"
61-
GameDataPath = appdataenv + "/CassetteBeasts/mods"
62-
GameDocumentsDirectory = appdataenv + "/CassetteBeasts"
63-
GameSavesDirectory = '%GAME_DOCUMENTS%'
172+
GameDataPath = "%USERPROFILE%/AppData/Roaming/CassetteBeasts/mods"
173+
GameDocumentsDirectory = "%USERPROFILE%/AppData/Roaming/CassetteBeasts"
174+
GameSavesDirectory = "%GAME_DOCUMENTS%"
64175
GameSaveExtension = "gcpf"
65176

66177
def init(self, organizer: mobase.IOrganizer) -> bool:
67178
super().init(organizer)
68179
self.dataChecker = CassetteBeastsModDataChecker(organizer)
69180
self._register_feature(self.dataChecker)
181+
self._register_feature(BasicLocalSavegames(self))
182+
self._register_feature(
183+
BasicGameSaveGameInfo(None, getMetadata)
184+
)
70185
return True
71186

72187
def executables(self):
@@ -81,6 +196,13 @@ def executables(self):
81196
),
82197
]
83198

199+
def listSaves(self, folder: QDir) -> list[mobase.ISaveGame]:
200+
ext = self._mappings.savegameExtension.get()
201+
return [
202+
CassetteBeastsSaveGame(path)
203+
for path in Path(folder.absolutePath()).glob(f"*.{ext}")
204+
]
205+
84206
@cached_property
85207
def _base_dlls(self) -> set[str]:
86208
base_dir = Path(self.gameDirectory().absolutePath())

games/game_ovkwalkingdead.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class OTWDGame(BasicGame):
179179
GameDataUE4SSMods = "Binaries/Win64/Mods"
180180
GameDataPakMods = "Content/Paks/~Mods"
181181
GameDataMovieMods = "Content/Movies"
182-
GameDocumentsDirectory = "%LOCALAPPDATA%/OTWD/Saved/Config/WindowsClient"
182+
GameDocumentsDirectory = "%USERPROFILE%/AppData/Local/OTWD/Saved/Config/WindowsClient"
183183
GameSaveExtension = "sav"
184184
_main_window: QMainWindow
185185
_ue4ss_tab: UE4SSTabWidget

games/game_pacificdrive.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class PacificDriveGame(BasicGame):
180180
GameDataUE4SSMods = "Binaries/Win64/Mods"
181181
GameDataPakMods = "Content/Paks/~Mods"
182182
GameDataMovieMods = "Content/Movies"
183-
GameDocumentsDirectory = "%LOCALAPPDATA%/PenDriverPro/Saved/Config/WindowsNoEditor"
183+
GameDocumentsDirectory = "%USERPROFILE%/AppData/Local/PenDriverPro/Saved/Config/WindowsNoEditor"
184184
GameSaveExtension = "sav"
185185
_main_window: QMainWindow
186186
_ue4ss_tab: UE4SSTabWidget

games/game_payday1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ class Payday1Game(BasicGame):
199199
GameSteamId = 24240
200200
GameBinary = "payday_win32_release.exe"
201201
GameDataPath = "%GAME_PATH%"
202-
GameDocumentsDirectory = "%LOCALAPPDATA%/PAYDAY"
202+
GameDocumentsDirectory = "%USERPROFILE%/AppData/Local/PAYDAY"
203203
_forced_libraries = ["IPHLPAPI.dll", "WSOCK32.dll"]
204204

205205
def init(self, organizer: mobase.IOrganizer) -> bool:

games/game_payday3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ class Payday3Game(BasicGame):
212212
GameDataUE4SSMods = "Binaries/Win64/Mods"
213213
GameDataPakMods = "Content/Paks/~Mods"
214214
GameDataMovieMods = "Content/Movies"
215-
GameDocumentsDirectory = "%LOCALAPPDATA%/PAYDAY3/Saved/Config/WindowsClient"
215+
GameDocumentsDirectory = "%USERPROFILE%/AppData/Local/PAYDAY3/Saved/Config/WindowsClient"
216216
GameSaveExtension = "sav"
217217
_main_window: QMainWindow
218218
_ue4ss_tab: UE4SSTabWidget

games/game_roadtovostok.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ def fix(self, filetree: mobase.IFileTree) -> mobase.IFileTree:
4242
class RoadToVostokGame(BasicGame):
4343

4444
Name = "Road to Vostok Support Plugin"
45-
Author = "modworkshop"
45+
Author = "modworkshop"
4646
Version = "1"
4747
GameName = "Road to Vostok"
4848
GameShortName = "road-to-vostok"
4949
GameSteamId = 1963610
5050
GameBinary = "Road_to_Vostok_Demo.exe"
5151
GameDataPath = "%GAME_PATH%"
5252
GameModsPath = "mods"
53-
GameDocumentsDirectory = "%APPDATA%/Godot/app_userdata/Road to Vostok"
53+
GameDocumentsDirectory = "%USERPROFILE%/AppData/Local/Godot/app_userdata/Road to Vostok"
5454
GameSaveExtension = "tres"
5555

5656
def init(self, organizer: mobase.IOrganizer) -> bool:

games/game_titanfall2.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ def _Fix_Installed_Mod(self, mod: mobase.IModInterface):
102102
):
103103
path = mod.absolutePath()
104104
json_path = os.path.join(path, northstarModPath + "FOLDERNAME/mod.json")
105-
mod_data = json.load(open(json_path, encoding="utf-8"))
105+
with open(json_path, "r") as json_data:
106+
mod_data = json.load(json_data)
107+
json_data.close()
106108
modname = mod_data["name"]
107109
old_path = os.path.join(path, northstarModPath + "FOLDERNAME")
108110
new_path = os.path.join(path, northstarModPath + f"{modname}")

0 commit comments

Comments
 (0)