pyboy 2.4.0__tar.gz → 2.5.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.
- {pyboy-2.4.0/pyboy.egg-info → pyboy-2.5.0}/PKG-INFO +8 -8
- {pyboy-2.4.0 → pyboy-2.5.0}/README.md +5 -5
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/__main__.py +65 -11
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/__init__.py +8 -1
- pyboy-2.5.0/pyboy/api/gameshark.pxd +24 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/gameshark.py +15 -10
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/memory_scanner.pxd +1 -1
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/memory_scanner.py +17 -11
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/screen.pxd +2 -3
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/screen.py +13 -10
- pyboy-2.5.0/pyboy/api/sound.pxd +17 -0
- pyboy-2.5.0/pyboy/api/sound.py +174 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/sprite.py +4 -2
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/tile.pxd +2 -2
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/tile.py +10 -13
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/tilemap.py +10 -8
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/conftest.py +37 -28
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/__init__.py +0 -2
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/bootrom.pxd +1 -0
- pyboy-2.5.0/pyboy/core/bootrom.py +25 -0
- pyboy-2.5.0/pyboy/core/cartridge/__init__.py +10 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/base_mbc.pxd +7 -7
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/base_mbc.py +39 -27
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/cartridge.pxd +1 -1
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/cartridge.py +12 -11
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc1.py +3 -3
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc2.py +2 -3
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc3.py +0 -1
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc5.py +1 -2
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/rtc.pxd +2 -2
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/rtc.py +13 -11
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cpu.pxd +4 -4
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cpu.py +22 -14
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/interaction.pxd +2 -2
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/interaction.py +1 -2
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/lcd.pxd +26 -30
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/lcd.py +297 -241
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/mb.pxd +3 -5
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/mb.py +204 -161
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/opcodes.py +0 -1
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/opcodes_gen.py +275 -231
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/ram.pxd +2 -2
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/ram.py +2 -2
- pyboy-2.5.0/pyboy/core/sound.pxd +188 -0
- pyboy-2.5.0/pyboy/core/sound.py +973 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/timer.pxd +4 -4
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/timer.py +10 -7
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/logging/__init__.py +7 -1
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/logging/_logging.py +1 -1
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/__init__.py +8 -9
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/auto_pause.py +2 -2
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/base_plugin.pxd +12 -10
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/base_plugin.py +44 -55
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/debug.pxd +4 -4
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/debug.py +71 -67
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/debug_prompt.pxd +1 -1
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/debug_prompt.py +4 -6
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_kirby_dream_land.py +12 -12
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_pokemon_gen1.py +6 -12
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_pokemon_pinball.pxd +1 -2
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_pokemon_pinball.py +173 -161
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_super_mario_land.pxd +2 -3
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_super_mario_land.py +139 -36
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_tetris.pxd +1 -4
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_tetris.py +18 -20
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/manager.pxd +3 -5
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/manager.py +28 -27
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/manager_gen.py +75 -14
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/record_replay.py +7 -4
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/rewind.py +10 -16
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/screen_recorder.py +2 -3
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/screenshot_recorder.py +1 -3
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_null.pxd +1 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_null.py +0 -1
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_open_gl.pxd +2 -5
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_open_gl.py +38 -15
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_sdl2.pxd +16 -4
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_sdl2.py +111 -15
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/pyboy.pxd +14 -12
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/pyboy.py +249 -113
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/utils.pxd +4 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/utils.py +180 -18
- {pyboy-2.4.0 → pyboy-2.5.0/pyboy.egg-info}/PKG-INFO +8 -8
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/SOURCES.txt +2 -2
- {pyboy-2.4.0 → pyboy-2.5.0}/pyproject.toml +23 -20
- {pyboy-2.4.0 → pyboy-2.5.0}/setup.py +19 -12
- pyboy-2.4.0/pyboy/api/gameshark.pxd +0 -24
- pyboy-2.4.0/pyboy/core/bootrom.py +0 -44
- pyboy-2.4.0/pyboy/core/cartridge/__init__.py +0 -12
- pyboy-2.4.0/pyboy/core/sound.pxd +0 -165
- pyboy-2.4.0/pyboy/core/sound.py +0 -637
- pyboy-2.4.0/pyboy/plugins/disable_input.pxd +0 -15
- pyboy-2.4.0/pyboy/plugins/disable_input.py +0 -15
- {pyboy-2.4.0 → pyboy-2.5.0}/LICENSE.md +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/MANIFEST.in +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/__init__.py +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/constants.py +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/sprite.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/tilemap.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/bootrom_cgb.bin +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/bootrom_dmg.bin +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc1.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc2.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc3.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc5.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/opcodes.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/default_rom.gb +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/logging/logging.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/logging/logging.pyx +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/auto_pause.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/font.txt +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_kirby_dream_land.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_pokemon_gen1.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/record_replay.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/rewind.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/screen_recorder.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/screenshot_recorder.pxd +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/dependency_links.txt +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/entry_points.txt +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/not-zip-safe +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/requires.txt +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/top_level.txt +0 -0
- {pyboy-2.4.0 → pyboy-2.5.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: pyboy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: Game Boy emulator written in Python
|
|
5
5
|
Author-email: Mads Ynddal <mads@ynddal.dk>
|
|
6
6
|
License: GNU Lesser General Public License
|
|
@@ -177,7 +177,7 @@ Classifier: Programming Language :: Cython
|
|
|
177
177
|
Classifier: Programming Language :: Python :: 3
|
|
178
178
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
179
179
|
Classifier: Topic :: System :: Emulators
|
|
180
|
-
Requires-Python: >=3.
|
|
180
|
+
Requires-Python: >=3.9
|
|
181
181
|
Description-Content-Type: text/markdown
|
|
182
182
|
License-File: LICENSE.md
|
|
183
183
|
Requires-Dist: numpy
|
|
@@ -193,9 +193,9 @@ Requires-Dist: gym; extra == "all"
|
|
|
193
193
|
<img src="extras/README/pyboy.svg" width="480">
|
|
194
194
|
</p>
|
|
195
195
|
|
|
196
|
-
__If you have any questions, or just want to chat, [join us on Discord](https://discord.gg/
|
|
196
|
+
__If you have any questions, or just want to chat, [join us on Discord](https://discord.gg/wUbag3KNqQ).__
|
|
197
197
|
|
|
198
|
-
[](https://discord.gg/
|
|
198
|
+
[](https://discord.gg/wUbag3KNqQ)
|
|
199
199
|
|
|
200
200
|
<!---
|
|
201
201
|
Generate GIF with the layout and captions
|
|
@@ -289,7 +289,7 @@ If you are looking to make a bot or AI, then these resources are a good place to
|
|
|
289
289
|
* [Example: Tetris](https://github.com/Baekalfen/PyBoy/wiki/Example-Tetris)
|
|
290
290
|
* [Example: Super Mario Land](https://github.com/Baekalfen/PyBoy/wiki/Example-Super-Mario-Land)
|
|
291
291
|
* [Code Examples](https://github.com/Baekalfen/PyBoy/tree/master/extras/examples)
|
|
292
|
-
* [Discord](https://discord.gg/
|
|
292
|
+
* [Discord](https://discord.gg/wUbag3KNqQ)
|
|
293
293
|
|
|
294
294
|
|
|
295
295
|
When the emulator is running, you can easily access [PyBoy's API](https://baekalfen.github.io/PyBoy/index.html):
|
|
@@ -377,9 +377,9 @@ do 3160 hours of gameplay in 1 hour.
|
|
|
377
377
|
|
|
378
378
|
Contributing
|
|
379
379
|
============
|
|
380
|
-
Any contribution is appreciated. The currently known problems are tracked in [the Issues tab](https://github.com/Baekalfen/PyBoy/issues). Feel free to take a swing at any one of them. If you have something original in mind, come and [discuss it on on Discord](https://discord.gg/
|
|
380
|
+
Any contribution is appreciated. The currently known problems are tracked in [the Issues tab](https://github.com/Baekalfen/PyBoy/issues). Feel free to take a swing at any one of them. If you have something original in mind, come and [discuss it on on Discord](https://discord.gg/wUbag3KNqQ).
|
|
381
381
|
|
|
382
|
-
[](https://discord.gg/
|
|
382
|
+
[](https://discord.gg/wUbag3KNqQ)
|
|
383
383
|
|
|
384
384
|
For the more major features, there are the following that you can give a try. They are also described in more detail in the [project list in the Wiki](https://github.com/Baekalfen/PyBoy/wiki/Student-Projects):
|
|
385
385
|
* Hacking games
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
<img src="extras/README/pyboy.svg" width="480">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
__If you have any questions, or just want to chat, [join us on Discord](https://discord.gg/
|
|
5
|
+
__If you have any questions, or just want to chat, [join us on Discord](https://discord.gg/wUbag3KNqQ).__
|
|
6
6
|
|
|
7
|
-
[](https://discord.gg/
|
|
7
|
+
[](https://discord.gg/wUbag3KNqQ)
|
|
8
8
|
|
|
9
9
|
<!---
|
|
10
10
|
Generate GIF with the layout and captions
|
|
@@ -98,7 +98,7 @@ If you are looking to make a bot or AI, then these resources are a good place to
|
|
|
98
98
|
* [Example: Tetris](https://github.com/Baekalfen/PyBoy/wiki/Example-Tetris)
|
|
99
99
|
* [Example: Super Mario Land](https://github.com/Baekalfen/PyBoy/wiki/Example-Super-Mario-Land)
|
|
100
100
|
* [Code Examples](https://github.com/Baekalfen/PyBoy/tree/master/extras/examples)
|
|
101
|
-
* [Discord](https://discord.gg/
|
|
101
|
+
* [Discord](https://discord.gg/wUbag3KNqQ)
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
When the emulator is running, you can easily access [PyBoy's API](https://baekalfen.github.io/PyBoy/index.html):
|
|
@@ -186,9 +186,9 @@ do 3160 hours of gameplay in 1 hour.
|
|
|
186
186
|
|
|
187
187
|
Contributing
|
|
188
188
|
============
|
|
189
|
-
Any contribution is appreciated. The currently known problems are tracked in [the Issues tab](https://github.com/Baekalfen/PyBoy/issues). Feel free to take a swing at any one of them. If you have something original in mind, come and [discuss it on on Discord](https://discord.gg/
|
|
189
|
+
Any contribution is appreciated. The currently known problems are tracked in [the Issues tab](https://github.com/Baekalfen/PyBoy/issues). Feel free to take a swing at any one of them. If you have something original in mind, come and [discuss it on on Discord](https://discord.gg/wUbag3KNqQ).
|
|
190
190
|
|
|
191
|
-
[](https://discord.gg/
|
|
191
|
+
[](https://discord.gg/wUbag3KNqQ)
|
|
192
192
|
|
|
193
193
|
For the more major features, there are the following that you can give a try. They are also described in more detail in the [project list in the Wiki](https://github.com/Baekalfen/PyBoy/wiki/Student-Projects):
|
|
194
194
|
* Hacking games
|
|
@@ -9,9 +9,10 @@ import copy
|
|
|
9
9
|
import os
|
|
10
10
|
|
|
11
11
|
import pyboy
|
|
12
|
-
from pyboy import PyBoy
|
|
12
|
+
from pyboy import PyBoy
|
|
13
13
|
from pyboy.plugins.manager import parser_arguments
|
|
14
14
|
from pyboy.pyboy import defaults
|
|
15
|
+
from pyboy.utils import PyBoyInvalidInputException
|
|
15
16
|
|
|
16
17
|
logger = pyboy.logging.get_logger(__name__)
|
|
17
18
|
|
|
@@ -20,13 +21,15 @@ INTERNAL_LOADSTATE = "INTERNAL_LOADSTATE_TOKEN"
|
|
|
20
21
|
|
|
21
22
|
def color_tuple(string):
|
|
22
23
|
color_palette = [int(c.strip(), 16) for c in string.split(",")]
|
|
23
|
-
|
|
24
|
+
if not (len(color_palette) == 4):
|
|
25
|
+
raise PyBoyInvalidInputException(f"Not the correct amount of colors! Expected four, got {len(color_palette)}")
|
|
24
26
|
return color_palette
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
def cgb_color_tuple(string):
|
|
28
30
|
color_palette = [int(c.strip(), 16) for c in string.split(",")]
|
|
29
|
-
|
|
31
|
+
if not (len(color_palette) == 12):
|
|
32
|
+
raise PyBoyInvalidInputException(f"Not the correct amount of colors! Expected twelve, got {len(color_palette)}")
|
|
30
33
|
return [color_palette[0:4], color_palette[4:8], color_palette[8:12]]
|
|
31
34
|
|
|
32
35
|
|
|
@@ -37,24 +40,41 @@ def valid_file_path(path):
|
|
|
37
40
|
return path
|
|
38
41
|
|
|
39
42
|
|
|
43
|
+
def valid_volume(vol):
|
|
44
|
+
if isinstance(vol, bool):
|
|
45
|
+
return 100 if vol else 0
|
|
46
|
+
elif vol.isnumeric() and (0 <= int(vol) <= 100):
|
|
47
|
+
return int(vol)
|
|
48
|
+
else:
|
|
49
|
+
raise ValueError("Invalid volume")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def valid_sample_rate(freq):
|
|
53
|
+
if freq.isnumeric() and int(freq) % 60 == 0:
|
|
54
|
+
return int(freq)
|
|
55
|
+
else:
|
|
56
|
+
raise ValueError("Invalid sample rate")
|
|
57
|
+
|
|
58
|
+
|
|
40
59
|
parser = argparse.ArgumentParser(
|
|
41
60
|
description="PyBoy -- Game Boy emulator written in Python",
|
|
42
61
|
epilog="Warning: Features marked with (internal use) might be subject to change.",
|
|
43
62
|
)
|
|
44
63
|
parser.add_argument("ROM", type=valid_file_path, help="Path to a Game Boy compatible ROM file")
|
|
45
64
|
parser.add_argument("-b", "--bootrom", dest="bootrom", type=valid_file_path, help="Path to a boot-ROM file")
|
|
65
|
+
parser.add_argument("--no-input", action="store_true", help="Disable all user-input (mostly for autonomous testing)")
|
|
46
66
|
parser.add_argument(
|
|
47
67
|
"--log-level",
|
|
48
68
|
default=defaults["log_level"],
|
|
49
69
|
type=str,
|
|
50
70
|
choices=["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
|
|
51
|
-
help="Set logging level"
|
|
71
|
+
help="Set logging level",
|
|
52
72
|
)
|
|
53
73
|
parser.add_argument(
|
|
54
74
|
"--color-palette",
|
|
55
75
|
type=color_tuple,
|
|
56
76
|
default=defaults["color_palette"],
|
|
57
|
-
help=('Four comma seperated, hexadecimal, RGB values for colors (i.e. "FFFFFF,999999,555555,000000")')
|
|
77
|
+
help=('Four comma seperated, hexadecimal, RGB values for colors (i.e. "FFFFFF,999999,555555,000000")'),
|
|
58
78
|
)
|
|
59
79
|
parser.add_argument(
|
|
60
80
|
"--cgb-color-palette",
|
|
@@ -62,7 +82,7 @@ parser.add_argument(
|
|
|
62
82
|
default=defaults["cgb_color_palette"],
|
|
63
83
|
help=(
|
|
64
84
|
'Three sets of four comma seperated, hexadecimal, RGB values for colors in the order of: background, obj0, obj1 (i.e. "FFFFFF,7BFF31,0063C5,000000,FFFFFF,FF8484,FF8484,000000,FFFFFF,FF8484,FF8484,000000")'
|
|
65
|
-
)
|
|
85
|
+
),
|
|
66
86
|
)
|
|
67
87
|
parser.add_argument(
|
|
68
88
|
"-l",
|
|
@@ -74,7 +94,7 @@ parser.add_argument(
|
|
|
74
94
|
help=(
|
|
75
95
|
"Load state from file. If filepath is specified, it will load the given path. Otherwise, it will automatically "
|
|
76
96
|
"locate a saved state next to the ROM file."
|
|
77
|
-
)
|
|
97
|
+
),
|
|
78
98
|
)
|
|
79
99
|
parser.add_argument(
|
|
80
100
|
"-w",
|
|
@@ -82,15 +102,14 @@ parser.add_argument(
|
|
|
82
102
|
default=defaults["window"],
|
|
83
103
|
type=str,
|
|
84
104
|
choices=["SDL2", "OpenGL", "null"],
|
|
85
|
-
help="Specify window-type to use"
|
|
105
|
+
help="Specify window-type to use",
|
|
86
106
|
)
|
|
87
107
|
parser.add_argument("-s", "--scale", default=defaults["scale"], type=int, help="The scaling multiplier for the window")
|
|
88
|
-
parser.add_argument("--sound", action="store_true", help="Enable sound (beta)")
|
|
89
108
|
parser.add_argument("--no-renderer", action="store_true", help="Disable rendering (internal use)")
|
|
90
109
|
parser.add_argument(
|
|
91
110
|
"--gameshark",
|
|
92
111
|
type=str,
|
|
93
|
-
help="Add GameShark cheats on start-up. Add multiple by comma separation (i.e. '010138CD, 01033CD1')"
|
|
112
|
+
help="Add GameShark cheats on start-up. Add multiple by comma separation (i.e. '010138CD, 01033CD1')",
|
|
94
113
|
)
|
|
95
114
|
|
|
96
115
|
gameboy_type_parser = parser.add_mutually_exclusive_group()
|
|
@@ -101,6 +120,39 @@ gameboy_type_parser.add_argument(
|
|
|
101
120
|
"--cgb", action="store_const", const=True, dest="cgb", help="Force emulator to run as Game Boy Color"
|
|
102
121
|
)
|
|
103
122
|
|
|
123
|
+
# NOTE: Inverted logic on variable
|
|
124
|
+
parser.add_argument(
|
|
125
|
+
"--no-sound-emulation",
|
|
126
|
+
default=True,
|
|
127
|
+
action="store_false",
|
|
128
|
+
dest="sound_emulated",
|
|
129
|
+
help="Disables sound emulation (not just muted!)",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
sound_parser = parser.add_mutually_exclusive_group()
|
|
133
|
+
|
|
134
|
+
sound_parser.add_argument(
|
|
135
|
+
"--sound",
|
|
136
|
+
action="store_const",
|
|
137
|
+
const=100,
|
|
138
|
+
dest="sound_volume",
|
|
139
|
+
help="Deprecated setting. Now sets sound volume to 100%. See '--sound-volume'",
|
|
140
|
+
)
|
|
141
|
+
sound_parser.add_argument(
|
|
142
|
+
"--sound-volume",
|
|
143
|
+
default=100,
|
|
144
|
+
type=valid_volume,
|
|
145
|
+
help="Set sound volume in percent (0-100)",
|
|
146
|
+
)
|
|
147
|
+
parser.set_defaults(sound_volume=100)
|
|
148
|
+
|
|
149
|
+
parser.add_argument(
|
|
150
|
+
"--sound-sample-rate",
|
|
151
|
+
default=None,
|
|
152
|
+
type=valid_sample_rate,
|
|
153
|
+
help="Set sound sample rate. Has to be divisible in 60.",
|
|
154
|
+
)
|
|
155
|
+
|
|
104
156
|
for arguments in parser_arguments():
|
|
105
157
|
for a in arguments:
|
|
106
158
|
*args, kwargs = a
|
|
@@ -154,6 +206,9 @@ See "pyboy --help" for how to enable rewind and other awesome features!
|
|
|
154
206
|
kwargs.pop("ROM", None)
|
|
155
207
|
kwargs.pop("loadstate", None)
|
|
156
208
|
kwargs.pop("no_renderer", None)
|
|
209
|
+
if kwargs.pop("sound_emulated", None) is False:
|
|
210
|
+
# This disables the entire sound emulation, not just muting the sound.
|
|
211
|
+
kwargs["sound"] = False
|
|
157
212
|
pyboy = PyBoy(argv.ROM, **kwargs)
|
|
158
213
|
|
|
159
214
|
if argv.loadstate is not None:
|
|
@@ -168,7 +223,6 @@ See "pyboy --help" for how to enable rewind and other awesome features!
|
|
|
168
223
|
with open(state_path, "rb") as f:
|
|
169
224
|
pyboy.load_state(f)
|
|
170
225
|
|
|
171
|
-
render = not argv.no_renderer
|
|
172
226
|
while pyboy.tick():
|
|
173
227
|
pass
|
|
174
228
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#
|
|
2
|
+
# License: See LICENSE.md file
|
|
3
|
+
# GitHub: https://github.com/Baekalfen/PyBoy
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
from libc.stdint cimport uint8_t, int64_t
|
|
7
|
+
|
|
8
|
+
from pyboy.logging.logging cimport Logger
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
cdef Logger logger
|
|
12
|
+
|
|
13
|
+
cdef class GameShark:
|
|
14
|
+
cdef object memory
|
|
15
|
+
cdef dict cheats
|
|
16
|
+
cdef bint enabled
|
|
17
|
+
|
|
18
|
+
cdef int64_t _get_value(self, int, int) except -1
|
|
19
|
+
cdef int64_t _set_value(self, int, int, int) except -1
|
|
20
|
+
cdef tuple _convert_cheat(self, str code)
|
|
21
|
+
cpdef int64_t add(self, str code) except -1
|
|
22
|
+
cpdef int64_t remove(self, str code, bint restore_value=*) except -1
|
|
23
|
+
cpdef int64_t clear_all(self, bint restore_value=*) except -1
|
|
24
|
+
cdef int64_t tick(self) except -1 with gil
|
|
@@ -4,9 +4,14 @@
|
|
|
4
4
|
#
|
|
5
5
|
|
|
6
6
|
from pyboy.logging import get_logger
|
|
7
|
+
from pyboy.utils import PyBoyInvalidInputException
|
|
7
8
|
|
|
8
9
|
logger = get_logger(__name__)
|
|
9
10
|
|
|
11
|
+
__pdoc__ = {
|
|
12
|
+
"GameShark.tick": False,
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
|
|
11
16
|
class GameShark:
|
|
12
17
|
def __init__(self, memory):
|
|
@@ -15,7 +20,7 @@ class GameShark:
|
|
|
15
20
|
self.enabled = False
|
|
16
21
|
|
|
17
22
|
def _convert_cheat(self, gameshark_code):
|
|
18
|
-
|
|
23
|
+
"""
|
|
19
24
|
A GameShark code for these consoles is written in the format ttvvaaaa. tt specifies the type, which is usually 01.
|
|
20
25
|
vv specifies the hexadecimal value the code will write into the game's memory. aaaa specifies the memory address
|
|
21
26
|
that will be modified, with the low byte first (e.g. address C056 is written as 56C0).
|
|
@@ -28,18 +33,18 @@ class GameShark:
|
|
|
28
33
|
https://doc.kodewerx.org/hacking_gb.html
|
|
29
34
|
|
|
30
35
|
There seems to be conflicting information about the presence of other types than 01.
|
|
31
|
-
|
|
36
|
+
"""
|
|
32
37
|
# Check if the input cheat code has the correct length (8 characters)
|
|
33
38
|
if len(gameshark_code) != 8:
|
|
34
39
|
raise ValueError("Invalid cheat code length. Cheat code must be 8 characters long.")
|
|
35
40
|
|
|
36
41
|
# Extract components from the cheat code
|
|
37
42
|
_type = int(gameshark_code[:2], 16)
|
|
38
|
-
value = int(gameshark_code[2:4], 16)
|
|
39
|
-
unconverted_address = gameshark_code[4:]
|
|
40
|
-
lower = unconverted_address[:2]
|
|
41
|
-
upper = unconverted_address[2:]
|
|
42
|
-
address_converted = upper + lower
|
|
43
|
+
value = int(gameshark_code[2:4], 16) # Convert hexadecimal value to an integer
|
|
44
|
+
unconverted_address = gameshark_code[4:] # Ex: 1ED1
|
|
45
|
+
lower = unconverted_address[:2] # Ex: 1E
|
|
46
|
+
upper = unconverted_address[2:] # Ex: D1
|
|
47
|
+
address_converted = upper + lower # Ex: 0xD11E # Converting to Ram Readable address
|
|
43
48
|
address = int(address_converted, 16)
|
|
44
49
|
|
|
45
50
|
if not 0x8000 <= address:
|
|
@@ -100,7 +105,7 @@ class GameShark:
|
|
|
100
105
|
if code not in self.cheats:
|
|
101
106
|
self.cheats[code] = (self._get_value(_type, address), (_type, value, address))
|
|
102
107
|
else:
|
|
103
|
-
|
|
108
|
+
raise PyBoyInvalidInputException("GameShark code already applied!")
|
|
104
109
|
|
|
105
110
|
def remove(self, code, restore_value=True):
|
|
106
111
|
"""
|
|
@@ -117,7 +122,7 @@ class GameShark:
|
|
|
117
122
|
restore_value (bool): True to restore original value at address, otherwise don't restore
|
|
118
123
|
"""
|
|
119
124
|
|
|
120
|
-
if not
|
|
125
|
+
if code not in self.cheats:
|
|
121
126
|
raise ValueError("GameShark code cannot be removed. Hasn't been applied.")
|
|
122
127
|
|
|
123
128
|
original_value, (_type, _, address) = self.cheats.pop(code)
|
|
@@ -145,7 +150,7 @@ class GameShark:
|
|
|
145
150
|
|
|
146
151
|
def tick(self):
|
|
147
152
|
if not self.enabled:
|
|
148
|
-
return
|
|
153
|
+
return 0
|
|
149
154
|
# https://gbdev.io/pandocs/Shark_Cheats.html
|
|
150
155
|
# "As far as it is understood, patching is implemented by hooking the original VBlank interrupt handler, and
|
|
151
156
|
# re-writing RAM values each frame."
|
|
@@ -7,5 +7,5 @@ cdef class MemoryScanner:
|
|
|
7
7
|
cdef int _memory_cache_byte_width
|
|
8
8
|
|
|
9
9
|
cpdef list rescan_memory(self, object new_value=*, dynamic_comparison_type=*, byteorder=*)
|
|
10
|
-
cpdef list scan_memory(self, object target_value=*, int start_addr=*, int end_addr=*, standard_comparison_type=*, value_type=*, int byte_width=*, byteorder=*)
|
|
10
|
+
cpdef list scan_memory(self, object target_value=*, int start_addr=*, int end_addr=*, standard_comparison_type=*, value_type=*, int byte_width=*, byteorder=*)
|
|
11
11
|
cpdef bint _check_value(self, uint64_t, uint64_t, int)noexcept
|
|
@@ -5,6 +5,7 @@ from pyboy.utils import bcd_to_dec
|
|
|
5
5
|
|
|
6
6
|
class StandardComparisonType(Enum):
|
|
7
7
|
"""Enumeration for defining types of comparisons that do not require a previous value."""
|
|
8
|
+
|
|
8
9
|
EXACT = 1
|
|
9
10
|
LESS_THAN = 2
|
|
10
11
|
GREATER_THAN = 3
|
|
@@ -14,6 +15,7 @@ class StandardComparisonType(Enum):
|
|
|
14
15
|
|
|
15
16
|
class DynamicComparisonType(Enum):
|
|
16
17
|
"""Enumeration for defining types of comparisons that require a previous value."""
|
|
18
|
+
|
|
17
19
|
UNCHANGED = 1
|
|
18
20
|
CHANGED = 2
|
|
19
21
|
INCREASED = 3
|
|
@@ -23,12 +25,14 @@ class DynamicComparisonType(Enum):
|
|
|
23
25
|
|
|
24
26
|
class ScanMode(Enum):
|
|
25
27
|
"""Enumeration for defining scanning modes."""
|
|
28
|
+
|
|
26
29
|
INT = 1
|
|
27
30
|
BCD = 2
|
|
28
31
|
|
|
29
32
|
|
|
30
|
-
class MemoryScanner
|
|
33
|
+
class MemoryScanner:
|
|
31
34
|
"""A class for scanning memory within a given range."""
|
|
35
|
+
|
|
32
36
|
def __init__(self, pyboy):
|
|
33
37
|
self.pyboy = pyboy
|
|
34
38
|
self._memory_cache = {}
|
|
@@ -42,7 +46,7 @@ class MemoryScanner():
|
|
|
42
46
|
standard_comparison_type=StandardComparisonType.EXACT,
|
|
43
47
|
value_type=ScanMode.INT,
|
|
44
48
|
byte_width=1,
|
|
45
|
-
byteorder="little"
|
|
49
|
+
byteorder="little",
|
|
46
50
|
):
|
|
47
51
|
"""
|
|
48
52
|
This function scans a specified range of memory for a target value from the `start_addr` to the `end_addr` (both included).
|
|
@@ -69,9 +73,11 @@ class MemoryScanner():
|
|
|
69
73
|
"""
|
|
70
74
|
self._memory_cache = {}
|
|
71
75
|
self._memory_cache_byte_width = byte_width
|
|
72
|
-
for addr in range(
|
|
76
|
+
for addr in range(
|
|
77
|
+
start_addr, end_addr - (byte_width - 1) + 1
|
|
78
|
+
): # Adjust the loop to prevent reading past end_addr
|
|
73
79
|
# Read multiple bytes based on byte_width and byteorder
|
|
74
|
-
value_bytes = self.pyboy.memory[addr:addr + byte_width]
|
|
80
|
+
value_bytes = self.pyboy.memory[addr : addr + byte_width]
|
|
75
81
|
value = int.from_bytes(value_bytes, byteorder)
|
|
76
82
|
|
|
77
83
|
if value_type == ScanMode.BCD:
|
|
@@ -113,30 +119,30 @@ class MemoryScanner():
|
|
|
113
119
|
"""
|
|
114
120
|
for addr, value in self._memory_cache.copy().items():
|
|
115
121
|
current_value = int.from_bytes(
|
|
116
|
-
self.pyboy.memory[addr:addr + self._memory_cache_byte_width], byteorder=byteorder
|
|
122
|
+
self.pyboy.memory[addr : addr + self._memory_cache_byte_width], byteorder=byteorder
|
|
117
123
|
)
|
|
118
|
-
if
|
|
124
|
+
if dynamic_comparison_type == DynamicComparisonType.UNCHANGED:
|
|
119
125
|
if value != current_value:
|
|
120
126
|
self._memory_cache.pop(addr)
|
|
121
127
|
else:
|
|
122
128
|
self._memory_cache[addr] = current_value
|
|
123
|
-
elif
|
|
129
|
+
elif dynamic_comparison_type == DynamicComparisonType.CHANGED:
|
|
124
130
|
if value == current_value:
|
|
125
131
|
self._memory_cache.pop(addr)
|
|
126
132
|
else:
|
|
127
133
|
self._memory_cache[addr] = current_value
|
|
128
|
-
elif
|
|
134
|
+
elif dynamic_comparison_type == DynamicComparisonType.INCREASED:
|
|
129
135
|
if value >= current_value:
|
|
130
136
|
self._memory_cache.pop(addr)
|
|
131
137
|
else:
|
|
132
138
|
self._memory_cache[addr] = current_value
|
|
133
|
-
elif
|
|
139
|
+
elif dynamic_comparison_type == DynamicComparisonType.DECREASED:
|
|
134
140
|
if value <= current_value:
|
|
135
141
|
self._memory_cache.pop(addr)
|
|
136
142
|
else:
|
|
137
143
|
self._memory_cache[addr] = current_value
|
|
138
|
-
elif
|
|
139
|
-
if new_value
|
|
144
|
+
elif dynamic_comparison_type == DynamicComparisonType.MATCH:
|
|
145
|
+
if new_value is None:
|
|
140
146
|
raise ValueError("new_value must be specified when using DynamicComparisonType.MATCH")
|
|
141
147
|
if current_value != new_value:
|
|
142
148
|
self._memory_cache.pop(addr)
|
|
@@ -11,13 +11,12 @@ from pyboy.core.mb cimport Motherboard
|
|
|
11
11
|
|
|
12
12
|
cdef class Screen:
|
|
13
13
|
cdef Motherboard mb
|
|
14
|
-
cpdef ((int, int), (int, int)) get_tilemap_position(self)
|
|
14
|
+
cpdef ((int, int), (int, int)) get_tilemap_position(self) noexcept
|
|
15
15
|
# cdef readonly list tilemap_position_list
|
|
16
16
|
# cdef readonly uint8_t[:,:] tilemap_position_list
|
|
17
17
|
cdef readonly uint32_t[:,:] raw_buffer
|
|
18
18
|
cdef readonly (int, int) raw_buffer_dims
|
|
19
19
|
cdef readonly str raw_buffer_format
|
|
20
|
-
|
|
21
|
-
# cpdef np.ndarray[np.uint8_t, ndim=3] get_ndarray(self)
|
|
20
|
+
|
|
22
21
|
cdef readonly object ndarray
|
|
23
22
|
cdef readonly object image
|
|
@@ -29,6 +29,7 @@ class Screen:
|
|
|
29
29
|
If you're making an AI or bot, it's highly recommended to _not_ use this class for detecting objects on the screen.
|
|
30
30
|
It's much more efficient to use `pyboy.PyBoy.tilemap_background`, `pyboy.PyBoy.tilemap_window`, and `pyboy.PyBoy.get_sprite` instead.
|
|
31
31
|
"""
|
|
32
|
+
|
|
32
33
|
def __init__(self, mb):
|
|
33
34
|
self.mb = mb
|
|
34
35
|
|
|
@@ -53,8 +54,8 @@ class Screen:
|
|
|
53
54
|
|
|
54
55
|
Returns
|
|
55
56
|
-------
|
|
56
|
-
|
|
57
|
-
92160 bytes of screen data
|
|
57
|
+
memoryview:
|
|
58
|
+
92160 bytes memoryview of screen data.
|
|
58
59
|
"""
|
|
59
60
|
self.raw_buffer_dims = self.mb.lcd.renderer.buffer_dims
|
|
60
61
|
"""
|
|
@@ -119,7 +120,8 @@ class Screen:
|
|
|
119
120
|
RGB image of (160, 144) pixels
|
|
120
121
|
"""
|
|
121
122
|
if not Image:
|
|
122
|
-
logger.warning(
|
|
123
|
+
logger.warning('Cannot generate screen image. Missing dependency "Pillow".')
|
|
124
|
+
self.image = utils.PillowImportError()
|
|
123
125
|
else:
|
|
124
126
|
self._set_image()
|
|
125
127
|
|
|
@@ -164,14 +166,15 @@ class Screen:
|
|
|
164
166
|
|
|
165
167
|
def _set_image(self):
|
|
166
168
|
self.image = Image.frombuffer(
|
|
167
|
-
self.mb.lcd.renderer.color_format,
|
|
168
|
-
self.mb.lcd.renderer.
|
|
169
|
+
self.mb.lcd.renderer.color_format,
|
|
170
|
+
self.mb.lcd.renderer.buffer_dims[::-1],
|
|
171
|
+
self.mb.lcd.renderer._screenbuffer_raw,
|
|
169
172
|
)
|
|
170
173
|
|
|
171
174
|
@property
|
|
172
175
|
def tilemap_position_list(self):
|
|
173
176
|
"""
|
|
174
|
-
This function provides the screen (SCX, SCY) and window (WX
|
|
177
|
+
This function provides the screen (SCX, SCY) and window (WX, WY) position for each horizontal line in the
|
|
175
178
|
screen buffer. These parameters are often used for visual effects, and some games will reset the registers at
|
|
176
179
|
the end of each call to `pyboy.PyBoy.tick()`.
|
|
177
180
|
|
|
@@ -181,18 +184,18 @@ class Screen:
|
|
|
181
184
|
```python
|
|
182
185
|
>>> pyboy.tick(25)
|
|
183
186
|
True
|
|
184
|
-
>>> swoosh = pyboy.screen.tilemap_position_list[
|
|
187
|
+
>>> swoosh = pyboy.screen.tilemap_position_list[66:77]
|
|
185
188
|
>>> print(*swoosh, sep=newline) # Just to pretty-print it
|
|
186
189
|
[0, 0, -7, 0]
|
|
187
190
|
[1, 0, -7, 0]
|
|
188
191
|
[2, 0, -7, 0]
|
|
189
192
|
[2, 0, -7, 0]
|
|
190
|
-
[
|
|
191
|
-
[3, 0, -7, 0]
|
|
193
|
+
[2, 0, -7, 0]
|
|
192
194
|
[3, 0, -7, 0]
|
|
193
195
|
[3, 0, -7, 0]
|
|
194
196
|
[2, 0, -7, 0]
|
|
195
197
|
[1, 0, -7, 0]
|
|
198
|
+
[1, 0, -7, 0]
|
|
196
199
|
[0, 0, -7, 0]
|
|
197
200
|
|
|
198
201
|
```
|
|
@@ -204,7 +207,7 @@ class Screen:
|
|
|
204
207
|
"""
|
|
205
208
|
|
|
206
209
|
if self.mb.lcd._LCDC.lcd_enable:
|
|
207
|
-
return [[line[0], line[1], line[2], line[3]] for line in self.mb.lcd._scanlineparameters]
|
|
210
|
+
return [[line[0], line[1], line[2] - 7, line[3]] for line in self.mb.lcd._scanlineparameters]
|
|
208
211
|
else:
|
|
209
212
|
return [[0, 0, 0, 0] for line in range(144)]
|
|
210
213
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#
|
|
2
|
+
# License: See LICENSE.md file
|
|
3
|
+
# GitHub: https://github.com/Baekalfen/PyBoy
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
from libc.stdint cimport uint64_t, int8_t
|
|
7
|
+
from pyboy.core.mb cimport Motherboard
|
|
8
|
+
|
|
9
|
+
cdef class Sound:
|
|
10
|
+
cdef Motherboard mb
|
|
11
|
+
cdef readonly uint64_t sample_rate
|
|
12
|
+
|
|
13
|
+
cdef readonly int8_t[:] raw_buffer
|
|
14
|
+
cdef readonly (int, int) raw_buffer_dims
|
|
15
|
+
cdef readonly str raw_buffer_format
|
|
16
|
+
cdef readonly int raw_buffer_length
|
|
17
|
+
cdef readonly object raw_ndarray
|