Skip to content

Commit 0332bb8

Browse files
committed
refactor: 重构模块化配置系统
通过 linkme 在链接期收集各模块声明的强类型 section,移除中央手写模板和运行时字符串 key 查询。 中央配置层统一负责 TOML 加载、版本与值域校验、Disabled 语义、未知项诊断和规范化序列化;无效配置会阻止 TLS 前内存修改。 迁移所有内置模块配置,并将 DeviceLostFix 合并进 D3D9Ex;System 的模式与刷新率改为类型化值,刷新率使用 60/120 用户语义。 补充配置注册、写回、校验、D3D9 合并及 early patch 回归测试,并更新 README。
1 parent 204e676 commit 0332bb8

52 files changed

Lines changed: 2217 additions & 788 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ crate-type = ["cdylib"]
1010

1111
[dependencies]
1212
chu-abi = { path = "chu-abi" }
13+
linkme = "0.3.36"
1314
toml = "0.8"
1415
once_cell = "1"
1516
retour = "0.4.0-alpha.4"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
3333
## 配置
3434

35-
所有功能通过游戏目录下的 `AppleChu.toml` 控制。取消注释对应的 `[Section]` 即可启用该功能,并填写其下的键值
35+
所有功能通过游戏目录下的 `AppleChu.toml` 控制。默认关闭的栏目取消注释即可启用;默认开启的栏目可用 `Disabled = true` 关闭。程序会根据各模块声明的 schema 校验并规范化配置文件。
3636

3737
> [!NOTE]
3838
> 如需图形化编辑,可使用 [ChuChartManager](https://github.com/MuNET-OSS/ChuChartManager)

src/aime/mod.rs

Lines changed: 90 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,71 @@ static AIME_DEVICE: Mutex<Option<SgNfcDevice>> = Mutex::new(None);
2424
static AIME_FD: AtomicUsize = AtomicUsize::new(0);
2525
static EXTERNAL: Lazy<Mutex<Option<ExternalAimeIo>>> = Lazy::new(|| Mutex::new(None));
2626

27+
crate::config_section! {
28+
pub(crate) struct AimeSectionConfig => AIME_CONFIG_SECTION {
29+
section: "Aime",
30+
order: 300,
31+
default_enabled: true,
32+
always_enabled: false,
33+
hidden: false,
34+
comment: "Aime 读卡器模拟",
35+
fields: {
36+
pub cvt_port: u32 = 2,
37+
key: "cvtPort",
38+
comment: "CVT 模式串口号";
39+
pub sp_port: u32 = 3,
40+
key: "spPort",
41+
comment: "SP 模式串口号";
42+
pub high_baudrate: bool = false,
43+
key: "highBaud",
44+
comment: "使用高波特率";
45+
pub aime_path: String = String::from("aime.txt"),
46+
key: "aimePath",
47+
comment: "Aime 卡号文件";
48+
pub felica_path: String = String::from("felica.txt"),
49+
key: "felicaPath",
50+
comment: "FeliCa 卡号文件";
51+
pub authdata_path: String = String::from("DEVICE\\authdata.bin"),
52+
key: "authdataPath",
53+
comment: "认证数据文件";
54+
pub aime_gen: bool = true,
55+
key: "aimeGen",
56+
comment: "缺少 Aime 卡号时自动生成";
57+
pub felica_gen: bool = false,
58+
key: "felicaGen",
59+
comment: "缺少 FeliCa 卡号时自动生成";
60+
pub scan: i32 = 0x0D,
61+
comment: "读卡按键的虚拟键码";
62+
pub gen: u8 = 3,
63+
comment: "读卡器代数";
64+
pub proxy_flag: u8 = 2,
65+
key: "proxyFlag",
66+
comment: "读卡代理标志";
67+
}
68+
}
69+
}
70+
71+
crate::config_section! {
72+
pub(crate) struct AimeIoConfig => AIME_IO_CONFIG_SECTION {
73+
section: "AimeIo",
74+
order: 301,
75+
default_enabled: true,
76+
always_enabled: true,
77+
hidden: false,
78+
comment: "外部 Aime IO DLL",
79+
fields: {
80+
pub path: String = String::new(),
81+
comment: "所有架构共用的 DLL 路径";
82+
pub path32: String = String::new(),
83+
comment: "32 位 DLL 路径";
84+
pub path64: String = String::new(),
85+
comment: "64 位 DLL 路径";
86+
}
87+
}
88+
}
89+
2790
#[derive(Clone)]
2891
pub struct AimeConfig {
29-
pub enable: bool,
3092
pub cvt_port: u32,
3193
pub sp_port: u32,
3294
pub high_baudrate: bool,
@@ -115,13 +177,19 @@ impl AimeReader {
115177
}
116178
}
117179

118-
pub fn init(api: &Api, config: &Config, port: u32) {
119-
let cfg = load_config(config, &config.base_dir);
120-
if !cfg.enable {
180+
pub fn init(api: &Api, config: &Config, is_sp: bool) {
181+
let Some(section) = config.section::<AimeSectionConfig>() else {
182+
return;
183+
};
184+
if !section.enabled {
121185
return;
122186
}
187+
let cfg = load_config(&section, config.base_dir());
188+
let port = if is_sp { cfg.sp_port } else { cfg.cvt_port };
123189

124-
let path = dll_path(config, "AimeIo", "aimeio");
190+
let path = config
191+
.section::<AimeIoConfig>()
192+
.map_or_else(String::new, |config| dll_path(&config));
125193
if !path.is_empty() {
126194
match unsafe { ExternalAimeIo::load(&path) } {
127195
Ok(external) => {
@@ -254,48 +322,19 @@ fn aime_port() -> u32 {
254322
AIME_PORT.lock().map_or(4, |p| *p)
255323
}
256324

257-
pub fn load_config(config: &Config, base_dir: impl AsRef<Path>) -> AimeConfig {
258-
let path = config.get_string_alias(&[("Aime", "aime_path"), ("aime", "aimePath")], "aime.txt");
259-
let aime_path = if Path::new(&path).is_absolute() {
260-
PathBuf::from(&path)
261-
} else {
262-
base_dir.as_ref().join(path)
263-
};
264-
let authdata_path = resolve_path(
265-
base_dir.as_ref(),
266-
&config.get_string_alias(
267-
&[
268-
("Aime", "authdata_path"),
269-
("Aime", "authdataPath"),
270-
("aime", "authdataPath"),
271-
],
272-
"DEVICE\\authdata.bin",
273-
),
274-
);
275-
let felica_path = config.get_string_alias(
276-
&[("Aime", "felica_path"), ("aime", "felicaPath")],
277-
"felica.txt",
278-
);
279-
let felica_path = if Path::new(&felica_path).is_absolute() {
280-
PathBuf::from(&felica_path)
281-
} else {
282-
base_dir.as_ref().join(felica_path)
283-
};
284-
325+
fn load_config(config: &AimeSectionConfig, base_dir: impl AsRef<Path>) -> AimeConfig {
285326
AimeConfig {
286-
enable: config.get_bool_alias(&[("Aime", "enable"), ("aime", "enable")], true),
287-
cvt_port: config.get_int_alias(&[("Aime", "cvt_port"), ("aime", "cvtPort")], 4) as u32,
288-
sp_port: config.get_int_alias(&[("Aime", "sp_port"), ("aime", "spPort")], 3) as u32,
289-
high_baudrate: config
290-
.get_bool_alias(&[("Aime", "high_baudrate"), ("aime", "highBaud")], false),
291-
aime_path,
292-
felica_path,
293-
authdata_path,
294-
aime_gen: config.get_bool_alias(&[("Aime", "aime_gen"), ("aime", "aimeGen")], true),
295-
felica_gen: config.get_bool_alias(&[("Aime", "felica_gen"), ("aime", "felicaGen")], false),
296-
scan_key: config.get_int_alias(&[("Aime", "scan"), ("aime", "scan")], 0x0D) as i32,
297-
gen: config.get_int_alias(&[("Aime", "gen"), ("aime", "gen")], 3) as u8,
298-
proxy_flag: config.get_int_alias(&[("Aime", "proxy_flag"), ("aime", "proxyFlag")], 2) as u8,
327+
cvt_port: config.cvt_port,
328+
sp_port: config.sp_port,
329+
high_baudrate: config.high_baudrate,
330+
aime_path: resolve_path(base_dir.as_ref(), &config.aime_path),
331+
felica_path: resolve_path(base_dir.as_ref(), &config.felica_path),
332+
authdata_path: resolve_path(base_dir.as_ref(), &config.authdata_path),
333+
aime_gen: config.aime_gen,
334+
felica_gen: config.felica_gen,
335+
scan_key: config.scan,
336+
gen: config.gen,
337+
proxy_flag: config.proxy_flag,
299338
}
300339
}
301340

@@ -307,16 +346,16 @@ where
307346
guard.as_ref().map(call)
308347
}
309348

310-
fn dll_path(config: &Config, upper: &str, lower: &str) -> String {
349+
fn dll_path(config: &AimeIoConfig) -> String {
311350
let arch_path = if cfg!(target_pointer_width = "64") {
312-
config.get_string_alias(&[(upper, "path64"), (lower, "path64")], "")
351+
&config.path64
313352
} else {
314-
config.get_string_alias(&[(upper, "path32"), (lower, "path32")], "")
353+
&config.path32
315354
};
316355
if !arch_path.is_empty() {
317-
return arch_path;
356+
return arch_path.clone();
318357
}
319-
config.get_string_alias(&[(upper, "path"), (lower, "path")], "")
358+
config.path.clone()
320359
}
321360

322361
fn resolve_path(base_dir: impl AsRef<Path>, path: &str) -> PathBuf {

src/autoplay/autoplay.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use std::ffi::c_void;
22
use std::ptr;
33
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
44

5-
use crate::config::Config;
65
use crate::util::api::Api;
76

87
static ENABLED: AtomicBool = AtomicBool::new(false);
@@ -38,9 +37,9 @@ extern "system" {
3837
}
3938
const VK_HOME: i32 = 0x24;
4039

41-
pub fn init(api: &Api, config: &Config) {
40+
pub fn init(api: &Api, hotkey: &str) {
4241
unsafe { API_HANDLE = Some(*api) };
43-
let hotkey = parse_hotkey_config(config).unwrap_or(VK_HOME);
42+
let hotkey = parse_hotkey(hotkey).unwrap_or(VK_HOME);
4443
HOTKEY.store(hotkey, Ordering::Relaxed);
4544
RUNNING.store(true, Ordering::Relaxed);
4645

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

466-
fn parse_hotkey_config(config: &Config) -> Option<i32> {
467-
match config.get_value("Autoplay", "hotkey")? {
468-
toml::Value::Integer(value) => i32::try_from(*value).ok().filter(|value| *value > 0),
469-
toml::Value::String(value) => parse_hotkey(value),
470-
_ => None,
471-
}
472-
}
473-
474465
fn parse_hotkey(value: &str) -> Option<i32> {
475466
let key = value.trim();
476467
if key.is_empty() {

src/autoplay/mod.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,37 @@ pub use autoplay::{is_enabled, was_used};
55
use crate::config::Config;
66
use crate::util::api::Api;
77

8+
crate::config_section! {
9+
pub(crate) struct AutoplayConfig => AUTOPLAY_CONFIG_SECTION {
10+
section: "Autoplay",
11+
order: 180,
12+
default_enabled: false,
13+
always_enabled: false,
14+
hidden: false,
15+
comment: "自动游玩",
16+
fields: {
17+
pub hotkey: String = String::from("Home"),
18+
comment: "自动游玩切换按键";
19+
}
20+
}
21+
}
22+
823
pub fn init_all(api: &Api, config: &Config) {
9-
if config.is_enabled("Autoplay") {
10-
autoplay::init(api, config);
24+
if let Some(config) = config
25+
.section::<AutoplayConfig>()
26+
.filter(|config| config.enabled)
27+
{
28+
autoplay::init(api, &config.hotkey);
1129
autoplay::init_upload_guard(api);
1230
}
1331
}
1432

33+
pub fn is_config_enabled(config: &Config) -> bool {
34+
config
35+
.section::<AutoplayConfig>()
36+
.is_some_and(|config| config.enabled)
37+
}
38+
1539
pub fn shutdown_all() {
1640
autoplay::shutdown_upload_guard();
1741
autoplay::shutdown();

0 commit comments

Comments
 (0)