Skip to content

资源转换管线 — 概念

资源管线总览

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 构建中的替代方案

处理步骤

  1. 扫描:Python 脚本遍历所有 .c 文件,用正则表达式匹配 INCGFX("...") 调用
  2. 转换:将 GBA 格式的图片数据(.4bpp.8bpp)转换为 C 字节数组
  3. 生成:输出头文件,使 INCGFX 宏展开为 extern 数组引用
c
// WASM 构建中生成的替代代码
// generated_wasm_assets.h
extern const u32 gSprite_Bulbasaur_data[];

// INCGFX 宏重定义为数组引用
#define INCGFX(path)  __referenced_data_##path

支持的图形格式

格式后缀每像素位数颜色数用途
4bpp.4bpp4 位16 色精灵图、背景图块
8bpp.8bpp8 位256 色全彩背景、大图
GB.gb2 位4 色GB 兼容图形
1bpp.1bpp1 位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 编译 + 链接,但结果是等价的
  • 两种方式生成的数据在内存中的布局完全一致,游戏代码无需修改

相关概念