diff --git a/wasm/Makefile b/wasm/Makefile index ce0e44b..3c7843a 100644 --- a/wasm/Makefile +++ b/wasm/Makefile @@ -58,7 +58,7 @@ endif CFLAGS += -Werror -Wall -Wno-strict-aliasing -Wno-unknown-warning -Wno-unknown-warning-option -Wno-multichar -Wno-int-in-bool-context -std=gnu11 -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES # CFLAGS += -DGB_INTERNAL=1 # get access to internal APIs CFLAGS += -I$(CORE_DIR) -CFLAGS += -s WASM=1 -s USE_SDL=2 --preload-file $(BOOTROMS_DIR)@/BootROMs -s "EXTRA_EXPORTED_RUNTIME_METHODS=['FS']" +CFLAGS += -s WASM=1 -s USE_SDL=2 --preload-file $(BOOTROMS_DIR)@/BootROMs -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap', 'getValue', 'AsciiToString', 'FS']" # CFLAGS += -Wcast-align -Wover-aligned -s SAFE_HEAP=1 -s WARN_UNALIGNED=1 WASM_LDFLAGS := diff --git a/wasm/index-shell.html b/wasm/index-shell.html index 4bc341c..da58b99 100644 --- a/wasm/index-shell.html +++ b/wasm/index-shell.html @@ -98,6 +98,34 @@ const progressElement = document.querySelector('#progress'); var Module = { + get_models: function () { + const ptr = Module._get_models_string_pointer(); + const ptr_size = Module._get_models_string_pointer_size(); + const size = Module._get_models_string_size() - ptr_size; // last element is a end marker + const models = []; + + for (let i = 0; i < size; i += ptr_size) { + const key = AsciiToString(getValue(ptr + i, '*')); + + models.push(key.replace(/^MODEL_/, '')); + } + + return models + }, + get_sgb_revisions: function () { + const ptr = Module._get_sgb_revisions_string_pointer(); + const ptr_size = Module._get_sgb_revisions_string_pointer_size(); + const size = Module._get_sgb_revisions_string_size() - ptr_size; // last element is a end marker + const revisions = []; + + for (let i = 0; i < size; i += ptr_size) { + const key = AsciiToString(getValue(ptr + i, '*')); + + revisions.push(key.replace(/^SGB_/, '')); + } + + return revisions + }, sync_fs: function () { console.log("Syncing file system ..."); diff --git a/wasm/main.c b/wasm/main.c index 7d59c2a..3a04613 100644 --- a/wasm/main.c +++ b/wasm/main.c @@ -80,11 +80,15 @@ int save_battery(GB_gameboy_t *gb, const char *path) { unsigned query_sample_rate_of_audiocontexts() { return EM_ASM_INT({ - const AudioContext = window.AudioContext || window.webkitAudioContext; - const ctx = new AudioContext(); - const sr = ctx.sampleRate; - ctx.close(); - return sr; + if (!Module.SDL2 || !Module.SDL2.audioContext) { + const AudioContext = window.AudioContext || window.webkitAudioContext; + const ctx = new AudioContext(); + const sr = ctx.sampleRate; + ctx.close(); + return sr; + } + + return Module.SDL2.audioContext.sampleRate; }); } @@ -217,7 +221,7 @@ void init_gb() { } int EMSCRIPTEN_KEEPALIVE init() { - #define str(x) #x +#define str(x) #x #define xstr(x) str(x) pixel_format = (SDL_PixelFormat *) malloc(sizeof(SDL_PixelFormat)); @@ -292,7 +296,7 @@ int EMSCRIPTEN_KEEPALIVE init() { want_aspec.freq = audio_sample_rate; want_aspec.format = AUDIO_S16SYS; want_aspec.channels = 2; - want_aspec.samples = 512; + want_aspec.samples = 2048; want_aspec.callback = audio_callback; want_aspec.userdata = &gb; @@ -302,27 +306,51 @@ int EMSCRIPTEN_KEEPALIVE init() { fprintf(stderr, "Failed to open audio: %s", SDL_GetError()); } - EM_ASM({ - var AudioContext = window.AudioContext || window.webkitAudioContext; - var ctx = new AudioContext(); + fprintf(stderr, "WANT:\nfreq: %d\nchannels: %d\nsilence: %d\nsamples: %d\nsize: %d\nformat: %d\n", want_aspec.freq, want_aspec.channels, want_aspec.silence, want_aspec.samples, want_aspec.size, want_aspec.format); + fprintf(stderr, "HAVE:\nfreq: %d\nchannels: %d\nsilence: %d\nsamples: %d\nsize: %d\nformat: %d\n", have_aspec.freq, have_aspec.channels, have_aspec.silence, have_aspec.samples, have_aspec.size, have_aspec.format); - // unlock audio for iOS - if (ctx && ctx.currentTime == 0) { - var buffer = ctx.createBuffer(1, 1, 22050); - var source = ctx.createBufferSource(); - source.buffer = buffer; - source.connect(ctx.destination); - source.start(0); + EM_ASM({ + function audio_workaround(e) { + if (!Module.SDL2 || !Module.SDL2.audioContext || !Module.SDL2.audioContext.resume) return; + + console.log('Applying audio workarounds...'); + + if (Module.SDL2.audioContext.state == 'suspended') { + Module.SDL2.audioContext.resume(); + } + + if (Module.SDL2.audioContext.state == 'running') { + document.removeEventListener('touchstart', audio_workaround); + document.removeEventListener('click', audio_workaround); + document.removeEventListener('keydown', audio_workaround); + + if (Module.canvas) { + Module.canvas.removeEventListener('touchstart', audio_workaround); + Module.canvas.removeEventListener('click', audio_workaround); + Module.canvas.removeEventListener('keydown', audio_workaround); + } + } + else if (Module.SDL2.audioContext && Module.SDL2.audioContext.currentTime == 0) { + // unlock audio for iOS + let buffer = Module.SDL2.audioContext.createBuffer(1, 1, 22050); + let source = Module.SDL2.audioContext.createBufferSource(); + source.buffer = buffer; + source.connect(Module.SDL2.audioContext.destination); + source.start(0); + } } - // Google audio enable work-around: - // https://github.com/emscripten-ports/SDL2/issues/57 - try { - if (!Module.SDL2 || !ctx || !ctx.resume) return; - ctx.resume(); - } catch (err) {} + document.addEventListener('touchstart', audio_workaround); + document.addEventListener('click', audio_workaround); + document.addEventListener('keydown', audio_workaround); - ctx.close(); + if (Module.canvas) { + Module.canvas.addEventListener('touchstart', audio_workaround); + Module.canvas.addEventListener('click', audio_workaround); + Module.canvas.addEventListener('keydown', audio_workaround); + } + + audio_workaround(); }); init_gb(); @@ -332,7 +360,11 @@ int EMSCRIPTEN_KEEPALIVE init() { return EXIT_SUCCESS; } -int EMSCRIPTEN_KEEPALIVE load_rom(char* filename, char* battery_save_path) { +int EMSCRIPTEN_KEEPALIVE load_boot_rom_from_file(char* filename) { + return GB_load_boot_rom(&gb, filename); +} + +int EMSCRIPTEN_KEEPALIVE load_rom_from_file(char* filename, char* battery_save_path) { int result = GB_load_rom(&gb, filename); if (result == 0) { diff --git a/wasm/main.h b/wasm/main.h index 6161ff4..a8807a3 100644 --- a/wasm/main.h +++ b/wasm/main.h @@ -1,6 +1,9 @@ #ifndef main_h #define main_h +#define str(x) #x +#define xstr(x) str(x) + #define VIDEO_WIDTH 160 #define VIDEO_HEIGHT 144 #define VIDEO_PIXELS (VIDEO_WIDTH * VIDEO_HEIGHT) @@ -9,6 +12,22 @@ #define SGB_VIDEO_HEIGHT 224 #define SGB_VIDEO_PIXELS (SGB_VIDEO_WIDTH * SGB_VIDEO_HEIGHT) +#define GENERATE_ENUM(ENUM) ENUM, +#define GENERATE_STRING(STRING) #STRING, + +#define MODELS(MODEL) \ + MODEL(MODEL_DMG) \ + MODEL(MODEL_CGB) \ + MODEL(MODEL_AGB) \ + MODEL(MODEL_SGB) \ + MODEL(MODEL_MAX) + +#define SGB_REVISIONS(REVISION) \ + REVISION(SGB_NTSC) \ + REVISION(SGB_PAL) \ + REVISION(SGB_2) \ + REVISION(SGB_MAX) + typedef enum { JOYPAD_AXISES_X, JOYPAD_AXISES_Y, @@ -24,11 +43,7 @@ typedef struct { char filter[32]; enum { - MODEL_DMG, - MODEL_CGB, - MODEL_AGB, - MODEL_SGB, - MODEL_MAX, + MODELS(GENERATE_ENUM) } model; /* v0.11 */ @@ -39,11 +54,42 @@ typedef struct { /* v0.12 */ enum { - SGB_NTSC, - SGB_PAL, - SGB_2, - SGB_MAX + SGB_REVISIONS(GENERATE_ENUM) } sgb_revision; } configuration_t; +// TODO: There must be a better way to not duplicate this data on the JavaScript side +static const char *MODELS_STRING[] = { + MODELS(GENERATE_STRING) +}; + +static const char *SGB_REVISIONS_STRING[] = { + SGB_REVISIONS(GENERATE_STRING) +}; + +static const char** EMSCRIPTEN_KEEPALIVE get_models_string_pointer() { + return MODELS_STRING; +} + +static const size_t EMSCRIPTEN_KEEPALIVE get_models_string_pointer_size() { + return sizeof(*MODELS_STRING); +} + +static const size_t EMSCRIPTEN_KEEPALIVE get_models_string_size() { + return sizeof(MODELS_STRING); +} + +static const char** EMSCRIPTEN_KEEPALIVE get_sgb_revisions_string_pointer() { + return SGB_REVISIONS_STRING; +} + +static const size_t EMSCRIPTEN_KEEPALIVE get_sgb_revisions_string_pointer_size() { + return sizeof(*SGB_REVISIONS_STRING); +} + +static const size_t EMSCRIPTEN_KEEPALIVE get_sgb_revisions_string_size() { + return sizeof(SGB_REVISIONS_STRING); +} + + #endif /* main_h */ diff --git a/wasm/main.js b/wasm/main.js index 7c52926..e06fcc1 100644 --- a/wasm/main.js +++ b/wasm/main.js @@ -50,7 +50,7 @@ const loadRomFromMemory = (name, data) => { const rom_path = allocate(intArrayFromString(`/rom/${name}`), 'i8', ALLOC_NORMAL); const battery_path = allocate(intArrayFromString(`/persist/${battery_name}`), 'i8', ALLOC_NORMAL); - Module._load_rom(rom_path, battery_path); + Module._load_rom_from_file(rom_path, battery_path); // The ROM has been read into memory, we can unlink the file now FS.unlink(`/rom/${name}`) @@ -123,6 +123,9 @@ document.getElementById('file').addEventListener('change', e => { }, false); Module.onRuntimeInitialized = _ => { + console.log(Module.get_models()); + console.log(Module.get_sgb_revisions()); + FS.mkdir('/persist'); FS.mount(IDBFS, { }, '/persist');