v2.2.4.2
- Minor refactoring - Moved from VDF dynamic parsing to the more reliable method introduced in v2.2.4.1 - Fixed a bug where selections were validating prematurely
This commit is contained in:
parent
f1b7e04842
commit
947809b4ad
15 changed files with 1363 additions and 1386 deletions
|
@ -118,8 +118,8 @@ csharp_style_prefer_index_operator = true
|
||||||
csharp_style_prefer_null_check_over_type_check = true
|
csharp_style_prefer_null_check_over_type_check = true
|
||||||
csharp_style_prefer_range_operator = true
|
csharp_style_prefer_range_operator = true
|
||||||
csharp_style_throw_expression = true
|
csharp_style_throw_expression = true
|
||||||
csharp_style_unused_value_assignment_preference = discard_variable
|
csharp_style_unused_value_assignment_preference = discard_variable:silent
|
||||||
csharp_style_unused_value_expression_statement_preference = unused_local_variable:suggestion
|
csharp_style_unused_value_expression_statement_preference = discard_variable
|
||||||
|
|
||||||
# 'using' directive preferences
|
# 'using' directive preferences
|
||||||
csharp_using_directive_placement = outside_namespace:suggestion
|
csharp_using_directive_placement = outside_namespace:suggestion
|
||||||
|
|
|
@ -1,60 +1,59 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace CreamInstaller
|
namespace CreamInstaller;
|
||||||
|
|
||||||
|
internal static class ExceptionHandler
|
||||||
{
|
{
|
||||||
internal static class ExceptionHandler
|
internal static bool OutputException(Exception e)
|
||||||
{
|
{
|
||||||
internal static bool OutputException(Exception e)
|
while (e.InnerException is not null)
|
||||||
{
|
{
|
||||||
while (e.InnerException is not null)
|
e = e.InnerException;
|
||||||
{
|
|
||||||
e = e.InnerException;
|
|
||||||
}
|
|
||||||
|
|
||||||
string output = "";
|
|
||||||
string[] stackTrace = e.StackTrace?.Split('\n');
|
|
||||||
if (stackTrace is not null && stackTrace.Length > 0)
|
|
||||||
{
|
|
||||||
output += "STACK TRACE\n";
|
|
||||||
for (int i = 0; i < Math.Min(stackTrace.Length, 3); i++)
|
|
||||||
{
|
|
||||||
string line = stackTrace[i];
|
|
||||||
if (line is not null)
|
|
||||||
{
|
|
||||||
output += "\n " + line[line.IndexOf("at")..];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
string[] messageLines = e.Message?.Split('\n');
|
|
||||||
if (messageLines is not null && messageLines.Length > 0)
|
|
||||||
{
|
|
||||||
if (output.Length > 0)
|
|
||||||
{
|
|
||||||
output += "\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
output += "MESSAGE\n";
|
|
||||||
for (int i = 0; i < messageLines.Length; i++)
|
|
||||||
{
|
|
||||||
string line = messageLines[i];
|
|
||||||
if (line is not null)
|
|
||||||
{
|
|
||||||
output += "\n " + messageLines[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return MessageBox.Show(output, caption: "CreamInstaller encountered an exception", buttons: MessageBoxButtons.RetryCancel, icon: MessageBoxIcon.Error) == DialogResult.Retry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string output = "";
|
||||||
|
string[] stackTrace = e.StackTrace?.Split('\n');
|
||||||
|
if (stackTrace is not null && stackTrace.Length > 0)
|
||||||
|
{
|
||||||
|
output += "STACK TRACE\n";
|
||||||
|
for (int i = 0; i < Math.Min(stackTrace.Length, 3); i++)
|
||||||
|
{
|
||||||
|
string line = stackTrace[i];
|
||||||
|
if (line is not null)
|
||||||
|
{
|
||||||
|
output += "\n " + line[line.IndexOf("at")..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string[] messageLines = e.Message?.Split('\n');
|
||||||
|
if (messageLines is not null && messageLines.Length > 0)
|
||||||
|
{
|
||||||
|
if (output.Length > 0)
|
||||||
|
{
|
||||||
|
output += "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
output += "MESSAGE\n";
|
||||||
|
for (int i = 0; i < messageLines.Length; i++)
|
||||||
|
{
|
||||||
|
string line = messageLines[i];
|
||||||
|
if (line is not null)
|
||||||
|
{
|
||||||
|
output += "\n " + messageLines[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MessageBox.Show(output, caption: "CreamInstaller encountered an exception", buttons: MessageBoxButtons.RetryCancel, icon: MessageBoxIcon.Error) == DialogResult.Retry;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class CustomMessageException : Exception
|
internal class CustomMessageException : Exception
|
||||||
{
|
{
|
||||||
private readonly string message;
|
private readonly string message;
|
||||||
public override string Message => message ?? "CustomMessageException";
|
public override string Message => message ?? "CustomMessageException";
|
||||||
|
|
||||||
public override string ToString() => Message;
|
public override string ToString() => Message;
|
||||||
|
|
||||||
internal CustomMessageException(string message) => this.message = message;
|
internal CustomMessageException(string message) => this.message = message;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,25 +1,24 @@
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace CreamInstaller
|
namespace CreamInstaller;
|
||||||
{
|
|
||||||
internal static class InstallationLog
|
|
||||||
{
|
|
||||||
internal static readonly Color Background = Color.DarkSlateGray;
|
|
||||||
internal static readonly Color Operation = Color.LightGray;
|
|
||||||
internal static readonly Color Resource = Color.LightBlue;
|
|
||||||
internal static readonly Color Success = Color.LightGreen;
|
|
||||||
internal static readonly Color Cleanup = Color.YellowGreen;
|
|
||||||
internal static readonly Color Warning = Color.Yellow;
|
|
||||||
internal static readonly Color Error = Color.DarkOrange;
|
|
||||||
|
|
||||||
internal static void AppendText(this RichTextBox logTextBox, string text, Color color)
|
internal static class InstallationLog
|
||||||
{
|
{
|
||||||
logTextBox.SelectionStart = logTextBox.TextLength;
|
internal static readonly Color Background = Color.DarkSlateGray;
|
||||||
logTextBox.SelectionLength = 0;
|
internal static readonly Color Operation = Color.LightGray;
|
||||||
logTextBox.SelectionColor = color;
|
internal static readonly Color Resource = Color.LightBlue;
|
||||||
logTextBox.AppendText(text);
|
internal static readonly Color Success = Color.LightGreen;
|
||||||
logTextBox.SelectionColor = logTextBox.ForeColor;
|
internal static readonly Color Cleanup = Color.YellowGreen;
|
||||||
}
|
internal static readonly Color Warning = Color.Yellow;
|
||||||
|
internal static readonly Color Error = Color.DarkOrange;
|
||||||
|
|
||||||
|
internal static void AppendText(this RichTextBox logTextBox, string text, Color color)
|
||||||
|
{
|
||||||
|
logTextBox.SelectionStart = logTextBox.TextLength;
|
||||||
|
logTextBox.SelectionLength = 0;
|
||||||
|
logTextBox.SelectionColor = color;
|
||||||
|
logTextBox.AppendText(text);
|
||||||
|
logTextBox.SelectionColor = logTextBox.ForeColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,131 +7,130 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
using Gameloop.Vdf.Linq;
|
using Gameloop.Vdf.Linq;
|
||||||
|
|
||||||
namespace CreamInstaller
|
namespace CreamInstaller;
|
||||||
|
|
||||||
|
internal class ProgramSelection
|
||||||
{
|
{
|
||||||
internal class ProgramSelection
|
internal bool Enabled = false;
|
||||||
|
internal bool Usable = true;
|
||||||
|
|
||||||
|
internal int SteamAppId = 0;
|
||||||
|
internal string Name = "Program";
|
||||||
|
|
||||||
|
internal Image Icon;
|
||||||
|
private string iconPath;
|
||||||
|
internal string IconPath
|
||||||
{
|
{
|
||||||
internal bool Enabled = false;
|
get => iconPath;
|
||||||
internal bool Usable = true;
|
set
|
||||||
|
|
||||||
internal int SteamAppId = 0;
|
|
||||||
internal string Name = "Program";
|
|
||||||
|
|
||||||
internal Image Icon;
|
|
||||||
private string iconPath;
|
|
||||||
internal string IconPath
|
|
||||||
{
|
{
|
||||||
get => iconPath;
|
iconPath = value;
|
||||||
set
|
Task.Run(async () => Icon = await Program.GetImageFromUrl(iconPath));
|
||||||
{
|
|
||||||
iconPath = value;
|
|
||||||
Task.Run(async () => Icon = await Program.GetImageFromUrl(iconPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal string IconStaticID
|
|
||||||
{
|
|
||||||
set => IconPath = $"https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/{SteamAppId}/{value}.jpg";
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Image ClientIcon;
|
|
||||||
private string clientIconPath;
|
|
||||||
internal string ClientIconPath
|
|
||||||
{
|
|
||||||
get => clientIconPath;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
clientIconPath = value;
|
|
||||||
Task.Run(async () => ClientIcon = await Program.GetImageFromUrl(clientIconPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal string ClientIconStaticID
|
|
||||||
{
|
|
||||||
set => ClientIconPath = $"https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/{SteamAppId}/{value}.ico";
|
|
||||||
}
|
|
||||||
|
|
||||||
internal string RootDirectory;
|
|
||||||
internal List<string> SteamApiDllDirectories;
|
|
||||||
|
|
||||||
internal VProperty AppInfo = null;
|
|
||||||
|
|
||||||
internal readonly SortedList<int, string> AllSteamDlc = new();
|
|
||||||
internal readonly SortedList<int, string> SelectedSteamDlc = new();
|
|
||||||
internal readonly List<Tuple<int, string, SortedList<int, string>>> ExtraSteamAppIdDlc = new();
|
|
||||||
|
|
||||||
internal bool AreSteamApiDllsLocked
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
foreach (string directory in SteamApiDllDirectories)
|
|
||||||
{
|
|
||||||
string api = directory + @"\steam_api.dll";
|
|
||||||
string api64 = directory + @"\steam_api64.dll";
|
|
||||||
if (api.IsFilePathLocked() || api64.IsFilePathLocked()) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Toggle(KeyValuePair<int, string> dlcApp, bool enabled)
|
|
||||||
{
|
|
||||||
if (enabled) SelectedSteamDlc[dlcApp.Key] = dlcApp.Value;
|
|
||||||
else SelectedSteamDlc.Remove(dlcApp.Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ToggleDlc(int dlcAppId, bool enabled)
|
|
||||||
{
|
|
||||||
foreach (KeyValuePair<int, string> dlcApp in AllSteamDlc)
|
|
||||||
if (dlcApp.Key == dlcAppId)
|
|
||||||
{
|
|
||||||
Toggle(dlcApp, enabled);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Enabled = SelectedSteamDlc.Any();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ToggleAllDlc(bool enabled)
|
|
||||||
{
|
|
||||||
if (!enabled) SelectedSteamDlc.Clear();
|
|
||||||
else foreach (KeyValuePair<int, string> dlcApp in AllSteamDlc) Toggle(dlcApp, enabled);
|
|
||||||
Enabled = SelectedSteamDlc.Any();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ProgramSelection() => All.Add(this);
|
|
||||||
|
|
||||||
internal void Validate()
|
|
||||||
{
|
|
||||||
if (Program.BlockProtectedGames && Program.IsGameBlocked(Name, RootDirectory))
|
|
||||||
{
|
|
||||||
All.Remove(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!Directory.Exists(RootDirectory))
|
|
||||||
{
|
|
||||||
All.Remove(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SteamApiDllDirectories.RemoveAll(directory => !Directory.Exists(directory));
|
|
||||||
if (!SteamApiDllDirectories.Any()) All.Remove(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void ValidateAll() => AllSafe.ForEach(selection => selection.Validate());
|
|
||||||
|
|
||||||
internal static List<ProgramSelection> All => Program.ProgramSelections;
|
|
||||||
|
|
||||||
internal static List<ProgramSelection> AllSafe => All.ToList();
|
|
||||||
|
|
||||||
internal static List<ProgramSelection> AllUsable => All.FindAll(s => s.Usable);
|
|
||||||
|
|
||||||
internal static List<ProgramSelection> AllUsableEnabled => AllUsable.FindAll(s => s.Enabled);
|
|
||||||
|
|
||||||
internal static ProgramSelection FromAppId(int appId) => AllUsable.Find(s => s.SteamAppId == appId);
|
|
||||||
|
|
||||||
internal static KeyValuePair<int, string>? GetDlcFromAppId(int appId)
|
|
||||||
{
|
|
||||||
foreach (ProgramSelection selection in AllUsable)
|
|
||||||
foreach (KeyValuePair<int, string> app in selection.AllSteamDlc)
|
|
||||||
if (app.Key == appId) return app;
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
internal string IconStaticID
|
||||||
|
{
|
||||||
|
set => IconPath = $"https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/{SteamAppId}/{value}.jpg";
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Image ClientIcon;
|
||||||
|
private string clientIconPath;
|
||||||
|
internal string ClientIconPath
|
||||||
|
{
|
||||||
|
get => clientIconPath;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
clientIconPath = value;
|
||||||
|
Task.Run(async () => ClientIcon = await Program.GetImageFromUrl(clientIconPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal string ClientIconStaticID
|
||||||
|
{
|
||||||
|
set => ClientIconPath = $"https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/{SteamAppId}/{value}.ico";
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string RootDirectory;
|
||||||
|
internal List<string> SteamApiDllDirectories;
|
||||||
|
|
||||||
|
internal VProperty AppInfo = null;
|
||||||
|
|
||||||
|
internal readonly SortedList<int, string> AllSteamDlc = new();
|
||||||
|
internal readonly SortedList<int, string> SelectedSteamDlc = new();
|
||||||
|
internal readonly List<Tuple<int, string, SortedList<int, string>>> ExtraSteamAppIdDlc = new();
|
||||||
|
|
||||||
|
internal bool AreSteamApiDllsLocked
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
foreach (string directory in SteamApiDllDirectories)
|
||||||
|
{
|
||||||
|
string api = directory + @"\steam_api.dll";
|
||||||
|
string api64 = directory + @"\steam_api64.dll";
|
||||||
|
if (api.IsFilePathLocked() || api64.IsFilePathLocked()) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Toggle(KeyValuePair<int, string> dlcApp, bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled) SelectedSteamDlc[dlcApp.Key] = dlcApp.Value;
|
||||||
|
else SelectedSteamDlc.Remove(dlcApp.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ToggleDlc(int dlcAppId, bool enabled)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<int, string> dlcApp in AllSteamDlc)
|
||||||
|
if (dlcApp.Key == dlcAppId)
|
||||||
|
{
|
||||||
|
Toggle(dlcApp, enabled);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Enabled = SelectedSteamDlc.Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ToggleAllDlc(bool enabled)
|
||||||
|
{
|
||||||
|
if (!enabled) SelectedSteamDlc.Clear();
|
||||||
|
else foreach (KeyValuePair<int, string> dlcApp in AllSteamDlc) Toggle(dlcApp, enabled);
|
||||||
|
Enabled = SelectedSteamDlc.Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ProgramSelection() => All.Add(this);
|
||||||
|
|
||||||
|
internal void Validate()
|
||||||
|
{
|
||||||
|
if (Program.BlockProtectedGames && Program.IsGameBlocked(Name, RootDirectory))
|
||||||
|
{
|
||||||
|
All.Remove(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Directory.Exists(RootDirectory))
|
||||||
|
{
|
||||||
|
All.Remove(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SteamApiDllDirectories.RemoveAll(directory => !Directory.Exists(directory));
|
||||||
|
if (!SteamApiDllDirectories.Any()) All.Remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ValidateAll() => AllSafe.ForEach(selection => selection.Validate());
|
||||||
|
|
||||||
|
internal static List<ProgramSelection> All => Program.ProgramSelections;
|
||||||
|
|
||||||
|
internal static List<ProgramSelection> AllSafe => All.ToList();
|
||||||
|
|
||||||
|
internal static List<ProgramSelection> AllUsable => All.FindAll(s => s.Usable);
|
||||||
|
|
||||||
|
internal static List<ProgramSelection> AllUsableEnabled => AllUsable.FindAll(s => s.Enabled);
|
||||||
|
|
||||||
|
internal static ProgramSelection FromAppId(int appId) => AllUsable.Find(s => s.SteamAppId == appId);
|
||||||
|
|
||||||
|
internal static KeyValuePair<int, string>? GetDlcFromAppId(int appId)
|
||||||
|
{
|
||||||
|
foreach (ProgramSelection selection in AllUsable)
|
||||||
|
foreach (KeyValuePair<int, string> app in selection.AllSteamDlc)
|
||||||
|
if (app.Key == appId) return app;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,157 +11,158 @@ using System.Windows.Forms;
|
||||||
|
|
||||||
using Gameloop.Vdf.Linq;
|
using Gameloop.Vdf.Linq;
|
||||||
|
|
||||||
namespace CreamInstaller
|
namespace CreamInstaller;
|
||||||
|
|
||||||
|
internal static class SteamCMD
|
||||||
{
|
{
|
||||||
internal static class SteamCMD
|
internal static readonly string DirectoryPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller";
|
||||||
|
internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe";
|
||||||
|
internal static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip";
|
||||||
|
internal static readonly string DllPath = DirectoryPath + @"\steamclient.dll";
|
||||||
|
internal static readonly string AppCachePath = DirectoryPath + @"\appcache";
|
||||||
|
internal static readonly string AppCacheAppInfoPath = AppCachePath + @"\appinfo.vdf";
|
||||||
|
internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo";
|
||||||
|
|
||||||
|
internal static readonly Version MinimumAppInfoVersion = Version.Parse("2.0.3.2");
|
||||||
|
internal static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt";
|
||||||
|
|
||||||
|
internal static async Task<string> Run(string command) => await Task.Run(() =>
|
||||||
{
|
{
|
||||||
internal static readonly string DirectoryPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller";
|
if (Program.Canceled) return "";
|
||||||
internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe";
|
List<string> logs = new();
|
||||||
internal static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip";
|
ProcessStartInfo processStartInfo = new()
|
||||||
internal static readonly string DllPath = DirectoryPath + @"\steamclient.dll";
|
|
||||||
internal static readonly string AppCachePath = DirectoryPath + @"\appcache";
|
|
||||||
internal static readonly string AppCacheAppInfoPath = AppCachePath + @"\appinfo.vdf";
|
|
||||||
internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo";
|
|
||||||
|
|
||||||
internal static readonly Version MinimumAppInfoVersion = Version.Parse("2.0.3.2");
|
|
||||||
internal static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt";
|
|
||||||
|
|
||||||
internal static async Task<string> Run(string command) => await Task.Run(() =>
|
|
||||||
{
|
{
|
||||||
if (Program.Canceled) return "";
|
FileName = FilePath,
|
||||||
List<string> logs = new();
|
RedirectStandardOutput = true,
|
||||||
ProcessStartInfo processStartInfo = new()
|
RedirectStandardInput = true,
|
||||||
{
|
RedirectStandardError = true,
|
||||||
FileName = FilePath,
|
UseShellExecute = false,
|
||||||
RedirectStandardOutput = true,
|
Arguments = command,
|
||||||
RedirectStandardInput = true,
|
CreateNoWindow = true,
|
||||||
RedirectStandardError = true,
|
StandardInputEncoding = Encoding.UTF8,
|
||||||
UseShellExecute = false,
|
StandardOutputEncoding = Encoding.UTF8,
|
||||||
Arguments = command,
|
StandardErrorEncoding = Encoding.UTF8
|
||||||
CreateNoWindow = true,
|
};
|
||||||
StandardInputEncoding = Encoding.UTF8,
|
using Process process = Process.Start(processStartInfo);
|
||||||
StandardOutputEncoding = Encoding.UTF8,
|
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
||||||
StandardErrorEncoding = Encoding.UTF8
|
process.BeginOutputReadLine();
|
||||||
};
|
process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
||||||
using Process process = Process.Start(processStartInfo);
|
process.BeginErrorReadLine();
|
||||||
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
process.WaitForExit();
|
||||||
process.BeginOutputReadLine();
|
return string.Join("\r\n", logs);
|
||||||
process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
});
|
||||||
process.BeginErrorReadLine();
|
|
||||||
process.WaitForExit();
|
|
||||||
return string.Join("\r\n", logs);
|
|
||||||
});
|
|
||||||
|
|
||||||
internal static async Task Setup()
|
internal static async Task Setup()
|
||||||
|
{
|
||||||
|
await Kill();
|
||||||
|
if (!File.Exists(FilePath))
|
||||||
{
|
{
|
||||||
await Kill();
|
using (HttpClient httpClient = new())
|
||||||
if (!File.Exists(FilePath))
|
|
||||||
{
|
{
|
||||||
using (HttpClient httpClient = new())
|
byte[] file = await httpClient.GetByteArrayAsync("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip");
|
||||||
{
|
file.Write(ArchivePath);
|
||||||
byte[] file = await httpClient.GetByteArrayAsync("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip");
|
|
||||||
file.Write(ArchivePath);
|
|
||||||
}
|
|
||||||
ZipFile.ExtractToDirectory(ArchivePath, DirectoryPath);
|
|
||||||
File.Delete(ArchivePath);
|
|
||||||
}
|
}
|
||||||
if (File.Exists(AppCacheAppInfoPath)) File.Delete(AppCacheAppInfoPath);
|
ZipFile.ExtractToDirectory(ArchivePath, DirectoryPath);
|
||||||
if (!File.Exists(AppInfoVersionPath) || !Version.TryParse(File.ReadAllText(AppInfoVersionPath, Encoding.UTF8), out Version version) || version < MinimumAppInfoVersion)
|
File.Delete(ArchivePath);
|
||||||
{
|
|
||||||
if (Directory.Exists(AppInfoPath)) Directory.Delete(AppInfoPath, true);
|
|
||||||
Directory.CreateDirectory(AppInfoPath);
|
|
||||||
File.WriteAllText(AppInfoVersionPath, Application.ProductVersion, Encoding.UTF8);
|
|
||||||
}
|
|
||||||
if (!File.Exists(DllPath)) await Run($@"+quit");
|
|
||||||
}
|
}
|
||||||
|
if (File.Exists(AppCacheAppInfoPath)) File.Delete(AppCacheAppInfoPath);
|
||||||
internal static async Task<VProperty> GetAppInfo(int appId, string branch = "public", int buildId = 0)
|
if (!File.Exists(AppInfoVersionPath) || !Version.TryParse(File.ReadAllText(AppInfoVersionPath, Encoding.UTF8), out Version version) || version < MinimumAppInfoVersion)
|
||||||
{
|
{
|
||||||
if (Program.Canceled) return null;
|
if (Directory.Exists(AppInfoPath)) Directory.Delete(AppInfoPath, true);
|
||||||
string output;
|
Directory.CreateDirectory(AppInfoPath);
|
||||||
string appUpdatePath = $@"{AppInfoPath}\{appId}";
|
File.WriteAllText(AppInfoVersionPath, Application.ProductVersion, Encoding.UTF8);
|
||||||
string appUpdateFile = $@"{appUpdatePath}\appinfo.txt";
|
|
||||||
restart:
|
|
||||||
if (Program.Canceled) return null;
|
|
||||||
if (Directory.Exists(appUpdatePath) && File.Exists(appUpdateFile))
|
|
||||||
output = File.ReadAllText(appUpdateFile, Encoding.UTF8);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
output = await Run($@"+@ShutdownOnFailedCommand 0 +login anonymous +app_info_print {appId} +force_install_dir {appUpdatePath} +app_update 4 +quit");
|
|
||||||
int openBracket = output.IndexOf("{");
|
|
||||||
int closeBracket = output.LastIndexOf("}");
|
|
||||||
if (openBracket != -1 && closeBracket != -1)
|
|
||||||
{
|
|
||||||
output = $"\"{appId}\"\n" + output[openBracket..(1 + closeBracket)];
|
|
||||||
File.WriteAllText(appUpdateFile, output, Encoding.UTF8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Program.Canceled || output is null) return null;
|
|
||||||
if (!ValveDataFile.TryDeserialize(output, out VProperty appInfo))
|
|
||||||
{
|
|
||||||
if (Directory.Exists(appUpdatePath))
|
|
||||||
{
|
|
||||||
Directory.Delete(appUpdatePath, true);
|
|
||||||
goto restart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (appInfo.Value is VValue) goto restart;
|
|
||||||
if (appInfo is null || appInfo.Value?.Children()?.ToList()?.Count == 0) return appInfo;
|
|
||||||
VToken type = appInfo.Value?.TryGet("common")?.TryGet("type");
|
|
||||||
if (type is null || type.ToString() == "Game")
|
|
||||||
{
|
|
||||||
string buildid = appInfo.Value?.TryGet("depots")?.TryGet("branches")?.TryGet(branch)?.TryGet("buildid")?.ToString();
|
|
||||||
if (buildid is null && type is not null) return appInfo;
|
|
||||||
if (type is null || int.Parse(buildid) < buildId)
|
|
||||||
{
|
|
||||||
List<int> dlcAppIds = await ParseDlcAppIds(appInfo);
|
|
||||||
foreach (int id in dlcAppIds)
|
|
||||||
{
|
|
||||||
string dlcAppUpdatePath = $@"{AppInfoPath}\{id}";
|
|
||||||
if (Directory.Exists(dlcAppUpdatePath)) Directory.Delete(dlcAppUpdatePath, true);
|
|
||||||
}
|
|
||||||
if (Directory.Exists(appUpdatePath)) Directory.Delete(appUpdatePath, true);
|
|
||||||
goto restart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return appInfo;
|
|
||||||
}
|
}
|
||||||
|
if (!File.Exists(DllPath)) await Run($@"+quit");
|
||||||
|
}
|
||||||
|
|
||||||
internal static async Task<List<int>> ParseDlcAppIds(VProperty appInfo) => await Task.Run(() =>
|
internal static async Task<VProperty> GetAppInfo(int appId, string branch = "public", int buildId = 0)
|
||||||
|
{
|
||||||
|
if (Program.Canceled) return null;
|
||||||
|
string output;
|
||||||
|
string appUpdatePath = $@"{AppInfoPath}\{appId}";
|
||||||
|
string appUpdateFile = $@"{appUpdatePath}\appinfo.txt";
|
||||||
|
restart:
|
||||||
|
if (Program.Canceled) return null;
|
||||||
|
if (Directory.Exists(appUpdatePath) && File.Exists(appUpdateFile))
|
||||||
|
output = File.ReadAllText(appUpdateFile, Encoding.UTF8);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
List<int> dlcIds = new();
|
output = await Run($@"+@ShutdownOnFailedCommand 0 +login anonymous +app_info_print {appId} +force_install_dir {appUpdatePath} +app_update 4 +quit");
|
||||||
if (Program.Canceled || appInfo is not VProperty) return dlcIds;
|
int openBracket = output.IndexOf("{");
|
||||||
VToken extended = appInfo.Value.TryGet("extended");
|
int closeBracket = output.LastIndexOf("}");
|
||||||
if (extended is not null) foreach (VProperty property in extended)
|
if (openBracket != -1 && closeBracket != -1)
|
||||||
if (property.Key.ToString() == "listofdlc") foreach (string id in property.Value.ToString().Split(","))
|
|
||||||
if (!dlcIds.Contains(int.Parse(id))) dlcIds.Add(int.Parse(id));
|
|
||||||
VToken depots = appInfo.Value.TryGet("depots");
|
|
||||||
if (depots is not null) foreach (VProperty property in depots)
|
|
||||||
if (int.TryParse(property.Key.ToString(), out int _)
|
|
||||||
&& int.TryParse(property.Value.TryGet("dlcappid")?.ToString(), out int appid)
|
|
||||||
&& !dlcIds.Contains(appid))
|
|
||||||
dlcIds.Add(appid);
|
|
||||||
return dlcIds;
|
|
||||||
});
|
|
||||||
|
|
||||||
internal static async Task Kill()
|
|
||||||
{
|
|
||||||
List<Task> tasks = new();
|
|
||||||
foreach (Process process in Process.GetProcessesByName("steamcmd"))
|
|
||||||
{
|
{
|
||||||
process.Kill();
|
output = $"\"{appId}\"\n" + output[openBracket..(1 + closeBracket)];
|
||||||
tasks.Add(Task.Run(() => process.WaitForExit()));
|
File.WriteAllText(appUpdateFile, output, Encoding.UTF8);
|
||||||
}
|
}
|
||||||
foreach (Task task in tasks) await task;
|
|
||||||
}
|
}
|
||||||
|
if (Program.Canceled || output is null) return null;
|
||||||
internal static void Dispose()
|
if (!ValveDataFile.TryDeserialize(output, out VProperty appInfo))
|
||||||
{
|
{
|
||||||
Kill().Wait();
|
if (Directory.Exists(appUpdatePath))
|
||||||
if (Directory.Exists(DirectoryPath))
|
|
||||||
{
|
{
|
||||||
Directory.Delete(DirectoryPath, true);
|
Directory.Delete(appUpdatePath, true);
|
||||||
|
goto restart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (appInfo.Value is VValue) goto restart;
|
||||||
|
if (appInfo is null || appInfo.Value?.Children()?.ToList()?.Count == 0) return appInfo;
|
||||||
|
VToken type = appInfo.Value?.GetChild("common")?.GetChild("type");
|
||||||
|
if (type is null || type.ToString() == "Game")
|
||||||
|
{
|
||||||
|
string buildid = appInfo.Value?.GetChild("depots")?.GetChild("branches")?.GetChild(branch)?.GetChild("buildid")?.ToString();
|
||||||
|
if (buildid is null && type is not null) return appInfo;
|
||||||
|
if (type is null || int.Parse(buildid) < buildId)
|
||||||
|
{
|
||||||
|
List<int> dlcAppIds = await ParseDlcAppIds(appInfo);
|
||||||
|
foreach (int id in dlcAppIds)
|
||||||
|
{
|
||||||
|
string dlcAppUpdatePath = $@"{AppInfoPath}\{id}";
|
||||||
|
if (Directory.Exists(dlcAppUpdatePath)) Directory.Delete(dlcAppUpdatePath, true);
|
||||||
|
}
|
||||||
|
if (Directory.Exists(appUpdatePath)) Directory.Delete(appUpdatePath, true);
|
||||||
|
goto restart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return appInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task<List<int>> ParseDlcAppIds(VProperty appInfo) => await Task.Run(() =>
|
||||||
|
{
|
||||||
|
List<int> dlcIds = new();
|
||||||
|
if (Program.Canceled || appInfo is not VProperty) return dlcIds;
|
||||||
|
VToken extended = appInfo.Value.GetChild("extended");
|
||||||
|
if (extended is not null)
|
||||||
|
foreach (VProperty property in extended)
|
||||||
|
if (property.Key.ToString() == "listofdlc")
|
||||||
|
foreach (string id in property.Value.ToString().Split(","))
|
||||||
|
if (!dlcIds.Contains(int.Parse(id))) dlcIds.Add(int.Parse(id));
|
||||||
|
VToken depots = appInfo.Value.GetChild("depots");
|
||||||
|
if (depots is not null) foreach (VProperty property in depots)
|
||||||
|
if (int.TryParse(property.Key.ToString(), out int _)
|
||||||
|
&& int.TryParse(property.Value.GetChild("dlcappid")?.ToString(), out int appid)
|
||||||
|
&& !dlcIds.Contains(appid))
|
||||||
|
dlcIds.Add(appid);
|
||||||
|
return dlcIds;
|
||||||
|
});
|
||||||
|
|
||||||
|
internal static async Task Kill()
|
||||||
|
{
|
||||||
|
List<Task> tasks = new();
|
||||||
|
foreach (Process process in Process.GetProcessesByName("steamcmd"))
|
||||||
|
{
|
||||||
|
process.Kill();
|
||||||
|
tasks.Add(Task.Run(() => process.WaitForExit()));
|
||||||
|
}
|
||||||
|
foreach (Task task in tasks) await task;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Dispose()
|
||||||
|
{
|
||||||
|
Kill().Wait();
|
||||||
|
if (Directory.Exists(DirectoryPath))
|
||||||
|
{
|
||||||
|
Directory.Delete(DirectoryPath, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ internal static class ValveDataFile
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static VToken TryGet(this VToken token, string index)
|
internal static VToken GetChild(this VToken token, string index)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
|
||||||
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
|
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
|
||||||
<Version>2.2.4.1</Version>
|
<Version>2.2.4.2</Version>
|
||||||
<PackageIcon>Resources\ini.ico</PackageIcon>
|
<PackageIcon>Resources\ini.ico</PackageIcon>
|
||||||
<PackageIconUrl />
|
<PackageIconUrl />
|
||||||
<Description>Automatically generates and installs CreamAPI files for Steam games on the user's computer. It can also generate and install CreamAPI for the Paradox Launcher should the user select a Paradox Interactive game.</Description>
|
<Description>Automatically generates and installs CreamAPI files for Steam games on the user's computer. It can also generate and install CreamAPI for the Paradox Launcher should the user select a Paradox Interactive game.</Description>
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace CreamInstaller
|
namespace CreamInstaller;
|
||||||
|
|
||||||
|
internal class CustomForm : Form
|
||||||
{
|
{
|
||||||
internal class CustomForm : Form
|
internal CustomForm() : base() => Icon = Properties.Resources.Icon;
|
||||||
|
|
||||||
|
internal CustomForm(IWin32Window owner) : this() => Owner = owner as Form;
|
||||||
|
|
||||||
|
protected override CreateParams CreateParams // Double buffering for all controls
|
||||||
{
|
{
|
||||||
internal CustomForm() : base() => Icon = Properties.Resources.Icon;
|
get
|
||||||
|
|
||||||
internal CustomForm(IWin32Window owner) : this() => Owner = owner as Form;
|
|
||||||
|
|
||||||
protected override CreateParams CreateParams // Double buffering for all controls
|
|
||||||
{
|
{
|
||||||
get
|
CreateParams handleParam = base.CreateParams;
|
||||||
{
|
handleParam.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
|
||||||
CreateParams handleParam = base.CreateParams;
|
return handleParam;
|
||||||
handleParam.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
|
|
||||||
return handleParam;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,56 +2,55 @@
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace CreamInstaller
|
namespace CreamInstaller;
|
||||||
|
|
||||||
|
internal class CustomTreeView : TreeView
|
||||||
{
|
{
|
||||||
internal class CustomTreeView : TreeView
|
protected override void WndProc(ref Message m)
|
||||||
{
|
{
|
||||||
protected override void WndProc(ref Message m)
|
if (m.Msg == 0x203)
|
||||||
{
|
{
|
||||||
if (m.Msg == 0x203)
|
m.Result = IntPtr.Zero;
|
||||||
{
|
|
||||||
m.Result = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
base.WndProc(ref m);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
internal CustomTreeView() : base()
|
|
||||||
{
|
{
|
||||||
DrawMode = TreeViewDrawMode.OwnerDrawAll;
|
base.WndProc(ref m);
|
||||||
DrawNode += new DrawTreeNodeEventHandler(DrawTreeNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawTreeNode(object sender, DrawTreeNodeEventArgs e)
|
|
||||||
{
|
|
||||||
e.DrawDefault = true;
|
|
||||||
TreeNode node = e.Node;
|
|
||||||
if (!node.IsVisible)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Graphics graphics = e.Graphics;
|
|
||||||
Color backColor = BackColor;
|
|
||||||
SolidBrush brush = new(backColor);
|
|
||||||
Font font = Font;
|
|
||||||
Font subFont = new(font.FontFamily, font.SizeInPoints, FontStyle.Regular, font.Unit, font.GdiCharSet, font.GdiVerticalFont);
|
|
||||||
|
|
||||||
string subText = node.Name;
|
|
||||||
if (subText is null || !int.TryParse(subText, out int subInt) || subInt <= 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Size subSize = TextRenderer.MeasureText(graphics, subText, subFont);
|
|
||||||
Rectangle bounds = node.Bounds;
|
|
||||||
Rectangle subBounds = new(bounds.X + bounds.Width, bounds.Y, subSize.Width, bounds.Height);
|
|
||||||
graphics.FillRectangle(brush, subBounds);
|
|
||||||
Point location = subBounds.Location;
|
|
||||||
Point subLocation = new(location.X - 1, location.Y + 1);
|
|
||||||
TextRenderer.DrawText(graphics, subText, subFont, subLocation, Color.Gray);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
internal CustomTreeView() : base()
|
||||||
|
{
|
||||||
|
DrawMode = TreeViewDrawMode.OwnerDrawAll;
|
||||||
|
DrawNode += new DrawTreeNodeEventHandler(DrawTreeNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawTreeNode(object sender, DrawTreeNodeEventArgs e)
|
||||||
|
{
|
||||||
|
e.DrawDefault = true;
|
||||||
|
TreeNode node = e.Node;
|
||||||
|
if (!node.IsVisible)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Graphics graphics = e.Graphics;
|
||||||
|
Color backColor = BackColor;
|
||||||
|
SolidBrush brush = new(backColor);
|
||||||
|
Font font = Font;
|
||||||
|
Font subFont = new(font.FontFamily, font.SizeInPoints, FontStyle.Regular, font.Unit, font.GdiCharSet, font.GdiVerticalFont);
|
||||||
|
|
||||||
|
string subText = node.Name;
|
||||||
|
if (subText is null || !int.TryParse(subText, out int subInt) || subInt <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Size subSize = TextRenderer.MeasureText(graphics, subText, subFont);
|
||||||
|
Rectangle bounds = node.Bounds;
|
||||||
|
Rectangle subBounds = new(bounds.X + bounds.Width, bounds.Y, subSize.Width, bounds.Height);
|
||||||
|
graphics.FillRectangle(brush, subBounds);
|
||||||
|
Point location = subBounds.Location;
|
||||||
|
Point subLocation = new(location.X - 1, location.Y + 1);
|
||||||
|
TextRenderer.DrawText(graphics, subText, subFont, subLocation, Color.Gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,25 +1,24 @@
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace CreamInstaller
|
namespace CreamInstaller;
|
||||||
{
|
|
||||||
internal partial class DialogForm : CustomForm
|
|
||||||
{
|
|
||||||
internal DialogForm(IWin32Window owner) : base(owner) => InitializeComponent();
|
|
||||||
|
|
||||||
internal DialogResult Show(string formName, Icon descriptionIcon, string descriptionText, string acceptButtonText, string cancelButtonText = null)
|
internal partial class DialogForm : CustomForm
|
||||||
|
{
|
||||||
|
internal DialogForm(IWin32Window owner) : base(owner) => InitializeComponent();
|
||||||
|
|
||||||
|
internal DialogResult Show(string formName, Icon descriptionIcon, string descriptionText, string acceptButtonText, string cancelButtonText = null)
|
||||||
|
{
|
||||||
|
icon.Image = descriptionIcon.ToBitmap();
|
||||||
|
Text = formName;
|
||||||
|
descriptionLabel.Text = descriptionText;
|
||||||
|
acceptButton.Text = acceptButtonText;
|
||||||
|
if (cancelButtonText is null)
|
||||||
{
|
{
|
||||||
icon.Image = descriptionIcon.ToBitmap();
|
cancelButton.Enabled = false;
|
||||||
Text = formName;
|
cancelButton.Visible = false;
|
||||||
descriptionLabel.Text = descriptionText;
|
|
||||||
acceptButton.Text = acceptButtonText;
|
|
||||||
if (cancelButtonText is null)
|
|
||||||
{
|
|
||||||
cancelButton.Enabled = false;
|
|
||||||
cancelButton.Visible = false;
|
|
||||||
}
|
|
||||||
else cancelButton.Text = cancelButtonText;
|
|
||||||
return ShowDialog();
|
|
||||||
}
|
}
|
||||||
|
else cancelButton.Text = cancelButtonText;
|
||||||
|
return ShowDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,236 +8,227 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace CreamInstaller
|
namespace CreamInstaller;
|
||||||
|
|
||||||
|
internal partial class InstallForm : CustomForm
|
||||||
{
|
{
|
||||||
internal partial class InstallForm : CustomForm
|
internal bool Reselecting = false;
|
||||||
|
internal bool Uninstalling = false;
|
||||||
|
|
||||||
|
internal InstallForm(IWin32Window owner, bool uninstall = false) : base(owner)
|
||||||
{
|
{
|
||||||
internal bool Reselecting = false;
|
InitializeComponent();
|
||||||
internal bool Uninstalling = false;
|
Text = Program.ApplicationName;
|
||||||
|
Program.InstallForm = this;
|
||||||
|
logTextBox.BackColor = InstallationLog.Background;
|
||||||
|
Uninstalling = uninstall;
|
||||||
|
}
|
||||||
|
|
||||||
internal InstallForm(IWin32Window owner, bool uninstall = false) : base(owner)
|
private int OperationsCount;
|
||||||
|
private int CompleteOperationsCount;
|
||||||
|
|
||||||
|
internal void UpdateProgress(int progress)
|
||||||
|
{
|
||||||
|
int value = (int)((float)(CompleteOperationsCount / (float)OperationsCount) * 100) + (progress / OperationsCount);
|
||||||
|
if (value < userProgressBar.Value) return;
|
||||||
|
userProgressBar.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task UpdateUser(string text, Color color, bool info = true, bool log = true)
|
||||||
|
{
|
||||||
|
if (info) userInfoLabel.Text = text;
|
||||||
|
if (log && !logTextBox.IsDisposed)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
if (logTextBox.Text.Length > 0) logTextBox.AppendText(Environment.NewLine, color);
|
||||||
Text = Program.ApplicationName;
|
logTextBox.AppendText(text, color);
|
||||||
Program.InstallForm = this;
|
|
||||||
logTextBox.BackColor = InstallationLog.Background;
|
|
||||||
Uninstalling = uninstall;
|
|
||||||
}
|
}
|
||||||
|
await Task.Run(() => Thread.Sleep(0)); // to keep the text box control from glitching
|
||||||
|
}
|
||||||
|
|
||||||
private int OperationsCount;
|
internal async Task WriteConfiguration(StreamWriter writer, int steamAppId, string name, SortedList<int, string> steamDlcApps)
|
||||||
private int CompleteOperationsCount;
|
{
|
||||||
|
writer.WriteLine();
|
||||||
internal void UpdateProgress(int progress)
|
writer.WriteLine($"; {name}");
|
||||||
|
writer.WriteLine("[steam]");
|
||||||
|
writer.WriteLine($"appid = {steamAppId}");
|
||||||
|
writer.WriteLine();
|
||||||
|
writer.WriteLine("[dlc]");
|
||||||
|
await UpdateUser($"Added game to cream_api.ini with appid {steamAppId} ({name})", InstallationLog.Resource, info: false);
|
||||||
|
foreach (KeyValuePair<int, string> dlcApp in steamDlcApps)
|
||||||
{
|
{
|
||||||
int value = (int)((float)(CompleteOperationsCount / (float)OperationsCount) * 100) + (progress / OperationsCount);
|
writer.WriteLine($"{dlcApp.Key} = {dlcApp.Value}");
|
||||||
if (value < userProgressBar.Value) return;
|
await UpdateUser($"Added DLC to cream_api.ini with appid {dlcApp.Key} ({dlcApp.Value})", InstallationLog.Resource, info: false);
|
||||||
userProgressBar.Value = value;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal async Task UpdateUser(string text, Color color, bool info = true, bool log = true)
|
private async Task OperateFor(ProgramSelection selection)
|
||||||
|
{
|
||||||
|
UpdateProgress(0);
|
||||||
|
int count = selection.SteamApiDllDirectories.Count;
|
||||||
|
int cur = 0;
|
||||||
|
foreach (string directory in selection.SteamApiDllDirectories)
|
||||||
{
|
{
|
||||||
if (info) userInfoLabel.Text = text;
|
await UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation);
|
||||||
if (log && !logTextBox.IsDisposed)
|
if (!Program.IsProgramRunningDialog(this, selection)) throw new OperationCanceledException();
|
||||||
|
string api = directory + @"\steam_api.dll";
|
||||||
|
string api_o = directory + @"\steam_api_o.dll";
|
||||||
|
string api64 = directory + @"\steam_api64.dll";
|
||||||
|
string api64_o = directory + @"\steam_api64_o.dll";
|
||||||
|
string cApi = directory + @"\cream_api.ini";
|
||||||
|
if (Uninstalling)
|
||||||
{
|
{
|
||||||
if (logTextBox.Text.Length > 0) logTextBox.AppendText(Environment.NewLine, color);
|
if (File.Exists(api_o))
|
||||||
logTextBox.AppendText(text, color);
|
{
|
||||||
|
if (File.Exists(api))
|
||||||
|
{
|
||||||
|
File.Delete(api);
|
||||||
|
await UpdateUser($"Deleted file: {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
|
||||||
|
}
|
||||||
|
File.Move(api_o, api);
|
||||||
|
await UpdateUser($"Renamed file: {Path.GetFileName(api_o)} -> {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
|
||||||
|
}
|
||||||
|
if (File.Exists(api64_o))
|
||||||
|
{
|
||||||
|
if (File.Exists(api64))
|
||||||
|
{
|
||||||
|
File.Delete(api64);
|
||||||
|
await UpdateUser($"Deleted file: {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
|
||||||
|
}
|
||||||
|
File.Move(api64_o, api64);
|
||||||
|
await UpdateUser($"Renamed file: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
|
||||||
|
}
|
||||||
|
if (File.Exists(cApi))
|
||||||
|
{
|
||||||
|
File.Delete(cApi);
|
||||||
|
await UpdateUser($"Deleted file: {Path.GetFileName(cApi)}", InstallationLog.Resource, info: false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await Task.Run(() => Thread.Sleep(0)); // to keep the text box control from glitching
|
else
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task WriteConfiguration(StreamWriter writer, int steamAppId, string name, SortedList<int, string> steamDlcApps)
|
|
||||||
{
|
|
||||||
writer.WriteLine();
|
|
||||||
writer.WriteLine($"; {name}");
|
|
||||||
writer.WriteLine("[steam]");
|
|
||||||
writer.WriteLine($"appid = {steamAppId}");
|
|
||||||
writer.WriteLine();
|
|
||||||
writer.WriteLine("[dlc]");
|
|
||||||
await UpdateUser($"Added game to cream_api.ini with appid {steamAppId} ({name})", InstallationLog.Resource, info: false);
|
|
||||||
foreach (KeyValuePair<int, string> dlcApp in steamDlcApps)
|
|
||||||
{
|
{
|
||||||
writer.WriteLine($"{dlcApp.Key} = {dlcApp.Value}");
|
if (File.Exists(api) && !File.Exists(api_o))
|
||||||
await UpdateUser($"Added DLC to cream_api.ini with appid {dlcApp.Key} ({dlcApp.Value})", InstallationLog.Resource, info: false);
|
{
|
||||||
|
File.Move(api, api_o);
|
||||||
|
await UpdateUser($"Renamed file: {Path.GetFileName(api)} -> {Path.GetFileName(api_o)}", InstallationLog.Resource, info: false);
|
||||||
|
}
|
||||||
|
if (File.Exists(api_o))
|
||||||
|
{
|
||||||
|
Properties.Resources.API.Write(api);
|
||||||
|
await UpdateUser($"Wrote resource to file: {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
|
||||||
|
}
|
||||||
|
if (File.Exists(api64) && !File.Exists(api64_o))
|
||||||
|
{
|
||||||
|
File.Move(api64, api64_o);
|
||||||
|
await UpdateUser($"Renamed file: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", InstallationLog.Resource, info: false);
|
||||||
|
}
|
||||||
|
if (File.Exists(api64_o))
|
||||||
|
{
|
||||||
|
Properties.Resources.API64.Write(api64);
|
||||||
|
await UpdateUser($"Wrote resource to file: {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
|
||||||
|
}
|
||||||
|
await UpdateUser("Generating CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation);
|
||||||
|
File.Create(cApi).Close();
|
||||||
|
StreamWriter writer = new(cApi, true, Encoding.UTF8);
|
||||||
|
writer.WriteLine("; " + Application.CompanyName + " v" + Application.ProductVersion);
|
||||||
|
if (selection.SteamAppId > 0) await WriteConfiguration(writer, selection.SteamAppId, selection.Name, selection.SelectedSteamDlc);
|
||||||
|
foreach (Tuple<int, string, SortedList<int, string>> extraAppDlc in selection.ExtraSteamAppIdDlc)
|
||||||
|
await WriteConfiguration(writer, extraAppDlc.Item1, extraAppDlc.Item2, extraAppDlc.Item3);
|
||||||
|
writer.Flush();
|
||||||
|
writer.Close();
|
||||||
}
|
}
|
||||||
|
UpdateProgress(++cur / count * 100);
|
||||||
}
|
}
|
||||||
|
UpdateProgress(100);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task OperateFor(ProgramSelection selection)
|
private async Task Operate()
|
||||||
|
{
|
||||||
|
List<ProgramSelection> programSelections = ProgramSelection.AllUsableEnabled;
|
||||||
|
OperationsCount = programSelections.Count;
|
||||||
|
CompleteOperationsCount = 0;
|
||||||
|
List<ProgramSelection> disabledSelections = new();
|
||||||
|
foreach (ProgramSelection selection in programSelections)
|
||||||
{
|
{
|
||||||
UpdateProgress(0);
|
if (!Program.IsProgramRunningDialog(this, selection)) throw new OperationCanceledException();
|
||||||
int count = selection.SteamApiDllDirectories.Count;
|
|
||||||
int cur = 0;
|
|
||||||
foreach (string directory in selection.SteamApiDllDirectories)
|
|
||||||
{
|
|
||||||
await UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation);
|
|
||||||
if (!Program.IsProgramRunningDialog(this, selection))
|
|
||||||
{
|
|
||||||
throw new OperationCanceledException();
|
|
||||||
}
|
|
||||||
string api = directory + @"\steam_api.dll";
|
|
||||||
string api_o = directory + @"\steam_api_o.dll";
|
|
||||||
string api64 = directory + @"\steam_api64.dll";
|
|
||||||
string api64_o = directory + @"\steam_api64_o.dll";
|
|
||||||
string cApi = directory + @"\cream_api.ini";
|
|
||||||
if (Uninstalling)
|
|
||||||
{
|
|
||||||
if (File.Exists(api_o))
|
|
||||||
{
|
|
||||||
if (File.Exists(api))
|
|
||||||
{
|
|
||||||
File.Delete(api);
|
|
||||||
await UpdateUser($"Deleted file: {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
|
|
||||||
}
|
|
||||||
File.Move(api_o, api);
|
|
||||||
await UpdateUser($"Renamed file: {Path.GetFileName(api_o)} -> {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
|
|
||||||
}
|
|
||||||
if (File.Exists(api64_o))
|
|
||||||
{
|
|
||||||
if (File.Exists(api64))
|
|
||||||
{
|
|
||||||
File.Delete(api64);
|
|
||||||
await UpdateUser($"Deleted file: {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
|
|
||||||
}
|
|
||||||
File.Move(api64_o, api64);
|
|
||||||
await UpdateUser($"Renamed file: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
|
|
||||||
}
|
|
||||||
if (File.Exists(cApi))
|
|
||||||
{
|
|
||||||
File.Delete(cApi);
|
|
||||||
await UpdateUser($"Deleted file: {Path.GetFileName(cApi)}", InstallationLog.Resource, info: false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (File.Exists(api) && !File.Exists(api_o))
|
|
||||||
{
|
|
||||||
File.Move(api, api_o);
|
|
||||||
await UpdateUser($"Renamed file: {Path.GetFileName(api)} -> {Path.GetFileName(api_o)}", InstallationLog.Resource, info: false);
|
|
||||||
}
|
|
||||||
if (File.Exists(api_o))
|
|
||||||
{
|
|
||||||
Properties.Resources.API.Write(api);
|
|
||||||
await UpdateUser($"Wrote resource to file: {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
|
|
||||||
}
|
|
||||||
if (File.Exists(api64) && !File.Exists(api64_o))
|
|
||||||
{
|
|
||||||
File.Move(api64, api64_o);
|
|
||||||
await UpdateUser($"Renamed file: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", InstallationLog.Resource, info: false);
|
|
||||||
}
|
|
||||||
if (File.Exists(api64_o))
|
|
||||||
{
|
|
||||||
Properties.Resources.API64.Write(api64);
|
|
||||||
await UpdateUser($"Wrote resource to file: {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
|
|
||||||
}
|
|
||||||
await UpdateUser("Generating CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation);
|
|
||||||
File.Create(cApi).Close();
|
|
||||||
StreamWriter writer = new(cApi, true, Encoding.UTF8);
|
|
||||||
writer.WriteLine("; " + Application.CompanyName + " v" + Application.ProductVersion);
|
|
||||||
if (selection.SteamAppId > 0)
|
|
||||||
{
|
|
||||||
await WriteConfiguration(writer, selection.SteamAppId, selection.Name, selection.SelectedSteamDlc);
|
|
||||||
}
|
|
||||||
foreach (Tuple<int, string, SortedList<int, string>> extraAppDlc in selection.ExtraSteamAppIdDlc)
|
|
||||||
{
|
|
||||||
await WriteConfiguration(writer, extraAppDlc.Item1, extraAppDlc.Item2, extraAppDlc.Item3);
|
|
||||||
}
|
|
||||||
writer.Flush();
|
|
||||||
writer.Close();
|
|
||||||
}
|
|
||||||
UpdateProgress(++cur / count * 100);
|
|
||||||
}
|
|
||||||
UpdateProgress(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Operate()
|
|
||||||
{
|
|
||||||
List<ProgramSelection> programSelections = ProgramSelection.AllUsableEnabled;
|
|
||||||
OperationsCount = programSelections.Count;
|
|
||||||
CompleteOperationsCount = 0;
|
|
||||||
List<ProgramSelection> disabledSelections = new();
|
|
||||||
foreach (ProgramSelection selection in programSelections)
|
|
||||||
{
|
|
||||||
if (!Program.IsProgramRunningDialog(this, selection)) throw new OperationCanceledException();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await OperateFor(selection);
|
|
||||||
await UpdateUser($"Operation succeeded for {selection.Name}.", InstallationLog.Success);
|
|
||||||
selection.Enabled = false;
|
|
||||||
disabledSelections.Add(selection);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
await UpdateUser($"Operation failed for {selection.Name}: " + exception.ToString(), InstallationLog.Error);
|
|
||||||
}
|
|
||||||
++CompleteOperationsCount;
|
|
||||||
}
|
|
||||||
Program.Cleanup();
|
|
||||||
List<ProgramSelection> FailedSelections = ProgramSelection.AllUsableEnabled;
|
|
||||||
if (FailedSelections.Any())
|
|
||||||
if (FailedSelections.Count == 1) throw new CustomMessageException($"Operation failed for {FailedSelections.First().Name}.");
|
|
||||||
else throw new CustomMessageException($"Operation failed for {FailedSelections.Count} programs.");
|
|
||||||
foreach (ProgramSelection selection in disabledSelections) selection.Enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly int ProgramCount = ProgramSelection.AllUsableEnabled.Count;
|
|
||||||
|
|
||||||
private async void Start()
|
|
||||||
{
|
|
||||||
acceptButton.Enabled = false;
|
|
||||||
retryButton.Enabled = false;
|
|
||||||
cancelButton.Enabled = true;
|
|
||||||
reselectButton.Enabled = false;
|
|
||||||
userProgressBar.Value = userProgressBar.Minimum;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Operate();
|
await OperateFor(selection);
|
||||||
await UpdateUser($"CreamAPI successfully {(Uninstalling ? "uninstalled" : "installed and generated")} for " + ProgramCount + " program(s).", InstallationLog.Success);
|
await UpdateUser($"Operation succeeded for {selection.Name}.", InstallationLog.Success);
|
||||||
|
selection.Enabled = false;
|
||||||
|
disabledSelections.Add(selection);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
await UpdateUser($"CreamAPI {(Uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + exception.ToString(), InstallationLog.Error);
|
await UpdateUser($"Operation failed for {selection.Name}: " + exception.ToString(), InstallationLog.Error);
|
||||||
retryButton.Enabled = true;
|
|
||||||
}
|
}
|
||||||
userProgressBar.Value = userProgressBar.Maximum;
|
++CompleteOperationsCount;
|
||||||
acceptButton.Enabled = true;
|
|
||||||
cancelButton.Enabled = false;
|
|
||||||
reselectButton.Enabled = true;
|
|
||||||
}
|
}
|
||||||
|
Program.Cleanup();
|
||||||
|
List<ProgramSelection> FailedSelections = ProgramSelection.AllUsableEnabled;
|
||||||
|
if (FailedSelections.Any())
|
||||||
|
if (FailedSelections.Count == 1) throw new CustomMessageException($"Operation failed for {FailedSelections.First().Name}.");
|
||||||
|
else throw new CustomMessageException($"Operation failed for {FailedSelections.Count} programs.");
|
||||||
|
foreach (ProgramSelection selection in disabledSelections) selection.Enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnLoad(object sender, EventArgs _)
|
private readonly int ProgramCount = ProgramSelection.AllUsableEnabled.Count;
|
||||||
|
|
||||||
|
private async void Start()
|
||||||
|
{
|
||||||
|
acceptButton.Enabled = false;
|
||||||
|
retryButton.Enabled = false;
|
||||||
|
cancelButton.Enabled = true;
|
||||||
|
reselectButton.Enabled = false;
|
||||||
|
userProgressBar.Value = userProgressBar.Minimum;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
retry:
|
await Operate();
|
||||||
try
|
await UpdateUser($"CreamAPI successfully {(Uninstalling ? "uninstalled" : "installed and generated")} for " + ProgramCount + " program(s).", InstallationLog.Success);
|
||||||
{
|
|
||||||
userInfoLabel.Text = "Loading . . . ";
|
|
||||||
logTextBox.Text = string.Empty;
|
|
||||||
Start();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
if (ExceptionHandler.OutputException(e)) goto retry;
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (Exception exception)
|
||||||
private void OnAccept(object sender, EventArgs e)
|
|
||||||
{
|
{
|
||||||
Program.Cleanup();
|
await UpdateUser($"CreamAPI {(Uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + exception.ToString(), InstallationLog.Error);
|
||||||
Close();
|
retryButton.Enabled = true;
|
||||||
}
|
}
|
||||||
|
userProgressBar.Value = userProgressBar.Maximum;
|
||||||
|
acceptButton.Enabled = true;
|
||||||
|
cancelButton.Enabled = false;
|
||||||
|
reselectButton.Enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnRetry(object sender, EventArgs e)
|
private void OnLoad(object sender, EventArgs _)
|
||||||
|
{
|
||||||
|
retry:
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Program.Cleanup();
|
userInfoLabel.Text = "Loading . . . ";
|
||||||
|
logTextBox.Text = string.Empty;
|
||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
private void OnCancel(object sender, EventArgs e) => Program.Cleanup();
|
|
||||||
|
|
||||||
private void OnReselect(object sender, EventArgs e)
|
|
||||||
{
|
{
|
||||||
Program.Cleanup();
|
if (ExceptionHandler.OutputException(e)) goto retry;
|
||||||
Reselecting = true;
|
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void OnAccept(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Program.Cleanup();
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRetry(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Program.Cleanup();
|
||||||
|
Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCancel(object sender, EventArgs e) => Program.Cleanup();
|
||||||
|
|
||||||
|
private void OnReselect(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Program.Cleanup();
|
||||||
|
Reselecting = true;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,189 +15,188 @@ using Onova;
|
||||||
using Onova.Models;
|
using Onova.Models;
|
||||||
using Onova.Services;
|
using Onova.Services;
|
||||||
|
|
||||||
namespace CreamInstaller
|
namespace CreamInstaller;
|
||||||
|
|
||||||
|
internal partial class MainForm : CustomForm
|
||||||
{
|
{
|
||||||
internal partial class MainForm : CustomForm
|
internal MainForm() : base()
|
||||||
{
|
{
|
||||||
internal MainForm() : base()
|
InitializeComponent();
|
||||||
|
Text = Program.ApplicationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CancellationTokenSource cancellationTokenSource;
|
||||||
|
|
||||||
|
private void StartProgram()
|
||||||
|
{
|
||||||
|
if (cancellationTokenSource is not null)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
cancellationTokenSource.Cancel();
|
||||||
Text = Program.ApplicationName;
|
cancellationTokenSource.Dispose();
|
||||||
|
cancellationTokenSource = null;
|
||||||
}
|
}
|
||||||
|
Hide();
|
||||||
|
new SelectForm(this).ShowDialog();
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
private static CancellationTokenSource cancellationTokenSource;
|
private static readonly HttpClient httpClient = new();
|
||||||
|
private static UpdateManager updateManager = null;
|
||||||
|
private static Version latestVersion = null;
|
||||||
|
private static IReadOnlyList<Version> versions;
|
||||||
|
|
||||||
private void StartProgram()
|
private async void OnLoad()
|
||||||
|
{
|
||||||
|
Size = new(420, 85);
|
||||||
|
progressBar1.Visible = false;
|
||||||
|
ignoreButton.Visible = true;
|
||||||
|
updateButton.Text = "Update";
|
||||||
|
updateButton.Click -= OnUpdateCancel;
|
||||||
|
label1.Text = "Checking for updates . . .";
|
||||||
|
changelogTreeView.Visible = false;
|
||||||
|
changelogTreeView.Location = new(12, 41);
|
||||||
|
changelogTreeView.Size = new(380, 208);
|
||||||
|
|
||||||
|
GithubPackageResolver resolver = new("pointfeev", "CreamInstaller", "CreamInstaller.zip");
|
||||||
|
ZipPackageExtractor extractor = new();
|
||||||
|
updateManager = new(AssemblyMetadata.FromAssembly(Program.EntryAssembly, Program.CurrentProcessFilePath), resolver, extractor);
|
||||||
|
|
||||||
|
if (latestVersion is null)
|
||||||
{
|
{
|
||||||
if (cancellationTokenSource is not null)
|
CheckForUpdatesResult checkForUpdatesResult = null;
|
||||||
{
|
|
||||||
cancellationTokenSource.Cancel();
|
|
||||||
cancellationTokenSource.Dispose();
|
|
||||||
cancellationTokenSource = null;
|
|
||||||
}
|
|
||||||
Hide();
|
|
||||||
new SelectForm(this).ShowDialog();
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly HttpClient httpClient = new();
|
|
||||||
private static UpdateManager updateManager = null;
|
|
||||||
private static Version latestVersion = null;
|
|
||||||
private static IReadOnlyList<Version> versions;
|
|
||||||
|
|
||||||
private async void OnLoad()
|
|
||||||
{
|
|
||||||
Size = new(420, 85);
|
|
||||||
progressBar1.Visible = false;
|
|
||||||
ignoreButton.Visible = true;
|
|
||||||
updateButton.Text = "Update";
|
|
||||||
updateButton.Click -= OnUpdateCancel;
|
|
||||||
label1.Text = "Checking for updates . . .";
|
|
||||||
changelogTreeView.Visible = false;
|
|
||||||
changelogTreeView.Location = new(12, 41);
|
|
||||||
changelogTreeView.Size = new(380, 208);
|
|
||||||
|
|
||||||
GithubPackageResolver resolver = new("pointfeev", "CreamInstaller", "CreamInstaller.zip");
|
|
||||||
ZipPackageExtractor extractor = new();
|
|
||||||
updateManager = new(AssemblyMetadata.FromAssembly(Program.EntryAssembly, Program.CurrentProcessFilePath), resolver, extractor);
|
|
||||||
|
|
||||||
if (latestVersion is null)
|
|
||||||
{
|
|
||||||
CheckForUpdatesResult checkForUpdatesResult = null;
|
|
||||||
cancellationTokenSource = new();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
checkForUpdatesResult = await updateManager.CheckForUpdatesAsync(cancellationTokenSource.Token);
|
|
||||||
cancellationTokenSource.Dispose();
|
|
||||||
cancellationTokenSource = null;
|
|
||||||
if (checkForUpdatesResult.CanUpdate)
|
|
||||||
{
|
|
||||||
latestVersion = checkForUpdatesResult.LastVersion;
|
|
||||||
versions = checkForUpdatesResult.Versions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (latestVersion is null)
|
|
||||||
{
|
|
||||||
updateManager.Dispose();
|
|
||||||
StartProgram();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Size = new(420, 300);
|
|
||||||
label1.Text = $"An update is available: v{latestVersion}";
|
|
||||||
ignoreButton.Enabled = true;
|
|
||||||
updateButton.Enabled = true;
|
|
||||||
updateButton.Click += new(OnUpdate);
|
|
||||||
changelogTreeView.Visible = true;
|
|
||||||
Version currentVersion = new(Application.ProductVersion);
|
|
||||||
foreach (Version version in versions)
|
|
||||||
if (version > currentVersion && !changelogTreeView.Nodes.ContainsKey(version.ToString()))
|
|
||||||
{
|
|
||||||
TreeNode root = new($"v{version}");
|
|
||||||
root.Name = root.Text;
|
|
||||||
changelogTreeView.Nodes.Add(root);
|
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string url = $"https://github.com/pointfeev/CreamInstaller/releases/tag/v{version}";
|
|
||||||
using HttpRequestMessage request = new(HttpMethod.Get, url);
|
|
||||||
using HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
using Stream stream = await response.Content.ReadAsStreamAsync();
|
|
||||||
using StreamReader reader = new(stream, Encoding.UTF8);
|
|
||||||
HtmlAgilityPack.HtmlDocument document = new();
|
|
||||||
document.LoadHtml(reader.ReadToEnd());
|
|
||||||
foreach (HtmlNode node in document.DocumentNode.SelectNodes("//div[@data-test-selector='body-content']/ul/li"))
|
|
||||||
{
|
|
||||||
changelogTreeView.Invoke((MethodInvoker)delegate
|
|
||||||
{
|
|
||||||
TreeNode change = new();
|
|
||||||
change.Text = $"{HttpUtility.HtmlDecode(node.InnerText)}";
|
|
||||||
root.Nodes.Add(change);
|
|
||||||
root.Expand();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
changelogTreeView.Nodes.Remove(root);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnLoad(object sender, EventArgs _)
|
|
||||||
{
|
|
||||||
retry:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string FileName = Path.GetFileName(Program.CurrentProcessFilePath);
|
|
||||||
if (FileName != "CreamInstaller.exe")
|
|
||||||
if (new DialogForm(this).Show(Program.ApplicationName, SystemIcons.Warning,
|
|
||||||
"WARNING: CreamInstaller.exe was renamed!" +
|
|
||||||
"\n\nThis will cause unwanted behavior when updating the program!",
|
|
||||||
"Ignore", "Abort") == DialogResult.Cancel)
|
|
||||||
{
|
|
||||||
Application.Exit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OnLoad();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
if (ExceptionHandler.OutputException(e)) goto retry;
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnIgnore(object sender, EventArgs e) => StartProgram();
|
|
||||||
|
|
||||||
private async void OnUpdate(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
progressBar1.Visible = true;
|
|
||||||
ignoreButton.Visible = false;
|
|
||||||
updateButton.Text = "Cancel";
|
|
||||||
updateButton.Click -= OnUpdate;
|
|
||||||
updateButton.Click += new(OnUpdateCancel);
|
|
||||||
changelogTreeView.Location = new(12, 70);
|
|
||||||
changelogTreeView.Size = new(380, 179);
|
|
||||||
|
|
||||||
Progress<double> progress = new();
|
|
||||||
progress.ProgressChanged += new(delegate (object sender, double _progress)
|
|
||||||
{
|
|
||||||
label1.Text = $"Updating . . . {(int)_progress}%";
|
|
||||||
progressBar1.Value = (int)_progress;
|
|
||||||
});
|
|
||||||
|
|
||||||
label1.Text = "Updating . . . ";
|
|
||||||
cancellationTokenSource = new();
|
cancellationTokenSource = new();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await updateManager.PrepareUpdateAsync(latestVersion, progress, cancellationTokenSource.Token);
|
checkForUpdatesResult = await updateManager.CheckForUpdatesAsync(cancellationTokenSource.Token);
|
||||||
cancellationTokenSource.Dispose();
|
cancellationTokenSource.Dispose();
|
||||||
cancellationTokenSource = null;
|
cancellationTokenSource = null;
|
||||||
|
if (checkForUpdatesResult.CanUpdate)
|
||||||
|
{
|
||||||
|
latestVersion = checkForUpdatesResult.LastVersion;
|
||||||
|
versions = checkForUpdatesResult.Versions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
if (updateManager is not null && updateManager.IsUpdatePrepared(latestVersion))
|
|
||||||
{
|
|
||||||
updateManager.LaunchUpdater(latestVersion);
|
|
||||||
Application.Exit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else OnLoad();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUpdateCancel(object sender, EventArgs e)
|
if (latestVersion is null)
|
||||||
{
|
{
|
||||||
cancellationTokenSource.Cancel();
|
|
||||||
updateManager.Dispose();
|
updateManager.Dispose();
|
||||||
updateManager = null;
|
StartProgram();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Size = new(420, 300);
|
||||||
|
label1.Text = $"An update is available: v{latestVersion}";
|
||||||
|
ignoreButton.Enabled = true;
|
||||||
|
updateButton.Enabled = true;
|
||||||
|
updateButton.Click += new(OnUpdate);
|
||||||
|
changelogTreeView.Visible = true;
|
||||||
|
Version currentVersion = new(Application.ProductVersion);
|
||||||
|
foreach (Version version in versions)
|
||||||
|
if (version > currentVersion && !changelogTreeView.Nodes.ContainsKey(version.ToString()))
|
||||||
|
{
|
||||||
|
TreeNode root = new($"v{version}");
|
||||||
|
root.Name = root.Text;
|
||||||
|
changelogTreeView.Nodes.Add(root);
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string url = $"https://github.com/pointfeev/CreamInstaller/releases/tag/v{version}";
|
||||||
|
using HttpRequestMessage request = new(HttpMethod.Get, url);
|
||||||
|
using HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
using Stream stream = await response.Content.ReadAsStreamAsync();
|
||||||
|
using StreamReader reader = new(stream, Encoding.UTF8);
|
||||||
|
HtmlAgilityPack.HtmlDocument document = new();
|
||||||
|
document.LoadHtml(reader.ReadToEnd());
|
||||||
|
foreach (HtmlNode node in document.DocumentNode.SelectNodes("//div[@data-test-selector='body-content']/ul/li"))
|
||||||
|
{
|
||||||
|
changelogTreeView.Invoke((MethodInvoker)delegate
|
||||||
|
{
|
||||||
|
TreeNode change = new();
|
||||||
|
change.Text = $"{HttpUtility.HtmlDecode(node.InnerText)}";
|
||||||
|
root.Nodes.Add(change);
|
||||||
|
root.Expand();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
changelogTreeView.Nodes.Remove(root);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void OnLoad(object sender, EventArgs _)
|
||||||
|
{
|
||||||
|
retry:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string FileName = Path.GetFileName(Program.CurrentProcessFilePath);
|
||||||
|
if (FileName != "CreamInstaller.exe")
|
||||||
|
if (new DialogForm(this).Show(Program.ApplicationName, SystemIcons.Warning,
|
||||||
|
"WARNING: CreamInstaller.exe was renamed!" +
|
||||||
|
"\n\nThis will cause unwanted behavior when updating the program!",
|
||||||
|
"Ignore", "Abort") == DialogResult.Cancel)
|
||||||
|
{
|
||||||
|
Application.Exit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OnLoad();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (ExceptionHandler.OutputException(e)) goto retry;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnIgnore(object sender, EventArgs e) => StartProgram();
|
||||||
|
|
||||||
|
private async void OnUpdate(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
progressBar1.Visible = true;
|
||||||
|
ignoreButton.Visible = false;
|
||||||
|
updateButton.Text = "Cancel";
|
||||||
|
updateButton.Click -= OnUpdate;
|
||||||
|
updateButton.Click += new(OnUpdateCancel);
|
||||||
|
changelogTreeView.Location = new(12, 70);
|
||||||
|
changelogTreeView.Size = new(380, 179);
|
||||||
|
|
||||||
|
Progress<double> progress = new();
|
||||||
|
progress.ProgressChanged += new(delegate (object sender, double _progress)
|
||||||
|
{
|
||||||
|
label1.Text = $"Updating . . . {(int)_progress}%";
|
||||||
|
progressBar1.Value = (int)_progress;
|
||||||
|
});
|
||||||
|
|
||||||
|
label1.Text = "Updating . . . ";
|
||||||
|
cancellationTokenSource = new();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await updateManager.PrepareUpdateAsync(latestVersion, progress, cancellationTokenSource.Token);
|
||||||
|
cancellationTokenSource.Dispose();
|
||||||
|
cancellationTokenSource = null;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
if (updateManager is not null && updateManager.IsUpdatePrepared(latestVersion))
|
||||||
|
{
|
||||||
|
updateManager.LaunchUpdater(latestVersion);
|
||||||
|
Application.Exit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else OnLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUpdateCancel(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
updateManager.Dispose();
|
||||||
|
updateManager = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,125 +10,124 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace CreamInstaller
|
namespace CreamInstaller;
|
||||||
|
|
||||||
|
internal static class Program
|
||||||
{
|
{
|
||||||
internal static class Program
|
internal static readonly string ApplicationName = Application.CompanyName + " v" + Application.ProductVersion + ": " + Application.ProductName;
|
||||||
|
internal static readonly Assembly EntryAssembly = Assembly.GetEntryAssembly();
|
||||||
|
internal static readonly Process CurrentProcess = Process.GetCurrentProcess();
|
||||||
|
internal static readonly string CurrentProcessFilePath = CurrentProcess.MainModule.FileName;
|
||||||
|
internal static readonly string CurrentProcessDirectory = CurrentProcessFilePath.Substring(0, CurrentProcessFilePath.LastIndexOf("\\"));
|
||||||
|
internal static readonly string BackupFileExtension = ".creaminstaller.backup";
|
||||||
|
|
||||||
|
internal static bool BlockProtectedGames = true;
|
||||||
|
internal static readonly string[] ProtectedGameNames = { "PAYDAY 2", "Call to Arms" }; // non-functioning CreamAPI or DLL detections
|
||||||
|
internal static readonly string[] ProtectedGameDirectories = { @"\EasyAntiCheat", @"\BattlEye" }; // DLL detections
|
||||||
|
internal static readonly string[] ProtectedGameDirectoryExceptions = { "Arma 3" }; // Arma 3's BattlEye doesn't detect DLL changes?
|
||||||
|
|
||||||
|
internal static bool IsGameBlocked(string name, string directory)
|
||||||
{
|
{
|
||||||
internal static readonly string ApplicationName = Application.CompanyName + " v" + Application.ProductVersion + ": " + Application.ProductName;
|
if (ProtectedGameNames.Contains(name)) return true;
|
||||||
internal static readonly Assembly EntryAssembly = Assembly.GetEntryAssembly();
|
if (!ProtectedGameDirectoryExceptions.Contains(name))
|
||||||
internal static readonly Process CurrentProcess = Process.GetCurrentProcess();
|
foreach (string path in ProtectedGameDirectories)
|
||||||
internal static readonly string CurrentProcessFilePath = CurrentProcess.MainModule.FileName;
|
if (Directory.Exists(directory + path)) return true;
|
||||||
internal static readonly string CurrentProcessDirectory = CurrentProcessFilePath.Substring(0, CurrentProcessFilePath.LastIndexOf("\\"));
|
return false;
|
||||||
internal static readonly string BackupFileExtension = ".creaminstaller.backup";
|
}
|
||||||
|
|
||||||
internal static bool BlockProtectedGames = true;
|
[STAThread]
|
||||||
internal static readonly string[] ProtectedGameNames = { "PAYDAY 2", "Call to Arms" }; // non-functioning CreamAPI or DLL detections
|
private static void Main()
|
||||||
internal static readonly string[] ProtectedGameDirectories = { @"\EasyAntiCheat", @"\BattlEye" }; // DLL detections
|
{
|
||||||
internal static readonly string[] ProtectedGameDirectoryExceptions = { "Arma 3" }; // Arma 3's BattlEye doesn't detect DLL changes?
|
Mutex mutex = new(true, "CreamInstaller", out bool createdNew);
|
||||||
|
if (createdNew)
|
||||||
internal static bool IsGameBlocked(string name, string directory)
|
|
||||||
{
|
|
||||||
if (ProtectedGameNames.Contains(name)) return true;
|
|
||||||
if (!ProtectedGameDirectoryExceptions.Contains(name))
|
|
||||||
foreach (string path in ProtectedGameDirectories)
|
|
||||||
if (Directory.Exists(directory + path)) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[STAThread]
|
|
||||||
private static void Main()
|
|
||||||
{
|
|
||||||
Mutex mutex = new(true, "CreamInstaller", out bool createdNew);
|
|
||||||
if (createdNew)
|
|
||||||
{
|
|
||||||
Application.SetHighDpiMode(HighDpiMode.SystemAware);
|
|
||||||
Application.EnableVisualStyles();
|
|
||||||
Application.SetCompatibleTextRenderingDefault(false);
|
|
||||||
Application.ApplicationExit += new(OnApplicationExit);
|
|
||||||
retry:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Application.Run(new MainForm());
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
if (ExceptionHandler.OutputException(e)) goto retry;
|
|
||||||
Application.Exit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static async Task<Image> GetImageFromUrl(string url)
|
|
||||||
{
|
{
|
||||||
|
Application.SetHighDpiMode(HighDpiMode.SystemAware);
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
Application.ApplicationExit += new(OnApplicationExit);
|
||||||
|
retry:
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpClient httpClient = new();
|
Application.Run(new MainForm());
|
||||||
httpClient.DefaultRequestHeaders.Add("user-agent", "CreamInstaller");
|
|
||||||
return new Bitmap(await httpClient.GetStreamAsync(url));
|
|
||||||
}
|
}
|
||||||
catch { }
|
catch (Exception e)
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OpenDirectoryInFileExplorer(string path) => Process.Start(new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = "explorer.exe",
|
|
||||||
Arguments = path
|
|
||||||
});
|
|
||||||
|
|
||||||
internal static void OpenUrlInInternetBrowser(string url) => Process.Start(new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = url,
|
|
||||||
UseShellExecute = true
|
|
||||||
});
|
|
||||||
|
|
||||||
internal static Image GetFileIconImage(string path) => File.Exists(path) ? Icon.ExtractAssociatedIcon(path).ToBitmap() : null;
|
|
||||||
|
|
||||||
internal static Image GetFileExplorerImage() => GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe");
|
|
||||||
|
|
||||||
internal static bool IsProgramRunningDialog(Form form, ProgramSelection selection)
|
|
||||||
{
|
|
||||||
if (selection.AreSteamApiDllsLocked)
|
|
||||||
{
|
{
|
||||||
if (new DialogForm(form).Show(ApplicationName, SystemIcons.Error,
|
if (ExceptionHandler.OutputException(e)) goto retry;
|
||||||
$"ERROR: {selection.Name} is currently running!" +
|
Application.Exit();
|
||||||
"\n\nPlease close the program/game to continue . . . ",
|
return;
|
||||||
"Retry", "Cancel") == DialogResult.OK)
|
|
||||||
return IsProgramRunningDialog(form, selection);
|
|
||||||
}
|
}
|
||||||
else return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool IsFilePathLocked(this string filePath)
|
|
||||||
{
|
|
||||||
try { File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None).Close(); }
|
|
||||||
catch (FileNotFoundException) { return false; }
|
|
||||||
catch (IOException) { return true; }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static SelectForm SelectForm;
|
|
||||||
internal static InstallForm InstallForm;
|
|
||||||
|
|
||||||
internal static List<ProgramSelection> ProgramSelections = new();
|
|
||||||
|
|
||||||
internal static bool Canceled = false;
|
|
||||||
internal static async void Cleanup(bool cancel = true)
|
|
||||||
{
|
|
||||||
Canceled = cancel;
|
|
||||||
await SteamCMD.Kill();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnApplicationExit(object s, EventArgs e) => Cleanup();
|
|
||||||
|
|
||||||
internal static void InheritLocation(this Form form, Form fromForm)
|
|
||||||
{
|
|
||||||
int X = fromForm.Location.X + fromForm.Size.Width / 2 - form.Size.Width / 2;
|
|
||||||
int Y = fromForm.Location.Y + fromForm.Size.Height / 2 - form.Size.Height / 2;
|
|
||||||
form.Location = new(X, Y);
|
|
||||||
}
|
}
|
||||||
|
mutex.Close();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
internal static async Task<Image> GetImageFromUrl(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HttpClient httpClient = new();
|
||||||
|
httpClient.DefaultRequestHeaders.Add("user-agent", "CreamInstaller");
|
||||||
|
return new Bitmap(await httpClient.GetStreamAsync(url));
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void OpenDirectoryInFileExplorer(string path) => Process.Start(new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "explorer.exe",
|
||||||
|
Arguments = path
|
||||||
|
});
|
||||||
|
|
||||||
|
internal static void OpenUrlInInternetBrowser(string url) => Process.Start(new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = url,
|
||||||
|
UseShellExecute = true
|
||||||
|
});
|
||||||
|
|
||||||
|
internal static Image GetFileIconImage(string path) => File.Exists(path) ? Icon.ExtractAssociatedIcon(path).ToBitmap() : null;
|
||||||
|
|
||||||
|
internal static Image GetFileExplorerImage() => GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe");
|
||||||
|
|
||||||
|
internal static bool IsProgramRunningDialog(Form form, ProgramSelection selection)
|
||||||
|
{
|
||||||
|
if (selection.AreSteamApiDllsLocked)
|
||||||
|
{
|
||||||
|
if (new DialogForm(form).Show(ApplicationName, SystemIcons.Error,
|
||||||
|
$"ERROR: {selection.Name} is currently running!" +
|
||||||
|
"\n\nPlease close the program/game to continue . . . ",
|
||||||
|
"Retry", "Cancel") == DialogResult.OK)
|
||||||
|
return IsProgramRunningDialog(form, selection);
|
||||||
|
}
|
||||||
|
else return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsFilePathLocked(this string filePath)
|
||||||
|
{
|
||||||
|
try { File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None).Close(); }
|
||||||
|
catch (FileNotFoundException) { return false; }
|
||||||
|
catch (IOException) { return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static SelectForm SelectForm;
|
||||||
|
internal static InstallForm InstallForm;
|
||||||
|
|
||||||
|
internal static List<ProgramSelection> ProgramSelections = new();
|
||||||
|
|
||||||
|
internal static bool Canceled = false;
|
||||||
|
internal static async void Cleanup(bool cancel = true)
|
||||||
|
{
|
||||||
|
Canceled = cancel;
|
||||||
|
await SteamCMD.Kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnApplicationExit(object s, EventArgs e) => Cleanup();
|
||||||
|
|
||||||
|
internal static void InheritLocation(this Form form, Form fromForm)
|
||||||
|
{
|
||||||
|
int X = fromForm.Location.X + fromForm.Size.Width / 2 - form.Size.Width / 2;
|
||||||
|
int Y = fromForm.Location.Y + fromForm.Size.Height / 2 - form.Size.Height / 2;
|
||||||
|
form.Location = new(X, Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace CreamInstaller
|
namespace CreamInstaller;
|
||||||
|
|
||||||
|
internal static class FileResourceExtensions
|
||||||
{
|
{
|
||||||
internal static class FileResourceExtensions
|
internal static void Write(this byte[] resource, string filePath)
|
||||||
{
|
{
|
||||||
internal static void Write(this byte[] resource, string filePath)
|
using FileStream file = new(filePath, FileMode.Create, FileAccess.Write);
|
||||||
{
|
file.Write(resource);
|
||||||
using FileStream file = new(filePath, FileMode.Create, FileAccess.Write);
|
|
||||||
file.Write(resource);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue