- Minor refactoring
- Updated HtmlAgilityPack dependency
- Replaced node right click with a beefy context menu with ICONS :O
- Small improvements to selection node display
- Fixed selections sometimes being prematurely removed
- Enabled selections will now be saved and restored after installation
This commit is contained in:
pointfeev 2022-01-23 03:13:45 -05:00
parent af556628de
commit f151a1ba2a
7 changed files with 195 additions and 107 deletions

View file

@ -1,7 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Gameloop.Vdf.Linq; using Gameloop.Vdf.Linq;
@ -12,9 +14,42 @@ namespace CreamInstaller
internal bool Enabled = false; internal bool Enabled = false;
internal bool Usable = true; internal bool Usable = true;
internal string Name; internal int SteamAppId = 0;
internal string Name = "Program";
internal Image Icon;
private string iconPath;
internal string IconPath
{
get => iconPath;
set
{
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 string RootDirectory;
internal int SteamAppId;
internal List<string> SteamApiDllDirectories; internal List<string> SteamApiDllDirectories;
internal VProperty AppInfo = null; internal VProperty AppInfo = null;
@ -31,10 +66,7 @@ namespace CreamInstaller
{ {
string api = directory + @"\steam_api.dll"; string api = directory + @"\steam_api.dll";
string api64 = directory + @"\steam_api64.dll"; string api64 = directory + @"\steam_api64.dll";
if (api.IsFilePathLocked() || api64.IsFilePathLocked()) if (api.IsFilePathLocked() || api64.IsFilePathLocked()) return true;
{
return true;
}
} }
return false; return false;
} }
@ -42,43 +74,25 @@ namespace CreamInstaller
private void Toggle(KeyValuePair<int, string> dlcApp, bool enabled) private void Toggle(KeyValuePair<int, string> dlcApp, bool enabled)
{ {
if (enabled) if (enabled) SelectedSteamDlc[dlcApp.Key] = dlcApp.Value;
{ else SelectedSteamDlc.Remove(dlcApp.Key);
SelectedSteamDlc[dlcApp.Key] = dlcApp.Value;
}
else
{
SelectedSteamDlc.Remove(dlcApp.Key);
}
} }
internal 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)
{
if (dlcApp.Key == dlcAppId) if (dlcApp.Key == dlcAppId)
{ {
Toggle(dlcApp, enabled); Toggle(dlcApp, enabled);
break; break;
} }
}
Enabled = SelectedSteamDlc.Any(); Enabled = SelectedSteamDlc.Any();
} }
internal void ToggleAllDlc(bool enabled) internal void ToggleAllDlc(bool enabled)
{ {
if (!enabled) if (!enabled) SelectedSteamDlc.Clear();
{ else foreach (KeyValuePair<int, string> dlcApp in AllSteamDlc) Toggle(dlcApp, enabled);
SelectedSteamDlc.Clear();
}
else
{
foreach (KeyValuePair<int, string> dlcApp in AllSteamDlc)
{
Toggle(dlcApp, enabled);
}
}
Enabled = SelectedSteamDlc.Any(); Enabled = SelectedSteamDlc.Any();
} }
@ -86,36 +100,37 @@ namespace CreamInstaller
internal void Validate() internal void Validate()
{ {
SteamApiDllDirectories.RemoveAll(directory => !Directory.Exists(directory)); if (Program.BlockProtectedGames && Program.IsGameBlocked(Name, RootDirectory))
if (!Directory.Exists(RootDirectory) || !SteamApiDllDirectories.Any())
{ {
All.Remove(this); 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() => All.ForEach(selection => selection.Validate()); internal static void ValidateAll() => AllSafe.ForEach(selection => selection.Validate());
internal static List<ProgramSelection> All => Program.ProgramSelections; internal static List<ProgramSelection> All => Program.ProgramSelections;
internal static List<ProgramSelection> AllSafe => All.FindAll(s => s.Usable); internal static List<ProgramSelection> AllSafe => All.ToList();
internal static List<ProgramSelection> AllSafeEnabled => AllSafe.FindAll(s => s.Enabled); internal static List<ProgramSelection> AllUsable => All.FindAll(s => s.Usable);
internal static ProgramSelection FromAppId(int appId) => AllSafe.Find(s => s.SteamAppId == appId); 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) internal static KeyValuePair<int, string>? GetDlcFromAppId(int appId)
{ {
foreach (ProgramSelection selection in AllSafe) foreach (ProgramSelection selection in AllUsable)
{
foreach (KeyValuePair<int, string> app in selection.AllSteamDlc) foreach (KeyValuePair<int, string> app in selection.AllSteamDlc)
{ if (app.Key == appId) return app;
if (app.Key == appId)
{
return app;
}
}
}
return null; return null;
} }
} }

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.2.0</Version> <Version>2.2.3.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>
@ -42,7 +42,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Gameloop.Vdf" Version="0.6.1" /> <PackageReference Include="Gameloop.Vdf" Version="0.6.1" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.38" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.40" />
<PackageReference Include="Onova" Version="2.6.2" /> <PackageReference Include="Onova" Version="2.6.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -150,9 +150,10 @@ namespace CreamInstaller
private async Task Operate() private async Task Operate()
{ {
List<ProgramSelection> programSelections = ProgramSelection.AllSafeEnabled; List<ProgramSelection> programSelections = ProgramSelection.AllUsableEnabled;
OperationsCount = programSelections.Count; OperationsCount = programSelections.Count;
CompleteOperationsCount = 0; CompleteOperationsCount = 0;
List<ProgramSelection> disabledSelections = new();
foreach (ProgramSelection selection in programSelections) foreach (ProgramSelection selection in programSelections)
{ {
if (!Program.IsProgramRunningDialog(this, selection)) throw new OperationCanceledException(); if (!Program.IsProgramRunningDialog(this, selection)) throw new OperationCanceledException();
@ -161,6 +162,7 @@ namespace CreamInstaller
await OperateFor(selection); await OperateFor(selection);
await UpdateUser($"Operation succeeded for {selection.Name}.", InstallationLog.Success); await UpdateUser($"Operation succeeded for {selection.Name}.", InstallationLog.Success);
selection.Enabled = false; selection.Enabled = false;
disabledSelections.Add(selection);
} }
catch (Exception exception) catch (Exception exception)
{ {
@ -169,13 +171,14 @@ namespace CreamInstaller
++CompleteOperationsCount; ++CompleteOperationsCount;
} }
await Program.Cleanup(); await Program.Cleanup();
List<ProgramSelection> FailedSelections = ProgramSelection.AllSafeEnabled; List<ProgramSelection> FailedSelections = ProgramSelection.AllUsableEnabled;
if (FailedSelections.Any()) if (FailedSelections.Any())
if (FailedSelections.Count == 1) throw new CustomMessageException($"Operation failed for {FailedSelections.First().Name}."); if (FailedSelections.Count == 1) throw new CustomMessageException($"Operation failed for {FailedSelections.First().Name}.");
else throw new CustomMessageException($"Operation failed for {FailedSelections.Count} programs."); else throw new CustomMessageException($"Operation failed for {FailedSelections.Count} programs.");
foreach (ProgramSelection selection in disabledSelections) selection.Enabled = true;
} }
private readonly int ProgramCount = ProgramSelection.AllSafeEnabled.Count; private readonly int ProgramCount = ProgramSelection.AllUsableEnabled.Count;
private async void Start() private async void Start()
{ {

View file

@ -31,6 +31,7 @@ namespace CreamInstaller
/// </summary> /// </summary>
private void InitializeComponent() private void InitializeComponent()
{ {
this.components = new System.ComponentModel.Container();
this.installButton = new System.Windows.Forms.Button(); this.installButton = new System.Windows.Forms.Button();
this.cancelButton = new System.Windows.Forms.Button(); this.cancelButton = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label();
@ -46,6 +47,7 @@ namespace CreamInstaller
this.progressLabel = 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.nodeContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
this.groupBox1.SuspendLayout(); this.groupBox1.SuspendLayout();
this.flowLayoutPanel1.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout();
this.flowLayoutPanel2.SuspendLayout(); this.flowLayoutPanel2.SuspendLayout();
@ -166,7 +168,6 @@ namespace CreamInstaller
this.selectionTreeView.CheckBoxes = true; this.selectionTreeView.CheckBoxes = true;
this.selectionTreeView.Enabled = false; this.selectionTreeView.Enabled = false;
this.selectionTreeView.FullRowSelect = true; this.selectionTreeView.FullRowSelect = true;
this.selectionTreeView.HotTracking = true;
this.selectionTreeView.Location = new System.Drawing.Point(6, 22); this.selectionTreeView.Location = new System.Drawing.Point(6, 22);
this.selectionTreeView.Name = "selectionTreeView"; this.selectionTreeView.Name = "selectionTreeView";
this.selectionTreeView.Size = new System.Drawing.Size(548, 280); this.selectionTreeView.Size = new System.Drawing.Size(548, 280);
@ -206,12 +207,12 @@ namespace CreamInstaller
this.progressBar1.Size = new System.Drawing.Size(560, 23); this.progressBar1.Size = new System.Drawing.Size(560, 23);
this.progressBar1.TabIndex = 9; this.progressBar1.TabIndex = 9;
// //
// label2 // progressLabel
// //
this.progressLabel.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.progressLabel.Location = new System.Drawing.Point(12, 279); this.progressLabel.Location = new System.Drawing.Point(12, 279);
this.progressLabel.Name = "label2"; this.progressLabel.Name = "progressLabel";
this.progressLabel.Size = new System.Drawing.Size(760, 15); this.progressLabel.Size = new System.Drawing.Size(760, 15);
this.progressLabel.TabIndex = 10; this.progressLabel.TabIndex = 10;
this.progressLabel.Text = "Loading . . ."; this.progressLabel.Text = "Loading . . .";
@ -242,6 +243,11 @@ namespace CreamInstaller
this.uninstallButton.UseVisualStyleBackColor = true; this.uninstallButton.UseVisualStyleBackColor = true;
this.uninstallButton.Click += new System.EventHandler(this.OnUninstall); this.uninstallButton.Click += new System.EventHandler(this.OnUninstall);
// //
// nodeContextMenu
//
this.nodeContextMenu.Name = "nodeContextMenu";
this.nodeContextMenu.Size = new System.Drawing.Size(61, 4);
//
// SelectForm // SelectForm
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
@ -290,6 +296,7 @@ namespace CreamInstaller
private FlowLayoutPanel flowLayoutPanel1; private FlowLayoutPanel flowLayoutPanel1;
private FlowLayoutPanel flowLayoutPanel2; private FlowLayoutPanel flowLayoutPanel2;
private Button uninstallButton; private Button uninstallButton;
private ContextMenuStrip nodeContextMenu;
} }
} }

View file

@ -2,7 +2,6 @@
using System.Collections; using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -157,24 +156,9 @@ namespace CreamInstaller
int buildId = program.Item4; int buildId = program.Item4;
string directory = program.Item5; string directory = program.Item5;
ProgramSelection selection = ProgramSelection.FromAppId(appId); ProgramSelection selection = ProgramSelection.FromAppId(appId);
if (selection is not null) selection.Validate();
if (Program.Canceled) return; if (Program.Canceled) return;
if (Program.BlockProtectedGames) if (Program.BlockProtectedGames && Program.IsGameBlocked(name, directory)) continue;
{
bool blockedGame = Program.ProtectedGameNames.Contains(name);
if (!Program.ProtectedGameDirectoryExceptions.Contains(name))
foreach (string path in Program.ProtectedGameDirectories)
if (Directory.Exists(directory + path))
blockedGame = true;
if (blockedGame)
{
if (selection is not null)
{
selection.Enabled = false;
selection.Usable = false;
}
continue;
}
}
RunningTasks.Add(Task.Run(async () => RunningTasks.Add(Task.Run(async () =>
{ {
if (Program.Canceled) return; if (Program.Canceled) return;
@ -215,11 +199,20 @@ namespace CreamInstaller
selection ??= new(); selection ??= new();
selection.Usable = true; selection.Usable = true;
selection.SteamAppId = appId;
selection.Name = name; selection.Name = name;
selection.RootDirectory = directory; selection.RootDirectory = directory;
selection.SteamAppId = appId;
selection.SteamApiDllDirectories = dllDirectories; selection.SteamApiDllDirectories = dllDirectories;
selection.AppInfo = appInfo; selection.AppInfo = appInfo;
if (selection.Icon is null)
{
if (appId == 0) selection.Icon = Program.GetFileIconImage(directory + @"\launcher\bootstrapper-v2.exe");
else
{
selection.IconStaticID = appInfo?.Value?["common"]?["icon"]?.ToString();
selection.ClientIconStaticID = appInfo?.Value?["common"]?["clienticon"]?.ToString();
}
}
if (allCheckBox.Checked) selection.Enabled = true; if (allCheckBox.Checked) selection.Enabled = true;
foreach (Task task in dlcTasks) foreach (Task task in dlcTasks)
@ -270,7 +263,7 @@ namespace CreamInstaller
progress.Report(RunningTasks.Count); progress.Report(RunningTasks.Count);
} }
private async void OnLoad(bool validating = false) private async void OnLoad()
{ {
retry: retry:
try try
@ -300,16 +293,15 @@ namespace CreamInstaller
if (_progress < 0) maxProgress = -_progress; if (_progress < 0) maxProgress = -_progress;
else curProgress = _progress; else curProgress = _progress;
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) progressLabel.Text = $"Validating . . . {p}% ({curProgress}/{maxProgress})"; progressLabel.Text = setup ? $"Setting up SteamCMD . . . {p}% ({curProgress}/{maxProgress})"
else if (setup) progressLabel.Text = $"Setting up SteamCMD . . . {p}% ({curProgress}/{maxProgress})"; : $"Gathering and caching your applicable games and their DLCs . . . {p}% ({curProgress}/{maxProgress})";
else progressLabel.Text = $"Gathering and caching your applicable games and their DLCs . . . {p}% ({curProgress}/{maxProgress})";
progressBar1.Value = p; progressBar1.Value = p;
}; };
iProgress.Report(-1660); // not exact, number varies iProgress.Report(-1660); // not exact, number varies
int cur = 0; int cur = 0;
iProgress.Report(cur); iProgress.Report(cur);
if (!validating) progressLabel.Text = "Setting up SteamCMD . . . "; progressLabel.Text = "Setting up SteamCMD . . . ";
if (!Directory.Exists(SteamCMD.DirectoryPath)) Directory.CreateDirectory(SteamCMD.DirectoryPath); if (!Directory.Exists(SteamCMD.DirectoryPath)) Directory.CreateDirectory(SteamCMD.DirectoryPath);
FileSystemWatcher watcher = new(SteamCMD.DirectoryPath); FileSystemWatcher watcher = new(SteamCMD.DirectoryPath);
@ -321,14 +313,13 @@ namespace CreamInstaller
watcher.Dispose(); watcher.Dispose();
setup = false; setup = false;
if (!validating) progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . "; progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . ";
await GetCreamApiApplicablePrograms(iProgress);
ProgramSelection.ValidateAll(); ProgramSelection.ValidateAll();
TreeNodes.ForEach(node => TreeNodes.ForEach(node =>
{ {
if (node.Parent is null && ProgramSelection.FromAppId(int.Parse(node.Name)) is null) node.Remove(); if (!int.TryParse(node.Name, out int appId) || node.Parent is null && ProgramSelection.FromAppId(appId) is null) node.Remove();
}); });
await GetCreamApiApplicablePrograms(iProgress);
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);
@ -337,15 +328,12 @@ namespace CreamInstaller
selectionTreeView.Enabled = ProgramSelection.All.Any(); selectionTreeView.Enabled = ProgramSelection.All.Any();
allCheckBox.Enabled = selectionTreeView.Enabled; allCheckBox.Enabled = selectionTreeView.Enabled;
noneFoundLabel.Visible = !selectionTreeView.Enabled; noneFoundLabel.Visible = !selectionTreeView.Enabled;
installButton.Enabled = ProgramSelection.AllSafeEnabled.Any(); installButton.Enabled = ProgramSelection.AllUsableEnabled.Any();
uninstallButton.Enabled = installButton.Enabled; uninstallButton.Enabled = installButton.Enabled;
cancelButton.Enabled = false; cancelButton.Enabled = false;
scanButton.Enabled = true; scanButton.Enabled = true;
blockedGamesCheckBox.Enabled = true; blockedGamesCheckBox.Enabled = true;
blockProtectedHelpButton.Enabled = true; blockProtectedHelpButton.Enabled = true;
progressLabel.Text = "Validating . . . ";
//if (!validating && !Program.Canceled) OnLoad(true);
} }
catch (Exception e) catch (Exception e)
{ {
@ -383,7 +371,7 @@ namespace CreamInstaller
allCheckBox.CheckedChanged += OnAllCheckBoxChanged; allCheckBox.CheckedChanged += OnAllCheckBoxChanged;
} }
} }
installButton.Enabled = ProgramSelection.AllSafeEnabled.Any(); installButton.Enabled = ProgramSelection.AllUsableEnabled.Any();
uninstallButton.Enabled = installButton.Enabled; uninstallButton.Enabled = installButton.Enabled;
} }
@ -391,9 +379,9 @@ namespace CreamInstaller
{ {
public int Compare(object a, object b) public int Compare(object a, object b)
{ {
TreeNode A = a as TreeNode; if (!int.TryParse((a as TreeNode).Name, out int A)) return 1;
TreeNode B = b as TreeNode; if (!int.TryParse((b as TreeNode).Name, out int B)) return 0;
return int.Parse(A.Name) > int.Parse(B.Name) ? 1 : 0; return A > B ? 1 : 0;
} }
} }
@ -401,16 +389,54 @@ namespace CreamInstaller
{ {
selectionTreeView.TreeViewNodeSorter = new TreeNodeSorter(); selectionTreeView.TreeViewNodeSorter = new TreeNodeSorter();
selectionTreeView.AfterCheck += OnTreeViewNodeCheckedChanged; selectionTreeView.AfterCheck += OnTreeViewNodeCheckedChanged;
Dictionary<string, Image> images = new();
Task.Run(async () =>
{
images["File Explorer"] = Program.GetFileExplorerImage();
images["SteamDB"] = await Program.GetImageFromUrl("https://steamdb.info/favicon.ico");
images["Steam Store"] = await Program.GetImageFromUrl("https://store.steampowered.com/favicon.ico");
images["Steam Community"] = await Program.GetImageFromUrl("https://steamcommunity.com/favicon.ico");
});
Image Image(string identifier) => images.GetValueOrDefault(identifier, null);
selectionTreeView.NodeMouseClick += (sender, e) => selectionTreeView.NodeMouseClick += (sender, e) =>
{ {
TreeNode node = e.Node; TreeNode node = e.Node;
string appId = node.Name; TreeNode parentNode = node.Parent;
if (e.Button == MouseButtons.Right && node.Bounds.Contains(e.Location) && appId != "0") if (!int.TryParse(node.Name, out int appId)) return;
Process.Start(new ProcessStartInfo ProgramSelection selection = ProgramSelection.FromAppId(appId);
if (e.Button == MouseButtons.Right && node.Bounds.Contains(e.Location))
{
selectionTreeView.SelectedNode = node;
nodeContextMenu.Items.Clear();
if (selection is not null)
{ {
FileName = "https://steamdb.info/app/" + appId, nodeContextMenu.Items.Add(new ToolStripMenuItem(selection.Name, selection.Icon));
UseShellExecute = true nodeContextMenu.Items.Add(new ToolStripSeparator());
}); nodeContextMenu.Items.Add(new ToolStripMenuItem("Open Root Directory", Image("File Explorer"),
new EventHandler((sender, e) => Program.OpenDirectoryInFileExplorer(selection.RootDirectory))));
for (int i = 0; i < selection.SteamApiDllDirectories.Count; i++)
{
string directory = selection.SteamApiDllDirectories[i];
nodeContextMenu.Items.Add(new ToolStripMenuItem($"Open Steamworks Directory ({i + 1})", Image("File Explorer"),
new EventHandler((sender, e) => Program.OpenDirectoryInFileExplorer(directory))));
}
}
else
{
nodeContextMenu.Items.Add(new ToolStripMenuItem(node.Text));
nodeContextMenu.Items.Add(new ToolStripSeparator());
}
if (appId != 0)
{
nodeContextMenu.Items.Add(new ToolStripMenuItem("Open SteamDB", Image("SteamDB"),
new EventHandler((sender, e) => Program.OpenUrlInInternetBrowser("https://steamdb.info/app/" + appId))));
nodeContextMenu.Items.Add(new ToolStripMenuItem("Open Steam Store", Image("Steam Store"),
new EventHandler((sender, e) => Program.OpenUrlInInternetBrowser("https://store.steampowered.com/app/" + appId))));
if (selection is not null) nodeContextMenu.Items.Add(new ToolStripMenuItem("Open Steam Community", selection.ClientIcon ?? Image("Steam Community"),
new EventHandler((sender, e) => Program.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + appId))));
}
nodeContextMenu.Show(selectionTreeView, e.Location);
}
}; };
OnLoad(); OnLoad();
} }
@ -421,14 +447,14 @@ namespace CreamInstaller
if (paradoxLauncher is not null) if (paradoxLauncher is not null)
{ {
paradoxLauncher.ExtraSteamAppIdDlc.Clear(); paradoxLauncher.ExtraSteamAppIdDlc.Clear();
foreach (ProgramSelection selection in ProgramSelection.AllSafeEnabled) foreach (ProgramSelection selection in ProgramSelection.AllUsableEnabled)
{ {
if (selection.Name == paradoxLauncher.Name) continue; if (selection.Name == paradoxLauncher.Name) continue;
if (selection.AppInfo.Value["extended"]["publisher"].ToString() != "Paradox Interactive") continue; if (selection.AppInfo.Value["extended"]["publisher"].ToString() != "Paradox Interactive") continue;
paradoxLauncher.ExtraSteamAppIdDlc.Add(new(selection.SteamAppId, selection.Name, selection.SelectedSteamDlc)); paradoxLauncher.ExtraSteamAppIdDlc.Add(new(selection.SteamAppId, selection.Name, selection.SelectedSteamDlc));
} }
if (!paradoxLauncher.ExtraSteamAppIdDlc.Any()) if (!paradoxLauncher.ExtraSteamAppIdDlc.Any())
foreach (ProgramSelection selection in ProgramSelection.AllSafe) foreach (ProgramSelection selection in ProgramSelection.AllUsable)
{ {
if (selection.Name == paradoxLauncher.Name) continue; if (selection.Name == paradoxLauncher.Name) continue;
if (selection.AppInfo.Value["extended"]["publisher"].ToString() != "Paradox Interactive") continue; if (selection.AppInfo.Value["extended"]["publisher"].ToString() != "Paradox Interactive") continue;
@ -445,12 +471,10 @@ namespace CreamInstaller
PopulateParadoxLauncherDlc(paradoxLauncher); PopulateParadoxLauncherDlc(paradoxLauncher);
if (!paradoxLauncher.ExtraSteamAppIdDlc.Any()) if (!paradoxLauncher.ExtraSteamAppIdDlc.Any())
{ {
if (new DialogForm(form).Show(Program.ApplicationName, SystemIcons.Warning, return new DialogForm(form).Show(Program.ApplicationName, SystemIcons.Warning,
$"WARNING: There are no installed games with DLC that can be added to the Paradox Launcher!" + $"WARNING: There are no installed games with DLC that can be added to the Paradox Launcher!" +
"\n\nInstalling CreamAPI for the Paradox Launcher is pointless, since no DLC will be added to the configuration!", "\n\nInstalling CreamAPI for the Paradox Launcher is pointless, since no DLC will be added to the configuration!",
"Ignore", "Cancel") == DialogResult.OK) "Ignore", "Cancel") != DialogResult.OK;
return false;
return true;
} }
} }
return false; return false;
@ -460,7 +484,7 @@ namespace CreamInstaller
{ {
if (ProgramSelection.All.Any()) if (ProgramSelection.All.Any())
{ {
foreach (ProgramSelection selection in ProgramSelection.AllSafeEnabled) foreach (ProgramSelection selection in ProgramSelection.AllUsableEnabled)
if (!Program.IsProgramRunningDialog(this, selection)) return; if (!Program.IsProgramRunningDialog(this, selection)) return;
if (ParadoxLauncherDlcDialog(this)) return; if (ParadoxLauncherDlcDialog(this)) return;
Hide(); Hide();
@ -483,10 +507,7 @@ namespace CreamInstaller
private void OnCancel(object sender, EventArgs e) private void OnCancel(object sender, EventArgs e)
{ {
progressLabel.Text = "Cancelling . . . "; progressLabel.Text = "Cancelling . . . ";
Task.Run(async () => Task.Run(async () => await Program.Cleanup());
{
await Program.Cleanup();
});
} }
private void OnAllCheckBoxChanged(object sender, EventArgs e) private void OnAllCheckBoxChanged(object sender, EventArgs e)

View file

@ -57,4 +57,7 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<metadata name="nodeContextMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root> </root>

View file

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -24,6 +26,15 @@ namespace CreamInstaller
internal static readonly string[] ProtectedGameDirectories = { @"\EasyAntiCheat", @"\BattlEye" }; // 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 readonly string[] ProtectedGameDirectoryExceptions = { "Arma 3" }; // Arma 3's BattlEye doesn't detect DLL changes?
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] [STAThread]
private static void Main() private static void Main()
{ {
@ -49,6 +60,34 @@ namespace CreamInstaller
mutex.Close(); 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) internal static bool IsProgramRunningDialog(Form form, ProgramSelection selection)
{ {
if (selection.AreSteamApiDllsLocked) if (selection.AreSteamApiDllsLocked)