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,1450 @@
1
+ #include "rc_hash.h"
2
+
3
+ #include "../rhash/rc_hash_internal.h"
4
+
5
+ #include "../rc_compat.h"
6
+ #include "../test_framework.h"
7
+ #include "data.h"
8
+ #include "mock_filereader.h"
9
+
10
+ #include <stdlib.h>
11
+
12
+ /* in test_hash.c */
13
+ void test_hash_full_file(uint32_t console_id, const char* filename, size_t size, const char* expected_md5);
14
+ void test_hash_m3u(uint32_t console_id, const char* filename, size_t size, const char* expected_md5);
15
+
16
+ /* ========================================================================= */
17
+
18
+ static void test_hash_unknown_format(uint32_t console_id, const char* path)
19
+ {
20
+ char hash_file[33] = "", hash_iterator[33] = "";
21
+
22
+ /* test file hash (won't match) */
23
+ int result_file = rc_hash_generate_from_file(hash_file, console_id, path);
24
+
25
+ /* test file identification from iterator (won't match) */
26
+ int result_iterator;
27
+ struct rc_hash_iterator iterator;
28
+
29
+ rc_hash_initialize_iterator(&iterator, path, NULL, 0);
30
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
31
+ rc_hash_destroy_iterator(&iterator);
32
+
33
+ /* validation */
34
+ ASSERT_NUM_EQUALS(result_file, 0);
35
+ ASSERT_STR_EQUALS(hash_file, "");
36
+
37
+ ASSERT_NUM_EQUALS(result_iterator, 0);
38
+ ASSERT_STR_EQUALS(hash_iterator, "");
39
+ }
40
+
41
+ /* ========================================================================= */
42
+
43
+ static void test_hash_3do_bin()
44
+ {
45
+ size_t image_size;
46
+ uint8_t* image = generate_3do_bin(1, 123456, &image_size);
47
+ char hash_file[33], hash_iterator[33];
48
+ const char* expected_md5 = "9b2266b8f5abed9c12cce780750e88d6";
49
+
50
+ mock_file(0, "game.bin", image, image_size);
51
+
52
+ /* test file hash */
53
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.bin");
54
+
55
+ /* test file identification from iterator */
56
+ int result_iterator;
57
+ struct rc_hash_iterator iterator;
58
+
59
+ mock_file_size(0, 45678901); /* must be > 32MB for iterator to consider CD formats for bin */
60
+ rc_hash_initialize_iterator(&iterator, "game.bin", NULL, 0);
61
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
62
+ rc_hash_destroy_iterator(&iterator);
63
+
64
+ /* cleanup */
65
+ free(image);
66
+
67
+ /* validation */
68
+ ASSERT_NUM_EQUALS(result_file, 1);
69
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
70
+
71
+ ASSERT_NUM_EQUALS(result_iterator, 1);
72
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
73
+ }
74
+
75
+ static void test_hash_3do_cue()
76
+ {
77
+ size_t image_size;
78
+ uint8_t* image = generate_3do_bin(1, 9347, &image_size);
79
+ char hash_file[33], hash_iterator[33];
80
+ const char* expected_md5 = "257d1d19365a864266b236214dbea29c";
81
+
82
+ mock_file(0, "game.bin", image, image_size);
83
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
84
+
85
+ /* test file hash */
86
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.cue");
87
+
88
+ /* test file identification from iterator */
89
+ int result_iterator;
90
+ struct rc_hash_iterator iterator;
91
+
92
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
93
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
94
+ rc_hash_destroy_iterator(&iterator);
95
+
96
+ /* cleanup */
97
+ free(image);
98
+
99
+ /* validation */
100
+ ASSERT_NUM_EQUALS(result_file, 1);
101
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
102
+
103
+ ASSERT_NUM_EQUALS(result_iterator, 1);
104
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
105
+ }
106
+
107
+ static void test_hash_3do_iso()
108
+ {
109
+ size_t image_size;
110
+ uint8_t* image = generate_3do_bin(1, 9347, &image_size);
111
+ char hash_file[33], hash_iterator[33];
112
+ const char* expected_md5 = "257d1d19365a864266b236214dbea29c";
113
+
114
+ mock_file(0, "game.iso", image, image_size);
115
+
116
+ /* test file hash */
117
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.iso");
118
+
119
+ /* test file identification from iterator */
120
+ int result_iterator;
121
+ struct rc_hash_iterator iterator;
122
+
123
+ rc_hash_initialize_iterator(&iterator, "game.iso", NULL, 0);
124
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
125
+ rc_hash_destroy_iterator(&iterator);
126
+
127
+ /* cleanup */
128
+ free(image);
129
+
130
+ /* validation */
131
+ ASSERT_NUM_EQUALS(result_file, 1);
132
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
133
+
134
+ ASSERT_NUM_EQUALS(result_iterator, 1);
135
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
136
+ }
137
+
138
+ static void test_hash_3do_invalid_header()
139
+ {
140
+ /* this is meant to simulate attempting to open a non-3DO CD. TODO: generate PSX CD */
141
+ size_t image_size;
142
+ uint8_t* image = generate_3do_bin(1, 12, &image_size);
143
+ char hash_file[33];
144
+
145
+ /* make the header not match */
146
+ image[3] = 0x34;
147
+
148
+ mock_file(0, "game.bin", image, image_size);
149
+
150
+ /* test file hash */
151
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.bin");
152
+
153
+ /* cleanup */
154
+ free(image);
155
+
156
+ /* validation */
157
+ ASSERT_NUM_EQUALS(result_file, 0);
158
+ }
159
+
160
+ static void test_hash_3do_launchme_case_insensitive()
161
+ {
162
+ /* main executable for "Captain Quazar" is "launchme" */
163
+ /* main executable for "Rise of the Robots" is "launchMe" */
164
+ /* main executable for "Road Rash" is "LaunchMe" */
165
+ /* main executable for "Sewer Shark" is "Launchme" */
166
+ size_t image_size;
167
+ uint8_t* image = generate_3do_bin(1, 6543, &image_size);
168
+ char hash_file[33];
169
+ const char* expected_md5 = "59622882e3261237e8a1e396825ae4f5";
170
+
171
+ memcpy(&image[2048 + 0x14 + 0x48 + 0x20], "launchme", 8);
172
+ mock_file(0, "game.bin", image, image_size);
173
+
174
+ /* test file hash */
175
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.bin");
176
+
177
+ /* cleanup */
178
+ free(image);
179
+
180
+ /* validation */
181
+ ASSERT_NUM_EQUALS(result_file, 1);
182
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
183
+ }
184
+
185
+ static void test_hash_3do_no_launchme()
186
+ {
187
+ /* this case should not happen */
188
+ size_t image_size;
189
+ uint8_t* image = generate_3do_bin(1, 6543, &image_size);
190
+ char hash_file[33];
191
+
192
+ memcpy(&image[2048 + 0x14 + 0x48 + 0x20], "filename", 8);
193
+ mock_file(0, "game.bin", image, image_size);
194
+
195
+ /* test file hash */
196
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.bin");
197
+
198
+ /* cleanup */
199
+ free(image);
200
+
201
+ /* validation */
202
+ ASSERT_NUM_EQUALS(result_file, 0);
203
+ }
204
+
205
+ static void test_hash_3do_long_directory()
206
+ {
207
+ /* root directory for "Dragon's Lair" uses more than one sector */
208
+ size_t image_size;
209
+ uint8_t* image = generate_3do_bin(3, 6543, &image_size);
210
+ char hash_file[33];
211
+ const char* expected_md5 = "8979e876ae502e0f79218f7ff7bd8c2a";
212
+
213
+ mock_file(0, "game.bin", image, image_size);
214
+
215
+ /* test file hash */
216
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.bin");
217
+
218
+ /* cleanup */
219
+ free(image);
220
+
221
+ /* validation */
222
+ ASSERT_NUM_EQUALS(result_file, 1);
223
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
224
+ }
225
+
226
+ /* ========================================================================= */
227
+
228
+ static void test_hash_atari_jaguar_cd()
229
+ {
230
+ const char* cue_file =
231
+ "REM SESSION 01\n"
232
+ "FILE \"track01.bin\" BINARY\n"
233
+ " TRACK 01 AUDIO\n"
234
+ " INDEX 01 00:00:00\n"
235
+ "REM SESSION 02\n"
236
+ "FILE \"track02.bin\" BINARY\n"
237
+ " TRACK 02 AUDIO\n"
238
+ " INDEX 01 00:00:00\n"
239
+ "FILE \"track03.bin\" BINARY\n"
240
+ " TRACK 03 AUDIO\n"
241
+ " INDEX 01 00:00:00\n";
242
+ size_t image_size;
243
+ uint8_t* image = generate_jaguarcd_bin(2, 60024, 0, &image_size);
244
+ char hash_file[33], hash_iterator[33];
245
+ const char* expected_md5 = "c324d95dc5831c2d5c470eefb18c346b";
246
+
247
+ mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file));
248
+ mock_file(1, "track02.bin", image, image_size);
249
+
250
+ rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */
251
+
252
+ /* test file hash */
253
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue");
254
+
255
+ /* test file identification from iterator */
256
+ int result_iterator;
257
+ struct rc_hash_iterator iterator;
258
+
259
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
260
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
261
+ rc_hash_destroy_iterator(&iterator);
262
+
263
+ /* cleanup */
264
+ free(image);
265
+ init_mock_cdreader();
266
+
267
+ /* validation */
268
+ ASSERT_NUM_EQUALS(result_file, 1);
269
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
270
+
271
+ ASSERT_NUM_EQUALS(result_iterator, 1);
272
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
273
+ }
274
+
275
+ static void test_hash_atari_jaguar_cd_byteswapped()
276
+ {
277
+ const char* cue_file =
278
+ "REM SESSION 01\n"
279
+ "FILE \"track01.bin\" BINARY\n"
280
+ " TRACK 01 AUDIO\n"
281
+ " INDEX 01 00:00:00\n"
282
+ "REM SESSION 02\n"
283
+ "FILE \"track02.bin\" BINARY\n"
284
+ " TRACK 02 AUDIO\n"
285
+ " INDEX 01 00:00:00\n"
286
+ "FILE \"track03.bin\" BINARY\n"
287
+ " TRACK 03 AUDIO\n"
288
+ " INDEX 01 00:00:00\n";
289
+ size_t image_size;
290
+ uint8_t* image = generate_jaguarcd_bin(2, 60024, 1, &image_size);
291
+ char hash_file[33], hash_iterator[33];
292
+ const char* expected_md5 = "c324d95dc5831c2d5c470eefb18c346b";
293
+
294
+ mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file));
295
+ mock_file(1, "track02.bin", image, image_size);
296
+
297
+ rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */
298
+
299
+ /* test file hash */
300
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue");
301
+
302
+ /* test file identification from iterator */
303
+ int result_iterator;
304
+ struct rc_hash_iterator iterator;
305
+
306
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
307
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
308
+ rc_hash_destroy_iterator(&iterator);
309
+
310
+ /* cleanup */
311
+ free(image);
312
+ init_mock_cdreader();
313
+
314
+ /* validation */
315
+ ASSERT_NUM_EQUALS(result_file, 1);
316
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
317
+
318
+ ASSERT_NUM_EQUALS(result_iterator, 1);
319
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
320
+ }
321
+
322
+ static void test_hash_atari_jaguar_cd_track3()
323
+ {
324
+ const char* cue_file =
325
+ "REM SESSION 01\n"
326
+ "FILE \"track01.bin\" BINARY\n"
327
+ " TRACK 01 AUDIO\n"
328
+ " INDEX 01 00:00:00\n"
329
+ "FILE \"track02.bin\" BINARY\n"
330
+ " TRACK 02 AUDIO\n"
331
+ " INDEX 01 00:00:00\n"
332
+ "REM SESSION 02\n"
333
+ "FILE \"track03.bin\" BINARY\n"
334
+ " TRACK 03 AUDIO\n"
335
+ " INDEX 01 00:00:00\n";
336
+ size_t image_size;
337
+ uint8_t* image = generate_jaguarcd_bin(1470, 99200, 1, &image_size);
338
+ char hash_file[33], hash_iterator[33];
339
+ const char* expected_md5 = "060e9d223c584b581cf7d7ce17c0e5dc";
340
+
341
+ mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file));
342
+ mock_file(1, "track03.bin", image, image_size);
343
+
344
+ rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */
345
+
346
+ /* test file hash */
347
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue");
348
+
349
+ /* test file identification from iterator */
350
+ int result_iterator;
351
+ struct rc_hash_iterator iterator;
352
+
353
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
354
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
355
+ rc_hash_destroy_iterator(&iterator);
356
+
357
+ /* cleanup */
358
+ free(image);
359
+ init_mock_cdreader();
360
+
361
+ /* validation */
362
+ ASSERT_NUM_EQUALS(result_file, 1);
363
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
364
+
365
+ ASSERT_NUM_EQUALS(result_iterator, 1);
366
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
367
+ }
368
+
369
+ static void test_hash_atari_jaguar_cd_no_header()
370
+ {
371
+ const char* cue_file =
372
+ "REM SESSION 01\n"
373
+ "FILE \"track01.bin\" BINARY\n"
374
+ " TRACK 01 AUDIO\n"
375
+ " INDEX 01 00:00:00\n"
376
+ "REM SESSION 02\n"
377
+ "FILE \"track02.bin\" BINARY\n"
378
+ " TRACK 02 AUDIO\n"
379
+ " INDEX 01 00:00:00\n"
380
+ "FILE \"track03.bin\" BINARY\n"
381
+ " TRACK 03 AUDIO\n"
382
+ " INDEX 01 00:00:00\n";
383
+ size_t image_size;
384
+ uint8_t* image = generate_jaguarcd_bin(2, 32768, 1, &image_size);
385
+ char hash_file[33], hash_iterator[33];
386
+
387
+ image[2 + 64 + 12] = 'B'; /* corrupt the header */
388
+
389
+ mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file));
390
+ mock_file(1, "track02.bin", image, image_size);
391
+
392
+ rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */
393
+
394
+ /* test file hash */
395
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue");
396
+
397
+ /* test file identification from iterator */
398
+ int result_iterator;
399
+ struct rc_hash_iterator iterator;
400
+
401
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
402
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
403
+ rc_hash_destroy_iterator(&iterator);
404
+
405
+ /* cleanup */
406
+ free(image);
407
+ init_mock_cdreader();
408
+
409
+ /* validation */
410
+ ASSERT_NUM_EQUALS(result_file, 0);
411
+ ASSERT_NUM_EQUALS(result_iterator, 0);
412
+ }
413
+
414
+ static void test_hash_atari_jaguar_cd_no_sessions()
415
+ {
416
+ const char* cue_file =
417
+ "FILE \"track01.bin\" BINARY\n"
418
+ " TRACK 01 AUDIO\n"
419
+ " INDEX 01 00:00:00\n"
420
+ "FILE \"track02.bin\" BINARY\n"
421
+ " TRACK 02 AUDIO\n"
422
+ " INDEX 01 00:00:00\n"
423
+ "FILE \"track03.bin\" BINARY\n"
424
+ " TRACK 03 AUDIO\n"
425
+ " INDEX 01 00:00:00\n";
426
+ size_t image_size;
427
+ uint8_t* image = generate_jaguarcd_bin(2, 99200, 1, &image_size);
428
+ char hash_file[33], hash_iterator[33];
429
+
430
+ mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file));
431
+ mock_file(1, "track03.bin", image, image_size);
432
+
433
+ rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */
434
+
435
+ /* test file hash */
436
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue");
437
+
438
+ /* test file identification from iterator */
439
+ int result_iterator;
440
+ struct rc_hash_iterator iterator;
441
+
442
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
443
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
444
+ rc_hash_destroy_iterator(&iterator);
445
+
446
+ /* cleanup */
447
+ free(image);
448
+ init_mock_cdreader();
449
+
450
+ /* validation */
451
+ ASSERT_NUM_EQUALS(result_file, 0);
452
+ ASSERT_NUM_EQUALS(result_iterator, 0);
453
+ }
454
+
455
+ extern const char* _rc_hash_jaguar_cd_homebrew_hash;
456
+
457
+ static void test_hash_atari_jaguar_cd_homebrew()
458
+ {
459
+ /* Jaguar CD homebrew games all appear to have a common bootloader in the primary boot executable space. They only
460
+ * differ in a secondary executable in the second track (part of the first session). This doesn't appear to be
461
+ * intentional behavior based on the CD BIOS documentation, which states that all developer code should be in the
462
+ * first track of the second session. I speculate this is done to work around the authentication logic. */
463
+ const char* cue_file =
464
+ "REM SESSION 01\n"
465
+ "FILE \"track01.bin\" BINARY\n"
466
+ " TRACK 01 AUDIO\n"
467
+ " INDEX 01 00:00:00\n"
468
+ "FILE \"track02.bin\" BINARY\n"
469
+ " TRACK 02 AUDIO\n"
470
+ " INDEX 01 00:00:00\n"
471
+ "REM SESSION 02\n"
472
+ "FILE \"track03.bin\" BINARY\n"
473
+ " TRACK 03 AUDIO\n"
474
+ " INDEX 01 00:00:00\n";
475
+ size_t image_size, image_size2;
476
+ uint8_t* image = generate_jaguarcd_bin(2, 45760, 1, &image_size);
477
+ uint8_t* image2 = generate_jaguarcd_bin(2, 986742, 1, &image_size2);
478
+ char hash_file[33], hash_iterator[33];
479
+ const char* expected_md5 = "3fdf70e362c845524c9e447aacaed0a9";
480
+
481
+ image2[0x60] = 0x21; /* ATARI APPROVED DATA HEADER ATRI! */
482
+ memcpy(&image2[0xA2], &image2[0x62], 8); /* addr / size */
483
+ memcpy(&image2[0x62], "RTKARTKARTKARTKA", 16); /* KARTKARTKARTKART */
484
+ memcpy(&image2[0x72], "RTKARTKARTKARTKA", 16);
485
+ memcpy(&image2[0x82], "RTKARTKARTKARTKA", 16);
486
+ memcpy(&image2[0x92], "RTKARTKARTKARTKA", 16);
487
+
488
+ mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file));
489
+ mock_file(2, "track02.bin", image2, image_size2);
490
+ mock_file(1, "track03.bin", image, image_size);
491
+
492
+ rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */
493
+ _rc_hash_jaguar_cd_homebrew_hash = "4e4114b2675eff21bb77dd41e141ddd6"; /* mock the hash of the homebrew bootloader */
494
+
495
+ /* test file hash */
496
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue");
497
+
498
+ /* test file identification from iterator */
499
+ int result_iterator;
500
+ struct rc_hash_iterator iterator;
501
+
502
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
503
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
504
+ rc_hash_destroy_iterator(&iterator);
505
+
506
+ /* cleanup */
507
+ _rc_hash_jaguar_cd_homebrew_hash = NULL;
508
+ free(image);
509
+ free(image2);
510
+ init_mock_cdreader();
511
+
512
+ /* validation */
513
+ ASSERT_NUM_EQUALS(result_file, 1);
514
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
515
+
516
+ ASSERT_NUM_EQUALS(result_iterator, 1);
517
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
518
+ }
519
+
520
+ /* ========================================================================= */
521
+
522
+ static void test_hash_dreamcast_single_bin()
523
+ {
524
+ size_t image_size;
525
+ uint8_t* image = generate_dreamcast_bin(45000, 1458208, &image_size);
526
+ char hash_file[33], hash_iterator[33];
527
+ const char* expected_md5 = "2a550500caee9f06e5d061fe10a46f6e";
528
+
529
+ mock_file(0, "track03.bin", image, image_size);
530
+ mock_file_first_sector(0, 45000);
531
+ mock_file(1, "game.gdi", (uint8_t*)"game.bin", 8);
532
+ mock_cd_num_tracks(3);
533
+
534
+ /* test file hash */
535
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_DREAMCAST, "game.gdi");
536
+
537
+ /* test file identification from iterator */
538
+ int result_iterator;
539
+ struct rc_hash_iterator iterator;
540
+
541
+ rc_hash_initialize_iterator(&iterator, "game.gdi", NULL, 0);
542
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
543
+ rc_hash_destroy_iterator(&iterator);
544
+
545
+ /* cleanup */
546
+ free(image);
547
+
548
+ /* validation */
549
+ ASSERT_NUM_EQUALS(result_file, 1);
550
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
551
+
552
+ ASSERT_NUM_EQUALS(result_iterator, 1);
553
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
554
+ }
555
+
556
+ static void test_hash_dreamcast_split_bin()
557
+ {
558
+ size_t image_size;
559
+ uint8_t* image = generate_dreamcast_bin(548106, 1830912, &image_size);
560
+ char hash_file[33], hash_iterator[33];
561
+ const char* expected_md5 = "771e56aff169230ede4505013a4bcf9f";
562
+
563
+ mock_file(0, "game.gdi", (uint8_t*)"game.bin", 8);
564
+ mock_file(1, "track03.bin", image, image_size);
565
+ mock_file_first_sector(1, 45000);
566
+ mock_file(2, "track26.bin", image, image_size);
567
+ mock_file_first_sector(2, 548106);
568
+ mock_cd_num_tracks(26);
569
+
570
+ /* test file hash */
571
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_DREAMCAST, "game.gdi");
572
+
573
+ /* test file identification from iterator */
574
+ int result_iterator;
575
+ struct rc_hash_iterator iterator;
576
+
577
+ rc_hash_initialize_iterator(&iterator, "game.gdi", NULL, 0);
578
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
579
+ rc_hash_destroy_iterator(&iterator);
580
+
581
+ /* cleanup */
582
+ free(image);
583
+
584
+ /* validation */
585
+ ASSERT_NUM_EQUALS(result_file, 1);
586
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
587
+
588
+ ASSERT_NUM_EQUALS(result_iterator, 1);
589
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
590
+ }
591
+
592
+ static void test_hash_dreamcast_cue()
593
+ {
594
+ const char* cue_file =
595
+ "FILE \"track01.bin\" BINARY\n"
596
+ " TRACK 01 MODE1/2352\n"
597
+ " INDEX 01 00:00:00\n"
598
+ "FILE \"track02.bin\" BINARY\n"
599
+ " TRACK 02 AUDIO\n"
600
+ " INDEX 00 00:00:00\n"
601
+ " INDEX 01 00:02:00\n"
602
+ "FILE \"track03.bin\" BINARY\n"
603
+ " TRACK 03 MODE1/2352\n"
604
+ " INDEX 01 00:00:00\n"
605
+ "FILE \"track04.bin\" BINARY\n"
606
+ " TRACK 04 AUDIO\n"
607
+ " INDEX 00 00:00:00\n"
608
+ " INDEX 01 00:02:00\n"
609
+ "FILE \"track05.bin\" BINARY\n"
610
+ " TRACK 05 MODE1/2352\n"
611
+ " INDEX 00 00:00:00\n"
612
+ " INDEX 01 00:03:00\n";
613
+ size_t image_size;
614
+ uint8_t* image = convert_to_2352(generate_dreamcast_bin(45000, 1697028, &image_size), &image_size, 45000);
615
+ char hash_file[33], hash_iterator[33];
616
+ const char* expected_md5 = "c952864c3364591d2a8793ce2cfbf3a0";
617
+
618
+ mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file));
619
+ mock_file(1, "track01.bin", image, 1425312); /* 606 sectors */
620
+ mock_file(2, "track02.bin", image, 1589952); /* 676 sectors */
621
+ mock_file(3, "track03.bin", image, image_size); /* 737 sectors */
622
+ mock_file(4, "track04.bin", image, 1237152); /* 526 sectors */
623
+ mock_file(5, "track05.bin", image, image_size);
624
+
625
+ rc_hash_init_default_cdreader(); /* want to test actual first_track_sector calculation */
626
+
627
+ /* test file hash */
628
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_DREAMCAST, "game.cue");
629
+
630
+ /* test file identification from iterator */
631
+ int result_iterator;
632
+ struct rc_hash_iterator iterator;
633
+
634
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
635
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
636
+ rc_hash_destroy_iterator(&iterator);
637
+
638
+ /* cleanup */
639
+ free(image);
640
+ init_mock_cdreader();
641
+
642
+ /* validation */
643
+ ASSERT_NUM_EQUALS(result_file, 1);
644
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
645
+
646
+ ASSERT_NUM_EQUALS(result_iterator, 1);
647
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
648
+ }
649
+
650
+ /* ========================================================================= */
651
+
652
+ static void test_hash_gamecube()
653
+ {
654
+ size_t image_size;
655
+ uint8_t* image = generate_gamecube_iso(32, &image_size);
656
+ char hash_file[33], hash_iterator[33];
657
+ const char* expected_md5 = "c7803b704fa43d22d8f6e55f4789cb45";
658
+
659
+ mock_file(0, "test.iso", image, image_size);
660
+
661
+ /* test file hash */
662
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_GAMECUBE, "test.iso");
663
+
664
+ /* test file identification from iterator */
665
+ int result_iterator;
666
+ struct rc_hash_iterator iterator;
667
+
668
+ rc_hash_initialize_iterator(&iterator, "test.iso", NULL, 0);
669
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
670
+ rc_hash_destroy_iterator(&iterator);
671
+
672
+ /* cleanup */
673
+ free(image);
674
+
675
+ /* validation */
676
+ ASSERT_NUM_EQUALS(result_file, 1);
677
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
678
+
679
+ ASSERT_NUM_EQUALS(result_iterator, 1);
680
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
681
+
682
+ ASSERT_NUM_EQUALS(image_size, 32 * 1024 * 1024);
683
+ }
684
+
685
+ /* ========================================================================= */
686
+
687
+ static void test_hash_neogeocd()
688
+ {
689
+ const char* ipl_txt = "FIXA.FIX,0,0\r\nPROG.PRG,0,0\r\nSOUND.PCM,0,0\r\n\x1a";
690
+ const size_t prog_prg_size = 273470;
691
+ uint8_t* prog_prg = generate_generic_file(prog_prg_size);
692
+ size_t image_size;
693
+ uint8_t* image = generate_iso9660_bin(160, "TEST", &image_size);
694
+ char hash_file[33], hash_iterator[33];
695
+ const char* expected_md5 = "96f35b20c6cf902286da45e81a50b2a3";
696
+
697
+ generate_iso9660_file(image, "IPL.TXT", (uint8_t*)ipl_txt, strlen(ipl_txt));
698
+ generate_iso9660_file(image, "PROG.PRG", prog_prg, prog_prg_size);
699
+
700
+ mock_file(0, "game.bin", image, image_size);
701
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
702
+
703
+ /* test file hash */
704
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_NEO_GEO_CD, "game.cue");
705
+
706
+ /* test file identification from iterator */
707
+ int result_iterator;
708
+ struct rc_hash_iterator iterator;
709
+
710
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
711
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
712
+ rc_hash_destroy_iterator(&iterator);
713
+
714
+ /* cleanup */
715
+ free(image);
716
+ free(prog_prg);
717
+
718
+ /* validation */
719
+ ASSERT_NUM_EQUALS(result_file, 1);
720
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
721
+
722
+ ASSERT_NUM_EQUALS(result_iterator, 1);
723
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
724
+ }
725
+
726
+ static void test_hash_neogeocd_multiple_prg()
727
+ {
728
+ const char* ipl_txt = "FIXA.FIX,0,0\r\nPROG1.PRG,0,0\r\nSOUND.PCM,0,0\r\nPROG2.PRG,0,44000\r\n\x1a";
729
+ const size_t prog1_prg_size = 273470;
730
+ uint8_t* prog1_prg = generate_generic_file(prog1_prg_size);
731
+ const size_t prog2_prg_size = 13768;
732
+ uint8_t* prog2_prg = generate_generic_file(prog2_prg_size);
733
+ size_t image_size;
734
+ uint8_t* image = generate_iso9660_bin(160, "TEST", &image_size);
735
+ char hash_file[33], hash_iterator[33];
736
+ const char* expected_md5 = "d62df483c4786d3c63f27b6c5f17eeca";
737
+
738
+ generate_iso9660_file(image, "IPL.TXT", (uint8_t*)ipl_txt, strlen(ipl_txt));
739
+ generate_iso9660_file(image, "PROG1.PRG", prog1_prg, prog1_prg_size);
740
+ generate_iso9660_file(image, "PROG2.PRG", prog2_prg, prog2_prg_size);
741
+
742
+ mock_file(0, "game.bin", image, image_size);
743
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
744
+
745
+ /* test file hash */
746
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_NEO_GEO_CD, "game.cue");
747
+
748
+ /* test file identification from iterator */
749
+ int result_iterator;
750
+ struct rc_hash_iterator iterator;
751
+
752
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
753
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
754
+ rc_hash_destroy_iterator(&iterator);
755
+
756
+ /* cleanup */
757
+ free(image);
758
+ free(prog1_prg);
759
+ free(prog2_prg);
760
+
761
+ /* validation */
762
+ ASSERT_NUM_EQUALS(result_file, 1);
763
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
764
+
765
+ ASSERT_NUM_EQUALS(result_iterator, 1);
766
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
767
+ }
768
+
769
+ static void test_hash_neogeocd_lowercase_ipl_contents()
770
+ {
771
+ const char* ipl_txt = "fixa.fix,0,0\r\nprog.prg,0,0\r\nsound.pcm,0,0\r\n\x1a";
772
+ const size_t prog_prg_size = 273470;
773
+ uint8_t* prog_prg = generate_generic_file(prog_prg_size);
774
+ size_t image_size;
775
+ uint8_t* image = generate_iso9660_bin(160, "TEST", &image_size);
776
+ char hash_file[33], hash_iterator[33];
777
+ const char* expected_md5 = "96f35b20c6cf902286da45e81a50b2a3";
778
+
779
+ generate_iso9660_file(image, "IPL.TXT", (uint8_t*)ipl_txt, strlen(ipl_txt));
780
+ generate_iso9660_file(image, "PROG.PRG", prog_prg, prog_prg_size);
781
+
782
+ mock_file(0, "game.bin", image, image_size);
783
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
784
+
785
+ /* test file hash */
786
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_NEO_GEO_CD, "game.cue");
787
+
788
+ /* test file identification from iterator */
789
+ int result_iterator;
790
+ struct rc_hash_iterator iterator;
791
+
792
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
793
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
794
+ rc_hash_destroy_iterator(&iterator);
795
+
796
+ /* cleanup */
797
+ free(image);
798
+ free(prog_prg);
799
+
800
+ /* validation */
801
+ ASSERT_NUM_EQUALS(result_file, 1);
802
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
803
+
804
+ ASSERT_NUM_EQUALS(result_iterator, 1);
805
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
806
+ }
807
+
808
+ /* ========================================================================= */
809
+
810
+ static void test_hash_pce_cd()
811
+ {
812
+ size_t image_size;
813
+ uint8_t* image = generate_pce_cd_bin(72, &image_size);
814
+ char hash_file[33], hash_iterator[33];
815
+ const char* expected_md5 = "6565819195a49323e080e7539b54f251";
816
+
817
+ mock_file(0, "game.bin", image, image_size);
818
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
819
+
820
+ /* test file hash */
821
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PC_ENGINE_CD, "game.cue");
822
+
823
+ /* test file identification from iterator */
824
+ int result_iterator;
825
+ struct rc_hash_iterator iterator;
826
+
827
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
828
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
829
+ rc_hash_destroy_iterator(&iterator);
830
+
831
+ /* cleanup */
832
+ free(image);
833
+
834
+ /* validation */
835
+ ASSERT_NUM_EQUALS(result_file, 1);
836
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
837
+
838
+ ASSERT_NUM_EQUALS(result_iterator, 1);
839
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
840
+ }
841
+
842
+ static void test_hash_pce_cd_invalid_header()
843
+ {
844
+ size_t image_size;
845
+ uint8_t* image = generate_pce_cd_bin(72, &image_size);
846
+
847
+ mock_file(0, "game.bin", image, image_size);
848
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
849
+
850
+ /* make the header not match */
851
+ image[2048 + 0x24] = 0x34;
852
+
853
+ test_hash_unknown_format(RC_CONSOLE_PC_ENGINE_CD, "game.cue");
854
+
855
+ free(image);
856
+ }
857
+
858
+ /* ========================================================================= */
859
+
860
+ static void test_hash_pcfx()
861
+ {
862
+ size_t image_size;
863
+ uint8_t* image = generate_pcfx_bin(72, &image_size);
864
+ char hash_file[33], hash_iterator[33];
865
+ const char* expected_md5 = "0a03af66559b8529c50c4e7788379598";
866
+
867
+ mock_file(0, "game.bin", image, image_size);
868
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
869
+
870
+ /* test file hash */
871
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PCFX, "game.cue");
872
+
873
+ /* test file identification from iterator */
874
+ int result_iterator;
875
+ struct rc_hash_iterator iterator;
876
+
877
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
878
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
879
+ rc_hash_destroy_iterator(&iterator);
880
+
881
+ /* cleanup */
882
+ free(image);
883
+
884
+ /* validation */
885
+ ASSERT_NUM_EQUALS(result_file, 1);
886
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
887
+
888
+ ASSERT_NUM_EQUALS(result_iterator, 1);
889
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
890
+ }
891
+
892
+ static void test_hash_pcfx_invalid_header()
893
+ {
894
+ size_t image_size;
895
+ uint8_t* image = generate_pcfx_bin(72, &image_size);
896
+
897
+ mock_file(0, "game.bin", image, image_size);
898
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
899
+
900
+ /* make the header not match */
901
+ image[12] = 0x34;
902
+
903
+ test_hash_unknown_format(RC_CONSOLE_PCFX, "game.cue");
904
+
905
+ free(image);
906
+ }
907
+
908
+ static void test_hash_pcfx_pce_cd()
909
+ {
910
+ /* Battle Heat is formatted as a PC-Engine CD */
911
+ size_t image_size;
912
+ uint8_t* image = generate_pce_cd_bin(72, &image_size);
913
+ char hash_file[33], hash_iterator[33];
914
+ const char* expected_md5 = "6565819195a49323e080e7539b54f251";
915
+
916
+ mock_file(0, "game.bin", image, image_size);
917
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
918
+ mock_file(2, "game2.bin", image, image_size); /* PC-Engine CD check only applies to track 2 */
919
+
920
+ /* test file hash */
921
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PCFX, "game.cue");
922
+
923
+ /* test file identification from iterator */
924
+ int result_iterator;
925
+ struct rc_hash_iterator iterator;
926
+
927
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
928
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
929
+ rc_hash_destroy_iterator(&iterator);
930
+
931
+ /* cleanup */
932
+ free(image);
933
+
934
+ /* validation */
935
+ ASSERT_NUM_EQUALS(result_file, 1);
936
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
937
+
938
+ ASSERT_NUM_EQUALS(result_iterator, 1);
939
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
940
+ }
941
+
942
+ static void test_hash_psx_cd()
943
+ {
944
+ /* BOOT=cdrom:\SLUS_007.45 */
945
+ size_t image_size;
946
+ uint8_t* image = generate_psx_bin("SLUS_007.45", 0x07D800, &image_size);
947
+ char hash_file[33], hash_iterator[33];
948
+ const char* expected_md5 = "db433fb038cde4fb15c144e8c7dea6e3";
949
+
950
+ mock_file(0, "game.bin", image, image_size);
951
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
952
+
953
+ /* test file hash */
954
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION, "game.cue");
955
+
956
+ /* test file identification from iterator */
957
+ int result_iterator;
958
+ struct rc_hash_iterator iterator;
959
+
960
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
961
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
962
+ rc_hash_destroy_iterator(&iterator);
963
+
964
+ /* cleanup */
965
+ free(image);
966
+
967
+ /* validation */
968
+ ASSERT_NUM_EQUALS(result_file, 1);
969
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
970
+
971
+ ASSERT_NUM_EQUALS(result_iterator, 1);
972
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
973
+ }
974
+
975
+ static void test_hash_psx_cd_no_system_cnf()
976
+ {
977
+ size_t image_size;
978
+ uint8_t* image;
979
+ char hash_file[33], hash_iterator[33];
980
+ const char* expected_md5 = "e494c79a7315be0dc3e8571c45df162c";
981
+ uint32_t binary_size = 0x12000;
982
+ const uint32_t sectors_needed = (((binary_size + 2047) / 2048) + 20);
983
+ uint8_t* exe;
984
+
985
+ image = generate_iso9660_bin(sectors_needed, "HOMEBREW", &image_size);
986
+ exe = generate_iso9660_file(image, "PSX.EXE", NULL, binary_size);
987
+ memcpy(exe, "PS-X EXE", 8);
988
+ binary_size -= 2048;
989
+ exe[28] = binary_size & 0xFF;
990
+ exe[29] = (binary_size >> 8) & 0xFF;
991
+ exe[30] = (binary_size >> 16) & 0xFF;
992
+ exe[31] = (binary_size >> 24) & 0xFF;
993
+
994
+ mock_file(0, "game.bin", image, image_size);
995
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
996
+
997
+ /* test file hash */
998
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION, "game.cue");
999
+
1000
+ /* test file identification from iterator */
1001
+ int result_iterator;
1002
+ struct rc_hash_iterator iterator;
1003
+
1004
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
1005
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
1006
+ rc_hash_destroy_iterator(&iterator);
1007
+
1008
+ /* cleanup */
1009
+ free(image);
1010
+
1011
+ /* validation */
1012
+ ASSERT_NUM_EQUALS(result_file, 1);
1013
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
1014
+
1015
+ ASSERT_NUM_EQUALS(result_iterator, 1);
1016
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
1017
+ }
1018
+
1019
+ static void test_hash_psx_cd_exe_in_subfolder()
1020
+ {
1021
+ /* BOOT=cdrom:\bin\SLUS_012.37 */
1022
+ size_t image_size;
1023
+ uint8_t* image = generate_psx_bin("bin\\SCES_012.37", 0x07D800, &image_size);
1024
+ char hash_file[33], hash_iterator[33];
1025
+ const char* expected_md5 = "674018e23a4052113665dfb264e9c2fc";
1026
+
1027
+ mock_file(0, "game.bin", image, image_size);
1028
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
1029
+
1030
+ /* test file hash */
1031
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION, "game.cue");
1032
+
1033
+ /* test file identification from iterator */
1034
+ int result_iterator;
1035
+ struct rc_hash_iterator iterator;
1036
+
1037
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
1038
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
1039
+ rc_hash_destroy_iterator(&iterator);
1040
+
1041
+ /* cleanup */
1042
+ free(image);
1043
+
1044
+ /* validation */
1045
+ ASSERT_NUM_EQUALS(result_file, 1);
1046
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
1047
+
1048
+ ASSERT_NUM_EQUALS(result_iterator, 1);
1049
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
1050
+ }
1051
+
1052
+ static void test_hash_psx_cd_extra_slash()
1053
+ {
1054
+ /* BOOT=cdrom:\\SLUS_007.45 */
1055
+ size_t image_size;
1056
+ uint8_t* image = generate_psx_bin("\\SLUS_007.45", 0x07D800, &image_size);
1057
+ char hash_file[33], hash_iterator[33];
1058
+ const char* expected_md5 = "db433fb038cde4fb15c144e8c7dea6e3";
1059
+
1060
+ mock_file(0, "game.bin", image, image_size);
1061
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
1062
+
1063
+ /* test file hash */
1064
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION, "game.cue");
1065
+
1066
+ /* test file identification from iterator */
1067
+ int result_iterator;
1068
+ struct rc_hash_iterator iterator;
1069
+
1070
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
1071
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
1072
+ rc_hash_destroy_iterator(&iterator);
1073
+
1074
+ /* cleanup */
1075
+ free(image);
1076
+
1077
+ /* validation */
1078
+ ASSERT_NUM_EQUALS(result_file, 1);
1079
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
1080
+
1081
+ ASSERT_NUM_EQUALS(result_iterator, 1);
1082
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
1083
+ }
1084
+
1085
+ static void test_hash_ps2_iso()
1086
+ {
1087
+ size_t image_size;
1088
+ uint8_t* image = generate_ps2_bin("SLUS_200.64", 0x07D800, &image_size);
1089
+ char hash_file[33], hash_iterator[33];
1090
+ const char* expected_md5 = "01a517e4ad72c6c2654d1b839be7579d";
1091
+
1092
+ mock_file(0, "game.iso", image, image_size);
1093
+
1094
+ /* test file hash */
1095
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION_2, "game.iso");
1096
+
1097
+ /* test file identification from iterator */
1098
+ int result_iterator;
1099
+ struct rc_hash_iterator iterator;
1100
+
1101
+ rc_hash_initialize_iterator(&iterator, "game.iso", NULL, 0);
1102
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
1103
+ rc_hash_destroy_iterator(&iterator);
1104
+
1105
+ /* cleanup */
1106
+ free(image);
1107
+
1108
+ /* validation */
1109
+ ASSERT_NUM_EQUALS(result_file, 1);
1110
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
1111
+
1112
+ ASSERT_NUM_EQUALS(result_iterator, 1);
1113
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
1114
+ }
1115
+
1116
+ static void test_hash_ps2_psx()
1117
+ {
1118
+ size_t image_size;
1119
+ uint8_t* image = generate_psx_bin("SLUS_007.45", 0x07D800, &image_size);
1120
+ char hash_file[33], hash_iterator[33];
1121
+ const char* expected_md5 = "db433fb038cde4fb15c144e8c7dea6e3";
1122
+
1123
+ mock_file(0, "game.bin", image, image_size);
1124
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
1125
+
1126
+ /* test file hash */
1127
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION_2, "game.cue");
1128
+
1129
+ /* test file identification from iterator */
1130
+ int result_iterator;
1131
+ struct rc_hash_iterator iterator;
1132
+
1133
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
1134
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
1135
+ ASSERT_NUM_EQUALS(result_iterator, 1);
1136
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5); /* PSX hash */
1137
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
1138
+ rc_hash_destroy_iterator(&iterator);
1139
+
1140
+ /* cleanup */
1141
+ free(image);
1142
+
1143
+ /* validation (should not generate PS2 hash for PSX file) */
1144
+ ASSERT_NUM_EQUALS(result_file, 0);
1145
+ ASSERT_NUM_EQUALS(result_iterator, 0);
1146
+ }
1147
+
1148
+ static void test_hash_psp()
1149
+ {
1150
+ const size_t param_sfo_size = 690;
1151
+ uint8_t* param_sfo = generate_generic_file(param_sfo_size);
1152
+ const size_t eboot_bin_size = 273470;
1153
+ uint8_t* eboot_bin = generate_generic_file(eboot_bin_size);
1154
+ size_t image_size;
1155
+ uint8_t* image = generate_iso9660_bin(160, "TEST", &image_size);
1156
+ char hash_file[33], hash_iterator[33];
1157
+ const char* expected_md5 = "27ec2f9b7238b2ef29af31ddd254f201";
1158
+
1159
+ generate_iso9660_file(image, "PSP_GAME\\PARAM.SFO", param_sfo, param_sfo_size);
1160
+ generate_iso9660_file(image, "PSP_GAME\\SYSDIR\\EBOOT.BIN", eboot_bin, eboot_bin_size);
1161
+
1162
+ mock_file(0, "game.iso", image, image_size);
1163
+
1164
+ /* test file hash */
1165
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PSP, "game.iso");
1166
+
1167
+ /* test file identification from iterator */
1168
+ int result_iterator;
1169
+ struct rc_hash_iterator iterator;
1170
+
1171
+ rc_hash_initialize_iterator(&iterator, "game.iso", NULL, 0);
1172
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
1173
+ rc_hash_destroy_iterator(&iterator);
1174
+
1175
+ /* cleanup */
1176
+ free(image);
1177
+ free(eboot_bin);
1178
+ free(param_sfo);
1179
+
1180
+ /* validation */
1181
+ ASSERT_NUM_EQUALS(result_file, 1);
1182
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
1183
+
1184
+ ASSERT_NUM_EQUALS(result_iterator, 1);
1185
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
1186
+ }
1187
+
1188
+ static void test_hash_psp_video()
1189
+ {
1190
+ const size_t param_sfo_size = 690;
1191
+ uint8_t* param_sfo = generate_generic_file(param_sfo_size);
1192
+ const size_t eboot_bin_size = 273470;
1193
+ uint8_t* eboot_bin = generate_generic_file(eboot_bin_size);
1194
+ size_t image_size;
1195
+ uint8_t* image = generate_iso9660_bin(160, "TEST", &image_size);
1196
+ char hash_file[33], hash_iterator[33];
1197
+
1198
+ /* UMD video disc may have an UPDATE folder, but nothing in the PSP_GAME or SYSDIR folders. */
1199
+ generate_iso9660_file(image, "PSP_GAME\\SYSDIR\\UPDATE\\EBOOT.BIN", eboot_bin, eboot_bin_size);
1200
+ /* the PARAM.SFO file is in the UMD_VIDEO folder. */
1201
+ generate_iso9660_file(image, "UMD_VIDEO\\PARAM.SFO", param_sfo, param_sfo_size);
1202
+
1203
+ mock_file(0, "game.iso", image, image_size);
1204
+
1205
+ /* test file hash */
1206
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PSP, "game.iso");
1207
+
1208
+ /* test file identification from iterator */
1209
+ int result_iterator;
1210
+ struct rc_hash_iterator iterator;
1211
+
1212
+ rc_hash_initialize_iterator(&iterator, "game.iso", NULL, 0);
1213
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
1214
+ rc_hash_destroy_iterator(&iterator);
1215
+
1216
+ /* cleanup */
1217
+ free(image);
1218
+ free(eboot_bin);
1219
+ free(param_sfo);
1220
+
1221
+ /* validation */
1222
+ ASSERT_NUM_EQUALS(result_file, 0);
1223
+ ASSERT_NUM_EQUALS(result_iterator, 0);
1224
+ }
1225
+
1226
+ static void test_hash_psp_homebrew()
1227
+ {
1228
+ const size_t image_size = 3532124;
1229
+ uint8_t* image = generate_generic_file(image_size);
1230
+ char hash_file[33], hash_iterator[33];
1231
+ const char* expected_md5 = "fcde8760893b09e508e5f4fe642eb132";
1232
+
1233
+ mock_file(0, "eboot.pbp", image, image_size);
1234
+
1235
+ /* test file hash */
1236
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PSP, "eboot.pbp");
1237
+
1238
+ /* test file identification from iterator */
1239
+ int result_iterator;
1240
+ struct rc_hash_iterator iterator;
1241
+
1242
+ rc_hash_initialize_iterator(&iterator, "eboot.pbp", NULL, 0);
1243
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
1244
+ rc_hash_destroy_iterator(&iterator);
1245
+
1246
+ /* cleanup */
1247
+ free(image);
1248
+
1249
+ /* validation */
1250
+ ASSERT_NUM_EQUALS(result_file, 1);
1251
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
1252
+
1253
+ ASSERT_NUM_EQUALS(result_iterator, 1);
1254
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
1255
+ }
1256
+
1257
+ static void test_hash_sega_cd()
1258
+ {
1259
+ /* the first 512 bytes of sector 0 are a volume header and ROM header.
1260
+ * generate a generic block and add the Sega CD marker */
1261
+ size_t image_size = 512;
1262
+ uint8_t* image = generate_generic_file(image_size);
1263
+ char hash_file[33], hash_iterator[33];
1264
+ const char* expected_md5 = "574498e1453cb8934df60c4ab906e783";
1265
+ memcpy(image, "SEGADISCSYSTEM ", 16);
1266
+
1267
+ mock_file(0, "game.bin", image, image_size);
1268
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
1269
+
1270
+ /* test file hash */
1271
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_SEGA_CD, "game.cue");
1272
+
1273
+ /* test file identification from iterator */
1274
+ int result_iterator;
1275
+ struct rc_hash_iterator iterator;
1276
+
1277
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
1278
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
1279
+ rc_hash_destroy_iterator(&iterator);
1280
+
1281
+ /* cleanup */
1282
+ free(image);
1283
+
1284
+ /* validation */
1285
+ ASSERT_NUM_EQUALS(result_file, 1);
1286
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
1287
+
1288
+ ASSERT_NUM_EQUALS(result_iterator, 1);
1289
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
1290
+ }
1291
+
1292
+ static void test_hash_sega_cd_invalid_header()
1293
+ {
1294
+ size_t image_size = 512;
1295
+ uint8_t* image = generate_generic_file(image_size);
1296
+
1297
+ mock_file(0, "game.bin", image, image_size);
1298
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
1299
+
1300
+ test_hash_unknown_format(RC_CONSOLE_SEGA_CD, "game.cue");
1301
+
1302
+ free(image);
1303
+ }
1304
+
1305
+ static void test_hash_saturn()
1306
+ {
1307
+ /* the first 512 bytes of sector 0 are a volume header and ROM header.
1308
+ * generate a generic block and add the Sega CD marker */
1309
+ size_t image_size = 512;
1310
+ uint8_t* image = generate_generic_file(image_size);
1311
+ char hash_file[33], hash_iterator[33];
1312
+ const char* expected_md5 = "4cd9c8e41cd8d137be15bbe6a93ae1d8";
1313
+ memcpy(image, "SEGA SEGASATURN ", 16);
1314
+
1315
+ mock_file(0, "game.bin", image, image_size);
1316
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
1317
+
1318
+ /* test file hash */
1319
+ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_SATURN, "game.cue");
1320
+
1321
+ /* test file identification from iterator */
1322
+ int result_iterator;
1323
+ struct rc_hash_iterator iterator;
1324
+
1325
+ rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0);
1326
+ result_iterator = rc_hash_iterate(hash_iterator, &iterator);
1327
+ rc_hash_destroy_iterator(&iterator);
1328
+
1329
+ /* cleanup */
1330
+ free(image);
1331
+
1332
+ /* validation */
1333
+ ASSERT_NUM_EQUALS(result_file, 1);
1334
+ ASSERT_STR_EQUALS(hash_file, expected_md5);
1335
+
1336
+ ASSERT_NUM_EQUALS(result_iterator, 1);
1337
+ ASSERT_STR_EQUALS(hash_iterator, expected_md5);
1338
+ }
1339
+
1340
+ static void test_hash_saturn_invalid_header()
1341
+ {
1342
+ size_t image_size = 512;
1343
+ uint8_t* image = generate_generic_file(image_size);
1344
+
1345
+ mock_file(0, "game.bin", image, image_size);
1346
+ mock_file(1, "game.cue", (uint8_t*)"game.bin", 8);
1347
+
1348
+ test_hash_unknown_format(RC_CONSOLE_SATURN, "game.cue");
1349
+
1350
+ free(image);
1351
+ }
1352
+
1353
+ /* ========================================================================= */
1354
+
1355
+ void test_hash_disc(void) {
1356
+ TEST_SUITE_BEGIN();
1357
+
1358
+ init_mock_filereader();
1359
+ init_mock_cdreader();
1360
+
1361
+ /* 3DO */
1362
+ TEST(test_hash_3do_bin);
1363
+ TEST(test_hash_3do_cue);
1364
+ TEST(test_hash_3do_iso);
1365
+ TEST(test_hash_3do_invalid_header);
1366
+ TEST(test_hash_3do_launchme_case_insensitive);
1367
+ TEST(test_hash_3do_no_launchme);
1368
+ TEST(test_hash_3do_long_directory);
1369
+
1370
+ /* Amstrad CPC */
1371
+ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_AMSTRAD_PC, "test.dsk", 194816, "9d616e4ad3f16966f61422c57e22aadd");
1372
+ TEST_PARAMS4(test_hash_m3u, RC_CONSOLE_AMSTRAD_PC, "test.dsk", 194816, "9d616e4ad3f16966f61422c57e22aadd");
1373
+
1374
+ /* Apple II */
1375
+ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_APPLE_II, "test.nib", 232960, "96e8d33bdc385fd494327d6e6791cbe4");
1376
+ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_APPLE_II, "test.dsk", 143360, "88be638f4d78b4072109e55f13e8a0ac");
1377
+ TEST_PARAMS4(test_hash_m3u, RC_CONSOLE_APPLE_II, "test.dsk", 143360, "88be638f4d78b4072109e55f13e8a0ac");
1378
+
1379
+ /* Atari Jaguar CD */
1380
+ TEST(test_hash_atari_jaguar_cd);
1381
+ TEST(test_hash_atari_jaguar_cd_byteswapped);
1382
+ TEST(test_hash_atari_jaguar_cd_track3);
1383
+ TEST(test_hash_atari_jaguar_cd_no_header);
1384
+ TEST(test_hash_atari_jaguar_cd_no_sessions);
1385
+ TEST(test_hash_atari_jaguar_cd_homebrew);
1386
+
1387
+ /* Commodore 64 */
1388
+ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_COMMODORE_64, "test.nib", 327936, "e7767d32b23e3fa62c5a250a08caeba3");
1389
+ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_COMMODORE_64, "test.d64", 174848, "ecd5a8ef4e77f2e9469d9b6e891394f0");
1390
+ TEST_PARAMS4(test_hash_m3u, RC_CONSOLE_COMMODORE_64, "test.d64", 174848, "ecd5a8ef4e77f2e9469d9b6e891394f0");
1391
+
1392
+ /* Dreamcast */
1393
+ TEST(test_hash_dreamcast_single_bin);
1394
+ TEST(test_hash_dreamcast_split_bin);
1395
+ TEST(test_hash_dreamcast_cue);
1396
+
1397
+ /* Gamecube */
1398
+ TEST(test_hash_gamecube);
1399
+
1400
+ /* MSX */
1401
+ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_MSX, "test.dsk", 737280, "0e73fe94e5f2e2d8216926eae512b7a6");
1402
+ TEST_PARAMS4(test_hash_m3u, RC_CONSOLE_MSX, "test.dsk", 737280, "0e73fe94e5f2e2d8216926eae512b7a6");
1403
+
1404
+ /* Neo Geo CD */
1405
+ TEST(test_hash_neogeocd);
1406
+ TEST(test_hash_neogeocd_multiple_prg);
1407
+ TEST(test_hash_neogeocd_lowercase_ipl_contents);
1408
+
1409
+ /* PC-8800 */
1410
+ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_PC8800, "test.d88", 348288, "8cca4121bf87200f45e91b905a9f5afd");
1411
+ TEST_PARAMS4(test_hash_m3u, RC_CONSOLE_PC8800, "test.d88", 348288, "8cca4121bf87200f45e91b905a9f5afd");
1412
+
1413
+ /* PC Engine CD */
1414
+ TEST(test_hash_pce_cd);
1415
+ TEST(test_hash_pce_cd_invalid_header);
1416
+
1417
+ /* PC-FX */
1418
+ TEST(test_hash_pcfx);
1419
+ TEST(test_hash_pcfx_invalid_header);
1420
+ TEST(test_hash_pcfx_pce_cd);
1421
+
1422
+ /* Playstation */
1423
+ TEST(test_hash_psx_cd);
1424
+ TEST(test_hash_psx_cd_no_system_cnf);
1425
+ TEST(test_hash_psx_cd_exe_in_subfolder);
1426
+ TEST(test_hash_psx_cd_extra_slash);
1427
+
1428
+ /* Playstation 2 */
1429
+ TEST(test_hash_ps2_iso);
1430
+ TEST(test_hash_ps2_psx);
1431
+
1432
+ /* Playstation Portable */
1433
+ TEST(test_hash_psp);
1434
+ TEST(test_hash_psp_video);
1435
+ TEST(test_hash_psp_homebrew);
1436
+
1437
+ /* Sega CD */
1438
+ TEST(test_hash_sega_cd);
1439
+ TEST(test_hash_sega_cd_invalid_header);
1440
+
1441
+ /* Sega Saturn */
1442
+ TEST(test_hash_saturn);
1443
+ TEST(test_hash_saturn_invalid_header);
1444
+
1445
+ /* ZX Spectrum */
1446
+ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_ZX_SPECTRUM, "test.tap", 1596, "714a9f455e616813dd5421c5b347e5e5");
1447
+ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_ZX_SPECTRUM, "test.tzx", 14971, "93723e6d1100f9d1d448a27cf6618c47");
1448
+
1449
+ TEST_SUITE_END();
1450
+ }