#!/usr/bin/env bash # Compila o Neverputt do fredygames.com: o minigolfe do projeto Neverball (GPL-2.0-or-later, com as # excecoes do LICENSE.md: fontes DejaVu e WenQuanYi, miniz MIT), em WebAssembly, com o Emscripten # 6.0.10 e as portas SDL2 2.32.10, SDL2_ttf (FreeType 2.14.3, HarfBuzz 3.2.0), libpng 1.6.58, # libjpeg 9f, zlib 1.3.2, libogg 1.3.5 e libvorbis 1.3.7 dele, e o gl4es (MIT), que emula o OpenGL do # jogo sobre o WebGL, como na build web oficial do Neverball. Este script e o patch 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-neverputt.sh # # Fonte: o mesmo neverball-source.zip da build do Neverball servida no site (Neverball 1.6.0, commit # 84d4264992fd9b469510f5268cd662ce9d5022b5), ja sem data/ball/octocat/ (o Octocat e usado pelo # projeto com permissao do GitHub, que nao se estende a nos); e o arquivo publicado em # /bundles/neverball/src/. O gl4es e o tarball do GitHub do commit fixo abaixo (o mesmo do # AstroMenace). Os dois sao conferidos pelo SHA-256; na pasta de fontes do site, o script acha os # arquivos e o patch ao lado dele. # # O projeto so tem build web do Neverball; esta e a mesma receita (emscripten/ball.mk), com os fontes # do Neverputt (os do Makefile principal) e o som pelo audio.c do jogo (SDL + Ogg Vorbis), no lugar # do audio_emscripten.c, que depende da pagina oficial. neverputt-fredygames.patch (so ativo com # __EMSCRIPTEN__): # - putt/main.c: o canvas acompanha o tamanho que a pagina da a ele (como no Neverball web); F11 e # F12 (tela cheia e captura de tela) nao fazem nada; emscripten_sleep() a cada quadro (o build usa # Asyncify); fredygames_save(), que a pagina chama quando a aba some ou fecha: o jogo so grava as # opcoes ao sair; # - share/base_config.c: pasta do usuario definida no build (EMSCRIPTEN_USER_DIR); o Neverputt usa # /neverputt, com banco proprio no IndexedDB, separado do Neverball. # # Dados: os campos de golfe (.map) sao compilados pelo mapc do proprio projeto, aqui para o Node.js # (emscripten/mapc.mk); vao para o neverputt.data so os arquivos que o Neverputt usa: os nove campos # de courses.txt, as imagens deles e os recursos comuns (fundos, bola, musica, sons, texturas, fontes). # # Saida: artifacts/build/neverputt-1.6.0/neverputt.{js,wasm,data}, que o bundle.yaml do jogo confere # pelo SHA-256. set -euo pipefail VERSION=1.6.0 SRC_SHA256=8d677b5ae102ee9be77cd8230df6e22404a0ba7d258f478885ddee3ed336200a GL4ES_COMMIT=a025615bc79ac43ec911dced8fe5b8eb45a0b72d GL4ES_SHA256=b4e68ee01c45c114d89d0df290618ea7aa6f4078db11c276a893af39b9ad9b76 GL4ES_DATE=1791993939 # data do commit do gl4es (2026-10-14 16:05:39 UTC) EMSCRIPTEN=6.0.10 here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" root="$(git -C "$here" rev-parse --show-toplevel 2>/dev/null || pwd)" out="$root/artifacts/build/neverputt-$VERSION" cache="$root/artifacts/cache/neverputt" 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 # Arquivo auxiliar: em neverputt/ (repositorio) ou ao lado do script (pasta de fontes do site). aux() { if [ -f "$here/neverputt/$1" ]; then echo "$here/neverputt/$1"; else echo "$here/$1"; fi } # Arquivo conferido: ao lado do script, no cache ou baixado. fetch() { # local file if [ -f "$here/$1" ]; then file="$here/$1" else file="$cache/$1" if [ ! -f "$file" ]; then mkdir -p "$cache" curl -fsSL -o "$file.part" "$2" mv "$file.part" "$file" fi fi echo "$3 $file" | shasum -a 256 -c - >&2 echo "$file" } src_zip="$(fetch neverball-source.zip \ https://fredygames.com/bundles/neverball/src/neverball-source.zip "$SRC_SHA256")" gl4es_tar="$(fetch "gl4es-${GL4ES_COMMIT:0:10}.tar.gz" \ "https://github.com/ptitSeb/gl4es/archive/$GL4ES_COMMIT.tar.gz" "$GL4ES_SHA256")" rm -rf "$out" mkdir -p "$out/obj" "$out/mapc-obj" unzip -q "$src_zip" -d "$out" tar xzf "$gl4es_tar" -C "$out" src="$out/neverball-source" gl4es="$out/gl4es-$GL4ES_COMMIT" patch -d "$src" -p1 --no-backup-if-mismatch < "$(aux neverputt-fredygames.patch)" # share/version.h, como o scripts/version.sh gera fora de um clone do git. printf '#ifndef VERSION_H\n#define VERSION_H 1\n#define VERSION "%s"\n#endif\n' "$VERSION" \ > "$src/share/version.h" # O gl4es grava a data do build (__DATE__ e __TIME__); com SOURCE_DATE_EPOCH o Clang usa a do commit. # Os caminhos dos fontes (__FILE__) saem relativos a pasta de saida (-ffile-prefix-map). export SOURCE_DATE_EPOCH=$GL4ES_DATE # gl4es como biblioteca estatica (receita do COMPILE.md dele para Emscripten). ( cd "$gl4es" emcmake cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DNOX11=ON -DNOEGL=ON -DSTATICLIB=ON \ -DCMAKE_C_FLAGS="-ffile-prefix-map=$out/=" >/dev/null cmake --build build -j 8 >/dev/null ) # mapc para o Node.js (fontes de emscripten/mapc.mk), e os campos do Neverputt compilados com ele. mapc_srcs=(share/mapclib.c share/mapc.c share/array.c share/base_config.c share/base_image.c share/binary.c share/common.c share/dir.c share/fs_common.c share/fs_jpg.c share/fs_png.c share/fs_stdio.c share/miniz.c share/list.c share/log.c share/queue.c share/solid_base.c share/vec3.c) courses=(map-putt map-paxed map-paxed2 map-paxed3 map-slippi map-ckk map-vidski map-iCourse) assets=(back ball/basic-ball bgm geom gui icon item lang png snd textures ttf shot-putt courses.txt lights.txt) ( cd "$src" emcc -O2 -std=gnu99 -w -Ishare -DNDEBUG -ffile-prefix-map="$out/=" -sUSE_SDL=2 -sUSE_LIBPNG=1 \ -sUSE_LIBJPEG=1 -sALLOW_MEMORY_GROWTH=1 -sNODERAWFS=1 -sENVIRONMENT=node \ "${mapc_srcs[@]}" -o "$out/mapc.js" cd data find "${courses[@]}" ball/basic-ball geom gui item -name '*.map' ! -name '*.autosave.map' | LC_ALL=C sort | while read -r m; do node "$out/mapc.js" "$m" . >/dev/null 2>&1 done ) # Dados do neverputt.data: o que o Neverputt le (sem o resto do Neverball), sem os arquivos de edicao # que o empacotador do projeto tambem deixa de fora (mk/package.mk), nem os .map ja compilados. stage="$out/data" mkdir -p "$stage" ( cd "$src/data" for p in "${assets[@]}" holes-*.txt; do mkdir -p "$stage/$(dirname "$p")" cp -R "$p" "$stage/$p" done for d in "${courses[@]}"; do find "$d" -name '*.sol' | while read -r f; do mkdir -p "$stage/$(dirname "$f")" cp "$f" "$stage/$f" done done find "$stage" \( -name '*.map' -o -name '*.xcf' -o -name '*.wings' -o -name screenshot.png \) \ -delete ) # Jogo: fontes do Neverputt no Makefile principal, com as escolhas de emscripten/ball.mk (gl4es, # fs_stdio + miniz, sem HMD) e o audio.c do jogo (SDL + Ogg Vorbis). putt_srcs=(share/lang.c share/st_common.c share/vec3.c share/base_image.c share/image.c share/solid_base.c share/solid_vary.c share/solid_draw.c share/solid_all.c share/mtrl.c share/part.c share/geom.c share/ball.c share/base_config.c share/config.c share/video.c share/glext.c share/binary.c share/audio.c share/state.c share/ease.c share/transition.c share/gui.c share/font.c share/theme.c share/text.c share/common.c share/list.c share/fs_common.c share/fs_png.c share/fs_jpg.c share/fs_ov.c share/dir.c share/fbo.c share/array.c share/log.c share/joy.c share/solid_sim_sol.c share/fs_stdio.c share/miniz.c share/hmd_null.c putt/hud.c putt/game.c putt/hole.c putt/course.c putt/st_all.c putt/st_conf.c putt/main.c) flags=(-O2 -std=gnu99 -w -Ishare -DNDEBUG -DEMSCRIPTEN_USER_DIR='"/neverputt"' -ffile-prefix-map="$out/=" -I"$gl4es/include" -sUSE_SDL=2 -sUSE_SDL_TTF=2 -sUSE_LIBPNG=1 -sUSE_LIBJPEG=1 -sUSE_OGG=1 -sUSE_VORBIS=1) ( cd "$src" objs=() for f in "${putt_srcs[@]}"; do o="$out/obj/$(echo "$f" | tr / _).o" emcc "${flags[@]}" -c "$f" -o "$o" objs+=("$o") done cd "$out" emcc "${flags[@]}" "${objs[@]}" "$gl4es/lib/libGL.a" -lidbfs.js \ -sFULL_ES2=1 -sASYNCIFY -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=128MB -sSTACK_SIZE=1MB \ -sENVIRONMENT=web -sEXIT_RUNTIME=1 \ -sEXPORTED_RUNTIME_METHODS=FS,addRunDependency,removeRunDependency \ --preload-file data@/data -o neverputt.js ) shasum -a 256 "$out/neverputt.js" "$out/neverputt.wasm" "$out/neverputt.data"