From 38a76abfed9d4b4c0194f50e84cd9eef83be0ca6 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 19 Mar 2026 22:45:15 -0700
Subject: [PATCH] Added Emscripten persistent filesystem support
---
CMakeLists.txt | 2 +-
game/Maelstrom_Globals.h | 3 ++
game/init.cpp | 44 +++++++++++++++++++++--------
game/main.cpp | 7 -----
utils/files.c | 61 ++++++++++++++++++++++++++++++++++++++++
utils/files.h | 1 +
utils/prefs.cpp | 3 ++
7 files changed, 101 insertions(+), 20 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 301e5ee5..995bdfac 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -197,7 +197,7 @@ target_link_libraries(Maelstrom PRIVATE SDL3_net::SDL3_net)
if(EMSCRIPTEN)
# Increase the default stack size
- target_link_options(Maelstrom PRIVATE -sSTACK_SIZE=1048576)
+ target_link_options(Maelstrom PRIVATE -sSTACK_SIZE=1048576 -lidbfs.js)
# on the web, we have to put the files inside of the webassembly
# somewhat unintuitively, this is done via a linker argument.
diff --git a/game/Maelstrom_Globals.h b/game/Maelstrom_Globals.h
index 69b239e0..51ede40d 100644
--- a/game/Maelstrom_Globals.h
+++ b/game/Maelstrom_Globals.h
@@ -43,6 +43,9 @@
#include "gameinfo.h"
#include "steam.h"
+#define MAELSTROM_ORGANIZATION "Ambrosia Software"
+#define MAELSTROM_NAME "Maelstrom"
+
// Preferences keys
#define PREFERENCES_RESOLUTION "Resolution"
#define PREFERENCES_HANDLE "Handle"
diff --git a/game/init.cpp b/game/init.cpp
index 54cd2577..23c7cca6 100644
--- a/game/init.cpp
+++ b/game/init.cpp
@@ -107,6 +107,7 @@ enum LoadingStage
LOAD_STAGE_BLITS25,
LOAD_STAGE_SHOTS,
LOAD_STAGE_SPRITES,
+ LOAD_STAGE_FILESYSTEM,
LOAD_STAGE_COMPLETE
};
static int gLoadingStage = LOAD_STAGE_WAITING;
@@ -794,18 +795,12 @@ bool StartInitialization(int window_width, int window_height, Uint32 window_flag
gNetworkAvailable = NET_Init();
// -- Initialize some variables
- gLastHigh = -1;
-
- // -- Create our scores file
- LoadScores();
-
- // -- Load our preferences files
prefs = new Prefs(GAME_PREFS_FILE);
- prefs->Load();
+ gLastHigh = -1;
- // -- Load our controls
- LoadControls();
- InitPlayerControls();
+ if (!InitFilesystem(MAELSTROM_ORGANIZATION, MAELSTROM_NAME)) {
+ return false;
+ }
/* Load the Maelstrom icon */
#if !defined(SDL_PLATFORM_APPLE) || defined(ENABLE_STEAM)
@@ -833,14 +828,14 @@ bool StartInitialization(int window_width, int window_height, Uint32 window_flag
/* Load the Font Server and fonts */
fontserv = new FontServ(screen, "Maelstrom Fonts");
- if ( fontserv->Error() ) {
+ if (fontserv->Error()) {
error("Fatal: %s\n", fontserv->Error());
return false;
}
/* Load the Sound Server and initialize sound */
sound = new Sound("Maelstrom Sounds", gSoundLevel);
- if ( sound->Error() ) {
+ if (sound->Error()) {
error("Fatal: %s\n", sound->Error());
return false;
}
@@ -882,6 +877,8 @@ bool StartInitialization(int window_width, int window_height, Uint32 window_flag
bool ContinueInitialization()
{
+ bool failed;
+
switch (gLoadingStage) {
case LOAD_STAGE_STARTING:
/* -- Load in the prize CICN's */
@@ -944,6 +941,29 @@ bool ContinueInitialization()
return false;
}
+ gLoadingStage = LOAD_STAGE_FILESYSTEM;
+
+ // Fallthrough...
+ //break;
+
+ case LOAD_STAGE_FILESYSTEM:
+ if (!FilesystemReady(&failed)) {
+ if (failed) {
+ return false;
+ }
+ break;
+ }
+
+ // -- Create our scores file
+ LoadScores();
+
+ // -- Load our preferences files
+ prefs->Load();
+
+ // -- Load our controls
+ LoadControls();
+ InitPlayerControls();
+
gLoadingStage = LOAD_STAGE_COMPLETE;
// Fallthrough...
diff --git a/game/main.cpp b/game/main.cpp
index 380d33d4..e990095b 100644
--- a/game/main.cpp
+++ b/game/main.cpp
@@ -49,9 +49,6 @@
#include "../screenlib/UIElementCheckbox.h"
#include "../screenlib/UIElementEditbox.h"
-#define MAELSTROM_ORGANIZATION "Ambrosia Software"
-#define MAELSTROM_NAME "Maelstrom"
-
static const char *Version =
"Maelstrom v1.4.3 (GPL version 4.0.0) -- 10/08/2011 by Sam Lantinga\n";
@@ -161,10 +158,6 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
/* Initializing Steam can set up environment variables, so do this first */
InitSteam();
- if ( !InitFilesystem(MAELSTROM_ORGANIZATION, MAELSTROM_NAME) ) {
- return SDL_APP_FAILURE;
- }
-
/* Seed the random number generator */
SeedRandom(0L);
diff --git a/utils/files.c b/utils/files.c
index 1f057ff7..25ad7e44 100644
--- a/utils/files.c
+++ b/utils/files.c
@@ -20,6 +20,10 @@
#include "files.h"
+#ifdef SDL_PLATFORM_EMSCRIPTEN
+#include <emscripten/emscripten.h>
+#endif
+
#ifndef PATH_MAX
#define PATH_MAX 256
#endif
@@ -35,6 +39,29 @@ bool InitFilesystem(const char *org, const char *app)
storage_org = org;
storage_app = app;
+#ifdef SDL_PLATFORM_EMSCRIPTEN
+ char *prefpath = SDL_GetPrefPath(org, app);
+ if (prefpath) {
+ MAIN_THREAD_EM_ASM({
+ const prefpath = UTF8ToString($0);
+ FS.mkdirTree(prefpath);
+ FS.mount(IDBFS, {}, prefpath);
+ filesystem_ready = 0;
+ FS.syncfs(true, function(err) {
+ if (err) {
+ console.log("Couldn't mount " + prefpath + ": " + err);
+ filesystem_ready = -1;
+ } else {
+ //console.log("Filesystem ready");
+ filesystem_ready = 1;
+ }
+ });
+ }, prefpath);
+
+ SDL_free(prefpath);
+ }
+#endif // SDL_PLATFORM_EMSCRIPTEN
+
if (env) {
SDL_strlcpy(datapath, env, sizeof(datapath));
return true;
@@ -69,6 +96,25 @@ bool InitFilesystem(const char *org, const char *app)
#endif // MAELSTROM_DATA
}
+bool FilesystemReady(bool *failed)
+{
+ *failed = false;
+
+#ifdef SDL_PLATFORM_EMSCRIPTEN
+ int result = MAIN_THREAD_EM_ASM_INT({ return filesystem_ready; });
+ switch (result) {
+ case -1:
+ *failed = true;
+ return false;
+ case 0:
+ return false;
+ default:
+ return true;
+ }
+#endif
+ return true;
+}
+
SDL_IOStream *OpenRead(const char *file)
{
char path[PATH_MAX];
@@ -185,5 +231,20 @@ bool SaveUserFile(const char *file, SDL_IOStream *src)
result = false;
}
SDL_CloseIO(src);
+
+#ifdef SDL_PLATFORM_EMSCRIPTEN
+ if (result) {
+ MAIN_THREAD_EM_ASM({
+ FS.syncfs(false, function(err) {
+ if (err) {
+ console.log("Couldn't save file: " + err);
+ } else {
+ //console.log("File saved!");
+ }
+ });
+ });
+ }
+#endif // SDL_PLATFORM_EMSCRIPTEN
+
return result;
}
diff --git a/utils/files.h b/utils/files.h
index 8f9cbe92..d391f986 100644
--- a/utils/files.h
+++ b/utils/files.h
@@ -28,6 +28,7 @@ extern "C" {
#endif
bool InitFilesystem(const char *org, const char *app);
+bool FilesystemReady(bool *failed);
SDL_IOStream *OpenRead(const char *file);
diff --git a/utils/prefs.cpp b/utils/prefs.cpp
index 8f5de9ff..3d686c51 100644
--- a/utils/prefs.cpp
+++ b/utils/prefs.cpp
@@ -163,6 +163,9 @@ Prefs::SetString(const char *key, const char *value, bool dirty)
if (dirty) {
m_dirty = true;
+
+ // Save immediately, in case we crash or are unloaded
+ Save();
}
}