mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-04 01:46:42 +00:00
EMSCRIPTEN: Major build improvements
- Updated Emscripten to version 3.1.8 (+ additional patches) - Support for dynamic plugins - Adding ScummvmFS with support for HTTP Range Requests for game data - Automated games/demos bundling and ini config generation during build - Allow passing CLI arguments via fragment identifier of the website (i.e. scummvm.html#—debuglevel=9 ) - UI improvements with nicer status messages, splash screen + favicon - Fixed HiDPI handling and responsiveness - Bugfix: Don't crash if gamepad support isn't available
This commit is contained in:
parent
8f0174689b
commit
bc3679e928
88
configure
vendored
88
configure
vendored
@ -838,6 +838,7 @@ Special configuration feature:
|
||||
psp for PlayStation Portable
|
||||
samsungtv for Samsung TV
|
||||
switch for Nintendo Switch
|
||||
wasm32-unknown-emscripten for WebAssembly
|
||||
wii for Nintendo Wii
|
||||
|
||||
Game engines:
|
||||
@ -1709,7 +1710,7 @@ wasm32-*)
|
||||
_endian=little # the endian check below fails, but emscripten is always little endian anyway
|
||||
_host_os=emscripten
|
||||
_host_cpu=wasm32
|
||||
datadir='/scummvm'
|
||||
datadir='/data'
|
||||
CXX="em++"
|
||||
;;
|
||||
wii)
|
||||
@ -2888,24 +2889,46 @@ EOF
|
||||
;;
|
||||
emscripten)
|
||||
# mandatory emscripten flags
|
||||
append_var LDFLAGS "-s FULL_ES2=1 -s MAX_WEBGL_VERSION=1 -s ASYNCIFY -s FORCE_FILESYSTEM=1 -s ALLOW_MEMORY_GROWTH=1"
|
||||
# enable emscripten-ports and set up paths accordingly
|
||||
append_var LDFLAGS "-s USE_SDL=2 -s USE_SDL_MIXER=2 -s USE_OGG=1 -s USE_VORBIS=1 -s USE_LIBJPEG=1 -s USE_FREETYPE=1 -s USE_ZLIB"
|
||||
_sdlpath="$EMSDK/upstream/emscripten/system/bin/"
|
||||
_pngpath="$EMSDK/upstream/emscripten/cache/"
|
||||
_freetypepath="$EMSDK/upstream/emscripten/cache/ports-builds/freetype/"
|
||||
VORBIS_CFLAGS="-I$EMSDK/upstream/emscripten/cache//ports-builds/vorbis/include"
|
||||
VORBIS_LIBS="-L$EMSDK/upstream/emscripten/cache/ports-builds/vorbis/lib"
|
||||
FREETYPE2_CFLAGS="-I$_freetypepath/include"
|
||||
_freetype_found="true"
|
||||
if test "$_debug_build" = no; then
|
||||
_optimization_level=-O3 -g4
|
||||
append_var LDFLAGS "-s ALLOW_MEMORY_GROWTH=1 -s ASYNCIFY -s FORCE_FILESYSTEM=1"
|
||||
|
||||
append_var DEFINES "-DEMSCRIPTEN"
|
||||
add_line_to_config_mk 'EMSCRIPTEN = 1'
|
||||
|
||||
if test "$_debug_build" = yes; then
|
||||
_optimization_level=-O2
|
||||
append_var LDFLAGS "-O2 -g3 -s ASSERTIONS=2"
|
||||
else
|
||||
_optimization_level=-O2
|
||||
_optimization_level=-O3
|
||||
append_var LDFLAGS "-O3"
|
||||
fi
|
||||
|
||||
# activate emscripten-ports
|
||||
if test "$_freetype2" != no; then
|
||||
append_var LDFLAGS "-s USE_FREETYPE=1 -s SUPPORT_LONGJMP=1" # freetype requires setjmp
|
||||
# neither pkg-config nor freetype-config work, so we setup freetype manually
|
||||
_freetypepath="$EMSCRIPTEN/cache/ports-builds/freetype/"
|
||||
FREETYPE2_CFLAGS="-I$_freetypepath/include" # there were link errors / missing symbols without this
|
||||
_freetype_found="true"
|
||||
else
|
||||
#use link time optimization to further reduce exe size (this can't be used with setjmp whcih freetype requires)
|
||||
# TODO: Figure out why this is a conflict and/or if freetype can be built without setjmp
|
||||
append_var CXXFLAGS "-flto"
|
||||
append_var LDFLAGS "-flto"
|
||||
fi
|
||||
if test "$_jpeg" != no; then
|
||||
append_var LDFLAGS "-s USE_LIBJPEG=1"
|
||||
fi
|
||||
if test "$_png" != no; then
|
||||
append_var LDFLAGS "-s USE_LIBPNG=1"
|
||||
fi
|
||||
if test "$_sdl" != no; then
|
||||
append_var LDFLAGS "-s USE_SDL=2 "
|
||||
fi
|
||||
if test "$_vorbis" != no; then
|
||||
append_var LDFLAGS "-s USE_OGG=1" # vorbis needs to be linked against OGG (even if we use an external vorbis lib)
|
||||
fi
|
||||
if test "$_zlib" != no; then
|
||||
append_var LDFLAGS "-s USE_ZLIB=1"
|
||||
fi
|
||||
;;
|
||||
freebsd*)
|
||||
@ -3247,8 +3270,11 @@ if test -n "$_host"; then
|
||||
;;
|
||||
wasm*-emscripten)
|
||||
_backend="sdl"
|
||||
HOSTEXEPRE=
|
||||
HOSTEXEEXT=.html
|
||||
# Disable cloud and SDL_Net as this is handled in the browser
|
||||
_cloud=no
|
||||
_sdlnet=no
|
||||
_libcurl=no
|
||||
_curl=no
|
||||
_ar="emar cr"
|
||||
_ranlib="emranlib"
|
||||
;;
|
||||
@ -4127,12 +4153,13 @@ PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/ds/plugin.ld -mthumb -mthumb
|
||||
;;
|
||||
emscripten)
|
||||
_plugin_prefix="lib"
|
||||
_plugin_suffix=".wasm"
|
||||
_plugin_suffix=".so"
|
||||
append_var DEFINES "-DUNCACHED_PLUGINS"
|
||||
append_var CXXFLAGS "-fPIC"
|
||||
append_var LIBS ""
|
||||
_mak_plugins='
|
||||
PLUGIN_EXTRA_DEPS =
|
||||
PLUGIN_LDFLAGS += -s SIDE_MODULE=1 -s EXPORT_ALL=1
|
||||
PLUGIN_LDFLAGS += $(LDFLAGS) -s SIDE_MODULE=1 -s ASYNCIFY_IMPORTS=["*"] -s EXPORT_ALL=1
|
||||
PRE_OBJS_FLAGS := -s MAIN_MODULE=1 -s EXPORT_ALL=1
|
||||
POST_OBJS_FLAGS :=
|
||||
'
|
||||
@ -5389,7 +5416,11 @@ if test "$_opengl_mode" != none ; then
|
||||
darwin*)
|
||||
_opengl_mode=gl
|
||||
;;
|
||||
|
||||
emscripten)
|
||||
_opengl_mode=gles2
|
||||
_opengl_glad=no
|
||||
append_var LDFLAGS "-s FULL_ES2=1 -s MAX_WEBGL_VERSION=1"
|
||||
;;
|
||||
*)
|
||||
# As SDL2 supports everything, let the user choose if he wants to
|
||||
test "$_opengl_mode" = "auto" && _opengl_mode=any
|
||||
@ -5403,9 +5434,6 @@ if test "$_opengl_mode" != none ; then
|
||||
_opengl_mode=gles2
|
||||
_opengl_glad=yes
|
||||
;;
|
||||
wasm*-emscripten)
|
||||
_opengl_mode=gles2
|
||||
;;
|
||||
*)
|
||||
# On all other platforms, by default don't enable OpenGL
|
||||
test "$_opengl_mode" = "auto" && _opengl_mode=none
|
||||
@ -6185,6 +6213,22 @@ case $_host_os in
|
||||
# append_var LDFLAGS "-Wl,--retain-symbols-file,ds.syms"
|
||||
fi
|
||||
;;
|
||||
emscripten)
|
||||
append_var LDFLAGS "--pre-js ./dists/emscripten/custom_shell-pre.js --post-js ./dists/emscripten/custom_shell-post.js --shell-file ./dists/emscripten/custom_shell.html"
|
||||
# we remove some linker flags for libs which will be added by emscripten from emscripten-ports to avoid duplicate symbols
|
||||
if test "${LDFLAGS#*-s USE_LIBJPEG=1}" != "$LDFLAGS"; then
|
||||
LIBS=`echo ${LIBS} | sed 's/-ljpeg//g'`
|
||||
fi
|
||||
if test "${LDFLAGS#*-s USE_LIBPNG=1}" != "$LDFLAGS"; then
|
||||
LIBS=`echo ${LIBS} | sed 's/-lpng -lz//g'`
|
||||
fi
|
||||
if test "${LDFLAGS#*-s USE_OGG=1}" != "$LDFLAGS"; then
|
||||
LIBS=`echo ${LIBS} | sed 's/-logg//g'`
|
||||
fi
|
||||
if test "${LDFLAGS#*-s USE_ZLIB=1}" != "$LDFLAGS"; then
|
||||
LIBS=`echo ${LIBS} | sed 's/-lz//g'`
|
||||
fi
|
||||
;;
|
||||
mingw*)
|
||||
if test "$_windows_unicode" = yes; then
|
||||
append_var DEFINES "-DUNICODE -D_UNICODE"
|
||||
|
@ -1,66 +1,93 @@
|
||||
|
||||
# Building ScummVM for Webassembly
|
||||
The [Emscripten](https://emscripten.org/) target provides a script to build ScummVM as a single page browser app.
|
||||
> Emscripten is an LLVM/Clang-based compiler that compiles C and C++ source code to WebAssembly for execution in web browsers.
|
||||
|
||||
## Current State
|
||||
* All engines compile (though I didn't test all of them), including ResidualVM with WebGL acceleration and shaders.
|
||||
* Audio works and 3rd-party libraries for sound and video decoding are integrated.
|
||||
* Proof of concept integration with [BrowserFS](https://github.com/jvilk/browserfs) to download game data lazily when required and to support local savegames.
|
||||
## 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 `emscripten/build.sh`
|
||||
### Running build.sh
|
||||
|
||||
`emscripten/build.sh` needs to be run from the root of the project.
|
||||
`build.sh` needs to be run from the root of the project.
|
||||
```Shell
|
||||
./dists/emscripten/build.sh libs|configure|make|data|dist|all|clean
|
||||
./dists/emscripten/build.sh [Tasks] [Options]
|
||||
```
|
||||
It accepts a single parameter with 7 valid commands:
|
||||
* `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
|
||||
* `data`: Download some demos and set up all data require for the demo page
|
||||
* `dist`: Copy all files into a single build-emscripten folder to bring it all together
|
||||
* `all`: Run all of the above commands
|
||||
* `clean`: Remove all object files, built libs, bundled data etc
|
||||
|
||||
**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`
|
||||
* `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 work 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
|
||||
Some ideas for possible improvements:
|
||||
|
||||
### Emscripten Optimizations
|
||||
* Optimize asyncify behaviour (we only have SDL functions calling wait currently), e.g with [SDL_HINT_EMSCRIPTEN_ASYNCIFY](https://wiki.libsdl.org/SDL_HINT_EMSCRIPTEN_ASYNCIFY).
|
||||
* Specify a `ASYNCIFY_ONLY` list to to make asyncify only instrument functions in the call path as described in [emscripten.org: Asyncify](https://emscripten.org/docs/porting/asyncify.html)
|
||||
* Optimize asyncify behaviour (we only have SDL functions calling wait currently), e.g with [SDL_HINT_EMSCRIPTEN_ASYNCIFY](https://wiki.libsdl.org/SDL_HINT_EMSCRIPTEN_ASYNCIFY)
|
||||
* 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`.
|
||||
* Don't use asyncify but rewrite main loop to improve performance
|
||||
* Shrink code size or execution speed with `-Os` or `-Oz` [emcc arguments](https://emscripten.org/docs/tools_reference/emcc.html#emcc-compiler-optimization-options).
|
||||
|
||||
### Storage Integration
|
||||
* BrowserFS seems abandoned and never did a stable 2.0.0 release. Maybe there's a better way to handle storage?
|
||||
|
||||
* File loading improvements:
|
||||
* Load assets with HTTP Range request headers.
|
||||
* Load assets asynchronously (not blocking) via a worker.
|
||||
* BrowserFS seems abandoned and never did a stable 2.0.0 release. It's worth replacing it.
|
||||
* `scummvm_fs.js` is an early prototype for a custom FS which can be adopted for ScummVM specific needs, i.e.
|
||||
* Download all game assets in background once the game has started
|
||||
* Presist last game and last plugin for offline use
|
||||
* Implement support for range requests (currently not supported with `emrun` so another development server would have to be included as well)
|
||||
* Pre-load assets asynchronously (not blocking) - i.e. rest of the data of a game which has been launched
|
||||
* Loading indicators
|
||||
|
||||
* Add support for save games (and game data?) on personal cloud storage (Dropbox, Google Drive).
|
||||
|
||||
### UI Integration
|
||||
* Responsiveness: Adjust the canvas size when resizing the browser.
|
||||
|
||||
* Bug: Fullscreen mode doesn't work.
|
||||
|
||||
* Build a nice webpage around the canvas.
|
||||
* Allow hiding of console, replace buttons/checkboxes from default emscripten template.
|
||||
* Allow showing/hiding of console, replace buttons/checkboxes from default emscripten template.
|
||||
* Bonus: Adapt page padding/background color to theme (black when in game)
|
||||
|
||||
* ScummVM shouldn't be able to "close" (there's no concept for that:
|
||||
* Remove "exit" buttons from all menus.
|
||||
* Change any programmatic "exits" to cause a restart of Scummvm (or refresh of the page).
|
||||
* Automatically show console in case of exceptions
|
||||
|
||||
* Pass CLI parameters for ScummVM via URL parameters to allow for "deep-linking" to a specific game.
|
||||
|
||||
### Other Bugs + Tasks
|
||||
* Bug: Vorbis support is broken - parts seems to have been patched out so `-lvorbisfile` triggers an error during configure (and [emscripten-core/emscripten#9849](https://github.com/emscripten-core/emscripten/pull/9849) doesn't seem to fix this).
|
||||
* Bug: Going back to main menu from Grim (and other Residual Games?) messes up the render context and the UI is unusable.
|
||||
* Check all disabled features (e.g. TiMidity++) and see if they could be enabled (some might never make sense, e.g. anything requiring MIDI Hardware, Update Checking etc).
|
||||
* 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
|
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 |
6
dists/emscripten/assets/scummvm.ini
Normal file
6
dists/emscripten/assets/scummvm.ini
Normal file
@ -0,0 +1,6 @@
|
||||
[scummvm]
|
||||
gfx_mode=opengl
|
||||
pluginspath=/plugins
|
||||
grouping=company
|
||||
renderer=opengl_shaders
|
||||
|
496
dists/emscripten/assets/scummvm_fs.js
Normal file
496
dists/emscripten/assets/scummvm_fs.js
Normal file
@ -0,0 +1,496 @@
|
||||
|
||||
/*
|
||||
* ScummvmFS - A custom Emscripten filesystem for ScummVM
|
||||
*
|
||||
* This is the filesystem used to load any read-only files used by ScummVM: data, games and
|
||||
* plugins. It supports range-requests and caches data in memory to minimize latency when loading
|
||||
* data from the network.
|
||||
*
|
||||
* Adapted from Emscripten's NodeFS and BrowserFS' EmscriptenFS + XHR backend:
|
||||
* https://github.com/emscripten-core/emscripten/blob/main/src/library_nodefs.js
|
||||
* https://github.com/jvilk/BrowserFS/blob/master/src/generic/emscripten_fs.ts
|
||||
* https://github.com/jvilk/BrowserFS/blob/master/src/generic/xhr.ts
|
||||
*/
|
||||
const DIR_MODE = 16895; // 040777
|
||||
const FILE_MODE = 33206; // 100666
|
||||
const SEEK_SET = 0;
|
||||
const SEEK_CUR = 1;
|
||||
const SEEK_END = 2;
|
||||
const RANGE_REQUEST_BLOCK_SIZE = 1024 * 1024
|
||||
const ERRNO_CODES = {
|
||||
// TODO: We should get these from Emscripten - see https://github.com/emscripten-core/emscripten/issues/10061 and https://github.com/emscripten-core/emscripten/issues/14783
|
||||
EPERM: 1, // Operation not permitted
|
||||
ENOENT: 2, // No such file or directory
|
||||
EINVAL: 22 // I©nvalid argument
|
||||
};
|
||||
|
||||
|
||||
const DEBUG = false
|
||||
|
||||
|
||||
export class ScummvmFS {
|
||||
url;
|
||||
fs_index;
|
||||
stream_ops;
|
||||
node_ops;
|
||||
FS;
|
||||
constructor(_FS, _url) {
|
||||
this.FS = _FS;
|
||||
this.url = _url
|
||||
var req = new XMLHttpRequest(); // a new request
|
||||
req.open("GET", _url + "/index.json", false);
|
||||
req.send(null);
|
||||
var json_index = JSON.parse(req.responseText)
|
||||
this.fs_index = {}
|
||||
var walk_index = function (path, dir) {
|
||||
logger(path, "walk_index")
|
||||
this.fs_index[path] = null
|
||||
if (path != "/") {
|
||||
path = path + "/"
|
||||
}
|
||||
for (var key in dir) {
|
||||
if (typeof dir[key] === 'object') {
|
||||
walk_index(path + key, dir[key]) // toLowerCase to simulate a case-insensitive filesystem
|
||||
} else {
|
||||
if (key !== "index.json") {
|
||||
this.fs_index[path + key] = dir[key] // toLowerCase to simulate a case-insensitive filesystem
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}.bind(this)
|
||||
|
||||
walk_index("/", json_index)
|
||||
}
|
||||
|
||||
listDirectory(_path) {
|
||||
const path = _path.path
|
||||
var result = []
|
||||
for (var node in this.fs_index) {
|
||||
if (node.startsWith(path) && node.lastIndexOf("/") <= path.length && node !== path && node.substr(path.length + 1).length > 0 && node.charAt(path.length) == "/") {
|
||||
result.push(node.substr(path.length + 1))
|
||||
}
|
||||
}
|
||||
return { ok: true, data: result };
|
||||
}
|
||||
|
||||
// used for open
|
||||
get(_path) {
|
||||
const path = _path.path
|
||||
logger(path, "get")
|
||||
if (path in this.fs_index) {
|
||||
// if this.fs_index[path] is still a integer (hence a file), we now initialize the array to store any file data
|
||||
if (Number.isInteger(this.fs_index[path])) { // if not a number we either already have iniitalized the data or it's a folder
|
||||
const size = this.fs_index[path];
|
||||
var data;
|
||||
data = new Array(Math.ceil(size / RANGE_REQUEST_BLOCK_SIZE)) // data will be an array of blocks
|
||||
|
||||
this.fs_index[path] = { size: this.fs_index[path], data: data }
|
||||
return { ok: true, data: data, size: size };
|
||||
} else if (typeof this.fs_index[path] == "object" && this.fs_index[path] !== null) {
|
||||
return { ok: true, data: this.fs_index[path].data, size: this.fs_index[path].size }; // already initialized
|
||||
} else {
|
||||
return { ok: true, data: null }; // directory
|
||||
}
|
||||
} else {
|
||||
return { ok: false }
|
||||
}
|
||||
}
|
||||
|
||||
// used for close, mknod
|
||||
put(args) {
|
||||
const path = args.path
|
||||
logger(path, "put")
|
||||
if (!this.fs_index[path]) {
|
||||
throw new FS.ErrnoError(ERRNO_CODES.ENOENT);
|
||||
}
|
||||
|
||||
return { ok: true, data: this.fs_index[path].data };
|
||||
}
|
||||
|
||||
read(args) {
|
||||
const path = args.path;
|
||||
logger(path, "read, args:" + JSON.stringify(args))
|
||||
|
||||
if (typeof this.fs_index[path] !== "object") {
|
||||
console.error("File hasn't been opened yet")
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EPERM);
|
||||
}
|
||||
const start = args.start;
|
||||
const end = (args.end > (this.fs_index[path].size)) ? (this.fs_index[path].size) : args.end // sometimes we get requests beyond the end of the file (????)
|
||||
var first_block = Math.floor(start / RANGE_REQUEST_BLOCK_SIZE)
|
||||
var last_block = Math.floor(end / RANGE_REQUEST_BLOCK_SIZE)
|
||||
if (start > end) {
|
||||
return { ok: true, data: [] };
|
||||
}
|
||||
var alreadyLoaded = false;
|
||||
if (Array.isArray(this.fs_index[path].data)) {
|
||||
alreadyLoaded = true
|
||||
for (var idx = first_block; idx <= last_block; idx++) {
|
||||
if (this.fs_index[path].data[idx] == undefined) {
|
||||
logger(path, "block " + idx + " missing")
|
||||
alreadyLoaded = false
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let data = null;
|
||||
logger(path, "file alreadyLoaded=" + alreadyLoaded)
|
||||
if (alreadyLoaded) {
|
||||
data = new Uint8Array(end - start + 1);
|
||||
for (var block = first_block; block <= last_block; block++) {
|
||||
// TODO: we should start at start at the request start and not block start (same for end)
|
||||
for (var idx = Math.max(start, block * RANGE_REQUEST_BLOCK_SIZE); idx <= Math.min(end, block * RANGE_REQUEST_BLOCK_SIZE + this.fs_index[path].data[block].length - 1); idx++) {
|
||||
if (idx >= start && idx <= end) {
|
||||
data[idx - start] = this.fs_index[path].data[block][idx - block * RANGE_REQUEST_BLOCK_SIZE]
|
||||
}
|
||||
}
|
||||
}
|
||||
logger(path, "cache loaded ")
|
||||
} else {
|
||||
|
||||
data = this.download(path, this.url, first_block, last_block, start, end);
|
||||
}
|
||||
return { ok: true, data: data };
|
||||
}
|
||||
download(path, _url, first_block, last_block, start, end) {
|
||||
self = this;
|
||||
let data = null;
|
||||
const req = new XMLHttpRequest();
|
||||
const url = _url + path;
|
||||
req.open('GET', url, false);
|
||||
|
||||
let err = null;
|
||||
// On most platforms, we cannot set the responseType of synchronous downloads.
|
||||
// Classic hack to download binary data as a string.
|
||||
req.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
|
||||
// Trying to use range requests where possible
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range
|
||||
var range_start = first_block * RANGE_REQUEST_BLOCK_SIZE
|
||||
var range_end = Math.min((last_block + 1) * RANGE_REQUEST_BLOCK_SIZE, this.fs_index[path].size) - 1
|
||||
if (this.fs_index[path].size > (range_start - range_end + 1)) {
|
||||
req.setRequestHeader('Range', 'bytes=' + range_start + '-' + range_end);
|
||||
}
|
||||
|
||||
if (this.fs_index[path].data === null) {
|
||||
this.fs_index[path].data = new Array(Math.ceil(this.fs_index[path].size / RANGE_REQUEST_BLOCK_SIZE));
|
||||
}
|
||||
req.onreadystatechange = function (e) {
|
||||
if (req.readyState === 4) {
|
||||
var text = req.responseText;
|
||||
logger(path, "Downloaded " + text.length + " bytes");
|
||||
data = new Uint8Array(end - start + 1);
|
||||
if (req.status === 200) { // range request wasn't respected or requested
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
if (i >= start && i <= end) {
|
||||
// This will automatically throw away the upper bit of each
|
||||
// character for us.
|
||||
data[i - start] = text.charCodeAt(i);
|
||||
}
|
||||
var block = Math.floor(i / RANGE_REQUEST_BLOCK_SIZE)
|
||||
if (self.fs_index[path].data[block] === undefined) {
|
||||
self.fs_index[path].data[block] = new Uint8Array(RANGE_REQUEST_BLOCK_SIZE)
|
||||
}
|
||||
self.fs_index[path].data[block][i - block * RANGE_REQUEST_BLOCK_SIZE] = text.charCodeAt(i)
|
||||
}
|
||||
logger(path, "Downloaded [full download]");
|
||||
} else if (req.status === 206) { // partial response to range request
|
||||
var start_offset = start - range_start
|
||||
var end_offset = range_end - end
|
||||
|
||||
logger(path, "First block: " + first_block + " last block: " + last_block + "Text length: " + text.length)
|
||||
var char_length = Math.round((range_end - range_start + 1) / text.length);
|
||||
if (char_length == 2 && text.length == (range_end - range_start + 1) / 2 - 1) {
|
||||
// The above hack to get binary data as text breaks if the first two bytes of the range are U+FEFF which is a BOM
|
||||
// for UTF16 and the browsers convert the data into a UTF16 string. I initially tied to fix this by breaking up
|
||||
// the UTF16 characters into 2 bytes and prepend the stripped BOM again, but it turned out that there were other
|
||||
// issues how browsers handle UTF16 (e.g. 0xDFC3, 0xDFAD, 0xDFFB, 0xDF5B all somehow getting converted to 0xFFFD
|
||||
// - i.e. "REPLACEMENT CHARACTER") so this now just reruns shifts the start of the download.
|
||||
// That's wasting some data, but it's a rare enough occurance
|
||||
//
|
||||
// TODO: The only proper fix for this is to implement a asynchronous filesystem for emscripten. Something which currently
|
||||
// isn't possible
|
||||
data = self.download(path, _url, first_block - 1, last_block, start, end)
|
||||
} else if (char_length == 1) {
|
||||
|
||||
for (let i = 0; i < (range_end - range_start + 1); i++) {
|
||||
var block = Math.floor((range_start + i) / RANGE_REQUEST_BLOCK_SIZE)
|
||||
if (self.fs_index[path].data[block] === undefined) {
|
||||
self.fs_index[path].data[block] = new Uint8Array(RANGE_REQUEST_BLOCK_SIZE)
|
||||
}
|
||||
var block_pos = (range_start + i) - (block * RANGE_REQUEST_BLOCK_SIZE)
|
||||
// This will automatically throw away the upper bit of each
|
||||
// character for us.
|
||||
self.fs_index[path].data[block][block_pos] = text.charCodeAt(i)
|
||||
|
||||
|
||||
|
||||
}
|
||||
logger(path, "First block length: " + self.fs_index[path].data[block] + " last block length: " + self.fs_index[path].data[block] + "Text length: " + text.length)
|
||||
for (var block = first_block; block <= last_block; block++) {
|
||||
// TODO: we should start at start at the request start and not block start (same for end)
|
||||
for (var idx = Math.max(start, block * RANGE_REQUEST_BLOCK_SIZE); idx <= Math.min(end, block * RANGE_REQUEST_BLOCK_SIZE + self.fs_index[path].data[block].length - 1); idx++) {
|
||||
if (idx >= start && idx <= end) {
|
||||
data[idx - start] = self.fs_index[path].data[block][idx - block * RANGE_REQUEST_BLOCK_SIZE]
|
||||
}
|
||||
}
|
||||
}
|
||||
logger(path, "Downloaded [range request]");
|
||||
}
|
||||
} else {
|
||||
console.error(req);
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
|
||||
}
|
||||
}
|
||||
};
|
||||
req.send();
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
mount(mount) {
|
||||
return this.createNode(null, "/", DIR_MODE, 0);
|
||||
}
|
||||
|
||||
createNode(parent, name, mode, size) {
|
||||
logger(name, "createNode")
|
||||
if (!this.FS.isDir(mode) && !this.FS.isFile(mode)) {
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
|
||||
}
|
||||
let node = this.FS.createNode(parent, name, mode);
|
||||
node.node_ops = this.node_ops;
|
||||
node.stream_ops = this.stream_ops;
|
||||
node.size = size
|
||||
return node;
|
||||
}
|
||||
|
||||
convertResult(result) {
|
||||
if (result.ok) {
|
||||
return result.data;
|
||||
}
|
||||
else {
|
||||
let error;
|
||||
if (result.status === 404) {
|
||||
error = new FS.ErrnoError(ERRNO_CODES.ENOENT);
|
||||
}
|
||||
else {
|
||||
error = new FS.ErrnoError(ERRNO_CODES.EPERM);
|
||||
}
|
||||
error.cause = result.error;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
node_ops = {
|
||||
getattr: (node) => {
|
||||
return {
|
||||
dev: 1,
|
||||
ino: node.id,
|
||||
mode: node.mode,
|
||||
nlink: 1,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
rdev: undefined,
|
||||
size: node.size,
|
||||
atime: new Date(node.timestamp),
|
||||
mtime: new Date(node.timestamp),
|
||||
ctime: new Date(node.timestamp),
|
||||
blksize: 4096,
|
||||
blocks: 0,
|
||||
};
|
||||
},
|
||||
setattr: (node, attr) => {
|
||||
// Doesn't really do anything
|
||||
if (attr.mode !== undefined) {
|
||||
node.mode = attr.mode;
|
||||
}
|
||||
if (attr.timestamp !== undefined) {
|
||||
node.timestamp = attr.timestamp;
|
||||
}
|
||||
},
|
||||
lookup: (parent, name) => {
|
||||
logger(name, "lookup ")
|
||||
if (parent instanceof FS.FSStream) { //sometimes we get a stream instead of a node
|
||||
parent = parent.node;
|
||||
}
|
||||
const path = realPath(parent, name);
|
||||
const result = this.get({ path });
|
||||
if (!result.ok) {
|
||||
// I wish Javascript had inner exceptions
|
||||
throw new FS.ErrnoError(ERRNO_CODES.ENOENT);
|
||||
}
|
||||
return this.createNode(parent, name, result.data === null ? DIR_MODE : FILE_MODE, result.data ? result.size : null);
|
||||
},
|
||||
mknod: (parent, name, mode, dev) => {
|
||||
logger(name, "mknod ")
|
||||
const node = this.createNode(parent, name, mode, 0);
|
||||
const path = realPath(node);
|
||||
if (this.FS.isDir(node.mode)) {
|
||||
this.convertResult(this.put({ path, value: null }));
|
||||
}
|
||||
else {
|
||||
this.convertResult(this.put({ path, value: "" }));
|
||||
}
|
||||
return node;
|
||||
},
|
||||
|
||||
rename: (oldNode, newDir, newName) => {
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EPERM);
|
||||
const oldPath = realPath(oldNode);
|
||||
const newPath = realPath(newDir, newName);
|
||||
this.convertResult(this.move({ path: oldPath, newPath: newPath }));
|
||||
oldNode.name = newName;
|
||||
},
|
||||
|
||||
unlink: (parent, name) => {
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EPERM);
|
||||
const path = realPath(parent, name);
|
||||
this.convertResult(this.delete({ path }));
|
||||
},
|
||||
|
||||
rmdir: (parent, name) => {
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EPERM);
|
||||
const path = realPath(parent, name);
|
||||
this.convertResult(this.delete({ path }));
|
||||
},
|
||||
|
||||
readdir: (node) => {
|
||||
const path = realPath(node);
|
||||
let result = this.convertResult(this.listDirectory({ path }));
|
||||
if (!result.includes(".")) {
|
||||
result.push(".");
|
||||
}
|
||||
if (!result.includes("..")) {
|
||||
result.push("..");
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
symlink: (parent, newName, oldPath) => {
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EPERM);
|
||||
},
|
||||
|
||||
readlink: (node) => {
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EPERM);
|
||||
}
|
||||
}
|
||||
|
||||
stream_ops = {
|
||||
open: (stream) => {
|
||||
logger(stream.path, "Open stream ")
|
||||
const path = realPath(stream.node);
|
||||
if (FS.isFile(stream.node.mode)) {
|
||||
const result = this.get({ path });
|
||||
if (result.data === null || result.data === undefined) {
|
||||
return;
|
||||
}
|
||||
stream.fileData = result.data;
|
||||
stream.fileSize = result.size;
|
||||
}
|
||||
},
|
||||
|
||||
close: (stream) => {
|
||||
logger(stream.path, "close stream ")
|
||||
const path = realPath(stream.node);
|
||||
if (FS.isFile(stream.node.mode) && stream.fileData) {
|
||||
const fileData = stream.fileData
|
||||
// TODO: Track open/closed files differently so we can warn but don't lose the cached data
|
||||
//stream.fileData = undefined;
|
||||
this.convertResult(this.put({ path, value: fileData }));
|
||||
}
|
||||
},
|
||||
|
||||
read: (stream, buffer, offset, length, position) => {
|
||||
if (!position) {
|
||||
position = stream.position
|
||||
}
|
||||
// logger(stream.path, "read stream - offset:" + offset + " length:" + length + " position:" + position)
|
||||
const path = realPath(stream.node);
|
||||
var _a, _b;
|
||||
if (length <= 0)
|
||||
return 0;
|
||||
|
||||
var size = length
|
||||
if (typeof stream.fileData === 'object' && stream.fileSize < position + length) {
|
||||
size = stream.fileSize - position
|
||||
}
|
||||
|
||||
// logger(stream.path, "Length, Position " + length + "," + position)
|
||||
// logger(stream.path, "Size " + size)
|
||||
// logger(stream.path, "stream.fileSize " + stream.fileSize)
|
||||
if (size < 0) {
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
|
||||
}
|
||||
if (size > 0) {
|
||||
var fileData = this.convertResult(this.read({ path: path, start: position, end: position + size - 1 }));
|
||||
logger(stream.path, "fileData (start: " + (position) + " end: " + (position + size - 1).toString() + " (length: " + fileData.length + ")")
|
||||
|
||||
if (DEBUG) {
|
||||
logger(stream.path, Uint8Array2hex(fileData))
|
||||
}
|
||||
buffer.set(fileData, offset);
|
||||
}
|
||||
// buffer.set(stream.fileData.subarray(position, position + size), offset);
|
||||
|
||||
return size;
|
||||
},
|
||||
|
||||
write: (stream, buffer, offset, length, position) => {
|
||||
// this FS actually can't write
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EPERM);
|
||||
},
|
||||
|
||||
llseek: (stream, offset, whence) => {
|
||||
let position = offset; // SEEK_SET
|
||||
if (whence === SEEK_CUR) {
|
||||
position += stream.position;
|
||||
}
|
||||
else if (whence === SEEK_END) {
|
||||
if (this.FS.isFile(stream.node.mode)) {
|
||||
position += stream.fileSize;
|
||||
}
|
||||
} else if (whence !== SEEK_SET) {
|
||||
console.error("Illegal Whence: " + whence)
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
|
||||
}
|
||||
if (position < 0) {
|
||||
console.error("CRITICAL: Position < 0")
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
|
||||
}
|
||||
stream.position = position
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function realPath(node, fileName) {
|
||||
const parts = [];
|
||||
while (node.parent !== node) {
|
||||
parts.push(node.name);
|
||||
node = node.parent;
|
||||
}
|
||||
parts.push(node.mount.opts.root);
|
||||
parts.reverse();
|
||||
if (fileName !== undefined && fileName !== null) {
|
||||
parts.push(fileName);
|
||||
}
|
||||
return parts.join("/");
|
||||
}
|
||||
|
||||
|
||||
function logger(path, message) {
|
||||
if (DEBUG) {
|
||||
console.log(path + ": " + message)
|
||||
}
|
||||
}
|
||||
function Uint8Array2hex(byteArray) {
|
||||
return Array.prototype.map.call(byteArray, function (byte) {
|
||||
return ('0' + (byte & 0xFF).toString(16)).slice(-2).toUpperCase();
|
||||
}).join(' ');
|
||||
}
|
45
dists/emscripten/build-add_games.js
Normal file
45
dists/emscripten/build-add_games.js
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
const http = require('http');
|
||||
const fs = require('fs');
|
||||
const puppeteer = require('puppeteer');
|
||||
const static = require('node-static');
|
||||
|
||||
var file = new (static.Server)("./");
|
||||
const server = http.createServer(function (req, res) {
|
||||
file.serve(req, res);
|
||||
}).listen(8080, async () => {
|
||||
const browser = await puppeteer.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
|
||||
await page.goto('http://localhost:8080/scummvm.html#--add --path=/games --recursive');
|
||||
|
||||
await page.screenshot({ path: 'example.png' });
|
||||
const regex = /Added ([0-9]+) games/;
|
||||
page.on('console', async msg => {
|
||||
const text = msg.text()
|
||||
console.log(text)
|
||||
const match = text.match(regex);
|
||||
if (match != null && match.length > 0) {
|
||||
console.log("Detection finished, exporting ini file for " + match[1] + " detected games.")
|
||||
const localStorage = await page.evaluate(() => Object.assign({}, window.localStorage));
|
||||
|
||||
const ini_inode_id = "1b4a97d1-4ce0-417f-985c-e0f22ca21aef" // defined in custom_shell.html
|
||||
const ini_lines = Buffer.from(localStorage[ini_inode_id], 'base64').toString().split('\n');
|
||||
// GRIM games check data consistency by reading all files. That's an expensive operation over
|
||||
// the network. Since we anyway should have known good data at build time, this script disables
|
||||
// that check.
|
||||
for (var i = 0; i < ini_lines.length; i++) {
|
||||
if (ini_lines[i] == "engineid=grim") {
|
||||
ini_lines[i] = "check_gamedata=false\n" + ini_lines[i]
|
||||
}
|
||||
}
|
||||
fs.writeFileSync("scummvm.ini", ini_lines.join('\n'));
|
||||
browser.close();
|
||||
server.close();
|
||||
|
||||
console.log('Done');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
177
dists/emscripten/build-download_games.js
Normal file
177
dists/emscripten/build-download_games.js
Normal file
@ -0,0 +1,177 @@
|
||||
const request = require('request');
|
||||
const fs = require('fs');
|
||||
const fsprocess = require('process');
|
||||
const { url } = require('inspector');
|
||||
|
||||
process.on('uncaughtException', (err, origin) => {
|
||||
console.error(err)
|
||||
console.error(origin)
|
||||
process.exitCode = 2
|
||||
})
|
||||
const args_games = process.argv.slice(2);
|
||||
|
||||
/*
|
||||
Copied from https://github.com/scummvm/scummvm-web/blob/master/include/DataUtils.php
|
||||
*/
|
||||
const SHEET_URL = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQamumX0p-DYQa5Umi3RxX-pHM6RZhAj1qvUP0jTmaqutN9FwzyriRSXlO9rq6kR60pGIuPvCDzZL3s/pub?output=tsv';
|
||||
const SHEET_IDS = {
|
||||
'platforms': '1061029686',
|
||||
'compatibility': '854570757',
|
||||
'games': '1946612063',
|
||||
'engines': '0',
|
||||
'companies': '226191984',
|
||||
'versions': '1225902887',
|
||||
'game_demos': '713475305',
|
||||
'series': '1095671818',
|
||||
'screenshots': '1985243204',
|
||||
'scummvm_downloads': '1057392663',
|
||||
'game_downloads': '1287892109',
|
||||
'director_demos': '1256563740',
|
||||
}
|
||||
|
||||
// Small Helper function as having followRedirect:true sometimes lead to ECONNRESET errors
|
||||
function getGoogleSheet(url, callback) {
|
||||
request({
|
||||
url: url,
|
||||
followRedirect: function (response) {
|
||||
return false
|
||||
}
|
||||
}, function (error, response, body) {
|
||||
if (response.headers && response.headers.location) {
|
||||
request({
|
||||
url: response.headers.location,
|
||||
followRedirect: false
|
||||
}, callback);
|
||||
} else {
|
||||
callback(error, response, body)
|
||||
}
|
||||
});
|
||||
}
|
||||
function parseTSV(text) {
|
||||
const lines = text.split("\r\n")
|
||||
const headers = lines[0].split("\t")
|
||||
var ret = []
|
||||
for (var i = 1; i < lines.length; i++) {
|
||||
ret[i - 1] = {}
|
||||
lines[i].split("\t").forEach((value, col) => ret[i - 1][headers[col]] = value)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var games = {}
|
||||
// Get Freeware Games
|
||||
function get_freeware_games() {
|
||||
console.error("download_games.js: Fetching list of freeware games")
|
||||
return new Promise((resolve, reject) => {
|
||||
var url = SHEET_URL + "&gid=" + SHEET_IDS['game_downloads'];
|
||||
getGoogleSheet(url, (error, response, body) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
return
|
||||
}
|
||||
parseTSV(body).forEach((downloads) => {
|
||||
if (downloads['category'] == "games" && !(downloads['game_id'] in games)) {
|
||||
games[downloads['game_id']] = "/frs/extras/" + downloads['url']
|
||||
}
|
||||
filename = downloads['url'].substring(downloads['url'].lastIndexOf("/"))
|
||||
games[downloads['game_id'] + filename] = "/frs/extras/" + downloads['url']
|
||||
})
|
||||
resolve()
|
||||
})
|
||||
});
|
||||
}
|
||||
// Get Demos Games
|
||||
function get_demos() {
|
||||
console.error("download_games.js: Fetching list of game demos")
|
||||
return new Promise((resolve, reject) => {
|
||||
var url = SHEET_URL + "&gid=" + SHEET_IDS['game_demos'];
|
||||
getGoogleSheet(url, (error, response, body) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
return
|
||||
}
|
||||
parseTSV(body).forEach((downloads) => {
|
||||
if (!(downloads['id'] in games)) {
|
||||
games[downloads['id']] = downloads['url']
|
||||
}
|
||||
filename = downloads['url'].substring(downloads['url'].lastIndexOf("/"))
|
||||
games[downloads['id'] + filename] = downloads['url']
|
||||
})
|
||||
resolve()
|
||||
})
|
||||
});
|
||||
}
|
||||
// Get Director Demos
|
||||
function get_director_demos() {
|
||||
console.error("download_games.js: Fetching list of director demos")
|
||||
return new Promise((resolve, reject) => {
|
||||
var url = SHEET_URL + "&gid=" + SHEET_IDS['director_demos'];
|
||||
getGoogleSheet(url, (error, response, body) => {
|
||||
if (error || body == undefined) {
|
||||
reject(error)
|
||||
return
|
||||
}
|
||||
parseTSV(body).forEach((downloads) => {
|
||||
if (!(downloads['id'] in games)) {
|
||||
games[downloads['id']] = downloads['url']
|
||||
}
|
||||
filename = downloads['url'].substring(downloads['url'].lastIndexOf("/"))
|
||||
games[downloads['id'] + filename] = downloads['url']
|
||||
});
|
||||
resolve()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Download a file
|
||||
var download_file = function (uri, filename) {
|
||||
// TODO: Rewrite as promise to serialize this (easier to return status updates)
|
||||
return request.get(uri).on('error', function (err) {
|
||||
throw err
|
||||
}).on('response', function (response) {
|
||||
if (response.statusCode == 200) {
|
||||
console.error("download_games.js: Downloading " + uri)
|
||||
} else {
|
||||
console.error(response)
|
||||
throw new Error(response.statusCode)
|
||||
}
|
||||
}).pipe(fs.createWriteStream(filename))
|
||||
|
||||
}
|
||||
|
||||
const download_all_games = async (gameIds) => {
|
||||
for (var gameId of gameIds) {
|
||||
if (gameId.startsWith("http")) {
|
||||
var url = gameId
|
||||
var filename = url.substring(url.lastIndexOf("/") + 1)
|
||||
|
||||
console.log(filename)
|
||||
if (!fs.existsSync(filename)) {
|
||||
await download_file(url, filename)
|
||||
}
|
||||
} else if (!(gameId in games)) {
|
||||
console.error("download_games.js: GameID " + gameId + " not known")
|
||||
process.exit(1)
|
||||
} else {
|
||||
var url = "https://downloads.scummvm.org" + games[gameId]
|
||||
if (gameId.includes("/")) {
|
||||
gameId = gameId.substring(0, gameId.lastIndexOf("/"))
|
||||
}
|
||||
var filename = url.substring(url.lastIndexOf("/") + 1)
|
||||
if (!filename.startsWith(gameId)) { filename = gameId + "-" + filename }
|
||||
console.log(filename)
|
||||
if (!fs.existsSync(filename)) {
|
||||
await download_file(url, filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// start everything
|
||||
get_freeware_games()
|
||||
.then(get_demos)
|
||||
.then(get_director_demos)
|
||||
.then(() => {
|
||||
download_all_games(args_games)
|
||||
|
||||
});
|
59
dists/emscripten/build-make_http_index.js
Normal file
59
dists/emscripten/build-make_http_index.js
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Based on https://github.com/jvilk/BrowserFS/blob/master/scripts/make_http_index.ts
|
||||
* Copyright (c) 2013, 2014, 2015, 2016, 2017 John Vilk and other BrowserFS contributors.
|
||||
* MIT License https://github.com/jvilk/BrowserFS/blob/master/LICENSE
|
||||
*/
|
||||
"use strict";
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const symLinks = {};
|
||||
const ignoreFiles = ['.git', 'node_modules', 'bower_components', 'build', 'index.json'];
|
||||
function rdSync(dpath, tree, name) {
|
||||
const files = fs.readdirSync(dpath);
|
||||
files.forEach((file) => {
|
||||
// ignore non-essential directories / files
|
||||
if (ignoreFiles.indexOf(file) !== -1 || file[0] === '.') {
|
||||
return;
|
||||
}
|
||||
const fpath = `${dpath}/${file}`;
|
||||
try {
|
||||
// Avoid infinite loops.
|
||||
const lstat = fs.lstatSync(fpath);
|
||||
if (lstat.isSymbolicLink()) {
|
||||
if (!symLinks[lstat.dev]) {
|
||||
symLinks[lstat.dev] = {};
|
||||
}
|
||||
// Ignore if we've seen it before
|
||||
if (symLinks[lstat.dev][lstat.ino]) {
|
||||
return;
|
||||
}
|
||||
symLinks[lstat.dev][lstat.ino] = true;
|
||||
}
|
||||
const fstat = fs.statSync(fpath);
|
||||
if (fstat.isDirectory()) {
|
||||
const child = tree[file] = {};
|
||||
rdSync(fpath, child, file);
|
||||
}
|
||||
else {
|
||||
tree[file] = fstat.size;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
// Ignore and move on.
|
||||
}
|
||||
});
|
||||
return tree;
|
||||
}
|
||||
const fsListing = JSON.stringify(rdSync(process.cwd(), {}, '/'));
|
||||
if (process.argv.length === 3) {
|
||||
const fname = process.argv[2];
|
||||
let parent = path.dirname(fname);
|
||||
while (!fs.existsSync(parent)) {
|
||||
fs.mkdirSync(parent);
|
||||
parent = path.dirname(parent);
|
||||
}
|
||||
fs.writeFileSync(fname, fsListing, { encoding: 'utf8' });
|
||||
}
|
||||
else {
|
||||
console.log(fsListing);
|
||||
}
|
@ -1,206 +1,357 @@
|
||||
#!/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/>.
|
||||
#
|
||||
|
||||
# print commands
|
||||
set -o xtrace
|
||||
# exit when any command fails
|
||||
set -e
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "$0: exactly 1 arguments expected: configure, make, data, all"
|
||||
exit 3
|
||||
if [[ "$_verbose" = true ]]; then
|
||||
set -o xtrace
|
||||
fi
|
||||
|
||||
EMSDK_VERSION="2.0.26"
|
||||
# exit when any command fails
|
||||
set -e
|
||||
|
||||
EMSDK_VERSION="3.1.8"
|
||||
ROOT_FOLDER=$(pwd)
|
||||
DIST_FOLDER="$ROOT_FOLDER/dists/emscripten"
|
||||
LIBS_FOLDER="$DIST_FOLDER/libs"
|
||||
if [[ ! -d "$DIST_FOLDER" ]]; then
|
||||
echo "/dists/emscripten/ not found. Please make sure to run this script from the root of the project - ./dists/emscripten/build.sh "
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$1" =~ ^(clean)$ ]]; then
|
||||
make clean
|
||||
make distclean
|
||||
rm -rf ./dists/emscripten/libs/build
|
||||
rm -rf ./dists/emscripten/libs/*/
|
||||
rm -rf ./dists/emscripten/emsdk*/
|
||||
rm scummvm.debug.wasm
|
||||
find . -name "*.o"
|
||||
find . -name "*.a"
|
||||
find . -name "*.wasm"
|
||||
exit 0
|
||||
TASKS=()
|
||||
CONFIGURE_ARGS=()
|
||||
_bundle_games=()
|
||||
_verbose=false
|
||||
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
|
||||
--bundle-games= comma-separated list of demos and freeware games to bundle.
|
||||
-v, --verbose print all commands run by the script
|
||||
--* all other options are passed on to the configure script
|
||||
"
|
||||
|
||||
# parse inputs
|
||||
for i in "$@"; do
|
||||
case $i in
|
||||
--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
|
||||
|
||||
# Activate Emscripten
|
||||
if [[ ! -d "$DIST_FOLDER/emsdk-$EMSDK_VERSION" ]]; then
|
||||
echo "$DIST_FOLDER/emsdk-$EMSDK_VERSION not found. Installing Emscripten"
|
||||
cd "$DIST_FOLDER"
|
||||
wget -nc --content-disposition "https://github.com/emscripten-core/emsdk/archive/refs/tags/${EMSDK_VERSION}.tar.gz"
|
||||
tar xzvf "emsdk-${EMSDK_VERSION}.tar.gz"
|
||||
cd "emsdk-${EMSDK_VERSION}"
|
||||
./emsdk install ${EMSDK_VERSION}
|
||||
./emsdk activate ${EMSDK_VERSION}
|
||||
#################################
|
||||
# Setup Toolchain
|
||||
#################################
|
||||
|
||||
if [[ "setup" =~ $(echo ^\(${TASKS}\)$) || "build" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
|
||||
# Activate 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 "https://github.com/emscripten-core/emsdk/archive/refs/tags/${EMSDK_VERSION}.tar.gz"
|
||||
tar -xf "emsdk-${EMSDK_VERSION}.tar.gz"
|
||||
fi
|
||||
cd "$DIST_FOLDER/emsdk-${EMSDK_VERSION}"
|
||||
./emsdk install ${EMSCRIPTEN_VERSION}
|
||||
# We currently require a few patches for unreleased changes in SDL2 and Emscripten
|
||||
if [[ "$EMSCRIPTEN_VERSION" == "3.1.8" ]]; then
|
||||
cd upstream/emscripten
|
||||
# until https://github.com/emscripten-core/emscripten/pull/15893 gets merged and released, we need to manually patch it
|
||||
patch -p1 --verbose <"$DIST_FOLDER/emscripten-15893.patch"
|
||||
# some additional fixes on top of 15893:
|
||||
patch -p1 --verbose <"$DIST_FOLDER/emscripten-15893-fix.patch"
|
||||
|
||||
# until https://github.com/emscripten-core/emscripten/pull/16559 gets merged and released, we need to manually patch it
|
||||
patch -p1 --verbose <"$DIST_FOLDER/emscripten-16559.patch"
|
||||
# until https://github.com/emscripten-core/emscripten/pull/16687 gets merged and released, we need to manually patch it
|
||||
patch -p1 --verbose <"$DIST_FOLDER/emscripten-16687.patch"
|
||||
fi
|
||||
|
||||
cd "$DIST_FOLDER/emsdk-${EMSDK_VERSION}"
|
||||
./emsdk activate ${EMSCRIPTEN_VERSION}
|
||||
|
||||
# install some required npm packages
|
||||
source "$DIST_FOLDER/emsdk-$EMSDK_VERSION/emsdk_env.sh"
|
||||
EMSDK_NPM=$(dirname $EMSDK_NODE)/npm
|
||||
export NODE_PATH=$(dirname $EMSDK_NODE)/../lib/node_modules/
|
||||
"$EMSDK_NPM" -g install "puppeteer@13.5.1"
|
||||
"$EMSDK_NPM" -g install "request@2.88.2"
|
||||
"$EMSDK_NPM" -g install "node-static@0.7.11"
|
||||
fi
|
||||
fi
|
||||
source "$DIST_FOLDER/emsdk-$EMSDK_VERSION/emsdk_env.sh"
|
||||
|
||||
# Download + Install Libraries
|
||||
mkdir -p "$LIBS_FOLDER"
|
||||
if [[ ! -d "$LIBS_FOLDER/build" ]]; then
|
||||
echo "$LIBS_FOLDER/build/ not found. Building plugins..."
|
||||
echo "build libtheora-1.1.1"
|
||||
cd "$LIBS_FOLDER"
|
||||
pwd
|
||||
wget -nc "https://downloads.xiph.org/releases/theora/libtheora-1.1.1.tar.xz"
|
||||
tar -xf libtheora-1.1.1.tar.xz
|
||||
cd "./libtheora-1.1.1/"
|
||||
CFLAGS="-fPIC -s USE_OGG=1 -s USE_VORBIS=1 " emconfigure ./configure --host=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/" --disable-asm
|
||||
emmake make -j 3
|
||||
emmake make install
|
||||
|
||||
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 "./faad2-2.8.8/"
|
||||
CFLAGS="-fPIC" emconfigure ./configure --host=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/"
|
||||
emmake make
|
||||
emmake make install
|
||||
|
||||
echo "building libmad-0.15.1b"
|
||||
cd "$LIBS_FOLDER"
|
||||
# libmad needs patching: https://stackoverflow.com/questions/14015747/gccs-fforce-mem-option
|
||||
wget -nc "http://www.linuxfromscratch.org/patches/blfs/svn/libmad-0.15.1b-fixes-1.patch"
|
||||
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/"
|
||||
patch -Np1 -i ../libmad-0.15.1b-fixes-1.patch
|
||||
emconfigure ./configure --host=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/" --with-pic --enable-fpm=no
|
||||
emmake make
|
||||
emmake make install
|
||||
|
||||
echo "building libmpeg2-0.5.1"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "http://libmpeg2.sourceforge.net/files/libmpeg2-0.5.1.tar.gz"
|
||||
tar -xf libmpeg2-0.5.1.tar.gz
|
||||
cd "$LIBS_FOLDER/libmpeg2-0.5.1/"
|
||||
|
||||
CFLAGS="-fPIC" emconfigure ./configure --host=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/" --disable-sdl
|
||||
emmake make
|
||||
emmake make install
|
||||
|
||||
echo "building a52dec-0.7.4"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "https://liba52.sourceforge.io/files/a52dec-0.7.4.tar.gz"
|
||||
tar -xf a52dec-0.7.4.tar.gz
|
||||
cd "$LIBS_FOLDER/a52dec-0.7.4/"
|
||||
CFLAGS="-fPIC" emconfigure ./configure --host=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/"
|
||||
emmake make -j 3
|
||||
emmake make install
|
||||
fi
|
||||
# export node_path - so we can use all node_modules bundled with emscripten (e.g. requests)
|
||||
EMSDK_NPM=$(dirname $EMSDK_NODE)/npm
|
||||
export NODE_PATH=$(dirname $EMSDK_NODE)/../lib/node_modules/
|
||||
LIBS_FLAGS=""
|
||||
|
||||
cd "$ROOT_FOLDER"
|
||||
|
||||
## Emscripten configuration (should probably go into the configure file)
|
||||
## IMPORTANT: ASYNCIFY WITH -O0 doesnt work (presumably because the stack gets too big)
|
||||
export LDFLAGS="-O2 -s ASSERTIONS=1 -s GL_ASSERTIONS=1 -s LLD_REPORT_UNDEFINED -s INITIAL_MEMORY=33554432"
|
||||
|
||||
#debugging
|
||||
export LDFLAGS="${LDFLAGS} -g -gseparate-dwarf=scummvm.debug.wasm -s SEPARATE_DWARF_URL=\"http://localhost:8080/scummvm.debug.wasm\""
|
||||
|
||||
# linker flags (bundle JS and default assets)
|
||||
export LDFLAGS_LINKER=" --pre-js ./dists/emscripten/pre.js --post-js ./dists/emscripten/post.js --shell-file ./dists/emscripten/custom_shell.html "
|
||||
|
||||
if [[ "$1" =~ ^(configure|all)$ ]]; then
|
||||
|
||||
echo "clean, & configure"
|
||||
make clean || true
|
||||
emconfigure ./configure --enable-debug --enable-verbose-build --host=wasm32-unknown-emscripten \
|
||||
--disable-all-engines \
|
||||
--enable-engine=testbed,scumm,scumm_7_8,grim,monkey4,mohawk,myst,riven,sci32,agos2,sword2,drascula,sky,lure,queen,testbed \
|
||||
--with-theoradec-prefix="$LIBS_FOLDER/build/" \
|
||||
--with-faad-prefix="$LIBS_FOLDER/build/" \
|
||||
--with-mad-prefix="$LIBS_FOLDER/build/" \
|
||||
--with-mpeg2-prefix="$LIBS_FOLDER/build/" \
|
||||
--with-a52-prefix="$LIBS_FOLDER/build/"
|
||||
|
||||
# TODO: enable dynamic linking so we can enable more plugins
|
||||
# https://forums.scummvm.org/viewtopic.php?t=14918
|
||||
# https://github.com/emscripten-core/emscripten/wiki/Linking
|
||||
# https://freecontent.manning.com/dynamic-linking-a-crash-course/
|
||||
# https://iandouglasscott.com/2019/07/18/experimenting-with-webassembly-dynamic-linking-with-clang/
|
||||
|
||||
# HACK: the preload flags break emcc during configure as emcc enables NODERAWFS when run as part of configure
|
||||
# which doesn't support preloading assets, so we have to manually add those after configure to the config.mk file
|
||||
echo "LDFLAGS += ${LDFLAGS_LINKER}" >>config.mk
|
||||
|
||||
# configure currently doesn't clean up all files it created
|
||||
rm scummvm-conf.*
|
||||
|
||||
#################################
|
||||
# Clean
|
||||
#################################
|
||||
if [[ "clean" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
emmake make clean || true
|
||||
emmake make distclean || true
|
||||
rm -rf ./dists/emscripten/libs/build || true
|
||||
rm -rf ./dists/emscripten/libs/*/ || true
|
||||
rm -rf ./build-emscripten/ || true
|
||||
rm scummvm.debug.wasm || true
|
||||
find . -name "*.o" || true
|
||||
find . -name "*.a" || true
|
||||
find . -name "*.wasm" || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$1" =~ ^(data|all)$ ]]; then
|
||||
cd "${ROOT_FOLDER}"
|
||||
rm -rf ./build-emscripten/games/
|
||||
mkdir -p ./build-emscripten/games/
|
||||
cd dists/engine-data
|
||||
./create-testbed-data.sh
|
||||
mv testbed "${ROOT_FOLDER}/build-emscripten/games/testbed"
|
||||
#################################
|
||||
# Download + Install Libraries
|
||||
#################################
|
||||
if [[ "libs" =~ $(echo ^\(${TASKS}\)$) || "build" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
|
||||
games=true
|
||||
if [ "$games" = true ]; then
|
||||
mkdir -p ./dists/emscripten/games/
|
||||
cd "${ROOT_FOLDER}/dists/emscripten/games/"
|
||||
wget -nc https://downloads.scummvm.org/frs/demos/scumm/ft-dos-demo-en.zip
|
||||
unzip -n ft-dos-demo-en -d "${ROOT_FOLDER}/build-emscripten/games/ft-dos-demo-en/"
|
||||
wget -nc https://downloads.scummvm.org/frs/demos/mohawk/myst-win-demo-en.zip
|
||||
unzip -n myst-win-demo-en.zip -d "${ROOT_FOLDER}/build-emscripten/games/myst-win-demo-en/"
|
||||
wget -nc https://downloads.scummvm.org/frs/demos/mohawk/riven-win-demo-en.zip
|
||||
unzip -n riven-win-demo-en.zip -d "${ROOT_FOLDER}/build-emscripten/games/riven-win-demo-en/" -x DXSETUP/* -x QTWSETUP/*
|
||||
wget -nc https://downloads.scummvm.org/frs/demos/sword2/sword2-win-demo-en.zip
|
||||
unzip -n sword2-win-demo-en.zip -d "${ROOT_FOLDER}/build-emscripten/games/sword2-win-demo-en/"
|
||||
wget -nc https://downloads.scummvm.org/frs/demos/scumm/comi-win-large-demo-en.zip
|
||||
unzip -n comi-win-large-demo-en.zip -d "${ROOT_FOLDER}/build-emscripten/games/comi-win-large-demo-en/"
|
||||
wget -nc https://downloads.scummvm.org/frs/demos/grim/emi-win-demo-en.zip
|
||||
unzip -n emi-win-demo-en.zip -d "${ROOT_FOLDER}/build-emscripten/games/emi-win-demo-en/"
|
||||
wget -nc https://downloads.scummvm.org/frs/demos/grim/grim-win-demo2-en.zip
|
||||
unzip -n grim-win-demo2-en.zip -d "${ROOT_FOLDER}/build-emscripten/games/grim-win-demo2-en/"
|
||||
wget -nc https://downloads.scummvm.org/frs/demos/agos/feeble-dos-ni-demo-en.zip
|
||||
unzip -n feeble-dos-ni-demo-en.zip -d "${ROOT_FOLDER}/build-emscripten/games/feeble-dos-ni-demo-en/"
|
||||
wget -nc https://downloads.scummvm.org/frs/demos/sci/lsl7-dos-demo-en.zip
|
||||
unzip -n lsl7-dos-demo-en.zip -d "${ROOT_FOLDER}/build-emscripten/games/lsl7-dos-demo-en/"
|
||||
wget -nc https://downloads.scummvm.org/frs/demos/sci/phantasmagoria-dos-win-demo-en.zip
|
||||
unzip -n phantasmagoria-dos-win-demo-en -d "${ROOT_FOLDER}/build-emscripten/games/phantasmagoria-dos-win-demo-en/"
|
||||
wget -nc https://downloads.scummvm.org/frs/extras/Beneath%20a%20Steel%20Sky/BASS-Floppy-1.3.zip
|
||||
unzip -n BASS-Floppy-1.3.zip -d "${ROOT_FOLDER}/build-emscripten/games/bass-floppy/"
|
||||
wget -nc https://downloads.scummvm.org/frs/extras/Drascula_%20The%20Vampire%20Strikes%20Back/drascula-1.0.zip
|
||||
unzip -n drascula-1.0.zip -d "${ROOT_FOLDER}/build-emscripten/games/drascula/"
|
||||
wget -nc https://downloads.scummvm.org/frs/extras/Drascula_%20The%20Vampire%20Strikes%20Back/drascula-audio-mp3-2.0.zip
|
||||
unzip -n drascula-audio-mp3-2.0.zip -d "${ROOT_FOLDER}/build-emscripten/games/drascula/"
|
||||
wget -nc https://downloads.scummvm.org/frs/extras/Flight%20of%20the%20Amazon%20Queen/FOTAQ_Floppy.zip
|
||||
unzip -n FOTAQ_Floppy.zip -d "${ROOT_FOLDER}/build-emscripten/games/fotaq-floppy/"
|
||||
wget -nc https://downloads.scummvm.org/frs/extras/Lure%20of%20the%20Temptress/lure-1.1.zip
|
||||
unzip -n lure-1.1.zip -d "${ROOT_FOLDER}/build-emscripten/games/lure/"
|
||||
if [[ ! -d "$LIBS_FOLDER/build" ]]; then
|
||||
mkdir -p "$LIBS_FOLDER/build"
|
||||
fi
|
||||
|
||||
cd "${ROOT_FOLDER}/build-emscripten/games/"
|
||||
NODE_DIR=$(dirname "$EMSDK_NODE")
|
||||
"$NODE_DIR/npx" -p browserfs make_xhrfs_index >index.json
|
||||
# Emscripten has an official port for vorbis, but it doesn't properly link vorbisfile https://github.com/emscripten-core/emscripten/pull/14005
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libvorbis.a" ]]; then
|
||||
echo "building libvorbis-1.3.7"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "https://downloads.xiph.org/releases/vorbis/libvorbis-1.3.7.tar.gz"
|
||||
tar -xf libvorbis-1.3.7.tar.gz
|
||||
cd "$LIBS_FOLDER/libvorbis-1.3.7"
|
||||
CFLAGS="-fPIC -s USE_OGG=1" emconfigure ./configure --host=wasm32-unknown-none --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/"
|
||||
emmake make -j 3
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-vorbis-prefix=$LIBS_FOLDER/build/"
|
||||
|
||||
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 "./libtheora-1.1.1/"
|
||||
CFLAGS="-fPIC -s USE_OGG=1" emconfigure ./configure --host=wasm32-unknown-none --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/" --disable-asm
|
||||
emmake make -j 3
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-theoradec-prefix=$LIBS_FOLDER/build/"
|
||||
|
||||
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 "./faad2-2.8.8/"
|
||||
CFLAGS="-fPIC" emconfigure ./configure --host=wasm32-unknown-none --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/"
|
||||
emmake make
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-faad-prefix=$LIBS_FOLDER/build/"
|
||||
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libmad.a" ]]; then
|
||||
echo "building libmad-0.15.1b"
|
||||
cd "$LIBS_FOLDER"
|
||||
# libmad needs patching: https://stackoverflow.com/questions/14015747/gccs-fforce-mem-option
|
||||
wget -nc "http://www.linuxfromscratch.org/patches/blfs/svn/libmad-0.15.1b-fixes-1.patch"
|
||||
wget -nc "https://downloads.sourceforge.net/mad/libmad-0.15.1b.tar.gz"
|
||||
rm -rf "$LIBS_FOLDER/libmad-0.15.1b/"
|
||||
tar -xf libmad-0.15.1b.tar.gz
|
||||
cd "$LIBS_FOLDER/libmad-0.15.1b/"
|
||||
patch -Np1 -i ../libmad-0.15.1b-fixes-1.patch &&
|
||||
emconfigure ./configure --host=wasm32-unknown-none --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/" --with-pic --enable-fpm=no
|
||||
emmake make
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-mad-prefix=$LIBS_FOLDER/build/"
|
||||
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/libmpeg2.a" ]]; then
|
||||
echo "building libmpeg2-0.5.1"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "http://libmpeg2.sourceforge.net/files/libmpeg2-0.5.1.tar.gz"
|
||||
tar -xf libmpeg2-0.5.1.tar.gz
|
||||
cd "$LIBS_FOLDER/libmpeg2-0.5.1/"
|
||||
CFLAGS="-fPIC" emconfigure ./configure --host=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/" --disable-sdl
|
||||
emmake make
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-mpeg2-prefix=$LIBS_FOLDER/build/"
|
||||
|
||||
if [[ ! -f "$LIBS_FOLDER/build/lib/liba52.a" ]]; then
|
||||
echo "building a52dec-0.7.4"
|
||||
cd "$LIBS_FOLDER"
|
||||
wget -nc "https://liba52.sourceforge.io/files/a52dec-0.7.4.tar.gz"
|
||||
tar -xf a52dec-0.7.4.tar.gz
|
||||
cd "$LIBS_FOLDER/a52dec-0.7.4/"
|
||||
CFLAGS="-fPIC" emconfigure ./configure --host=wasm32-unknown-none --build=wasm32-unknown-none --prefix="$LIBS_FOLDER/build/"
|
||||
emmake make -j 3
|
||||
emmake make install
|
||||
fi
|
||||
LIBS_FLAGS="${LIBS_FLAGS} --with-a52-prefix=$LIBS_FOLDER/build/"
|
||||
fi
|
||||
|
||||
if [[ "$1" =~ ^(make|all)$ ]]; then
|
||||
#################################
|
||||
# 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"
|
||||
emmake make
|
||||
emmake make dist-generic
|
||||
# preload data
|
||||
"$EMSDK_PYTHON" "$EMSDK/upstream/emscripten/tools/file_packager.py" files.data --preload ./dist-generic/scummvm/data@/scummvm --use-preload-cache --js-output=files.js
|
||||
rm -rf dist-generic/
|
||||
fi
|
||||
|
||||
if [[ "$1" =~ ^(dist|all)$ ]]; then
|
||||
# The following steps copy stuff to build-emscripten:
|
||||
mkdir -p "${ROOT_FOLDER}/build-emscripten/"
|
||||
|
||||
#################################
|
||||
# Create Games & Testbed Data
|
||||
#################################
|
||||
if [[ "games" =~ $(echo ^\(${TASKS}\)$) || "build" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
cd "${ROOT_FOLDER}"
|
||||
mkdir -p build-emscripten
|
||||
mv scummvm.* build-emscripten/
|
||||
mv files.* build-emscripten/
|
||||
cp dists/emscripten/scummvm-512.png build-emscripten/
|
||||
cp dists/emscripten/scummvm-192.png build-emscripten/
|
||||
cp dists/emscripten/manifest.json build-emscripten/
|
||||
echo "Creating Games + Testbed Data"
|
||||
mkdir -p "${ROOT_FOLDER}/build-emscripten/games/"
|
||||
|
||||
if [[ "testbed" =~ $(echo ^\(${_bundle_games// /|}\)$) ]]; then
|
||||
_bundle_games="${_bundle_games//testbed/}"
|
||||
rm -rf "${ROOT_FOLDER}/build-emscripten/games/testbed"
|
||||
cd "${ROOT_FOLDER}/dists/engine-data"
|
||||
./create-testbed-data.sh
|
||||
mv testbed "${ROOT_FOLDER}/build-emscripten/games/testbed"
|
||||
fi
|
||||
|
||||
if [ -n "$_bundle_games" ]; then
|
||||
mkdir -p "${DIST_FOLDER}/games/"
|
||||
cd "${DIST_FOLDER}/games/"
|
||||
files=$("$EMSDK_NODE" --unhandled-rejections=strict --trace-warnings "$DIST_FOLDER/build-download_games.js" ${_bundle_games})
|
||||
for dir in "${ROOT_FOLDER}/build-emscripten/games/"*; do # cleanup games folder
|
||||
if [ $(basename $dir) != "testbed" ]; then
|
||||
rm -rf "$dir"
|
||||
fi
|
||||
done
|
||||
for f in $files; do # unpack into games folder
|
||||
echo "Unzipping $f ..."
|
||||
unzip -q -n "$f" -d "${ROOT_FOLDER}/build-emscripten/games/${f%.zip}"
|
||||
# some zip files have weird permissions, this fixes that:
|
||||
find "${ROOT_FOLDER}/build-emscripten/games/${f%.zip}" -type d -exec chmod 0755 {} \;
|
||||
find "${ROOT_FOLDER}/build-emscripten/games/${f%.zip}" -type f -exec chmod 0644 {} \;
|
||||
done
|
||||
fi
|
||||
cd "${ROOT_FOLDER}/build-emscripten/games/"
|
||||
"$EMSDK_NODE" "$DIST_FOLDER/build-make_http_index.js" >index.json
|
||||
fi
|
||||
|
||||
#################################
|
||||
# Bundle everything into a neat package
|
||||
#################################
|
||||
if [[ "dist" =~ $(echo ^\(${TASKS}\)$) || "build" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
cd "${ROOT_FOLDER}"
|
||||
mv "${ROOT_FOLDER}"/scummvm.* "${ROOT_FOLDER}"/build-emscripten/ || true
|
||||
|
||||
# prepare data
|
||||
if [[ -d "${ROOT_FOLDER}/dist-generic/scummvm/data" ]]; then
|
||||
echo "Bundle ScummVM + Data"
|
||||
rm -rf "${ROOT_FOLDER}/build-emscripten/data"
|
||||
mv "${ROOT_FOLDER}/dist-generic/scummvm/data" "${ROOT_FOLDER}/build-emscripten/"
|
||||
cd "${ROOT_FOLDER}/build-emscripten/data"
|
||||
"$EMSDK_NODE" "$DIST_FOLDER/build-make_http_index.js" >index.json
|
||||
rm -rf "${ROOT_FOLDER}/dist-generic/"
|
||||
fi
|
||||
|
||||
# bundle plugins
|
||||
echo "Bundle Plugins"
|
||||
mkdir -p "${ROOT_FOLDER}/build-emscripten/plugins"
|
||||
mv "${ROOT_FOLDER}/plugins/"* "${ROOT_FOLDER}/build-emscripten/plugins/" || true
|
||||
cd "${ROOT_FOLDER}/build-emscripten/plugins"
|
||||
"$EMSDK_NODE" "$DIST_FOLDER/build-make_http_index.js" >index.json
|
||||
|
||||
# add logos and other assets
|
||||
cd "${ROOT_FOLDER}"
|
||||
cp "$DIST_FOLDER/assets/"* "${ROOT_FOLDER}/build-emscripten/"
|
||||
cp "$ROOT_FOLDER/gui/themes/common-svg/logo.svg" "${ROOT_FOLDER}/build-emscripten/"
|
||||
cp "$ROOT_FOLDER/icons/scummvm.ico" "${ROOT_FOLDER}/build-emscripten/favicon.ico"
|
||||
fi
|
||||
|
||||
#################################
|
||||
# Automatically detect games and create scummvm.ini file
|
||||
#################################
|
||||
if [[ "add-games" =~ $(echo ^\(${TASKS}\)$) || "build" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
cd "${ROOT_FOLDER}"
|
||||
cp "$DIST_FOLDER/assets/scummvm.ini" "${ROOT_FOLDER}/build-emscripten/"
|
||||
cd "${ROOT_FOLDER}/build-emscripten/"
|
||||
"$EMSDK_NODE" "$DIST_FOLDER/build-add_games.js"
|
||||
fi
|
||||
|
||||
#################################
|
||||
# Run Development Server
|
||||
#################################
|
||||
if [[ "run" =~ $(echo ^\(${TASKS}\)$) ]]; then
|
||||
echo "Run ScummVM"
|
||||
cd "${ROOT_FOLDER}/build-emscripten/"
|
||||
# emrun doesn't support range requests. Once it will, we don't need node-static anymore
|
||||
# emrun --browser=chrome scummvm.html
|
||||
|
||||
EMSDK_NPX=$(dirname $EMSDK_NODE)/npx
|
||||
$EMSDK_NPX -p node-static static .
|
||||
fi
|
||||
|
17
dists/emscripten/custom_shell-pre.js
Normal file
17
dists/emscripten/custom_shell-pre.js
Normal file
@ -0,0 +1,17 @@
|
||||
/*global Module*/
|
||||
Module["arguments"] = [];
|
||||
Module["arguments"].push("--config=/local/scummvm.ini");
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API only works in secure contexts and supported browsers.
|
||||
// This disables joystick support to avoid a crash when initializing the sdl subsystem without the gamepad API being available.
|
||||
if (!navigator.getGamepads && !navigator.webkitGetGamepads) {
|
||||
Module["arguments"].push("--joystick=-1")
|
||||
}
|
||||
|
||||
// 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);
|
||||
})
|
||||
}
|
File diff suppressed because one or more lines are too long
44
dists/emscripten/emscripten-15893-fix.patch
Normal file
44
dists/emscripten/emscripten-15893-fix.patch
Normal file
@ -0,0 +1,44 @@
|
||||
From 2ea11b4734de5656aaf3285145831e2bb9ad53ae Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Christian=20Ku=CC=88ndig?= <kuendig@scandit.com>
|
||||
Date: Sun, 24 Apr 2022 18:55:51 +0200
|
||||
Subject: [PATCH] Keeping a reference to the original function in
|
||||
instrumentWasmExports and using that in _dlsym_js to pass the right method to
|
||||
addFunction.
|
||||
|
||||
---
|
||||
src/library_async.js | 3 +++
|
||||
src/library_dylink.js | 4 ++--
|
||||
2 files changed, 5 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/library_async.js b/src/library_async.js
|
||||
index 14511ba05..32ce7abd3 100644
|
||||
--- a/src/library_async.js
|
||||
+++ b/src/library_async.js
|
||||
@@ -130,6 +130,9 @@ mergeInto(LibraryManager.library, {
|
||||
}
|
||||
}
|
||||
};
|
||||
+#if MAIN_MODULE
|
||||
+ ret[x].orig = original;
|
||||
+#endif
|
||||
} else {
|
||||
ret[x] = original;
|
||||
}
|
||||
diff --git a/src/library_dylink.js b/src/library_dylink.js
|
||||
index 291c50ef2..3c70cfca7 100644
|
||||
--- a/src/library_dylink.js
|
||||
+++ b/src/library_dylink.js
|
||||
@@ -964,8 +967,8 @@ var LibraryDylink = {
|
||||
#endif
|
||||
|
||||
#if ASYNCIFY
|
||||
- if(symbol in GOT && GOT[symbol].value != 0) {
|
||||
- return GOT[symbol].value
|
||||
+ if ('orig' in result) {
|
||||
+ result = result.orig;
|
||||
}
|
||||
#endif
|
||||
// Insert the function into the wasm table. If its a direct wasm function
|
||||
--
|
||||
2.31.0
|
||||
|
386
dists/emscripten/emscripten-15893.patch
Normal file
386
dists/emscripten/emscripten-15893.patch
Normal file
@ -0,0 +1,386 @@
|
||||
From 33d2935283bdf734dff5f5a2560571480f47b6b7 Mon Sep 17 00:00:00 2001
|
||||
From: kamenokonokotan <kamenokonokotan@gmail.com>
|
||||
Date: Thu, 6 Jan 2022 14:55:48 +0900
|
||||
Subject: [PATCH 01/10] import mutable globals used in Asyncify pass
|
||||
|
||||
---
|
||||
emcc.py | 9 +++++++++
|
||||
emscripten.py | 1 +
|
||||
src/library.js | 6 ++++++
|
||||
3 files changed, 16 insertions(+)
|
||||
|
||||
diff --git a/emcc.py b/emcc.py
|
||||
index 34df446434c..e5a425d1f31 100755
|
||||
--- a/emcc.py
|
||||
+++ b/emcc.py
|
||||
@@ -554,6 +554,8 @@ def get_binaryen_passes():
|
||||
passes += ['--fpcast-emu']
|
||||
if settings.ASYNCIFY:
|
||||
passes += ['--asyncify']
|
||||
+ if settings.MAIN_MODULE or settings.SIDE_MODULE:
|
||||
+ passes += ['--pass-arg=asyncify-side-module']
|
||||
if settings.ASSERTIONS:
|
||||
passes += ['--pass-arg=asyncify-asserts']
|
||||
if settings.ASYNCIFY_ADVISE:
|
||||
@@ -1854,6 +1856,13 @@ def phase_linker_setup(options, state, newargs, user_settings):
|
||||
'__heap_base',
|
||||
'__stack_pointer',
|
||||
]
|
||||
+
|
||||
+ if settings.ASYNCIFY:
|
||||
+ settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [
|
||||
+ '__asyncify_state',
|
||||
+ '__asyncify_data'
|
||||
+ ]
|
||||
+
|
||||
# Unconditional dependency in library_dylink.js
|
||||
settings.REQUIRED_EXPORTS += ['setThrew']
|
||||
|
||||
diff --git a/emscripten.py b/emscripten.py
|
||||
index cd0c27dc2f3..b7d1bff3994 100644
|
||||
--- a/emscripten.py
|
||||
+++ b/emscripten.py
|
||||
@@ -344,6 +344,7 @@ def emscript(in_wasm, out_wasm, outfile_js, memfile):
|
||||
|
||||
if settings.ASYNCIFY:
|
||||
exports += ['asyncify_start_unwind', 'asyncify_stop_unwind', 'asyncify_start_rewind', 'asyncify_stop_rewind']
|
||||
+ metadata['globalImports'] += ['__asyncify_state', '__asyncify_data']
|
||||
|
||||
report_missing_symbols(forwarded_json['libraryFunctions'])
|
||||
|
||||
diff --git a/src/library.js b/src/library.js
|
||||
index 401e17d086a..2c4d375d52e 100644
|
||||
--- a/src/library.js
|
||||
+++ b/src/library.js
|
||||
@@ -3521,6 +3521,12 @@ LibraryManager.library = {
|
||||
__c_longjmp: "new WebAssembly.Tag({'parameters': ['{{{ POINTER_TYPE }}}']})",
|
||||
__c_longjmp_import: true,
|
||||
#endif
|
||||
+#if ASYNCIFY
|
||||
+ __asyncify_state: "new WebAssembly.Global({'value': 'i32', 'mutable': true}, 0)",
|
||||
+ __asyncify_state__import: true,
|
||||
+ __asyncify_data: "new WebAssembly.Global({'value': 'i32', 'mutable': true}, 0)",
|
||||
+ __asyncify_data__import: true,
|
||||
+#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
From 065cb3c3aef101b8b8e249e32a44843830e5fa73 Mon Sep 17 00:00:00 2001
|
||||
From: nokotan <kamenokonokotan@gmail.com>
|
||||
Date: Sun, 23 Jan 2022 21:54:03 +0900
|
||||
Subject: [PATCH 02/10] move globals metadata modification
|
||||
|
||||
---
|
||||
emscripten.py | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/emscripten.py b/emscripten.py
|
||||
index b7d1bff3994..810eeb93dda 100644
|
||||
--- a/emscripten.py
|
||||
+++ b/emscripten.py
|
||||
@@ -324,6 +324,9 @@ def emscript(in_wasm, out_wasm, outfile_js, memfile):
|
||||
if settings.INITIAL_TABLE == -1:
|
||||
settings.INITIAL_TABLE = dylink_sec.table_size + 1
|
||||
|
||||
+ if settings.ASYNCIFY:
|
||||
+ metadata['globalImports'] += ['__asyncify_state', '__asyncify_data']
|
||||
+
|
||||
glue, forwarded_data = compile_settings()
|
||||
if DEBUG:
|
||||
logger.debug(' emscript: glue took %s seconds' % (time.time() - t))
|
||||
@@ -344,7 +347,6 @@ def emscript(in_wasm, out_wasm, outfile_js, memfile):
|
||||
|
||||
if settings.ASYNCIFY:
|
||||
exports += ['asyncify_start_unwind', 'asyncify_stop_unwind', 'asyncify_start_rewind', 'asyncify_stop_rewind']
|
||||
- metadata['globalImports'] += ['__asyncify_state', '__asyncify_data']
|
||||
|
||||
report_missing_symbols(forwarded_json['libraryFunctions'])
|
||||
|
||||
|
||||
From fc1edebd9df7e1c84e0fd7d700dd80dbf5a6e83b Mon Sep 17 00:00:00 2001
|
||||
From: kamenokonokotan <kamenokonokotan@gmail.com>
|
||||
Date: Tue, 25 Jan 2022 22:00:21 +0900
|
||||
Subject: [PATCH 03/10] Remove redundant spaces
|
||||
|
||||
---
|
||||
emcc.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/emcc.py b/emcc.py
|
||||
index e5a425d1f31..2c51a6d4264 100755
|
||||
--- a/emcc.py
|
||||
+++ b/emcc.py
|
||||
@@ -1862,7 +1862,7 @@ def phase_linker_setup(options, state, newargs, user_settings):
|
||||
'__asyncify_state',
|
||||
'__asyncify_data'
|
||||
]
|
||||
-
|
||||
+
|
||||
# Unconditional dependency in library_dylink.js
|
||||
settings.REQUIRED_EXPORTS += ['setThrew']
|
||||
|
||||
|
||||
From 163609c529d31d9c6a07866ffb6f0d6a2901d8c9 Mon Sep 17 00:00:00 2001
|
||||
From: kamenokonokotan <kamenokonokotan@gmail.com>
|
||||
Date: Tue, 25 Jan 2022 22:10:36 +0900
|
||||
Subject: [PATCH 04/10] Add test_asyncify_side_module
|
||||
|
||||
---
|
||||
tests/test_core.py | 28 ++++++++++++++++++++++++++++
|
||||
1 file changed, 28 insertions(+)
|
||||
|
||||
diff --git a/tests/test_core.py b/tests/test_core.py
|
||||
index 6eeee235e5b..e004def18e6 100644
|
||||
--- a/tests/test_core.py
|
||||
+++ b/tests/test_core.py
|
||||
@@ -7903,6 +7903,34 @@ def test_asyncify_indirect_lists(self, args, should_pass):
|
||||
if should_pass:
|
||||
raise
|
||||
|
||||
+ @needs_dylink
|
||||
+ @no_memory64('TODO: asyncify for wasm64')
|
||||
+ def test_asyncify_side_module(self):
|
||||
+ self.set_setting('ASYNCIFY')
|
||||
+ self.emcc_args += ['-sASYNCIFY_IMPORTS=["_Z8my_sleepi"]']
|
||||
+ self.dylink_test(r'''
|
||||
+ #include <stdio.h>
|
||||
+ #include "header.h"
|
||||
+
|
||||
+ int main() {
|
||||
+ my_sleep(1);
|
||||
+ return 0;
|
||||
+ }
|
||||
+ ''', r'''
|
||||
+ #include <emscripten.h>
|
||||
+ #include <stdio.h>
|
||||
+ #include "header.h"
|
||||
+
|
||||
+ void my_sleep(int milli_seconds) {
|
||||
+ // put variable onto stack
|
||||
+ volatile int value = 42;
|
||||
+ printf("%d ", value);
|
||||
+ emscripten_sleep(milli_seconds);
|
||||
+ // variable on stack in side module function should be restored.
|
||||
+ printf("%d\n", value);
|
||||
+ }
|
||||
+ ''', '42 42', header='void my_sleep(int);')
|
||||
+
|
||||
@no_asan('asyncify stack operations confuse asan')
|
||||
@no_memory64('TODO: asyncify for wasm64')
|
||||
def test_emscripten_scan_registers(self):
|
||||
|
||||
From 8066f18bf46c2d694bba624e00cb6f38c11499a5 Mon Sep 17 00:00:00 2001
|
||||
From: kamenokonokotan <kamenokonokotan@gmail.com>
|
||||
Date: Wed, 26 Jan 2022 01:29:14 +0900
|
||||
Subject: [PATCH 05/10] flake8, add EXIT_RUNTIME
|
||||
|
||||
---
|
||||
tests/test_core.py | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/tests/test_core.py b/tests/test_core.py
|
||||
index e004def18e6..b30806192ab 100644
|
||||
--- a/tests/test_core.py
|
||||
+++ b/tests/test_core.py
|
||||
@@ -7907,13 +7907,14 @@ def test_asyncify_indirect_lists(self, args, should_pass):
|
||||
@no_memory64('TODO: asyncify for wasm64')
|
||||
def test_asyncify_side_module(self):
|
||||
self.set_setting('ASYNCIFY')
|
||||
+ self.set_setting('EXIT_RUNTIME', 1)
|
||||
self.emcc_args += ['-sASYNCIFY_IMPORTS=["_Z8my_sleepi"]']
|
||||
self.dylink_test(r'''
|
||||
#include <stdio.h>
|
||||
#include "header.h"
|
||||
|
||||
int main() {
|
||||
- my_sleep(1);
|
||||
+ my_sleep(1);
|
||||
return 0;
|
||||
}
|
||||
''', r'''
|
||||
|
||||
From 2f451abf5fa81f6bc08a9a671d7251f630c5ea67 Mon Sep 17 00:00:00 2001
|
||||
From: kamenokonokotan <kamenokonokotan@gmail.com>
|
||||
Date: Sun, 30 Jan 2022 02:39:01 +0900
|
||||
Subject: [PATCH 06/10] add instrumentWasmExports
|
||||
|
||||
---
|
||||
src/library_dylink.js | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/src/library_dylink.js b/src/library_dylink.js
|
||||
index e71ceb1b65b..8a0a3df9469 100644
|
||||
--- a/src/library_dylink.js
|
||||
+++ b/src/library_dylink.js
|
||||
@@ -583,6 +583,9 @@ var LibraryDylink = {
|
||||
// add new entries to functionsInTableMap
|
||||
updateTableMap(tableBase, metadata.tableSize);
|
||||
moduleExports = relocateExports(instance.exports, memoryBase);
|
||||
+#if ASYNCIFY
|
||||
+ moduleExports = Asyncify.instrumentWasmExports(moduleExports);
|
||||
+#endif
|
||||
if (!flags.allowUndefined) {
|
||||
reportUndefinedSymbols();
|
||||
}
|
||||
|
||||
From d09570de2e65ead70ab31f4a843ecde29512436c Mon Sep 17 00:00:00 2001
|
||||
From: kamenokonokotan <kamenokonokotan@gmail.com>
|
||||
Date: Sun, 30 Jan 2022 02:39:56 +0900
|
||||
Subject: [PATCH 07/10] add searched symbols in getDataRewindFunc
|
||||
|
||||
---
|
||||
src/library_async.js | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
diff --git a/src/library_async.js b/src/library_async.js
|
||||
index da09a1ae2d8..0ceb072dd37 100644
|
||||
--- a/src/library_async.js
|
||||
+++ b/src/library_async.js
|
||||
@@ -205,6 +205,11 @@ mergeInto(LibraryManager.library, {
|
||||
var id = {{{ makeGetValue('ptr', C_STRUCTS.asyncify_data_s.rewind_id, 'i32') }}};
|
||||
var name = Asyncify.callStackIdToName[id];
|
||||
var func = Module['asm'][name];
|
||||
+#if RELOCATABLE
|
||||
+ if (!func) {
|
||||
+ func = Module[asmjsMangle(name)];
|
||||
+ }
|
||||
+#endif
|
||||
return func;
|
||||
},
|
||||
|
||||
|
||||
From 3a71ca6d2d93f1d933dc43b9aa34e56086a30534 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Christian=20K=C3=BCndig?= <christian@kuendig.info>
|
||||
Date: Fri, 18 Feb 2022 18:15:53 +0100
|
||||
Subject: [PATCH 08/10] Fixing dlsym for emscripten-core/emscripten#15893
|
||||
|
||||
---
|
||||
src/library_dylink.js | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
diff --git a/src/library_dylink.js b/src/library_dylink.js
|
||||
index 8a0a3df9469..9fca8adf488 100644
|
||||
--- a/src/library_dylink.js
|
||||
+++ b/src/library_dylink.js
|
||||
@@ -965,6 +965,12 @@ var LibraryDylink = {
|
||||
#if DYLINK_DEBUG
|
||||
err('dlsym: ' + symbol + ' getting table slot for: ' + result);
|
||||
#endif
|
||||
+
|
||||
+#if ASYNCIFY
|
||||
+ if(symbol in GOT && GOT[symbol].value != 0) {
|
||||
+ return GOT[symbol].value
|
||||
+ }
|
||||
+#endif
|
||||
// Insert the function into the wasm table. If its a direct wasm function
|
||||
// the second argument will not be needed. If its a JS function we rely
|
||||
// on the `sig` attribute being set based on the `<func>__sig` specified
|
||||
|
||||
From 7e451a8df7144c6293a57c8563538d409b904fe0 Mon Sep 17 00:00:00 2001
|
||||
From: kamenokonokotan <kamenokonokotan@gmail.com>
|
||||
Date: Sun, 6 Mar 2022 00:05:00 +0900
|
||||
Subject: [PATCH 09/10] Add test case test_asyncify_dlfcn
|
||||
|
||||
---
|
||||
tests/test_core.py | 33 +++++++++++++++++++++++++++++++++
|
||||
1 file changed, 33 insertions(+)
|
||||
|
||||
diff --git a/tests/test_core.py b/tests/test_core.py
|
||||
index b30806192ab..0c978f39343 100644
|
||||
--- a/tests/test_core.py
|
||||
+++ b/tests/test_core.py
|
||||
@@ -7932,6 +7932,39 @@ def test_asyncify_side_module(self):
|
||||
}
|
||||
''', '42 42', header='void my_sleep(int);')
|
||||
|
||||
+ @needs_dylink
|
||||
+ @no_memory64('TODO: asyncify for wasm64')
|
||||
+ def test_asyncify_dlfcn(self):
|
||||
+ self.set_setting('ASYNCIFY')
|
||||
+ self.set_setting('EXIT_RUNTIME', 1)
|
||||
+ self.emcc_args += ['-sASYNCIFY_IGNORE_INDIRECT=0']
|
||||
+ self.dylink_test(r'''
|
||||
+ #include <iostream>
|
||||
+ #include <dlfcn.h>
|
||||
+
|
||||
+ typedef int (*func_t)();
|
||||
+
|
||||
+ int main(int argc, char **argv)
|
||||
+ {
|
||||
+ void *_dlHandle = dlopen("liblib.so", RTLD_NOW | RTLD_LOCAL);
|
||||
+ func_t my_func = (func_t)dlsym(_dlHandle, "side_module_run");
|
||||
+ printf("%d\n", my_func());
|
||||
+ return 0;
|
||||
+ }
|
||||
+ ''', r'''
|
||||
+ #include <iostream>
|
||||
+ #include <emscripten/emscripten.h>
|
||||
+
|
||||
+ extern "C"
|
||||
+ {
|
||||
+ int side_module_run()
|
||||
+ {
|
||||
+ emscripten_sleep(1000);
|
||||
+ return 42;
|
||||
+ }
|
||||
+ }
|
||||
+ ''', '42', need_reverse=False)
|
||||
+
|
||||
@no_asan('asyncify stack operations confuse asan')
|
||||
@no_memory64('TODO: asyncify for wasm64')
|
||||
def test_emscripten_scan_registers(self):
|
||||
|
||||
From 1b85abaab0186939a36207f2480cf836b096840f Mon Sep 17 00:00:00 2001
|
||||
From: kamenokonokotan <kamenokonokotan@gmail.com>
|
||||
Date: Wed, 6 Apr 2022 01:40:08 +0900
|
||||
Subject: [PATCH 10/10] Update test case
|
||||
|
||||
---
|
||||
tests/test_core.py | 10 +++++++---
|
||||
1 file changed, 7 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/tests/test_core.py b/tests/test_core.py
|
||||
index 0c978f39343..7009c539f12 100644
|
||||
--- a/tests/test_core.py
|
||||
+++ b/tests/test_core.py
|
||||
@@ -7914,7 +7914,9 @@ def test_asyncify_side_module(self):
|
||||
#include "header.h"
|
||||
|
||||
int main() {
|
||||
+ printf("before sleep\n");
|
||||
my_sleep(1);
|
||||
+ printf("after sleep\n");
|
||||
return 0;
|
||||
}
|
||||
''', r'''
|
||||
@@ -7925,12 +7927,12 @@ def test_asyncify_side_module(self):
|
||||
void my_sleep(int milli_seconds) {
|
||||
// put variable onto stack
|
||||
volatile int value = 42;
|
||||
- printf("%d ", value);
|
||||
+ printf("%d\n", value);
|
||||
emscripten_sleep(milli_seconds);
|
||||
// variable on stack in side module function should be restored.
|
||||
printf("%d\n", value);
|
||||
}
|
||||
- ''', '42 42', header='void my_sleep(int);')
|
||||
+ ''', 'before sleep\n42\n42\nafter sleep\n', header='void my_sleep(int);')
|
||||
|
||||
@needs_dylink
|
||||
@no_memory64('TODO: asyncify for wasm64')
|
||||
@@ -7959,11 +7961,13 @@ def test_asyncify_dlfcn(self):
|
||||
{
|
||||
int side_module_run()
|
||||
{
|
||||
+ printf("before sleep\n");
|
||||
emscripten_sleep(1000);
|
||||
+ printf("after sleep\n");
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
- ''', '42', need_reverse=False)
|
||||
+ ''', 'before sleep\nafter sleep\n42', need_reverse=False)
|
||||
|
||||
@no_asan('asyncify stack operations confuse asan')
|
||||
@no_memory64('TODO: asyncify for wasm64')
|
61
dists/emscripten/emscripten-16559.patch
Normal file
61
dists/emscripten/emscripten-16559.patch
Normal file
@ -0,0 +1,61 @@
|
||||
From 229c16de0b827321a0c3e55975e980017d234d43 Mon Sep 17 00:00:00 2001
|
||||
From: Charlie Birks <charlie@daft.games>
|
||||
Date: Tue, 22 Mar 2022 13:13:34 +0000
|
||||
Subject: [PATCH 1/2] Update SDL2 for #16462
|
||||
|
||||
---
|
||||
tools/ports/sdl2.py | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/tools/ports/sdl2.py b/tools/ports/sdl2.py
|
||||
index f499ca8fa15..7c0a30cbd71 100644
|
||||
--- a/tools/ports/sdl2.py
|
||||
+++ b/tools/ports/sdl2.py
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
import os
|
||||
|
||||
-TAG = 'release-2.0.20'
|
||||
-HASH = '67e1abe1183b04836b35d724fd495c83c9559b4530d4a5c9bcc89648af0ac7cc51c02f7055a1664fe5f5f90953d22a6c431fa8bc5cdd77c94a97f107c47e2d62'
|
||||
+TAG = '4b8d69a41687e5f6f4b05f7fd9804dd9fcac0347'
|
||||
+HASH = '2d4d577c7584da22306b05a44bc08200460a33cd414fed2dc948e2a86e7b2d1a5cbc13bacadb63618823ba63c210f21c570adbab39f7645bf902196fa91c6b4e'
|
||||
SUBDIR = 'SDL-' + TAG
|
||||
|
||||
|
||||
|
||||
From 1eb16caf951bf0a38dda07d3335b4fdeb397ebc7 Mon Sep 17 00:00:00 2001
|
||||
From: Charlie Birks <charlie@daft.games>
|
||||
Date: Fri, 25 Mar 2022 11:24:00 +0000
|
||||
Subject: [PATCH 2/2] Add an extra move to the SDL2 mouse test
|
||||
|
||||
The "first" event now has valid relative motion, so don't need that workaround either.
|
||||
---
|
||||
tests/sdl2_mouse.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/tests/sdl2_mouse.c b/tests/sdl2_mouse.c
|
||||
index f7786cdb02d..6ec8871d427 100644
|
||||
--- a/tests/sdl2_mouse.c
|
||||
+++ b/tests/sdl2_mouse.c
|
||||
@@ -26,11 +26,10 @@ void one() {
|
||||
printf("motion : %d,%d %d,%d\n", m->x, m->y, m->xrel, m->yrel);
|
||||
|
||||
if (mouse_motions == 0) {
|
||||
- // xrel/yrel will be zero for the first motion
|
||||
#ifdef TEST_SDL_MOUSE_OFFSETS
|
||||
- assert(eq(m->x, 5) && eq(m->y, 15) && eq(m->xrel, 0) && eq(m->yrel, 0));
|
||||
+ assert(eq(m->x, 5) && eq(m->y, 15) && eq(m->xrel, 5) && eq(m->yrel, 15));
|
||||
#else
|
||||
- assert(eq(m->x, 10) && eq(m->y, 20) && eq(m->xrel, 0) && eq(m->yrel, 0));
|
||||
+ assert(eq(m->x, 10) && eq(m->y, 20) && eq(m->xrel, 10) && eq(m->yrel, 20));
|
||||
#endif
|
||||
} else if (mouse_motions == 1) {
|
||||
#ifdef TEST_SDL_MOUSE_OFFSETS
|
||||
@@ -93,6 +92,7 @@ int main() {
|
||||
}
|
||||
|
||||
void main_2(void* arg) {
|
||||
+ emscripten_run_script("window.simulateMouseEvent(0, 0, -1)");
|
||||
emscripten_run_script("window.simulateMouseEvent(10, 20, -1)"); // move from 0,0 to 10,20
|
||||
emscripten_run_script("window.simulateMouseEvent(10, 20, 0)"); // click
|
||||
emscripten_run_script("window.simulateMouseEvent(10, 20, 0)"); // click some more, but this one should be ignored through PeepEvent
|
22
dists/emscripten/emscripten-16687.patch
Normal file
22
dists/emscripten/emscripten-16687.patch
Normal file
@ -0,0 +1,22 @@
|
||||
From 5ed10829c6d806d630d98943432c222cf8f02017 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Christian=20K=C3=BCndig?= <christian@kuendig.info>
|
||||
Date: Sat, 9 Apr 2022 16:12:40 +0200
|
||||
Subject: [PATCH] SDL2: Fix SDL_OpenURL
|
||||
|
||||
---
|
||||
tools/ports/sdl2.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/tools/ports/sdl2.py b/tools/ports/sdl2.py
|
||||
index 7c0a30cbd71..fb6ea398bfd 100644
|
||||
--- a/tools/ports/sdl2.py
|
||||
+++ b/tools/ports/sdl2.py
|
||||
@@ -58,7 +58,7 @@ def create(final):
|
||||
power/emscripten/SDL_syspower.c joystick/emscripten/SDL_sysjoystick.c
|
||||
filesystem/emscripten/SDL_sysfilesystem.c timer/unix/SDL_systimer.c haptic/dummy/SDL_syshaptic.c
|
||||
main/dummy/SDL_dummy_main.c locale/SDL_locale.c locale/emscripten/SDL_syslocale.c misc/SDL_url.c
|
||||
- misc/dummy/SDL_sysurl.c'''.split()
|
||||
+ misc/emscripten/SDL_sysurl.c'''.split()
|
||||
thread_srcs = ['SDL_syscond.c', 'SDL_sysmutex.c', 'SDL_syssem.c', 'SDL_systhread.c', 'SDL_systls.c']
|
||||
thread_backend = 'generic' if not settings.USE_PTHREADS else 'pthread'
|
||||
srcs += ['thread/%s/%s' % (thread_backend, s) for s in thread_srcs]
|
@ -1,4 +0,0 @@
|
||||
/*global Module*/
|
||||
Module["arguments"] = [];
|
||||
Module["arguments"].push("--config=/data/local/scummvm.ini");
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 8.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
Loading…
x
Reference in New Issue
Block a user