mirror of
https://github.com/ModOrganizer2/modorganizer-basic_games.git
synced 2026-07-27 14:07:29 -07:00
165 lines
4.5 KiB
Python
165 lines
4.5 KiB
Python
import io
|
|
import struct
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import BinaryIO, Optional, cast
|
|
|
|
import lzokay # pyright: ignore[reportMissingTypeStubs]
|
|
|
|
from .XRIO import XRReader, XRStream
|
|
from .XRObject import XRCreatureActor, XRFlag
|
|
|
|
|
|
class XRSave:
|
|
filepath: Path
|
|
player: XRCreatureActor
|
|
|
|
_factions = {
|
|
0: "Loner",
|
|
1: "Monster",
|
|
2: "Trader",
|
|
3: "Army",
|
|
4: "Sin",
|
|
5: "Bandit",
|
|
6: "Duty",
|
|
7: "Ecologist",
|
|
8: "Freedom",
|
|
9: "Mercenary",
|
|
10: "Army",
|
|
11: "Monolith",
|
|
12: "Sin",
|
|
13: "Loner",
|
|
14: "Zombified",
|
|
15: "Clear Sky",
|
|
16: "UNISG",
|
|
17: "Renegade",
|
|
18: "Loner",
|
|
19: "Bandit",
|
|
20: "Duty",
|
|
21: "Freedom",
|
|
22: "Clear Sky",
|
|
23: "Ecologist",
|
|
24: "Mercenary",
|
|
25: "Military",
|
|
26: "Monolith",
|
|
27: "Zombified",
|
|
28: "Sin",
|
|
29: "UNISG",
|
|
30: "Renegade",
|
|
31: "Participant",
|
|
}
|
|
|
|
_ranks = {
|
|
1999: "Novice",
|
|
3999: "Trainee",
|
|
6999: "Experienced",
|
|
9999: "Professional",
|
|
14999: "Veteran",
|
|
20999: "Expert",
|
|
27999: "Master",
|
|
"max": "Legend",
|
|
}
|
|
|
|
_reputation = {
|
|
-2000: "Terrible",
|
|
-1500: "Really Bad",
|
|
-1000: "Very Bad",
|
|
-500: "Bad",
|
|
499: "Netural",
|
|
999: "Good",
|
|
1499: "Very Good",
|
|
1999: "Really Good",
|
|
"max": "Excellent",
|
|
}
|
|
|
|
def __init__(self, filepath: Path):
|
|
self.filepath = filepath
|
|
self.fetchInfo()
|
|
with open(filepath, "rb") as file:
|
|
stream = self.readFile(file)
|
|
if stream:
|
|
self.readObject(stream)
|
|
|
|
def fetchInfo(self):
|
|
self.splitInfo()
|
|
self.time = self.filepath.stat().st_mtime
|
|
self.time_date = datetime.fromtimestamp(self.time)
|
|
self.time_fmt = self.time_date.strftime("%I:%M %m/%d/%Y")
|
|
|
|
def splitInfo(self):
|
|
save_clean = self.filepath.name.split(".scop", 1)[0]
|
|
save_split = save_clean.split(" - ", 1)
|
|
self.user = save_split[0]
|
|
if len(save_split) > 1:
|
|
save_end = save_split[1].split("_", 1)
|
|
if len(save_end) > 1:
|
|
self.save_fmt = f"{save_end[0]} (#{save_end[1]})".title()
|
|
else:
|
|
self.save_fmt = f"{save_end[0]}".title()
|
|
else:
|
|
self.save_fmt = "Unknown"
|
|
|
|
def readFile(self, file: BinaryIO) -> Optional[XRStream]:
|
|
size = self.filepath.stat().st_size
|
|
if size < 8:
|
|
return None
|
|
|
|
(start, version, source) = struct.unpack("@iii", file.read(12))
|
|
if (start == -1) and (version >= 6):
|
|
file.seek(12)
|
|
data = file.read(size - 12)
|
|
return XRStream(
|
|
cast(
|
|
bytes,
|
|
lzokay.decompress( # pyright: ignore[reportUnknownMemberType]
|
|
data, source
|
|
),
|
|
)
|
|
)
|
|
|
|
return None
|
|
|
|
def readObject(self, stream: XRStream):
|
|
chunk = stream.open_chunk(XRFlag.CHUNK_OBJECT)
|
|
if chunk:
|
|
chunk.seek(4, io.SEEK_CUR) # obj_count
|
|
count_spawn = chunk.u16()
|
|
spawn = XRReader(chunk.read(count_spawn))
|
|
actor = XRCreatureActor()
|
|
actor.read_spawn(spawn)
|
|
count_update = chunk.u16()
|
|
update = XRReader(chunk.read(count_update))
|
|
actor.read_update(update)
|
|
if actor:
|
|
self.player = actor
|
|
return None
|
|
|
|
def getFaction(self) -> str:
|
|
player = self.player
|
|
if player:
|
|
player_faction = player.community_index
|
|
for faction in self._factions:
|
|
if player_faction == faction:
|
|
return self._factions[faction]
|
|
return "Unknown"
|
|
|
|
def getRank(self) -> str:
|
|
player = self.player
|
|
if player:
|
|
player_rank = player.rank
|
|
for rank in self._ranks:
|
|
if isinstance(rank, int):
|
|
if player_rank <= rank:
|
|
return self._ranks[rank]
|
|
return self._ranks["max"]
|
|
|
|
def getReputation(self) -> str:
|
|
player = self.player
|
|
if player:
|
|
player_rep = player.reputation
|
|
for rep in self._reputation:
|
|
if isinstance(rep, int):
|
|
if player_rep <= rep:
|
|
return self._reputation[rep]
|
|
return self._reputation["max"]
|