feat(icon): 轨迹时序指纹修复 —— 1ms 图标 4/8→7/8=0,errorCode 语义与轨迹对拍收口
- 真因:dt 分布与真机严重不符(旧 50% 落 0~16ms,真机 79% 落 13~20ms)+ 点数 随机 57~65(真机 6/6 恒 61)。按 6 条真机轨迹重拟合 _sample_dt,新增 _fit_points 钉死 61 点(补齐用 [0,0,dt] 驻留点,不能复制位移点——净位移会被放大)。 - A/B:1ms 图标 0×4→0×7(前 7 连 0);残余偶发 12 为连发频控形态,低频使用即可。 - errorCode 语义标定:50=答案被否(容差实测 25~40px,我们点偏仅 2~3px), 12=包级拒答;判点位只看 50/0。 - 顺带修 build_plain:轨迹落末槽(k=37)时 chunk2 恒补前导逗号 → 非法 JSON。 - 新增 scripts/verify/send_campaign.py(批量真发+落图+统计)与 judge_icons.py (提示目标 vs 点击点并排判读);账本 #64~#74 + 专文 ICON2408.md。 复验:selftest 全绿;判读图 24/24 点位全对;mliev 图标 9/10=0。
This commit is contained in:
@@ -219,36 +219,52 @@ def split_collect_segments(plain: str) -> Tuple[str, str, str, str]:
|
||||
sd = '"sd":{...}}'
|
||||
前三段各用空格 pad 到 24 字节整数倍,sd 不 pad。
|
||||
|
||||
⚠️ 轨迹落在数组两端时,相邻那段的逗号按 **JSON.stringify 语义** 消失:
|
||||
k == 0 -> chunk1 = '{"cd":[' (真机成功包实测)
|
||||
k == n-1 -> chunk2 = '],' (2026-10-12 实测构建)
|
||||
|
||||
做法:先按模 24 算术列出**候选** pad,再对候选三元组做结构校验
|
||||
(首段以 '{"cd":[' 开头、末段以 '"sd"' 开头、去掉 pad 后是合法 JSON)。
|
||||
校验这一步不可省 —— 字符串里的普通空格同样会命中候选判据。
|
||||
"""
|
||||
data = plain.encode("latin1")
|
||||
cands = _candidate_pads(data)
|
||||
n_struct = n_json = 0
|
||||
for (a1, b1), (a2, b2), (a3, b3) in itertools.combinations(cands, 3):
|
||||
if not (b1 <= a2 and b2 <= a3):
|
||||
continue
|
||||
segs = [data[0:a1], data[b1:a2], data[b2:a3], data[b3:]]
|
||||
# 段边界一定落在 JSON 记号边界上(3 份真机样本一致):
|
||||
# chunk1 以 ',' 结尾;traj 是**一个**完整 JSON 值;chunk2 以 ',' 开头、以 '],' 结尾
|
||||
if not (segs[0].startswith(b'{"cd":[') and segs[0].endswith(b',')
|
||||
and segs[2].startswith(b',') and segs[2].endswith(b'],')
|
||||
# 段边界一定落在 JSON 记号边界上:
|
||||
# chunk1 = '{"cd":[' + 若干槽 + [',' 或 ''] —— 轨迹正好是第 0 槽时**没有那个逗号**
|
||||
# (真机成功包实测:chunk1 就是 '{"cd":[',随后 17 个空格 pad)
|
||||
# traj = **一个**完整 JSON 值
|
||||
# chunk2 = [',' + 若干槽] + '],' —— 轨迹正好是最后 1 槽时**没有那个前导逗号**
|
||||
if not (segs[0].startswith(b'{"cd":[')
|
||||
and (segs[0].endswith(b',') or segs[0] == b'{"cd":[')
|
||||
and segs[2].endswith(b'],')
|
||||
and (segs[2].startswith(b',') or segs[2] == b'],')
|
||||
and segs[3].startswith(b'"sd"')):
|
||||
n_struct += 1
|
||||
continue
|
||||
try:
|
||||
json.loads(segs[1].decode("latin1")) # traj 必须是独立可解析的 JSON 值
|
||||
except Exception: # noqa: BLE001
|
||||
n_struct += 1
|
||||
continue
|
||||
try:
|
||||
obj = json.loads(b"".join(segs).decode("latin1"))
|
||||
except Exception: # noqa: BLE001
|
||||
n_json += 1
|
||||
continue
|
||||
if "cd" not in obj or "sd" not in obj:
|
||||
n_json += 1
|
||||
continue
|
||||
return (segs[0].decode("latin1"), segs[1].decode("latin1"),
|
||||
segs[2].decode("latin1"), segs[3].decode("latin1"))
|
||||
raise ValueError(
|
||||
f"未能切出「四段加密」结构(候选 pad {len(cands)} 个);这份明文可能不是 4 段格式")
|
||||
f"未能切出「四段加密」结构(候选 pad {len(cands)} 个,"
|
||||
f"边界结构不符 {n_struct} 组,拼回 JSON 校验失败 {n_json} 组);"
|
||||
f"这份明文可能不是 4 段格式,或生成侧拼出了非法 JSON")
|
||||
|
||||
|
||||
def xtea_decrypt_bytes(data: bytes, key: List[int]) -> bytes:
|
||||
|
||||
Reference in New Issue
Block a user