外观
BattleExArgs:BTL v3+ ExtensionArea
状态:Authoritative Reference v1
1. 物理入口
BTL v3+ 在固定区段之后可以携带由 Header H54 指定长度的 protobuf payload:
text
fixed BTL sections
-> H54 bytes ExtensionArea
-> Game.ProtoBuf.BattleExArgs当前最新审计 1.30.0 extension corpus:
text
v1/v2 AbsentByVersion = 450
v3 PresentEmpty = 518
v3 PresentNonEmpty = 415415/415 个非空 payload 都符合从 1.30 原版 SO 恢复出的 Game.proto descriptor:
- 无未知 field number;
- 无错误 wire type;
- 无截断 message;
- 无 unresolved message type;
- 无 required-field 缺失。
总 ExtensionArea 数据量为 121070 字节。历史 1.28.0 基线仍保留 407 个非空 payload / 120389 字节,用于既有 native consumer 与 writer 研究对照。
2. Descriptor authority
历史直接 descriptor authority 来自 Android 1.28.0 原版 ARM64 SO .rodata 中的完整 FileDescriptorProto:
text
file offset = 0x83BEF4
length = 12676 bytes
end = 0x83F078
sha256 = 578ec76aee16db23f0814930052ba9b508837f914e6feb40fb9673c3d5c032fc
package = Game.ProtoBuf
syntax = proto2
messages = 79 top-level
Enums = 2 top-levelBattleExArgs 可达类型树共有 17 个 message、78 个字段。
Android 1.29 与 1.30 的 ARM64 descriptor 直接对比显示,该 17/78 可达树逐字段不变;BattleExArgs 顶层 9 个字段和 BattleArmyExArgs 7 个字段完全一致。79 个顶层 message 中只有不属于这棵树的 ArmyGroupPassArgs 发生 schema 变化。因此 1.30 精英皮肤没有通过新增 Extension protobuf 字段实现。
这意味着字段名、编号、label 和 protobuf 类型不是社区推断,而是原版 descriptor 直接证据。1.30 对比证据见 reports/btl_reference/android_1300_baseline_and_elite_skin_index.json。
3. BattleExArgs 顶层字段
| Field | 名称 | 类型 | BTL loader consumer | 当前 1.30 corpus | 语义状态 |
|---|---|---|---|---|---|
| 1 | countryExs | repeated BattleCountryExArgs | 按 country id 查找 Country,委托 FUN_0048934C | 0 | Confirmed consumer / UnknownNoPositiveCorpus |
| 2 | secondaryConditions | repeated SSecondaryConditionArgs | 复制 condition/num 到 Battle 条件表 | 812 | Confirmed |
| 4 | armyExs | repeated BattleArmyExArgs | 按 area 查找 Army,委托 FUN_00480CC8 | 8467 | Confirmed |
| 5 | conquerEvents | repeated SConquerEventArgs | 构造事件运行时条目并设置相关状态 | 0 | Confirmed consumer / UnknownNoPositiveCorpus |
| 6 | warrecords | repeated SWarRecordArgs | 复制六整数战报记录 | 0 | Confirmed consumer / UnknownNoPositiveCorpus |
| 7 | numintkeyarmy | optional int32 | 写入 Battle+0x25C | 0 | Confirmed consumer / UnknownNoPositiveCorpus |
| 8 | armyGroupReinforcements | repeated SArmyGroupReinforcementArgs | 向 Reinforcement service 提交 armyId/num/cost | 0 | Confirmed consumer / UnknownNoPositiveCorpus |
| 9 | buildingExs | repeated BattleBuildingExArgs | 按 area 查找 Building,委托 FUN_004879FC | 0 | Confirmed consumer / UnknownNoPositiveCorpus |
| 10 | gloryChallengeStats | repeated GloryChallengeStatArgs | 解析 id/count 并追加运行时记录 | 0 | Confirmed consumer / UnknownNoPositiveCorpus |
注意:当前 1.30 BTL corpus 仍只出现 field 2 和 field 4,不等于其他七个字段“未实现”。原版 BTL loader 已经存在它们的直接导入分派;缺少的是当前原版磁盘正实例。
4. Loader consumer coverage
原版主导入链:
text
FUN_00331CD4
-> ParseFromArray(H54 bytes)
-> FUN_00339B58(Battle*, BattleExArgs*)当前静态 consumer matrix:
text
BattleExArgs top-level dispatch 9 / 9
reachable messages 17
reachable descriptor fields 78
RuntimeConsumed 77
NoLoaderConsumerObserved 1
schema/matrix exact 78 / 78唯一在 descriptor/generated object 中存在,但当前 BTL loader 导入链没有读取的字段是:
text
BattleCountryExArgs.countryTechCategoryIds它的正式状态是:
text
SchemaPresentButIgnoredByBtlLoaderImportPath这只证明当前版本 BTL loader 不读取它,不能推出该 protobuf 字段在其他路径永远无效。
5. 立即对象引用
FUN_00339B58 对三类 Extension record 使用立即 lookup:
countryEx.id
text
countryEx.id
-> FUN_00346FB0
-> 按 runtime Country 首个 int32 / Country.RecordId 精确查找
-> missing => null
-> 不调用该条 countryEx 的委托 consumerarmyEx.area
text
armyEx.area
-> global Tile object vector lookup
-> Tile exists and has Army
-> FUN_00480CC8buildingEx.area
text
buildingEx.area
-> global Tile object vector lookup
-> Tile exists and has Building
-> FUN_004879FC未解析的立即引用不会由 loader 自动创建、重定位或修复对象。用户可见提示和后续动态副作用仍需单独行为验收。
6. area 使用全局 TileId 地址空间
Extension 中 armyEx.area/buildingEx.area 使用的是 runtime global Tile object 地址空间。
不能用:
text
Header.AlignedTileSlotCount作为有效性上限。
当前 1.30 的 8467 条 armyEx.area:
text
fixed Unit.TileId 至少一条同值 = 8467 / 8467
唯一固定 Unit 匹配 = 8409
多条固定 Unit 同 TileId = 58
无固定 Unit 匹配 = 0其中 10 条 area 大于等于当前 BTL 本地 TileSlotCount,但仍有合法 fixed Unit.TileId 同值。这些是全局 TileId,不得因超出当前 crop/local slot 就删除或清零。
7. 当前可达 schema
原版 BattleExArgs 可达的 17 个 message:
text
BattleExArgs
BattleCountryExArgs
BattleEliteCountArgs
SCardCDArgs
SCardRoundArgs
SEventBuffArgs
TunnelArgs
SSecondaryConditionArgs
BattleArmyExArgs
SArmyBuffArgs
SArmyStatArgs
SConquerEventArgs
SWarRecordArgs
SArmyGroupReinforcementArgs
BattleBuildingExArgs
SBuildingBuffArgs
GloryChallengeStatArgs本 Reference 当前优先对实际 corpus 中存在的:
text
SSecondaryConditionArgs
BattleArmyExArgs做逐字段公开说明。
其余消息的 descriptor 与 BTL loader consumer 已静态闭合,但由于当前原版 BTL 无正实例,不能凭函数签名替代真实值域和动态玩法结果。
8. 原版 runtime -> protobuf writer 已直接恢复
本轮 fresh ARM64 1.28.0 Ghidra 恢复确认,原版并不是把读取时的 ExtensionArea 原始 bytes 原样保存,而是从 runtime state 重新构造一份新的 protobuf message。
主 writer:
text
FUN_003332C4Army Extension 的存在性 gate:
text
FUN_00480C54(Army)其直接 caller 只有 FUN_003332C4 的三个调用点:
text
0x00333480
0x00333544
0x00333754当 gate 返回 true 时,writer 会:
text
allocate fresh BattleArmyExArgs protobuf object (0x60 bytes)
-> set area from runtime TileId
-> FUN_00480FCC(runtime Army, BattleArmyExArgs)FUN_00480FCC 直接投影:
text
Army+0xC8 -> bynameId
Army+0x11C -> tunnelFromArea
Army+0x174 -> steelWillValue
Army+0xCC -> legendArmyId
Army+0x88..0x98 -> repeated armyBuffs
Army+0x198..0x1A8 -> repeated armyStats因此 FUN_00480C54 不再是“可能的 serializer gate”,而是已确认的 BattleArmyExArgs inclusion gate。
9. H54 与变长 ExtensionArea 的原版 writer 机制
FUN_003332C4 构造完整 BattleExArgs 后:
text
FUN_00563778(BattleExArgs)
-> 得到 protobuf serialized size
-> allocate exactly that many bytes
-> google::protobuf::MessageLite::SerializeToArray(...)序列化成功后:
text
param_1 + 0x7C = serialized byte length而该 staging object 写回 BTL Header 时,以 param_1+0x28 对应文件 Header 0x00:
text
0x7C - 0x28 = 0x54因此:
text
Header H54 = serialized BattleExArgs byte length随后 protobuf bytes 被 memcpy 到所有固定 section 之后的终端位置。
如果 serialized size 为 0,writer 保持此前初始化的:
text
H54 = 0所以“ExtensionArea 是终端变长 protobuf,H54 等于其字节长度”现在有原版 writer 直接证据,不再只是 parser/corpus 侧推导。
10. Serializer / wire-preservation 边界已经收紧
原版 audited save path 的关键事实是:
text
fresh BattleExArgs object
+ runtime -> protobuf projection
+ MessageLite::SerializeToArray这意味着至少对当前 1.28 writer 路径:
- 原版不会以“保留输入 raw protobuf bytes”为目标;
- Army Extension 会从 runtime 已知状态重新生成;
- 输入 field order 不需要被原版 writer 原样保留;
- 显式/缺省表示可以被 protobuf generated serializer 规范化;
- 没有证据显示原始 parsed unknown-field set 被复制进新构造的
BattleExArgs。
因此旧的:
text
canonical protobuf reserialize = mechanism unknown已经被 supersede。更准确的是:
text
original same-version regeneration mechanism = Confirmed
scoped editor writer foundation = Implemented+CorpusQualified(bynameId, legendArmyId only)
production editor writer = NoGo
cross-version future unknown-field safety = ProtectedByLosslessLocalPolicy, complete original behavior Unknown
automatic reference repair = NoGo仍不能因此直接宣布任意编辑 writer 安全。BtlExtensionWriterDefinitionGate/v1 已明确采用更保守的编辑器策略:
- 第一门只允许
BattleArmyExArgs.bynameId与legendArmyId; area只作为 resolved primary Army 的全局 TileId identity,不允许用户直接编辑;- duplicate singular、同 area 多 record、malformed wire、shadowed/rejected Unit 一律 fail closed;
- 未命中的 top-level/child wire node 与 unknown fields 要原字节保留;
- 不对整个 ExtensionArea 做 generated-protobuf canonical reserialize;
- 现有 fixed-length writer 与 preservation contract 不得放宽;
- 变长 Extension 需要独立 transaction/save/preservation contract。
因此“必须 wire-preserve 才能像原版一样保存”仍然不成立;这里选择 lossless local rewrite 是编辑器 future-data 安全策略。正式门禁见 writer_gate.md 与 reports/btl_reference/extension_writer_gate.json。
11. 当前 closure backlog
Extension 当前真正剩余的是:
- 当前无正样本的七个顶层字段的真实 BTL authoring 值域;
countryEx/buildingEx正实例;countryTechCategoryIds在 BTL loader 外是否有 protobuf consumer;- 无效 country/army/building/config 引用的完整动态反馈;
- duplicate Unit 后续失败对象 lifecycle;初始 primary Unit winner 已由后续 authority 闭合;
- duplicate singular / malformed protobuf / 跨版本 unknown-field 的兼容边界;
- 静态 consumer 对应的全部动态玩法效果;
- lossless local wire transformer、H54 联动、独立 variable-length preservation/transaction、Preview/Apply/Undo/Redo 和 corpus reparse 已完成;剩余为 package boundary qualification、Scenario Editor admission 与 original-game behavior acceptance。
12. 1.30 精英皮肤与 Extension 的边界
Android 1.30 新出现的精英皮肤 BTL 正样本不属于本 protobuf 区域:
text
conquest9.btl: Version=3, H54=0
Unit+0x2F: 30 条非零,值均为 1
BattleExArgs reachable schema: 1.29 -> 1.30 unchanged
BattleArmyExArgs: 仍只有 field 1..7因此不能把 Unit+0x2F 的 Elite skin index 迁移、镜像或解释成 BattleArmyExArgs 新字段。两者是不同的磁盘结构域。
主要证据:
text
reconstruction/docs/btl_editor_round39_extension_descriptor_complete_schema_recovery_report.md
reconstruction/docs/btl_editor_round40_extension_native_consumer_coverage_recovery_report.md
reconstruction/docs/btl_editor_round41_extension_reference_integrity_evidence_recovery_report.md
reports/btl_editor/round39/extension_descriptor_schema_evidence.json
reports/btl_editor/round40/native_consumer_coverage.json
reports/btl_editor/round41/reference_integrity_evidence.json
reports/btl_reference/army_extension_runtime_semantics.json