Skip to content

Commit 224c6a0

Browse files
authored
Added Savegame Data for Cassette Beasts
Thanks PandoraFox for the code for unpacking gcpf that this is based on.
1 parent 5b4aa6e commit 224c6a0

1 file changed

Lines changed: 76 additions & 3 deletions

File tree

games/game_cassettebeasts.py

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1+
from collections.abc import Mapping
2+
from io import BytesIO
3+
import json
4+
import mobase
5+
import math
16
import os
27
import shutil
3-
import mobase
8+
import struct
9+
import sys
10+
import zlib
411

512
from pathlib import Path
613
from functools import cached_property
714

15+
from ..basic_features import BasicLocalSavegames
16+
from ..basic_features.basic_save_game_info import (BasicGameSaveGame,BasicGameSaveGameInfo)
817
from ..basic_game import BasicGame
918

10-
from PyQt6.QtCore import QDir, QFileInfo
19+
from PyQt6.QtCore import QDateTime, QDir, QFile, QFileInfo
1120

1221

1322
class CassetteBeastsModDataChecker(mobase.ModDataChecker):
@@ -47,6 +56,60 @@ def fix(self, filetree: mobase.IFileTree) -> mobase.IFileTree:
4756
return None
4857
return filetree
4958

59+
class CassetteBlock:
60+
def __init__(self):
61+
compressed_size = None
62+
data = None
63+
64+
class CassetteBeastsSaveGame(BasicGameSaveGame):
65+
def __init__(self, filepath: Path):
66+
super().__init__(filepath)
67+
self.name: str = ""
68+
self.cheated: str = ""
69+
self.lastsave: int = 0
70+
self.elapsed: int = 0
71+
info = bytearray()
72+
data = bytes()
73+
74+
with open(filepath, 'rb') as infile:
75+
magic_string = infile.read(4)
76+
77+
compression_mode, blocksize, raw_size = struct.unpack("III", infile.read(12))
78+
79+
num_blocks = math.ceil(raw_size / blocksize)
80+
81+
blocks = []
82+
83+
for bnum in range(num_blocks):
84+
block = CassetteBlock()
85+
block.compressed_size = struct.unpack("I", infile.read(4))[0]
86+
blocks.append(block)
87+
88+
for block in blocks:
89+
block.data = infile.read(block.compressed_size)
90+
91+
magic_string = infile.read(4)
92+
infile.close()
93+
94+
for block in blocks:
95+
data = zlib.decompress(block.data, wbits=40, bufsize=blocksize)
96+
info = info + data
97+
98+
save_data = json.load(BytesIO(info))
99+
self.name = save_data["party"]["player"]["custom"]["name"]
100+
self.cheated = save_data["has_cheated"]
101+
102+
def getName(self) -> str:
103+
return self.name
104+
105+
def getCheated(self) -> str:
106+
return self.cheated
107+
108+
def getMetadata(p: Path, save: mobase.ISaveGame) -> Mapping[str, str]:
109+
return {
110+
"Character": save.getName(),
111+
"Cheated": save.getCheated()
112+
}
50113

51114
class CassetteBeastsGame(BasicGame):
52115
appdataenv = os.getenv("APPDATA")
@@ -60,13 +123,16 @@ class CassetteBeastsGame(BasicGame):
60123
GameBinary = "CassetteBeasts.exe"
61124
GameDataPath = appdataenv + "/CassetteBeasts/mods"
62125
GameDocumentsDirectory = appdataenv + "/CassetteBeasts"
63-
GameSavesDirectory = '%GAME_DOCUMENTS%'
64126
GameSaveExtension = "gcpf"
65127

66128
def init(self, organizer: mobase.IOrganizer) -> bool:
67129
super().init(organizer)
68130
self.dataChecker = CassetteBeastsModDataChecker(organizer)
69131
self._register_feature(self.dataChecker)
132+
self._register_feature(BasicLocalSavegames(self))
133+
self._register_feature(
134+
BasicGameSaveGameInfo(None, getMetadata)
135+
)
70136
return True
71137

72138
def executables(self):
@@ -81,6 +147,13 @@ def executables(self):
81147
),
82148
]
83149

150+
def listSaves(self, folder: QDir) -> list[mobase.ISaveGame]:
151+
ext = self._mappings.savegameExtension.get()
152+
return [
153+
CassetteBeastsSaveGame(path)
154+
for path in Path(folder.absolutePath()).glob(f"*.{ext}")
155+
]
156+
84157
@cached_property
85158
def _base_dlls(self) -> set[str]:
86159
base_dir = Path(self.gameDirectory().absolutePath())

0 commit comments

Comments
 (0)