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,426 @@
1
+ #include "rc_hash_internal.h"
2
+
3
+ #include "../rc_compat.h"
4
+
5
+ #include <ctype.h>
6
+
7
+ /* ===================================================== */
8
+
9
+ static int rc_hash_unheadered_iterator_buffer(char hash[33], const rc_hash_iterator_t* iterator, size_t header_size)
10
+ {
11
+ return rc_hash_buffer(hash, iterator->buffer + header_size, iterator->buffer_size - header_size, iterator);
12
+ }
13
+
14
+ static int rc_hash_iterator_buffer(char hash[33], const rc_hash_iterator_t* iterator)
15
+ {
16
+ return rc_hash_buffer(hash, iterator->buffer, iterator->buffer_size, iterator);
17
+ }
18
+
19
+ /* ===================================================== */
20
+
21
+ int rc_hash_7800(char hash[33], const rc_hash_iterator_t* iterator)
22
+ {
23
+ /* if the file contains a header, ignore it */
24
+ if (memcmp(&iterator->buffer[1], "ATARI7800", 9) == 0) {
25
+ rc_hash_iterator_verbose(iterator, "Ignoring 7800 header");
26
+ return rc_hash_unheadered_iterator_buffer(hash, iterator, 128);
27
+ }
28
+
29
+ return rc_hash_iterator_buffer(hash, iterator);
30
+ }
31
+
32
+ int rc_hash_arcade(char hash[33], const rc_hash_iterator_t* iterator)
33
+ {
34
+ /* arcade hash is just the hash of the filename (no extension) - the cores are pretty stringent about having the right ROM data */
35
+ const char* filename = rc_path_get_filename(iterator->path);
36
+ const char* ext = rc_path_get_extension(filename);
37
+ char buffer[128]; /* realistically, this should never need more than ~32 characters */
38
+ size_t filename_length = ext - filename - 1;
39
+
40
+ /* fbneo supports loading subsystems by using specific folder names.
41
+ * if one is found, include it in the hash.
42
+ * https://github.com/libretro/FBNeo/blob/master/src/burner/libretro/README.md#emulating-consoles-and-computers
43
+ */
44
+ if (filename > iterator->path + 1) {
45
+ int include_folder = 0;
46
+ const char* folder = filename - 1;
47
+ size_t parent_folder_length = 0;
48
+
49
+ do {
50
+ if (folder[-1] == '/' || folder[-1] == '\\')
51
+ break;
52
+
53
+ --folder;
54
+ } while (folder > iterator->path);
55
+
56
+ parent_folder_length = filename - folder - 1;
57
+ if (parent_folder_length < 16) {
58
+ char* ptr = buffer;
59
+ while (folder < filename - 1)
60
+ *ptr++ = tolower(*folder++);
61
+ *ptr = '\0';
62
+
63
+ folder = buffer;
64
+ }
65
+
66
+ switch (parent_folder_length) {
67
+ case 3:
68
+ if (memcmp(folder, "nes", 3) == 0 || /* NES */
69
+ memcmp(folder, "fds", 3) == 0 || /* FDS */
70
+ memcmp(folder, "sms", 3) == 0 || /* Master System */
71
+ memcmp(folder, "msx", 3) == 0 || /* MSX */
72
+ memcmp(folder, "ngp", 3) == 0 || /* NeoGeo Pocket */
73
+ memcmp(folder, "pce", 3) == 0 || /* PCEngine */
74
+ memcmp(folder, "chf", 3) == 0 || /* ChannelF */
75
+ memcmp(folder, "sgx", 3) == 0) /* SuperGrafX */
76
+ include_folder = 1;
77
+ break;
78
+ case 4:
79
+ if (memcmp(folder, "tg16", 4) == 0 || /* TurboGrafx-16 */
80
+ memcmp(folder, "msx1", 4) == 0) /* MSX */
81
+ include_folder = 1;
82
+ break;
83
+ case 5:
84
+ if (memcmp(folder, "neocd", 5) == 0) /* NeoGeo CD */
85
+ include_folder = 1;
86
+ break;
87
+ case 6:
88
+ if (memcmp(folder, "coleco", 6) == 0 || /* Colecovision */
89
+ memcmp(folder, "sg1000", 6) == 0) /* SG-1000 */
90
+ include_folder = 1;
91
+ break;
92
+ case 7:
93
+ if (memcmp(folder, "genesis", 7) == 0) /* Megadrive (Genesis) */
94
+ include_folder = 1;
95
+ break;
96
+ case 8:
97
+ if (memcmp(folder, "gamegear", 8) == 0 || /* Game Gear */
98
+ memcmp(folder, "megadriv", 8) == 0 || /* Megadrive */
99
+ memcmp(folder, "pcengine", 8) == 0 || /* PCEngine */
100
+ memcmp(folder, "channelf", 8) == 0 || /* ChannelF */
101
+ memcmp(folder, "spectrum", 8) == 0) /* ZX Spectrum */
102
+ include_folder = 1;
103
+ break;
104
+ case 9:
105
+ if (memcmp(folder, "megadrive", 9) == 0) /* Megadrive */
106
+ include_folder = 1;
107
+ break;
108
+ case 10:
109
+ if (memcmp(folder, "supergrafx", 10) == 0 || /* SuperGrafX */
110
+ memcmp(folder, "zxspectrum", 10) == 0) /* ZX Spectrum */
111
+ include_folder = 1;
112
+ break;
113
+ case 12:
114
+ if (memcmp(folder, "mastersystem", 12) == 0 || /* Master System */
115
+ memcmp(folder, "colecovision", 12) == 0) /* Colecovision */
116
+ include_folder = 1;
117
+ break;
118
+ default:
119
+ break;
120
+ }
121
+
122
+ if (include_folder) {
123
+ if (parent_folder_length + filename_length + 1 < sizeof(buffer)) {
124
+ buffer[parent_folder_length] = '_';
125
+ memcpy(&buffer[parent_folder_length + 1], filename, filename_length);
126
+ return rc_hash_buffer(hash, (uint8_t*)&buffer[0], parent_folder_length + filename_length + 1, iterator);
127
+ }
128
+ }
129
+ }
130
+
131
+ return rc_hash_buffer(hash, (uint8_t*)filename, filename_length, iterator);
132
+ }
133
+
134
+ static int rc_hash_text(char hash[33], const rc_hash_iterator_t* iterator)
135
+ {
136
+ md5_state_t md5;
137
+ const uint8_t* scan = iterator->buffer;
138
+ const uint8_t* stop = iterator->buffer + iterator->buffer_size;
139
+
140
+ md5_init(&md5);
141
+
142
+ do {
143
+ const uint8_t* line = scan;
144
+
145
+ /* find end of line */
146
+ while (scan < stop && *scan != '\r' && *scan != '\n')
147
+ ++scan;
148
+
149
+ md5_append(&md5, line, (int)(scan - line));
150
+
151
+ /* include a normalized line ending */
152
+ /* NOTE: this causes a line ending to be hashed at the end of the file, even if one was not present */
153
+ md5_append(&md5, (const uint8_t*)"\n", 1);
154
+
155
+ /* skip newline */
156
+ if (scan < stop && *scan == '\r')
157
+ ++scan;
158
+ if (scan < stop && *scan == '\n')
159
+ ++scan;
160
+
161
+ } while (scan < stop);
162
+
163
+ return rc_hash_finalize(iterator, &md5, hash);
164
+ }
165
+
166
+ int rc_hash_arduboy(char hash[33], const rc_hash_iterator_t* iterator)
167
+ {
168
+ if (iterator->path && rc_path_compare_extension(iterator->path, "arduboy")) {
169
+ #ifndef RC_HASH_NO_ZIP
170
+ return rc_hash_arduboyfx(hash, iterator);
171
+ #else
172
+ rc_hash_iterator_verbose(iterator, ".arduboy file processing not enabled");
173
+ return 0;
174
+ #endif
175
+ }
176
+
177
+ if (!iterator->buffer)
178
+ return rc_hash_buffered_file(hash, RC_CONSOLE_ARDUBOY, iterator);
179
+
180
+ /* https://en.wikipedia.org/wiki/Intel_HEX */
181
+ return rc_hash_text(hash, iterator);
182
+ }
183
+
184
+ int rc_hash_lynx(char hash[33], const rc_hash_iterator_t* iterator)
185
+ {
186
+ /* if the file contains a header, ignore it */
187
+ if (memcmp(&iterator->buffer[0], "LYNX", 5) == 0) {
188
+ rc_hash_iterator_verbose(iterator, "Ignoring LYNX header");
189
+ return rc_hash_unheadered_iterator_buffer(hash, iterator, 64);
190
+ }
191
+
192
+ return rc_hash_iterator_buffer(hash, iterator);
193
+ }
194
+
195
+ int rc_hash_nes(char hash[33], const rc_hash_iterator_t* iterator)
196
+ {
197
+ /* if the file contains a header, ignore it */
198
+ if (memcmp(&iterator->buffer[0], "NES\x1a", 4) == 0) {
199
+ rc_hash_iterator_verbose(iterator, "Ignoring NES header");
200
+ return rc_hash_unheadered_iterator_buffer(hash, iterator, 16);
201
+ }
202
+
203
+ if (memcmp(&iterator->buffer[0], "FDS\x1a", 4) == 0) {
204
+ rc_hash_iterator_verbose(iterator, "Ignoring FDS header");
205
+ return rc_hash_unheadered_iterator_buffer(hash, iterator, 16);
206
+ }
207
+
208
+ return rc_hash_iterator_buffer(hash, iterator);
209
+ }
210
+
211
+ int rc_hash_n64(char hash[33], const rc_hash_iterator_t* iterator)
212
+ {
213
+ uint8_t* buffer;
214
+ uint8_t* stop;
215
+ const size_t buffer_size = 65536;
216
+ md5_state_t md5;
217
+ size_t remaining;
218
+ void* file_handle;
219
+ int is_v64 = 0;
220
+ int is_n64 = 0;
221
+
222
+ file_handle = rc_file_open(iterator, iterator->path);
223
+ if (!file_handle)
224
+ return rc_hash_iterator_error(iterator, "Could not open file");
225
+
226
+ buffer = (uint8_t*)malloc(buffer_size);
227
+ if (!buffer) {
228
+ rc_file_close(iterator, file_handle);
229
+ return rc_hash_iterator_error(iterator, "Could not allocate temporary buffer");
230
+ }
231
+ stop = buffer + buffer_size;
232
+
233
+ /* read first byte so we can detect endianness */
234
+ rc_file_seek(iterator, file_handle, 0, SEEK_SET);
235
+ rc_file_read(iterator, file_handle, buffer, 1);
236
+
237
+ if (buffer[0] == 0x80) { /* z64 format (big endian [native]) */
238
+ }
239
+ else if (buffer[0] == 0x37) { /* v64 format (byteswapped) */
240
+ rc_hash_iterator_verbose(iterator, "converting v64 to z64");
241
+ is_v64 = 1;
242
+ }
243
+ else if (buffer[0] == 0x40) { /* n64 format (little endian) */
244
+ rc_hash_iterator_verbose(iterator, "converting n64 to z64");
245
+ is_n64 = 1;
246
+ }
247
+ else if (buffer[0] == 0xE8 || buffer[0] == 0x22) { /* ndd format (don't byteswap) */
248
+ }
249
+ else {
250
+ free(buffer);
251
+ rc_file_close(iterator, file_handle);
252
+
253
+ rc_hash_iterator_verbose(iterator, "Not a Nintendo 64 ROM");
254
+ return 0;
255
+ }
256
+
257
+ /* calculate total file size */
258
+ rc_file_seek(iterator, file_handle, 0, SEEK_END);
259
+ remaining = (size_t)rc_file_tell(iterator, file_handle);
260
+ if (remaining > MAX_BUFFER_SIZE)
261
+ remaining = MAX_BUFFER_SIZE;
262
+
263
+ rc_hash_iterator_verbose_formatted(iterator, "Hashing %u bytes", (unsigned)remaining);
264
+
265
+ /* begin hashing */
266
+ md5_init(&md5);
267
+
268
+ rc_file_seek(iterator, file_handle, 0, SEEK_SET);
269
+ while (remaining >= buffer_size) {
270
+ rc_file_read(iterator, file_handle, buffer, (int)buffer_size);
271
+
272
+ if (is_v64)
273
+ rc_hash_byteswap16(buffer, stop);
274
+ else if (is_n64)
275
+ rc_hash_byteswap32(buffer, stop);
276
+
277
+ md5_append(&md5, buffer, (int)buffer_size);
278
+ remaining -= buffer_size;
279
+ }
280
+
281
+ if (remaining > 0) {
282
+ rc_file_read(iterator, file_handle, buffer, (int)remaining);
283
+
284
+ stop = buffer + remaining;
285
+ if (is_v64)
286
+ rc_hash_byteswap16(buffer, stop);
287
+ else if (is_n64)
288
+ rc_hash_byteswap32(buffer, stop);
289
+
290
+ md5_append(&md5, buffer, (int)remaining);
291
+ }
292
+
293
+ /* cleanup */
294
+ rc_file_close(iterator, file_handle);
295
+ free(buffer);
296
+
297
+ return rc_hash_finalize(iterator, &md5, hash);
298
+ }
299
+
300
+ int rc_hash_nintendo_ds(char hash[33], const rc_hash_iterator_t* iterator)
301
+ {
302
+ uint8_t header[512];
303
+ uint8_t* hash_buffer;
304
+ uint32_t hash_size, arm9_size, arm9_addr, arm7_size, arm7_addr, icon_addr;
305
+ size_t num_read;
306
+ int64_t offset = 0;
307
+ md5_state_t md5;
308
+ void* file_handle;
309
+
310
+ file_handle = rc_file_open(iterator, iterator->path);
311
+ if (!file_handle)
312
+ return rc_hash_iterator_error(iterator, "Could not open file");
313
+
314
+ rc_file_seek(iterator, file_handle, 0, SEEK_SET);
315
+ if (rc_file_read(iterator, file_handle, header, sizeof(header)) != 512)
316
+ return rc_hash_iterator_error(iterator, "Failed to read header");
317
+
318
+ if (header[0] == 0x2E && header[1] == 0x00 && header[2] == 0x00 && header[3] == 0xEA &&
319
+ header[0xB0] == 0x44 && header[0xB1] == 0x46 && header[0xB2] == 0x96 && header[0xB3] == 0) {
320
+ /* SuperCard header detected, ignore it */
321
+ rc_hash_iterator_verbose(iterator, "Ignoring SuperCard header");
322
+
323
+ offset = 512;
324
+ rc_file_seek(iterator, file_handle, offset, SEEK_SET);
325
+ rc_file_read(iterator, file_handle, header, sizeof(header));
326
+ }
327
+
328
+ arm9_addr = header[0x20] | (header[0x21] << 8) | (header[0x22] << 16) | (header[0x23] << 24);
329
+ arm9_size = header[0x2C] | (header[0x2D] << 8) | (header[0x2E] << 16) | (header[0x2F] << 24);
330
+ arm7_addr = header[0x30] | (header[0x31] << 8) | (header[0x32] << 16) | (header[0x33] << 24);
331
+ arm7_size = header[0x3C] | (header[0x3D] << 8) | (header[0x3E] << 16) | (header[0x3F] << 24);
332
+ icon_addr = header[0x68] | (header[0x69] << 8) | (header[0x6A] << 16) | (header[0x6B] << 24);
333
+
334
+ if (arm9_size + arm7_size > 16 * 1024 * 1024) {
335
+ /* sanity check - code blocks are typically less than 1MB each - assume not a DS ROM */
336
+ return rc_hash_iterator_error_formatted(iterator, "arm9 code size (%u) + arm7 code size (%u) exceeds 16MB", arm9_size, arm7_size);
337
+ }
338
+
339
+ hash_size = 0xA00;
340
+ if (arm9_size > hash_size)
341
+ hash_size = arm9_size;
342
+ if (arm7_size > hash_size)
343
+ hash_size = arm7_size;
344
+
345
+ hash_buffer = (uint8_t*)malloc(hash_size);
346
+ if (!hash_buffer) {
347
+ rc_file_close(iterator, file_handle);
348
+ return rc_hash_iterator_error_formatted(iterator, "Failed to allocate %u bytes", hash_size);
349
+ }
350
+
351
+ md5_init(&md5);
352
+
353
+ rc_hash_iterator_verbose(iterator, "Hashing 352 byte header");
354
+ md5_append(&md5, header, 0x160);
355
+
356
+ rc_hash_iterator_verbose_formatted(iterator, "Hashing %u byte arm9 code (at %08X)", arm9_size, arm9_addr);
357
+
358
+ rc_file_seek(iterator, file_handle, arm9_addr + offset, SEEK_SET);
359
+ rc_file_read(iterator, file_handle, hash_buffer, arm9_size);
360
+ md5_append(&md5, hash_buffer, arm9_size);
361
+
362
+ rc_hash_iterator_verbose_formatted(iterator, "Hashing %u byte arm7 code (at %08X)", arm7_size, arm7_addr);
363
+
364
+ rc_file_seek(iterator, file_handle, arm7_addr + offset, SEEK_SET);
365
+ rc_file_read(iterator, file_handle, hash_buffer, arm7_size);
366
+ md5_append(&md5, hash_buffer, arm7_size);
367
+
368
+ rc_hash_iterator_verbose_formatted(iterator, "Hashing 2560 byte icon and labels data (at %08X)", icon_addr);
369
+
370
+ rc_file_seek(iterator, file_handle, icon_addr + offset, SEEK_SET);
371
+ num_read = rc_file_read(iterator, file_handle, hash_buffer, 0xA00);
372
+ if (num_read < 0xA00) {
373
+ /* some homebrew games don't provide a full icon block, and no data after the icon block.
374
+ * if we didn't get a full icon block, fill the remaining portion with 0s
375
+ */
376
+ rc_hash_iterator_verbose_formatted(iterator,
377
+ "Warning: only got %u bytes for icon and labels data, 0-padding to 2560 bytes", (unsigned)num_read);
378
+
379
+ memset(&hash_buffer[num_read], 0, 0xA00 - num_read);
380
+ }
381
+ md5_append(&md5, hash_buffer, 0xA00);
382
+
383
+ free(hash_buffer);
384
+ rc_file_close(iterator, file_handle);
385
+
386
+ return rc_hash_finalize(iterator, &md5, hash);
387
+ }
388
+
389
+ int rc_hash_pce(char hash[33], const rc_hash_iterator_t* iterator)
390
+ {
391
+ /* The PCE header doesn't bear any distinguishable marks, so we have to detect
392
+ * it by looking at the file size. The core looks for anything that's 512 bytes
393
+ * more than a multiple of 8KB, so we'll do that too.
394
+ * https://github.com/libretro/beetle-pce-libretro/blob/af28fb0385d00e0292c4703b3aa7e72762b564d2/mednafen/pce/huc.cpp#L196-L202
395
+ */
396
+ if (iterator->buffer_size & 512) {
397
+ rc_hash_iterator_verbose(iterator, "Ignoring PCE header");
398
+ return rc_hash_unheadered_iterator_buffer(hash, iterator, 512);
399
+ }
400
+
401
+ return rc_hash_iterator_buffer(hash, iterator);
402
+ }
403
+
404
+ int rc_hash_scv(char hash[33], const rc_hash_iterator_t* iterator)
405
+ {
406
+ /* if the file contains a header, ignore it */
407
+ /* https://gitlab.com/MaaaX-EmuSCV/libretro-emuscv/-/blob/master/readme.txt#L211 */
408
+ if (memcmp(iterator->buffer, "EmuSCV", 6) == 0) {
409
+ rc_hash_iterator_verbose(iterator, "Ignoring SCV header");
410
+ return rc_hash_unheadered_iterator_buffer(hash, iterator, 32);
411
+ }
412
+
413
+ return rc_hash_iterator_buffer(hash, iterator);
414
+ }
415
+
416
+ int rc_hash_snes(char hash[33], const rc_hash_iterator_t* iterator)
417
+ {
418
+ /* if the file contains a header, ignore it */
419
+ uint32_t calc_size = ((uint32_t)iterator->buffer_size / 0x2000) * 0x2000;
420
+ if (iterator->buffer_size - calc_size == 512) {
421
+ rc_hash_iterator_verbose(iterator, "Ignoring SNES header");
422
+ return rc_hash_unheadered_iterator_buffer(hash, iterator, 512);
423
+ }
424
+
425
+ return rc_hash_iterator_buffer(hash, iterator);
426
+ }