#!/bin/bash

# Launch LOTRO client from CLI.
#
# To be used
#	with cygwin on windows if you just can't stand .NET
#    or with wine/cedega under GNU/Linux or *BSD
# to play The Lord of the Rings Online: Shadows of Angmar.
#
#
# (C) 2007-2011 SNy <SNy@bmx-chemnitz.de>
# (C) 2020 Dong <dong115@uwindsor.ca>
#
# Modded to take command line args and windoze use and by AtomicMew 6/3/12
# Port & adapt to dnd64client.exe by Dong 8/10/20

########### SETUP

# options for wget etc. and for starting the game, add everything you need here
wgetOptions="--no-check-certificate -q"

########### START
myaccount="youraccount"  # change as needed
mypass="yourpass"        # change as needed
myserver="7"             # change as needed
characterName="yourname" # change as needed        

account=${1:-$myaccount}
pass=${2:-$mypass}
selectedServer=${3:-$myserver}

echo -e "\nWelcome to the CLI launcher for LOTRO/DDO modded v 0.0000001\n"

# official launcher config file
configFile="ddo.launcherconfig"

# make this script be callable from elsewhere (desktop shortcuts etc)
# change directory to where the launcher resides
oldDir=`pwd`
gameDir="~/Game/DDO"  # change as needed
cd "$gameDir"

# cleanup temp directory for configuration files downloaded from the official servers
rm -rf .launcher
mkdir .launcher

# launcher config file starts it all, get Game and DataCenter settings
if ! [ -r $configFile ] ; then
    echo -e "\nError: $configFile cannot be read.\n"
    cd "$oldDir"
    read dummy
    exit
fi

echo "Reading launcher configuration..."
glsDataCenter_URL=`grep -h "Launcher.DataCenterService.GLS" $configFile | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
game=`grep -h "DataCenter.GameName" $configFile | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`

# get configuration info (xml-file containing auth, patch and game servers with their corresponding settings)
# NOTE: while a normal HTTP GET to /GLS.DataCenterServer/Service.asmx/GetDatacenters?game=LOTROEU
#       works fine for the european datacenter, it does not work for the US/AU/... LOTRO one
# 	instead, we need to send a SOAP request there, ending up with a SOAP answer (no whitespace whatsoever)
# 	now, to have at least some whitespace we can deal with, sed is used to insert a newline after each closing xml tag below
wget $wgetOptions \
    --header 'Content-Type: text/xml; charset=utf-8' \
    --header 'SOAPAction: "http://www.turbine.com/SE/GLS/GetDatacenters"' \
    --post-data "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><soap:Body><GetDatacenters xmlns=\"http://www.turbine.com/SE/GLS\"><game>${game}</game></GetDatacenters></soap:Body></soap:Envelope>" \
    "${glsDataCenter_URL}" -O .launcher/GLSDataCenter.config
if ! [ -r .launcher/GLSDataCenter.config ] ; then
    echo -e "\nError: Could not fetch GLS data center configuration.\n"
    cd "$oldDir"
    read dummy
    exit
fi
# insert the whitespace after closing xml tags here
sed -e "s#\(</[^>]*>\)#\1\n#g" -i .launcher/GLSDataCenter.config
# skip all datacenters but the one we are adressing
# precaution as the DDO datacenter config contains more than one <Datacenter> section which breaks things
echo -e "<!--\n  NOTE\n  This file is NOT a valid XML file!\n-->" >> .launcher/GLSDataCenter.config.${game}
cat .launcher/GLSDataCenter.config | sed -n -e "/^.*<Datacenter><Name>${game}<\/Name>/,/<\/Datacenter>.*$/ p" >> .launcher/GLSDataCenter.config.${game}

# get dynamic launcher settings
patchServer_ADR=`grep -h "<PatchServer>" .launcher/GLSDataCenter.config.${game} | grep -v '<!--.*-->' | sed -e "s/^.*<PatchServer>//;s/<\/PatchServer>.*$//"`
launcherCfg_URL=`grep -h "<LauncherConfigurationServer>" .launcher/GLSDataCenter.config.${game} | grep -v '<!--.*-->' | sed -e "s/^.*<LauncherConfigurationServer>//;s/<\/LauncherConfigurationServer>.*$//"`
wget $wgetOptions "${launcherCfg_URL}" -O .launcher/launcher.config
if ! [ -r .launcher/launcher.config ] ; then
    echo -e "\nError: Could not fetch dynamic launcher configuration.\n"
    cd "$oldDir"
    read dummy
    exit
fi

# extract game settings from launcher settings
worldQueue_URL=`grep -h "WorldQueue.LoginQueue.URL" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
worldQueue_ARGTMPL=`grep -h "WorldQueue.TakeANumber.Parameters" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
gameClient_FILE=`grep -h "GameClient.WIN64.Filename" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
gameClient_ARGTMPL=`grep -h "GameClient.OSX.ArgTemplate" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`

# extract list of game servers and settings
# this is a PITA without a proper parser
#   grep for "<World>" and output 4 lines afterwards (which contain the server config)
#   then grep for "<Name>" and strip off any tags
# just the name is saved here, other info will be retrieved later on
worlds=`grep -h -A 4 "<World>" .launcher/GLSDataCenter.config.${game} | grep "<Name>" | grep -v '<!--.*-->' | sed -e "s/^.*<Name>//;s/<\/Name>.*$//"`

# now we have a list of servernames, separated by new-line-characters, split them there into an array, ignore all other whitespace
IFS=$'\n'
i=0
for name in $worlds ; do
    serverNames[$i]=$name
    i=$(($i + 1))
done
serverNames[$i]="end-of-list"
unset IFS

# extract the auth server URL from the configuration file
authServer_URL=`grep -h "<AuthServer>" .launcher/GLSDataCenter.config.${game} | grep -v '<!--.*-->' | sed -e "s/^.*<AuthServer>//;s/<\/AuthServer>.*$//"`
if [ -z "${authServer_URL}" ] ; then
    echo -e "\nError: Could not extract authentication server URL from launcher configuration.\n"
    cd "$oldDir"
    read dummy
    exit
fi


########### CHOOSE LANGUAGE
languages[0]=english
selectedLanguage=0

########### PATCHING
#no patching

########### AUTHENTICATION

function GLSAuth() {

    # "submit" the login form via POST, will download a file called "LoginAccount"
    # NOTE: the same thing as with the DataCenterServer applies here
    # 	the lotroeugls server even has a service description and test form online
    #	well, at least they provide the SOAP request body as well...
    echo "Requesting GLS authentication ticket..."
    wget $wgetOptions \
        --header 'Content-Type: text/xml; charset=utf-8' \
        --header 'SOAPAction: "http://www.turbine.com/SE/GLS/LoginAccount"' \
        --post-data "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><LoginAccount xmlns=\"http://www.turbine.com/SE/GLS\"><username>${account}</username><password>${pass}</password><additionalInfo></additionalInfo></LoginAccount></soap:Body></soap:Envelope>" \
        "${authServer_URL}" -O .launcher/GLSAuthServer.config
    if ! [ -s .launcher/GLSAuthServer.config ] ; then
        echo -e "\nError: GLS auth server request failed. Wrong account/password?\n"
        echo -e "\nRetry (Y/n)? "
        read retry
        if [[ $retry == "y" || $retry == "Y" || $retry == "" ]] ; then
	        GLSAuth
        else
            cd "$oldDir"
            read dummy
            exit
        fi
    fi
}

GLSAuth

########### Multiple subscription check?  Must be a LOTRO thing

# check for multiple game subscriptions for the authenticated account
# insert whitespace after </GameSubscription> tags here (to have linebreaks as separators for accounts)
sed -e "s#\(</GameSubscription>\)#\1\n#g" -i .launcher/GLSAuthServer.config
# extract each of the ids as wells as descriptions for subscriptions to the current game
subNames=`grep -h "<Game>${game}<\/Game>" .launcher/GLSAuthServer.config | grep "<Name>" | grep -v '<!--.*-->' | sed -e "s/^.*<Name>//;s/<\/Name>.*$//"`
subDescs=`grep -h "<Game>${game}<\/Game>" .launcher/GLSAuthServer.config | grep "<Description>" | grep -v '<!--.*-->' | sed -e "s/^.*<Description>//;s/<\/Description>.*$//"`

IFS=$'\n'
# list of subscription ids and descriptions
i=0
for sub in $subNames ; do
    subs[$i]=$sub
    i=$(($i + 1))
done
subs[$i]="end-of-list"
i=0
for desc in $subDescs ; do
    descs[$i]=$desc
    i=$(($i + 1))
done
descs[$i]="end-of-list"
unset IFS

# check for at least one active subscription
if [[ $i == 0 ]] ; then
    echo -e "\nError: There appears to be no subscription for $game.\n"
    cd "$oldDir"
    read dummy
    exit
fi

# if there are multiple subscriptions to the current game available for the account, ask which one to use
if [[ $i > 1 ]] ; then
    i=0
    echo "You have the following subscriptions for $game:"
    while [[ "${subs[$i]}" != "end-of-list" ]] ; do
	echo -e "\t$i:\t${subs[$i]}\t'${descs[$i]}'"
        i=$(($i + 1))
    done
    echo -n "Please select the one you wish to use (enter the number on the left): "
    read selectedSub
else
    selectedSub=0
fi

# extract the ticket from the GLS auth file, check for failure
# NOTE:	only the id (not the ticket) is subscription-specific
glsTicket=`sed -n -e "s/^.*<Ticket>//;s/<\/Ticket>.*$// p" .launcher/GLSAuthServer.config`
glsTicketlifetime=21600
if [[ -z "${glsTicket}" ]] ; then
    echo -e "\nError: Could not extract auth result from GLS auth server response.\n"
    cd "$oldDir"
    read dummy
    exit
fi
echo -e "Logged in."


########### REALM SELECTION

echo -e "\nThe following servers are available:"
i=0
while [[ "${serverNames[$i]}" != "end-of-list" ]] ; do
    echo -e "\t$i:\t${serverNames[$i]}"
    i=$(($i + 1))
done

# with the given index (and therefore server name), look up the server info in the configuration file
# chat server adress is given directly, other stuff needs another file (cache_$REALMNAME.xml) from the server
serverChat=`grep -h -A 4 "<World>" .launcher/GLSDataCenter.config.${game} | grep -F -A 3 "${serverNames[$selectedServer]}" | grep -h "<ChatServerUrl>" | grep -v '<!--.*-->' | sed -e "s/^.*<ChatServerUrl>//;s/<\/ChatServerUrl>.*$//"`
serverStatus_URL=`grep -h -A 4 "<World>" .launcher/GLSDataCenter.config.${game} | grep -F -A 3 "${serverNames[$selectedServer]}" | grep -h "<StatusServerUrl>" | grep -v '<!--.*-->' | sed -e "s/^.*<StatusServerUrl>//;s/<\/StatusServerUrl>.*$//"`

# now we know where the chat server resides and we have the adress of the server xml status file
# download the status file and get server adress and login queue adress to establish the connection
# ToDo: this file also contains current availability information, use it
wget $wgetOptions "$serverStatus_URL" -O .launcher/server.config

# extract the list of loginServers and queue URLs (two at this time, it seems)
loginServers=`grep -h "<loginservers>" .launcher/server.config | grep -v '<!--.*-->' | sed -e "s/^.*<loginservers>//;s/<\/loginservers>.*$//"`
queueUrls=`grep -h "<queueurls>" .launcher/server.config | grep -v '<!--.*-->' | sed -e "s/^.*<queueurls>//;s/<\/queueurls>.*$//"`
if [ -z "${loginServers}" ] || [ -z "${queueUrls}" ] ; then
    echo -e "\nError: Could not extract server information for realm ${serverNames[$selectedServer]}.\n"
    cd "$oldDir"
    read dummy
    exit
fi

IFS=";"
i=0
for adr in $loginServers ; do
    serverAdresses[$i]=$adr
    i=$(($i + 1))
done
i=0
for adr in $queueUrls ; do
    serverQueues[$i]=$adr
    i=$(($i + 1))
done
unset IFS

# just use the respective first one given
serverAdress="${serverAdresses[0]}"
serverQueue="${serverQueues[0]}"

# the ticket can contain special characters and needs to be URL encoded before POSTing it (same for queue_url)
# launcher.config also includes a parameter template for the world queue request, used the same way as the client args below

glsTicketURLencoded=$(echo ${glsTicket} | sed -e 's/%/%25/g' -e 's/ /%20/g' -e 's/!/%21/g' -e 's/"/%22/g' -e 's/#/%23/g' -e 's/\$/%24/g' -e 's/\&/%26/g' -e 's/'\''/%27/g' -e 's/(/%28/g' -e 's/)/%29/g' -e 's/\*/%2a/g' -e 's/+/%2b/g' -e 's/,/%2c/g' -e 's/-/%2d/g' -e 's/\./%2e/g' -e 's/\//%2f/g' -e 's/:/%3a/g' -e 's/;/%3b/g' -e 's//%3e/g' -e 's/?/%3f/g' -e 's/@/%40/g' -e 's/\[/%5b/g' -e 's/\\/%5c/g' -e 's/\]/%5d/g' -e 's/\^/%5e/g' -e 's/_/%5f/g' -e 's/`/%60/g' -e 's/{/%7b/g' -e 's/|/%7c/g' -e 's/}/%7d/g' -e 's/~/%7e/g')
loginQueueURLencoded=$(echo ${serverQueue} | sed -e 's/%/%25/g' -e 's/ /%20/g' -e 's/!/%21/g' -e 's/"/%22/g' -e 's/#/%23/g' -e 's/\$/%24/g' -e 's/\&/%26/g' -e 's/'\''/%27/g' -e 's/(/%28/g' -e 's/)/%29/g' -e 's/\*/%2a/g' -e 's/+/%2b/g' -e 's/,/%2c/g' -e 's/-/%2d/g' -e 's/\./%2e/g' -e 's/\//%2f/g' -e 's/:/%3a/g' -e 's/;/%3b/g' -e 's//%3e/g' -e 's/?/%3f/g' -e 's/@/%40/g' -e 's/\[/%5b/g' -e 's/\\/%5c/g' -e 's/\]/%5d/g' -e 's/\^/%5e/g' -e 's/_/%5f/g' -e 's/`/%60/g' -e 's/{/%7b/g' -e 's/|/%7c/g' -e 's/}/%7d/g' -e 's/~/%7e/g')

# the launcher.config also includes a parameter template for the world queue request
worldQueue_ARGS=`echo "${worldQueue_ARGTMPL}" | sed -e "s/[{]0[}]/${subs[$selectedSub]}/;s/[{]1[}]/${glsTicketURLencoded}/;s/[{]2[}]/${loginQueueURLencoded}/;s/\&amp\;/\&/g"`
echo $worldQueue_ARGS

########### LOGIN QUEUE / CLIENT START
function JoinQueue() {

    # now get a queue number from the world login queue so that the client can enqueue and authenticate
    echo -e "\nConnecting to world login queue for realm ${serverNames[$selectedServer]}...";
    inQueue=1
    while [ "$inQueue" == "1" ]; do
        # loop when necessary
        wget $wgetOptions --post-data="${worldQueue_ARGS}" "${worldQueue_URL}" -O .launcher/WorldQueue.config
        if ! [ -r .launcher/WorldQueue.config ] ; then
            echo -e "\nError: World login queue request failed.\n"
            cd "$oldDir"
            read dummy
            exit
        fi

        # check the result, should be HRESULT 0x00000000, indicating success (Windows API madness)
        hresult=`grep -h "<HResult>" .launcher/WorldQueue.config | grep -v '<!--.*-->' | sed -e "s/^.*<HResult>//;s/<\/HResult>.*$//"`
        if [ "$hresult" != "0x00000000" ] ; then
	        echo -e "\nError: World login queue response indicates failure."
            cd "$oldDir"
            read dummy
            exit
        else
            # lets see what position we are at
            queuePos=`grep -h "<QueueNumber>" .launcher/WorldQueue.config | grep -v '<!--.*-->' | sed -e "s/^.*<QueueNumber>//;s/<\/QueueNumber>.*$//"`
            queueSrvd=`grep -h "<NowServingNumber>" .launcher/WorldQueue.config | grep -v '<!--.*-->' | sed -e "s/^.*<NowServingNumber>//;s/<\/NowServingNumber>.*$//"`
            queueAhead=$(( ${queuePos} - ${queueSrvd} ))
            if [ ${queueAhead} -gt 0 ]; then
                # d'oh, need to wait
                echo -e "\n${queueAhead} ahead in queue, retrying in 5s..."
                sleep 5
            else
                # hah, ready to go
                inQueue=0
            fi
        fi
    done
}

# new: world queue is unused on (some?) EU servers, just like with DDO, skip if no queue URL
if [ -n "${serverQueue}" ] ; then
    JoinQueue
else
    echo -e "\nWorld login queue seems to be disabled, skipping..."
fi

supportURL="https://tss.ddo.com/TSSTrowser/trowser.aspx"
bugURL="http://ddobugs.turbine.com?task=ticket"
supportServiceURL="https://tss.ddo.com/TSSTrowser/SubmitTicket.asmx"
# OK, so we have a template for the client arguments and we have the arguments
# note that for replacing the glsTicket, I use s### instead of s/// due to the ticket containing slashes
# hopefully, it doesn't contain any sharp characters :)
#gameClient_ARGS=`echo "${gameClient_ARGTMPL}" | sed -e "s/[{]0[}]/${subs[$selectedSub]}/;s/[{]1[}]/${serverAdress}/;s#[{]2[}]#${glsTicket}#;s/[{]3[}]/${serverChat}/;s/[{]4[}]/${languages[$selectedLanguage]}/;s/[{]5[}]/${authServer_URL}/;s/[{]6[}]/${glsTicketlifetime}/;s/[{]7[}]/${supportURL}/;s/[{]8[}]/${bugURL}/;s/[{]9[}]/${supportServiceURL}/"`
#serverAdress="198.252.160.45:9003"

gameClient_ARGS="-a ${subs[$selectedSub]} -h ${serverAdress} --glsticketdirect ${glsTicket} --chatserver ${serverChat}  --rodat on --language ${languages[$selectedLanguage]} --gametype DDO --authserverurl ${authServer_URL} --glsticketlifetime ${glsTicketlifetime} --supporturl ${supportURL} --bugurl ${bugURL} --supportserviceurl ${supportServiceURL} -r ${characterName}"
# all ready, now fire up the client
echo "Ready. Now starting the client..."
echo $gameClient_ARGS

wine64 x64/dndclient64.exe $gameClient_ARGS &

# get back to where the caller was
cd "$oldDir"

exit