- Upgraded to .NET 7
- String culture fixes
- Rare SteamCMD exception fix
- Fixed Steam store scanning getting stuck on certain games
- Code cleanup
This commit is contained in:
pointfeev 2022-12-20 16:26:35 -05:00
parent 195287601e
commit 9736e6dc1d
41 changed files with 1758 additions and 1359 deletions

View file

@ -1,12 +1,11 @@
using CreamInstaller.Platforms.Paradox;
using CreamInstaller.Utility;
using System;
using System;
using System.Collections.Concurrent;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
using CreamInstaller.Platforms.Paradox;
using CreamInstaller.Utility;
namespace CreamInstaller.Components;
@ -14,64 +13,79 @@ internal class ContextMenuItem : ToolStripMenuItem
{
private static readonly ConcurrentDictionary<string, Image> images = new();
private static async Task TryImageIdentifier(ContextMenuItem item, string imageIdentifier) => await Task.Run(async () =>
{
if (images.TryGetValue(imageIdentifier, out Image image) && image is not null) item.Image = image;
else
private static async Task TryImageIdentifier(ContextMenuItem item, string imageIdentifier) => await Task.Run(
async () =>
{
switch (imageIdentifier)
if (images.TryGetValue(imageIdentifier, out Image image) && image is not null)
{
case "Paradox Launcher":
if (Directory.Exists(ParadoxLauncher.InstallPath))
foreach (string file in Directory.EnumerateFiles(ParadoxLauncher.InstallPath, "*.exe"))
{
image = IconGrabber.GetFileIconImage(file);
break;
}
break;
case "Notepad":
image = IconGrabber.GetNotepadImage();
break;
case "Command Prompt":
image = IconGrabber.GetCommandPromptImage();
break;
case "File Explorer":
image = IconGrabber.GetFileExplorerImage();
break;
case "SteamDB":
image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("steamdb.info"));
break;
case "Steam Store":
image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("store.steampowered.com"));
break;
case "Steam Community":
image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("steamcommunity.com"));
break;
case "ScreamDB":
image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("scream-db.web.app"));
break;
case "Epic Games":
image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("epicgames.com"));
break;
case "Ubisoft Store":
image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("store.ubi.com"));
break;
default:
return;
}
if (image is not null)
{
images[imageIdentifier] = image;
item.Image = image;
}
}
});
else
{
switch (imageIdentifier)
{
case "Paradox Launcher":
if (Directory.Exists(ParadoxLauncher.InstallPath))
foreach (string file in Directory.EnumerateFiles(ParadoxLauncher.InstallPath, "*.exe"))
{
image = file.GetFileIconImage();
break;
}
break;
case "Notepad":
image = IconGrabber.GetNotepadImage();
break;
case "Command Prompt":
image = IconGrabber.GetCommandPromptImage();
break;
case "File Explorer":
image = IconGrabber.GetFileExplorerImage();
break;
case "SteamDB":
image = await HttpClientManager.GetImageFromUrl(
IconGrabber.GetDomainFaviconUrl("steamdb.info"));
break;
case "Steam Store":
image = await HttpClientManager.GetImageFromUrl(
IconGrabber.GetDomainFaviconUrl("store.steampowered.com"));
break;
case "Steam Community":
image = await HttpClientManager.GetImageFromUrl(
IconGrabber.GetDomainFaviconUrl("steamcommunity.com"));
break;
case "ScreamDB":
image = await HttpClientManager.GetImageFromUrl(
IconGrabber.GetDomainFaviconUrl("scream-db.web.app"));
break;
case "Epic Games":
image = await HttpClientManager.GetImageFromUrl(
IconGrabber.GetDomainFaviconUrl("epicgames.com"));
break;
case "Ubisoft Store":
image = await HttpClientManager.GetImageFromUrl(
IconGrabber.GetDomainFaviconUrl("store.ubi.com"));
break;
default:
return;
}
if (image is not null)
{
images[imageIdentifier] = image;
item.Image = image;
}
}
});
private static async Task TryImageIdentifierInfo(ContextMenuItem item, (string id, string iconUrl) imageIdentifierInfo, Action onFail = null) => await Task.Run(async () =>
private static async Task TryImageIdentifierInfo(ContextMenuItem item,
(string id, string iconUrl) imageIdentifierInfo,
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;
if (images.TryGetValue(imageIdentifier, out Image image) && image is not null)
{
item.Image = image;
}
else
{
image = await HttpClientManager.GetImageFromUrl(iconUrl);
@ -81,11 +95,14 @@ internal class ContextMenuItem : ToolStripMenuItem
item.Image = image;
}
else if (onFail is not null)
{
onFail();
}
}
});
private readonly EventHandler OnClickEvent;
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
@ -105,9 +122,15 @@ internal class ContextMenuItem : ToolStripMenuItem
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, EventHandler onClick = null)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo);
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, string imageIdentifierFallback, EventHandler onClick = null)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo, async () => await TryImageIdentifier(this, imageIdentifierFallback));
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo,
string imageIdentifierFallback, EventHandler onClick = null)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo,
async () => await TryImageIdentifier(
this, imageIdentifierFallback));
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, (string id, string iconUrl) imageIdentifierInfoFallback, EventHandler onClick = null)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo, async () => await TryImageIdentifierInfo(this, imageIdentifierInfoFallback));
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo,
(string id, string iconUrl) imageIdentifierInfoFallback, EventHandler onClick = null)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo,
async () => await TryImageIdentifierInfo(
this, imageIdentifierInfoFallback));
}

View file

@ -4,12 +4,21 @@ using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using CreamInstaller.Forms;
namespace CreamInstaller.Components;
internal class CustomForm : Form
{
internal CustomForm() : base()
internal const short SWP_NOACTIVATE = 0x0010;
internal const short SWP_SHOWWINDOW = 0x0040;
internal const short SWP_NOMOVE = 0x0002;
internal const short SWP_NOSIZE = 0x0001;
internal static readonly IntPtr HWND_NOTOPMOST = new(-2);
internal static readonly IntPtr HWND_TOPMOST = new(-1);
internal CustomForm()
{
Icon = Properties.Resources.Icon;
KeyPreview = true;
@ -19,47 +28,6 @@ internal class CustomForm : Form
HelpButtonClicked += OnHelpButtonClicked;
}
internal void OnHelpButtonClicked(object sender, EventArgs args)
{
using DialogForm helpDialog = new(this);
helpDialog.HelpButton = false;
string acidicoala = "https://github.com/acidicoala";
string repository = $"https://github.com/{Program.RepositoryOwner}/{Program.RepositoryName}";
_ = 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"
+ "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"
+ "\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"
+ "\n"
+ "NOTE: This program does not automatically download nor install actual DLC files for you. As the title of the program says, it's\n"
+ "only a DLC Unlocker installer. Should the game you wish to unlock DLC for not already come with the DLCs installed (very many\n"
+ "do not), you have to find, download, and install those yourself. Preferably, you should be referring to the proper cs.rin.ru post for\n"
+ "the game(s) you're tinkering with; you'll usually find any answer to your problems there.\n"
+ "\n"
+ "USAGE:\n"
+ " 1. Choose which programs and/or games the program should scan for DLC.\n"
+ " The program automatically gathers all installed games from Steam, Epic and Ubisoft directories.\n"
+ " 2. Wait for the program to download and install SteamCMD (if you chose a Steam game).\n"
+ " 3. Wait for the program to gather and cache the chosen games' information && DLCs.\n"
+ " May take some time on the first run; depends on how many DLCs the games you chose have.\n"
+ " 4. CAREFULLY select which games' DLCs you wish to unlock.\n"
+ " Obviously none of the DLC unlockers are tested for every single game!\n"
+ " 5. Choose whether or not to install with Koaloader, and if so then also pick the proxy DLL to use.\n"
+ " If the default \'version.dll\' doesn't work, then see [here](https://cs.rin.ru/forum/viewtopic.php?p=2552172#p2552172) to find one that does.\n"
+ " 6. Click the \"Generate and Install\" button.\n"
+ " 7. Click the \"OK\" button to close the program.\n"
+ " 8. If any of the DLC unlockers cause problems with any of the games you installed them on, simply go back\n"
+ " 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 CustomForm(IWin32Window owner) : this()
{
if (owner is Form form)
@ -73,27 +41,6 @@ internal class CustomForm : Form
}
}
internal void OnActivation(object sender, EventArgs args) => Activate();
internal static readonly IntPtr HWND_NOTOPMOST = new(-2);
internal static readonly IntPtr HWND_TOPMOST = new(-1);
internal const short SWP_NOACTIVATE = 0x0010;
internal const short SWP_SHOWWINDOW = 0x0040;
internal const short SWP_NOMOVE = 0x0002;
internal const short SWP_NOSIZE = 0x0001;
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern void SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
internal void BringToFrontWithoutActivation()
{
bool topMost = TopMost;
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
if (!topMost)
SetWindowPos(Handle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
}
protected override CreateParams CreateParams // Double buffering for all controls
{
get
@ -104,13 +51,69 @@ internal class CustomForm : Form
}
}
internal void OnHelpButtonClicked(object sender, EventArgs args)
{
using DialogForm helpDialog = new(this);
helpDialog.HelpButton = false;
string acidicoala = "https://github.com/acidicoala";
string repository = $"https://github.com/{Program.RepositoryOwner}/{Program.RepositoryName}";
_ = 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"
+ "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"
+ "\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"
+ "\n"
+ "NOTE: This program does not automatically download nor install actual DLC files for you. As the title of the program says, it's\n"
+ "only a DLC Unlocker installer. Should the game you wish to unlock DLC for not already come with the DLCs installed (very many\n"
+ "do not), you have to find, download, and install those yourself. Preferably, you should be referring to the proper cs.rin.ru post for\n"
+ "the game(s) you're tinkering with; you'll usually find any answer to your problems there.\n"
+ "\n"
+ "USAGE:\n"
+ " 1. Choose which programs and/or games the program should scan for DLC.\n"
+ " The program automatically gathers all installed games from Steam, Epic and Ubisoft directories.\n"
+ " 2. Wait for the program to download and install SteamCMD (if you chose a Steam game).\n"
+ " 3. Wait for the program to gather and cache the chosen games' information && DLCs.\n"
+ " May take some time on the first run; depends on how many DLCs the games you chose have.\n"
+ " 4. CAREFULLY select which games' DLCs you wish to unlock.\n"
+ " Obviously none of the DLC unlockers are tested for every single game!\n"
+ " 5. Choose whether or not to install with Koaloader, and if so then also pick the proxy DLL to use.\n"
+ " If the default \'version.dll\' doesn't work, then see [here](https://cs.rin.ru/forum/viewtopic.php?p=2552172#p2552172) to find one that does.\n"
+ " 6. Click the \"Generate and Install\" button.\n"
+ " 7. Click the \"OK\" button to close the program.\n"
+ " 8. If any of the DLC unlockers cause problems with any of the games you installed them on, simply go back\n"
+ " 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();
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern void SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy,
uint uFlags);
internal void BringToFrontWithoutActivation()
{
bool topMost = TopMost;
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
if (!topMost)
SetWindowPos(Handle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
}
internal void InheritLocation(Form fromForm)
{
if (fromForm is null)
return;
int X = fromForm.Location.X + fromForm.Size.Width / 2 - Size.Width / 2;
int Y = fromForm.Location.Y + fromForm.Size.Height / 2 - Size.Height / 2;
Location = new(X, Y);
Location = new Point(X, Y);
}
private void OnKeyPress(object s, KeyPressEventArgs e)
@ -124,7 +127,8 @@ internal class CustomForm : Form
using EncoderParameters encoding = new(1);
using EncoderParameter encoderParam = new(Encoder.Quality, 100L);
encoding.Param[0] = encoderParam;
graphics.CopyFromScreen(new(bounds.Left + 7, bounds.Top), Point.Empty, new(Size.Width - 14, Size.Height - 7));
graphics.CopyFromScreen(new Point(bounds.Left + 7, bounds.Top), Point.Empty,
new Size(Size.Width - 14, Size.Height - 7));
Clipboard.SetImage(bitmap);
e.Handled = true;
}

View file

@ -1,25 +1,35 @@
using CreamInstaller.Resources;
using System;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using CreamInstaller.Forms;
using CreamInstaller.Resources;
using static CreamInstaller.Resources.Resources;
using TreeView = System.Windows.Forms.TreeView;
namespace CreamInstaller.Components;
internal class CustomTreeView : TreeView
{
private Form form;
private const string koaloaderToggleString = "Koaloader";
private readonly Dictionary<ProgramSelection, Rectangle> checkBoxBounds = new();
private readonly Dictionary<ProgramSelection, Rectangle> comboBoxBounds = new();
private readonly Dictionary<TreeNode, Rectangle> selectionBounds = new();
private SolidBrush backBrush;
private Font comboBoxFont;
private ToolStripDropDown comboBoxDropDown;
private Font comboBoxFont;
private Form form;
internal CustomTreeView()
{
DrawMode = TreeViewDrawMode.OwnerDrawText;
DrawNode += DrawTreeNode;
TreeViewNodeSorter = PlatformIdComparer.NodeName;
Disposed += OnDisposed;
}
protected override void WndProc(ref Message m)
{
@ -30,14 +40,6 @@ internal class CustomTreeView : TreeView
form = FindForm();
}
internal CustomTreeView() : base()
{
DrawMode = TreeViewDrawMode.OwnerDrawText;
DrawNode += new DrawTreeNodeEventHandler(DrawTreeNode);
TreeViewNodeSorter = PlatformIdComparer.NodeName;
Disposed += OnDisposed;
}
private void OnDisposed(object sender, EventArgs e)
{
backBrush?.Dispose();
@ -48,72 +50,68 @@ internal class CustomTreeView : TreeView
comboBoxDropDown = null;
}
private readonly Dictionary<TreeNode, Rectangle> selectionBounds = new();
private readonly Dictionary<ProgramSelection, Rectangle> checkBoxBounds = new();
private readonly Dictionary<ProgramSelection, Rectangle> comboBoxBounds = new();
private const string koaloaderToggleString = "Koaloader";
private void DrawTreeNode(object sender, DrawTreeNodeEventArgs e)
{
e.DrawDefault = true;
TreeNode node = e.Node;
if (!node.IsVisible)
return;
bool highlighted = (e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected && Focused;
Graphics graphics = e.Graphics;
backBrush ??= new(BackColor);
backBrush ??= new SolidBrush(BackColor);
Font font = node.NodeFont ?? Font;
Brush brush = highlighted ? SystemBrushes.Highlight : backBrush;
string text;// = e.Node.Text;
string text; // = e.Node.Text;
Size size;
Rectangle bounds = node.Bounds;
Rectangle selectionBounds = bounds;
Color color;// = highlighted ? SystemColors.HighlightText : (node.ForeColor != Color.Empty) ? node.ForeColor : node.TreeView.ForeColor;
Color
color; // = highlighted ? SystemColors.HighlightText : (node.ForeColor != Color.Empty) ? node.ForeColor : node.TreeView.ForeColor;
Point point;
/*Size textSize = TextRenderer.MeasureText(text, font);
Point textLoc = new(bounds.X - 1, bounds.Y);
bounds = new Rectangle(textLoc, new Size(textSize.Width, bounds.Height));
graphics.FillRectangle(brush, bounds);
TextRenderer.DrawText(graphics, text, font, bounds, color, TextFormatFlags.Default);*/
Form form = FindForm();
if (form is not SelectForm and not SelectDialogForm)
return;
string platformId = node.Name;
Platform platform = (node.Tag as Platform?).GetValueOrDefault(Platform.None);
if (string.IsNullOrWhiteSpace(platformId) || platform is Platform.None)
return;
color = highlighted ? ColorTranslator.FromHtml("#FFFF99") : Enabled ? ColorTranslator.FromHtml("#696900") : ColorTranslator.FromHtml("#AAAA69");
color = highlighted
? ColorTranslator.FromHtml("#FFFF99")
: Enabled
? ColorTranslator.FromHtml("#696900")
: ColorTranslator.FromHtml("#AAAA69");
text = platform.ToString();
size = TextRenderer.MeasureText(graphics, text, font);
bounds = new(bounds.X + bounds.Width, bounds.Y, size.Width, bounds.Height);
selectionBounds = new(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width, 0));
bounds = new Rectangle(bounds.X + bounds.Width, bounds.Y, size.Width, bounds.Height);
selectionBounds
= new Rectangle(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width, 0));
graphics.FillRectangle(brush, bounds);
point = new(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);
if (platform is not Platform.Paradox)
{
color = highlighted ? ColorTranslator.FromHtml("#99FFFF") : Enabled ? ColorTranslator.FromHtml("#006969") : ColorTranslator.FromHtml("#69AAAA");
text = platformId.ToString();
color = highlighted
? ColorTranslator.FromHtml("#99FFFF")
: Enabled
? ColorTranslator.FromHtml("#006969")
: ColorTranslator.FromHtml("#69AAAA");
text = platformId;
size = TextRenderer.MeasureText(graphics, text, font);
int left = -4;
bounds = new(bounds.X + bounds.Width + left, bounds.Y, size.Width, bounds.Height);
selectionBounds = new(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width + left, 0));
bounds = new Rectangle(bounds.X + bounds.Width + left, bounds.Y, size.Width, bounds.Height);
selectionBounds = new Rectangle(selectionBounds.Location,
selectionBounds.Size + new Size(bounds.Size.Width + left, 0));
graphics.FillRectangle(brush, bounds);
point = new(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);
}
/*if (highlighted)
ControlPaint.DrawFocusRectangle(graphics, selectionBounds, color, SystemColors.Highlight);*/
if (form is SelectForm)
{
ProgramSelection selection = ProgramSelection.FromPlatformId(platform, platformId);
@ -121,64 +119,71 @@ internal class CustomTreeView : TreeView
{
if (bounds == node.Bounds)
{
size = new(4, 0);
bounds = new(bounds.X + bounds.Width, bounds.Y, size.Width, bounds.Height);
size = new Size(4, 0);
bounds = new Rectangle(bounds.X + bounds.Width, bounds.Y, size.Width, bounds.Height);
graphics.FillRectangle(brush, bounds);
}
CheckBoxState checkBoxState = selection.Koaloader
? Enabled ? CheckBoxState.CheckedPressed : CheckBoxState.CheckedDisabled
: Enabled ? CheckBoxState.UncheckedPressed : CheckBoxState.UncheckedDisabled;
: Enabled
? CheckBoxState.UncheckedPressed
: CheckBoxState.UncheckedDisabled;
size = CheckBoxRenderer.GetGlyphSize(graphics, checkBoxState);
bounds = new(bounds.X + bounds.Width, bounds.Y, size.Width, bounds.Height);
selectionBounds = new(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width, 0));
bounds = new Rectangle(bounds.X + bounds.Width, bounds.Y, size.Width, bounds.Height);
selectionBounds = new Rectangle(selectionBounds.Location,
selectionBounds.Size + new Size(bounds.Size.Width, 0));
Rectangle checkBoxBounds = bounds;
graphics.FillRectangle(backBrush, bounds);
point = new(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);
CheckBoxRenderer.DrawCheckBox(graphics, point, checkBoxState);
text = koaloaderToggleString;
size = TextRenderer.MeasureText(graphics, text, font);
int left = 1;
bounds = new(bounds.X + bounds.Width, bounds.Y, size.Width + left, bounds.Height);
selectionBounds = new(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width, 0));
checkBoxBounds = new(checkBoxBounds.Location, checkBoxBounds.Size + new Size(bounds.Size.Width, 0));
bounds = new Rectangle(bounds.X + bounds.Width, bounds.Y, size.Width + left, bounds.Height);
selectionBounds = new Rectangle(selectionBounds.Location,
selectionBounds.Size + new Size(bounds.Size.Width, 0));
checkBoxBounds = new Rectangle(checkBoxBounds.Location,
checkBoxBounds.Size + new Size(bounds.Size.Width, 0));
graphics.FillRectangle(backBrush, bounds);
point = new(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,
Enabled ? ColorTranslator.FromHtml("#006900") : ColorTranslator.FromHtml("#69AA69"),
TextFormatFlags.Default);
Enabled
? ColorTranslator.FromHtml("#006900")
: ColorTranslator.FromHtml("#69AA69"),
TextFormatFlags.Default);
this.checkBoxBounds[selection] = RectangleToClient(checkBoxBounds);
string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy;
if (selection.Koaloader && proxy is not null)
{
comboBoxFont ??= new(font.FontFamily, 6, font.Style, font.Unit, font.GdiCharSet, font.GdiVerticalFont);
comboBoxFont ??= new Font(font.FontFamily, 6, font.Style, font.Unit, font.GdiCharSet,
font.GdiVerticalFont);
ComboBoxState comboBoxState = Enabled ? ComboBoxState.Normal : ComboBoxState.Disabled;
text = proxy + ".dll";
size = TextRenderer.MeasureText(graphics, text, comboBoxFont) + new Size(6, 0);
int padding = 2;
bounds = new(bounds.X + bounds.Width, bounds.Y + padding / 2, size.Width, bounds.Height - padding);
selectionBounds = new(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width, 0));
bounds = new Rectangle(bounds.X + bounds.Width, bounds.Y + padding / 2, size.Width,
bounds.Height - padding);
selectionBounds = new Rectangle(selectionBounds.Location,
selectionBounds.Size + new Size(bounds.Size.Width, 0));
Rectangle comboBoxBounds = bounds;
graphics.FillRectangle(backBrush, bounds);
ComboBoxRenderer.DrawTextBox(graphics, bounds, text, comboBoxFont, comboBoxState);
size = new(14, 0);
size = new Size(14, 0);
left = -1;
bounds = new(bounds.X + bounds.Width + left, bounds.Y, size.Width, bounds.Height);
selectionBounds = new(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width + left, 0));
comboBoxBounds = new(comboBoxBounds.Location, comboBoxBounds.Size + new Size(bounds.Size.Width + left, 0));
bounds = new Rectangle(bounds.X + bounds.Width + left, bounds.Y, size.Width, bounds.Height);
selectionBounds = new Rectangle(selectionBounds.Location,
selectionBounds.Size + new Size(bounds.Size.Width + left, 0));
comboBoxBounds = new Rectangle(comboBoxBounds.Location,
comboBoxBounds.Size + new Size(bounds.Size.Width + left, 0));
ComboBoxRenderer.DrawDropDownButton(graphics, bounds, comboBoxState);
this.comboBoxBounds[selection] = RectangleToClient(comboBoxBounds);
}
else _ = comboBoxBounds.Remove(selection);
else
{
_ = comboBoxBounds.Remove(selection);
}
}
}
this.selectionBounds[node] = RectangleToClient(selectionBounds);
}
@ -189,9 +194,10 @@ internal class CustomTreeView : TreeView
Point clickPoint = PointToClient(e.Location);
SelectForm selectForm = (form ??= FindForm()) as SelectForm;
foreach (KeyValuePair<TreeNode, Rectangle> pair in selectionBounds.ToList())
{
if (pair.Key.TreeView is null)
{
_ = selectionBounds.Remove(pair.Key);
}
else if (pair.Key.IsVisible && pair.Value.Contains(clickPoint))
{
SelectedNode = pair.Key;
@ -199,23 +205,24 @@ internal class CustomTreeView : TreeView
selectForm.OnNodeRightClick(pair.Key, e.Location);
break;
}
}
if (e.Button is MouseButtons.Left)
{
if (comboBoxBounds.Any() && selectForm is not null)
foreach (KeyValuePair<ProgramSelection, Rectangle> pair in comboBoxBounds.ToList())
{
if (!ProgramSelection.All.Contains(pair.Key))
{
_ = comboBoxBounds.Remove(pair.Key);
}
else if (pair.Value.Contains(clickPoint))
{
List<string> proxies = EmbeddedResources.FindAll(r => r.StartsWith("Koaloader"))
.Select(p =>
{
p.GetProxyInfoFromIdentifier(out string proxyName, out _);
return proxyName;
}).Distinct().ToList();
comboBoxDropDown ??= new();
.Select(p =>
{
p.GetProxyInfoFromIdentifier(
out string proxyName, out _);
return proxyName;
}).Distinct().ToList();
comboBoxDropDown ??= new ToolStripDropDown();
comboBoxDropDown.ShowItemToolTips = false;
comboBoxDropDown.Items.Clear();
foreach (string proxy in proxies)
@ -233,26 +240,25 @@ internal class CustomTreeView : TreeView
if (canUse)
_ = comboBoxDropDown.Items.Add(new ToolStripButton(proxy + ".dll", null, (s, e) =>
{
pair.Key.KoaloaderProxy = proxy == ProgramSelection.DefaultKoaloaderProxy ? null : proxy;
pair.Key.KoaloaderProxy
= proxy == ProgramSelection.DefaultKoaloaderProxy ? null : proxy;
selectForm.OnKoaloaderChanged();
})
{ Font = comboBoxFont });
}) { Font = comboBoxFont });
}
comboBoxDropDown.Show(this, PointToScreen(new(pair.Value.Left, pair.Value.Bottom - 1)));
comboBoxDropDown.Show(this, PointToScreen(new Point(pair.Value.Left, pair.Value.Bottom - 1)));
break;
}
}
foreach (KeyValuePair<ProgramSelection, Rectangle> pair in checkBoxBounds.ToList())
{
if (!ProgramSelection.All.Contains(pair.Key))
{
_ = checkBoxBounds.Remove(pair.Key);
}
else if (pair.Value.Contains(clickPoint))
{
pair.Key.Koaloader = !pair.Key.Koaloader;
selectForm.OnKoaloaderChanged();
break;
}
}
}
}
}

View file

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

View file

@ -1,17 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows10.0.22621.0</TargetFramework>
<TargetFramework>net7.0-windows</TargetFramework>
<UseWindowsForms>True</UseWindowsForms>
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
<Version>4.2.3.1</Version>
<Version>4.3.0.0</Version>
<Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright>
<Company>CreamInstaller</Company>
<Product>Automatic DLC Unlocker Installer &amp; Configuration Generator</Product>
<StartupObject>CreamInstaller.Program</StartupObject>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<AnalysisLevel>latest-all</AnalysisLevel>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
<AnalysisLevel>latest-recommended</AnalysisLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
@ -27,6 +26,12 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<AssemblyName>$(Company)-debug</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\Koaloader\audioses-32\audioses.dll" />
<None Remove="Resources\Koaloader\audioses-64\audioses.dll" />
@ -137,10 +142,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Gameloop.Vdf" Version="0.6.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.45" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Onova" Version="2.6.2" />
<PackageReference Include="System.Reflection.Metadata" Version="6.0.1" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="Onova" Version="2.6.3" />
<PackageReference Include="System.Reflection.Metadata" Version="7.0.0" />
<PackageReference Include="System.ServiceModel.Primitives" Version="4.10.0" />
</ItemGroup>
<ItemGroup>

View file

@ -1,11 +1,14 @@
namespace CreamInstaller;
using System.ComponentModel;
using System.Windows.Forms;
namespace CreamInstaller.Forms;
partial class DebugForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
private IContainer components = null;
/// <summary>
/// Clean up any resources being used.
@ -65,5 +68,5 @@ partial class DebugForm
#endregion
private System.Windows.Forms.RichTextBox debugTextBox;
private RichTextBox debugTextBox;
}

View file

@ -1,32 +1,34 @@
using CreamInstaller.Components;
using CreamInstaller.Utility;
using System;
using System;
using System.Drawing;
using System.Windows.Forms;
using CreamInstaller.Components;
using CreamInstaller.Utility;
namespace CreamInstaller;
namespace CreamInstaller.Forms;
internal partial class DebugForm : CustomForm
{
internal static DebugForm current;
private Form attachedForm;
internal DebugForm()
{
InitializeComponent();
debugTextBox.BackColor = LogTextBox.Background;
}
internal static DebugForm Current
{
get
{
if (current is not null && (current.Disposing || current.IsDisposed))
current = null;
return current ??= new();
return current ??= new DebugForm();
}
set => current = value;
}
internal DebugForm() : base()
{
InitializeComponent();
debugTextBox.BackColor = LogTextBox.Background;
}
protected override void WndProc(ref Message message) // make form immovable by user
{
if (message.Msg == 0x0112) // WM_SYSCOMMAND
@ -38,8 +40,6 @@ internal partial class DebugForm : CustomForm
base.WndProc(ref message);
}
private Form attachedForm;
internal void Attach(Form form)
{
if (attachedForm is not null)
@ -64,7 +64,7 @@ internal partial class DebugForm : CustomForm
if (attachedForm is not null && attachedForm.Visible)
{
//Size = new(Size.Width, attachedForm.Size.Height);
Location = new(attachedForm.Right, attachedForm.Top);
Location = new Point(attachedForm.Right, attachedForm.Top);
BringToFrontWithoutActivation();
}
}
@ -74,13 +74,11 @@ internal partial class DebugForm : CustomForm
internal void Log(string text, Color color)
{
if (!debugTextBox.Disposing && !debugTextBox.IsDisposed)
{
debugTextBox.Invoke(() =>
{
if (debugTextBox.Text.Length > 0)
debugTextBox.AppendText(Environment.NewLine, color, scroll: true);
debugTextBox.AppendText(text, color, scroll: true);
debugTextBox.AppendText(Environment.NewLine, color, true);
debugTextBox.AppendText(text, color, true);
});
}
}
}

View file

@ -1,8 +1,7 @@

using System.ComponentModel;
using System.ComponentModel;
using System.Windows.Forms;
namespace CreamInstaller
namespace CreamInstaller.Forms
{
partial class DialogForm
{

View file

@ -1,31 +1,30 @@
using CreamInstaller.Components;
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using CreamInstaller.Components;
namespace CreamInstaller;
namespace CreamInstaller.Forms;
internal partial class DialogForm : CustomForm
{
internal DialogForm(IWin32Window owner) : base(owner) => InitializeComponent();
internal DialogResult Show(Icon descriptionIcon, string descriptionText, string acceptButtonText = "OK", string cancelButtonText = null, string customFormText = null, Icon customFormIcon = null)
internal DialogResult Show(Icon descriptionIcon, string descriptionText, string acceptButtonText = "OK",
string cancelButtonText = null, string customFormText = null, Icon customFormIcon = null)
{
descriptionIcon ??= Icon;
icon.Image = descriptionIcon.ToBitmap();
List<LinkLabel.Link> links = new();
for (int i = 0; i < descriptionText.Length; i++)
{
if (descriptionText[i] == '[')
{
int textLeft = descriptionText.IndexOf("[", i);
int textRight = descriptionText.IndexOf("]", textLeft == -1 ? i : textLeft);
int linkLeft = descriptionText.IndexOf("(", textRight == -1 ? i : textRight);
int linkRight = descriptionText.IndexOf(")", linkLeft == -1 ? i : linkLeft);
int textLeft = descriptionText.IndexOf("[", i, StringComparison.Ordinal);
int textRight = descriptionText.IndexOf("]", textLeft == -1 ? i : textLeft, StringComparison.Ordinal);
int linkLeft = descriptionText.IndexOf("(", textRight == -1 ? i : textRight, StringComparison.Ordinal);
int linkRight = descriptionText.IndexOf(")", linkLeft == -1 ? i : linkLeft, StringComparison.Ordinal);
if (textLeft != -1 && textRight == linkLeft - 1 && linkRight != -1)
{
string text = descriptionText[(textLeft + 1)..textRight];
@ -36,7 +35,6 @@ internal partial class DialogForm : CustomForm
links.Add(new LinkLabel.Link(i, text.Length, link));
}
}
}
descriptionLabel.Text = descriptionText;
acceptButton.Text = acceptButtonText;
if (cancelButtonText is null)
@ -44,9 +42,14 @@ internal partial class DialogForm : CustomForm
cancelButton.Enabled = false;
cancelButton.Visible = false;
}
else cancelButton.Text = cancelButtonText;
else
{
cancelButton.Text = cancelButtonText;
}
if (customFormText is not null)
{
Text = customFormText;
}
else
{
OnResize(null, null);
@ -58,7 +61,8 @@ internal partial class DialogForm : CustomForm
{
foreach (LinkLabel.Link link in links)
_ = descriptionLabel.Links.Add(link);
descriptionLabel.LinkClicked += (s, e) => Process.Start(new ProcessStartInfo((string)e.Link.LinkData) { UseShellExecute = true });
descriptionLabel.LinkClicked += (s, e)
=> Process.Start(new ProcessStartInfo((string)e.Link.LinkData) { UseShellExecute = true });
}
return ShowDialog();
}
@ -66,7 +70,7 @@ internal partial class DialogForm : CustomForm
private void OnResize(object s, EventArgs e) =>
Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100
? TextRenderer.MeasureText(Program.ApplicationNameShort, Font).Width > Size.Width - 100
? Program.Name
: Program.ApplicationNameShort
? Program.Name
: Program.ApplicationNameShort
: Program.ApplicationName;
}

View file

@ -1,8 +1,7 @@

using System.ComponentModel;
using System.ComponentModel;
using System.Windows.Forms;
namespace CreamInstaller
namespace CreamInstaller.Forms
{
partial class InstallForm
{

View file

@ -1,26 +1,30 @@
using CreamInstaller.Components;
using CreamInstaller.Resources;
using CreamInstaller.Utility;
using System;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CreamInstaller.Components;
using CreamInstaller.Resources;
using CreamInstaller.Utility;
using static CreamInstaller.Platforms.Paradox.ParadoxLauncher;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller;
namespace CreamInstaller.Forms;
internal partial class InstallForm : CustomForm
{
internal bool Reselecting;
internal readonly bool Uninstalling;
private readonly List<ProgramSelection> DisabledSelections = new();
internal InstallForm(bool uninstall = false) : base()
private readonly int ProgramCount = ProgramSelection.AllEnabled.Count;
internal readonly bool Uninstalling;
private int CompleteOperationsCount;
private int OperationsCount;
internal bool Reselecting;
internal InstallForm(bool uninstall = false)
{
InitializeComponent();
Text = Program.ApplicationName;
@ -28,9 +32,6 @@ internal partial class InstallForm : CustomForm
Uninstalling = uninstall;
}
private int OperationsCount;
private int CompleteOperationsCount;
internal void UpdateProgress(int progress)
{
if (!userProgressBar.Disposing && !userProgressBar.IsDisposed)
@ -46,14 +47,12 @@ internal partial class InstallForm : CustomForm
{
if (info) _ = userInfoLabel.Invoke(() => userInfoLabel.Text = text);
if (log && !logTextBox.Disposing && !logTextBox.IsDisposed)
{
logTextBox.Invoke(() =>
{
if (logTextBox.Text.Length > 0) logTextBox.AppendText(Environment.NewLine, color);
logTextBox.AppendText(text, color);
logTextBox.Invalidate();
});
}
}
private async Task OperateFor(ProgramSelection selection)
@ -61,14 +60,16 @@ internal partial class InstallForm : CustomForm
UpdateProgress(0);
if (selection.Id == "PL")
{
UpdateUser($"Repairing Paradox Launcher . . . ", LogTextBox.Operation);
UpdateUser("Repairing Paradox Launcher . . . ", LogTextBox.Operation);
_ = await Repair(this, selection);
}
UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")}" +
$" {(Uninstalling ? "from" : "for")} " + selection.Name + $" with root directory \"{selection.RootDirectory}\" . . . ", LogTextBox.Operation);
$" {(Uninstalling ? "from" : "for")} " + selection.Name
+ $" with root directory \"{selection.RootDirectory}\" . . . ", LogTextBox.Operation);
IEnumerable<string> invalidDirectories = (await selection.RootDirectory.GetExecutables())
?.Where(d => !selection.ExecutableDirectories.Any(s => s.directory == Path.GetDirectoryName(d.path)))
?.Select(d => Path.GetDirectoryName(d.path));
?.Where(d => !selection.ExecutableDirectories.Any(
s => s.directory == Path.GetDirectoryName(d.path)))
?.Select(d => Path.GetDirectoryName(d.path));
if (!selection.ExecutableDirectories.Any(s => s.directory == selection.RootDirectory))
invalidDirectories = invalidDirectories?.Append(selection.RootDirectory);
invalidDirectories = invalidDirectories?.Distinct();
@ -78,30 +79,33 @@ internal partial class InstallForm : CustomForm
if (Program.Canceled) throw new CustomMessageException("The operation was canceled.");
directory.GetKoaloaderComponents(out List<string> proxies, out string config);
if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader))
|| directory != selection.RootDirectory && Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll))
|| File.Exists(config))
|| (directory != selection.RootDirectory
&& Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll)))
|| File.Exists(config))
{
UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in incorrect directory \"{directory}\" . . . ", LogTextBox.Operation);
await Koaloader.Uninstall(directory, rootDirectory: selection.RootDirectory, this);
UpdateUser("Uninstalling Koaloader from " + selection.Name
+ $" in incorrect directory \"{directory}\" . . . ",
LogTextBox.Operation);
await Koaloader.Uninstall(directory, selection.RootDirectory, this);
}
Thread.Sleep(1);
}
if (Uninstalling || !selection.Koaloader)
{
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
{
if (Program.Canceled) throw new CustomMessageException("The operation was canceled.");
directory.GetKoaloaderComponents(out List<string> proxies, out string config);
if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader))
|| Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll))
|| File.Exists(config))
|| Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll))
|| File.Exists(config))
{
UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
await Koaloader.Uninstall(directory, rootDirectory: selection.RootDirectory, this);
UpdateUser(
"Uninstalling Koaloader from " + selection.Name + $" in directory \"{directory}\" . . . ",
LogTextBox.Operation);
await Koaloader.Uninstall(directory, selection.RootDirectory, this);
}
Thread.Sleep(1);
}
}
bool uninstallProxy = Uninstalling || selection.Koaloader;
int count = selection.DllDirectories.Count, cur = 0;
foreach (string directory in selection.DllDirectories)
@ -109,11 +113,15 @@ internal partial class InstallForm : CustomForm
if (Program.Canceled) throw new CustomMessageException("The operation was canceled.");
if (selection.Platform is Platform.Steam or Platform.Paradox)
{
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string cache);
if (uninstallProxy ? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) || File.Exists(cache) : File.Exists(api32) || File.Exists(api64))
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64,
out string api64_o, out string config, out string cache);
if (uninstallProxy
? File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) || File.Exists(cache)
: File.Exists(api32) || File.Exists(api64))
{
UpdateUser($"{(uninstallProxy ? "Uninstalling" : "Installing")} SmokeAPI" +
$" {(uninstallProxy ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
$" {(uninstallProxy ? "from" : "for")} " + selection.Name
+ $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
if (uninstallProxy)
await SmokeAPI.Uninstall(directory, this);
else
@ -122,11 +130,15 @@ internal partial class InstallForm : CustomForm
}
if (selection.Platform is Platform.Epic or Platform.Paradox)
{
directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, 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))
directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64,
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))
{
UpdateUser($"{(uninstallProxy ? "Uninstalling" : "Installing")} ScreamAPI" +
$" {(uninstallProxy ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
$" {(uninstallProxy ? "from" : "for")} " + selection.Name
+ $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
if (uninstallProxy)
await ScreamAPI.Uninstall(directory, this);
else
@ -135,21 +147,29 @@ internal partial class InstallForm : CustomForm
}
if (selection.Platform is Platform.Ubisoft)
{
directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, 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))
directory.GetUplayR1Components(out string api32, out string api32_o, out string api64,
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))
{
UpdateUser($"{(uninstallProxy ? "Uninstalling" : "Installing")} Uplay R1 Unlocker" +
$" {(uninstallProxy ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
$" {(uninstallProxy ? "from" : "for")} " + selection.Name
+ $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
if (uninstallProxy)
await UplayR1.Uninstall(directory, this);
else
await UplayR1.Install(directory, selection, this);
}
directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config);
if (uninstallProxy ? 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))
directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o,
out api64, out api64_o, out config);
if (uninstallProxy
? 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))
{
UpdateUser($"{(uninstallProxy ? "Uninstalling" : "Installing")} Uplay R2 Unlocker" +
$" {(uninstallProxy ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
$" {(uninstallProxy ? "from" : "for")} " + selection.Name
+ $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
if (uninstallProxy)
await UplayR2.Uninstall(directory, this);
else
@ -160,20 +180,17 @@ internal partial class InstallForm : CustomForm
Thread.Sleep(1);
}
if (selection.Koaloader && !Uninstalling)
{
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
{
if (Program.Canceled) throw new CustomMessageException("The operation was canceled.");
UpdateUser("Installing Koaloader to " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
await Koaloader.Install(directory, binaryType, selection, rootDirectory: selection.RootDirectory, this);
UpdateUser("Installing Koaloader to " + selection.Name + $" in directory \"{directory}\" . . . ",
LogTextBox.Operation);
await Koaloader.Install(directory, binaryType, selection, selection.RootDirectory, this);
Thread.Sleep(1);
}
}
UpdateProgress(100);
}
private readonly List<ProgramSelection> DisabledSelections = new();
private async Task Operate()
{
List<ProgramSelection> programSelections = ProgramSelection.AllEnabled;
@ -181,7 +198,8 @@ internal partial class InstallForm : CustomForm
CompleteOperationsCount = 0;
foreach (ProgramSelection selection in programSelections)
{
if (Program.Canceled || !Program.IsProgramRunningDialog(this, selection)) throw new CustomMessageException("The operation was canceled.");
if (Program.Canceled || !Program.IsProgramRunningDialog(this, selection))
throw new CustomMessageException("The operation was canceled.");
try
{
await OperateFor(selection);
@ -207,8 +225,6 @@ internal partial class InstallForm : CustomForm
DisabledSelections.Clear();
}
private readonly int ProgramCount = ProgramSelection.AllEnabled.Count;
private async void Start()
{
Program.Canceled = false;
@ -220,11 +236,13 @@ internal partial class InstallForm : CustomForm
try
{
await Operate();
UpdateUser($"DLC unlocker(s) successfully {(Uninstalling ? "uninstalled" : "installed and generated")} for " + ProgramCount + " program(s).", LogTextBox.Success);
UpdateUser($"DLC unlocker(s) successfully {(Uninstalling ? "uninstalled" : "installed and generated")} for "
+ ProgramCount + " program(s).", LogTextBox.Success);
}
catch (Exception exception)
{
UpdateUser($"DLC unlocker {(Uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + exception, LogTextBox.Error);
UpdateUser($"DLC unlocker {(Uninstalling ? "uninstallation" : "installation and/or generation")} failed: "
+ exception, LogTextBox.Error);
retryButton.Enabled = true;
}
userProgressBar.Value = userProgressBar.Maximum;
@ -235,7 +253,7 @@ internal partial class InstallForm : CustomForm
private void OnLoad(object sender, EventArgs _)
{
retry:
retry:
try
{
userInfoLabel.Text = "Loading . . . ";
@ -244,7 +262,7 @@ internal partial class InstallForm : CustomForm
}
catch (Exception e)
{
if (e.HandleException(form: this)) goto retry;
if (e.HandleException(this)) goto retry;
Close();
}
}

View file

@ -1,11 +1,8 @@

using System;
using System.ComponentModel;
using System.ComponentModel;
using System.Windows.Forms;
using CreamInstaller.Components;
namespace CreamInstaller
namespace CreamInstaller.Forms
{
partial class MainForm
{

View file

@ -1,13 +1,4 @@
using CreamInstaller.Components;
using CreamInstaller.Utility;
using HtmlAgilityPack;
using Onova;
using Onova.Models;
using Onova.Services;
using System;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
@ -16,19 +7,29 @@ using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Windows.Forms;
using CreamInstaller.Components;
using CreamInstaller.Utility;
using HtmlAgilityPack;
using Onova;
using Onova.Models;
using Onova.Services;
namespace CreamInstaller;
namespace CreamInstaller.Forms;
internal partial class MainForm : CustomForm
{
internal MainForm() : base()
private CancellationTokenSource cancellationTokenSource;
private Version latestVersion;
private UpdateManager updateManager;
private IReadOnlyList<Version> versions;
internal MainForm()
{
InitializeComponent();
Text = Program.ApplicationNameShort;
}
private CancellationTokenSource cancellationTokenSource;
private void StartProgram()
{
if (cancellationTokenSource is not null)
@ -49,10 +50,6 @@ internal partial class MainForm : CustomForm
#endif
}
private UpdateManager updateManager;
private Version latestVersion;
private IReadOnlyList<Version> versions;
private async void OnLoad()
{
progressBar.Visible = false;
@ -61,18 +58,22 @@ internal partial class MainForm : CustomForm
updateButton.Click -= OnUpdateCancel;
progressLabel.Text = "Checking for updates . . .";
changelogTreeView.Visible = false;
changelogTreeView.Location = new(progressLabel.Location.X, progressLabel.Location.Y + progressLabel.Size.Height + 13);
changelogTreeView.Location = new Point(progressLabel.Location.X,
progressLabel.Location.Y + progressLabel.Size.Height + 13);
Refresh();
#if DEBUG
DebugForm.Current.Attach(this);
#endif
GithubPackageResolver resolver = new(Program.RepositoryOwner, Program.RepositoryName, Program.RepositoryPackage);
GithubPackageResolver resolver = new(Program.RepositoryOwner, Program.RepositoryName,
Program.RepositoryPackage);
ZipPackageExtractor extractor = new();
updateManager = new(AssemblyMetadata.FromAssembly(Program.EntryAssembly, Program.CurrentProcessFilePath), resolver, extractor);
updateManager
= new UpdateManager(AssemblyMetadata.FromAssembly(Program.EntryAssembly, Program.CurrentProcessFilePath),
resolver, extractor);
if (latestVersion is null)
{
CheckForUpdatesResult checkForUpdatesResult = null;
cancellationTokenSource = new();
cancellationTokenSource = new CancellationTokenSource();
try
{
checkForUpdatesResult = await updateManager.CheckForUpdatesAsync(cancellationTokenSource.Token);
@ -80,8 +81,8 @@ internal partial class MainForm : CustomForm
if (checkForUpdatesResult.CanUpdate)
{
#endif
latestVersion = checkForUpdatesResult.LastVersion;
versions = checkForUpdatesResult.Versions;
latestVersion = checkForUpdatesResult.LastVersion;
versions = checkForUpdatesResult.Versions;
#if !DEBUG
}
#endif
@ -90,7 +91,8 @@ internal partial class MainForm : CustomForm
catch (TaskCanceledException) { }
catch (Exception e)
{
DebugForm.Current.Log($"Exception while checking for updates: {e.GetType()} ({e.Message})", LogTextBox.Warning);
DebugForm.Current.Log($"Exception while checking for updates: {e.GetType()} ({e.Message})",
LogTextBox.Warning);
}
#else
catch { }
@ -112,19 +114,17 @@ internal partial class MainForm : CustomForm
progressLabel.Text = $"An update is available: v{latestVersion}";
ignoreButton.Enabled = true;
updateButton.Enabled = true;
updateButton.Click += new(OnUpdate);
updateButton.Click += OnUpdate;
changelogTreeView.Visible = true;
Version currentVersion = new(Program.Version);
#if DEBUG
foreach (Version version in versions.Where(v => (v > currentVersion || v == latestVersion) && !changelogTreeView.Nodes.ContainsKey(v.ToString())))
foreach (Version version in versions.Where(v => (v > currentVersion || v == latestVersion)
&& !changelogTreeView.Nodes.ContainsKey(v.ToString())))
#else
foreach (Version version in versions.Where(v => v > currentVersion && !changelogTreeView.Nodes.ContainsKey(v.ToString())))
#endif
{
TreeNode root = new($"v{version}")
{
Name = version.ToString()
};
TreeNode root = new($"v{version}") { Name = version.ToString() };
changelogTreeView.Nodes.Add(root);
if (changelogTreeView.Nodes.Count > 0)
changelogTreeView.Nodes[0].EnsureVisible();
@ -137,19 +137,14 @@ internal partial class MainForm : CustomForm
changelogTreeView.Nodes.Remove(root);
else
foreach (HtmlNode node in nodes)
{
changelogTreeView.Invoke(delegate
{
TreeNode change = new()
{
Text = HttpUtility.HtmlDecode(node.InnerText)
};
TreeNode change = new() { Text = HttpUtility.HtmlDecode(node.InnerText) };
root.Nodes.Add(change);
root.Expand();
if (changelogTreeView.Nodes.Count > 0)
changelogTreeView.Nodes[0].EnsureVisible();
});
}
});
}
}
@ -157,7 +152,7 @@ internal partial class MainForm : CustomForm
private void OnLoad(object sender, EventArgs _)
{
retry:
retry:
try
{
string FileName = Path.GetFileName(Program.CurrentProcessFilePath);
@ -165,9 +160,9 @@ internal partial class MainForm : CustomForm
{
using DialogForm form = new(this);
if (form.Show(SystemIcons.Warning,
"WARNING: " + Program.ApplicationExecutable + " was renamed!" +
"\n\nThis will cause undesirable behavior when updating the program!",
"Ignore", "Abort") == DialogResult.Cancel)
"WARNING: " + Program.ApplicationExecutable + " was renamed!" +
"\n\nThis will cause undesirable behavior when updating the program!",
"Ignore", "Abort") == DialogResult.Cancel)
{
Application.Exit();
return;
@ -177,7 +172,7 @@ internal partial class MainForm : CustomForm
}
catch (Exception e)
{
if (e.HandleException(form: this)) goto retry;
if (e.HandleException(this)) goto retry;
Close();
}
}
@ -190,19 +185,18 @@ internal partial class MainForm : CustomForm
ignoreButton.Visible = false;
updateButton.Text = "Cancel";
updateButton.Click -= OnUpdate;
updateButton.Click += new(OnUpdateCancel);
changelogTreeView.Location = new(progressBar.Location.X, progressBar.Location.Y + progressBar.Size.Height + 6);
updateButton.Click += OnUpdateCancel;
changelogTreeView.Location
= new Point(progressBar.Location.X, progressBar.Location.Y + progressBar.Size.Height + 6);
Refresh();
Progress<double> progress = new();
progress.ProgressChanged += new(delegate (object sender, double _progress)
progress.ProgressChanged += delegate(object sender, double _progress)
{
progressLabel.Text = $"Updating . . . {(int)_progress}%";
progressBar.Value = (int)_progress;
});
};
progressLabel.Text = "Updating . . . ";
cancellationTokenSource = new();
cancellationTokenSource = new CancellationTokenSource();
try
{
await updateManager.PrepareUpdateAsync(latestVersion, progress, cancellationTokenSource.Token);
@ -211,7 +205,8 @@ internal partial class MainForm : CustomForm
catch (TaskCanceledException) { }
catch (Exception ex)
{
DebugForm.Current.Log($"Exception while preparing update: {ex.GetType()} ({ex.Message})", LogTextBox.Warning);
DebugForm.Current.Log($"Exception while preparing update: {ex.GetType()} ({ex.Message})",
LogTextBox.Warning);
}
#else
catch { }
@ -221,14 +216,13 @@ internal partial class MainForm : CustomForm
cancellationTokenSource.Dispose();
cancellationTokenSource = null;
}
if (updateManager is not null && updateManager.IsUpdatePrepared(latestVersion))
{
updateManager.LaunchUpdater(latestVersion);
Application.Exit();
return;
}
else OnLoad();
OnLoad();
}
private void OnUpdateCancel(object sender, EventArgs e)

View file

@ -1,10 +1,8 @@

using System.ComponentModel;
using System.Windows.Forms;
using CreamInstaller.Components;
using System.ComponentModel;
using System.Windows.Forms;
namespace CreamInstaller
namespace CreamInstaller.Forms
{
partial class SelectDialogForm
{

View file

@ -1,20 +1,19 @@
using CreamInstaller.Components;
using CreamInstaller.Utility;
using System;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using CreamInstaller.Components;
using CreamInstaller.Utility;
namespace CreamInstaller;
namespace CreamInstaller.Forms;
internal partial class SelectDialogForm : CustomForm
{
private readonly List<(Platform platform, string id, string name)> selected = new();
internal SelectDialogForm(IWin32Window owner) : base(owner) => InitializeComponent();
private readonly List<(Platform platform, string id, string name)> selected = new();
internal List<(Platform platform, string id, string name)> QueryUser(string groupBoxText, List<(Platform platform, string id, string name, bool alreadySelected)> choices)
internal List<(Platform platform, string id, string name)> QueryUser(
string groupBoxText, List<(Platform platform, string id, string name, bool alreadySelected)> choices)
{
if (!choices.Any()) return null;
groupBox.Text = groupBoxText;
@ -23,13 +22,7 @@ internal partial class SelectDialogForm : CustomForm
selectionTreeView.AfterCheck += OnTreeNodeChecked;
foreach ((Platform platform, string id, string name, bool alreadySelected) in choices)
{
TreeNode node = new()
{
Tag = platform,
Name = id,
Text = name,
Checked = alreadySelected
};
TreeNode node = new() { Tag = platform, Name = id, Text = name, Checked = alreadySelected };
OnTreeNodeChecked(node);
_ = selectionTreeView.Nodes.Add(node);
}

View file

@ -1,10 +1,8 @@
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using CreamInstaller.Components;
namespace CreamInstaller
namespace CreamInstaller.Forms
{
partial class SelectForm
{

View file

@ -1,15 +1,5 @@
#pragma warning disable IDE0058
using CreamInstaller.Components;
using CreamInstaller.Platforms.Epic;
using CreamInstaller.Platforms.Paradox;
using CreamInstaller.Platforms.Steam;
using CreamInstaller.Platforms.Ubisoft;
using CreamInstaller.Resources;
using CreamInstaller.Utility;
using Gameloop.Vdf.Linq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -20,26 +10,45 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using CreamInstaller.Components;
using CreamInstaller.Platforms.Epic;
using CreamInstaller.Platforms.Paradox;
using CreamInstaller.Platforms.Steam;
using CreamInstaller.Platforms.Ubisoft;
using CreamInstaller.Resources;
using CreamInstaller.Utility;
using Gameloop.Vdf.Linq;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller;
namespace CreamInstaller.Forms;
internal partial class SelectForm : CustomForm
{
internal SelectForm() : base()
private readonly string helpButtonListPrefix = "\n • ";
private readonly SynchronizedCollection<string> RemainingDLCs = new();
private readonly SynchronizedCollection<string> RemainingGames = new();
private List<(Platform platform, string id, string name)> ProgramsToScan;
internal SelectForm()
{
InitializeComponent();
Text = Program.ApplicationName;
}
public override ContextMenuStrip ContextMenuStrip => base.ContextMenuStrip ??= new();
public override ContextMenuStrip ContextMenuStrip => base.ContextMenuStrip ??= new ContextMenuStrip();
internal List<TreeNode> TreeNodes => GatherTreeNodes(selectionTreeView.Nodes);
private static void UpdateRemaining(Label label, SynchronizedCollection<string> list, string descriptor) =>
label.Text = list.Any() ? $"Remaining {descriptor} ({list.Count}): " + string.Join(", ", list).Replace("&", "&&") : "";
label.Text = list.Any()
? $"Remaining {descriptor} ({list.Count}): " + string.Join(", ", list).Replace("&", "&&")
: "";
private readonly SynchronizedCollection<string> RemainingGames = new();
private void UpdateRemainingGames() => UpdateRemaining(progressLabelGames, RemainingGames, "games");
private void AddToRemainingGames(string gameName)
{
if (Program.Canceled) return;
@ -51,6 +60,7 @@ internal partial class SelectForm : CustomForm
UpdateRemainingGames();
});
}
private void RemoveFromRemainingGames(string gameName)
{
if (Program.Canceled) return;
@ -62,8 +72,8 @@ internal partial class SelectForm : CustomForm
});
}
private readonly SynchronizedCollection<string> RemainingDLCs = new();
private void UpdateRemainingDLCs() => UpdateRemaining(progressLabelDLCs, RemainingDLCs, "DLCs");
private void AddToRemainingDLCs(string dlcId)
{
if (Program.Canceled) return;
@ -75,6 +85,7 @@ internal partial class SelectForm : CustomForm
UpdateRemainingDLCs();
});
}
private void RemoveFromRemainingDLCs(string dlcId)
{
if (Program.Canceled) return;
@ -109,21 +120,23 @@ internal partial class SelectForm : CustomForm
List<Task> appTasks = new();
if (ProgramsToScan.Any(c => c.platform is Platform.Paradox))
{
List<string> dllDirectories = await ParadoxLauncher.InstallPath.GetDllDirectoriesFromGameDirectory(Platform.Paradox);
List<string> dllDirectories
= await ParadoxLauncher.InstallPath.GetDllDirectoriesFromGameDirectory(Platform.Paradox);
if (dllDirectories is not null)
{
ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Paradox, "PL");
selection ??= new();
selection ??= new ProgramSelection();
if (allCheckBox.Checked) selection.Enabled = true;
if (koaloaderAllCheckBox.Checked) selection.Koaloader = true;
selection.Id = "PL";
selection.Name = "Paradox Launcher";
selection.RootDirectory = ParadoxLauncher.InstallPath;
selection.ExecutableDirectories = await ParadoxLauncher.GetExecutableDirectories(selection.RootDirectory);
selection.ExecutableDirectories
= await ParadoxLauncher.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Paradox;
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Paradox && s.Name == selection.Id) ?? new();
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Paradox && s.Name == selection.Id)
?? new TreeNode();
programNode.Tag = selection.Platform;
programNode.Name = selection.Id;
programNode.Text = selection.Name;
@ -135,12 +148,14 @@ internal partial class SelectForm : CustomForm
int steamGamesToCheck;
if (ProgramsToScan.Any(c => c.platform is Platform.Steam))
{
List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = await SteamLibrary.GetGames();
List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames
= await SteamLibrary.GetGames();
steamGamesToCheck = steamGames.Count;
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in steamGames)
{
if (Program.Canceled) return;
if (Program.IsGameBlocked(name, gameDirectory) || !ProgramsToScan.Any(c => c.platform is Platform.Steam && c.id == appId))
if (Program.IsGameBlocked(name, gameDirectory)
|| !ProgramsToScan.Any(c => c.platform is Platform.Steam && c.id == appId))
{
Interlocked.Decrement(ref steamGamesToCheck);
continue;
@ -149,7 +164,8 @@ internal partial class SelectForm : CustomForm
Task task = Task.Run(async () =>
{
if (Program.Canceled) return;
List<string> dllDirectories = await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Steam);
List<string> dllDirectories
= await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Steam);
if (dllDirectories is null)
{
Interlocked.Decrement(ref steamGamesToCheck);
@ -199,11 +215,15 @@ internal partial class SelectForm : CustomForm
if (dlcAppInfo is not null)
{
dlcName = dlcAppInfo.Value?.GetChild("common")?.GetChild("name")?.ToString();
string dlcIconStaticId = dlcAppInfo.Value?.GetChild("common")?.GetChild("icon")?.ToString();
dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo_small")?.ToString();
dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo")?.ToString();
string dlcIconStaticId = dlcAppInfo.Value?.GetChild("common")?.GetChild("icon")
?.ToString();
dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo_small")
?.ToString();
dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo")
?.ToString();
if (dlcIconStaticId is not null)
dlcIcon = IconGrabber.SteamAppImagesPath + @$"\{dlcAppId}\{dlcIconStaticId}.jpg";
dlcIcon = IconGrabber.SteamAppImagesPath
+ @$"\{dlcAppId}\{dlcIconStaticId}.jpg";
}
}
if (Program.Canceled) return;
@ -227,27 +247,32 @@ internal partial class SelectForm : CustomForm
await task;
}
steamGamesToCheck = 0;
ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Steam, appId) ?? new();
selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any();
ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Steam, appId)
?? new ProgramSelection();
selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any()
|| selection.ExtraSelectedDlc.Any();
if (koaloaderAllCheckBox.Checked) selection.Koaloader = true;
selection.Id = appId;
selection.Name = appData?.name ?? name;
selection.RootDirectory = gameDirectory;
selection.ExecutableDirectories = await SteamLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.ExecutableDirectories
= await SteamLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Steam;
selection.ProductUrl = "https://store.steampowered.com/app/" + appId;
selection.IconUrl = IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("icon")}.jpg";
selection.SubIconUrl = appData?.header_image ?? IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("clienticon")}.ico";
selection.Publisher = appData?.publishers[0] ?? appInfo?.Value?.GetChild("extended")?.GetChild("publisher")?.ToString();
selection.IconUrl = IconGrabber.SteamAppImagesPath
+ @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("icon")}.jpg";
selection.SubIconUrl = appData?.header_image ?? IconGrabber.SteamAppImagesPath
+ @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("clienticon")}.ico";
selection.Publisher = appData?.publishers[0]
?? appInfo?.Value?.GetChild("extended")?.GetChild("publisher")?.ToString();
selection.WebsiteUrl = appData?.website;
if (Program.Canceled) return;
selectionTreeView.Invoke(delegate
{
if (Program.Canceled) return;
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == appId) ?? new();
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == appId)
?? new TreeNode();
programNode.Tag = selection.Platform;
programNode.Name = appId;
programNode.Text = appData?.name ?? name;
@ -261,7 +286,8 @@ internal partial class SelectForm : CustomForm
(DlcType type, string name, string icon) dlcApp = pair.Value;
selection.AllDlc[appId] = dlcApp;
if (allCheckBox.Checked && dlcApp.name != "Unknown") selection.SelectedDlc[appId] = dlcApp;
TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == appId) ?? new();
TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == appId)
?? new TreeNode();
dlcNode.Tag = selection.Platform;
dlcNode.Name = appId;
dlcNode.Text = dlcApp.name;
@ -285,7 +311,8 @@ internal partial class SelectForm : CustomForm
string name = manifest.DisplayName;
string directory = manifest.InstallLocation;
if (Program.Canceled) return;
if (Program.IsGameBlocked(name, directory) || !ProgramsToScan.Any(c => c.platform is Platform.Epic && c.id == @namespace)) continue;
if (Program.IsGameBlocked(name, directory)
|| !ProgramsToScan.Any(c => c.platform is Platform.Epic && c.id == @namespace)) continue;
AddToRemainingGames(name);
Task task = Task.Run(async () =>
{
@ -297,12 +324,14 @@ internal partial class SelectForm : CustomForm
return;
}
if (Program.Canceled) return;
ConcurrentDictionary<string, (string name, string product, string icon, string developer)> entitlements = new();
ConcurrentDictionary<string, (string name, string product, string icon, string developer)>
entitlements = new();
List<Task> dlcTasks = new();
List<(string id, string name, string product, string icon, string developer)> entitlementIds = await EpicStore.QueryEntitlements(@namespace);
List<(string id, string name, string product, string icon, string developer)> entitlementIds
= await EpicStore.QueryEntitlements(@namespace);
if (entitlementIds.Any())
{
foreach ((string id, string name, string product, string icon, string developer) in entitlementIds)
foreach ((string id, string name, string product, string icon, string developer) in
entitlementIds)
{
if (Program.Canceled) return;
AddToRemainingDLCs(id);
@ -314,8 +343,7 @@ internal partial class SelectForm : CustomForm
});
dlcTasks.Add(task);
}
}
if (/*!catalogItems.Any() && */!entitlements.Any())
if ( /*!catalogItems.Any() && */!entitlements.Any())
{
RemoveFromRemainingGames(name);
return;
@ -326,28 +354,31 @@ internal partial class SelectForm : CustomForm
if (Program.Canceled) return;
await task;
}
ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Epic, @namespace) ?? new();
selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any();
ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Epic, @namespace)
?? new ProgramSelection();
selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any()
|| selection.ExtraSelectedDlc.Any();
if (koaloaderAllCheckBox.Checked) selection.Koaloader = true;
selection.Id = @namespace;
selection.Name = name;
selection.RootDirectory = directory;
selection.ExecutableDirectories = await EpicLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.ExecutableDirectories
= await EpicLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Epic;
foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> pair in entitlements.Where(p => p.Value.name == selection.Name))
foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> pair in
entitlements.Where(p => p.Value.name == selection.Name))
{
selection.ProductUrl = "https://www.epicgames.com/store/product/" + pair.Value.product;
selection.IconUrl = pair.Value.icon;
selection.Publisher = pair.Value.developer;
}
if (Program.Canceled) return;
selectionTreeView.Invoke(delegate
{
if (Program.Canceled) return;
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == @namespace) ?? new();
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == @namespace)
?? new TreeNode();
programNode.Tag = selection.Platform;
programNode.Name = @namespace;
programNode.Text = name;
@ -362,7 +393,6 @@ internal partial class SelectForm : CustomForm
if (catalogItemsNode.Parent is null)
programNode.Nodes.Add(catalogItemsNode);*/
if (entitlements.Any())
{
/*TreeNode entitlementsNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == @namespace + "_entitlements") ?? new();
entitlementsNode.Tag = selection.Platform;
entitlementsNode.Name = @namespace + "_entitlements";
@ -370,14 +400,17 @@ internal partial class SelectForm : CustomForm
entitlementsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.Entitlement);
if (entitlementsNode.Parent is null)
programNode.Nodes.Add(entitlementsNode);*/
foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> pair in entitlements)
foreach (KeyValuePair<string, (string name, string product, string icon, string developer)>
pair in entitlements)
{
if (programNode is null/* || entitlementsNode is null*/) return;
if (programNode is null /* || entitlementsNode is null*/) return;
string dlcId = pair.Key;
(DlcType type, string name, string icon) dlcApp = (DlcType.EpicEntitlement, pair.Value.name, pair.Value.icon);
(DlcType type, string name, string icon) dlcApp = (
DlcType.EpicEntitlement, pair.Value.name, pair.Value.icon);
selection.AllDlc[dlcId] = dlcApp;
if (allCheckBox.Checked) selection.SelectedDlc[dlcId] = dlcApp;
TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == dlcId) ?? new();
TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == dlcId)
?? new TreeNode();
dlcNode.Tag = selection.Platform;
dlcNode.Name = dlcId;
dlcNode.Text = dlcApp.name;
@ -385,7 +418,6 @@ internal partial class SelectForm : CustomForm
if (dlcNode.Parent is null)
_ = programNode.Nodes.Add(dlcNode); //entitlementsNode.Nodes.Add(dlcNode);
}
}
});
if (Program.Canceled) return;
RemoveFromRemainingGames(name);
@ -399,34 +431,38 @@ internal partial class SelectForm : CustomForm
foreach ((string gameId, string name, string gameDirectory) in ubisoftGames)
{
if (Program.Canceled) return;
if (Program.IsGameBlocked(name, gameDirectory) || !ProgramsToScan.Any(c => c.platform is Platform.Ubisoft && c.id == gameId)) continue;
if (Program.IsGameBlocked(name, gameDirectory)
|| !ProgramsToScan.Any(c => c.platform is Platform.Ubisoft && c.id == gameId)) continue;
AddToRemainingGames(name);
Task task = Task.Run(async () =>
{
if (Program.Canceled) return;
List<string> dllDirectories = await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Ubisoft);
List<string> dllDirectories
= await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Ubisoft);
if (dllDirectories is null)
{
RemoveFromRemainingGames(name);
return;
}
if (Program.Canceled) return;
ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Ubisoft, gameId) ?? new();
selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any();
ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Ubisoft, gameId)
?? new ProgramSelection();
selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any()
|| selection.ExtraSelectedDlc.Any();
if (koaloaderAllCheckBox.Checked) selection.Koaloader = true;
selection.Id = gameId;
selection.Name = name;
selection.RootDirectory = gameDirectory;
selection.ExecutableDirectories = await UbisoftLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.ExecutableDirectories
= await UbisoftLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Ubisoft;
selection.IconUrl = IconGrabber.GetDomainFaviconUrl("store.ubi.com");
selectionTreeView.Invoke(delegate
{
if (Program.Canceled) return;
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Ubisoft && s.Name == gameId) ?? new();
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Ubisoft && s.Name == gameId)
?? new TreeNode();
programNode.Tag = selection.Platform;
programNode.Name = gameId;
programNode.Text = name;
@ -448,7 +484,6 @@ internal partial class SelectForm : CustomForm
steamGamesToCheck = 0;
}
private List<(Platform platform, string id, string name)> ProgramsToScan;
private async void OnLoad(bool forceScan = false, bool forceProvideChoices = false)
{
Program.Canceled = false;
@ -470,27 +505,37 @@ internal partial class SelectForm : CustomForm
resetKoaloaderButton.Enabled = false;
progressLabel.Text = "Waiting for user to select which programs/games to scan . . .";
ShowProgressBar();
await ProgramData.Setup();
bool scan = forceScan;
if (!scan && (ProgramsToScan is null || !ProgramsToScan.Any() || forceProvideChoices))
{
List<(Platform platform, string id, string name, bool alreadySelected)> gameChoices = new();
if (Directory.Exists(ParadoxLauncher.InstallPath))
gameChoices.Add((Platform.Paradox, "PL", "Paradox Launcher", ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Paradox && p.id == "PL")));
gameChoices.Add((Platform.Paradox, "PL", "Paradox Launcher",
ProgramsToScan is not null
&& ProgramsToScan.Any(p => p.platform is Platform.Paradox && p.id == "PL")));
if (Directory.Exists(SteamLibrary.InstallPath))
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in (await SteamLibrary.GetGames()).Where(g => !Program.IsGameBlocked(g.name, g.gameDirectory)))
gameChoices.Add((Platform.Steam, appId, name, ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Steam && p.id == appId)));
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in
(await SteamLibrary.GetGames()).Where(g => !Program.IsGameBlocked(g.name, g.gameDirectory)))
gameChoices.Add((Platform.Steam, appId, name,
ProgramsToScan is not null
&& ProgramsToScan.Any(p => p.platform is Platform.Steam && p.id == appId)));
if (Directory.Exists(EpicLibrary.EpicManifestsPath))
foreach (Manifest manifest in (await EpicLibrary.GetGames()).Where(m => !Program.IsGameBlocked(m.DisplayName, m.InstallLocation)))
gameChoices.Add((Platform.Epic, manifest.CatalogNamespace, manifest.DisplayName, ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Epic && p.id == manifest.CatalogNamespace)));
foreach ((string gameId, string name, string gameDirectory) in (await UbisoftLibrary.GetGames()).Where(g => !Program.IsGameBlocked(g.name, g.gameDirectory)))
gameChoices.Add((Platform.Ubisoft, gameId, name, ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Ubisoft && p.id == gameId)));
foreach (Manifest manifest in (await EpicLibrary.GetGames()).Where(
m => !Program.IsGameBlocked(m.DisplayName, m.InstallLocation)))
gameChoices.Add((Platform.Epic, manifest.CatalogNamespace, manifest.DisplayName,
ProgramsToScan is not null && ProgramsToScan.Any(
p => p.platform is Platform.Epic && p.id == manifest.CatalogNamespace)));
foreach ((string gameId, string name, string gameDirectory) in (await UbisoftLibrary.GetGames()).Where(
g => !Program.IsGameBlocked(g.name, g.gameDirectory)))
gameChoices.Add((Platform.Ubisoft, gameId, name,
ProgramsToScan is not null
&& ProgramsToScan.Any(p => p.platform is Platform.Ubisoft && p.id == gameId)));
if (gameChoices.Any())
{
using SelectDialogForm form = new(this);
List<(Platform platform, string id, string name)> choices = form.QueryUser("Choose which programs and/or games to scan for DLC:", gameChoices);
List<(Platform platform, string id, string name)> choices
= form.QueryUser("Choose which programs and/or games to scan for DLC:", gameChoices);
scan = choices is not null && choices.Any();
string retry = "\n\nPress the \"Rescan Programs / Games\" button to re-choose.";
if (scan)
@ -499,12 +544,15 @@ internal partial class SelectForm : CustomForm
noneFoundLabel.Text = "None of the chosen programs nor games were applicable!" + retry;
}
else
{
noneFoundLabel.Text = "You didn't choose any programs nor games!" + retry;
}
}
else
{
noneFoundLabel.Text = "No applicable programs nor games were found on your computer!";
}
}
if (scan)
{
bool setup = true;
@ -518,13 +566,15 @@ internal partial class SelectForm : CustomForm
if (_progress < 0 || _progress > maxProgress) maxProgress = -_progress;
else curProgress = _progress;
int p = Math.Max(Math.Min((int)((float)curProgress / maxProgress * 100), 100), 0);
progressLabel.Text = setup ? $"Setting up SteamCMD . . . {p}%"
progressLabel.Text = setup
? $"Setting up SteamCMD . . . {p}%"
: $"Gathering and caching your applicable games and their DLCs . . . {p}%";
progressBar.Value = p;
};
if (Directory.Exists(SteamLibrary.InstallPath) && ProgramsToScan is not null && ProgramsToScan.Any(c => c.platform is Platform.Steam))
if (Directory.Exists(SteamLibrary.InstallPath) && ProgramsToScan is not null
&& ProgramsToScan.Any(c => c.platform is Platform.Steam))
{
progressLabel.Text = $"Setting up SteamCMD . . . ";
progressLabel.Text = "Setting up SteamCMD . . . ";
await SteamCMD.Setup(iProgress);
}
setup = false;
@ -542,10 +592,8 @@ internal partial class SelectForm : CustomForm
await GetApplicablePrograms(iProgress);
await SteamCMD.Cleanup();
}
OnLoadDlc(null, null);
OnLoadKoaloader(null, null);
HideProgressBar();
selectionTreeView.Enabled = ProgramSelection.All.Any();
allCheckBox.Enabled = selectionTreeView.Enabled;
@ -605,7 +653,8 @@ internal partial class SelectForm : CustomForm
{
string id = node.Name;
Platform platform = (Platform)node.Tag;
(string gameId, (DlcType type, string name, string icon) app)? dlc = ProgramSelection.GetDlcFromPlatformId(platform, id);
(string gameId, (DlcType type, string name, string icon) app)? dlc
= ProgramSelection.GetDlcFromPlatformId(platform, id);
if (dlc.HasValue)
{
(string gameId, _) = dlc.Value;
@ -620,7 +669,6 @@ internal partial class SelectForm : CustomForm
}
}
internal List<TreeNode> TreeNodes => GatherTreeNodes(selectionTreeView.Nodes);
private List<TreeNode> GatherTreeNodes(TreeNodeCollection nodeCollection)
{
List<TreeNode> treeNodes = new();
@ -642,12 +690,13 @@ internal partial class SelectForm : CustomForm
progressLabelDLCs.Text = "";
progressLabelDLCs.Visible = true;
progressBar.Visible = true;
programsGroupBox.Size = new(programsGroupBox.Size.Width, programsGroupBox.Size.Height - 3
- progressLabel.Size.Height
- progressLabelGames.Size.Height
- progressLabelDLCs.Size.Height
- progressBar.Size.Height);
programsGroupBox.Size = new Size(programsGroupBox.Size.Width, programsGroupBox.Size.Height - 3
- progressLabel.Size.Height
- progressLabelGames.Size.Height
- progressLabelDLCs.Size.Height
- progressBar.Size.Height);
}
private void HideProgressBar()
{
progressBar.Value = 100;
@ -655,11 +704,11 @@ internal partial class SelectForm : CustomForm
progressLabelGames.Visible = false;
progressLabelDLCs.Visible = false;
progressBar.Visible = false;
programsGroupBox.Size = new(programsGroupBox.Size.Width, programsGroupBox.Size.Height + 3
+ progressLabel.Size.Height
+ progressLabelGames.Size.Height
+ progressLabelDLCs.Size.Height
+ progressBar.Size.Height);
programsGroupBox.Size = new Size(programsGroupBox.Size.Width, programsGroupBox.Size.Height + 3
+ progressLabel.Size.Height
+ progressLabelGames.Size.Height
+ progressLabelDLCs.Size.Height
+ progressBar.Size.Height);
}
internal void OnNodeRightClick(TreeNode node, Point location) => Invoke(() =>
@ -683,11 +732,11 @@ internal partial class SelectForm : CustomForm
return;
ContextMenuItem header = null;
if (id == "PL")
header = new(node.Text, "Paradox Launcher");
header = new ContextMenuItem(node.Text, "Paradox Launcher");
else if (selection is not null)
header = new(node.Text, (id, selection.IconUrl));
header = new ContextMenuItem(node.Text, (id, selection.IconUrl));
else if (dlc is not null && dlcParentSelection is not null)
header = new(node.Text, (id, dlc.Value.app.icon), (id, dlcParentSelection.IconUrl));
header = new ContextMenuItem(node.Text, (id, dlc.Value.app.icon), (id, dlcParentSelection.IconUrl));
items.Add(header ?? new ContextMenuItem(node.Text));
string appInfoVDF = $@"{SteamCMD.AppInfoPath}\{id}.vdf";
string appInfoJSON = $@"{SteamCMD.AppInfoPath}\{id}.json";
@ -697,39 +746,42 @@ internal partial class SelectForm : CustomForm
List<ContextMenuItem> queries = new();
if (File.Exists(appInfoJSON))
{
string platformString = (selection is null || selection.Platform is Platform.Steam) ? "Steam Store "
: selection.Platform is Platform.Epic ? "Epic GraphQL " : "";
string platformString = selection is null || selection.Platform is Platform.Steam
? "Steam Store "
: selection.Platform is Platform.Epic
? "Epic GraphQL "
: "";
queries.Add(new ContextMenuItem($"Open {platformString}Query", "Notepad",
new EventHandler((sender, e) => Diagnostics.OpenFileInNotepad(appInfoJSON))));
(sender, e) => Diagnostics.OpenFileInNotepad(appInfoJSON)));
}
if (File.Exists(appInfoVDF))
queries.Add(new ContextMenuItem("Open SteamCMD Query", "Notepad",
new EventHandler((sender, e) => Diagnostics.OpenFileInNotepad(appInfoVDF))));
(sender, e) => Diagnostics.OpenFileInNotepad(appInfoVDF)));
if (queries.Any())
{
items.Add(new ToolStripSeparator());
foreach (ContextMenuItem query in queries)
items.Add(query);
items.Add(new ContextMenuItem("Refresh Queries", "Command Prompt",
new EventHandler((sender, e) =>
{
try
{
File.Delete(appInfoVDF);
}
catch { }
try
{
File.Delete(appInfoJSON);
}
catch { }
try
{
File.Delete(cooldown);
}
catch { }
OnLoad(forceScan: true);
})));
(sender, e) =>
{
try
{
File.Delete(appInfoVDF);
}
catch { }
try
{
File.Delete(appInfoJSON);
}
catch { }
try
{
File.Delete(cooldown);
}
catch { }
OnLoad(true);
}));
}
}
if (selection is not null)
@ -738,89 +790,109 @@ internal partial class SelectForm : CustomForm
{
items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Repair", "Command Prompt",
new EventHandler(async (sender, e) => await ParadoxLauncher.Repair(this, selection))));
async (sender, e) => await ParadoxLauncher.Repair(this, selection)));
}
items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open Root Directory", "File Explorer",
new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(selection.RootDirectory))));
(sender, e) => Diagnostics.OpenDirectoryInFileExplorer(
selection.RootDirectory)));
int executables = 0;
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories.ToList())
{
items.Add(new ContextMenuItem($"Open Executable Directory #{++executables} ({(binaryType == BinaryType.BIT32 ? "32" : "64")}-bit)", "File Explorer",
new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory))));
}
items.Add(new ContextMenuItem(
$"Open Executable Directory #{++executables} ({(binaryType == BinaryType.BIT32 ? "32" : "64")}-bit)",
"File Explorer",
(sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)));
List<string> directories = selection.DllDirectories.ToList();
int steam = 0, epic = 0, r1 = 0, r2 = 0;
if (selection.Platform is Platform.Steam or Platform.Paradox)
foreach (string directory in directories)
{
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string cache);
if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config) || File.Exists(cache))
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64,
out string api64_o, out string config, out string cache);
if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o)
|| File.Exists(config) || File.Exists(cache))
items.Add(new ContextMenuItem($"Open Steamworks Directory #{++steam}", "File Explorer",
new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory))));
(sender, e) => Diagnostics
.OpenDirectoryInFileExplorer(directory)));
}
if (selection.Platform is Platform.Epic or Platform.Paradox)
foreach (string directory in directories)
{
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.Exists(api64) || File.Exists(api64_o) || File.Exists(config))
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.Exists(api64) || File.Exists(api64_o)
|| File.Exists(config))
items.Add(new ContextMenuItem($"Open EOS Directory #{++epic}", "File Explorer",
new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory))));
(sender, e) => Diagnostics
.OpenDirectoryInFileExplorer(directory)));
}
if (selection.Platform is Platform.Ubisoft)
foreach (string directory in directories)
{
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.Exists(api64) || File.Exists(api64_o) || File.Exists(config))
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.Exists(api64) || File.Exists(api64_o)
|| File.Exists(config))
items.Add(new ContextMenuItem($"Open Uplay R1 Directory #{++r1}", "File Explorer",
new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory))));
directory.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))
(sender, e) => Diagnostics
.OpenDirectoryInFileExplorer(directory)));
directory.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))
items.Add(new ContextMenuItem($"Open Uplay R2 Directory #{++r2}", "File Explorer",
new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory))));
(sender, e) => Diagnostics
.OpenDirectoryInFileExplorer(directory)));
}
}
if (id != "PL")
{
if (selection is not null && selection.Platform is Platform.Steam
|| dlcParentSelection is not null && dlcParentSelection.Platform is Platform.Steam)
if ((selection is not null && selection.Platform is Platform.Steam)
|| (dlcParentSelection is not null && dlcParentSelection.Platform is Platform.Steam))
{
items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open SteamDB", "SteamDB",
new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamdb.info/app/" + id))));
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(
"https://steamdb.info/app/" + id)));
}
if (selection is not null)
{
if (selection.Platform is Platform.Steam)
{
items.Add(new ContextMenuItem("Open Steam Store", "Steam Store",
new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl))));
items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIconUrl), "Steam Community",
new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + id))));
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(
selection.ProductUrl)));
items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIconUrl),
"Steam Community",
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(
"https://steamcommunity.com/app/" + id)));
}
if (selection.Platform is Platform.Epic)
{
items.Add(new ToolStripSeparator());
items.Add(new ContextMenuItem("Open ScreamDB", "ScreamDB",
new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://scream-db.web.app/offers/" + id))));
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(
"https://scream-db.web.app/offers/" + id)));
items.Add(new ContextMenuItem("Open Epic Games Store", "Epic Games",
new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl))));
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(
selection.ProductUrl)));
}
if (selection.Platform is Platform.Ubisoft)
{
items.Add(new ToolStripSeparator());
#pragma warning disable CA1308 // Normalize strings to uppercase
items.Add(new ContextMenuItem("Open Ubisoft Store", "Ubisoft Store",
new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://store.ubi.com/us/" + selection.Name.Replace(" ", "-").ToLowerInvariant()))));
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(
"https://store.ubi.com/us/" + selection.Name.Replace(" ", "-")
.ToLowerInvariant())));
#pragma warning restore CA1308 // Normalize strings to uppercase
}
}
}
if (selection is not null && selection.WebsiteUrl is not null)
{
items.Add(new ContextMenuItem("Open Official Website", ("Web_" + id, IconGrabber.GetDomainFaviconUrl(selection.WebsiteUrl)),
new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.WebsiteUrl))));
}
items.Add(new ContextMenuItem("Open Official Website",
("Web_" + id, IconGrabber.GetDomainFaviconUrl(selection.WebsiteUrl)),
(sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.WebsiteUrl)));
contextMenuStrip.Show(selectionTreeView, location);
contextMenuStrip.Refresh();
ContextMenuStrip.Tag = null;
@ -828,7 +900,7 @@ internal partial class SelectForm : CustomForm
private void OnLoad(object sender, EventArgs _)
{
retry:
retry:
try
{
HideProgressBar();
@ -837,7 +909,7 @@ internal partial class SelectForm : CustomForm
}
catch (Exception e)
{
if (e.HandleException(form: this)) goto retry;
if (e.HandleException(this)) goto retry;
Close();
}
}
@ -847,7 +919,8 @@ internal partial class SelectForm : CustomForm
if (ProgramSelection.All.Any())
{
foreach (ProgramSelection selection in ProgramSelection.AllEnabled)
if (!Program.IsProgramRunningDialog(this, selection)) return;
if (!Program.IsProgramRunningDialog(this, selection))
return;
if (!uninstall && ParadoxLauncher.DlcDialog(this)) return;
Hide();
#pragma warning disable CA2000 // Dispose objects before losing scope
@ -865,7 +938,10 @@ internal partial class SelectForm : CustomForm
#endif
OnLoad();
}
else Close();
else
{
Close();
}
};
form.Show();
Hide();
@ -875,7 +951,7 @@ internal partial class SelectForm : CustomForm
}
}
private void OnInstall(object sender, EventArgs e) => OnAccept(false);
private void OnInstall(object sender, EventArgs e) => OnAccept();
private void OnUninstall(object sender, EventArgs e) => OnAccept(true);
@ -900,7 +976,7 @@ internal partial class SelectForm : CustomForm
if (node.Parent is null && node.Checked != shouldCheck)
{
node.Checked = shouldCheck;
OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
OnTreeViewNodeCheckedChanged(null, new TreeViewEventArgs(node, TreeViewAction.ByMouse));
}
allCheckBox.CheckedChanged -= OnAllCheckBoxChanged;
allCheckBox.Checked = shouldCheck;
@ -927,16 +1003,20 @@ internal partial class SelectForm : CustomForm
private bool AreSelectionsDefault()
{
foreach (TreeNode node in TreeNodes)
if (node.Parent is not null && node.Tag is Platform && (node.Text == "Unknown" ? node.Checked : !node.Checked))
if (node.Parent is not null && node.Tag is Platform
&& (node.Text == "Unknown" ? node.Checked : !node.Checked))
return false;
return true;
}
private bool CanSaveDlc() => installButton.Enabled && (ProgramData.ReadDlcChoices() is not null || !AreSelectionsDefault());
private bool CanSaveDlc()
=> installButton.Enabled && (ProgramData.ReadDlcChoices() is not null || !AreSelectionsDefault());
private void OnSaveDlc(object sender, EventArgs e)
{
List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices() ?? new();
List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices()
?? new List<(Platform platform, string gameId,
string dlcId)>();
foreach (TreeNode node in TreeNodes)
if (node.Parent is TreeNode parent && node.Tag is Platform platform)
{
@ -960,10 +1040,11 @@ internal partial class SelectForm : CustomForm
foreach (TreeNode node in TreeNodes)
if (node.Parent is TreeNode parent && node.Tag is Platform platform)
{
node.Checked = choices.Any(c => c.platform == platform && c.gameId == parent.Name && c.dlcId == node.Name)
? node.Text == "Unknown"
: node.Text != "Unknown";
OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
node.Checked
= choices.Any(c => c.platform == platform && c.gameId == parent.Name && c.dlcId == node.Name)
? node.Text == "Unknown"
: node.Text != "Unknown";
OnTreeViewNodeCheckedChanged(null, new TreeViewEventArgs(node, TreeViewAction.ByMouse));
}
}
@ -975,7 +1056,7 @@ internal partial class SelectForm : CustomForm
if (node.Parent is not null && node.Tag is Platform)
{
node.Checked = node.Text != "Unknown";
OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse));
OnTreeViewNodeCheckedChanged(null, new TreeViewEventArgs(node, TreeViewAction.ByMouse));
}
resetButton.Enabled = CanResetDlc();
}
@ -988,16 +1069,22 @@ internal partial class SelectForm : CustomForm
return true;
}
private static bool CanSaveKoaloader() => ProgramData.ReadKoaloaderChoices() is not null || !AreKoaloaderSelectionsDefault();
private static bool CanSaveKoaloader()
=> ProgramData.ReadKoaloaderChoices() is not null || !AreKoaloaderSelectionsDefault();
private void OnSaveKoaloader(object sender, EventArgs e)
{
List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices() ?? new();
List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices()
?? new List<(Platform platform, string id, string proxy, bool enabled)>();
foreach (ProgramSelection selection in ProgramSelection.AllSafe)
{
_ = choices.RemoveAll(c => c.platform == selection.Platform && c.id == selection.Id);
if (selection.KoaloaderProxy is not null and not ProgramSelection.DefaultKoaloaderProxy || !selection.Koaloader)
choices.Add((selection.Platform, selection.Id, selection.KoaloaderProxy == ProgramSelection.DefaultKoaloaderProxy ? null : selection.KoaloaderProxy, selection.Koaloader));
if (selection.KoaloaderProxy is not null and not ProgramSelection.DefaultKoaloaderProxy
|| !selection.Koaloader)
choices.Add((selection.Platform, selection.Id,
selection.KoaloaderProxy == ProgramSelection.DefaultKoaloaderProxy
? null
: selection.KoaloaderProxy, selection.Koaloader));
}
ProgramData.WriteKoaloaderProxyChoices(choices);
saveKoaloaderButton.Enabled = CanSaveKoaloader();
@ -1011,7 +1098,6 @@ internal partial class SelectForm : CustomForm
List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices();
if (choices is null) return;
foreach (ProgramSelection selection in ProgramSelection.AllSafe)
{
if (choices.Any(c => c.platform == selection.Platform && c.id == selection.Id))
{
(Platform platform, string id, string proxy, bool enabled) choice =
@ -1023,11 +1109,14 @@ internal partial class SelectForm : CustomForm
if (proxy != currentProxy && choices.Remove(choice)) // convert pre-v4.1.0.0 choices
choices.Add((platform, id, currentProxy, enabled));
if (currentProxy is null or ProgramSelection.DefaultKoaloaderProxy && enabled)
{
_ = choices.RemoveAll(c => c.platform == platform && c.id == id);
}
else
{
selection.Koaloader = enabled;
selection.KoaloaderProxy = currentProxy == ProgramSelection.DefaultKoaloaderProxy ? currentProxy : proxy;
selection.KoaloaderProxy
= currentProxy == ProgramSelection.DefaultKoaloaderProxy ? currentProxy : proxy;
}
}
else
@ -1035,7 +1124,6 @@ internal partial class SelectForm : CustomForm
selection.Koaloader = true;
selection.KoaloaderProxy = null;
}
}
ProgramData.WriteKoaloaderProxyChoices(choices);
loadKoaloaderButton.Enabled = CanLoadKoaloader();
OnKoaloaderChanged();
@ -1069,7 +1157,6 @@ internal partial class SelectForm : CustomForm
OnLoad(forceProvideChoices: true);
}
private readonly string helpButtonListPrefix = "\n • ";
private void OnBlockProtectedGamesHelpButtonClicked(object sender, EventArgs e)
{
StringBuilder blockedGames = new();
@ -1083,12 +1170,18 @@ internal partial class SelectForm : CustomForm
_ = blockedDirectoryExceptions.Append(helpButtonListPrefix + name);
using DialogForm form = new(this);
_ = form.Show(SystemIcons.Information,
"Blocks the program from caching and displaying games protected by anti-cheats." +
"\nYou disable this option and install DLC unlockers to protected games at your own risk!" +
"\n\nBlocked games: " + (string.IsNullOrWhiteSpace(blockedGames.ToString()) ? "(none)" : blockedGames) +
"\n\nBlocked game sub-directories: " + (string.IsNullOrWhiteSpace(blockedDirectories.ToString()) ? "(none)" : blockedDirectories) +
"\n\nBlocked game sub-directory exceptions: " + (string.IsNullOrWhiteSpace(blockedDirectoryExceptions.ToString()) ? "(none)" : blockedDirectoryExceptions),
"OK", customFormText: "Block Protected Games");
"Blocks the program from caching and displaying games protected by anti-cheats." +
"\nYou disable this option and install DLC unlockers to protected games at your own risk!" +
"\n\nBlocked games: "
+ (string.IsNullOrWhiteSpace(blockedGames.ToString()) ? "(none)" : blockedGames) +
"\n\nBlocked game sub-directories: " + (string.IsNullOrWhiteSpace(blockedDirectories.ToString())
? "(none)"
: blockedDirectories) +
"\n\nBlocked game sub-directory exceptions: "
+ (string.IsNullOrWhiteSpace(blockedDirectoryExceptions.ToString())
? "(none)"
: blockedDirectoryExceptions),
"OK", customFormText: "Block Protected Games");
}
private void OnSortCheckBoxChanged(object sender, EventArgs e) => selectionTreeView.TreeViewNodeSorter

View file

@ -1,14 +1,10 @@
using CreamInstaller.Resources;
using CreamInstaller.Utility;
using Microsoft.Win32;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using CreamInstaller.Utility;
using Microsoft.Win32;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Platforms.Epic;
@ -16,21 +12,32 @@ namespace CreamInstaller.Platforms.Epic;
internal static class EpicLibrary
{
private static string epicManifestsPath;
internal static string EpicManifestsPath
{
get
{
epicManifestsPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Epic Games\EOS", "ModSdkMetadataDir", null) as string;
epicManifestsPath ??= 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")) epicManifestsPath += @"\Manifests";
epicManifestsPath
??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Epic Games\EOS", "ModSdkMetadataDir",
null) as string;
epicManifestsPath
??= 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"))
epicManifestsPath += @"\Manifests";
return epicManifestsPath.BeautifyPath();
}
}
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(filterCommon: true));
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(
string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
internal static async Task<List<Manifest>> GetGames() => await Task.Run(() =>
{
@ -45,10 +52,12 @@ internal static class EpicLibrary
{
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.Any(g => g.CatalogItemId == manifest.CatalogItemId
&& g.InstallLocation == manifest.InstallLocation))
games.Add(manifest);
}
catch { };
catch { }
;
}
return games;
});

View file

@ -1,9 +1,4 @@
using CreamInstaller.Platforms.Epic.GraphQL;
using CreamInstaller.Utility;
using Newtonsoft.Json;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -11,6 +6,9 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
using CreamInstaller.Platforms.Epic.GraphQL;
using CreamInstaller.Utility;
using Newtonsoft.Json;
namespace CreamInstaller.Platforms.Epic;
@ -24,7 +22,9 @@ internal static class EpicStore
}*/
private const int COOLDOWN_ENTITLEMENT = 600;
internal static async Task<List<(string id, string name, string product, string icon, string developer)>> QueryEntitlements(string categoryNamespace)
internal static async Task<List<(string id, string name, string product, string icon, string developer)>>
QueryEntitlements(string categoryNamespace)
{
List<(string id, string name, string product, string icon, string developer)> dlcIds = new();
string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json";
@ -35,19 +35,21 @@ internal static class EpicStore
response = await QueryGraphQL(categoryNamespace);
try
{
File.WriteAllText(cacheFile, JsonConvert.SerializeObject(response, Formatting.Indented));
await File.WriteAllTextAsync(cacheFile, JsonConvert.SerializeObject(response, Formatting.Indented));
}
catch { }
}
else if (cachedExists)
{
try
{
response = JsonConvert.DeserializeObject<Response>(File.ReadAllText(cacheFile));
response = JsonConvert.DeserializeObject<Response>(await File.ReadAllTextAsync(cacheFile));
}
catch
{
File.Delete(cacheFile);
}
}
if (response is null)
return dlcIds;
List<Element> searchStore = new(response.Data.Catalog.SearchStore.Elements);
@ -55,7 +57,8 @@ internal static class EpicStore
{
string title = element.Title;
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;
for (int i = 0; i < element.KeyImages?.Length; i++)
{
@ -67,14 +70,15 @@ internal static class EpicStore
}
}
foreach (Item item in element.Items)
dlcIds.Populate(item.Id, title, product, icon, null, canOverwrite: element.Items.Length == 1);
dlcIds.Populate(item.Id, title, product, icon, null, element.Items.Length == 1);
}
List<Element> catalogOffers = new(response.Data.Catalog.CatalogOffers.Elements);
foreach (Element element in catalogOffers)
{
string title = element.Title;
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;
for (int i = 0; i < element.KeyImages?.Length; i++)
{
@ -86,12 +90,14 @@ internal static class EpicStore
}
}
foreach (Item item in element.Items)
dlcIds.Populate(item.Id, title, product, icon, item.Developer, canOverwrite: element.Items.Length == 1);
dlcIds.Populate(item.Id, title, product, icon, item.Developer, element.Items.Length == 1);
}
return dlcIds;
}
private static void Populate(this List<(string id, string name, string product, string icon, string developer)> dlcIds, string id, string title, string product, string icon, string developer, bool canOverwrite = false)
private static void Populate(
this List<(string id, string name, string product, string icon, string developer)> dlcIds, string id,
string title, string product, string icon, string developer, bool canOverwrite = false)
{
if (id == null) return;
bool found = false;
@ -121,7 +127,8 @@ internal static class EpicStore
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpClient client = HttpClientManager.HttpClient;
if (client is null) return null;
HttpResponseMessage httpResponse = 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();
string response = await httpResponse.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Response>(response);

View file

@ -64,7 +64,6 @@ internal class Request
{
[JsonProperty(PropertyName = "Content-Type")]
private string ContentType => "application/graphql";
}
private class Variables

View file

@ -1,15 +1,13 @@
#pragma warning disable CA1819 // Properties should not return arrays
using Newtonsoft.Json;
using System;
using Newtonsoft.Json;
namespace CreamInstaller.Platforms.Epic.GraphQL;
public class Response
{
[JsonProperty(PropertyName = "data")]
public ResponseData Data { get; protected set; }
[JsonProperty(PropertyName = "data")] public ResponseData Data { get; protected set; }
}
public class ResponseData
@ -35,17 +33,14 @@ public class ElementContainer
public class Element
{
[JsonProperty(PropertyName = "id")]
public string Id { get; protected set; }
[JsonProperty(PropertyName = "id")] public string Id { get; protected set; }
[JsonProperty(PropertyName = "title")]
public string Title { get; protected set; }
[JsonProperty(PropertyName = "title")] public string Title { get; protected set; }
[JsonProperty(PropertyName = "keyImages")]
public KeyImage[] KeyImages { get; protected set; }
[JsonProperty(PropertyName = "items")]
public Item[] Items { get; protected set; }
[JsonProperty(PropertyName = "items")] public Item[] Items { get; protected set; }
[JsonProperty(PropertyName = "catalogNs")]
public CatalogNs CatalogNs { get; protected set; }
@ -53,11 +48,9 @@ public class Element
public class Item
{
[JsonProperty(PropertyName = "id")]
public string Id { get; protected set; }
[JsonProperty(PropertyName = "id")] public string Id { get; protected set; }
[JsonProperty(PropertyName = "title")]
public string Title { get; protected set; }
[JsonProperty(PropertyName = "title")] public string Title { get; protected set; }
[JsonProperty(PropertyName = "developer")]
public string Developer { get; protected set; }
@ -65,11 +58,9 @@ public class Item
public class KeyImage
{
[JsonProperty(PropertyName = "type")]
public string Type { get; protected set; }
[JsonProperty(PropertyName = "type")] public string Type { get; protected set; }
[JsonProperty(PropertyName = "url")]
public Uri Url { get; protected set; }
[JsonProperty(PropertyName = "url")] public Uri Url { get; protected set; }
}
public class CatalogNs

View file

@ -1,34 +1,46 @@
using CreamInstaller.Resources;
using CreamInstaller.Utility;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using CreamInstaller.Forms;
using CreamInstaller.Resources;
using CreamInstaller.Utility;
using Microsoft.Win32;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Platforms.Paradox;
internal static class ParadoxLauncher
{
public enum RepairResult
{
ProgramRunning = -2, Failure, Unnecessary = 0,
Success
}
private static string installPath;
internal static string InstallPath
{
get
{
installPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Paradox Interactive\Paradox Launcher v2", "LauncherInstallation", null) as string;
installPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Wow6432Node\Paradox Interactive\Paradox Launcher v2", "LauncherInstallation", null) as string;
installPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Paradox Interactive\Paradox Launcher v2",
"LauncherInstallation", null) as string;
installPath ??= Registry.GetValue(
@"HKEY_CURRENT_USER\Software\Wow6432Node\Paradox Interactive\Paradox Launcher v2",
"LauncherInstallation", null) as string;
return installPath.BeautifyPath();
}
}
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(validFunc: path => !Path.GetFileName(path).Contains("bootstrapper")));
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(
string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(validFunc: path
=> !Path.GetFileName(path)
.Contains("bootstrapper")));
private static void PopulateDlc(ProgramSelection paradoxLauncher = null)
{
@ -37,16 +49,26 @@ internal static class ParadoxLauncher
{
paradoxLauncher.ExtraDlc.Clear();
paradoxLauncher.ExtraSelectedDlc.Clear();
foreach (ProgramSelection selection in ProgramSelection.AllEnabled.Where(s => s != paradoxLauncher && s.Publisher == "Paradox Interactive"))
foreach (ProgramSelection selection in ProgramSelection.AllEnabled.Where(
s => s != paradoxLauncher && s.Publisher == "Paradox Interactive"))
{
paradoxLauncher.ExtraDlc.Add(new(selection.Id, selection.Name, selection.AllDlc));
paradoxLauncher.ExtraSelectedDlc.Add(new(selection.Id, selection.Name, selection.SelectedDlc));
paradoxLauncher.ExtraDlc.Add(
new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>(
selection.Id, selection.Name, selection.AllDlc));
paradoxLauncher.ExtraSelectedDlc.Add(
new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>(
selection.Id, selection.Name, selection.SelectedDlc));
}
if (!paradoxLauncher.ExtraDlc.Any())
foreach (ProgramSelection selection in ProgramSelection.AllSafe.Where(s => s != paradoxLauncher && s.Publisher == "Paradox Interactive"))
foreach (ProgramSelection selection in ProgramSelection.AllSafe.Where(
s => s != paradoxLauncher && s.Publisher == "Paradox Interactive"))
{
paradoxLauncher.ExtraDlc.Add(new(selection.Id, selection.Name, selection.AllDlc));
paradoxLauncher.ExtraSelectedDlc.Add(new(selection.Id, selection.Name, selection.AllDlc));
paradoxLauncher.ExtraDlc.Add(
new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>(
selection.Id, selection.Name, selection.AllDlc));
paradoxLauncher.ExtraSelectedDlc.Add(
new ValueTuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>(
selection.Id, selection.Name, selection.AllDlc));
}
}
}
@ -61,27 +83,21 @@ internal static class ParadoxLauncher
{
using DialogForm dialogForm = new(form);
return dialogForm.Show(SystemIcons.Warning,
$"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", customFormText: "Paradox Launcher") != DialogResult.OK;
"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", "Paradox Launcher") != DialogResult.OK;
}
}
return false;
}
public enum RepairResult
{
ProgramRunning = -2,
Failure,
Unnecessary = 0,
Success
}
internal static async Task<RepairResult> Repair(Form form, ProgramSelection selection)
{
InstallForm installForm = form as InstallForm;
if (!Program.IsProgramRunningDialog(form, selection))
return form is InstallForm ? throw new CustomMessageException("Repair failed! The launcher is currently running!")
return form is InstallForm
? throw new CustomMessageException("Repair failed! The launcher is currently running!")
: RepairResult.ProgramRunning;
bool smokeInstalled = false;
byte[] steamOriginalSdk32 = null;
@ -92,33 +108,39 @@ internal static class ParadoxLauncher
foreach (string directory in selection.DllDirectories)
{
bool koaloaderInstalled = Koaloader.AutoLoadDlls
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile());
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out _);
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile());
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o,
out string config, out _);
smokeInstalled = smokeInstalled
|| File.Exists(api32_o) || File.Exists(api64_o)
|| File.Exists(config) && !koaloaderInstalled
|| File.Exists(api32) && api32.IsResourceFile(ResourceIdentifier.Steamworks32)
|| File.Exists(api64) && api64.IsResourceFile(ResourceIdentifier.Steamworks64);
|| File.Exists(api32_o) || File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled)
|| (File.Exists(api32) && api32.IsResourceFile(ResourceIdentifier.Steamworks32))
|| (File.Exists(api64) && api64.IsResourceFile(ResourceIdentifier.Steamworks64));
await SmokeAPI.Uninstall(directory, deleteConfig: false);
if (steamOriginalSdk32 is null && File.Exists(api32) && !api32.IsResourceFile(ResourceIdentifier.Steamworks32))
steamOriginalSdk32 = File.ReadAllBytes(api32);
if (steamOriginalSdk64 is null && File.Exists(api64) && !api64.IsResourceFile(ResourceIdentifier.Steamworks64))
steamOriginalSdk64 = File.ReadAllBytes(api64);
if (steamOriginalSdk32 is null && File.Exists(api32)
&& !api32.IsResourceFile(ResourceIdentifier.Steamworks32))
steamOriginalSdk32 = await File.ReadAllBytesAsync(api32);
if (steamOriginalSdk64 is null && File.Exists(api64)
&& !api64.IsResourceFile(ResourceIdentifier.Steamworks64))
steamOriginalSdk64 = await File.ReadAllBytesAsync(api64);
directory.GetScreamApiComponents(out api32, out api32_o, out api64, out api64_o, out config);
screamInstalled = screamInstalled
|| File.Exists(api32_o) || File.Exists(api64_o)
|| File.Exists(config) && !koaloaderInstalled
|| File.Exists(api32) && api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32)
|| File.Exists(api64) && api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64);
|| File.Exists(api32_o) || File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled)
|| (File.Exists(api32) && api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32))
|| (File.Exists(api64) && api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64));
await ScreamAPI.Uninstall(directory, deleteConfig: false);
if (epicOriginalSdk32 is null && File.Exists(api32) && !api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32))
epicOriginalSdk32 = File.ReadAllBytes(api32);
if (epicOriginalSdk64 is null && File.Exists(api64) && !api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64))
epicOriginalSdk64 = File.ReadAllBytes(api64);
if (epicOriginalSdk32 is null && File.Exists(api32)
&& !api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32))
epicOriginalSdk32 = await File.ReadAllBytesAsync(api32);
if (epicOriginalSdk64 is null && File.Exists(api64)
&& !api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64))
epicOriginalSdk64 = await File.ReadAllBytesAsync(api64);
}
using DialogForm dialogForm = new(form);
if (steamOriginalSdk32 is not null || steamOriginalSdk64 is not null || epicOriginalSdk32 is not null || epicOriginalSdk64 is not null)
if (steamOriginalSdk32 is not null || steamOriginalSdk64 is not null || epicOriginalSdk32 is not null
|| epicOriginalSdk64 is not null)
{
bool neededRepair = false;
foreach (string directory in selection.DllDirectories)
@ -159,28 +181,26 @@ internal static class ParadoxLauncher
if (installForm is not null)
installForm.UpdateUser("Paradox Launcher successfully repaired!", LogTextBox.Success);
else
_ = dialogForm.Show(form.Icon, "Paradox Launcher successfully repaired!", "OK", customFormText: "Paradox Launcher");
_ = dialogForm.Show(form.Icon, "Paradox Launcher successfully repaired!", "OK",
customFormText: "Paradox Launcher");
return RepairResult.Success;
}
if (installForm is not null)
installForm.UpdateUser("Paradox Launcher did not need to be repaired.", LogTextBox.Success);
else
{
if (installForm is not null)
installForm.UpdateUser("Paradox Launcher did not need to be repaired.", LogTextBox.Success);
else
_ = dialogForm.Show(SystemIcons.Information, "Paradox Launcher does not need to be repaired.", "OK", customFormText: "Paradox Launcher");
return RepairResult.Unnecessary;
}
}
else
{
_ = form is InstallForm
? throw new CustomMessageException("Repair failed! " +
"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, "Paradox Launcher repair failed!"
+ "\n\nAn original Steamworks and/or Epic Online Services file could not be found."
+ "\nYou will likely have to reinstall Paradox Launcher to fix this issue.", "OK", customFormText: "Paradox Launcher");
return RepairResult.Failure;
_ = dialogForm.Show(SystemIcons.Information, "Paradox Launcher does not need to be repaired.", "OK",
customFormText: "Paradox Launcher");
return RepairResult.Unnecessary;
}
_ = form is InstallForm
? throw new CustomMessageException("Repair failed! " +
"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, "Paradox Launcher repair failed!"
+ "\n\nAn original Steamworks and/or Epic Online Services file could not be found."
+ "\nYou will likely have to reinstall Paradox Launcher to fix this issue.",
"OK", customFormText: "Paradox Launcher");
return RepairResult.Failure;
}
}

View file

@ -1,9 +1,4 @@
using CreamInstaller.Resources;
using CreamInstaller.Utility;
using Gameloop.Vdf.Linq;
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
@ -14,6 +9,9 @@ using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CreamInstaller.Resources;
using CreamInstaller.Utility;
using Gameloop.Vdf.Linq;
namespace CreamInstaller.Platforms.Steam;
@ -21,20 +19,33 @@ internal static class SteamCMD
{
internal const int ProcessLimit = 20;
internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe";
private static readonly ConcurrentDictionary<string, int>
AttemptCount = new(); // the more app_updates, the longer SteamCMD should wait for app_info_print
private static readonly int[] locks = new int[ProcessLimit];
internal static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip";
internal static readonly string DllPath = DirectoryPath + @"\steamclient.dll";
internal static readonly string AppCachePath = DirectoryPath + @"\appcache";
internal static readonly string ConfigPath = DirectoryPath + @"\config";
internal static readonly string DumpsPath = DirectoryPath + @"\dumps";
internal static readonly string LogsPath = DirectoryPath + @"\logs";
internal static readonly string SteamAppsPath = DirectoryPath + @"\steamapps";
internal static string DirectoryPath => ProgramData.DirectoryPath;
internal static string AppInfoPath => ProgramData.AppInfoPath;
internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe";
private static readonly ConcurrentDictionary<string, int> AttemptCount = new(); // the more app_updates, the longer SteamCMD should wait for app_info_print
private static string GetArguments(string appId) => AttemptCount.TryGetValue(appId, out int attempts)
? $@"@ShutdownOnFailedCommand 0 +force_install_dir {DirectoryPath} +login anonymous +app_info_print {appId} " + string.Concat(Enumerable.Repeat("+app_update 4 ", attempts)) + "+quit"
? $@"@ShutdownOnFailedCommand 0 +force_install_dir {DirectoryPath} +login anonymous +app_info_print {appId} "
+ string.Concat(Enumerable.Repeat("+app_update 4 ", attempts)) + "+quit"
: $"+login anonymous +app_info_print {appId} +quit";
private static readonly int[] locks = new int[ProcessLimit];
internal static async Task<string> Run(string appId) => await Task.Run(() =>
{
wait_for_lock:
wait_for_lock:
if (Program.Canceled) return "";
for (int i = 0; i < locks.Length; i++)
{
@ -42,22 +53,17 @@ internal static class SteamCMD
if (Interlocked.CompareExchange(ref locks[i], 1, 0) == 0)
{
if (appId is not null)
if (AttemptCount.ContainsKey(appId))
AttemptCount[appId]++;
else
AttemptCount[appId] = 0;
{
AttemptCount.TryGetValue(appId, out int count);
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,
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);
@ -96,7 +102,10 @@ internal static class SteamCMD
_ = output.Clear();
_ = appInfo.Clear();
}
else break;
else
{
break;
}
}
}
_ = Interlocked.Decrement(ref locks[i]);
@ -108,9 +117,6 @@ internal static class SteamCMD
goto wait_for_lock;
});
internal static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip";
internal static readonly string DllPath = DirectoryPath + @"\steamclient.dll";
internal static async Task Setup(IProgress<int> progress)
{
await Cleanup();
@ -118,7 +124,8 @@ internal static class SteamCMD
{
HttpClient httpClient = HttpClientManager.HttpClient;
if (httpClient is null) return;
byte[] file = await httpClient.GetByteArrayAsync(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);
ZipFile.ExtractToDirectory(ArchivePath, DirectoryPath);
File.Delete(ArchivePath);
@ -127,9 +134,7 @@ internal static class SteamCMD
{
FileSystemWatcher watcher = new(DirectoryPath)
{
Filter = "*",
IncludeSubdirectories = true,
EnableRaisingEvents = true
Filter = "*", IncludeSubdirectories = true, EnableRaisingEvents = true
};
if (File.Exists(DllPath)) progress.Report(-15); // update (not used at the moment)
else progress.Report(-1660); // install
@ -141,12 +146,6 @@ internal static class SteamCMD
}
}
internal static readonly string AppCachePath = DirectoryPath + @"\appcache";
internal static readonly string ConfigPath = DirectoryPath + @"\config";
internal static readonly string DumpsPath = DirectoryPath + @"\dumps";
internal static readonly string LogsPath = DirectoryPath + @"\logs";
internal static readonly string SteamAppsPath = DirectoryPath + @"\steamapps";
internal static async Task Cleanup() => await Task.Run(async () =>
{
if (!Directory.Exists(DirectoryPath)) return;
@ -165,7 +164,8 @@ internal static class SteamCMD
foreach (string file in Directory.EnumerateFiles(DirectoryPath, "*.ntfs_transaction_failed"))
File.Delete(file);
if (Directory.Exists(AppCachePath))
Directory.Delete(AppCachePath, true); // this is definitely needed, so SteamCMD gets the latest information for us
Directory.Delete(AppCachePath,
true); // this is definitely needed, so SteamCMD gets the latest information for us
if (Directory.Exists(DumpsPath))
Directory.Delete(DumpsPath, true);
if (Directory.Exists(LogsPath))
@ -181,21 +181,41 @@ internal static class SteamCMD
if (Program.Canceled) return null;
string output;
string appUpdateFile = $@"{AppInfoPath}\{appId}.vdf";
restart:
restart:
if (Program.Canceled) return null;
if (File.Exists(appUpdateFile)) output = File.ReadAllText(appUpdateFile, Encoding.UTF8);
if (File.Exists(appUpdateFile))
{
try
{
output = await File.ReadAllTextAsync(appUpdateFile, Encoding.UTF8);
}
catch
{
goto restart;
}
}
else
{
output = await Run(appId) ?? "";
int openBracket = output.IndexOf("{");
int closeBracket = output.LastIndexOf("}");
if (output is not null && openBracket != -1 && closeBracket != -1 && closeBracket > openBracket)
int openBracket = output.IndexOf("{", StringComparison.Ordinal);
int closeBracket = output.LastIndexOf("}", StringComparison.Ordinal);
if (openBracket != -1 && closeBracket != -1 && closeBracket > openBracket)
{
output = $"\"{appId}\"\n" + output[openBracket..(1 + closeBracket)];
output = output.Replace("ERROR! Failed to install app '4' (Invalid platform)", "");
File.WriteAllText(appUpdateFile, output, Encoding.UTF8);
try
{
await File.WriteAllTextAsync(appUpdateFile, output, Encoding.UTF8);
}
catch
{
goto restart;
}
}
else
{
goto restart;
}
else goto restart;
}
if (Program.Canceled || output is null) return null;
if (!ValveDataFile.TryDeserialize(output, out VProperty appInfo) || appInfo.Value is VValue)
@ -207,13 +227,15 @@ internal static class SteamCMD
VToken type = appInfo.Value?.GetChild("common")?.GetChild("type");
if (type is null || type.ToString() == "Game")
{
string buildid = appInfo.Value?.GetChild("depots")?.GetChild("branches")?.GetChild(branch)?.GetChild("buildid")?.ToString();
string buildid = appInfo.Value?.GetChild("depots")?.GetChild("branches")?.GetChild(branch)
?.GetChild("buildid")?.ToString();
if (buildid is null && type is not null) return appInfo;
if (type is null || int.TryParse(buildid, out int gamebuildId) && gamebuildId < buildId)
if (type is null || (int.TryParse(buildid, out int gamebuildId) && gamebuildId < buildId))
{
List<string> dlcAppIds = await ParseDlcAppIds(appInfo);
foreach (string dlcAppUpdateFile in dlcAppIds.Select(id => $@"{AppInfoPath}\{id}.vdf"))
if (File.Exists(dlcAppUpdateFile)) File.Delete(dlcAppUpdateFile);
if (File.Exists(dlcAppUpdateFile))
File.Delete(dlcAppUpdateFile);
if (File.Exists(appUpdateFile)) File.Delete(appUpdateFile);
goto restart;
}
@ -236,8 +258,10 @@ internal static class SteamCMD
dlcIds.Add("" + appId);
VToken depots = appInfo.Value.GetChild("depots");
if (depots is not null)
foreach (VProperty property in depots.Where(p => p is VProperty && int.TryParse((p as VProperty).Key, out int _)))
if (int.TryParse(property.Value.GetChild("dlcappid")?.ToString(), out int appId) && !dlcIds.Contains("" + appId))
foreach (VProperty property in depots.Where(
p => p is VProperty && int.TryParse((p as VProperty).Key, out int _)))
if (int.TryParse(property.Value.GetChild("dlcappid")?.ToString(), out int appId)
&& !dlcIds.Contains("" + appId))
dlcIds.Add("" + appId);
#pragma warning restore IDE0220 // Add explicit cast
return dlcIds;

View file

@ -1,16 +1,11 @@
using CreamInstaller.Resources;
using CreamInstaller.Utility;
using Gameloop.Vdf.Linq;
using Microsoft.Win32;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CreamInstaller.Utility;
using Gameloop.Vdf.Linq;
using Microsoft.Win32;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Platforms.Steam;
@ -18,34 +13,42 @@ namespace CreamInstaller.Platforms.Steam;
internal static class SteamLibrary
{
private static string installPath;
internal static string InstallPath
{
get
{
installPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Valve\Steam", "InstallPath", null) as string;
installPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Valve\Steam", "InstallPath", null) as string;
installPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Valve\Steam", "InstallPath",
null) as string;
installPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Valve\Steam", "InstallPath",
null) as string;
return installPath.BeautifyPath();
}
}
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(filterCommon: true));
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(
string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
internal static async Task<List<(string appId, string name, string branch, int buildId, string gameDirectory)>> GetGames() => await Task.Run(async () =>
internal static async Task<List<(string appId, string name, string branch, int buildId, string gameDirectory)>>
GetGames() => 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;
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) game in (await GetGamesFromLibraryDirectory(libraryDirectory))
.Where(game => !games.Any(_game => _game.appId == game.appId && _game.gameDirectory == game.gameDirectory)))
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) game in
(await GetGamesFromLibraryDirectory(libraryDirectory))
.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(() =>
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;
@ -59,9 +62,9 @@ internal static class SteamLibrary
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))
|| 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;
@ -87,9 +90,11 @@ internal static class SteamLibrary
{
gameDirectories.Add(libraryFolder);
string libraryFolders = libraryFolder + @"\libraryfolders.vdf";
if (File.Exists(libraryFolders) && ValveDataFile.TryDeserialize(File.ReadAllText(libraryFolders, Encoding.UTF8), out VProperty result))
if (File.Exists(libraryFolders)
&& ValveDataFile.TryDeserialize(File.ReadAllText(libraryFolders, Encoding.UTF8), out VProperty result))
#pragma warning disable IDE0220 // Add explicit cast
foreach (VProperty property in result.Value.Where(p => p is VProperty && int.TryParse((p as VProperty).Key, out int _)))
foreach (VProperty property in result.Value.Where(
p => p is VProperty && int.TryParse((p as VProperty).Key, out int _)))
{
string path = property.Value.GetChild("path")?.ToString();
if (string.IsNullOrWhiteSpace(path)) continue;

View file

@ -1,13 +1,11 @@
using CreamInstaller.Utility;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using CreamInstaller.Forms;
using CreamInstaller.Utility;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
#if DEBUG
using System;
#endif
@ -16,6 +14,9 @@ namespace CreamInstaller.Platforms.Steam;
internal static class SteamStore
{
private const int COOLDOWN_GAME = 600;
private const int COOLDOWN_DLC = 1200;
internal static async Task<List<string>> ParseDlcAppIds(AppData appData) => await Task.Run(() =>
{
List<string> dlcIds = new();
@ -25,17 +26,15 @@ internal static class SteamStore
return dlcIds;
});
private const int COOLDOWN_GAME = 600;
private const int COOLDOWN_DLC = 1200;
internal static async Task<AppData> QueryStoreAPI(string appId, bool isDlc = false)
internal static async Task<AppData> QueryStoreAPI(string appId, bool isDlc = false, int attempts = 0)
{
if (Program.Canceled) return null;
string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json";
bool cachedExists = File.Exists(cacheFile);
if (!cachedExists || ProgramData.CheckCooldown(appId, isDlc ? COOLDOWN_DLC : COOLDOWN_GAME))
{
string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}");
string response
= await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}");
if (response is not null)
{
IDictionary<string, JToken> apps = (IDictionary<string, JToken>)JsonConvert.DeserializeObject(response);
@ -50,7 +49,9 @@ internal static class SteamStore
if (!appDetails.success)
{
#if DEBUG
DebugForm.Current.Log($"Query unsuccessful for appid {appId}{(isDlc ? " (DLC)" : "")}: {app.Value.ToString(Formatting.None)}", LogTextBox.Warning);
DebugForm.Current.Log(
$"Query unsuccessful for appid {appId}{(isDlc ? " (DLC)" : "")}: {app.Value.ToString(Formatting.None)}",
LogTextBox.Warning);
#endif
if (data is null)
return null;
@ -59,13 +60,15 @@ internal static class SteamStore
{
try
{
File.WriteAllText(cacheFile, JsonConvert.SerializeObject(data, Formatting.Indented));
await File.WriteAllTextAsync(
cacheFile, JsonConvert.SerializeObject(data, Formatting.Indented));
}
catch
#if DEBUG
(Exception e)
(Exception e)
{
DebugForm.Current.Log($"Unsuccessful serialization of query for appid {appId}{(isDlc ? " (DLC)" : "")}: {e.GetType()} ({e.Message})");
DebugForm.Current.Log(
$"Unsuccessful serialization of query for appid {appId}{(isDlc ? " (DLC)" : "")}: {e.GetType()} ({e.Message})");
}
#else
{ }
@ -73,18 +76,24 @@ internal static class SteamStore
return data;
}
#if DEBUG
else DebugForm.Current.Log($"Response data null for appid {appId}{(isDlc ? " (DLC)" : "")}: {app.Value.ToString(Formatting.None)}");
DebugForm.Current.Log(
$"Response data null for appid {appId}{(isDlc ? " (DLC)" : "")}: {app.Value.ToString(Formatting.None)}");
#endif
}
#if DEBUG
else DebugForm.Current.Log($"Response details null for appid {appId}{(isDlc ? " (DLC)" : "")}: {app.Value.ToString(Formatting.None)}");
else
{
DebugForm.Current.Log(
$"Response details null for appid {appId}{(isDlc ? " (DLC)" : "")}: {app.Value.ToString(Formatting.None)}");
}
#endif
}
catch
#if DEBUG
(Exception e)
(Exception e)
{
DebugForm.Current.Log($"Unsuccessful deserialization of query for appid {appId}{(isDlc ? " (DLC)" : "")}: {e.GetType()} ({e.Message})");
DebugForm.Current.Log(
$"Unsuccessful deserialization of query for appid {appId}{(isDlc ? " (DLC)" : "")}: {e.GetType()} ({e.Message})");
}
#else
{ }
@ -93,23 +102,26 @@ internal static class SteamStore
else DebugForm.Current.Log("Response deserialization null for appid " + appId);
#endif
}
else
{
#if DEBUG
else DebugForm.Current.Log("Response null for appid " + appId, LogTextBox.Warning);
DebugForm.Current.Log("Response null for appid " + appId, LogTextBox.Warning);
#endif
}
}
if (cachedExists)
try
{
return JsonConvert.DeserializeObject<AppData>(File.ReadAllText(cacheFile));
return JsonConvert.DeserializeObject<AppData>(await File.ReadAllTextAsync(cacheFile));
}
catch
{
File.Delete(cacheFile);
}
if (!isDlc)
if (!isDlc && attempts < 10)
{
Thread.Sleep(1000);
return await QueryStoreAPI(appId, isDlc);
return await QueryStoreAPI(appId, isDlc, ++attempts);
}
return null;
}

View file

@ -1,13 +1,9 @@
using CreamInstaller.Resources;
using CreamInstaller.Utility;
using Microsoft.Win32;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CreamInstaller.Utility;
using Microsoft.Win32;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Platforms.Ubisoft;
@ -15,6 +11,7 @@ namespace CreamInstaller.Platforms.Ubisoft;
internal static class UbisoftLibrary
{
private static RegistryKey installsKey;
internal static RegistryKey InstallsKey
{
get
@ -25,21 +22,23 @@ internal static class UbisoftLibrary
}
}
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(filterCommon: true));
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(
string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
internal static async Task<List<(string gameId, string name, string gameDirectory)>> GetGames() => await Task.Run(() =>
{
List<(string gameId, string name, string gameDirectory)> games = new();
RegistryKey installsKey = InstallsKey;
if (installsKey is null) return games;
foreach (string gameId in installsKey.GetSubKeyNames())
internal static async Task<List<(string gameId, string name, string gameDirectory)>> GetGames() => await Task.Run(
() =>
{
RegistryKey installKey = installsKey.OpenSubKey(gameId);
string installDir = installKey?.GetValue("InstallDir")?.ToString()?.BeautifyPath();
if (installDir is not null && !games.Any(g => g.gameId == gameId && g.gameDirectory == installDir))
games.Add((gameId, new DirectoryInfo(installDir).Name, installDir));
}
return games;
});
List<(string gameId, string name, string gameDirectory)> games = new();
RegistryKey installsKey = InstallsKey;
if (installsKey is null) return games;
foreach (string gameId in installsKey.GetSubKeyNames())
{
RegistryKey installKey = installsKey.OpenSubKey(gameId);
string installDir = installKey?.GetValue("InstallDir")?.ToString()?.BeautifyPath();
if (installDir is not null && !games.Any(g => g.gameId == gameId && g.gameDirectory == installDir))
games.Add((gameId, new DirectoryInfo(installDir).Name, installDir));
}
return games;
});
}

View file

@ -1,6 +1,3 @@
using CreamInstaller.Platforms.Steam;
using CreamInstaller.Utility;
using System;
using System.Diagnostics;
using System.Drawing;
@ -9,6 +6,9 @@ using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using CreamInstaller.Forms;
using CreamInstaller.Platforms.Steam;
using CreamInstaller.Utility;
namespace CreamInstaller;
@ -46,7 +46,8 @@ internal static class Program
if (ProtectedGames.Contains(name)) return true;
if (directory is not null && !ProtectedGameDirectoryExceptions.Contains(name))
foreach (string path in ProtectedGameDirectories)
if (Directory.Exists(directory + path)) return true;
if (Directory.Exists(directory + path))
return true;
return false;
}
@ -56,12 +57,15 @@ internal static class Program
{
using DialogForm dialogForm = new(form);
if (dialogForm.Show(SystemIcons.Error,
$"ERROR: {selection.Name} is currently running!" +
"\n\nPlease close the program/game to continue . . . ",
"Retry", "Cancel") == DialogResult.OK)
$"ERROR: {selection.Name} is currently running!" +
"\n\nPlease close the program/game to continue . . . ",
"Retry", "Cancel") == DialogResult.OK)
return IsProgramRunningDialog(form, selection);
}
else return true;
else
{
return true;
}
return false;
}
@ -74,11 +78,12 @@ internal static class Program
_ = Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ApplicationExit += new(OnApplicationExit);
Application.ThreadException += new((s, e) => e.Exception?.HandleFatalException());
Application.ApplicationExit += OnApplicationExit;
Application.ThreadException += (s, e) => e.Exception?.HandleFatalException();
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += new((s, e) => (e.ExceptionObject as Exception)?.HandleFatalException());
retry:
AppDomain.CurrentDomain.UnhandledException += (s, e)
=> (e.ExceptionObject as Exception)?.HandleFatalException();
retry:
try
{
HttpClientManager.Setup();
@ -99,6 +104,7 @@ internal static class Program
}
internal static bool Canceled;
internal static async void Cleanup(bool cancel = true)
{
Canceled = cancel;

View file

@ -1,28 +1,21 @@
using CreamInstaller.Components;
using CreamInstaller.Resources;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CreamInstaller.Components;
using CreamInstaller.Resources;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller;
public enum Platform
{
None = 0,
Paradox,
Steam,
Epic,
Ubisoft
None = 0, Paradox, Steam,
Epic, Ubisoft
}
public enum DlcType
{
Steam,
SteamHidden,
EpicCatalogItem,
Steam, SteamHidden, EpicCatalogItem,
EpicEntitlement
}
@ -49,11 +42,17 @@ internal class ProgramSelection
internal List<(string directory, BinaryType binaryType)> ExecutableDirectories;
internal List<string> DllDirectories;
internal readonly SortedList<string, (DlcType type, string name, string icon)> AllDlc = new(PlatformIdComparer.String);
internal readonly SortedList<string, (DlcType type, string name, string icon)> SelectedDlc = new(PlatformIdComparer.String);
internal readonly SortedList<string, (DlcType type, string name, string icon)> AllDlc
= new(PlatformIdComparer.String);
internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> dlc)> ExtraDlc = new(); // for Paradox Launcher
internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> dlc)> ExtraSelectedDlc = new(); // for Paradox Launcher
internal readonly SortedList<string, (DlcType type, string name, string icon)> SelectedDlc
= new(PlatformIdComparer.String);
internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> dlc)>
ExtraDlc = new(); // for Paradox Launcher
internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> dlc)>
ExtraSelectedDlc = new(); // for Paradox Launcher
internal bool AreDllsLocked
{
@ -63,49 +62,54 @@ internal class ProgramSelection
{
if (Platform is Platform.Steam or Platform.Paradox)
{
directory.GetCreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
directory.GetCreamApiComponents(out string api32, out string api32_o, out string api64,
out string api64_o, out string config);
if (api32.IsFilePathLocked()
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked())
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked())
return true;
directory.GetSmokeApiComponents(out api32, out api32_o, out api64, out api64_o, out config, out string cache);
directory.GetSmokeApiComponents(out api32, out api32_o, out api64, out api64_o, out config,
out string cache);
if (api32.IsFilePathLocked()
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked()
|| cache.IsFilePathLocked())
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked()
|| cache.IsFilePathLocked())
return true;
}
if (Platform is Platform.Epic or Platform.Paradox)
{
directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64,
out string api64_o, out string config);
if (api32.IsFilePathLocked()
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked())
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked())
return true;
}
if (Platform is Platform.Ubisoft)
{
directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
directory.GetUplayR1Components(out string api32, out string api32_o, out string api64,
out string api64_o, out string config);
if (api32.IsFilePathLocked()
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked())
|| api32_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| config.IsFilePathLocked())
return true;
directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config);
directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o,
out api64, out api64_o, out config);
if (old_api32.IsFilePathLocked()
|| old_api64.IsFilePathLocked()
|| api32.IsFilePathLocked()
|| api32_o.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;
}
}
@ -164,7 +168,8 @@ internal class ProgramSelection
internal static void ValidateAll() => AllSafe.ForEach(selection => selection.Validate());
internal static void ValidateAll(List<(Platform platform, string id, string name)> programsToScan) => AllSafe.ForEach(selection => selection.Validate(programsToScan));
internal static void ValidateAll(List<(Platform platform, string id, string name)> programsToScan)
=> AllSafe.ForEach(selection => selection.Validate(programsToScan));
internal static readonly List<ProgramSelection> All = new();
@ -172,12 +177,15 @@ internal class ProgramSelection
internal static List<ProgramSelection> AllEnabled => AllSafe.FindAll(s => s.Enabled);
internal static ProgramSelection FromPlatformId(Platform platform, string gameId) => AllSafe.Find(s => s.Platform == platform && s.Id == gameId);
internal static ProgramSelection FromPlatformId(Platform platform, string gameId)
=> AllSafe.Find(s => s.Platform == platform && s.Id == gameId);
internal static (string gameId, (DlcType type, string name, string icon) app)? GetDlcFromPlatformId(Platform platform, string dlcId)
internal static (string gameId, (DlcType type, string name, string icon) app)? GetDlcFromPlatformId(
Platform platform, string dlcId)
{
foreach (ProgramSelection selection in AllSafe.Where(s => s.Platform == platform))
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in selection.AllDlc.Where(p => p.Key == dlcId))
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in selection.AllDlc.Where(
p => p.Key == dlcId))
return (selection.Id, pair.Value);
return null;
}

View file

@ -8,10 +8,16 @@
// </auto-generated>
//------------------------------------------------------------------------------
using System.CodeDom.Compiler;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.Resources;
using System.Runtime.CompilerServices;
namespace CreamInstaller.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
@ -19,27 +25,27 @@ namespace CreamInstaller.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[DebuggerNonUserCode()]
[CompilerGenerated()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
private static CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
[EditorBrowsable(EditorBrowsableState.Advanced)]
internal static ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CreamInstaller.Properties.Resources", typeof(Resources).Assembly);
if (ReferenceEquals(resourceMan, null)) {
ResourceManager temp = new ResourceManager("CreamInstaller.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
@ -50,8 +56,8 @@ namespace CreamInstaller.Properties {
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
[EditorBrowsable(EditorBrowsableState.Advanced)]
internal static CultureInfo Culture {
get {
return resourceCulture;
}
@ -63,10 +69,10 @@ namespace CreamInstaller.Properties {
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon Icon {
internal static Icon Icon {
get {
object obj = ResourceManager.GetObject("Icon", resourceCulture);
return ((System.Drawing.Icon)(obj));
return ((Icon)(obj));
}
}
}

View file

@ -1,25 +1,35 @@
using CreamInstaller.Components;
using CreamInstaller.Utility;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CreamInstaller.Components;
using CreamInstaller.Forms;
using CreamInstaller.Utility;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Resources;
internal static class Koaloader
{
internal static void GetKoaloaderComponents(
this string directory,
out List<string> proxies,
out string config
)
internal static readonly List<(string unlocker, string dll)> AutoLoadDlls = new()
{
proxies = new();
("Koaloader", "Unlocker.dll"), ("Koaloader", "Unlocker32.dll"), ("Koaloader", "Unlocker64.dll"),
("Lyptus", "Lyptus.dll"), ("Lyptus", "Lyptus32.dll"), ("Lyptus", "Lyptus64.dll"),
("SmokeAPI", "SmokeAPI.dll"), ("SmokeAPI", "SmokeAPI32.dll"), ("SmokeAPI", "SmokeAPI64.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 R2 Unlocker", "UplayR2Unlocker.dll"),
("Uplay R2 Unlocker", "UplayR2Unlocker32.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker64.dll")
};
internal static void GetKoaloaderComponents(
this string directory,
out List<string> proxies,
out string config
)
{
proxies = new List<string>();
foreach (string proxy in EmbeddedResources.Select(proxy =>
{
proxy = proxy[(proxy.IndexOf('.') + 1)..];
@ -41,25 +51,20 @@ internal static class Koaloader
}
}
internal static void GetProxyInfoFromIdentifier(this string resourceIdentifier, out string proxyName, out BinaryType binaryType)
internal static void GetProxyInfoFromIdentifier(this string resourceIdentifier, out string proxyName,
out BinaryType binaryType)
{
string baseIdentifier = resourceIdentifier[(resourceIdentifier.IndexOf('.') + 1)..];
baseIdentifier = baseIdentifier[..baseIdentifier.IndexOf('.')];
proxyName = baseIdentifier[..baseIdentifier.LastIndexOf('_')];
string bitness = baseIdentifier[(baseIdentifier.LastIndexOf('_') + 1)..];
binaryType = bitness == "32" ? BinaryType.BIT32 : bitness == "64" ? BinaryType.BIT64 : BinaryType.Unknown;
binaryType = bitness == "32"
? BinaryType.BIT32
: bitness == "64"
? BinaryType.BIT64
: BinaryType.Unknown;
}
internal static readonly List<(string unlocker, string dll)> AutoLoadDlls = new()
{
("Koaloader", "Unlocker.dll"), ("Koaloader", "Unlocker32.dll"), ("Koaloader", "Unlocker64.dll"),
("Lyptus", "Lyptus.dll"), ("Lyptus", "Lyptus32.dll"), ("Lyptus", "Lyptus64.dll"),
("SmokeAPI", "SmokeAPI.dll"), ("SmokeAPI", "SmokeAPI32.dll"), ("SmokeAPI", "SmokeAPI64.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 R2 Unlocker", "UplayR2Unlocker.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker32.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker64.dll")
};
internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null)
{
directory.GetKoaloaderComponents(out _, out string config);
@ -78,11 +83,13 @@ internal static class Koaloader
else if (File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action,
false);
}
}
internal static void WriteConfig(StreamWriter writer, SortedList<string, string> targets, SortedList<string, string> modules, InstallForm installForm = null)
internal static void WriteConfig(StreamWriter writer, SortedList<string, string> targets,
SortedList<string, string> modules, InstallForm installForm = null)
{
writer.WriteLine("{");
writer.WriteLine(" \"logging\": false,");
@ -96,12 +103,14 @@ internal static class Koaloader
{
string path = pair.Value;
writer.WriteLine($" \"{path}\"{(pair.Equals(lastTarget) ? "" : ",")}");
installForm?.UpdateUser($"Added target to Koaloader.json with path {path}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Added target to Koaloader.json with path {path}", LogTextBox.Action, false);
}
writer.WriteLine(" ]");
}
else
{
writer.WriteLine(" \"targets\": []");
}
if (modules.Any())
{
writer.WriteLine(" \"modules\": [");
@ -110,37 +119,41 @@ internal static class Koaloader
{
string path = pair.Value;
writer.WriteLine(" {");
writer.WriteLine($" \"path\": \"" + path + "\",");
writer.WriteLine($" \"required\": true");
writer.WriteLine(" \"path\": \"" + path + "\",");
writer.WriteLine(" \"required\": true");
writer.WriteLine(" }" + (pair.Equals(lastModule) ? "" : ","));
installForm?.UpdateUser($"Added module to Koaloader.json with path {path}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Added module to Koaloader.json with path {path}", LogTextBox.Action, false);
}
writer.WriteLine(" ]");
}
else
{
writer.WriteLine(" \"modules\": []");
}
writer.WriteLine("}");
}
internal static async Task Uninstall(string directory, string rootDirectory = null, InstallForm installForm = null, bool deleteConfig = true) => await Task.Run(async () =>
internal static async Task Uninstall(string directory, string rootDirectory = null, InstallForm installForm = null,
bool deleteConfig = true) => await Task.Run(async () =>
{
directory.GetKoaloaderComponents(out List<string> proxies, out string config);
foreach (string proxyPath in proxies.Where(proxyPath => File.Exists(proxyPath) && proxyPath.IsResourceFile(ResourceIdentifier.Koaloader)))
foreach (string proxyPath in proxies.Where(
proxyPath => File.Exists(proxyPath) && proxyPath.IsResourceFile(ResourceIdentifier.Koaloader)))
{
File.Delete(proxyPath);
installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(proxyPath)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(proxyPath)}", LogTextBox.Action, false);
}
foreach ((string unlocker, string path) in AutoLoadDlls
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Where(pair => File.Exists(pair.path) && pair.path.IsResourceFile()))
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Where(pair => File.Exists(pair.path) && pair.path.IsResourceFile()))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted {unlocker}: {Path.GetFileName(path)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted {unlocker}: {Path.GetFileName(path)}", LogTextBox.Action, false);
}
if (deleteConfig && File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
}
await SmokeAPI.Uninstall(directory, installForm, deleteConfig);
await ScreamAPI.Uninstall(directory, installForm, deleteConfig);
@ -150,20 +163,26 @@ internal static class Koaloader
await Uninstall(rootDirectory, null, installForm, deleteConfig);
});
internal static async Task Install(string directory, BinaryType binaryType, ProgramSelection selection, string rootDirectory = null, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() =>
internal static async Task Install(string directory, BinaryType binaryType, ProgramSelection selection,
string rootDirectory = null, InstallForm installForm = null,
bool generateConfig = true) => 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)))
foreach (string _path in proxies.Where(p => p != path && File.Exists(p)
&& p.IsResourceFile(ResourceIdentifier.Koaloader)))
{
File.Delete(_path);
installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(_path)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(_path)}", LogTextBox.Action, false);
}
if (File.Exists(path) && !path.IsResourceFile(ResourceIdentifier.Koaloader))
throw new CustomMessageException("A non-Koaloader DLL named " + proxy + ".dll already exists in this directory!");
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, info: false);
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))
@ -185,12 +204,15 @@ internal static class Koaloader
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
path = rootDirectory + @"\SmokeAPI32.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, info: false);
installForm?.UpdateUser(
$"Wrote SmokeAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
if (bit64)
{
@ -200,12 +222,15 @@ internal static class Koaloader
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, info: false);
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, info: false);
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);
}
@ -219,12 +244,15 @@ internal static class Koaloader
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
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, info: false);
installForm?.UpdateUser(
$"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
if (bit64)
{
@ -234,12 +262,15 @@ internal static class Koaloader
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, info: false);
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, info: false);
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);
}
@ -253,12 +284,16 @@ internal static class Koaloader
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, info: false);
installForm?.UpdateUser(
$"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
path = rootDirectory + @"\UplayR1Unlocker32.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, info: false);
installForm?.UpdateUser(
$"Wrote Uplay R1 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
if (bit64)
{
@ -268,12 +303,16 @@ internal static class Koaloader
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, info: false);
installForm?.UpdateUser(
$"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
path = rootDirectory + @"\UplayR1Unlocker64.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, info: false);
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)
@ -284,12 +323,16 @@ internal static class Koaloader
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, info: false);
installForm?.UpdateUser(
$"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
path = rootDirectory + @"\UplayR2Unlocker32.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, info: false);
installForm?.UpdateUser(
$"Wrote Uplay R2 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}",
LogTextBox.Action, false);
}
if (bit64)
{
@ -299,12 +342,16 @@ internal static class Koaloader
if (File.Exists(path))
{
File.Delete(path);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, info: false);
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, info: false);
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);
}

View file

@ -1,6 +1,4 @@
using CreamInstaller.Utility;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -9,12 +7,14 @@ using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using CreamInstaller.Utility;
namespace CreamInstaller.Resources;
internal static class Resources
{
internal static List<string> embeddedResources;
internal static List<string> EmbeddedResources
{
get
@ -22,7 +22,7 @@ internal static class Resources
if (embeddedResources is null)
{
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
embeddedResources = new();
embeddedResources = new List<string>();
foreach (string resourceName in names.Where(n => n.StartsWith("CreamInstaller.Resources.")))
embeddedResources.Add(resourceName[25..]);
}
@ -32,7 +32,8 @@ internal static class Resources
internal static void Write(this string resourceIdentifier, string filePath)
{
using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier);
using Stream resource = Assembly.GetExecutingAssembly()
.GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier);
using FileStream file = new(filePath, FileMode.Create, FileAccess.Write);
resource.CopyTo(file);
}
@ -62,55 +63,59 @@ internal static class Resources
internal enum BinaryType
{
Unknown = -1,
BIT32 = 0,
DOS = 1,
WOW = 2,
PIF = 3,
POSIX = 4,
OS216 = 5,
BIT64 = 6,
Unknown = -1, BIT32 = 0, DOS = 1,
WOW = 2, PIF = 3, POSIX = 4,
OS216 = 5, BIT64 = 6
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
private static extern bool GetBinaryType(string lpApplicationName, out BinaryType lpBinaryType);
internal static bool TryGetFileBinaryType(this string path, out BinaryType binaryType) => GetBinaryType(path, out binaryType);
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(this string rootDirectory, bool filterCommon = false, Func<string, bool> validFunc = null) =>
await Task.Run(async () => (await rootDirectory.GetExecutables(filterCommon: filterCommon, validFunc: validFunc)
?? (filterCommon || validFunc is not null ? await rootDirectory.GetExecutables() : null))?.Select(e =>
internal static bool TryGetFileBinaryType(this string path, out BinaryType binaryType)
=> GetBinaryType(path, out binaryType);
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(
this string rootDirectory, bool filterCommon = false, Func<string, bool> validFunc = null) =>
await Task.Run(async () => (await rootDirectory.GetExecutables(filterCommon, validFunc)
?? (filterCommon || validFunc is not null
? await rootDirectory.GetExecutables()
: null))?.Select(e =>
{
e.path = Path.GetDirectoryName(e.path);
return e;
})?.DistinctBy(e => e.path).ToList());
internal static async Task<List<(string path, BinaryType binaryType)>> GetExecutables(this string rootDirectory, bool filterCommon = false, Func<string, bool> validFunc = null) => await Task.Run(() =>
{
List<(string path, BinaryType binaryType)> executables = new();
if (Program.Canceled || !Directory.Exists(rootDirectory)) return null;
foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe", new EnumerationOptions() { RecurseSubdirectories = true }))
internal static async Task<List<(string path, BinaryType binaryType)>> GetExecutables(
this string rootDirectory, bool filterCommon = false, Func<string, bool> validFunc = null) => await Task.Run(
() =>
{
if (Program.Canceled) return null;
if (!executables.Any(e => e.path == path)
&& (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path))
&& (validFunc is null || validFunc(path))
&& path.TryGetFileBinaryType(out BinaryType binaryType) && binaryType is BinaryType.BIT64)
executables.Add((path, binaryType));
Thread.Sleep(1);
}
foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe", new EnumerationOptions() { RecurseSubdirectories = true }))
{
if (Program.Canceled) return null;
if (!executables.Any(e => e.path == path)
&& (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path))
&& (validFunc is null || validFunc(path))
&& path.TryGetFileBinaryType(out BinaryType binaryType) && binaryType is BinaryType.BIT32)
executables.Add((path, binaryType));
Thread.Sleep(1);
}
return !executables.Any() ? null : executables;
});
List<(string path, BinaryType binaryType)> executables = new();
if (Program.Canceled || !Directory.Exists(rootDirectory)) return null;
foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe",
new EnumerationOptions { RecurseSubdirectories = true }))
{
if (Program.Canceled) return null;
if (!executables.Any(e => e.path == path)
&& (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path))
&& (validFunc is null || validFunc(path))
&& path.TryGetFileBinaryType(out BinaryType binaryType) && binaryType is BinaryType.BIT64)
executables.Add((path, binaryType));
Thread.Sleep(1);
}
foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe",
new EnumerationOptions { RecurseSubdirectories = true }))
{
if (Program.Canceled) return null;
if (!executables.Any(e => e.path == path)
&& (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path))
&& (validFunc is null || validFunc(path))
&& path.TryGetFileBinaryType(out BinaryType binaryType) && binaryType is BinaryType.BIT32)
executables.Add((path, binaryType));
Thread.Sleep(1);
}
return !executables.Any() ? null : executables;
});
internal static bool IsCommonIncorrectExecutable(this string rootDirectory, string path)
{
@ -118,69 +123,77 @@ internal static class Resources
return subPath.Contains("SETUP")
|| subPath.Contains("REDIST")
|| subPath.Contains("SUPPORT")
|| subPath.Contains("CRASH") && (subPath.Contains("PAD") || subPath.Contains("REPORT"))
|| (subPath.Contains("CRASH") && (subPath.Contains("PAD") || subPath.Contains("REPORT")))
|| 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("MOD") && subPath.Contains("MANAGER"))
|| subPath.Contains("BATTLEYE")
|| subPath.Contains("ANTICHEAT");
}
internal static async Task<List<string>> GetDllDirectoriesFromGameDirectory(this string gameDirectory, Platform platform) => await Task.Run(() =>
internal static async Task<List<string>> GetDllDirectoriesFromGameDirectory(
this string gameDirectory, Platform platform) => 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))
foreach (string directory in Directory
.EnumerateDirectories(gameDirectory, "*",
new EnumerationOptions { RecurseSubdirectories = true })
.Append(gameDirectory))
{
if (Program.Canceled) return null;
string subDirectory = directory.BeautifyPath();
if (!dllDirectories.Contains(subDirectory))
{
bool koaloaderInstalled = Koaloader.AutoLoadDlls
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile());
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Any(pair => File.Exists(pair.path) && pair.path.IsResourceFile());
if (platform is Platform.Steam or Platform.Paradox)
{
subDirectory.GetSmokeApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string config, out string cache);
subDirectory.GetSmokeApiComponents(out string api, out string api_o, out string api64,
out string api64_o, out string config, out string cache);
if (File.Exists(api)
|| File.Exists(api_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| File.Exists(config) && !koaloaderInstalled
|| File.Exists(cache) && !koaloaderInstalled)
|| File.Exists(api_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled)
|| (File.Exists(cache) && !koaloaderInstalled))
dllDirectories.Add(subDirectory);
}
if (platform is Platform.Epic or Platform.Paradox)
{
subDirectory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
subDirectory.GetScreamApiComponents(out string api32, out string api32_o, out string api64,
out string api64_o, out string config);
if (File.Exists(api32)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| File.Exists(config) && !koaloaderInstalled)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled))
dllDirectories.Add(subDirectory);
}
if (platform is Platform.Ubisoft)
{
subDirectory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
subDirectory.GetUplayR1Components(out string api32, out string api32_o, out string api64,
out string api64_o, out string config);
if (File.Exists(api32)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| File.Exists(config) && !koaloaderInstalled)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled))
dllDirectories.Add(subDirectory);
subDirectory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config);
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)
|| 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);
}
}
@ -189,10 +202,10 @@ internal static class Resources
});
internal static void GetCreamApiComponents(
this string directory,
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config)
this string directory,
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config)
{
api32 = directory + @"\steam_api.dll";
api32_o = directory + @"\steam_api_o.dll";
@ -203,22 +216,15 @@ internal static class Resources
internal enum ResourceIdentifier
{
Koaloader,
Steamworks32,
Steamworks64,
EpicOnlineServices32,
EpicOnlineServices64,
Uplay32,
Uplay64,
Upc32,
Upc64,
Koaloader, Steamworks32, Steamworks64,
EpicOnlineServices32, EpicOnlineServices64, Uplay32,
Uplay64, Upc32, Upc64
}
internal static readonly Dictionary<ResourceIdentifier, IReadOnlyList<string>> ResourceMD5s = new()
{
{
ResourceIdentifier.Koaloader,
new List<string>()
ResourceIdentifier.Koaloader, new List<string>
{
"8A0958687B5ED7C34DAD037856DD1530", // Koaloader v2.0.0
"8FECDEB40980F4E687C10E056232D96B", // Koaloader v2.0.0
@ -463,75 +469,67 @@ internal static class Resources
"76CAB00C7DD33FC19F7CDD1849FF9CA2", // Koaloader v2.4.0
"DA4D6A7C0872757A74DDAE05A2C1D160", // Koaloader v2.4.0
"1F46DE8747C0A157841AFFE6185CE4C9", // Koaloader v2.4.0
"BE16B588D018D8EFF1F3B6A600F26BED" // Koaloader v2.4.0
"BE16B588D018D8EFF1F3B6A600F26BED" // Koaloader v2.4.0
}
},
{
ResourceIdentifier.EpicOnlineServices32,
new List<string>()
ResourceIdentifier.EpicOnlineServices32, new List<string>
{
"069A57B1834A960193D2AD6B96926D70", // ScreamAPI v3.0.0
"E2FB3A4A9583FDC215832E5F935E4440" // ScreamAPI v3.0.1
"E2FB3A4A9583FDC215832E5F935E4440" // ScreamAPI v3.0.1
}
},
{
ResourceIdentifier.EpicOnlineServices64,
new List<string>()
ResourceIdentifier.EpicOnlineServices64, new List<string>
{
"0D62E57139F1A64F807A9934946A9474", // ScreamAPI v3.0.0
"3875C7B735EE80C23239CC4749FDCBE6" // ScreamAPI v3.0.1
"3875C7B735EE80C23239CC4749FDCBE6" // ScreamAPI v3.0.1
}
},
{
ResourceIdentifier.Steamworks32,
new List<string>()
ResourceIdentifier.Steamworks32, new List<string>
{
"02594110FE56B2945955D46670B9A094", // CreamAPI v4.5.0.0 Hotfix
"B2434578957CBE38BDCE0A671C1262FC", // SmokeAPI v1.0.0
"973AB1632B747D4BF3B2666F32E34327", // SmokeAPI v1.0.1
"C7E41F569FC6A347D67D2BFB2BD10F25", // SmokeAPI v1.0.2
"F9E7D5B248B86D1C2F2F2905A9F37755" // SmokeAPI v1.0.3
"F9E7D5B248B86D1C2F2F2905A9F37755" // SmokeAPI v1.0.3
}
},
{
ResourceIdentifier.Steamworks64,
new List<string>()
ResourceIdentifier.Steamworks64, new List<string>
{
"30091B91923D9583A54A93ED1145554B", // CreamAPI v4.5.0.0 Hotfix
"08713035CAD6F52548FF324D0487B88D", // SmokeAPI v1.0.0
"D077737B9979D32458AC938A2978FA3C", // SmokeAPI v1.0.1
"49122A2E2E51CBB0AE5E1D59B280E4CD", // SmokeAPI v1.0.2
"13F3E9476116F7670E21365A400357AC" // SmokeAPI v1.0.3
"13F3E9476116F7670E21365A400357AC" // SmokeAPI v1.0.3
}
},
{
ResourceIdentifier.Uplay32,
new List<string>()
ResourceIdentifier.Uplay32, new List<string>
{
"1977967B2549A38EC2DB39D4C8ED499B" // Uplay R1 Unlocker v2.0.0
}
},
{
ResourceIdentifier.Uplay64,
new List<string>()
ResourceIdentifier.Uplay64, new List<string>
{
"333FEDD9DC2B299419B37ED1624FF8DB" // Uplay R1 Unlocker v2.0.0
}
},
{
ResourceIdentifier.Upc32,
new List<string>()
ResourceIdentifier.Upc32, new List<string>
{
"C14368BC4EE19FDE8DBAC07E31C67AE4", // Uplay R2 Unlocker v3.0.0
"DED3A3EA1876E3110D7D87B9A22946B0" // Uplay R2 Unlocker v3.0.1
"DED3A3EA1876E3110D7D87B9A22946B0" // Uplay R2 Unlocker v3.0.1
}
},
{
ResourceIdentifier.Upc64,
new List<string>()
ResourceIdentifier.Upc64, new List<string>
{
"7D9A4C12972BAABCB6C181920CC0F19B", // Uplay R2 Unlocker v3.0.0
"D7FDBFE0FC8D7600FEB8EC0A97713184" // Uplay R2 Unlocker v3.0.1
"D7FDBFE0FC8D7600FEB8EC0A97713184" // Uplay R2 Unlocker v3.0.1
}
}
};
@ -547,7 +545,10 @@ internal static class Resources
return BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant();
}
internal static bool IsResourceFile(this string filePath, ResourceIdentifier identifier) => filePath.ComputeMD5() is string hash && ResourceMD5s[identifier].Contains(hash);
internal static bool IsResourceFile(this string filePath, ResourceIdentifier identifier)
=> filePath.ComputeMD5() is string hash && ResourceMD5s[identifier].Contains(hash);
internal static bool IsResourceFile(this string filePath) => filePath.ComputeMD5() is string hash && ResourceMD5s.Values.Any(hashes => hashes.Contains(hash));
internal static bool IsResourceFile(this string filePath) => filePath.ComputeMD5() is string hash
&& ResourceMD5s.Values.Any(
hashes => hashes.Contains(hash));
}

View file

@ -1,22 +1,22 @@
using CreamInstaller.Components;
using CreamInstaller.Utility;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CreamInstaller.Components;
using CreamInstaller.Forms;
using CreamInstaller.Utility;
namespace CreamInstaller.Resources;
internal static class ScreamAPI
{
internal static void GetScreamApiComponents(
this string directory,
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config
)
this string directory,
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config
)
{
api32 = directory + @"\EOSSDK-Win32-Shipping.dll";
api32_o = directory + @"\EOSSDK-Win32-Shipping_o.dll";
@ -28,11 +28,15 @@ internal static class ScreamAPI
internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null)
{
directory.GetScreamApiComponents(out _, out _, out _, out _, out string config);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> overrideCatalogItems = 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 selection.ExtraSelectedDlc)
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> overrideCatalogItems
= 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
selection.ExtraSelectedDlc)
overrideCatalogItems = overrideCatalogItems.Except(extraDlc);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> entitlements = selection.SelectedDlc.Where(pair => pair.Value.type == DlcType.EpicEntitlement);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> _dlc) in selection.ExtraSelectedDlc)
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> entitlements
= selection.SelectedDlc.Where(pair => pair.Value.type == DlcType.EpicEntitlement);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> _dlc) in
selection.ExtraSelectedDlc)
entitlements = entitlements.Concat(_dlc.Where(pair => pair.Value.type == DlcType.EpicEntitlement));
if (overrideCatalogItems.Any() || entitlements.Any())
{
@ -41,20 +45,27 @@ internal static class ScreamAPI
File.Create(config).Close();
StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer,
new(overrideCatalogItems.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
new(entitlements.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
installForm);
new SortedList<string, (DlcType type, string name, string icon)>(
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.Close();
}
else if (File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action,
false);
}
}
internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> overrideCatalogItems, SortedList<string, (DlcType type, string name, string icon)> entitlements, InstallForm installForm = null)
internal static void WriteConfig(StreamWriter writer,
SortedList<string, (DlcType type, string name, string icon)> overrideCatalogItems,
SortedList<string, (DlcType type, string name, string icon)> entitlements,
InstallForm installForm = null)
{
writer.WriteLine("{");
writer.WriteLine(" \"version\": 2,");
@ -66,18 +77,22 @@ internal static class ScreamAPI
if (overrideCatalogItems.Any())
{
writer.WriteLine(" \"override\": [");
KeyValuePair<string, (DlcType type, string name, string icon)> lastOverrideCatalogItem = overrideCatalogItems.Last();
KeyValuePair<string, (DlcType type, string name, string icon)> lastOverrideCatalogItem
= overrideCatalogItems.Last();
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in overrideCatalogItems)
{
string id = pair.Key;
(_, string name, _) = pair.Value;
writer.WriteLine($" \"{id}\"{(pair.Equals(lastOverrideCatalogItem) ? "" : ",")}");
installForm?.UpdateUser($"Added override catalog item to ScreamAPI.json with id {id} ({name})", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Added override catalog item to ScreamAPI.json with id {id} ({name})",
LogTextBox.Action, false);
}
writer.WriteLine(" ]");
}
else
{
writer.WriteLine(" \"override\": []");
}
writer.WriteLine(" },");
writer.WriteLine(" \"entitlements\": {");
writer.WriteLine(" \"unlock_all\": true,");
@ -91,68 +106,79 @@ internal static class ScreamAPI
string id = pair.Key;
(_, string name, _) = pair.Value;
writer.WriteLine($" \"{id}\"{(pair.Equals(lastEntitlement) ? "" : ",")}");
installForm?.UpdateUser($"Added entitlement to ScreamAPI.json with id {id} ({name})", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Added entitlement to ScreamAPI.json with id {id} ({name})", LogTextBox.Action,
false);
}
writer.WriteLine(" ]");
}
else
{
writer.WriteLine(" \"inject\": []");
}
writer.WriteLine(" }");
writer.WriteLine("}");
}
internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true) => 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_o))
internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true)
=> await Task.Run(() =>
{
if (File.Exists(api32))
directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o,
out string config);
if (File.Exists(api32_o))
{
File.Delete(api32);
installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api32)}", LogTextBox.Action, info: false);
if (File.Exists(api32))
{
File.Delete(api32);
installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false);
}
File.Move(api32_o, api32);
installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}",
LogTextBox.Action, false);
}
File.Move(api32_o, api32);
installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action, info: false);
}
if (File.Exists(api64_o))
{
if (File.Exists(api64))
if (File.Exists(api64_o))
{
File.Delete(api64);
installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api64)}", LogTextBox.Action, info: false);
if (File.Exists(api64))
{
File.Delete(api64);
installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false);
}
File.Move(api64_o, api64);
installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}",
LogTextBox.Action, false);
}
File.Move(api64_o, api64);
installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, info: false);
}
if (deleteConfig && File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, info: false);
}
});
if (deleteConfig && File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
}
});
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() =>
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null,
bool generateConfig = true) => await Task.Run(() =>
{
directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
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);
installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}",
LogTextBox.Action, false);
}
if (File.Exists(api32_o))
{
"ScreamAPI.EOSSDK-Win32-Shipping.dll".Write(api32);
installForm?.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(api32)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false);
}
if (File.Exists(api64) && !File.Exists(api64_o))
{
File.Move(api64, api64_o);
installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}",
LogTextBox.Action, false);
}
if (File.Exists(api64_o))
{
"ScreamAPI.EOSSDK-Win64-Shipping.dll".Write(api64);
installForm?.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(api64)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false);
}
if (generateConfig)
CheckConfig(directory, selection, installForm);

View file

@ -1,22 +1,22 @@
using CreamInstaller.Components;
using CreamInstaller.Utility;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CreamInstaller.Components;
using CreamInstaller.Forms;
using CreamInstaller.Utility;
namespace CreamInstaller.Resources;
internal static class SmokeAPI
{
internal static void GetSmokeApiComponents(
this string directory,
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config,
out string cache)
this string directory,
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_o = directory + @"\steam_api_o.dll";
@ -29,14 +29,18 @@ internal static class SmokeAPI
internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null)
{
directory.GetSmokeApiComponents(out _, out _, out _, out _, out string config, out _);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> overrideDlc = selection.AllDlc.Except(selection.SelectedDlc);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> overrideDlc
= selection.AllDlc.Except(selection.SelectedDlc);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in
selection.ExtraSelectedDlc)
overrideDlc = overrideDlc.Except(extraDlc);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> injectDlc = new List<KeyValuePair<string, (DlcType type, string name, string icon)>>();
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> injectDlc
= new List<KeyValuePair<string, (DlcType type, string name, string icon)>>();
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));
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in
selection.ExtraSelectedDlc)
if (selection.ExtraDlc.Where(e => e.id == id).Single().dlc.Count > 64)
injectDlc = injectDlc.Concat(extraDlc.Where(pair => pair.Value.type is DlcType.SteamHidden));
}
@ -47,20 +51,26 @@ internal static class SmokeAPI
File.Create(config).Close();
StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer,
new(overrideDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
new(injectDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
installForm);
new SortedList<string, (DlcType type, string name, string icon)>(
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.Close();
}
else if (File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action,
false);
}
}
internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> overrideDlc, SortedList<string, (DlcType type, string name, string icon)> injectDlc, InstallForm installForm = null)
internal static void WriteConfig(StreamWriter writer,
SortedList<string, (DlcType type, string name, string icon)> overrideDlc,
SortedList<string, (DlcType type, string name, string icon)> injectDlc,
InstallForm installForm = null)
{
writer.WriteLine("{");
writer.WriteLine(" \"$version\": 1,");
@ -76,12 +86,15 @@ internal static class SmokeAPI
string dlcId = pair.Key;
(_, string dlcName, _) = pair.Value;
writer.WriteLine($" {dlcId}{(pair.Equals(lastOverrideDlc) ? "" : ",")}");
installForm?.UpdateUser($"Added override DLC to SmokeAPI.json with appid {dlcId} ({dlcName})", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Added override DLC to SmokeAPI.json with appid {dlcId} ({dlcName})",
LogTextBox.Action, false);
}
writer.WriteLine(" ],");
}
else
{
writer.WriteLine(" \"override\": [],");
}
if (injectDlc.Count > 0)
{
writer.WriteLine(" \"dlc_ids\": [");
@ -91,86 +104,101 @@ internal static class SmokeAPI
string dlcId = pair.Key;
(_, string dlcName, _) = pair.Value;
writer.WriteLine($" {dlcId}{(pair.Equals(lastInjectDlc) ? "" : ",")}");
installForm?.UpdateUser($"Added inject DLC to SmokeAPI.json with appid {dlcId} ({dlcName})", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Added inject DLC to SmokeAPI.json with appid {dlcId} ({dlcName})",
LogTextBox.Action, false);
}
writer.WriteLine(" ],");
}
else
{
writer.WriteLine(" \"dlc_ids\": [],");
}
writer.WriteLine(" \"auto_inject_inventory\": true,");
writer.WriteLine(" \"inventory_items\": []");
writer.WriteLine("}");
}
internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true) => await Task.Run(() =>
{
directory.GetCreamApiComponents(out _, out _, out _, out _, out string oldConfig);
if (File.Exists(oldConfig))
internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true)
=> await Task.Run(() =>
{
File.Delete(oldConfig);
installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", LogTextBox.Action, info: false);
}
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string cache);
if (File.Exists(api32_o))
{
if (File.Exists(api32))
directory.GetCreamApiComponents(out _, out _, out _, out _, out string oldConfig);
if (File.Exists(oldConfig))
{
File.Delete(api32);
installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, info: false);
File.Delete(oldConfig);
installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}",
LogTextBox.Action, false);
}
File.Move(api32_o, api32);
installForm?.UpdateUser($"Restored Steamworks: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action, info: false);
}
if (File.Exists(api64_o))
{
if (File.Exists(api64))
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o,
out string config, out string cache);
if (File.Exists(api32_o))
{
File.Delete(api64);
installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api64)}", LogTextBox.Action, info: false);
if (File.Exists(api32))
{
File.Delete(api32);
installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false);
}
File.Move(api32_o, api32);
installForm?.UpdateUser(
$"Restored Steamworks: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action,
false);
}
File.Move(api64_o, api64);
installForm?.UpdateUser($"Restored Steamworks: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, info: false);
}
if (deleteConfig && File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, info: false);
}
if (deleteConfig && File.Exists(cache))
{
File.Delete(cache);
installForm?.UpdateUser($"Deleted cache: {Path.GetFileName(cache)}", LogTextBox.Action, info: false);
}
});
if (File.Exists(api64_o))
{
if (File.Exists(api64))
{
File.Delete(api64);
installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false);
}
File.Move(api64_o, api64);
installForm?.UpdateUser(
$"Restored Steamworks: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action,
false);
}
if (deleteConfig && File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
}
if (deleteConfig && File.Exists(cache))
{
File.Delete(cache);
installForm?.UpdateUser($"Deleted cache: {Path.GetFileName(cache)}", LogTextBox.Action, false);
}
});
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() =>
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null,
bool generateConfig = true) => await Task.Run(() =>
{
directory.GetCreamApiComponents(out _, out _, out _, out _, out string oldConfig);
if (File.Exists(oldConfig))
{
File.Delete(oldConfig);
installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}",
LogTextBox.Action, false);
}
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out _);
directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o,
out 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)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}",
LogTextBox.Action, false);
}
if (File.Exists(api32_o))
{
"SmokeAPI.steam_api.dll".Write(api32);
installForm?.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false);
}
if (File.Exists(api64) && !File.Exists(api64_o))
{
File.Move(api64, api64_o);
installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}",
LogTextBox.Action, false);
}
if (File.Exists(api64_o))
{
"SmokeAPI.steam_api64.dll".Write(api64);
installForm?.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(api64)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false);
}
if (generateConfig)
CheckConfig(directory, selection, installForm);

View file

@ -1,22 +1,22 @@
using CreamInstaller.Components;
using CreamInstaller.Utility;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CreamInstaller.Components;
using CreamInstaller.Forms;
using CreamInstaller.Utility;
namespace CreamInstaller.Resources;
internal static class UplayR1
{
internal static void GetUplayR1Components(
this string directory,
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config
)
this string directory,
out string api32, out string api32_o,
out string api64, out string api64_o,
out string config
)
{
api32 = directory + @"\uplay_r1_loader.dll";
api32_o = directory + @"\uplay_r1_loader_o.dll";
@ -28,8 +28,10 @@ internal static class UplayR1
internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null)
{
directory.GetUplayR1Components(out _, out _, out _, out _, out string config);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> blacklistDlc = selection.AllDlc.Except(selection.SelectedDlc);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> blacklistDlc
= selection.AllDlc.Except(selection.SelectedDlc);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in
selection.ExtraSelectedDlc)
blacklistDlc = blacklistDlc.Except(extraDlc);
if (blacklistDlc.Any())
{
@ -37,18 +39,24 @@ internal static class UplayR1
installForm.UpdateUser("Generating Uplay R1 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
File.Create(config).Close();
StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm);
WriteConfig(writer,
new SortedList<string, (DlcType type, string name, string icon)>(
blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
installForm);
writer.Flush();
writer.Close();
}
else if (File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action,
false);
}
}
internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> blacklistDlc, InstallForm installForm = null)
internal static void WriteConfig(StreamWriter writer,
SortedList<string, (DlcType type, string name, string icon)> blacklistDlc,
InstallForm installForm = null)
{
writer.WriteLine("{");
writer.WriteLine(" \"logging\": false,");
@ -63,67 +71,80 @@ internal static class UplayR1
string dlcId = pair.Key;
(_, string dlcName, _) = pair.Value;
writer.WriteLine($" {dlcId}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}");
installForm?.UpdateUser($"Added blacklist DLC to UplayR1Unlocker.jsonc with appid {dlcId} ({dlcName})", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Added blacklist DLC to UplayR1Unlocker.jsonc with appid {dlcId} ({dlcName})",
LogTextBox.Action, false);
}
writer.WriteLine(" ],");
}
else
{
writer.WriteLine(" \"blacklist\": [],");
}
writer.WriteLine("}");
}
internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true) => 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_o))
internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true)
=> await Task.Run(() =>
{
if (File.Exists(api32))
directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o,
out string config);
if (File.Exists(api32_o))
{
File.Delete(api32);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, info: false);
if (File.Exists(api32))
{
File.Delete(api32);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action,
false);
}
File.Move(api32_o, api32);
installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}",
LogTextBox.Action, false);
}
File.Move(api32_o, api32);
installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action, info: false);
}
if (File.Exists(api64_o))
{
if (File.Exists(api64))
if (File.Exists(api64_o))
{
File.Delete(api64);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, info: false);
if (File.Exists(api64))
{
File.Delete(api64);
installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action,
false);
}
File.Move(api64_o, api64);
installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}",
LogTextBox.Action, false);
}
File.Move(api64_o, api64);
installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, info: false);
}
if (deleteConfig && File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, info: false);
}
});
if (deleteConfig && File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
}
});
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() =>
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null,
bool generateConfig = true) => await Task.Run(() =>
{
directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config);
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);
installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}",
LogTextBox.Action, false);
}
if (File.Exists(api32_o))
{
"UplayR1.uplay_r1_loader.dll".Write(api32);
installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, false);
}
if (File.Exists(api64) && !File.Exists(api64_o))
{
File.Move(api64, api64_o);
installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}",
LogTextBox.Action, false);
}
if (File.Exists(api64_o))
{
"UplayR1.uplay_r1_loader64.dll".Write(api64);
installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, false);
}
if (generateConfig)
CheckConfig(directory, selection, installForm);

View file

@ -1,22 +1,22 @@
using CreamInstaller.Components;
using CreamInstaller.Utility;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CreamInstaller.Components;
using CreamInstaller.Forms;
using CreamInstaller.Utility;
namespace CreamInstaller.Resources;
internal static class UplayR2
{
internal static void GetUplayR2Components(
this string directory,
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)
this string directory,
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_api64 = directory + @"\uplay_r2_loader64.dll";
@ -30,8 +30,10 @@ internal static class UplayR2
internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null)
{
directory.GetUplayR2Components(out _, out _, out _, out _, out _, out _, out string config);
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> blacklistDlc = selection.AllDlc.Except(selection.SelectedDlc);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in selection.ExtraSelectedDlc)
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> blacklistDlc
= selection.AllDlc.Except(selection.SelectedDlc);
foreach ((string id, string name, SortedList<string, (DlcType type, string name, string icon)> extraDlc) in
selection.ExtraSelectedDlc)
blacklistDlc = blacklistDlc.Except(extraDlc);
if (blacklistDlc.Any())
{
@ -39,18 +41,24 @@ internal static class UplayR2
installForm.UpdateUser("Generating Uplay R2 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
File.Create(config).Close();
StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm);
WriteConfig(writer,
new SortedList<string, (DlcType type, string name, string icon)>(
blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
installForm);
writer.Flush();
writer.Close();
}
else if (File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action,
false);
}
}
internal static void WriteConfig(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> blacklistDlc, InstallForm installForm = null)
internal static void WriteConfig(StreamWriter writer,
SortedList<string, (DlcType type, string name, string icon)> blacklistDlc,
InstallForm installForm = null)
{
writer.WriteLine("{");
writer.WriteLine(" \"logging\": false,");
@ -67,71 +75,84 @@ internal static class UplayR2
string dlcId = pair.Key;
(_, string dlcName, _) = pair.Value;
writer.WriteLine($" {dlcId}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}");
installForm?.UpdateUser($"Added blacklist DLC to UplayR2Unlocker.jsonc with appid {dlcId} ({dlcName})", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Added blacklist DLC to UplayR2Unlocker.jsonc with appid {dlcId} ({dlcName})",
LogTextBox.Action, false);
}
writer.WriteLine(" ],");
}
else
{
writer.WriteLine(" \"blacklist\": [],");
}
writer.WriteLine("}");
}
internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true) => 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);
if (File.Exists(api32_o))
internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteConfig = true)
=> await Task.Run(() =>
{
string api = File.Exists(old_api32) ? old_api32 : api32;
if (File.Exists(api))
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);
if (File.Exists(api32_o))
{
File.Delete(api);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, info: false);
string api = File.Exists(old_api32) ? old_api32 : api32;
if (File.Exists(api))
{
File.Delete(api);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action,
false);
}
File.Move(api32_o, api);
installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api)}",
LogTextBox.Action, false);
}
File.Move(api32_o, api);
installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api)}", LogTextBox.Action, info: false);
}
if (File.Exists(api64_o))
{
string api = File.Exists(old_api64) ? old_api64 : api64;
if (File.Exists(api))
if (File.Exists(api64_o))
{
File.Delete(api);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, info: false);
string api = File.Exists(old_api64) ? old_api64 : api64;
if (File.Exists(api))
{
File.Delete(api);
installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action,
false);
}
File.Move(api64_o, api);
installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api)}",
LogTextBox.Action, false);
}
File.Move(api64_o, api);
installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api)}", LogTextBox.Action, info: false);
}
if (deleteConfig && File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, info: false);
}
});
if (deleteConfig && File.Exists(config))
{
File.Delete(config);
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
}
});
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() =>
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null,
bool generateConfig = true) => 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);
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);
installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api32_o)}",
LogTextBox.Action, false);
}
if (File.Exists(api32_o))
{
"UplayR2.upc_r2_loader.dll".Write(api);
installForm?.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false);
}
api = File.Exists(old_api64) ? old_api64 : api64;
if (File.Exists(api) && !File.Exists(api64_o))
{
File.Move(api, api64_o);
installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api64_o)}",
LogTextBox.Action, false);
}
if (File.Exists(api64_o))
{
"UplayR2.upc_r2_loader64.dll".Write(api);
installForm?.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, info: false);
installForm?.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false);
}
if (generateConfig)
CheckConfig(directory, selection, installForm);

View file

@ -1,20 +1,21 @@
using Microsoft.Win32;
using System;
using System;
using System.Diagnostics;
using System.IO;
using Microsoft.Win32;
namespace CreamInstaller.Utility;
internal static class Diagnostics
{
private static string notepadPlusPlusPath;
internal static string NotepadPlusPlusPath
{
get
{
notepadPlusPlusPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Notepad++", "", null) as string;
notepadPlusPlusPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Notepad++", "", null) as string;
notepadPlusPlusPath
??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Notepad++", "", null) as string;
return notepadPlusPlusPath;
}
}
@ -34,29 +35,18 @@ internal static class Diagnostics
OpenFileInWindowsNotepad(path);
}
private static void OpenFileInNotepadPlusPlus(string npp, string path) => Process.Start(new ProcessStartInfo
{
FileName = npp,
Arguments = path
});
private static void OpenFileInNotepadPlusPlus(string npp, string path)
=> Process.Start(new ProcessStartInfo { FileName = npp, Arguments = path });
private static void OpenFileInWindowsNotepad(string path) => Process.Start(new ProcessStartInfo
{
FileName = "notepad.exe",
Arguments = path
});
private static void OpenFileInWindowsNotepad(string path)
=> Process.Start(new ProcessStartInfo { FileName = "notepad.exe", Arguments = path });
internal static void OpenDirectoryInFileExplorer(string path) => Process.Start(new ProcessStartInfo
{
FileName = "explorer.exe",
Arguments = path
});
internal static void OpenDirectoryInFileExplorer(string path)
=> Process.Start(new ProcessStartInfo { FileName = "explorer.exe", Arguments = path });
internal static void OpenUrlInInternetBrowser(string url) => Process.Start(new ProcessStartInfo
{
FileName = url,
UseShellExecute = true
});
internal static void OpenUrlInInternetBrowser(string url)
=> Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true });
internal static string BeautifyPath(this string path) => path is null ? null : Path.TrimEndingDirectorySeparator(Path.GetFullPath(path));
internal static string BeautifyPath(this string path)
=> path is null ? null : Path.TrimEndingDirectorySeparator(Path.GetFullPath(path));
}

View file

@ -2,12 +2,14 @@
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using CreamInstaller.Forms;
namespace CreamInstaller.Utility;
internal static class ExceptionHandler
{
internal static bool HandleException(this Exception e, Form form = null, string caption = null, string acceptButtonText = "Retry", string cancelButtonText = "Cancel")
internal static bool HandleException(this Exception e, Form form = null, string caption = null,
string acceptButtonText = "Retry", string cancelButtonText = "Cancel")
{
caption ??= Program.Name + " encountered an exception";
StringBuilder output = new();
@ -21,35 +23,40 @@ internal static class ExceptionHandler
string[] stackTrace = e.StackTrace?.Split('\n');
if (stackTrace is not null && stackTrace.Length > 0)
{
_ = output.Append(e.GetType() + (e.Message is not null ? (": " + e.Message) : ""));
_ = output.Append(e.GetType() + (": " + e.Message));
for (int i = 0; i < stackTrace.Length; i++)
{
string line = stackTrace[i];
int atNum = line.IndexOf("at ");
int inNum = line.IndexOf("in ");
int ciNum = line.LastIndexOf(@"CreamInstaller\");
int lineNum = line.LastIndexOf(":line ");
if (line is not null && atNum != -1)
int atNum = line.IndexOf("at ", StringComparison.Ordinal);
int inNum = line.IndexOf("in ", StringComparison.Ordinal);
int ciNum = line.LastIndexOf(@"CreamInstaller\", StringComparison.Ordinal);
int lineNum = line.LastIndexOf(":line ", StringComparison.Ordinal);
if (atNum != -1)
_ = output.Append("\n " + (inNum != -1 ? line[atNum..(inNum - 1)] : line[atNum..])
+ (inNum != -1 ? ("\n "
+ (ciNum != -1 ? ("in "
+ (lineNum != -1 ? line[ciNum..lineNum]
+ "\n on " + line[(lineNum + 1)..]
: line[ciNum..]))
: line[inNum..]))
: null));
+ (inNum != -1
? "\n "
+ (ciNum != -1
? "in "
+ (lineNum != -1
? line[ciNum..lineNum]
+ "\n on " + line[(lineNum + 1)..]
: line[ciNum..])
: line[inNum..])
: null));
}
}
e = e.InnerException;
stackDepth++;
}
using DialogForm dialogForm = new(form ?? Form.ActiveForm);
return dialogForm.Show(SystemIcons.Error, output.ToString(), acceptButtonText, cancelButtonText, customFormText: caption) == DialogResult.OK;
return dialogForm.Show(SystemIcons.Error, output.ToString(), acceptButtonText, cancelButtonText, caption)
== DialogResult.OK;
}
internal static void HandleFatalException(this Exception e)
{
bool? restart = e?.HandleException(caption: Program.Name + " encountered a fatal exception", acceptButtonText: "Restart");
bool? restart = e?.HandleException(caption: Program.Name + " encountered a fatal exception",
acceptButtonText: "Restart");
if (restart.HasValue && restart.Value)
Application.Restart();
Application.Exit();
@ -58,14 +65,12 @@ internal static class ExceptionHandler
public class CustomMessageException : Exception
{
private readonly string message;
public override string Message => message;
public CustomMessageException() => Message = "CustomMessageException";
public CustomMessageException(string message) : base(message) => Message = message;
public CustomMessageException(string message, Exception e) : base(message, e) => Message = message;
public override string Message { get; }
public override string ToString() => Message;
public CustomMessageException() : base() => message = "CustomMessageException";
public CustomMessageException(string message) : base(message) => this.message = message;
public CustomMessageException(string message, Exception e) : base(message, e) => this.message = message;
}

View file

@ -1,18 +1,18 @@
using HtmlAgilityPack;
using System;
using System;
using System.Drawing;
using System.Net.Http;
using System.Threading.Tasks;
using HtmlAgilityPack;
namespace CreamInstaller.Utility;
internal static class HttpClientManager
{
internal static HttpClient HttpClient;
internal static void Setup()
{
HttpClient = new();
HttpClient = new HttpClient();
HttpClient.DefaultRequestHeaders.Add("User-Agent", $"CI{Program.Version.Replace(".", "")}");
}
@ -21,7 +21,8 @@ internal static class HttpClientManager
try
{
using HttpRequestMessage request = new(HttpMethod.Get, url);
using HttpResponseMessage response = await HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
using HttpResponseMessage response
= await HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
_ = response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
@ -31,16 +32,18 @@ internal static class HttpClientManager
}
}
internal static HtmlAgilityPack.HtmlDocument ToHtmlDocument(this string html)
internal static HtmlDocument ToHtmlDocument(this string html)
{
HtmlAgilityPack.HtmlDocument document = new();
HtmlDocument document = new();
document.LoadHtml(html);
return document;
}
internal static async Task<HtmlNodeCollection> GetDocumentNodes(string url, string xpath) => (await EnsureGet(url))?.ToHtmlDocument()?.DocumentNode?.SelectNodes(xpath);
internal static async Task<HtmlNodeCollection> GetDocumentNodes(string url, string xpath)
=> (await EnsureGet(url))?.ToHtmlDocument()?.DocumentNode?.SelectNodes(xpath);
internal static HtmlNodeCollection GetDocumentNodes(this HtmlAgilityPack.HtmlDocument htmlDocument, string xpath) => htmlDocument.DocumentNode?.SelectNodes(xpath);
internal static HtmlNodeCollection GetDocumentNodes(this HtmlDocument htmlDocument, string xpath)
=> htmlDocument.DocumentNode?.SelectNodes(xpath);
internal static async Task<Image> GetImageFromUrl(string url)
{

View file

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

View file

@ -1,18 +1,21 @@
using Newtonsoft.Json;
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace CreamInstaller.Utility;
internal static class ProgramData
{
internal static readonly string DirectoryPathOld = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller";
internal static readonly string DirectoryPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\CreamInstaller";
internal static readonly string DirectoryPathOld
= Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller";
internal static readonly string DirectoryPath
= Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\CreamInstaller";
internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo";
internal static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt";
@ -34,7 +37,9 @@ internal static class ProgramData
Directory.Move(DirectoryPathOld, DirectoryPath);
}
if (!Directory.Exists(DirectoryPath)) _ = Directory.CreateDirectory(DirectoryPath);
if (!File.Exists(AppInfoVersionPath) || !Version.TryParse(File.ReadAllText(AppInfoVersionPath, Encoding.UTF8), out Version version) || version < MinimumAppInfoVersion)
if (!File.Exists(AppInfoVersionPath)
|| !Version.TryParse(File.ReadAllText(AppInfoVersionPath, Encoding.UTF8), out Version version)
|| version < MinimumAppInfoVersion)
{
if (Directory.Exists(AppInfoPath)) Directory.Delete(AppInfoPath, true);
_ = Directory.CreateDirectory(AppInfoPath);
@ -55,23 +60,23 @@ internal static class ProgramData
SetCooldown(identifier, now);
return cooldownOver;
}
private static DateTime? GetCooldown(string identifier)
{
if (Directory.Exists(CooldownPath))
{
string cooldownFile = CooldownPath + @$"\{identifier}.txt";
if (File.Exists(cooldownFile))
{
try
{
if (DateTime.TryParse(File.ReadAllText(cooldownFile), out DateTime cooldown))
return cooldown;
}
catch { }
}
}
return null;
}
private static void SetCooldown(string identifier, DateTime time)
{
if (!Directory.Exists(CooldownPath))
@ -79,7 +84,7 @@ internal static class ProgramData
string cooldownFile = CooldownPath + @$"\{identifier}.txt";
try
{
File.WriteAllText(cooldownFile, time.ToString());
File.WriteAllText(cooldownFile, time.ToString(CultureInfo.InvariantCulture));
}
catch { }
}
@ -90,13 +95,15 @@ internal static class ProgramData
try
{
return JsonConvert.DeserializeObject(File.ReadAllText(ProgramChoicesPath),
typeof(List<(Platform platform, string id)>)) as List<(Platform platform, string id)>;
typeof(List<(Platform platform, string id)>)) as
List<(Platform platform, string id)>;
}
catch
{
return new();
return new List<(Platform platform, string id)>();
}
}
internal static void WriteProgramChoices(List<(Platform platform, string id)> choices)
{
try
@ -115,13 +122,15 @@ internal static class ProgramData
try
{
return JsonConvert.DeserializeObject(File.ReadAllText(DlcChoicesPath),
typeof(List<(Platform platform, string gameId, string dlcId)>)) as List<(Platform platform, string gameId, string dlcId)>;
typeof(List<(Platform platform, string gameId, string dlcId)>)) as
List<(Platform platform, string gameId, string dlcId)>;
}
catch
{
return new();
return new List<(Platform platform, string gameId, string dlcId)>();
}
}
internal static void WriteDlcChoices(List<(Platform platform, string gameId, string dlcId)> choices)
{
try
@ -140,15 +149,18 @@ internal static class ProgramData
try
{
return JsonConvert.DeserializeObject(File.ReadAllText(KoaloaderProxyChoicesPath),
typeof(List<(Platform platform, string id, string proxy, bool enabled)>)) as List<(Platform platform, string id, string proxy, bool enabled)>;
typeof(
List<(Platform platform, string id, string proxy, bool enabled)>))
as List<(Platform platform, string id, string proxy, bool enabled)>;
}
catch
{
return new();
return new List<(Platform platform, string id, string proxy, bool enabled)>();
}
}
internal static void WriteKoaloaderProxyChoices(List<(Platform platform, string id, string proxy, bool enabled)> choices)
internal static void WriteKoaloaderProxyChoices(
List<(Platform platform, string id, string proxy, bool enabled)> choices)
{
try
{