@wasm-gaming/fbneo-wasm 0.2.0 → 0.3.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.
Files changed (50) hide show
  1. package/README.md +10 -1
  2. package/dist/fbneo/app/app.js +1 -1
  3. package/dist/fbneo/fbneo.js +1 -1
  4. package/dist/fbneo/fbneo.options.d.ts +6 -0
  5. package/dist/fbneo/fbneo.options.d.ts.map +1 -1
  6. package/dist/fbneo/fbneo.options.js +6 -0
  7. package/dist/fbneo/fbneo.options.js.map +1 -1
  8. package/dist/fbneo/fbneo.sdk.d.ts +21 -1
  9. package/dist/fbneo/fbneo.sdk.d.ts.map +1 -1
  10. package/dist/fbneo/fbneo.sdk.js +57 -1
  11. package/dist/fbneo/fbneo.sdk.js.map +1 -1
  12. package/dist/fbneo/fbneo.wasm +2 -2
  13. package/dist/fbneo/romset.d.ts +0 -0
  14. package/dist/fbneo/romset.d.ts.map +1 -1
  15. package/dist/fbneo/romset.js.map +1 -1
  16. package/dist/fbneo/romsets/atari.json +5036 -118
  17. package/dist/fbneo/romsets/capcom.json +3392 -848
  18. package/dist/fbneo/romsets/cave.json +460 -115
  19. package/dist/fbneo/romsets/channelf.json +200 -50
  20. package/dist/fbneo/romsets/coleco.json +2650 -658
  21. package/dist/fbneo/romsets/cps3.json +240 -60
  22. package/dist/fbneo/romsets/dataeast.json +1352 -338
  23. package/dist/fbneo/romsets/galaxian.json +1192 -298
  24. package/dist/fbneo/romsets/gba.json +197 -0
  25. package/dist/fbneo/romsets/index.json +16 -12
  26. package/dist/fbneo/romsets/irem.json +796 -199
  27. package/dist/fbneo/romsets/konami.json +1644 -411
  28. package/dist/fbneo/romsets/megadrive.json +11899 -2926
  29. package/dist/fbneo/romsets/midway.json +560 -140
  30. package/dist/fbneo/romsets/msx.json +7204 -1798
  31. package/dist/fbneo/romsets/neogeo.json +2704 -614
  32. package/dist/fbneo/romsets/nes.json +12815 -3155
  33. package/dist/fbneo/romsets/pce.json +1952 -488
  34. package/dist/fbneo/romsets/pgm.json +796 -199
  35. package/dist/fbneo/romsets/pgm2.json +32 -8
  36. package/dist/fbneo/romsets/pre90s.json +10002 -2084
  37. package/dist/fbneo/romsets/psikyo.json +160 -40
  38. package/dist/fbneo/romsets/pst90s.json +6400 -1597
  39. package/dist/fbneo/romsets/sega.json +2756 -658
  40. package/dist/fbneo/romsets/sg1000.json +1032 -255
  41. package/dist/fbneo/romsets/sms.json +6012 -1503
  42. package/dist/fbneo/romsets/snes.json +10991 -2816
  43. package/dist/fbneo/romsets/spectrum.json +13579 -3361
  44. package/dist/fbneo/romsets/taito.json +2496 -624
  45. package/dist/fbneo/romsets/toaplan.json +576 -144
  46. package/package.json +1 -1
  47. package/src/app/app.js +1 -1
  48. package/src/fbneo.options.ts +12 -0
  49. package/src/fbneo.sdk.ts +70 -1
  50. package/src/romset.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wasm-gaming/fbneo-wasm",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "FBNeo (FinalBurn Neo arcade emulator) compiled to WebAssembly, with a self-contained JS SDK implementing the wasm-gaming engine contract.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/app/app.js CHANGED
@@ -619,7 +619,7 @@ export async function mount(target, host) {
619
619
  render();
620
620
  play = await new FbneoSDK()
621
621
  .mount(stage)
622
- .options({ driver })
622
+ .options({ driver, ...(host.options?.neogeo ? { neogeo: host.options.neogeo } : {}) })
623
623
  .assets({ ...host.assets, rom })
624
624
  .storage(host.storage)
625
625
  .on('error', ({ detail }) => console.error('[fbneo app]', detail))
@@ -34,6 +34,12 @@ export interface FbneoOptions {
34
34
  driver?: string;
35
35
  /** The ROM zip's file name (`mslug.zip`), used for the driver when `driver` is empty. */
36
36
  romFileName?: string;
37
+ /**
38
+ * How a Neo Geo set boots: `mvs`, the arcade board (coins, credits — FBNeo's default),
39
+ * or `aes`, the home console: the AES BIOS from the `bios` zip (or Universe BIOS 4.0 in
40
+ * AES mode) and a memory card per game. Other systems ignore it.
41
+ */
42
+ neogeo?: 'mvs' | 'aes';
37
43
  }
38
44
 
39
45
  export interface FbneoAssets {
@@ -99,6 +105,12 @@ export const FBNEO_SPEC: EngineSpec<FbneoPayloads> = {
99
105
  live: false,
100
106
  description: 'The ROM zip file name; its stem is the driver when driver is empty.',
101
107
  },
108
+ neogeo: {
109
+ default: 'mvs',
110
+ enum: ['mvs', 'aes'],
111
+ live: false,
112
+ description: 'Neo Geo sets: mvs (arcade, FBNeo default) or aes (home console: AES BIOS, memory card).',
113
+ },
102
114
  },
103
115
  assets: {
104
116
  rom: {
package/src/fbneo.sdk.ts CHANGED
@@ -54,6 +54,7 @@ export type {
54
54
  FbneoStorage,
55
55
  } from './fbneo.options.js';
56
56
  export { FBNEO_SPEC, FBNEO_VIDEO } from './fbneo.options.js';
57
+ import { parseZipEntries } from './romset.js';
57
58
 
58
59
  const ENGINE_ID = 'fbneo';
59
60
  const DEFAULT_STORAGE_NAMESPACE = 'default';
@@ -86,6 +87,8 @@ interface FbneoModule {
86
87
  _web_load_state(slot: number): number;
87
88
  _web_running(): number;
88
89
  _web_quit(): void;
90
+ _web_memcard_insert(): number;
91
+ ccall(name: string, returnType: 'number', argTypes: string[], args: unknown[]): number;
89
92
  SDL2?: { audioContext?: AudioContext };
90
93
  }
91
94
 
@@ -153,6 +156,30 @@ function sameBytes(a: Uint8Array | undefined, b: Uint8Array): boolean {
153
156
  return true;
154
157
  }
155
158
 
159
+ /** The Neo Geo BIOS FBNeo should run, from what the BIOS zip holds (by CRC). */
160
+ export interface NeoGeoBiosChoice {
161
+ /** `aes`: the real AES BIOS; `unibios`: Universe BIOS 4.0 in its AES mode. */
162
+ bios: 'aes' | 'unibios';
163
+ /** The driver's settings to apply: `[group, option]`, as FBNeo names them. */
164
+ dips: Array<[string, string]>;
165
+ }
166
+
167
+ const AES_WORLD_CRC = 'd27a71f1'; // neo-epo.bin
168
+ const UNIBIOS_40_CRC = 'a7aab458'; // uni-bios_4_0.rom
169
+
170
+ /**
171
+ * How to boot a Neo Geo as the AES (the home console) with the BIOS zip at hand: the
172
+ * real AES World BIOS when it is there, else Universe BIOS 4.0 in AES mode, else null.
173
+ */
174
+ export function neoGeoAesBios(entries: Array<{ crc: string }>): NeoGeoBiosChoice | null {
175
+ const crcs = new Set(entries.map((entry) => entry.crc.toLowerCase()));
176
+ if (crcs.has(AES_WORLD_CRC)) return { bios: 'aes', dips: [['BIOS', 'AES World']] };
177
+ if (crcs.has(UNIBIOS_40_CRC)) {
178
+ return { bios: 'unibios', dips: [['BIOS', 'Universe BIOS (Hack, Ver. 4.0)'], ['Unibios mode', 'AES']] };
179
+ }
180
+ return null;
181
+ }
182
+
156
183
  /** The player's save-state slots: 0-3. */
157
184
  export const STATE_SLOTS = 4;
158
185
  /**
@@ -198,6 +225,7 @@ export class FbneoPlay extends EnginePlayBase<FbneoPayloads> {
198
225
  #destroyed = false;
199
226
  #pauseOwner: string | null = null;
200
227
  #driver = '';
228
+ #neogeo: { mode: 'aes'; bios: 'aes' | 'unibios'; card: boolean } | null = null;
201
229
 
202
230
  #canvas: HTMLCanvasElement | null = null;
203
231
  #ownsCanvas = false;
@@ -234,6 +262,11 @@ export class FbneoPlay extends EnginePlayBase<FbneoPayloads> {
234
262
  return this.#driver;
235
263
  }
236
264
 
265
+ /** How a Neo Geo set was booted as the AES: `{ mode, bios, card }`; null otherwise. */
266
+ get neogeo(): { mode: 'aes'; bios: 'aes' | 'unibios'; card: boolean } | null {
267
+ return this.#neogeo;
268
+ }
269
+
237
270
  /** Who holds the pause, or null while running. */
238
271
  get pausedBy(): string | null {
239
272
  return this.#pauseOwner;
@@ -572,6 +605,8 @@ export class FbneoPlay extends EnginePlayBase<FbneoPayloads> {
572
605
  );
573
606
  }
574
607
 
608
+ if (options.neogeo === 'aes') await this.#bootAes(Module, bios);
609
+
575
610
  if (this.#persist) {
576
611
  this.#saveTimer = setInterval(() => void this.flushSaves(), SAVE_INTERVAL_MS);
577
612
  document.addEventListener('visibilitychange', this.#onHidden);
@@ -580,6 +615,40 @@ export class FbneoPlay extends EnginePlayBase<FbneoPayloads> {
580
615
  this.#fit();
581
616
  }
582
617
 
618
+ /**
619
+ * Neo Geo as the AES: pick the BIOS from the BIOS zip, set the driver's BIOS settings,
620
+ * insert the game's memory card and reset, so the machine boots that BIOS (FBNeo
621
+ * loads it at reset). A set that isn't Neo Geo has no "BIOS" setting: left as is.
622
+ */
623
+ async #bootAes(Module: FbneoModule, bios: Uint8Array | null): Promise<void> {
624
+ let choice: NeoGeoBiosChoice | null = null;
625
+ try {
626
+ choice = bios ? neoGeoAesBios(parseZipEntries(bios)) : null;
627
+ } catch {
628
+ choice = null; // not a zip: no BIOS to choose from
629
+ }
630
+ const setDip = (group: string, option: string): boolean =>
631
+ Module.ccall('web_set_dip', 'number', ['string', 'string'], [group, option]) === 1;
632
+ if (!choice) {
633
+ // Only a Neo Geo driver has the setting; anything else plays as it always has.
634
+ if (!setDip('BIOS', 'AES World')) return;
635
+ throw new Error(
636
+ 'fbneo: Neo Geo as the AES needs a BIOS zip (neogeo.zip) with the AES BIOS (neo-epo.bin) or Universe BIOS 4.0',
637
+ );
638
+ }
639
+ for (const [group, option] of choice.dips) {
640
+ if (!setDip(group, option)) {
641
+ if (group === 'BIOS') return; // not a Neo Geo set
642
+ throw new Error(`fbneo: this FBNeo build has no "${group}: ${option}" setting`);
643
+ }
644
+ }
645
+ const card = Module._web_memcard_insert() === 1;
646
+ // The settings reach the driver on the next frame; the BIOS is loaded at reset.
647
+ await new Promise((resolve) => setTimeout(resolve, 100));
648
+ Module._web_reset();
649
+ this.#neogeo = { mode: 'aes', bios: choice.bios, card };
650
+ }
651
+
583
652
  /** The plain values behind one payload's descriptors. */
584
653
  async #values<T>(kind: 'config' | 'options' | 'assets' | 'storage'): Promise<T> {
585
654
  const described = (await this[kind]()) as Record<string, { value: unknown }>;
@@ -669,7 +738,7 @@ export class FbneoPlay extends EnginePlayBase<FbneoPayloads> {
669
738
  export class FbneoSDK extends EngineSDKBase<FbneoPayloads> {
670
739
  static readonly id = ENGINE_ID;
671
740
  /** Kept in step with package.json by `node scripts/sync-version.mjs`. */
672
- static readonly version = '0.2.0';
741
+ static readonly version = '0.3.0';
673
742
  static readonly name = 'FinalBurn Neo';
674
743
  static readonly description =
675
744
  'FBNeo, the arcade emulator (Capcom CPS-1/2/3, SNK Neo Geo, Sega System 16 and more), compiled to WebAssembly. Ships no games: the player provides the ROM sets, and they never leave their browser.';
package/src/romset.ts CHANGED
Binary file