外观
BattleArmyExArgs:单位 Extension
状态:Authoritative Reference v1 / Runtime Semantics Updated 2026-09-18
1. Schema
原版 Game.proto descriptor:
proto
message BattleArmyExArgs {
int32 area = 1;
repeated SArmyBuffArgs armyBuffs = 2;
int32 bynameId = 3;
int32 tunnelFromArea = 4;
int32 steelWillValue = 5;
repeated SArmyStatArgs armyStats = 6;
int32 legendArmyId = 7;
}7/7 字段均进入原版 BTL Extension loader 的 Army 委托链,因此字段存在、编号和 loader consumer coverage 均为 Confirmed。Android 1.29 -> 1.30 的 descriptor 对比确认这 7 个字段逐项不变,没有新增 EliteSkin 字段;1.30 精英皮肤正样本位于固定 Unit record +0x2F。
当前还进一步恢复了这 7 个字段在 ARM64 1.28.0 runtime Army 对象中的投影;这些 runtime 语义证据继续保留其 1.28 版本标签,不因 1.30 磁盘 corpus 升级而自动改写:
| Extension 字段 | runtime 位置 / 容器 | 当前状态 |
|---|---|---|
armyBuffs | Army+0x88..+0x98,stride 0x18 | 结构与导入链 Confirmed |
bynameId | Army+0xC8 | Confirmed projection |
legendArmyId | Army+0xCC | Confirmed projection |
tunnelFromArea | Army+0x11C | Confirmed;隧道挖掘当前 link 的来源全局 TileId |
steelWillValue | Army+0x174 | Confirmed projection,Steel Will runtime semantics recovered |
armyStats | Army+0x198..+0x1A8,stride 0x08 | runtime keyed state-map structure Confirmed |
2. Corpus
当前最新 1.30.0 extension corpus:
text
含 armyExs 的 BTL = 114
BattleArmyExArgs 总数 = 8467
area 出现 = 8467,非零 8467
bynameId 出现 = 8467,非零 8014
tunnelFromArea 出现 = 8421,非零 0
steelWillValue 出现 = 8405,非零 0
legendArmyId 出现 = 8265,非零 453
armyBuffs disk 实例 = 0
armyStats disk 实例 = 0这里必须区分两类证据:
- disk corpus value domain:当前原版 BTL 尾部有没有正实例;
- runtime semantics:原版 loader / producer / consumer 对字段实际做什么。
因此 armyBuffs、armyStats 虽然没有当前 disk-tail 正实例,其 runtime 结构已经明显超过“只知道 schema”;steelWillValue 虽然 disk corpus 仍全 0,其运行时含义已经可以闭合到“钢铁意志叠层计数”。
3. area:运行时 Army 查找键
导入链:
text
BattleArmyExArgs.area
-> global Tile object vector lookup
-> Tile exists and contains Army
-> FUN_00480CC8(Army, BattleArmyExArgs)当前 1.30 的 8467/8467 条 area 都至少匹配一条固定 Unit.TileId:
text
唯一 fixed Unit 匹配 = 8409
多条 same-Tile Unit = 58
未匹配 = 0其中仍有 10 条 area >= Header.AlignedTileSlotCount,但都有合法 fixed Unit.TileId 同值,再次确认 area 使用全局 TileId/Army identity 而不是本地 crop slot index。
因此 area 是全局地图 Tile/Army identity,而不是 Unit record index。
后续 duplicate-Unit 构造恢复已经进一步确认:普通 Unit 按序列化顺序构造,第一条成功构造并成功放置到该 Tile 的 Unit 占据初始 primary Unit 槽;后续 same-Tile Unit 在放置阶段被拒绝。被拒绝对象之后的完整生命周期仍是 Unknown,但不能再把当前 58 组 duplicate 简单描述成“初始主槽 winner 完全未知”。
3.1 与 1.30 Elite skin 的边界
BattleArmyExArgs 在 1.30 仍只有上述 7 个字段。conquest9.btl 的 H54 = 0,但固定 Unit record 中有 30 条 +0x2F = 1 的精英皮肤正样本。因此 EliteSkinIndexRaw 不属于本 message,也不能与 legendArmyId、bynameId 或其他 Army Extension 字段混同。
证据:reports/btl_reference/android_1300_baseline_and_elite_skin_index.json。
4. armyBuffs:运行时 ArmyBuff 状态
原版子消息:
proto
message SArmyBuffArgs {
int32 buffId = 1;
int32 round = 2;
int32 countryIdx = 3;
int32 value = 4;
}4.1 BTL 导入
FUN_00480CC8 对每条 SArmyBuffArgs:
text
generated copy
-> buffId definition lookup
-> lookup success: build runtime ArmyBuff entry
-> append to Army+0x88..+0x98
-> post-import refreshbuffId 不是任意标量。原版调用:
text
FUN_0030B71C(runtimeService+0x158, buffId)取得对应的 ArmyBuffSettings concrete definition。若 lookup 返回 null,该条 imported buff 被跳过,不会按原始整数硬塞进 runtime 向量。结合 SkillSettings.ArmyBuff、ArmyFeatureSettings.ArmyBuff、EventBuffSettings.ArmyBuffs[] 三条 stock 引用链均已确认指向 ArmyBuffSettings.Id,以及正常 runtime Buff API 使用同一 lookup,可以把 SArmyBuffArgs.buffId 的运行时引用域闭合为 ArmyBuffSettings.Id。Android 1.30 精确 stock runtime gate audit 进一步确认:在 armyExs -> ArmyEx importer -> ArmyBuff registry lookup -> post-import refresh -> native writer 这条链中不存在额外的 ArmyBuff Type 白名单或 BattleMode 导入白名单;只要目标 Army 已解析且 buffId 能在 ArmyBuff registry 中 lookup 成功,该记录就会 materialize。
成功条目的 runtime layout 已恢复:
| runtime entry offset | 来源 |
|---|---|
+0x00 | ArmyBuffSettings definition pointer |
+0x08 | round |
+0x0C | countryIdx |
+0x10 | value |
| stride | 0x18 bytes |
导入至少一条有效 Buff 后,loader 会调用:
text
FUN_00476E4C(Army)
FUN_0047A538(Army)执行相关 Army 派生状态重算/刷新。
这里还有一个重要的导入边界:FUN_00480CC8 不会先调用正常 runtime 的 FUN_0047A04C 做同 definition / 同 Type 合并,而是对每条 lookup 成功的 SArmyBuffArgs 直接向 Army+0x88..+0x98 append 一个 0x18 entry。当前看到的两个 post-import refresh 调用负责派生状态刷新,没有证据表明它们会把刚导入的 vector 按同 Id/同 Type 去重。因此就 loader 本身而言,BTL 中重复的合法 armyBuffs 记录会作为独立 runtime entries 进入容器;其后各具体 Type consumer 如何读取多个同 Type entry 仍需按 consumer 判断,不能套用正常 add/refresh API 的替换规则。
Android 1.30 精确函数迁移如下,且当前 accepted plaintext SO 在这些相关函数区间与 stock 1.30 逐字节一致:
| 语义 | 1.28 ARM64 | 1.30 stock ARM64 |
|---|---|---|
| ArmyBuff registry lookup | FUN_0030B71C | FUN_0030CB4C |
| Extension dispatcher | FUN_00339B58 | FUN_0033BCCC |
| ArmyEx importer | FUN_00480CC8 | FUN_00486220 |
| 正常 runtime add/merge | FUN_0047A04C | FUN_0047F36C |
| post-import refresh A | FUN_00476E4C | FUN_0047C16C |
| post-import refresh B | FUN_0047A538 | FUN_0047F858 |
| round lifecycle | FUN_0047A824 | FUN_0047FB44 |
| writer presence predicate | FUN_00480C54 | FUN_004861AC |
| ArmyEx writer | FUN_00480FCC | FUN_00486524 |
1.30 importer FUN_00486220 仍只做 registry lookup 成功检查后直接 append;顶层 FUN_0033BCCC 对 armyExs 的 dispatch 不受后续 Battle 状态 gate 包裹;FUN_0047C16C/FUN_0047F858 不移除“不支持”的 Type;FUN_004861AC/FUN_00486524 对非空 runtime Buff vector 也不做 Type/模式筛选。因此 runtime acceptance 可闭合为 all lookup-resolvable ArmyBuffSettings.Id in the audited BTL load/post-load/save path。这不等同于“每个 Type 在每种模式都有有意义的 gameplay consumer”。
4.2 与正常 runtime Buff API 的一致性
正常运行时添加/刷新 Buff 的函数 FUN_0047A04C 使用同一 definition lookup 和同一 Army+0x88 容器;其第三参数直接写入 runtime entry +0x08,即 round。同一 definition 已存在时,传入 round 更大才刷新;round 相同则再比较当前 Country 派生的 countryIdx,较大的来源值覆盖旧值。同 Type 不同 definition 时则比较 abs(definition+0x28),新 definition 优先级不低于旧 definition 才替换。FUN_0047A768 会在最终条目的 +0x10 上累加传入值。因此 Extension 的 round/countryIdx/value 都已能映射到原版正常 runtime Buff 实例,而不是保留字段。
当前还观察到 definition Type/category 的若干分支:
text
Type 0x0C -> FUN_00476E4C
Type 0x0F -> FUN_0047A538
Type 0x1D -> Map runtime +0x211 = 1
Type 0x22 -> 通过 FUN_0047D740 重算 Army+0xE0
Type 0x32 -> 特殊替换/移除路径同类 Buff 的替换逻辑还会参考 definition +0x28 的绝对值,表现为 priority-like 选择。
4.3 当前仍未闭合
当前磁盘 BTL Tail:
text
positive instances = 0因此现在可以确认 buffId 的引用 identity 是 ArmyBuffSettings.Id,并且在已审计的 Android 1.30 BTL runtime load/post-load/native-save 路径中,不存在额外的 Type/模式 acceptance 白名单。仍不能从游戏 APK 反推出 EasyTech 内部/官方 authoring 工具 UI 是否人为过滤可选 Id,也不能仅凭 importer acceptance 推导所有 Type 在所有 BattleMode 中都有实际 gameplay 效果。
此外:
round已确认进入 runtime entry+0x08,且正常 add/refresh 采用“更大 round 覆盖、相同 round 再按 countryIdx 来源优先”的规则;countryIdx已闭合为CountryContainer+0x48的 current/selected country 容器索引,不是Country.RecordId。FUN_00347184以它索引CountryContainer.children[],Route6 存档头+0x24也保存/恢复这一容器索引;- 生命周期 owner
FUN_0047A824(Army,countryIdx)只处理entry.countryIdx == 当前 tick 国家索引的 Buff:round >= 0时减 1,减到 0 后删除;round = -1 / 0xFFFFFFFF不走递减,是该 tick 路径下的不超时 sentinel; FUN_0047A824由FUN_00475050调用,而国家轮转FUN_0033C724读取同一CountryContainer+0x48并对战场对象执行这条维护链。因此 Buff duration 是按其记录的国家容器索引分区,在对应 operation-country maintenance 时递减;- 官方/内部 authoring UI 是否过滤可选 ArmyBuff Id、异常 round/countryIdx/value、crafted duplicate 的 Type-specific 最终消费以及非零 BTL save/load 行为仍未完成。
当前状态:
text
schema/runtime structure = Confirmed
runtime lifecycle = Confirmed
countryIdx identity = Confirmed CountryContainer index
runtime Type/mode gate = Confirmed: no extra whitelist in audited 1.30 load/post-load/native-save path
disk positive corpus = None
official authoring UI = Unknown
editor writer = NoGo5. bynameId
当前 1.30 corpus:
text
出现 = 8467
非零 = 8014
不同值 = 277
范围 = 0..11302原版 descriptor 名 bynameId 已确认。现有资源和 UI consumer 证据强烈支持:
text
bynameId > 0
-> unit_name_<id>
-> 覆盖具体地图单位的完整显示名称UI-facing 名称覆盖字段本身已有直接 consumer/fallback 证据;bynameId 到该最终 UI 字段的精确标量 setter 仍有少量长尾,因此当前业务语义保持 StrongEvidence / Partial。
bynameId = 0 表示不提供该名称覆盖的方向有 corpus/资源支持,但 writer 仍未开放。
6. tunnelFromArea
当前 1.30 corpus:
text
字段显式出现 = 8421
值为 0 = 8421
字段缺省 = 46
非零实例 = 0原版 FUN_00480CC8 会把它直接复制到:
text
Army+0x11C在 Extension 导入阶段不会立即按 Tile lookup,但 fresh native 恢复已经找到它真正的 runtime owner:
text
FUN_004B8370 case 0x59
-> FUN_00482AB0(Army, sourceArea, targetArea, ...)ArmyFeatureSettings.Type 89 / 0x59 已由现有技能 authority 闭合为 “隧道挖掘”。
FUN_00482AB0 成功路径:
text
FUN_00489A00(...) 验证 source/target
-> Map lookup(sourceArea)
-> Map lookup(targetArea)
-> source Tile+0x68 = target Tile
-> target Tile+0x68 = source Tile
-> 刷新两个 Tile
-> Army+0x11C = sourceArea因此 tunnelFromArea 可以正式解释为:
当前 Army 的“隧道挖掘”link 所保存的来源/起点全局 TileId。
已有旧 link 时,FUN_00482AB0 会先读取 Army+0x11C,按该全局 TileId 找回起点 Tile,清掉起点和对端双方的 Tile+0x68 互指,调用旧 area cleanup,再把 Army+0x11C 清零,之后才允许建立新 link。
当前状态:
text
schema/runtime storage = Confirmed
nonzero producer/consumer = Confirmed
reference domain = global Map TileId
feature binding = ArmyFeature Type 0x59 隧道挖掘
disk nonzero positive sample = None
writer = NoGo还不能把它和 BattleCountryExArgs.tunnels/TunnelArgs 自动合并:两者是否属于同一上层 tunnel 系统仍需要单独证据。FUN_00489A00 内部的全部非法 source/target 判定也尚未逐项命名。
跨架构时尤其要避免一个陷阱:Route5 旧证据中的 x86 Unit+0x11C 对应 ARM64 的另一个字段 Unit+0x178,与这里的 ARM64 Army+0x11C 不是同一个字段。
7. steelWillValue:钢铁意志叠层计数
这一字段的 runtime 语义已经从旧的 UnknownNoPositiveCorpus 明显推进。
7.1 BTL 与 runtime 字段
BTL loader:
text
BattleArmyExArgs.steelWillValue
-> Army+0x174当前历史 disk corpus:
text
字段出现 = 8405
字段缺省 = 62
非零 = 0所以“原版静态 BTL 正值域”仍无正样本;但这不再等于“字段用途未知”。
7.2 原版 producer
原版战斗结果应用 FUN_004D0ED4 的已恢复分支会在满足相应单位/技能 gate 时:
text
SkillSettings.Type == 0x89
-> Army+0x174 += 1因此 steelWillValue 是可累积的 runtime counter。
7.3 原版 damage consumer
正常伤害计算 FUN_004C5A10 的 defender-side SkillSettings 分支读取同一 Army+0x174:
text
stack = min(steelWillValue, 3)
damage factor
= (100 - skill.field30 - 10 * stack) / 100即每层额外增加 10 个百分点的减伤贡献,公式只把叠层计数有效截到最多 3 层。
7.4 配置与中文资源闭环
原版中文字符串:
ini
skill_name_137=钢铁意志
skill_info_137=指挥步兵时受到伤害降低%d%,并且受到敌军攻击后额外增加10%减伤,持续1回合,最多叠加3次而:
text
SkillSettings.Type 137 == 0x891.29 官方 SkillSettings.json 中 Type 137 的 5 级配置:
| Level | Id | SkillEffect |
|---|---|---|
| 1 | 3371 | 4 |
| 2 | 3372 | 8 |
| 3 | 3373 | 12 |
| 4 | 3374 | 16 |
| 5 | 3375 | 20 |
这与 native 的 Type 0x89 damage branch 和 Army+0x174 叠层 consumer 构成一致证据链。
7.5 Namespace 警告
0x89 不是全游戏统一枚举:
text
SkillSettings.Type 0x89 = 钢铁意志
ArmyFeatureSettings.Type 0x89 = 火控雷达两者是不同 namespace。这里的 steelWillValue 证据来自 entity+0x78 的 SkillSettings container,必须解释为前者,不能和火控雷达合并。
7.6 还剩什么
当前可以发布:
text
runtime meaning = Steel Will stack counter
per-stack contribution = +10 percentage points reduction
effective stack cap = 3
UI-described lifetime = 1 roundfresh ARM64 1.28.0 native 恢复已经定位到精确 reset owner:
text
FUN_0048157C
0x00481CF0 STR WZR,[X19,#0x174]该写零发生在统一单位回合维护中,位于主动槽持续/CD +0x110/+0x114/+0x118 递减之后、Army+0x198 keyed-state 重建之前。因此“持续 1 回合”已经由中文资源与 native 生命周期双重闭环。
仍未闭合的只剩:
- 原版 disk-tail 没有非零
steelWillValue正实例; - 非零 BTL authoring/save-load 的行为验收与异常值合法域尚未建立。
当前状态:RuntimeSemanticsConfirmed / NoPositiveBtlCorpus / Writer NoGo。
8. armyStats:runtime keyed state map
原版子消息:
proto
message SArmyStatArgs {
int32 statType = 1;
int32 value = 2;
}旧文档把它笼统称作“runtime stat collection”,容易让人误解成攻击、防御等基础属性。现有证据已经能更精确地说:
text
SArmyStatArgs
-> generated copy
-> append 8-byte (statType, value) pair
-> Army+0x198..+0x1A8这个容器在原版其他 runtime 路径中存在直接 helper。Android 1.30 exact-stock 复核把 1.28 helper 逐一迁移为:
text
1.28 FUN_00482438(Unit,key,value) -> 1.30 FUN_00487B80 // upsert
1.28 FUN_0047D704(Unit,key) -> 1.30 FUN_00482AD4 // query
1.28 FUN_00482BC0(Unit,key,delta) -> 1.30 FUN_00488308 // add/increment跨架构恢复也确认:
text
ARM64 Unit+0x198/+0x1A0/+0x1A8
<-> x86 Unit+0x138/+0x13C/+0x140因此 armyStats 更准确的当前语义是:
一组由
statType作为 key、value作为 value 的单位 runtime 状态/统计 pair map,供特性、反应、跨回合派生状态与一次性锁存逻辑查询和更新;它不是攻击/防御/生命等基础面板属性表。
Android 1.30 对三个 helper 的全 caller xref 已直接观察到 key:
text
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11同时 exact-stock 新回合维护 FUN_00486AD4 重新确认派生 key 0x10。因此当前审计到的 runtime namespace 至少覆盖 1..17;这里的“覆盖”只表示这些 key 已在原版路径中出现,不代表每个 key 都已经获得稳定的官方自然名称。
当前可按证据强度标注为:
| statType | 当前最安全语义 | 关键证据 | 状态 |
|---|---|---|---|
1 | 本回合击杀/消灭计数;ArmyFeature Type 102“贫铀弹药”Lv4 与 Type 149“动态校准”在击杀反应尾部递增,Type149 按阈值消费后赋予射程 Buff 1611 | FUN_004DF78C + stock Feature 文案 | Confirmed scoped semantic |
2 | ArmyFeature Type 60“布雷”的本轮执行计数 | query→+1→upsert,与 Feature 阈值比较 | Confirmed |
3 | ArmyFeature Type 84“扫射压制”的本轮成功执行计数 | Fight 成功后递增 | Confirmed |
4 | Skill Type 157 “Wipe Out The North” 的本回合再次行动概率累加值;每次击杀 +20,随后直接与 RNG(100) 比较 | FUN_004DF720/FUN_004D7E18 + stock Skill 文案 | Confirmed |
5 | ArmyFeature Type 145“蜂群部署”的本轮执行计数 | query→+1→upsert,与 Feature 阈值比较 | Confirmed |
6 | 待下回合生效的通用机动力正向 payload;新回合被提取并最多按 20 转移到 key 7 | FUN_00486AD4 | Confirmed lifecycle / producer family Partial |
7 | 当前回合有效机动力加成;由 key6 转入并可叠加 Legend/上下文贡献,移动力计算直接把其正值加到最终 movement | FUN_00486AD4 -> FUN_00482B10 | Confirmed |
8 | Leclerc Division(LegendArmy Id 5)Tactic Type28 / Id10502 的受益单位士气提升锁;该 tactic 的目标筛选通过后,仅在 recipient 的 key8 为 0 时执行 Morale +1,随后写 key8=1,因此同一 recipient 在本轮不会重复吃这次士气提升 | FUN_0047E5F4 -> FUN_0039B110(...,0x1C) -> FUN_0039B4B4 -> FUN_004D7E18 + 1.30 LegendArmy/LegendTactics 数据 | Confirmed scoped semantic |
9 | Skill Type173 Marshall Plan 本回合近距离击杀奖励触发次数;3 格内敌军被消灭时,key9<3 才递增并发放 40 Industry + 4 Technology,上限正好 3 次 | FUN_003722A8 + Skill173 官方文案 | Confirmed |
10 | Lightning Division(LegendArmy Id 6)Tactic Type32 / Id10602 的 Effect 投影槽;FUN_004DFBE8 对 tactic 选中的目标写入 key10 = Effect,stock 1.30 该 row 为 Effect=20 / Distance=3。但统一 query helper 全 xref 与全程序直接 stat-map consumer 扫描均未发现 key10 gameplay reader,因此不得把 20 推成 +20% 伤害等效果 | FUN_004DFBE8 + LegendTactics Id10602 + exact-stock negative-consumer audit | Confirmed producer / No gameplay consumer observed |
0x0B | ArmyFeature Type 153 “Aerial Countermeasures” 的下回合机动力降低 payload;新回合投影到 ArmyBuff 1641 “Fuel Consumption: Reduced Mobility” 的 runtime value | movement path + FUN_00486AD4 + Buff 1641 | Confirmed |
0x0C | ArmyFeature Type153 Aerial Countermeasures 的移动/恢复生命周期计数;符合条件的 ArmySettings.Type=13 单位在移动完成时写入 key0x0B=SecondEffect 并将 key0x0C +1;恢复路径仅在 key0x0C==1 时同时清零 key0x0B/key0x0C,对应官方文案中的移动后 Fuel Consumption 与休整恢复链 | FUN_0047DE90 -> FUN_0047604C / FUN_00371558 + Type153 官方文案 | Confirmed lifecycle semantic |
0x0D | ArmyBuff Type 73 “Disorganized” 的本回合已触发/禁止再次施加锁;触发并移除效果后置 1,后续传播先检查该 key | FUN_004D7E18 + Buff 1731 文案 | Confirmed |
0x0E | Unit definition id 0x64 专属伤害倍率 payload;正值按 (100+value)/100 进入伤害计算 | FUN_004CAF34(1.28 对应 FUN_004C41E0) | Confirmed scoped consumer |
0x0F | Skill Type 187 “Ace Tank IV” 的待下回合机动力加成;击杀分支写入 3 | FUN_004D7E18 + Skill187 文案 | Confirmed |
0x10 | key0x0F 在新回合转移出的当前回合机动力加成;最终 movement 直接加其正值 | FUN_00486AD4 -> FUN_00482B10 | Confirmed |
0x11 | ArmyBuff Type 81 “Lightning General” 的本回合再次攻击已消费锁;首次满足条件时置 1,阻止同一 once-per-turn 再攻击再次触发 | FUN_004D7E18 + Buff 1811 文案 | Confirmed |
因此必须避免把:
text
statType = X
value = Y直接解释成“攻击力/防御力/生命值 +Y”。同一 pair map 同时承载计数、概率累计、机动力跨回合传递、Buff payload、Legend tactic recipient latch 和 once-per-turn latch。
这里还出现了一个值得明确保留的 NoConsumerObserved 边界:statType 10 的 producer、owner 和 payload 来源已经闭合,但 Android 1.30 exact-stock 中,FUN_00482AD4 的全部 direct xref 没有一次以 key10 查询;对全程序同时直接触碰 Army+0x198/+0x1A0 形状的函数做 decompiler 反扫,也没有得到可归属到 Unit stat-map 的 key10 gameplay consumer。原版 writer 仍会序列化这条 pair,因此应记录为“可写/可保存的 runtime state,当前 stock gameplay consumer 未观察到”,而不是凭 Effect=20 推断效果。
1.30 exact-stock 新回合维护 FUN_00486AD4 还把生命周期重新闭合了一遍:
text
read old key6 / key0x0F / key0x0B
-> Army+0x1A0 = Army+0x198 // clear logical map
-> min(key6,20) -> key7
-> key0x0F -> key0x10
-> key0x0B -> ArmyBuff Id 0x669 / 1641 runtime value
-> Legend/context may add more key7随后 FUN_00482B10 的最终 movement 计算直接读取 key7 和 key0x10 的正值并相加;ArmyBuff 1641/Type64 则在移动力路径中作为 “Fuel Consumption” 机动力降低量被扣除。这使 6→7、0x0B→1641、0x0F→0x10 三组跨回合语义在 1.30 上也闭合,而不再只依赖 1.28 证据。
当前磁盘 BTL Tail 的 field 6 正实例仍为 0,所以 BTL authoring value domain、异常 key/value 行为、非零 save/load acceptance 与官方 authoring UI 是否允许全部 key 继续保持 Partial/Unknown。就 stock runtime 语义本身而言,8/9/0x0C 已从此前 Partial 提升到 scoped/生命周期 Confirmed;10 则收口为 producer/owner Confirmed + gameplay consumer NoConsumerObserved。
9. legendArmyId
当前:
text
字段出现 = 8265
字段缺省 = 202
非零 = 453
非零范围 = 1..10分布:
| ID | 数量 |
|---|---|
| 1 | 53 |
| 2 | 47 |
| 3 | 40 |
| 4 | 43 |
| 5 | 52 |
| 6 | 41 |
| 7 | 44 |
| 8 | 45 |
| 9 | 41 |
| 10 | 47 |
原版 descriptor 名、Legend 语料和 legend_army_* 资源共同支持它表示 Legend 编制/协同身份,当前为 StrongEvidence。
但精确运行时协同消费者和全部判定入口尚未闭合,所以不把它扩大为完整玩法规则。
10. bynameId 与 legendArmyId 绝不能合并
两者在原版 schema 是独立字段,corpus 也存在独立组合。
例如已有记录满足:
text
bynameId = 0
legendArmyId = 10同时资源存在:
text
legend_army_10 = 第51高地师这说明:
bynameId可以负责地图单位显示名称覆盖;legendArmyId可以在不提供bynameId的情况下标记 Legend 编制身份;- “显示名相同”不等于“同一字段”。
工具若把两者合并,会破坏原版数据语义。
11. area 与当前 local TileSlotCount
当前有 10 条 armyEx.area:
text
area >= Header.AlignedTileSlotCount但它们全部仍能命中固定 Unit 的全局 TileId。
因此:
text
local TileSlotCount overflow
!= invalid armyEx.area尤其 external-map/cropped BTL 必须按全局 Tile 地址空间判断。
12. Army 是否携带 Extension 状态的原版判定
原版 FUN_00480C54 依次检查:
text
Army+0x88 buff vector 非空
OR bynameId > 0
OR legendArmyId > 0
OR tunnelFromArea > 0
OR steelWillValue > 0
OR Army+0x198 stat/state vector 非空任意条件成立即返回 true。
这证明原版把这 6 类数据视作同一组“Army extra/runtime state”的存在性条件。
fresh Ghidra 已恢复其唯一真实函数 caller FUN_003332C4(调用点 0x00333480 / 0x00333544 / 0x00333754)。当该 gate 返回 true 时,原版 writer 会分配新的 BattleArmyExArgs protobuf object、写入 area,再调用 FUN_00480FCC 将这六类 runtime extra state 投影到 protobuf。因此它已经可以正式定性为 BattleArmyExArgs inclusion gate。
13. Writer 边界
本轮是语义恢复,不自动打开 Extension writer。
BtlExtensionWriterDefinitionGate/v1 已完成,BtlExtensionWireTransformFoundation/v1 也已通过 synthetic + 1.28 corpus;当前是low-level foundation implemented / production writer NoGo。当前边界:
- 原版 runtime→fresh protobuf→
SerializeToArray的变长重建机制已确认;Reconstruction 已实现更保守的 lossless local wire-transform foundation; - same-version 原版 writer 并不保留输入 field order/raw bytes;duplicate singular/malformed input 与跨版本 future unknown-field compatibility 仍未闭合;
armyBuffs/armyStats没有原版 disk-tail 正实例;Buff runtime 生命周期与countryIdxidentity 已闭合,但完整 ArmyBuff Id/Type authoring domain 和 Stats 各 key 自然语义仍未知/Partial;tunnelFromArearuntime producer/consumer、全局 TileId 域和“隧道挖掘”绑定已闭合,但没有非零 BTL disk-tail 正样本,FUN_00489A00全部非法目标规则及与TunnelArgs的关系仍未闭合;steelWillValueruntime producer/consumer/reset owner 已闭合,但非零 BTL 正样本与 authoring/save-load acceptance 仍缺;- 第一 writer gate 只允许
bynameId与legendArmyId:bynameId非零必须由unit_name_<id>resolver 验证;legendArmyId暂限原版 corpus 域0..10;area只作为 resolved primary Army global TileId identity,不可直接编辑; - 自动引用修复仍为 NoGo。
因此:
text
read/research authority = open
runtime semantic documentation = open for recovered fields
scoped writer foundation = Implemented+CorpusQualified(bynameId, legendArmyId only)
production/product writer = NoGo pending package + product admission
arbitrary Extension writer = NoGo14. 主要证据
text
reports/btl_v3_tail_legend_recovery.md
reports/btl_v3_army_extensions_matrix.csv
reports/btl_reference/army_extension_runtime_semantics.json
reports/btl_editor/round40/delegated_consumers_decompile.txt
reports/rendering/static_scene_viewer/unit_count_loader_tail2.txt
reports/raw_recovery/attack_system_rounds01_14/attack_system_full_recovery/native/0047a04c_FUN_0047a04c/decompile.c
reports/raw_recovery/attack_system_rounds01_14/attack_system_full_recovery/native/0047a768_FUN_0047a768/decompile.c
reports/raw_recovery/attack_system_rounds01_14/attack_system_full_recovery/native/004d0ed4_FUN_004d0ed4/decompile.c
reports/raw_recovery/attack_system_rounds01_14/attack_system_full_recovery/native/004c5a10_FUN_004c5a10/decompile.c
reports/raw_recovery/damage_rounds06_08/damage_formula_recovery_rounds06_08/round07_defender_skill_matrix.csv
reports/route5_fixed_seed_fight_recovery/unit_type_application_cross_arch_field_mapping.csv
reports/route5_fixed_seed_fight_recovery/unit_type_specific_application_write_matrix.csv
reports/action_ai_country_current_index.md
reports/route6_save_recovery/summary_header_matrix.csv
reports/ai_unit_skill_usage_complete.md
reconstruction/docs/btl_editor_round45_duplicate_unit_tile_construction_order_recovery_report.md本轮已找回迁移到 _legacy/pre-split-root-20260726/tools/ghidra_12.1.2_PUBLIC 的 Ghidra,并在原 Extension project 的一次性临时副本上以 read-only / -noanalysis 模式重新执行 caller/XRef、反编译和指令区间审计;原始 Ghidra project 未被修改。fresh 结果使用的 ARM64 1.28.0 SO SHA-256 为 456e7dceeb3f310cdf6409613b0f166170e16fb7f086f7e806d4d0eaf463cf15。