Appearance
源码兼容与适配 — 代码走读
垫片头文件走读
include/wasm/stdio.h
c
// include/wasm/stdio.h — 完整文件
#ifndef GUARD_WASM_STDIO_H
#define GUARD_WASM_STDIO_H
#include <stdarg.h> // 提供 va_list 类型
int sprintf(char *str, const char *format, ...);
int vsprintf(char *str, const char *format, va_list args);
int snprintf(char *str, unsigned long size, const char *format, ...);
#endif要点:
#include <stdarg.h>引入va_list定义,供vsprintf使用- 三个函数只有声明没有实现——由 Clang 编译器内建提供(生成对应的 WASM 指令)
snprintf的size参数使用unsigned long而非size_t,匹配 Clang 内建签名
include/wasm/stdlib.h
c
// include/wasm/stdlib.h — 完整文件
#ifndef GUARD_WASM_STDLIB_H
#define GUARD_WASM_STDLIB_H
int abs(int n); // 游戏代码中唯一使用的 stdlib 函数
#endifinclude/wasm/string.h
c
// include/wasm/string.h — 完整文件
#ifndef GUARD_WASM_STRING_H
#define GUARD_WASM_STRING_H
#include <stddef.h> // 提供 size_t 类型
void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);
void *memset(void *s, int c, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
char *strcat(char *dest, const char *src);
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
size_t strlen(const char *s);
char *strchr(const char *s, int c);
#endif条件编译实例走读
src/main.c — 主循环适配
c
// src/main.c — AgbMain() 中的关键适配 (第 92-144 行)
void AgbMain(void) {
// ... 初始化代码 ...
#if WASM
gFlashMemoryPresent = TRUE; // 直接假定 Flash 存在
#else
m4aSoundInit(); // GBA 音频初始化
EnableVCountIntrAtLine150(); // LCD 扫描线中断
InitRFU(); // 无线通信初始化
RtcInit(); // RTC 时钟初始化
CheckForFlashMemory(); // 检测 Flash 芯片
#endif
// ... 通用初始化代码 ...
#if WASM
return; // WASM: 初始化后返回 JS
#else
for (;;) // GBA: 无限循环
WasmRunFrame();
#endif
}c
// src/main.c — WasmRunFrame() 中的 VBlank 适配 (第 146-187 行)
void WasmRunFrame(void) {
ReadKeys();
// ... 游戏逻辑 ...
#if WASM
VBlankIntr(); // 直接调用 VBlank 处理函数
#else
WaitForVBlank(); // 等待硬件 VBlank 中断
#endif
}src/m4a.c — 音频系统适配
c
// src/m4a.c (第 24 行)
#if WASM
// WASM: 整个 m4a(Music For Advance)音频系统被跳过
// 不初始化音频硬件,不播放音乐
#endifGBA 的音频系统直接操作 Sound DMA/FIFO 硬件寄存器,在 WASM 中无法直接使用。整个音频初始化在 WASM 构建中被跳过。
src/agb_flash.c — Flash 存储适配
c
// src/agb_flash.c (第 11, 152, 207, 257 行)
#if WASM
// 跳过 Flash 芯片硬件检测和切换命令
// 直接使用 JS 运行时通过 localStorage 模拟的 Flash 存储空间
#endif4 处 #if WASM 守卫分别跳过:
- Flash 芯片检测序列(向硬件发送识别命令)
- 扇区擦除操作(由 JS 处理)
- 写入操作(直接修改 WASM 内存,由 JS 定期保存到 localStorage)
- 等待操作完成(硬件轮询,WASM 中不需要)
src/sound.c — 声音播放适配
c
// src/sound.c (第 608, 630 行)
#if WASM
// 跳过声音播放函数的实现
// 避免调用 GBA 音频硬件寄存器
#endifsrc/link.c / src/union_room.c — 链接通信适配
c
// src/link.c (第 239 行)
#if WASM
// 禁用 GBA 链接线通信功能
#endif
// src/union_room.c (第 3296 行)
#if WASM
// 禁用联机房间功能
#endif链接通信依赖 GBA 的串行通信硬件(SIO 寄存器),浏览器环境无法直接模拟。两个文件中的 #if WASM 守卫禁用了所有链接相关功能。
wasm_field_effect_scripts.c 走读
文件结构
核心设计:packed 结构体模拟字节码
c
// 原始 GBA 汇编的字节码序列(示例):
// .byte 3 // CMD_CALL_NATIVE
// .word FldEff_Dust // 函数指针
// .byte 4 // CMD_END
// WASM 中的等效 C 结构体:
struct FieldEffectScriptCallNative {
u8 cmd; // 命令码
FieldEffectFunc func; // 函数指针
u8 end; // 结束标记
} __attribute__((packed));__attribute__((packed)) 确保结构体没有填充字节,内存布局与 ARM 汇编中的字节码序列完全一致:
脚本宏简化定义
c
// 简单脚本:只调用本地函数
#define SCRIPT_CALL_NATIVE(name, func) \
static const struct FieldEffectScriptCallNative sScript_##name = { \
FIELD_EFFECT_SCRIPT_CALL_NATIVE, func, FIELD_EFFECT_SCRIPT_END \
}
// 复合脚本:加载调色板 + 调用本地函数
#define SCRIPT_LOAD_FADED_PAL_CALL_NATIVE(name, palette, func) \
static const struct FieldEffectScriptLoadFadedPalCallNative sScript_##name = { \
FIELD_EFFECT_SCRIPT_LOAD_FADED_PAL_CALL_NATIVE, palette, func, FIELD_EFFECT_SCRIPT_END \
}兼容接口
c
// 最终生成的指针数组,与 GBA 版本接口完全一致
u8 *gFieldEffectScriptPointers[] = {
[FLDEFF_EXCLAMATION_MARK_ICON] = SCRIPT_PTR(ExclamationMarkIcon1),
[FLDEFF_TALL_GRASS] = SCRIPT_PTR(TallGrass),
// ... 60+ 个特效脚本指针
};SCRIPT_PTR(name) 宏将结构体地址转为 u8*,与游戏引擎的脚本执行器(通过字节码遍历器读取)兼容。
关键函数/文件索引
| 文件 | 条件编译位置 | 适配内容 |
|---|---|---|
include/wasm/stdio.h | 全文件 | sprintf/vsprintf/snprintf 声明垫片 |
include/wasm/stdlib.h | 全文件 | abs() 声明垫片 |
include/wasm/string.h | 全文件 | memcpy/memset/strlen 等 11 个函数声明 |
src/main.c:79 | #if WASM | WasmRunFrame() 前向声明 |
src/main.c:104 | #if WASM | Flash 存在标志 + 跳过硬件初始化 |
src/main.c:138 | #if WASM | AgbMain 提前返回(不进入无限循环) |
src/main.c:182 | #if WASM | 直接调用 VBlankIntr(不等待硬件) |
src/m4a.c:24 | #if WASM | 跳过音频系统初始化 |
src/agb_flash.c | 4 处 #if WASM | 跳过 Flash 硬件操作 |
src/sound.c:608,630 | #if WASM | 禁用声音播放 |
src/link.c:239 | #if WASM | 禁用链接通信 |
src/union_room.c:3296 | #if WASM | 禁用联机房间 |
src/field_player_avatar.c | 3 处 #if WASM | 输入系统适配 |
src/pokemon.c:3710,4079 | #if WASM | 宝可梦数据结构适配 |
src/intro.c:1151 | #if WASM | 开场动画适配 |
src/menu.c:1764 | #if WASM | 菜单系统适配 |
src/naming_screen.c:2011 | #if WASM | 命名屏幕适配 |
src/wasm_field_effect_scripts.c | #if WASM ... #endif | 唯一 WASM 专用文件,场景特效脚本 |