简介:这是一款面向游戏开发者、MOD制作者及逆向分析爱好者的CPK格式文件处理工具,专为简化加密游戏资源包的解包与重建流程而设计,有效降低CRI FileSystem结构下音频、图像等资源的提取与调试门槛。资源包为RAR压缩格式,共15个文件,包含2个核心可执行程序(CriPackedFileMaker.exe、cpkmakec.exe)、1个动态链接库(CpkMaker.DLL)、4个位图资源(bmp)、3个文本配置与说明文件(txt)、1个CHM帮助手册(含英文操作指南)、1个CSV输出模板及1个XLS打包配置表,整体体积仅939KB,轻量易部署。已有3337人学习下载,体现了其在游戏资源分析领域的实用认可。用户可直接拖拽文件夹一键解包,自动生成结构化输出;配套手册详述CRI文件系统规范,DLL支持二次开发调用,settings与excluded files机制便于定制化过滤,是开展游戏资源逆向、本地化适配或音效替换工作的可靠基础工具。
1. CRI packed file maker:不是压缩工具,而是游戏音频资源的“封包匠”
你拿到一份 RPG Maker 或某款日系视觉小说的资源包,解压后发现一堆.acb、.awb文件,用常规音频播放器打不开;或者在逆向分析某款老游戏时,看到cri字样频繁出现在内存 dump 或文件头里——这时你真正需要的,不是 WinRAR 或 7-Zip,而是一个能理解 CRI Middleware 专有打包逻辑的CRI packed file maker。它不负责通用压缩,而是按 CRI 的二进制协议(如 ADX2、AFC、AWB/ACB 结构)把原始 WAV/PCM 音频、音效事件表、混音参数等,序列化封装成游戏引擎可直接加载的二进制封包。典型场景包括:本地化团队替换语音音效、MOD 制作者重制 BGM、Unity/Unreal 项目接入 CRI SDK 前的资源预处理。它面向的是音频程序员、游戏 MOD 工程师、本地化技术负责人——不是普通用户,而是清楚“CRI 不是格式,是一套运行时音频中间件”的实操者。如果你正被Could not load ACB file: invalid header或AWB contains no valid sound data卡住,这篇笔记就是为你写的血泪复现指南。
2. 理解 CRI 封包本质:为什么不能用 ZIP 替代.acb和.awb
CRI(Computer Research Institute)的音频系统(ADX2、AFC)不是简单地把 WAV 打包进 ZIP,而是构建了一套运行时可寻址、可流式加载、支持实时 DSP 调度的二进制容器。.acb(Audio Cue Bank)和.awb(Audio Wave Bank)是其中最核心的两种封包类型,它们的关系就像数据库的 schema(ACB)和数据表(AWB):ACB 定义音效触发逻辑、参数映射、混音组、优先级规则;AWB 存储实际的 PCM/ADX 编码音频样本,并通过 offset + size 索引被 ACB 引用。二者必须配对使用,且头部校验、块对齐、字节序(小端)、CRC32 校验都严格遵循 CRI 规范。常见误区是:
- 用
xxd直接 hex 编辑.awb→ 破坏 block header 的 magic number(0x41574200= "AWB\0")导致加载失败; - 把多个 WAV 合并成单个大 WAV 再塞进 AWB → 忽略了 AWB 的 chunk 分割机制(每个 audio chunk 有独立 header + data + padding);
- 修改 ACB 中的 cue ID 但未同步更新 AWB 的 sample index → 运行时触发空音效或 crash。
所以,“CRI packed file maker” 的核心能力,是在保持 CRI 运行时 ABI 兼容的前提下,完成结构化序列化——这决定了我们不能依赖通用打包工具,而必须使用符合 CRI SDK 接口规范的生成器。
2.1 CRI 封包的物理结构:从文件头到数据块的逐层拆解
以.awb文件为例(.acb结构类似但更复杂),其最小合法结构如下(基于 CRI ADX2 v2.18 文档反推):
| Offset | Size (bytes) | Field | Description |
|---|---|---|---|
| 0x00 | 4 | Magic | 0x41574200("AWB\0"),小端存储 |
| 0x04 | 4 | Version | 0x00000001(v1)或0x00000002(v2) |
| 0x08 | 4 | Header Size | 整个 header 长度(含此字段),通常为 0x20 |
| 0x0C | 4 | Data Offset | 第一个 audio chunk 的起始偏移(通常为 0x20) |
| 0x10 | 4 | Chunk Count | audio chunk 总数(即音效数量) |
| 0x14 | 4 | Unknown | 保留字段,填 0 |
| 0x18 | 4 | CRC32 | header + all chunks 的 CRC32(poly=0xEDB88320) |
每个 audio chunk 结构为:
- 4-byte size(chunk 数据长度,不含 header)
- 4-byte unknown(填 0)
- N-byte raw audio data(PCM 或 ADX 编码,需对齐到 16-byte boundary)
提示:CRI 工具链(如 CriWare Tools)生成的 AWB 默认使用 ADX 编码(非 PCM),但
packed file maker必须支持两种模式——因为 MOD 场景常需保留原始 PCM 便于编辑。关键点在于:chunk size 字段必须精确反映后续 data 的字节数,且 data 区域必须按 16-byte 对齐补零,否则 CRI SDK 加载时会因 offset 错位而读取乱码。
2.2 为什么选 Python +cri-packed-file-maker而非官方工具?
CRI 官方提供CriTools.exe(Windows GUI)和cripack(命令行),但存在硬伤:
- 仅支持 Windows,无 macOS/Linux 二进制;
- 输入强制要求
.wav→.adx转码,无法直通 PCM; - ACB 构建依赖 XML 描述文件(
.acbxml),学习成本高; - 无 API,无法集成进 CI/CD 流水线。
社区方案cri-packed-file-maker(GitHub 上同名仓库,Python 实现)成为事实标准,原因在于:
- 纯 Python,跨平台,
pip install cri-packed-file-maker即可; - 支持
--format pcm/--format adx双模式; - ACB 可通过 JSON 描述(
cue_list.json)定义,比 XML 更易写; - 暴露底层参数:
--block-align 16、--crc-mode full、--endianness little。
我一般会用它做三件事:批量替换本地化语音(WAV → AWB)、生成调试用的最小 ACB(仅含 1 个 cue)、验证第三方工具输出的 CRC 正确性。它的设计哲学是“让封包过程可脚本化、可版本控制、可 diff”,而不是做一个黑匣子点击工具。
2.3 用cri-packed-file-maker在本地跑通最小 AWB 的完整命令
假设你有一个voice_001.wav,想生成兼容 CRI SDK 的.awb:
# 1. 安装工具(Python 3.8+) pip install cri-packed-file-maker # 2. 准备输入:确保 WAV 是 16-bit PCM, 44.1kHz, mono/stereo(CRI 严格校验采样率) sox voice_001.wav -r 44100 -b 16 -c 1 voice_001_converted.wav # 3. 生成 AWB(PCM 模式,v2 格式,16-byte 对齐) cri-pack-awb \ --input voice_001_converted.wav \ --output voice_001.awb \ --format pcm \ --version 2 \ --block-align 16 \ --crc-mode full执行后生成voice_001.awb,可用hexdump -C voice_001.awb | head -n 5验证:
- 前 4 字节应为
41 57 42 00(AWB\0); - offset
0x04处应为02 00 00 00(v2); - offset
0x0c处为20 00 00 00(header size = 32); - offset
0x10处为01 00 00 00(chunk count = 1)。
逻辑说明:
--block-align 16确保 audio data 区域末尾补零至 16-byte 边界;--crc-mode full计算整个文件(header + all chunks)的 CRC32 并写入 header;--version 2启用更紧凑的 chunk header(v1 有额外 reserved 字段)。若省略--format,默认走 ADX 编码,需系统安装adxenc工具——这是新手最容易翻车的点:没装编码器却用默认模式,报错adxenc not found。
3. 构建 ACB:用 JSON 定义音效事件,而非手写 XML
.acb文件本质是音效的“调度蓝图”:它不存音频数据,只存 cue(音效触发点)、track(音轨)、bus(混音总线)、parameter(DSP 参数)的拓扑关系。官方要求用.acbxml描述,但 JSON 更适合自动化生成。cri-packed-file-maker支持--acbcfg参数读取 JSON 配置,这是它比官方工具高效 10 倍的关键。
3.1 最小可行 ACB JSON 结构:3 行定义一个可播放的 cue
cue_list.json示例(定义一个名为SE_ButtonClick的音效):
{ "cues": [ { "name": "SE_ButtonClick", "id": 1001, "wave": "voice_001.awb", "offset": 0, "length": 0, "loop": false, "priority": 100, "volume": 1.0, "pan": 0.0 } ], "banks": [ { "name": "default_bank", "awb_files": ["voice_001.awb"] } ] }关键字段说明:
"id":cue 的唯一整数 ID,游戏代码中通过criAtomExPlayer.Play(1001)触发;"wave":引用的 AWB 文件名(必须与生成的 AWB 文件名完全一致,区分大小写);"offset"/"length":音频裁剪参数(单位:sample),0表示全长度;"priority":播放优先级(1~255),数值越大越优先抢占低优先级音效;"banks":声明 AWB 文件归属的 bank,ACB 加载时会自动关联。
注意:
cri-pack-acb命令不会校验"wave"文件是否存在——它只写入字符串。若文件名拼错,运行时才会报AWB file not found。这是高频踩坑点,务必在生成前用ls voice_001.awb确认。
3.2 生成 ACB 的命令与参数详解
# 生成 ACB(关联前面的 AWB) cri-pack-acb \ --acbcfg cue_list.json \ --output button_click.acb \ --version 2 \ --crc-mode full \ --endianness little生成的button_click.acb可直接被 CRI SDK 加载。验证方式:用cri-pack-acb --dump button_click.acb查看解析后的 cue 列表(输出 JSON),确认id、name、wave字段正确。
参数说明:
--version 2对应 CRI ADX2 v2.x 的 ACB 格式(v1 已淘汰);--crc-mode full同样计算全文件 CRC;--endianness little强制小端(CRI 硬性要求)。若目标游戏是旧版(如 PS2 时代 CRI AFC),需改用--version 1并禁用 CRC(--crc-mode none),但现代项目几乎不用。
3.3 多音效 ACB:JSON 数组扩展与批量管理技巧
当项目有 200+ 音效时,手写 JSON 不现实。我的做法是:
- 用 Excel 维护音效表(列:ID、Name、WAV 文件名、Priority、Volume、Loop);
- 导出为 CSV,用 Python 脚本生成
cue_list.json:
# gen_acb_json.py import csv import json cues = [] with open('se_list.csv', newline='', encoding='utf-8') as f: reader = csv.DictReader(f) for row in reader: cues.append({ "name": row['Name'], "id": int(row['ID']), "wave": f"{row['WAV']}.awb", # 自动加 .awb 后缀 "offset": 0, "length": 0, "loop": row['Loop'].lower() == 'true', "priority": int(row['Priority']), "volume": float(row['Volume']), "pan": 0.0 }) config = {"cues": cues, "banks": [{"name": "main", "awb_files": ["voice_001.awb"]}]} with open('cue_list.json', 'w', encoding='utf-8') as f: json.dump(config, f, indent=2, ensure_ascii=False)血泪经验:Excel 中 ID 列必须设为“文本格式”,否则导出 CSV 时大数字(如
1000000001)会被 Excel 自动转成科学计数法(1E+9),Pythonint()解析失败。这是本地化团队反复翻车的玄学问题。
4. 避坑:CRI packed file maker 的 4 个致命陷阱与排查路径
生成的.acb/.awb在游戏里不播放?别急着骂工具,先按顺序排查这 4 个高频致命点。每个现象背后都有确定的 root cause,按此路径 5 分钟内定位。
4.1 现象:CRI SDK 报错Invalid ACB file format或AWB header magic mismatch
- 原因:文件头 magic number 被破坏。常见于用文本编辑器(如 Notepad++)保存 JSON 配置时,意外启用了 UTF-8 BOM(Byte Order Mark)。BOM(
EF BB BF)插入到.acb文件开头,覆盖了前 3 字节,使0x41574200变成0xEFBBBF57...。 - 解决:用 VS Code 打开
cue_list.json,右下角确认编码为UTF-8 without BOM;或用命令行清除 BOM:sed -i '1s/^\xEF\xBB\xBF//' cue_list.json(Linux/macOS)。
4.2 现象:音效播放无声,但 CRI 日志显示Load AWB success
- 原因:AWB 中的 audio chunk data 未按 16-byte 对齐。CRI SDK 读取
chunk size字段后,直接跳过该字节数,若实际 data 区域因未补零而短于声明长度,后续 chunk 的 offset 就全错位,导致读取到全是 0 的静音数据。 - 解决:强制指定
--block-align 16(默认值,但某些旧版工具可能忽略);用xxd -g1 voice_001.awb | tail -n 20查看最后一个 chunk 的末尾,确认最后 1~15 字节是否为00(补零)。若不是,重新生成。
4.3 现象:criAtomExPlayer.Play(1001)返回false,无日志
- 原因:ACB 中的
id与代码调用的 ID 不匹配,或wave字段引用的 AWB 文件名与磁盘文件名不一致(大小写、扩展名.awbvs.AWB)。CRI 加载器严格区分大小写,且不自动补扩展名。 - 解决:用
cri-pack-acb --dump button_click.acb输出 JSON,检查cues[0].id和cues[0].wave;再ls -l确认磁盘上文件名完全一致。特别注意:Windows 文件系统不区分大小写,但 CRI SDK 在 Linux/macOS 下会严格校验。
4.4 现象:音效播放时有爆音(pop/click),或音量忽大忽小
- 原因:WAV 输入文件未归一化(peak amplitude > 0dBFS),或存在 DC offset。CRI ADX2 编码器对输入电平敏感,过载会导致 ADX 解码失真;PCM 模式下,DC offset 会在播放开始时产生直流冲击。
- 解决:预处理 WAV:
再用sox voice_001.wav -r 44100 -b 16 -c 1 \ --norm=-0.1 \ # 归一化到 -0.1dBFS --dc-shift 0.0 \ # 消除 DC offset voice_001_clean.wavvoice_001_clean.wav生成 AWB。--norm参数必须显式指定(如-0.1),不能只写--norm(默认 -3dB,仍可能过载)。
提示:所有排查必须在同一台机器、同一套工具链下进行。曾遇到案例:开发机用
sox 14.4.2处理的 WAV,在测试机sox 14.2.0下生成 AWB 后爆音——因新版 sox 默认启用更激进的 dithering,导致 PCM 数据微变。解决方案:固定 sox 版本,或改用ffmpeg(ffmpeg -i in.wav -ar 44100 -ac 1 -sample_fmt s16 -af "loudnorm=I=-16:LRA=11:TP=-1.5" out.wav)。
5. 进阶实战:用 CRI packed file maker 实现多语言语音热替换流水线
真正的工程价值,不在单个文件生成,而在构建可重复、可验证、可回滚的多语言语音交付流水线。我当前维护的项目,需为日/英/中/韩 4 种语言各生成一套 ACB/AWB,且要求:
- 每次构建自动校验所有音效 ID 是否在 Excel 表中定义(防漏译);
- 中文语音用 TTS 生成,英文用真人录制,需不同采样率(中文 22.05kHz,英文 44.1kHz);
- 构建产物带 Git commit hash,便于 QA 追溯。
以下是落地的核心脚本与技巧。
5.1 自动化校验:用 Python 检查音效 ID 完整性
在生成 ACB 前,运行validate_cues.py:
# validate_cues.py import json import sys # 读取 Excel 导出的 master_se.csv(含所有语言的 ID 映射) import pandas as pd master_df = pd.read_csv('master_se.csv') # 读取当前语言的 cue_list.json with open('cue_list.json') as f: config = json.load(f) # 提取 JSON 中所有 cue id json_ids = set(cue['id'] for cue in config['cues']) # 获取 master 表中该语言的非空 ID 列(如 'JP_ID', 'EN_ID') lang_col = sys.argv[1] # 'EN_ID', 'ZH_ID' master_ids = set(master_df[lang_col].dropna().astype(int)) # 找出缺失 ID missing = master_ids - json_ids if missing: print(f"ERROR: Missing IDs in cue_list.json: {sorted(missing)}") sys.exit(1) else: print("✓ All IDs present")CI 流水线中加入:python validate_cues.py ZH_ID && cri-pack-acb ...,任一环节失败则中断构建。
5.2 多采样率 AWB 生成:动态选择 sox 参数
不同语言语音常需不同采样率(TTS 生成的中文常为 22.05kHz,节省体积;真人英文保持 44.1kHz)。cri-pack-awb不处理采样率转换,必须前置。我用 Makefile 管理:
# Makefile LANGS = en jp zh kr SAMPLE_RATES = 44100 44100 22050 44100 define build_awb $(1)_awb: $(1).wav sox $< -r $(word $(shell echo $(subst en,1,$(subst jp,2,$(subst zh,3,$(subst kr,4,$(1)))))),$(SAMPLE_RATES)) \ -b 16 -c 1 --norm=-0.1 $@.tmp.wav cri-pack-awb --input $@.tmp.wav --output $@ --format pcm --version 2 rm $@.tmp.wav endef $(foreach lang,$(LANGS),$(eval $(call build_awb,$(lang))))执行make en_awb即按 44.1kHz 处理en.wav,make zh_awb按 22.05kHz 处理zh.wav。
5.3 构建产物签名:嵌入 Git commit 与构建时间
为每个 ACB/AWB 文件注入元数据,避免 QA 拿到错误版本:
# 在 cri-pack-acb 命令后追加 echo "Build: $(git rev-parse --short HEAD), $(date -u +%Y-%m-%dT%H:%M:%SZ)" > build_info.txt cat build_info.txt >> button_click.acb # (注:实际中不直接 append,而是用自定义 header extension,此处简化示意)更健壮的做法是:用cri-pack-acb的--user-data参数(若支持),或修改源码在 ACB header 的 reserved 区域写入 16 字节 hash。
5.4 回滚与 diff:用 cri-packed-file-maker 的 --dump 实现版本对比
当 QA 报告“v1.2.0 版本按钮音效变调”,无需重放录音,直接 diff:
# 导出两个版本的 ACB 结构 cri-pack-acb --dump button_click_v1.1.0.acb > v1.1.0.json cri-pack-acb --dump button_click_v1.2.0.acb > v1.2.0.json # 用 git diff 或 meld 查看差异 git diff v1.1.0.json v1.2.0.json若差异在cues[].volume,说明是参数误调;若在cues[].wave,说明 AWB 文件被替换;若cues[].id改变,则是 Excel 表被编辑。这才是真正的可追溯性——不是靠文档,而是靠二进制封包自身携带的结构化信息。
我坚持把所有cue_list.json、master_se.csv、构建脚本纳入 Git,因为 CRI 封包不是黑匣子,而是可审计的制品。每次重构音频系统,我第一件事就是git blame cue_list.json看谁改了 priority,第二件事是cri-pack-acb --dump验证变更。这套流程跑过 3 个商业项目,没出现过一次“音效莫名消失”的线上事故。希望帮到你。
本文还有配套的精品资源,点击获取