From 0e6b63d1a264f78db123e43b952d5eb9e43ef709 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 30 May 2021 18:57:23 +0200 Subject: [PATCH] [utils/lfs] Use custom configparser wrapper The main reason is to prevent user configs from being overwritten and also to only actually write the config file if it has been modified from within Legendary itself. Closes #279 --- legendary/lfs/lgndry.py | 23 ++++++++++++++++++++--- legendary/utils/config.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 legendary/utils/config.py diff --git a/legendary/lfs/lgndry.py b/legendary/lfs/lgndry.py index 1008c1e..dfef533 100644 --- a/legendary/lfs/lgndry.py +++ b/legendary/lfs/lgndry.py @@ -2,12 +2,12 @@ import json import os -import configparser import logging from pathlib import Path from legendary.models.game import * +from legendary.utils.config import LGDConf from legendary.utils.lfs import clean_filename @@ -29,7 +29,7 @@ class LGDLFS: # EGS metadata self._game_metadata = dict() # Config with game specific settings (e.g. start parameters, env variables) - self.config = configparser.ConfigParser(comment_prefixes='/', allow_no_value=True) + self.config = LGDConf(comment_prefixes='/', allow_no_value=True) self.config.optionxform = str # ensure folders exist. @@ -65,7 +65,14 @@ class LGDLFS: f'{e!r}, please remove manually') # try loading config - self.config.read(os.path.join(self.path, 'config.ini')) + try: + self.config.read(os.path.join(self.path, 'config.ini')) + except Exception as e: + self.log.error(f'Unable to read configuration file, please ensure that file is valid! ' + f'(Error: {repr(e)})') + self.log.warning(f'Continuing with blank config in safe-mode...') + self.config.read_only = True + # make sure "Legendary" section exists if 'Legendary' not in self.config: self.config['Legendary'] = dict() @@ -261,6 +268,16 @@ class LGDLFS: return [InstalledGame.from_json(i) for i in self._installed.values()] def save_config(self): + # do not save if in read-only mode or file hasn't changed + if self.config.read_only or not self.config.modified: + return + # if config file has been modified externally, back-up the user-modified version before writing + if (modtime := int(os.stat(os.path.join(self.path, 'config.ini')).st_mtime)) != self.config.modtime: + new_filename = f'config.{modtime}.ini' + self.log.warning(f'Configuration file has been modified while legendary was running, ' + f'user-modified config will be renamed to "{new_filename}"...') + os.rename(os.path.join(self.path, 'config.ini'), os.path.join(self.path, new_filename)) + with open(os.path.join(self.path, 'config.ini'), 'w') as cf: self.config.write(cf) diff --git a/legendary/utils/config.py b/legendary/utils/config.py new file mode 100644 index 0000000..4ef809e --- /dev/null +++ b/legendary/utils/config.py @@ -0,0 +1,31 @@ +import configparser +import os + + +class LGDConf(configparser.ConfigParser): + def __init__(self, *args, **kwargs): + self.modified = False + self.read_only = False + self.modtime = None + super().__init__(*args, **kwargs) + + def read(self, filename): + # if config file exists, save modification time + if os.path.exists(filename): + self.modtime = int(os.stat(filename).st_mtime) + + return super().read(filename) + + def set(self, *args, **kwargs): + if self.read_only: + return + + self.modified = True + super().set(*args, **kwargs) + + def __setitem__(self, key, value): + if self.read_only: + return + + self.modified = True + super().__setitem__(key, value)