mirror of
https://github.com/libretro/RetroArch
synced 2025-03-14 10:21:46 +00:00
This reverts commit cacd5a9a234f3c9abc8d352f6fea19117e2e1a00.
This commit is contained in:
parent
cacd5a9a23
commit
2870a0a8ad
@ -1526,10 +1526,7 @@ ifeq ($(HAVE_GL_CONTEXT), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_EMSCRIPTEN), 1)
|
||||
ifeq ($(HAVE_EGL), 1)
|
||||
OBJ += gfx/drivers_context/emscriptenegl_ctx.o
|
||||
endif
|
||||
OBJ += gfx/drivers_context/emscriptenwebgl_ctx.o
|
||||
OBJ += gfx/drivers_context/emscriptenegl_ctx.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_MALI_FBDEV), 1)
|
||||
|
@ -24,8 +24,7 @@ HAVE_SCREENSHOTS = 1
|
||||
HAVE_REWIND = 1
|
||||
HAVE_AUDIOMIXER = 1
|
||||
HAVE_CC_RESAMPLER = 1
|
||||
HAVE_EGL ?= 0
|
||||
HAVE_OPENGLES = 1
|
||||
HAVE_EGL = 1
|
||||
HAVE_RJPEG = 0
|
||||
HAVE_RPNG = 1
|
||||
HAVE_EMSCRIPTEN = 1
|
||||
@ -49,8 +48,6 @@ HAVE_7ZIP = 1
|
||||
HAVE_BSV_MOVIE = 1
|
||||
HAVE_AL = 1
|
||||
HAVE_CHD ?= 0
|
||||
HAVE_WASMFS ?= 1
|
||||
HAVE_WORKER ?= 1
|
||||
|
||||
# WARNING -- READ BEFORE ENABLING
|
||||
# The rwebaudio driver is known to have several audio bugs, such as
|
||||
@ -71,7 +68,7 @@ HAVE_OPENGLES3 ?= 0
|
||||
|
||||
ASYNC ?= 0
|
||||
LTO ?= 0
|
||||
PTHREAD ?= 4
|
||||
PTHREAD ?= 0
|
||||
|
||||
STACK_SIZE ?= 4194304
|
||||
INITIAL_HEAP ?= 134217728
|
||||
@ -95,20 +92,37 @@ _cmd_toggle_menu,_cmd_reload_config,_cmd_toggle_grab_mouse,_cmd_toggle_game_focu
|
||||
_cmd_set_volume,_cmd_set_shader,_cmd_cheat_set_code,_cmd_cheat_get_code,_cmd_cheat_toggle_index,_cmd_cheat_get_code_state,_cmd_cheat_realloc,\
|
||||
_cmd_cheat_get_size,_cmd_cheat_apply_cheats
|
||||
|
||||
EXPORTS := callMain,FS,PATH,ERRNO_CODES,stringToNewUTF8,UTF8ToString
|
||||
|
||||
LIBS := -s USE_ZLIB=1
|
||||
LDFLAGS := -L. --no-heap-copy -s $(LIBS) -s STACK_SIZE=$(STACK_SIZE) -s INITIAL_MEMORY=$(INITIAL_HEAP) \
|
||||
-s EXPORTED_RUNTIME_METHODS=callMain,FS,PATH,ERRNO_CODES,stringToNewUTF8,UTF8ToString \
|
||||
-s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS="$(EXPORTED_FUNCTIONS)" \
|
||||
-s MODULARIZE=1 -s EXPORT_ES6=1 -s EXPORT_NAME="libretro_$(subst -,_,$(LIBRETRO))" \
|
||||
--extern-pre-js emscripten/pre.js \
|
||||
--js-library emscripten/library_rwebcam.js \
|
||||
--js-library emscripten/library_platform_emscripten.js
|
||||
|
||||
ifeq ($(HAVE_WASMFS), 1)
|
||||
LIBS += -s WASMFS -s FORCE_FILESYSTEM=1 -lfetchfs.js -lopfs.js
|
||||
EXPORTS := $(EXPORTS),FETCHFS,OPFS
|
||||
ifeq ($(HAVE_RWEBAUDIO), 1)
|
||||
LDFLAGS += --js-library emscripten/library_rwebaudio.js
|
||||
DEFINES += -DHAVE_RWEBAUDIO
|
||||
endif
|
||||
ifeq ($(HAVE_AL), 1)
|
||||
LDFLAGS += -lopenal
|
||||
DEFINES += -DHAVE_AL
|
||||
override ASYNC = 1
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_WORKER), 1)
|
||||
LIBS += -s PROXY_TO_PTHREAD -s USE_ES6_IMPORT_META=0 -sENVIRONMENT=worker,web
|
||||
else
|
||||
ifeq ($(HAVE_AL), 1)
|
||||
override ASYNC = 1
|
||||
ifneq ($(PTHREAD), 0)
|
||||
LDFLAGS += -s MAXIMUM_MEMORY=1073741824 -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD)
|
||||
CFLAGS += -pthread
|
||||
HAVE_THREADS=1
|
||||
else
|
||||
HAVE_THREADS=0
|
||||
endif
|
||||
|
||||
ifeq ($(ASYNC), 1)
|
||||
LDFLAGS += -s ASYNCIFY=$(ASYNC) -s ASYNCIFY_STACK_SIZE=8192
|
||||
ifeq ($(DEBUG), 1)
|
||||
LDFLAGS += -s ASYNCIFY_DEBUG=1 # -s ASYNCIFY_ADVISE
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -133,45 +147,6 @@ ifeq ($(HAVE_SDL2), 1)
|
||||
DEFINES += -DHAVE_SDL2
|
||||
endif
|
||||
|
||||
|
||||
LDFLAGS := -L. --no-heap-copy -s $(LIBS) -s STACK_SIZE=$(STACK_SIZE) -s INITIAL_MEMORY=$(INITIAL_HEAP) \
|
||||
-s EXPORTED_RUNTIME_METHODS=$(EXPORTS) \
|
||||
-s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS="$(EXPORTED_FUNCTIONS)" \
|
||||
-s MODULARIZE=1 -s EXPORT_ES6=1 -s EXPORT_NAME="libretro_$(subst -,_,$(LIBRETRO))" \
|
||||
-s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 \
|
||||
-s OFFSCREENCANVAS_SUPPORT \
|
||||
-s OFFSCREEN_FRAMEBUFFER \
|
||||
--extern-pre-js emscripten/pre.js \
|
||||
--js-library emscripten/library_rwebcam.js \
|
||||
--js-library emscripten/library_platform_emscripten.js
|
||||
|
||||
ifeq ($(HAVE_RWEBAUDIO), 1)
|
||||
LDFLAGS += --js-library emscripten/library_rwebaudio.js
|
||||
DEFINES += -DHAVE_RWEBAUDIO
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_AL), 1)
|
||||
LDFLAGS += -lopenal
|
||||
DEFINES += -DHAVE_AL
|
||||
endif
|
||||
|
||||
ifneq ($(PTHREAD), 0)
|
||||
LDFLAGS += -s WASM_MEM_MAX=1073741824 -pthread -s PTHREAD_POOL_SIZE=$(PTHREAD)
|
||||
CFLAGS += -pthread -s SHARED_MEMORY
|
||||
HAVE_THREADS=1
|
||||
else
|
||||
HAVE_THREADS=0
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(ASYNC), 1)
|
||||
DEFINES += -DEMSCRIPTEN_ASYNCIFY
|
||||
LDFLAGS += -s ASYNCIFY=$(ASYNC) -s ASYNCIFY_STACK_SIZE=8192
|
||||
ifeq ($(DEBUG), 1)
|
||||
LDFLAGS += -s ASYNCIFY_DEBUG=1 # -s ASYNCIFY_ADVISE
|
||||
endif
|
||||
endif
|
||||
|
||||
include Makefile.common
|
||||
|
||||
CFLAGS += $(DEF_FLAGS) -Ideps -Ideps/stb
|
||||
@ -208,10 +183,8 @@ RARCH_OBJ := $(addprefix $(OBJDIR)/,$(OBJ))
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(libretro_new) : $(libretro)
|
||||
mv -f $(libretro) $(libretro_new)
|
||||
|
||||
$(TARGET): $(RARCH_OBJ) $(libretro_new)
|
||||
$(TARGET): $(RARCH_OBJ) $(libretro)
|
||||
@$(if $(libretro), mv -f $(libretro) $(libretro_new),)
|
||||
@$(if $(Q), $(shell echo echo "LD $@ \<obj\> $(libretro_new) $(LIBS) $(LDFLAGS)"),)
|
||||
$(Q)$(LD) -o $@ $(RARCH_OBJ) $(libretro_new) $(LIBS) $(LDFLAGS)
|
||||
|
||||
|
@ -235,8 +235,8 @@ static void frontend_emscripten_get_env(int *argc, char *argv[],
|
||||
"config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONTENT], user_path,
|
||||
"content", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONTENT]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], base_path,
|
||||
"downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], user_path,
|
||||
"content/downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST], user_path,
|
||||
"playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST]));
|
||||
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP], g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG],
|
||||
@ -308,10 +308,7 @@ int main(int argc, char *argv[])
|
||||
specialHTMLTargets["!canvas"] = Module.canvas;
|
||||
});
|
||||
|
||||
emscripten_set_canvas_element_size("!canvas", 800, 600);
|
||||
emscripten_set_element_css_size("!canvas", 800.0, 600.0);
|
||||
emscripten_set_main_loop(emscripten_mainloop, 0, 0);
|
||||
emscripten_set_main_loop_timing(EM_TIMING_RAF, 1);
|
||||
rarch_main(argc, argv, NULL);
|
||||
|
||||
return 0;
|
||||
|
@ -124,9 +124,11 @@ static void gfx_ctx_emscripten_destroy(void *data)
|
||||
|
||||
if (!emscripten)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_EGL
|
||||
egl_destroy(&emscripten->egl);
|
||||
#endif
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
@ -189,6 +191,7 @@ static void *gfx_ctx_emscripten_init(void *video_driver)
|
||||
#endif
|
||||
|
||||
return emscripten;
|
||||
|
||||
error:
|
||||
gfx_ctx_emscripten_destroy(video_driver);
|
||||
return NULL;
|
||||
|
@ -1,279 +0,0 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2012-2015 - Michael Lelli
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
#include <emscripten/html5.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
#include "../../retroarch.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx;
|
||||
unsigned fb_width;
|
||||
unsigned fb_height;
|
||||
} emscripten_ctx_data_t;
|
||||
|
||||
static void gfx_ctx_emscripten_webgl_swap_interval(void *data, int interval)
|
||||
{
|
||||
if (interval == 0)
|
||||
emscripten_set_main_loop_timing(EM_TIMING_SETIMMEDIATE, 0);
|
||||
else
|
||||
emscripten_set_main_loop_timing(EM_TIMING_RAF, interval);
|
||||
}
|
||||
|
||||
static void gfx_ctx_emscripten_webgl_get_canvas_size(int *width, int *height)
|
||||
{
|
||||
EmscriptenFullscreenChangeEvent fullscreen_status;
|
||||
bool is_fullscreen = false;
|
||||
EMSCRIPTEN_RESULT r = emscripten_get_fullscreen_status(&fullscreen_status);
|
||||
|
||||
if (r == EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
if (fullscreen_status.isFullscreen)
|
||||
{
|
||||
is_fullscreen = true;
|
||||
*width = fullscreen_status.screenWidth;
|
||||
*height = fullscreen_status.screenHeight;
|
||||
}
|
||||
}
|
||||
if (!is_fullscreen)
|
||||
{
|
||||
double w, h;
|
||||
r = emscripten_get_element_css_size("#canvas", &w, &h);
|
||||
*width = (int)w;
|
||||
*height = (int)h;
|
||||
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
*width = 800;
|
||||
*height = 600;
|
||||
RARCH_ERR("[EMSCRIPTEN/WebGL]: Could not get screen dimensions: %d\n",r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_ctx_emscripten_webgl_check_window(void *data, bool *quit,
|
||||
bool *resize, unsigned *width, unsigned *height)
|
||||
{
|
||||
int input_width=0;
|
||||
int input_height=0;
|
||||
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
|
||||
|
||||
*resize = false;
|
||||
gfx_ctx_emscripten_webgl_get_canvas_size(&input_width, &input_height);
|
||||
*width = (unsigned)input_width;
|
||||
*height = (unsigned)input_height;
|
||||
if(*width != emscripten->fb_width || *height != emscripten->fb_height) {
|
||||
*resize = true;
|
||||
}
|
||||
emscripten->fb_width = (unsigned)*width;
|
||||
emscripten->fb_height = (unsigned)*height;
|
||||
*quit = false;
|
||||
}
|
||||
|
||||
static void gfx_ctx_emscripten_webgl_swap_buffers(void *data)
|
||||
{
|
||||
emscripten_webgl_commit_frame();
|
||||
}
|
||||
|
||||
static void gfx_ctx_emscripten_webgl_get_video_size(void *data,
|
||||
unsigned *width, unsigned *height)
|
||||
{
|
||||
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
|
||||
int s_width, s_height;
|
||||
if (!emscripten)
|
||||
return;
|
||||
gfx_ctx_emscripten_webgl_get_canvas_size(&s_width, &s_height);
|
||||
*width = (unsigned)s_width;
|
||||
*height = (unsigned)s_height;
|
||||
}
|
||||
|
||||
static void gfx_ctx_emscripten_webgl_destroy(void *data)
|
||||
{
|
||||
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
|
||||
|
||||
if (!emscripten)
|
||||
return;
|
||||
|
||||
emscripten_webgl_destroy_context(emscripten->ctx);
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void *gfx_ctx_emscripten_webgl_init(void *video_driver)
|
||||
{
|
||||
int width, height;
|
||||
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)
|
||||
calloc(1, sizeof(*emscripten));
|
||||
|
||||
EmscriptenWebGLContextAttributes attrs={0};
|
||||
emscripten_webgl_init_context_attributes(&attrs);
|
||||
attrs.alpha = false;
|
||||
attrs.depth = true;
|
||||
attrs.stencil = true;
|
||||
attrs.antialias = false;
|
||||
attrs.powerPreference = EM_WEBGL_POWER_PREFERENCE_HIGH_PERFORMANCE;
|
||||
attrs.majorVersion = 2;
|
||||
attrs.minorVersion = 2;
|
||||
attrs.enableExtensionsByDefault = true;
|
||||
attrs.explicitSwapControl = true;
|
||||
attrs.renderViaOffscreenBackBuffer = true;
|
||||
attrs.proxyContextToMainThread = EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW;
|
||||
|
||||
if (!emscripten)
|
||||
return NULL;
|
||||
|
||||
emscripten->ctx = emscripten_webgl_create_context("#canvas", &attrs);
|
||||
if(!emscripten->ctx) {
|
||||
RARCH_LOG("[EMSCRIPTEN/WEBGL]: Failed to initialize webgl\n");
|
||||
goto error;
|
||||
}
|
||||
emscripten_webgl_get_drawing_buffer_size(emscripten->ctx, &width, &height);
|
||||
emscripten_webgl_make_context_current(emscripten->ctx);
|
||||
emscripten->fb_width = (unsigned)width;
|
||||
emscripten->fb_height = (unsigned)height;
|
||||
RARCH_LOG("[EMSCRIPTEN/WEBGL]: Dimensions: %ux%u\n", emscripten->fb_width, emscripten->fb_height);
|
||||
|
||||
return emscripten;
|
||||
|
||||
error:
|
||||
gfx_ctx_emscripten_webgl_destroy(video_driver);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool gfx_ctx_emscripten_webgl_set_video_mode(void *data,
|
||||
unsigned width, unsigned height,
|
||||
bool fullscreen)
|
||||
{
|
||||
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
|
||||
EMSCRIPTEN_RESULT r;
|
||||
if(!emscripten || !emscripten->ctx) return false;
|
||||
|
||||
if (width != 0 && height != 0) {
|
||||
RARCH_LOG("[EMSCRIPTEN/WebGL]: set canvas size to %d, %d\n", width, height);
|
||||
r = emscripten_set_canvas_element_size("#canvas",
|
||||
(int)width, (int)height);
|
||||
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS) {
|
||||
RARCH_ERR("[EMSCRIPTEN/WebGL]: error resizing canvas: %d\n", r);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
emscripten->fb_width = width;
|
||||
emscripten->fb_height = height;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gfx_ctx_emscripten_webgl_set_resize(void *data, unsigned width, unsigned height) {
|
||||
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
|
||||
EMSCRIPTEN_RESULT r;
|
||||
if(!emscripten || !emscripten->ctx) return false;
|
||||
RARCH_LOG("[EMSCRIPTEN/WebGL]: set canvas size to %d, %d\n", width, height);
|
||||
r = emscripten_set_canvas_element_size("#canvas",
|
||||
(int)width, (int)height);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS) {
|
||||
RARCH_ERR("[EMSCRIPTEN/WebGL]: error resizing canvas: %d\n", r);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum gfx_ctx_api gfx_ctx_emscripten_webgl_get_api(void *data) { return GFX_CTX_OPENGL_ES_API; }
|
||||
|
||||
static bool gfx_ctx_emscripten_webgl_bind_api(void *data,
|
||||
enum gfx_ctx_api api, unsigned major, unsigned minor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gfx_ctx_emscripten_webgl_input_driver(void *data,
|
||||
const char *name,
|
||||
input_driver_t **input, void **input_data)
|
||||
{
|
||||
void *rwebinput = input_driver_init_wrap(&input_rwebinput, name);
|
||||
*input = rwebinput ? &input_rwebinput : NULL;
|
||||
*input_data = rwebinput;
|
||||
}
|
||||
|
||||
static bool gfx_ctx_emscripten_webgl_has_focus(void *data) {
|
||||
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
|
||||
return emscripten && emscripten->ctx;
|
||||
}
|
||||
|
||||
static bool gfx_ctx_emscripten_webgl_suppress_screensaver(void *data, bool enable) { return false; }
|
||||
|
||||
static float gfx_ctx_emscripten_webgl_translate_aspect(void *data,
|
||||
unsigned width, unsigned height) { return (float)width / height; }
|
||||
|
||||
static void gfx_ctx_emscripten_webgl_bind_hw_render(void *data, bool enable)
|
||||
{
|
||||
emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data;
|
||||
emscripten_webgl_make_context_current(emscripten->ctx);
|
||||
}
|
||||
|
||||
static uint32_t gfx_ctx_emscripten_webgl_get_flags(void *data)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_GLSL);
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void gfx_ctx_emscripten_webgl_set_flags(void *data, uint32_t flags) { }
|
||||
|
||||
const gfx_ctx_driver_t gfx_ctx_emscripten_webgl = {
|
||||
gfx_ctx_emscripten_webgl_init,
|
||||
gfx_ctx_emscripten_webgl_destroy,
|
||||
gfx_ctx_emscripten_webgl_get_api,
|
||||
gfx_ctx_emscripten_webgl_bind_api,
|
||||
gfx_ctx_emscripten_webgl_swap_interval,
|
||||
gfx_ctx_emscripten_webgl_set_video_mode,
|
||||
gfx_ctx_emscripten_webgl_get_video_size,
|
||||
NULL, /* get_refresh_rate */
|
||||
NULL, /* get_video_output_size */
|
||||
NULL, /* get_video_output_prev */
|
||||
NULL, /* get_video_output_next */
|
||||
NULL, /* get_metrics */
|
||||
gfx_ctx_emscripten_webgl_translate_aspect,
|
||||
NULL, /* update_title */
|
||||
gfx_ctx_emscripten_webgl_check_window,
|
||||
gfx_ctx_emscripten_webgl_set_resize, /* set_resize */
|
||||
gfx_ctx_emscripten_webgl_has_focus,
|
||||
gfx_ctx_emscripten_webgl_suppress_screensaver,
|
||||
false,
|
||||
gfx_ctx_emscripten_webgl_swap_buffers,
|
||||
gfx_ctx_emscripten_webgl_input_driver,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
"webgl_emscripten",
|
||||
gfx_ctx_emscripten_webgl_get_flags,
|
||||
gfx_ctx_emscripten_webgl_set_flags,
|
||||
gfx_ctx_emscripten_webgl_bind_hw_render,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
@ -162,11 +162,8 @@ static const gfx_ctx_driver_t *gfx_ctx_gl_drivers[] = {
|
||||
#ifdef HAVE_OSMESA
|
||||
&gfx_ctx_osmesa,
|
||||
#endif
|
||||
#if (defined(EMSCRIPTEN) && defined(HAVE_EGL))
|
||||
&gfx_ctx_emscripten,
|
||||
#endif
|
||||
#ifdef EMSCRIPTEN
|
||||
&gfx_ctx_emscripten_webgl,
|
||||
&gfx_ctx_emscripten,
|
||||
#endif
|
||||
&gfx_ctx_null,
|
||||
NULL
|
||||
|
@ -1384,7 +1384,6 @@ extern const gfx_ctx_driver_t gfx_ctx_cgl;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_cocoagl;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_cocoavk;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_emscripten;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_emscripten_webgl;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_opendingux_fbdev;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_khr_display;
|
||||
extern const gfx_ctx_driver_t gfx_ctx_gdi;
|
||||
|
@ -417,7 +417,7 @@ static void *rwebinput_input_init(const char *joypad_driver)
|
||||
rwebinput_generate_lut();
|
||||
|
||||
r = emscripten_set_keydown_callback(
|
||||
"#canvas", rwebinput, false,
|
||||
"!canvas", rwebinput, false,
|
||||
rwebinput_keyboard_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
@ -426,7 +426,7 @@ static void *rwebinput_input_init(const char *joypad_driver)
|
||||
}
|
||||
|
||||
r = emscripten_set_keyup_callback(
|
||||
"#canvas", rwebinput, false,
|
||||
"!canvas", rwebinput, false,
|
||||
rwebinput_keyboard_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
@ -435,7 +435,7 @@ static void *rwebinput_input_init(const char *joypad_driver)
|
||||
}
|
||||
|
||||
r = emscripten_set_keypress_callback(
|
||||
"#canvas", rwebinput, false,
|
||||
"!canvas", rwebinput, false,
|
||||
rwebinput_keyboard_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
@ -443,7 +443,7 @@ static void *rwebinput_input_init(const char *joypad_driver)
|
||||
"[EMSCRIPTEN/INPUT] failed to create keypress callback: %d\n", r);
|
||||
}
|
||||
|
||||
r = emscripten_set_mousedown_callback("#canvas", rwebinput, false,
|
||||
r = emscripten_set_mousedown_callback("!canvas", rwebinput, false,
|
||||
rwebinput_mouse_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
@ -451,7 +451,7 @@ static void *rwebinput_input_init(const char *joypad_driver)
|
||||
"[EMSCRIPTEN/INPUT] failed to create mousedown callback: %d\n", r);
|
||||
}
|
||||
|
||||
r = emscripten_set_mouseup_callback("#canvas", rwebinput, false,
|
||||
r = emscripten_set_mouseup_callback("!canvas", rwebinput, false,
|
||||
rwebinput_mouse_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
@ -459,7 +459,7 @@ static void *rwebinput_input_init(const char *joypad_driver)
|
||||
"[EMSCRIPTEN/INPUT] failed to create mouseup callback: %d\n", r);
|
||||
}
|
||||
|
||||
r = emscripten_set_mousemove_callback("#canvas", rwebinput, false,
|
||||
r = emscripten_set_mousemove_callback("!canvas", rwebinput, false,
|
||||
rwebinput_mouse_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
@ -468,7 +468,7 @@ static void *rwebinput_input_init(const char *joypad_driver)
|
||||
}
|
||||
|
||||
r = emscripten_set_wheel_callback(
|
||||
"#canvas", rwebinput, false,
|
||||
"!canvas", rwebinput, false,
|
||||
rwebinput_wheel_cb);
|
||||
if (r != EMSCRIPTEN_RESULT_SUCCESS)
|
||||
{
|
||||
@ -819,7 +819,7 @@ static void rwebinput_input_poll(void *data)
|
||||
static void rwebinput_grab_mouse(void *data, bool state)
|
||||
{
|
||||
if (state)
|
||||
emscripten_request_pointerlock("#canvas", EM_TRUE);
|
||||
emscripten_request_pointerlock("!canvas", EM_TRUE);
|
||||
else
|
||||
emscripten_exit_pointerlock();
|
||||
}
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include <psp2/kernel/threadmgr.h>
|
||||
#elif defined(_3DS)
|
||||
#include <3ds.h>
|
||||
#elif (defined(EMSCRIPTEN) && defined(EMSCRIPTEN_ASYNCIFY))
|
||||
#elif defined(EMSCRIPTEN)
|
||||
#include <emscripten/emscripten.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
@ -100,7 +100,7 @@ static int nanosleepDOS(const struct timespec *rqtp, struct timespec *rmtp)
|
||||
#define retro_sleep(msec) (usleep(1000 * (msec)))
|
||||
#elif defined(WIIU)
|
||||
#define retro_sleep(msec) (OSSleepTicks(ms_to_ticks((msec))))
|
||||
#elif defined(EMSCRIPTEN) && defined(EMSCRIPTEN_ASYNCIFY)
|
||||
#elif defined(EMSCRIPTEN)
|
||||
#define retro_sleep(msec) (emscripten_sleep(msec))
|
||||
#else
|
||||
static INLINE void retro_sleep(unsigned msec)
|
||||
|
@ -1,204 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>RetroArch Web Player</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.0/css/font-awesome.min.css">
|
||||
<!-- Material Design Bootstrap -->
|
||||
<link href="//cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.1.1/css/mdb.min.css" rel="stylesheet">
|
||||
<link href="libretro.css" rel="stylesheet" type="text/css">
|
||||
<link rel="shortcut icon" href="media/retroarch.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<!--Navbar-->
|
||||
<nav class="navbar navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<!--navbar content-->
|
||||
<div class="navbar-toggleable-xs">
|
||||
<!--Links-->
|
||||
<ul class="nav navbar-nav">
|
||||
<div class="dropdown">
|
||||
<li class="nav-item dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Core Selection</button>
|
||||
<div class="dropdown-menu dropdown-primary" aria-labelledby="dropdownMenu1" data-dropdown-in="fadeIn" data-dropdown-out="fadeOut" id="core-selector">
|
||||
<a class="dropdown-item" href="." data-core="2048">2048</a>
|
||||
<a class="dropdown-item" href="." data-core="anarch">Anarch</a>
|
||||
<a class="dropdown-item" href="." data-core="ardens">Arduboy (Ardens)</a>
|
||||
<a class="dropdown-item" href="." data-core="arduous">Arduboy (Arduous)</a>
|
||||
<a class="dropdown-item" href="." data-core="bk">Elektronika - BK-0010/BK-0011 (BK)</a>
|
||||
<a class="dropdown-item" href="." data-core="chailove">ChaiLove</a>
|
||||
<a class="dropdown-item" href="." data-core="craft">Minecraft (Craft)</a>
|
||||
<a class="dropdown-item" href="." data-core="DoubleCherryGB">Nintendo - Game Boy / Color (DoubleCherryGB)</a>
|
||||
<a class="dropdown-item" href="." data-core="ecwolf">Wolfenstein 3D (ECWolf)</a>
|
||||
<a class="dropdown-item" href="." data-core="fbalpha2012">Arcade (FB Alpha 2012)</a>
|
||||
<a class="dropdown-item" href="." data-core="fbalpha2012_cps1">Arcade (FB Alpha 2012 CPS1)</a>
|
||||
<a class="dropdown-item" href="." data-core="fbalpha2012_cps2">Arcade (FB Alpha 2012 CPS2)</a>
|
||||
<a class="dropdown-item" href="." data-core="fbalpha2012_neogeo">Arcade (FB Alpha 2012 NeoGeo)</a>
|
||||
<a class="dropdown-item" href="." data-core="fceumm">Nintendo - NES / Famicom (FCEUmm)</a>
|
||||
<a class="dropdown-item" href="." data-core="freechaf">Fairchild ChannelF (FreeChaF)</a>
|
||||
<a class="dropdown-item" href="." data-core="galaksija">Galaksija</a>
|
||||
<a class="dropdown-item" href="." data-core="gambatte">Nintendo - Game Boy / Color (Gambatte)</a>
|
||||
<a class="dropdown-item" href="." data-core="gme">Game Music Emu</a>
|
||||
<a class="dropdown-item" href="." data-core="gearboy">Nintendo - Game Boy / Color (GearBoy)</a>
|
||||
<a class="dropdown-item" href="." data-core="gearcoleco">Coleco - ColecoVision (GearColeco)</a>
|
||||
<a class="dropdown-item" href="." data-core="gearsystem">Sega - MS/GG/SG-1000 (GearSystem)</a>
|
||||
<a class="dropdown-item" href="." data-core="genesis_plus_gx">Sega - MS/GG/MD/CD (Genesis Plus GX)</a>
|
||||
<a class="dropdown-item" href="." data-core="genesis_plus_gx_wide">Sega - MS/GG/MD/CD (Genesis Plus GX Wide)</a>
|
||||
<a class="dropdown-item" href="." data-core="gong">Gong</a>
|
||||
<a class="dropdown-item" href="." data-core="gw">Handheld Electronic (GW)</a>
|
||||
<a class="dropdown-item" href="." data-core="handy">Atari - Lynx (Handy)</a>
|
||||
<a class="dropdown-item" href="." data-core="jaxe">CHIP-8/S-CHIP/XO-CHIP (JAXE)</a>
|
||||
<a class="dropdown-item" href="." data-core="jumpnbump">Jump 'n Bump</a>
|
||||
<a class="dropdown-item" href="." data-core="lowresnx">LowResNX</a>
|
||||
<a class="dropdown-item" href="." data-core="lutro">Lua Engine (Lutro)</a>
|
||||
<a class="dropdown-item" href="." data-core="m2000">Philips - P2000T (M2000)</a>
|
||||
<a class="dropdown-item" href="." data-core="mame2000">Arcade - MAME 2000</a>
|
||||
<a class="dropdown-item" href="." data-core="mame2003">Arcade - MAME 2003</a>
|
||||
<a class="dropdown-item" href="." data-core="mame2003_plus">Arcade - MAME 2003-Plus</a>
|
||||
<a class="dropdown-item" href="." data-core="mednafen_lynx">Atari - Lynx (Beetle Lynx)</a>
|
||||
<a class="dropdown-item" href="." data-core="mednafen_ngp">SNK - Neo Geo Pocket / Color (Beetle Neo Geo Pop)</a>
|
||||
<a class="dropdown-item" href="." data-core="mednafen_pce_fast">NEC - PC Engine / CD (Beetle PC Engine Fast)</a>
|
||||
<a class="dropdown-item" href="." data-core="mednafen_vb">Nintendo - Virtual Boy (Beetle VB)</a>
|
||||
<a class="dropdown-item" href="." data-core="mednafen_wswan">Bandai - WonderSwan/Color (Beetle WonderSwan)</a>
|
||||
<a class="dropdown-item" href="." data-core="mgba">Nintendo - Game Boy Advance (mGBA)</a>
|
||||
<a class="dropdown-item" href="." data-core="minivmac">Mac II (MiniVmac)</a>
|
||||
<a class="dropdown-item" href="." data-core="mu">Palm OS(Mu)</a>
|
||||
<a class="dropdown-item" href="." data-core="mrboom">Bomberman (Mr.Boom)</a>
|
||||
<a class="dropdown-item" href="." data-core="neocd">SNK - Neo Geo CD (NeoCD)</a>
|
||||
<a class="dropdown-item" href="." data-core="nestopia">Nintendo - NES / Famicom (Nestopia)</a>
|
||||
<a class="dropdown-item" href="." data-core="numero">Texas Instruments TI-83 (Numero)</a>
|
||||
<a class="dropdown-item" href="." data-core="nxengine">Cave Story (NX Engine)</a>
|
||||
<a class="dropdown-item" href="." data-core="o2em">Magnavox - Odyssey2 / Philips Videopac+ (O2EM)</a>
|
||||
<a class="dropdown-item" href="." data-core="opera">The 3DO Company - 3DO (Opera)</a>
|
||||
<a class="dropdown-item" href="." data-core="pcsx_rearmed">Sony - PlayStation (PCSX ReARMed)</a>
|
||||
<a class="dropdown-item" href="." data-core="picodrive">Sega - MS/GG/MD/CD/32X (PicoDrive)</a>
|
||||
<a class="dropdown-item" href="." data-core="pocketcdg">PocketCDG</a>
|
||||
<a class="dropdown-item" href="." data-core="prboom">Doom (PrBoom)</a>
|
||||
<a class="dropdown-item" href="." data-core="quasi88">NEC - PC-8000 / PC-8800 series (QUASI88)</a>
|
||||
<a class="dropdown-item" href="." data-core="quicknes">Nintendo - NES / Famicom (QuickNES)</a>
|
||||
<a class="dropdown-item" href="." data-core="retro8">PICO-8 (Retro8)</a>
|
||||
<a class="dropdown-item" href="." data-core="scummvm">ScummVM</a>
|
||||
<a class="dropdown-item" href="." data-core="snes9x2002">Nintendo - SNES / SFC (Snes9x 2002)</a>
|
||||
<a class="dropdown-item" href="." data-core="snes9x2005">Nintendo - SNES / SFC (Snes9x 2005)</a>
|
||||
<a class="dropdown-item" href="." data-core="snes9x2010">Nintendo - SNES / SFC (Snes9x 2010)</a>
|
||||
<a class="dropdown-item" href="." data-core="snes9x">Nintendo - SNES / SFC (Snes9x)</a>
|
||||
<a class="dropdown-item" href="." data-core="squirreljme">Java ME (SquirrelJME)</a>
|
||||
<a class="dropdown-item" href="." data-core="tamalibretro">Bandai - Tamagothci P1 (TamaLIBretro)</a>
|
||||
<a class="dropdown-item" href="." data-core="tgbdual">Nintendo - Game Boy / Color (TGB Dual)</a>
|
||||
<a class="dropdown-item" href="." data-core="theodore">Theodore (Thomson TO8/TO9)</a>
|
||||
<a class="dropdown-item" href="." data-core="tic80">TIC-80</a>
|
||||
<a class="dropdown-item" href="." data-core="tyrquake">Quake (TyrQuake)</a>
|
||||
<a class="dropdown-item" href="." data-core="uw8">MicroW8 (UW8)</a>
|
||||
<a class="dropdown-item" href="." data-core="uzem">Uzebox (Uzem)</a>
|
||||
<a class="dropdown-item" href="." data-core="vaporspec">Vaporspec</a>
|
||||
<a class="dropdown-item" href="." data-core="vba_next">Nintendo - Game Boy Advance (VBA Next)</a>
|
||||
<a class="dropdown-item" href="." data-core="vecx">GCE - Vectrex (Vecx)</a>
|
||||
<a class="dropdown-item" href="." data-core="vice_x64">Commodore - C64 (VICE x64, fast)</a>
|
||||
<a class="dropdown-item" href="." data-core="vice_x64sc">Commodore - C64 (VICE x64sc, accurate)</a>
|
||||
<a class="dropdown-item" href="." data-core="vice_x128">Commodore - C128 (VICE x128)</a>
|
||||
<a class="dropdown-item" href="." data-core="vice_xcbm2">Commodore - CBM-II 6x0/7x0 (VICE xcbm2)</a>
|
||||
<a class="dropdown-item" href="." data-core="vice_xcbm5x0">Commodore - CBM-II 5x0 (xcbm5x0)</a>
|
||||
<a class="dropdown-item" href="." data-core="vice_xpet">Commodore - PET (VICE xpet)</a>
|
||||
<a class="dropdown-item" href="." data-core="vice_xplus4">Commodore - PLUS/4 (VICE xplus4)</a>
|
||||
<a class="dropdown-item" href="." data-core="vice_xscpu64">Commodore - C64 SuperCPU (VICE xscpu4)</a>
|
||||
<a class="dropdown-item" href="." data-core="vice_xvic">Commodore - VIC-20 (VICE xvic)</a>
|
||||
<a class="dropdown-item" href="." data-core="virtualxt">VirtualXT</a>
|
||||
<a class="dropdown-item" href="." data-core="vitaquake2">Quake II (vitaQuake 2)</a>
|
||||
<a class="dropdown-item" href="." data-core="vitaquake2-rogue">Quake II - Ground Zero (vitaQuake2 (rogue))</a>
|
||||
<a class="dropdown-item" href="." data-core="vitaquake2-xatrix">Quake II - The Reckoning (vitaQuake2 (xatrix))</a>
|
||||
<a class="dropdown-item" href="." data-core="vitaquake2-zaero">Quake II - Zaero (vitaQuake2 (zaero))</a>
|
||||
<a class="dropdown-item" href="." data-core="wasm4">WASM4</a>
|
||||
<a class="dropdown-item" href="." data-core="x1">Sharp X1 (X Millenium)</a>
|
||||
<a class="dropdown-item" href="." data-core="xrick">Rick Dangerous (XRick)</a>
|
||||
</div>
|
||||
<button class="btn btn-primary disabled" id="btnRun" disabled>
|
||||
<span class="fa fa-spinner fa-spin" id="icnRun"></span> Run
|
||||
</button>
|
||||
<button class="btn btn-primary disabled" id="btnAdd" disabled>
|
||||
<span class="fa fa-plus" id="icnAdd"></span> Add Content
|
||||
</button>
|
||||
<input style="display: none" type="file" id="btnRom" name="upload" multiple />
|
||||
<button class="btn btn-primary tooltip-enable" id="btnClean" title="Cleanup storage">
|
||||
<span class="fa fa-trash-o" id="icnClean"></span> <span class="sr-only">Cleanup</span>
|
||||
</button>
|
||||
<button class="btn btn-primary disabled tooltip-enable" id="btnMenu" title="Menu toggle" disabled>
|
||||
<span class="fa fa-bars" id="icnMenu"></span> <span class="sr-only">Menu</span>
|
||||
</button>
|
||||
<button class="btn btn-primary disabled tooltip-enable" id="btnFullscreen" title="Fullscreen" disabled>
|
||||
<span class="fa fa-desktop" id="icnFullscreen"></span> <span class="sr-only">Fullscreen</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary tooltip-enable" data-toggle="modal" data-target="#helpModal">Help</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
<div class="toggleMenu">
|
||||
<button class="btn btn-primary" id="btnHideMenu" title="Toggle Menu">
|
||||
<span class="fa fa-chevron-up" id="icnHideMenu"></span> <span class="sr-only">Hide Top Navigation</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Basics steps modal for Web Libretro -->
|
||||
<div class="modal fade" id="helpModal" role="dialog" style="color:black;">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h1 class="modal-title">Basics</h1>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h3><b>Load Core</b></h3>
|
||||
<p>Load your core by clicking on the first tab. Scroll down until you reach the desired Core. We will use Nestopia for now. Don't forget - Content must be compatible with the matched Core.</p>
|
||||
<ul>
|
||||
<li>Nes: <i>NESTOPIA</i></li>
|
||||
<li>Game Boy / Color: <i>Gambatte</i></li>
|
||||
</ul>
|
||||
<p>etc.</p>
|
||||
<p></p>
|
||||
<h3><b>Load Content</b></h3>
|
||||
<p>After selecting Core, click Run. After RetroArch opens, click Add Content and select your compatible ROM.</p>
|
||||
<ul>
|
||||
<li>Nestopia > <i>YourGame.nes</i></li>
|
||||
<li>Gambatte > <i>YourGame.gbc</i></li>
|
||||
</ul>
|
||||
<p>etc.</p>
|
||||
<p></p>
|
||||
<h3><b><span class="fa fa-trash-o"></span> Cleanup Storage</b></h3>
|
||||
<p>The trashcan erases your existing configuration and presets. If the Web Player doesn't start, you should click the trashcan and refresh the cache in your browser (usually F5 or Shift+F5).</p>
|
||||
<p></p>
|
||||
<h3><b><span class="fa fa-bars"></span> Quick Menu</b></h3>
|
||||
<p>If you click on the three line icons, the Quick Menu will open here as in RetroArch.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--/.navbar content-->
|
||||
</nav>
|
||||
<div class="bg-inverse webplayer-container">
|
||||
<div class="webplayer_border text-xs-center" id="canvas_div">
|
||||
<div class="showMenu">
|
||||
<button type="button" class="btn btn-link">
|
||||
<span class="fa fa-chevron-down" id="icnShowMenu"></span> <span class="sr-only">Show Top Navigation</span>
|
||||
</button>
|
||||
</div>
|
||||
<canvas class="webplayer" id="canvas" tabindex="1" oncontextmenu="event.preventDefault()" style="display: none"></canvas>
|
||||
<img class="webplayer-preview img-fluid" src="media/canvas.png" width="960" height="720" alt="RetroArch Logo">
|
||||
</div>
|
||||
</div>
|
||||
<script src="//code.jquery.com/jquery-3.1.0.min.js"></script>
|
||||
<script src="//rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.3.4/js/tether.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/js/bootstrap.min.js"></script>
|
||||
<script src="analytics.js"></script>
|
||||
<!--script src="//wzrd.in/standalone/browserfs@0.6.1"></script-->
|
||||
<script src="browserfs.min.js"></script>
|
||||
<script src="libretro.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,34 +0,0 @@
|
||||
#! /usr/bin/env coffee
|
||||
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
|
||||
symLinks = {}
|
||||
|
||||
rdSync = (dpath, tree, name) ->
|
||||
files = fs.readdirSync(dpath)
|
||||
for file in files
|
||||
# ignore non-essential directories / files
|
||||
continue if file in ['.git', 'node_modules', 'bower_components', 'build'] or file[0] is '.'
|
||||
fpath = dpath + '/' + file
|
||||
try
|
||||
# Avoid infinite loops.
|
||||
lstat = fs.lstatSync(fpath)
|
||||
if lstat.isSymbolicLink()
|
||||
symLinks[lstat.dev] ?= {}
|
||||
# Ignore if we've seen it before
|
||||
continue if symLinks[lstat.dev][lstat.ino]?
|
||||
symLinks[lstat.dev][lstat.ino] = 0
|
||||
|
||||
fstat = fs.statSync(fpath)
|
||||
if fstat.isDirectory()
|
||||
tree[file] = child = {}
|
||||
rdSync(fpath, child, file)
|
||||
else
|
||||
tree[file] = null
|
||||
catch e
|
||||
# Ignore and move on.
|
||||
return tree
|
||||
|
||||
fs_listing = rdSync(process.cwd(), {}, '/')
|
||||
console.log(JSON.stringify(fs_listing))
|
@ -1,120 +0,0 @@
|
||||
/**
|
||||
* RetroArch Web Player
|
||||
*
|
||||
* This provides the basic styling for the RetroArch web player.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Make sure the background of the player is black.
|
||||
* Also make sure line height is 0 so there's no extra space on the bottom.
|
||||
*/
|
||||
.webplayer-container {
|
||||
background-color: black;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Webplayer Preview when not loaded.
|
||||
*/
|
||||
.webplayer-preview {
|
||||
margin: 0 auto;
|
||||
cursor: wait;
|
||||
opacity: 0.2;
|
||||
transition: all 0.8s;
|
||||
-webkit-animation: loading 0.8s ease-in-out infinite alternate;
|
||||
-moz-animation: loading 0.8s ease-in-out infinite alternate;
|
||||
animation: loading 0.8s ease-in-out infinite alternate;
|
||||
}
|
||||
.webplayer-preview.loaded {
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
-webkit-animation: loaded 0.8s ease-in-out;
|
||||
-moz-animation: loaded 0.8s ease-in-out;
|
||||
animation: loaded 0.8s ease-in-out;
|
||||
}
|
||||
@keyframes loaded {
|
||||
from {
|
||||
opacity: 0.2;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@-moz-keyframes loaded {
|
||||
from {
|
||||
opacity: 0.2;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes loaded {
|
||||
from {
|
||||
opacity: 0.2;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes loading{
|
||||
from {
|
||||
opacity: 0.2;
|
||||
}
|
||||
to {
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
@-moz-keyframes loading{
|
||||
from {
|
||||
opacity: 0.2;
|
||||
}
|
||||
to {
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes loading {
|
||||
from {
|
||||
opacity: 0.2;
|
||||
}
|
||||
to {
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the border around the player.
|
||||
*/
|
||||
canvas.webplayer {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: monospace;
|
||||
font-size: 0.7em;
|
||||
height: 95%;
|
||||
width: 95%;
|
||||
border-style: none;
|
||||
border-color: transparent;
|
||||
overflow: auto;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle Top Navigation
|
||||
*/
|
||||
.toggleMenu {
|
||||
float: right;
|
||||
}
|
||||
.showMenu {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
#icnShowMenu {
|
||||
color: #565656 !important;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
box-shadow: none;
|
||||
}
|
@ -1,389 +0,0 @@
|
||||
/**
|
||||
* RetroArch Web Player
|
||||
*
|
||||
* This provides the basic JavaScript for the RetroArch web player.
|
||||
*/
|
||||
var BrowserFS = BrowserFS;
|
||||
var afs;
|
||||
var initializationCount = 0;
|
||||
|
||||
var Module = {
|
||||
noInitialRun: true,
|
||||
arguments: ["-v", "--menu"],
|
||||
|
||||
encoder: new TextEncoder(),
|
||||
message_queue: [],
|
||||
message_out: [],
|
||||
message_accum: "",
|
||||
|
||||
retroArchSend: function(msg) {
|
||||
let bytes = this.encoder.encode(msg + "\n");
|
||||
this.message_queue.push([bytes, 0]);
|
||||
},
|
||||
retroArchRecv: function() {
|
||||
let out = this.message_out.shift();
|
||||
if (out == null && this.message_accum != "") {
|
||||
out = this.message_accum;
|
||||
this.message_accum = "";
|
||||
}
|
||||
return out;
|
||||
},
|
||||
preRun: [
|
||||
function(module) {
|
||||
function stdin() {
|
||||
// Return ASCII code of character, or null if no input
|
||||
while (module.message_queue.length > 0) {
|
||||
var msg = module.message_queue[0][0];
|
||||
var index = module.message_queue[0][1];
|
||||
if (index >= msg.length) {
|
||||
module.message_queue.shift();
|
||||
} else {
|
||||
module.message_queue[0][1] = index + 1;
|
||||
// assumption: msg is a uint8array
|
||||
return msg[index];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stdout(c) {
|
||||
if (c == null) {
|
||||
// flush
|
||||
if (module.message_accum != "") {
|
||||
module.message_out.push(module.message_accum);
|
||||
module.message_accum = "";
|
||||
}
|
||||
} else {
|
||||
let s = String.fromCharCode(c);
|
||||
if (s == "\n") {
|
||||
if (module.message_accum != "") {
|
||||
module.message_out.push(module.message_accum);
|
||||
module.message_accum = "";
|
||||
}
|
||||
} else {
|
||||
module.message_accum = module.message_accum + s;
|
||||
}
|
||||
}
|
||||
}
|
||||
module.FS.init(stdin, stdout);
|
||||
}
|
||||
],
|
||||
postRun: [],
|
||||
onRuntimeInitialized: function() {
|
||||
appInitialized();
|
||||
},
|
||||
print: function(text) {
|
||||
console.log("stdout:", text);
|
||||
},
|
||||
printErr: function(text) {
|
||||
console.log("stderr:", text);
|
||||
},
|
||||
canvas: document.getElementById("canvas"),
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: function(left) {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function cleanupStorage() {
|
||||
localStorage.clear();
|
||||
if (BrowserFS.FileSystem.IndexedDB.isAvailable()) {
|
||||
var req = indexedDB.deleteDatabase("RetroArch");
|
||||
req.onsuccess = function() {
|
||||
console.log("Deleted database successfully");
|
||||
};
|
||||
req.onerror = function() {
|
||||
console.error("Couldn't delete database");
|
||||
};
|
||||
req.onblocked = function() {
|
||||
console.error("Couldn't delete database due to the operation being blocked");
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementById("btnClean").disabled = true;
|
||||
}
|
||||
|
||||
function idbfsInit() {
|
||||
$('#icnLocal').removeClass('fa-globe');
|
||||
$('#icnLocal').addClass('fa-spinner fa-spin');
|
||||
var imfs = new BrowserFS.FileSystem.InMemory();
|
||||
if (BrowserFS.FileSystem.IndexedDB.isAvailable()) {
|
||||
afs = new BrowserFS.FileSystem.AsyncMirror(imfs,
|
||||
new BrowserFS.FileSystem.IndexedDB(function(e, fs) {
|
||||
if (e) {
|
||||
// fallback to imfs
|
||||
afs = new BrowserFS.FileSystem.InMemory();
|
||||
console.error("WEBPLAYER: error: " + e + " falling back to in-memory filesystem");
|
||||
appInitialized();
|
||||
} else {
|
||||
// initialize afs by copying files from async storage to sync storage.
|
||||
afs.initialize(function(e) {
|
||||
if (e) {
|
||||
afs = new BrowserFS.FileSystem.InMemory();
|
||||
console.error("WEBPLAYER: error: " + e + " falling back to in-memory filesystem");
|
||||
appInitialized();
|
||||
} else {
|
||||
idbfsSyncComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
"RetroArch"));
|
||||
}
|
||||
}
|
||||
|
||||
function idbfsSyncComplete() {
|
||||
$('#icnLocal').removeClass('fa-spinner').removeClass('fa-spin');
|
||||
$('#icnLocal').addClass('fa-check');
|
||||
console.log("WEBPLAYER: idbfs setup successful");
|
||||
|
||||
appInitialized();
|
||||
}
|
||||
|
||||
function appInitialized() {
|
||||
/* Need to wait for the file system, the wasm runtime, and the zip download
|
||||
to complete before enabling the Run button. */
|
||||
initializationCount++;
|
||||
if (initializationCount == 3) {
|
||||
setupFileSystem("browser");
|
||||
preLoadingComplete();
|
||||
}
|
||||
}
|
||||
|
||||
function preLoadingComplete() {
|
||||
// Make the Preview image clickable to start RetroArch.
|
||||
$('.webplayer-preview').addClass('loaded').click(function() {
|
||||
startRetroArch();
|
||||
return false;
|
||||
});
|
||||
$('#btnRun').removeClass('disabled').removeAttr("disabled").click(function() {
|
||||
startRetroArch();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
var zipTOC;
|
||||
|
||||
function zipfsInit() {
|
||||
// 256 MB max bundle size
|
||||
let buffer = new ArrayBuffer(256 * 1024 * 1024);
|
||||
let bufferView = new Uint8Array(buffer);
|
||||
let idx = 0;
|
||||
// bundle should be in five parts (this can be changed later)
|
||||
Promise.all([fetch("assets/frontend/bundle.zip.aa"),
|
||||
fetch("assets/frontend/bundle.zip.ab"),
|
||||
fetch("assets/frontend/bundle.zip.ac"),
|
||||
fetch("assets/frontend/bundle.zip.ad"),
|
||||
fetch("assets/frontend/bundle.zip.ae")
|
||||
]).then(function(resps) {
|
||||
Promise.all(resps.map((r) => r.arrayBuffer())).then(function(buffers) {
|
||||
for (let buf of buffers) {
|
||||
if (idx + buf.byteLength > buffer.maxByteLength) {
|
||||
console.error("WEBPLAYER: error: bundle.zip is too large");
|
||||
}
|
||||
bufferView.set(new Uint8Array(buf), idx, buf.byteLength);
|
||||
idx += buf.byteLength;
|
||||
}
|
||||
BrowserFS.FileSystem.ZipFS.computeIndex(BrowserFS.BFSRequire('buffer').Buffer(new Uint8Array(buffer, 0, idx)), function(toc) {
|
||||
zipTOC = toc;
|
||||
appInitialized();
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function setupFileSystem(backend) {
|
||||
// create a mountable filesystem that will server as a root mountpoint for browserfs
|
||||
var mfs = new BrowserFS.FileSystem.MountableFileSystem();
|
||||
|
||||
// create an XmlHttpRequest filesystem for the bundled data
|
||||
var xfs1 = new BrowserFS.FileSystem.ZipFS(zipTOC);
|
||||
// create an XmlHttpRequest filesystem for core assets
|
||||
var xfs2 = new BrowserFS.FileSystem.XmlHttpRequest(".index-xhr", "assets/cores/");
|
||||
|
||||
console.log("WEBPLAYER: initializing filesystem: " + backend);
|
||||
mfs.mount('/home/web_user/retroarch/userdata', afs);
|
||||
|
||||
mfs.mount('/home/web_user/retroarch/', xfs1);
|
||||
mfs.mount('/home/web_user/retroarch/userdata/content/downloads', xfs2);
|
||||
BrowserFS.initialize(mfs);
|
||||
var BFS = new BrowserFS.EmscriptenFS(Module.FS, Module.PATH, Module.ERRNO_CODES);
|
||||
Module.FS.mount(BFS, {
|
||||
root: '/home'
|
||||
}, '/home');
|
||||
console.log("WEBPLAYER: " + backend + " filesystem initialization successful");
|
||||
}
|
||||
|
||||
// Retrieve the value of the given GET parameter.
|
||||
function getParam(name) {
|
||||
var results = new RegExp('[?&]' + name + '=([^&#]*)').exec(window.location.href);
|
||||
if (results) {
|
||||
return results[1] || null;
|
||||
}
|
||||
}
|
||||
|
||||
function startRetroArch() {
|
||||
$('.webplayer').show();
|
||||
$('.webplayer-preview').hide();
|
||||
document.getElementById("btnRun").disabled = true;
|
||||
|
||||
$('#btnAdd').removeClass("disabled").removeAttr("disabled").click(function() {
|
||||
$('#btnRom').click();
|
||||
});
|
||||
$('#btnRom').removeAttr("disabled").change(function(e) {
|
||||
selectFiles(e.target.files);
|
||||
});
|
||||
$('#btnMenu').removeClass("disabled").removeAttr("disabled").click(function() {
|
||||
Module._cmd_toggle_menu();
|
||||
Module.canvas.focus();
|
||||
});
|
||||
$('#btnFullscreen').removeClass("disabled").removeAttr("disabled").click(function() {
|
||||
Module.requestFullscreen(false);
|
||||
Module.canvas.focus();
|
||||
});
|
||||
|
||||
Module.canvas.focus();
|
||||
Module.canvas.addEventListener("pointerdown", function() {
|
||||
Module.canvas.focus();
|
||||
}, false);
|
||||
Module.callMain(Module.arguments);
|
||||
}
|
||||
|
||||
function selectFiles(files) {
|
||||
$('#btnAdd').addClass('disabled');
|
||||
$('#icnAdd').removeClass('fa-plus');
|
||||
$('#icnAdd').addClass('fa-spinner spinning');
|
||||
var count = files.length;
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
filereader = new FileReader();
|
||||
filereader.file_name = files[i].name;
|
||||
filereader.readAsArrayBuffer(files[i]);
|
||||
filereader.onload = function() {
|
||||
uploadData(this.result, this.file_name)
|
||||
};
|
||||
filereader.onloadend = function(evt) {
|
||||
console.log("WEBPLAYER: file: " + this.file_name + " upload complete");
|
||||
if (evt.target.readyState == FileReader.DONE) {
|
||||
$('#btnAdd').removeClass('disabled');
|
||||
$('#icnAdd').removeClass('fa-spinner spinning');
|
||||
$('#icnAdd').addClass('fa-plus');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uploadData(data, name) {
|
||||
var dataView = new Uint8Array(data);
|
||||
Module.FS.createDataFile('/', name, dataView, true, false);
|
||||
|
||||
var data = Module.FS.readFile(name, {
|
||||
encoding: 'binary'
|
||||
});
|
||||
Module.FS.writeFile('/home/web_user/retroarch/userdata/content/' + name, data, {
|
||||
encoding: 'binary'
|
||||
});
|
||||
Module.FS.unlink(name);
|
||||
}
|
||||
|
||||
function switchCore(corename) {
|
||||
localStorage.setItem("core", corename);
|
||||
}
|
||||
|
||||
function switchStorage(backend) {
|
||||
if (backend != localStorage.getItem("backend")) {
|
||||
localStorage.setItem("backend", backend);
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
// When the browser has loaded everything.
|
||||
$(function() {
|
||||
// Enable data clear
|
||||
$('#btnClean').click(function() {
|
||||
cleanupStorage();
|
||||
});
|
||||
|
||||
// Enable all available ToolTips.
|
||||
$('.tooltip-enable').tooltip({
|
||||
placement: 'right'
|
||||
});
|
||||
|
||||
// Allow hiding the top menu.
|
||||
$('.showMenu').hide();
|
||||
$('#btnHideMenu, .showMenu').click(function() {
|
||||
$('nav').slideToggle('slow');
|
||||
$('.showMenu').toggle('slow');
|
||||
});
|
||||
|
||||
// Attempt to disable some default browser keys.
|
||||
var keys = {
|
||||
9: "tab",
|
||||
13: "enter",
|
||||
16: "shift",
|
||||
18: "alt",
|
||||
27: "esc",
|
||||
33: "rePag",
|
||||
34: "avPag",
|
||||
35: "end",
|
||||
36: "home",
|
||||
37: "left",
|
||||
38: "up",
|
||||
39: "right",
|
||||
40: "down",
|
||||
112: "F1",
|
||||
113: "F2",
|
||||
114: "F3",
|
||||
115: "F4",
|
||||
116: "F5",
|
||||
117: "F6",
|
||||
118: "F7",
|
||||
119: "F8",
|
||||
120: "F9",
|
||||
121: "F10",
|
||||
122: "F11",
|
||||
123: "F12"
|
||||
};
|
||||
window.addEventListener('keydown', function(e) {
|
||||
if (keys[e.which]) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// Switch the core when selecting one.
|
||||
$('#core-selector a').click(function() {
|
||||
var coreChoice = $(this).data('core');
|
||||
switchCore(coreChoice);
|
||||
});
|
||||
// Find which core to load.
|
||||
var core = localStorage.getItem("core", core);
|
||||
if (!core) {
|
||||
core = 'gambatte';
|
||||
}
|
||||
loadCore(core);
|
||||
});
|
||||
|
||||
function loadCore(core) {
|
||||
// Make the core the selected core in the UI.
|
||||
var coreTitle = $('#core-selector a[data-core="' + core + '"]').addClass('active').text();
|
||||
$('#dropdownMenu1').text(coreTitle);
|
||||
// Load the Core's related JavaScript.
|
||||
import("./" + core + "_libretro.js").then(script => {
|
||||
script.default(Module).then(mod => {
|
||||
Module = mod;
|
||||
$('#icnRun').removeClass('fa-spinner').removeClass('fa-spin');
|
||||
$('#icnRun').addClass('fa-play');
|
||||
$('#lblDrop').removeClass('active');
|
||||
$('#lblLocal').addClass('active');
|
||||
idbfsInit();
|
||||
zipfsInit();
|
||||
}).catch(err => {
|
||||
console.error("Couldn't instantiate module", err);
|
||||
throw err;
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error("Couldn't load script", err);
|
||||
throw err;
|
||||
});
|
||||
}
|
@ -192,13 +192,13 @@
|
||||
<img class="webplayer-preview img-fluid" src="media/canvas.png" width="960" height="720" alt="RetroArch Logo">
|
||||
</div>
|
||||
</div>
|
||||
<script crossorigin="anonymous" src="//code.jquery.com/jquery-3.1.0.min.js"></script>
|
||||
<script crossorigin="anonymous" src="//rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js"></script>
|
||||
<script crossorigin="anonymous" src="//cdnjs.cloudflare.com/ajax/libs/tether/1.3.4/js/tether.min.js"></script>
|
||||
<script crossorigin="anonymous" src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/js/bootstrap.min.js"></script>
|
||||
<script src="//code.jquery.com/jquery-3.1.0.min.js"></script>
|
||||
<script src="//rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.3.4/js/tether.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.3/js/bootstrap.min.js"></script>
|
||||
<script src="analytics.js"></script>
|
||||
<!--script src="//wzrd.in/standalone/browserfs@0.6.1"></script-->
|
||||
<script type="text/javascript" src="zip.min.js"></script>
|
||||
<script src="browserfs.min.js"></script>
|
||||
<script src="libretro.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -3,8 +3,9 @@
|
||||
*
|
||||
* This provides the basic JavaScript for the RetroArch web player.
|
||||
*/
|
||||
var retroarch_ready = false;
|
||||
var setImmediate;
|
||||
var BrowserFS = BrowserFS;
|
||||
var afs;
|
||||
var initializationCount = 0;
|
||||
|
||||
var Module = {
|
||||
noInitialRun: true,
|
||||
@ -69,7 +70,6 @@ var Module = {
|
||||
],
|
||||
postRun: [],
|
||||
onRuntimeInitialized: function() {
|
||||
retroarch_ready = true;
|
||||
appInitialized();
|
||||
},
|
||||
print: function(text) {
|
||||
@ -86,22 +86,70 @@ var Module = {
|
||||
};
|
||||
|
||||
|
||||
async function cleanupStorage()
|
||||
{
|
||||
localStorage.clear();
|
||||
let storage = await navigator.storage.getDirectory();
|
||||
await storage.remove({recursive: true});
|
||||
document.getElementById("btnClean").disabled = true;
|
||||
function cleanupStorage() {
|
||||
localStorage.clear();
|
||||
if (BrowserFS.FileSystem.IndexedDB.isAvailable()) {
|
||||
var req = indexedDB.deleteDatabase("RetroArch");
|
||||
req.onsuccess = function() {
|
||||
console.log("Deleted database successfully");
|
||||
};
|
||||
req.onerror = function() {
|
||||
console.error("Couldn't delete database");
|
||||
};
|
||||
req.onblocked = function() {
|
||||
console.error("Couldn't delete database due to the operation being blocked");
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementById("btnClean").disabled = true;
|
||||
}
|
||||
|
||||
function appInitialized()
|
||||
{
|
||||
/* Need to wait for the wasm runtime to load before enabling the Run button. */
|
||||
if (retroarch_ready)
|
||||
{
|
||||
setupFileSystem().then(() => { preLoadingComplete(); });
|
||||
}
|
||||
}
|
||||
function idbfsInit() {
|
||||
$('#icnLocal').removeClass('fa-globe');
|
||||
$('#icnLocal').addClass('fa-spinner fa-spin');
|
||||
var imfs = new BrowserFS.FileSystem.InMemory();
|
||||
if (BrowserFS.FileSystem.IndexedDB.isAvailable()) {
|
||||
afs = new BrowserFS.FileSystem.AsyncMirror(imfs,
|
||||
new BrowserFS.FileSystem.IndexedDB(function(e, fs) {
|
||||
if (e) {
|
||||
// fallback to imfs
|
||||
afs = new BrowserFS.FileSystem.InMemory();
|
||||
console.error("WEBPLAYER: error: " + e + " falling back to in-memory filesystem");
|
||||
appInitialized();
|
||||
} else {
|
||||
// initialize afs by copying files from async storage to sync storage.
|
||||
afs.initialize(function(e) {
|
||||
if (e) {
|
||||
afs = new BrowserFS.FileSystem.InMemory();
|
||||
console.error("WEBPLAYER: error: " + e + " falling back to in-memory filesystem");
|
||||
appInitialized();
|
||||
} else {
|
||||
idbfsSyncComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
"RetroArch"));
|
||||
}
|
||||
}
|
||||
|
||||
function idbfsSyncComplete() {
|
||||
$('#icnLocal').removeClass('fa-spinner').removeClass('fa-spin');
|
||||
$('#icnLocal').addClass('fa-check');
|
||||
console.log("WEBPLAYER: idbfs setup successful");
|
||||
|
||||
appInitialized();
|
||||
}
|
||||
|
||||
function appInitialized() {
|
||||
/* Need to wait for the file system, the wasm runtime, and the zip download
|
||||
to complete before enabling the Run button. */
|
||||
initializationCount++;
|
||||
if (initializationCount == 3) {
|
||||
setupFileSystem("browser");
|
||||
preLoadingComplete();
|
||||
}
|
||||
}
|
||||
|
||||
function preLoadingComplete() {
|
||||
// Make the Preview image clickable to start RetroArch.
|
||||
@ -115,63 +163,56 @@ function preLoadingComplete() {
|
||||
});
|
||||
}
|
||||
|
||||
async function setupZipFS(mount) {
|
||||
let buffers = await Promise.all([
|
||||
fetch("assets/frontend/bundle.zip.aa").then((r) => r.arrayBuffer()),
|
||||
fetch("assets/frontend/bundle.zip.ab").then((r) => r.arrayBuffer()),
|
||||
fetch("assets/frontend/bundle.zip.ac").then((r) => r.arrayBuffer()),
|
||||
fetch("assets/frontend/bundle.zip.ad").then((r) => r.arrayBuffer())
|
||||
]);
|
||||
let buffer = new ArrayBuffer(256*1024*1024);
|
||||
let bufferView = new Uint8Array(buffer);
|
||||
let idx = 0;
|
||||
for (let buf of buffers) {
|
||||
if (idx+buf.byteLength > buffer.maxByteLength) {
|
||||
console.log("WEBPLAYER: error: bundle.zip is too large");
|
||||
}
|
||||
bufferView.set(new Uint8Array(buf), idx, buf.byteLength);
|
||||
idx += buf.byteLength;
|
||||
}
|
||||
const zipBuf = new Uint8Array(buffer, 0, idx);
|
||||
const zipReader = new zip.ZipReader(new zip.Uint8ArrayReader(zipBuf), {useWebWorkers:false});
|
||||
const entries = await zipReader.getEntries();
|
||||
for(const file of entries) {
|
||||
if (file.getData && !file.directory) {
|
||||
const writer = new zip.Uint8ArrayWriter();
|
||||
const data = await file.getData(writer);
|
||||
Module.FS.createPreloadedFile(mount+"/"+file.filename, undefined, data, true, true);
|
||||
} else if (file.directory) {
|
||||
Module.FS.mkdirTree(mount+"/"+file.filename);
|
||||
}
|
||||
}
|
||||
await zipReader.close();
|
||||
var zipTOC;
|
||||
|
||||
function zipfsInit() {
|
||||
// 256 MB max bundle size
|
||||
let buffer = new ArrayBuffer(256 * 1024 * 1024);
|
||||
let bufferView = new Uint8Array(buffer);
|
||||
let idx = 0;
|
||||
// bundle should be in five parts (this can be changed later)
|
||||
Promise.all([fetch("assets/frontend/bundle.zip.aa"),
|
||||
fetch("assets/frontend/bundle.zip.ab"),
|
||||
fetch("assets/frontend/bundle.zip.ac"),
|
||||
fetch("assets/frontend/bundle.zip.ad"),
|
||||
fetch("assets/frontend/bundle.zip.ae")
|
||||
]).then(function(resps) {
|
||||
Promise.all(resps.map((r) => r.arrayBuffer())).then(function(buffers) {
|
||||
for (let buf of buffers) {
|
||||
if (idx + buf.byteLength > buffer.maxByteLength) {
|
||||
console.error("WEBPLAYER: error: bundle.zip is too large");
|
||||
}
|
||||
bufferView.set(new Uint8Array(buf), idx, buf.byteLength);
|
||||
idx += buf.byteLength;
|
||||
}
|
||||
BrowserFS.FileSystem.ZipFS.computeIndex(BrowserFS.BFSRequire('buffer').Buffer(new Uint8Array(buffer, 0, idx)), function(toc) {
|
||||
zipTOC = toc;
|
||||
appInitialized();
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function loadIndex(index, path) {
|
||||
for (const key of Object.keys(index)) {
|
||||
if (index[key]) {
|
||||
Module.FS.mkdirTree(path+key+"/");
|
||||
loadIndex(index[key], path+key+"/");
|
||||
} else {
|
||||
Module.FS.open(path+key, "w+");
|
||||
}
|
||||
}
|
||||
}
|
||||
function setupFileSystem(backend) {
|
||||
// create a mountable filesystem that will server as a root mountpoint for browserfs
|
||||
var mfs = new BrowserFS.FileSystem.MountableFileSystem();
|
||||
|
||||
async function setupFileSystem()
|
||||
{
|
||||
Module.FS.mkdirTree("/home/web_user/retroarch/userdata");
|
||||
// create an XmlHttpRequest filesystem for the bundled data
|
||||
var xfs1 = new BrowserFS.FileSystem.ZipFS(zipTOC);
|
||||
// create an XmlHttpRequest filesystem for core assets
|
||||
var xfs2 = new BrowserFS.FileSystem.XmlHttpRequest(".index-xhr", "assets/cores/");
|
||||
|
||||
Module.FS.mount(Module.OPFS, {}, "/home/web_user/retroarch/userdata");
|
||||
|
||||
Module.FS.mkdir("/home/web_user/retroarch/downloads",700);
|
||||
let index = await (await fetch("assets/cores/.index-xhr")).json();
|
||||
let manifest = {};
|
||||
Module.FS.mount(Module.FETCHFS, {"base_url":"assets/cores"}, "/home/web_user/retroarch/downloads");
|
||||
loadIndex(index, "/home/web_user/retroarch/downloads/");
|
||||
console.log("WEBPLAYER: initializing filesystem: " + backend);
|
||||
mfs.mount('/home/web_user/retroarch/userdata', afs);
|
||||
|
||||
setupZipFS("/home/web_user/retroarch");
|
||||
console.log("WEBPLAYER: filesystem initialization successful");
|
||||
mfs.mount('/home/web_user/retroarch/', xfs1);
|
||||
mfs.mount('/home/web_user/retroarch/userdata/content/downloads', xfs2);
|
||||
BrowserFS.initialize(mfs);
|
||||
var BFS = new BrowserFS.EmscriptenFS(Module.FS, Module.PATH, Module.ERRNO_CODES);
|
||||
Module.FS.mount(BFS, {
|
||||
root: '/home'
|
||||
}, '/home');
|
||||
console.log("WEBPLAYER: " + backend + " filesystem initialization successful");
|
||||
}
|
||||
|
||||
// Retrieve the value of the given GET parameter.
|
||||
@ -201,6 +242,7 @@ function startRetroArch() {
|
||||
Module.requestFullscreen(false);
|
||||
Module.canvas.focus();
|
||||
});
|
||||
|
||||
Module.canvas.focus();
|
||||
Module.canvas.addEventListener("pointerdown", function() {
|
||||
Module.canvas.focus();
|
||||
@ -322,26 +364,26 @@ $(function() {
|
||||
loadCore(core);
|
||||
});
|
||||
|
||||
async function downloadScript(src) {
|
||||
let resp = await fetch(src);
|
||||
let blob = await resp.blob();
|
||||
return blob;
|
||||
}
|
||||
|
||||
function loadCore(core) {
|
||||
// Make the core the selected core in the UI.
|
||||
var coreTitle = $('#core-selector a[data-core="' + core + '"]').addClass('active').text();
|
||||
$('#dropdownMenu1').text(coreTitle);
|
||||
downloadScript("./"+core+"_libretro.js").then(scriptBlob => {
|
||||
Module.mainScriptUrlOrBlob = scriptBlob;
|
||||
import(URL.createObjectURL(scriptBlob)).then(script => {
|
||||
script.default(Module).then(mod => {
|
||||
Module = mod;
|
||||
$('#icnRun').removeClass('fa-spinner').removeClass('fa-spin');
|
||||
$('#icnRun').addClass('fa-play');
|
||||
$('#lblDrop').removeClass('active');
|
||||
$('#lblLocal').addClass('active');
|
||||
}).catch(err => { console.error("Couldn't instantiate module",err); throw err; });
|
||||
}).catch(err => { console.error("Couldn't load script",err); throw err; });
|
||||
// Load the Core's related JavaScript.
|
||||
import("./" + core + "_libretro.js").then(script => {
|
||||
script.default(Module).then(mod => {
|
||||
Module = mod;
|
||||
$('#icnRun').removeClass('fa-spinner').removeClass('fa-spin');
|
||||
$('#icnRun').addClass('fa-play');
|
||||
$('#lblDrop').removeClass('active');
|
||||
$('#lblLocal').addClass('active');
|
||||
idbfsInit();
|
||||
zipfsInit();
|
||||
}).catch(err => {
|
||||
console.error("Couldn't instantiate module", err);
|
||||
throw err;
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error("Couldn't load script", err);
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}
|
1
pkg/emscripten/libretro/zip.min.js
vendored
1
pkg/emscripten/libretro/zip.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user