2025.01.26 18:36:15 (desktop)

This commit is contained in:
Christian Moser 2025-01-26 18:36:16 +01:00
parent 2daf302e54
commit 604d753d1b
Failed to extract signature
5 changed files with 193 additions and 5 deletions

View File

@ -33,12 +33,12 @@ from ..game import Game
from ..settings import settings
class Archiver(GObject):
def __init__(self,key:str,name:str,extensions:list[str],decription:str|None=None):
def __init__(self,key:str,name:str,extensions:list[str],description:str|None=None):
GObject.__init__(self)
self.__key = key
self.__name = name
if decription:
self.__description = decription
if description:
self.__description = description
else:
self.__description = ""

View File

@ -0,0 +1,157 @@
###############################################################################
# sgbackup - The SaveGame Backup tool #
# Copyright (C) 2024 Christian Moser #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
###############################################################################
from gi.repository.GObject import Property
from gi.repository import GLib
from ._archiver import Archiver
from tarfile import TarFile
from tempfile import mkdtemp,NamedTemporaryFile
import json
import os
from ..game import Game
import logging
logger = logging.getLogger(__name__)
class TarfileArchiver(Archiver):
def __init__(self,
key='tarfile',
name="TarFile",
extensions=['.tar'],
description="Archiver for .tar files.",
compression=None):
Archiver.__init__(self,key,name,extensions,description)
if compression is None:
self.__compression=""
else:
self.__compression=compression
@Property
def compression(self):
return self.__compression
def is_archive(self, filename):
if (Archiver.is_archive()):
try:
with TarFile(filename,"r:{}".format(self.compression)) as tf:
return ("gameconf.json" in tf.getnames())
except:
pass
return False
def do_backup(self, game, filename):
_calc_fraction = lambda n,cnt: ((1.0 / n) * cnt)
self._backup_progress(game,0.0,"Starting {game} ...".format(game.name))
files = game.get_backup_files()
n = len(files) + 2
cnt=1
data=json.dumps(game.serialize(),ensure_ascii=False,indent=4)
with TarFile(filename,'x:{}'.format(self.compression)) as tf:
self._backup_progress(game,_calc_fraction(n,cnt),"gameconf.json")
gcf = os.path.join(GLib.get_tmp_dir(),"sgbackup-" + GLib.get_user_name() + "." + "backup." + game.key + ".gameconf.tmp")
with open(gcf,"wt",encoding="utf-8") as gcfile:
gcfile.write(data)
tf.add(gcf,"gameconf.json")
for path,arcname in files:
cnt += 1
self._backup_progress(game,_calc_fraction(n,cnt),"arcname")
tf.add(path,arcname)
self._backup_progress(game,1.0,message="Finished ...")
return True
def do_restore(self,filename):
def rmdir_recursive(dir):
for dirent in os.listdir(dir):
fname = os.path.join(dirent)
if os.path.islink(fname):
os.unlink(fname)
elif os.path.isdir(fname):
rmdir_recursive(fname)
else:
os.unlink(fname)
os.rmdir(dir)
if not self.is_archive(filename):
raise RuntimeError("{file} is not a vaild {archiver} archive!".format(file=filename,archiver=self.name))
tempdir = mkdtemp(suffix="-sgbackup")
tempfile= os.path.join(tempdir,"gameconf.json")
try:
with TarFile(filename,'r:{}'.format(self.compression)) as tf:
tf.extract("gameconf.json",tempdir)
with open(tempfile,"r",encoding="utf-8") as ifile:
game = Game.new_from_json_file(tempfile)
if not os.path.isdir(game.savegame_root):
os.makedirs(game.savegame_root)
for arcname in [i for i in tf.getnames() if i not in ("gameconf.json",'.','..')]:
tf.extract(arcname,path=game.savegame_root)
rmdir_recursive(tempdir)
return True
except Exception as ex:
logger.error("Restoring archive {file} failed! ({message})".format(
file=filename,
message=str(ex)))
if os.path.isdir(tempdir):
rmdir_recursive(tempdir)
return False
class TarfileBz2Archiver(TarfileArchiver):
def __init__(self):
TarfileArchiver.__init__(self,
'tarfile-bz2',
"TarfileBzip2",
['.tbz','.tar.bz2','tar.bzip2'],
"Archiver for bzip2 compressedd tar archives.",
"bz2")
class TarfileGzArchiver(TarfileArchiver):
def __init__(self):
TarfileArchiver.__init__(self,
'tarfile-gz',
"TarfileGzip",
['.tgz','.tar.gz'],
"Archiver for gzip compressed tar archives.",
"gz")
class TarfileXzArchiver(TarfileArchiver):
def __init__(self):
TarfileArchiver.__init__(self,
'tarfile-xz',
"TarfileXz",
['.txz','.tar.xz'],
"Archiver for xz compressed tar archives.",
'xz')
ARCHIVERS=[
TarfileArchiver(),
TarfileBz2Archiver(),
TarfileGzArchiver(),
TarfileXzArchiver(),
]

View File

@ -68,7 +68,7 @@ class ZipfileArchiver(Archiver):
except:
game = zip_game
if not game.savegame_root:
if not os.path.isdir(game.savegame_root):
os.makedirs(game.savegame_root)
extract_files = [i for i in zf.filelist if i.startswith(zip_game.savegame_dir + "/")]

View File

@ -534,7 +534,7 @@ class AppWindow(Gtk.ApplicationWindow):
builder = Gtk.Builder.new()
Gtk.ApplicationWindow.__init__(self,**kwargs)
self.set_default_size(800,600)
self.set_default_size(800,700)
self.set_icon_name('org.sgbackup.sgbackup-symbolic')
self.__builder = builder
@ -561,6 +561,7 @@ class AppWindow(Gtk.ApplicationWindow):
self.__vpaned.set_end_child(self.backupview)
self.__vpaned.set_resize_start_child(True)
self.__vpaned.set_resize_end_child(True)
self.__vpaned.set_position(400)
vbox.append(self.__vpaned)

View File

@ -43,6 +43,13 @@ for _zc,_zs in ZIPFILE_COMPRESSION_STR.items():
del _zc
del _zs
if sys.platform.lower() == 'win32':
PLATFORM_WINDOWS = True
import winreg
else:
PLATFORM_WINDOWS = False
class Settings(GObject.GObject):
__gtype_name__ = "Settings"
@ -129,6 +136,27 @@ class Settings(GObject.GObject):
for v in vars:
self.parser.set('variables',v[0],v[1])
@GObject.Property(type=str)
def steam_installpath(self):
if self.parser.has_section('steam') and self.parser.has_option('installpath'):
return self.parser.get('steam','installdir')
if PLATFORM_WINDOWS:
for i in ('SOFTWARE\\WOW6432Node\\Valve\\Steam','SOFTWARE\\Valve\\Steam'):
try:
skey = None
skey = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE,i)
svalue = winreg.QueryValueEx(skey,'InstallPath')[0]
if svalue:
self.parser.set('steam','installpath',svalue)
return svalue
except:
continue
finally:
if skey:
skey.Close()
return ""
def add_variable(self,name:str,value:str):
self.parser.set('variables',name,value)
@ -149,6 +177,8 @@ class Settings(GObject.GObject):
ret.update({
"DOCUMENTS": GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS),
"DOCUMENTS_DIR": GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS),
"DATADIR": GLib.get_user_data_dir(),
"STEAM_INSTALLPATH": self.steam_installpath,
})
ret.update(self.variables)
return ret