- Significant refactoring & optimizations
- Updated to Koaloader v3.0.1
This commit is contained in:
pointfeev 2023-01-13 15:15:13 -05:00
parent 51db005c34
commit 45eaed35dd
80 changed files with 746 additions and 691 deletions

View file

@ -9,16 +9,16 @@ using CreamInstaller.Utility;
namespace CreamInstaller.Components; namespace CreamInstaller.Components;
internal class ContextMenuItem : ToolStripMenuItem internal sealed class ContextMenuItem : ToolStripMenuItem
{ {
private static readonly ConcurrentDictionary<string, Image> images = new(); private static readonly ConcurrentDictionary<string, Image> Images = new();
private readonly EventHandler OnClickEvent; private readonly EventHandler onClickEvent;
internal ContextMenuItem(string text, EventHandler onClick = null) internal ContextMenuItem(string text, EventHandler onClick = null)
{ {
Text = text; Text = text;
OnClickEvent = onClick; onClickEvent = onClick;
} }
internal ContextMenuItem(string text, string imageIdentifier, EventHandler onClick = null) : this(text, onClick) internal ContextMenuItem(string text, string imageIdentifier, EventHandler onClick = null) : this(text, onClick)
@ -29,16 +29,22 @@ internal class ContextMenuItem : ToolStripMenuItem
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, string imageIdentifierFallback, EventHandler onClick = null) : internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, string imageIdentifierFallback, EventHandler onClick = null) :
this(text, onClick) this(text, onClick)
=> _ = TryImageIdentifierInfo(this, imageIdentifierInfo, async () => await TryImageIdentifier(this, imageIdentifierFallback)); {
async void OnFail() => await TryImageIdentifier(this, imageIdentifierFallback);
_ = TryImageIdentifierInfo(this, imageIdentifierInfo, OnFail);
}
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, (string id, string iconUrl) imageIdentifierInfoFallback, internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, (string id, string iconUrl) imageIdentifierInfoFallback,
EventHandler onClick = null) : this(text, onClick) EventHandler onClick = null) : this(text, onClick)
=> _ = TryImageIdentifierInfo(this, imageIdentifierInfo, async () => await TryImageIdentifierInfo(this, imageIdentifierInfoFallback)); {
async void OnFail() => await TryImageIdentifierInfo(this, imageIdentifierInfoFallback);
_ = TryImageIdentifierInfo(this, imageIdentifierInfo, OnFail);
}
private static async Task TryImageIdentifier(ContextMenuItem item, string imageIdentifier) private static async Task TryImageIdentifier(ContextMenuItem item, string imageIdentifier)
=> await Task.Run(async () => => await Task.Run(async () =>
{ {
if (images.TryGetValue(imageIdentifier, out Image image) && image is not null) if (Images.TryGetValue(imageIdentifier, out Image image) && image is not null)
item.Image = image; item.Image = image;
else else
{ {
@ -84,7 +90,7 @@ internal class ContextMenuItem : ToolStripMenuItem
} }
if (image is not null) if (image is not null)
{ {
images[imageIdentifier] = image; Images[imageIdentifier] = image;
item.Image = image; item.Image = image;
} }
} }
@ -95,26 +101,24 @@ internal class ContextMenuItem : ToolStripMenuItem
{ {
(string id, string iconUrl) = imageIdentifierInfo; (string id, string iconUrl) = imageIdentifierInfo;
string imageIdentifier = "Icon_" + id; string imageIdentifier = "Icon_" + id;
if (images.TryGetValue(imageIdentifier, out Image image) && image is not null) if (Images.TryGetValue(imageIdentifier, out Image image) && image is not null)
item.Image = image; item.Image = image;
else else
{ {
image = await HttpClientManager.GetImageFromUrl(iconUrl); image = await HttpClientManager.GetImageFromUrl(iconUrl);
if (image is not null) if (image is not null)
{ {
images[imageIdentifier] = image; Images[imageIdentifier] = image;
item.Image = image; item.Image = image;
} }
else if (onFail is not null) else
onFail(); onFail?.Invoke();
} }
}); });
protected override void OnClick(EventArgs e) protected override void OnClick(EventArgs e)
{ {
base.OnClick(e); base.OnClick(e);
if (OnClickEvent is null) onClickEvent?.Invoke(this, e);
return;
OnClickEvent.Invoke(this, e);
} }
} }

View file

@ -124,4 +124,4 @@ internal class CustomForm : Form
Clipboard.SetImage(bitmap); Clipboard.SetImage(bitmap);
e.Handled = true; e.Handled = true;
} }
} }

View file

@ -11,9 +11,9 @@ using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Components; namespace CreamInstaller.Components;
internal class CustomTreeView : TreeView internal sealed class CustomTreeView : TreeView
{ {
private const string koaloaderToggleString = "Koaloader"; private const string KoaloaderToggleString = "Koaloader";
private readonly Dictionary<ProgramSelection, Rectangle> checkBoxBounds = new(); private readonly Dictionary<ProgramSelection, Rectangle> checkBoxBounds = new();
private readonly Dictionary<ProgramSelection, Rectangle> comboBoxBounds = new(); private readonly Dictionary<ProgramSelection, Rectangle> comboBoxBounds = new();
@ -34,7 +34,7 @@ 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 = nint.Zero;
else else
base.WndProc(ref m); base.WndProc(ref m);
form = FindForm(); form = FindForm();
@ -54,11 +54,11 @@ internal class CustomTreeView : TreeView
{ {
e.DrawDefault = true; e.DrawDefault = true;
TreeNode node = e.Node; TreeNode node = e.Node;
if (!node.IsVisible) if (node is not { IsVisible: true })
return; return;
bool highlighted = (e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected && Focused; bool highlighted = (e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected && Focused;
Graphics graphics = e.Graphics; Graphics graphics = e.Graphics;
backBrush ??= new SolidBrush(BackColor); backBrush ??= new(BackColor);
Font font = node.NodeFont ?? Font; Font font = node.NodeFont ?? Font;
Brush brush = highlighted ? SystemBrushes.Highlight : backBrush; Brush brush = highlighted ? SystemBrushes.Highlight : backBrush;
string text; // = e.Node.Text; string text; // = e.Node.Text;
@ -87,9 +87,9 @@ internal class CustomTreeView : TreeView
text = platform.ToString(); text = platform.ToString();
size = TextRenderer.MeasureText(graphics, text, font); size = TextRenderer.MeasureText(graphics, text, font);
bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width }; bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width };
selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 }); selectionBounds = new(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
graphics.FillRectangle(brush, bounds); graphics.FillRectangle(brush, bounds);
point = new Point(bounds.Location.X - 1, bounds.Location.Y + 1); point = new(bounds.Location.X - 1, bounds.Location.Y + 1);
TextRenderer.DrawText(graphics, text, font, point, color, TextFormatFlags.Default); TextRenderer.DrawText(graphics, text, font, point, color, TextFormatFlags.Default);
if (platform is not Platform.Paradox) if (platform is not Platform.Paradox)
{ {
@ -100,11 +100,11 @@ internal class CustomTreeView : TreeView
: ColorTranslator.FromHtml("#69AAAA"); : ColorTranslator.FromHtml("#69AAAA");
text = platformId; text = platformId;
size = TextRenderer.MeasureText(graphics, text, font); size = TextRenderer.MeasureText(graphics, text, font);
int left = -4; const int left = -4;
bounds = bounds with { X = bounds.X + bounds.Width + left, Width = size.Width }; bounds = bounds with { X = bounds.X + bounds.Width + left, Width = size.Width };
selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width + left, 0)); selectionBounds = new(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width + left, 0));
graphics.FillRectangle(brush, bounds); graphics.FillRectangle(brush, bounds);
point = new Point(bounds.Location.X - 1, bounds.Location.Y + 1); point = new(bounds.Location.X - 1, bounds.Location.Y + 1);
TextRenderer.DrawText(graphics, text, font, point, color, TextFormatFlags.Default); TextRenderer.DrawText(graphics, text, font, point, color, TextFormatFlags.Default);
} }
/*if (highlighted) /*if (highlighted)
@ -116,7 +116,7 @@ internal class CustomTreeView : TreeView
{ {
if (bounds == node.Bounds) if (bounds == node.Bounds)
{ {
size = new Size(4, 0); size = new(4, 0);
bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width }; bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width };
graphics.FillRectangle(brush, bounds); graphics.FillRectangle(brush, bounds);
} }
@ -127,47 +127,45 @@ internal class CustomTreeView : TreeView
: CheckBoxState.UncheckedDisabled; : CheckBoxState.UncheckedDisabled;
size = CheckBoxRenderer.GetGlyphSize(graphics, checkBoxState); size = CheckBoxRenderer.GetGlyphSize(graphics, checkBoxState);
bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width }; bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width };
selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 }); selectionBounds = new(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
Rectangle checkBoxBounds = bounds; Rectangle checkBoxBounds = bounds;
graphics.FillRectangle(backBrush, bounds); graphics.FillRectangle(backBrush, bounds);
point = new Point(bounds.Left, bounds.Top + bounds.Height / 2 - size.Height / 2 - 1); point = new(bounds.Left, bounds.Top + bounds.Height / 2 - size.Height / 2 - 1);
CheckBoxRenderer.DrawCheckBox(graphics, point, checkBoxState); CheckBoxRenderer.DrawCheckBox(graphics, point, checkBoxState);
text = koaloaderToggleString; text = KoaloaderToggleString;
size = TextRenderer.MeasureText(graphics, text, font); size = TextRenderer.MeasureText(graphics, text, font);
int left = 1; int left = 1;
bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width + left }; bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width + left };
selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 }); selectionBounds = new(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
checkBoxBounds = new Rectangle(checkBoxBounds.Location, checkBoxBounds.Size + bounds.Size with { Height = 0 }); checkBoxBounds = new(checkBoxBounds.Location, checkBoxBounds.Size + bounds.Size with { Height = 0 });
graphics.FillRectangle(backBrush, bounds); graphics.FillRectangle(backBrush, bounds);
point = new Point(bounds.Location.X - 1 + left, bounds.Location.Y + 1); point = new(bounds.Location.X - 1 + left, bounds.Location.Y + 1);
TextRenderer.DrawText(graphics, text, font, point, Enabled ? ColorTranslator.FromHtml("#006900") : ColorTranslator.FromHtml("#69AA69"), TextRenderer.DrawText(graphics, text, font, point, Enabled ? ColorTranslator.FromHtml("#006900") : ColorTranslator.FromHtml("#69AA69"),
TextFormatFlags.Default); TextFormatFlags.Default);
this.checkBoxBounds[selection] = RectangleToClient(checkBoxBounds); this.checkBoxBounds[selection] = RectangleToClient(checkBoxBounds);
string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy; string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy;
if (selection.Koaloader && proxy is not null) if (selection.Koaloader)
{ {
comboBoxFont ??= new Font(font.FontFamily, 6, font.Style, font.Unit, font.GdiCharSet, font.GdiVerticalFont); comboBoxFont ??= new(font.FontFamily, 6, font.Style, font.Unit, font.GdiCharSet, font.GdiVerticalFont);
ComboBoxState comboBoxState = Enabled ? ComboBoxState.Normal : ComboBoxState.Disabled; ComboBoxState comboBoxState = Enabled ? ComboBoxState.Normal : ComboBoxState.Disabled;
text = proxy + ".dll"; text = proxy + ".dll";
size = TextRenderer.MeasureText(graphics, text, comboBoxFont) + new Size(6, 0); size = TextRenderer.MeasureText(graphics, text, comboBoxFont) + new Size(6, 0);
int padding = 2; const int padding = 2;
bounds = new Rectangle(bounds.X + bounds.Width, bounds.Y + padding / 2, size.Width, bounds.Height - padding); bounds = new(bounds.X + bounds.Width, bounds.Y + padding / 2, size.Width, bounds.Height - padding);
selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 }); selectionBounds = new(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
Rectangle comboBoxBounds = bounds; Rectangle comboBoxBounds = bounds;
graphics.FillRectangle(backBrush, bounds); graphics.FillRectangle(backBrush, bounds);
ComboBoxRenderer.DrawTextBox(graphics, bounds, text, comboBoxFont, comboBoxState); ComboBoxRenderer.DrawTextBox(graphics, bounds, text, comboBoxFont, comboBoxState);
size = new Size(14, 0); size = new(14, 0);
left = -1; left = -1;
bounds = bounds with { X = bounds.X + bounds.Width + left, Width = size.Width }; bounds = bounds with { X = bounds.X + bounds.Width + left, Width = size.Width };
selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width + left, 0)); selectionBounds = new(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width + left, 0));
comboBoxBounds = new Rectangle(comboBoxBounds.Location, comboBoxBounds.Size + new Size(bounds.Size.Width + left, 0)); comboBoxBounds = new(comboBoxBounds.Location, comboBoxBounds.Size + new Size(bounds.Size.Width + left, 0));
ComboBoxRenderer.DrawDropDownButton(graphics, bounds, comboBoxState); ComboBoxRenderer.DrawDropDownButton(graphics, bounds, comboBoxState);
this.comboBoxBounds[selection] = RectangleToClient(comboBoxBounds); this.comboBoxBounds[selection] = RectangleToClient(comboBoxBounds);
} }
else else
{
_ = comboBoxBounds.Remove(selection); _ = comboBoxBounds.Remove(selection);
}
} }
} }
this.selectionBounds[node] = RectangleToClient(selectionBounds); this.selectionBounds[node] = RectangleToClient(selectionBounds);
@ -179,11 +177,9 @@ internal class CustomTreeView : TreeView
Refresh(); Refresh();
Point clickPoint = PointToClient(e.Location); Point clickPoint = PointToClient(e.Location);
SelectForm selectForm = (form ??= FindForm()) as SelectForm; SelectForm selectForm = (form ??= FindForm()) as SelectForm;
foreach (KeyValuePair<TreeNode, Rectangle> pair in selectionBounds.ToList()) foreach (KeyValuePair<TreeNode, Rectangle> pair in selectionBounds)
if (pair.Key.TreeView is null) if (pair.Key.TreeView is null)
{
_ = selectionBounds.Remove(pair.Key); _ = selectionBounds.Remove(pair.Key);
}
else if (pair.Key.IsVisible && pair.Value.Contains(clickPoint)) else if (pair.Key.IsVisible && pair.Value.Contains(clickPoint))
{ {
SelectedNode = pair.Key; SelectedNode = pair.Key;
@ -191,57 +187,51 @@ internal class CustomTreeView : TreeView
selectForm.OnNodeRightClick(pair.Key, e.Location); selectForm.OnNodeRightClick(pair.Key, e.Location);
break; break;
} }
if (e.Button is MouseButtons.Left) if (e.Button is not MouseButtons.Left)
{ return;
if (comboBoxBounds.Any() && selectForm is not null) if (comboBoxBounds.Any() && selectForm is not null)
foreach (KeyValuePair<ProgramSelection, Rectangle> pair in comboBoxBounds.ToList()) foreach (KeyValuePair<ProgramSelection, Rectangle> pair in comboBoxBounds)
if (!ProgramSelection.All.Contains(pair.Key))
{
_ = comboBoxBounds.Remove(pair.Key);
}
else if (pair.Value.Contains(clickPoint))
{
List<string> proxies = EmbeddedResources.FindAll(r => r.StartsWith("Koaloader")).Select(p =>
{
p.GetProxyInfoFromIdentifier(out string proxyName, out _);
return proxyName;
}).Distinct().ToList();
comboBoxDropDown ??= new ToolStripDropDown();
comboBoxDropDown.ShowItemToolTips = false;
comboBoxDropDown.Items.Clear();
foreach (string proxy in proxies)
{
bool canUse = true;
foreach ((string directory, BinaryType binaryType) in pair.Key.ExecutableDirectories)
{
string path = directory + @"\" + proxy + ".dll";
if (File.Exists(path) && !path.IsResourceFile(ResourceIdentifier.Koaloader))
{
canUse = false;
break;
}
}
if (canUse)
_ = comboBoxDropDown.Items.Add(new ToolStripButton(proxy + ".dll", null, (s, e) =>
{
pair.Key.KoaloaderProxy = proxy == ProgramSelection.DefaultKoaloaderProxy ? null : proxy;
selectForm.OnKoaloaderChanged();
}) { Font = comboBoxFont });
}
comboBoxDropDown.Show(this, PointToScreen(new Point(pair.Value.Left, pair.Value.Bottom - 1)));
break;
}
foreach (KeyValuePair<ProgramSelection, Rectangle> pair in checkBoxBounds.ToList())
if (!ProgramSelection.All.Contains(pair.Key)) if (!ProgramSelection.All.Contains(pair.Key))
{ _ = comboBoxBounds.Remove(pair.Key);
_ = checkBoxBounds.Remove(pair.Key);
}
else if (pair.Value.Contains(clickPoint)) else if (pair.Value.Contains(clickPoint))
{ {
pair.Key.Koaloader = !pair.Key.Koaloader; List<string> proxies = EmbeddedResources.FindAll(r => r.StartsWith("Koaloader")).Select(p =>
selectForm.OnKoaloaderChanged(); {
p.GetProxyInfoFromIdentifier(out string proxyName, out _);
return proxyName;
}).Distinct().ToList();
comboBoxDropDown ??= new();
comboBoxDropDown.ShowItemToolTips = false;
comboBoxDropDown.Items.Clear();
foreach (string proxy in proxies)
{
bool canUse = true;
foreach ((string directory, BinaryType binaryType) in pair.Key.ExecutableDirectories)
{
string path = directory + @"\" + proxy + ".dll";
if (!File.Exists(path) || path.IsResourceFile(ResourceIdentifier.Koaloader))
continue;
canUse = false;
break;
}
if (canUse)
_ = comboBoxDropDown.Items.Add(new ToolStripButton(proxy + ".dll", null, (s, e) =>
{
pair.Key.KoaloaderProxy = proxy == ProgramSelection.DefaultKoaloaderProxy ? null : proxy;
selectForm.OnKoaloaderChanged();
}) { Font = comboBoxFont });
}
comboBoxDropDown.Show(this, PointToScreen(new(pair.Value.Left, pair.Value.Bottom - 1)));
break; break;
} }
} foreach (KeyValuePair<ProgramSelection, Rectangle> pair in checkBoxBounds)
if (!ProgramSelection.All.Contains(pair.Key))
_ = checkBoxBounds.Remove(pair.Key);
else if (pair.Value.Contains(clickPoint))
{
pair.Key.Koaloader = !pair.Key.Koaloader;
selectForm?.OnKoaloaderChanged();
break;
}
} }
} }

View file

@ -20,7 +20,7 @@ internal static class PlatformIdComparer
internal static NodeTextComparer NodeText => nodeTextComparer ??= new(); internal static NodeTextComparer NodeText => nodeTextComparer ??= new();
} }
internal class StringComparer : IComparer<string> internal sealed class StringComparer : IComparer<string>
{ {
public int Compare(string a, string b) public int Compare(string a, string b)
=> !int.TryParse(a, out _) && !int.TryParse(b, out _) => !int.TryParse(a, out _) && !int.TryParse(b, out _)
@ -36,7 +36,7 @@ internal class StringComparer : IComparer<string>
: 0; : 0;
} }
internal class NodeComparer : IComparer<TreeNode> internal sealed class NodeComparer : IComparer<TreeNode>
{ {
public int Compare(TreeNode a, TreeNode b) public int Compare(TreeNode a, TreeNode b)
=> a.Tag is not Platform A => a.Tag is not Platform A
@ -50,7 +50,7 @@ internal class NodeComparer : IComparer<TreeNode>
: 0; : 0;
} }
internal class NodeNameComparer : IComparer internal sealed class NodeNameComparer : IComparer
{ {
public int Compare(object a, object b) public int Compare(object a, object b)
=> a is not TreeNode A => a is not TreeNode A
@ -62,7 +62,7 @@ internal class NodeNameComparer : IComparer
: PlatformIdComparer.String.Compare(A.Name, B.Name); : PlatformIdComparer.String.Compare(A.Name, B.Name);
} }
internal class NodeTextComparer : IComparer internal sealed class NodeTextComparer : IComparer
{ {
public int Compare(object a, object b) public int Compare(object a, object b)
=> a is not TreeNode A => a is not TreeNode A

View file

@ -4,7 +4,7 @@
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net7.0-windows</TargetFramework>
<UseWindowsForms>True</UseWindowsForms> <UseWindowsForms>True</UseWindowsForms>
<ApplicationIcon>Resources\ini.ico</ApplicationIcon> <ApplicationIcon>Resources\ini.ico</ApplicationIcon>
<Version>4.3.0.1</Version> <Version>4.4.0.0</Version>
<Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright> <Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright>
<Company>CreamInstaller</Company> <Company>CreamInstaller</Company>
<Product>Automatic DLC Unlocker Installer &amp; Configuration Generator</Product> <Product>Automatic DLC Unlocker Installer &amp; Configuration Generator</Product>
@ -19,6 +19,7 @@
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DefineConstants>$(DefineConstants)</DefineConstants> <DefineConstants>$(DefineConstants)</DefineConstants>
<ProduceReferenceAssembly>False</ProduceReferenceAssembly>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'"> <PropertyGroup Condition="'$(Configuration)'=='Release'">
<AssemblyName>$(Company)</AssemblyName> <AssemblyName>$(Company)</AssemblyName>

View file

@ -6,13 +6,13 @@ using CreamInstaller.Utility;
namespace CreamInstaller.Forms; namespace CreamInstaller.Forms;
internal partial class DebugForm : CustomForm internal sealed partial class DebugForm : CustomForm
{ {
internal static DebugForm current; private static DebugForm current;
private Form attachedForm; private Form attachedForm;
internal DebugForm() private DebugForm()
{ {
InitializeComponent(); InitializeComponent();
debugTextBox.BackColor = LogTextBox.Background; debugTextBox.BackColor = LogTextBox.Background;
@ -26,7 +26,6 @@ internal partial class DebugForm : CustomForm
current = null; current = null;
return current ??= new(); return current ??= new();
} }
set => current = value;
} }
protected override void WndProc(ref Message message) // make form immovable by user protected override void WndProc(ref Message message) // make form immovable by user
@ -57,16 +56,15 @@ internal partial class DebugForm : CustomForm
UpdateAttachment(); UpdateAttachment();
} }
internal void OnChange(object sender, EventArgs args) => UpdateAttachment(); private void OnChange(object sender, EventArgs args) => UpdateAttachment();
internal void UpdateAttachment() private void UpdateAttachment()
{ {
if (attachedForm is not null && attachedForm.Visible) if (attachedForm is null || !attachedForm.Visible)
{ return;
//Size = new(Size.Width, attachedForm.Size.Height); //Size = new(Size.Width, attachedForm.Size.Height);
Location = new(attachedForm.Right, attachedForm.Top); Location = new(attachedForm.Right, attachedForm.Top);
BringToFrontWithoutActivation(); BringToFrontWithoutActivation();
}
} }
internal void Log(string text) => Log(text, LogTextBox.Error); internal void Log(string text) => Log(text, LogTextBox.Error);
@ -81,4 +79,4 @@ internal partial class DebugForm : CustomForm
debugTextBox.AppendText(text, color, true); debugTextBox.AppendText(text, color, true);
}); });
} }
} }

View file

@ -8,7 +8,7 @@ using CreamInstaller.Components;
namespace CreamInstaller.Forms; namespace CreamInstaller.Forms;
internal partial class DialogForm : CustomForm internal sealed partial class DialogForm : CustomForm
{ {
internal DialogForm(IWin32Window owner) : base(owner) => InitializeComponent(); internal DialogForm(IWin32Window owner) : base(owner) => InitializeComponent();
@ -16,7 +16,7 @@ internal partial class DialogForm : CustomForm
string customFormText = null, Icon customFormIcon = null) string customFormText = null, Icon customFormIcon = null)
{ {
descriptionIcon ??= Icon; descriptionIcon ??= Icon;
icon.Image = descriptionIcon.ToBitmap(); icon.Image = descriptionIcon?.ToBitmap();
List<LinkLabel.Link> links = new(); List<LinkLabel.Link> links = new();
for (int i = 0; i < descriptionText.Length; i++) for (int i = 0; i < descriptionText.Length; i++)
if (descriptionText[i] == '[') if (descriptionText[i] == '[')
@ -25,15 +25,14 @@ internal partial class DialogForm : CustomForm
int textRight = descriptionText.IndexOf("]", textLeft == -1 ? i : textLeft, StringComparison.Ordinal); int textRight = descriptionText.IndexOf("]", textLeft == -1 ? i : textLeft, StringComparison.Ordinal);
int linkLeft = descriptionText.IndexOf("(", textRight == -1 ? i : textRight, StringComparison.Ordinal); int linkLeft = descriptionText.IndexOf("(", textRight == -1 ? i : textRight, StringComparison.Ordinal);
int linkRight = descriptionText.IndexOf(")", linkLeft == -1 ? i : linkLeft, StringComparison.Ordinal); int linkRight = descriptionText.IndexOf(")", linkLeft == -1 ? i : linkLeft, StringComparison.Ordinal);
if (textLeft != -1 && textRight == linkLeft - 1 && linkRight != -1) if (textLeft == -1 || textRight != linkLeft - 1 || linkRight == -1)
{ continue;
string text = descriptionText[(textLeft + 1)..textRight]; string text = descriptionText[(textLeft + 1)..textRight];
string link = descriptionText[(linkLeft + 1)..linkRight]; string link = descriptionText[(linkLeft + 1)..linkRight];
if (string.IsNullOrWhiteSpace(link)) if (string.IsNullOrWhiteSpace(link))
link = text; link = text;
descriptionText = descriptionText.Remove(i, linkRight + 1 - i).Insert(i, text); descriptionText = descriptionText.Remove(i, linkRight + 1 - i).Insert(i, text);
links.Add(new(i, text.Length, link)); links.Add(new(i, text.Length, link));
}
} }
descriptionLabel.Text = descriptionText; descriptionLabel.Text = descriptionText;
acceptButton.Text = acceptButtonText; acceptButton.Text = acceptButtonText;
@ -53,12 +52,11 @@ internal partial class DialogForm : CustomForm
} }
if (customFormIcon is not null) if (customFormIcon is not null)
Icon = customFormIcon; Icon = customFormIcon;
if (links.Any()) if (!links.Any())
{ return ShowDialog();
foreach (LinkLabel.Link link in links) foreach (LinkLabel.Link link in links)
_ = descriptionLabel.Links.Add(link); _ = descriptionLabel.Links.Add(link);
descriptionLabel.LinkClicked += (s, e) => Process.Start(new ProcessStartInfo((string)e.Link.LinkData) { UseShellExecute = true }); descriptionLabel.LinkClicked += (s, e) => Process.Start(new ProcessStartInfo((string)e.Link.LinkData) { UseShellExecute = true });
}
return ShowDialog(); return ShowDialog();
} }
@ -66,4 +64,4 @@ internal partial class DialogForm : CustomForm
=> Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100 => Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100
? TextRenderer.MeasureText(Program.ApplicationNameShort, Font).Width > Size.Width - 100 ? Program.Name : Program.ApplicationNameShort ? TextRenderer.MeasureText(Program.ApplicationNameShort, Font).Width > Size.Width - 100 ? Program.Name : Program.ApplicationNameShort
: Program.ApplicationName; : Program.ApplicationName;
} }

View file

@ -3,7 +3,7 @@ using System.Windows.Forms;
namespace CreamInstaller.Forms namespace CreamInstaller.Forms
{ {
partial class InstallForm sealed partial class InstallForm
{ {
private IContainer components = null; private IContainer components = null;
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)

View file

@ -13,15 +13,15 @@ using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Forms; namespace CreamInstaller.Forms;
internal partial class InstallForm : CustomForm internal sealed partial class InstallForm : CustomForm
{ {
private readonly List<ProgramSelection> DisabledSelections = new(); private readonly List<ProgramSelection> disabledSelections = new();
private readonly int ProgramCount = ProgramSelection.AllEnabled.Count; private readonly int programCount = ProgramSelection.AllEnabled.Count;
internal readonly bool Uninstalling; private readonly bool uninstalling;
private int CompleteOperationsCount; private int completeOperationsCount;
private int OperationsCount; private int operationsCount;
internal bool Reselecting; internal bool Reselecting;
internal InstallForm(bool uninstall = false) internal InstallForm(bool uninstall = false)
@ -29,15 +29,15 @@ internal partial class InstallForm : CustomForm
InitializeComponent(); InitializeComponent();
Text = Program.ApplicationName; Text = Program.ApplicationName;
logTextBox.BackColor = LogTextBox.Background; logTextBox.BackColor = LogTextBox.Background;
Uninstalling = uninstall; uninstalling = uninstall;
} }
internal void UpdateProgress(int progress) private void UpdateProgress(int progress)
{ {
if (!userProgressBar.Disposing && !userProgressBar.IsDisposed) if (!userProgressBar.Disposing && !userProgressBar.IsDisposed)
userProgressBar.Invoke(() => userProgressBar.Invoke(() =>
{ {
int value = (int)((float)CompleteOperationsCount / OperationsCount * 100) + progress / OperationsCount; int value = (int)((float)completeOperationsCount / operationsCount * 100) + progress / operationsCount;
if (value < userProgressBar.Value) if (value < userProgressBar.Value)
return; return;
userProgressBar.Value = value; userProgressBar.Value = value;
@ -67,12 +67,12 @@ internal partial class InstallForm : CustomForm
_ = await Repair(this, selection); _ = await Repair(this, selection);
} }
UpdateUser( UpdateUser(
$"{(Uninstalling ? "Uninstalling" : "Installing")}" + $" {(Uninstalling ? "from" : "for")} " + selection.Name $"{(uninstalling ? "Uninstalling" : "Installing")}" + $" {(uninstalling ? "from" : "for")} " + selection.Name
+ $" with root directory \"{selection.RootDirectory}\" . . . ", LogTextBox.Operation); + $" with root directory \"{selection.RootDirectory}\" . . . ", LogTextBox.Operation);
IEnumerable<string> invalidDirectories = (await selection.RootDirectory.GetExecutables()) IEnumerable<string> invalidDirectories = (await selection.RootDirectory.GetExecutables())
?.Where(d => !selection.ExecutableDirectories.Any(s => s.directory == Path.GetDirectoryName(d.path))) ?.Where(d => selection.ExecutableDirectories.All(s => s.directory != Path.GetDirectoryName(d.path)))
?.Select(d => Path.GetDirectoryName(d.path)); ?.Select(d => Path.GetDirectoryName(d.path));
if (!selection.ExecutableDirectories.Any(s => s.directory == selection.RootDirectory)) if (selection.ExecutableDirectories.All(s => s.directory != selection.RootDirectory))
invalidDirectories = invalidDirectories?.Append(selection.RootDirectory); invalidDirectories = invalidDirectories?.Append(selection.RootDirectory);
invalidDirectories = invalidDirectories?.Distinct(); invalidDirectories = invalidDirectories?.Distinct();
if (invalidDirectories is not null) if (invalidDirectories is not null)
@ -80,30 +80,31 @@ internal partial class InstallForm : CustomForm
{ {
if (Program.Canceled) if (Program.Canceled)
throw new CustomMessageException("The operation was canceled."); throw new CustomMessageException("The operation was canceled.");
directory.GetKoaloaderComponents(out List<string> proxies, out string config); directory.GetKoaloaderComponents(out List<string> proxies, out string old_config, out string config);
if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader)) if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader))
|| directory != selection.RootDirectory && Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll)) || File.Exists(config)) || directory != selection.RootDirectory && Koaloader.AutoLoadDLLs.Any(pair => File.Exists(directory + @"\" + pair.dll))
|| File.Exists(old_config) || File.Exists(config))
{ {
UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in incorrect directory \"{directory}\" . . . ", LogTextBox.Operation); UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in incorrect directory \"{directory}\" . . . ", LogTextBox.Operation);
await Koaloader.Uninstall(directory, selection.RootDirectory, this); await Koaloader.Uninstall(directory, selection.RootDirectory, this);
} }
Thread.Sleep(1); Thread.Sleep(1);
} }
if (Uninstalling || !selection.Koaloader) if (uninstalling || !selection.Koaloader)
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories) foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
{ {
if (Program.Canceled) if (Program.Canceled)
throw new CustomMessageException("The operation was canceled."); throw new CustomMessageException("The operation was canceled.");
directory.GetKoaloaderComponents(out List<string> proxies, out string config); directory.GetKoaloaderComponents(out List<string> proxies, out string old_config, out string config);
if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader)) if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader))
|| Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll)) || File.Exists(config)) || Koaloader.AutoLoadDLLs.Any(pair => File.Exists(directory + @"\" + pair.dll)) || File.Exists(old_config) || File.Exists(config))
{ {
UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
await Koaloader.Uninstall(directory, selection.RootDirectory, this); await Koaloader.Uninstall(directory, selection.RootDirectory, this);
} }
Thread.Sleep(1); Thread.Sleep(1);
} }
bool uninstallProxy = Uninstalling || selection.Koaloader; bool uninstallProxy = uninstalling || selection.Koaloader;
int count = selection.DllDirectories.Count, cur = 0; int count = selection.DllDirectories.Count, cur = 0;
foreach (string directory in selection.DllDirectories) foreach (string directory in selection.DllDirectories)
{ {
@ -111,10 +112,10 @@ internal partial class InstallForm : CustomForm
throw new CustomMessageException("The operation was canceled."); throw new CustomMessageException("The operation was canceled.");
if (selection.Platform is Platform.Steam or Platform.Paradox) if (selection.Platform is Platform.Steam or Platform.Paradox)
{ {
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string old_config,
out string cache); out string config, out string cache);
if (uninstallProxy if (uninstallProxy
? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) || File.Exists(cache) ? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(old_config) || File.Exists(config) || File.Exists(cache)
: File.Exists(api32) || File.Exists(api64)) : File.Exists(api32) || File.Exists(api64))
{ {
UpdateUser( UpdateUser(
@ -170,7 +171,7 @@ internal partial class InstallForm : CustomForm
UpdateProgress(++cur / count * 100); UpdateProgress(++cur / count * 100);
Thread.Sleep(1); Thread.Sleep(1);
} }
if (selection.Koaloader && !Uninstalling) if (selection.Koaloader && !uninstalling)
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories) foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
{ {
if (Program.Canceled) if (Program.Canceled)
@ -185,8 +186,8 @@ internal partial class InstallForm : CustomForm
private async Task Operate() private async Task Operate()
{ {
List<ProgramSelection> programSelections = ProgramSelection.AllEnabled; List<ProgramSelection> programSelections = ProgramSelection.AllEnabled;
OperationsCount = programSelections.Count; operationsCount = programSelections.Count;
CompleteOperationsCount = 0; completeOperationsCount = 0;
foreach (ProgramSelection selection in programSelections) foreach (ProgramSelection selection in programSelections)
{ {
if (Program.Canceled || !Program.IsProgramRunningDialog(this, selection)) if (Program.Canceled || !Program.IsProgramRunningDialog(this, selection))
@ -196,24 +197,24 @@ internal partial class InstallForm : CustomForm
await OperateFor(selection); await OperateFor(selection);
UpdateUser($"Operation succeeded for {selection.Name}.", LogTextBox.Success); UpdateUser($"Operation succeeded for {selection.Name}.", LogTextBox.Success);
selection.Enabled = false; selection.Enabled = false;
DisabledSelections.Add(selection); disabledSelections.Add(selection);
} }
catch (Exception exception) catch (Exception exception)
{ {
UpdateUser($"Operation failed for {selection.Name}: " + exception, LogTextBox.Error); UpdateUser($"Operation failed for {selection.Name}: " + exception, LogTextBox.Error);
} }
++CompleteOperationsCount; ++completeOperationsCount;
} }
Program.Cleanup(); Program.Cleanup();
List<ProgramSelection> FailedSelections = ProgramSelection.AllEnabled; List<ProgramSelection> failedSelections = ProgramSelection.AllEnabled;
if (FailedSelections.Any()) if (failedSelections.Any())
if (FailedSelections.Count == 1) if (failedSelections.Count == 1)
throw new CustomMessageException($"Operation failed for {FailedSelections.First().Name}."); throw new CustomMessageException($"Operation failed for {failedSelections.First().Name}.");
else else
throw new CustomMessageException($"Operation failed for {FailedSelections.Count} programs."); throw new CustomMessageException($"Operation failed for {failedSelections.Count} programs.");
foreach (ProgramSelection selection in DisabledSelections) foreach (ProgramSelection selection in disabledSelections)
selection.Enabled = true; selection.Enabled = true;
DisabledSelections.Clear(); disabledSelections.Clear();
} }
private async void Start() private async void Start()
@ -227,12 +228,12 @@ internal partial class InstallForm : CustomForm
try try
{ {
await Operate(); await Operate();
UpdateUser($"DLC unlocker(s) successfully {(Uninstalling ? "uninstalled" : "installed and generated")} for " + ProgramCount + " program(s).", UpdateUser($"DLC unlocker(s) successfully {(uninstalling ? "uninstalled" : "installed and generated")} for " + programCount + " program(s).",
LogTextBox.Success); LogTextBox.Success);
} }
catch (Exception exception) catch (Exception exception)
{ {
UpdateUser($"DLC unlocker {(Uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + exception, LogTextBox.Error); UpdateUser($"DLC unlocker {(uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + exception, LogTextBox.Error);
retryButton.Enabled = true; retryButton.Enabled = true;
} }
userProgressBar.Value = userProgressBar.Maximum; userProgressBar.Value = userProgressBar.Maximum;
@ -276,9 +277,9 @@ internal partial class InstallForm : CustomForm
{ {
Program.Cleanup(); Program.Cleanup();
Reselecting = true; Reselecting = true;
foreach (ProgramSelection selection in DisabledSelections) foreach (ProgramSelection selection in disabledSelections)
selection.Enabled = true; selection.Enabled = true;
DisabledSelections.Clear(); disabledSelections.Clear();
Close(); Close();
} }
} }

View file

@ -16,7 +16,7 @@ using Onova.Services;
namespace CreamInstaller.Forms; namespace CreamInstaller.Forms;
internal partial class MainForm : CustomForm internal sealed partial class MainForm : CustomForm
{ {
private CancellationTokenSource cancellationTokenSource; private CancellationTokenSource cancellationTokenSource;
private Version latestVersion; private Version latestVersion;
@ -68,11 +68,10 @@ internal partial class MainForm : CustomForm
updateManager = new(AssemblyMetadata.FromAssembly(Program.EntryAssembly, Program.CurrentProcessFilePath), resolver, extractor); updateManager = new(AssemblyMetadata.FromAssembly(Program.EntryAssembly, Program.CurrentProcessFilePath), resolver, extractor);
if (latestVersion is null) if (latestVersion is null)
{ {
CheckForUpdatesResult checkForUpdatesResult = null;
cancellationTokenSource = new(); cancellationTokenSource = new();
try try
{ {
checkForUpdatesResult = await updateManager.CheckForUpdatesAsync(cancellationTokenSource.Token); CheckForUpdatesResult checkForUpdatesResult = await updateManager.CheckForUpdatesAsync(cancellationTokenSource.Token);
#if !DEBUG #if !DEBUG
if (checkForUpdatesResult.CanUpdate) if (checkForUpdatesResult.CanUpdate)
{ {
@ -133,7 +132,7 @@ internal partial class MainForm : CustomForm
foreach (HtmlNode node in nodes) foreach (HtmlNode node in nodes)
changelogTreeView.Invoke(delegate changelogTreeView.Invoke(delegate
{ {
TreeNode change = new() { Text = HttpUtility.HtmlDecode(node.InnerText) }; TreeNode change = new() { Text = HttpUtility.HtmlDecode(node.InnerText) ?? string.Empty };
root.Nodes.Add(change); root.Nodes.Add(change);
root.Expand(); root.Expand();
if (changelogTreeView.Nodes.Count > 0) if (changelogTreeView.Nodes.Count > 0)
@ -149,8 +148,8 @@ internal partial class MainForm : CustomForm
retry: retry:
try try
{ {
string FileName = Path.GetFileName(Program.CurrentProcessFilePath); string fileName = Path.GetFileName(Program.CurrentProcessFilePath);
if (FileName != Program.ApplicationExecutable) if (fileName != Program.ApplicationExecutable)
{ {
using DialogForm form = new(this); using DialogForm form = new(this);
if (form.Show(SystemIcons.Warning, if (form.Show(SystemIcons.Warning,
@ -232,4 +231,4 @@ internal partial class MainForm : CustomForm
cancellationTokenSource?.Dispose(); cancellationTokenSource?.Dispose();
updateManager?.Dispose(); updateManager?.Dispose();
} }
} }

View file

@ -7,7 +7,7 @@ using CreamInstaller.Utility;
namespace CreamInstaller.Forms; namespace CreamInstaller.Forms;
internal partial class SelectDialogForm : CustomForm internal sealed partial class SelectDialogForm : CustomForm
{ {
private readonly List<(Platform platform, string id, string name)> selected = new(); private readonly List<(Platform platform, string id, string name)> selected = new();
internal SelectDialogForm(IWin32Window owner) : base(owner) => InitializeComponent(); internal SelectDialogForm(IWin32Window owner) : base(owner) => InitializeComponent();
@ -69,9 +69,7 @@ internal partial class SelectDialogForm : CustomForm
private void OnAllCheckBoxChanged(object sender, EventArgs e) private void OnAllCheckBoxChanged(object sender, EventArgs e)
{ {
bool shouldCheck = false; bool shouldCheck = selectionTreeView.Nodes.Cast<TreeNode>().Any(n => !n.Checked);
if (selectionTreeView.Nodes.Cast<TreeNode>().Any(n => !n.Checked))
shouldCheck = true;
foreach (TreeNode node in selectionTreeView.Nodes) foreach (TreeNode node in selectionTreeView.Nodes)
{ {
node.Checked = shouldCheck; node.Checked = shouldCheck;
@ -84,8 +82,8 @@ internal partial class SelectDialogForm : CustomForm
private void OnLoad(object sender, EventArgs e) private void OnLoad(object sender, EventArgs e)
{ {
List<(Platform platform, string id)> choices = ProgramData.ReadProgramChoices(); List<(Platform platform, string id)> choices = ProgramData.ReadProgramChoices().ToList();
if (choices is null) if (!choices.Any())
return; return;
foreach (TreeNode node in selectionTreeView.Nodes) foreach (TreeNode node in selectionTreeView.Nodes)
{ {
@ -96,10 +94,7 @@ internal partial class SelectDialogForm : CustomForm
private void OnSave(object sender, EventArgs e) private void OnSave(object sender, EventArgs e)
{ {
List<(Platform platform, string id)> choices = new(); ProgramData.WriteProgramChoices(selectionTreeView.Nodes.Cast<TreeNode>().Where(n => n.Checked).Select(node => ((Platform)node.Tag, node.Name)));
foreach (TreeNode node in selectionTreeView.Nodes.Cast<TreeNode>().Where(n => n.Checked))
choices.Add(((Platform)node.Tag, node.Name));
ProgramData.WriteProgramChoices(choices);
loadButton.Enabled = ProgramData.ReadProgramChoices() is not null; loadButton.Enabled = ProgramData.ReadProgramChoices() is not null;
} }
} }

View file

@ -1,6 +1,4 @@
#pragma warning disable IDE0058 using System;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
@ -22,15 +20,15 @@ using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Forms; namespace CreamInstaller.Forms;
internal partial class SelectForm : CustomForm internal sealed partial class SelectForm : CustomForm
{ {
private readonly string helpButtonListPrefix = "\n • "; private const string HelpButtonListPrefix = "\n • ";
private readonly SynchronizedCollection<string> RemainingDLCs = new(); private readonly SynchronizedCollection<string> remainingDLCs = new();
private readonly SynchronizedCollection<string> RemainingGames = new(); private readonly SynchronizedCollection<string> remainingGames = new();
private List<(Platform platform, string id, string name)> ProgramsToScan; private List<(Platform platform, string id, string name)> programsToScan;
internal SelectForm() internal SelectForm()
{ {
@ -40,12 +38,12 @@ internal partial class SelectForm : CustomForm
public override ContextMenuStrip ContextMenuStrip => base.ContextMenuStrip ??= new(); public override ContextMenuStrip ContextMenuStrip => base.ContextMenuStrip ??= new();
internal List<TreeNode> TreeNodes => GatherTreeNodes(selectionTreeView.Nodes); private List<TreeNode> TreeNodes => GatherTreeNodes(selectionTreeView.Nodes);
private static void UpdateRemaining(Label label, SynchronizedCollection<string> list, string descriptor) private static void UpdateRemaining(Label label, SynchronizedCollection<string> list, string descriptor)
=> label.Text = list.Any() ? $"Remaining {descriptor} ({list.Count}): " + string.Join(", ", list).Replace("&", "&&") : ""; => label.Text = list.Any() ? $"Remaining {descriptor} ({list.Count}): " + string.Join(", ", list).Replace("&", "&&") : "";
private void UpdateRemainingGames() => UpdateRemaining(progressLabelGames, RemainingGames, "games"); private void UpdateRemainingGames() => UpdateRemaining(progressLabelGames, remainingGames, "games");
private void AddToRemainingGames(string gameName) private void AddToRemainingGames(string gameName)
{ {
@ -55,8 +53,8 @@ internal partial class SelectForm : CustomForm
{ {
if (Program.Canceled) if (Program.Canceled)
return; return;
if (!RemainingGames.Contains(gameName)) if (!remainingGames.Contains(gameName))
RemainingGames.Add(gameName); remainingGames.Add(gameName);
UpdateRemainingGames(); UpdateRemainingGames();
}); });
} }
@ -69,12 +67,12 @@ internal partial class SelectForm : CustomForm
{ {
if (Program.Canceled) if (Program.Canceled)
return; return;
RemainingGames.Remove(gameName); remainingGames.Remove(gameName);
UpdateRemainingGames(); UpdateRemainingGames();
}); });
} }
private void UpdateRemainingDLCs() => UpdateRemaining(progressLabelDLCs, RemainingDLCs, "DLCs"); private void UpdateRemainingDLCs() => UpdateRemaining(progressLabelDLCs, remainingDLCs, "DLCs");
private void AddToRemainingDLCs(string dlcId) private void AddToRemainingDLCs(string dlcId)
{ {
@ -84,8 +82,8 @@ internal partial class SelectForm : CustomForm
{ {
if (Program.Canceled) if (Program.Canceled)
return; return;
if (!RemainingDLCs.Contains(dlcId)) if (!remainingDLCs.Contains(dlcId))
RemainingDLCs.Add(dlcId); remainingDLCs.Add(dlcId);
UpdateRemainingDLCs(); UpdateRemainingDLCs();
}); });
} }
@ -98,35 +96,35 @@ internal partial class SelectForm : CustomForm
{ {
if (Program.Canceled) if (Program.Canceled)
return; return;
RemainingDLCs.Remove(dlcId); remainingDLCs.Remove(dlcId);
UpdateRemainingDLCs(); UpdateRemainingDLCs();
}); });
} }
private async Task GetApplicablePrograms(IProgress<int> progress) private async Task GetApplicablePrograms(IProgress<int> progress)
{ {
if (ProgramsToScan is null || !ProgramsToScan.Any()) if (programsToScan is null || !programsToScan.Any())
return; return;
int TotalGameCount = 0; int totalGameCount = 0;
int CompleteGameCount = 0; int completeGameCount = 0;
void AddToRemainingGames(string gameName) void AddToRemainingGames(string gameName)
{ {
this.AddToRemainingGames(gameName); this.AddToRemainingGames(gameName);
progress.Report(-Interlocked.Increment(ref TotalGameCount)); progress.Report(-Interlocked.Increment(ref totalGameCount));
progress.Report(CompleteGameCount); progress.Report(completeGameCount);
} }
void RemoveFromRemainingGames(string gameName) void RemoveFromRemainingGames(string gameName)
{ {
this.RemoveFromRemainingGames(gameName); this.RemoveFromRemainingGames(gameName);
progress.Report(Interlocked.Increment(ref CompleteGameCount)); progress.Report(Interlocked.Increment(ref completeGameCount));
} }
if (Program.Canceled) if (Program.Canceled)
return; return;
List<TreeNode> treeNodes = TreeNodes; List<TreeNode> treeNodes = TreeNodes;
RemainingGames.Clear(); // for display purposes only, otherwise ignorable remainingGames.Clear(); // for display purposes only, otherwise ignorable
RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable remainingDLCs.Clear(); // for display purposes only, otherwise ignorable
List<Task> appTasks = new(); List<Task> appTasks = new();
if (ProgramsToScan.Any(c => c.platform is Platform.Paradox)) if (programsToScan.Any(c => c.platform is Platform.Paradox))
{ {
List<string> dllDirectories = await ParadoxLauncher.InstallPath.GetDllDirectoriesFromGameDirectory(Platform.Paradox); List<string> dllDirectories = await ParadoxLauncher.InstallPath.GetDllDirectoriesFromGameDirectory(Platform.Paradox);
if (dllDirectories is not null) if (dllDirectories is not null)
@ -153,7 +151,7 @@ internal partial class SelectForm : CustomForm
} }
} }
int steamGamesToCheck; int steamGamesToCheck;
if (ProgramsToScan.Any(c => c.platform is Platform.Steam)) if (programsToScan.Any(c => c.platform is Platform.Steam))
{ {
List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = await SteamLibrary.GetGames(); List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = await SteamLibrary.GetGames();
steamGamesToCheck = steamGames.Count; steamGamesToCheck = steamGames.Count;
@ -161,7 +159,7 @@ internal partial class SelectForm : CustomForm
{ {
if (Program.Canceled) if (Program.Canceled)
return; return;
if (Program.IsGameBlocked(name, gameDirectory) || !ProgramsToScan.Any(c => c.platform is Platform.Steam && c.id == appId)) if (Program.IsGameBlocked(name, gameDirectory) || !programsToScan.Any(c => c.platform is Platform.Steam && c.id == appId))
{ {
Interlocked.Decrement(ref steamGamesToCheck); Interlocked.Decrement(ref steamGamesToCheck);
continue; continue;
@ -287,7 +285,7 @@ internal partial class SelectForm : CustomForm
_ = selectionTreeView.Nodes.Add(programNode); _ = selectionTreeView.Nodes.Add(programNode);
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in dlc) foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in dlc)
{ {
if (Program.Canceled || programNode is null) if (Program.Canceled)
return; return;
string appId = pair.Key; string appId = pair.Key;
(DlcType type, string name, string icon) dlcApp = pair.Value; (DlcType type, string name, string icon) dlcApp = pair.Value;
@ -310,7 +308,7 @@ internal partial class SelectForm : CustomForm
appTasks.Add(task); appTasks.Add(task);
} }
} }
if (ProgramsToScan.Any(c => c.platform is Platform.Epic)) if (programsToScan.Any(c => c.platform is Platform.Epic))
{ {
List<Manifest> epicGames = await EpicLibrary.GetGames(); List<Manifest> epicGames = await EpicLibrary.GetGames();
foreach (Manifest manifest in epicGames) foreach (Manifest manifest in epicGames)
@ -320,7 +318,7 @@ internal partial class SelectForm : CustomForm
string directory = manifest.InstallLocation; string directory = manifest.InstallLocation;
if (Program.Canceled) if (Program.Canceled)
return; return;
if (Program.IsGameBlocked(name, directory) || !ProgramsToScan.Any(c => c.platform is Platform.Epic && c.id == @namespace)) if (Program.IsGameBlocked(name, directory) || !programsToScan.Any(c => c.platform is Platform.Epic && c.id == @namespace))
continue; continue;
AddToRemainingGames(name); AddToRemainingGames(name);
Task task = Task.Run(async () => Task task = Task.Run(async () =>
@ -437,14 +435,14 @@ internal partial class SelectForm : CustomForm
appTasks.Add(task); appTasks.Add(task);
} }
} }
if (ProgramsToScan.Any(c => c.platform is Platform.Ubisoft)) if (programsToScan.Any(c => c.platform is Platform.Ubisoft))
{ {
List<(string gameId, string name, string gameDirectory)> ubisoftGames = await UbisoftLibrary.GetGames(); List<(string gameId, string name, string gameDirectory)> ubisoftGames = await UbisoftLibrary.GetGames();
foreach ((string gameId, string name, string gameDirectory) in ubisoftGames) foreach ((string gameId, string name, string gameDirectory) in ubisoftGames)
{ {
if (Program.Canceled) if (Program.Canceled)
return; return;
if (Program.IsGameBlocked(name, gameDirectory) || !ProgramsToScan.Any(c => c.platform is Platform.Ubisoft && c.id == gameId)) if (Program.IsGameBlocked(name, gameDirectory) || !programsToScan.Any(c => c.platform is Platform.Ubisoft && c.id == gameId))
continue; continue;
AddToRemainingGames(name); AddToRemainingGames(name);
Task task = Task.Run(async () => Task task = Task.Run(async () =>
@ -521,34 +519,34 @@ internal partial class SelectForm : CustomForm
ShowProgressBar(); ShowProgressBar();
await ProgramData.Setup(); await ProgramData.Setup();
bool scan = forceScan; bool scan = forceScan;
if (!scan && (ProgramsToScan is null || !ProgramsToScan.Any() || forceProvideChoices)) if (!scan && (programsToScan is null || !programsToScan.Any() || forceProvideChoices))
{ {
List<(Platform platform, string id, string name, bool alreadySelected)> gameChoices = new(); List<(Platform platform, string id, string name, bool alreadySelected)> gameChoices = new();
if (Directory.Exists(ParadoxLauncher.InstallPath)) if (Directory.Exists(ParadoxLauncher.InstallPath))
gameChoices.Add((Platform.Paradox, "PL", "Paradox Launcher", gameChoices.Add((Platform.Paradox, "PL", "Paradox Launcher",
ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Paradox && p.id == "PL"))); programsToScan is not null && programsToScan.Any(p => p.platform is Platform.Paradox && p.id == "PL")));
if (Directory.Exists(SteamLibrary.InstallPath)) if (Directory.Exists(SteamLibrary.InstallPath))
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in (await SteamLibrary.GetGames()).Where(g foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in (await SteamLibrary.GetGames()).Where(g
=> !Program.IsGameBlocked(g.name, g.gameDirectory))) => !Program.IsGameBlocked(g.name, g.gameDirectory)))
gameChoices.Add((Platform.Steam, appId, name, gameChoices.Add((Platform.Steam, appId, name,
ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Steam && p.id == appId))); programsToScan is not null && programsToScan.Any(p => p.platform is Platform.Steam && p.id == appId)));
if (Directory.Exists(EpicLibrary.EpicManifestsPath)) if (Directory.Exists(EpicLibrary.EpicManifestsPath))
foreach (Manifest manifest in (await EpicLibrary.GetGames()).Where(m => !Program.IsGameBlocked(m.DisplayName, m.InstallLocation))) foreach (Manifest manifest in (await EpicLibrary.GetGames()).Where(m => !Program.IsGameBlocked(m.DisplayName, m.InstallLocation)))
gameChoices.Add((Platform.Epic, manifest.CatalogNamespace, manifest.DisplayName, gameChoices.Add((Platform.Epic, manifest.CatalogNamespace, manifest.DisplayName,
ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Epic && p.id == manifest.CatalogNamespace))); programsToScan is not null && programsToScan.Any(p => p.platform is Platform.Epic && p.id == manifest.CatalogNamespace)));
foreach ((string gameId, string name, string gameDirectory) in (await UbisoftLibrary.GetGames()).Where(g foreach ((string gameId, string name, string gameDirectory) in (await UbisoftLibrary.GetGames()).Where(g
=> !Program.IsGameBlocked(g.name, g.gameDirectory))) => !Program.IsGameBlocked(g.name, g.gameDirectory)))
gameChoices.Add((Platform.Ubisoft, gameId, name, gameChoices.Add((Platform.Ubisoft, gameId, name,
ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Ubisoft && p.id == gameId))); programsToScan is not null && programsToScan.Any(p => p.platform is Platform.Ubisoft && p.id == gameId)));
if (gameChoices.Any()) if (gameChoices.Any())
{ {
using SelectDialogForm form = new(this); using SelectDialogForm form = new(this);
List<(Platform platform, string id, string name)> choices = form.QueryUser("Choose which programs and/or games to scan for DLC:", gameChoices); List<(Platform platform, string id, string name)> choices = form.QueryUser("Choose which programs and/or games to scan for DLC:", gameChoices);
scan = choices is not null && choices.Any(); scan = choices is not null && choices.Any();
string retry = "\n\nPress the \"Rescan Programs / Games\" button to re-choose."; const string retry = "\n\nPress the \"Rescan Programs / Games\" button to re-choose.";
if (scan) if (scan)
{ {
ProgramsToScan = choices; programsToScan = choices;
noneFoundLabel.Text = "None of the chosen programs nor games were applicable!" + retry; noneFoundLabel.Text = "None of the chosen programs nor games were applicable!" + retry;
} }
else else
@ -576,14 +574,14 @@ internal partial class SelectForm : CustomForm
progressLabel.Text = setup ? $"Setting up SteamCMD . . . {p}%" : $"Gathering and caching your applicable games and their DLCs . . . {p}%"; progressLabel.Text = setup ? $"Setting up SteamCMD . . . {p}%" : $"Gathering and caching your applicable games and their DLCs . . . {p}%";
progressBar.Value = p; progressBar.Value = p;
}; };
if (Directory.Exists(SteamLibrary.InstallPath) && ProgramsToScan is not null && ProgramsToScan.Any(c => c.platform is Platform.Steam)) if (Directory.Exists(SteamLibrary.InstallPath) && programsToScan is not null && programsToScan.Any(c => c.platform is Platform.Steam))
{ {
progressLabel.Text = "Setting up SteamCMD . . . "; progressLabel.Text = "Setting up SteamCMD . . . ";
await SteamCMD.Setup(iProgress); await SteamCMD.Setup(iProgress);
} }
setup = false; setup = false;
progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . "; progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . ";
ProgramSelection.ValidateAll(ProgramsToScan); ProgramSelection.ValidateAll(programsToScan);
/*TreeNodes.ForEach(node => /*TreeNodes.ForEach(node =>
{ {
if (node.Tag is not Platform platform if (node.Tag is not Platform platform
@ -638,11 +636,16 @@ internal partial class SelectForm : CustomForm
private static void SyncNodeAncestors(TreeNode node) private static void SyncNodeAncestors(TreeNode node)
{ {
TreeNode parentNode = node.Parent; while (true)
if (parentNode is not null)
{ {
parentNode.Checked = parentNode.Nodes.Cast<TreeNode>().Any(childNode => childNode.Checked); TreeNode parentNode = node.Parent;
SyncNodeAncestors(parentNode); if (parentNode is not null)
{
parentNode.Checked = parentNode.Nodes.Cast<TreeNode>().Any(childNode => childNode.Checked);
node = parentNode;
continue;
}
break;
} }
} }
@ -721,7 +724,7 @@ internal partial class SelectForm : CustomForm
=> Invoke(() => => Invoke(() =>
{ {
ContextMenuStrip contextMenuStrip = ContextMenuStrip; ContextMenuStrip contextMenuStrip = ContextMenuStrip;
while (ContextMenuStrip.Tag is bool used && used) while (ContextMenuStrip.Tag is true)
Thread.Sleep(100); Thread.Sleep(100);
ContextMenuStrip.Tag = true; ContextMenuStrip.Tag = true;
ToolStripItemCollection items = contextMenuStrip.Items; ToolStripItemCollection items = contextMenuStrip.Items;
@ -737,18 +740,18 @@ internal partial class SelectForm : CustomForm
dlcParentSelection = ProgramSelection.FromPlatformId(platform, dlc.Value.gameAppId); dlcParentSelection = ProgramSelection.FromPlatformId(platform, dlc.Value.gameAppId);
if (selection is null && dlcParentSelection is null) if (selection is null && dlcParentSelection is null)
return; return;
ContextMenuItem header = null; ContextMenuItem header;
if (id == "PL") if (id == "PL")
header = new(node.Text, "Paradox Launcher"); header = new(node.Text, "Paradox Launcher");
else if (selection is not null) else if (selection is not null)
header = new(node.Text, (id, selection.IconUrl)); header = new(node.Text, (id, selection.IconUrl));
else if (dlc is not null && dlcParentSelection is not null) else
header = new(node.Text, (id, dlc.Value.app.icon), (id, dlcParentSelection.IconUrl)); header = new(node.Text, (id, dlc.Value.app.icon), (id, dlcParentSelection.IconUrl));
items.Add(header ?? new ContextMenuItem(node.Text)); items.Add(header);
string appInfoVDF = $@"{SteamCMD.AppInfoPath}\{id}.vdf"; string appInfoVDF = $@"{SteamCMD.AppInfoPath}\{id}.vdf";
string appInfoJSON = $@"{SteamCMD.AppInfoPath}\{id}.json"; string appInfoJSON = $@"{SteamCMD.AppInfoPath}\{id}.json";
string cooldown = $@"{ProgramData.CooldownPath}\{id}.txt"; string cooldown = $@"{ProgramData.CooldownPath}\{id}.txt";
if ((File.Exists(appInfoVDF) || File.Exists(appInfoJSON)) && (selection is not null || dlc is not null)) if (File.Exists(appInfoVDF) || File.Exists(appInfoJSON))
{ {
List<ContextMenuItem> queries = new(); List<ContextMenuItem> queries = new();
if (File.Exists(appInfoJSON)) if (File.Exists(appInfoJSON))
@ -773,17 +776,26 @@ internal partial class SelectForm : CustomForm
{ {
File.Delete(appInfoVDF); File.Delete(appInfoVDF);
} }
catch { } catch
{
// ignored
}
try try
{ {
File.Delete(appInfoJSON); File.Delete(appInfoJSON);
} }
catch { } catch
{
// ignored
}
try try
{ {
File.Delete(cooldown); File.Delete(cooldown);
} }
catch { } catch
{
// ignored
}
OnLoad(true); OnLoad(true);
})); }));
} }
@ -793,7 +805,8 @@ internal partial class SelectForm : CustomForm
if (id == "PL") if (id == "PL")
{ {
items.Add(new ToolStripSeparator()); items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Repair", "Command Prompt", async (sender, e) => await ParadoxLauncher.Repair(this, selection))); async void EventHandler(object sender, EventArgs e) => await ParadoxLauncher.Repair(this, selection);
items.Add(new ContextMenuItem("Repair", "Command Prompt", EventHandler));
} }
items.Add(new ToolStripSeparator()); items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open Root Directory", "File Explorer", items.Add(new ContextMenuItem("Open Root Directory", "File Explorer",
@ -807,10 +820,10 @@ internal partial class SelectForm : CustomForm
if (selection.Platform is Platform.Steam or Platform.Paradox) if (selection.Platform is Platform.Steam or Platform.Paradox)
foreach (string directory in directories) foreach (string directory in directories)
{ {
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string old_config,
out string cache); out string config, out string cache);
if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config) if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(old_config)
|| File.Exists(cache)) || File.Exists(config) || File.Exists(cache))
items.Add(new ContextMenuItem($"Open Steamworks Directory #{++steam}", "File Explorer", items.Add(new ContextMenuItem($"Open Steamworks Directory #{++steam}", "File Explorer",
(sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory))); (sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)));
} }
@ -838,42 +851,37 @@ internal partial class SelectForm : CustomForm
} }
if (id != "PL") if (id != "PL")
{ {
if (selection is not null && selection.Platform is Platform.Steam if (selection?.Platform is Platform.Steam || dlcParentSelection?.Platform is Platform.Steam)
|| dlcParentSelection is not null && dlcParentSelection.Platform is Platform.Steam)
{ {
items.Add(new ToolStripSeparator()); items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open SteamDB", "SteamDB", items.Add(new ContextMenuItem("Open SteamDB", "SteamDB",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamdb.info/app/" + id))); (sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamdb.info/app/" + id)));
} }
if (selection is not null) if (selection is not null)
{ switch (selection.Platform)
if (selection.Platform is Platform.Steam)
{ {
items.Add(new ContextMenuItem("Open Steam Store", "Steam Store", case Platform.Steam:
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl))); items.Add(new ContextMenuItem("Open Steam Store", "Steam Store",
items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIconUrl), "Steam Community", (sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl)));
(sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + id))); items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIconUrl), "Steam Community",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + id)));
break;
case Platform.Epic:
items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open ScreamDB", "ScreamDB",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://scream-db.web.app/offers/" + id)));
items.Add(new ContextMenuItem("Open Epic Games Store", "Epic Games",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl)));
break;
case Platform.Ubisoft:
items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open Ubisoft Store", "Ubisoft Store",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://store.ubi.com/us/"
+ selection.Name.Replace(" ", "-").ToLowerInvariant())));
break;
} }
if (selection.Platform is Platform.Epic)
{
items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open ScreamDB", "ScreamDB",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://scream-db.web.app/offers/" + id)));
items.Add(new ContextMenuItem("Open Epic Games Store", "Epic Games",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl)));
}
if (selection.Platform is Platform.Ubisoft)
{
items.Add(new ToolStripSeparator());
#pragma warning disable CA1308 // Normalize strings to uppercase
items.Add(new ContextMenuItem("Open Ubisoft Store", "Ubisoft Store",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://store.ubi.com/us/"
+ selection.Name.Replace(" ", "-").ToLowerInvariant())));
#pragma warning restore CA1308 // Normalize strings to uppercase
}
}
} }
if (selection is not null && selection.WebsiteUrl is not null) if (selection?.WebsiteUrl != null)
items.Add(new ContextMenuItem("Open Official Website", ("Web_" + id, IconGrabber.GetDomainFaviconUrl(selection.WebsiteUrl)), items.Add(new ContextMenuItem("Open Official Website", ("Web_" + id, IconGrabber.GetDomainFaviconUrl(selection.WebsiteUrl)),
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.WebsiteUrl))); (sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.WebsiteUrl)));
contextMenuStrip.Show(selectionTreeView, location); contextMenuStrip.Show(selectionTreeView, location);
@ -900,38 +908,34 @@ internal partial class SelectForm : CustomForm
private void OnAccept(bool uninstall = false) private void OnAccept(bool uninstall = false)
{ {
if (ProgramSelection.All.Any()) if (!ProgramSelection.All.Any())
return;
if (ProgramSelection.AllEnabled.Any(selection => !Program.IsProgramRunningDialog(this, selection)))
return;
if (!uninstall && ParadoxLauncher.DlcDialog(this))
return;
Hide();
InstallForm form = new(uninstall);
form.InheritLocation(this);
form.FormClosing += (s, e) =>
{ {
foreach (ProgramSelection selection in ProgramSelection.AllEnabled) if (form.Reselecting)
if (!Program.IsProgramRunningDialog(this, selection))
return;
if (!uninstall && ParadoxLauncher.DlcDialog(this))
return;
Hide();
#pragma warning disable CA2000 // Dispose objects before losing scope
InstallForm form = new(uninstall);
#pragma warning restore CA2000 // Dispose objects before losing scope
form.InheritLocation(this);
form.FormClosing += (s, e) =>
{ {
if (form.Reselecting) InheritLocation(form);
{ Show();
InheritLocation(form);
Show();
#if DEBUG #if DEBUG
DebugForm.Current.Attach(this); DebugForm.Current.Attach(this);
#endif #endif
OnLoad(); OnLoad();
} }
else else
Close(); Close();
}; };
form.Show(); form.Show();
Hide(); Hide();
#if DEBUG #if DEBUG
DebugForm.Current.Attach(form); DebugForm.Current.Attach(form);
#endif #endif
}
} }
private void OnInstall(object sender, EventArgs e) => OnAccept(); private void OnInstall(object sender, EventArgs e) => OnAccept();
@ -948,33 +952,20 @@ internal partial class SelectForm : CustomForm
private void OnAllCheckBoxChanged(object sender, EventArgs e) private void OnAllCheckBoxChanged(object sender, EventArgs e)
{ {
bool shouldCheck = false; bool shouldCheck = TreeNodes.Any(node => node.Parent is null && !node.Checked);
foreach (TreeNode node in TreeNodes) foreach (TreeNode node in TreeNodes.Where(node => node.Parent is null && node.Checked != shouldCheck))
if (node.Parent is null && !node.Checked) {
{ node.Checked = shouldCheck;
shouldCheck = true; OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
break; }
}
foreach (TreeNode node in TreeNodes)
if (node.Parent is null && node.Checked != shouldCheck)
{
node.Checked = shouldCheck;
OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
}
allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; allCheckBox.CheckedChanged -= OnAllCheckBoxChanged;
allCheckBox.Checked = shouldCheck; allCheckBox.Checked = shouldCheck;
allCheckBox.CheckedChanged += OnAllCheckBoxChanged; allCheckBox.CheckedChanged += OnAllCheckBoxChanged;
} }
internal void OnKoaloaderAllCheckBoxChanged(object sender, EventArgs e) private void OnKoaloaderAllCheckBoxChanged(object sender, EventArgs e)
{ {
bool shouldCheck = false; bool shouldCheck = ProgramSelection.AllSafe.Any(selection => !selection.Koaloader);
foreach (ProgramSelection selection in ProgramSelection.AllSafe)
if (!selection.Koaloader)
{
shouldCheck = true;
break;
}
foreach (ProgramSelection selection in ProgramSelection.AllSafe) foreach (ProgramSelection selection in ProgramSelection.AllSafe)
selection.Koaloader = shouldCheck; selection.Koaloader = shouldCheck;
selectionTreeView.Invalidate(); selectionTreeView.Invalidate();
@ -984,21 +975,15 @@ internal partial class SelectForm : CustomForm
} }
private bool AreSelectionsDefault() private bool AreSelectionsDefault()
{ => TreeNodes.All(node => node.Parent is null || node.Tag is not Platform || (node.Text == "Unknown" ? !node.Checked : node.Checked));
foreach (TreeNode node in TreeNodes)
if (node.Parent is not null && node.Tag is Platform && (node.Text == "Unknown" ? node.Checked : !node.Checked))
return false;
return true;
}
private bool CanSaveDlc() => installButton.Enabled && (ProgramData.ReadDlcChoices() is not null || !AreSelectionsDefault()); private bool CanSaveDlc() => installButton.Enabled && (ProgramData.ReadDlcChoices().Any() || !AreSelectionsDefault());
private void OnSaveDlc(object sender, EventArgs e) private void OnSaveDlc(object sender, EventArgs e)
{ {
List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices() List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices().ToList();
?? new List<(Platform platform, string gameId, string dlcId)>();
foreach (TreeNode node in TreeNodes) foreach (TreeNode node in TreeNodes)
if (node.Parent is TreeNode parent && node.Tag is Platform platform) if (node.Parent is { } parent && node.Tag is Platform platform)
{ {
if (node.Text == "Unknown" ? node.Checked : !node.Checked) if (node.Text == "Unknown" ? node.Checked : !node.Checked)
choices.Add((platform, node.Parent.Name, node.Name)); choices.Add((platform, node.Parent.Name, node.Name));
@ -1011,15 +996,13 @@ internal partial class SelectForm : CustomForm
saveButton.Enabled = CanSaveDlc(); saveButton.Enabled = CanSaveDlc();
} }
private static bool CanLoadDlc() => ProgramData.ReadDlcChoices() is not null; private static bool CanLoadDlc() => ProgramData.ReadDlcChoices().Any();
private void OnLoadDlc(object sender, EventArgs e) private void OnLoadDlc(object sender, EventArgs e)
{ {
List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices(); List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices().ToList();
if (choices is null)
return;
foreach (TreeNode node in TreeNodes) foreach (TreeNode node in TreeNodes)
if (node.Parent is TreeNode parent && node.Tag is Platform platform) if (node.Parent is { } parent && node.Tag is Platform platform)
{ {
node.Checked = choices.Any(c => c.platform == platform && c.gameId == parent.Name && c.dlcId == node.Name) node.Checked = choices.Any(c => c.platform == platform && c.gameId == parent.Name && c.dlcId == node.Name)
? node.Text == "Unknown" ? node.Text == "Unknown"
@ -1032,29 +1015,21 @@ internal partial class SelectForm : CustomForm
private void OnResetDlc(object sender, EventArgs e) private void OnResetDlc(object sender, EventArgs e)
{ {
foreach (TreeNode node in TreeNodes) foreach (TreeNode node in TreeNodes.Where(node => node.Parent is not null && node.Tag is Platform))
if (node.Parent is not null && node.Tag is Platform) {
{ node.Checked = node.Text != "Unknown";
node.Checked = node.Text != "Unknown"; OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse)); }
}
resetButton.Enabled = CanResetDlc(); resetButton.Enabled = CanResetDlc();
} }
private static bool AreKoaloaderSelectionsDefault() private static bool AreKoaloaderSelectionsDefault() => ProgramSelection.AllSafe.All(selection => selection.Koaloader && selection.KoaloaderProxy is null);
{
foreach (ProgramSelection selection in ProgramSelection.AllSafe)
if (!selection.Koaloader || selection.KoaloaderProxy is not null)
return false;
return true;
}
private static bool CanSaveKoaloader() => ProgramData.ReadKoaloaderChoices() is not null || !AreKoaloaderSelectionsDefault(); private static bool CanSaveKoaloader() => ProgramData.ReadKoaloaderChoices().Any() || !AreKoaloaderSelectionsDefault();
private void OnSaveKoaloader(object sender, EventArgs e) private void OnSaveKoaloader(object sender, EventArgs e)
{ {
List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices() List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices().ToList();
?? new List<(Platform platform, string id, string proxy, bool enabled)>();
foreach (ProgramSelection selection in ProgramSelection.AllSafe) foreach (ProgramSelection selection in ProgramSelection.AllSafe)
{ {
_ = choices.RemoveAll(c => c.platform == selection.Platform && c.id == selection.Id); _ = choices.RemoveAll(c => c.platform == selection.Platform && c.id == selection.Id);
@ -1067,13 +1042,11 @@ internal partial class SelectForm : CustomForm
loadKoaloaderButton.Enabled = CanLoadKoaloader(); loadKoaloaderButton.Enabled = CanLoadKoaloader();
} }
private static bool CanLoadKoaloader() => ProgramData.ReadKoaloaderChoices() is not null; private static bool CanLoadKoaloader() => ProgramData.ReadKoaloaderChoices().Any();
private void OnLoadKoaloader(object sender, EventArgs e) private void OnLoadKoaloader(object sender, EventArgs e)
{ {
List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices(); List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices().ToList();
if (choices is null)
return;
foreach (ProgramSelection selection in ProgramSelection.AllSafe) foreach (ProgramSelection selection in ProgramSelection.AllSafe)
if (choices.Any(c => c.platform == selection.Platform && c.id == selection.Id)) if (choices.Any(c => c.platform == selection.Platform && c.id == selection.Id))
{ {
@ -1135,25 +1108,23 @@ internal partial class SelectForm : CustomForm
{ {
StringBuilder blockedGames = new(); StringBuilder blockedGames = new();
foreach (string name in Program.ProtectedGames) foreach (string name in Program.ProtectedGames)
_ = blockedGames.Append(helpButtonListPrefix + name); _ = blockedGames.Append(HelpButtonListPrefix + name);
StringBuilder blockedDirectories = new(); StringBuilder blockedDirectories = new();
foreach (string path in Program.ProtectedGameDirectories) foreach (string path in Program.ProtectedGameDirectories)
_ = blockedDirectories.Append(helpButtonListPrefix + path); _ = blockedDirectories.Append(HelpButtonListPrefix + path);
StringBuilder blockedDirectoryExceptions = new(); StringBuilder blockedDirectoryExceptions = new();
foreach (string name in Program.ProtectedGameDirectoryExceptions) foreach (string name in Program.ProtectedGameDirectoryExceptions)
_ = blockedDirectoryExceptions.Append(helpButtonListPrefix + name); _ = blockedDirectoryExceptions.Append(HelpButtonListPrefix + name);
using DialogForm form = new(this); using DialogForm form = new(this);
_ = form.Show(SystemIcons.Information, _ = form.Show(SystemIcons.Information,
"Blocks the program from caching and displaying games protected by anti-cheats." "Blocks the program from caching and displaying games protected by anti-cheats."
+ "\nYou disable this option and install DLC unlockers to protected games at your own risk!" + "\n\nBlocked games: " + "\nYou disable this option and install DLC unlockers to protected games at your own risk!" + "\n\nBlocked games: "
+ (string.IsNullOrWhiteSpace(blockedGames.ToString()) ? "(none)" : blockedGames) + "\n\nBlocked game sub-directories: " + (string.IsNullOrWhiteSpace(blockedGames.ToString()) ? "(none)" : blockedGames) + "\n\nBlocked game sub-directories: "
+ (string.IsNullOrWhiteSpace(blockedDirectories.ToString()) ? "(none)" : blockedDirectories) + "\n\nBlocked game sub-directory exceptions: " + (string.IsNullOrWhiteSpace(blockedDirectories.ToString()) ? "(none)" : blockedDirectories) + "\n\nBlocked game sub-directory exceptions: "
+ (string.IsNullOrWhiteSpace(blockedDirectoryExceptions.ToString()) ? "(none)" : blockedDirectoryExceptions), "OK", + (string.IsNullOrWhiteSpace(blockedDirectoryExceptions.ToString()) ? "(none)" : blockedDirectoryExceptions),
customFormText: "Block Protected Games"); customFormText: "Block Protected Games");
} }
private void OnSortCheckBoxChanged(object sender, EventArgs e) private void OnSortCheckBoxChanged(object sender, EventArgs e)
=> selectionTreeView.TreeViewNodeSorter = sortCheckBox.Checked ? PlatformIdComparer.NodeText : PlatformIdComparer.NodeName; => selectionTreeView.TreeViewNodeSorter = sortCheckBox.Checked ? PlatformIdComparer.NodeText : PlatformIdComparer.NodeName;
} }
#pragma warning restore IDE0058

View file

@ -49,8 +49,10 @@ internal static class EpicLibrary
=> g.CatalogItemId == manifest.CatalogItemId && g.InstallLocation == manifest.InstallLocation)) => g.CatalogItemId == manifest.CatalogItemId && g.InstallLocation == manifest.InstallLocation))
games.Add(manifest); games.Add(manifest);
} }
catch { } catch
; {
// ignored
}
} }
return games; return games;
}); });

View file

@ -13,14 +13,14 @@ namespace CreamInstaller.Platforms.Epic;
internal static class EpicStore internal static class EpicStore
{ {
//private const int COOLDOWN_CATALOG_ITEM = 600; //private const int CooldownCatalogItem = 600;
/* need a method to query catalog items /* need a method to query catalog items
internal static async Task QueryCatalogItems(Manifest manifest) internal static async Task QueryCatalogItems(Manifest manifest)
{ {
}*/ }*/
private const int COOLDOWN_ENTITLEMENT = 600; private const int CooldownEntitlement = 600;
internal static async Task<List<(string id, string name, string product, string icon, string developer)>> QueryEntitlements(string categoryNamespace) internal static async Task<List<(string id, string name, string product, string icon, string developer)>> QueryEntitlements(string categoryNamespace)
{ {
@ -28,16 +28,19 @@ internal static class EpicStore
string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json"; string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json";
bool cachedExists = File.Exists(cacheFile); bool cachedExists = File.Exists(cacheFile);
Response response = null; Response response = null;
if (!cachedExists || ProgramData.CheckCooldown(categoryNamespace, COOLDOWN_ENTITLEMENT)) if (!cachedExists || ProgramData.CheckCooldown(categoryNamespace, CooldownEntitlement))
{ {
response = await QueryGraphQL(categoryNamespace); response = await QueryGraphQL(categoryNamespace);
try try
{ {
await File.WriteAllTextAsync(cacheFile, JsonConvert.SerializeObject(response, Formatting.Indented)); await File.WriteAllTextAsync(cacheFile, JsonConvert.SerializeObject(response, Formatting.Indented));
} }
catch { } catch
{
// ignored
}
} }
else if (cachedExists) else
try try
{ {
response = JsonConvert.DeserializeObject<Response>(await File.ReadAllTextAsync(cacheFile)); response = JsonConvert.DeserializeObject<Response>(await File.ReadAllTextAsync(cacheFile));
@ -57,11 +60,10 @@ internal static class EpicStore
for (int i = 0; i < element.KeyImages?.Length; i++) for (int i = 0; i < element.KeyImages?.Length; i++)
{ {
KeyImage keyImage = element.KeyImages[i]; KeyImage keyImage = element.KeyImages[i];
if (keyImage.Type == "DieselStoreFront") if (keyImage.Type != "DieselStoreFront")
{ continue;
icon = keyImage.Url.ToString(); icon = keyImage.Url.ToString();
break; break;
}
} }
foreach (Item item in element.Items) foreach (Item item in element.Items)
dlcIds.Populate(item.Id, title, product, icon, null, element.Items.Length == 1); dlcIds.Populate(item.Id, title, product, icon, null, element.Items.Length == 1);
@ -75,11 +77,10 @@ internal static class EpicStore
for (int i = 0; i < element.KeyImages?.Length; i++) for (int i = 0; i < element.KeyImages?.Length; i++)
{ {
KeyImage keyImage = element.KeyImages[i]; KeyImage keyImage = element.KeyImages[i];
if (keyImage.Type == "Thumbnail") if (keyImage.Type != "Thumbnail")
{ continue;
icon = keyImage.Url.ToString(); icon = keyImage.Url.ToString();
break; break;
}
} }
foreach (Item item in element.Items) foreach (Item item in element.Items)
dlcIds.Populate(item.Id, title, product, icon, item.Developer, element.Items.Length == 1); dlcIds.Populate(item.Id, title, product, icon, item.Developer, element.Items.Length == 1);
@ -96,14 +97,13 @@ internal static class EpicStore
for (int i = 0; i < dlcIds.Count; i++) for (int i = 0; i < dlcIds.Count; i++)
{ {
(string id, string name, string product, string icon, string developer) app = dlcIds[i]; (string id, string name, string product, string icon, string developer) app = dlcIds[i];
if (app.id == id) if (app.id != id)
{ continue;
found = true; found = true;
dlcIds[i] = canOverwrite dlcIds[i] = canOverwrite
? (app.id, title ?? app.name, product ?? app.product, icon ?? app.icon, developer ?? app.developer) ? (app.id, title ?? app.name, product ?? app.product, icon ?? app.icon, developer ?? app.developer)
: (app.id, app.name ?? title, app.product ?? product, app.icon ?? icon, app.developer ?? developer); : (app.id, app.name ?? title, app.product ?? product, app.icon ?? icon, app.developer ?? developer);
break; break;
}
} }
if (!found) if (!found)
dlcIds.Add((id, title, product, icon, developer)); dlcIds.Add((id, title, product, icon, developer));
@ -131,4 +131,4 @@ internal static class EpicStore
return null; return null;
} }
} }
} }

View file

@ -7,7 +7,7 @@ using Newtonsoft.Json;
namespace CreamInstaller.Platforms.Epic.GraphQL; namespace CreamInstaller.Platforms.Epic.GraphQL;
internal class Request internal sealed class Request
{ {
internal Request(string @namespace) => Vars = new(@namespace); internal Request(string @namespace) => Vars = new(@namespace);
@ -61,13 +61,13 @@ internal class Request
[JsonProperty(PropertyName = "variables")] [JsonProperty(PropertyName = "variables")]
private Variables Vars { get; set; } private Variables Vars { get; set; }
private class Headers private sealed class Headers
{ {
[JsonProperty(PropertyName = "Content-Type")] [JsonProperty(PropertyName = "Content-Type")]
private string ContentType => "application/graphql"; private string ContentType => "application/graphql";
} }
private class Variables private sealed class Variables
{ {
internal Variables(string @namespace) => Namespace = @namespace; internal Variables(string @namespace) => Namespace = @namespace;

View file

@ -15,4 +15,4 @@ public class Manifest
public string MainGameCatalogNamespace { get; set; } public string MainGameCatalogNamespace { get; set; }
public string MainGameCatalogItemId { get; set; } public string MainGameCatalogItemId { get; set; }
} }

View file

@ -98,10 +98,12 @@ internal static class ParadoxLauncher
byte[] epicOriginalSdk64 = null; byte[] epicOriginalSdk64 = null;
foreach (string directory in selection.DllDirectories) foreach (string directory in selection.DllDirectories)
{ {
bool koaloaderInstalled = Koaloader.AutoLoadDlls.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll)) bool koaloaderInstalled = Koaloader.AutoLoadDLLs.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile()); .Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile());
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out _); directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string old_config,
smokeInstalled = smokeInstalled || File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled out string config, out _);
smokeInstalled = smokeInstalled || File.Exists(api32_o) || File.Exists(api64_o)
|| (File.Exists(config) || File.Exists(config)) && !koaloaderInstalled
|| File.Exists(api32) && api32.IsResourceFile(ResourceIdentifier.Steamworks32) || File.Exists(api32) && api32.IsResourceFile(ResourceIdentifier.Steamworks32)
|| File.Exists(api64) && api64.IsResourceFile(ResourceIdentifier.Steamworks64); || File.Exists(api64) && api64.IsResourceFile(ResourceIdentifier.Steamworks64);
await SmokeAPI.Uninstall(directory, deleteConfig: false); await SmokeAPI.Uninstall(directory, deleteConfig: false);
@ -125,7 +127,7 @@ internal static class ParadoxLauncher
bool neededRepair = false; bool neededRepair = false;
foreach (string directory in selection.DllDirectories) foreach (string directory in selection.DllDirectories)
{ {
directory.GetSmokeApiComponents(out string api32, out _, out string api64, out _, out _, out _); directory.GetSmokeApiComponents(out string api32, out _, out string api64, out _, out _, out _, out _);
if (steamOriginalSdk32 is not null && api32.IsResourceFile(ResourceIdentifier.Steamworks32)) if (steamOriginalSdk32 is not null && api32.IsResourceFile(ResourceIdentifier.Steamworks32))
{ {
steamOriginalSdk32.Write(api32); steamOriginalSdk32.Write(api32);
@ -161,13 +163,13 @@ internal static class ParadoxLauncher
if (installForm is not null) if (installForm is not null)
installForm.UpdateUser("Paradox Launcher successfully repaired!", LogTextBox.Success); installForm.UpdateUser("Paradox Launcher successfully repaired!", LogTextBox.Success);
else else
_ = dialogForm.Show(form.Icon, "Paradox Launcher successfully repaired!", "OK", customFormText: "Paradox Launcher"); _ = dialogForm.Show(form.Icon, "Paradox Launcher successfully repaired!", customFormText: "Paradox Launcher");
return RepairResult.Success; return RepairResult.Success;
} }
if (installForm is not null) if (installForm is not null)
installForm.UpdateUser("Paradox Launcher did not need to be repaired.", LogTextBox.Success); installForm.UpdateUser("Paradox Launcher did not need to be repaired.", LogTextBox.Success);
else else
_ = dialogForm.Show(SystemIcons.Information, "Paradox Launcher does not need to be repaired.", "OK", customFormText: "Paradox Launcher"); _ = dialogForm.Show(SystemIcons.Information, "Paradox Launcher does not need to be repaired.", customFormText: "Paradox Launcher");
return RepairResult.Unnecessary; return RepairResult.Unnecessary;
} }
_ = form is InstallForm _ = form is InstallForm
@ -175,8 +177,8 @@ internal static class ParadoxLauncher
+ "You will likely have to reinstall Paradox Launcher to fix this issue.") + "You will likely have to reinstall Paradox Launcher to fix this issue.")
: dialogForm.Show(SystemIcons.Error, : dialogForm.Show(SystemIcons.Error,
"Paradox Launcher repair failed!" + "\n\nAn original Steamworks and/or Epic Online Services file could not be found." "Paradox Launcher repair failed!" + "\n\nAn original Steamworks and/or Epic Online Services file could not be found."
+ "\nYou will likely have to reinstall Paradox Launcher to fix this issue.", "OK", + "\nYou will likely have to reinstall Paradox Launcher to fix this issue.",
customFormText: "Paradox Launcher"); customFormText: "Paradox Launcher");
return RepairResult.Failure; return RepairResult.Failure;
} }
} }

View file

@ -17,24 +17,24 @@ namespace CreamInstaller.Platforms.Steam;
internal static class SteamCMD internal static class SteamCMD
{ {
internal const int ProcessLimit = 20; private const int ProcessLimit = 20;
internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe"; private static readonly string FilePath = DirectoryPath + @"\steamcmd.exe";
private static readonly ConcurrentDictionary<string, int> AttemptCount = new(); // the more app_updates, the longer SteamCMD should wait for app_info_print private static readonly ConcurrentDictionary<string, int> AttemptCount = new(); // the more app_updates, the longer SteamCMD should wait for app_info_print
private static readonly int[] locks = new int[ProcessLimit]; private static readonly int[] Locks = new int[ProcessLimit];
internal static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip"; private static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip";
internal static readonly string DllPath = DirectoryPath + @"\steamclient.dll"; private static readonly string DllPath = DirectoryPath + @"\steamclient.dll";
internal static readonly string AppCachePath = DirectoryPath + @"\appcache"; private static readonly string AppCachePath = DirectoryPath + @"\appcache";
internal static readonly string ConfigPath = DirectoryPath + @"\config"; private static readonly string ConfigPath = DirectoryPath + @"\config";
internal static readonly string DumpsPath = DirectoryPath + @"\dumps"; private static readonly string DumpsPath = DirectoryPath + @"\dumps";
internal static readonly string LogsPath = DirectoryPath + @"\logs"; private static readonly string LogsPath = DirectoryPath + @"\logs";
internal static readonly string SteamAppsPath = DirectoryPath + @"\steamapps"; private static readonly string SteamAppsPath = DirectoryPath + @"\steamapps";
internal static string DirectoryPath => ProgramData.DirectoryPath; private static string DirectoryPath => ProgramData.DirectoryPath;
internal static string AppInfoPath => ProgramData.AppInfoPath; internal static string AppInfoPath => ProgramData.AppInfoPath;
private static string GetArguments(string appId) private static string GetArguments(string appId)
@ -43,19 +43,19 @@ internal static class SteamCMD
+ string.Concat(Enumerable.Repeat("+app_update 4 ", attempts)) + "+quit" + string.Concat(Enumerable.Repeat("+app_update 4 ", attempts)) + "+quit"
: $"+login anonymous +app_info_print {appId} +quit"; : $"+login anonymous +app_info_print {appId} +quit";
internal static async Task<string> Run(string appId) private static async Task<string> Run(string appId)
=> await Task.Run(() => => await Task.Run(() =>
{ {
wait_for_lock: wait_for_lock:
if (Program.Canceled) if (Program.Canceled)
return ""; return "";
for (int i = 0; i < locks.Length; i++) for (int i = 0; i < Locks.Length; i++)
{ {
if (Program.Canceled) if (Program.Canceled)
return ""; return "";
if (Interlocked.CompareExchange(ref locks[i], 1, 0) == 0) if (Interlocked.CompareExchange(ref Locks[i], 1, 0) == 0)
{ {
if (appId is not null) if (appId != null)
{ {
AttemptCount.TryGetValue(appId, out int count); AttemptCount.TryGetValue(appId, out int count);
AttemptCount[appId] = ++count; AttemptCount[appId] = ++count;
@ -69,11 +69,13 @@ internal static class SteamCMD
StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8
}; };
Process process = Process.Start(processStartInfo); Process process = Process.Start(processStartInfo);
if (appId == null)
return "";
StringBuilder output = new(); StringBuilder output = new();
StringBuilder appInfo = new(); StringBuilder appInfo = new();
bool appInfoStarted = false; bool appInfoStarted = false;
DateTime lastOutput = DateTime.UtcNow; DateTime lastOutput = DateTime.UtcNow;
while (true) while (process != null)
{ {
if (Program.Canceled) if (Program.Canceled)
{ {
@ -92,24 +94,23 @@ internal static class SteamCMD
} }
DateTime now = DateTime.UtcNow; DateTime now = DateTime.UtcNow;
TimeSpan timeDiff = now - lastOutput; TimeSpan timeDiff = now - lastOutput;
if (timeDiff.TotalSeconds > 0.1) if (!(timeDiff.TotalSeconds > 0.1))
continue;
process.Kill(true);
process.Close();
if (output.ToString().Contains($"No app info for AppID {appId} found, requesting..."))
{ {
process.Kill(true); AttemptCount[appId]++;
process.Close(); processStartInfo.Arguments = GetArguments(appId);
if (output.ToString().Contains($"No app info for AppID {appId} found, requesting...")) process = Process.Start(processStartInfo);
{ appInfoStarted = false;
AttemptCount[appId]++; _ = output.Clear();
processStartInfo.Arguments = GetArguments(appId); _ = appInfo.Clear();
process = Process.Start(processStartInfo);
appInfoStarted = false;
_ = output.Clear();
_ = appInfo.Clear();
}
else
break;
} }
else
break;
} }
_ = Interlocked.Decrement(ref locks[i]); _ = Interlocked.Decrement(ref Locks[i]);
return appInfo.ToString(); return appInfo.ToString();
} }
Thread.Sleep(200); Thread.Sleep(200);
@ -174,7 +175,10 @@ internal static class SteamCMD
if (Directory.Exists(SteamAppsPath)) if (Directory.Exists(SteamAppsPath))
Directory.Delete(SteamAppsPath, true); // this is just a useless folder created from +app_update 4 Directory.Delete(SteamAppsPath, true); // this is just a useless folder created from +app_update 4
} }
catch { } catch
{
// ignored
}
}); });
internal static async Task<VProperty> GetAppInfo(string appId, string branch = "public", int buildId = 0) internal static async Task<VProperty> GetAppInfo(string appId, string branch = "public", int buildId = 0)
@ -223,12 +227,12 @@ internal static class SteamCMD
File.Delete(appUpdateFile); File.Delete(appUpdateFile);
goto restart; goto restart;
} }
if (appInfo is null || appInfo.Value?.Children()?.ToList()?.Count == 0) if (appInfo.Value.Children().ToList().Count == 0)
return appInfo; return appInfo;
VToken type = appInfo.Value?.GetChild("common")?.GetChild("type"); VToken type = appInfo.Value.GetChild("common")?.GetChild("type");
if (type is not null && type.ToString() != "Game") if (type is not null && type.ToString() != "Game")
return appInfo; return appInfo;
string buildid = appInfo.Value?.GetChild("depots")?.GetChild("branches")?.GetChild(branch)?.GetChild("buildid")?.ToString(); string buildid = appInfo.Value.GetChild("depots")?.GetChild("branches")?.GetChild(branch)?.GetChild("buildid")?.ToString();
if (buildid is null && type is not null) if (buildid is null && type is not null)
return appInfo; return appInfo;
if (type is not null && (!int.TryParse(buildid, out int gamebuildId) || gamebuildId >= buildId)) if (type is not null && (!int.TryParse(buildid, out int gamebuildId) || gamebuildId >= buildId))
@ -279,7 +283,10 @@ internal static class SteamCMD
process.WaitForExit(); process.WaitForExit();
process.Close(); process.Close();
} }
catch { } catch
{
// ignored
}
})).ToList(); })).ToList();
foreach (Task task in tasks) foreach (Task task in tasks)
await task; await task;
@ -291,4 +298,4 @@ internal static class SteamCMD
if (Directory.Exists(DirectoryPath)) if (Directory.Exists(DirectoryPath))
Directory.Delete(DirectoryPath, true); Directory.Delete(DirectoryPath, true);
} }
} }

View file

@ -110,4 +110,4 @@ internal static class SteamLibrary
} }
return gameDirectories; return gameDirectories;
}); });
} }

View file

@ -124,4 +124,4 @@ internal static class SteamStore
} }
return null; return null;
} }
} }

View file

@ -13,7 +13,10 @@ internal static class ValveDataFile
result = VdfConvert.Deserialize(value); result = VdfConvert.Deserialize(value);
return true; return true;
} }
catch { } catch
{
// ignored
}
return false; return false;
} }
@ -23,7 +26,10 @@ internal static class ValveDataFile
{ {
return token[index]; return token[index];
} }
catch { } catch
{
// ignored
}
return null; return null;
} }
} }

View file

@ -41,4 +41,4 @@ internal static class UbisoftLibrary
} }
return games; return games;
}); });
} }

View file

@ -15,7 +15,7 @@ namespace CreamInstaller;
internal static class Program internal static class Program
{ {
internal static readonly string Name = Application.CompanyName; internal static readonly string Name = Application.CompanyName;
internal static readonly string Description = Application.ProductName; private static readonly string Description = Application.ProductName;
internal static readonly string Version = Application.ProductVersion; internal static readonly string Version = Application.ProductVersion;
internal const string RepositoryOwner = "pointfeev"; internal const string RepositoryOwner = "pointfeev";
@ -32,8 +32,8 @@ internal static class Program
#endif #endif
internal static readonly Assembly EntryAssembly = Assembly.GetEntryAssembly(); internal static readonly Assembly EntryAssembly = Assembly.GetEntryAssembly();
internal static readonly Process CurrentProcess = Process.GetCurrentProcess(); private static readonly Process CurrentProcess = Process.GetCurrentProcess();
internal static readonly string CurrentProcessFilePath = CurrentProcess.MainModule.FileName; internal static readonly string CurrentProcessFilePath = CurrentProcess.MainModule?.FileName;
internal static bool BlockProtectedGames = true; internal static bool BlockProtectedGames = true;
internal static readonly string[] ProtectedGames = { "PAYDAY 2" }; internal static readonly string[] ProtectedGames = { "PAYDAY 2" };
@ -46,25 +46,27 @@ internal static class Program
return false; return false;
if (ProtectedGames.Contains(name)) if (ProtectedGames.Contains(name))
return true; return true;
if (directory is not null && !ProtectedGameDirectoryExceptions.Contains(name)) if (directory is null || ProtectedGameDirectoryExceptions.Contains(name))
foreach (string path in ProtectedGameDirectories) return false;
if (Directory.Exists(directory + path)) return ProtectedGameDirectories.Any(path => Directory.Exists(directory + path));
return true;
return false;
} }
internal static bool IsProgramRunningDialog(Form form, ProgramSelection selection) internal static bool IsProgramRunningDialog(Form form, ProgramSelection selection)
{ {
if (selection.AreDllsLocked) while (true)
{ {
using DialogForm dialogForm = new(form); if (selection.AreDllsLocked)
if (dialogForm.Show(SystemIcons.Error, $"ERROR: {selection.Name} is currently running!" + "\n\nPlease close the program/game to continue . . . ", {
"Retry", "Cancel") == DialogResult.OK) using DialogForm dialogForm = new(form);
return IsProgramRunningDialog(form, selection); if (dialogForm.Show(SystemIcons.Error,
$"ERROR: {selection.Name} is currently running!" + "\n\nPlease close the program/game to continue . . . ", "Retry", "Cancel")
== DialogResult.OK)
continue;
}
else
return true;
return false;
} }
else
return true;
return false;
} }
[STAThread] [STAThread]
@ -114,4 +116,4 @@ internal static class Program
Cleanup(); Cleanup();
HttpClientManager.Dispose(); HttpClientManager.Dispose();
} }
} }

View file

@ -19,7 +19,7 @@ public enum DlcType
EpicEntitlement EpicEntitlement
} }
internal class ProgramSelection internal sealed class ProgramSelection
{ {
internal const string DefaultKoaloaderProxy = "version"; internal const string DefaultKoaloaderProxy = "version";
@ -68,9 +68,9 @@ internal class ProgramSelection
if (api32.IsFilePathLocked() || api32_o.IsFilePathLocked() || api64.IsFilePathLocked() || api64_o.IsFilePathLocked() if (api32.IsFilePathLocked() || api32_o.IsFilePathLocked() || api64.IsFilePathLocked() || api64_o.IsFilePathLocked()
|| config.IsFilePathLocked()) || config.IsFilePathLocked())
return true; return true;
directory.GetSmokeApiComponents(out api32, out api32_o, out api64, out api64_o, out config, out string cache); directory.GetSmokeApiComponents(out api32, out api32_o, out api64, out api64_o, out string old_config, out config, out string cache);
if (api32.IsFilePathLocked() || api32_o.IsFilePathLocked() || api64.IsFilePathLocked() || api64_o.IsFilePathLocked() if (api32.IsFilePathLocked() || api32_o.IsFilePathLocked() || api64.IsFilePathLocked() || api64_o.IsFilePathLocked()
|| config.IsFilePathLocked() || cache.IsFilePathLocked()) || old_config.IsFilePathLocked() || config.IsFilePathLocked() || cache.IsFilePathLocked())
return true; return true;
} }
if (Platform is Platform.Epic or Platform.Paradox) if (Platform is Platform.Epic or Platform.Paradox)
@ -114,16 +114,15 @@ internal class ProgramSelection
{ {
string appId = pair.Key; string appId = pair.Key;
(DlcType type, string name, string icon) dlcApp = pair.Value; (DlcType type, string name, string icon) dlcApp = pair.Value;
if (appId == dlcId) if (appId != dlcId)
{ continue;
Toggle(appId, dlcApp, enabled); Toggle(appId, dlcApp, enabled);
break; break;
}
} }
Enabled = SelectedDlc.Any() || ExtraSelectedDlc.Any(); Enabled = SelectedDlc.Any() || ExtraSelectedDlc.Any();
} }
internal void Validate() private void Validate()
{ {
if (Program.IsGameBlocked(Name, RootDirectory)) if (Program.IsGameBlocked(Name, RootDirectory))
{ {
@ -140,7 +139,7 @@ internal class ProgramSelection
_ = All.Remove(this); _ = All.Remove(this);
} }
internal void Validate(List<(Platform platform, string id, string name)> programsToScan) private void Validate(List<(Platform platform, string id, string name)> programsToScan)
{ {
if (programsToScan is null || !programsToScan.Any(p => p.platform == Platform && p.id == Id)) if (programsToScan is null || !programsToScan.Any(p => p.platform == Platform && p.id == Id))
{ {
@ -164,4 +163,4 @@ internal class ProgramSelection
return (selection.Id, pair.Value); return (selection.Id, pair.Value);
return null; return null;
} }
} }

View file

@ -12,7 +12,7 @@ namespace CreamInstaller.Resources;
internal static class Koaloader internal static class Koaloader
{ {
internal static readonly List<(string unlocker, string dll)> AutoLoadDlls = new() internal static readonly List<(string unlocker, string dll)> AutoLoadDLLs = new()
{ {
("Koaloader", "Unlocker.dll"), ("Koaloader", "Unlocker32.dll"), ("Koaloader", "Unlocker64.dll"), ("Lyptus", "Lyptus.dll"), ("Koaloader", "Unlocker.dll"), ("Koaloader", "Unlocker32.dll"), ("Koaloader", "Unlocker64.dll"), ("Lyptus", "Lyptus.dll"),
("Lyptus", "Lyptus32.dll"), ("Lyptus", "Lyptus64.dll"), ("SmokeAPI", "SmokeAPI.dll"), ("SmokeAPI", "SmokeAPI32.dll"), ("Lyptus", "Lyptus32.dll"), ("Lyptus", "Lyptus64.dll"), ("SmokeAPI", "SmokeAPI.dll"), ("SmokeAPI", "SmokeAPI32.dll"),
@ -21,28 +21,26 @@ internal static class Koaloader
("Uplay R2 Unlocker", "UplayR2Unlocker.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker32.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker64.dll") ("Uplay R2 Unlocker", "UplayR2Unlocker.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker32.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker64.dll")
}; };
internal static void GetKoaloaderComponents(this string directory, out List<string> proxies, out string config) internal static void GetKoaloaderComponents(this string directory, out List<string> proxies, out string old_config, out string config)
{ {
proxies = new(); proxies = EmbeddedResources.Select(proxy =>
foreach (string proxy in EmbeddedResources.Select(proxy =>
{ {
proxy = proxy[(proxy.IndexOf('.') + 1)..]; proxy = proxy[(proxy.IndexOf('.') + 1)..];
return proxy[(proxy.IndexOf('.') + 1)..]; return proxy[(proxy.IndexOf('.') + 1)..];
})) }).Select(proxy => directory + @"\" + proxy).ToList();
proxies.Add(directory + @"\" + proxy); old_config = directory + @"\Koaloader.json";
config = directory + @"\Koaloader.json"; config = directory + @"\Koaloader.config.json";
} }
internal static void WriteProxy(this string path, string proxyName, BinaryType binaryType) private static void WriteProxy(this string path, string proxyName, BinaryType binaryType)
{ {
foreach (string resourceIdentifier in EmbeddedResources.FindAll(r => r.StartsWith("Koaloader"))) foreach (string resourceIdentifier in EmbeddedResources.FindAll(r => r.StartsWith("Koaloader")))
{ {
resourceIdentifier.GetProxyInfoFromIdentifier(out string _proxyName, out BinaryType _binaryType); resourceIdentifier.GetProxyInfoFromIdentifier(out string _proxyName, out BinaryType _binaryType);
if (_proxyName == proxyName && _binaryType == binaryType) if (_proxyName != proxyName || _binaryType != binaryType)
{ continue;
resourceIdentifier.Write(path); resourceIdentifier.Write(path);
break; break;
}
} }
} }
@ -52,16 +50,17 @@ internal static class Koaloader
baseIdentifier = baseIdentifier[..baseIdentifier.IndexOf('.')]; baseIdentifier = baseIdentifier[..baseIdentifier.IndexOf('.')];
proxyName = baseIdentifier[..baseIdentifier.LastIndexOf('_')]; proxyName = baseIdentifier[..baseIdentifier.LastIndexOf('_')];
string bitness = baseIdentifier[(baseIdentifier.LastIndexOf('_') + 1)..]; string bitness = baseIdentifier[(baseIdentifier.LastIndexOf('_') + 1)..];
binaryType = bitness == "32" binaryType = bitness switch { "32" => BinaryType.BIT32, "64" => BinaryType.BIT64, _ => BinaryType.Unknown };
? BinaryType.BIT32
: bitness == "64"
? BinaryType.BIT64
: BinaryType.Unknown;
} }
internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null) private static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null)
{ {
directory.GetKoaloaderComponents(out _, out string config); directory.GetKoaloaderComponents(out _, out string old_config, out string config);
if (File.Exists(old_config))
{
File.Delete(old_config);
installForm?.UpdateUser($"Deleted old configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, false);
}
SortedList<string, string> targets = new(PlatformIdComparer.String); SortedList<string, string> targets = new(PlatformIdComparer.String);
SortedList<string, string> modules = new(PlatformIdComparer.String); SortedList<string, string> modules = new(PlatformIdComparer.String);
if (targets.Any() || modules.Any()) if (targets.Any() || modules.Any())
@ -81,8 +80,7 @@ internal static class Koaloader
} }
} }
internal static void WriteConfig(StreamWriter writer, SortedList<string, string> targets, SortedList<string, string> modules, private static void WriteConfig(StreamWriter writer, SortedList<string, string> targets, SortedList<string, string> modules, InstallForm installForm = null)
InstallForm installForm = null)
{ {
writer.WriteLine("{"); writer.WriteLine("{");
writer.WriteLine(" \"logging\": false,"); writer.WriteLine(" \"logging\": false,");
@ -125,18 +123,23 @@ internal static class Koaloader
internal static async Task Uninstall(string directory, string rootDirectory = null, InstallForm installForm = null, bool deleteConfig = true) internal static async Task Uninstall(string directory, string rootDirectory = null, InstallForm installForm = null, bool deleteConfig = true)
=> await Task.Run(async () => => await Task.Run(async () =>
{ {
directory.GetKoaloaderComponents(out List<string> proxies, out string config); directory.GetKoaloaderComponents(out List<string> proxies, out string old_config, out string config);
foreach (string proxyPath in proxies.Where(proxyPath => File.Exists(proxyPath) && proxyPath.IsResourceFile(ResourceIdentifier.Koaloader))) foreach (string proxyPath in proxies.Where(proxyPath => File.Exists(proxyPath) && proxyPath.IsResourceFile(ResourceIdentifier.Koaloader)))
{ {
File.Delete(proxyPath); File.Delete(proxyPath);
installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(proxyPath)}", LogTextBox.Action, false); installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(proxyPath)}", LogTextBox.Action, false);
} }
foreach ((string unlocker, string path) in AutoLoadDlls.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll)) foreach ((string unlocker, string path) in AutoLoadDLLs.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Where(pair => File.Exists(pair.path) && pair.path.IsResourceFile())) .Where(pair => File.Exists(pair.path) && pair.path.IsResourceFile()))
{ {
File.Delete(path); File.Delete(path);
installForm?.UpdateUser($"Deleted {unlocker}: {Path.GetFileName(path)}", LogTextBox.Action, false); installForm?.UpdateUser($"Deleted {unlocker}: {Path.GetFileName(path)}", LogTextBox.Action, false);
} }
if (deleteConfig && File.Exists(old_config))
{
File.Delete(old_config);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, false);
}
if (deleteConfig && File.Exists(config)) if (deleteConfig && File.Exists(config))
{ {
File.Delete(config); File.Delete(config);
@ -154,7 +157,7 @@ internal static class Koaloader
InstallForm installForm = null, bool generateConfig = true) InstallForm installForm = null, bool generateConfig = true)
=> await Task.Run(() => => await Task.Run(() =>
{ {
directory.GetKoaloaderComponents(out List<string> proxies, out string config); directory.GetKoaloaderComponents(out List<string> proxies, out _, out _);
string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy; string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy;
string path = directory + @"\" + proxy + ".dll"; string path = directory + @"\" + proxy + ".dll";
foreach (string _path in proxies.Where(p => p != path && File.Exists(p) && p.IsResourceFile(ResourceIdentifier.Koaloader))) foreach (string _path in proxies.Where(p => p != path && File.Exists(p) && p.IsResourceFile(ResourceIdentifier.Koaloader)))
@ -171,10 +174,15 @@ internal static class Koaloader
foreach (string executable in Directory.EnumerateFiles(directory, "*.exe")) foreach (string executable in Directory.EnumerateFiles(directory, "*.exe"))
if (executable.TryGetFileBinaryType(out BinaryType binaryType)) if (executable.TryGetFileBinaryType(out BinaryType binaryType))
{ {
if (binaryType == BinaryType.BIT32) switch (binaryType)
bit32 = true; {
else if (binaryType == BinaryType.BIT64) case BinaryType.BIT32:
bit64 = true; bit32 = true;
break;
case BinaryType.BIT64:
bit64 = true;
break;
}
if (bit32 && bit64) if (bit32 && bit64)
break; break;
} }
@ -216,118 +224,127 @@ internal static class Koaloader
} }
SmokeAPI.CheckConfig(rootDirectory ?? directory, selection, installForm); SmokeAPI.CheckConfig(rootDirectory ?? directory, selection, installForm);
} }
if (selection.Platform is Platform.Epic or Platform.Paradox) switch (selection.Platform)
{ {
if (bit32) case Platform.Epic or Platform.Paradox:
{ {
path = directory + @"\ScreamAPI32.dll"; if (bit32)
if (rootDirectory is not null && directory != rootDirectory)
{ {
if (File.Exists(path)) path = directory + @"\ScreamAPI32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
File.Delete(path); if (File.Exists(path))
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false); {
File.Delete(path);
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
path = rootDirectory + @"\ScreamAPI32.dll";
} }
path = rootDirectory + @"\ScreamAPI32.dll"; "ScreamAPI.EOSSDK-Win32-Shipping.dll".Write(path);
installForm?.UpdateUser(
$"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
"ScreamAPI.EOSSDK-Win32-Shipping.dll".Write(path); if (bit64)
installForm?.UpdateUser( {
$"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}", path = directory + @"\ScreamAPI64.dll";
LogTextBox.Action, false); if (rootDirectory is not null && directory != rootDirectory)
{
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
path = rootDirectory + @"\ScreamAPI64.dll";
}
"ScreamAPI.EOSSDK-Win64-Shipping.dll".Write(path);
installForm?.UpdateUser(
$"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
ScreamAPI.CheckConfig(rootDirectory ?? directory, selection, installForm);
break;
} }
if (bit64) case Platform.Ubisoft:
{ {
path = directory + @"\ScreamAPI64.dll"; if (bit32)
if (rootDirectory is not null && directory != rootDirectory)
{ {
if (File.Exists(path)) path = directory + @"\UplayR1Unlocker32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
File.Delete(path); if (File.Exists(path))
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false); {
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action,
false);
}
path = rootDirectory + @"\UplayR1Unlocker32.dll";
} }
path = rootDirectory + @"\ScreamAPI64.dll"; "UplayR1.uplay_r1_loader.dll".Write(path);
installForm?.UpdateUser(
$"Wrote Uplay R1 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
"ScreamAPI.EOSSDK-Win64-Shipping.dll".Write(path); if (bit64)
installForm?.UpdateUser(
$"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
ScreamAPI.CheckConfig(rootDirectory ?? directory, selection, installForm);
}
if (selection.Platform is Platform.Ubisoft)
{
if (bit32)
{
path = directory + @"\UplayR1Unlocker32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
if (File.Exists(path)) path = directory + @"\UplayR1Unlocker64.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
File.Delete(path); if (File.Exists(path))
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false); {
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action,
false);
}
path = rootDirectory + @"\UplayR1Unlocker64.dll";
} }
path = rootDirectory + @"\UplayR1Unlocker32.dll"; "UplayR1.uplay_r1_loader64.dll".Write(path);
installForm?.UpdateUser(
$"Wrote Uplay R1 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
"UplayR1.uplay_r1_loader.dll".Write(path); UplayR1.CheckConfig(rootDirectory ?? directory, selection, installForm);
installForm?.UpdateUser( if (bit32)
$"Wrote Uplay R1 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
if (bit64)
{
path = directory + @"\UplayR1Unlocker64.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
if (File.Exists(path)) path = directory + @"\UplayR2Unlocker32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
File.Delete(path); if (File.Exists(path))
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false); {
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action,
false);
}
path = rootDirectory + @"\UplayR2Unlocker32.dll";
} }
path = rootDirectory + @"\UplayR1Unlocker64.dll"; "UplayR2.upc_r2_loader.dll".Write(path);
installForm?.UpdateUser(
$"Wrote Uplay R2 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
"UplayR1.uplay_r1_loader64.dll".Write(path); if (bit64)
installForm?.UpdateUser(
$"Wrote Uplay R1 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
UplayR1.CheckConfig(rootDirectory ?? directory, selection, installForm);
if (bit32)
{
path = directory + @"\UplayR2Unlocker32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
if (File.Exists(path)) path = directory + @"\UplayR2Unlocker64.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
File.Delete(path); if (File.Exists(path))
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false); {
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action,
false);
}
path = rootDirectory + @"\UplayR2Unlocker64.dll";
} }
path = rootDirectory + @"\UplayR2Unlocker32.dll"; "UplayR2.upc_r2_loader64.dll".Write(path);
installForm?.UpdateUser(
$"Wrote Uplay R2 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
"UplayR2.upc_r2_loader.dll".Write(path); UplayR2.CheckConfig(rootDirectory ?? directory, selection, installForm);
installForm?.UpdateUser( break;
$"Wrote Uplay R2 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
if (bit64)
{
path = directory + @"\UplayR2Unlocker64.dll";
if (rootDirectory is not null && directory != rootDirectory)
{
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
path = rootDirectory + @"\UplayR2Unlocker64.dll";
}
"UplayR2.upc_r2_loader64.dll".Write(path);
installForm?.UpdateUser(
$"Wrote Uplay R2 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
UplayR2.CheckConfig(rootDirectory ?? directory, selection, installForm);
} }
if (generateConfig) if (generateConfig)
CheckConfig(directory, selection, installForm); CheckConfig(directory, selection, installForm);
}); });
} }

View file

@ -13,9 +13,9 @@ namespace CreamInstaller.Resources;
internal static class Resources internal static class Resources
{ {
internal static List<string> embeddedResources; private static List<string> embeddedResources;
internal static readonly Dictionary<ResourceIdentifier, IReadOnlyList<string>> ResourceMD5s = new() private static readonly Dictionary<ResourceIdentifier, IReadOnlyList<string>> ResourceMD5s = new()
{ {
{ {
ResourceIdentifier.Koaloader, new List<string> ResourceIdentifier.Koaloader, new List<string>
@ -263,7 +263,51 @@ internal static class Resources
"76CAB00C7DD33FC19F7CDD1849FF9CA2", // Koaloader v2.4.0 "76CAB00C7DD33FC19F7CDD1849FF9CA2", // Koaloader v2.4.0
"DA4D6A7C0872757A74DDAE05A2C1D160", // Koaloader v2.4.0 "DA4D6A7C0872757A74DDAE05A2C1D160", // Koaloader v2.4.0
"1F46DE8747C0A157841AFFE6185CE4C9", // Koaloader v2.4.0 "1F46DE8747C0A157841AFFE6185CE4C9", // Koaloader v2.4.0
"BE16B588D018D8EFF1F3B6A600F26BED" // Koaloader v2.4.0 "BE16B588D018D8EFF1F3B6A600F26BED", // Koaloader v2.4.0
"4633C8CD34B05138C5FE4B8950D18A4F", // Koaloader v3.0.1
"B8FDA04A5C46AAE8701A332275FA1D79", // Koaloader v3.0.1
"1C82C832029D12FA8AF25931C0B30A51", // Koaloader v3.0.1
"2AD8B1B70AB1763F612DFFE6BA95C786", // Koaloader v3.0.1
"7D05AE4D30C175BA1579C141DDC8A6EA", // Koaloader v3.0.1
"BF2BD33D755E7D5BE7262F528F7D2892", // Koaloader v3.0.1
"DB00C89FF7ED4E3EF7A3222BDF339A8F", // Koaloader v3.0.1
"27EFBABFACA05C95F548AA1BCA2C35D8", // Koaloader v3.0.1
"CF676B825204D41B5A1461990146C0AA", // Koaloader v3.0.1
"9D4BFD2814B62AB466B11B6740A8C003", // Koaloader v3.0.1
"54F4593C319223AFEB1A3ECAC3EB5FD2", // Koaloader v3.0.1
"158425881AE6A4DC398579E7589EFCF8", // Koaloader v3.0.1
"78E94DF180F264044C07EA7D279058A3", // Koaloader v3.0.1
"94FB4AF523BB8D553D926590FA8C4F0A", // Koaloader v3.0.1
"63AAAA347EB9D4699CA745A539647356", // Koaloader v3.0.1
"1B660B7CC1EB4318B7FC5C2B9D1DF6AD", // Koaloader v3.0.1
"805AFEEE7DF85B3019ACD0C4329AAADD", // Koaloader v3.0.1
"421567BD7E44A6A3CD8CBE529AED6BB9", // Koaloader v3.0.1
"A4769BB227D64337E097FE176CB3DA78", // Koaloader v3.0.1
"F03C50515A9FA6B35CF4608577B77D5E", // Koaloader v3.0.1
"E606329ED2593839BA479E948640E515", // Koaloader v3.0.1
"2A0ABCDC9CF3AC598893D823A188A2AE", // Koaloader v3.0.1
"FC8F96E934B7275077B92C1EA59186AC", // Koaloader v3.0.1
"06F41AB13C803D0680BBDA231A696795", // Koaloader v3.0.1
"D1106C578EE1AA7870CEFD1A06DD57C4", // Koaloader v3.0.1
"64C7F3CE83EC5558B3DA2A749122D711", // Koaloader v3.0.1
"90556EF98B420EFE3DA97A4BB1141095", // Koaloader v3.0.1
"5CEA22F2E663C53ACC6EA80B40789619", // Koaloader v3.0.1
"414528403EC318912B424E1984BA4D48", // Koaloader v3.0.1
"28CA4DA6C30E69A255234BD3C78E2AD1", // Koaloader v3.0.1
"CBE2786E9A493ACDEB4E3276D355EBEC", // Koaloader v3.0.1
"2E1E0FAD1EC473DC750636D4C565BD62", // Koaloader v3.0.1
"D35E4F7BCE7F909F75B5C6CADF962F54", // Koaloader v3.0.1
"184323CD159F9C1883F5276977B543BF", // Koaloader v3.0.1
"328258F4E16803BD5FE4100B716A3968", // Koaloader v3.0.1
"9F250DEEC8AE1CE49CC91176B5BA3EAC", // Koaloader v3.0.1
"F39C05830A7E405990619191A2881C87", // Koaloader v3.0.1
"2EED18EC00C83E3756F8A6154BB44817", // Koaloader v3.0.1
"F63362E4B1CAABAEC0255BEC78E9EE66", // Koaloader v3.0.1
"F77C655EB7A7892DBF0A3591E07E7A00", // Koaloader v3.0.1
"4AF5004DDBBD93C21440430255EAF9F3", // Koaloader v3.0.1
"E68CFB48E827A0BA486CB900B0A6B24F", // Koaloader v3.0.1
"F395ADCA7D27C28121D1AE2C19DDBD6B", // Koaloader v3.0.1
"CBB805C763AF199AF2DB35B265A4FF15" // Koaloader v3.0.1
} }
}, },
{ {
@ -287,7 +331,8 @@ internal static class Resources
"B2434578957CBE38BDCE0A671C1262FC", // SmokeAPI v1.0.0 "B2434578957CBE38BDCE0A671C1262FC", // SmokeAPI v1.0.0
"973AB1632B747D4BF3B2666F32E34327", // SmokeAPI v1.0.1 "973AB1632B747D4BF3B2666F32E34327", // SmokeAPI v1.0.1
"C7E41F569FC6A347D67D2BFB2BD10F25", // SmokeAPI v1.0.2 "C7E41F569FC6A347D67D2BFB2BD10F25", // SmokeAPI v1.0.2
"F9E7D5B248B86D1C2F2F2905A9F37755" // SmokeAPI v1.0.3 "F9E7D5B248B86D1C2F2F2905A9F37755", // SmokeAPI v1.0.3
"FD9032CCF73E3A4D7E187F35388BD569" // SmokeAPI v2.0.0-rc01
} }
}, },
{ {
@ -297,7 +342,8 @@ internal static class Resources
"08713035CAD6F52548FF324D0487B88D", // SmokeAPI v1.0.0 "08713035CAD6F52548FF324D0487B88D", // SmokeAPI v1.0.0
"D077737B9979D32458AC938A2978FA3C", // SmokeAPI v1.0.1 "D077737B9979D32458AC938A2978FA3C", // SmokeAPI v1.0.1
"49122A2E2E51CBB0AE5E1D59B280E4CD", // SmokeAPI v1.0.2 "49122A2E2E51CBB0AE5E1D59B280E4CD", // SmokeAPI v1.0.2
"13F3E9476116F7670E21365A400357AC" // SmokeAPI v1.0.3 "13F3E9476116F7670E21365A400357AC", // SmokeAPI v1.0.3
"151D09637E54A6DF281EAC5A9C484616" // SmokeAPI v2.0.0-rc01
} }
}, },
{ {
@ -332,13 +378,12 @@ internal static class Resources
{ {
get get
{ {
if (embeddedResources is null) if (embeddedResources is not null)
{ return embeddedResources;
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames(); string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
embeddedResources = new(); embeddedResources = new();
foreach (string resourceName in names.Where(n => n.StartsWith("CreamInstaller.Resources."))) foreach (string resourceName in names.Where(n => n.StartsWith("CreamInstaller.Resources.")))
embeddedResources.Add(resourceName[25..]); embeddedResources.Add(resourceName[25..]);
}
return embeddedResources; return embeddedResources;
} }
} }
@ -347,7 +392,7 @@ internal static class Resources
{ {
using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier); using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier);
using FileStream file = new(filePath, FileMode.Create, FileAccess.Write); using FileStream file = new(filePath, FileMode.Create, FileAccess.Write);
resource.CopyTo(file); resource?.CopyTo(file);
} }
internal static void Write(this byte[] resource, string filePath) internal static void Write(this byte[] resource, string filePath)
@ -419,7 +464,7 @@ internal static class Resources
return !executables.Any() ? null : executables; return !executables.Any() ? null : executables;
}); });
internal static bool IsCommonIncorrectExecutable(this string rootDirectory, string path) private static bool IsCommonIncorrectExecutable(this string rootDirectory, string path)
{ {
string subPath = path[rootDirectory.Length..].ToUpperInvariant().BeautifyPath(); string subPath = path[rootDirectory.Length..].ToUpperInvariant().BeautifyPath();
return subPath.Contains("SETUP") || subPath.Contains("REDIST") || subPath.Contains("SUPPORT") return subPath.Contains("SETUP") || subPath.Contains("REDIST") || subPath.Contains("SUPPORT")
@ -441,37 +486,33 @@ internal static class Resources
if (Program.Canceled) if (Program.Canceled)
return null; return null;
string subDirectory = directory.BeautifyPath(); string subDirectory = directory.BeautifyPath();
if (!dllDirectories.Contains(subDirectory)) if (dllDirectories.Contains(subDirectory))
continue;
bool koaloaderInstalled = Koaloader.AutoLoadDLLs.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile());
if (platform is Platform.Steam or Platform.Paradox)
{ {
bool koaloaderInstalled = Koaloader.AutoLoadDlls.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll)) subDirectory.GetSmokeApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string old_config,
.Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile()); out string config, out string cache);
if (platform is Platform.Steam or Platform.Paradox) if (File.Exists(api) || File.Exists(api_o) || File.Exists(api64) || File.Exists(api64_o)
{ || (File.Exists(old_config) || File.Exists(config)) && !koaloaderInstalled || File.Exists(cache) && !koaloaderInstalled)
subDirectory.GetSmokeApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string config, dllDirectories.Add(subDirectory);
out string cache); }
if (File.Exists(api) || File.Exists(api_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled if (platform is Platform.Epic or Platform.Paradox)
|| File.Exists(cache) && !koaloaderInstalled) {
dllDirectories.Add(subDirectory); subDirectory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
} if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled)
if (platform is Platform.Epic or Platform.Paradox) dllDirectories.Add(subDirectory);
{ }
subDirectory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config); if (platform is Platform.Ubisoft)
if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) {
|| File.Exists(config) && !koaloaderInstalled) subDirectory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
dllDirectories.Add(subDirectory); if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled)
} dllDirectories.Add(subDirectory);
if (platform is Platform.Ubisoft) subDirectory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config);
{ if (File.Exists(old_api32) || File.Exists(old_api64) || File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64)
subDirectory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config); || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled)
if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) dllDirectories.Add(subDirectory);
|| File.Exists(config) && !koaloaderInstalled)
dllDirectories.Add(subDirectory);
subDirectory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o,
out config);
if (File.Exists(old_api32) || File.Exists(old_api64) || File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64)
|| File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled)
dllDirectories.Add(subDirectory);
}
} }
} }
return !dllDirectories.Any() ? null : dllDirectories; return !dllDirectories.Any() ? null : dllDirectories;
@ -487,7 +528,7 @@ internal static class Resources
config = directory + @"\cream_api.ini"; config = directory + @"\cream_api.ini";
} }
internal static string ComputeMD5(this string filePath) private static string ComputeMD5(this string filePath)
{ {
if (!File.Exists(filePath)) if (!File.Exists(filePath))
return null; return null;
@ -500,10 +541,9 @@ internal static class Resources
} }
internal static bool IsResourceFile(this string filePath, ResourceIdentifier identifier) internal static bool IsResourceFile(this string filePath, ResourceIdentifier identifier)
=> filePath.ComputeMD5() is string hash && ResourceMD5s[identifier].Contains(hash); => filePath.ComputeMD5() is { } hash && ResourceMD5s[identifier].Contains(hash);
internal static bool IsResourceFile(this string filePath) internal static bool IsResourceFile(this string filePath) => filePath.ComputeMD5() is { } hash && ResourceMD5s.Values.Any(hashes => hashes.Contains(hash));
=> filePath.ComputeMD5() is string hash && ResourceMD5s.Values.Any(hashes => hashes.Contains(hash));
internal enum BinaryType internal enum BinaryType
{ {
@ -518,4 +558,4 @@ internal static class Resources
EpicOnlineServices32, EpicOnlineServices64, Uplay32, EpicOnlineServices32, EpicOnlineServices64, Uplay32,
Uplay64, Upc32, Upc64 Uplay64, Upc32, Upc64
} }
} }

View file

@ -50,7 +50,7 @@ internal static class ScreamAPI
} }
} }
internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> overrideCatalogItems, private static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> overrideCatalogItems,
SortedList<string, (DlcType type, string name, string icon)> entitlements, InstallForm installForm = null) SortedList<string, (DlcType type, string name, string icon)> entitlements, InstallForm installForm = null)
{ {
writer.WriteLine("{"); writer.WriteLine("{");
@ -156,4 +156,4 @@ internal static class ScreamAPI
if (generateConfig) if (generateConfig)
CheckConfig(directory, selection, installForm); CheckConfig(directory, selection, installForm);
}); });
} }

View file

@ -12,19 +12,20 @@ namespace CreamInstaller.Resources;
internal static class SmokeAPI internal static class SmokeAPI
{ {
internal static void GetSmokeApiComponents(this string directory, out string api32, out string api32_o, out string api64, out string api64_o, internal static void GetSmokeApiComponents(this string directory, out string api32, out string api32_o, out string api64, out string api64_o,
out string config, out string cache) out string old_config, out string config, out string cache)
{ {
api32 = directory + @"\steam_api.dll"; api32 = directory + @"\steam_api.dll";
api32_o = directory + @"\steam_api_o.dll"; api32_o = directory + @"\steam_api_o.dll";
api64 = directory + @"\steam_api64.dll"; api64 = directory + @"\steam_api64.dll";
api64_o = directory + @"\steam_api64_o.dll"; api64_o = directory + @"\steam_api64_o.dll";
config = directory + @"\SmokeAPI.json"; old_config = directory + @"\SmokeAPI.json";
config = directory + @"\SmokeAPI.config.json";
cache = directory + @"\SmokeAPI.cache.json"; cache = directory + @"\SmokeAPI.cache.json";
} }
internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null) internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null)
{ {
directory.GetSmokeApiComponents(out _, out _, out _, out _, out string config, out _); directory.GetSmokeApiComponents(out _, out _, out _, out _, out string old_config, out _, out _);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> overrideDlc = selection.AllDlc.Except(selection.SelectedDlc); IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> overrideDlc = selection.AllDlc.Except(selection.SelectedDlc);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc) foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
overrideDlc = overrideDlc.Except(extraDlc); overrideDlc = overrideDlc.Except(extraDlc);
@ -34,28 +35,28 @@ internal static class SmokeAPI
{ {
injectDlc = injectDlc.Concat(selection.SelectedDlc.Where(pair => pair.Value.type is DlcType.SteamHidden)); injectDlc = injectDlc.Concat(selection.SelectedDlc.Where(pair => pair.Value.type is DlcType.SteamHidden));
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc) foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
if (selection.ExtraDlc.Where(e => e.id == id).Single().dlc.Count > 64) if (selection.ExtraDlc.Single(e => e.id == id).dlc.Count > 64)
injectDlc = injectDlc.Concat(extraDlc.Where(pair => pair.Value.type is DlcType.SteamHidden)); injectDlc = injectDlc.Concat(extraDlc.Where(pair => pair.Value.type is DlcType.SteamHidden));
} }
if (overrideDlc.Any() || injectDlc.Any()) if (overrideDlc.Any() || injectDlc.Any())
{ {
/*if (installForm is not null) /*if (installForm is not null)
installForm.UpdateUser("Generating SmokeAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ installForm.UpdateUser("Generating SmokeAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
File.Create(config).Close(); File.Create(old_config).Close();
StreamWriter writer = new(config, true, Encoding.UTF8); StreamWriter writer = new(old_config, true, Encoding.UTF8);
WriteConfig(writer, new(overrideDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), WriteConfig(writer, new(overrideDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
new(injectDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm); new(injectDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm);
writer.Flush(); writer.Flush();
writer.Close(); writer.Close();
} }
else if (File.Exists(config)) else if (File.Exists(old_config))
{ {
File.Delete(config); File.Delete(old_config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, false);
} }
} }
internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> overrideDlc, private static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> overrideDlc,
SortedList<string, (DlcType type, string name, string icon)> injectDlc, InstallForm installForm = null) SortedList<string, (DlcType type, string name, string icon)> injectDlc, InstallForm installForm = null)
{ {
writer.WriteLine("{"); writer.WriteLine("{");
@ -107,7 +108,8 @@ internal static class SmokeAPI
File.Delete(oldConfig); File.Delete(oldConfig);
installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", LogTextBox.Action, false); installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", LogTextBox.Action, false);
} }
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string cache); directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string old_config,
out string config, out string cache);
if (File.Exists(api32_o)) if (File.Exists(api32_o))
{ {
if (File.Exists(api32)) if (File.Exists(api32))
@ -128,6 +130,11 @@ internal static class SmokeAPI
File.Move(api64_o, api64); File.Move(api64_o, api64);
installForm?.UpdateUser($"Restored Steamworks: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, false); installForm?.UpdateUser($"Restored Steamworks: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, false);
} }
if (deleteConfig && File.Exists(old_config))
{
File.Delete(old_config);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, false);
}
if (deleteConfig && File.Exists(config)) if (deleteConfig && File.Exists(config))
{ {
File.Delete(config); File.Delete(config);
@ -149,7 +156,7 @@ internal static class SmokeAPI
File.Delete(oldConfig); File.Delete(oldConfig);
installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", LogTextBox.Action, false); installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", LogTextBox.Action, false);
} }
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out _); directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out _, out _, out _);
if (File.Exists(api32) && !File.Exists(api32_o)) if (File.Exists(api32) && !File.Exists(api32_o))
{ {
File.Move(api32, api32_o); File.Move(api32, api32_o);
@ -173,4 +180,4 @@ internal static class SmokeAPI
if (generateConfig) if (generateConfig)
CheckConfig(directory, selection, installForm); CheckConfig(directory, selection, installForm);
}); });
} }

View file

@ -44,7 +44,7 @@ internal static class UplayR1
} }
} }
internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> blacklistDlc, private static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> blacklistDlc,
InstallForm installForm = null) InstallForm installForm = null)
{ {
writer.WriteLine("{"); writer.WriteLine("{");
@ -127,4 +127,4 @@ internal static class UplayR1
if (generateConfig) if (generateConfig)
CheckConfig(directory, selection, installForm); CheckConfig(directory, selection, installForm);
}); });
} }

View file

@ -46,7 +46,7 @@ internal static class UplayR2
} }
} }
internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> blacklistDlc, private static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> blacklistDlc,
InstallForm installForm = null) InstallForm installForm = null)
{ {
writer.WriteLine("{"); writer.WriteLine("{");
@ -137,4 +137,4 @@ internal static class UplayR2
if (generateConfig) if (generateConfig)
CheckConfig(directory, selection, installForm); CheckConfig(directory, selection, installForm);
}); });
} }

View file

@ -43,4 +43,4 @@ internal static class Diagnostics
internal static void OpenUrlInInternetBrowser(string url) => Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true }); internal static void OpenUrlInInternetBrowser(string url) => Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true });
internal static string BeautifyPath(this string path) => path is null ? null : Path.TrimEndingDirectorySeparator(Path.GetFullPath(path)); internal static string BeautifyPath(this string path) => path is null ? null : Path.TrimEndingDirectorySeparator(Path.GetFullPath(path));
} }

View file

@ -65,4 +65,4 @@ public class CustomMessageException : Exception
public override string Message { get; } public override string Message { get; }
public override string ToString() => Message; public override string ToString() => Message;
} }

View file

@ -56,4 +56,4 @@ internal static class HttpClientManager
} }
internal static void Dispose() => HttpClient?.Dispose(); internal static void Dispose() => HttpClient?.Dispose();
} }

View file

@ -25,4 +25,4 @@ internal static class IconGrabber
internal static Image GetCommandPromptImage() => GetFileIconImage(Environment.SystemDirectory + @"\cmd.exe"); internal static Image GetCommandPromptImage() => GetFileIconImage(Environment.SystemDirectory + @"\cmd.exe");
internal static Image GetFileExplorerImage() => GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe"); internal static Image GetFileExplorerImage() => GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe");
} }

View file

@ -26,4 +26,4 @@ internal static class LogTextBox
textBox.SelectionColor = textBox.ForeColor; textBox.SelectionColor = textBox.ForeColor;
textBox.Invalidate(); textBox.Invalidate();
} }
} }

View file

@ -11,21 +11,21 @@ namespace CreamInstaller.Utility;
internal static class ProgramData internal static class ProgramData
{ {
internal static readonly string DirectoryPathOld = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller"; private static readonly string DirectoryPathOld = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller";
internal static readonly string DirectoryPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\CreamInstaller"; internal static readonly string DirectoryPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\CreamInstaller";
internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo"; internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo";
internal static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt"; private static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt";
internal static readonly Version MinimumAppInfoVersion = Version.Parse("3.2.0.0"); private static readonly Version MinimumAppInfoVersion = Version.Parse("3.2.0.0");
internal static readonly string CooldownPath = DirectoryPath + @"\cooldown"; internal static readonly string CooldownPath = DirectoryPath + @"\cooldown";
internal static readonly string OldProgramChoicesPath = DirectoryPath + @"\choices.txt"; private static readonly string OldProgramChoicesPath = DirectoryPath + @"\choices.txt";
internal static readonly string ProgramChoicesPath = DirectoryPath + @"\choices.json"; private static readonly string ProgramChoicesPath = DirectoryPath + @"\choices.json";
internal static readonly string DlcChoicesPath = DirectoryPath + @"\dlc.json"; private static readonly string DlcChoicesPath = DirectoryPath + @"\dlc.json";
internal static readonly string KoaloaderProxyChoicesPath = DirectoryPath + @"\proxies.json"; private static readonly string KoaloaderProxyChoicesPath = DirectoryPath + @"\proxies.json";
internal static async Task Setup() internal static async Task Setup()
=> await Task.Run(() => => await Task.Run(() =>
@ -64,16 +64,19 @@ internal static class ProgramData
private static DateTime? GetCooldown(string identifier) private static DateTime? GetCooldown(string identifier)
{ {
if (Directory.Exists(CooldownPath)) if (!Directory.Exists(CooldownPath))
return null;
string cooldownFile = CooldownPath + @$"\{identifier}.txt";
if (!File.Exists(cooldownFile))
return null;
try
{ {
string cooldownFile = CooldownPath + @$"\{identifier}.txt"; if (DateTime.TryParse(File.ReadAllText(cooldownFile), out DateTime cooldown))
if (File.Exists(cooldownFile)) return cooldown;
try }
{ catch
if (DateTime.TryParse(File.ReadAllText(cooldownFile), out DateTime cooldown)) {
return cooldown; // ignored
}
catch { }
} }
return null; return null;
} }
@ -87,13 +90,16 @@ internal static class ProgramData
{ {
File.WriteAllText(cooldownFile, time.ToString(CultureInfo.InvariantCulture)); File.WriteAllText(cooldownFile, time.ToString(CultureInfo.InvariantCulture));
} }
catch { } catch
{
// ignored
}
} }
internal static List<(Platform platform, string id)> ReadProgramChoices() internal static IEnumerable<(Platform platform, string id)> ReadProgramChoices()
{ {
if (!File.Exists(ProgramChoicesPath)) if (!File.Exists(ProgramChoicesPath))
return null; return Enumerable.Empty<(Platform platform, string id)>();
try try
{ {
return JsonConvert.DeserializeObject(File.ReadAllText(ProgramChoicesPath), typeof(List<(Platform platform, string id)>)) as return JsonConvert.DeserializeObject(File.ReadAllText(ProgramChoicesPath), typeof(List<(Platform platform, string id)>)) as
@ -101,11 +107,11 @@ internal static class ProgramData
} }
catch catch
{ {
return new(); return Enumerable.Empty<(Platform platform, string id)>();
} }
} }
internal static void WriteProgramChoices(List<(Platform platform, string id)> choices) internal static void WriteProgramChoices(IEnumerable<(Platform platform, string id)> choices)
{ {
try try
{ {
@ -114,21 +120,24 @@ internal static class ProgramData
else else
File.WriteAllText(ProgramChoicesPath, JsonConvert.SerializeObject(choices)); File.WriteAllText(ProgramChoicesPath, JsonConvert.SerializeObject(choices));
} }
catch { } catch
{
// ignored
}
} }
internal static List<(Platform platform, string gameId, string dlcId)> ReadDlcChoices() internal static IEnumerable<(Platform platform, string gameId, string dlcId)> ReadDlcChoices()
{ {
if (!File.Exists(DlcChoicesPath)) if (!File.Exists(DlcChoicesPath))
return null; return Enumerable.Empty<(Platform platform, string gameId, string dlcId)>();
try try
{ {
return JsonConvert.DeserializeObject(File.ReadAllText(DlcChoicesPath), typeof(List<(Platform platform, string gameId, string dlcId)>)) as return JsonConvert.DeserializeObject(File.ReadAllText(DlcChoicesPath), typeof(IEnumerable<(Platform platform, string gameId, string dlcId)>)) as
List<(Platform platform, string gameId, string dlcId)>; IEnumerable<(Platform platform, string gameId, string dlcId)>;
} }
catch catch
{ {
return new(); return Enumerable.Empty<(Platform platform, string gameId, string dlcId)>();
} }
} }
@ -141,25 +150,29 @@ internal static class ProgramData
else else
File.WriteAllText(DlcChoicesPath, JsonConvert.SerializeObject(choices)); File.WriteAllText(DlcChoicesPath, JsonConvert.SerializeObject(choices));
} }
catch { } catch
{
// ignored
}
} }
internal static List<(Platform platform, string id, string proxy, bool enabled)> ReadKoaloaderChoices() internal static IEnumerable<(Platform platform, string id, string proxy, bool enabled)> ReadKoaloaderChoices()
{ {
if (!File.Exists(KoaloaderProxyChoicesPath)) if (!File.Exists(KoaloaderProxyChoicesPath))
return null; return null;
try try
{ {
return JsonConvert.DeserializeObject(File.ReadAllText(KoaloaderProxyChoicesPath), return JsonConvert.DeserializeObject(File.ReadAllText(KoaloaderProxyChoicesPath),
typeof(List<(Platform platform, string id, string proxy, bool enabled)>)) as List<(Platform platform, string id, string proxy, bool enabled)>; typeof(IEnumerable<(Platform platform, string id, string proxy, bool enabled)>)) as
IEnumerable<(Platform platform, string id, string proxy, bool enabled)>;
} }
catch catch
{ {
return new(); return Enumerable.Empty<(Platform platform, string id, string proxy, bool enabled)>();
} }
} }
internal static void WriteKoaloaderProxyChoices(List<(Platform platform, string id, string proxy, bool enabled)> choices) internal static void WriteKoaloaderProxyChoices(IEnumerable<(Platform platform, string id, string proxy, bool enabled)> choices)
{ {
try try
{ {
@ -168,6 +181,9 @@ internal static class ProgramData
else else
File.WriteAllText(KoaloaderProxyChoicesPath, JsonConvert.SerializeObject(choices)); File.WriteAllText(KoaloaderProxyChoicesPath, JsonConvert.SerializeObject(choices));
} }
catch { } catch
{
// ignored
}
} }
} }