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,35 +90,31 @@ 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 imageIdentifier = "Icon_" + id;
if (images.TryGetValue(imageIdentifier, out Image image) && image is not null)
{ {
item.Image = image; (string id, string iconUrl) = imageIdentifierInfo;
} string imageIdentifier = "Icon_" + id;
else if (images.TryGetValue(imageIdentifier, out Image image) && image is not null)
{
image = await HttpClientManager.GetImageFromUrl(iconUrl);
if (image is not null)
{
images[imageIdentifier] = image;
item.Image = image; item.Image = image;
} else
else if (onFail is not null)
{ {
onFail(); image = await HttpClientManager.GetImageFromUrl(iconUrl);
if (image is not null)
{
images[imageIdentifier] = image;
item.Image = image;
}
else if (onFail is not null)
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

@ -58,46 +58,38 @@ internal class CustomForm : Form
string acidicoala = "https://github.com/acidicoala"; string acidicoala = "https://github.com/acidicoala";
string repository = $"https://github.com/{Program.RepositoryOwner}/{Program.RepositoryName}"; string repository = $"https://github.com/{Program.RepositoryOwner}/{Program.RepositoryName}";
_ = 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" + "NOTE: This program does not automatically download nor install actual DLC files for you. As the title of the program says, it's\n"
+ "\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"
+ "NOTE: This program does not automatically download nor install actual DLC files for you. As the title of the program says, it's\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"
+ "only a DLC Unlocker installer. Should the game you wish to unlock DLC for not already come with the DLCs installed (very many\n" + "the game(s) you're tinkering with; you'll usually find any answer to your problems there.\n" + "\n" + "USAGE:\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" + " 1. Choose which programs and/or games the program should scan for DLC.\n"
+ "the game(s) you're tinkering with; you'll usually find any answer to your problems there.\n" + " The program automatically gathers all installed games from Steam, Epic and Ubisoft directories.\n"
+ "\n" + " 2. Wait for the program to download and install SteamCMD (if you chose a Steam game).\n"
+ "USAGE:\n" + " 3. Wait for the program to gather and cache the chosen games' information && DLCs.\n"
+ " 1. Choose which programs and/or games the program should scan for DLC.\n" + " May take some time on the first run; depends on how many DLCs the games you chose have.\n"
+ " The program automatically gathers all installed games from Steam, Epic and Ubisoft directories.\n" + " 4. CAREFULLY select which games' DLCs you wish to unlock.\n"
+ " 2. Wait for the program to download and install SteamCMD (if you chose a Steam game).\n" + " Obviously none of the DLC unlockers are tested for every single game!\n"
+ " 3. Wait for the program to gather and cache the chosen games' information && DLCs.\n" + " 5. Choose whether or not to install with Koaloader, and if so then also pick the proxy DLL to use.\n"
+ " May take some time on the first run; depends on how many DLCs the games you chose have.\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"
+ " 4. CAREFULLY select which games' DLCs you wish to unlock.\n" + " 6. Click the \"Generate and Install\" button.\n" + " 7. Click the \"OK\" button to close the program.\n"
+ " Obviously none of the DLC unlockers are tested for every single game!\n" + " 8. If any of the DLC unlockers cause problems with any of the games you installed them on, simply go back\n"
+ " 5. Choose whether or not to install with Koaloader, and if so then also pick the proxy DLL to use.\n" + " to step 5 and select what games you wish you revert changes to, and instead click the \"Uninstall\" button this time.\n" + "\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" + $"For reliable and quick assistance, all bugs, crashes and other issues should be referred to the [GitHub Issues]({repository}/issues) page!\n"
+ " 6. Click the \"Generate and Install\" button.\n" + "\n" + "SteamCMD installation and appinfo cache can be found at [C:\\ProgramData\\CreamInstaller]().\n"
+ " 7. Click the \"OK\" button to close the program.\n" + $"The program automatically and very quickly updates from [GitHub]({repository}) using [Onova](https://github.com/Tyrrrz/Onova). (updates can be ignored)\n"
+ " 8. If any of the DLC unlockers cause problems with any of the games you installed them on, simply go back\n" + $"The program source and other information can be found on [GitHub]({repository}).");
+ " to step 5 and select what games you wish you revert changes to, and instead click the \"Uninstall\" button this time.\n"
+ "\n"
+ $"For reliable and quick assistance, all bugs, crashes and other issues should be referred to the [GitHub Issues]({repository}/issues) page!\n"
+ "\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 source and other information can be found on [GitHub]({repository}).");
} }
internal void OnActivation(object sender, EventArgs args) => Activate(); internal void OnActivation(object sender, EventArgs args) => Activate();
[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 TextFormatFlags.Default);
? ColorTranslator.FromHtml("#006900")
: ColorTranslator.FromHtml("#69AA69"),
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,13 +201,11 @@ 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(out string proxyName, out _);
p.GetProxyInfoFromIdentifier( return proxyName;
out string proxyName, out _); }).Distinct().ToList();
return proxyName;
}).Distinct().ToList();
comboBoxDropDown ??= new ToolStripDropDown(); comboBoxDropDown ??= new ToolStripDropDown();
comboBoxDropDown.ShowItemToolTips = false; comboBoxDropDown.ShowItemToolTips = false;
comboBoxDropDown.Items.Clear(); comboBoxDropDown.Items.Clear();
@ -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,18 +107,19 @@ 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);
else else
@ -130,15 +128,12 @@ 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);
else else
@ -147,29 +142,25 @@ 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);
else else
@ -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,9 +154,8 @@ 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();
return; return;
@ -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);

File diff suppressed because it is too large Load diff

View file

@ -17,48 +17,41 @@ 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();
string manifests = EpicManifestsPath;
if (!Directory.Exists(manifests)) return games;
foreach (string file in Directory.EnumerateFiles(manifests, "*.item"))
{ {
if (Program.Canceled) return games; List<Manifest> games = new();
string json = File.ReadAllText(file); string manifests = EpicManifestsPath;
try if (!Directory.Exists(manifests))
return games;
foreach (string file in Directory.EnumerateFiles(manifests, "*.item"))
{ {
Manifest manifest = JsonSerializer.Deserialize<Manifest>(json); if (Program.Canceled)
if (manifest is not null && manifest.CatalogItemId == manifest.MainGameCatalogItemId return games;
&& !games.Any(g => g.CatalogItemId == manifest.CatalogItemId string json = File.ReadAllText(file);
&& g.InstallLocation == manifest.InstallLocation)) try
games.Add(manifest); {
Manifest manifest = JsonSerializer.Deserialize<Manifest>(json);
if (manifest is not null && manifest.CatalogItemId == manifest.MainGameCatalogItemId && !games.Any(g
=> g.CatalogItemId == manifest.CatalogItemId && g.InstallLocation == manifest.InstallLocation))
games.Add(manifest);
}
catch { }
;
} }
catch { } return games;
; });
}
return games;
});
} }

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));
} }
} }
} }
@ -83,10 +77,9 @@ 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,84 +37,86 @@ 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)
? $@"@ShutdownOnFailedCommand 0 +force_install_dir {DirectoryPath} +login anonymous +app_info_print {appId} " => AttemptCount.TryGetValue(appId, out int attempts)
+ string.Concat(Enumerable.Repeat("+app_update 4 ", attempts)) + "+quit" ? $@"@ShutdownOnFailedCommand 0 +force_install_dir {DirectoryPath} +login anonymous +app_info_print {appId} "
: $"+login anonymous +app_info_print {appId} +quit"; + string.Concat(Enumerable.Repeat("+app_update 4 ", attempts)) + "+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:
if (Program.Canceled) return "";
for (int i = 0; i < locks.Length; i++)
{ {
if (Program.Canceled) return ""; wait_for_lock:
if (Interlocked.CompareExchange(ref locks[i], 1, 0) == 0) if (Program.Canceled)
return "";
for (int i = 0; i < locks.Length; i++)
{ {
if (appId is not null) if (Program.Canceled)
{ return "";
AttemptCount.TryGetValue(appId, out int count); if (Interlocked.CompareExchange(ref locks[i], 1, 0) == 0)
AttemptCount[appId] = ++count;
}
if (Program.Canceled) return "";
ProcessStartInfo processStartInfo = new()
{
FileName = FilePath, RedirectStandardOutput = true, RedirectStandardInput = true,
RedirectStandardError = true, UseShellExecute = false,
Arguments = appId is null ? "+quit" : GetArguments(appId), CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8
};
Process process = Process.Start(processStartInfo);
StringBuilder output = new();
StringBuilder appInfo = new();
bool appInfoStarted = false;
DateTime lastOutput = DateTime.UtcNow;
while (true)
{ {
if (appId is not null)
{
AttemptCount.TryGetValue(appId, out int count);
AttemptCount[appId] = ++count;
}
if (Program.Canceled) if (Program.Canceled)
return "";
ProcessStartInfo processStartInfo = new()
{ {
process.Kill(true); FileName = FilePath, RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true,
process.Close(); UseShellExecute = false, Arguments = appId is null ? "+quit" : GetArguments(appId), CreateNoWindow = true,
break; StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8
} };
int c = process.StandardOutput.Read(); Process process = Process.Start(processStartInfo);
if (c != -1) StringBuilder output = new();
StringBuilder appInfo = new();
bool appInfoStarted = false;
DateTime lastOutput = DateTime.UtcNow;
while (true)
{ {
lastOutput = DateTime.UtcNow; if (Program.Canceled)
char ch = (char)c;
if (ch == '{') appInfoStarted = true;
_ = appInfoStarted ? appInfo.Append(ch) : output.Append(ch);
}
DateTime now = DateTime.UtcNow;
TimeSpan timeDiff = now - lastOutput;
if (timeDiff.TotalSeconds > 0.1)
{
process.Kill(true);
process.Close();
if (output.ToString().Contains($"No app info for AppID {appId} found, requesting..."))
{
AttemptCount[appId]++;
processStartInfo.Arguments = GetArguments(appId);
process = Process.Start(processStartInfo);
appInfoStarted = false;
_ = output.Clear();
_ = appInfo.Clear();
}
else
{ {
process.Kill(true);
process.Close();
break; break;
} }
int c = process.StandardOutput.Read();
if (c != -1)
{
lastOutput = DateTime.UtcNow;
char ch = (char)c;
if (ch == '{')
appInfoStarted = true;
_ = appInfoStarted ? appInfo.Append(ch) : output.Append(ch);
}
DateTime now = DateTime.UtcNow;
TimeSpan timeDiff = now - lastOutput;
if (timeDiff.TotalSeconds > 0.1)
{
process.Kill(true);
process.Close();
if (output.ToString().Contains($"No app info for AppID {appId} found, requesting..."))
{
AttemptCount[appId]++;
processStartInfo.Arguments = GetArguments(appId);
process = Process.Start(processStartInfo);
appInfoStarted = false;
_ = output.Clear();
_ = appInfo.Clear();
}
else
break;
}
} }
_ = Interlocked.Decrement(ref locks[i]);
return appInfo.ToString();
} }
_ = Interlocked.Decrement(ref locks[i]); Thread.Sleep(200);
return appInfo.ToString();
} }
Thread.Sleep(200); Thread.Sleep(200);
} goto wait_for_lock;
Thread.Sleep(200); });
goto wait_for_lock;
});
internal static async Task Setup(IProgress<int> progress) internal static async Task Setup(IProgress<int> progress)
{ {
@ -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,45 +146,47 @@ 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;
await Kill();
try
{ {
if (Directory.Exists(ConfigPath)) if (!Directory.Exists(DirectoryPath))
foreach (string file in Directory.EnumerateFiles(ConfigPath, "*.tmp")) return;
await Kill();
try
{
if (Directory.Exists(ConfigPath))
foreach (string file in Directory.EnumerateFiles(ConfigPath, "*.tmp"))
File.Delete(file);
foreach (string file in Directory.EnumerateFiles(DirectoryPath, "*.old"))
File.Delete(file); File.Delete(file);
foreach (string file in Directory.EnumerateFiles(DirectoryPath, "*.old")) foreach (string file in Directory.EnumerateFiles(DirectoryPath, "*.delete"))
File.Delete(file); File.Delete(file);
foreach (string file in Directory.EnumerateFiles(DirectoryPath, "*.delete")) foreach (string file in Directory.EnumerateFiles(DirectoryPath, "*.crash"))
File.Delete(file); File.Delete(file);
foreach (string file in Directory.EnumerateFiles(DirectoryPath, "*.crash")) foreach (string file in Directory.EnumerateFiles(DirectoryPath, "*.ntfs_transaction_failed"))
File.Delete(file); File.Delete(file);
foreach (string file in Directory.EnumerateFiles(DirectoryPath, "*.ntfs_transaction_failed")) if (Directory.Exists(AppCachePath))
File.Delete(file); Directory.Delete(AppCachePath, true); // this is definitely needed, so SteamCMD gets the latest information for us
if (Directory.Exists(AppCachePath)) if (Directory.Exists(DumpsPath))
Directory.Delete(AppCachePath, Directory.Delete(DumpsPath, true);
true); // this is definitely needed, so SteamCMD gets the latest information for us if (Directory.Exists(LogsPath))
if (Directory.Exists(DumpsPath)) Directory.Delete(LogsPath, true);
Directory.Delete(DumpsPath, true); if (Directory.Exists(SteamAppsPath))
if (Directory.Exists(LogsPath)) Directory.Delete(SteamAppsPath, true); // this is just a useless folder created from +app_update 4
Directory.Delete(LogsPath, true); }
if (Directory.Exists(SteamAppsPath)) catch { }
Directory.Delete(SteamAppsPath, true); // this is just a useless folder created from +app_update 4 });
}
catch { }
});
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,74 +214,75 @@ internal static class SteamCMD
} }
} }
else else
{
goto restart; goto restart;
}
} }
if (Program.Canceled) return null; if (Program.Canceled)
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(); {
if (Program.Canceled || appInfo is null) return dlcIds; List<string> dlcIds = new();
VToken extended = appInfo.Value.GetChild("extended"); if (Program.Canceled || appInfo is null)
if (extended is not null) return dlcIds;
foreach (VToken vToken in extended.Where(p => p is VProperty { Key: "listofdlc" })) VToken extended = appInfo.Value.GetChild("extended");
if (extended is not null)
foreach (VToken vToken in extended.Where(p => p is VProperty { Key: "listofdlc" }))
{
VProperty property = (VProperty)vToken;
foreach (string id in property.Value.ToString().Split(","))
if (int.TryParse(id, out int appId) && appId > 0 && !dlcIds.Contains("" + appId))
dlcIds.Add("" + appId);
}
VToken depots = appInfo.Value.GetChild("depots");
if (depots is null)
return dlcIds;
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;
foreach (string id in property.Value.ToString().Split(",")) if (int.TryParse(property.Value.GetChild("dlcappid")?.ToString(), out int appId) && appId > 0 && !dlcIds.Contains("" + appId))
if (int.TryParse(id, out int appId) && appId > 0 && !dlcIds.Contains("" + appId)) dlcIds.Add("" + appId);
dlcIds.Add("" + appId);
} }
VToken depots = appInfo.Value.GetChild("depots");
if (depots is null)
return dlcIds; return dlcIds;
foreach (VToken vToken in depots.Where(p => p is VProperty property && int.TryParse(property.Key, out int _))) });
{
VProperty property = (VProperty)vToken;
if (int.TryParse(property.Value.GetChild("dlcappid")?.ToString(), out int appId) && appId > 0
&& !dlcIds.Contains("" + appId))
dlcIds.Add("" + appId);
}
return dlcIds;
});
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 {
{ process.Kill(true);
process.Kill(true); process.WaitForExit();
process.WaitForExit(); process.Close();
process.Close(); }
} catch { }
catch { } })).ToList();
})) foreach (Task task in tasks)
.ToList(); await task;
foreach (Task task in tasks) await task;
} }
internal static void Dispose() internal static void Dispose()

View file

@ -18,93 +18,96 @@ 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> gameLibraryDirectories = await GetLibraryDirectories();
foreach (string libraryDirectory in gameLibraryDirectories)
{ {
if (Program.Canceled) return games; List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new();
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) game in List<string> gameLibraryDirectories = await GetLibraryDirectories();
(await GetGamesFromLibraryDirectory(libraryDirectory)) foreach (string libraryDirectory in gameLibraryDirectories)
.Where(game => !games.Any(_game => _game.appId == game.appId
&& _game.gameDirectory == game.gameDirectory)))
games.Add(game);
}
return games;
});
internal static async Task<List<(string appId, string name, string branch, int buildId, string gameDirectory)>>
GetGamesFromLibraryDirectory(string libraryDirectory) => await Task.Run(() =>
{
List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new();
if (Program.Canceled || !Directory.Exists(libraryDirectory)) return games;
foreach (string file in Directory.EnumerateFiles(libraryDirectory, "*.acf"))
{
if (Program.Canceled) return games;
if (ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty result))
{ {
string appId = result.Value.GetChild("appid")?.ToString(); if (Program.Canceled)
string installdir = result.Value.GetChild("installdir")?.ToString(); return games;
string name = result.Value.GetChild("name")?.ToString(); foreach ((string appId, string name, string branch, int buildId, string gameDirectory) game in
string buildId = result.Value.GetChild("buildid")?.ToString(); (await GetGamesFromLibraryDirectory(libraryDirectory)).Where(game
if (string.IsNullOrWhiteSpace(appId) => !games.Any(_game => _game.appId == game.appId && _game.gameDirectory == game.gameDirectory)))
|| string.IsNullOrWhiteSpace(installdir) games.Add(game);
|| string.IsNullOrWhiteSpace(name)
|| string.IsNullOrWhiteSpace(buildId))
continue;
string gameDirectory = (libraryDirectory + @"\common\" + installdir).BeautifyPath();
if (games.Any(g => g.appId == appId && g.gameDirectory == gameDirectory)) 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();
if (string.IsNullOrWhiteSpace(branch)) branch = "public";
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 appId, string name, string branch, int buildId, string gameDirectory)>>
{ GetGamesFromLibraryDirectory(string libraryDirectory)
List<string> gameDirectories = new(); => await Task.Run(() =>
if (Program.Canceled) return gameDirectories;
string steamInstallPath = InstallPath;
if (steamInstallPath != null && Directory.Exists(steamInstallPath))
{ {
string libraryFolder = steamInstallPath + @"\steamapps"; List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new();
if (Directory.Exists(libraryFolder)) if (Program.Canceled || !Directory.Exists(libraryDirectory))
return games;
foreach (string file in Directory.EnumerateFiles(libraryDirectory, "*.acf"))
{ {
gameDirectories.Add(libraryFolder); if (Program.Canceled)
string libraryFolders = libraryFolder + @"\libraryfolders.vdf"; return games;
if (File.Exists(libraryFolders) if (ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty result))
&& ValveDataFile.TryDeserialize(File.ReadAllText(libraryFolders, Encoding.UTF8), out VProperty result)) {
string appId = result.Value.GetChild("appid")?.ToString();
string installdir = result.Value.GetChild("installdir")?.ToString();
string name = result.Value.GetChild("name")?.ToString();
string buildId = result.Value.GetChild("buildid")?.ToString();
if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(installdir) || string.IsNullOrWhiteSpace(name)
|| string.IsNullOrWhiteSpace(buildId))
continue;
string gameDirectory = (libraryDirectory + @"\common\" + installdir).BeautifyPath();
if (games.Any(g => g.appId == appId && g.gameDirectory == gameDirectory))
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();
if (string.IsNullOrWhiteSpace(branch))
branch = "public";
games.Add((appId, name, branch, buildIdInt, gameDirectory));
}
}
return games;
});
internal static async Task<List<string>> GetLibraryDirectories()
=> await Task.Run(() =>
{
List<string> gameDirectories = new();
if (Program.Canceled)
return gameDirectories;
string steamInstallPath = InstallPath;
if (steamInstallPath != null && Directory.Exists(steamInstallPath))
{
string libraryFolder = steamInstallPath + @"\steamapps";
if (Directory.Exists(libraryFolder))
{
gameDirectories.Add(libraryFolder);
string libraryFolders = libraryFolder + @"\libraryfolders.vdf";
if (File.Exists(libraryFolders) && 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))
if (string.IsNullOrWhiteSpace(path)) continue; 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);
} }
#pragma warning restore IDE0220 // Add explicit cast #pragma warning restore IDE0220 // Add explicit cast
}
} }
} return gameDirectories;
return gameDirectories; });
});
} }

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(); {
if (appData.dlc is null) return dlcIds; List<string> dlcIds = new();
dlcIds.AddRange(from appId in appData.dlc where appId > 0 select appId.ToString()); if (appData.dlc is null)
return dlcIds; return dlcIds;
}); dlcIds.AddRange(from appId in appData.dlc where appId > 0 select appId.ToString());
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!" + "Retry", "Cancel") == DialogResult.OK)
"\n\nPlease close the program/game to continue . . . ",
"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,235 +118,216 @@ 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);
foreach (string proxyPath in proxies.Where(
proxyPath => File.Exists(proxyPath) && proxyPath.IsResourceFile(ResourceIdentifier.Koaloader)))
{ {
File.Delete(proxyPath); directory.GetKoaloaderComponents(out List<string> proxies, out string config);
installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(proxyPath)}", LogTextBox.Action, false); foreach (string proxyPath in proxies.Where(proxyPath => File.Exists(proxyPath) && proxyPath.IsResourceFile(ResourceIdentifier.Koaloader)))
} {
foreach ((string unlocker, string path) in AutoLoadDlls File.Delete(proxyPath);
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll)) installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(proxyPath)}", LogTextBox.Action, false);
.Where(pair => File.Exists(pair.path) && pair.path.IsResourceFile())) }
{ foreach ((string unlocker, string path) in AutoLoadDlls.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
File.Delete(path); .Where(pair => File.Exists(pair.path) && pair.path.IsResourceFile()))
installForm?.UpdateUser($"Deleted {unlocker}: {Path.GetFileName(path)}", LogTextBox.Action, false); {
} File.Delete(path);
if (deleteConfig && File.Exists(config)) installForm?.UpdateUser($"Deleted {unlocker}: {Path.GetFileName(path)}", LogTextBox.Action, false);
{ }
File.Delete(config); if (deleteConfig && File.Exists(config))
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); {
} File.Delete(config);
await SmokeAPI.Uninstall(directory, installForm, deleteConfig); installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
await ScreamAPI.Uninstall(directory, installForm, deleteConfig); }
await UplayR1.Uninstall(directory, installForm, deleteConfig); await SmokeAPI.Uninstall(directory, installForm, deleteConfig);
await UplayR2.Uninstall(directory, installForm, deleteConfig); await ScreamAPI.Uninstall(directory, installForm, deleteConfig);
if (rootDirectory is not null && directory != rootDirectory) await UplayR1.Uninstall(directory, installForm, deleteConfig);
await Uninstall(rootDirectory, null, installForm, deleteConfig); await UplayR2.Uninstall(directory, installForm, deleteConfig);
}); if (rootDirectory is not null && directory != rootDirectory)
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);
string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy;
string path = directory + @"\" + proxy + ".dll";
foreach (string _path in proxies.Where(p => p != path && File.Exists(p)
&& p.IsResourceFile(ResourceIdentifier.Koaloader)))
{ {
File.Delete(_path); directory.GetKoaloaderComponents(out List<string> proxies, out string config);
installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(_path)}", LogTextBox.Action, false); string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy;
} string path = directory + @"\" + proxy + ".dll";
if (File.Exists(path) && !path.IsResourceFile(ResourceIdentifier.Koaloader)) foreach (string _path in proxies.Where(p => p != path && File.Exists(p) && p.IsResourceFile(ResourceIdentifier.Koaloader)))
throw new CustomMessageException("A non-Koaloader DLL named " + proxy
+ ".dll already exists in this directory!");
path.WriteProxy(proxy, binaryType);
installForm?.UpdateUser(
$"Wrote {(binaryType == BinaryType.BIT32 ? "32-bit" : "64-bit")} Koaloader: {Path.GetFileName(path)}",
LogTextBox.Action, false);
bool bit32 = false, bit64 = false;
foreach (string executable in Directory.EnumerateFiles(directory, "*.exe"))
if (executable.TryGetFileBinaryType(out BinaryType binaryType))
{ {
if (binaryType == BinaryType.BIT32) File.Delete(_path);
bit32 = true; installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(_path)}", LogTextBox.Action, false);
else if (binaryType == BinaryType.BIT64)
bit64 = true;
if (bit32 && bit64)
break;
} }
if (selection.Platform is Platform.Steam or Platform.Paradox) if (File.Exists(path) && !path.IsResourceFile(ResourceIdentifier.Koaloader))
{ throw new CustomMessageException("A non-Koaloader DLL named " + proxy + ".dll already exists in this directory!");
if (bit32) path.WriteProxy(proxy, binaryType);
{ installForm?.UpdateUser($"Wrote {(binaryType == BinaryType.BIT32 ? "32-bit" : "64-bit")} Koaloader: {Path.GetFileName(path)}", LogTextBox.Action,
path = directory + @"\SmokeAPI32.dll"; false);
if (rootDirectory is not null && directory != rootDirectory) bool bit32 = false, bit64 = false;
foreach (string executable in Directory.EnumerateFiles(directory, "*.exe"))
if (executable.TryGetFileBinaryType(out BinaryType binaryType))
{ {
if (File.Exists(path)) if (binaryType == BinaryType.BIT32)
{ bit32 = true;
File.Delete(path); else if (binaryType == BinaryType.BIT64)
installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", bit64 = true;
LogTextBox.Action, false); if (bit32 && bit64)
} break;
path = rootDirectory + @"\SmokeAPI32.dll";
} }
"SmokeAPI.steam_api.dll".Write(path); if (selection.Platform is Platform.Steam or Platform.Paradox)
installForm?.UpdateUser(
$"Wrote SmokeAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
if (bit64)
{ {
path = directory + @"\SmokeAPI64.dll"; if (bit32)
if (rootDirectory is not null && directory != rootDirectory)
{ {
if (File.Exists(path)) path = directory + @"\SmokeAPI32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
File.Delete(path); if (File.Exists(path))
installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", {
LogTextBox.Action, false); File.Delete(path);
installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
path = rootDirectory + @"\SmokeAPI32.dll";
} }
path = rootDirectory + @"\SmokeAPI64.dll"; "SmokeAPI.steam_api.dll".Write(path);
installForm?.UpdateUser(
$"Wrote SmokeAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
"SmokeAPI.steam_api64.dll".Write(path); if (bit64)
installForm?.UpdateUser( {
$"Wrote SmokeAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}", path = directory + @"\SmokeAPI64.dll";
LogTextBox.Action, false); if (rootDirectory is not null && directory != rootDirectory)
{
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
path = rootDirectory + @"\SmokeAPI64.dll";
}
"SmokeAPI.steam_api64.dll".Write(path);
installForm?.UpdateUser(
$"Wrote SmokeAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
SmokeAPI.CheckConfig(rootDirectory ?? directory, selection, installForm);
} }
SmokeAPI.CheckConfig(rootDirectory ?? directory, selection, installForm); if (selection.Platform is Platform.Epic or Platform.Paradox)
}
if (selection.Platform is Platform.Epic or Platform.Paradox)
{
if (bit32)
{ {
path = directory + @"\ScreamAPI32.dll"; if (bit32)
if (rootDirectory is not null && directory != rootDirectory)
{ {
if (File.Exists(path)) path = directory + @"\ScreamAPI32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
File.Delete(path); if (File.Exists(path))
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", {
LogTextBox.Action, false); File.Delete(path);
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
path = rootDirectory + @"\ScreamAPI32.dll";
} }
path = rootDirectory + @"\ScreamAPI32.dll"; "ScreamAPI.EOSSDK-Win32-Shipping.dll".Write(path);
installForm?.UpdateUser(
$"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
"ScreamAPI.EOSSDK-Win32-Shipping.dll".Write(path); if (bit64)
installForm?.UpdateUser( {
$"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}", path = directory + @"\ScreamAPI64.dll";
LogTextBox.Action, false); if (rootDirectory is not null && directory != rootDirectory)
{
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
path = rootDirectory + @"\ScreamAPI64.dll";
}
"ScreamAPI.EOSSDK-Win64-Shipping.dll".Write(path);
installForm?.UpdateUser(
$"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
ScreamAPI.CheckConfig(rootDirectory ?? directory, selection, installForm);
} }
if (bit64) if (selection.Platform is Platform.Ubisoft)
{ {
path = directory + @"\ScreamAPI64.dll"; if (bit32)
if (rootDirectory is not null && directory != rootDirectory)
{ {
if (File.Exists(path)) path = directory + @"\UplayR1Unlocker32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
File.Delete(path); if (File.Exists(path))
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", {
LogTextBox.Action, false); File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
path = rootDirectory + @"\UplayR1Unlocker32.dll";
} }
path = rootDirectory + @"\ScreamAPI64.dll"; "UplayR1.uplay_r1_loader.dll".Write(path);
installForm?.UpdateUser(
$"Wrote Uplay R1 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
"ScreamAPI.EOSSDK-Win64-Shipping.dll".Write(path); if (bit64)
installForm?.UpdateUser(
$"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
ScreamAPI.CheckConfig(rootDirectory ?? directory, selection, installForm);
}
if (selection.Platform is Platform.Ubisoft)
{
if (bit32)
{
path = directory + @"\UplayR1Unlocker32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
if (File.Exists(path)) path = directory + @"\UplayR1Unlocker64.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
File.Delete(path); if (File.Exists(path))
installForm?.UpdateUser( {
$"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", File.Delete(path);
LogTextBox.Action, false); installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
path = rootDirectory + @"\UplayR1Unlocker64.dll";
} }
path = rootDirectory + @"\UplayR1Unlocker32.dll"; "UplayR1.uplay_r1_loader64.dll".Write(path);
installForm?.UpdateUser(
$"Wrote Uplay R1 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
"UplayR1.uplay_r1_loader.dll".Write(path); UplayR1.CheckConfig(rootDirectory ?? directory, selection, installForm);
installForm?.UpdateUser( if (bit32)
$"Wrote Uplay R1 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
if (bit64)
{
path = directory + @"\UplayR1Unlocker64.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
if (File.Exists(path)) path = directory + @"\UplayR2Unlocker32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
File.Delete(path); if (File.Exists(path))
installForm?.UpdateUser( {
$"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", File.Delete(path);
LogTextBox.Action, false); installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
path = rootDirectory + @"\UplayR2Unlocker32.dll";
} }
path = rootDirectory + @"\UplayR1Unlocker64.dll"; "UplayR2.upc_r2_loader.dll".Write(path);
installForm?.UpdateUser(
$"Wrote Uplay R2 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
"UplayR1.uplay_r1_loader64.dll".Write(path); if (bit64)
installForm?.UpdateUser(
$"Wrote Uplay R1 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
UplayR1.CheckConfig(rootDirectory ?? directory, selection, installForm);
if (bit32)
{
path = directory + @"\UplayR2Unlocker32.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
if (File.Exists(path)) path = directory + @"\UplayR2Unlocker64.dll";
if (rootDirectory is not null && directory != rootDirectory)
{ {
File.Delete(path); if (File.Exists(path))
installForm?.UpdateUser( {
$"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", File.Delete(path);
LogTextBox.Action, false); installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
path = rootDirectory + @"\UplayR2Unlocker64.dll";
} }
path = rootDirectory + @"\UplayR2Unlocker32.dll"; "UplayR2.upc_r2_loader64.dll".Write(path);
installForm?.UpdateUser(
$"Wrote Uplay R2 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
"UplayR2.upc_r2_loader.dll".Write(path); UplayR2.CheckConfig(rootDirectory ?? directory, selection, installForm);
installForm?.UpdateUser(
$"Wrote Uplay R2 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
} }
if (bit64) if (generateConfig)
{ CheckConfig(directory, selection, installForm);
path = directory + @"\UplayR2Unlocker64.dll"; });
if (rootDirectory is not null && directory != rootDirectory)
{
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser(
$"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
path = rootDirectory + @"\UplayR2Unlocker64.dll";
}
"UplayR2.upc_r2_loader64.dll".Write(path);
installForm?.UpdateUser(
$"Wrote Uplay R2 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
UplayR2.CheckConfig(rootDirectory ?? directory, selection, installForm);
}
if (generateConfig)
CheckConfig(directory, selection, installForm);
});
} }

View file

@ -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,91 +422,62 @@ 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();
if (Program.Canceled || !Directory.Exists(gameDirectory)) return null;
foreach (string directory in Directory
.EnumerateDirectories(gameDirectory, "*",
new EnumerationOptions { RecurseSubdirectories = true })
.Append(gameDirectory))
{ {
if (Program.Canceled) return null; List<string> dllDirectories = new();
string subDirectory = directory.BeautifyPath(); if (Program.Canceled || !Directory.Exists(gameDirectory))
if (!dllDirectories.Contains(subDirectory)) return null;
foreach (string directory in Directory.EnumerateDirectories(gameDirectory, "*", new EnumerationOptions { RecurseSubdirectories = true })
.Append(gameDirectory))
{ {
bool koaloaderInstalled = Koaloader.AutoLoadDlls if (Program.Canceled)
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll)) return null;
.Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile()); string subDirectory = directory.BeautifyPath();
if (platform is Platform.Steam or Platform.Paradox) if (!dllDirectories.Contains(subDirectory))
{ {
subDirectory.GetSmokeApiComponents(out string api, out string api_o, out string api64, bool koaloaderInstalled = Koaloader.AutoLoadDlls.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
out string api64_o, out string config, out string cache); .Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile());
if (File.Exists(api) if (platform is Platform.Steam or Platform.Paradox)
|| File.Exists(api_o) {
|| File.Exists(api64) subDirectory.GetSmokeApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string config,
|| File.Exists(api64_o) out string cache);
|| (File.Exists(config) && !koaloaderInstalled) if (File.Exists(api) || File.Exists(api_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled
|| (File.Exists(cache) && !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) dllDirectories.Add(subDirectory);
|| File.Exists(api64) }
|| File.Exists(api64_o) if (platform is Platform.Ubisoft)
|| (File.Exists(config) && !koaloaderInstalled)) {
dllDirectories.Add(subDirectory); subDirectory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
} if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o)
if (platform is Platform.Ubisoft) || File.Exists(config) && !koaloaderInstalled)
{ dllDirectories.Add(subDirectory);
subDirectory.GetUplayR1Components(out string api32, out string api32_o, out string api64, subDirectory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o,
out string api64_o, out string config); out config);
if (File.Exists(api32) if (File.Exists(old_api32) || File.Exists(old_api64) || File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64)
|| File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) && !koaloaderInstalled)
|| File.Exists(api64) dllDirectories.Add(subDirectory);
|| File.Exists(api64_o) }
|| (File.Exists(config) && !koaloaderInstalled))
dllDirectories.Add(subDirectory);
subDirectory.GetUplayR2Components(out string old_api32, out string old_api64, out api32,
out api32_o, out api64, out api64_o, out config);
if (File.Exists(old_api32)
|| File.Exists(old_api64)
|| File.Exists(api32)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled))
dllDirectories.Add(subDirectory);
} }
} }
} return !dllDirectories.Any() ? null : dllDirectories;
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,34 +129,31 @@ 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,
out string config);
if (File.Exists(api32) && !File.Exists(api32_o))
{ {
File.Move(api32, api32_o); directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", if (File.Exists(api32) && !File.Exists(api32_o))
LogTextBox.Action, false); {
} File.Move(api32, api32_o);
if (File.Exists(api32_o)) installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false);
{ }
"ScreamAPI.EOSSDK-Win32-Shipping.dll".Write(api32); if (File.Exists(api32_o))
installForm?.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false); {
} "ScreamAPI.EOSSDK-Win32-Shipping.dll".Write(api32);
if (File.Exists(api64) && !File.Exists(api64_o)) installForm?.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false);
{ }
File.Move(api64, api64_o); if (File.Exists(api64) && !File.Exists(api64_o))
installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", {
LogTextBox.Action, false); File.Move(api64, api64_o);
} installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false);
if (File.Exists(api64_o)) }
{ if (File.Exists(api64_o))
"ScreamAPI.EOSSDK-Win64-Shipping.dll".Write(api64); {
installForm?.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false); "ScreamAPI.EOSSDK-Win64-Shipping.dll".Write(api64);
} installForm?.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false);
if (generateConfig) }
CheckConfig(directory, selection, installForm); if (generateConfig)
}); CheckConfig(directory, selection, installForm);
});
} }

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,41 +140,37 @@ 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);
if (File.Exists(oldConfig))
{ {
File.Delete(oldConfig); directory.GetCreamApiComponents(out _, out _, out _, out _, out string oldConfig);
installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", if (File.Exists(oldConfig))
LogTextBox.Action, false); {
} File.Delete(oldConfig);
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", LogTextBox.Action, false);
out string config, out _); }
if (File.Exists(api32) && !File.Exists(api32_o)) directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out _);
{ if (File.Exists(api32) && !File.Exists(api32_o))
File.Move(api32, api32_o); {
installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", File.Move(api32, api32_o);
LogTextBox.Action, false); installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false);
} }
if (File.Exists(api32_o)) if (File.Exists(api32_o))
{ {
"SmokeAPI.steam_api.dll".Write(api32); "SmokeAPI.steam_api.dll".Write(api32);
installForm?.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false); installForm?.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false);
} }
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)) {
{ "SmokeAPI.steam_api64.dll".Write(api64);
"SmokeAPI.steam_api64.dll".Write(api64); installForm?.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false);
installForm?.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false); }
} if (generateConfig)
if (generateConfig) CheckConfig(directory, selection, installForm);
CheckConfig(directory, selection, installForm); });
});
} }

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,24 +33,19 @@ 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("{");
writer.WriteLine(" \"logging\": false,"); writer.WriteLine(" \"logging\": false,");
@ -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,34 +100,31 @@ 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,
out string config);
if (File.Exists(api32) && !File.Exists(api32_o))
{ {
File.Move(api32, api32_o); directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", if (File.Exists(api32) && !File.Exists(api32_o))
LogTextBox.Action, false); {
} File.Move(api32, api32_o);
if (File.Exists(api32_o)) installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false);
{ }
"UplayR1.uplay_r1_loader.dll".Write(api32); if (File.Exists(api32_o))
installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, false); {
} "UplayR1.uplay_r1_loader.dll".Write(api32);
if (File.Exists(api64) && !File.Exists(api64_o)) installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, false);
{ }
File.Move(api64, api64_o); if (File.Exists(api64) && !File.Exists(api64_o))
installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", {
LogTextBox.Action, false); File.Move(api64, api64_o);
} installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false);
if (File.Exists(api64_o)) }
{ if (File.Exists(api64_o))
"UplayR1.uplay_r1_loader64.dll".Write(api64); {
installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, false); "UplayR1.uplay_r1_loader64.dll".Write(api64);
} installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, false);
if (generateConfig) }
CheckConfig(directory, selection, installForm); if (generateConfig)
}); CheckConfig(directory, selection, installForm);
});
} }

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,24 +35,19 @@ 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("{");
writer.WriteLine(" \"logging\": false,"); writer.WriteLine(" \"logging\": false,");
@ -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,36 +107,34 @@ 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,
out string api64, out string api64_o, out string config);
string api = File.Exists(old_api32) ? old_api32 : api32;
if (File.Exists(api) && !File.Exists(api32_o))
{ {
File.Move(api, api32_o); directory.GetUplayR2Components(out string old_api32, out string old_api64, out string api32, out string api32_o, out string api64,
installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api32_o)}", out string api64_o, out string config);
LogTextBox.Action, false); string api = File.Exists(old_api32) ? old_api32 : api32;
} if (File.Exists(api) && !File.Exists(api32_o))
if (File.Exists(api32_o)) {
{ File.Move(api, api32_o);
"UplayR2.upc_r2_loader.dll".Write(api); installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false);
installForm?.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false); }
} if (File.Exists(api32_o))
api = File.Exists(old_api64) ? old_api64 : api64; {
if (File.Exists(api) && !File.Exists(api64_o)) "UplayR2.upc_r2_loader.dll".Write(api);
{ installForm?.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false);
File.Move(api, api64_o); }
installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api64_o)}", api = File.Exists(old_api64) ? old_api64 : api64;
LogTextBox.Action, false); if (File.Exists(api) && !File.Exists(api64_o))
} {
if (File.Exists(api64_o)) File.Move(api, api64_o);
{ installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false);
"UplayR2.upc_r2_loader64.dll".Write(api); }
installForm?.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false); if (File.Exists(api64_o))
} {
if (generateConfig) "UplayR2.upc_r2_loader64.dll".Write(api);
CheckConfig(directory, selection, installForm); installForm?.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false);
}); }
if (generateConfig)
CheckConfig(directory, selection, installForm);
});
} }

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,31 +32,23 @@ 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 : line[inNum..])
? "in " : null));
+ (lineNum != -1
? line[ciNum..lineNum]
+ "\n on " + line[(lineNum + 1)..]
: line[ciNum..])
: line[inNum..])
: null));
} }
} }
e = e.InnerException; e = e.InnerException;
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,27 +27,30 @@ 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(DirectoryPath)) Directory.Delete(DirectoryPath, true); if (Directory.Exists(DirectoryPathOld))
Directory.Move(DirectoryPathOld, DirectoryPath); {
} if (Directory.Exists(DirectoryPath))
if (!Directory.Exists(DirectoryPath)) _ = Directory.CreateDirectory(DirectoryPath); Directory.Delete(DirectoryPath, true);
if (!File.Exists(AppInfoVersionPath) Directory.Move(DirectoryPathOld, DirectoryPath);
|| !Version.TryParse(File.ReadAllText(AppInfoVersionPath, Encoding.UTF8), out Version version) }
|| version < MinimumAppInfoVersion) if (!Directory.Exists(DirectoryPath))
{ _ = Directory.CreateDirectory(DirectoryPath);
if (Directory.Exists(AppInfoPath)) Directory.Delete(AppInfoPath, true); if (!File.Exists(AppInfoVersionPath) || !Version.TryParse(File.ReadAllText(AppInfoVersionPath, Encoding.UTF8), out Version version)
_ = Directory.CreateDirectory(AppInfoPath); || version < MinimumAppInfoVersion)
File.WriteAllText(AppInfoVersionPath, Program.Version, Encoding.UTF8); {
} if (Directory.Exists(AppInfoPath))
if (!Directory.Exists(CooldownPath)) Directory.Delete(AppInfoPath, true);
_ = Directory.CreateDirectory(CooldownPath); _ = Directory.CreateDirectory(AppInfoPath);
if (File.Exists(OldProgramChoicesPath)) File.WriteAllText(AppInfoVersionPath, Program.Version, Encoding.UTF8);
File.Delete(OldProgramChoicesPath); }
}); if (!Directory.Exists(CooldownPath))
_ = Directory.CreateDirectory(CooldownPath);
if (File.Exists(OldProgramChoicesPath))
File.Delete(OldProgramChoicesPath);
});
internal static bool CheckCooldown(string identifier, int cooldown) internal static bool CheckCooldown(string identifier, int cooldown)
{ {
@ -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
{ {