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,852 @@
1
+ #include "rc_runtime.h"
2
+ #include "rc_internal.h"
3
+
4
+ #include "../rc_compat.h"
5
+ #include "../rhash/md5.h"
6
+
7
+ #include <stdlib.h>
8
+ #include <string.h>
9
+
10
+ #define RC_RICHPRESENCE_DISPLAY_BUFFER_SIZE 256
11
+
12
+ /* ===== natvis extensions ===== */
13
+
14
+ typedef struct __rc_runtime_trigger_list_t { rc_runtime_t runtime; } __rc_runtime_trigger_list_t;
15
+ typedef struct __rc_runtime_lboard_list_t { rc_runtime_t runtime; } __rc_runtime_lboard_list_t;
16
+
17
+ static void rc_runtime_natvis_helper(const rc_runtime_event_t* runtime_event)
18
+ {
19
+ struct natvis_extensions {
20
+ __rc_runtime_trigger_list_t trigger_list;
21
+ __rc_runtime_lboard_list_t lboard_list;
22
+ } natvis;
23
+
24
+ memset(&natvis, 0, sizeof(natvis));
25
+ (void)runtime_event;
26
+
27
+ natvis.lboard_list.runtime.lboard_count = 0;
28
+ }
29
+
30
+ /* ============================= */
31
+
32
+ rc_runtime_t* rc_runtime_alloc(void) {
33
+ rc_runtime_t* self;
34
+
35
+ /* create a reference to rc_runtime_natvis_helper so the extensions get compiled in. */
36
+ rc_runtime_event_handler_t unused = &rc_runtime_natvis_helper;
37
+ (void)unused;
38
+
39
+ self = (rc_runtime_t*)malloc(sizeof(rc_runtime_t));
40
+
41
+ if (self) {
42
+ rc_runtime_init(self);
43
+ self->owns_self = 1;
44
+ }
45
+
46
+ return self;
47
+ }
48
+
49
+ void rc_runtime_init(rc_runtime_t* self) {
50
+ memset(self, 0, sizeof(rc_runtime_t));
51
+
52
+ self->memrefs = (rc_memrefs_t*)malloc(sizeof(*self->memrefs));
53
+ rc_memrefs_init(self->memrefs);
54
+ }
55
+
56
+ void rc_runtime_destroy(rc_runtime_t* self) {
57
+ uint32_t i;
58
+
59
+ if (self->triggers) {
60
+ for (i = 0; i < self->trigger_count; ++i) {
61
+ if (self->triggers[i].buffer)
62
+ free(self->triggers[i].buffer);
63
+ }
64
+
65
+ free(self->triggers);
66
+ self->triggers = NULL;
67
+
68
+ self->trigger_count = self->trigger_capacity = 0;
69
+ }
70
+
71
+ if (self->lboards) {
72
+ for (i = 0; i < self->lboard_count; ++i) {
73
+ if (self->lboards[i].buffer)
74
+ free(self->lboards[i].buffer);
75
+ }
76
+
77
+ free(self->lboards);
78
+ self->lboards = NULL;
79
+
80
+ self->lboard_count = self->lboard_capacity = 0;
81
+ }
82
+
83
+ if (self->richpresence) {
84
+ if (self->richpresence->buffer)
85
+ free(self->richpresence->buffer);
86
+ free(self->richpresence);
87
+ }
88
+
89
+ if (self->memrefs)
90
+ rc_memrefs_destroy(self->memrefs);
91
+
92
+ if (self->owns_self)
93
+ free(self);
94
+ }
95
+
96
+ void rc_runtime_checksum(const char* memaddr, uint8_t* md5) {
97
+ md5_state_t state;
98
+ md5_init(&state);
99
+ md5_append(&state, (unsigned char*)memaddr, (int)strlen(memaddr));
100
+ md5_finish(&state, md5);
101
+ }
102
+
103
+ static void rc_runtime_deactivate_trigger_by_index(rc_runtime_t* self, uint32_t index) {
104
+ /* free the trigger, then replace it with the last trigger */
105
+ free(self->triggers[index].buffer);
106
+
107
+ if (--self->trigger_count > index)
108
+ memcpy(&self->triggers[index], &self->triggers[self->trigger_count], sizeof(rc_runtime_trigger_t));
109
+ }
110
+
111
+ void rc_runtime_deactivate_achievement(rc_runtime_t* self, uint32_t id) {
112
+ uint32_t i;
113
+
114
+ for (i = 0; i < self->trigger_count; ++i) {
115
+ if (self->triggers[i].id == id && self->triggers[i].trigger != NULL)
116
+ rc_runtime_deactivate_trigger_by_index(self, i);
117
+ }
118
+ }
119
+
120
+ int rc_runtime_activate_achievement(rc_runtime_t* self, uint32_t id, const char* memaddr, void* unused_L, int unused_funcs_idx) {
121
+ void* trigger_buffer;
122
+ rc_trigger_t* trigger;
123
+ rc_runtime_trigger_t* runtime_trigger;
124
+ rc_preparse_state_t preparse;
125
+ const char* preparse_memaddr = memaddr;
126
+ uint8_t md5[16];
127
+ int32_t size;
128
+ uint32_t i;
129
+
130
+ (void)unused_L;
131
+ (void)unused_funcs_idx;
132
+
133
+ if (memaddr == NULL)
134
+ return RC_INVALID_MEMORY_OPERAND;
135
+
136
+ rc_runtime_checksum(memaddr, md5);
137
+
138
+ /* check to see if the id is already registered with an active trigger */
139
+ for (i = 0; i < self->trigger_count; ++i) {
140
+ if (self->triggers[i].id == id && self->triggers[i].trigger != NULL) {
141
+ if (memcmp(self->triggers[i].md5, md5, 16) == 0) {
142
+ /* if the checksum hasn't changed, we can reuse the existing item */
143
+ rc_reset_trigger(self->triggers[i].trigger);
144
+ return RC_OK;
145
+ }
146
+
147
+ /* checksum has changed, deactivate the the item */
148
+ rc_runtime_deactivate_trigger_by_index(self, i);
149
+
150
+ /* deactivate may reorder the list so we should continue from the current index. however, we
151
+ * assume that only one trigger is active per id, so having found that, just stop scanning.
152
+ */
153
+ break;
154
+ }
155
+ }
156
+
157
+ /* check to see if a disabled trigger for the specific id matches the trigger being registered */
158
+ for (i = 0; i < self->trigger_count; ++i) {
159
+ if (self->triggers[i].id == id && memcmp(self->triggers[i].md5, md5, 16) == 0) {
160
+ /* retrieve the trigger pointer from the buffer */
161
+ size = 0;
162
+ trigger = (rc_trigger_t*)rc_alloc(self->triggers[i].buffer, &size, sizeof(rc_trigger_t), RC_ALIGNOF(rc_trigger_t), NULL, -1);
163
+ self->triggers[i].trigger = trigger;
164
+
165
+ rc_reset_trigger(trigger);
166
+ return RC_OK;
167
+ }
168
+ }
169
+
170
+ /* item has not been previously registered, determine how much space we need for it, and allocate it */
171
+ rc_init_preparse_state(&preparse);
172
+ preparse.parse.existing_memrefs = self->memrefs;
173
+ trigger = RC_ALLOC(rc_trigger_t, &preparse.parse);
174
+ rc_parse_trigger_internal(trigger, &preparse_memaddr, &preparse.parse);
175
+
176
+ size = preparse.parse.offset;
177
+ if (size < 0)
178
+ return size;
179
+
180
+ trigger_buffer = malloc(size);
181
+ if (!trigger_buffer)
182
+ return RC_OUT_OF_MEMORY;
183
+
184
+ /* populate the item, using the communal memrefs pool */
185
+ rc_reset_parse_state(&preparse.parse, trigger_buffer);
186
+ rc_preparse_reserve_memrefs(&preparse, self->memrefs);
187
+ trigger = RC_ALLOC(rc_trigger_t, &preparse.parse);
188
+ rc_parse_trigger_internal(trigger, &memaddr, &preparse.parse);
189
+ rc_destroy_preparse_state(&preparse);
190
+
191
+ if (preparse.parse.offset < 0) {
192
+ free(trigger_buffer);
193
+ return preparse.parse.offset;
194
+ }
195
+
196
+ /* grow the trigger buffer if necessary */
197
+ if (self->trigger_count == self->trigger_capacity) {
198
+ self->trigger_capacity += 32;
199
+ if (!self->triggers)
200
+ self->triggers = (rc_runtime_trigger_t*)malloc(self->trigger_capacity * sizeof(rc_runtime_trigger_t));
201
+ else
202
+ self->triggers = (rc_runtime_trigger_t*)realloc(self->triggers, self->trigger_capacity * sizeof(rc_runtime_trigger_t));
203
+
204
+ if (!self->triggers) {
205
+ free(trigger_buffer);
206
+ return RC_OUT_OF_MEMORY;
207
+ }
208
+ }
209
+
210
+ /* assign the new trigger */
211
+ runtime_trigger = &self->triggers[self->trigger_count];
212
+ runtime_trigger->id = id;
213
+ runtime_trigger->trigger = trigger;
214
+ runtime_trigger->buffer = trigger_buffer;
215
+ runtime_trigger->invalid_memref = NULL;
216
+ memcpy(runtime_trigger->md5, md5, 16);
217
+ runtime_trigger->serialized_size = 0;
218
+ ++self->trigger_count;
219
+
220
+ /* reset it, and return it */
221
+ rc_reset_trigger(trigger);
222
+ return RC_OK;
223
+ }
224
+
225
+ rc_trigger_t* rc_runtime_get_achievement(const rc_runtime_t* self, uint32_t id)
226
+ {
227
+ uint32_t i;
228
+
229
+ for (i = 0; i < self->trigger_count; ++i) {
230
+ if (self->triggers[i].id == id && self->triggers[i].trigger != NULL)
231
+ return self->triggers[i].trigger;
232
+ }
233
+
234
+ return NULL;
235
+ }
236
+
237
+ int rc_runtime_get_achievement_measured(const rc_runtime_t* runtime, uint32_t id, unsigned* measured_value, unsigned* measured_target)
238
+ {
239
+ const rc_trigger_t* trigger = rc_runtime_get_achievement(runtime, id);
240
+ if (!measured_value || !measured_target)
241
+ return 0;
242
+
243
+ if (!trigger) {
244
+ *measured_value = *measured_target = 0;
245
+ return 0;
246
+ }
247
+
248
+ if (rc_trigger_state_active(trigger->state)) {
249
+ *measured_value = (trigger->measured_value == RC_MEASURED_UNKNOWN) ? 0 : trigger->measured_value;
250
+ *measured_target = trigger->measured_target;
251
+ }
252
+ else {
253
+ /* don't report measured information for inactive triggers */
254
+ *measured_value = *measured_target = 0;
255
+ }
256
+
257
+ return 1;
258
+ }
259
+
260
+ int rc_runtime_format_achievement_measured(const rc_runtime_t* runtime, uint32_t id, char* buffer, size_t buffer_size)
261
+ {
262
+ const rc_trigger_t* trigger = rc_runtime_get_achievement(runtime, id);
263
+ uint32_t value;
264
+ if (!buffer || !buffer_size)
265
+ return 0;
266
+
267
+ if (!trigger || /* no trigger */
268
+ trigger->measured_target == 0 || /* not measured */
269
+ !rc_trigger_state_active(trigger->state)) { /* don't report measured value for inactive triggers */
270
+ *buffer = '\0';
271
+ return 0;
272
+ }
273
+
274
+ /* cap the value at the target so we can count past the target: "107 >= 100" */
275
+ value = (trigger->measured_value == RC_MEASURED_UNKNOWN) ? 0 : trigger->measured_value;
276
+ if (value > trigger->measured_target)
277
+ value = trigger->measured_target;
278
+
279
+ if (trigger->measured_as_percent) {
280
+ const uint32_t percent = (uint32_t)(((unsigned long long)value * 100) / trigger->measured_target);
281
+ return snprintf(buffer, buffer_size, "%u%%", percent);
282
+ }
283
+
284
+ return snprintf(buffer, buffer_size, "%u/%u", value, trigger->measured_target);
285
+ }
286
+
287
+ static void rc_runtime_deactivate_lboard_by_index(rc_runtime_t* self, uint32_t index) {
288
+ /* free the lboard, then replace it with the last lboard */
289
+ free(self->lboards[index].buffer);
290
+
291
+ if (--self->lboard_count > index)
292
+ memcpy(&self->lboards[index], &self->lboards[self->lboard_count], sizeof(rc_runtime_lboard_t));
293
+ }
294
+
295
+ void rc_runtime_deactivate_lboard(rc_runtime_t* self, uint32_t id) {
296
+ uint32_t i;
297
+
298
+ for (i = 0; i < self->lboard_count; ++i) {
299
+ if (self->lboards[i].id == id && self->lboards[i].lboard != NULL)
300
+ rc_runtime_deactivate_lboard_by_index(self, i);
301
+ }
302
+ }
303
+
304
+ int rc_runtime_activate_lboard(rc_runtime_t* self, uint32_t id, const char* memaddr, void* unused_L, int unused_funcs_idx) {
305
+ void* lboard_buffer;
306
+ uint8_t md5[16];
307
+ rc_lboard_t* lboard;
308
+ rc_preparse_state_t preparse;
309
+ rc_runtime_lboard_t* runtime_lboard;
310
+ int32_t size;
311
+ uint32_t i;
312
+
313
+ (void)unused_L;
314
+ (void)unused_funcs_idx;
315
+
316
+ if (memaddr == 0)
317
+ return RC_INVALID_MEMORY_OPERAND;
318
+
319
+ rc_runtime_checksum(memaddr, md5);
320
+
321
+ /* check to see if the id is already registered with an active lboard */
322
+ for (i = 0; i < self->lboard_count; ++i) {
323
+ if (self->lboards[i].id == id && self->lboards[i].lboard != NULL) {
324
+ if (memcmp(self->lboards[i].md5, md5, 16) == 0) {
325
+ /* if the checksum hasn't changed, we can reuse the existing item */
326
+ rc_reset_lboard(self->lboards[i].lboard);
327
+ return RC_OK;
328
+ }
329
+
330
+ /* checksum has changed, deactivate the the item */
331
+ rc_runtime_deactivate_lboard_by_index(self, i);
332
+
333
+ /* deactivate may reorder the list so we should continue from the current index. however, we
334
+ * assume that only one trigger is active per id, so having found that, just stop scanning.
335
+ */
336
+ break;
337
+ }
338
+ }
339
+
340
+ /* check to see if a disabled lboard for the specific id matches the lboard being registered */
341
+ for (i = 0; i < self->lboard_count; ++i) {
342
+ if (self->lboards[i].id == id && memcmp(self->lboards[i].md5, md5, 16) == 0) {
343
+ /* retrieve the lboard pointer from the buffer */
344
+ size = 0;
345
+ lboard = (rc_lboard_t*)rc_alloc(self->lboards[i].buffer, &size, sizeof(rc_lboard_t), RC_ALIGNOF(rc_lboard_t), NULL, -1);
346
+ self->lboards[i].lboard = lboard;
347
+
348
+ rc_reset_lboard(lboard);
349
+ return RC_OK;
350
+ }
351
+ }
352
+
353
+ /* item has not been previously registered, determine how much space we need for it, and allocate it */
354
+ rc_init_preparse_state(&preparse);
355
+ preparse.parse.existing_memrefs = self->memrefs;
356
+ lboard = RC_ALLOC(rc_lboard_t, &preparse.parse);
357
+ rc_parse_lboard_internal(lboard, memaddr, &preparse.parse);
358
+
359
+ size = preparse.parse.offset;
360
+ if (size < 0)
361
+ return size;
362
+
363
+ lboard_buffer = malloc(size);
364
+ if (!lboard_buffer)
365
+ return RC_OUT_OF_MEMORY;
366
+
367
+ /* populate the item, using the communal memrefs pool */
368
+ rc_reset_parse_state(&preparse.parse, lboard_buffer);
369
+ rc_preparse_reserve_memrefs(&preparse, self->memrefs);
370
+ lboard = RC_ALLOC(rc_lboard_t, &preparse.parse);
371
+ rc_parse_lboard_internal(lboard, memaddr, &preparse.parse);
372
+ rc_destroy_preparse_state(&preparse);
373
+
374
+ if (preparse.parse.offset < 0) {
375
+ free(lboard_buffer);
376
+ return preparse.parse.offset;
377
+ }
378
+
379
+ /* grow the lboard buffer if necessary */
380
+ if (self->lboard_count == self->lboard_capacity) {
381
+ self->lboard_capacity += 16;
382
+ if (!self->lboards)
383
+ self->lboards = (rc_runtime_lboard_t*)malloc(self->lboard_capacity * sizeof(rc_runtime_lboard_t));
384
+ else
385
+ self->lboards = (rc_runtime_lboard_t*)realloc(self->lboards, self->lboard_capacity * sizeof(rc_runtime_lboard_t));
386
+
387
+ if (!self->lboards) {
388
+ free(lboard_buffer);
389
+ return RC_OUT_OF_MEMORY;
390
+ }
391
+ }
392
+
393
+ /* assign the new lboard */
394
+ runtime_lboard = &self->lboards[self->lboard_count++];
395
+ runtime_lboard->id = id;
396
+ runtime_lboard->value = 0;
397
+ runtime_lboard->lboard = lboard;
398
+ runtime_lboard->buffer = lboard_buffer;
399
+ runtime_lboard->invalid_memref = NULL;
400
+ memcpy(runtime_lboard->md5, md5, 16);
401
+ runtime_lboard->serialized_size = 0;
402
+
403
+ /* reset it, and return it */
404
+ rc_reset_lboard(lboard);
405
+ return RC_OK;
406
+ }
407
+
408
+ rc_lboard_t* rc_runtime_get_lboard(const rc_runtime_t* self, uint32_t id)
409
+ {
410
+ uint32_t i;
411
+
412
+ for (i = 0; i < self->lboard_count; ++i) {
413
+ if (self->lboards[i].id == id && self->lboards[i].lboard != NULL)
414
+ return self->lboards[i].lboard;
415
+ }
416
+
417
+ return NULL;
418
+ }
419
+
420
+ int rc_runtime_format_lboard_value(char* buffer, int size, int32_t value, int format)
421
+ {
422
+ return rc_format_value(buffer, size, value, format);
423
+ }
424
+
425
+ int rc_runtime_activate_richpresence(rc_runtime_t* self, const char* script, void* unused_L, int unused_funcs_idx) {
426
+ rc_richpresence_t* richpresence;
427
+ rc_preparse_state_t preparse;
428
+ uint8_t md5[16];
429
+ int size;
430
+
431
+ (void)unused_L;
432
+ (void)unused_funcs_idx;
433
+
434
+ if (script == NULL)
435
+ return RC_MISSING_DISPLAY_STRING;
436
+
437
+ rc_runtime_checksum(script, md5);
438
+
439
+ /* look for existing match */
440
+ if (self->richpresence && self->richpresence->richpresence && memcmp(self->richpresence->md5, md5, 16) == 0) {
441
+ /* unchanged. reset all of the conditions */
442
+ rc_reset_richpresence(self->richpresence->richpresence);
443
+
444
+ /* return success*/
445
+ return RC_OK;
446
+ }
447
+
448
+ /* no existing match found, parse script */
449
+ rc_init_preparse_state(&preparse);
450
+ preparse.parse.existing_memrefs = self->memrefs;
451
+ richpresence = RC_ALLOC(rc_richpresence_t, &preparse.parse);
452
+ preparse.parse.variables = &richpresence->values;
453
+ rc_parse_richpresence_internal(richpresence, script, &preparse.parse);
454
+
455
+ size = preparse.parse.offset;
456
+ if (size < 0)
457
+ return size;
458
+
459
+ /* if there's a previous script, free it */
460
+ if (self->richpresence) {
461
+ free(self->richpresence->buffer);
462
+ free(self->richpresence);
463
+ }
464
+
465
+ /* allocate and process the new script */
466
+ self->richpresence = (rc_runtime_richpresence_t*)malloc(sizeof(rc_runtime_richpresence_t));
467
+ if (!self->richpresence)
468
+ return RC_OUT_OF_MEMORY;
469
+
470
+ memcpy(self->richpresence->md5, md5, sizeof(md5));
471
+
472
+ self->richpresence->buffer = malloc(size);
473
+ if (!self->richpresence->buffer)
474
+ return RC_OUT_OF_MEMORY;
475
+
476
+ rc_reset_parse_state(&preparse.parse, self->richpresence->buffer);
477
+ rc_preparse_reserve_memrefs(&preparse, self->memrefs);
478
+ richpresence = RC_ALLOC(rc_richpresence_t, &preparse.parse);
479
+ preparse.parse.variables = &richpresence->values;
480
+ rc_parse_richpresence_internal(richpresence, script, &preparse.parse);
481
+ rc_destroy_preparse_state(&preparse);
482
+
483
+ if (preparse.parse.offset < 0) {
484
+ free(self->richpresence->buffer);
485
+ free(self->richpresence);
486
+ self->richpresence = NULL;
487
+ return preparse.parse.offset;
488
+ }
489
+
490
+ if (!richpresence->first_display || !richpresence->first_display->display) {
491
+ /* non-existant rich presence */
492
+ self->richpresence->richpresence = NULL;
493
+ }
494
+ else {
495
+ /* reset all of the conditions */
496
+ rc_reset_richpresence(richpresence);
497
+ self->richpresence->richpresence = richpresence;
498
+ }
499
+
500
+ return RC_OK;
501
+ }
502
+
503
+ int rc_runtime_get_richpresence(const rc_runtime_t* self, char* buffer, size_t buffersize, rc_runtime_peek_t peek, void* peek_ud, void* unused_L) {
504
+ if (self->richpresence && self->richpresence->richpresence)
505
+ return rc_get_richpresence_display_string(self->richpresence->richpresence, buffer, buffersize, peek, peek_ud, unused_L);
506
+
507
+ *buffer = '\0';
508
+ return 0;
509
+ }
510
+
511
+ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, void* unused_L) {
512
+ rc_runtime_event_t runtime_event;
513
+ int i;
514
+
515
+ runtime_event.value = 0;
516
+
517
+ rc_update_memref_values(self->memrefs, peek, ud);
518
+
519
+ for (i = self->trigger_count - 1; i >= 0; --i) {
520
+ rc_trigger_t* trigger = self->triggers[i].trigger;
521
+ int old_state, new_state;
522
+ uint32_t old_measured_value;
523
+
524
+ if (!trigger)
525
+ continue;
526
+
527
+ if (self->triggers[i].invalid_memref) {
528
+ runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED;
529
+ runtime_event.id = self->triggers[i].id;
530
+ runtime_event.value = self->triggers[i].invalid_memref->address;
531
+
532
+ trigger->state = RC_TRIGGER_STATE_DISABLED;
533
+ self->triggers[i].invalid_memref = NULL;
534
+
535
+ event_handler(&runtime_event);
536
+
537
+ runtime_event.value = 0; /* achievement loop expects this to stay at 0 */
538
+ continue;
539
+ }
540
+
541
+ old_measured_value = trigger->measured_value;
542
+ old_state = trigger->state;
543
+ new_state = rc_evaluate_trigger(trigger, peek, ud, unused_L);
544
+
545
+ /* trigger->state doesn't actually change to RESET, RESET just serves as a notification.
546
+ * handle the notification, then look at the actual state */
547
+ if (new_state == RC_TRIGGER_STATE_RESET) {
548
+ runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_RESET;
549
+ runtime_event.id = self->triggers[i].id;
550
+ event_handler(&runtime_event);
551
+
552
+ new_state = trigger->state;
553
+ }
554
+
555
+ /* if the measured value changed and the achievement hasn't triggered, send a notification */
556
+ if (trigger->measured_value != old_measured_value && old_measured_value != RC_MEASURED_UNKNOWN &&
557
+ trigger->measured_target != 0 && trigger->measured_value <= trigger->measured_target &&
558
+ new_state != RC_TRIGGER_STATE_TRIGGERED &&
559
+ new_state != RC_TRIGGER_STATE_INACTIVE && new_state != RC_TRIGGER_STATE_WAITING) {
560
+
561
+ runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PROGRESS_UPDATED;
562
+ runtime_event.id = self->triggers[i].id;
563
+
564
+ if (trigger->measured_as_percent) {
565
+ /* if reporting measured value as a percentage, only send the notification if the percentage changes */
566
+ const int32_t old_percent = (int32_t)(((unsigned long long)old_measured_value * 100) / trigger->measured_target);
567
+ const int32_t new_percent = (int32_t)(((unsigned long long)trigger->measured_value * 100) / trigger->measured_target);
568
+ if (old_percent != new_percent) {
569
+ runtime_event.value = new_percent;
570
+ event_handler(&runtime_event);
571
+ }
572
+ }
573
+ else {
574
+ runtime_event.value = trigger->measured_value;
575
+ event_handler(&runtime_event);
576
+ }
577
+
578
+ runtime_event.value = 0; /* achievement loop expects this to stay at 0 */
579
+ }
580
+
581
+ /* if the state hasn't changed, there won't be any events raised */
582
+ if (new_state == old_state)
583
+ continue;
584
+
585
+ /* raise an UNPRIMED event when changing from PRIMED to anything else */
586
+ if (old_state == RC_TRIGGER_STATE_PRIMED) {
587
+ runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED;
588
+ runtime_event.id = self->triggers[i].id;
589
+ event_handler(&runtime_event);
590
+ }
591
+
592
+ /* raise events for each of the possible new states */
593
+ switch (new_state)
594
+ {
595
+ case RC_TRIGGER_STATE_TRIGGERED:
596
+ runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED;
597
+ runtime_event.id = self->triggers[i].id;
598
+ event_handler(&runtime_event);
599
+ break;
600
+
601
+ case RC_TRIGGER_STATE_PAUSED:
602
+ runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED;
603
+ runtime_event.id = self->triggers[i].id;
604
+ event_handler(&runtime_event);
605
+ break;
606
+
607
+ case RC_TRIGGER_STATE_PRIMED:
608
+ runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED;
609
+ runtime_event.id = self->triggers[i].id;
610
+ event_handler(&runtime_event);
611
+ break;
612
+
613
+ case RC_TRIGGER_STATE_ACTIVE:
614
+ /* only raise ACTIVATED event when transitioning from an inactive state.
615
+ * note that inactive in this case means active but cannot trigger. */
616
+ if (old_state == RC_TRIGGER_STATE_WAITING || old_state == RC_TRIGGER_STATE_PAUSED) {
617
+ runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED;
618
+ runtime_event.id = self->triggers[i].id;
619
+ event_handler(&runtime_event);
620
+ }
621
+ break;
622
+ }
623
+ }
624
+
625
+ for (i = self->lboard_count - 1; i >= 0; --i) {
626
+ rc_lboard_t* lboard = self->lboards[i].lboard;
627
+ int lboard_state;
628
+
629
+ if (!lboard)
630
+ continue;
631
+
632
+ if (self->lboards[i].invalid_memref) {
633
+ runtime_event.type = RC_RUNTIME_EVENT_LBOARD_DISABLED;
634
+ runtime_event.id = self->lboards[i].id;
635
+ runtime_event.value = self->lboards[i].invalid_memref->address;
636
+
637
+ lboard->state = RC_LBOARD_STATE_DISABLED;
638
+ self->lboards[i].invalid_memref = NULL;
639
+
640
+ event_handler(&runtime_event);
641
+ continue;
642
+ }
643
+
644
+ lboard_state = lboard->state;
645
+ switch (rc_evaluate_lboard(lboard, &runtime_event.value, peek, ud, unused_L))
646
+ {
647
+ case RC_LBOARD_STATE_STARTED: /* leaderboard is running */
648
+ if (lboard_state != RC_LBOARD_STATE_STARTED) {
649
+ self->lboards[i].value = runtime_event.value;
650
+
651
+ runtime_event.type = RC_RUNTIME_EVENT_LBOARD_STARTED;
652
+ runtime_event.id = self->lboards[i].id;
653
+ event_handler(&runtime_event);
654
+ }
655
+ else if (runtime_event.value != self->lboards[i].value) {
656
+ self->lboards[i].value = runtime_event.value;
657
+
658
+ runtime_event.type = RC_RUNTIME_EVENT_LBOARD_UPDATED;
659
+ runtime_event.id = self->lboards[i].id;
660
+ event_handler(&runtime_event);
661
+ }
662
+ break;
663
+
664
+ case RC_LBOARD_STATE_CANCELED:
665
+ if (lboard_state != RC_LBOARD_STATE_CANCELED) {
666
+ runtime_event.type = RC_RUNTIME_EVENT_LBOARD_CANCELED;
667
+ runtime_event.id = self->lboards[i].id;
668
+ event_handler(&runtime_event);
669
+ }
670
+ break;
671
+
672
+ case RC_LBOARD_STATE_TRIGGERED:
673
+ if (lboard_state != RC_RUNTIME_EVENT_LBOARD_TRIGGERED) {
674
+ runtime_event.type = RC_RUNTIME_EVENT_LBOARD_TRIGGERED;
675
+ runtime_event.id = self->lboards[i].id;
676
+ event_handler(&runtime_event);
677
+ }
678
+ break;
679
+ }
680
+ }
681
+
682
+ if (self->richpresence && self->richpresence->richpresence)
683
+ rc_update_richpresence(self->richpresence->richpresence, peek, ud, unused_L);
684
+ }
685
+
686
+ void rc_runtime_reset(rc_runtime_t* self) {
687
+ uint32_t i;
688
+
689
+ for (i = 0; i < self->trigger_count; ++i) {
690
+ if (self->triggers[i].trigger)
691
+ rc_reset_trigger(self->triggers[i].trigger);
692
+ }
693
+
694
+ for (i = 0; i < self->lboard_count; ++i) {
695
+ if (self->lboards[i].lboard)
696
+ rc_reset_lboard(self->lboards[i].lboard);
697
+ }
698
+
699
+ if (self->richpresence && self->richpresence->richpresence)
700
+ rc_reset_richpresence(self->richpresence->richpresence);
701
+ }
702
+
703
+ static int rc_condset_contains_memref(const rc_condset_t* condset, const rc_memref_t* memref) {
704
+ rc_condition_t* cond;
705
+ if (!condset)
706
+ return 0;
707
+
708
+ for (cond = condset->conditions; cond; cond = cond->next) {
709
+ if (rc_operand_is_memref(&cond->operand1) && cond->operand1.value.memref == memref)
710
+ return 1;
711
+ if (rc_operand_is_memref(&cond->operand2) && cond->operand2.value.memref == memref)
712
+ return 1;
713
+ }
714
+
715
+ return 0;
716
+ }
717
+
718
+ int rc_value_contains_memref(const rc_value_t* value, const rc_memref_t* memref) {
719
+ rc_condset_t* condset;
720
+ if (!value)
721
+ return 0;
722
+
723
+ for (condset = value->conditions; condset; condset = condset->next) {
724
+ if (rc_condset_contains_memref(condset, memref))
725
+ return 1;
726
+ }
727
+
728
+ return 0;
729
+ }
730
+
731
+ int rc_trigger_contains_memref(const rc_trigger_t* trigger, const rc_memref_t* memref) {
732
+ rc_condset_t* condset;
733
+ if (!trigger)
734
+ return 0;
735
+
736
+ if (rc_condset_contains_memref(trigger->requirement, memref))
737
+ return 1;
738
+
739
+ for (condset = trigger->alternative; condset; condset = condset->next) {
740
+ if (rc_condset_contains_memref(condset, memref))
741
+ return 1;
742
+ }
743
+
744
+ return 0;
745
+ }
746
+
747
+ static void rc_runtime_invalidate_memref(rc_runtime_t* self, rc_memref_t* memref) {
748
+ uint32_t i;
749
+
750
+ /* disable any achievements dependent on the address */
751
+ for (i = 0; i < self->trigger_count; ++i) {
752
+ if (!self->triggers[i].invalid_memref && rc_trigger_contains_memref(self->triggers[i].trigger, memref))
753
+ self->triggers[i].invalid_memref = memref;
754
+ }
755
+
756
+ /* disable any leaderboards dependent on the address */
757
+ for (i = 0; i < self->lboard_count; ++i) {
758
+ if (!self->lboards[i].invalid_memref) {
759
+ rc_lboard_t* lboard = self->lboards[i].lboard;
760
+ if (lboard) {
761
+ if (rc_trigger_contains_memref(&lboard->start, memref)) {
762
+ lboard->start.state = RC_TRIGGER_STATE_DISABLED;
763
+ self->lboards[i].invalid_memref = memref;
764
+ }
765
+
766
+ if (rc_trigger_contains_memref(&lboard->cancel, memref)) {
767
+ lboard->cancel.state = RC_TRIGGER_STATE_DISABLED;
768
+ self->lboards[i].invalid_memref = memref;
769
+ }
770
+
771
+ if (rc_trigger_contains_memref(&lboard->submit, memref)) {
772
+ lboard->submit.state = RC_TRIGGER_STATE_DISABLED;
773
+ self->lboards[i].invalid_memref = memref;
774
+ }
775
+
776
+ if (rc_value_contains_memref(&lboard->value, memref))
777
+ self->lboards[i].invalid_memref = memref;
778
+ }
779
+ }
780
+ }
781
+ }
782
+
783
+ void rc_runtime_invalidate_address(rc_runtime_t* self, uint32_t address) {
784
+ rc_memref_list_t* memref_list = &self->memrefs->memrefs;
785
+ do {
786
+ rc_memref_t* memref = memref_list->items;
787
+ const rc_memref_t* memref_stop = memref + memref_list->count;
788
+
789
+ for (; memref < memref_stop; ++memref) {
790
+ if (memref->address == address) {
791
+ memref->value.type = RC_VALUE_TYPE_NONE;
792
+ rc_runtime_invalidate_memref(self, memref);
793
+ }
794
+ }
795
+
796
+ memref_list = memref_list->next;
797
+ } while (memref_list);
798
+ }
799
+
800
+ void rc_runtime_validate_addresses(rc_runtime_t* self, rc_runtime_event_handler_t event_handler,
801
+ rc_runtime_validate_address_t validate_handler) {
802
+ int num_invalid = 0;
803
+ rc_memref_list_t* memref_list = &self->memrefs->memrefs;
804
+ do {
805
+ rc_memref_t* memref = memref_list->items;
806
+ const rc_memref_t* memref_stop = memref + memref_list->count;
807
+
808
+ for (; memref < memref_stop; ++memref) {
809
+ if (!validate_handler(memref->address)) {
810
+ memref->value.type = RC_VALUE_TYPE_NONE;
811
+ rc_runtime_invalidate_memref(self, memref);
812
+
813
+ ++num_invalid;
814
+ }
815
+ }
816
+
817
+ memref_list = memref_list->next;
818
+ } while (memref_list);
819
+
820
+ if (num_invalid) {
821
+ rc_runtime_event_t runtime_event;
822
+ int i;
823
+
824
+ for (i = self->trigger_count - 1; i >= 0; --i) {
825
+ rc_trigger_t* trigger = self->triggers[i].trigger;
826
+ if (trigger && self->triggers[i].invalid_memref) {
827
+ runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED;
828
+ runtime_event.id = self->triggers[i].id;
829
+ runtime_event.value = self->triggers[i].invalid_memref->address;
830
+
831
+ trigger->state = RC_TRIGGER_STATE_DISABLED;
832
+ self->triggers[i].invalid_memref = NULL;
833
+
834
+ event_handler(&runtime_event);
835
+ }
836
+ }
837
+
838
+ for (i = self->lboard_count - 1; i >= 0; --i) {
839
+ rc_lboard_t* lboard = self->lboards[i].lboard;
840
+ if (lboard && self->lboards[i].invalid_memref) {
841
+ runtime_event.type = RC_RUNTIME_EVENT_LBOARD_DISABLED;
842
+ runtime_event.id = self->lboards[i].id;
843
+ runtime_event.value = self->lboards[i].invalid_memref->address;
844
+
845
+ lboard->state = RC_LBOARD_STATE_DISABLED;
846
+ self->lboards[i].invalid_memref = NULL;
847
+
848
+ event_handler(&runtime_event);
849
+ }
850
+ }
851
+ }
852
+ }