#!/bin/bash
# advbld: copyright Mike Arnautov 2014-2024, licensed              
# under GPL (version 3 or later) or the Modified BSD Licence, 
# whichever is asserted by the supplied LICENCE file.   
#
# Builds an A-code game from sources (A-code or derived C ones).
#
CC=cc     # Select the C compiler
CPP=c++   # Only required for the Qt5 build.
#
! which $CC &>/dev/null && echo "$CC not found." && exit 1 
#
# Set ADV, BIN, ADIR, KDIR and define some functions.
#
. $(dirname `which $0`)/lib.sh
#
version=3.3
case "$(uname -m)" in
  *64*) arch=64 ;;
  *) arch=32 ;;
esac
name=
plain=
mode=
stable=0
build=YES
translate=YES
xref=
debug=
query=YES
wiz=NO
flags=
vrb=1
type=B
exetype='BROWSER/CONSOLE executable'
readline=
#-----------------------------------------------------------------------------
#
usage()
{
   cat <<EOT
Usage: $0 [<game>] [<options>]

The <game> parameter is either a pathname of the main source file
(the .acd suffix is optional), or the name of the game to be built.
Default <game> is the name of the current directory (less the version number,
if any; so if the drectory is e.g. adv770-2.20, adv770 will be built).
If a game name is specified, it is searched for in in the current directory,
then in the directory specified by the global variable ADVDIR, if this is set, 
and finally in directories one and two levels above this script.

C sources are created in the the C subdirectory of the directory containing
the located game source. 

Options specifying general build type. Except for -W they are mutually
exclusive, overriding any previosly specified type.

  -B ........... default; build combined console/browser executable
  -C ........... build create a console-only executable
  -J ........... build a JavaScript version (.html)
  -L ........... build a test executable using the library mode
  -Q ........... build a Qt5 executable
  -T ........... build a single turn executable
  -W ........... include opt/debug.acd (if exists) for "wizard" in-game commands

Options affecting the behaviour of this script.

  -q ........... be quiet (opposite of -v)
  -v ........... be verbose (opposite of -q)
  -x ........... echo commands being executed
  -h ........... print this text

The rest of options are of use only to an A-code game developer. 

Versions of acdc, kernel and game are deemed "unstable" if the relevant
directories lack a version number. The script defaults to using unstable
versions, if present, and the latest (highest version number) versions
otherwise.

  -s ........... insist on using latest stable versions
  -u ........... insist on using unstable versions
  -a <version> . use the specified version of acdc
  -k <version> . use the specified version of the A-code kernel

Other build options.

  -g ........... create a gdb-instrumented executable
  -gg .......... As -g but with gcc macro capability (forces CC=gcc)
  -D <symbol> .. add a symbol to the C compilation/linking command
  --m32 ........ on a 64 bit machine, force building 32 bit executable
  -p ........... (plain) don't encrypt game data
  -d ........... acdc's -debug -- echos to stderr A-code lines being executed
  -c ........... translate A-code to C but do not buid executable
  -b ........... build from pre-existing C sources (don't translate A-code to C)
  -w ........... show acdc warnings (these are suppressed by default)
  -X ........... generate cross-reference files for the A-code game source

EOT
   exit 1
}
#-----------------------------------------------------------------------------
# Parse command line
#
set -- $(comline 'akD' $@)    # Pre-process the command line
warn=NO                 # No acdc warnings by default
while [ $# -gt 0 ]; do
  case "$1" in 
    -B) type=B; exetype='HTTP/CONSOLE executable' ;;
    -C) type=C; exetype='CONSOLE-only executable' ;;
    -J) ! which emcc 2>/dev/null && \
        echo === Cannot build HTML/Javascript without emcc && exit 1
        type=J ; query=NO ;;
    -L) type=L; flags="$flags -DADVLIB"; query=NO;
        exetype='ADVLIB test executable' ;;
    -Q) type=Q; flags="$flags -DADVLIB -DQT"; query=NO
        exetype="Qt5 executable" ;;
    -T|-S) type=T; flags="$flags -DTURN"; query=NO; readline='-DNO_READLINE'
        exetype='SINGLE TURN executable' ;;
    -W) wiz=YES ;;
    -q) vrb=0 ;;
    -v) vrb=2 ;;
    -x) set -x ;;
    -h|--help) usage ;;
    -p) plain=-plain ;;
    -d) debug="$debug -d" ;;
    -b) translate=NO ;;
    -c) build=NO ;;
    -w) warn=YES ;;
    -X) xref=-x ;;
    -D) [ "$2" = 'NO_READLINE' ] && readline="$1$2" || flags="$flags $1$2"
        shift ;;
    -s) stable=1 ;;
    -u) stable=-1 ;;
    -a) shift; AVER="$1"; suptype="$suptype acdc-$1," ;;
    -k) shift; KVER="$1"; suptype="$suptype kernel-$1," ;;
    -g) [ -n "$dbg" ] && CC=gcc && dbg="-g -gdwarf-2 -g3" || dbg=-g ;;
    --m32) flags="$flags -m32"; arch=32; CPPFLAGS=-m32 ;;
    --fread) mode="$mode -file-read" ;;        # Deprecated
    --fmemory) mode="$mode -file-memory" ;;    # Deprecated
    --fpage) mode="$mode -file-page" ;;        # Deprecated
    --locates) flags="$flags -DLOC_STATS=2" ;; # Deprecated
    -*) echo Unknown keyword $1. Use -h to see usage details. && exit 1;;
    *) [ -n "$name" ] && echo Bad command line. && exit 1; name="$1" ;;
  esac
  shift
done
#
[ "$type" = J ] && dbg= && debug=
[ $warn = NO ] && mode="$mode -no-warnings"
#
[ $vrb != 0 ] && echo && echo "[A-code adventure builder, version $version]"
#
#-----------------------------------------------------------------------------
# Find and check the acdc and kernel versions to be used.
#
stb=''
[ $stable = -1 ] && stb=' unstable'
[ $stable = 1 ] && stb=' stable'
[ -z "$AVER" ] && acdc=acdc || acdc=acdc-$AVER 
ADIR=$(find_it $stable $acdc)
[ -z "$ADIR" ] && echo Unable to find$stb translator executable. && exit 1
[ ! -x "$ADIR/acdc" ] && echo Building acdcd... && (cd $ADIR; ./build -cc $CC)
[ ! -x "$ADIR/acdc" ] && echo No acdc executable in $ADIR! && exit 1
#
#
[ -z "$KVER" ] && kernel=kernel || kernel=kernel-$KVER
KDIR=$(find_it $stable $kernel)
[ -z "$KDIR" ] && echo Unable to find$stb A-code kernel! && exit 1
IKVER=$(kversion $KDIR)
#-----------------------------------------------------------------------------
# Find the game to build.
#
GAME=$(find_it $stable $name)
if [ -z "$GAME" ]; then
  [ -z "$name" ] && name=$(basename $(pwd))
  echo Unable to find$stb A-code source file for $name. && exit 1
fi
#
GDIR=$(dirname $GAME)
srcfile=$(basename $GAME)
[ -z "$name" ] && name=$(basename $GDIR) 
name=${name%%.acd}    # Strip off the .acd suffix, if there
#-----------------------------------------------------------------------------
# Set various build mode flags and variables.
#
[ "$type" = C ] && flags="$flags -DCONSOLE";
[ "$vrb" != 2 ] && mode="$mode -q"
[ "$wiz" = YES ] && suptype=" wizard mode,"
[ -n "$dbg" ] && suptype="$suptype gdb,"
[ -n "$debug" ] && suptype="$suptype embedded A-code,"
[ -n "$plain" ] && suptype="$suptype plain text,"
suptype=${suptype:0:$(expr ${#suptype} - 1)}
#
[ "$stable" = 1 ] && vstate='(stable)'
#
[ "$type" != J ] && case $arch in
  *32*) exetype="$exetype (32 bit)" ;;
  *64*) exetype="$exetype (64 bit)" ;;
  *) ;;
esac
[ $vrb != 0 ] && echo "Building $name $exetype $vstate"
name=$(basename $name)
name=`echo $name | sed s/-[0-9]*.[0-9]*//`
#
if [ ! -d "$GDIR" ]; then
   echo Not found: $GDIR
   exit 1
fi
cd "$GDIR"
#-----------------------------------------------------------------------------
# Point the the single-turn gameversion, if necessary
#
srcdir=.
if [ "$query" = NO ]; then
  [ "$name" = adv550 ] && srcfile=cgi/cgi_adv550.acd && srcdir=cgi
  [ "$name" = adv580 ] && srcfile=cgi/cgi_adv580.acd && srcdir=cgi
  [ "$name" = adv660 ] && srcfile=cgi/cgi_adv660.acd && srcdir=cgi
fi
#-----------------------------------------------------------------------------
# If need readline, set things up appropriately.
#
if [ -z "$readline" ]; then
   findlibs $arch readline ncurses  # Sets $libs and $mislib
  [ -n "$mislib" ] && findlibs $arch readline ncursesw
  case "$libs" in
    *-lreadline*-lncurses*) readline="$libs" ;;
    *) readline=-DNO_READLINE
       LIBWARN='WARNING: command line editing not supported.'
    ;;
  esac
fi
#
[ -n "$debug" ] && DEBUG='=-DDEBUG -g'
#-----------------------------------------------------------------------------
# Make sure build subdir exists.
#
if [ -d C ]; then
  if [ $translate = YES ]; then
    rm -rf C/*
  fi
else
  mkdir C
fi
rm -f bld.log
#
[ $vrb != 0 ] && echo [A-code kernel, version $IKVER] 
#-----------------------------------------------------------------------------
# Translate A-code source to C, unless told not to.
#
if [ "$translate" == YES ]; then
#
# Add wizard mode if requested, otherwise ensure it is absent.
#
  if [ $wiz = YES ]; then
    [ -r opt/debug.acd ] && cp opt/debug.acd $srcdir/
  else
    rm -f $srcdir/debug.acd
  fi
  $ADIR/acdc -v
  ! $ADIR/acdc $srcfile -o ./C $xref $debug $plain $mode &>bld.log && \
    ( [ $vrb != 0 ] && cat bld.log ) && exit 1
  rm -f $srcdir/debug.acd bld.log
#
# Copy the kernel.
#
  cp $KDIR/adv0* C/
  [ "$type" = L ] && cp $KDIR/extras/libtest.c C/
fi
#-----------------------------------------------------------------------------
# Cross-reference sort (if Perl available)
#
if [ -n "$xref" -a -n "$(which perl)" ]; then
  [ $srcdir != . ] && mv $srcdir/*.xrf ${name}.xrf
  $BIN/sortxrefs $name.xrf &>bld.log
  [ $vrb = 2 ] && cat bld.log
  rm bld.log
fi
#
[ "$build" != 'YES' ] && exit 0
#-----------------------------------------------------------------------------
# Check whether UTF8 is wanted and if it is, try to find/make the
# utf8proc static library.
#
grep UTF8 C/adv1.h | grep -q 1
if [ $? = 0 ]; then
  ULIB="$ADV/lib$arch"
  if [ ! -r "$ULIB/libutf8proc.c" ]; then
    flags="$flags -I $ADV/utf8proc $ADV/utf8proc/utf8proc.c"
  else
    echo Unable to find the utf8proc library. && exit 1  
  fi
fi
#   if [ ! -r "$ULIB/libutf8proc.a" ]; then
#     [ -r "$ADV"/utf8proc/Makefile ] &&
#     (
#       echo Building utf8proc library...
#       cd "$ADV"/utf8proc/;
#       make clean >/dev/null
#       CFLAGS="-O2 -m$arch" LDFLAGS=-m$arch make libutf8proc.a >/dev/null
#       cp libutf8proc.a "$ULIB/"
#     )
#   fi
#   [ ! -r "$ULIB/libutf8proc.a" ] && \
#     echo Unable to find/build the utf8proc library. && exit 1  
# #  cp "$ADV/utf8proc/utf8proc.h $ULIB/
# #  cp "$ADV/utf8proc/libutf8proc.a $ULIB/
#   flags="$flags -I $ADV/utf8proc -L $ULIB -lutf8proc"
# fi
#-----------------------------------------------------------------------------
# Time to build the game!
#
[ "$type" = C ] && [ "$readline" = '-DNO_READLINE' ] && \
  exetype="$exetype without command editing"
[ -n "$suptype" ] && [ $vrb != 0 ] && echo "Modifiers:$suptype."
#-----------------------------------------------------------------------------
# The JS mode is special.
#
if [ "$type" = J ]; then
   [ $vrb = 0 ] || echo Creating a JAVASCRIPT/HTML build
   cd C
#   [ -r /opt/emsdk/emsdk_env.sh ] && . /opt/emsdk/emsdk_env.sh &>/dev/null
   ! emcc -Os -s ASM_JS=1 -s WASM=0 -s ENVIRONMENT=web $flags \
     -DJS adv*.c  -Wno-parentheses-equality -lidbfs.js \
       -s "EXPORTED_RUNTIME_METHODS=['cwrap']" \
         -o $name.js -s EXPORTED_FUNCTIONS="['_advturn']" &>bld.log && \
           cat bld.log && exit 1
   rm bld.log
   [ $vrb != 0 ] && echo Merging $name.js into $name.html...
# 
# Now merge $KDIR/extras/acode.html with $name.js
#
echo $KDIR/extras/acode.html
pwd
exit
   RIFS="$IFS"
   IFS=
   while read line; do
     case "$line" in
       *\%JAVASCRIPT\%*) cat $name.js ;;
       *\%NAME\%*) echo "${line/\%NAME\%/$name}" ;;
       *) echo "$line";;
     esac
   done <$KDIR/extras/acode.html >$name.html
   IFS="$RIFS"
   rm $name.js && mv $name.html ../
   echo Created $name.html $vstate
   [ $vrb != 0 ] && echo
   exit
fi
#-----------------------------------------------------------------------------
# Qt5 build is also special.
#
if [ $type = Q ]; then
#
# Check whether we have the necessary software. NB findlibs sets the
# libs and mislibs variables.
#
  ! which $CPP &>/dev/null && echo Need $CPP for a QT build. && exit 1
  ! which qmake &>/dev/null && echo Need qmake for a QT build. && exit 1
  ! which make &>/dev/null && echo Need $make for a QT build. && exit 1
  findlibs $arch Qt5Core Qt5Widgets Qt5Gui Qt5WebKit Qt5WebKitWidgets pthread
  [ "$mislib" != '' ] && echo Missing $arch bit libraries: $mislib && exit 1
  cd C
  cp $KDIR/extras/*.{h,cpp} ../extras/advinfo.h .
  cp $KDIR/extras/acode.pro $name.pro
  qmake
  ! make -j $(nproc) >/dev/null 2>bld.log && cat bld.log && exit 1
  rm -f bld.log
  strip $name
  mv $name $GDIR/$name.qt5
  exit 0
fi
#-----------------------------------------------------------------------------
# Just a C build of some sort.
#
[ $vrb != 0 ] && echo "Compiling and linking $exetype"
[ -n "$LIBWARN" ] && echo $LIBWARN  
! $CC ./C/*.c $flags $dbg $readline -o $name $CFLAGS &>bld.log \
  && cat bld.log && exit 1
rm -f bld.log
#
# Get the data file, if one got created.
#
[ -f C/$name.dat ] && cp C/$name.dat .
[ -z "$dbg" ] && strip $name
echo "Created $name $vstate"
[ $vrb != 0 ] && echo
#-----------------------------------------------------------------------------
exit      # All done!
#
############################### End of script ################################
