diff --git a/CreamInstaller/Classes/FileGrabber.cs b/CreamInstaller/Classes/FileGrabber.cs new file mode 100644 index 0000000..e8d3989 --- /dev/null +++ b/CreamInstaller/Classes/FileGrabber.cs @@ -0,0 +1,65 @@ +using System.Diagnostics; +using System.IO; + +using Microsoft.Win32; + +namespace CreamInstaller.Classes; + +internal static class FileGrabber +{ + internal static string steamInstallPath = null; + internal static string SteamInstallPath + { + get + { + steamInstallPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\Software\Valve\Steam", "InstallPath", null) as string; + steamInstallPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\Software\Wow6432Node\Valve\Steam", "InstallPath", null) as string; + return steamInstallPath; + } + } + + internal static string paradoxLauncherInstallPath = null; + internal static string ParadoxLauncherInstallPath + { + get + { + paradoxLauncherInstallPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Paradox Interactive\Paradox Launcher v2", "LauncherInstallation", null) as string; + return paradoxLauncherInstallPath; + } + } + + internal static void GetApiComponents(this string directory, out string api, out string api_o, out string api64, out string api64_o, out string cApi) + { + api = directory + @"\steam_api.dll"; + api_o = directory + @"\steam_api_o.dll"; + api64 = directory + @"\steam_api64.dll"; + api64_o = directory + @"\steam_api64_o.dll"; + cApi = directory + @"\cream_api.ini"; + } + + internal static bool IsFilePathLocked(this string filePath) + { + try { File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None).Close(); } + catch (FileNotFoundException) { return false; } + catch (IOException) { return true; } + return false; + } + + internal static void OpenFileInNotepad(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 OpenUrlInInternetBrowser(string url) => Process.Start(new ProcessStartInfo + { + FileName = url, + UseShellExecute = true + }); +} diff --git a/CreamInstaller/Classes/HttpClientManager.cs b/CreamInstaller/Classes/HttpClientManager.cs new file mode 100644 index 0000000..e5ac363 --- /dev/null +++ b/CreamInstaller/Classes/HttpClientManager.cs @@ -0,0 +1,48 @@ +using System; +using System.Drawing; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +using HtmlAgilityPack; + +namespace CreamInstaller.Classes; + +internal static class HttpClientManager +{ + private static HttpClient httpClient; + internal static void Setup() + { + httpClient = new(); + httpClient.DefaultRequestHeaders.Add("user-agent", "CreamInstaller"); + } + + internal static async Task GetDocumentNodes(string url, string xpath) + { + try + { + using HttpRequestMessage request = new(HttpMethod.Get, url); + using HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + response.EnsureSuccessStatusCode(); + using Stream stream = await response.Content.ReadAsStreamAsync(); + using StreamReader reader = new(stream, Encoding.UTF8); + HtmlDocument document = new(); + document.LoadHtml(reader.ReadToEnd()); + return document.DocumentNode.SelectNodes(xpath); + } + catch { return null; } + } + + internal static async Task GetImageFromUrl(string url) + { + try + { + return new Bitmap(await httpClient.GetStreamAsync(url)); + } + catch { } + return null; + } + + internal static void Cleanup() => httpClient.Dispose(); +} diff --git a/CreamInstaller/Classes/IconGrabber.cs b/CreamInstaller/Classes/IconGrabber.cs new file mode 100644 index 0000000..6ee2bf2 --- /dev/null +++ b/CreamInstaller/Classes/IconGrabber.cs @@ -0,0 +1,27 @@ +using System; +using System.Drawing; +using System.IO; +using System.Threading.Tasks; + +namespace CreamInstaller.Classes; + +internal static class IconGrabber +{ + internal static Icon ToIcon(this Image image) + { + Bitmap dialogIconBitmap = new(image, new Size(image.Width, image.Height)); + return Icon.FromHandle(dialogIconBitmap.GetHicon()); + } + + private static readonly string SteamAppImagesPath = "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/"; + internal static async Task GetSteamIcon(int steamAppId, string iconStaticId) => await HttpClientManager.GetImageFromUrl(SteamAppImagesPath + $"/{steamAppId}/{iconStaticId}.jpg"); + internal static async Task GetSteamClientIcon(int steamAppId, string clientIconStaticId) => await HttpClientManager.GetImageFromUrl(SteamAppImagesPath + $"/{steamAppId}/{clientIconStaticId}.ico"); + + internal static Image GetFileIconImage(string path) => File.Exists(path) ? Icon.ExtractAssociatedIcon(path).ToBitmap() : null; + + internal static Image GetNotepadImage() => GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\notepad.exe"); + + internal static Image GetCommandPromptImage() => GetFileIconImage(Environment.SystemDirectory + @"\cmd.exe"); + + internal static Image GetFileExplorerImage() => GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe"); +} diff --git a/CreamInstaller/Classes/ProgramSelection.cs b/CreamInstaller/Classes/ProgramSelection.cs index 0d13191..6df9231 100644 --- a/CreamInstaller/Classes/ProgramSelection.cs +++ b/CreamInstaller/Classes/ProgramSelection.cs @@ -98,7 +98,7 @@ internal class ProgramSelection internal static void ValidateAll() => AllSafe.ForEach(selection => selection.Validate()); - internal static List All => Program.ProgramSelections; + internal static List All => new(); internal static List AllSafe => All.ToList(); diff --git a/CreamInstaller/Classes/SteamLibrary.cs b/CreamInstaller/Classes/SteamLibrary.cs new file mode 100644 index 0000000..e53bd7b --- /dev/null +++ b/CreamInstaller/Classes/SteamLibrary.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Gameloop.Vdf.Linq; + +namespace CreamInstaller.Classes; + +internal static class SteamLibrary +{ + internal static async Task> GetLibraryDirectories() => await Task.Run(() => + { + List gameDirectories = new(); + if (Program.Canceled) return gameDirectories; + string steamInstallPath = FileGrabber.SteamInstallPath; + if (steamInstallPath != null && Directory.Exists(steamInstallPath)) + { + string libraryFolder = steamInstallPath + @"\steamapps"; + if (Directory.Exists(libraryFolder)) + { + gameDirectories.Add(libraryFolder); + string libraryFolders = libraryFolder + @"\libraryfolders.vdf"; + if (File.Exists(libraryFolders) && ValveDataFile.TryDeserialize(File.ReadAllText(libraryFolders, Encoding.UTF8), out VProperty result)) + { + foreach (VProperty property in result.Value) + if (int.TryParse(property.Key, out int _)) + { + string path = property.Value.GetChild("path")?.ToString(); + if (string.IsNullOrWhiteSpace(path)) continue; + path += @"\steamapps"; + if (Directory.Exists(path) && !gameDirectories.Contains(path)) gameDirectories.Add(path); + } + } + } + } + return gameDirectories; + }); + + internal static async Task>> GetGamesFromLibraryDirectory(string libraryDirectory) => await Task.Run(() => + { + List> games = new(); + if (Program.Canceled || !Directory.Exists(libraryDirectory)) return null; + string[] files = Directory.GetFiles(libraryDirectory); + foreach (string file in files) + { + if (Program.Canceled) return null; + if (Path.GetExtension(file) == ".acf" && ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty result)) + { + string appId = result.Value.GetChild("appid")?.ToString(); + string installdir = result.Value.GetChild("installdir")?.ToString(); + string name = result.Value.GetChild("name")?.ToString(); + string buildId = result.Value.GetChild("buildid")?.ToString(); + if (string.IsNullOrWhiteSpace(appId) + || string.IsNullOrWhiteSpace(installdir) + || string.IsNullOrWhiteSpace(name) + || string.IsNullOrWhiteSpace(buildId)) + continue; + string branch = result.Value.GetChild("UserConfig")?.GetChild("betakey")?.ToString(); + if (string.IsNullOrWhiteSpace(branch)) branch = "public"; + string gameDirectory = libraryDirectory + @"\common\" + installdir; + if (!int.TryParse(appId, out int appIdInt)) continue; + if (!int.TryParse(buildId, out int buildIdInt)) continue; + games.Add(new(appIdInt, name, branch, buildIdInt, gameDirectory)); + } + } + return !games.Any() ? null : games; + }); + + internal static async Task> GetDllDirectoriesFromGameDirectory(string gameDirectory) => await Task.Run(async () => + { + List dllDirectories = new(); + if (Program.Canceled || !Directory.Exists(gameDirectory)) return null; + gameDirectory.GetApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string cApi); + if (File.Exists(api) + || File.Exists(api_o) + || File.Exists(api64) + || File.Exists(api64_o) + || File.Exists(cApi)) + dllDirectories.Add(gameDirectory); + string[] directories = Directory.GetDirectories(gameDirectory); + foreach (string _directory in directories) + { + if (Program.Canceled) return null; + try + { + List moreDllDirectories = await GetDllDirectoriesFromGameDirectory(_directory); + if (moreDllDirectories is not null) dllDirectories.AddRange(moreDllDirectories); + } + catch { } + } + return !dllDirectories.Any() ? null : dllDirectories; + }); +} diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index 1ae18bf..aae6dbb 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -5,7 +5,7 @@ True Resources\ini.ico true - 2.4.3.1 + 2.5.0.0 Resources\ini.ico Automatically generates and installs CreamAPI files for Steam games on the user's computer. It can also generate and install CreamAPI for the Paradox Launcher should the user select a Paradox Interactive game. @@ -43,7 +43,7 @@ - + diff --git a/CreamInstaller/Forms/InstallForm.cs b/CreamInstaller/Forms/InstallForm.cs index f28e224..2e62582 100644 --- a/CreamInstaller/Forms/InstallForm.cs +++ b/CreamInstaller/Forms/InstallForm.cs @@ -22,7 +22,6 @@ internal partial class InstallForm : CustomForm { InitializeComponent(); Text = Program.ApplicationName; - Program.InstallForm = this; logTextBox.BackColor = InstallationLog.Background; Uninstalling = uninstall; } diff --git a/CreamInstaller/Forms/MainForm.cs b/CreamInstaller/Forms/MainForm.cs index 9ed967d..b2df1cb 100644 --- a/CreamInstaller/Forms/MainForm.cs +++ b/CreamInstaller/Forms/MainForm.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Drawing; using System.IO; -using System.Net.Http; -using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web; @@ -43,7 +41,6 @@ internal partial class MainForm : CustomForm Close(); } - private static readonly HttpClient httpClient = new(); private static UpdateManager updateManager = null; private static Version latestVersion = null; private static IReadOnlyList versions; @@ -104,17 +101,11 @@ internal partial class MainForm : CustomForm changelogTreeView.Nodes.Add(root); _ = Task.Run(async () => { - try - { - string url = $"https://github.com/pointfeev/CreamInstaller/releases/tag/v{version}"; - using HttpRequestMessage request = new(HttpMethod.Get, url); - using HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); - response.EnsureSuccessStatusCode(); - using Stream stream = await response.Content.ReadAsStreamAsync(); - using StreamReader reader = new(stream, Encoding.UTF8); - HtmlAgilityPack.HtmlDocument document = new(); - document.LoadHtml(reader.ReadToEnd()); - foreach (HtmlNode node in document.DocumentNode.SelectNodes("//div[@data-test-selector='body-content']/ul/li")) + HtmlNodeCollection nodes = await HttpClientManager.GetDocumentNodes( + $"https://github.com/pointfeev/CreamInstaller/releases/tag/v{version}", + "//div[@data-test-selector='body-content']/ul/li"); + if (nodes is null) changelogTreeView.Nodes.Remove(root); + else foreach (HtmlNode node in nodes) { Program.Invoke(changelogTreeView, delegate { @@ -124,11 +115,6 @@ internal partial class MainForm : CustomForm root.Expand(); }); } - } - catch - { - changelogTreeView.Nodes.Remove(root); - } }); } } diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index 63f58cd..193c673 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; @@ -24,102 +23,6 @@ internal partial class SelectForm : CustomForm { InitializeComponent(); Text = Program.ApplicationName; - Program.SelectForm = this; - } - - private static async Task> GameLibraryDirectories() => await Task.Run(() => - { - List gameDirectories = new(); - if (Program.Canceled) return gameDirectories; - string steamInstallPath = Program.SteamInstallPath; - if (steamInstallPath != null && Directory.Exists(steamInstallPath)) - { - string libraryFolder = steamInstallPath + @"\steamapps"; - if (Directory.Exists(libraryFolder)) - { - gameDirectories.Add(libraryFolder); - string libraryFolders = libraryFolder + @"\libraryfolders.vdf"; - if (File.Exists(libraryFolders) && ValveDataFile.TryDeserialize(File.ReadAllText(libraryFolders, Encoding.UTF8), out VProperty result)) - { - foreach (VProperty property in result.Value) - if (int.TryParse(property.Key, out int _)) - { - string path = property.Value.GetChild("path")?.ToString(); - if (string.IsNullOrWhiteSpace(path)) continue; - path += @"\steamapps"; - if (Directory.Exists(path) && !gameDirectories.Contains(path)) gameDirectories.Add(path); - } - } - } - } - return gameDirectories; - }); - - private static async Task> GetDllDirectoriesFromGameDirectory(string gameDirectory) => await Task.Run(async () => - { - List dllDirectories = new(); - if (Program.Canceled || !Directory.Exists(gameDirectory)) return null; - gameDirectory.GetApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string cApi); - if (File.Exists(api) - || File.Exists(api_o) - || File.Exists(api64) - || File.Exists(api64_o) - || File.Exists(cApi)) - dllDirectories.Add(gameDirectory); - string[] directories = Directory.GetDirectories(gameDirectory); - foreach (string _directory in directories) - { - if (Program.Canceled) return null; - try - { - List moreDllDirectories = await GetDllDirectoriesFromGameDirectory(_directory); - if (moreDllDirectories is not null) dllDirectories.AddRange(moreDllDirectories); - } - catch { } - } - return !dllDirectories.Any() ? null : dllDirectories; - }); - - private static async Task>> GetGamesFromLibraryDirectory(string libraryDirectory) => await Task.Run(() => - { - List> games = new(); - if (Program.Canceled || !Directory.Exists(libraryDirectory)) return null; - string[] files = Directory.GetFiles(libraryDirectory); - foreach (string file in files) - { - if (Program.Canceled) return null; - if (Path.GetExtension(file) == ".acf" && ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty result)) - { - string appId = result.Value.GetChild("appid")?.ToString(); - string installdir = result.Value.GetChild("installdir")?.ToString(); - string name = result.Value.GetChild("name")?.ToString(); - string buildId = result.Value.GetChild("buildid")?.ToString(); - if (string.IsNullOrWhiteSpace(appId) - || string.IsNullOrWhiteSpace(installdir) - || string.IsNullOrWhiteSpace(name) - || string.IsNullOrWhiteSpace(buildId)) - continue; - string branch = result.Value.GetChild("UserConfig")?.GetChild("betakey")?.ToString(); - if (string.IsNullOrWhiteSpace(branch)) branch = "public"; - string gameDirectory = libraryDirectory + @"\common\" + installdir; - if (!int.TryParse(appId, out int appIdInt)) continue; - if (!int.TryParse(buildId, out int buildIdInt)) continue; - games.Add(new(appIdInt, name, branch, buildIdInt, gameDirectory)); - } - } - return !games.Any() ? null : games; - }); - - internal List TreeNodes => GatherTreeNodes(selectionTreeView.Nodes); - private List GatherTreeNodes(TreeNodeCollection nodeCollection) - { - List treeNodes = new(); - foreach (TreeNode rootNode in nodeCollection) - { - treeNodes.Add(rootNode); - treeNodes.AddRange(GatherTreeNodes(rootNode.Nodes)); - } - return treeNodes; } private static void UpdateRemaining(Label label, List list, string descriptor) => @@ -180,12 +83,12 @@ internal partial class SelectForm : CustomForm { if (Program.Canceled) return; List> applicablePrograms = new(); - if (Directory.Exists(Program.ParadoxLauncherInstallPath)) - applicablePrograms.Add(new(0, "Paradox Launcher", "", 0, Program.ParadoxLauncherInstallPath)); - List gameLibraryDirectories = await GameLibraryDirectories(); + if (Directory.Exists(FileGrabber.ParadoxLauncherInstallPath)) + applicablePrograms.Add(new(0, "Paradox Launcher", "", 0, FileGrabber.ParadoxLauncherInstallPath)); + List gameLibraryDirectories = await SteamLibrary.GetLibraryDirectories(); foreach (string libraryDirectory in gameLibraryDirectories) { - List> games = await GetGamesFromLibraryDirectory(libraryDirectory); + List> games = await SteamLibrary.GetGamesFromLibraryDirectory(libraryDirectory); if (games is not null) foreach (Tuple game in games) if (!applicablePrograms.Any(_game => _game.Item1 == game.Item1)) @@ -211,7 +114,7 @@ internal partial class SelectForm : CustomForm Task task = Task.Run(async () => { if (Program.Canceled) return; - List dllDirectories = await GetDllDirectoriesFromGameDirectory(directory); + List dllDirectories = await SteamLibrary.GetDllDirectoriesFromGameDirectory(directory); if (dllDirectories is null) { RemoveFromRemainingGames(name); @@ -436,14 +339,24 @@ internal partial class SelectForm : CustomForm uninstallButton.Enabled = installButton.Enabled; } + internal List TreeNodes => GatherTreeNodes(selectionTreeView.Nodes); + private List GatherTreeNodes(TreeNodeCollection nodeCollection) + { + List treeNodes = new(); + foreach (TreeNode rootNode in nodeCollection) + { + treeNodes.Add(rootNode); + treeNodes.AddRange(GatherTreeNodes(rootNode.Nodes)); + } + return treeNodes; + } + private class TreeNodeSorter : IComparer { - public int Compare(object a, object b) - { - if (!int.TryParse((a as TreeNode).Name, out int A)) return 1; - if (!int.TryParse((b as TreeNode).Name, out int B)) return 0; - return A > B ? 1 : 0; - } + public int Compare(object a, object b) => + !int.TryParse((a as TreeNode).Name, out int A) ? 1 + : !int.TryParse((b as TreeNode).Name, out int B) ? 0 + : A > B ? 1 : 0; } private void ShowProgressBar() @@ -484,26 +397,26 @@ internal partial class SelectForm : CustomForm Dictionary images = new(); Task.Run(async () => { - if (Directory.Exists(Program.ParadoxLauncherInstallPath)) + if (Directory.Exists(FileGrabber.ParadoxLauncherInstallPath)) { - foreach (string file in Directory.GetFiles(Program.ParadoxLauncherInstallPath, "*.exe")) + foreach (string file in Directory.GetFiles(FileGrabber.ParadoxLauncherInstallPath, "*.exe")) { - images["Paradox Launcher"] = Program.GetFileIconImage(file); + images["Paradox Launcher"] = IconGrabber.GetFileIconImage(file); break; } } - images["Notepad"] = Program.GetNotepadImage(); - images["Command Prompt"] = Program.GetCommandPromptImage(); - images["File Explorer"] = Program.GetFileExplorerImage(); - images["SteamDB"] = await Program.GetImageFromUrl("https://steamdb.info/favicon.ico"); - images["Steam Store"] = await Program.GetImageFromUrl("https://store.steampowered.com/favicon.ico"); - images["Steam Community"] = await Program.GetImageFromUrl("https://steamcommunity.com/favicon.ico"); + images["Notepad"] = IconGrabber.GetNotepadImage(); + images["Command Prompt"] = IconGrabber.GetCommandPromptImage(); + images["File Explorer"] = IconGrabber.GetFileExplorerImage(); + images["SteamDB"] = await HttpClientManager.GetImageFromUrl("https://steamdb.info/favicon.ico"); + images["Steam Store"] = await HttpClientManager.GetImageFromUrl("https://store.steampowered.com/favicon.ico"); + images["Steam Community"] = await HttpClientManager.GetImageFromUrl("https://steamcommunity.com/favicon.ico"); }); Image Image(string identifier) => images.GetValueOrDefault(identifier, null); void TrySetImageAsync(ToolStripMenuItem menuItem, int appId, string iconStaticId, bool client = false) => Task.Run(async () => { - menuItem.Image = client ? await Program.GetSteamClientIcon(appId, iconStaticId) : await Program.GetSteamIcon(appId, iconStaticId); + menuItem.Image = client ? await IconGrabber.GetSteamClientIcon(appId, iconStaticId) : await IconGrabber.GetSteamIcon(appId, iconStaticId); images[client ? "ClientIcon_" + appId : "Icon_" + appId] = menuItem.Image; }); selectionTreeView.NodeMouseClick += (sender, e) => @@ -540,7 +453,7 @@ internal partial class SelectForm : CustomForm { nodeContextMenu.Items.Add(new ToolStripSeparator()); nodeContextMenu.Items.Add(new ToolStripMenuItem("Open AppInfo", Image("Notepad"), - new EventHandler((sender, e) => Program.OpenFileInNotepad(appInfo)))); + new EventHandler((sender, e) => FileGrabber.OpenFileInNotepad(appInfo)))); nodeContextMenu.Items.Add(new ToolStripMenuItem("Refresh AppInfo", Image("Command Prompt"), new EventHandler((sender, e) => { @@ -610,25 +523,25 @@ internal partial class SelectForm : CustomForm } nodeContextMenu.Items.Add(new ToolStripSeparator()); nodeContextMenu.Items.Add(new ToolStripMenuItem("Open Root Directory", Image("File Explorer"), - new EventHandler((sender, e) => Program.OpenDirectoryInFileExplorer(selection.RootDirectory)))); + new EventHandler((sender, e) => FileGrabber.OpenDirectoryInFileExplorer(selection.RootDirectory)))); for (int i = 0; i < selection.SteamApiDllDirectories.Count; i++) { string directory = selection.SteamApiDllDirectories[i]; nodeContextMenu.Items.Add(new ToolStripMenuItem($"Open Steamworks Directory ({i + 1})", Image("File Explorer"), - new EventHandler((sender, e) => Program.OpenDirectoryInFileExplorer(directory)))); + new EventHandler((sender, e) => FileGrabber.OpenDirectoryInFileExplorer(directory)))); } } if (appId != 0) { nodeContextMenu.Items.Add(new ToolStripSeparator()); nodeContextMenu.Items.Add(new ToolStripMenuItem("Open SteamDB", Image("SteamDB"), - new EventHandler((sender, e) => Program.OpenUrlInInternetBrowser("https://steamdb.info/app/" + appId)))); + new EventHandler((sender, e) => FileGrabber.OpenUrlInInternetBrowser("https://steamdb.info/app/" + appId)))); nodeContextMenu.Items.Add(new ToolStripMenuItem("Open Steam Store", Image("Steam Store"), - new EventHandler((sender, e) => Program.OpenUrlInInternetBrowser("https://store.steampowered.com/app/" + appId)))); + new EventHandler((sender, e) => FileGrabber.OpenUrlInInternetBrowser("https://store.steampowered.com/app/" + appId)))); if (selection is not null) { ToolStripMenuItem steamCommunity = new("Open Steam Community", Image("ClientIcon_" + node.Name), - new EventHandler((sender, e) => Program.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + appId))); + new EventHandler((sender, e) => FileGrabber.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + appId))); nodeContextMenu.Items.Add(steamCommunity); if (steamCommunity.Image is null) { @@ -703,7 +616,9 @@ internal partial class SelectForm : CustomForm } private void OnInstall(object sender, EventArgs e) => OnAccept(false); + private void OnUninstall(object sender, EventArgs e) => OnAccept(true); + private void OnScan(object sender, EventArgs e) => OnLoad(); private void OnCancel(object sender, EventArgs e) diff --git a/CreamInstaller/Program.cs b/CreamInstaller/Program.cs index 1048221..50972ba 100644 --- a/CreamInstaller/Program.cs +++ b/CreamInstaller/Program.cs @@ -1,65 +1,29 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; -using System.Net.Http; using System.Reflection; using System.Threading; -using System.Threading.Tasks; using System.Windows.Forms; using CreamInstaller.Classes; -using Microsoft.Win32; - namespace CreamInstaller; internal static class Program { internal static readonly string ApplicationName = Application.CompanyName + " v" + Application.ProductVersion + ": " + Application.ProductName; + internal static readonly Assembly EntryAssembly = Assembly.GetEntryAssembly(); internal static readonly Process CurrentProcess = Process.GetCurrentProcess(); internal static readonly string CurrentProcessFilePath = CurrentProcess.MainModule.FileName; - internal static readonly string CurrentProcessDirectory = CurrentProcessFilePath[..CurrentProcessFilePath.LastIndexOf("\\")]; - internal static readonly string BackupFileExtension = ".creaminstaller.backup"; internal static bool BlockProtectedGames = true; internal static readonly string[] ProtectedGameNames = { "PAYDAY 2", "Call to Arms" }; // non-functioning CreamAPI or DLL detections internal static readonly string[] ProtectedGameDirectories = { @"\EasyAntiCheat", @"\BattlEye" }; // DLL detections internal static readonly string[] ProtectedGameDirectoryExceptions = { "Arma 3" }; // Arma 3's BattlEye doesn't detect DLL changes? - internal static string steamInstallPath = null; - internal static string SteamInstallPath - { - get - { - steamInstallPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\Software\Valve\Steam", "InstallPath", null) as string; - steamInstallPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\Software\Wow6432Node\Valve\Steam", "InstallPath", null) as string; - return steamInstallPath; - } - } - - internal static string paradoxLauncherInstallPath = null; - internal static string ParadoxLauncherInstallPath - { - get - { - paradoxLauncherInstallPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Paradox Interactive\Paradox Launcher v2", "LauncherInstallation", null) as string; - return paradoxLauncherInstallPath; - } - } - - internal static void GetApiComponents(this string directory, out string api, out string api_o, out string api64, out string api64_o, out string cApi) - { - api = directory + @"\steam_api.dll"; - api_o = directory + @"\steam_api_o.dll"; - api64 = directory + @"\steam_api64.dll"; - api64_o = directory + @"\steam_api64_o.dll"; - cApi = directory + @"\cream_api.ini"; - } - internal static bool IsGameBlocked(string name, string directory) { if (!BlockProtectedGames) return false; @@ -70,6 +34,20 @@ internal static class Program return false; } + internal static bool IsProgramRunningDialog(Form form, ProgramSelection selection) + { + if (selection.AreSteamApiDllsLocked) + { + if (new DialogForm(form).Show(ApplicationName, SystemIcons.Error, + $"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; + return false; + } + [STAThread] private static void Main() { @@ -83,6 +61,7 @@ internal static class Program retry: try { + HttpClientManager.Setup(); Application.Run(new MainForm()); } catch (Exception e) @@ -95,81 +74,8 @@ internal static class Program mutex.Close(); } - internal static Icon ToIcon(this Image image) - { - Bitmap dialogIconBitmap = new(image, new Size(image.Width, image.Height)); - return Icon.FromHandle(dialogIconBitmap.GetHicon()); - } - - private static readonly string SteamAppImagesPath = "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/"; - internal static async Task GetSteamIcon(int steamAppId, string iconStaticId) => await GetImageFromUrl(SteamAppImagesPath + $"/{steamAppId}/{iconStaticId}.jpg"); - internal static async Task GetSteamClientIcon(int steamAppId, string clientIconStaticId) => await GetImageFromUrl(SteamAppImagesPath + $"/{steamAppId}/{clientIconStaticId}.ico"); - - internal static async Task GetImageFromUrl(string url) - { - try - { - HttpClient httpClient = new(); - httpClient.DefaultRequestHeaders.Add("user-agent", "CreamInstaller"); - return new Bitmap(await httpClient.GetStreamAsync(url)); - } - catch { } - return null; - } - - internal static void OpenFileInNotepad(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 OpenUrlInInternetBrowser(string url) => Process.Start(new ProcessStartInfo - { - FileName = url, - UseShellExecute = true - }); - - internal static Image GetFileIconImage(string path) => File.Exists(path) ? Icon.ExtractAssociatedIcon(path).ToBitmap() : null; - - internal static Image GetNotepadImage() => GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\notepad.exe"); - - internal static Image GetCommandPromptImage() => GetFileIconImage(Environment.SystemDirectory + @"\cmd.exe"); - - internal static Image GetFileExplorerImage() => GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe"); - - internal static bool IsProgramRunningDialog(Form form, ProgramSelection selection) - { - if (selection.AreSteamApiDllsLocked) - { - if (new DialogForm(form).Show(ApplicationName, SystemIcons.Error, - $"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; - return false; - } - - internal static bool IsFilePathLocked(this string filePath) - { - try { File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None).Close(); } - catch (FileNotFoundException) { return false; } - catch (IOException) { return true; } - return false; - } - internal static void Invoke(this Control control, MethodInvoker methodInvoker) => control.Invoke(methodInvoker); - internal static SelectForm SelectForm; - internal static InstallForm InstallForm; - internal static void InheritLocation(this Form form, Form fromForm) { int X = fromForm.Location.X + fromForm.Size.Width / 2 - form.Size.Width / 2; @@ -177,12 +83,11 @@ internal static class Program form.Location = new(X, Y); } - internal static List ProgramSelections = new(); - internal static bool Canceled = false; internal static async void Cleanup(bool cancel = true) { Canceled = cancel; + HttpClientManager.Cleanup(); await SteamCMD.Cleanup(); }