Skip to content

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 位置 / 容器当前状态
armyBuffsArmy+0x88..+0x98,stride 0x18结构与导入链 Confirmed
bynameIdArmy+0xC8Confirmed projection
legendArmyIdArmy+0xCCConfirmed projection
tunnelFromAreaArmy+0x11CConfirmed;隧道挖掘当前 link 的来源全局 TileId
steelWillValueArmy+0x174Confirmed projection,Steel Will runtime semantics recovered
armyStatsArmy+0x198..+0x1A8,stride 0x08runtime 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 refresh

buffId 不是任意标量。原版调用:

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来源
+0x00ArmyBuffSettings definition pointer
+0x08round
+0x0CcountryIdx
+0x10value
stride0x18 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 ARM641.30 stock ARM64
ArmyBuff registry lookupFUN_0030B71CFUN_0030CB4C
Extension dispatcherFUN_00339B58FUN_0033BCCC
ArmyEx importerFUN_00480CC8FUN_00486220
正常 runtime add/mergeFUN_0047A04CFUN_0047F36C
post-import refresh AFUN_00476E4CFUN_0047C16C
post-import refresh BFUN_0047A538FUN_0047F858
round lifecycleFUN_0047A824FUN_0047FB44
writer presence predicateFUN_00480C54FUN_004861AC
ArmyEx writerFUN_00480FCCFUN_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            = NoGo

5. 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 == 0x89

1.29 官方 SkillSettings.json 中 Type 137 的 5 级配置:

LevelIdSkillEffect
133714
233728
3337312
4337416
5337520

这与 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 round

fresh 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 1611FUN_004DF78C + stock Feature 文案Confirmed scoped semantic
2ArmyFeature Type 60“布雷”的本轮执行计数query→+1→upsert,与 Feature 阈值比较Confirmed
3ArmyFeature Type 84“扫射压制”的本轮成功执行计数Fight 成功后递增Confirmed
4Skill Type 157 “Wipe Out The North” 的本回合再次行动概率累加值;每次击杀 +20,随后直接与 RNG(100) 比较FUN_004DF720/FUN_004D7E18 + stock Skill 文案Confirmed
5ArmyFeature Type 145“蜂群部署”的本轮执行计数query→+1→upsert,与 Feature 阈值比较Confirmed
6待下回合生效的通用机动力正向 payload;新回合被提取并最多按 20 转移到 key 7FUN_00486AD4Confirmed lifecycle / producer family Partial
7当前回合有效机动力加成;由 key6 转入并可叠加 Legend/上下文贡献,移动力计算直接把其正值加到最终 movementFUN_00486AD4 -> FUN_00482B10Confirmed
8Leclerc 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
9Skill Type173 Marshall Plan 本回合近距离击杀奖励触发次数;3 格内敌军被消灭时,key9<3 才递增并发放 40 Industry + 4 Technology,上限正好 3 次FUN_003722A8 + Skill173 官方文案Confirmed
10Lightning 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 auditConfirmed producer / No gameplay consumer observed
0x0BArmyFeature Type 153 “Aerial Countermeasures” 的下回合机动力降低 payload;新回合投影到 ArmyBuff 1641 “Fuel Consumption: Reduced Mobility” 的 runtime valuemovement path + FUN_00486AD4 + Buff 1641Confirmed
0x0CArmyFeature 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
0x0DArmyBuff Type 73 “Disorganized” 的本回合已触发/禁止再次施加锁;触发并移除效果后置 1,后续传播先检查该 keyFUN_004D7E18 + Buff 1731 文案Confirmed
0x0EUnit definition id 0x64 专属伤害倍率 payload;正值按 (100+value)/100 进入伤害计算FUN_004CAF34(1.28 对应 FUN_004C41E0)Confirmed scoped consumer
0x0FSkill Type 187 “Ace Tank IV” 的待下回合机动力加成;击杀分支写入 3FUN_004D7E18 + Skill187 文案Confirmed
0x10key0x0F 在新回合转移出的当前回合机动力加成;最终 movement 直接加其正值FUN_00486AD4 -> FUN_00482B10Confirmed
0x11ArmyBuff 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数量
153
247
340
443
552
641
744
845
941
1047

原版 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 生命周期与 countryIdx identity 已闭合,但完整 ArmyBuff Id/Type authoring domain 和 Stats 各 key 自然语义仍未知/Partial;
  • tunnelFromArea runtime producer/consumer、全局 TileId 域和“隧道挖掘”绑定已闭合,但没有非零 BTL disk-tail 正样本,FUN_00489A00 全部非法目标规则及与 TunnelArgs 的关系仍未闭合;
  • steelWillValue runtime 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      = NoGo

14. 主要证据 ​

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。

除另有说明外,本站有权许可的原创文档采用 CC BY-SA 4.0;第三方内容除外。