#!/bin/sh
# Builds Endless Sky (https://github.com/endless-sky/endless-sky, GPL-3.0-or-later
# code, art and sounds under the licences in its copyright file) for the site,
# from upstream source with Emscripten. endless-sky.diff beside this script
# makes it run in a browser page without threads (they would need
# cross-origin isolation):
# - the task queue runs its work on the main thread, a few milliseconds at a
#   time each frame, and sounds load as the game starts;
# - the frame timer sleeps with Asyncify, so the game's own loop hands the
#   browser its turn;
# - music (MP3 and FLAC streams decoded on threads) and AVIF images (none in
#   the game's data) are left out;
# - the page sizes the game (wg_resize) and runs full screen.
# The approach follows Endless Web (github.com/thomasballinger/endless-web).
# The data isn't bundled into a .data file: the play page downloads it in
# zips from R2 (/files/endless-sky/) and writes it to /es; the player's
# files live in /es-user.
#
# Needs git, zip and emsdk 4.0.7 (source emsdk_env.sh first).
# Usage: sh build.sh [work dir]
# Writes endless-sky.js and endless-sky.wasm next to this script, and the zips
# for R2 to <work dir>/r2/endless-sky/ (upload them to wasmgame-files under the
# same keys). For a newer Endless Sky, copy this folder to
# endless-sky-<new rev>-p1 and change the revision below; a change to the
# build here takes the next -p number.
set -eu

ES_REV=457d2e5f5053507da9747bb84f2913159119d4db
EMSDK_VERSION=4.0.7

here=$(cd "$(dirname "$0")" && pwd)
work=${1:-${TMPDIR:-/tmp}/endless-sky-build}

case "$(emcc --version | head -1)" in
  *" $EMSDK_VERSION "*) ;;
  *) echo "warning: built and tested with emsdk $EMSDK_VERSION" >&2 ;;
esac

# Emscripten fetches these ports as GitHub archives; clone them instead.
. "$here/ports.sh"
port zlib madler/zlib v1.3.1 zlib-1.3.1 https://github.com/madler/zlib/archive/refs/tags/v1.3.1.tar.gz
port sdl2 libsdl-org/SDL release-2.32.0 SDL-release-2.32.0 https://github.com/libsdl-org/SDL/archive/release-2.32.0.zip

mkdir -p "$work"
if [ ! -d "$work/src" ]; then
  git clone -q https://github.com/endless-sky/endless-sky.git "$work/src"
fi
cd "$work/src"
git checkout -q -f $ES_REV
git clean -q -fdx source
git apply "$here/endless-sky.diff"

# minizip (for plugin zips) comes with zlib.
if [ ! -d "$work/zlib" ]; then
  git -c advice.detachedHead=false clone -q --depth 1 -b v1.3.1 https://github.com/madler/zlib.git "$work/zlib"
fi
mkdir -p "$work/include/minizip"
cp "$work/zlib/contrib/minizip/unzip.h" "$work/zlib/contrib/minizip/ioapi.h" "$work/include/minizip/"

# libjpeg-turbo (the game reads JPEGs straight to RGBA, which plain libjpeg can't).
if [ ! -f "$work/deps/lib/libjpeg.a" ]; then
  rm -rf "$work/libjpeg-turbo"
  git -c advice.detachedHead=false clone -q --depth 1 -b 3.1.0 https://github.com/libjpeg-turbo/libjpeg-turbo.git "$work/libjpeg-turbo"
  emcmake cmake -S "$work/libjpeg-turbo" -B "$work/libjpeg-turbo/build" -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX="$work/deps" -DENABLE_SHARED=OFF -DWITH_SIMD=OFF -DWITH_TURBOJPEG=OFF
  make -j"$(nproc)" -C "$work/libjpeg-turbo/build" install
fi

FLAGS="-O2 -sUSE_SDL=2 -sUSE_LIBPNG=1 -sUSE_ZLIB=1 -fexceptions"
CXXFLAGS="$FLAGS -std=c++20 -DES_GLES -I$work/include -I$work/deps/include -Wno-unused-parameter"
obj=$work/obj
# A changed patch (headers) or flags: compile everything again.
patch_sum=$( (cat "$here/endless-sky.diff"; echo "$CXXFLAGS") | sha256sum | cut -c1-16)
if [ "$(cat "$obj/.patch" 2>/dev/null)" != "$patch_sum" ]; then rm -rf "$obj"; fi
mkdir -p "$obj"
# Everything but the tests' own entry points, Windows code and the music decoders.
find source -name '*.cpp' ! -path 'source/windows/*' ! -name 'Mp3Supplier.cpp' ! -name 'FlacSupplier.cpp' ! -name 'AsyncAudioSupplier.cpp' | LC_ALL=C sort > "$work/sources.txt"
# Builds the ports once before the parallel compile.
embuilder build sdl2 libpng zlib >/dev/null
xargs -P "${JOBS:-$(nproc)}" -I{} sh -c 'o="$0/$(echo "$1" | tr / _).o"; [ "$o" -nt "$1" ] || em++ $2 -c "$1" -o "$o"' "$obj" {} "$CXXFLAGS" < "$work/sources.txt"
for f in unzip ioapi; do
  emcc $FLAGS -DIOAPI_NO_64 -c "$work/zlib/contrib/minizip/$f.c" -o "$obj/minizip_$f.o"
done
echo "$patch_sum" > "$obj/.patch"
em++ $FLAGS -o "$work/endless-sky.js" "$obj"/*.o "$work/deps/lib/libjpeg.a" -lopenal \
  --js-library "$here/fullscreen.js" \
  -sASYNCIFY -sASYNCIFY_STACK_SIZE=65536 -sSTACK_SIZE=4MB \
  -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 \
  -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB \
  -sINVOKE_RUN=0 -sFORCE_FILESYSTEM -sEXIT_RUNTIME=0 \
  -sEXPORTED_RUNTIME_METHODS=callMain,FS,ccall,cwrap
cp "$work/endless-sky.js" "$work/endless-sky.wasm" "$here/"

# The data for R2.
rm -rf "$work/r2" && mkdir -p "$work/r2/endless-sky"
python3 "$here/pack.py" "$work/src" "$work/r2/endless-sky"
