Initial commit
This commit is contained in:
95
dists/emscripten/README.md
Normal file
95
dists/emscripten/README.md
Normal file
@@ -0,0 +1,95 @@
|
||||
|
||||
# Building ScummVM for Webassembly
|
||||
The [Emscripten](https://emscripten.org/) target provides a script to build ScummVM as a single page browser app.
|
||||
|
||||
## Goals
|
||||
This port of ScummVM has two primary use cases as its goals:
|
||||
|
||||
- **Demo App**: The goal of this use case is to provide an easy way for people to discover ScummVM and old adventure games. Game preservation is not just about archival but also accessibility. The primary goal is to make it as easy as possible to play any game which can legally be made available, and there's probably nothing easier than opening a webpage to do so.
|
||||
|
||||
- **ScummVM as a PWA** (progressive web app): There are platforms where native ScummVM is not readily available (primarily iOS/iPadOS). A PWA can work around these limitations. To really make this work, a few more features beyond what's in a Demo App would be required:
|
||||
* Offline Support: PWAs can run offline. This means we have to find a way to cache some data which is downloaded on demand (engine plugins, game data etc.)
|
||||
* Cloud Storage Integration: Users will have to have a way to bring their own games and export savegame data. This is best possible through cloud storage integration. This already exists in ScummVM, but a few adjustments will be necessary to make this work in a PWA.
|
||||
|
||||
See [chkuendig/scummvm-demo](http://github.com/chkuendig/scummvm-demo/) on how a ScummVM demo app can be built (incl. playable demo).
|
||||
|
||||
## About Webassembly and Emscripten
|
||||
Emscripten is an LLVM/Clang-based compiler that compiles C and C++ source code to WebAssembly for execution in web browsers.
|
||||
|
||||
**Note:** In general most code can be crosscompiled to webassembly just fine. There's a few minor things which are different, but the mayor difference comnes down to how instructions are processed: Javascript and webassembly do support asynchronous/non-blocking code, but in general everything is running in the same [event loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop). This means also that webassembly code has to pause for the browser to do it's operations - render the page, process inputs, run I/O and so on. One consequence of this is that the page is not re-drawn until the webassembly code "yields" to the browser. Emscripten provides as much tooling as possible for this, but there's sometimes still a need to manually add a call to sleep into some engines.
|
||||
|
||||
## How to build for Webassembly
|
||||
This folder contains a script to help build scummvm with Emscripten, it automatically downloads the correct emsdk version and also takes care of bundling the data and setting up a few demo games.
|
||||
|
||||
### Running build.sh
|
||||
|
||||
`build.sh` needs to be run from the root of the project.
|
||||
```Shell
|
||||
./dists/emscripten/build.sh [Tasks] [Options]
|
||||
```
|
||||
|
||||
**Tasks:** space separated list of tasks to run. These can be:
|
||||
* `build`: Run all tasks to build the complete app. These tasks are:
|
||||
* `setup`: Download + install EMSDK and emscripten
|
||||
* `libs`: Download and compile the required 3rd-party libraries required to build certain engines (libmad, a52dec etc)
|
||||
* `configure`: Run the configure script with emconfigure with the recommended settings for a simple demo page
|
||||
* `make`: Run the make scripts with emmake
|
||||
* `games`: Download some demos and set up all data require for the demo page. See `--bundle-games=` below.
|
||||
* `dist`: Copy all files into a single build-emscripten folder to bring it all together
|
||||
* `add-games`: Runs ScummVM once to add all bundled games to the default `scummvm.ini`
|
||||
* `icons`: Adds additional icons to the `gui-icons.dat` file. Please note that the `scummvm-icons` repository needs to be located in the parent folder of the project.
|
||||
* `clean`: Cleanup build artifacts (keeps libs + emsdk in place)
|
||||
* `run`: Start webserver and launch ScummVM in Chrome
|
||||
|
||||
**Options:**
|
||||
* `-h`, `--help`: print a short help text
|
||||
* `--bundle-games=<games>`: comma-separated list of demos and freeware games to bundle. Either specify a target (e.g. `comi` or a target and a specific file after a `/` , e.g. `comi/comi-win-large-demo-en.zip`)
|
||||
* `-v`, `--verbose`: print all commands run by the script
|
||||
* `--*`: all other options are passed on to the scummvm configure script
|
||||
|
||||
Independent of the command executed, the script sets up a pre-defined emsdk environment in the subfolder `./dists/emscripten/build.sh`
|
||||
|
||||
**Example:**
|
||||
|
||||
See e.g. [chkuendig/scummvm-demo/.github/workflows/main.yml](https://github.com/chkuendig/scummvm-demo/blob/main/.github/workflows/main.yml) for an example:
|
||||
```
|
||||
./dists/emscripten/build.sh build --verbose --disable-all-engines --enable-plugins --default-dynamic --enable-engine=adl,testbed,scumm,scumm_7_8,grim,monkey4,mohawk,myst,riven,sci32,agos2,sword2,drascula,sky,lure,queen,testbed,director,stark --bundle-games=testbed,comi/comi-win-large-demo-en.zip,warlock,sky/BASS-Floppy-1.3.zip,drascula/drascula-audio-mp3-2.0.zip,monkey4,feeble,queen/FOTAQ_Floppy.zip,ft,grim/grim-win-demo2-en.zip,lsl7,lure,myst,phantasmagoria,riven,hires1,tlj,sword2
|
||||
```
|
||||
|
||||
## Current Status of Port
|
||||
In general, ScummVM runs in the browser sufficiently to run all demos and freeware games.
|
||||
|
||||
* All engines compile (though I didn't test all of them), including ResidualVM with WebGL acceleration and shaders and run as plugins (which means the initial page load is somewhat limited)
|
||||
* Audio works and 3rd-party libraries for sound and video decoding are integrated.
|
||||
* All data can be downloaded on demand (or in the case of the testbed generated as part of the build script)
|
||||
|
||||
## Known Issues + Possible Improvements
|
||||
|
||||
### Emscripten Asyncify Optimizations
|
||||
ScummVM relies heavily on Asyncify (see note above), and this comes with a quite heavy performance penalty. Possible optimizations in this regard could be:
|
||||
* Specify a `ASYNCIFY_ONLY` list in `configure` to make asyncify only instrument functions in the call path as described in [emscripten.org: Asyncify](https://emscripten.org/docs/porting/asyncify.html)
|
||||
* Limit asyncify overhead by having a more specific setting for `ASYNCIFY_IMPORTS` in `configure`. This is especailly critical for plugins as when plugins are enabled, we currently add all functions as imports.
|
||||
* Don't use asyncify but rewrite main loop to improve performance.
|
||||
* Look into Stack Switching (emscripten-core/emscripten#16779) or multithreading as an alternative to Asyncify.
|
||||
|
||||
### Storage Integration
|
||||
* Settings can be persisted locally and assets can be loaded over HTTP, but more improvements could be possible:
|
||||
* Use Range-Requests to download only parts of a file when not the whole file is not needed
|
||||
* Download all game assets in background once the game has started
|
||||
* Persist last game and last plugin for offline use
|
||||
* Pre-load assets asynchronously (not blocking) - i.e. rest of the data of a game which has been launched
|
||||
* Loading indicators (doesn't work with the current synchronous/blocking filesystem)
|
||||
Emscripten is currently re-doing their filesystem code, which could help address some of the above issues ( emscripten-core/emscripten#15041 ).
|
||||
* Locally persisted file system for saved games and settings using the Browser IndexedDB. (using [Emscripten IDBFS](https://emscripten.org/docs/api_reference/Filesystem-API.html#filesystem-api-idbfs))
|
||||
* Cloud storage (Dropbox, Google Drive etc.) is exposed as a special folder on the file system. Only read access is implemented, but saved games can be synchronized via the regular cloud sync feature
|
||||
* Screenshots and Logfiles are automatically downloaded after creation
|
||||
* All other data is stored in memory and removed on reload (incl. temporarily stored logfiles and screenshots)
|
||||
|
||||
### UI Integration
|
||||
* Build a nice webpage around the canvas.
|
||||
* Allow showing/hiding of console (at the moment there's only the browser console)
|
||||
* Bonus: Adapt page padding/background color to theme (black when in game)
|
||||
* Automatically show console in case of exceptions
|
||||
* 🐞 Aspect Ratio is broken when starting a game until the window is resized once. Good starting points might be https://github.com/emscripten-ports/SDL2/issues/47 or https://github.com/emscripten-core/emscripten/issues/10285
|
||||
* doesn't seem to affect 3D engines in opengl mode
|
||||
* definitely affects testbed in OpenGL or other modes
|
||||
27
dists/emscripten/assets/manifest.json
Normal file
27
dists/emscripten/assets/manifest.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"short_name": "ScummVM",
|
||||
"name": "ScummVM: Script Creation Utility for Maniac Mansion Virtual Machine",
|
||||
"icons": [
|
||||
{
|
||||
"src": "scummvm-192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "scummvm-512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": "scummvm.html",
|
||||
"background_color": "#3367D6",
|
||||
"display": "fullscreen",
|
||||
"theme_color": "#c60",
|
||||
"shortcuts": [
|
||||
|
||||
],
|
||||
"description": "ScummVM provides a way to play many classic graphical point-and-click adventure games and RPGs - such as SCUMM games (like Monkey Island and Day of the Tentacle), Revolution's Beneath A Steel Sky, and many more.",
|
||||
"screenshots": [
|
||||
|
||||
]
|
||||
}
|
||||
BIN
dists/emscripten/assets/scummvm-192.png
Normal file
BIN
dists/emscripten/assets/scummvm-192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
BIN
dists/emscripten/assets/scummvm-512.png
Normal file
BIN
dists/emscripten/assets/scummvm-512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
97
dists/emscripten/build-make_http_index.py
Normal file
97
dists/emscripten/build-make_http_index.py
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
# ScummVM - Graphic Adventure Engine
|
||||
#
|
||||
# ScummVM is the legal property of its developers, whose names
|
||||
# are too numerous to list here. Please refer to the COPYRIGHT
|
||||
# file distributed with this source distribution.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sym_links = {}
|
||||
ignore_files = ['.git', 'index.json']
|
||||
|
||||
def rd_sync(dpath, tree, name):
|
||||
"""Recursively scan directory and build file tree structure."""
|
||||
try:
|
||||
files = os.listdir(dpath)
|
||||
except (OSError, PermissionError):
|
||||
return tree
|
||||
|
||||
for file in files:
|
||||
# ignore non-essential directories / files
|
||||
if file in ignore_files or file.startswith('.'):
|
||||
continue
|
||||
|
||||
fpath = os.path.join(dpath, file)
|
||||
|
||||
try:
|
||||
# Avoid infinite loops with symbolic links
|
||||
lstat = os.lstat(fpath)
|
||||
if os.path.islink(fpath):
|
||||
dev = lstat.st_dev
|
||||
ino = lstat.st_ino
|
||||
|
||||
if dev not in sym_links:
|
||||
sym_links[dev] = {}
|
||||
|
||||
# Ignore if we've seen it before
|
||||
if ino in sym_links[dev]:
|
||||
continue
|
||||
|
||||
sym_links[dev][ino] = True
|
||||
|
||||
if os.path.isdir(fpath):
|
||||
child = {}
|
||||
tree[file] = child
|
||||
rd_sync(fpath, child, file)
|
||||
|
||||
# Write index.json for this directory
|
||||
fs_listing = json.dumps(child)
|
||||
fname = os.path.join(fpath, "index.json")
|
||||
with open(fname, 'w', encoding='utf-8') as f:
|
||||
f.write(fs_listing)
|
||||
|
||||
# Reset tree entry to empty dict after writing index
|
||||
tree[file] = {}
|
||||
else:
|
||||
# Store file size
|
||||
stat = os.stat(fpath)
|
||||
tree[file] = stat.st_size
|
||||
|
||||
except (OSError, PermissionError):
|
||||
# Ignore and move on
|
||||
continue
|
||||
|
||||
return tree
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 2:
|
||||
root_folder = sys.argv[1]
|
||||
fs_listing = json.dumps(rd_sync(root_folder, {}, '/'))
|
||||
fname = os.path.join(root_folder, "index.json")
|
||||
with open(fname, 'w', encoding='utf-8') as f:
|
||||
f.write(fs_listing)
|
||||
else:
|
||||
root_folder = os.getcwd()
|
||||
fs_listing = json.dumps(rd_sync(root_folder, {}, '/'))
|
||||
print(fs_listing)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
403
dists/emscripten/build.sh
Normal file
403
dists/emscripten/build.sh
Normal file
@@ -0,0 +1,403 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# .dists/emscripten/build.sh -- Sets up an emscripten build environment and builds ScummVM for webassembly
|
||||
#
|
||||
# ScummVM is the legal property of its developers, whose names
|
||||
# are too numerous to list here. Please refer to the COPYRIGHT
|
||||
# file distributed with this source distribution.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
|
||||
|
||||
# exit when any command fails
|
||||
set -e
|
||||
|
||||
ROOT_FOLDER=$(pwd)
|
||||
DIST_FOLDER="$ROOT_FOLDER/dists/emscripten"
|
||||
LIBS_FOLDER="$DIST_FOLDER/libs"
|
||||
TASKS=()
|
||||
CONFIGURE_ARGS=()
|
||||
_bundle_games=()
|
||||
_verbose=false
|
||||
EMSDK_VERSION="${EMSDK_VERSION:-4.0.10}"
|
||||
EMSCRIPTEN_VERSION="$EMSDK_VERSION"
|
||||
|
||||
usage="\
|
||||
Usage: ./dists/emscripten/build.sh [TASKS] [OPTIONS]
|
||||
|
||||
Output the configuration name of the system \`$me' is run on.
|
||||
|
||||
Tasks:
|
||||
(space separated) List of tasks to run. See ./dists/emscripten/README.md for details.
|
||||
|
||||
Options:
|
||||
-h, --help print this help, then exit
|
||||
-v, --verbose print all commands run by the script
|
||||
--* all other options are passed on to the configure script
|
||||
Note: --enable-a52, --enable-faad, --enable-fluidlite, --enable-fribidi,
|
||||
--enable-mad, --enable-mpcdec, --enable-mpeg2, --enable-mikmod,
|
||||
--enable-retrowave, --enable-theoradec and --enable-vpx
|
||||
also download and build the required library
|
||||
"
|
||||
|
||||
_fluidlite=false
|
||||
_fribidi=false
|
||||
_liba52=false
|
||||
_libfaad=false
|
||||
_libmad=false
|
||||
_libmpcdec=false
|
||||
_libmpeg2=false
|
||||
_libmikmod=false
|
||||
_libtheoradec=false
|
||||
_libvpx=false
|
||||
_retrowave=false
|
||||
|
||||
# parse inputs
|
||||
for i in "$@"; do
|
||||
case $i in
|
||||
--enable-a52)
|
||||
_liba52=true
|
||||
CONFIGURE_ARGS+=" $i"
|
||||
;;
|
||||
--enable-faad)
|
||||
_libfaad=true
|
||||
CONFIGURE_ARGS+=" $i"
|
||||
;;
|
||||
--enable-fluidlite)
|
||||
_fluidlite=true
|
||||
CONFIGURE_ARGS+=" $i"
|
||||
;;
|
||||
--enable-fribidi)
|
||||
_fribidi=true
|
||||
CONFIGURE_ARGS+=" $i"
|
||||
;;
|
||||
--enable-mad)
|
||||
_libmad=true
|
||||
CONFIGURE_ARGS+=" $i"
|
||||
;;
|
||||
--enable-mpeg2)
|
||||
_libmpeg2=true
|
||||
CONFIGURE_ARGS+=" $i"
|
||||
;;
|
||||
--enable-mpcdec)
|
||||
_libmpcdec=true
|
||||
# We don't pass --enable-mpcdec as configure
|
||||
# has to establish which API to use (old or new)
|
||||
;;
|
||||
--enable-openmpt)
|
||||
_libopenmpt=true
|
||||
CONFIGURE_ARGS+=" $i"
|
||||
;;
|
||||
--enable-retrowave)
|
||||
_retrowave=true
|
||||
CONFIGURE_ARGS+=" $i"
|
||||
;;
|
||||
--enable-theoradec)
|
||||
_libtheoradec=true
|
||||
CONFIGURE_ARGS+=" $i"
|
||||
;;
|
||||
--enable-vpx)
|
||||
_libvpx=true
|
||||
CONFIGURE_ARGS+=" $i"
|
||||
;;
|
||||
--bundle-games=*)
|
||||
str="${i#*=}"
|
||||
_bundle_games="${str//,/ }"
|
||||
shift # past argument=value
|
||||
;;
|
||||
-h | --help)
|
||||
echo "$usage"
|
||||
exit
|
||||
;;
|
||||
-v | --verbose)
|
||||
_verbose=true
|
||||
;;
|
||||
-* | --*)
|
||||
CONFIGURE_ARGS+=" $i"
|
||||
;;
|
||||
*)
|
||||
TASKS+="|$i" # save positional arg
|
||||
shift # past argument
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
TASKS="${TASKS:1}"
|
||||
if [[ -z "$TASKS" ]]; then
|
||||
echo "$usage"
|
||||
exit
|
||||
fi
|
||||
|
||||
# print commands
|
||||
if [[ "$_verbose" = true ]]; then
|
||||
set -o xtrace
|
||||
fi
|
||||
|
||||
#################################
|
||||
# Setup Toolchain
|
||||
#################################
|
||||
|
||||
# Download Emscripten
|
||||
if [[ ! -d "$DIST_FOLDER/emsdk-$EMSDK_VERSION" ]]; then
|
||||
echo "$DIST_FOLDER/emsdk-$EMSDK_VERSION not found. Installing Emscripten"
|
||||
cd "$DIST_FOLDER"
|
||||
if [[ "$EMSDK_VERSION" = "tot" ]]; then
|
||||
git clone "https://github.com/emscripten-core/emsdk/" emsdk-tot
|
||||
else
|
||||
wget -nc --content-disposition --no-check-certificate "https://github.com/emscripten-core/emsdk/archive/refs/tags/${EMSDK_VERSION}.tar.gz"
|
||||
tar -xf "emsdk-${EMSDK_VERSION}.tar.gz"
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "$DIST_FOLDER/emsdk-${EMSDK_VERSION}"
|
||||
ret=0 # https://stackoverflow.com/questions/18621990/bash-get-exit-status-of-command-when-set-e-is-active
|
||||
./emsdk activate ${EMSCRIPTEN_VERSION} || ret=$?
|
||||
if [[ $ret != 0 ]]; then
|
||||
echo "install missing emscripten version"
|
||||
cd "$DIST_FOLDER/emsdk-${EMSDK_VERSION}"
|
||||
./emsdk install ${EMSCRIPTEN_VERSION}
|
||||
|
||||
cd "$DIST_FOLDER/emsdk-${EMSDK_VERSION}"
|
||||
./emsdk activate ${EMSCRIPTEN_VERSION}
|
||||
fi
|
||||
|
||||
source "$DIST_FOLDER/emsdk-$EMSDK_VERSION/emsdk_env.sh"
|
||||
|
||||
# export node_path - so we can use all node_modules bundled with emscripten (e.g. requests)
|
||||
EMSDK_NPM=$(dirname $EMSDK_NODE)/npm
|
||||
EMSDK_PYTHON="${EMSDK_PYTHON:-python3}"
|
||||
EMSDK_NPX=$(dirname $EMSDK_NODE)/npx
|
||||
export NODE_PATH="$(dirname $EMSDK_NODE)/../lib/node_modules/"
|
||||
LIBS_FLAGS=""
|
||||
|
||||
cd "$ROOT_FOLDER"
|
||||
|
||||
#################################
|
||||
# Download + Install Libraries (if not provided by Emscripten-Ports, those are handled by configure)
|
||||
#################################
|
||||
if [[ ! -d "$LIBS_FOLDER/build" ]]; then
|
||||
mkdir -p "$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
if [ "$_liba52" = true ]; then
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/liba52.a" ]]; then
|
||||
echo "building a52dec-0.7.4"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "https://code.videolan.org/videolan/liba52/-/archive/0.7.4/liba52-0.7.4.tar.gz"
|
||||
tar -xf liba52-0.7.4.tar.gz
|
||||
cd "$LIBS_FOLDER/liba52-0.7.4/"
|
||||
autoreconf -i
|
||||
CFLAGS="-fPIC -Oz" emconfigure ./configure --host=wasm32-unknown-none --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/"
|
||||
emmake make -j 5
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-a52-prefix=$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
if [ "$_libfaad" = true ]; then
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libfaad.a" ]]; then
|
||||
echo "building faad2-2.8.8"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "https://sourceforge.net/projects/faac/files/faad2-src/faad2-2.8.0/faad2-2.8.8.tar.gz"
|
||||
tar -xf faad2-2.8.8.tar.gz
|
||||
cd "$LIBS_FOLDER/faad2-2.8.8/"
|
||||
CFLAGS="-fPIC -Oz" emconfigure ./configure --host=wasm32-unknown-none --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/"
|
||||
emmake make -j 5
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-faad-prefix=$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
if [ "$_fluidlite" = true ]; then
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libfluidlite.a" ]]; then
|
||||
echo "building fluidlite-b0f187b"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc --content-disposition "https://github.com/divideconcept/FluidLite/archive/b0f187b404e393ee0a495b277154d55d7d03cbeb.tar.gz"
|
||||
tar -xf FluidLite-b0f187b404e393ee0a495b277154d55d7d03cbeb.tar.gz
|
||||
cd "$LIBS_FOLDER/FluidLite-b0f187b404e393ee0a495b277154d55d7d03cbeb/"
|
||||
emcmake cmake -B "build/" -DFLUIDLITE_BUILD_STATIC:BOOL="1" -DCMAKE_INSTALL_PREFIX="$LIBS_FOLDER/build/" -DCMAKE_INSTALL_LIBDIR="lib"
|
||||
cmake --build "build/"
|
||||
cmake --install "build/"
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-fluidlite-prefix=$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
if [ "$_fribidi" = true ]; then
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libfribidi.a" ]]; then
|
||||
echo "building fribidi-1.0.10"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "https://github.com/fribidi/fribidi/releases/download/v1.0.10/fribidi-1.0.10.tar.xz"
|
||||
tar -xf fribidi-1.0.10.tar.xz
|
||||
cd "$LIBS_FOLDER/fribidi-1.0.10/"
|
||||
CFLAGS="-fPIC -Oz" emconfigure ./configure --host=wasm32-unknown-none --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/"
|
||||
emmake make -j 5
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-fribidi-prefix=$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
if [ "$_libmad" = true ]; then
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libmad.a" ]]; then
|
||||
echo "building libmad-0.15.1b"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "https://downloads.sourceforge.net/mad/libmad-0.15.1b.tar.gz"
|
||||
tar -xf libmad-0.15.1b.tar.gz
|
||||
cd "$LIBS_FOLDER/libmad-0.15.1b/"
|
||||
# libmad needs patching as -fforce-mem has been removed in GCC 4.3 and later
|
||||
sed -i -e 's/-fforce-mem//g' configure
|
||||
CFLAGS="-Oz" emconfigure ./configure --host=wasm32-unknown-none --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/" --with-pic --enable-fpm=no
|
||||
emmake make -j 5
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-mad-prefix=$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
if [ "$_libmpeg2" = true ]; then
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libmpeg2.a" ]]; then
|
||||
echo "building libmpeg2-946bf4b5"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc --content-disposition "https://code.videolan.org/videolan/libmpeg2/-/archive/946bf4b518aacc224f845e73708f99e394744499/libmpeg2-946bf4b518aacc224f845e73708f99e394744499.tar.gz"
|
||||
tar -xf libmpeg2-946bf4b518aacc224f845e73708f99e394744499.tar.gz
|
||||
cd "$LIBS_FOLDER/libmpeg2-946bf4b518aacc224f845e73708f99e394744499/"
|
||||
autoreconf -i
|
||||
CFLAGS="-fPIC -Oz" emconfigure ./configure --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/" --disable-sdl
|
||||
emmake make -j 5
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-mpeg2-prefix=$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
if [ "$_libmpcdec" = true ]; then
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libmpcdec.a" ]]; then
|
||||
echo "building libmpcdec-1.2.6"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "https://files.musepack.net/source/libmpcdec-1.2.6.tar.bz2"
|
||||
tar -xf libmpcdec-1.2.6.tar.bz2
|
||||
cd "$LIBS_FOLDER/libmpcdec-1.2.6/"
|
||||
CFLAGS="-Oz" emconfigure ./configure --host=wasm32-unknown-none --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/" --with-pic --enable-fpm=no
|
||||
emmake make -j 5
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-mpcdec-prefix=$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
if [ "$_libopenmpt" = true ]; then
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libopenmpt.a" ]]; then
|
||||
echo "building libopenmpt-0.7.13"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "https://lib.openmpt.org/files/libopenmpt/src/libopenmpt-0.6.22+release.makefile.tar.gz"
|
||||
tar -xf libopenmpt-0.6.22+release.makefile.tar.gz
|
||||
cd "$LIBS_FOLDER/libopenmpt-0.6.22+release/"
|
||||
CFLAGS="-fPIC -Oz" emmake make -j 5 CONFIG=emscripten EMSCRIPTEN_TARGET=wasm
|
||||
emmake make install CONFIG=emscripten EMSCRIPTEN_TARGET=wasm PREFIX="$LIBS_FOLDER/build/"
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-openmpt-prefix=$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
if [ "$_retrowave" = true ]; then
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libRetroWave.a" ]]; then
|
||||
echo "build libRetroWave-e6bf60e"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc --content-disposition "https://github.com/SudoMaker/RetroWave/archive/e6bf60eed2d2bd1deff688d645be71a32bbf05bb.tar.gz"
|
||||
tar -xf RetroWave-e6bf60eed2d2bd1deff688d645be71a32bbf05bb.tar.gz
|
||||
cd "$LIBS_FOLDER/RetroWave-e6bf60eed2d2bd1deff688d645be71a32bbf05bb/"
|
||||
CFLAGS="-fPIC -s USE_ZLIB=1 -Oz" emcmake cmake -B "build/" -DRETROWAVE_BUILD_PLAYER=0 -DCMAKE_INSTALL_PREFIX="$LIBS_FOLDER/build/" -DCMAKE_INSTALL_LIBDIR="lib"
|
||||
cmake --build "build/"
|
||||
cmake --install "build/"
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-retrowave-prefix=$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
if [ "$_libtheoradec" = true ]; then
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libtheora.a" ]]; then
|
||||
echo "build libtheora-1.1.1"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "https://downloads.xiph.org/releases/theora/libtheora-1.1.1.tar.xz"
|
||||
tar -xf libtheora-1.1.1.tar.xz
|
||||
cd "$LIBS_FOLDER/libtheora-1.1.1/"
|
||||
CFLAGS="-fPIC -s USE_OGG=1 -Oz" emconfigure ./configure --host=wasm32-unknown-none --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/" --disable-asm
|
||||
emmake make -j 5
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-theoradec-prefix=$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
if [ "$_libvpx" = true ]; then
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libvpx.a" ]]; then
|
||||
echo "build libvpx-1.15.0"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc --content-disposition "https://github.com/webmproject/libvpx/archive/refs/tags/v1.15.0.tar.gz"
|
||||
tar -xf libvpx-1.15.0.tar.gz
|
||||
cd "$LIBS_FOLDER/libvpx-1.15.0/"
|
||||
CFLAGS="-fPIC -Oz" emconfigure ./configure --disable-vp8-encoder --target=generic-gnu --disable-vp9-encoder --prefix="$LIBS_FOLDER/build/"
|
||||
emmake make -j 5
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-vpx-prefix=$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
#################################
|
||||
# Configure
|
||||
#################################
|
||||
if [[ "configure" =~ $(echo ^\(${TASKS}\)$) || "build" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
cd "${ROOT_FOLDER}"
|
||||
echo "Running configure"
|
||||
# TODO: Figure out how configure could guess the host
|
||||
emconfigure ./configure --host=wasm32-unknown-emscripten --build=wasm32-unknown-emscripten ${CONFIGURE_ARGS} ${LIBS_FLAGS}
|
||||
|
||||
# TODO: configure currently doesn't clean up all files it creates
|
||||
rm scummvm-conf.*
|
||||
fi
|
||||
|
||||
#################################
|
||||
# Make / Compile
|
||||
#################################
|
||||
if [[ "make" =~ $(echo ^\(${TASKS}\)$) || "build" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
cd "${ROOT_FOLDER}"
|
||||
echo "Running make"
|
||||
num_cpus=$(nproc || grep -c ^processor /proc/cpuinfo || echo 1)
|
||||
emmake make -j ${num_cpus}
|
||||
fi
|
||||
|
||||
#################################
|
||||
# Bundle everything into a neat package
|
||||
#################################
|
||||
if [[ "dist" =~ $(echo ^\(${TASKS}\)$) || "build" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
echo "Bundle ScummVM for static file hosting"
|
||||
emmake make dist-emscripten
|
||||
fi
|
||||
|
||||
#################################
|
||||
# Run Development Server
|
||||
#################################
|
||||
if [[ "run" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
echo "Run ScummVM"
|
||||
cd "${ROOT_FOLDER}/build-emscripten/"
|
||||
emrun --browser=chrome scummvm.html
|
||||
fi
|
||||
|
||||
#################################
|
||||
# Clean
|
||||
#################################
|
||||
if [[ "clean" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
emmake make clean || true
|
||||
emmake make distclean || true
|
||||
emcc --clear-ports --clear-cache
|
||||
rm -rf ./build-emscripten/ || true
|
||||
rm scummvm.debug.wasm || true
|
||||
rm scummvm.wasm || true
|
||||
rm scummvm.js || true
|
||||
fi
|
||||
10
dists/emscripten/custom_shell-post.js
Normal file
10
dists/emscripten/custom_shell-post.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// Workaround for https://github.com/emscripten-core/emscripten/pull/9803
|
||||
// which results in mouse events not working anymore after context switches
|
||||
// (i.e. when launching a game)
|
||||
/*global JSEvents*/
|
||||
JSEvents.removeAllHandlersOnTarget = function(){};
|
||||
|
||||
// Make sure to release any resources (e.g. RetroWave or Midi Devices) when leaving the page
|
||||
window.addEventListener("beforeunload", function (e) {
|
||||
Module["_raise"](2); // SIGINT
|
||||
});
|
||||
25
dists/emscripten/custom_shell-pre.js
Normal file
25
dists/emscripten/custom_shell-pre.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/*global Module*/
|
||||
Module["arguments"] = [];
|
||||
|
||||
// Add all parameters passed via the fragment identifier
|
||||
if (window.location.hash.length > 0) {
|
||||
params = decodeURI(window.location.hash.substring(1)).split(" ")
|
||||
params.forEach((param) => {
|
||||
Module["arguments"].push(param);
|
||||
})
|
||||
}
|
||||
|
||||
// MIDI support
|
||||
var midiOutputMap;
|
||||
if (!("requestMIDIAccess" in navigator)) {
|
||||
console.error("No MIDI support in your browser.");
|
||||
} else {
|
||||
navigator
|
||||
.requestMIDIAccess({ sysex: true, software: true })
|
||||
.then((midiAccess) => {
|
||||
midiOutputMap = midiAccess.outputs;
|
||||
midiAccess.onstatechange = (e) => {
|
||||
midiOutputMap = e.target.outputs;
|
||||
};
|
||||
});
|
||||
}
|
||||
223
dists/emscripten/custom_shell.html
Normal file
223
dists/emscripten/custom_shell.html
Normal file
@@ -0,0 +1,223 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta content="width=device-width,initial-scale=1,viewport-fit=cover" name=viewport>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<link rel="apple-touch-icon" href="scummvm-192.png">
|
||||
<title>ScummVM</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: none;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.emscripten {
|
||||
padding-right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
textarea.emscripten {
|
||||
font-family: monospace;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
div.emscripten {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.emscripten_border {
|
||||
border: 1px solid black;
|
||||
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
|
||||
}
|
||||
|
||||
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
|
||||
canvas.emscripten {
|
||||
border: 0px none;
|
||||
background: url("logo.svg");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #cc6600;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
margin: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (orientation: landscape) {
|
||||
canvas.emscripten {
|
||||
background-size: auto 33%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: portrait) {
|
||||
canvas.emscripten {
|
||||
background-size: 80% auto;
|
||||
}
|
||||
}
|
||||
|
||||
#progress {
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
border: 0px;
|
||||
background: #c60
|
||||
}
|
||||
|
||||
progress::-moz-progress-bar {
|
||||
background: #f6e08a;
|
||||
}
|
||||
|
||||
progress::-webkit-progress-value {
|
||||
background: #f6e08a;
|
||||
}
|
||||
|
||||
progress {
|
||||
color: #f6e08a;
|
||||
}
|
||||
|
||||
#status {
|
||||
position: absolute;
|
||||
bottom: 5em;
|
||||
right: 0px;
|
||||
padding: 5px;
|
||||
text-align: right;
|
||||
border-top-left-radius: 1em;
|
||||
border-bottom-left-radius: 1em;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
z-index: 3;
|
||||
border: 3px solid black;
|
||||
border-right: none;
|
||||
background: #f6e08a;
|
||||
font: bold large/1.4 "Trebuchet MS", Verdana, Tahoma, Sans-Serif;
|
||||
}
|
||||
|
||||
#status.error {
|
||||
background: red
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class=emscripten>
|
||||
<progress hidden id=progress max=100 value=0></progress>
|
||||
</div>
|
||||
<div class="emscripten" id="status">Downloading ScummVM...</div>
|
||||
|
||||
<div class=emscripten_border>
|
||||
<canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<textarea class="emscripten" id="output" rows="8"></textarea>
|
||||
|
||||
<script type='text/javascript'>
|
||||
|
||||
var statusElement = document.getElementById('status');
|
||||
var progressElement = document.getElementById('progress');
|
||||
|
||||
function loadingDoneMessage() {
|
||||
document.getElementById("progress").style.zIndex = 0;
|
||||
return "All downloads complete."
|
||||
}
|
||||
|
||||
|
||||
var Module = {
|
||||
preRun: [],
|
||||
postRun: [],
|
||||
print: (function () {
|
||||
var element = document.getElementById('output');
|
||||
if (element) element.value = ''; // clear browser cache
|
||||
return function (text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
// These replacements are necessary if you render to raw HTML
|
||||
//text = text.replace(/&/g, "&");
|
||||
//text = text.replace(/</g, "<");
|
||||
//text = text.replace(/>/g, ">");
|
||||
//text = text.replace('\n', '<br>', 'g');
|
||||
console.log(text);
|
||||
if (element) {
|
||||
element.value += text + "\n";
|
||||
element.scrollTop = element.scrollHeight; // focus on bottom
|
||||
}
|
||||
};
|
||||
})(),
|
||||
printErr: function (text) {
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
console.error(text);
|
||||
},
|
||||
canvas: (function () {
|
||||
var canvas = document.getElementById('canvas');
|
||||
|
||||
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
|
||||
// application robust, you may want to override this behavior before shipping!
|
||||
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
|
||||
canvas.addEventListener("webglcontextlost", function (e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
|
||||
|
||||
return canvas;
|
||||
})(),
|
||||
setStatus: function (text) {
|
||||
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
|
||||
if (text === Module.setStatus.last.text) return;
|
||||
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
|
||||
var now = Date.now();
|
||||
if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
|
||||
Module.setStatus.last.time = now;
|
||||
Module.setStatus.last.text = text;
|
||||
if (m) {
|
||||
text = m[1];
|
||||
progressElement.value = parseInt(m[2]) * 100;
|
||||
progressElement.max = parseInt(m[4]) * 100;
|
||||
progressElement.hidden = false;
|
||||
|
||||
} else {
|
||||
progressElement.value = null;
|
||||
progressElement.max = null;
|
||||
progressElement.hidden = true;
|
||||
}
|
||||
if (text && text.length > 0) {
|
||||
console.log((new Date()).toLocaleTimeString() + " " + text)
|
||||
text += "⚡️"
|
||||
statusElement.style.display = "block";
|
||||
} else {
|
||||
statusElement.style.display = "none";
|
||||
}
|
||||
statusElement.innerHTML = text;
|
||||
},
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: function (left) {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies - left) + '/' + this.totalDependencies + ')' : loadingDoneMessage());
|
||||
}
|
||||
};
|
||||
Module.setStatus('Downloading ScummVM...');
|
||||
window.onerror = function () {
|
||||
statusElement.classList.add("error")
|
||||
Module.setStatus('Exception thrown, see JavaScript console');
|
||||
Module.setStatus = function (text) {
|
||||
if (text) Module.printErr('[post-exception status] ' + text);
|
||||
};
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
{{{ SCRIPT }}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user