diff --git a/Universal Updater/Dakirby309-Windows-8-Metro-Other-Update-Metro.256.ico b/Universal Updater/Dakirby309-Windows-8-Metro-Other-Update-Metro.256.ico new file mode 100644 index 0000000..3d5dc71 Binary files /dev/null and b/Universal Updater/Dakirby309-Windows-8-Metro-Other-Update-Metro.256.ico differ diff --git a/Universal Updater/DownloadPackages.cs b/Universal Updater/DownloadPackages.cs index 59a0557..57dbea6 100644 --- a/Universal Updater/DownloadPackages.cs +++ b/Universal Updater/DownloadPackages.cs @@ -8,6 +8,7 @@ using System.Net.NetworkInformation; using System.Security.Cryptography.X509Certificates; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Universal_Updater @@ -78,7 +79,7 @@ public static bool OnlineUpdate(string updateBuild) return isFeatureInstalled; } - public static void OfflineUpdate(string[] folderFiles, string pushFeature) + public static bool OfflineUpdate(string[] folderFiles, string pushFeature) { filterPackages: filteredPackages.Clear(); @@ -130,8 +131,8 @@ public static void OfflineUpdate(string[] folderFiles, string pushFeature) // Allow user to push all package if he want Program.WriteLine("\nFilter packages options: ", ConsoleColor.Blue); - Program.WriteLine("1. Filter packages (Recommended)", ConsoleColor.Green); - Program.WriteLine("2. Push all", ConsoleColor.DarkYellow); + Program.WriteLine("1. Filter packages", ConsoleColor.Green); + Program.WriteLine("2. Include all", ConsoleColor.DarkYellow); Program.Write("Choice: ", ConsoleColor.Magenta); ConsoleKeyInfo packagesFilterAction; do @@ -255,30 +256,56 @@ public static void OfflineUpdate(string[] folderFiles, string pushFeature) if (filteredPackages.Count > 0) { var testCabFile = filteredPackages.FirstOrDefault(); + var certificateIssuer = ""; + var certificateDate = ""; + var certificateExpireDate = ""; + var dtFmt = ""; + bool isTestSigned = false; try { X509Certificate cert = X509Certificate.CreateFromSignedFile(testCabFile); + certificateDate = cert.GetEffectiveDateString(); + certificateExpireDate = cert.GetExpirationDateString(); + certificateIssuer = cert.Issuer; + Match m = Regex.Match(certificateIssuer, @"CN=(.*?)(?=,)"); + if (m.Success) + { + certificateIssuer = m.Groups[1].Value; + } + + Program.WriteLine("\n[CERTIFICATE]", ConsoleColor.Green); + Program.Write("Issuer : ", ConsoleColor.DarkGray); + Program.WriteLine(certificateIssuer, ConsoleColor.DarkCyan); + Program.Write("Date : ", ConsoleColor.DarkGray); + Program.Write(certificateDate.ToDate(ref dtFmt).Value.ToString("d"), ConsoleColor.DarkGray); + Program.Write(" - ", ConsoleColor.DarkGray); + Program.WriteLine(certificateExpireDate.ToDate(ref dtFmt).Value.ToString("d"), ConsoleColor.DarkGray); + Program.Write("Type : ", ConsoleColor.DarkGray); + // Currently we do basic check using [Issuer] // Test signed mostly contain `Development` or `Test` - // Production has mostly `Microsoft Code Signing` - Program.Write("Packages signature may "); - var hasDevelopmentKeyword = cert.Issuer.IndexOf("Development", StringComparison.OrdinalIgnoreCase) >= 0; - var hasTestKeyword = cert.Issuer.IndexOf("Test", StringComparison.OrdinalIgnoreCase) >= 0; - var hasSigningKeyword = cert.Issuer.IndexOf("Microsoft Code Signing", StringComparison.OrdinalIgnoreCase) >= 0; - if ((hasDevelopmentKeyword || hasTestKeyword) && !hasSigningKeyword) + var hasDevelopmentKeyword = certificateIssuer.IndexOf("Development", StringComparison.OrdinalIgnoreCase) >= 0; + var hasTestKeyword = certificateIssuer.IndexOf("Test", StringComparison.OrdinalIgnoreCase) >= 0; + if ((hasDevelopmentKeyword || hasTestKeyword)) { - Program.WriteLine("test signed.", ConsoleColor.DarkYellow); + Program.Write("Test signed", ConsoleColor.DarkYellow); + Program.WriteLine(" (Please double check)", ConsoleColor.DarkGray); + isTestSigned = true; } else { - Program.WriteLine("production signed.", ConsoleColor.Green); + Program.Write("Production signed", ConsoleColor.Green); + Program.WriteLine(" (Please double check)", ConsoleColor.DarkGray); } } catch (Exception ex) { - } + + DateTime? formatedDate = null; + var expectedDateFormat = ""; + var dateModifiedString = ""; try { // Not sure how this works, but tested on Windows 11 and seems fine @@ -288,39 +315,44 @@ public static void OfflineUpdate(string[] folderFiles, string pushFeature) foreach (var item in cabFolder.Items()) { - var dateModifiedString = (string)cabFolder.GetDetailsOf(item, 3); + dateModifiedString = (string)cabFolder.GetDetailsOf(item, 3); // Fix possible encoding crap byte[] bytes = Encoding.Default.GetBytes(dateModifiedString); dateModifiedString = Encoding.UTF8.GetString(bytes).Replace("?", ""); - DateTime? formatedDate = dateModifiedString.ToDate(null); - - if (formatedDate.HasValue) - { - var dateOnly = formatedDate.Value.Date; - Program.Write("\nIf you are updating with ", ConsoleColor.DarkYellow); - Program.Write("(Test Signed)", ConsoleColor.Blue); - Program.WriteLine(" packages,", ConsoleColor.DarkYellow); - Program.WriteLine("set your device to the date below", ConsoleColor.DarkYellow); - var datePlusTwoDays = dateOnly.AddDays(2); - Program.Write("Date: " + datePlusTwoDays.ToString("d"), ConsoleColor.Green); - Program.WriteLine(" (Packages date: " + dateOnly.ToString("d") + ")", ConsoleColor.DarkGray); - } - else - { - Program.WriteLine("\nCould not parse date: " + dateModifiedString, ConsoleColor.DarkGray); - Program.WriteLine("(Ignore this if packages are not test signed)", ConsoleColor.DarkGray); - } + formatedDate = dateModifiedString.ToDate(ref expectedDateFormat); break; } } catch (Exception ex) { + } + + if (formatedDate != null && formatedDate.HasValue) + { + var dateOnly = formatedDate.Value.Date; + Program.Write("Files : " + dateOnly.ToString("d"), ConsoleColor.DarkGray); + var datePlusTwoDays = dateOnly.AddDays(2); + Program.WriteLine($" ({dtFmt})", ConsoleColor.DarkGray); + + if (isTestSigned) + { + // Show this red label if we expect that packages are test signed + Program.Write("\n[IMPORTANT] ", ConsoleColor.DarkRed); + Program.Write("\nPackages expected to be ", ConsoleColor.DarkYellow); + Program.WriteLine("(Test Signed)", ConsoleColor.Blue); + Program.WriteLine("Set your device to the required date", ConsoleColor.DarkYellow); + } + } + else + { + Program.WriteLine("\nCould not parse date: " + dateModifiedString, ConsoleColor.DarkGray); + Program.WriteLine("(Ignore this if packages are not test signed)", ConsoleColor.DarkGray); } Program.Write("\nTotal expected packages: ", ConsoleColor.DarkGray); Program.WriteLine(filteredPackages.Count.ToString(), ConsoleColor.DarkYellow); - Program.WriteLine("1. Push packages"); + Program.WriteLine("1. Push packages", ConsoleColor.Green); Program.WriteLine("2. Retry"); Program.Write("Choice: ", ConsoleColor.Magenta); ConsoleKeyInfo packagesAction; @@ -363,11 +395,16 @@ public static void OfflineUpdate(string[] folderFiles, string pushFeature) Program.WriteLine(""); goto filterPackages; } + else + { + return false; + } } + return true; } - public static async Task DownloadUpdate(string update) + public static async Task DownloadUpdate(string update) { WebClient client = new WebClient(); Process downloadProcess = new Process(); @@ -413,6 +450,8 @@ public static async Task DownloadUpdate(string update) while (fileSize != new FileInfo($@"{Environment.CurrentDirectory}\{GetDeviceInfo.SerialNumber[0]}\Packages\{downloadFile.LocalPath.Split('/').Last()}").Length); } } + + return true; } private static void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs downloadProgressChangedEventArgs) @@ -454,28 +493,15 @@ public static bool shouldSkip(string package, bool onlinePackage = false) } public static class Extensions { - /// Extension method parsing a date string to a DateTime? - /// - /// - /// The date string to parse - /// dateFmt is optional and allows to pass - /// a parsing pattern array or one or more patterns passed - /// as string parameters - /// Parsed DateTime or null - public static DateTime? ToDate(this string dateTimeStr, params string[] dateFmt) + public static DateTime? ToDate(this string dateTimeStr, ref string fmtOut) { - // example: var dt = "2011-03-21 13:26".ToDate(new string[]{"yyyy-MM-dd HH:mm", - // "M/d/yyyy h:mm:ss tt"}); - // or simpler: - // var dt = "2011-03-21 13:26".ToDate("yyyy-MM-dd HH:mm", "M/d/yyyy h:mm:ss tt"); const DateTimeStyles style = DateTimeStyles.AllowWhiteSpaces; - if (dateFmt == null) - { - var dateInfo = System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat; - dateFmt = dateInfo.GetAllDateTimePatterns(); - } - var result = DateTime.TryParseExact(dateTimeStr, dateFmt, CultureInfo.InvariantCulture, - style, out var dt) ? dt : null as DateTime?; + + DateTime? result = null; + CultureInfo currentCulture = CultureInfo.CurrentCulture; + fmtOut = currentCulture.DateTimeFormat.ShortDatePattern; + result = DateTime.TryParse(dateTimeStr, CultureInfo.InvariantCulture, + style, out var dt) ? dt : null as DateTime?; return result; } } diff --git a/Universal Updater/GetDeviceInfo.cs b/Universal Updater/GetDeviceInfo.cs index 974fad3..a4484f2 100644 --- a/Universal Updater/GetDeviceInfo.cs +++ b/Universal Updater/GetDeviceInfo.cs @@ -30,7 +30,7 @@ public static int GetLog() getDeviceInfoProcess.StartInfo.UseShellExecute = false; getDeviceInfoProcess.Start(); // Better to set timeout and retry? - if (getDeviceInfoProcess.WaitForExit(15000)) + if (getDeviceInfoProcess.WaitForExit(30000)) { return getDeviceInfoProcess.ExitCode; } diff --git a/Universal Updater/Program.cs b/Universal Updater/Program.cs index 7298a02..5b4ce3d 100644 --- a/Universal Updater/Program.cs +++ b/Universal Updater/Program.cs @@ -93,6 +93,12 @@ static async void StartUpdater() { string formattedErrorCode = "0x" + exitCode.ToString("X"); ConsoleStyle($"Cannot read device info ({formattedErrorCode}), retrying . . .", 0, ConsoleColor.Red); + if(formattedErrorCode == "0x8000FFFF") + { + // Usually appear if device is not connected + WriteLine(""); + WriteLine("Ensure the device is connected properly", ConsoleColor.Red); + } // Give user more time to read and copy error await Task.Delay(3000); } @@ -127,6 +133,8 @@ static async void StartUpdater() WriteLine("\nAVAILABLE UPDATES", ConsoleColor.DarkGray); WriteLine(availableUpdates); + var packagesReady = false; + Write("Choice: ", ConsoleColor.Magenta); if (CheckForUpdates.Choice == 7) { @@ -221,7 +229,7 @@ static async void StartUpdater() } WriteLine("\nPREPARING PACKAGES", ConsoleColor.DarkGray); - DownloadPackages.OfflineUpdate(folderFiles, featureChoice.KeyChar.ToString().ToUpper()); + packagesReady = DownloadPackages.OfflineUpdate(folderFiles, featureChoice.KeyChar.ToString().ToUpper()); } else { @@ -280,27 +288,34 @@ static async void StartUpdater() } ConsoleStyle("\nDOWNLOADING PACKAGES", 1, ConsoleColor.DarkGray); - await DownloadPackages.DownloadUpdate(updateStructure[CheckForUpdates.Choice - 1, Convert.ToInt32(Program.selectedUpdate.KeyChar.ToString()) - 1]); + packagesReady = await DownloadPackages.DownloadUpdate(updateStructure[CheckForUpdates.Choice - 1, Convert.ToInt32(Program.selectedUpdate.KeyChar.ToString()) - 1]); } - Write("\nPUSHING PACKAGES", ConsoleColor.DarkGray); - PushPackages.StartUpdate(); - await Task.Delay(15000); - if (!PushPackages.updateProcess.HasExited) + if (packagesReady) { - var commonErrors = "\n\nCommon Errors:"; - commonErrors += "\n0x800b010a: Signature Verification Issue\nSolution: Enable flight signing"; - commonErrors += "\n\n0x800b0101: Incorrect Date and Time\nSolution: Change date"; - commonErrors += "\n\n0x80188302: Package already present on the image"; - commonErrors += "\n\n0x80188305: Duplicate packages found in update set"; - commonErrors += "\n\n0x80188306: File Collision or Not Found\nSolution: ensure list match InstalledPackages.csv"; - commonErrors += "\n\n0x80188307: Package DSM; or contents are invalid\nSolution: Keep only one type in the folder CBS or SPKG,\nChoose the correct packages type"; - commonErrors += "\n\n0x80188308: Not enough space"; - commonErrors += "\n\n0x800b0114: Certificate has an invalid name\nSolution: Enable flight signing"; - commonErrors += "\n\n0x80004005: E_FAIL\nSolution: Reboot the phone and try again"; - commonErrors += "\n\n0x802A0006: Try with another PC"; + Write("\nPUSHING PACKAGES", ConsoleColor.DarkGray); + PushPackages.StartUpdate(); + await Task.Delay(10000); + if (!PushPackages.updateProcess.HasExited) + { + var commonErrors = "\n\nCommon Errors:"; + commonErrors += "\n0x800b010a: Signature Verification Issue\nSolution: Enable flight signing"; + commonErrors += "\n\n0x800b0101: Incorrect Date and Time\nSolution: Change date"; + commonErrors += "\n\n0x80188302: Package already present on the image"; + commonErrors += "\n\n0x80188305: Duplicate packages found in update set"; + commonErrors += "\n\n0x80188306: File Collision or Not Found\nSolution: ensure list match InstalledPackages.csv"; + commonErrors += "\n\n0x80188307: Package DSM; or contents are invalid\nSolution: Keep only one type in the folder CBS or SPKG,\nChoose the correct packages type"; + commonErrors += "\n\n0x80188308: Not enough space"; + commonErrors += "\n\n0x800b0114: Certificate has an invalid name\nSolution: Enable flight signing"; + commonErrors += "\n\n0x80004005: E_FAIL\nSolution: Reboot the phone and try again"; + commonErrors += "\n\n0x802A0006: Try with another PC"; - MessageBox((IntPtr)0, $"Please navigate to update settings on your phone. As soon as the update shows progression, you can safely disconnect your phone from the PC.\n{commonErrors}", "Universal Updater.exe", 0); + MessageBox((IntPtr)0, $"Please navigate to update settings on your phone. As soon as the update shows progression, you can safely disconnect your phone from the PC.\n{commonErrors}", "Universal Updater.exe", 0); + } + } + else + { + WriteLine("\nOperation cancelled", ConsoleColor.Red); } return; } diff --git a/Universal Updater/Universal Updater.csproj b/Universal Updater/Universal Updater.csproj index 1537f55..5be0a5f 100644 --- a/Universal Updater/Universal Updater.csproj +++ b/Universal Updater/Universal Updater.csproj @@ -35,6 +35,9 @@ app.manifest + + Dakirby309-Windows-8-Metro-Other-Update-Metro.256.ico + @@ -70,5 +73,8 @@ + + + \ No newline at end of file