From a76c7df84d82c0b8a058fe123ee434e1df36bfe6 Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Thu, 19 Feb 2026 16:49:38 +0100 Subject: [PATCH 01/14] v1.0.0-beta2 - Bug in ZXBSInstall installation bash --- ZXBSInstaller.Log/ServiceLayer.cs | 32 +++++++++++++++++++++++------- ZXBSInstaller/ZXBSInstaller.csproj | 2 +- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/ZXBSInstaller.Log/ServiceLayer.cs b/ZXBSInstaller.Log/ServiceLayer.cs index 174a25a..a1047b2 100644 --- a/ZXBSInstaller.Log/ServiceLayer.cs +++ b/ZXBSInstaller.Log/ServiceLayer.cs @@ -1073,17 +1073,35 @@ echo on bashFile = Path.Combine(GeneralConfig.BasePath, "downloads", "zxbsinstall.sh"); bash = @" #!/bin/bash +set -e echo ""Updating installer..."" sleep 5 -set -x -tar -xf ""{tempFile}"" -C ""{installationPath}"" -rm -f ""{tempFile}"" -cd ""{installationPath}"" || exit 1 - -# Ejecutar sin esperar (en segundo plano) -./ZXBSInstaller.exe &"; +ZIP_FILE=""{tempFile}"" +DEST_DIR=""{installationPath}"" + +extract_zip() { + if command -v unzip >/dev/null 2>&1; then + echo ""Using unzip..."" + unzip -o ""$ZIP_FILE"" -d ""$DEST_DIR"" + elif command -v tar >/dev/null 2>&1; then + echo ""Using tar..."" + tar -xf ""$ZIP_FILE"" -C ""$DEST_DIR"" + else + echo ""Error: Neither unzip nor tar is installed."" + exit 1 + fi +} + +extract_zip + +rm -f ""$ZIP_FILE"" +cd ""$DEST_DIR"" || exit 1 + +# Ejecutar sin esperar +./ZXBSInstaller & +"; } bash = bash.Replace("{tempFile}", tempFile).Replace("{installationPath}", installationPath); File.WriteAllText(bashFile, bash); diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index 008c57a..e03905f 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,7 +6,7 @@ app.manifest true zxbs.ico - 1.0.0.1 + 1.0.0.2 From 843f9af3a650d00070477e2bcbedc001e15d3ea6 Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Thu, 19 Feb 2026 18:20:06 +0100 Subject: [PATCH 02/14] v1.0.0-beta3 - Bug in lauch .sh file on Linux and Mac --- ZXBSInstaller.Log/ServiceLayer.cs | 42 +++++++++++++++++------- ZXBSInstaller/Controls/MainControl.axaml | 2 +- ZXBSInstaller/ZXBSInstaller.csproj | 2 +- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/ZXBSInstaller.Log/ServiceLayer.cs b/ZXBSInstaller.Log/ServiceLayer.cs index a1047b2..0892604 100644 --- a/ZXBSInstaller.Log/ServiceLayer.cs +++ b/ZXBSInstaller.Log/ServiceLayer.cs @@ -974,6 +974,11 @@ public static void DownloadAndInstallTool(ExternalTool tool, ExternalTools_Versi string step = ""; try { + if(tool==null || version == null) + { + return; + } + ShowStatusPanel($"Downloading {tool.Name} version {version.Version}..."); // Download path @@ -1106,22 +1111,34 @@ exit 1 bash = bash.Replace("{tempFile}", tempFile).Replace("{installationPath}", installationPath); File.WriteAllText(bashFile, bash); - // Set execute attr in Linux/Mac if (CurrentOperatingSystem != OperatingSystems.Windows) { - var process = new Process(); - process.StartInfo.FileName = "chmod"; - process.StartInfo.ArgumentList.Add("+x"); - process.StartInfo.ArgumentList.Add(bashFile); - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - process.StartInfo.UseShellExecute = false; - process.Start(); - process.WaitForExit(); + // Set execute attr in Linux/Mac + { + var process = new Process(); + process.StartInfo.FileName = "chmod"; + process.StartInfo.ArgumentList.Add("+x"); + process.StartInfo.ArgumentList.Add(bashFile); + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.UseShellExecute = false; + process.Start(); + process.WaitForExit(); + } + // Launch .sh file + ProcessStartInfo psi = new ProcessStartInfo + { + FileName = "bash", + Arguments = bashFile, + WorkingDirectory = Path.Combine(GeneralConfig.BasePath, "downloads"), + UseShellExecute = true, + }; + var p = new Process { StartInfo = psi }; + p.Start(); } - - // Run batch/bash file + else { + // Launch .bat file ProcessStartInfo psi = new ProcessStartInfo { FileName = bashFile, @@ -1132,6 +1149,7 @@ exit 1 p.Start(); } + // Exit app ExitApp(); } diff --git a/ZXBSInstaller/Controls/MainControl.axaml b/ZXBSInstaller/Controls/MainControl.axaml index c31b41e..2479b27 100644 --- a/ZXBSInstaller/Controls/MainControl.axaml +++ b/ZXBSInstaller/Controls/MainControl.axaml @@ -33,7 +33,7 @@ - + diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index e03905f..1703385 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,7 +6,7 @@ app.manifest true zxbs.ico - 1.0.0.2 + 1.0.0.3 From a1c4d3dcfc507bca6c278f490719e35cc3e40651 Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Thu, 19 Feb 2026 19:21:04 +0100 Subject: [PATCH 03/14] v1.0.0-beta4 - All buttons are disabled during long processes. - Added Cancel button --- README.md | 1 + ZXBSInstaller.Log/ServiceLayer.cs | 384 ++++++++++++-------- ZXBSInstaller/Assets/cancel.svg | 7 + ZXBSInstaller/Controls/MainControl.axaml | 8 +- ZXBSInstaller/Controls/MainControl.axaml.cs | 24 +- ZXBSInstaller/ZXBSInstaller.csproj | 4 +- 6 files changed, 273 insertions(+), 155 deletions(-) create mode 100644 ZXBSInstaller/Assets/cancel.svg diff --git a/README.md b/README.md index dea0ca1..4aac395 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,4 @@ Have fun! - Zest in MIT License - Neuicons in MIT License via - Dazzle Ui in CC Attribution License + - Siemens in MIT License diff --git a/ZXBSInstaller.Log/ServiceLayer.cs b/ZXBSInstaller.Log/ServiceLayer.cs index 0892604..09f7a09 100644 --- a/ZXBSInstaller.Log/ServiceLayer.cs +++ b/ZXBSInstaller.Log/ServiceLayer.cs @@ -25,6 +25,8 @@ public static class ServiceLayer public static Config GeneralConfig = null; public static ExternalTool[] ExternalTools = null; public static OperatingSystems CurrentOperatingSystem = OperatingSystems.All; + public static bool Cancel = false; + private static Action ShowStatusPanel = null; private static Action UpdateStatus = null; @@ -48,36 +50,44 @@ public static bool Initialize( Action callBackShowMessage, Action callBackExitApp) { - ShowStatusPanel = callBackShowStatusPanel; - UpdateStatus = callBackUpdateStatus; - HideStatusPanel = callBackHideStatusPanel; - RefreshTools = callBackGetExternalTools; - ShowMessage = callBackShowMessage; - ExitApp = callBackExitApp; + try + { + ShowStatusPanel = callBackShowStatusPanel; + UpdateStatus = callBackUpdateStatus; + HideStatusPanel = callBackHideStatusPanel; + RefreshTools = callBackGetExternalTools; + ShowMessage = callBackShowMessage; + ExitApp = callBackExitApp; - GetConfig(); + GetConfig(); - if (OperatingSystem.IsWindows()) - { - CurrentOperatingSystem = OperatingSystems.Windows; - } - else if (OperatingSystem.IsLinux()) - { - CurrentOperatingSystem = OperatingSystems.Linux; - } - else if (OperatingSystem.IsMacOS()) - { - if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + if (OperatingSystem.IsWindows()) { - CurrentOperatingSystem = OperatingSystems.MacOS_arm64; + CurrentOperatingSystem = OperatingSystems.Windows; } - else if (RuntimeInformation.ProcessArchitecture == Architecture.X64) + else if (OperatingSystem.IsLinux()) { - CurrentOperatingSystem = OperatingSystems.MacOS_x64; + CurrentOperatingSystem = OperatingSystems.Linux; + } + else if (OperatingSystem.IsMacOS()) + { + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + CurrentOperatingSystem = OperatingSystems.MacOS_arm64; + } + else if (RuntimeInformation.ProcessArchitecture == Architecture.X64) + { + CurrentOperatingSystem = OperatingSystems.MacOS_x64; + } } - } - return true; + return true; + } + catch (Exception ex) + { + ShowMessage($"Error initializing.\r\n{ex.Message}{ex.StackTrace}"); + return false; + } } @@ -101,10 +111,10 @@ public static Config GetConfig() SaveConfig(GeneralConfig); } return GeneralConfig; - } catch (Exception ex) { + ShowMessage($"Error retrieving configuration.\r\n{ex.Message}{ex.StackTrace}"); return null; } } @@ -112,32 +122,40 @@ public static Config GetConfig() public static Config CreateConfig() { - List toolsPaths = null; - var cfg = ServiceLayer.GeneralConfig; - if (cfg == null) + try { - cfg = new Config() + List toolsPaths = null; + var cfg = ServiceLayer.GeneralConfig; + if (cfg == null) { - BasePath = Directory.GetParent(Directory.GetCurrentDirectory()).FullName, - OnlyStableVersions = true, - SetZXBSConfig = true, - ToolsListURL = "https://zx.duefectucorp.com/zxbsinstaller.json" - }; - } + cfg = new Config() + { + BasePath = Directory.GetParent(Directory.GetCurrentDirectory()).FullName, + OnlyStableVersions = true, + SetZXBSConfig = true, + ToolsListURL = "https://zx.duefectucorp.com/zxbsinstaller.json" + }; + } - if (cfg.ExternalTools_Paths == null) - { - cfg.ExternalTools_Paths = new List(); + if (cfg.ExternalTools_Paths == null) + { + cfg.ExternalTools_Paths = new List(); + } + if (cfg.ExternalTools_Paths.Count == 0) + { + cfg.ExternalTools_Paths.Add(new ExternalTools_Path() + { + Id = "zxbsinstaller", + LocalPath = Directory.GetCurrentDirectory() + }); + } + return cfg; } - if (cfg.ExternalTools_Paths.Count == 0) + catch (Exception ex) { - cfg.ExternalTools_Paths.Add(new ExternalTools_Path() - { - Id = "zxbsinstaller", - LocalPath = Directory.GetCurrentDirectory() - }); + ShowMessage($"Error creating default configuration.\r\n{ex.Message}{ex.StackTrace}"); + return null; } - return cfg; } @@ -159,8 +177,10 @@ public static Config SaveConfig(Config config) } catch (Exception ex) { + ShowMessage($"Error saving configuration.\r\n{ex.Message}{ex.StackTrace}"); return null; } + } @@ -211,6 +231,7 @@ public static void OpenUrlInBrowser(string url) } catch (Exception ex) { + ShowMessage($"Error opening {url}.\r\n{ex.Message}{ex.StackTrace}"); } } @@ -239,6 +260,10 @@ public static ExternalTool[] GetExternalTools() int prg = 10; for (int n = 0; n < max; n++) { + if(Cancel) + { + return null; + } var tool = tools[n]; prg = (n * 90) / max; UpdateStatus?.Invoke($"Retrieving versions for {tool.Name}...", prg + 10); @@ -300,6 +325,7 @@ public static ExternalTool[] GetExternalTools() } catch (Exception ex) { + ShowMessage($"Error retrieving external tools information. Please check your internet connection and try again.\r\n{ex.Message}{ex.StackTrace}"); return null; } #if GENERATE_JSON @@ -446,101 +472,123 @@ public static ExternalTool[] GetExternalTools() public static void GetPaths(ref ExternalTool[] tools) { - var filePath = Path.Combine(Environment.GetFolderPath(SpecialFolder.ApplicationData), "ZXBasicStudio", "ZXBasicStudioOptions.json"); - if (!File.Exists(filePath)) + try { - return; - } - var jsonString = File.ReadAllText(filePath); - using JsonDocument doc = JsonDocument.Parse(jsonString); - JsonElement root = doc.RootElement; + var filePath = Path.Combine(Environment.GetFolderPath(SpecialFolder.ApplicationData), "ZXBasicStudio", "ZXBasicStudioOptions.json"); + if (!File.Exists(filePath)) + { + return; + } + var jsonString = File.ReadAllText(filePath); + using JsonDocument doc = JsonDocument.Parse(jsonString); + JsonElement root = doc.RootElement; - UpdatePath("zxbasic", "ZxbcPath", root, ref tools); - // ZX Basic Studio - { - var tool = tools.FirstOrDefault(t => t.Id == "zxbs"); - if (tool != null) + UpdatePath("zxbasic", "ZxbcPath", root, ref tools); + // ZX Basic Studio + { + var tool = tools.FirstOrDefault(t => t.Id == "zxbs"); + if (tool != null) + { + tool.LocalPath = Directory.GetCurrentDirectory(); + } + } + // ZX Basic Studio Installer { - tool.LocalPath = Directory.GetCurrentDirectory(); + var tool = tools.FirstOrDefault(t => t.Id == "zxbsinstaller"); + if (tool != null) + { + tool.LocalPath = Directory.GetCurrentDirectory(); + } } } - // ZX Basic Studio Installer + catch (Exception ex) { - var tool = tools.FirstOrDefault(t => t.Id == "zxbsinstaller"); - if (tool != null) - { - tool.LocalPath = Directory.GetCurrentDirectory(); - } + ShowMessage($"Error getting paths.\r\n{ex.Message}{ex.StackTrace}"); } } private static void UpdatePath(string toolId, string property, JsonElement root, ref ExternalTool[] tools) { - var tool = tools.FirstOrDefault(t => t.Id == toolId); - if (tool == null) + try { - return; + var tool = tools.FirstOrDefault(t => t.Id == toolId); + if (tool == null) + { + return; + } + if (root.TryGetProperty(property, out JsonElement element)) + { + string value = element.GetString(); + tool.FullLocalPath = value; + var fn = Path.GetFileName(value); + value = value.Replace(fn, ""); + tool.LocalPath = value; + } } - if (root.TryGetProperty(property, out JsonElement element)) + catch (Exception ex) { - string value = element.GetString(); - tool.FullLocalPath = value; - var fn = Path.GetFileName(value); - value = value.Replace(fn, ""); - tool.LocalPath = value; + ShowMessage($"Error updating path.\r\n{ex.Message}{ex.StackTrace}"); } } private static (int, int) GetVersionNumber(string versionString) { - int number = 0; - int betaNumber = 0; - string version = versionString; - if (version.Contains("-beta")) + try { - var mv = Regex.Match(version, @"beta(\d+)(?:[-\.]|$)", RegexOptions.IgnoreCase); - if (mv.Success) + int number = 0; + int betaNumber = 0; + string version = versionString; + if (version.Contains("-beta")) + { + var mv = Regex.Match(version, @"beta(\d+)(?:[-\.]|$)", RegexOptions.IgnoreCase); + if (mv.Success) + { + version = version.Replace("-beta", "."); + } + } + else { - version = version.Replace("-beta", "."); + version += ".0"; } - } - else - { - version += ".0"; - } - var versionParts = version.Split("."); - if (versionParts.Length == 5) - { - versionParts[3] += versionParts[4]; - } - for (int n = 0; n < 4; n++) - { - number *= 1000; - if (n < versionParts.Length) + var versionParts = version.Split("."); + if (versionParts.Length == 5) { - int v = ToInteger(versionParts[n]); - if (n == 3) + versionParts[3] += versionParts[4]; + } + for (int n = 0; n < 4; n++) + { + number *= 1000; + if (n < versionParts.Length) { - betaNumber = v; - if (betaNumber == 0) + int v = ToInteger(versionParts[n]); + if (n == 3) { - number += 999; + betaNumber = v; + if (betaNumber == 0) + { + number += 999; + } + else + { + number += betaNumber; + } } else { - number += betaNumber; + number += v; } } - else - { - number += v; - } } + return (number, betaNumber); + } + catch (Exception ex) + { + ShowMessage($"Error parsing version number.\r\n{ex.Message}{ex.StackTrace}"); + return (0, 0); } - return (number, betaNumber); } #endregion @@ -550,19 +598,27 @@ private static (int, int) GetVersionNumber(string versionString) private static ExternalTools_Version[] GetAvailableToolVersion(ExternalTool tool) { - switch (tool.Id) + try { - case "zxbasic": - return GetBorielBasicVersions(tool.VersionsUrl); + switch (tool.Id) + { + case "zxbasic": + return GetBorielBasicVersions(tool.VersionsUrl); - case "zxbs": - return GetBorielZXBSVersions(tool.VersionsUrl, false); + case "zxbs": + return GetBorielZXBSVersions(tool.VersionsUrl, false); - case "zxbsinstaller": - return GetBorielZXBSVersions(tool.VersionsUrl, true); + case "zxbsinstaller": + return GetBorielZXBSVersions(tool.VersionsUrl, true); - default: - return null; + default: + return null; + } + } + catch (Exception ex) + { + ShowMessage($"Error getting available versions.\r\n{ex.Message}{ex.StackTrace}"); + return null; } } @@ -628,6 +684,7 @@ private static ExternalTools_Version[] GetBorielBasicVersions(string versionsUrl } catch (Exception ex) { + ShowMessage($"Error retrieving Boriel Basic versions.\r\n{ex.Message}{ex.StackTrace}"); return null; } } @@ -683,6 +740,7 @@ private static ExternalTools_Version[] GetBorielZXBSVersions(string versionsUrl, } catch (Exception ex) { + ShowMessage($"Error retrieving ZXBS/Installer versions.\r\n{ex.Message}{ex.StackTrace}"); return null; } } @@ -747,6 +805,7 @@ private static ExternalTools_Version GetGitHubZXBSVersion(string fileLink) } catch (Exception ex) { + ShowMessage($"Error parsing ZXBS version.\r\n{ex.Message}{ex.StackTrace}"); return null; } } @@ -754,35 +813,44 @@ private static ExternalTools_Version GetGitHubZXBSVersion(string fileLink) private static string[] GetAllLinks(string url, string pattern) { - // Get html file - string html; - var handler = new HttpClientHandler - { - AllowAutoRedirect = true - }; - using (HttpClient client = new HttpClient(handler)) + try { - html = client.GetStringAsync(url).GetAwaiter().GetResult(); + // Get html file + string html; + var handler = new HttpClientHandler + { + AllowAutoRedirect = true + }; + using (HttpClient client = new HttpClient(handler)) + { + html = client.GetStringAsync(url).GetAwaiter().GetResult(); + } + if (string.IsNullOrEmpty(html)) + { + return null; + } + //File.WriteAllText("c:/temp/html.text", html); + // Get links + var links = new List(); + { + var regex = new Regex( + pattern, + RegexOptions.IgnoreCase); + var matches = regex.Matches(html); + foreach (Match match in matches) + { + links.Add(match.Groups[1].Value); + } + } + + return links.ToArray(); } - if (string.IsNullOrEmpty(html)) + catch (Exception ex) { + ShowMessage($"Error getting links.\r\n{ex.Message}{ex.StackTrace}"); return null; } - //File.WriteAllText("c:/temp/html.text", html); - // Get links - var links = new List(); - { - var regex = new Regex( - pattern, - RegexOptions.IgnoreCase); - var matches = regex.Matches(html); - foreach (Match match in matches) - { - links.Add(match.Groups[1].Value); - } - } - return links.ToArray(); } #endregion @@ -805,12 +873,12 @@ public static ExternalTools_Version GetToolVersion(string id) case "zxbsinstaller": return GetZXBSInstallerVersion(dir); } - return null; } catch (Exception ex) { - return null; + ShowMessage($"Error retrieving local version for {id}.\r\n{ex.Message}{ex.StackTrace}"); } + return null; } @@ -863,8 +931,10 @@ private static ExternalTools_Version GetBorielBasicVersion(string exePath) } catch (Exception ex) { + ShowMessage($"Error getting local Boriel Basic version.\r\n{ex.Message}{ex.StackTrace}"); return null; } + } @@ -911,6 +981,7 @@ private static ExternalTools_Version GetZXBSVersion(string exePath) } catch (Exception ex) { + ShowMessage($"Error getting local ZXBS version.\r\n{ex.Message}{ex.StackTrace}"); return null; } } @@ -945,6 +1016,7 @@ public static ExternalTools_Version GetZXBSInstallerVersion(string exePath) } catch (Exception ex) { + ShowMessage($"Error getting local ZXBSInstaller version.\r\n{ex.Message}{ex.StackTrace}"); return null; } } @@ -956,16 +1028,27 @@ public static ExternalTools_Version GetZXBSInstallerVersion(string exePath) public static void DownloadAndInstallTools() { - ShowStatusPanel($"Working..."); - foreach (var tool in ExternalTools) + try { - if (tool.IsSelected) + ShowStatusPanel($"Working..."); + foreach (var tool in ExternalTools) { - DownloadAndInstallTool(tool, tool.LatestVersion); + if (Cancel) + { + break; + } + if (tool.IsSelected) + { + DownloadAndInstallTool(tool, tool.LatestVersion); + } } + HideStatusPanel(); + RefreshTools(); + } + catch (Exception ex) + { + ShowMessage($"Error installing.\r\n{ex.Message}{ex.StackTrace}"); } - HideStatusPanel(); - RefreshTools(); } @@ -974,7 +1057,7 @@ public static void DownloadAndInstallTool(ExternalTool tool, ExternalTools_Versi string step = ""; try { - if(tool==null || version == null) + if (tool == null || version == null) { return; } @@ -1164,6 +1247,7 @@ exit 1 private static void ExtractFile(string archive, string destination) { + try { if (archive.ToLower().EndsWith(".zip")) { System.IO.Compression.ZipFile.ExtractToDirectory(archive, destination, true); @@ -1195,6 +1279,11 @@ private static void ExtractFile(string archive, string destination) return; } } + } + catch (Exception ex) + { + ShowMessage($"Error unpacking file {archive} on {destination}.\r\n{ex.Message}{ex.StackTrace}"); + } } @@ -1256,7 +1345,7 @@ private static void SetZXBSConfig() } catch (Exception ex) { - //ShowMessage($"Error updating ZX Basic Studio options.\r\n{ex.Message}\r\n{ex.StackTrace}"); + ShowMessage($"Error updating ZX Basic Studio options.\r\n{ex.Message}\r\n{ex.StackTrace}"); } } @@ -1288,10 +1377,9 @@ public static bool RunZXBasicStudio() } catch (Exception ex) { - ServiceLayer.ShowMessage("Error launching ZX Basic Studio. Please check the installation."); + ServiceLayer.ShowMessage($"Error launching ZX Basic Studio. Please check the installation.\r\n{ex.Message}{ex.StackTrace}"); return false; } } - } } \ No newline at end of file diff --git a/ZXBSInstaller/Assets/cancel.svg b/ZXBSInstaller/Assets/cancel.svg new file mode 100644 index 0000000..eb114de --- /dev/null +++ b/ZXBSInstaller/Assets/cancel.svg @@ -0,0 +1,7 @@ + + + + + + cancel + \ No newline at end of file diff --git a/ZXBSInstaller/Controls/MainControl.axaml b/ZXBSInstaller/Controls/MainControl.axaml index 2479b27..b2b6095 100644 --- a/ZXBSInstaller/Controls/MainControl.axaml +++ b/ZXBSInstaller/Controls/MainControl.axaml @@ -49,7 +49,7 @@ @@ -69,6 +69,12 @@ + diff --git a/ZXBSInstaller/Controls/MainControl.axaml.cs b/ZXBSInstaller/Controls/MainControl.axaml.cs index 2cd52f0..3e8cff3 100644 --- a/ZXBSInstaller/Controls/MainControl.axaml.cs +++ b/ZXBSInstaller/Controls/MainControl.axaml.cs @@ -61,7 +61,7 @@ private void ShowMessage(string message) { Dispatcher.UIThread.Post(() => { - pnlStatus.IsVisible = false; + HideStatusPanel(); var box = MessageBoxManager.GetMessageBoxStandard(new MessageBoxStandardParams { ButtonDefinitions = ButtonEnum.Ok, @@ -83,16 +83,14 @@ private void GetExternalTools() mainVersions.IsVisible = false; mainTools.IsVisible = true; - txtStatus.Text = "Working..."; - progressBar.Value = 0; - pnlStatus.IsVisible = true; + ShowStatusPanel("Working..."); }); var tools = ServiceLayer.GetExternalTools(); Dispatcher.UIThread.Post(() => { - pnlStatus.IsVisible = false; + HideStatusPanel(); if (tools == null) { var box = MessageBoxManager.GetMessageBoxStandard(new MessageBoxStandardParams @@ -230,9 +228,15 @@ private void ShowStatusPanel(string message) { Dispatcher.UIThread.Post(() => { + ServiceLayer.Cancel = false; txtStatus.Text = message; progressBar.Value = 0; pnlStatus.IsVisible = true; + + btnInstall.IsEnabled = false; + btnPlayZXBS.IsEnabled = false; + btnRefresh.IsEnabled = false; + btnSelectPath.IsEnabled = false; }); } @@ -242,6 +246,11 @@ private void HideStatusPanel() Dispatcher.UIThread.Post(() => { pnlStatus.IsVisible = false; + + btnInstall.IsEnabled = true; + btnPlayZXBS.IsEnabled = true; + btnRefresh.IsEnabled = true; + btnSelectPath.IsEnabled = true; }); } @@ -393,4 +402,9 @@ private void btnRefresh_Click(object? sender, RoutedEventArgs e) { new Thread(GetExternalTools).Start(); } + + private void btnCancel_Click(object? sender, RoutedEventArgs e) + { + ServiceLayer.Cancel = true; + } } \ No newline at end of file diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index 1703385..43f6a68 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,14 +6,16 @@ app.manifest true zxbs.ico - 1.0.0.3 + 1.0.0.4 + + From 2cd58a477a6e33b9a68f7d0166eaa930cf38c7ac Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Fri, 20 Feb 2026 18:26:00 +0100 Subject: [PATCH 04/14] ZXBSInstallar v1.0.0-beta5 ZXBasicStudio v1.7.0 --- ZXBSInstaller.Log/ServiceLayer.cs | 164 ++++++++++++------- ZXBSInstaller/Controls/MainControl.axaml.cs | 9 +- ZXBSInstaller/Program.cs | 21 ++- ZXBSInstaller/Properties/launchSettings.json | 11 ++ ZXBSInstaller/ZXBSInstaller.csproj | 9 +- ZXBSInstaller/version.txt | 1 + ZXBStudio/Program.cs | 10 +- ZXBStudio/ZXBasicStudio.csproj | 4 + ZXBStudio/version.txt | 1 + 9 files changed, 167 insertions(+), 63 deletions(-) create mode 100644 ZXBSInstaller/Properties/launchSettings.json create mode 100644 ZXBSInstaller/version.txt create mode 100644 ZXBStudio/version.txt diff --git a/ZXBSInstaller.Log/ServiceLayer.cs b/ZXBSInstaller.Log/ServiceLayer.cs index 09f7a09..099a112 100644 --- a/ZXBSInstaller.Log/ServiceLayer.cs +++ b/ZXBSInstaller.Log/ServiceLayer.cs @@ -260,7 +260,7 @@ public static ExternalTool[] GetExternalTools() int prg = 10; for (int n = 0; n < max; n++) { - if(Cancel) + if (Cancel) { return null; } @@ -889,12 +889,15 @@ private static ExternalTools_Version GetBorielBasicVersion(string exePath) var fileName = Path.Combine(exePath, "zxbc.exe"); if (!File.Exists(fileName)) { - fileName = Path.Combine(exePath, "zxbc"); + fileName = Path.Combine(exePath, "zxbc.py"); } if (!File.Exists(fileName)) { return null; } + return GetVersionFromParameter(fileName); + +#if OLD // Launch "zxbc.exe --version" ProcessStartInfo psi = new ProcessStartInfo { @@ -928,13 +931,13 @@ private static ExternalTools_Version GetBorielBasicVersion(string exePath) Version = version, VersionNumber = number }; +#endif } catch (Exception ex) { ShowMessage($"Error getting local Boriel Basic version.\r\n{ex.Message}{ex.StackTrace}"); return null; } - } @@ -942,46 +945,94 @@ private static ExternalTools_Version GetZXBSVersion(string exePath) { try { - var fileName = Path.Combine(exePath, "ZXBasicStudio.exe"); + var fileName = Path.Combine(exePath, "version.txt"); if (!File.Exists(fileName)) { - fileName = Path.Combine(exePath, "ZXBasicStudio"); + if(File.Exists(Path.Combine(exePath, "ZXBasicStudio.exe")) + || File.Exists(Path.Combine(exePath, "ZXBasicStudio"))) + { + return new ExternalTools_Version() + { + DownloadUrl = "", + BetaNumber = 0, + OperatingSystem = OperatingSystems.All, + Version = "OLD version", + VersionNumber = 0 + }; + } + return null; } + var txt = File.ReadAllText(fileName); + var v = GetVersionNumber(txt); + + var version = new ExternalTools_Version() + { + DownloadUrl = "", + BetaNumber = v.Item2, + OperatingSystem = OperatingSystems.All, + Version = txt, + VersionNumber = v.Item1 + }; + return version; + } + catch (Exception ex) + { + ShowMessage($"Error getting local ZXBS version.\r\n{ex.Message}{ex.StackTrace}"); + return null; + } + } + + + /// + /// Get version from file using --version parameter + /// + /// Executable filename + /// ExternalTools_Version with the version info + private static ExternalTools_Version GetVersionFromParameter(string fileName) + { + try + { if (!File.Exists(fileName)) { return null; } - var fvi = FileVersionInfo.GetVersionInfo(fileName); - if (fvi != null) + ProcessStartInfo psi = new ProcessStartInfo { - // Major, minor, Build, private - var version = $"{fvi.ProductMajorPart}.{fvi.ProductMinorPart}.{fvi.ProductBuildPart}"; - if (fvi.ProductPrivatePart > 0) - { - version += $"-beta{fvi.ProductPrivatePart}"; - } - if (version == "1.0.0") - { - version = "1.6.0-beta6.3"; - } - var v = GetVersionNumber(version); - var versionNumber = v.Item1; - var beta = v.Item2; - return new ExternalTools_Version() - { - DownloadUrl = "", - BetaNumber = beta, - OperatingSystem = OperatingSystems.All, - Version = version, - VersionNumber = versionNumber - }; + FileName = fileName, + Arguments = "--version", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + using Process process = new Process { StartInfo = psi }; + process.Start(); + string output = process.StandardOutput.ReadToEnd(); + string error = process.StandardError.ReadToEnd(); + process.WaitForExit(); + + if (string.IsNullOrEmpty(output)) + { + return null; } - return null; + var version = output.Replace("zxbc.py ", "").Replace("\n", "").Replace("\r", "").Replace("v", ""); + var v = GetVersionNumber(version); + int number = v.Item1; + int beta = v.Item2; + + return new ExternalTools_Version() + { + DownloadUrl = "", + BetaNumber = beta, + OperatingSystem = OperatingSystems.All, + Version = version, + VersionNumber = number + }; } catch (Exception ex) { - ShowMessage($"Error getting local ZXBS version.\r\n{ex.Message}{ex.StackTrace}"); + ShowMessage($"Error getting local Boriel Basic version.\r\n{ex.Message}{ex.StackTrace}"); return null; } } @@ -1160,7 +1211,7 @@ echo on { bashFile = Path.Combine(GeneralConfig.BasePath, "downloads", "zxbsinstall.sh"); bash = @" -#!/bin/bash +# !/bin/bash set -e echo ""Updating installer..."" @@ -1247,39 +1298,40 @@ exit 1 private static void ExtractFile(string archive, string destination) { - try { - if (archive.ToLower().EndsWith(".zip")) - { - System.IO.Compression.ZipFile.ExtractToDirectory(archive, destination, true); - } - else if (CurrentOperatingSystem != OperatingSystems.Windows) + try { - Directory.CreateDirectory(destination); - - var psi = new ProcessStartInfo + if (archive.ToLower().EndsWith(".zip")) { - FileName = "tar", - Arguments = $"-xzf \"{archive}\" -C \"{destination}\"", - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true - }; + System.IO.Compression.ZipFile.ExtractToDirectory(archive, destination, true); + } + else if (CurrentOperatingSystem != OperatingSystems.Windows) + { + Directory.CreateDirectory(destination); - using var process = Process.Start(psi)!; + var psi = new ProcessStartInfo + { + FileName = "tar", + Arguments = $"-xzf \"{archive}\" -C \"{destination}\"", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; - string stdout = process.StandardOutput.ReadToEnd(); - string stderr = process.StandardError.ReadToEnd(); + using var process = Process.Start(psi)!; - process.WaitForExit(); + string stdout = process.StandardOutput.ReadToEnd(); + string stderr = process.StandardError.ReadToEnd(); - if (process.ExitCode != 0) - { - ShowMessage($"Error unpacking file {archive}\r\n{stderr}"); - return; + process.WaitForExit(); + + if (process.ExitCode != 0) + { + ShowMessage($"Error unpacking file {archive}\r\n{stderr}"); + return; + } } } - } catch (Exception ex) { ShowMessage($"Error unpacking file {archive} on {destination}.\r\n{ex.Message}{ex.StackTrace}"); diff --git a/ZXBSInstaller/Controls/MainControl.axaml.cs b/ZXBSInstaller/Controls/MainControl.axaml.cs index 3e8cff3..0bb6d3c 100644 --- a/ZXBSInstaller/Controls/MainControl.axaml.cs +++ b/ZXBSInstaller/Controls/MainControl.axaml.cs @@ -198,7 +198,8 @@ private void UpdateSummary() pnlSummary.Children.Add(new TextBlock() { Text = ServiceLayer.GeneralConfig.BasePath, - Margin = new Thickness(10, 4, 0, 0) + Margin = new Thickness(10, 4, 0, 0), + TextWrapping = TextWrapping.Wrap }); } foreach (var tool in toolItemControls) @@ -211,12 +212,14 @@ private void UpdateSummary() { Text = tool.ExternalTool.Name + ":", Margin = new Thickness(0, 8, 0, 0), - Foreground = Yellow + Foreground = Yellow, + TextWrapping = TextWrapping.Wrap }); pnlSummary.Children.Add(new TextBlock() { Text = System.IO.Path.Combine(ServiceLayer.GeneralConfig.BasePath, tool.ExternalTool.Id), - Margin = new Thickness(10, 4, 0, 0) + Margin = new Thickness(10, 4, 0, 0), + TextWrapping = TextWrapping.Wrap }); } } diff --git a/ZXBSInstaller/Program.cs b/ZXBSInstaller/Program.cs index 16f6385..e73b612 100644 --- a/ZXBSInstaller/Program.cs +++ b/ZXBSInstaller/Program.cs @@ -1,16 +1,33 @@ using Avalonia; +using Avalonia.OpenGL; using System; +using System.IO; +using System.Linq; namespace ZXBSInstaller { internal class Program { + public static string Version = ""; + // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized // yet and stuff might break. [STAThread] - public static void Main(string[] args) => BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); + public static void Main(string[] args) + { + var assembly = System.Reflection.Assembly.GetEntryAssembly(); + var version = assembly.GetName().Version; + Version = $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}"; + + if (args.Contains("--version")) + { + Console.WriteLine($"{Version}"); + return; + } + + BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + } // Avalonia configuration, don't remove; also used by visual designer. public static AppBuilder BuildAvaloniaApp() diff --git a/ZXBSInstaller/Properties/launchSettings.json b/ZXBSInstaller/Properties/launchSettings.json new file mode 100644 index 0000000..dbd81c7 --- /dev/null +++ b/ZXBSInstaller/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "ZXBSInstaller": { + "commandName": "Project" + }, + "WSL": { + "commandName": "WSL2", + "distributionName": "" + } + } +} \ No newline at end of file diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index 43f6a68..830eaa9 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,13 +6,14 @@ app.manifest true zxbs.ico - 1.0.0.4 + 1.0.0.5 + @@ -51,4 +52,10 @@ + + + + PreserveNewest + + diff --git a/ZXBSInstaller/version.txt b/ZXBSInstaller/version.txt new file mode 100644 index 0000000..090eef7 --- /dev/null +++ b/ZXBSInstaller/version.txt @@ -0,0 +1 @@ +1.0.0.5 \ No newline at end of file diff --git a/ZXBStudio/Program.cs b/ZXBStudio/Program.cs index 15c8f84..a25ee63 100644 --- a/ZXBStudio/Program.cs +++ b/ZXBStudio/Program.cs @@ -3,6 +3,7 @@ using Newtonsoft.Json; using System; using System.Diagnostics; +using System.Linq; namespace ZXBasicStudio { @@ -39,13 +40,20 @@ public static string VersionDate { get [STAThread] public static void Main(string[] args) { + SetVersion(); + + if (args.Contains("--version")) + { + Console.WriteLine($"{Version}"); + return; + } + JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Formatting= Formatting.Indented, TypeNameHandling = TypeNameHandling.Auto, }; - SetVersion(); BuildAvaloniaApp() .StartWithClassicDesktopLifetime(args); diff --git a/ZXBStudio/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj index d3fb0ec..0edf91e 100644 --- a/ZXBStudio/ZXBasicStudio.csproj +++ b/ZXBStudio/ZXBasicStudio.csproj @@ -285,6 +285,7 @@ + @@ -555,6 +556,9 @@ PreserveNewest + + PreserveNewest + diff --git a/ZXBStudio/version.txt b/ZXBStudio/version.txt new file mode 100644 index 0000000..1f0cf6b --- /dev/null +++ b/ZXBStudio/version.txt @@ -0,0 +1 @@ +1.7.0.0 \ No newline at end of file From 63815a9d7e096ef142a71489529c93eae09fce3f Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Fri, 20 Feb 2026 18:48:43 +0100 Subject: [PATCH 05/14] Bug launching ZXBS --- ZXBSInstaller/Controls/MainControl.axaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ZXBSInstaller/Controls/MainControl.axaml.cs b/ZXBSInstaller/Controls/MainControl.axaml.cs index 0bb6d3c..57179da 100644 --- a/ZXBSInstaller/Controls/MainControl.axaml.cs +++ b/ZXBSInstaller/Controls/MainControl.axaml.cs @@ -384,6 +384,7 @@ private void Versions_Close(object? sender, RoutedEventArgs e) private void btnPlayZXBS_Click(object? sender, RoutedEventArgs e) { + ServiceLayer.RunZXBasicStudio(); ExitApp(); } From c13e83e1d66866e2ac4ae13f42cb3cd01ab76d40 Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Fri, 20 Feb 2026 23:08:34 +0100 Subject: [PATCH 06/14] ZXBSInstaller v1.0.0-beta6 - Retries if downloading the list of versions from the repositories fails - External tools list on a local file - Source code documented --- ZXBSInstaller.Log/ServiceLayer.cs | 459 ++++++++---------- ZXBSInstaller/Controls/MainControl.axaml.cs | 124 ++++- .../Controls/ToolItemControl.axaml.cs | 36 ++ .../Controls/VersionControl.axaml.cs | 45 +- ZXBSInstaller/ExternalTools.json | 62 +++ ZXBSInstaller/MainWindow.axaml.cs | 4 +- ZXBSInstaller/UITools.cs | 6 + ZXBSInstaller/ZXBSInstaller.csproj | 6 +- ZXBSInstaller/version.txt | 2 +- 9 files changed, 482 insertions(+), 262 deletions(-) create mode 100644 ZXBSInstaller/ExternalTools.json diff --git a/ZXBSInstaller.Log/ServiceLayer.cs b/ZXBSInstaller.Log/ServiceLayer.cs index 099a112..5cb2e56 100644 --- a/ZXBSInstaller.Log/ServiceLayer.cs +++ b/ZXBSInstaller.Log/ServiceLayer.cs @@ -22,17 +22,48 @@ namespace ZXBSInstaller.Log /// public static class ServiceLayer { + /// + /// Application configuration. It is stored in %AppData%\ZXBasicStudio\ZXBSInstallerOptions.json + /// public static Config GeneralConfig = null; + /// + /// List of external tools. + /// public static ExternalTool[] ExternalTools = null; + /// + /// Current operating system + /// public static OperatingSystems CurrentOperatingSystem = OperatingSystems.All; + /// + /// Used to cancel the current operation. It is set to true when the user clicks the cancel button and it is checked in all long operations to stop them if it is true. + /// public static bool Cancel = false; + // Callbacks to update the UI from the service layer + /// + /// Show the status panel with a message. The message is used to inform the user about the current operation being performed. It is called from the service layer to update the UI. + /// private static Action ShowStatusPanel = null; + /// + /// Update the status message and progress. It is called from the service layer to update the UI. The message is used to inform the user about the current operation being performed and the progress is a value between 0 and 100 that indicates the progress of the current operation. + /// private static Action UpdateStatus = null; + /// + /// Hide the status panel. It is called from the service layer to update the UI when the current operation is finished or cancelled. + /// private static Action HideStatusPanel = null; + /// + /// Refresh the list of external tools. It is called from the service layer to update the UI when the list of external tools is updated, for example, after installing or updating a tool. + /// private static Action RefreshTools = null; + /// + /// Show a message in a dialog window. It is called from the service layer to show error messages or any other information that needs to be shown to the user. + /// private static Action ShowMessage = null; + /// + /// Exit the application. It is called from the service layer to exit the application when launching ZXBasicStudio. + /// private static Action ExitApp = null; @@ -52,6 +83,7 @@ public static bool Initialize( { try { + // Set callbacks ShowStatusPanel = callBackShowStatusPanel; UpdateStatus = callBackUpdateStatus; HideStatusPanel = callBackHideStatusPanel; @@ -59,8 +91,10 @@ public static bool Initialize( ShowMessage = callBackShowMessage; ExitApp = callBackExitApp; + // Retrive the configuration GetConfig(); + // Set current operating system if (OperatingSystem.IsWindows()) { CurrentOperatingSystem = OperatingSystems.Windows; @@ -91,13 +125,19 @@ public static bool Initialize( } - public static Config GetConfig() + /// + /// Get the config from file or create a new one if it doesn't exist. The config file is stored in %AppData%\ZXBasicStudio\ZXBSInstallerOptions.json and it contains the configuration for the installer, such as the URL to retrieve the external tools list, the base path to install the tools, etc. + /// + /// Config data. ServiceLayer.GeneralConfig is set + private static Config GetConfig() { try { + // Build filePath var filePath = Path.Combine(Environment.GetFolderPath(SpecialFolder.ApplicationData), "ZXBasicStudio", "ZXBSInstallerOptions.json"); if (File.Exists(filePath)) { + // Read config from file var jsonString = File.ReadAllText(filePath); var cfg = JsonSerializer.Deserialize(jsonString); @@ -107,6 +147,7 @@ public static Config GetConfig() } else { + // Create default config and save it to file GeneralConfig = CreateConfig(); SaveConfig(GeneralConfig); } @@ -120,7 +161,11 @@ public static Config GetConfig() } - public static Config CreateConfig() + /// + /// Create the default config + /// + /// Config object with the default config + private static Config CreateConfig() { try { @@ -128,6 +173,7 @@ public static Config CreateConfig() var cfg = ServiceLayer.GeneralConfig; if (cfg == null) { + // Create base config cfg = new Config() { BasePath = Directory.GetParent(Directory.GetCurrentDirectory()).FullName, @@ -137,6 +183,7 @@ public static Config CreateConfig() }; } + // Fill paths if (cfg.ExternalTools_Paths == null) { cfg.ExternalTools_Paths = new List(); @@ -159,16 +206,24 @@ public static Config CreateConfig() } + /// + /// Save config to file. The config file is stored in %AppData%\ZXBasicStudio\ZXBSInstallerOptions.json and it contains the configuration for the installer, such as the URL to retrieve the external tools list, the base path to install the tools, etc. ServiceLayer.GeneralConfig is updated with the new config data. + /// + /// Config to store + /// Config with the current configuration public static Config SaveConfig(Config config) { try { + // Build dir and file path var dir = Path.Combine(Environment.GetFolderPath(SpecialFolder.ApplicationData), "ZXBasicStudio"); var fileName = Path.Combine(dir, "ZXBSInstallerOptions.json"); if (!Directory.Exists(dir)) { + // Create directory if it doesn't exist Directory.CreateDirectory(dir); } + // Save config to file var jsonString = JsonSerializer.Serialize(config, new JsonSerializerOptions() { WriteIndented = true }); File.WriteAllText(fileName, jsonString); GeneralConfig = config; @@ -208,6 +263,10 @@ private static int ToInteger(object value) } + /// + /// Open an url in the default browser. It is used to open the site and license urls of the external tools. It is called from the service layer when the user clicks on the site or license buttons of an external tool. + /// + /// Url to open public static void OpenUrlInBrowser(string url) { try @@ -242,7 +301,6 @@ public static void OpenUrlInBrowser(string url) /// /// Retrieves all external tools configured for use with the application. - /// The data is stored in https://raw.githubusercontent.com/boriel-basic/ZXBasicStudio/refs/heads/master/externaltools.json /// /// An array of objects representing the available external tools. The array is empty /// if no external tools are configured or can download the config file. @@ -252,27 +310,33 @@ public static ExternalTool[] GetExternalTools() { UpdateStatus?.Invoke("Retrieving external tools information...", 5); - using var httpClient = new HttpClient(); - string json = httpClient.GetStringAsync(GeneralConfig.ToolsListURL).GetAwaiter().GetResult(); + var json = File.ReadAllText("ExternalTools.json"); + //// Download the external tools list from the configured URL + //using var httpClient = new HttpClient(); + //string json = httpClient.GetStringAsync(GeneralConfig.ToolsListURL).GetAwaiter().GetResult(); var tools = JsonSerializer.Deserialize(json); int max = tools.Length; int prg = 10; for (int n = 0; n < max; n++) { + // Cancel? if (Cancel) { return null; } + // Update status var tool = tools[n]; prg = (n * 90) / max; UpdateStatus?.Invoke($"Retrieving versions for {tool.Name}...", prg + 10); + // Get available versions for tool tool.Versions = GetAvailableToolVersion(tool); if (tool.Versions == null) { tool.Versions = new ExternalTools_Version[0]; } + // Get installed version tool.InstalledVersion = GetToolVersion(tool.Id); // Set latest version @@ -313,14 +377,12 @@ public static ExternalTool[] GetExternalTools() } } } - + // Set tool local path tool.LocalPath = Path.Combine(GeneralConfig.BasePath, tool.Id); } - //GetPaths(ref tools); - + // order tools by order property ExternalTools = tools.OrderBy(d => d.Order).ToArray(); - return ExternalTools; } catch (Exception ex) @@ -328,211 +390,20 @@ public static ExternalTool[] GetExternalTools() ShowMessage($"Error retrieving external tools information. Please check your internet connection and try again.\r\n{ex.Message}{ex.StackTrace}"); return null; } -#if GENERATE_JSON - var lst = new List(); - // Compiler - { - UpdateStatus?.Invoke(null, 10); - var tool = new ExternalTool() - { - Id = "zxbasic", - Enabled = true, - Name = "Boriel ZX Basic Compiler", - Author = "Boriel", - Description = "ZXBCompiler is a ZX Spectrum BASIC cross compiler that translates ZX Spectrum BASIC code into optimized machine code, enabling faster execution and enhanced performance on ZX Spectrum systems. This tool is required to run and debug programs.", - DirectUpdate = true, - SupportedOperatingSystems = new OperatingSystems[] { OperatingSystems.Windows, OperatingSystems.Linux, OperatingSystems.MacOS }, - SiteUrl = "https://boriel-basic.net", - LicenceUrl = "https://raw.githubusercontent.com/boriel-basic/zxbasic/refs/heads/main/LICENSE.txt", - LicenseType = "GNU Affero General Public License v3.0", - VersionsUrl = "https://boriel.com/files/zxb/", - Order = 1 - }; - UpdateStatus?.Invoke(null, 15); - tool.Versions = GetBorielBasicVersions(tool.VersionsUrl); - lst.Add(tool); - UpdateStatus?.Invoke(null, 20); - } - - // ZXBasic Studio IDE - { - UpdateStatus?.Invoke(null, 20); - var tool = new ExternalTool() - { - Id = "zxbs", - Enabled = true, - Name = "ZX Basic Studio", - Author = "Dr.Gusman, Boriel, Duefectu, AdolFITO, Hash6Iron and SirRickster", - Description = "IDE (Integrated Development Environment) with Boriel Basic code editor, Assembler, UDGs, fonts, sprites, .tap editor, debugger, emulator, etc. This tool is optional but highly recommended.", - DirectUpdate = true, - SupportedOperatingSystems = new OperatingSystems[] { OperatingSystems.Windows, OperatingSystems.Linux, OperatingSystems.MacOS }, - SiteUrl = "https://github.com/boriel-basic/ZXBasicStudio", - LicenceUrl = "https://raw.githubusercontent.com/boriel-basic/ZXBasicStudio/refs/heads/master/LICENSE.txt", - LicenseType = "MIT License", - VersionsUrl = "https://github.com/boriel-basic/ZXBasicStudio/releases/", - Order=2 - }; - UpdateStatus?.Invoke(null, 25); - // Versions - var versions = new List(); - versions.Add(new ExternalTools_Version() - { - Version = "1.6.0-beta5", - BetaNumber = 5, - VersionNumber = 1006005, - DownloadUrl = "https://github.com/boriel-basic/ZXBasicStudio/releases/download/v1.6/ZXBasicStudio-linux-x64.v1.6.0-beta5.zip", - OperatingSystem = OperatingSystems.Linux, - }); - versions.Add(new ExternalTools_Version() - { - Version = "1.6.0-beta5", - BetaNumber = 5, - VersionNumber = 1006005, - DownloadUrl = "https://github.com/boriel-basic/ZXBasicStudio/releases/download/v1.6/ZXBasicStudio-osx-x64.v1.6.0-beta5.zip", - OperatingSystem = OperatingSystems.MacOS, - }); - versions.Add(new ExternalTools_Version() - { - Version = "1.6.0-beta5", - BetaNumber = 5, - VersionNumber = 1006005, - DownloadUrl = "https://github.com/boriel-basic/ZXBasicStudio/releases/download/v1.6/ZXBasicStudio-win-x64.v1.6.0-beta5.zip", - OperatingSystem = OperatingSystems.Windows, - }); - tool.Versions = versions.ToArray(); - lst.Add(tool); - UpdateStatus?.Invoke(null, 30); - } - // Get tools paths from ZXBS config file - GetPaths(ref lst); - - - // ZXBasic Studio Installer - { - UpdateStatus?.Invoke(null, 30); - var tool = new ExternalTool() - { - Id = "zxbsinstaller", - Enabled = true, - Name = "ZX Basic Studio Installer", - Author = "Duefectu", - Description = "This program, and it is used to download, install and keep all external tools and ZX Basic Studio itself up to date.", - DirectUpdate = true, - SupportedOperatingSystems = new OperatingSystems[] { OperatingSystems.Windows, OperatingSystems.Linux, OperatingSystems.MacOS }, - SiteUrl = "https://github.com/boriel-basic/ZXBasicStudio", - LicenceUrl = "https://raw.githubusercontent.com/boriel-basic/ZXBasicStudio/refs/heads/master/LICENSE.txt", - LicenseType = "MIT License", - VersionsUrl = "https://github.com/boriel-basic/ZXBasicStudio/releases/", - Order = 0 - }; - UpdateStatus?.Invoke(null, 35); - // Versions - var versions = new List(); - versions.Add(new ExternalTools_Version() - { - Version = "0.0.1-beta1", - BetaNumber = 1, - VersionNumber = 1001, - DownloadUrl = "https://github.com/boriel-basic/ZXBasicStudio/releases/download/v1.6/ZXBasicStudio-linux-x64.v1.6.0-beta5.zip", - OperatingSystem = OperatingSystems.Linux, - }); - versions.Add(new ExternalTools_Version() - { - Version = "0.0.1-beta1", - BetaNumber = 1, - VersionNumber = 1001, - DownloadUrl = "https://github.com/boriel-basic/ZXBasicStudio/releases/download/v1.6/ZXBasicStudio-osx-x64.v1.6.0-beta5.zip", - OperatingSystem = OperatingSystems.MacOS, - }); - versions.Add(new ExternalTools_Version() - { - Version = "0.0.1-beta1", - BetaNumber = 1, - VersionNumber = 1001, - DownloadUrl = "https://github.com/boriel-basic/ZXBasicStudio/releases/download/v1.6/ZXBasicStudio-win-x64.v1.6.0-beta5.zip", - OperatingSystem = OperatingSystems.Windows, - }); - tool.Versions = versions.ToArray(); - lst.Add(tool); - UpdateStatus?.Invoke(null, 40); - } - // Get tools paths from ZXBS config file - GetPaths(ref lst); - - externalTools = lst.OrderBy(d=>d.Order).ToArray(); - - var test=JsonSerializer.Serialize(externalTools); - File.WriteAllText(@"c:\temp\zxbsinstaller.json",test); - - return externalTools; -#endif - } - - - - public static void GetPaths(ref ExternalTool[] tools) - { - try - { - var filePath = Path.Combine(Environment.GetFolderPath(SpecialFolder.ApplicationData), "ZXBasicStudio", "ZXBasicStudioOptions.json"); - if (!File.Exists(filePath)) - { - return; - } - var jsonString = File.ReadAllText(filePath); - using JsonDocument doc = JsonDocument.Parse(jsonString); - JsonElement root = doc.RootElement; - - UpdatePath("zxbasic", "ZxbcPath", root, ref tools); - // ZX Basic Studio - { - var tool = tools.FirstOrDefault(t => t.Id == "zxbs"); - if (tool != null) - { - tool.LocalPath = Directory.GetCurrentDirectory(); - } - } - // ZX Basic Studio Installer - { - var tool = tools.FirstOrDefault(t => t.Id == "zxbsinstaller"); - if (tool != null) - { - tool.LocalPath = Directory.GetCurrentDirectory(); - } - } - } - catch (Exception ex) - { - ShowMessage($"Error getting paths.\r\n{ex.Message}{ex.StackTrace}"); - } - } - - - private static void UpdatePath(string toolId, string property, JsonElement root, ref ExternalTool[] tools) - { - try - { - var tool = tools.FirstOrDefault(t => t.Id == toolId); - if (tool == null) - { - return; - } - if (root.TryGetProperty(property, out JsonElement element)) - { - string value = element.GetString(); - tool.FullLocalPath = value; - var fn = Path.GetFileName(value); - value = value.Replace(fn, ""); - tool.LocalPath = value; - } - } - catch (Exception ex) - { - ShowMessage($"Error updating path.\r\n{ex.Message}{ex.StackTrace}"); - } } + /// + /// Get version numeric value and beta value from version string. + /// Four values are used, the last one identifying the beta version number. If it is 0, it is the stable version. + /// 1.2.3.0 = version 1.2.3 stable + /// 1.2.3.4 = version 1.2.3 beta 4 + /// Values are multiplied by 1000 to get a single integer value that can be compared easily. For example: + /// 1.2.3.0 = 1*1000*1000*1000 + 2*1000*1000 + 3*1000 + 0 = 1002003000 + /// 1.2.3.4 = 1*1000*1000*1000 + 2*1000*1000 + 3*1000 + 4 = 1002003004 + /// + /// Version string + /// Item1 = version number, Item2 = beta number private static (int, int) GetVersionNumber(string versionString) { try @@ -540,6 +411,7 @@ private static (int, int) GetVersionNumber(string versionString) int number = 0; int betaNumber = 0; string version = versionString; + // If it is a beta version, replace the -beta with . and add the beta number as the fourth value. if (version.Contains("-beta")) { var mv = Regex.Match(version, @"beta(\d+)(?:[-\.]|$)", RegexOptions.IgnoreCase); @@ -553,11 +425,13 @@ private static (int, int) GetVersionNumber(string versionString) version += ".0"; } + // Split version string var versionParts = version.Split("."); if (versionParts.Length == 5) { versionParts[3] += versionParts[4]; } + // Get value for each part of the version string for (int n = 0; n < 4; n++) { number *= 1000; @@ -596,6 +470,16 @@ private static (int, int) GetVersionNumber(string versionString) #region External tools versions retrieval + /// + /// Get the versions available for the specified tool. + /// The versions are retrieved from the tool's VersionsUrl property, which is configured in the external tools list. + /// The method parses the HTML of the VersionsUrl page to extract the available versions and their download URLs. + /// The parsing is specific for each tool, depending on how the versions are listed in the page. + /// The method returns an array of ExternalTools_Version objects that contain the version information, such as version number, beta number, download URL and operating system. + /// If there is an error retrieving or parsing the versions, it returns null and shows an error message to the user. + /// + /// + /// private static ExternalTools_Version[] GetAvailableToolVersion(ExternalTool tool) { try @@ -626,12 +510,14 @@ private static ExternalTools_Version[] GetAvailableToolVersion(ExternalTool tool /// /// Get versions data for Boriel Basic Compiler /// - /// - /// + /// Repository URL + /// Array of versions private static ExternalTools_Version[] GetBorielBasicVersions(string versionsUrl) { try { + // Get all hrefs in the page according to the pattern. + // The pattern is specific for the Boriel Basic repository page, which contains links to the versions in the format zxbasic-v1.2.3-win.zip var links = GetAllLinks(versionsUrl, @"]*href\s*=\s*[""']([^""']+)[""']"); // Parse links extracting versions data @@ -693,8 +579,9 @@ private static ExternalTools_Version[] GetBorielBasicVersions(string versionsUrl /// /// Get versions data for ZX Basic Studio Compiler /// - /// - /// + /// Repository URL + /// True to get installer versions or false to get ZXBS versions + /// Array of versions private static ExternalTools_Version[] GetBorielZXBSVersions(string versionsUrl, bool installer) { try @@ -704,6 +591,7 @@ private static ExternalTools_Version[] GetBorielZXBSVersions(string versionsUrl, // Get only releases links = links.Where(d => d.Contains("/boriel-basic/ZXBasicStudio/releases/tag/")).ToArray(); + // Process all links var versions = new List(); foreach (var link in links) { @@ -746,6 +634,11 @@ private static ExternalTools_Version[] GetBorielZXBSVersions(string versionsUrl, } + /// + /// Get version based on the link for ZXBS and ZXBSInstaller. + /// + /// Link of the version + /// Version info private static ExternalTools_Version GetGitHubZXBSVersion(string fileLink) { try @@ -811,25 +704,43 @@ private static ExternalTools_Version GetGitHubZXBSVersion(string fileLink) } + /// + /// Get all links of an url based on a regex pattern. + /// It is used to parse the HTML of the versions page of the tools to extract the links of the versions. + /// The pattern is specific for each tool, depending on how the versions are listed in the page. + /// + /// Url to retrive + /// Regex patter for the urls + /// Array of links in string format private static string[] GetAllLinks(string url, string pattern) { try { // Get html file - string html; + string html = ""; var handler = new HttpClientHandler { AllowAutoRedirect = true }; - using (HttpClient client = new HttpClient(handler)) + // Download page with retries in case of network errors or timeouts + int retries = 5; + while (retries-- > 0) { - html = client.GetStringAsync(url).GetAwaiter().GetResult(); + using (HttpClient client = new HttpClient(handler)) + { + html = client.GetStringAsync(url).GetAwaiter().GetResult(); + } + if (!string.IsNullOrEmpty(html)) + { + break; + } + Thread.Sleep(1000); } if (string.IsNullOrEmpty(html)) { return null; } - //File.WriteAllText("c:/temp/html.text", html); + // Get links var links = new List(); { @@ -858,6 +769,11 @@ private static string[] GetAllLinks(string url, string pattern) #region Local tools versions + /// + /// Get local version of one external tool + /// + /// Tool id + /// Installed version public static ExternalTools_Version GetToolVersion(string id) { try @@ -882,56 +798,29 @@ public static ExternalTools_Version GetToolVersion(string id) } + /// + /// Get installed Boriel ZX Basic Compiler version + /// + /// Path of the tool + /// Version info private static ExternalTools_Version GetBorielBasicVersion(string exePath) { try { + // Windows fileName var fileName = Path.Combine(exePath, "zxbc.exe"); if (!File.Exists(fileName)) { + // If not exist, try Linux/Mac fileName fileName = Path.Combine(exePath, "zxbc.py"); } if (!File.Exists(fileName)) { + // Not found return null; } + // Retrieve version executing with --version parameter return GetVersionFromParameter(fileName); - -#if OLD - // Launch "zxbc.exe --version" - ProcessStartInfo psi = new ProcessStartInfo - { - FileName = fileName, - Arguments = "--version", - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true - }; - using Process process = new Process { StartInfo = psi }; - process.Start(); - string output = process.StandardOutput.ReadToEnd(); - string error = process.StandardError.ReadToEnd(); - process.WaitForExit(); - - if (string.IsNullOrEmpty(output)) - { - return null; - } - var version = output.Replace("zxbc.py ", "").Replace("\n", "").Replace("\r", "").Replace("v", ""); - var v = GetVersionNumber(version); - int number = v.Item1; - int beta = v.Item2; - - return new ExternalTools_Version() - { - DownloadUrl = "", - BetaNumber = beta, - OperatingSystem = OperatingSystems.All, - Version = version, - VersionNumber = number - }; -#endif } catch (Exception ex) { @@ -941,6 +830,12 @@ private static ExternalTools_Version GetBorielBasicVersion(string exePath) } + /// + /// Get intalled ZX Basic Studio version. + /// The version is stored in version.txt file in ZXBS folder + /// + /// ZXBS path + /// Version info private static ExternalTools_Version GetZXBSVersion(string exePath) { try @@ -948,9 +843,11 @@ private static ExternalTools_Version GetZXBSVersion(string exePath) var fileName = Path.Combine(exePath, "version.txt"); if (!File.Exists(fileName)) { + // no version.txt file if(File.Exists(Path.Combine(exePath, "ZXBasicStudio.exe")) || File.Exists(Path.Combine(exePath, "ZXBasicStudio"))) { + // return "OLD version" return new ExternalTools_Version() { DownloadUrl = "", @@ -962,9 +859,17 @@ private static ExternalTools_Version GetZXBSVersion(string exePath) } return null; } + // Read version from file var txt = File.ReadAllText(fileName); var v = GetVersionNumber(txt); + // Is an stable version (ends with .0) + var parts = txt.Split("."); + if (parts.Length == 4 && parts[3] == "0") + { + txt = $"{parts[0]}.{parts[1]}.{parts[2]}"; + } + var version = new ExternalTools_Version() { DownloadUrl = "", @@ -1038,10 +943,16 @@ private static ExternalTools_Version GetVersionFromParameter(string fileName) } + /// + /// Gewt own version + /// + /// Not needed + /// Version info public static ExternalTools_Version GetZXBSInstallerVersion(string exePath) { try { + // Get assembly version var assemblyVersion = Assembly.GetEntryAssembly()?.GetName().Version?.ToString(); var parts = assemblyVersion.Split('.'); if (parts.Length < 4) @@ -1050,6 +961,7 @@ public static ExternalTools_Version GetZXBSInstallerVersion(string exePath) } ; var version = $"{parts[0]}.{parts[1]}.{parts[2]}"; + // It's a beta? var beta = ToInteger(parts[3]); if (beta > 0) { @@ -1077,6 +989,9 @@ public static ExternalTools_Version GetZXBSInstallerVersion(string exePath) #region Install external tool + /// + /// Download and install al selected tools + /// public static void DownloadAndInstallTools() { try @@ -1103,8 +1018,14 @@ public static void DownloadAndInstallTools() } + /// + /// Download and install one tool + /// + /// Tool to install + /// Version info to install public static void DownloadAndInstallTool(ExternalTool tool, ExternalTools_Version version) { + // For debug string step = ""; try { @@ -1187,6 +1108,13 @@ public static void DownloadAndInstallTool(ExternalTool tool, ExternalTools_Versi } + /// + /// Install ZXBSInstaller... + /// Generate a batch/bash file to expand .zip, decompress and launch ZXBSInstaller + /// + /// Tool info for ZXBSInstaller + /// Local path of the downloaded .zip file with the new version of ZXBSInstaller + /// Installation destination path private static void InstallInstaller(ExternalTool tool, string tempFile, string installationPath) { try @@ -1196,6 +1124,7 @@ private static void InstallInstaller(ExternalTool tool, string tempFile, string string bashFile = ""; if (CurrentOperatingSystem == OperatingSystems.Windows) { + // Windows bashFile = Path.Combine(GeneralConfig.BasePath, "downloads", "zxbsinstall.bat"); bash = @" @echo off @@ -1209,6 +1138,7 @@ echo on } else { + // Linux and Mac bashFile = Path.Combine(GeneralConfig.BasePath, "downloads", "zxbsinstall.sh"); bash = @" # !/bin/bash @@ -1283,7 +1213,6 @@ exit 1 p.Start(); } - // Exit app ExitApp(); } @@ -1296,16 +1225,23 @@ exit 1 } + /// + /// Extract file + /// + /// Path of the .zip file + /// Destination folder private static void ExtractFile(string archive, string destination) { try { if (archive.ToLower().EndsWith(".zip")) { + // Extract .zip file System.IO.Compression.ZipFile.ExtractToDirectory(archive, destination, true); } else if (CurrentOperatingSystem != OperatingSystems.Windows) { + // Extract .tar file on Linux and Mac Directory.CreateDirectory(destination); var psi = new ProcessStartInfo @@ -1339,13 +1275,18 @@ private static void ExtractFile(string archive, string destination) } + /// + /// Update ZX Basic Studio config with the tools path for "zxbc" and "zxbasm" + /// private static void SetZXBSConfig() { try { + // Build path for ZX Basic Studio configuration file var filePath = Path.Combine(Environment.GetFolderPath(SpecialFolder.ApplicationData), "ZXBasicStudio", "ZXBasicStudioOptions.json"); if (!File.Exists(filePath)) { + // Create a default config if not exists string data = @"{ ""ZxbasmPath"": """", ""ZxbcPath"": """", @@ -1363,10 +1304,12 @@ private static void SetZXBSConfig() File.WriteAllText(filePath, data); } + // Open config file var sb = new StringBuilder(); var sr = new StreamReader(filePath); while (!sr.EndOfStream) { + // Read one line var line = sr.ReadLine(); // Set values if (line.Contains("ZxbasmPath")) @@ -1405,6 +1348,10 @@ private static void SetZXBSConfig() #endregion + /// + /// Launch ZX Basic Studio + /// + /// True if correct or false if error public static bool RunZXBasicStudio() { try diff --git a/ZXBSInstaller/Controls/MainControl.axaml.cs b/ZXBSInstaller/Controls/MainControl.axaml.cs index 57179da..e582e36 100644 --- a/ZXBSInstaller/Controls/MainControl.axaml.cs +++ b/ZXBSInstaller/Controls/MainControl.axaml.cs @@ -21,31 +21,56 @@ namespace ZXBSInstaller.Controls; +/// +/// Main window +/// public partial class MainControl : UserControl { + /// + /// List of ToolItemControl created items + /// private List toolItemControls = new List(); + /// + /// Yellow color for labels + /// private static Brush Yellow = new SolidColorBrush(Colors.Yellow); + + /// + /// Main constructor + /// public MainControl() { InitializeComponent(); + // Set events this.Loaded += MainControl_Loaded; txtBasePath.TextChanged += TxtBasePath_TextChanged; chkOnlyStableVersions.IsCheckedChanged += ChkOnlyStableVersions_IsCheckedChanged; chkSetZXBSOptions.IsCheckedChanged += ChkSetZXBSOptions_IsCheckedChanged; } + + /// + /// Initialize in a new Thread when loaded + /// + /// + /// private void MainControl_Loaded(object? sender, RoutedEventArgs e) { new Thread(Initialize).Start(); } + /// + /// Initialize + /// private void Initialize() { + // Initialize ServiceLayer ServiceLayer.Initialize(ShowStatusPanel, UpdateStatus, HideStatusPanel, GetExternalTools, ShowMessage, ExitApp); + // Set config fields in UIThread Dispatcher.UIThread.Post(() => { txtBasePath.Text = ServiceLayer.GeneralConfig.BasePath; @@ -53,10 +78,15 @@ private void Initialize() chkSetZXBSOptions.IsChecked = ServiceLayer.GeneralConfig.SetZXBSConfig; }); + // Get external tools list GetExternalTools(); } + /// + /// Show a message into a dialog box + /// + /// Message to display private void ShowMessage(string message) { Dispatcher.UIThread.Post(() => @@ -76,16 +106,20 @@ private void ShowMessage(string message) } + /// + /// Get list of external tools and local versions + /// private void GetExternalTools() { + // Set UI... Dispatcher.UIThread.Post(() => { mainVersions.IsVisible = false; mainTools.IsVisible = true; - ShowStatusPanel("Working..."); }); + // Get tools var tools = ServiceLayer.GetExternalTools(); Dispatcher.UIThread.Post(() => @@ -93,6 +127,7 @@ private void GetExternalTools() HideStatusPanel(); if (tools == null) { + // Error! var box = MessageBoxManager.GetMessageBoxStandard(new MessageBoxStandardParams { ButtonDefinitions = ButtonEnum.Ok, @@ -107,12 +142,16 @@ private void GetExternalTools() } else { + // Show tools ShowData(); } }); } + /// + /// Show tools info + /// private void ShowData() { toolItemControls.Clear(); @@ -121,14 +160,21 @@ private void ShowData() pnlTools.Children.Clear(); foreach (var tool in tools) { + // Create on ToolItemControl foreach tool var control = new ToolItemControl(tool, Command_Received); toolItemControls.Add(control); pnlTools.Children.Add(control); } + // Update summary area UpdateSummary(); } + /// + /// Command received from sub-controls + /// + /// Tool id + /// Command private void Command_Received(string id, string command) { switch (command) @@ -146,6 +192,9 @@ private void Command_Received(string id, string command) } + /// + /// Show Summary panel + /// private void UpdateSummary() { Dispatcher.UIThread.Post(() => @@ -157,6 +206,7 @@ private void UpdateSummary() { if (tool.IsSelected) { + // Data for selected tools var tb = new TextBlock(); tb.TextWrapping = Avalonia.Media.TextWrapping.Wrap; if (tool.ExternalTool.InstalledVersion == null) @@ -173,6 +223,7 @@ private void UpdateSummary() } if (allUpToDate) { + // Nothing to update var tb = new TextBlock(); tb.TextWrapping = Avalonia.Media.TextWrapping.Wrap; tb.Text = "All tools are up to date."; @@ -188,7 +239,7 @@ private void UpdateSummary() pnlSummary.Children.Add(separator); } - // Show tools tree + // Show base path { pnlSummary.Children.Add(new TextBlock() { @@ -202,6 +253,7 @@ private void UpdateSummary() TextWrapping = TextWrapping.Wrap }); } + // Show tools installation paths foreach (var tool in toolItemControls) { var tb = new TextBlock(); @@ -227,6 +279,10 @@ private void UpdateSummary() } + /// + /// Show status panel + /// + /// Message to display private void ShowStatusPanel(string message) { Dispatcher.UIThread.Post(() => @@ -244,6 +300,9 @@ private void ShowStatusPanel(string message) } + /// + /// Hide status panel + /// private void HideStatusPanel() { Dispatcher.UIThread.Post(() => @@ -278,6 +337,12 @@ private void UpdateStatus(string message, int progress) }); } + + /// + /// Button select path + /// + /// + /// private void btnSelectPath_Click(object? sender, RoutedEventArgs e) { var dlg = new OpenFolderDialog() @@ -299,12 +364,23 @@ private void btnSelectPath_Click(object? sender, RoutedEventArgs e) } + + /// + /// Button install components + /// + /// + /// private void btnInstall_Click(object? sender, RoutedEventArgs e) { new Thread(ServiceLayer.DownloadAndInstallTools).Start(); } + /// + /// chkSetZXBSOptions checked changed + /// + /// + /// private void ChkSetZXBSOptions_IsCheckedChanged(object? sender, RoutedEventArgs e) { ServiceLayer.GeneralConfig.SetZXBSConfig = chkSetZXBSOptions.IsChecked == true; @@ -312,6 +388,11 @@ private void ChkSetZXBSOptions_IsCheckedChanged(object? sender, RoutedEventArgs } + /// + /// chkOnlyStableVersions checked changed + /// + /// + /// private void ChkOnlyStableVersions_IsCheckedChanged(object? sender, RoutedEventArgs e) { ServiceLayer.GeneralConfig.OnlyStableVersions = chkOnlyStableVersions.IsChecked == true; @@ -319,6 +400,11 @@ private void ChkOnlyStableVersions_IsCheckedChanged(object? sender, RoutedEventA } + /// + /// Base path changed + /// + /// + /// private void TxtBasePath_TextChanged(object? sender, TextChangedEventArgs e) { ServiceLayer.GeneralConfig.BasePath = txtBasePath.Text; @@ -326,6 +412,10 @@ private void TxtBasePath_TextChanged(object? sender, TextChangedEventArgs e) } + /// + /// Show version info for a tool + /// + /// Tool id private void ShowVersions(string id) { Dispatcher.UIThread.Post(() => @@ -333,8 +423,10 @@ private void ShowVersions(string id) mainTools.IsVisible = false; mainVersions.IsVisible = true; pnlVersions.Children.Clear(); + var tool = ServiceLayer.ExternalTools.FirstOrDefault(d => d.Id == id); + // Close button { var btn = new Button() { @@ -346,10 +438,12 @@ private void ShowVersions(string id) pnlVersions.Children.Add(btn); } + // Header var versionControlHeader = new VersionControl(null, null, Command_Received); pnlVersions.Children.Add(versionControlHeader); foreach (var version in tool.Versions) { + // Version line if (ServiceLayer.GeneralConfig.OnlyStableVersions && version.BetaNumber > 0) { continue; @@ -358,6 +452,7 @@ private void ShowVersions(string id) pnlVersions.Children.Add(versionControl); } + // Anothe Close button at the bottom of the list { var btn = new Button() { @@ -373,6 +468,11 @@ private void ShowVersions(string id) } + /// + /// Versions button + /// + /// + /// private void Versions_Close(object? sender, RoutedEventArgs e) { Dispatcher.UIThread.Post(() => @@ -382,6 +482,12 @@ private void Versions_Close(object? sender, RoutedEventArgs e) }); } + + /// + /// Run ZXBS button + /// + /// + /// private void btnPlayZXBS_Click(object? sender, RoutedEventArgs e) { ServiceLayer.RunZXBasicStudio(); @@ -389,6 +495,9 @@ private void btnPlayZXBS_Click(object? sender, RoutedEventArgs e) } + /// + /// Exit application + /// private void ExitApp() { Dispatcher.UIThread.Post(() => @@ -402,11 +511,22 @@ private void ExitApp() } + /// + /// Refresh button + /// + /// + /// private void btnRefresh_Click(object? sender, RoutedEventArgs e) { new Thread(GetExternalTools).Start(); } + + /// + /// Cacel button + /// + /// + /// private void btnCancel_Click(object? sender, RoutedEventArgs e) { ServiceLayer.Cancel = true; diff --git a/ZXBSInstaller/Controls/ToolItemControl.axaml.cs b/ZXBSInstaller/Controls/ToolItemControl.axaml.cs index 392cbcb..a8b277a 100644 --- a/ZXBSInstaller/Controls/ToolItemControl.axaml.cs +++ b/ZXBSInstaller/Controls/ToolItemControl.axaml.cs @@ -7,33 +7,56 @@ namespace ZXBSInstaller.Controls; + +/// +/// Box with the tool information +/// public partial class ToolItemControl : UserControl { + /// + /// External tool for the box + /// public ExternalTool ExternalTool = null; + /// + /// Tool selected for installation? + /// public bool IsSelected = false; + // Colors public static SolidColorBrush colorRed = new SolidColorBrush(Colors.Red); public static SolidColorBrush colorGreen = new SolidColorBrush(Colors.LightGreen); + /// + /// Command callBack + /// private static Action Command = null; + /// + /// Constructor + /// + /// External tool to display + /// Command delagate public ToolItemControl(ExternalTool tool, Action callBackCommand) { InitializeComponent(); + // Set fileds ExternalTool = tool; Command = callBackCommand; + // Show tool image and data UITools.ShowImage($"{ExternalTool.Id}.png", imgIcon); txtName.Text = tool.Name; txtDescription.Text = tool.Description; txtPath.Text = "Path: " + tool.LocalPath; + // Set chkSelect status IsSelected = tool.UpdateNeeded; tool.IsSelected = IsSelected; chkSelect.IsChecked = IsSelected; + // Display installed version if (tool.InstalledVersion == null) { txtActual.Text = $"Installed version: None"; @@ -52,6 +75,7 @@ public ToolItemControl(ExternalTool tool, Action callBackCommand } } + // Display latest version if (tool.LatestVersion == null) { txtLatest.Text = $"Latest version: Unknow"; @@ -62,6 +86,12 @@ public ToolItemControl(ExternalTool tool, Action callBackCommand } } + + /// + /// ChkSelected changes + /// + /// + /// private void chkSelect_IsCheckedChanged(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { IsSelected = chkSelect.IsChecked == true; @@ -69,6 +99,12 @@ private void chkSelect_IsCheckedChanged(object? sender, Avalonia.Interactivity.R Command(ExternalTool.Id, "CHECKED"); } + + /// + /// Versions button + /// + /// + /// private void btnAllVersions_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { Command(ExternalTool.Id, "VERSIONS"); diff --git a/ZXBSInstaller/Controls/VersionControl.axaml.cs b/ZXBSInstaller/Controls/VersionControl.axaml.cs index bbbda14..e99cb19 100644 --- a/ZXBSInstaller/Controls/VersionControl.axaml.cs +++ b/ZXBSInstaller/Controls/VersionControl.axaml.cs @@ -12,35 +12,58 @@ namespace ZXBSInstaller.Controls; public partial class VersionControl : UserControl { + /// + /// External tool of the item + /// public ExternalTool Tool { get; set; } + /// + /// Version info of the item + /// public ExternalTools_Version ToolVersion = null; + /// + /// Pair or odd row + /// private bool IsPair = false; - private static bool IsPairGlobal = false; + /// + /// Colours + /// private static SolidColorBrush ColorPair= new SolidColorBrush(Color.FromRgb(20, 20, 20)); private static SolidColorBrush ColorNormal= new SolidColorBrush(Colors.Black); private static SolidColorBrush ColorHover = new SolidColorBrush(Colors.DarkBlue); private static SolidColorBrush ColorGreen = new SolidColorBrush(Colors.LightGreen); + /// + /// CallBack Command + /// private static Action Command = null; + /// + /// Constructor + /// + /// ExternalTool of the item + /// Version info of the tool + /// CallBack command public VersionControl(ExternalTool tool, ExternalTools_Version toolVersion, Action callBackCommand) { InitializeComponent(); + // Set fields Tool= tool; ToolVersion = toolVersion; Command = callBackCommand; if (tool == null) { + // tool = null for a header pnlHeader.IsVisible = true; pnlRow.IsVisible = false; IsPair = true; } else { + // Display version data pnlHeader.IsVisible = false; pnlRow.IsVisible = true; txtPlatform.Text = ToolVersion.OperatingSystem.ToString(); @@ -53,6 +76,7 @@ public VersionControl(ExternalTool tool, ExternalTools_Version toolVersion, Acti } IsPairGlobal = !IsPairGlobal; + // Colour based on version operating system if (toolVersion.OperatingSystem == ServiceLayer.CurrentOperatingSystem) { btnDownload.Foreground = ColorGreen; @@ -76,6 +100,13 @@ public VersionControl(ExternalTool tool, ExternalTools_Version toolVersion, Acti } } + + + /// + /// Download button + /// + /// + /// private void btnDownload_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { new Thread(() => @@ -85,11 +116,23 @@ private void btnDownload_Click(object? sender, Avalonia.Interactivity.RoutedEven }).Start(); } + + /// + /// Change color when mouse is over + /// + /// + /// private void pnlRow_PointerEntered(object? sender, Avalonia.Input.PointerEventArgs e) { pnlRow.Background = ColorHover; } + + /// + /// Restore color when mouse is out + /// + /// + /// private void pnlRow_PointerExited(object? sender, Avalonia.Input.PointerEventArgs e) { if (IsPair) diff --git a/ZXBSInstaller/ExternalTools.json b/ZXBSInstaller/ExternalTools.json new file mode 100644 index 0000000..7da3f9a --- /dev/null +++ b/ZXBSInstaller/ExternalTools.json @@ -0,0 +1,62 @@ +[ + { + "Id": "zxbsinstaller", + "Enabled": true, + "Name": "ZX Basic Studio Installer", + "Author": "Duefectu", + "Description": "This program, and it is used to download, install and keep all external tools and ZX Basic Studio itself up to date.", + "SupportedOperatingSystems": [ + 1, + 2, + 3 + ], + "SiteUrl": "https://github.com/boriel-basic/ZXBasicStudio", + "LicenseType": "MIT License", + "LicenceUrl": "https://raw.githubusercontent.com/boriel-basic/ZXBasicStudio/refs/heads/master/LICENSE.txt", + "VersionsUrl": "https://github.com/boriel-basic/ZXBasicStudio/releases/", + "LocalPath": "", + "FullLocalPath": null, + "DirectUpdate": true, + "Order": 0 + }, + { + "Id": "zxbasic", + "Enabled": true, + "Name": "Boriel ZX Basic Compiler", + "Author": "Boriel", + "Description": "ZXBCompiler is a ZX Spectrum BASIC cross compiler that translates ZX Spectrum BASIC code into optimized machine code, enabling faster execution and enhanced performance on ZX Spectrum systems. This tool is required to run and debug programs.", + "SupportedOperatingSystems": [ + 1, + 2, + 3 + ], + "SiteUrl": "https://boriel-basic.net", + "LicenseType": "GNU Affero General Public License v3.0", + "LicenceUrl": "https://raw.githubusercontent.com/boriel-basic/zxbasic/refs/heads/main/LICENSE.txt", + "VersionsUrl": "https://boriel.com/files/zxb/", + "LocalPath": "", + "FullLocalPath": "C:\\ZXNext\\zxbasic\\zxbc.exe", + "DirectUpdate": true, + "Order": 1 + }, + { + "Id": "zxbs", + "Enabled": true, + "Name": "ZX Basic Studio", + "Author": "Dr.Gusman, Boriel, Duefectu, AdolFITO, Hash6Iron and SirRickster", + "Description": "IDE (Integrated Development Environment) with Boriel Basic code editor, Assembler, UDGs, fonts, sprites, .tap editor, debugger, emulator, etc. This tool is optional but highly recommended.", + "SupportedOperatingSystems": [ + 1, + 2, + 3 + ], + "SiteUrl": "https://github.com/boriel-basic/ZXBasicStudio", + "LicenseType": "MIT License", + "LicenceUrl": "https://raw.githubusercontent.com/boriel-basic/ZXBasicStudio/refs/heads/master/LICENSE.txt", + "VersionsUrl": "https://github.com/boriel-basic/ZXBasicStudio/releases/", + "LocalPath": "", + "FullLocalPath": null, + "DirectUpdate": true, + "Order": 2 + } +] \ No newline at end of file diff --git a/ZXBSInstaller/MainWindow.axaml.cs b/ZXBSInstaller/MainWindow.axaml.cs index 867ac60..9959143 100644 --- a/ZXBSInstaller/MainWindow.axaml.cs +++ b/ZXBSInstaller/MainWindow.axaml.cs @@ -18,7 +18,9 @@ namespace ZXBSInstaller public partial class MainWindow : Window { - + /// + /// Set Controls.MainControl as content + /// public MainWindow() { InitializeComponent(); diff --git a/ZXBSInstaller/UITools.cs b/ZXBSInstaller/UITools.cs index becc9ae..b46ead0 100644 --- a/ZXBSInstaller/UITools.cs +++ b/ZXBSInstaller/UITools.cs @@ -12,6 +12,12 @@ namespace ZXBSInstaller { internal static class UITools { + /// + /// Load and show an image from embded resource into an Image control + /// File must be "AvaloniaResource" + /// + /// File name + /// public static void ShowImage(string fileName, Image imgControl) { try diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index 830eaa9..0efcea6 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,13 +6,14 @@ app.manifest true zxbs.ico - 1.0.0.5 + 1.0.0.6 + @@ -32,6 +33,9 @@ PreserveNewest + + PreserveNewest + diff --git a/ZXBSInstaller/version.txt b/ZXBSInstaller/version.txt index 090eef7..bdf0de9 100644 --- a/ZXBSInstaller/version.txt +++ b/ZXBSInstaller/version.txt @@ -1 +1 @@ -1.0.0.5 \ No newline at end of file +1.0.0.6 \ No newline at end of file From caac588bdd28f08f52f06c82cec1412888679bfd Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Sat, 21 Feb 2026 11:34:56 +0100 Subject: [PATCH 07/14] ShutDownMode changed --- ZXBSInstaller/App.axaml.cs | 2 ++ ZXBSInstaller/ZXBSInstaller.csproj | 2 +- ZXBSInstaller/version.txt | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ZXBSInstaller/App.axaml.cs b/ZXBSInstaller/App.axaml.cs index 022a205..97f2c25 100644 --- a/ZXBSInstaller/App.axaml.cs +++ b/ZXBSInstaller/App.axaml.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; @@ -15,6 +16,7 @@ public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { + desktop.ShutdownMode = ShutdownMode.OnMainWindowClose; desktop.MainWindow = new MainWindow(); } diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index 0efcea6..057a7af 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,7 +6,7 @@ app.manifest true zxbs.ico - 1.0.0.6 + 1.0.0.7 diff --git a/ZXBSInstaller/version.txt b/ZXBSInstaller/version.txt index bdf0de9..306209a 100644 --- a/ZXBSInstaller/version.txt +++ b/ZXBSInstaller/version.txt @@ -1 +1 @@ -1.0.0.6 \ No newline at end of file +1.0.0.7 \ No newline at end of file From 0ed9569cb046a30eeaf00a26f30d47d1cd6b5943 Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Sat, 21 Feb 2026 20:37:09 +0100 Subject: [PATCH 08/14] Removed AvaloniaDialog dependencies --- ZXBSInstaller.Log/ServiceLayer.cs | 26 +++++++++++++++-- ZXBSInstaller/Controls/MainControl.axaml | 22 ++++++++++++-- ZXBSInstaller/Controls/MainControl.axaml.cs | 32 ++++++--------------- ZXBSInstaller/MainWindow.axaml.cs | 3 -- ZXBSInstaller/Program.cs | 10 ------- ZXBSInstaller/ZXBSInstaller.csproj | 3 +- ZXBSInstaller/version.txt | 2 +- 7 files changed, 53 insertions(+), 45 deletions(-) diff --git a/ZXBSInstaller.Log/ServiceLayer.cs b/ZXBSInstaller.Log/ServiceLayer.cs index 5cb2e56..9c183cd 100644 --- a/ZXBSInstaller.Log/ServiceLayer.cs +++ b/ZXBSInstaller.Log/ServiceLayer.cs @@ -519,7 +519,11 @@ private static ExternalTools_Version[] GetBorielBasicVersions(string versionsUrl // Get all hrefs in the page according to the pattern. // The pattern is specific for the Boriel Basic repository page, which contains links to the versions in the format zxbasic-v1.2.3-win.zip var links = GetAllLinks(versionsUrl, @"]*href\s*=\s*[""']([^""']+)[""']"); - + if (links == null) + { + ShowMessage("The Boriel Basic repository could not be accessed. Please check your internet connection or try again later."); + return null; + } // Parse links extracting versions data var lst = new List(); Regex _regex = new Regex( @@ -588,6 +592,12 @@ private static ExternalTools_Version[] GetBorielZXBSVersions(string versionsUrl, { // Get all hrefs var links = GetAllLinks(versionsUrl, @"href=""([^""]+)"""); + if (links == null) + { + ShowMessage("The ZX Basic Studio repository could not be accessed. Please check your internet connection or try again later."); + return null; + } + // Get only releases links = links.Where(d => d.Contains("/boriel-basic/ZXBasicStudio/releases/tag/")).ToArray(); @@ -598,6 +608,18 @@ private static ExternalTools_Version[] GetBorielZXBSVersions(string versionsUrl, var url = link.Replace("/boriel-basic/ZXBasicStudio/releases/tag/", ""); url = $"https://github.com/boriel-basic/ZXBasicStudio/releases/expanded_assets/{url}"; var filesLinks = GetAllLinks(url, @"href=""([^""]+)"""); + if (links == null) + { + if (installer) + { + ShowMessage("The ZXBSInstaller repository could not be accessed. Please check your internet connection or try again later."); + } + else + { + ShowMessage("The ZX Basic Studio repository could not be accessed. Please check your internet connection or try again later."); + } + return null; + } foreach (var fl in filesLinks) { if (fl.Contains("download")) @@ -844,7 +866,7 @@ private static ExternalTools_Version GetZXBSVersion(string exePath) if (!File.Exists(fileName)) { // no version.txt file - if(File.Exists(Path.Combine(exePath, "ZXBasicStudio.exe")) + if (File.Exists(Path.Combine(exePath, "ZXBasicStudio.exe")) || File.Exists(Path.Combine(exePath, "ZXBasicStudio"))) { // return "OLD version" diff --git a/ZXBSInstaller/Controls/MainControl.axaml b/ZXBSInstaller/Controls/MainControl.axaml index b2b6095..765e229 100644 --- a/ZXBSInstaller/Controls/MainControl.axaml +++ b/ZXBSInstaller/Controls/MainControl.axaml @@ -79,10 +79,26 @@ - + - - + + + + + + + + + + + + diff --git a/ZXBSInstaller/Controls/MainControl.axaml.cs b/ZXBSInstaller/Controls/MainControl.axaml.cs index e582e36..3b0e09e 100644 --- a/ZXBSInstaller/Controls/MainControl.axaml.cs +++ b/ZXBSInstaller/Controls/MainControl.axaml.cs @@ -6,9 +6,6 @@ using Avalonia.Media; using Avalonia.Threading; using Avalonia.VisualTree; -using MsBox.Avalonia; -using MsBox.Avalonia.Dto; -using MsBox.Avalonia.Enums; using System; using System.Collections.Generic; using System.Data; @@ -92,16 +89,8 @@ private void ShowMessage(string message) Dispatcher.UIThread.Post(() => { HideStatusPanel(); - var box = MessageBoxManager.GetMessageBoxStandard(new MessageBoxStandardParams - { - ButtonDefinitions = ButtonEnum.Ok, - ContentTitle = "ZX Basic Studio Installer", - ContentMessage = message, - Icon = MsBox.Avalonia.Enums.Icon.Info, - WindowIcon = ((Window)this.VisualRoot).Icon, - WindowStartupLocation = WindowStartupLocation.CenterOwner - }); - box.ShowAsPopupAsync(this); + txtModalMessage.Text = message; + pnlModal.IsVisible = true; }); } @@ -128,17 +117,7 @@ private void GetExternalTools() if (tools == null) { // Error! - var box = MessageBoxManager.GetMessageBoxStandard(new MessageBoxStandardParams - { - ButtonDefinitions = ButtonEnum.Ok, - ContentTitle = "ERROR", - ContentMessage = "Error retrieving the list of tools, please check your Internet connection.\r\nIt may be a temporary server error, report the error to duefectucorp@gmail.com and try again later.", - Icon = MsBox.Avalonia.Enums.Icon.Error, - WindowIcon = ((Window)this.VisualRoot).Icon, - WindowStartupLocation = WindowStartupLocation.CenterOwner - }); - box.ShowAsPopupAsync(this); - + ShowMessage("Error retrieving the list of tools, please check your Internet connection.\r\nIt may be a temporary server error, report the error to duefectucorp@gmail.com and try again later."); } else { @@ -531,4 +510,9 @@ private void btnCancel_Click(object? sender, RoutedEventArgs e) { ServiceLayer.Cancel = true; } + + private void btnModalClose_Click(object? sender, RoutedEventArgs e) + { + pnlModal.IsVisible = false; + } } \ No newline at end of file diff --git a/ZXBSInstaller/MainWindow.axaml.cs b/ZXBSInstaller/MainWindow.axaml.cs index 9959143..9d236a9 100644 --- a/ZXBSInstaller/MainWindow.axaml.cs +++ b/ZXBSInstaller/MainWindow.axaml.cs @@ -2,9 +2,6 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Interactivity; using Avalonia.Threading; -using MsBox.Avalonia; -using MsBox.Avalonia.Dto; -using MsBox.Avalonia.Enums; using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/ZXBSInstaller/Program.cs b/ZXBSInstaller/Program.cs index e73b612..7adff00 100644 --- a/ZXBSInstaller/Program.cs +++ b/ZXBSInstaller/Program.cs @@ -16,16 +16,6 @@ internal class Program [STAThread] public static void Main(string[] args) { - var assembly = System.Reflection.Assembly.GetEntryAssembly(); - var version = assembly.GetName().Version; - Version = $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}"; - - if (args.Contains("--version")) - { - Console.WriteLine($"{Version}"); - return; - } - BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index 057a7af..841e701 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,7 +6,7 @@ app.manifest true zxbs.ico - 1.0.0.7 + 1.0.0.8 @@ -53,7 +53,6 @@ - diff --git a/ZXBSInstaller/version.txt b/ZXBSInstaller/version.txt index 306209a..7a76ec4 100644 --- a/ZXBSInstaller/version.txt +++ b/ZXBSInstaller/version.txt @@ -1 +1 @@ -1.0.0.7 \ No newline at end of file +1.0.0.8 \ No newline at end of file From 6a9121a382016f9ed109c0e995a647ce046cde6d Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Sun, 22 Feb 2026 00:40:32 +0100 Subject: [PATCH 09/14] v1.0.0-beta9 Bug retrieving external tools --- ZXBSInstaller/Controls/MainControl.axaml.cs | 50 ++++++++++----------- ZXBSInstaller/ZXBSInstaller.csproj | 2 +- ZXBSInstaller/version.txt | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/ZXBSInstaller/Controls/MainControl.axaml.cs b/ZXBSInstaller/Controls/MainControl.axaml.cs index 3b0e09e..4c95441 100644 --- a/ZXBSInstaller/Controls/MainControl.axaml.cs +++ b/ZXBSInstaller/Controls/MainControl.axaml.cs @@ -111,20 +111,17 @@ private void GetExternalTools() // Get tools var tools = ServiceLayer.GetExternalTools(); - Dispatcher.UIThread.Post(() => + HideStatusPanel(); + if (tools == null) { - HideStatusPanel(); - if (tools == null) - { - // Error! - ShowMessage("Error retrieving the list of tools, please check your Internet connection.\r\nIt may be a temporary server error, report the error to duefectucorp@gmail.com and try again later."); - } - else - { - // Show tools - ShowData(); - } - }); + // Error! + ShowMessage("Error retrieving the list of tools, please check your Internet connection.\r\nIt may be a temporary server error, report the error to duefectucorp@gmail.com and try again later."); + } + else + { + // Show tools + ShowData(); + } } @@ -133,19 +130,22 @@ private void GetExternalTools() /// private void ShowData() { - toolItemControls.Clear(); - var tools = ServiceLayer.ExternalTools; - - pnlTools.Children.Clear(); - foreach (var tool in tools) + Dispatcher.UIThread.Post(() => { - // Create on ToolItemControl foreach tool - var control = new ToolItemControl(tool, Command_Received); - toolItemControls.Add(control); - pnlTools.Children.Add(control); - } - // Update summary area - UpdateSummary(); + toolItemControls.Clear(); + var tools = ServiceLayer.ExternalTools; + + pnlTools.Children.Clear(); + foreach (var tool in tools) + { + // Create on ToolItemControl foreach tool + var control = new ToolItemControl(tool, Command_Received); + toolItemControls.Add(control); + pnlTools.Children.Add(control); + } + // Update summary area + UpdateSummary(); + }); } diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index 841e701..d029898 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,7 +6,7 @@ app.manifest true zxbs.ico - 1.0.0.8 + 1.0.0.9 diff --git a/ZXBSInstaller/version.txt b/ZXBSInstaller/version.txt index 7a76ec4..d1b2fe5 100644 --- a/ZXBSInstaller/version.txt +++ b/ZXBSInstaller/version.txt @@ -1 +1 @@ -1.0.0.8 \ No newline at end of file +1.0.0.9 \ No newline at end of file From 873529c0ada1cdb09d40a2ed19ab15e8939c6a36 Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Sun, 22 Feb 2026 10:25:50 +0100 Subject: [PATCH 10/14] v1.0.0-beta10 List of tools from embedded resource --- ZXBSInstaller.Log/ServiceLayer.cs | 12 +++++++----- ZXBSInstaller/Controls/MainControl.axaml.cs | 7 ++++--- ZXBSInstaller/UITools.cs | 21 +++++++++++++++++++++ ZXBSInstaller/ZXBSInstaller.csproj | 8 ++++---- ZXBSInstaller/version.txt | 2 +- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/ZXBSInstaller.Log/ServiceLayer.cs b/ZXBSInstaller.Log/ServiceLayer.cs index 9c183cd..726182b 100644 --- a/ZXBSInstaller.Log/ServiceLayer.cs +++ b/ZXBSInstaller.Log/ServiceLayer.cs @@ -302,19 +302,21 @@ public static void OpenUrlInBrowser(string url) /// /// Retrieves all external tools configured for use with the application. /// + /// Json string with the external tools information /// An array of objects representing the available external tools. The array is empty /// if no external tools are configured or can download the config file. - public static ExternalTool[] GetExternalTools() + public static ExternalTool[] SetExternalTools(string json) { try { UpdateStatus?.Invoke("Retrieving external tools information...", 5); - var json = File.ReadAllText("ExternalTools.json"); - //// Download the external tools list from the configured URL - //using var httpClient = new HttpClient(); - //string json = httpClient.GetStringAsync(GeneralConfig.ToolsListURL).GetAwaiter().GetResult(); var tools = JsonSerializer.Deserialize(json); + if (tools == null) + { + ShowMessage("ERROR, unable to obtain the list of external tools. Download and install a new version of ZXBSInstaller."); + return null; + } int max = tools.Length; int prg = 10; diff --git a/ZXBSInstaller/Controls/MainControl.axaml.cs b/ZXBSInstaller/Controls/MainControl.axaml.cs index 4c95441..87c1805 100644 --- a/ZXBSInstaller/Controls/MainControl.axaml.cs +++ b/ZXBSInstaller/Controls/MainControl.axaml.cs @@ -109,13 +109,14 @@ private void GetExternalTools() }); // Get tools - var tools = ServiceLayer.GetExternalTools(); + var json = UITools.GetTextResource("ExternalTools.json"); + var tools = ServiceLayer.SetExternalTools(json); HideStatusPanel(); if (tools == null) { - // Error! - ShowMessage("Error retrieving the list of tools, please check your Internet connection.\r\nIt may be a temporary server error, report the error to duefectucorp@gmail.com and try again later."); + // No tools, no way! + ExitApp(); } else { diff --git a/ZXBSInstaller/UITools.cs b/ZXBSInstaller/UITools.cs index b46ead0..e8b75ca 100644 --- a/ZXBSInstaller/UITools.cs +++ b/ZXBSInstaller/UITools.cs @@ -31,5 +31,26 @@ public static void ShowImage(string fileName, Image imgControl) { } } + + + /// + /// Read a text file from embedded resource and return the content as string + /// + /// FileName + /// + public static string GetTextResource(string fileName) + { + try + { + var uri = new Uri($"avares://ZXBSInstaller/{fileName}"); + var asset = AssetLoader.Open(uri); + var txt= new StreamReader(asset).ReadToEnd(); + return txt; + } + catch (Exception ex) + { + return null; + } + } } } diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index d029898..c3f6a6f 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,7 +6,7 @@ app.manifest true zxbs.ico - 1.0.0.9 + 1.0.0.10 @@ -33,9 +33,9 @@ PreserveNewest - - PreserveNewest - + + + diff --git a/ZXBSInstaller/version.txt b/ZXBSInstaller/version.txt index d1b2fe5..8a6e6f2 100644 --- a/ZXBSInstaller/version.txt +++ b/ZXBSInstaller/version.txt @@ -1 +1 @@ -1.0.0.9 \ No newline at end of file +1.0.0.10 \ No newline at end of file From 4cb26cba73127e724fe300fd8402d6196a8f64d5 Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Sun, 22 Feb 2026 11:56:27 +0100 Subject: [PATCH 11/14] v1.0.0-beta11 Bug in compiler lastest version --- ZXBSInstaller.Log/ServiceLayer.cs | 44 ++++++++++++++++++++++++------ ZXBSInstaller/ZXBSInstaller.csproj | 2 +- ZXBSInstaller/version.txt | 2 +- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/ZXBSInstaller.Log/ServiceLayer.cs b/ZXBSInstaller.Log/ServiceLayer.cs index 726182b..e3b2774 100644 --- a/ZXBSInstaller.Log/ServiceLayer.cs +++ b/ZXBSInstaller.Log/ServiceLayer.cs @@ -35,6 +35,10 @@ public static class ServiceLayer /// public static OperatingSystems CurrentOperatingSystem = OperatingSystems.All; /// + /// True if the computer is a Mac + /// + public static bool IsMac=false; + /// /// Used to cancel the current operation. It is set to true when the user clicks the cancel button and it is checked in all long operations to stop them if it is true. /// public static bool Cancel = false; @@ -105,6 +109,7 @@ public static bool Initialize( } else if (OperatingSystem.IsMacOS()) { + IsMac = true; if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) { CurrentOperatingSystem = OperatingSystems.MacOS_arm64; @@ -344,18 +349,39 @@ public static ExternalTool[] SetExternalTools(string json) // Set latest version if (GeneralConfig.OnlyStableVersions) { - tool.LatestVersion = tool.Versions. - Where(d => d.OperatingSystem == CurrentOperatingSystem && - d.BetaNumber == 0). - OrderByDescending(d => d.VersionNumber). - FirstOrDefault(); + if (tool.Id == "zxbasic" && IsMac) + { + tool.LatestVersion = tool.Versions. + Where(d => d.OperatingSystem == OperatingSystems.MacOS && + d.BetaNumber == 0). + OrderByDescending(d => d.VersionNumber). + FirstOrDefault(); + } + else + { + tool.LatestVersion = tool.Versions. + Where(d => d.OperatingSystem == CurrentOperatingSystem && + d.BetaNumber == 0). + OrderByDescending(d => d.VersionNumber). + FirstOrDefault(); + } } if (tool.LatestVersion == null || !GeneralConfig.OnlyStableVersions) { - tool.LatestVersion = tool.Versions. - Where(d => d.OperatingSystem == CurrentOperatingSystem). - OrderByDescending(d => d.VersionNumber). - FirstOrDefault(); + if (tool.Id == "zxbasic" && IsMac) + { + tool.LatestVersion = tool.Versions. + Where(d => d.OperatingSystem == OperatingSystems.MacOS). + OrderByDescending(d => d.VersionNumber). + FirstOrDefault(); + } + else + { + tool.LatestVersion = tool.Versions. + Where(d => d.OperatingSystem == CurrentOperatingSystem). + OrderByDescending(d => d.VersionNumber). + FirstOrDefault(); + } } // Path for first versions of ZXBSInstalller diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index c3f6a6f..42e73d5 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,7 +6,7 @@ app.manifest true zxbs.ico - 1.0.0.10 + 1.0.0.11 diff --git a/ZXBSInstaller/version.txt b/ZXBSInstaller/version.txt index 8a6e6f2..6cb4627 100644 --- a/ZXBSInstaller/version.txt +++ b/ZXBSInstaller/version.txt @@ -1 +1 @@ -1.0.0.10 \ No newline at end of file +1.0.0.11 \ No newline at end of file From 48a352be82c22a7d94daf81d43daf5553ee74a18 Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Sun, 22 Feb 2026 12:24:28 +0100 Subject: [PATCH 12/14] Bug retrieving ZXBS releases from GitHub --- ZXBSInstaller.Log/ServiceLayer.cs | 2 +- ZXBSInstaller/ZXBSInstaller.csproj | 2 +- ZXBSInstaller/version.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ZXBSInstaller.Log/ServiceLayer.cs b/ZXBSInstaller.Log/ServiceLayer.cs index e3b2774..23e1e2a 100644 --- a/ZXBSInstaller.Log/ServiceLayer.cs +++ b/ZXBSInstaller.Log/ServiceLayer.cs @@ -636,7 +636,7 @@ private static ExternalTools_Version[] GetBorielZXBSVersions(string versionsUrl, var url = link.Replace("/boriel-basic/ZXBasicStudio/releases/tag/", ""); url = $"https://github.com/boriel-basic/ZXBasicStudio/releases/expanded_assets/{url}"; var filesLinks = GetAllLinks(url, @"href=""([^""]+)"""); - if (links == null) + if (filesLinks == null) { if (installer) { diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index 42e73d5..7ae2236 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,7 +6,7 @@ app.manifest true zxbs.ico - 1.0.0.11 + 1.0.0.12 diff --git a/ZXBSInstaller/version.txt b/ZXBSInstaller/version.txt index 6cb4627..35f2a39 100644 --- a/ZXBSInstaller/version.txt +++ b/ZXBSInstaller/version.txt @@ -1 +1 @@ -1.0.0.11 \ No newline at end of file +1.0.0.12 \ No newline at end of file From c190cdc4d3e5f84633a364cd63d494b013f14193 Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Sun, 22 Feb 2026 14:48:59 +0100 Subject: [PATCH 13/14] =?UTF-8?q?v1.0.0-beta13=20Varios=20cambios=20en=20l?= =?UTF-8?q?a=20ficha=20de=20herramientas=20-=20A=C3=B1adida=20m=C3=A1s=20i?= =?UTF-8?q?nformaci=C3=B3n=20-=20A=C3=B1adido=20bot=C3=B3n=20abrir=20sitio?= =?UTF-8?q?=20-=20Modificado=20el=20dise=C3=B1o=20de=20los=20botones?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 +++++++- ZXBSInstaller.Log/ServiceLayer.cs | 14 ++++++++-- ZXBSInstaller/Assets/history.svg | 7 +++++ ZXBSInstaller/Assets/internet.svg | 7 +++++ ZXBSInstaller/Controls/ToolItemControl.axaml | 26 +++++++++++++++---- .../Controls/ToolItemControl.axaml.cs | 9 ++++++- ZXBSInstaller/ExternalTools.json | 4 +-- ZXBSInstaller/ZXBSInstaller.csproj | 6 ++++- ZXBSInstaller/version.txt | 2 +- 9 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 ZXBSInstaller/Assets/history.svg create mode 100644 ZXBSInstaller/Assets/internet.svg diff --git a/README.md b/README.md index 4aac395..ff31161 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Documentation will come in the near future, take a look at the book [Boriel Basi Have fun! -## Credits +## ZX Basic Studio Team - Development team: - El Dr. Gusman - Boriel @@ -19,6 +19,11 @@ Have fun! - AdolFITO - HashIron - SirRickster +- Testers: + - AbenZaX + - Pedro Tomás (Pere) + +## Credits - Icons from [SVG REPO](https://www.svgrepo.com/): - Blivesta in MIT License - Dazzle Ui in CC Attribution License @@ -36,3 +41,7 @@ Have fun! - Neuicons in MIT License via - Dazzle Ui in CC Attribution License - Siemens in MIT License + - Zest in MIT License + - Ananthanath A X Kalaiism in PD License + + diff --git a/ZXBSInstaller.Log/ServiceLayer.cs b/ZXBSInstaller.Log/ServiceLayer.cs index 23e1e2a..b3cbc07 100644 --- a/ZXBSInstaller.Log/ServiceLayer.cs +++ b/ZXBSInstaller.Log/ServiceLayer.cs @@ -776,10 +776,15 @@ private static string[] GetAllLinks(string url, string pattern) int retries = 5; while (retries-- > 0) { - using (HttpClient client = new HttpClient(handler)) + try { - html = client.GetStringAsync(url).GetAwaiter().GetResult(); + using (HttpClient client = new HttpClient(handler)) + { + client.Timeout = TimeSpan.FromSeconds(20); + html = client.GetStringAsync(url).GetAwaiter().GetResult(); + } } + catch { } if (!string.IsNullOrEmpty(html)) { break; @@ -1084,6 +1089,11 @@ public static void DownloadAndInstallTool(ExternalTool tool, ExternalTools_Versi return; } + if(tool.Id == "zxbsinstaller") + { + ShowStatusPanel($"After installing or updating ZXBSInstaller, run this program from {tool.LocalPath}."); + } + ShowStatusPanel($"Downloading {tool.Name} version {version.Version}..."); // Download path diff --git a/ZXBSInstaller/Assets/history.svg b/ZXBSInstaller/Assets/history.svg new file mode 100644 index 0000000..11c4484 --- /dev/null +++ b/ZXBSInstaller/Assets/history.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/ZXBSInstaller/Assets/internet.svg b/ZXBSInstaller/Assets/internet.svg new file mode 100644 index 0000000..e25dda7 --- /dev/null +++ b/ZXBSInstaller/Assets/internet.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/ZXBSInstaller/Controls/ToolItemControl.axaml b/ZXBSInstaller/Controls/ToolItemControl.axaml index e7ca840..cc46644 100644 --- a/ZXBSInstaller/Controls/ToolItemControl.axaml +++ b/ZXBSInstaller/Controls/ToolItemControl.axaml @@ -2,23 +2,39 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - Width="360" Height="150" + xmlns:svg="using:Avalonia.Svg.Skia" + Width="360" Height="180" x:Class="ZXBSInstaller.Controls.ToolItemControl" FontSize="12"> Name - Description - + + + + + Description + + + - Path + Path Installed version: Latest version: - + + + + diff --git a/ZXBSInstaller/Controls/ToolItemControl.axaml.cs b/ZXBSInstaller/Controls/ToolItemControl.axaml.cs index a8b277a..3fb33b0 100644 --- a/ZXBSInstaller/Controls/ToolItemControl.axaml.cs +++ b/ZXBSInstaller/Controls/ToolItemControl.axaml.cs @@ -3,6 +3,7 @@ using Avalonia.Markup.Xaml; using Avalonia.Media; using System; +using ZXBSInstaller.Log; using ZXBSInstaller.Log.Neg; namespace ZXBSInstaller.Controls; @@ -50,7 +51,8 @@ public ToolItemControl(ExternalTool tool, Action callBackCommand txtName.Text = tool.Name; txtDescription.Text = tool.Description; txtPath.Text = "Path: " + tool.LocalPath; - + txtLicense.Text = "Licence: " + tool.LicenseType; + txtAuthor.Text = "Author(s): " + tool.Author; // Set chkSelect status IsSelected = tool.UpdateNeeded; tool.IsSelected = IsSelected; @@ -109,4 +111,9 @@ private void btnAllVersions_Click(object? sender, Avalonia.Interactivity.RoutedE { Command(ExternalTool.Id, "VERSIONS"); } + + private void btnViewSite_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + { + ServiceLayer.OpenUrlInBrowser(ExternalTool.SiteUrl); + } } \ No newline at end of file diff --git a/ZXBSInstaller/ExternalTools.json b/ZXBSInstaller/ExternalTools.json index 7da3f9a..45ffa91 100644 --- a/ZXBSInstaller/ExternalTools.json +++ b/ZXBSInstaller/ExternalTools.json @@ -24,7 +24,7 @@ "Enabled": true, "Name": "Boriel ZX Basic Compiler", "Author": "Boriel", - "Description": "ZXBCompiler is a ZX Spectrum BASIC cross compiler that translates ZX Spectrum BASIC code into optimized machine code, enabling faster execution and enhanced performance on ZX Spectrum systems. This tool is required to run and debug programs.", + "Description": "ZXBCompiler is a BORIEL BASIC cross compiler tool. It's a required tool.'", "SupportedOperatingSystems": [ 1, 2, @@ -44,7 +44,7 @@ "Enabled": true, "Name": "ZX Basic Studio", "Author": "Dr.Gusman, Boriel, Duefectu, AdolFITO, Hash6Iron and SirRickster", - "Description": "IDE (Integrated Development Environment) with Boriel Basic code editor, Assembler, UDGs, fonts, sprites, .tap editor, debugger, emulator, etc. This tool is optional but highly recommended.", + "Description": "IDE (Integrated Development Environment) with code editor, Assembler, UDGs, fonts, sprites, .tap editor, debugger, emulator, etc.", "SupportedOperatingSystems": [ 1, 2, diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index 7ae2236..6f07072 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,11 +6,13 @@ app.manifest true zxbs.ico - 1.0.0.12 + 1.0.0.13 + + @@ -18,7 +20,9 @@ + + diff --git a/ZXBSInstaller/version.txt b/ZXBSInstaller/version.txt index 35f2a39..b13c9eb 100644 --- a/ZXBSInstaller/version.txt +++ b/ZXBSInstaller/version.txt @@ -1 +1 @@ -1.0.0.12 \ No newline at end of file +1.0.0.13 \ No newline at end of file From 8edbf26be4964ab986fd264e2f2a5577bc24443f Mon Sep 17 00:00:00 2001 From: Juan Segura Date: Mon, 23 Feb 2026 16:28:32 +0100 Subject: [PATCH 14/14] v1.0.0 estable --- README.md | 2 ++ ZXBSInstaller/ZXBSInstaller.csproj | 2 +- ZXBSInstaller/version.txt | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ff31161..31be994 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ Have fun! - Testers: - AbenZaX - Pedro Tomás (Pere) + - Jose Daniel Fernandez Santos (Fenix) + - Yoruguaman ## Credits - Icons from [SVG REPO](https://www.svgrepo.com/): diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj index 6f07072..fa2d5ea 100644 --- a/ZXBSInstaller/ZXBSInstaller.csproj +++ b/ZXBSInstaller/ZXBSInstaller.csproj @@ -6,7 +6,7 @@ app.manifest true zxbs.ico - 1.0.0.13 + 1.0.0.0 diff --git a/ZXBSInstaller/version.txt b/ZXBSInstaller/version.txt index b13c9eb..bd2666a 100644 --- a/ZXBSInstaller/version.txt +++ b/ZXBSInstaller/version.txt @@ -1 +1 @@ -1.0.0.13 \ No newline at end of file +1.0.0.0 \ No newline at end of file