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.
- checksums.yaml +7 -0
- data/THIRD_PARTY_NOTICES +113 -0
- data/assets/JetBrainsMonoNL-Regular.ttf +0 -0
- data/assets/ark-pixel-12px-monospaced-ja.ttf +0 -0
- data/bin/gemba +14 -0
- data/ext/gemba/extconf.rb +185 -0
- data/ext/gemba/gemba_ext.c +1051 -0
- data/ext/gemba/gemba_ext.h +15 -0
- data/gemba.gemspec +38 -0
- data/lib/gemba/child_window.rb +62 -0
- data/lib/gemba/cli.rb +384 -0
- data/lib/gemba/config.rb +621 -0
- data/lib/gemba/core.rb +121 -0
- data/lib/gemba/headless.rb +12 -0
- data/lib/gemba/headless_player.rb +206 -0
- data/lib/gemba/hotkey_map.rb +202 -0
- data/lib/gemba/input_mappings.rb +214 -0
- data/lib/gemba/locale.rb +92 -0
- data/lib/gemba/locales/en.yml +157 -0
- data/lib/gemba/locales/ja.yml +157 -0
- data/lib/gemba/method_coverage_service.rb +265 -0
- data/lib/gemba/overlay_renderer.rb +109 -0
- data/lib/gemba/player.rb +1515 -0
- data/lib/gemba/recorder.rb +156 -0
- data/lib/gemba/recorder_decoder.rb +325 -0
- data/lib/gemba/rom_info_window.rb +346 -0
- data/lib/gemba/rom_loader.rb +100 -0
- data/lib/gemba/runtime.rb +39 -0
- data/lib/gemba/save_state_manager.rb +155 -0
- data/lib/gemba/save_state_picker.rb +199 -0
- data/lib/gemba/settings_window.rb +1173 -0
- data/lib/gemba/tip_service.rb +133 -0
- data/lib/gemba/toast_overlay.rb +128 -0
- data/lib/gemba/version.rb +5 -0
- data/lib/gemba.rb +17 -0
- data/test/fixtures/test.gba +0 -0
- data/test/fixtures/test.sav +0 -0
- data/test/shared/screenshot_helper.rb +113 -0
- data/test/shared/simplecov_config.rb +59 -0
- data/test/shared/teek_test_worker.rb +388 -0
- data/test/shared/tk_test_helper.rb +354 -0
- data/test/support/input_mocks.rb +61 -0
- data/test/support/player_helpers.rb +77 -0
- data/test/test_cli.rb +281 -0
- data/test/test_config.rb +897 -0
- data/test/test_core.rb +401 -0
- data/test/test_gamepad_map.rb +116 -0
- data/test/test_headless_player.rb +205 -0
- data/test/test_helper.rb +19 -0
- data/test/test_hotkey_map.rb +396 -0
- data/test/test_keyboard_map.rb +108 -0
- data/test/test_locale.rb +159 -0
- data/test/test_mgba.rb +26 -0
- data/test/test_overlay_renderer.rb +199 -0
- data/test/test_player.rb +903 -0
- data/test/test_recorder.rb +180 -0
- data/test/test_rom_loader.rb +149 -0
- data/test/test_save_state_manager.rb +289 -0
- data/test/test_settings_hotkeys.rb +434 -0
- data/test/test_settings_window.rb +1039 -0
- data/test/test_tip_service.rb +138 -0
- data/test/test_toast_overlay.rb +216 -0
- data/test/test_virtual_keyboard.rb +39 -0
- data/test/test_xor_delta.rb +61 -0
- metadata +234 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Gemba
|
|
6
|
+
# Virtual keyboard device that tracks key press/release state.
|
|
7
|
+
# Presents the same interface as an SDL gamepad: +button?+ and +closed?+.
|
|
8
|
+
class VirtualKeyboard
|
|
9
|
+
def initialize
|
|
10
|
+
@held = Set.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def press(keysym) = @held.add(keysym)
|
|
14
|
+
def release(keysym) = @held.delete(keysym)
|
|
15
|
+
def button?(keysym) = @held.include?(keysym)
|
|
16
|
+
def closed? = false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# GBA button label → bitmask (shared by KeyboardMap and GamepadMap)
|
|
20
|
+
GBA_BTN_BITS = {
|
|
21
|
+
a: KEY_A, b: KEY_B,
|
|
22
|
+
l: KEY_L, r: KEY_R,
|
|
23
|
+
up: KEY_UP, down: KEY_DOWN,
|
|
24
|
+
left: KEY_LEFT, right: KEY_RIGHT,
|
|
25
|
+
start: KEY_START, select: KEY_SELECT,
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
# Manages keyboard keysym → GBA bitmask mappings.
|
|
29
|
+
#
|
|
30
|
+
# Shares the same interface as {GamepadMap} so that Player can
|
|
31
|
+
# delegate to either without knowing which device type is active.
|
|
32
|
+
class KeyboardMap
|
|
33
|
+
DEFAULT_MAP = {
|
|
34
|
+
'z' => KEY_A,
|
|
35
|
+
'x' => KEY_B,
|
|
36
|
+
'BackSpace' => KEY_SELECT,
|
|
37
|
+
'Return' => KEY_START,
|
|
38
|
+
'Right' => KEY_RIGHT,
|
|
39
|
+
'Left' => KEY_LEFT,
|
|
40
|
+
'Up' => KEY_UP,
|
|
41
|
+
'Down' => KEY_DOWN,
|
|
42
|
+
'a' => KEY_L,
|
|
43
|
+
's' => KEY_R,
|
|
44
|
+
}.freeze
|
|
45
|
+
|
|
46
|
+
def initialize(config)
|
|
47
|
+
@config = config
|
|
48
|
+
@map = DEFAULT_MAP.dup
|
|
49
|
+
@device = nil
|
|
50
|
+
load_config
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
attr_writer :device
|
|
54
|
+
|
|
55
|
+
def mask
|
|
56
|
+
return 0 unless @device
|
|
57
|
+
m = 0
|
|
58
|
+
@map.each { |key, bit| m |= bit if @device.button?(key) }
|
|
59
|
+
m
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def set(gba_btn, input_key)
|
|
63
|
+
bit = GBA_BTN_BITS[gba_btn] or return
|
|
64
|
+
@map.delete_if { |_, v| v == bit }
|
|
65
|
+
@map[input_key.to_s] = bit
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def reset!
|
|
69
|
+
@map = DEFAULT_MAP.dup
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def load_config
|
|
73
|
+
cfg = @config.mappings(Config::KEYBOARD_GUID)
|
|
74
|
+
if cfg.empty?
|
|
75
|
+
@map = DEFAULT_MAP.dup
|
|
76
|
+
else
|
|
77
|
+
@map = {}
|
|
78
|
+
cfg.each do |gba_str, keysym|
|
|
79
|
+
bit = GBA_BTN_BITS[gba_str.to_sym]
|
|
80
|
+
next unless bit
|
|
81
|
+
@map[keysym] = bit
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def reload!
|
|
87
|
+
@config.reload!
|
|
88
|
+
load_config
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def labels
|
|
92
|
+
result = {}
|
|
93
|
+
@map.each do |input, bit|
|
|
94
|
+
gba_btn = GBA_BTN_BITS.key(bit)
|
|
95
|
+
result[gba_btn] = input if gba_btn
|
|
96
|
+
end
|
|
97
|
+
result
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def save_to_config
|
|
101
|
+
@map.each do |input, bit|
|
|
102
|
+
gba_btn = GBA_BTN_BITS.key(bit)
|
|
103
|
+
@config.set_mapping(Config::KEYBOARD_GUID, gba_btn, input) if gba_btn
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def supports_deadzone? = false
|
|
108
|
+
def dead_zone_pct = 0
|
|
109
|
+
|
|
110
|
+
def set_dead_zone(_)
|
|
111
|
+
raise NotImplementedError, "keyboard does not support dead zones"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Manages SDL gamepad button → GBA bitmask mappings.
|
|
116
|
+
#
|
|
117
|
+
# Shares the same interface as {KeyboardMap} so that Player can
|
|
118
|
+
# delegate to either without knowing which device type is active.
|
|
119
|
+
class GamepadMap
|
|
120
|
+
DEFAULT_MAP = {
|
|
121
|
+
a: KEY_A,
|
|
122
|
+
b: KEY_B,
|
|
123
|
+
back: KEY_SELECT,
|
|
124
|
+
start: KEY_START,
|
|
125
|
+
dpad_up: KEY_UP,
|
|
126
|
+
dpad_down: KEY_DOWN,
|
|
127
|
+
dpad_left: KEY_LEFT,
|
|
128
|
+
dpad_right: KEY_RIGHT,
|
|
129
|
+
left_shoulder: KEY_L,
|
|
130
|
+
right_shoulder: KEY_R,
|
|
131
|
+
}.freeze
|
|
132
|
+
|
|
133
|
+
DEFAULT_DEAD_ZONE = 8000
|
|
134
|
+
|
|
135
|
+
def initialize(config)
|
|
136
|
+
@config = config
|
|
137
|
+
@map = DEFAULT_MAP.dup
|
|
138
|
+
@device = nil
|
|
139
|
+
@dead_zone = DEFAULT_DEAD_ZONE
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
attr_accessor :device
|
|
143
|
+
attr_reader :dead_zone
|
|
144
|
+
|
|
145
|
+
def mask
|
|
146
|
+
return 0 unless @device && !@device.closed?
|
|
147
|
+
m = 0
|
|
148
|
+
@map.each { |btn, bit| m |= bit if @device.button?(btn) }
|
|
149
|
+
m
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def set(gba_btn, gp_btn)
|
|
153
|
+
bit = GBA_BTN_BITS[gba_btn] or return
|
|
154
|
+
@map.delete_if { |_, v| v == bit }
|
|
155
|
+
@map[gp_btn] = bit
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def reset!
|
|
159
|
+
@map = DEFAULT_MAP.dup
|
|
160
|
+
@dead_zone = DEFAULT_DEAD_ZONE
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def load_config
|
|
164
|
+
return unless @device
|
|
165
|
+
guid = @device.guid rescue return
|
|
166
|
+
gp_cfg = @config.gamepad(guid, name: @device.name)
|
|
167
|
+
|
|
168
|
+
@map = {}
|
|
169
|
+
gp_cfg['mappings'].each do |gba_str, gp_str|
|
|
170
|
+
bit = GBA_BTN_BITS[gba_str.to_sym]
|
|
171
|
+
next unless bit
|
|
172
|
+
@map[gp_str.to_sym] = bit
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
pct = gp_cfg['dead_zone']
|
|
176
|
+
@dead_zone = (pct / 100.0 * 32767).round
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def reload!
|
|
180
|
+
@config.reload!
|
|
181
|
+
load_config
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def labels
|
|
185
|
+
result = {}
|
|
186
|
+
@map.each do |input, bit|
|
|
187
|
+
gba_btn = GBA_BTN_BITS.key(bit)
|
|
188
|
+
result[gba_btn] = input.to_s if gba_btn
|
|
189
|
+
end
|
|
190
|
+
result
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def save_to_config
|
|
194
|
+
return unless @device
|
|
195
|
+
guid = @device.guid rescue return
|
|
196
|
+
@config.gamepad(guid, name: @device.name)
|
|
197
|
+
@config.set_dead_zone(guid, dead_zone_pct)
|
|
198
|
+
@map.each do |gp_btn, bit|
|
|
199
|
+
gba_btn = GBA_BTN_BITS.key(bit)
|
|
200
|
+
@config.set_mapping(guid, gba_btn, gp_btn) if gba_btn
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def supports_deadzone? = true
|
|
205
|
+
|
|
206
|
+
def dead_zone_pct
|
|
207
|
+
(@dead_zone.to_f / 32767 * 100).round
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def set_dead_zone(threshold)
|
|
211
|
+
@dead_zone = threshold.to_i
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
data/lib/gemba/locale.rb
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module Gemba
|
|
6
|
+
# Lightweight YAML-backed localization. No external gem dependencies.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# Locale.load('ja')
|
|
10
|
+
# Locale.translate('menu.file') #=> "ファイル"
|
|
11
|
+
# Locale.translate('toast.state_saved', slot: 3) #=> "スロット3にステートをセーブしました"
|
|
12
|
+
module Locale
|
|
13
|
+
# Load translations for the given language code.
|
|
14
|
+
# Falls back to English if the requested locale file doesn't exist.
|
|
15
|
+
#
|
|
16
|
+
# @param lang [String, nil] two-letter language code (e.g. 'en', 'ja').
|
|
17
|
+
# When nil, auto-detects from the OS environment.
|
|
18
|
+
# @return [String] the language code that was loaded
|
|
19
|
+
def self.load(lang = nil)
|
|
20
|
+
lang = detect_language if lang.nil? || lang == 'auto'
|
|
21
|
+
path = locale_path(lang)
|
|
22
|
+
path = locale_path('en') unless File.exist?(path)
|
|
23
|
+
@strings = YAML.safe_load_file(path)
|
|
24
|
+
@lang = lang
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Look up a translation by dot-separated key, with optional variable
|
|
28
|
+
# interpolation. Returns the key itself if no translation is found.
|
|
29
|
+
#
|
|
30
|
+
# @param key [String] dot-separated path (e.g. 'menu.file', 'toast.state_saved')
|
|
31
|
+
# @param vars [Hash] interpolation variables — replaces `{name}` in the string
|
|
32
|
+
# @return [String]
|
|
33
|
+
def self.translate(key, **vars)
|
|
34
|
+
parts = key.split('.')
|
|
35
|
+
str = @strings&.dig(*parts)
|
|
36
|
+
return key unless str.is_a?(String)
|
|
37
|
+
|
|
38
|
+
vars.each { |k, v| str = str.gsub("{#{k}}", v.to_s) }
|
|
39
|
+
str
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class << self
|
|
43
|
+
alias_method :t, :translate
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @return [String] the currently loaded language code
|
|
47
|
+
def self.language
|
|
48
|
+
@lang
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [Array<String>] sorted list of available language codes
|
|
52
|
+
def self.available_languages
|
|
53
|
+
Dir[locale_path('*')].map { |f| File.basename(f, '.yml') }.sort
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Detect the user's preferred language from environment variables.
|
|
57
|
+
# @return [String] two-letter language code (e.g. 'en', 'ja')
|
|
58
|
+
# @api private
|
|
59
|
+
private_class_method def self.detect_language
|
|
60
|
+
env = ENV['LANG'] || ENV['LC_ALL'] || ENV['LANGUAGE'] || 'en'
|
|
61
|
+
env[0, 2].downcase
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @api private
|
|
65
|
+
private_class_method def self.locale_path(lang)
|
|
66
|
+
File.join(__dir__, 'locales', "#{lang}.yml")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Auto-load English on first require so translate() always works,
|
|
70
|
+
# even when loaded outside the full Gemba boot path.
|
|
71
|
+
load('en') unless @strings
|
|
72
|
+
|
|
73
|
+
# Mixin for classes that need translation access.
|
|
74
|
+
# Include this to call `translate` / `t` as instance methods.
|
|
75
|
+
#
|
|
76
|
+
# @example
|
|
77
|
+
# class Player
|
|
78
|
+
# include Gemba::Locale::Translatable
|
|
79
|
+
# def build_menu
|
|
80
|
+
# label: translate('menu.file')
|
|
81
|
+
# end
|
|
82
|
+
# end
|
|
83
|
+
module Translatable
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def translate(key, **vars)
|
|
87
|
+
Locale.translate(key, **vars)
|
|
88
|
+
end
|
|
89
|
+
alias_method :t, :translate
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
menu:
|
|
2
|
+
file: "File"
|
|
3
|
+
open_rom: "Open ROM…"
|
|
4
|
+
recent: "Recent"
|
|
5
|
+
quit: "Quit"
|
|
6
|
+
settings: "Settings"
|
|
7
|
+
view: "View"
|
|
8
|
+
fullscreen: "Fullscreen"
|
|
9
|
+
rom_info: "ROM Info…"
|
|
10
|
+
emulation: "Emulation"
|
|
11
|
+
pause: "Pause"
|
|
12
|
+
resume: "Resume"
|
|
13
|
+
reset: "Reset"
|
|
14
|
+
quick_save: "Quick Save"
|
|
15
|
+
quick_load: "Quick Load"
|
|
16
|
+
save_states: "Save States…"
|
|
17
|
+
start_recording: "Start Recording"
|
|
18
|
+
stop_recording: "Stop Recording"
|
|
19
|
+
|
|
20
|
+
toast:
|
|
21
|
+
save_blocked: "Save blocked (too fast)"
|
|
22
|
+
state_saved: "State saved to slot {slot}"
|
|
23
|
+
save_failed: "Failed to save state"
|
|
24
|
+
no_state: "No state in slot {slot}"
|
|
25
|
+
state_loaded: "State loaded from slot {slot}"
|
|
26
|
+
load_failed: "Failed to load state"
|
|
27
|
+
loaded_sav: "Loaded {name}"
|
|
28
|
+
created_sav: "Created {name}"
|
|
29
|
+
screenshot_saved: "Screenshot saved: {name}"
|
|
30
|
+
screenshot_failed: "Screenshot failed"
|
|
31
|
+
rewound: "Rewound"
|
|
32
|
+
no_rewind: "No rewind data"
|
|
33
|
+
paused: "Paused"
|
|
34
|
+
waiting_for: "Waiting for {label}…"
|
|
35
|
+
recording_started: "Recording started"
|
|
36
|
+
recording_stopped: "Recorded {frames} frames"
|
|
37
|
+
|
|
38
|
+
dialog:
|
|
39
|
+
game_running_title: "Game Running"
|
|
40
|
+
game_running_msg: "Another game is running. Switch to {name}?"
|
|
41
|
+
drop_error_title: "Drop Error"
|
|
42
|
+
drop_single_file_only: "Please drop a single ROM file."
|
|
43
|
+
drop_unsupported_type: "Unsupported file type: {ext}"
|
|
44
|
+
no_rom_in_zip: "No ROM file found in {name}"
|
|
45
|
+
multiple_roms_in_zip: "Multiple ROM files found in {name}"
|
|
46
|
+
zip_read_error: "Cannot read ZIP file: {detail}"
|
|
47
|
+
rom_not_found_title: "ROM Not Found"
|
|
48
|
+
rom_not_found_msg: "The ROM file no longer exists:\n{path}"
|
|
49
|
+
reset_gamepad_title: "Reset Gamepad"
|
|
50
|
+
reset_gamepad_msg: "Reset all gamepad mappings and dead zone to defaults?"
|
|
51
|
+
reset_hotkeys_title: "Reset Hotkeys"
|
|
52
|
+
reset_hotkeys_msg: "Reset all hotkey bindings to defaults?"
|
|
53
|
+
key_conflict_title: "Key Conflict"
|
|
54
|
+
|
|
55
|
+
settings:
|
|
56
|
+
video: "Video"
|
|
57
|
+
audio: "Audio"
|
|
58
|
+
gamepad: "Gamepad"
|
|
59
|
+
save_states: "Save States"
|
|
60
|
+
window_scale: "Window Scale:"
|
|
61
|
+
turbo_speed: "Turbo Speed:"
|
|
62
|
+
toast_duration: "Toast Duration:"
|
|
63
|
+
maintain_aspect: "Maintain aspect ratio"
|
|
64
|
+
show_fps: "Show FPS"
|
|
65
|
+
pause_on_focus_loss: "Pause when window loses focus"
|
|
66
|
+
volume: "Volume:"
|
|
67
|
+
mute: "Mute"
|
|
68
|
+
dead_zone: "Dead zone:"
|
|
69
|
+
undo: "Undo"
|
|
70
|
+
reset_defaults: "Reset to Defaults"
|
|
71
|
+
quick_save_slot: "Quick Save Slot:"
|
|
72
|
+
keep_backup: "Keep backup of previous save state"
|
|
73
|
+
save: "Save"
|
|
74
|
+
key_up: "↑ Up"
|
|
75
|
+
key_down: "↓ Down"
|
|
76
|
+
key_left: "← Left"
|
|
77
|
+
key_right: "→ Right"
|
|
78
|
+
keyboard_only: "Keyboard Only"
|
|
79
|
+
no_gamepad: "No gamepad detected"
|
|
80
|
+
uncapped: "Uncapped"
|
|
81
|
+
press: "Press…"
|
|
82
|
+
open_config_folder: "Open Config Folder…"
|
|
83
|
+
hotkeys: "Hotkeys"
|
|
84
|
+
hk_quit: "Quit"
|
|
85
|
+
hk_pause: "Pause"
|
|
86
|
+
hk_fast_forward: "Fast Forward"
|
|
87
|
+
hk_fullscreen: "Fullscreen"
|
|
88
|
+
hk_show_fps: "Show FPS"
|
|
89
|
+
hk_quick_save: "Quick Save"
|
|
90
|
+
hk_quick_load: "Quick Load"
|
|
91
|
+
hk_save_states: "Save States"
|
|
92
|
+
hk_screenshot: "Screenshot"
|
|
93
|
+
hk_rewind: "Rewind"
|
|
94
|
+
hk_record: "Record"
|
|
95
|
+
hk_reset_defaults: "Reset to Defaults"
|
|
96
|
+
pixel_filter: "Pixel Filter:"
|
|
97
|
+
filter_nearest: "Nearest Neighbor"
|
|
98
|
+
filter_linear: "Bilinear"
|
|
99
|
+
integer_scale: "Integer scaling (no fractional pixels)"
|
|
100
|
+
color_correction: "GBA color correction"
|
|
101
|
+
frame_blending: "Frame blending (LCD ghosting)"
|
|
102
|
+
rewind: "Rewind (jump back in time)"
|
|
103
|
+
tip_rewind: "Saves periodic snapshots during gameplay.\nPress the rewind hotkey to jump back ~10 seconds."
|
|
104
|
+
per_game: "Per-game settings"
|
|
105
|
+
tip_pixel_filter: "Nearest: sharp pixels (original look). Bilinear: smoothed, slightly blurry."
|
|
106
|
+
tip_integer_scale: "Scales by whole numbers only to avoid blurry sub-pixel artifacts."
|
|
107
|
+
tip_color_correction: "Adjusts colors to match the original GBA LCD gamma and color crosstalk."
|
|
108
|
+
tip_frame_blending: "Blends consecutive frames to simulate the GBA LCD's slow pixel response.\nFixes flickering transparency effects in some games (Golden Sun, F-Zero)."
|
|
109
|
+
tip_dead_zone: "How far the analog stick must move before registering input.\nIncrease if you experience phantom inputs."
|
|
110
|
+
tip_keep_backup: "Saves a backup copy before overwriting a save state."
|
|
111
|
+
tip_per_game: "Save separate video, audio, and save state settings for each ROM."
|
|
112
|
+
tip_turbo_speed: "Fast-forward speed when holding the turbo hotkey."
|
|
113
|
+
tip_toast_duration: "How long on-screen notifications stay visible."
|
|
114
|
+
recording: "Recording"
|
|
115
|
+
recording_compression: "Compression:"
|
|
116
|
+
tip_recording_compression: "Zlib level for .grec files.\n1 = fastest (default), 6+ has diminishing returns."
|
|
117
|
+
recordings_folder: "Recordings folder:"
|
|
118
|
+
open_recordings_folder: "Open Recordings Folder…"
|
|
119
|
+
gp_a: "A"
|
|
120
|
+
gp_b: "B"
|
|
121
|
+
gp_l: "L"
|
|
122
|
+
gp_r: "R"
|
|
123
|
+
gp_up: "Up"
|
|
124
|
+
gp_down: "Down"
|
|
125
|
+
gp_left: "Left"
|
|
126
|
+
gp_right: "Right"
|
|
127
|
+
gp_start: "Start"
|
|
128
|
+
gp_select: "Select"
|
|
129
|
+
|
|
130
|
+
picker:
|
|
131
|
+
title: "Save States"
|
|
132
|
+
empty: "Empty"
|
|
133
|
+
no_preview: "No preview"
|
|
134
|
+
slot: "Slot {n}"
|
|
135
|
+
close: "Close"
|
|
136
|
+
|
|
137
|
+
rom_info:
|
|
138
|
+
title: "ROM Info"
|
|
139
|
+
field_title: "Title:"
|
|
140
|
+
game_code: "Game Code:"
|
|
141
|
+
publisher: "Publisher:"
|
|
142
|
+
platform: "Platform:"
|
|
143
|
+
rom_size: "ROM Size:"
|
|
144
|
+
checksum: "Checksum:"
|
|
145
|
+
rom_file: "ROM File:"
|
|
146
|
+
save_file: "Save File:"
|
|
147
|
+
resolution: "Resolution:"
|
|
148
|
+
close: "Close"
|
|
149
|
+
na: "N/A"
|
|
150
|
+
|
|
151
|
+
player:
|
|
152
|
+
open_rom_hint: "File > Open ROM…"
|
|
153
|
+
fps: "{fps} fps"
|
|
154
|
+
ff: ">> {speed}x"
|
|
155
|
+
ff_max: ">> MAX"
|
|
156
|
+
none: "(none)"
|
|
157
|
+
clear: "Clear"
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
menu:
|
|
2
|
+
file: "ファイル"
|
|
3
|
+
open_rom: "ROMを開く…"
|
|
4
|
+
recent: "最近使ったファイル"
|
|
5
|
+
quit: "終了"
|
|
6
|
+
settings: "設定"
|
|
7
|
+
view: "表示"
|
|
8
|
+
fullscreen: "フルスクリーン"
|
|
9
|
+
rom_info: "ROM情報…"
|
|
10
|
+
emulation: "エミュレーション"
|
|
11
|
+
pause: "一時停止"
|
|
12
|
+
resume: "再開"
|
|
13
|
+
reset: "リセット"
|
|
14
|
+
quick_save: "クイックセーブ"
|
|
15
|
+
quick_load: "クイックロード"
|
|
16
|
+
save_states: "ステートセーブ…"
|
|
17
|
+
start_recording: "録画開始"
|
|
18
|
+
stop_recording: "録画停止"
|
|
19
|
+
|
|
20
|
+
toast:
|
|
21
|
+
save_blocked: "セーブ制限中(間隔が短すぎます)"
|
|
22
|
+
state_saved: "スロット{slot}にステートをセーブしました"
|
|
23
|
+
save_failed: "ステートセーブに失敗しました"
|
|
24
|
+
no_state: "スロット{slot}にステートがありません"
|
|
25
|
+
state_loaded: "スロット{slot}からステートをロードしました"
|
|
26
|
+
load_failed: "ステートロードに失敗しました"
|
|
27
|
+
loaded_sav: "{name}を読み込みました"
|
|
28
|
+
created_sav: "{name}を作成しました"
|
|
29
|
+
screenshot_saved: "スクリーンショットを保存: {name}"
|
|
30
|
+
screenshot_failed: "スクリーンショットに失敗しました"
|
|
31
|
+
rewound: "巻き戻しました"
|
|
32
|
+
no_rewind: "巻き戻しデータがありません"
|
|
33
|
+
paused: "一時停止"
|
|
34
|
+
waiting_for: "{label}を待機中…"
|
|
35
|
+
recording_started: "録画を開始しました"
|
|
36
|
+
recording_stopped: "{frames}フレームを録画しました"
|
|
37
|
+
|
|
38
|
+
dialog:
|
|
39
|
+
game_running_title: "ゲーム実行中"
|
|
40
|
+
game_running_msg: "別のゲームが実行中です。{name}に切り替えますか?"
|
|
41
|
+
drop_error_title: "ドロップエラー"
|
|
42
|
+
drop_single_file_only: "ROMファイルを1つだけドロップしてください。"
|
|
43
|
+
drop_unsupported_type: "対応していないファイル形式: {ext}"
|
|
44
|
+
no_rom_in_zip: "{name}にROMファイルが見つかりません"
|
|
45
|
+
multiple_roms_in_zip: "{name}に複数のROMファイルがあります"
|
|
46
|
+
zip_read_error: "ZIPファイルを読み込めません: {detail}"
|
|
47
|
+
rom_not_found_title: "ROMが見つかりません"
|
|
48
|
+
rom_not_found_msg: "ROMファイルが存在しません:\n{path}"
|
|
49
|
+
reset_gamepad_title: "ゲームパッドをリセット"
|
|
50
|
+
reset_gamepad_msg: "すべてのゲームパッド設定をデフォルトに戻しますか?"
|
|
51
|
+
reset_hotkeys_title: "ホットキーをリセット"
|
|
52
|
+
reset_hotkeys_msg: "すべてのホットキー設定をデフォルトに戻しますか?"
|
|
53
|
+
key_conflict_title: "キーの競合"
|
|
54
|
+
|
|
55
|
+
settings:
|
|
56
|
+
video: "映像"
|
|
57
|
+
audio: "音声"
|
|
58
|
+
gamepad: "ゲームパッド"
|
|
59
|
+
save_states: "ステートセーブ"
|
|
60
|
+
window_scale: "ウィンドウ倍率:"
|
|
61
|
+
turbo_speed: "早送り速度:"
|
|
62
|
+
toast_duration: "通知表示時間:"
|
|
63
|
+
maintain_aspect: "アスペクト比を維持"
|
|
64
|
+
show_fps: "FPSを表示"
|
|
65
|
+
pause_on_focus_loss: "ウィンドウのフォーカスが外れたら一時停止"
|
|
66
|
+
volume: "音量:"
|
|
67
|
+
mute: "ミュート"
|
|
68
|
+
dead_zone: "デッドゾーン:"
|
|
69
|
+
undo: "元に戻す"
|
|
70
|
+
reset_defaults: "デフォルトに戻す"
|
|
71
|
+
quick_save_slot: "クイックセーブスロット:"
|
|
72
|
+
keep_backup: "ステートセーブのバックアップを保持"
|
|
73
|
+
save: "保存"
|
|
74
|
+
key_up: "↑ 上"
|
|
75
|
+
key_down: "↓ 下"
|
|
76
|
+
key_left: "← 左"
|
|
77
|
+
key_right: "→ 右"
|
|
78
|
+
keyboard_only: "キーボードのみ"
|
|
79
|
+
no_gamepad: "ゲームパッドが検出されません"
|
|
80
|
+
uncapped: "無制限"
|
|
81
|
+
press: "入力待ち…"
|
|
82
|
+
open_config_folder: "設定フォルダを開く…"
|
|
83
|
+
hotkeys: "ホットキー"
|
|
84
|
+
hk_quit: "終了"
|
|
85
|
+
hk_pause: "一時停止"
|
|
86
|
+
hk_fast_forward: "早送り"
|
|
87
|
+
hk_fullscreen: "フルスクリーン"
|
|
88
|
+
hk_show_fps: "FPS表示"
|
|
89
|
+
hk_quick_save: "クイックセーブ"
|
|
90
|
+
hk_quick_load: "クイックロード"
|
|
91
|
+
hk_save_states: "ステートセーブ"
|
|
92
|
+
hk_screenshot: "スクリーンショット"
|
|
93
|
+
hk_rewind: "巻き戻し"
|
|
94
|
+
hk_record: "録画"
|
|
95
|
+
hk_reset_defaults: "デフォルトに戻す"
|
|
96
|
+
pixel_filter: "ピクセルフィルタ:"
|
|
97
|
+
filter_nearest: "ニアレストネイバー"
|
|
98
|
+
filter_linear: "バイリニア"
|
|
99
|
+
integer_scale: "整数スケーリング(端数ピクセルなし)"
|
|
100
|
+
color_correction: "GBA色補正"
|
|
101
|
+
frame_blending: "フレームブレンド(LCD残像)"
|
|
102
|
+
rewind: "巻き戻し(過去に戻る)"
|
|
103
|
+
tip_rewind: "プレイ中に定期的にスナップショットを保存します。\n巻き戻しホットキーで約10秒前に戻れます。"
|
|
104
|
+
per_game: "ゲーム別設定"
|
|
105
|
+
tip_pixel_filter: "ニアレスト: シャープなピクセル(原寸)。バイリニア: 滑らか、やや不鮮明。"
|
|
106
|
+
tip_integer_scale: "整数倍のみでスケーリングし、ぼやけたサブピクセルを防止します。"
|
|
107
|
+
tip_color_correction: "GBA液晶のガンマとカラークロストークに合わせて色を補正します。"
|
|
108
|
+
tip_frame_blending: "連続フレームをブレンドしてGBA液晶の遅い応答を再現します。\n一部のゲーム(黄金の太陽、F-Zero)のちらつき透明効果を修正します。"
|
|
109
|
+
tip_dead_zone: "入力を認識するまでのアナログスティックの遊び幅。\n誤入力が発生する場合は増やしてください。"
|
|
110
|
+
tip_keep_backup: "ステートを上書きする前にバックアップを保存します。"
|
|
111
|
+
tip_per_game: "ROM毎に映像・音声・ステートセーブの設定を個別に保存します。"
|
|
112
|
+
tip_turbo_speed: "早送りホットキー使用時の速度。"
|
|
113
|
+
tip_toast_duration: "画面上の通知の表示時間。"
|
|
114
|
+
recording: "録画"
|
|
115
|
+
recording_compression: "圧縮レベル:"
|
|
116
|
+
tip_recording_compression: ".grecファイルのzlib圧縮レベル。\n1 = 最速(デフォルト)、6以上は効果が小さくなります。"
|
|
117
|
+
recordings_folder: "録画フォルダ:"
|
|
118
|
+
open_recordings_folder: "録画フォルダを開く…"
|
|
119
|
+
gp_a: "A"
|
|
120
|
+
gp_b: "B"
|
|
121
|
+
gp_l: "L"
|
|
122
|
+
gp_r: "R"
|
|
123
|
+
gp_up: "上"
|
|
124
|
+
gp_down: "下"
|
|
125
|
+
gp_left: "左"
|
|
126
|
+
gp_right: "右"
|
|
127
|
+
gp_start: "スタート"
|
|
128
|
+
gp_select: "セレクト"
|
|
129
|
+
|
|
130
|
+
picker:
|
|
131
|
+
title: "ステートセーブ"
|
|
132
|
+
empty: "空"
|
|
133
|
+
no_preview: "プレビューなし"
|
|
134
|
+
slot: "スロット{n}"
|
|
135
|
+
close: "閉じる"
|
|
136
|
+
|
|
137
|
+
rom_info:
|
|
138
|
+
title: "ROM情報"
|
|
139
|
+
field_title: "タイトル:"
|
|
140
|
+
game_code: "ゲームコード:"
|
|
141
|
+
publisher: "メーカー:"
|
|
142
|
+
platform: "プラットフォーム:"
|
|
143
|
+
rom_size: "ROMサイズ:"
|
|
144
|
+
checksum: "チェックサム:"
|
|
145
|
+
rom_file: "ROMファイル:"
|
|
146
|
+
save_file: "セーブファイル:"
|
|
147
|
+
resolution: "解像度:"
|
|
148
|
+
close: "閉じる"
|
|
149
|
+
na: "N/A"
|
|
150
|
+
|
|
151
|
+
player:
|
|
152
|
+
open_rom_hint: "ファイル > ROMを開く…"
|
|
153
|
+
fps: "{fps} fps"
|
|
154
|
+
ff: ">> {speed}x"
|
|
155
|
+
ff_max: ">> 最大"
|
|
156
|
+
none: "(なし)"
|
|
157
|
+
clear: "クリア"
|