@wasm-gaming/fbneo-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.
@@ -0,0 +1,4 @@
1
+ import type { EngineManifest } from '@wasm-gaming/engine-specs';
2
+ export declare const manifest: EngineManifest;
3
+ export default manifest;
4
+ //# sourceMappingURL=fbneo.manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fbneo.manifest.d.ts","sourceRoot":"","sources":["../../src/fbneo.manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,eAAO,MAAM,QAAQ,EAAE,cAgCtB,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { FBNEO_OPTIONS_SCHEMA } from './fbneo.options.js';
2
+ export const manifest = {
3
+ id: 'fbneo',
4
+ version: '0.1.0',
5
+ name: 'FinalBurn Neo (WebAssembly)',
6
+ description: 'FBNeo arcade emulator (Capcom CPS-1/2/3, SNK Neo Geo, Sega System 16, and more) compiled to WebAssembly.',
7
+ artifacts: {
8
+ wasm: 'fbneo/fbneo.wasm',
9
+ js: 'fbneo/fbneo.js',
10
+ },
11
+ assets: [
12
+ {
13
+ key: 'rom',
14
+ mountPath: '/roms/game.zip',
15
+ required: true,
16
+ accept: ['.zip', '.7z'],
17
+ description: 'Arcade ROM set (zip) written to Emscripten MEMFS. FBNeo identifies the game by the zip filename.',
18
+ },
19
+ {
20
+ key: 'bios',
21
+ mountPath: '/roms/neogeo.zip',
22
+ required: false,
23
+ accept: ['.zip'],
24
+ description: 'Optional system BIOS set (e.g. neogeo.zip for Neo Geo, or a CPS-3 BIOS). Required only by ROM sets that depend on it.',
25
+ },
26
+ ],
27
+ input: 'fbneo',
28
+ video: { baseWidth: 384, baseHeight: 224, aspect: '4:3' },
29
+ options: FBNEO_OPTIONS_SCHEMA,
30
+ capabilities: { saveStates: false, sram: false, coreSelectable: false },
31
+ };
32
+ export default manifest;
33
+ //# sourceMappingURL=fbneo.manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fbneo.manifest.js","sourceRoot":"","sources":["../../src/fbneo.manifest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,CAAC,MAAM,QAAQ,GAAmB;IACtC,EAAE,EAAE,OAAO;IACX,OAAO,EAAE,OAAO;IAChB,IAAI,EAAE,6BAA6B;IACnC,WAAW,EACT,0GAA0G;IAC5G,SAAS,EAAE;QACT,IAAI,EAAE,kBAAkB;QACxB,EAAE,EAAE,gBAAgB;KACrB;IACD,MAAM,EAAE;QACN;YACE,GAAG,EAAE,KAAK;YACV,SAAS,EAAE,gBAAgB;YAC3B,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC;YACvB,WAAW,EACT,kGAAkG;SACrG;QACD;YACE,GAAG,EAAE,MAAM;YACX,SAAS,EAAE,kBAAkB;YAC7B,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,CAAC,MAAM,CAAC;YAChB,WAAW,EACT,uHAAuH;SAC1H;KACF;IACD,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE;IACzD,OAAO,EAAE,oBAAoB;IAC7B,YAAY,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE;CACxE,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { JSONSchema } from '@wasm-gaming/engine-specs';
2
+ export interface FbneoOptions {
3
+ /**
4
+ * Filename used when writing the ROM zip bytes to Emscripten MEMFS.
5
+ * FBNeo identifies a game by the zip filename (without extension), so this
6
+ * should match the driver's expected ROM set name (e.g. "mslug" for Metal Slug).
7
+ */
8
+ romFileName?: string;
9
+ /**
10
+ * The FBNeo driver/game name to load (e.g. "mslug", "sf2", "kof98").
11
+ * If omitted, the driver is inferred from `romFileName` (without extension).
12
+ */
13
+ driver?: string;
14
+ /** Canvas scaling filter: `pixelated` for crisp pixels, `smooth` for linear filtering. */
15
+ renderFilter?: 'pixelated' | 'smooth';
16
+ /** Target audio sample rate in Hz. FBNeo defaults to 44100. */
17
+ audioSampleRate?: number;
18
+ /** Enable vertical sync. Defaults to true. */
19
+ vsync?: boolean;
20
+ /** Use integer scaling (no fractional stretching). Defaults to false. */
21
+ integerScale?: boolean;
22
+ /** Window scale multiplier for the internal render resolution. Defaults to 2. */
23
+ windowScale?: number;
24
+ }
25
+ export declare const DEFAULT_FBNEO_OPTIONS: Required<FbneoOptions>;
26
+ export declare const FBNEO_OPTIONS_SCHEMA: JSONSchema;
27
+ //# sourceMappingURL=fbneo.options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fbneo.options.d.ts","sourceRoot":"","sources":["../../src/fbneo.options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,YAAY,CAAC,EAAE,WAAW,GAAG,QAAQ,CAAC;IACtC,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yEAAyE;IACzE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,YAAY,CAQxD,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,UA8ClC,CAAC"}
@@ -0,0 +1,56 @@
1
+ export const DEFAULT_FBNEO_OPTIONS = {
2
+ romFileName: 'game.zip',
3
+ driver: '',
4
+ renderFilter: 'pixelated',
5
+ audioSampleRate: 44100,
6
+ vsync: true,
7
+ integerScale: false,
8
+ windowScale: 2,
9
+ };
10
+ export const FBNEO_OPTIONS_SCHEMA = {
11
+ type: 'object',
12
+ additionalProperties: false,
13
+ properties: {
14
+ romFileName: {
15
+ type: 'string',
16
+ default: 'game.zip',
17
+ description: 'ROM zip filename written to Emscripten MEMFS when loading a game.',
18
+ },
19
+ driver: {
20
+ type: 'string',
21
+ default: '',
22
+ description: 'FBNeo driver/game name to load (e.g. "mslug"). If empty, inferred from romFileName without extension.',
23
+ },
24
+ renderFilter: {
25
+ type: 'string',
26
+ enum: ['pixelated', 'smooth'],
27
+ default: 'pixelated',
28
+ description: 'Canvas scaling filter: pixelated for crisp pixels, smooth for linear filtering.',
29
+ },
30
+ audioSampleRate: {
31
+ type: 'integer',
32
+ default: 44100,
33
+ minimum: 11025,
34
+ maximum: 96000,
35
+ description: 'Target audio sample rate in Hz.',
36
+ },
37
+ vsync: {
38
+ type: 'boolean',
39
+ default: true,
40
+ description: 'Enable vertical sync.',
41
+ },
42
+ integerScale: {
43
+ type: 'boolean',
44
+ default: false,
45
+ description: 'Use integer scaling (no fractional stretching).',
46
+ },
47
+ windowScale: {
48
+ type: 'integer',
49
+ default: 2,
50
+ minimum: 1,
51
+ maximum: 8,
52
+ description: 'Window scale multiplier for the internal render resolution.',
53
+ },
54
+ },
55
+ };
56
+ //# sourceMappingURL=fbneo.options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fbneo.options.js","sourceRoot":"","sources":["../../src/fbneo.options.ts"],"names":[],"mappings":"AA0BA,MAAM,CAAC,MAAM,qBAAqB,GAA2B;IAC3D,WAAW,EAAE,UAAU;IACvB,MAAM,EAAE,EAAE;IACV,YAAY,EAAE,WAAW;IACzB,eAAe,EAAE,KAAK;IACtB,KAAK,EAAE,IAAI;IACX,YAAY,EAAE,KAAK;IACnB,WAAW,EAAE,CAAC;CACf,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAe;IAC9C,IAAI,EAAE,QAAQ;IACd,oBAAoB,EAAE,KAAK;IAC3B,UAAU,EAAE;QACV,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,UAAU;YACnB,WAAW,EAAE,mEAAmE;SACjF;QACD,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,EAAE;YACX,WAAW,EACT,uGAAuG;SAC1G;QACD,YAAY,EAAE;YACZ,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,WAAW,EAAE,QAAQ,CAAC;YAC7B,OAAO,EAAE,WAAW;YACpB,WAAW,EAAE,iFAAiF;SAC/F;QACD,eAAe,EAAE;YACf,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,KAAK;YACd,WAAW,EAAE,iCAAiC;SAC/C;QACD,KAAK,EAAE;YACL,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,uBAAuB;SACrC;QACD,YAAY,EAAE;YACZ,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,KAAK;YACd,WAAW,EAAE,iDAAiD;SAC/D;QACD,WAAW,EAAE;YACX,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,6DAA6D;SAC3E;KACF;CACF,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { EngineConfig, EngineInstance } from '@wasm-gaming/engine-specs';
2
+ import { manifest } from './fbneo.manifest.js';
3
+ export { manifest };
4
+ export declare function load(config: EngineConfig): Promise<EngineInstance>;
5
+ declare const _default: {
6
+ manifest: import("@wasm-gaming/engine-specs").EngineManifest;
7
+ load: typeof load;
8
+ };
9
+ export default _default;
10
+ //# sourceMappingURL=fbneo.sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fbneo.sdk.d.ts","sourceRoot":"","sources":["../../src/fbneo.sdk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,YAAY,EAEZ,cAAc,EAGf,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAG/C,OAAO,EAAE,QAAQ,EAAE,CAAC;AAyGpB,wBAAsB,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAyIxE;;;;;AAED,wBAAkC"}
@@ -0,0 +1,199 @@
1
+ import { manifest } from './fbneo.manifest.js';
2
+ import { DEFAULT_FBNEO_OPTIONS } from './fbneo.options.js';
3
+ export { manifest };
4
+ const scriptLoadCache = new Map();
5
+ function loadClassicScriptOnce(src) {
6
+ const cached = scriptLoadCache.get(src);
7
+ if (cached)
8
+ return cached;
9
+ const p = new Promise((resolve, reject) => {
10
+ const script = document.createElement('script');
11
+ script.src = src;
12
+ script.async = true;
13
+ script.onload = () => resolve();
14
+ script.onerror = () => reject(new Error(`fbneo: failed to load script: ${src}`));
15
+ document.head.appendChild(script);
16
+ });
17
+ scriptLoadCache.set(src, p);
18
+ return p;
19
+ }
20
+ function toUint8(x) {
21
+ if (x == null)
22
+ return null;
23
+ if (typeof x === 'string')
24
+ return new TextEncoder().encode(x);
25
+ if (x instanceof Uint8Array)
26
+ return x;
27
+ if (x instanceof ArrayBuffer)
28
+ return new Uint8Array(x);
29
+ if (ArrayBuffer.isView(x))
30
+ return new Uint8Array(x.buffer, x.byteOffset, x.byteLength);
31
+ throw new TypeError('fbneo: asset must be Uint8Array | ArrayBuffer | string');
32
+ }
33
+ /** Strip a directory and extension from a zip filename to get the FBNeo driver name. */
34
+ function driverFromFileName(fileName) {
35
+ const base = fileName.replace(/^.*[\\/]/, '');
36
+ return base.replace(/\.(zip|7z)$/i, '');
37
+ }
38
+ /**
39
+ * Resolve the render canvas from the contract's `EngineConfig`.
40
+ * The contract is a union: hosts pass either `canvasEl` (a render target) or
41
+ * `attachTo` (a container the SDK mounts its own canvas into). Legacy `canvas`
42
+ * is still accepted as a defensive fallback for hosts built against 0.1.x.
43
+ */
44
+ function resolveCanvas(config) {
45
+ const c = config.canvasEl ??
46
+ config.canvas;
47
+ if (c)
48
+ return c;
49
+ const attachTo = config.attachTo;
50
+ if (attachTo) {
51
+ const existing = attachTo.querySelector('canvas');
52
+ if (existing)
53
+ return existing;
54
+ const created = document.createElement('canvas');
55
+ attachTo.appendChild(created);
56
+ return created;
57
+ }
58
+ throw new Error('fbneo: config.canvasEl or config.attachTo is required');
59
+ }
60
+ /**
61
+ * Ensure the canvas used by FBNeo has `id="canvas"`.
62
+ * Emscripten SDL2 queries `document.querySelector('#canvas')` to locate its render target.
63
+ */
64
+ function prepareCanvas(canvas, renderFilter) {
65
+ if (canvas.id !== 'canvas')
66
+ canvas.id = 'canvas';
67
+ if (!canvas.width)
68
+ canvas.width = manifest.video.baseWidth;
69
+ if (!canvas.height)
70
+ canvas.height = manifest.video.baseHeight;
71
+ // SDL2's Emscripten backend measures getBoundingClientRect() (borders/padding
72
+ // included) to detect external CSS sizing; decoration on the canvas itself
73
+ // makes it create a tiny window. Host pages should decorate a wrapper instead.
74
+ canvas.style.border = '0';
75
+ canvas.style.padding = '0';
76
+ canvas.style.imageRendering = renderFilter === 'pixelated' ? 'pixelated' : 'auto';
77
+ return canvas;
78
+ }
79
+ export async function load(config) {
80
+ const { assets, onEvent } = config;
81
+ const emit = (e) => {
82
+ try {
83
+ onEvent?.(e);
84
+ }
85
+ catch {
86
+ // host callback must not break the engine runtime
87
+ }
88
+ };
89
+ // Generic hosts (e.g. the engine-specs demo shell) pass the picked file's name
90
+ // as `options.fileName`; treat it as an alias for `romFileName` so the driver
91
+ // can be inferred from the ROM's filename without a FBNeo-specific option.
92
+ const rawOptions = config.options;
93
+ const opts = {
94
+ ...DEFAULT_FBNEO_OPTIONS,
95
+ ...rawOptions,
96
+ romFileName: rawOptions?.romFileName ??
97
+ rawOptions?.fileName ??
98
+ DEFAULT_FBNEO_OPTIONS.romFileName,
99
+ };
100
+ const romBytes = toUint8(assets?.rom ?? assets?.data);
101
+ if (!romBytes) {
102
+ throw new Error('fbneo: no ROM bytes provided — pass assets.rom (an arcade ROM zip)');
103
+ }
104
+ const biosBytes = toUint8(assets?.bios);
105
+ const canvas = prepareCanvas(resolveCanvas(config), opts.renderFilter);
106
+ // FBNeo boots a game by its ROM-set short name ("driver", e.g. "mslug") and
107
+ // searches its ROM paths for a matching `<driver>.zip`. Prefer an explicit
108
+ // `options.driver`; otherwise derive it from the ROM filename (stripping the
109
+ // extension). Whatever the source, the zip is mounted as `<driver>.zip` so the
110
+ // name FBNeo looks up and the name on disk always agree.
111
+ const driver = (opts.driver || driverFromFileName(opts.romFileName)).trim();
112
+ if (!driver || driver === 'game') {
113
+ throw new Error('fbneo: could not determine the ROM-set name. Pass options.driver (the FBNeo ' +
114
+ 'short name, e.g. "mslug") or name the ROM file after it (e.g. mslug.zip).');
115
+ }
116
+ const romFileName = `${driver}.zip`;
117
+ const jsUrl = config.jsUrl ?? new URL('./fbneo.js', import.meta.url).href;
118
+ const wasmUrl = config.wasmUrl ?? new URL('./fbneo.wasm', jsUrl).href;
119
+ let modRef = null;
120
+ let readyResolve = () => { };
121
+ const readyPromise = new Promise((resolve) => {
122
+ readyResolve = resolve;
123
+ });
124
+ // FBNeo's default ROM search paths (src/burner/sdl/drv.cpp) are the absolute
125
+ // "/usr/local/share/roms/" and the cwd-relative "roms/". Under Emscripten the
126
+ // cwd is not guaranteed to be "/", so write to the absolute path (always
127
+ // searched) as well as "/roms/". FBNeo matches ROM files inside each zip by
128
+ // name/CRC, so the zip must be named after the driver.
129
+ const romDirs = ['/usr/local/share/roms', '/roms'];
130
+ const writeRoms = (mod) => {
131
+ for (const dir of romDirs) {
132
+ mod.FS.mkdirTree(dir);
133
+ mod.FS.writeFile(`${dir}/${romFileName}`, romBytes);
134
+ if (biosBytes)
135
+ mod.FS.writeFile(`${dir}/neogeo.zip`, biosBytes);
136
+ }
137
+ };
138
+ const moduleOverrides = {
139
+ canvas,
140
+ noInitialRun: true,
141
+ locateFile(path) {
142
+ if (path.endsWith('.wasm'))
143
+ return wasmUrl;
144
+ return new URL(path, jsUrl).href;
145
+ },
146
+ };
147
+ await loadClassicScriptOnce(jsUrl);
148
+ const g = globalThis;
149
+ // Our build is a MODULARIZE factory (createFbneoModule). The factory promise
150
+ // resolves *after* the runtime is initialized and FS is available, so this is
151
+ // the correct point to populate MEMFS — write the ROMs before callMain().
152
+ if (typeof g.createFbneoModule === 'function') {
153
+ modRef = await g.createFbneoModule(moduleOverrides);
154
+ }
155
+ else if (g.Module) {
156
+ modRef = g.Module;
157
+ }
158
+ else {
159
+ throw new Error('fbneo: unable to initialize runtime module from fbneo.js');
160
+ }
161
+ writeRoms(modRef);
162
+ readyResolve();
163
+ await readyPromise;
164
+ let running = false;
165
+ const boot = () => {
166
+ if (running)
167
+ return;
168
+ running = true;
169
+ // FBNeo SDL takes the ROM set short name and searches its rom paths for it.
170
+ modRef.callMain([driver]);
171
+ emit({ type: 'ready' });
172
+ };
173
+ const setInput = (_map) => {
174
+ // Input is handled by the Emscripten SDL2 event loop; no external override yet.
175
+ };
176
+ return {
177
+ start() {
178
+ boot();
179
+ },
180
+ pause() {
181
+ modRef?.pauseMainLoop?.();
182
+ },
183
+ resume() {
184
+ modRef?.resumeMainLoop?.();
185
+ },
186
+ reset() {
187
+ // FBNeo does not expose a soft-reset hook through this WASM build.
188
+ throw new Error('fbneo: reset is not supported by this build');
189
+ },
190
+ setInput,
191
+ destroy() {
192
+ modRef?.pauseMainLoop?.();
193
+ running = false;
194
+ emit({ type: 'exit' });
195
+ },
196
+ };
197
+ }
198
+ export default { manifest, load };
199
+ //# sourceMappingURL=fbneo.sdk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fbneo.sdk.js","sourceRoot":"","sources":["../../src/fbneo.sdk.ts"],"names":[],"mappings":"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"}
Binary file
@@ -0,0 +1,91 @@
1
+ {
2
+ "id": "fbneo",
3
+ "version": "0.1.0",
4
+ "name": "FinalBurn Neo (WebAssembly)",
5
+ "description": "FBNeo arcade emulator (Capcom CPS-1/2/3, SNK Neo Geo, Sega System 16, and more) compiled to WebAssembly.",
6
+ "artifacts": {
7
+ "wasm": "fbneo/fbneo.wasm",
8
+ "js": "fbneo/fbneo.js"
9
+ },
10
+ "assets": [
11
+ {
12
+ "key": "rom",
13
+ "mountPath": "/roms/game.zip",
14
+ "required": true,
15
+ "accept": [
16
+ ".zip",
17
+ ".7z"
18
+ ],
19
+ "description": "Arcade ROM set (zip) written to Emscripten MEMFS. FBNeo identifies the game by the zip filename."
20
+ },
21
+ {
22
+ "key": "bios",
23
+ "mountPath": "/roms/neogeo.zip",
24
+ "required": false,
25
+ "accept": [
26
+ ".zip"
27
+ ],
28
+ "description": "Optional system BIOS set (e.g. neogeo.zip for Neo Geo, or a CPS-3 BIOS). Required only by ROM sets that depend on it."
29
+ }
30
+ ],
31
+ "input": "fbneo",
32
+ "video": {
33
+ "baseWidth": 384,
34
+ "baseHeight": 224,
35
+ "aspect": "4:3"
36
+ },
37
+ "options": {
38
+ "type": "object",
39
+ "additionalProperties": false,
40
+ "properties": {
41
+ "romFileName": {
42
+ "type": "string",
43
+ "default": "game.zip",
44
+ "description": "ROM zip filename written to Emscripten MEMFS when loading a game."
45
+ },
46
+ "driver": {
47
+ "type": "string",
48
+ "default": "",
49
+ "description": "FBNeo driver/game name to load (e.g. \"mslug\"). If empty, inferred from romFileName without extension."
50
+ },
51
+ "renderFilter": {
52
+ "type": "string",
53
+ "enum": [
54
+ "pixelated",
55
+ "smooth"
56
+ ],
57
+ "default": "pixelated",
58
+ "description": "Canvas scaling filter: pixelated for crisp pixels, smooth for linear filtering."
59
+ },
60
+ "audioSampleRate": {
61
+ "type": "integer",
62
+ "default": 44100,
63
+ "minimum": 11025,
64
+ "maximum": 96000,
65
+ "description": "Target audio sample rate in Hz."
66
+ },
67
+ "vsync": {
68
+ "type": "boolean",
69
+ "default": true,
70
+ "description": "Enable vertical sync."
71
+ },
72
+ "integerScale": {
73
+ "type": "boolean",
74
+ "default": false,
75
+ "description": "Use integer scaling (no fractional stretching)."
76
+ },
77
+ "windowScale": {
78
+ "type": "integer",
79
+ "default": 2,
80
+ "minimum": 1,
81
+ "maximum": 8,
82
+ "description": "Window scale multiplier for the internal render resolution."
83
+ }
84
+ }
85
+ },
86
+ "capabilities": {
87
+ "saveStates": false,
88
+ "sram": false,
89
+ "coreSelectable": false
90
+ }
91
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@wasm-gaming/fbneo-wasm",
3
+ "version": "0.1.0",
4
+ "description": "FBNeo (FinalBurn Neo arcade emulator) compiled to WebAssembly via Emscripten, with a JS SDK conforming to the wasm-gaming engine contract.",
5
+ "type": "module",
6
+ "main": "./dist/fbneo/fbneo.sdk.js",
7
+ "types": "./dist/fbneo/fbneo.sdk.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/fbneo/fbneo.sdk.d.ts",
11
+ "import": "./dist/fbneo/fbneo.sdk.js"
12
+ },
13
+ "./options": {
14
+ "types": "./dist/fbneo/fbneo.options.d.ts",
15
+ "import": "./dist/fbneo/fbneo.options.js"
16
+ },
17
+ "./manifest": "./dist/manifest.json"
18
+ },
19
+ "files": [
20
+ "dist/fbneo/",
21
+ "dist/manifest.json",
22
+ "src/fbneo.manifest.ts",
23
+ "src/fbneo.options.ts",
24
+ "src/fbneo.sdk.ts",
25
+ "README.md"
26
+ ],
27
+ "comment:scripts": "Build logic lives in the Makefile, not here (make build / build-sdk / build-wasm / preview).",
28
+ "scripts": {},
29
+ "peerDependencies": {
30
+ "@wasm-gaming/engine-specs": "*"
31
+ },
32
+ "peerDependenciesMeta": {
33
+ "@wasm-gaming/engine-specs": {
34
+ "optional": true
35
+ }
36
+ },
37
+ "devDependencies": {
38
+ "@wasm-gaming/engine-specs": "^0.2.3",
39
+ "typescript": "^5.5.0"
40
+ },
41
+ "engineContract": "https://github.com/wasm-gaming/engine-specs",
42
+ "license": "MIT"
43
+ }
@@ -0,0 +1,38 @@
1
+ import type { EngineManifest } from '@wasm-gaming/engine-specs';
2
+ import { FBNEO_OPTIONS_SCHEMA } from './fbneo.options.js';
3
+
4
+ export const manifest: EngineManifest = {
5
+ id: 'fbneo',
6
+ version: '0.1.0',
7
+ name: 'FinalBurn Neo (WebAssembly)',
8
+ description:
9
+ 'FBNeo arcade emulator (Capcom CPS-1/2/3, SNK Neo Geo, Sega System 16, and more) compiled to WebAssembly.',
10
+ artifacts: {
11
+ wasm: 'fbneo/fbneo.wasm',
12
+ js: 'fbneo/fbneo.js',
13
+ },
14
+ assets: [
15
+ {
16
+ key: 'rom',
17
+ mountPath: '/roms/game.zip',
18
+ required: true,
19
+ accept: ['.zip', '.7z'],
20
+ description:
21
+ 'Arcade ROM set (zip) written to Emscripten MEMFS. FBNeo identifies the game by the zip filename.',
22
+ },
23
+ {
24
+ key: 'bios',
25
+ mountPath: '/roms/neogeo.zip',
26
+ required: false,
27
+ accept: ['.zip'],
28
+ description:
29
+ 'Optional system BIOS set (e.g. neogeo.zip for Neo Geo, or a CPS-3 BIOS). Required only by ROM sets that depend on it.',
30
+ },
31
+ ],
32
+ input: 'fbneo',
33
+ video: { baseWidth: 384, baseHeight: 224, aspect: '4:3' },
34
+ options: FBNEO_OPTIONS_SCHEMA,
35
+ capabilities: { saveStates: false, sram: false, coreSelectable: false },
36
+ };
37
+
38
+ export default manifest;
@@ -0,0 +1,83 @@
1
+ import type { JSONSchema } from '@wasm-gaming/engine-specs';
2
+
3
+ export interface FbneoOptions {
4
+ /**
5
+ * Filename used when writing the ROM zip bytes to Emscripten MEMFS.
6
+ * FBNeo identifies a game by the zip filename (without extension), so this
7
+ * should match the driver's expected ROM set name (e.g. "mslug" for Metal Slug).
8
+ */
9
+ romFileName?: string;
10
+ /**
11
+ * The FBNeo driver/game name to load (e.g. "mslug", "sf2", "kof98").
12
+ * If omitted, the driver is inferred from `romFileName` (without extension).
13
+ */
14
+ driver?: string;
15
+ /** Canvas scaling filter: `pixelated` for crisp pixels, `smooth` for linear filtering. */
16
+ renderFilter?: 'pixelated' | 'smooth';
17
+ /** Target audio sample rate in Hz. FBNeo defaults to 44100. */
18
+ audioSampleRate?: number;
19
+ /** Enable vertical sync. Defaults to true. */
20
+ vsync?: boolean;
21
+ /** Use integer scaling (no fractional stretching). Defaults to false. */
22
+ integerScale?: boolean;
23
+ /** Window scale multiplier for the internal render resolution. Defaults to 2. */
24
+ windowScale?: number;
25
+ }
26
+
27
+ export const DEFAULT_FBNEO_OPTIONS: Required<FbneoOptions> = {
28
+ romFileName: 'game.zip',
29
+ driver: '',
30
+ renderFilter: 'pixelated',
31
+ audioSampleRate: 44100,
32
+ vsync: true,
33
+ integerScale: false,
34
+ windowScale: 2,
35
+ };
36
+
37
+ export const FBNEO_OPTIONS_SCHEMA: JSONSchema = {
38
+ type: 'object',
39
+ additionalProperties: false,
40
+ properties: {
41
+ romFileName: {
42
+ type: 'string',
43
+ default: 'game.zip',
44
+ description: 'ROM zip filename written to Emscripten MEMFS when loading a game.',
45
+ },
46
+ driver: {
47
+ type: 'string',
48
+ default: '',
49
+ description:
50
+ 'FBNeo driver/game name to load (e.g. "mslug"). If empty, inferred from romFileName without extension.',
51
+ },
52
+ renderFilter: {
53
+ type: 'string',
54
+ enum: ['pixelated', 'smooth'],
55
+ default: 'pixelated',
56
+ description: 'Canvas scaling filter: pixelated for crisp pixels, smooth for linear filtering.',
57
+ },
58
+ audioSampleRate: {
59
+ type: 'integer',
60
+ default: 44100,
61
+ minimum: 11025,
62
+ maximum: 96000,
63
+ description: 'Target audio sample rate in Hz.',
64
+ },
65
+ vsync: {
66
+ type: 'boolean',
67
+ default: true,
68
+ description: 'Enable vertical sync.',
69
+ },
70
+ integerScale: {
71
+ type: 'boolean',
72
+ default: false,
73
+ description: 'Use integer scaling (no fractional stretching).',
74
+ },
75
+ windowScale: {
76
+ type: 'integer',
77
+ default: 2,
78
+ minimum: 1,
79
+ maximum: 8,
80
+ description: 'Window scale multiplier for the internal render resolution.',
81
+ },
82
+ },
83
+ };