diff --git a/PCK-Studio-Updater/API/GithubParams.cs b/PCK-Studio-Updater/API/GithubParams.cs new file mode 100644 index 00000000..77aed764 --- /dev/null +++ b/PCK-Studio-Updater/API/GithubParams.cs @@ -0,0 +1,22 @@ +using System.Text.RegularExpressions; + +namespace PCKStudio_Updater +{ + public sealed class GithubParams + { + public readonly string RepositoryOwnerName; + public readonly string RepositoryName; + public readonly string TargetExecutableName; + public readonly bool UsePreRelease; + public readonly Regex VersionMatcher; + + public GithubParams(string repositoryOwnerName, string repositoryName, string targetExecutableName, bool usePreRelease, Regex versionMatcher) + { + RepositoryOwnerName = repositoryOwnerName; + RepositoryName = repositoryName; + TargetExecutableName = targetExecutableName; + UsePreRelease = usePreRelease; + VersionMatcher = versionMatcher; + } + } +} diff --git a/PCK-Studio-Updater/API/GithubUpdateDownloader.cs b/PCK-Studio-Updater/API/GithubUpdateDownloader.cs new file mode 100644 index 00000000..b89c3897 --- /dev/null +++ b/PCK-Studio-Updater/API/GithubUpdateDownloader.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO.Compression; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Octokit; + +namespace PCKStudio_Updater +{ + public sealed class GithubUpdateDownloader : IUpdateDownloader + { + private static readonly Assembly updaterAssembly = Assembly.GetAssembly(typeof(GithubUpdateDownloader)); + + private readonly GithubParams _updateParams; + private readonly GitHubClient githubClient; + private Release latestFetchedRelease; + private Version latestReleaseVersion; + private DirectoryInfo downloadDirectory; + + + public GithubUpdateDownloader(GithubParams updateParams) + { + _updateParams = updateParams; + var githubClientProductHeader = new ProductHeaderValue(updaterAssembly.GetName().Name); + githubClient = new GitHubClient(githubClientProductHeader); + } + + public bool IsUpdateAvailable(FileVersionInfo fileVersionInfo) + { + return IsUpdateAvailable(fileVersionInfo.ProductVersion); + } + + public bool IsUpdateAvailable(Assembly currentAssembly) + { + if (!File.Exists(currentAssembly.Location)) + return false; + FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(currentAssembly.Location); + return IsUpdateAvailable(fileVersionInfo.ProductVersion); + } + + public bool IsUpdateAvailable(Version productVersion) + { + Debug.WriteLine("Release Product ver.: " + latestReleaseVersion); + Debug.WriteLine("Current Product ver.: " + productVersion); + return latestReleaseVersion.CompareTo(productVersion) > 0; + } + + public bool IsUpdateAvailable(string productVersion) + { + GetLatestRelease(_updateParams.UsePreRelease); + if (Version.TryParse(productVersion, out var currentVersion)) + { + return IsUpdateAvailable(currentVersion); + } + return false; + } + + private void UnpackZip(string zipFilePath) + { + ZipFile.ExtractToDirectory(zipFilePath, Path.GetDirectoryName(zipFilePath)); + } + + private static void DownloadAsset(ReleaseAsset asset, Stream destination) + { + string downloadUrl = asset.BrowserDownloadUrl; + var client = new WebClient(); + using (var serverStream = client.OpenRead(downloadUrl)) + { + serverStream.CopyTo(destination); + } + } + + private void GetLatestRelease(bool prerelease) + { + Release release; + if (prerelease) + { + var prereleaseTask = githubClient.Repository.Release.GetAll(_updateParams.RepositoryOwnerName, _updateParams.RepositoryName); + prereleaseTask.Wait(); + var prereleases = prereleaseTask.Result.OrderByDescending(release => release.PublishedAt ?? release.CreatedAt).Where(release => release.Prerelease).ToArray(); + release = latestFetchedRelease = prereleases[0]; + } + else + { + var latestReleaseTask = githubClient.Repository.Release.GetLatest(_updateParams.RepositoryOwnerName, _updateParams.RepositoryName); + latestReleaseTask.Wait(); + release = latestFetchedRelease = latestReleaseTask.Result; + } + var match = _updateParams.VersionMatcher.Match(release.Name); + if (match.Success) + { + string versionString = match.Value; + Version.TryParse(versionString, out latestReleaseVersion); + } + } + + private void EmptyDirectory(DirectoryInfo directory) + { + string appname = Assembly.GetExecutingAssembly().GetName().Name; + foreach (FileInfo file in directory.GetFiles()) + { + if (Path.GetFileNameWithoutExtension(file.Name) != appname && file.Name != "update.zip") + file.Delete(); + } + foreach (DirectoryInfo subDirectory in directory.GetDirectories()) + subDirectory.Delete(true); + } + + public void DownloadTo(DirectoryInfo directory) + { + if (latestFetchedRelease is null) + GetLatestRelease(_updateParams.UsePreRelease); + if (latestFetchedRelease.Assets?.Count > 0) + { + var asset = latestFetchedRelease.Assets[0]; + string zipFilePath = Path.Combine(directory.FullName, "update.zip"); + using(var zipFileStream = File.OpenWrite(zipFilePath)) + { + DownloadAsset(asset, zipFileStream); + } + Debug.WriteLine("Download Complete", category: nameof(GithubUpdateDownloader)); + EmptyDirectory(directory); + UnpackZip(zipFilePath); + File.Delete(zipFilePath); + downloadDirectory = directory; + } + } + + public void Launch() + { + if (downloadDirectory is null) + { + throw new ArgumentNullException("Download directory not set."); + } + + var files = downloadDirectory.GetFiles(_updateParams.TargetExecutableName + ".exe", SearchOption.TopDirectoryOnly); + if (files is not null && files.Length > 0) + { + Process.Start(files[0].FullName); + } + } + } +} diff --git a/PCK-Studio-Updater/API/IUpdateDownloader.cs b/PCK-Studio-Updater/API/IUpdateDownloader.cs new file mode 100644 index 00000000..ef422762 --- /dev/null +++ b/PCK-Studio-Updater/API/IUpdateDownloader.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PCKStudio_Updater +{ + public interface IUpdateDownloader + { + public bool IsUpdateAvailable(Version currentVersion); + public bool IsUpdateAvailable(string currentVersionString); + + public void DownloadTo(DirectoryInfo directory); + + public void Launch(); + } +} diff --git a/PCK-Studio-Updater/PCK-Studio-Updater.csproj b/PCK-Studio-Updater/PCK-Studio-Updater.csproj new file mode 100644 index 00000000..a47ab997 --- /dev/null +++ b/PCK-Studio-Updater/PCK-Studio-Updater.csproj @@ -0,0 +1,105 @@ + + + + + Debug + AnyCPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF} + WinExe + PCKStudio_Updater + PCK-Studio-Updater + latest + enable + v4.8 + 512 + true + true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + PCKStudio_Updater.Program + + + ProjectLogo.ico + + + + + + + + + + + + + + + + + + + + + + 5.7.0 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + 7.1.0 + + + + + False + Microsoft .NET Framework 4.8 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + \ No newline at end of file diff --git a/PCK-Studio-Updater/Program.cs b/PCK-Studio-Updater/Program.cs new file mode 100644 index 00000000..d150ad4c --- /dev/null +++ b/PCK-Studio-Updater/Program.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime; +using System.Text; +using System.Text.RegularExpressions; + +namespace PCKStudio_Updater +{ + internal class Program + { + static void Main(string[] args) + { + Uri projectUrl = new Uri("https://github.com/PhoenixARC/-PCK-Studio"); + if (args.Length > 0) + { + projectUrl = new Uri(args[0]); + } + + string executableName = "PCK-Studio"; + if (args.Length > 1) + { + executableName = args[1]; + } + + bool prerelease = false; + if (args.Length > 2) + { + prerelease = args[2].ToLower() == "true" || args[2].ToLower() == "1"; + } + + var versionMatcher = new Regex("(\\*|\\d+(\\.\\d+){0,3}(\\.\\*)?)"); + if (args.Length > 3) + { + versionMatcher = new Regex(args[3]); + } + + GithubParams updateParams = new GithubParams( + Path.GetDirectoryName(projectUrl.AbsolutePath).Replace("\\", ""), + Path.GetFileName(projectUrl.AbsolutePath), + executableName, + prerelease, + versionMatcher + ); + + IUpdateDownloader updater = new GithubUpdateDownloader(updateParams); + + if (!File.Exists(updateParams.TargetExecutableName + ".exe") || updater.IsUpdateAvailable(FileVersionInfo.GetVersionInfo(updateParams.TargetExecutableName + ".exe").ProductVersion)) + { + updater.DownloadTo(new DirectoryInfo(".")); + updater.Launch(); + return; + } + } + } +} diff --git a/PCK-Studio-Updater/ProjectLogo.ico b/PCK-Studio-Updater/ProjectLogo.ico new file mode 100644 index 00000000..85357f6f Binary files /dev/null and b/PCK-Studio-Updater/ProjectLogo.ico differ diff --git a/PCK-Studio-Updater/Properties/AssemblyInfo.cs b/PCK-Studio-Updater/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..8e714011 --- /dev/null +++ b/PCK-Studio-Updater/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PckStudio-Updater")] +[assembly: AssemblyDescription("Updater for PCK-Studio")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PckStudio-Updater")] +[assembly: AssemblyCopyright("Copyright © 2023 Miku-666")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5b223556-15b9-41da-aa0b-5e7f45e743bf")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/PCK-Studio-Updater/Properties/app.manifest b/PCK-Studio-Updater/Properties/app.manifest new file mode 100644 index 00000000..c02c2d89 --- /dev/null +++ b/PCK-Studio-Updater/Properties/app.manifest @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PCK-Studio/App.config b/PCK-Studio/App.config index fff538b6..f3cb4efa 100644 --- a/PCK-Studio/App.config +++ b/PCK-Studio/App.config @@ -71,6 +71,12 @@ True + + True + + + False + False diff --git a/PCK-Studio/Classes/Misc/RichPresenceClient.cs b/PCK-Studio/Classes/Misc/RichPresenceClient.cs index e7bef380..fdbad35f 100644 --- a/PCK-Studio/Classes/Misc/RichPresenceClient.cs +++ b/PCK-Studio/Classes/Misc/RichPresenceClient.cs @@ -27,7 +27,7 @@ namespace PckStudio.Classes.Misc new Button() { Label = "Check it out!", - Url = Program.ProjectUrl, + Url = Program.ProjectUrl.AbsoluteUri, } }; diff --git a/PCK-Studio/Classes/Networking/Network.cs b/PCK-Studio/Classes/Networking/Network.cs deleted file mode 100644 index f35575ac..00000000 --- a/PCK-Studio/Classes/Networking/Network.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net; -using System.Windows.Forms; - -namespace PckStudio.Classes.Networking -{ - [Obsolete] - class Network - { - public static string Version = Application.ProductVersion; - public static bool IsBeta = true; - public static bool Portable = false; - public static bool NeedsUpdate = false; - public static Uri MainURL = new Uri(Program.BaseAPIUrl); - public static Uri BackUpURL = new Uri(Program.BackUpAPIUrl); - static readonly string UpdatePath = "/update/Version"; - static readonly string BetaUpdatePath = "/update/VersionBeta"; - - public static void CheckUpdate() - { - using WebClient wc = new WebClient(); - try - { - Update.CheckForUpdate(null); // TODO - - Uri versionUri = new Uri(MainURL, IsBeta ? BetaUpdatePath : UpdatePath); - Console.WriteLine(versionUri); - string serverVersion = wc.DownloadString(versionUri); - if (Version != serverVersion) - { - if (MessageBox.Show("An update is available! Do you want to update?" + - $"\nYour Version: {Version}" + - $"\nAvailable version: {serverVersion}", "Update Available", MessageBoxButtons.YesNo) == DialogResult.Yes) - { - Update.UpdateProgram(new UpdateOptions( - isBeta: IsBeta, - isPortable: Portable, - baseUri: new Uri(MainURL, "/Update/Download/setup/PCKStudio-Setup.msi"), - betaUri: new Uri(MainURL, "/Update/Download/setup/beta/PCKStudioBeta-Setup.msi") - ) - ); - } - else - { - NeedsUpdate = true; - } - } - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message); - MessageBox.Show("Can't connect to the server!", "Server unavailabe"); - } - } - - - } -} diff --git a/PCK-Studio/Classes/Networking/Update.cs b/PCK-Studio/Classes/Networking/Update.cs deleted file mode 100644 index 4813ae45..00000000 --- a/PCK-Studio/Classes/Networking/Update.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading; -using System.IO; -using System.Net; -using System.Windows.Forms; - -namespace PckStudio.Classes.Networking -{ - [Obsolete] - public enum UpdateResult - { - // Base Failure value - Failure = -1, - // Base Success value - Success, - - UpdateAvailable, - - UpdateFailure, - } - - [Obsolete] - class UpdateOptions - { - public bool IsBeta { get; set; } - public bool IsPortable { get; set; } - public string Domain - { - get => _baseDomain?.OriginalString ?? (_betaDomain?.OriginalString ?? throw new NullReferenceException(nameof(_betaDomain))); - set - { - _ = value ?? throw new NullReferenceException(nameof(value)); - _baseDomain = new Uri(value); - } - } - - private Uri _baseDomain; - private Uri _betaDomain; - - public UpdateOptions(bool isBeta, bool isPortable, Uri baseUri, Uri betaUri) - { - IsBeta = isBeta; - IsPortable = isPortable; - _baseDomain = baseUri; - _betaDomain = betaUri; - } - } - - [Obsolete] - static class Update - { - public static UpdateResult CheckForUpdate(UpdateOptions options) - { - // TODO: implement this - return UpdateResult.Failure; - } - - public static void UpdateProgram(UpdateOptions options) - { - string updateURL = options.Domain; - if (options.IsPortable) - { - updateURL = updateURL.Replace(".msi","Portable.msi"); - } - - string downloadPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Temp\\"; - string destinationURL = options.Domain; - if (TryDownloadFile(downloadPath + Path.GetFileName(destinationURL), destinationURL)) - { - Process.Start(downloadPath + Path.GetFileName(destinationURL)); - Application.Exit(); - } - } - - static bool TryDownloadFile(string filePath, string url) - { - try - { - using (WebClient client = new WebClient()) - client.DownloadFile(url, filePath); - return true; - } - catch (Exception ex) { Console.WriteLine(ex.Message); } - return false; - } - } -} diff --git a/PCK-Studio/Forms/AppSettingsForm.Designer.cs b/PCK-Studio/Forms/AppSettingsForm.Designer.cs index d651aef3..977513d6 100644 --- a/PCK-Studio/Forms/AppSettingsForm.Designer.cs +++ b/PCK-Studio/Forms/AppSettingsForm.Designer.cs @@ -36,6 +36,7 @@ this.autoLoadPckCheckBox = new MetroFramework.Controls.MetroCheckBox(); this.showPresenceCheckBox = new MetroFramework.Controls.MetroCheckBox(); this.grf_paramKeyComboBoxCheckBox = new MetroFramework.Controls.MetroCheckBox(); + this.usePrereleaseCheckBox = new MetroFramework.Controls.MetroCheckBox(); this.SuspendLayout(); // // autoSaveCheckBox @@ -76,7 +77,6 @@ // autoUpdateCheckBox // this.autoUpdateCheckBox.AutoSize = true; - this.autoUpdateCheckBox.Enabled = false; this.autoUpdateCheckBox.Location = new System.Drawing.Point(23, 105); this.autoUpdateCheckBox.Name = "autoUpdateCheckBox"; this.autoUpdateCheckBox.Size = new System.Drawing.Size(90, 15); @@ -86,6 +86,7 @@ this.autoUpdateCheckBox.Theme = MetroFramework.MetroThemeStyle.Dark; this.SettingToolTip.SetToolTip(this.autoUpdateCheckBox, "Whether to automatically check for updates"); this.autoUpdateCheckBox.UseSelectable = true; + this.autoUpdateCheckBox.CheckedChanged += new System.EventHandler(this.autoUpdateCheckBox_CheckedChanged); // // autoLoadPckCheckBox // @@ -129,11 +130,26 @@ this.grf_paramKeyComboBoxCheckBox.UseSelectable = true; this.grf_paramKeyComboBoxCheckBox.CheckedChanged += new System.EventHandler(this.grf_paramKeyComboBoxCheckBox_CheckedChanged); // + // usePrereleaseCheckBox + // + this.usePrereleaseCheckBox.AutoSize = true; + this.usePrereleaseCheckBox.Location = new System.Drawing.Point(119, 105); + this.usePrereleaseCheckBox.Name = "usePrereleaseCheckBox"; + this.usePrereleaseCheckBox.Size = new System.Drawing.Size(98, 15); + this.usePrereleaseCheckBox.Style = MetroFramework.MetroColorStyle.White; + this.usePrereleaseCheckBox.TabIndex = 6; + this.usePrereleaseCheckBox.Text = "Use Beta Build"; + this.usePrereleaseCheckBox.Theme = MetroFramework.MetroThemeStyle.Dark; + this.SettingToolTip.SetToolTip(this.usePrereleaseCheckBox, "Whether to automatically check for updates"); + this.usePrereleaseCheckBox.UseSelectable = true; + this.usePrereleaseCheckBox.Visible = false; + // // AppSettingsForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(527, 270); + this.Controls.Add(this.usePrereleaseCheckBox); this.Controls.Add(this.grf_paramKeyComboBoxCheckBox); this.Controls.Add(this.showPresenceCheckBox); this.Controls.Add(this.autoLoadPckCheckBox); @@ -164,5 +180,6 @@ private MetroFramework.Controls.MetroCheckBox autoLoadPckCheckBox; private MetroFramework.Controls.MetroCheckBox showPresenceCheckBox; private MetroFramework.Controls.MetroCheckBox grf_paramKeyComboBoxCheckBox; + private MetroFramework.Controls.MetroCheckBox usePrereleaseCheckBox; } } \ No newline at end of file diff --git a/PCK-Studio/Forms/AppSettingsForm.cs b/PCK-Studio/Forms/AppSettingsForm.cs index 71725fb0..b62445a0 100644 --- a/PCK-Studio/Forms/AppSettingsForm.cs +++ b/PCK-Studio/Forms/AppSettingsForm.cs @@ -40,11 +40,21 @@ namespace PckStudio.Forms Settings.Default.ShowRichPresence = showPresenceCheckBox.Checked; } + private void autoUpdateCheckBox_CheckedChanged(object sender, EventArgs e) + { + usePrereleaseCheckBox.Visible = Settings.Default.AutoUpdate = autoUpdateCheckBox.Checked; + } + private void grf_paramKeyComboBoxCheckBox_CheckedChanged(object sender, EventArgs e) { Settings.Default.UseComboBoxForGRFParameter = grf_paramKeyComboBoxCheckBox.Checked; } + private void usePrereleaseCheckBox_CheckedChanged(object sender, EventArgs e) + { + Settings.Default.UsePrerelease = usePrereleaseCheckBox.Checked; + } + private void LoadCheckboxState(CheckBox checkBox, EventHandler eventHandler, bool state) { checkBox.CheckedChanged -= eventHandler; @@ -58,6 +68,8 @@ namespace PckStudio.Forms LoadCheckboxState(endianCheckBox, endianCheckBox_CheckedChanged, Settings.Default.UseLittleEndianAsDefault); LoadCheckboxState(autoLoadPckCheckBox, autoLoadPckCheckBox_CheckedChanged, Settings.Default.LoadSubPcks); LoadCheckboxState(showPresenceCheckBox, showPresenceCheckBox_CheckedChanged, Settings.Default.ShowRichPresence); + LoadCheckboxState(autoUpdateCheckBox, autoUpdateCheckBox_CheckedChanged, Settings.Default.AutoUpdate); + LoadCheckboxState(usePrereleaseCheckBox, usePrereleaseCheckBox_CheckedChanged, Settings.Default.UsePrerelease); LoadCheckboxState(grf_paramKeyComboBoxCheckBox, grf_paramKeyComboBoxCheckBox_CheckedChanged, Settings.Default.UseComboBoxForGRFParameter); } diff --git a/PCK-Studio/Forms/Skins-And-Textures/generateModel.resx b/PCK-Studio/Forms/Skins-And-Textures/generateModel.resx index 6db730f5..f3549bdb 100644 --- a/PCK-Studio/Forms/Skins-And-Textures/generateModel.resx +++ b/PCK-Studio/Forms/Skins-And-Textures/generateModel.resx @@ -1442,7 +1442,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAElPcGVuVEssIFZlcnNpb249My4zLjMuMCwgQ3VsdHVyZT1uZXV0 cmFsLCBQdWJsaWNLZXlUb2tlbj1iYWQxOTlmZTg0ZWIzZGY0BQEAAAAOT3BlblRLLlZlY3RvcjICAAAA AVgBWQAACwsCAAAAAAAAAAAAAAAL - + diff --git a/PCK-Studio/MainForm.Designer.cs b/PCK-Studio/MainForm.Designer.cs index 5a9e5a79..84c5d271 100644 --- a/PCK-Studio/MainForm.Designer.cs +++ b/PCK-Studio/MainForm.Designer.cs @@ -33,6 +33,7 @@ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); System.Windows.Forms.ToolStripSeparator toolStripSeparator1; System.Windows.Forms.ToolStripSeparator toolStripSeparator3; + toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); this.contextMenuPCKEntries = new System.Windows.Forms.ContextMenuStrip(this.components); this.createToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.folderToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -99,6 +100,7 @@ this.joinDevelopmentDiscordToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.trelloBoardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.checkForUpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.videosToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.howToMakeABasicSkinPackToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -147,7 +149,6 @@ this.skinRenderer3DToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); logoPictureBox = new System.Windows.Forms.PictureBox(); toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); - toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); ((System.ComponentModel.ISupportInitialize)(logoPictureBox)).BeginInit(); this.contextMenuPCKEntries.SuspendLayout(); this.menuStrip.SuspendLayout(); @@ -640,6 +641,7 @@ // helpToolStripMenuItem // this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.checkForUpdatesToolStripMenuItem, this.aboutToolStripMenuItem, toolStripSeparator1, this.videosToolStripMenuItem, @@ -650,6 +652,12 @@ this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; resources.ApplyResources(this.helpToolStripMenuItem, "helpToolStripMenuItem"); // + // checkForUpdatesToolStripMenuItem + // + this.checkForUpdatesToolStripMenuItem.Name = "checkForUpdatesToolStripMenuItem"; + resources.ApplyResources(this.checkForUpdatesToolStripMenuItem, "checkForUpdatesToolStripMenuItem"); + this.checkForUpdatesToolStripMenuItem.Click += new System.EventHandler(this.checkForUpdatesToolStripMenuItem_Click); + // // aboutToolStripMenuItem // resources.ApplyResources(this.aboutToolStripMenuItem, "aboutToolStripMenuItem"); @@ -1241,6 +1249,7 @@ private System.Windows.Forms.ToolStripMenuItem toPhoenixARCDeveloperToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem forMattNLContributorToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem checkForUpdatesToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem skinRenderer3DToolStripMenuItem; } } diff --git a/PCK-Studio/MainForm.cs b/PCK-Studio/MainForm.cs index 14806253..1e974d40 100644 --- a/PCK-Studio/MainForm.cs +++ b/PCK-Studio/MainForm.cs @@ -32,10 +32,11 @@ using PckStudio.Extensions; using PckStudio.Popups; using PckStudio.Classes.Utils; using PckStudio.Helper; +using PCKStudio_Updater; namespace PckStudio { - public partial class MainForm : MetroFramework.Forms.MetroForm + public partial class MainForm : MetroFramework.Forms.MetroForm { private PckManager PckManager = null; string saveLocation = string.Empty; @@ -51,45 +52,45 @@ namespace PckStudio { InitializeComponent(); - skinToolStripMenuItem1.Click += (sender, e) => SetFileType(PckFileType.SkinFile); - capeToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.CapeFile); - textureToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.TextureFile); - languagesFileLOCToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.LocalisationFile); - gameRulesFileGRFToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.GameRulesFile); - audioPCKFileToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.AudioFile); - coloursCOLFileToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.ColourTableFile); - gameRulesHeaderGRHToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.GameRulesHeader); - skinsPCKToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.SkinDataFile); - modelsFileBINToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.ModelsFile); - behavioursFileBINToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.BehavioursFile); - entityMaterialsFileBINToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.MaterialFile); + skinToolStripMenuItem1.Click += (sender, e) => SetFileType(PckFileType.SkinFile); + capeToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.CapeFile); + textureToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.TextureFile); + languagesFileLOCToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.LocalisationFile); + gameRulesFileGRFToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.GameRulesFile); + audioPCKFileToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.AudioFile); + coloursCOLFileToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.ColourTableFile); + gameRulesHeaderGRHToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.GameRulesHeader); + skinsPCKToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.SkinDataFile); + modelsFileBINToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.ModelsFile); + behavioursFileBINToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.BehavioursFile); + entityMaterialsFileBINToolStripMenuItem.Click += (sender, e) => SetFileType(PckFileType.MaterialFile); - treeViewMain.TreeViewNodeSorter = new PckNodeSorter(); + treeViewMain.TreeViewNodeSorter = new PckNodeSorter(); pckOpen.AllowDrop = true; Text = Application.ProductName; - labelVersion.Text = $"{Application.ProductName}: {Application.ProductVersion}"; + labelVersion.Text = $"{Application.ProductName}: {Application.ProductVersion}"; ChangelogRichTextBox.Text = Resources.CHANGELOG; - pckFileTypeHandler = new Dictionary>(15) + pckFileTypeHandler = new Dictionary>(15) { - [PckFileType.SkinFile] = HandleSkinFile, - [PckFileType.CapeFile] = null, - [PckFileType.TextureFile] = HandleTextureFile, - [PckFileType.UIDataFile] = _ => throw new NotSupportedException("unused in-game"), - [PckFileType.InfoFile] = null, + [PckFileType.SkinFile] = HandleSkinFile, + [PckFileType.CapeFile] = null, + [PckFileType.TextureFile] = HandleTextureFile, + [PckFileType.UIDataFile] = _ => throw new NotSupportedException("unused in-game"), + [PckFileType.InfoFile] = null, [PckFileType.TexturePackInfoFile] = null, - [PckFileType.LocalisationFile] = HandleLocalisationFile, - [PckFileType.GameRulesFile] = HandleGameRuleFile, - [PckFileType.AudioFile] = HandleAudioFile, - [PckFileType.ColourTableFile] = HandleColourFile, - [PckFileType.GameRulesHeader] = HandleGameRuleFile, - [PckFileType.SkinDataFile] = null, - [PckFileType.ModelsFile] = HandleModelsFile, - [PckFileType.BehavioursFile] = HandleBehavioursFile, - [PckFileType.MaterialFile] = HandleMaterialFile, + [PckFileType.LocalisationFile] = HandleLocalisationFile, + [PckFileType.GameRulesFile] = HandleGameRuleFile, + [PckFileType.AudioFile] = HandleAudioFile, + [PckFileType.ColourTableFile] = HandleColourFile, + [PckFileType.GameRulesHeader] = HandleGameRuleFile, + [PckFileType.SkinDataFile] = null, + [PckFileType.ModelsFile] = HandleModelsFile, + [PckFileType.BehavioursFile] = HandleBehavioursFile, + [PckFileType.MaterialFile] = HandleMaterialFile, }; } @@ -98,7 +99,7 @@ namespace PckStudio checkSaveState(); treeViewMain.Nodes.Clear(); currentPCK = openPck(filepath); - if (currentPCK == null) + if (currentPCK == null) { MessageBox.Show(string.Format("Failed to load {0}", Path.GetFileName(filepath)), "Error"); return; @@ -112,8 +113,8 @@ namespace PckStudio { SettingsManager.RegisterPropertyChangedCallback(nameof(Settings.Default.UseLittleEndianAsDefault), state => { - LittleEndianCheckBox.Checked = state; - }); + LittleEndianCheckBox.Checked = state; + }); SettingsManager.RegisterPropertyChangedCallback(nameof(Settings.Default.LoadSubPcks), () => { if (currentPCK is not null) @@ -122,32 +123,32 @@ namespace PckStudio } }); - imageList.Images.Add(Resources.ZZFolder); // Icon for folders - imageList.Images.Add(Resources.BINKA_ICON); // Icon for music cue file (audio.pck) - imageList.Images.Add(Resources.IMAGE_ICON); // Icon for images (unused for now) - imageList.Images.Add(Resources.LOC_ICON); // Icon for string localization files (languages.loc;localisation.loc) - imageList.Images.Add(Resources.PCK_ICON); // Icon for generic PCK files (*.pck) - imageList.Images.Add(Resources.ZUnknown); // Icon for Unknown formats - imageList.Images.Add(Resources.COL_ICON); // Icon for color palette files (colours.col) - imageList.Images.Add(Resources.SKINS_ICON); // Icon for Skin.pck archives (skins.pck) - imageList.Images.Add(Resources.MODELS_ICON); // Icon for Model files (models.bin) - imageList.Images.Add(Resources.GRF_ICON); // Icon for Game Rule files (*.grf) - imageList.Images.Add(Resources.GRH_ICON); // Icon for Game Rule Header files (*.grh) - imageList.Images.Add(Resources.INFO_ICON); // Icon for Info files (0) - imageList.Images.Add(Resources.SKIN_ICON); // Icon for Skin files (*.png) - imageList.Images.Add(Resources.CAPE_ICON); // Icon for Cape files (*.png) - imageList.Images.Add(Resources.TEXTURE_ICON); // Icon for Texture files (*.png;*.tga) - imageList.Images.Add(Resources.BEHAVIOURS_ICON); // Icon for Behaviour files (behaviours.bin) - imageList.Images.Add(Resources.ENTITY_MATERIALS_ICON); // Icon for Entity Material files (entityMaterials.bin) + imageList.Images.Add(Resources.ZZFolder); // Icon for folders + imageList.Images.Add(Resources.BINKA_ICON); // Icon for music cue file (audio.pck) + imageList.Images.Add(Resources.IMAGE_ICON); // Icon for images (unused for now) + imageList.Images.Add(Resources.LOC_ICON); // Icon for string localization files (languages.loc;localisation.loc) + imageList.Images.Add(Resources.PCK_ICON); // Icon for generic PCK files (*.pck) + imageList.Images.Add(Resources.ZUnknown); // Icon for Unknown formats + imageList.Images.Add(Resources.COL_ICON); // Icon for color palette files (colours.col) + imageList.Images.Add(Resources.SKINS_ICON); // Icon for Skin.pck archives (skins.pck) + imageList.Images.Add(Resources.MODELS_ICON); // Icon for Model files (models.bin) + imageList.Images.Add(Resources.GRF_ICON); // Icon for Game Rule files (*.grf) + imageList.Images.Add(Resources.GRH_ICON); // Icon for Game Rule Header files (*.grh) + imageList.Images.Add(Resources.INFO_ICON); // Icon for Info files (0) + imageList.Images.Add(Resources.SKIN_ICON); // Icon for Skin files (*.png) + imageList.Images.Add(Resources.CAPE_ICON); // Icon for Cape files (*.png) + imageList.Images.Add(Resources.TEXTURE_ICON); // Icon for Texture files (*.png;*.tga) + imageList.Images.Add(Resources.BEHAVIOURS_ICON); // Icon for Behaviour files (behaviours.bin) + imageList.Images.Add(Resources.ENTITY_MATERIALS_ICON); // Icon for Entity Material files (entityMaterials.bin) - isSelectingTab = true; - tabControl.SelectTab(0); - isSelectingTab = false; + isSelectingTab = true; + tabControl.SelectTab(0); + isSelectingTab = false; - UpdateRichPresence(); + UpdateRichPresence(); } - private void MainForm_FormClosing(object sender, FormClosingEventArgs e) + private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { PckManager?.Close(); checkSaveState(); @@ -222,7 +223,7 @@ namespace PckStudio isSelectingTab = true; tabControl.SelectTab(1); isSelectingTab = false; - UpdateRichPresence(); + UpdateRichPresence(); } private void CloseEditorTab() @@ -243,35 +244,35 @@ namespace PckStudio saveToolStripMenuItem1.Enabled = false; quickChangeToolStripMenuItem.Enabled = false; closeToolStripMenuItem.Visible = false; - packSettingsToolStripMenuItem.Visible = false; - convertToBedrockToolStripMenuItem.Enabled = false; + packSettingsToolStripMenuItem.Visible = false; + convertToBedrockToolStripMenuItem.Enabled = false; addCustomPackImageToolStripMenuItem.Enabled = false; fileEntryCountLabel.Text = string.Empty; pckFileLabel.Text = string.Empty; UpdateRichPresence(); } - private void UpdateRichPresence() - { - if (currentPCK is not null && - TryGetLocFile(out LOCFile locfile) && - locfile.HasLocEntry("IDS_DISPLAY_NAME") && - locfile.Languages.Contains("en-EN")) - { - RPC.SetPresence($"Editing a Pack: {locfile.GetLocEntry("IDS_DISPLAY_NAME", "en-EN")}"); - return; - } - // default - RPC.SetPresence("An Open Source .PCK File Editor"); - } + private void UpdateRichPresence() + { + if (currentPCK is not null && + TryGetLocFile(out LOCFile locfile) && + locfile.HasLocEntry("IDS_DISPLAY_NAME") && + locfile.Languages.Contains("en-EN")) + { + RPC.SetPresence($"Editing a Pack: {locfile.GetLocEntry("IDS_DISPLAY_NAME", "en-EN")}"); + return; + } + // default + RPC.SetPresence("An Open Source .PCK File Editor"); + } - /// - /// wrapper that allows the use of in TreeNode.Nodes.Find(, ...) and TreeNode.Nodes.ContainsKey() - /// - /// - /// - /// new Created TreeNode - public static TreeNode CreateNode(string name, object tag = null) + /// + /// wrapper that allows the use of in TreeNode.Nodes.Find(, ...) and TreeNode.Nodes.ContainsKey() + /// + /// + /// + /// new Created TreeNode + public static TreeNode CreateNode(string name, object tag = null) { TreeNode node = new TreeNode(name); node.Name = name; @@ -307,25 +308,25 @@ namespace PckStudio node.Tag = file; if (Settings.Default.LoadSubPcks && (file.Filetype == PckFileType.SkinDataFile || file.Filetype == PckFileType.TexturePackInfoFile) && - file.Size > 0) + file.Size > 0) { - using (var stream = new MemoryStream(file.Data)) + using (var stream = new MemoryStream(file.Data)) + { + try { - try - { - var reader = new PckFileReader(LittleEndianCheckBox.Checked ? OMI.Endianness.LittleEndian : OMI.Endianness.BigEndian); - PckFile subPCKfile = reader.FromStream(stream); - // passes parent path to remove from sub pck filepaths - BuildPckTreeView(node.Nodes, subPCKfile, file.Filename + "/"); - } - catch (OverflowException ex) - { - MessageBox.Show("Failed to open pck\n" + - "Try checking the 'Open/Save as Switch/Vita/PS4 pck' checkbox in the upper right corner.", - "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - Debug.WriteLine(ex.Message); - } + var reader = new PckFileReader(LittleEndianCheckBox.Checked ? OMI.Endianness.LittleEndian : OMI.Endianness.BigEndian); + PckFile subPCKfile = reader.FromStream(stream); + // passes parent path to remove from sub pck filepaths + BuildPckTreeView(node.Nodes, subPCKfile, file.Filename + "/"); } + catch (OverflowException ex) + { + MessageBox.Show("Failed to open pck\n" + + "Try checking the 'Open/Save as Switch/Vita/PS4 pck' checkbox in the upper right corner.", + "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + Debug.WriteLine(ex.Message); + } + } } SetNodeIcon(node, file.Filetype); }; @@ -352,10 +353,10 @@ namespace PckStudio treeViewMain.Sort(); TreeNode[] selectedNodes; - if (!string.IsNullOrEmpty(selectedNodeText) && + if (!string.IsNullOrEmpty(selectedNodeText) && (selectedNodes = treeViewMain.Nodes.Find(selectedNodeText, true)).Length > 0) { - treeViewMain.SelectedNode = selectedNodes[0]; + treeViewMain.SelectedNode = selectedNodes[0]; } } @@ -373,8 +374,8 @@ namespace PckStudio { var img = file.GetTexture(); var res = img.Width / 16; // texture count on X axes - var size = new Size(res, res); - var viewer = new TextureAtlasEditor(currentPCK, file.Filename, img, size); + var size = new Size(res, res); + var viewer = new TextureAtlasEditor(currentPCK, file.Filename, img, size); if (viewer.ShowDialog() == DialogResult.OK) { file.SetData(viewer.FinalTexture, ImageFormat.Png); @@ -437,17 +438,17 @@ namespace PckStudio } return; } - + var img = file.GetTexture(); using var skinViewer = new SkinPreview(img, file.Properties.GetPropertyValue("ANIM", SkinANIM.FromString)); - skinViewer.ShowDialog(this); - } + skinViewer.ShowDialog(this); + } public void HandleModelsFile(PckFileData file) { MessageBox.Show("Models.bin support has not been implemented. You can use the Spark Editor for the time being to edit these files.", "Not implemented yet."); } - + public void HandleBehavioursFile(PckFileData file) { using BehaviourEditor edit = new BehaviourEditor(file); @@ -459,7 +460,7 @@ namespace PckStudio using MaterialsEditor edit = new MaterialsEditor(file); wasModified = edit.ShowDialog(this) == DialogResult.OK; } - + private void selectNode(object sender, TreeViewEventArgs e) { ReloadMetaTreeView(); @@ -499,7 +500,7 @@ namespace PckStudio img = new Bitmap(img); } - try + try { previewPictureBox.Image = img; labelImageSize.Text = $"{previewPictureBox.Image.Size.Width}x{previewPictureBox.Image.Size.Height}"; @@ -567,7 +568,7 @@ namespace PckStudio File.WriteAllBytes(extractFilePath, file.Data); if (file.Properties.Count > 0) { - using var fs = File.CreateText($"{extractFilePath}.txt"); + using var fs = File.CreateText($"{extractFilePath}.txt"); file.Properties.ForEach(property => fs.WriteLine($"{property.Key}: {property.Value}")); } // Verification that file extraction path was successful @@ -682,29 +683,29 @@ namespace PckStudio /// True if the remove should be canceled, otherwise False private bool BeforeFileRemove(PckFileData file) { - string itemPath = "res/textures/items/"; + string itemPath = "res/textures/items/"; - // warn the user about deleting compass.png and clock.png - if (file.Filetype == PckFileType.TextureFile && - (file.Filename == itemPath + "compass.png" || file.Filename == itemPath + "clock.png")) - { - if (MessageBox.Show("Are you sure want to delete this file? If \"compass.png\" or \"clock.png\" are missing, your game will crash upon loading this pack.", "Warning", + // warn the user about deleting compass.png and clock.png + if (file.Filetype == PckFileType.TextureFile && + (file.Filename == itemPath + "compass.png" || file.Filename == itemPath + "clock.png")) + { + if (MessageBox.Show("Are you sure want to delete this file? If \"compass.png\" or \"clock.png\" are missing, your game will crash upon loading this pack.", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No) return true; - } + } - // remove loc key if its a skin/cape - if (file.Filetype == PckFileType.SkinFile || file.Filetype == PckFileType.CapeFile) - { - if (TryGetLocFile(out LOCFile locFile)) - { + // remove loc key if its a skin/cape + if (file.Filetype == PckFileType.SkinFile || file.Filetype == PckFileType.CapeFile) + { + if (TryGetLocFile(out LOCFile locFile)) + { locFile.RemoveLocKey(file.Properties.GetPropertyValue("THEMENAMEID")); locFile.RemoveLocKey(file.Properties.GetPropertyValue("DISPLAYNAMEID")); - TrySetLocFile(locFile); - } - } + TrySetLocFile(locFile); + } + } return false; - } + } private void deleteFileToolStripMenuItem_Click(object sender, EventArgs e) { @@ -716,7 +717,7 @@ namespace PckStudio if (node.TryGetTagData(out PckFileData file)) { - if (!BeforeFileRemove(file) && currentPCK.RemoveFile(file)) + if (!BeforeFileRemove(file) && currentPCK.RemoveFile(file)) { node.Remove(); wasModified = true; @@ -824,7 +825,7 @@ namespace PckStudio audioPck.AddCategory(PckAudioFile.AudioCategory.EAudioType.Nether); audioPck.AddCategory(PckAudioFile.AudioCategory.EAudioType.End); PckFileData pckFileData = new PckFileData("audio.pck", PckFileType.AudioFile); - pckFileData.SetData(new PckAudioFileWriter(audioPck, isLittle ? OMI.Endianness.LittleEndian : OMI.Endianness.BigEndian)); + pckFileData.SetData(new PckAudioFileWriter(audioPck, isLittle ? OMI.Endianness.LittleEndian : OMI.Endianness.BigEndian)); return pckFileData; } @@ -844,7 +845,7 @@ namespace PckStudio var file = CreateNewAudioFile(LittleEndianCheckBox.Checked); AudioEditor diag = new AudioEditor(file, LittleEndianCheckBox.Checked); - if(diag.ShowDialog(this) == DialogResult.OK) + if (diag.ShowDialog(this) == DialogResult.OK) { currentPCK.AddFile(file); } @@ -875,7 +876,7 @@ namespace PckStudio { wasModified = true; AnimationHelper.SaveAnimationToFile(file, animation); - currentPCK.AddFile(file); + currentPCK.AddFile(file); BuildMainTreeView(); ReloadMetaTreeView(); } @@ -898,7 +899,7 @@ namespace PckStudio List GetAllChildNodes(TreeNodeCollection root) { List childNodes = new List(); - foreach(TreeNode node in root) + foreach (TreeNode node in root) { childNodes.Add(node); if (node.Nodes.Count > 0) @@ -914,7 +915,7 @@ namespace PckStudio string parentPath = childPath.Replace('\\', '/'); Debug.WriteLine(parentPath); string[] s = parentPath.Split('/'); - Debug.WriteLine(s.Length); + Debug.WriteLine(s.Length); foreach (var node in s) { TreeNode parent = treeViewMain.Nodes.Find(node, true)[0]; @@ -931,16 +932,16 @@ namespace PckStudio { // Support for if a file is edited within a nested PCK File (AKA SubPCK) - if(!IsSubPCKNode(childPath)) return; + if (!IsSubPCKNode(childPath)) return; TreeNode parent = GetSubPCK(childPath); - Debug.WriteLine(parent.Name); + Debug.WriteLine(parent.Name); if (parent == null) return; PckFileData parent_file = parent.Tag as PckFileData; if (parent_file.Filetype is PckFileType.TexturePackInfoFile || parent_file.Filetype is PckFileType.SkinDataFile) { - Debug.WriteLine("Rebuilding " + parent_file.Filename); + Debug.WriteLine("Rebuilding " + parent_file.Filename); PckFile newPCKFile = new PckFile(3, parent_file.Filetype is PckFileType.SkinDataFile); foreach (TreeNode node in GetAllChildNodes(parent.Nodes)) @@ -964,12 +965,12 @@ namespace PckStudio { if (treeViewMain.SelectedNode.TryGetTagData(out PckFileData file)) { - if (file.Size <= 0) - { - Trace.WriteLine($"'{file.Filename}' has no data attached.", category: nameof(treeViewMain_DoubleClick)); - return; - } - pckFileTypeHandler[file.Filetype]?.Invoke(file); + if (file.Size <= 0) + { + Trace.WriteLine($"'{file.Filename}' has no data attached.", category: nameof(treeViewMain_DoubleClick)); + return; + } + pckFileTypeHandler[file.Filetype]?.Invoke(file); } } @@ -1080,9 +1081,9 @@ namespace PckStudio if (GetAllChildNodes(treeViewMain.Nodes).Find(n => n.FullPath == diag.NewText) != null) { MessageBox.Show( - this, + this, $"A file with the path \"{diag.NewText}\" already exists. " + - $"Please try again with a different name.", + $"Please try again with a different name.", "Key already exists"); return; } @@ -1119,7 +1120,7 @@ namespace PckStudio foreach (var property in file.Properties) { treeMeta.Nodes.Add(CreateNode(property.Key, property)); - } + } } } @@ -1173,7 +1174,7 @@ namespace PckStudio private PckFile InitializePack(int packId, int packVersion, string packName, bool createSkinsPCK) { var pack = new PckFile(3); - + var zeroFile = pack.CreateNewFile("0", PckFileType.InfoFile); zeroFile.Properties.Add("PACKID", packId.ToString()); zeroFile.Properties.Add("PACKVERSION", packVersion.ToString()); @@ -1186,7 +1187,7 @@ namespace PckStudio LittleEndianCheckBox.Checked ? OMI.Endianness.LittleEndian : OMI.Endianness.BigEndian)); - + return pack; } @@ -1231,9 +1232,9 @@ namespace PckStudio new KeyValuePair("spawnY", "0"), new KeyValuePair("spawnZ", "0") ); - + gameRuleFile.SetData(new GameRuleFileWriter(grfFile)); - + return pack; } @@ -1357,7 +1358,7 @@ namespace PckStudio FileInfo fileinfo = new FileInfo(filepath); fileinfo.Directory.Create(); File.WriteAllBytes(filepath, file.Data); // writes data to file - //attempts to generate reimportable metadata file out of minefiles metadata + //attempts to generate reimportable metadata file out of minefiles metadata string metaData = ""; foreach (var entry in file.Properties) @@ -1433,7 +1434,7 @@ namespace PckStudio if (File.Exists(fullfilename + ".txt")) { - string[] properties = File.ReadAllText(fullfilename + ".txt").Split(new string[]{ Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + string[] properties = File.ReadAllText(fullfilename + ".txt").Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); foreach (string property in properties) { string[] param = property.Split(':'); @@ -1588,7 +1589,7 @@ namespace PckStudio private void folderToolStripMenuItem_Click(object sender, EventArgs e) { TextPrompt folderNamePrompt = new TextPrompt(); - if(treeViewMain.SelectedNode is not null) folderNamePrompt.contextLabel.Text = $"New folder at the location of \"{treeViewMain.SelectedNode.FullPath}\""; + if (treeViewMain.SelectedNode is not null) folderNamePrompt.contextLabel.Text = $"New folder at the location of \"{treeViewMain.SelectedNode.FullPath}\""; folderNamePrompt.OKButtonText = "Add"; if (folderNamePrompt.ShowDialog() == DialogResult.OK) { @@ -1619,9 +1620,9 @@ namespace PckStudio MessageBox.Show("This feature is currently being reworked.", "Currently unavailable", MessageBoxButtons.OK, MessageBoxIcon.Information); } - private void openPckCenterToolStripMenuItem_Click(object sender, EventArgs e) + private void openPckCenterToolStripMenuItem_Click(object sender, EventArgs e) { - MessageBox.Show("This feature is currently being reworked.", "Currently unavailable", MessageBoxButtons.OK, MessageBoxIcon.Information); + MessageBox.Show("This feature is currently being reworked.", "Currently unavailable", MessageBoxButtons.OK, MessageBoxIcon.Information); #if false DateTime Begin = DateTime.Now; //pckCenter open = new pckCenter(); @@ -1703,14 +1704,14 @@ namespace PckStudio if (currentPCK is not null && wasModified && MessageBox.Show("Save PCK?", "Unsaved PCK", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) + { + if (isTemplateFile || string.IsNullOrEmpty(saveLocation)) { - if (isTemplateFile || string.IsNullOrEmpty(saveLocation)) - { - SaveTemplate(); - return; - } - Save(saveLocation); + SaveTemplate(); + return; } + Save(saveLocation); + } } private void OpenPck_DragEnter(object sender, DragEventArgs e) @@ -1892,15 +1893,15 @@ namespace PckStudio { string mippedPath = $"{textureDirectory}/{textureName}MipMapLevel{i}{textureExtension}"; Debug.WriteLine(mippedPath); - if (currentPCK.HasFile(mippedPath, PckFileType.TextureFile)) + if (currentPCK.HasFile(mippedPath, PckFileType.TextureFile)) currentPCK.RemoveFile(currentPCK.GetFile(mippedPath, PckFileType.TextureFile)); PckFileData MipMappedFile = new PckFileData(mippedPath, PckFileType.TextureFile); Image originalTexture = Image.FromStream(new MemoryStream(file.Data)); - int NewWidth = Math.Max(originalTexture.Width / (int)Math.Pow(2,i - 1), 1); + int NewWidth = Math.Max(originalTexture.Width / (int)Math.Pow(2, i - 1), 1); int NewHeight = Math.Max(originalTexture.Height / (int)Math.Pow(2, i - 1), 1); - + Rectangle tileArea = new Rectangle(0, 0, NewWidth, NewHeight); Image mippedTexture = new Bitmap(NewWidth, NewHeight); using (Graphics gfx = Graphics.FromImage(mippedTexture)) @@ -1910,7 +1911,7 @@ namespace PckStudio gfx.PixelOffsetMode = PixelOffsetMode.HighQuality; gfx.DrawImage(originalTexture, tileArea); } - + MipMappedFile.SetData(mippedTexture, ImageFormat.Png); currentPCK.InsertFile(currentPCK.IndexOfFile(file) + i - 1, MipMappedFile); @@ -1985,9 +1986,9 @@ namespace PckStudio if (treeViewMain.SelectedNode.TryGetTagData(out PckFileData file) && file.Filetype == PckFileType.SkinFile) { - foreach(var p in file.Properties.FindAll(s => s.Key == "BOX" || s.Key == "OFFSET")) + foreach (var p in file.Properties.FindAll(s => s.Key == "BOX" || s.Key == "OFFSET")) { - file.Properties[file.Properties.IndexOf(p)] = new KeyValuePair(p.Key, p.Value.Replace(',','.')); + file.Properties[file.Properties.IndexOf(p)] = new KeyValuePair(p.Key, p.Value.Replace(',', '.')); } ReloadMetaTreeView(); RebuildSubPCK(treeViewMain.SelectedNode.FullPath); @@ -2007,9 +2008,9 @@ namespace PckStudio if (currentPCK is not null) { - DialogResult prompt = MessageBox.Show(this, - "Would you like to use the current PackID? You can enter any PackID if not.", - "", + DialogResult prompt = MessageBox.Show(this, + "Would you like to use the current PackID? You can enter any PackID if not.", + "", MessageBoxButtons.YesNoCancel); switch (prompt) @@ -2018,9 +2019,9 @@ namespace PckStudio if (!currentPCK.TryGetFile("0", PckFileType.InfoFile, out PckFileData file) || string.IsNullOrEmpty(file.Properties.GetPropertyValue("PACKID"))) { - MessageBox.Show(this, + MessageBox.Show(this, "No PackID is present in this PCK. " + - "To avoid this error, ensure that the PCK has a proper PackID property on the \"0\" Info file before trying again.", + "To avoid this error, ensure that the PCK has a proper PackID property on the \"0\" Info file before trying again.", "Operation Aborted", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } @@ -2191,21 +2192,21 @@ namespace PckStudio if (!PckManager.Visible) { PckManager.Show(); - PckManager.BringToFront(); - } + PckManager.BringToFront(); + } if (PckManager.Focus()) PckManager.BringToFront(); } private void wavBinkaToolStripMenuItem_Click(object sender, EventArgs e) { - using OpenFileDialog fileDialog = new OpenFileDialog - { - Multiselect = true, - Filter = "WAV files (*.wav)|*.wav", - Title = "Please choose WAV files to convert to BINKA" - }; - if (fileDialog.ShowDialog() == DialogResult.OK) + using OpenFileDialog fileDialog = new OpenFileDialog + { + Multiselect = true, + Filter = "WAV files (*.wav)|*.wav", + Title = "Please choose WAV files to convert to BINKA" + }; + if (fileDialog.ShowDialog() == DialogResult.OK) { BinkaConverter.ToBinka(fileDialog.FileNames, new DirectoryInfo(Path.GetDirectoryName(fileDialog.FileName))); } @@ -2213,32 +2214,32 @@ namespace PckStudio private void binkaWavToolStripMenuItem_Click(object sender, EventArgs e) { - using OpenFileDialog fileDialog = new OpenFileDialog - { - Multiselect = true, - Filter = "BINKA files (*.binka)|*.binka", - Title = "Please choose BINKA files to convert to WAV" - }; - if (fileDialog.ShowDialog() == DialogResult.OK) + using OpenFileDialog fileDialog = new OpenFileDialog + { + Multiselect = true, + Filter = "BINKA files (*.binka)|*.binka", + Title = "Please choose BINKA files to convert to WAV" + }; + if (fileDialog.ShowDialog() == DialogResult.OK) { BinkaConverter.ToWav(fileDialog.FileNames, new DirectoryInfo(Path.GetDirectoryName(fileDialog.FileName))); } } - private void fullBoxSupportToolStripMenuItem_CheckedChanged(object sender, EventArgs e) - { + private void fullBoxSupportToolStripMenuItem_CheckedChanged(object sender, EventArgs e) + { currentPCK.SetVersion(fullBoxSupportToolStripMenuItem.Checked); - } + } - private void settingsToolStripMenuItem_Click(object sender, EventArgs e) - { + private void settingsToolStripMenuItem_Click(object sender, EventArgs e) + { var appSettings = new AppSettingsForm(); appSettings.ShowDialog(this); - } + } private void addBOXEntryToolStripMenuItem1_Click(object sender, EventArgs e) { - if(treeViewMain.SelectedNode is TreeNode t && t.Tag is PckFileData file) + if (treeViewMain.SelectedNode is TreeNode t && t.Tag is PckFileData file) { using BoxEditor diag = new BoxEditor(SkinBOX.Empty, IsSubPCKNode(treeViewMain.SelectedNode.FullPath)); if (diag.ShowDialog(this) == DialogResult.OK) @@ -2268,10 +2269,20 @@ namespace PckStudio } } - private void skinRenderer3DToolStripMenuItem_Click(object sender, EventArgs e) - { + private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e) + { + if (Program.Updater.IsUpdateAvailable(Application.ProductVersion)) + { + Program.UpdateToLatest("Would you like to download it?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, DialogResult.Yes); + return; + } + MessageBox.Show("Already up to date.", "No update available"); + } + + private void skinRenderer3DToolStripMenuItem_Click(object sender, EventArgs e) + { var gl = new TestGL(); gl.Show(); - } - } + } + } } \ No newline at end of file diff --git a/PCK-Studio/MainForm.resx b/PCK-Studio/MainForm.resx index 39cd9c44..10818175 100644 --- a/PCK-Studio/MainForm.resx +++ b/PCK-Studio/MainForm.resx @@ -815,6 +815,9 @@ 177, 6 + + 177, 6 + False @@ -1570,6 +1573,12 @@ More + + 180, 22 + + + Check for updates + iVBORw0KGgoAAAANSUhEUgAAAtAAAALQCAYAAAC5V0ecAAAABGdBTUEAALGPC/xhBQAAazFJREFUeF7t @@ -6810,6 +6819,12 @@ System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + checkForUpdatesToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + aboutToolStripMenuItem diff --git a/PCK-Studio/PckStudio.csproj b/PCK-Studio/PckStudio.csproj index 2fd819fc..d5dcc65c 100644 --- a/PCK-Studio/PckStudio.csproj +++ b/PCK-Studio/PckStudio.csproj @@ -4,6 +4,7 @@ preview true + NDEBUG publish\ true Disk @@ -40,7 +41,7 @@ full false bin\Debug\ - DEBUG;TRACE + $(DefineConstants);DEBUG;TRACE prompt 4 true @@ -50,7 +51,7 @@ none true bin\Release\ - TRACE + $(DefineConstants);TRACE prompt 4 true @@ -60,7 +61,7 @@ pdbonly false bin\Beta\ - BETA;TRACE + $(DefineConstants);BETA;TRACE prompt 4 true @@ -228,9 +229,7 @@ - - UserControl @@ -571,6 +570,7 @@ generateModel.cs + Designer SkinPreview.cs @@ -734,6 +734,10 @@ + + {5B223556-15B9-41DA-AA0B-5E7F45E743BF} + PCK-Studio-Updater + {693AEBC1-293D-4DF0-BCAE-26A1099FE7BB} OMI Filetype Library diff --git a/PCK-Studio/Program.cs b/PCK-Studio/Program.cs index 644b6c70..38fe6f56 100644 --- a/PCK-Studio/Program.cs +++ b/PCK-Studio/Program.cs @@ -1,21 +1,36 @@ using System; using System.Diagnostics; using System.IO; +using System.Text.RegularExpressions; using System.Windows.Forms; using PckStudio.Classes.Misc; using PckStudio.Internal; +using PckStudio.Properties; +using PCKStudio_Updater; + namespace PckStudio { static class Program { - public static readonly string ProjectUrl = "https://github.com/PhoenixARC/-PCK-Studio"; - public static readonly string BaseAPIUrl = "http://api.pckstudio.xyz/api/pck"; - public static readonly string BackUpAPIUrl = "https://raw.githubusercontent.com/PhoenixARC/pckstudio.tk/main/studio/PCK/api/"; - public static readonly string AppData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Application.ProductName); - public static readonly string AppDataCache = Path.Combine(AppData, "cache"); + internal static readonly Uri ProjectUrl = new Uri("https://github.com/PhoenixARC/-PCK-Studio"); + internal static readonly string BaseAPIUrl = "http://api.pckstudio.xyz/api/pck"; + internal static readonly string BackUpAPIUrl = "https://raw.githubusercontent.com/PhoenixARC/pckstudio.tk/main/studio/PCK/api/"; - public static MainForm MainInstance { get; private set; } + internal static readonly string AppData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Application.ProductName); + internal static readonly string AppDataCache = Path.Combine(AppData, "cache"); + + private static readonly GithubParams UpdateParams = new GithubParams( + Path.GetDirectoryName(ProjectUrl.AbsolutePath).Replace("\\", ""), + Path.GetFileName(ProjectUrl.AbsolutePath), + Application.ProductName, + Settings.Default.UsePrerelease, + new Regex("(\\*|\\d+(\\.\\d+){0,3}(\\.\\*)?)") + ); + internal static readonly IUpdateDownloader Updater = new GithubUpdateDownloader(UpdateParams); + + + internal static MainForm MainInstance { get; private set; } /// /// The main entry point for the application. @@ -23,6 +38,11 @@ namespace PckStudio [STAThread] static void Main(string[] args) { + if (Settings.Default.AutoUpdate) + { + UpdateToLatest("Click Ok to continue.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, DialogResult.OK); + } + ApplicationScope.Initialize(); Trace.TraceInformation("Startup"); RPC.Initialize(); @@ -32,5 +52,21 @@ namespace PckStudio Application.ApplicationExit += (sender, e) => { RPC.Deinitialize(); }; Application.Run(MainInstance); } + + [Conditional("NDEBUG")] + internal static void UpdateToLatest(string message, MessageBoxButtons buttons, MessageBoxIcon icon, DialogResult dialogResult) + { + bool updateAvailable = Updater.IsUpdateAvailable(Application.ProductVersion); + if (updateAvailable && MessageBox.Show( + "New update available.\n" + + message, + "Update Available", + buttons, icon, MessageBoxDefaultButton.Button1) == dialogResult) + { + Updater.DownloadTo(new DirectoryInfo(Application.StartupPath)); + Updater.Launch(); + Application.Exit(); + } + } } -} +} \ No newline at end of file diff --git a/PCK-Studio/Properties/AssemblyInfo.cs b/PCK-Studio/Properties/AssemblyInfo.cs index 27386512..9e7ee662 100644 --- a/PCK-Studio/Properties/AssemblyInfo.cs +++ b/PCK-Studio/Properties/AssemblyInfo.cs @@ -32,6 +32,6 @@ using System.Security.Permissions; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("7.0")] -[assembly: AssemblyFileVersion("7.0")] +[assembly: AssemblyVersion("7.0.0.0")] +[assembly: AssemblyFileVersion("7.0.0.0")] [assembly: NeutralResourcesLanguage("")] diff --git a/PCK-Studio/Properties/Settings.Designer.cs b/PCK-Studio/Properties/Settings.Designer.cs index e6d87129..95fa109d 100644 --- a/PCK-Studio/Properties/Settings.Designer.cs +++ b/PCK-Studio/Properties/Settings.Designer.cs @@ -80,6 +80,30 @@ namespace PckStudio.Properties { } } + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool UsePrerelease { + get { + return ((bool)(this["UsePrerelease"])); + } + set { + this["UsePrerelease"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool AutoUpdate { + get { + return ((bool)(this["AutoUpdate"])); + } + set { + this["AutoUpdate"] = value; + } + } + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] diff --git a/PCK-Studio/Properties/Settings.settings b/PCK-Studio/Properties/Settings.settings index 8724beec..2e32328f 100644 --- a/PCK-Studio/Properties/Settings.settings +++ b/PCK-Studio/Properties/Settings.settings @@ -17,6 +17,12 @@ True + + True + + + False + False diff --git a/PCK_Studio.sln b/PCK_Studio.sln index 8062268c..772bd2c5 100644 --- a/PCK_Studio.sln +++ b/PCK_Studio.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OMI Filetype Library", "Ven EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpMss32", "Vendor\SharpMss32\SharpMss32\SharpMss32.csproj", "{E8D0B671-3AB1-48B6-A767-58DF67BD5D11}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCK-Studio-Updater", "PCK-Studio-Updater\PCK-Studio-Updater.csproj", "{5B223556-15B9-41DA-AA0B-5E7F45E743BF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Beta|Any CPU = Beta|Any CPU @@ -78,6 +80,24 @@ Global {E8D0B671-3AB1-48B6-A767-58DF67BD5D11}.Release|x64.Build.0 = Release|Any CPU {E8D0B671-3AB1-48B6-A767-58DF67BD5D11}.Release|x86.ActiveCfg = Release|Any CPU {E8D0B671-3AB1-48B6-A767-58DF67BD5D11}.Release|x86.Build.0 = Release|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Beta|Any CPU.ActiveCfg = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Beta|Any CPU.Build.0 = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Beta|x64.ActiveCfg = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Beta|x64.Build.0 = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Beta|x86.ActiveCfg = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Beta|x86.Build.0 = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Debug|x64.ActiveCfg = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Debug|x64.Build.0 = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Debug|x86.ActiveCfg = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Debug|x86.Build.0 = Debug|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Release|Any CPU.Build.0 = Release|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Release|x64.ActiveCfg = Release|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Release|x64.Build.0 = Release|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Release|x86.ActiveCfg = Release|Any CPU + {5B223556-15B9-41DA-AA0B-5E7F45E743BF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE