2017-02-26 19:23:31 +00:00
using System ;
using System.Collections.Generic ;
2020-04-14 10:18:52 +00:00
using CommandLine ;
using CommandLine.Text ;
2017-02-26 19:23:31 +00:00
using System.Diagnostics ;
using System.IO ;
using System.Linq ;
2018-10-20 00:20:53 +00:00
using System.Net ;
2020-04-13 10:10:35 +00:00
using System.Runtime.CompilerServices ;
2017-02-26 19:23:31 +00:00
using System.Threading ;
using System.Threading.Tasks ;
using System.Windows.Forms ;
using HeliosDisplayManagement.InterProcess ;
using HeliosDisplayManagement.Resources ;
using HeliosDisplayManagement.Shared ;
using HeliosDisplayManagement.Steam ;
2020-04-14 10:18:52 +00:00
using HeliosDisplayManagement.Uplay ;
2017-02-26 19:23:31 +00:00
using HeliosDisplayManagement.UIForms ;
2020-04-14 10:18:52 +00:00
using System.Net.NetworkInformation ;
2017-02-26 19:23:31 +00:00
namespace HeliosDisplayManagement
{
internal static class Program
{
2020-04-13 03:47:38 +00:00
2017-02-26 19:23:31 +00:00
internal static bool GoProfile ( Profile profile )
{
if ( profile . IsActive )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
return true ;
2018-10-20 00:27:25 +00:00
}
2017-08-10 14:21:45 +00:00
var instanceStatus = IPCService . GetInstance ( ) . Status ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
try
{
IPCService . GetInstance ( ) . Status = InstanceStatus . Busy ;
var failed = false ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( new SplashForm ( ( ) = >
{
Task . Factory . StartNew ( ( ) = >
{
if ( ! profile . Apply ( ) )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
failed = true ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
} , TaskCreationOptions . LongRunning ) ;
2018-10-20 00:27:25 +00:00
} , 3 , 30 ) . ShowDialog ( ) ! =
DialogResult . Cancel )
2017-02-26 19:23:31 +00:00
{
if ( failed )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Profile_is_invalid_or_not_possible_to_apply ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
return true ;
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
return false ;
}
finally
{
2017-08-10 14:21:45 +00:00
IPCService . GetInstance ( ) . Status = instanceStatus ;
2017-02-26 19:23:31 +00:00
}
}
2020-04-14 10:18:52 +00:00
private static void CreateShortcut ( IReadOnlyList < Profile > profiles , int profileIndex , CommandLineOptions options )
2017-02-26 19:23:31 +00:00
{
if ( profileIndex < 0 )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Selected_profile_is_invalid_or_not_found ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
IPCService . GetInstance ( ) . Status = InstanceStatus . User ;
new ShortcutForm ( profiles [ profileIndex ] )
{
2020-04-14 10:18:52 +00:00
FileName = options . ExecuteFilename ,
SteamAppId = options . ExecuteSteamApp ,
Arguments = options . ExecuteArguments ,
ProcessName = options . ExecuteProcessName ,
Timeout = options . ExecuteProcessTimeout
2017-02-26 19:23:31 +00:00
} . ShowDialog ( ) ;
}
2020-04-14 10:18:52 +00:00
private static void EditProfile ( IList < Profile > profiles , int profileIndex , CommandLineOptions options )
2017-02-26 19:23:31 +00:00
{
if ( profileIndex < 0 )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Selected_profile_is_invalid_or_not_found ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
IPCService . GetInstance ( ) . Status = InstanceStatus . User ;
var editForm = new EditForm ( profiles [ profileIndex ] ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( editForm . ShowDialog ( ) = = DialogResult . OK )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
profiles [ profileIndex ] = editForm . Profile ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if ( ! Profile . SetAllProfiles ( profiles ) )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Failed_to_save_profile ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
}
2020-04-13 10:10:35 +00:00
2017-02-26 19:23:31 +00:00
/// <summary>
/// The main entry point for the application.
/// </summary>
2020-04-13 10:10:35 +00:00
[STAThread]
private static int Main ( string [ ] args )
2017-02-26 19:23:31 +00:00
{
2020-04-13 10:10:35 +00:00
2020-04-16 10:49:11 +00:00
var parser = new Parser ( config = > config . HelpWriter = null ) ;
var parserResult = parser . ParseArguments < CommandLineOptions > ( args ) ;
parserResult . WithParsed < CommandLineOptions > ( options = > RunOptions ( parserResult , options ) ) ;
parserResult . WithNotParsed < CommandLineOptions > ( errs = > DisplayHelp ( parserResult , errs ) ) ;
return 0 ;
2020-04-14 10:18:52 +00:00
}
2020-04-13 10:10:35 +00:00
2020-04-14 10:18:52 +00:00
static void DisplayHelp < T > ( ParserResult < T > result , IEnumerable < Error > errs )
{
2020-04-16 10:49:11 +00:00
var helpText = errs . ToList ( ) ;
2020-04-14 10:18:52 +00:00
Console . WriteLine ( helpText ) ;
}
2017-02-26 19:23:31 +00:00
2020-04-16 10:49:11 +00:00
static void RunOptions ( ParserResult < CommandLineOptions > parserResult , CommandLineOptions options )
2020-04-14 10:18:52 +00:00
{
2020-04-16 10:49:11 +00:00
2020-04-14 10:18:52 +00:00
// Validate combinations of Command line options
// If a profileId is supplied then we need an action other than None
if ( options . Action = = HeliosStartupAction . None & & ! String . IsNullOrEmpty ( options . Profile ) )
{
Console . WriteLine ( "Error - If you supply a Profile ID or Name then you must also provide an Action." ) ;
2020-04-16 10:49:11 +00:00
Environment . Exit ( 1 ) ;
2020-04-14 10:18:52 +00:00
}
2020-04-13 10:10:35 +00:00
2020-04-14 10:18:52 +00:00
if ( options . Action ! = HeliosStartupAction . None & & String . IsNullOrEmpty ( options . Profile ) )
{
Console . WriteLine ( "Error - If you want to perform an Action then you must also provide a Profile ID or Name." ) ;
2020-04-16 10:49:11 +00:00
Environment . Exit ( 2 ) ;
2020-04-14 10:18:52 +00:00
}
2018-10-20 00:27:25 +00:00
2020-04-14 10:18:52 +00:00
Application . EnableVisualStyles ( ) ;
Application . SetCompatibleTextRenderingDefault ( false ) ;
ServicePointManager . SecurityProtocol = SecurityProtocolType . Tls12 ;
2018-10-20 00:27:25 +00:00
2020-04-14 10:18:52 +00:00
try
{
if ( ! IPCService . StartService ( ) )
{
throw new Exception ( Language . Can_not_open_a_named_pipe_for_Inter_process_communication ) ;
}
2018-10-20 00:27:25 +00:00
2020-04-14 10:18:52 +00:00
// Create an array of profiles
var profiles = Profile . GetAllProfiles ( ) . ToArray ( ) ;
// Show the user the profiles if they want to look
foreach ( Profile aprofile in profiles )
{
2020-04-16 10:49:11 +00:00
Console . WriteLine ( $"Found Profile: '{aprofile.Name}' (ID:{aprofile.Id})" ) ;
2020-04-14 10:18:52 +00:00
}
// Try and lookup the profile in the profiles' ID fields
var profileIndex = profiles . Length > 0 ? Array . FindIndex ( profiles , p = > p . Id . Equals ( options . Profile , StringComparison . InvariantCultureIgnoreCase ) ) : - 1 ;
// If the profileID wasn't there, maybe they used the profile name?
if ( profileIndex = = - 1 )
{
// Try and lookup the profile in the profiles' Name fields
2020-04-16 10:49:11 +00:00
profileIndex = profiles . Length > 0 ? Array . FindIndex ( profiles , p = > p . Name . StartsWith ( options . Profile , StringComparison . InvariantCultureIgnoreCase ) ) : - 1 ;
2020-04-14 10:18:52 +00:00
}
// If the profileID still isn't there, then raise the alarm
if ( profileIndex = = - 1 )
{
2020-04-16 10:49:11 +00:00
if ( ! string . IsNullOrEmpty ( options . Profile ) )
{
Console . WriteLine ( $"ERROR - Couldn't find Profile Name or ID supplied via command line: '{options.Profile}'. Please check the Profile Name or ID you supplied on the command line is correct." ) ;
Environment . Exit ( 3 ) ;
}
}
else
{
Console . WriteLine ( $"Using Profile: '{profiles[profileIndex].Name}' (ID:{profiles[profileIndex].Id})" ) ;
2020-04-14 10:18:52 +00:00
}
2020-04-16 10:49:11 +00:00
2020-04-13 10:10:35 +00:00
2020-04-14 10:18:52 +00:00
switch ( options . Action )
{
case HeliosStartupAction . SwitchProfile :
SwitchProfile ( profiles , profileIndex , options ) ;
2020-04-13 10:10:35 +00:00
2020-04-14 10:18:52 +00:00
break ;
case HeliosStartupAction . EditProfile :
EditProfile ( profiles , profileIndex , options ) ;
2020-04-13 10:10:35 +00:00
2020-04-14 10:18:52 +00:00
break ;
case HeliosStartupAction . CreateShortcut :
CreateShortcut ( profiles , profileIndex , options ) ;
2020-04-13 10:10:35 +00:00
2020-04-14 10:18:52 +00:00
break ;
default :
IPCService . GetInstance ( ) . Status = InstanceStatus . User ;
Application . Run ( new MainForm ( ) ) ;
2018-10-20 00:27:25 +00:00
2020-04-14 10:18:52 +00:00
break ;
2017-02-26 19:23:31 +00:00
}
2020-04-13 03:47:38 +00:00
2020-04-14 10:18:52 +00:00
}
catch ( Exception e )
{
MessageBox . Show (
string . Format ( Language . Operation_Failed , e . Message ) ,
Language . Fatal_Error ,
MessageBoxButtons . OK ,
MessageBoxIcon . Error ) ;
}
2020-04-16 10:49:11 +00:00
2020-04-14 10:18:52 +00:00
}
static void HandleParseError ( IEnumerable < Error > errs )
{
//handle errors
2017-02-26 19:23:31 +00:00
}
2020-04-13 10:10:35 +00:00
2017-08-10 14:12:44 +00:00
// ReSharper disable once CyclomaticComplexity
2020-04-14 10:18:52 +00:00
private static void SwitchProfile ( IReadOnlyList < Profile > profiles , int profileIndex , CommandLineOptions options )
2017-02-26 19:23:31 +00:00
{
var rollbackProfile = Profile . GetCurrent ( string . Empty ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( profileIndex < 0 )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Selected_profile_is_invalid_or_not_found ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if ( ! profiles [ profileIndex ] . IsPossible )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Selected_profile_is_not_possible ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if (
IPCClient . QueryAll ( )
. Any (
client = >
2018-10-20 00:27:25 +00:00
client . Status = = InstanceStatus . Busy | |
client . Status = = InstanceStatus . OnHold ) )
{
2017-02-26 19:23:31 +00:00
throw new Exception (
Language
. Another_instance_of_this_program_is_in_working_state_Please_close_other_instances_before_trying_to_switch_profile ) ;
2018-10-20 00:27:25 +00:00
}
2020-04-14 10:18:52 +00:00
if ( ! string . IsNullOrWhiteSpace ( options . ExecuteFilename ) )
2017-02-26 19:23:31 +00:00
{
2020-04-14 10:18:52 +00:00
if ( ! File . Exists ( options . ExecuteFilename ) )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Executable_file_not_found ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if ( ! GoProfile ( profiles [ profileIndex ] ) )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Can_not_change_active_profile ) ;
2018-10-20 00:27:25 +00:00
}
2020-04-14 10:18:52 +00:00
var process = System . Diagnostics . Process . Start ( options . ExecuteFilename ,
options . ExecuteArguments ) ;
2020-04-13 03:47:38 +00:00
var processes = new System . Diagnostics . Process [ 0 ] ;
2018-10-20 00:27:25 +00:00
2020-04-14 10:18:52 +00:00
if ( ! string . IsNullOrWhiteSpace ( options . ExecuteProcessName ) )
2017-02-26 19:23:31 +00:00
{
var ticks = 0 ;
2018-10-20 00:27:25 +00:00
2020-04-14 10:18:52 +00:00
while ( ticks < options . ExecuteProcessTimeout * 1000 )
2017-02-26 19:23:31 +00:00
{
2020-04-14 10:18:52 +00:00
processes = System . Diagnostics . Process . GetProcessesByName ( options . ExecuteProcessName ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( processes . Length > 0 )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
break ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
Thread . Sleep ( 300 ) ;
ticks + = 300 ;
}
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( processes . Length = = 0 )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
processes = new [ ] { process } ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
IPCService . GetInstance ( ) . HoldProcessId = processes . FirstOrDefault ( ) ? . Id ? ? 0 ;
IPCService . GetInstance ( ) . Status = InstanceStatus . OnHold ;
2017-08-10 14:12:44 +00:00
NotifyIcon notify = null ;
2018-10-20 00:27:25 +00:00
2017-08-10 14:12:44 +00:00
try
{
notify = new NotifyIcon
{
Icon = Properties . Resources . Icon ,
Text = string . Format (
Language . Waiting_for_the_0_to_terminate ,
processes [ 0 ] . ProcessName ) ,
Visible = true
} ;
Application . DoEvents ( ) ;
}
catch
{
// ignored
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
foreach ( var p in processes )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
try
{
p . WaitForExit ( ) ;
}
catch
{
// ignored
}
2018-10-20 00:27:25 +00:00
}
2017-08-10 14:12:44 +00:00
if ( notify ! = null )
{
notify . Visible = false ;
notify . Dispose ( ) ;
Application . DoEvents ( ) ;
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
IPCService . GetInstance ( ) . Status = InstanceStatus . Busy ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( ! rollbackProfile . IsActive )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
if ( ! GoProfile ( rollbackProfile ) )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Can_not_change_active_profile ) ;
2018-10-20 00:27:25 +00:00
}
}
2017-02-26 19:23:31 +00:00
}
2020-04-14 10:18:52 +00:00
else if ( options . ExecuteSteamApp > 0 )
2017-02-26 19:23:31 +00:00
{
2020-04-14 10:18:52 +00:00
var steamGame = new SteamGame ( options . ExecuteSteamApp ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( ! SteamGame . SteamInstalled )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Steam_is_not_installed ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if ( ! File . Exists ( SteamGame . SteamAddress ) )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Steam_executable_file_not_found ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if ( ! steamGame . IsInstalled )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Steam_game_is_not_installed ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if ( ! steamGame . IsOwned )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Steam_game_is_not_owned ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
if ( ! GoProfile ( profiles [ profileIndex ] ) )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Can_not_change_active_profile ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
var address = $"steam://rungameid/{steamGame.AppId}" ;
2018-10-20 00:27:25 +00:00
2020-04-14 10:18:52 +00:00
if ( ! string . IsNullOrWhiteSpace ( options . ExecuteArguments ) )
2018-10-20 00:27:25 +00:00
{
2020-04-14 10:18:52 +00:00
address + = "/" + options . ExecuteArguments ;
2018-10-20 00:27:25 +00:00
}
2020-04-13 03:47:38 +00:00
var steamProcess = System . Diagnostics . Process . Start ( address ) ;
2017-02-26 19:23:31 +00:00
// Wait for steam game to update and then run
var ticks = 0 ;
2018-10-20 00:27:25 +00:00
2020-04-14 10:18:52 +00:00
while ( ticks < options . ExecuteProcessTimeout * 1000 )
2017-02-26 19:23:31 +00:00
{
if ( steamGame . IsRunning )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
break ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
Thread . Sleep ( 300 ) ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( ! steamGame . IsUpdating )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
ticks + = 300 ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
IPCService . GetInstance ( ) . HoldProcessId = steamProcess ? . Id ? ? 0 ;
IPCService . GetInstance ( ) . Status = InstanceStatus . OnHold ;
2017-08-10 14:12:44 +00:00
NotifyIcon notify = null ;
2018-10-20 00:27:25 +00:00
2017-08-10 14:12:44 +00:00
try
{
notify = new NotifyIcon
{
Icon = Properties . Resources . Icon ,
Text = string . Format (
Language . Waiting_for_the_0_to_terminate ,
steamGame . Name ) ,
Visible = true
} ;
Application . DoEvents ( ) ;
}
catch
{
// ignored
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
// Wait for the game to exit
if ( steamGame . IsRunning )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
while ( true )
{
if ( ! steamGame . IsRunning )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
break ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
Thread . Sleep ( 300 ) ;
}
2018-10-20 00:27:25 +00:00
}
2017-08-10 14:12:44 +00:00
if ( notify ! = null )
{
notify . Visible = false ;
notify . Dispose ( ) ;
Application . DoEvents ( ) ;
}
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
IPCService . GetInstance ( ) . Status = InstanceStatus . Busy ;
2018-10-20 00:27:25 +00:00
2017-02-26 19:23:31 +00:00
if ( ! rollbackProfile . IsActive )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
if ( ! GoProfile ( rollbackProfile ) )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Can_not_change_active_profile ) ;
2018-10-20 00:27:25 +00:00
}
}
2017-02-26 19:23:31 +00:00
}
else
{
if ( ! GoProfile ( profiles [ profileIndex ] ) )
2018-10-20 00:27:25 +00:00
{
2017-02-26 19:23:31 +00:00
throw new Exception ( Language . Can_not_change_active_profile ) ;
2018-10-20 00:27:25 +00:00
}
2017-02-26 19:23:31 +00:00
}
}
}
}