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,935 @@
1
+ #include "rc_internal.h"
2
+
3
+ #include <string.h> /* memset */
4
+ #include <ctype.h> /* isdigit */
5
+ #include <float.h> /* FLT_EPSILON */
6
+ #include <math.h> /* fmod */
7
+
8
+ int rc_is_valid_variable_character(char ch, int is_first) {
9
+ if (is_first) {
10
+ if (!isalpha((unsigned char)ch))
11
+ return 0;
12
+ }
13
+ else {
14
+ if (!isalnum((unsigned char)ch))
15
+ return 0;
16
+ }
17
+ return 1;
18
+ }
19
+
20
+ static void rc_parse_cond_value(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
21
+ rc_condset_t** next_clause;
22
+
23
+ next_clause = &self->conditions;
24
+
25
+ do
26
+ {
27
+ parse->measured_target = 0; /* passing is_value=1 should prevent any conflicts, but clear it out anyway */
28
+ *next_clause = rc_parse_condset(memaddr, parse);
29
+ if (parse->offset < 0) {
30
+ return;
31
+ }
32
+
33
+ if (**memaddr == 'S' || **memaddr == 's') {
34
+ /* alt groups not supported */
35
+ parse->offset = RC_INVALID_VALUE_FLAG;
36
+ }
37
+ else if (parse->measured_target == 0) {
38
+ parse->offset = RC_MISSING_VALUE_MEASURED;
39
+ }
40
+ else if (**memaddr == '$') {
41
+ /* maximum of */
42
+ ++(*memaddr);
43
+ next_clause = &(*next_clause)->next;
44
+ continue;
45
+ }
46
+
47
+ break;
48
+ } while (1);
49
+
50
+ (*next_clause)->next = 0;
51
+ }
52
+
53
+ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
54
+ rc_condset_with_trailing_conditions_t* condset_with_conditions;
55
+ rc_condition_t** next;
56
+ rc_condset_t** next_clause;
57
+ rc_condset_t* condset;
58
+ rc_condition_t local_cond;
59
+ rc_condition_t* cond;
60
+ uint32_t num_measured_conditions;
61
+ char buffer[64] = "A:";
62
+ const char* buffer_ptr;
63
+ char* ptr;
64
+ int done;
65
+
66
+ /* convert legacy format into condset */
67
+ next_clause = &self->conditions;
68
+ do {
69
+ /* count the number of joiners and add one to determine the number of clauses. */
70
+ buffer[0] = 'A'; /* reset to AddSource */
71
+ done = 0;
72
+ num_measured_conditions = 1;
73
+ buffer_ptr = *memaddr;
74
+ do {
75
+ switch (*buffer_ptr++) {
76
+ case '_': /* add next */
77
+ ++num_measured_conditions;
78
+ buffer[0] = 'A'; /* reset to AddSource */
79
+ break;
80
+
81
+ case '*': /* multiply */
82
+ if (*buffer_ptr == '-') {
83
+ /* multiplication by a negative number will convert to SubSource */
84
+ ++buffer_ptr;
85
+ buffer[0] = 'B';
86
+ }
87
+ break;
88
+
89
+ case '\0': /* end of string */
90
+ case '$': /* maximum of */
91
+ case ':': /* end of leaderboard clause */
92
+ case ')': /* end of rich presence macro */
93
+ done = 1;
94
+ break;
95
+
96
+ default: /* assume everything else is valid - bad stuff will be filtered out later */
97
+ break;
98
+ }
99
+ } while (!done);
100
+
101
+ /* if last condition is not AddSource, we'll need to add a dummy condition for the Measured */
102
+ if (buffer[0] != 'A')
103
+ ++num_measured_conditions;
104
+
105
+ condset_with_conditions = RC_ALLOC_WITH_TRAILING(rc_condset_with_trailing_conditions_t,
106
+ rc_condition_t, conditions, num_measured_conditions, parse);
107
+ if (parse->offset < 0)
108
+ return;
109
+
110
+ condset = (rc_condset_t*)condset_with_conditions;
111
+ memset(condset, 0, sizeof(*condset));
112
+ condset->num_measured_conditions = num_measured_conditions;
113
+ cond = &condset_with_conditions->conditions[0];
114
+
115
+ next = &condset->conditions;
116
+
117
+ for (;; ++(*memaddr)) {
118
+ buffer[0] = 'A'; /* reset to AddSource */
119
+ ptr = &buffer[2];
120
+
121
+ /* extract the next clause */
122
+ for (;; ++(*memaddr)) {
123
+ switch (**memaddr) {
124
+ case '_': /* add next */
125
+ *ptr = '\0';
126
+ break;
127
+
128
+ case '$': /* maximum of */
129
+ case '\0': /* end of string */
130
+ case ':': /* end of leaderboard clause */
131
+ case ')': /* end of rich presence macro */
132
+ /* the last condition needs to be Measured - AddSource can be changed here,
133
+ * SubSource will be handled later */
134
+ if (buffer[0] == 'A')
135
+ buffer[0] = 'M';
136
+
137
+ *ptr = '\0';
138
+ break;
139
+
140
+ case '*':
141
+ *ptr++ = '*';
142
+
143
+ buffer_ptr = *memaddr + 1;
144
+ if (*buffer_ptr == '-') {
145
+ buffer[0] = 'B'; /* change to SubSource */
146
+ ++(*memaddr); /* don't copy sign */
147
+ ++buffer_ptr; /* ignore sign when doing floating point check */
148
+ }
149
+ else if (*buffer_ptr == '+') {
150
+ ++buffer_ptr; /* ignore sign when doing floating point check */
151
+ }
152
+
153
+ /* if it looks like a floating point number, add the 'f' prefix */
154
+ while (isdigit((unsigned char)*buffer_ptr))
155
+ ++buffer_ptr;
156
+ if (*buffer_ptr == '.')
157
+ *ptr++ = 'f';
158
+ continue;
159
+
160
+ default:
161
+ *ptr++ = **memaddr;
162
+ continue;
163
+ }
164
+
165
+ break;
166
+ }
167
+
168
+ /* process the clause */
169
+ if (!parse->buffer)
170
+ cond = &local_cond;
171
+
172
+ buffer_ptr = buffer;
173
+ rc_parse_condition_internal(cond, &buffer_ptr, parse);
174
+ if (parse->offset < 0)
175
+ return;
176
+
177
+ if (*buffer_ptr) {
178
+ /* whatever we copied as a single condition was not fully consumed */
179
+ parse->offset = RC_INVALID_COMPARISON;
180
+ return;
181
+ }
182
+
183
+ if (cond->type == RC_CONDITION_MEASURED && !rc_operator_is_modifying(cond->oper)) {
184
+ /* ignore non-modifying operator on measured clause. if it were parsed as an AddSource
185
+ * or SubSource, that would have already happened in rc_parse_condition_internal, and
186
+ * legacy formatted values are essentially a series of AddSources. */
187
+ cond->oper = RC_OPERATOR_NONE;
188
+ }
189
+
190
+ rc_condition_update_parse_state(cond, parse);
191
+
192
+ *next = cond;
193
+ next = &cond->next;
194
+
195
+ if (**memaddr != '_') /* add next */
196
+ break;
197
+
198
+ ++cond;
199
+ }
200
+
201
+ /* -- end of clause -- */
202
+
203
+ /* clause must end in a Measured. if it doesn't, append one */
204
+ if (cond->type != RC_CONDITION_MEASURED) {
205
+ if (!parse->buffer)
206
+ cond = &local_cond;
207
+ else
208
+ ++cond;
209
+
210
+ buffer_ptr = "M:0";
211
+ rc_parse_condition_internal(cond, &buffer_ptr, parse);
212
+ *next = cond;
213
+ next = &cond->next;
214
+ rc_condition_update_parse_state(cond, parse);
215
+ }
216
+
217
+ *next = NULL;
218
+
219
+ /* finalize clause */
220
+ *next_clause = condset;
221
+ next_clause = &condset->next;
222
+
223
+ if (**memaddr != '$') {
224
+ /* end of valid string */
225
+ *next_clause = NULL;
226
+ break;
227
+ }
228
+
229
+ /* max of ($), start a new clause */
230
+ ++(*memaddr);
231
+ } while (1);
232
+ }
233
+
234
+ void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
235
+ const uint8_t was_value = parse->is_value;
236
+ const rc_condition_t* condition;
237
+ parse->is_value = 1;
238
+
239
+ /* if it starts with a condition flag (M: A: B: C:), parse the conditions */
240
+ if ((*memaddr)[1] == ':')
241
+ rc_parse_cond_value(self, memaddr, parse);
242
+ else
243
+ rc_parse_legacy_value(self, memaddr, parse);
244
+
245
+ if (parse->offset >= 0 && parse->buffer) {
246
+ self->name = "(unnamed)";
247
+ self->value.value = self->value.prior = 0;
248
+ self->value.memref_type = RC_MEMREF_TYPE_VALUE;
249
+ self->value.changed = 0;
250
+ self->has_memrefs = 0;
251
+
252
+ for (condition = self->conditions->conditions; condition; condition = condition->next) {
253
+ if (condition->type == RC_CONDITION_MEASURED) {
254
+ if (rc_operand_is_float(&condition->operand1)) {
255
+ self->value.size = RC_MEMSIZE_FLOAT;
256
+ self->value.type = RC_VALUE_TYPE_FLOAT;
257
+ }
258
+ else {
259
+ self->value.size = RC_MEMSIZE_32_BITS;
260
+ self->value.type = RC_VALUE_TYPE_UNSIGNED;
261
+ }
262
+ break;
263
+ }
264
+ }
265
+ }
266
+
267
+ parse->is_value = was_value;
268
+ }
269
+
270
+ int rc_value_size(const char* memaddr) {
271
+ rc_value_with_memrefs_t* value;
272
+ rc_preparse_state_t preparse;
273
+ rc_init_preparse_state(&preparse);
274
+
275
+ value = RC_ALLOC(rc_value_with_memrefs_t, &preparse.parse);
276
+ rc_parse_value_internal(&value->value, &memaddr, &preparse.parse);
277
+ rc_preparse_alloc_memrefs(NULL, &preparse);
278
+
279
+ rc_destroy_preparse_state(&preparse);
280
+ return preparse.parse.offset;
281
+ }
282
+
283
+ rc_value_t* rc_parse_value(void* buffer, const char* memaddr, void* unused_L, int unused_funcs_idx) {
284
+ rc_value_with_memrefs_t* value;
285
+ rc_preparse_state_t preparse;
286
+ const char* preparse_memaddr = memaddr;
287
+
288
+ (void)unused_L;
289
+ (void)unused_funcs_idx;
290
+
291
+ if (!buffer || !memaddr)
292
+ return NULL;
293
+
294
+ rc_init_preparse_state(&preparse);
295
+ value = RC_ALLOC(rc_value_with_memrefs_t, &preparse.parse);
296
+ rc_parse_value_internal(&value->value, &preparse_memaddr, &preparse.parse);
297
+
298
+ rc_reset_parse_state(&preparse.parse, buffer);
299
+ value = RC_ALLOC(rc_value_with_memrefs_t, &preparse.parse);
300
+ rc_preparse_alloc_memrefs(&value->memrefs, &preparse);
301
+
302
+ rc_parse_value_internal(&value->value, &memaddr, &preparse.parse);
303
+ value->value.has_memrefs = 1;
304
+
305
+ rc_destroy_preparse_state(&preparse);
306
+ return (preparse.parse.offset >= 0) ? &value->value : NULL;
307
+ }
308
+
309
+ static void rc_update_value_memrefs(rc_value_t* self, rc_peek_t peek, void* ud) {
310
+ if (self->has_memrefs) {
311
+ rc_value_with_memrefs_t* value = (rc_value_with_memrefs_t*)self;
312
+ rc_update_memref_values(&value->memrefs, peek, ud);
313
+ }
314
+ }
315
+
316
+ int rc_evaluate_value_typed(rc_value_t* self, rc_typed_value_t* value, rc_peek_t peek, void* ud) {
317
+ rc_eval_state_t eval_state;
318
+ rc_condset_t* condset;
319
+ int valid = 0;
320
+
321
+ rc_update_value_memrefs(self, peek, ud);
322
+
323
+ value->value.i32 = 0;
324
+ value->type = RC_VALUE_TYPE_SIGNED;
325
+
326
+ for (condset = self->conditions; condset != NULL; condset = condset->next) {
327
+ memset(&eval_state, 0, sizeof(eval_state));
328
+ eval_state.peek = peek;
329
+ eval_state.peek_userdata = ud;
330
+
331
+ rc_test_condset(condset, &eval_state);
332
+
333
+ if (condset->is_paused)
334
+ continue;
335
+
336
+ if (eval_state.was_reset) {
337
+ /* if any ResetIf condition was true, reset the hit counts
338
+ * NOTE: ResetIf only affects the current condset when used in values!
339
+ */
340
+ rc_reset_condset(condset);
341
+ }
342
+
343
+ if (eval_state.measured_value.type != RC_VALUE_TYPE_NONE) {
344
+ if (!valid) {
345
+ /* capture the first valid measurement, which may be negative */
346
+ memcpy(value, &eval_state.measured_value, sizeof(*value));
347
+ valid = 1;
348
+ }
349
+ else {
350
+ /* multiple condsets are currently only used for the MAX_OF operation.
351
+ * only keep the condset's value if it's higher than the current highest value.
352
+ */
353
+ if (rc_typed_value_compare(&eval_state.measured_value, value, RC_OPERATOR_GT))
354
+ memcpy(value, &eval_state.measured_value, sizeof(*value));
355
+ }
356
+ }
357
+ }
358
+
359
+ return valid;
360
+ }
361
+
362
+ int32_t rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, void* unused_L) {
363
+ rc_typed_value_t result;
364
+ int valid = rc_evaluate_value_typed(self, &result, peek, ud);
365
+
366
+ (void)unused_L;
367
+
368
+ if (valid) {
369
+ /* if not paused, store the value so that it's available when paused. */
370
+ rc_typed_value_convert(&result, RC_VALUE_TYPE_UNSIGNED);
371
+ rc_update_memref_value(&self->value, result.value.u32);
372
+ }
373
+ else {
374
+ /* when paused, the Measured value will not be captured, use the last captured value. */
375
+ result.value.u32 = self->value.value;
376
+ result.type = RC_VALUE_TYPE_UNSIGNED;
377
+ }
378
+
379
+ rc_typed_value_convert(&result, RC_VALUE_TYPE_SIGNED);
380
+ return result.value.i32;
381
+ }
382
+
383
+ void rc_reset_value(rc_value_t* self) {
384
+ rc_condset_t* condset = self->conditions;
385
+ while (condset != NULL) {
386
+ rc_reset_condset(condset);
387
+ condset = condset->next;
388
+ }
389
+
390
+ self->value.value = self->value.prior = 0;
391
+ self->value.changed = 0;
392
+ }
393
+
394
+ int rc_value_from_hits(rc_value_t* self)
395
+ {
396
+ rc_condset_t* condset = self->conditions;
397
+ for (; condset != NULL; condset = condset->next) {
398
+ rc_condition_t* condition = condset->conditions;
399
+ for (; condition != NULL; condition = condition->next) {
400
+ if (condition->type == RC_CONDITION_MEASURED)
401
+ return (condition->required_hits != 0);
402
+ }
403
+ }
404
+
405
+ return 0;
406
+ }
407
+
408
+ rc_value_t* rc_alloc_variable(const char* memaddr, size_t memaddr_len, rc_parse_state_t* parse) {
409
+ rc_value_t** value_ptr = parse->variables;
410
+ rc_value_t* value;
411
+ const char* name;
412
+ uint32_t measured_target;
413
+
414
+ if (!value_ptr)
415
+ return NULL;
416
+
417
+ while (*value_ptr) {
418
+ value = *value_ptr;
419
+ if (strncmp(value->name, memaddr, memaddr_len) == 0 && value->name[memaddr_len] == 0)
420
+ return value;
421
+
422
+ value_ptr = &value->next;
423
+ }
424
+
425
+ /* capture name before calling parse as parse will update memaddr pointer */
426
+ name = rc_alloc_str(parse, memaddr, memaddr_len);
427
+ if (!name)
428
+ return NULL;
429
+
430
+ /* no match found, create a new entry */
431
+ value = RC_ALLOC_SCRATCH(rc_value_t, parse);
432
+ memset(value, 0, sizeof(value->value));
433
+ value->value.size = RC_MEMSIZE_VARIABLE;
434
+ value->next = NULL;
435
+
436
+ /* the helper variable likely has a Measured condition. capture the current measured_target so we can restore it
437
+ * after generating the variable so the variable's Measured target doesn't conflict with the rest of the trigger. */
438
+ measured_target = parse->measured_target;
439
+ rc_parse_value_internal(value, &memaddr, parse);
440
+ parse->measured_target = measured_target;
441
+
442
+ /* store name after calling parse as parse will set name to (unnamed) */
443
+ value->name = name;
444
+
445
+ *value_ptr = value;
446
+ return value;
447
+ }
448
+
449
+ uint32_t rc_count_values(const rc_value_t* values) {
450
+ uint32_t count = 0;
451
+ while (values) {
452
+ ++count;
453
+ values = values->next;
454
+ }
455
+
456
+ return count;
457
+ }
458
+
459
+ void rc_update_values(rc_value_t* values, rc_peek_t peek, void* ud) {
460
+ rc_typed_value_t result;
461
+
462
+ rc_value_t* value = values;
463
+ for (; value; value = value->next) {
464
+ if (rc_evaluate_value_typed(value, &result, peek, ud)) {
465
+ /* store the raw bytes and type to be restored by rc_typed_value_from_memref_value */
466
+ rc_update_memref_value(&value->value, result.value.u32);
467
+ value->value.type = result.type;
468
+ }
469
+ }
470
+ }
471
+
472
+ void rc_reset_values(rc_value_t* values) {
473
+ rc_value_t* value = values;
474
+
475
+ for (; value; value = value->next)
476
+ rc_reset_value(value);
477
+ }
478
+
479
+ void rc_typed_value_from_memref_value(rc_typed_value_t* value, const rc_memref_value_t* memref) {
480
+ /* raw value is always u32, type can mark it as something else */
481
+ value->value.u32 = memref->value;
482
+ value->type = memref->type;
483
+ }
484
+
485
+ void rc_typed_value_convert(rc_typed_value_t* value, char new_type) {
486
+ switch (new_type) {
487
+ case RC_VALUE_TYPE_UNSIGNED:
488
+ switch (value->type) {
489
+ case RC_VALUE_TYPE_UNSIGNED:
490
+ return;
491
+ case RC_VALUE_TYPE_SIGNED:
492
+ value->value.u32 = (unsigned)value->value.i32;
493
+ break;
494
+ case RC_VALUE_TYPE_FLOAT:
495
+ value->value.u32 = (unsigned)value->value.f32;
496
+ break;
497
+ default:
498
+ value->value.u32 = 0;
499
+ break;
500
+ }
501
+ break;
502
+
503
+ case RC_VALUE_TYPE_SIGNED:
504
+ switch (value->type) {
505
+ case RC_VALUE_TYPE_SIGNED:
506
+ return;
507
+ case RC_VALUE_TYPE_UNSIGNED:
508
+ value->value.i32 = (int)value->value.u32;
509
+ break;
510
+ case RC_VALUE_TYPE_FLOAT:
511
+ value->value.i32 = (int)value->value.f32;
512
+ break;
513
+ default:
514
+ value->value.i32 = 0;
515
+ break;
516
+ }
517
+ break;
518
+
519
+ case RC_VALUE_TYPE_FLOAT:
520
+ switch (value->type) {
521
+ case RC_VALUE_TYPE_FLOAT:
522
+ return;
523
+ case RC_VALUE_TYPE_UNSIGNED:
524
+ value->value.f32 = (float)value->value.u32;
525
+ break;
526
+ case RC_VALUE_TYPE_SIGNED:
527
+ value->value.f32 = (float)value->value.i32;
528
+ break;
529
+ default:
530
+ value->value.f32 = 0.0;
531
+ break;
532
+ }
533
+ break;
534
+
535
+ default:
536
+ break;
537
+ }
538
+
539
+ value->type = new_type;
540
+ }
541
+
542
+ static rc_typed_value_t* rc_typed_value_convert_into(rc_typed_value_t* dest, const rc_typed_value_t* source, char new_type) {
543
+ memcpy(dest, source, sizeof(rc_typed_value_t));
544
+ rc_typed_value_convert(dest, new_type);
545
+ return dest;
546
+ }
547
+
548
+ void rc_typed_value_negate(rc_typed_value_t* value) {
549
+ switch (value->type)
550
+ {
551
+ case RC_VALUE_TYPE_UNSIGNED:
552
+ rc_typed_value_convert(value, RC_VALUE_TYPE_SIGNED);
553
+ /* fallthrough */ /* to RC_VALUE_TYPE_SIGNED */
554
+
555
+ case RC_VALUE_TYPE_SIGNED:
556
+ value->value.i32 = -(value->value.i32);
557
+ break;
558
+
559
+ case RC_VALUE_TYPE_FLOAT:
560
+ value->value.f32 = -(value->value.f32);
561
+ break;
562
+
563
+ default:
564
+ break;
565
+ }
566
+ }
567
+
568
+ void rc_typed_value_add(rc_typed_value_t* value, const rc_typed_value_t* amount) {
569
+ rc_typed_value_t converted;
570
+
571
+ if (amount->type != value->type && value->type != RC_VALUE_TYPE_NONE) {
572
+ if (amount->type == RC_VALUE_TYPE_FLOAT)
573
+ rc_typed_value_convert(value, RC_VALUE_TYPE_FLOAT);
574
+ else
575
+ amount = rc_typed_value_convert_into(&converted, amount, value->type);
576
+ }
577
+
578
+ switch (value->type)
579
+ {
580
+ case RC_VALUE_TYPE_UNSIGNED:
581
+ value->value.u32 += amount->value.u32;
582
+ break;
583
+
584
+ case RC_VALUE_TYPE_SIGNED:
585
+ value->value.i32 += amount->value.i32;
586
+ break;
587
+
588
+ case RC_VALUE_TYPE_FLOAT:
589
+ value->value.f32 += amount->value.f32;
590
+ break;
591
+
592
+ case RC_VALUE_TYPE_NONE:
593
+ memcpy(value, amount, sizeof(rc_typed_value_t));
594
+ break;
595
+
596
+ default:
597
+ break;
598
+ }
599
+ }
600
+
601
+ void rc_typed_value_multiply(rc_typed_value_t* value, const rc_typed_value_t* amount) {
602
+ rc_typed_value_t converted;
603
+
604
+ switch (value->type)
605
+ {
606
+ case RC_VALUE_TYPE_UNSIGNED:
607
+ switch (amount->type)
608
+ {
609
+ case RC_VALUE_TYPE_UNSIGNED:
610
+ /* the c standard for unsigned multiplication is well defined as non-overflowing truncation
611
+ * to the type's size. this allows negative multiplication through twos-complements. i.e.
612
+ * 1 * -1 (0xFFFFFFFF) = 0xFFFFFFFF = -1
613
+ * 3 * -2 (0xFFFFFFFE) = 0x2FFFFFFFA & 0xFFFFFFFF = 0xFFFFFFFA = -6
614
+ * 10 * -5 (0xFFFFFFFB) = 0x9FFFFFFCE & 0xFFFFFFFF = 0xFFFFFFCE = -50
615
+ */
616
+ value->value.u32 *= amount->value.u32;
617
+ break;
618
+
619
+ case RC_VALUE_TYPE_SIGNED:
620
+ value->value.u32 *= (unsigned)amount->value.i32;
621
+ break;
622
+
623
+ case RC_VALUE_TYPE_FLOAT:
624
+ rc_typed_value_convert(value, RC_VALUE_TYPE_FLOAT);
625
+ value->value.f32 *= amount->value.f32;
626
+ break;
627
+
628
+ default:
629
+ value->type = RC_VALUE_TYPE_NONE;
630
+ break;
631
+ }
632
+ break;
633
+
634
+ case RC_VALUE_TYPE_SIGNED:
635
+ switch (amount->type)
636
+ {
637
+ case RC_VALUE_TYPE_SIGNED:
638
+ value->value.i32 *= amount->value.i32;
639
+ break;
640
+
641
+ case RC_VALUE_TYPE_UNSIGNED:
642
+ value->value.i32 *= (int)amount->value.u32;
643
+ break;
644
+
645
+ case RC_VALUE_TYPE_FLOAT:
646
+ rc_typed_value_convert(value, RC_VALUE_TYPE_FLOAT);
647
+ value->value.f32 *= amount->value.f32;
648
+ break;
649
+
650
+ default:
651
+ value->type = RC_VALUE_TYPE_NONE;
652
+ break;
653
+ }
654
+ break;
655
+
656
+ case RC_VALUE_TYPE_FLOAT:
657
+ if (amount->type == RC_VALUE_TYPE_NONE) {
658
+ value->type = RC_VALUE_TYPE_NONE;
659
+ }
660
+ else {
661
+ amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT);
662
+ value->value.f32 *= amount->value.f32;
663
+ }
664
+ break;
665
+
666
+ default:
667
+ value->type = RC_VALUE_TYPE_NONE;
668
+ break;
669
+ }
670
+ }
671
+
672
+ void rc_typed_value_divide(rc_typed_value_t* value, const rc_typed_value_t* amount) {
673
+ rc_typed_value_t converted;
674
+
675
+ switch (amount->type)
676
+ {
677
+ case RC_VALUE_TYPE_UNSIGNED:
678
+ if (amount->value.u32 == 0) { /* divide by zero */
679
+ value->type = RC_VALUE_TYPE_NONE;
680
+ return;
681
+ }
682
+
683
+ switch (value->type) {
684
+ case RC_VALUE_TYPE_UNSIGNED: /* integer math */
685
+ value->value.u32 /= amount->value.u32;
686
+ return;
687
+ case RC_VALUE_TYPE_SIGNED: /* integer math */
688
+ value->value.i32 /= (int)amount->value.u32;
689
+ return;
690
+ case RC_VALUE_TYPE_FLOAT:
691
+ amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT);
692
+ break;
693
+ default:
694
+ value->type = RC_VALUE_TYPE_NONE;
695
+ return;
696
+ }
697
+ break;
698
+
699
+ case RC_VALUE_TYPE_SIGNED:
700
+ if (amount->value.i32 == 0) { /* divide by zero */
701
+ value->type = RC_VALUE_TYPE_NONE;
702
+ return;
703
+ }
704
+
705
+ switch (value->type) {
706
+ case RC_VALUE_TYPE_SIGNED: /* integer math */
707
+ value->value.i32 /= amount->value.i32;
708
+ return;
709
+ case RC_VALUE_TYPE_UNSIGNED: /* integer math */
710
+ value->value.u32 /= (unsigned)amount->value.i32;
711
+ return;
712
+ case RC_VALUE_TYPE_FLOAT:
713
+ amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT);
714
+ break;
715
+ default:
716
+ value->type = RC_VALUE_TYPE_NONE;
717
+ return;
718
+ }
719
+ break;
720
+
721
+ case RC_VALUE_TYPE_FLOAT:
722
+ break;
723
+
724
+ default:
725
+ value->type = RC_VALUE_TYPE_NONE;
726
+ return;
727
+ }
728
+
729
+ if (amount->value.f32 == 0.0) { /* divide by zero */
730
+ value->type = RC_VALUE_TYPE_NONE;
731
+ return;
732
+ }
733
+
734
+ rc_typed_value_convert(value, RC_VALUE_TYPE_FLOAT);
735
+ value->value.f32 /= amount->value.f32;
736
+ }
737
+
738
+ void rc_typed_value_modulus(rc_typed_value_t* value, const rc_typed_value_t* amount) {
739
+ rc_typed_value_t converted;
740
+
741
+ switch (amount->type)
742
+ {
743
+ case RC_VALUE_TYPE_UNSIGNED:
744
+ if (amount->value.u32 == 0) { /* divide by zero */
745
+ value->type = RC_VALUE_TYPE_NONE;
746
+ return;
747
+ }
748
+
749
+ switch (value->type) {
750
+ case RC_VALUE_TYPE_UNSIGNED: /* integer math */
751
+ value->value.u32 %= amount->value.u32;
752
+ return;
753
+ case RC_VALUE_TYPE_SIGNED: /* integer math */
754
+ value->value.i32 %= (int)amount->value.u32;
755
+ return;
756
+ case RC_VALUE_TYPE_FLOAT:
757
+ amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT);
758
+ break;
759
+ default:
760
+ value->type = RC_VALUE_TYPE_NONE;
761
+ return;
762
+ }
763
+ break;
764
+
765
+ case RC_VALUE_TYPE_SIGNED:
766
+ if (amount->value.i32 == 0) { /* divide by zero */
767
+ value->type = RC_VALUE_TYPE_NONE;
768
+ return;
769
+ }
770
+
771
+ switch (value->type) {
772
+ case RC_VALUE_TYPE_SIGNED: /* integer math */
773
+ value->value.i32 %= amount->value.i32;
774
+ return;
775
+ case RC_VALUE_TYPE_UNSIGNED: /* integer math */
776
+ value->value.u32 %= (unsigned)amount->value.i32;
777
+ return;
778
+ case RC_VALUE_TYPE_FLOAT:
779
+ amount = rc_typed_value_convert_into(&converted, amount, RC_VALUE_TYPE_FLOAT);
780
+ break;
781
+ default:
782
+ value->type = RC_VALUE_TYPE_NONE;
783
+ return;
784
+ }
785
+ break;
786
+
787
+ case RC_VALUE_TYPE_FLOAT:
788
+ break;
789
+
790
+ default:
791
+ value->type = RC_VALUE_TYPE_NONE;
792
+ return;
793
+ }
794
+
795
+ if (amount->value.f32 == 0.0) { /* divide by zero */
796
+ value->type = RC_VALUE_TYPE_NONE;
797
+ return;
798
+ }
799
+
800
+ rc_typed_value_convert(value, RC_VALUE_TYPE_FLOAT);
801
+ value->value.f32 = (float)fmod(value->value.f32, amount->value.f32);
802
+ }
803
+
804
+ void rc_typed_value_combine(rc_typed_value_t* value, rc_typed_value_t* amount, uint8_t oper) {
805
+ switch (oper) {
806
+ case RC_OPERATOR_MULT:
807
+ rc_typed_value_multiply(value, amount);
808
+ break;
809
+
810
+ case RC_OPERATOR_DIV:
811
+ rc_typed_value_divide(value, amount);
812
+ break;
813
+
814
+ case RC_OPERATOR_AND:
815
+ rc_typed_value_convert(value, RC_VALUE_TYPE_UNSIGNED);
816
+ rc_typed_value_convert(amount, RC_VALUE_TYPE_UNSIGNED);
817
+ value->value.u32 &= amount->value.u32;
818
+ break;
819
+
820
+ case RC_OPERATOR_XOR:
821
+ rc_typed_value_convert(value, RC_VALUE_TYPE_UNSIGNED);
822
+ rc_typed_value_convert(amount, RC_VALUE_TYPE_UNSIGNED);
823
+ value->value.u32 ^= amount->value.u32;
824
+ break;
825
+
826
+ case RC_OPERATOR_MOD:
827
+ rc_typed_value_modulus(value, amount);
828
+ break;
829
+
830
+ case RC_OPERATOR_ADD:
831
+ rc_typed_value_add(value, amount);
832
+ break;
833
+
834
+ case RC_OPERATOR_SUB:
835
+ rc_typed_value_negate(amount);
836
+ rc_typed_value_add(value, amount);
837
+ break;
838
+ }
839
+ }
840
+
841
+
842
+ static int rc_typed_value_compare_floats(float f1, float f2, char oper) {
843
+ if (f1 == f2) {
844
+ /* exactly equal */
845
+ }
846
+ else {
847
+ /* attempt to match 7 significant digits (24-bit mantissa supports just over 7 significant decimal digits) */
848
+ /* https://stackoverflow.com/questions/17333/what-is-the-most-effective-way-for-float-and-double-comparison */
849
+ const float abs1 = (f1 < 0) ? -f1 : f1;
850
+ const float abs2 = (f2 < 0) ? -f2 : f2;
851
+ const float threshold = ((abs1 < abs2) ? abs1 : abs2) * FLT_EPSILON;
852
+ const float diff = f1 - f2;
853
+ const float abs_diff = (diff < 0) ? -diff : diff;
854
+
855
+ if (abs_diff <= threshold) {
856
+ /* approximately equal */
857
+ }
858
+ else if (diff > threshold) {
859
+ /* greater */
860
+ switch (oper) {
861
+ case RC_OPERATOR_NE:
862
+ case RC_OPERATOR_GT:
863
+ case RC_OPERATOR_GE:
864
+ return 1;
865
+
866
+ default:
867
+ return 0;
868
+ }
869
+ }
870
+ else {
871
+ /* lesser */
872
+ switch (oper) {
873
+ case RC_OPERATOR_NE:
874
+ case RC_OPERATOR_LT:
875
+ case RC_OPERATOR_LE:
876
+ return 1;
877
+
878
+ default:
879
+ return 0;
880
+ }
881
+ }
882
+ }
883
+
884
+ /* exactly or approximately equal */
885
+ switch (oper) {
886
+ case RC_OPERATOR_EQ:
887
+ case RC_OPERATOR_GE:
888
+ case RC_OPERATOR_LE:
889
+ return 1;
890
+
891
+ default:
892
+ return 0;
893
+ }
894
+ }
895
+
896
+ int rc_typed_value_compare(const rc_typed_value_t* value1, const rc_typed_value_t* value2, char oper) {
897
+ rc_typed_value_t converted_value;
898
+ if (value2->type != value1->type) {
899
+ /* if either side is a float, convert both sides to float. otherwise, assume the signed-ness of the left side. */
900
+ if (value2->type == RC_VALUE_TYPE_FLOAT)
901
+ value1 = rc_typed_value_convert_into(&converted_value, value1, value2->type);
902
+ else
903
+ value2 = rc_typed_value_convert_into(&converted_value, value2, value1->type);
904
+ }
905
+
906
+ switch (value1->type) {
907
+ case RC_VALUE_TYPE_UNSIGNED:
908
+ switch (oper) {
909
+ case RC_OPERATOR_EQ: return value1->value.u32 == value2->value.u32;
910
+ case RC_OPERATOR_NE: return value1->value.u32 != value2->value.u32;
911
+ case RC_OPERATOR_LT: return value1->value.u32 < value2->value.u32;
912
+ case RC_OPERATOR_LE: return value1->value.u32 <= value2->value.u32;
913
+ case RC_OPERATOR_GT: return value1->value.u32 > value2->value.u32;
914
+ case RC_OPERATOR_GE: return value1->value.u32 >= value2->value.u32;
915
+ default: return 1;
916
+ }
917
+
918
+ case RC_VALUE_TYPE_SIGNED:
919
+ switch (oper) {
920
+ case RC_OPERATOR_EQ: return value1->value.i32 == value2->value.i32;
921
+ case RC_OPERATOR_NE: return value1->value.i32 != value2->value.i32;
922
+ case RC_OPERATOR_LT: return value1->value.i32 < value2->value.i32;
923
+ case RC_OPERATOR_LE: return value1->value.i32 <= value2->value.i32;
924
+ case RC_OPERATOR_GT: return value1->value.i32 > value2->value.i32;
925
+ case RC_OPERATOR_GE: return value1->value.i32 >= value2->value.i32;
926
+ default: return 1;
927
+ }
928
+
929
+ case RC_VALUE_TYPE_FLOAT:
930
+ return rc_typed_value_compare_floats(value1->value.f32, value2->value.f32, oper);
931
+
932
+ default:
933
+ return 1;
934
+ }
935
+ }