mirror of
https://github.com/gryf/fs-uae-wrapper.git
synced 2026-02-02 22:25:47 +01:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b0ef15eae | |||
| 59bd1b6029 | |||
| f5e6471555 | |||
| 4c61c3d7ea | |||
| 7e3d68624f | |||
| f311605019 | |||
| 418e480fb5 | |||
| 118d758ec7 | |||
| 3b597e34ee | |||
| 7b40974779 | |||
| b4ab8ac4f5 | |||
| 8d8d38d5c0 | |||
| bd0aa3dee4 | |||
| d2a9f39fd9 |
33
.github/workflows/test.yml
vendored
Normal file
33
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install flake8 pytest tox coverage pytest-cov pytest-pep8
|
||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
tox
|
||||
11
.travis.yml
11
.travis.yml
@@ -1,11 +0,0 @@
|
||||
language: python
|
||||
env:
|
||||
- TOXENV=py27
|
||||
- TOXENV=py27-flake8
|
||||
- TOXENV=py3
|
||||
- TOXENV=py3-flake8
|
||||
install: pip install tox
|
||||
script: tox
|
||||
before_install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -y python-tk python3-tk
|
||||
82
README.rst
82
README.rst
@@ -2,8 +2,8 @@
|
||||
FS-UAE Wrapper
|
||||
==============
|
||||
|
||||
.. image:: https://travis-ci.org/gryf/fs-uae-wrapper.svg?branch=master
|
||||
:target: https://travis-ci.org/gryf/fs-uae-wrapper
|
||||
.. image:: https://github.com/gryf/fs-uae-wrapper/workflows/Test/badge.svg
|
||||
:target: https://github.com/gryf/fs-uae-wrapper/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/fs-uae-wrapper.svg
|
||||
:target: https://pypi.python.org/pypi/fs-uae-wrapper
|
||||
@@ -137,6 +137,7 @@ Currently, couple of wrapper modules are available:
|
||||
- cd32
|
||||
- archive
|
||||
- savestate
|
||||
- whdload
|
||||
|
||||
plain
|
||||
-----
|
||||
@@ -323,11 +324,13 @@ Options used:
|
||||
* ``wrapper_whdload_base`` (required) path to the whdload base system. Usually
|
||||
it's minimal system containing at least whdload executables in C, and config
|
||||
in S. Read on below for further details.
|
||||
* ``wrapper_whdload_options`` (optional) this option will replace the line in
|
||||
``s:whdload-startup`` with specific ``whdload`` options for certain slave.
|
||||
For reference look at WHDLoad documentation and/or on ``s:WHDLoad.prefs``.
|
||||
Note, that ``Slave=`` option must not be used.
|
||||
* ``wrapper_archive`` (optional) path to the whdload archive, defaults to same
|
||||
name as configuration file with some detected archive extension. Note, that
|
||||
name is case sensitive
|
||||
* ``wrapper_archiver`` (optional) archiver to use for storage save state -
|
||||
default ``7z``.
|
||||
|
||||
This module is solely used with whdload distributed games (not just whdload
|
||||
slave files, but whole games, which can be found on several places on the
|
||||
@@ -336,22 +339,74 @@ internet).
|
||||
Base image
|
||||
~~~~~~~~~~
|
||||
|
||||
To make it work, first the minimal system archive need to be prepared. There
|
||||
are few dependences to be included in such small system:
|
||||
To make it work, first the absolute minimal image need to contain following
|
||||
structure:
|
||||
|
||||
.. code::
|
||||
|
||||
.
|
||||
├── C
|
||||
│  ├── DIC
|
||||
│  ├── Execute
|
||||
│  ├── Patcher
|
||||
│  ├── RawDIC
|
||||
│  ├── SetPatch
|
||||
│  ├── WHDLoad
|
||||
│  └── WHDLoadCD32
|
||||
└── S
|
||||
├── startup-sequence
|
||||
└── WHDLoad.prefs
|
||||
|
||||
where the minimum dependences are:
|
||||
|
||||
- ``Excecute`` from your copy of Workbench
|
||||
- `WHDLoad`_ 18.9
|
||||
- `uaequit`_
|
||||
- `SetPatch`_ 43.6
|
||||
- ``Excecute``, ``Assign`` and whatever commands you'll be use in scripts from
|
||||
your copy of Workbench
|
||||
|
||||
and the ``S/startup-sequence`` should at least contain:
|
||||
|
||||
.. code::
|
||||
|
||||
setpatch QUIET
|
||||
|
||||
IF EXISTS S:whdload-startup
|
||||
Execute S:whdload-startup
|
||||
EndIF
|
||||
|
||||
To leverage more pleasant UX, additionally those bits should be installed (or -
|
||||
copied into base image filesystem):
|
||||
|
||||
- ``Assign`` and whatever commands you'll be use in scripts from your copy of
|
||||
Workbench
|
||||
- `uaequit`_ - this will allow to quit emulator, after quiting game
|
||||
- `kgiconload`_ - tool for reading icon and executing *default tool* with
|
||||
optionally defined tool types as parameters (in this case: WHDLoad)
|
||||
- `SKick`_ optionally - for kickstart relocations. Also images of corresponding
|
||||
kickstart ROM images will be needed.
|
||||
|
||||
|
||||
and then ``s/startup-sequence`` might looks a follows:
|
||||
|
||||
.. code::
|
||||
|
||||
Assign >NIL: ENV: RAM:
|
||||
Assign >NIL: T: RAM:
|
||||
|
||||
setpatch QUIET
|
||||
|
||||
IF EXISTS S:whdload-startup
|
||||
Execute S:whdload-startup
|
||||
EndIF
|
||||
|
||||
C:UAEquit
|
||||
|
||||
Creating base image archive
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Now, the tree for the minimal image could look like that:
|
||||
|
||||
.. code::
|
||||
|
||||
.
|
||||
├── C
|
||||
│  ├── Assign
|
||||
@@ -369,9 +424,12 @@ Now, the tree for the minimal image could look like that:
|
||||
└── WHDLoad.prefs
|
||||
|
||||
to use relocation tables you'll need to place ``Kickstarts`` drawer into Devs
|
||||
drawer, so it'll looks like this:
|
||||
drawer. Also keep in mind, that corresponding kickstart rom images need to be
|
||||
placed there as well, otherwise it may or may not work. Structure looks like
|
||||
this:
|
||||
|
||||
.. code::
|
||||
|
||||
.
|
||||
├── C
|
||||
│  ├── Assign
|
||||
@@ -421,7 +479,7 @@ tar with different compressions: ``tar Jcf /tmp/base.tar.xz .``, ``tar zcf
|
||||
/tmp/base.tgz .``, ``tar jcf /tmp/base.tar.bz2 .``. It should work with all
|
||||
mentioned at the beginning of this document archivers.
|
||||
|
||||
Starting point is in ``S/startup-sequence`` file, where eventually
|
||||
Starting point is in ``S/startup-sequence`` file, where eventually
|
||||
``S/whdload-startup`` is executed, which will be created by fs-uae-warpper
|
||||
before execution by fs-uae.
|
||||
|
||||
@@ -449,7 +507,7 @@ And execution is as usual:
|
||||
|
||||
Now, similar to the archive module, it will create temporary directory, unpack
|
||||
base image there, unpack WHDLoad game archive, search for slave file, and
|
||||
preapre ``s:whdload-startup``, and finally pass all the configuration to
|
||||
prepare ``s:whdload-startup``, and finally pass all the configuration to
|
||||
fs-uae.
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ the temporary one.
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from fs_uae_wrapper import base
|
||||
from fs_uae_wrapper import utils
|
||||
from fs_uae_wrapper import base, utils
|
||||
|
||||
|
||||
class Wrapper(base.ArchiveBase):
|
||||
|
||||
@@ -4,11 +4,9 @@ Base class for all wrapper modules
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from fs_uae_wrapper import utils
|
||||
from fs_uae_wrapper import path
|
||||
from fs_uae_wrapper import path, utils
|
||||
|
||||
|
||||
class Base(object):
|
||||
@@ -46,7 +44,7 @@ class Base(object):
|
||||
if not self._validate_options():
|
||||
return False
|
||||
|
||||
self.dir = tempfile.mkdtemp()
|
||||
self.dir = tempfile.mkdtemp(prefix='fs-uae-wrapper-')
|
||||
self._normalize_options()
|
||||
self._set_assets_paths()
|
||||
|
||||
@@ -84,7 +82,7 @@ class Base(object):
|
||||
"""execute fs-uae"""
|
||||
curdir = os.path.abspath('.')
|
||||
os.chdir(self.dir)
|
||||
utils.run_command(['fs-uae'] + self.fsuae_options.list())
|
||||
utils.run_command(['fs-uae', *self.fsuae_options.list()])
|
||||
os.chdir(curdir)
|
||||
return True
|
||||
|
||||
@@ -325,6 +323,6 @@ class ArchiveBase(Base):
|
||||
for ext in ('.7z', '.lha', '.lzx', '.zip', '.rar', '.tar', '.tgz',
|
||||
'.tar.gz', '.tar.bz2', '.tar.xz'):
|
||||
if ((basename + ext).lower() == fname.lower() and
|
||||
basename == os.path.splitext(fname)[0]):
|
||||
basename == os.path.splitext(fname)[0]):
|
||||
return fname
|
||||
return None
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
"""
|
||||
File archive classes
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
from fs_uae_wrapper import path
|
||||
|
||||
|
||||
class Archive(object):
|
||||
"""Base class for archive support"""
|
||||
ADD = ['a']
|
||||
EXTRACT = ['x']
|
||||
ADD = ('a',)
|
||||
EXTRACT = ('x',)
|
||||
ARCH = 'false'
|
||||
|
||||
def __init__(self):
|
||||
@@ -27,8 +27,8 @@ class Archive(object):
|
||||
files = files if files else ['.']
|
||||
logging.debug("Calling `%s %s %s %s'.", self._compress,
|
||||
" ".join(self.ADD), arch_name, " ".join(files))
|
||||
result = subprocess.call([self._compress] + self.ADD + [arch_name]
|
||||
+ files)
|
||||
result = subprocess.call([self._compress, *self.ADD, arch_name,
|
||||
*files])
|
||||
if result != 0:
|
||||
logging.error("Unable to create archive `%s'.", arch_name)
|
||||
return False
|
||||
@@ -44,8 +44,7 @@ class Archive(object):
|
||||
|
||||
logging.debug("Calling `%s %s %s'.", self._compress,
|
||||
" ".join(self.ADD), arch_name)
|
||||
result = subprocess.call([self._decompress] + self.EXTRACT +
|
||||
[arch_name])
|
||||
result = subprocess.call([self._decompress, *self.EXTRACT, arch_name])
|
||||
if result != 0:
|
||||
logging.error("Unable to extract archive `%s'.", arch_name)
|
||||
return False
|
||||
@@ -53,16 +52,16 @@ class Archive(object):
|
||||
|
||||
|
||||
class TarArchive(Archive):
|
||||
ADD = ['cf']
|
||||
EXTRACT = ['xf']
|
||||
ADD = ('cf',)
|
||||
EXTRACT = ('xf',)
|
||||
ARCH = 'tar'
|
||||
|
||||
def create(self, arch_name, files=None):
|
||||
files = files if files else sorted(os.listdir('.'))
|
||||
logging.debug("Calling `%s %s %s %s'.", self._compress,
|
||||
" ".join(self.ADD), arch_name, " ".join(files))
|
||||
result = subprocess.call([self._compress] + self.ADD + [arch_name] +
|
||||
files)
|
||||
result = subprocess.call([self._compress, *self.ADD, arch_name,
|
||||
*files])
|
||||
if result != 0:
|
||||
logging.error("Unable to create archive `%s'.", arch_name)
|
||||
return False
|
||||
@@ -70,15 +69,15 @@ class TarArchive(Archive):
|
||||
|
||||
|
||||
class TarGzipArchive(TarArchive):
|
||||
ADD = ['zcf']
|
||||
ADD = ('zcf',)
|
||||
|
||||
|
||||
class TarBzip2Archive(TarArchive):
|
||||
ADD = ['jcf']
|
||||
ADD = ('jcf',)
|
||||
|
||||
|
||||
class TarXzArchive(TarArchive):
|
||||
ADD = ['Jcf']
|
||||
ADD = ('Jcf',)
|
||||
|
||||
|
||||
class LhaArchive(Archive):
|
||||
@@ -86,8 +85,8 @@ class LhaArchive(Archive):
|
||||
|
||||
|
||||
class ZipArchive(Archive):
|
||||
ADD = ['a', '-tzip']
|
||||
ARCH = ['7z', 'zip']
|
||||
ADD = ('a', '-tzip')
|
||||
ARCH = ('7z', 'zip')
|
||||
|
||||
def __init__(self):
|
||||
super(ZipArchive, self).__init__()
|
||||
@@ -102,7 +101,7 @@ class SevenZArchive(Archive):
|
||||
|
||||
|
||||
class LzxArchive(Archive):
|
||||
EXTRACT = ['-x']
|
||||
EXTRACT = ('-x',)
|
||||
ARCH = 'unlzx'
|
||||
|
||||
@classmethod
|
||||
@@ -113,7 +112,7 @@ class LzxArchive(Archive):
|
||||
|
||||
|
||||
class RarArchive(Archive):
|
||||
ARCH = ['rar', 'unrar']
|
||||
ARCH = ('rar', 'unrar')
|
||||
|
||||
def create(self, arch_name, files=None):
|
||||
files = files if files else sorted(os.listdir('.'))
|
||||
@@ -124,8 +123,8 @@ class RarArchive(Archive):
|
||||
|
||||
logging.debug("Calling `%s %s %s %s'.", self._compress,
|
||||
" ".join(self.ADD), arch_name, " ".join(files))
|
||||
result = subprocess.call([self._compress] + self.ADD + [arch_name] +
|
||||
files)
|
||||
result = subprocess.call([self._compress, *self.ADD, arch_name,
|
||||
*files])
|
||||
if result != 0:
|
||||
logging.error("Unable to create archive `%s'.", arch_name)
|
||||
return False
|
||||
@@ -134,7 +133,7 @@ class RarArchive(Archive):
|
||||
|
||||
class Archivers(object):
|
||||
"""Archivers class"""
|
||||
archivers = [{'arch': TarArchive, 'name': 'tar', 'ext': ['tar']},
|
||||
archivers = ({'arch': TarArchive, 'name': 'tar', 'ext': ['tar']},
|
||||
{'arch': TarGzipArchive, 'name': 'tgz',
|
||||
'ext': ['tar.gz', 'tgz']},
|
||||
{'arch': TarBzip2Archive, 'name': 'tar.bz2',
|
||||
@@ -144,7 +143,7 @@ class Archivers(object):
|
||||
{'arch': SevenZArchive, 'name': '7z', 'ext': ['7z']},
|
||||
{'arch': ZipArchive, 'name': 'zip', 'ext': ['zip']},
|
||||
{'arch': LhaArchive, 'name': 'lha', 'ext': ['lha', 'lzh']},
|
||||
{'arch': LzxArchive, 'name': 'lzx', 'ext': ['lzx']}]
|
||||
{'arch': LzxArchive, 'name': 'lzx', 'ext': ['lzx']})
|
||||
|
||||
@classmethod
|
||||
def get(cls, extension):
|
||||
|
||||
@@ -20,7 +20,7 @@ class MessageGui(tkinter.Tk):
|
||||
# Display window without decorations
|
||||
self.wm_attributes('-type', 'splash')
|
||||
|
||||
self.frame = ttk.Frame(self, padding=5, borderwidth=0)
|
||||
self.frame = tkinter.ttk.Frame(self, padding=5, borderwidth=0)
|
||||
self.frame.grid()
|
||||
tkinter.ttk.Label(self.frame, text=msg, relief="ridge",
|
||||
padding=10).grid()
|
||||
|
||||
18
fs_uae_wrapper/nogui_message.py
Normal file
18
fs_uae_wrapper/nogui_message.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Display message as simple text on console
|
||||
"""
|
||||
import sys
|
||||
|
||||
|
||||
class Message:
|
||||
"""Just a fake message window for systems without TK"""
|
||||
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
self._process = None
|
||||
|
||||
def show(self):
|
||||
sys.stdout.write(self.msg + "\n")
|
||||
|
||||
def close(self):
|
||||
return None
|
||||
@@ -2,8 +2,7 @@
|
||||
Simple class for executing fs-uae with specified parameters. This is a
|
||||
failsafe class for running fs-uae.
|
||||
"""
|
||||
from fs_uae_wrapper import base
|
||||
from fs_uae_wrapper import utils
|
||||
from fs_uae_wrapper import base, utils
|
||||
|
||||
|
||||
class Wrapper(base.Base):
|
||||
@@ -17,8 +16,8 @@ class Wrapper(base.Base):
|
||||
|
||||
def _run_emulator(self):
|
||||
"""execute fs-uae"""
|
||||
utils.run_command(['fs-uae'] + [self.conf_file] +
|
||||
self.fsuae_options.list())
|
||||
utils.run_command(['fs-uae', self.conf_file,
|
||||
*self.fsuae_options.list()])
|
||||
|
||||
def clean(self):
|
||||
"""Do the cleanup. Here - just do nothing"""
|
||||
|
||||
@@ -8,8 +8,11 @@ import pathlib
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from fs_uae_wrapper import message
|
||||
from fs_uae_wrapper import file_archive
|
||||
try:
|
||||
from fs_uae_wrapper.message import Message
|
||||
except ModuleNotFoundError:
|
||||
from fs_uae_wrapper.nogui_message import Message
|
||||
|
||||
|
||||
class CmdOption(dict):
|
||||
@@ -65,7 +68,7 @@ def operate_archive(arch_name, operation, text, params):
|
||||
if archiver is None:
|
||||
return False
|
||||
|
||||
msg = message.Message(text)
|
||||
msg = Message(text)
|
||||
if text:
|
||||
msg.show()
|
||||
|
||||
|
||||
@@ -5,10 +5,8 @@ It will use compressed base image and compressed directories.
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from fs_uae_wrapper import base
|
||||
from fs_uae_wrapper import utils
|
||||
from fs_uae_wrapper import base, utils
|
||||
|
||||
|
||||
class Wrapper(base.ArchiveBase):
|
||||
@@ -62,7 +60,6 @@ class Wrapper(base.ArchiveBase):
|
||||
"location.", base_image)
|
||||
return False
|
||||
|
||||
title = self._get_title()
|
||||
curdir = os.path.abspath('.')
|
||||
os.chdir(self.dir)
|
||||
result = utils.extract_archive(base_image)
|
||||
@@ -83,12 +80,20 @@ class Wrapper(base.ArchiveBase):
|
||||
# find slave name
|
||||
slave_fname = None
|
||||
slave_path = None
|
||||
case_insensitvie_map = {}
|
||||
|
||||
# build case insensitive map of paths and find the slave file
|
||||
for root, dirnames, fnames in os.walk('.'):
|
||||
for dirname in dirnames:
|
||||
full_path = os.path.normpath(os.path.join(root, dirname))
|
||||
case_insensitvie_map[full_path.lower()] = full_path
|
||||
|
||||
for root, dirs, fnames in os.walk('.'):
|
||||
for fname in fnames:
|
||||
if fname.lower().endswith('.slave'):
|
||||
full_path = os.path.normpath(os.path.join(root, fname))
|
||||
case_insensitvie_map[full_path.lower()] = full_path
|
||||
if not slave_fname and fname.lower().endswith('.slave'):
|
||||
slave_path, slave_fname = os.path.normpath(root), fname
|
||||
break
|
||||
|
||||
if slave_fname is None:
|
||||
logging.error("Cannot find .slave file in archive.")
|
||||
return False
|
||||
@@ -97,8 +102,8 @@ class Wrapper(base.ArchiveBase):
|
||||
icon_fname = None
|
||||
for fname in os.listdir(slave_path):
|
||||
if (fname.lower().endswith('.info') and
|
||||
os.path.splitext(slave_fname)[0].lower() ==
|
||||
os.path.splitext(fname)[0].lower()):
|
||||
os.path.splitext(slave_fname)[0].lower() ==
|
||||
os.path.splitext(fname)[0].lower()):
|
||||
icon_fname = fname
|
||||
break
|
||||
if icon_fname is None:
|
||||
@@ -106,10 +111,26 @@ class Wrapper(base.ArchiveBase):
|
||||
"archive.", slave_fname)
|
||||
return False
|
||||
|
||||
# Write startup file
|
||||
with open("S/whdload-startup", "w") as fobj:
|
||||
fobj.write(f"cd {slave_path}\n")
|
||||
fobj.write(f"C:kgiconload {icon_fname}\n")
|
||||
# find proper way to handle slave
|
||||
# 1. check if there are user provided params
|
||||
contents = f"cd {slave_path}\n"
|
||||
if self.all_options.get('wrapper_whdload_options'):
|
||||
contents = (f"{contents}"
|
||||
f"C:whdload "
|
||||
f"{self.all_options['wrapper_whdload_options']} "
|
||||
f"Slave={slave_fname}\n")
|
||||
else:
|
||||
# no params, find if kgiconload is available
|
||||
if case_insensitvie_map.get('c/kgiconload'):
|
||||
contents = f"{contents}C:kgiconload {icon_fname}\n"
|
||||
else:
|
||||
# if not, just add common defaults
|
||||
contents = (f"{contents}C:whdload Preload "
|
||||
f"Slave={slave_fname}\n")
|
||||
|
||||
fname = os.path.join(case_insensitvie_map.get('s'), 'whdload-startup')
|
||||
with open(fname, "w") as fobj:
|
||||
fobj.write(contents)
|
||||
|
||||
os.chdir(curdir)
|
||||
return True
|
||||
|
||||
@@ -7,8 +7,7 @@ import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from fs_uae_wrapper import utils
|
||||
from fs_uae_wrapper import WRAPPER_KEY
|
||||
from fs_uae_wrapper import WRAPPER_KEY, utils
|
||||
|
||||
|
||||
def setup_logger(options):
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
[build-system]
|
||||
requires = ["setuptools >= 61.0"]
|
||||
requires = ["setuptools >= 77.0", "wheel", "setuptools-git-versioning"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "fs-uae-wrapper"
|
||||
authors = [{name = "Roman Dobosz", email = "gryf73@gmail.com"}]
|
||||
license = {text = "BSD"}
|
||||
license = "BSD-3-Clause"
|
||||
description = "Automate archives support and state saves for fs-uae"
|
||||
readme = "README.rst"
|
||||
requires-python = ">=3.8"
|
||||
keywords = ["uae", "fs-uae", "amiga", "emulator", "wrapper"]
|
||||
version = "0.10.0"
|
||||
dynamic = ["version"]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
@@ -34,7 +33,18 @@ Homepage = "https://github.com/gryf/fs-uae-wrapper"
|
||||
fs-uae-wrapper = "fs_uae_wrapper.wrapper:run"
|
||||
|
||||
[tool.setuptools]
|
||||
py-modules = ["fs_uae_wrapper"]
|
||||
packages = ["fs_uae_wrapper"]
|
||||
|
||||
[tool.distutils.bdist_wheel]
|
||||
universal = true
|
||||
|
||||
[tool.setuptools-git-versioning]
|
||||
enabled = true
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"F", # pyflakes
|
||||
"E", # pycodestyle
|
||||
"I", # isort
|
||||
"RUF", # ruff-specific rules
|
||||
]
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
pytest
|
||||
pytest-cov
|
||||
pytest-pep8
|
||||
coverage
|
||||
flake8
|
||||
@@ -1,11 +1,9 @@
|
||||
import os
|
||||
import shutil
|
||||
from tempfile import mkdtemp
|
||||
from unittest import TestCase
|
||||
from unittest import mock
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from fs_uae_wrapper import archive
|
||||
from fs_uae_wrapper import utils
|
||||
from fs_uae_wrapper import archive, utils
|
||||
|
||||
|
||||
class TestArchive(TestCase):
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
from tempfile import mkstemp, mkdtemp
|
||||
from unittest import TestCase
|
||||
from unittest import mock
|
||||
import sys
|
||||
from tempfile import mkdtemp, mkstemp
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from fs_uae_wrapper import base
|
||||
from fs_uae_wrapper import utils
|
||||
from fs_uae_wrapper import base, utils
|
||||
|
||||
|
||||
class TestBase(TestCase):
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
from unittest import TestCase
|
||||
from unittest import mock
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from fs_uae_wrapper import cd32
|
||||
from fs_uae_wrapper import utils
|
||||
from fs_uae_wrapper import cd32, utils
|
||||
|
||||
|
||||
class TestCD32(TestCase):
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import os
|
||||
import shutil
|
||||
from tempfile import mkdtemp
|
||||
from unittest import TestCase
|
||||
from unittest import mock
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from fs_uae_wrapper import file_archive
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from unittest import TestCase
|
||||
import os
|
||||
from unittest import mock
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from fs_uae_wrapper import message
|
||||
from fs_uae_wrapper import nogui_message
|
||||
|
||||
if os.environ.get('DISPLAY'):
|
||||
import tkinter as tk
|
||||
@@ -39,6 +39,19 @@ class TestMessage(TestCase):
|
||||
msg._process.join.assert_called_once()
|
||||
|
||||
|
||||
class TestNOPMessage(TestCase):
|
||||
|
||||
@mock.patch('sys.stdout.write')
|
||||
def test_show(self, stdout_write):
|
||||
msg = nogui_message.Message('display that')
|
||||
msg.show()
|
||||
stdout_write.assert_called_once()
|
||||
|
||||
def test_close(self):
|
||||
msg = nogui_message.Message('display that')
|
||||
self.assertIsNone(msg.close())
|
||||
|
||||
|
||||
if os.environ.get('DISPLAY'):
|
||||
# Tkinter needs graphic environment for the widgets
|
||||
class TestSpawn(TestCase):
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
from unittest import TestCase
|
||||
from unittest import mock
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from fs_uae_wrapper import plain
|
||||
from fs_uae_wrapper import utils
|
||||
from fs_uae_wrapper import plain, utils
|
||||
|
||||
|
||||
class TestPlainModule(TestCase):
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import os
|
||||
import shutil
|
||||
from tempfile import mkdtemp
|
||||
from unittest import TestCase
|
||||
from unittest import mock
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from fs_uae_wrapper import savestate
|
||||
from fs_uae_wrapper import utils
|
||||
from fs_uae_wrapper import savestate, utils
|
||||
|
||||
|
||||
class TestSaveState(TestCase):
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import os
|
||||
import sys
|
||||
from tempfile import mkstemp, mkdtemp
|
||||
from unittest import TestCase
|
||||
import shutil
|
||||
from unittest import mock
|
||||
import sys
|
||||
from tempfile import mkdtemp, mkstemp
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from fs_uae_wrapper import utils
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import os
|
||||
import shutil
|
||||
from tempfile import mkdtemp
|
||||
from unittest import TestCase
|
||||
from unittest import mock
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from fs_uae_wrapper import whdload
|
||||
from fs_uae_wrapper import utils
|
||||
from fs_uae_wrapper import utils, whdload
|
||||
|
||||
|
||||
class TestWHDLoad(TestCase):
|
||||
@@ -163,7 +161,7 @@ class TestWHDLoad(TestCase):
|
||||
@mock.patch('os.chdir')
|
||||
def test_find_slave_no_slave_file(self, chdir, walk):
|
||||
walk.return_value = [(".", ('game'), ()),
|
||||
('./game', (), ('foo', 'bar', 'baz'))]
|
||||
('./game', (), ('foo', 'bar', 'baz'))]
|
||||
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
|
||||
self.assertFalse(wrapper._find_slave())
|
||||
|
||||
@@ -173,20 +171,63 @@ class TestWHDLoad(TestCase):
|
||||
def test_find_slave_no_corresponding_icon(self, chdir, walk, listdir):
|
||||
contents = ('foo', 'bar', 'baz.slave')
|
||||
walk.return_value = [(".", ('game'), ()),
|
||||
('./game', (), contents)]
|
||||
('./game', (), contents)]
|
||||
listdir.return_value = contents
|
||||
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
|
||||
self.assertFalse(wrapper._find_slave())
|
||||
|
||||
@mock.patch('builtins.open')
|
||||
@mock.patch('os.listdir')
|
||||
@mock.patch('os.walk')
|
||||
@mock.patch('os.chdir')
|
||||
def test_find_slave_success(self, chdir, walk, listdir, bopen):
|
||||
def test_find_slave_success(self, chdir, walk, listdir):
|
||||
contents = ('foo', 'bar', 'baz.slave', 'baz.info')
|
||||
walk.return_value = [(".", ('game'), ()),
|
||||
('./game', (), contents)]
|
||||
_open = mock.mock_open()
|
||||
walk.return_value = [(".", ('C', 'S', 'game'), ()),
|
||||
('./C', (), ('Assign', 'kgiconload')),
|
||||
('./S', (), ()),
|
||||
('./game', (), contents)]
|
||||
listdir.return_value = contents
|
||||
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
|
||||
self.assertTrue(wrapper._find_slave())
|
||||
bopen.assert_called_once()
|
||||
with mock.patch('builtins.open', _open):
|
||||
self.assertTrue(wrapper._find_slave())
|
||||
handle = _open()
|
||||
handle.write.assert_called_once_with('cd game\n'
|
||||
'C:kgiconload baz.info\n')
|
||||
|
||||
@mock.patch('os.listdir')
|
||||
@mock.patch('os.walk')
|
||||
@mock.patch('os.chdir')
|
||||
def test_find_slave_minial(self, chdir, walk, listdir):
|
||||
contents = ('foo', 'bar', 'baz.slave', 'baz.info')
|
||||
_open = mock.mock_open()
|
||||
walk.return_value = [(".", ('C', 'S', 'game'), ()),
|
||||
('./C', (), ('Assign', 'WHDLoad')),
|
||||
('./S', (), ()),
|
||||
('./game', (), contents)]
|
||||
listdir.return_value = contents
|
||||
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
|
||||
with mock.patch('builtins.open', _open):
|
||||
self.assertTrue(wrapper._find_slave())
|
||||
handle = _open()
|
||||
handle.write.assert_called_once_with('cd game\nC:whdload Preload '
|
||||
'Slave=baz.slave\n')
|
||||
|
||||
@mock.patch('os.listdir')
|
||||
@mock.patch('os.walk')
|
||||
@mock.patch('os.chdir')
|
||||
def test_find_custom_options(self, chdir, walk, listdir):
|
||||
contents = ('foo', 'bar', 'baz.slave', 'baz.info')
|
||||
_open = mock.mock_open()
|
||||
walk.return_value = [(".", ('C', 'S', 'game'), ()),
|
||||
('./C', (), ('Assign', 'WHDLoad')),
|
||||
('./S', (), ()),
|
||||
('./game', (), contents)]
|
||||
listdir.return_value = contents
|
||||
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
|
||||
whdl_opts = 'Preload SplashDelay=0 MMU PAL'
|
||||
wrapper.all_options['wrapper_whdload_options'] = whdl_opts
|
||||
with mock.patch('builtins.open', _open):
|
||||
self.assertTrue(wrapper._find_slave())
|
||||
handle = _open()
|
||||
handle.write.assert_called_once_with(f'cd game\nC:whdload {whdl_opts} '
|
||||
'Slave=baz.slave\n')
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import os
|
||||
import sys
|
||||
from tempfile import mkstemp, mkdtemp
|
||||
from unittest import TestCase
|
||||
import shutil
|
||||
from unittest import mock
|
||||
import sys
|
||||
from tempfile import mkdtemp, mkstemp
|
||||
from unittest import TestCase, mock
|
||||
|
||||
from fs_uae_wrapper import wrapper
|
||||
|
||||
|
||||
Reference in New Issue
Block a user