This commit is contained in:
pointfeev 2023-01-05 13:22:44 -05:00
parent 0e48a18eee
commit 05e2ea5519
32 changed files with 1391 additions and 1735 deletions

View file

@ -21,31 +21,25 @@ internal class ContextMenuItem : ToolStripMenuItem
OnClickEvent = onClick; OnClickEvent = onClick;
} }
internal ContextMenuItem(string text, string imageIdentifier, EventHandler onClick = null) internal ContextMenuItem(string text, string imageIdentifier, EventHandler onClick = null) : this(text, onClick)
: this(text, onClick) => _ = TryImageIdentifier(this, imageIdentifier); => _ = TryImageIdentifier(this, imageIdentifier);
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, EventHandler onClick = null) internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, EventHandler onClick = null) : this(text, onClick)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo); => _ = TryImageIdentifierInfo(this, imageIdentifierInfo);
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, string imageIdentifierFallback, EventHandler onClick = null) :
string imageIdentifierFallback, EventHandler onClick = null) this(text, onClick)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo, => _ = TryImageIdentifierInfo(this, imageIdentifierInfo, async () => await TryImageIdentifier(this, imageIdentifierFallback));
async () => await TryImageIdentifier(
this, imageIdentifierFallback));
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, (string id, string iconUrl) imageIdentifierInfoFallback,
(string id, string iconUrl) imageIdentifierInfoFallback, EventHandler onClick = null) EventHandler onClick = null) : this(text, onClick)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo, => _ = TryImageIdentifierInfo(this, imageIdentifierInfo, async () => await TryImageIdentifierInfo(this, imageIdentifierInfoFallback));
async () => await TryImageIdentifierInfo(
this, imageIdentifierInfoFallback));
private static async Task TryImageIdentifier(ContextMenuItem item, string imageIdentifier) => await Task.Run( private static async Task TryImageIdentifier(ContextMenuItem item, string imageIdentifier)
async () => => await Task.Run(async () =>
{ {
if (images.TryGetValue(imageIdentifier, out Image image) && image is not null) if (images.TryGetValue(imageIdentifier, out Image image) && image is not null)
{
item.Image = image; item.Image = image;
}
else else
{ {
switch (imageIdentifier) switch (imageIdentifier)
@ -68,28 +62,22 @@ internal class ContextMenuItem : ToolStripMenuItem
image = IconGrabber.GetFileExplorerImage(); image = IconGrabber.GetFileExplorerImage();
break; break;
case "SteamDB": case "SteamDB":
image = await HttpClientManager.GetImageFromUrl( image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("steamdb.info"));
IconGrabber.GetDomainFaviconUrl("steamdb.info"));
break; break;
case "Steam Store": case "Steam Store":
image = await HttpClientManager.GetImageFromUrl( image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("store.steampowered.com"));
IconGrabber.GetDomainFaviconUrl("store.steampowered.com"));
break; break;
case "Steam Community": case "Steam Community":
image = await HttpClientManager.GetImageFromUrl( image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("steamcommunity.com"));
IconGrabber.GetDomainFaviconUrl("steamcommunity.com"));
break; break;
case "ScreamDB": case "ScreamDB":
image = await HttpClientManager.GetImageFromUrl( image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("scream-db.web.app"));
IconGrabber.GetDomainFaviconUrl("scream-db.web.app"));
break; break;
case "Epic Games": case "Epic Games":
image = await HttpClientManager.GetImageFromUrl( image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("epicgames.com"));
IconGrabber.GetDomainFaviconUrl("epicgames.com"));
break; break;
case "Ubisoft Store": case "Ubisoft Store":
image = await HttpClientManager.GetImageFromUrl( image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("store.ubi.com"));
IconGrabber.GetDomainFaviconUrl("store.ubi.com"));
break; break;
default: default:
return; return;
@ -102,16 +90,13 @@ internal class ContextMenuItem : ToolStripMenuItem
} }
}); });
private static async Task TryImageIdentifierInfo(ContextMenuItem item, private static async Task TryImageIdentifierInfo(ContextMenuItem item, (string id, string iconUrl) imageIdentifierInfo, Action onFail = null)
(string id, string iconUrl) imageIdentifierInfo, => await Task.Run(async () =>
Action onFail = null) => await Task.Run(async () =>
{ {
(string id, string iconUrl) = imageIdentifierInfo; (string id, string iconUrl) = imageIdentifierInfo;
string imageIdentifier = "Icon_" + id; string imageIdentifier = "Icon_" + id;
if (images.TryGetValue(imageIdentifier, out Image image) && image is not null) if (images.TryGetValue(imageIdentifier, out Image image) && image is not null)
{
item.Image = image; item.Image = image;
}
else else
{ {
image = await HttpClientManager.GetImageFromUrl(iconUrl); image = await HttpClientManager.GetImageFromUrl(iconUrl);
@ -121,16 +106,15 @@ internal class ContextMenuItem : ToolStripMenuItem
item.Image = image; item.Image = image;
} }
else if (onFail is not null) else if (onFail is not null)
{
onFail(); onFail();
} }
}
}); });
protected override void OnClick(EventArgs e) protected override void OnClick(EventArgs e)
{ {
base.OnClick(e); base.OnClick(e);
if (OnClickEvent is null) return; if (OnClickEvent is null)
return;
OnClickEvent.Invoke(this, e); OnClickEvent.Invoke(this, e);
} }
} }

View file

@ -60,17 +60,13 @@ internal class CustomForm : Form
_ = helpDialog.Show(SystemIcons.Information, _ = helpDialog.Show(SystemIcons.Information,
"Automatically finds all installed Steam, Epic and Ubisoft games with their respective DLC-related DLL locations on the user's computer,\n" "Automatically finds all installed Steam, Epic and Ubisoft games with their respective DLC-related DLL locations on the user's computer,\n"
+ "parses SteamCMD, Steam Store and Epic Games Store for user-selected games' DLCs, then provides a very simple graphical interface\n" + "parses SteamCMD, Steam Store and Epic Games Store for user-selected games' DLCs, then provides a very simple graphical interface\n"
+ "utilizing the gathered information for the maintenance of DLC unlockers.\n" + "utilizing the gathered information for the maintenance of DLC unlockers.\n" + "\n"
+ "\n"
+ $"The program utilizes the latest versions of [Koaloader]({acidicoala}/Koaloader), [SmokeAPI]({acidicoala}/SmokeAPI), [ScreamAPI]({acidicoala}/ScreamAPI), [Uplay R1 Unlocker]({acidicoala}/UplayR1Unlocker) and [Uplay R2 Unlocker]({acidicoala}/UplayR2Unlocker), all by\n" + $"The program utilizes the latest versions of [Koaloader]({acidicoala}/Koaloader), [SmokeAPI]({acidicoala}/SmokeAPI), [ScreamAPI]({acidicoala}/ScreamAPI), [Uplay R1 Unlocker]({acidicoala}/UplayR1Unlocker) and [Uplay R2 Unlocker]({acidicoala}/UplayR2Unlocker), all by\n"
+ $"the wonderful [acidicoala]({acidicoala}), and all downloaded and embedded into the program itself; no further downloads necessary on your part!\n" + $"the wonderful [acidicoala]({acidicoala}), and all downloaded and embedded into the program itself; no further downloads necessary on your part!\n"
+ "\n" + "\n" + "NOTE: This program does not automatically download nor install actual DLC files for you. As the title of the program says, it's\n"
+ "NOTE: This program does not automatically download nor install actual DLC files for you. As the title of the program says, it's\n"
+ "only a DLC Unlocker installer. Should the game you wish to unlock DLC for not already come with the DLCs installed (very many\n" + "only a DLC Unlocker installer. Should the game you wish to unlock DLC for not already come with the DLCs installed (very many\n"
+ "do not), you have to find, download, and install those yourself. Preferably, you should be referring to the proper cs.rin.ru post for\n" + "do not), you have to find, download, and install those yourself. Preferably, you should be referring to the proper cs.rin.ru post for\n"
+ "the game(s) you're tinkering with; you'll usually find any answer to your problems there.\n" + "the game(s) you're tinkering with; you'll usually find any answer to your problems there.\n" + "\n" + "USAGE:\n"
+ "\n"
+ "USAGE:\n"
+ " 1. Choose which programs and/or games the program should scan for DLC.\n" + " 1. Choose which programs and/or games the program should scan for DLC.\n"
+ " The program automatically gathers all installed games from Steam, Epic and Ubisoft directories.\n" + " The program automatically gathers all installed games from Steam, Epic and Ubisoft directories.\n"
+ " 2. Wait for the program to download and install SteamCMD (if you chose a Steam game).\n" + " 2. Wait for the program to download and install SteamCMD (if you chose a Steam game).\n"
@ -80,14 +76,11 @@ internal class CustomForm : Form
+ " Obviously none of the DLC unlockers are tested for every single game!\n" + " Obviously none of the DLC unlockers are tested for every single game!\n"
+ " 5. Choose whether or not to install with Koaloader, and if so then also pick the proxy DLL to use.\n" + " 5. Choose whether or not to install with Koaloader, and if so then also pick the proxy DLL to use.\n"
+ " If the default \'version.dll\' doesn't work, then see [here](https://cs.rin.ru/forum/viewtopic.php?p=2552172#p2552172) to find one that does.\n" + " If the default \'version.dll\' doesn't work, then see [here](https://cs.rin.ru/forum/viewtopic.php?p=2552172#p2552172) to find one that does.\n"
+ " 6. Click the \"Generate and Install\" button.\n" + " 6. Click the \"Generate and Install\" button.\n" + " 7. Click the \"OK\" button to close the program.\n"
+ " 7. Click the \"OK\" button to close the program.\n"
+ " 8. If any of the DLC unlockers cause problems with any of the games you installed them on, simply go back\n" + " 8. If any of the DLC unlockers cause problems with any of the games you installed them on, simply go back\n"
+ " to step 5 and select what games you wish you revert changes to, and instead click the \"Uninstall\" button this time.\n" + " to step 5 and select what games you wish you revert changes to, and instead click the \"Uninstall\" button this time.\n" + "\n"
+ "\n"
+ $"For reliable and quick assistance, all bugs, crashes and other issues should be referred to the [GitHub Issues]({repository}/issues) page!\n" + $"For reliable and quick assistance, all bugs, crashes and other issues should be referred to the [GitHub Issues]({repository}/issues) page!\n"
+ "\n" + "\n" + "SteamCMD installation and appinfo cache can be found at [C:\\ProgramData\\CreamInstaller]().\n"
+ "SteamCMD installation and appinfo cache can be found at [C:\\ProgramData\\CreamInstaller]().\n"
+ $"The program automatically and very quickly updates from [GitHub]({repository}) using [Onova](https://github.com/Tyrrrz/Onova). (updates can be ignored)\n" + $"The program automatically and very quickly updates from [GitHub]({repository}) using [Onova](https://github.com/Tyrrrz/Onova). (updates can be ignored)\n"
+ $"The program source and other information can be found on [GitHub]({repository})."); + $"The program source and other information can be found on [GitHub]({repository}).");
} }
@ -96,8 +89,7 @@ internal class CustomForm : Form
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern void SetWindowPos(nint hWnd, nint hWndInsertAfter, int x, int y, int cx, int cy, internal static extern void SetWindowPos(nint hWnd, nint hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
uint uFlags);
internal void BringToFrontWithoutActivation() internal void BringToFrontWithoutActivation()
{ {
@ -113,12 +105,13 @@ internal class CustomForm : Form
return; return;
int X = fromForm.Location.X + fromForm.Size.Width / 2 - Size.Width / 2; int X = fromForm.Location.X + fromForm.Size.Width / 2 - Size.Width / 2;
int Y = fromForm.Location.Y + fromForm.Size.Height / 2 - Size.Height / 2; int Y = fromForm.Location.Y + fromForm.Size.Height / 2 - Size.Height / 2;
Location = new Point(X, Y); Location = new(X, Y);
} }
private void OnKeyPress(object s, KeyPressEventArgs e) private void OnKeyPress(object s, KeyPressEventArgs e)
{ {
if (e.KeyChar != 'S') return; // Shift + S if (e.KeyChar != 'S')
return; // Shift + S
UpdateBounds(); UpdateBounds();
Rectangle bounds = Bounds; Rectangle bounds = Bounds;
using Bitmap bitmap = new(Size.Width - 14, Size.Height - 7); using Bitmap bitmap = new(Size.Width - 14, Size.Height - 7);
@ -127,8 +120,7 @@ internal class CustomForm : Form
using EncoderParameters encoding = new(1); using EncoderParameters encoding = new(1);
using EncoderParameter encoderParam = new(Encoder.Quality, 100L); using EncoderParameter encoderParam = new(Encoder.Quality, 100L);
encoding.Param[0] = encoderParam; encoding.Param[0] = encoderParam;
graphics.CopyFromScreen(new Point(bounds.Left + 7, bounds.Top), Point.Empty, graphics.CopyFromScreen(new(bounds.Left + 7, bounds.Top), Point.Empty, new(Size.Width - 14, Size.Height - 7));
new Size(Size.Width - 14, Size.Height - 7));
Clipboard.SetImage(bitmap); Clipboard.SetImage(bitmap);
e.Handled = true; e.Handled = true;
} }

View file

@ -65,8 +65,7 @@ internal class CustomTreeView : TreeView
Size size; Size size;
Rectangle bounds = node.Bounds; Rectangle bounds = node.Bounds;
Rectangle selectionBounds = bounds; Rectangle selectionBounds = bounds;
Color Color color; // = highlighted ? SystemColors.HighlightText : (node.ForeColor != Color.Empty) ? node.ForeColor : node.TreeView.ForeColor;
color; // = highlighted ? SystemColors.HighlightText : (node.ForeColor != Color.Empty) ? node.ForeColor : node.TreeView.ForeColor;
Point point; Point point;
/*Size textSize = TextRenderer.MeasureText(text, font); /*Size textSize = TextRenderer.MeasureText(text, font);
Point textLoc = new(bounds.X - 1, bounds.Y); Point textLoc = new(bounds.X - 1, bounds.Y);
@ -88,8 +87,7 @@ internal class CustomTreeView : TreeView
text = platform.ToString(); text = platform.ToString();
size = TextRenderer.MeasureText(graphics, text, font); size = TextRenderer.MeasureText(graphics, text, font);
bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width }; bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width };
selectionBounds selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
= new Rectangle(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
graphics.FillRectangle(brush, bounds); graphics.FillRectangle(brush, bounds);
point = new Point(bounds.Location.X - 1, bounds.Location.Y + 1); point = new Point(bounds.Location.X - 1, bounds.Location.Y + 1);
TextRenderer.DrawText(graphics, text, font, point, color, TextFormatFlags.Default); TextRenderer.DrawText(graphics, text, font, point, color, TextFormatFlags.Default);
@ -104,8 +102,7 @@ internal class CustomTreeView : TreeView
size = TextRenderer.MeasureText(graphics, text, font); size = TextRenderer.MeasureText(graphics, text, font);
int left = -4; int left = -4;
bounds = bounds with { X = bounds.X + bounds.Width + left, Width = size.Width }; bounds = bounds with { X = bounds.X + bounds.Width + left, Width = size.Width };
selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width + left, 0));
selectionBounds.Size + new Size(bounds.Size.Width + left, 0));
graphics.FillRectangle(brush, bounds); graphics.FillRectangle(brush, bounds);
point = new Point(bounds.Location.X - 1, bounds.Location.Y + 1); point = new Point(bounds.Location.X - 1, bounds.Location.Y + 1);
TextRenderer.DrawText(graphics, text, font, point, color, TextFormatFlags.Default); TextRenderer.DrawText(graphics, text, font, point, color, TextFormatFlags.Default);
@ -130,8 +127,7 @@ internal class CustomTreeView : TreeView
: CheckBoxState.UncheckedDisabled; : CheckBoxState.UncheckedDisabled;
size = CheckBoxRenderer.GetGlyphSize(graphics, checkBoxState); size = CheckBoxRenderer.GetGlyphSize(graphics, checkBoxState);
bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width }; bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width };
selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
selectionBounds.Size + bounds.Size with { Height = 0 });
Rectangle checkBoxBounds = bounds; Rectangle checkBoxBounds = bounds;
graphics.FillRectangle(backBrush, bounds); graphics.FillRectangle(backBrush, bounds);
point = new Point(bounds.Left, bounds.Top + bounds.Height / 2 - size.Height / 2 - 1); point = new Point(bounds.Left, bounds.Top + bounds.Height / 2 - size.Height / 2 - 1);
@ -140,41 +136,31 @@ internal class CustomTreeView : TreeView
size = TextRenderer.MeasureText(graphics, text, font); size = TextRenderer.MeasureText(graphics, text, font);
int left = 1; int left = 1;
bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width + left }; bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width + left };
selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
selectionBounds.Size + bounds.Size with { Height = 0 }); checkBoxBounds = new Rectangle(checkBoxBounds.Location, checkBoxBounds.Size + bounds.Size with { Height = 0 });
checkBoxBounds = new Rectangle(checkBoxBounds.Location,
checkBoxBounds.Size + bounds.Size with { Height = 0 });
graphics.FillRectangle(backBrush, bounds); graphics.FillRectangle(backBrush, bounds);
point = new Point(bounds.Location.X - 1 + left, bounds.Location.Y + 1); point = new Point(bounds.Location.X - 1 + left, bounds.Location.Y + 1);
TextRenderer.DrawText(graphics, text, font, point, TextRenderer.DrawText(graphics, text, font, point, Enabled ? ColorTranslator.FromHtml("#006900") : ColorTranslator.FromHtml("#69AA69"),
Enabled
? ColorTranslator.FromHtml("#006900")
: ColorTranslator.FromHtml("#69AA69"),
TextFormatFlags.Default); TextFormatFlags.Default);
this.checkBoxBounds[selection] = RectangleToClient(checkBoxBounds); this.checkBoxBounds[selection] = RectangleToClient(checkBoxBounds);
string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy; string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy;
if (selection.Koaloader && proxy is not null) if (selection.Koaloader && proxy is not null)
{ {
comboBoxFont ??= new Font(font.FontFamily, 6, font.Style, font.Unit, font.GdiCharSet, comboBoxFont ??= new Font(font.FontFamily, 6, font.Style, font.Unit, font.GdiCharSet, font.GdiVerticalFont);
font.GdiVerticalFont);
ComboBoxState comboBoxState = Enabled ? ComboBoxState.Normal : ComboBoxState.Disabled; ComboBoxState comboBoxState = Enabled ? ComboBoxState.Normal : ComboBoxState.Disabled;
text = proxy + ".dll"; text = proxy + ".dll";
size = TextRenderer.MeasureText(graphics, text, comboBoxFont) + new Size(6, 0); size = TextRenderer.MeasureText(graphics, text, comboBoxFont) + new Size(6, 0);
int padding = 2; int padding = 2;
bounds = new Rectangle(bounds.X + bounds.Width, bounds.Y + padding / 2, size.Width, bounds = new Rectangle(bounds.X + bounds.Width, bounds.Y + padding / 2, size.Width, bounds.Height - padding);
bounds.Height - padding); selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
selectionBounds = new Rectangle(selectionBounds.Location,
selectionBounds.Size + bounds.Size with { Height = 0 });
Rectangle comboBoxBounds = bounds; Rectangle comboBoxBounds = bounds;
graphics.FillRectangle(backBrush, bounds); graphics.FillRectangle(backBrush, bounds);
ComboBoxRenderer.DrawTextBox(graphics, bounds, text, comboBoxFont, comboBoxState); ComboBoxRenderer.DrawTextBox(graphics, bounds, text, comboBoxFont, comboBoxState);
size = new Size(14, 0); size = new Size(14, 0);
left = -1; left = -1;
bounds = bounds with { X = bounds.X + bounds.Width + left, Width = size.Width }; bounds = bounds with { X = bounds.X + bounds.Width + left, Width = size.Width };
selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds = new Rectangle(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width + left, 0));
selectionBounds.Size + new Size(bounds.Size.Width + left, 0)); comboBoxBounds = new Rectangle(comboBoxBounds.Location, comboBoxBounds.Size + new Size(bounds.Size.Width + left, 0));
comboBoxBounds = new Rectangle(comboBoxBounds.Location,
comboBoxBounds.Size + new Size(bounds.Size.Width + left, 0));
ComboBoxRenderer.DrawDropDownButton(graphics, bounds, comboBoxState); ComboBoxRenderer.DrawDropDownButton(graphics, bounds, comboBoxState);
this.comboBoxBounds[selection] = RectangleToClient(comboBoxBounds); this.comboBoxBounds[selection] = RectangleToClient(comboBoxBounds);
} }
@ -215,11 +201,9 @@ internal class CustomTreeView : TreeView
} }
else if (pair.Value.Contains(clickPoint)) else if (pair.Value.Contains(clickPoint))
{ {
List<string> proxies = EmbeddedResources.FindAll(r => r.StartsWith("Koaloader")) List<string> proxies = EmbeddedResources.FindAll(r => r.StartsWith("Koaloader")).Select(p =>
.Select(p =>
{ {
p.GetProxyInfoFromIdentifier( p.GetProxyInfoFromIdentifier(out string proxyName, out _);
out string proxyName, out _);
return proxyName; return proxyName;
}).Distinct().ToList(); }).Distinct().ToList();
comboBoxDropDown ??= new ToolStripDropDown(); comboBoxDropDown ??= new ToolStripDropDown();
@ -240,8 +224,7 @@ internal class CustomTreeView : TreeView
if (canUse) if (canUse)
_ = comboBoxDropDown.Items.Add(new ToolStripButton(proxy + ".dll", null, (s, e) => _ = comboBoxDropDown.Items.Add(new ToolStripButton(proxy + ".dll", null, (s, e) =>
{ {
pair.Key.KoaloaderProxy pair.Key.KoaloaderProxy = proxy == ProgramSelection.DefaultKoaloaderProxy ? null : proxy;
= proxy == ProgramSelection.DefaultKoaloaderProxy ? null : proxy;
selectForm.OnKoaloaderChanged(); selectForm.OnKoaloaderChanged();
}) { Font = comboBoxFont }); }) { Font = comboBoxFont });
} }

View file

@ -14,16 +14,16 @@ internal static class PlatformIdComparer
private static NodeNameComparer nodeNameComparer; private static NodeNameComparer nodeNameComparer;
private static NodeTextComparer nodeTextComparer; private static NodeTextComparer nodeTextComparer;
internal static StringComparer String => stringComparer ??= new StringComparer(); internal static StringComparer String => stringComparer ??= new();
internal static NodeComparer Node => nodeComparer ??= new NodeComparer(); internal static NodeComparer Node => nodeComparer ??= new();
internal static NodeNameComparer NodeName => nodeNameComparer ??= new NodeNameComparer(); internal static NodeNameComparer NodeName => nodeNameComparer ??= new();
internal static NodeTextComparer NodeText => nodeTextComparer ??= new NodeTextComparer(); internal static NodeTextComparer NodeText => nodeTextComparer ??= new();
} }
internal class StringComparer : IComparer<string> internal class StringComparer : IComparer<string>
{ {
public int Compare(string a, string b) => public int Compare(string a, string b)
!int.TryParse(a, out _) && !int.TryParse(b, out _) => !int.TryParse(a, out _) && !int.TryParse(b, out _)
? string.Compare(a, b, StringComparison.Ordinal) ? string.Compare(a, b, StringComparison.Ordinal)
: !int.TryParse(a, out int A) : !int.TryParse(a, out int A)
? 1 ? 1
@ -38,8 +38,8 @@ internal class StringComparer : IComparer<string>
internal class NodeComparer : IComparer<TreeNode> internal class NodeComparer : IComparer<TreeNode>
{ {
public int Compare(TreeNode a, TreeNode b) => public int Compare(TreeNode a, TreeNode b)
a.Tag is not Platform A => a.Tag is not Platform A
? 1 ? 1
: b.Tag is not Platform B : b.Tag is not Platform B
? -1 ? -1
@ -52,8 +52,8 @@ internal class NodeComparer : IComparer<TreeNode>
internal class NodeNameComparer : IComparer internal class NodeNameComparer : IComparer
{ {
public int Compare(object a, object b) => public int Compare(object a, object b)
a is not TreeNode A => a is not TreeNode A
? 1 ? 1
: b is not TreeNode B : b is not TreeNode B
? -1 ? -1
@ -64,8 +64,8 @@ internal class NodeNameComparer : IComparer
internal class NodeTextComparer : IComparer internal class NodeTextComparer : IComparer
{ {
public int Compare(object a, object b) => public int Compare(object a, object b)
a is not TreeNode A => a is not TreeNode A
? 1 ? 1
: b is not TreeNode B : b is not TreeNode B
? -1 ? -1

View file

@ -24,7 +24,7 @@ internal partial class DebugForm : CustomForm
{ {
if (current is not null && (current.Disposing || current.IsDisposed)) if (current is not null && (current.Disposing || current.IsDisposed))
current = null; current = null;
return current ??= new DebugForm(); return current ??= new();
} }
set => current = value; set => current = value;
} }
@ -64,7 +64,7 @@ internal partial class DebugForm : CustomForm
if (attachedForm is not null && attachedForm.Visible) if (attachedForm is not null && attachedForm.Visible)
{ {
//Size = new(Size.Width, attachedForm.Size.Height); //Size = new(Size.Width, attachedForm.Size.Height);
Location = new Point(attachedForm.Right, attachedForm.Top); Location = new(attachedForm.Right, attachedForm.Top);
BringToFrontWithoutActivation(); BringToFrontWithoutActivation();
} }
} }

View file

@ -12,8 +12,8 @@ internal partial class DialogForm : CustomForm
{ {
internal DialogForm(IWin32Window owner) : base(owner) => InitializeComponent(); internal DialogForm(IWin32Window owner) : base(owner) => InitializeComponent();
internal DialogResult Show(Icon descriptionIcon, string descriptionText, string acceptButtonText = "OK", internal DialogResult Show(Icon descriptionIcon, string descriptionText, string acceptButtonText = "OK", string cancelButtonText = null,
string cancelButtonText = null, string customFormText = null, Icon customFormIcon = null) string customFormText = null, Icon customFormIcon = null)
{ {
descriptionIcon ??= Icon; descriptionIcon ??= Icon;
icon.Image = descriptionIcon.ToBitmap(); icon.Image = descriptionIcon.ToBitmap();
@ -32,7 +32,7 @@ internal partial class DialogForm : CustomForm
if (string.IsNullOrWhiteSpace(link)) if (string.IsNullOrWhiteSpace(link))
link = text; link = text;
descriptionText = descriptionText.Remove(i, linkRight + 1 - i).Insert(i, text); descriptionText = descriptionText.Remove(i, linkRight + 1 - i).Insert(i, text);
links.Add(new LinkLabel.Link(i, text.Length, link)); links.Add(new(i, text.Length, link));
} }
} }
descriptionLabel.Text = descriptionText; descriptionLabel.Text = descriptionText;
@ -43,13 +43,9 @@ internal partial class DialogForm : CustomForm
cancelButton.Visible = false; cancelButton.Visible = false;
} }
else else
{
cancelButton.Text = cancelButtonText; cancelButton.Text = cancelButtonText;
}
if (customFormText is not null) if (customFormText is not null)
{
Text = customFormText; Text = customFormText;
}
else else
{ {
OnResize(null, null); OnResize(null, null);
@ -61,16 +57,13 @@ internal partial class DialogForm : CustomForm
{ {
foreach (LinkLabel.Link link in links) foreach (LinkLabel.Link link in links)
_ = descriptionLabel.Links.Add(link); _ = descriptionLabel.Links.Add(link);
descriptionLabel.LinkClicked += (s, e) descriptionLabel.LinkClicked += (s, e) => Process.Start(new ProcessStartInfo((string)e.Link.LinkData) { UseShellExecute = true });
=> Process.Start(new ProcessStartInfo((string)e.Link.LinkData) { UseShellExecute = true });
} }
return ShowDialog(); return ShowDialog();
} }
private void OnResize(object s, EventArgs e) => private void OnResize(object s, EventArgs e)
Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100 => Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100
? TextRenderer.MeasureText(Program.ApplicationNameShort, Font).Width > Size.Width - 100 ? TextRenderer.MeasureText(Program.ApplicationNameShort, Font).Width > Size.Width - 100 ? Program.Name : Program.ApplicationNameShort
? Program.Name
: Program.ApplicationNameShort
: Program.ApplicationName; : Program.ApplicationName;
} }

View file

@ -38,18 +38,21 @@ internal partial class InstallForm : CustomForm
userProgressBar.Invoke(() => userProgressBar.Invoke(() =>
{ {
int value = (int)((float)CompleteOperationsCount / OperationsCount * 100) + progress / OperationsCount; int value = (int)((float)CompleteOperationsCount / OperationsCount * 100) + progress / OperationsCount;
if (value < userProgressBar.Value) return; if (value < userProgressBar.Value)
return;
userProgressBar.Value = value; userProgressBar.Value = value;
}); });
} }
internal void UpdateUser(string text, Color color, bool info = true, bool log = true) internal void UpdateUser(string text, Color color, bool info = true, bool log = true)
{ {
if (info) _ = userInfoLabel.Invoke(() => userInfoLabel.Text = text); if (info)
_ = userInfoLabel.Invoke(() => userInfoLabel.Text = text);
if (log && !logTextBox.Disposing && !logTextBox.IsDisposed) if (log && !logTextBox.Disposing && !logTextBox.IsDisposed)
logTextBox.Invoke(() => logTextBox.Invoke(() =>
{ {
if (logTextBox.Text.Length > 0) logTextBox.AppendText(Environment.NewLine, color); if (logTextBox.Text.Length > 0)
logTextBox.AppendText(Environment.NewLine, color);
logTextBox.AppendText(text, color); logTextBox.AppendText(text, color);
logTextBox.Invalidate(); logTextBox.Invalidate();
}); });
@ -63,12 +66,11 @@ internal partial class InstallForm : CustomForm
UpdateUser("Repairing Paradox Launcher . . . ", LogTextBox.Operation); UpdateUser("Repairing Paradox Launcher . . . ", LogTextBox.Operation);
_ = await Repair(this, selection); _ = await Repair(this, selection);
} }
UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")}" + UpdateUser(
$" {(Uninstalling ? "from" : "for")} " + selection.Name $"{(Uninstalling ? "Uninstalling" : "Installing")}" + $" {(Uninstalling ? "from" : "for")} " + selection.Name
+ $" with root directory \"{selection.RootDirectory}\" . . . ", LogTextBox.Operation); + $" with root directory \"{selection.RootDirectory}\" . . . ", LogTextBox.Operation);
IEnumerable<string> invalidDirectories = (await selection.RootDirectory.GetExecutables()) IEnumerable<string> invalidDirectories = (await selection.RootDirectory.GetExecutables())
?.Where(d => !selection.ExecutableDirectories.Any( ?.Where(d => !selection.ExecutableDirectories.Any(s => s.directory == Path.GetDirectoryName(d.path)))
s => s.directory == Path.GetDirectoryName(d.path)))
?.Select(d => Path.GetDirectoryName(d.path)); ?.Select(d => Path.GetDirectoryName(d.path));
if (!selection.ExecutableDirectories.Any(s => s.directory == selection.RootDirectory)) if (!selection.ExecutableDirectories.Any(s => s.directory == selection.RootDirectory))
invalidDirectories = invalidDirectories?.Append(selection.RootDirectory); invalidDirectories = invalidDirectories?.Append(selection.RootDirectory);
@ -76,16 +78,13 @@ internal partial class InstallForm : CustomForm
if (invalidDirectories is not null) if (invalidDirectories is not null)
foreach (string directory in invalidDirectories) foreach (string directory in invalidDirectories)
{ {
if (Program.Canceled) throw new CustomMessageException("The operation was canceled."); 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 config);
if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader)) if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader))
|| (directory != selection.RootDirectory || directory != selection.RootDirectory && Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll)) || File.Exists(config))
&& Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll)))
|| File.Exists(config))
{ {
UpdateUser("Uninstalling Koaloader from " + selection.Name UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in incorrect directory \"{directory}\" . . . ", LogTextBox.Operation);
+ $" in incorrect directory \"{directory}\" . . . ",
LogTextBox.Operation);
await Koaloader.Uninstall(directory, selection.RootDirectory, this); await Koaloader.Uninstall(directory, selection.RootDirectory, this);
} }
Thread.Sleep(1); Thread.Sleep(1);
@ -93,15 +92,13 @@ internal partial class InstallForm : CustomForm
if (Uninstalling || !selection.Koaloader) if (Uninstalling || !selection.Koaloader)
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories) foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
{ {
if (Program.Canceled) throw new CustomMessageException("The operation was canceled."); 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 config);
if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader)) if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader))
|| Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll)) || Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll)) || File.Exists(config))
|| File.Exists(config))
{ {
UpdateUser( UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
"Uninstalling Koaloader from " + selection.Name + $" in directory \"{directory}\" . . . ",
LogTextBox.Operation);
await Koaloader.Uninstall(directory, selection.RootDirectory, this); await Koaloader.Uninstall(directory, selection.RootDirectory, this);
} }
Thread.Sleep(1); Thread.Sleep(1);
@ -110,17 +107,18 @@ internal partial class InstallForm : CustomForm
int count = selection.DllDirectories.Count, cur = 0; int count = selection.DllDirectories.Count, cur = 0;
foreach (string directory in selection.DllDirectories) foreach (string directory in selection.DllDirectories)
{ {
if (Program.Canceled) throw new CustomMessageException("The operation was canceled."); if (Program.Canceled)
throw new CustomMessageException("The operation was canceled.");
if (selection.Platform is Platform.Steam or Platform.Paradox) if (selection.Platform is Platform.Steam or Platform.Paradox)
{ {
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config,
out string api64_o, out string config, out string cache); out string cache);
if (uninstallProxy if (uninstallProxy
? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) || File.Exists(cache) ? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) || File.Exists(cache)
: File.Exists(api32) || File.Exists(api64)) : File.Exists(api32) || File.Exists(api64))
{ {
UpdateUser($"{(uninstallProxy ? "Uninstalling" : "Installing")} SmokeAPI" + UpdateUser(
$" {(uninstallProxy ? "from" : "for")} " + selection.Name $"{(uninstallProxy ? "Uninstalling" : "Installing")} SmokeAPI" + $" {(uninstallProxy ? "from" : "for")} " + selection.Name
+ $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
if (uninstallProxy) if (uninstallProxy)
await SmokeAPI.Uninstall(directory, this); await SmokeAPI.Uninstall(directory, this);
@ -130,14 +128,11 @@ internal partial class InstallForm : CustomForm
} }
if (selection.Platform is Platform.Epic or Platform.Paradox) if (selection.Platform is Platform.Epic or Platform.Paradox)
{ {
directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
out string api64_o, out string config); if (uninstallProxy ? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) : File.Exists(api32) || File.Exists(api64))
if (uninstallProxy
? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config)
: File.Exists(api32) || File.Exists(api64))
{ {
UpdateUser($"{(uninstallProxy ? "Uninstalling" : "Installing")} ScreamAPI" + UpdateUser(
$" {(uninstallProxy ? "from" : "for")} " + selection.Name $"{(uninstallProxy ? "Uninstalling" : "Installing")} ScreamAPI" + $" {(uninstallProxy ? "from" : "for")} " + selection.Name
+ $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
if (uninstallProxy) if (uninstallProxy)
await ScreamAPI.Uninstall(directory, this); await ScreamAPI.Uninstall(directory, this);
@ -147,28 +142,24 @@ internal partial class InstallForm : CustomForm
} }
if (selection.Platform is Platform.Ubisoft) if (selection.Platform is Platform.Ubisoft)
{ {
directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
out string api64_o, out string config); if (uninstallProxy ? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) : File.Exists(api32) || File.Exists(api64))
if (uninstallProxy
? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config)
: File.Exists(api32) || File.Exists(api64))
{ {
UpdateUser($"{(uninstallProxy ? "Uninstalling" : "Installing")} Uplay R1 Unlocker" + UpdateUser(
$" {(uninstallProxy ? "from" : "for")} " + selection.Name $"{(uninstallProxy ? "Uninstalling" : "Installing")} Uplay R1 Unlocker" + $" {(uninstallProxy ? "from" : "for")} " + selection.Name
+ $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
if (uninstallProxy) if (uninstallProxy)
await UplayR1.Uninstall(directory, this); await UplayR1.Uninstall(directory, this);
else else
await UplayR1.Install(directory, selection, this); await UplayR1.Install(directory, selection, this);
} }
directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config);
out api64, out api64_o, out config);
if (uninstallProxy if (uninstallProxy
? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) ? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config)
: File.Exists(old_api32) || File.Exists(old_api64) || File.Exists(api32) || File.Exists(api64)) : File.Exists(old_api32) || File.Exists(old_api64) || File.Exists(api32) || File.Exists(api64))
{ {
UpdateUser($"{(uninstallProxy ? "Uninstalling" : "Installing")} Uplay R2 Unlocker" + UpdateUser(
$" {(uninstallProxy ? "from" : "for")} " + selection.Name $"{(uninstallProxy ? "Uninstalling" : "Installing")} Uplay R2 Unlocker" + $" {(uninstallProxy ? "from" : "for")} " + selection.Name
+ $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
if (uninstallProxy) if (uninstallProxy)
await UplayR2.Uninstall(directory, this); await UplayR2.Uninstall(directory, this);
@ -182,9 +173,9 @@ internal partial class InstallForm : CustomForm
if (selection.Koaloader && !Uninstalling) if (selection.Koaloader && !Uninstalling)
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories) foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
{ {
if (Program.Canceled) throw new CustomMessageException("The operation was canceled."); if (Program.Canceled)
UpdateUser("Installing Koaloader to " + selection.Name + $" in directory \"{directory}\" . . . ", throw new CustomMessageException("The operation was canceled.");
LogTextBox.Operation); UpdateUser("Installing Koaloader to " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
await Koaloader.Install(directory, binaryType, selection, selection.RootDirectory, this); await Koaloader.Install(directory, binaryType, selection, selection.RootDirectory, this);
Thread.Sleep(1); Thread.Sleep(1);
} }
@ -236,13 +227,12 @@ internal partial class InstallForm : CustomForm
try try
{ {
await Operate(); await Operate();
UpdateUser($"DLC unlocker(s) successfully {(Uninstalling ? "uninstalled" : "installed and generated")} for " UpdateUser($"DLC unlocker(s) successfully {(Uninstalling ? "uninstalled" : "installed and generated")} for " + ProgramCount + " program(s).",
+ ProgramCount + " program(s).", LogTextBox.Success); LogTextBox.Success);
} }
catch (Exception exception) catch (Exception exception)
{ {
UpdateUser($"DLC unlocker {(Uninstalling ? "uninstallation" : "installation and/or generation")} failed: " UpdateUser($"DLC unlocker {(Uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + exception, LogTextBox.Error);
+ exception, LogTextBox.Error);
retryButton.Enabled = true; retryButton.Enabled = true;
} }
userProgressBar.Value = userProgressBar.Maximum; userProgressBar.Value = userProgressBar.Maximum;
@ -262,7 +252,8 @@ internal partial class InstallForm : CustomForm
} }
catch (Exception e) catch (Exception e)
{ {
if (e.HandleException(this)) goto retry; if (e.HandleException(this))
goto retry;
Close(); Close();
} }
} }

View file

@ -58,24 +58,18 @@ internal partial class MainForm : CustomForm
updateButton.Click -= OnUpdateCancel; updateButton.Click -= OnUpdateCancel;
progressLabel.Text = "Checking for updates . . ."; progressLabel.Text = "Checking for updates . . .";
changelogTreeView.Visible = false; changelogTreeView.Visible = false;
changelogTreeView.Location = progressLabel.Location with changelogTreeView.Location = progressLabel.Location with { Y = progressLabel.Location.Y + progressLabel.Size.Height + 13 };
{
Y = progressLabel.Location.Y + progressLabel.Size.Height + 13
};
Refresh(); Refresh();
#if DEBUG #if DEBUG
DebugForm.Current.Attach(this); DebugForm.Current.Attach(this);
#endif #endif
GithubPackageResolver resolver = new(Program.RepositoryOwner, Program.RepositoryName, GithubPackageResolver resolver = new(Program.RepositoryOwner, Program.RepositoryName, Program.RepositoryPackage);
Program.RepositoryPackage);
ZipPackageExtractor extractor = new(); ZipPackageExtractor extractor = new();
updateManager updateManager = new(AssemblyMetadata.FromAssembly(Program.EntryAssembly, Program.CurrentProcessFilePath), resolver, extractor);
= new UpdateManager(AssemblyMetadata.FromAssembly(Program.EntryAssembly, Program.CurrentProcessFilePath),
resolver, extractor);
if (latestVersion is null) if (latestVersion is null)
{ {
CheckForUpdatesResult checkForUpdatesResult = null; CheckForUpdatesResult checkForUpdatesResult = null;
cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource = new();
try try
{ {
checkForUpdatesResult = await updateManager.CheckForUpdatesAsync(cancellationTokenSource.Token); checkForUpdatesResult = await updateManager.CheckForUpdatesAsync(cancellationTokenSource.Token);
@ -93,8 +87,7 @@ internal partial class MainForm : CustomForm
catch (TaskCanceledException) { } catch (TaskCanceledException) { }
catch (Exception e) catch (Exception e)
{ {
DebugForm.Current.Log($"Exception while checking for updates: {e.GetType()} ({e.Message})", DebugForm.Current.Log($"Exception while checking for updates: {e.GetType()} ({e.Message})", LogTextBox.Warning);
LogTextBox.Warning);
} }
#else #else
catch { } catch { }
@ -120,8 +113,7 @@ internal partial class MainForm : CustomForm
changelogTreeView.Visible = true; changelogTreeView.Visible = true;
Version currentVersion = new(Program.Version); Version currentVersion = new(Program.Version);
#if DEBUG #if DEBUG
foreach (Version version in versions.Where(v => (v > currentVersion || v == latestVersion) foreach (Version version in versions.Where(v => (v > currentVersion || v == latestVersion) && !changelogTreeView.Nodes.ContainsKey(v.ToString())))
&& !changelogTreeView.Nodes.ContainsKey(v.ToString())))
#else #else
foreach (Version version in versions.Where(v => v > currentVersion && !changelogTreeView.Nodes.ContainsKey(v.ToString()))) foreach (Version version in versions.Where(v => v > currentVersion && !changelogTreeView.Nodes.ContainsKey(v.ToString())))
#endif #endif
@ -162,8 +154,7 @@ internal partial class MainForm : CustomForm
{ {
using DialogForm form = new(this); using DialogForm form = new(this);
if (form.Show(SystemIcons.Warning, if (form.Show(SystemIcons.Warning,
"WARNING: " + Program.ApplicationExecutable + " was renamed!" + "WARNING: " + Program.ApplicationExecutable + " was renamed!" + "\n\nThis will cause undesirable behavior when updating the program!",
"\n\nThis will cause undesirable behavior when updating the program!",
"Ignore", "Abort") == DialogResult.Cancel) "Ignore", "Abort") == DialogResult.Cancel)
{ {
Application.Exit(); Application.Exit();
@ -174,7 +165,8 @@ internal partial class MainForm : CustomForm
} }
catch (Exception e) catch (Exception e)
{ {
if (e.HandleException(this)) goto retry; if (e.HandleException(this))
goto retry;
Close(); Close();
} }
} }
@ -188,8 +180,7 @@ internal partial class MainForm : CustomForm
updateButton.Text = "Cancel"; updateButton.Text = "Cancel";
updateButton.Click -= OnUpdate; updateButton.Click -= OnUpdate;
updateButton.Click += OnUpdateCancel; updateButton.Click += OnUpdateCancel;
changelogTreeView.Location changelogTreeView.Location = progressBar.Location with { Y = progressBar.Location.Y + progressBar.Size.Height + 6 };
= progressBar.Location with { Y = progressBar.Location.Y + progressBar.Size.Height + 6 };
Refresh(); Refresh();
Progress<double> progress = new(); Progress<double> progress = new();
progress.ProgressChanged += delegate(object sender, double _progress) progress.ProgressChanged += delegate(object sender, double _progress)
@ -198,7 +189,7 @@ internal partial class MainForm : CustomForm
progressBar.Value = (int)_progress; progressBar.Value = (int)_progress;
}; };
progressLabel.Text = "Updating . . . "; progressLabel.Text = "Updating . . . ";
cancellationTokenSource = new CancellationTokenSource(); cancellationTokenSource = new();
try try
{ {
await updateManager.PrepareUpdateAsync(latestVersion, progress, cancellationTokenSource.Token); await updateManager.PrepareUpdateAsync(latestVersion, progress, cancellationTokenSource.Token);
@ -207,8 +198,7 @@ internal partial class MainForm : CustomForm
catch (TaskCanceledException) { } catch (TaskCanceledException) { }
catch (Exception ex) catch (Exception ex)
{ {
DebugForm.Current.Log($"Exception while preparing update: {ex.GetType()} ({ex.Message})", DebugForm.Current.Log($"Exception while preparing update: {ex.GetType()} ({ex.Message})", LogTextBox.Warning);
LogTextBox.Warning);
} }
#else #else
catch { } catch { }

View file

@ -12,10 +12,11 @@ internal partial class SelectDialogForm : CustomForm
private readonly List<(Platform platform, string id, string name)> selected = new(); private readonly List<(Platform platform, string id, string name)> selected = new();
internal SelectDialogForm(IWin32Window owner) : base(owner) => InitializeComponent(); internal SelectDialogForm(IWin32Window owner) : base(owner) => InitializeComponent();
internal List<(Platform platform, string id, string name)> QueryUser( internal List<(Platform platform, string id, string name)> QueryUser(string groupBoxText,
string groupBoxText, List<(Platform platform, string id, string name, bool alreadySelected)> choices) List<(Platform platform, string id, string name, bool alreadySelected)> choices)
{ {
if (!choices.Any()) return null; if (!choices.Any())
return null;
groupBox.Text = groupBoxText; groupBox.Text = groupBoxText;
allCheckBox.Enabled = false; allCheckBox.Enabled = false;
acceptButton.Enabled = false; acceptButton.Enabled = false;
@ -26,7 +27,8 @@ internal partial class SelectDialogForm : CustomForm
OnTreeNodeChecked(node); OnTreeNodeChecked(node);
_ = selectionTreeView.Nodes.Add(node); _ = selectionTreeView.Nodes.Add(node);
} }
if (!selected.Any()) OnLoad(null, null); if (!selected.Any())
OnLoad(null, null);
allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; allCheckBox.CheckedChanged -= OnAllCheckBoxChanged;
allCheckBox.Checked = selectionTreeView.Nodes.Cast<TreeNode>().All(n => n.Checked); allCheckBox.Checked = selectionTreeView.Nodes.Cast<TreeNode>().All(n => n.Checked);
allCheckBox.CheckedChanged += OnAllCheckBoxChanged; allCheckBox.CheckedChanged += OnAllCheckBoxChanged;
@ -59,13 +61,11 @@ internal partial class SelectDialogForm : CustomForm
allCheckBox.CheckedChanged += OnAllCheckBoxChanged; allCheckBox.CheckedChanged += OnAllCheckBoxChanged;
} }
private void OnResize(object s, EventArgs e) => private void OnResize(object s, EventArgs e)
Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100 => Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100 ? Program.ApplicationNameShort : Program.ApplicationName;
? Program.ApplicationNameShort
: Program.ApplicationName;
private void OnSortCheckBoxChanged(object sender, EventArgs e) => selectionTreeView.TreeViewNodeSorter private void OnSortCheckBoxChanged(object sender, EventArgs e)
= sortCheckBox.Checked ? PlatformIdComparer.NodeText : PlatformIdComparer.NodeName; => selectionTreeView.TreeViewNodeSorter = sortCheckBox.Checked ? PlatformIdComparer.NodeText : PlatformIdComparer.NodeName;
private void OnAllCheckBoxChanged(object sender, EventArgs e) private void OnAllCheckBoxChanged(object sender, EventArgs e)
{ {
@ -85,7 +85,8 @@ internal partial class SelectDialogForm : CustomForm
private void OnLoad(object sender, EventArgs e) private void OnLoad(object sender, EventArgs e)
{ {
List<(Platform platform, string id)> choices = ProgramData.ReadProgramChoices(); List<(Platform platform, string id)> choices = ProgramData.ReadProgramChoices();
if (choices is null) return; if (choices is null)
return;
foreach (TreeNode node in selectionTreeView.Nodes) foreach (TreeNode node in selectionTreeView.Nodes)
{ {
node.Checked = choices.Any(n => n.platform == (Platform)node.Tag && n.id == node.Name); node.Checked = choices.Any(n => n.platform == (Platform)node.Tag && n.id == node.Name);

View file

@ -38,23 +38,23 @@ internal partial class SelectForm : CustomForm
Text = Program.ApplicationName; Text = Program.ApplicationName;
} }
public override ContextMenuStrip ContextMenuStrip => base.ContextMenuStrip ??= new ContextMenuStrip(); public override ContextMenuStrip ContextMenuStrip => base.ContextMenuStrip ??= new();
internal List<TreeNode> TreeNodes => GatherTreeNodes(selectionTreeView.Nodes); internal List<TreeNode> TreeNodes => GatherTreeNodes(selectionTreeView.Nodes);
private static void UpdateRemaining(Label label, SynchronizedCollection<string> list, string descriptor) => private static void UpdateRemaining(Label label, SynchronizedCollection<string> list, string descriptor)
label.Text = list.Any() => label.Text = list.Any() ? $"Remaining {descriptor} ({list.Count}): " + string.Join(", ", list).Replace("&", "&&") : "";
? $"Remaining {descriptor} ({list.Count}): " + string.Join(", ", list).Replace("&", "&&")
: "";
private void UpdateRemainingGames() => UpdateRemaining(progressLabelGames, RemainingGames, "games"); private void UpdateRemainingGames() => UpdateRemaining(progressLabelGames, RemainingGames, "games");
private void AddToRemainingGames(string gameName) private void AddToRemainingGames(string gameName)
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
progressLabelGames.Invoke(delegate progressLabelGames.Invoke(delegate
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
if (!RemainingGames.Contains(gameName)) if (!RemainingGames.Contains(gameName))
RemainingGames.Add(gameName); RemainingGames.Add(gameName);
UpdateRemainingGames(); UpdateRemainingGames();
@ -63,10 +63,12 @@ internal partial class SelectForm : CustomForm
private void RemoveFromRemainingGames(string gameName) private void RemoveFromRemainingGames(string gameName)
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
progressLabelGames.Invoke(delegate progressLabelGames.Invoke(delegate
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
RemainingGames.Remove(gameName); RemainingGames.Remove(gameName);
UpdateRemainingGames(); UpdateRemainingGames();
}); });
@ -76,10 +78,12 @@ internal partial class SelectForm : CustomForm
private void AddToRemainingDLCs(string dlcId) private void AddToRemainingDLCs(string dlcId)
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
progressLabelDLCs.Invoke(delegate progressLabelDLCs.Invoke(delegate
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
if (!RemainingDLCs.Contains(dlcId)) if (!RemainingDLCs.Contains(dlcId))
RemainingDLCs.Add(dlcId); RemainingDLCs.Add(dlcId);
UpdateRemainingDLCs(); UpdateRemainingDLCs();
@ -88,10 +92,12 @@ internal partial class SelectForm : CustomForm
private void RemoveFromRemainingDLCs(string dlcId) private void RemoveFromRemainingDLCs(string dlcId)
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
progressLabelDLCs.Invoke(delegate progressLabelDLCs.Invoke(delegate
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
RemainingDLCs.Remove(dlcId); RemainingDLCs.Remove(dlcId);
UpdateRemainingDLCs(); UpdateRemainingDLCs();
}); });
@ -99,7 +105,8 @@ internal partial class SelectForm : CustomForm
private async Task GetApplicablePrograms(IProgress<int> progress) private async Task GetApplicablePrograms(IProgress<int> progress)
{ {
if (ProgramsToScan is null || !ProgramsToScan.Any()) return; if (ProgramsToScan is null || !ProgramsToScan.Any())
return;
int TotalGameCount = 0; int TotalGameCount = 0;
int CompleteGameCount = 0; int CompleteGameCount = 0;
void AddToRemainingGames(string gameName) void AddToRemainingGames(string gameName)
@ -113,30 +120,30 @@ internal partial class SelectForm : CustomForm
this.RemoveFromRemainingGames(gameName); this.RemoveFromRemainingGames(gameName);
progress.Report(Interlocked.Increment(ref CompleteGameCount)); progress.Report(Interlocked.Increment(ref CompleteGameCount));
} }
if (Program.Canceled) return; if (Program.Canceled)
return;
List<TreeNode> treeNodes = TreeNodes; List<TreeNode> treeNodes = TreeNodes;
RemainingGames.Clear(); // for display purposes only, otherwise ignorable RemainingGames.Clear(); // for display purposes only, otherwise ignorable
RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable
List<Task> appTasks = new(); List<Task> appTasks = new();
if (ProgramsToScan.Any(c => c.platform is Platform.Paradox)) if (ProgramsToScan.Any(c => c.platform is Platform.Paradox))
{ {
List<string> dllDirectories List<string> dllDirectories = await ParadoxLauncher.InstallPath.GetDllDirectoriesFromGameDirectory(Platform.Paradox);
= await ParadoxLauncher.InstallPath.GetDllDirectoriesFromGameDirectory(Platform.Paradox);
if (dllDirectories is not null) if (dllDirectories is not null)
{ {
ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Paradox, "PL"); ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Paradox, "PL");
selection ??= new ProgramSelection(); selection ??= new();
if (allCheckBox.Checked) selection.Enabled = true; if (allCheckBox.Checked)
if (koaloaderAllCheckBox.Checked) selection.Koaloader = true; selection.Enabled = true;
if (koaloaderAllCheckBox.Checked)
selection.Koaloader = true;
selection.Id = "PL"; selection.Id = "PL";
selection.Name = "Paradox Launcher"; selection.Name = "Paradox Launcher";
selection.RootDirectory = ParadoxLauncher.InstallPath; selection.RootDirectory = ParadoxLauncher.InstallPath;
selection.ExecutableDirectories selection.ExecutableDirectories = await ParadoxLauncher.GetExecutableDirectories(selection.RootDirectory);
= await ParadoxLauncher.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories; selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Paradox; selection.Platform = Platform.Paradox;
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Paradox && s.Name == selection.Id) TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Paradox && s.Name == selection.Id) ?? new TreeNode();
?? new TreeNode();
programNode.Tag = selection.Platform; programNode.Tag = selection.Platform;
programNode.Name = selection.Id; programNode.Name = selection.Id;
programNode.Text = selection.Name; programNode.Text = selection.Name;
@ -148,14 +155,13 @@ internal partial class SelectForm : CustomForm
int steamGamesToCheck; int steamGamesToCheck;
if (ProgramsToScan.Any(c => c.platform is Platform.Steam)) if (ProgramsToScan.Any(c => c.platform is Platform.Steam))
{ {
List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = await SteamLibrary.GetGames();
= await SteamLibrary.GetGames();
steamGamesToCheck = steamGames.Count; steamGamesToCheck = steamGames.Count;
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in steamGames) foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in steamGames)
{ {
if (Program.Canceled) return; if (Program.Canceled)
if (Program.IsGameBlocked(name, gameDirectory) return;
|| !ProgramsToScan.Any(c => c.platform is Platform.Steam && c.id == appId)) if (Program.IsGameBlocked(name, gameDirectory) || !ProgramsToScan.Any(c => c.platform is Platform.Steam && c.id == appId))
{ {
Interlocked.Decrement(ref steamGamesToCheck); Interlocked.Decrement(ref steamGamesToCheck);
continue; continue;
@ -163,9 +169,9 @@ internal partial class SelectForm : CustomForm
AddToRemainingGames(name); AddToRemainingGames(name);
Task task = Task.Run(async () => Task task = Task.Run(async () =>
{ {
if (Program.Canceled) return; if (Program.Canceled)
List<string> dllDirectories return;
= await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Steam); List<string> dllDirectories = await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Steam);
if (dllDirectories is null) if (dllDirectories is null)
{ {
Interlocked.Decrement(ref steamGamesToCheck); Interlocked.Decrement(ref steamGamesToCheck);
@ -180,25 +186,30 @@ internal partial class SelectForm : CustomForm
RemoveFromRemainingGames(name); RemoveFromRemainingGames(name);
return; return;
} }
if (Program.Canceled) return; if (Program.Canceled)
return;
ConcurrentDictionary<string, (DlcType type, string name, string icon)> dlc = new(); ConcurrentDictionary<string, (DlcType type, string name, string icon)> dlc = new();
List<Task> dlcTasks = new(); List<Task> dlcTasks = new();
List<string> dlcIds = new(); List<string> dlcIds = new();
if (appData is not null) dlcIds.AddRange(await SteamStore.ParseDlcAppIds(appData)); if (appData is not null)
if (appInfo is not null) dlcIds.AddRange(await SteamCMD.ParseDlcAppIds(appInfo)); dlcIds.AddRange(await SteamStore.ParseDlcAppIds(appData));
if (appInfo is not null)
dlcIds.AddRange(await SteamCMD.ParseDlcAppIds(appInfo));
if (dlcIds.Count > 0) if (dlcIds.Count > 0)
{
foreach (string dlcAppId in dlcIds) foreach (string dlcAppId in dlcIds)
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
AddToRemainingDLCs(dlcAppId); AddToRemainingDLCs(dlcAppId);
Task task = Task.Run(async () => Task task = Task.Run(async () =>
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
do // give games steam store api limit priority do // give games steam store api limit priority
Thread.Sleep(200); Thread.Sleep(200);
while (!Program.Canceled && steamGamesToCheck > 0); while (!Program.Canceled && steamGamesToCheck > 0);
if (Program.Canceled) return; if (Program.Canceled)
return;
string dlcName = null; string dlcName = null;
string dlcIcon = null; string dlcIcon = null;
bool onSteamStore = false; bool onSteamStore = false;
@ -215,18 +226,15 @@ internal partial class SelectForm : CustomForm
if (dlcAppInfo is not null) if (dlcAppInfo is not null)
{ {
dlcName = dlcAppInfo.Value?.GetChild("common")?.GetChild("name")?.ToString(); dlcName = dlcAppInfo.Value?.GetChild("common")?.GetChild("name")?.ToString();
string dlcIconStaticId = dlcAppInfo.Value?.GetChild("common")?.GetChild("icon") string dlcIconStaticId = dlcAppInfo.Value?.GetChild("common")?.GetChild("icon")?.ToString();
?.ToString(); dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo_small")?.ToString();
dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo_small") dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo")?.ToString();
?.ToString();
dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo")
?.ToString();
if (dlcIconStaticId is not null) if (dlcIconStaticId is not null)
dlcIcon = IconGrabber.SteamAppImagesPath dlcIcon = IconGrabber.SteamAppImagesPath + @$"\{dlcAppId}\{dlcIconStaticId}.jpg";
+ @$"\{dlcAppId}\{dlcIconStaticId}.jpg";
} }
} }
if (Program.Canceled) return; if (Program.Canceled)
return;
if (string.IsNullOrWhiteSpace(dlcName)) if (string.IsNullOrWhiteSpace(dlcName))
dlcName = "Unknown"; dlcName = "Unknown";
dlc[dlcAppId] = (onSteamStore ? DlcType.Steam : DlcType.SteamHidden, dlcName, dlcIcon); dlc[dlcAppId] = (onSteamStore ? DlcType.Steam : DlcType.SteamHidden, dlcName, dlcIcon);
@ -234,45 +242,43 @@ internal partial class SelectForm : CustomForm
}); });
dlcTasks.Add(task); dlcTasks.Add(task);
} }
}
else else
{ {
RemoveFromRemainingGames(name); RemoveFromRemainingGames(name);
return; return;
} }
if (Program.Canceled) return; if (Program.Canceled)
return;
foreach (Task task in dlcTasks) foreach (Task task in dlcTasks)
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
await task; await task;
} }
steamGamesToCheck = 0; steamGamesToCheck = 0;
ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Steam, appId) ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Steam, appId) ?? new ProgramSelection();
?? new ProgramSelection(); selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any();
selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() if (koaloaderAllCheckBox.Checked)
|| selection.ExtraSelectedDlc.Any(); selection.Koaloader = true;
if (koaloaderAllCheckBox.Checked) selection.Koaloader = true;
selection.Id = appId; selection.Id = appId;
selection.Name = appData?.name ?? name; selection.Name = appData?.name ?? name;
selection.RootDirectory = gameDirectory; selection.RootDirectory = gameDirectory;
selection.ExecutableDirectories selection.ExecutableDirectories = await SteamLibrary.GetExecutableDirectories(selection.RootDirectory);
= await SteamLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories; selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Steam; selection.Platform = Platform.Steam;
selection.ProductUrl = "https://store.steampowered.com/app/" + appId; selection.ProductUrl = "https://store.steampowered.com/app/" + appId;
selection.IconUrl = IconGrabber.SteamAppImagesPath selection.IconUrl = IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("icon")}.jpg";
+ @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("icon")}.jpg";
selection.SubIconUrl = appData?.header_image ?? IconGrabber.SteamAppImagesPath selection.SubIconUrl = appData?.header_image ?? IconGrabber.SteamAppImagesPath
+ @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("clienticon")}.ico"; + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("clienticon")}.ico";
selection.Publisher = appData?.publishers[0] selection.Publisher = appData?.publishers[0] ?? appInfo?.Value?.GetChild("extended")?.GetChild("publisher")?.ToString();
?? appInfo?.Value?.GetChild("extended")?.GetChild("publisher")?.ToString();
selection.WebsiteUrl = appData?.website; selection.WebsiteUrl = appData?.website;
if (Program.Canceled) return; if (Program.Canceled)
return;
selectionTreeView.Invoke(delegate selectionTreeView.Invoke(delegate
{ {
if (Program.Canceled) return; if (Program.Canceled)
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == appId) return;
?? new TreeNode(); TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == appId) ?? new TreeNode();
programNode.Tag = selection.Platform; programNode.Tag = selection.Platform;
programNode.Name = appId; programNode.Name = appId;
programNode.Text = appData?.name ?? name; programNode.Text = appData?.name ?? name;
@ -281,13 +287,14 @@ internal partial class SelectForm : CustomForm
_ = selectionTreeView.Nodes.Add(programNode); _ = selectionTreeView.Nodes.Add(programNode);
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in dlc) foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in dlc)
{ {
if (Program.Canceled || programNode is null) return; if (Program.Canceled || programNode is null)
return;
string appId = pair.Key; string appId = pair.Key;
(DlcType type, string name, string icon) dlcApp = pair.Value; (DlcType type, string name, string icon) dlcApp = pair.Value;
selection.AllDlc[appId] = dlcApp; selection.AllDlc[appId] = dlcApp;
if (allCheckBox.Checked && dlcApp.name != "Unknown") selection.SelectedDlc[appId] = dlcApp; if (allCheckBox.Checked && dlcApp.name != "Unknown")
TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == appId) selection.SelectedDlc[appId] = dlcApp;
?? new TreeNode(); TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == appId) ?? new TreeNode();
dlcNode.Tag = selection.Platform; dlcNode.Tag = selection.Platform;
dlcNode.Name = appId; dlcNode.Name = appId;
dlcNode.Text = dlcApp.name; dlcNode.Text = dlcApp.name;
@ -296,7 +303,8 @@ internal partial class SelectForm : CustomForm
_ = programNode.Nodes.Add(dlcNode); _ = programNode.Nodes.Add(dlcNode);
} }
}); });
if (Program.Canceled) return; if (Program.Canceled)
return;
RemoveFromRemainingGames(name); RemoveFromRemainingGames(name);
}); });
appTasks.Add(task); appTasks.Add(task);
@ -310,34 +318,37 @@ internal partial class SelectForm : CustomForm
string @namespace = manifest.CatalogNamespace; string @namespace = manifest.CatalogNamespace;
string name = manifest.DisplayName; string name = manifest.DisplayName;
string directory = manifest.InstallLocation; string directory = manifest.InstallLocation;
if (Program.Canceled) return; if (Program.Canceled)
if (Program.IsGameBlocked(name, directory) return;
|| !ProgramsToScan.Any(c => c.platform is Platform.Epic && c.id == @namespace)) continue; if (Program.IsGameBlocked(name, directory) || !ProgramsToScan.Any(c => c.platform is Platform.Epic && c.id == @namespace))
continue;
AddToRemainingGames(name); AddToRemainingGames(name);
Task task = Task.Run(async () => Task task = Task.Run(async () =>
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
List<string> dllDirectories = await directory.GetDllDirectoriesFromGameDirectory(Platform.Epic); List<string> dllDirectories = await directory.GetDllDirectoriesFromGameDirectory(Platform.Epic);
if (dllDirectories is null) if (dllDirectories is null)
{ {
RemoveFromRemainingGames(name); RemoveFromRemainingGames(name);
return; return;
} }
if (Program.Canceled) return; if (Program.Canceled)
ConcurrentDictionary<string, (string name, string product, string icon, string developer)> return;
entitlements = new(); ConcurrentDictionary<string, (string name, string product, string icon, string developer)> entitlements = new();
List<Task> dlcTasks = new(); List<Task> dlcTasks = new();
List<(string id, string name, string product, string icon, string developer)> entitlementIds List<(string id, string name, string product, string icon, string developer)> entitlementIds
= await EpicStore.QueryEntitlements(@namespace); = await EpicStore.QueryEntitlements(@namespace);
if (entitlementIds.Any()) if (entitlementIds.Any())
foreach ((string id, string name, string product, string icon, string developer) in foreach ((string id, string name, string product, string icon, string developer) in entitlementIds)
entitlementIds)
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
AddToRemainingDLCs(id); AddToRemainingDLCs(id);
Task task = Task.Run(() => Task task = Task.Run(() =>
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
entitlements[id] = (name, product, icon, developer); entitlements[id] = (name, product, icon, developer);
RemoveFromRemainingDLCs(id); RemoveFromRemainingDLCs(id);
}); });
@ -348,37 +359,38 @@ internal partial class SelectForm : CustomForm
RemoveFromRemainingGames(name); RemoveFromRemainingGames(name);
return; return;
} }
if (Program.Canceled) return; if (Program.Canceled)
return;
foreach (Task task in dlcTasks) foreach (Task task in dlcTasks)
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
await task; await task;
} }
ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Epic, @namespace) ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Epic, @namespace) ?? new ProgramSelection();
?? new ProgramSelection(); selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any();
selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() if (koaloaderAllCheckBox.Checked)
|| selection.ExtraSelectedDlc.Any(); selection.Koaloader = true;
if (koaloaderAllCheckBox.Checked) selection.Koaloader = true;
selection.Id = @namespace; selection.Id = @namespace;
selection.Name = name; selection.Name = name;
selection.RootDirectory = directory; selection.RootDirectory = directory;
selection.ExecutableDirectories selection.ExecutableDirectories = await EpicLibrary.GetExecutableDirectories(selection.RootDirectory);
= await EpicLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories; selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Epic; selection.Platform = Platform.Epic;
foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> pair in foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> pair in entitlements.Where(p
entitlements.Where(p => p.Value.name == selection.Name)) => p.Value.name == selection.Name))
{ {
selection.ProductUrl = "https://www.epicgames.com/store/product/" + pair.Value.product; selection.ProductUrl = "https://www.epicgames.com/store/product/" + pair.Value.product;
selection.IconUrl = pair.Value.icon; selection.IconUrl = pair.Value.icon;
selection.Publisher = pair.Value.developer; selection.Publisher = pair.Value.developer;
} }
if (Program.Canceled) return; if (Program.Canceled)
return;
selectionTreeView.Invoke(delegate selectionTreeView.Invoke(delegate
{ {
if (Program.Canceled) return; if (Program.Canceled)
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == @namespace) return;
?? new TreeNode(); TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == @namespace) ?? new TreeNode();
programNode.Tag = selection.Platform; programNode.Tag = selection.Platform;
programNode.Name = @namespace; programNode.Name = @namespace;
programNode.Text = name; programNode.Text = name;
@ -400,17 +412,16 @@ internal partial class SelectForm : CustomForm
entitlementsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.Entitlement); entitlementsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.Entitlement);
if (entitlementsNode.Parent is null) if (entitlementsNode.Parent is null)
programNode.Nodes.Add(entitlementsNode);*/ programNode.Nodes.Add(entitlementsNode);*/
foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> pair in entitlements)
pair in entitlements)
{ {
if (programNode is null /* || entitlementsNode is null*/) return; if (programNode is null /* || entitlementsNode is null*/)
return;
string dlcId = pair.Key; string dlcId = pair.Key;
(DlcType type, string name, string icon) dlcApp = ( (DlcType type, string name, string icon) dlcApp = (DlcType.EpicEntitlement, pair.Value.name, pair.Value.icon);
DlcType.EpicEntitlement, pair.Value.name, pair.Value.icon);
selection.AllDlc[dlcId] = dlcApp; selection.AllDlc[dlcId] = dlcApp;
if (allCheckBox.Checked) selection.SelectedDlc[dlcId] = dlcApp; if (allCheckBox.Checked)
TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == dlcId) selection.SelectedDlc[dlcId] = dlcApp;
?? new TreeNode(); TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == dlcId) ?? new TreeNode();
dlcNode.Tag = selection.Platform; dlcNode.Tag = selection.Platform;
dlcNode.Name = dlcId; dlcNode.Name = dlcId;
dlcNode.Text = dlcApp.name; dlcNode.Text = dlcApp.name;
@ -419,7 +430,8 @@ internal partial class SelectForm : CustomForm
_ = programNode.Nodes.Add(dlcNode); //entitlementsNode.Nodes.Add(dlcNode); _ = programNode.Nodes.Add(dlcNode); //entitlementsNode.Nodes.Add(dlcNode);
} }
}); });
if (Program.Canceled) return; if (Program.Canceled)
return;
RemoveFromRemainingGames(name); RemoveFromRemainingGames(name);
}); });
appTasks.Add(task); appTasks.Add(task);
@ -430,39 +442,39 @@ internal partial class SelectForm : CustomForm
List<(string gameId, string name, string gameDirectory)> ubisoftGames = await UbisoftLibrary.GetGames(); List<(string gameId, string name, string gameDirectory)> ubisoftGames = await UbisoftLibrary.GetGames();
foreach ((string gameId, string name, string gameDirectory) in ubisoftGames) foreach ((string gameId, string name, string gameDirectory) in ubisoftGames)
{ {
if (Program.Canceled) return; if (Program.Canceled)
if (Program.IsGameBlocked(name, gameDirectory) return;
|| !ProgramsToScan.Any(c => c.platform is Platform.Ubisoft && c.id == gameId)) continue; if (Program.IsGameBlocked(name, gameDirectory) || !ProgramsToScan.Any(c => c.platform is Platform.Ubisoft && c.id == gameId))
continue;
AddToRemainingGames(name); AddToRemainingGames(name);
Task task = Task.Run(async () => Task task = Task.Run(async () =>
{ {
if (Program.Canceled) return; if (Program.Canceled)
List<string> dllDirectories return;
= await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Ubisoft); List<string> dllDirectories = await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Ubisoft);
if (dllDirectories is null) if (dllDirectories is null)
{ {
RemoveFromRemainingGames(name); RemoveFromRemainingGames(name);
return; return;
} }
if (Program.Canceled) return; if (Program.Canceled)
ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Ubisoft, gameId) return;
?? new ProgramSelection(); ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Ubisoft, gameId) ?? new ProgramSelection();
selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any();
|| selection.ExtraSelectedDlc.Any(); if (koaloaderAllCheckBox.Checked)
if (koaloaderAllCheckBox.Checked) selection.Koaloader = true; selection.Koaloader = true;
selection.Id = gameId; selection.Id = gameId;
selection.Name = name; selection.Name = name;
selection.RootDirectory = gameDirectory; selection.RootDirectory = gameDirectory;
selection.ExecutableDirectories selection.ExecutableDirectories = await UbisoftLibrary.GetExecutableDirectories(selection.RootDirectory);
= await UbisoftLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories; selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Ubisoft; selection.Platform = Platform.Ubisoft;
selection.IconUrl = IconGrabber.GetDomainFaviconUrl("store.ubi.com"); selection.IconUrl = IconGrabber.GetDomainFaviconUrl("store.ubi.com");
selectionTreeView.Invoke(delegate selectionTreeView.Invoke(delegate
{ {
if (Program.Canceled) return; if (Program.Canceled)
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Ubisoft && s.Name == gameId) return;
?? new TreeNode(); TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Ubisoft && s.Name == gameId) ?? new TreeNode();
programNode.Tag = selection.Platform; programNode.Tag = selection.Platform;
programNode.Name = gameId; programNode.Name = gameId;
programNode.Text = name; programNode.Text = name;
@ -470,7 +482,8 @@ internal partial class SelectForm : CustomForm
if (programNode.TreeView is null) if (programNode.TreeView is null)
_ = selectionTreeView.Nodes.Add(programNode); _ = selectionTreeView.Nodes.Add(programNode);
}); });
if (Program.Canceled) return; if (Program.Canceled)
return;
RemoveFromRemainingGames(name); RemoveFromRemainingGames(name);
}); });
appTasks.Add(task); appTasks.Add(task);
@ -478,7 +491,8 @@ internal partial class SelectForm : CustomForm
} }
foreach (Task task in appTasks) foreach (Task task in appTasks)
{ {
if (Program.Canceled) return; if (Program.Canceled)
return;
await task; await task;
} }
steamGamesToCheck = 0; steamGamesToCheck = 0;
@ -512,30 +526,24 @@ internal partial class SelectForm : CustomForm
List<(Platform platform, string id, string name, bool alreadySelected)> gameChoices = new(); List<(Platform platform, string id, string name, bool alreadySelected)> gameChoices = new();
if (Directory.Exists(ParadoxLauncher.InstallPath)) if (Directory.Exists(ParadoxLauncher.InstallPath))
gameChoices.Add((Platform.Paradox, "PL", "Paradox Launcher", gameChoices.Add((Platform.Paradox, "PL", "Paradox Launcher",
ProgramsToScan is not null ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Paradox && p.id == "PL")));
&& ProgramsToScan.Any(p => p.platform is Platform.Paradox && p.id == "PL")));
if (Directory.Exists(SteamLibrary.InstallPath)) if (Directory.Exists(SteamLibrary.InstallPath))
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in (await SteamLibrary.GetGames()).Where(g
(await SteamLibrary.GetGames()).Where(g => !Program.IsGameBlocked(g.name, g.gameDirectory))) => !Program.IsGameBlocked(g.name, g.gameDirectory)))
gameChoices.Add((Platform.Steam, appId, name, gameChoices.Add((Platform.Steam, appId, name,
ProgramsToScan is not null ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Steam && p.id == appId)));
&& ProgramsToScan.Any(p => p.platform is Platform.Steam && p.id == appId)));
if (Directory.Exists(EpicLibrary.EpicManifestsPath)) if (Directory.Exists(EpicLibrary.EpicManifestsPath))
foreach (Manifest manifest in (await EpicLibrary.GetGames()).Where( foreach (Manifest manifest in (await EpicLibrary.GetGames()).Where(m => !Program.IsGameBlocked(m.DisplayName, m.InstallLocation)))
m => !Program.IsGameBlocked(m.DisplayName, m.InstallLocation)))
gameChoices.Add((Platform.Epic, manifest.CatalogNamespace, manifest.DisplayName, gameChoices.Add((Platform.Epic, manifest.CatalogNamespace, manifest.DisplayName,
ProgramsToScan is not null && ProgramsToScan.Any( ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Epic && p.id == manifest.CatalogNamespace)));
p => p.platform is Platform.Epic && p.id == manifest.CatalogNamespace))); foreach ((string gameId, string name, string gameDirectory) in (await UbisoftLibrary.GetGames()).Where(g
foreach ((string gameId, string name, string gameDirectory) in (await UbisoftLibrary.GetGames()).Where( => !Program.IsGameBlocked(g.name, g.gameDirectory)))
g => !Program.IsGameBlocked(g.name, g.gameDirectory)))
gameChoices.Add((Platform.Ubisoft, gameId, name, gameChoices.Add((Platform.Ubisoft, gameId, name,
ProgramsToScan is not null ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Ubisoft && p.id == gameId)));
&& ProgramsToScan.Any(p => p.platform is Platform.Ubisoft && p.id == gameId)));
if (gameChoices.Any()) if (gameChoices.Any())
{ {
using SelectDialogForm form = new(this); using SelectDialogForm form = new(this);
List<(Platform platform, string id, string name)> choices List<(Platform platform, string id, string name)> choices = form.QueryUser("Choose which programs and/or games to scan for DLC:", gameChoices);
= form.QueryUser("Choose which programs and/or games to scan for DLC:", gameChoices);
scan = choices is not null && choices.Any(); scan = choices is not null && choices.Any();
string retry = "\n\nPress the \"Rescan Programs / Games\" button to re-choose."; string retry = "\n\nPress the \"Rescan Programs / Games\" button to re-choose.";
if (scan) if (scan)
@ -544,15 +552,11 @@ internal partial class SelectForm : CustomForm
noneFoundLabel.Text = "None of the chosen programs nor games were applicable!" + retry; noneFoundLabel.Text = "None of the chosen programs nor games were applicable!" + retry;
} }
else else
{
noneFoundLabel.Text = "You didn't choose any programs nor games!" + retry; noneFoundLabel.Text = "You didn't choose any programs nor games!" + retry;
} }
}
else else
{
noneFoundLabel.Text = "No applicable programs nor games were found on your computer!"; noneFoundLabel.Text = "No applicable programs nor games were found on your computer!";
} }
}
if (scan) if (scan)
{ {
bool setup = true; bool setup = true;
@ -562,17 +566,17 @@ internal partial class SelectForm : CustomForm
IProgress<int> iProgress = progress; IProgress<int> iProgress = progress;
progress.ProgressChanged += (sender, _progress) => progress.ProgressChanged += (sender, _progress) =>
{ {
if (Program.Canceled) return; if (Program.Canceled)
if (_progress < 0 || _progress > maxProgress) maxProgress = -_progress; return;
else curProgress = _progress; if (_progress < 0 || _progress > maxProgress)
maxProgress = -_progress;
else
curProgress = _progress;
int p = Math.Max(Math.Min((int)((float)curProgress / maxProgress * 100), 100), 0); int p = Math.Max(Math.Min((int)((float)curProgress / maxProgress * 100), 100), 0);
progressLabel.Text = setup progressLabel.Text = setup ? $"Setting up SteamCMD . . . {p}%" : $"Gathering and caching your applicable games and their DLCs . . . {p}%";
? $"Setting up SteamCMD . . . {p}%"
: $"Gathering and caching your applicable games and their DLCs . . . {p}%";
progressBar.Value = p; progressBar.Value = p;
}; };
if (Directory.Exists(SteamLibrary.InstallPath) && ProgramsToScan is not null if (Directory.Exists(SteamLibrary.InstallPath) && ProgramsToScan is not null && ProgramsToScan.Any(c => c.platform is Platform.Steam))
&& ProgramsToScan.Any(c => c.platform is Platform.Steam))
{ {
progressLabel.Text = "Setting up SteamCMD . . . "; progressLabel.Text = "Setting up SteamCMD . . . ";
await SteamCMD.Setup(iProgress); await SteamCMD.Setup(iProgress);
@ -615,9 +619,11 @@ internal partial class SelectForm : CustomForm
private void OnTreeViewNodeCheckedChanged(object sender, TreeViewEventArgs e) private void OnTreeViewNodeCheckedChanged(object sender, TreeViewEventArgs e)
{ {
if (e.Action == TreeViewAction.Unknown) return; if (e.Action == TreeViewAction.Unknown)
return;
TreeNode node = e.Node; TreeNode node = e.Node;
if (node is null) return; if (node is null)
return;
SyncNode(node); SyncNode(node);
SyncNodeAncestors(node); SyncNodeAncestors(node);
SyncNodeDescendants(node); SyncNodeDescendants(node);
@ -640,10 +646,11 @@ internal partial class SelectForm : CustomForm
} }
} }
private static void SyncNodeDescendants(TreeNode node) => private static void SyncNodeDescendants(TreeNode node)
node.Nodes.Cast<TreeNode>().ToList().ForEach(childNode => => node.Nodes.Cast<TreeNode>().ToList().ForEach(childNode =>
{ {
if (childNode.Text == "Unknown") return; if (childNode.Text == "Unknown")
return;
childNode.Checked = node.Checked; childNode.Checked = node.Checked;
SyncNode(childNode); SyncNode(childNode);
SyncNodeDescendants(childNode); SyncNodeDescendants(childNode);
@ -653,8 +660,7 @@ internal partial class SelectForm : CustomForm
{ {
string id = node.Name; string id = node.Name;
Platform platform = (Platform)node.Tag; Platform platform = (Platform)node.Tag;
(string gameId, (DlcType type, string name, string icon) app)? dlc (string gameId, (DlcType type, string name, string icon) app)? dlc = ProgramSelection.GetDlcFromPlatformId(platform, id);
= ProgramSelection.GetDlcFromPlatformId(platform, id);
if (dlc.HasValue) if (dlc.HasValue)
{ {
(string gameId, _) = dlc.Value; (string gameId, _) = dlc.Value;
@ -692,10 +698,7 @@ internal partial class SelectForm : CustomForm
progressBar.Visible = true; progressBar.Visible = true;
programsGroupBox.Size = programsGroupBox.Size with programsGroupBox.Size = programsGroupBox.Size with
{ {
Height = programsGroupBox.Size.Height - 3 Height = programsGroupBox.Size.Height - 3 - progressLabel.Size.Height - progressLabelGames.Size.Height - progressLabelDLCs.Size.Height
- progressLabel.Size.Height
- progressLabelGames.Size.Height
- progressLabelDLCs.Size.Height
- progressBar.Size.Height - progressBar.Size.Height
}; };
} }
@ -709,15 +712,13 @@ internal partial class SelectForm : CustomForm
progressBar.Visible = false; progressBar.Visible = false;
programsGroupBox.Size = programsGroupBox.Size with programsGroupBox.Size = programsGroupBox.Size with
{ {
Height = programsGroupBox.Size.Height + 3 Height = programsGroupBox.Size.Height + 3 + progressLabel.Size.Height + progressLabelGames.Size.Height + progressLabelDLCs.Size.Height
+ progressLabel.Size.Height
+ progressLabelGames.Size.Height
+ progressLabelDLCs.Size.Height
+ progressBar.Size.Height + progressBar.Size.Height
}; };
} }
internal void OnNodeRightClick(TreeNode node, Point location) => Invoke(() => internal void OnNodeRightClick(TreeNode node, Point location)
=> Invoke(() =>
{ {
ContextMenuStrip contextMenuStrip = ContextMenuStrip; ContextMenuStrip contextMenuStrip = ContextMenuStrip;
while (ContextMenuStrip.Tag is bool used && used) while (ContextMenuStrip.Tag is bool used && used)
@ -738,11 +739,11 @@ internal partial class SelectForm : CustomForm
return; return;
ContextMenuItem header = null; ContextMenuItem header = null;
if (id == "PL") if (id == "PL")
header = new ContextMenuItem(node.Text, "Paradox Launcher"); header = new(node.Text, "Paradox Launcher");
else if (selection is not null) else if (selection is not null)
header = new ContextMenuItem(node.Text, (id, selection.IconUrl)); header = new(node.Text, (id, selection.IconUrl));
else if (dlc is not null && dlcParentSelection is not null) else if (dlc is not null && dlcParentSelection is not null)
header = new ContextMenuItem(node.Text, (id, dlc.Value.app.icon), (id, dlcParentSelection.IconUrl)); header = new(node.Text, (id, dlc.Value.app.icon), (id, dlcParentSelection.IconUrl));
items.Add(header ?? new ContextMenuItem(node.Text)); items.Add(header ?? new ContextMenuItem(node.Text));
string appInfoVDF = $@"{SteamCMD.AppInfoPath}\{id}.vdf"; string appInfoVDF = $@"{SteamCMD.AppInfoPath}\{id}.vdf";
string appInfoJSON = $@"{SteamCMD.AppInfoPath}\{id}.json"; string appInfoJSON = $@"{SteamCMD.AppInfoPath}\{id}.json";
@ -757,19 +758,16 @@ internal partial class SelectForm : CustomForm
: selection.Platform is Platform.Epic : selection.Platform is Platform.Epic
? "Epic GraphQL " ? "Epic GraphQL "
: ""; : "";
queries.Add(new ContextMenuItem($"Open {platformString}Query", "Notepad", queries.Add(new($"Open {platformString}Query", "Notepad", (sender, e) => Diagnostics.OpenFileInNotepad(appInfoJSON)));
(sender, e) => Diagnostics.OpenFileInNotepad(appInfoJSON)));
} }
if (File.Exists(appInfoVDF)) if (File.Exists(appInfoVDF))
queries.Add(new ContextMenuItem("Open SteamCMD Query", "Notepad", queries.Add(new("Open SteamCMD Query", "Notepad", (sender, e) => Diagnostics.OpenFileInNotepad(appInfoVDF)));
(sender, e) => Diagnostics.OpenFileInNotepad(appInfoVDF)));
if (queries.Any()) if (queries.Any())
{ {
items.Add(new ToolStripSeparator()); items.Add(new ToolStripSeparator());
foreach (ContextMenuItem query in queries) foreach (ContextMenuItem query in queries)
items.Add(query); items.Add(query);
items.Add(new ContextMenuItem("Refresh Queries", "Command Prompt", items.Add(new ContextMenuItem("Refresh Queries", "Command Prompt", (sender, e) =>
(sender, e) =>
{ {
try try
{ {
@ -795,109 +793,88 @@ internal partial class SelectForm : CustomForm
if (id == "PL") if (id == "PL")
{ {
items.Add(new ToolStripSeparator()); items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Repair", "Command Prompt", items.Add(new ContextMenuItem("Repair", "Command Prompt", async (sender, e) => await ParadoxLauncher.Repair(this, selection)));
async (sender, e) => await ParadoxLauncher.Repair(this, selection)));
} }
items.Add(new ToolStripSeparator()); items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open Root Directory", "File Explorer", items.Add(new ContextMenuItem("Open Root Directory", "File Explorer",
(sender, e) => Diagnostics.OpenDirectoryInFileExplorer( (sender, e) => Diagnostics.OpenDirectoryInFileExplorer(selection.RootDirectory)));
selection.RootDirectory)));
int executables = 0; int executables = 0;
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories.ToList()) foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories.ToList())
items.Add(new ContextMenuItem( items.Add(new ContextMenuItem($"Open Executable Directory #{++executables} ({(binaryType == BinaryType.BIT32 ? "32" : "64")}-bit)",
$"Open Executable Directory #{++executables} ({(binaryType == BinaryType.BIT32 ? "32" : "64")}-bit)", "File Explorer", (sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)));
"File Explorer",
(sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)));
List<string> directories = selection.DllDirectories.ToList(); List<string> directories = selection.DllDirectories.ToList();
int steam = 0, epic = 0, r1 = 0, r2 = 0; int steam = 0, epic = 0, r1 = 0, r2 = 0;
if (selection.Platform is Platform.Steam or Platform.Paradox) if (selection.Platform is Platform.Steam or Platform.Paradox)
foreach (string directory in directories) foreach (string directory in directories)
{ {
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config,
out string api64_o, out string config, out string cache); out string cache);
if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config)
|| File.Exists(config) || File.Exists(cache)) || File.Exists(cache))
items.Add(new ContextMenuItem($"Open Steamworks Directory #{++steam}", "File Explorer", items.Add(new ContextMenuItem($"Open Steamworks Directory #{++steam}", "File Explorer",
(sender, e) => Diagnostics (sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)));
.OpenDirectoryInFileExplorer(directory)));
} }
if (selection.Platform is Platform.Epic or Platform.Paradox) if (selection.Platform is Platform.Epic or Platform.Paradox)
foreach (string directory in directories) foreach (string directory in directories)
{ {
directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
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))
if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o)
|| File.Exists(config))
items.Add(new ContextMenuItem($"Open EOS Directory #{++epic}", "File Explorer", items.Add(new ContextMenuItem($"Open EOS Directory #{++epic}", "File Explorer",
(sender, e) => Diagnostics (sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)));
.OpenDirectoryInFileExplorer(directory)));
} }
if (selection.Platform is Platform.Ubisoft) if (selection.Platform is Platform.Ubisoft)
foreach (string directory in directories) foreach (string directory in directories)
{ {
directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
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))
if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o)
|| File.Exists(config))
items.Add(new ContextMenuItem($"Open Uplay R1 Directory #{++r1}", "File Explorer", items.Add(new ContextMenuItem($"Open Uplay R1 Directory #{++r1}", "File Explorer",
(sender, e) => Diagnostics (sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)));
.OpenDirectoryInFileExplorer(directory))); directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config);
directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, if (File.Exists(old_api32) || File.Exists(old_api64) || File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64)
out api64, out api64_o, out config); || File.Exists(api64_o) || File.Exists(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))
items.Add(new ContextMenuItem($"Open Uplay R2 Directory #{++r2}", "File Explorer", items.Add(new ContextMenuItem($"Open Uplay R2 Directory #{++r2}", "File Explorer",
(sender, e) => Diagnostics (sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)));
.OpenDirectoryInFileExplorer(directory)));
} }
} }
if (id != "PL") if (id != "PL")
{ {
if ((selection is not null && selection.Platform is Platform.Steam) if (selection is not null && selection.Platform is Platform.Steam
|| (dlcParentSelection is not null && dlcParentSelection.Platform is Platform.Steam)) || dlcParentSelection is not null && dlcParentSelection.Platform is Platform.Steam)
{ {
items.Add(new ToolStripSeparator()); items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open SteamDB", "SteamDB", items.Add(new ContextMenuItem("Open SteamDB", "SteamDB",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser( (sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamdb.info/app/" + id)));
"https://steamdb.info/app/" + id)));
} }
if (selection is not null) if (selection is not null)
{ {
if (selection.Platform is Platform.Steam) if (selection.Platform is Platform.Steam)
{ {
items.Add(new ContextMenuItem("Open Steam Store", "Steam Store", items.Add(new ContextMenuItem("Open Steam Store", "Steam Store",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser( (sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl)));
selection.ProductUrl))); items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIconUrl), "Steam Community",
items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIconUrl), (sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + id)));
"Steam Community",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(
"https://steamcommunity.com/app/" + id)));
} }
if (selection.Platform is Platform.Epic) if (selection.Platform is Platform.Epic)
{ {
items.Add(new ToolStripSeparator()); items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open ScreamDB", "ScreamDB", items.Add(new ContextMenuItem("Open ScreamDB", "ScreamDB",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser( (sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://scream-db.web.app/offers/" + id)));
"https://scream-db.web.app/offers/" + id)));
items.Add(new ContextMenuItem("Open Epic Games Store", "Epic Games", items.Add(new ContextMenuItem("Open Epic Games Store", "Epic Games",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser( (sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl)));
selection.ProductUrl)));
} }
if (selection.Platform is Platform.Ubisoft) if (selection.Platform is Platform.Ubisoft)
{ {
items.Add(new ToolStripSeparator()); items.Add(new ToolStripSeparator());
#pragma warning disable CA1308 // Normalize strings to uppercase #pragma warning disable CA1308 // Normalize strings to uppercase
items.Add(new ContextMenuItem("Open Ubisoft Store", "Ubisoft Store", items.Add(new ContextMenuItem("Open Ubisoft Store", "Ubisoft Store",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser( (sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://store.ubi.com/us/"
"https://store.ubi.com/us/" + selection.Name.Replace(" ", "-") + selection.Name.Replace(" ", "-").ToLowerInvariant())));
.ToLowerInvariant())));
#pragma warning restore CA1308 // Normalize strings to uppercase #pragma warning restore CA1308 // Normalize strings to uppercase
} }
} }
} }
if (selection is not null && selection.WebsiteUrl is not null) if (selection is not null && selection.WebsiteUrl is not null)
items.Add(new ContextMenuItem("Open Official Website", items.Add(new ContextMenuItem("Open Official Website", ("Web_" + id, IconGrabber.GetDomainFaviconUrl(selection.WebsiteUrl)),
("Web_" + id, IconGrabber.GetDomainFaviconUrl(selection.WebsiteUrl)),
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.WebsiteUrl))); (sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.WebsiteUrl)));
contextMenuStrip.Show(selectionTreeView, location); contextMenuStrip.Show(selectionTreeView, location);
contextMenuStrip.Refresh(); contextMenuStrip.Refresh();
@ -915,7 +892,8 @@ internal partial class SelectForm : CustomForm
} }
catch (Exception e) catch (Exception e)
{ {
if (e.HandleException(this)) goto retry; if (e.HandleException(this))
goto retry;
Close(); Close();
} }
} }
@ -927,7 +905,8 @@ internal partial class SelectForm : CustomForm
foreach (ProgramSelection selection in ProgramSelection.AllEnabled) foreach (ProgramSelection selection in ProgramSelection.AllEnabled)
if (!Program.IsProgramRunningDialog(this, selection)) if (!Program.IsProgramRunningDialog(this, selection))
return; return;
if (!uninstall && ParadoxLauncher.DlcDialog(this)) return; if (!uninstall && ParadoxLauncher.DlcDialog(this))
return;
Hide(); Hide();
#pragma warning disable CA2000 // Dispose objects before losing scope #pragma warning disable CA2000 // Dispose objects before losing scope
InstallForm form = new(uninstall); InstallForm form = new(uninstall);
@ -945,9 +924,7 @@ internal partial class SelectForm : CustomForm
OnLoad(); OnLoad();
} }
else else
{
Close(); Close();
}
}; };
form.Show(); form.Show();
Hide(); Hide();
@ -982,7 +959,7 @@ internal partial class SelectForm : CustomForm
if (node.Parent is null && node.Checked != shouldCheck) if (node.Parent is null && node.Checked != shouldCheck)
{ {
node.Checked = shouldCheck; node.Checked = shouldCheck;
OnTreeViewNodeCheckedChanged(null, new TreeViewEventArgs(node, TreeViewAction.ByMouse)); OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
} }
allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; allCheckBox.CheckedChanged -= OnAllCheckBoxChanged;
allCheckBox.Checked = shouldCheck; allCheckBox.Checked = shouldCheck;
@ -1009,20 +986,17 @@ internal partial class SelectForm : CustomForm
private bool AreSelectionsDefault() private bool AreSelectionsDefault()
{ {
foreach (TreeNode node in TreeNodes) foreach (TreeNode node in TreeNodes)
if (node.Parent is not null && node.Tag is Platform if (node.Parent is not null && node.Tag is Platform && (node.Text == "Unknown" ? node.Checked : !node.Checked))
&& (node.Text == "Unknown" ? node.Checked : !node.Checked))
return false; return false;
return true; return true;
} }
private bool CanSaveDlc() private bool CanSaveDlc() => installButton.Enabled && (ProgramData.ReadDlcChoices() is not null || !AreSelectionsDefault());
=> installButton.Enabled && (ProgramData.ReadDlcChoices() is not null || !AreSelectionsDefault());
private void OnSaveDlc(object sender, EventArgs e) private void OnSaveDlc(object sender, EventArgs e)
{ {
List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices() List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices()
?? new List<(Platform platform, string gameId, ?? new List<(Platform platform, string gameId, string dlcId)>();
string dlcId)>();
foreach (TreeNode node in TreeNodes) foreach (TreeNode node in TreeNodes)
if (node.Parent is TreeNode parent && node.Tag is Platform platform) if (node.Parent is TreeNode parent && node.Tag is Platform platform)
{ {
@ -1042,15 +1016,15 @@ internal partial class SelectForm : CustomForm
private void OnLoadDlc(object sender, EventArgs e) private void OnLoadDlc(object sender, EventArgs e)
{ {
List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices(); List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices();
if (choices is null) return; if (choices is null)
return;
foreach (TreeNode node in TreeNodes) foreach (TreeNode node in TreeNodes)
if (node.Parent is TreeNode parent && node.Tag is Platform platform) if (node.Parent is TreeNode parent && node.Tag is Platform platform)
{ {
node.Checked node.Checked = choices.Any(c => c.platform == platform && c.gameId == parent.Name && c.dlcId == node.Name)
= choices.Any(c => c.platform == platform && c.gameId == parent.Name && c.dlcId == node.Name)
? node.Text == "Unknown" ? node.Text == "Unknown"
: node.Text != "Unknown"; : node.Text != "Unknown";
OnTreeViewNodeCheckedChanged(null, new TreeViewEventArgs(node, TreeViewAction.ByMouse)); OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
} }
} }
@ -1062,7 +1036,7 @@ internal partial class SelectForm : CustomForm
if (node.Parent is not null && node.Tag is Platform) if (node.Parent is not null && node.Tag is Platform)
{ {
node.Checked = node.Text != "Unknown"; node.Checked = node.Text != "Unknown";
OnTreeViewNodeCheckedChanged(null, new TreeViewEventArgs(node, TreeViewAction.ByMouse)); OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
} }
resetButton.Enabled = CanResetDlc(); resetButton.Enabled = CanResetDlc();
} }
@ -1075,8 +1049,7 @@ internal partial class SelectForm : CustomForm
return true; return true;
} }
private static bool CanSaveKoaloader() private static bool CanSaveKoaloader() => ProgramData.ReadKoaloaderChoices() is not null || !AreKoaloaderSelectionsDefault();
=> ProgramData.ReadKoaloaderChoices() is not null || !AreKoaloaderSelectionsDefault();
private void OnSaveKoaloader(object sender, EventArgs e) private void OnSaveKoaloader(object sender, EventArgs e)
{ {
@ -1085,12 +1058,9 @@ internal partial class SelectForm : CustomForm
foreach (ProgramSelection selection in ProgramSelection.AllSafe) foreach (ProgramSelection selection in ProgramSelection.AllSafe)
{ {
_ = choices.RemoveAll(c => c.platform == selection.Platform && c.id == selection.Id); _ = choices.RemoveAll(c => c.platform == selection.Platform && c.id == selection.Id);
if (selection.KoaloaderProxy is not null and not ProgramSelection.DefaultKoaloaderProxy if (selection.KoaloaderProxy is not null and not ProgramSelection.DefaultKoaloaderProxy || !selection.Koaloader)
|| !selection.Koaloader)
choices.Add((selection.Platform, selection.Id, choices.Add((selection.Platform, selection.Id,
selection.KoaloaderProxy == ProgramSelection.DefaultKoaloaderProxy selection.KoaloaderProxy == ProgramSelection.DefaultKoaloaderProxy ? null : selection.KoaloaderProxy, selection.Koaloader));
? null
: selection.KoaloaderProxy, selection.Koaloader));
} }
ProgramData.WriteKoaloaderProxyChoices(choices); ProgramData.WriteKoaloaderProxyChoices(choices);
saveKoaloaderButton.Enabled = CanSaveKoaloader(); saveKoaloaderButton.Enabled = CanSaveKoaloader();
@ -1102,12 +1072,13 @@ internal partial class SelectForm : CustomForm
private void OnLoadKoaloader(object sender, EventArgs e) private void OnLoadKoaloader(object sender, EventArgs e)
{ {
List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices(); List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices();
if (choices is null) return; if (choices is null)
return;
foreach (ProgramSelection selection in ProgramSelection.AllSafe) foreach (ProgramSelection selection in ProgramSelection.AllSafe)
if (choices.Any(c => c.platform == selection.Platform && c.id == selection.Id)) if (choices.Any(c => c.platform == selection.Platform && c.id == selection.Id))
{ {
(Platform platform, string id, string proxy, bool enabled) choice = (Platform platform, string id, string proxy, bool enabled)
choices.First(c => c.platform == selection.Platform && c.id == selection.Id); choice = choices.First(c => c.platform == selection.Platform && c.id == selection.Id);
(Platform platform, string id, string proxy, bool enabled) = choice; (Platform platform, string id, string proxy, bool enabled) = choice;
string currentProxy = proxy; string currentProxy = proxy;
if (proxy is not null && proxy.Contains('.')) // convert pre-v4.1.0.0 choices if (proxy is not null && proxy.Contains('.')) // convert pre-v4.1.0.0 choices
@ -1115,14 +1086,11 @@ internal partial class SelectForm : CustomForm
if (proxy != currentProxy && choices.Remove(choice)) // convert pre-v4.1.0.0 choices if (proxy != currentProxy && choices.Remove(choice)) // convert pre-v4.1.0.0 choices
choices.Add((platform, id, currentProxy, enabled)); choices.Add((platform, id, currentProxy, enabled));
if (currentProxy is null or ProgramSelection.DefaultKoaloaderProxy && enabled) if (currentProxy is null or ProgramSelection.DefaultKoaloaderProxy && enabled)
{
_ = choices.RemoveAll(c => c.platform == platform && c.id == id); _ = choices.RemoveAll(c => c.platform == platform && c.id == id);
}
else else
{ {
selection.Koaloader = enabled; selection.Koaloader = enabled;
selection.KoaloaderProxy selection.KoaloaderProxy = currentProxy == ProgramSelection.DefaultKoaloaderProxy ? currentProxy : proxy;
= currentProxy == ProgramSelection.DefaultKoaloaderProxy ? currentProxy : proxy;
} }
} }
else else
@ -1176,22 +1144,16 @@ internal partial class SelectForm : CustomForm
_ = blockedDirectoryExceptions.Append(helpButtonListPrefix + name); _ = blockedDirectoryExceptions.Append(helpButtonListPrefix + name);
using DialogForm form = new(this); using DialogForm form = new(this);
_ = form.Show(SystemIcons.Information, _ = form.Show(SystemIcons.Information,
"Blocks the program from caching and displaying games protected by anti-cheats." + "Blocks the program from caching and displaying games protected by anti-cheats."
"\nYou disable this option and install DLC unlockers to protected games at your own risk!" + + "\nYou disable this option and install DLC unlockers to protected games at your own risk!" + "\n\nBlocked games: "
"\n\nBlocked games: " + (string.IsNullOrWhiteSpace(blockedGames.ToString()) ? "(none)" : blockedGames) + "\n\nBlocked game sub-directories: "
+ (string.IsNullOrWhiteSpace(blockedGames.ToString()) ? "(none)" : blockedGames) + + (string.IsNullOrWhiteSpace(blockedDirectories.ToString()) ? "(none)" : blockedDirectories) + "\n\nBlocked game sub-directory exceptions: "
"\n\nBlocked game sub-directories: " + (string.IsNullOrWhiteSpace(blockedDirectories.ToString()) + (string.IsNullOrWhiteSpace(blockedDirectoryExceptions.ToString()) ? "(none)" : blockedDirectoryExceptions), "OK",
? "(none)" customFormText: "Block Protected Games");
: blockedDirectories) +
"\n\nBlocked game sub-directory exceptions: "
+ (string.IsNullOrWhiteSpace(blockedDirectoryExceptions.ToString())
? "(none)"
: blockedDirectoryExceptions),
"OK", customFormText: "Block Protected Games");
} }
private void OnSortCheckBoxChanged(object sender, EventArgs e) => selectionTreeView.TreeViewNodeSorter private void OnSortCheckBoxChanged(object sender, EventArgs e)
= sortCheckBox.Checked ? PlatformIdComparer.NodeText : PlatformIdComparer.NodeName; => selectionTreeView.TreeViewNodeSorter = sortCheckBox.Checked ? PlatformIdComparer.NodeText : PlatformIdComparer.NodeName;
} }
#pragma warning restore IDE0058 #pragma warning restore IDE0058

View file

@ -17,43 +17,36 @@ internal static class EpicLibrary
{ {
get get
{ {
epicManifestsPath epicManifestsPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Epic Games\EOS", "ModSdkMetadataDir", null) as string;
??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Epic Games\EOS", "ModSdkMetadataDir", epicManifestsPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Wow6432Node\Epic Games\EOS", "ModSdkMetadataDir", null) as string;
null) as string; epicManifestsPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Epic Games\EpicGamesLauncher", "AppDataPath", null) as string;
epicManifestsPath epicManifestsPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Epic Games\EpicGamesLauncher", "AppDataPath", null) as string;
??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Wow6432Node\Epic Games\EOS", "ModSdkMetadataDir",
null) as string;
epicManifestsPath
??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Epic Games\EpicGamesLauncher", "AppDataPath",
null) as string;
epicManifestsPath
??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Epic Games\EpicGamesLauncher",
"AppDataPath", null) as string;
if (epicManifestsPath is not null && epicManifestsPath.EndsWith(@"\Data")) if (epicManifestsPath is not null && epicManifestsPath.EndsWith(@"\Data"))
epicManifestsPath += @"\Manifests"; epicManifestsPath += @"\Manifests";
return epicManifestsPath.BeautifyPath(); return epicManifestsPath.BeautifyPath();
} }
} }
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories( internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory)
string gameDirectory) => => await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
internal static async Task<List<Manifest>> GetGames() => await Task.Run(() => internal static async Task<List<Manifest>> GetGames()
=> await Task.Run(() =>
{ {
List<Manifest> games = new(); List<Manifest> games = new();
string manifests = EpicManifestsPath; string manifests = EpicManifestsPath;
if (!Directory.Exists(manifests)) return games; if (!Directory.Exists(manifests))
return games;
foreach (string file in Directory.EnumerateFiles(manifests, "*.item")) foreach (string file in Directory.EnumerateFiles(manifests, "*.item"))
{ {
if (Program.Canceled) return games; if (Program.Canceled)
return games;
string json = File.ReadAllText(file); string json = File.ReadAllText(file);
try try
{ {
Manifest manifest = JsonSerializer.Deserialize<Manifest>(json); Manifest manifest = JsonSerializer.Deserialize<Manifest>(json);
if (manifest is not null && manifest.CatalogItemId == manifest.MainGameCatalogItemId if (manifest is not null && manifest.CatalogItemId == manifest.MainGameCatalogItemId && !games.Any(g
&& !games.Any(g => g.CatalogItemId == manifest.CatalogItemId => g.CatalogItemId == manifest.CatalogItemId && g.InstallLocation == manifest.InstallLocation))
&& g.InstallLocation == manifest.InstallLocation))
games.Add(manifest); games.Add(manifest);
} }
catch { } catch { }

View file

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web; using System.Web;
using CreamInstaller.Platforms.Epic.GraphQL; using CreamInstaller.Platforms.Epic.GraphQL;
@ -23,8 +22,7 @@ internal static class EpicStore
private const int COOLDOWN_ENTITLEMENT = 600; private const int COOLDOWN_ENTITLEMENT = 600;
internal static async Task<List<(string id, string name, string product, string icon, string developer)>> internal static async Task<List<(string id, string name, string product, string icon, string developer)>> QueryEntitlements(string categoryNamespace)
QueryEntitlements(string categoryNamespace)
{ {
List<(string id, string name, string product, string icon, string developer)> dlcIds = new(); List<(string id, string name, string product, string icon, string developer)> dlcIds = new();
string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json"; string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json";
@ -40,7 +38,6 @@ internal static class EpicStore
catch { } catch { }
} }
else if (cachedExists) else if (cachedExists)
{
try try
{ {
response = JsonConvert.DeserializeObject<Response>(await File.ReadAllTextAsync(cacheFile)); response = JsonConvert.DeserializeObject<Response>(await File.ReadAllTextAsync(cacheFile));
@ -49,16 +46,13 @@ internal static class EpicStore
{ {
File.Delete(cacheFile); File.Delete(cacheFile);
} }
}
if (response is null) if (response is null)
return dlcIds; return dlcIds;
List<Element> searchStore = new(response.Data.Catalog.SearchStore.Elements); List<Element> searchStore = new(response.Data.Catalog.SearchStore.Elements);
foreach (Element element in searchStore) foreach (Element element in searchStore)
{ {
string title = element.Title; string title = element.Title;
string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Any() string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Any() ? element.CatalogNs.Mappings.First().PageSlug : null;
? element.CatalogNs.Mappings.First().PageSlug
: null;
string icon = null; string icon = null;
for (int i = 0; i < element.KeyImages?.Length; i++) for (int i = 0; i < element.KeyImages?.Length; i++)
{ {
@ -76,9 +70,7 @@ internal static class EpicStore
foreach (Element element in catalogOffers) foreach (Element element in catalogOffers)
{ {
string title = element.Title; string title = element.Title;
string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Any() string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Any() ? element.CatalogNs.Mappings.First().PageSlug : null;
? element.CatalogNs.Mappings.First().PageSlug
: null;
string icon = null; string icon = null;
for (int i = 0; i < element.KeyImages?.Length; i++) for (int i = 0; i < element.KeyImages?.Length; i++)
{ {
@ -95,11 +87,11 @@ internal static class EpicStore
return dlcIds; return dlcIds;
} }
private static void Populate( private static void Populate(this List<(string id, string name, string product, string icon, string developer)> dlcIds, string id, string title,
this List<(string id, string name, string product, string icon, string developer)> dlcIds, string id, string product, string icon, string developer, bool canOverwrite = false)
string title, string product, string icon, string developer, bool canOverwrite = false)
{ {
if (id == null) return; if (id == null)
return;
bool found = false; bool found = false;
for (int i = 0; i < dlcIds.Count; i++) for (int i = 0; i < dlcIds.Count; i++)
{ {
@ -113,7 +105,8 @@ internal static class EpicStore
break; break;
} }
} }
if (!found) dlcIds.Add((id, title, product, icon, developer)); if (!found)
dlcIds.Add((id, title, product, icon, developer));
} }
private static async Task<Response> QueryGraphQL(string categoryNamespace) private static async Task<Response> QueryGraphQL(string categoryNamespace)
@ -124,11 +117,11 @@ internal static class EpicStore
Request request = new(encoded); Request request = new(encoded);
string payload = JsonConvert.SerializeObject(request); string payload = JsonConvert.SerializeObject(request);
using HttpContent content = new StringContent(payload); using HttpContent content = new StringContent(payload);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); content.Headers.ContentType = new("application/json");
HttpClient client = HttpClientManager.HttpClient; HttpClient client = HttpClientManager.HttpClient;
if (client is null) return null; if (client is null)
HttpResponseMessage httpResponse return null;
= await client.PostAsync(new Uri("https://graphql.epicgames.com/graphql"), content); HttpResponseMessage httpResponse = await client.PostAsync(new Uri("https://graphql.epicgames.com/graphql"), content);
_ = httpResponse.EnsureSuccessStatusCode(); _ = httpResponse.EnsureSuccessStatusCode();
string response = await httpResponse.Content.ReadAsStringAsync(); string response = await httpResponse.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Response>(response); return JsonConvert.DeserializeObject<Response>(response);

View file

@ -9,10 +9,11 @@ namespace CreamInstaller.Platforms.Epic.GraphQL;
internal class Request internal class Request
{ {
internal Request(string @namespace) => Vars = new Variables(@namespace); internal Request(string @namespace) => Vars = new(@namespace);
[JsonProperty(PropertyName = "query")] [JsonProperty(PropertyName = "query")]
private string Query => @"query searchOffers($namespace: String!) { private string Query
=> @"query searchOffers($namespace: String!) {
Catalog { Catalog {
searchStore(category: ""*"", namespace: $namespace){ searchStore(category: ""*"", namespace: $namespace){
elements { elements {

View file

@ -27,20 +27,16 @@ internal static class ParadoxLauncher
{ {
get get
{ {
installPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Paradox Interactive\Paradox Launcher v2", installPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Paradox Interactive\Paradox Launcher v2", "LauncherInstallation", null) as string;
"LauncherInstallation", null) as string; installPath
installPath ??= Registry.GetValue( ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Wow6432Node\Paradox Interactive\Paradox Launcher v2", "LauncherInstallation",
@"HKEY_CURRENT_USER\Software\Wow6432Node\Paradox Interactive\Paradox Launcher v2", null) as string;
"LauncherInstallation", null) as string;
return installPath.BeautifyPath(); return installPath.BeautifyPath();
} }
} }
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories( internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory)
string gameDirectory) => => await Task.Run(async () => await gameDirectory.GetExecutableDirectories(validFunc: path => !Path.GetFileName(path).Contains("bootstrapper")));
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(validFunc: path
=> !Path.GetFileName(path)
.Contains("bootstrapper")));
private static void PopulateDlc(ProgramSelection paradoxLauncher = null) private static void PopulateDlc(ProgramSelection paradoxLauncher = null)
{ {
@ -49,26 +45,24 @@ internal static class ParadoxLauncher
{ {
paradoxLauncher.ExtraDlc.Clear(); paradoxLauncher.ExtraDlc.Clear();
paradoxLauncher.ExtraSelectedDlc.Clear(); paradoxLauncher.ExtraSelectedDlc.Clear();
foreach (ProgramSelection selection in ProgramSelection.AllEnabled.Where( foreach (ProgramSelection selection in ProgramSelection.AllEnabled.Where(s => s != paradoxLauncher && s.Publisher == "Paradox Interactive"))
s => s != paradoxLauncher && s.Publisher == "Paradox Interactive"))
{ {
paradoxLauncher.ExtraDlc.Add( paradoxLauncher.ExtraDlc.Add(
new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>( new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>(selection.Id, selection.Name,
selection.Id, selection.Name, selection.AllDlc)); selection.AllDlc));
paradoxLauncher.ExtraSelectedDlc.Add( paradoxLauncher.ExtraSelectedDlc.Add(
new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>( new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>(selection.Id, selection.Name,
selection.Id, selection.Name, selection.SelectedDlc)); selection.SelectedDlc));
} }
if (!paradoxLauncher.ExtraDlc.Any()) if (!paradoxLauncher.ExtraDlc.Any())
foreach (ProgramSelection selection in ProgramSelection.AllSafe.Where( foreach (ProgramSelection selection in ProgramSelection.AllSafe.Where(s => s != paradoxLauncher && s.Publisher == "Paradox Interactive"))
s => s != paradoxLauncher && s.Publisher == "Paradox Interactive"))
{ {
paradoxLauncher.ExtraDlc.Add( paradoxLauncher.ExtraDlc.Add(
new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>( new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>(selection.Id, selection.Name,
selection.Id, selection.Name, selection.AllDlc)); selection.AllDlc));
paradoxLauncher.ExtraSelectedDlc.Add( paradoxLauncher.ExtraSelectedDlc.Add(
new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>( new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>(selection.Id, selection.Name,
selection.Id, selection.Name, selection.AllDlc)); selection.AllDlc));
} }
} }
} }
@ -84,9 +78,8 @@ internal static class ParadoxLauncher
using DialogForm dialogForm = new(form); using DialogForm dialogForm = new(form);
return dialogForm.Show(SystemIcons.Warning, return dialogForm.Show(SystemIcons.Warning,
"WARNING: There are no scanned games with DLC that can be added to the Paradox Launcher!" "WARNING: There are no scanned games with DLC that can be added to the Paradox Launcher!"
+ + "\n\nInstalling DLC unlockers for the Paradox Launcher alone can cause existing configurations to be deleted!", "Ignore", "Cancel",
"\n\nInstalling DLC unlockers for the Paradox Launcher alone can cause existing configurations to be deleted!", "Paradox Launcher") != DialogResult.OK;
"Ignore", "Cancel", "Paradox Launcher") != DialogResult.OK;
} }
} }
return false; return false;
@ -96,9 +89,7 @@ internal static class ParadoxLauncher
{ {
InstallForm installForm = form as InstallForm; InstallForm installForm = form as InstallForm;
if (!Program.IsProgramRunningDialog(form, selection)) if (!Program.IsProgramRunningDialog(form, selection))
return form is InstallForm return form is InstallForm ? throw new CustomMessageException("Repair failed! The launcher is currently running!") : RepairResult.ProgramRunning;
? throw new CustomMessageException("Repair failed! The launcher is currently running!")
: RepairResult.ProgramRunning;
bool smokeInstalled = false; bool smokeInstalled = false;
byte[] steamOriginalSdk32 = null; byte[] steamOriginalSdk32 = null;
byte[] steamOriginalSdk64 = null; byte[] steamOriginalSdk64 = null;
@ -107,40 +98,29 @@ internal static class ParadoxLauncher
byte[] epicOriginalSdk64 = null; byte[] epicOriginalSdk64 = null;
foreach (string directory in selection.DllDirectories) foreach (string directory in selection.DllDirectories)
{ {
bool koaloaderInstalled = Koaloader.AutoLoadDlls bool koaloaderInstalled = Koaloader.AutoLoadDlls.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile()); .Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile());
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out _);
out string config, out _); smokeInstalled = smokeInstalled || File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled
smokeInstalled = smokeInstalled || File.Exists(api32) && api32.IsResourceFile(ResourceIdentifier.Steamworks32)
|| File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(api64) && api64.IsResourceFile(ResourceIdentifier.Steamworks64);
|| (File.Exists(config) && !koaloaderInstalled)
|| (File.Exists(api32) && api32.IsResourceFile(ResourceIdentifier.Steamworks32))
|| (File.Exists(api64) && api64.IsResourceFile(ResourceIdentifier.Steamworks64));
await SmokeAPI.Uninstall(directory, deleteConfig: false); await SmokeAPI.Uninstall(directory, deleteConfig: false);
if (steamOriginalSdk32 is null && File.Exists(api32) if (steamOriginalSdk32 is null && File.Exists(api32) && !api32.IsResourceFile(ResourceIdentifier.Steamworks32))
&& !api32.IsResourceFile(ResourceIdentifier.Steamworks32))
steamOriginalSdk32 = await File.ReadAllBytesAsync(api32); steamOriginalSdk32 = await File.ReadAllBytesAsync(api32);
if (steamOriginalSdk64 is null && File.Exists(api64) if (steamOriginalSdk64 is null && File.Exists(api64) && !api64.IsResourceFile(ResourceIdentifier.Steamworks64))
&& !api64.IsResourceFile(ResourceIdentifier.Steamworks64))
steamOriginalSdk64 = await File.ReadAllBytesAsync(api64); steamOriginalSdk64 = await File.ReadAllBytesAsync(api64);
directory.GetScreamApiComponents(out api32, out api32_o, out api64, out api64_o, out config); directory.GetScreamApiComponents(out api32, out api32_o, out api64, out api64_o, out config);
screamInstalled = screamInstalled screamInstalled = screamInstalled || File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled
|| File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(api32) && api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32)
|| (File.Exists(config) && !koaloaderInstalled) || File.Exists(api64) && api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64);
|| (File.Exists(api32) && api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32))
|| (File.Exists(api64) && api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64));
await ScreamAPI.Uninstall(directory, deleteConfig: false); await ScreamAPI.Uninstall(directory, deleteConfig: false);
if (epicOriginalSdk32 is null && File.Exists(api32) if (epicOriginalSdk32 is null && File.Exists(api32) && !api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32))
&& !api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32))
epicOriginalSdk32 = await File.ReadAllBytesAsync(api32); epicOriginalSdk32 = await File.ReadAllBytesAsync(api32);
if (epicOriginalSdk64 is null && File.Exists(api64) if (epicOriginalSdk64 is null && File.Exists(api64) && !api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64))
&& !api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64))
epicOriginalSdk64 = await File.ReadAllBytesAsync(api64); epicOriginalSdk64 = await File.ReadAllBytesAsync(api64);
} }
using DialogForm dialogForm = new(form); using DialogForm dialogForm = new(form);
if (steamOriginalSdk32 is not null || steamOriginalSdk64 is not null || epicOriginalSdk32 is not null if (steamOriginalSdk32 is not null || steamOriginalSdk64 is not null || epicOriginalSdk32 is not null || epicOriginalSdk64 is not null)
|| epicOriginalSdk64 is not null)
{ {
bool neededRepair = false; bool neededRepair = false;
foreach (string directory in selection.DllDirectories) foreach (string directory in selection.DllDirectories)
@ -181,26 +161,22 @@ internal static class ParadoxLauncher
if (installForm is not null) if (installForm is not null)
installForm.UpdateUser("Paradox Launcher successfully repaired!", LogTextBox.Success); installForm.UpdateUser("Paradox Launcher successfully repaired!", LogTextBox.Success);
else else
_ = dialogForm.Show(form.Icon, "Paradox Launcher successfully repaired!", "OK", _ = dialogForm.Show(form.Icon, "Paradox Launcher successfully repaired!", "OK", customFormText: "Paradox Launcher");
customFormText: "Paradox Launcher");
return RepairResult.Success; return RepairResult.Success;
} }
if (installForm is not null) if (installForm is not null)
installForm.UpdateUser("Paradox Launcher did not need to be repaired.", LogTextBox.Success); installForm.UpdateUser("Paradox Launcher did not need to be repaired.", LogTextBox.Success);
else else
_ = dialogForm.Show(SystemIcons.Information, "Paradox Launcher does not need to be repaired.", "OK", _ = dialogForm.Show(SystemIcons.Information, "Paradox Launcher does not need to be repaired.", "OK", customFormText: "Paradox Launcher");
customFormText: "Paradox Launcher");
return RepairResult.Unnecessary; return RepairResult.Unnecessary;
} }
_ = form is InstallForm _ = form is InstallForm
? throw new CustomMessageException("Repair failed! " + ? throw new CustomMessageException("Repair failed! " + "An original Steamworks and/or Epic Online Services file could not be found. "
"An original Steamworks and/or Epic Online Services file could not be found. " + "You will likely have to reinstall Paradox Launcher to fix this issue.")
+ : dialogForm.Show(SystemIcons.Error,
"You will likely have to reinstall Paradox Launcher to fix this issue.") "Paradox Launcher repair failed!" + "\n\nAn original Steamworks and/or Epic Online Services file could not be found."
: dialogForm.Show(SystemIcons.Error, "Paradox Launcher repair failed!" + "\nYou will likely have to reinstall Paradox Launcher to fix this issue.", "OK",
+ "\n\nAn original Steamworks and/or Epic Online Services file could not be found." customFormText: "Paradox Launcher");
+ "\nYou will likely have to reinstall Paradox Launcher to fix this issue.",
"OK", customFormText: "Paradox Launcher");
return RepairResult.Failure; return RepairResult.Failure;
} }
} }

View file

@ -21,8 +21,7 @@ internal static class SteamCMD
internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe"; internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe";
private static readonly ConcurrentDictionary<string, int> private static readonly ConcurrentDictionary<string, int> AttemptCount = new(); // the more app_updates, the longer SteamCMD should wait for app_info_print
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];
@ -38,18 +37,22 @@ internal static class SteamCMD
internal static string DirectoryPath => ProgramData.DirectoryPath; internal static string DirectoryPath => ProgramData.DirectoryPath;
internal static string AppInfoPath => ProgramData.AppInfoPath; internal static string AppInfoPath => ProgramData.AppInfoPath;
private static string GetArguments(string appId) => AttemptCount.TryGetValue(appId, out int attempts) private static string GetArguments(string appId)
=> AttemptCount.TryGetValue(appId, out int attempts)
? $@"@ShutdownOnFailedCommand 0 +force_install_dir {DirectoryPath} +login anonymous +app_info_print {appId} " ? $@"@ShutdownOnFailedCommand 0 +force_install_dir {DirectoryPath} +login anonymous +app_info_print {appId} "
+ string.Concat(Enumerable.Repeat("+app_update 4 ", attempts)) + "+quit" + string.Concat(Enumerable.Repeat("+app_update 4 ", attempts)) + "+quit"
: $"+login anonymous +app_info_print {appId} +quit"; : $"+login anonymous +app_info_print {appId} +quit";
internal static async Task<string> Run(string appId) => await Task.Run(() => internal static async Task<string> Run(string appId)
=> await Task.Run(() =>
{ {
wait_for_lock: wait_for_lock:
if (Program.Canceled) return ""; 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 (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 is not null)
@ -57,14 +60,13 @@ internal static class SteamCMD
AttemptCount.TryGetValue(appId, out int count); AttemptCount.TryGetValue(appId, out int count);
AttemptCount[appId] = ++count; AttemptCount[appId] = ++count;
} }
if (Program.Canceled) return ""; if (Program.Canceled)
return "";
ProcessStartInfo processStartInfo = new() ProcessStartInfo processStartInfo = new()
{ {
FileName = FilePath, RedirectStandardOutput = true, RedirectStandardInput = true, FileName = FilePath, RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true,
RedirectStandardError = true, UseShellExecute = false, UseShellExecute = false, Arguments = appId is null ? "+quit" : GetArguments(appId), CreateNoWindow = true,
Arguments = appId is null ? "+quit" : GetArguments(appId), CreateNoWindow = true, StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8
StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8
}; };
Process process = Process.Start(processStartInfo); Process process = Process.Start(processStartInfo);
StringBuilder output = new(); StringBuilder output = new();
@ -84,7 +86,8 @@ internal static class SteamCMD
{ {
lastOutput = DateTime.UtcNow; lastOutput = DateTime.UtcNow;
char ch = (char)c; char ch = (char)c;
if (ch == '{') appInfoStarted = true; if (ch == '{')
appInfoStarted = true;
_ = appInfoStarted ? appInfo.Append(ch) : output.Append(ch); _ = appInfoStarted ? appInfo.Append(ch) : output.Append(ch);
} }
DateTime now = DateTime.UtcNow; DateTime now = DateTime.UtcNow;
@ -103,11 +106,9 @@ internal static class SteamCMD
_ = appInfo.Clear(); _ = appInfo.Clear();
} }
else else
{
break; break;
} }
} }
}
_ = Interlocked.Decrement(ref locks[i]); _ = Interlocked.Decrement(ref locks[i]);
return appInfo.ToString(); return appInfo.ToString();
} }
@ -123,21 +124,20 @@ internal static class SteamCMD
if (!File.Exists(FilePath)) if (!File.Exists(FilePath))
{ {
HttpClient httpClient = HttpClientManager.HttpClient; HttpClient httpClient = HttpClientManager.HttpClient;
if (httpClient is null) return; if (httpClient is null)
byte[] file = await httpClient.GetByteArrayAsync( return;
new Uri("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip")); byte[] file = await httpClient.GetByteArrayAsync(new Uri("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip"));
file.Write(ArchivePath); file.Write(ArchivePath);
ZipFile.ExtractToDirectory(ArchivePath, DirectoryPath); ZipFile.ExtractToDirectory(ArchivePath, DirectoryPath);
File.Delete(ArchivePath); File.Delete(ArchivePath);
} }
if (!File.Exists(DllPath)) if (!File.Exists(DllPath))
{ {
FileSystemWatcher watcher = new(DirectoryPath) FileSystemWatcher watcher = new(DirectoryPath) { Filter = "*", IncludeSubdirectories = true, EnableRaisingEvents = true };
{ if (File.Exists(DllPath))
Filter = "*", IncludeSubdirectories = true, EnableRaisingEvents = true progress.Report(-15); // update (not used at the moment)
}; else
if (File.Exists(DllPath)) progress.Report(-15); // update (not used at the moment) progress.Report(-1660); // install
else progress.Report(-1660); // install
int cur = 0; int cur = 0;
progress.Report(cur); progress.Report(cur);
watcher.Changed += (sender, e) => progress.Report(++cur); watcher.Changed += (sender, e) => progress.Report(++cur);
@ -146,9 +146,11 @@ internal static class SteamCMD
} }
} }
internal static async Task Cleanup() => await Task.Run(async () => internal static async Task Cleanup()
=> await Task.Run(async () =>
{ {
if (!Directory.Exists(DirectoryPath)) return; if (!Directory.Exists(DirectoryPath))
return;
await Kill(); await Kill();
try try
{ {
@ -164,8 +166,7 @@ internal static class SteamCMD
foreach (string file in Directory.EnumerateFiles(DirectoryPath, "*.ntfs_transaction_failed")) foreach (string file in Directory.EnumerateFiles(DirectoryPath, "*.ntfs_transaction_failed"))
File.Delete(file); File.Delete(file);
if (Directory.Exists(AppCachePath)) if (Directory.Exists(AppCachePath))
Directory.Delete(AppCachePath, Directory.Delete(AppCachePath, true); // this is definitely needed, so SteamCMD gets the latest information for us
true); // this is definitely needed, so SteamCMD gets the latest information for us
if (Directory.Exists(DumpsPath)) if (Directory.Exists(DumpsPath))
Directory.Delete(DumpsPath, true); Directory.Delete(DumpsPath, true);
if (Directory.Exists(LogsPath)) if (Directory.Exists(LogsPath))
@ -178,13 +179,14 @@ internal static class SteamCMD
internal static async Task<VProperty> GetAppInfo(string appId, string branch = "public", int buildId = 0) internal static async Task<VProperty> GetAppInfo(string appId, string branch = "public", int buildId = 0)
{ {
if (Program.Canceled) return null; if (Program.Canceled)
return null;
string output; string output;
string appUpdateFile = $@"{AppInfoPath}\{appId}.vdf"; string appUpdateFile = $@"{AppInfoPath}\{appId}.vdf";
restart: restart:
if (Program.Canceled) return null; if (Program.Canceled)
return null;
if (File.Exists(appUpdateFile)) if (File.Exists(appUpdateFile))
{
try try
{ {
output = await File.ReadAllTextAsync(appUpdateFile, Encoding.UTF8); output = await File.ReadAllTextAsync(appUpdateFile, Encoding.UTF8);
@ -193,7 +195,6 @@ internal static class SteamCMD
{ {
goto restart; goto restart;
} }
}
else else
{ {
output = await Run(appId) ?? ""; output = await Run(appId) ?? "";
@ -213,37 +214,40 @@ internal static class SteamCMD
} }
} }
else else
{
goto restart; goto restart;
} }
} if (Program.Canceled)
if (Program.Canceled) return null; return null;
if (!ValveDataFile.TryDeserialize(output, out VProperty appInfo) || appInfo.Value is VValue) if (!ValveDataFile.TryDeserialize(output, out VProperty appInfo) || appInfo.Value is VValue)
{ {
File.Delete(appUpdateFile); File.Delete(appUpdateFile);
goto restart; goto restart;
} }
if (appInfo is null || appInfo.Value?.Children()?.ToList()?.Count == 0) return appInfo; if (appInfo is null || 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") if (type is not null && type.ToString() != "Game")
return appInfo; return appInfo;
string buildid = appInfo.Value?.GetChild("depots")?.GetChild("branches")?.GetChild(branch) string buildid = appInfo.Value?.GetChild("depots")?.GetChild("branches")?.GetChild(branch)?.GetChild("buildid")?.ToString();
?.GetChild("buildid")?.ToString(); if (buildid is null && type is not null)
if (buildid is null && type is not null) return appInfo; return appInfo;
if (type is not null && (!int.TryParse(buildid, out int gamebuildId) || gamebuildId >= buildId)) if (type is not null && (!int.TryParse(buildid, out int gamebuildId) || gamebuildId >= buildId))
return appInfo; return appInfo;
List<string> dlcAppIds = await ParseDlcAppIds(appInfo); List<string> dlcAppIds = await ParseDlcAppIds(appInfo);
foreach (string dlcAppUpdateFile in dlcAppIds.Select(id => $@"{AppInfoPath}\{id}.vdf")) foreach (string dlcAppUpdateFile in dlcAppIds.Select(id => $@"{AppInfoPath}\{id}.vdf"))
if (File.Exists(dlcAppUpdateFile)) if (File.Exists(dlcAppUpdateFile))
File.Delete(dlcAppUpdateFile); File.Delete(dlcAppUpdateFile);
if (File.Exists(appUpdateFile)) File.Delete(appUpdateFile); if (File.Exists(appUpdateFile))
File.Delete(appUpdateFile);
goto restart; goto restart;
} }
internal static async Task<List<string>> ParseDlcAppIds(VProperty appInfo) => await Task.Run(() => internal static async Task<List<string>> ParseDlcAppIds(VProperty appInfo)
=> await Task.Run(() =>
{ {
List<string> dlcIds = new(); List<string> dlcIds = new();
if (Program.Canceled || appInfo is null) return dlcIds; if (Program.Canceled || appInfo is null)
return dlcIds;
VToken extended = appInfo.Value.GetChild("extended"); VToken extended = appInfo.Value.GetChild("extended");
if (extended is not null) if (extended is not null)
foreach (VToken vToken in extended.Where(p => p is VProperty { Key: "listofdlc" })) foreach (VToken vToken in extended.Where(p => p is VProperty { Key: "listofdlc" }))
@ -259,8 +263,7 @@ internal static class SteamCMD
foreach (VToken vToken in depots.Where(p => p is VProperty property && int.TryParse(property.Key, out int _))) foreach (VToken vToken in depots.Where(p => p is VProperty property && int.TryParse(property.Key, out int _)))
{ {
VProperty property = (VProperty)vToken; VProperty property = (VProperty)vToken;
if (int.TryParse(property.Value.GetChild("dlcappid")?.ToString(), out int appId) && appId > 0 if (int.TryParse(property.Value.GetChild("dlcappid")?.ToString(), out int appId) && appId > 0 && !dlcIds.Contains("" + appId))
&& !dlcIds.Contains("" + appId))
dlcIds.Add("" + appId); dlcIds.Add("" + appId);
} }
return dlcIds; return dlcIds;
@ -268,8 +271,7 @@ internal static class SteamCMD
private static async Task Kill() private static async Task Kill()
{ {
List<Task> tasks = Process.GetProcessesByName("steamcmd") List<Task> tasks = Process.GetProcessesByName("steamcmd").Select(process => Task.Run(() =>
.Select(process => Task.Run(() =>
{ {
try try
{ {
@ -278,9 +280,9 @@ internal static class SteamCMD
process.Close(); process.Close();
} }
catch { } catch { }
})) })).ToList();
.ToList(); foreach (Task task in tasks)
foreach (Task task in tasks) await task; await task;
} }
internal static void Dispose() internal static void Dispose()

View file

@ -18,70 +18,74 @@ internal static class SteamLibrary
{ {
get get
{ {
installPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Valve\Steam", "InstallPath", installPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Valve\Steam", "InstallPath", null) as string;
null) as string; installPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Valve\Steam", "InstallPath", null) as string;
installPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Valve\Steam", "InstallPath",
null) as string;
return installPath.BeautifyPath(); return installPath.BeautifyPath();
} }
} }
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories( internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory)
string gameDirectory) => => await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
internal static async Task<List<(string appId, string name, string branch, int buildId, string gameDirectory)>> internal static async Task<List<(string appId, string name, string branch, int buildId, string gameDirectory)>> GetGames()
GetGames() => await Task.Run(async () => => await Task.Run(async () =>
{ {
List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new(); List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new();
List<string> gameLibraryDirectories = await GetLibraryDirectories(); List<string> gameLibraryDirectories = await GetLibraryDirectories();
foreach (string libraryDirectory in gameLibraryDirectories) foreach (string libraryDirectory in gameLibraryDirectories)
{ {
if (Program.Canceled) return games; if (Program.Canceled)
return games;
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) game in foreach ((string appId, string name, string branch, int buildId, string gameDirectory) game in
(await GetGamesFromLibraryDirectory(libraryDirectory)) (await GetGamesFromLibraryDirectory(libraryDirectory)).Where(game
.Where(game => !games.Any(_game => _game.appId == game.appId => !games.Any(_game => _game.appId == game.appId && _game.gameDirectory == game.gameDirectory)))
&& _game.gameDirectory == game.gameDirectory)))
games.Add(game); games.Add(game);
} }
return games; return games;
}); });
internal static async Task<List<(string appId, string name, string branch, int buildId, string gameDirectory)>> internal static async Task<List<(string appId, string name, string branch, int buildId, string gameDirectory)>>
GetGamesFromLibraryDirectory(string libraryDirectory) => await Task.Run(() => GetGamesFromLibraryDirectory(string libraryDirectory)
=> await Task.Run(() =>
{ {
List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new(); List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new();
if (Program.Canceled || !Directory.Exists(libraryDirectory)) return games; if (Program.Canceled || !Directory.Exists(libraryDirectory))
return games;
foreach (string file in Directory.EnumerateFiles(libraryDirectory, "*.acf")) foreach (string file in Directory.EnumerateFiles(libraryDirectory, "*.acf"))
{ {
if (Program.Canceled) return games; if (Program.Canceled)
return games;
if (ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty result)) if (ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty result))
{ {
string appId = result.Value.GetChild("appid")?.ToString(); string appId = result.Value.GetChild("appid")?.ToString();
string installdir = result.Value.GetChild("installdir")?.ToString(); string installdir = result.Value.GetChild("installdir")?.ToString();
string name = result.Value.GetChild("name")?.ToString(); string name = result.Value.GetChild("name")?.ToString();
string buildId = result.Value.GetChild("buildid")?.ToString(); string buildId = result.Value.GetChild("buildid")?.ToString();
if (string.IsNullOrWhiteSpace(appId) if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(installdir) || string.IsNullOrWhiteSpace(name)
|| string.IsNullOrWhiteSpace(installdir)
|| string.IsNullOrWhiteSpace(name)
|| string.IsNullOrWhiteSpace(buildId)) || string.IsNullOrWhiteSpace(buildId))
continue; continue;
string gameDirectory = (libraryDirectory + @"\common\" + installdir).BeautifyPath(); string gameDirectory = (libraryDirectory + @"\common\" + installdir).BeautifyPath();
if (games.Any(g => g.appId == appId && g.gameDirectory == gameDirectory)) continue; if (games.Any(g => g.appId == appId && g.gameDirectory == gameDirectory))
if (!int.TryParse(appId, out int appIdInt)) continue; continue;
if (!int.TryParse(buildId, out int buildIdInt)) continue; if (!int.TryParse(appId, out int appIdInt))
continue;
if (!int.TryParse(buildId, out int buildIdInt))
continue;
string branch = result.Value.GetChild("UserConfig")?.GetChild("betakey")?.ToString(); string branch = result.Value.GetChild("UserConfig")?.GetChild("betakey")?.ToString();
if (string.IsNullOrWhiteSpace(branch)) branch = "public"; if (string.IsNullOrWhiteSpace(branch))
branch = "public";
games.Add((appId, name, branch, buildIdInt, gameDirectory)); games.Add((appId, name, branch, buildIdInt, gameDirectory));
} }
} }
return games; return games;
}); });
internal static async Task<List<string>> GetLibraryDirectories() => await Task.Run(() => internal static async Task<List<string>> GetLibraryDirectories()
=> await Task.Run(() =>
{ {
List<string> gameDirectories = new(); List<string> gameDirectories = new();
if (Program.Canceled) return gameDirectories; if (Program.Canceled)
return gameDirectories;
string steamInstallPath = InstallPath; string steamInstallPath = InstallPath;
if (steamInstallPath != null && Directory.Exists(steamInstallPath)) if (steamInstallPath != null && Directory.Exists(steamInstallPath))
{ {
@ -90,14 +94,13 @@ internal static class SteamLibrary
{ {
gameDirectories.Add(libraryFolder); gameDirectories.Add(libraryFolder);
string libraryFolders = libraryFolder + @"\libraryfolders.vdf"; string libraryFolders = libraryFolder + @"\libraryfolders.vdf";
if (File.Exists(libraryFolders) if (File.Exists(libraryFolders) && ValveDataFile.TryDeserialize(File.ReadAllText(libraryFolders, Encoding.UTF8), out VProperty result))
&& ValveDataFile.TryDeserialize(File.ReadAllText(libraryFolders, Encoding.UTF8), out VProperty result))
#pragma warning disable IDE0220 // Add explicit cast #pragma warning disable IDE0220 // Add explicit cast
foreach (VProperty property in result.Value.Where( foreach (VProperty property in result.Value.Where(p => p is VProperty && int.TryParse((p as VProperty).Key, out int _)))
p => p is VProperty && int.TryParse((p as VProperty).Key, out int _)))
{ {
string path = property.Value.GetChild("path")?.ToString(); string path = property.Value.GetChild("path")?.ToString();
if (string.IsNullOrWhiteSpace(path)) continue; if (string.IsNullOrWhiteSpace(path))
continue;
path += @"\steamapps"; path += @"\steamapps";
if (Directory.Exists(path) && !gameDirectories.Contains(path)) if (Directory.Exists(path) && !gameDirectories.Contains(path))
gameDirectories.Add(path); gameDirectories.Add(path);

View file

@ -18,23 +18,25 @@ internal static class SteamStore
private const int COOLDOWN_GAME = 600; private const int COOLDOWN_GAME = 600;
private const int COOLDOWN_DLC = 1200; private const int COOLDOWN_DLC = 1200;
internal static async Task<List<string>> ParseDlcAppIds(AppData appData) => await Task.Run(() => internal static async Task<List<string>> ParseDlcAppIds(AppData appData)
=> await Task.Run(() =>
{ {
List<string> dlcIds = new(); List<string> dlcIds = new();
if (appData.dlc is null) return dlcIds; if (appData.dlc is null)
return dlcIds;
dlcIds.AddRange(from appId in appData.dlc where appId > 0 select appId.ToString()); dlcIds.AddRange(from appId in appData.dlc where appId > 0 select appId.ToString());
return dlcIds; return dlcIds;
}); });
internal static async Task<AppData> QueryStoreAPI(string appId, bool isDlc = false, int attempts = 0) internal static async Task<AppData> QueryStoreAPI(string appId, bool isDlc = false, int attempts = 0)
{ {
if (Program.Canceled) return null; if (Program.Canceled)
return null;
string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json"; string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json";
bool cachedExists = File.Exists(cacheFile); bool cachedExists = File.Exists(cacheFile);
if (!cachedExists || ProgramData.CheckCooldown(appId, isDlc ? COOLDOWN_DLC : COOLDOWN_GAME)) if (!cachedExists || ProgramData.CheckCooldown(appId, isDlc ? COOLDOWN_DLC : COOLDOWN_GAME))
{ {
string response string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}");
= await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}");
if (response is not null) if (response is not null)
{ {
IDictionary<string, JToken> apps = (IDictionary<string, JToken>)JsonConvert.DeserializeObject(response); IDictionary<string, JToken> apps = (IDictionary<string, JToken>)JsonConvert.DeserializeObject(response);
@ -60,8 +62,7 @@ internal static class SteamStore
{ {
try try
{ {
await File.WriteAllTextAsync( await File.WriteAllTextAsync(cacheFile, JsonConvert.SerializeObject(data, Formatting.Indented));
cacheFile, JsonConvert.SerializeObject(data, Formatting.Indented));
} }
catch catch
#if DEBUG #if DEBUG
@ -76,16 +77,13 @@ internal static class SteamStore
return data; return data;
} }
#if DEBUG #if DEBUG
DebugForm.Current.Log( DebugForm.Current.Log($"Response data null for appid {appId}{(isDlc ? " (DLC)" : "")}: {app.Value.ToString(Formatting.None)}");
$"Response data null for appid {appId}{(isDlc ? " (DLC)" : "")}: {app.Value.ToString(Formatting.None)}");
#endif #endif
} }
#if DEBUG #if DEBUG
else else
{
DebugForm.Current.Log( DebugForm.Current.Log(
$"Response details null for appid {appId}{(isDlc ? " (DLC)" : "")}: {app.Value.ToString(Formatting.None)}"); $"Response details null for appid {appId}{(isDlc ? " (DLC)" : "")}: {app.Value.ToString(Formatting.None)}");
}
#endif #endif
} }
catch catch
@ -99,7 +97,8 @@ internal static class SteamStore
{ } { }
#endif #endif
#if DEBUG #if DEBUG
else DebugForm.Current.Log("Response deserialization null for appid " + appId); else
DebugForm.Current.Log("Response deserialization null for appid " + appId);
#endif #endif
} }
else else

View file

@ -22,16 +22,16 @@ internal static class UbisoftLibrary
} }
} }
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories( internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory)
string gameDirectory) => => await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
internal static async Task<List<(string gameId, string name, string gameDirectory)>> GetGames() => await Task.Run( internal static async Task<List<(string gameId, string name, string gameDirectory)>> GetGames()
() => => await Task.Run(() =>
{ {
List<(string gameId, string name, string gameDirectory)> games = new(); List<(string gameId, string name, string gameDirectory)> games = new();
RegistryKey installsKey = InstallsKey; RegistryKey installsKey = InstallsKey;
if (installsKey is null) return games; if (installsKey is null)
return games;
foreach (string gameId in installsKey.GetSubKeyNames()) foreach (string gameId in installsKey.GetSubKeyNames())
{ {
RegistryKey installKey = installsKey.OpenSubKey(gameId); RegistryKey installKey = installsKey.OpenSubKey(gameId);

View file

@ -42,8 +42,10 @@ internal static class Program
internal static bool IsGameBlocked(string name, string directory = null) internal static bool IsGameBlocked(string name, string directory = null)
{ {
if (!BlockProtectedGames) return false; if (!BlockProtectedGames)
if (ProtectedGames.Contains(name)) return true; return false;
if (ProtectedGames.Contains(name))
return true;
if (directory is not null && !ProtectedGameDirectoryExceptions.Contains(name)) if (directory is not null && !ProtectedGameDirectoryExceptions.Contains(name))
foreach (string path in ProtectedGameDirectories) foreach (string path in ProtectedGameDirectories)
if (Directory.Exists(directory + path)) if (Directory.Exists(directory + path))
@ -56,16 +58,12 @@ internal static class Program
if (selection.AreDllsLocked) if (selection.AreDllsLocked)
{ {
using DialogForm dialogForm = new(form); using DialogForm dialogForm = new(form);
if (dialogForm.Show(SystemIcons.Error, if (dialogForm.Show(SystemIcons.Error, $"ERROR: {selection.Name} is currently running!" + "\n\nPlease close the program/game to continue . . . ",
$"ERROR: {selection.Name} is currently running!" +
"\n\nPlease close the program/game to continue . . . ",
"Retry", "Cancel") == DialogResult.OK) "Retry", "Cancel") == DialogResult.OK)
return IsProgramRunningDialog(form, selection); return IsProgramRunningDialog(form, selection);
} }
else else
{
return true; return true;
}
return false; return false;
} }
@ -81,8 +79,7 @@ internal static class Program
Application.ApplicationExit += OnApplicationExit; Application.ApplicationExit += OnApplicationExit;
Application.ThreadException += (s, e) => e.Exception?.HandleFatalException(); Application.ThreadException += (s, e) => e.Exception?.HandleFatalException();
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += (s, e) AppDomain.CurrentDomain.UnhandledException += (s, e) => (e.ExceptionObject as Exception)?.HandleFatalException();
=> (e.ExceptionObject as Exception)?.HandleFatalException();
retry: retry:
try try
{ {
@ -95,7 +92,8 @@ internal static class Program
} }
catch (Exception e) catch (Exception e)
{ {
if (e.HandleException()) goto retry; if (e.HandleException())
goto retry;
Application.Exit(); Application.Exit();
return; return;
} }

View file

@ -25,17 +25,14 @@ internal class ProgramSelection
internal static readonly List<ProgramSelection> All = new(); internal static readonly List<ProgramSelection> All = new();
internal readonly SortedList<string, (DlcType type, string name, string icon)> AllDlc internal readonly SortedList<string, (DlcType type, string name, string icon)> AllDlc = new(PlatformIdComparer.String);
= new(PlatformIdComparer.String);
internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> dlc)> internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> dlc)> ExtraDlc = new(); // for Paradox Launcher
ExtraDlc = new(); // for Paradox Launcher
internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> dlc)> internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> dlc)>
ExtraSelectedDlc = new(); // for Paradox Launcher ExtraSelectedDlc = new(); // for Paradox Launcher
internal readonly SortedList<string, (DlcType type, string name, string icon)> SelectedDlc internal readonly SortedList<string, (DlcType type, string name, string icon)> SelectedDlc = new(PlatformIdComparer.String);
= new(PlatformIdComparer.String);
internal List<string> DllDirectories; internal List<string> DllDirectories;
internal bool Enabled; internal bool Enabled;
@ -67,54 +64,31 @@ internal class ProgramSelection
{ {
if (Platform is Platform.Steam or Platform.Paradox) if (Platform is Platform.Steam or Platform.Paradox)
{ {
directory.GetCreamApiComponents(out string api32, out string api32_o, out string api64, directory.GetCreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
out string api64_o, out string config); if (api32.IsFilePathLocked() || api32_o.IsFilePathLocked() || api64.IsFilePathLocked() || api64_o.IsFilePathLocked()
if (api32.IsFilePathLocked()
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked()) || config.IsFilePathLocked())
return true; return true;
directory.GetSmokeApiComponents(out api32, out api32_o, out api64, out api64_o, out config, directory.GetSmokeApiComponents(out api32, out api32_o, out api64, out api64_o, out config, out string cache);
out string cache); if (api32.IsFilePathLocked() || api32_o.IsFilePathLocked() || api64.IsFilePathLocked() || api64_o.IsFilePathLocked()
if (api32.IsFilePathLocked() || config.IsFilePathLocked() || cache.IsFilePathLocked())
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked()
|| cache.IsFilePathLocked())
return true; return true;
} }
if (Platform is Platform.Epic or Platform.Paradox) if (Platform is Platform.Epic or Platform.Paradox)
{ {
directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
out string api64_o, out string config); if (api32.IsFilePathLocked() || api32_o.IsFilePathLocked() || api64.IsFilePathLocked() || api64_o.IsFilePathLocked()
if (api32.IsFilePathLocked()
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked()) || config.IsFilePathLocked())
return true; return true;
} }
if (Platform is Platform.Ubisoft) if (Platform is Platform.Ubisoft)
{ {
directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
out string api64_o, out string config); if (api32.IsFilePathLocked() || api32_o.IsFilePathLocked() || api64.IsFilePathLocked() || api64_o.IsFilePathLocked()
if (api32.IsFilePathLocked()
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked()) || config.IsFilePathLocked())
return true; return true;
directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config);
out api64, out api64_o, out config); if (old_api32.IsFilePathLocked() || old_api64.IsFilePathLocked() || api32.IsFilePathLocked() || api32_o.IsFilePathLocked()
if (old_api32.IsFilePathLocked() || api64.IsFilePathLocked() || api64_o.IsFilePathLocked() || config.IsFilePathLocked())
|| old_api64.IsFilePathLocked()
|| api32.IsFilePathLocked()
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked())
return true; return true;
} }
} }
@ -128,8 +102,10 @@ internal class ProgramSelection
private void Toggle(string dlcAppId, (DlcType type, string name, string icon) dlcApp, bool enabled) private void Toggle(string dlcAppId, (DlcType type, string name, string icon) dlcApp, bool enabled)
{ {
if (enabled) SelectedDlc[dlcAppId] = dlcApp; if (enabled)
else _ = SelectedDlc.Remove(dlcAppId); SelectedDlc[dlcAppId] = dlcApp;
else
_ = SelectedDlc.Remove(dlcAppId);
} }
internal void ToggleDlc(string dlcId, bool enabled) internal void ToggleDlc(string dlcId, bool enabled)
@ -160,7 +136,8 @@ internal class ProgramSelection
return; return;
} }
_ = DllDirectories.RemoveAll(directory => !Directory.Exists(directory)); _ = DllDirectories.RemoveAll(directory => !Directory.Exists(directory));
if (!DllDirectories.Any()) _ = All.Remove(this); if (!DllDirectories.Any())
_ = All.Remove(this);
} }
internal void Validate(List<(Platform platform, string id, string name)> programsToScan) internal void Validate(List<(Platform platform, string id, string name)> programsToScan)
@ -178,15 +155,12 @@ internal class ProgramSelection
internal static void ValidateAll(List<(Platform platform, string id, string name)> programsToScan) internal static void ValidateAll(List<(Platform platform, string id, string name)> programsToScan)
=> AllSafe.ForEach(selection => selection.Validate(programsToScan)); => AllSafe.ForEach(selection => selection.Validate(programsToScan));
internal static ProgramSelection FromPlatformId(Platform platform, string gameId) internal static ProgramSelection FromPlatformId(Platform platform, string gameId) => AllSafe.Find(s => s.Platform == platform && s.Id == gameId);
=> AllSafe.Find(s => s.Platform == platform && s.Id == gameId);
internal static (string gameId, (DlcType type, string name, string icon) app)? GetDlcFromPlatformId( internal static (string gameId, (DlcType type, string name, string icon) app)? GetDlcFromPlatformId(Platform platform, string dlcId)
Platform platform, string dlcId)
{ {
foreach (ProgramSelection selection in AllSafe.Where(s => s.Platform == platform)) foreach (ProgramSelection selection in AllSafe.Where(s => s.Platform == platform))
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in selection.AllDlc.Where( foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in selection.AllDlc.Where(p => p.Key == dlcId))
p => p.Key == dlcId))
return (selection.Id, pair.Value); return (selection.Id, pair.Value);
return null; return null;
} }

View file

@ -14,27 +14,22 @@ 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"), ("Koaloader", "Unlocker.dll"), ("Koaloader", "Unlocker32.dll"), ("Koaloader", "Unlocker64.dll"), ("Lyptus", "Lyptus.dll"),
("Lyptus", "Lyptus.dll"), ("Lyptus", "Lyptus32.dll"), ("Lyptus", "Lyptus64.dll"), ("Lyptus", "Lyptus32.dll"), ("Lyptus", "Lyptus64.dll"), ("SmokeAPI", "SmokeAPI.dll"), ("SmokeAPI", "SmokeAPI32.dll"),
("SmokeAPI", "SmokeAPI.dll"), ("SmokeAPI", "SmokeAPI32.dll"), ("SmokeAPI", "SmokeAPI64.dll"), ("SmokeAPI", "SmokeAPI64.dll"), ("ScreamAPI", "ScreamAPI.dll"), ("ScreamAPI", "ScreamAPI32.dll"), ("ScreamAPI", "ScreamAPI64.dll"),
("ScreamAPI", "ScreamAPI.dll"), ("ScreamAPI", "ScreamAPI32.dll"), ("ScreamAPI", "ScreamAPI64.dll"), ("Uplay R1 Unlocker", "UplayR1Unlocker.dll"), ("Uplay R1 Unlocker", "UplayR1Unlocker32.dll"), ("Uplay R1 Unlocker", "UplayR1Unlocker64.dll"),
("Uplay R1 Unlocker", "UplayR1Unlocker.dll"), ("Uplay R1 Unlocker", "UplayR1Unlocker32.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker32.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker64.dll")
("Uplay R1 Unlocker", "UplayR1Unlocker64.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker.dll"),
("Uplay R2 Unlocker", "UplayR2Unlocker32.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker64.dll")
}; };
internal static void GetKoaloaderComponents( internal static void GetKoaloaderComponents(this string directory, out List<string> proxies, out string config)
this string directory,
out List<string> proxies,
out string config
)
{ {
proxies = new List<string>(); proxies = new();
foreach (string proxy in EmbeddedResources.Select(proxy => foreach (string proxy in EmbeddedResources.Select(proxy =>
{ {
proxy = proxy[(proxy.IndexOf('.') + 1)..]; proxy = proxy[(proxy.IndexOf('.') + 1)..];
return proxy[(proxy.IndexOf('.') + 1)..]; return proxy[(proxy.IndexOf('.') + 1)..];
})) proxies.Add(directory + @"\" + proxy); }))
proxies.Add(directory + @"\" + proxy);
config = directory + @"\Koaloader.json"; config = directory + @"\Koaloader.json";
} }
@ -51,8 +46,7 @@ internal static class Koaloader
} }
} }
internal static void GetProxyInfoFromIdentifier(this string resourceIdentifier, out string proxyName, internal static void GetProxyInfoFromIdentifier(this string resourceIdentifier, out string proxyName, out BinaryType binaryType)
out BinaryType binaryType)
{ {
string baseIdentifier = resourceIdentifier[(resourceIdentifier.IndexOf('.') + 1)..]; string baseIdentifier = resourceIdentifier[(resourceIdentifier.IndexOf('.') + 1)..];
baseIdentifier = baseIdentifier[..baseIdentifier.IndexOf('.')]; baseIdentifier = baseIdentifier[..baseIdentifier.IndexOf('.')];
@ -83,13 +77,12 @@ internal static class Koaloader
else if (File.Exists(config)) else if (File.Exists(config))
{ {
File.Delete(config); File.Delete(config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
false);
} }
} }
internal static void WriteConfig(StreamWriter writer, SortedList<string, string> targets, internal static void WriteConfig(StreamWriter writer, SortedList<string, string> targets, SortedList<string, string> modules,
SortedList<string, string> modules, InstallForm installForm = null) InstallForm installForm = null)
{ {
writer.WriteLine("{"); writer.WriteLine("{");
writer.WriteLine(" \"logging\": false,"); writer.WriteLine(" \"logging\": false,");
@ -108,9 +101,7 @@ internal static class Koaloader
writer.WriteLine(" ]"); writer.WriteLine(" ]");
} }
else else
{
writer.WriteLine(" \"targets\": []"); writer.WriteLine(" \"targets\": []");
}
if (modules.Any()) if (modules.Any())
{ {
writer.WriteLine(" \"modules\": ["); writer.WriteLine(" \"modules\": [");
@ -127,24 +118,20 @@ internal static class Koaloader
writer.WriteLine(" ]"); writer.WriteLine(" ]");
} }
else else
{
writer.WriteLine(" \"modules\": []"); writer.WriteLine(" \"modules\": []");
}
writer.WriteLine("}"); writer.WriteLine("}");
} }
internal static async Task Uninstall(string directory, string rootDirectory = null, InstallForm installForm = null, internal static async Task Uninstall(string directory, string rootDirectory = null, InstallForm installForm = null, bool deleteConfig = true)
bool deleteConfig = true) => await Task.Run(async () => => await Task.Run(async () =>
{ {
directory.GetKoaloaderComponents(out List<string> proxies, out string config); directory.GetKoaloaderComponents(out List<string> proxies, out string config);
foreach (string proxyPath in proxies.Where( foreach (string proxyPath in proxies.Where(proxyPath => File.Exists(proxyPath) && proxyPath.IsResourceFile(ResourceIdentifier.Koaloader)))
proxyPath => File.Exists(proxyPath) && proxyPath.IsResourceFile(ResourceIdentifier.Koaloader)))
{ {
File.Delete(proxyPath); File.Delete(proxyPath);
installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(proxyPath)}", LogTextBox.Action, false); installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(proxyPath)}", LogTextBox.Action, false);
} }
foreach ((string unlocker, string path) in AutoLoadDlls foreach ((string unlocker, string path) in AutoLoadDlls.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Where(pair => File.Exists(pair.path) && pair.path.IsResourceFile())) .Where(pair => File.Exists(pair.path) && pair.path.IsResourceFile()))
{ {
File.Delete(path); File.Delete(path);
@ -163,26 +150,23 @@ internal static class Koaloader
await Uninstall(rootDirectory, null, installForm, deleteConfig); await Uninstall(rootDirectory, null, installForm, deleteConfig);
}); });
internal static async Task Install(string directory, BinaryType binaryType, ProgramSelection selection, internal static async Task Install(string directory, BinaryType binaryType, ProgramSelection selection, string rootDirectory = null,
string rootDirectory = null, InstallForm installForm = null, InstallForm installForm = null, bool generateConfig = true)
bool generateConfig = true) => await Task.Run(() => => await Task.Run(() =>
{ {
directory.GetKoaloaderComponents(out List<string> proxies, out string config); directory.GetKoaloaderComponents(out List<string> proxies, out string config);
string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy; string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy;
string path = directory + @"\" + proxy + ".dll"; string path = directory + @"\" + proxy + ".dll";
foreach (string _path in proxies.Where(p => p != path && File.Exists(p) foreach (string _path in proxies.Where(p => p != path && File.Exists(p) && p.IsResourceFile(ResourceIdentifier.Koaloader)))
&& p.IsResourceFile(ResourceIdentifier.Koaloader)))
{ {
File.Delete(_path); File.Delete(_path);
installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(_path)}", LogTextBox.Action, false); installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(_path)}", LogTextBox.Action, false);
} }
if (File.Exists(path) && !path.IsResourceFile(ResourceIdentifier.Koaloader)) if (File.Exists(path) && !path.IsResourceFile(ResourceIdentifier.Koaloader))
throw new CustomMessageException("A non-Koaloader DLL named " + proxy throw new CustomMessageException("A non-Koaloader DLL named " + proxy + ".dll already exists in this directory!");
+ ".dll already exists in this directory!");
path.WriteProxy(proxy, binaryType); path.WriteProxy(proxy, binaryType);
installForm?.UpdateUser( installForm?.UpdateUser($"Wrote {(binaryType == BinaryType.BIT32 ? "32-bit" : "64-bit")} Koaloader: {Path.GetFileName(path)}", LogTextBox.Action,
$"Wrote {(binaryType == BinaryType.BIT32 ? "32-bit" : "64-bit")} Koaloader: {Path.GetFileName(path)}", false);
LogTextBox.Action, false);
bool bit32 = false, bit64 = false; bool bit32 = false, bit64 = false;
foreach (string executable in Directory.EnumerateFiles(directory, "*.exe")) foreach (string executable in Directory.EnumerateFiles(directory, "*.exe"))
if (executable.TryGetFileBinaryType(out BinaryType binaryType)) if (executable.TryGetFileBinaryType(out BinaryType binaryType))
@ -204,8 +188,7 @@ internal static class Koaloader
if (File.Exists(path)) if (File.Exists(path))
{ {
File.Delete(path); File.Delete(path);
installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
path = rootDirectory + @"\SmokeAPI32.dll"; path = rootDirectory + @"\SmokeAPI32.dll";
} }
@ -222,8 +205,7 @@ internal static class Koaloader
if (File.Exists(path)) if (File.Exists(path))
{ {
File.Delete(path); File.Delete(path);
installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
path = rootDirectory + @"\SmokeAPI64.dll"; path = rootDirectory + @"\SmokeAPI64.dll";
} }
@ -244,8 +226,7 @@ internal static class Koaloader
if (File.Exists(path)) if (File.Exists(path))
{ {
File.Delete(path); File.Delete(path);
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
path = rootDirectory + @"\ScreamAPI32.dll"; path = rootDirectory + @"\ScreamAPI32.dll";
} }
@ -262,8 +243,7 @@ internal static class Koaloader
if (File.Exists(path)) if (File.Exists(path))
{ {
File.Delete(path); File.Delete(path);
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
path = rootDirectory + @"\ScreamAPI64.dll"; path = rootDirectory + @"\ScreamAPI64.dll";
} }
@ -284,9 +264,7 @@ internal static class Koaloader
if (File.Exists(path)) if (File.Exists(path))
{ {
File.Delete(path); File.Delete(path);
installForm?.UpdateUser( installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
$"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
path = rootDirectory + @"\UplayR1Unlocker32.dll"; path = rootDirectory + @"\UplayR1Unlocker32.dll";
} }
@ -303,9 +281,7 @@ internal static class Koaloader
if (File.Exists(path)) if (File.Exists(path))
{ {
File.Delete(path); File.Delete(path);
installForm?.UpdateUser( installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
$"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
path = rootDirectory + @"\UplayR1Unlocker64.dll"; path = rootDirectory + @"\UplayR1Unlocker64.dll";
} }
@ -323,9 +299,7 @@ internal static class Koaloader
if (File.Exists(path)) if (File.Exists(path))
{ {
File.Delete(path); File.Delete(path);
installForm?.UpdateUser( installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
$"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
path = rootDirectory + @"\UplayR2Unlocker32.dll"; path = rootDirectory + @"\UplayR2Unlocker32.dll";
} }
@ -342,9 +316,7 @@ internal static class Koaloader
if (File.Exists(path)) if (File.Exists(path))
{ {
File.Delete(path); File.Delete(path);
installForm?.UpdateUser( installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
$"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
path = rootDirectory + @"\UplayR2Unlocker64.dll"; path = rootDirectory + @"\UplayR2Unlocker64.dll";
} }

View file

@ -335,7 +335,7 @@ internal static class Resources
if (embeddedResources is null) if (embeddedResources is null)
{ {
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames(); string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
embeddedResources = new List<string>(); embeddedResources = new();
foreach (string resourceName in names.Where(n => n.StartsWith("CreamInstaller.Resources."))) foreach (string resourceName in names.Where(n => n.StartsWith("CreamInstaller.Resources.")))
embeddedResources.Add(resourceName[25..]); embeddedResources.Add(resourceName[25..]);
} }
@ -345,8 +345,7 @@ internal static class Resources
internal static void Write(this string resourceIdentifier, string filePath) internal static void Write(this string resourceIdentifier, string filePath)
{ {
using Stream resource = Assembly.GetExecutingAssembly() using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier);
.GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier);
using FileStream file = new(filePath, FileMode.Create, FileAccess.Write); using FileStream file = new(filePath, FileMode.Create, FileAccess.Write);
resource.CopyTo(file); resource.CopyTo(file);
} }
@ -378,45 +377,42 @@ internal static class Resources
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
private static extern bool GetBinaryType(string lpApplicationName, out BinaryType lpBinaryType); private static extern bool GetBinaryType(string lpApplicationName, out BinaryType lpBinaryType);
internal static bool TryGetFileBinaryType(this string path, out BinaryType binaryType) internal static bool TryGetFileBinaryType(this string path, out BinaryType binaryType) => GetBinaryType(path, out binaryType);
=> GetBinaryType(path, out binaryType);
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories( internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(this string rootDirectory, bool filterCommon = false,
this string rootDirectory, bool filterCommon = false, Func<string, bool> validFunc = null) => Func<string, bool> validFunc = null)
await Task.Run(async () => (await rootDirectory.GetExecutables(filterCommon, validFunc) => await Task.Run(async ()
?? (filterCommon || validFunc is not null => (await rootDirectory.GetExecutables(filterCommon, validFunc)
? await rootDirectory.GetExecutables() ?? (filterCommon || validFunc is not null ? await rootDirectory.GetExecutables() : null))?.Select(e =>
: null))?.Select(e =>
{ {
e.path = Path.GetDirectoryName(e.path); e.path = Path.GetDirectoryName(e.path);
return e; return e;
})?.DistinctBy(e => e.path).ToList()); })?.DistinctBy(e => e.path).ToList());
internal static async Task<List<(string path, BinaryType binaryType)>> GetExecutables( internal static async Task<List<(string path, BinaryType binaryType)>> GetExecutables(this string rootDirectory, bool filterCommon = false,
this string rootDirectory, bool filterCommon = false, Func<string, bool> validFunc = null) => await Task.Run( Func<string, bool> validFunc = null)
() => => await Task.Run(() =>
{ {
List<(string path, BinaryType binaryType)> executables = new(); List<(string path, BinaryType binaryType)> executables = new();
if (Program.Canceled || !Directory.Exists(rootDirectory)) return null; if (Program.Canceled || !Directory.Exists(rootDirectory))
foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe", return null;
new EnumerationOptions { RecurseSubdirectories = true })) foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe", new EnumerationOptions { RecurseSubdirectories = true }))
{ {
if (Program.Canceled) return null; if (Program.Canceled)
if (executables.All(e => e.path != path) return null;
&& (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path)) if (executables.All(e => e.path != path) && (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path))
&& (validFunc is null || validFunc(path)) && (validFunc is null || validFunc(path)) && path.TryGetFileBinaryType(out BinaryType binaryType)
&& path.TryGetFileBinaryType(out BinaryType binaryType) && binaryType is BinaryType.BIT64) && binaryType is BinaryType.BIT64)
executables.Add((path, binaryType)); executables.Add((path, binaryType));
Thread.Sleep(1); Thread.Sleep(1);
} }
foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe", foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe", new EnumerationOptions { RecurseSubdirectories = true }))
new EnumerationOptions { RecurseSubdirectories = true }))
{ {
if (Program.Canceled) return null; if (Program.Canceled)
if (executables.All(e => e.path != path) return null;
&& (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path)) if (executables.All(e => e.path != path) && (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path))
&& (validFunc is null || validFunc(path)) && (validFunc is null || validFunc(path)) && path.TryGetFileBinaryType(out BinaryType binaryType)
&& path.TryGetFileBinaryType(out BinaryType binaryType) && binaryType is BinaryType.BIT32) && binaryType is BinaryType.BIT32)
executables.Add((path, binaryType)); executables.Add((path, binaryType));
Thread.Sleep(1); Thread.Sleep(1);
} }
@ -426,80 +422,54 @@ internal static class Resources
internal static bool IsCommonIncorrectExecutable(this string rootDirectory, string path) internal static bool IsCommonIncorrectExecutable(this string rootDirectory, string path)
{ {
string subPath = path[rootDirectory.Length..].ToUpperInvariant().BeautifyPath(); string subPath = path[rootDirectory.Length..].ToUpperInvariant().BeautifyPath();
return subPath.Contains("SETUP") return subPath.Contains("SETUP") || subPath.Contains("REDIST") || subPath.Contains("SUPPORT")
|| subPath.Contains("REDIST") || subPath.Contains("CRASH") && (subPath.Contains("PAD") || subPath.Contains("REPORT")) || subPath.Contains("HELPER")
|| subPath.Contains("SUPPORT") || subPath.Contains("CEFPROCESS") || subPath.Contains("ZFGAMEBROWSER") || subPath.Contains("MONO") || subPath.Contains("PLUGINS")
|| (subPath.Contains("CRASH") && (subPath.Contains("PAD") || subPath.Contains("REPORT"))) || subPath.Contains("MODDING") || subPath.Contains("MOD") && subPath.Contains("MANAGER") || subPath.Contains("BATTLEYE")
|| subPath.Contains("HELPER")
|| subPath.Contains("CEFPROCESS")
|| subPath.Contains("ZFGAMEBROWSER")
|| subPath.Contains("MONO")
|| subPath.Contains("PLUGINS")
|| subPath.Contains("MODDING")
|| (subPath.Contains("MOD") && subPath.Contains("MANAGER"))
|| subPath.Contains("BATTLEYE")
|| subPath.Contains("ANTICHEAT"); || subPath.Contains("ANTICHEAT");
} }
internal static async Task<List<string>> GetDllDirectoriesFromGameDirectory( internal static async Task<List<string>> GetDllDirectoriesFromGameDirectory(this string gameDirectory, Platform platform)
this string gameDirectory, Platform platform) => await Task.Run(() => => await Task.Run(() =>
{ {
List<string> dllDirectories = new(); List<string> dllDirectories = new();
if (Program.Canceled || !Directory.Exists(gameDirectory)) return null; if (Program.Canceled || !Directory.Exists(gameDirectory))
foreach (string directory in Directory return null;
.EnumerateDirectories(gameDirectory, "*", foreach (string directory in Directory.EnumerateDirectories(gameDirectory, "*", new EnumerationOptions { RecurseSubdirectories = true })
new EnumerationOptions { RecurseSubdirectories = true })
.Append(gameDirectory)) .Append(gameDirectory))
{ {
if (Program.Canceled) return null; if (Program.Canceled)
return null;
string subDirectory = directory.BeautifyPath(); string subDirectory = directory.BeautifyPath();
if (!dllDirectories.Contains(subDirectory)) if (!dllDirectories.Contains(subDirectory))
{ {
bool koaloaderInstalled = Koaloader.AutoLoadDlls bool koaloaderInstalled = Koaloader.AutoLoadDlls.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile()); .Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile());
if (platform is Platform.Steam or Platform.Paradox) if (platform is Platform.Steam or Platform.Paradox)
{ {
subDirectory.GetSmokeApiComponents(out string api, out string api_o, out string api64, subDirectory.GetSmokeApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string config,
out string api64_o, out string config, out string cache); out string cache);
if (File.Exists(api) if (File.Exists(api) || File.Exists(api_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled
|| File.Exists(api_o) || File.Exists(cache) && !koaloaderInstalled)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled)
|| (File.Exists(cache) && !koaloaderInstalled))
dllDirectories.Add(subDirectory); dllDirectories.Add(subDirectory);
} }
if (platform is Platform.Epic or Platform.Paradox) if (platform is Platform.Epic or Platform.Paradox)
{ {
subDirectory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, subDirectory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
out string api64_o, out string config); if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o)
if (File.Exists(api32) || File.Exists(config) && !koaloaderInstalled)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled))
dllDirectories.Add(subDirectory); dllDirectories.Add(subDirectory);
} }
if (platform is Platform.Ubisoft) if (platform is Platform.Ubisoft)
{ {
subDirectory.GetUplayR1Components(out string api32, out string api32_o, out string api64, subDirectory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
out string api64_o, out string config); if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o)
if (File.Exists(api32) || File.Exists(config) && !koaloaderInstalled)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled))
dllDirectories.Add(subDirectory); dllDirectories.Add(subDirectory);
subDirectory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, subDirectory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o,
out api32_o, out api64, out api64_o, out config); out config);
if (File.Exists(old_api32) if (File.Exists(old_api32) || File.Exists(old_api64) || File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64)
|| File.Exists(old_api64) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled)
|| File.Exists(api32)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled))
dllDirectories.Add(subDirectory); dllDirectories.Add(subDirectory);
} }
} }
@ -507,10 +477,7 @@ internal static class Resources
return !dllDirectories.Any() ? null : dllDirectories; return !dllDirectories.Any() ? null : dllDirectories;
}); });
internal static void GetCreamApiComponents( internal static void GetCreamApiComponents(this string directory, out string api32, out string api32_o, out string api64, out string api64_o,
this string directory,
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config) out string config)
{ {
api32 = directory + @"\steam_api.dll"; api32 = directory + @"\steam_api.dll";
@ -522,7 +489,8 @@ internal static class Resources
internal static string ComputeMD5(this string filePath) internal static string ComputeMD5(this string filePath)
{ {
if (!File.Exists(filePath)) return null; if (!File.Exists(filePath))
return null;
#pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms #pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms
using MD5 md5 = MD5.Create(); using MD5 md5 = MD5.Create();
#pragma warning restore CA5351 // Do Not Use Broken Cryptographic Algorithms #pragma warning restore CA5351 // Do Not Use Broken Cryptographic Algorithms
@ -534,9 +502,8 @@ internal static class Resources
internal static bool IsResourceFile(this string filePath, ResourceIdentifier identifier) internal static bool IsResourceFile(this string filePath, ResourceIdentifier identifier)
=> filePath.ComputeMD5() is string hash && ResourceMD5s[identifier].Contains(hash); => filePath.ComputeMD5() is string hash && ResourceMD5s[identifier].Contains(hash);
internal static bool IsResourceFile(this string filePath) => filePath.ComputeMD5() is string hash internal static bool IsResourceFile(this string filePath)
&& ResourceMD5s.Values.Any( => filePath.ComputeMD5() is string hash && ResourceMD5s.Values.Any(hashes => hashes.Contains(hash));
hashes => hashes.Contains(hash));
internal enum BinaryType internal enum BinaryType
{ {

View file

@ -11,12 +11,8 @@ namespace CreamInstaller.Resources;
internal static class ScreamAPI internal static class ScreamAPI
{ {
internal static void GetScreamApiComponents( internal static void GetScreamApiComponents(this string directory, out string api32, out string api32_o, out string api64, out string api64_o,
this string directory, out string config)
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config
)
{ {
api32 = directory + @"\EOSSDK-Win32-Shipping.dll"; api32 = directory + @"\EOSSDK-Win32-Shipping.dll";
api32_o = directory + @"\EOSSDK-Win32-Shipping_o.dll"; api32_o = directory + @"\EOSSDK-Win32-Shipping_o.dll";
@ -30,13 +26,11 @@ internal static class ScreamAPI
directory.GetScreamApiComponents(out _, out _, out _, out _, out string config); directory.GetScreamApiComponents(out _, out _, out _, out _, out string config);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> overrideCatalogItems IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> overrideCatalogItems
= selection.AllDlc.Where(pair => pair.Value.type is DlcType.EpicCatalogItem).Except(selection.SelectedDlc); = selection.AllDlc.Where(pair => pair.Value.type is DlcType.EpicCatalogItem).Except(selection.SelectedDlc);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
selection.ExtraSelectedDlc)
overrideCatalogItems = overrideCatalogItems.Except(extraDlc); overrideCatalogItems = overrideCatalogItems.Except(extraDlc);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> entitlements IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> entitlements
= selection.SelectedDlc.Where(pair => pair.Value.type == DlcType.EpicEntitlement); = selection.SelectedDlc.Where(pair => pair.Value.type == DlcType.EpicEntitlement);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> _dlc) in foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> _dlc) in selection.ExtraSelectedDlc)
selection.ExtraSelectedDlc)
entitlements = entitlements.Concat(_dlc.Where(pair => pair.Value.type == DlcType.EpicEntitlement)); entitlements = entitlements.Concat(_dlc.Where(pair => pair.Value.type == DlcType.EpicEntitlement));
if (overrideCatalogItems.Any() || entitlements.Any()) if (overrideCatalogItems.Any() || entitlements.Any())
{ {
@ -44,28 +38,20 @@ internal static class ScreamAPI
installForm.UpdateUser("Generating ScreamAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ installForm.UpdateUser("Generating ScreamAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
File.Create(config).Close(); File.Create(config).Close();
StreamWriter writer = new(config, true, Encoding.UTF8); StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer, WriteConfig(writer, new(overrideCatalogItems.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
new SortedList<string, (DlcType type, string name, string icon)>( new(entitlements.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm);
overrideCatalogItems.ToDictionary(pair => pair.Key, pair => pair.Value),
PlatformIdComparer.String),
new SortedList<string, (DlcType type, string name, string icon)>(
entitlements.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
installForm);
writer.Flush(); writer.Flush();
writer.Close(); writer.Close();
} }
else if (File.Exists(config)) else if (File.Exists(config))
{ {
File.Delete(config); File.Delete(config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
false);
} }
} }
internal static void WriteConfig(StreamWriter writer, internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> overrideCatalogItems,
SortedList<string, (DlcType type, string name, string icon)> overrideCatalogItems, SortedList<string, (DlcType type, string name, string icon)> entitlements, InstallForm installForm = null)
SortedList<string, (DlcType type, string name, string icon)> entitlements,
InstallForm installForm = null)
{ {
writer.WriteLine("{"); writer.WriteLine("{");
writer.WriteLine(" \"version\": 2,"); writer.WriteLine(" \"version\": 2,");
@ -77,22 +63,18 @@ internal static class ScreamAPI
if (overrideCatalogItems.Any()) if (overrideCatalogItems.Any())
{ {
writer.WriteLine(" \"override\": ["); writer.WriteLine(" \"override\": [");
KeyValuePair<string, (DlcType type, string name, string icon)> lastOverrideCatalogItem KeyValuePair<string, (DlcType type, string name, string icon)> lastOverrideCatalogItem = overrideCatalogItems.Last();
= overrideCatalogItems.Last();
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in overrideCatalogItems) foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in overrideCatalogItems)
{ {
string id = pair.Key; string id = pair.Key;
(_, string name, _) = pair.Value; (_, string name, _) = pair.Value;
writer.WriteLine($" \"{id}\"{(pair.Equals(lastOverrideCatalogItem) ? "" : ",")}"); writer.WriteLine($" \"{id}\"{(pair.Equals(lastOverrideCatalogItem) ? "" : ",")}");
installForm?.UpdateUser($"Added override catalog item to ScreamAPI.json with id {id} ({name})", installForm?.UpdateUser($"Added override catalog item to ScreamAPI.json with id {id} ({name})", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
writer.WriteLine(" ]"); writer.WriteLine(" ]");
} }
else else
{
writer.WriteLine(" \"override\": []"); writer.WriteLine(" \"override\": []");
}
writer.WriteLine(" },"); writer.WriteLine(" },");
writer.WriteLine(" \"entitlements\": {"); writer.WriteLine(" \"entitlements\": {");
writer.WriteLine(" \"unlock_all\": true,"); writer.WriteLine(" \"unlock_all\": true,");
@ -106,15 +88,12 @@ internal static class ScreamAPI
string id = pair.Key; string id = pair.Key;
(_, string name, _) = pair.Value; (_, string name, _) = pair.Value;
writer.WriteLine($" \"{id}\"{(pair.Equals(lastEntitlement) ? "" : ",")}"); writer.WriteLine($" \"{id}\"{(pair.Equals(lastEntitlement) ? "" : ",")}");
installForm?.UpdateUser($"Added entitlement to ScreamAPI.json with id {id} ({name})", LogTextBox.Action, installForm?.UpdateUser($"Added entitlement to ScreamAPI.json with id {id} ({name})", LogTextBox.Action, false);
false);
} }
writer.WriteLine(" ]"); writer.WriteLine(" ]");
} }
else else
{
writer.WriteLine(" \"inject\": []"); writer.WriteLine(" \"inject\": []");
}
writer.WriteLine(" }"); writer.WriteLine(" }");
writer.WriteLine("}"); writer.WriteLine("}");
} }
@ -122,8 +101,7 @@ internal static class ScreamAPI
internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true) internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true)
=> await Task.Run(() => => await Task.Run(() =>
{ {
directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
out string config);
if (File.Exists(api32_o)) if (File.Exists(api32_o))
{ {
if (File.Exists(api32)) if (File.Exists(api32))
@ -132,8 +110,7 @@ internal static class ScreamAPI
installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false); installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false);
} }
File.Move(api32_o, api32); File.Move(api32_o, api32);
installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (File.Exists(api64_o)) if (File.Exists(api64_o))
{ {
@ -143,8 +120,7 @@ internal static class ScreamAPI
installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false); installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false);
} }
File.Move(api64_o, api64); File.Move(api64_o, api64);
installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (deleteConfig && File.Exists(config)) if (deleteConfig && File.Exists(config))
{ {
@ -153,16 +129,14 @@ internal static class ScreamAPI
} }
}); });
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true)
bool generateConfig = true) => await Task.Run(() => => await Task.Run(() =>
{ {
directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
out string config);
if (File.Exists(api32) && !File.Exists(api32_o)) if (File.Exists(api32) && !File.Exists(api32_o))
{ {
File.Move(api32, api32_o); File.Move(api32, api32_o);
installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (File.Exists(api32_o)) if (File.Exists(api32_o))
{ {
@ -172,8 +146,7 @@ internal static class ScreamAPI
if (File.Exists(api64) && !File.Exists(api64_o)) if (File.Exists(api64) && !File.Exists(api64_o))
{ {
File.Move(api64, api64_o); File.Move(api64, api64_o);
installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (File.Exists(api64_o)) if (File.Exists(api64_o))
{ {

View file

@ -11,12 +11,8 @@ namespace CreamInstaller.Resources;
internal static class SmokeAPI internal static class SmokeAPI
{ {
internal static void GetSmokeApiComponents( internal static void GetSmokeApiComponents(this string directory, out string api32, out string api32_o, out string api64, out string api64_o,
this string directory, out string config, out string cache)
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config,
out string cache)
{ {
api32 = directory + @"\steam_api.dll"; api32 = directory + @"\steam_api.dll";
api32_o = directory + @"\steam_api_o.dll"; api32_o = directory + @"\steam_api_o.dll";
@ -29,18 +25,15 @@ internal static class SmokeAPI
internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null) internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null)
{ {
directory.GetSmokeApiComponents(out _, out _, out _, out _, out string config, out _); directory.GetSmokeApiComponents(out _, out _, out _, out _, out string config, out _);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> overrideDlc IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> overrideDlc = selection.AllDlc.Except(selection.SelectedDlc);
= selection.AllDlc.Except(selection.SelectedDlc); foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in
selection.ExtraSelectedDlc)
overrideDlc = overrideDlc.Except(extraDlc); overrideDlc = overrideDlc.Except(extraDlc);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> injectDlc IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> injectDlc
= new List<KeyValuePair<string, (DlcType type, string name, string icon)>>(); = new List<KeyValuePair<string, (DlcType type, string name, string icon)>>();
if (selection.AllDlc.Count > 64 || selection.ExtraDlc.Any(e => e.dlc.Count > 64)) if (selection.AllDlc.Count > 64 || selection.ExtraDlc.Any(e => e.dlc.Count > 64))
{ {
injectDlc = injectDlc.Concat(selection.SelectedDlc.Where(pair => pair.Value.type is DlcType.SteamHidden)); injectDlc = injectDlc.Concat(selection.SelectedDlc.Where(pair => pair.Value.type is DlcType.SteamHidden));
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
selection.ExtraSelectedDlc)
if (selection.ExtraDlc.Where(e => e.id == id).Single().dlc.Count > 64) if (selection.ExtraDlc.Where(e => e.id == id).Single().dlc.Count > 64)
injectDlc = injectDlc.Concat(extraDlc.Where(pair => pair.Value.type is DlcType.SteamHidden)); injectDlc = injectDlc.Concat(extraDlc.Where(pair => pair.Value.type is DlcType.SteamHidden));
} }
@ -50,27 +43,20 @@ internal static class SmokeAPI
installForm.UpdateUser("Generating SmokeAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ installForm.UpdateUser("Generating SmokeAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
File.Create(config).Close(); File.Create(config).Close();
StreamWriter writer = new(config, true, Encoding.UTF8); StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer, WriteConfig(writer, new(overrideDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
new SortedList<string, (DlcType type, string name, string icon)>( new(injectDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm);
overrideDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
new SortedList<string, (DlcType type, string name, string icon)>(
injectDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
installForm);
writer.Flush(); writer.Flush();
writer.Close(); writer.Close();
} }
else if (File.Exists(config)) else if (File.Exists(config))
{ {
File.Delete(config); File.Delete(config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
false);
} }
} }
internal static void WriteConfig(StreamWriter writer, internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> overrideDlc,
SortedList<string, (DlcType type, string name, string icon)> overrideDlc, SortedList<string, (DlcType type, string name, string icon)> injectDlc, InstallForm installForm = null)
SortedList<string, (DlcType type, string name, string icon)> injectDlc,
InstallForm installForm = null)
{ {
writer.WriteLine("{"); writer.WriteLine("{");
writer.WriteLine(" \"$version\": 1,"); writer.WriteLine(" \"$version\": 1,");
@ -86,15 +72,12 @@ internal static class SmokeAPI
string dlcId = pair.Key; string dlcId = pair.Key;
(_, string dlcName, _) = pair.Value; (_, string dlcName, _) = pair.Value;
writer.WriteLine($" {dlcId}{(pair.Equals(lastOverrideDlc) ? "" : ",")}"); writer.WriteLine($" {dlcId}{(pair.Equals(lastOverrideDlc) ? "" : ",")}");
installForm?.UpdateUser($"Added override DLC to SmokeAPI.json with appid {dlcId} ({dlcName})", installForm?.UpdateUser($"Added override DLC to SmokeAPI.json with appid {dlcId} ({dlcName})", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
writer.WriteLine(" ],"); writer.WriteLine(" ],");
} }
else else
{
writer.WriteLine(" \"override\": [],"); writer.WriteLine(" \"override\": [],");
}
if (injectDlc.Count > 0) if (injectDlc.Count > 0)
{ {
writer.WriteLine(" \"dlc_ids\": ["); writer.WriteLine(" \"dlc_ids\": [");
@ -104,15 +87,12 @@ internal static class SmokeAPI
string dlcId = pair.Key; string dlcId = pair.Key;
(_, string dlcName, _) = pair.Value; (_, string dlcName, _) = pair.Value;
writer.WriteLine($" {dlcId}{(pair.Equals(lastInjectDlc) ? "" : ",")}"); writer.WriteLine($" {dlcId}{(pair.Equals(lastInjectDlc) ? "" : ",")}");
installForm?.UpdateUser($"Added inject DLC to SmokeAPI.json with appid {dlcId} ({dlcName})", installForm?.UpdateUser($"Added inject DLC to SmokeAPI.json with appid {dlcId} ({dlcName})", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
writer.WriteLine(" ],"); writer.WriteLine(" ],");
} }
else else
{
writer.WriteLine(" \"dlc_ids\": [],"); writer.WriteLine(" \"dlc_ids\": [],");
}
writer.WriteLine(" \"auto_inject_inventory\": true,"); writer.WriteLine(" \"auto_inject_inventory\": true,");
writer.WriteLine(" \"inventory_items\": []"); writer.WriteLine(" \"inventory_items\": []");
writer.WriteLine("}"); writer.WriteLine("}");
@ -125,11 +105,9 @@ internal static class SmokeAPI
if (File.Exists(oldConfig)) if (File.Exists(oldConfig))
{ {
File.Delete(oldConfig); File.Delete(oldConfig);
installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string cache);
out string config, out string cache);
if (File.Exists(api32_o)) if (File.Exists(api32_o))
{ {
if (File.Exists(api32)) if (File.Exists(api32))
@ -138,9 +116,7 @@ internal static class SmokeAPI
installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false); installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false);
} }
File.Move(api32_o, api32); File.Move(api32_o, api32);
installForm?.UpdateUser( installForm?.UpdateUser($"Restored Steamworks: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action, false);
$"Restored Steamworks: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action,
false);
} }
if (File.Exists(api64_o)) if (File.Exists(api64_o))
{ {
@ -150,9 +126,7 @@ internal static class SmokeAPI
installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false); installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false);
} }
File.Move(api64_o, api64); File.Move(api64_o, api64);
installForm?.UpdateUser( installForm?.UpdateUser($"Restored Steamworks: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, false);
$"Restored Steamworks: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action,
false);
} }
if (deleteConfig && File.Exists(config)) if (deleteConfig && File.Exists(config))
{ {
@ -166,23 +140,20 @@ internal static class SmokeAPI
} }
}); });
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true)
bool generateConfig = true) => await Task.Run(() => => await Task.Run(() =>
{ {
directory.GetCreamApiComponents(out _, out _, out _, out _, out string oldConfig); directory.GetCreamApiComponents(out _, out _, out _, out _, out string oldConfig);
if (File.Exists(oldConfig)) if (File.Exists(oldConfig))
{ {
File.Delete(oldConfig); File.Delete(oldConfig);
installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out _);
out string config, out _);
if (File.Exists(api32) && !File.Exists(api32_o)) if (File.Exists(api32) && !File.Exists(api32_o))
{ {
File.Move(api32, api32_o); File.Move(api32, api32_o);
installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (File.Exists(api32_o)) if (File.Exists(api32_o))
{ {
@ -192,8 +163,7 @@ internal static class SmokeAPI
if (File.Exists(api64) && !File.Exists(api64_o)) if (File.Exists(api64) && !File.Exists(api64_o))
{ {
File.Move(api64, api64_o); File.Move(api64, api64_o);
installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (File.Exists(api64_o)) if (File.Exists(api64_o))
{ {

View file

@ -11,12 +11,8 @@ namespace CreamInstaller.Resources;
internal static class UplayR1 internal static class UplayR1
{ {
internal static void GetUplayR1Components( internal static void GetUplayR1Components(this string directory, out string api32, out string api32_o, out string api64, out string api64_o,
this string directory, out string config)
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config
)
{ {
api32 = directory + @"\uplay_r1_loader.dll"; api32 = directory + @"\uplay_r1_loader.dll";
api32_o = directory + @"\uplay_r1_loader_o.dll"; api32_o = directory + @"\uplay_r1_loader_o.dll";
@ -28,10 +24,8 @@ internal static class UplayR1
internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null) internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null)
{ {
directory.GetUplayR1Components(out _, out _, out _, out _, out string config); directory.GetUplayR1Components(out _, out _, out _, out _, out string config);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> blacklistDlc IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> blacklistDlc = selection.AllDlc.Except(selection.SelectedDlc);
= selection.AllDlc.Except(selection.SelectedDlc); foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in
selection.ExtraSelectedDlc)
blacklistDlc = blacklistDlc.Except(extraDlc); blacklistDlc = blacklistDlc.Except(extraDlc);
if (blacklistDlc.Any()) if (blacklistDlc.Any())
{ {
@ -39,23 +33,18 @@ internal static class UplayR1
installForm.UpdateUser("Generating Uplay R1 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ installForm.UpdateUser("Generating Uplay R1 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
File.Create(config).Close(); File.Create(config).Close();
StreamWriter writer = new(config, true, Encoding.UTF8); StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer, WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm);
new SortedList<string, (DlcType type, string name, string icon)>(
blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
installForm);
writer.Flush(); writer.Flush();
writer.Close(); writer.Close();
} }
else if (File.Exists(config)) else if (File.Exists(config))
{ {
File.Delete(config); File.Delete(config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
false);
} }
} }
internal static void WriteConfig(StreamWriter writer, internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> blacklistDlc,
SortedList<string, (DlcType type, string name, string icon)> blacklistDlc,
InstallForm installForm = null) InstallForm installForm = null)
{ {
writer.WriteLine("{"); writer.WriteLine("{");
@ -71,46 +60,38 @@ internal static class UplayR1
string dlcId = pair.Key; string dlcId = pair.Key;
(_, string dlcName, _) = pair.Value; (_, string dlcName, _) = pair.Value;
writer.WriteLine($" {dlcId}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}"); writer.WriteLine($" {dlcId}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}");
installForm?.UpdateUser($"Added blacklist DLC to UplayR1Unlocker.jsonc with appid {dlcId} ({dlcName})", installForm?.UpdateUser($"Added blacklist DLC to UplayR1Unlocker.jsonc with appid {dlcId} ({dlcName})", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
writer.WriteLine(" ],"); writer.WriteLine(" ],");
} }
else else
{
writer.WriteLine(" \"blacklist\": [],"); writer.WriteLine(" \"blacklist\": [],");
}
writer.WriteLine("}"); writer.WriteLine("}");
} }
internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true) internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true)
=> await Task.Run(() => => await Task.Run(() =>
{ {
directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
out string config);
if (File.Exists(api32_o)) if (File.Exists(api32_o))
{ {
if (File.Exists(api32)) if (File.Exists(api32))
{ {
File.Delete(api32); File.Delete(api32);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, false);
false);
} }
File.Move(api32_o, api32); File.Move(api32_o, api32);
installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (File.Exists(api64_o)) if (File.Exists(api64_o))
{ {
if (File.Exists(api64)) if (File.Exists(api64))
{ {
File.Delete(api64); File.Delete(api64);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, false);
false);
} }
File.Move(api64_o, api64); File.Move(api64_o, api64);
installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (deleteConfig && File.Exists(config)) if (deleteConfig && File.Exists(config))
{ {
@ -119,16 +100,14 @@ internal static class UplayR1
} }
}); });
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true)
bool generateConfig = true) => await Task.Run(() => => await Task.Run(() =>
{ {
directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
out string config);
if (File.Exists(api32) && !File.Exists(api32_o)) if (File.Exists(api32) && !File.Exists(api32_o))
{ {
File.Move(api32, api32_o); File.Move(api32, api32_o);
installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (File.Exists(api32_o)) if (File.Exists(api32_o))
{ {
@ -138,8 +117,7 @@ internal static class UplayR1
if (File.Exists(api64) && !File.Exists(api64_o)) if (File.Exists(api64) && !File.Exists(api64_o))
{ {
File.Move(api64, api64_o); File.Move(api64, api64_o);
installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (File.Exists(api64_o)) if (File.Exists(api64_o))
{ {

View file

@ -11,12 +11,8 @@ namespace CreamInstaller.Resources;
internal static class UplayR2 internal static class UplayR2
{ {
internal static void GetUplayR2Components( internal static void GetUplayR2Components(this string directory, out string old_api32, out string old_api64, out string api32, out string api32_o,
this string directory, out string api64, out string api64_o, out string config)
out string old_api32, out string old_api64,
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config)
{ {
old_api32 = directory + @"\uplay_r2_loader.dll"; old_api32 = directory + @"\uplay_r2_loader.dll";
old_api64 = directory + @"\uplay_r2_loader64.dll"; old_api64 = directory + @"\uplay_r2_loader64.dll";
@ -30,10 +26,8 @@ internal static class UplayR2
internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null) internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null)
{ {
directory.GetUplayR2Components(out _, out _, out _, out _, out _, out _, out string config); directory.GetUplayR2Components(out _, out _, out _, out _, out _, out _, out string config);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> blacklistDlc IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> blacklistDlc = selection.AllDlc.Except(selection.SelectedDlc);
= selection.AllDlc.Except(selection.SelectedDlc); foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in
selection.ExtraSelectedDlc)
blacklistDlc = blacklistDlc.Except(extraDlc); blacklistDlc = blacklistDlc.Except(extraDlc);
if (blacklistDlc.Any()) if (blacklistDlc.Any())
{ {
@ -41,23 +35,18 @@ internal static class UplayR2
installForm.UpdateUser("Generating Uplay R2 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ installForm.UpdateUser("Generating Uplay R2 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
File.Create(config).Close(); File.Create(config).Close();
StreamWriter writer = new(config, true, Encoding.UTF8); StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer, WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm);
new SortedList<string, (DlcType type, string name, string icon)>(
blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
installForm);
writer.Flush(); writer.Flush();
writer.Close(); writer.Close();
} }
else if (File.Exists(config)) else if (File.Exists(config))
{ {
File.Delete(config); File.Delete(config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
false);
} }
} }
internal static void WriteConfig(StreamWriter writer, internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> blacklistDlc,
SortedList<string, (DlcType type, string name, string icon)> blacklistDlc,
InstallForm installForm = null) InstallForm installForm = null)
{ {
writer.WriteLine("{"); writer.WriteLine("{");
@ -75,35 +64,30 @@ internal static class UplayR2
string dlcId = pair.Key; string dlcId = pair.Key;
(_, string dlcName, _) = pair.Value; (_, string dlcName, _) = pair.Value;
writer.WriteLine($" {dlcId}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}"); writer.WriteLine($" {dlcId}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}");
installForm?.UpdateUser($"Added blacklist DLC to UplayR2Unlocker.jsonc with appid {dlcId} ({dlcName})", installForm?.UpdateUser($"Added blacklist DLC to UplayR2Unlocker.jsonc with appid {dlcId} ({dlcName})", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
writer.WriteLine(" ],"); writer.WriteLine(" ],");
} }
else else
{
writer.WriteLine(" \"blacklist\": [],"); writer.WriteLine(" \"blacklist\": [],");
}
writer.WriteLine("}"); writer.WriteLine("}");
} }
internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true) internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true)
=> await Task.Run(() => => await Task.Run(() =>
{ {
directory.GetUplayR2Components(out string old_api32, out string old_api64, out string api32, directory.GetUplayR2Components(out string old_api32, out string old_api64, out string api32, out string api32_o, out string api64,
out string api32_o, out string api64, out string api64_o, out string config); out string api64_o, out string config);
if (File.Exists(api32_o)) if (File.Exists(api32_o))
{ {
string api = File.Exists(old_api32) ? old_api32 : api32; string api = File.Exists(old_api32) ? old_api32 : api32;
if (File.Exists(api)) if (File.Exists(api))
{ {
File.Delete(api); File.Delete(api);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false);
false);
} }
File.Move(api32_o, api); File.Move(api32_o, api);
installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api)}", installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (File.Exists(api64_o)) if (File.Exists(api64_o))
{ {
@ -111,12 +95,10 @@ internal static class UplayR2
if (File.Exists(api)) if (File.Exists(api))
{ {
File.Delete(api); File.Delete(api);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false);
false);
} }
File.Move(api64_o, api); File.Move(api64_o, api);
installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api)}", installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (deleteConfig && File.Exists(config)) if (deleteConfig && File.Exists(config))
{ {
@ -125,17 +107,16 @@ internal static class UplayR2
} }
}); });
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true)
bool generateConfig = true) => await Task.Run(() => => await Task.Run(() =>
{ {
directory.GetUplayR2Components(out string old_api32, out string old_api64, out string api32, out string api32_o, directory.GetUplayR2Components(out string old_api32, out string old_api64, out string api32, out string api32_o, out string api64,
out string api64, out string api64_o, out string config); out string api64_o, out string config);
string api = File.Exists(old_api32) ? old_api32 : api32; string api = File.Exists(old_api32) ? old_api32 : api32;
if (File.Exists(api) && !File.Exists(api32_o)) if (File.Exists(api) && !File.Exists(api32_o))
{ {
File.Move(api, api32_o); File.Move(api, api32_o);
installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api32_o)}", installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (File.Exists(api32_o)) if (File.Exists(api32_o))
{ {
@ -146,8 +127,7 @@ internal static class UplayR2
if (File.Exists(api) && !File.Exists(api64_o)) if (File.Exists(api) && !File.Exists(api64_o))
{ {
File.Move(api, api64_o); File.Move(api, api64_o);
installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api64_o)}", installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false);
LogTextBox.Action, false);
} }
if (File.Exists(api64_o)) if (File.Exists(api64_o))
{ {

View file

@ -14,8 +14,7 @@ internal static class Diagnostics
get get
{ {
notepadPlusPlusPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Notepad++", "", null) as string; notepadPlusPlusPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Notepad++", "", null) as string;
notepadPlusPlusPath notepadPlusPlusPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Notepad++", "", null) as string;
??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Notepad++", "", null) as string;
return notepadPlusPlusPath; return notepadPlusPlusPath;
} }
} }
@ -35,18 +34,13 @@ internal static class Diagnostics
OpenFileInWindowsNotepad(path); OpenFileInWindowsNotepad(path);
} }
private static void OpenFileInNotepadPlusPlus(string npp, string path) private static void OpenFileInNotepadPlusPlus(string npp, string path) => Process.Start(new ProcessStartInfo { FileName = npp, Arguments = path });
=> Process.Start(new ProcessStartInfo { FileName = npp, Arguments = path });
private static void OpenFileInWindowsNotepad(string path) private static void OpenFileInWindowsNotepad(string path) => Process.Start(new ProcessStartInfo { FileName = "notepad.exe", Arguments = path });
=> Process.Start(new ProcessStartInfo { FileName = "notepad.exe", Arguments = path });
internal static void OpenDirectoryInFileExplorer(string path) internal static void OpenDirectoryInFileExplorer(string path) => Process.Start(new ProcessStartInfo { FileName = "explorer.exe", Arguments = path });
=> Process.Start(new ProcessStartInfo { FileName = "explorer.exe", Arguments = path });
internal static void OpenUrlInInternetBrowser(string url) internal static void OpenUrlInInternetBrowser(string url) => Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true });
=> Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true });
internal static string BeautifyPath(this string path) internal static string BeautifyPath(this string path) => path is null ? null : Path.TrimEndingDirectorySeparator(Path.GetFullPath(path));
=> path is null ? null : Path.TrimEndingDirectorySeparator(Path.GetFullPath(path));
} }

View file

@ -8,8 +8,8 @@ namespace CreamInstaller.Utility;
internal static class ExceptionHandler internal static class ExceptionHandler
{ {
internal static bool HandleException(this Exception e, Form form = null, string caption = null, internal static bool HandleException(this Exception e, Form form = null, string caption = null, string acceptButtonText = "Retry",
string acceptButtonText = "Retry", string cancelButtonText = "Cancel") string cancelButtonText = "Cancel")
{ {
caption ??= Program.Name + " encountered an exception"; caption ??= Program.Name + " encountered an exception";
StringBuilder output = new(); StringBuilder output = new();
@ -32,15 +32,9 @@ internal static class ExceptionHandler
int ciNum = line.LastIndexOf(@"CreamInstaller\", StringComparison.Ordinal); int ciNum = line.LastIndexOf(@"CreamInstaller\", StringComparison.Ordinal);
int lineNum = line.LastIndexOf(":line ", StringComparison.Ordinal); int lineNum = line.LastIndexOf(":line ", StringComparison.Ordinal);
if (atNum != -1) if (atNum != -1)
_ = output.Append("\n " + (inNum != -1 ? line[atNum..(inNum - 1)] : line[atNum..]) _ = output.Append("\n " + (inNum != -1 ? line[atNum..(inNum - 1)] : line[atNum..]) + (inNum != -1
+ (inNum != -1 ? "\n " + (ciNum != -1
? "\n " ? "in " + (lineNum != -1 ? line[ciNum..lineNum] + "\n on " + line[(lineNum + 1)..] : line[ciNum..])
+ (ciNum != -1
? "in "
+ (lineNum != -1
? line[ciNum..lineNum]
+ "\n on " + line[(lineNum + 1)..]
: line[ciNum..])
: line[inNum..]) : line[inNum..])
: null)); : null));
} }
@ -49,14 +43,12 @@ internal static class ExceptionHandler
stackDepth++; stackDepth++;
} }
using DialogForm dialogForm = new(form ?? Form.ActiveForm); using DialogForm dialogForm = new(form ?? Form.ActiveForm);
return dialogForm.Show(SystemIcons.Error, output.ToString(), acceptButtonText, cancelButtonText, caption) return dialogForm.Show(SystemIcons.Error, output.ToString(), acceptButtonText, cancelButtonText, caption) == DialogResult.OK;
== DialogResult.OK;
} }
internal static void HandleFatalException(this Exception e) internal static void HandleFatalException(this Exception e)
{ {
bool? restart = e?.HandleException(caption: Program.Name + " encountered a fatal exception", bool? restart = e?.HandleException(caption: Program.Name + " encountered a fatal exception", acceptButtonText: "Restart");
acceptButtonText: "Restart");
if (restart.HasValue && restart.Value) if (restart.HasValue && restart.Value)
Application.Restart(); Application.Restart();
Application.Exit(); Application.Exit();

View file

@ -12,7 +12,7 @@ internal static class HttpClientManager
internal static void Setup() internal static void Setup()
{ {
HttpClient = new HttpClient(); HttpClient = new();
HttpClient.DefaultRequestHeaders.Add("User-Agent", $"CI{Program.Version.Replace(".", "")}"); HttpClient.DefaultRequestHeaders.Add("User-Agent", $"CI{Program.Version.Replace(".", "")}");
} }
@ -21,8 +21,7 @@ internal static class HttpClientManager
try try
{ {
using HttpRequestMessage request = new(HttpMethod.Get, url); using HttpRequestMessage request = new(HttpMethod.Get, url);
using HttpResponseMessage response using HttpResponseMessage response = await HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
= await HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
_ = response.EnsureSuccessStatusCode(); _ = response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync(); return await response.Content.ReadAsStringAsync();
} }
@ -42,8 +41,7 @@ internal static class HttpClientManager
internal static async Task<HtmlNodeCollection> GetDocumentNodes(string url, string xpath) internal static async Task<HtmlNodeCollection> GetDocumentNodes(string url, string xpath)
=> (await EnsureGet(url))?.ToHtmlDocument()?.DocumentNode?.SelectNodes(xpath); => (await EnsureGet(url))?.ToHtmlDocument()?.DocumentNode?.SelectNodes(xpath);
internal static HtmlNodeCollection GetDocumentNodes(this HtmlDocument htmlDocument, string xpath) internal static HtmlNodeCollection GetDocumentNodes(this HtmlDocument htmlDocument, string xpath) => htmlDocument.DocumentNode?.SelectNodes(xpath);
=> htmlDocument.DocumentNode?.SelectNodes(xpath);
internal static async Task<Image> GetImageFromUrl(string url) internal static async Task<Image> GetImageFromUrl(string url)
{ {

View file

@ -6,27 +6,23 @@ namespace CreamInstaller.Utility;
internal static class IconGrabber internal static class IconGrabber
{ {
internal const string SteamAppImagesPath internal const string SteamAppImagesPath = "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/";
= "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/";
internal const string GoogleFaviconsApiUrl = "https://www.google.com/s2/favicons"; internal const string GoogleFaviconsApiUrl = "https://www.google.com/s2/favicons";
internal static Icon ToIcon(this Image image) internal static Icon ToIcon(this Image image)
{ {
using Bitmap dialogIconBitmap = new(image, new Size(image.Width, image.Height)); using Bitmap dialogIconBitmap = new(image, new(image.Width, image.Height));
return Icon.FromHandle(dialogIconBitmap.GetHicon()); return Icon.FromHandle(dialogIconBitmap.GetHicon());
} }
internal static string GetDomainFaviconUrl(string domain, int size = 16) internal static string GetDomainFaviconUrl(string domain, int size = 16) => GoogleFaviconsApiUrl + $"?domain={domain}&sz={size}";
=> GoogleFaviconsApiUrl + $"?domain={domain}&sz={size}";
internal static Image GetFileIconImage(this string path) internal static Image GetFileIconImage(this string path) => File.Exists(path) ? Icon.ExtractAssociatedIcon(path)?.ToBitmap() : null;
=> File.Exists(path) ? Icon.ExtractAssociatedIcon(path)?.ToBitmap() : null;
internal static Image GetNotepadImage() => GetFileIconImage(Diagnostics.GetNotepadPath()); internal static Image GetNotepadImage() => GetFileIconImage(Diagnostics.GetNotepadPath());
internal static Image GetCommandPromptImage() => GetFileIconImage(Environment.SystemDirectory + @"\cmd.exe"); internal static Image GetCommandPromptImage() => GetFileIconImage(Environment.SystemDirectory + @"\cmd.exe");
internal static Image GetFileExplorerImage() internal static Image GetFileExplorerImage() => GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe");
=> GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe");
} }

View file

@ -18,9 +18,11 @@ internal static class LogTextBox
textBox.SelectionStart = textBox.TextLength; textBox.SelectionStart = textBox.TextLength;
textBox.SelectionLength = 0; textBox.SelectionLength = 0;
textBox.SelectionColor = color; textBox.SelectionColor = color;
if (scroll) textBox.ScrollToCaret(); if (scroll)
textBox.ScrollToCaret();
textBox.AppendText(text); textBox.AppendText(text);
if (scroll) textBox.ScrollToCaret(); if (scroll)
textBox.ScrollToCaret();
textBox.SelectionColor = textBox.ForeColor; textBox.SelectionColor = textBox.ForeColor;
textBox.Invalidate(); textBox.Invalidate();
} }

View file

@ -11,11 +11,9 @@ namespace CreamInstaller.Utility;
internal static class ProgramData internal static class ProgramData
{ {
internal static readonly string DirectoryPathOld internal static readonly string DirectoryPathOld = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller";
= Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller";
internal static readonly string DirectoryPath internal static readonly string DirectoryPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\CreamInstaller";
= Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\CreamInstaller";
internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo"; internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo";
internal static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt"; internal static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt";
@ -29,19 +27,22 @@ internal static class ProgramData
internal static readonly string DlcChoicesPath = DirectoryPath + @"\dlc.json"; internal static readonly string DlcChoicesPath = DirectoryPath + @"\dlc.json";
internal static readonly string KoaloaderProxyChoicesPath = DirectoryPath + @"\proxies.json"; internal static readonly string KoaloaderProxyChoicesPath = DirectoryPath + @"\proxies.json";
internal static async Task Setup() => await Task.Run(() => internal static async Task Setup()
=> await Task.Run(() =>
{ {
if (Directory.Exists(DirectoryPathOld)) if (Directory.Exists(DirectoryPathOld))
{ {
if (Directory.Exists(DirectoryPath)) Directory.Delete(DirectoryPath, true); if (Directory.Exists(DirectoryPath))
Directory.Delete(DirectoryPath, true);
Directory.Move(DirectoryPathOld, DirectoryPath); Directory.Move(DirectoryPathOld, DirectoryPath);
} }
if (!Directory.Exists(DirectoryPath)) _ = Directory.CreateDirectory(DirectoryPath); if (!Directory.Exists(DirectoryPath))
if (!File.Exists(AppInfoVersionPath) _ = Directory.CreateDirectory(DirectoryPath);
|| !Version.TryParse(File.ReadAllText(AppInfoVersionPath, Encoding.UTF8), out Version version) if (!File.Exists(AppInfoVersionPath) || !Version.TryParse(File.ReadAllText(AppInfoVersionPath, Encoding.UTF8), out Version version)
|| version < MinimumAppInfoVersion) || version < MinimumAppInfoVersion)
{ {
if (Directory.Exists(AppInfoPath)) Directory.Delete(AppInfoPath, true); if (Directory.Exists(AppInfoPath))
Directory.Delete(AppInfoPath, true);
_ = Directory.CreateDirectory(AppInfoPath); _ = Directory.CreateDirectory(AppInfoPath);
File.WriteAllText(AppInfoVersionPath, Program.Version, Encoding.UTF8); File.WriteAllText(AppInfoVersionPath, Program.Version, Encoding.UTF8);
} }
@ -91,16 +92,16 @@ internal static class ProgramData
internal static List<(Platform platform, string id)> ReadProgramChoices() internal static List<(Platform platform, string id)> ReadProgramChoices()
{ {
if (!File.Exists(ProgramChoicesPath)) return null; if (!File.Exists(ProgramChoicesPath))
return null;
try try
{ {
return JsonConvert.DeserializeObject(File.ReadAllText(ProgramChoicesPath), return JsonConvert.DeserializeObject(File.ReadAllText(ProgramChoicesPath), typeof(List<(Platform platform, string id)>)) as
typeof(List<(Platform platform, string id)>)) as
List<(Platform platform, string id)>; List<(Platform platform, string id)>;
} }
catch catch
{ {
return new List<(Platform platform, string id)>(); return new();
} }
} }
@ -118,16 +119,16 @@ internal static class ProgramData
internal static List<(Platform platform, string gameId, string dlcId)> ReadDlcChoices() internal static List<(Platform platform, string gameId, string dlcId)> ReadDlcChoices()
{ {
if (!File.Exists(DlcChoicesPath)) return null; if (!File.Exists(DlcChoicesPath))
return null;
try try
{ {
return JsonConvert.DeserializeObject(File.ReadAllText(DlcChoicesPath), return JsonConvert.DeserializeObject(File.ReadAllText(DlcChoicesPath), typeof(List<(Platform platform, string gameId, string dlcId)>)) as
typeof(List<(Platform platform, string gameId, string dlcId)>)) as
List<(Platform platform, string gameId, string dlcId)>; List<(Platform platform, string gameId, string dlcId)>;
} }
catch catch
{ {
return new List<(Platform platform, string gameId, string dlcId)>(); return new();
} }
} }
@ -145,22 +146,20 @@ internal static class ProgramData
internal static List<(Platform platform, string id, string proxy, bool enabled)> ReadKoaloaderChoices() internal static List<(Platform platform, string id, string proxy, bool enabled)> ReadKoaloaderChoices()
{ {
if (!File.Exists(KoaloaderProxyChoicesPath)) return null; if (!File.Exists(KoaloaderProxyChoicesPath))
return null;
try try
{ {
return JsonConvert.DeserializeObject(File.ReadAllText(KoaloaderProxyChoicesPath), return JsonConvert.DeserializeObject(File.ReadAllText(KoaloaderProxyChoicesPath),
typeof( typeof(List<(Platform platform, string id, string proxy, bool enabled)>)) as List<(Platform platform, string id, string proxy, bool enabled)>;
List<(Platform platform, string id, string proxy, bool enabled)>))
as List<(Platform platform, string id, string proxy, bool enabled)>;
} }
catch catch
{ {
return new List<(Platform platform, string id, string proxy, bool enabled)>(); return new();
} }
} }
internal static void WriteKoaloaderProxyChoices( internal static void WriteKoaloaderProxyChoices(List<(Platform platform, string id, string proxy, bool enabled)> choices)
List<(Platform platform, string id, string proxy, bool enabled)> choices)
{ {
try try
{ {