#!/bin/bash
#-----------------------------------------------------
# Demoplay v0.5
#-----------------------------------------------------
# Script written by Andreas Textor
# Original Q3 Config by Kevin `OomJan' Gardthausen
#           (from some windows demo tool i think)
# Contact: texray@fuckMicrosoft.com 
# Website: http://texray.no-ip.org/
#
# Purpose: Select & Play Quake 3 Demos easily
#          run demoplay --help or demoplay -h for help
#          and instructions
#-----------------------------------------------------
# Demo Controls:
# --------------
# Left Arrow -> Decrease Playback Speed by 0.1 below 1.0 and by halve above 1.0 (Minimum 0.0)
# Right Arrow -> Increase Playback Speed by 0.1 below 1.0 and doubles above 1.0 (Maximum 128.0)
# Up Arrow -> Normal Playback Speed (1.0)
# Down Arrow -> Freezes demo (pictures freezes, but demo continues at slow pace)
# Numpad 0 -> Quit Game
# Numpad 1 -> 3rd Person left pan
# Numpad 2 -> Toggle Timer
# Numpad 3 -> 3rd Person right pan
# NumPad 4 -> Restart Demo
# Numpad 5 -> Toggle Third-person mode
# Numpad 6 -> Take Sceenshot
# Numpad 7 -> Decrease in game Volume
# Numpad 8 -> Mute Volume
# Numpad 9 -> Increase in game Volume
# Numpad Minus (-) -> Display Score (where possible)
# Home -> Decrease 3rd Person Range
# End -> Increase 3rd Person Range
#
# Furthermore:
# If `enableavi' is true (default false, see below):
#    F1 -> Record at 25 frames per second
#    F2 -> Stop recording

###################################
# Block: Setting Up Variables
###################################

# change these settings if necessary!!
higfx="false"  # self-explanatory
enableavi="false"
demopath="$HOME/.q3a/osp/demos"
quake3="quake3"
myconfig="/usr/local/games/quake3/baseq3/autoexec.cfg"

# do not change these settings!
tempfile="$HOME/tmp$$"
tempfile2="$HOME/tmp2$$"
dirprefix="DIR-"
fileprefix="...-"
running="1"
backtitle="Demoplay 0.5 (http://texray.no-ip.org/)"
header="`pwd`"
ls="`which ls`"
dialog="`which dialog`"
dialogrows=30
dialogcols=100
dialoginner=23

###################################
# Block: Functions
###################################

function DisplayHelp
{
 #
 # Display Programs Help
 #
cat << EOF
Demoplay v0.5 - Select & Play Quake 3 Demos easily
Written by Andreas Textor. 
(texray@gmx.de   http://texray.kantico.de)

This script requires the dialog package to display a menu
and a running installation of Quake 3 with OSP.
Copy your demos to $HOME/.q3a/osp/demos.

Usage: demoplay /home/me/justdownloadeddemo.dm_68
or simply run demoplay to display a selection-menu.

Controls:
 Left Arrow -> Decrease Playback Speed by 0.1 below 1.0 and by halve above 1.0 (Minimum 0.0)
 Right Arrow -> Increase Playback Speed by 0.1 below 1.0 and doubles above 1.0 (Maximum 128.0)
 Up Arrow -> Normal Playback Speed (1.0)
 Down Arrow -> Freezes demo (pictures freezes, but demo continues at slow pace)
 Numpad 0 -> Quit Game
 Numpad 1 -> 3rd Person left pan
 Numpad 2 -> Toggle Timer
 Numpad 3 -> 3rd Person right pan
 NumPad 4 -> Restart Demo
 Numpad 5 -> Toggle Third-person mode
 Numpad 6 -> Take Sceenshot
 Numpad 7 -> Decrease in game Volume
 Numpad 8 -> Mute Volume
 Numpad 9 -> Increase in game Volume
 Numpad Minus (-) -> Display Score (where possible)
 Home -> Decrease 3rd Person Range
 End -> Increase 3rd Person Range
 If "enableavi" is true (default false, see below):
    F1 -> Record at 25 frames per second
    F2 -> Stop recording

Settings:
 Open the demoplay script file and edit the settings if necessary:
 - config   : set the default q3 config (default: /usr/local/games/quake3/baseq3/autoexec.cfg)
 - higfx    : determine wether the graphics settings in the default q3 config file should
              be overridden with highgfx settings.
 - enableavi: enable this setting to bind the cl_avidemo command to F1 and F2 keys.
              (use this function to create movies from q3demos.)

Troubleshooting:
 If you can't use the numpad-keys without cancelling the demo, try
 installing the numlockx package. Demoplay will use this program to
 switch off numlock, enabling the keys in q3a.
EOF
 exit
}

function scandir {
 
 count=1
 filename[1]=".."
 files=`$ls`
 i=0

 # select all files.
 for i in $files; do
  prefix=$fileprefix  
  if [ -z "$tmp" ]; then
   name="$i"
  else
   name="$tmp $i"
  fi

  let count=count+1;
	         
  # make sure it is a file or a directory
  if [ -e "$name" ]; then
   if [ -d "$name" ]; then
    prefix=$dirprefix
   fi
   filename[$count]="$name"
   tmp=
   # generate selection list
   echo -e -n "$count $prefix\"$(echo ${filename[$count]} | tr ' ' '_')\" " >> $tempfile
  else
   let count=count-1;
   tmp="$name"
  fi

 done
}

function quit {
 # clean up
 if [ -e "$tempfile" ]; then
  rm "$tempfile" 
 fi
 if [ -e "$tempfile2" ]; then
  rm "$tempfile2"
 fi
 exit 1
}

function createCfg {
if [ -e "$myconfig" ]; then
 cat "$myconfig" > $HOME/.q3a/osp/demoplay.cfg
fi

cat << EOF >> $HOME/.q3a/osp/demoplay.cfg
unbindall

// Playback Controls
bind leftarrow "vstr seis_speed-"
bind rightarrow "vstr seis_speed+"
bind downarrow "vstr seis_speed_0.0"
bind uparrow "vstr seis_speed_1.0"
set seis_speed- "vstr seis_speed_0.9"
set seis_speed+ "vstr seis_speed_2.0"
set seis_speed_0.0 "set timescale 0.0001; cl_freezedemo 1; set seis_speed- vstr seis_speed_0.0; set seis_speed+ vstr seis_speed_0.1;"
set seis_speed_0.1 "set timescale 0.1; cl_freezedemo 0; set seis_speed- vstr seis_speed_0.0; set seis_speed+ vstr seis_speed_0.2;"
set seis_speed_0.2 "set timescale 0.2; cl_freezedemo 0; set seis_speed- vstr seis_speed_0.1; set seis_speed+ vstr seis_speed_0.3;"
set seis_speed_0.3 "set timescale 0.3; cl_freezedemo 0; set seis_speed- vstr seis_speed_0.2; set seis_speed+ vstr seis_speed_0.4;"
set seis_speed_0.4 "set timescale 0.4; cl_freezedemo 0; set seis_speed- vstr seis_speed_0.3; set seis_speed+ vstr seis_speed_0.5;"
set seis_speed_0.5 "set timescale 0.5; cl_freezedemo 0; set seis_speed- vstr seis_speed_0.4; set seis_speed+ vstr seis_speed_0.6;"
set seis_speed_0.6 "set timescale 0.6; cl_freezedemo 0; set seis_speed- vstr seis_speed_0.5; set seis_speed+ vstr seis_speed_0.7;"
set seis_speed_0.7 "set timescale 0.7; cl_freezedemo 0; set seis_speed- vstr seis_speed_0.6; set seis_speed+ vstr seis_speed_0.8;"
set seis_speed_0.8 "set timescale 0.8; cl_freezedemo 0; set seis_speed- vstr seis_speed_0.7; set seis_speed+ vstr seis_speed_0.9;"
set seis_speed_0.9 "set timescale 0.9; cl_freezedemo 0; set seis_speed- vstr seis_speed_0.8; set seis_speed+ vstr seis_speed_1.0;"
set seis_speed_1.0 "set timescale 1.0; cl_freezedemo 0; set seis_speed- vstr seis_speed_0.9; set seis_speed+ vstr seis_speed_2.0;"
set seis_speed_2.0 "set timescale 2.0; cl_freezedemo 0; set seis_speed- vstr seis_speed_1.0; set seis_speed+ vstr seis_speed_4.0;"
set seis_speed_4.0 "set timescale 4.0; cl_freezedemo 0; set seis_speed- vstr seis_speed_2.0; set seis_speed+ vstr seis_speed_8.0;"
set seis_speed_8.0 "set timescale 8.0; cl_freezedemo 0; set seis_speed- vstr seis_speed_4.0; set seis_speed+ vstr seis_speed_16.0;"
set seis_speed_16.0 "set timescale 16.0; cl_freezedemo 0; set seis_speed- vstr seis_speed_8.0; set seis_speed+ vstr seis_speed_32.0;"
set seis_speed_32.0 "set timescale 32.0; cl_freezedemo 0; set seis_speed- vstr seis_speed_16.0; set seis_speed+ vstr seis_speed_64.0;"
set seis_speed_64.0 "set timescale 64.0; cl_freezedemo 0; set seis_speed- vstr seis_speed_32.0; set seis_speed+ vstr seis_speed_128.0;"
set seis_speed_128.0 "set timescale 128.0; cl_freezedemo 0; set seis_speed- vstr seis_speed_64.0; set seis_speed+ vstr seis_speed_128.0;"

// Quits Game
bind kp_ins "quit;"

// Show Scores (Where possible)
bind kp_minus "+scores"

// Restarts Demo
bind kp_leftarrow "vstr seis_PlayCurrentDemo;"

// Takes Screenshot
bind kp_rightarrow "screenshot;"
bind f12 "screenshotJPEG"

// Toggles Timer
set seis_timer_enable "cg_drawtimer 1; bind kp_downarrow "vstr seis_timer_disable;"
set seis_timer_disable "cg_drawtimer 0; bind kp_downarrow "vstr seis_timer_enable;"
bind kp_downarrow "vstr seis_timer_enable;"

// Toggles 3rd Person mode
set seis_third_disable "cg_thirdperson 0; bind kp_5 vstr seis_third_enable;"
set seis_third_enable "cg_thirdperson 1; bind kp_5 vstr seis_third_disable; bind kp_end vstr seis_pl; bind kp_pgdn vstr seis_pr; bind home vstr seis_Range-; bind end vstr seis_Range+;"
bind kp_5 "vstr seis_third_enable"

// 3rd Person Panning
bind kp_end ""
bind kp_pgdn ""
set seis_pl "vstr seis_A350;"
set seis_pr "vstr seis_A_10;"
set seis_A__0 "cg_thirdpersonangle   0; set seis_pl vstr seis_A350; set seis_pr vstr seis_A_10"
set seis_A_10 "cg_thirdpersonangle  10; set seis_pl vstr seis_A__0; set seis_pr vstr seis_A_20"
set seis_A_20 "cg_thirdpersonangle  20; set seis_pl vstr seis_A_10; set seis_pr vstr seis_A_30"
set seis_A_30 "cg_thirdpersonangle  30; set seis_pl vstr seis_A_20; set seis_pr vstr seis_A_40"
set seis_A_40 "cg_thirdpersonangle  40; set seis_pl vstr seis_A_30; set seis_pr vstr seis_A_50"
set seis_A_50 "cg_thirdpersonangle  50; set seis_pl vstr seis_A_40; set seis_pr vstr seis_A_60"
set seis_A_60 "cg_thirdpersonangle  60; set seis_pl vstr seis_A_50; set seis_pr vstr seis_A_70"
set seis_A_70 "cg_thirdpersonangle  70; set seis_pl vstr seis_A_60; set seis_pr vstr seis_A_80"
set seis_A_80 "cg_thirdpersonangle  80; set seis_pl vstr seis_A_70; set seis_pr vstr seis_A_90"
set seis_A_90 "cg_thirdpersonangle  90; set seis_pl vstr seis_A_80; set seis_pr vstr seis_A100"
set seis_A100 "cg_thirdpersonangle 100; set seis_pl vstr seis_A_90; set seis_pr vstr seis_A110"
set seis_A110 "cg_thirdpersonangle 110; set seis_pl vstr seis_A100; set seis_pr vstr seis_A120"
set seis_A120 "cg_thirdpersonangle 120; set seis_pl vstr seis_A110; set seis_pr vstr seis_A130"
set seis_A130 "cg_thirdpersonangle 130; set seis_pl vstr seis_A120; set seis_pr vstr seis_A140"
set seis_A140 "cg_thirdpersonangle 140; set seis_pl vstr seis_A130; set seis_pr vstr seis_A150"
set seis_A150 "cg_thirdpersonangle 150; set seis_pl vstr seis_A140; set seis_pr vstr seis_A160"
set seis_A160 "cg_thirdpersonangle 160; set seis_pl vstr seis_A150; set seis_pr vstr seis_A170"
set seis_A170 "cg_thirdpersonangle 170; set seis_pl vstr seis_A160; set seis_pr vstr seis_A180"
set seis_A180 "cg_thirdpersonangle 180; set seis_pl vstr seis_A170; set seis_pr vstr seis_A190"
set seis_A190 "cg_thirdpersonangle 190; set seis_pl vstr seis_A180; set seis_pr vstr seis_A200"
set seis_A200 "cg_thirdpersonangle 200; set seis_pl vstr seis_A190; set seis_pr vstr seis_A210"
set seis_A210 "cg_thirdpersonangle 210; set seis_pl vstr seis_A200; set seis_pr vstr seis_A220"
set seis_A220 "cg_thirdpersonangle 220; set seis_pl vstr seis_A210; set seis_pr vstr seis_A230"
set seis_A230 "cg_thirdpersonangle 230; set seis_pl vstr seis_A220; set seis_pr vstr seis_A240"
set seis_A240 "cg_thirdpersonangle 240; set seis_pl vstr seis_A230; set seis_pr vstr seis_A250"
set seis_A250 "cg_thirdpersonangle 250; set seis_pl vstr seis_A240; set seis_pr vstr seis_A260"
set seis_A260 "cg_thirdpersonangle 260; set seis_pl vstr seis_A250; set seis_pr vstr seis_A270"
set seis_A270 "cg_thirdpersonangle 270; set seis_pl vstr seis_A260; set seis_pr vstr seis_A280"
set seis_A280 "cg_thirdpersonangle 280; set seis_pl vstr seis_A270; set seis_pr vstr seis_A290"
set seis_A290 "cg_thirdpersonangle 290; set seis_pl vstr seis_A280; set seis_pr vstr seis_A300"
set seis_A300 "cg_thirdpersonangle 300; set seis_pl vstr seis_A290; set seis_pr vstr seis_A310"
set seis_A310 "cg_thirdpersonangle 310; set seis_pl vstr seis_A300; set seis_pr vstr seis_A320"
set seis_A320 "cg_thirdpersonangle 320; set seis_pl vstr seis_A310; set seis_pr vstr seis_A330"
set seis_A330 "cg_thirdpersonangle 330; set seis_pl vstr seis_A320; set seis_pr vstr seis_A340"
set seis_A340 "cg_thirdpersonangle 340; set seis_pl vstr seis_A330; set seis_pr vstr seis_A350"
set seis_A350 "cg_thirdpersonangle 350; set seis_pl vstr seis_A340; set seis_pr vstr seis_A__0"

// 3rd Person Range
bind home ""
bind end ""
set seis_Range- "vstr seis_R_40"
set seis_Range+ "vstr seis_R_60"
set seis_R_20 "cg_thirdpersonrange  20; set seis_Range- vstr seis_R_20; set seis_Range+ vstr seis_R_40"
set seis_R_40 "cg_thirdpersonrange  30; set seis_Range- vstr seis_R_20; set seis_Range+ vstr seis_R_60"
set seis_R_60 "cg_thirdpersonrange  40; set seis_Range- vstr seis_R_40; set seis_Range+ vstr seis_R_80"
set seis_R_80 "cg_thirdpersonrange  60; set seis_Range- vstr seis_R_40; set seis_Range+ vstr seis_R100"
set seis_R100 "cg_thirdpersonrange  80; set seis_Range- vstr seis_R_60; set seis_Range+ vstr seis_R120"
set seis_R120 "cg_thirdpersonrange 100; set seis_Range- vstr seis_R_80; set seis_Range+ vstr seis_R140"
set seis_R140 "cg_thirdpersonrange 120; set seis_Range- vstr seis_R100; set seis_Range+ vstr seis_R160"
set seis_R160 "cg_thirdpersonrange 140; set seis_Range- vstr seis_R120; set seis_Range+ vstr seis_R180"
set seis_R180 "cg_thirdpersonrange 160; set seis_Range- vstr seis_R140; set seis_Range+ vstr seis_R200"
set seis_R200 "cg_thirdpersonrange 180; set seis_Range- vstr seis_R160; set seis_Range+ vstr seis_R200"

// Volume Bind
bind kp_pgup "vstr seis_vol+"
bind kp_home "vstr seis_vol-"
bind kp_uparrow "set s_volume 0.0; set seis_vol- vstr seis_vol+-; set seis_vol+ vstr seis_vol01;"
set seis_vol- "vstr seis_vol03;"
set seis_vol+ "vstr seis_vol05;"
set seis_vol+- "set s_volume 0.0; set seis_vol- vstr seis_vol+-; set seis_vol+ vstr seis_vol01;"
set seis_vol01 "set s_volume 0.2; set seis_vol- vstr seis_vol+-; set seis_vol+ vstr seis_vol02;"
set seis_vol02 "set s_volume 0.4; set seis_vol- vstr seis_vol01; set seis_vol+ vstr seis_vol03;"
set seis_vol03 "set s_volume 0.6; set seis_vol- vstr seis_vol02; set seis_vol+ vstr seis_vol04;"
set seis_vol04 "set s_volume 0.8; set seis_vol- vstr seis_vol03; set seis_vol+ vstr seis_vol05;"
set seis_vol05 "set s_volume 1.0; set seis_vol- vstr seis_vol04; set seis_vol+ vstr seis_vol06;"
set seis_vol06 "set s_volume 1.2; set seis_vol- vstr seis_vol05; set seis_vol+ vstr seis_vol07;"
set seis_vol07 "set s_volume 1.4; set seis_vol- vstr seis_vol06; set seis_vol+ vstr seis_vol08;"
set seis_vol08 "set s_volume 1.6; set seis_vol- vstr seis_vol07; set seis_vol+ vstr seis_vol09;"
set seis_vol09 "set s_volume 1.8; set seis_vol- vstr seis_vol08; set seis_vol+ vstr seis_vol10;"
set seis_vol10 "set s_volume 2.0; set seis_vol- vstr seis_vol09; set seis_vol+ vstr seis_vol10;"

seta cg_NoMOTDDisplayDuringDemo "1"
EOF

if [ $higfx = "true" ]; then
cat << EOF >> $HOME/.q3a/osp/demoplay.cfg
set r_picmip "0"
set r_vertexlight "0"
set r_mode "3"
set r_texturemode "gl_linear_mipmap_nearest"
set r_depthbits "16"
set r_colorbits "16"
set r_texturebits "16"
set r_lodbias "0"
set r_subdivisions "4"
set cg_simpleitems "0"
EOF
fi

if [ $enableavi = "true" ]; then
cat << EOF >> $HOME/.q3a/osp/demoplay.cfg
// Record
bind F1 "cl_avidemo 25"

// Stop record
bind F2 "cl_avidemo 0"
EOF
fi
}

###################################
# Block: Main Program
###################################

# check for request for help
if [ "$1" = "--help" -o "$1" = "-h" ]; then
 DisplayHelp
fi

# check for direct-play params
if [ $# -eq 1 ]; then
  if [ -e "$HOME/.q3a/osp/demoplay.cfg" ];then
   rm $HOME/.q3a/osp/demoplay.cfg
  fi
  createCfg
  
  if [ ! -d $HOME/.q3a/baseq3/demos ]; then
   mkdir -p $HOME/.q3a/baseq3/demos
  fi
  cp "$1" $HOME/.q3a/baseq3/demos
  
  echo -e -n "DEMOPLAY: $quake3 +exec demoplay.cfg +set fs_game osp +demo ${1##*/}\n"
  $quake3 +exec demoplay.cfg +set fs_game osp +demo ${1##*/}
  echo -e -n "DEMOPLAY: thank you for using demoplay. this has been ${1##*/}.\n"
  rm $HOME/.q3a/baseq3/demos/${1##*/}
  quit
fi

# some problem-prevention
if [ ! -e $dialog ]; then
 echo -e -n "\nThis program requires the dialog package.\n"
 echo -e -n "Please install dialog and restart the program.\n"
 quit
fi

if [ ! `tput cols` -ge $dialogcols -o ! `tput lines` -ge $dialogrows ]; then
 echo -e -n "\nYour terminal seems to be too small. Please resize it and start the program again.\n"
 quit
fi

if [ "`$ls -l $demopath`" = "total 0" ]; then
 $dialog --backtitle "$backtitle" --msgbox "No demos were found in $HOME/.q3a/osp/demos. \
 Please make sure they are in the right directory." $dialogrows $dialogcols
 quit
fi

# start normal selection menu
cd "$demopath"
while [ running != "0" ]; do
 scandir
 header=`pwd`
 # display menu
 if [ "$header" != "$demopath" ]; then
  $dialog --backtitle "$backtitle" --menu "demos${header##*$demopath}" \
  $dialogrows $dialogcols $dialoginner 1 $dirprefix.. `cat $tempfile` 2>$tempfile2
 else
  $dialog --backtitle "$backtitle" --menu "demos${header##*$demopath}" \
  $dialogrows $dialogcols $dialoginner `cat $tempfile` 2>$tempfile2
 fi

 c=$(cat $tempfile2)
 
 if [ -z $c ]; then
  echo -e -n "Demoplay cancelled by `whoami`.\n"
  quit
 fi

 if [ -d "${filename[$c]}" ]; then
  rm "$tempfile" "$tempfile2" 
  count=0
  cd "${filename[$c]}"
  c=
 else
  if [ -e "$HOME/.q3a/osp/demoplay.cfg" ];then
   rm $HOME/.q3a/osp/demoplay.cfg
  fi
  createCfg
  
  # do something with the chosen file.
  finalpath=${header##*$demopath/}
  if [ "$finalpath" = "$demopath" ]; then
   finalpath=
  else
   finalpath=$finalpath/
  fi
  # check for numlockx - if numlock is enabled, the KP-Block won't work.
  [ -x "`which numlockx`" ] && numlockx off
  echo -e -n "DEMOPLAY: $quake3 +exec demoplay.cfg +set fs_game osp +demo $finalpath${filename[$c]}\n"
  $quake3 +exec demoplay.cfg +set fs_game osp +demo $finalpath${filename[$c]}
  echo -e -n "DEMOPLAY: thank you for using demoplay. this has been ${filename[$c]}.\n"
  [ -x "`which numlockx`" ] && numlockx on
  quit
 fi

done

quit

