#!/bin/bash

pid=$(pidof "hl_linux")
libpath=$(realpath "libhlcheat.so")

if [ "$pid" == "" ]; then
   echo "inject-debug.sh: process not running."
   exit 1
fi

show_help() {
    echo "Usage: $0 [OPTIONS]"
    echo "Options:"
    echo "  --help          Show this help message"
    echo "  --unload        Debug the unload process interactively"
    echo "  --inject        Debug the injection process interactively"
    echo ""
    echo "Common GDB commands to use during debugging:"
    echo "  bt              Show backtrace"
    echo "  info locals     Show local variables"
    echo "  n               Step over (next line)"
    echo "  s               Step into function"
    echo "  c               Continue execution"
    echo "  p expression    Print value of expression"
    echo "  finish          Run until current function returns"
    echo ""
    echo "Without options, the script will inject the cheat normally."
    exit 0
}

UNLOAD_MODE=0
INJECT_MODE=0

for arg in "$@"; do
    case $arg in
        --help)
            show_help
            ;;
        --unload)
            UNLOAD_MODE=1
            ;;
        --inject)
            INJECT_MODE=1
            ;;
    esac
done

cat > /tmp/gdbinit-goldsrc << EOF
set confirm off
set pagination off
set print pretty on

define hook-stop
    echo \nBREAKPOINT HIT\n
    bt
    echo \nLOCAL VARIABLES:\n
    info locals
    echo \nSTACK FRAME:\n
    info frame
    echo \nCommands: n (next), s (step), c (continue), bt (backtrace), finish (run until function returns)\n
end

EOF

if [ $UNLOAD_MODE -eq 1 ]; then
    echo "Starting interactive unload debugging session..."
    
    cat >> /tmp/gdbinit-goldsrc << EOF
# Set up functions to help with dlopen/dlclose
set \$dlopen = (void* (*)(char*, int))dlopen
set \$dlclose = (int (*)(void*))dlclose
set \$dlerror = (char* (*)(void))dlerror

# Set breakpoints on critical functions
break self_unload
break unload
break safe_unload_with_debug
break UNINJECT_CommandHandler
break hooks_restore
break globals_restore
break GL_UNHOOK

# Command to manually invoke the unload from GDB
define uninject
    set \$self = \$dlopen("$libpath", 6)
    p \$self
    call \$dlclose(\$self)
    call \$dlclose(\$self)
    call \$dlerror()
end

define call_uninject_cmd
    call UNINJECT_CommandHandler()
end

echo \nType 'call_uninject_cmd' to execute the uninject command\n
echo Type 'uninject' to manually trigger the unload process\n
EOF

    sudo gdb -x /tmp/gdbinit-goldsrc -p $pid
    
    echo "Interactive unload debugging session ended."
    exit 0
fi

if [ $INJECT_MODE -eq 1 ]; then
    echo "Starting interactive injection debugging session..."
    
    cat >> /tmp/gdbinit-goldsrc << EOF
# Set up functions to help with dlopen/dlclose
set \$dlopen = (void* (*)(char*, int))dlopen
set \$dlclose = (int (*)(void*))dlclose
set \$dlerror = (char* (*)(void))dlerror

# Set breakpoints on critical functions
break load
break globals_init
break cvars_init
break hooks_init

# Command to manually inject the library
define inject
    call \$dlopen("$libpath", 2)
    call \$dlerror()
end

echo \nType 'inject' to load the library\n
EOF

    sudo gdb -x /tmp/gdbinit-goldsrc -p $pid
    
    echo "Interactive injection debugging session ended."
    exit 0
fi

echo "Injecting cheat..."

if grep -q "$libpath" "/proc/$pid/maps"; then
    echo -e "goldsource-cheat already loaded. Reloading...\n"

    cat > /tmp/gdbinit-goldsrc << EOF
set confirm off
set pagination off
set print pretty on
set \$dlopen = (void* (*)(char*, int))dlopen
set \$dlclose = (int (*)(void*))dlclose
set \$dlerror = (char* (*)(void))dlerror

# Reload library
define reload_lib
    set \$self = \$dlopen("$libpath", 6)
    call \$dlclose(\$self)
    call \$dlclose(\$self)
    call \$dlopen("$libpath", 2)
    call \$dlerror()
    echo "\nReload complete. Library has been reloaded.\n"
    echo "You can now debug interactively.\n"
    echo "Type 'continue' or 'c' to let the game run normally.\n"
    echo "Type 'call_uninject_cmd' to trigger the uninject command.\n"
end

# Command to manually trigger uninject
define call_uninject_cmd
    call UNINJECT_CommandHandler()
end

# Execute reload automatically
reload_lib

# Break on uninject command
break UNINJECT_CommandHandler
break safe_unload_with_debug
break self_unload

echo "\nType 'help' for GDB commands or 'continue' to let the game run.\n"
EOF

    sudo gdb -x /tmp/gdbinit-goldsrc -p $pid
else
    cat > /tmp/gdbinit-goldsrc << EOF
set confirm off
set pagination off
set print pretty on
set \$dlopen = (void* (*)(char*, int))dlopen
set \$dlclose = (int (*)(void*))dlclose
set \$dlerror = (char* (*)(void))dlerror

# Initial library load
define load_lib
    call \$dlopen("$libpath", 2)
    call \$dlerror()
    echo "\nInjection complete. Library has been loaded.\n"
    echo "You can now debug interactively.\n"
    echo "Type 'continue' or 'c' to let the game run normally.\n"
    echo "Type 'call_uninject_cmd' to trigger the uninject command.\n"
end

# Command to manually trigger uninject
define call_uninject_cmd
    call UNINJECT_CommandHandler()
end

# Execute load automatically
load_lib

# Break on uninject command
break UNINJECT_CommandHandler
break safe_unload_with_debug
break self_unload

echo "\nType 'help' for GDB commands or 'continue' to let the game run.\n"
EOF

    sudo gdb -x /tmp/gdbinit-goldsrc -p $pid
fi

echo -e "\nDebug session ended."