gameabc.min.js 逆向结果固化到 fixtures/spirit-semantics.json:
SpiritType 4 = cc.EditBox
逆向证据: phoneInfo Layer00029.xml 含 type-4 spirit (Spirit3246 FrameText=12,
Spirit3247 FrameText=1 等); YouleNexus Group106_PhoneVerify.prefab (映射到
Layer00029) 含 cc.EditBox 节点, 与 type-4 FrameText 输入框语义一致.
独有属性: FrameText (默认/显示文本) + ImgResID (背景图) + CanClick=1.
SpiritType 5 = cc.Sprite (type=SLICED)
逆向证据 1 (smoking gun): Layer00002.xml Spirit159 (370x170, 边缘 100/80/100/80)
→ YouleNexus Login_Layer.prefab 唯一 cc.Sprite._type=1 (SLICED), _contentSize
严格等于 370x170. 1:1 同一个节点.
逆向证据 2: Layer00609.xml '设置背景' (100/134/100/135) → YouleNexus
Layer609_Setting.prefab cc.Sprite._type=1.
独有属性: LeftEdge/TopEdge/RightEdge/BottomEdge (4 边缘宽度) = 9-slice scaling.
SizeCalcMode 0/2 公式 (55 Layer 全量解析: 仅观察到这两种值):
0 = 直接读 Width/Height (Task 3 已实现).
2 = parent_size (lookup by SizeParentID) * SizeRelaWidth/Height / 10000. 全部采样
WidthCalcMode=1, SizeRelaWidth=10000 (即按 100% 比例继承父 spirit 尺寸).
测试 spirit-type-reverse.test.mjs 3 个 case 全 PASS:
- SpiritType 4 component != unknown
- SpiritType 5 component != unknown
- spirit_types 含 5 项且全部 component != unknown
64 lines
5.4 KiB
JSON
64 lines
5.4 KiB
JSON
{
|
||
"_comment": "gameabc.min.js 逆向结果(PoC 阶段 1, 2026-09-02). 数据源权威: 下游消费方不得猜测/修补/兜底, unknown → throw.",
|
||
"spirit_types": {
|
||
"0": {
|
||
"component": "cc.Sprite",
|
||
"note": "普通精灵; _type=0 (SIMPLE), _sizeMode=0",
|
||
"evidence": "Game_Surface_3 save/Layer*.xml 中 type=0 的 spirit 均含 ImgResID (sprite 图集索引) + BelongGroupID, 无独有属性; YouleNexus Login_Layer.prefab 的普通 sprite (_type=0, _sizeMode=0) 与 type=0 spirit 一一对应"
|
||
},
|
||
"1": {
|
||
"component": "cc.Label",
|
||
"note": "文本标签; 含 Text/FontSize/AlignHorz/FontColor/BackColor 等独有属性",
|
||
"evidence": "Game_Surface_3 中 type=1 spirit 独有 Text/FontSize/FontBold/FontColor/BackColor/AlignHorz/AlignVert/AutoSize/WordWrap/LineSpace/TextStyle 这 11 个属性; 全量 XML 解析 38/55 Layer 含 type=1; 与 YouleNexus Login_Layer 中 cc.Label 节点字段对齐"
|
||
},
|
||
"3": {
|
||
"component": "cc.ProgressBar",
|
||
"note": "进度条; 含 Frame 属性 (frame=进度 0-1 范围 或 frame id)",
|
||
"evidence": "Game_Surface_3 中 type=3 spirit 独有 Frame 属性 (例如 Layer00002 'Spirit3254' Frame=进度条); 45/55 Layer 含 type=3; 与 YouleNexus Login_Layer.prefab 中 '进度条底' cc.ProgressBar 节点对应"
|
||
},
|
||
"4": {
|
||
"component": "cc.EditBox",
|
||
"note": "文本输入框; 独有属性 FrameText (默认/显示文本); CanClick=1; 含 ImgResID (背景图); CanDragSize=0",
|
||
"evidence": "Game_Surface_3 中 type=4 spirit 独有 FrameText 属性 (e.g. Layer00029 phoneInfo Spirit3246 FrameText='12' Spirit3247 FrameText='1' / Layer00014 Pay_Layer Spirit446 FrameText='12'); 15/55 Layer 含 type=4; YouleNexus Group106_PhoneVerify.prefab (映射到 Layer00029.xml phoneInfo) 含 1 个 cc.EditBox 节点, 与 type=4 的 FrameText 输入框语义一致 (phoneInfo 是手机号/验证码输入场景)"
|
||
},
|
||
"5": {
|
||
"component": "cc.Sprite",
|
||
"note": "9-slice 缩放精灵; 独有属性 LeftEdge/TopEdge/RightEdge/BottomEdge (4 个边缘宽度); CanClick=1, CanDragSize=1; Cocos 映射: cc.Sprite._type = 1 (SLICED)",
|
||
"evidence": "Game_Surface_3 中 type=5 spirit 独有 LeftEdge/TopEdge/RightEdge/BottomEdge 属性 (Layer00002 Spirit159 (100/80/100/80, 隐式) / Layer00006 Spirit287 (70/1/70/1) / Layer00017 '个人信息背景' (70/70/70/70) / Layer00609 '设置背景' (100/134/100/135) / Layer00612 Tips_Layer 5 个 type=5 边缘容器); 39/55 Layer 含 type=5; YouleNexus Login_Layer.prefab 中唯一一个 cc.Sprite._type=1 (SLICED) 节点的 _contentSize 是 370×170, 严格匹配 Layer00002 Spirit159 Width=370 Height=170 — 同一个节点, 1:1 对应; YouleNexus Layer609_Setting.prefab 中 cc.Sprite._type=1 对应 Layer00609 '设置背景' (9-slice 边缘切片背景); 4 个边缘宽度正是 9-slice 缩放 slice 区域定义"
|
||
}
|
||
},
|
||
"size_calc_mode": {
|
||
"_comment": "55 个 Layer 全量解析: 只观察到 SizeCalcMode = 0 和 2 两种值; 0 直接读 Width/Height, 2 通过 SizeParentID 引用父 Spirit 尺寸再乘以 SizeRelaWidth/SizeRelaHeight (万分比) 比率; WidthCalcMode/HeightCalcMode=1 表示启用相对宽度/高度 (SizeRelaXxx 字段), =0 表示使用 Width/Height 自身.",
|
||
"0": {
|
||
"formula": "_contentSize = { width: parseFloat(Spirit.Width), height: parseFloat(Spirit.Height) }",
|
||
"rationale": "直接读精灵自身 Width/Height 字段作为 _contentSize; gameabc.min.js 中 SizeCalcMode=0 是最简路径 (背景图 Layer00001/00002 全屏 1280×720 即此路径)"
|
||
},
|
||
"2": {
|
||
"formula": "parent_size = lookup_Spirit(Spirit.SizeParentID)._contentSize; _contentSize = { width: parent_size.width * Spirit.SizeRelaWidth / 10000, height: parent_size.height * Spirit.SizeRelaHeight / 10000 }; 当 WidthCalcMode=0 时回退到 Spirit.Width (parseFloat), 当 WidthCalcMode=1 时用 SizeRelaWidth 公式; HeightCalcMode 同理",
|
||
"rationale": "Layer00001 '友乐背景' (SizeCalcMode=2, SizeParentID=21, SizeRelaWidth=10000) / Layer00002 '微信登录背景' (SizeCalcMode=2, SizeParentID=20, SizeRelaWidth=10000) 全量采样: 全部 WidthCalcMode=1, HeightCalcMode=1, SizeRelaWidth=10000, SizeRelaHeight=10000; 即按 100% 比例继承父 spirit 尺寸; 这种 pattern 用于 '充满父容器' 的背景 (1280×720 全屏背景), SizeParentID 指向另一个 1280×720 的 spirit"
|
||
}
|
||
},
|
||
"width_calc_mode": {
|
||
"0": "use Spirit.Width directly (直接读 Width)",
|
||
"1": "use parent_size.width * Spirit.SizeRelaWidth / 10000 (按父尺寸比例计算)"
|
||
},
|
||
"height_calc_mode": {
|
||
"0": "use Spirit.Height directly (直接读 Height)",
|
||
"1": "use parent_size.height * Spirit.SizeRelaHeight / 10000 (按父尺寸比例计算)"
|
||
},
|
||
"coord_self_pos": {
|
||
"_note": "原 X, Y 是精灵左上角绝对屏幕坐标 (基于 1280x720); Cocos 用中心锚点",
|
||
"cocos_lpos_x": "original_X + original_W/2 - 640",
|
||
"cocos_lpos_y": "360 - (original_Y + original_H/2)",
|
||
"coord_self_pos_mode": {
|
||
"_note": "CoordSelfPos=0 在 55 个 Layer 全量中占绝对多数; CoordSelfPos > 0 的精灵 (例如相对父节点的偏移) 暂未观察到案例, 公式留 follow-up",
|
||
"0": "use X/Y 作为相对父节点偏移 (直接读, 见 coord_self_pos 公式)"
|
||
}
|
||
},
|
||
"anchor_point": {
|
||
"cocos_default": { "x": 0.5, "y": 0.5 },
|
||
"original_default": "左上角 (0, 0)",
|
||
"evidence": "Login_Layer '渠道logo' 节点 (原 734,143,59,148) → Cocos _lpos (123.5, 143, 0) 1:1 匹配, 锚点 (0.5, 0.5) 验证通过"
|
||
}
|
||
}
|