finish v4.8.0.0

This commit is contained in:
pointfeev
2023-04-06 02:43:11 -04:00
parent 65933a0ed6
commit 24915e4495
12 changed files with 183 additions and 81 deletions
+116 -26
View File
@@ -1,8 +1,13 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using CreamInstaller.Components;
using CreamInstaller.Utility;
@@ -12,7 +17,12 @@ namespace CreamInstaller.Forms;
internal sealed partial class UpdateForm : CustomForm
{
private Release latestRelease;
private static readonly string PackagePath = ProgramData.DirectoryPath + @"\" + Program.RepositoryPackage;
private static readonly string ExecutablePath = ProgramData.DirectoryPath + @"\" + Program.RepositoryExecutable;
private static readonly string UpdaterPath = ProgramData.DirectoryPath + @"\updater.cmd";
private CancellationTokenSource cancellation;
private ProgramRelease latestRelease;
internal UpdateForm()
{
@@ -45,11 +55,11 @@ internal sealed partial class UpdateForm : CustomForm
#if !DEBUG
Version currentVersion = new(Program.Version);
#endif
List<Release> releases = null;
List<ProgramRelease> releases = null;
string response = await HttpClientManager.EnsureGet($"https://api.github.com/repos/{Program.RepositoryOwner}/{Program.RepositoryName}/releases");
if (response is not null)
releases = JsonConvert.DeserializeObject<List<Release>>(response)
?.Where(release => !release.Draft && !release.Prerelease && release.Assets.Count > 0).ToList();
releases = JsonConvert.DeserializeObject<List<ProgramRelease>>(response)
?.Where(release => !release.Draft && !release.Prerelease && release.Asset is not null).ToList();
latestRelease = releases?.FirstOrDefault();
#if DEBUG
if (latestRelease?.Version is not { } latestVersion)
@@ -66,7 +76,7 @@ internal sealed partial class UpdateForm : CustomForm
changelogTreeView.Visible = true;
for (int r = releases!.Count - 1; r >= 0; r--)
{
Release release = releases[r];
ProgramRelease release = releases[r];
#if !DEBUG
if (release.Version <= currentVersion)
continue;
@@ -94,18 +104,7 @@ internal sealed partial class UpdateForm : CustomForm
retry:
try
{
string fileName = Path.GetFileName(Program.CurrentProcessFilePath);
if (fileName != Program.ApplicationExecutable)
{
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)
{
Application.Exit();
return;
}
}
UpdaterPath.DeleteFile();
OnLoad();
}
catch (Exception e)
@@ -118,8 +117,9 @@ internal sealed partial class UpdateForm : CustomForm
private void OnIgnore(object sender, EventArgs e) => StartProgram();
private void OnUpdate(object sender, EventArgs e)
private async void OnUpdate(object sender, EventArgs e)
{
progressBar.Value = 0;
progressBar.Visible = true;
ignoreButton.Visible = false;
updateButton.Text = "Cancel";
@@ -127,20 +127,109 @@ internal sealed partial class UpdateForm : CustomForm
updateButton.Click += OnUpdateCancel;
changelogTreeView.Location = progressBar.Location with { Y = progressBar.Location.Y + progressBar.Size.Height + 6 };
Refresh();
Progress<double> progress = new();
progress.ProgressChanged += delegate(object _, double _progress)
Progress<int> progress = new();
IProgress<int> iProgress = progress;
progress.ProgressChanged += delegate(object _, int _progress)
{
progressLabel.Text = $"Updating . . . {(int)_progress}%";
progressBar.Value = (int)_progress;
progressLabel.Text = $"Updating . . . {_progress}%";
progressBar.Value = _progress;
};
progressLabel.Text = "Updating . . . ";
// do update
OnLoad();
cancellation = new();
bool success = true;
PackagePath.DeleteFile(true);
await using Stream update = PackagePath.CreateFile(true);
bool retry = true;
try
{
if (cancellation is null || Program.Canceled)
throw new TaskCanceledException();
using HttpResponseMessage response = await HttpClientManager.HttpClient.GetAsync(latestRelease.Asset.BrowserDownloadUrl,
HttpCompletionOption.ResponseHeadersRead, cancellation.Token);
response.EnsureSuccessStatusCode();
if (cancellation is null || Program.Canceled)
throw new TaskCanceledException();
await using Stream download = await response.Content.ReadAsStreamAsync(cancellation.Token);
double bytes = latestRelease.Asset.Size;
byte[] buffer = new byte[16384];
long bytesRead = 0;
int newBytes;
while (cancellation is not null && !Program.Canceled
&& (newBytes = await download.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellation.Token)) != 0)
{
if (cancellation is null || Program.Canceled)
throw new TaskCanceledException();
await update.WriteAsync(buffer.AsMemory(0, newBytes), cancellation.Token);
bytesRead += newBytes;
int report = (int)(bytesRead / bytes * 100);
if (report <= progressBar.Value)
continue;
iProgress.Report(report);
}
iProgress.Report((int)(bytesRead / bytes * 100));
if (cancellation is null || Program.Canceled)
throw new TaskCanceledException();
}
catch (TaskCanceledException)
{
success = false;
}
catch (Exception ex)
{
retry = ex.HandleException(this, Program.Name + " encountered an exception while updating");
success = false;
}
cancellation?.Dispose();
cancellation = null;
await update.DisposeAsync();
bool canContinue = success && !Program.Canceled;
if (canContinue)
updateButton.Enabled = false;
ExecutablePath.DeleteFile(canContinue);
if (canContinue)
await Task.Run(() => PackagePath.ExtractZip(ProgramData.DirectoryPath, true, this));
PackagePath.DeleteFile(canContinue);
if (canContinue)
{
string currentPath = Program.CurrentProcessFilePath;
string currentDirectory = Path.GetDirectoryName(currentPath);
string properExecutable = Path.GetFileName(ExecutablePath);
string properExecutablePath = Path.Combine(currentDirectory!, properExecutable!);
StringBuilder commands = new();
commands.AppendLine(CultureInfo.InvariantCulture, $"\nTASKKILL /F /T /PID {Program.CurrentProcessId}");
commands.AppendLine(CultureInfo.InvariantCulture, $":LOOP");
commands.AppendLine(CultureInfo.InvariantCulture, $"TASKLIST | FIND \"{Program.CurrentProcessId}\"");
commands.AppendLine(CultureInfo.InvariantCulture, $"IF NOT ERRORLEVEL 1 (");
commands.AppendLine(CultureInfo.InvariantCulture, $" TIMEOUT /T 1");
commands.AppendLine(CultureInfo.InvariantCulture, $" GOTO LOOP");
commands.AppendLine(CultureInfo.InvariantCulture, $")");
commands.AppendLine(CultureInfo.InvariantCulture, $"DEL /F /Q \"{currentPath}\"");
commands.AppendLine(CultureInfo.InvariantCulture, $"DEL /F /Q \"{properExecutablePath}\"");
commands.AppendLine(CultureInfo.InvariantCulture, $"MOVE /Y \"{ExecutablePath}\" \"{properExecutablePath}\"");
commands.AppendLine(CultureInfo.InvariantCulture, $"START \"\" /D \"{currentDirectory}\" \"{properExecutable}\"");
commands.AppendLine(CultureInfo.InvariantCulture, $"EXIT");
UpdaterPath.WriteFile(commands.ToString(), true, this);
Process process = new();
ProcessStartInfo startInfo = new()
{
WorkingDirectory = ProgramData.DirectoryPath, FileName = "cmd.exe", Arguments = $"/C START \"UPDATER\" /B {Path.GetFileName(UpdaterPath)}",
CreateNoWindow = true
};
process.StartInfo = startInfo;
process.Start();
return;
}
if (!retry)
StartProgram();
else
OnLoad();
}
private void OnUpdateCancel(object sender, EventArgs e)
{
// cancel update
cancellation?.Cancel();
cancellation?.Dispose();
cancellation = null;
}
protected override void Dispose(bool disposing)
@@ -148,5 +237,6 @@ internal sealed partial class UpdateForm : CustomForm
if (disposing)
components?.Dispose();
base.Dispose(disposing);
OnUpdateCancel(null, null);
}
}
+2 -4
View File
@@ -2,7 +2,6 @@ using System;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using CreamInstaller.Forms;
@@ -20,19 +19,18 @@ internal static class Program
internal const string RepositoryOwner = "pointfeev";
internal static readonly string RepositoryName = Name;
internal static readonly string RepositoryPackage = Name + ".zip";
internal static readonly string RepositoryExecutable = Name + ".exe";
#if DEBUG
internal static readonly string ApplicationName = Name + " v" + Version + "-debug: " + Description;
internal static readonly string ApplicationNameShort = Name + " v" + Version + "-debug";
internal static readonly string ApplicationExecutable = Name + "-debug.exe"; // should be the same as in .csproj
#else
internal static readonly string ApplicationName = Name + " v" + Version + ": " + Description;
internal static readonly string ApplicationNameShort = Name + " v" + Version;
internal static readonly string ApplicationExecutable = Name + ".exe"; // should be the same as in .csproj
#endif
internal static readonly Assembly EntryAssembly = Assembly.GetEntryAssembly();
private static readonly Process CurrentProcess = Process.GetCurrentProcess();
internal static readonly string CurrentProcessFilePath = CurrentProcess.MainModule?.FileName;
internal static readonly int CurrentProcessId = CurrentProcess.Id;
internal static bool BlockProtectedGames = true;
internal static readonly string[] ProtectedGames = { "PAYDAY 2" };
@@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace CreamInstaller;
public class Release
public class ProgramRelease
{
private Asset asset;
private string[] changes;
private Version version;
@@ -28,6 +31,8 @@ public class Release
[JsonProperty("body", NullValueHandling = NullValueHandling.Ignore)]
public string Body { get; set; }
public Asset Asset => asset ??= Assets.FirstOrDefault(a => a.Name == Program.RepositoryPackage);
public Version Version => version ??= new(TagName[1..]);
public string[] Changes => changes ??= Body.Replace("- ", "").Split("\r\n");
@@ -38,12 +43,6 @@ public class Asset
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
[JsonProperty("content_type", NullValueHandling = NullValueHandling.Ignore)]
public string ContentType { get; set; }
[JsonProperty("state", NullValueHandling = NullValueHandling.Ignore)]
public string State { get; set; }
[JsonProperty("size", NullValueHandling = NullValueHandling.Ignore)]
public int Size { get; set; }
+1 -1
View File
@@ -79,7 +79,7 @@ internal static class Koaloader
{
/*if (installForm is not null)
installForm.UpdateUser("Generating Koaloader configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
config.CreateFile(true, installForm);
config.CreateFile(true, installForm).Close();
StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer, targets, modules, installForm);
writer.Flush();
+4 -4
View File
@@ -458,9 +458,9 @@ internal static class Resources
resource?.CopyTo(file);
break;
}
catch
catch (Exception e)
{
if (filePath.IOWarn("Failed to write a crucial manifest resource (" + resourceIdentifier + ")") is not DialogResult.OK)
if (filePath.IOWarn("Failed to write a crucial manifest resource (" + resourceIdentifier + ")", e) is not DialogResult.OK)
break;
}
}
@@ -474,9 +474,9 @@ internal static class Resources
fileStream.Write(resource);
break;
}
catch
catch (Exception e)
{
if (filePath.IOWarn("Failed to write a crucial resource") is not DialogResult.OK)
if (filePath.IOWarn("Failed to write a crucial resource", e) is not DialogResult.OK)
break;
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ internal static class ScreamAPI
{
/*if (installForm is not null)
installForm.UpdateUser("Generating ScreamAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
config.CreateFile(true, installForm);
config.CreateFile(true, installForm).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);
+1 -1
View File
@@ -60,7 +60,7 @@ internal static class SmokeAPI
{
/*if (installForm is not null)
installForm.UpdateUser("Generating SmokeAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
config.CreateFile(true, installForm);
config.CreateFile(true, installForm).Close();
StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer, selection.Id, new(extraApps.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
new(overrideDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String),
+1 -1
View File
@@ -33,7 +33,7 @@ internal static class UplayR1
{
/*if (installForm is not null)
installForm.UpdateUser("Generating Uplay R1 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
config.CreateFile(true, installForm);
config.CreateFile(true, installForm).Close();
StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm);
writer.Flush();
+1 -1
View File
@@ -35,7 +35,7 @@ internal static class UplayR2
{
/*if (installForm is not null)
installForm.UpdateUser("Generating Uplay R2 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/
config.CreateFile(true, installForm);
config.CreateFile(true, installForm).Close();
StreamWriter writer = new(config, true, Encoding.UTF8);
WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm);
writer.Flush();
+9 -4
View File
@@ -8,10 +8,8 @@ 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 string FormatException(this Exception e)
{
caption ??= Program.Name + " encountered an exception";
StringBuilder output = new();
int stackDepth = 0;
while (e is not null)
@@ -41,7 +39,14 @@ internal static class ExceptionHandler
e = e.InnerException;
stackDepth++;
}
string outputString = output.ToString();
return output.ToString();
}
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";
string outputString = e.FormatException();
if (string.IsNullOrWhiteSpace(outputString))
outputString = e?.ToString() ?? "Unknown exception";
using DialogForm dialogForm = new(form ?? Form.ActiveForm);
+41 -31
View File
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.IO.Compression;
@@ -38,9 +39,9 @@ internal static class SafeIO
Directory.CreateDirectory(directoryPath);
break;
}
catch
catch (Exception e)
{
if (!crucial || directoryPath.DirectoryExists() || directoryPath.IOWarn("Failed to create a crucial directory", form) is not DialogResult.OK)
if (!crucial || directoryPath.DirectoryExists() || directoryPath.IOWarn("Failed to create a crucial directory", e, form) is not DialogResult.OK)
break;
}
}
@@ -55,9 +56,9 @@ internal static class SafeIO
Directory.Move(directoryPath, newDirectoryPath);
break;
}
catch
catch (Exception e)
{
if (!crucial || !directoryPath.DirectoryExists() || directoryPath.IOWarn("Failed to move a crucial directory", form) is not DialogResult.OK)
if (!crucial || !directoryPath.DirectoryExists() || directoryPath.IOWarn("Failed to move a crucial directory", e, form) is not DialogResult.OK)
break;
}
}
@@ -72,9 +73,10 @@ internal static class SafeIO
Directory.Delete(directoryPath, true);
break;
}
catch
catch (Exception e)
{
if (!crucial || !directoryPath.DirectoryExists() || directoryPath.IOWarn("Failed to delete a crucial directory", form) is not DialogResult.OK)
if (!crucial || !directoryPath.DirectoryExists()
|| directoryPath.IOWarn("Failed to delete a crucial directory", e, form) is not DialogResult.OK)
break;
}
}
@@ -91,10 +93,10 @@ internal static class SafeIO
? Directory.EnumerateFiles(directoryPath, filePattern, new EnumerationOptions { RecurseSubdirectories = true })
: Directory.EnumerateFiles(directoryPath, filePattern);
}
catch
catch (Exception e)
{
if (!crucial || !directoryPath.DirectoryExists()
|| directoryPath.IOWarn("Failed to enumerate a crucial directory's files", form) is not DialogResult.OK)
|| directoryPath.IOWarn("Failed to enumerate a crucial directory's files", e, form) is not DialogResult.OK)
break;
}
return Enumerable.Empty<string>();
@@ -112,10 +114,10 @@ internal static class SafeIO
? Directory.EnumerateDirectories(directoryPath, directoryPattern, new EnumerationOptions { RecurseSubdirectories = true })
: Directory.EnumerateDirectories(directoryPath, directoryPattern);
}
catch
catch (Exception e)
{
if (!crucial || !directoryPath.DirectoryExists()
|| directoryPath.IOWarn("Failed to enumerate a crucial directory's subdirectories", form) is not DialogResult.OK)
|| directoryPath.IOWarn("Failed to enumerate a crucial directory's subdirectories", e, form) is not DialogResult.OK)
break;
}
return Enumerable.Empty<string>();
@@ -123,19 +125,19 @@ internal static class SafeIO
internal static bool FileExists(this string filePath) => File.Exists(filePath);
internal static void CreateFile(this string filePath, bool crucial = false, Form form = null)
internal static FileStream CreateFile(this string filePath, bool crucial = false, Form form = null)
{
while (!Program.Canceled)
try
{
File.Create(filePath).Close();
break;
return File.Create(filePath);
}
catch
catch (Exception e)
{
if (!crucial || filePath.IOWarn("Failed to create a crucial file", form) is not DialogResult.OK)
if (!crucial || filePath.IOWarn("Failed to create a crucial file", e, form) is not DialogResult.OK)
break;
}
return null;
}
internal static void MoveFile(this string filePath, string newFilePath, bool crucial = false, Form form = null)
@@ -148,9 +150,9 @@ internal static class SafeIO
File.Move(filePath, newFilePath);
break;
}
catch
catch (Exception e)
{
if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to move a crucial file", form) is not DialogResult.OK)
if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to move a crucial file", e, form) is not DialogResult.OK)
break;
}
}
@@ -165,9 +167,9 @@ internal static class SafeIO
File.Delete(filePath);
break;
}
catch
catch (Exception e)
{
if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to delete a crucial file", form) is not DialogResult.OK)
if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to delete a crucial file", e, form) is not DialogResult.OK)
break;
}
}
@@ -181,9 +183,9 @@ internal static class SafeIO
{
return File.ReadAllText(filePath, Encoding.UTF8);
}
catch
catch (Exception e)
{
if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to read a crucial file", form) is not DialogResult.OK)
if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to read a crucial file", e, form) is not DialogResult.OK)
break;
}
return null;
@@ -198,9 +200,9 @@ internal static class SafeIO
{
return File.ReadAllBytes(filePath);
}
catch
catch (Exception e)
{
if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to read a crucial file", form) is not DialogResult.OK)
if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to read a crucial file", e, form) is not DialogResult.OK)
break;
}
return null;
@@ -214,9 +216,9 @@ internal static class SafeIO
File.WriteAllText(filePath, text, Encoding.UTF8);
break;
}
catch
catch (Exception e)
{
if (!crucial || filePath.IOWarn("Failed to write a crucial file", form) is not DialogResult.OK)
if (!crucial || filePath.IOWarn("Failed to write a crucial file", e, form) is not DialogResult.OK)
break;
}
}
@@ -231,16 +233,24 @@ internal static class SafeIO
ZipFile.ExtractToDirectory(archivePath, destinationPath);
break;
}
catch
catch (Exception e)
{
if (!crucial || !archivePath.FileExists() || archivePath.IOWarn("Failed to extract a crucial zip file", form) is not DialogResult.OK)
if (!crucial || !archivePath.FileExists() || archivePath.IOWarn("Failed to extract a crucial zip file", e, form) is not DialogResult.OK)
break;
}
}
internal static DialogResult IOWarn(this string filePath, string message, Form form = null)
internal static DialogResult IOWarn(this string filePath, string message, Exception e, Form form = null)
{
using DialogForm dialogForm = new(form ?? Form.ActiveForm);
return dialogForm.Show(SystemIcons.Warning, message + ": " + filePath.BeautifyPath(), "Retry", "OK");
form ??= Form.ActiveForm;
if (form is null || !form.InvokeRequired)
return filePath.IOWarnInternal(message, e, form);
return form.Invoke(() => filePath.IOWarnInternal(message, e, form));
}
private static DialogResult IOWarnInternal(this string filePath, string message, Exception e, Form form = null)
{
using DialogForm dialogForm = new(form);
return dialogForm.Show(SystemIcons.Warning, message + ": " + filePath.BeautifyPath() + "\n\n" + e.FormatException(), "Retry", "OK");
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 24 KiB