- Converted multiple methods to asynchronous to improve performance
- Added a minimal delay to the install form updates to keep the log from glitching
- Added double buffering to all controls to reduce flickering
- Fixed the cancel button not fully cancelling SteamCMD processes
- Internalized all possible classes, methods, accessors and fields
This commit is contained in:
pointfeev 2022-01-21 16:10:15 -05:00
parent 40ba6ab448
commit b3526c7fbf
14 changed files with 210 additions and 181 deletions

View file

@ -3,9 +3,9 @@ using System.Windows.Forms;
namespace CreamInstaller namespace CreamInstaller
{ {
public static class ExceptionHandler internal static class ExceptionHandler
{ {
public static bool OutputException(Exception e) internal static bool OutputException(Exception e)
{ {
while (e.InnerException is not null) while (e.InnerException is not null)
{ {
@ -48,7 +48,7 @@ namespace CreamInstaller
} }
} }
public 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";
@ -58,7 +58,7 @@ namespace CreamInstaller
return Message; return Message;
} }
public CustomMessageException(string message) internal CustomMessageException(string message)
{ {
this.message = message; this.message = message;
} }

View file

@ -3,17 +3,17 @@ using System.Windows.Forms;
namespace CreamInstaller namespace CreamInstaller
{ {
public static class InstallationLog internal static class InstallationLog
{ {
public static readonly Color Background = Color.DarkSlateGray; internal static readonly Color Background = Color.DarkSlateGray;
public static readonly Color Operation = Color.LightGray; internal static readonly Color Operation = Color.LightGray;
public static readonly Color Resource = Color.LightBlue; internal static readonly Color Resource = Color.LightBlue;
public static readonly Color Success = Color.LightGreen; internal static readonly Color Success = Color.LightGreen;
public static readonly Color Cleanup = Color.YellowGreen; internal static readonly Color Cleanup = Color.YellowGreen;
public static readonly Color Warning = Color.Yellow; internal static readonly Color Warning = Color.Yellow;
public static readonly Color Error = Color.DarkOrange; internal static readonly Color Error = Color.DarkOrange;
public static void AppendText(this RichTextBox logTextBox, string text, Color color) internal static void AppendText(this RichTextBox logTextBox, string text, Color color)
{ {
logTextBox.SelectionStart = logTextBox.TextLength; logTextBox.SelectionStart = logTextBox.TextLength;
logTextBox.SelectionLength = 0; logTextBox.SelectionLength = 0;

View file

@ -6,23 +6,23 @@ using System.Linq;
namespace CreamInstaller namespace CreamInstaller
{ {
public class ProgramSelection internal class ProgramSelection
{ {
public bool Enabled = false; internal bool Enabled = false;
public bool Usable = true; internal bool Usable = true;
public string Name; internal string Name;
public string RootDirectory; internal string RootDirectory;
public int SteamAppId; internal int SteamAppId;
public List<string> SteamApiDllDirectories; internal List<string> SteamApiDllDirectories;
public VProperty AppInfo = null; internal VProperty AppInfo = null;
public readonly SortedList<int, string> AllSteamDlc = new(); internal readonly SortedList<int, string> AllSteamDlc = new();
public readonly SortedList<int, string> SelectedSteamDlc = new(); internal readonly SortedList<int, string> SelectedSteamDlc = new();
public readonly List<Tuple<int, string, SortedList<int, string>>> ExtraSteamAppIdDlc = new(); internal readonly List<Tuple<int, string, SortedList<int, string>>> ExtraSteamAppIdDlc = new();
public bool AreSteamApiDllsLocked internal bool AreSteamApiDllsLocked
{ {
get get
{ {
@ -51,7 +51,7 @@ namespace CreamInstaller
} }
} }
public void ToggleDlc(int dlcAppId, bool enabled) internal void ToggleDlc(int dlcAppId, bool enabled)
{ {
foreach (KeyValuePair<int, string> dlcApp in AllSteamDlc) foreach (KeyValuePair<int, string> dlcApp in AllSteamDlc)
{ {
@ -64,7 +64,7 @@ namespace CreamInstaller
Enabled = SelectedSteamDlc.Any(); Enabled = SelectedSteamDlc.Any();
} }
public void ToggleAllDlc(bool enabled) internal void ToggleAllDlc(bool enabled)
{ {
if (!enabled) if (!enabled)
{ {
@ -81,12 +81,12 @@ namespace CreamInstaller
Enabled = SelectedSteamDlc.Any(); Enabled = SelectedSteamDlc.Any();
} }
public ProgramSelection() internal ProgramSelection()
{ {
All.Add(this); All.Add(this);
} }
public void Validate() internal void Validate()
{ {
SteamApiDllDirectories.RemoveAll(directory => !Directory.Exists(directory)); SteamApiDllDirectories.RemoveAll(directory => !Directory.Exists(directory));
if (!Directory.Exists(RootDirectory) || !SteamApiDllDirectories.Any()) if (!Directory.Exists(RootDirectory) || !SteamApiDllDirectories.Any())
@ -95,23 +95,23 @@ namespace CreamInstaller
} }
} }
public static void ValidateAll() internal static void ValidateAll()
{ {
All.ForEach(selection => selection.Validate()); All.ForEach(selection => selection.Validate());
} }
public static List<ProgramSelection> All => Program.ProgramSelections; internal static List<ProgramSelection> All => Program.ProgramSelections;
public static List<ProgramSelection> AllSafe => All.FindAll(s => s.Usable); internal static List<ProgramSelection> AllSafe => All.FindAll(s => s.Usable);
public static List<ProgramSelection> AllSafeEnabled => AllSafe.FindAll(s => s.Enabled); internal static List<ProgramSelection> AllSafeEnabled => AllSafe.FindAll(s => s.Enabled);
public static ProgramSelection FromAppId(int appId) internal static ProgramSelection FromAppId(int appId)
{ {
return AllSafe.Find(s => s.SteamAppId == appId); return AllSafe.Find(s => s.SteamAppId == appId);
} }
public static KeyValuePair<int, string>? GetDlcFromAppId(int appId) internal static KeyValuePair<int, string>? GetDlcFromAppId(int appId)
{ {
foreach (ProgramSelection selection in AllSafe) foreach (ProgramSelection selection in AllSafe)
{ {

View file

@ -8,26 +8,30 @@ using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
namespace CreamInstaller namespace CreamInstaller
{ {
public static class SteamCMD internal static class SteamCMD
{ {
public static readonly string DirectoryPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller"; internal static readonly string DirectoryPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller";
public static readonly string FilePath = DirectoryPath + @"\steamcmd.exe"; internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe";
public static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip"; internal static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip";
public static readonly string DllPath = DirectoryPath + @"\steamclient.dll"; internal static readonly string DllPath = DirectoryPath + @"\steamclient.dll";
public static readonly string AppCachePath = DirectoryPath + @"\appcache"; internal static readonly string AppCachePath = DirectoryPath + @"\appcache";
public static readonly string AppCacheAppInfoPath = AppCachePath + @"\appinfo.vdf"; internal static readonly string AppCacheAppInfoPath = AppCachePath + @"\appinfo.vdf";
public static readonly string AppInfoPath = DirectoryPath + @"\appinfo"; internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo";
public static readonly Version MinimumAppInfoVersion = Version.Parse("2.0.3.2"); internal static readonly Version MinimumAppInfoVersion = Version.Parse("2.0.3.2");
public static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt"; internal static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt";
public static bool Run(string command, out string output) internal static string Run(string command)
{ {
bool success = true; if (Program.Canceled)
{
return "";
}
List<string> logs = new(); List<string> logs = new();
ProcessStartInfo processStartInfo = new() ProcessStartInfo processStartInfo = new()
{ {
@ -50,18 +54,17 @@ namespace CreamInstaller
process.BeginErrorReadLine(); process.BeginErrorReadLine();
process.WaitForExit(); process.WaitForExit();
} }
output = string.Join("\r\n", logs); return string.Join("\r\n", logs);
return success;
} }
public static void Setup() internal static async Task Setup()
{ {
Kill(); await Kill();
if (!File.Exists(FilePath)) if (!File.Exists(FilePath))
{ {
using (HttpClient httpClient = new()) using (HttpClient httpClient = new())
{ {
byte[] file = httpClient.GetByteArrayAsync("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip").Result; byte[] file = await httpClient.GetByteArrayAsync("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip");
file.Write(ArchivePath); file.Write(ArchivePath);
} }
@ -85,11 +88,11 @@ namespace CreamInstaller
} }
if (!File.Exists(DllPath)) if (!File.Exists(DllPath))
{ {
Run($@"+quit", out _); Run($@"+quit");
} }
} }
public static bool GetAppInfo(int appId, out VProperty appInfo, string branch = "public", int buildId = 0) internal static bool GetAppInfo(int appId, out VProperty appInfo, string branch = "public", int buildId = 0)
{ {
appInfo = null; appInfo = null;
if (Program.Canceled) if (Program.Canceled)
@ -101,14 +104,19 @@ namespace CreamInstaller
string appUpdatePath = $@"{AppInfoPath}\{appId}"; string appUpdatePath = $@"{AppInfoPath}\{appId}";
string appUpdateFile = $@"{appUpdatePath}\appinfo.txt"; string appUpdateFile = $@"{appUpdatePath}\appinfo.txt";
restart: restart:
if (Program.Canceled)
{
return false;
}
if (Directory.Exists(appUpdatePath) && File.Exists(appUpdateFile)) if (Directory.Exists(appUpdatePath) && File.Exists(appUpdateFile))
{ {
output = File.ReadAllText(appUpdateFile, Encoding.UTF8); output = File.ReadAllText(appUpdateFile, Encoding.UTF8);
} }
else else
{ {
Run($@"+@ShutdownOnFailedCommand 0 +login anonymous +app_info_print {appId} +force_install_dir {appUpdatePath} +app_update 4 +quit", out _); Run($@"+@ShutdownOnFailedCommand 0 +login anonymous +app_info_print {appId} +force_install_dir {appUpdatePath} +app_update 4 +quit");
Run($@"+@ShutdownOnFailedCommand 0 +login anonymous +app_info_print {appId} +quit", out output); output = Run($@"+@ShutdownOnFailedCommand 0 +login anonymous +app_info_print {appId} +quit");
int openBracket = output.IndexOf("{"); int openBracket = output.IndexOf("{");
int closeBracket = output.LastIndexOf("}"); int closeBracket = output.LastIndexOf("}");
if (openBracket != -1 && closeBracket != -1) if (openBracket != -1 && closeBracket != -1)
@ -149,7 +157,6 @@ namespace CreamInstaller
{ {
return true; return true;
} }
if (type is null || int.Parse(buildid) < buildId) if (type is null || int.Parse(buildid) < buildId)
{ {
foreach (int id in ParseDlcAppIds(appInfo)) foreach (int id in ParseDlcAppIds(appInfo))
@ -164,14 +171,13 @@ namespace CreamInstaller
{ {
Directory.Delete(appUpdatePath, true); Directory.Delete(appUpdatePath, true);
} }
goto restart; goto restart;
} }
} }
return true; return true;
} }
public static List<int> ParseDlcAppIds(VProperty appInfo) internal static List<int> ParseDlcAppIds(VProperty appInfo)
{ {
List<int> dlcIds = new(); List<int> dlcIds = new();
if (appInfo is not VProperty) if (appInfo is not VProperty)
@ -213,17 +219,23 @@ namespace CreamInstaller
return dlcIds; return dlcIds;
} }
public static void Kill() internal static async Task Kill()
{ {
List<Task> tasks = new();
foreach (Process process in Process.GetProcessesByName("steamcmd")) foreach (Process process in Process.GetProcessesByName("steamcmd"))
{ {
process.Kill(); process.Kill();
tasks.Add(Task.Run(() => process.WaitForExit()));
}
foreach (Task task in tasks)
{
await task;
} }
} }
public static void Dispose() internal static void Dispose()
{ {
Kill(); Kill().Wait();
if (Directory.Exists(DirectoryPath)) if (Directory.Exists(DirectoryPath))
{ {
Directory.Delete(DirectoryPath, true); Directory.Delete(DirectoryPath, true);

View file

@ -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.0.0</Version> <Version>2.2.1.0</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>

View file

@ -0,0 +1,27 @@
using System.Windows.Forms;
namespace CreamInstaller
{
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
{
get
{
CreateParams handleParam = base.CreateParams;
handleParam.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return handleParam;
}
}
}
}

View file

@ -4,7 +4,7 @@ using System.Windows.Forms;
namespace CreamInstaller namespace CreamInstaller
{ {
public class CustomTreeView : TreeView internal class CustomTreeView : TreeView
{ {
protected override void WndProc(ref Message m) protected override void WndProc(ref Message m)
{ {
@ -18,7 +18,7 @@ namespace CreamInstaller
} }
} }
public CustomTreeView() : base() internal CustomTreeView() : base()
{ {
DrawMode = TreeViewDrawMode.OwnerDrawAll; DrawMode = TreeViewDrawMode.OwnerDrawAll;
DrawNode += new DrawTreeNodeEventHandler(DrawTreeNode); DrawNode += new DrawTreeNodeEventHandler(DrawTreeNode);

View file

@ -3,16 +3,14 @@ using System.Windows.Forms;
namespace CreamInstaller namespace CreamInstaller
{ {
public partial class DialogForm : Form internal partial class DialogForm : CustomForm
{ {
public DialogForm(IWin32Window owner) internal DialogForm(IWin32Window owner) : base(owner)
{ {
Owner = owner as Form;
InitializeComponent(); InitializeComponent();
Icon = Properties.Resources.Icon;
} }
public DialogResult Show(string formName, Icon descriptionIcon, string descriptionText, string acceptButtonText, string cancelButtonText = null) internal DialogResult Show(string formName, Icon descriptionIcon, string descriptionText, string acceptButtonText, string cancelButtonText = null)
{ {
icon.Image = descriptionIcon.ToBitmap(); icon.Image = descriptionIcon.ToBitmap();
Text = formName; Text = formName;

View file

@ -10,18 +10,16 @@ using System.Windows.Forms;
namespace CreamInstaller namespace CreamInstaller
{ {
public partial class InstallForm : Form internal partial class InstallForm : CustomForm
{ {
public bool Reselecting = false; internal bool Reselecting = false;
public bool Uninstalling = false; internal bool Uninstalling = false;
public InstallForm(IWin32Window owner, bool uninstall = false) internal InstallForm(IWin32Window owner, bool uninstall = false) : base(owner)
{ {
Owner = owner as Form;
InitializeComponent(); InitializeComponent();
Program.InstallForm = this;
Text = Program.ApplicationName; Text = Program.ApplicationName;
Icon = Properties.Resources.Icon; Program.InstallForm = this;
logTextBox.BackColor = InstallationLog.Background; logTextBox.BackColor = InstallationLog.Background;
Uninstalling = uninstall; Uninstalling = uninstall;
} }
@ -29,7 +27,7 @@ namespace CreamInstaller
private int OperationsCount; private int OperationsCount;
private int CompleteOperationsCount; private int CompleteOperationsCount;
public void UpdateProgress(int progress) internal void UpdateProgress(int progress)
{ {
int value = (int)((float)(CompleteOperationsCount / (float)OperationsCount) * 100) + (progress / OperationsCount); int value = (int)((float)(CompleteOperationsCount / (float)OperationsCount) * 100) + (progress / OperationsCount);
if (value < userProgressBar.Value) if (value < userProgressBar.Value)
@ -39,7 +37,7 @@ namespace CreamInstaller
userProgressBar.Value = value; userProgressBar.Value = value;
} }
public void UpdateUser(string text, Color color, bool log = true) internal async Task UpdateUser(string text, Color color, bool log = true)
{ {
userInfoLabel.Text = text; userInfoLabel.Text = text;
if (log && !logTextBox.IsDisposed) if (log && !logTextBox.IsDisposed)
@ -50,6 +48,23 @@ namespace CreamInstaller
} }
logTextBox.AppendText(userInfoLabel.Text, color); logTextBox.AppendText(userInfoLabel.Text, color);
} }
await Task.Run(() => Thread.Sleep(1)); // to keep the text box control from glitching
}
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);
foreach (KeyValuePair<int, string> dlcApp in steamDlcApps)
{
writer.WriteLine($"{dlcApp.Key} = {dlcApp.Value}");
await UpdateUser($"Added DLC to cream_api.ini with appid {dlcApp.Key} ({dlcApp.Value})", InstallationLog.Resource);
}
} }
private async Task OperateFor(ProgramSelection selection) private async Task OperateFor(ProgramSelection selection)
@ -59,7 +74,7 @@ namespace CreamInstaller
int cur = 0; int cur = 0;
foreach (string directory in selection.SteamApiDllDirectories) foreach (string directory in selection.SteamApiDllDirectories)
{ {
UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); await UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation);
if (!Program.IsProgramRunningDialog(this, selection)) if (!Program.IsProgramRunningDialog(this, selection))
{ {
throw new OperationCanceledException(); throw new OperationCanceledException();
@ -76,25 +91,25 @@ namespace CreamInstaller
if (File.Exists(api)) if (File.Exists(api))
{ {
File.Delete(api); File.Delete(api);
UpdateUser($"Deleted file: {Path.GetFileName(api)}", InstallationLog.Resource); await UpdateUser($"Deleted file: {Path.GetFileName(api)}", InstallationLog.Resource);
} }
File.Move(api_o, api); File.Move(api_o, api);
UpdateUser($"Renamed file: {Path.GetFileName(api_o)} -> {Path.GetFileName(api)}", InstallationLog.Resource); await UpdateUser($"Renamed file: {Path.GetFileName(api_o)} -> {Path.GetFileName(api)}", InstallationLog.Resource);
} }
if (File.Exists(api64_o)) if (File.Exists(api64_o))
{ {
if (File.Exists(api64)) if (File.Exists(api64))
{ {
File.Delete(api64); File.Delete(api64);
UpdateUser($"Deleted file: {Path.GetFileName(api64)}", InstallationLog.Resource); await UpdateUser($"Deleted file: {Path.GetFileName(api64)}", InstallationLog.Resource);
} }
File.Move(api64_o, api64); File.Move(api64_o, api64);
UpdateUser($"Renamed file: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", InstallationLog.Resource); await UpdateUser($"Renamed file: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", InstallationLog.Resource);
} }
if (File.Exists(cApi)) if (File.Exists(cApi))
{ {
File.Delete(cApi); File.Delete(cApi);
UpdateUser($"Deleted file: {Path.GetFileName(cApi)}", InstallationLog.Resource); await UpdateUser($"Deleted file: {Path.GetFileName(cApi)}", InstallationLog.Resource);
} }
} }
else else
@ -102,60 +117,38 @@ namespace CreamInstaller
if (File.Exists(api) && !File.Exists(api_o)) if (File.Exists(api) && !File.Exists(api_o))
{ {
File.Move(api, api_o); File.Move(api, api_o);
UpdateUser($"Renamed file: {Path.GetFileName(api)} -> {Path.GetFileName(api_o)}", InstallationLog.Resource); await UpdateUser($"Renamed file: {Path.GetFileName(api)} -> {Path.GetFileName(api_o)}", InstallationLog.Resource);
} }
if (File.Exists(api_o)) if (File.Exists(api_o))
{ {
Properties.Resources.API.Write(api); Properties.Resources.API.Write(api);
UpdateUser($"Wrote resource to file: {Path.GetFileName(api)}", InstallationLog.Resource); await UpdateUser($"Wrote resource to file: {Path.GetFileName(api)}", InstallationLog.Resource);
} }
if (File.Exists(api64) && !File.Exists(api64_o)) if (File.Exists(api64) && !File.Exists(api64_o))
{ {
File.Move(api64, api64_o); File.Move(api64, api64_o);
UpdateUser($"Renamed file: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", InstallationLog.Resource); await UpdateUser($"Renamed file: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", InstallationLog.Resource);
} }
if (File.Exists(api64_o)) if (File.Exists(api64_o))
{ {
Properties.Resources.API64.Write(api64); Properties.Resources.API64.Write(api64);
UpdateUser($"Wrote resource to file: {Path.GetFileName(api64)}", InstallationLog.Resource); await UpdateUser($"Wrote resource to file: {Path.GetFileName(api64)}", InstallationLog.Resource);
} }
UpdateUser("Generating CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); await UpdateUser("Generating CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation);
File.Create(cApi).Close(); File.Create(cApi).Close();
StreamWriter writer = new(cApi, true, Encoding.UTF8); StreamWriter writer = new(cApi, true, Encoding.UTF8);
writer.WriteLine("; " + Application.CompanyName + " v" + Application.ProductVersion); writer.WriteLine("; " + Application.CompanyName + " v" + Application.ProductVersion);
if (selection.SteamAppId > 0) if (selection.SteamAppId > 0)
{ {
writer.WriteLine(); await WriteConfiguration(writer, selection.SteamAppId, selection.Name, selection.SelectedSteamDlc);
writer.WriteLine($"; {selection.Name}");
writer.WriteLine("[steam]");
writer.WriteLine($"appid = {selection.SteamAppId}");
writer.WriteLine();
writer.WriteLine("[dlc]");
UpdateUser($"Added game to cream_api.ini with appid {selection.SteamAppId} ({selection.Name})", InstallationLog.Resource);
foreach (KeyValuePair<int, string> dlcApp in selection.SelectedSteamDlc)
{
writer.WriteLine($"{dlcApp.Key} = {dlcApp.Value}");
UpdateUser($"Added DLC to cream_api.ini with appid {dlcApp.Key} ({dlcApp.Value})", InstallationLog.Resource);
}
} }
foreach (Tuple<int, string, SortedList<int, string>> extraAppDlc in selection.ExtraSteamAppIdDlc) foreach (Tuple<int, string, SortedList<int, string>> extraAppDlc in selection.ExtraSteamAppIdDlc)
{ {
writer.WriteLine(); await WriteConfiguration(writer, extraAppDlc.Item1, extraAppDlc.Item2, extraAppDlc.Item3);
writer.WriteLine("[steam]");
writer.WriteLine($"appid = {extraAppDlc.Item1}");
writer.WriteLine();
writer.WriteLine("[dlc]");
UpdateUser($"Added game to cream_api.ini with appid {extraAppDlc.Item1} ({extraAppDlc.Item2})", InstallationLog.Resource);
foreach (KeyValuePair<int, string> dlcApp in extraAppDlc.Item3)
{
writer.WriteLine($"{dlcApp.Key} = {dlcApp.Value}");
UpdateUser($"Added DLC to cream_api.ini with appid {dlcApp.Key} ({dlcApp.Value})", InstallationLog.Resource);
}
} }
writer.Flush(); writer.Flush();
writer.Close(); writer.Close();
} }
await Task.Run(() => Thread.Sleep(10)); // to keep the text box control from glitching
UpdateProgress(++cur / count * 100); UpdateProgress(++cur / count * 100);
} }
UpdateProgress(100); UpdateProgress(100);
@ -175,16 +168,16 @@ namespace CreamInstaller
try try
{ {
await OperateFor(selection); await OperateFor(selection);
UpdateUser($"Operation succeeded for {selection.Name}.", InstallationLog.Success); await UpdateUser($"Operation succeeded for {selection.Name}.", InstallationLog.Success);
selection.Enabled = false; selection.Enabled = false;
} }
catch (Exception exception) catch (Exception exception)
{ {
UpdateUser($"Operation failed for {selection.Name}: " + exception.ToString(), InstallationLog.Error); await UpdateUser($"Operation failed for {selection.Name}: " + exception.ToString(), InstallationLog.Error);
} }
++CompleteOperationsCount; ++CompleteOperationsCount;
} }
Program.Cleanup(); await Program.Cleanup();
List<ProgramSelection> FailedSelections = ProgramSelection.AllSafeEnabled; List<ProgramSelection> FailedSelections = ProgramSelection.AllSafeEnabled;
if (FailedSelections.Any()) if (FailedSelections.Any())
{ {
@ -211,11 +204,11 @@ namespace CreamInstaller
try try
{ {
await Operate(); await Operate();
UpdateUser($"CreamAPI successfully {(Uninstalling ? "uninstalled" : "installed and generated")} for " + ProgramCount + " program(s).", InstallationLog.Success); await UpdateUser($"CreamAPI successfully {(Uninstalling ? "uninstalled" : "installed and generated")} for " + ProgramCount + " program(s).", InstallationLog.Success);
} }
catch (Exception exception) catch (Exception exception)
{ {
UpdateUser($"CreamAPI {(Uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + exception.ToString(), InstallationLog.Error); await UpdateUser($"CreamAPI {(Uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + exception.ToString(), InstallationLog.Error);
retryButton.Enabled = true; retryButton.Enabled = true;
} }
userProgressBar.Value = userProgressBar.Maximum; userProgressBar.Value = userProgressBar.Maximum;
@ -239,31 +232,30 @@ namespace CreamInstaller
{ {
goto retry; goto retry;
} }
Close(); Close();
} }
} }
private void OnAccept(object sender, EventArgs e) private void OnAccept(object sender, EventArgs e)
{ {
Program.Cleanup(); Program.Cleanup().Wait();
Close(); Close();
} }
private void OnRetry(object sender, EventArgs e) private void OnRetry(object sender, EventArgs e)
{ {
Program.Cleanup(); Program.Cleanup().Wait();
Start(); Start();
} }
private void OnCancel(object sender, EventArgs e) private void OnCancel(object sender, EventArgs e)
{ {
Program.Cleanup(); Program.Cleanup().Wait();
} }
private void OnReselect(object sender, EventArgs e) private void OnReselect(object sender, EventArgs e)
{ {
Program.Cleanup(); Program.Cleanup().Wait();
Reselecting = true; Reselecting = true;
Close(); Close();
} }

View file

@ -15,13 +15,12 @@ using System.Windows.Forms;
namespace CreamInstaller namespace CreamInstaller
{ {
public partial class MainForm : Form internal partial class MainForm : CustomForm
{ {
public MainForm() internal MainForm() : base()
{ {
InitializeComponent(); InitializeComponent();
Text = Program.ApplicationName; Text = Program.ApplicationName;
Icon = Properties.Resources.Icon;
} }
private static CancellationTokenSource cancellationTokenSource; private static CancellationTokenSource cancellationTokenSource;

View file

@ -43,7 +43,7 @@ namespace CreamInstaller
this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel();
this.allCheckBox = new System.Windows.Forms.CheckBox(); this.allCheckBox = new System.Windows.Forms.CheckBox();
this.progressBar1 = new System.Windows.Forms.ProgressBar(); this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.label2 = new System.Windows.Forms.Label(); this.progressLabel = new System.Windows.Forms.Label();
this.scanButton = new System.Windows.Forms.Button(); this.scanButton = new System.Windows.Forms.Button();
this.uninstallButton = new System.Windows.Forms.Button(); this.uninstallButton = new System.Windows.Forms.Button();
this.groupBox1.SuspendLayout(); this.groupBox1.SuspendLayout();
@ -208,13 +208,13 @@ namespace CreamInstaller
// //
// label2 // label2
// //
this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) this.progressLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.label2.Location = new System.Drawing.Point(12, 279); this.progressLabel.Location = new System.Drawing.Point(12, 279);
this.label2.Name = "label2"; this.progressLabel.Name = "label2";
this.label2.Size = new System.Drawing.Size(760, 15); this.progressLabel.Size = new System.Drawing.Size(760, 15);
this.label2.TabIndex = 10; this.progressLabel.TabIndex = 10;
this.label2.Text = "Loading . . ."; this.progressLabel.Text = "Loading . . .";
// //
// scanButton // scanButton
// //
@ -254,7 +254,7 @@ namespace CreamInstaller
this.Controls.Add(this.label1); this.Controls.Add(this.label1);
this.Controls.Add(this.cancelButton); this.Controls.Add(this.cancelButton);
this.Controls.Add(this.installButton); this.Controls.Add(this.installButton);
this.Controls.Add(this.label2); this.Controls.Add(this.progressLabel);
this.DoubleBuffered = true; this.DoubleBuffered = true;
this.MaximizeBox = false; this.MaximizeBox = false;
this.MinimizeBox = false; this.MinimizeBox = false;
@ -280,7 +280,7 @@ namespace CreamInstaller
private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label1;
private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.ProgressBar progressBar1; private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Label label2; private System.Windows.Forms.Label progressLabel;
private System.Windows.Forms.CheckBox allCheckBox; private System.Windows.Forms.CheckBox allCheckBox;
private Button scanButton; private Button scanButton;
private Label noneFoundLabel; private Label noneFoundLabel;

View file

@ -15,15 +15,13 @@ using System.Windows.Forms;
namespace CreamInstaller namespace CreamInstaller
{ {
public partial class SelectForm : Form internal partial class SelectForm : CustomForm
{ {
public SelectForm(IWin32Window owner) internal SelectForm(IWin32Window owner) : base(owner)
{ {
Owner = owner as Form;
InitializeComponent(); InitializeComponent();
Program.SelectForm = this;
Text = Program.ApplicationName; Text = Program.ApplicationName;
Icon = Properties.Resources.Icon; Program.SelectForm = this;
} }
private static List<string> GameLibraryDirectories private static List<string> GameLibraryDirectories
@ -194,7 +192,7 @@ namespace CreamInstaller
internal List<Task> RunningTasks = new(); internal List<Task> RunningTasks = new();
private void GetCreamApiApplicablePrograms(IProgress<int> progress) private async Task GetCreamApiApplicablePrograms(IProgress<int> progress)
{ {
int cur = 0; int cur = 0;
if (Program.Canceled) if (Program.Canceled)
@ -256,7 +254,7 @@ namespace CreamInstaller
continue; continue;
} }
} }
Task task = Task.Run(() => Task task = Task.Run(async () =>
{ {
if (Program.Canceled || !GetDllDirectoriesFromGameDirectory(directory, out List<string> dllDirectories)) if (Program.Canceled || !GetDllDirectoriesFromGameDirectory(directory, out List<string> dllDirectories))
{ {
@ -350,8 +348,7 @@ namespace CreamInstaller
{ {
return; return;
} }
await task;
task.Wait();
} }
if (Program.Canceled) if (Program.Canceled)
{ {
@ -411,8 +408,7 @@ namespace CreamInstaller
{ {
return; return;
} }
await task;
task.Wait();
} }
progress.Report(RunningTasks.Count); progress.Report(RunningTasks.Count);
} }
@ -433,7 +429,7 @@ namespace CreamInstaller
uninstallButton.Enabled = installButton.Enabled; uninstallButton.Enabled = installButton.Enabled;
selectionTreeView.Enabled = false; selectionTreeView.Enabled = false;
label2.Visible = true; progressLabel.Visible = true;
progressBar1.Visible = true; progressBar1.Visible = true;
progressBar1.Value = 0; progressBar1.Value = 0;
groupBox1.Size = new(groupBox1.Size.Width, groupBox1.Size.Height - 44); groupBox1.Size = new(groupBox1.Size.Width, groupBox1.Size.Height - 44);
@ -457,15 +453,15 @@ namespace CreamInstaller
int p = Math.Max(Math.Min((int)((float)(curProgress / (float)maxProgress) * 100), 100), 0); int p = Math.Max(Math.Min((int)((float)(curProgress / (float)maxProgress) * 100), 100), 0);
if (validating) if (validating)
{ {
label2.Text = $"Validating . . . {p}% ({curProgress}/{maxProgress})"; progressLabel.Text = $"Validating . . . {p}% ({curProgress}/{maxProgress})";
} }
else if (setup) else if (setup)
{ {
label2.Text = $"Setting up SteamCMD . . . {p}% ({curProgress}/{maxProgress})"; progressLabel.Text = $"Setting up SteamCMD . . . {p}% ({curProgress}/{maxProgress})";
} }
else else
{ {
label2.Text = $"Gathering and caching your applicable games and their DLCs . . . {p}% ({curProgress}/{maxProgress})"; progressLabel.Text = $"Gathering and caching your applicable games and their DLCs . . . {p}% ({curProgress}/{maxProgress})";
} }
progressBar1.Value = p; progressBar1.Value = p;
@ -476,7 +472,7 @@ namespace CreamInstaller
iProgress.Report(cur); iProgress.Report(cur);
if (!validating) if (!validating)
{ {
label2.Text = "Setting up SteamCMD . . . "; progressLabel.Text = "Setting up SteamCMD . . . ";
} }
if (!Directory.Exists(SteamCMD.DirectoryPath)) if (!Directory.Exists(SteamCMD.DirectoryPath))
@ -489,16 +485,16 @@ namespace CreamInstaller
watcher.Filter = "*"; watcher.Filter = "*";
watcher.IncludeSubdirectories = true; watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true; watcher.EnableRaisingEvents = true;
await Task.Run(() => SteamCMD.Setup()); await SteamCMD.Setup();
watcher.Dispose(); watcher.Dispose();
setup = false; setup = false;
if (!validating) if (!validating)
{ {
label2.Text = "Gathering and caching your applicable games and their DLCs . . . "; progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . ";
} }
await Task.Run(() => GetCreamApiApplicablePrograms(iProgress)); await GetCreamApiApplicablePrograms(iProgress);
ProgramSelection.ValidateAll(); ProgramSelection.ValidateAll();
TreeNodes.ForEach(node => TreeNodes.ForEach(node =>
{ {
@ -510,7 +506,7 @@ namespace CreamInstaller
progressBar1.Value = 100; progressBar1.Value = 100;
groupBox1.Size = new(groupBox1.Size.Width, groupBox1.Size.Height + 44); groupBox1.Size = new(groupBox1.Size.Width, groupBox1.Size.Height + 44);
label2.Visible = false; progressLabel.Visible = false;
progressBar1.Visible = false; progressBar1.Visible = false;
selectionTreeView.Enabled = ProgramSelection.All.Any(); selectionTreeView.Enabled = ProgramSelection.All.Any();
@ -525,7 +521,7 @@ namespace CreamInstaller
blockedGamesCheckBox.Enabled = true; blockedGamesCheckBox.Enabled = true;
blockProtectedHelpButton.Enabled = true; blockProtectedHelpButton.Enabled = true;
label2.Text = "Validating . . . "; progressLabel.Text = "Validating . . . ";
if (!validating && !Program.Canceled) if (!validating && !Program.Canceled)
{ {
OnLoad(true); OnLoad(true);
@ -597,9 +593,9 @@ namespace CreamInstaller
selectionTreeView.NodeMouseClick += (sender, e) => selectionTreeView.NodeMouseClick += (sender, e) =>
{ {
TreeNode node = e.Node; TreeNode node = e.Node;
string appId = node.Name;
if (e.Button == MouseButtons.Right && node.Bounds.Contains(e.Location)) if (e.Button == MouseButtons.Right && node.Bounds.Contains(e.Location))
{ {
string appId = node.Name;
if (appId != "0") if (appId != "0")
{ {
Process.Start(new ProcessStartInfo Process.Start(new ProcessStartInfo
@ -718,7 +714,11 @@ namespace CreamInstaller
private void OnCancel(object sender, EventArgs e) private void OnCancel(object sender, EventArgs e)
{ {
Program.Cleanup(); progressLabel.Text = "Cancelling . . . ";
Task.Run(async () =>
{
await Program.Cleanup();
});
} }
private void OnAllCheckBoxChanged(object sender, EventArgs e) private void OnAllCheckBoxChanged(object sender, EventArgs e)

View file

@ -5,23 +5,24 @@ using System.Drawing;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
namespace CreamInstaller namespace CreamInstaller
{ {
public static class Program internal static class Program
{ {
public static readonly string ApplicationName = Application.CompanyName + " v" + Application.ProductVersion + ": " + Application.ProductName; internal static readonly string ApplicationName = Application.CompanyName + " v" + Application.ProductVersion + ": " + Application.ProductName;
public static readonly Assembly EntryAssembly = Assembly.GetEntryAssembly(); internal static readonly Assembly EntryAssembly = Assembly.GetEntryAssembly();
public static readonly Process CurrentProcess = Process.GetCurrentProcess(); internal static readonly Process CurrentProcess = Process.GetCurrentProcess();
public static readonly string CurrentProcessFilePath = CurrentProcess.MainModule.FileName; internal static readonly string CurrentProcessFilePath = CurrentProcess.MainModule.FileName;
public static readonly string CurrentProcessDirectory = CurrentProcessFilePath.Substring(0, CurrentProcessFilePath.LastIndexOf("\\")); internal static readonly string CurrentProcessDirectory = CurrentProcessFilePath.Substring(0, CurrentProcessFilePath.LastIndexOf("\\"));
public static readonly string BackupFileExtension = ".creaminstaller.backup"; internal static readonly string BackupFileExtension = ".creaminstaller.backup";
public static bool BlockProtectedGames = true; internal static bool BlockProtectedGames = true;
public static readonly string[] ProtectedGameNames = { "PAYDAY 2", "Call to Arms" }; // non-functioning CreamAPI or DLL detections internal static readonly string[] ProtectedGameNames = { "PAYDAY 2", "Call to Arms" }; // non-functioning CreamAPI or DLL detections
public static readonly string[] ProtectedGameDirectories = { @"\EasyAntiCheat", @"\BattlEye" }; // DLL detections internal static readonly string[] ProtectedGameDirectories = { @"\EasyAntiCheat", @"\BattlEye" }; // DLL detections
public static readonly string[] ProtectedGameDirectoryExceptions = { "Arma 3" }; // Arma 3's BattlEye doesn't detect DLL changes? internal static readonly string[] ProtectedGameDirectoryExceptions = { "Arma 3" }; // Arma 3's BattlEye doesn't detect DLL changes?
[STAThread] [STAThread]
private static void Main() private static void Main()
@ -52,7 +53,7 @@ namespace CreamInstaller
mutex.Close(); mutex.Close();
} }
public static bool IsProgramRunningDialog(Form form, ProgramSelection selection) internal static bool IsProgramRunningDialog(Form form, ProgramSelection selection)
{ {
if (selection.AreSteamApiDllsLocked) if (selection.AreSteamApiDllsLocked)
{ {
@ -71,7 +72,7 @@ namespace CreamInstaller
return false; return false;
} }
public static bool IsFilePathLocked(this string filePath) internal static bool IsFilePathLocked(this string filePath)
{ {
try { File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None).Close(); } try { File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None).Close(); }
catch (FileNotFoundException) { return false; } catch (FileNotFoundException) { return false; }
@ -79,22 +80,22 @@ namespace CreamInstaller
return false; return false;
} }
public static SelectForm SelectForm; internal static SelectForm SelectForm;
public static InstallForm InstallForm; internal static InstallForm InstallForm;
public static List<ProgramSelection> ProgramSelections = new(); internal static List<ProgramSelection> ProgramSelections = new();
public static bool Canceled = false; internal static bool Canceled = false;
public static void Cleanup(bool cancel = true) internal static async Task Cleanup(bool cancel = true)
{ {
Canceled = cancel; Canceled = cancel;
SteamCMD.Kill(); await SteamCMD.Kill();
} }
private static void OnApplicationExit(object s, EventArgs e) private static void OnApplicationExit(object s, EventArgs e)
{ {
Cleanup(); Cleanup().Wait();
} }
internal static void InheritLocation(this Form form, Form fromForm) internal static void InheritLocation(this Form form, Form fromForm)

View file

@ -2,9 +2,9 @@
namespace CreamInstaller namespace CreamInstaller
{ {
public static class FileResourceExtensions internal static class FileResourceExtensions
{ {
public 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); using FileStream file = new(filePath, FileMode.Create, FileAccess.Write);
file.Write(resource); file.Write(resource);