diff --git a/.gitignore b/.gitignore
index 7aab984b..90427473 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,7 @@
*.smdh
*.bnr
*.cia
-
+*.html
*.nacp
*.nro
*.nso
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/README.md b/README.md
index 63e53da0..a03ffd80 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,8 @@ Installation will vary by console and executable type. If it is a console with a
Pico 8 cart files go in the `p8carts/` directory of your memory card (SD card on 3DS, Switch, and Wii U, memory card at `ux0:/` on Vita). `.p8` text file carts and `.p8.png` image file carts are supported.
+The Standalone webpage is different. You use the '/' key to upload, the '.' key to save everything, and the '`' key to reset the filesystem (don't worry, it will ask you if you're sure you want to reset everything.)
+
Launch FAKE-08 either via the homebrew menu or normal system UI (depending on how you installed). Use left and right to cycle through carts on the SD card. Choose a cart using the `A` (Nintendo consoles) or `X`(Vita) button. To exit the currently running cart, press `Start` or `+` to open the pause menu and select `Exit to Menu`. Press `R` to cycle between rendering sizes. Press `L` and `R` simultaneously to exit the appication. You can also close it via your console's operating system controls (home button etc).
For bittboy and similar consoles, back up `emus/pico8/pico8.elf` and replace it with the one from the release. Place your cart files in `roms/pico-8/` and use the front end of choice to launch games. Press the menu button to return to the menu (though you can also press start and exit to the FAKE-08 bios menu if you would like).
@@ -42,6 +44,8 @@ Building for bittboy requires builing your own toolchain first (and will probabl
Building for Miyoo mini uses shauninman's Union Miyoo Mini toolchain: https://github.com/shauninman/union-miyoomini-toolchain
+Building the standalone webpage requires emsdk: The installation instructions are available at https://emscripten.org/docs/getting_started/downloads.html
+When emsdk is installed (and sourced), use `emmake make wasm` to build.
## Acknowledgements
* Zep/Lexaloffle software for making pico 8. Buy a copy if you can. You won't regret it. https://www.lexaloffle.com/pico-8.php
* Nintendo Homebrew Community
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..32bfae15
--- /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.html
+BUILD := build
+SOURCES := ${SOURCES} 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 -sASYNCIFY -sINITIAL_MEMORY=100mb -sALLOW_MEMORY_GROWTH -lidbfs.js --pre-js $(CURDIR)/../pre.js -s EXPORTED_RUNTIME_METHODS=callMain -fsanitize=address --shell-file $(CURDIR)/../shell.htm -sNO_DISABLE_EXCEPTION_CATCHING -O3
+
+
+#---------------------------------------------------------------------------------
+# 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..70c982fe
--- /dev/null
+++ b/platform/Webassembly/pre.js
@@ -0,0 +1,39 @@
+//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/");
+ FS.chdir("/home/web_user");
+
+}
+Module['noInitialRun'] = true
+document.addEventListener('click', (ev) => {
+ console.log("event is captured only once.");
+ args = []
+ document.getElementById("instructions").remove();
+ FS.syncfs(true,function(){
+ try {
+ FS.mkdir("/home/web_user/p8carts")
+ } catch (error) {
+
+ }
+ try {
+ FS.mkdir("/home/web_user/fake08")
+ } 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..153d9f1d
--- /dev/null
+++ b/platform/Webassembly/shell.htm
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+ Fake-08
+
+
+
+
+
+
+ Once the page fully loads, click on the page to start!
+
+
+
+
+
+ {{{ SCRIPT }}}
+
+
+
+
\ No newline at end of file
diff --git a/platform/Webassembly/source/WASMHost.cpp b/platform/Webassembly/source/WASMHost.cpp
new file mode 100644
index 00000000..c4bcf690
--- /dev/null
+++ b/platform/Webassembly/source/WASMHost.cpp
@@ -0,0 +1,247 @@
+
+#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 = "/home/web_user/fake08";
+string _desktopSdl2SettingsPrefix = "/home/web_user/fake08/";
+string _desktopSdl2customBiosLua = "cartpath = \"~/p8carts/\"\n"
+ "selectbtn = \"z\"\n"
+ "pausebtn = \"esc\"\n"
+ "exitbtn = \"close tab\"\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(cuurentdir = "p8carts"; 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;
+ case SDLK_BACKQUOTE: EM_ASM(if(confirm("Do you want to erase all carts?")){
+ FS.syncfs(true, function(){
+ FS.syncfs(false,function(){});
+ })
+ });
+ #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/platform/Webassembly/source/sdl2basehost.cpp b/platform/Webassembly/source/sdl2basehost.cpp
new file mode 100644
index 00000000..46b60deb
--- /dev/null
+++ b/platform/Webassembly/source/sdl2basehost.cpp
@@ -0,0 +1,453 @@
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+using namespace std;
+
+#include "sdl2basehost.h"
+#include "../../../source/hostVmShared.h"
+#include "../../../source/nibblehelpers.h"
+#include "../../../source/logger.h"
+#include "../../../source/filehelpers.h"
+
+// sdl
+#include
+
+#define SAMPLERATE 22050
+#define SAMPLESPERBUF (SAMPLERATE / 30)
+#define NUM_BUFFERS 2
+
+int _windowWidth = 128;
+int _windowHeight = 128;
+
+
+int _screenWidth = 128;
+int _screenHeight = 128;
+
+int _maxNoStretchWidth = 128;
+int _maxNoStretchHeight = 128;
+
+const int PicoScreenWidth = 128;
+const int PicoScreenHeight = 128;
+
+uint32_t _windowFlags;
+uint32_t _rendererFlags;
+uint32_t _pixelFormat;
+
+uint32_t last_time;
+uint32_t now_time;
+uint32_t frame_time;
+uint32_t targetFrameTimeMs;
+
+uint8_t currKDown;
+uint8_t currKHeld;
+bool stretchKeyPressed = false;
+
+Audio* _audio;
+
+SDL_Window* window;
+SDL_Renderer *renderer;
+SDL_Texture *texture = NULL;
+SDL_AudioSpec want, have;
+SDL_AudioDeviceID dev;
+void *pixels;
+uint8_t *base;
+int pitch;
+
+SDL_Rect DestR;
+SDL_Rect SrcR;
+double textureAngle;
+SDL_RendererFlip flip;
+
+int joystickCount = 0;
+
+bool audioInitialized = false;
+
+
+void postFlipFunction(){
+ // We're done rendering, so we end the frame here.
+ SDL_UnlockTexture(texture);
+ SDL_RenderCopyEx(renderer, texture, &SrcR, &DestR, textureAngle, NULL, flip);
+
+ SDL_RenderPresent(renderer);
+}
+
+void audioCleanup(){
+ audioInitialized = false;
+
+ SDL_CloseAudioDevice(dev);
+}
+
+
+void FillAudioDeviceBuffer(void* UserData, Uint8* DeviceBuffer, int Length)
+{
+ _audio->FillAudioBuffer(DeviceBuffer, 0, Length / 4);
+}
+
+void audioSetup(){
+ //modifed from SDL docs: https://wiki.libsdl.org/SDL_OpenAudioDevice
+
+ SDL_memset(&want, 0, sizeof(want)); /* or SDL_zero(want) */
+ want.freq = SAMPLERATE;
+ want.format = AUDIO_S16;
+ want.channels = 2;
+ want.samples = 4096;
+ want.callback = FillAudioDeviceBuffer;
+
+
+ dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
+ if (dev == 0) {
+ Logger_Write("Failed to open audio: %s", SDL_GetError());
+ } else {
+ if (have.format != want.format) { /* we let this one thing change. */
+ Logger_Write("We didn't get requested audio format.");
+ }
+ SDL_PauseAudioDevice(dev, 0); /* start audio playing. */
+ audioInitialized = true;
+ }
+}
+
+void _changeStretch(StretchOption newStretch){
+ if (newStretch == PixelPerfect) {
+ _screenWidth = PicoScreenWidth;
+ _screenHeight = PicoScreenHeight;
+ }
+ else if (newStretch == StretchToFit) {
+ _screenWidth = _windowHeight;
+ _screenHeight = _windowHeight;
+ }
+ else if (newStretch == StretchToFill){
+ _screenWidth = _windowWidth;
+ _screenHeight = _windowHeight;
+ }
+ else if (newStretch == PixelPerfectStretch) {
+ _screenWidth = _maxNoStretchWidth;
+ _screenHeight = _maxNoStretchHeight;
+ }
+ else if (newStretch == FourByThreeVertPerfect) {
+ _screenWidth = _maxNoStretchHeight * 4 / 3;
+ _screenHeight = _maxNoStretchHeight;
+ }
+ else if (newStretch == FourByThreeStretch) {
+ _screenWidth = _windowHeight * 4 / 3;
+ _screenHeight = _windowHeight;
+ }
+
+
+ DestR.x = _windowWidth / 2 - _screenWidth / 2;
+ DestR.y = _windowHeight / 2 - _screenHeight / 2;
+ DestR.w = _screenWidth;
+ DestR.h = _screenHeight;
+
+ SrcR.x = 0;
+ SrcR.y = 0;
+ SrcR.w = PicoScreenWidth;
+ SrcR.h = PicoScreenHeight;
+
+ textureAngle = 0;
+ flip = SDL_FLIP_NONE;
+}
+
+void Host::setPlatformParams(
+ int windowWidth,
+ int windowHeight,
+ uint32_t sdlWindowFlags,
+ uint32_t sdlRendererFlags,
+ uint32_t sdlPixelFormat,
+ std::string logFilePrefix,
+ std::string customBiosLua,
+ std::string cartDirectory)
+{
+ _windowWidth = windowWidth;
+ _windowHeight = windowHeight;
+
+ //assume wide screen, height is limiting factor
+ int maxNoStretchFactor = _windowHeight / PicoScreenHeight;
+
+ _maxNoStretchWidth = maxNoStretchFactor * PicoScreenWidth;
+ _maxNoStretchHeight = maxNoStretchFactor * PicoScreenHeight;
+
+ _screenWidth = _maxNoStretchWidth;
+ _screenHeight = _maxNoStretchHeight;
+
+ _windowFlags = sdlWindowFlags;
+ _rendererFlags = sdlRendererFlags;
+ _pixelFormat = sdlPixelFormat;
+
+ _logFilePrefix = logFilePrefix;
+ _customBiosLua = customBiosLua;
+ _cartDirectory = cartDirectory;
+
+}
+
+
+void Host::oneTimeSetup(Audio* audio){
+ //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;
+ }
+
+ window = SDL_CreateWindow("FAKE-08", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, _windowWidth, _windowHeight, _windowFlags);
+ if (!window)
+ {
+ quit = 1;
+ return;
+ }
+
+ renderer = SDL_CreateRenderer(window, -1, _rendererFlags);
+ if (!renderer)
+ {
+ quit = 1;
+ return;
+ }
+
+ texture = SDL_CreateTexture(renderer, _pixelFormat, SDL_TEXTUREACCESS_STREAMING, PicoScreenWidth, PicoScreenHeight);
+ if (!texture)
+ {
+ fprintf(stderr, "Error creating texture.\n");
+ quit = 1;
+ return;
+ }
+
+ atexit(SDL_Quit);
+
+ _audio = audio;
+ audioSetup();
+
+ joystickCount = SDL_NumJoysticks();
+ for (int i = 0; i < joystickCount; i++) {
+ if (SDL_JoystickOpen(i) == NULL) {
+ printf("Failed to open joystick %d!\n", i);
+ quit = 1;
+ }
+ }
+
+ last_time = 0;
+ now_time = 0;
+ frame_time = 0;
+ targetFrameTimeMs = 0;
+
+ currKDown = 0;
+ currKHeld = 0;
+
+ loadSettingsIni();
+
+ _changeStretch(stretch);
+
+ scaleX = _screenWidth / (float)PicoScreenWidth;
+ scaleY = _screenHeight / (float)PicoScreenHeight;
+ mouseOffsetX = DestR.x;
+ mouseOffsetY = DestR.y;
+}
+
+void Host::oneTimeCleanup(){
+ saveSettingsIni();
+
+ audioCleanup();
+
+ SDL_DestroyTexture(texture);
+ SDL_DestroyRenderer(renderer);
+ SDL_DestroyWindow(window);
+ SDL_Quit();
+}
+
+void Host::setTargetFps(int targetFps){
+ targetFrameTimeMs = 1000 / targetFps;
+}
+
+void Host::changeStretch(){
+ if (stretchKeyPressed && resizekey == YesResize) {
+ StretchOption newStretch = stretch;
+
+ if (stretch == PixelPerfectStretch) {
+ newStretch = PixelPerfect;
+ }
+ else if (stretch == PixelPerfect) {
+ newStretch = StretchToFit;
+ }
+ else if (stretch == StretchToFit) {
+ newStretch = StretchToFill;
+ }
+ else if (stretch == StretchToFill) {
+ newStretch = FourByThreeVertPerfect;
+ }
+ else if (stretch == FourByThreeVertPerfect) {
+ newStretch = FourByThreeStretch;
+ }
+ else if (stretch == FourByThreeStretch) {
+ newStretch = PixelPerfectStretch;
+ }
+
+ _changeStretch(newStretch);
+
+ stretch = newStretch;
+ scaleX = _screenWidth / (float)PicoScreenWidth;
+ scaleY = _screenHeight / (float)PicoScreenHeight;
+ mouseOffsetX = DestR.x;
+ mouseOffsetY = DestR.y;
+ }
+}
+
+void Host::forceStretch(StretchOption newStretch) {
+ _changeStretch(newStretch);
+ stretch = newStretch;
+ scaleX = _screenWidth / (float)PicoScreenWidth;
+ scaleY = _screenHeight / (float)PicoScreenHeight;
+ mouseOffsetX = DestR.x;
+ mouseOffsetY = DestR.y;
+}
+
+bool Host::shouldQuit() {
+ return quit == 1;
+}
+
+void Host::waitForTargetFps(){
+ now_time = SDL_GetTicks();
+ frame_time = now_time - last_time;
+ last_time = now_time;
+
+
+ //sleep for remainder of time
+ if (frame_time < targetFrameTimeMs) {
+ uint32_t msToSleep = targetFrameTimeMs - frame_time;
+
+ emscripten_sleep(msToSleep);
+
+ last_time += msToSleep;
+ }
+}
+
+
+void Host::drawFrame(uint8_t* picoFb, uint8_t* screenPaletteMap, uint8_t drawMode){
+ //clear screen to all black
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
+ SDL_RenderClear(renderer);
+
+ SDL_LockTexture(texture, NULL, &pixels, &pitch);
+
+ for (int y = 0; y < PicoScreenHeight; y ++){
+ for (int x = 0; x < PicoScreenWidth; x ++){
+ uint8_t c = getPixelNibble(x, y, picoFb);
+ Color col = _paletteColors[screenPaletteMap[c]];
+
+ base = ((Uint8 *)pixels) + (4 * ( y * PicoScreenHeight + x));
+ base[0] = col.Blue;
+ base[1] = col.Green;
+ base[2] = col.Red;
+ base[3] = col.Alpha;
+ }
+ }
+
+ SrcR.x = 0;
+ SrcR.y = 0;
+
+ switch(drawMode){
+ case 1:
+ SrcR.w = 64;
+ SrcR.h = PicoScreenHeight;
+ textureAngle = 0;
+ flip = SDL_FLIP_NONE;
+ break;
+ case 2:
+ SrcR.w = PicoScreenWidth;
+ SrcR.h = 64;
+ textureAngle = 0;
+ flip = SDL_FLIP_NONE;
+ break;
+ case 3:
+ SrcR.w = 64;
+ SrcR.h = 64;
+ textureAngle = 0;
+ flip = SDL_FLIP_NONE;
+ break;
+ //todo: mirroring
+ //case 4,6,7
+ case 129:
+ SrcR.w = PicoScreenWidth;
+ SrcR.h = PicoScreenHeight;
+ textureAngle = 0;
+ flip = SDL_FLIP_HORIZONTAL;
+ break;
+ case 130:
+ SrcR.w = PicoScreenWidth;
+ SrcR.h = PicoScreenHeight;
+ textureAngle = 0;
+ flip = SDL_FLIP_VERTICAL;
+ break;
+ case 131:
+ SrcR.w = PicoScreenWidth;
+ SrcR.h = PicoScreenHeight;
+ textureAngle = 0;
+ flip = (SDL_RendererFlip)(SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL);
+ break;
+ case 133:
+ SrcR.w = PicoScreenWidth;
+ SrcR.h = PicoScreenHeight;
+ textureAngle = 90;
+ flip = SDL_FLIP_NONE;
+ break;
+ case 134:
+ SrcR.w = PicoScreenWidth;
+ SrcR.h = PicoScreenHeight;
+ textureAngle = 180;
+ flip = SDL_FLIP_NONE;
+ break;
+ case 135:
+ SrcR.w = PicoScreenWidth;
+ SrcR.h = PicoScreenHeight;
+ textureAngle = 270;
+ flip = SDL_FLIP_NONE;
+ break;
+ default:
+ SrcR.w = PicoScreenWidth;
+ SrcR.h = PicoScreenHeight;
+ textureAngle = 0;
+ flip = SDL_FLIP_NONE;
+ break;
+ }
+
+
+ postFlipFunction();
+}
+
+bool Host::shouldFillAudioBuff(){
+ return false;
+}
+
+void* Host::getAudioBufferPointer(){
+ return nullptr;
+}
+
+size_t Host::getAudioBufferSize(){
+ return 0;
+}
+
+void Host::playFilledAudioBuffer(){
+}
+
+bool Host::shouldRunMainLoop(){
+ if (shouldQuit()){
+ return false;
+ }
+
+ return true;
+}
+
+const char* Host::logFilePrefix() {
+ return _logFilePrefix.c_str();
+}
+
+std::string Host::customBiosLua() {
+ return _customBiosLua;
+}
+
+std::string Host::getCartDirectory() {
+ return _cartDirectory;
+}
diff --git a/platform/Webassembly/source/sdl2basehost.h b/platform/Webassembly/source/sdl2basehost.h
new file mode 100644
index 00000000..0f08b91f
--- /dev/null
+++ b/platform/Webassembly/source/sdl2basehost.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "../../../source/host.h"
+
+class SDL2BaseHost : public Host {
+
+
+};
\ No newline at end of file
diff --git a/source/main.cpp b/source/main.cpp
index 6fffa7a4..2b6cd93c 100644
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -17,7 +17,6 @@
#endif
-
int main(int argc, char* argv[])
{
Host *host = new Host();
@@ -70,9 +69,7 @@ int main(int argc, char* argv[])
// Main loop
Logger_Write("Starting main loop\n");
-
vm->GameLoop();
-
Logger_Write("Turning off vm and exiting logger\n");
vm->CloseCart();
diff --git a/source/picoluaapi.cpp b/source/picoluaapi.cpp
index a7204812..a0c008fe 100644
--- a/source/picoluaapi.cpp
+++ b/source/picoluaapi.cpp
@@ -17,6 +17,10 @@ using namespace std;
#include
//}
+#ifdef __EMSCRIPTEN__
+#include
+#endif
+
Graphics* _graphicsForLuaApi;
Input* _inputForLuaApi;
Vm* _vmForLuaApi;
@@ -1187,11 +1191,21 @@ int dget(lua_State *L) {
}
int dset(lua_State *L) {
+
int dest = lua_tonumber(L,1);
fix32 val = lua_tonumber(L,2);
_vmForLuaApi->vm_dset(dest, val);
+ EM_ASM(FS.syncfs(false,function(err) {
+ if(err)
+ {
+ alert("FileSystem Save Error: " + err);
+ return false;
+ }
+ return true;
+ }));
+
return 0;
}
diff --git a/source/vm.cpp b/source/vm.cpp
index 36316293..d24c3277 100644
--- a/source/vm.cpp
+++ b/source/vm.cpp
@@ -32,6 +32,7 @@
#include
//}
+
using namespace z8;
static const char BiosCartName[] = "__FAKE08-BIOS.p8";
@@ -689,15 +690,19 @@ string Vm::GetBiosError() {
}
void Vm::GameLoop() {
+
while (_host->shouldRunMainLoop())
{
+
//shouldn't need to set this every frame
_host->setTargetFps(_targetFps);
//is this better at the end of the loop?
_host->waitForTargetFps();
+
if (_host->shouldQuit()) break; // break in order to return to hbmenu
+
//this should probably be handled just in the host class
_host->changeStretch();
@@ -716,7 +721,9 @@ void Vm::GameLoop() {
_host->playFilledAudioBuffer();
}
+
}
+
}
bool Vm::ExecuteLua(string luaString, string callbackFunction){
diff --git a/test/doctest.h b/test/doctest.h
index d25f5268..f6ec0664 100644
--- a/test/doctest.h
+++ b/test/doctest.h
@@ -265,8 +265,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#undef DOCTEST_CONFIG_WINDOWS_SEH
#endif // DOCTEST_CONFIG_NO_WINDOWS_SEH
-#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \
- !defined(__EMSCRIPTEN__)
+#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(__EMSCRIPTEN__)
#define DOCTEST_CONFIG_POSIX_SIGNALS
#endif // _WIN32
#if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS)