View Raw
# CCLS Games CLI Tool
# Complete Version with Setup, Search, Get functionality, and Logging

# Configuration
$baseUrl = "https://games.ccls.icu"
$cliApiUrl = "$baseUrl/CLI"
$settingsFolder = ".\settings"
$credentialsFile = "$settingsFolder\credentials.dat"
$settingsFile = "$settingsFolder\settings.json" # Using JSON instead of INI for simplicity
$logsFolder = ".\logs" # Logs folder path

# Script-level variables for progress display and logging
$script:progressInitialized = $false
$script:topProgressLine = $null
$script:logFile = $null
$script:sessionStartTime = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"

# Ensure settings and logs directories exist
if (-not (Test-Path $settingsFolder)) {
    New-Item -Path $settingsFolder -ItemType Directory | Out-Null
}

if (-not (Test-Path $logsFolder)) {
    New-Item -Path $logsFolder -ItemType Directory | Out-Null
}

# Initialize log file for the current session
$script:logFile = Join-Path -Path $logsFolder -ChildPath "ccls_session_$($script:sessionStartTime).log"
"CCLS Games CLI Session started at $($script:sessionStartTime)" | Out-File -FilePath $script:logFile

# Custom Write-Host function to handle logging and output filtering
function Write-CCLSHost {
    param (
        [Parameter(Position = 0)]
        [string]$Message = "",
        
        [Parameter()]
        [System.ConsoleColor]$ForegroundColor = [System.ConsoleColor]::White,
        
        [Parameter()]
        [System.ConsoleColor]$BackgroundColor = [System.ConsoleColor]::Black,
        
        [Parameter()]
        [switch]$NoNewline,
        
        [Parameter()]
        [switch]$Log,
        
        [Parameter()]
        [switch]$NoConsole
    )
    
    # Check if the message is marked for removal
    if ($Message.StartsWith('#')) {
        # Don't show in console, but log it if needed
        if ($Log) {
            $cleanMessage = $Message.Substring(1).Trim()
            if (-not [string]::IsNullOrWhiteSpace($cleanMessage)) {
                $cleanMessage | Out-File -FilePath $script:logFile -Append
            }
        }
        return
    }
    
    if ($Message.StartsWith('/')) {
        # Skip completely - don't log or display
        return
    }
    
    # Handle empty lines
    if ([string]::IsNullOrWhiteSpace($Message)) {
        # Display in console if not suppressed
        if (-not $NoConsole) {
            Write-Host "" -NoNewline:$NoNewline
        }
        # But don't log empty lines
        return
    }
    
    # Display in console if not suppressed
    if (-not $NoConsole) {
        Write-Host $Message -ForegroundColor $ForegroundColor -BackgroundColor $BackgroundColor -NoNewline:$NoNewline
    }
    
    # Log if requested
    if ($Log) {
        $plainMessage = $Message
        $plainMessage | Out-File -FilePath $script:logFile -Append
    }
}

# Initialize or load settings
function Initialize-Settings {
    if (-not (Test-Path $settingsFile)) {
        # Create default settings file
        $defaultSettings = @{
            RememberLogin = $false
            DownloadPath = ".\downloads"
            TempDownloadPath = ".\tmp"
            HasCompletedSetup = $false
        }
        $defaultSettings | ConvertTo-Json | Set-Content -Path $settingsFile
    }
    
    # Load settings
    $settings = Get-Content -Path $settingsFile | ConvertFrom-Json
    return $settings
}

# Save settings
function Save-Settings($settings) {
    $settings | ConvertTo-Json | Set-Content -Path $settingsFile
}

# Store credentials securely
function Save-Credentials($username, $password) {
    $credentials = @{
        Username = $username
        Password = $password
    } | ConvertTo-Json
    
    # Simple encryption (replace with more secure method if needed)
    $securePassword = ConvertTo-SecureString -String $credentials -AsPlainText -Force
    $encryptedText = ConvertFrom-SecureString -SecureString $securePassword
    
    # Save to file
    $encryptedText | Set-Content -Path $credentialsFile
}

# Load stored credentials
function Get-StoredCredentials {
    if (Test-Path $credentialsFile) {
        try {
            $encryptedText = Get-Content -Path $credentialsFile
            $securePassword = ConvertTo-SecureString -String $encryptedText
            $credentials = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
                [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword)
            )
            return $credentials | ConvertFrom-Json
        }
        catch {
            Write-CCLSHost "Error reading credentials: $($_.Exception.Message)" -ForegroundColor Red -Log
            return $null
        }
    }
    return $null
}

# Run setup process
function Start-Setup {
    Write-CCLSHost "`n`nCCLS Games CLI Setup" -ForegroundColor Green -Log
    Write-CCLSHost "=====================" -ForegroundColor Green -Log
    Write-CCLSHost "Please configure the following settings:`n" -ForegroundColor Cyan -Log
    
    $settings = Initialize-Settings
    
    # Get game installation directory
    $validPath = $false
    while (-not $validPath) {
        Write-CCLSHost "Set your games default installation directory: " -ForegroundColor Yellow -NoNewline -Log
        $downloadPath = Read-Host
        Write-CCLSHost "$downloadPath" -NoConsole -Log
        
        if ([string]::IsNullOrWhiteSpace($downloadPath)) {
            Write-CCLSHost "Please enter a valid directory path." -ForegroundColor Red -Log
        }
        else {
            # Create directory if it doesn't exist
            if (-not (Test-Path $downloadPath)) {
                try {
                    New-Item -ItemType Directory -Path $downloadPath -Force | Out-Null
                    $validPath = $true
                }
                catch {
                    Write-CCLSHost "Error creating directory: $($_.Exception.Message)" -ForegroundColor Red -Log
                }
            }
            else {
                $validPath = $true
            }
        }
    }
    
    # Get temporary download directory
    $validTempPath = $false
    while (-not $validTempPath) {
        Write-CCLSHost "Set the temporary directory of downloading files before they have finished downloading: " -ForegroundColor Yellow -NoNewline -Log
        $tempDownloadPath = Read-Host
        Write-CCLSHost "$tempDownloadPath" -NoConsole -Log
        
        if ([string]::IsNullOrWhiteSpace($tempDownloadPath)) {
            Write-CCLSHost "Please enter a valid directory path." -ForegroundColor Red -Log
        }
        else {
            # Create directory if it doesn't exist
            if (-not (Test-Path $tempDownloadPath)) {
                try {
                    New-Item -ItemType Directory -Path $tempDownloadPath -Force | Out-Null
                    $validTempPath = $true
                }
                catch {
                    Write-CCLSHost "Error creating directory: $($_.Exception.Message)" -ForegroundColor Red -Log
                }
            }
            else {
                $validTempPath = $true
            }
        }
    }
    
    # Update settings
    $settings.DownloadPath = $downloadPath
    $settings.TempDownloadPath = $tempDownloadPath
    $settings.HasCompletedSetup = $true
    Save-Settings -settings $settings
    
    Write-CCLSHost "`nGreat, you have now completed the setup. Type 'help' for a list of commands to get you started." -ForegroundColor Green -Log
}

# Validate username against server
function Test-Username($username) {
    $params = @{
        Uri = "$cliApiUrl/username_check.php"
        Method = "POST"
        Headers = @{
            "User-Agent" = "CCLS-CLI/1.0"
        }
        Body = @{
            username = $username
        }
    }
    
    try {
        $response = Invoke-RestMethod @params
        return $response.exists
    }
    catch {
        Write-CCLSHost "Error connecting to the server: $($_.Exception.Message)" -ForegroundColor Red -Log
        return $false
    }
}

# Validate password against server
function Test-Password($username, $password) {
    $params = @{
        Uri = "$cliApiUrl/password_check.php"
        Method = "POST"
        Headers = @{
            "User-Agent" = "CCLS-CLI/1.0"
        }
        Body = @{
            username = $username
            password = $password
        }
    }
    
    try {
        $response = Invoke-RestMethod @params
        return $response.success
    }
    catch {
        Write-CCLSHost "Error connecting to the server: $($_.Exception.Message)" -ForegroundColor Red -Log
        return $false
    }
}

function Start-AutoLogin {
    $credentials = Get-StoredCredentials
    if ($credentials -ne $null) {
        Write-CCLSHost "#Attempting to login with stored credentials..." -Log
        
        if (Test-Password -username $credentials.Username -password $credentials.Password) {
            Write-CCLSHost "#Auto-login successful!" -Log
            return @{
                Success = $true
                Username = $credentials.Username
            }
        }
        else {
            Write-CCLSHost "Stored credentials are no longer valid." -ForegroundColor Yellow -Log
            # Remove invalid credentials
            if (Test-Path $credentialsFile) {
                Remove-Item -Path $credentialsFile -Force
            }
        }
    }
    
    return @{
        Success = $false
    }
}

function Start-ManualLogin {
    $maxAttempts = 3
    $attempts = 0
    
    while ($attempts -lt $maxAttempts) {
        Write-CCLSHost "Username: " -ForegroundColor Cyan -NoNewline -Log
        $username = Read-Host
        Write-CCLSHost "$username" -NoConsole -Log
        
        # Check if username exists
        Write-CCLSHost "#Checking username..." -Log
        if (Test-Username -username $username) {
            Write-CCLSHost "#Username found!" -Log
            
            Write-CCLSHost "Password: " -ForegroundColor Cyan -NoNewline -Log
            $password = Read-Host -AsSecureString
            $passwordPlain = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
                [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
            )
            Write-CCLSHost "********" -NoConsole -Log
            
            Write-CCLSHost "#Validating password..." -Log
            if (Test-Password -username $username -password $passwordPlain) {
                Write-CCLSHost "#Login successful!" -Log
                
                # Ask if user wants to save credentials - no newline before this
                Write-CCLSHost "Do you want to remember your login info for next time? (Y/N)" -ForegroundColor Yellow -Log
                $rememberLogin = Read-Host
                Write-CCLSHost "$rememberLogin" -NoConsole -Log
                
                if ($rememberLogin.ToLower() -eq "y") {
                    Save-Credentials -username $username -password $passwordPlain
                    $settings = Initialize-Settings
                    $settings.RememberLogin = $true
                    Save-Settings -settings $settings
                    Write-CCLSHost "#Login information saved." -Log
                }
                
                return @{
                    Success = $true
                    Username = $username
                }
            }
            else {
                Write-CCLSHost "Incorrect password, please try again." -ForegroundColor Red -Log
                $attempts++
            }
        }
        else {
            Write-CCLSHost "Username not found, please try again." -ForegroundColor Red -Log
            $attempts++
        }
    }
    
    Write-CCLSHost "Too many failed login attempts. Please try again later." -ForegroundColor Red -Log
    return @{
        Success = $false
    }
}

# Search for game information by CG number
function Search-Game($cgNumber) {
    # Validate CG number format
    if ($cgNumber -notmatch "^cg\d{4}$") {
        Write-CCLSHost "Invalid CG number format. Please use format 'cg0000'." -ForegroundColor Red -Log
        return
    }
    
    try {
        # Construct the URL for the game JSON file
        $gameJsonUrl = "$cliApiUrl/search.php?id=$cgNumber"
        
        # Attempt to fetch the game information
        $params = @{
            Uri = $gameJsonUrl
            Method = "GET"
            Headers = @{
                "User-Agent" = "CCLS-CLI/1.0"
            }
        }
        
        Write-CCLSHost "#Searching for game information..." -Log
        
        # Fetch game data from server
        try {
            $response = Invoke-RestMethod @params
            
            # Check if the request was successful
            if (-not $response.success) {
                Write-CCLSHost "Error: $($response.message)" -ForegroundColor Red -Log
                return
            }
            
            $gameInfo = $response
        }
        catch {
            Write-CCLSHost "Error fetching game information: $($_.Exception.Message)" -ForegroundColor Red -Log
            return
        }
        
        # Display game information in a formatted way
        Write-CCLSHost "`n==========================================================" -ForegroundColor DarkGray -Log
        Write-CCLSHost "Game Information for $($gameInfo.name) ($($gameInfo.id))" -ForegroundColor Green -Log
        Write-CCLSHost "==========================================================" -ForegroundColor DarkGray -Log
        
        Write-CCLSHost "`nDescription:" -ForegroundColor Cyan -Log
        Write-CCLSHost $gameInfo.description -Log
        
        if ($gameInfo.safety_score -or $gameInfo.safety_level) {
            Write-CCLSHost "`nSafety:" -ForegroundColor Cyan -Log
            if ($gameInfo.safety_score) { Write-CCLSHost "Score: $($gameInfo.safety_score)" -Log }
            if ($gameInfo.safety_level) { Write-CCLSHost "Level: $($gameInfo.safety_level)" -Log }
        }
        
        Write-CCLSHost "`nDetails:" -ForegroundColor Cyan -Log
        if ($gameInfo.size) { Write-CCLSHost "Size: $($gameInfo.size)" -Log }
        if ($gameInfo.version -and $gameInfo.version -ne "") { Write-CCLSHost "Version: $($gameInfo.version)" -Log }
        
        if (($gameInfo.online -and $gameInfo.online -ne "") -or 
            ($gameInfo.steam -and $gameInfo.steam -ne "") -or 
            ($gameInfo.epic -and $gameInfo.epic -ne "")) {
            Write-CCLSHost "`nAvailability:" -ForegroundColor Cyan -Log
            if ($gameInfo.online -and $gameInfo.online -ne "") { Write-CCLSHost "Online: $($gameInfo.online)" -Log }
            if ($gameInfo.steam -and $gameInfo.steam -ne "") { Write-CCLSHost "Steam: $($gameInfo.steam)" -Log }
            if ($gameInfo.epic -and $gameInfo.epic -ne "") { Write-CCLSHost "Epic: $($gameInfo.epic)" -Log }
        }
        
        if ($gameInfo.false_av -and $gameInfo.false_av -ne "") {
            Write-CCLSHost "`nNote: " -ForegroundColor Yellow -NoNewline -Log
            Write-CCLSHost "This game may trigger false antivirus alerts: $($gameInfo.false_av)" -Log
        }
        
        if ($gameInfo.system_requirements) {
            Write-CCLSHost "`nSystem Requirements:" -ForegroundColor Cyan -Log
            
            if ($gameInfo.system_requirements.minimum) {
                Write-CCLSHost "Minimum:" -ForegroundColor Yellow -Log
                foreach ($prop in $gameInfo.system_requirements.minimum.PSObject.Properties) {
                    Write-CCLSHost "  $($prop.Name): $($prop.Value)" -Log
                }
            }
            
            if ($gameInfo.system_requirements.recommended) {
                Write-CCLSHost "`nRecommended:" -ForegroundColor Yellow -Log
                foreach ($prop in $gameInfo.system_requirements.recommended.PSObject.Properties) {
                    Write-CCLSHost "  $($prop.Name): $($prop.Value)" -Log
                }
            }
        }
        
        Write-CCLSHost "#Press any key to return to the main menu..." -Log
        $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
    }
    catch {
        Write-CCLSHost "An error occurred while processing game information: $($_.Exception.Message)" -ForegroundColor Red -Log
        Write-CCLSHost "#Press any key to return to the main menu..." -Log
        $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
    }
}

# Function to format file size
function Format-FileSize {
    param ([string]$Size)
    
    if ([string]::IsNullOrEmpty($Size)) {
        return "Unknown Size"
    }
    
    # Extract numeric part and unit
    if ($Size -match "(\d+\.?\d*)\s*(GB|MB|KB|B)") {
        $value = [double]$matches[1]
        $unit = $matches[2]
        
        return "$value $unit"
    }
    
    return $Size
}

function Get-Game {
    param (
        [string]$cgNumber,
        [switch]$SkipConfirmation
    )
    
    # Validate CG number format
    if ($cgNumber -notmatch "^cg\d{4}$") {
        Write-CCLSHost "Invalid CG number format. Please use format 'cg0000'." -ForegroundColor Red -Log
        return
    }
    
    # Check if setup has been completed
    $settings = Initialize-Settings
    if (-not $settings.HasCompletedSetup) {
        Write-CCLSHost "ALERT, you must run the 'setup' command before downloading games." -ForegroundColor Red -Log
        return
    }
    
    # Ensure download directories exist
    if (-not (Test-Path $settings.DownloadPath)) {
        try {
            New-Item -ItemType Directory -Path $settings.DownloadPath -Force | Out-Null
        }
        catch {
            Write-CCLSHost "Error creating download directory: $($_.Exception.Message)" -ForegroundColor Red -Log
            return
        }
    }
    
    if (-not (Test-Path $settings.TempDownloadPath)) {
        try {
            New-Item -ItemType Directory -Path $settings.TempDownloadPath -Force | Out-Null
        }
        catch {
            Write-CCLSHost "Error creating temporary download directory: $($_.Exception.Message)" -ForegroundColor Red -Log
            return
        }
    }
    
    # Fetch game download information
    try {
        $gameInfoUrl = "$cliApiUrl/get.php?id=$cgNumber"
        
        $params = @{
            Uri = $gameInfoUrl
            Method = "GET"
            Headers = @{
                "User-Agent" = "CCLS-CLI/1.0"
            }
        }
        
        Write-CCLSHost "#Fetching game download information..." -Log
        
        $response = Invoke-RestMethod @params
        
        if (-not $response.success) {
            Write-CCLSHost "Error: $($response.message)" -ForegroundColor Red -Log
            return
        }
        
        $gameName = $response.name
        $gameId = $response.id
        $downloadUrl = $response.download_url
        $gameSize = $response.size
        
        # Create download file name from URL
        $fileName = Split-Path -Path $downloadUrl -Leaf
        if ([string]::IsNullOrEmpty($fileName)) {
            $fileName = "$gameId.7z"
        }
        
        $downloadPath = Join-Path -Path $settings.TempDownloadPath -ChildPath $fileName
        
        # Confirm download with user
        Write-CCLSHost "==========================================================" -ForegroundColor DarkGray -Log
        Write-CCLSHost "Game Download: $gameName ($gameId)" -ForegroundColor Green -Log
        Write-CCLSHost "==========================================================" -ForegroundColor DarkGray -Log
        Write-CCLSHost "Size: $gameSize" -ForegroundColor Yellow -Log
        Write-CCLSHost "Download URL: $downloadUrl" -ForegroundColor Cyan -Log
        
        $proceed = $true
        if (-not $SkipConfirmation) {
            Write-CCLSHost "ALERT, the get command will start the downloading process of the game specified, to stop it do 'Ctrl C'" -ForegroundColor Red -Log
            Write-CCLSHost "Do you want to proceed with the download? (Y/N)" -ForegroundColor Yellow -Log
            
            $confirmation = Read-Host
            Write-CCLSHost "$confirmation" -NoConsole -Log
            
            if ($confirmation.ToLower() -ne "y") {
                Write-CCLSHost "Download cancelled by user." -ForegroundColor Yellow -Log
                return
            }
        }
        else {
            Write-CCLSHost "#Automatically proceeding with download (confirmation skipped)..." -ForegroundColor Green -Log
        }
        
        try {
            # Try to invoke Python to check if it's available
            $pythonVersion = python --version 2>&1
            $pythonAvailable = $true
            Write-CCLSHost "#Python detected: $pythonVersion" -Log
            
            # Determine the actual location of the Python script
            $scriptLocation = if ($PSScriptRoot) {
                # If running from a script, use its location
                $PSScriptRoot
            } else {
                # If running in console, use current directory
                (Get-Location).Path
            }

            $pythonScript = Join-Path -Path $scriptLocation -ChildPath "ccls_downloader.py"

            # Debug the script path
            Write-CCLSHost "#Python script path: $pythonScript" -Log

            # Check if the Python script exists
            if (-not (Test-Path $pythonScript)) {
                Write-CCLSHost "#Python script not found at $pythonScript, creating it..." -Log
                
                # You need to paste the content of ccls_downloader.py here as a multiline string
                $pythonCode = @'
#!/usr/bin/env python3
"""
CCLS High-Speed Downloader (Improved Version)
--------------------------------------------
A high-performance Python script for downloading games from CCLS with improved
connection handling, retry logic, and resume capabilities.
"""

import os
import sys
import time
import requests
import signal
import random
from datetime import datetime, timedelta

# Constants for downloading
CHUNK_SIZE = 1024 * 1024  # 1MB chunks for better performance
USER_AGENT = "CCLS-CLI/1.0 (Python Downloader)"
MAX_RETRIES = 5  # Increased from 3
CONNECT_TIMEOUT = 30  # Connection timeout in seconds
READ_TIMEOUT = 60  # Read timeout in seconds
RETRY_BASE_DELAY = 2  # Base delay for exponential backoff
RETRY_MAX_DELAY = 60  # Maximum delay between retries

# Setup signal handling for clean exit on Ctrl+C
def signal_handler(sig, frame):
    print("\nDOWNLOAD_CANCELLED")
    sys.exit(1)

signal.signal(signal.SIGINT, signal_handler)

def format_size(size_bytes):
    """Format bytes into human-readable format"""
    if size_bytes >= 1_000_000_000:  # GB
        return f"{size_bytes / 1_000_000_000:.2f} GB"
    elif size_bytes >= 1_000_000:  # MB
        return f"{size_bytes / 1_000_000:.2f} MB"
    elif size_bytes >= 1000:  # KB
        return f"{size_bytes / 1000:.2f} KB"
    else:
        return f"{size_bytes} B"

def format_time(seconds):
    """Format seconds into HH:MM:SS format"""
    hours = seconds // 3600
    minutes = (seconds % 3600) // 60
    secs = seconds % 60
    if hours > 0:
        return f"{hours:02d}:{minutes:02d}:{secs:02d}"
    else:
        return f"{minutes:02d}:{secs:02d}"

def get_file_size(url, headers):
    """Get the size of the file to be downloaded"""
    for attempt in range(MAX_RETRIES):
        try:
            # Only get the headers, dont download the content yet
            response = requests.head(url, headers=headers, 
                                 timeout=(CONNECT_TIMEOUT, READ_TIMEOUT))
            response.raise_for_status()
            
            # Return the content length if available
            content_length = response.headers.get('content-length')
            if content_length:
                return int(content_length)
            
            # If we cant get the size via HEAD request, return None
            return None
        
        except (requests.exceptions.RequestException, IOError) as e:
            delay = min(RETRY_BASE_DELAY * (2 ** attempt) + random.uniform(0, 1), RETRY_MAX_DELAY)
            if attempt < MAX_RETRIES - 1:
                print(f"Error getting file size: {str(e)}. Retrying in {delay:.1f} seconds...")
                time.sleep(delay)
            else:
                print(f"Failed to get file size after {MAX_RETRIES} attempts: {str(e)}")
                return None

def can_resume(url, headers):
    """Check if the server supports resuming downloads"""
    try:
        # Add range header to check if the server supports it
        resume_headers = headers.copy()
        resume_headers['Range'] = 'bytes=0-0'
        
        response = requests.head(url, headers=resume_headers, 
                             timeout=(CONNECT_TIMEOUT, READ_TIMEOUT))
        
        # If we get a 206 status, the server supports resume
        return response.status_code == 206
    except:
        # If theres any error, assume we cant resume to be safe
        return False

def download_file(url, destination, game_name, game_id, expected_size=None):
    """Download a file with progress tracking and resume support"""
    headers = {
        "User-Agent": USER_AGENT,
        "Connection": "keep-alive",
        "Accept-Encoding": "gzip, deflate"
    }
    
    # Get the file size if not provided
    total_size = expected_size
    if total_size is None:
        total_size = get_file_size(url, headers)
        if total_size is None:
            print("Warning: Unable to determine file size in advance. Progress reporting may be inaccurate.")

    # Check if we can resume
    supports_resume = can_resume(url, headers)
    
    # Check if the file already exists and if we should resume
    downloaded = 0
    if os.path.exists(destination) and supports_resume:
        downloaded = os.path.getsize(destination)
        if total_size and downloaded >= total_size:
            print(f"\nFile already completely downloaded: {destination}")
            return True
        elif downloaded > 0:
            print(f"\nResuming download from {format_size(downloaded)}")
            headers['Range'] = f'bytes={downloaded}-'
    
    # Prepare for progress tracking
    start_time = time.time()
    init_downloaded = downloaded  # Keep the initial download amount for speed calculation
    last_update_time = start_time
    last_downloaded = downloaded  # For calculating current download speed
    
    # Setup for retrying connection
    for attempt in range(MAX_RETRIES):
        try:
            # Open the file in append mode if resuming, otherwise write mode
            file_mode = 'ab' if downloaded > 0 else 'wb'
            
            with requests.get(url, headers=headers, stream=True, 
                             timeout=(CONNECT_TIMEOUT, READ_TIMEOUT)) as response:
                response.raise_for_status()
                
                # If we requested a range but got the whole file, adjust our counter
                if downloaded > 0 and response.status_code != 206:
                    print("Warning: Server doesnt support resuming. Starting from beginning.")
                    downloaded = 0
                    file_mode = 'wb'
                
                # Update total_size if we can get it from headers
                if total_size is None and 'content-length' in response.headers:
                    total_size = int(response.headers['content-length']) + downloaded
                
                print(f"\n[Download Progress - {game_name} ({game_id})]")
                
                with open(destination, file_mode) as f:
                    for chunk in response.iter_content(chunk_size=CHUNK_SIZE):
                        if chunk:
                            f.write(chunk)
                            downloaded += len(chunk)
                            current_time = time.time()
                            
                            # Update progress every 0.5 seconds
                            if (current_time - last_update_time) >= 0.5:
                                last_update_time = current_time
                                
                                # Calculate progress values
                                elapsed_time = current_time - start_time
                                elapsed_seconds = int(elapsed_time)
                                progress_percent = int((downloaded / total_size) * 100) if total_size and total_size > 0 else 0
                                
                                # Calculate overall average speed
                                avg_download_speed = (downloaded - init_downloaded) / elapsed_time if elapsed_time > 0 else 0
                                avg_download_speed_mbps = avg_download_speed * 8 / 1024 / 1024  # Convert to Mbps
                                
                                # Calculate current window speed (last 0.5 seconds)
                                current_window_size = downloaded - last_downloaded
                                current_speed = current_window_size / (current_time - last_update_time + 0.001)
                                current_speed_mbps = current_speed * 8 / 1024 / 1024  # Convert to Mbps
                                last_downloaded = downloaded
                                
                                # Calculate remaining time based on average speed
                                remaining_bytes = total_size - downloaded if total_size else 0
                                if avg_download_speed > 0 and remaining_bytes > 0:
                                    remaining_seconds = int(remaining_bytes / avg_download_speed)
                                else:
                                    remaining_seconds = 0
                                
                                # Simple output - replace previous line with new status
                                # Carriage return to move cursor to beginning of line
                                sys.stdout.write("\r" + " " * 80 + "\r")  # Clear line
                                
                                # Print progress information
                                if total_size and total_size > 0:
                                    prog_str = f"Progress: {progress_percent}% | "
                                else:
                                    prog_str = ""
                                
                                # Show actual progress info
                                size_info = f"of {format_size(total_size)}" if total_size else ""
                                status = f"{prog_str}Downloaded: {format_size(downloaded)} {size_info} | Speed: {avg_download_speed_mbps:.2f} Mbps"
                                sys.stdout.write(status)
                                sys.stdout.flush()
            
            # Final update
            elapsed_time = time.time() - start_time
            elapsed_seconds = int(elapsed_time)
            avg_download_speed = (downloaded - init_downloaded) / elapsed_time if elapsed_time > 0 else 0
            avg_download_speed_mbps = avg_download_speed * 8 / 1024 / 1024
            
            # Print final stats on new lines
            print("\n\nDownload completed successfully!")
            print(f"Total Size: {format_size(downloaded)}")
            print(f"Average Speed: {avg_download_speed_mbps:.2f} Mbps")
            print(f"Time Elapsed: {format_time(elapsed_seconds)}")
            
            return True
            
        except KeyboardInterrupt:
            print("\nDownload cancelled by user.")
            # Dont delete the file - we might want to resume later
            return False
            
        except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
            # Handle connection timeout with exponential backoff retry
            delay = min(RETRY_BASE_DELAY * (2 ** attempt) + random.uniform(0, 1), RETRY_MAX_DELAY)
            if attempt < MAX_RETRIES - 1:
                print(f"\nConnection timed out or lost: {str(e)}. Retrying in {delay:.1f} seconds...")
                print(f"Downloaded so far: {format_size(downloaded)}")
                time.sleep(delay)
                # Update headers for resuming from the current position
                headers['Range'] = f'bytes={downloaded}-'
                last_update_time = time.time()  # Reset the update timer
                # Print a new header for the next attempt
                print(f"\n[Download Progress - {game_name} ({game_id}) - Attempt {attempt+2}]")
            else:
                print(f"\nDownload failed after {MAX_RETRIES} attempts: {str(e)}")
                print(f"Downloaded: {format_size(downloaded)}. You can try running the command again to resume.")
                return False
            
        except requests.exceptions.RequestException as e:
            # Handle other request exceptions
            if attempt < MAX_RETRIES - 1:
                delay = min(RETRY_BASE_DELAY * (2 ** attempt) + random.uniform(0, 1), RETRY_MAX_DELAY)
                print(f"\nDownload error: {str(e)}. Retrying in {delay:.1f} seconds...")
                time.sleep(delay)
                # Update headers for resuming from the current position
                headers['Range'] = f'bytes={downloaded}-'
                last_update_time = time.time()
                # Print a new header for the next attempt
                print(f"\n[Download Progress - {game_name} ({game_id}) - Attempt {attempt+2}]")
            else:
                print(f"\nDownload failed after {MAX_RETRIES} attempts: {str(e)}")
                print(f"Downloaded: {format_size(downloaded)}. You can try running the command again to resume.")
                return False
        
        except IOError as e:
            # Handle file I/O errors
            print(f"\nFile I/O error: {str(e)}")
            return False
    
    return False

def main():
    """Main entry point for the script"""
    # Check if we have enough arguments
    if len(sys.argv) < 5:
        print("Usage: python ccls_downloader.py <download_url> <output_file> <game_name> <game_id> [<total_size_bytes>]")
        return 1
    
    try:
        download_url = sys.argv[1]
        output_file = sys.argv[2]
        game_name = sys.argv[3]
        game_id = sys.argv[4]
        
        # Optional size parameter
        expected_size = None
        if len(sys.argv) > 5:
            try:
                expected_size = int(sys.argv[5])
            except ValueError:
                print(f"Warning: Invalid size parameter '{sys.argv[5]}', will use content-length from server")
        
        # Create output directory if it doesnt exist
        output_dir = os.path.dirname(output_file)
        if output_dir and not os.path.exists(output_dir):
            try:
                os.makedirs(output_dir)
            except Exception as e:
                print(f"Error: Could not create directory: {str(e)}")
                return 1
        
        # Start download
        result = download_file(download_url, output_file, game_name, game_id, expected_size)
        
        # Return success or failure code
        return 0 if result else 1
        
    except Exception as e:
        print(f"Error: Unexpected error: {str(e)}")
        return 1

if __name__ == "__main__":
    try:
        sys.exit(main())
    except KeyboardInterrupt:
        print("\nDownload cancelled by user.")
        sys.exit(1)
'@
        
                # Write the Python script to file
                Set-Content -Path $pythonScript -Value $pythonCode -Encoding UTF8
                Write-CCLSHost "#Created Python script at $pythonScript" -Log
            }
            
            # If we have Python and the downloader script, use it
            if ($pythonAvailable -and (Test-Path $pythonScript)) {
                Write-CCLSHost "#Using Python high-speed downloader" -Log
                
                # Convert the game size to bytes if possible (for progress calculation)
                $sizeInBytes = $null
                if ($gameSize -match "(\d+\.?\d*)\s*(GB|MB|KB|B)") {
                    $value = [double]$matches[1]
                    $unit = $matches[2]
                    
                    switch ($unit) {
                        "GB" { $sizeInBytes = [math]::Round($value * 1GB) }
                        "MB" { $sizeInBytes = [math]::Round($value * 1MB) }
                        "KB" { $sizeInBytes = [math]::Round($value * 1KB) }
                        "B"  { $sizeInBytes = [math]::Round($value) }
                    }
                }
                
                try {
                    # Build the command line with proper quoting
                    $argList = @()
                    $argList += "`"$pythonScript`""
                    $argList += "`"$downloadUrl`""
                    $argList += "`"$downloadPath`""
                    $argList += "`"$gameName`""
                    $argList += "`"$gameId`""
                    
                    if ($sizeInBytes) {
                        $argList += "$sizeInBytes"
                    }
                    
                    $commandLine = "python $($argList -join ' ')"
                    Write-CCLSHost "#Running command: $commandLine" -Log
                    
                    # Run Python as a normal process without redirecting output
                    # This allows it to directly handle console output
                    $process = Start-Process python -ArgumentList $argList -NoNewWindow -PassThru -Wait
                    
                    if ($process.ExitCode -ne 0) {
                        Write-CCLSHost "Python downloader failed with exit code $($process.ExitCode)" -ForegroundColor Red -Log
                        Write-CCLSHost "#Falling back to PowerShell download method" -Log
                        Use-PowerShellDownload -downloadUrl $downloadUrl -downloadPath $downloadPath -gameName $gameName -gameId $gameId
                    }
                }
                catch {
                    Write-CCLSHost "Error running Python downloader: $($_.Exception.Message)" -ForegroundColor Red -Log
                    Write-CCLSHost "#Falling back to PowerShell download method" -Log
                    Use-PowerShellDownload -downloadUrl $downloadUrl -downloadPath $downloadPath -gameName $gameName -gameId $gameId
                }
            }
            else {
                # Fall back to the original PowerShell download method
                Write-CCLSHost "#Python downloader not available, using PowerShell download method" -Log
                Use-PowerShellDownload -downloadUrl $downloadUrl -downloadPath $downloadPath -gameName $gameName -gameId $gameId
            }
        } 
        catch {
            $pythonAvailable = $false
            Write-CCLSHost "#Python not found, falling back to PowerShell download method" -Log
            Use-PowerShellDownload -downloadUrl $downloadUrl -downloadPath $downloadPath -gameName $gameName -gameId $gameId
        }
        
        # Start extraction process
        Write-CCLSHost "#Starting extraction process..." -Log
        
        try {
            # Define the self-contained extraction function
            function Start-GameExtraction {
                # Load settings independently (similar to ass.ps1 but with adjusted path)
                $scriptPath = $settingsFolder
                $settingsPath = Join-Path -Path $scriptPath -ChildPath "settings.json"
                
                Write-CCLSHost "#Loading settings from: $settingsPath" -Log
                
                # Check if settings file exists
                if (-not (Test-Path -Path $settingsPath)) {
                    Write-CCLSHost "Error: Settings file not found at '$settingsPath'." -ForegroundColor Red -Log
                    return
                }
                
                # Load and parse the settings.json file
                $extractSettings = Get-Content -Path $settingsPath -ErrorAction Stop | ConvertFrom-Json
                
                # Get paths from settings
                $tempDownloadPath = $extractSettings.TempDownloadPath
                $downloadPath = $extractSettings.DownloadPath
                
                Write-CCLSHost "#Loaded TempDownloadPath: $tempDownloadPath" -Log
                Write-CCLSHost "#Loaded DownloadPath: $downloadPath" -Log
                
                # Verify paths exist
                if (-not (Test-Path -Path $tempDownloadPath)) {
                    Write-CCLSHost "Error: TempDownloadPath '$tempDownloadPath' does not exist." -ForegroundColor Red -Log
                    return
                }
                
                if (-not (Test-Path -Path $downloadPath)) {
                    Write-CCLSHost "Error: DownloadPath '$downloadPath' does not exist." -ForegroundColor Red -Log
                    return
                }
                
                # Check if 7-Zip is installed
                $7zipPath = "C:\Program Files\7-Zip\7z.exe"
                if (-not (Test-Path -Path $7zipPath)) {
                    # Try alternative paths
                    $alternativePaths = @(
                        "${env:ProgramFiles(x86)}\7-Zip\7z.exe",
                        ".\7zip\7z.exe"
                    )
                    
                    $found = $false
                    foreach ($path in $alternativePaths) {
                        if (Test-Path -Path $path) {
                            $7zipPath = $path
                            $found = $true
                            break
                        }
                    }
                    
                    if (-not $found) {
                        Write-CCLSHost "Error: 7-Zip is not installed at any expected location." -ForegroundColor Red -Log
                        Write-CCLSHost "Please install 7-Zip or ensure it's in one of these locations:" -ForegroundColor Yellow -Log
                        Write-CCLSHost "  - C:\Program Files\7-Zip\7z.exe" -ForegroundColor Yellow -Log
                        Write-CCLSHost "  - C:\Program Files (x86)\7-Zip\7z.exe" -ForegroundColor Yellow -Log
                        Write-CCLSHost "  - .\7zip\7z.exe" -ForegroundColor Yellow -Log
                        return
                    }
                }
                
                # Get all .7z files in the temp download path
                Write-CCLSHost "#Searching for .7z files in: $tempDownloadPath" -Log
                $7zFiles = Get-ChildItem -Path $tempDownloadPath -Filter "*.7z"
                
                # If no .7z files found, exit
                if ($7zFiles.Count -eq 0) {
                    Write-CCLSHost "No .7z files found in '$tempDownloadPath'." -ForegroundColor Yellow -Log
                    return
                }
                
                Write-CCLSHost "#Found $($7zFiles.Count) .7z files to process." -Log
                
                # Process each .7z file
                foreach ($file in $7zFiles) {
                    $filePath = $file.FullName
                    
                    # Extract the file
                    Write-CCLSHost "Extracting: $filePath to $downloadPath" -ForegroundColor Cyan -Log
                    & "$7zipPath" x "$filePath" -o"$downloadPath" -y
                    
                    # Check if extraction was successful
                    if ($LASTEXITCODE -eq 0) {
                        # Delete the original .7z file
                        Write-CCLSHost "Extraction successful. Deleting original file: $filePath" -ForegroundColor Green -Log
                        Remove-Item -Path $filePath -Force
                    } else {
                        Write-CCLSHost "Failed to extract: $filePath. Skipping deletion." -ForegroundColor Red -Log
                    }
                }
                
                Write-CCLSHost "#All .7z files have been processed." -Log
                Write-CCLSHost "Go to $downloadPath to play the game." -ForegroundColor Cyan -Log
            }
            
            # Call the extraction function
            Start-GameExtraction
        }
        catch {
            Write-CCLSHost "An error occurred during extraction: $($_.Exception.Message)" -ForegroundColor Red -Log
            Write-CCLSHost "Error details: $($_.Exception.StackTrace)" -ForegroundColor Red -Log
        }
    }
    catch {
        Write-CCLSHost "An error occurred: $($_.Exception.Message)" -ForegroundColor Red -Log
    }
}

# Main CLI interface
function Start-MainInterface($username) {
    Write-CCLSHost "`n`nWelcome to CCLS Games CLI Tool, $username!" -ForegroundColor Green -Log
    
    # Load settings to check setup status
    $settings = Initialize-Settings
    Test-VersionUpdate
    
    # Show appropriate message based on setup status
    if ($settings.HasCompletedSetup) {
        Write-CCLSHost "Type 'help' for a list of available commands.`n" -ForegroundColor Cyan -Log
    }
    else {
        Write-CCLSHost "ALERT, type command 'setup' to set critical values before downloading." -ForegroundColor Red -Log
    }
}

# Function to list all available games
function Get-GamesList {
    try {
        # Construct the URL for the game list API
        $gamesListUrl = "$cliApiUrl/list.php"
        
        $params = @{
            Uri = $gamesListUrl
            Method = "GET"
            Headers = @{
                "User-Agent" = "CCLS-CLI/1.0"
            }
        }
        
        Write-CCLSHost "#Fetching game library..." -Log
        
        # Fetch game library from server
        try {
            $response = Invoke-RestMethod @params
            
            # Check if the request was successful
            if (-not $response.success) {
                Write-CCLSHost "Error: $($response.message)" -ForegroundColor Red -Log
                return
            }
            
            # Display game list
            Write-CCLSHost "==========================================================" -ForegroundColor DarkGray -Log
            Write-CCLSHost "Game Library - $($response.count) games available" -ForegroundColor Green -Log
            Write-CCLSHost "==========================================================" -ForegroundColor DarkGray -Log
            
            if ($response.count -eq 0) {
                Write-CCLSHost "No games found in the library." -ForegroundColor Yellow -Log
                return
            }
            
            # Determine the maximum length for proper formatting
            $maxNameLength = ($response.games | ForEach-Object { $_.name.Length } | Measure-Object -Maximum).Maximum
            $maxIdLength = ($response.games | ForEach-Object { $_.id.Length } | Measure-Object -Maximum).Maximum
            
            # Ensure minimum column widths
            $nameColumnWidth = [Math]::Max($maxNameLength, 30)
            $idColumnWidth = [Math]::Max($maxIdLength, 8)
            
            # Create header
            Write-CCLSHost "$("Game Name".PadRight($nameColumnWidth)) => $("CG Number".PadRight($idColumnWidth))" -ForegroundColor Cyan -Log
            Write-CCLSHost "$("-" * $nameColumnWidth) => $("-" * $idColumnWidth)" -ForegroundColor Cyan -Log
            
            # Print each game with proper formatting
            foreach ($game in $response.games) {
                Write-CCLSHost "$($game.name.PadRight($nameColumnWidth)) => $($game.id)" -ForegroundColor White -Log
            }
            
            Write-CCLSHost "Use 'search [cgnumber]' to get detailed information about a specific game." -ForegroundColor Yellow -Log
            
            # REMOVED: The following lines that were causing the extra keypress requirement
            # Write-CCLSHost "#Press any key to return to the main menu..." -Log
            # $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
        }
        catch {
            Write-CCLSHost "Error fetching game library: $($_.Exception.Message)" -ForegroundColor Red -Log
            # Also removed the keypress requirement here
        }
    }
    catch {
        Write-CCLSHost "An error occurred while processing game library: $($_.Exception.Message)" -ForegroundColor Red -Log
        # Also removed the keypress requirement here
    }
}

function Start-CclsCliTool {
    Write-CCLSHost "Hello and welcome to the CCLS Games CLI Tool" -ForegroundColor Green -Log
    Write-CCLSHost "Before proceeding to using this software you will need to sign in." -Log
    Write-CCLSHost "If you do not have an account already please go to $baseUrl/login.php?signup to register a new account." -ForegroundColor Cyan -Log
    
    $settings = Initialize-Settings
    
    # Try auto-login if remember login is enabled
    $loginResult = @{ Success = $false }
    if ($settings.RememberLogin) {
        $loginResult = Start-AutoLogin
    }
    
    # If auto-login failed or is disabled, do manual login
    if (-not $loginResult.Success) {
        $loginResult = Start-ManualLogin
    }
    
    # If login succeeded, show main interface
    if ($loginResult.Success) {
        # Check for version updates
        Test-VersionUpdate
        
        # Notice we removed the "`n`n" at the beginning of the welcome message
        Write-CCLSHost "Welcome to CCLS Games CLI Tool, $($loginResult.Username)!" -ForegroundColor Green -Log
        
        # Show appropriate message based on setup status
        $settings = Initialize-Settings
        if ($settings.HasCompletedSetup) {
            Write-CCLSHost "Type 'help' for a list of available commands." -ForegroundColor Cyan -Log
        }
        else {
            Write-CCLSHost "ALERT, type command 'setup' to set critical values before downloading." -ForegroundColor Red -Log
        }
        
        Start-CommandInterface -username $loginResult.Username
    }
    else {
        Write-CCLSHost "Login failed. Press any key to exit..." -ForegroundColor Red -Log
        $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
    }
}

function Clear-ConsoleScreen {
    # Log the clear command
    Write-CCLSHost "#User cleared the console screen" -Log -NoConsole

    # Use the PowerShell Clear-Host cmdlet to clear the console
    Clear-Host
}

# Function to format file size in a human-readable way
function Format-Size {
    param (
        [long]$Size
    )
    
    if ($Size -ge 1GB) {
        return "{0:N2} GB" -f ($Size / 1GB)
    }
    elseif ($Size -ge 1MB) {
        return "{0:N2} MB" -f ($Size / 1MB)
    }
    elseif ($Size -ge 1KB) {
        return "{0:N2} KB" -f ($Size / 1KB)
    }
    else {
        return "$Size B"
    }
}

# Function to get folder size including subfolders
function Get-FolderSize {
    param (
        [string]$Path
    )
    
    $totalSize = 0
    
    try {
        # Get all files in the folder and subfolders
        $files = Get-ChildItem -Path $Path -Recurse -File -ErrorAction SilentlyContinue
        foreach ($file in $files) {
            $totalSize += $file.Length
        }
    }
    catch {
        Write-CCLSHost "Error calculating folder size: $($_.Exception.Message)" -ForegroundColor Red -Log
    }
    
    return $totalSize
}

# Function to list games in the download directory
function Get-InstalledGames {
    param (
        [switch]$Detailed
    )
    
    # Get settings to find download directory
    $settings = Initialize-Settings
    $downloadPath = $settings.DownloadPath
    
    # Check if download path exists
    if (-not (Test-Path $downloadPath)) {
        Write-CCLSHost "Downloads folder does not exist yet. No games are installed." -ForegroundColor Yellow -Log
        return
    }
    
    # Get all folders in the download path
    try {
        $gameFolders = Get-ChildItem -Path $downloadPath -Directory
        
        # If no games found
        if ($gameFolders.Count -eq 0) {
            Write-CCLSHost "No games found in $downloadPath" -ForegroundColor Yellow -Log
            return
        }
        
        # Header
        Write-CCLSHost "`nInstalled Games" -ForegroundColor Green -Log
        Write-CCLSHost "==============" -ForegroundColor Green -Log
        
        # Calculate total size if detailed view
        $totalSize = 0
        
        # List each game
        foreach ($folder in $gameFolders) {
            if ($Detailed) {
                # Get folder size for detailed view
                $size = Get-FolderSize -Path $folder.FullName
                $totalSize += $size
                $sizeFormatted = Format-Size -Size $size
                
                # Display with size
                Write-CCLSHost "$($folder.Name) - $sizeFormatted" -Log
            }
            else {
                # Simple list of names
                Write-CCLSHost "$($folder.Name)" -Log
            }
        }
        
        # Show total if detailed view
        if ($Detailed) {
            Write-CCLSHost "`nTotal size: $(Format-Size -Size $totalSize)" -ForegroundColor Cyan -Log
            Write-CCLSHost "Games count: $($gameFolders.Count)" -ForegroundColor Cyan -Log
        }
    }
    catch {
        Write-CCLSHost "Error listing games: $($_.Exception.Message)" -ForegroundColor Red -Log
    }
}

function Get-FullFileTree {
    param (
        [string]$Path,
        [string]$Indent = ""
    )
    
    $output = @()
    
    try {
        # Get items in the current directory
        $items = Get-ChildItem -Path $Path -ErrorAction SilentlyContinue
        
        foreach ($item in $items) {
            if ($item.PSIsContainer) {
                # It's a directory
                $output += "$Indent|- $($item.Name) (folder)"
                
                # Recursively get all subdirectories with no depth limit
                $childOutput = Get-FullFileTree -Path $item.FullName -Indent "$Indent|   "
                if ($childOutput) {
                    $output += $childOutput
                }
            }
            else {
                # It's a file
                $sizeFormatted = Format-Size -Size $item.Length
                $output += "$Indent|- $($item.Name) ($sizeFormatted)"
            }
        }
    }
    catch {
        $output += "$Indent Error accessing path: $($_.Exception.Message)"
    }
    
    return $output
}

# Updated function to list information about a specific game (with unlimited file tree depth)
function Get-GameInfo {
    param (
        [string]$GameName,
        [switch]$Detailed
    )
    
    # Get settings to find download directory
    $settings = Initialize-Settings
    $downloadPath = $settings.DownloadPath
    $gamePath = Join-Path -Path $downloadPath -ChildPath $GameName
    
    # Check if game exists
    if (-not (Test-Path $gamePath)) {
        Write-CCLSHost "Game '$GameName' not found in $downloadPath" -ForegroundColor Yellow -Log
        return
    }
    
    # Get game size
    $size = Get-FolderSize -Path $gamePath
    $sizeFormatted = Format-Size -Size $size
    
    # Header
    Write-CCLSHost "`nGame Information: $GameName" -ForegroundColor Green -Log
    Write-CCLSHost "=======================" -ForegroundColor Green -Log
    Write-CCLSHost "Location: $gamePath" -Log
    Write-CCLSHost "Size: $sizeFormatted" -Log
    
    # If detailed view, show file tree
    if ($Detailed) {
        Write-CCLSHost "`nFile Structure (full directory tree):" -ForegroundColor Cyan -Log
        $fileTree = Get-FullFileTree -Path $gamePath
        foreach ($line in $fileTree) {
            Write-CCLSHost $line -Log
        }
    }
}

# Function to delete a game
function Remove-Game {
    param (
        [string]$GameName,
        [switch]$Force
    )
    
    # Get settings to find download directory
    $settings = Initialize-Settings
    $downloadPath = $settings.DownloadPath
    $gamePath = Join-Path -Path $downloadPath -ChildPath $GameName
    
    # Check if game exists
    if (-not (Test-Path $gamePath)) {
        Write-CCLSHost "Game '$GameName' not found in $downloadPath" -ForegroundColor Yellow -Log
        return
    }
    
    # Get game size for informational purposes
    $size = Get-FolderSize -Path $gamePath
    $sizeFormatted = Format-Size -Size $size
    
    # Provide information about what will be deleted
    Write-CCLSHost "`nGame Deletion: $GameName" -ForegroundColor Red -Log
    Write-CCLSHost "===================" -ForegroundColor Red -Log
    Write-CCLSHost "Location: $gamePath" -Log
    Write-CCLSHost "Size: $sizeFormatted" -Log
    
    # If not forced, prompt for confirmation
    if (-not $Force) {
        Write-CCLSHost "`nWARNING: This will permanently delete the game and all its files!" -ForegroundColor Yellow -Log
        Write-CCLSHost "Are you sure you want to delete '$GameName'? (Y/N)" -ForegroundColor Yellow -Log
        
        $confirmation = Read-Host
        Write-CCLSHost "$confirmation" -NoConsole -Log
        
        if ($confirmation.ToLower() -ne "y") {
            Write-CCLSHost "Deletion cancelled." -ForegroundColor Green -Log
            return
        }
    }
    
    # Proceed with deletion
    try {
        # Use Remove-Item with -Recurse to delete the game folder and all contents
        Remove-Item -Path $gamePath -Recurse -Force
        
        Write-CCLSHost "`nGame '$GameName' has been successfully deleted." -ForegroundColor Green -Log
        Write-CCLSHost "Freed up $sizeFormatted of disk space." -ForegroundColor Green -Log
    }
    catch {
        Write-CCLSHost "Error deleting game: $($_.Exception.Message)" -ForegroundColor Red -Log
    }
}

# Version checking function
function Test-VersionUpdate {
    # Current version - update this when releasing new versions
    $currentVersion = "1.1.0"
    
    try {
        # Get the latest version from the server
        $params = @{
            Uri = "https://games.ccls.icu/CLI/latest.txt"
            Method = "GET"
            Headers = @{
                "User-Agent" = "CCLS-CLI/1.0"
            }
        }
        
        Write-CCLSHost "#Checking for updates..." -Log
        $latestVersion = Invoke-RestMethod @params
        
        # Strip any whitespace or newlines
        $latestVersion = $latestVersion.Trim()
        
        # Compare versions
        if ($currentVersion -ne $latestVersion) {
            Write-CCLSHost "ALERT, you are running $currentVersion run 'update' command to update to latest version $latestVersion" -ForegroundColor Red -Log
        }
        
        # Create and return result without displaying it
        $result = [PSCustomObject]@{
            CurrentVersion = $currentVersion
            LatestVersion = $latestVersion
            IsLatest = ($currentVersion -eq $latestVersion)
        }
        
        # Use Write-Output to return without console display
        return $result
    }
    catch {
        Write-CCLSHost "#Error checking for updates: $($_.Exception.Message)" -Log
        
        # Create and return result without displaying it
        $result = [PSCustomObject]@{
            CurrentVersion = $currentVersion
            LatestVersion = $null
            IsLatest = $true  # Assume latest if can't check to avoid unnecessary alerts
        }
        
        return $result
    }
}

# Function to handle the update process
function Update-CliTool {
    Write-CCLSHost "Checking for updates..." -ForegroundColor Cyan -Log
    
    # Get version information and suppress automatic output
    $versionInfo = Test-VersionUpdate
    $currentVersion = $versionInfo.CurrentVersion
    $latestVersion = $versionInfo.LatestVersion
    
    # Make sure we have valid version information
    if ([string]::IsNullOrWhiteSpace($latestVersion)) {
        Write-CCLSHost "Error: Couldn't retrieve latest version information." -ForegroundColor Red -Log
        Write-CCLSHost "Please check your internet connection and try again later." -ForegroundColor Red -Log
        return
    }
    
    # Compare versions
    if ($versionInfo.IsLatest) {
        Write-CCLSHost "You are already running the latest version ($currentVersion)." -ForegroundColor Green -Log
        return
    }
    
    Write-CCLSHost "New version available: $latestVersion (Current: $currentVersion)" -ForegroundColor Yellow -Log
    Write-CCLSHost "Starting update process..." -ForegroundColor Cyan -Log
    
    try {
        # Determine the current script directory
        $scriptLocation = if ($PSScriptRoot) {
            # If running from a script, use its location
            $PSScriptRoot
        } else {
            # If running in console, use current directory
            (Get-Location).Path
        }
        
        # Get the current script path
        $scriptPath = Join-Path -Path $scriptLocation -ChildPath "CLI.ps1"
        
        # Set the update path
        $updatePath = Join-Path -Path $scriptLocation -ChildPath "update.ps1"
        
        # Create backups directory if it doesn't exist
        $backupsFolder = Join-Path -Path $scriptLocation -ChildPath "backups"
        if (-not (Test-Path $backupsFolder)) {
            New-Item -Path $backupsFolder -ItemType Directory -Force | Out-Null
            Write-CCLSHost "#Created backups directory at $backupsFolder" -Log
        }
        
        # Download the new CLI file directly to the script directory
        $cliDownloadUrl = "https://games.ccls.icu/CLI/version/CLI.ps1"
        
        Write-CCLSHost "Downloading update to $updatePath..." -ForegroundColor Cyan -Log
        
        $webClient = New-Object System.Net.WebClient
        $webClient.Headers.Add("User-Agent", "CCLS-CLI/1.0")
        $webClient.DownloadFile($cliDownloadUrl, $updatePath)
        
        Write-CCLSHost "Download completed." -ForegroundColor Green -Log
        
        # Ask for confirmation
        Write-CCLSHost "Are you sure you want to update to version $latestVersion? (Y/N)" -ForegroundColor Yellow -Log
        $confirmation = Read-Host
        Write-CCLSHost "$confirmation" -NoConsole -Log
        
        if ($confirmation.ToLower() -ne "y") {
            Write-CCLSHost "Update cancelled." -ForegroundColor Yellow -Log
            # Clean up downloaded file
            if (Test-Path $updatePath) {
                Remove-Item -Path $updatePath -Force
            }
            return
        }
        
        # Create a backup with timestamp in the backups folder
        $timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
        $backupFileName = "CLI_v$($currentVersion)_$timestamp.ps1.bak"
        $backupPath = Join-Path -Path $backupsFolder -ChildPath $backupFileName
        
        Copy-Item -Path $scriptPath -Destination $backupPath -Force
        Write-CCLSHost "Created backup at $backupPath" -ForegroundColor Cyan -Log
        
        # Read the new CLI content
        $newContent = Get-Content -Path $updatePath -Raw
        
        # Replace the current script with the new content
        Set-Content -Path $scriptPath -Value $newContent -Force
        
        # Clean up the update file
        Remove-Item -Path $updatePath -Force
        
        Write-CCLSHost "Successfully downloaded version $latestVersion" -ForegroundColor Green -Log
        Write-CCLSHost "Restart the program to apply update." -ForegroundColor Yellow -Log
        
        # Exit the tool to allow the user to restart with the new version
        Write-CCLSHost "Press any key to exit..." -ForegroundColor Cyan -Log
        $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
        Exit
    }
    catch {
        Write-CCLSHost "Error during update process: $($_.Exception.Message)" -ForegroundColor Red -Log
        Write-CCLSHost "Update failed. Please try again later." -ForegroundColor Red -Log
    }
}

# Modified Start-CommandInterface function to include the secret command
function Start-CommandInterface($username) {
    $running = $true
    while ($running) {
        Write-CCLSHost "CCLS>" -ForegroundColor Yellow -NoNewline -Log
        $command = Read-Host
        Write-CCLSHost "$command" -NoConsole -Log
        
        switch -Regex ($command.ToLower()) {
            "^exit|^quit" {
                $running = $false
                Write-CCLSHost "Thank you for using the CCLS Games CLI Tool. Goodbye!" -ForegroundColor Cyan -Log
            }
            "^clear|^cls" {
                # Call the clear screen function
                Clear-ConsoleScreen
            }
            "^help" {
                Write-CCLSHost "Available Commands:" -ForegroundColor Green -Log
                Write-CCLSHost "  help                - Show this help message" -Log
                Write-CCLSHost "  clear, cls          - Clear the console screen" -Log
                Write-CCLSHost "  setup               - Configure download directories" -Log
                Write-CCLSHost "  search [cg0000]     - Search for game information by CG number" -Log
                Write-CCLSHost "  search library      - List all available games for download" -Log
                Write-CCLSHost "  get [cg0000]        - Download and install a game by CG number" -Log
                Write-CCLSHost "  get [cg0000] -y     - Download and install a game by CG number without confirmation" -Log
                Write-CCLSHost "  list games          - List installed games (names only)" -Log
                Write-CCLSHost "  list games -d       - List installed games with sizes" -Log
                Write-CCLSHost "  list game [name]    - Show info about a specific game" -Log
                Write-CCLSHost "  list game [name] -d - Show detailed info about a specific game with file tree" -Log
                Write-CCLSHost "  del [name]          - Delete a game (with confirmation)" -Log
                Write-CCLSHost "  del [name] -y       - Delete a game without confirmation" -Log
                Write-CCLSHost "  update              - Update the CLI tool to the latest version" -Log
                Write-CCLSHost "  exit, quit          - Exit the application" -Log
                Write-CCLSHost "  logout              - Log out and exit" -Log
                Write-CCLSHost "  forget              - Remove stored credentials" -Log
            }
            "^update$" {
                Update-CliTool
            }
            "^setup" {
                Start-Setup
            }
            "^search$" {
                Write-CCLSHost "To use the search command please provide a valid CG number in this format 'search cg0000'" -ForegroundColor Cyan -Log
                Write-CCLSHost "To get the CG number of a game, go to the main games page and click on any game, then check" -ForegroundColor Cyan -Log
                Write-CCLSHost "the URL 'https://games.ccls.icu/game.php?id=cg0000' and the 'cg0000' part in URL is the CG number" -ForegroundColor Cyan -Log
            }
            "^search\s+(cg\d{4})$" {
                $cgNumber = $matches[1]
                Search-Game -cgNumber $cgNumber
            }
            "^get$" {
                Write-CCLSHost "To use the get command please provide a valid CG number in this format 'get cg0000'" -ForegroundColor Cyan -Log
                Write-CCLSHost "To get the CG number of a game, go to the main games page and click on any game, then check" -ForegroundColor Cyan -Log
                Write-CCLSHost "the URL 'https://games.ccls.icu/game.php?id=cg0000' and the 'cg0000' part in URL is the CG number." -ForegroundColor Cyan -Log
                Write-CCLSHost "You can use 'get cg0000 -y' to skip confirmation prompt." -ForegroundColor Cyan -Log
                Write-CCLSHost "ALERT, the get command will start the downloading process of the game specified, to stop it do 'Ctrl C'" -ForegroundColor Red -Log
            }
            "^get\s+(cg\d{4})(?:\s+-y)?$" {
                $cgNumber = $matches[1]
                $skipConfirmation = $command -match "-y$"
                Get-Game -cgNumber $cgNumber -SkipConfirmation:$skipConfirmation
            }
            "^search\s+library$|^library$" {
                Get-GamesList
            }
            "^list\s+games$" {
                # List games with minimal detail
                Get-InstalledGames
            }
            "^list\s+games\s+-d$" {
                # List games with detailed information
                Get-InstalledGames -Detailed
            }
            "^list\s+game\s+(.+?)(?:\s+-d)?$" {
                $gameName = $matches[1]
                $detailed = $command -match "-d$"
                
                # Show information about a specific game
                Get-GameInfo -GameName $gameName -Detailed:$detailed
            }
            "^del\s+(.+?)(?:\s+-y)?$" {
                $gameName = $matches[1]
                $force = $command -match "-y$"
                
                # Delete the specified game
                Remove-Game -GameName $gameName -Force:$force
            }
            "^logout" {
                $running = $false
                Write-CCLSHost "Logging out..." -ForegroundColor Cyan -Log
                # Clear the current session but keep credentials if they exist
            }
            "^forget" {
                if (Test-Path $credentialsFile) {
                    Remove-Item -Path $credentialsFile -Force
                    $settings = Initialize-Settings
                    $settings.RememberLogin = $false
                    Save-Settings -settings $settings
                    Write-CCLSHost "Stored credentials have been removed." -ForegroundColor Green -Log
                }
                else {
                    Write-CCLSHost "No stored credentials found." -ForegroundColor Yellow -Log
                }
            }
            default {
                Write-CCLSHost "Unknown command. Type 'help' for a list of available commands." -ForegroundColor Red -Log
            }
        }
    }
}

# Add a line to log script completion
function End-Logging {
    $endTime = Get-Date
    "CCLS Games CLI Session ended at $endTime, duration: $(($endTime - [DateTime]::ParseExact($script:sessionStartTime, 'yyyy-MM-dd_HH-mm-ss', $null)).ToString())" | Out-File -FilePath $script:logFile -Append
}

# Register script exit event to ensure logging is completed
Register-EngineEvent -SourceIdentifier ([System.Management.Automation.PsEngineEvent]::Exiting) -Action {
    End-Logging
} | Out-Null

# Start the application
Try {
    Start-CclsCliTool
}
Finally {
    # Ensure logging is completed even if script exits unexpectedly
    End-Logging
}