Compare commits
1
Commits
a4bdc22042
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c98fd8290 |
@@ -10,18 +10,33 @@
|
|||||||
- `tools/cslol-tools/` — cslol 工具链(`mod-tools.exe` / `cslol-dll.dll` / `wad-extract.exe` / `wad-make.exe`)。
|
- `tools/cslol-tools/` — cslol 工具链(`mod-tools.exe` / `cslol-dll.dll` / `wad-extract.exe` / `wad-make.exe`)。
|
||||||
- `runtime/` — 全部本地状态(git 忽略)。
|
- `runtime/` — 全部本地状态(git 忽略)。
|
||||||
|
|
||||||
## 注入器(cslol-dll.dll)本地自管
|
## 注入器(cslol-dll.dll)不更新
|
||||||
|
|
||||||
`tools/cslol-tools/` 下 `mod-tools.exe` / `cslol-dll.dll` / `wad-extract.exe` / `wad-make.exe` 都是**本地管理的二进制**,
|
`tools/cslol-tools/` 下 `mod-tools.exe` / `cslol-dll.dll` / `wad-extract.exe` / `wad-make.exe` 都是**本地管理的二进制**,
|
||||||
其中 `cslol-dll.dll` 与游戏大版本强绑定,**不自动更新**。
|
其中 `cslol-dll.dll` **不更新**——updater 内不存在任何能写入它的代码路径(2026-10-02 起)。
|
||||||
|
|
||||||
原因:cslol-manager 上游 patcher 已换代(AeroHook,509344B),对当前游戏版本报
|
原因:cslol-manager 的新 patcher(AeroHook,509384B)被**故意硬绑定**为只能由
|
||||||
`ah_result != 0 / C0000229`(STATUS_ORDINAL_NOT_FOUND),换上即全盘失效;而可用的
|
`cslol-manager.exe` / `ltk-manager` 启动,走 `mod-tools.exe` CLI 一律返回
|
||||||
MinHook 版(247128B,内部名 `League-of-Legends-Patch.dll`)不在上游 git 历史里,没有可 pin 的地址。
|
`ah_result != 0 / C0000229`。上游 issue #437 维护者原话:
|
||||||
|
"this does mean cli tool is unfortunately unusable"。
|
||||||
|
即它不是"坏了",而是设计上不给 CLI 用,因此 pin 任何 commit 都无意义。
|
||||||
|
|
||||||
`npm run update -- check` / `auto` 只校验本地指纹(已验证 SHA256 记录在 `packages/core/src/sources.ts`),
|
已于 2026-10-02 实际发生一次:`tools/cslol-tools/cslol-dll.dll.bak` 的**创建时间**
|
||||||
不符会告警但**不会**自动下载替换。确需人工替换时才用
|
(`2026/10/02 00:11:17`)即旧 `syncToolchain()` 覆盖掉可用 DLL 的时刻。
|
||||||
`sync --only toolchain`,且必须先设 `LOLSKIN_ALLOW_INJECTOR_SYNC=1`。
|
|
||||||
|
可用的 MinHook 版(247128B,内部名 `League-of-Legends-Patch.dll`)对应源码为
|
||||||
|
`LeagueToolkit/cslol-patcher-legacy`,该仓库**只有源码、无预编译产物**,故无可自动更新的地址。
|
||||||
|
|
||||||
|
**要换 DLL 时人工放置文件**,然后用 `npm run update -- check` 核对指纹
|
||||||
|
(已验证 SHA256 记录在 `packages/core/src/sources.ts` 的 `INJECTOR.knownGoodSha256`)。
|
||||||
|
`check` / `auto` / `verify` 只读该文件,指纹不符会告警但绝不替换。
|
||||||
|
`git` 内也必须存着已验证版本 —— 曾因初始提交存的是坏的 509384B,一度靠工作区覆盖才可用。
|
||||||
|
|
||||||
|
游戏大版本更新后如换肤失效,看 `runtime/state/overlay.log`:
|
||||||
|
`Failed to find CRYPTO_free ptr.` / `Failed to write crypto free ptr:` 说明 patcher 需重编译;
|
||||||
|
**日志无任何报错但不生效** 说明 `cslol-patcher-legacy/src/cslol-dll.c` 中校验返回点的
|
||||||
|
机器码常量 `0x48C38B70247C8B48`(约 199 行)已变 —— 属静默失败,最难排查。
|
||||||
|
需重编译时只改 3 个常量:`pattern[16]`(约 266 行)、`offset + 32`(约 276 行)、以及上述机器码。
|
||||||
|
|
||||||
## 命令
|
## 命令
|
||||||
|
|
||||||
|
|||||||
@@ -49,13 +49,15 @@ export const CDRAGON = {
|
|||||||
* 与 mod-tools.exe / wad-extract.exe / wad-make.exe 一样按本地文件管理。
|
* 与 mod-tools.exe / wad-extract.exe / wad-make.exe 一样按本地文件管理。
|
||||||
*/
|
*/
|
||||||
export const INJECTOR = {
|
export const INJECTOR = {
|
||||||
/** 仅供人工排查/取证;不要接进自动更新。 */
|
|
||||||
upstreamDlls: [
|
|
||||||
"https://cdn.jsdelivr.net/gh/LeagueToolkit/cslol-manager@master/cslol-tools/vendor/cslol-patcher/cslol-dll.dll",
|
|
||||||
"https://raw.githubusercontent.com/LeagueToolkit/cslol-manager/master/cslol-tools/vendor/cslol-patcher/cslol-dll.dll",
|
|
||||||
],
|
|
||||||
/** 已验证可用的注入器指纹(与 Aether 自带那份字节一致)。不符即说明被换掉了。 */
|
/** 已验证可用的注入器指纹(与 Aether 自带那份字节一致)。不符即说明被换掉了。 */
|
||||||
knownGoodSha256: "25EA29710A83C5B12126E3BB8E6EC1549EACD0DCD4A4A10DA105A7037CB310CD",
|
knownGoodSha256: "25EA29710A83C5B12126E3BB8E6EC1549EACD0DCD4A4A10DA105A7037CB310CD",
|
||||||
/** 重新编译/获取可用 patcher 的源码仓库(无预编译产物)。 */
|
/** 重新编译/获取可用 patcher 的源码仓库(无预编译产物)。 */
|
||||||
legacySource: "https://github.com/LeagueToolkit/cslol-patcher-legacy",
|
legacySource: "https://github.com/LeagueToolkit/cslol-patcher-legacy",
|
||||||
|
/**
|
||||||
|
* 取证用地址,**刻意不以数据形式保留**,确保 updater 无法下载它。
|
||||||
|
* cslol-manager@cslol-tools/vendor/cslol-patcher/cslol-dll.dll
|
||||||
|
* (raw.githubusercontent 同路径)
|
||||||
|
* 该文件是 AeroHook 代(509384B),硬绑定只能由 cslol-manager.exe 启动,
|
||||||
|
* 走 mod-tools CLI 必然 `C0000229`。谁再想接自动更新,先读上面那段注释。
|
||||||
|
*/
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ function usage(): void {
|
|||||||
logInfo(
|
logInfo(
|
||||||
[
|
[
|
||||||
"lolskin-updater <命令>",
|
"lolskin-updater <命令>",
|
||||||
" auto | update 一键:检测 ->(有更新才)更新注入器 -> 校验",
|
" auto | update 一键:检测 -> 校验(注入器为本地自管,不更新)",
|
||||||
" check 只检测,不下载",
|
" check 只检测,不下载",
|
||||||
" sync [--only hashes|toolchain] 更新(默认注入器)",
|
" sync [--only hashes] 同步 hashes 库(注入器不参与更新)",
|
||||||
" fetch-skin <heroId> <skinId> 单皮肤全量下载(主程序委托用)",
|
" fetch-skin <heroId> <skinId> 单皮肤全量下载(主程序委托用)",
|
||||||
" prefetch-skins [--hero <id>] 批量预下(占位)",
|
" prefetch-skins [--hero <id>] 批量预下(占位)",
|
||||||
" verify [--fix] 完整性校验 / 修复",
|
" verify [--fix] 完整性校验 / 修复",
|
||||||
@@ -65,6 +65,14 @@ async function main(argv: string[]): Promise<number> {
|
|||||||
const only = rest.includes("--only")
|
const only = rest.includes("--only")
|
||||||
? (rest[rest.indexOf("--only") + 1] as SyncOptions["only"])
|
? (rest[rest.indexOf("--only") + 1] as SyncOptions["only"])
|
||||||
: undefined;
|
: undefined;
|
||||||
|
if (only === ("toolchain" as SyncOptions["only"])) {
|
||||||
|
logError(
|
||||||
|
"sync --only toolchain 已移除:注入器不再由 updater 更新。" +
|
||||||
|
"请人工放置 cslol-dll.dll,再用 `npm run update -- check` 核对指纹。" +
|
||||||
|
"背景见 packages/core/src/sources.ts 的 INJECTOR 注释。"
|
||||||
|
);
|
||||||
|
return EXIT.USAGE;
|
||||||
|
}
|
||||||
return guarded(() => runSync({ only }));
|
return guarded(() => runSync({ only }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,61 +1,28 @@
|
|||||||
import * as fs from "node:fs";
|
import * as fs from "node:fs";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { CDRAGON, CSLOL_DLL, HASH_DIR, INJECTOR, fileExists, logInfo, updateManifest } from "@lolskin/core";
|
import { CDRAGON, HASH_DIR, logInfo } from "@lolskin/core";
|
||||||
import { downloadToFile, getBuffer, sha256File } from "./downloader";
|
import { getBuffer } from "./downloader";
|
||||||
import { EXIT, type ExitCode } from "./exits";
|
import { EXIT, type ExitCode } from "./exits";
|
||||||
|
|
||||||
export interface SyncOptions {
|
export interface SyncOptions {
|
||||||
only?: "hashes" | "toolchain";
|
only?: "hashes";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 人工替换注入器 cslol-dll.dll(**仅显式调用**,自动流程不会触发)。
|
* 注入器 cslol-dll.dll:**已彻底移除更新路径**(2026-10-02)。
|
||||||
*
|
*
|
||||||
* 警告:上游 cslol-manager 的 patcher 已换代(AeroHook,509344B),对当前游戏版本
|
* 原 `syncToolchain()` 会从 cslol-manager 的可变 `@master` 引用下载 patcher,
|
||||||
* 报 `C0000229`;本地可用的 MinHook 版(247128B)不在上游 git 历史里。
|
* 而上游该文件是**故意只允许 cslol-manager.exe 启动**的 AeroHook 版
|
||||||
* 换之前请确认新 DLL 与本地 mod-tools.exe 同代,否则换肤会整体失效。
|
* (509384B),走 mod-tools CLI 一律 `ah_result != 0 / C0000229`。
|
||||||
* 详见 packages/core/src/sources.ts 的 INJECTOR 注释。
|
* 上游 issue LeagueToolkit/cslol-manager#437 维护者原话:
|
||||||
|
* "this does mean cli tool is unfortunately unusable"。
|
||||||
|
*
|
||||||
|
* 曾于 2026-10-02 00:11 实际发生:`cslol-dll.dll.bak` 的创建时间即该次覆盖时刻。
|
||||||
|
*
|
||||||
|
* 现在 updater 内**没有任何**能写入 CSLOL_DLL 的代码路径。
|
||||||
|
* 需要更换时由人工放置文件,并用 `npm run update -- check` 核对指纹。
|
||||||
|
* 背景见 packages/core/src/sources.ts 的 INJECTOR 注释。
|
||||||
*/
|
*/
|
||||||
async function syncToolchain(): Promise<ExitCode> {
|
|
||||||
if (!process.env.LOLSKIN_ALLOW_INJECTOR_SYNC) {
|
|
||||||
throw new Error(
|
|
||||||
"注入器为本地自管二进制,自动更新已停用。" +
|
|
||||||
"如确认要替换,请设置环境变量 LOLSKIN_ALLOW_INJECTOR_SYNC=1 后重试," +
|
|
||||||
"并自行核对 SHA256 是否与已验证版本一致。"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const bak = `${CSLOL_DLL}.bak`;
|
|
||||||
if (fileExists(CSLOL_DLL)) {
|
|
||||||
fs.copyFileSync(CSLOL_DLL, bak);
|
|
||||||
logInfo(`已备份旧注入器 -> ${path.basename(bak)}`);
|
|
||||||
}
|
|
||||||
const tmp = `${CSLOL_DLL}.new`;
|
|
||||||
let ok = false;
|
|
||||||
let lastErr = "";
|
|
||||||
for (const url of INJECTOR.upstreamDlls) {
|
|
||||||
try {
|
|
||||||
await downloadToFile(url, tmp);
|
|
||||||
ok = true;
|
|
||||||
break;
|
|
||||||
} catch (e) {
|
|
||||||
lastErr = (e as Error).message;
|
|
||||||
fs.rmSync(tmp, { force: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!ok) throw new Error(`注入器下载失败(所有源均失败):${lastErr}`);
|
|
||||||
fs.renameSync(tmp, CSLOL_DLL);
|
|
||||||
const sha = sha256File(CSLOL_DLL);
|
|
||||||
if (sha.toUpperCase() !== INJECTOR.knownGoodSha256.toUpperCase()) {
|
|
||||||
logInfo(
|
|
||||||
`注意:新注入器指纹 ${sha.slice(0, 12)}… 与已验证版本不一致(上游已换代),换肤可能失效。`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
updateManifest((m) => {
|
|
||||||
m.resources.toolchain = { ...m.resources.toolchain, present: true, injectorSha256: sha };
|
|
||||||
});
|
|
||||||
logInfo(`注入器已更新 (${fs.statSync(CSLOL_DLL).size}B, sha256=${sha.slice(0, 12)}…)`);
|
|
||||||
return EXIT.OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hashes 库(可选,仅 wad-extract 需要):
|
* hashes 库(可选,仅 wad-extract 需要):
|
||||||
@@ -87,8 +54,6 @@ async function syncHashes(): Promise<ExitCode> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function runSync(opts: SyncOptions = {}): Promise<ExitCode> {
|
export async function runSync(opts: SyncOptions = {}): Promise<ExitCode> {
|
||||||
if (opts.only === "hashes") return syncHashes();
|
void opts;
|
||||||
if (opts.only === "toolchain") return syncToolchain();
|
return syncHashes();
|
||||||
// 默认:按需更新注入器;hashes 不主动拉(可选)
|
|
||||||
return syncToolchain();
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user