gemba 0.1.1 → 0.2.1

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 (289) 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 +190 -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 +511 -0
  17. data/lib/gemba/achievements/retro_achievements/cli_sync_requester.rb +64 -0
  18. data/lib/gemba/achievements/retro_achievements/ping_worker.rb +28 -0
  19. data/lib/gemba/achievements/retro_achievements/unlock_retry_worker.rb +35 -0
  20. data/lib/gemba/achievements.rb +19 -0
  21. data/lib/gemba/achievements_window.rb +556 -0
  22. data/lib/gemba/app_controller.rb +1036 -0
  23. data/lib/gemba/bios.rb +54 -0
  24. data/lib/gemba/boxart_fetcher/libretro_backend.rb +39 -0
  25. data/lib/gemba/boxart_fetcher/null_backend.rb +12 -0
  26. data/lib/gemba/boxart_fetcher.rb +79 -0
  27. data/lib/gemba/bus_emitter.rb +13 -0
  28. data/lib/gemba/child_window.rb +24 -1
  29. data/lib/gemba/cli/commands/config_cmd.rb +83 -0
  30. data/lib/gemba/cli/commands/decode.rb +154 -0
  31. data/lib/gemba/cli/commands/patch.rb +78 -0
  32. data/lib/gemba/cli/commands/play.rb +78 -0
  33. data/lib/gemba/cli/commands/record.rb +114 -0
  34. data/lib/gemba/cli/commands/replay.rb +161 -0
  35. data/lib/gemba/cli/commands/retro_achievements.rb +213 -0
  36. data/lib/gemba/cli/commands/version.rb +22 -0
  37. data/lib/gemba/cli.rb +52 -364
  38. data/lib/gemba/config.rb +154 -1
  39. data/lib/gemba/data/gb_games.json +1 -0
  40. data/lib/gemba/data/gb_md5.json +1 -0
  41. data/lib/gemba/data/gba_games.json +1 -0
  42. data/lib/gemba/data/gba_md5.json +1 -0
  43. data/lib/gemba/data/gbc_games.json +1 -0
  44. data/lib/gemba/data/gbc_md5.json +1 -0
  45. data/lib/gemba/emulator_frame.rb +1084 -0
  46. data/lib/gemba/event_bus.rb +48 -0
  47. data/lib/gemba/frame_stack.rb +70 -0
  48. data/lib/gemba/game_index.rb +84 -0
  49. data/lib/gemba/game_picker_frame.rb +309 -0
  50. data/lib/gemba/gamepad_map.rb +103 -0
  51. data/lib/gemba/headless.rb +6 -5
  52. data/lib/gemba/headless_player.rb +33 -3
  53. data/lib/gemba/help_window.rb +61 -0
  54. data/lib/gemba/hotkey_map.rb +3 -1
  55. data/lib/gemba/input_recorder.rb +107 -0
  56. data/lib/gemba/input_replayer.rb +119 -0
  57. data/lib/gemba/keyboard_map.rb +90 -0
  58. data/lib/gemba/list_picker_frame.rb +271 -0
  59. data/lib/gemba/locales/en.yml +109 -5
  60. data/lib/gemba/locales/ja.yml +109 -5
  61. data/lib/gemba/main_window.rb +56 -0
  62. data/lib/gemba/modal_stack.rb +81 -0
  63. data/lib/gemba/patcher_window.rb +223 -0
  64. data/lib/gemba/platform/gb.rb +21 -0
  65. data/lib/gemba/platform/gba.rb +21 -0
  66. data/lib/gemba/platform/gbc.rb +23 -0
  67. data/lib/gemba/platform.rb +20 -0
  68. data/lib/gemba/platform_open.rb +19 -0
  69. data/lib/gemba/recorder.rb +4 -3
  70. data/lib/gemba/replay_player.rb +691 -0
  71. data/lib/gemba/rom_info.rb +57 -0
  72. data/lib/gemba/rom_info_window.rb +16 -3
  73. data/lib/gemba/rom_library.rb +106 -0
  74. data/lib/gemba/rom_overrides.rb +47 -0
  75. data/lib/gemba/rom_patcher/bps.rb +161 -0
  76. data/lib/gemba/rom_patcher/ips.rb +101 -0
  77. data/lib/gemba/rom_patcher/ups.rb +119 -0
  78. data/lib/gemba/rom_patcher.rb +109 -0
  79. data/lib/gemba/{rom_loader.rb → rom_resolver.rb} +7 -6
  80. data/lib/gemba/runtime.rb +59 -26
  81. data/lib/gemba/save_state_manager.rb +4 -7
  82. data/lib/gemba/save_state_picker.rb +17 -4
  83. data/lib/gemba/session_logger.rb +64 -0
  84. data/lib/gemba/settings/audio_tab.rb +77 -0
  85. data/lib/gemba/settings/gamepad_tab.rb +351 -0
  86. data/lib/gemba/settings/hotkeys_tab.rb +259 -0
  87. data/lib/gemba/settings/paths.rb +11 -0
  88. data/lib/gemba/settings/recording_tab.rb +83 -0
  89. data/lib/gemba/settings/save_states_tab.rb +91 -0
  90. data/lib/gemba/settings/system_tab.rb +377 -0
  91. data/lib/gemba/settings/video_tab.rb +318 -0
  92. data/lib/gemba/settings_window.rb +162 -1036
  93. data/lib/gemba/version.rb +1 -1
  94. data/lib/gemba/virtual_keyboard.rb +19 -0
  95. data/lib/gemba.rb +2 -12
  96. data/test/achievements_window/test_bulk_sync.rb +218 -0
  97. data/test/achievements_window/test_bus_events.rb +125 -0
  98. data/test/achievements_window/test_close_confirmation.rb +201 -0
  99. data/test/achievements_window/test_initial_state.rb +164 -0
  100. data/test/achievements_window/test_sorting.rb +227 -0
  101. data/test/achievements_window/test_tree_rendering.rb +133 -0
  102. data/test/fixtures/fake_bios.bin +0 -0
  103. data/test/fixtures/pong.gba +0 -0
  104. data/test/fixtures/test.gb +0 -0
  105. data/test/fixtures/test.gbc +0 -0
  106. data/test/fixtures/test_quicksave.ss +0 -0
  107. data/test/screenshots/no_focus.png +0 -0
  108. data/test/shared/teek_test_worker.rb +17 -1
  109. data/test/shared/tk_test_helper.rb +92 -4
  110. data/test/support/achievements_window_helpers.rb +18 -0
  111. data/test/support/fake_core.rb +25 -0
  112. data/test/support/fake_ra_runtime.rb +74 -0
  113. data/test/support/fake_requester.rb +78 -0
  114. data/test/support/player_helpers.rb +20 -5
  115. data/test/test_achievement.rb +32 -0
  116. data/test/{test_player.rb → test_app_controller.rb} +353 -85
  117. data/test/test_bios.rb +123 -0
  118. data/test/test_boxart_fetcher.rb +150 -0
  119. data/test/test_cli.rb +17 -265
  120. data/test/test_cli_config.rb +64 -0
  121. data/test/test_cli_decode.rb +97 -0
  122. data/test/test_cli_patch.rb +58 -0
  123. data/test/test_cli_play.rb +213 -0
  124. data/test/test_cli_ra.rb +175 -0
  125. data/test/test_cli_record.rb +69 -0
  126. data/test/test_cli_replay.rb +72 -0
  127. data/test/test_cli_sync_requester.rb +152 -0
  128. data/test/test_cli_version.rb +27 -0
  129. data/test/test_config.rb +3 -3
  130. data/test/test_config_ra.rb +69 -0
  131. data/test/test_core.rb +62 -1
  132. data/test/test_credentials_presenter.rb +192 -0
  133. data/test/test_event_bus.rb +100 -0
  134. data/test/test_fake_backend_achievements.rb +130 -0
  135. data/test/test_fake_backend_auth.rb +68 -0
  136. data/test/test_game_index.rb +77 -0
  137. data/test/test_game_picker_frame.rb +310 -0
  138. data/test/test_gamepad_map.rb +1 -3
  139. data/test/test_headless_player.rb +17 -3
  140. data/test/test_help_window.rb +82 -0
  141. data/test/test_hotkey_map.rb +22 -1
  142. data/test/test_input_recorder.rb +179 -0
  143. data/test/test_input_replay_determinism.rb +113 -0
  144. data/test/test_input_replayer.rb +162 -0
  145. data/test/test_keyboard_map.rb +1 -3
  146. data/test/test_libretro_backend.rb +41 -0
  147. data/test/test_list_picker_frame.rb +391 -0
  148. data/test/test_locale.rb +1 -1
  149. data/test/test_logging.rb +123 -0
  150. data/test/test_null_backend.rb +42 -0
  151. data/test/test_offline_backend.rb +116 -0
  152. data/test/test_overlay_renderer.rb +1 -1
  153. data/test/test_platform.rb +149 -0
  154. data/test/test_ra_backend.rb +313 -0
  155. data/test/test_ra_backend_unlock_gate.rb +56 -0
  156. data/test/test_ra_backend_unlock_retry.rb +123 -0
  157. data/test/test_recorder.rb +0 -3
  158. data/test/test_replay_player.rb +316 -0
  159. data/test/test_rom_info.rb +149 -0
  160. data/test/test_rom_overrides.rb +86 -0
  161. data/test/test_rom_patcher.rb +383 -0
  162. data/test/{test_rom_loader.rb → test_rom_resolver.rb} +25 -26
  163. data/test/test_save_state_manager.rb +2 -4
  164. data/test/test_settings_audio.rb +107 -0
  165. data/test/test_settings_hotkeys.rb +83 -66
  166. data/test/test_settings_recording.rb +49 -0
  167. data/test/test_settings_save_states.rb +97 -0
  168. data/test/test_settings_system.rb +133 -0
  169. data/test/test_settings_video.rb +450 -0
  170. data/test/test_settings_window.rb +76 -507
  171. data/test/test_tip_service.rb +6 -6
  172. data/test/test_toast_overlay.rb +1 -1
  173. data/test/test_virtual_events.rb +221 -0
  174. data/test/test_virtual_keyboard.rb +1 -1
  175. data/vendor/rcheevos/CHANGELOG.md +495 -0
  176. data/vendor/rcheevos/LICENSE +21 -0
  177. data/vendor/rcheevos/Package.swift +33 -0
  178. data/vendor/rcheevos/README.md +67 -0
  179. data/vendor/rcheevos/include/module.modulemap +70 -0
  180. data/vendor/rcheevos/include/rc_api_editor.h +296 -0
  181. data/vendor/rcheevos/include/rc_api_info.h +280 -0
  182. data/vendor/rcheevos/include/rc_api_request.h +77 -0
  183. data/vendor/rcheevos/include/rc_api_runtime.h +417 -0
  184. data/vendor/rcheevos/include/rc_api_user.h +262 -0
  185. data/vendor/rcheevos/include/rc_client.h +877 -0
  186. data/vendor/rcheevos/include/rc_client_raintegration.h +101 -0
  187. data/vendor/rcheevos/include/rc_consoles.h +138 -0
  188. data/vendor/rcheevos/include/rc_error.h +59 -0
  189. data/vendor/rcheevos/include/rc_export.h +100 -0
  190. data/vendor/rcheevos/include/rc_hash.h +200 -0
  191. data/vendor/rcheevos/include/rc_runtime.h +148 -0
  192. data/vendor/rcheevos/include/rc_runtime_types.h +452 -0
  193. data/vendor/rcheevos/include/rc_util.h +51 -0
  194. data/vendor/rcheevos/include/rcheevos.h +8 -0
  195. data/vendor/rcheevos/src/rapi/rc_api_common.c +1379 -0
  196. data/vendor/rcheevos/src/rapi/rc_api_common.h +88 -0
  197. data/vendor/rcheevos/src/rapi/rc_api_editor.c +625 -0
  198. data/vendor/rcheevos/src/rapi/rc_api_info.c +587 -0
  199. data/vendor/rcheevos/src/rapi/rc_api_runtime.c +901 -0
  200. data/vendor/rcheevos/src/rapi/rc_api_user.c +483 -0
  201. data/vendor/rcheevos/src/rc_client.c +6941 -0
  202. data/vendor/rcheevos/src/rc_client_external.c +281 -0
  203. data/vendor/rcheevos/src/rc_client_external.h +177 -0
  204. data/vendor/rcheevos/src/rc_client_external_versions.h +171 -0
  205. data/vendor/rcheevos/src/rc_client_internal.h +409 -0
  206. data/vendor/rcheevos/src/rc_client_raintegration.c +566 -0
  207. data/vendor/rcheevos/src/rc_client_raintegration_internal.h +61 -0
  208. data/vendor/rcheevos/src/rc_client_types.natvis +396 -0
  209. data/vendor/rcheevos/src/rc_compat.c +251 -0
  210. data/vendor/rcheevos/src/rc_compat.h +121 -0
  211. data/vendor/rcheevos/src/rc_libretro.c +915 -0
  212. data/vendor/rcheevos/src/rc_libretro.h +98 -0
  213. data/vendor/rcheevos/src/rc_util.c +199 -0
  214. data/vendor/rcheevos/src/rc_version.c +11 -0
  215. data/vendor/rcheevos/src/rc_version.h +32 -0
  216. data/vendor/rcheevos/src/rcheevos/alloc.c +312 -0
  217. data/vendor/rcheevos/src/rcheevos/condition.c +754 -0
  218. data/vendor/rcheevos/src/rcheevos/condset.c +777 -0
  219. data/vendor/rcheevos/src/rcheevos/consoleinfo.c +1215 -0
  220. data/vendor/rcheevos/src/rcheevos/format.c +330 -0
  221. data/vendor/rcheevos/src/rcheevos/lboard.c +287 -0
  222. data/vendor/rcheevos/src/rcheevos/memref.c +805 -0
  223. data/vendor/rcheevos/src/rcheevos/operand.c +607 -0
  224. data/vendor/rcheevos/src/rcheevos/rc_internal.h +390 -0
  225. data/vendor/rcheevos/src/rcheevos/rc_runtime_types.natvis +541 -0
  226. data/vendor/rcheevos/src/rcheevos/rc_validate.c +1406 -0
  227. data/vendor/rcheevos/src/rcheevos/rc_validate.h +18 -0
  228. data/vendor/rcheevos/src/rcheevos/richpresence.c +922 -0
  229. data/vendor/rcheevos/src/rcheevos/runtime.c +852 -0
  230. data/vendor/rcheevos/src/rcheevos/runtime_progress.c +1073 -0
  231. data/vendor/rcheevos/src/rcheevos/trigger.c +344 -0
  232. data/vendor/rcheevos/src/rcheevos/value.c +935 -0
  233. data/vendor/rcheevos/src/rhash/aes.c +480 -0
  234. data/vendor/rcheevos/src/rhash/aes.h +49 -0
  235. data/vendor/rcheevos/src/rhash/cdreader.c +838 -0
  236. data/vendor/rcheevos/src/rhash/hash.c +1402 -0
  237. data/vendor/rcheevos/src/rhash/hash_disc.c +1340 -0
  238. data/vendor/rcheevos/src/rhash/hash_encrypted.c +566 -0
  239. data/vendor/rcheevos/src/rhash/hash_rom.c +426 -0
  240. data/vendor/rcheevos/src/rhash/hash_zip.c +460 -0
  241. data/vendor/rcheevos/src/rhash/md5.c +382 -0
  242. data/vendor/rcheevos/src/rhash/md5.h +91 -0
  243. data/vendor/rcheevos/src/rhash/rc_hash_internal.h +116 -0
  244. data/vendor/rcheevos/test/libretro.h +205 -0
  245. data/vendor/rcheevos/test/rapi/test_rc_api_common.c +941 -0
  246. data/vendor/rcheevos/test/rapi/test_rc_api_editor.c +931 -0
  247. data/vendor/rcheevos/test/rapi/test_rc_api_info.c +545 -0
  248. data/vendor/rcheevos/test/rapi/test_rc_api_runtime.c +2213 -0
  249. data/vendor/rcheevos/test/rapi/test_rc_api_user.c +998 -0
  250. data/vendor/rcheevos/test/rcheevos/mock_memory.h +32 -0
  251. data/vendor/rcheevos/test/rcheevos/test_condition.c +570 -0
  252. data/vendor/rcheevos/test/rcheevos/test_condset.c +5170 -0
  253. data/vendor/rcheevos/test/rcheevos/test_consoleinfo.c +203 -0
  254. data/vendor/rcheevos/test/rcheevos/test_format.c +112 -0
  255. data/vendor/rcheevos/test/rcheevos/test_lboard.c +746 -0
  256. data/vendor/rcheevos/test/rcheevos/test_memref.c +520 -0
  257. data/vendor/rcheevos/test/rcheevos/test_operand.c +692 -0
  258. data/vendor/rcheevos/test/rcheevos/test_rc_validate.c +502 -0
  259. data/vendor/rcheevos/test/rcheevos/test_richpresence.c +1564 -0
  260. data/vendor/rcheevos/test/rcheevos/test_runtime.c +1667 -0
  261. data/vendor/rcheevos/test/rcheevos/test_runtime_progress.c +1821 -0
  262. data/vendor/rcheevos/test/rcheevos/test_timing.c +166 -0
  263. data/vendor/rcheevos/test/rcheevos/test_trigger.c +2521 -0
  264. data/vendor/rcheevos/test/rcheevos/test_value.c +870 -0
  265. data/vendor/rcheevos/test/rcheevos-test.sln +46 -0
  266. data/vendor/rcheevos/test/rcheevos-test.vcxproj +239 -0
  267. data/vendor/rcheevos/test/rcheevos-test.vcxproj.filters +335 -0
  268. data/vendor/rcheevos/test/rhash/data.c +657 -0
  269. data/vendor/rcheevos/test/rhash/data.h +32 -0
  270. data/vendor/rcheevos/test/rhash/mock_filereader.c +236 -0
  271. data/vendor/rcheevos/test/rhash/mock_filereader.h +31 -0
  272. data/vendor/rcheevos/test/rhash/test_cdreader.c +920 -0
  273. data/vendor/rcheevos/test/rhash/test_hash.c +310 -0
  274. data/vendor/rcheevos/test/rhash/test_hash_disc.c +1450 -0
  275. data/vendor/rcheevos/test/rhash/test_hash_rom.c +899 -0
  276. data/vendor/rcheevos/test/rhash/test_hash_zip.c +551 -0
  277. data/vendor/rcheevos/test/test.c +113 -0
  278. data/vendor/rcheevos/test/test_framework.h +205 -0
  279. data/vendor/rcheevos/test/test_rc_client.c +10509 -0
  280. data/vendor/rcheevos/test/test_rc_client_external.c +2197 -0
  281. data/vendor/rcheevos/test/test_rc_client_raintegration.c +441 -0
  282. data/vendor/rcheevos/test/test_rc_libretro.c +952 -0
  283. data/vendor/rcheevos/test/test_types.natvis +9 -0
  284. data/vendor/rcheevos/validator/validator.c +658 -0
  285. data/vendor/rcheevos/validator/validator.vcxproj +152 -0
  286. data/vendor/rcheevos/validator/validator.vcxproj.filters +82 -0
  287. metadata +277 -10
  288. data/lib/gemba/input_mappings.rb +0 -214
  289. data/lib/gemba/player.rb +0 -1525
@@ -5,8 +5,12 @@ menu:
5
5
  quit: "Quit"
6
6
  settings: "Settings"
7
7
  view: "View"
8
+ game_library: "Game Library"
8
9
  fullscreen: "Fullscreen"
9
10
  rom_info: "ROM Info…"
11
+ achievements: "Achievements…"
12
+ open_logs_dir: "Open Logs Directory"
13
+ patch_rom: "Patch ROM…"
10
14
  emulation: "Emulation"
11
15
  pause: "Pause"
12
16
  resume: "Resume"
@@ -14,8 +18,10 @@ menu:
14
18
  quick_save: "Quick Save"
15
19
  quick_load: "Quick Load"
16
20
  save_states: "Save States…"
17
- start_recording: "Start Recording"
18
- stop_recording: "Stop Recording"
21
+ start_recording: "Start Capture"
22
+ stop_recording: "Stop Capture"
23
+ start_input_recording: "Start Recording Inputs"
24
+ stop_input_recording: "Stop Recording Inputs"
19
25
 
20
26
  toast:
21
27
  save_blocked: "Save blocked (too fast)"
@@ -32,12 +38,18 @@ toast:
32
38
  no_rewind: "No rewind data"
33
39
  paused: "Paused"
34
40
  waiting_for: "Waiting for {label}…"
35
- recording_started: "Recording started"
36
- recording_stopped: "Recorded {frames} frames"
41
+ recording_started: "Capture started"
42
+ recording_stopped: "Captured {frames} frames"
43
+ input_recording_started: "Recording inputs"
44
+ input_recording_stopped: "Inputs recorded ({frames} frames)"
37
45
 
38
46
  dialog:
39
47
  game_running_title: "Game Running"
40
48
  game_running_msg: "Another game is running. Switch to {name}?"
49
+ return_to_library_title: "Return to Game Library"
50
+ return_to_library_msg: "Return to the Game Library? Unsaved progress will be lost."
51
+ quit_title: "Quit Gemba"
52
+ quit_msg: "A game is running. Quit anyway? Unsaved progress will be lost."
41
53
  drop_error_title: "Drop Error"
42
54
  drop_single_file_only: "Please drop a single ROM file."
43
55
  drop_unsupported_type: "Unsupported file type: {ext}"
@@ -51,6 +63,8 @@ dialog:
51
63
  reset_hotkeys_title: "Reset Hotkeys"
52
64
  reset_hotkeys_msg: "Reset all hotkey bindings to defaults?"
53
65
  key_conflict_title: "Key Conflict"
66
+ cancel_bulk_sync_title: "Cancel Sync?"
67
+ cancel_bulk_sync_msg: "A bulk sync is in progress. Closing will cancel it — games will need to be re-synced manually."
54
68
 
55
69
  settings:
56
70
  video: "Video"
@@ -91,7 +105,9 @@ settings:
91
105
  hk_save_states: "Save States"
92
106
  hk_screenshot: "Screenshot"
93
107
  hk_rewind: "Rewind"
94
- hk_record: "Record"
108
+ hk_record: "Capture"
109
+ hk_input_record: "Record Inputs"
110
+ hk_open_rom: "Open ROM"
95
111
  hk_reset_defaults: "Reset to Defaults"
96
112
  pixel_filter: "Pixel Filter:"
97
113
  filter_nearest: "Nearest Neighbor"
@@ -111,11 +127,40 @@ settings:
111
127
  tip_per_game: "Save separate video, audio, and save state settings for each ROM."
112
128
  tip_turbo_speed: "Fast-forward speed when holding the turbo hotkey."
113
129
  tip_toast_duration: "How long on-screen notifications stay visible."
130
+ system: "System"
131
+ bios_header: "GBA BIOS"
132
+ bios_path: "BIOS file:"
133
+ bios_browse: "Browse…"
134
+ bios_clear: "Clear"
135
+ bios_not_set: "Not set — using built-in HLE (recommended for most games)"
136
+ bios_not_found: "File not found"
137
+ skip_bios: "Skip boot animation"
138
+ tip_skip_bios: "Jump straight to the game, skipping the Game Boy Advance logo screen.\nOnly applies when a real BIOS file is loaded."
139
+ retroachievements: "Achievements"
140
+ ra_enabled: "Enable RetroAchievements"
141
+ ra_credentials: "Account"
142
+ ra_username_placeholder: "Username:"
143
+ ra_token_placeholder: "Password:"
144
+ ra_rich_presence: "Rich Presence (per-game)"
145
+ ra_screenshot_on_unlock: "Screenshot on achievement unlock (per-game)"
146
+ ra_hardcore: "Hardcore mode (disables save states and rewind)"
147
+ tip_ra_password: "Your password is only used to fetch an API token and is never saved."
148
+ ra_login: "Login"
149
+ ra_verify: "Verify Token"
150
+ ra_logout: "Logout"
151
+ ra_reset: "Reset"
152
+ ra_reset_title: "Reset RetroAchievements"
153
+ ra_reset_confirm: "Clear saved credentials and log out?"
154
+ ra_test_ok: "Connection OK ✓"
155
+ ra_disabled: "RetroAchievements is disabled"
156
+ ra_not_logged_in: "Not logged in"
157
+ ra_logged_in_as: "Logged in as {username}"
114
158
  recording: "Recording"
115
159
  recording_compression: "Compression:"
116
160
  tip_recording_compression: "Zlib level for .grec files.\n1 = fastest (default), 6+ has diminishing returns."
117
161
  recordings_folder: "Recordings folder:"
118
162
  open_recordings_folder: "Open Recordings Folder…"
163
+ open_replay_player: "Open Input Replay…"
119
164
  gp_a: "A"
120
165
  gp_b: "B"
121
166
  gp_l: "L"
@@ -134,6 +179,24 @@ picker:
134
179
  slot: "Slot {n}"
135
180
  close: "Close"
136
181
 
182
+ game_picker:
183
+ menu:
184
+ play: "Play"
185
+ quick_load: "Quick Load"
186
+ set_boxart: "Set Boxart"
187
+ remove: "Remove from Library"
188
+
189
+ picker:
190
+ toolbar:
191
+ boxart_view: "Box art view"
192
+ list_view: "List view"
193
+
194
+ list_picker:
195
+ columns:
196
+ title: "Title"
197
+ last_played: "Last Played"
198
+ never_played: "Never"
199
+
137
200
  rom_info:
138
201
  title: "ROM Info"
139
202
  field_title: "Title:"
@@ -148,6 +211,29 @@ rom_info:
148
211
  close: "Close"
149
212
  na: "N/A"
150
213
 
214
+ replay:
215
+ open_recording: "Open Input Recording…"
216
+ replay_player: "Input Replay"
217
+ ended: "Replay complete ({frames} frames)"
218
+ empty_hint: "Open Input Recording (Cmd+O)"
219
+
220
+ patcher:
221
+ title: "Patch ROM"
222
+ rom_label: "ROM file:"
223
+ patch_label: "Patch file:"
224
+ outdir_label: "Output dir:"
225
+ browse: "Browse…"
226
+ apply: "Apply Patch"
227
+ working: "Applying patch…"
228
+ done: "Done →"
229
+ err_missing_fields: "Please fill in all fields."
230
+ err_rom_not_found: "ROM file not found."
231
+ err_patch_not_found: "Patch file not found."
232
+ err_failed: "Patch failed:"
233
+ overwrite_title: "File Exists"
234
+ overwrite_msg: "{path} already exists. Overwrite it?"
235
+ thread_mode_warn: "Note: Progress may appear stuck on Ruby < 4"
236
+
151
237
  player:
152
238
  open_rom_hint: "File > Open ROM…"
153
239
  fps: "{fps} fps"
@@ -155,3 +241,21 @@ player:
155
241
  ff_max: ">> MAX"
156
242
  none: "(none)"
157
243
  clear: "Clear"
244
+
245
+ achievements:
246
+ title: "Achievements"
247
+ game_label: "Game:"
248
+ sync: "Sync"
249
+ name_col: "Achievement"
250
+ points_col: "Points"
251
+ earned_col: "Earned"
252
+ none: "No achievements loaded"
253
+ earned_label: "{earned} / {total} earned"
254
+ sync_pending: "Syncing…"
255
+ sync_failed: "Sync failed"
256
+ sync_no_game: "No game loaded"
257
+ sync_timeout: "Sync timed out"
258
+ not_logged_in: "Not logged in"
259
+ include_unofficial: "Include Unofficial"
260
+ bulk_syncing: "Syncing {title} ({n}/{total})…"
261
+ bulk_sync_done: "Synced {count} games"
@@ -5,8 +5,12 @@ menu:
5
5
  quit: "終了"
6
6
  settings: "設定"
7
7
  view: "表示"
8
+ game_library: "ゲームライブラリ"
8
9
  fullscreen: "フルスクリーン"
9
10
  rom_info: "ROM情報…"
11
+ achievements: "アチーブメント…"
12
+ open_logs_dir: "ログフォルダを開く"
13
+ patch_rom: "ROMをパッチ…"
10
14
  emulation: "エミュレーション"
11
15
  pause: "一時停止"
12
16
  resume: "再開"
@@ -14,8 +18,10 @@ menu:
14
18
  quick_save: "クイックセーブ"
15
19
  quick_load: "クイックロード"
16
20
  save_states: "ステートセーブ…"
17
- start_recording: "録画開始"
18
- stop_recording: "録画停止"
21
+ start_recording: "キャプチャ開始"
22
+ stop_recording: "キャプチャ停止"
23
+ start_input_recording: "入力記録開始"
24
+ stop_input_recording: "入力記録停止"
19
25
 
20
26
  toast:
21
27
  save_blocked: "セーブ制限中(間隔が短すぎます)"
@@ -32,12 +38,18 @@ toast:
32
38
  no_rewind: "巻き戻しデータがありません"
33
39
  paused: "一時停止"
34
40
  waiting_for: "{label}を待機中…"
35
- recording_started: "録画を開始しました"
36
- recording_stopped: "{frames}フレームを録画しました"
41
+ recording_started: "キャプチャを開始しました"
42
+ recording_stopped: "{frames}フレームをキャプチャしました"
43
+ input_recording_started: "入力記録を開始しました"
44
+ input_recording_stopped: "入力を記録しました({frames}フレーム)"
37
45
 
38
46
  dialog:
39
47
  game_running_title: "ゲーム実行中"
40
48
  game_running_msg: "別のゲームが実行中です。{name}に切り替えますか?"
49
+ return_to_library_title: "ゲームライブラリに戻る"
50
+ return_to_library_msg: "ゲームライブラリに戻りますか?保存されていない進行状況は失われます。"
51
+ quit_title: "Gembaを終了"
52
+ quit_msg: "ゲームが実行中です。終了しますか?保存されていない進行状況は失われます。"
41
53
  drop_error_title: "ドロップエラー"
42
54
  drop_single_file_only: "ROMファイルを1つだけドロップしてください。"
43
55
  drop_unsupported_type: "対応していないファイル形式: {ext}"
@@ -51,6 +63,8 @@ dialog:
51
63
  reset_hotkeys_title: "ホットキーをリセット"
52
64
  reset_hotkeys_msg: "すべてのホットキー設定をデフォルトに戻しますか?"
53
65
  key_conflict_title: "キーの競合"
66
+ cancel_bulk_sync_title: "同期をキャンセル?"
67
+ cancel_bulk_sync_msg: "一括同期が実行中です。閉じるとキャンセルされます — 手動で再同期が必要になります。"
54
68
 
55
69
  settings:
56
70
  video: "映像"
@@ -91,7 +105,9 @@ settings:
91
105
  hk_save_states: "ステートセーブ"
92
106
  hk_screenshot: "スクリーンショット"
93
107
  hk_rewind: "巻き戻し"
94
- hk_record: "録画"
108
+ hk_record: "キャプチャ"
109
+ hk_input_record: "入力記録"
110
+ hk_open_rom: "ROM を開く"
95
111
  hk_reset_defaults: "デフォルトに戻す"
96
112
  pixel_filter: "ピクセルフィルタ:"
97
113
  filter_nearest: "ニアレストネイバー"
@@ -111,11 +127,40 @@ settings:
111
127
  tip_per_game: "ROM毎に映像・音声・ステートセーブの設定を個別に保存します。"
112
128
  tip_turbo_speed: "早送りホットキー使用時の速度。"
113
129
  tip_toast_duration: "画面上の通知の表示時間。"
130
+ system: "システム"
131
+ bios_header: "GBA BIOS"
132
+ bios_path: "BIOSファイル:"
133
+ bios_browse: "参照…"
134
+ bios_clear: "クリア"
135
+ bios_not_set: "未設定 — 内蔵HLEを使用(ほとんどのゲームに推奨)"
136
+ bios_not_found: "ファイルが見つかりません"
137
+ skip_bios: "起動アニメーションをスキップ"
138
+ tip_skip_bios: "ゲームボーイアドバンスのロゴ画面をスキップして\n直接ゲームを開始します。実BIOSが必要です。"
139
+ retroachievements: "実績"
140
+ ra_enabled: "RetroAchievementsを有効にする"
141
+ ra_credentials: "アカウント"
142
+ ra_username_placeholder: "ユーザー名:"
143
+ ra_token_placeholder: "パスワード:"
144
+ ra_rich_presence: "リッチプレゼンス(ゲームごと)"
145
+ ra_screenshot_on_unlock: "実績解除時にスクリーンショット(ゲームごと)"
146
+ ra_hardcore: "ハードコアモード(セーブステートと巻き戻し無効)"
147
+ tip_ra_password: "パスワードはAPIトークン取得のみに使用され、保存されません。"
148
+ ra_login: "ログイン"
149
+ ra_verify: "トークン確認"
150
+ ra_logout: "ログアウト"
151
+ ra_reset: "リセット"
152
+ ra_reset_title: "RetroAchievementsをリセット"
153
+ ra_reset_confirm: "保存済みの認証情報を削除してログアウトしますか?"
154
+ ra_test_ok: "接続OK ✓"
155
+ ra_disabled: "RetroAchievementsは無効です"
156
+ ra_not_logged_in: "未ログイン"
157
+ ra_logged_in_as: "{username}でログイン中"
114
158
  recording: "録画"
115
159
  recording_compression: "圧縮レベル:"
116
160
  tip_recording_compression: ".grecファイルのzlib圧縮レベル。\n1 = 最速(デフォルト)、6以上は効果が小さくなります。"
117
161
  recordings_folder: "録画フォルダ:"
118
162
  open_recordings_folder: "録画フォルダを開く…"
163
+ open_replay_player: "入力リプレイを開く…"
119
164
  gp_a: "A"
120
165
  gp_b: "B"
121
166
  gp_l: "L"
@@ -134,6 +179,24 @@ picker:
134
179
  slot: "スロット{n}"
135
180
  close: "閉じる"
136
181
 
182
+ game_picker:
183
+ menu:
184
+ play: "プレイ"
185
+ quick_load: "クイックロード"
186
+ set_boxart: "カスタム画像を設定"
187
+ remove: "ライブラリから削除"
188
+
189
+ picker:
190
+ toolbar:
191
+ boxart_view: "ボックスアート表示"
192
+ list_view: "リスト表示"
193
+
194
+ list_picker:
195
+ columns:
196
+ title: "タイトル"
197
+ last_played: "最終プレイ日"
198
+ never_played: "未プレイ"
199
+
137
200
  rom_info:
138
201
  title: "ROM情報"
139
202
  field_title: "タイトル:"
@@ -148,6 +211,29 @@ rom_info:
148
211
  close: "閉じる"
149
212
  na: "N/A"
150
213
 
214
+ replay:
215
+ open_recording: "入力記録を開く…"
216
+ replay_player: "入力リプレイ"
217
+ ended: "リプレイ完了({frames}フレーム)"
218
+ empty_hint: "入力記録を開く (Cmd+O)"
219
+
220
+ patcher:
221
+ title: "ROMをパッチ"
222
+ rom_label: "ROMファイル:"
223
+ patch_label: "パッチファイル:"
224
+ outdir_label: "出力フォルダ:"
225
+ browse: "参照…"
226
+ apply: "パッチを適用"
227
+ working: "パッチ適用中…"
228
+ done: "完了 →"
229
+ err_missing_fields: "すべての項目を入力してください。"
230
+ err_rom_not_found: "ROMファイルが見つかりません。"
231
+ err_patch_not_found: "パッチファイルが見つかりません。"
232
+ err_failed: "パッチ失敗:"
233
+ overwrite_title: "ファイルが存在します"
234
+ overwrite_msg: "{path} は既に存在します。上書きしますか?"
235
+ thread_mode_warn: "注意: Ruby < 4 ではプログレスが止まって見える場合があります"
236
+
151
237
  player:
152
238
  open_rom_hint: "ファイル > ROMを開く…"
153
239
  fps: "{fps} fps"
@@ -155,3 +241,21 @@ player:
155
241
  ff_max: ">> 最大"
156
242
  none: "(なし)"
157
243
  clear: "クリア"
244
+
245
+ achievements:
246
+ title: "アチーブメント"
247
+ game_label: "ゲーム:"
248
+ sync: "同期"
249
+ name_col: "アチーブメント"
250
+ points_col: "ポイント"
251
+ earned_col: "取得日時"
252
+ none: "アチーブメントなし"
253
+ earned_label: "{earned} / {total} 取得"
254
+ sync_pending: "同期中…"
255
+ sync_failed: "同期に失敗しました"
256
+ sync_no_game: "ゲームが読み込まれていません"
257
+ sync_timeout: "同期がタイムアウトしました"
258
+ not_logged_in: "ログインしていません"
259
+ include_unofficial: "非公式を含む"
260
+ bulk_syncing: "{title} を同期中 ({n}/{total})…"
261
+ bulk_sync_done: "{count} ゲームを同期しました"
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ module Gemba
5
+ # Pure Tk shell — creates the app window and hosts a FrameStack.
6
+ #
7
+ # MainWindow knows nothing about ROMs, emulation, menus, or config.
8
+ # It provides geometry/title/fullscreen primitives that the AppController
9
+ # drives. Its only structural contribution is the FrameStack, which
10
+ # manages show/hide transitions to prevent visual flash (FOUC).
11
+ class MainWindow
12
+ attr_reader :app, :frame_stack
13
+
14
+ def initialize
15
+ @app = Teek::App.new
16
+ @app.show
17
+ @frame_stack = FrameStack.new
18
+ end
19
+
20
+ def set_title(title)
21
+ @app.set_window_title(title)
22
+ end
23
+
24
+ def set_geometry(w, h)
25
+ @app.set_window_geometry("#{w}x#{h}")
26
+ end
27
+
28
+ def set_aspect(numer, denom)
29
+ @app.command(:wm, 'aspect', '.', numer, denom, numer, denom)
30
+ end
31
+
32
+ def set_minsize(w, h)
33
+ @app.command(:wm, 'minsize', '.', w, h)
34
+ end
35
+
36
+ def reset_minsize
37
+ @app.command(:wm, 'minsize', '.', 0, 0)
38
+ end
39
+
40
+ def reset_aspect_ratio
41
+ @app.command(:wm, 'aspect', '.', '', '', '', '')
42
+ end
43
+
44
+ def set_timer_speed(ms)
45
+ @app.interp.thread_timer_ms = ms
46
+ end
47
+
48
+ def fullscreen=(val)
49
+ @app.command(:wm, 'attributes', '.', '-fullscreen', val ? 1 : 0)
50
+ end
51
+
52
+ def mainloop
53
+ @app.mainloop
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gemba
4
+ # Push/pop stack for modal child windows.
5
+ #
6
+ # One modal can push another (e.g. Settings → Replay Player) and the
7
+ # previous modal is automatically re-shown on pop.
8
+ #
9
+ # Windows must implement the ModalWindow protocol:
10
+ # show_modal(**args) — reveal the window (deiconify, grab, position)
11
+ # withdraw — hide the window (release grab, withdraw — NO callback)
12
+ #
13
+ # @example
14
+ # stack = ModalStack.new(
15
+ # on_enter: ->(name) { pause_emulation },
16
+ # on_exit: -> { unpause_emulation },
17
+ # on_focus_change: ->(name) { update_toast(name) },
18
+ # )
19
+ # stack.push(:settings, @settings_window, show_args: { tab: :video })
20
+ # stack.push(:replay, @replay_player) # settings auto-withdrawn
21
+ # stack.pop # replay closed, settings re-shown
22
+ # stack.pop # settings closed, on_exit fired
23
+ class ModalStack
24
+ Entry = Data.define(:name, :window, :show_args)
25
+
26
+ # @param on_enter [Proc] called with (name) when stack goes empty → non-empty
27
+ # @param on_exit [Proc] called when stack goes non-empty → empty
28
+ # @param on_focus_change [Proc, nil] called with (name) whenever the top modal changes
29
+ def initialize(on_enter:, on_exit:, on_focus_change: nil)
30
+ @stack = []
31
+ @on_enter = on_enter
32
+ @on_exit = on_exit
33
+ @on_focus_change = on_focus_change
34
+ end
35
+
36
+ # @return [Boolean] true if any modal is open
37
+ def active? = !@stack.empty?
38
+
39
+ # @return [Symbol, nil] name of the topmost modal, or nil
40
+ def current = @stack.last&.name
41
+
42
+ # @return [Integer] number of modals on the stack
43
+ def size = @stack.length
44
+
45
+ # Push a modal onto the stack.
46
+ #
47
+ # If another modal is on top, it is withdrawn (without callback).
48
+ # If the stack was empty, on_enter is fired (pause emulation, etc.).
49
+ #
50
+ # @param name [Symbol] identifier for the modal (e.g. :settings, :picker)
51
+ # @param window [#show_modal, #withdraw] the modal window object
52
+ # @param show_args [Hash] keyword arguments forwarded to window.show_modal
53
+ def push(name, window, show_args: {})
54
+ was_empty = @stack.empty?
55
+
56
+ # Withdraw current top without firing its on_dismiss
57
+ @stack.last&.window&.withdraw
58
+
59
+ @stack.push(Entry.new(name: name, window: window, show_args: show_args))
60
+ @on_enter.call(name) if was_empty
61
+ @on_focus_change&.call(name)
62
+ window.show_modal(**show_args)
63
+ end
64
+
65
+ # Pop the current modal off the stack.
66
+ #
67
+ # If the stack still has entries, the previous modal is re-shown.
68
+ # If the stack is now empty, on_exit is fired (unpause emulation, etc.).
69
+ def pop
70
+ return unless (entry = @stack.pop)
71
+ entry.window.withdraw
72
+
73
+ if (prev = @stack.last)
74
+ @on_focus_change&.call(prev.name)
75
+ prev.window.show_modal(**prev.show_args)
76
+ else
77
+ @on_exit.call
78
+ end
79
+ end
80
+ end
81
+ end