Preserve installed MAME settings in isolated ordinary runs
This commit is contained in:
@@ -146,6 +146,10 @@ host-зависимости и проверять их перед упаковк
|
|||||||
разные локальные диски. Не требовать закрывать все чужие процессы MAME по
|
разные локальные диски. Не требовать закрывать все чужие процессы MAME по
|
||||||
общему шаблону: предотвращать конфликт только за конкретный изменяемый
|
общему шаблону: предотвращать конфликт только за конкретный изменяемый
|
||||||
носитель/IPC-сеанс. Закрытие MAME не должно терять лог и снимки теста.
|
носитель/IPC-сеанс. Закрытие MAME не должно терять лог и снимки теста.
|
||||||
|
Обычный `run_sprinter_mame.py` перед стартом копирует `cfg` и `nvram` из
|
||||||
|
`MAME_HOME` в каталог сессии, затем включает обе клавиатуры в копии
|
||||||
|
`sprinter.cfg`, сохраняя остальные настройки. Исходная установка не меняется;
|
||||||
|
отладчик DAP по-прежнему начинает с чистого изолированного состояния.
|
||||||
|
|
||||||
## Граница MAME и отладки
|
## Граница MAME и отладки
|
||||||
|
|
||||||
|
|||||||
@@ -60,5 +60,25 @@ class LauncherTests(unittest.TestCase):
|
|||||||
self.assertEqual(keyboards, {':': '1', ':kbd:ms_naturl': '1'})
|
self.assertEqual(keyboards, {':': '1', ':kbd:ms_naturl': '1'})
|
||||||
|
|
||||||
|
|
||||||
|
def test_existing_config_is_merged_not_replaced(self):
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
path = Path(directory)/'sprinter.cfg'
|
||||||
|
path.write_text('\ufeff<?xml version="1.0"?>\n<mameconfig version="10">'
|
||||||
|
'<system name="sprinter"><input>'
|
||||||
|
'<keyboard tag=":" enabled="0" />'
|
||||||
|
'<port tag=":TURBO" type="OTHER" mask="1" defvalue="0" value="1" />'
|
||||||
|
'</input><video><target index="0" /></video>'
|
||||||
|
'</system></mameconfig>', encoding='utf-8')
|
||||||
|
write_keyboard_config(Path(directory))
|
||||||
|
root = ET.parse(path).getroot()
|
||||||
|
system = root.find('system')
|
||||||
|
self.assertEqual([item.tag for item in system.find('input')],
|
||||||
|
['keyboard', 'keyboard', 'port'])
|
||||||
|
keyboards = {item.attrib['tag']: item.attrib['enabled']
|
||||||
|
for item in system.findall('input/keyboard')}
|
||||||
|
self.assertEqual(keyboards, {':': '1', ':kbd:ms_naturl': '1'})
|
||||||
|
self.assertEqual(system.find('input/port').attrib['value'], '1')
|
||||||
|
self.assertIsNotNone(system.find('video/target'))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
+42
-11
@@ -5,6 +5,8 @@ from __future__ import annotations
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -77,16 +79,45 @@ def from_arguments(args) -> MameProfile:
|
|||||||
for name in names})
|
for name in names})
|
||||||
|
|
||||||
|
|
||||||
|
KEYBOARD_TAGS = (":", ":kbd:ms_naturl")
|
||||||
|
|
||||||
|
|
||||||
def write_keyboard_config(cfg_directory: Path) -> None:
|
def write_keyboard_config(cfg_directory: Path) -> None:
|
||||||
"""Включить обе клавиатуры Sprinter, включая прямой ввод в DSS."""
|
"""Включить обе клавиатуры Sprinter, включая прямой ввод в DSS.
|
||||||
|
|
||||||
|
Если в каталоге уже лежит sprinter.cfg (например, скопированный из
|
||||||
|
MAME_HOME/cfg), остальные его настройки сохраняются, а в копии
|
||||||
|
включаются только нужные клавиатуры.
|
||||||
|
"""
|
||||||
cfg_directory.mkdir(parents=True, exist_ok=True)
|
cfg_directory.mkdir(parents=True, exist_ok=True)
|
||||||
(cfg_directory / "sprinter.cfg").write_text(
|
path = cfg_directory / "sprinter.cfg"
|
||||||
'<?xml version="1.0"?>\n'
|
root = None
|
||||||
'<mameconfig version="10">\n'
|
if path.is_file():
|
||||||
' <system name="sprinter">\n'
|
try:
|
||||||
' <input>\n'
|
root = ET.parse(path).getroot()
|
||||||
' <keyboard tag=":" enabled="1" />\n'
|
except ET.ParseError as error:
|
||||||
' <keyboard tag=":kbd:ms_naturl" enabled="1" />\n'
|
print(f"mame_profile: {path} не разобран ({error}), создаётся заново",
|
||||||
' </input>\n'
|
file=sys.stderr)
|
||||||
' </system>\n'
|
if root is None or root.tag != "mameconfig":
|
||||||
'</mameconfig>\n', encoding="utf-8")
|
root = ET.Element("mameconfig", version="10")
|
||||||
|
system = next((item for item in root.findall("system")
|
||||||
|
if item.get("name") == "sprinter"), None)
|
||||||
|
if system is None:
|
||||||
|
system = ET.SubElement(root, "system", name="sprinter")
|
||||||
|
input_node = system.find("input")
|
||||||
|
if input_node is None:
|
||||||
|
input_node = ET.SubElement(system, "input")
|
||||||
|
keyboards = {item.get("tag"): item for item in input_node.findall("keyboard")}
|
||||||
|
for tag in KEYBOARD_TAGS:
|
||||||
|
keyboard = keyboards.get(tag)
|
||||||
|
if keyboard is None:
|
||||||
|
keyboard = ET.Element("keyboard", tag=tag)
|
||||||
|
# MAME ожидает keyboard перед port внутри input.
|
||||||
|
position = sum(1 for item in input_node if item.tag == "keyboard")
|
||||||
|
input_node.insert(position, keyboard)
|
||||||
|
keyboard.set("enabled", "1")
|
||||||
|
tree = ET.ElementTree(root)
|
||||||
|
ET.indent(tree, space=" ")
|
||||||
|
tree.write(path, encoding="utf-8", xml_declaration=True)
|
||||||
|
with path.open("a", encoding="utf-8") as stream:
|
||||||
|
stream.write("\n")
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
@@ -43,13 +44,21 @@ def main() -> int:
|
|||||||
state = Path(directory)
|
state = Path(directory)
|
||||||
for name in ("cfg", "nvram", "diff", "snapshot"):
|
for name in ("cfg", "nvram", "diff", "snapshot"):
|
||||||
(state / name).mkdir()
|
(state / name).mkdir()
|
||||||
|
# Настройки и NVRAM установленной среды берутся копией: MAME
|
||||||
|
# перезаписывает их при выходе, а MAME_HOME должен остаться нетронутым.
|
||||||
|
for name in ("cfg", "nvram"):
|
||||||
|
source = profile.home / name if profile.home else None
|
||||||
|
if source and source.is_dir():
|
||||||
|
shutil.copytree(source, state / name, dirs_exist_ok=True)
|
||||||
snapshots = Path(args.snapshot_dir).resolve() if args.snapshot_dir else state / "snapshot"
|
snapshots = Path(args.snapshot_dir).resolve() if args.snapshot_dir else state / "snapshot"
|
||||||
snapshots.mkdir(parents=True, exist_ok=True)
|
snapshots.mkdir(parents=True, exist_ok=True)
|
||||||
write_keyboard_config(state / "cfg")
|
write_keyboard_config(state / "cfg")
|
||||||
command = [str(profile.binary), "sprinter", "-noreadconfig",
|
command = [str(profile.binary), "sprinter", "-noreadconfig",
|
||||||
"-rompath", str(profile.rompath), "-bios", profile.bios,
|
"-rompath", str(profile.rompath), "-bios", profile.bios,
|
||||||
"-kbd", "ms_naturl,bios=sp2k", "-video", args.video,
|
"-kbd", "ms_naturl,bios=sp2k", "-video", args.video,
|
||||||
"-window", "-skip_gameinfo", "-beta:wd179x:0", "35hd",
|
"-window", "-skip_gameinfo",
|
||||||
|
"-beta:wd179x:0", "35hd",
|
||||||
|
"-beta:wd179x:1", "35hd",
|
||||||
"-flop2", str(profile.dss_image),
|
"-flop2", str(profile.dss_image),
|
||||||
"-hard1", str(profile.system_hdd_image)]
|
"-hard1", str(profile.system_hdd_image)]
|
||||||
if "floppy" in media:
|
if "floppy" in media:
|
||||||
|
|||||||
Reference in New Issue
Block a user