Skip to content

源码兼容与适配 — 代码走读

垫片头文件走读

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 指令)
  • snprintfsize 参数使用 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 函数

#endif

include/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)音频系统被跳过
// 不初始化音频硬件,不播放音乐
#endif

GBA 的音频系统直接操作 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 存储空间
#endif

4 处 #if WASM 守卫分别跳过:

  • Flash 芯片检测序列(向硬件发送识别命令)
  • 扇区擦除操作(由 JS 处理)
  • 写入操作(直接修改 WASM 内存,由 JS 定期保存到 localStorage)
  • 等待操作完成(硬件轮询,WASM 中不需要)

src/sound.c — 声音播放适配

c
// src/sound.c (第 608, 630 行)
#if WASM
// 跳过声音播放函数的实现
// 避免调用 GBA 音频硬件寄存器
#endif
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 WASMWasmRunFrame() 前向声明
src/main.c:104#if WASMFlash 存在标志 + 跳过硬件初始化
src/main.c:138#if WASMAgbMain 提前返回(不进入无限循环)
src/main.c:182#if WASM直接调用 VBlankIntr(不等待硬件)
src/m4a.c:24#if WASM跳过音频系统初始化
src/agb_flash.c4 处 #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.c3 处 #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 专用文件,场景特效脚本