diff --git a/3rdParty/libmpq/CMakeLists.txt b/3rdParty/libmpq/CMakeLists.txt
index 114f133e1..b28197b74 100644
--- a/3rdParty/libmpq/CMakeLists.txt
+++ b/3rdParty/libmpq/CMakeLists.txt
@@ -1,4 +1,6 @@
-find_package(ZLIB REQUIRED)
+if(NOT TARGET ZLIB::ZLIB)
+ find_package(ZLIB REQUIRED)
+endif()
if(NOT TARGET BZip2::BZip2)
find_package(BZip2 REQUIRED)
diff --git a/CMake/Dependencies.cmake b/CMake/Dependencies.cmake
index aa1e1c1b7..394ee6865 100644
--- a/CMake/Dependencies.cmake
+++ b/CMake/Dependencies.cmake
@@ -1,8 +1,13 @@
# Options that control whether to use system dependencies or build them from source,
# and whether to link them statically.
include(functions/dependency_options)
+include(functions/emscripten_system_library)
-if(USE_SDL1)
+if(EMSCRIPTEN)
+ # We use `USE_PTHREADS=1` here to get a version of SDL2 that supports threads.
+ emscripten_system_library("SDL2" SDL2::SDL2 USE_SDL=2 USE_PTHREADS=1)
+ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/3rdParty/SDL2/CMake")
+elseif(USE_SDL1)
find_package(SDL REQUIRED)
include_directories(${SDL_INCLUDE_DIR})
else()
@@ -52,21 +57,25 @@ macro(_find_SDL_image QUIET_OR_REQUIRED)
endif()
endmacro()
-if(NOT DEFINED DEVILUTIONX_SYSTEM_SDL_IMAGE)
- _find_SDL_image(QUIET)
- if(SDL_image_FOUND)
- message("-- Found SDL_image")
+if(EMSCRIPTEN)
+ emscripten_system_library("SDL_image" SDL2::SDL2_image USE_SDL_IMAGE=2 "SDL2_IMAGE_FORMATS='[\"png\"]'")
+else()
+ if(NOT DEFINED DEVILUTIONX_SYSTEM_SDL_IMAGE)
+ _find_SDL_image(QUIET)
+ if(SDL_image_FOUND)
+ message("-- Found SDL_image")
+ else()
+ message("-- Suitable system SDL_image package not found, will use SDL_image from source")
+ set(DEVILUTIONX_SYSTEM_SDL_IMAGE OFF)
+ endif()
+ endif()
+ dependency_options("SDL_image" DEVILUTIONX_SYSTEM_SDL_IMAGE ON DEVILUTIONX_STATIC_SDL_IMAGE)
+ if(DEVILUTIONX_SYSTEM_SDL_IMAGE)
+ _find_SDL_image(REQUIRED)
else()
- message("-- Suitable system SDL_image package not found, will use SDL_image from source")
- set(DEVILUTIONX_SYSTEM_SDL_IMAGE OFF)
+ add_subdirectory(3rdParty/SDL_image)
endif()
endif()
-dependency_options("SDL_image" DEVILUTIONX_SYSTEM_SDL_IMAGE ON DEVILUTIONX_STATIC_SDL_IMAGE)
-if(DEVILUTIONX_SYSTEM_SDL_IMAGE)
- _find_SDL_image(REQUIRED)
-else()
- add_subdirectory(3rdParty/SDL_image)
-endif()
if(NOT NOSOUND)
dependency_options("SDL_audiolib" DEVILUTIONX_SYSTEM_SDL_AUDIOLIB OFF DEVILUTIONX_STATIC_SDL_AUDIOLIB)
@@ -110,6 +119,10 @@ else()
add_subdirectory(3rdParty/bzip2)
endif()
+if(EMSCRIPTEN)
+ emscripten_system_library("zlib" ZLIB::ZLIB USE_ZLIB=1)
+endif()
+
add_subdirectory(3rdParty/libsmackerdec)
if(WIN32)
diff --git a/CMake/Platforms.cmake b/CMake/Platforms.cmake
index 1d696d0ff..9db6ba0e4 100644
--- a/CMake/Platforms.cmake
+++ b/CMake/Platforms.cmake
@@ -58,3 +58,7 @@ endif()
if(IOS)
include(platforms/ios)
endif()
+
+if(EMSCRIPTEN)
+ include(platforms/emscripten)
+endif()
diff --git a/CMake/functions/emscripten_system_library.cmake b/CMake/functions/emscripten_system_library.cmake
new file mode 100644
index 000000000..161000ee1
--- /dev/null
+++ b/CMake/functions/emscripten_system_library.cmake
@@ -0,0 +1,17 @@
+# This function defines a target that points to an Emscripten system library.
+#
+# Arguments:
+# LIB_NAME: a human-readable library name.
+# TARGET_NAME: the library target name
+# ...ARGN: Emscripten flags.
+#
+# Example:
+# emscripten_system_library("SDL2_image" SDL2::SDL2_image USE_SDL_IMAGE=2 "SDL2_IMAGE_FORMATS='[\"png\"]'")
+function(emscripten_system_library LIB_NAME TARGET_NAME)
+ add_library(${TARGET_NAME} INTERFACE IMPORTED GLOBAL)
+ foreach(arg ${ARGN})
+ target_compile_options(${TARGET_NAME} INTERFACE "SHELL:-s ${arg}")
+ target_link_options(${TARGET_NAME} INTERFACE "SHELL:-s ${arg}")
+ endforeach()
+ message("-- 📚 ${LIB_NAME}: Emscripten system library via ${ARGN}")
+endfunction()
diff --git a/CMake/platforms/emscripten.cmake b/CMake/platforms/emscripten.cmake
new file mode 100644
index 000000000..a5c312541
--- /dev/null
+++ b/CMake/platforms/emscripten.cmake
@@ -0,0 +1,10 @@
+set(BUILD_TESTING OFF)
+set(DISABLE_ZERO_TIER ON)
+set(DEVILUTIONX_SYSTEM_SDL_AUDIOLIB OFF)
+set(DEVILUTIONX_SYSTEM_LIBSODIUM OFF)
+set(DEVILUTIONX_SYSTEM_LIBFMT OFF)
+
+# Emscripten ports do have a bzip2 but it fails to link with this error:
+# warning: _BZ2_bzDecompress may need to be added to EXPORTED_FUNCTIONS if it arrives from a system library
+# error: undefined symbol: BZ2_bzDecompressEnd (referenced by top-level compiled C/C++ code)
+set(DEVILUTIONX_SYSTEM_BZIP2 OFF)
diff --git a/docs/building.md b/docs/building.md
index 5327c36ae..63402d919 100644
--- a/docs/building.md
+++ b/docs/building.md
@@ -353,6 +353,18 @@ You can do this by selecting the DevilutionX icon, then hold right mouse button
select Icons -> Information in the top menu.
+Emscripten
+
+Emscripten port is a work in progress. It builds but does not do more than that currently.
+
+To build, install the [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html), then run:
+
+~~~ bash
+emcmake cmake -S. -Bbuild-em -DCMAKE_BUILD_TYPE=Release
+cmake --build build-em -j $(getconf _NPROCESSORS_ONLN)
+~~~
+
+
CMake build options
### General