- 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;
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)
{
Text = text;
OnClickEvent = onClick;
onClickEvent = 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) :
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,
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)
=> 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;
else
{
@ -84,7 +90,7 @@ internal class ContextMenuItem : ToolStripMenuItem
}
if (image is not null)
{
images[imageIdentifier] = image;
Images[imageIdentifier] = image;
item.Image = image;
}
}
@ -95,26 +101,24 @@ internal class ContextMenuItem : ToolStripMenuItem
{
(string id, string iconUrl) = imageIdentifierInfo;
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;
else
{
image = await HttpClientManager.GetImageFromUrl(iconUrl);
if (image is not null)
{
images[imageIdentifier] = image;
Images[imageIdentifier] = image;
item.Image = image;
}
else if (onFail is not null)
onFail();
else
onFail?.Invoke();
}
});
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
if (OnClickEvent is null)
return;
OnClickEvent.Invoke(this, e);
onClickEvent?.Invoke(this, e);
}
}

View file

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

View file

@ -11,9 +11,9 @@ using static CreamInstaller.Resources.Resources;
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> comboBoxBounds = new();
@ -34,7 +34,7 @@ internal class CustomTreeView : TreeView
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x203)
m.Result = IntPtr.Zero;
m.Result = nint.Zero;
else
base.WndProc(ref m);
form = FindForm();
@ -54,11 +54,11 @@ internal class CustomTreeView : TreeView
{
e.DrawDefault = true;
TreeNode node = e.Node;
if (!node.IsVisible)
if (node is not { IsVisible: true })
return;
bool highlighted = (e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected && Focused;
Graphics graphics = e.Graphics;
backBrush ??= new SolidBrush(BackColor);
backBrush ??= new(BackColor);
Font font = node.NodeFont ?? Font;
Brush brush = highlighted ? SystemBrushes.Highlight : backBrush;
string text; // = e.Node.Text;
@ -87,9 +87,9 @@ internal class CustomTreeView : TreeView
text = platform.ToString();
size = TextRenderer.MeasureText(graphics, text, font);
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);
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);
if (platform is not Platform.Paradox)
{
@ -100,11 +100,11 @@ internal class CustomTreeView : TreeView
: ColorTranslator.FromHtml("#69AAAA");
text = platformId;
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 };
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);
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);
}
/*if (highlighted)
@ -116,7 +116,7 @@ internal class CustomTreeView : TreeView
{
if (bounds == node.Bounds)
{
size = new Size(4, 0);
size = new(4, 0);
bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width };
graphics.FillRectangle(brush, bounds);
}
@ -127,47 +127,45 @@ internal class CustomTreeView : TreeView
: CheckBoxState.UncheckedDisabled;
size = CheckBoxRenderer.GetGlyphSize(graphics, checkBoxState);
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;
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);
text = koaloaderToggleString;
text = KoaloaderToggleString;
size = TextRenderer.MeasureText(graphics, text, font);
int left = 1;
bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width + left };
selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
checkBoxBounds = new Rectangle(checkBoxBounds.Location, checkBoxBounds.Size + bounds.Size with { Height = 0 });
selectionBounds = new(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
checkBoxBounds = new(checkBoxBounds.Location, checkBoxBounds.Size + bounds.Size with { Height = 0 });
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"),
TextFormatFlags.Default);
this.checkBoxBounds[selection] = RectangleToClient(checkBoxBounds);
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;
text = proxy + ".dll";
size = TextRenderer.MeasureText(graphics, text, comboBoxFont) + new Size(6, 0);
int padding = 2;
bounds = new Rectangle(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 });
const int padding = 2;
bounds = new(bounds.X + bounds.Width, bounds.Y + padding / 2, size.Width, bounds.Height - padding);
selectionBounds = new(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
Rectangle comboBoxBounds = bounds;
graphics.FillRectangle(backBrush, bounds);
ComboBoxRenderer.DrawTextBox(graphics, bounds, text, comboBoxFont, comboBoxState);
size = new Size(14, 0);
size = new(14, 0);
left = -1;
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));
comboBoxBounds = new Rectangle(comboBoxBounds.Location, comboBoxBounds.Size + new Size(bounds.Size.Width + left, 0));
selectionBounds = new(selectionBounds.Location, selectionBounds.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);
this.comboBoxBounds[selection] = RectangleToClient(comboBoxBounds);
}
else
{
_ = comboBoxBounds.Remove(selection);
}
}
}
this.selectionBounds[node] = RectangleToClient(selectionBounds);
@ -179,11 +177,9 @@ internal class CustomTreeView : TreeView
Refresh();
Point clickPoint = PointToClient(e.Location);
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)
{
_ = selectionBounds.Remove(pair.Key);
}
else if (pair.Key.IsVisible && pair.Value.Contains(clickPoint))
{
SelectedNode = pair.Key;
@ -191,57 +187,51 @@ internal class CustomTreeView : TreeView
selectForm.OnNodeRightClick(pair.Key, e.Location);
break;
}
if (e.Button is MouseButtons.Left)
{
if (comboBoxBounds.Any() && selectForm is not null)
foreach (KeyValuePair<ProgramSelection, Rectangle> pair in comboBoxBounds.ToList())
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 (e.Button is not MouseButtons.Left)
return;
if (comboBoxBounds.Any() && selectForm is not null)
foreach (KeyValuePair<ProgramSelection, Rectangle> pair in comboBoxBounds)
if (!ProgramSelection.All.Contains(pair.Key))
{
_ = checkBoxBounds.Remove(pair.Key);
}
_ = comboBoxBounds.Remove(pair.Key);
else if (pair.Value.Contains(clickPoint))
{
pair.Key.Koaloader = !pair.Key.Koaloader;
selectForm.OnKoaloaderChanged();
List<string> proxies = EmbeddedResources.FindAll(r => r.StartsWith("Koaloader")).Select(p =>
{
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;
}
}
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 class StringComparer : IComparer<string>
internal sealed class StringComparer : IComparer<string>
{
public int Compare(string a, string b)
=> !int.TryParse(a, out _) && !int.TryParse(b, out _)
@ -36,7 +36,7 @@ internal class StringComparer : IComparer<string>
: 0;
}
internal class NodeComparer : IComparer<TreeNode>
internal sealed class NodeComparer : IComparer<TreeNode>
{
public int Compare(TreeNode a, TreeNode b)
=> a.Tag is not Platform A
@ -50,7 +50,7 @@ internal class NodeComparer : IComparer<TreeNode>
: 0;
}
internal class NodeNameComparer : IComparer
internal sealed class NodeNameComparer : IComparer
{
public int Compare(object a, object b)
=> a is not TreeNode A
@ -62,7 +62,7 @@ internal class NodeNameComparer : IComparer
: PlatformIdComparer.String.Compare(A.Name, B.Name);
}
internal class NodeTextComparer : IComparer
internal sealed class NodeTextComparer : IComparer
{
public int Compare(object a, object b)
=> a is not TreeNode A

View file

@ -4,7 +4,7 @@
<TargetFramework>net7.0-windows</TargetFramework>
<UseWindowsForms>True</UseWindowsForms>
<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>
<Company>CreamInstaller</Company>
<Product>Automatic DLC Unlocker Installer &amp; Configuration Generator</Product>
@ -19,6 +19,7 @@
<DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols>
<DefineConstants>$(DefineConstants)</DefineConstants>
<ProduceReferenceAssembly>False</ProduceReferenceAssembly>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<AssemblyName>$(Company)</AssemblyName>

View file

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

View file

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

View file

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

View file

@ -13,15 +13,15 @@ using static CreamInstaller.Resources.Resources;
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;
internal readonly bool Uninstalling;
private int CompleteOperationsCount;
private readonly int programCount = ProgramSelection.AllEnabled.Count;
private readonly bool uninstalling;
private int completeOperationsCount;
private int OperationsCount;
private int operationsCount;
internal bool Reselecting;
internal InstallForm(bool uninstall = false)
@ -29,15 +29,15 @@ internal partial class InstallForm : CustomForm
InitializeComponent();
Text = Program.ApplicationName;
logTextBox.BackColor = LogTextBox.Background;
Uninstalling = uninstall;
uninstalling = uninstall;
}
internal void UpdateProgress(int progress)
private void UpdateProgress(int progress)
{
if (!userProgressBar.Disposing && !userProgressBar.IsDisposed)
userProgressBar.Invoke(() =>
{
int value = (int)((float)CompleteOperationsCount / OperationsCount * 100) + progress / OperationsCount;
int value = (int)((float)completeOperationsCount / operationsCount * 100) + progress / operationsCount;
if (value < userProgressBar.Value)
return;
userProgressBar.Value = value;
@ -67,12 +67,12 @@ internal partial class InstallForm : CustomForm
_ = await Repair(this, selection);
}
UpdateUser(
$"{(Uninstalling ? "Uninstalling" : "Installing")}" + $" {(Uninstalling ? "from" : "for")} " + selection.Name
$"{(uninstalling ? "Uninstalling" : "Installing")}" + $" {(uninstalling ? "from" : "for")} " + selection.Name
+ $" with root directory \"{selection.RootDirectory}\" . . . ", LogTextBox.Operation);
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));
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?.Distinct();
if (invalidDirectories is not null)
@ -80,30 +80,31 @@ internal partial class InstallForm : CustomForm
{
if (Program.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))
|| 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);
await Koaloader.Uninstall(directory, selection.RootDirectory, this);
}
Thread.Sleep(1);
}
if (Uninstalling || !selection.Koaloader)
if (uninstalling || !selection.Koaloader)
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
{
if (Program.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))
|| 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);
await Koaloader.Uninstall(directory, selection.RootDirectory, this);
}
Thread.Sleep(1);
}
bool uninstallProxy = Uninstalling || selection.Koaloader;
bool uninstallProxy = uninstalling || selection.Koaloader;
int count = selection.DllDirectories.Count, cur = 0;
foreach (string directory in selection.DllDirectories)
{
@ -111,10 +112,10 @@ internal partial class InstallForm : CustomForm
throw new CustomMessageException("The operation was canceled.");
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,
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 (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))
{
UpdateUser(
@ -170,7 +171,7 @@ internal partial class InstallForm : CustomForm
UpdateProgress(++cur / count * 100);
Thread.Sleep(1);
}
if (selection.Koaloader && !Uninstalling)
if (selection.Koaloader && !uninstalling)
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
{
if (Program.Canceled)
@ -185,8 +186,8 @@ internal partial class InstallForm : CustomForm
private async Task Operate()
{
List<ProgramSelection> programSelections = ProgramSelection.AllEnabled;
OperationsCount = programSelections.Count;
CompleteOperationsCount = 0;
operationsCount = programSelections.Count;
completeOperationsCount = 0;
foreach (ProgramSelection selection in programSelections)
{
if (Program.Canceled || !Program.IsProgramRunningDialog(this, selection))
@ -196,24 +197,24 @@ internal partial class InstallForm : CustomForm
await OperateFor(selection);
UpdateUser($"Operation succeeded for {selection.Name}.", LogTextBox.Success);
selection.Enabled = false;
DisabledSelections.Add(selection);
disabledSelections.Add(selection);
}
catch (Exception exception)
{
UpdateUser($"Operation failed for {selection.Name}: " + exception, LogTextBox.Error);
}
++CompleteOperationsCount;
++completeOperationsCount;
}
Program.Cleanup();
List<ProgramSelection> FailedSelections = ProgramSelection.AllEnabled;
if (FailedSelections.Any())
if (FailedSelections.Count == 1)
throw new CustomMessageException($"Operation failed for {FailedSelections.First().Name}.");
List<ProgramSelection> failedSelections = ProgramSelection.AllEnabled;
if (failedSelections.Any())
if (failedSelections.Count == 1)
throw new CustomMessageException($"Operation failed for {failedSelections.First().Name}.");
else
throw new CustomMessageException($"Operation failed for {FailedSelections.Count} programs.");
foreach (ProgramSelection selection in DisabledSelections)
throw new CustomMessageException($"Operation failed for {failedSelections.Count} programs.");
foreach (ProgramSelection selection in disabledSelections)
selection.Enabled = true;
DisabledSelections.Clear();
disabledSelections.Clear();
}
private async void Start()
@ -227,12 +228,12 @@ internal partial class InstallForm : CustomForm
try
{
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);
}
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;
}
userProgressBar.Value = userProgressBar.Maximum;
@ -276,9 +277,9 @@ internal partial class InstallForm : CustomForm
{
Program.Cleanup();
Reselecting = true;
foreach (ProgramSelection selection in DisabledSelections)
foreach (ProgramSelection selection in disabledSelections)
selection.Enabled = true;
DisabledSelections.Clear();
disabledSelections.Clear();
Close();
}
}
}

View file

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

View file

@ -7,7 +7,7 @@ using CreamInstaller.Utility;
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();
internal SelectDialogForm(IWin32Window owner) : base(owner) => InitializeComponent();
@ -69,9 +69,7 @@ internal partial class SelectDialogForm : CustomForm
private void OnAllCheckBoxChanged(object sender, EventArgs e)
{
bool shouldCheck = false;
if (selectionTreeView.Nodes.Cast<TreeNode>().Any(n => !n.Checked))
shouldCheck = true;
bool shouldCheck = selectionTreeView.Nodes.Cast<TreeNode>().Any(n => !n.Checked);
foreach (TreeNode node in selectionTreeView.Nodes)
{
node.Checked = shouldCheck;
@ -84,8 +82,8 @@ internal partial class SelectDialogForm : CustomForm
private void OnLoad(object sender, EventArgs e)
{
List<(Platform platform, string id)> choices = ProgramData.ReadProgramChoices();
if (choices is null)
List<(Platform platform, string id)> choices = ProgramData.ReadProgramChoices().ToList();
if (!choices.Any())
return;
foreach (TreeNode node in selectionTreeView.Nodes)
{
@ -96,10 +94,7 @@ internal partial class SelectDialogForm : CustomForm
private void OnSave(object sender, EventArgs e)
{
List<(Platform platform, string id)> choices = new();
foreach (TreeNode node in selectionTreeView.Nodes.Cast<TreeNode>().Where(n => n.Checked))
choices.Add(((Platform)node.Tag, node.Name));
ProgramData.WriteProgramChoices(choices);
ProgramData.WriteProgramChoices(selectionTreeView.Nodes.Cast<TreeNode>().Where(n => n.Checked).Select(node => ((Platform)node.Tag, node.Name)));
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.Generic;
using System.Drawing;
@ -22,15 +20,15 @@ using static CreamInstaller.Resources.Resources;
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()
{
@ -40,12 +38,12 @@ internal partial class SelectForm : CustomForm
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)
=> 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)
{
@ -55,8 +53,8 @@ internal partial class SelectForm : CustomForm
{
if (Program.Canceled)
return;
if (!RemainingGames.Contains(gameName))
RemainingGames.Add(gameName);
if (!remainingGames.Contains(gameName))
remainingGames.Add(gameName);
UpdateRemainingGames();
});
}
@ -69,12 +67,12 @@ internal partial class SelectForm : CustomForm
{
if (Program.Canceled)
return;
RemainingGames.Remove(gameName);
remainingGames.Remove(gameName);
UpdateRemainingGames();
});
}
private void UpdateRemainingDLCs() => UpdateRemaining(progressLabelDLCs, RemainingDLCs, "DLCs");
private void UpdateRemainingDLCs() => UpdateRemaining(progressLabelDLCs, remainingDLCs, "DLCs");
private void AddToRemainingDLCs(string dlcId)
{
@ -84,8 +82,8 @@ internal partial class SelectForm : CustomForm
{
if (Program.Canceled)
return;
if (!RemainingDLCs.Contains(dlcId))
RemainingDLCs.Add(dlcId);
if (!remainingDLCs.Contains(dlcId))
remainingDLCs.Add(dlcId);
UpdateRemainingDLCs();
});
}
@ -98,35 +96,35 @@ internal partial class SelectForm : CustomForm
{
if (Program.Canceled)
return;
RemainingDLCs.Remove(dlcId);
remainingDLCs.Remove(dlcId);
UpdateRemainingDLCs();
});
}
private async Task GetApplicablePrograms(IProgress<int> progress)
{
if (ProgramsToScan is null || !ProgramsToScan.Any())
if (programsToScan is null || !programsToScan.Any())
return;
int TotalGameCount = 0;
int CompleteGameCount = 0;
int totalGameCount = 0;
int completeGameCount = 0;
void AddToRemainingGames(string gameName)
{
this.AddToRemainingGames(gameName);
progress.Report(-Interlocked.Increment(ref TotalGameCount));
progress.Report(CompleteGameCount);
progress.Report(-Interlocked.Increment(ref totalGameCount));
progress.Report(completeGameCount);
}
void RemoveFromRemainingGames(string gameName)
{
this.RemoveFromRemainingGames(gameName);
progress.Report(Interlocked.Increment(ref CompleteGameCount));
progress.Report(Interlocked.Increment(ref completeGameCount));
}
if (Program.Canceled)
return;
List<TreeNode> treeNodes = TreeNodes;
RemainingGames.Clear(); // for display purposes only, otherwise ignorable
RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable
remainingGames.Clear(); // for display purposes only, otherwise ignorable
remainingDLCs.Clear(); // for display purposes only, otherwise ignorable
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);
if (dllDirectories is not null)
@ -153,7 +151,7 @@ internal partial class SelectForm : CustomForm
}
}
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();
steamGamesToCheck = steamGames.Count;
@ -161,7 +159,7 @@ internal partial class SelectForm : CustomForm
{
if (Program.Canceled)
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);
continue;
@ -287,7 +285,7 @@ internal partial class SelectForm : CustomForm
_ = selectionTreeView.Nodes.Add(programNode);
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in dlc)
{
if (Program.Canceled || programNode is null)
if (Program.Canceled)
return;
string appId = pair.Key;
(DlcType type, string name, string icon) dlcApp = pair.Value;
@ -310,7 +308,7 @@ internal partial class SelectForm : CustomForm
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();
foreach (Manifest manifest in epicGames)
@ -320,7 +318,7 @@ internal partial class SelectForm : CustomForm
string directory = manifest.InstallLocation;
if (Program.Canceled)
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;
AddToRemainingGames(name);
Task task = Task.Run(async () =>
@ -437,14 +435,14 @@ internal partial class SelectForm : CustomForm
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();
foreach ((string gameId, string name, string gameDirectory) in ubisoftGames)
{
if (Program.Canceled)
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;
AddToRemainingGames(name);
Task task = Task.Run(async () =>
@ -521,34 +519,34 @@ internal partial class SelectForm : CustomForm
ShowProgressBar();
await ProgramData.Setup();
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();
if (Directory.Exists(ParadoxLauncher.InstallPath))
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))
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in (await SteamLibrary.GetGames()).Where(g
=> !Program.IsGameBlocked(g.name, g.gameDirectory)))
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))
foreach (Manifest manifest in (await EpicLibrary.GetGames()).Where(m => !Program.IsGameBlocked(m.DisplayName, m.InstallLocation)))
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
=> !Program.IsGameBlocked(g.name, g.gameDirectory)))
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())
{
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);
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)
{
ProgramsToScan = choices;
programsToScan = choices;
noneFoundLabel.Text = "None of the chosen programs nor games were applicable!" + retry;
}
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}%";
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 . . . ";
await SteamCMD.Setup(iProgress);
}
setup = false;
progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . ";
ProgramSelection.ValidateAll(ProgramsToScan);
ProgramSelection.ValidateAll(programsToScan);
/*TreeNodes.ForEach(node =>
{
if (node.Tag is not Platform platform
@ -638,11 +636,16 @@ internal partial class SelectForm : CustomForm
private static void SyncNodeAncestors(TreeNode node)
{
TreeNode parentNode = node.Parent;
if (parentNode is not null)
while (true)
{
parentNode.Checked = parentNode.Nodes.Cast<TreeNode>().Any(childNode => childNode.Checked);
SyncNodeAncestors(parentNode);
TreeNode parentNode = node.Parent;
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(() =>
{
ContextMenuStrip contextMenuStrip = ContextMenuStrip;
while (ContextMenuStrip.Tag is bool used && used)
while (ContextMenuStrip.Tag is true)
Thread.Sleep(100);
ContextMenuStrip.Tag = true;
ToolStripItemCollection items = contextMenuStrip.Items;
@ -737,18 +740,18 @@ internal partial class SelectForm : CustomForm
dlcParentSelection = ProgramSelection.FromPlatformId(platform, dlc.Value.gameAppId);
if (selection is null && dlcParentSelection is null)
return;
ContextMenuItem header = null;
ContextMenuItem header;
if (id == "PL")
header = new(node.Text, "Paradox Launcher");
else if (selection is not null)
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));
items.Add(header ?? new ContextMenuItem(node.Text));
items.Add(header);
string appInfoVDF = $@"{SteamCMD.AppInfoPath}\{id}.vdf";
string appInfoJSON = $@"{SteamCMD.AppInfoPath}\{id}.json";
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();
if (File.Exists(appInfoJSON))
@ -773,17 +776,26 @@ internal partial class SelectForm : CustomForm
{
File.Delete(appInfoVDF);
}
catch { }
catch
{
// ignored
}
try
{
File.Delete(appInfoJSON);
}
catch { }
catch
{
// ignored
}
try
{
File.Delete(cooldown);
}
catch { }
catch
{
// ignored
}
OnLoad(true);
}));
}
@ -793,7 +805,8 @@ internal partial class SelectForm : CustomForm
if (id == "PL")
{
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 ContextMenuItem("Open Root Directory", "File Explorer",
@ -807,10 +820,10 @@ internal partial class SelectForm : CustomForm
if (selection.Platform is Platform.Steam or Platform.Paradox)
foreach (string directory in directories)
{
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config,
out string cache);
if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config)
|| File.Exists(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) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(old_config)
|| File.Exists(config) || File.Exists(cache))
items.Add(new ContextMenuItem($"Open Steamworks Directory #{++steam}", "File Explorer",
(sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)));
}
@ -838,42 +851,37 @@ internal partial class SelectForm : CustomForm
}
if (id != "PL")
{
if (selection is not null && selection.Platform is Platform.Steam
|| dlcParentSelection is not null && dlcParentSelection.Platform is Platform.Steam)
if (selection?.Platform is Platform.Steam || dlcParentSelection?.Platform is Platform.Steam)
{
items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open SteamDB", "SteamDB",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamdb.info/app/" + id)));
}
if (selection is not null)
{
if (selection.Platform is Platform.Steam)
switch (selection.Platform)
{
items.Add(new ContextMenuItem("Open Steam Store", "Steam Store",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl)));
items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIconUrl), "Steam Community",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + id)));
case Platform.Steam:
items.Add(new ContextMenuItem("Open Steam Store", "Steam Store",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl)));
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)),
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.WebsiteUrl)));
contextMenuStrip.Show(selectionTreeView, location);
@ -900,38 +908,34 @@ internal partial class SelectForm : CustomForm
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 (!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)
{
if (form.Reselecting)
{
InheritLocation(form);
Show();
InheritLocation(form);
Show();
#if DEBUG
DebugForm.Current.Attach(this);
DebugForm.Current.Attach(this);
#endif
OnLoad();
}
else
Close();
};
form.Show();
Hide();
OnLoad();
}
else
Close();
};
form.Show();
Hide();
#if DEBUG
DebugForm.Current.Attach(form);
DebugForm.Current.Attach(form);
#endif
}
}
private void OnInstall(object sender, EventArgs e) => OnAccept();
@ -948,33 +952,20 @@ internal partial class SelectForm : CustomForm
private void OnAllCheckBoxChanged(object sender, EventArgs e)
{
bool shouldCheck = false;
foreach (TreeNode node in TreeNodes)
if (node.Parent is null && !node.Checked)
{
shouldCheck = true;
break;
}
foreach (TreeNode node in TreeNodes)
if (node.Parent is null && node.Checked != shouldCheck)
{
node.Checked = shouldCheck;
OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
}
bool shouldCheck = TreeNodes.Any(node => node.Parent is null && !node.Checked);
foreach (TreeNode node in TreeNodes.Where(node => node.Parent is null && node.Checked != shouldCheck))
{
node.Checked = shouldCheck;
OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
}
allCheckBox.CheckedChanged -= OnAllCheckBoxChanged;
allCheckBox.Checked = shouldCheck;
allCheckBox.CheckedChanged += OnAllCheckBoxChanged;
}
internal void OnKoaloaderAllCheckBoxChanged(object sender, EventArgs e)
private void OnKoaloaderAllCheckBoxChanged(object sender, EventArgs e)
{
bool shouldCheck = false;
foreach (ProgramSelection selection in ProgramSelection.AllSafe)
if (!selection.Koaloader)
{
shouldCheck = true;
break;
}
bool shouldCheck = ProgramSelection.AllSafe.Any(selection => !selection.Koaloader);
foreach (ProgramSelection selection in ProgramSelection.AllSafe)
selection.Koaloader = shouldCheck;
selectionTreeView.Invalidate();
@ -984,21 +975,15 @@ internal partial class SelectForm : CustomForm
}
private bool AreSelectionsDefault()
{
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;
}
=> TreeNodes.All(node => node.Parent is null || node.Tag is not Platform || (node.Text == "Unknown" ? !node.Checked : node.Checked));
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)
{
List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices()
?? new List<(Platform platform, string gameId, string dlcId)>();
List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices().ToList();
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)
choices.Add((platform, node.Parent.Name, node.Name));
@ -1011,15 +996,13 @@ internal partial class SelectForm : CustomForm
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)
{
List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices();
if (choices is null)
return;
List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices().ToList();
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.Text == "Unknown"
@ -1032,29 +1015,21 @@ internal partial class SelectForm : CustomForm
private void OnResetDlc(object sender, EventArgs e)
{
foreach (TreeNode node in TreeNodes)
if (node.Parent is not null && node.Tag is Platform)
{
node.Checked = node.Text != "Unknown";
OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
}
foreach (TreeNode node in TreeNodes.Where(node => node.Parent is not null && node.Tag is Platform))
{
node.Checked = node.Text != "Unknown";
OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
}
resetButton.Enabled = CanResetDlc();
}
private static bool AreKoaloaderSelectionsDefault()
{
foreach (ProgramSelection selection in ProgramSelection.AllSafe)
if (!selection.Koaloader || selection.KoaloaderProxy is not null)
return false;
return true;
}
private static bool AreKoaloaderSelectionsDefault() => ProgramSelection.AllSafe.All(selection => selection.Koaloader && selection.KoaloaderProxy is null);
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)
{
List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices()
?? new List<(Platform platform, string id, string proxy, bool enabled)>();
List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices().ToList();
foreach (ProgramSelection selection in ProgramSelection.AllSafe)
{
_ = choices.RemoveAll(c => c.platform == selection.Platform && c.id == selection.Id);
@ -1067,13 +1042,11 @@ internal partial class SelectForm : CustomForm
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)
{
List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices();
if (choices is null)
return;
List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices().ToList();
foreach (ProgramSelection selection in ProgramSelection.AllSafe)
if (choices.Any(c => c.platform == selection.Platform && c.id == selection.Id))
{
@ -1135,25 +1108,23 @@ internal partial class SelectForm : CustomForm
{
StringBuilder blockedGames = new();
foreach (string name in Program.ProtectedGames)
_ = blockedGames.Append(helpButtonListPrefix + name);
_ = blockedGames.Append(HelpButtonListPrefix + name);
StringBuilder blockedDirectories = new();
foreach (string path in Program.ProtectedGameDirectories)
_ = blockedDirectories.Append(helpButtonListPrefix + path);
_ = blockedDirectories.Append(HelpButtonListPrefix + path);
StringBuilder blockedDirectoryExceptions = new();
foreach (string name in Program.ProtectedGameDirectoryExceptions)
_ = blockedDirectoryExceptions.Append(helpButtonListPrefix + name);
_ = blockedDirectoryExceptions.Append(HelpButtonListPrefix + name);
using DialogForm form = new(this);
_ = form.Show(SystemIcons.Information,
"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: "
+ (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(blockedDirectoryExceptions.ToString()) ? "(none)" : blockedDirectoryExceptions), "OK",
+ (string.IsNullOrWhiteSpace(blockedDirectoryExceptions.ToString()) ? "(none)" : blockedDirectoryExceptions),
customFormText: "Block Protected Games");
}
private void OnSortCheckBoxChanged(object sender, EventArgs e)
=> 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))
games.Add(manifest);
}
catch { }
;
catch
{
// ignored
}
}
return games;
});

View file

@ -13,14 +13,14 @@ namespace CreamInstaller.Platforms.Epic;
internal static class EpicStore
{
//private const int COOLDOWN_CATALOG_ITEM = 600;
//private const int CooldownCatalogItem = 600;
/* need a method to query catalog items
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)
{
@ -28,16 +28,19 @@ internal static class EpicStore
string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json";
bool cachedExists = File.Exists(cacheFile);
Response response = null;
if (!cachedExists || ProgramData.CheckCooldown(categoryNamespace, COOLDOWN_ENTITLEMENT))
if (!cachedExists || ProgramData.CheckCooldown(categoryNamespace, CooldownEntitlement))
{
response = await QueryGraphQL(categoryNamespace);
try
{
await File.WriteAllTextAsync(cacheFile, JsonConvert.SerializeObject(response, Formatting.Indented));
}
catch { }
catch
{
// ignored
}
}
else if (cachedExists)
else
try
{
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++)
{
KeyImage keyImage = element.KeyImages[i];
if (keyImage.Type == "DieselStoreFront")
{
icon = keyImage.Url.ToString();
break;
}
if (keyImage.Type != "DieselStoreFront")
continue;
icon = keyImage.Url.ToString();
break;
}
foreach (Item item in element.Items)
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++)
{
KeyImage keyImage = element.KeyImages[i];
if (keyImage.Type == "Thumbnail")
{
icon = keyImage.Url.ToString();
break;
}
if (keyImage.Type != "Thumbnail")
continue;
icon = keyImage.Url.ToString();
break;
}
foreach (Item item in element.Items)
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++)
{
(string id, string name, string product, string icon, string developer) app = dlcIds[i];
if (app.id == id)
{
found = true;
dlcIds[i] = canOverwrite
? (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);
break;
}
if (app.id != id)
continue;
found = true;
dlcIds[i] = canOverwrite
? (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);
break;
}
if (!found)
dlcIds.Add((id, title, product, icon, developer));
@ -131,4 +131,4 @@ internal static class EpicStore
return null;
}
}
}
}

View file

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

View file

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

View file

@ -98,10 +98,12 @@ internal static class ParadoxLauncher
byte[] epicOriginalSdk64 = null;
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());
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out _);
smokeInstalled = smokeInstalled || File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string old_config,
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(api64) && api64.IsResourceFile(ResourceIdentifier.Steamworks64);
await SmokeAPI.Uninstall(directory, deleteConfig: false);
@ -125,7 +127,7 @@ internal static class ParadoxLauncher
bool neededRepair = false;
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))
{
steamOriginalSdk32.Write(api32);
@ -161,13 +163,13 @@ internal static class ParadoxLauncher
if (installForm is not null)
installForm.UpdateUser("Paradox Launcher successfully repaired!", LogTextBox.Success);
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;
}
if (installForm is not null)
installForm.UpdateUser("Paradox Launcher did not need to be repaired.", LogTextBox.Success);
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;
}
_ = form is InstallForm
@ -175,8 +177,8 @@ internal static class ParadoxLauncher
+ "You will likely have to reinstall Paradox Launcher to fix this issue.")
: dialogForm.Show(SystemIcons.Error,
"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");
return RepairResult.Failure;
}
}
}

View file

@ -17,24 +17,24 @@ namespace CreamInstaller.Platforms.Steam;
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 int[] locks = new int[ProcessLimit];
private static readonly int[] Locks = new int[ProcessLimit];
internal static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip";
internal static readonly string DllPath = DirectoryPath + @"\steamclient.dll";
private static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip";
private static readonly string DllPath = DirectoryPath + @"\steamclient.dll";
internal static readonly string AppCachePath = DirectoryPath + @"\appcache";
internal static readonly string ConfigPath = DirectoryPath + @"\config";
internal static readonly string DumpsPath = DirectoryPath + @"\dumps";
internal static readonly string LogsPath = DirectoryPath + @"\logs";
internal static readonly string SteamAppsPath = DirectoryPath + @"\steamapps";
private static readonly string AppCachePath = DirectoryPath + @"\appcache";
private static readonly string ConfigPath = DirectoryPath + @"\config";
private static readonly string DumpsPath = DirectoryPath + @"\dumps";
private static readonly string LogsPath = DirectoryPath + @"\logs";
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;
private static string GetArguments(string appId)
@ -43,19 +43,19 @@ internal static class SteamCMD
+ string.Concat(Enumerable.Repeat("+app_update 4 ", attempts)) + "+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(() =>
{
wait_for_lock:
if (Program.Canceled)
return "";
for (int i = 0; i < locks.Length; i++)
for (int i = 0; i < Locks.Length; i++)
{
if (Program.Canceled)
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[appId] = ++count;
@ -69,11 +69,13 @@ internal static class SteamCMD
StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8
};
Process process = Process.Start(processStartInfo);
if (appId == null)
return "";
StringBuilder output = new();
StringBuilder appInfo = new();
bool appInfoStarted = false;
DateTime lastOutput = DateTime.UtcNow;
while (true)
while (process != null)
{
if (Program.Canceled)
{
@ -92,24 +94,23 @@ internal static class SteamCMD
}
DateTime now = DateTime.UtcNow;
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);
process.Close();
if (output.ToString().Contains($"No app info for AppID {appId} found, requesting..."))
{
AttemptCount[appId]++;
processStartInfo.Arguments = GetArguments(appId);
process = Process.Start(processStartInfo);
appInfoStarted = false;
_ = output.Clear();
_ = appInfo.Clear();
}
else
break;
AttemptCount[appId]++;
processStartInfo.Arguments = GetArguments(appId);
process = Process.Start(processStartInfo);
appInfoStarted = false;
_ = output.Clear();
_ = appInfo.Clear();
}
else
break;
}
_ = Interlocked.Decrement(ref locks[i]);
_ = Interlocked.Decrement(ref Locks[i]);
return appInfo.ToString();
}
Thread.Sleep(200);
@ -174,7 +175,10 @@ internal static class SteamCMD
if (Directory.Exists(SteamAppsPath))
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)
@ -223,12 +227,12 @@ internal static class SteamCMD
File.Delete(appUpdateFile);
goto restart;
}
if (appInfo is null || appInfo.Value?.Children()?.ToList()?.Count == 0)
if (appInfo.Value.Children().ToList().Count == 0)
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")
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)
return appInfo;
if (type is not null && (!int.TryParse(buildid, out int gamebuildId) || gamebuildId >= buildId))
@ -279,7 +283,10 @@ internal static class SteamCMD
process.WaitForExit();
process.Close();
}
catch { }
catch
{
// ignored
}
})).ToList();
foreach (Task task in tasks)
await task;
@ -291,4 +298,4 @@ internal static class SteamCMD
if (Directory.Exists(DirectoryPath))
Directory.Delete(DirectoryPath, true);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -12,7 +12,7 @@ namespace CreamInstaller.Resources;
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"),
("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")
};
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();
foreach (string proxy in EmbeddedResources.Select(proxy =>
proxies = EmbeddedResources.Select(proxy =>
{
proxy = proxy[(proxy.IndexOf('.') + 1)..];
return proxy[(proxy.IndexOf('.') + 1)..];
}))
proxies.Add(directory + @"\" + proxy);
config = directory + @"\Koaloader.json";
}).Select(proxy => directory + @"\" + proxy).ToList();
old_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")))
{
resourceIdentifier.GetProxyInfoFromIdentifier(out string _proxyName, out BinaryType _binaryType);
if (_proxyName == proxyName && _binaryType == binaryType)
{
resourceIdentifier.Write(path);
break;
}
if (_proxyName != proxyName || _binaryType != binaryType)
continue;
resourceIdentifier.Write(path);
break;
}
}
@ -52,16 +50,17 @@ internal static class Koaloader
baseIdentifier = baseIdentifier[..baseIdentifier.IndexOf('.')];
proxyName = baseIdentifier[..baseIdentifier.LastIndexOf('_')];
string bitness = baseIdentifier[(baseIdentifier.LastIndexOf('_') + 1)..];
binaryType = bitness == "32"
? BinaryType.BIT32
: bitness == "64"
? BinaryType.BIT64
: BinaryType.Unknown;
binaryType = bitness switch { "32" => BinaryType.BIT32, "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> modules = new(PlatformIdComparer.String);
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,
InstallForm installForm = null)
private static void WriteConfig(StreamWriter writer, SortedList<string, string> targets, SortedList<string, string> modules, InstallForm installForm = null)
{
writer.WriteLine("{");
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)
=> 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)))
{
File.Delete(proxyPath);
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()))
{
File.Delete(path);
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))
{
File.Delete(config);
@ -154,7 +157,7 @@ internal static class Koaloader
InstallForm installForm = null, bool generateConfig = true)
=> 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 path = directory + @"\" + proxy + ".dll";
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"))
if (executable.TryGetFileBinaryType(out BinaryType binaryType))
{
if (binaryType == BinaryType.BIT32)
bit32 = true;
else if (binaryType == BinaryType.BIT64)
bit64 = true;
switch (binaryType)
{
case BinaryType.BIT32:
bit32 = true;
break;
case BinaryType.BIT64:
bit64 = true;
break;
}
if (bit32 && bit64)
break;
}
@ -216,118 +224,127 @@ internal static class Koaloader
}
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 (rootDirectory is not null && directory != rootDirectory)
if (bit32)
{
if (File.Exists(path))
path = directory + @"\ScreamAPI32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{
File.Delete(path);
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
if (File.Exists(path))
{
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);
installForm?.UpdateUser(
$"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
if (bit64)
{
path = directory + @"\ScreamAPI64.dll";
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 (rootDirectory is not null && directory != rootDirectory)
if (bit32)
{
if (File.Exists(path))
path = directory + @"\UplayR1Unlocker32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{
File.Delete(path);
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
if (File.Exists(path))
{
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);
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 (bit64)
{
if (File.Exists(path))
path = directory + @"\UplayR1Unlocker64.dll";
if (rootDirectory is not null && directory != rootDirectory)
{
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
if (File.Exists(path))
{
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);
installForm?.UpdateUser(
$"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)
UplayR1.CheckConfig(rootDirectory ?? directory, selection, installForm);
if (bit32)
{
if (File.Exists(path))
path = directory + @"\UplayR2Unlocker32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
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 + @"\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);
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 (bit64)
{
if (File.Exists(path))
path = directory + @"\UplayR2Unlocker64.dll";
if (rootDirectory is not null && directory != rootDirectory)
{
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
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";
}
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);
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);
break;
}
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)
CheckConfig(directory, selection, installForm);
});
}
}

View file

@ -13,9 +13,9 @@ namespace CreamInstaller.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>
@ -263,7 +263,51 @@ internal static class Resources
"76CAB00C7DD33FC19F7CDD1849FF9CA2", // Koaloader v2.4.0
"DA4D6A7C0872757A74DDAE05A2C1D160", // 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
"973AB1632B747D4BF3B2666F32E34327", // SmokeAPI v1.0.1
"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
"D077737B9979D32458AC938A2978FA3C", // SmokeAPI v1.0.1
"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
{
if (embeddedResources is null)
{
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
embeddedResources = new();
foreach (string resourceName in names.Where(n => n.StartsWith("CreamInstaller.Resources.")))
embeddedResources.Add(resourceName[25..]);
}
if (embeddedResources is not null)
return embeddedResources;
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
embeddedResources = new();
foreach (string resourceName in names.Where(n => n.StartsWith("CreamInstaller.Resources.")))
embeddedResources.Add(resourceName[25..]);
return embeddedResources;
}
}
@ -347,7 +392,7 @@ internal static class Resources
{
using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier);
using FileStream file = new(filePath, FileMode.Create, FileAccess.Write);
resource.CopyTo(file);
resource?.CopyTo(file);
}
internal static void Write(this byte[] resource, string filePath)
@ -419,7 +464,7 @@ internal static class Resources
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();
return subPath.Contains("SETUP") || subPath.Contains("REDIST") || subPath.Contains("SUPPORT")
@ -441,37 +486,33 @@ internal static class Resources
if (Program.Canceled)
return null;
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))
.Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile());
if (platform is Platform.Steam or Platform.Paradox)
{
subDirectory.GetSmokeApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string config,
out string cache);
if (File.Exists(api) || File.Exists(api_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled
|| File.Exists(cache) && !koaloaderInstalled)
dllDirectories.Add(subDirectory);
}
if (platform is Platform.Epic or Platform.Paradox)
{
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)
dllDirectories.Add(subDirectory);
}
if (platform is Platform.Ubisoft)
{
subDirectory.GetUplayR1Components(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)
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);
}
subDirectory.GetSmokeApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string old_config,
out string config, out string cache);
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)
dllDirectories.Add(subDirectory);
}
if (platform is Platform.Epic or Platform.Paradox)
{
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)
dllDirectories.Add(subDirectory);
}
if (platform is Platform.Ubisoft)
{
subDirectory.GetUplayR1Components(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)
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;
@ -487,7 +528,7 @@ internal static class Resources
config = directory + @"\cream_api.ini";
}
internal static string ComputeMD5(this string filePath)
private static string ComputeMD5(this string filePath)
{
if (!File.Exists(filePath))
return null;
@ -500,10 +541,9 @@ internal static class Resources
}
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)
=> filePath.ComputeMD5() is string hash && ResourceMD5s.Values.Any(hashes => hashes.Contains(hash));
internal static bool IsResourceFile(this string filePath) => filePath.ComputeMD5() is { } hash && ResourceMD5s.Values.Any(hashes => hashes.Contains(hash));
internal enum BinaryType
{
@ -518,4 +558,4 @@ internal static class Resources
EpicOnlineServices32, EpicOnlineServices64, Uplay32,
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)
{
writer.WriteLine("{");
@ -156,4 +156,4 @@ internal static class ScreamAPI
if (generateConfig)
CheckConfig(directory, selection, installForm);
});
}
}

View file

@ -12,19 +12,20 @@ namespace CreamInstaller.Resources;
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,
out string config, out string cache)
out string old_config, out string config, out string cache)
{
api32 = directory + @"\steam_api.dll";
api32_o = directory + @"\steam_api_o.dll";
api64 = directory + @"\steam_api64.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";
}
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);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
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));
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));
}
if (overrideDlc.Any() || injectDlc.Any())
{
/*if (installForm is not null)
installForm.UpdateUser("Generating SmokeAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
File.Create(config).Close();
StreamWriter writer = new(config, true, Encoding.UTF8);
File.Create(old_config).Close();
StreamWriter writer = new(old_config, true, Encoding.UTF8);
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);
writer.Flush();
writer.Close();
}
else if (File.Exists(config))
else if (File.Exists(old_config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
File.Delete(old_config);
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)
{
writer.WriteLine("{");
@ -107,7 +108,8 @@ internal static class SmokeAPI
File.Delete(oldConfig);
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))
@ -128,6 +130,11 @@ internal static class SmokeAPI
File.Move(api64_o, api64);
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))
{
File.Delete(config);
@ -149,7 +156,7 @@ internal static class SmokeAPI
File.Delete(oldConfig);
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))
{
File.Move(api32, api32_o);
@ -173,4 +180,4 @@ internal static class SmokeAPI
if (generateConfig)
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)
{
writer.WriteLine("{");
@ -127,4 +127,4 @@ internal static class UplayR1
if (generateConfig)
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)
{
writer.WriteLine("{");
@ -137,4 +137,4 @@ internal static class UplayR2
if (generateConfig)
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 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 ToString() => Message;
}
}

View file

@ -56,4 +56,4 @@ internal static class HttpClientManager
}
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 GetFileExplorerImage() => GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe");
}
}

View file

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

View file

@ -11,21 +11,21 @@ namespace CreamInstaller.Utility;
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 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 OldProgramChoicesPath = DirectoryPath + @"\choices.txt";
internal static readonly string ProgramChoicesPath = DirectoryPath + @"\choices.json";
internal static readonly string DlcChoicesPath = DirectoryPath + @"\dlc.json";
internal static readonly string KoaloaderProxyChoicesPath = DirectoryPath + @"\proxies.json";
private static readonly string OldProgramChoicesPath = DirectoryPath + @"\choices.txt";
private static readonly string ProgramChoicesPath = DirectoryPath + @"\choices.json";
private static readonly string DlcChoicesPath = DirectoryPath + @"\dlc.json";
private static readonly string KoaloaderProxyChoicesPath = DirectoryPath + @"\proxies.json";
internal static async Task Setup()
=> await Task.Run(() =>
@ -64,16 +64,19 @@ internal static class ProgramData
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 (File.Exists(cooldownFile))
try
{
if (DateTime.TryParse(File.ReadAllText(cooldownFile), out DateTime cooldown))
return cooldown;
}
catch { }
if (DateTime.TryParse(File.ReadAllText(cooldownFile), out DateTime cooldown))
return cooldown;
}
catch
{
// ignored
}
return null;
}
@ -87,13 +90,16 @@ internal static class ProgramData
{
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))
return null;
return Enumerable.Empty<(Platform platform, string id)>();
try
{
return JsonConvert.DeserializeObject(File.ReadAllText(ProgramChoicesPath), typeof(List<(Platform platform, string id)>)) as
@ -101,11 +107,11 @@ internal static class ProgramData
}
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
{
@ -114,21 +120,24 @@ internal static class ProgramData
else
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))
return null;
return Enumerable.Empty<(Platform platform, string gameId, string dlcId)>();
try
{
return JsonConvert.DeserializeObject(File.ReadAllText(DlcChoicesPath), typeof(List<(Platform platform, string gameId, string dlcId)>)) as
List<(Platform platform, string gameId, string dlcId)>;
return JsonConvert.DeserializeObject(File.ReadAllText(DlcChoicesPath), typeof(IEnumerable<(Platform platform, string gameId, string dlcId)>)) as
IEnumerable<(Platform platform, string gameId, string dlcId)>;
}
catch
{
return new();
return Enumerable.Empty<(Platform platform, string gameId, string dlcId)>();
}
}
@ -141,25 +150,29 @@ internal static class ProgramData
else
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))
return null;
try
{
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
{
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
{
@ -168,6 +181,9 @@ internal static class ProgramData
else
File.WriteAllText(KoaloaderProxyChoicesPath, JsonConvert.SerializeObject(choices));
}
catch { }
catch
{
// ignored
}
}
}
}