@wasm-gaming/mgba-wasm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,203 @@
1
+ # @wasm-gaming/mgba-wasm
2
+
3
+ [mGBA](https://github.com/mgba-emu/mgba) — the accuracy-focused **Game Boy
4
+ Advance** emulator, which also runs **Game Boy** and **Game Boy Color** —
5
+ compiled to WebAssembly via Emscripten and packaged as a wasm-gaming engine SDK.
6
+
7
+ This subproject follows the same engine-package approach used by snes9x-wasm,
8
+ geolith-wasm, fbneo-wasm, jgenesis-wasm, blastem-wasm, and rsdkv*:
9
+
10
+ - typed `manifest`
11
+ - typed `options`
12
+ - `load(config)` engine SDK surface
13
+ - Makefile-driven build (`build-sdk`, `build-wasm`, `preview`)
14
+
15
+ It conforms to the [`@wasm-gaming/engine-specs`](https://github.com/wasm-gaming/engine-specs)
16
+ contract (`EngineSDK` = `{ manifest, load }`), targeting **0.2.6** — 0.2.5 was the first
17
+ version in which the in-game ESC menu belongs to the host's demo shell rather
18
+ than to each engine, so this package ships the option rows and applies the
19
+ writes instead of drawing a menu itself.
20
+
21
+ mGBA ships several frontends (`qt/`, `sdl/`, `libretro/`); none of them suit a
22
+ browser, so this package supplies its own
23
+ ([scripts/shim/mgba_shim.c](scripts/shim/mgba_shim.c)) instead of an Emscripten
24
+ SDL layer: **no ASYNCIFY, no SDL, no emulated GL**. Because mGBA already
25
+ publishes `struct mCore` — a frontend-facing vtable that is identical for both
26
+ its cores — the shim is a thin flat-C ABI over it rather than a reimplemented
27
+ port. The JS SDK drives one `core->runFrame()` per frame, blits the framebuffer
28
+ to a 2D canvas, and streams audio through an `AudioWorklet` ring buffer.
29
+ Emulation is **audio-clocked**: frames are produced to keep ~90 ms of audio
30
+ queued.
31
+
32
+ ## ROM format
33
+
34
+ Pass the cartridge image as the `rom` asset: `.gba`, `.gb`, `.gbc` or `.sgb`.
35
+ **Zipped dumps work too** — the archive is opened in JS
36
+ ([src/mgba.zip.ts](src/mgba.zip.ts)) with `DecompressionStream`, since this
37
+ build carries no libzip.
38
+
39
+ The right core is chosen from the image itself, so a Game Boy ROM and a GBA ROM
40
+ both just work; force it with `options.system` for headerless or mislabelled
41
+ files.
42
+
43
+ ### BIOS
44
+
45
+ **Optional.** mGBA has a high-level BIOS replacement that runs virtually every
46
+ commercial title, so the `bios` asset is only needed for the boot animation and
47
+ the handful of games that read the BIOS directly. Supply a 16 KiB GBA BIOS dump
48
+ as `assets.bios` if you want it; without one, `skipBios` is forced on and drops
49
+ out of the settings menu, because there is no intro to skip.
50
+
51
+ ## Contract surface
52
+
53
+ ```js
54
+ import { manifest, load } from '@wasm-gaming/mgba-wasm';
55
+
56
+ const engine = await load({
57
+ canvasEl: canvas, // or attachTo: containerEl
58
+ assets: {
59
+ rom: gbaFileBytes, // .gba/.gb/.gbc image, or a zip of one
60
+ // bios: gbaBiosBytes, // optional
61
+ },
62
+ options: { system: 'auto', aspect: 'native' },
63
+ persist: 'opfs',
64
+ storageNamespace: 'minish-cap',
65
+ onEvent: (e) => console.log(e),
66
+ });
67
+ engine.start();
68
+
69
+ engine.system; // 'gba' | 'gb' — which core actually booted
70
+ ```
71
+
72
+ ### Options
73
+
74
+ | Option | Default | Description |
75
+ |--------|---------|-------------|
76
+ | `system` | `auto` | Which core boots the ROM: `auto` (sniffed from the image), `gba`, or `gb`. **Needs reset.** |
77
+ | `gbModel` | `auto` | Game Boy hardware: `auto` (from the header), `dmg`, `sgb`, `cgb`, `agb`. Ignored on GBA. **Needs reset.** |
78
+ | `idleOptimization` | `remove` | How the GBA core treats busy-wait loops. `remove` is where nearly all the speed comes from; `ignore` emulates them cycle for cycle. |
79
+ | `skipBios` | `true` | Skip the boot animation. Forced on, and hidden, when no BIOS image was supplied. **Needs reset.** |
80
+ | `allowOpposingDirections` | `false` | Let the D-pad report left+right at once. Real hardware cannot. |
81
+ | `renderFilter` | `pixelated` | Canvas scaling filter (`pixelated` or `smooth`). |
82
+ | `aspect` | `native` | `native` is square pixels — 3:2 on GBA, 10:9 on Game Boy; `4:3` fills a TV-shaped frame. |
83
+ | `interframeBlending` | `false` | Blend consecutive frames, approximating the ghosting of the original unlit LCD. Restores transparency effects games drew by flickering sprites. |
84
+ | `volume` | `1.0` | Master audio volume (0–1). |
85
+ | `gamepads` | `true` | Poll connected gamepads (standard mapping) each frame. |
86
+ | `logLevel` | `error` | Core messages printed to the console (`off`, `error`, `debug`). |
87
+ | `escMenu` | `true` | Let the demo shell show its in-game settings menu on Escape. |
88
+
89
+ ## In-game settings menu
90
+
91
+ As of engine-specs 0.2.5 the ESC menu is a **demo-shell component**, not
92
+ something each engine draws. This package supplies the rows and applies the
93
+ writes:
94
+
95
+ ```js
96
+ demo.init({
97
+ escMenu: {
98
+ optionGroups: toEscMenuGroups(), // from ./options
99
+ onOptionChange: (key, value) => engine.config.write(key, value),
100
+ onRestoreDefaults: () => engine.config.restoreDefaults(),
101
+ },
102
+ });
103
+ ```
104
+
105
+ Most of these apply to the running game: mGBA re-reads its `mCoreConfig` when
106
+ the frontend calls `reloadConfigOption()`, which is how its own Qt frontend
107
+ applies a settings change mid-play. The ones the core consumes while mapping
108
+ the cartridge — `system`, `gbModel`, `skipBios` — are tagged **needs reset**,
109
+ and the menu's *Reset game* re-maps it (battery RAM is carried across).
110
+
111
+ - Changes persist per `storageNamespace` in `localStorage` and are re-applied
112
+ on the next `load()`. Explicit `options` passed by the host still win.
113
+ - Settings the loaded `mgba.wasm` cannot apply — a build predating one of the
114
+ shim's setters — are omitted rather than erroring, so the menu degrades
115
+ cleanly against an older artifact.
116
+
117
+ Every knob is declared once in [src/mgba.options.ts](src/mgba.options.ts); the
118
+ manifest's options schema, the defaults and the menu rows are all derived from
119
+ that catalog, so a new row there is enough to expose a new setting.
120
+
121
+ Drive it yourself with `options: { escMenu: false }`:
122
+
123
+ ```js
124
+ engine.config.write('interframeBlending', true);
125
+ engine.config.read('idleOptimization'); // 'remove'
126
+ ```
127
+
128
+ ### Sizing the canvas
129
+
130
+ The presentation ratio is not a constant a host can hardcode: 3:2 on GBA,
131
+ 10:9 on Game Boy, 8:7 once a Super Game Boy border appears, and 4:3 when the
132
+ player picks `aspect: '4:3'`. So `load()` publishes it on the canvas two ways —
133
+ `aspect-ratio`, which shapes the element, and `--mgba-aspect`, the same number
134
+ in a form arithmetic can use:
135
+
136
+ ```css
137
+ .runtime canvas {
138
+ width: min(100%, calc(100% * var(--mgba-aspect)));
139
+ height: auto;
140
+ }
141
+ ```
142
+
143
+ Both are updated whenever the geometry or the option changes, so a game
144
+ switching resolution re-fits without the host doing anything.
145
+
146
+ ### Capabilities
147
+
148
+ - **Save states**: `saveState()` / `loadState()` via mGBA's in-memory snapshot
149
+ API — 388 KiB on GBA, 70 KiB on Game Boy.
150
+ - **SRAM persistence**: battery-backed savedata is persisted to OPFS
151
+ (`mgba/<storageNamespace>/sram.bin`) on pause/destroy and every 15 s;
152
+ `purgeStorage()` removes the active namespace, along with the menu settings
153
+ saved for it.
154
+ - **Screenshots**: `screenshot()` returns a PNG blob.
155
+ - **`coreSelectable`**: one package, two cores. `options.system` is where a
156
+ host's core choice lands.
157
+
158
+ ### Default controls
159
+
160
+ | Control | Key |
161
+ |---------|-----|
162
+ | D-pad | Arrow keys |
163
+ | A / B | X / Z |
164
+ | L / R | A / S |
165
+ | Start | Enter |
166
+ | Select | Right Shift |
167
+
168
+ Gamepads (standard mapping) are polled automatically. Rebind via
169
+ `engine.setInput({ 'a': 'KeyJ', ... })` (KeyboardEvent codes); a `p1.` prefix is
170
+ accepted but not required, since the handheld has one controller.
171
+
172
+ ## Build
173
+
174
+ ```sh
175
+ make build # Full build: WASM (Docker/Emscripten) + TypeScript SDK
176
+ make build-sdk # TypeScript only (SDK + manifest + demo shell)
177
+ make build-wasm # mGBA WASM only (via Docker)
178
+ make preview # Serve dist/ at :8030 with COOP/COEP headers
179
+ make smoke # Headless boot test (needs a ROM in roms/)
180
+ ```
181
+
182
+ The WASM build clones a pinned mGBA revision and builds it with its own CMake —
183
+ `DISABLE_FRONTENDS` plus `DISABLE_DEPS` is exactly the "just the emulator"
184
+ configuration this needs, and it generates `version.c` and `flags.h` along the
185
+ way — then links the shim against the resulting `libmgba.a`. No upstream
186
+ patching.
187
+
188
+ ## WASM artifacts
189
+
190
+ | File | Description |
191
+ |------|-------------|
192
+ | `mgba.js` | Emscripten module loader (`createMgbaModule`). |
193
+ | `mgba.wasm` | Compiled mGBA core + shim (~790 KB). |
194
+
195
+ No SharedArrayBuffer or COOP/COEP headers are required at runtime (the preview
196
+ server sets them anyway for parity with sibling engines).
197
+
198
+ See [CORE.md](CORE.md) for the mapping between upstream mGBA capabilities and
199
+ what this wrapper exposes, and for the Emscripten-specific build traps.
200
+
201
+ ## License
202
+
203
+ The SDK and shim are MPL-2.0, matching mGBA.
@@ -0,0 +1,146 @@
1
+ {
2
+ "id": "mgba",
3
+ "version": "0.1.0",
4
+ "name": "mGBA (WebAssembly)",
5
+ "description": "mGBA — the accuracy-focused Game Boy Advance emulator, which also runs Game Boy and Game Boy Color — compiled to WebAssembly. Loads .gba/.gb/.gbc cartridge images; the GBA BIOS is optional, since mGBA ships a high-level replacement.",
6
+ "artifacts": {
7
+ "wasm": "mgba/mgba.wasm",
8
+ "js": "mgba/mgba.js"
9
+ },
10
+ "assets": [
11
+ {
12
+ "key": "rom",
13
+ "mountPath": "/rom.gba",
14
+ "required": true,
15
+ "accept": [
16
+ ".gba",
17
+ ".gb",
18
+ ".gbc",
19
+ ".sgb"
20
+ ],
21
+ "description": "Game Boy Advance, Game Boy or Game Boy Color cartridge image. The right core is picked from the image itself, so the extension only has to be one mGBA recognizes."
22
+ },
23
+ {
24
+ "key": "bios",
25
+ "mountPath": "/bios.bin",
26
+ "required": false,
27
+ "accept": [
28
+ ".bin",
29
+ ".rom"
30
+ ],
31
+ "description": "Optional GBA BIOS dump (16 KiB). mGBA has a high-level replacement that runs virtually everything, so this is only needed for the boot animation and the handful of titles that read the BIOS directly.",
32
+ "validate": {
33
+ "bytes": 16384
34
+ }
35
+ }
36
+ ],
37
+ "input": "mgba",
38
+ "video": {
39
+ "baseWidth": 240,
40
+ "baseHeight": 160,
41
+ "aspect": "3:2"
42
+ },
43
+ "options": {
44
+ "type": "object",
45
+ "additionalProperties": false,
46
+ "properties": {
47
+ "renderFilter": {
48
+ "type": "string",
49
+ "enum": [
50
+ "pixelated",
51
+ "smooth"
52
+ ],
53
+ "default": "pixelated",
54
+ "description": "Scaling filter applied when the picture is stretched to the canvas. Pixelated keeps pixel art crisp; smooth softens it."
55
+ },
56
+ "aspect": {
57
+ "type": "string",
58
+ "enum": [
59
+ "native",
60
+ "4:3"
61
+ ],
62
+ "default": "native",
63
+ "description": "Native is square pixels, as the handheld LCDs had them: 3:2 on GBA, 10:9 on Game Boy. 4:3 stretches the picture into a TV-shaped frame."
64
+ },
65
+ "interframeBlending": {
66
+ "type": "boolean",
67
+ "default": false,
68
+ "description": "Blend each frame with the previous one, approximating the ghosting of the original unlit LCD. Restores transparency effects that games drew by flickering sprites every other frame."
69
+ },
70
+ "volume": {
71
+ "type": "number",
72
+ "default": 1,
73
+ "minimum": 0,
74
+ "maximum": 1,
75
+ "description": "Master audio volume."
76
+ },
77
+ "system": {
78
+ "type": "string",
79
+ "enum": [
80
+ "auto",
81
+ "gba",
82
+ "gb"
83
+ ],
84
+ "default": "auto",
85
+ "description": "Which core boots the ROM. Auto sniffs the image, which is right for every well-formed dump; force it for headerless or mislabelled files."
86
+ },
87
+ "gbModel": {
88
+ "type": "string",
89
+ "enum": [
90
+ "auto",
91
+ "dmg",
92
+ "sgb",
93
+ "cgb",
94
+ "agb"
95
+ ],
96
+ "default": "auto",
97
+ "description": "Hardware the Game Boy core emulates. Auto follows the cartridge header. Ignored when a GBA ROM is loaded."
98
+ },
99
+ "idleOptimization": {
100
+ "type": "string",
101
+ "enum": [
102
+ "remove",
103
+ "detect",
104
+ "ignore"
105
+ ],
106
+ "default": "remove",
107
+ "description": "How the GBA core treats the busy-wait loops games spin in. Remove skips them and is where nearly all of the speed comes from; ignore emulates them cycle for cycle."
108
+ },
109
+ "skipBios": {
110
+ "type": "boolean",
111
+ "default": true,
112
+ "description": "Jump straight into the game instead of playing the boot animation. Forced on when no BIOS image was supplied, since the built-in HLE BIOS has no intro."
113
+ },
114
+ "allowOpposingDirections": {
115
+ "type": "boolean",
116
+ "default": false,
117
+ "description": "Let the D-pad report left+right (or up+down) at once. Real hardware cannot, and a few games misbehave when it happens."
118
+ },
119
+ "gamepads": {
120
+ "type": "boolean",
121
+ "default": true,
122
+ "description": "Poll connected gamepads (standard mapping) each frame."
123
+ },
124
+ "logLevel": {
125
+ "type": "string",
126
+ "enum": [
127
+ "off",
128
+ "error",
129
+ "debug"
130
+ ],
131
+ "default": "error",
132
+ "description": "Core messages printed to the browser console: errors and warnings, or also its informational ones."
133
+ },
134
+ "escMenu": {
135
+ "type": "boolean",
136
+ "default": true,
137
+ "description": "Show the demo shell's in-game settings menu when the player presses Escape."
138
+ }
139
+ }
140
+ },
141
+ "capabilities": {
142
+ "saveStates": true,
143
+ "sram": true,
144
+ "coreSelectable": true
145
+ }
146
+ }
@@ -0,0 +1,41 @@
1
+ import { type MgbaOptionKey, type MgbaOptionValue } from './mgba.options.js';
2
+ /**
3
+ * Pushes a value into the running emulator: an `mCoreConfig` write followed by
4
+ * `reloadConfigOption()`, a canvas style change, a gain node — whatever that
5
+ * option means. Called after the value has been validated and recorded.
6
+ */
7
+ export type MgbaApplier = (value: MgbaOptionValue) => void;
8
+ /**
9
+ * The appliers the SDK installs for the build it actually loaded. An option
10
+ * with no applier is reported as unsupported and disappears from the menu,
11
+ * which is what keeps this package usable against an `mgba.wasm` built before
12
+ * a given shim setter existed.
13
+ */
14
+ export type MgbaAppliers = Partial<Record<MgbaOptionKey, MgbaApplier>>;
15
+ /**
16
+ * Typed façade over the emulator's live settings.
17
+ *
18
+ * mGBA does own a configuration object — the `mCoreConfig` embedded in every
19
+ * `mCore` — but it is C-side and the SDK keeps the rest (canvas, audio graph,
20
+ * frame blending) in JS, so `state` is the record both this façade and the
21
+ * running SDK read from, and every write goes through the matching applier.
22
+ */
23
+ export interface MgbaConfig {
24
+ supports(key: string): boolean;
25
+ read(key: string): MgbaOptionValue | undefined;
26
+ /** Returns `false` when the key is unknown, unsupported, or the value invalid. */
27
+ write(key: string, value: MgbaOptionValue): boolean;
28
+ /** Current value of every supported option. */
29
+ values(): Record<string, MgbaOptionValue>;
30
+ /** Restores this package's declared defaults. */
31
+ restoreDefaults(): void;
32
+ }
33
+ export declare function bindConfig(state: Record<string, MgbaOptionValue>, appliers: MgbaAppliers): MgbaConfig;
34
+ /** Per-namespace persistence for menu tweaks, so they survive a page reload. */
35
+ export interface MgbaSettingsStore {
36
+ load(): Record<string, MgbaOptionValue>;
37
+ save(values: Record<string, MgbaOptionValue>): void;
38
+ clear(): void;
39
+ }
40
+ export declare function createSettingsStore(namespace: string): MgbaSettingsStore;
41
+ //# sourceMappingURL=mgba.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mgba.config.d.ts","sourceRoot":"","sources":["../../src/mgba.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,mBAAmB,CAAC;AAE3B;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC;AAEvE;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IAC/C,kFAAkF;IAClF,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC;IACpD,+CAA+C;IAC/C,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC1C,iDAAiD;IACjD,eAAe,IAAI,IAAI,CAAC;CACzB;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EACtC,QAAQ,EAAE,YAAY,GACrB,UAAU,CAwCZ;AAED,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IAChC,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,IAAI,CAAC;IACpD,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAkDxE"}
@@ -0,0 +1,93 @@
1
+ import { coerceOptionValue, mgbaOption, MGBA_ENGINE_OPTIONS, } from './mgba.options.js';
2
+ export function bindConfig(state, appliers) {
3
+ const applierFor = (key) => appliers[key];
4
+ const supports = (key) => Boolean(mgbaOption(key)) && applierFor(key) !== undefined;
5
+ const read = (key) => mgbaOption(key) ? state[key] : undefined;
6
+ const write = (key, value) => {
7
+ const option = mgbaOption(key);
8
+ const apply = applierFor(key);
9
+ if (!option || !apply)
10
+ return false;
11
+ const next = coerceOptionValue(option, value);
12
+ if (next === undefined)
13
+ return false;
14
+ state[key] = next;
15
+ apply(next);
16
+ return true;
17
+ };
18
+ return {
19
+ supports,
20
+ read,
21
+ write,
22
+ values() {
23
+ const snapshot = {};
24
+ for (const option of MGBA_ENGINE_OPTIONS) {
25
+ if (supports(option.key))
26
+ snapshot[option.key] = state[option.key];
27
+ }
28
+ return snapshot;
29
+ },
30
+ restoreDefaults() {
31
+ for (const option of MGBA_ENGINE_OPTIONS) {
32
+ write(option.key, option.default);
33
+ }
34
+ },
35
+ };
36
+ }
37
+ export function createSettingsStore(namespace) {
38
+ const storageKey = `mgba:options:${namespace}`;
39
+ // Storage access throws outright in some privacy modes, so every call is
40
+ // guarded — losing persistence must never take the emulator down with it.
41
+ const storage = () => {
42
+ try {
43
+ return typeof localStorage === 'undefined' ? null : localStorage;
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ };
49
+ return {
50
+ load() {
51
+ try {
52
+ const raw = storage()?.getItem(storageKey);
53
+ if (!raw)
54
+ return {};
55
+ const parsed = JSON.parse(raw);
56
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
57
+ return {};
58
+ // Anything the catalog no longer recognizes is dropped rather than fed
59
+ // back into the core: this is user-writable storage.
60
+ const values = {};
61
+ for (const [key, value] of Object.entries(parsed)) {
62
+ const option = mgbaOption(key);
63
+ if (!option)
64
+ continue;
65
+ const coerced = coerceOptionValue(option, value);
66
+ if (coerced !== undefined)
67
+ values[key] = coerced;
68
+ }
69
+ return values;
70
+ }
71
+ catch {
72
+ return {};
73
+ }
74
+ },
75
+ save(values) {
76
+ try {
77
+ storage()?.setItem(storageKey, JSON.stringify(values));
78
+ }
79
+ catch {
80
+ // persistence is best-effort
81
+ }
82
+ },
83
+ clear() {
84
+ try {
85
+ storage()?.removeItem(storageKey);
86
+ }
87
+ catch {
88
+ // persistence is best-effort
89
+ }
90
+ },
91
+ };
92
+ }
93
+ //# sourceMappingURL=mgba.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mgba.config.js","sourceRoot":"","sources":["../../src/mgba.config.ts"],"names":[],"mappings":"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"}