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.
Files changed (123) hide show
  1. {pyboy-2.4.0/pyboy.egg-info → pyboy-2.5.0}/PKG-INFO +8 -8
  2. {pyboy-2.4.0 → pyboy-2.5.0}/README.md +5 -5
  3. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/__main__.py +65 -11
  4. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/__init__.py +8 -1
  5. pyboy-2.5.0/pyboy/api/gameshark.pxd +24 -0
  6. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/gameshark.py +15 -10
  7. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/memory_scanner.pxd +1 -1
  8. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/memory_scanner.py +17 -11
  9. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/screen.pxd +2 -3
  10. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/screen.py +13 -10
  11. pyboy-2.5.0/pyboy/api/sound.pxd +17 -0
  12. pyboy-2.5.0/pyboy/api/sound.py +174 -0
  13. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/sprite.py +4 -2
  14. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/tile.pxd +2 -2
  15. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/tile.py +10 -13
  16. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/tilemap.py +10 -8
  17. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/conftest.py +37 -28
  18. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/__init__.py +0 -2
  19. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/bootrom.pxd +1 -0
  20. pyboy-2.5.0/pyboy/core/bootrom.py +25 -0
  21. pyboy-2.5.0/pyboy/core/cartridge/__init__.py +10 -0
  22. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/base_mbc.pxd +7 -7
  23. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/base_mbc.py +39 -27
  24. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/cartridge.pxd +1 -1
  25. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/cartridge.py +12 -11
  26. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc1.py +3 -3
  27. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc2.py +2 -3
  28. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc3.py +0 -1
  29. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc5.py +1 -2
  30. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/rtc.pxd +2 -2
  31. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/rtc.py +13 -11
  32. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cpu.pxd +4 -4
  33. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cpu.py +22 -14
  34. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/interaction.pxd +2 -2
  35. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/interaction.py +1 -2
  36. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/lcd.pxd +26 -30
  37. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/lcd.py +297 -241
  38. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/mb.pxd +3 -5
  39. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/mb.py +204 -161
  40. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/opcodes.py +0 -1
  41. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/opcodes_gen.py +275 -231
  42. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/ram.pxd +2 -2
  43. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/ram.py +2 -2
  44. pyboy-2.5.0/pyboy/core/sound.pxd +188 -0
  45. pyboy-2.5.0/pyboy/core/sound.py +973 -0
  46. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/timer.pxd +4 -4
  47. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/timer.py +10 -7
  48. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/logging/__init__.py +7 -1
  49. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/logging/_logging.py +1 -1
  50. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/__init__.py +8 -9
  51. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/auto_pause.py +2 -2
  52. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/base_plugin.pxd +12 -10
  53. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/base_plugin.py +44 -55
  54. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/debug.pxd +4 -4
  55. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/debug.py +71 -67
  56. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/debug_prompt.pxd +1 -1
  57. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/debug_prompt.py +4 -6
  58. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_kirby_dream_land.py +12 -12
  59. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_pokemon_gen1.py +6 -12
  60. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_pokemon_pinball.pxd +1 -2
  61. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_pokemon_pinball.py +173 -161
  62. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_super_mario_land.pxd +2 -3
  63. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_super_mario_land.py +139 -36
  64. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_tetris.pxd +1 -4
  65. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_tetris.py +18 -20
  66. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/manager.pxd +3 -5
  67. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/manager.py +28 -27
  68. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/manager_gen.py +75 -14
  69. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/record_replay.py +7 -4
  70. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/rewind.py +10 -16
  71. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/screen_recorder.py +2 -3
  72. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/screenshot_recorder.py +1 -3
  73. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_null.pxd +1 -0
  74. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_null.py +0 -1
  75. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_open_gl.pxd +2 -5
  76. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_open_gl.py +38 -15
  77. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_sdl2.pxd +16 -4
  78. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/window_sdl2.py +111 -15
  79. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/pyboy.pxd +14 -12
  80. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/pyboy.py +249 -113
  81. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/utils.pxd +4 -0
  82. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/utils.py +180 -18
  83. {pyboy-2.4.0 → pyboy-2.5.0/pyboy.egg-info}/PKG-INFO +8 -8
  84. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/SOURCES.txt +2 -2
  85. {pyboy-2.4.0 → pyboy-2.5.0}/pyproject.toml +23 -20
  86. {pyboy-2.4.0 → pyboy-2.5.0}/setup.py +19 -12
  87. pyboy-2.4.0/pyboy/api/gameshark.pxd +0 -24
  88. pyboy-2.4.0/pyboy/core/bootrom.py +0 -44
  89. pyboy-2.4.0/pyboy/core/cartridge/__init__.py +0 -12
  90. pyboy-2.4.0/pyboy/core/sound.pxd +0 -165
  91. pyboy-2.4.0/pyboy/core/sound.py +0 -637
  92. pyboy-2.4.0/pyboy/plugins/disable_input.pxd +0 -15
  93. pyboy-2.4.0/pyboy/plugins/disable_input.py +0 -15
  94. {pyboy-2.4.0 → pyboy-2.5.0}/LICENSE.md +0 -0
  95. {pyboy-2.4.0 → pyboy-2.5.0}/MANIFEST.in +0 -0
  96. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/__init__.py +0 -0
  97. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/constants.py +0 -0
  98. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/sprite.pxd +0 -0
  99. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/api/tilemap.pxd +0 -0
  100. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/bootrom_cgb.bin +0 -0
  101. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/bootrom_dmg.bin +0 -0
  102. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc1.pxd +0 -0
  103. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc2.pxd +0 -0
  104. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc3.pxd +0 -0
  105. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/cartridge/mbc5.pxd +0 -0
  106. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/core/opcodes.pxd +0 -0
  107. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/default_rom.gb +0 -0
  108. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/logging/logging.pxd +0 -0
  109. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/logging/logging.pyx +0 -0
  110. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/auto_pause.pxd +0 -0
  111. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/font.txt +0 -0
  112. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_kirby_dream_land.pxd +0 -0
  113. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/game_wrapper_pokemon_gen1.pxd +0 -0
  114. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/record_replay.pxd +0 -0
  115. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/rewind.pxd +0 -0
  116. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/screen_recorder.pxd +0 -0
  117. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy/plugins/screenshot_recorder.pxd +0 -0
  118. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/dependency_links.txt +0 -0
  119. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/entry_points.txt +0 -0
  120. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/not-zip-safe +0 -0
  121. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/requires.txt +0 -0
  122. {pyboy-2.4.0 → pyboy-2.5.0}/pyboy.egg-info/top_level.txt +0 -0
  123. {pyboy-2.4.0 → pyboy-2.5.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: pyboy
3
- Version: 2.4.0
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.8
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/Zrf2nyH).__
196
+ __If you have any questions, or just want to chat, [join us on Discord](https://discord.gg/wUbag3KNqQ).__
197
197
 
198
- [![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](https://discord.gg/Zrf2nyH)
198
+ [![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](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/Zrf2nyH)
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/Zrf2nyH).
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
- [![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](https://discord.gg/Zrf2nyH)
382
+ [![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](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/Zrf2nyH).__
5
+ __If you have any questions, or just want to chat, [join us on Discord](https://discord.gg/wUbag3KNqQ).__
6
6
 
7
- [![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](https://discord.gg/Zrf2nyH)
7
+ [![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](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/Zrf2nyH)
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/Zrf2nyH).
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
- [![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](https://discord.gg/Zrf2nyH)
191
+ [![Discord](https://img.shields.io/discord/584149554132418570?style=for-the-badge&logo=Discord&label=PyBoy)](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, core, utils
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
- assert len(color_palette) == 4, f"Not the correct amount of colors! Expected four, got {len(color_palette)}"
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
- assert len(color_palette) == 12, f"Not the correct amount of colors! Expected twelve, got {len(color_palette)}"
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
 
@@ -17,4 +17,11 @@ from .tilemap import TileMap
17
17
  # "constants": False,
18
18
  # "manager": False,
19
19
  # }
20
- # __all__ = ["API"]
20
+ __all__ = [
21
+ "constants",
22
+ "GameShark",
23
+ "Screen",
24
+ "Sprite",
25
+ "Tile",
26
+ "TileMap",
27
+ ]
@@ -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) # Convert hexadecimal value to an integer
39
- unconverted_address = gameshark_code[4:] # Ex: 1ED1
40
- lower = unconverted_address[:2] # Ex: 1E
41
- upper = unconverted_address[2:] # Ex: D1
42
- address_converted = upper + lower # Ex: 0xD11E # Converting to Ram Readable address
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
- logger.error("GameShark code already applied!")
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 code in self.cheats:
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=*) noexcept
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(start_addr, end_addr - (byte_width-1) + 1): # Adjust the loop to prevent reading past end_addr
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 (dynamic_comparison_type == DynamicComparisonType.UNCHANGED):
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 (dynamic_comparison_type == DynamicComparisonType.CHANGED):
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 (dynamic_comparison_type == DynamicComparisonType.INCREASED):
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 (dynamic_comparison_type == DynamicComparisonType.DECREASED):
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 (dynamic_comparison_type == DynamicComparisonType.MATCH):
139
- if new_value == None:
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
- # cdef readonly uint8_t[144][160][4] memoryview
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
- bytes:
57
- 92160 bytes of screen data in a `bytes` object.
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("Cannot generate screen image. Missing dependency \"Pillow\".")
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, self.mb.lcd.renderer.buffer_dims[::-1],
168
- self.mb.lcd.renderer._screenbuffer_raw
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. WY) position for each horizontal line in the
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[67:78]
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
- [3, 0, -7, 0]
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