diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj
index 3e7e6aa..f0e5675 100644
--- a/CreamInstaller/CreamInstaller.csproj
+++ b/CreamInstaller/CreamInstaller.csproj
@@ -4,7 +4,7 @@
net7.0-windows
True
Resources\ini.ico
- 4.7.1.3
+ 4.8.0.0
2021, pointfeev (https://github.com/pointfeev)
CreamInstaller
Automatic DLC Unlocker Installer & Configuration Generator
@@ -147,7 +147,6 @@
-
diff --git a/CreamInstaller/Forms/MainForm.cs b/CreamInstaller/Forms/MainForm.cs
deleted file mode 100644
index 57d2452..0000000
--- a/CreamInstaller/Forms/MainForm.cs
+++ /dev/null
@@ -1,240 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.IO;
-using System.Linq;
-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.Forms;
-
-internal sealed partial class MainForm : CustomForm
-{
- private CancellationTokenSource cancellationTokenSource;
- private Version latestVersion;
-
- private UpdateManager updateManager;
- private IReadOnlyList versions;
-
- internal MainForm()
- {
- InitializeComponent();
- Text = Program.ApplicationNameShort;
- }
-
- private void StartProgram()
- {
- if (cancellationTokenSource is not null)
- {
- cancellationTokenSource.Cancel();
- cancellationTokenSource.Dispose();
- cancellationTokenSource = null;
- }
-#pragma warning disable CA2000 // Dispose objects before losing scope
- SelectForm form = new();
-#pragma warning restore CA2000 // Dispose objects before losing scope
- form.InheritLocation(this);
- form.FormClosing += (_, _) => Close();
- form.Show();
- Hide();
-#if DEBUG
- DebugForm.Current.Attach(form);
-#endif
- }
-
- private async void OnLoad()
- {
- progressBar.Visible = false;
- ignoreButton.Visible = true;
- updateButton.Text = "Update";
- updateButton.Click -= OnUpdateCancel;
- progressLabel.Text = "Checking for updates . . .";
- changelogTreeView.Visible = false;
- changelogTreeView.Location = progressLabel.Location with { Y = progressLabel.Location.Y + progressLabel.Size.Height + 13 };
- Refresh();
-#if DEBUG
- DebugForm.Current.Attach(this);
-#endif
- GithubPackageResolver resolver = new(Program.RepositoryOwner, Program.RepositoryName, Program.RepositoryPackage);
- ZipPackageExtractor extractor = new();
- updateManager = new(AssemblyMetadata.FromAssembly(Program.EntryAssembly, Program.CurrentProcessFilePath), resolver, extractor);
- if (latestVersion is null)
- {
- cancellationTokenSource = new();
- try
- {
- CheckForUpdatesResult checkForUpdatesResult = await updateManager.CheckForUpdatesAsync(cancellationTokenSource.Token);
-#if !DEBUG
- if (checkForUpdatesResult.CanUpdate)
- {
-#endif
- latestVersion = checkForUpdatesResult.LastVersion;
- versions = checkForUpdatesResult.Versions;
-#if !DEBUG
- }
-#endif
- }
-#if DEBUG
- catch (TaskCanceledException) { }
- catch (Exception e)
- {
- DebugForm.Current.Log($"Exception while checking for updates: {e.GetType()} ({e.Message})", LogTextBox.Warning);
- }
-#else
- catch
- {
- // ignored
- }
-#endif
- finally
- {
- cancellationTokenSource.Dispose();
- cancellationTokenSource = null;
- }
- }
- if (latestVersion is null)
- {
- updateManager.Dispose();
- updateManager = null;
- StartProgram();
- }
- else
- {
- progressLabel.Text = $"An update is available: v{latestVersion}";
- ignoreButton.Enabled = true;
- updateButton.Enabled = true;
- 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())))
-#else
- foreach (Version version in versions.Where(v => v > currentVersion && !changelogTreeView.Nodes.ContainsKey(v.ToString())))
-#endif
- {
- TreeNode root = new($"v{version}") { Name = version.ToString() };
- changelogTreeView.Nodes.Add(root);
- if (changelogTreeView.Nodes.Count > 0)
- changelogTreeView.Nodes[0].EnsureVisible();
- _ = Task.Run(async () =>
- {
- HtmlNodeCollection nodes = await HttpClientManager.GetDocumentNodes(
- $"https://github.com/{Program.RepositoryOwner}/{Program.RepositoryName}/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)
- changelogTreeView.Invoke(delegate
- {
- TreeNode change = new() { Text = HttpUtility.HtmlDecode(node.InnerText) ?? string.Empty };
- root.Nodes.Add(change);
- root.Expand();
- if (changelogTreeView.Nodes.Count > 0)
- changelogTreeView.Nodes[0].EnsureVisible();
- });
- });
- }
- }
- }
-
- private void OnLoad(object sender, EventArgs _)
- {
- 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;
- }
- }
- OnLoad();
- }
- catch (Exception e)
- {
- if (e.HandleException(this))
- goto retry;
- Close();
- }
- }
-
- private void OnIgnore(object sender, EventArgs e) => StartProgram();
-
- private async void OnUpdate(object sender, EventArgs e)
- {
- progressBar.Visible = true;
- ignoreButton.Visible = false;
- updateButton.Text = "Cancel";
- updateButton.Click -= OnUpdate;
- updateButton.Click += OnUpdateCancel;
- changelogTreeView.Location = progressBar.Location with { Y = progressBar.Location.Y + progressBar.Size.Height + 6 };
- Refresh();
- Progress progress = new();
- progress.ProgressChanged += delegate(object _, double _progress)
- {
- progressLabel.Text = $"Updating . . . {(int)_progress}%";
- progressBar.Value = (int)_progress;
- };
- progressLabel.Text = "Updating . . . ";
- cancellationTokenSource = new();
- try
- {
- await updateManager.PrepareUpdateAsync(latestVersion, progress, cancellationTokenSource.Token);
- }
-#if DEBUG
- catch (TaskCanceledException) { }
- catch (Exception ex)
- {
- DebugForm.Current.Log($"Exception while preparing update: {ex.GetType()} ({ex.Message})", LogTextBox.Warning);
- }
-#else
- catch
- {
- // ignored
- }
-#endif
- finally
- {
- cancellationTokenSource.Dispose();
- cancellationTokenSource = null;
- }
- if (updateManager is not null && updateManager.IsUpdatePrepared(latestVersion))
- {
- updateManager.LaunchUpdater(latestVersion);
- Application.Exit();
- return;
- }
- OnLoad();
- }
-
- private void OnUpdateCancel(object sender, EventArgs e)
- {
- cancellationTokenSource?.Cancel();
- updateManager?.Dispose();
- updateManager = null;
- }
-
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- components?.Dispose();
- base.Dispose(disposing);
- cancellationTokenSource?.Dispose();
- updateManager?.Dispose();
- }
-}
\ No newline at end of file
diff --git a/CreamInstaller/Forms/MainForm.Designer.cs b/CreamInstaller/Forms/UpdateForm.Designer.cs
similarity index 97%
rename from CreamInstaller/Forms/MainForm.Designer.cs
rename to CreamInstaller/Forms/UpdateForm.Designer.cs
index 946069b..267e1f7 100644
--- a/CreamInstaller/Forms/MainForm.Designer.cs
+++ b/CreamInstaller/Forms/UpdateForm.Designer.cs
@@ -4,7 +4,7 @@ using CreamInstaller.Components;
namespace CreamInstaller.Forms
{
- partial class MainForm
+ partial class UpdateForm
{
private IContainer components = null;
@@ -78,7 +78,7 @@ namespace CreamInstaller.Forms
this.changelogTreeView.Sorted = true;
this.changelogTreeView.TabIndex = 5;
//
- // MainForm
+ // UpdateForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
@@ -94,9 +94,9 @@ namespace CreamInstaller.Forms
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
- this.Name = "MainForm";
+ this.Name = "UpdateForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
- this.Text = "MainForm";
+ this.Text = "UpdateForm";
this.Load += new System.EventHandler(this.OnLoad);
this.ResumeLayout(false);
diff --git a/CreamInstaller/Forms/UpdateForm.cs b/CreamInstaller/Forms/UpdateForm.cs
new file mode 100644
index 0000000..ccbf6dc
--- /dev/null
+++ b/CreamInstaller/Forms/UpdateForm.cs
@@ -0,0 +1,152 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Windows.Forms;
+using CreamInstaller.Components;
+using CreamInstaller.Utility;
+using Newtonsoft.Json;
+
+namespace CreamInstaller.Forms;
+
+internal sealed partial class UpdateForm : CustomForm
+{
+ private Release latestRelease;
+
+ internal UpdateForm()
+ {
+ InitializeComponent();
+ Text = Program.ApplicationNameShort;
+ }
+
+ private void StartProgram()
+ {
+ SelectForm form = new();
+ form.InheritLocation(this);
+ form.FormClosing += (_, _) => Close();
+ form.Show();
+ Hide();
+#if DEBUG
+ DebugForm.Current.Attach(form);
+#endif
+ }
+
+ private async void OnLoad()
+ {
+ progressBar.Visible = false;
+ ignoreButton.Visible = true;
+ updateButton.Text = "Update";
+ updateButton.Click -= OnUpdateCancel;
+ progressLabel.Text = "Checking for updates . . .";
+ changelogTreeView.Visible = false;
+ changelogTreeView.Location = progressLabel.Location with { Y = progressLabel.Location.Y + progressLabel.Size.Height + 13 };
+ Refresh();
+#if !DEBUG
+ Version currentVersion = new(Program.Version);
+#endif
+ List 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>(response)
+ ?.Where(release => !release.Draft && !release.Prerelease && release.Assets.Count > 0).ToList();
+ latestRelease = releases?.FirstOrDefault();
+#if DEBUG
+ if (latestRelease?.Version is not { } latestVersion)
+#else
+ if (latestRelease?.Version is not { } latestVersion || latestVersion <= currentVersion)
+#endif
+ StartProgram();
+ else
+ {
+ progressLabel.Text = $"An update is available: v{latestVersion}";
+ ignoreButton.Enabled = true;
+ updateButton.Enabled = true;
+ updateButton.Click += OnUpdate;
+ changelogTreeView.Visible = true;
+ for (int r = releases!.Count - 1; r >= 0; r--)
+ {
+ Release release = releases[r];
+#if !DEBUG
+ if (release.Version <= currentVersion)
+ continue;
+#endif
+ TreeNode root = new(release.Name) { Name = release.Name };
+ changelogTreeView.Nodes.Add(root);
+ if (changelogTreeView.Nodes.Count > 0)
+ changelogTreeView.Nodes[0].EnsureVisible();
+ for (int i = release.Changes.Length - 1; i >= 0; i--)
+ changelogTreeView.Invoke(delegate
+ {
+ string change = release.Changes[i];
+ TreeNode changeNode = new() { Text = change };
+ root.Nodes.Add(changeNode);
+ root.Expand();
+ if (changelogTreeView.Nodes.Count > 0)
+ changelogTreeView.Nodes[0].EnsureVisible();
+ });
+ }
+ }
+ }
+
+ private void OnLoad(object sender, EventArgs _)
+ {
+ 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;
+ }
+ }
+ OnLoad();
+ }
+ catch (Exception e)
+ {
+ if (e.HandleException(this))
+ goto retry;
+ Close();
+ }
+ }
+
+ private void OnIgnore(object sender, EventArgs e) => StartProgram();
+
+ private void OnUpdate(object sender, EventArgs e)
+ {
+ progressBar.Visible = true;
+ ignoreButton.Visible = false;
+ updateButton.Text = "Cancel";
+ updateButton.Click -= OnUpdate;
+ updateButton.Click += OnUpdateCancel;
+ changelogTreeView.Location = progressBar.Location with { Y = progressBar.Location.Y + progressBar.Size.Height + 6 };
+ Refresh();
+ Progress progress = new();
+ progress.ProgressChanged += delegate(object _, double _progress)
+ {
+ progressLabel.Text = $"Updating . . . {(int)_progress}%";
+ progressBar.Value = (int)_progress;
+ };
+ progressLabel.Text = "Updating . . . ";
+ // do update
+ OnLoad();
+ }
+
+ private void OnUpdateCancel(object sender, EventArgs e)
+ {
+ // cancel update
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ components?.Dispose();
+ base.Dispose(disposing);
+ }
+}
\ No newline at end of file
diff --git a/CreamInstaller/Forms/MainForm.resx b/CreamInstaller/Forms/UpdateForm.resx
similarity index 100%
rename from CreamInstaller/Forms/MainForm.resx
rename to CreamInstaller/Forms/UpdateForm.resx
diff --git a/CreamInstaller/Platforms/Steam/SteamStore.cs b/CreamInstaller/Platforms/Steam/SteamStore.cs
index 0d38df4..db10308 100644
--- a/CreamInstaller/Platforms/Steam/SteamStore.cs
+++ b/CreamInstaller/Platforms/Steam/SteamStore.cs
@@ -40,7 +40,7 @@ internal static class SteamStore
string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}");
if (response is not null)
{
- IDictionary apps = (IDictionary)JsonConvert.DeserializeObject(response);
+ Dictionary apps = JsonConvert.DeserializeObject>(response);
if (apps is not null)
foreach (KeyValuePair app in apps)
try
diff --git a/CreamInstaller/Program.cs b/CreamInstaller/Program.cs
index 1ccf65a..7f24d2f 100644
--- a/CreamInstaller/Program.cs
+++ b/CreamInstaller/Program.cs
@@ -86,7 +86,7 @@ internal static class Program
try
{
HttpClientManager.Setup();
- using MainForm form = new();
+ using UpdateForm form = new();
#if DEBUG
DebugForm.Current.Attach(form);
#endif
diff --git a/CreamInstaller/Release.cs b/CreamInstaller/Release.cs
new file mode 100644
index 0000000..3e4e8de
--- /dev/null
+++ b/CreamInstaller/Release.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace CreamInstaller;
+
+public class Release
+{
+ private string[] changes;
+
+ private Version version;
+
+ [JsonProperty("tag_name", NullValueHandling = NullValueHandling.Ignore)]
+ public string TagName { get; set; }
+
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; set; }
+
+ [JsonProperty("draft", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Draft { get; set; }
+
+ [JsonProperty("prerelease", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Prerelease { get; set; }
+
+ [JsonProperty("assets", NullValueHandling = NullValueHandling.Ignore)]
+ public List Assets { get; } = new();
+
+ [JsonProperty("body", NullValueHandling = NullValueHandling.Ignore)]
+ public string Body { get; set; }
+
+ public Version Version => version ??= new(TagName[1..]);
+
+ public string[] Changes => changes ??= Body.Replace("- ", "").Split("\r\n");
+}
+
+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; }
+
+ [JsonProperty("browser_download_url", NullValueHandling = NullValueHandling.Ignore)]
+ public string BrowserDownloadUrl { get; set; }
+}
\ No newline at end of file
diff --git a/CreamInstaller/Utility/HttpClientManager.cs b/CreamInstaller/Utility/HttpClientManager.cs
index 967a51f..16fa397 100644
--- a/CreamInstaller/Utility/HttpClientManager.cs
+++ b/CreamInstaller/Utility/HttpClientManager.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Drawing;
using System.Net;
using System.Net.Http;
@@ -14,6 +15,8 @@ internal static class HttpClientManager
{
internal static HttpClient HttpClient;
+ private static readonly Dictionary HttpContentCache = new();
+
internal static void Setup()
{
HttpClient = new();
@@ -26,8 +29,12 @@ internal static class HttpClientManager
{
using HttpRequestMessage request = new(HttpMethod.Get, url);
using HttpResponseMessage response = await HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
+ if (response.StatusCode is HttpStatusCode.NotModified && HttpContentCache.TryGetValue(url, out string content))
+ return content;
_ = response.EnsureSuccessStatusCode();
- return await response.Content.ReadAsStringAsync();
+ content = await response.Content.ReadAsStringAsync();
+ HttpContentCache[url] = content;
+ return content;
}
catch (HttpRequestException e)
{