Skip to content

Commit 8dc7504

Browse files
committed
refactor: 切换至 winhttp 代理并重构 ABI/d3d9 架构 - 代理目标从 version.dll 切换为 winhttp.dll,利用 chusanApp.exe 对 winhttp 的静态导入实现自然加载;改用入口点劫持(EntryHijack)驱动初始化时机;抽出 chu-abi crate 承载共享 ABI 类型;删除独立 d3d9-proxy crate 合并进 loader/src/d3d9;升级 CHUMOD_API_VERSION 3 到 4 新增 d3d9 服务 API;新增 console/crash_ui/crash_zip/hash 模块增强崩溃报告;删除英文文档中文文档转为默认
1 parent 41684c5 commit 8dc7504

38 files changed

Lines changed: 2381 additions & 1802 deletions

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace]
2-
members = ["loader", "d3d9-proxy"]
2+
members = ["chu-abi", "loader"]
33
resolver = "2"
44

55
[profile.release]

README.en.md

Lines changed: 0 additions & 47 deletions
This file was deleted.

README.md

Lines changed: 141 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,166 @@
11
# ChuModLoader
22

3-
[English](README.en.md) | 简体中文
3+
<p align="center">
4+
<strong>CHUNITHM Mod 加载框架</strong>
5+
</p>
46

5-
CHUNITHM Mod 加载框架。通过代理 `version.dll`,在游戏启动时自动从 `mods/` 目录加载 mod DLL。
7+
<p align="center">
8+
<a href="#许可证"><img src="https://img.shields.io/badge/license-Apache--2.0-blue.svg" alt="License"></a>
9+
<img src="https://img.shields.io/badge/platform-Windows%20x86-lightgrey.svg" alt="Platform">
10+
<img src="https://img.shields.io/badge/rust-nightly-orange.svg" alt="Rust">
11+
<img src="https://img.shields.io/badge/ABI-v4-green.svg" alt="ABI Version">
12+
</p>
613

7-
## 功能
14+
通过代理 `winhttp.dll`,在游戏启动时自动从 `mods/` 目录加载 Mod DLL。框架使用 Rust 编写,Mod 可用 Rust 或 C/C++ 开发
815

9-
- `version.dll` 代理,转发全部 17 个系统导出
10-
- 自动扫描 `mods/*.dll` 加载
11-
- Mod API (C ABI):内存读写、AOB 扫描、inline hook、RTTI vtable 查找、mod 间通信
12-
- TOML / INI 配置 API
13-
- 分级日志 (info/warn/error) + 控制台颜色 + per-mod 日志文件
14-
- 依赖拓扑排序
15-
- 崩溃保护 (catch_unwind)
16-
- 游戏版本检测 + min_loader_version 检查
17-
- 生命周期: init → on_ready → on_frame → shutdown
18-
- 热重载 (reload_mod API + reload.flag)
19-
- crash dump + 栈回溯
20-
- Rust 编写,mod 可用 Rust、C/C++ 或任何能编译 Win32 DLL 的语言
16+
---
17+
18+
## 目录
19+
20+
- [功能特性](#功能特性)
21+
- [工作原理](#工作原理)
22+
- [安装](#安装)
23+
- [目录结构](#目录结构)
24+
- [构建](#构建)
25+
- [Mod 开发](#mod-开发)
26+
- [文档](#文档)
27+
- [许可证](#许可证)
28+
29+
## 功能特性
30+
31+
| 分类 | 说明 |
32+
| --- | --- |
33+
| **DLL 代理** | 代理 `winhttp.dll`,转发系统 `winhttp.dll` 的全部导出,对游戏完全透明 |
34+
| **自动加载** | 启动时自动扫描并加载 `mods/*.dll` |
35+
| **内存操作** | 内存读写、填充、AOB 特征扫描 |
36+
| **Hook** | Inline hook(创建 / 启用 / 禁用 / 移除)、RTTI vtable 查找 |
37+
| **d3d9 服务** | 代理 Direct3D 9 设备,提供每帧回调、帧率锁定、设备 / 窗口句柄 |
38+
| **Mod 间通信** | 命名服务查找 + 基于 topic 的发布 / 订阅 |
39+
| **配置** | TOML / INI 单 Mod 配置读写 |
40+
| **日志** | 分级日志(info / warn / error)+ 控制台颜色 + per-mod 日志文件 |
41+
| **依赖管理** | 依赖声明 + 拓扑排序,确保加载顺序 |
42+
| **生命周期** | `init → on_ready → on_frame → shutdown` 完整生命周期 |
43+
| **热重载** | `reload_mod` API + `reload.flag` 文件触发 |
44+
| **崩溃保护** | `catch_unwind` 包裹回调 + SEH 过滤器 + crash dump + 栈回溯 |
45+
| **版本检测** | 游戏版本检测 + `min_loader_version` 兼容性检查 |
46+
47+
## 工作原理
48+
49+
`chusanApp.exe` 静态导入了系统 `winhttp.dll`。ChuModLoader 把自己编译成同名的 `winhttp.dll` 放在游戏目录,由于 DLL 搜索顺序优先于 `System32`,游戏启动时会自然加载它。框架再把所有导出转发给真正的系统 `winhttp.dll`,因此对游戏完全透明。
50+
51+
加载后,框架并不会立刻初始化(此时游戏自身尚未运行)。它在 `DllMain`**劫持游戏 EXE 的入口点**:改写入口处的指令,使游戏真正开始执行时先跳转到框架的 bootstrap,完成 Mod 加载、crash dump 安装、d3d9 代理注入后,再恢复原始入口点继续运行游戏。
52+
53+
```text
54+
chusanApp.exe 启动
55+
└─ 加载游戏目录的 winhttp.dll(实为 ChuModLoader)
56+
├─ 转发系统导出 → System32\winhttp.dll
57+
└─ DllMain:劫持游戏入口点
58+
游戏入口点首次执行
59+
└─ bootstrap
60+
├─ 安装 crash dump / SEH
61+
├─ 扫描 mods/*.dll,依赖拓扑排序
62+
├─ 逐个驱动 Mod 生命周期
63+
│ init → on_ready → on_frame(循环) → shutdown
64+
├─ 注入 d3d9 设备代理
65+
└─ 恢复原始入口点,游戏正常运行
66+
```
2167

2268
## 安装
2369

24-
1. 构建或下载 `version.dll`
70+
1. 构建或下载 `winhttp.dll`
2571
2. 放到游戏目录(和 `chusanApp.exe` 同级)
26-
3.mod DLL 放进 `mods/`
72+
3.Mod DLL 放进 `mods/` 目录
2773
4. 正常启动游戏
2874

75+
加载日志会写入 `chumod_loader.log`,单 Mod 日志位于 `mods/log/`,崩溃报告位于 `mods/crash/`
76+
77+
## 目录结构
78+
79+
```text
80+
ChuModLoader/
81+
├─ Cargo.toml # workspace(成员:chu-abi、loader)
82+
├─ rust-toolchain.toml
83+
├─ chu-abi/ # C ABI 定义(ChuModInfo / ChuModAPI 等共享类型)
84+
│ ├─ Cargo.toml
85+
│ └─ src/
86+
├─ loader/ # 加载器主体,编译产物为 winhttp.dll
87+
│ ├─ Cargo.toml
88+
│ ├─ build.rs # 生成 winhttp 导出转发
89+
│ └─ src/
90+
│ ├─ lib.rs # 入口、winhttp 代理、游戏入口点劫持
91+
│ ├─ loader/ # Mod 扫描、依赖排序、日志、生命周期、崩溃处理
92+
│ ├─ api_impl/ # ChuModAPI 函数表实现
93+
│ └─ d3d9/ # Direct3D 9 设备代理
94+
├─ include/
95+
│ └─ chumod.h # C/C++ Mod 头文件
96+
└─ docs/ # 开发文档
97+
```
98+
99+
游戏目录下框架运行时使用的布局:
100+
101+
```text
102+
游戏目录/
103+
├─ chusanApp.exe
104+
├─ winhttp.dll # ChuModLoader
105+
├─ chumod_loader.log # 加载器日志
106+
└─ mods/
107+
├─ <mod>.dll # Mod 二进制
108+
├─ config/<mod_name>.toml # 单 Mod 配置(或 .ini)
109+
├─ manifest/<mod_name>.toml # 单 Mod manifest
110+
├─ log/<mod_name>.log # 单 Mod 日志
111+
├─ crash/ # 崩溃报告
112+
└─ reload.flag # 创建即触发全部热重载
113+
```
114+
29115
## 构建
30116

31-
需要 Rust nightly + `i686-pc-windows-msvc`
117+
需要 Rust nightly 工具链 + `i686-pc-windows-msvc` target(仓库已通过 `rust-toolchain.toml` 固定)
32118

33119
```bash
34120
cargo build --release
35121
```
36122

37-
输出: `target/i686-pc-windows-msvc/release/version.dll`
123+
输出文件:
124+
125+
```text
126+
target/i686-pc-windows-msvc/release/winhttp.dll
127+
```
128+
129+
> [!NOTE]
130+
> 游戏为 32 位程序,必须使用 `i686-pc-windows-msvc` target,不能用默认的 64 位 target。
38131
39132
## Mod 开发
40133

41-
参见 [docs/mod-development.md](docs/mod-development.md)[docs/api-reference.md](docs/api-reference.md)
134+
最小的 C/C++ Mod 示例:
135+
136+
```c
137+
#include "chumod.h"
138+
139+
static const ChuModAPI* g_api = NULL;
140+
141+
CHUMOD_API const char* chumod_name(void) { return "Example Mod"; }
142+
CHUMOD_API const char* chumod_version(void) { return "1.0.0"; }
143+
144+
CHUMOD_API int chumod_init(const ChuModInfo* info, const ChuModAPI* api) {
145+
(void)info;
146+
g_api = api;
147+
g_api->log_info("Example Mod 已初始化");
148+
return 0; // 返回 0 保留加载,非 0 跳过卸载
149+
}
150+
151+
CHUMOD_API void chumod_shutdown(void) {
152+
if (g_api) g_api->log_info("Example Mod 已卸载");
153+
}
154+
```
155+
156+
将其编译为 32 位 Windows DLL 后放入 `mods/` 即可。完整的 Rust / C++ 示例、生命周期、配置、热重载等说明见开发文档。
157+
158+
## 文档
42159
43-
头文件: [include/chumod.h](include/chumod.h)
160+
- [Mod 开发指南](docs/mod-development.md) — 三种 Mod 编写方式、生命周期、配置、依赖、热重载、崩溃保护
161+
- [API 参考](docs/api-reference.md) — 完整 C ABI 函数表与导出说明
162+
- [C/C++ 头文件](include/chumod.h) — `chumod.h`
44163
45164
## 许可证
46165
47-
Apache-2.0
166+
[Apache-2.0](LICENSE)

chu-abi/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "chu-abi"
3+
version = "1.0.0"
4+
edition = "2021"
5+
license = "Apache-2.0"
6+
7+
[lib]
8+
name = "chu_abi"
Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
1-
use std::ffi::{c_char, c_void};
1+
//! ChuMod ABI
2+
//!
3+
//! ChuModLoader与 Mod 共同依赖此 crate
4+
//!
5+
//! ABI 安全约束 :
6+
//! - 跨边界结构 `#[repr(C)]`,仅用定宽整型 / 裸指针;
7+
//! - 禁止跨边界使用 Rust `bool`/`Vec`/`String`/slice/trait object/无显式 repr 枚举;
8+
//! - chumod 回调表统一 `extern "C"`(i686 cdecl),不可改 stdcall,否则爆栈;
9+
//! - 仅 append-only 增长:永不重排字段、不缩类型、不改回调签名;
10+
//! - 新字段靠 `ChuModAPI::struct_size` 向后兼容
211
3-
pub const CHUMOD_API_VERSION: u32 = 3;
12+
#![no_std]
13+
#![allow(non_camel_case_types)]
14+
15+
use core::ffi::{c_char, c_void};
16+
17+
/// 当前 ABI 版本 v4:d3d9 服务
18+
pub const CHUMOD_API_VERSION: u32 = 4;
19+
20+
/// Loader 版本
421
pub const LOADER_VERSION: &str = "1.0.0";
522

23+
/// 传给 `chumod_init` 的运行时信息(append-only)
624
#[repr(C)]
725
pub struct ChuModInfo {
826
pub api_version: u32,
@@ -14,22 +32,38 @@ pub struct ChuModInfo {
1432
pub text_size: u32,
1533
pub rdata_base: usize,
1634
pub rdata_size: u32,
17-
pub game_version: *const c_char,
1835
}
1936

37+
pub type ChuModInitFunc = unsafe extern "C" fn(*const ChuModInfo, *const ChuModAPI) -> i32;
38+
pub type ChuModReadyFunc = unsafe extern "C" fn();
39+
pub type ChuModFrameFunc = unsafe extern "C" fn();
40+
pub type ChuModShutdownFunc = unsafe extern "C" fn();
41+
pub type ChuModNameFunc = unsafe extern "C" fn() -> *const c_char;
42+
pub type ChuModDependsFunc = unsafe extern "C" fn() -> *const c_char;
43+
pub type ChuModStringFunc = unsafe extern "C" fn() -> *const c_char;
44+
45+
/// 每次 Present 调用(FPS OSD 等) 参数为原生 `IDirect3DDevice9*`
46+
pub type ChuModPresentCallback = unsafe extern "C" fn(device: *mut c_void);
47+
/// 设备 Reset 事件 `phase`: 0 = Reset 前(释放 D3DPOOL_DEFAULT),1 = Reset 成功后(重建)
48+
pub type ChuModResetCallback = unsafe extern "C" fn(device: *mut c_void, phase: u32);
49+
50+
/// Loader 提供给 mod 的 API 函数表(append-only;用 `struct_size` 判断可用字段)
2051
#[repr(C)]
2152
pub struct ChuModAPI {
2253
pub struct_size: u32,
54+
2355
pub log: Option<unsafe extern "C" fn(*const c_char, ...)>,
56+
2457
pub aob_scan: Option<unsafe extern "C" fn(usize, u32, *const u8, *const c_char) -> usize>,
2558
pub mem_read: Option<unsafe extern "C" fn(usize, *mut c_void, u32) -> i32>,
2659
pub mem_write: Option<unsafe extern "C" fn(usize, *const c_void, u32) -> i32>,
2760
pub mem_fill: Option<unsafe extern "C" fn(usize, u8, u32) -> i32>,
28-
pub hook_create:
29-
Option<unsafe extern "C" fn(*mut c_void, *mut c_void, *mut *mut c_void) -> i32>,
61+
62+
pub hook_create: Option<unsafe extern "C" fn(*mut c_void, *mut c_void, *mut *mut c_void) -> i32>,
3063
pub hook_enable: Option<unsafe extern "C" fn(*mut c_void) -> i32>,
3164
pub hook_disable: Option<unsafe extern "C" fn(*mut c_void) -> i32>,
3265
pub hook_remove: Option<unsafe extern "C" fn(*mut c_void) -> i32>,
66+
3367
pub register_service: Option<unsafe extern "C" fn(*const c_char, *mut c_void) -> i32>,
3468
pub get_service: Option<unsafe extern "C" fn(*const c_char) -> *mut c_void>,
3569
pub publish: Option<unsafe extern "C" fn(*const c_char, *mut c_void, u32) -> i32>,
@@ -39,7 +73,9 @@ pub struct ChuModAPI {
3973
Option<unsafe extern "C" fn(*const c_char, *mut c_void, u32)>,
4074
) -> i32,
4175
>,
76+
4277
pub rtti_find_vtable: Option<unsafe extern "C" fn(*const c_char) -> usize>,
78+
4379
pub config_get_int: Option<unsafe extern "C" fn(*const c_char, i32) -> i32>,
4480
pub config_get_float: Option<unsafe extern "C" fn(*const c_char, f32) -> f32>,
4581
pub config_get_bool: Option<unsafe extern "C" fn(*const c_char, i32) -> i32>,
@@ -49,25 +85,28 @@ pub struct ChuModAPI {
4985
pub config_set_float: Option<unsafe extern "C" fn(*const c_char, f32) -> i32>,
5086
pub config_set_bool: Option<unsafe extern "C" fn(*const c_char, i32) -> i32>,
5187
pub config_set_string: Option<unsafe extern "C" fn(*const c_char, *const c_char) -> i32>,
88+
5289
pub log_info: Option<unsafe extern "C" fn(*const c_char)>,
5390
pub log_warn: Option<unsafe extern "C" fn(*const c_char)>,
5491
pub log_error: Option<unsafe extern "C" fn(*const c_char)>,
92+
5593
pub log_path: *const c_char,
94+
5695
pub toml_section_exists: Option<unsafe extern "C" fn(*const c_char) -> i32>,
5796
pub toml_get_bool: Option<unsafe extern "C" fn(*const c_char, *const c_char, i32) -> i32>,
5897
pub toml_get_int: Option<unsafe extern "C" fn(*const c_char, *const c_char, i32) -> i32>,
5998
pub toml_get_float: Option<unsafe extern "C" fn(*const c_char, *const c_char, f32) -> f32>,
6099
pub toml_get_string: Option<
61100
unsafe extern "C" fn(*const c_char, *const c_char, *mut c_char, u32, *const c_char) -> i32,
62101
>,
102+
63103
pub get_manifest_path: Option<unsafe extern "C" fn() -> *const c_char>,
104+
64105
pub reload_mod: Option<unsafe extern "C" fn(*const c_char) -> i32>,
65-
}
66106

67-
pub type ChuModInitFunc = unsafe extern "C" fn(*const ChuModInfo, *const ChuModAPI) -> i32;
68-
pub type ChuModReadyFunc = unsafe extern "C" fn();
69-
pub type ChuModFrameFunc = unsafe extern "C" fn();
70-
pub type ChuModShutdownFunc = unsafe extern "C" fn();
71-
pub type ChuModNameFunc = unsafe extern "C" fn() -> *const c_char;
72-
pub type ChuModDependsFunc = unsafe extern "C" fn() -> *const c_char;
73-
pub type ChuModStringFunc = unsafe extern "C" fn() -> *const c_char;
107+
pub register_present_callback: Option<unsafe extern "C" fn(Option<ChuModPresentCallback>) -> i32>,
108+
pub register_reset_callback: Option<unsafe extern "C" fn(Option<ChuModResetCallback>) -> i32>,
109+
pub set_frame_lock: Option<unsafe extern "C" fn(u32) -> i32>,
110+
pub get_d3d9_device: Option<unsafe extern "C" fn() -> *mut c_void>,
111+
pub get_game_hwnd: Option<unsafe extern "C" fn() -> usize>,
112+
}

d3d9-proxy/Cargo.toml

Lines changed: 0 additions & 15 deletions
This file was deleted.

d3d9-proxy/build.rs

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)