-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsystems.py
More file actions
428 lines (325 loc) · 12.8 KB
/
Copy pathsystems.py
File metadata and controls
428 lines (325 loc) · 12.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
#!/usr/bin/env python3
"""System definitions for SGM.
All system and emulator definitions are loaded from emulators.json.
This module provides backward-compatible interfaces to the emulator plugin system.
System definitions can be queried directly or through the emulator registry.
"""
import logging
from pathlib import Path
from typing import Dict, List, Optional, Set
from emulators import (
get_registry,
get_system,
get_system_emulator,
SystemPlugin,
EmulatorPlugin,
list_all_systems,
)
logger = logging.getLogger(__name__)
# ═══════════════════════════════════════════════════════════════════════
# Backward Compatibility Layer
# ═══════════════════════════════════════════════════════════════════════
def get_system_def(system_id: str) -> Optional[SystemPlugin]:
"""Get a system definition by ID (backward compatible API).
Args:
system_id: The system identifier (e.g., 'n64', 'psx').
Returns:
The SystemPlugin for the system, or None.
"""
return get_system(system_id)
def get_system_ids() -> List[str]:
"""Get all known system IDs."""
registry = get_registry()
return list(registry.list_systems().keys())
# Alias for compatibility
list_supported_systems = get_system_ids
def get_system_info(system_id: str) -> Optional[Dict]:
"""Get system information as a dictionary.
Args:
system_id: The system identifier.
Returns:
Dictionary with system info, or None if not found.
"""
system = get_system(system_id)
if system:
return system.get_info()
return None
def get_extensions(system_id: str) -> Set[str]:
"""Get supported file extensions for a system."""
system = get_system(system_id)
if system:
return system.extensions
return set()
def is_rom_file(system_id: str, filename: str) -> bool:
"""Check if a filename is a valid ROM for a system."""
system = get_system(system_id)
if system:
return system.is_rom_file(filename)
return False
def get_default_emulator_id(system_id: str) -> str:
"""Get the default emulator ID for a system."""
system = get_system(system_id)
if system:
return system.default_emulator_id
return ""
def get_emulator_plugin(system_id: str, emulator_id: Optional[str] = None) -> Optional[EmulatorPlugin]:
"""Get the emulator plugin for a system.
Args:
system_id: The system name.
emulator_id: Optional specific emulator ID (overrides default).
Returns:
The EmulatorPlugin, or None.
"""
return get_system_emulator(system_id, emulator_id)
def list_systems() -> Dict[str, SystemPlugin]:
"""List all available systems."""
return get_registry().list_systems()
class _SYSTEMS:
"""Backward-compatible SYSTEMS dict wrapper.
Allows SYSTEMS.keys(), SYSTEMS.items(), SYSTEMS[...], etc.
"""
def __iter__(self):
return get_registry().list_systems().keys().__iter__()
def __getitem__(self, key):
return get_registry().list_systems()[key]
def __contains__(self, key):
return key in get_registry().list_systems()
def keys(self):
return get_registry().list_systems().keys()
def values(self):
return get_registry().list_systems().values()
def items(self):
return get_registry().list_systems().items()
def get(self, key, default=None):
return get_registry().list_systems().get(key, default)
# Backward-compatible SYSTEMS constant
SYSTEMS = _SYSTEMS()
def get_steam_category(system_id: str) -> str:
"""Get the Steam library category for a system."""
system = get_system(system_id)
if system:
return system.steam_category
return system_id.upper()
def list_supported_systems() -> List[str]:
"""Get list of supported system names."""
return get_system_ids()
def all_category_tags(system_id: str) -> Set[str]:
"""Return all tag strings to match when purging old shortcuts.
Includes the current category name plus any legacy aliases.
"""
system = get_system(system_id)
if system:
return {system.steam_category}
return {system_id.upper()}
# ═══════════════════════════════════════════════════════════════════════
# Legacy SystemDef wrapper (for code compatibility)
# ═══════════════════════════════════════════════════════════════════════
class SystemDef:
"""Backward-compatible SystemDef wrapper around SystemPlugin.
This class provides the same interface as the old SystemDef dataclass
for code that depends on the old structure.
"""
def __init__(self, system_id: str):
"""Create a SystemDef wrapper for a system.
Args:
system_id: The system identifier.
"""
self._system_id = system_id
self._plugin = get_system(system_id)
@property
def name(self) -> str:
"""Folder name."""
return self._system_id
@property
def fullname(self) -> str:
"""Display name."""
if self._plugin:
return self._plugin.fullname
return self._system_id
@property
def manufacturer(self) -> str:
"""System manufacturer."""
if self._plugin:
return self._plugin.manufacturer
return ""
@property
def extensions(self) -> Set[str]:
"""Valid ROM extensions."""
if self._plugin:
return self._plugin.extensions
return set()
@property
def screenscraper_id(self) -> Optional[int]:
"""ScreenScraper platform ID."""
if self._plugin:
return self._plugin.screenscraper_id
return None
@property
def thegamesdb_id(self) -> Optional[str]:
"""TheGamesDB platform ID."""
if self._plugin:
return self._plugin.thegamesdb_id
return None
@property
def steam_category(self) -> Optional[str]:
"""Steam category tag."""
if self._plugin:
return self._plugin.steam_category
return None
@property
def legacy_tags(self) -> Set[str]:
"""Legacy SRM/external tag names."""
# Legacy tags not stored in new config, return empty set
return set()
@property
def scan_as_dirs(self) -> bool:
"""Games installed as app directories (Vita3K style)."""
if self._plugin:
return self._plugin.scan_as_dirs
return False
@property
def default_emulator(self) -> Optional[str]:
"""Default emulator plugin ID."""
if self._plugin:
return self._plugin.default_emulator_id
return None
@property
def release_year(self) -> Optional[int]:
"""Release year."""
if self._plugin:
return self._plugin.release_year
return None
@property
def hardware_type(self) -> str:
"""Hardware type."""
if self._plugin:
return self._plugin.hardware_type
return "console"
@property
def skip_extensions(self) -> Set[str]:
"""Extensions to skip (save files, etc.)."""
return {
".srm", ".state", ".state1", ".state2", ".state3", ".state4",
".state5", ".sav", ".oops", ".cfg", ".nfo", ".txt", ".xml",
".png", ".jpg", ".jpeg", ".gif", ".bmp", ".db", ".ini",
".log", ".bak", ".old", ".sync-conflict"
}
@property
def emulator(self):
"""Get EmulatorConfig-like object for backward compatibility."""
from systems import get_emulator_plugin
plugin = get_emulator_plugin(self._system_id)
if plugin:
return EmulatorConfigWrapper(plugin)
return None
def is_rom_file(self, filename: str) -> bool:
"""Check if a filename is a valid ROM for this system."""
if self._plugin:
return self._plugin.is_rom_file(filename)
lower = filename.lower()
ext = Path(filename).suffix.lower()
if filename.startswith("."):
return False
if ext in self.skip_extensions:
return False
if ext == ".bin" and "(Track" in filename:
return False
return ext in self.extensions
def get_steam_category(self) -> str:
"""Get the Steam library category for this system."""
return self.steam_category or self.fullname
def all_category_tags(self) -> Set[str]:
"""Return all tag strings to match when purging old shortcuts."""
return {self.get_steam_category()} | self.legacy_tags
def get_default_emulator_id(self) -> str:
"""Get the default emulator plugin ID for this system."""
if self.default_emulator:
return self.default_emulator
if self._plugin and self._plugin.emulator:
emulator = self._plugin.emulator
if hasattr(emulator, 'core'):
return f"retroarch/{emulator.core}"
if emulator.flatpak_id:
return emulator.flatpak_id
return "retroarch"
def get_retroarch_core(self) -> Optional[str]:
"""Get the RetroArch core if this system uses RetroArch."""
if self._plugin and self._plugin.emulator:
if hasattr(self._plugin.emulator, 'core'):
return self._plugin.emulator.core
return None
class EmulatorConfigWrapper:
"""Backward-compatible wrapper around EmulatorPlugin.
Provides the same interface as the old EmulatorConfig dataclass.
"""
def __init__(self, plugin: EmulatorPlugin):
self._plugin = plugin
@property
def emulator(self) -> str:
"""Executable name or path."""
if self._plugin.flatpak_id:
return f"/usr/bin/flatpak run {self._plugin.flatpak_id}"
if self._plugin.executable:
return self._plugin.executable
return self._plugin.id
@property
def core(self) -> Optional[str]:
"""RetroArch core name (if applicable)."""
if hasattr(self._plugin, 'core'):
return self._plugin.core
return None
@property
def flatpak_id(self) -> Optional[str]:
"""Flatpak app ID."""
return self._plugin.flatpak_id
@property
def launch_args(self) -> str:
"""Template for launch arguments."""
return self._plugin.launch_args
@property
def launch_mode(self) -> str:
"""Launch mode: 'rom' or 'title_id'."""
return self._plugin.launch_mode
def get_executable(self) -> str:
"""Get the full executable path/command."""
return self.emulator
def get_launch_options(self, rom_path: str) -> str:
"""Get launch options for a specific ROM."""
return self._plugin.launch_args.replace("{rom}", rom_path)
def get_steam_exe(self, rom_path: str) -> str:
"""Build the Steam shortcut 'exe' field in SRM-compatible format."""
return self._plugin.get_steam_exe(rom_path)
# ═══════════════════════════════════════════════════════════════════════
# Vita3K Helpers (kept for backward compatibility)
# ═══════════════════════════════════════════════════════════════════════
_VITA3K_BINARY: Optional[str] = None
def _find_vita3k_binary() -> str:
"""Find the Vita3K executable path.
Delegates to Vita3KPlugin.find_executable() which searches
multiple known locations and falls back to PATH.
"""
global _VITA3K_BINARY
if _VITA3K_BINARY is not None:
return _VITA3K_BINARY
from emulators import get_emulator
plugin = get_emulator("vita3k")
if plugin and hasattr(plugin, 'find_executable'):
try:
_VITA3K_BINARY = plugin.find_executable()
return _VITA3K_BINARY
except FileNotFoundError:
pass
_VITA3K_BINARY = 'Vita3K'
return _VITA3K_BINARY
def find_vita3k_data_dir() -> Optional[Path]:
"""Find the Vita3K data directory containing installed games.
Delegates to Vita3KPlugin.data_dir which checks config.yml
pref-path and known data directory locations.
"""
from emulators import get_emulator
plugin = get_emulator("vita3k")
if plugin and hasattr(plugin, 'data_dir'):
result = plugin.data_dir
if result:
return result
return None