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,777 @@
1
+ #include "rc_internal.h"
2
+
3
+ #include <string.h> /* memcpy */
4
+
5
+ enum {
6
+ RC_CONDITION_CLASSIFICATION_COMBINING,
7
+ RC_CONDITION_CLASSIFICATION_PAUSE,
8
+ RC_CONDITION_CLASSIFICATION_RESET,
9
+ RC_CONDITION_CLASSIFICATION_HITTARGET,
10
+ RC_CONDITION_CLASSIFICATION_MEASURED,
11
+ RC_CONDITION_CLASSIFICATION_OTHER,
12
+ RC_CONDITION_CLASSIFICATION_INDIRECT
13
+ };
14
+
15
+ static int rc_classify_condition(const rc_condition_t* cond) {
16
+ switch (cond->type) {
17
+ case RC_CONDITION_PAUSE_IF:
18
+ return RC_CONDITION_CLASSIFICATION_PAUSE;
19
+
20
+ case RC_CONDITION_RESET_IF:
21
+ return RC_CONDITION_CLASSIFICATION_RESET;
22
+
23
+ case RC_CONDITION_ADD_ADDRESS:
24
+ case RC_CONDITION_ADD_SOURCE:
25
+ case RC_CONDITION_SUB_SOURCE:
26
+ /* these are handled by rc_modified_memref_t */
27
+ return RC_CONDITION_CLASSIFICATION_INDIRECT;
28
+
29
+ case RC_CONDITION_ADD_HITS:
30
+ case RC_CONDITION_AND_NEXT:
31
+ case RC_CONDITION_OR_NEXT:
32
+ case RC_CONDITION_REMEMBER:
33
+ case RC_CONDITION_RESET_NEXT_IF:
34
+ case RC_CONDITION_SUB_HITS:
35
+ return RC_CONDITION_CLASSIFICATION_COMBINING;
36
+
37
+ case RC_CONDITION_MEASURED:
38
+ case RC_CONDITION_MEASURED_IF:
39
+ /* even if not measuring a hit target, we still want to evaluate it every frame */
40
+ return RC_CONDITION_CLASSIFICATION_MEASURED;
41
+
42
+ default:
43
+ if (cond->required_hits != 0)
44
+ return RC_CONDITION_CLASSIFICATION_HITTARGET;
45
+
46
+ return RC_CONDITION_CLASSIFICATION_OTHER;
47
+ }
48
+ }
49
+
50
+ static int32_t rc_classify_conditions(rc_condset_t* self, const char* memaddr, const rc_parse_state_t* parent_parse) {
51
+ rc_parse_state_t parse;
52
+ rc_memrefs_t memrefs;
53
+ rc_condition_t condition;
54
+ int classification;
55
+ uint32_t index = 0;
56
+ uint32_t chain_length = 1;
57
+
58
+ rc_init_parse_state(&parse, NULL);
59
+ rc_init_parse_state_memrefs(&parse, &memrefs);
60
+ parse.ignore_non_parse_errors = parent_parse->ignore_non_parse_errors;
61
+
62
+ do {
63
+ rc_parse_condition_internal(&condition, &memaddr, &parse);
64
+
65
+ if (parse.offset < 0) {
66
+ rc_destroy_parse_state(&parse);
67
+ return parse.offset;
68
+ }
69
+
70
+ ++index;
71
+
72
+ classification = rc_classify_condition(&condition);
73
+ switch (classification) {
74
+ case RC_CONDITION_CLASSIFICATION_COMBINING:
75
+ ++chain_length;
76
+ continue;
77
+
78
+ case RC_CONDITION_CLASSIFICATION_INDIRECT:
79
+ ++self->num_indirect_conditions;
80
+ continue;
81
+
82
+ case RC_CONDITION_CLASSIFICATION_PAUSE:
83
+ self->num_pause_conditions += chain_length;
84
+ break;
85
+
86
+ case RC_CONDITION_CLASSIFICATION_RESET:
87
+ self->num_reset_conditions += chain_length;
88
+ break;
89
+
90
+ case RC_CONDITION_CLASSIFICATION_HITTARGET:
91
+ self->num_hittarget_conditions += chain_length;
92
+ break;
93
+
94
+ case RC_CONDITION_CLASSIFICATION_MEASURED:
95
+ self->num_measured_conditions += chain_length;
96
+ break;
97
+
98
+ default:
99
+ self->num_other_conditions += chain_length;
100
+ break;
101
+ }
102
+
103
+ chain_length = 1;
104
+ } while (*memaddr++ == '_');
105
+
106
+ /* any combining conditions that don't actually feed into a non-combining condition
107
+ * need to still have space allocated for them. put them in "other" to match the
108
+ * logic in rc_find_next_classification */
109
+ self->num_other_conditions += chain_length - 1;
110
+
111
+ rc_destroy_parse_state(&parse);
112
+
113
+ return (int32_t)index;
114
+ }
115
+
116
+ static int rc_find_next_classification(const char* memaddr) {
117
+ rc_parse_state_t parse;
118
+ rc_memrefs_t memrefs;
119
+ rc_condition_t condition;
120
+ int classification;
121
+
122
+ rc_init_parse_state(&parse, NULL);
123
+ rc_init_parse_state_memrefs(&parse, &memrefs);
124
+
125
+ do {
126
+ rc_parse_condition_internal(&condition, &memaddr, &parse);
127
+ if (parse.offset < 0)
128
+ break;
129
+
130
+ classification = rc_classify_condition(&condition);
131
+ switch (classification) {
132
+ case RC_CONDITION_CLASSIFICATION_COMBINING:
133
+ case RC_CONDITION_CLASSIFICATION_INDIRECT:
134
+ break;
135
+
136
+ default:
137
+ rc_destroy_parse_state(&parse);
138
+ return classification;
139
+ }
140
+ } while (*memaddr++ == '_');
141
+
142
+ rc_destroy_parse_state(&parse);
143
+ return RC_CONDITION_CLASSIFICATION_OTHER;
144
+ }
145
+
146
+ static void rc_condition_update_recall_operand(rc_operand_t* operand, const rc_operand_t* remember)
147
+ {
148
+ if (operand->type == RC_OPERAND_RECALL) {
149
+ if (rc_operand_type_is_memref(operand->memref_access_type) && operand->value.memref == NULL) {
150
+ memcpy(operand, remember, sizeof(*remember));
151
+ operand->memref_access_type = operand->type;
152
+ operand->type = RC_OPERAND_RECALL;
153
+ }
154
+ }
155
+ else if (rc_operand_is_memref(operand) && operand->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) {
156
+ rc_modified_memref_t* modified_memref = (rc_modified_memref_t*)operand->value.memref;
157
+ rc_condition_update_recall_operand(&modified_memref->parent, remember);
158
+ rc_condition_update_recall_operand(&modified_memref->modifier, remember);
159
+ }
160
+ }
161
+
162
+ static void rc_update_condition_pause_remember(rc_condset_t* self) {
163
+ rc_operand_t* pause_remember = NULL;
164
+ rc_condition_t* condition;
165
+ rc_condition_t* pause_conditions;
166
+ const rc_condition_t* end_pause_condition;
167
+
168
+ /* ASSERT: pause conditions are first conditions */
169
+ pause_conditions = rc_condset_get_conditions(self);
170
+ end_pause_condition = pause_conditions + self->num_pause_conditions;
171
+
172
+ for (condition = pause_conditions; condition < end_pause_condition; ++condition) {
173
+ if (condition->type == RC_CONDITION_REMEMBER) {
174
+ pause_remember = &condition->operand1;
175
+ }
176
+ else if (pause_remember == NULL) {
177
+ /* if we picked up a non-pause remember, discard it */
178
+ if (condition->operand1.type == RC_OPERAND_RECALL &&
179
+ rc_operand_type_is_memref(condition->operand1.memref_access_type)) {
180
+ condition->operand1.value.memref = NULL;
181
+ }
182
+
183
+ if (condition->operand2.type == RC_OPERAND_RECALL &&
184
+ rc_operand_type_is_memref(condition->operand2.memref_access_type)) {
185
+ condition->operand2.value.memref = NULL;
186
+ }
187
+ }
188
+ }
189
+
190
+ if (pause_remember) {
191
+ for (condition = self->conditions; condition; condition = condition->next) {
192
+ if (condition >= end_pause_condition) {
193
+ /* if we didn't find a remember for a non-pause condition, use the last pause remember */
194
+ rc_condition_update_recall_operand(&condition->operand1, pause_remember);
195
+ rc_condition_update_recall_operand(&condition->operand2, pause_remember);
196
+ }
197
+
198
+ /* Anything after this point will have already been handled */
199
+ if (condition->type == RC_CONDITION_REMEMBER)
200
+ break;
201
+ }
202
+ }
203
+ }
204
+
205
+ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
206
+ rc_condset_with_trailing_conditions_t* condset_with_conditions;
207
+ rc_condset_t local_condset;
208
+ rc_condset_t* self;
209
+ rc_condition_t condition;
210
+ rc_condition_t* conditions;
211
+ rc_condition_t** next;
212
+ rc_condition_t* pause_conditions = NULL;
213
+ rc_condition_t* reset_conditions = NULL;
214
+ rc_condition_t* hittarget_conditions = NULL;
215
+ rc_condition_t* measured_conditions = NULL;
216
+ rc_condition_t* other_conditions = NULL;
217
+ rc_condition_t* indirect_conditions = NULL;
218
+ int classification, combining_classification = RC_CONDITION_CLASSIFICATION_COMBINING;
219
+ uint32_t measured_target = 0;
220
+ int32_t result;
221
+
222
+ if (**memaddr == 'S' || **memaddr == 's' || !**memaddr) {
223
+ /* empty group - editor allows it, so we have to support it */
224
+ self = RC_ALLOC(rc_condset_t, parse);
225
+ memset(self, 0, sizeof(*self));
226
+ return self;
227
+ }
228
+
229
+ memset(&local_condset, 0, sizeof(local_condset));
230
+ result = rc_classify_conditions(&local_condset, *memaddr, parse);
231
+ if (result < 0) {
232
+ parse->offset = result;
233
+ return NULL;
234
+ }
235
+
236
+ condset_with_conditions = RC_ALLOC_WITH_TRAILING(rc_condset_with_trailing_conditions_t,
237
+ rc_condition_t, conditions, result, parse);
238
+ if (parse->offset < 0)
239
+ return NULL;
240
+
241
+ self = (rc_condset_t*)condset_with_conditions;
242
+ memcpy(self, &local_condset, sizeof(local_condset));
243
+ conditions = &condset_with_conditions->conditions[0];
244
+
245
+ if (parse->buffer) {
246
+ pause_conditions = conditions;
247
+ conditions += self->num_pause_conditions;
248
+
249
+ reset_conditions = conditions;
250
+ conditions += self->num_reset_conditions;
251
+
252
+ hittarget_conditions = conditions;
253
+ conditions += self->num_hittarget_conditions;
254
+
255
+ measured_conditions = conditions;
256
+ conditions += self->num_measured_conditions;
257
+
258
+ other_conditions = conditions;
259
+ conditions += self->num_other_conditions;
260
+
261
+ indirect_conditions = conditions;
262
+ }
263
+
264
+ next = &self->conditions;
265
+
266
+ /* prevent bleedthrough of incomplete conditions from other groups */
267
+ parse->addsource_oper = RC_OPERATOR_NONE;
268
+ parse->addsource_parent.type = RC_OPERAND_NONE;
269
+ parse->indirect_parent.type = RC_OPERAND_NONE;
270
+
271
+ /* each condition set has a functionally new recall accumulator */
272
+ parse->remember.type = RC_OPERAND_NONE;
273
+
274
+ for (;;) {
275
+ rc_parse_condition_internal(&condition, memaddr, parse);
276
+
277
+ if (parse->offset < 0)
278
+ return NULL;
279
+
280
+ if (condition.oper == RC_OPERATOR_NONE && !parse->ignore_non_parse_errors) {
281
+ switch (condition.type) {
282
+ case RC_CONDITION_ADD_ADDRESS:
283
+ case RC_CONDITION_ADD_SOURCE:
284
+ case RC_CONDITION_SUB_SOURCE:
285
+ case RC_CONDITION_REMEMBER:
286
+ /* these conditions don't require a right hand side (implied *1) */
287
+ break;
288
+
289
+ case RC_CONDITION_MEASURED:
290
+ /* right hand side is not required when Measured is used in a value */
291
+ if (parse->is_value)
292
+ break;
293
+ /* fallthrough */ /* to default */
294
+
295
+ default:
296
+ parse->offset = RC_INVALID_OPERATOR;
297
+ return NULL;
298
+ }
299
+ }
300
+
301
+ switch (condition.type) {
302
+ case RC_CONDITION_MEASURED:
303
+ if (measured_target != 0) {
304
+ /* multiple Measured flags cannot exist in the same group */
305
+ if (!parse->ignore_non_parse_errors) {
306
+ parse->offset = RC_MULTIPLE_MEASURED;
307
+ return NULL;
308
+ }
309
+ }
310
+ else if (parse->is_value) {
311
+ measured_target = (uint32_t)-1;
312
+ if (!rc_operator_is_modifying(condition.oper)) {
313
+ /* measuring comparison in a value results in a tally (hit count). set target to MAX_INT */
314
+ condition.required_hits = measured_target;
315
+ }
316
+ }
317
+ else if (condition.required_hits != 0) {
318
+ measured_target = condition.required_hits;
319
+ }
320
+ else if (condition.operand2.type == RC_OPERAND_CONST) {
321
+ measured_target = condition.operand2.value.num;
322
+ }
323
+ else if (condition.operand2.type == RC_OPERAND_FP) {
324
+ measured_target = (unsigned)condition.operand2.value.dbl;
325
+ }
326
+ else if (!parse->ignore_non_parse_errors) {
327
+ parse->offset = RC_INVALID_MEASURED_TARGET;
328
+ return NULL;
329
+ }
330
+
331
+ if (parse->measured_target && measured_target != parse->measured_target) {
332
+ /* multiple Measured flags in separate groups must have the same target */
333
+ if (!parse->ignore_non_parse_errors) {
334
+ parse->offset = RC_MULTIPLE_MEASURED;
335
+ return NULL;
336
+ }
337
+ }
338
+
339
+ parse->measured_target = measured_target;
340
+ break;
341
+
342
+ case RC_CONDITION_STANDARD:
343
+ case RC_CONDITION_TRIGGER:
344
+ /* these flags are not allowed in value expressions */
345
+ if (parse->is_value && !parse->ignore_non_parse_errors) {
346
+ parse->offset = RC_INVALID_VALUE_FLAG;
347
+ return NULL;
348
+ }
349
+ break;
350
+ }
351
+
352
+ rc_condition_update_parse_state(&condition, parse);
353
+
354
+ if (parse->buffer) {
355
+ classification = rc_classify_condition(&condition);
356
+ if (classification == RC_CONDITION_CLASSIFICATION_COMBINING) {
357
+ if (combining_classification == RC_CONDITION_CLASSIFICATION_COMBINING) {
358
+ if (**memaddr == '_')
359
+ combining_classification = rc_find_next_classification(&(*memaddr)[1]); /* skip over '_' */
360
+ else
361
+ combining_classification = RC_CONDITION_CLASSIFICATION_OTHER;
362
+ }
363
+
364
+ classification = combining_classification;
365
+ }
366
+ else {
367
+ combining_classification = RC_CONDITION_CLASSIFICATION_COMBINING;
368
+ }
369
+
370
+ switch (classification) {
371
+ case RC_CONDITION_CLASSIFICATION_PAUSE:
372
+ memcpy(pause_conditions, &condition, sizeof(condition));
373
+ *next = pause_conditions++;
374
+ break;
375
+
376
+ case RC_CONDITION_CLASSIFICATION_RESET:
377
+ memcpy(reset_conditions, &condition, sizeof(condition));
378
+ *next = reset_conditions++;
379
+ break;
380
+
381
+ case RC_CONDITION_CLASSIFICATION_HITTARGET:
382
+ memcpy(hittarget_conditions, &condition, sizeof(condition));
383
+ *next = hittarget_conditions++;
384
+ break;
385
+
386
+ case RC_CONDITION_CLASSIFICATION_MEASURED:
387
+ memcpy(measured_conditions, &condition, sizeof(condition));
388
+ *next = measured_conditions++;
389
+ break;
390
+
391
+ case RC_CONDITION_CLASSIFICATION_INDIRECT:
392
+ memcpy(indirect_conditions, &condition, sizeof(condition));
393
+ *next = indirect_conditions++;
394
+ break;
395
+
396
+ default:
397
+ memcpy(other_conditions, &condition, sizeof(condition));
398
+ *next = other_conditions++;
399
+ break;
400
+ }
401
+
402
+ next = &(*next)->next;
403
+ }
404
+
405
+ if (**memaddr != '_')
406
+ break;
407
+
408
+ (*memaddr)++;
409
+ }
410
+
411
+ *next = NULL;
412
+
413
+ self->has_pause = self->num_pause_conditions > 0;
414
+ if (self->has_pause && parse->buffer && parse->remember.type != RC_OPERAND_NONE)
415
+ rc_update_condition_pause_remember(self);
416
+
417
+ return self;
418
+ }
419
+
420
+ static uint8_t rc_condset_evaluate_condition_no_add_hits(rc_condition_t* condition, rc_eval_state_t* eval_state) {
421
+ /* evaluate the current condition */
422
+ uint8_t cond_valid = (uint8_t)rc_test_condition(condition, eval_state);
423
+ condition->is_true = cond_valid;
424
+
425
+ if (eval_state->reset_next) {
426
+ /* previous ResetNextIf resets the hit count on this condition and prevents it from being true */
427
+ eval_state->was_cond_reset |= (condition->current_hits != 0);
428
+
429
+ condition->current_hits = 0;
430
+ cond_valid = 0;
431
+ }
432
+ else {
433
+ /* apply chained logic flags */
434
+ cond_valid &= eval_state->and_next;
435
+ cond_valid |= eval_state->or_next;
436
+
437
+ if (cond_valid) {
438
+ /* true conditions should update their hit count */
439
+ eval_state->has_hits = 1;
440
+
441
+ if (condition->required_hits == 0) {
442
+ /* no target hit count, just keep tallying */
443
+ ++condition->current_hits;
444
+ }
445
+ else if (condition->current_hits < condition->required_hits) {
446
+ /* target hit count hasn't been met, tally and revalidate - only true if hit count becomes met */
447
+ ++condition->current_hits;
448
+ cond_valid = (condition->current_hits == condition->required_hits);
449
+ }
450
+ else {
451
+ /* target hit count has been met, do nothing */
452
+ }
453
+ }
454
+ else if (condition->current_hits > 0) {
455
+ /* target has been true in the past, if the hit target is met, consider it true now */
456
+ eval_state->has_hits = 1;
457
+ cond_valid = (condition->current_hits == condition->required_hits);
458
+ }
459
+ }
460
+
461
+ /* reset chained logic flags for the next condition */
462
+ eval_state->and_next = 1;
463
+ eval_state->or_next = 0;
464
+
465
+ return cond_valid;
466
+ }
467
+
468
+ static uint32_t rc_condset_evaluate_total_hits(rc_condition_t* condition, rc_eval_state_t* eval_state) {
469
+ uint32_t total_hits = condition->current_hits;
470
+
471
+ if (condition->required_hits != 0) {
472
+ /* if the condition has a target hit count, we have to recalculate cond_valid including the AddHits counter */
473
+ const int32_t signed_hits = (int32_t)condition->current_hits + eval_state->add_hits;
474
+ total_hits = (signed_hits >= 0) ? (uint32_t)signed_hits : 0;
475
+ }
476
+ else {
477
+ /* no target hit count. we can't tell if the add_hits value is from this frame or not, so ignore it.
478
+ complex condition will only be true if the current condition is true */
479
+ }
480
+
481
+ eval_state->add_hits = 0;
482
+
483
+ return total_hits;
484
+ }
485
+
486
+ static uint8_t rc_condset_evaluate_condition(rc_condition_t* condition, rc_eval_state_t* eval_state) {
487
+ uint8_t cond_valid = rc_condset_evaluate_condition_no_add_hits(condition, eval_state);
488
+
489
+ if (eval_state->add_hits != 0 && condition->required_hits != 0) {
490
+ uint32_t total_hits = rc_condset_evaluate_total_hits(condition, eval_state);
491
+ cond_valid = (total_hits >= condition->required_hits);
492
+ }
493
+
494
+ /* reset logic flags for the next condition */
495
+ eval_state->reset_next = 0;
496
+
497
+ return cond_valid;
498
+ }
499
+
500
+ static void rc_condset_evaluate_standard(rc_condition_t* condition, rc_eval_state_t* eval_state) {
501
+ const uint8_t cond_valid = rc_condset_evaluate_condition(condition, eval_state);
502
+
503
+ eval_state->is_true &= cond_valid;
504
+ eval_state->is_primed &= cond_valid;
505
+
506
+ if (!cond_valid && eval_state->can_short_curcuit)
507
+ eval_state->stop_processing = 1;
508
+ }
509
+
510
+ static void rc_condset_evaluate_pause_if(rc_condition_t* condition, rc_eval_state_t* eval_state) {
511
+ const uint8_t cond_valid = rc_condset_evaluate_condition(condition, eval_state);
512
+
513
+ if (cond_valid) {
514
+ eval_state->is_paused = 1;
515
+
516
+ /* set cannot be valid if it's paused */
517
+ eval_state->is_true = eval_state->is_primed = 0;
518
+
519
+ /* as soon as we find a PauseIf that evaluates to true, stop processing the rest of the group */
520
+ eval_state->stop_processing = 1;
521
+ }
522
+ else if (condition->required_hits == 0) {
523
+ /* PauseIf didn't evaluate true, and doesn't have a HitCount, reset the HitCount to indicate the condition didn't match */
524
+ condition->current_hits = 0;
525
+ }
526
+ else {
527
+ /* PauseIf has a HitCount that hasn't been met, ignore it for now. */
528
+ }
529
+ }
530
+
531
+ static void rc_condset_evaluate_reset_if(rc_condition_t* condition, rc_eval_state_t* eval_state) {
532
+ const uint8_t cond_valid = rc_condset_evaluate_condition(condition, eval_state);
533
+
534
+ if (cond_valid) {
535
+ /* flag the condition as being responsible for the reset */
536
+ /* make sure not to modify bit0, as we use bitwise-and operators to combine truthiness */
537
+ condition->is_true |= 0x02;
538
+
539
+ /* set cannot be valid if we've hit a reset condition */
540
+ eval_state->is_true = eval_state->is_primed = 0;
541
+
542
+ /* let caller know to reset all hit counts */
543
+ eval_state->was_reset = 1;
544
+
545
+ /* can stop processing once an active ResetIf is encountered */
546
+ eval_state->stop_processing = 1;
547
+ }
548
+ }
549
+
550
+ static void rc_condset_evaluate_trigger(rc_condition_t* condition, rc_eval_state_t* eval_state) {
551
+ const uint8_t cond_valid = rc_condset_evaluate_condition(condition, eval_state);
552
+
553
+ eval_state->is_true &= cond_valid;
554
+ }
555
+
556
+ static void rc_condset_evaluate_measured(rc_condition_t* condition, rc_eval_state_t* eval_state) {
557
+ if (condition->required_hits == 0) {
558
+ rc_condset_evaluate_standard(condition, eval_state);
559
+
560
+ /* Measured condition without a hit target measures the value of the left operand */
561
+ rc_evaluate_operand(&eval_state->measured_value, &condition->operand1, eval_state);
562
+ eval_state->measured_from_hits = 0;
563
+ }
564
+ else {
565
+ /* this largely mimicks rc_condset_evaluate_condition, but captures the total_hits */
566
+ uint8_t cond_valid = rc_condset_evaluate_condition_no_add_hits(condition, eval_state);
567
+ const uint32_t total_hits = rc_condset_evaluate_total_hits(condition, eval_state);
568
+
569
+ cond_valid = (total_hits >= condition->required_hits);
570
+ eval_state->is_true &= cond_valid;
571
+ eval_state->is_primed &= cond_valid;
572
+
573
+ /* if there is a hit target, capture the current hits */
574
+ eval_state->measured_value.value.u32 = total_hits;
575
+ eval_state->measured_value.type = RC_VALUE_TYPE_UNSIGNED;
576
+ eval_state->measured_from_hits = 1;
577
+
578
+ /* reset logic flags for the next condition */
579
+ eval_state->reset_next = 0;
580
+ }
581
+ }
582
+
583
+ static void rc_condset_evaluate_measured_if(rc_condition_t* condition, rc_eval_state_t* eval_state) {
584
+ const uint8_t cond_valid = rc_condset_evaluate_condition(condition, eval_state);
585
+
586
+ eval_state->is_true &= cond_valid;
587
+ eval_state->is_primed &= cond_valid;
588
+ eval_state->can_measure &= cond_valid;
589
+ }
590
+
591
+ static void rc_condset_evaluate_add_hits(rc_condition_t* condition, rc_eval_state_t* eval_state) {
592
+ rc_condset_evaluate_condition_no_add_hits(condition, eval_state);
593
+
594
+ eval_state->add_hits += (int32_t)condition->current_hits;
595
+
596
+ /* ResetNextIf was applied to this AddHits condition; don't apply it to future conditions */
597
+ eval_state->reset_next = 0;
598
+ }
599
+
600
+ static void rc_condset_evaluate_sub_hits(rc_condition_t* condition, rc_eval_state_t* eval_state) {
601
+ rc_condset_evaluate_condition_no_add_hits(condition, eval_state);
602
+
603
+ eval_state->add_hits -= (int32_t)condition->current_hits;
604
+
605
+ /* ResetNextIf was applied to this AddHits condition; don't apply it to future conditions */
606
+ eval_state->reset_next = 0;
607
+ }
608
+
609
+ static void rc_condset_evaluate_reset_next_if(rc_condition_t* condition, rc_eval_state_t* eval_state) {
610
+ eval_state->reset_next = rc_condset_evaluate_condition_no_add_hits(condition, eval_state);
611
+ }
612
+
613
+ static void rc_condset_evaluate_and_next(rc_condition_t* condition, rc_eval_state_t* eval_state) {
614
+ eval_state->and_next = rc_condset_evaluate_condition_no_add_hits(condition, eval_state);
615
+ }
616
+
617
+ static void rc_condset_evaluate_or_next(rc_condition_t* condition, rc_eval_state_t* eval_state) {
618
+ eval_state->or_next = rc_condset_evaluate_condition_no_add_hits(condition, eval_state);
619
+ }
620
+
621
+ void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions,
622
+ rc_eval_state_t* eval_state, int can_short_circuit) {
623
+ const rc_condition_t* condition_end = condition + num_conditions;
624
+ for (; condition < condition_end; ++condition) {
625
+ switch (condition->type) {
626
+ case RC_CONDITION_STANDARD:
627
+ rc_condset_evaluate_standard(condition, eval_state);
628
+ break;
629
+ case RC_CONDITION_PAUSE_IF:
630
+ rc_condset_evaluate_pause_if(condition, eval_state);
631
+ break;
632
+ case RC_CONDITION_RESET_IF:
633
+ rc_condset_evaluate_reset_if(condition, eval_state);
634
+ break;
635
+ case RC_CONDITION_TRIGGER:
636
+ rc_condset_evaluate_trigger(condition, eval_state);
637
+ break;
638
+ case RC_CONDITION_MEASURED:
639
+ rc_condset_evaluate_measured(condition, eval_state);
640
+ break;
641
+ case RC_CONDITION_MEASURED_IF:
642
+ rc_condset_evaluate_measured_if(condition, eval_state);
643
+ break;
644
+ case RC_CONDITION_ADD_SOURCE:
645
+ case RC_CONDITION_SUB_SOURCE:
646
+ case RC_CONDITION_ADD_ADDRESS:
647
+ case RC_CONDITION_REMEMBER:
648
+ /* these are handled by rc_modified_memref_t */
649
+ break;
650
+ case RC_CONDITION_ADD_HITS:
651
+ rc_condset_evaluate_add_hits(condition, eval_state);
652
+ break;
653
+ case RC_CONDITION_SUB_HITS:
654
+ rc_condset_evaluate_sub_hits(condition, eval_state);
655
+ break;
656
+ case RC_CONDITION_RESET_NEXT_IF:
657
+ rc_condset_evaluate_reset_next_if(condition, eval_state);
658
+ break;
659
+ case RC_CONDITION_AND_NEXT:
660
+ rc_condset_evaluate_and_next(condition, eval_state);
661
+ break;
662
+ case RC_CONDITION_OR_NEXT:
663
+ rc_condset_evaluate_or_next(condition, eval_state);
664
+ break;
665
+ default:
666
+ eval_state->stop_processing = 1;
667
+ eval_state->is_true = eval_state->is_primed = 0;
668
+ break;
669
+ }
670
+
671
+ if (eval_state->stop_processing && can_short_circuit)
672
+ break;
673
+ }
674
+ }
675
+
676
+ rc_condition_t* rc_condset_get_conditions(rc_condset_t* self) {
677
+ if (self->conditions)
678
+ return RC_GET_TRAILING(self, rc_condset_with_trailing_conditions_t, rc_condition_t, conditions);
679
+
680
+ return NULL;
681
+ }
682
+
683
+ int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state) {
684
+ rc_condition_t* conditions;
685
+
686
+ /* reset the processing state before processing each condset. do not reset the result state. */
687
+ eval_state->measured_value.type = RC_VALUE_TYPE_NONE;
688
+ eval_state->add_hits = 0;
689
+ eval_state->is_true = 1;
690
+ eval_state->is_primed = 1;
691
+ eval_state->is_paused = 0;
692
+ eval_state->can_measure = 1;
693
+ eval_state->measured_from_hits = 0;
694
+ eval_state->and_next = 1;
695
+ eval_state->or_next = 0;
696
+ eval_state->reset_next = 0;
697
+ eval_state->stop_processing = 0;
698
+
699
+ /* the conditions array is allocated immediately after the rc_condset_t, without a separate pointer */
700
+ conditions = rc_condset_get_conditions(self);
701
+
702
+ if (self->num_pause_conditions) {
703
+ /* one or more Pause conditions exist. if any of them are true (eval_state->is_paused),
704
+ * stop processing this group */
705
+ rc_test_condset_internal(conditions, self->num_pause_conditions, eval_state, 1);
706
+
707
+ self->is_paused = eval_state->is_paused;
708
+ if (self->is_paused) {
709
+ /* condset is paused. stop processing immediately. */
710
+ return 0;
711
+ }
712
+
713
+ conditions += self->num_pause_conditions;
714
+ }
715
+
716
+ if (self->num_reset_conditions) {
717
+ /* one or more Reset conditions exists. if any of them are true (eval_state->was_reset),
718
+ * we'll skip some of the later steps */
719
+ rc_test_condset_internal(conditions, self->num_reset_conditions, eval_state, eval_state->can_short_curcuit);
720
+ conditions += self->num_reset_conditions;
721
+ }
722
+
723
+ if (self->num_hittarget_conditions) {
724
+ /* one or more hit target conditions exists. these must be processed every frame,
725
+ * unless their hit count is going to be reset */
726
+ if (!eval_state->was_reset)
727
+ rc_test_condset_internal(conditions, self->num_hittarget_conditions, eval_state, 0);
728
+
729
+ conditions += self->num_hittarget_conditions;
730
+ }
731
+
732
+ if (self->num_measured_conditions) {
733
+ /* IMPORTANT: reset hit counts on these conditions before processing them so
734
+ * the MeasuredIf logic and Measured value are correct.
735
+ * NOTE: a ResetIf in a later alt group may not have been processed yet.
736
+ * Accept that as a weird edge case, and just recommend the user
737
+ * move the ResetIf if it becomes a problem. */
738
+ if (eval_state->was_reset) {
739
+ int i;
740
+ for (i = 0; i < self->num_measured_conditions; ++i)
741
+ conditions[i].current_hits = 0;
742
+ }
743
+
744
+ /* the measured value must be calculated every frame, even if hit counts will be reset */
745
+ rc_test_condset_internal(conditions, self->num_measured_conditions, eval_state, 0);
746
+ conditions += self->num_measured_conditions;
747
+
748
+ if (eval_state->measured_value.type != RC_VALUE_TYPE_NONE) {
749
+ /* if a MeasuredIf was false (!eval_state->can_measure), or the measured
750
+ * value is a hitcount and a ResetIf is true, zero out the measured value */
751
+ if (!eval_state->can_measure ||
752
+ (eval_state->measured_from_hits && eval_state->was_reset)) {
753
+ eval_state->measured_value.type = RC_VALUE_TYPE_UNSIGNED;
754
+ eval_state->measured_value.value.u32 = 0;
755
+ }
756
+ }
757
+ }
758
+
759
+ if (self->num_other_conditions) {
760
+ /* the remaining conditions only need to be evaluated if the rest of the condset is true */
761
+ if (eval_state->is_true)
762
+ rc_test_condset_internal(conditions, self->num_other_conditions, eval_state, eval_state->can_short_curcuit);
763
+ /* something else is false. if we can't short circuit, and there wasn't a reset, we still need to evaluate these */
764
+ else if (!eval_state->can_short_curcuit && !eval_state->was_reset)
765
+ rc_test_condset_internal(conditions, self->num_other_conditions, eval_state, eval_state->can_short_curcuit);
766
+ }
767
+
768
+ return eval_state->is_true;
769
+ }
770
+
771
+ void rc_reset_condset(rc_condset_t* self) {
772
+ rc_condition_t* condition;
773
+
774
+ for (condition = self->conditions; condition != 0; condition = condition->next) {
775
+ condition->current_hits = 0;
776
+ }
777
+ }