gemba 0.1.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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/THIRD_PARTY_NOTICES +113 -0
  3. data/assets/JetBrainsMonoNL-Regular.ttf +0 -0
  4. data/assets/ark-pixel-12px-monospaced-ja.ttf +0 -0
  5. data/bin/gemba +14 -0
  6. data/ext/gemba/extconf.rb +185 -0
  7. data/ext/gemba/gemba_ext.c +1051 -0
  8. data/ext/gemba/gemba_ext.h +15 -0
  9. data/gemba.gemspec +38 -0
  10. data/lib/gemba/child_window.rb +62 -0
  11. data/lib/gemba/cli.rb +384 -0
  12. data/lib/gemba/config.rb +621 -0
  13. data/lib/gemba/core.rb +121 -0
  14. data/lib/gemba/headless.rb +12 -0
  15. data/lib/gemba/headless_player.rb +206 -0
  16. data/lib/gemba/hotkey_map.rb +202 -0
  17. data/lib/gemba/input_mappings.rb +214 -0
  18. data/lib/gemba/locale.rb +92 -0
  19. data/lib/gemba/locales/en.yml +157 -0
  20. data/lib/gemba/locales/ja.yml +157 -0
  21. data/lib/gemba/method_coverage_service.rb +265 -0
  22. data/lib/gemba/overlay_renderer.rb +109 -0
  23. data/lib/gemba/player.rb +1515 -0
  24. data/lib/gemba/recorder.rb +156 -0
  25. data/lib/gemba/recorder_decoder.rb +325 -0
  26. data/lib/gemba/rom_info_window.rb +346 -0
  27. data/lib/gemba/rom_loader.rb +100 -0
  28. data/lib/gemba/runtime.rb +39 -0
  29. data/lib/gemba/save_state_manager.rb +155 -0
  30. data/lib/gemba/save_state_picker.rb +199 -0
  31. data/lib/gemba/settings_window.rb +1173 -0
  32. data/lib/gemba/tip_service.rb +133 -0
  33. data/lib/gemba/toast_overlay.rb +128 -0
  34. data/lib/gemba/version.rb +5 -0
  35. data/lib/gemba.rb +17 -0
  36. data/test/fixtures/test.gba +0 -0
  37. data/test/fixtures/test.sav +0 -0
  38. data/test/shared/screenshot_helper.rb +113 -0
  39. data/test/shared/simplecov_config.rb +59 -0
  40. data/test/shared/teek_test_worker.rb +388 -0
  41. data/test/shared/tk_test_helper.rb +354 -0
  42. data/test/support/input_mocks.rb +61 -0
  43. data/test/support/player_helpers.rb +77 -0
  44. data/test/test_cli.rb +281 -0
  45. data/test/test_config.rb +897 -0
  46. data/test/test_core.rb +401 -0
  47. data/test/test_gamepad_map.rb +116 -0
  48. data/test/test_headless_player.rb +205 -0
  49. data/test/test_helper.rb +19 -0
  50. data/test/test_hotkey_map.rb +396 -0
  51. data/test/test_keyboard_map.rb +108 -0
  52. data/test/test_locale.rb +159 -0
  53. data/test/test_mgba.rb +26 -0
  54. data/test/test_overlay_renderer.rb +199 -0
  55. data/test/test_player.rb +903 -0
  56. data/test/test_recorder.rb +180 -0
  57. data/test/test_rom_loader.rb +149 -0
  58. data/test/test_save_state_manager.rb +289 -0
  59. data/test/test_settings_hotkeys.rb +434 -0
  60. data/test/test_settings_window.rb +1039 -0
  61. data/test/test_tip_service.rb +138 -0
  62. data/test/test_toast_overlay.rb +216 -0
  63. data/test/test_virtual_keyboard.rb +39 -0
  64. data/test/test_xor_delta.rb +61 -0
  65. metadata +234 -0
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require_relative "shared/tk_test_helper"
5
+
6
+ class TestTipService < Minitest::Test
7
+ include TeekTestHelper
8
+
9
+ def test_register_sets_underline_font
10
+ assert_tk_app("register sets underlined font") do
11
+ require "gemba/tip_service"
12
+ tips = Gemba::TipService.new(app)
13
+
14
+ lbl = ".test_lbl"
15
+ app.command('ttk::label', lbl, text: "Test")
16
+ app.command(:pack, lbl)
17
+ app.update
18
+
19
+ tips.register(lbl, "Help text")
20
+ app.update
21
+
22
+ font = app.command(lbl, 'cget', '-font')
23
+ underline = app.tcl_eval("font actual #{font} -underline")
24
+ assert_equal '1', underline
25
+ end
26
+ end
27
+
28
+ def test_show_creates_tooltip
29
+ assert_tk_app("show creates a tooltip toplevel") do
30
+ require "gemba/tip_service"
31
+ tips = Gemba::TipService.new(app)
32
+
33
+ lbl = ".test_lbl"
34
+ app.command('ttk::label', lbl, text: "Test")
35
+ app.command(:pack, lbl)
36
+ app.show
37
+ app.update
38
+
39
+ refute tips.showing?
40
+ tips.show(lbl, "Help text")
41
+ app.update
42
+
43
+ assert tips.showing?
44
+ assert_equal lbl, tips.target
45
+ end
46
+ end
47
+
48
+ def test_hide_destroys_tooltip
49
+ assert_tk_app("hide destroys tooltip") do
50
+ require "gemba/tip_service"
51
+ tips = Gemba::TipService.new(app)
52
+
53
+ lbl = ".test_lbl"
54
+ app.command('ttk::label', lbl, text: "Test")
55
+ app.command(:pack, lbl)
56
+ app.show
57
+ app.update
58
+
59
+ tips.show(lbl, "Help text")
60
+ app.update
61
+ assert tips.showing?
62
+
63
+ tips.hide
64
+ app.update
65
+ refute tips.showing?
66
+ assert_nil tips.target
67
+ end
68
+ end
69
+
70
+ def test_toggle_behavior
71
+ assert_tk_app("clicking same label toggles tooltip") do
72
+ require "gemba/tip_service"
73
+ tips = Gemba::TipService.new(app)
74
+
75
+ lbl = ".test_lbl"
76
+ app.command('ttk::label', lbl, text: "Test")
77
+ app.command(:pack, lbl)
78
+ app.show
79
+ app.update
80
+
81
+ tips.register(lbl, "Help text")
82
+ app.update
83
+
84
+ # First click — show
85
+ app.command(:event, 'generate', lbl, '<Button-1>')
86
+ app.update
87
+ assert tips.showing?
88
+ assert_equal lbl, tips.target
89
+
90
+ # Second click — hide
91
+ app.command(:event, 'generate', lbl, '<Button-1>')
92
+ app.update
93
+ refute tips.showing?
94
+ end
95
+ end
96
+
97
+ def test_only_one_tooltip_at_a_time
98
+ assert_tk_app("showing a second tooltip hides the first") do
99
+ require "gemba/tip_service"
100
+ tips = Gemba::TipService.new(app)
101
+
102
+ lbl1 = ".test_lbl1"
103
+ lbl2 = ".test_lbl2"
104
+ app.command('ttk::label', lbl1, text: "Label 1")
105
+ app.command('ttk::label', lbl2, text: "Label 2")
106
+ app.command(:pack, lbl1)
107
+ app.command(:pack, lbl2)
108
+ app.show
109
+ app.update
110
+
111
+ tips.register(lbl1, "Help 1")
112
+ tips.register(lbl2, "Help 2")
113
+ app.update
114
+
115
+ # Show first tooltip
116
+ tips.show(lbl1, "Help 1")
117
+ app.update
118
+ assert_equal lbl1, tips.target
119
+
120
+ # Show second — first should be gone
121
+ tips.show(lbl2, "Help 2")
122
+ app.update
123
+ assert_equal lbl2, tips.target
124
+ end
125
+ end
126
+
127
+ def test_dismiss_ms_is_configurable
128
+ assert_tk_app("dismiss_ms is configurable") do
129
+ require "gemba/tip_service"
130
+ tips = Gemba::TipService.new(app, dismiss_ms: 2000)
131
+
132
+ assert_equal 2000, tips.dismiss_ms
133
+
134
+ tips.dismiss_ms = 5000
135
+ assert_equal 5000, tips.dismiss_ms
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require_relative "../lib/gemba/toast_overlay"
5
+
6
+ class TestToastOverlay < Minitest::Test
7
+ # Minimal texture mock — records destroy calls.
8
+ class MockTexture
9
+ attr_reader :calls, :width, :height
10
+ attr_accessor :blend_mode
11
+
12
+ def initialize(w, h)
13
+ @width = w
14
+ @height = h
15
+ @blend_mode = nil
16
+ @calls = []
17
+ end
18
+
19
+ def update(pixels)
20
+ @calls << [:update, pixels.bytesize]
21
+ end
22
+
23
+ def destroy
24
+ @calls << [:destroy]
25
+ end
26
+ end
27
+
28
+ # Recording renderer mock.
29
+ class MockRenderer
30
+ attr_reader :calls, :textures
31
+
32
+ def initialize
33
+ @calls = []
34
+ @textures = []
35
+ end
36
+
37
+ def create_texture(w, h, mode)
38
+ tex = MockTexture.new(w, h)
39
+ @textures << tex
40
+ @calls << [:create_texture, w, h, mode]
41
+ tex
42
+ end
43
+
44
+ def output_size
45
+ [720, 480]
46
+ end
47
+
48
+ def copy(tex, src, dst)
49
+ @calls << [:copy, src, dst]
50
+ end
51
+ end
52
+
53
+ # Recording font mock.
54
+ class MockFont
55
+ attr_reader :calls
56
+
57
+ def initialize
58
+ @calls = []
59
+ end
60
+
61
+ def ascent
62
+ 10
63
+ end
64
+
65
+ def measure(_text)
66
+ [100, 14]
67
+ end
68
+
69
+ def render_text(text, r, g, b)
70
+ @calls << [:render_text, text, r, g, b]
71
+ MockTexture.new(text.length * 7, 14)
72
+ end
73
+ end
74
+
75
+ # Stub for the C-implemented toast_background — returns dummy ARGB pixels.
76
+ STUB_BG_FN = ->(w, h, _radius) { "\x00".b * (w * h * 4) }
77
+
78
+ def setup
79
+ @renderer = MockRenderer.new
80
+ @font = MockFont.new
81
+ @toast = Gemba::ToastOverlay.new(
82
+ renderer: @renderer, font: @font, duration: 1.5, bg_fn: STUB_BG_FN
83
+ )
84
+ end
85
+
86
+ # -- visibility ------------------------------------------------------------
87
+
88
+ def test_not_visible_initially
89
+ refute @toast.visible?
90
+ end
91
+
92
+ def test_visible_after_show
93
+ @toast.show("Hello")
94
+ assert @toast.visible?
95
+ end
96
+
97
+ def test_not_visible_after_destroy
98
+ @toast.show("Hello")
99
+ @toast.destroy
100
+ refute @toast.visible?
101
+ end
102
+
103
+ # -- show ------------------------------------------------------------------
104
+
105
+ def test_show_renders_text
106
+ @toast.show("Saved")
107
+ render_calls = @font.calls.select { |c| c[0] == :render_text }
108
+ assert_equal 1, render_calls.size
109
+ assert_equal "Saved", render_calls[0][1]
110
+ # White text
111
+ assert_equal [255, 255, 255], render_calls[0][2..4]
112
+ end
113
+
114
+ def test_show_creates_background_texture
115
+ @toast.show("Saved")
116
+ create_calls = @renderer.calls.select { |c| c[0] == :create_texture }
117
+ assert_equal 1, create_calls.size
118
+ # Background should be wider than text (padded)
119
+ assert create_calls[0][1] > 0
120
+ assert create_calls[0][2] > 0
121
+ assert_equal :streaming, create_calls[0][3]
122
+ end
123
+
124
+ def test_show_replaces_previous_toast
125
+ @toast.show("First")
126
+ @toast.show("Second")
127
+ # Should have rendered text twice
128
+ render_calls = @font.calls.select { |c| c[0] == :render_text }
129
+ assert_equal 2, render_calls.size
130
+ assert_equal "Second", render_calls[1][1]
131
+ # First toast's textures should have been destroyed
132
+ first_bg = @renderer.textures[0]
133
+ assert first_bg.calls.any? { |c| c[0] == :destroy }
134
+ end
135
+
136
+ # -- draw ------------------------------------------------------------------
137
+
138
+ def test_draw_does_nothing_when_no_toast
139
+ r = MockRenderer.new
140
+ @toast.draw(r, nil)
141
+ assert_empty r.calls
142
+ end
143
+
144
+ def test_draw_copies_bg_and_text
145
+ @toast.show("Hello")
146
+ r = MockRenderer.new
147
+ @toast.draw(r, [0, 0, 720, 480])
148
+ copy_calls = r.calls.select { |c| c[0] == :copy }
149
+ assert_equal 2, copy_calls.size # background + text
150
+ end
151
+
152
+ def test_draw_centers_in_dest_rect
153
+ @toast.show("Hi")
154
+ r = MockRenderer.new
155
+ @toast.draw(r, [100, 0, 500, 400])
156
+ copy_calls = r.calls.select { |c| c[0] == :copy }
157
+ # Background dest rect x should be centered around 100 + 250 = 350
158
+ bg_dst = copy_calls[0][2]
159
+ cx = 100 + 500 / 2
160
+ assert_equal cx - bg_dst[2] / 2, bg_dst[0]
161
+ end
162
+
163
+ def test_draw_uses_output_size_when_no_dest
164
+ @toast.show("Hi")
165
+ r = MockRenderer.new
166
+ @toast.draw(r, nil)
167
+ copy_calls = r.calls.select { |c| c[0] == :copy }
168
+ bg_dst = copy_calls[0][2]
169
+ # Should center around output_size width / 2 = 360
170
+ assert_equal 360 - bg_dst[2] / 2, bg_dst[0]
171
+ end
172
+
173
+ # -- expiration ------------------------------------------------------------
174
+
175
+ def test_draw_destroys_expired_toast
176
+ @toast.show("Bye", duration: 0.0)
177
+ sleep 0.01 # ensure expired
178
+ r = MockRenderer.new
179
+ @toast.draw(r, nil)
180
+ # Should have self-destroyed, no copy calls
181
+ assert_empty r.calls.select { |c| c[0] == :copy }
182
+ refute @toast.visible?
183
+ end
184
+
185
+ def test_permanent_toast_does_not_expire
186
+ @toast.show("Wait", permanent: true)
187
+ sleep 0.01
188
+ r = MockRenderer.new
189
+ @toast.draw(r, [0, 0, 720, 480])
190
+ copy_calls = r.calls.select { |c| c[0] == :copy }
191
+ assert_equal 2, copy_calls.size
192
+ assert @toast.visible?
193
+ end
194
+
195
+ # -- destroy ---------------------------------------------------------------
196
+
197
+ def test_destroy_frees_textures
198
+ @toast.show("Hello")
199
+ bg_tex = @renderer.textures.last
200
+ @toast.destroy
201
+ assert bg_tex.calls.any? { |c| c[0] == :destroy }
202
+ end
203
+
204
+ def test_destroy_is_safe_when_empty
205
+ @toast.destroy # no-op, should not raise
206
+ refute @toast.visible?
207
+ end
208
+
209
+ # -- duration accessor -----------------------------------------------------
210
+
211
+ def test_duration_accessor
212
+ assert_in_delta 1.5, @toast.duration
213
+ @toast.duration = 3.0
214
+ assert_in_delta 3.0, @toast.duration
215
+ end
216
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require_relative "../lib/gemba/input_mappings"
5
+
6
+ class TestVirtualKeyboard < Minitest::Test
7
+ def setup
8
+ @kb = Gemba::VirtualKeyboard.new
9
+ end
10
+
11
+ def test_button_false_initially
12
+ refute @kb.button?('z')
13
+ end
14
+
15
+ def test_press_makes_button_true
16
+ @kb.press('z')
17
+ assert @kb.button?('z')
18
+ end
19
+
20
+ def test_release_makes_button_false
21
+ @kb.press('z')
22
+ @kb.release('z')
23
+ refute @kb.button?('z')
24
+ end
25
+
26
+ def test_multiple_keys
27
+ @kb.press('z')
28
+ @kb.press('x')
29
+ assert @kb.button?('z')
30
+ assert @kb.button?('x')
31
+ @kb.release('z')
32
+ refute @kb.button?('z')
33
+ assert @kb.button?('x')
34
+ end
35
+
36
+ def test_never_closed
37
+ refute @kb.closed?
38
+ end
39
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/autorun"
4
+ require "gemba/headless"
5
+
6
+ class TestXorDelta < Minitest::Test
7
+ def test_xor_identical_strings_produces_zeros
8
+ a = "\xFF\x00\xAB".b
9
+ b = "\xFF\x00\xAB".b
10
+ result = Gemba.xor_delta(a, b)
11
+ assert_equal "\x00\x00\x00".b, result
12
+ end
13
+
14
+ def test_xor_against_zeros_returns_original
15
+ a = "\xDE\xAD\xBE\xEF".b
16
+ b = "\x00\x00\x00\x00".b
17
+ result = Gemba.xor_delta(a, b)
18
+ assert_equal a, result
19
+ end
20
+
21
+ def test_xor_is_reversible
22
+ a = "hello world!".b
23
+ b = "goodbye!!!12".b
24
+ delta = Gemba.xor_delta(a, b)
25
+ restored = Gemba.xor_delta(delta, b)
26
+ assert_equal a, restored
27
+ end
28
+
29
+ def test_xor_mismatched_lengths_raises
30
+ assert_raises(ArgumentError) do
31
+ Gemba.xor_delta("abc", "ab")
32
+ end
33
+ end
34
+
35
+ def test_xor_empty_strings
36
+ result = Gemba.xor_delta("".b, "".b)
37
+ assert_equal "".b, result
38
+ end
39
+
40
+ # -- count_changed_pixels --
41
+
42
+ def test_count_all_zeros
43
+ delta = "\x00\x00\x00\x00\x00\x00\x00\x00".b # 2 pixels, both zero
44
+ assert_equal 0, Gemba.count_changed_pixels(delta)
45
+ end
46
+
47
+ def test_count_all_changed
48
+ delta = "\xFF\x00\x00\x00\x00\xFF\x00\x00".b # 2 pixels, both non-zero
49
+ assert_equal 2, Gemba.count_changed_pixels(delta)
50
+ end
51
+
52
+ def test_count_mixed
53
+ # 3 pixels: changed, zero, changed
54
+ delta = "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01".b
55
+ assert_equal 2, Gemba.count_changed_pixels(delta)
56
+ end
57
+
58
+ def test_count_empty
59
+ assert_equal 0, Gemba.count_changed_pixels("".b)
60
+ end
61
+ end
metadata ADDED
@@ -0,0 +1,234 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gemba
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - James Cook
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: teek
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 0.1.2
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 0.1.2
26
+ - !ruby/object:Gem::Dependency
27
+ name: teek-sdl2
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.1.3
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 0.1.3
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '13.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '13.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rake-compiler
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: minitest
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '6.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '6.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: method_source
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: simplecov
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.22'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.22'
110
+ - !ruby/object:Gem::Dependency
111
+ name: listen
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '3.0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: rubyzip
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '2.4'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '2.4'
138
+ description: Wraps libmgba's mCore C API and provides a full-featured GBA player with
139
+ SDL2 rendering, input, save states, and a Tk-based settings UI
140
+ email:
141
+ - jcook.rubyist@gmail.com
142
+ executables:
143
+ - gemba
144
+ extensions:
145
+ - ext/gemba/extconf.rb
146
+ extra_rdoc_files: []
147
+ files:
148
+ - THIRD_PARTY_NOTICES
149
+ - assets/JetBrainsMonoNL-Regular.ttf
150
+ - assets/ark-pixel-12px-monospaced-ja.ttf
151
+ - bin/gemba
152
+ - ext/gemba/extconf.rb
153
+ - ext/gemba/gemba_ext.c
154
+ - ext/gemba/gemba_ext.h
155
+ - gemba.gemspec
156
+ - lib/gemba.rb
157
+ - lib/gemba/child_window.rb
158
+ - lib/gemba/cli.rb
159
+ - lib/gemba/config.rb
160
+ - lib/gemba/core.rb
161
+ - lib/gemba/headless.rb
162
+ - lib/gemba/headless_player.rb
163
+ - lib/gemba/hotkey_map.rb
164
+ - lib/gemba/input_mappings.rb
165
+ - lib/gemba/locale.rb
166
+ - lib/gemba/locales/en.yml
167
+ - lib/gemba/locales/ja.yml
168
+ - lib/gemba/method_coverage_service.rb
169
+ - lib/gemba/overlay_renderer.rb
170
+ - lib/gemba/player.rb
171
+ - lib/gemba/recorder.rb
172
+ - lib/gemba/recorder_decoder.rb
173
+ - lib/gemba/rom_info_window.rb
174
+ - lib/gemba/rom_loader.rb
175
+ - lib/gemba/runtime.rb
176
+ - lib/gemba/save_state_manager.rb
177
+ - lib/gemba/save_state_picker.rb
178
+ - lib/gemba/settings_window.rb
179
+ - lib/gemba/tip_service.rb
180
+ - lib/gemba/toast_overlay.rb
181
+ - lib/gemba/version.rb
182
+ - test/fixtures/test.gba
183
+ - test/fixtures/test.sav
184
+ - test/shared/screenshot_helper.rb
185
+ - test/shared/simplecov_config.rb
186
+ - test/shared/teek_test_worker.rb
187
+ - test/shared/tk_test_helper.rb
188
+ - test/support/input_mocks.rb
189
+ - test/support/player_helpers.rb
190
+ - test/test_cli.rb
191
+ - test/test_config.rb
192
+ - test/test_core.rb
193
+ - test/test_gamepad_map.rb
194
+ - test/test_headless_player.rb
195
+ - test/test_helper.rb
196
+ - test/test_hotkey_map.rb
197
+ - test/test_keyboard_map.rb
198
+ - test/test_locale.rb
199
+ - test/test_mgba.rb
200
+ - test/test_overlay_renderer.rb
201
+ - test/test_player.rb
202
+ - test/test_recorder.rb
203
+ - test/test_rom_loader.rb
204
+ - test/test_save_state_manager.rb
205
+ - test/test_settings_hotkeys.rb
206
+ - test/test_settings_window.rb
207
+ - test/test_tip_service.rb
208
+ - test/test_toast_overlay.rb
209
+ - test/test_virtual_keyboard.rb
210
+ - test/test_xor_delta.rb
211
+ homepage: https://github.com/jamescook/gemba
212
+ licenses:
213
+ - MIT
214
+ metadata: {}
215
+ rdoc_options: []
216
+ require_paths:
217
+ - lib
218
+ required_ruby_version: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '3.2'
223
+ required_rubygems_version: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - ">="
226
+ - !ruby/object:Gem::Version
227
+ version: '0'
228
+ requirements:
229
+ - libmgba development headers
230
+ - rubyzip gem >= 2.4 (optional, for loading ROMs from .zip files)
231
+ rubygems_version: 4.0.3
232
+ specification_version: 4
233
+ summary: GBA emulator frontend powered by teek and libmgba
234
+ test_files: []