From 8e7a722c3908f6df8ca4943ee0facf594991f41a Mon Sep 17 00:00:00 2001 From: Lock l00p Date: Tue, 23 Apr 2024 02:14:01 +0000 Subject: [PATCH 01/10] Begin WASM support --- Makefile | 6 + platform/SDL2Common/source/sdl2basehost.cpp | 3 +- platform/Webassembly/Makefile | 119 ++++++++++ platform/Webassembly/pre.js | 32 +++ platform/Webassembly/shell.htm | 64 ++++++ platform/Webassembly/source/SDL2Host.cpp | 242 ++++++++++++++++++++ source/main.cpp | 16 +- source/vm.cpp | 13 ++ 8 files changed, 492 insertions(+), 3 deletions(-) create mode 100644 platform/Webassembly/Makefile create mode 100644 platform/Webassembly/pre.js create mode 100644 platform/Webassembly/shell.htm create mode 100644 platform/Webassembly/source/SDL2Host.cpp diff --git a/Makefile b/Makefile index 15d25ccf..12182aeb 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,9 @@ clean-miyoomini: clean-windows: @$(MAKE) -C platform/windows clean +clean-wasm: + @$(MAKE) -C platform/Webassembly clean + 3ds: @$(MAKE) -C platform/3ds @@ -75,6 +78,9 @@ vita: sdl2: @$(MAKE) -C platform/SDL2Desktop +wasm: + @$(MAKE) -C platform/Webassembly + sdl: @$(MAKE) -C platform/SDL1_2 diff --git a/platform/SDL2Common/source/sdl2basehost.cpp b/platform/SDL2Common/source/sdl2basehost.cpp index 44fb7f8c..e3601793 100644 --- a/platform/SDL2Common/source/sdl2basehost.cpp +++ b/platform/SDL2Common/source/sdl2basehost.cpp @@ -186,7 +186,8 @@ void Host::setPlatformParams( void Host::oneTimeSetup(Audio* audio){ - if (SDL_Init(SDL_INIT_EVERYTHING) != 0) + //Haptic support is not being used and many devices are not compiled with haptic support + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER|SDL_INIT_JOYSTICK) != 0) { fprintf(stderr, "SDL could not initialize\n"); return; diff --git a/platform/Webassembly/Makefile b/platform/Webassembly/Makefile new file mode 100644 index 00000000..6880f34b --- /dev/null +++ b/platform/Webassembly/Makefile @@ -0,0 +1,119 @@ + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing header files +# +#--------------------------------------------------------------------------------- +TARGET := FAKE08.js +BUILD := build +SOURCES := ${SOURCES} ../SDL2Common/source source +INCLUDES := ${INCLUDES} + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +CC = $(CXX) + +CFLAGS := -g -Wall -Wno-deprecated -ffunction-sections -std=c++17 -sUSE_SDL=2 \ + $(DEFINES) + +CFLAGS += $(INCLUDE) -DVER_STR=\"$(APP_VERSION)\" + +CXXFLAGS := $(CFLAGS) -fno-rtti +#-std=gnu++11 was used before... not sure of difference + +LIBS := -lSDL2 + +LDFLAGS := $(LIBS) -sSINGLE_FILE -sUSE_SDL=2 -sINITIAL_MEMORY=100mb -lidbfs.js --pre-js $(CURDIR)/../pre.js -s EXPORTED_RUNTIME_METHODS=callMain -fsanitize=address --shell-file $(CURDIR)/../shell.htm + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET) $(OUTPUT) + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT) + +$(OUTPUT) : $(OFILES) + $(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/platform/Webassembly/pre.js b/platform/Webassembly/pre.js new file mode 100644 index 00000000..f269da0f --- /dev/null +++ b/platform/Webassembly/pre.js @@ -0,0 +1,32 @@ +//current command in ascii decimal +let currentcmd = [0,0,0] +let currentfile = ""; +const sleep = ms => new Promise(r => setTimeout(r,ms)); +Module['print'] = function(text){console.log(text);} +Module['preRun'] = function() +{ + + function stdin(){return 10}; + var stdout = null; + var stderr = null; + FS.init(stdin,stdout,stderr); + FS.mount(IDBFS,{},"/home/web_user/"); + +} +Module['noInitialRun'] = true +document.addEventListener('click', (ev) => { + console.log("event is captured only once."); + args = [] + FS.syncfs(true,function(){ + try { + FS.mkdir("/home/web_user/p8carts") + } catch (error) { + + } + + Module.callMain(args); +}); + + }, { once: true }); + + \ No newline at end of file diff --git a/platform/Webassembly/shell.htm b/platform/Webassembly/shell.htm new file mode 100644 index 00000000..fc252a15 --- /dev/null +++ b/platform/Webassembly/shell.htm @@ -0,0 +1,64 @@ + + + + + + + Fake-08 + + + + + + + + + + + + + {{{ SCRIPT }}} + + + + \ No newline at end of file diff --git a/platform/Webassembly/source/SDL2Host.cpp b/platform/Webassembly/source/SDL2Host.cpp new file mode 100644 index 00000000..6718b70c --- /dev/null +++ b/platform/Webassembly/source/SDL2Host.cpp @@ -0,0 +1,242 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +using namespace std; + +#include "../../SDL2Common/source/sdl2basehost.h" +#include "../../../source/hostVmShared.h" +#include "../../../source/nibblehelpers.h" +#include "../../../source/filehelpers.h" +#include "../../../source/logger.h" + +#include "../../../source/emojiconversion.h" + +// sdl +#include +#include + +/*#define WINDOW_SIZE_X 1280 +#define WINDOW_SIZE_Y 720*/ + +#define WINDOW_FLAGS 0 + +#define RENDERER_FLAGS SDL_RENDERER_ACCELERATED +#define PIXEL_FORMAT SDL_PIXELFORMAT_ARGB8888 + +#define KB_ENABLED true + +SDL_Event event; + + +string _desktopSdl2SettingsDir = "fake08"; +string _desktopSdl2SettingsPrefix = "fake08/"; +string _desktopSdl2customBiosLua = "cartpath = \"~/p8carts/\"\n" + "selectbtn = \"z\"\n" + "pausebtn = \"esc\"\n" + "exitbtn = \"close window\"\n" + "sizebtn = \"\""; + +Host::Host() +{ + + SDL_DisplayMode current; + + SDL_Init(SDL_INIT_VIDEO); + + int should_be_zero = SDL_GetCurrentDisplayMode(0, ¤t); + + if(should_be_zero != 0) { + // In case of error... + SDL_Log("Could not get display mode for video display #%d: %s", 0, SDL_GetError()); + } + + else { + // On success, print the current display mode. + SDL_Log("Display #%d: current display mode is %dx%dpx @ %dhz.", 0, current.w, current.h, current.refresh_rate); + } + + int WINDOW_SIZE_X=current.w; + int WINDOW_SIZE_Y=current.h; + + struct stat st = {0}; + + int res = chdir(getenv("HOME")); + if (res == 0 && stat(_desktopSdl2SettingsDir.c_str(), &st) == -1) { + res = mkdir(_desktopSdl2SettingsDir.c_str(), 0777); + } + + string cartdatadir = _desktopSdl2SettingsPrefix + "cdata"; + if (res == 0 && stat(cartdatadir.c_str(), &st) == -1) { + res = mkdir(cartdatadir.c_str(), 0777); + } + + #if KB_ENABLED + SDL_StartTextInput(); + #endif + + std::string home = getenv("HOME"); + + std::string fullCartDir = home + "/p8carts"; + + setPlatformParams( + WINDOW_SIZE_X, + WINDOW_SIZE_Y, + WINDOW_FLAGS, + RENDERER_FLAGS, + PIXEL_FORMAT, + _desktopSdl2SettingsPrefix, + _desktopSdl2customBiosLua, + fullCartDir + ); +} + + +InputState_t Host::scanInput(){ + currKDown = 0; + uint8_t kUp = 0; + stretchKeyPressed = false; + + currKBDown = false; + currKBKey = ""; + + while (SDL_PollEvent(&event)) { + switch (event.type) { + + #if KB_ENABLED + case SDL_TEXTINPUT: + //Logger_Write( charset::upper_to_emoji(event.text.text).c_str() ); + //Logger_Write("\n"); + currKBKey = charset::upper_to_emoji(event.text.text); + currKBDown = true; + + break; + #endif + + + case SDL_KEYDOWN: + + #if KB_ENABLED + switch (event.key.keysym.scancode) + { + case SDL_SCANCODE_BACKSPACE: currKBDown = true; currKBKey = "\b"; break; + case SDL_SCANCODE_RETURN: currKBDown = true; currKBKey = "\r"; break; + case SDL_SCANCODE_ESCAPE: currKBDown = true; currKBKey = "\27"; break; + case SDL_SCANCODE_TAB: currKBDown = true; currKBKey = "\t"; break; + default : break; + } + #endif + + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE:case SDLK_RETURN:case SDLK_RETURN2: + currKDown |= P8_KEY_PAUSE; break; + case SDLK_LEFT: currKDown |= P8_KEY_LEFT; break; + case SDLK_RIGHT: currKDown |= P8_KEY_RIGHT; break; + case SDLK_UP: currKDown |= P8_KEY_UP; break; + case SDLK_DOWN: currKDown |= P8_KEY_DOWN; break; + case SDLK_z: currKDown |= P8_KEY_X; break; + case SDLK_x: currKDown |= P8_KEY_O; break; + case SDLK_c: currKDown |= P8_KEY_X; break; + case SDLK_r: stretchKeyPressed = true; break; + case SDLK_F2: currKDown |= P8_KEY_7; break; + #ifndef NOUPLOAD + case SDLK_SLASH: EM_ASM(file_selector.click();); break; + case SDLK_PERIOD: EM_ASM(FS.syncfs(false,function(err) { + if(err) + { + alert("FileSystem Save Error: " + err); + return false; + } + + alert("Filesystem Saved!"); + return true; + }));break; + #endif + //case SDLK_F2: currKBKey = "F2"; currKBDown = true; break; + //case SDLK_F4: currKBKey = "F4"; currKBDown = true; break; + } + break; + + case SDL_KEYUP: + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE:case SDLK_RETURN:case SDLK_RETURN2: + kUp |= P8_KEY_PAUSE; break; + case SDLK_LEFT: kUp |= P8_KEY_LEFT; break; + case SDLK_RIGHT: kUp |= P8_KEY_RIGHT; break; + case SDLK_UP: kUp |= P8_KEY_UP; break; + case SDLK_DOWN: kUp |= P8_KEY_DOWN; break; + case SDLK_z: kUp |= P8_KEY_X; break; + case SDLK_x: kUp |= P8_KEY_O; break; + case SDLK_c: kUp |= P8_KEY_X; break; + } + break; + + case SDL_QUIT: + quit = 1; + break; + } + } + + int mouseX = 0; + int mouseY = 0; + uint32_t sdlMouseBtnState = SDL_GetMouseState(&mouseX, &mouseY); + //adjust for scale + mouseX -= mouseOffsetX; + mouseY -= mouseOffsetY; + mouseX /= scaleX; + mouseY /= scaleY; + uint8_t picoMouseState = 0; + if (sdlMouseBtnState & SDL_BUTTON(SDL_BUTTON_LEFT)) { + picoMouseState |= 1; + } + if (sdlMouseBtnState & SDL_BUTTON(SDL_BUTTON_MIDDLE)) { + picoMouseState |= 4; + } + if (sdlMouseBtnState & SDL_BUTTON(SDL_BUTTON_RIGHT)) { + picoMouseState |= 2; + } + + currKHeld |= currKDown; + currKHeld ^= kUp; + + return InputState_t { + currKDown, + currKHeld, + (int16_t)mouseX, + (int16_t)mouseY, + picoMouseState, + currKBDown, + currKBKey + }; +} + +vector Host::listcarts(){ + vector carts; + + DIR *dir; + struct dirent *ent; + if ((dir = opendir (_cartDirectory.c_str())) != NULL) { + /* print all the files and directories within directory */ + while ((ent = readdir (dir)) != NULL) { + if (isCartFile(ent->d_name)){ + carts.push_back(ent->d_name); + } + } + closedir (dir); + } else { + /* could not open directory */ + perror (""); + } + + return carts; +} + diff --git a/source/main.cpp b/source/main.cpp index 6fffa7a4..6dc3dd77 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -16,7 +16,17 @@ #endif +#if __EMSCRIPTEN__ +#include + + + +void loop(void* vm){ + static_cast(vm)->GameLoop(); +} + +#endif int main(int argc, char* argv[]) { @@ -70,9 +80,11 @@ int main(int argc, char* argv[]) // Main loop Logger_Write("Starting main loop\n"); - + #if __EMSCRIPTEN__ + emscripten_set_main_loop_arg(loop,vm,60,true); + #else vm->GameLoop(); - + #endif Logger_Write("Turning off vm and exiting logger\n"); vm->CloseCart(); diff --git a/source/vm.cpp b/source/vm.cpp index 36316293..dc3ed27c 100644 --- a/source/vm.cpp +++ b/source/vm.cpp @@ -32,6 +32,9 @@ #include //} +#ifdef __EMSCRIPTEN__ + #include +#endif using namespace z8; static const char BiosCartName[] = "__FAKE08-BIOS.p8"; @@ -689,15 +692,23 @@ string Vm::GetBiosError() { } void Vm::GameLoop() { + #ifndef __EMSCRIPTEN__ while (_host->shouldRunMainLoop()) { + #endif //shouldn't need to set this every frame _host->setTargetFps(_targetFps); //is this better at the end of the loop? _host->waitForTargetFps(); + #ifndef __EMSCRIPTEN__ if (_host->shouldQuit()) break; // break in order to return to hbmenu + + #else + if (_host->shouldQuit()) emscripten_cancel_main_loop(); + + #endif //this should probably be handled just in the host class _host->changeStretch(); @@ -716,7 +727,9 @@ void Vm::GameLoop() { _host->playFilledAudioBuffer(); } + #ifndef __EMSCRIPTEN__ } + #endif } bool Vm::ExecuteLua(string luaString, string callbackFunction){ From 73e313d83c1751c33e84e9234ca9b6af55af2d34 Mon Sep 17 00:00:00 2001 From: Lock l00p Date: Tue, 23 Apr 2024 02:47:27 +0000 Subject: [PATCH 02/10] Get file uploads to work --- platform/Webassembly/Makefile | 2 +- platform/Webassembly/shell.htm | 3 ++- platform/Webassembly/source/{SDL2Host.cpp => WASMHost.cpp} | 2 +- source/main.cpp | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) rename platform/Webassembly/source/{SDL2Host.cpp => WASMHost.cpp} (98%) diff --git a/platform/Webassembly/Makefile b/platform/Webassembly/Makefile index 6880f34b..1316cf30 100644 --- a/platform/Webassembly/Makefile +++ b/platform/Webassembly/Makefile @@ -26,7 +26,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti LIBS := -lSDL2 -LDFLAGS := $(LIBS) -sSINGLE_FILE -sUSE_SDL=2 -sINITIAL_MEMORY=100mb -lidbfs.js --pre-js $(CURDIR)/../pre.js -s EXPORTED_RUNTIME_METHODS=callMain -fsanitize=address --shell-file $(CURDIR)/../shell.htm +LDFLAGS := $(LIBS) -sSINGLE_FILE -sUSE_SDL=2 -sINITIAL_MEMORY=100mb -lidbfs.js --pre-js $(CURDIR)/../pre.js -s EXPORTED_RUNTIME_METHODS=callMain -fsanitize=address --shell-file $(CURDIR)/../shell.htm -sNO_DISABLE_EXCEPTION_CATCHING #--------------------------------------------------------------------------------- diff --git a/platform/Webassembly/shell.htm b/platform/Webassembly/shell.htm index fc252a15..5f360b73 100644 --- a/platform/Webassembly/shell.htm +++ b/platform/Webassembly/shell.htm @@ -20,10 +20,11 @@ Module['preRun'] = []