1
0
mirror of https://github.com/gryf/fs-uae-wrapper.git synced 2025-12-19 12:28:12 +01:00

Added cd32 wrapper module

This commit is contained in:
2016-12-27 21:44:44 +01:00
parent 71e0319e1f
commit c6522061c8
2 changed files with 289 additions and 0 deletions

207
fs_uae_wrapper/cd32.py Normal file
View File

@@ -0,0 +1,207 @@
"""
Run CD32 games using fsuae
It will use compressed directories, and create 7z archive for save state dirs.
It is assumed, that filename of cue file (without extension) is the same as
archive with game assets, while using config name (without extension) will be
used as a base for save state (it will append '_save.7z' to the archive file
name.
"""
import os
import shutil
import subprocess
import sys
import tempfile
from fs_uae_wrapper import utils
class CD32(object):
"""
Class for performing extracting archive, copying emulator files, and
cleaning it back again
"""
def __init__(self):
self.dir = None
self.conf_file = None
self.save_filename = None
self.arch_filepath = None
self.all_options = None
self.fsuae_config = None
def run(self, conf_file, fs_uae_options, configuration):
"""
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
Params:
conf_file: a relative path to provided configuration file
fs_uae_options: is an CmdOption object created out of command line
parameters
configuration: is config dictionary created out of config file
"""
self.all_options = utils.merge_all_options(configuration,
fs_uae_options)
if 'wrapper_archive' not in self.all_options:
sys.stderr.write("Configuration lacks of required "
"`wrapper_archive' option.\n")
return False
self.fsuae_config = configuration
self.conf_file = conf_file
self.dir = tempfile.mkdtemp()
self.set_assets_paths()
if not self._extract():
return False
for method in (self._copy_conf, self._load_save):
if not method():
return False
kick_opts = self.kickstart_option()
if kick_opts:
fs_uae_options.update(kick_opts)
if self._run_game(fs_uae_options.list()):
return self._save_save()
return True
def clean(self):
"""Remove temporary file"""
if self.dir:
shutil.rmtree(self.dir)
return
def kickstart_option(self):
"""
This is kind of hack - since we potentially can have a relative path
to kickstart directory, there is a need for getting this option from
configuration files (which unfortunately can be spanned all over the
different places, see https://fs-uae.net/configuration-files) and
check whether or not one of 'kickstarts_dir', 'kickstart_file' or
'kickstart_ext_file' options are set. In either case if one of those
options are set and are relative, they should be set to absolute path,
so that kickstart files can be found by relocated configuration file.
"""
conf = utils.get_config(self.conf_file)
kick = {}
for key in ('kickstart_file', 'kickstart_ext_file', 'kickstarts_dir'):
val = conf.get(key)
if val:
if not os.path.isabs(val):
val = utils.interpolate_variables(val, self.conf_file)
kick[key] = os.path.abspath(val)
else:
kick[key] = val
return kick
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]
arch = self.all_options['wrapper_archive']
if os.path.isabs(arch):
self.arch_filepath = arch
else:
self.arch_filepath = os.path.join(conf_abs_dir, arch)
self.save_filename = os.path.join(conf_abs_dir, conf_base + '_save.7z')
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 _extract(self):
"""Extract archive to temp dir"""
item = self.all_options.get('title')
if not item:
item = self.all_options['wrapper_archive']
curdir = os.path.abspath('.')
os.chdir(self.dir)
utils.extract_archive(self.arch_filepath,
self.all_options.get('wrapper_gui_msg'), item)
os.chdir(curdir)
return True
def _run_game(self, fs_uae_options):
"""execute game in provided directory"""
curdir = os.path.abspath('.')
os.chdir(self.dir)
try:
subprocess.call(['fs-uae'] + fs_uae_options)
except subprocess.CalledProcessError:
sys.stderr.write('Warning: fs-uae returned non 0 exit code\n')
os.chdir(curdir)
return True
def _save_save(self):
"""
Get the saves from emulator and store it where configuration is placed
"""
save_path = os.path.join(self.dir, 'fs-uae-save')
if not os.path.exists(save_path):
return True
if os.path.exists(self.save_filename):
os.unlink(self.save_filename)
try:
subprocess.call(['7z', 'a', self.save_filename,
os.path.join(self.dir, 'fs-uae-save')])
except subprocess.CalledProcessError:
sys.stderr.write('Warning: archiving save state failed\n')
return False
return True
def _load_save(self):
"""
Put the saves (if exists) to the temp directory.
"""
if not os.path.exists(self.save_filename):
return True
curdir = os.path.abspath('.')
os.chdir(self.dir)
try:
subprocess.call(['7z', 'x', self.save_filename])
except subprocess.CalledProcessError:
sys.stderr.write('Warning: extracting archive with save state '
'failed\n')
os.chdir(curdir)
return True
def run(config_file, fs_uae_options, configuration):
"""Run fs-uae with provided config file and options"""
runner = CD32()
try:
return runner.run(config_file, fs_uae_options, configuration)
finally:
runner.clean()

82
tests/test_cd32.py Normal file
View File

@@ -0,0 +1,82 @@
import os
import sys
from tempfile import mkstemp, mkdtemp
from unittest import TestCase
import shutil
try:
from unittest import mock
except ImportError:
import mock
from fs_uae_wrapper import cd32
class TestCD32(TestCase):
def setUp(self):
fd, self.fname = mkstemp()
self.dirname = mkdtemp()
os.close(fd)
self._argv = sys.argv[:]
sys.argv = ['fs-uae-wrapper']
self.curdir = os.path.abspath(os.curdir)
def tearDown(self):
os.chdir(self.curdir)
try:
shutil.rmtree(self.dirname)
except OSError:
pass
os.unlink(self.fname)
sys.argv = self._argv[:]
def test_clean(self):
acd32 = cd32.CD32()
acd32.clean()
self.assertTrue(os.path.exists(self.dirname))
acd32.dir = self.dirname
acd32.clean()
self.assertFalse(os.path.exists(self.dirname))
@mock.patch('fs_uae_wrapper.utils.get_config')
def test_kickstart_option(self, get_config):
acd32 = cd32.CD32()
get_config.return_value = {'foo': 'bar'}
self.assertDictEqual(acd32.kickstart_option(), {})
get_config.return_value = {'kickstarts_dir': '/some/path'}
self.assertDictEqual(acd32.kickstart_option(),
{'kickstarts_dir': '/some/path'})
os.chdir(self.dirname)
get_config.return_value = {'kickstarts_dir': '../some/path'}
result = os.path.abspath(os.path.join(self.dirname, '../some/path'))
self.assertDictEqual(acd32.kickstart_option(),
{'kickstarts_dir': result})
acd32.conf_file = os.path.join(self.dirname, 'Config.fs-uae')
get_config.return_value = {'kickstarts_dir': '$CONFIG/../path'}
result = os.path.abspath(os.path.join(self.dirname, '../path'))
self.assertDictEqual(acd32.kickstart_option(),
{'kickstarts_dir': result})
def test_set_assets_paths(self):
acd32 = cd32.CD32()
os.chdir(self.dirname)
acd32.conf_file = 'Config.fs-uae'
acd32.all_options = {'wrapper_archive': 'foo.7z'}
acd32.set_assets_paths()
full_path = os.path.join(self.dirname, 'Config_save.7z')
self.assertEqual(acd32.save_filename, full_path)
acd32.all_options = {'wrapper_archive': '/home/user/foo.7z'}
acd32.set_assets_paths()
full_path = os.path.join(self.dirname, 'Config_save.7z')
self.assertEqual(acd32.save_filename, full_path)