#!/usr/bin/env bash # Compila o Crimson Fields do fredygames.com: o jogo de estrategia por turnos de Jens Granseuer (codigo, # graficos, sons e mapas sob a GPL 2 ou posterior; musica de Dave Fancella, GPL ou CC BY-SA 1.0; fonte # Bepa, derivada da Bitstream Vera) em WebAssembly, com o Emscripten 6.0.10 e as portas SDL2 2.32.10, # SDL2_ttf 2.20.2 (FreeType 2.14.3, HarfBuzz 3.2.0), SDL2_mixer 2.8.0 (libogg 1.3.5, libvorbis 1.3.7) e zlib 1.3.2 # dele. Este script, o patch e os arquivos de crimson-fields/ fazem parte do codigo-fonte publicado # junto do jogo. # # source /emsdk_env.sh # emsdk install 6.0.10 && emsdk activate 6.0.10 # src/FredGames.Bundler/Builds/build-crimson-fields.sh # precisa tambem de node, python3 e fluidsynth 2.6.1 # # Fonte: o tarball crimson-0.5.3.tar.bz2 publicado pelo autor em crimson.seul.org, conferido pelo # SHA-256 e guardado em artifacts/cache/crimson-fields/ (o site hospeda o mesmo arquivo). Na pasta de # fontes do site, o script acha o tarball e os arquivos auxiliares ao lado dele. # # Passos: # 1. As ferramentas de dados do proprio jogo (mkdatafile, mktileset, mkunitset, mklocale, cfed) sao # compiladas com o mesmo Emscripten para rodar no node e geram cf.dat, default.tiles, # default.units, os idiomas (locale/*.dat) e os mapas (levels/*.lev), como o "make" do jogo faz. # 2. A musica: o default.mid (380 faixas) vira formato 0 com midi-to-type0.py, porque o FluidSynth so # le ate 128 faixas; o FluidSynth 2.6.1 o toca com o banco de sons FluidR3_GM (Frank Wen, licenca # MIT; arquivo fluid-soundfont_3.1.orig.tar.gz do Debian) e o wav2ogg.c, compilado aqui para o # node com o libvorbis do Emscripten, grava music/default.ogg. O jogo procura primeiro a versao # .ogg da musica. O resultado e conferido pelo SHA-256. # 3. O jogo, com cfsdl12.c/.h (as chamadas de SDL 1.2 que o jogo usa, sobre a SDL2; incluido antes de # cada arquivo) e sem rede (-DDISABLE_NETWORK). Os dados vao num crimson.data que o proprio # Emscripten carrega (--preload-file) em /data/. # # crimson-fields-fredygames.patch: # - SDL_zlib.c usa a interface de RWops da SDL2; Surface::GetColorKey le a chave com # SDL_GetColorKey; View::FetchEvent usa o SDL_PeepEvents da SDL2; o campo de texto le o caractere # digitado do keysym.unused, onde o cfsdl12.c o guarda; # - fredygames_save(), que a pagina chama quando a aba some ou fecha e o jogo chama ao liberar um # mapa da campanha: o jogo so grava as opcoes e os mapas liberados ao sair. # # Saida: artifacts/build/crimson-fields-0.5.3/crimson.{js,wasm,data}, que o bundle.yaml do jogo confere # pelo SHA-256. set -euo pipefail VERSION=0.5.3 SRC_SHA256=d59858c05e340367c61c8ca1dd00c36642a0c56d10d1d9a1626c5ba7d88b40d6 EMSCRIPTEN=6.0.10 FLUIDSYNTH=2.6.1 SOUNDFONT_TAR=fluid-soundfont_3.1.orig.tar.gz SOUNDFONT_TAR_SHA256=2621acaa1c78e4abdb24bdd163230cc577e61276936d6aa6e3180582142f0343 SOUNDFONT_SHA256=74594e8f4250680adf590507a306655a299935343583256f3b722c48a1bc1cb0 MUSIC_SHA256=b0fbaaf14560917661e22247f25012d2f890bd7e6eaea99fa85110d4c77afec6 here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" root="$(git -C "$here" rev-parse --show-toplevel 2>/dev/null || pwd)" out="$root/artifacts/build/crimson-fields-$VERSION" cache="$root/artifacts/cache/crimson-fields" sfcache="$root/artifacts/cache/soundfonts" tarball="crimson-$VERSION.tar.bz2" if ! emcc --version 2>/dev/null | head -1 | grep -q " $EMSCRIPTEN "; then echo "Precisa do Emscripten $EMSCRIPTEN no PATH (source /emsdk_env.sh)." >&2 exit 1 fi if ! fluidsynth --version 2>/dev/null | grep -q "FluidSynth runtime version $FLUIDSYNTH\$"; then echo "Precisa do FluidSynth $FLUIDSYNTH no PATH (outra versao pode tocar a musica diferente)." >&2 exit 1 fi # Arquivo auxiliar: em crimson-fields/ (repositorio) ou ao lado do script (pasta de fontes do site). aux() { if [ -f "$here/crimson-fields/$1" ]; then echo "$here/crimson-fields/$1"; else echo "$here/$1"; fi } fetch() { # url destino sha256 if [ ! -f "$2" ]; then mkdir -p "$(dirname "$2")" curl -fsSL -o "$2.part" "$1" mv "$2.part" "$2" fi echo "$3 $2" | shasum -a 256 -c - >&2 } if [ -f "$here/$tarball" ]; then src_tar="$here/$tarball" echo "$SRC_SHA256 $src_tar" | shasum -a 256 -c - >&2 else src_tar="$cache/$tarball" fetch "https://crimson.seul.org/files/$tarball" "$src_tar" "$SRC_SHA256" fi fetch "https://deb.debian.org/debian/pool/main/f/fluid-soundfont/$SOUNDFONT_TAR" "$sfcache/$SOUNDFONT_TAR" \ "$SOUNDFONT_TAR_SHA256" soundfont="$sfcache/fluid-soundfont-3.1/FluidR3_GM.sf2" if [ ! -f "$soundfont" ]; then tar xzf "$sfcache/$SOUNDFONT_TAR" -C "$sfcache" fluid-soundfont-3.1/FluidR3_GM.sf2 fi echo "$SOUNDFONT_SHA256 $soundfont" | shasum -a 256 -c - >&2 rm -rf "$out" mkdir -p "$out/obj" "$out/tools" "$out/data/levels" "$out/data/locale" "$out/data/sound" "$out/data/music" tar xjf "$src_tar" -C "$out" s="$out/crimson-$VERSION" patch -d "$s" -p1 --no-backup-if-mismatch < "$(aux crimson-fields-fredygames.patch)" cp "$(aux cfsdl12.h)" "$(aux cfsdl12.c)" "$out/" defs=(-DHAVE_DIRENT_H -DHAVE_STRCASECMP -DHAVE_STRNCASECMP -DHAVE_LIBZ -DVERSION="\"$VERSION\"") ports=(-sUSE_SDL=2 -sUSE_ZLIB=1) cflags=(-O2 -w -ffile-prefix-map="$out/=" -include "$out/cfsdl12.h" -I"$s/src/common") # As duas partes em C, compiladas uma vez para as ferramentas e para o jogo. ( cd "$out" emcc "${cflags[@]}" "${defs[@]}" "${ports[@]}" -c crimson-$VERSION/src/common/SDL_zlib.c -o obj/SDL_zlib.o emcc "${cflags[@]}" "${ports[@]}" -c cfsdl12.c -o obj/cfsdl12.o ) # 1. Ferramentas de dados (rodam no node, leem e gravam direto no disco). tool() { # nome fontes... local name="$1"; shift em++ "${cflags[@]}" -std=gnu++98 "${defs[@]}" -DDISABLE_SOUND -I"$s/src/comet" "${ports[@]}" \ "$@" "$out/obj/SDL_zlib.o" "$out/obj/cfsdl12.o" -sNODERAWFS -sENVIRONMENT=node \ -sALLOW_MEMORY_GROWTH -sEXIT_RUNTIME=1 -o "$out/tools/$name.js" } c=$s/src/common t=$s/tools tool mkdatafile $t/mkdatafile.cpp $t/mksurface.cpp $c/fileio.cpp $c/surface.cpp tool mktileset $t/mktileset.cpp $t/mksurface.cpp $t/parser.cpp $c/fileio.cpp $c/lang.cpp \ $c/lset.cpp $c/rect.cpp $c/sound.cpp $c/strutil.cpp $c/surface.cpp tool mkunitset $t/mkunitset.cpp $t/mksurface.cpp $t/parser.cpp $c/fileio.cpp $c/lang.cpp $c/lset.cpp \ $c/rect.cpp $c/sound.cpp $c/strutil.cpp $c/surface.cpp tool mklocale $t/mklocale.cpp $c/lang.cpp $c/fileio.cpp $c/strutil.cpp tool cfed $t/cfed.cpp $t/parser.cpp $c/fileio.cpp $c/lang.cpp $c/list.cpp $c/lset.cpp \ $c/misc.cpp $c/rect.cpp $c/sound.cpp $c/strutil.cpp $c/surface.cpp $s/src/comet/building.cpp \ $s/src/comet/map.cpp $s/src/comet/mission.cpp $s/src/comet/unit.cpp d="$out/data" node "$out/tools/mkdatafile.js" "$s/gfx/CFIcons.bmp" "$d/cf.dat" node "$out/tools/mktileset.js" "$t/default.tsrc" "$d/default.tiles" "$s/gfx" node "$out/tools/mkunitset.js" "$t/default.usrc" "$d/default.units" "$s/gfx" for f in $(LC_ALL=C ls "$s/locale/"*.tmpl); do node "$out/tools/mklocale.js" -l "$f" "$d/locale/$(basename "$f" .tmpl).dat" done for f in $(LC_ALL=C ls "$s/levels/"*.src); do node "$out/tools/cfed.js" "$f" --tiles "$d/default.tiles" --units "$d/default.units" \ -o "$d/levels/$(basename "$f" .src).lev" done cp "$s/gfx/title.bmp" "$s/gfx/Bepa-Roman.ttf" "$d/" cp "$s/sfx/"*.wav "$d/sound/" # 2. Musica. python3 -I "$(aux midi-to-type0.py)" "$s/music/default.mid" "$out/obj/default-type0.mid" fluidsynth -ni -q -F "$out/obj/default.wav" -r 44100 -T wav -O s16 -g 0.8 "$soundfont" \ "$out/obj/default-type0.mid" emcc -O2 -ffile-prefix-map="$out/=" -sUSE_OGG=1 -sUSE_VORBIS=1 "$(aux wav2ogg.c)" -sNODERAWFS \ -sENVIRONMENT=node -sALLOW_MEMORY_GROWTH -o "$out/tools/wav2ogg.js" node "$out/tools/wav2ogg.js" "$out/obj/default.wav" "$d/music/default.ogg" 0.3 rm "$out/obj/default.wav" echo "$MUSIC_SHA256 $d/music/default.ogg" | shasum -a 256 -c - >&2 # 3. O jogo. Compila de dentro da pasta de saida, com caminhos relativos: o carregador do # crimson.data guarda o nome que a pasta tem aqui. ( cd "$out" em++ "${cflags[@]}" -std=gnu++98 "${defs[@]}" -DDISABLE_NETWORK -DCF_DATADIR='"/data/"' \ -I"$s/src/cf" "${ports[@]}" -sUSE_SDL_TTF=2 -sUSE_SDL_MIXER=2 -sSDL2_MIXER_FORMATS='["ogg"]' \ $(cd "$out" && LC_ALL=C ls crimson-$VERSION/src/cf/*.cpp crimson-$VERSION/src/common/*.cpp) \ obj/SDL_zlib.o obj/cfsdl12.o -lidbfs.js \ -sASYNCIFY -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=64MB -sENVIRONMENT=web -sEXIT_RUNTIME=1 \ -sEXPORTED_RUNTIME_METHODS=FS,addRunDependency,removeRunDependency \ --preload-file data@/data -o crimson.js ) shasum -a 256 "$out/crimson.js" "$out/crimson.wasm" "$out/crimson.data" "$d/music/default.ogg"