GTAMODX
圣安地列斯
圣安地列斯
272
圣安地列斯安卓版
圣安地列斯安卓版
32
罪恶都市
罪恶都市
721
罪恶都市安卓版
罪恶都市安卓版
178
GTA3
GTA3
136
GTA3安卓版
GTA3安卓版
51
查看所有游戏
专栏组织QQ群
动态
登录
GTAMODX

为游戏爱好者打造的模组分享社区。发现、创造、分享,让游戏世界无限可能。

支持

  • 关于我们
  • 帮助中心
  • 用户协议
  • 隐私政策

创作者

  • 创作中心
  • 上传规范
  • 开放平台
  • 组织广场

资源

  • 全部MOD
  • 游戏下载
  • 最新资讯

平台

  • 搜索模组
  • 社区动态
  • 网站地图
  • QQ频道
  • QQ群
  • 支持MODX

友情链接

bilibiliMikiGTAOL攻略资源网罪恶都市gta吧

© 2026 GTAMODX. All rights reserved.

隐私政策服务条款
CHK格式 - 游戏纹理文件
教程

CHK格式 - 游戏纹理文件

Lzh10_慕黑Lzh10_慕黑 2026年9月1日 1 评论

GTA .chk(ChkFile)Format Specification + CHK Tool Development Reference

版本:v2.3(开发级规范,2026-09-04。v2.1:并入附录 E「全语料普查」49/49 实证与证据 E024–E033,原编号与原文内容不变;v2.2:文风修整 + 本机绝对路径改语料内相对路径,事实与结论零变动;v2.3:工程审计增补——多字节/单字节端序表述精确化、【NORMATIVE】标记约定、§6.1 0x28/0x2C 勘误升格为规范裁定(实现一律以 §8.2 meta_base+0x10 为准)、§32 结论表 PSP 推断链口径注、伪代码切片显式端点消歧义、新增 §29.1 最小测试向量三组(原始字节可复现);既有结论零变动)。基于 crates/txd-format/src/chk.rs 的实现 + 逆向样例 + 全语料(49 个 .chk)逐字节核对。 .chk 没有公开官方规范,本文区分 ✅ 已确证 / ⚠️ 推断 / ❓ 待验证,不把推测写成事实。 目标:开发者仅凭本文即可彼此独立实现出一致结果的 Parser / Decoder / Encoder / Serializer / Validator。 旧版(格式研究文档,chk-format.md.bak-pre-spec)的全部信息已逐字保留并归入新章节(新旧章节映射见附录 D);新增技术结论一律标注证据 ID(§31 证据表 E001…)。


目录

  1. 概述 Overview
  2. 术语 Terminology
  3. 支持的家族 Supported Families
  4. 二进制布局 Binary Layout
  5. 通用头 Common Header
  6. 头字段表 Header Field Table(A/B 逐字段)
  7. 偏移与尺寸规则 Offset / Size Rules
  8. 重定位与指针系统 Relocation / Pointer System
  9. Family A 实现规范
  10. Family A 像素格式
  11. Family A 调色板
  12. Family A 元数据块(Meta 92B)
  13. Family B 实现规范
  14. Family B PAL8(8bit 块状)
  15. Family B PAL4(4bit 块状)
  16. 块布局 Block Layout
  17. 半字节布局 Nibble Layout(4bit)
  18. 调色板规范 Palette Specification
  19. 纹理解码 Texture Decode(CHK→索引→RGBA)
  20. 纹理编码 Texture Encode(RGBA→索引→CHK)
  21. 编码管线 PNG→CHK
  22. 解码管线 CHK→PNG
  23. 序列化与字段变更策略 Serialization / Mutation Policy
  24. 精确保留模式 Exact Preservation Mode
  25. 重建模式 Rebuild Mode
  26. 校验器规范 Validator Specification
  27. 修复规范 Repair Specification
  28. 错误分类 Error Classification
  29. 测试语料 Test Corpus(SHA-256)
  30. 往返测试 Round-trip Tests
  31. 证据与置信度体系 Evidence / Confidence
  32. 已知未知 Known Unknowns
  33. 参考伪代码 Reference Pseudocode
  34. 实现规则(禁止行为清单)Implementation Rules
  35. 开发闭环架构图
  36. 与 TXD 的区别
  37. 附录 A:真实样例头部十六进制 dump
  38. 附录 B:通道映射测试法
  39. 附录 C:已知不一致记录(只记录,不改判定)
  40. 附录 D:新旧章节映射
  41. 附记(原文逐字保留)
  42. 附录 E:全语料普查(Census)

1. 概述 Overview

  • .chk 是一张 调色板索引 纹理(非真彩、非 TXD 字典):每个像素存的是调色板下标。
  • 魔数:b"xet\0"(0x78 0x65 0x74 0x00),注意是 "xet" 反序(对应 "tex")。
  • 一个 .chk 文件 = 单张贴图(宽 × 高 + 调色板 + 索引像素),与 RenderWare .txd(可含多贴图)不同。
  • 平台:✅ Family A = PS2,⚠️ 推断 Family B = PSP(依据:用户给的 LOADSC0/LOADSC3 确认为 PS2 (E011);PSP 专用工具 GTAStoriesTex 只认 Family B (E010))。
  • 字节序(✅):所有字段(魔数、尺寸、偏移、地址表、log2 几何、位深类型)均为 little-endian。截至当前样例未发现 big-endian 版本;若未来样本呈高位端序,需按像素做字节交换,此处留作警示。
    • (v2.3 精确化)端序描述适用于多字节整数字段(u32 偏移/尺寸/地址表等,按 LE 读写的由 le32/put_le32 承担);单字节字段(魔数逐字节、B 头 0x41–0x44、名字字节等)是原始字节值,不涉及端序概念。
  • 不含 mipmap / 无级联(✅):.chk 是单一级的位图,解析未发现任何多级/mip 链,只有一个像素区 + 一个调色板。它不是像 .txd 那样可含多贴图/多 mip 的字典。
  • 两家族容器头结构完全相同(✅),不能用 header 前部区分平台,只能靠 0x3C 标志 + 像素布局区分(见 §3、§5.1)。
  • 实现参考(公式的最终裁判):crates/txd-format/src/chk.rs。本文所有公式均与该实现逐行核对。
  • (v2.3)规范性标记约定:✅/⚠️/❓ 为置信标记;正文中【NORMATIVE】表示"实现必须遵守、不得自行变体"的规范要求(检测顺序、指针表、判定①等);未标注的公式与伪代码为规范性实现指引;E 证据、普查过程性描述为参考信息(informative)。

2. 术语 Terminology

术语含义
Family A0x40 头 + 方形 8bit 行主序像素 + 256 色 RGBA 调色板 + 92B meta + tail 的布局(PS2,见 §9)
Family B0xA0 头 + 128 字节块状像素 + 256/16 色 RGBA 调色板 + tail 的布局(PSP,见 §13)
kindchk.rs 中的布局枚举:Psp8Bit256(A)/ PspBlock8Bit / PspBlock4Bit(B 两种位深)
PAL8 / PAL48bit 索引(256 色调色板)/ 4bit 索引(16 色调色板)
pixel region像素区:header 之后、palette 之前的索引数据区
blockFamily B 的 128 字节存取单元:8 行 × 16 字节/行(8bit=8×16 像素,4bit=8×32 像素)
nibble4bit 半字节;PAL4 一个字节装 2 个像素索引
data_endheader 0x0C/0x10 字段 = 文件大小 − tail(0x1C)【✅】
metaFamily A 独有的 92 字节元数据块(名字 + 指针,见 §12)
tail文件末尾 0x1C=28 字节的 7×u32 偏移/地址表(见 §8)
swap34Family A 索引 bit3↔bit4 交换变换(自反,见 §10.2)
row-major行主序:逻辑像素按行从左到右、从上到下排列
block-major块主序:先按块(左→右、上→下),块内再按行
byte-exact roundtripparse→serialize 输出与原文件逐字节相等(最强证据形式)
preserve / recalc / regenerate字段保存策略:原样保留 / 按公式重算 / 从文档模型重建(见 §23)
unknown_xx语义未知的字段命名约定(如 unknown_04),工具必须原样保留
Tool Policy工具(编码器)自由选择的策略(如量化算法),不是 CHK 格式规则(见 §20.3)

3. 支持的家族 Supported Families

Family AFamily B(8bit)Family B(4bit)
kind(chk.rs)Psp8Bit256PspBlock8BitPspBlock4Bit
平台【✅ A / ⚠️ B】PS2 (E011)PSP(推断,E010)PSP(推断,E010)
header 大小0x40(64B)0xA0(160B)0xA0(160B)
位深8bit 索引8bit 索引4bit 索引
调色板256×RGBA256×RGBA16×RGBA
像素布局方形(W==H),行主序块状 8×16 像素/块块状 8×32 像素/块
像素区大小W×H 字节(完全平方数)block_rows×block_cols×128同左
索引交换swap34(bit3↔bit4)无无
meta 块92B(名字在 meta[0x18])无无
名字位置meta[0x18](8B,NUL 截断)header 0x58(8B,NUL 截断)同左
tail0x1C(7×u32,meta 基址序列)0x1C(7×u32,header 内偏移常量表)同左

家族自动检测(ChkFile::parse,chk.rs L101–L143)(✅),检测顺序是规范的一部分【NORMATIVE——任何语言实现必须保持同一顺序,不得调换 A/B 试探次序或增删步骤】:

  1. 校验魔数 xet\0、文件 ≥ 0x40+0x1C、0x08==实际长度、0x0C==长度−0x1C;
  2. 先做 Family A 几何试探:按 A 公式算 palette_off8 = len − 0x1C − 92 − 1024,pixel_bytes8 = palette_off8 − 0x40;若 pixel_bytes8 是完全平方数(isqrt 判定)→ 按 Family A 解析;
  3. 否则若 le32(0x3C) == 0xA0 → 按 Family B 解析;
  4. 都不满足 → 报错 unrecognized .chk pixel layout。

⚠️ 注意:A 的检测不依赖 0x3C(A 文件 0x3C=0),是纯几何判定;B 检测依赖 0x3C==0xA0。理论上存在"4bit B 文件像素区恰好通过 A 方形试探"的构造可能,当前 49 样本中未出现(E017),检测顺序照抄实现即可。


4. 二进制布局 Binary Layout

4.1 容器(通用结构)【✅ 原文保留】

[ header ] [ pixel region ] [ palette ] [ (Family A: meta block) ] [ tail 0x1C ]
  • tail:0x1C = 28 字节(7 个 u32),两家族共用。
  • header 关键字段(little-endian u32,偏移自文件头):
    • 0x08 = 文件总大小(解析时校验 == 实际长度)。
    • 0x0c = data_end(= 文件大小 − tail)。
    • 0x10 = data_end。
    • 0x3c = 0xA0 → Family B 标志(用于自动识别家族)。
  • 容器头结构在 Family A / Family B 完全相同(✅):不能用 header 分 PSP/PS2,只能靠像素布局分。

4.1.1 布局图(v2.3 补充,informative;数值取自 §29.1 向量实测)

Family A(512×512,总长 0x404B8 = LOADSC0.CHK)        Family B(512×1024 4bit,总长 0x400FC = FONTSJP.CHK)
0x00000 ┌ header      0x40                          0x00000 ┌ header      0xA0
0x00040 ├ pixels  W·H = 0x40000(行主序+swap34)      0x000A0 ├ pixels  br·bc·128B 块区
0x40040 ├ palette 256×4B = 0x400                              │  (4bit: bc=W/32, br=H/8;
0x40440 ├ meta     92B(meta_base,名字@0x18)                │   8bit: bc=W/16, br=H/8)
0x4049C └ tail     7×u32 = 0x1C                 0x400A0 ├ palette nb×4B(8bit 0x400 / 4bit 0x40)
                                                0x400E0 └ tail    7×u32 = 0x1C

4.2 Family A 全文件结构表(W==H,参考 512×512)

列说明:Read = 何时读(Parse=解析、Validate=校验);Write = 保存策略(preserve=原样保留 / recalc=按公式重算 / regenerate=从文档模型重建,详见 §23);Confidence = 置信度。

OffsetSizeTypeFieldDescriptionReadWriteConfidence
0x004u8[4]magic"xet\0" = 78 65 74 00Parse (V01)regenerate(恒定值)✅
0x044u32unknown_04观测恒 0;语义未知,NEVER 修改Parsepreserve✅ 值 / ❓ 语义
0x084u32file_size文件总字节数,== 实际长度Parse (V03)recalc✅
0x0C4u32data_end= file_size − 0x1CParse (V04)recalc✅
0x104u32data_end2data_end 复本,== 0x0C 值Parse (V05)recalc✅
0x144u32unknown_14观测恒 7;语义未知Parsepreserve✅ 值 / ❓ 语义
0x184u32unknown_18观测恒 0Parsepreserve✅
0x1C4u32unknown_1C观测恒 0Parsepreserve✅
0x204u32unknown_20观测恒 6;语义未知Parsepreserve✅ 值 / ❓ 语义
0x244u32unknown_24观测恒 0Parsepreserve✅
0x284u32ptr_28观测 = meta_base+0x10(如 0x40450);见 §8、附录 C-1Parse/Validate (V19)recalc✅ 字节观测 / ⚠️ 语义
0x2C4u32ptr_2C== 0x28 的值Parse/Validate (V19)recalc✅
0x304u32unknown_30家族特有值,逐样本可变(A=0x042C4100);NEVER 自动修改Parsepreserve⚠️
0x344u32unknown_34同 0x30(A=0x056DC9F8)Parsepreserve⚠️
0x384u32unknown_38观测恒 0Parsepreserve✅
0x3C4u32family_flagFamily A 观测 0(B 为 0xA0)Parsepreserve✅
0x40W×Hu8[W*H]pixels索引像素,行主序,每字节经 swap34(§10.2)Parseregenerate✅
palette_offset1024RGBA×256palette256×[R,G,B,A],紧邻像素区(§11)Parseregenerate/preserve✅
palette_offset+102492metameta元数据块(§12)Parsepreserve+patch✅
file_size−0x1C0x1Cu32×7tail偏移/地址表(§8.3 A 表)Parse/Validate (V18)首两项 preserve+后五项 recalc✅
  • pixel_offset = 0x40(✅);pixel_region_size = W*H(✅;W==H,像素字节数为完全平方数,isqrt 检测)。
  • palette_offset = file_size − 0x1C − 92 − 256*4(✅);palette_size = 1024(✅)。
  • meta_offset = palette_offset + 1024(✅);meta_size = 92(✅);tail_offset = file_size − 0x1C(✅)。
  • file_size = 0x40 + W*H + 1024 + 92 + 0x1C = W² + 1208(✅ 逐项相加核对成立)。W=512 → 262144 + 1208 = 263352 = 0x404B8,与 LOADSC0/LOADSC3 实际字节数一致 ✔(263352 − 0x40 − 1024 − 92 − 0x1C = 262144 = 512² ✔)。

4.3 Family B 全文件结构表(PAL8 / PAL4)

OffsetSizeTypeFieldDescriptionReadWriteConfidence
0x004u8[4]magic"xet\0"Parse (V01)regenerate(恒定值)✅
0x044u32unknown_04观测恒 0Parsepreserve✅ 值 / ❓ 语义
0x084u32file_size文件总字节数Parse (V03)recalc✅
0x0C4u32data_end= file_size − 0x1CParse (V04)recalc✅
0x104u32data_end2data_end 复本Parse (V05)recalc✅
0x144u32unknown_14观测恒 7Parsepreserve✅ 值 / ❓ 语义
0x184u32unknown_18观测恒 0Parsepreserve✅
0x1C4u32unknown_1C观测恒 0Parsepreserve✅
0x204u32unknown_20观测恒 6Parsepreserve✅ 值 / ❓ 语义
0x244u32unknown_24观测恒 0Parsepreserve✅
0x284u32const_28观测恒 0x50(跨内容/尺寸恒定)Parsepreserve✅
0x2C4u32const_2C观测恒 0x50Parsepreserve✅
0x304u32unknown_30逐样本可变(46 文件 0x0AAE1BA0;FONTSJP 0x0AAE33A0);NEVER 自动修改Parsepreserve⚠️
0x344u32unknown_34同 0x30(0x08BF08CC / 0x08BEF28C)Parsepreserve⚠️
0x384u32unknown_38观测恒 0Parsepreserve✅
0x3C4u32family_flag= 0xA0 → Family B 标志Parse (V06)preserve(重建时 regenerate=0xA0)✅
0x401u8type观测恒 0x00Parsepreserve(重建 regenerate=0x00)✅
0x411u8bits_div48bit=0x02,4bit=0x01(位深冗余判定②)Parse重建时 recalc✅
0x421u8log2_wW=2^该值(读取时 & 0x0F)Parse (V07)重建时 recalc✅
0x431u8log2_hH=2^该值(读取时 & 0x0F)Parse (V07)重建时 recalc✅
0x441u8depth_type0x08=8bit,0x04=4bit(位深判定①:byte&0x08)Parse重建时 recalc✅
0x451u8unknown_45观测恒 0x01Parsepreserve✅ 值 / ❓ 语义
0x461u8unknown_46观测恒 0x04Parsepreserve✅ 值 / ❓ 语义
0x471u8unknown_478bit=0x25,4bit=0x45(随位深联动)Parse重建时 recalc✅ 值 / ❓ 语义
0x484u32unknown_48观测恒 56Parsepreserve✅ 值 / ❓ 语义
0x4C4u32unknown_4C观测恒 32Parsepreserve✅ 值 / ❓ 语义
0x504u32unknown_50观测恒 40Parsepreserve✅ 值 / ❓ 语义
0x544u32unknown_54观测恒 40Parsepreserve✅ 值 / ❓ 语义
0x588u8[8]name贴图名,NUL 截断(如 "capcom");可溢出到 0x60+(SCEJLEGALJ 为 10 字节名,溢出 2 字节,E029)Parsepatch(name_to_8)✅
0x6056u8[56]unknown_60观测全 0,除 0x98(见下行);普查字节级确认 0x60..0x9F 除 0x98 外唯一非零 = SCEJLEGALJ.CHK 0x60–0x61=4C 4A(名字溢出,C-10/E029)Parsepreserve✅ 观测 / ❓ 语义
0x981u8unknown_9846/47 文件=0x01;FONTSJP1=0x00 (E015);语义未知;普查复核 47/47 一致(E029)Parsepreserve⚠️
0x997u8[7]unknown_99观测全 0Parsepreserve✅ 观测
0xA0变块区pixels128B 块阵列,block-major(§16),无 swap34Parseregenerate✅
palette_offsetnb×4RGBA×nbpalettenb=256(8bit)/16(4bit)Parseregenerate/preserve✅
file_size−0x1C0x1Cu32×7tailB 固定值表(§8.3 B 表)Parse/Validate (V18)preserve(重建 regenerate)✅
  • pixel_offset = 0xA0(✅);block_bytes = 128、block_rows_per_block = 8、block_row_bytes = 16(✅)。
  • palette_offset = file_size − 0x1C − nb*4(✅);palette_size = nb*4(8bit=1024 / 4bit=64,✅);无 meta 块(✅)。
  • file_size 公式与样本核对见 §7.2。

5. 通用头 Common Header

5.1 两家族共有字段(0x00–0x3F)【✅ 原文保留并扩充】

偏移含义观测置信
0x00魔数 "xet\0"(= 0x00746578)恒定✅
0x08文件总大小= 文件实际字节数✅
0x0cdata_end= 文件大小 − tail(0x1C)✅
0x10data_end(复本)= data_end✅
0x14常量7(两家族恒为 7)✅ 家族共用常量,语义未明
0x18常量0✅
0x1c常量0✅
0x20常量6(两家族恒为 6)✅ 家族共用常量,语义未明
0x24常量0✅
0x30家族特有值(非恒定)A=0x042C4100;B:0x0AAE1BA0(FONTSJP1/INTRO1) / 0x0AAE33A0(FONTSJP)⚠️ 同家族+同名样本间仍可变,非内容哈希,非全局常量;语义未知
0x34家族特有值(非恒定)A=0x056DC9F8;B:0x08BF08CC(FONTSJP1/INTRO1) / 0x08BEF28C(FONTSJP)⚠️ 同 0x30,随样本变化;语义未知
0x38常量0✅
0x3cFamily B 标志B:0xA0;A:0✅

全语料补充(E013):0x30/0x34 在 49 样本中恰为 3 组,即 Family A 两样本一组(0x042C4100/0x056DC9F8);Family B 中 46 个文件一组(0x0AAE1BA0/0x08BF08CC,含 FONTSJP1/INTRO1/全部 TXD 样本),FONTSJP 单独一组(0x0AAE33A0/0x08BEF28C)。工具策略:原样保留,永不自动生成/修改。

普查补充(2026-09-04,E025/E028):上表恒定行(0x00/0x04/0x14/0x18/0x1C/0x20/0x24/0x38)经独立普查脚本对 49 个原生样本复核:两家族逐字段恒定 49/49,无例外。0x30/0x34 的排除性证据增强:内容哈希(CRC32/adler32/sum8/xor32/fnv1a × whole_file/header/pixel_region/palette/pixel+palette/name±NUL/filename 等 10 类对象)与 14 项布局指针/大小对候选在全部样本上 0 命中(附录 E.2):"非内容哈希、非布局指针"由假设升级为排除性实证;语义仍未知(❓ 不升级)。

5.2 解析期强校验(parse 阶段即失败项)【✅ 来自 chk.rs L101–L124】

校验条件失败错误(原文风格)
魔数bytes[0..4] == b"xet\0"bad magic (expected b"xet\0")
最小长度len ≥ 0x40 + 0x1Cfile too small (N bytes)
file_sizele32(0x08) == 实际长度header file size N != actual M
data_endle32(0x0C) == len − 0x1Cheader data end N != M

6. 头字段表(Header Field Table,A/B 逐字段)

偏移 + 观测值来自对全部样例(LOADSC0/LOADSC3 Family A 与 TXD\*.chk Family B)的逐字段 dump。 u32 均为 little-endian。数值栏给出"观测常量"或"随内容变化的样例"。 标注:✅ 确证 / ⚠️ 推断 / ❓ 语义未知。

6.1 Family A 特有(0x40 头 + meta)

偏移含义观测置信
0x28,0x2cmeta 起始地址= data_end − 92✅
meta[0x00]常量0x40✅
meta[0x08]meta 基址= meta 起始✅
meta[0x18]贴图名(8 字节,NUL 截断)如 "loadsc0"✅
meta[其余]大多为 00❓ 少量字段语义未确认

❌ 规范裁定(E019;v2.3 升格为正式结论):上表 0x28/0x2C = data_end − 92 一行已被字节实测证伪,禁止按该式实现。规范值:0x28/0x2C = meta_base + 0x10(§8.2 表【NORMATIVE】,与 chk.rs L488–489 写入一致,byte-exact 通过)。核对数据:LOADSC0/LOADSC3 实测 0x00040450,而 data_end − 92 = 0x00040440(恰为 meta_base),两者差恒为 0x10。原文行按"逐字保留"原则未改动;实现者一律以 §8.2 与附录 C-1 为准。

6.2 Family B 特有(0xA0 头)

偏移含义观测置信
0x28,0x2c常量0x50,0x50✅ 跨内容/尺寸恒定
0x40类型0x00✅
0x41bits / 48bit=0x02,4bit=0x01✅
0x42log2(W)如 512→0x09✅
0x43log2(H)512→0x09,256→0x08✅
0x44位深类型8bit=0x08,4bit=0x04✅
0x45常量0x01✅
0x46常量0x04✅
0x47常量8bit=0x25,4bit=0x45✅
0x48常量0x38(56)✅ 跨 8bit/4bit 相同
0x4c常量0x20(32)✅ 同上
0x50常量0x28(40)✅ 同上
0x54常量0x28(40)✅ 同上
0x58贴图名(8 字节,NUL 截断)如 "capcom"✅

全语料补充(E015):0x60..0x9F 在全部 47 个 Family B 样本中除 0x98 外全为 0;header[0x98] 46 个文件为 0x01、FONTSJP1 为 0x00。unknown_98 语义未知,工具原样保留。

6.3 位深判定的两条途径(必须交叉核对)

  • 判定①(解析用,parse_block L333):bits = (header[0x44] & 0x08) != 0 ? 8 : 4(✅)。
  • 判定②(冗余校验用):header[0x41] == 0x02 即 8bit,== 0x01 即 4bit(✅)。两条途径在全部样本中一致;若不一致,Validator 报 CHK_ERR_UNSUPPORTED_BIT_DEPTH(V 检查见 §26)。

7. 偏移与尺寸规则(Offset / Size Rules)

7.1 Family A 公式【✅ 全部经 49 样本核对(E017)】

header_size    = 0x40
pixel_offset   = 0x40
W == H                     # 方形约束(编码强制;解析按 isqrt 检测完全平方数)
pixel_region_size = W * H  # 每像素 1 字节;必须为完全平方数(isqrt 判定)
palette_offset = file_size − 0x1C − 92 − 256*4
palette_size   = 256 * 4 = 1024
meta_offset    = palette_offset + 1024
meta_size      = 92
tail_offset    = file_size − 0x1C
file_size      = 0x40 + W*H + 1024 + 92 + 0x1C

样本核对:

  • LOADSC0.CHK:263352 = 0x40 + 262144 + 1024 + 92 + 28 ✔(263352 = 0x404B8;palette_offset = 263352−28−92−1024 = 262208 = 0x40040;meta_offset = 263232 = 0x40440;tail_offset = 263324 = 0x4049C)
  • LOADSC3.CHK:同上(同长度 263352 = 0x404B8)✔

7.2 Family B 公式【✅ 全部经 49 样本核对(E017)】

header_size    = 0xA0
W              = 2 ^ header[0x42]      # 读取时按字节取:1 << (b[0x42] & 0x0F)
H              = 2 ^ header[0x43]      # 1 << (b[0x43] & 0x0F)
bits           = (b[0x44] & 0x08) != 0 ? 8 : 4     # 判定①
               # 冗余判定②:b[0x41]==2 → 8bit;==1 → 4bit(须与①一致)
block_w        = 16 (8bit) | 32 (4bit)
block_h        = 8
block_bytes    = 128                   # 恒定
block_cols     = W / block_w
block_rows     = H / block_h
pixel_region_size = block_rows * block_cols * 128   # = file_size − 0xA0 − nb*4
palette_offset = file_size − 0x1C − nb*4           # nb = 256 (8bit) | 16 (4bit)
palette_size   = nb * 4                            # 1024 | 64
tail_offset    = file_size − 0x1C
file_size      = 0xA0 + block_rows*block_cols*128 + nb*4 + 0x1C

样本核对(四种文件长度全部被公式精确解释):

  • FONTSJP.CHK:262396 = 0x400FC = 0xA0 + (64×8×128=262144) + 16×4 + 28 ✔(512×1024 4bit;palette_offset = 262396−28−64 = 262304 = 0x400A0)
  • CAPCOM.CHK(8bit 512×512):263356 = 160 + 262144 + 1024 + 28 ✔
  • TXD\LOADSC0.CHK(8bit 512×256):132284 = 160 + 131072 + 1024 + 28 ✔
  • TXD\INTRO1.CHK(4bit 512×512):131324 = 160 + 131072 + 64 + 28 ✔
  • TXD\MEMCARD.CHK(4bit 512×256):65788 = 160 + 65536 + 64 + 28 ✔
  • 全量普查(E026,附录 E.7):上述公式(含 Family A 侧 file_size = 0x40 + W·H + 1024 + 92 + 0x1C 与容器 0x08/0x0C/0x10 公式)对全部 49 个原生样本逐文件核验,49/49 OK、误差为 0;尺寸类分布:A 512²×8 ×2;B 512×256×8 ×18、512×512×4 ×23、512×512×8 ×4、512×1024×4 ×1(FONTSJP)、512×256×4 ×1(MEMCARD)。

7.3 尺寸约束(Family B,引 validate_block_dims chk.rs L265–278)【✅】

  • W、H 必须为 2 的幂(因为 header 存的是 log2);
  • 8bit:W ≥ 16;4bit:W ≥ 32;H ≥ 8(块对齐:W % block_w == 0,H % 8 == 0);
  • 违反报错:chk 尺寸需为 2 的幂(8bit 宽≥16、4bit 宽≥32、高≥8);请求 {w}x{h} / chk 尺寸过小:宽需≥{min_w}、高需≥8;请求 {w}x{h}。
  • Family A 尺寸约束:W == H(方形,✅ 见 §9)。

7.4 像素区布局总公式【✅ 原文保留】

pixel_region_size = palette_offset - header_size,且 =
  (Family A: W*H)      (8bit 方形,每像素 1 字节)
  (Family B: block_rows * block_cols * block_bytes 块布局)

8. 重定位与指针系统(Relocation / Pointer System)

8.1 已确证结论总览

哪些字段存 offset、绝对还是相对:

字段内容绝对/相对置信
header 0x08文件总大小(自指)绝对(自文件头 0)✅
header 0x0C/0x10data_end = file_size − 0x1C绝对✅
header 0x28/0x2CFamily A:meta 基址序列成员(实测 = meta_base+0x10,E019;原文表记 data_end−92,冲突见附录 C-1);Family B:常量 0x50A 绝对 / B 常量✅ 字节 / ⚠️ A 语义;【NORMATIVE】实现以本行为准
header 0x30/0x34家族特有值,逐样本可变,语义未知Unknown⚠️
meta[0x08]meta 基址(A 的 meta 内自指针)绝对✅
tail 全部 7 项偏移/地址表(见 8.3)A 后 5 项绝对 / B 后 5 项为 header 内偏移("相对")✅

Family A 指针公式(chk.rs serialize_square L482–500 逐行):

meta_base      = 0x40 + W*H + 1024            # = palette_offset + 1024 = meta_offset
header[0x08]   = meta_base + 92 + 0x1C        # = file_size
header[0x0C]   = header[0x10] = meta_base + 92
header[0x28]   = header[0x2C] = meta_base + 0x10
meta[0x00]     = 0x40                         # 恒定(观测 + default_meta)
meta[0x08]     = meta_base
tail[0x08..0x18] = meta_base + {0, 8, 0xC, 0x10, 0x14}

⚠️ meta[0x0C]、meta[0x10]、meta[0x14] 在样本中观测为 32/40/40(E012),chk.rs 解析后原样保留、不重算。meta[0x04..0x08] 观测 0x49 0x82 0x10 0x00(= u32 0x00108249,E012),语义未知,原样保留。

Family B 指针公式(chk.rs serialize_block L544–553 + default_font_header/default_font_tail):

header[0x08]   = 0xA0 + packed_len + palette_bytes_len + 0x1C   # = file_size
header[0x0C]   = header[0x10] = 0xA0 + packed_len + palette_bytes_len
header[0x28]   = header[0x2C] = 0x50                # 常量,不随内容变
tail           = {0x28, 0x2C, 0x3C, 0x48, 0x4C, 0x50, 0x54}  # 全常量

改布局后要更新哪些(Mutation 视角,详见 §23):

  • A:改像素区尺寸 → 重算 0x08/0x0C/0x10/0x28/0x2C、meta[0x08]、tail 后 5 项;0x30/0x34/0x14/0x20 等 unknown 一律原样保留。
  • B:改像素区尺寸/位深 → 重算 0x08/0x0C/0x10 + 0x41/0x42/0x43/0x44/0x47;0x28/0x2C=0x50、tail 全表、0x30/0x34、0x45/0x46/0x48/0x4C/0x50/0x54/0x98 原样保留(重建时按固定表重新生成)。

如何"重新生成":除上述 recalc 项外,Family B 的 0x28/0x2C/tail/常量字段、Family A 的 0x14/0x20/meta[0x00] 均有实测常量,可按 §6/§8 的固定值表重新生成(rebuilt_header_matches_real_family_b 已对 ≥40 个真文件验证重建头逐字段一致,E005)。0x30/0x34 没有可用的生成规则:精确保留模式下必须从原文件拷贝,重建模式下 chk.rs 写入家族代表值(A=0x042C4100/0x056DC9F8,B=0x0AAE1BA0/0x08BF08CC),此时不再是逐字节等价重建(⚠️ 如实标注)。

8.2 Family A 指针/常量一览(实现用)

位置观测值(LOADSC0/LOADSC3)生成规则置信
hdr 0x28 / 0x2C0x00040450meta_base + 0x10(E019;附录 C-1 记录与原文表差异)✅ 字节 / ⚠️ 语义
meta[0x00]0x40恒定✅
meta[0x04..0x08]49 82 10 00(u32 0x00108249)无规则,原样保留⚠️(E012)
meta[0x08]0x00040440= meta_base✅
meta[0x0C]32无规则,原样保留⚠️(E012)
meta[0x10]40无规则,原样保留⚠️(E012)
meta[0x14]40无规则,原样保留⚠️(E012)
meta[0x18..0x20]"loadsc0\0\0"name_to_8(name)✅
meta[0x58]0x01无规则,原样保留⚠️(E012)
meta 其余0原样保留✅ 观测

8.3 tail(0x1C = 28 字节,两家族共用结构)—— 子结构偏移/地址表【✅ 原文保留】

tail 是 7 个 u32 的偏移/地址表:前 2 项是指向内部子结构的固定偏移,后 5 项因家族而异。与生成器 default_tail/default_font_tail 逐项一致(已用原生 CAPCOM/LOADSC0 验证 byte-exact)。

Family A(PS2 方形):绝对地址(随文件大小变)

tail 偏移值置信
0x000x28✅
0x040x2c✅
0x08meta_base(如 0x00040440)✅
0x0cmeta_base + 0x08✅
0x10meta_base + 0x0c✅
0x14meta_base + 0x10✅
0x18meta_base + 0x14✅

Family B(PSP 块状):header 内偏移(恒定)

tail 偏移值置信
0x000x28✅
0x040x2c✅
0x080x3c✅
0x0c0x48✅
0x100x4c✅
0x140x50✅
0x180x54✅

Family A/B 的 tail[0..1] 同为 0x28/0x2C;Family B 后 5 项是 header 内偏移(0x3C, 0x48, 0x4C, 0x50, 0x54),Family A 后 5 项是 meta 基址序列。结构表语义已确证,但其消费方(是否每个都必须有效、固定)仍需游戏/工具侧验证。

E014 全语料交叉。49 个样本的 tail 值集恰为上述两组:46+2=48 个文件(全部 Family B + Family A 两样本)分别逐字节匹配 B 表与 A 表(A 表 meta_base=0x40440)。serialize_square 对 tail[0x00]/[0x04] 不重写(从原 tail 保留),default_tail/default_font_tail 的固定表与观测一致,精确保留与重建两条路都闭合。

普查刷新(2026-09-04,E027,附录 E.3)。独立普查脚本全量核验:A 2/2 + B 47/47 = 49/49 逐项精确匹配、无例外(补齐 E014 当时未计入的 FONTSJP,"46+2=48" 口径就此关闭)。_chk_verify 11 个工具生成文件的 tail 亦全部匹配 B 表(A 重建件匹配 A 表)。


9. Family A 实现规范

  • header 大小:0x40(64 字节)。✅ 原文保留
  • 几何要求:宽 == 高(正方形)。参考尺寸 512×512。
  • 布局:
    [ header 0x40 ] [ pixel region (square) ] [ palette 256×4 ] [ meta 92 ] [ tail 0x1C ]
    
    • palette_offset = len − tail(0x1C) − meta(92) − 256×4
    • pixel region = header[0x40 .. palette_offset],像素字节数为完全平方数(用 isqrt 检测)。
  • 索引:每像素 1 字节的 8bit 调色板下标,行主序;但解码/编码时做 chk_swap34 交换(交换 index 的 bit3 与 bit4)。
  • 调色板:256 个 [R,G,B,A](紧邻像素区之后)。
  • meta 块:92 字节;0x18 处存 8 字节贴图名(NUL 截断),0x08 处为偏移字段(回写用)。

实现要点(与 chk.rs 对齐):

  • Header:解析时整体保存 bytes[0..0x40] 原始字节(header: Vec<u8>,byte-exact 的关键);序列化时 clone 原始头,仅 patch 0x08/0x0C/0x10/0x28/0x2C(serialize_square L478–489)。
  • Pixel Offset:恒 0x40。
  • Pixel Size:palette_offset − 0x40 字节;W = H = isqrt(pixel_bytes),且必须 W*W == pixel_bytes(否则不是 Family A)。
  • W·H 推导:除 isqrt 外没有显式的 W/H 字段,Family A 头部不存尺寸,尺寸完全由文件大小反推(✅,实现推论:parse_square 参数 w=h=side)。
  • Palette Offset / Size:见 §7.1;Entry 布局 [R,G,B,A](§18)。
  • Meta:92B,整体保存原始字节;序列化时 patch meta[0x08]=meta_base 与 meta[0x18..0x20]=name_to_8(name)(L490、L476),其余字节原样。
  • Tail:整体保存原始字节;序列化时 patch 后 5 项(L496–500);首两项 0x28/0x2C 从原 tail 保留(与 default_tail 值一致)。
  • 索引纹理无损性:decode(encode(x)) == x 对索引层恒成立(swap34 是自反对合;palette 查表后若索引唯一则 RGBA 亦无损,✅)。

10. Family A 像素格式

10.1 行主序索引

  • 每像素 1 字节 = 调色板下标 0..255,逻辑顺序 row-major:pixel_index(x, y) = y * W + x(✅)。存储值是逻辑索引经 swap34 之后的值(见 10.2)。

10.2 索引交换(仅 Family A)【✅ 原文保留 + 公式】

chk_swap34(v):交换一个 8bit 索引的 bit3 与 bit4。仅 Family A 使用;Family B 直接存下标。

-- 位运算公式(chk.rs L566–568)--
swapped = (v & 0xE7) | ((v & 0x08) << 1) | ((v & 0x10) >> 1)
  # 0xE7 = 1110 0111b:清掉 bit3/bit4,其余位原样
  # bit3(0x08) 左移 1 位 → bit4;bit4(0x10) 右移 1 位 → bit3
-- swap34 是自反(involution):swap34(swap34(v)) == v,编码/解码同一个函数(L1299–1311 测试覆盖 0..255 全域)--

11. Family A 调色板

  • 256 个条目,每条目 4 字节 [R,G,B,A],紧邻像素区之后(offset = file_size − 0x1C − 92 − 1024,✅)。
  • 通道序 RGBA(✅ 决定性证据 E003:LOADSC0 解码与参考 PNG diff=0);Alpha 全范围行为见 §18。
  • 解析:for i in 0..256: entry[i] = bytes[palette_offset + i*4 : palette_offset + i*4 + 4](逐字节拷贝,不做通道交换;v2.3 显式端点)。

12. Family A 元数据块(Meta 92B)

  • 位置:palette_offset + 1024;大小 92 字节;解析时整体保存原始字节(✅)。
meta 偏移含义观测(LOADSC0/LOADSC3)置信
0x00常量0x40✅
0x04..0x08未知0x00108249(49 82 10 00,两样本相同)❓ 语义未知(E012)
0x08meta 基址= meta 起始(如 0x00040440)✅
0x0C未知32❓ 语义未知(E012)
0x10未知40❓(E012)
0x14未知40❓(E012)
0x18贴图名(8 字节,NUL 截断)如 "loadsc0"✅
0x58未知0x01(两样本相同)❓ 语义未知(E012)
其余观测为 00✅ 观测
  • 保存策略:除 0x08(recalc)与 0x18(随名字 patch)外全部 preserve / NEVER 自动修改(§23);语义未知字段没有证据不得改写。
  • 序列化实现:meta 长度 ≠92 时 resize 补 0(L472–474),这是重建路径的宽容行为,精确保留路径要求原始 92 字节原样。

13. Family B 实现规范

  • header 大小:0xA0(160 字节);贴图名在 0x58(8 字节,NUL 截断)。✅ 原文保留
  • 位深:byte[0x44] & 0x08 非零则 8bit,否则 4bit。
  • 几何:byte[0x42] = log2(W),byte[0x43] = log2(H)(因此 W/H 必须为 2 的幂)。
  • 布局:
    [ header 0xA0 ] [ block pixel region ] [ palette 256或16×4 ] [ tail 0x1C ]
    
    • palette_offset = len − tail(0x1C) − nb×4(nb = 256 或 16)。
    • pixel region = header[0xA0 .. palette_offset],按 128 字节块 排列。
  • 块布局:每个块 = 8 行 × 16 字节/行(CHK_BLOCK_BYTES=128,CHK_BLOCK_ROWS=8,CHK_BLOCK_ROW_BYTES=16)。
    • 8bit:每块 8×16 像素(1 字节/texel)。
    • 4bit:每块 8×32 像素(2 texel/字节,低 4bit 在前,高 4bit 在后)。
    • 块栅格:block_cols = W/16(8bit)或 W/32(4bit),block_rows = H/8。
  • 尺寸约束(✅):2 的幂;8bit 宽 ≥16、4bit 宽 ≥32;高 ≥8;块对齐(W % block_w == 0,H % 8 == 0)。
  • 索引:不做 bit3/4 交换。
  • 调色板:8bit=256 色,4bit=16 色。

实现要点(与 chk.rs 对齐):

  • parse_block(L332–384)流程:判位深(0x44&0x08)→ 定 nb/palette_offset → texels = pixel_bytes * (8/bits) → 读 w = 1 << (b[0x42] & 0x0F)、h = 1 << (b[0x43] & 0x0F) → 交叉核对 w*h == texels(不一致报 block .chk geometry inconsistent)→ 块对齐检查(不一致报 block .chk size not block-aligned)→ 读调色板 → 读名字(0x58,8B,NUL 截断,from_utf8_lossy)→ unpack_block 展开 → 保存 bytes[0..0xA0] 原始头与原样 tail。
  • serialize_block(L512–561):pack_block 重打包 → patch 头 0x08/0x0C/0x10 + 0x58..0x60 名字 → tail 原样 → 顺序拼接。
  • palette 交叉核对:kind() 由 header.len()==0xA0 && palette.len() 恢复(L157–167);位深判定①与调色板大小必须一致,否则 Validator 报错(V09)。
  • 尺寸约束引 validate_block_dims(§7.3)。

13.1 完整坐标映射公式【✅ 与 unpack_block/pack_block(L388/L416)一致】

-- 输入:逻辑坐标 (x, y),0 <= x < W,0 <= y < H;输出:pixel_offset(文件内字节偏移)--
block_x    = x / block_w          # block_w = 16(8bit) | 32(4bit)
block_y    = y / block_h          # block_h = 8
local_x    = x % block_w
local_y    = y % block_h
block_index  = block_y * block_cols + block_x      # block-major:先按块(左→右、上→下)
block_offset = block_index * 128
8bit:  byte_off = local_y * 16 + local_x           # 每 texel 1 字节
4bit:  byte_off = local_y * 16 + local_x / 2       # 每 2 texel 1 字节
       nibble   = local_x % 2                      # 0=低 4bit,1=高 4bit
pixel_offset = header_size + block_offset + byte_off   # header_size = 0xA0

-- 反向(文件 → 逻辑索引,unpack_block)--
idx8 = data[pixel_offset]                                    # 8bit 直接
raw  = data[pixel_offset]; idx4 = (nibble==0) ? raw & 0x0F : (raw >> 4) & 0x0F

Family B 不做 swap34:索引直接寻址调色板(✅)。块内顺序为行序(local_y*16 步进 16 字节),块与块之间按 block-major 连续存放。

13.2 尺寸/位深/名字字段在重建时的写入(patch_block_header L286–298)

header[0x40] = 0x00
header[0x41] = bits / 4
header[0x42] = log2(W)        # trailing_zeros,min(0x0F)
header[0x43] = log2(H)        # trailing_zeros,min(0x0F)
header[0x44] = 0x08 (8bit) | 0x04 (4bit)
header[0x45] = 0x01
header[0x46] = 0x04
header[0x47] = 0x25 (8bit) | 0x45 (4bit)

rebuilt_header_matches_real_family_b(L1053–1111)对 ≥40 个真文件验证:用零图重建的 header 在 0x28/0x2C/0x30/0x34/0x3C/0x40/0x44/0x48/0x4C/0x50/0x54 字段上与原文件逐字段一致(E005)。注意该测试比较的 u32 0x44 槽位同时覆盖 0x44..0x48(即 0x44/0x45/0x46/0x47 四字节)。

14. Family B PAL8(8bit 块状)

  • 位深 8bit:header[0x44]=0x08、header[0x41]=0x02、header[0x47]=0x25(✅)。
  • 块 = 8×16 像素 = 128 字节(1 字节/texel);block_w=16;W≥16 且为 2 的幂;H≥8 且为 2 的幂。
  • 调色板 256×RGBA(1024 字节)位于 file_size − 0x1C − 1024。
  • 索引直接寻址调色板,无 swap34、无 nibble。
  • 样本:CAPCOM/WARNING/SCEJLEGALJ/FONTSJP1(512×512)、TXD\LOADSC0–3/MPLOAD0–7/NEWS*/SPLASH1–3(512×256)。

15. Family B PAL4(4bit 块状)

  • 位深 4bit:header[0x44]=0x04、header[0x41]=0x01、header[0x47]=0x45(✅)。
  • 块 = 8×32 像素 = 128 字节(2 texel/字节);block_w=32;W≥32 且为 2 的幂;H≥8 且为 2 的幂。
  • 调色板 16×RGBA(64 字节)位于 file_size − 0x1C − 64。
  • Nibble 打包见 §17;低 4bit 在前(先到的像素放低半字节)(✅)。
  • 4bit 调色板是真 16 色 RGBA(不限灰度):源文件恰好是灰/alpha 字型,但彩色编辑图保留色相(from_rgba_font_keeps_color 测试,L978–1008,✅)。
  • 样本:FONTSJP(512×1024)、INTRO1–4 / LOADSC4–22(512×512)、MEMCARD(512×256)。

16. 块布局(Block Layout)【✅】

  • 像素区被切分为 128 字节块:每块 8 行 × 16 字节/行。
  • 块栅格:block_cols = W / block_w(8bit: block_w=16;4bit: block_w=32),block_rows = H / 8。
  • 块顺序:block-major:block_index = block_y * block_cols + block_x,从左到右、从上到下连续存放;块内按行序(k = 0..8),行内按字节序(a = 0..16)。
  • unpack_block(L388–412):对每个块、每行、每列把 packed[base + k*16 + a](或 nibble 拆分)写入 out[(bi*8+k)*w + bj*px_per_row + a]。
  • pack_block(L416–444):严格的逆变换;4bit 写入时低半字节初始化为 0 再 |= 高半字节,保证两个 nibble 都正确落位。
  • block_bytes = block_h * block_w / (8 / bits),两种位深均 = 128:

4.1 节原文:块栅格与 4bit 打包/解包【✅ 原文保留】

-- 位深(family B header 判定)--
bits = (header[0x41] * 4)          # header[0x41]=2->8bit, =1->4bit

-- 块几何(像素单位)--
block_w = 16  if bits==8  else 32  # 每块宽(像素)
block_h = 8                        # 每块高(像素,恒定)
block_cols = ceil( W / block_w )
block_rows = ceil( H / block_h )
block_bytes = block_h * block_w / (8 / bits)  # 恒为 128 字节
#   8bit: 8 行 × 16 字节 = 128
#   4bit: 8 行 × 32texel × 0.5 字节 = 128(每行仍 16 字节)

-- 4bit 半字节打包:低 4bit 在前,高 4bit 在后 --
def pack4(lo, hi): return (hi << 4) | lo          # lo=低4bit(先画), hi=高4bit
def unpack4(byte): return (byte & 0x0F), ((byte >> 4) & 0x0F)

-- 像素区布局 --
pixel_region_size = block_rows * block_cols * block_bytes
# 块按"从左到右、从上到下"的顺序连续存放;块内按行序,行内按字节序。
# 由 pixel_region_size = palette_offset - header_size,且 =
#   (Family A: W*H)      (8bit 方形,每像素 1 字节)
#   (Family B: 见上式块布局)

说明:block_bytes 两种位深都是 128,因此块大小恒定;差异只在 每行换算的像素数(8bit=16 像素/行,4bit=32 像素/行,均为 16 字节/行)。 实现注:有效样本的 W/H 均为 block_w/8 的整数倍,ceil(...) 在实际公式中退化为整除(parse_block 使用整除并显式校验块对齐);header[0x41]*4 与判定①(0x44&0x08)在全部样本中一致。

17. 半字节布局(Nibble Layout,4bit)【✅】

  • 读:byte = data[offset];local_x % 2 == 0(偶数位像素,先到)取 byte & 0x0F;local_x % 2 == 1(奇数位像素,后到)取 (byte >> 4) & 0x0F。
  • 写:先到的像素(偶数 local_x)放低 4bit,后到的像素(奇数 local_x)放高 4bit:byte = (hi << 4) | lo。
  • 1 byte = 2 像素;一行 32 像素 = 16 字节;与既有"低 4bit 在前"结论一致(✅:unpack_block/pack_block L402–405/L431–437 逐行一致,byte-exact 证据覆盖全部 4bit 样本,E002/E004)。
  • 序列化注意:写高半字节时用 out[byte_idx] |= idx4 << 4 且输出缓冲区已清零,不能直接赋值覆盖低半字节。

18. 调色板规范(Palette Specification)

18.1 条目布局

位深条目数条目大小总大小家族
8bit256 色(PAL8)4B1024BFamily A(PS2,方形)、Family B(PSP,块状)
4bit16 色(PAL4)4B64B仅 Family B(PSP,块状)
  • 调色板条目每项 4 字节 [R,G,B,A](✅ 原文保留)。字节序:byte0=R、byte1=G、byte2=B、byte3=A。
    • ✅ 决定性证据(E003):解析 LOADSC0.CHK 的 RGBA 与参考 loadsc0.png 逐字节 diff=0(512×512),通道序若有任何偏差(如 BGRA)diff 不可能为 0。
  • count 判定:Family A 恒 256;Family B 由位深判定①决定(8bit 时 256,4bit 时 16),并须与判定②(0x41)及调色板大小公式(palette_size = nb*4)交叉一致。
  • 实测首项示例(E012,仅供观察,非规范):FONTSJP 调色板首 8 字节 FD FD FD F5 FF FF FF 67(entry0=[253,253,253,245] 亮灰近不透明、entry1=[255,255,255,103] 白色半透明,与"字型灰/alpha"用途一致);LOADSC0 entry0=[246,212,107,255]。

18.2 Alpha 行为(⚠️/❓ 如实标注)

  • Alpha 字节取值 0..255 均出现在真实样本中(FONTSJP 即含 0xF5/0x67 等中间值,E012;✅ 存在性)。
  • 游戏渲染端对全范围 alpha 的插值/阶梯行为未证实(❓),需要运行时证据。工具侧策略:原样保留 alpha 字节,不做任何折算。
  • 普查收窄(2026-09-04,E030,附录 E.6):全语料 6544 个调色板条目中 alpha≠255 共 297 条,去重值集 49 种(0..254;其余 206 个中间值语料内未见);含 alpha==0 条目的文件 25 个。仅 FONTSJP 的 alpha≠255 条目被像素数据实际引用(≥12/16 条,且解码与 fontjp.png diff=0);其余 24 个含 alpha 条目文件的 alpha 条目全部未被索引引用。上句"0..255 均出现"应读作存在性成立;全范围覆盖语料内未证实(矛盾登记 C-13),渲染端语义仍 ❓ 不升级。

18.3 通道映射测试法(附录 B 摘要,详见 §38)

构造纯红/纯绿/纯蓝/纯黑/纯白/半透明测试图,编码后解码比对通道:若红绿互换说明通道序错误;若 alpha 全丢失说明第 4 字节不是 A。判定标准 = 解码 RGBA 与源 PNG diff=0。

19. 纹理解码(Texture Decode:CHK → 索引 → RGBA)

Family A 解码管线:

CHK → read header[0..0x40] → parse_square:
  pixel region bytes = file[0x40 .. palette_offset]
  logical index i = swap34(raw_byte)          # raw_byte = file[0x40 + (y*W + x)]
  RGBA(x,y) = palette[i]                       # 256 项查表

Family B 解码管线:

CHK → read header[0..0xA0] → parse_block:
  bits = (b[0x44]&0x08)?8:4; W=1<<(b[0x42]&0x0F); H=1<<(b[0x43]&0x0F)
  对每个 (x,y) 按 §13.1 公式定位 → raw index(4bit 按 nibble 拆)
  logical index = raw index(无 swap34)
  RGBA(x,y) = palette[index]                   # 256/16 项查表
  • 越界索引处理(✅ 实现定义):palette.get(e).unwrap_or([0,0,0,255]),索引 ≥ palette 长度时输出不透明白(to_rgba L178)。Validator 应把"索引 ≥ nb"列为错误(V14)。
  • 解码输出:height * width * 4 紧密 RGBA 缓冲。
  • Decoder 只读不改原始数据:解析结果除逻辑索引缓冲(unpack 后)外,header/meta/tail 原始字节必须原样保存(ChkFile 的 header/meta/tail 字段语义,L91–96)。

20. 纹理编码(Texture Encode:RGBA → 索引 → CHK)

20.1 索引生成(Format-enforced 部分)

  • Family A:logical_index = quantized_index,存储时 stored = swap34(logical_index)(from_rgba L198–200);编码前校验 W==H。
  • Family B 8bit:直接存 0..255 索引(from_rgba_block8)。
  • Family B 4bit:索引 &0x0F(from_rgba_font);16 色调色板。

20.2 调色板生成(Tool Policy,见 20.3)

chk.rs 参考实现 quantize_rgba(L591–689)的做法(作为参考策略描述,非格式规则):

  • 全透明像素(A==0)不参与颜色聚类,固定占 index 0([0,0,0,0]);
  • 其余颜色经 median-cut 聚类到 k−1(或 k)个箱,箱代表 = 频率加权平均(box_avg);
  • 最近邻分配用 dist2 = dr²+dg²+db²+(da/2)²,即 Alpha 降权(颜色主导,L586);
  • 调色板不足 k 项时用最后一项补齐。

20.3 Native Format Rules vs Tool Encoding Policy【必须分离】

条目类别说明
CHK 只规定 Palette + Index 两层格式规则任何合法量化算法产生的 palette+index 都是合法 CHK
索引必须落在 0..nb−1格式规则违反即非法文件
Family A 存储索引经 swap34 / B 不交换格式规则见 §10/§13
透明像素占 index 0 不参与聚类Tool Policy(chk.rs quantize_rgba)格式不强制 index 0 必须透明; encoder 自由
median-cutTool Policy换成 k-means、octree 等同样合法
Alpha 降权(dist2 中 da/2)Tool Policy(L586)距离度量是 encoder 内部选择
4bit "真 16 色 RGBA 不限灰度"格式允许(样本恰为灰度)encoder 可用彩色,保留色相已被测试固化
量化算法选择Encoder 自由但量化有损,必须通过 Round-trip Test C 如实报告

21. 编码管线(PNG→CHK)

每步标注【F】=格式强制 /【T】=工具策略:

#步骤输入输出类别
1加载 PNG → RGBA8888PNG 文件w×h×4 RGBA【T】
2Alpha AnalysisRGBA透明像素统计【T】
3Quantization(median-cut 等)RGBA≤k 代表色【T】算法自由
4Palette Generation代表色palette[0..k]【T】;index0 透明是策略
5Index AssignmentRGBA+palette索引缓冲【T】最近邻自由
6索引合法性索引0..k−1 校验【F】
74/8bit Index索引索引字节流【F】4bit &0x0F
8Nibble / Block 转换字节流on-disk 像素区【F】(A: swap34+行主序;B: pack_block)
9Header 生成家族/尺寸/位深/名字0x40/0xA0 头【F】字段布局+【T】unknown 取值策略
10Offset/Size 计算各区大小0x08/0x0C/0x10 等【F】公式见 §7
11Pointer/Tail 更新meta_base 等0x28/0x2C/meta[0x08]/tail【F】A 公式 / B 常量
12Validator完整字节流PASS/错误码【F】
13输出 CHK—.chk 文件—
  • 家族/位深选择(步骤 9 前):目标 Family A 必须 W==H;Family B 必须 2 的幂 + 块对齐(§7.3),不满足则拒绝(rejects_non_square / rejects_non_pow2_block_dims 测试)。
  • 参考编码入口:from_rgba(A)、from_rgba_block8(B 8bit)、from_rgba_font(B 4bit)→ serialize()。

22. 解码管线(CHK→PNG)

CHK bytes
 → Validate Header(§5.2 强校验;失败即 CHK_ERR_*)
 → Detect Family(§3 检测顺序:A 几何试探 → B 0x3C==0xA0)
 → Bit Depth(B: 0x44&0x08;A 恒 8bit)
 → Dimensions(A: isqrt 反推;B: 2^0x42 × 2^0x43)
 → Locate Pixel Region(palette_offset 公式反推)
 → Block 展开 unpack_block(B)/ 行主序+swap34(A)
 → Nibble 拆分(仅 B 4bit)
 → Index 缓冲(row-major,1B/texel)
 → Palette 查表(§18)
 → RGBA 缓冲
 → PNG 编码
  • Decoder 只读:不修改文件任何字节;原始 header/meta/tail 原样保存在文档模型中。
  • 参考解码路径:ChkFile::parse → to_rgba() → PNG writer(本仓库 preview 管线对全部样本验证尺寸一致,E006)。

23. 序列化与字段变更策略(Serialization / Field Mutation Policy)

23.1 序列化总规则(与 chk.rs 逐行对齐)

  • serialize() 按 kind 分派:A→serialize_square,B8→serialize_block(8),B4→serialize_block(4)。
  • 序列化前自检:palette 长度必须 ==256/16(否则 expected N palette colors, got M);pixels 长度必须 ==W*H(否则 expected N pixels, got M)。
  • 输出顺序恒为:header → pixel region → palette → (meta) → tail;总长 == header[0x08](byte-exact 的前提)。
  • 名字写入:name_to_8(L788–794),UTF-8 字节截断到 8,不足补 0;A 写 meta[0x18..0x20],B 写 header[0x58..0x60]。

23.2 Mutation 表(每字段 Read/Preserve/Recalculate/Regenerate)

术语:preserve=原样保留输入文件的字节;recalc=按公式重算;regenerate=从文档模型重建(重建模式下的固定表/恒定值)。unknown 字段一律 preserve,NEVER 自动修改。括号内为典型触发场景。

字段Family AFamily B依据/证据
magic (0x00)preservepreservebyte-exact(E002)
unknown_04/18/1C/24/38preserve(NEVER 修改)preserve(NEVER 修改)观测恒 0(E013)
0x08 file_sizerecalc(任何保存)recalcserialize L485/L546
0x0C / 0x10 data_endrecalcrecalcL486–487/L547–548
unknown_14=7 / unknown_20=6preservepreserve观测恒定(E013);重建时 regenerate=7/6
0x28 / 0x2Crecalc(=meta_base+0x10)preserve(重建 regenerate=0x50)L488–489(A);B 观测恒 0x50(E013)
unknown_30 / unknown_34preserve(NEVER 修改;无生成规则)preserve(同左)§8.1;⚠️ 重建时 chk.rs 写家族代表值(E005 重建头逐字段=原文件即以此验证)
0x3C family_flagpreserve(观测 0)preserve(重建 regenerate=0xA0)E013
B 0x40 typen/apreserve(重建 regenerate=0x00)patch_block_header
B 0x41 bits/4n/arecalc(尺寸/位深变化时)patch_block_header L291
B 0x42 log2W / 0x43 log2Hn/arecalc(尺寸变化时)L292–293
B 0x44 depth_typen/arecalc(位深变化时)L294
B 0x45=0x01 / 0x46=0x04n/apreserve(重建 regenerate)patch_block_header;观测恒定
B 0x47n/arecalc(随位深联动 0x25/0x45)L297
B 0x48/4C/50/54(56/32/40/40)n/apreserve(重建 regenerate)观测恒定(E013)
B 0x58 namen/apatch(name_to_8)L536–537
B 0x60..0x9F(除 0x98)n/apreserveE015 观测全 0
B unknown_98n/apreserveE015(46/47=0x01)
A 0x28/0x2C 同上、meta[0x00]=0x40preserve(重建 regenerate=0x40)n/adefault_meta
A meta[0x08]recalc(=meta_base)n/aL490
A meta[0x18] 名字patch(name_to_8)n/aL476
A meta 其余(0x04、0x0C、0x10、0x14、0x58…)preserve(NEVER 修改)n/aE012
pixel regionregenerate(索引内容来自文档模型)regeneratepack_block/直拷
paletteregenerate 或 preserve(只改色即 preserve 其余项)regenerate 或 preservepalette_edit 测试(E009)
tail[0x00]/[0x04](=0x28/0x2C)preserve(从原 tail;重建 regenerate=0x28/0x2C)preserve(重建 regenerate)恒定,两家族 tail[0..1]=0x28/0x2C 不变
A tail[0x08..0x18]recalc(meta_base+{0,8,0xC,0x10,0x14})n/aL496–500
B tail 全表n/apreserve(重建 regenerate=固定值表)default_font_tail

三类典型场景:

  1. 只改调色板:pixel/tail/头全 preserve,仅写调色板 4B×改动项(§24 精确保留模式;E009:索引不被重量化)。
  2. 改尺寸/位深(Family B):recalc 0x08/0x0C/0x10/0x41/0x42/0x43/0x44/0x47,regenerate 像素区/调色板/tail;其余 preserve 或按固定表 regenerate(§25 重建模式;E008 位深转换往返测试)。
  3. 从零生成(新文件):无 preserve 来源,unknown 字段按家族代表值生成(A 头 default_header:0x14=7、0x20=6、0x30=0x042C4100、0x34=0x056DC9F8;B 头 default_font_header:0x28/0x2C=0x50、0x30=0x0AAE1BA0、0x34=0x08BF08CC、0x3C=0xA0、0x40..0x54 按 patch_block_header+常量表、0x48..0x54=56/32/40/40;A meta default_meta=0x40;tail 按家族表)。生成结果对 unknown 字段不保证与"原版工具"一致(0x30/0x34 语义未知),须在输出标注。字节级锚点:default_header/default_meta/default_tail 的常量字节以 §29.1 三向量的实测 dump 为准(变量槽位按本表 recalc 规则回填)。

24. 精确保留模式(Exact Preservation Mode)

定义:打开 → 解析 → 只改某字段 → 只写必要字节 → 未知字段原样 → 保存。文件其余部分逐字节不变。

流程:

  1. 读入全部字节;Parser 解析并保存 raw_sections(header/meta/tail 原始字节);
  2. 编辑器只修改 ChkDocument 的逻辑字段(如 palette[17]);
  3. 保存时对目标字段就地 patch:只写被修改的字节区间,其余字节从原文件拷贝;
  4. Validator 重跑(§26)确认一致。

理想结果示例(只改 Palette[17],Family A):

  • 改动字节:file[palette_offset + 17*4 : palette_offset + 17*4 + 4] 共 4 字节(新 RGBA);文件其余全部字节(263352−4 字节)逐字节不变,包括 header 全部、0x30/0x34、meta、tail、像素区全部索引;文件长度不变,无需重算任何指针(palette 大小不变,布局不变)。

实现要点:

  • 保存 raw header / raw meta / raw tail,序列化时整体拷贝、仅 patch 已知可变字段(chk.rs serialize 即此模式:头只 patch 0x08/0x0C/0x10/0x28/0x2C,meta 只 patch 0x08 与名字,tail 只 patch A 的后 5 项);
  • 禁止在精确保留模式下触发重量化/重新编码像素(E009:改调色板不得改变索引);
  • 未知字段(unknown_30/34、meta[0x04]、0x98 等)原样透传,不做任何"规范化";
  • 若只改了不影响布局的字段,输出长度必须 == 原长度(parse_all 系列断言的长度不变性即此检查)。

25. 重建模式(Rebuild Mode)

定义:不基于原文件字节,而是从 ChkDocument 重新生成整份文件。适用:改尺寸 / 改位深 / 转家族 / 重新量化。

流程:

ChkDocument
 → Generate Pixel Region(pack_block / swap34 行主序)
 → Generate Palette(量化产物或文档模型中的调色板)
 → Generate Metadata(A: default_meta + 名字;B: 无)
 → Calculate Offsets/Sizes(§7 公式)
 → Update Pointers(§8 公式)
 → Generate Tail(A: 0x28/0x2C + meta_base 序列;B: 固定值表)
 → Serialize(header → pixels → palette → meta → tail)
 → Validate Output(§26)
 → CHK bytes
  • 入口:from_rgba / from_rgba_block8 / from_rgba_font(重建即"RGBA→量化→重建布局")。
  • 与精确保留的边界:重建后的文件不承诺与原文件逐字节一致:unknown 字段取家族代表值、Family A 的 meta 非零观测字段(E012)在 default_meta 中为 0、0x30/0x34 无生成规则。重建正确性的判据是:Validator 通过 + parse 回读后逻辑内容(尺寸/位深/kind/调色板/索引)一致(E008/E009 测试模式)。 实现能力分级(Implementation Scope,v2.3 增补)——明示各能力层级在当前证据下的可靠度,防止"能生成文件"被误读为"与原生等价":
Level能力可靠度依据
L1Parser(CHK→ChkDocument)✅ 可直接实现本文即为规范;48/49 byte-exact
L2Decoder(CHK→RGBA)✅ 可直接实现E003/E031 像素级 diff=0
L3Validator✅ 可直接实现§26 检查项 + §30 往返测试
L4Exact Preservation 编辑器✅ 可直接实现§24;E009 索引不变性
L5Family B Encoder(RGBA→B8/B4)✅ 证据充分E008 位深转换往返;语料 byte-exact
L6Family A Encoder(RGBA→A)⚠️ Rebuild/兼容模式0x30/0x34 与 meta 非零字段无生成规则(E012/E028);产物过 Validator 但不承诺与"原版工具"逐字节一致
L7改尺寸后"原生等价"重建🔴 不支持unknown 字段平台语义未证实;作此声明将超出当前证据
L8跨家族转换(A↔B)⚠️ 仅结构层可行像素层可转,语义字段按 L6 处理;宣称需实机验证

后续验证待办(v2.3 登记):①独立实现交叉测试——仅凭本文用第二语言实现 Parser,对 49 样本与 chk.rs 输出逐字节比对(即开头目标句的终审);②FONTSJP1 byte-exact 缺口补齐(§29 表②)。

  • 模式选择规则表:
用户操作模式原因
改 1..N 个调色板色Exact Preservation布局不变;索引必须不动(E009)
改名字Exact Preservation仅 patch meta[0x18](A)/ 0x58(B)
改像素内容(同尺寸同位深)Exact Preservation(仅重写像素区)或 Rebuild同尺寸下布局不变
改尺寸Rebuild偏移/tail/0x42/0x43 全部变化
改位深Rebuildpalette 大小、0x41/0x44/0x47、块几何变化(E008)
Family A ↔ B 转换Rebuild头大小/布局完全不同
重新量化(换调色板)Rebuild索引与调色板同时重建

26. 校验器规范(Validator Specification)

输出格式(样例):

CHK VALID
  Family:        A (PS2 square, 8-bit)
  Bit Depth:     8
  Resolution:    512 x 512
  File Size:     263352 bytes (0x000404B8)
  Magic:         PASS (V01)
  Header FileSize: PASS (V03)
  Data End:      PASS (V04, V05)
  Family Flag:   PASS (V06)
  Geometry:      PASS (V07, V08, V09)
  Pixel Region:  PASS (V11)
  Palette:       PASS (V12)
  Block/Nibble:  N/A (Family A)
  Metadata:      PASS (V21)
  Tail:          PASS (V18)
  Pointers:      PASS (V19)
  Constants:     PASS (V20)
Validation: 24/24 PASS

失败时每项输出 FAIL (Vxx, CHK_ERR_*)。

检查项(24 项):

#名称判定规则错误码级别
V01Magic前 4 字节 == 78 65 74 00CHK_ERR_INVALID_MAGICERROR
V02Actual Size长度 ≥ 0x40+0x1CCHK_ERR_TRUNCATEDERROR
V03Header FileSizele32(0x08) == 实际长度CHK_ERR_INVALID_SIZEERROR
V04Data Endle32(0x0C) == 长度−0x1CCHK_ERR_INVALID_SIZEERROR
V05Data End 复本le32(0x10) == le32(0x0C)CHK_ERR_INVALID_SIZEWARNING
V06Family0x3C==0(A,且方形试探通过)或 ==0xA0(B)CHK_ERR_UNSUPPORTED_FAMILYERROR
V07W/H > 0A: isqrt 反推 >0;B: 2^(0x42&0x0F)×2^(0x43&0x0F) >0 且 == texelsCHK_ERR_INVALID_GEOMETRYERROR
V08Pixel SizeA: (palette_offset−0x40)==WH 且为完全平方数;B: 像素区字节数==block_rowsblock_cols*128CHK_ERR_INVALID_GEOMETRYERROR
V09Palette SizeA: 1024;B: 1024(8bit)/64(4bit),且与 0x41/0x44 判定一致CHK_ERR_INVALID_PALETTEERROR
V10Palette Offsetpalette_offset == 长度−0x1C−(meta?)+公式值,且 > header_sizeCHK_ERR_INVALID_OFFSETERROR
V11Pixel Offset==header_size(A:0x40 / B:0xA0)CHK_ERR_INVALID_OFFSETERROR
V12Block AlignmentB: W%block_w==0 且 H%8==0(chk.rs L348 报 block .chk size not block-aligned)CHK_ERR_ALIGNMENTERROR
V13Nibble AlignmentB 4bit: 每行 32 texel == 16 字节,无奇数截断CHK_ERR_ALIGNMENTERROR
V14Index Bounds所有索引 < nb(to_rgba 的 fallback 只是宽容行为,不是格式保证)CHK_ERR_INVALID_OFFSETWARNING
V15Pow2 DimsB: W/H 为 2 的幂(validate_block_dims L267)CHK_ERR_INVALID_GEOMETRYERROR(编码侧)
V16Min DimsB: 8bit W≥16 / 4bit W≥32 / H≥8(L272)CHK_ERR_INVALID_GEOMETRYERROR(编码侧)
V17Tail Boundstail_offset == 长度−0x1C ≥ palette 末尾CHK_ERR_INVALID_OFFSETERROR
V18Tail TableB: 7 项 == {0x28,0x2C,0x3C,0x48,0x4C,0x50,0x54};A: 后 5 项 == meta_base+{0,8,0xC,0x10,0x14} 且首两项 == 0x28/0x2CCHK_ERR_POINTER_MISMATCHWARNING
V19Pointer BoundsA: 0x28/0x2C ∈ 文件范围且 == meta_base+0x10(E019);meta[0x08]==meta_base;B: 0x28/0x2C==0x50CHK_ERR_POINTER_MISMATCHWARNING
V20Constant Fields0x14==7、0x20==6;B: 0x40==0、0x45==0x01、0x46==0x04、0x47∈{0x25,0x45}、0x48==56、0x4C==32、0x50==40、0x54==40CHK_ERR_CONST_FIELD_MISMATCHWARNING
V21Metadata BoundsA: meta 区 [palette_offset+1024, +92) 恰在 tail 之前;meta[0x00]==0x40CHK_ERR_INVALID_OFFSETERROR
V22No OOBpalette_offset + palette_size (+meta+tail) == file_size,任何派生区不越界CHK_ERR_TRUNCATEDERROR
V23Name Encoding名字区 8 字节可安全按 NUL 截断(≥1 个 NUL 或恰 8 字节)CHK_ERR_NAME_ENCODINGINFO
V24Bit Depth 一致性B: 判定①(0x44&0x08)与判定②(0x41)与调色板大小三者一致;0x44∈{0x04,0x08}CHK_ERR_UNSUPPORTED_BIT_DEPTHERROR

对照说明:chk.rs 现有实现只做 §5.2 的 4 项强校验 + 几何/对齐校验;V05/V14/V18/V19/V20/V23/V24 是本规范对 Validator 的要求,读取端可以宽容、写入端必须执行(与 TxdError::Chk 错误信息风格保持一致)。

27. 修复规范(Repair Specification)

Strict 模式:任何 ERROR 即拒绝打开/保存(默认)。 Repair 模式:只修复有明确规则的项目,其余原样并警告。

类别项目处理
✅ 可自动修复header file_size (0x08) 与实际不符recalc 后修复(有公式)
✅ 可自动修复data_end (0x0C/0x10) 错误/不一致recalc
✅ 可自动修复Family A tail 后 5 项、meta[0x08](meta_base 序列可由长度公式重推)recalc
⚠️ 只警告不修offset 越界(0x28/0x2C 指向文件外)不许盲修——可能指向未知子结构;必须警告人工确认
⚠️ 只警告不修tail 常量表不匹配(B)按证据可重写为固定表(default_font_tail),但先警告:可能是有意变体
⚠️ 只警告不修0x14/0x20 等常量字段不匹配警告 + preserve(语义未知,可能承载版本信息)
❌ 拒绝palette 长度不足(< nb*4)ERROR:CHK_ERR_INVALID_PALETTE,不修
❌ 拒绝magic 错误ERROR:CHK_ERR_INVALID_MAGIC,不修
❌ 拒绝像素区字节数 ≠ W*H / texelsERROR:CHK_ERR_INVALID_GEOMETRY,不修
❌ 拒绝unknown 字段NEVER 自动修改(无语义,修了等于制造新文件)

28. 错误分类(Error Classification)

级别定义:

  • ERROR:文件不可能按规范解析/写入,必须中止。
  • WARNING:偏离观测规律但仍可处理,输出警告继续。
  • INFO:纯提示(如名字截断)。

错误码表(与 chk.rs 现有错误信息风格对照,TxdError::Chk(String)):

错误码触发chk.rs 现有对应信息
CHK_ERR_INVALID_MAGIC魔数错bad magic (expected b"xet\0")
CHK_ERR_TRUNCATED文件过小/像素区不完整file too small (N bytes)
CHK_ERR_INVALID_SIZE0x08/0x0C/0x10 与实际不符;B 几何与 texels 不符header file size N != actual M / header data end N != M / block .chk geometry inconsistent: header WxH, texels T
CHK_ERR_INVALID_OFFSETpalette/pixel/meta/tail/指针越界(Validator 层新增)
CHK_ERR_INVALID_PALETTE调色板长度不符expected N palette colors, got M
CHK_ERR_INVALID_GEOMETRY非方形(A 编码)/ 非块对齐 / 尺寸非法chk geometry must be square for the 8-bit format; got WxH / block .chk size not block-aligned (WxH, block Bx8) / chk 尺寸需为 2 的幂(…);请求 WxH / chk 尺寸过小:…
CHK_ERR_UNSUPPORTED_FAMILY0x3C 非法且非方形unrecognized .chk pixel layout (pixel region N bytes)
CHK_ERR_UNSUPPORTED_BIT_DEPTH0x44/0x41/调色板大小互相矛盾(Validator 层新增;parse 现按 0x44&0x08 宽容二分)
CHK_ERR_ALIGNMENT块/半字节对齐失败block .chk size not block-aligned (WxH, block Bx8)
CHK_ERR_POINTER_MISMATCHtail/0x28/0x2C/meta[0x08] 与公式不符(Validator 层新增)
CHK_ERR_CONST_FIELD_MISMATCH0x14/0x20/B 常量字段不匹配(Validator 层新增)
CHK_ERR_NAME_ENCODING名字区异常(Validator 层新增;INFO 级)
CHK_ERR_BUFFER_TOO_SMALL输入 RGBA 不足 wh4rgba buffer too small
CHK_ERR_PIXEL_COUNT_MISMATCHpixels 长度 ≠ w*hexpected N pixels, got M

29. 测试语料(Test Corpus,SHA-256)

SHA-256 计算方式:PowerShell Get-FileHash -Algorithm SHA256,2026-09-04 对全部 49 个 .chk(根目录 4 + TXD\ 45)+ 参考图实测,禁止编造。

PixelSize/PaletteSize/MetaSize/TailSize 列的推导过程(非猜测):

  • Family A:PixelSize = file_size − 0x40 − 1024 − 92 − 28;PaletteSize=1024;MetaSize=92;TailSize=28。核验:263352−64−1024−92−28 = 262144 = 512² ✔。
  • Family B:先由 header 0x44/0x42/0x43 读出 bits/W/H,得 nb(8bit=256/4bit=16);PaletteSize = nb*4;MetaSize = 0;TailSize = 28;PixelSize = file_size − 0xA0 − nb*4 − 28;并交叉核验 PixelSize × (8/bits) == W×H(texels)。脚本化全量核验结果 FORMULA_CHECK_ALL_OK=True(49/49 通过,E017)。
Filename(语料内相对路径)SHA-256PlatformFamilyWidthHeightBitDepthPixelSizePaletteSizeMetaSizeTailSizeDecode StatusEncode StatusRoundtrip Status
FONTSJP.CHK926a9d5d1a3d50c1c13d5eb80b476cd3003a28a15fcfc0ac4f13a022d5727ccePSP(推断)B451210244262144(texels=524288)64028OKOK(serialize)OK(byte-exact)
FONTSJP1.CHK07b7363812006756d6a223259dc9947ba79fc78e3e78a5ab8bd89fa481c1ace9PSP(推断)B85125128262144(texels=262144)1024028OK(结构核验①)未逐样本验证②未逐样本验证②
LOADSC0.CHKc93ce323647c96d47bf29e9b0cd17f641134984d149665a0b06f42cc4260d324PS2A512512826214410249228OKOK(serialize)OK(byte-exact)
LOADSC3.CHK3f1badbaaf3dd0d9ed94d6d2af980a29c7f25ffb2b3262d38a675f8361542f12PS2A512512826214410249228OKOK(serialize)OK(byte-exact)
TXD\CAPCOM.CHKe476dac66e7ee02ba010f5c96b6b3114e81e139c1d75472b26ed7bb4cb62ad83PSP(推断)B85125128262144(texels=262144)1024028OKOK(serialize)OK(byte-exact)
TXD\INTRO1.CHK6d241c0c7a3b7862b6c3f7961cfd9483799c8ca4f9d6192b660bc72e659b837bPSP(推断)B45125124131072(texels=262144)64028OKOK(serialize)OK(byte-exact)
TXD\INTRO2.CHK45eb9bf128eddfd0e05c154c5d1a5217a5cdd2e819191ea58170ecffc91ac6e6PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\INTRO3.CHKaa49d72d65b2cf48a17bc41c20c27b3899b726094227a350a2524e644eae9c54PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\INTRO4.CHK3196dfdbfafb90033a2d593ca3ab9636d9265882c7219863859619687e9241a8PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC0.CHK959e6ad2d9f1fb3b59b3d739df8547529a1fa46409b4ffa34875bcb20c6cc4faPSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\LOADSC1.CHKf8369a8e9fdabb95841cf3b7d16d03fa8485c8d9e074da7f9137eee469da832cPSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\LOADSC10.CHKff7158b8acdf7387bd641c87af642f749354071b111647cfa2abeb1721be9ff5PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC11.CHK16522252d0c12784dd9c5c8ce894af0d1e3aacfb4f9c897605b2fc6a9e1ec05aPSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC12.CHK0bca0b1b3200dd0b805efaae4218ad311820194a191adcb3873c06c601d9eed4PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC13.CHKcf43e50cecebc6ca37148e5ba2708e9604f7fa5a5c949a54b8912e907dbdb249PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC14.CHK6c8dce3a0be54c68186f51d08519c06333ebf1add115237042e8ac5a08c97984PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC15.CHKc266492d3fb746bd32ecaa3c02499644c1af6a943a9f2762c15aa0c81ed4b6ffPSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC16.CHK09017cd5460f5203e6d23f7362a168bfc3dc45e8651a95b682bf18ce8fc039abPSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC17.CHKff383c7f28c70430395819dc15a3c7c18428a8da5478ea3b7b63d5d376dbeedaPSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC18.CHK8de1608bf74c95cefd5bddd4d9593bbe40abf314811328829e46aa3ef2a30666PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC19.CHKfbcd3aed8d90c383eb63426d3444cad951e7a2f6258e82a8ee46a3db00c203ecPSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC2.CHK73cae31bdcd82fe5e36b67bc410cf9a2b8985e5e0f533e1cff8f09a1723a45baPSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\LOADSC20.CHK4a5fa7e361b26417f5ca5ec7beb525742f383c77424c0af0215e1761b369c980PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC21.CHK2ef16f6c777d35ac0358888bf099e0f38061e1ee14be111869da9d9a1ff11b63PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC22.CHK677ed2cf5c8cd775ba11f7d8d4ded7bbbfd32700732594bc182083696559236ePSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC3.CHK753f792acd772b563d7e31a7b6a0debc9046de2549fef44a227789d538e970e8PSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\LOADSC4.CHK4da87275215ab230432f468eb7ab26ee69c8e424c43c7bb6b07b6b9da0e74687PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC5.CHK77f2750035e3fd463c32d984347117fc5e19093d9787035201d4a32e0b521f60PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC6.CHK8ab0515da0730d1964437b8d151a38d363ef4038732bd887a40b32724144e484PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC7.CHK20d730fe857e9ae24ce6c426690fc59d0aaf07fece81ee74436e4c06dbea736fPSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC8.CHK915beadbf5743ea9631ebac64173d4419ad5c4717c7588ae5a741564ec71e254PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\LOADSC9.CHKef9cd5a1388808f6cb47daa9c2ffc37a48fa3dc36797a6ca77b48d9e65b12584PSP(推断)B45125124131072(texels=262144)64028OK未逐样本验证②未逐样本验证②
TXD\MEMCARD.CHK4f80fad01cdd1b32f504767720a5c6baf4c33cc34932fc3484eb12baed1cd0e6PSP(推断)B4512256465536(texels=131072)64028OK未逐样本验证②未逐样本验证②
TXD\MPLOAD0.CHKf1b929024c790f893ba09473481a51adf068ad39d80e8d537265049479664cf5PSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\MPLOAD1.CHKd6132a0251c9aab508a91f1bbcf22126739e5870ddc5658e85d3d2e75d13ece1PSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\MPLOAD2.CHKc9c3a2e1d254a3420d2a29fe05ba04e42a6f2ae4560834a8b7e33fcb722bf32bPSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\MPLOAD3.CHKd8d3e1633185b13179d2e8bc750a60f87c831440f4f58d6b87fb55511b5de1c8PSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\MPLOAD4.CHK26047370ba73d9d0575d035d498bdef98e854fc12e4088ff2680f48b7c8f22a7PSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\MPLOAD5.CHK7e159d87e14c14c40408980aa7251a88b8495e414dcf85641e0c902b87e39899PSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\MPLOAD6.CHKa805d9f581e45a7543d98cd9d26b89c10dab2d4c6fad1c972b866a431dc269efPSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\MPLOAD7.CHKe00ea256b9e3ae429ebe3b19bc302745a71558cdcb51c9b3c1af040be6e0bb7bPSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\NEWS.CHK473606dd49e4826cb2865c4ba649fe30a22694552614d585f124d5125082afd7PSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\NEWS2.CHKe446521f64e64c7231cdd61d88228521da367029d9e2a6f3157b2180e277e71cPSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\NEWS3.CHKe9f5b61b984747e2fd17ce41ad7b8da4fc85ea50007b16740d3c0ab00d27c4e9PSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\SCEJLEGALJ.CHK8d3cf1ed559c975615d332d50a40943da8c52cfb1fc9b665a0ee1391d23564fbPSP(推断)B85125128262144(texels=262144)1024028OK未逐样本验证②未逐样本验证②
TXD\SPLASH1.CHK4666dba37ec2584a6e07a5e243911d845e872388de96a6eb532b20a032bf3d64PSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\SPLASH2.CHK7705165420d349ea65651cd3ca77608b88759dfcceec8b45e06eff861148114aPSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\SPLASH3.CHKc93f0044356d08490685c2e9c1ac89a0ddbcb7efbafe0319c226fa8f56555e89PSP(推断)B85122568131072(texels=131072)1024028OK未逐样本验证②未逐样本验证②
TXD\WARNING.CHKdfe6547b0f7f739fde4eac65e71c75f84e672702391cd78820b2b55d2897249bPSP(推断)B85125128262144(texels=262144)1024028OK未逐样本验证②未逐样本验证②

参考图(SHA-256 实测):

文件大小SHA-256用途
loadsc0.png95472bfb3ea4900f0907046042bf0bc2b763b396aa111a5ec46b975c5581e15e36406LOADSC0.CHK 解码 diff=0 的参考(E003)
fontjp.png135439aa81cb65e8f59057072fd1650375667c565a4f58e3eea56e0f5e3445651dc3dcFONTSJP.CHK 参考图
decode_loadsc0.png30886913d40b14a27a0a59cea1fad779519e41a74acaa5c85b1175dda322dfba883187解码过程产物

注①:FONTSJP1.CHK 未被 Rust 测试套件覆盖(parse_all_family_b_in_dir 只扫 TXD 目录);其家族/尺寸/位深/名字(B8 512×512 "fontjp")由本次整理对 header 字段(0x3C/0x41/0x42/0x43/0x44/0x58)的结构核验得出,且文件长度被 §7.2 公式精确解释(E017)。 注②:"未逐样本验证"指未为该样本单独运行/记录往返测试。补充事实:批量测试 parse_all_family_b_in_dir(chk.rs L877–879)的断言体实际对 TXD 目录每个 .chk 断言 serialize() 输出逐字节等于原文件且长度不变,即该测试每次运行即覆盖目录内全部样本的 byte-exact(附录 C-5 记录该口径差异)。既有文档的正式口径为"ok=4 bad=0(LOADSC0/LOADSC3/CAPCOM/INTRO1)"(E001)。 注②-bis(2026-09-04 复测后覆盖口径核算,E032):原生 49 个中 48 个已有 byte-exact 实测:TXD 45 个 B(parse_all_family_b_in_dir 批量断言)+ 根目录 LOADSC0/LOADSC3(E001 诊断 + LOADSC0 专项测试)+ FONTSJP(roundtrip_fontjp_is_byte_exact);唯一无 byte-exact 实测的是 FONTSJP1(根目录、B8 512×512),其结构/几何/调色板/0x98 已由普查核验(E024–E030)。另:reverse_diag::byte_exact_roundtrip 对 TXD 目录的枚举使用大小写敏感扩展名过滤(== Some("chk"),实际文件为 .CHK 大写),故该诊断测试结构性只覆盖 4 个显式路径(本次实测 ok=4 bad=0,E032)。 注③:⚠️ 同名不同家族:根目录 LOADSC0.CHK(263352 B,Family A/PS2)≠ TXD\LOADSC0.CHK(132284 B,Family B/PSP 推断);LOADSC3 同理(E023)。引用样本时必须写明所在目录(根目录或 TXD\)。 注④:样本计数现状:TXD\ 实测 45 个 .chk;旧文档写"47 个 Family B",47 = 全语料 Family B 总数(TXD 45 + 根目录 FONTSJP/FONTSJP1),两数并存不矛盾,但"TXD 目录 47 个"的旧表述与现状不符(E022,附录 C-2)。 注⑤(普查复核,2026-09-04,E024–E030,附录 E):独立普查脚本对本表全部 49 行的 Width/Height/BitDepth/PixelSize/PaletteSize 逐项复核,49/49 与本表一致(几何/布局公式零误差,E026);Family 判定与 §3 规则一致(A=2、B=47)。测试状态刷新:B 各行 Decode Status 的"OK"由"结构核验"升级为"结构核验 + 全量几何公式核验(E026)";FONTSJP1 的 Encode/Roundtrip 仍未逐样本 byte-exact(覆盖口径见注②-bis),其余 48/49 均有 byte-exact 实测。语料完整性:全树另见 _chkprobe\ 下 FONTSJP/LOADSC0 两份副本,SHA-256 与原件一致,不计入样本(E024)。

29.1 最小测试向量(v2.3 新增;informative,供独立实现自检)

三个最小向量直接摘自语料原生样本(SHA-256 与 §29 表逐一致;排除 _chkprobe 副本与 _chk_verify 重建件)。任何语言的实现应能:①解析三文件得到相同的家族/几何/位深判定;②重新序列化得到逐字节相同输出。提取脚本:docs\tools\chk_test_vectors.py(可复现)。

向量 1 — Family A · 512×512 · 8bit(根目录 LOADSC0.CHK,263352 B,SHA-256 c93ce323647c96d47bf29e9b0cd17f641134984d149665a0b06f42cc4260d324)

0000: 78 65 74 00 00 00 00 00 B8 04 04 00 9C 04 04 00
0010: 9C 04 04 00 07 00 00 00 00 00 00 00 00 00 00 00
0020: 06 00 00 00 00 00 00 00 50 04 04 00 50 04 04 00
0030: 00 41 2C 04 F8 C9 6D 05 00 00 00 00 00 00 00 00
palette first 32B @ 0x40040: F6 D4 6B FF FA D6 6A FF FA DA 6E FF FA D6 66 FF FA DA 6A FF FA D2 66 FF FA D2 62 FF FA D6 6E FF
tail 28B @ 0x4049C: 28 00 00 00 2C 00 00 00 40 04 04 00 48 04 04 00 4C 04 04 00 50 04 04 00 54 04 04 00

期望:family=A、W=H=512、bits=8、0x28/0x2C=0x00040450=meta_base+0x10、0x30=0x042C4100、0x34=0x056DC9F8;解码 RGBA 与 loadsc0.png diff=0(E003)。

向量 2 — Family B 8bit · 512×256(TXD\LOADSC0.CHK,132284 B,SHA-256 959e6ad2d9f1fb3b59b3d739df8547529a1fa46409b4ffa34875bcb20c6cc4fa)

0000: 78 65 74 00 00 00 00 00 BC 04 02 00 A0 04 02 00
0010: A0 04 02 00 07 00 00 00 00 00 00 00 00 00 00 00
0020: 06 00 00 00 00 00 00 00 50 00 00 00 50 00 00 00
0030: A0 1B AE 0A CC 08 BF 08 00 00 00 00 A0 00 00 00
0040: 00 02 09 08 08 01 04 25 38 00 00 00 20 00 00 00
0050: 28 00 00 00 28 00 00 00 6C 6F 61 64 73 63 30 00
0090: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
palette first 32B @ 0x200A0: 00 00 00 FF 01 01 00 FF 04 01 00 FF 03 03 03 FF 08 01 01 FF 05 04 02 FF 0B 01 02 FF 05 04 05 FF
tail 28B @ 0x204A0: 28 00 00 00 2C 00 00 00 3C 00 00 00 48 00 00 00 4C 00 00 00 50 00 00 00 54 00 00 00

期望:family=B、W=512、H=256、bits=8、name=loadsc0、0x30=0x0AAE1BA0、0x34=0x08BF08CC、0x41=0x02(判定②)↔ 0x44=0x08(判定①)、0x42=0x09=log2(512)、0x43=0x08=log2(256)、0x47=0x25、0x48..0x54=56/32/40/40、0x98=0x01(原生观测;C-11:重建件为 0)、0x60..0x97=0。

向量 3 — Family B 4bit · 512×1024(FONTSJP.CHK,262396 B,SHA-256 926a9d5d1a3d50c1c13d5eb80b476cd3003a28a15fcfc0ac4f13a022d5727cce)

0000: 78 65 74 00 00 00 00 00 FC 00 04 00 E0 00 04 00
0010: E0 00 04 00 07 00 00 00 00 00 00 00 00 00 00 00
0020: 06 00 00 00 00 00 00 00 50 00 00 00 50 00 00 00
0030: A0 33 AE 0A 8C F2 BE 08 00 00 00 00 A0 00 00 00
0040: 00 01 09 0A 04 01 04 45 38 00 00 00 20 00 00 00
0050: 28 00 00 00 28 00 00 00 66 6F 6E 74 6A 70 00 00
0090: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
palette first 32B @ 0x400A0: FD FD FD F5 FF FF FF 67 00 00 00 00 FF FF FF BC 6D 6D 6D F9 FF FF FF 00 FF FF FF 8B FF FF FF E9
tail 28B @ 0x400E0: 28 00 00 00 2C 00 00 00 3C 00 00 00 48 00 00 00 4C 00 00 00 50 00 00 00 54 00 00 00

期望:family=B、W=512、H=1024、bits=4、name=fontjp、0x30=0x0AAE33A0、0x34=0x08BEF28C(FONTSJP 单独值组,E028)、0x41=0x01 ↔ 0x44=0x04、0x42=0x09、0x43=0x0A=log2(1024)、0x47=0x45;解码与 fontjp.png diff=0(E031)。

备注:B 头 0x40..0x5F 在同类(几何×位深)内逐字节恒定(E029);0x45=0x01、0x46=0x04 恒定;0x47 随位深(本语料 8bit=0x25 / 4bit=0x45);A 头 0x14=7、0x20=6 恒定,0x08/0x0C/0x10/0x28/0x2C 随文件重算(§23)。

30. 往返测试(Round-trip Tests)

测试路径无损性通过判据既有证据
测试 A:序列化往返CHK → Parse → Serialize → Binary Compare完全无损(未编辑文件)输出与输入逐字节相等(含长度)E001/E002:LOADSC0/LOADSC3/CAPCOM/INTRO1 正式记录 ok=4 bad=0;FONTSJP 专项测试;TXD 目录批量断言(注②);2026-09-04 复测通过(E032,覆盖口径 48/49 见 §29 注②-bis)
测试 B:解码往返CHK → Decode(RGBA) → Encode → Decode索引层无损;RGBA 层无损(当索引→颜色映射唯一时)两次解码 RGBA diff=0E003:LOADSC0 与 loadsc0.png diff=0(512×512);Family B 侧 to_rgba→pack/unpack 由测试 B 覆盖(from_rgba_roundtrip 等合成测试)
测试 C:编码往返(有损)PNG → Encode → Decode → PNG量化有损记录 Pixel Diff / Max Channel Error / Different Pixel Count;透明像素(A==0)必须精确往返(index 0 策略)尚无正式记录的逐样本报告表——作为 Validator 附属输出(TBD:需要工具实现输出报告)
  • 测试 A 是格式规范正确性的最终判据("重新写回来和原文件逐字节一致",见附记);测试 B 检验 decode/encode 互逆(swap34 自反、pack/unpack_block 互逆,E007/E002);测试 C 的有损指标只描述量化质量,不影响格式合法性。
  • 2026-09-04 普查轮实测(E032):cargo test -p txd-format -- --include-ignored(workdir 为本仓库根目录)→ 75 tests:74 passed / 1 failed。唯一失败为 txd::fonts_diag::dump_fontgif(读 FONTS.TXD 时 panic:txd.rs:248 index out of bounds: len 1049029, index 16804789),属 TXD 侧诊断测试自身缺陷,与 CHK 无关;全部 CHK 测试通过(含 parse_all_family_b_in_dir、rebuilt_header_matches_real_family_b、preview_dims_match_for_all_family_b、from_rgba/palette/depth 往返等)。byte-exact 诊断复测 ok=4 bad=0(LOADSC0/LOADSC3/CAPCOM/INTRO1)。附带发现:重建 B 文件(_chk_verify)的 0x60..0x9F 区域为 0(0x98=0),而原生 46/47 为 0x98=0x01,rebuilt_header_matches_real_family_b 的槽位比较不含该区域(附录 C-11)。

31. 证据与置信度体系(Evidence / Confidence)

9 节原文:置信度【✅ 原文保留】

项目置信度依据
魔数、容器头 0x08/0x0c/tail✅ 确证解析实现 + LOADSC0/LOADSC3/TXD/*.chk 样例字节
Family A 方形 8bit、bit3/4 交换✅ 确证实现 + LOADSC0 正常显示
Family B 块状、log2 几何、0x58 名✅ 确证实现 + 47 个样例
byte-exact roundtrip(全家族全 kind)✅ 强证据parse→serialize 对 LOADSC0/LOADSC3/CAPCOM/INTRO1 逐字节还原,ok=4 bad=0;2026-09-04 复测同结果,覆盖口径 48/49(E032)
调色板字节序(RGBA)+ 索引交换还原原图✅ 决定性解析 LOADSC0.CHK 的 RGBA 与参考 loadsc0.png 逐字节 diff=0(512×512)
Family A 方形 / Family B 块 的像素布局+索引交换✅ 强证据byte-exact 还原证实(非仅"看起来对");普查公式 49/49 零误差(E026)+ FONTSJP 全图 diff=0(E031)
Family A=PS2、Family B=PSP✅ 确证用户确认 LOADSC0/3=PS2;PSP 专用工具 GTAStoriesTex 仅认 Family B;且 byte-exact 证实两家族均为索引纹理(注:其中 Family B=PSP 为强推断链 E010,非直接观测;此处"确证"指该证据链在语料内成立)
PS2 vs PSP 的字节序/通道交换差异⚠️ 样例内未发现两家族各自解码颜色正确 + byte-exact;但无 PSP/PS2 成对图无法最终定论
Family A / B 头、meta、tail 各偏移字段✅(原第 10 节逐字段表)用 LOADSC0/LOADSC3 + TXD*.chk 逐字节比对
家族特有值(0x30/0x34)⚠️ 逐样本可变见 §5.1:A 两样本恒定,B 在两同名样本间不同(FONTSJP vs FONTSJP1);族内不恒定,语义未知
tail 偏移/地址表✅原生 CAPCOM/INTRO1/LOADSC0 逐项 byte-exact;普查 49/49 逐项精确匹配(E027)
布局/几何公式(§7/§8:尺寸、指针、调色板定位)✅ 确证(49/49,零误差)普查逐文件核验(E026/E027)——由 2 样本手工核对升级为全语料实证
B 位深双判定 + 调色板大小三方一致✅ 确证(47/47)普查交叉验证(E033);C-4 关闭
FONTSJP(B4 512×1024)解码正确性✅ 强证据fontjp.png 逐像素 diff=0(E031,Python 独立复现 chk.rs to_rgba)
0x30/0x34 排除面⚠️ 排除性实证:非内容哈希、非布局指针/大小;语义仍 ❓E028(哈希与指针对假设全语料 0 命中)

证据 ID 表(正文引用 (E0xx) 的定义)

ID类型内容支持的结论
E001Round-trip/样本验证byte_exact_roundtrip 诊断:LOADSC0/LOADSC3/TXD\CAPCOM/TXD\INTRO1 parse→serialize 逐字节相等,ok=4 bad=0容器/指针/布局公式整体正确
E002Round-trip/代码实现roundtrip_real_loadsc0_is_byte_exact、roundtrip_fontjp_is_byte_exact(chk.rs L886/L898)A/B 两条 serialize 路径 byte-exact
E003样本验证(决定性)LOADSC0.CHK 解码 RGBA 与 loadsc0.png diff=0(512×512)调色板 RGBA 通道序、A 布局、swap34 正确
E004多样本交叉parse_all_family_b_in_dir(L843–883):TXD 目录每个 .chk parse+断言(含 serialize 逐字节与长度不变,count≥40)B 布局普适
E005多样本交叉/代码实现rebuilt_header_matches_real_family_b(L1053–1111):重建 header 在 0x28/0x2C/0x30/0x34/0x3C/0x40/0x44/0x48/0x4C/0x50/0x54 逐字段==真文件(≥40 样本)B 常量表与重建公式正确
E006多样本交叉preview_dims_match_for_all_family_b(L1011–1050):全部样本 to_rgba→raster→decode 尺寸一致解码管线
E007代码实现/单元测试swap34_swaps_bits_3_and_4(L1299–1311):8 个向量 + 0..255 全域 involutionswap34 定义
E008Round-tripchk_depth_conversion_roundtrip(L1268–1290):CAPCOM 8bit→4bit→8bit 保持几何/kind位深转换(Rebuild 模式)
E009Round-trippalette_edit_roundtrip_preserves_indices(L1250–1265):改调色板不重量化索引精确保留模式(§24)
E010外部工具行为GTAStoriesTex.exe(PSP 专用)只认 Family BFamily B=PSP(⚠️ 推断链)
E011用户证言用户确认 LOADSC0/LOADSC3 为 PS2Family A=PS2(✅)
E012Hex/实测(本次整理)Family A meta 非零字节(0x04..0x07=0x00108249、0x0C=32、0x10=40、0x14=40、0x58=0x01);FONTSJP 调色板首 8 字节 FD FD FD F5 FF FF FF 67;LOADSC0 调色板首 16 字节 F6 D4 6B FF FA D6 6A FF FA DA 6E FF FA D6 66 FF§12/§18 观测;alpha 中间值存在
E013多样本交叉/实测(49 文件)0x30/0x34 恰 3 组(A:0x042C4100/0x056DC9F8;B 常见:0x0AAE1BA0/0x08BF08CC ×46;FONTSJP:0x0AAE33A0/0x08BEF28C);0x14=7、0x20=6、B 0x28/0x2C=0x50 全语料恒定§5.1/§8;unknown 字段策略
E014多样本交叉/实测(49 文件)tail 值集恰两组:B 47 文件={0x28,0x2C,0x3C,0x48,0x4C,0x50,0x54};A 2 文件={0x28,0x2C,0x40440,0x40448,0x4044C,0x40450,0x40454}(=meta_base 序列)§8.3 两表
E015Hex/实测(47 B 文件)header 0x60..0x9F 除 0x98 外全 0;0x98=0x01(46 文件)/0x00(FONTSJP1)§6.2 补充;unknown_98
E016Hex(原文档)附录 A:LOADSC0(A)与 FONTSJP(B)头部 dump,逐字段核对头部全部字段
E017公式推导+实测(49 文件)§7 公式解释全部文件长度(263352/263356/132284/131324/65788/262396),脚本核验 True§7 全部公式
E018代码实现chk.rs 行号对照(parse L101、serialize L148、unpack/pack L388/L416、quantize L591、default_* L728–778 等)本文与实现一致
E019字节实测(本次整理)A 的 header 0x28/0x2C = 0x00040450 = meta_base+0x10(LOADSC0/LOADSC3),与 chk.rs L488–489 写入一致;与原文表 "= data_end − 92"(=0x40440)不一致,差 0x10§8.2;附录 C-1(只记录,未改判定)
E020代码实现/负面测试rejects_bad_magic、rejects_non_square、rejects_non_pow2_block_dims(L925/L1148/L1293)§26 校验项、§27 拒绝类
E021外部参考代码png2chk.utf8.cpp(旧写入器)的 0x30/0x34 写入有误,以 chk.rs 为准§11 原文;unknown_30/34 策略
E022实测计数(本次整理)TXD\ 实测 45 个 .chk;全语料 Family B = 47(45+根目录 2);旧文档"TXD*.chk(47 个)"与现状不符附录 C-2
E023多样本交叉/实测同名不同家族:根目录 LOADSC0/LOADSC3(A, 263352B)≠ TXD\LOADSC0/LOADSC3(B, 132284B);FONTSJP(B4 512×1024)≠ FONTSJP1(B8 512×512)§29 注③;路径敏感性
E024语料清点(普查脚本 2026-09-04)普查语料 = 根目录 4 + TXD\ 45 = 49 原生;另有 _chk_verify\ 11 个工具生成件(非原生,单独标注)与 _chkprobe\ 下 FONTSJP/LOADSC0 两份副本(SHA-256 与原件一致,不计入样本)§29 表语料完整性;附录 E.0
E025字段恒定性普查(49 文件)通用头恒定字段 0x00/0x04/0x14=7/0x18/0x1C/0x20=6/0x24/0x38 49/49 无例外;0x3C A=0/B=0xA0 49/49;0x28/0x2C 家族内恒定(A=0x40450×2、B=0x50×47)§5.1/§6.1/§6.2 恒定列
E026几何公式全量核验(49 文件)§7 全部公式(容器 0x08/0x0C/0x10;A file_size=0x40+W·H+1024+92+0x1C、0x28/0x2C=meta_base+0x10、meta[0x08]=meta_base;B file_size=0xA0+block_rows·block_cols·128+nb*4+0x1C、pixel_region=br·bc·128、texels=W·H、0x28/0x2C=0x50)逐文件核验 49/49 OK、误差 0§7.1/§7.2/§8;§29 W/H/BitDepth 列
E027tail 全量核验(49 文件)A 2/2 匹配 meta_base 序列表、B 47/47 匹配固定表,逐项精确、无例外(刷新 E014 "46+2=48" 口径)§8.3
E028排除性实测(0x30/0x34)5 算法(crc32/adler32/sum8/xor32/fnv1a)×10 类对象(whole_file/header/pixel_region/palette/pixel+palette/name±NUL/filename 等)哈希与 14 项布局指针/大小对候选在全部 49 文件上 0 命中;v34−v30:A=0x014188F8(2/2)、B=0xFE10ED2C(46)/0xFE10BEEC(1)§5.1;unknown_30/34 排除面(语义仍未知)
E029字节级实测(47 B 文件,0x60..0x9F)除 0x98 外唯一非零:TXD\SCEJLEGALJ.CHK 0x60–0x61=4C 4A(10 字节名 SCEJLEGALJ 溢出 0x58 名字区 2 字节);0x98=0x01×46、FONTSJP1=0x00×1;0x40..0x5F 逐字节恒定:0x40=0x00、0x42=0x09、0x45=0x01、0x46=0x04、0x48=0x38、0x4C=0x20、0x50=0x28、0x54=0x28§6.2;附录 C-10;unknown_98 复核
E030调色板统计(49 文件,6544 条目)alpha≠255 共 297 条;含 alpha==0 条目的文件 25 个(FONTSJP + TXD\LOADSC4-22 + NEWS2/SCEJLEGALJ/SPLASH1-3);index0 共 11 种((0,0,0,255)×39 为主,LOADSC0(A)=(246,212,107,255)、FONTSJP=(253,253,253,245) 等);alpha≠255 去重值集 49 种(0..254,206 个中间值语料内未见)§18.2;unknown_alpha 表述收窄
E031图像级验证(Python 复刻 to_rgba)LOADSC0(A)↔loadsc0.png diff=0(262144 像素);FONTSJP(B4)↔fontjp.png diff=0(524288 像素);FONTSJP1↔fontJAP_1.png 尺寸不一致(512×512 vs 2048×1024)无法逐像素比对E003 的 Family B 侧独立复现;§19 解码管线
E032测试运行记录(2026-09-04)cargo test -p txd-format -- --include-ignored:75 tests,74 passed / 1 failed——唯一失败 txd::fonts_diag::dump_fontgif(txd.rs:248 panic,TXD 侧诊断测试,与 CHK 无关);CHK 全部测试通过;byte-exact 诊断 ok=4 bad=0;byte-exact 覆盖 48/49(唯一缺口 FONTSJP1,见 §29 注②-bis)§30;附录 C-12
E033位深交叉验证(47 B 文件)判定①(0x44&0x08)== 判定②(b[0x41]∈{2,1})== 调色板大小 47/47 三方一致;0x47 联动:0x25(8bit)×22、0x45(4bit)×25§6.3;C-4 全语料版关闭

32. 已知未知(Known Unknowns)

10.5 节原文:待确认点 / 下一步【✅ 原文保留】

  • header 0x14=7、0x20=6、Family A/B 的 0x30/0x34、Family B 0x45/0x46/0x47/0x48..0x54 等恒定常量:数值已确证,但确切语义(版本号?格式标记?)仍需对照更多已知家族/版本才能锁定。❓
  • tail 表在游戏/工具中的消费方式(是否逐项校验):未验证。❓
  • 家族间字节序/通道交换是否真有差异:目前两家族均按各自规则解码且颜色正确,样例范围内未发现需额外交换。⚠️

普查补充(2026-09-04,E024–E033,附录 E),上列各项在 49/49 全语料下的新状态:

  • 恒定常量类(0x14/0x20/0x3C/B 0x40..0x5F 逐字节恒定部分/tail 表):数值恒定性全部升级为 49/49 或 47/47 实证(E025/E027/E029/E033);但语义类未知一律不升级(仍是 ❓)。
  • 0x30/0x34:排除面实证增强,非内容哈希(5 算法×10 对象 0 命中)、非布局指针/大小(14 项候选 0 命中)(E028);语义仍 ❓。
  • unknown_98:47/47 复核维持(46×0x01 + FONTSJP1×0x00,E029);语义 ❓ 不变;另发现重建件 0x98=0 与原生 0x01 的系统性差异(C-11)。
  • 0x60..0x9F 零区:唯一例外 SCEJLEGALJ 0x60–0x61 为名字溢出(E029/C-10),"0x60..0x9F 除 0x98 外全 0"表述精确化。
  • Alpha:语料内实测 alpha≠255 条目 297/6544、去重值 49 种(0..254),"字节直出"在语料内一致成立,但只能记「语料内未见反例」,不能升 ✅(渲染端证据仍缺)。
  • tail 消费方式 / 平台通道交换 / PSP 归属:普查无新证据,维持原状(❓/⚠️,不升级)。

12 节原文:已完全掌握 vs 需外部资料【✅ 原文保留】

✅ 已完全掌握(能忠实编解码 + 还原)

  • 容器结构:魔数、0x08 大小、0x0c data_end、tail 偏移/地址表(header/meta/tail 逐字段表见原第 10 节,现 §5/§6/§8)。
  • 家族与 kind:PS2 Family A(方形 8bit · 索引 bit3/4 交换)、PSP Family B(块状 8bit/4bit · 8×16 / 8×32)。
  • 调色板RGBA 字节序、索引读取、块打包、4bit 半字节打包(低 4bit 在前)。
  • 硬证据:
    • parse → serialize 对 LOADSC0/LOADSC3/CAPCOM/INTRO1 byte-exact(ok=4 bad=0)。
    • 解析 LOADSC0.CHK 的 RGBA 与参考 loadsc0.png 逐字节 diff=0(512×512)。
  • 平台归属:Family A=PS2、Family B=PSP(用户确认 + PSP 工具只认 Family B + byte-exact)。

❓ 需外部资料才能进一步确证(非现有样例/资源可解,属于"格式之外"的解释性知识)

  • 常量字段 0x14=7、0x20=6、0x30/0x34、0x45..0x54 的确切语义(版本号?格式标记?),需要跨版本家族对照或官方/社区 spec。
  • tail 偏移表在游戏/引擎中的消费方式(是否逐项校验),需要运行时/strace 逆向。
  • PSP/PS2 通道交换差异的最终确认,需要同一张图在两平台的成对 .chk(样例范围内两家族 byte-exact 且颜色正确,未发现差异)。

结论:".chk 格式"本身(结构、布局、字节序、平台、调色板)已完全掌握并被 byte-exact + diff=0 证明。以上 ❓ 项是元数据语义 / 消费方层面的知识,不影响正确读写 .chk,需要不同来源的样本或运行时才能继续锁定。

v2.2 收敛表述(不改上文,仅限定口径):上段结论的准确范围是当前 49 样本语料。严谨说法:在当前样本范围内,.chk 的可读写二进制结构、纹理布局与调色板编码已足以实现可靠的编解码,并已通过 byte-exact(48/49)与像素级 diff=0 验证;0x30/0x34 语义、tail 消费方式、部分常量语义及 Family B 平台归属仍为 ❓/⚠️(§32)。未来出现新样本时以新证据为准。

未知项登记表(每项:观察/假设/证据/所需样本/工具处理)

未知项当前观察当前假设(仅假设)证据需要什么样本当前工具处理
unknown_04(header 0x04..0x07)两家族观测恒 0保留字段/版本E016任何非 0 样本preserve,NEVER 修改
unknown_14=7、unknown_20=6两家族恒为 7/6版本号或格式标记E016、E013跨游戏/跨版本家族样本preserve;重建按 default 写 7/6
unknown_30/unknown_34A 一组;B 46 文件一组;FONTSJP 单独一组;同家族同名样本间仍可变非内容哈希、非全局常量、非尺寸函数E013、E021、E005;普查排除面(E028:5 算法×10 对象哈希 + 14 项指针/大小对候选,49 文件 0 命中)同图跨版本对;官方生成器;更大语料preserve,NEVER 自动修改;从零生成时写家族代表值并标注
Family B 0x45..0x54 常量(01/04/25|45/56/32/40/40)跨内容/尺寸恒定;0x47 随位深联动格式标记/块几何提示E016、E005、E013跨版本样本preserve;重建按固定表 regenerate
Family A meta 非零字段(0x04=u32 0x00108249、0x0C=32、0x10=40、0x14=40、0x58=1)两个 A 样本完全一致;tail 后 5 项恰好指向 meta[0x00]/[0x08]/[0x0C]/[0x10]/[0x14]与 tail 指针序列对应的子结构(无更细证据不下结论)E012、E014更多 Family A 样本(非 512²)preserve,NEVER 自动修改
unknown_98(B header 0x98)47/47 复核:46 文件=0x01;FONTSJP1=0x00(唯一离群,E029);重建件全部=0x00(C-11)无E015、E029更多 B 样本(尤其非 TXD 来源)preserve
tail 表消费方式结构确证(E014);游戏是否逐项校验未知可能逐项用于定位子结构E014运行时逆向/strace按公式重建(A)/固定表(B)
平台通道交换差异样例内未发现需要交换的证据无差异E003、E013同一张图双平台成对 .chk不做任何通道交换
Alpha 全范围行为语料内 6544 条目中 alpha≠255 共 297 条、去重 49 值(0..254);未发现与"字节直出"矛盾的样本字节直出E012、E030(普查;206 个中间值语料内未见,只能记「语料内未见反例」,不升 ✅)游戏渲染端证据原样保留 alpha 字节
Family A 非 512² 支持仅有 512² 证据isqrt 自适应应可支持其他方形尺寸E017、E018非 512 的方形 A 样本isqrt 自适应(不做硬编码假设)
fontJAP_1.png(2048×1024)与 FONTSJP1(B8 512×512)的对应关系尺寸不一致,无法逐像素比对(普查 E.9);fontJAP_1 更可能是 FONTSJP(B4 512×1024)整图/拼接图或无关参考无E031该 PNG 的来源说明或对应版本 FONTSJP1不参与判定

33. 参考伪代码(Reference Pseudocode)

语言无关;以 chk.rs 实际逻辑为蓝本(E018)。

# ---------- 解析 ----------
function parse_chk(file: byte[]) -> ChkDocument:
    if len(file) < 4 or file[0..4] != b"xet\0":            error CHK_ERR_INVALID_MAGIC
    if len(file) < 0x40 + 0x1C:                            error CHK_ERR_TRUNCATED
    file_size = le32(file, 0x08);  data_end = le32(file, 0x0C)
    if file_size != len(file):                             error CHK_ERR_INVALID_SIZE
    if data_end != len(file) - 0x1C:                       error CHK_ERR_INVALID_SIZE
    # Family A 几何试探(先于 0x3C 判定)
    pal_a = len(file) - 0x1C - 92 - 256*4
    pix_a = pal_a - 0x40
    side  = isqrt(pix_a)
    if side * side == pix_a:
        return parse_family_a(file, pal_a, side)           # 方形 A
    if le32(file, 0x3C) == 0xA0:
        return parse_family_b(file)                        # 块状 B
    error CHK_ERR_UNSUPPORTED_FAMILY

function parse_family_a(file, palette_offset, side) -> ChkDocument:
    W = H = side
    palette = [ file[palette_offset + i*4 : palette_offset + i*4 + 4] for i in 0..256 ]   # RGBA 原样拷贝(显式端点)
    meta_offset = palette_offset + 1024
    meta    = file[meta_offset : meta_offset + 92]                        # 原始 92B(v2.3 消歧义:显式端点)
    name    = nul_trim(meta[0x18 .. 0x20])
    pixels  = file[0x40 .. palette_offset]                                # 存储序(含 swap34)
    header  = file[0 .. 0x40];  tail = file[len-0x1C ..]
    return {family: A, width: W, height: H, name, palette,
            pixels, raw_header: header, raw_meta: meta, raw_tail: tail}

function parse_family_b(file) -> ChkDocument:
    bits = (file[0x44] & 0x08) != 0 ? 8 : 4
    nb   = bits==8 ? 256 : 16
    palette_offset = len(file) - 0x1C - nb*4
    texels = (palette_offset - 0xA0) * (8 / bits)
    W = 1 << (file[0x42] & 0x0F);  H = 1 << (file[0x43] & 0x0F)
    if W*H != texels:                       error CHK_ERR_INVALID_SIZE   # geometry inconsistent
    block_w = bits==8 ? 16 : 32
    if W % block_w != 0 or H % 8 != 0:      error CHK_ERR_ALIGNMENT      # not block-aligned
    palette = [ file[palette_offset + i*4 : palette_offset + i*4 + 4] for i in 0..nb ]    # 显式端点
    name    = nul_trim(file[0x58 .. 0x60])
    pixels  = unpack_block(file[0xA0 .. palette_offset], W, H, bits)
    header  = file[0 .. 0xA0];  tail = file[len-0x1C ..]
    return {family: B, bit_depth: bits, width: W, height: H, name, palette,
            pixels, raw_header: header, raw_meta: [], raw_tail: tail}

# ---------- 编码 ----------
function encode_chk(doc) -> byte[]:
    validate_document(doc)                                              # §26 编码侧子集
    nb = doc.family==A ? 256 : (doc.bit_depth==8 ? 256 : 16)
    if doc.palette.len != nb:                                           error CHK_ERR_INVALID_PALETTE
    if doc.pixels.len != doc.width * doc.height:                        error CHK_ERR_PIXEL_COUNT_MISMATCH
    packed = encode_pixels(doc)                # A: 存储值=swap34(逻辑值) 行主序;B: pack_block
    header = doc.raw_header ?? default_header(doc.family)
    if doc.rebuild and doc.family==B: patch_block_header(header, W, H, bits)   # 0x40..0x48
    data_end = (doc.family==A ? 0x40 : 0xA0) + len(packed) + nb*4 (+ doc.family==A ? 92 : 0)
    put_le32(header, 0x08, data_end + 0x1C)
    put_le32(header, 0x0C, data_end);  put_le32(header, 0x10, data_end)
    if doc.family == A:
        meta = doc.raw_meta ?? default_meta()                  # 92B,0x00=0x40
        meta_base = 0x40 + len(packed) + nb*4
        put_le32(header, 0x28, meta_base + 0x10);  put_le32(header, 0x2C, meta_base + 0x10)
        put_le32(meta, 0x08, meta_base)
        meta[0x18..0x20] = name_to_8(doc.name)
        tail = doc.raw_tail ?? default_tail()                  # 0x00=0x28, 0x04=0x2C
        put_le32(tail, 0x08, meta_base); put_le32(tail, 0x0C, meta_base+0x08)
        put_le32(tail, 0x10, meta_base+0x0C); put_le32(tail, 0x14, meta_base+0x10)
        put_le32(tail, 0x18, meta_base+0x14)
        return header + packed + palette_bytes + meta + tail
    else:
        header[0x58..0x60] = name_to_8(doc.name)
        tail = doc.raw_tail ?? default_font_tail()             # 固定 7 项
        return header + packed + palette_bytes + tail
    # 末尾统一:validate_output(bytes)  → §26 全量校验

# ---------- 像素解码(单像素) ----------
function decode_pixel(doc, x, y) -> RGBA:
    if doc.family == A:
        raw = file[0x40 + y*W + x]
        idx = chk_swap34(raw)                       # A 唯一需要 swap
    else:                                           # Family B,§13.1
        block_x = x / block_w;  block_y = y / block_h
        local_x = x % block_w;  local_y = y % block_h
        base    = header_size + (block_y*block_cols + block_x)*128
        if bits == 8:
            idx = file[base + local_y*16 + local_x]
        else:
            b  = file[base + local_y*16 + local_x/2]
            idx = (local_x % 2 == 0) ? (b & 0x0F) : ((b >> 4) & 0x0F)
    return doc.palette[idx]                         # 越界时实现回退 [0,0,0,255](to_rgba L178)

# ---------- 基础变换 ----------
function chk_swap34(v: u8) -> u8:
    return (v & 0xE7) | ((v & 0x08) << 1) | ((v & 0x10) >> 1)   # 自反:swap34(swap34(v))==v
function pack4(lo: u8, hi: u8) -> u8:  return (hi << 4) | lo   # lo=先到像素(低 4bit)
function unpack4(b: u8) -> (u8, u8):   return (b & 0x0F, (b >> 4) & 0x0F)

34. 实现规则(禁止行为清单,Implementation Rules)

  1. 不许猜测读未知字段:unknown_xx 字段的语义未知,读取时只保存原始字节,不解释、不错位。
  2. 不许 Encoder 改未知字段:0x30/0x34、meta[0x04]、0x98 等一律 preserve;无证据的"规范化"等于制造新文件。
  3. 不许 GUI 直接操作二进制:界面只操作 ChkDocument;字节级读写只在 Parser/Serializer 层。
  4. 不许混用 A/B 规则:swap34 仅 Family A;块布局仅 Family B;meta 仅 Family A。
  5. 不许把 PAL4 当 PAL8:16 色 vs 256 色、64B vs 1024B、0x41=1 vs 2、0x44=0x04 vs 0x08。
  6. 不许忽略 Block Layout:Family B 严禁行主序直写;必须走 pack/unpack_block(§16)。
  7. 不许假设正方形:方形证据仅覆盖 Family A 语料(512²,E017);通用 .chk 不得假设方形,Family B 明确支持 512×256/512×1024 等非方形。
  8. 不许假设平台严格对应 Family:Family A=PS2 为用户确认(E011);Family B=PSP 是推断(E010,⚠️);不得反向用"平台"决定解析分支。
  9. 不许因能显示就认为正确:显示正确 ≠ 布局正确;必须以 byte-exact/diff=0 为判据(附记方法论)。
  10. 必须过 Validator:任何写入(patch/rebuild)输出前必须通过 §26。
  11. 必须尽量过 Round-trip:未编辑文件 serialize 必须逐字节还原(测试 A);编辑文件必须能被自己的 parser 读回且逻辑一致。
  12. 未知字段必须保留:preserve 是默认策略;recalc/regenerate 仅限本文列出公式的字段(§23.2)。
  13. 推断必须标注:一切 ⚠️/❓ 不得写成 ✅;新结论必须带证据 ID。
  14. 新结论必须有证据:样本/Hex/代码/Round-trip 之一;"看起来对"不是证据。

35. 开发闭环架构图

                    ┌────────────────────────────────────────────────────────────┐
                    │                        CHK File (bytes)                    │
                    └───────┬────────────────────────────────────────────┬───────┘
                            │ parse(§33 伪代码)                        ▲ serialize(§23)
                            ▼                                            │
                    ┌───────────────┐   family detect (0x3C / isqrt)   ┌──┴────────────┐
                    │   Detector    │──────────┬──────────────────────►│   Serializer   │
                    └───────┬───────┘          │                       └──┬────────────┘
                            ▼                  ▼                          │
                 ┌──────────────────┐  ┌──────────────────┐                │
                 │ Family A Parser  │  │ Family B Parser  │                │
                 │ (square, swap34) │  │ (blocks, nibble) │                │
                 └────────┬─────────┘  └────────┬─────────┘                │
                          └──────────┬──────────┘                          │
                                     ▼                                     │
                    ┌────────────────────────────────┐   rebuild   ┌────────▼────────┐
                    │         ChkDocument            │────────────►│     Encoder     │
                    │ format / family / platform     │             │ (quantize,      │
                    │ width / height / bit_depth     │             │  pack, swap34)  │
                    │ pixels (logical indices)       │             └────────┬────────┘
                    │ palette / metadata / name      │                      │
                    │ unknown_fields (raw bytes)     │                      │
                    │ relocation / raw_sections      │                      │
                    └────────────────┬───────────────┘                      │
                                     │ edit(GUI 不接触二进制)              │
                                     ▼                                      │
                    ┌────────────────────────────────┐                      │
                    │      Editor / Validator        │──────────────────────┘
                    │  (§24 Exact Preservation /     │      Validator(§26)
                    │   §25 Rebuild / §26 24 项)     │
                    └────────────────┬───────────────┘
                                     ▼
                              Valid CHK File

链路:CHK Binary → Parser → ChkDocument → Editor → Encoder → Serializer → Validator → CHK Binary。

36. 与 TXD 的区别【✅ 原文保留】

.txd(RenderWare).chk
结构字典,可含多贴图单贴图
格式真彩 8888 / ABGR、16bit 4444/5551/565、PAL8、DXT调色板索引(4/8bit)
平台PC(D3D8/D3D9)、PS2、XBOXPSP / PS2 自制纹
解码直接像素内容调色板查表

37. 附录 A:真实样例头部十六进制 dump【✅ 原文保留】

字节来自 LOADSC0.CHK(Family A)与 FONTSJP.CHK(Family B / 4bit),已逐字段核对。 偏移列 = 文件内字节偏移(十六进制)。

Family A · PS2 · LOADSC0.CHK(263352 字节)—— 前 0x40(64 字节)头

0000  78 65 74 00  00 00 00 00  B8 04 04 00  9C 04 04 00
0010  9C 04 04 00  07 00 00 00  00 00 00 00  00 00 00 00
0020  06 00 00 00  00 00 00 00  50 04 04 00  50 04 04 00
0030  00 41 2C 04  F8 C9 6D 05  00 00 00 00  00 00 00 00
偏移字节含义
0x0078 65 74 00魔数 "xet\0"(= 0x00746578)
0x08B8 04 04 00文件总大小 = 0x000404B8 = 263352
0x0C9C 04 04 00data_end = 0x0004049C = 263324 = 大小 − 0x1C
0x10同上data_end 复本
0x1407 00 00 00家族共用常量 7(语义未明)
0x2006 00 00 00家族共用常量 6(语义未明)
0x2850 04 04 00meta 基址相关(0x00040450,见 §8.2/§8.3)
0x2C同上同上(+0x14)
0x3000 41 2C 040x042C4100(家族特有值,非恒定)
0x34F8 C9 6D 050x056DC9F8(同上,非恒定)
0x3C00 00 00 00= 0 → Family A 标志
0x40…像素区(行主序 512×512,每像素 1 字节)

Family B · PSP · FONTSJP.CHK(262396 字节)—— 前 0x60(含名字区)

0000  78 65 74 00  00 00 00 00  FC 00 04 00  E0 00 04 00
0010  E0 00 04 00  07 00 00 00  00 00 00 00  00 00 00 00
0020  06 00 00 00  00 00 00 00  50 00 00 00  50 00 00 00
0030  A0 33 AE 0A  8C F2 BE 08  00 00 00 00  A0 00 00 00
0040  00 01 09 0A  04 01 04 45  38 00 00 00  20 00 00 00
0050  28 00 00 00  28 00 00 00  66 6F 6E 74 6A 70 00 00
(0x60..0x9F 全为 0;0xA0 起为 128 字节块像素区)
偏移字节含义
0x0078 65 74 00魔数
0x08FC 00 04 00大小 = 0x000400FC = 262396
0x0C / 0x10E0 00 04 00data_end = 0x000400E0 = 262368
0x14 / 0x2007 / 06家族共用常量
0x28 / 0x2C50 00 00 00常量 0x50
0x30A0 33 AE 0A0x0AAE33A0(此处 ≠ FONTSJP1/INTRO1 的 0x0AAE1BA0;非恒定)
0x348C F2 BE 080x08BEF28C(同上,非恒定)
0x3CA0 00 00 00= 0xA0 → Family B 标志
0x4101bits/4 = 1 → 4bit
0x4209log2(W) = 512
0x430Alog2(H) = 1024
0x4404位深类型 = 0x04(4bit)
0x45 / 0x4601 / 04常量
0x4838常量 56
0x4C / 0x50 / 0x5420 / 28 / 28常量 32 / 40 / 40
0x5866 6F 6E 74 6A 70 00 00贴图名 "fontjp"(NUL 截断)
0xA0…块像素区起点

⚠️ 注意:0x30/0x34 在本例(FONTSJP)与 §5.1 表、以及 FONTSJP1/INTRO1 均不同,是逐样本可变的值,已据此把置信度改为 ⚠️。Family A/B 头主体结构一致,只能靠 0x3C 标志 + 像素布局区分家族(这正是 §4.1 的结论)。

E015 补充(本次实测):"0x60..0x9F 全为 0"在本例 dump 时成立到字节级指认之外;实测 0x98=0x01(46/47 个 B 文件,FONTSJP 亦为 0x01;FONTSJP1=0x00)。原句按 dump 注释保留;精确零区边界见 §6.2。

38. 附录 B:通道映射测试法

目的:验证调色板条目的字节序确为 [R,G,B,A] 且 Alpha 在第 4 字节。

步骤:

  1. 构造测试 PNG(如 64×64):8 个纵向色带,即纯黑(0,0,0,255)、纯白(255,255,255,255)、纯红(255,0,0,255)、纯绿(0,255,0,255)、纯蓝(0,0,255,255)、透明(0,0,0,0)、半透明白(255,255,255,128)、中间灰(128,128,128,255)。8 ≤ 16 ≤ 256,PAL4/PAL8 都放得下。
  2. PNG → Encode(按 §21,目标家族任选,两种都跑)→ CHK。
  3. CHK → Decode(§22)→ RGBA 缓冲。
  4. 逐像素比对解码 RGBA 与源 PNG。

判定标准:

  • 全部 8 色 diff=0,则通道序 [R,G,B,A] 与 alpha 直通成立(✅ 若通过)。
  • 红绿互换(红带变蓝/绿带变红等)说明通道序错误(如 BGRA)。
  • 半透明白变为全透/全不透说明 alpha 未直通(第 4 字节不是 A)。
  • 透明带必须恰好映射到 index 0(当前工具策略,§20.3)。
  • Family A 上必须重复一次(swap34 只影响索引不影响通道序,理论上与 B 同判)。

当前证据状态:LOADSC0 已通过等价验证(E003 diff=0);本测试法作为新工具的自检程序(❓ 其余家族×位深组合的 diff=0 报告暂无正式记录)。

39. 附录 C:已知不一致记录(只记录,不改判定)

#不一致详情处理
C-1原文 §10.2 表(§6.1)"0x28/0x2C = data_end − 92" vs 字节实测/代码LOADSC0/LOADSC3 实测 0x28/0x2C = 0x00040450;data_end−92 = 0x00040440(= meta_base);0x40450 = meta_base + 0x10,与 chk.rs serialize_square L488–489 写入一致(byte-exact 测试通过)。两者差恒为 0x10。§13 dump 注(0x40450)本身与代码一致,与 §10.2 公式矛盾原文表逐字保留(§6.1);规范采用 meta_base+0x10(§8.2,E019);正式判定留给维护者复核
C-2原文"TXD*.chk(47 个 Family B)" vs 实测 45TXD\ 现有 45 个 .chk(2026-09-04 清点,E022)。47 = 全语料 Family B 总数(45 + 根目录 FONTSJP/FONTSJP1),疑为口径混用两口径并存记录(§29 注④),不硬凑
C-3§16 原文 block_cols = ceil(W/block_w) vs parse_block 整除代码先校验块对齐(W%block_w==0)再整除;ceil 在合法文件上等价原文保留;实现注已说明(§16)
C-4§16 原文位深判定 bits = header[0x41]*4 vs 代码判定① 0x44&0x08两者在全部样本一致;parse 实际用 0x44(L333),0x41 为冗余校验§6.3 双判定并列,原文保留
C-5"ok=4 bad=0"口径 vs parse_all_family_b_in_dir 断言体诊断测试正式记录 4 个指定样本 ok=4 bad=0(E001);parse_all_family_b_in_dir(L877–879)实际对 TXD 目录每个文件断言 serialize 逐字节相等(每次运行覆盖全部 45 个)§29 注② 两个口径并存,按原文口径填表、批量事实加注
C-6原文"FONTSJP 0x60..0x9F 全为 0" vs 实测 0x98=0x01FONTSJP 自身 0x98=0x01(本次实测,E015);原文 dump 注释在 0x98 这一个字节上不精确原文保留 + §37 补充注
C-7FONTSJP1 的 0x98=0x00 离群47 个 B 文件中唯一(E015)无判定,登记 §32
C-8ChkFile docstring 称 pixels 存"逻辑索引" vs from_rgba 实际先 swap34 再存Family A 的 pixels 在内存中是存储序(swap34 后)值;to_rgba 对 A 再做一次 swap34 得逻辑索引。对 Family B 两者相同。对使用者透明(encode/decode 互逆,E007/E002),但按字面理解 docstring 会写错 A 的直接序列化§19/§20 按实际行为描述;实现者注意
C-9原文 §13 dump 注"0x2C 同上(+0x14)" 表述含糊0x2C 与 0x28 字节相同(均为 0x40450);"+0x14"疑为对 tail 槽位 0x14(其值=meta_base+0x10=0x40450)的注记原文保留;规范值见 §8.2
C-10§6.2/E015 "0x60..0x9F 除 0x98 外全 0" vs SCEJLEGALJ.CHK 0x60–0x61 非零(普查新发现)普查(E029)字节级确认 0x60..0x9F 除 0x98 外唯一非零:TXD\SCEJLEGALJ.CHK 0x60–0x61=4C 4A——系 10 字节名 SCEJLEGALJ 从 0x58 名字区溢出 2 字节,非独立字段;"名字 8 字节"应精确为"名义 8B、可溢出"(47 B 文件中仅此 1 例)原文保留;§6.2 已加注;普查记录于附录 E.5
C-11chk.rs 从零重建的 B 文件 0x60..0x9F=0(含 0x98=0) vs 原生 46/47=0x98=0x01普查实测 11 个 _chk_verify 重建件 0x98 全为 0(E024/E029);rebuilt_header_matches_real_family_b 的槽位比较(0x28..0x54)不覆盖 0x60..0x9F,故"重建头逐字段相等"与"该区域系统性差异"并存;parse→serialize 的 byte-exact 往返不受影响(保留原字节)登记;重建策略(preserve)不变;待维护者决定是否让 default_header 写 0x98=0x01
C-12"测试套件全绿"表述 vs 2026-09-04 实测 74/75cargo test -p txd-format -- --include-ignored 75 tests:74 passed / 1 failed——唯一失败 txd::fonts_diag::dump_fontgif(txd.rs:248 panic index out of bounds: len 1049029, index 16804789,读 FONTS.TXD 的 TXD 侧诊断测试自身缺陷,与 CHK 格式无关);全部 CHK 测试通过(E032)§30/§31 已按实测记录;dump_fontgif 修复留待 TXD 侧维护
C-13§18.2 "Alpha 字节取值 0..255 均出现在真实样本中" vs 普查实测 49 种离散值普查(E030):6544 条目中 alpha≠255 共 297 条、去重 49 值(0..254),206 个中间值语料内未见——"全范围均出现"在字面上不成立,成立的是存在性(中间值大量出现)原文保留;§18.2 已加收窄注;渲染端语义仍 ❓

40. 附录 D:新旧章节映射

旧版章节新位置
1 概述§1
2 容器(通用结构)§4.1
3 Family A§9(原文)+ §4.2/§6.1/§7.1/§10/§11/§12
4 Family B / 4.1 块栅格与 4bit 打包§13(原文)+ §16/§17;4.1 代码块原文在 §16
5 位深 / 调色板§18.1
6 索引交换(仅 Family A)§10.2
7 从 RGBA 生成(量化)§20
8 与 TXD 的区别§36
9 置信度§31(原文表)
10 字段级映射 10.1–10.5§5.1(10.1)、§6.1(10.2)、§6.2(10.3)、§8.3(10.4)、§32(10.5)
11 参考 / 样例§41 附记(原文)+ §29 语料
12 已完全掌握 vs 需外部资料§32(原文照录)
13 附录 A Hex dump§37
附记(致谢与许可)§41
(新增)§2 术语、§3 家族判定、§4.2–4.3/§6 字段表、§7 公式、§8 指针、§14/§15、§19–§28、§29–§35、§38–§40

41. 附记(致谢与许可)【✅ 原文保留】

这个 .chk 格式没有公开官方规范,本文靠的是字节级对照 + parse→serialize 的 byte-exact 验证,而不是猜测。核心方法论很简单,也最能避免翻车:

结构、字节序、平台、调色板——都要能通过「重新写回来和原文件逐字节一致」才算证明,而不是"看起来对"。

  • 参考:写入器 png2chk.utf8.cpp(注意它写 0x30/0x34 有误,以本文为准);PSP 专用工具 GTAStoriesTex.exe(只认 Family B)。
  • 许可:本文可自由转载/改写,请保留"区分【✅/⚠️/❓】的置信度标注"——那一列比字段表本身更值钱,它告诉你哪些结论可以被放心依赖、哪些还要打问号。
  • 别把可变值当常量:0x30/0x34、0x14/0x20 等字段是逐样本可变的,硬编码进自己的工具会让它只在少数文件上碰巧能用。

欢迎指正与补充样本,尤其这几类最有助于把 ❓ 变 ✅:

  1. 同一张图在两平台的成对 .chk(锁定 PSP/PS2 的通道交换差异);
  2. 跨游戏/跨版本的 Family 常量样本(锁定 0x30/0x34、0x14/0x20 的真实语义);
  3. 任何结构化异常样本(非 2 的幂、非方形、极端尺寸、0x1C tail 被改的个案)。

关于「家族常量」:如果你拿去加载一个官方没做过的自定义 .chk,尽量让工具按家族几何+调色板自适应,而不是依赖那几个未明语义的常量——这样最稳。

(完)


42. 附录 E:全语料普查(Census)【新增,2026-09-04】

本附录是 v2.1 整理轮新增:用独立 Python 脚本对全部 49 个原生 .chk(+11 个工具生成件,单独标注)做一次实证普查,把正文各节的"观测"从 2 样本手工核对升级为全语料逐文件核验。原始逐文件数据(399 行)完整保存于 docs\tools\chk_census_results.txt,本附录只收录结论与关键数字。所有新增证据 ID 为 E024–E033(§31),新增矛盾登记 C-10/C-11/C-13(§39)。

E.0 工具、语料与可复现性【E024】

  • 脚本:docs\tools\chk_census.py(Python 3.14,无第三方依赖)。复现命令:python docs\tools\chk_census.py(自动写 chk_census_results.txt)。
  • 语料:根目录 4(LOADSC0/LOADSC3/FONTSJP/FONTSJP1)+ TXD\ 45 = 49 原生;另含 _chk_verify\ 11 个工具生成件(非原生,所有统计单独标注)。
  • 家族判定与 chk.rs parse 完全一致:A=完全平方几何试探先行;B=le32(0x3C)==0xA0。结果 A=2、B=47,与 §3 一致。
  • 语料完整性核对:全树 walk 实得 62 个 .chk = 49 原生 + 11 生成 + _chkprobe\ 下 FONTSJP/LOADSC0 两份副本(SHA-256 与原件逐字节一致:926a9d5d…/c93ce323…,不计入样本),普查语料无遗漏。

E.1 字段恒定性(普查 a 节)【E025】

  • 两家族恒定同一值(49/49 无例外):0x00=0x00746578(魔数)、0x04=0、0x14=7、0x18=0、0x1C=0、0x20=6、0x24=0、0x38=0。
  • 家族内恒定、家族间不同(49/49):0x28/0x2C:A=0x00040450(=meta_base+0x10,两样本);B=0x00000050(47/47)。0x3C:A=0;B=0xA0(家族判据)。
  • 家族内可变但公式导出:0x08/0x0C/0x10 在 B 侧恰 5 组值,与 5 个尺寸/位深类一一对应(512×512×4→0x000200FC/0x000200E0;512×256×8→0x000204BC/0x000204A0;512×512×8→0x000404BC/0x000404A0;512×1024×4→0x000400FC/0x000400E0;512×256×4→0x000100FC/0x000100E0);A 侧单组(0x000404B8/0x0004049C)。全部被 §7 公式解释(E026)。
  • B 头 0x40..0x5F 逐字节恒定(47/47):0x40=0x00、0x42=0x09、0x45=0x01、0x46=0x04、0x48=0x38、0x49..0x4B=0、0x4C=0x20、0x4D..0x4F=0、0x50=0x28、0x51..0x53=0、0x54=0x28、0x55..0x57=0;随位深/尺寸联动:0x41(0x01×25 / 0x02×22)、0x43(0x08×19 / 0x09×27 / 0x0A×1)、0x44(0x04×25 / 0x08×22)、0x47(0x25×22 / 0x45×25);0x58..0x5F 为名字区(46 种取值,逐文件与 §29 文件名对应)。
  • B 头 0x60..0x9C(u32 视角):0x64..0x94 全 0(47/47);0x9C=0(47/47);仅 0x60 与 0x98 有非零例外(见 E.5)。

E.2 0x30/0x34 语义破解(普查 b 节)【E028】

  • 全语料观测恰 3 组 (0x30,0x34):A=(0x042C4100, 0x056DC9F8)×2;B 常见=(0x0AAE1BA0, 0x08BF08CC)×46(全部 TXD + FONTSJP1);FONTSJP 单独=(0x0AAE33A0, 0x08BEF28C)×1。11 个工具生成件全部写 B 常见组(与 chk.rs default/E005 重建头比较一致)。
  • 哈希暴力破解:全部 0 命中。算法 {CRC32, adler32, sum8, xor32(按 u32 累积), FNV-1a} × 对象 {whole_file, header, pixel_region, palette, pixel+palette, name8 含 NUL, name 不含 NUL, filename(含扩展名), filename(无扩展名), meta 92B},没有任何 (算法×对象) 组合在任何文件上命中 0x30 或 0x34。
  • 指针/大小对假设:全部排除。14 项候选(file_size、data_end、pixel_region_size、palette_offset、palette_size、meta_offset、W·H、W·H·4、texels、tail_offset、meta_base+0x10、0x08−0x1C、pixel_offset、file_size×4)hit30=hit34=0(逐文件比较)。
  • 相对关系:v34−v30 = A 0x014188F8(2/2)、B 0xFE10ED2C(46)、FONTSJP 0xFE10BEEC(1),组内一致、组间不同,无布局含义。
  • 结论(不升级语义):0x30/0x34 不是语料内任何内容/文件名/元数据的常见校验和,也不是任何布局指针或尺寸量;"逐来源组恒定、跨组不同"的观测面扩大到 49/49。语义仍 ❓(§32)。

E.3 tail 全量核验(普查 c 节)【E027】

  • A:2/2 匹配 {0x28, 0x2C, meta_base, meta_base+8, +0xC, +0x10, +0x14}(meta_base=0x40440);B:47/47 匹配 {0x28, 0x2C, 0x3C, 0x48, 0x4C, 0x50, 0x54},无例外(刷新 E014 的 "46+2=48" 口径 → 49/49)。
  • 11 个生成件 tail 亦全部匹配(A 重建件匹配 A 表、B 重建件匹配 B 表)。

E.4 Family A meta 92B 全 dump(普查 d 节)【E012/E014 复核】

  • 两个 A 样本(LOADSC0/LOADSC3)meta 逐字节对比:除名字字节(loadsc0\0 vs loadsc3\0 @ meta[0x18..0x20])外完全一致,非零字段集(18 字节):
    • meta[0x00..0x04]=u32 0x00000040;meta[0x04..0x08]=u32 0x00108249;meta[0x08..0x0C]=u32 0x00040440(=meta_base);meta[0x0C]=32;meta[0x10]=40;meta[0x14]=40;meta[0x58]=1;其余 74 字节全 0。
  • 与 §12/E012 既有记录一致(确证,非新字段);tail 后 5 项=meta_base 序列恰指向 meta[0x00]/[0x08]/[0x0C]/[0x10]/[0x14](§8.2/E014)。

E.5 B 头 0x60..0x9F(普查 e 节)【E029 → C-10/C-11】

  • 字节级普查:0x60..0x9F 全 0 的文件 1/47;非零字节仅两类:
    1. 0x98:46 文件=0x01,FONTSJP1=0x00(唯一原生离群,复核 E015 成立);
    2. TXD\SCEJLEGALJ.CHK 的 0x60=0x4C('L')、0x61=0x4A('J'),系名字 SCEJLEGALJ(10 字节)从 0x58 名字区溢出 2 字节,47 文件中唯一一例;0x60..0x9F 不存在其他独立字段。
  • C-11(重建件差异):11 个 _chk_verify 重建件 0x60..0x9F 全 0(0x98=0),rebuilt_header_matches_real_family_b 的槽位比较(0x28..0x54)不覆盖该区域;parse→serialize byte-exact 不受影响(原字节保留)。

E.6 调色板与 Alpha(普查 f 节)【E030 → C-13】

  • 原生 49 文件调色板条目总数 6544(A 512 + B 6032);alpha≠255 条目 297 条;含 alpha==0 条目的文件 25 个(FONTSJP、TXD\LOADSC4-22 共 19 个、NEWS2、SCEJLEGALJ、SPLASH1-3)。
  • alpha≠255 去重值集 49 种(0..254);206 个中间值语料内未见,故"0..255 均出现"按存在性理解(C-13),全范围覆盖未证实。
  • 引用关系(新发现):仅 FONTSJP 的 alpha≠255 条目被像素数据实际引用(≥12/16 条;其解码与 fontjp.png diff=0 佐证这些 alpha 参与成像);其余 24 个含 alpha 条目文件的 alpha≠255 条目全部未被像素索引引用(如 TXD\LOADSC4-22 每文件 16 条中 14 条未用、且未用的恰为全部 alpha≠255 条目)。
  • index0 分布 11 种:(0,0,0,255)×39 为主;彩色 idx0:LOADSC0(A)=(246,212,107,255)、LOADSC3(A)=(186,90,46,255)、NEWS=(112,161,187,255)、NEWS2=(26,103,161,255)、NEWS3=(39,113,132,255)、SPLASH1-3、MEMCARD=(4,4,4,255)、FONTSJP=(253,253,253,245)。
  • 重复色条目:CAPCOM 29、SPLASH1 13、SPLASH2 11、SPLASH3 5、FONTSJP1 1、SCEJLEGALJ 1,其余 0,palette_edit/depth-conversion 类测试需注意(§23/§26 的索引层无损不受影响)。

E.7 几何/布局公式全量核验(普查 g 节)【E026】

49/49 OK,误差 0。核验项:容器 0x08/0x0C/0x10;A:file_size=0x40+W·H+1024+92+0x1C、0x28/0x2C=meta_base+0x10、meta[0x08]=meta_base;B:块对齐、file_size=0xA0+block_rows·block_cols·128+nb*4+0x1C、pixel_region=br·bc·128、texels=W·H、0x28/0x2C=0x50。尺寸类分组(原生):

类(W×H×bits)文件数file_size代表
A 512×512×82263352根目录 LOADSC0/LOADSC3
B 512×256×818132284TXD\LOADSC0-3、MPLOAD0-7、NEWS×3、SPLASH×3
B 512×512×423131324TXD\LOADSC4-22、INTRO1-4
B 512×512×84263356CAPCOM、SCEJLEGALJ、WARNING、FONTSJP1
B 512×1024×41262396FONTSJP
B 512×256×4165788MEMCARD

11 个生成件(含 1024×1024、2048×512、256×512 等非语料尺寸)几何亦全部满足公式。

E.8 位深双判定交叉(普查 h 节)【E033 → C-4 全语料版关闭】

  • 判定①(b[0x44]&0x08)== 判定②(b[0x41]∈{2,1})== 调色板大小(nb*4):47/47 三方一致,无不一致样本。
  • 0x47 与位深联动:0x25(8bit)×22、0x45(4bit)×25。

E.9 解码 vs 参考 PNG 逐像素 diff(普查 i 节)【E031】

  • LOADSC0.CHK(A 512×512)↔ loadsc0.png:diff=0 / 262144(复现 E003)。
  • FONTSJP.CHK(B4 512×1024)↔ fontjp.png:diff=0 / 524288(新增决定性证据:Family B 块序+nibble 布局+调色板 RGBA 首次获得图像级全图验证)。
  • FONTSJP1.CHK(B8 512×512)↔ fontJAP_1.png(2048×1024):尺寸不一致,无法逐像素比对;fontJAP_1 与 FONTSJP1 的对应关系未定(新 ❓,登记 §32 补充)。

E.10 对置信度体系的影响(汇总)

  • 升级(字段恒定性/布局公式类,仅此两类):§5.1 通用头恒定字段、§6.2 B 头 0x40..0x5F 恒定字节、§7 全部公式、§8.3 tail 表、§12 meta 布局,由 2 样本核对升级为 49/49(E025–E027、E033)。
  • 不升级(语义类,维持原判):0x30/0x34 语义(排除面扩大但语义仍 ❓,E028)、0x98 语义(E029)、0x14/0x20 语义、tail 消费方式、alpha 渲染语义(E030 只收窄表述)、通道交换差异、Family B=PSP 归属(E010 推断链不变)。
  • 新登记矛盾:C-10(名字溢出 vs "0x60..0x9F 全 0")、C-11(重建件 0x98=0 vs 原生 0x01)、C-13("0..255 均出现" vs 49 种离散值实测)。
  • cargo test 刷新:E032(75 tests,74 passed / 1 failed,唯一失败为 TXD 侧 dump_fontgif;CHK 全过;byte-exact 覆盖 48/49)。
#CHK#VCS#Wiki#LCS

推荐项目

雨云服务器稳定靠谱 点击前往

雨云服务器稳定靠谱 点击前往

站长亲测,订外卖返利

站长亲测,订外卖返利

支付宝碰一碰红包,扫码领取,红包更大

支付宝碰一碰红包,扫码领取,红包更大

扫码红包薅羊毛

扫码红包薅羊毛

GTA系列下载

GTA系列下载

相关推荐

教程【日版】侠盗猎车手之汉化制作教学
【日版】侠盗猎车手之汉化制作教学
Lzh10_慕黑
41320
教程GTAVC - japanese.gxt 文本资源
GTAVC - japanese.gxt 文本资源
Lzh10_慕黑
69500
教程GXT - 侠盗猎车手文本资源文件
GXT - 侠盗猎车手文本资源文件
Lzh10_慕黑
22010
教程网站补全计划 - 简介点击查看大图功能实现
网站补全计划 - 简介点击查看大图功能实现
Lzh10_慕黑
28002
教程GTAVC - american.gxt 文本资源
GTAVC - american.gxt 文本资源
Lzh10_慕黑
75300
教程便捷的游戏文本编辑器 - GXTStudio 之使用教学
便捷的游戏文本编辑器 - GXTStudio 之使用教学
Lzh10_慕黑
57710
玩家故事【捐赠专用页】感谢你们的支持!
【捐赠专用页】感谢你们的支持!
Lzh10_慕黑
33010
教程圣安地列斯全部特种车任务及其奖励
圣安地列斯全部特种车任务及其奖励
Devil
21600