GTAMODX
圣安地列斯
圣安地列斯
256
圣安地列斯安卓版
圣安地列斯安卓版
28
罪恶都市
罪恶都市
701
罪恶都市安卓版
罪恶都市安卓版
177
GTA3
GTA3
124
GTA3安卓版
GTA3安卓版
53
查看所有游戏
专栏组织QQ群
动态
登录
GTAMODX

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

支持

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

创作者

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

资源

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

平台

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

友情链接

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

© 2026 GTAMODX. All rights reserved.

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

CHK格式 - 游戏纹理文件

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

GTA .chk 纹理格式(ChkFile)Wiki

版本:基于 crates/txd-format/src/chk.rs 的实现 + 逆向样例。.chk 没有公开官方规范, 本文区分【✅ 已确证】与【⚠️ 推断】与【❓ 待验证】,不把推测写成事实。


目录

  1. 概述
  2. 容器(通用结构)
  3. Family A(PS2 · 8bit 方形 · 256 色)
  4. Family B(PSP · 块状 · 8bit 256 / 4bit 16)
  5. 位深 / 调色板
  6. 索引交换(仅 Family A)
  7. 从 RGBA 生成(量化)
  8. 与 TXD 的区别
  9. 置信度
  10. 字段级映射(header / meta / tail 各偏移)
  11. 参考 / 样例
  12. 已完全掌握 vs 需外部资料
  13. 附录 A:真实样例头部十六进制 dump

1. 概述

  • .chk 是一张 调色板索引 纹理(非真彩、非 TXD 字典):每个像素存的是调色板下标。
  • 魔数:b"xet\0"(0x78 0x65 0x74 0x00)——注意是 "xet" 反序(对应 "tex")。
  • 一个 .chk 文件 = 单张贴图(宽 × 高 + 调色板 + 索引像素),与 RenderWare .txd(可含多贴图)不同。
  • 平台:【✅】Family A = PS2,【⚠️ 推断】Family B = PSP(依据:你给的 LOADSC0/LOADSC3 确认为 PS2;PSP 专用工具 GTAStoriesTex 只认 Family B)。
  • 字节序【✅】:所有字段(魔数、尺寸、偏移、地址表、log2 几何、位深类型)均为 little-endian。截至当前样例未发现 big-endian 版本;若未来样本呈高位端序,需按像素做字节交换,此处留作警示。
  • 不含 mipmap / 无级联【✅】:.chk 是单一级的位图,解析未发现任何多级/mip 链——只有一个像素区 + 一个调色板。它不是像 .txd 那样可含多贴图/多 mip 的字典。

2. 容器(通用结构)

[ 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,只能靠像素布局分。

3. Family A(PS2 · 8bit 方形 · 256 色)【✅ 已确证】

  • 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 处为偏移字段(回写用)。

4. Family B(PSP · 块状 · 8bit 256 / 4bit 16)【✅ 已确证】

  • 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 色。

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 字节/行)。


5. 位深 / 调色板

位深调色板家族
8bit256 色(PAL8)Family A(PS2,方形)、Family B(PSP,块状)
4bit16 色(PAL4)仅 Family B(PSP,块状)
  • 调色板条目每项 4 字节 [R,G,B,A]。

6. 索引交换(仅 Family A)

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


7. 从 RGBA 生成(量化)【✅ 已确证】

写入时用 median-cut 把 RGBA 量化到 256/16 色:

  • 全透明像素(A==0)不参与颜色聚类,固定占一个透明项(index 0),不挤占真实色槽。
  • 颜色距离 dist2 中 Alpha 降权(颜色主导)。
  • 8bit:from_rgba(方形 A)/ from_rgba_block8(块 B);4bit:from_rgba_font。

8. 与 TXD 的区别

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

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
调色板字节序(RGBA)+ 索引交换还原原图✅ 决定性解析 LOADSC0.CHK 的 RGBA 与参考 loadsc0.png 逐字节 diff=0(512×512)
Family A 方形 / Family B 块 的像素布局+索引交换✅ 强证据byte-exact 还原证实(非仅"看起来对")
Family A=PS2、Family B=PSP✅ 确证用户确认 LOADSC0/3=PS2;PSP 专用工具 GTAStoriesTex 仅认 Family B;且 byte-exact 证实两家族均为索引纹理
PS2 vs PSP 的字节序/通道交换差异⚠️ 样例内未发现两家族各自解码颜色正确 + byte-exact;但无 PSP/PS2 成对图无法最终定论
Family A / B 头、meta、tail 各偏移字段✅(第 10 节逐字段表)用 LOADSC0/LOADSC3 + TXD*.chk 逐字节比对
家族特有值(0x30/0x34)⚠️ 逐样本可变见 §10.1:A 两样本恒定,B 在两同名样本间不同(FONTSJP vs FONTSJP1);族内不恒定,语义未知
tail 偏移/地址表✅原生 CAPCOM/INTRO1/LOADSC0 逐项 byte-exact

10. 字段级映射(header / meta / tail 各偏移)

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

10.1 通用 header(两家族共有)

偏移含义观测置信
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✅

10.2 Family A(方形 8bit,PS2)特有

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

10.3 Family B(块状,PSP)特有

偏移含义观测置信
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"✅

10.4 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 基址序列。结构表语义已确证,但其消费方(是否每个都必须有效、固定)仍需游戏/工具侧验证。

10.5 待确认点 / 下一步

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

11. 参考 / 样例

  • png2chk.utf8.cpp:参考写入器(注:其 0x30/0x34 写入有误,以本实现为准)。
  • GTAStoriesTex.exe:PSP 专用工具,只认 Family B。
  • 样例:LOADSC0.CHK、LOADSC3.CHK(PS2 · Family A,263352 字节);TXD\*.chk(47 个 Family B)。

12. 已完全掌握 vs 需外部资料

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

  • 容器结构:魔数、0x08 大小、0x0c data_end、tail 偏移/地址表(header/meta/tail 逐字段表见第 10 节)。
  • 家族与 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,需要不同来源的样本或运行时才能继续锁定。


13. 附录 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,见 §10.2/10.4)
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)与 §10.1 表、以及 FONTSJP1/INTRO1 均不同,是逐样本可变的值,已据此把置信度改为 ⚠️。Family A/B 头主体结构一致,只能靠 0x3C 标志 + 像素布局区分家族(这正是 §2 的结论)。


附记(致谢与许可)

这个 .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,尽量让工具按家族几何+调色板自适应,而不是依赖那几个未明语义的常量——这样最稳。

(完)

#CHK#VCS#Wiki#LCS

相关推荐

教程便捷的游戏文本编辑器 - GXTStudio 之使用教学
便捷的游戏文本编辑器 - GXTStudio 之使用教学
Lzh10_慕黑
48310
教程GTAVC - american.gxt 文本资源
GTAVC - american.gxt 文本资源
Lzh10_慕黑
66700
教程GTAVC - japanese.gxt 文本资源
GTAVC - japanese.gxt 文本资源
Lzh10_慕黑
51900
教程【日版】侠盗猎车手之汉化制作教学
【日版】侠盗猎车手之汉化制作教学
Lzh10_慕黑
27420
教程GXT - 侠盗猎车手文本资源文件
GXT - 侠盗猎车手文本资源文件
Lzh10_慕黑
17510
教程网站补全计划 - 简介点击查看大图功能实现
网站补全计划 - 简介点击查看大图功能实现
Lzh10_慕黑
14802