diff --git a/.github/workflows/make.yml b/.github/workflows/make.yml
index b61291a4ae7..f399ae28829 100644
--- a/.github/workflows/make.yml
+++ b/.github/workflows/make.yml
@@ -342,3 +342,44 @@ jobs:
artifactErrorsFailBuild: true
prerelease: true
replacesArtifacts: true
+ make-emscripten:
+ name: Make (Emscripten)
+ runs-on: ubuntu-latest
+ container: emscripten/emsdk:3.1.74
+ timeout-minutes: 30
+ defaults:
+ run:
+ shell: bash
+ steps:
+ - uses: actions/checkout@v4
+ - name: Install dependencies
+ run: |
+ sudo apt-get -y update
+ sudo apt-get -y install gettext p7zip-full
+ - name: Build
+ run: |
+ emmake make -f Makefile.emscripten -j "$(nproc)"
+ env:
+ FHEROES2_STRICT_COMPILATION: ON
+ - name: Create package
+ run: |
+ # Translations and H2D files are already included in fheroes2.data
+ 7z a -bb1 -tzip -- fheroes2_emscripten.zip LICENSE changelog.txt fheroes2.data fheroes2.js fheroes2.wasm ./docs/README.txt ./files/emscripten/*
+ - uses: actions/upload-artifact@v4
+ if: ${{ github.event_name == 'pull_request' }}
+ with:
+ name: fheroes2_emscripten.zip
+ path: fheroes2_emscripten.zip
+ if-no-files-found: error
+ - uses: ncipollo/release-action@v1
+ if: ${{ github.event_name == 'push' }}
+ with:
+ artifacts: fheroes2_emscripten.zip
+ body: ${{ github.event.head_commit.message }}
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Emscripten build (latest commit)
+ tag: fheroes2-emscripten-sdl2_dev
+ allowUpdates: true
+ artifactErrorsFailBuild: true
+ prerelease: true
+ replacesArtifacts: true
diff --git a/.gitignore b/.gitignore
index af8057fc82b..3793277c993 100644
--- a/.gitignore
+++ b/.gitignore
@@ -92,7 +92,14 @@ android/.idea
android/.gradle
# Automatically created Android assets
-android/app/src/main/assets/files/
+android/app/src/main/assets/
# Mac magic folders
.DS_Store
+
+# Web build artifacts
+*.wasm
+*.wasm.map
+fheroes2.js
+fheroes2.data
+src/dist/web/dist/*
diff --git a/Makefile.emscripten b/Makefile.emscripten
new file mode 100644
index 00000000000..07feece5450
--- /dev/null
+++ b/Makefile.emscripten
@@ -0,0 +1,43 @@
+###########################################################################
+# fheroes2: https://github.com/ihhub/fheroes2 #
+# Copyright (C) 2025 #
+# #
+# This program is free software; you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation; either version 2 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program; if not, write to the #
+# Free Software Foundation, Inc., #
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
+###########################################################################
+
+# Options:
+#
+# FHEROES2_STRICT_COMPILATION: build in strict compilation mode (turns warnings into errors)
+# FHEROES2_WITH_DEBUG: build in debug mode
+# FHEROES2_DATA: set the built-in path to the fheroes2 data directory (e.g. /usr/share/fheroes2)
+
+.PHONY: all clean translations
+
+all: fheroes2.js
+
+fheroes2.js: translations
+ $(MAKE) -C src/dist PLATFORM=emscripten
+ cp src/dist/fheroes2-ems/fheroes2.data .
+ cp src/dist/fheroes2-ems/fheroes2.js .
+ cp src/dist/fheroes2-ems/fheroes2.wasm .
+
+translations:
+ $(MAKE) -C files/lang
+
+clean:
+ $(MAKE) -C src/dist clean
+ $(MAKE) -C files/lang clean
+ rm -f fheroes2.data fheroes2.js fheroes2.wasm
diff --git a/Makefile.vita b/Makefile.vita
index 18a547c6b23..956ec8d6694 100644
--- a/Makefile.vita
+++ b/Makefile.vita
@@ -1,6 +1,6 @@
###########################################################################
# fheroes2: https://github.com/ihhub/fheroes2 #
-# Copyright (C) 2021 - 2024 #
+# Copyright (C) 2021 - 2025 #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
@@ -40,7 +40,7 @@ fheroes2.vpk: eboot.bin param.sfo translations
--add files/images/platform/psv/sce_sys/livearea/contents/template.xml=sce_sys/livearea/contents/template.xml \
--add files/data=files/data \
--add files/lang/vita_temp=files/lang \
- fheroes2.vpk
+ fheroes2.vpk
rm -r files/lang/vita_temp
translations: fheroes2.elf
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 54991ba2fac..8167f0b5abc 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -99,6 +99,14 @@ tasks.register('copyH2D', Copy) {
into 'src/main/assets/files/data'
}
+tasks.register('copyTimidity', Copy) {
+ from('../../files/timidity') {
+ include 'instruments/*.pat'
+ include 'timidity.cfg'
+ }
+ into 'src/main/assets'
+}
+
tasks.register('copyTranslations', Copy) {
from('../../files/lang') {
include '*.mo'
@@ -107,5 +115,5 @@ tasks.register('copyTranslations', Copy) {
}
preBuild {
- dependsOn copyH2D, copyTranslations
+ dependsOn copyH2D, copyTimidity, copyTranslations
}
diff --git a/files/emscripten/fheroes2.jpeg b/files/emscripten/fheroes2.jpeg
new file mode 100644
index 00000000000..d0d3f9f04e4
Binary files /dev/null and b/files/emscripten/fheroes2.jpeg differ
diff --git a/files/emscripten/index.html b/files/emscripten/index.html
new file mode 100644
index 00000000000..99352febb69
--- /dev/null
+++ b/files/emscripten/index.html
@@ -0,0 +1,210 @@
+
+
+ fheroes2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/assets/instruments/README.txt b/files/timidity/instruments/README.txt
similarity index 100%
rename from android/app/src/main/assets/instruments/README.txt
rename to files/timidity/instruments/README.txt
diff --git a/android/app/src/main/assets/instruments/acpiano.pat b/files/timidity/instruments/acpiano.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/acpiano.pat
rename to files/timidity/instruments/acpiano.pat
diff --git a/android/app/src/main/assets/instruments/agogohi.pat b/files/timidity/instruments/agogohi.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/agogohi.pat
rename to files/timidity/instruments/agogohi.pat
diff --git a/android/app/src/main/assets/instruments/agogolo.pat b/files/timidity/instruments/agogolo.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/agogolo.pat
rename to files/timidity/instruments/agogolo.pat
diff --git a/android/app/src/main/assets/instruments/bassoon.pat b/files/timidity/instruments/bassoon.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/bassoon.pat
rename to files/timidity/instruments/bassoon.pat
diff --git a/android/app/src/main/assets/instruments/belltree.pat b/files/timidity/instruments/belltree.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/belltree.pat
rename to files/timidity/instruments/belltree.pat
diff --git a/android/app/src/main/assets/instruments/bongohi.pat b/files/timidity/instruments/bongohi.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/bongohi.pat
rename to files/timidity/instruments/bongohi.pat
diff --git a/android/app/src/main/assets/instruments/bongolo.pat b/files/timidity/instruments/bongolo.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/bongolo.pat
rename to files/timidity/instruments/bongolo.pat
diff --git a/android/app/src/main/assets/instruments/bowglass.pat b/files/timidity/instruments/bowglass.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/bowglass.pat
rename to files/timidity/instruments/bowglass.pat
diff --git a/android/app/src/main/assets/instruments/cabasa.pat b/files/timidity/instruments/cabasa.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cabasa.pat
rename to files/timidity/instruments/cabasa.pat
diff --git a/android/app/src/main/assets/instruments/castinet.pat b/files/timidity/instruments/castinet.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/castinet.pat
rename to files/timidity/instruments/castinet.pat
diff --git a/android/app/src/main/assets/instruments/cello.pat b/files/timidity/instruments/cello.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cello.pat
rename to files/timidity/instruments/cello.pat
diff --git a/android/app/src/main/assets/instruments/choir.pat b/files/timidity/instruments/choir.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/choir.pat
rename to files/timidity/instruments/choir.pat
diff --git a/android/app/src/main/assets/instruments/church.pat b/files/timidity/instruments/church.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/church.pat
rename to files/timidity/instruments/church.pat
diff --git a/android/app/src/main/assets/instruments/claps.pat b/files/timidity/instruments/claps.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/claps.pat
rename to files/timidity/instruments/claps.pat
diff --git a/android/app/src/main/assets/instruments/clarinet.pat b/files/timidity/instruments/clarinet.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/clarinet.pat
rename to files/timidity/instruments/clarinet.pat
diff --git a/android/app/src/main/assets/instruments/clave.pat b/files/timidity/instruments/clave.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/clave.pat
rename to files/timidity/instruments/clave.pat
diff --git a/android/app/src/main/assets/instruments/congahi1.pat b/files/timidity/instruments/congahi1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/congahi1.pat
rename to files/timidity/instruments/congahi1.pat
diff --git a/android/app/src/main/assets/instruments/congahi2.pat b/files/timidity/instruments/congahi2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/congahi2.pat
rename to files/timidity/instruments/congahi2.pat
diff --git a/android/app/src/main/assets/instruments/congalo.pat b/files/timidity/instruments/congalo.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/congalo.pat
rename to files/timidity/instruments/congalo.pat
diff --git a/android/app/src/main/assets/instruments/contraba.pat b/files/timidity/instruments/contraba.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/contraba.pat
rename to files/timidity/instruments/contraba.pat
diff --git a/android/app/src/main/assets/instruments/cowbell.pat b/files/timidity/instruments/cowbell.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cowbell.pat
rename to files/timidity/instruments/cowbell.pat
diff --git a/android/app/src/main/assets/instruments/cuica1.pat b/files/timidity/instruments/cuica1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cuica1.pat
rename to files/timidity/instruments/cuica1.pat
diff --git a/android/app/src/main/assets/instruments/cuica2.pat b/files/timidity/instruments/cuica2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cuica2.pat
rename to files/timidity/instruments/cuica2.pat
diff --git a/android/app/src/main/assets/instruments/cymbell.pat b/files/timidity/instruments/cymbell.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cymbell.pat
rename to files/timidity/instruments/cymbell.pat
diff --git a/android/app/src/main/assets/instruments/cymchina.pat b/files/timidity/instruments/cymchina.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cymchina.pat
rename to files/timidity/instruments/cymchina.pat
diff --git a/android/app/src/main/assets/instruments/cymcrsh1.pat b/files/timidity/instruments/cymcrsh1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cymcrsh1.pat
rename to files/timidity/instruments/cymcrsh1.pat
diff --git a/android/app/src/main/assets/instruments/cymcrsh2.pat b/files/timidity/instruments/cymcrsh2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cymcrsh2.pat
rename to files/timidity/instruments/cymcrsh2.pat
diff --git a/android/app/src/main/assets/instruments/cymride1.pat b/files/timidity/instruments/cymride1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cymride1.pat
rename to files/timidity/instruments/cymride1.pat
diff --git a/android/app/src/main/assets/instruments/cymride2.pat b/files/timidity/instruments/cymride2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cymride2.pat
rename to files/timidity/instruments/cymride2.pat
diff --git a/android/app/src/main/assets/instruments/cymsplsh.pat b/files/timidity/instruments/cymsplsh.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/cymsplsh.pat
rename to files/timidity/instruments/cymsplsh.pat
diff --git a/android/app/src/main/assets/instruments/englhorn.pat b/files/timidity/instruments/englhorn.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/englhorn.pat
rename to files/timidity/instruments/englhorn.pat
diff --git a/android/app/src/main/assets/instruments/flute.pat b/files/timidity/instruments/flute.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/flute.pat
rename to files/timidity/instruments/flute.pat
diff --git a/android/app/src/main/assets/instruments/frenchrn.pat b/files/timidity/instruments/frenchrn.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/frenchrn.pat
rename to files/timidity/instruments/frenchrn.pat
diff --git a/android/app/src/main/assets/instruments/guiro1.pat b/files/timidity/instruments/guiro1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/guiro1.pat
rename to files/timidity/instruments/guiro1.pat
diff --git a/android/app/src/main/assets/instruments/guiro2.pat b/files/timidity/instruments/guiro2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/guiro2.pat
rename to files/timidity/instruments/guiro2.pat
diff --git a/android/app/src/main/assets/instruments/harp.pat b/files/timidity/instruments/harp.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/harp.pat
rename to files/timidity/instruments/harp.pat
diff --git a/android/app/src/main/assets/instruments/highq.pat b/files/timidity/instruments/highq.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/highq.pat
rename to files/timidity/instruments/highq.pat
diff --git a/android/app/src/main/assets/instruments/hihatcl.pat b/files/timidity/instruments/hihatcl.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/hihatcl.pat
rename to files/timidity/instruments/hihatcl.pat
diff --git a/android/app/src/main/assets/instruments/hihatop.pat b/files/timidity/instruments/hihatop.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/hihatop.pat
rename to files/timidity/instruments/hihatop.pat
diff --git a/android/app/src/main/assets/instruments/hihatpd.pat b/files/timidity/instruments/hihatpd.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/hihatpd.pat
rename to files/timidity/instruments/hihatpd.pat
diff --git a/android/app/src/main/assets/instruments/hrpschrd.pat b/files/timidity/instruments/hrpschrd.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/hrpschrd.pat
rename to files/timidity/instruments/hrpschrd.pat
diff --git a/android/app/src/main/assets/instruments/jingles.pat b/files/timidity/instruments/jingles.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/jingles.pat
rename to files/timidity/instruments/jingles.pat
diff --git a/android/app/src/main/assets/instruments/kick1.pat b/files/timidity/instruments/kick1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/kick1.pat
rename to files/timidity/instruments/kick1.pat
diff --git a/android/app/src/main/assets/instruments/kick2.pat b/files/timidity/instruments/kick2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/kick2.pat
rename to files/timidity/instruments/kick2.pat
diff --git a/android/app/src/main/assets/instruments/maracas.pat b/files/timidity/instruments/maracas.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/maracas.pat
rename to files/timidity/instruments/maracas.pat
diff --git a/android/app/src/main/assets/instruments/marcato.pat b/files/timidity/instruments/marcato.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/marcato.pat
rename to files/timidity/instruments/marcato.pat
diff --git a/android/app/src/main/assets/instruments/metbell.pat b/files/timidity/instruments/metbell.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/metbell.pat
rename to files/timidity/instruments/metbell.pat
diff --git a/android/app/src/main/assets/instruments/metclick.pat b/files/timidity/instruments/metclick.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/metclick.pat
rename to files/timidity/instruments/metclick.pat
diff --git a/android/app/src/main/assets/instruments/nyguitar.pat b/files/timidity/instruments/nyguitar.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/nyguitar.pat
rename to files/timidity/instruments/nyguitar.pat
diff --git a/android/app/src/main/assets/instruments/oboe.pat b/files/timidity/instruments/oboe.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/oboe.pat
rename to files/timidity/instruments/oboe.pat
diff --git a/android/app/src/main/assets/instruments/piccolo.pat b/files/timidity/instruments/piccolo.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/piccolo.pat
rename to files/timidity/instruments/piccolo.pat
diff --git a/android/app/src/main/assets/instruments/pizzcato.pat b/files/timidity/instruments/pizzcato.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/pizzcato.pat
rename to files/timidity/instruments/pizzcato.pat
diff --git a/android/app/src/main/assets/instruments/scratch1.pat b/files/timidity/instruments/scratch1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/scratch1.pat
rename to files/timidity/instruments/scratch1.pat
diff --git a/android/app/src/main/assets/instruments/scratch2.pat b/files/timidity/instruments/scratch2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/scratch2.pat
rename to files/timidity/instruments/scratch2.pat
diff --git a/android/app/src/main/assets/instruments/shaker.pat b/files/timidity/instruments/shaker.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/shaker.pat
rename to files/timidity/instruments/shaker.pat
diff --git a/android/app/src/main/assets/instruments/slap.pat b/files/timidity/instruments/slap.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/slap.pat
rename to files/timidity/instruments/slap.pat
diff --git a/android/app/src/main/assets/instruments/slowstr.pat b/files/timidity/instruments/slowstr.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/slowstr.pat
rename to files/timidity/instruments/slowstr.pat
diff --git a/android/app/src/main/assets/instruments/snap.pat b/files/timidity/instruments/snap.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/snap.pat
rename to files/timidity/instruments/snap.pat
diff --git a/android/app/src/main/assets/instruments/snare1.pat b/files/timidity/instruments/snare1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/snare1.pat
rename to files/timidity/instruments/snare1.pat
diff --git a/android/app/src/main/assets/instruments/snare2.pat b/files/timidity/instruments/snare2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/snare2.pat
rename to files/timidity/instruments/snare2.pat
diff --git a/android/app/src/main/assets/instruments/snarerol.pat b/files/timidity/instruments/snarerol.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/snarerol.pat
rename to files/timidity/instruments/snarerol.pat
diff --git a/android/app/src/main/assets/instruments/sqrclick.pat b/files/timidity/instruments/sqrclick.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/sqrclick.pat
rename to files/timidity/instruments/sqrclick.pat
diff --git a/android/app/src/main/assets/instruments/stickrim.pat b/files/timidity/instruments/stickrim.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/stickrim.pat
rename to files/timidity/instruments/stickrim.pat
diff --git a/android/app/src/main/assets/instruments/sticks.pat b/files/timidity/instruments/sticks.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/sticks.pat
rename to files/timidity/instruments/sticks.pat
diff --git a/android/app/src/main/assets/instruments/surdo1.pat b/files/timidity/instruments/surdo1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/surdo1.pat
rename to files/timidity/instruments/surdo1.pat
diff --git a/android/app/src/main/assets/instruments/surdo2.pat b/files/timidity/instruments/surdo2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/surdo2.pat
rename to files/timidity/instruments/surdo2.pat
diff --git a/android/app/src/main/assets/instruments/synstr2.pat b/files/timidity/instruments/synstr2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/synstr2.pat
rename to files/timidity/instruments/synstr2.pat
diff --git a/android/app/src/main/assets/instruments/taiko.pat b/files/timidity/instruments/taiko.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/taiko.pat
rename to files/timidity/instruments/taiko.pat
diff --git a/android/app/src/main/assets/instruments/tamborin.pat b/files/timidity/instruments/tamborin.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/tamborin.pat
rename to files/timidity/instruments/tamborin.pat
diff --git a/android/app/src/main/assets/instruments/timbaleh.pat b/files/timidity/instruments/timbaleh.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/timbaleh.pat
rename to files/timidity/instruments/timbaleh.pat
diff --git a/android/app/src/main/assets/instruments/timbalel.pat b/files/timidity/instruments/timbalel.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/timbalel.pat
rename to files/timidity/instruments/timbalel.pat
diff --git a/android/app/src/main/assets/instruments/timpani.pat b/files/timidity/instruments/timpani.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/timpani.pat
rename to files/timidity/instruments/timpani.pat
diff --git a/android/app/src/main/assets/instruments/tomhi1.pat b/files/timidity/instruments/tomhi1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/tomhi1.pat
rename to files/timidity/instruments/tomhi1.pat
diff --git a/android/app/src/main/assets/instruments/tomhi2.pat b/files/timidity/instruments/tomhi2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/tomhi2.pat
rename to files/timidity/instruments/tomhi2.pat
diff --git a/android/app/src/main/assets/instruments/tomlo1.pat b/files/timidity/instruments/tomlo1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/tomlo1.pat
rename to files/timidity/instruments/tomlo1.pat
diff --git a/android/app/src/main/assets/instruments/tomlo2.pat b/files/timidity/instruments/tomlo2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/tomlo2.pat
rename to files/timidity/instruments/tomlo2.pat
diff --git a/android/app/src/main/assets/instruments/tommid1.pat b/files/timidity/instruments/tommid1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/tommid1.pat
rename to files/timidity/instruments/tommid1.pat
diff --git a/android/app/src/main/assets/instruments/tommid2.pat b/files/timidity/instruments/tommid2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/tommid2.pat
rename to files/timidity/instruments/tommid2.pat
diff --git a/android/app/src/main/assets/instruments/tremstr.pat b/files/timidity/instruments/tremstr.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/tremstr.pat
rename to files/timidity/instruments/tremstr.pat
diff --git a/android/app/src/main/assets/instruments/triangl1.pat b/files/timidity/instruments/triangl1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/triangl1.pat
rename to files/timidity/instruments/triangl1.pat
diff --git a/android/app/src/main/assets/instruments/triangl2.pat b/files/timidity/instruments/triangl2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/triangl2.pat
rename to files/timidity/instruments/triangl2.pat
diff --git a/android/app/src/main/assets/instruments/trombone.pat b/files/timidity/instruments/trombone.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/trombone.pat
rename to files/timidity/instruments/trombone.pat
diff --git a/android/app/src/main/assets/instruments/vibslap.pat b/files/timidity/instruments/vibslap.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/vibslap.pat
rename to files/timidity/instruments/vibslap.pat
diff --git a/android/app/src/main/assets/instruments/viola.pat b/files/timidity/instruments/viola.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/viola.pat
rename to files/timidity/instruments/viola.pat
diff --git a/android/app/src/main/assets/instruments/whistle1.pat b/files/timidity/instruments/whistle1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/whistle1.pat
rename to files/timidity/instruments/whistle1.pat
diff --git a/android/app/src/main/assets/instruments/whistle2.pat b/files/timidity/instruments/whistle2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/whistle2.pat
rename to files/timidity/instruments/whistle2.pat
diff --git a/android/app/src/main/assets/instruments/woodblk1.pat b/files/timidity/instruments/woodblk1.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/woodblk1.pat
rename to files/timidity/instruments/woodblk1.pat
diff --git a/android/app/src/main/assets/instruments/woodblk2.pat b/files/timidity/instruments/woodblk2.pat
similarity index 100%
rename from android/app/src/main/assets/instruments/woodblk2.pat
rename to files/timidity/instruments/woodblk2.pat
diff --git a/android/app/src/main/assets/timidity.cfg b/files/timidity/timidity.cfg
similarity index 100%
rename from android/app/src/main/assets/timidity.cfg
rename to files/timidity/timidity.cfg
diff --git a/src/dist/Makefile b/src/dist/Makefile
index 4e1c76db630..103f1a860cc 100644
--- a/src/dist/Makefile
+++ b/src/dist/Makefile
@@ -1,6 +1,6 @@
###########################################################################
# fheroes2: https://github.com/ihhub/fheroes2 #
-# Copyright (C) 2021 - 2024 #
+# Copyright (C) 2021 - 2025 #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
@@ -159,7 +159,11 @@ ifndef FHEROES2_WITH_SYSTEM_SMACKER
$(MAKE) -C thirdparty/libsmacker CCFLAGS="$(CCFLAGS_TP)" CFLAGS="$(CFLAGS_TP)" CXXFLAGS="$(CXXFLAGS_TP)" CPPFLAGS="$(CPPFLAGS_TP)"
endif
$(MAKE) -C engine
+ifeq ($(PLATFORM),emscripten)
+ $(MAKE) -C fheroes2-ems
+else
$(MAKE) -C fheroes2
+endif
ifdef FHEROES2_WITH_TOOLS
$(MAKE) -C tools
endif
@@ -169,5 +173,9 @@ ifndef FHEROES2_WITH_SYSTEM_SMACKER
$(MAKE) -C thirdparty/libsmacker clean
endif
$(MAKE) -C engine clean
+ifeq ($(PLATFORM),emscripten)
+ $(MAKE) -C fheroes2-ems clean
+else
$(MAKE) -C fheroes2 clean
+endif
$(MAKE) -C tools clean
diff --git a/src/dist/Makefile.emscripten b/src/dist/Makefile.emscripten
new file mode 100644
index 00000000000..98e74871f08
--- /dev/null
+++ b/src/dist/Makefile.emscripten
@@ -0,0 +1,43 @@
+###########################################################################
+# fheroes2: https://github.com/ihhub/fheroes2 #
+# Copyright (C) 2025 #
+# #
+# This program is free software; you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation; either version 2 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program; if not, write to the #
+# Free Software Foundation, Inc., #
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
+###########################################################################
+
+CCFLAGS := $(filter-out -pthread,$(CCFLAGS)) \
+ --use-port=sdl2 \
+ --use-port=sdl2_mixer \
+ --use-port=zlib \
+ -Wno-variadic-macro-arguments-omitted
+
+LDFLAGS := $(filter-out -pthread,$(LDFLAGS)) \
+ --preload-file ../../../files/data/resurrection.h2d@/files/data/resurrection.h2d \
+ --preload-file ../../../files/lang/@/files/lang/ \
+ --preload-file ../../../files/timidity/@/etc/ \
+ -sASYNCIFY \
+ -sASYNCIFY_STACK_SIZE=20480 \
+ -sENVIRONMENT=web \
+ -sINITIAL_MEMORY=128mb \
+ -sNO_DISABLE_EXCEPTION_CATCHING \
+ -sSDL2_MIXER_FORMATS=mid,mp3,ogg \
+ -sSTACK_SIZE=262144 \
+ -lidbfs.js
+
+ifdef FHEROES2_WITH_DEBUG
+CCFLAGS := $(CCFLAGS) -gsource-map
+LDFLAGS := $(LDFLAGS) -gsource-map
+endif
diff --git a/src/dist/fheroes2-ems/Makefile b/src/dist/fheroes2-ems/Makefile
new file mode 100644
index 00000000000..200a2fe3c13
--- /dev/null
+++ b/src/dist/fheroes2-ems/Makefile
@@ -0,0 +1,48 @@
+###########################################################################
+# fheroes2: https://github.com/ihhub/fheroes2 #
+# Copyright (C) 2025 #
+# #
+# This program is free software; you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation; either version 2 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program; if not, write to the #
+# Free Software Foundation, Inc., #
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
+###########################################################################
+
+DEPLIBS := ../engine/libengine.a
+CCFLAGS := $(CCFLAGS) -I../../engine
+
+ifndef FHEROES2_WITH_SYSTEM_SMACKER
+DEPLIBS := $(DEPLIBS) ../thirdparty/libsmacker/libsmacker.a
+CCFLAGS := $(CCFLAGS) -I../../thirdparty/libsmacker
+endif
+
+SOURCEROOT := ../../fheroes2
+SOURCEDIRS := $(filter %/,$(wildcard $(SOURCEROOT)/*/))
+SOURCES := $(wildcard $(SOURCEROOT)/*/*.cpp)
+
+VPATH := $(SOURCEDIRS)
+
+.PHONY: all clean
+
+all: fheroes2.js
+
+fheroes2.js: $(notdir $(patsubst %.cpp, %.o, $(SOURCES))) $(DEPLIBS)
+ $(CXX) -o $@ $^ $(LIBS) $(LDFLAGS)
+
+%.o: %.cpp
+ $(CXX) -c -MD $< $(addprefix -I, $(SOURCEDIRS)) $(CCFLAGS) $(CXXFLAGS) $(CPPFLAGS)
+
+include $(wildcard *.d)
+
+clean:
+ rm -f *.d *.o fheroes2.data fheroes2.js fheroes2.wasm
diff --git a/src/engine/audio.cpp b/src/engine/audio.cpp
index f152d36e63d..731954a1ca4 100644
--- a/src/engine/audio.cpp
+++ b/src/engine/audio.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
- * Copyright (C) 2019 - 2024 *
+ * Copyright (C) 2019 - 2025 *
* *
* Free Heroes2 Engine: http://sourceforge.net/projects/fheroes2 *
* Copyright (C) 2008 by Andrey Afletdinov *
@@ -540,6 +540,8 @@ namespace
// Either there is no need to resume music playback, or the resumption failed. Let's try to
// start the playback from the beginning.
if ( returnCode != 0 ) {
+ track->setPosition( 0 );
+
returnCode = Mix_FadeInMusic( mus.get(), loopCount, musicFadeInMs );
if ( returnCode != 0 ) {
diff --git a/src/engine/serialize.cpp b/src/engine/serialize.cpp
index d528e8b5f72..e05b777bd91 100644
--- a/src/engine/serialize.cpp
+++ b/src/engine/serialize.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
- * Copyright (C) 2019 - 2024 *
+ * Copyright (C) 2019 - 2025 *
* *
* Free Heroes2 Engine: http://sourceforge.net/projects/fheroes2 *
* Copyright (C) 2012 by Andrey Afletdinov *
@@ -29,6 +29,16 @@
#include
#include
+#ifdef __EMSCRIPTEN__
+#include
+
+#include
+#include
+#include
+
+#include "tools.h"
+#endif
+
#include "logging.h"
namespace
@@ -622,3 +632,41 @@ std::string StreamFile::getString( const size_t size /* = 0 */ )
return { buf.begin(), std::find( buf.begin(), buf.end(), 0 ) };
}
+
+int StreamFile::closeFile( std::FILE * f )
+{
+#ifdef __EMSCRIPTEN__
+ const bool needSyncFS = [f]() {
+ static_assert( CountBits( O_WRONLY ) == 1 && CountBits( O_RDWR ) == 1 );
+
+ const int fn = fileno( f );
+ if ( fn < 0 ) {
+ ERROR_LOG( "fileno() error: " << errno )
+ return false;
+ }
+
+ const int flags = fcntl( fn, F_GETFL );
+ if ( flags < 0 ) {
+ ERROR_LOG( "fcntl() error: " << errno )
+ return false;
+ }
+
+ return ( flags & O_WRONLY ) || ( flags & O_RDWR );
+ }();
+#endif
+
+ const int res = std::fclose( f );
+
+#ifdef __EMSCRIPTEN__
+ if ( needSyncFS ) {
+ EM_ASM(
+ // The following code is not C++ code, but JavaScript code.
+ // clang-format off
+ FS.syncfs( err => err && console.warn( "FS.syncfs() error:", err ) )
+ // clang-format on
+ );
+ }
+#endif
+
+ return res;
+}
diff --git a/src/engine/serialize.h b/src/engine/serialize.h
index efc71abfb30..05883448e55 100644
--- a/src/engine/serialize.h
+++ b/src/engine/serialize.h
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
- * Copyright (C) 2019 - 2024 *
+ * Copyright (C) 2019 - 2025 *
* *
* Free Heroes2 Engine: http://sourceforge.net/projects/fheroes2 *
* Copyright (C) 2012 by Andrey Afletdinov *
@@ -669,7 +669,9 @@ class StreamFile final : public IStreamBase, public OStreamBase
}
}
- std::unique_ptr _file{ nullptr, []( std::FILE * f ) { return std::fclose( f ); } };
+ static int closeFile( std::FILE * f );
+
+ std::unique_ptr _file{ nullptr, closeFile };
};
namespace fheroes2
diff --git a/src/engine/thread.cpp b/src/engine/thread.cpp
index 10f86736900..e396ae026fa 100644
--- a/src/engine/thread.cpp
+++ b/src/engine/thread.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
- * Copyright (C) 2022 - 2023 *
+ * Copyright (C) 2022 - 2025 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -23,10 +23,39 @@
#include
#include
+namespace
+{
+#ifdef __EMSCRIPTEN__
+ class MutexUnlocker
+ {
+ public:
+ explicit MutexUnlocker( std::mutex & mutex )
+ : _mutex( mutex )
+ {
+ _mutex.unlock();
+ }
+
+ MutexUnlocker( const MutexUnlocker & ) = delete;
+
+ ~MutexUnlocker()
+ {
+ _mutex.lock();
+ }
+
+ MutexUnlocker & operator=( const MutexUnlocker & ) = delete;
+
+ private:
+ std::mutex & _mutex;
+ };
+#endif
+}
+
namespace MultiThreading
{
void AsyncManager::createWorker()
{
+ // Emscripten has no pthread support. The worker thread should not be started.
+#ifndef __EMSCRIPTEN__
if ( !_worker ) {
_runFlag = true;
_worker = std::make_unique( AsyncManager::_workerThread, this );
@@ -37,10 +66,15 @@ namespace MultiThreading
_masterNotification.wait( lock, [this] { return !_runFlag; } );
}
}
+#endif
}
void AsyncManager::stopWorker()
{
+#ifdef __EMSCRIPTEN__
+ // Emscripten has no pthread support. The worker thread should not be running here.
+ assert( !_worker );
+#else
if ( _worker ) {
{
const std::scoped_lock lock( _mutex );
@@ -54,13 +88,33 @@ namespace MultiThreading
_worker->join();
_worker.reset();
}
+#endif
}
void AsyncManager::notifyWorker()
{
_runFlag = true;
+ // Emscripten has no pthread support, so instead of passing tasks to the worker thread, we will process them immediately in the current thread.
+#ifdef __EMSCRIPTEN__
+ assert( !_exitFlag );
+
+ while ( _runFlag ) {
+ const bool moreTasks = prepareTask();
+ if ( !moreTasks ) {
+ _runFlag = false;
+ }
+
+ {
+ // In accordance with the contract, the _mutex should NOT be acquired while calling the executeTask().
+ MutexUnlocker unlocker( _mutex );
+
+ executeTask();
+ }
+ }
+#else
_workerNotification.notify_all();
+#endif
}
void AsyncManager::_workerThread( AsyncManager * manager )
diff --git a/src/engine/thread.h b/src/engine/thread.h
index d3f9bb00ae2..4885dea2252 100644
--- a/src/engine/thread.h
+++ b/src/engine/thread.h
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
- * Copyright (C) 2022 *
+ * Copyright (C) 2022 - 2025 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -53,11 +53,10 @@ namespace MultiThreading
// Notify the worker thread about a new task. The _mutex should be acquired while calling this method.
void notifyWorker();
- // Prepare a task which requires mutex lock. Returns true if more tasks are available.
+ // Prepare the next task. The _mutex will be acquired while calling this method. Returns true if more tasks are available.
virtual bool prepareTask() = 0;
- // Task execution is done in non-thread safe mode! No mutex lock for any means of synchronizations are
- // done for this call.
+ // Execute the next task. NOTE WELL: the _mutex will NOT be acquired while calling this method.
virtual void executeTask() = 0;
private:
diff --git a/src/fheroes2/game/game_hotkeys.cpp b/src/fheroes2/game/game_hotkeys.cpp
index be067217449..4df9820230e 100644
--- a/src/fheroes2/game/game_hotkeys.cpp
+++ b/src/fheroes2/game/game_hotkeys.cpp
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
- * Copyright (C) 2019 - 2024 *
+ * Copyright (C) 2019 - 2025 *
* *
* Free Heroes2 Engine: http://sourceforge.net/projects/fheroes2 *
* Copyright (C) 2010 by Andrey Afletdinov *
@@ -27,7 +27,6 @@
#include
#include
#include
-#include
#include
#include