From fe7496f2fb64a82a9abf8f803e131a497d6eba1d Mon Sep 17 00:00:00 2001
From: MasterChiefCN <49179444+MasterChiefCN@users.noreply.github.com>
Date: Mon, 22 Jun 2026 14:59:10 +0800
Subject: [PATCH 1/2] add README_ZH.md
add Chinese translation of README
---
README_ZH.md | 271 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 271 insertions(+)
create mode 100644 README_ZH.md
diff --git a/README_ZH.md b/README_ZH.md
new file mode 100644
index 0000000..39940a6
--- /dev/null
+++ b/README_ZH.md
@@ -0,0 +1,271 @@
+
+
+## 功能特性
+
+### 核心解锁
+- 解锁任意数量未拥有的游戏
+- 解锁未拥有游戏的所有 DLC
+- 支持从 Lua 配置自动加载仓库(depot)解密密钥
+- 支持通过 `opensteamtool` / `steamrun` / `wudrm` 上游 API 自动下载 manifest(默认为 `opensteamtool`),或通过自定义 Lua 端点(参见 [通过 Lua 获取 Manifest](#通过-lua-获取-manifest))
+- 支持下载需要访问令牌的保护游戏或 DLC
+- 支持绑定 manifest 以防止特定游戏被更新
+
+### 热重载
+- 在任何监视目录中添加、修改、删除或覆盖 `.lua` 文件会自动触发重载。无需重启,无需切换离线/在线模式
+
+### 注入
+- 通过 `opensteamtool.toml` 中的 `[inject]` 添加可选的游戏进程库注入
+- 配置 `enabled`、`library_x64` 和 `library_x86`;注入的库必须与目标进程架构匹配。路径可以是绝对路径,也可以是相对于 Steam 根目录的相对路径
+
+### 家庭共享和远程同乐
+- 绕过 Steam 家庭共享限制,适用于通过 Lua 中 `addappid` 添加到库的游戏。参与共享的 Steam 家庭中的所有账户都必须使用 OpenSteamTool 才能生效
+
+### 兼容 Denuvo 和 SteamStub 保护的游戏
+- 仅 SteamStub 保护的游戏不需要配置 `AppTicket`。OpenSteamTool 可以重用 Steam 本地 ConfigStore 令牌,通过 SteamDRMP 令牌解析漏洞伪造请求的 AppId,无需注入游戏进程
+- Denuvo 保护的游戏仍需要显式令牌数据。OpenSteamTool 通过平台凭据存储保存 `AppTicket` 和 `ETicket`
+- 在 Lua 配置中使用 `setAppTicket(appid, "hex")` 和 `setETicket(appid, "hex")` 自动将这些值写入平台凭据存储
+- Denuvo 验证有 30 分钟有效窗口。过期后授权可能失败,显示 Denuvo 错误代码 `88500005`;重试前请刷新令牌数据
+- AppTicket 优先级:显式令牌优先级最高,包括通过 `setAppTicket` 配置的令牌和已缓存的 `AppTicket` 凭据值。若无可用显式 AppTicket,OpenSteamTool 回退到伪造的本地 ConfigStore 令牌路径
+- SteamID 优先级:优先读取缓存的 `SteamID`;若缺失,则从显式 `AppTicket` 解析。在 Windows 上,凭据存储后端当前使用 `HKCU\Software\Valve\Steam\Apps\`。Linux 后端尚未实现
+
+#### 使用 `extract_tickets` 提取令牌
+
+`extract_tickets` 工具会转储你需要的 `AppTicket` 和 `ETicket` 十六进制字符串,用于 `setAppTicket` / `setETicket`。在 Steam 运行并登录到**拥有**目标游戏的账户的机器上运行它。
+
+1. 构建工具(参见 [构建](#构建));二进制文件位于 `build/tools/Release/extract_tickets.exe`
+2. 使用目标 AppId 运行它(或不带参数运行,在提示时输入 AppId):
+ ```powershell
+ extract_tickets.exe 1361510
+ ```
+3. 它从注册表读取 Steam 安装路径,加载 `steamclient64.dll`,并将所有内容写入可执行文件旁边的 `/` 文件夹:
+ - `appticket.bin` — 原始应用所有权令牌(二进制)
+ - `eticket.bin` — 原始加密应用令牌(二进制)
+ - `tickets.txt` — 包含十六进制字符串的纯文本摘要:
+ ```
+ appid:1361510
+ appticket(184 bytes):14000000...
+ eticket(143 bytes):...
+ ```
+ 无法获取的令牌报告为 `appticket:null` / `eticket:null`
+4. 将 `tickets.txt` 中的十六进制字符串粘贴到你的 Lua 配置中:
+ ```lua
+ setAppTicket(1361510, "14000000...")
+ setETicket(1361510, "...")
+ ```
+
+> **注意:** 令牌仅当从**真正拥有**游戏的账户提取时才有效
+
+### 统计和成就
+- 为未拥有的游戏启用统计和成就
+- 使用 `setStat(appid, "steamid")` 配置拉取哪个 SteamID 的成就数据
+- 如果某个应用未配置 `setStat`,当 `[stats] enable_api = true`(默认)时,OpenSteamTool 查询 `https://stats.opensteamtool.com/{appid}`
+- 优先级:`setStat` > stats API(启用且有效时)> 硬编码预设 SteamID `76561198028121353`
+
+### 在线修复
+- 在 Steam 启动参数中添加 `-onlinefix` 以在需要使用大厅匹配的游戏里启用基于 480 的在线游戏。当前限制是同一时间只能运行一个这样的游戏。要撤销,只需从启动参数中移除 `-onlinefix` — 下次启动时在线游戏恢复正常
+
+## 未来计划
+- Steam 云同步支持。(这是个超级大工程)
+
+## 使用方法
+
+1. 在项目根目录运行 `build.bat` 构建项目
+2. 将生成的 `dwmapi.dll`、`xinput1_4.dll` 和 `OpenSteamTool.dll` 复制到 Steam 根目录
+3. 创建 Lua 目录(例如 `C:\steam\config\lua`)并将 Lua 脚本放在那里。DLL 会自动加载并执行它们
+4. Lua 示例:
+```lua
+addappid(1361510) -- 解锁 appid 为 1361510 的游戏
+
+addappid(1361511, 0,"5954562e7f5260400040a818bc29b60b335bb690066ff767e20d145a3b6b4af0") -- 解锁 appid 为 1361511 的游戏,depotKey 为 "5954562e7f5260400040a818bc29b60b335bb690066ff767e20d145a3b6b4af0"
+
+addtoken(1361510,"2764735786934684318") -- 为 appid 为 1361510 的游戏添加访问令牌 ("2764735786934684318")
+-- 不再支持:
+--pinApp(1361510) -- 固定 appid 为 1361510 的游戏以防止其被更新
+
+setManifestid(1361511,"5656605350306673283") -- 固定 depotid:1361511 manifest_gid:5656605350306673283,大小默认为 0
+setManifestid(1361511,"5656605350306673283", 12345678) -- 同上,但指定明确大小
+
+setAppTicket(1361510,"0100000000000000...") -- 将 AppTicket 写入凭据存储;在 Windows 上:HKCU\Software\Valve\Steam\Apps\1361510\AppTicket
+
+setETicket(1361510,"0100000000000000...") -- 将 ETicket 写入凭据存储;在 Windows 上:HKCU\Software\Valve\Steam\Apps\1361510\ETicket
+
+setStat(1361510, "76561197960287930") -- 使用指定 SteamID 的成就数据用于 appid 1361510
+-- 若未配置,启用时使用 stats API;否则使用默认 SteamID 76561198028121353
+```
+
+所有函数名**不区分大小写**。`setAppTicket`、`setappticket`、`SetAppticket`、`SETAPPTICKET` 等都是等价的。每个注册的函数都适用(`addAppId`、`AddToken`、`SETManifestid` 等)。
+
+### 配置(可选)
+
+将 `opensteamtool.example.toml` 重命名为 `opensteamtool.toml` 并放在 Steam 根目录(与 `steam.exe` 同级)。
+若找不到配置文件,则使用内置默认值——不会自动创建。
+文件在 Steam 运行时被监视;有效更改会热重载,无需重启 Steam。
+
+```toml
+[log]
+# 仅调试构建。级别:trace、debug、info、warn、error
+level = "info"
+
+[manifest]
+# 仓库 manifest 请求码的上游 API。选项:"opensteamtool"、"steamrun"、"wudrm"
+url = "opensteamtool"
+
+# manifest 请求的 HTTP 超时(毫秒)
+timeout_resolve_ms = 5000
+timeout_connect_ms = 5000
+timeout_send_ms = 10000
+timeout_recv_ms = 10000
+
+[stats]
+# 当没有 Lua setStat 覆盖时查询 https://stats.opensteamtool.com/{appid}
+# 优先级:setStat > stats API > 硬编码预设 SteamID
+enable_api = true
+
+# 额外的 Lua 配置目录(可选)
+# 文件在默认 /config/lua 文件夹之后加载
+# 默认文件夹总是最后加载,因此用户文件优先级更高
+[lua]
+paths = []
+
+[inject]
+# 可选的游戏进程库注入
+# 注入的库必须与目标进程架构匹配
+enabled = false
+# library_x64 = "OpenSteamTool.GameHook.x64.dll"
+# library_x86 = "OpenSteamTool.GameHook.x86.dll"
+
+# 可选元数据镜像。参见下面的"Steam 版本兼容性"
+[remote]
+# url_template = "https://your.server/{channel}/{component}/{sha256}.toml"
+```
+
+### 通过 Lua 获取 Manifest
+
+支持两个 manifest 码函数:
+
+#### `fetch_manifest_code(gid)`
+
+基础函数,只接收 manifest GID
+
+#### `fetch_manifest_code_ex(app_id, depot_id, gid)` *(推荐)*
+
+扩展函数,接收 `app_id`、`depot_id` 和 `gid`。允许构造需要应用识别的 API 端点
+
+C++ 运行时提供两个 Lua 辅助函数:
+
+| 函数 | 签名 | 返回值 |
+|------|------|--------|
+| `http_get` | `http_get(url [, headers])` | `body, status_code` |
+| `http_post` | `http_post(url, body [, headers])` | `body, status_code` |
+
+`headers` 是可选表:`{["Key"]="Value", ...}`
+
+### Steam 版本兼容性
+
+OpenSteamTool 不再在 DLL 中内置字节模式签名。相反,每次启动时它计算磁盘上 `steamclient64.dll` 和 `steamui.dll` 的 SHA-256,并从上游跟踪器 [`OpenSteam001/steam-monitor`](https://github.com/OpenSteam001/steam-monitor)(`pattern` 分支)查找匹配的模式文件
+
+查找顺序(每次启动):
+
+1. **GitHub raw** — `https://raw.githubusercontent.com/OpenSteam001/steam-monitor/pattern/...`。规范来源
+2. **jsDelivr CDN** — 如果 GitHub raw 无法访问(连接拒绝/超时/5xx)时自动回退。无需配置。在 `raw.githubusercontent.com` 被封锁但 jsDelivr 可访问的地区很有用(如中国大陆)
+3. **本地缓存** — `\opensteamtool\pattern\\.toml`。仅当远程不可达时使用。每次成功远程获取后覆盖缓存
+
+每次启动都会咨询远程,因此用户自动获取上游重新发布(例如机器人添加新签名或修复现有签名),无需清除任何缓存
+
+如果某步返回 **HTTP 404**,镜像循环立即停止——所有镜像提供相同内容,因此 404 意味着上游机器人尚未为此 Steam 版本发布 TOML。代码然后回退到本地缓存(如果存在);否则出现一次性弹窗,显示不匹配的 DLL 名称、其 SHA-256、预期缓存路径和上游 URL。仅禁用与该 DLL 相关的钩子——OpenSteamTool 的其余部分继续工作
+
+如果你知道给定版本的布局,也可以手动将模式 TOML 放入缓存目录;文件名必须为 `.toml`。下次远程不可达时缓存回退会拾取它
+
+> 每次启动执行简短的出站 HTTPS 请求(每个 DLL 一个:`steamclient64.dll`、`steamui.dll`)。下载的内容很小(每个约 10 KB),工作在线程上运行,因此永远不会阻塞 Steam 加载器
+
+#### 使用不同的镜像
+
+对大多数用户来说,内置的 **GitHub -> jsDelivr** 回退已经足够。要使用私有镜像或内网服务器,配置完整的 URL 模板。自定义镜像替换内置远程源;本地缓存回退仍然可用
+
+模板必须包含 `{channel}`、`{component}` 和 `{sha256}`。当前使用的通道是 `pattern` 和 `ipc`
+
+```toml
+[remote]
+url_template = "https://your.server/{channel}/{component}/{sha256}.toml"
+# url_template = "https://fast.jsdelivr.net/gh/OpenSteam001/steam-monitor@{channel}/{component}/{sha256}.toml"
+```
+
+### 调试日志
+
+调试构建在 `/opensteamtool/` 下写入每个模块的日志文件:
+
+| 文件 | 来源 | 内容 |
+|------|------|------|
+| `main.log` | 通用 | 初始化、配置加载、Lua 解析、工具 |
+| `ipc.log` | `LOG_IPC_*` | IPC 命令、InterfaceCall 分发、欺骗 |
+| `netpacket.log` | `LOG_NETPACKET_*` | 网络包发送/接收、eMsg 分发 |
+| `manifest.log` | `LOG_MANIFEST_*` | Manifest 下载、`fetch_manifest_code`、manifest 绑定 |
+| `decryptionkey.log` | `LOG_DECRYPTIONKEY_*` | 仓库解密密钥注入 |
+| `keyvalue.log` | `LOG_KEYVALUE_*` | KeyValues 补丁(manifest 绑定) |
+| `misc.log` | `LOG_MISC_*` | 引擎指针捕获、AppId 提示 |
+| `achievement.log` | `LOG_ACHIEVEMENT_*` | UserStats 请求/响应、steamid 欺骗 |
+| `pics.log` | `LOG_PICS_*` | PICS 访问令牌注入 |
+| `package.log` | `LOG_PACKAGE_*` | 包注入、FileWatcher 事件 |
+| `onlinefix.log` | `LOG_ONLINEFIX_*` | 在线修复(480 AppId 欺骗) |
+| `richpresence.log` | `LOG_RICHPRESENCE_*` | 丰富状态包构造和注入 |
+| `steamui.log` | `LOG_STEAMUI_*` | SteamUI 钩子诊断 |
+| `pipe.log` | `LOG_PIPE_*` | 管道握手、进程检查、Denuvo 授权、库注入 |
+| `platform.log` | `LOG_PLATFORM_*` | 平台助手诊断,包括远程进程操作 |
+
+日志级别由 `opensteamtool.toml` 中的 `[log] level` 控制
+
+## 构建
+
+### 要求
+- Windows 10/11
+- CMake 3.20+
+- 带有 MSVC(x64 工具链)的 Visual Studio 2022
+
+### 运行时要求
+- Steam 更新后首次启动需要访问 `raw.githubusercontent.com` 的出站 HTTPS(参见 [Steam 版本兼容性](#steam-版本兼容性))。之后会缓存
+
+### 快速构建
+```powershell
+build.bat
+```
+
+### 输出
+- Debug:`build/Debug/OpenSteamTool.dll`、`build/Debug/dwmapi.dll`、`build/Debug/xinput1_4.dll`
+- Release:`build/Release/OpenSteamTool.dll`、`build/Release/dwmapi.dll`、`build/Release/xinput1_4.dll`
+
+## 免责声明
+本项目仅供研究和教育目的使用。你负责遵守当地法律、平台服务条款和软件许可证。
From 70137baca0b7f0d5d417c2ccf60c990c5d3f106e Mon Sep 17 00:00:00 2001
From: MasterChiefCN <49179444+MasterChiefCN@users.noreply.github.com>
Date: Mon, 22 Jun 2026 15:02:25 +0800
Subject: [PATCH 2/2] add Chinese (Simplified) language option to README
- Added a link to README_ZH.md in the language switcher
- Included China flag icon via flagcdn.com
---
README.md | 45 +++++++++++++++++++++++++--------------------
1 file changed, 25 insertions(+), 20 deletions(-)
diff --git a/README.md b/README.md
index 48930ce..d62b9fa 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,11 @@
Español
+ |
+
+
+ 中文
+
@@ -84,11 +89,11 @@ The `extract_tickets` tool dumps the `AppTicket` and `ETicket` hex strings you n
> **Note:** Tickets are only valid when extracted from an account that **genuinely owns** the game.
-### Stats and Achievements
-- Enable stats and achievements for unowned games.
-- Uses `setStat(appid, "steamid")` to configure which SteamID's achievement data to pull.
-- If no `setStat` is configured for an app, OpenSteamTool queries `https://stats.opensteamtool.com/{appid}` when `[stats] enable_api = true` (default).
-- Priority: `setStat` > stats API when enabled and valid > hardcoded preset SteamID `76561198028121353`.
+### Stats and Achievements
+- Enable stats and achievements for unowned games.
+- Uses `setStat(appid, "steamid")` to configure which SteamID's achievement data to pull.
+- If no `setStat` is configured for an app, OpenSteamTool queries `https://stats.opensteamtool.com/{appid}` when `[stats] enable_api = true` (default).
+- Priority: `setStat` > stats API when enabled and valid > hardcoded preset SteamID `76561198028121353`.
### Online Fix
- Add `-onlinefix` to the Steam launch parameters to enable 480-based online play in games that use lobby matchmaking. The current limitation is that only one such game can run at a time.To revert, simply remove -onlinefix from the launch parameters — online play returns to normal on the next launch.
@@ -117,9 +122,9 @@ setAppTicket(1361510,"0100000000000000...") -- write AppTicket to the credential
setETicket(1361510,"0100000000000000...") -- write ETicket to the credential store; on Windows: HKCU\Software\Valve\Steam\Apps\1361510\ETicket
-setStat(1361510, "76561197960287930") -- use the specified SteamID's achievement data for appid 1361510
--- If not configured, the stats API is used when enabled; otherwise default SteamID 76561198028121353 is used.
-```
+setStat(1361510, "76561197960287930") -- use the specified SteamID's achievement data for appid 1361510
+-- If not configured, the stats API is used when enabled; otherwise default SteamID 76561198028121353 is used.
+```
All function names are **case-insensitive**. `setAppTicket`, `setappticket`, `SetAppticket`, `SETAPPTICKET` etc. are all equivalent. The same applies to every registered function (`addAppId`, `AddToken`, `SETManifestid`, etc.).
@@ -140,18 +145,18 @@ url = "opensteamtool"
# HTTP timeouts for manifest requests (milliseconds)
timeout_resolve_ms = 5000
-timeout_connect_ms = 5000
-timeout_send_ms = 10000
-timeout_recv_ms = 10000
-
-[stats]
-# Query https://stats.opensteamtool.com/{appid} when no Lua setStat override exists.
-# Priority: setStat > stats API > hardcoded preset SteamID.
-enable_api = true
-
-# Additional Lua config directories (optional).
-# Files are loaded after the default /config/lua folder.
-# The default folder is always loaded last so user files take priority.
+timeout_connect_ms = 5000
+timeout_send_ms = 10000
+timeout_recv_ms = 10000
+
+[stats]
+# Query https://stats.opensteamtool.com/{appid} when no Lua setStat override exists.
+# Priority: setStat > stats API > hardcoded preset SteamID.
+enable_api = true
+
+# Additional Lua config directories (optional).
+# Files are loaded after the default /config/lua folder.
+# The default folder is always loaded last so user files take priority.
[lua]
paths = []