################################################################################
#
# This Tcl script uses the "cdtool-1.0" package to add CD audio support to
# the Linux version of id's game Quake. It reads commands from the stdin
# channel and calls cdir, cdplay, cdpause, cdstop and cdeject, respectively.
# The following commands are currently understood:
#   "play start" -> play track assigned to the Introduction level (auto repeat)
#   "play exit"  -> play track assigned to the end of each level (auto repeat)
#   "play end"   -> play track assigned to Shub-Niggurath's Pit (auto repeat)
#   "play eNmM"  -> play track assigned to this SP level (auto repeat)
#   "play dmN"   -> play track assigned to this DM level (auto repeat)
#   "play demoN" -> play track assigned to this demo (auto repeat)
#   "play #N"    -> play track number N (once)
#   "play +N"    -> play track number N (auto-repeat)
#   "pause"      -> pause CD player
#   "unpause"    -> resume CD player
#   "off"        -> disable CD player
#   "on"         -> re-enable CD player
#   "reset"      -> re-read the CD directory and trackassign.tcl
#   "eject"      -> eject the currently inserted CD
#   "info"       -> show info on currently inserted CD
#   "current"    -> display currently played track
#   "stop"       -> stop cd player and exit this program
#   "test"       -> check if playcd is running
#
# (C) Copyright 1996 Michael Schumacher (mike@hightec.saarlink.de)
#
################################################################################

# set verbose to 1 if you want to see which track is currently played

set verbose 0

################################################################################
#
# Don't edit stuff below (unless you know exactly whatcha doing... ;)
#
################################################################################

proc handle_input { desc } {
  global track tracktime curtrack path cdstate verbose autorepeat cd_enabled

  gets $desc line
  if { [eof $desc] } {
    set line stop
  }
  set cmd [lindex $line 0]
  set special 0
  switch $cmd {
    on {
      if { !$cd_enabled } {
        init_cd
      }
      set cd_enabled 1
    }
    off {
      after cancel repeat_track
      catch {exec $path/cdstop}
      set cd_enabled 0
    }
    play {
      if { !$cd_enabled } {
	return
      }
      if { "$cdstate" == "fail" } {
	init_cd
	if { "$cdstate" == "fail" } {
	  return
	}
      }
      if { "[string index [lindex $line 1] 0]" == "#" } {
	set wanttrack [string range [lindex $line 1] 1 end]
	set autorepeat 0
	set special 1
      } elseif { "[string index [lindex $line 1] 0]" == "+" } {
	set wanttrack [string range [lindex $line 1] 1 end]
	set autorepeat 1
	set special 1
      } else {
	set wanttrack [lindex $line 1]
      }
      if { $special } {
	if { [info exists tracktime($wanttrack)] } {
	  if { "$curtrack" == "$wanttrack" } {
	    if { $verbose } {
	      if { !$autorepeat } {
		puts "playcd: stopping music when track #$curtrack is finished."
	      } else {
	        puts "playcd: (re-)enabling auto-repeat for track #$curtrack."
	      }
	    }
	    return
	  } else {
	    set curtrack $wanttrack
	  }
	} else {
	  puts "playcd: unknown track #$wanttrack, using #2."
	  set curtrack 2
	}
      } else {
	if { [info exists track($wanttrack)] } {
	  if { "$curtrack" == "$track($wanttrack)" } {
	    if { $verbose } {
	      puts "playcd: already playing track $curtrack."
	    }
	    return
	  } else {
	    set curtrack $track($wanttrack)
	  }
	} else {
	  puts "playcd: unknown track number for <$wanttrack>, using #2."
	  set curtrack 2
	}
	if { ![info exists tracktime($curtrack)] && ("$curtrack" != "none") } {
	  puts "playcd: unknown track number <$curtrack>, ignored."
	}
	set autorepeat 1
      }
      if { $verbose } {
	if { $special } {
	  if { $autorepeat } {
	    puts "playcd: playing track #$curtrack (auto-repeat)."
	  } else {
	    puts "playcd: playing track #$curtrack (once)."
	  }
	} else {
	  puts "playcd: playing track $curtrack for $wanttrack (auto-repeat)."
	}
      }
      if { "$curtrack" == "none" } {
        catch {exec $path/cdstop}
      } else {
        catch {exec $path/cdplay $curtrack $curtrack}
	if { $autorepeat } {
          catch {after $tracktime($curtrack) repeat_track}
	}
      }
      set cdstate playing
    }
    pause {
      if { ("$cdstate" != "playing") || !$cd_enabled } return
      catch {exec $path/cdpause}
      set cdstate paused
    }
    unpause {
      if { ("$cdstate" != "paused") || !$cd_enabled } return
      catch {exec $path/cdplay}
      set cdstate playing
    }
    info {
      if { [catch {exec $path/cdir} res] } {
	puts "playcd: \"cdir\" failed; reason:\n$res"
	set cdstate fail
	return
      }
      puts $res
    }
    current {
      if { "$cdstate" == "fail" } {
	puts "playcd: CD player is currently not activated."
      } elseif { ($curtrack == -1) || !$cd_enabled } {
        puts "playcd: no track selected; maybe CD player is not initialized."
      } else {
	if { $autorepeat } {
	  puts "playcd: currently playing track #$curtrack (auto-repeat)."
	} else {
	  puts "playcd: currently playing track #$curtrack (once)."
	}
      }
    }
    reset {
      init_cd
    }
    eject {
      if { "$cdstate" != "fail" } {
	puts "playcd: latest track was #$curtrack."
      }
      if { [catch {exec $path/cdeject} res] } {
	puts "playcd: eject failed; reason:\n$res"
      }
      set cdstate fail
    }
    stop {
      if { "$cdstate" != "fail" } {
        catch {exec $path/cdstop}
      }
      after cancel repeat_track
      exit
    }
    test {
      # dummy function to check if playcd is up and running
    }
    default {
      puts "playcd: unknown command <$line>, ignored."
    }
  }
}

proc init_cd {} {
  global path cdstate tracktime curtrack track autorepeat cd_enabled

  # (re)load the level/track assignments and reset the CD player
  after cancel repeat_track
  catch {exec $path/cdstop}
  catch {unset track}
  set autorepeat 1
  set cd_enabled 1
  set cdstate ok
  set curtrack -1 
  set num {[0-9]+}
  set ws "\[ \t\]*"

  if { [catch {source $path/trackassign.tcl} res] } {
    puts "playcd: couldn't source $path/trackassign.tcl; reason:\n$res"
    set cdstate fail
    return
  }
  if { [catch {exec $path/cdir} res] } {
    puts "playcd: init_cd failed; reason:\n$res"
    set cdstate fail
    return
  }

  foreach title [lrange [split $res \n] 1 end] {
    if { [regexp "$ws\($num):($num)$ws\($num)" $title {} min sec trk] } {
      if { "[set sec [string trimleft $sec 0]]" == "" } { set sec 0 }
      set tracktime($trk) [expr ($min * 60 + $sec) * 1000]
    }
  }
}

proc repeat_track {} {
  global path curtrack tracktime cdstate autorepeat

  if { "$cdstate" == "paused" } {
    after 250 repeat_track
    return
  }

  if { [catch {exec $path/cdir} res] } {
    puts "playcd: repeat_track failed; reason:\n$res"
    set cdstate fail
    return
  }

  if { [regexp {\[PLAYING|PAUSED\]} $res] } {
    after 1000 repeat_track
  } else {
    if { $autorepeat } {
      catch {after cancel repeat_track}
      catch {exec $path/cdplay $curtrack $curtrack}
      catch {after $tracktime($curtrack) repeat_track}
    } else {
      set curtrack -1
    }
  }
}

if { ![info exists env(LXQCD_BINPATH)] } {
  set env(LXQCD_BINPATH) [pwd]
}
set path $env(LXQCD_BINPATH)
init_cd
fileevent stdin readable "handle_input stdin"
#
# and now a subtle joke for the Tcl-aware Quakers...  :^)
#
vwait Shub-Niggurath

### EOF ########################################################################
