Appearance
资源转换管线 — 概念
资源管线总览
INCGFX 宏处理
INCGFX 宏的作用
在原始 GBA 构建中,图形资源通过 INCGFX 宏包含到 C 代码中:
c
// 原始 GBA 构建中的用法
const u32 gSprite_Bulbasaur[] = INCGFX("graphics/pokemon/bulbasaur/0.4bpp");在 ARM 构建中,INCGFX 展开为 .incbin 汇编指令,直接将二进制文件嵌入 ROM。但在 WASM 构建中,Clang 不支持 .incbin,因此需要不同的处理方式。
WASM 构建中的替代方案
处理步骤:
- 扫描:Python 脚本遍历所有
.c文件,用正则表达式匹配INCGFX("...")调用 - 转换:将 GBA 格式的图片数据(
.4bpp、.8bpp)转换为 C 字节数组 - 生成:输出头文件,使
INCGFX宏展开为extern数组引用
c
// WASM 构建中生成的替代代码
// generated_wasm_assets.h
extern const u32 gSprite_Bulbasaur_data[];
// INCGFX 宏重定义为数组引用
#define INCGFX(path) __referenced_data_##path支持的图形格式
| 格式 | 后缀 | 每像素位数 | 颜色数 | 用途 |
|---|---|---|---|---|
| 4bpp | .4bpp | 4 位 | 16 色 | 精灵图、背景图块 |
| 8bpp | .8bpp | 8 位 | 256 色 | 全彩背景、大图 |
| GB | .gb | 2 位 | 4 色 | GB 兼容图形 |
| 1bpp | .1bpp | 1 位 | 2 色 | 字体、单色图标 |
INCBIN 二进制包含
INCBIN 的作用
INCBIN 用于包含任意二进制数据:
c
// 包含原始二进制数据
const u8 gBattleBackground[] = INCBIN("graphics/battle/bg.bin");与 INCGFX 类似,原始 ARM 构建通过汇编指令嵌入,WASM 构建需要 Python 脚本预处理。
INCBIN 处理流程
压缩格式处理:
GBA 游戏通常使用 LZ77 或 Run-Length 编码压缩数据以节省 ROM 空间。在 WASM 构建中:
- 某些数据保持压缩,运行时由游戏代码调用
LZ77UnCompVram解压 - 某些数据由 Python 脚本预解压,减少 WASM 运行时开销
地图与事件数据转换
地图数据
Pokemon Emerald 的每张地图包含多个数据文件:
wasm_asm_data.py 将这些数据从 GBA 汇编格式(.s 文件中的 .word、.byte 指令)转换为 C 数组:
c
// 转换前: 汇编格式 (map_data.s)
.global gMapLayout_PetalburgCity
gMapLayout_PetalburgCity:
.byte 30, 20 /* 宽度, 高度 */
.byte 0x01, 0x02 /* 碰撞数据 */
.word 0x06008000 /* 图块映射地址 */
// 转换后: C 数组 (generated)
const u8 gMapLayout_PetalburgCity[] = {
30, 20,
0x01, 0x02,
0x00, 0x80, 0x00, 0x06
};事件与脚本数据
游戏的事件系统和脚本以类似方式转换:
- 事件数据:NPC 位置、道具、触发器
- 脚本命令:对话文本、战斗触发、地图切换
- 战斗数据:精灵属性、招式、道具效果
战斗数据转换
战斗系统需要大量数据表,包括:
| 数据类型 | 说明 | 格式 |
|---|---|---|
| 宝可梦基础数据 | HP、攻击、防御、速度等 | 结构体数组 |
| 招式数据 | 威力、命中率、属性、PP | 结构体数组 |
| 属性相克表 | 18x18 属性克制关系 | 二维数组 |
| 经验值表 | 每个等级所需经验值 | 一维数组 |
| 进化数据 | 进化条件和目标 | 结构体数组 |
| 道具数据 | 道具效果和参数 | 结构体数组 |
这些数据在原始项目中以 GBA 汇编或 C 头文件形式存在。wasm_asm_data.py 负责统一转换,确保数据对齐和字节序正确。
音频数据生成(generate_wasm_sound.py)
GBA 版本的音乐/音效以 MIDI 源文件(sound/songs/midi/)经 mid2agb 转换为 AGB 音频字节码,再汇编进 ROM。WASM 构建无法走这条汇编链路,因此新增了构建时脚本 tools/generate_wasm_sound.py,在编译前把音频数据预生成为 C 头文件 build/wasm/wasm_sound.h:
对应的 Makefile 规则:
makefile
WASM_SOUND_HEADER := $(WASM_BUILD_DIR)/wasm_sound.h
# 由 MIDI/AGB 数据生成音频头文件
$(WASM_SOUND_HEADER): tools/generate_wasm_sound.py sound/song_table.inc sound/songs/midi/midi.cfg $(MID_SRCS)
uv run python tools/generate_wasm_sound.py $@
# m4a.o 显式依赖生成的音频头文件
$(WASM_OBJ_DIR)/m4a.o: $(WASM_SOUND_HEADER)关键点:
| 要点 | 说明 |
|---|---|
| 生成产物 | wasm_sound.h 包含所有歌曲/音效的数据数组与查找表 |
| 依赖注入 | m4a.o 显式依赖 wasm_sound.h;WASM 编译新增 -I $(WASM_BUILD_DIR) 让 m4a.c 能 #include 到它 |
| 修复背景 | 早期 WASM 版本完全跳过音频数据,导致进化、硬化、树果文本等场景因缺少音效数据而崩溃;该脚本补齐了缺失的音频数据 |
该脚本与
generate_wasm_assets.py(图形)、wasm_asm_data.py(汇编数据)并列,是 WASM 资源管线的第三个构建时生成器。参见 源码兼容与适配 中对m4a.c音频适配的说明。
资源嵌入方式对比
关键差异:
- ARM 构建在汇编阶段嵌入数据,零额外开销
- WASM 构建需要 Python 预处理 + C 编译 + 链接,但结果是等价的
- 两种方式生成的数据在内存中的布局完全一致,游戏代码无需修改