- Minor code refactoring
- Added a Paradox Launcher Repair feature to the right-click context menu
This commit is contained in:
pointfeev 2022-02-21 16:30:10 -05:00
parent cbf614c04d
commit b9d84c0c34
6 changed files with 196 additions and 88 deletions

View file

@ -33,9 +33,13 @@ internal class ProgramSelection
{ {
foreach (string directory in SteamApiDllDirectories) foreach (string directory in SteamApiDllDirectories)
{ {
string api = directory + @"\steam_api.dll"; directory.GetApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string cApi);
string api64 = directory + @"\steam_api64.dll"; if (api.IsFilePathLocked()
if (api.IsFilePathLocked() || api64.IsFilePathLocked()) return true; || api_o.IsFilePathLocked()
|| api64.IsFilePathLocked()
|| api64_o.IsFilePathLocked()
|| cApi.IsFilePathLocked())
return true;
} }
return false; return false;
} }

View file

@ -9,8 +9,10 @@ internal partial class DialogForm : CustomForm
{ {
internal DialogForm(IWin32Window owner) : base(owner) => InitializeComponent(); internal DialogForm(IWin32Window owner) : base(owner) => InitializeComponent();
internal DialogResult Show(string formName, Icon descriptionIcon, string descriptionText, string acceptButtonText, string cancelButtonText = null) internal DialogResult Show(string formName, Icon descriptionIcon, string descriptionText, string acceptButtonText, string cancelButtonText = null, Icon customFormIcon = null)
{ {
if (customFormIcon is not null)
Icon = customFormIcon;
icon.Image = descriptionIcon.ToBitmap(); icon.Image = descriptionIcon.ToBitmap();
Text = formName; Text = formName;
descriptionLabel.Text = descriptionText; descriptionLabel.Text = descriptionText;

View file

@ -4,7 +4,6 @@ using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
@ -33,23 +32,29 @@ internal partial class InstallForm : CustomForm
internal void UpdateProgress(int progress) internal void UpdateProgress(int progress)
{ {
int value = (int)((float)(CompleteOperationsCount / (float)OperationsCount) * 100) + progress / OperationsCount; if (!userProgressBar.Disposing && !userProgressBar.IsDisposed)
if (value < userProgressBar.Value) return; userProgressBar.Invoke(() =>
userProgressBar.Value = value; {
int value = (int)((float)(CompleteOperationsCount / (float)OperationsCount) * 100) + progress / OperationsCount;
if (value < userProgressBar.Value) return;
userProgressBar.Value = value;
});
} }
internal async Task UpdateUser(string text, Color color, bool info = true, bool log = true) internal void UpdateUser(string text, Color color, bool info = true, bool log = true)
{ {
if (info) userInfoLabel.Text = text; if (info) userInfoLabel.Invoke(() => userInfoLabel.Text = text);
if (log && !logTextBox.IsDisposed) if (log && !logTextBox.Disposing && !logTextBox.IsDisposed)
{ {
if (logTextBox.Text.Length > 0) logTextBox.AppendText(Environment.NewLine, color); logTextBox.Invoke(() =>
logTextBox.AppendText(text, color); {
if (logTextBox.Text.Length > 0) logTextBox.AppendText(Environment.NewLine, color);
logTextBox.AppendText(text, color);
});
} }
await Task.Run(() => Thread.Sleep(0)); // to keep the text box control from glitching
} }
internal async Task WriteConfiguration(StreamWriter writer, int steamAppId, string name, SortedList<int, (string name, string iconStaticId)> steamDlcApps) internal static void WriteConfiguration(StreamWriter writer, int steamAppId, string name, SortedList<int, (string name, string iconStaticId)> steamDlcApps, InstallForm installForm = null)
{ {
writer.WriteLine(); writer.WriteLine();
writer.WriteLine($"; {name}"); writer.WriteLine($"; {name}");
@ -57,16 +62,93 @@ internal partial class InstallForm : CustomForm
writer.WriteLine($"appid = {steamAppId}"); writer.WriteLine($"appid = {steamAppId}");
writer.WriteLine(); writer.WriteLine();
writer.WriteLine("[dlc]"); writer.WriteLine("[dlc]");
await UpdateUser($"Added game to cream_api.ini with appid {steamAppId} ({name})", InstallationLog.Resource, info: false); if (installForm is not null)
installForm.UpdateUser($"Added game to cream_api.ini with appid {steamAppId} ({name})", InstallationLog.Resource, info: false);
foreach (KeyValuePair<int, (string name, string iconStaticId)> pair in steamDlcApps) foreach (KeyValuePair<int, (string name, string iconStaticId)> pair in steamDlcApps)
{ {
int appId = pair.Key; int appId = pair.Key;
(string name, string iconStaticId) dlcApp = pair.Value; (string name, string iconStaticId) dlcApp = pair.Value;
writer.WriteLine($"{appId} = {dlcApp.name}"); writer.WriteLine($"{appId} = {dlcApp.name}");
await UpdateUser($"Added DLC to cream_api.ini with appid {appId} ({dlcApp.name})", InstallationLog.Resource, info: false); if (installForm is not null)
installForm.UpdateUser($"Added DLC to cream_api.ini with appid {appId} ({dlcApp.name})", InstallationLog.Resource, info: false);
} }
} }
internal static async Task UninstallCreamAPI(string directory, InstallForm installForm = null) => await Task.Run(() =>
{
directory.GetApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string cApi);
if (File.Exists(api_o))
{
if (File.Exists(api))
{
File.Delete(api);
if (installForm is not null)
installForm.UpdateUser($"Deleted file: {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
}
File.Move(api_o, api);
if (installForm is not null)
installForm.UpdateUser($"Renamed file: {Path.GetFileName(api_o)} -> {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
}
if (File.Exists(api64_o))
{
if (File.Exists(api64))
{
File.Delete(api64);
if (installForm is not null)
installForm.UpdateUser($"Deleted file: {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
}
File.Move(api64_o, api64);
if (installForm is not null)
installForm.UpdateUser($"Renamed file: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
}
if (File.Exists(cApi))
{
File.Delete(cApi);
if (installForm is not null)
installForm.UpdateUser($"Deleted file: {Path.GetFileName(cApi)}", InstallationLog.Resource, info: false);
}
});
internal static async Task InstallCreamAPI(string directory, ProgramSelection selection, InstallForm installForm = null) => await Task.Run(() =>
{
directory.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.Move(api, api_o);
if (installForm is not null)
installForm.UpdateUser($"Renamed file: {Path.GetFileName(api)} -> {Path.GetFileName(api_o)}", InstallationLog.Resource, info: false);
}
if (File.Exists(api_o))
{
Properties.Resources.API.Write(api);
if (installForm is not null)
installForm.UpdateUser($"Wrote resource to file: {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
}
if (File.Exists(api64) && !File.Exists(api64_o))
{
File.Move(api64, api64_o);
if (installForm is not null)
installForm.UpdateUser($"Renamed file: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", InstallationLog.Resource, info: false);
}
if (File.Exists(api64_o))
{
Properties.Resources.API64.Write(api64);
if (installForm is not null)
installForm.UpdateUser($"Wrote resource to file: {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
}
if (installForm is not null)
installForm.UpdateUser("Generating CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation);
File.Create(cApi).Close();
StreamWriter writer = new(cApi, true, Encoding.UTF8);
writer.WriteLine("; " + Application.CompanyName + " v" + Application.ProductVersion);
if (selection.SteamAppId > 0)
WriteConfiguration(writer, selection.SteamAppId, selection.Name, selection.SelectedSteamDlc, installForm);
foreach (Tuple<int, string, SortedList<int, (string name, string iconStaticId)>> extraAppDlc in selection.ExtraSteamAppIdDlc)
WriteConfiguration(writer, extraAppDlc.Item1, extraAppDlc.Item2, extraAppDlc.Item3, installForm);
writer.Flush();
writer.Close();
});
private async Task OperateFor(ProgramSelection selection) private async Task OperateFor(ProgramSelection selection)
{ {
UpdateProgress(0); UpdateProgress(0);
@ -74,73 +156,12 @@ internal partial class InstallForm : CustomForm
int cur = 0; int cur = 0;
foreach (string directory in selection.SteamApiDllDirectories) foreach (string directory in selection.SteamApiDllDirectories)
{ {
await UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation);
if (!Program.IsProgramRunningDialog(this, selection)) throw new OperationCanceledException(); if (!Program.IsProgramRunningDialog(this, selection)) throw new OperationCanceledException();
string api = directory + @"\steam_api.dll";
string api_o = directory + @"\steam_api_o.dll";
string api64 = directory + @"\steam_api64.dll";
string api64_o = directory + @"\steam_api64_o.dll";
string cApi = directory + @"\cream_api.ini";
if (Uninstalling) if (Uninstalling)
{ await UninstallCreamAPI(directory, this);
if (File.Exists(api_o))
{
if (File.Exists(api))
{
File.Delete(api);
await UpdateUser($"Deleted file: {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
}
File.Move(api_o, api);
await UpdateUser($"Renamed file: {Path.GetFileName(api_o)} -> {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
}
if (File.Exists(api64_o))
{
if (File.Exists(api64))
{
File.Delete(api64);
await UpdateUser($"Deleted file: {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
}
File.Move(api64_o, api64);
await UpdateUser($"Renamed file: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
}
if (File.Exists(cApi))
{
File.Delete(cApi);
await UpdateUser($"Deleted file: {Path.GetFileName(cApi)}", InstallationLog.Resource, info: false);
}
}
else else
{ await InstallCreamAPI(directory, selection, this);
if (File.Exists(api) && !File.Exists(api_o))
{
File.Move(api, api_o);
await UpdateUser($"Renamed file: {Path.GetFileName(api)} -> {Path.GetFileName(api_o)}", InstallationLog.Resource, info: false);
}
if (File.Exists(api_o))
{
Properties.Resources.API.Write(api);
await UpdateUser($"Wrote resource to file: {Path.GetFileName(api)}", InstallationLog.Resource, info: false);
}
if (File.Exists(api64) && !File.Exists(api64_o))
{
File.Move(api64, api64_o);
await UpdateUser($"Renamed file: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", InstallationLog.Resource, info: false);
}
if (File.Exists(api64_o))
{
Properties.Resources.API64.Write(api64);
await UpdateUser($"Wrote resource to file: {Path.GetFileName(api64)}", InstallationLog.Resource, info: false);
}
await UpdateUser("Generating CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation);
File.Create(cApi).Close();
StreamWriter writer = new(cApi, true, Encoding.UTF8);
writer.WriteLine("; " + Application.CompanyName + " v" + Application.ProductVersion);
if (selection.SteamAppId > 0) await WriteConfiguration(writer, selection.SteamAppId, selection.Name, selection.SelectedSteamDlc);
foreach (Tuple<int, string, SortedList<int, (string name, string iconStaticId)>> extraAppDlc in selection.ExtraSteamAppIdDlc)
await WriteConfiguration(writer, extraAppDlc.Item1, extraAppDlc.Item2, extraAppDlc.Item3);
writer.Flush();
writer.Close();
}
UpdateProgress(++cur / count * 100); UpdateProgress(++cur / count * 100);
} }
UpdateProgress(100); UpdateProgress(100);
@ -158,13 +179,13 @@ internal partial class InstallForm : CustomForm
try try
{ {
await OperateFor(selection); await OperateFor(selection);
await UpdateUser($"Operation succeeded for {selection.Name}.", InstallationLog.Success); UpdateUser($"Operation succeeded for {selection.Name}.", InstallationLog.Success);
selection.Enabled = false; selection.Enabled = false;
disabledSelections.Add(selection); disabledSelections.Add(selection);
} }
catch (Exception exception) catch (Exception exception)
{ {
await UpdateUser($"Operation failed for {selection.Name}: " + exception.ToString(), InstallationLog.Error); UpdateUser($"Operation failed for {selection.Name}: " + exception.ToString(), InstallationLog.Error);
} }
++CompleteOperationsCount; ++CompleteOperationsCount;
} }
@ -188,11 +209,11 @@ internal partial class InstallForm : CustomForm
try try
{ {
await Operate(); await Operate();
await UpdateUser($"CreamAPI successfully {(Uninstalling ? "uninstalled" : "installed and generated")} for " + ProgramCount + " program(s).", InstallationLog.Success); UpdateUser($"CreamAPI successfully {(Uninstalling ? "uninstalled" : "installed and generated")} for " + ProgramCount + " program(s).", InstallationLog.Success);
} }
catch (Exception exception) catch (Exception exception)
{ {
await UpdateUser($"CreamAPI {(Uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + exception.ToString(), InstallationLog.Error); UpdateUser($"CreamAPI {(Uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + exception.ToString(), InstallationLog.Error);
retryButton.Enabled = true; retryButton.Enabled = true;
} }
userProgressBar.Value = userProgressBar.Maximum; userProgressBar.Value = userProgressBar.Maximum;

View file

@ -12,6 +12,7 @@ using System.Windows.Forms;
using CreamInstaller.Classes; using CreamInstaller.Classes;
using CreamInstaller.Forms.Components; using CreamInstaller.Forms.Components;
using CreamInstaller.Resources;
using Gameloop.Vdf.Linq; using Gameloop.Vdf.Linq;
@ -58,9 +59,13 @@ internal partial class SelectForm : CustomForm
{ {
List<string> dllDirectories = new(); List<string> dllDirectories = new();
if (Program.Canceled || !Directory.Exists(gameDirectory)) return null; if (Program.Canceled || !Directory.Exists(gameDirectory)) return null;
string api = gameDirectory + @"\steam_api.dll"; gameDirectory.GetApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string cApi);
string api64 = gameDirectory + @"\steam_api64.dll"; if (File.Exists(api)
if (File.Exists(api) || File.Exists(api64)) dllDirectories.Add(gameDirectory); || File.Exists(api_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| File.Exists(cApi))
dllDirectories.Add(gameDirectory);
string[] directories = Directory.GetDirectories(gameDirectory); string[] directories = Directory.GetDirectories(gameDirectory);
foreach (string _directory in directories) foreach (string _directory in directories)
{ {
@ -549,6 +554,56 @@ internal partial class SelectForm : CustomForm
} }
if (selection is not null) if (selection is not null)
{ {
if (appId == 0)
{
nodeContextMenu.Items.Add(new ToolStripSeparator());
nodeContextMenu.Items.Add(new ToolStripMenuItem("Repair", Image("Command Prompt"),
new EventHandler(async (sender, e) =>
{
if (!Program.IsProgramRunningDialog(this, selection)) return;
bool shouldReinstall = false;
byte[] properApi = null;
byte[] properApi64 = null;
foreach (string directory in selection.SteamApiDllDirectories)
{
directory.GetApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string cApi);
shouldReinstall = File.Exists(cApi);
await InstallForm.UninstallCreamAPI(directory);
if (properApi is null && File.Exists(api) && !FileResourceExtensions.Equals(Properties.Resources.API, api))
properApi = File.ReadAllBytes(api);
if (properApi64 is null && File.Exists(api64) && !FileResourceExtensions.Equals(Properties.Resources.API64, api64))
properApi64 = File.ReadAllBytes(api64);
}
if (properApi is not null || properApi64 is not null)
{
bool neededRepair = false;
foreach (string directory in selection.SteamApiDllDirectories)
{
directory.GetApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string cApi);
if (properApi is not null && FileResourceExtensions.Equals(Properties.Resources.API, api))
{
properApi.Write(api);
neededRepair = true;
}
if (properApi64 is not null && FileResourceExtensions.Equals(Properties.Resources.API64, api64))
{
properApi64.Write(api64);
neededRepair = true;
}
if (shouldReinstall)
await InstallForm.InstallCreamAPI(directory, selection);
}
if (neededRepair)
new DialogForm(this).Show("Paradox Launcher Repair", Icon, "Paradox Launcher successfully repaired!", "OK");
else
new DialogForm(this).Show("Paradox Launcher Repair", SystemIcons.Information, "Paradox Launcher does not need to be repaired.", "OK");
}
else
new DialogForm(this).Show("Paradox Launcher Repair", SystemIcons.Error, "Paradox Launcher repair failed!"
+ "\n\nAn original Steamworks API file could not be found."
+ "\nYou must reinstall Paradox Launcher to fix this issue.", "OK");
})));
}
nodeContextMenu.Items.Add(new ToolStripSeparator()); nodeContextMenu.Items.Add(new ToolStripSeparator());
nodeContextMenu.Items.Add(new ToolStripMenuItem("Open Root Directory", Image("File Explorer"), nodeContextMenu.Items.Add(new ToolStripMenuItem("Open Root Directory", Image("File Explorer"),
new EventHandler((sender, e) => Program.OpenDirectoryInFileExplorer(selection.RootDirectory)))); new EventHandler((sender, e) => Program.OpenDirectoryInFileExplorer(selection.RootDirectory))));

View file

@ -51,6 +51,15 @@ internal static class Program
} }
} }
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) internal static bool IsGameBlocked(string name, string directory)
{ {
if (!BlockProtectedGames) return false; if (!BlockProtectedGames) return false;
@ -86,6 +95,12 @@ internal static class Program
mutex.Close(); 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/"; private static readonly string SteamAppImagesPath = "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/";
internal static async Task<Image> GetSteamIcon(int steamAppId, string iconStaticId) => await GetImageFromUrl(SteamAppImagesPath + $"/{steamAppId}/{iconStaticId}.jpg"); internal static async Task<Image> GetSteamIcon(int steamAppId, string iconStaticId) => await GetImageFromUrl(SteamAppImagesPath + $"/{steamAppId}/{iconStaticId}.jpg");
internal static async Task<Image> GetSteamClientIcon(int steamAppId, string clientIconStaticId) => await GetImageFromUrl(SteamAppImagesPath + $"/{steamAppId}/{clientIconStaticId}.ico"); internal static async Task<Image> GetSteamClientIcon(int steamAppId, string clientIconStaticId) => await GetImageFromUrl(SteamAppImagesPath + $"/{steamAppId}/{clientIconStaticId}.ico");

View file

@ -9,4 +9,15 @@ internal static class FileResourceExtensions
using FileStream file = new(filePath, FileMode.Create, FileAccess.Write); using FileStream file = new(filePath, FileMode.Create, FileAccess.Write);
file.Write(resource); file.Write(resource);
} }
internal static bool Equals(byte[] resource, string filePath)
{
byte[] file = File.ReadAllBytes(filePath);
if (resource.Length != file.Length)
return false;
for (int i = 0; i < resource.Length; i++)
if (resource[i] != file[i])
return false;
return true;
}
} }