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,566 @@
1
+ #include "rc_client_raintegration_internal.h"
2
+
3
+ #include "rc_client_internal.h"
4
+
5
+ #include "rapi/rc_api_common.h"
6
+
7
+ #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
8
+
9
+ /* ===== natvis extensions ===== */
10
+
11
+ typedef struct __rc_client_raintegration_event_enum_t { uint8_t value; } __rc_client_raintegration_event_enum_t;
12
+ static void rc_client_raintegration_natvis_helper(void)
13
+ {
14
+ struct natvis_extensions {
15
+ __rc_client_raintegration_event_enum_t raintegration_event_type;
16
+ } natvis;
17
+
18
+ natvis.raintegration_event_type.value = RC_CLIENT_RAINTEGRATION_EVENT_TYPE_NONE;
19
+ natvis.raintegration_event_type.value = RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED;
20
+ natvis.raintegration_event_type.value = RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED;
21
+ natvis.raintegration_event_type.value = RC_CLIENT_RAINTEGRATION_EVENT_PAUSE;
22
+ natvis.raintegration_event_type.value = RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED;
23
+ }
24
+
25
+ /* ============================= */
26
+
27
+ static void rc_client_raintegration_load_dll(rc_client_t* client,
28
+ const wchar_t* search_directory, rc_client_callback_t callback, void* callback_userdata)
29
+ {
30
+ wchar_t sPath[_MAX_PATH];
31
+ const int nPathSize = sizeof(sPath) / sizeof(sPath[0]);
32
+ rc_client_raintegration_t* raintegration;
33
+ int sPathIndex = 0;
34
+ DWORD dwAttrib;
35
+ HINSTANCE hDLL;
36
+
37
+ if (search_directory) {
38
+ sPathIndex = swprintf_s(sPath, nPathSize, L"%s\\", search_directory);
39
+ if (sPathIndex > nPathSize - 22) {
40
+ callback(RC_INVALID_STATE, "search_directory too long", client, callback_userdata);
41
+ return;
42
+ }
43
+ }
44
+
45
+ #if defined(_M_X64) || defined(__amd64__)
46
+ wcscpy_s(&sPath[sPathIndex], nPathSize - sPathIndex, L"RA_Integration-x64.dll");
47
+ dwAttrib = GetFileAttributesW(sPath);
48
+ if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
49
+ wcscpy_s(&sPath[sPathIndex], nPathSize - sPathIndex, L"RA_Integration.dll");
50
+ dwAttrib = GetFileAttributesW(sPath);
51
+ }
52
+ #else
53
+ wcscpy_s(&sPath[sPathIndex], nPathSize - sPathIndex, L"RA_Integration.dll");
54
+ dwAttrib = GetFileAttributesW(sPath);
55
+ #endif
56
+
57
+ if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
58
+ callback(RC_MISSING_VALUE, "RA_Integration.dll not found in search directory", client, callback_userdata);
59
+ return;
60
+ }
61
+
62
+ hDLL = LoadLibraryW(sPath);
63
+ if (hDLL == NULL) {
64
+ char error_message[512];
65
+ const DWORD last_error = GetLastError();
66
+ int offset = snprintf(error_message, sizeof(error_message), "Failed to load RA_Integration.dll (%u)", last_error);
67
+
68
+ if (last_error != 0) {
69
+ LPSTR messageBuffer = NULL;
70
+ const DWORD size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
71
+ NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
72
+
73
+ snprintf(&error_message[offset], sizeof(error_message) - offset, ": %.*s", size, messageBuffer);
74
+
75
+ LocalFree(messageBuffer);
76
+ }
77
+
78
+ callback(RC_ABORTED, error_message, client, callback_userdata);
79
+ return;
80
+ }
81
+
82
+ raintegration = (rc_client_raintegration_t*)rc_buffer_alloc(&client->state.buffer, sizeof(rc_client_raintegration_t));
83
+ memset(raintegration, 0, sizeof(*raintegration));
84
+ raintegration->hDLL = hDLL;
85
+
86
+ raintegration->get_version = (rc_client_raintegration_get_string_func_t)GetProcAddress(hDLL, "_RA_IntegrationVersion");
87
+ raintegration->get_host_url = (rc_client_raintegration_get_string_func_t)GetProcAddress(hDLL, "_RA_HostUrl");
88
+ raintegration->init_client = (rc_client_raintegration_init_client_func_t)GetProcAddress(hDLL, "_RA_InitClient");
89
+ raintegration->init_client_offline = (rc_client_raintegration_init_client_func_t)GetProcAddress(hDLL, "_RA_InitOffline");
90
+ raintegration->set_console_id = (rc_client_raintegration_set_int_func_t)GetProcAddress(hDLL, "_RA_SetConsoleID");
91
+ raintegration->shutdown = (rc_client_raintegration_action_func_t)GetProcAddress(hDLL, "_RA_Shutdown");
92
+
93
+ raintegration->update_main_window_handle = (rc_client_raintegration_hwnd_action_func_t)GetProcAddress(hDLL, "_RA_UpdateHWnd");
94
+
95
+ raintegration->get_external_client = (rc_client_raintegration_get_external_client_func_t)GetProcAddress(hDLL, "_Rcheevos_GetExternalClient");
96
+ raintegration->get_menu = (rc_client_raintegration_get_menu_func_t)GetProcAddress(hDLL, "_Rcheevos_RAIntegrationGetMenu");
97
+ raintegration->activate_menu_item = (rc_client_raintegration_activate_menuitem_func_t)GetProcAddress(hDLL, "_Rcheevos_ActivateRAIntegrationMenuItem");
98
+ raintegration->set_write_memory_function = (rc_client_raintegration_set_write_memory_func_t)GetProcAddress(hDLL, "_Rcheevos_SetRAIntegrationWriteMemoryFunction");
99
+ raintegration->set_get_game_name_function = (rc_client_raintegration_set_get_game_name_func_t)GetProcAddress(hDLL, "_Rcheevos_SetRAIntegrationGetGameNameFunction");
100
+ raintegration->set_event_handler = (rc_client_raintegration_set_event_handler_func_t)GetProcAddress(hDLL, "_Rcheevos_SetRAIntegrationEventHandler");
101
+ raintegration->has_modifications = (rc_client_raintegration_get_int_func_t)GetProcAddress(hDLL, "_Rcheevos_HasModifications");
102
+ raintegration->get_achievement_state = (rc_client_raintegration_get_achievement_state_func_t)GetProcAddress(hDLL, "_Rcheevos_GetAchievementState");
103
+
104
+ if (!raintegration->get_version ||
105
+ !raintegration->init_client ||
106
+ !raintegration->get_external_client) {
107
+ FreeLibrary(hDLL);
108
+
109
+ callback(RC_ABORTED, "One or more required exports was not found in RA_Integration.dll", client, callback_userdata);
110
+
111
+ /* dummy reference to natvis helper to ensure extensions get compiled in. */
112
+ raintegration->shutdown = rc_client_raintegration_natvis_helper;
113
+ }
114
+ else {
115
+ rc_mutex_lock(&client->state.mutex);
116
+ client->state.raintegration = raintegration;
117
+ rc_mutex_unlock(&client->state.mutex);
118
+
119
+ RC_CLIENT_LOG_INFO_FORMATTED(client, "RA_Integration.dll %s loaded", client->state.raintegration->get_version());
120
+ }
121
+ }
122
+
123
+ typedef struct rc_client_version_validation_callback_data_t {
124
+ rc_client_t* client;
125
+ rc_client_callback_t callback;
126
+ void* callback_userdata;
127
+ HWND main_window_handle;
128
+ char* client_name;
129
+ char* client_version;
130
+ rc_client_async_handle_t async_handle;
131
+ } rc_client_version_validation_callback_data_t;
132
+
133
+ int rc_client_version_less(const char* left, const char* right)
134
+ {
135
+ do {
136
+ int left_len = 0;
137
+ int right_len = 0;
138
+ while (*left && *left == '0')
139
+ ++left;
140
+ while (left[left_len] && left[left_len] != '.')
141
+ ++left_len;
142
+ while (*right && *right == '0')
143
+ ++right;
144
+ while (right[right_len] && right[right_len] != '.')
145
+ ++right_len;
146
+
147
+ if (left_len != right_len)
148
+ return (left_len < right_len);
149
+
150
+ while (left_len--) {
151
+ if (*left != *right)
152
+ return (*left < *right);
153
+ ++left;
154
+ ++right;
155
+ }
156
+
157
+ if (*left == '.')
158
+ ++left;
159
+ if (*right == '.')
160
+ ++right;
161
+ } while (*left || *right);
162
+
163
+ return 0;
164
+ }
165
+
166
+ static void rc_client_init_raintegration(rc_client_t* client,
167
+ rc_client_version_validation_callback_data_t* version_validation_callback_data)
168
+ {
169
+ rc_client_raintegration_init_client_func_t init_func = client->state.raintegration->init_client;
170
+
171
+ if (client->state.raintegration->get_host_url) {
172
+ const char* host_url = client->state.raintegration->get_host_url();
173
+ if (host_url) {
174
+ if (strcmp(host_url, "OFFLINE") != 0) {
175
+ if (strcmp(host_url, rc_api_default_host()) != 0)
176
+ rc_client_set_host(client, host_url);
177
+ }
178
+ else if (client->state.raintegration->init_client_offline) {
179
+ init_func = client->state.raintegration->init_client_offline;
180
+ RC_CLIENT_LOG_INFO(client, "Initializing in offline mode");
181
+ }
182
+ }
183
+ }
184
+
185
+ if (!init_func || !init_func(version_validation_callback_data->main_window_handle,
186
+ version_validation_callback_data->client_name,
187
+ version_validation_callback_data->client_version)) {
188
+ const char* error_message = "RA_Integration initialization failed";
189
+
190
+ rc_client_unload_raintegration(client);
191
+
192
+ RC_CLIENT_LOG_ERR(client, error_message);
193
+ version_validation_callback_data->callback(RC_ABORTED, error_message, client, version_validation_callback_data->callback_userdata);
194
+ }
195
+ else {
196
+ rc_client_external_t* external_client = (rc_client_external_t*)
197
+ rc_buffer_alloc(&client->state.buffer, sizeof(*external_client));
198
+ memset(external_client, 0, sizeof(*external_client));
199
+
200
+ if (!client->state.raintegration->get_external_client(external_client, RC_CLIENT_EXTERNAL_VERSION)) {
201
+ const char* error_message = "RA_Integration external client export failed";
202
+
203
+ rc_client_unload_raintegration(client);
204
+
205
+ RC_CLIENT_LOG_ERR(client, error_message);
206
+ version_validation_callback_data->callback(RC_ABORTED, error_message, client, version_validation_callback_data->callback_userdata);
207
+ }
208
+ else {
209
+ /* copy state to the external client */
210
+ if (external_client->enable_logging)
211
+ external_client->enable_logging(client, client->state.log_level, client->callbacks.log_call);
212
+
213
+ if (external_client->set_event_handler)
214
+ external_client->set_event_handler(client, client->callbacks.event_handler);
215
+ if (external_client->set_read_memory)
216
+ external_client->set_read_memory(client, client->callbacks.read_memory);
217
+
218
+ if (external_client->set_hardcore_enabled)
219
+ external_client->set_hardcore_enabled(rc_client_get_hardcore_enabled(client));
220
+ if (external_client->set_unofficial_enabled)
221
+ external_client->set_unofficial_enabled(rc_client_get_unofficial_enabled(client));
222
+ if (external_client->set_encore_mode_enabled)
223
+ external_client->set_encore_mode_enabled(rc_client_get_encore_mode_enabled(client));
224
+ if (external_client->set_spectator_mode_enabled)
225
+ external_client->set_spectator_mode_enabled(rc_client_get_spectator_mode_enabled(client));
226
+ if (external_client->set_allow_background_memory_reads)
227
+ external_client->set_allow_background_memory_reads(client->state.allow_background_memory_reads);
228
+
229
+ /* attach the external client and call the callback */
230
+ client->state.external_client = external_client;
231
+
232
+ client->state.raintegration->hMainWindow = version_validation_callback_data->main_window_handle;
233
+ client->state.raintegration->bIsInited = 1;
234
+
235
+ version_validation_callback_data->callback(RC_OK, NULL,
236
+ client, version_validation_callback_data->callback_userdata);
237
+ }
238
+ }
239
+ }
240
+
241
+ static void rc_client_version_validation_callback(const rc_api_server_response_t* server_response, void* callback_data)
242
+ {
243
+ rc_client_version_validation_callback_data_t* version_validation_callback_data =
244
+ (rc_client_version_validation_callback_data_t*)callback_data;
245
+ rc_client_t* client = version_validation_callback_data->client;
246
+
247
+ if (rc_client_async_handle_aborted(client, &version_validation_callback_data->async_handle)) {
248
+ RC_CLIENT_LOG_VERBOSE(client, "Version validation aborted");
249
+ }
250
+ else {
251
+ rc_api_response_t response;
252
+ int result;
253
+ const char* current_version;
254
+ const char* minimum_version = "";
255
+
256
+ rc_json_field_t fields[] = {
257
+ RC_JSON_NEW_FIELD("Success"),
258
+ RC_JSON_NEW_FIELD("Error"),
259
+ RC_JSON_NEW_FIELD("MinimumVersion"),
260
+ };
261
+
262
+ memset(&response, 0, sizeof(response));
263
+ rc_buffer_init(&response.buffer);
264
+
265
+ result = rc_json_parse_server_response(&response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
266
+ if (result == RC_OK) {
267
+ if (!rc_json_get_required_string(&minimum_version, &response, &fields[2], "MinimumVersion"))
268
+ result = RC_MISSING_VALUE;
269
+ }
270
+
271
+ if (result != RC_OK) {
272
+ RC_CLIENT_LOG_ERR_FORMATTED(client, "Failed to fetch latest integration version: %.*s", server_response->body_length, server_response->body);
273
+
274
+ rc_client_unload_raintegration(client);
275
+
276
+ version_validation_callback_data->callback(result, rc_error_str(result),
277
+ client, version_validation_callback_data->callback_userdata);
278
+ }
279
+ else {
280
+ current_version = client->state.raintegration->get_version();
281
+
282
+ if (rc_client_version_less(current_version, minimum_version)) {
283
+ char error_message[256];
284
+
285
+ rc_client_unload_raintegration(client);
286
+
287
+ snprintf(error_message, sizeof(error_message),
288
+ "RA_Integration version %s is lower than minimum version %s", current_version, minimum_version);
289
+ RC_CLIENT_LOG_WARN(client, error_message);
290
+ version_validation_callback_data->callback(RC_ABORTED, error_message, client, version_validation_callback_data->callback_userdata);
291
+ }
292
+ else {
293
+ RC_CLIENT_LOG_INFO_FORMATTED(client, "Validated RA_Integration version %s (minimum %s)", current_version, minimum_version);
294
+
295
+ rc_client_init_raintegration(client, version_validation_callback_data);
296
+ }
297
+ }
298
+
299
+ rc_buffer_destroy(&response.buffer);
300
+ }
301
+
302
+ free(version_validation_callback_data->client_name);
303
+ free(version_validation_callback_data->client_version);
304
+ free(version_validation_callback_data);
305
+ }
306
+
307
+ rc_client_async_handle_t* rc_client_begin_load_raintegration(rc_client_t* client,
308
+ const wchar_t* search_directory, HWND main_window_handle,
309
+ const char* client_name, const char* client_version,
310
+ rc_client_callback_t callback, void* callback_userdata)
311
+ {
312
+ rc_client_version_validation_callback_data_t* callback_data;
313
+ rc_api_url_builder_t builder;
314
+ rc_api_request_t request;
315
+
316
+ if (!client) {
317
+ callback(RC_INVALID_STATE, "client is required", client, callback_userdata);
318
+ return NULL;
319
+ }
320
+
321
+ if (!client_name) {
322
+ callback(RC_INVALID_STATE, "client_name is required", client, callback_userdata);
323
+ return NULL;
324
+ }
325
+
326
+ if (!client_version) {
327
+ callback(RC_INVALID_STATE, "client_version is required", client, callback_userdata);
328
+ return NULL;
329
+ }
330
+
331
+ if (client->state.user != RC_CLIENT_USER_STATE_NONE) {
332
+ callback(RC_INVALID_STATE, "Cannot initialize RAIntegration after login", client, callback_userdata);
333
+ return NULL;
334
+ }
335
+
336
+ if (!client->state.raintegration) {
337
+ if (!main_window_handle) {
338
+ callback(RC_INVALID_STATE, "main_window_handle is required", client, callback_userdata);
339
+ return NULL;
340
+ }
341
+
342
+ rc_client_raintegration_load_dll(client, search_directory, callback, callback_userdata);
343
+ if (!client->state.raintegration)
344
+ return NULL;
345
+ }
346
+
347
+ if (client->state.raintegration->get_host_url) {
348
+ const char* host_url = client->state.raintegration->get_host_url();
349
+ if (host_url && strcmp(host_url, rc_api_default_host()) != 0 &&
350
+ strcmp(host_url, "OFFLINE") != 0) {
351
+ /* if the DLL specifies a custom host, use it */
352
+ rc_client_set_host(client, host_url);
353
+ }
354
+ }
355
+
356
+ memset(&request, 0, sizeof(request));
357
+ rc_api_url_build_dorequest_url(&request, &client->state.host);
358
+ rc_url_builder_init(&builder, &request.buffer, 48);
359
+ rc_url_builder_append_str_param(&builder, "r", "latestintegration");
360
+ request.post_data = rc_url_builder_finalize(&builder);
361
+
362
+ callback_data = calloc(1, sizeof(*callback_data));
363
+ if (!callback_data) {
364
+ callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, callback_userdata);
365
+ return NULL;
366
+ }
367
+
368
+ callback_data->client = client;
369
+ callback_data->callback = callback;
370
+ callback_data->callback_userdata = callback_userdata;
371
+ callback_data->client_name = strdup(client_name);
372
+ callback_data->client_version = strdup(client_version);
373
+ callback_data->main_window_handle = main_window_handle;
374
+
375
+ client->callbacks.server_call(&request, rc_client_version_validation_callback, callback_data, client);
376
+ return &callback_data->async_handle;
377
+ }
378
+
379
+ void rc_client_raintegration_update_main_window_handle(rc_client_t* client, HWND main_window_handle)
380
+ {
381
+ if (client && client->state.raintegration) {
382
+ client->state.raintegration->hMainWindow = main_window_handle;
383
+
384
+ if (client->state.raintegration->bIsInited &&
385
+ client->state.raintegration->update_main_window_handle) {
386
+ client->state.raintegration->update_main_window_handle(main_window_handle);
387
+ }
388
+ }
389
+ }
390
+
391
+ void rc_client_raintegration_set_write_memory_function(rc_client_t* client, rc_client_raintegration_write_memory_func_t handler)
392
+ {
393
+ if (client && client->state.raintegration && client->state.raintegration->set_write_memory_function)
394
+ client->state.raintegration->set_write_memory_function(client, handler);
395
+ }
396
+
397
+ void rc_client_raintegration_set_get_game_name_function(rc_client_t* client, rc_client_raintegration_get_game_name_func_t handler)
398
+ {
399
+ if (client && client->state.raintegration && client->state.raintegration->set_get_game_name_function)
400
+ client->state.raintegration->set_get_game_name_function(client, handler);
401
+ }
402
+
403
+ void rc_client_raintegration_set_event_handler(rc_client_t* client,
404
+ rc_client_raintegration_event_handler_t handler)
405
+ {
406
+ if (client && client->state.raintegration && client->state.raintegration->set_event_handler)
407
+ client->state.raintegration->set_event_handler(client, handler);
408
+ }
409
+
410
+ const rc_client_raintegration_menu_t* rc_client_raintegration_get_menu(const rc_client_t* client)
411
+ {
412
+ if (!client || !client->state.raintegration ||
413
+ !client->state.raintegration->bIsInited ||
414
+ !client->state.raintegration->get_menu) {
415
+ return NULL;
416
+ }
417
+
418
+ return client->state.raintegration->get_menu();
419
+ }
420
+
421
+ void rc_client_raintegration_set_console_id(rc_client_t* client, uint32_t console_id)
422
+ {
423
+ if (client && client->state.raintegration && client->state.raintegration->set_console_id)
424
+ client->state.raintegration->set_console_id(console_id);
425
+ }
426
+
427
+ int rc_client_raintegration_has_modifications(const rc_client_t* client)
428
+ {
429
+ if (!client || !client->state.raintegration ||
430
+ !client->state.raintegration->bIsInited ||
431
+ !client->state.raintegration->has_modifications) {
432
+ return 0;
433
+ }
434
+
435
+ return client->state.raintegration->has_modifications();
436
+ }
437
+
438
+ int rc_client_raintegration_get_achievement_state(const rc_client_t* client, uint32_t achievement_id)
439
+ {
440
+ if (!client || !client->state.raintegration ||
441
+ !client->state.raintegration->bIsInited ||
442
+ !client->state.raintegration->get_achievement_state) {
443
+ return RC_CLIENT_RAINTEGRATION_ACHIEVEMENT_STATE_NONE;
444
+ }
445
+
446
+ return client->state.raintegration->get_achievement_state(achievement_id);
447
+ }
448
+
449
+ void rc_client_raintegration_rebuild_submenu(rc_client_t* client, HMENU hMenu)
450
+ {
451
+ HMENU hPopupMenu = NULL;
452
+ const rc_client_raintegration_menu_t* menu;
453
+
454
+ if (!client || !client->state.raintegration)
455
+ return;
456
+
457
+ /* destroy the existing menu */
458
+ if (client->state.raintegration->hPopupMenu)
459
+ DestroyMenu(client->state.raintegration->hPopupMenu);
460
+
461
+ /* create the popup menu */
462
+ hPopupMenu = CreatePopupMenu();
463
+
464
+ menu = rc_client_raintegration_get_menu(client);
465
+ if (menu && menu->num_items)
466
+ {
467
+ const rc_client_raintegration_menu_item_t* menuitem = menu->items;
468
+ const rc_client_raintegration_menu_item_t* stop = menu->items + menu->num_items;
469
+
470
+ for (; menuitem < stop; ++menuitem)
471
+ {
472
+ if (menuitem->id == 0)
473
+ AppendMenuA(hPopupMenu, MF_SEPARATOR, 0U, NULL);
474
+ else
475
+ {
476
+ UINT flags = MF_STRING;
477
+ if (menuitem->checked)
478
+ flags |= MF_CHECKED;
479
+ if (!menuitem->enabled)
480
+ flags |= MF_GRAYED;
481
+
482
+ AppendMenuA(hPopupMenu, flags, menuitem->id, menuitem->label);
483
+ }
484
+ }
485
+ }
486
+
487
+ /* add/update the item containing the popup menu */
488
+ {
489
+ int nIndex = GetMenuItemCount(hMenu);
490
+ const char* menuText = "&RetroAchievements";
491
+ char buffer[64];
492
+
493
+ UINT flags = MF_POPUP | MF_STRING;
494
+ if (!menu || !menu->num_items)
495
+ flags |= MF_GRAYED;
496
+
497
+ while (--nIndex >= 0)
498
+ {
499
+ if (GetMenuStringA(hMenu, nIndex, buffer, sizeof(buffer) - 1, MF_BYPOSITION))
500
+ {
501
+ if (strcmp(buffer, menuText) == 0)
502
+ break;
503
+ }
504
+ }
505
+
506
+ if (nIndex == -1)
507
+ AppendMenuA(hMenu, flags, (UINT_PTR)hPopupMenu, menuText);
508
+ else
509
+ ModifyMenuA(hMenu, nIndex, flags | MF_BYPOSITION, (UINT_PTR)hPopupMenu, menuText);
510
+
511
+ if (client->state.raintegration->hMainWindow && GetMenu(client->state.raintegration->hMainWindow) == hMenu)
512
+ DrawMenuBar(client->state.raintegration->hMainWindow);
513
+ }
514
+
515
+ client->state.raintegration->hPopupMenu = hPopupMenu;
516
+ }
517
+
518
+ void rc_client_raintegration_update_menu_item(const rc_client_t* client, const rc_client_raintegration_menu_item_t* menuitem)
519
+ {
520
+ if (client && client->state.raintegration && client->state.raintegration->hPopupMenu)
521
+ {
522
+ UINT flags = MF_STRING;
523
+ if (menuitem->checked)
524
+ flags |= MF_CHECKED;
525
+
526
+ CheckMenuItem(client->state.raintegration->hPopupMenu, menuitem->id, flags | MF_BYCOMMAND);
527
+
528
+ flags = (menuitem->enabled) ? MF_ENABLED : MF_GRAYED;
529
+ EnableMenuItem(client->state.raintegration->hPopupMenu, menuitem->id, flags | MF_BYCOMMAND);
530
+ }
531
+ }
532
+
533
+ int rc_client_raintegration_activate_menu_item(const rc_client_t* client, uint32_t menu_item_id)
534
+ {
535
+ if (!client || !client->state.raintegration || !client->state.raintegration->activate_menu_item)
536
+ return 0;
537
+
538
+ return client->state.raintegration->activate_menu_item(menu_item_id);
539
+ }
540
+
541
+ void rc_client_unload_raintegration(rc_client_t* client)
542
+ {
543
+ HINSTANCE hDLL;
544
+
545
+ if (!client || !client->state.raintegration)
546
+ return;
547
+
548
+ RC_CLIENT_LOG_INFO(client, "Unloading RA_Integration")
549
+
550
+ if (client->state.external_client && client->state.external_client->destroy)
551
+ client->state.external_client->destroy();
552
+
553
+ if (client->state.raintegration->shutdown)
554
+ client->state.raintegration->shutdown();
555
+
556
+ rc_mutex_lock(&client->state.mutex);
557
+ hDLL = client->state.raintegration->hDLL;
558
+ client->state.raintegration = NULL;
559
+ client->state.external_client = NULL;
560
+ rc_mutex_unlock(&client->state.mutex);
561
+
562
+ if (hDLL)
563
+ FreeLibrary(hDLL);
564
+ }
565
+
566
+ #endif /* RC_CLIENT_SUPPORTS_RAINTEGRATION */
@@ -0,0 +1,61 @@
1
+ #ifndef RC_CLIENT_RAINTEGRATION_INTERNAL_H
2
+ #define RC_CLIENT_RAINTEGRATION_INTERNAL_H
3
+
4
+ #include "rc_client_raintegration.h"
5
+
6
+ #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
7
+
8
+ #include "rc_client_external.h"
9
+ #include "rc_compat.h"
10
+
11
+ RC_BEGIN_C_DECLS
12
+
13
+ /* RAIntegration follows the same calling convention as rcheevos */
14
+
15
+ typedef void (RC_CCONV* rc_client_raintegration_action_func_t)(void);
16
+ typedef const char* (RC_CCONV* rc_client_raintegration_get_string_func_t)(void);
17
+ typedef int (RC_CCONV* rc_client_raintegration_init_client_func_t)(HWND hMainWnd, const char* sClientName, const char* sClientVersion);
18
+ typedef int (RC_CCONV* rc_client_raintegration_get_external_client_func_t)(rc_client_external_t* pClient, int nVersion);
19
+ typedef void (RC_CCONV* rc_client_raintegration_hwnd_action_func_t)(HWND hWnd);
20
+ typedef int (RC_CCONV* rc_client_raintegration_get_achievement_state_func_t)(uint32_t nMenuItemId);
21
+ typedef const rc_client_raintegration_menu_t* (RC_CCONV* rc_client_raintegration_get_menu_func_t)(void);
22
+ typedef int (RC_CCONV* rc_client_raintegration_activate_menuitem_func_t)(uint32_t nMenuItemId);
23
+ typedef void (RC_CCONV* rc_client_raintegration_set_write_memory_func_t)(rc_client_t* pClient, rc_client_raintegration_write_memory_func_t handler);
24
+ typedef void (RC_CCONV* rc_client_raintegration_set_get_game_name_func_t)(rc_client_t* pClient, rc_client_raintegration_get_game_name_func_t handler);
25
+ typedef void (RC_CCONV* rc_client_raintegration_set_event_handler_func_t)(rc_client_t* pClient, rc_client_raintegration_event_handler_t handler);
26
+ typedef void (RC_CCONV* rc_client_raintegration_set_int_func_t)(int);
27
+ typedef int (RC_CCONV* rc_client_raintegration_get_int_func_t)(void);
28
+
29
+ typedef struct rc_client_raintegration_t
30
+ {
31
+ HINSTANCE hDLL;
32
+ HWND hMainWindow;
33
+ HMENU hPopupMenu;
34
+ uint8_t bIsInited;
35
+
36
+ rc_client_raintegration_get_string_func_t get_version;
37
+ rc_client_raintegration_get_string_func_t get_host_url;
38
+ rc_client_raintegration_init_client_func_t init_client;
39
+ rc_client_raintegration_init_client_func_t init_client_offline;
40
+ rc_client_raintegration_set_int_func_t set_console_id;
41
+ rc_client_raintegration_action_func_t shutdown;
42
+
43
+ rc_client_raintegration_hwnd_action_func_t update_main_window_handle;
44
+
45
+ rc_client_raintegration_set_write_memory_func_t set_write_memory_function;
46
+ rc_client_raintegration_set_get_game_name_func_t set_get_game_name_function;
47
+ rc_client_raintegration_set_event_handler_func_t set_event_handler;
48
+ rc_client_raintegration_get_menu_func_t get_menu;
49
+ rc_client_raintegration_activate_menuitem_func_t activate_menu_item;
50
+ rc_client_raintegration_get_int_func_t has_modifications;
51
+ rc_client_raintegration_get_achievement_state_func_t get_achievement_state;
52
+
53
+ rc_client_raintegration_get_external_client_func_t get_external_client;
54
+
55
+ } rc_client_raintegration_t;
56
+
57
+ RC_END_C_DECLS
58
+
59
+ #endif /* RC_CLIENT_SUPPORTS_RAINTEGRATION */
60
+
61
+ #endif /* RC_CLIENT_RAINTEGRATION_INTERNAL_H */