""" Base class for all wrapper modules """ import os import sys import shutil import tempfile from fs_uae_wrapper import utils from fs_uae_wrapper import path class Base(object): """ Base class for wrapper modules """ def __init__(self, conf_file, fsuae_options, configuration): """ Params: conf_file: a relative path to provided configuration file fsuae_options: is an CmdOption object created out of command line parameters configuration: is config dictionary created out of config file """ self.conf_file = conf_file self.fsuae_config = configuration self.fsuae_options = fsuae_options self.all_options = utils.merge_all_options(configuration, fsuae_options) self.dir = None self.save_filename = None def run(self): """ Main function which accepts configuration file for FS-UAE It will do as follows: - set needed full path for asset files - extract archive file - copy configuration - [copy save if exists] - run the emulation - archive save state """ if not self._validate_options(): return False self._normalize_options() self.dir = tempfile.mkdtemp() self._set_assets_paths() return True def clean(self): """Remove temporary file""" if self.dir: shutil.rmtree(self.dir) return def _set_assets_paths(self): """ Set full paths for archive file (without extension) and for save state archive file """ conf_abs_dir = os.path.dirname(os.path.abspath(self.conf_file)) conf_base = os.path.basename(self.conf_file) conf_base = os.path.splitext(conf_base)[0] # set optional save_state arch_ext = utils.get_arch_ext(self.all_options.get('wrapper_archiver')) if arch_ext: self.save_filename = os.path.join(conf_abs_dir, conf_base + '_save' + arch_ext) def _copy_conf(self): """copy provided configuration as Config.fs-uae""" shutil.copy(self.conf_file, self.dir) os.rename(os.path.join(self.dir, os.path.basename(self.conf_file)), os.path.join(self.dir, 'Config.fs-uae')) return True def _run_emulator(self, fsuae_options): """execute fs-uae in provided directory""" curdir = os.path.abspath('.') os.chdir(self.dir) utils.run_command(['fs-uae'] + fsuae_options) os.chdir(curdir) return True def _get_title(self): """ Return the title if found in configuration. As a fallback archive file name will be used as title. """ title = '' gui_msg = self.all_options.get('wrapper_gui_msg', '0') if gui_msg == '1': title = self.all_options.get('title') if not title: title = self.all_options['wrapper_archive'] return title def _save_save(self): """ Get the saves from emulator and store it where configuration is placed """ if self.all_options.get('wrapper_save_state', '0') != '1': return True os.chdir(self.dir) save_path = self._get_saves_dir() if not save_path: return True if os.path.exists(self.save_filename): os.unlink(self.save_filename) curdir = os.path.abspath('.') if not utils.create_archive(self.save_filename, '', [save_path]): sys.stderr.write('Error: archiving save state failed\n') os.chdir(curdir) return False os.chdir(curdir) return True def _load_save(self): """ Put the saves (if exists) to the temp directory. """ if self.all_options.get('wrapper_save_state', '0') != '1': return True if not os.path.exists(self.save_filename): return True curdir = os.path.abspath('.') os.chdir(self.dir) utils.extract_archive(self.save_filename) os.chdir(curdir) return True def _get_saves_dir(self): """ Return path to save state directory or None in cases: - there is no save state dir set relative to config file - save state dir is set globally - save state dir is set relative to the config file - save state dir doesn't exists Note, that returned path is relative not absolute """ if not self.all_options.get('save_states_dir'): return None if self.all_options['save_states_dir'].startswith('$CONFIG') and \ '..' not in self.all_options['save_states_dir']: save = self.all_options['save_states_dir'].replace('$CONFIG/', '') else: return None save_path = os.path.join(self.dir, save) if not os.path.exists(save_path) or not os.path.isdir(save_path): return None if save.endswith('/'): save = save[:-1] return save def _normalize_options(self): """ Search and replace values for options which starts with $CONFIG with absolute path for all options, except: - save_states_dir Configuration file will be placed in new directory, therefore it is needed to calculate new paths so that emulator can find assets. """ exclude_list = ['save_states_dir'] conf_abs_dir = os.path.dirname(os.path.abspath(self.conf_file)) changed_options = {} for key, val in utils.get_config(self.conf_file).items(): if val.startswith('/'): continue if key in exclude_list: continue if val.startswith('$CONFIG'): abspath = os.path.abspath(val.replace('$CONFIG', conf_abs_dir)) changed_options[key] = abspath continue changed_options[key] = os.path.abspath(val) self.fsuae_options.update(changed_options) def _validate_options(self): """Validate mandatory options""" if 'wrapper' not in self.all_options: sys.stderr.write("Configuration lacks of required " "`wrapper' option.\n") return False if self.all_options.get('wrapper_save_state', '0') == '0': return True if 'wrapper_archiver' not in self.all_options: sys.stderr.write("Configuration lacks of required " "`wrapper_archiver' option.\n") return False if not path.which(self.all_options['wrapper_archiver']): sys.stderr.write("Cannot find archiver `%s'." % self.all_options['wrapper_archiver']) return False return True class ArchiveBase(Base): """ Base class for archive based wrapper modules """ def __init__(self, conf_file, fsuae_options, configuration): """ Params: conf_file: a relative path to provided configuration file fsuae_options: is an CmdOption object created out of command line parameters configuration: is config dictionary created out of config file """ super(ArchiveBase, self).__init__(conf_file, fsuae_options, configuration) self.arch_filepath = None def _set_assets_paths(self): """ Set full paths for archive file (without extension) and for save state archive file """ super(ArchiveBase, self)._set_assets_paths() conf_abs_dir = os.path.dirname(os.path.abspath(self.conf_file)) arch = self.all_options.get('wrapper_archive') if arch: if os.path.isabs(arch): self.arch_filepath = arch else: self.arch_filepath = os.path.join(conf_abs_dir, arch) def _extract(self): """Extract archive to temp dir""" title = self._get_title() curdir = os.path.abspath('.') os.chdir(self.dir) result = utils.extract_archive(self.arch_filepath, title) os.chdir(curdir) return result def _validate_options(self): validation_result = super(ArchiveBase, self)._validate_options() if 'wrapper_archive' not in self.all_options: sys.stderr.write("Configuration lacks of required " "`wrapper_archive' option.\n") validation_result = False return validation_result