mirror of
https://github.com/c9moser/sgbackup.git
synced 2026-01-19 11:30:13 +00:00
2025.01.07 09:40:43
This commit is contained in:
parent
ab4b11f118
commit
ec55f3b8eb
@ -16,5 +16,51 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
###############################################################################
|
||||
|
||||
from gi.repository.GObject import (
|
||||
GObject,
|
||||
Property,
|
||||
Signal,
|
||||
SignalFlags,
|
||||
signal_accumulator_true_handled,
|
||||
)
|
||||
|
||||
from .game import Game
|
||||
|
||||
class Archiver:
|
||||
pass
|
||||
def __init__(self,key:str,name:str,extensions:list[str],decription:str|None=None):
|
||||
self.__key = key
|
||||
self.__name = name
|
||||
if decription:
|
||||
self.__description = decription
|
||||
else:
|
||||
self.__description = ""
|
||||
|
||||
@Property(type=str)
|
||||
def name(self)->str:
|
||||
return self.__name
|
||||
|
||||
@Property
|
||||
def key(self)->str:
|
||||
return self.__key
|
||||
|
||||
@Property
|
||||
def description(self)->str:
|
||||
return self.__description
|
||||
|
||||
def backup(self,game)->bool:
|
||||
pass
|
||||
|
||||
def restore(self,game,file)->bool:
|
||||
pass
|
||||
|
||||
@Signal(name="backup",flags=SignalFlags.RUN_FIRST,
|
||||
return_type=bool, arg_types=(GObject,str),
|
||||
accumulator=signal_accumulator_true_handled)
|
||||
def do_backup(self,game:Game,filename:str):
|
||||
pass
|
||||
|
||||
@Signal(name="restore",flags=SignalFlags.RUN_FIRST,
|
||||
return_type=bool,arg_types=(GObject,str),
|
||||
accumulator=signal_accumulator_true_handled)
|
||||
def do_backup(self,game:Game,filanme:str):
|
||||
pass
|
||||
492
sgbackup/game.py
492
sgbackup/game.py
@ -46,21 +46,71 @@ else:
|
||||
|
||||
|
||||
class SavegameType(StrEnum):
|
||||
"""
|
||||
SavegameType The savegame type for `Game` instance.
|
||||
|
||||
The SavegameType selects the `GameData` provider for the
|
||||
`Game` instance.
|
||||
"""
|
||||
|
||||
#: UNSET The SavegameType is unset
|
||||
UNSET = "unset"
|
||||
|
||||
#: OTHER Not listed game-provider.
|
||||
#:
|
||||
#: **Currently not supported!**
|
||||
OTHER = "other"
|
||||
|
||||
#: WINDOWS Native Windows game
|
||||
WINDOWS = "windows"
|
||||
|
||||
#: LINUX Native Linux game
|
||||
LINUX = "linux"
|
||||
|
||||
#: MACOS Native MacOS game
|
||||
MACOS = "macos"
|
||||
|
||||
#: STEAM_WINDOWS *Steam* for Windows
|
||||
STEAM_WINDOWS = "steam_windows"
|
||||
|
||||
#: STEAM_LINUX *Steam* for Linux
|
||||
STEAM_LINUX = "steam_linux"
|
||||
|
||||
#: STEAM_MACOS *Steam* for MacOS
|
||||
STEAM_MACOS = "steam_macos"
|
||||
|
||||
#: GOG WINDOWS *Good old Games* for Windows
|
||||
#:
|
||||
#: **Currently not supported!**
|
||||
GOG_WINDOWS = "gog_windows"
|
||||
|
||||
#: GOG_LINUX *Good old Games* for Linux
|
||||
#:
|
||||
#: **Currently not supported!**
|
||||
GOG_LINUX = "gog_linux"
|
||||
|
||||
#: EPIC_WINDOWS *Epic Games* for Windows
|
||||
#:
|
||||
#: **Currently not supported!**
|
||||
EPIC_WINDOWS = "epic_windows"
|
||||
|
||||
#: EPIC_LINUX *Epic Games* for Linux
|
||||
#:
|
||||
#: **Currently not supported!**
|
||||
EPIC_LINUX = "epic_linux"
|
||||
|
||||
@staticmethod
|
||||
def from_string(typestring:str):
|
||||
"""
|
||||
from_string Get SavegameType from string.
|
||||
|
||||
:param typestring: The string to parse
|
||||
:type typestring: str
|
||||
:return: The SavegameType if any. If no matching SavegameType is found,
|
||||
SavegameType.UNSET is returned.
|
||||
:rtype: SavegameType
|
||||
"""
|
||||
|
||||
st=SavegameType
|
||||
s=typestring.lower()
|
||||
if (s == 'other'):
|
||||
@ -71,31 +121,53 @@ class SavegameType(StrEnum):
|
||||
return st.LINUX
|
||||
elif (s == 'macos'):
|
||||
return st.MACOS
|
||||
elif (s == 'steam_windows' or s == 'steamwindows' or s == 'steam.windows'):
|
||||
elif (s == 'steam_windows' or s == 'steam-windows' or s == 'steamwindows' or s == 'steam.windows'):
|
||||
return st.STEAM_WINDOWS
|
||||
elif (s == 'steam_linux' or s == 'steamlinux' or s == 'steam.linux'):
|
||||
elif (s == 'steam_linux' or s == 'steam-linux' or s == 'steamlinux' or s == 'steam.linux'):
|
||||
return st.STEAM_LINUX
|
||||
elif (s == 'steam_macos' or s == 'steammacos' or s == 'steam.macos'):
|
||||
elif (s == 'steam_macos' or s == 'steam-macos' or s == 'steammacos' or s == 'steam.macos'):
|
||||
return st.STEAM_MACOS
|
||||
elif (s == 'gog_winows' or s == 'gogwindows' or s == 'gog.windows'):
|
||||
elif (s == 'gog_winows' or s == 'gog-windows' or s == 'gogwindows' or s == 'gog.windows'):
|
||||
return st.GOG_WINDOWS
|
||||
elif (s == 'gog_linux' or s == 'goglinux' or s == 'gog.linux'):
|
||||
elif (s == 'gog_linux' or s == 'gog-linux' or s == 'goglinux' or s == 'gog.linux'):
|
||||
return st.GOG_LINUX
|
||||
elif (s == 'epic_windows' or s == 'epicwindows' or s == 'epic.windows'):
|
||||
elif (s == 'epic_windows' or s == 'epic-windows' or s == 'epicwindows' or s == 'epic.windows'):
|
||||
return st.EPIC_WINDOWS
|
||||
elif (s == 'epic_linux' or s == 'epiclinux' or s == 'epic.linux'):
|
||||
elif (s == 'epic_linux' or s == 'epic-linux' or s == 'epiclinux' or s == 'epic.linux'):
|
||||
return st.EPIC_LINUX
|
||||
|
||||
return st.UNSET
|
||||
|
||||
|
||||
class GameFileType(StrEnum):
|
||||
"""
|
||||
GameFileType The file matcher type for `GameFileMatcher`.
|
||||
|
||||
The path to be matched is originating from *${SAVEGAME_ROOT}/${SAVEGAME_DIR}*.
|
||||
"""
|
||||
|
||||
#: GLOB Glob matching.
|
||||
GLOB = "glob"
|
||||
|
||||
#: REGEX Regex file matching
|
||||
REGEX = "regex"
|
||||
|
||||
#: FILENAME Filename matching.
|
||||
FILENAME = "filename"
|
||||
|
||||
@staticmethod
|
||||
def from_string(typestring:str):
|
||||
"""
|
||||
from_string Get the `GameFileType` from a string.
|
||||
|
||||
If an illegal string-value is given this method raises a `ValueError`.
|
||||
|
||||
:param typestring: The string to be used.
|
||||
:type typestring: str
|
||||
:raises ValueError: If an illegal string is given.
|
||||
:return: The corresponding Enum-value
|
||||
:rtype: GameFileType
|
||||
"""
|
||||
s = typestring.lower()
|
||||
if (s == 'glob'):
|
||||
return GameFileType.GLOB
|
||||
@ -104,9 +176,12 @@ class GameFileType(StrEnum):
|
||||
elif (s == 'filename'):
|
||||
return GameFileType.FILENAME
|
||||
|
||||
raise ValueError("Unknown GameFileType \"{}\"!".fomrat(typestring))
|
||||
raise ValueError("Unknown GameFileType \"{}\"!".format(typestring))
|
||||
|
||||
class GameFileMatcher(GObject):
|
||||
"""
|
||||
GameFileMatcher Match savegame files if they are to be included in the backup.
|
||||
"""
|
||||
__gtype_name__ = "GameFileMatcher"
|
||||
|
||||
def __init__(self,match_type:GameFileType,match_file:str):
|
||||
@ -116,7 +191,14 @@ class GameFileMatcher(GObject):
|
||||
|
||||
@Property
|
||||
def match_type(self)->GameFileType:
|
||||
"""
|
||||
match_type The type of the matcher.
|
||||
|
||||
:type: GameFileType
|
||||
"""
|
||||
|
||||
return self.__match_type
|
||||
|
||||
@match_type.setter
|
||||
def match_type(self,type:GameFileType):
|
||||
if not isinstance(type,GameFileType):
|
||||
@ -125,15 +207,27 @@ class GameFileMatcher(GObject):
|
||||
|
||||
@Property(type=str)
|
||||
def match_file(self)->str:
|
||||
"""
|
||||
match_file The matcher value.
|
||||
|
||||
:type: str
|
||||
"""
|
||||
return self.__match_file
|
||||
@match_file.setter
|
||||
def match_file(self,file:str):
|
||||
self.__match_file = file
|
||||
self.__match_file = file
|
||||
|
||||
## @}
|
||||
|
||||
def match(self,rel_filename:str):
|
||||
def match_glob(filename):
|
||||
def match(self,rel_filename:str)->bool:
|
||||
"""
|
||||
match Match the file.
|
||||
|
||||
:param rel_filename: The relative filename originating from
|
||||
*${SAVEGAME_ROOT}/${SAVEGAME_DIR}*.
|
||||
:type rel_filename: str
|
||||
:rtype: bool
|
||||
:returns: True if file matches
|
||||
"""
|
||||
def match_glob(filename)->bool:
|
||||
return fnmatch.fnmatch(filename,self.match_file)
|
||||
# match_glob()
|
||||
|
||||
@ -170,8 +264,7 @@ class GameData(GObject):
|
||||
__gtype_name__ = 'GameData'
|
||||
|
||||
"""
|
||||
:class: GameData
|
||||
:brief: Base class for platform specific data.
|
||||
:class: GameData Base class for savegame specific data data.
|
||||
"""
|
||||
def __init__(self,
|
||||
savegame_type:SavegameType,
|
||||
@ -185,8 +278,8 @@ class GameData(GObject):
|
||||
self.__savegame_root = savegame_root
|
||||
self.__savegame_dir = savegame_dir
|
||||
self.__variables = {}
|
||||
self.__filematch = []
|
||||
self.__ignorematch = []
|
||||
self.__filematchers = []
|
||||
self.__ignorematchers = []
|
||||
|
||||
if variables is not None:
|
||||
variables.update(variables)
|
||||
@ -202,15 +295,14 @@ class GameData(GObject):
|
||||
@Property
|
||||
def savegame_type(self)->SavegameType:
|
||||
"""
|
||||
:attr: savegame_type
|
||||
:brief: Type of the class.
|
||||
:type: SavegameType
|
||||
"""
|
||||
return self.__savegame_type
|
||||
|
||||
@Property(type=str)
|
||||
def savegame_root(self)->str:
|
||||
"""
|
||||
:attr: savegame_root
|
||||
:type: str
|
||||
"""
|
||||
return self.__savegame_root
|
||||
|
||||
@ -221,7 +313,7 @@ class GameData(GObject):
|
||||
@Property
|
||||
def savegame_dir(self)->str:
|
||||
"""
|
||||
:attr: savegame_dir
|
||||
:type: str
|
||||
"""
|
||||
return self.__savegame_dir
|
||||
|
||||
@ -230,7 +322,10 @@ class GameData(GObject):
|
||||
self.__savegame_dir = sgdir
|
||||
|
||||
@Property
|
||||
def variables(self)->dict:
|
||||
def variables(self)->dict[str:str]:
|
||||
"""
|
||||
:type: dict[str:str]
|
||||
"""
|
||||
return self.__variables
|
||||
@variables.setter
|
||||
def variables(self,vars:dict|None):
|
||||
@ -240,109 +335,225 @@ class GameData(GObject):
|
||||
self.__variables = dict(vars)
|
||||
|
||||
@Property
|
||||
def file_match(self):
|
||||
return self.__filematch
|
||||
@file_match.setter
|
||||
def file_match(self,fm:list[GameFileMatcher]|None):
|
||||
def file_matchers(self)->list[GameFileMatcher]:
|
||||
"""
|
||||
:type: list[GameFileMatcher]
|
||||
"""
|
||||
return self.__filematchers
|
||||
|
||||
@file_matchers.setter
|
||||
def file_matchers(self,fm:list[GameFileMatcher]|None):
|
||||
if not fm:
|
||||
self.__filematch = []
|
||||
self.__filematchers = []
|
||||
else:
|
||||
for matcher in fm:
|
||||
if not isinstance(matcher,GameFileMatcher):
|
||||
raise TypeError("\"file_match\" needs to be \"None\" or a list of \"GameFileMatcher\" instances!")
|
||||
self.__filematch = list(fm)
|
||||
self.__filematchers = list(fm)
|
||||
|
||||
|
||||
@Property
|
||||
def ignore_match(self):
|
||||
return self.__ignorematch
|
||||
@file_match.setter
|
||||
def file_match(self,im:list[GameFileMatcher]|None):
|
||||
def ignore_matchers(self)->list[GameFileMatcher]:
|
||||
"""
|
||||
:type: list[GameFileMatcher]
|
||||
"""
|
||||
return self.__ignorematchers
|
||||
@ignore_matchers.setter
|
||||
def ignore_matchers(self,im:list[GameFileMatcher]|None):
|
||||
if not im:
|
||||
self.__ignorematch = []
|
||||
self.__ignorematchers = []
|
||||
else:
|
||||
for matcher in im:
|
||||
if not isinstance(matcher,GameFileMatcher):
|
||||
raise TypeError("\"ignore_match\" needs to be \"None\" or a list of \"GameFileMatcher\" instances!")
|
||||
self.__ignorematch = list(im)
|
||||
self.__ignorematchers = list(im)
|
||||
|
||||
def has_variable(self,name:str)->bool:
|
||||
"""
|
||||
has_variable Check if variable exists.
|
||||
|
||||
:param name: The variable name.
|
||||
:type name: str
|
||||
:return: `True` if the variable exists.
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
return (name in self.__variables)
|
||||
|
||||
def get_variable(self,name:str)->str:
|
||||
"""
|
||||
get_variable Get a variable value byy variable name.
|
||||
|
||||
:param name: The variable name
|
||||
:type name: str
|
||||
:return: The vairable value if the variable exists or an empty string.
|
||||
:rtype: str
|
||||
"""
|
||||
if name not in self.__variables:
|
||||
return ""
|
||||
return self.__variables[name]
|
||||
|
||||
def set_variable(self,name:str,value:str):
|
||||
"""
|
||||
set_variable Set a variable.
|
||||
|
||||
If the variable exists, it is replaced by the new variable.
|
||||
|
||||
:param name: The variable name.
|
||||
:type name: str
|
||||
:param value: The variable value.
|
||||
:type value: str
|
||||
"""
|
||||
self.__variables[name] = value
|
||||
|
||||
def delete_variable(self,name:str):
|
||||
"""
|
||||
delete_variable Deletes as variable if the variable exists
|
||||
|
||||
:param name: The vairable name to delete.
|
||||
:type name: str
|
||||
"""
|
||||
if name in self.__variables:
|
||||
del self.__variables[name]
|
||||
|
||||
def get_variables(self):
|
||||
def get_variables(self)->dict[str:str]:
|
||||
"""
|
||||
get_variables Get the variables set by this instance.
|
||||
|
||||
:return: The variables as a dict.
|
||||
:rtype: dict[str:str]
|
||||
"""
|
||||
return self.variables
|
||||
|
||||
def match_file(self,rel_filename:str):
|
||||
if not self.__filematch:
|
||||
def match_file(self,rel_filename:str)->bool:
|
||||
"""
|
||||
match_file Matches a file with the `GameFileMatcher`s for this class.
|
||||
|
||||
This method returns `True` if there is no `GameFileMatcher` set for
|
||||
`GameData.file_match`.
|
||||
|
||||
:param rel_filename: The relative filename originating from *$SAVEGAME_DIR*
|
||||
:type rel_filename: str
|
||||
:return: `True` if the file matches.
|
||||
:rtype: bool
|
||||
"""
|
||||
if not self.file_matchers:
|
||||
return True
|
||||
|
||||
for fm in self.__filematch:
|
||||
for fm in self.file_matchers:
|
||||
if fm.match(rel_filename):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def match_ignore(self,rel_filename:str):
|
||||
if not self.__ignorematch:
|
||||
def match_ignore(self,rel_filename:str)->bool:
|
||||
"""
|
||||
match_ignore Matches file agains the ignore_match `GameFileMatcher`s.
|
||||
|
||||
This method returns `False` if there is no `GameFileMatcher` set in
|
||||
`GameData.ignore_match`.
|
||||
|
||||
:param rel_filename: The relative filename originating from *$SAVEGAME_DIR*
|
||||
:type rel_filename: str
|
||||
:return: `True` if the file matches.
|
||||
:rtype: bool
|
||||
"""
|
||||
if not self.ignore_matchers:
|
||||
return False
|
||||
|
||||
for fm in self.__ignorematch:
|
||||
for fm in self.ignore_matchers:
|
||||
if fm.match(rel_filename):
|
||||
return True
|
||||
return False
|
||||
|
||||
def match(self,rel_filename:str):
|
||||
def match(self,rel_filename:str)->bool:
|
||||
"""
|
||||
match Match files against `file_match` and `ignore_match`.
|
||||
|
||||
If this method returns `True` the file should be included in the
|
||||
savegame backup.
|
||||
|
||||
:param rel_filename: The relative filename originating from *$SAVEGAME_DIR*
|
||||
:type rel_filename: str
|
||||
:return: True if the file should be included in the savegame backup.
|
||||
:rtype: bool
|
||||
"""
|
||||
if self.match_file(rel_filename) and not self.match_ignore(rel_filename):
|
||||
return True
|
||||
return False
|
||||
|
||||
def add_file_match(self,matcher:GameFileMatcher):
|
||||
"""
|
||||
add_file_match Add a `GameFileMatcher` to `file_match`.
|
||||
|
||||
:param matcher: The `GameFileMatcher` to add.
|
||||
:type matcher: GameFileMatcher
|
||||
:raises TypeError: If the `matcher` is not a `GameFileMatcher` instance.
|
||||
"""
|
||||
if not isinstance(matcher,GameFileMatcher):
|
||||
raise TypeError("matcher is not a \"GameFileMatcher\" instance!")
|
||||
self.__filematch.append(matcher)
|
||||
self.__filematchers.append(matcher)
|
||||
|
||||
def remove_file_match(self,matcher:GameFileMatcher):
|
||||
for i in reversed(range(len(self.__filematch))):
|
||||
if (matcher == self.__filematch[i]):
|
||||
del self.__filematch[i]
|
||||
"""
|
||||
remove_file_match Remove a file_matcher.
|
||||
|
||||
:param matcher: The `GameFileMatcher` to remove.
|
||||
:type matcher: GameFileMatcher
|
||||
"""
|
||||
for i in reversed(range(len(self.__filematchers))):
|
||||
if (matcher == self.__filematchers[i]):
|
||||
del self.__filematchers[i]
|
||||
|
||||
def add_ignore_match(self,matcher:GameFileMatcher):
|
||||
"""
|
||||
add_file_match Add a `GameFileMatcher` to `ignore_match`.
|
||||
|
||||
:param matcher: The `GameFileMatcher` to add.
|
||||
:type matcher: GameFileMatcher
|
||||
:raises TypeError: If the `matcher` is not a `GameFileMatcher` instance.
|
||||
"""
|
||||
if not isinstance(matcher,GameFileMatcher):
|
||||
raise TypeError("matcher is not a \"GameFileMatcher\" instance!")
|
||||
self.__ignorematch.append(matcher)
|
||||
self.__ignorematchers.append(matcher)
|
||||
|
||||
def remove_ignore_match(self,matcher:GameFileMatcher):
|
||||
for i in reversed(range(len(self.__ignorematch))):
|
||||
if (matcher == self.__ignorematch[i]):
|
||||
del self.__ignorematch[i]
|
||||
"""
|
||||
remove_file_match Remove a ignore_match.
|
||||
|
||||
:param matcher: The `GameFileMatcher` to remove.
|
||||
:type matcher: GameFileMatcher
|
||||
"""
|
||||
|
||||
for i in reversed(range(len(self.__ignorematchers))):
|
||||
if (matcher == self.__ignorematchers[i]):
|
||||
del self.__ignorematchers[i]
|
||||
|
||||
def serialize(self)->dict:
|
||||
"""
|
||||
serialize Serialize the instance to a dict.
|
||||
|
||||
This method should be overloaded by child-classes, so that their data
|
||||
is exported too.
|
||||
|
||||
:return: The dict holding the data for recreating an instance of this class.
|
||||
:rtype: dict
|
||||
"""
|
||||
ret = {
|
||||
'savegame_root': self.savegame_root,
|
||||
'savegame_dir': self.savegame_dir,
|
||||
}
|
||||
if (self.__variables):
|
||||
ret['variables'] = self.variables
|
||||
if (self.file_match):
|
||||
if (self.file_matchers):
|
||||
fm = []
|
||||
for matcher in self.file_match:
|
||||
for matcher in self.file_matchers:
|
||||
fm.append({'type':matcher.match_type.value,'match':matcher.match_file})
|
||||
ret['file_match'] = fm
|
||||
|
||||
if (self.add_ignore_match):
|
||||
if (self.ignore_matchers):
|
||||
im = []
|
||||
for matcher in self.ignore_match:
|
||||
for matcher in self.ignore_matchers:
|
||||
im.append({'type':matcher.match_type.value,'match':matcher.match_file})
|
||||
ret['ignore_match'] = im
|
||||
|
||||
@ -978,7 +1189,19 @@ class Game(GObject):
|
||||
if not isinstance(data,SteamMacOSGame):
|
||||
raise TypeError("SteamWindowsGame")
|
||||
self.__steam_macos = data
|
||||
|
||||
|
||||
@Property
|
||||
def savegame_root(self)->str|None:
|
||||
if not self.game_data:
|
||||
return None
|
||||
return self.game_data.savegame_root
|
||||
|
||||
@Property
|
||||
def savegame_dir(self)->str|None:
|
||||
if not self.game_data:
|
||||
return None
|
||||
return self.game_data.savegame_dir
|
||||
|
||||
def add_variable(self,name:str,value:str):
|
||||
self.__variables[str(name)] = str(value)
|
||||
|
||||
@ -1038,58 +1261,121 @@ class Game(GObject):
|
||||
|
||||
with open(new_path,'wt',encoding='utf-8') as ofile:
|
||||
ofile.write(json.dumps(self.serialize(),ensure_ascii=False,indent=4))
|
||||
|
||||
|
||||
def __bool__(self):
|
||||
return (bool(self.game_data) and bool(self.savegame_root) and bool(self.savegame_dir))
|
||||
|
||||
def is_backup_file(self,filename:str):
|
||||
pass
|
||||
|
||||
def get_backup_files(self)->dict[str:str]|None:
|
||||
def get_backup_files_recursive(sgroot:pathlib.Path,sgdir:str,subdir:str|None=None):
|
||||
ret = {}
|
||||
if subdir:
|
||||
path = sgroot / sgdir / subdir
|
||||
else:
|
||||
path = sgroot / sgdir
|
||||
|
||||
ret = {}
|
||||
for dirent in os.listdir(path):
|
||||
file_path = path / dirent
|
||||
if file_path.is_file():
|
||||
if subdir:
|
||||
fname = (os.path.join(subdir,dirent))
|
||||
else:
|
||||
fname = dirent
|
||||
|
||||
if self.game_data.match(fname):
|
||||
ret[str(path)] = os.path.join(sgdir,fname)
|
||||
elif file_path.is_dir():
|
||||
ret.update(get_backup_files_recursive(sgroot,sgdir,os.path.join(subdir,dirent)))
|
||||
|
||||
return ret
|
||||
|
||||
if not bool(self):
|
||||
return None
|
||||
|
||||
sgroot = pathlib.Path(self.savegame_root).resolve()
|
||||
sgdir = self.savegame_dir
|
||||
sgpath = sgroot / sgdir
|
||||
if not os.path.exists(sgpath):
|
||||
return None
|
||||
|
||||
backup_files = get_backup_files_recursive(sgroot,sgdir)
|
||||
|
||||
GAMES={}
|
||||
STEAM_GAMES={}
|
||||
STEAM_LINUX_GAMES={}
|
||||
STEAM_WINDOWS_GAMES={}
|
||||
STEAM_MACOS_GAMES={}
|
||||
|
||||
def __init_games():
|
||||
gameconf_dir = settings.gameconf_dir
|
||||
if not os.path.isdir(gameconf_dir):
|
||||
return
|
||||
|
||||
for gcf in (os.path.join(gameconf_dir,i) for i in os.listdir(gameconf_dir)):
|
||||
if not os.path.isfile(gcf) or not gcf.endswith('.gameconf'):
|
||||
continue
|
||||
class GameManager(GObject):
|
||||
__global_gamemanager = None
|
||||
|
||||
@staticmethod
|
||||
def get_global():
|
||||
if GameManager.__global_gamemanager is None:
|
||||
GameManager.__global_gamemanager = GameManager()
|
||||
return GameManager.__global_gamemanager
|
||||
|
||||
def __init__(self):
|
||||
GObject.__init__(self)
|
||||
|
||||
self.__games = {}
|
||||
self.__steam_games = {}
|
||||
self.__steam_linux_games = {}
|
||||
self.__steam_windows_games = {}
|
||||
self.__steam_macos_games = {}
|
||||
|
||||
self.load()
|
||||
|
||||
@Property(type=object)
|
||||
def games(self)->dict[str:Game]:
|
||||
return self.__games
|
||||
|
||||
@Property(type=object)
|
||||
def stam_games(self)->dict[int:Game]:
|
||||
return self.__steam_games
|
||||
|
||||
@Property(type=object)
|
||||
def steam_windows_games(self)->dict[int:Game]:
|
||||
return self.__steam_windows_games
|
||||
|
||||
@Property(type=object)
|
||||
def steam_linux_games(self)->dict[int:Game]:
|
||||
return self.__steam_linux_games
|
||||
|
||||
@Property(type=object)
|
||||
def steam_macos_games(self)->dict[int:Game]:
|
||||
return self.__steam_macos_games
|
||||
|
||||
def load(self):
|
||||
if self.__games:
|
||||
self.__games = {}
|
||||
|
||||
try:
|
||||
game = Game.new_from_json_file(gcf)
|
||||
if not game:
|
||||
gameconf_dir = settings.gameconf_dir
|
||||
if not os.path.isdir(gameconf_dir):
|
||||
return
|
||||
|
||||
for gcf in (os.path.join(gameconf_dir,i) for i in os.listdir(gameconf_dir)):
|
||||
if not os.path.isfile(gcf) or not gcf.endswith('.gameconf'):
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
|
||||
try:
|
||||
game = Game.new_from_json_file(gcf)
|
||||
if not game:
|
||||
continue
|
||||
except Exception as ex:
|
||||
logger.error("Unable to load gameconf {gameconf}! ({what})".format(
|
||||
gameconf = os.path.basename(gcf),
|
||||
what = str(ex)))
|
||||
continue
|
||||
|
||||
self.add_game(game)
|
||||
|
||||
GAMES[game.key] = game
|
||||
if (game.steam_windows):
|
||||
if not game.steam_windows.appid in STEAM_GAMES:
|
||||
STEAM_GAMES[game.steam_windows.appid] = game
|
||||
STEAM_WINDOWS_GAMES[game.steam_windows.appid] = game
|
||||
if (game.steam_linux):
|
||||
if not game.steam_linux.appid in STEAM_GAMES:
|
||||
STEAM_GAMES[game.steam_linux.appid] = game
|
||||
STEAM_LINUX_GAMES[game.steam_linux.appid] = game
|
||||
def add_game(self,game:Game):
|
||||
self.__[game.key] = game
|
||||
if (game.steam_macos):
|
||||
if not game.steam_macos.appid in STEAM_GAMES:
|
||||
STEAM_GAMES[game.steam_macos.appid] = game
|
||||
STEAM_MACOS_GAMES[game.steam_macos.appid] = game
|
||||
__init_games()
|
||||
|
||||
def add_game(game:Game):
|
||||
GAMES[game.key] = game
|
||||
if game.steam_windows:
|
||||
if not game.steam_windows.appid in STEAM_GAMES:
|
||||
STEAM_GAMES[game.steam_windows.appid] = game
|
||||
STEAM_WINDOWS_GAMES[game.steam_windows.appid] = game
|
||||
if (game.steam_linux):
|
||||
if not game.steam_linux.appid in STEAM_GAMES:
|
||||
STEAM_GAMES[game.steam_linux.appid] = game
|
||||
STEAM_LINUX_GAMES[game.steam_linux.appid] = game
|
||||
if (game.steam_macos):
|
||||
if not game.steam_macos.appid in STEAM_GAMES:
|
||||
STEAM_GAMES[game.steam_macos.appid] = game
|
||||
STEAM_MACOS_GAMES[game.steam_macos.appid] = game
|
||||
|
||||
self.__steam_games[game.steam_macos.appid] = game
|
||||
self.__steam_macos_games[game.steam_macos.appid] = game
|
||||
if (game.steam_linux):
|
||||
self.__steam_games[game.steam_linux.appid] = game
|
||||
self.__steam_linux_games[game.steam_linux.appid] = game
|
||||
if (game.steam_windows):
|
||||
self.__steam_games[game.steam_windows.appid] = game
|
||||
self.__steam_windows_games[game.steam_windows.appid] = game
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
###############################################################################
|
||||
|
||||
from ._app import Application,AppWindow
|
||||
from ._app import Application,AppWindow,GameView,BackupView
|
||||
from ._settingsdialog import SettingsDialog
|
||||
from ._gamedialog import GameDialog
|
||||
|
||||
|
||||
@ -28,11 +28,19 @@ from .. import game
|
||||
from ..settings import settings
|
||||
from ._settingsdialog import SettingsDialog
|
||||
from ._gamedialog import GameDialog
|
||||
from ..game import Game
|
||||
|
||||
__gtype_name__ = __name__
|
||||
|
||||
class GameView(Gtk.ScrolledWindow):
|
||||
__gtype_name__ = "sgbackup-gui-GameView"
|
||||
__gtype_name__ = "GameView"
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
GameView The View for games.
|
||||
|
||||
This is widget presents a clumnview for the installed games.
|
||||
"""
|
||||
Gtk.ScrolledWindow.__init__(self)
|
||||
|
||||
self.__liststore = Gio.ListStore.new(game.Game)
|
||||
@ -78,7 +86,7 @@ class GameView(Gtk.ScrolledWindow):
|
||||
return self.__liststore
|
||||
|
||||
@property
|
||||
def _columnview(self):
|
||||
def _columnview(self)->Gtk.ColumnView:
|
||||
return self.__columnview
|
||||
|
||||
def _on_key_column_setup(self,factory,item):
|
||||
@ -149,6 +157,15 @@ class GameView(Gtk.ScrolledWindow):
|
||||
dialog.props.secondary_use_markup = False
|
||||
dialog.connect('response',on_dialog_response)
|
||||
dialog.present()
|
||||
|
||||
@property
|
||||
def current_game(self)->Game|None:
|
||||
selection = self._columnview.get_model()
|
||||
pos = selection.get_selected()
|
||||
if pos == Gtk.INVALID_LIST_POSITION:
|
||||
return None
|
||||
return selection.get_model().get_item(pos)
|
||||
|
||||
# GameView class
|
||||
|
||||
class BackupViewData(GObject.GObject):
|
||||
@ -188,6 +205,9 @@ class BackupViewData(GObject.GObject):
|
||||
@GObject.Property
|
||||
def timestamp(self):
|
||||
return self.__timestamp
|
||||
|
||||
def _on_selection_changed(self,selection):
|
||||
pass
|
||||
|
||||
class BackupView(Gtk.ScrolledWindow):
|
||||
__gtype_name__ = "BackupView"
|
||||
@ -443,6 +463,6 @@ class Application(Gtk.Application):
|
||||
flags=GObject.SignalFlags.RUN_LAST,
|
||||
return_type=None,
|
||||
arg_types=(SettingsDialog,))
|
||||
def settings_dialog_init(self,dialog):
|
||||
def do_settings_dialog_init(self,dialog):
|
||||
pass
|
||||
|
||||
|
||||
@ -28,11 +28,32 @@ from ..game import (
|
||||
SteamLinuxGame,
|
||||
SteamWindowsGame,
|
||||
SteamMacOSGame,
|
||||
GameManager,
|
||||
)
|
||||
|
||||
|
||||
class GameVariableData(GObject.GObject):
|
||||
"""
|
||||
GameVariableData The Gio.ListStore data for Variables.
|
||||
"""
|
||||
|
||||
def __init__(self,name:str,value:str):
|
||||
"""
|
||||
GameVariableData
|
||||
|
||||
:param name: The variable name
|
||||
:type name: str
|
||||
:param value: The variable value
|
||||
:type value: str
|
||||
|
||||
Properties
|
||||
__________
|
||||
.. py:property:: name
|
||||
:type: str
|
||||
|
||||
.. py:property:: value
|
||||
:type: str
|
||||
"""
|
||||
GObject.GObject.__init__(self)
|
||||
self.name = name
|
||||
self.value = value
|
||||
@ -52,13 +73,27 @@ class GameVariableData(GObject.GObject):
|
||||
self.__value = value
|
||||
|
||||
class RegistryKeyData(GObject.GObject):
|
||||
def __init__(self,regkey=None):
|
||||
"""
|
||||
RegistyKeyData The data for Windows registry keys.
|
||||
"""
|
||||
def __init__(self,regkey:str|None=None):
|
||||
"""
|
||||
RegistryKeyData
|
||||
|
||||
:param regkey: The registry key ot set, defaults to None
|
||||
:type regkey: str | None, optional
|
||||
|
||||
Properties
|
||||
__________
|
||||
.. py:property:: regkey
|
||||
:type: str
|
||||
"""
|
||||
GObject.GObject.__init__(self)
|
||||
if not regkey:
|
||||
self.__regkey = ""
|
||||
|
||||
@GObject.Property(type=str)
|
||||
def regkey(self):
|
||||
def regkey(self)->str:
|
||||
return self.__regkey
|
||||
@regkey.setter
|
||||
def regkey(self,key:str):
|
||||
@ -67,14 +102,35 @@ class RegistryKeyData(GObject.GObject):
|
||||
def __bool__(self):
|
||||
return bool(self.__regkey)
|
||||
|
||||
|
||||
class GameFileMatcherData(GObject.GObject):
|
||||
"""
|
||||
GameFileMatcherData The data for the file matcher.
|
||||
"""
|
||||
def __init__(self,match_type:GameFileType,match_value:str):
|
||||
"""
|
||||
GameFileMatcherData
|
||||
|
||||
:param match_type: The type of the game file matcher.
|
||||
:type match_type: GameFileType
|
||||
:param match_value: The value to match the file.
|
||||
:type match_value: str
|
||||
|
||||
Properties
|
||||
__________
|
||||
.. py:property:: match_type
|
||||
:type: GameFileType
|
||||
|
||||
.. py:property:: match_value
|
||||
:type: str
|
||||
"""
|
||||
GObject.GObject.__init__(self)
|
||||
self.match_type = match_type
|
||||
self.match_value = match_value
|
||||
|
||||
@GObject.Property
|
||||
def match_type(self)->GameFileType:
|
||||
|
||||
return self.__match_type
|
||||
@match_type.setter
|
||||
def match_type(self,type:GameFileType):
|
||||
@ -83,12 +139,30 @@ class GameFileMatcherData(GObject.GObject):
|
||||
@GObject.Property(type=str)
|
||||
def match_value(self)->str:
|
||||
return self.__match_value
|
||||
|
||||
@match_value.setter
|
||||
def match_value(self,value:str):
|
||||
self.__match_value = value
|
||||
|
||||
class GameFileTypeData(GObject.GObject):
|
||||
""" GameFileTypeData The *Gio.Liststore* data for GameFileType *Gtk.DropDown* widgets."""
|
||||
def __init__(self,match_type:GameFileType,name:str):
|
||||
"""
|
||||
GameFileTypeData
|
||||
|
||||
:param match_type: The matcher type
|
||||
:type match_type: GameFileType
|
||||
:param name: The name of the matcher type
|
||||
:type name: str
|
||||
|
||||
Properties:
|
||||
___________
|
||||
.. py:property:: match_type
|
||||
:type: GameFileType
|
||||
|
||||
.. py:property:: name
|
||||
:type: str
|
||||
"""
|
||||
GObject.GObject.__init__(self)
|
||||
self.__match_type = match_type
|
||||
self.__name = name
|
||||
@ -102,7 +176,34 @@ class GameFileTypeData(GObject.GObject):
|
||||
return self.__name
|
||||
|
||||
class SavegameTypeData(GObject.GObject):
|
||||
"""
|
||||
SavegameTypeData Holds the data for the SavegameType *Gtk.DropDown*.
|
||||
"""
|
||||
|
||||
def __init__(self,type:SavegameType,name:str,icon_name:str):
|
||||
"""
|
||||
SavegameTypeData
|
||||
|
||||
:param type: The SavegameType to select.
|
||||
:type type: SavegameType
|
||||
:param name: The name of the SavegameType.
|
||||
:type name: str
|
||||
:param icon_name: The Icon name to display for the SavegameType
|
||||
:type icon_name: str
|
||||
|
||||
Properties
|
||||
__________
|
||||
|
||||
.. py:property:: savegame_type
|
||||
:type: SavegameType
|
||||
|
||||
.. py:property:: name
|
||||
:type: str
|
||||
|
||||
.. py:property:: icon_name
|
||||
:type: str
|
||||
|
||||
"""
|
||||
GObject.GObject.__init__(self)
|
||||
self.__sgtype = type
|
||||
self.__name = name
|
||||
@ -122,7 +223,27 @@ class SavegameTypeData(GObject.GObject):
|
||||
|
||||
|
||||
class GameVariableDialog(Gtk.Dialog):
|
||||
"""
|
||||
GameVariableDialog The dialog for setting game variables.
|
||||
|
||||
It is bound to on the GameDialog variable columnviews. This dialog
|
||||
will update the given columnview automatically if the response is
|
||||
*Gtk.Response.APPLY* and destroy itself on any response.
|
||||
|
||||
If not variable is given, this dialog will create a new one
|
||||
|
||||
"""
|
||||
def __init__(self,parent:Gtk.Window,columnview:Gtk.ColumnView,variable:GameVariableData|None=None):
|
||||
"""
|
||||
GameVariableDialog
|
||||
|
||||
:param parent: The parent window (should be a GameDialog instance).
|
||||
:type parent: Gtk.Window
|
||||
:param columnview: The Columnview to operate on.
|
||||
:type columnview: Gtk.ColumnView
|
||||
:param variable: The variable to edit, defaults to None
|
||||
:type variable: GameVariableData | None, optional
|
||||
"""
|
||||
Gtk.Dialog.__init__(self)
|
||||
self.set_transient_for(parent)
|
||||
self.set_default_size(600,-1)
|
||||
@ -186,14 +307,24 @@ class GameVariableDialog(Gtk.Dialog):
|
||||
model = self.__columnview.get_model().get_model()
|
||||
model.append(GameVariableData(self.__name_entry.get_text(),self.__value_entry.get_text()))
|
||||
self.hide()
|
||||
self.destroy()
|
||||
self.destroy()
|
||||
|
||||
|
||||
|
||||
class GameDialog(Gtk.Dialog):
|
||||
def __init__(self,
|
||||
parent:Gtk.Window|None=None,
|
||||
game:Game|None=Game):
|
||||
game:Game|None=None):
|
||||
"""
|
||||
GameDialog This dialog is for setting game config.
|
||||
|
||||
The dialog automatically saves the game.
|
||||
|
||||
:param parent: The parent Window, defaults to None
|
||||
:type parent: Gtk.Window | None, optional
|
||||
:param game: The game to configure, defaults to None
|
||||
:type game: Game | None, optional
|
||||
"""
|
||||
|
||||
Gtk.Dialog.__init__(self)
|
||||
if (parent):
|
||||
@ -685,6 +816,9 @@ class GameDialog(Gtk.Dialog):
|
||||
return widget
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
reset Resets the dialog to the Game set on init or clears the dialog if no Game was set.
|
||||
"""
|
||||
self.__active_switch.set_active(True)
|
||||
self.__live_switch.set_active(True)
|
||||
self.__name_entry.set_text("")
|
||||
@ -758,11 +892,11 @@ class GameDialog(Gtk.Dialog):
|
||||
|
||||
#filematch
|
||||
fm_model = self.__windows.filematch.columnview.get_model().get_model()
|
||||
for fm in self.__game.windows.filematch:
|
||||
for fm in self.__game.windows.file_matchers:
|
||||
fm_model.append(GameFileMatcherData(fm.match_type,fm.match_file))
|
||||
|
||||
im_model = self.__windows.ignorematch.columnview.get_model().get_model()
|
||||
for im in self.__game.windows.ignorematch:
|
||||
for im in self.__game.windows.ignore_matchers:
|
||||
im_model.append(GameFileMatcherData(im.match_type,im.match_file))
|
||||
|
||||
# set lookup regkeys
|
||||
@ -787,11 +921,11 @@ class GameDialog(Gtk.Dialog):
|
||||
|
||||
#filematch
|
||||
fm_model = self.__linux.filematch.columnview.get_model().get_model()
|
||||
for fm in self.__game.linux.filematch:
|
||||
for fm in self.__game.linux.file_matchers:
|
||||
fm_model.append(GameFileMatcherData(fm.match_type,fm.match_file))
|
||||
|
||||
im_model = self.__linux.ignorematch.columnview.get_model().get_model()
|
||||
for im in self.__game.linux.ignorematch:
|
||||
for im in self.__game.linux.ignore_matchers:
|
||||
im_model.append(GameFileMatcherData(im.match_type,im.match_file))
|
||||
|
||||
var_model = self.__linux.variables.columnview.get_model().get_model()
|
||||
@ -805,11 +939,11 @@ class GameDialog(Gtk.Dialog):
|
||||
|
||||
#filematch
|
||||
fm_model = self.__macos.filematch.columnview.get_model().get_model()
|
||||
for fm in self.__game.macos.filematch:
|
||||
for fm in self.__game.macos.file_matchers:
|
||||
fm_model.append(GameFileMatcherData(fm.match_type,fm.match_file))
|
||||
|
||||
im_model = self.__macos.ignorematch.columnview.get_model().get_model()
|
||||
for im in self.__game.macos.ignorematch:
|
||||
for im in self.__game.macos.ignore_matchers:
|
||||
im_model.append(GameFileMatcherData(im.match_type,im.match_file))
|
||||
|
||||
var_model = self.__macos.variables.columnview.get_model().get_model()
|
||||
@ -824,11 +958,11 @@ class GameDialog(Gtk.Dialog):
|
||||
|
||||
#filematch
|
||||
fm_model = self.__steam_windows.filematch.columnview.get_model().get_model()
|
||||
for fm in self.__game.steam_windows.filematch:
|
||||
for fm in self.__game.steam_windows.file_matchers:
|
||||
fm_model.append(GameFileMatcherData(fm.match_type,fm.match_file))
|
||||
|
||||
im_model = self.__steam_windows.ignorematch.columnview.get_model().get_model()
|
||||
for im in self.__game.steam_windows.ignorematch:
|
||||
for im in self.__game.steam_windows.ignore_matchers:
|
||||
im_model.append(GameFileMatcherData(im.match_type,im.match_file))
|
||||
|
||||
var_model = self.__steam_windows.variables.columnview.get_model().get_model()
|
||||
@ -842,11 +976,11 @@ class GameDialog(Gtk.Dialog):
|
||||
self.__steam_linux.installdir_entry.set_text(self.__game.steam_linux.installdir)
|
||||
|
||||
fm_model = self.__steam_linux.filematch.columnview.get_model().get_model()
|
||||
for fm in self.__game.steam_linux.filematch:
|
||||
for fm in self.__game.steam_linux.file_matchers:
|
||||
fm_model.append(GameFileMatcherData(fm.match_type,fm.match_file))
|
||||
|
||||
im_model = self.__steam_linux.ignorematch.columnview.get_model().get_model()
|
||||
for im in self.__game.steam_linux.ignorematch:
|
||||
for im in self.__game.steam_linux.ignore_matchers:
|
||||
im_model.append(GameFileMatcherData(im.match_type,im.match_file))
|
||||
|
||||
var_model = self.__steam_linux.variables.columnview.get_model().get_model()
|
||||
@ -860,11 +994,11 @@ class GameDialog(Gtk.Dialog):
|
||||
self.__steam_macos.installdir_entry.set_text(self.__game.steam_macos.installdir)
|
||||
|
||||
fm_model = self.__steam_macos.filematch.columnview.get_model().get_model()
|
||||
for fm in self.__game.steam_macos.filematch:
|
||||
for fm in self.__game.steam_macos.file_matchers:
|
||||
fm_model.append(GameFileMatcherData(fm.match_type,fm.match_file))
|
||||
|
||||
im_model = self.__steam_macos.ignorematch.columnview.get_model().get_model()
|
||||
for im in self.__game.steam_macos.ignorematch:
|
||||
for im in self.__game.steam_macos.ignore_matchers:
|
||||
im_model.append(GameFileMatcherData(im.match_type,im.match_file))
|
||||
|
||||
var_model = self.__steam_macos.variables.columnview.get_model().get_model()
|
||||
@ -873,6 +1007,9 @@ class GameDialog(Gtk.Dialog):
|
||||
# reset()
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
save Saves the game configuration to file.
|
||||
"""
|
||||
def get_game_data(widget):
|
||||
fm_model = widget.filematch.columnview.get_model().get_model()
|
||||
im_model = widget.ignorematch.columnview.get_model().get_model()
|
||||
@ -1078,14 +1215,28 @@ class GameDialog(Gtk.Dialog):
|
||||
self.__steam_macos = None
|
||||
|
||||
self.__game.save()
|
||||
GameManager.get_global().add_game(self.__game)
|
||||
|
||||
|
||||
def get_is_valid(self):
|
||||
def get_is_valid(self)->bool:
|
||||
"""
|
||||
get_is_valid Check if the configuration is valid for saving.
|
||||
|
||||
:returns: bool
|
||||
"""
|
||||
if (self.__key_entry.get_text() and self.__name_entry.get_text() and self.__sgname_entry.get_text()):
|
||||
sgtype_data = self.__savegame_type_dropdown.get_selected_item()
|
||||
return self.get_is_valid_savegame_type(sgtype_data.savegame_type)
|
||||
return False
|
||||
|
||||
def get_is_valid_savegame_type(self,sgtype:SavegameType)->bool:
|
||||
"""
|
||||
get_is_valid_savegame_type Check if the data for a SavegameType savegame is valid.
|
||||
|
||||
:param sgtype: The type of the Savegame provider
|
||||
:type: sgbackup.game.SavegameType
|
||||
:returns: bool
|
||||
"""
|
||||
def check_is_valid(widget):
|
||||
return (bool(widget.sgroot_entry.get_text()) and bool(widget.sgdir_entry.get_text()))
|
||||
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/org/sgbackup/sgbackup/icons/32x32/apps">
|
||||
<file>sgbackup.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/org/sgbackup/sgbacukp/icons/64x64/apps">
|
||||
<file>sgbackup.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/org/sgbackup/sgbackup/icons/128x128/apps">
|
||||
<file>sgbackup.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/org/sgbackup/sgbackup/icons/256x256/apps">
|
||||
<file>sgbackup.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/org/sgbackup/sgbackup/icons/512x512/apps">
|
||||
<file>sgbackup.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/org/sgbackup/sgbackup/icons/scalable/apps">
|
||||
<file>icons8-windows-10.svg</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.531 20.317h-3.719c-0.291 0-0.531 0.24-0.531 0.537v2.667c0 0.281 0.24 0.531 0.531 0.531h3.735v1.76h-4.667c-0.744 0-1.359-0.615-1.359-1.375v-4.516c0-0.749 0.615-1.359 1.375-1.359h4.635zM10.88 15.385c0 0.776-0.625 1.401-1.401 1.401h-5.973v-1.803h5.041c0.297 0 0.532-0.235 0.532-0.531v-5.932c0-0.297-0.235-0.537-0.532-0.537h-2.692c-0.303-0.005-0.548 0.235-0.548 0.537v2.692c0 0.308 0.24 0.532 0.532 0.532h2.161v1.801h-3.093c-0.771 0-1.401-0.615-1.401-1.385v-4.588c0-0.761 0.631-1.385 1.401-1.385h4.563c0.771 0 1.395 0.624 1.395 1.385v7.812zM28.479 25.812h-1.76v-5.495h-1.24c-0.291 0-0.531 0.24-0.531 0.537v4.957h-1.776v-5.495h-1.24c-0.292 0-0.531 0.24-0.531 0.537v4.957h-1.776v-5.891c0-0.749 0.615-1.359 1.375-1.359h7.479zM28.495 15.385c0 0.776-0.631 1.401-1.401 1.401h-5.973v-1.803h5.041c0.292 0 0.532-0.235 0.532-0.531v-5.932c0-0.297-0.24-0.537-0.532-0.537h-2.708c-0.297 0-0.532 0.24-0.532 0.537v2.692c0 0.308 0.24 0.532 0.532 0.532h2.161v1.801h-3.084c-0.771 0-1.395-0.615-1.395-1.385v-4.588c0-0.761 0.624-1.385 1.395-1.385h4.573c0.776 0 1.401 0.624 1.401 1.385v7.812zM18.292 6.188h-4.584c-0.776 0-1.391 0.624-1.391 1.385v4.588c0 0.771 0.615 1.385 1.391 1.385h4.584c0.76 0 1.391-0.615 1.391-1.385v-4.588c0-0.761-0.631-1.385-1.391-1.385zM17.896 8.521v2.692c0 0.297-0.24 0.532-0.536 0.532h-2.709c-0.291 0-0.531-0.235-0.531-0.532v-2.683c0-0.291 0.229-0.531 0.531-0.531h2.683c0.307 0 0.531 0.24 0.531 0.531zM16.839 18.563h-4.521c-0.755 0-1.369 0.609-1.369 1.359v4.516c0 0.76 0.615 1.375 1.369 1.375h4.521c0.76 0 1.375-0.615 1.375-1.375v-4.516c0-0.749-0.615-1.359-1.375-1.359zM16.437 20.855v2.667c0 0.291-0.235 0.531-0.531 0.531v-0.011h-2.652c-0.296 0-0.536-0.239-0.536-0.536v-2.651c0-0.292 0.24-0.537 0.536-0.537h2.667c0.292 0 0.532 0.245 0.532 0.537zM31.317 1.469c-0.432-0.448-1.031-0.693-1.651-0.699h-27.333c-1.292-0.005-2.339 1.041-2.333 2.333v25.792c-0.005 1.292 1.041 2.339 2.333 2.333h27.333c1.292 0.005 2.339-1.041 2.333-2.333v-25.792c0-0.635-0.265-1.224-0.683-1.651zM31.317 28.896c0.011 0.911-0.733 1.656-1.651 1.651h-27.333c-0.921 0.016-1.672-0.735-1.667-1.651v-25.792c-0.005-0.911 0.74-1.656 1.651-1.651h27.333c0.917 0 1.656 0.74 1.656 1.651v25.792z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@ -8,7 +8,7 @@ keys=consoleHandler,fileHandler
|
||||
keys=consoleFormatter,fileFormatter
|
||||
|
||||
[logger_root]
|
||||
level=DEBUG
|
||||
level=INFO
|
||||
handlers=consoleHandler,fileHandler
|
||||
|
||||
[logger_console]
|
||||
|
||||
@ -22,7 +22,7 @@ from pathlib import Path
|
||||
import sys
|
||||
import json
|
||||
from .settings import settings
|
||||
from .game import STEAM_GAMES,STEAM_WINDOWS_GAMES,STEAM_LINUX_GAMES,STEAM_MACOS_GAMES
|
||||
from .game import GameManager
|
||||
|
||||
__gtype_name__ = __name__
|
||||
|
||||
@ -102,7 +102,7 @@ class IgnoreSteamApp(GObject):
|
||||
appid = conf['appid']
|
||||
name = conf['name']
|
||||
reason = conf['reason'] if 'reason' in conf else ""
|
||||
return SteamIgnoreApp(appid,name,reason)
|
||||
return IgnoreSteamApp(appid,name,reason)
|
||||
|
||||
return None
|
||||
|
||||
@ -331,23 +331,25 @@ class Steam(GObject):
|
||||
return sorted(new_apps)
|
||||
|
||||
def update_steam_apps(self):
|
||||
gm = GameManager.get_global()
|
||||
|
||||
for lib in self.libraries():
|
||||
for app in lib.steam_apps:
|
||||
if PLATFORM_WINDOWS:
|
||||
if ((app.appid in STEAM_WINDOWS_GAMES)
|
||||
and (STEAM_WINDOWS_GAMES[app.appid].installdir != app.installdir)):
|
||||
game = STEAM_WINDOWS_GAMES[app.appid]
|
||||
if ((app.appid in gm.steam_windows_games)
|
||||
and (gm.steam_windows_games[app.appid].installdir != app.installdir)):
|
||||
game = gm.steam_windows_games[app.appid]
|
||||
game.installdir = app.installdir
|
||||
game.save()
|
||||
elif PLATFORM_LINUX:
|
||||
if ((app.appid in STEAM_LINUX_GAMES)
|
||||
and (STEAM_LINUX_GAMES[app.appid].installdir != app.installdir)):
|
||||
game = STEAM_LINUX_GAMES[app.appid]
|
||||
if ((app.appid in gm.steam_linux_games)
|
||||
and (gm.steam_linux_games[app.appid].installdir != app.installdir)):
|
||||
game = gm.steam_linux_games[app.appid]
|
||||
game.installdir = app.installdir
|
||||
game.save()
|
||||
elif PLATFORM_MACOS:
|
||||
if ((app.appid in STEAM_MACOS_GAMES)
|
||||
and (STEAM_MACOS_GAMES[app.appid].installdir != app.installdir)):
|
||||
game = STEAM_MACOS_GAMES[app.appid]
|
||||
if ((app.appid in gm.steam_macos_games)
|
||||
and (gm.steam_macos_games[app.appid].installdir != app.installdir)):
|
||||
game = gm.steam_macos_games[app.appid]
|
||||
game.installdir = app.installdir
|
||||
game.save()
|
||||
|
||||
@ -27,7 +27,12 @@ extensions = [
|
||||
]
|
||||
language = 'en'
|
||||
master_doc = 'index'
|
||||
source_suffix = '.rst'
|
||||
source_suffix = {
|
||||
'.rst': "restructuredtext",
|
||||
'.txt': "restructuredtext",
|
||||
'.md': 'markdown',
|
||||
'.markdown': 'mardown',
|
||||
}
|
||||
templates_path = ['templates']
|
||||
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
@ -26,4 +26,6 @@ Table of Contents
|
||||
modules/sgbackup.settings.rst
|
||||
modules/sgbackup.game.rst
|
||||
modules/sgbackup.archiver.rst
|
||||
modules/sgbackup.gui.rst
|
||||
|
||||
|
||||
|
||||
76
sphinx/modules/sgbackup.game.rst
Normal file
76
sphinx/modules/sgbackup.game.rst
Normal file
@ -0,0 +1,76 @@
|
||||
=====================
|
||||
Module: sgbackup.game
|
||||
=====================
|
||||
|
||||
.. title:: sgbackup API documentation
|
||||
|
||||
Game classes
|
||||
------------
|
||||
|
||||
.. autoclass:: sgbackup.game.Game
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: sgbackup.game.GameManager
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
GameData classes
|
||||
----------------
|
||||
|
||||
.. autoclass:: sgbackup.game.GameData
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: sgbackup.game.WindowsGame
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: sgbackup.game.LinuxGame
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: sgbackup.game.MacOSGame
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: sgbackup.game.SteamWindowsGame
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: sgbackup.game.SteamLinuxGame
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: sgbackup.game.SteamMacOSGame
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Helper classes
|
||||
--------------
|
||||
|
||||
.. autoclass:: sgbackup.game.GameFileMatcher
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Enums
|
||||
-----
|
||||
|
||||
.. autoclass:: sgbackup.game.SavegameType
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: sgbackup.game.GameFileType
|
||||
:members:
|
||||
:show-inheritance:
|
||||
18
sphinx/modules/sgbackup.gui-app.rst
Normal file
18
sphinx/modules/sgbackup.gui-app.rst
Normal file
@ -0,0 +1,18 @@
|
||||
============
|
||||
Applicaction
|
||||
============
|
||||
|
||||
Applicaction
|
||||
------------
|
||||
|
||||
.. autoclass:: sgbackup.gui.Application
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
Application Window
|
||||
------------------
|
||||
|
||||
.. autoclass:: sgbackup.gui.AppWindow
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
22
sphinx/modules/sgbackup.gui-data.rst
Normal file
22
sphinx/modules/sgbackup.gui-data.rst
Normal file
@ -0,0 +1,22 @@
|
||||
==========================
|
||||
Module: sgbackup.gui._game
|
||||
==========================
|
||||
|
||||
Game data classes
|
||||
-----------------
|
||||
|
||||
.. autoclass:: sgbackup.gui._gamedialog.GameFileMatcherData
|
||||
:members:
|
||||
|
||||
.. autoclass:: sgbackup.gui._gamedialog.GameFileTypeData
|
||||
:members:
|
||||
|
||||
.. autoclass:: sgbackup.gui._gamedialog.GameVariableData
|
||||
:members:
|
||||
|
||||
.. autoclass:: sgbackup.gui._gamedialog.RegistryKeyData
|
||||
:members:
|
||||
|
||||
.. autoclass:: sgbackup.gui._gamedialog.SavegameTypeData
|
||||
:members:
|
||||
|
||||
23
sphinx/modules/sgbackup.gui-dialogs.rst
Normal file
23
sphinx/modules/sgbackup.gui-dialogs.rst
Normal file
@ -0,0 +1,23 @@
|
||||
=======
|
||||
Dialogs
|
||||
=======
|
||||
|
||||
.. title:: sgbackup API documentation
|
||||
|
||||
SettingsDialog
|
||||
---------------
|
||||
|
||||
.. autoclass:: sgbackup.gui.SettingsDialog
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
|
||||
GameDialog
|
||||
----------
|
||||
|
||||
.. autoclass:: sgbackup.gui.GameDialog
|
||||
:members:
|
||||
|
||||
.. autoclass:: sgbackup.gui._gamedialog.GameVariableDialog
|
||||
:members:
|
||||
|
||||
11
sphinx/modules/sgbackup.gui-widgets.rst
Normal file
11
sphinx/modules/sgbackup.gui-widgets.rst
Normal file
@ -0,0 +1,11 @@
|
||||
=======
|
||||
Widgets
|
||||
=======
|
||||
|
||||
.. autoclass:: sgbackup.gui.GameView
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. autoclass:: sgbackup.gui.BackupView
|
||||
:members:
|
||||
:undoc-members:
|
||||
11
sphinx/modules/sgbackup.gui.rst
Normal file
11
sphinx/modules/sgbackup.gui.rst
Normal file
@ -0,0 +1,11 @@
|
||||
=====================
|
||||
Package: sgbackup.gui
|
||||
=====================
|
||||
|
||||
.. title:: sgbackup API documentation
|
||||
|
||||
.. toctree:: 1
|
||||
sgbackup.gui-app.rst
|
||||
sgbackup.gui-widgets.rst
|
||||
sgbackup.gui-dialogs.rst
|
||||
sgbackup.gui-data.rst
|
||||
@ -4,7 +4,7 @@ sgbackup API
|
||||
|
||||
.. title:: sgbackup API
|
||||
|
||||
.. toctree:: 1
|
||||
sgbackup.game.rst
|
||||
sgbackup.gui.rst
|
||||
|
||||
.. automodule:: sgbackup
|
||||
:imported-mebers:
|
||||
:undoc-members:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user