#!/bin/bash ################################################################################ # # This script automates downloading, compiling, patching and running Quake 3 on # the Raspberry Pi. # # This script is written and maintained by Chris Lowe. # # http://www.technologist.site # # NOTE: This script requires a copy of "PAK0.PK3" from your orginal Quake 3 # Arena disc. Use the '--source' argument to specify the location of the # required files. This location should contain the following file structure: # # (baseq3) # pak0.pk3 # (missionpack) # pak0.pk3 * # # With these minimal files available, the script will attempt to download and # apply the required patch files. To avoid the download, you can also provide a # copy of the Linux Q3 1.32 patch on the storage device: # # linuxq3apoint-1.32b-3.x86.run # (baseq3) # pak0.pk3 # (missionpack) # pak0.pk3 * # # Alternatively, if you already have the additional PK3 files from the patch, # you can include those on the storage device: # # (baseq3) # pak0.pk3 # pak1.pk3 # pak2.pk3 # pak3.pk3 # pak4.pk3 # pak5.pk3 # pak6.pk3 # pak7.pk3 # pak8.pk3 # (missionpack) # pak0.pk3 * # pak1.pk3 * # pak2.pk3 * # pak3.pk3 * # # * NOTE: The missionpack files are optional, and only required if you wish to # play Quake 3 TEAM Area. # # To mount a USB storage device, use the following commands: # sudo mkdir /mnt/usb # sudo mount /dev/sda1 /mnt/usb # # ./quake3.sh --source /mnt/usb # # To mount a Windows network share, use the following commands: # sudo mkdir /mnt/net # sudo mount -t cifs -o user=USER,password=PASSWORD //192.168.1.10/SHARE /mnt/net # # ./quake3.sh --source /mnt/net # ################################################################################# STARTDIR=$(pwd) HOMEDIR=$(echo ~) ################################################################################ QUAKE3_NAME="Quake 3" QUAKE3_ROOT=$HOMEDIR/quake3 QUAKE3_CONF=$HOMEDIR/.q3a QUAKE3_MAKE=$QUAKE3_ROOT/build_rpi_raspbian.sh QUAKE3_GAME=$QUAKE3_ROOT/build/release-linux-arm QUAKE3_EXEC=$QUAKE3_GAME/ioquake3.arm QUAKE3_SRCE=$HOMEDIR QUAKE3_GIT_URL=https://:@github.com/raspberrypi/quake3.git QUAKE3_PATCH_FILE=linuxq3apoint-1.32b-3.x86.run QUAKE3_PATCH_SIZE=30915710 QUAKE3_PATCH_LIST=( \ ftp://ftp.tw.freebsd.org/pub/distfiles/$QUAKE3_PATCH_FILE \ ftp://ftp.gamers.org/pub/idgames/idstuff/quake3/linux/$QUAKE3_PATCH_FILE \ ftp://ftp.filearena.net/.pub1/gentoo/distfiles/$QUAKE3_PATCH_FILE \ ftp://ftp.gameaholic.com/pub/mirrors/ftp.idsoftware.com/quake3/linux/$QUAKE3_PATCH_FILE \ ftp://ftp.demon.nl/pub/idsoftware/idstuff/quake3/linux/$QUAKE3_PATCH_FILE \ ftp://sourceforge.mirrorservice.org/sites/distfiles.gentoo.org/distfiles/$QUAKE3_PATCH_FILE \ ftp://ftp.idsoftware.com/idstuff/quake3/linux/$QUAKE3_PATCH_FILE \ ) QUAKE3_FLAG_1080P=0 QUAKE3_FLAG_TIMEDEMO=0 QUAKE3_ARGS_GAME="+set fs_game \"baseq3\"" QUAKE3_ARGS_VIDEO_QUALITY="+set r_vertexLight \"0\" +set r_lodbias \"0\" +set r_picmip \"0\" +set r_texturebits \"32\" +set r_textureMode \"GL_LINEAR_MIPMAP_LINEAR\"" QUAKE3_ARGS_VIDEO_FOV="+set cg_fov \"90\"" QUAKE3_ARGS_VIDEO_1080p="+set r_mode \"-1\" +set r_customwidth \"1920\" +set r_customheight \"1080\"" QUAKE3_ARGS_SOUND="+set s_initsound \"1\"" QUAKE3_ARGS_TIMEDEMO="+timedemo \"1\" +set demodone \"quit\" +set demoloop \"demo four; set nextdemo vstr demodone\" +vstr demoloop" ################################################################################ # # Prerequisite ($1) # # $1: Package Name # # Determines if a prerequisite package is already installed, and if not then # installs the missing prerequisite. # ################################################################################ Prerequisite() { installed=$(dpkg -s $1 2>&1 | grep -c "Status: install ok installed") if [ "$installed" -eq 0 ]; then echo -n 'Installing Prerequisite "'$1'" ... ' sudo apt-get install $1 -qq -y > /dev/null 2>&1 echo 'Done' fi } ################################################################################ # # GitClone ($1 $2 $3 $4) # # $1: Full GIT clone URL # $2: Source Name # $3: Source Path # $4: Makefile or build script # # Determines if the source code exists, and if not then performs a GIT Clone of # the specified code repository. # ################################################################################ GitClone() { if [ ! -d "$3" ] || [ ! -f "$4" ]; then echo -n 'Cloning GIT source "'$2'" ... ' git clone $1 > /dev/null 2>&1 if [ $? -eq 0 ]; then sed -i "s/-lvmcs_rpc_client//g" $QUAKE3_MAKE sed -i "s/-lEGL/-lbrcmEGL/g" $QUAKE3_MAKE sed -i "s/-lGLESv2/-lbrcmGLESv2/g" $QUAKE3_MAKE echo 'Done' else echo 'Failed' exit 1 fi fi } ################################################################################ # # Compile ($1 $2 $3 $4) # # $1: Source Name # $2: Source Path # $3: Makefile or build script # $4: Compiled executable # # Determines whether the compiled executable exists, and if not then performs a # build of the source code using the specified Makefile or build script. Reports # the time taken to perform the build. # ################################################################################ Compile() { if [ -d "$2" ] && [ -f "$3" ]; then if [ ! -x "$4" ]; then echo -n 'Compiling source "'$1'" ... ' cd $2 { time $3; } 2>&1 | awk '/^real\t.*s$/ { printf "%s\n", $2 }' fi else echo 'Source not found!' exit 1 fi } ################################################################################ # # Copy ($1 $2 $3) # # $1: Source Directory # $2: Destination directory # $3: Filename # # Determines whether a specified destination file exists, and if not then copies # the specified source file to the specified destination. The eXecute attribute # is removed from the destination file after copying. # ################################################################################ Copy() { if [ ! -f "$2/$3" ] && [ -f "$1/$3" ]; then echo -n 'Copying file to "'$2'/'$3'" ... ' cp "$1/$3" "$2/$3" chmod -x "$2/$3" echo 'Done' fi } ################################################################################ # # CopyPak3Files ($1 $2 $3) # # $1: Source Directory # $2: Destination Directory # $3: Count # # If the destination directory exists, attempt to copy the specified number of # PAK?.PK3 files from the source to destination directories. # ################################################################################ CopyPak3Files() { for (( i=0; i<=$3; i++ )); do Copy "$1" "$2" "pak$i.pk3" done } ################################################################################ # # Download ($1) # # $1: Source URLs # $2: Destination Path # $3: Source Name # # Determines whether the specified file exists, and if not then attempts to # download the file from a list of known source URLs. # ################################################################################ Download() { if [ ! -f "$2/$3" ]; then echo -n 'Downloading "'$3'" ... ' cd $2 urls=$1 for url in ${urls[@]}; do wget -q -t 1 $url if [ $? -eq 0 ] && [ -f "$2/$3" ]; then break fi if [ -f "$2/$3" ]; then rm "$2/$3"; fi done if [ -f "$2/$3" ]; then echo 'Done' return 0 else echo 'Failed' return 1 fi fi } ################################################################################ # # Patch () # # Determines whether the Quake 3 patch is required, and if so then obtains a # local copy of the patch file from either storage device (if available) or by # download. Once obtained, the patch file is extracted to a temporary directory, # the required files are moved to the game directories, and the temporary # directory is removed. # ################################################################################ Patch() { patch=0 # Check if the additional baseq3 PAK?.pk3 files exist (1-8) for i in {1..8}; do if [ ! -f "$QUAKE3_GAME/baseq3/pak$i.pk3" ]; then ((patch++)); fi done # Check if the additional missionpack PAK?.pk3 files exist (1-3) for i in {1..3}; do if [ ! -f "$QUAKE3_GAME/missionpack/pak$i.pk3" ]; then ((patch++)); fi done # Is patching required? if [ $patch -gt 0 ]; then # Attempt to copy patch file from local source Copy "$QUAKE3_SRCE" "$QUAKE3_ROOT" "$QUAKE3_PATCH_FILE" # Attempt to download patch file from known source list Download "$QUAKE3_PATCH_LIST" "$QUAKE3_ROOT" "$QUAKE3_PATCH_FILE" echo -n 'Patching from "'$QUAKE3_PATCH_FILE'" ... ' # Create a temporary directory TEMPDIR=$(mktemp -d) # Extract files from the patch archive into the temporary directory offset=$(head -n 355 "$QUAKE3_ROOT/$QUAKE3_PATCH_FILE" | wc -c | tr -d " ") for s in $QUAKE3_PATCH_SIZE do blocks=$(expr $s / 1024) bytes=$(expr $s % 1024) dd if="$QUAKE3_ROOT/$QUAKE3_PATCH_FILE" ibs=$offset skip=1 obs=1024 conv=sync 2> /dev/null | \ { test $blocks -gt 0 && dd ibs=1024 obs=1024 count=$blocks ; \ test $bytes -gt 0 && dd ibs=1 obs=1024 count=$bytes ; } 2> /dev/null | \ tar xzf - -C "$TEMPDIR" 2>&1 || \ { echo Failed > /dev/tty; kill -15 $$; } offset=$(expr $offset + $s) done # Move the updated PAK?.PK3 files into the game directories mv -f "$TEMPDIR/baseq3/"*.pk3 "$QUAKE3_GAME/baseq3/" mv -f "$TEMPDIR/missionpack/"*.pk3 "$QUAKE3_GAME/missionpack/" # Remove the temporary directory rm -rf "$TEMPDIR" echo 'Done' fi } ################################################################################ # # Launch () # # Determines whether the Quake 3 executable exists and whether the script is # being run from the local console session. If both conditions are true, then # build a command line string from the scripts' command line arguments and then # launch the game! # ################################################################################ Launch() { if [ -d "$QUAKE3_ROOT" ] && [ -x "$QUAKE3_EXEC" ]; then # Build Command Line Arguments QUAKE3_CMD_ARGS="$QUAKE3_ARGS_GAME $QUAKE3_ARGS_VIDEO_QUALITY $QUAKE3_ARGS_VIDEO_FOV $QUAKE3_ARGS_SOUND" if [ $QUAKE3_FLAG_1080P -eq 1 ]; then QUAKE3_CMD_ARGS="$QUAKE3_CMD_ARGS $QUAKE3_ARGS_VIDEO_1080p" fi if [ $QUAKE3_FLAG_TIMEDEMO -eq 1 ]; then QUAKE3_CMD_ARGS="$QUAKE3_CMD_ARGS $QUAKE3_ARGS_TIMEDEMO" fi # Launch Quake 3 if [ $QUAKE3_FLAG_TIMEDEMO -eq 1 ]; then $QUAKE3_EXEC $QUAKE3_CMD_ARGS 2>&1 | awk '/^[0-9]+ frames [0-9]+\.[0-9] seconds [0-9]+\.[0-9] fps / { printf "%s fps\n", $5 }' else $QUAKE3_EXEC $QUAKE3_CMD_ARGS fi else echo 'Quake 3 could not be found!' fi } ################################################################################ # # MAIN # ################################################################################ cd $HOMEDIR # Process Command Line Arguments for arg in "$@" do if [ "$arg" = "-n" ]; then key="$arg" else key=$(echo "${arg%%=*}") fi val=$(echo "${arg#*=}") case $key in --1080|--1080p) QUAKE3_FLAG_1080P=1 ;; -c|--clear) if [ -d "$QUAKE3_CONF" ]; then rm -fr "$QUAKE3_CONF" fi ;; -f|--fov) if [ $val -ge 0 ] && [ $val -le 160 ]; then QUAKE3_ARGS_VIDEO_FOV="+set cg_fov \"$val\"" else echo 'The value provided for Field-of-View is out of range (0-160, default is 90).' exit 1 fi ;; -g|--game) if [ -d "$QUAKE3_GAME/$val" ]; then QUAKE3_ARGS_GAME="+set fs_game \"$val\"" else echo 'The specified game directory does not exist: '$val exit 1 fi ;; -m|--mp|--missionpack|--q3ta) if [ -d "$QUAKE3_GAME/missionpack" ]; then QUAKE3_ARGS_GAME="+set fs_game \"missionpack\"" else echo 'The specified game directory does not exist: missionpack' fi ;; -n|--ns|--nosound|--soundoff) QUAKE3_ARGS_SOUND="+set s_initsound \"0\"" ;; -t|--td|--timedemo) QUAKE3_FLAG_TIMEDEMO=1 ;; -s|--source) if [ -f "$val/baseq3/pak0.pk3" ]; then QUAKE3_SRCE=$val else echo "Invalid source path" exit 1 fi ;; --help) echo 'Quake 3 script for Raspberry Pi' echo ' --1080p Forces the game engine to render at 1920 x 1080.' echo ' -c, --clear Clear the game configuration.' echo ' -f, --fov= Specifies value for Field-of-View (default 90).' echo ' -g, --game= Specifies the game directory (default "baseq3").' echo ' -m, --missionpack, --q3ta Launches the Team Arena mission pack add-on.' echo ' -n, --nosound Disables the game sound engine.' echo ' -s, --source= Specifies the path to the required source files.' echo ' -t, --timedemo Executes a timedemo benchmark of the FOUR demo' echo ' and returns a result in Frames Per Second (FPS).' echo ' --help Display this help and exit.' echo echo 'Git: ' echo 'Web: ' exit 1 ;; *) echo 'Invalid argument: '$arg exit 1 ;; esac done # Install Prerequisites Prerequisite "git" Prerequisite "libsdl1.2-dev" # Perform a GIT Clone of the Quake 3 source GitClone "$QUAKE3_GIT_URL" "$QUAKE3_NAME" "$QUAKE3_ROOT" "$QUAKE3_MAKE" # Compile the Quake 3 source Compile "$QUAKE3_NAME" "$QUAKE3_ROOT" "$QUAKE3_MAKE" "$QUAKE3_EXEC" # Copy the PAK?.PK3 files to the game directories CopyPak3Files "$QUAKE3_SRCE/baseq3" "$QUAKE3_GAME/baseq3" 8 CopyPak3Files "$QUAKE3_SRCE/missionpack" "$QUAKE3_GAME/missionpack" 3 # Patch the game Patch # Launch the game Launch cd $STARTDIR ################################################################################