gemba 0.1.1 → 0.2.0

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 (285) hide show
  1. checksums.yaml +4 -4
  2. data/THIRD_PARTY_NOTICES +37 -2
  3. data/assets/placeholder_boxart.png +0 -0
  4. data/bin/gemba +2 -2
  5. data/ext/gemba/extconf.rb +23 -1
  6. data/ext/gemba/gemba_ext.c +436 -2
  7. data/ext/gemba/gemba_ext.h +2 -0
  8. data/gemba.gemspec +5 -3
  9. data/lib/gemba/achievements/achievement.rb +23 -0
  10. data/lib/gemba/achievements/backend.rb +186 -0
  11. data/lib/gemba/achievements/cache.rb +70 -0
  12. data/lib/gemba/achievements/credentials_presenter.rb +142 -0
  13. data/lib/gemba/achievements/fake_backend.rb +205 -0
  14. data/lib/gemba/achievements/null_backend.rb +11 -0
  15. data/lib/gemba/achievements/offline_backend.rb +168 -0
  16. data/lib/gemba/achievements/retro_achievements/backend.rb +453 -0
  17. data/lib/gemba/achievements/retro_achievements/cli_sync_requester.rb +64 -0
  18. data/lib/gemba/achievements/retro_achievements/ping_worker.rb +27 -0
  19. data/lib/gemba/achievements.rb +19 -0
  20. data/lib/gemba/achievements_window.rb +556 -0
  21. data/lib/gemba/app_controller.rb +1015 -0
  22. data/lib/gemba/bios.rb +54 -0
  23. data/lib/gemba/boxart_fetcher/libretro_backend.rb +39 -0
  24. data/lib/gemba/boxart_fetcher/null_backend.rb +12 -0
  25. data/lib/gemba/boxart_fetcher.rb +79 -0
  26. data/lib/gemba/bus_emitter.rb +13 -0
  27. data/lib/gemba/child_window.rb +24 -1
  28. data/lib/gemba/cli/commands/config_cmd.rb +83 -0
  29. data/lib/gemba/cli/commands/decode.rb +154 -0
  30. data/lib/gemba/cli/commands/patch.rb +78 -0
  31. data/lib/gemba/cli/commands/play.rb +78 -0
  32. data/lib/gemba/cli/commands/record.rb +114 -0
  33. data/lib/gemba/cli/commands/replay.rb +161 -0
  34. data/lib/gemba/cli/commands/retro_achievements.rb +213 -0
  35. data/lib/gemba/cli/commands/version.rb +22 -0
  36. data/lib/gemba/cli.rb +52 -364
  37. data/lib/gemba/config.rb +134 -1
  38. data/lib/gemba/data/gb_games.json +1 -0
  39. data/lib/gemba/data/gb_md5.json +1 -0
  40. data/lib/gemba/data/gba_games.json +1 -0
  41. data/lib/gemba/data/gba_md5.json +1 -0
  42. data/lib/gemba/data/gbc_games.json +1 -0
  43. data/lib/gemba/data/gbc_md5.json +1 -0
  44. data/lib/gemba/emulator_frame.rb +1060 -0
  45. data/lib/gemba/event_bus.rb +48 -0
  46. data/lib/gemba/frame_stack.rb +60 -0
  47. data/lib/gemba/game_index.rb +84 -0
  48. data/lib/gemba/game_picker_frame.rb +268 -0
  49. data/lib/gemba/gamepad_map.rb +103 -0
  50. data/lib/gemba/headless.rb +6 -5
  51. data/lib/gemba/headless_player.rb +33 -3
  52. data/lib/gemba/help_window.rb +61 -0
  53. data/lib/gemba/hotkey_map.rb +3 -1
  54. data/lib/gemba/input_recorder.rb +107 -0
  55. data/lib/gemba/input_replayer.rb +119 -0
  56. data/lib/gemba/keyboard_map.rb +90 -0
  57. data/lib/gemba/locales/en.yml +97 -5
  58. data/lib/gemba/locales/ja.yml +97 -5
  59. data/lib/gemba/main_window.rb +56 -0
  60. data/lib/gemba/modal_stack.rb +81 -0
  61. data/lib/gemba/patcher_window.rb +223 -0
  62. data/lib/gemba/platform/gb.rb +21 -0
  63. data/lib/gemba/platform/gba.rb +21 -0
  64. data/lib/gemba/platform/gbc.rb +23 -0
  65. data/lib/gemba/platform.rb +20 -0
  66. data/lib/gemba/platform_open.rb +19 -0
  67. data/lib/gemba/recorder.rb +4 -3
  68. data/lib/gemba/replay_player.rb +691 -0
  69. data/lib/gemba/rom_info.rb +57 -0
  70. data/lib/gemba/rom_info_window.rb +16 -3
  71. data/lib/gemba/rom_library.rb +106 -0
  72. data/lib/gemba/rom_overrides.rb +47 -0
  73. data/lib/gemba/rom_patcher/bps.rb +161 -0
  74. data/lib/gemba/rom_patcher/ips.rb +101 -0
  75. data/lib/gemba/rom_patcher/ups.rb +118 -0
  76. data/lib/gemba/rom_patcher.rb +109 -0
  77. data/lib/gemba/{rom_loader.rb → rom_resolver.rb} +7 -6
  78. data/lib/gemba/runtime.rb +59 -26
  79. data/lib/gemba/save_state_manager.rb +4 -7
  80. data/lib/gemba/save_state_picker.rb +17 -4
  81. data/lib/gemba/session_logger.rb +64 -0
  82. data/lib/gemba/settings/audio_tab.rb +77 -0
  83. data/lib/gemba/settings/gamepad_tab.rb +351 -0
  84. data/lib/gemba/settings/hotkeys_tab.rb +259 -0
  85. data/lib/gemba/settings/paths.rb +11 -0
  86. data/lib/gemba/settings/recording_tab.rb +83 -0
  87. data/lib/gemba/settings/save_states_tab.rb +91 -0
  88. data/lib/gemba/settings/system_tab.rb +362 -0
  89. data/lib/gemba/settings/video_tab.rb +318 -0
  90. data/lib/gemba/settings_window.rb +162 -1036
  91. data/lib/gemba/version.rb +1 -1
  92. data/lib/gemba/virtual_keyboard.rb +19 -0
  93. data/lib/gemba.rb +2 -12
  94. data/test/achievements_window/test_bulk_sync.rb +218 -0
  95. data/test/achievements_window/test_bus_events.rb +125 -0
  96. data/test/achievements_window/test_close_confirmation.rb +201 -0
  97. data/test/achievements_window/test_initial_state.rb +164 -0
  98. data/test/achievements_window/test_sorting.rb +227 -0
  99. data/test/achievements_window/test_tree_rendering.rb +133 -0
  100. data/test/fixtures/fake_bios.bin +0 -0
  101. data/test/fixtures/pong.gba +0 -0
  102. data/test/fixtures/test.gb +0 -0
  103. data/test/fixtures/test.gbc +0 -0
  104. data/test/fixtures/test_quicksave.ss +0 -0
  105. data/test/screenshots/no_focus.png +0 -0
  106. data/test/shared/teek_test_worker.rb +17 -1
  107. data/test/shared/tk_test_helper.rb +91 -4
  108. data/test/support/achievements_window_helpers.rb +18 -0
  109. data/test/support/fake_core.rb +25 -0
  110. data/test/support/fake_ra_runtime.rb +74 -0
  111. data/test/support/fake_requester.rb +68 -0
  112. data/test/support/player_helpers.rb +20 -5
  113. data/test/test_achievement.rb +32 -0
  114. data/test/{test_player.rb → test_app_controller.rb} +353 -85
  115. data/test/test_bios.rb +123 -0
  116. data/test/test_boxart_fetcher.rb +150 -0
  117. data/test/test_cli.rb +17 -265
  118. data/test/test_cli_config.rb +64 -0
  119. data/test/test_cli_decode.rb +97 -0
  120. data/test/test_cli_patch.rb +58 -0
  121. data/test/test_cli_play.rb +213 -0
  122. data/test/test_cli_ra.rb +175 -0
  123. data/test/test_cli_record.rb +69 -0
  124. data/test/test_cli_replay.rb +72 -0
  125. data/test/test_cli_sync_requester.rb +152 -0
  126. data/test/test_cli_version.rb +27 -0
  127. data/test/test_config.rb +2 -3
  128. data/test/test_config_ra.rb +69 -0
  129. data/test/test_core.rb +62 -1
  130. data/test/test_credentials_presenter.rb +192 -0
  131. data/test/test_event_bus.rb +100 -0
  132. data/test/test_fake_backend_achievements.rb +130 -0
  133. data/test/test_fake_backend_auth.rb +68 -0
  134. data/test/test_game_index.rb +77 -0
  135. data/test/test_game_picker_frame.rb +310 -0
  136. data/test/test_gamepad_map.rb +1 -3
  137. data/test/test_headless_player.rb +17 -3
  138. data/test/test_help_window.rb +82 -0
  139. data/test/test_hotkey_map.rb +22 -1
  140. data/test/test_input_recorder.rb +179 -0
  141. data/test/test_input_replay_determinism.rb +113 -0
  142. data/test/test_input_replayer.rb +162 -0
  143. data/test/test_keyboard_map.rb +1 -3
  144. data/test/test_libretro_backend.rb +41 -0
  145. data/test/test_locale.rb +1 -1
  146. data/test/test_logging.rb +123 -0
  147. data/test/test_null_backend.rb +42 -0
  148. data/test/test_offline_backend.rb +116 -0
  149. data/test/test_overlay_renderer.rb +1 -1
  150. data/test/test_platform.rb +149 -0
  151. data/test/test_ra_backend.rb +313 -0
  152. data/test/test_ra_backend_unlock_gate.rb +56 -0
  153. data/test/test_recorder.rb +0 -3
  154. data/test/test_replay_player.rb +316 -0
  155. data/test/test_rom_info.rb +149 -0
  156. data/test/test_rom_overrides.rb +86 -0
  157. data/test/test_rom_patcher.rb +382 -0
  158. data/test/{test_rom_loader.rb → test_rom_resolver.rb} +25 -26
  159. data/test/test_save_state_manager.rb +2 -4
  160. data/test/test_settings_audio.rb +107 -0
  161. data/test/test_settings_hotkeys.rb +83 -66
  162. data/test/test_settings_recording.rb +49 -0
  163. data/test/test_settings_save_states.rb +97 -0
  164. data/test/test_settings_system.rb +133 -0
  165. data/test/test_settings_video.rb +450 -0
  166. data/test/test_settings_window.rb +76 -507
  167. data/test/test_tip_service.rb +6 -6
  168. data/test/test_toast_overlay.rb +1 -1
  169. data/test/test_virtual_events.rb +156 -0
  170. data/test/test_virtual_keyboard.rb +1 -1
  171. data/vendor/rcheevos/CHANGELOG.md +495 -0
  172. data/vendor/rcheevos/LICENSE +21 -0
  173. data/vendor/rcheevos/Package.swift +33 -0
  174. data/vendor/rcheevos/README.md +67 -0
  175. data/vendor/rcheevos/include/module.modulemap +70 -0
  176. data/vendor/rcheevos/include/rc_api_editor.h +296 -0
  177. data/vendor/rcheevos/include/rc_api_info.h +280 -0
  178. data/vendor/rcheevos/include/rc_api_request.h +77 -0
  179. data/vendor/rcheevos/include/rc_api_runtime.h +417 -0
  180. data/vendor/rcheevos/include/rc_api_user.h +262 -0
  181. data/vendor/rcheevos/include/rc_client.h +877 -0
  182. data/vendor/rcheevos/include/rc_client_raintegration.h +101 -0
  183. data/vendor/rcheevos/include/rc_consoles.h +138 -0
  184. data/vendor/rcheevos/include/rc_error.h +59 -0
  185. data/vendor/rcheevos/include/rc_export.h +100 -0
  186. data/vendor/rcheevos/include/rc_hash.h +200 -0
  187. data/vendor/rcheevos/include/rc_runtime.h +148 -0
  188. data/vendor/rcheevos/include/rc_runtime_types.h +452 -0
  189. data/vendor/rcheevos/include/rc_util.h +51 -0
  190. data/vendor/rcheevos/include/rcheevos.h +8 -0
  191. data/vendor/rcheevos/src/rapi/rc_api_common.c +1379 -0
  192. data/vendor/rcheevos/src/rapi/rc_api_common.h +88 -0
  193. data/vendor/rcheevos/src/rapi/rc_api_editor.c +625 -0
  194. data/vendor/rcheevos/src/rapi/rc_api_info.c +587 -0
  195. data/vendor/rcheevos/src/rapi/rc_api_runtime.c +901 -0
  196. data/vendor/rcheevos/src/rapi/rc_api_user.c +483 -0
  197. data/vendor/rcheevos/src/rc_client.c +6941 -0
  198. data/vendor/rcheevos/src/rc_client_external.c +281 -0
  199. data/vendor/rcheevos/src/rc_client_external.h +177 -0
  200. data/vendor/rcheevos/src/rc_client_external_versions.h +171 -0
  201. data/vendor/rcheevos/src/rc_client_internal.h +409 -0
  202. data/vendor/rcheevos/src/rc_client_raintegration.c +566 -0
  203. data/vendor/rcheevos/src/rc_client_raintegration_internal.h +61 -0
  204. data/vendor/rcheevos/src/rc_client_types.natvis +396 -0
  205. data/vendor/rcheevos/src/rc_compat.c +251 -0
  206. data/vendor/rcheevos/src/rc_compat.h +121 -0
  207. data/vendor/rcheevos/src/rc_libretro.c +915 -0
  208. data/vendor/rcheevos/src/rc_libretro.h +98 -0
  209. data/vendor/rcheevos/src/rc_util.c +199 -0
  210. data/vendor/rcheevos/src/rc_version.c +11 -0
  211. data/vendor/rcheevos/src/rc_version.h +32 -0
  212. data/vendor/rcheevos/src/rcheevos/alloc.c +312 -0
  213. data/vendor/rcheevos/src/rcheevos/condition.c +754 -0
  214. data/vendor/rcheevos/src/rcheevos/condset.c +777 -0
  215. data/vendor/rcheevos/src/rcheevos/consoleinfo.c +1215 -0
  216. data/vendor/rcheevos/src/rcheevos/format.c +330 -0
  217. data/vendor/rcheevos/src/rcheevos/lboard.c +287 -0
  218. data/vendor/rcheevos/src/rcheevos/memref.c +805 -0
  219. data/vendor/rcheevos/src/rcheevos/operand.c +607 -0
  220. data/vendor/rcheevos/src/rcheevos/rc_internal.h +390 -0
  221. data/vendor/rcheevos/src/rcheevos/rc_runtime_types.natvis +541 -0
  222. data/vendor/rcheevos/src/rcheevos/rc_validate.c +1406 -0
  223. data/vendor/rcheevos/src/rcheevos/rc_validate.h +18 -0
  224. data/vendor/rcheevos/src/rcheevos/richpresence.c +922 -0
  225. data/vendor/rcheevos/src/rcheevos/runtime.c +852 -0
  226. data/vendor/rcheevos/src/rcheevos/runtime_progress.c +1073 -0
  227. data/vendor/rcheevos/src/rcheevos/trigger.c +344 -0
  228. data/vendor/rcheevos/src/rcheevos/value.c +935 -0
  229. data/vendor/rcheevos/src/rhash/aes.c +480 -0
  230. data/vendor/rcheevos/src/rhash/aes.h +49 -0
  231. data/vendor/rcheevos/src/rhash/cdreader.c +838 -0
  232. data/vendor/rcheevos/src/rhash/hash.c +1402 -0
  233. data/vendor/rcheevos/src/rhash/hash_disc.c +1340 -0
  234. data/vendor/rcheevos/src/rhash/hash_encrypted.c +566 -0
  235. data/vendor/rcheevos/src/rhash/hash_rom.c +426 -0
  236. data/vendor/rcheevos/src/rhash/hash_zip.c +460 -0
  237. data/vendor/rcheevos/src/rhash/md5.c +382 -0
  238. data/vendor/rcheevos/src/rhash/md5.h +91 -0
  239. data/vendor/rcheevos/src/rhash/rc_hash_internal.h +116 -0
  240. data/vendor/rcheevos/test/libretro.h +205 -0
  241. data/vendor/rcheevos/test/rapi/test_rc_api_common.c +941 -0
  242. data/vendor/rcheevos/test/rapi/test_rc_api_editor.c +931 -0
  243. data/vendor/rcheevos/test/rapi/test_rc_api_info.c +545 -0
  244. data/vendor/rcheevos/test/rapi/test_rc_api_runtime.c +2213 -0
  245. data/vendor/rcheevos/test/rapi/test_rc_api_user.c +998 -0
  246. data/vendor/rcheevos/test/rcheevos/mock_memory.h +32 -0
  247. data/vendor/rcheevos/test/rcheevos/test_condition.c +570 -0
  248. data/vendor/rcheevos/test/rcheevos/test_condset.c +5170 -0
  249. data/vendor/rcheevos/test/rcheevos/test_consoleinfo.c +203 -0
  250. data/vendor/rcheevos/test/rcheevos/test_format.c +112 -0
  251. data/vendor/rcheevos/test/rcheevos/test_lboard.c +746 -0
  252. data/vendor/rcheevos/test/rcheevos/test_memref.c +520 -0
  253. data/vendor/rcheevos/test/rcheevos/test_operand.c +692 -0
  254. data/vendor/rcheevos/test/rcheevos/test_rc_validate.c +502 -0
  255. data/vendor/rcheevos/test/rcheevos/test_richpresence.c +1564 -0
  256. data/vendor/rcheevos/test/rcheevos/test_runtime.c +1667 -0
  257. data/vendor/rcheevos/test/rcheevos/test_runtime_progress.c +1821 -0
  258. data/vendor/rcheevos/test/rcheevos/test_timing.c +166 -0
  259. data/vendor/rcheevos/test/rcheevos/test_trigger.c +2521 -0
  260. data/vendor/rcheevos/test/rcheevos/test_value.c +870 -0
  261. data/vendor/rcheevos/test/rcheevos-test.sln +46 -0
  262. data/vendor/rcheevos/test/rcheevos-test.vcxproj +239 -0
  263. data/vendor/rcheevos/test/rcheevos-test.vcxproj.filters +335 -0
  264. data/vendor/rcheevos/test/rhash/data.c +657 -0
  265. data/vendor/rcheevos/test/rhash/data.h +32 -0
  266. data/vendor/rcheevos/test/rhash/mock_filereader.c +236 -0
  267. data/vendor/rcheevos/test/rhash/mock_filereader.h +31 -0
  268. data/vendor/rcheevos/test/rhash/test_cdreader.c +920 -0
  269. data/vendor/rcheevos/test/rhash/test_hash.c +310 -0
  270. data/vendor/rcheevos/test/rhash/test_hash_disc.c +1450 -0
  271. data/vendor/rcheevos/test/rhash/test_hash_rom.c +899 -0
  272. data/vendor/rcheevos/test/rhash/test_hash_zip.c +551 -0
  273. data/vendor/rcheevos/test/test.c +113 -0
  274. data/vendor/rcheevos/test/test_framework.h +205 -0
  275. data/vendor/rcheevos/test/test_rc_client.c +10509 -0
  276. data/vendor/rcheevos/test/test_rc_client_external.c +2197 -0
  277. data/vendor/rcheevos/test/test_rc_client_raintegration.c +441 -0
  278. data/vendor/rcheevos/test/test_rc_libretro.c +952 -0
  279. data/vendor/rcheevos/test/test_types.natvis +9 -0
  280. data/vendor/rcheevos/validator/validator.c +658 -0
  281. data/vendor/rcheevos/validator/validator.vcxproj +152 -0
  282. data/vendor/rcheevos/validator/validator.vcxproj.filters +82 -0
  283. metadata +274 -11
  284. data/lib/gemba/input_mappings.rb +0 -214
  285. data/lib/gemba/player.rb +0 -1525
@@ -0,0 +1,915 @@
1
+ /* This file provides a series of functions for integrating RetroAchievements with libretro.
2
+ * These functions will be called by a libretro frontend to validate certain expected behaviors
3
+ * and simplify mapping core data to the RAIntegration DLL.
4
+ *
5
+ * Originally designed to be shared between RALibretro and RetroArch, but will simplify
6
+ * integrating with any other frontends.
7
+ */
8
+
9
+ #include "rc_libretro.h"
10
+
11
+ #include "rc_consoles.h"
12
+ #include "rc_compat.h"
13
+ #include "rhash/rc_hash_internal.h"
14
+
15
+ #include <ctype.h>
16
+ #include <string.h>
17
+
18
+ static rc_libretro_message_callback rc_libretro_verbose_message_callback = NULL;
19
+
20
+ /* a value that starts with a comma is a CSV.
21
+ * if it starts with an exclamation point, it's everything but the provided value.
22
+ * if it starts with an exclamntion point followed by a comma, it's everything but the CSV values.
23
+ * values are case-insensitive */
24
+ typedef struct rc_disallowed_core_settings_t
25
+ {
26
+ const char* library_name;
27
+ const rc_disallowed_setting_t* disallowed_settings;
28
+ } rc_disallowed_core_settings_t;
29
+
30
+
31
+ static const rc_disallowed_setting_t _rc_disallowed_beetle_psx_settings[] = {
32
+ { "beetle_psx_cpu_freq_scale", "<100" },
33
+ { NULL, NULL }
34
+ };
35
+
36
+ static const rc_disallowed_setting_t _rc_disallowed_beetle_psx_hw_settings[] = {
37
+ { "beetle_psx_hw_cpu_freq_scale", "<100" },
38
+ { NULL, NULL }
39
+ };
40
+
41
+ static const rc_disallowed_setting_t _rc_disallowed_bsnes_settings[] = {
42
+ { "bsnes_region", "pal" },
43
+ { NULL, NULL }
44
+ };
45
+
46
+ static const rc_disallowed_setting_t _rc_disallowed_cap32_settings[] = {
47
+ { "cap32_autorun", "disabled" },
48
+ { NULL, NULL }
49
+ };
50
+
51
+ static const rc_disallowed_setting_t _rc_disallowed_dolphin_settings[] = {
52
+ { "dolphin_cheats_enabled", "enabled" },
53
+ { NULL, NULL }
54
+ };
55
+
56
+ static const rc_disallowed_setting_t _rc_disallowed_dosbox_pure_settings[] = {
57
+ { "dosbox_pure_strict_mode", "false" },
58
+ { NULL, NULL }
59
+ };
60
+
61
+ static const rc_disallowed_setting_t _rc_disallowed_duckstation_settings[] = {
62
+ { "duckstation_CDROM.LoadImagePatches", "true" },
63
+ { NULL, NULL }
64
+ };
65
+
66
+ static const rc_disallowed_setting_t _rc_disallowed_ecwolf_settings[] = {
67
+ { "ecwolf-invulnerability", "enabled" },
68
+ { NULL, NULL }
69
+ };
70
+
71
+ static const rc_disallowed_setting_t _rc_disallowed_fbneo_settings[] = {
72
+ { "fbneo-allow-patched-romsets", "enabled" },
73
+ { "fbneo-cheat-*", "!,Disabled,0 - Disabled" },
74
+ { "fbneo-cpu-speed-adjust", "??%" }, /* disallow speeds under 100% */
75
+ { "fbneo-dipswitch-*", "Universe BIOS*" },
76
+ { "fbneo-neogeo-mode", "UNIBIOS" },
77
+ { NULL, NULL }
78
+ };
79
+
80
+ static const rc_disallowed_setting_t _rc_disallowed_fceumm_settings[] = {
81
+ { "fceumm_game_genie", "!disabled" },
82
+ { "fceumm_region", ",PAL,Dendy" },
83
+ { NULL, NULL }
84
+ };
85
+
86
+ static const rc_disallowed_setting_t _rc_disallowed_flycast_settings[] = {
87
+ { "reicast_sh4clock", "<200" },
88
+ { NULL, NULL }
89
+ };
90
+
91
+ static const rc_disallowed_setting_t _rc_disallowed_gpgx_settings[] = {
92
+ { "genesis_plus_gx_lock_on", ",action replay (pro),game genie" },
93
+ { "genesis_plus_gx_region_detect", "pal" },
94
+ { NULL, NULL }
95
+ };
96
+
97
+ static const rc_disallowed_setting_t _rc_disallowed_gpgx_wide_settings[] = {
98
+ { "genesis_plus_gx_wide_lock_on", ",action replay (pro),game genie" },
99
+ { "genesis_plus_gx_wide_region_detect", "pal" },
100
+ { NULL, NULL }
101
+ };
102
+
103
+ static const rc_disallowed_setting_t _rc_disallowed_mesen_settings[] = {
104
+ { "mesen_region", ",PAL,Dendy" },
105
+ { NULL, NULL }
106
+ };
107
+
108
+ static const rc_disallowed_setting_t _rc_disallowed_mesen_s_settings[] = {
109
+ { "mesen-s_region", "PAL" },
110
+ { NULL, NULL }
111
+ };
112
+
113
+ static const rc_disallowed_setting_t _rc_disallowed_neocd_settings[] = {
114
+ { "neocd_bios", "uni-bios*" },
115
+ { NULL, NULL }
116
+ };
117
+
118
+ static const rc_disallowed_setting_t _rc_disallowed_pcsx_rearmed_settings[] = {
119
+ { "pcsx_rearmed_psxclock", ",!auto,<55" },
120
+ { "pcsx_rearmed_region", "pal" },
121
+ { NULL, NULL }
122
+ };
123
+
124
+ static const rc_disallowed_setting_t _rc_disallowed_picodrive_settings[] = {
125
+ { "picodrive_region", ",Europe,Japan PAL" },
126
+ { NULL, NULL }
127
+ };
128
+
129
+ static const rc_disallowed_setting_t _rc_disallowed_ppsspp_settings[] = {
130
+ { "ppsspp_cheats", "enabled" },
131
+ { NULL, NULL }
132
+ };
133
+
134
+ static const rc_disallowed_setting_t _rc_disallowed_quasi88_settings[] = {
135
+ { "q88_cpu_clock", ",1,2" },
136
+ { NULL, NULL }
137
+ };
138
+
139
+ static const rc_disallowed_setting_t _rc_disallowed_smsplus_settings[] = {
140
+ { "smsplus_region", "pal" },
141
+ { NULL, NULL }
142
+ };
143
+
144
+ static const rc_disallowed_setting_t _rc_disallowed_snes9x_settings[] = {
145
+ { "snes9x_gfx_clip", "disabled" },
146
+ { "snes9x_gfx_transp", "disabled" },
147
+ { "snes9x_layer_*", "disabled" },
148
+ { "snes9x_region", "pal" },
149
+ { NULL, NULL }
150
+ };
151
+
152
+ static const rc_disallowed_setting_t _rc_disallowed_swanstation_settings[] = {
153
+ { "swanstation_CPU_Overclock", "<100" },
154
+ { NULL, NULL }
155
+ };
156
+
157
+ static const rc_disallowed_setting_t _rc_disallowed_vice_settings[] = {
158
+ { "vice_autostart", "disabled" }, /* autostart dictates initial load and reset from menu */
159
+ { "vice_reset", "!autostart" }, /* reset dictates behavior when pressing reset button (END) */
160
+ { NULL, NULL }
161
+ };
162
+
163
+ static const rc_disallowed_setting_t _rc_disallowed_virtual_jaguar_settings[] = {
164
+ { "virtualjaguar_pal", "enabled" },
165
+ { NULL, NULL }
166
+ };
167
+
168
+ static const rc_disallowed_core_settings_t rc_disallowed_core_settings[] = {
169
+ { "Beetle PSX", _rc_disallowed_beetle_psx_settings },
170
+ { "Beetle PSX HW", _rc_disallowed_beetle_psx_hw_settings },
171
+ { "bsnes-mercury", _rc_disallowed_bsnes_settings },
172
+ { "cap32", _rc_disallowed_cap32_settings },
173
+ { "dolphin-emu", _rc_disallowed_dolphin_settings },
174
+ { "DOSBox-pure", _rc_disallowed_dosbox_pure_settings },
175
+ { "DuckStation", _rc_disallowed_duckstation_settings },
176
+ { "ecwolf", _rc_disallowed_ecwolf_settings },
177
+ { "FCEUmm", _rc_disallowed_fceumm_settings },
178
+ { "FinalBurn Neo", _rc_disallowed_fbneo_settings },
179
+ { "Flycast", _rc_disallowed_flycast_settings },
180
+ { "Genesis Plus GX", _rc_disallowed_gpgx_settings },
181
+ { "Genesis Plus GX Wide", _rc_disallowed_gpgx_wide_settings },
182
+ { "Mesen", _rc_disallowed_mesen_settings },
183
+ { "Mesen-S", _rc_disallowed_mesen_s_settings },
184
+ { "NeoCD", _rc_disallowed_neocd_settings },
185
+ { "PPSSPP", _rc_disallowed_ppsspp_settings },
186
+ { "PCSX-ReARMed", _rc_disallowed_pcsx_rearmed_settings },
187
+ { "PicoDrive", _rc_disallowed_picodrive_settings },
188
+ { "QUASI88", _rc_disallowed_quasi88_settings },
189
+ { "SMS Plus GX", _rc_disallowed_smsplus_settings },
190
+ { "Snes9x", _rc_disallowed_snes9x_settings },
191
+ { "SwanStation", _rc_disallowed_swanstation_settings },
192
+ { "VICE x64", _rc_disallowed_vice_settings },
193
+ { "Virtual Jaguar", _rc_disallowed_virtual_jaguar_settings },
194
+ { NULL, NULL }
195
+ };
196
+
197
+ static int rc_libretro_string_equal_nocase_wildcard(const char* test, const char* match) {
198
+ char c1, c2;
199
+ while ((c1 = *test++)) {
200
+ if (tolower(c1) != tolower(c2 = *match++) && c2 != '?')
201
+ return (c2 == '*');
202
+ }
203
+
204
+ return (*match == '\0');
205
+ }
206
+
207
+ static int rc_libretro_numeric_less_than(const char* test, const char* value) {
208
+ int test_num = atoi(test);
209
+ int value_num = atoi(value);
210
+ return (test_num < value_num);
211
+ }
212
+
213
+ static int rc_libretro_match_token(const char* val, const char* token, size_t size, int* result) {
214
+ if (*token == '!') {
215
+ /* !X => if X is a match, it's explicitly allowed. match with result = false */
216
+ if (rc_libretro_match_token(val, token + 1, size - 1, result)) {
217
+ *result = 0;
218
+ return 1;
219
+ }
220
+ }
221
+
222
+ if (*token == '<') {
223
+ /* if val < token, match with result = true */
224
+ char buffer[128];
225
+ memcpy(buffer, token + 1, size - 1);
226
+ buffer[size - 1] = '\0';
227
+ if (rc_libretro_numeric_less_than(val, buffer)) {
228
+ *result = 1;
229
+ return 1;
230
+ }
231
+ }
232
+
233
+ if (memcmp(token, val, size) == 0 && val[size] == 0) {
234
+ /* exact match, match with result = true */
235
+ *result = 1;
236
+ return 1;
237
+ }
238
+ else {
239
+ /* check for case insensitive match */
240
+ char buffer[128];
241
+ memcpy(buffer, token, size);
242
+ buffer[size] = '\0';
243
+ if (rc_libretro_string_equal_nocase_wildcard(val, buffer)) {
244
+ /* case insensitive match, match with result = true */
245
+ *result = 1;
246
+ return 1;
247
+ }
248
+ }
249
+
250
+ /* no match */
251
+ return 0;
252
+ }
253
+
254
+ static int rc_libretro_match_value(const char* val, const char* match) {
255
+ int result = 0;
256
+
257
+ /* if value starts with a comma, it's a CSV list of potential matches */
258
+ if (*match == ',') {
259
+ do {
260
+ const char* ptr = ++match;
261
+ size_t size;
262
+
263
+ while (*match && *match != ',')
264
+ ++match;
265
+
266
+ size = match - ptr;
267
+ if (rc_libretro_match_token(val, ptr, size, &result))
268
+ return result;
269
+
270
+ } while (*match == ',');
271
+ }
272
+ else {
273
+ /* a leading exclamation point means the provided value(s) are not forbidden (are allowed) */
274
+ if (*match == '!')
275
+ return !rc_libretro_match_value(val, &match[1]);
276
+
277
+ /* just a single value, attempt to match it */
278
+ if (rc_libretro_match_token(val, match, strlen(match), &result))
279
+ return result;
280
+ }
281
+
282
+ /* value did not match filters, assume it's allowed */
283
+ return 0;
284
+ }
285
+
286
+ int rc_libretro_is_setting_allowed(const rc_disallowed_setting_t* disallowed_settings, const char* setting, const char* value) {
287
+ const char* key;
288
+ size_t key_len;
289
+
290
+ for (; disallowed_settings->setting; ++disallowed_settings) {
291
+ key = disallowed_settings->setting;
292
+ key_len = strlen(key);
293
+
294
+ if (key[key_len - 1] == '*') {
295
+ if (memcmp(setting, key, key_len - 1) == 0) {
296
+ if (rc_libretro_match_value(value, disallowed_settings->value))
297
+ return 0;
298
+ }
299
+ }
300
+ else {
301
+ if (memcmp(setting, key, key_len + 1) == 0) {
302
+ if (rc_libretro_match_value(value, disallowed_settings->value))
303
+ return 0;
304
+ }
305
+ }
306
+ }
307
+
308
+ return 1;
309
+ }
310
+
311
+ const rc_disallowed_setting_t* rc_libretro_get_disallowed_settings(const char* library_name) {
312
+ const rc_disallowed_core_settings_t* core_filter = rc_disallowed_core_settings;
313
+ size_t library_name_length;
314
+
315
+ if (!library_name || !library_name[0])
316
+ return NULL;
317
+
318
+ library_name_length = strlen(library_name) + 1;
319
+ while (core_filter->library_name) {
320
+ if (memcmp(core_filter->library_name, library_name, library_name_length) == 0)
321
+ return core_filter->disallowed_settings;
322
+
323
+ ++core_filter;
324
+ }
325
+
326
+ return NULL;
327
+ }
328
+
329
+ typedef struct rc_disallowed_core_systems_t
330
+ {
331
+ const char* library_name;
332
+ const uint32_t disallowed_consoles[4];
333
+ } rc_disallowed_core_systems_t;
334
+
335
+ static const rc_disallowed_core_systems_t rc_disallowed_core_systems[] = {
336
+ /* https://github.com/libretro/Mesen-S/issues/8 */
337
+ { "Mesen-S", { RC_CONSOLE_GAMEBOY, RC_CONSOLE_GAMEBOY_COLOR, 0 }},
338
+ { NULL, { 0 } }
339
+ };
340
+
341
+ int rc_libretro_is_system_allowed(const char* library_name, uint32_t console_id) {
342
+ const rc_disallowed_core_systems_t* core_filter = rc_disallowed_core_systems;
343
+ size_t library_name_length;
344
+ size_t i;
345
+
346
+ if (!library_name || !library_name[0])
347
+ return 1;
348
+
349
+ library_name_length = strlen(library_name) + 1;
350
+ while (core_filter->library_name) {
351
+ if (memcmp(core_filter->library_name, library_name, library_name_length) == 0) {
352
+ for (i = 0; i < sizeof(core_filter->disallowed_consoles) / sizeof(core_filter->disallowed_consoles[0]); ++i) {
353
+ if (core_filter->disallowed_consoles[i] == console_id)
354
+ return 0;
355
+ }
356
+ break;
357
+ }
358
+
359
+ ++core_filter;
360
+ }
361
+
362
+ return 1;
363
+ }
364
+
365
+ uint8_t* rc_libretro_memory_find_avail(const rc_libretro_memory_regions_t* regions, uint32_t address, uint32_t* avail) {
366
+ uint32_t i;
367
+
368
+ for (i = 0; i < regions->count; ++i) {
369
+ const size_t size = regions->size[i];
370
+ if (address < size) {
371
+ if (regions->data[i] == NULL)
372
+ break;
373
+
374
+ if (avail)
375
+ *avail = (uint32_t)(size - address);
376
+
377
+ return &regions->data[i][address];
378
+ }
379
+
380
+ address -= (uint32_t)size;
381
+ }
382
+
383
+ if (avail)
384
+ *avail = 0;
385
+
386
+ return NULL;
387
+ }
388
+
389
+ uint8_t* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regions, uint32_t address) {
390
+ return rc_libretro_memory_find_avail(regions, address, NULL);
391
+ }
392
+
393
+ uint32_t rc_libretro_memory_read(const rc_libretro_memory_regions_t* regions, uint32_t address,
394
+ uint8_t* buffer, uint32_t num_bytes) {
395
+ uint32_t bytes_read = 0;
396
+ uint32_t avail;
397
+ uint32_t i;
398
+
399
+ for (i = 0; i < regions->count; ++i) {
400
+ const size_t size = regions->size[i];
401
+ if (address >= size) {
402
+ /* address is not in this block, adjust and look at next block */
403
+ address -= (unsigned)size;
404
+ continue;
405
+ }
406
+
407
+ if (regions->data[i] == NULL) /* no memory associated to this block. abort */
408
+ break;
409
+
410
+ avail = (unsigned)(size - address);
411
+ if (avail >= num_bytes) {
412
+ /* requested memory is fully within this block, copy and return it */
413
+ memcpy(buffer, &regions->data[i][address], num_bytes);
414
+ bytes_read += num_bytes;
415
+ return bytes_read;
416
+ }
417
+
418
+ /* copy whatever is available in this block, and adjust for the next block */
419
+ memcpy(buffer, &regions->data[i][address], avail);
420
+ buffer += avail;
421
+ bytes_read += avail;
422
+ num_bytes -= avail;
423
+ address = 0;
424
+ }
425
+
426
+ return bytes_read;
427
+ }
428
+
429
+ void rc_libretro_init_verbose_message_callback(rc_libretro_message_callback callback) {
430
+ rc_libretro_verbose_message_callback = callback;
431
+ }
432
+
433
+ static void rc_libretro_verbose(const char* message) {
434
+ if (rc_libretro_verbose_message_callback)
435
+ rc_libretro_verbose_message_callback(message);
436
+ }
437
+
438
+ static const char* rc_memory_type_str(int type) {
439
+ switch (type)
440
+ {
441
+ case RC_MEMORY_TYPE_SAVE_RAM:
442
+ return "SRAM";
443
+ case RC_MEMORY_TYPE_VIDEO_RAM:
444
+ return "VRAM";
445
+ case RC_MEMORY_TYPE_UNUSED:
446
+ return "UNUSED";
447
+ default:
448
+ break;
449
+ }
450
+
451
+ return "SYSTEM RAM";
452
+ }
453
+
454
+ static void rc_libretro_memory_register_region(rc_libretro_memory_regions_t* regions, int type,
455
+ uint8_t* data, size_t size, const char* description) {
456
+ if (size == 0)
457
+ return;
458
+
459
+ if (regions->count == (sizeof(regions->size) / sizeof(regions->size[0]))) {
460
+ rc_libretro_verbose("Too many memory memory regions to register");
461
+ return;
462
+ }
463
+
464
+ if (!data && regions->count > 0 && !regions->data[regions->count - 1]) {
465
+ /* extend null region */
466
+ regions->size[regions->count - 1] += size;
467
+ }
468
+ else if (data && regions->count > 0 &&
469
+ data == (regions->data[regions->count - 1] + regions->size[regions->count - 1])) {
470
+ /* extend non-null region */
471
+ regions->size[regions->count - 1] += size;
472
+ }
473
+ else {
474
+ /* create new region */
475
+ regions->data[regions->count] = data;
476
+ regions->size[regions->count] = size;
477
+ ++regions->count;
478
+ }
479
+
480
+ regions->total_size += size;
481
+
482
+ if (rc_libretro_verbose_message_callback) {
483
+ char message[128];
484
+ snprintf(message, sizeof(message), "Registered 0x%04X bytes of %s at $%06X (%s)", (unsigned)size,
485
+ rc_memory_type_str(type), (unsigned)(regions->total_size - size), description);
486
+ rc_libretro_verbose_message_callback(message);
487
+ }
488
+ }
489
+
490
+ static void rc_libretro_memory_init_without_regions(rc_libretro_memory_regions_t* regions,
491
+ rc_libretro_get_core_memory_info_func get_core_memory_info) {
492
+ /* no regions specified, assume system RAM followed by save RAM */
493
+ char description[64];
494
+ rc_libretro_core_memory_info_t info;
495
+
496
+ snprintf(description, sizeof(description), "offset 0x%06x", 0);
497
+
498
+ get_core_memory_info(RETRO_MEMORY_SYSTEM_RAM, &info);
499
+ if (info.size)
500
+ rc_libretro_memory_register_region(regions, RC_MEMORY_TYPE_SYSTEM_RAM, info.data, info.size, description);
501
+
502
+ get_core_memory_info(RETRO_MEMORY_SAVE_RAM, &info);
503
+ if (info.size)
504
+ rc_libretro_memory_register_region(regions, RC_MEMORY_TYPE_SAVE_RAM, info.data, info.size, description);
505
+ }
506
+
507
+ static const struct retro_memory_descriptor* rc_libretro_memory_get_descriptor(const struct retro_memory_map* mmap, uint32_t real_address, size_t* offset)
508
+ {
509
+ const struct retro_memory_descriptor* desc = mmap->descriptors;
510
+ const struct retro_memory_descriptor* end = desc + mmap->num_descriptors;
511
+
512
+ for (; desc < end; desc++) {
513
+ if (desc->select == 0) {
514
+ /* if select is 0, attempt to explcitly match the address */
515
+ if (real_address >= desc->start && real_address < desc->start + desc->len) {
516
+ *offset = real_address - desc->start;
517
+ return desc;
518
+ }
519
+ }
520
+ else {
521
+ /* otherwise, attempt to match the address by matching the select bits */
522
+ /* address is in the block if (addr & select) == (start & select) */
523
+ if (((desc->start ^ real_address) & desc->select) == 0) {
524
+ /* get the relative offset of the address from the start of the memory block */
525
+ uint32_t reduced_address = real_address - (unsigned)desc->start;
526
+
527
+ /* remove any bits from the reduced_address that correspond to the bits in the disconnect
528
+ * mask and collapse the remaining bits. this code was copied from the mmap_reduce function
529
+ * in RetroArch. i'm not exactly sure how it works, but it does. */
530
+ uint32_t disconnect_mask = (unsigned)desc->disconnect;
531
+ while (disconnect_mask) {
532
+ const uint32_t tmp = (disconnect_mask - 1) & ~disconnect_mask;
533
+ reduced_address = (reduced_address & tmp) | ((reduced_address >> 1) & ~tmp);
534
+ disconnect_mask = (disconnect_mask & (disconnect_mask - 1)) >> 1;
535
+ }
536
+
537
+ /* calculate the offset within the descriptor */
538
+ *offset = reduced_address;
539
+
540
+ /* sanity check - make sure the descriptor is large enough to hold the target address */
541
+ if (reduced_address < desc->len)
542
+ return desc;
543
+ }
544
+ }
545
+ }
546
+
547
+ *offset = 0;
548
+ return NULL;
549
+ }
550
+
551
+ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
552
+ const rc_memory_regions_t* console_regions) {
553
+ char description[64];
554
+ uint32_t i;
555
+ uint8_t* region_start;
556
+ uint8_t* desc_start;
557
+ size_t desc_size;
558
+ size_t offset;
559
+
560
+ for (i = 0; i < console_regions->num_regions; ++i) {
561
+ const rc_memory_region_t* console_region = &console_regions->region[i];
562
+ size_t console_region_size = console_region->end_address - console_region->start_address + 1;
563
+ uint32_t real_address = console_region->real_address;
564
+ uint32_t disconnect_size = 0;
565
+
566
+ while (console_region_size > 0) {
567
+ const struct retro_memory_descriptor* desc = rc_libretro_memory_get_descriptor(mmap, real_address, &offset);
568
+ if (!desc) {
569
+ if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
570
+ snprintf(description, sizeof(description), "Could not map region starting at $%06X",
571
+ (unsigned)(real_address - console_region->real_address + console_region->start_address));
572
+ rc_libretro_verbose(description);
573
+ }
574
+
575
+ if (disconnect_size && console_region_size > disconnect_size) {
576
+ rc_libretro_memory_register_region(regions, console_region->type, NULL, disconnect_size, "null filler");
577
+ console_region_size -= disconnect_size;
578
+ real_address += disconnect_size;
579
+ disconnect_size = 0;
580
+ continue;
581
+ }
582
+
583
+ rc_libretro_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler");
584
+ break;
585
+ }
586
+
587
+ snprintf(description, sizeof(description), "descriptor %u, offset 0x%06X%s",
588
+ (unsigned)(desc - mmap->descriptors) + 1, (int)offset, desc->ptr ? "" : " [no pointer]");
589
+
590
+ if (desc->ptr) {
591
+ desc_start = (uint8_t*)desc->ptr + desc->offset;
592
+ region_start = desc_start + offset;
593
+ }
594
+ else {
595
+ region_start = NULL;
596
+ }
597
+
598
+ desc_size = desc->len - offset;
599
+ if (desc->disconnect && desc_size > desc->disconnect) {
600
+ /* if we need to extract a disconnect bit, the largest block we can read is up to
601
+ * the next time that bit flips */
602
+ /* https://stackoverflow.com/questions/12247186/find-the-lowest-set-bit */
603
+ disconnect_size = (desc->disconnect & -((int)desc->disconnect));
604
+ desc_size = disconnect_size - (real_address & (disconnect_size - 1));
605
+ }
606
+
607
+ if (console_region_size > desc_size) {
608
+ if (desc_size == 0) {
609
+ if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
610
+ snprintf(description, sizeof(description), "Could not map region starting at $%06X",
611
+ (unsigned)(real_address - console_region->real_address + console_region->start_address));
612
+ rc_libretro_verbose(description);
613
+ }
614
+
615
+ rc_libretro_memory_register_region(regions, console_region->type, NULL, console_region_size, "null filler");
616
+ console_region_size = 0;
617
+ }
618
+ else {
619
+ rc_libretro_memory_register_region(regions, console_region->type, region_start, desc_size, description);
620
+ console_region_size -= desc_size;
621
+ real_address += (unsigned)desc_size;
622
+ }
623
+ }
624
+ else {
625
+ rc_libretro_memory_register_region(regions, console_region->type, region_start, console_region_size, description);
626
+ console_region_size = 0;
627
+ }
628
+ }
629
+ }
630
+ }
631
+
632
+ static uint32_t rc_libretro_memory_console_region_to_ram_type(uint8_t region_type) {
633
+ switch (region_type)
634
+ {
635
+ case RC_MEMORY_TYPE_SAVE_RAM:
636
+ return RETRO_MEMORY_SAVE_RAM;
637
+ case RC_MEMORY_TYPE_VIDEO_RAM:
638
+ return RETRO_MEMORY_VIDEO_RAM;
639
+ default:
640
+ break;
641
+ }
642
+
643
+ return RETRO_MEMORY_SYSTEM_RAM;
644
+ }
645
+
646
+ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regions_t* regions,
647
+ rc_libretro_get_core_memory_info_func get_core_memory_info, const rc_memory_regions_t* console_regions) {
648
+ char description[64];
649
+ uint32_t i, j;
650
+ rc_libretro_core_memory_info_t info;
651
+ size_t offset;
652
+ int found_aligning_padding = 0;
653
+
654
+ for (i = 0; i < console_regions->num_regions; ++i) {
655
+ const rc_memory_region_t* console_region = &console_regions->region[i];
656
+ const size_t console_region_size = console_region->end_address - console_region->start_address + 1;
657
+ const uint32_t type = rc_libretro_memory_console_region_to_ram_type(console_region->type);
658
+ uint32_t base_address = 0;
659
+
660
+ if (console_region->type == RC_MEMORY_TYPE_UNUSED && console_region_size >= 0x10000 && !found_aligning_padding) {
661
+ if (console_regions->region[console_regions->num_regions - 1].end_address > 0x01000000) {
662
+ /* assume anything exposing more than 16MB of regions with at least one 64KB+ UNUSED region
663
+ * is padding so things align with real addresses. this indicates the memory is disjoint
664
+ * in the system, so we cannot expect it to be contiguous in the RETRO_SYSTEM_RAM.
665
+ * stop processing regions now, and just fill the remaining memory map with null filler. */
666
+ found_aligning_padding = 1;
667
+ }
668
+ }
669
+
670
+ for (j = 0; j <= i; ++j) {
671
+ const rc_memory_region_t* console_region2 = &console_regions->region[j];
672
+ if (rc_libretro_memory_console_region_to_ram_type(console_region2->type) == type) {
673
+ base_address = console_region2->start_address;
674
+ break;
675
+ }
676
+ }
677
+ offset = console_region->start_address - base_address;
678
+
679
+ if (!found_aligning_padding) {
680
+ get_core_memory_info(type, &info);
681
+ }
682
+ else {
683
+ info.data = NULL;
684
+ info.size = console_region_size;
685
+ }
686
+
687
+ if (offset < info.size) {
688
+ info.size -= offset;
689
+
690
+ if (info.data) {
691
+ snprintf(description, sizeof(description), "offset 0x%06X", (int)offset);
692
+ info.data += offset;
693
+ }
694
+ else {
695
+ snprintf(description, sizeof(description), "null filler");
696
+ }
697
+ }
698
+ else {
699
+ if (rc_libretro_verbose_message_callback && console_region->type != RC_MEMORY_TYPE_UNUSED) {
700
+ snprintf(description, sizeof(description), "Could not map region starting at $%06X", (unsigned)console_region->start_address);
701
+ rc_libretro_verbose(description);
702
+ }
703
+
704
+ info.data = NULL;
705
+ info.size = 0;
706
+ }
707
+
708
+ if (console_region_size > info.size) {
709
+ /* want more than what is available, take what we can and null fill the rest */
710
+ rc_libretro_memory_register_region(regions, console_region->type, info.data, info.size, description);
711
+ rc_libretro_memory_register_region(regions, console_region->type, NULL, console_region_size - info.size, "null filler");
712
+ }
713
+ else {
714
+ /* only take as much as we need */
715
+ rc_libretro_memory_register_region(regions, console_region->type, info.data, console_region_size, description);
716
+ }
717
+ }
718
+ }
719
+
720
+ int rc_libretro_memory_init(rc_libretro_memory_regions_t* regions, const struct retro_memory_map* mmap,
721
+ rc_libretro_get_core_memory_info_func get_core_memory_info, uint32_t console_id) {
722
+ const rc_memory_regions_t* console_regions = rc_console_memory_regions(console_id);
723
+ rc_libretro_memory_regions_t new_regions;
724
+ int has_valid_region = 0;
725
+ uint32_t i;
726
+
727
+ if (!regions)
728
+ return 0;
729
+
730
+ memset(&new_regions, 0, sizeof(new_regions));
731
+
732
+ if (console_regions == NULL || console_regions->num_regions == 0)
733
+ rc_libretro_memory_init_without_regions(&new_regions, get_core_memory_info);
734
+ else if (mmap && mmap->num_descriptors != 0)
735
+ rc_libretro_memory_init_from_memory_map(&new_regions, mmap, console_regions);
736
+ else
737
+ rc_libretro_memory_init_from_unmapped_memory(&new_regions, get_core_memory_info, console_regions);
738
+
739
+ /* determine if any valid regions were found */
740
+ for (i = 0; i < new_regions.count; i++) {
741
+ if (new_regions.data[i]) {
742
+ has_valid_region = 1;
743
+ break;
744
+ }
745
+ }
746
+
747
+ memcpy(regions, &new_regions, sizeof(*regions));
748
+ return has_valid_region;
749
+ }
750
+
751
+ void rc_libretro_memory_destroy(rc_libretro_memory_regions_t* regions) {
752
+ memset(regions, 0, sizeof(*regions));
753
+ }
754
+
755
+ void rc_libretro_hash_set_init(struct rc_libretro_hash_set_t* hash_set,
756
+ const char* m3u_path, rc_libretro_get_image_path_func get_image_path,
757
+ const rc_hash_filereader_t* file_reader) {
758
+ char image_path[1024];
759
+ char* m3u_contents;
760
+ char* ptr;
761
+ int64_t file_len;
762
+ void* file_handle;
763
+ int index = 0;
764
+
765
+ memset(hash_set, 0, sizeof(*hash_set));
766
+
767
+ if (!rc_path_compare_extension(m3u_path, "m3u"))
768
+ return;
769
+
770
+ file_handle = file_reader->open(m3u_path);
771
+ if (!file_handle) {
772
+ rc_hash_iterator_t iterator;
773
+ memset(&iterator, 0, sizeof(iterator));
774
+ memcpy(&iterator.callbacks, &hash_set->callbacks, sizeof(hash_set->callbacks));
775
+ rc_hash_iterator_error(&iterator, "Could not open playlist");
776
+ return;
777
+ }
778
+
779
+ file_reader->seek(file_handle, 0, SEEK_END);
780
+ file_len = file_reader->tell(file_handle);
781
+ file_reader->seek(file_handle, 0, SEEK_SET);
782
+
783
+ m3u_contents = (char*)malloc((size_t)file_len + 1);
784
+ if (m3u_contents) {
785
+ file_reader->read(file_handle, m3u_contents, (int)file_len);
786
+ m3u_contents[file_len] = '\0';
787
+
788
+ ptr = m3u_contents;
789
+ do
790
+ {
791
+ /* ignore whitespace */
792
+ while (isspace((int)*ptr))
793
+ ++ptr;
794
+
795
+ if (*ptr == '#') {
796
+ /* ignore comment unless it's the special SAVEDISK extension */
797
+ if (memcmp(ptr, "#SAVEDISK:", 10) == 0) {
798
+ /* get the path to the save disk from the frontend, assign it a bogus hash so
799
+ * it doesn't get hashed later */
800
+ if (get_image_path(index, image_path, sizeof(image_path))) {
801
+ const char save_disk_hash[33] = "[SAVE DISK]";
802
+ rc_libretro_hash_set_add(hash_set, image_path, -1, save_disk_hash);
803
+ ++index;
804
+ }
805
+ }
806
+ }
807
+ else {
808
+ /* non-empty line, tally a file */
809
+ ++index;
810
+ }
811
+
812
+ /* find the end of the line */
813
+ while (*ptr && *ptr != '\n')
814
+ ++ptr;
815
+
816
+ } while (*ptr);
817
+
818
+ free(m3u_contents);
819
+ }
820
+
821
+ if (file_reader->close)
822
+ file_reader->close(file_handle);
823
+
824
+ if (hash_set->entries_count > 0) {
825
+ /* at least one save disk was found. make sure the core supports the #SAVEDISK: extension by
826
+ * asking for the last expected disk. if it's not found, assume no #SAVEDISK: support */
827
+ if (!get_image_path(index - 1, image_path, sizeof(image_path)))
828
+ hash_set->entries_count = 0;
829
+ }
830
+ }
831
+
832
+ void rc_libretro_hash_set_destroy(struct rc_libretro_hash_set_t* hash_set) {
833
+ if (hash_set->entries)
834
+ free(hash_set->entries);
835
+ memset(hash_set, 0, sizeof(*hash_set));
836
+ }
837
+
838
+ static uint32_t rc_libretro_djb2(const char* input)
839
+ {
840
+ uint32_t result = 5381;
841
+ char c;
842
+
843
+ while ((c = *input++) != '\0')
844
+ result = ((result << 5) + result) + c; /* result = result * 33 + c */
845
+
846
+ return result;
847
+ }
848
+
849
+ void rc_libretro_hash_set_add(struct rc_libretro_hash_set_t* hash_set,
850
+ const char* path, uint32_t game_id, const char hash[33]) {
851
+ const uint32_t path_djb2 = (path != NULL) ? rc_libretro_djb2(path) : 0;
852
+ struct rc_libretro_hash_entry_t* entry = NULL;
853
+ struct rc_libretro_hash_entry_t* scan;
854
+ struct rc_libretro_hash_entry_t* stop = hash_set->entries + hash_set->entries_count;;
855
+
856
+ if (path_djb2) {
857
+ /* attempt to match the path */
858
+ for (scan = hash_set->entries; scan < stop; ++scan) {
859
+ if (scan->path_djb2 == path_djb2) {
860
+ entry = scan;
861
+ break;
862
+ }
863
+ }
864
+ }
865
+
866
+ if (!entry)
867
+ {
868
+ /* entry not found, allocate a new one */
869
+ if (hash_set->entries_size == 0) {
870
+ hash_set->entries_size = 4;
871
+ hash_set->entries = (struct rc_libretro_hash_entry_t*)
872
+ malloc(hash_set->entries_size * sizeof(struct rc_libretro_hash_entry_t));
873
+ }
874
+ else if (hash_set->entries_count == hash_set->entries_size) {
875
+ hash_set->entries_size += 4;
876
+ hash_set->entries = (struct rc_libretro_hash_entry_t*)realloc(hash_set->entries,
877
+ hash_set->entries_size * sizeof(struct rc_libretro_hash_entry_t));
878
+ }
879
+
880
+ if (hash_set->entries == NULL) /* unexpected, but better than crashing */
881
+ return;
882
+
883
+ entry = hash_set->entries + hash_set->entries_count++;
884
+ }
885
+
886
+ /* update the entry */
887
+ entry->path_djb2 = path_djb2;
888
+ entry->game_id = game_id;
889
+ memcpy(entry->hash, hash, sizeof(entry->hash));
890
+ }
891
+
892
+ const char* rc_libretro_hash_set_get_hash(const struct rc_libretro_hash_set_t* hash_set, const char* path)
893
+ {
894
+ const uint32_t path_djb2 = rc_libretro_djb2(path);
895
+ struct rc_libretro_hash_entry_t* scan = hash_set->entries;
896
+ struct rc_libretro_hash_entry_t* stop = scan + hash_set->entries_count;
897
+ for (; scan < stop; ++scan) {
898
+ if (scan->path_djb2 == path_djb2)
899
+ return scan->hash;
900
+ }
901
+
902
+ return NULL;
903
+ }
904
+
905
+ int rc_libretro_hash_set_get_game_id(const struct rc_libretro_hash_set_t* hash_set, const char* hash)
906
+ {
907
+ struct rc_libretro_hash_entry_t* scan = hash_set->entries;
908
+ struct rc_libretro_hash_entry_t* stop = scan + hash_set->entries_count;
909
+ for (; scan < stop; ++scan) {
910
+ if (memcmp(scan->hash, hash, sizeof(scan->hash)) == 0)
911
+ return scan->game_id;
912
+ }
913
+
914
+ return 0;
915
+ }