Maelstrom: Use automatic persistent storage for Emscripten in the latest SDL

From 4344da65ab83b0ffbee3eaf04be7389a83324e69 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 27 Mar 2026 23:43:42 -0700
Subject: [PATCH] Use automatic persistent storage for Emscripten in the latest
 SDL

---
 CMakeLists.txt           |  5 +++-
 game/Maelstrom_Globals.h |  3 --
 game/init.cpp            | 40 +++++++-------------------
 game/main.cpp            |  7 +++++
 utils/files.c            | 61 ----------------------------------------
 utils/files.h            |  1 -
 utils/prefs.cpp          |  3 --
 7 files changed, 21 insertions(+), 99 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2fa92b61..673e9088 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,6 +17,9 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$<CONFIGURATION>")
 # on Web targets, we need CMake to generate a HTML webpage.
 if(EMSCRIPTEN)
     set(CMAKE_EXECUTABLE_SUFFIX_CXX ".html" CACHE INTERNAL "")
+
+    # Enable persistent storage
+    set(SDL_EMSCRIPTEN_PERSISTENT_PATH /storage)
 endif()
 
 add_subdirectory(maclib)
@@ -203,7 +206,7 @@ target_link_libraries(${TARGET_NAME} PRIVATE SDL3::SDL3)
 
 if(EMSCRIPTEN)
     # Increase the default stack size
-    target_link_options(${TARGET_NAME} PRIVATE -sSTACK_SIZE=1048576 -lidbfs.js)
+    target_link_options(${TARGET_NAME} PRIVATE -sSTACK_SIZE=1048576)
 
     # 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 51ede40d..69b239e0 100644
--- a/game/Maelstrom_Globals.h
+++ b/game/Maelstrom_Globals.h
@@ -43,9 +43,6 @@
 #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 a590a35b..01e71c59 100644
--- a/game/init.cpp
+++ b/game/init.cpp
@@ -107,7 +107,6 @@ enum LoadingStage
 	LOAD_STAGE_BLITS25,
 	LOAD_STAGE_SHOTS,
 	LOAD_STAGE_SPRITES,
-	LOAD_STAGE_FILESYSTEM,
 	LOAD_STAGE_COMPLETE
 };
 static int gLoadingStage = LOAD_STAGE_WAITING;
@@ -826,12 +825,18 @@ bool StartInitialization(int window_width, int window_height, Uint32 window_flag
 	gNetworkAvailable = NET_Init();
 
 	// -- Initialize some variables
-	prefs = new Prefs(GAME_PREFS_FILE);
 	gLastHigh = -1;
 
-	if (!InitFilesystem(MAELSTROM_ORGANIZATION, MAELSTROM_NAME)) {
-		return false;
-	}
+	// -- Create our scores file
+	LoadScores();
+
+	// -- Load our preferences files
+	prefs = new Prefs(GAME_PREFS_FILE);
+	prefs->Load();
+
+	// -- Load our controls
+	LoadControls();
+	InitPlayerControls();
 
 	/* Load the Maelstrom icon */
 	if (!LoadIcon(&icon)) {
@@ -904,8 +909,6 @@ 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 */
@@ -968,29 +971,6 @@ 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 3c4a545c..40dae1b0 100644
--- a/game/main.cpp
+++ b/game/main.cpp
@@ -49,6 +49,9 @@
 #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";
 
@@ -158,6 +161,10 @@ 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 25ad7e44..1f057ff7 100644
--- a/utils/files.c
+++ b/utils/files.c
@@ -20,10 +20,6 @@
 
 #include "files.h"
 
-#ifdef SDL_PLATFORM_EMSCRIPTEN
-#include <emscripten/emscripten.h>
-#endif
-
 #ifndef PATH_MAX
 #define PATH_MAX    256
 #endif
@@ -39,29 +35,6 @@ 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;
@@ -96,25 +69,6 @@ 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];
@@ -231,20 +185,5 @@ 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 d391f986..8f9cbe92 100644
--- a/utils/files.h
+++ b/utils/files.h
@@ -28,7 +28,6 @@ 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 3d686c51..8f5de9ff 100644
--- a/utils/prefs.cpp
+++ b/utils/prefs.cpp
@@ -163,9 +163,6 @@ 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();
 	}
 }