Skip to content

Commit fa6f383

Browse files
committed
feat: 统一虚拟键码配置
- IO4、Aime 和 Autoplay 共用支持键名与数字输入的 VirtualKey 类型 - 兼容旧十进制及十六进制配置,规范化保存时统一输出 0xNN - 将运行时消费者改为读取已解析键码并补充配置与示例回归测试
1 parent 8a11bbf commit fa6f383

11 files changed

Lines changed: 275 additions & 116 deletions

File tree

packages/applechu-schema/src/example.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,8 @@ mod tests {
127127
assert!(!example.contains("EnableConsole"));
128128
assert!(full.contains("#EnableConsole = true"));
129129
assert!(full.contains("#AppendConfigArgs = true"));
130+
assert!(example.contains("#Test = 0x70"));
131+
assert!(example.contains("#Hotkey = 0x24"));
132+
assert!(full.contains("#Scan = 0x0D"));
130133
}
131134
}

packages/applechu/src/aime/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use once_cell::sync::Lazy;
99

1010
#[cfg(target_arch = "x86_64")]
1111
use crate::config::Config;
12+
use crate::config::VirtualKey;
1213
use crate::iohook::uart;
1314
use crate::iohook::{self, Irp, IrpOp};
1415
#[cfg(target_arch = "x86_64")]
@@ -76,9 +77,9 @@ crate::config_section! {
7677
pub felica_gen: bool = false,
7778
advanced: true,
7879
comment: "缺少 felica.txt 卡号时自动生成";
79-
pub scan: i32 = 0x0D,
80+
pub scan: VirtualKey = VirtualKey::new(0x0D),
8081
advanced: true,
81-
comment: "读卡按键的虚拟键码";
82+
comment: "读卡按键";
8283
// 0 表示按机台模式选择:CVT=Gen2,SP=Gen3
8384
pub gen: u8 = 0,
8485
advanced: true,
@@ -438,7 +439,7 @@ fn load_config(config: &AimeSectionConfig, base_dir: impl AsRef<Path>) -> AimeCo
438439
authdata_path: resolve_path(base_dir.as_ref(), &config.authdata_path),
439440
aime_gen: config.aime_gen,
440441
felica_gen: config.felica_gen,
441-
scan_key: config.scan,
442+
scan_key: config.scan.code(),
442443
gen: config.gen,
443444
proxy_flag: config.proxy_flag,
444445
}

packages/applechu/src/autoplay/autoplay.rs

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@ extern "system" {
3838
}
3939
const VK_HOME: i32 = 0x24;
4040

41-
pub fn init(api: &Api, hotkey: &str) {
41+
pub fn init(api: &Api, hotkey: i32) {
4242
let _ = API_HANDLE.set(*api);
43-
let hotkey = parse_hotkey(hotkey).unwrap_or(VK_HOME);
4443
HOTKEY.store(hotkey, Ordering::Relaxed);
4544
RUNNING.store(true, Ordering::Relaxed);
4645

@@ -463,53 +462,6 @@ fn jump_target(text: &[u8], text_base: usize, target: usize) -> Option<usize> {
463462
Some(target.wrapping_add(5).wrapping_add(rel as usize))
464463
}
465464

466-
fn parse_hotkey(value: &str) -> Option<i32> {
467-
let key = value.trim();
468-
if key.is_empty() {
469-
return None;
470-
}
471-
if let Some(hex) = key.strip_prefix("0x").or_else(|| key.strip_prefix("0X")) {
472-
return i32::from_str_radix(hex, 16).ok().filter(|value| *value > 0);
473-
}
474-
if let Ok(value) = key.parse::<i32>() {
475-
return (value > 0).then_some(value);
476-
}
477-
478-
let normalized = key
479-
.trim_start_matches("VK_")
480-
.replace([' ', '-', '_'], "")
481-
.to_ascii_uppercase();
482-
match normalized.as_str() {
483-
"BACKSPACE" | "BACK" => Some(0x08),
484-
"TAB" => Some(0x09),
485-
"ENTER" | "RETURN" => Some(0x0D),
486-
"SHIFT" => Some(0x10),
487-
"CTRL" | "CONTROL" => Some(0x11),
488-
"ALT" | "MENU" => Some(0x12),
489-
"PAUSE" => Some(0x13),
490-
"CAPSLOCK" | "CAPITAL" => Some(0x14),
491-
"ESC" | "ESCAPE" => Some(0x1B),
492-
"SPACE" => Some(0x20),
493-
"PAGEUP" | "PRIOR" => Some(0x21),
494-
"PAGEDOWN" | "NEXT" => Some(0x22),
495-
"END" => Some(0x23),
496-
"HOME" => Some(VK_HOME),
497-
"LEFT" => Some(0x25),
498-
"UP" => Some(0x26),
499-
"RIGHT" => Some(0x27),
500-
"DOWN" => Some(0x28),
501-
"INSERT" => Some(0x2D),
502-
"DELETE" | "DEL" => Some(0x2E),
503-
key if key.len() == 1 => key.as_bytes().first().copied().map(i32::from),
504-
key if key.starts_with('F') => key[1..]
505-
.parse::<i32>()
506-
.ok()
507-
.filter(|value| (1..=24).contains(value))
508-
.map(|value| 0x70 + value - 1),
509-
_ => None,
510-
}
511-
}
512-
513465
fn hotkey_name(vk: i32) -> String {
514466
match vk {
515467
VK_HOME => "Home".to_owned(),

packages/applechu/src/autoplay/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub fn was_used() -> bool {
1414
false
1515
}
1616

17-
use crate::config::Config;
17+
use crate::config::{Config, VirtualKey};
1818
use crate::util::api::Api;
1919

2020
crate::config_section! {
@@ -29,7 +29,7 @@ crate::config_section! {
2929
description_en: "Only blocks score data, settings/progress saved normally",
3030
comment: "自动游玩",
3131
fields: {
32-
pub hotkey: String = String::from("Home"),
32+
pub hotkey: VirtualKey = VirtualKey::new(0x24),
3333
description: "支持 Home、Insert、F1 或虚拟键码",
3434
description_en: "Supports Home, Insert, F1, or a virtual-key code",
3535
comment: "自动游玩切换按键";
@@ -45,7 +45,7 @@ crate::config_section! {
4545
pub fn init_all(api: &Api, config: &AutoplayConfig) {
4646
#[cfg(target_arch = "x86")]
4747
{
48-
autoplay::init(api, &config.hotkey);
48+
autoplay::init(api, config.hotkey.code());
4949
autoplay::init_upload_guard(api);
5050
}
5151
#[cfg(not(target_arch = "x86"))]

packages/applechu/src/chuniio/config.rs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,51 @@ impl ChuniIoConfig {
6161
.map_or_else(crate::io4::Io4Config::default, |value| (*value).clone());
6262

6363
Self {
64-
vk_test: io4.test,
65-
vk_service: io4.service,
66-
vk_coin: io4.coin,
67-
vk_ir_emu: io4.ir,
68-
vk_ir: [io4.air1, io4.air2, io4.air3, io4.air4, io4.air5, io4.air6],
64+
vk_test: io4.test.code(),
65+
vk_service: io4.service.code(),
66+
vk_coin: io4.coin.code(),
67+
vk_ir_emu: io4.ir.code(),
68+
vk_ir: [
69+
io4.air1.code(),
70+
io4.air2.code(),
71+
io4.air3.code(),
72+
io4.air4.code(),
73+
io4.air5.code(),
74+
io4.air6.code(),
75+
],
6976
vk_cell: [
70-
io4.cell1, io4.cell2, io4.cell3, io4.cell4, io4.cell5, io4.cell6, io4.cell7,
71-
io4.cell8, io4.cell9, io4.cell10, io4.cell11, io4.cell12, io4.cell13, io4.cell14,
72-
io4.cell15, io4.cell16, io4.cell17, io4.cell18, io4.cell19, io4.cell20, io4.cell21,
73-
io4.cell22, io4.cell23, io4.cell24, io4.cell25, io4.cell26, io4.cell27, io4.cell28,
74-
io4.cell29, io4.cell30, io4.cell31, io4.cell32,
77+
io4.cell1.code(),
78+
io4.cell2.code(),
79+
io4.cell3.code(),
80+
io4.cell4.code(),
81+
io4.cell5.code(),
82+
io4.cell6.code(),
83+
io4.cell7.code(),
84+
io4.cell8.code(),
85+
io4.cell9.code(),
86+
io4.cell10.code(),
87+
io4.cell11.code(),
88+
io4.cell12.code(),
89+
io4.cell13.code(),
90+
io4.cell14.code(),
91+
io4.cell15.code(),
92+
io4.cell16.code(),
93+
io4.cell17.code(),
94+
io4.cell18.code(),
95+
io4.cell19.code(),
96+
io4.cell20.code(),
97+
io4.cell21.code(),
98+
io4.cell22.code(),
99+
io4.cell23.code(),
100+
io4.cell24.code(),
101+
io4.cell25.code(),
102+
io4.cell26.code(),
103+
io4.cell27.code(),
104+
io4.cell28.code(),
105+
io4.cell29.code(),
106+
io4.cell30.code(),
107+
io4.cell31.code(),
108+
io4.cell32.code(),
75109
],
76110
}
77111
}

packages/applechu/src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ mod document;
22
pub mod schema;
33
mod validation;
44
pub mod value;
5+
mod virtual_key;
56

67
pub use document::Config;
78
pub use schema::{ConfigDiagnostic, ConfigSection, DiagnosticLevel};
9+
pub use virtual_key::VirtualKey;
810

911
#[doc(hidden)]
1012
#[macro_export]

packages/applechu/src/config/schema.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,6 @@ pub fn append_entry<T: ConfigValue>(output: &mut String, key: &str, value: &T, e
246246
}
247247
output.push_str(&applechu_schema::canonical_key(key));
248248
output.push_str(" = ");
249-
output.push_str(&value.to_toml().to_string());
249+
output.push_str(&value.to_toml_literal());
250250
output.push('\n');
251251
}

packages/applechu/src/config/tests.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,15 +237,50 @@ fn io4_owns_keyboard_input_mapping() {
237237
.expect("Aime 配置必须完成注入");
238238

239239
// Then: IO4 类型直接持有所有输入映射。
240-
assert_eq!(io4.test, 65);
241-
assert_eq!(io4.air1, 66);
242-
assert_eq!(io4.cell1, 67);
240+
assert_eq!(io4.test.code(), 65);
241+
assert_eq!(io4.air1.code(), 66);
242+
assert_eq!(io4.cell1.code(), 67);
243243
assert_eq!(chuniio.vk_test, 65);
244244
assert_eq!(chuniio.vk_ir[0], 66);
245245
assert_eq!(chuniio.vk_cell[0], 67);
246246
assert_eq!(aime.iodll, "aimeio.dll");
247247
}
248248

249+
#[test]
250+
fn virtual_keys_accept_names_and_normalize_to_hex() {
251+
// Given: 键位同时使用键名、旧十进制整数和十六进制整数。
252+
let source = concat!(
253+
"ConfigVersion = 1\n",
254+
"[Io4]\n",
255+
"Test = \"F2\"\n",
256+
"Service = 32\n",
257+
"Coin = 0x72\n",
258+
"[Aime]\n",
259+
"Scan = \"Space\"\n",
260+
"[Autoplay]\n",
261+
"Hotkey = \"Insert\"\n",
262+
);
263+
264+
// When: 配置经过读取和规范化保存。
265+
let config = Config::parse(".", source).expect("测试配置必须有效");
266+
let output = config.to_toml();
267+
268+
// Then: 所有输入都转换为确定的虚拟键码,并统一使用十六进制。
269+
let io4 = config
270+
.section::<crate::io4::Io4Config>()
271+
.expect("IO4 配置必须完成注入");
272+
let aime = config
273+
.section::<crate::aime::AimeSectionConfig>()
274+
.expect("Aime 配置必须完成注入");
275+
assert_eq!(io4.test.code(), 0x71);
276+
assert_eq!(aime.scan.code(), 0x20);
277+
assert!(output.contains("Test = 0x71"));
278+
assert!(output.contains("Service = 0x20"));
279+
assert!(output.contains("Coin = 0x72"));
280+
assert!(output.contains("Scan = 0x20"));
281+
assert!(output.contains("Hotkey = 0x2D"));
282+
}
283+
249284
#[test]
250285
fn canonical_io_config_has_no_legacy_sections_or_repeated_comments() {
251286
// Given: 使用全部默认值的配置。

packages/applechu/src/config/value.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
pub trait ConfigValue: Clone + Send + Sync + 'static {
22
fn parse(value: &toml::Value) -> Option<Self>;
33
fn to_toml(&self) -> toml::Value;
4+
5+
fn to_toml_literal(&self) -> String {
6+
self.to_toml().to_string()
7+
}
48
}
59

610
impl<T: ConfigValue> ConfigValue for Option<T> {
@@ -13,6 +17,13 @@ impl<T: ConfigValue> ConfigValue for Option<T> {
1317
.map(ConfigValue::to_toml)
1418
.unwrap_or(toml::Value::Boolean(false))
1519
}
20+
21+
fn to_toml_literal(&self) -> String {
22+
self.as_ref().map_or_else(
23+
|| toml::Value::Boolean(false).to_string(),
24+
ConfigValue::to_toml_literal,
25+
)
26+
}
1627
}
1728

1829
impl ConfigValue for bool {

0 commit comments

Comments
 (0)