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,1073 @@
1
+ #include "rc_runtime.h"
2
+ #include "rc_internal.h"
3
+
4
+ #include "rc_util.h"
5
+ #include "../rhash/md5.h"
6
+
7
+ #include <assert.h>
8
+ #include <stdlib.h>
9
+ #include <string.h>
10
+
11
+ #define RC_RUNTIME_MARKER 0x0A504152 /* RAP\n */
12
+
13
+ #define RC_RUNTIME_CHUNK_MEMREFS 0x4645524D /* MREF */
14
+ #define RC_RUNTIME_CHUNK_VARIABLES 0x53524156 /* VARS */
15
+ #define RC_RUNTIME_CHUNK_ACHIEVEMENT 0x56484341 /* ACHV */
16
+ #define RC_RUNTIME_CHUNK_LEADERBOARD 0x4452424C /* LBRD */
17
+ #define RC_RUNTIME_CHUNK_RICHPRESENCE 0x48434952 /* RICH */
18
+
19
+ #define RC_RUNTIME_CHUNK_DONE 0x454E4F44 /* DONE */
20
+
21
+ #define RC_RUNTIME_MIN_BUFFER_SIZE 4 + 8 + 16 /* RUNTIME_MARKER, CHUNK_DONE, MD5 */
22
+
23
+ typedef struct rc_runtime_progress_t {
24
+ const rc_runtime_t* runtime;
25
+
26
+ uint32_t offset;
27
+ uint8_t* buffer;
28
+ uint32_t buffer_size;
29
+
30
+ uint32_t chunk_size_offset;
31
+ } rc_runtime_progress_t;
32
+
33
+ #define assert_chunk_size(expected_size) assert((uint32_t)(progress->offset - progress->chunk_size_offset - 4) == (uint32_t)(expected_size))
34
+
35
+ #define RC_TRIGGER_STATE_UNUPDATED 0x7F
36
+
37
+ #define RC_MEMREF_FLAG_CHANGED_THIS_FRAME 0x00010000
38
+
39
+ #define RC_VAR_FLAG_HAS_COND_DATA 0x01000000
40
+
41
+ #define RC_COND_FLAG_IS_TRUE_MASK 0x00000003
42
+ #define RC_COND_FLAG_OPERAND1_IS_INDIRECT_MEMREF 0x00010000
43
+ #define RC_COND_FLAG_OPERAND1_MEMREF_CHANGED_THIS_FRAME 0x00020000
44
+ #define RC_COND_FLAG_OPERAND2_IS_INDIRECT_MEMREF 0x00100000
45
+ #define RC_COND_FLAG_OPERAND2_MEMREF_CHANGED_THIS_FRAME 0x00200000
46
+
47
+ static void rc_runtime_progress_write_uint(rc_runtime_progress_t* progress, uint32_t value)
48
+ {
49
+ if (progress->buffer) {
50
+ progress->buffer[progress->offset + 0] = value & 0xFF; value >>= 8;
51
+ progress->buffer[progress->offset + 1] = value & 0xFF; value >>= 8;
52
+ progress->buffer[progress->offset + 2] = value & 0xFF; value >>= 8;
53
+ progress->buffer[progress->offset + 3] = value & 0xFF;
54
+ }
55
+
56
+ progress->offset += 4;
57
+ }
58
+
59
+ static uint32_t rc_runtime_progress_read_uint(rc_runtime_progress_t* progress)
60
+ {
61
+ uint32_t value = progress->buffer[progress->offset + 0] |
62
+ (progress->buffer[progress->offset + 1] << 8) |
63
+ (progress->buffer[progress->offset + 2] << 16) |
64
+ (progress->buffer[progress->offset + 3] << 24);
65
+
66
+ progress->offset += 4;
67
+ return value;
68
+ }
69
+
70
+ static void rc_runtime_progress_write_md5(rc_runtime_progress_t* progress, uint8_t* md5)
71
+ {
72
+ if (progress->buffer)
73
+ memcpy(&progress->buffer[progress->offset], md5, 16);
74
+
75
+ progress->offset += 16;
76
+ }
77
+
78
+ static int rc_runtime_progress_match_md5(rc_runtime_progress_t* progress, uint8_t* md5)
79
+ {
80
+ int result = 0;
81
+ if (progress->buffer)
82
+ result = (memcmp(&progress->buffer[progress->offset], md5, 16) == 0);
83
+
84
+ progress->offset += 16;
85
+
86
+ return result;
87
+ }
88
+
89
+ static void rc_runtime_progress_start_chunk(rc_runtime_progress_t* progress, uint32_t chunk_id)
90
+ {
91
+ rc_runtime_progress_write_uint(progress, chunk_id);
92
+
93
+ progress->chunk_size_offset = progress->offset;
94
+
95
+ progress->offset += 4;
96
+ }
97
+
98
+ static void rc_runtime_progress_end_chunk(rc_runtime_progress_t* progress)
99
+ {
100
+ uint32_t length;
101
+ uint32_t offset;
102
+
103
+ progress->offset = (progress->offset + 3) & ~0x03; /* align to 4 byte boundary */
104
+
105
+ if (progress->buffer) {
106
+ /* ignore chunk size field when calculating chunk size */
107
+ length = (uint32_t)(progress->offset - progress->chunk_size_offset - 4);
108
+
109
+ /* temporarily update the write pointer to write the chunk size field */
110
+ offset = progress->offset;
111
+ progress->offset = progress->chunk_size_offset;
112
+ rc_runtime_progress_write_uint(progress, length);
113
+ progress->offset = offset;
114
+ }
115
+ }
116
+
117
+ static void rc_runtime_progress_init(rc_runtime_progress_t* progress, const rc_runtime_t* runtime)
118
+ {
119
+ memset(progress, 0, sizeof(rc_runtime_progress_t));
120
+ progress->runtime = runtime;
121
+ }
122
+
123
+ #define RC_RUNTIME_SERIALIZED_MEMREF_SIZE 16 /* 4x uint: address, flags, value, prior */
124
+
125
+ static int rc_runtime_progress_write_memrefs(rc_runtime_progress_t* progress)
126
+ {
127
+ uint32_t count = rc_memrefs_count_memrefs(progress->runtime->memrefs);
128
+ if (count == 0)
129
+ return RC_OK;
130
+
131
+ if (progress->offset + 8 + count * RC_RUNTIME_SERIALIZED_MEMREF_SIZE > progress->buffer_size)
132
+ return RC_INSUFFICIENT_BUFFER;
133
+
134
+ rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_MEMREFS);
135
+
136
+ if (!progress->buffer) {
137
+ progress->offset += count * RC_RUNTIME_SERIALIZED_MEMREF_SIZE;
138
+ }
139
+ else {
140
+ uint32_t flags = 0;
141
+ const rc_memref_list_t* memref_list = &progress->runtime->memrefs->memrefs;
142
+ const rc_memref_t* memref;
143
+
144
+ for (; memref_list; memref_list = memref_list->next) {
145
+ const rc_memref_t* memref_end;
146
+
147
+ memref = memref_list->items;
148
+ memref_end = memref + memref_list->count;
149
+ for (; memref < memref_end; ++memref) {
150
+ flags = memref->value.size;
151
+ if (memref->value.changed)
152
+ flags |= RC_MEMREF_FLAG_CHANGED_THIS_FRAME;
153
+
154
+ rc_runtime_progress_write_uint(progress, memref->address);
155
+ rc_runtime_progress_write_uint(progress, flags);
156
+ rc_runtime_progress_write_uint(progress, memref->value.value);
157
+ rc_runtime_progress_write_uint(progress, memref->value.prior);
158
+ }
159
+ }
160
+ }
161
+
162
+ assert_chunk_size(count * RC_RUNTIME_SERIALIZED_MEMREF_SIZE);
163
+ rc_runtime_progress_end_chunk(progress);
164
+ return RC_OK;
165
+ }
166
+
167
+ static void rc_runtime_progress_update_modified_memrefs(rc_runtime_progress_t* progress)
168
+ {
169
+ rc_typed_value_t value, prior_value, modifier, prior_modifier;
170
+ rc_modified_memref_list_t* modified_memref_list;
171
+ rc_modified_memref_t* modified_memref;
172
+ rc_operand_t prior_parent_operand, prior_modifier_operand;
173
+ rc_memref_t prior_parent_memref, prior_modifier_memref;
174
+
175
+ modified_memref_list = &progress->runtime->memrefs->modified_memrefs;
176
+ for (; modified_memref_list; modified_memref_list = modified_memref_list->next) {
177
+ const rc_modified_memref_t* modified_memref_end;
178
+ modified_memref = modified_memref_list->items;
179
+ modified_memref_end = modified_memref + modified_memref_list->count;
180
+ for (; modified_memref < modified_memref_end; ++modified_memref) {
181
+ modified_memref->memref.value.changed = 0;
182
+
183
+ /* indirect memref values are stored in conditions */
184
+ if (modified_memref->modifier_type == RC_OPERATOR_INDIRECT_READ)
185
+ continue;
186
+
187
+ /* non-indirect memref values can be reconstructed from the parents */
188
+ memcpy(&prior_parent_operand, &modified_memref->parent, sizeof(prior_parent_operand));
189
+ if (rc_operand_is_memref(&prior_parent_operand)) {
190
+ memcpy(&prior_parent_memref, modified_memref->parent.value.memref, sizeof(prior_parent_memref));
191
+ prior_parent_memref.value.value = prior_parent_memref.value.prior;
192
+ modified_memref->memref.value.changed |= prior_parent_memref.value.changed;
193
+ prior_parent_operand.value.memref = &prior_parent_memref;
194
+ }
195
+
196
+ memcpy(&prior_modifier_operand, &modified_memref->modifier, sizeof(prior_modifier_operand));
197
+ if (rc_operand_is_memref(&prior_modifier_operand)) {
198
+ memcpy(&prior_modifier_memref, modified_memref->modifier.value.memref, sizeof(prior_modifier_memref));
199
+ prior_modifier_memref.value.value = prior_modifier_memref.value.prior;
200
+ modified_memref->memref.value.changed |= prior_modifier_memref.value.changed;
201
+ prior_modifier_operand.value.memref = &prior_modifier_memref;
202
+ }
203
+
204
+ rc_evaluate_operand(&value, &modified_memref->parent, NULL);
205
+ rc_evaluate_operand(&modifier, &modified_memref->modifier, NULL);
206
+ rc_evaluate_operand(&prior_value, &prior_parent_operand, NULL);
207
+ rc_evaluate_operand(&prior_modifier, &prior_modifier_operand, NULL);
208
+
209
+ if (modified_memref->modifier_type == RC_OPERATOR_SUB_PARENT) {
210
+ rc_typed_value_negate(&value);
211
+ rc_typed_value_add(&value, &modifier);
212
+
213
+ rc_typed_value_negate(&prior_value);
214
+ rc_typed_value_add(&prior_value, &prior_modifier);
215
+ }
216
+ else {
217
+ rc_typed_value_combine(&value, &modifier, modified_memref->modifier_type);
218
+ rc_typed_value_combine(&prior_value, &prior_modifier, modified_memref->modifier_type);
219
+ }
220
+
221
+ rc_typed_value_convert(&value, modified_memref->memref.value.type);
222
+ modified_memref->memref.value.value = value.value.u32;
223
+
224
+ rc_typed_value_convert(&prior_value, modified_memref->memref.value.type);
225
+ modified_memref->memref.value.prior = prior_value.value.u32;
226
+ }
227
+ }
228
+ }
229
+
230
+ static int rc_runtime_progress_read_memrefs(rc_runtime_progress_t* progress)
231
+ {
232
+ uint32_t entries;
233
+ uint32_t address, flags, value, prior;
234
+ uint8_t size;
235
+ rc_memref_list_t* unmatched_memref_list = &progress->runtime->memrefs->memrefs;
236
+ rc_memref_t* first_unmatched_memref = unmatched_memref_list->items;
237
+ rc_memref_t* memref;
238
+
239
+ /* re-read the chunk size to determine how many memrefs are present */
240
+ progress->offset -= 4;
241
+ entries = rc_runtime_progress_read_uint(progress) / RC_RUNTIME_SERIALIZED_MEMREF_SIZE;
242
+
243
+ while (entries != 0) {
244
+ address = rc_runtime_progress_read_uint(progress);
245
+ flags = rc_runtime_progress_read_uint(progress);
246
+ value = rc_runtime_progress_read_uint(progress);
247
+ prior = rc_runtime_progress_read_uint(progress);
248
+
249
+ size = flags & 0xFF;
250
+
251
+ memref = first_unmatched_memref;
252
+ if (memref->address == address && memref->value.size == size) {
253
+ memref->value.value = value;
254
+ memref->value.changed = (flags & RC_MEMREF_FLAG_CHANGED_THIS_FRAME) ? 1 : 0;
255
+ memref->value.prior = prior;
256
+
257
+ first_unmatched_memref++;
258
+ if (first_unmatched_memref >= unmatched_memref_list->items + unmatched_memref_list->count) {
259
+ unmatched_memref_list = unmatched_memref_list->next;
260
+ if (!unmatched_memref_list)
261
+ break;
262
+ first_unmatched_memref = unmatched_memref_list->items;
263
+ }
264
+ }
265
+ else {
266
+ rc_memref_list_t* memref_list = unmatched_memref_list;
267
+ do {
268
+ ++memref;
269
+ if (memref >= memref_list->items + memref_list->count) {
270
+ memref_list = memref_list->next;
271
+ if (!memref_list)
272
+ break;
273
+
274
+ memref = memref_list->items;
275
+ }
276
+
277
+ if (memref->address == address && memref->value.size == size) {
278
+ memref->value.value = value;
279
+ memref->value.changed = (flags & RC_MEMREF_FLAG_CHANGED_THIS_FRAME) ? 1 : 0;
280
+ memref->value.prior = prior;
281
+ break;
282
+ }
283
+
284
+ } while (1);
285
+ }
286
+
287
+ --entries;
288
+ }
289
+
290
+ rc_runtime_progress_update_modified_memrefs(progress);
291
+
292
+ return RC_OK;
293
+ }
294
+
295
+ static int rc_runtime_progress_is_indirect_memref(rc_operand_t* oper)
296
+ {
297
+ switch (oper->type)
298
+ {
299
+ case RC_OPERAND_CONST:
300
+ case RC_OPERAND_FP:
301
+ case RC_OPERAND_RECALL:
302
+ case RC_OPERAND_FUNC:
303
+ return 0;
304
+
305
+ default:
306
+ if (oper->value.memref->value.memref_type != RC_MEMREF_TYPE_MODIFIED_MEMREF)
307
+ return 0;
308
+
309
+ return ((const rc_modified_memref_t*)oper->value.memref)->modifier_type == RC_OPERATOR_INDIRECT_READ;
310
+ }
311
+ }
312
+
313
+ static int rc_runtime_progress_write_condset(rc_runtime_progress_t* progress, rc_condset_t* condset)
314
+ {
315
+ rc_condition_t* cond;
316
+ uint32_t flags;
317
+
318
+ if (progress->offset + 4 > progress->buffer_size)
319
+ return RC_INSUFFICIENT_BUFFER;
320
+
321
+ rc_runtime_progress_write_uint(progress, condset->is_paused);
322
+
323
+ cond = condset->conditions;
324
+ while (cond) {
325
+ flags = (cond->is_true & RC_COND_FLAG_IS_TRUE_MASK);
326
+
327
+ if (rc_runtime_progress_is_indirect_memref(&cond->operand1)) {
328
+ flags |= RC_COND_FLAG_OPERAND1_IS_INDIRECT_MEMREF;
329
+ if (cond->operand1.value.memref->value.changed)
330
+ flags |= RC_COND_FLAG_OPERAND1_MEMREF_CHANGED_THIS_FRAME;
331
+ }
332
+
333
+ if (rc_runtime_progress_is_indirect_memref(&cond->operand2)) {
334
+ flags |= RC_COND_FLAG_OPERAND2_IS_INDIRECT_MEMREF;
335
+ if (cond->operand2.value.memref->value.changed)
336
+ flags |= RC_COND_FLAG_OPERAND2_MEMREF_CHANGED_THIS_FRAME;
337
+ }
338
+
339
+ if (progress->offset + 8 > progress->buffer_size)
340
+ return RC_INSUFFICIENT_BUFFER;
341
+
342
+ rc_runtime_progress_write_uint(progress, cond->current_hits);
343
+ rc_runtime_progress_write_uint(progress, flags);
344
+
345
+ if (flags & RC_COND_FLAG_OPERAND1_IS_INDIRECT_MEMREF) {
346
+ if (progress->offset + 8 > progress->buffer_size)
347
+ return RC_INSUFFICIENT_BUFFER;
348
+
349
+ rc_runtime_progress_write_uint(progress, cond->operand1.value.memref->value.value);
350
+ rc_runtime_progress_write_uint(progress, cond->operand1.value.memref->value.prior);
351
+ }
352
+
353
+ if (flags & RC_COND_FLAG_OPERAND2_IS_INDIRECT_MEMREF) {
354
+ if (progress->offset + 8 > progress->buffer_size)
355
+ return RC_INSUFFICIENT_BUFFER;
356
+
357
+ rc_runtime_progress_write_uint(progress, cond->operand2.value.memref->value.value);
358
+ rc_runtime_progress_write_uint(progress, cond->operand2.value.memref->value.prior);
359
+ }
360
+
361
+ cond = cond->next;
362
+ }
363
+
364
+ return RC_OK;
365
+ }
366
+
367
+ static int rc_runtime_progress_read_condset(rc_runtime_progress_t* progress, rc_condset_t* condset)
368
+ {
369
+ rc_condition_t* cond;
370
+ uint32_t flags;
371
+
372
+ condset->is_paused = (char)rc_runtime_progress_read_uint(progress);
373
+
374
+ cond = condset->conditions;
375
+ while (cond) {
376
+ cond->current_hits = rc_runtime_progress_read_uint(progress);
377
+ flags = rc_runtime_progress_read_uint(progress);
378
+
379
+ cond->is_true = (flags & RC_COND_FLAG_IS_TRUE_MASK);
380
+
381
+ if (flags & RC_COND_FLAG_OPERAND1_IS_INDIRECT_MEMREF) {
382
+ if (!rc_operand_is_memref(&cond->operand1)) /* this should never happen, but better safe than sorry */
383
+ return RC_INVALID_STATE;
384
+
385
+ cond->operand1.value.memref->value.value = rc_runtime_progress_read_uint(progress);
386
+ cond->operand1.value.memref->value.prior = rc_runtime_progress_read_uint(progress);
387
+ cond->operand1.value.memref->value.changed = (flags & RC_COND_FLAG_OPERAND1_MEMREF_CHANGED_THIS_FRAME) ? 1 : 0;
388
+ }
389
+
390
+ if (flags & RC_COND_FLAG_OPERAND2_IS_INDIRECT_MEMREF) {
391
+ if (!rc_operand_is_memref(&cond->operand2)) /* this should never happen, but better safe than sorry */
392
+ return RC_INVALID_STATE;
393
+
394
+ cond->operand2.value.memref->value.value = rc_runtime_progress_read_uint(progress);
395
+ cond->operand2.value.memref->value.prior = rc_runtime_progress_read_uint(progress);
396
+ cond->operand2.value.memref->value.changed = (flags & RC_COND_FLAG_OPERAND2_MEMREF_CHANGED_THIS_FRAME) ? 1 : 0;
397
+ }
398
+
399
+ cond = cond->next;
400
+ }
401
+
402
+ return RC_OK;
403
+ }
404
+
405
+ static uint32_t rc_runtime_progress_should_serialize_variable_condset(const rc_condset_t* conditions)
406
+ {
407
+ const rc_condition_t* condition;
408
+
409
+ /* predetermined presence of pause flag - must serialize */
410
+ if (conditions->has_pause)
411
+ return RC_VAR_FLAG_HAS_COND_DATA;
412
+
413
+ /* if any conditions has required hits, must serialize */
414
+ /* ASSERT: Measured with comparison and no explicit target will set hit target to 0xFFFFFFFF */
415
+ for (condition = conditions->conditions; condition; condition = condition->next) {
416
+ if (condition->required_hits > 0)
417
+ return RC_VAR_FLAG_HAS_COND_DATA;
418
+ }
419
+
420
+ /* can safely be reset without affecting behavior */
421
+ return 0;
422
+ }
423
+
424
+ static int rc_runtime_progress_write_variable(rc_runtime_progress_t* progress, const rc_value_t* variable)
425
+ {
426
+ uint32_t flags;
427
+
428
+ if (progress->offset + 12 > progress->buffer_size)
429
+ return RC_INSUFFICIENT_BUFFER;
430
+
431
+ flags = rc_runtime_progress_should_serialize_variable_condset(variable->conditions);
432
+ if (variable->value.changed)
433
+ flags |= RC_MEMREF_FLAG_CHANGED_THIS_FRAME;
434
+
435
+ rc_runtime_progress_write_uint(progress, flags);
436
+ rc_runtime_progress_write_uint(progress, variable->value.value);
437
+ rc_runtime_progress_write_uint(progress, variable->value.prior);
438
+
439
+ if (flags & RC_VAR_FLAG_HAS_COND_DATA) {
440
+ int result = rc_runtime_progress_write_condset(progress, variable->conditions);
441
+ if (result != RC_OK)
442
+ return result;
443
+ }
444
+
445
+ return RC_OK;
446
+ }
447
+
448
+ static int rc_runtime_progress_write_variables(rc_runtime_progress_t* progress)
449
+ {
450
+ uint32_t count;
451
+ const rc_value_t* value;
452
+ int result;
453
+
454
+ if (!progress->runtime->richpresence || !progress->runtime->richpresence->richpresence)
455
+ return RC_OK;
456
+
457
+ value = progress->runtime->richpresence->richpresence->values;
458
+ count = rc_count_values(value);
459
+ if (count == 0)
460
+ return RC_OK;
461
+
462
+ /* header + count + count(djb2,flags,value,prior,?cond) */
463
+ if (progress->offset + 8 + 4 + count * 16 > progress->buffer_size)
464
+ return RC_INSUFFICIENT_BUFFER;
465
+
466
+ rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_VARIABLES);
467
+ rc_runtime_progress_write_uint(progress, count);
468
+
469
+ for (; value; value = value->next) {
470
+ const uint32_t djb2 = rc_djb2(value->name);
471
+ if (progress->offset + 16 > progress->buffer_size)
472
+ return RC_INSUFFICIENT_BUFFER;
473
+
474
+ rc_runtime_progress_write_uint(progress, djb2);
475
+
476
+ result = rc_runtime_progress_write_variable(progress, value);
477
+ if (result != RC_OK)
478
+ return result;
479
+ }
480
+
481
+ rc_runtime_progress_end_chunk(progress);
482
+ return RC_OK;
483
+ }
484
+
485
+ static int rc_runtime_progress_read_variable(rc_runtime_progress_t* progress, rc_value_t* variable)
486
+ {
487
+ uint32_t flags = rc_runtime_progress_read_uint(progress);
488
+ variable->value.changed = (flags & RC_MEMREF_FLAG_CHANGED_THIS_FRAME) ? 1 : 0;
489
+ variable->value.value = rc_runtime_progress_read_uint(progress);
490
+ variable->value.prior = rc_runtime_progress_read_uint(progress);
491
+
492
+ if (flags & RC_VAR_FLAG_HAS_COND_DATA) {
493
+ int result = rc_runtime_progress_read_condset(progress, variable->conditions);
494
+ if (result != RC_OK)
495
+ return result;
496
+ }
497
+ else {
498
+ rc_reset_condset(variable->conditions);
499
+ }
500
+
501
+ return RC_OK;
502
+ }
503
+
504
+ static int rc_runtime_progress_read_variables(rc_runtime_progress_t* progress)
505
+ {
506
+ struct rc_pending_value_t
507
+ {
508
+ rc_value_t* variable;
509
+ uint32_t djb2;
510
+ };
511
+ struct rc_pending_value_t local_pending_variables[32];
512
+ struct rc_pending_value_t* pending_variables;
513
+ rc_value_t* value;
514
+ uint32_t count, serialized_count;
515
+ int result;
516
+ int32_t i;
517
+
518
+ serialized_count = rc_runtime_progress_read_uint(progress);
519
+ if (serialized_count == 0)
520
+ return RC_OK;
521
+
522
+ if (!progress->runtime->richpresence || !progress->runtime->richpresence->richpresence)
523
+ return RC_OK;
524
+
525
+ value = progress->runtime->richpresence->richpresence->values;
526
+ count = rc_count_values(value);
527
+ if (count == 0)
528
+ return RC_OK;
529
+
530
+ if (count <= sizeof(local_pending_variables) / sizeof(local_pending_variables[0])) {
531
+ pending_variables = local_pending_variables;
532
+ }
533
+ else {
534
+ pending_variables = (struct rc_pending_value_t*)malloc(count * sizeof(struct rc_pending_value_t));
535
+ if (pending_variables == NULL)
536
+ return RC_OUT_OF_MEMORY;
537
+ }
538
+
539
+ i = (int32_t)count;
540
+ for (; value; value = value->next) {
541
+ --i;
542
+ pending_variables[i].variable = value;
543
+ pending_variables[i].djb2 = rc_djb2(value->name);
544
+ }
545
+
546
+ result = RC_OK;
547
+ for (; serialized_count > 0 && result == RC_OK; --serialized_count) {
548
+ uint32_t djb2 = rc_runtime_progress_read_uint(progress);
549
+ for (i = (int32_t)count - 1; i >= 0; --i) {
550
+ if (pending_variables[i].djb2 == djb2) {
551
+ value = pending_variables[i].variable;
552
+ result = rc_runtime_progress_read_variable(progress, value);
553
+ if (result == RC_OK) {
554
+ if (i < (int32_t)count - 1)
555
+ memcpy(&pending_variables[i], &pending_variables[count - 1], sizeof(struct rc_pending_value_t));
556
+ count--;
557
+ }
558
+ break;
559
+ }
560
+ }
561
+ }
562
+
563
+ /* VS raises a C6385 warning here because it thinks count can exceed the size of the local_pending_variables array.
564
+ * When count is larger, pending_variables points to allocated memory, so the warning is wrong. */
565
+ #if defined (_MSC_VER)
566
+ #pragma warning(push)
567
+ #pragma warning(disable:6385)
568
+ #endif
569
+ while (count > 0)
570
+ rc_reset_value(pending_variables[--count].variable);
571
+ #if defined (_MSC_VER)
572
+ #pragma warning(pop)
573
+ #endif
574
+
575
+ if (pending_variables != local_pending_variables)
576
+ free(pending_variables);
577
+
578
+ return result;
579
+ }
580
+
581
+ static int rc_runtime_progress_write_trigger(rc_runtime_progress_t* progress, const rc_trigger_t* trigger)
582
+ {
583
+ rc_condset_t* condset;
584
+ int result;
585
+
586
+ rc_runtime_progress_write_uint(progress, trigger->state);
587
+ rc_runtime_progress_write_uint(progress, trigger->measured_value);
588
+
589
+ if (trigger->requirement) {
590
+ result = rc_runtime_progress_write_condset(progress, trigger->requirement);
591
+ if (result != RC_OK)
592
+ return result;
593
+ }
594
+
595
+ condset = trigger->alternative;
596
+ while (condset) {
597
+ result = rc_runtime_progress_write_condset(progress, condset);
598
+ if (result != RC_OK)
599
+ return result;
600
+
601
+ condset = condset->next;
602
+ }
603
+
604
+ return RC_OK;
605
+ }
606
+
607
+ static int rc_runtime_progress_read_trigger(rc_runtime_progress_t* progress, rc_trigger_t* trigger)
608
+ {
609
+ rc_condset_t* condset;
610
+ int result;
611
+
612
+ trigger->state = (char)rc_runtime_progress_read_uint(progress);
613
+ trigger->measured_value = rc_runtime_progress_read_uint(progress);
614
+
615
+ if (trigger->requirement) {
616
+ result = rc_runtime_progress_read_condset(progress, trigger->requirement);
617
+ if (result != RC_OK)
618
+ return result;
619
+ }
620
+
621
+ condset = trigger->alternative;
622
+ while (condset) {
623
+ result = rc_runtime_progress_read_condset(progress, condset);
624
+ if (result != RC_OK)
625
+ return result;
626
+
627
+ condset = condset->next;
628
+ }
629
+
630
+ return RC_OK;
631
+ }
632
+
633
+ static int rc_runtime_progress_write_achievements(rc_runtime_progress_t* progress)
634
+ {
635
+ uint32_t i;
636
+ int initial_offset = 0;
637
+ int result;
638
+
639
+ for (i = 0; i < progress->runtime->trigger_count; ++i) {
640
+ rc_runtime_trigger_t* runtime_trigger = &progress->runtime->triggers[i];
641
+ if (!runtime_trigger->trigger)
642
+ continue;
643
+
644
+ /* don't store state for inactive or triggered achievements */
645
+ if (!rc_trigger_state_active(runtime_trigger->trigger->state))
646
+ continue;
647
+
648
+ if (!progress->buffer) {
649
+ if (runtime_trigger->serialized_size) {
650
+ progress->offset += runtime_trigger->serialized_size;
651
+ continue;
652
+ }
653
+
654
+ initial_offset = progress->offset;
655
+ } else {
656
+ if (progress->offset + runtime_trigger->serialized_size > progress->buffer_size)
657
+ return RC_INSUFFICIENT_BUFFER;
658
+ }
659
+
660
+ rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_ACHIEVEMENT);
661
+ rc_runtime_progress_write_uint(progress, runtime_trigger->id);
662
+ rc_runtime_progress_write_md5(progress, runtime_trigger->md5);
663
+
664
+ result = rc_runtime_progress_write_trigger(progress, runtime_trigger->trigger);
665
+ if (result != RC_OK)
666
+ return result;
667
+
668
+ if (runtime_trigger->serialized_size) {
669
+ /* runtime_trigger->serialized_size includes the header */
670
+ assert_chunk_size(runtime_trigger->serialized_size - 8);
671
+ }
672
+
673
+ rc_runtime_progress_end_chunk(progress);
674
+
675
+ if (!progress->buffer)
676
+ runtime_trigger->serialized_size = progress->offset - initial_offset;
677
+ }
678
+
679
+ return RC_OK;
680
+ }
681
+
682
+ static int rc_runtime_progress_read_achievement(rc_runtime_progress_t* progress)
683
+ {
684
+ uint32_t id = rc_runtime_progress_read_uint(progress);
685
+ uint32_t i;
686
+
687
+ for (i = 0; i < progress->runtime->trigger_count; ++i) {
688
+ rc_runtime_trigger_t* runtime_trigger = &progress->runtime->triggers[i];
689
+ if (runtime_trigger->id == id && runtime_trigger->trigger != NULL) {
690
+ /* ignore triggered and waiting achievements */
691
+ if (runtime_trigger->trigger->state == RC_TRIGGER_STATE_UNUPDATED) {
692
+ /* only update state if definition hasn't changed (md5 matches) */
693
+ if (rc_runtime_progress_match_md5(progress, runtime_trigger->md5))
694
+ return rc_runtime_progress_read_trigger(progress, runtime_trigger->trigger);
695
+ break;
696
+ }
697
+ }
698
+ }
699
+
700
+ return RC_OK;
701
+ }
702
+
703
+ static int rc_runtime_progress_write_leaderboards(rc_runtime_progress_t* progress)
704
+ {
705
+ uint32_t i;
706
+ uint32_t flags;
707
+ int initial_offset = 0;
708
+ int result;
709
+
710
+ for (i = 0; i < progress->runtime->lboard_count; ++i) {
711
+ rc_runtime_lboard_t* runtime_lboard = &progress->runtime->lboards[i];
712
+ if (!runtime_lboard->lboard)
713
+ continue;
714
+
715
+ /* don't store state for inactive leaderboards */
716
+ if (!rc_lboard_state_active(runtime_lboard->lboard->state))
717
+ continue;
718
+
719
+ if (!progress->buffer) {
720
+ if (runtime_lboard->serialized_size) {
721
+ progress->offset += runtime_lboard->serialized_size;
722
+ continue;
723
+ }
724
+
725
+ initial_offset = progress->offset;
726
+ } else {
727
+ if (progress->offset + runtime_lboard->serialized_size > progress->buffer_size)
728
+ return RC_INSUFFICIENT_BUFFER;
729
+ }
730
+
731
+ rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_LEADERBOARD);
732
+ rc_runtime_progress_write_uint(progress, runtime_lboard->id);
733
+ rc_runtime_progress_write_md5(progress, runtime_lboard->md5);
734
+
735
+ flags = runtime_lboard->lboard->state;
736
+ rc_runtime_progress_write_uint(progress, flags);
737
+
738
+ result = rc_runtime_progress_write_trigger(progress, &runtime_lboard->lboard->start);
739
+ if (result != RC_OK)
740
+ return result;
741
+
742
+ result = rc_runtime_progress_write_trigger(progress, &runtime_lboard->lboard->submit);
743
+ if (result != RC_OK)
744
+ return result;
745
+
746
+ result = rc_runtime_progress_write_trigger(progress, &runtime_lboard->lboard->cancel);
747
+ if (result != RC_OK)
748
+ return result;
749
+
750
+ result = rc_runtime_progress_write_variable(progress, &runtime_lboard->lboard->value);
751
+ if (result != RC_OK)
752
+ return result;
753
+
754
+ if (runtime_lboard->serialized_size) {
755
+ /* runtime_lboard->serialized_size includes the header */
756
+ assert_chunk_size(runtime_lboard->serialized_size - 8);
757
+ }
758
+
759
+ rc_runtime_progress_end_chunk(progress);
760
+
761
+ if (!progress->buffer)
762
+ runtime_lboard->serialized_size = progress->offset - initial_offset;
763
+ }
764
+
765
+ return RC_OK;
766
+ }
767
+
768
+ static int rc_runtime_progress_read_leaderboard(rc_runtime_progress_t* progress)
769
+ {
770
+ uint32_t id = rc_runtime_progress_read_uint(progress);
771
+ uint32_t i;
772
+ int result;
773
+
774
+ for (i = 0; i < progress->runtime->lboard_count; ++i) {
775
+ rc_runtime_lboard_t* runtime_lboard = &progress->runtime->lboards[i];
776
+ if (runtime_lboard->id == id && runtime_lboard->lboard != NULL) {
777
+ /* ignore triggered and waiting achievements */
778
+ if (runtime_lboard->lboard->state == RC_TRIGGER_STATE_UNUPDATED) {
779
+ /* only update state if definition hasn't changed (md5 matches) */
780
+ if (rc_runtime_progress_match_md5(progress, runtime_lboard->md5)) {
781
+ uint32_t flags = rc_runtime_progress_read_uint(progress);
782
+
783
+ result = rc_runtime_progress_read_trigger(progress, &runtime_lboard->lboard->start);
784
+ if (result != RC_OK)
785
+ return result;
786
+
787
+ result = rc_runtime_progress_read_trigger(progress, &runtime_lboard->lboard->submit);
788
+ if (result != RC_OK)
789
+ return result;
790
+
791
+ result = rc_runtime_progress_read_trigger(progress, &runtime_lboard->lboard->cancel);
792
+ if (result != RC_OK)
793
+ return result;
794
+
795
+ result = rc_runtime_progress_read_variable(progress, &runtime_lboard->lboard->value);
796
+ if (result != RC_OK)
797
+ return result;
798
+
799
+ runtime_lboard->lboard->state = (char)(flags & 0x7F);
800
+ }
801
+ break;
802
+ }
803
+ }
804
+ }
805
+
806
+ return RC_OK;
807
+ }
808
+
809
+ static int rc_runtime_progress_write_rich_presence(rc_runtime_progress_t* progress)
810
+ {
811
+ const rc_richpresence_display_t* display;
812
+ int result;
813
+
814
+ if (!progress->runtime->richpresence || !progress->runtime->richpresence->richpresence)
815
+ return RC_OK;
816
+
817
+ /* if there are no conditional display strings, there's nothing to capture */
818
+ display = progress->runtime->richpresence->richpresence->first_display;
819
+ if (!display->next)
820
+ return RC_OK;
821
+
822
+ if (progress->offset + 8 + 16 > progress->buffer_size)
823
+ return RC_INSUFFICIENT_BUFFER;
824
+
825
+ rc_runtime_progress_start_chunk(progress, RC_RUNTIME_CHUNK_RICHPRESENCE);
826
+ rc_runtime_progress_write_md5(progress, progress->runtime->richpresence->md5);
827
+
828
+ for (; display->next; display = display->next) {
829
+ result = rc_runtime_progress_write_trigger(progress, &display->trigger);
830
+ if (result != RC_OK)
831
+ return result;
832
+ }
833
+
834
+ rc_runtime_progress_end_chunk(progress);
835
+ return RC_OK;
836
+ }
837
+
838
+ static int rc_runtime_progress_read_rich_presence(rc_runtime_progress_t* progress)
839
+ {
840
+ rc_richpresence_display_t* display;
841
+ int result;
842
+
843
+ if (!progress->runtime->richpresence || !progress->runtime->richpresence->richpresence)
844
+ return RC_OK;
845
+
846
+ if (!rc_runtime_progress_match_md5(progress, progress->runtime->richpresence->md5)) {
847
+ rc_reset_richpresence_triggers(progress->runtime->richpresence->richpresence);
848
+ return RC_OK;
849
+ }
850
+
851
+ display = progress->runtime->richpresence->richpresence->first_display;
852
+ for (; display->next; display = display->next) {
853
+ result = rc_runtime_progress_read_trigger(progress, &display->trigger);
854
+ if (result != RC_OK)
855
+ return result;
856
+ }
857
+
858
+ return RC_OK;
859
+ }
860
+
861
+ static int rc_runtime_progress_serialize_internal(rc_runtime_progress_t* progress)
862
+ {
863
+ md5_state_t state;
864
+ uint8_t md5[16];
865
+ int result;
866
+
867
+ if (progress->buffer_size < RC_RUNTIME_MIN_BUFFER_SIZE)
868
+ return RC_INSUFFICIENT_BUFFER;
869
+
870
+ rc_runtime_progress_write_uint(progress, RC_RUNTIME_MARKER);
871
+
872
+ if ((result = rc_runtime_progress_write_memrefs(progress)) != RC_OK)
873
+ return result;
874
+
875
+ if ((result = rc_runtime_progress_write_variables(progress)) != RC_OK)
876
+ return result;
877
+
878
+ if ((result = rc_runtime_progress_write_achievements(progress)) != RC_OK)
879
+ return result;
880
+
881
+ if ((result = rc_runtime_progress_write_leaderboards(progress)) != RC_OK)
882
+ return result;
883
+
884
+ if ((result = rc_runtime_progress_write_rich_presence(progress)) != RC_OK)
885
+ return result;
886
+
887
+ if (progress->offset + 8 + 16 > progress->buffer_size)
888
+ return RC_INSUFFICIENT_BUFFER;
889
+
890
+ rc_runtime_progress_write_uint(progress, RC_RUNTIME_CHUNK_DONE);
891
+ rc_runtime_progress_write_uint(progress, 16);
892
+
893
+ if (progress->buffer) {
894
+ md5_init(&state);
895
+ md5_append(&state, progress->buffer, progress->offset);
896
+ md5_finish(&state, md5);
897
+ }
898
+
899
+ rc_runtime_progress_write_md5(progress, md5);
900
+
901
+ return RC_OK;
902
+ }
903
+
904
+ uint32_t rc_runtime_progress_size(const rc_runtime_t* runtime, void* unused_L)
905
+ {
906
+ rc_runtime_progress_t progress;
907
+ int result;
908
+
909
+ (void)unused_L;
910
+
911
+ rc_runtime_progress_init(&progress, runtime);
912
+ progress.buffer_size = 0xFFFFFFFF;
913
+
914
+ result = rc_runtime_progress_serialize_internal(&progress);
915
+ if (result != RC_OK)
916
+ return 0;
917
+
918
+ return progress.offset;
919
+ }
920
+
921
+ int rc_runtime_serialize_progress(void* buffer, const rc_runtime_t* runtime, void* unused_L)
922
+ {
923
+ return rc_runtime_serialize_progress_sized((uint8_t*)buffer, 0xFFFFFFFF, runtime, unused_L);
924
+ }
925
+
926
+ int rc_runtime_serialize_progress_sized(uint8_t* buffer, uint32_t buffer_size, const rc_runtime_t* runtime, void* unused_L)
927
+ {
928
+ rc_runtime_progress_t progress;
929
+
930
+ (void)unused_L;
931
+
932
+ if (!buffer)
933
+ return RC_INVALID_STATE;
934
+
935
+ rc_runtime_progress_init(&progress, runtime);
936
+ progress.buffer = (uint8_t*)buffer;
937
+ progress.buffer_size = buffer_size;
938
+
939
+ return rc_runtime_progress_serialize_internal(&progress);
940
+ }
941
+
942
+ int rc_runtime_deserialize_progress(rc_runtime_t* runtime, const uint8_t* serialized, void* unused_L)
943
+ {
944
+ return rc_runtime_deserialize_progress_sized(runtime, serialized, 0xFFFFFFFF, unused_L);
945
+ }
946
+
947
+ int rc_runtime_deserialize_progress_sized(rc_runtime_t* runtime, const uint8_t* serialized, uint32_t serialized_size, void* unused_L)
948
+ {
949
+ rc_runtime_progress_t progress;
950
+ md5_state_t state;
951
+ uint8_t md5[16];
952
+ uint32_t chunk_id;
953
+ uint32_t chunk_size;
954
+ uint32_t next_chunk_offset;
955
+ uint32_t i;
956
+ int seen_rich_presence = 0;
957
+ int result = RC_OK;
958
+
959
+ (void)unused_L;
960
+
961
+ if (!serialized || serialized_size < RC_RUNTIME_MIN_BUFFER_SIZE) {
962
+ rc_runtime_reset(runtime);
963
+ return RC_INSUFFICIENT_BUFFER;
964
+ }
965
+
966
+ rc_runtime_progress_init(&progress, runtime);
967
+ progress.buffer = (uint8_t*)serialized;
968
+
969
+ if (rc_runtime_progress_read_uint(&progress) != RC_RUNTIME_MARKER) {
970
+ rc_runtime_reset(runtime);
971
+ return RC_INVALID_STATE;
972
+ }
973
+
974
+ for (i = 0; i < runtime->trigger_count; ++i) {
975
+ rc_runtime_trigger_t* runtime_trigger = &runtime->triggers[i];
976
+ if (runtime_trigger->trigger) {
977
+ /* don't update state for inactive or triggered achievements */
978
+ if (rc_trigger_state_active(runtime_trigger->trigger->state)) {
979
+ /* mark active achievements as unupdated. anything that's still unupdated
980
+ * after deserializing the progress will be reset to waiting */
981
+ runtime_trigger->trigger->state = RC_TRIGGER_STATE_UNUPDATED;
982
+ }
983
+ }
984
+ }
985
+
986
+ for (i = 0; i < runtime->lboard_count; ++i) {
987
+ rc_runtime_lboard_t* runtime_lboard = &runtime->lboards[i];
988
+ if (runtime_lboard->lboard) {
989
+ /* don't update state for inactive or triggered achievements */
990
+ if (rc_lboard_state_active(runtime_lboard->lboard->state)) {
991
+ /* mark active achievements as unupdated. anything that's still unupdated
992
+ * after deserializing the progress will be reset to waiting */
993
+ runtime_lboard->lboard->state = RC_TRIGGER_STATE_UNUPDATED;
994
+ }
995
+ }
996
+ }
997
+
998
+ do {
999
+ if (progress.offset + 8 >= serialized_size) {
1000
+ result = RC_INSUFFICIENT_BUFFER;
1001
+ break;
1002
+ }
1003
+
1004
+ chunk_id = rc_runtime_progress_read_uint(&progress);
1005
+ chunk_size = rc_runtime_progress_read_uint(&progress);
1006
+ next_chunk_offset = progress.offset + chunk_size;
1007
+
1008
+ if (next_chunk_offset > serialized_size) {
1009
+ result = RC_INSUFFICIENT_BUFFER;
1010
+ break;
1011
+ }
1012
+
1013
+ switch (chunk_id) {
1014
+ case RC_RUNTIME_CHUNK_MEMREFS:
1015
+ result = rc_runtime_progress_read_memrefs(&progress);
1016
+ break;
1017
+
1018
+ case RC_RUNTIME_CHUNK_VARIABLES:
1019
+ result = rc_runtime_progress_read_variables(&progress);
1020
+ break;
1021
+
1022
+ case RC_RUNTIME_CHUNK_ACHIEVEMENT:
1023
+ result = rc_runtime_progress_read_achievement(&progress);
1024
+ break;
1025
+
1026
+ case RC_RUNTIME_CHUNK_LEADERBOARD:
1027
+ result = rc_runtime_progress_read_leaderboard(&progress);
1028
+ break;
1029
+
1030
+ case RC_RUNTIME_CHUNK_RICHPRESENCE:
1031
+ seen_rich_presence = 1;
1032
+ result = rc_runtime_progress_read_rich_presence(&progress);
1033
+ break;
1034
+
1035
+ case RC_RUNTIME_CHUNK_DONE:
1036
+ md5_init(&state);
1037
+ md5_append(&state, progress.buffer, progress.offset);
1038
+ md5_finish(&state, md5);
1039
+ if (!rc_runtime_progress_match_md5(&progress, md5))
1040
+ result = RC_INVALID_STATE;
1041
+ break;
1042
+
1043
+ default:
1044
+ if (chunk_size & 0xFFFF0000)
1045
+ result = RC_INVALID_STATE; /* assume unknown chunk > 64KB is invalid */
1046
+ break;
1047
+ }
1048
+
1049
+ progress.offset = next_chunk_offset;
1050
+ } while (result == RC_OK && chunk_id != RC_RUNTIME_CHUNK_DONE);
1051
+
1052
+ if (result != RC_OK) {
1053
+ rc_runtime_reset(runtime);
1054
+ }
1055
+ else {
1056
+ for (i = 0; i < runtime->trigger_count; ++i) {
1057
+ rc_trigger_t* trigger = runtime->triggers[i].trigger;
1058
+ if (trigger && trigger->state == RC_TRIGGER_STATE_UNUPDATED)
1059
+ rc_reset_trigger(trigger);
1060
+ }
1061
+
1062
+ for (i = 0; i < runtime->lboard_count; ++i) {
1063
+ rc_lboard_t* lboard = runtime->lboards[i].lboard;
1064
+ if (lboard && lboard->state == RC_TRIGGER_STATE_UNUPDATED)
1065
+ rc_reset_lboard(lboard);
1066
+ }
1067
+
1068
+ if (!seen_rich_presence && runtime->richpresence && runtime->richpresence->richpresence)
1069
+ rc_reset_richpresence_triggers(runtime->richpresence->richpresence);
1070
+ }
1071
+
1072
+ return result;
1073
+ }