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 Update-DownloadProgress {
param (
[long]$bytesReceived,
[long]$totalBytes,
[int]$progressPercentage,
[TimeSpan]$elapsedTime,
[double]$downloadSpeed,
[string]$gameName,
[string]$gameId
)
# Calculate values for display
$downloadSpeedMbps = [math]::Round($downloadSpeed / 1024 / 1024 * 8, 2)
$remainingBytes = $totalBytes - $bytesReceived
$estimatedTimeRemaining = if ($downloadSpeed -gt 0) {
[TimeSpan]::FromSeconds([math]::Floor($remainingBytes / $downloadSpeed))
} else {
[TimeSpan]::Zero
}
# Format sizes for display
$bytesSoFar = if ($bytesReceived -ge 1GB) {
"{0:N2} GB" -f ($bytesReceived / 1GB)
} elseif ($bytesReceived -ge 1MB) {
"{0:N2} MB" -f ($bytesReceived / 1MB)
} elseif ($bytesReceived -ge 1KB) {
"{0:N2} KB" -f ($bytesReceived / 1KB)
} else {
"$bytesReceived B"
}
$totalSize = if ($totalBytes -ge 1GB) {
"{0:N2} GB" -f ($totalBytes / 1GB)
} elseif ($totalBytes -ge 1MB) {
"{0:N2} MB" -f ($totalBytes / 1MB)
} elseif ($totalBytes -ge 1KB) {
"{0:N2} KB" -f ($totalBytes / 1KB)
} else {
"$totalBytes B"
}
$estimatedTime = if ($estimatedTimeRemaining.TotalHours -ge 1) {
"{0:D2}:{1:D2}:{2:D2}" -f $estimatedTimeRemaining.Hours, $estimatedTimeRemaining.Minutes, $estimatedTimeRemaining.Seconds
} else {
"{0:D2}:{1:D2}" -f $estimatedTimeRemaining.Minutes, $estimatedTimeRemaining.Seconds
}
$elapsedTimeStr = if ($elapsedTime.TotalHours -ge 1) {
"{0:D2}:{1:D2}:{2:D2}" -f $elapsedTime.Hours, $elapsedTime.Minutes, $elapsedTime.Seconds
} else {
"{0:D2}:{1:D2}" -f $elapsedTime.Minutes, $elapsedTime.Seconds
}
# Create the progress information lines
$lines = @(
"Progress: $progressPercentage%",
"Downloaded: $bytesSoFar of $totalSize",
"Speed: $downloadSpeedMbps Mbps",
"Time Elapsed: $elapsedTimeStr",
"Estimated Time Remaining: $estimatedTime",
"Press Ctrl+C to cancel the download"
)
# Log the progress
foreach ($line in $lines) {
$line | Out-File -FilePath $script:logFile -Append
}
# First time initialization
if (-not $script:progressInitialized) {
# Mark this as our first time
$script:progressInitialized = $true
# Print a header for the download progress - FIXED to properly display game name
Write-CCLSHost "`n[Download Progress - $gameName ($gameId)]" -ForegroundColor Green
# Create the initial progress block
foreach ($line in $lines) {
if ($line -eq $lines[-1]) {
Write-CCLSHost $line -ForegroundColor Red
} else {
Write-CCLSHost $line -ForegroundColor Yellow
}
}
# Store the beginning line number for future updates
# Note: We're using the current cursor position - lines count to get the top line
$script:topProgressLine = $host.UI.RawUI.CursorPosition.Y - $lines.Count
}
else {
# Save current cursor position
$currentPosition = $host.UI.RawUI.CursorPosition
# Update each line of the progress display in place
for ($i = 0; $i -lt $lines.Count; $i++) {
# Set cursor to beginning of this progress line
$host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 0, ($script:topProgressLine + $i)
# Clear the entire line first
$lineWidth = $host.UI.RawUI.BufferSize.Width - 1
Write-Host (" " * $lineWidth) -NoNewline
# Reset cursor and write the content
$host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 0, ($script:topProgressLine + $i)
if ($i -eq ($lines.Count - 1)) {
# Last line in red
Write-Host $lines[$i] -ForegroundColor Red
} else {
# Other lines in yellow
Write-Host $lines[$i] -ForegroundColor Yellow
}
}
# Restore cursor position
$host.UI.RawUI.CursorPosition = $currentPosition
}
}
# Function to properly reset the progress display when download completes or fails
function Reset-ProgressDisplay {
# Only do something if we've initialized a progress display
if ($script:progressInitialized) {
# Save current cursor position
$currentPosition = $host.UI.RawUI.CursorPosition
# Move to one line after the progress display to add completion message
$host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 0, ($script:topProgressLine + 6)
# Reset the initialization flag
$script:progressInitialized = $false
$script:topProgressLine = $null
# We don't reset cursor position here because we want to continue output from the end of progress display
}
}
# Function to call before starting a new download
function Initialize-DownloadTracking {
# Ensure we always start fresh
$script:progressInitialized = $false
$script:topProgressLine = $null
# Reset global variables used by event handlers
$global:startTime = $null
$global:lastUpdateTime = $null
}
# Function to download and extract a game
function Get-Game($cgNumber) {
# Initialize progress tracking for a fresh download
Initialize-DownloadTracking
# 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
Write-CCLSHost "Download Location: $downloadPath" -ForegroundColor Cyan -Log
Write-CCLSHost "Extract Location: $($settings.DownloadPath)" -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
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
}
# Set global variables that will be used for the download progress display
# Make sure we're explicitly setting these so they're accessible in the event handlers
$global:gameNameForProgress = $gameName
$global:gameIdForProgress = $gameId
# Start download
$webClient = New-Object System.Net.WebClient
$startTime = Get-Date
$lastUpdateTime = $startTime
# Set global variables for use in event handlers
$global:startTime = $startTime
$global:lastUpdateTime = $lastUpdateTime
# Create event handlers for download progress
$downloadProgressEvent = Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -Action {
$bytesReceived = $EventArgs.BytesReceived
$totalBytes = $EventArgs.TotalBytesToReceive
$progressPercentage = $EventArgs.ProgressPercentage
$currentTime = Get-Date
# Only update display every 500ms to avoid console flicker
if (($currentTime - $global:lastUpdateTime).TotalMilliseconds -ge 500) {
$global:lastUpdateTime = $currentTime
$elapsedTime = $currentTime - $global:startTime
$elapsedSeconds = [math]::Floor($elapsedTime.TotalSeconds)
$downloadSpeed = if ($elapsedSeconds -gt 0) { $bytesReceived / $elapsedSeconds } else { 0 }
# Use the new progress update function - PASSING PROPER GAME NAME AND ID
Update-DownloadProgress -bytesReceived $bytesReceived -totalBytes $totalBytes `
-progressPercentage $progressPercentage -elapsedTime $elapsedTime `
-downloadSpeed $downloadSpeed -gameName $global:gameNameForProgress -gameId $global:gameIdForProgress
}
}
$downloadCompletedEvent = Register-ObjectEvent -InputObject $webClient -EventName DownloadFileCompleted -Action {
# Reset progress display properly
Reset-ProgressDisplay
Write-CCLSHost "#Download completed!" -Log
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
}
}
# Start the download
try {
$webClient.DownloadFileAsync([Uri]$downloadUrl, $downloadPath)
# Wait for download to complete
while ($webClient.IsBusy) {
Start-Sleep -Milliseconds 100
}
}
catch {
# Reset the progress display
Reset-ProgressDisplay
Write-CCLSHost "An error occurred during download: $($_.Exception.Message)" -ForegroundColor Red -Log
}
finally {
# Clean up event handlers
if ($downloadProgressEvent) { Unregister-Event -SourceIdentifier $downloadProgressEvent.Name }
if ($downloadCompletedEvent) { Unregister-Event -SourceIdentifier $downloadCompletedEvent.Name }
$webClient.Dispose()
}
}
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
# 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) {
# 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
# Optionally, display a minimal header after clearing
Write-CCLSHost "CCLS Games CLI Tool" -ForegroundColor Green -Log
}
# 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
}
}
# Alternative implementation using a direct embedding approach if web fetch doesn't work
function Show-Dirty-Secret {
try {
$moosArt = Get-Content -Path "moos.txt" -Raw -Encoding UTF8
# If we can't get the file, embed a basic version of the art
if ([string]::IsNullOrEmpty($moosArt)) {
# Embed the ASCII art directly from moos.txt
$moosArt = @'
╢╢▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢╣╣╣
╢╢╣╣╣╣╢╣▓╣▓▓▓▓╣╣╢╢╢╢╢╢╢╢╢╢╢╢╢╢╣╢╣╣╢╣╣╣╣╣╢╢╢╢╢╢╢╢╢▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢╢╢╢╢
╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╣╢╢╣╣╣╣╣╣▒▒▒▒▒▒▒╢▒╢╢╢╣╢╢╢╢╢╢╢╢╢╢╢╢╣╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢╢╢╢╢
▒╢╣╢╢╢╢╢╢╣╣╣╣╣╣╣╣╣╢╣╢╢╢▒╢▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒╢╣╣╣╢╢╣╢╢╢╢╢╢╢╢╢╢╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╣╢╢╢╣
▒▒▒╢╢╢╣╣╣╣╣╣╣╣╢╣▒╢▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒╣╣╣╣╣╢╣╢╢╢╢╢╢╢╢╢╢╢▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╢╢╢╢╢
▒▒▒▒╣╣╢╣╣╣╣╣╣╣╣▒╣╣╣╣▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒╣╣╣╣╣╣╢╣╢╢╢╢╢╢╢╢╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╢╢╢╣
▒▒▒▒▒▒╣╣╣╣╢╣╣╣╣╣╣╣╣▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒╣╣▒╢╣╣╣╣╣╣╣╣╣╣╢╢╢╢╢╢╢╢╢╢╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢
▒▒▒▒▒╣╣╢╣╣╣╢╣╣╣╣╣╣╣╣╣╣╣╣╣▒▒▒╣▒╣╣╣╣╢╣╣╣╣╣╣╢╣╣╣╢╢╢╢╢╢╢╢╢╢╢╢╢╢▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╢
▒▒▒▒▒▒╣╣╣╣╢╣╣╢╣╣╣╣╣╣╣╣╣╣╣╣╣▒╣╣╣╣╣╣╣╣╣╣╢╣╣╣╣╣╢╢╢╢╢╢╣╢╢╢╢╢╢╢╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╢╢
▒▒▒▒╣╣╣╣╣╣╢╢╢╣╢╣╣╣╣╣╣╣╣╣╣╣╣╣╣╣╣╣╣╣╣╢╣╣╢╣╢╢╣╣╣╢╣╢╢╢╢╢╢╢╢╢╢╢╣╣╢╢▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╣
▒▒▒▒╢╣╢╣╢╢╢╢╣╢╣╣╣╢╣╢╢╣╣╣╣╣╣╣╣╣╣╢╢╣╢╣╣╣╣╣╣╢╢╢╣╢╢╢╢╢╢╢╢╢╢╢╣╣╣╣╣╣╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▒▒▒▒▒╢╣╢╣╣╢╢╢╢╢╣╢╢╢╢╣╣╣╢╢╣╣╣╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╢╣╣╣╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▒▒▒▒▒╣╢╢╢╢╢╢╢╢╢╣╢╢╢╢╢╢╣╢╢╣╢╢╢╢╢╢╣╣╣╣╣╣▓▓╣╣╣╣╢╢╢╢╢╢╣╣╢╣╣▓╣╣╢╣╣╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▒▒▒╢╢╢╢╢╢╢╢╢╣╢╢╢╢╢╢╢╣╢╣╢╢╢╢╢╢╢╢╣╢╣▓▓╣╢╢╢╢╣╢╢╢╢╢╢╣╢╣╣╣╢╣▓▓▓╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣
▒▒╢╢╢╣╣╣╣╣▓▓╣▓▓▓╣╣╢╢╢╢╢╢╢╢╢╢╢╢╢╣╣╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╣▓╣╣╣╣╣╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢
▒▒▒╢╢╢╢╢╢╣▓▓╣▓▓▓▓╣╣╣╢╢╢╢╢╢╢╢╢╢╣╣╢╢▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╣▓╣╣▓▓▓▓▓▓▓▓▓▓▓▓╣╣▓▓▓▓▓╣╢
▒▒▒▒╢╢╢╣▓▓▓▓▓▓▓▓▓╣╣╣╢╢╢╢╢╢╢╢╢╢╢╢╢╢╣╣╣╣╣▓▓▓▓▓▓▓▓▓▓╣╢╢╣╣╣╣╣▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢╢╢▓▓▓╢╢╢
▒▒▒▒▒▒╢╢╢╢▓▓▓▓▓╣▓▓▓╣╣╣╢╢╢╢╢╢╢╣╣╢╣╢╢▓▓▓▓▓▓▓▓╣╢╣╣╣╣╣╢╢╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢╢▓▓╣╢╢╢╢
╢╣╣▒╢▒╢╢╢╢╢╢╢╣╣▓▓▓▓▓╣╢╢╢╢╢╣╢╢╢╢╢╣╣╢╢╢╢╢╣╣╣╣╢╢╢╣╣╢╣╣╣╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢╢╣▓╣╢╢╢╢╢
▒▒╢╢▒╣╢╢╢╢╢╣╣▓▓╣▓▓╣╢╢╢╢╢╢╣╣╣▓▓╣╣╣╣╢╢╣╣╢╢╢╣╣╣╣╣╣╣╣╣╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢╢╢╢╢╢╢╢╢╢╢
╣╣╣╣╣╣╣╢╢╢╢╣╣╣╣╣╣╣╢╢╣╢╢╢╣▓▓╣▓▓▓╣▓▓▓▓▓╣╣╣▓╣╢╢╢╣╣╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢╣╣╢╢╢╢╢╢╢╢
╣╣╣╣╣╣╣╢╢╢╢╣╣╣▓▓╣╣╢╣╣╢╢▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╢╢╣╢╢
╣╣╣╣╣╢╢╣╣╢╢╢╣▓▓▓▓▓╣╣╢╢╢╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╣╣
╢╣╣╣╢╣╣╢╣╢╢╢╢╣▓▓▓▓╣╣╢╢╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
╢╢╢╢╢╢╢╢▓▓╣╢╢╢▓▓▓▓▓╣╢╢╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓╣╣╢▓▓▓▓▓▓╣╢╢╢▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓╣╣▓▓▓▓▓▓▓▓╣╢▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓╣╢▓▓▓▓▓▓▓▓▓▓╣╢▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓╣▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╢▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
'@
}
# Display the art directly
Write-Host $moosArt -ForegroundColor Cyan
}
catch {
Write-CCLSHost "moos no no no no no" -ForegroundColor Yellow -Log
Write-CCLSHost "#Error details: $($_.Exception.Message)" -ForegroundColor Red -Log -NoConsole
}
}
# 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
}
# Secret command that isn't shown in help
"^moos$" {
# Call the moos function
Show-Dirty-Secret
}
"^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 - Search for game information by CG number" -Log
Write-CCLSHost " search library - List all available games for download" -Log
Write-CCLSHost " get - Download and install a game by CG number" -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 " exit, quit - Exit the application" -Log
Write-CCLSHost " logout - Log out and exit" -Log
Write-CCLSHost " forget - Remove stored credentials" -Log
}
"^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 "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})$" {
$cgNumber = $matches[1]
Get-Game -cgNumber $cgNumber
}
"^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
}