From ce3db71e9fcbbb5ce2949dfcc78858071bcf2514 Mon Sep 17 00:00:00 2001 From: Oknehsorod Date: Thu, 16 Jul 2026 15:00:08 +0300 Subject: [PATCH 1/3] feat: Add DFU save support --- games/game_daggerfallunity.py | 64 +++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/games/game_daggerfallunity.py b/games/game_daggerfallunity.py index 6c170f1a..97d7853b 100644 --- a/games/game_daggerfallunity.py +++ b/games/game_daggerfallunity.py @@ -1,6 +1,17 @@ import mobase +import json +from datetime import datetime, timedelta + +from PyQt6.QtCore import QDir +from pathlib import Path + +from ..basic_features.basic_save_game_info import ( + BasicGameSaveGame, + BasicGameSaveGameInfo, +) from ..basic_game import BasicGame +from ..basic_features import BasicLocalSavegames class DaggerfallUnityModDataChecker(mobase.ModDataChecker): @@ -35,15 +46,55 @@ def dataLooksValid( return mobase.ModDataChecker.INVALID +class DaggerfallSaveGame(BasicGameSaveGame): + def __init__(self, filepath: Path): + super().__init__(filepath) + self.saveInfo = json.loads(open(self._filepath / "SaveInfo.txt", "r").read()) + + def getName(self) -> str: + return f'[{self.saveInfo["characterName"]}] {self.saveInfo["saveName"]}' + + def getFilepath(self): + return self._filepath.as_posix() + + def allFiles(self): + return list(map(str, self._filepath.glob("*"))) + + +def parse_daggerfall_save_metadata(save_path: Path, save: mobase.ISaveGame): + saveInfo = json.loads(open(save_path / "SaveInfo.txt", "r").read()) + saveData = json.loads(open(save_path / "SaveData.txt", "r").read()) + + epoch = datetime(1, 1, 1) + save_time = epoch + timedelta(microseconds=saveInfo["dateAndTime"]["realTime"] / 10) + + return { + "Save Name": saveInfo["saveName"], + "Character Name": saveInfo["characterName"], + "Level": saveData["playerData"]["playerEntity"]["level"], + "Gender": saveData["playerData"]["playerEntity"]["gender"], + "Race": saveData["playerData"]["playerEntity"]["raceTemplate"]["Name"], + "Reflexes": saveData["playerData"]["playerEntity"]["reflexes"], + "Date": save_time.strftime("%Y-%m-%d %H:%M:%S"), + } + + class DaggerfallUnityGame(BasicGame): def init(self, organizer: mobase.IOrganizer) -> bool: super().init(organizer) self._register_feature(DaggerfallUnityModDataChecker()) + self._register_feature(BasicLocalSavegames(self)) + self._register_feature( + BasicGameSaveGameInfo( + lambda p: Path(p or "", "Screenshot.jpg"), + parse_daggerfall_save_metadata, + ) + ) return True Name = "Daggerfall Unity Support Plugin" - Author = "HomerSimpleton" - Version = "1.0.0" + Author = "HomerSimpleton & Oknehsorod" + Version = "1.0.1" GameName = "Daggerfall Unity" GameShortName = "daggerfallunity" @@ -54,3 +105,12 @@ def init(self, organizer: mobase.IOrganizer) -> bool: r"https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/" "Game:-Daggerfall-Unity" ) + GameSavesDirectory = ( + r"%USERPROFILE%/AppData/LocalLow/Daggerfall Workshop/Daggerfall Unity/Saves" + ) + + def listSaves(self, folder: QDir) -> list[mobase.ISaveGame]: + save_games = super().listSaves(folder) + path = Path(folder.absolutePath()) + save_games.extend(DaggerfallSaveGame(f) for f in path.glob("SAVE*")) + return save_games From 0b06410eed281ab66ef69d959f873a59c60db95f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 12:03:46 +0000 Subject: [PATCH 2/3] [pre-commit.ci] Auto fixes from pre-commit.com hooks. --- games/game_daggerfallunity.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/games/game_daggerfallunity.py b/games/game_daggerfallunity.py index 97d7853b..fe81b7f8 100644 --- a/games/game_daggerfallunity.py +++ b/games/game_daggerfallunity.py @@ -1,17 +1,17 @@ -import mobase import json from datetime import datetime, timedelta +from pathlib import Path from PyQt6.QtCore import QDir -from pathlib import Path +import mobase + +from ..basic_features import BasicLocalSavegames from ..basic_features.basic_save_game_info import ( BasicGameSaveGame, BasicGameSaveGameInfo, ) - from ..basic_game import BasicGame -from ..basic_features import BasicLocalSavegames class DaggerfallUnityModDataChecker(mobase.ModDataChecker): @@ -52,7 +52,7 @@ def __init__(self, filepath: Path): self.saveInfo = json.loads(open(self._filepath / "SaveInfo.txt", "r").read()) def getName(self) -> str: - return f'[{self.saveInfo["characterName"]}] {self.saveInfo["saveName"]}' + return f"[{self.saveInfo['characterName']}] {self.saveInfo['saveName']}" def getFilepath(self): return self._filepath.as_posix() From 6d4388984f619532c58d0788b3480c2fc36be641 Mon Sep 17 00:00:00 2001 From: Oknehsorod Date: Thu, 16 Jul 2026 16:39:03 +0300 Subject: [PATCH 3/3] feat: (DGU) Add save group identifier --- games/game_daggerfallunity.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/games/game_daggerfallunity.py b/games/game_daggerfallunity.py index 97d7853b..20c123d3 100644 --- a/games/game_daggerfallunity.py +++ b/games/game_daggerfallunity.py @@ -60,6 +60,9 @@ def getFilepath(self): def allFiles(self): return list(map(str, self._filepath.glob("*"))) + def getSaveGroupIdentifier(self): + return self.saveInfo["characterName"] + def parse_daggerfall_save_metadata(save_path: Path, save: mobase.ISaveGame): saveInfo = json.loads(open(save_path / "SaveInfo.txt", "r").read())