@bingine/cemem 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PORT-SCOPE.md +169 -0
- package/README.md +451 -2
- package/dist/cjs/addr/expression.js +650 -0
- package/dist/cjs/addr/expression.js.map +1 -0
- package/dist/cjs/addresslist/memory-record.js +612 -0
- package/dist/cjs/addresslist/memory-record.js.map +1 -0
- package/dist/cjs/asm/assembler.js +354 -0
- package/dist/cjs/asm/assembler.js.map +1 -0
- package/dist/cjs/asm/autoassembler-directives.js +362 -0
- package/dist/cjs/asm/autoassembler-directives.js.map +1 -0
- package/dist/cjs/asm/autoassembler.js +2163 -0
- package/dist/cjs/asm/autoassembler.js.map +1 -0
- package/dist/cjs/asm/backend.js +394 -0
- package/dist/cjs/asm/backend.js.map +1 -0
- package/dist/cjs/asm/disassembler.js +715 -0
- package/dist/cjs/asm/disassembler.js.map +1 -0
- package/dist/cjs/backend/dbk-backend.js +683 -0
- package/dist/cjs/backend/dbk-backend.js.map +1 -0
- package/dist/cjs/backend/native-ce-backend.js +667 -0
- package/dist/cjs/backend/native-ce-backend.js.map +1 -0
- package/dist/cjs/backend/self-backend.js +449 -0
- package/dist/cjs/backend/self-backend.js.map +1 -0
- package/dist/cjs/backend/types.js +48 -0
- package/dist/cjs/backend/types.js.map +1 -0
- package/dist/cjs/cheattable/ct-format.js +533 -0
- package/dist/cjs/cheattable/ct-format.js.map +1 -0
- package/dist/cjs/index.js +219 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/lua/aa.js +230 -0
- package/dist/cjs/lua/aa.js.map +1 -0
- package/dist/cjs/lua/addressing.js +118 -0
- package/dist/cjs/lua/addressing.js.map +1 -0
- package/dist/cjs/lua/api.js +736 -0
- package/dist/cjs/lua/api.js.map +1 -0
- package/dist/cjs/lua/asm.js +220 -0
- package/dist/cjs/lua/asm.js.map +1 -0
- package/dist/cjs/lua/bridge.js +526 -0
- package/dist/cjs/lua/bridge.js.map +1 -0
- package/dist/cjs/lua/marshal.js +562 -0
- package/dist/cjs/lua/marshal.js.map +1 -0
- package/dist/cjs/lua/memory.js +213 -0
- package/dist/cjs/lua/memory.js.map +1 -0
- package/dist/cjs/lua/objects.js +1070 -0
- package/dist/cjs/lua/objects.js.map +1 -0
- package/dist/cjs/lua/scan.js +543 -0
- package/dist/cjs/lua/scan.js.map +1 -0
- package/dist/cjs/lua/types.js +103 -0
- package/dist/cjs/lua/types.js.map +1 -0
- package/dist/cjs/scanner/matcher.js +781 -0
- package/dist/cjs/scanner/matcher.js.map +1 -0
- package/dist/cjs/scanner/pointer-scan.js +546 -0
- package/dist/cjs/scanner/pointer-scan.js.map +1 -0
- package/dist/cjs/scanner/scanner.js +513 -0
- package/dist/cjs/scanner/scanner.js.map +1 -0
- package/dist/cjs/scanner/shared-layout.js +207 -0
- package/dist/cjs/scanner/shared-layout.js.map +1 -0
- package/dist/cjs/types.js +190 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/value-codec.js +515 -0
- package/dist/cjs/value-codec.js.map +1 -0
- package/dist/cjs/win/process.js +475 -0
- package/dist/cjs/win/process.js.map +1 -0
- package/dist/cjs/win/scan-worker.js +84 -0
- package/dist/cjs/win/scan-worker.js.map +1 -0
- package/dist/cjs/win/winapi.js +403 -0
- package/dist/cjs/win/winapi.js.map +1 -0
- package/dist/types/addr/expression.d.ts +98 -0
- package/dist/types/addr/expression.d.ts.map +1 -0
- package/dist/types/addresslist/memory-record.d.ts +218 -0
- package/dist/types/addresslist/memory-record.d.ts.map +1 -0
- package/dist/types/asm/assembler.d.ts +106 -0
- package/dist/types/asm/assembler.d.ts.map +1 -0
- package/dist/types/asm/autoassembler-directives.d.ts +118 -0
- package/dist/types/asm/autoassembler-directives.d.ts.map +1 -0
- package/dist/types/asm/autoassembler.d.ts +434 -0
- package/dist/types/asm/autoassembler.d.ts.map +1 -0
- package/dist/types/asm/backend.d.ts +167 -0
- package/dist/types/asm/backend.d.ts.map +1 -0
- package/dist/types/asm/disassembler.d.ts +194 -0
- package/dist/types/asm/disassembler.d.ts.map +1 -0
- package/dist/types/backend/dbk-backend.d.ts +353 -0
- package/dist/types/backend/dbk-backend.d.ts.map +1 -0
- package/dist/types/backend/native-ce-backend.d.ts +213 -0
- package/dist/types/backend/native-ce-backend.d.ts.map +1 -0
- package/dist/types/backend/self-backend.d.ts +64 -0
- package/dist/types/backend/self-backend.d.ts.map +1 -0
- package/dist/types/backend/types.d.ts +176 -0
- package/dist/types/backend/types.d.ts.map +1 -0
- package/dist/types/cheattable/ct-format.d.ts +100 -0
- package/dist/types/cheattable/ct-format.d.ts.map +1 -0
- package/dist/types/index.d.ts +54 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/lua/aa.d.ts +107 -0
- package/dist/types/lua/aa.d.ts.map +1 -0
- package/dist/types/lua/addressing.d.ts +41 -0
- package/dist/types/lua/addressing.d.ts.map +1 -0
- package/dist/types/lua/api.d.ts +46 -0
- package/dist/types/lua/api.d.ts.map +1 -0
- package/dist/types/lua/asm.d.ts +42 -0
- package/dist/types/lua/asm.d.ts.map +1 -0
- package/dist/types/lua/bridge.d.ts +129 -0
- package/dist/types/lua/bridge.d.ts.map +1 -0
- package/dist/types/lua/marshal.d.ts +244 -0
- package/dist/types/lua/marshal.d.ts.map +1 -0
- package/dist/types/lua/memory.d.ts +61 -0
- package/dist/types/lua/memory.d.ts.map +1 -0
- package/dist/types/lua/objects.d.ts +210 -0
- package/dist/types/lua/objects.d.ts.map +1 -0
- package/dist/types/lua/scan.d.ts +106 -0
- package/dist/types/lua/scan.d.ts.map +1 -0
- package/dist/types/lua/types.d.ts +146 -0
- package/dist/types/lua/types.d.ts.map +1 -0
- package/dist/types/scanner/matcher.d.ts +122 -0
- package/dist/types/scanner/matcher.d.ts.map +1 -0
- package/dist/types/scanner/pointer-scan.d.ts +187 -0
- package/dist/types/scanner/pointer-scan.d.ts.map +1 -0
- package/dist/types/scanner/scanner.d.ts +150 -0
- package/dist/types/scanner/scanner.d.ts.map +1 -0
- package/dist/types/scanner/shared-layout.d.ts +96 -0
- package/dist/types/scanner/shared-layout.d.ts.map +1 -0
- package/dist/types/types.d.ts +239 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/value-codec.d.ts +134 -0
- package/dist/types/value-codec.d.ts.map +1 -0
- package/dist/types/win/process.d.ts +190 -0
- package/dist/types/win/process.d.ts.map +1 -0
- package/dist/types/win/scan-worker.d.ts +51 -0
- package/dist/types/win/scan-worker.d.ts.map +1 -0
- package/dist/types/win/winapi.d.ts +144 -0
- package/dist/types/win/winapi.d.ts.map +1 -0
- package/docs/assembler.md +371 -0
- package/docs/autoassembler.md +423 -0
- package/docs/backend-dbk.md +305 -0
- package/docs/backends.md +273 -0
- package/docs/evidence/atomics-wait-io.js +20 -0
- package/docs/evidence/mini-repro.js +98 -0
- package/docs/lua-api.md +718 -0
- package/package.json +60 -4
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 扫描比较器 —— 逐项对齐 Cheat Engine `memscan.pas` 的 TScanner 比较函数族。
|
|
3
|
+
*
|
|
4
|
+
* CE 的结构是:为每种变量类型维护一个 typesmatch[] 布尔数组,每次比较把所有
|
|
5
|
+
* 候选类型的比较结果 AND 进去,最后只要还有任何一个类型匹配就保留该地址。
|
|
6
|
+
* 这里用等价的实现:对每个候选类型分别收集命中的地址。
|
|
7
|
+
*
|
|
8
|
+
* 已对齐的语义(含容易搞错的细节):
|
|
9
|
+
* - SingleExtremeRounded 用闭区间 [min,max];DoubleExtremeRounded 用开区间 (min,max)
|
|
10
|
+
* —— 见 TScanner.SingleExact / DoubleExact,CE 自身在这里就不一致。
|
|
11
|
+
* - Truncated 用 [value, value+step) 左闭右开。
|
|
12
|
+
* - Rounded 用 RoundTo(actual, -accuracy) === value(含 FPC 的偶舍入)。
|
|
13
|
+
* - IncreasedValueBy / DecreasedValueBy 对浮点会先比较原始位模式不等,再按精度判等。
|
|
14
|
+
* - 百分比系列的边界开闭、以及 dvalue>dvalue2 时自动交换。
|
|
15
|
+
* - 字符串不区分大小写时只把 ASCII a-z 折叠为大写(ANSI)/
|
|
16
|
+
* 用 UpCase(Unicode),对齐 CaseInsensitiveAnsiStringExact / UnicodeString。
|
|
17
|
+
* - AOB 支持半字节通配,对齐 parseAob 的掩码语义。
|
|
18
|
+
*/
|
|
19
|
+
import { BinaryPattern, FloatCompareParams, PatternByte, roundTo } from '../value-codec';
|
|
20
|
+
import { RoundingType, ScanConfig, ScanOption, VariableType } from '../types';
|
|
21
|
+
/** 整数类型的比较参数 */
|
|
22
|
+
interface IntCompare {
|
|
23
|
+
/** 主值(无符号 64 位语义) */
|
|
24
|
+
v: bigint;
|
|
25
|
+
/** 上界(区间用) */
|
|
26
|
+
v2: bigint;
|
|
27
|
+
signed: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** 单个变量类型的编译后比较规则 */
|
|
30
|
+
interface TypeRule {
|
|
31
|
+
type: VariableType;
|
|
32
|
+
width: number;
|
|
33
|
+
/** 值比较 */
|
|
34
|
+
ints?: IntCompare;
|
|
35
|
+
floats?: FloatCompareParams;
|
|
36
|
+
/** 字符串目标与长度(字节) */
|
|
37
|
+
str?: {
|
|
38
|
+
target: string;
|
|
39
|
+
upper: string;
|
|
40
|
+
byteLength: number;
|
|
41
|
+
wide: boolean;
|
|
42
|
+
caseSensitive: boolean;
|
|
43
|
+
};
|
|
44
|
+
/** AOB 模式 */
|
|
45
|
+
aob?: {
|
|
46
|
+
pattern: PatternByte[];
|
|
47
|
+
nibble: boolean;
|
|
48
|
+
};
|
|
49
|
+
/** 二进制位串 */
|
|
50
|
+
bin?: {
|
|
51
|
+
pattern: BinaryPattern;
|
|
52
|
+
width: number;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** 编译后的扫描配置 */
|
|
56
|
+
export interface CompiledConfig {
|
|
57
|
+
option: ScanOption;
|
|
58
|
+
rounding: RoundingType;
|
|
59
|
+
/** 需要参与比较的每种类型一条规则;vtAll 会展开为 6 条数值规则 */
|
|
60
|
+
rules: TypeRule[];
|
|
61
|
+
/** 各种类型都开启(vtAll 的"全部"扫描) */
|
|
62
|
+
isAll: boolean;
|
|
63
|
+
/** 反选(排除匹配) */
|
|
64
|
+
inverse: boolean;
|
|
65
|
+
/** 浮点扫描时排除带指数的浮点数 */
|
|
66
|
+
floatScanWithoutExponents: boolean;
|
|
67
|
+
/** 对齐步长;0 表示逐字节 */
|
|
68
|
+
alignment: number;
|
|
69
|
+
/** 分组长扫描 */
|
|
70
|
+
group?: CompiledGroup;
|
|
71
|
+
}
|
|
72
|
+
/** 编译后的分组长扫描 */
|
|
73
|
+
export interface CompiledGroup {
|
|
74
|
+
elements: CompiledGroupElement[];
|
|
75
|
+
/** 整块总宽度 */
|
|
76
|
+
blockSize: number;
|
|
77
|
+
outOfOrder: boolean;
|
|
78
|
+
/** 乱序且要求按宽度对齐 */
|
|
79
|
+
outOfOrderAligned: boolean;
|
|
80
|
+
}
|
|
81
|
+
interface CompiledGroupElement {
|
|
82
|
+
type: VariableType;
|
|
83
|
+
wildcard: boolean;
|
|
84
|
+
width: number;
|
|
85
|
+
ints?: IntCompare;
|
|
86
|
+
floats?: FloatCompareParams;
|
|
87
|
+
str?: {
|
|
88
|
+
target: string;
|
|
89
|
+
upper: string;
|
|
90
|
+
byteLength: number;
|
|
91
|
+
wide: boolean;
|
|
92
|
+
caseSensitive: boolean;
|
|
93
|
+
};
|
|
94
|
+
aob?: {
|
|
95
|
+
pattern: PatternByte[];
|
|
96
|
+
nibble: boolean;
|
|
97
|
+
};
|
|
98
|
+
bin?: {
|
|
99
|
+
pattern: BinaryPattern;
|
|
100
|
+
width: number;
|
|
101
|
+
};
|
|
102
|
+
/** 乱序模式下的输出偏移 */
|
|
103
|
+
outOffset?: number;
|
|
104
|
+
}
|
|
105
|
+
/** 是否需要保存"上一次的值"(next scan 的旧值语义)。 */
|
|
106
|
+
export declare function optionNeedsOldValue(option: ScanOption): boolean;
|
|
107
|
+
/** 编译用户扫描配置。 */
|
|
108
|
+
export declare function compileConfig(cfg: ScanConfig): CompiledConfig;
|
|
109
|
+
/**
|
|
110
|
+
* 在内存块中扫描,把命中的"块内偏移"写入 out。
|
|
111
|
+
*
|
|
112
|
+
* @param cc 编译后的配置
|
|
113
|
+
* @param buf 当前内存块
|
|
114
|
+
* @param prev 上一次扫描时同一区域的原始内存(next scan 用);长度与 buf 对齐
|
|
115
|
+
* @param out 输出块内偏移
|
|
116
|
+
* @param outMatches 输出每个偏移命中的规则下标(与 out 平行,按类型区分结果时需要)
|
|
117
|
+
*/
|
|
118
|
+
export declare function scanBlock(cc: CompiledConfig, buf: Buffer, prev: Buffer | null, out: number[], outMatches?: number[][]): void;
|
|
119
|
+
/** 用编译后的配置测试"某个值是否匹配"(便于单元测试,不依赖进程)。 */
|
|
120
|
+
export declare function testValueMatches(cfg: ScanConfig, current: Buffer, previous?: Buffer): boolean[];
|
|
121
|
+
export { roundTo };
|
|
122
|
+
//# sourceMappingURL=matcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matcher.d.ts","sourceRoot":"","sources":["../../../src/scanner/matcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 指针扫描 —— 对齐 Cheat Engine `pointerscancontroller.pas` / `pointerscanstructures.pas`。
|
|
3
|
+
*
|
|
4
|
+
* 目标:找出 "基址 + 多级偏移" 表达式,使得
|
|
5
|
+
* `[ [ [base + off[N]] + off[N-1] ] ... + off[1] ] == targetAddress`
|
|
6
|
+
*
|
|
7
|
+
* CE 的做法分两步(本实现一致):
|
|
8
|
+
* 1. 建立**指针映射**(pointermap):遍历目标进程所有可扫描段,
|
|
9
|
+
* 把每个"看起来像指针"的槽位记为 (存放地址 storeAddr, 指向值 ptrValue)。
|
|
10
|
+
* 只收对齐槽位,且 ptrValue 必须落在某个已提交区域内(否则不可能是有效指针)。
|
|
11
|
+
* 2. **逐级回溯**:第 0 级的"待匹配地址集合"只有 targetAddress。
|
|
12
|
+
* 对每一级,在指针映射里找那些 ptrValue ∈ 当前集合的槽位;
|
|
13
|
+
* 于是 storeAddr 成为上一级的待匹配地址,偏移 = 当前地址 − ptrValue。
|
|
14
|
+
* 重复 maxLevel 次,得到 基址 → 偏移链。
|
|
15
|
+
* 最后按 maxOffset / maxDifferentOffsets / 是否允许负偏移做剪枝。
|
|
16
|
+
*
|
|
17
|
+
* 与 CE 的差异:
|
|
18
|
+
* - CE 把指针映射缓存为 .scandata 二进制文件(PSCAN 格式)可跨会话复用;
|
|
19
|
+
* 本实现提供内存缓存,并支持导出为 JSON(格式见 docs/pscan-format.md),
|
|
20
|
+
* 与 CE 的二进制格式不兼容。
|
|
21
|
+
* - CE 的 "Only find static addresses" 用模块列表判定;这里用 MEM_IMAGE 判定。
|
|
22
|
+
*/
|
|
23
|
+
import { Process } from '../win/process';
|
|
24
|
+
/** 一条指针扫描结果:基址 + 偏移链 */
|
|
25
|
+
export interface PointerPath {
|
|
26
|
+
/** 基址(第 maxLevel 级的存放地址,通常落在模块的静态区) */
|
|
27
|
+
baseAddress: bigint;
|
|
28
|
+
/**
|
|
29
|
+
* 偏移链,按"从基址到目标"的顺序排列。
|
|
30
|
+
* 应用方式:addr = base; for (const o of offsets) { addr = readPtr(addr) + o } —— 见 applyPointerPath。
|
|
31
|
+
*/
|
|
32
|
+
offsets: number[];
|
|
33
|
+
/** 该路径最终指向的地址(等于扫描时的 targetAddress) */
|
|
34
|
+
targetAddress: bigint;
|
|
35
|
+
/**
|
|
36
|
+
* 该路径的层数(偏移链长度)。
|
|
37
|
+
*
|
|
38
|
+
* 仅在 `preferShallow: true` 时填充 —— 那种模式下会汇总多个层级的路径,
|
|
39
|
+
* 调用方需要知道每条的层数才能做排序与展示。默认的单深度模式不填(保持原形状)。
|
|
40
|
+
*/
|
|
41
|
+
depth?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface PointerScanOptions {
|
|
44
|
+
/** 最大层级(偏移个数)。CE 默认 5。 */
|
|
45
|
+
maxLevel?: number;
|
|
46
|
+
/** 单级偏移的最大绝对值。CE 默认 2048。 */
|
|
47
|
+
maxOffset?: number;
|
|
48
|
+
/**
|
|
49
|
+
* 汇总"层数 ≤ maxLevel"的**全部**路径,而不是只返回恰好等于 maxLevel 的。
|
|
50
|
+
*
|
|
51
|
+
* 为什么需要:只返回精确深度的路径时,调用方为了拿到那条又浅又稳的路径,
|
|
52
|
+
* 只能逐级重扫(慢),而且深层候选一多,浅层路径会被结果上限挤掉。
|
|
53
|
+
* 打开这个开关后一次扫描就拿到全部层级,并按「静态基址优先、层数浅优先」排序,
|
|
54
|
+
* 再由 maxResults 截断 —— 这正是 CE 指针扫描的实际语义。
|
|
55
|
+
*
|
|
56
|
+
* 打开时每条结果的 `depth` 会被填充。默认 false(保持原行为)。
|
|
57
|
+
*/
|
|
58
|
+
preferShallow?: boolean;
|
|
59
|
+
/** 每个存放地址允许的最大不同偏移数量(0 = 不限制)。 */
|
|
60
|
+
maxDifferentOffsets?: number;
|
|
61
|
+
/** 是否允许负偏移 */
|
|
62
|
+
allowNegativeOffsets?: boolean;
|
|
63
|
+
/** 是否只保留最终基址位于静态区(可执行映像)的路径 */
|
|
64
|
+
staticOnly?: boolean;
|
|
65
|
+
/** 指针宽度:4 或 8。默认按目标进程位数推断。 */
|
|
66
|
+
pointerSize?: 4 | 8;
|
|
67
|
+
/** 需要跳过的段(例如已知的栈区),避免噪声 */
|
|
68
|
+
excludeSegments?: Array<{
|
|
69
|
+
start: bigint;
|
|
70
|
+
end: bigint;
|
|
71
|
+
}>;
|
|
72
|
+
/** 最多返回多少条路径(0 = 不限制)。默认 0。 */
|
|
73
|
+
maxResults?: number;
|
|
74
|
+
/** 最多处理多少个段(防护,0 = 不限制) */
|
|
75
|
+
maxSegments?: number;
|
|
76
|
+
/** 进度回调 */
|
|
77
|
+
onProgress?: (stage: string, current: number, total: number) => void;
|
|
78
|
+
}
|
|
79
|
+
export interface PointerScanStats {
|
|
80
|
+
/** 建档的段数 */
|
|
81
|
+
segmentsScanned: number;
|
|
82
|
+
/** 建档读取的字节数 */
|
|
83
|
+
bytesIndexed: bigint;
|
|
84
|
+
/** 指针映射条目数 */
|
|
85
|
+
pointerEntries: number;
|
|
86
|
+
/** 每一级找到的候选数量 */
|
|
87
|
+
levelCounts: number[];
|
|
88
|
+
elapsedMs: number;
|
|
89
|
+
/** 最终返回的路径数(**已通过可解析性验证**) */
|
|
90
|
+
resultCount: number;
|
|
91
|
+
/**
|
|
92
|
+
* 因**无法解析**(中间指针读不通,或解析结果不等于目标地址)而被丢弃的
|
|
93
|
+
* 候选数量。见 `scan()` 里"返回值验证"那段的说明。
|
|
94
|
+
*
|
|
95
|
+
* 这个数字有意义:目标进程的动态内存越多(例如 V8 堆),按值匹配产生的
|
|
96
|
+
* "值巧合"候选就越多,丢弃量也越大。数量异常大时说明结果被环境噪声稀释。
|
|
97
|
+
*/
|
|
98
|
+
droppedUnresolvable: number;
|
|
99
|
+
/**
|
|
100
|
+
* **通过验证的候选总数**(尚未按 `maxResults` 截断)。
|
|
101
|
+
*
|
|
102
|
+
* 与 `resultCount` 的区别:`resultCount` 是最终返回的条数(已受上限约束),
|
|
103
|
+
* 而它是在上限之外还剩多少可用路径。`candidateCount > resultCount`
|
|
104
|
+
* 即说明**结果触及了上限**、可能有更多路径没返回 ——
|
|
105
|
+
* 调用方据此决定是否提示用户调大 `maxResults`。
|
|
106
|
+
*/
|
|
107
|
+
candidateCount: number;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 指针扫描器。
|
|
111
|
+
*
|
|
112
|
+
* ```ts
|
|
113
|
+
* const ps = new PointerScanner(proc, { maxLevel: 5, maxOffset: 2048 });
|
|
114
|
+
* const paths = ps.scan(targetAddress);
|
|
115
|
+
* for (const p of paths) console.log(formatPointerPath(p));
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export declare class PointerScanner {
|
|
119
|
+
private readonly proc;
|
|
120
|
+
private readonly opts;
|
|
121
|
+
/** 缓存的指针映射(同一进程可复用) */
|
|
122
|
+
private pointerMap;
|
|
123
|
+
/** 记录静态区范围,用于 staticOnly 判定 */
|
|
124
|
+
private staticRanges;
|
|
125
|
+
/** 已提交区域范围(升序),用于判定某值是否可能是有效指针 */
|
|
126
|
+
private committedRanges;
|
|
127
|
+
private lastStats;
|
|
128
|
+
constructor(process: Process, options?: PointerScanOptions);
|
|
129
|
+
get stats(): PointerScanStats | null;
|
|
130
|
+
/** 指针映射是否已建好(可跳过一次全量建档)。 */
|
|
131
|
+
get hasPointerMap(): boolean;
|
|
132
|
+
/** 丢弃已建好的指针映射。 */
|
|
133
|
+
clearPointerMap(): void;
|
|
134
|
+
/** 执行指针扫描。 */
|
|
135
|
+
scan(targetAddress: bigint): PointerPath[];
|
|
136
|
+
/**
|
|
137
|
+
* 沿着一条路径解析出最终地址(用于验证结果)。
|
|
138
|
+
* `readPtr` 由调用方提供,便于在测试中注入。
|
|
139
|
+
*/
|
|
140
|
+
resolvePath(path: PointerPath, readPtr?: (addr: bigint) => bigint): bigint;
|
|
141
|
+
/** 读取目标进程中的一个指针值。 */
|
|
142
|
+
readPointer(address: bigint): bigint;
|
|
143
|
+
private lastSegmentCount;
|
|
144
|
+
private lastBytesIndexed;
|
|
145
|
+
/** 建立(或复用)指针映射。 */
|
|
146
|
+
private ensurePointerMap;
|
|
147
|
+
/** 该值是否落在某个已提交区域(有效指针的基本条件)。 */
|
|
148
|
+
private isCommittedAddress;
|
|
149
|
+
/**
|
|
150
|
+
* 二分查找"落在 [lo, hi] 区间内"的条目下标范围 `[from, to)`。
|
|
151
|
+
*
|
|
152
|
+
* 这是指针扫描的关键索引操作:`expandLevel` 需要拿到**所有在 maxOffset 窗口内**
|
|
153
|
+
* 指向当前层地址的槽位,再由调用方逐条算出精确偏移。
|
|
154
|
+
*
|
|
155
|
+
* ⚠️ 曾经的严重缺陷:这里原来做的是"值**恰好等于** target"的精确查找,
|
|
156
|
+
* 于是 `expandLevel` 里 `offset = path.address - entry.value` **恒为 0**,
|
|
157
|
+
* `maxOffset` 成了死代码 —— 表现为"只要偏移不为 0 就永远找不到指针路径",
|
|
158
|
+
* 而真实游戏里指针几乎总带偏移,等于指针扫描功能基本失效。
|
|
159
|
+
* 回归测试见 `test/pointer-scan.test.js` 的 "maxOffset 必须真正生效"。
|
|
160
|
+
*/
|
|
161
|
+
private findValueRange;
|
|
162
|
+
/** 第一个 `value >= bound` 的下标(标准 lower_bound)。 */
|
|
163
|
+
private lowerBound;
|
|
164
|
+
/** 第一个 `value > bound` 的下标(标准 upper_bound)。 */
|
|
165
|
+
private upperBound;
|
|
166
|
+
/**
|
|
167
|
+
* 展开一级:对当前每个"待匹配地址",找出所有指向它的槽位,
|
|
168
|
+
* 生成新的候选(存放地址 + 偏移)。
|
|
169
|
+
*/
|
|
170
|
+
private expandLevel;
|
|
171
|
+
private isStatic;
|
|
172
|
+
private isExcludedRange;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 把偏移链渲染成 CE 风格的表达式。
|
|
176
|
+
*
|
|
177
|
+
* CE 显示顺序是"从基址到目标":
|
|
178
|
+
* `"game.exe"+00123ABC -> 0x10 -> 0x2C -> 0x8`
|
|
179
|
+
* 本实现的 offsets 数组即该顺序。
|
|
180
|
+
*/
|
|
181
|
+
export declare function formatPointerPath(path: PointerPath, moduleOf?: (addr: bigint) => {
|
|
182
|
+
name: string;
|
|
183
|
+
base: bigint;
|
|
184
|
+
} | null): string;
|
|
185
|
+
/** 应用一条路径得到最终地址(便捷函数)。 */
|
|
186
|
+
export declare function applyPointerPath(path: PointerPath, readPointer: (addr: bigint) => bigint): bigint;
|
|
187
|
+
//# sourceMappingURL=pointer-scan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pointer-scan.d.ts","sourceRoot":"","sources":["../../../src/scanner/pointer-scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 扫描会话 —— 对齐 Cheat Engine `memscan.pas` 的 TMemScan / TScanController。
|
|
3
|
+
*
|
|
4
|
+
* 一次会话的典型生命周期(与 CE 界面操作一一对应):
|
|
5
|
+
* const scan = new ScanSession(process);
|
|
6
|
+
* scan.firstScan(cfg); // 首次扫描(对应 CE「首次扫描」)
|
|
7
|
+
* scan.nextScan({ ...cfg, scanOption: ScanOption.DecreasedValue }); // 再次扫描
|
|
8
|
+
* scan.results // bigint[] 结果地址
|
|
9
|
+
*
|
|
10
|
+
* 关键状态:
|
|
11
|
+
* - chunks:本会话实际读过的内存块(地址、长度)—— 对齐 CE 的 MemoryRegions 副本;
|
|
12
|
+
* - addressMap:块内偏移 → 结果地址 的换算依据;
|
|
13
|
+
* - snapshot:块原始内存副本 —— 对齐 CE 的 SecondaryAddressBuffer 机制,
|
|
14
|
+
* next scan 的「旧值」就来自这里。
|
|
15
|
+
*
|
|
16
|
+
* 性能:默认使用 worker 线程并行读取+比较(对齐 CE 的多线程扫描),
|
|
17
|
+
* 实测本机 ReadProcessMemory 约 1.9 GB/s。
|
|
18
|
+
*/
|
|
19
|
+
import { Process } from '../win/process';
|
|
20
|
+
import { ScanConfig, ScanProgressCallback, ScanResult, VariableType } from '../types';
|
|
21
|
+
/** 一个已读取的内存块 */
|
|
22
|
+
export interface Chunk {
|
|
23
|
+
/** 块在目标进程中的起始地址 */
|
|
24
|
+
address: bigint;
|
|
25
|
+
/** 块长度(字节) */
|
|
26
|
+
size: number;
|
|
27
|
+
/** 首次扫描时保留的原始内存副本(用于 next scan 的旧值比较与未变动检测) */
|
|
28
|
+
snapshot: Buffer | null;
|
|
29
|
+
/** 本块命中的块内偏移 */
|
|
30
|
+
offsets: number[];
|
|
31
|
+
/** 每个命中偏移对应的规则下标(all 模式用) */
|
|
32
|
+
matches: number[][] | null;
|
|
33
|
+
}
|
|
34
|
+
export interface ScanSessionOptions {
|
|
35
|
+
/** 扫描块大小(字节)。默认 4MB。 */
|
|
36
|
+
blockSize?: number;
|
|
37
|
+
/** 是否用 worker 线程并行扫描,默认 true(块数足够多时才生效) */
|
|
38
|
+
parallel?: boolean;
|
|
39
|
+
/** 并行 worker 数,默认 CPU 逻辑核数(上限 8) */
|
|
40
|
+
workerCount?: number;
|
|
41
|
+
/**
|
|
42
|
+
* 启用并行的最小扫描体量(字节)。默认 96MB。
|
|
43
|
+
* 小于该值时 worker 启动开销大于收益,直接用单线程。
|
|
44
|
+
*/
|
|
45
|
+
parallelMinBytes?: number;
|
|
46
|
+
/**
|
|
47
|
+
* 原始内存快照的总字节上限。默认 1.5GB。
|
|
48
|
+
* 超过时不保留快照(结果仍有效,但无法继续 next scan 的「旧值」类比较)。
|
|
49
|
+
*/
|
|
50
|
+
maxSnapshotBytes?: number;
|
|
51
|
+
/** 进度回调 */
|
|
52
|
+
onProgress?: ScanProgressCallback;
|
|
53
|
+
}
|
|
54
|
+
export interface ScanStats {
|
|
55
|
+
/** 扫描的区域数 */
|
|
56
|
+
regionsScanned: number;
|
|
57
|
+
/** 扫描的字节数 */
|
|
58
|
+
bytesScanned: bigint;
|
|
59
|
+
/** 本次扫描耗时(毫秒) */
|
|
60
|
+
elapsedMs: number;
|
|
61
|
+
/** 首次扫描后结果数 */
|
|
62
|
+
resultCount: number;
|
|
63
|
+
/** 是否保留了快照(可继续 next scan) */
|
|
64
|
+
hasSnapshot: boolean;
|
|
65
|
+
/** 实际使用的 worker 数 */
|
|
66
|
+
workersUsed: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 一个进程上的扫描会话。
|
|
70
|
+
*
|
|
71
|
+
* 与 CE 的差异说明:CE 的 TMemScan 与 GUI 强耦合(进度窗口、保存扫描结果文件),
|
|
72
|
+
* 这里保持纯数据/纯流程,进度通过回调暴露。
|
|
73
|
+
*/
|
|
74
|
+
export declare class ScanSession {
|
|
75
|
+
/** 本会话读过的内存块 */
|
|
76
|
+
readonly chunks: Chunk[];
|
|
77
|
+
private readonly proc;
|
|
78
|
+
private readonly opts;
|
|
79
|
+
private compiled;
|
|
80
|
+
private config;
|
|
81
|
+
private resultAddresses;
|
|
82
|
+
private snapshotBytes;
|
|
83
|
+
private lastStats;
|
|
84
|
+
/** 是否有可比较的旧值(决定 next scan 的可用选项) */
|
|
85
|
+
private hasOldValues;
|
|
86
|
+
/** 是否在下次扫描时忽略对齐(内部用) */
|
|
87
|
+
private truncatedChunks;
|
|
88
|
+
/** 并行路径中由主线程补扫的块数(worker 启动失败时 > 0) */
|
|
89
|
+
private fallbackChunks;
|
|
90
|
+
/** 并行路径被证明不可用时置位,后续扫描退化到单线程 */
|
|
91
|
+
private parallelDisabled;
|
|
92
|
+
/** worker 池(惰性创建,跨扫描复用) */
|
|
93
|
+
private workers;
|
|
94
|
+
private workerReady;
|
|
95
|
+
constructor(process: Process, options?: ScanSessionOptions);
|
|
96
|
+
get process(): Process;
|
|
97
|
+
/** 结果地址列表 */
|
|
98
|
+
get results(): readonly bigint[];
|
|
99
|
+
/** 结果数量 */
|
|
100
|
+
get resultCount(): number;
|
|
101
|
+
/** 上一次扫描的统计信息 */
|
|
102
|
+
get stats(): ScanStats | null;
|
|
103
|
+
/** 是否可以继续 next scan(CE 里显示为「再次扫描」是否可用) */
|
|
104
|
+
get canNextScan(): boolean;
|
|
105
|
+
/** 把扫描区域切成块,尽量与区域边界对齐。 */
|
|
106
|
+
private buildChunks;
|
|
107
|
+
/**
|
|
108
|
+
* 首次扫描。对应 CE 的 TMemScan.firstScan。
|
|
109
|
+
*
|
|
110
|
+
* @param config 扫描配置
|
|
111
|
+
*/
|
|
112
|
+
firstScan(config: ScanConfig): ScanStats;
|
|
113
|
+
/**
|
|
114
|
+
* 再次扫描。对应 CE 的 TMemScan.nextScan。
|
|
115
|
+
*
|
|
116
|
+
* 重要语义:nextScan 只在上一次的结果地址范围内重新比较,
|
|
117
|
+
* 因此需要上一次快照提供的「旧值」。若首次扫描时因内存上限未保留快照,
|
|
118
|
+
* 则只支持不需要旧值的选项(精确值/区间/大于/小于)。
|
|
119
|
+
*/
|
|
120
|
+
nextScan(config: ScanConfig): ScanStats;
|
|
121
|
+
/** 首次扫描:单线程或 worker 并行。 */
|
|
122
|
+
private runScan;
|
|
123
|
+
/** worker 并行路径:每个 worker 一个独立共享段,Atomics 同步等待。 */
|
|
124
|
+
private runParallel;
|
|
125
|
+
/** 单块扫描(单线程路径)。 */
|
|
126
|
+
private scanChunkSingle;
|
|
127
|
+
/** 单线程扫描循环,带进度回调。 */
|
|
128
|
+
private progressLoop;
|
|
129
|
+
/** next scan:只在存活块的旧偏移上比较。 */
|
|
130
|
+
private runNextScan;
|
|
131
|
+
/**
|
|
132
|
+
* 单点比较。返回命中的规则下标数组(all 模式),未命中返回 null。
|
|
133
|
+
*
|
|
134
|
+
* 实现方式:把该地址周围一小段窗口交给 scanBlock,
|
|
135
|
+
* 因此"命中"等价于 CE 在 SecondaryAddressBuffer 上做的逐点比较。
|
|
136
|
+
*/
|
|
137
|
+
private compareSingle;
|
|
138
|
+
/** 把各块的块内偏移汇总成结果地址列表。 */
|
|
139
|
+
private collectResults;
|
|
140
|
+
/** 读取结果地址列表(bigint)。 */
|
|
141
|
+
getResults(): bigint[];
|
|
142
|
+
/** 读取结果地址与当前值。 */
|
|
143
|
+
getResultValues(type: VariableType, max?: number): ScanResult[];
|
|
144
|
+
private widthOfType;
|
|
145
|
+
/** 清空会话状态。 */
|
|
146
|
+
reset(): void;
|
|
147
|
+
/** 释放 worker 资源(不关闭进程句柄)。 */
|
|
148
|
+
dispose(): void;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../../src/scanner/scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 并行扫描的共享内存布局。
|
|
3
|
+
*
|
|
4
|
+
* 为什么需要共享内存:扫描 API 是同步的(对齐 CE 的 firstScan/nextScan 阻塞语义),
|
|
5
|
+
* 而 Node 主线程在同步函数里无法 await worker 的 postMessage。
|
|
6
|
+
* 因此主线程把结果区/快照区放在 SharedArrayBuffer 里,用 Atomics.wait 阻塞等待。
|
|
7
|
+
*
|
|
8
|
+
* 容量约束(重要):
|
|
9
|
+
* V8 对单个 SharedArrayBuffer 约 1.5GB 上限(实测 1610612736 字节即抛
|
|
10
|
+
* RangeError)。因此**每个 worker 批次一个独立的、容量受控的共享段**,
|
|
11
|
+
* 而不是"N 块 × 4MB"组成的单个大段。
|
|
12
|
+
*
|
|
13
|
+
* 布局(按 8 字节对齐):
|
|
14
|
+
* [0] control:Int32Array(CONTROL_INTS)
|
|
15
|
+
* [0] 待处理块数 [1] 已完成块数 [2] 失败块数 [3] 快照区水位
|
|
16
|
+
* [1] slots:每块一个槽(2 个头 + slotCapacity 个偏移)
|
|
17
|
+
* slots[i*SLOT_STRIDE + 0] = 命中数
|
|
18
|
+
* slots[i*SLOT_STRIDE + 1] = 是否截断
|
|
19
|
+
* slots[i*SLOT_STRIDE + 2 ..] = 命中偏移
|
|
20
|
+
* [2] markers:每块 4 字节;-1 表示未保留快照,否则为 arena 内偏移
|
|
21
|
+
* [3] handled:每块 4 字节;1 表示该块已被 worker 处理(用于检测 worker 启动失败)
|
|
22
|
+
* [4] arena:快照数据区
|
|
23
|
+
*/
|
|
24
|
+
/** 默认每块结果槽可容纳的偏移数(64KB / 4B) */
|
|
25
|
+
export declare const DEFAULT_SLOT_CAPACITY: number;
|
|
26
|
+
/** control 区 Int32 数量 */
|
|
27
|
+
export declare const CONTROL_INTS = 8;
|
|
28
|
+
export declare const CTRL_PENDING = 0;
|
|
29
|
+
export declare const CTRL_DONE = 1;
|
|
30
|
+
export declare const CTRL_FAILED = 2;
|
|
31
|
+
export declare const CTRL_ARENA_USED = 3;
|
|
32
|
+
/** V8 对 SharedArrayBuffer 的实际上限(留出安全余量) */
|
|
33
|
+
export declare const SAB_MAX_BYTES = 1000000000;
|
|
34
|
+
export interface SharedLayout {
|
|
35
|
+
/** 共享内存本体 */
|
|
36
|
+
buffer: SharedArrayBuffer;
|
|
37
|
+
/** 结果槽区 */
|
|
38
|
+
slots: Int32Array;
|
|
39
|
+
/** 快照标记区 */
|
|
40
|
+
markers: Int32Array;
|
|
41
|
+
/** 块处理标记区 */
|
|
42
|
+
handled: Int32Array;
|
|
43
|
+
/** 快照数据区 */
|
|
44
|
+
arena: Uint8Array;
|
|
45
|
+
/** 控制区 */
|
|
46
|
+
control: Int32Array;
|
|
47
|
+
/** 每块的槽跨度(Int32 个数) */
|
|
48
|
+
slotStride: number;
|
|
49
|
+
slotCapacity: number;
|
|
50
|
+
jobCount: number;
|
|
51
|
+
arenaCapacity: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 构建一个批次用的共享布局。
|
|
55
|
+
*
|
|
56
|
+
* @param jobCount 本批次的块数
|
|
57
|
+
* @param slotCapacity 每块的偏移容量
|
|
58
|
+
* @param arenaCapacity 快照区容量(字节)
|
|
59
|
+
* @param existing 已存在的共享内存(worker 侧重建视图时传入);
|
|
60
|
+
* 省略则新建一块。
|
|
61
|
+
*/
|
|
62
|
+
export declare function createBatchLayout(jobCount: number, slotCapacity: number, arenaCapacity: number, existing?: SharedArrayBuffer): SharedLayout;
|
|
63
|
+
/** 重置共享区状态。 */
|
|
64
|
+
export declare function resetShared(layout: SharedLayout): void;
|
|
65
|
+
/** worker 侧:写入某块的命中偏移列表。 */
|
|
66
|
+
export declare function writeSlot(layout: SharedLayout, index: number, offsets: number[]): void;
|
|
67
|
+
/** 主线程侧:读取某块的命中偏移。 */
|
|
68
|
+
export declare function readSlot(layout: SharedLayout, index: number): {
|
|
69
|
+
offsets: number[];
|
|
70
|
+
truncated: boolean;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* worker 侧:在快照竞技场中为某块抢占空间并写入。
|
|
74
|
+
* 空间不足时放弃保留(next scan 会退化为"无旧值可用")。
|
|
75
|
+
*/
|
|
76
|
+
export declare function writeSnapshot(layout: SharedLayout, index: number, data: Buffer): boolean;
|
|
77
|
+
/** 主线程侧:读取某块的快照。 */
|
|
78
|
+
export declare function readSnapshot(layout: SharedLayout, index: number, size: number): Buffer | null;
|
|
79
|
+
/** worker 侧:标记本块已完成。 */
|
|
80
|
+
export declare function markDone(layout: SharedLayout, failed: boolean): void;
|
|
81
|
+
/** worker 侧:标记本块已被处理(无论成功与否)。 */
|
|
82
|
+
export declare function markHandled(layout: SharedLayout, index: number): void;
|
|
83
|
+
/** 主线程侧:同步等待本批次所有块完成。 */
|
|
84
|
+
export declare function waitAll(layout: SharedLayout, timeoutMs?: number): boolean;
|
|
85
|
+
export interface WorkerRequestLayout {
|
|
86
|
+
buffer: SharedArrayBuffer;
|
|
87
|
+
ctrl: Int32Array;
|
|
88
|
+
data: Uint8Array;
|
|
89
|
+
capacity: number;
|
|
90
|
+
}
|
|
91
|
+
export type WorkerReplyLayout = WorkerRequestLayout;
|
|
92
|
+
/** 创建请求/响应区(主线程侧) */
|
|
93
|
+
export declare function createWorkerRequestLayout(ctrlInts: number, capacity: number): WorkerRequestLayout;
|
|
94
|
+
/** 创建响应区(主线程侧)。与请求区布局相同,单独成函数便于两端语义对称。 */
|
|
95
|
+
export declare function createWorkerReplyLayout(ctrlInts: number, capacity: number): WorkerReplyLayout;
|
|
96
|
+
//# sourceMappingURL=shared-layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-layout.d.ts","sourceRoot":"","sources":["../../../src/scanner/shared-layout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,gCAAgC;AAChC,eAAO,MAAM,qBAAqB,QAAY,CAAC;AAE/C,yBAAyB;AACzB,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,2CAA2C;AAC3C,eAAO,MAAM,aAAa,aAAgB,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,aAAa;IACb,MAAM,EAAE,iBAAiB,CAAC;IAC1B,WAAW;IACX,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY;IACZ,OAAO,EAAE,UAAU,CAAC;IACpB,aAAa;IACb,OAAO,EAAE,UAAU,CAAC;IACpB,YAAY;IACZ,KAAK,EAAE,UAAU,CAAC;IAClB,UAAU;IACV,OAAO,EAAE,UAAU,CAAC;IACpB,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,YAAY,CA0Cd;AAMD,eAAe;AACf,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAYtD;AAED,4BAA4B;AAC5B,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAQtF;AAED,sBAAsB;AACtB,wBAAgB,QAAQ,CACtB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,MAAM,GACZ;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAO3C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAexF;AAED,oBAAoB;AACpB,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO7F;AAED,wBAAwB;AACxB,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAIpE;AAED,iCAAiC;AACjC,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAErE;AAED,yBAAyB;AACzB,wBAAgB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,SAAI,GAAG,OAAO,CAYpE;AAUD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAEpD,qBAAqB;AACrB,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAQjG;AAED,0CAA0C;AAC1C,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAE7F"}
|