libretro.py 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {libretro_py-0.2.0 → libretro_py-0.3.0}/CHANGELOG.md +26 -0
- {libretro_py-0.2.0/src/libretro.py.egg-info → libretro_py-0.3.0}/PKG-INFO +1 -1
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/video/context.py +1 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/builder.py +207 -65
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/content/standard.py +5 -3
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/path/__init__.py +2 -1
- libretro_py-0.3.0/src/libretro/drivers/path/driver.py +64 -0
- libretro_py-0.3.0/src/libretro/drivers/path/explicit.py +164 -0
- libretro_py-0.3.0/src/libretro/drivers/path/temp.py +124 -0
- libretro_py-0.3.0/src/libretro/drivers/video/driver.py +471 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/video/multi.py +84 -15
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/video/opengl/moderngl.py +314 -210
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/video/software/base.py +1 -1
- libretro_py-0.3.0/src/libretro/py/test/_common.py +173 -0
- libretro_py-0.3.0/src/libretro/py/test/loads_content.py +41 -0
- libretro_py-0.3.0/src/libretro/py/test/runs.py +102 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0/src/libretro.py.egg-info}/PKG-INFO +1 -1
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro.py.egg-info/SOURCES.txt +4 -1
- libretro_py-0.2.0/src/libretro/drivers/path/default.py +0 -98
- libretro_py-0.2.0/src/libretro/drivers/path/driver.py +0 -38
- libretro_py-0.2.0/src/libretro/drivers/video/driver.py +0 -271
- libretro_py-0.2.0/src/libretro/py/test/_common.py +0 -35
- {libretro_py-0.2.0 → libretro_py-0.3.0}/LICENSE +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/MANIFEST.in +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/README.md +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/pyproject.toml +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/setup.cfg +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/setup.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/_typing.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/_utils.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/_utils.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/audio.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/av.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/camera.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/content.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/disk.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/environment.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/input/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/input/analog.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/input/device.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/input/joypad.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/input/keyboard.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/input/lightgun.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/input/mouse.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/input/pointer.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/led.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/location.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/log.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/memory.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/message.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/microphone.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/midi.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/netpacket.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/options.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/perf.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/power.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/proc.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/rumble.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/savestate.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/sensor.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/timing.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/user.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/vfs.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/video/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/video/frame.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/video/negotiate.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/api/video/render.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/core.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/audio/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/audio/array.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/audio/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/audio/wave.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/camera/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/camera/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/camera/generator.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/content/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/content/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/disk/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/disk/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/environment/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/environment/composite.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/environment/default.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/environment/dict.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/environment/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/input/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/input/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/input/generator.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/led/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/led/dict.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/led/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/location/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/location/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/location/generator.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/log/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/log/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/log/unformatted.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/message/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/message/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/message/logger.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/microphone/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/microphone/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/microphone/generator.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/midi/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/midi/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/midi/generator.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/netpacket/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/netpacket/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/netpacket/socket.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/options/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/options/dict.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/options/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/perf/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/perf/default.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/perf/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/power/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/power/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/rumble/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/rumble/default.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/rumble/interface.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/sensor/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/sensor/generator.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/sensor/interface.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/timing/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/timing/default.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/timing/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/user/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/user/default.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/user/driver.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/vfs/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/vfs/default.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/vfs/history.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/vfs/interface.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/video/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/video/opengl/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/video/opengl/moderngl_frag.glsl +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/video/opengl/moderngl_vertex.glsl +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/video/software/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/drivers/video/software/array.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/error.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/h.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/py/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/py/test/__init__.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/py/test/api_version.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/py/test/inits.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/py/test/loads.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/py/test/sets_callbacks.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/py/test/system_info.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro/session.py +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro.py.egg-info/dependency_links.txt +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro.py.egg-info/requires.txt +0 -0
- {libretro_py-0.2.0 → libretro_py-0.3.0}/src/libretro.py.egg-info/top_level.txt +0 -0
|
@@ -10,6 +10,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
10
10
|
> breaking changes may be introduced
|
|
11
11
|
> at any time without warning.
|
|
12
12
|
|
|
13
|
+
## [0.3.0] - 2024-09-25
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Add `TempDirPathDriver`.
|
|
18
|
+
- Added various runnable scripts for test purposes.
|
|
19
|
+
- Add a new guide for taking a capture with RenderDoc.
|
|
20
|
+
- Add labels to OpenGL objects created by `ModernGlVideoDriver`.
|
|
21
|
+
- Add debug groups to important methods within `ModernGlVideoDriver`.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **BREAKING:** Rename `DefaultPathDriver` to `ExplicitPathDriver`.
|
|
26
|
+
- Make `TempDirPathDriver` the default path driver used by `SessionBuilder`.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- Improved documentation for parts of `SessionBuilder` and `VideoDriver`
|
|
31
|
+
- Removed a `glClear` call in `ModernGlVideoDriver` that was left in by accident.
|
|
32
|
+
- Clear the `glGetError` queue at various places in `ModernGlVideoDriver`
|
|
33
|
+
to prevent PyOpenGL from misreporting errors that came from moderngl or the loaded core.
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
|
|
37
|
+
- **BREAKING:** Remove `VideoDriverInitArgs`.
|
|
38
|
+
|
|
13
39
|
## [0.2.0] - 2024-09-12
|
|
14
40
|
|
|
15
41
|
Thanks to @JSensebe for his contributions!
|
|
@@ -16,23 +16,21 @@ from libretro.api import (
|
|
|
16
16
|
ThrottleMode,
|
|
17
17
|
retro_device_power,
|
|
18
18
|
retro_game_info,
|
|
19
|
-
retro_hw_render_callback,
|
|
20
19
|
retro_throttle_state,
|
|
21
20
|
)
|
|
22
21
|
from libretro.core import Core
|
|
23
22
|
from libretro.drivers import (
|
|
24
23
|
ArrayAudioDriver,
|
|
25
|
-
ArrayVideoDriver,
|
|
26
24
|
AudioDriver,
|
|
27
25
|
CompositeEnvironmentDriver,
|
|
28
26
|
ConstantPowerDriver,
|
|
29
27
|
ContentDriver,
|
|
30
|
-
DefaultPathDriver,
|
|
31
28
|
DefaultPerfDriver,
|
|
32
29
|
DefaultTimingDriver,
|
|
33
30
|
DefaultUserDriver,
|
|
34
31
|
DictLedDriver,
|
|
35
32
|
DictOptionDriver,
|
|
33
|
+
DriverMap,
|
|
36
34
|
FileSystemInterface,
|
|
37
35
|
GeneratorInputDriver,
|
|
38
36
|
GeneratorLocationDriver,
|
|
@@ -57,31 +55,30 @@ from libretro.drivers import (
|
|
|
57
55
|
PowerDriver,
|
|
58
56
|
StandardContentDriver,
|
|
59
57
|
StandardFileSystemInterface,
|
|
58
|
+
TempDirPathDriver,
|
|
60
59
|
TimingDriver,
|
|
61
60
|
UnformattedLogDriver,
|
|
62
61
|
UserDriver,
|
|
63
62
|
VideoDriver,
|
|
64
|
-
VideoDriverInitArgs,
|
|
65
63
|
)
|
|
66
64
|
from libretro.session import Session
|
|
67
65
|
|
|
68
|
-
try:
|
|
69
|
-
from libretro.drivers.video import ModernGlVideoDriver
|
|
70
|
-
except ImportError:
|
|
71
|
-
ModernGlVideoDriver = None
|
|
72
|
-
|
|
73
66
|
|
|
74
67
|
class _DefaultType(Enum):
|
|
75
68
|
DEFAULT = auto()
|
|
76
69
|
|
|
77
70
|
|
|
78
71
|
DEFAULT = _DefaultType.DEFAULT
|
|
79
|
-
Default = Literal[_DefaultType.DEFAULT]
|
|
80
72
|
"""
|
|
81
|
-
A placeholder that indicates the default value for
|
|
82
|
-
When passed to
|
|
73
|
+
A placeholder that indicates the default value for one of :obj:`SessionBuilder`'s arguments.
|
|
74
|
+
When passed to one of :obj:`.SessionBuilder`'s ``with_`` methods,
|
|
75
|
+
it will use the default driver or argument configuration
|
|
76
|
+
unless otherwise noted.
|
|
83
77
|
"""
|
|
84
78
|
|
|
79
|
+
Default: TypeAlias = Literal[_DefaultType.DEFAULT]
|
|
80
|
+
|
|
81
|
+
|
|
85
82
|
T = TypeVar("T")
|
|
86
83
|
|
|
87
84
|
_RequiredFactory: TypeAlias = Callable[[], T]
|
|
@@ -100,7 +97,7 @@ InputDriverArg: TypeAlias = (
|
|
|
100
97
|
| InputStateIterator
|
|
101
98
|
| Default
|
|
102
99
|
)
|
|
103
|
-
VideoDriverArg: TypeAlias = _RequiredArg[VideoDriver] | Default
|
|
100
|
+
VideoDriverArg: TypeAlias = _RequiredArg[VideoDriver] | DriverMap | Default
|
|
104
101
|
ContentArg: TypeAlias = (
|
|
105
102
|
Content | SubsystemContent | _OptionalFactory[Content | SubsystemContent] | None
|
|
106
103
|
)
|
|
@@ -110,7 +107,7 @@ MessageDriverArg: TypeAlias = _OptionalArg[MessageInterface] | Logger
|
|
|
110
107
|
OptionDriverArg: TypeAlias = (
|
|
111
108
|
_OptionalArg[OptionDriver] | Mapping[AnyStr, AnyStr] | Literal[0, 1, 2]
|
|
112
109
|
)
|
|
113
|
-
PathDriverArg: TypeAlias =
|
|
110
|
+
PathDriverArg: TypeAlias = PathDriver | Callable[[Core], PathDriver | None] | Default | None
|
|
114
111
|
LogDriverArg: TypeAlias = _OptionalArg[LogDriver] | Logger
|
|
115
112
|
PerfDriverArg: TypeAlias = _OptionalArg[PerfDriver]
|
|
116
113
|
LocationDriverArg: TypeAlias = _OptionalArg[LocationDriver] | LocationInputGenerator
|
|
@@ -146,7 +143,7 @@ class _SessionBuilderArgs(TypedDict):
|
|
|
146
143
|
overscan: _OptionalFactory[bool] # TODO: Replace with some driver (not sure what yet)
|
|
147
144
|
message: _OptionalFactory[MessageInterface]
|
|
148
145
|
options: _OptionalFactory[OptionDriver]
|
|
149
|
-
path:
|
|
146
|
+
path: Callable[[Core], PathDriver | None]
|
|
150
147
|
log: _OptionalFactory[LogDriver]
|
|
151
148
|
perf: _OptionalFactory[PerfDriver]
|
|
152
149
|
location: _OptionalFactory[LocationDriver]
|
|
@@ -168,13 +165,23 @@ class _SessionBuilderArgs(TypedDict):
|
|
|
168
165
|
|
|
169
166
|
class SessionBuilder:
|
|
170
167
|
"""
|
|
171
|
-
A builder class for constructing Session
|
|
168
|
+
A builder class for constructing a :py:class:`.Session`.
|
|
172
169
|
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
At minimum, a :py:class:`.Session` requires a :py:class:`.Core`,
|
|
171
|
+
an :py:class:`.AudioDriver`,
|
|
172
|
+
an :py:class:`.InputDriver`,
|
|
173
|
+
and a :py:class:`.VideoDriver`;
|
|
174
|
+
each ``with_`` method sets an argument (mostly drivers) for the :py:class:`.Session`.
|
|
175
175
|
"""
|
|
176
176
|
|
|
177
177
|
def __init__(self):
|
|
178
|
+
"""
|
|
179
|
+
Initializes a new :py:class:`SessionBuilder` with no arguments,
|
|
180
|
+
not even the required ones.
|
|
181
|
+
|
|
182
|
+
Calling :py:meth:`build` before setting any of the required arguments
|
|
183
|
+
will raise a :py:class:`RequiredError`.
|
|
184
|
+
"""
|
|
178
185
|
self._args = _SessionBuilderArgs(
|
|
179
186
|
core=lambda: _raise_required_error("A Core is required"),
|
|
180
187
|
audio=lambda: _raise_required_error("An AudioDriver is required"),
|
|
@@ -185,7 +192,7 @@ class SessionBuilder:
|
|
|
185
192
|
overscan=_nothing,
|
|
186
193
|
message=_nothing,
|
|
187
194
|
options=_nothing,
|
|
188
|
-
path=
|
|
195
|
+
path=lambda _: None,
|
|
189
196
|
log=_nothing,
|
|
190
197
|
perf=_nothing,
|
|
191
198
|
location=_nothing,
|
|
@@ -203,23 +210,33 @@ class SessionBuilder:
|
|
|
203
210
|
power=_nothing,
|
|
204
211
|
)
|
|
205
212
|
|
|
213
|
+
@classmethod
|
|
214
|
+
def defaults(cls, core: CoreArg) -> Self:
|
|
215
|
+
"""
|
|
216
|
+
Alias to :py:func:`defaults`.
|
|
217
|
+
"""
|
|
218
|
+
return defaults(core)
|
|
219
|
+
|
|
206
220
|
def with_core(self, core: CoreArg) -> Self:
|
|
207
221
|
"""
|
|
208
222
|
Sets the core to use for the session.
|
|
209
223
|
|
|
210
|
-
|
|
224
|
+
:param core: The core to use for the session. May be one of the following:
|
|
225
|
+
|
|
226
|
+
:class:`.Core`
|
|
227
|
+
Will be used as-is.
|
|
228
|
+
|
|
229
|
+
:class:`str`, :class:`~os.PathLike`
|
|
230
|
+
Will load a :class:`.Core` from this path in :meth:`build`.
|
|
211
231
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
It will be loaded as a :class:`Core` when the session is built.
|
|
215
|
-
- A :class:`CDLL` object that represents a loaded binary.
|
|
216
|
-
It will be loaded into a :class:`Core` when the session is built.
|
|
217
|
-
- A :class:`Callable` that returns one of the above types.
|
|
218
|
-
It will be evaluated when the session is built.
|
|
232
|
+
:class:`~ctypes.CDLL`
|
|
233
|
+
Will load a :class:`.Core` from this library in :meth:`build`.
|
|
219
234
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
235
|
+
:class:`~collections.abc.Callable` () -> :class:`.Core`
|
|
236
|
+
Zero-argument function that returns a :class:`.Core`.
|
|
237
|
+
Will be called in :meth:`build`.
|
|
238
|
+
|
|
239
|
+
:return: This :class:`SessionBuilder` object.
|
|
223
240
|
:raises TypeError: If ``core`` is not one of the permitted types.
|
|
224
241
|
"""
|
|
225
242
|
match core:
|
|
@@ -229,11 +246,9 @@ class SessionBuilder:
|
|
|
229
246
|
self._args["core"] = lambda: core
|
|
230
247
|
case str() | PathLike() | CDLL():
|
|
231
248
|
self._args["core"] = lambda: Core(core)
|
|
232
|
-
case _DefaultType.DEFAULT:
|
|
233
|
-
raise ValueError("Core does not have a default value")
|
|
234
249
|
case _:
|
|
235
250
|
raise TypeError(
|
|
236
|
-
f"Expected Core, str, PathLike, a CDLL, or a callable that returns
|
|
251
|
+
f"Expected Core, str, PathLike, a CDLL, or a callable that returns a Core; got {type(core).__name__}"
|
|
237
252
|
)
|
|
238
253
|
|
|
239
254
|
return self
|
|
@@ -241,10 +256,38 @@ class SessionBuilder:
|
|
|
241
256
|
def with_content(self, content: ContentArg) -> Self:
|
|
242
257
|
"""
|
|
243
258
|
Sets the content to use for this session.
|
|
259
|
+
Will be loaded and managed by this builder's assigned :class:`.ContentDriver`.
|
|
260
|
+
|
|
261
|
+
:param content: The content to use for this session. May be one of the following:
|
|
244
262
|
|
|
245
|
-
|
|
263
|
+
:class:`str`, :class:`~os.PathLike`
|
|
264
|
+
Will load a single content file without enabling a subsystem.
|
|
246
265
|
|
|
247
|
-
|
|
266
|
+
:class:`zipfile.Path`
|
|
267
|
+
Will load a single content file within a ZIP archive without enabling a subsystem.
|
|
268
|
+
|
|
269
|
+
:class:`bytes`, :class:`bytearray`, :class:`memoryview`, :class:`~collections.abc.Buffer`
|
|
270
|
+
Will expose a single unnamed content buffer without enabling a subsystem.
|
|
271
|
+
|
|
272
|
+
:class:`.SubsystemContent`
|
|
273
|
+
Will enable a subsystem and load multiple associated content files.
|
|
274
|
+
|
|
275
|
+
:class:`.retro_game_info`
|
|
276
|
+
Will be passed to the core as-is without enabling a subsystem.
|
|
277
|
+
|
|
278
|
+
:obj:`None`
|
|
279
|
+
Will load the core without using any content or enabling a subsystem;
|
|
280
|
+
if not supported by the core, this will raise an error in :meth:`build`.
|
|
281
|
+
Note that ``retro_load_game`` **will** be called.
|
|
282
|
+
|
|
283
|
+
:class:`~collections.abc.Callable` () -> :data:`.Content` | :class:`.SubsystemContent` | :obj:`None`
|
|
284
|
+
Zero-argument function that returns one of the above.
|
|
285
|
+
Will be called in :meth:`build`.
|
|
286
|
+
|
|
287
|
+
:return: This :class:`.SessionBuilder` object.
|
|
288
|
+
:raises TypeError: If ``content`` is not one of the permitted types.
|
|
289
|
+
|
|
290
|
+
:see: :meth:`.ContentDriver.load` for details on how loaded content is exposed to the core.
|
|
248
291
|
"""
|
|
249
292
|
match content:
|
|
250
293
|
case Callable() as func:
|
|
@@ -326,20 +369,26 @@ class SessionBuilder:
|
|
|
326
369
|
"""
|
|
327
370
|
Sets the video driver for this session.
|
|
328
371
|
|
|
329
|
-
The
|
|
372
|
+
:param video: The video driver to use for this session. May be one of the following:
|
|
330
373
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
absent if not.
|
|
374
|
+
:class:`.VideoDriver`
|
|
375
|
+
Used by the built :class:`.Session` as-is.
|
|
334
376
|
|
|
335
|
-
|
|
377
|
+
:const:`DEFAULT`
|
|
378
|
+
Uses a :class:`.MultiVideoDriver` with its default configuration.
|
|
379
|
+
See its documentation for more details.
|
|
336
380
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
381
|
+
:class:`~collections.abc.Mapping` [:class:`.HardwareContext`, :class:`~collections.abc.Callable` () -> :class:`.VideoDriver`]
|
|
382
|
+
Uses a :class:`.MultiVideoDriver` with the provided driver map.
|
|
383
|
+
|
|
384
|
+
:class:`~collections.abc.Callable` () -> :class:`.VideoDriver`
|
|
385
|
+
Zero-argument function that returns a :class:`.VideoDriver`.
|
|
386
|
+
Called in :meth:`build`.
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
:return: This :class:`SessionBuilder` object.
|
|
390
|
+
:raises TypeError: If ``video`` is not one of the aforementioned types.
|
|
391
|
+
:raises ValueError: If ``video`` does not contain a mapping for :attr:`.HardwareContext.NONE`.
|
|
343
392
|
"""
|
|
344
393
|
|
|
345
394
|
match video:
|
|
@@ -348,20 +397,28 @@ class SessionBuilder:
|
|
|
348
397
|
case VideoDriver():
|
|
349
398
|
self._args["video"] = lambda: video
|
|
350
399
|
case _DefaultType.DEFAULT:
|
|
351
|
-
|
|
352
|
-
|
|
400
|
+
self._args["video"] = MultiVideoDriver
|
|
401
|
+
case Mapping():
|
|
402
|
+
if HardwareContext.NONE not in video:
|
|
403
|
+
raise ValueError("A driver for HardwareContext.NONE is required")
|
|
404
|
+
|
|
405
|
+
if not all(isinstance(k, HardwareContext) for k in video.keys()):
|
|
406
|
+
raise TypeError(
|
|
407
|
+
"Each key in the provided driver map must be a HardwareContext"
|
|
408
|
+
)
|
|
353
409
|
|
|
354
|
-
if
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
410
|
+
if not all(callable(v) for v in video.values()):
|
|
411
|
+
raise TypeError(
|
|
412
|
+
"Each value in the provided driver map must be a callable that returns a VideoDriver"
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
self._args["video"] = lambda: MultiVideoDriver(video)
|
|
358
416
|
|
|
359
|
-
self._args["video"] = lambda: MultiVideoDriver(drivers)
|
|
360
417
|
case None:
|
|
361
418
|
raise ValueError("A video driver is required")
|
|
362
419
|
case _:
|
|
363
420
|
raise TypeError(
|
|
364
|
-
f"Expected a VideoDriver, a callable that returns one, or DEFAULT; got {type(video).__name__}"
|
|
421
|
+
f"Expected a VideoDriver, a callable that returns one, a map of HardwareContexts to Callables, or DEFAULT; got {type(video).__name__}"
|
|
365
422
|
)
|
|
366
423
|
|
|
367
424
|
return self
|
|
@@ -420,6 +477,41 @@ class SessionBuilder:
|
|
|
420
477
|
return self
|
|
421
478
|
|
|
422
479
|
def with_options(self, options: OptionDriverArg) -> Self:
|
|
480
|
+
"""
|
|
481
|
+
Configures the options driver for this session.
|
|
482
|
+
|
|
483
|
+
:param options: May be one of the following:
|
|
484
|
+
|
|
485
|
+
:class:`.OptionDriver`
|
|
486
|
+
Will be used by the built :class:`.Session` as-is.
|
|
487
|
+
|
|
488
|
+
:class:`~collections.abc.Mapping` [:obj:`~typing.AnyStr`, :obj:`~typing.AnyStr`]
|
|
489
|
+
Will be used to initialize a :class:`.DictOptionDriver` with the provided options
|
|
490
|
+
and with API version 2.
|
|
491
|
+
All keys and values must be either :class:`str` or :class:`bytes`;
|
|
492
|
+
mixing the two is not allowed.
|
|
493
|
+
|
|
494
|
+
``0``, ``1``, or ``2``
|
|
495
|
+
Will be used to initialize a :class:`.DictOptionDriver` with no initial options
|
|
496
|
+
using the provided API version.
|
|
497
|
+
|
|
498
|
+
:data:`.DEFAULT`
|
|
499
|
+
Will use a :class:`.DictOptionDriver` with API version 2
|
|
500
|
+
and no initial options.
|
|
501
|
+
|
|
502
|
+
:obj:`None`
|
|
503
|
+
All environment calls that :class:`.OptionDriver` normally implements
|
|
504
|
+
will be unavailable to the loaded :class:`.Core`.
|
|
505
|
+
|
|
506
|
+
:class:`~collections.abc.Callable` () -> :class:`.OptionDriver` | :obj:`None`
|
|
507
|
+
Zero-argument function that returns an :class:`.OptionDriver` or :obj:`None`.
|
|
508
|
+
Will be called in :meth:`build`.
|
|
509
|
+
|
|
510
|
+
:return: This :class:`.SessionBuilder` object.
|
|
511
|
+
:raises TypeError: If ``options`` is not one of the aforementioned types.
|
|
512
|
+
:raises ValueError: If ``options`` is a :class:`~collections.abc.Mapping` whose keys and values
|
|
513
|
+
are not all :class:`str` or :class:`bytes`.
|
|
514
|
+
"""
|
|
423
515
|
_types = (str, bytes)
|
|
424
516
|
match options:
|
|
425
517
|
case Callable() as func:
|
|
@@ -427,11 +519,17 @@ class SessionBuilder:
|
|
|
427
519
|
case OptionDriver() as driver:
|
|
428
520
|
driver: OptionDriver
|
|
429
521
|
self._args["options"] = lambda: driver
|
|
430
|
-
case
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
522
|
+
case Mapping() as optionvars:
|
|
523
|
+
optionvars: Mapping[AnyStr, AnyStr]
|
|
524
|
+
all_str = all(
|
|
525
|
+
isinstance(k, str) and isinstance(v, str) for k, v in optionvars.items()
|
|
526
|
+
)
|
|
527
|
+
all_bytes = all(
|
|
528
|
+
isinstance(k, bytes) and isinstance(v, bytes) for k, v in optionvars.items()
|
|
529
|
+
)
|
|
530
|
+
if not (all_str or all_bytes):
|
|
531
|
+
raise ValueError("All keys and values must be either str or bytes")
|
|
532
|
+
self._args["options"] = lambda: DictOptionDriver(2, True, optionvars)
|
|
435
533
|
case 0 | 1 | 2 as version:
|
|
436
534
|
self._args["options"] = lambda: DictOptionDriver(version)
|
|
437
535
|
case _DefaultType.DEFAULT:
|
|
@@ -440,21 +538,46 @@ class SessionBuilder:
|
|
|
440
538
|
self._args["options"] = _nothing
|
|
441
539
|
case _:
|
|
442
540
|
raise TypeError(
|
|
443
|
-
f"Expected OptionDriver, a
|
|
541
|
+
f"Expected an OptionDriver, a Mapping, DEFAULT, an API version, or a Callable that returns an OptionDriver, or None; got {type(options).__name__}"
|
|
444
542
|
)
|
|
445
543
|
|
|
446
544
|
return self
|
|
447
545
|
|
|
448
546
|
def with_paths(self, path: PathDriverArg) -> Self:
|
|
547
|
+
"""
|
|
548
|
+
Configures the path driver for this session.
|
|
549
|
+
|
|
550
|
+
:param path: May be one of the following:
|
|
551
|
+
|
|
552
|
+
:class:`.PathDriver`
|
|
553
|
+
Will be used by the built :class:`.Session` as-is.
|
|
554
|
+
|
|
555
|
+
:data:`.DEFAULT`
|
|
556
|
+
Will use a :class:`.TempDirPathDriver`
|
|
557
|
+
configured with an unspecified temporary directory
|
|
558
|
+
and the provided :class:`.Core`'s path.
|
|
559
|
+
|
|
560
|
+
:class:`~collections.abc.Callable` (:class:`.Core`) -> :class:`.PathDriver` | :obj:`None`
|
|
561
|
+
One-argument function that accepts a :class:`.Core`
|
|
562
|
+
and returns a :class:`.PathDriver` or :obj:`None`.
|
|
563
|
+
Will be called in :meth:`build` with the configured :class:`.Core` as the argument.
|
|
564
|
+
|
|
565
|
+
:obj:`None`
|
|
566
|
+
All environment calls that :class:`.PathDriver` normally implements
|
|
567
|
+
will be unavailable to the loaded :class:`.Core`.
|
|
568
|
+
|
|
569
|
+
:return: This :class:`.SessionBuilder` object.
|
|
570
|
+
:raises TypeError: If ``path`` is not one of the aforementioned types.
|
|
571
|
+
"""
|
|
449
572
|
match path:
|
|
450
573
|
case Callable() as func:
|
|
451
574
|
self._args["path"] = func
|
|
452
575
|
case PathDriver():
|
|
453
|
-
self._args["path"] = lambda: path
|
|
576
|
+
self._args["path"] = lambda _: path
|
|
454
577
|
case _DefaultType.DEFAULT:
|
|
455
|
-
self._args["path"] =
|
|
578
|
+
self._args["path"] = lambda core: TempDirPathDriver(core)
|
|
456
579
|
case None:
|
|
457
|
-
self._args["path"] =
|
|
580
|
+
self._args["path"] = lambda _: None
|
|
458
581
|
case _:
|
|
459
582
|
raise TypeError(
|
|
460
583
|
f"Expected PathDriver, a callable that returns one, DEFAULT, or None; got {type(path).__name__}"
|
|
@@ -742,7 +865,12 @@ class SessionBuilder:
|
|
|
742
865
|
|
|
743
866
|
def build(self) -> Session:
|
|
744
867
|
"""
|
|
745
|
-
Constructs a Session
|
|
868
|
+
Constructs a :py:class:`.Session` with the provided arguments.
|
|
869
|
+
|
|
870
|
+
:raises RequiredError: If a :py:class:`.Core`, :py:class:`.AudioDriver`, :py:class:`.InputDriver`, or :py:class:`.VideoDriver` is not set.
|
|
871
|
+
:raises Exception: Any exception raised by a registered driver factory or initializer.
|
|
872
|
+
|
|
873
|
+
:return: A :py:class:`.Session` object.
|
|
746
874
|
"""
|
|
747
875
|
core = self._args["core"]()
|
|
748
876
|
content = self._args["content"]()
|
|
@@ -754,7 +882,7 @@ class SessionBuilder:
|
|
|
754
882
|
overscan=self._args["overscan"](),
|
|
755
883
|
message=self._args["message"](),
|
|
756
884
|
options=self._args["options"](),
|
|
757
|
-
path=self._args["path"](),
|
|
885
|
+
path=self._args["path"](core),
|
|
758
886
|
log=self._args["log"](),
|
|
759
887
|
perf=self._args["perf"](),
|
|
760
888
|
location=self._args["location"](),
|
|
@@ -777,6 +905,19 @@ class SessionBuilder:
|
|
|
777
905
|
|
|
778
906
|
|
|
779
907
|
def defaults(core: CoreArg) -> SessionBuilder:
|
|
908
|
+
"""
|
|
909
|
+
Constructs a :py:class:`SessionBuilder` with the recommended drivers and their default values.
|
|
910
|
+
Does not build the session, so these defaults may still be overridden.
|
|
911
|
+
|
|
912
|
+
:param core: The core to use for the session.
|
|
913
|
+
|
|
914
|
+
Examples::
|
|
915
|
+
|
|
916
|
+
builder = SessionBuilder.defaults(core)
|
|
917
|
+
with builder.with_log(None).build() as session:
|
|
918
|
+
pass
|
|
919
|
+
|
|
920
|
+
"""
|
|
780
921
|
return (
|
|
781
922
|
SessionBuilder()
|
|
782
923
|
.with_core(core)
|
|
@@ -810,4 +951,5 @@ __all__ = [
|
|
|
810
951
|
"SessionBuilder",
|
|
811
952
|
"DEFAULT",
|
|
812
953
|
"defaults",
|
|
954
|
+
"RequiredError",
|
|
813
955
|
]
|
|
@@ -92,6 +92,8 @@ class StandardContentDriver(ContentDriver):
|
|
|
92
92
|
raise RuntimeError("System info not set")
|
|
93
93
|
|
|
94
94
|
with ExitStack() as stack:
|
|
95
|
+
# We may be loading several files, each of which needs its own context manager
|
|
96
|
+
# So we use ExitStack to manage all of their lives at once
|
|
95
97
|
loaded_content: Sequence[LoadedContentFile | None] | None
|
|
96
98
|
subsystem: retro_subsystem_info | None = None
|
|
97
99
|
match content:
|
|
@@ -273,12 +275,12 @@ class StandardContentDriver(ContentDriver):
|
|
|
273
275
|
yield LoadedContentFile(loaded_info, loaded_info_ext)
|
|
274
276
|
|
|
275
277
|
# For test cases that provide content by path
|
|
276
|
-
case str(
|
|
278
|
+
case (str() | PathLike()) as path, ContentAttributes(need_fullpath=True):
|
|
277
279
|
loaded_info = retro_game_info(os.fsencode(path), None, 0, None)
|
|
278
280
|
loaded_info_ext = _make_game_info_ext(loaded_info)
|
|
279
281
|
yield LoadedContentFile(loaded_info, loaded_info_ext)
|
|
280
282
|
# There's no data to persist, so no cleanup needed
|
|
281
|
-
case str(
|
|
283
|
+
case (str() | PathLike()) as path, ContentAttributes(persistent_data=False):
|
|
282
284
|
with mmap_file(path) as view:
|
|
283
285
|
loaded_info = retro_game_info(
|
|
284
286
|
os.fsencode(path), addressof_buffer(view), len(view), None
|
|
@@ -286,7 +288,7 @@ class StandardContentDriver(ContentDriver):
|
|
|
286
288
|
loaded_info_ext = _make_game_info_ext(loaded_info)
|
|
287
289
|
yield LoadedContentFile(loaded_info, loaded_info_ext)
|
|
288
290
|
# Content is not persistent, so just let the with statement clean up the view
|
|
289
|
-
case str(
|
|
291
|
+
case (str() | PathLike()) as path, ContentAttributes(persistent_data=True):
|
|
290
292
|
context = mmap_file(path)
|
|
291
293
|
view = context.__enter__()
|
|
292
294
|
loaded_info = retro_game_info(
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from typing import Protocol, runtime_checkable
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@runtime_checkable
|
|
6
|
+
class PathDriver(Protocol):
|
|
7
|
+
"""
|
|
8
|
+
Interface for a driver that defines various paths exposed to libretro cores.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def system_dir(self) -> bytes | None:
|
|
14
|
+
"""
|
|
15
|
+
Corresponds to :py:attr:`.EnvironmentCall.GET_SYSTEM_DIRECTORY`.
|
|
16
|
+
|
|
17
|
+
If :py:obj:`None`, a core's call to ``RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY`` should return ``false``.
|
|
18
|
+
"""
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def libretro_path(self) -> bytes | None:
|
|
24
|
+
"""
|
|
25
|
+
Corresponds to :py:attr:`.EnvironmentCall.GET_LIBRETRO_PATH`.
|
|
26
|
+
|
|
27
|
+
If :py:obj:`None`, a core's call to ``RETRO_ENVIRONMENT_GET_LIBRETRO_PATH`` should return ``false``.
|
|
28
|
+
"""
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def core_assets_dir(self) -> bytes | None:
|
|
34
|
+
"""
|
|
35
|
+
Corresponds to :py:attr:`.EnvironmentCall.GET_CORE_ASSETS_DIRECTORY`.
|
|
36
|
+
|
|
37
|
+
If :py:obj:`None`, a core's call to ``RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY`` should return ``false``.
|
|
38
|
+
"""
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def save_dir(self) -> bytes | None:
|
|
44
|
+
"""
|
|
45
|
+
Corresponds to :py:attr:`.EnvironmentCall.GET_SAVE_DIRECTORY`.
|
|
46
|
+
|
|
47
|
+
If :py:obj:`None`, a core's call to ``RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY`` should return ``false``.
|
|
48
|
+
"""
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def playlist_dir(self) -> bytes | None:
|
|
54
|
+
"""
|
|
55
|
+
Corresponds to :py:attr:`.EnvironmentCall.GET_PLAYLIST_DIRECTORY`.
|
|
56
|
+
|
|
57
|
+
If :py:obj:`None`, a core's call to ``RETRO_ENVIRONMENT_GET_PLAYLIST_DIRECTORY`` should return ``false``.
|
|
58
|
+
"""
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
__all__ = [
|
|
63
|
+
"PathDriver",
|
|
64
|
+
]
|