From 68f71f92a37fb68ba3fc26047f7c0cca44923e96 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 4 May 2024 15:14:49 -0400 Subject: [PATCH 01/10] Handle KeyNotFound when parsing PARAM.SFO versions --- Refresher/UI/IntegratedPatchForm.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Refresher/UI/IntegratedPatchForm.cs b/Refresher/UI/IntegratedPatchForm.cs index 104f1b9..6c490fe 100644 --- a/Refresher/UI/IntegratedPatchForm.cs +++ b/Refresher/UI/IntegratedPatchForm.cs @@ -100,10 +100,20 @@ protected virtual void PathChanged(object? sender, EventArgs ev) Stream sfoStream = this.Accessor.OpenRead(sfoPath); sfo = new ParamSfo(sfoStream); - item.Version = sfo.Table["APP_VER"].ToString() ?? "01.00"; + item.Version = "01.00"; + if (sfo.Table.TryGetValue("APP_VER", out object? value)) + { + string? appVersion = value.ToString(); + if (appVersion != null) + item.Version = appVersion; + } + else + { + Program.Log($"Could not find APP_VER for {game}. Defaulting to 01.00.", "SFO", BreadcrumbLevel.Warning); + } item.Text = $"{sfo.Table["TITLE"]} [{game} {item.Version}]"; - Program.Log($"Processed {game}'s PARAM.SFO file. text:\"{item.Text}\" version:\"{item.Version}", "SFO"); + Program.Log($"Processed {game}'s PARAM.SFO file. text:\"{item.Text}\" version:\"{item.Version}\"", "SFO"); } else { From 9dcfc318d9bf335cdd3e571e1de7a8835c8a56a8 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 4 May 2024 15:29:12 -0400 Subject: [PATCH 02/10] Better error handling when using AutoDiscover --- Refresher/Accessors/ConsolePatchAccessor.cs | 2 - Refresher/UI/PatchForm.cs | 50 ++++++++++++++++----- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/Refresher/Accessors/ConsolePatchAccessor.cs b/Refresher/Accessors/ConsolePatchAccessor.cs index b708627..9c8c2fd 100644 --- a/Refresher/Accessors/ConsolePatchAccessor.cs +++ b/Refresher/Accessors/ConsolePatchAccessor.cs @@ -1,5 +1,3 @@ -using System.Net; -using System.Net.Sockets; using FluentFTP; using Refresher.Exceptions; diff --git a/Refresher/UI/PatchForm.cs b/Refresher/UI/PatchForm.cs index 2a89da8..b5b6e59 100644 --- a/Refresher/UI/PatchForm.cs +++ b/Refresher/UI/PatchForm.cs @@ -1,5 +1,6 @@ using System.ComponentModel; using System.Diagnostics; +using System.Net.Sockets; using System.Runtime.InteropServices; using Eto.Drawing; using Eto.Forms; @@ -144,41 +145,66 @@ private void AutoDiscover(object? sender, EventArgs arg) { using HttpClient client = new(); client.BaseAddress = autodiscoverUri; - + HttpResponseMessage response = client.GetAsync("/autodiscover").Result; response.EnsureSuccessStatusCode(); - + Stream stream = response.Content.ReadAsStream(); - + JsonSerializer serializer = new(); using StreamReader streamReader = new(stream); using JsonTextReader jsonReader = new(streamReader); - + AutodiscoverResponse? autodiscover = serializer.Deserialize(jsonReader); if (autodiscover == null) throw new InvalidOperationException("autoresponse was null"); - + string text = $"Successfully found a '{autodiscover.ServerBrand}' server at the given URL!\n\n" + $"Server's recommended patch URL: {autodiscover.Url}\n" + $"Custom digest key?: {(autodiscover.UsesCustomDigestKey.GetValueOrDefault() ? "Yes" : "No")}\n\n" + $"Use this server's configuration?"; - + DialogResult result = MessageBox.Show(text, MessageBoxButtons.YesNo); if (result != DialogResult.Yes) return; - + this.UrlField.Text = autodiscover.Url; this.PatchDigest = autodiscover.UsesCustomDigestKey ?? false; this._usedAutoDiscover = true; } - catch (HttpRequestException e) + catch (AggregateException aggregate) { - if (e.StatusCode == null) throw; - MessageBox.Show($"AutoDiscover failed, because the server responded with {(int)e.StatusCode} {e.StatusCode}."); + aggregate.Handle(HandleAutoDiscoverError); } catch(Exception e) { - SentrySdk.CaptureException(e); - MessageBox.Show($"AutoDiscover failed: {e}", MessageBoxType.Error); + if (!HandleAutoDiscoverError(e)) + { + SentrySdk.CaptureException(e); + MessageBox.Show($"AutoDiscover failed: {e}", MessageBoxType.Error); + } + } + } + + private static bool HandleAutoDiscoverError(Exception inner) + { + if (inner is HttpRequestException httpException) + { + if (httpException.StatusCode == null) + { + MessageBox.Show($"AutoDiscover failed, because we couldn't communicate with the server: {inner.Message}"); + return true; + } + + MessageBox.Show($"AutoDiscover failed, because the server responded with {(int)httpException.StatusCode} {httpException.StatusCode}."); + return true; } + + if (inner is SocketException) + { + MessageBox.Show($"AutoDiscover failed, because we couldn't communicate with the server: {inner.Message}"); + return true; + } + + return false; } /// From 414b8cef3fedea9f33de996f31b45fbdb7f0cc9f Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 4 May 2024 16:42:15 -0400 Subject: [PATCH 03/10] Catch OpenURL exceptions --- Refresher/UI/PatchForm.cs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/Refresher/UI/PatchForm.cs b/Refresher/UI/PatchForm.cs index b5b6e59..a10ec67 100644 --- a/Refresher/UI/PatchForm.cs +++ b/Refresher/UI/PatchForm.cs @@ -117,16 +117,26 @@ public virtual void Guide(object? sender, EventArgs e) protected void OpenUrl(string url) { + try + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + Process.Start("xdg-open", url); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + Process.Start("open", url); + else + throw new PlatformNotSupportedException("Cannot open a URL on this platform."); + } + catch (Exception e) + { + Program.Log(e.ToString(), "OpenUrl", BreadcrumbLevel.Error); + MessageBox.Show("We couldn't open your browser due to an error.\n" + + $"You can use this link instead: {url}\n\n" + + $"Exception details: {e.GetType().Name} {e.Message}", + MessageBoxType.Error); + } // based off of https://stackoverflow.com/a/43232486 - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - Process.Start("xdg-open", url); - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - Process.Start("open", url); - else - throw new PlatformNotSupportedException("Cannot open a URL on this platform."); } private void AutoDiscover(object? sender, EventArgs arg) From d0bf4a70915342a3c5a16e41ea98180453f2fbc7 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 4 May 2024 20:53:31 -0400 Subject: [PATCH 04/10] Don't capture NotSupportedExceptions when setting images --- Refresher/UI/IntegratedPatchForm.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Refresher/UI/IntegratedPatchForm.cs b/Refresher/UI/IntegratedPatchForm.cs index 6c490fe..0236fb6 100644 --- a/Refresher/UI/IntegratedPatchForm.cs +++ b/Refresher/UI/IntegratedPatchForm.cs @@ -68,7 +68,7 @@ protected virtual void PathChanged(object? sender, EventArgs ev) try { Stream? iconStream = null; - + if (GameCacheAccessor.IconExistsInCache(game)) { iconStream = GameCacheAccessor.GetIconFromCache(game); @@ -78,13 +78,18 @@ protected virtual void PathChanged(object? sender, EventArgs ev) iconStream = this.Accessor.OpenRead(iconPath); GameCacheAccessor.WriteIconToCache(game, iconStream); } - + if (iconStream != null) { item.Image = new Bitmap(iconStream).WithSize(new Size(64, 64)); iconStream.Dispose(); } } + catch (NotSupportedException) + { + // Failed to set image for NPEB01899: System.NotSupportedException: No imaging component suitable to complete this operation was found. + // ignore for now + } catch(Exception e) { Program.Log($"Failed to set image for {game}: {e}", level: BreadcrumbLevel.Warning); From 510746035538939319b30a09b79577da0f1d7557 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 4 May 2024 20:55:40 -0400 Subject: [PATCH 05/10] Handle EndOfStreamException when reading PARAM.SFO --- Refresher/UI/IntegratedPatchForm.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Refresher/UI/IntegratedPatchForm.cs b/Refresher/UI/IntegratedPatchForm.cs index 0236fb6..133fc6e 100644 --- a/Refresher/UI/IntegratedPatchForm.cs +++ b/Refresher/UI/IntegratedPatchForm.cs @@ -114,17 +114,25 @@ protected virtual void PathChanged(object? sender, EventArgs ev) } else { - Program.Log($"Could not find APP_VER for {game}. Defaulting to 01.00.", "SFO", BreadcrumbLevel.Warning); + Program.Log($"Could not find APP_VER for {game}. Defaulting to 01.00.", "SFO", + BreadcrumbLevel.Warning); } + item.Text = $"{sfo.Table["TITLE"]} [{game} {item.Version}]"; - Program.Log($"Processed {game}'s PARAM.SFO file. text:\"{item.Text}\" version:\"{item.Version}\"", "SFO"); + Program.Log($"Processed {game}'s PARAM.SFO file. text:\"{item.Text}\" version:\"{item.Version}\"", + "SFO"); } else { - Program.Log($"No PARAM.SFO exists for {game} (path should be '{sfoPath}')", "SFO", BreadcrumbLevel.Warning); + Program.Log($"No PARAM.SFO exists for {game} (path should be '{sfoPath}')", "SFO", + BreadcrumbLevel.Warning); } } + catch (EndOfStreamException) + { + Program.Log($"Couldn't load {game}'s PARAM.SFO because the file was incomplete.", "SFO", BreadcrumbLevel.Error); + } catch(Exception e) { item.Text = $"Unknown PARAM.SFO [{game}]"; From f8ebf72a28599e438ffda36c838ce0b2116aeb7b Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 4 May 2024 21:06:13 -0400 Subject: [PATCH 06/10] Error handling for IDPS --- Refresher/Accessors/ConsolePatchAccessor.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Refresher/Accessors/ConsolePatchAccessor.cs b/Refresher/Accessors/ConsolePatchAccessor.cs index 9c8c2fd..570e263 100644 --- a/Refresher/Accessors/ConsolePatchAccessor.cs +++ b/Refresher/Accessors/ConsolePatchAccessor.cs @@ -46,8 +46,24 @@ public ConsolePatchAccessor(string remoteIp) private HttpResponseMessage? IdpsRequestStep(ReadOnlySpan stepName, HttpClient client, Uri uri) { + HttpResponseMessage response; + Program.Log($" {stepName} ({uri.AbsolutePath})", "IDPS"); - HttpResponseMessage response = client.GetAsync(uri).Result; + try + { + response = client.GetAsync(uri).Result; + } + catch (HttpRequestException e) + { + Program.Log($"Couldn't fetch the IDPS from the PS3 because we couldn't make the request: {e.Message}", "IDPS", BreadcrumbLevel.Error); + return null; + } + catch (Exception e) + { + Program.Log($"Couldn't fetch the IDPS from the PS3 because of an unknown error: {e}", "IDPS", BreadcrumbLevel.Error); + SentrySdk.CaptureException(e); + return null; + } Program.Log($" {(int)response.StatusCode} {response.StatusCode} (success: {response.IsSuccessStatusCode})", "IDPS"); if (!response.IsSuccessStatusCode) From 525c8d97ba6daec03a0761d6fab1238f96febeb4 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 4 May 2024 21:08:19 -0400 Subject: [PATCH 07/10] Handle FTP errors --- Refresher/UI/IntegratedPatchForm.cs | 122 +++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 22 deletions(-) diff --git a/Refresher/UI/IntegratedPatchForm.cs b/Refresher/UI/IntegratedPatchForm.cs index 133fc6e..db09935 100644 --- a/Refresher/UI/IntegratedPatchForm.cs +++ b/Refresher/UI/IntegratedPatchForm.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Eto.Drawing; using Eto.Forms; +using FluentFTP.Exceptions; using Refresher.Accessors; using Refresher.Patching; using Refresher.UI.Items; @@ -44,6 +45,11 @@ protected IntegratedPatchForm(string subtitle) : base(subtitle) this.InitializePatcher(); } + protected void HandleFtpError(FtpException exception, string action) + { + this.FailVerify($"An error occurred while {action}: {exception.Message}\n\nTry again in a couple minutes to let things 'cool off'. Sometimes the PS3's kernel just likes to throw a fit."); + } + protected virtual void PathChanged(object? sender, EventArgs ev) { Debug.Assert(this.Accessor != null); @@ -85,6 +91,11 @@ protected virtual void PathChanged(object? sender, EventArgs ev) iconStream.Dispose(); } } + catch (FtpException e) + { + this.HandleFtpError(e, "downloading a game's icon"); + return; + } catch (NotSupportedException) { // Failed to set image for NPEB01899: System.NotSupportedException: No imaging component suitable to complete this operation was found. @@ -129,6 +140,11 @@ protected virtual void PathChanged(object? sender, EventArgs ev) BreadcrumbLevel.Warning); } } + catch (FtpException e) + { + this.HandleFtpError(e, "downloading a game's PARAM.SFO file"); + return; + } catch (EndOfStreamException) { Program.Log($"Couldn't load {game}'s PARAM.SFO because the file was incomplete.", "SFO", BreadcrumbLevel.Error); @@ -183,22 +199,40 @@ protected virtual void GameChanged(object? sender, EventArgs ev) this._usrDir = Path.Combine("game", game.TitleId, "USRDIR"); string ebootPath = Path.Combine(this._usrDir, "EBOOT.BIN.ORIG"); // Prefer original backup over active copy - // If the backup doesn't exist, use the EBOOT.BIN - if (!this.Accessor.FileExists(ebootPath)) + try { - this.LogMessage("Couldn't find an original backup of the EBOOT, using active copy. This is not an error."); - ebootPath = Path.Combine(this._usrDir, "EBOOT.BIN"); - - // If we land here, then we have no valid patch target without any way to recover. - // This is very inconvenient for us and the user. + // If the backup doesn't exist, use the EBOOT.BIN if (!this.Accessor.FileExists(ebootPath)) { - this.FailVerify("The EBOOT.BIN file does not exist, nor does the original backup exist. This usually means you haven't installed any updates for your game."); - return; + this.LogMessage("Couldn't find an original backup of the EBOOT, using active copy. This is not an error."); + ebootPath = Path.Combine(this._usrDir, "EBOOT.BIN"); + + // If we land here, then we have no valid patch target without any way to recover. + // This is very inconvenient for us and the user. + if (!this.Accessor.FileExists(ebootPath)) + { + this.FailVerify("The EBOOT.BIN file does not exist, nor does the original backup exist. This usually means you haven't installed any updates for your game."); + return; + } } } + catch (FtpException e) + { + this.HandleFtpError(e, "finding the EBOOT to use"); + return; + } - string downloadedFile = this.Accessor.DownloadFile(ebootPath); + string downloadedFile; + + try + { + downloadedFile = this.Accessor.DownloadFile(ebootPath); + } + catch (FtpException e) + { + this.HandleFtpError(e, "downloading the game's EBOOT"); + return; + } this.LogMessage($"Downloaded EBOOT Path: {downloadedFile}"); if (!File.Exists(downloadedFile)) @@ -206,12 +240,20 @@ protected virtual void GameChanged(object? sender, EventArgs ev) this.FailVerify("Could not find the EBOOT we downloaded. This is likely a bug. Patching cannot continue.", clear: false); return; } - - // if this is a NP game then download the RIF for the right content ID, disc copies don't need anything else - if (game.TitleId.StartsWith('N')) + + try { - Program.Log("Digital game detected, trying to download license file"); - this.DownloadLicenseFile(downloadedFile, game); + // if this is a NP game then download the RIF for the right content ID, disc copies don't need anything else + if (game.TitleId.StartsWith('N')) + { + Program.Log("Digital game detected, trying to download license file"); + this.DownloadLicenseFile(downloadedFile, game); + } + } + catch (FtpException e) + { + this.HandleFtpError(e, "downloading the game's license file"); + return; } this._tempFile = Path.GetTempFileName(); @@ -227,7 +269,17 @@ protected virtual void GameChanged(object? sender, EventArgs ev) if (game.TitleId.StartsWith('B')) { Program.Log("Disc game detected - trying to gather a license as a workaround for LBP Hub"); - this.DownloadLicenseFile(downloadedFile, game); + + try + { + this.DownloadLicenseFile(downloadedFile, game); + } + catch (FtpException e) + { + this.HandleFtpError(e, "downloading the game's license file (with Hub workaround)"); + return; + } + LibSceToolSharp.Decrypt(downloadedFile, this._tempFile); } @@ -354,17 +406,43 @@ public override void CompletePatch(object? sender, EventArgs e) { if (this.ShouldReplaceExecutable) { string backup = destination + ".ORIG"; - if (!this.Accessor.FileExists(backup)) - this.Accessor.CopyFile(destination, backup); + + try + { + if (!this.Accessor.FileExists(backup)) + this.Accessor.CopyFile(destination, backup); + } + catch (FtpException exception) + { + this.HandleFtpError(exception, "making a backup of the EBOOT"); + return; + } + } + + try + { + if (this.Accessor.FileExists(destination)) + this.Accessor.RemoveFile(destination); + } + catch (FtpException exception) + { + this.HandleFtpError(exception, "removing the original EBOOT"); + return; } - - if (this.Accessor.FileExists(destination)) - this.Accessor.RemoveFile(destination); // wait a second for the ps3 to calm down Thread.Sleep(1000); // TODO: don't. block. the. main. thread. - this.Accessor.UploadFile(fileToUpload, destination); + try + { + this.Accessor.UploadFile(fileToUpload, destination); + } + catch (FtpException exception) + { + this.HandleFtpError(exception, "applying the EBOOT patch"); + return; + } + MessageBox.Show(this, $"Successfully patched EBOOT! It was saved to '{destination}'.", "Success!"); // Re-initialize patcher so we can patch with the same parameters again From d0385e9b6108dc596712212b840a0f1dce1f5054 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 4 May 2024 21:17:01 -0400 Subject: [PATCH 08/10] Adjust unhandled exception text --- Refresher/Program.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Refresher/Program.cs b/Refresher/Program.cs index ff31558..18ae20f 100644 --- a/Refresher/Program.cs +++ b/Refresher/Program.cs @@ -70,10 +70,10 @@ public static void Main(string[] args) SentrySdk.CaptureException((Exception)eventArgs.ExceptionObject); SentrySdk.Flush(); MessageBox.Show($""" - There was an unhandled error in Refresher! - *Please* screenshot this message box and send it to us over GitHub or Discord with details on what you were doing. This is likely a bug in Refresher. + There was an unhandled error in Refresher. + This has been automatically reported to us. The exception details has been displayed for further debugging: - Exception details: {eventArgs.ExceptionObject} + {eventArgs.ExceptionObject} """, "Critical Error"); @@ -88,10 +88,10 @@ There was an unhandled error in Refresher! SentrySdk.CaptureException(ex); SentrySdk.Flush(); MessageBox.Show($""" - There was an unhandled error in Refresher! - *Please* screenshot this message box and send it to us over GitHub or Discord with details on what you were doing. This is likely a bug in Refresher. + There was an unhandled error in Refresher. + This has been automatically reported to us. The exception details has been displayed for further debugging: - Exception details: {ex} + {ex} """, "Critical Error"); } App.Dispose(); From 6329070c1cd9a558e6095d5ce7807842a0248301 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 4 May 2024 22:00:36 -0400 Subject: [PATCH 09/10] Automagic PPU hash detection Thanks to @uhwot for figuring out how to do this in Python, which I referenced for this implementation. --- Refresher/Patching/EbootPatcher.cs | 49 ++++++++++++++++++++++++------ Refresher/Program.cs | 1 + Refresher/UI/EmulatorPatchForm.cs | 19 ++---------- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/Refresher/Patching/EbootPatcher.cs b/Refresher/Patching/EbootPatcher.cs index dc33adb..59e74e0 100644 --- a/Refresher/Patching/EbootPatcher.cs +++ b/Refresher/Patching/EbootPatcher.cs @@ -1,9 +1,12 @@ +using System.Buffers.Binary; using System.Collections.Frozen; using System.Diagnostics; using System.Diagnostics.Contracts; +using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using ELFSharp.ELF; +using ELFSharp.ELF.Segments; using Refresher.Verification; namespace Refresher.Patching; @@ -11,10 +14,10 @@ namespace Refresher.Patching; public partial class EbootPatcher : IPatcher { private readonly Lazy> _targets; + private readonly Lazy _ppuHash = null; public bool GenerateRpcs3Patch = false; public string? Rpcs3PatchFolder = null; - public string? PpuHash = null; public string? GameVersion = null; public string? GameName; public string? TitleId; @@ -29,6 +32,7 @@ public EbootPatcher(Stream stream) this.Stream.Position = 0; this._targets = new Lazy>(() => FindPatchableElements(stream)); + this._ppuHash = new Lazy(() => GeneratePpuHash(stream)); } public Stream Stream { get; } @@ -308,6 +312,38 @@ private static void FindDigestAroundCookie(BinaryReader reader, List found } } } + + public static string GeneratePpuHash(Stream stream) + { + stream.Position = 0; + ELF elfFile = ELFReader.Load(stream, false); // Assuming ELF32. Change to Elf64 if needed. + + using SHA1 hash = SHA1.Create(); + foreach (Segment? segment in elfFile.Segments) + { + hash.TransformBlock(BitConverter.GetBytes(BinaryPrimitives.ReverseEndianness((uint)segment.Type)), 0, 4, null, 0); + hash.TransformBlock(BitConverter.GetBytes(BinaryPrimitives.ReverseEndianness((uint)segment.Flags)), 0, 4, null, 0); + + if (segment.Type != SegmentType.Load || segment.Size == 0) continue; + + hash.TransformBlock(BitConverter.GetBytes(BinaryPrimitives.ReverseEndianness(segment.Address)), 0, 8, null, 0); + hash.TransformBlock(BitConverter.GetBytes(BinaryPrimitives.ReverseEndianness(segment.Size)), 0, 8, null, 0); + + byte[]? segmentData = segment.GetFileContents(); + + hash.TransformBlock(segmentData, 0, segmentData.Length, null, 0); + } + + // trigger generation of hash after reading segments + hash.TransformFinalBlock([], 0, 0); + + stream.Position = 0; + + string ppuHash = BitConverter.ToString(hash.Hash!).Replace("-", "").ToLower(); + + Program.Log($"PPU hash: PPU-{ppuHash}", "PPU", BreadcrumbLevel.Debug); + return ppuHash; + } /// /// Checks the contents of the EBOOT to verify that it is patchable. @@ -360,10 +396,10 @@ public List Verify(string url, bool patchDigest) if (this.GenerateRpcs3Patch) { - if (string.IsNullOrWhiteSpace(this.PpuHash)) + if (string.IsNullOrWhiteSpace(this._ppuHash.Value)) { messages.Add(new Message(MessageLevel.Error, - "Missing PPU hash! This is used by RPCS3 to know which game the patch is used for, please read the RPCS3 patching guide to learn how to get this value.")); + "Couldn't determine the PPU hash. This is used by RPCS3 to know which game the patch is used for. Without it, we cannot continue patching.")); } } @@ -377,7 +413,6 @@ public void Patch(string url, bool patchDigest) Debug.Assert(this.Rpcs3PatchFolder != null); Debug.Assert(this.GameName != null); Debug.Assert(this.TitleId != null); - Debug.Assert(!string.IsNullOrEmpty(this.PpuHash)); Debug.Assert(!string.IsNullOrEmpty(this.GameVersion)); string patchesFile = Path.Combine(this.Rpcs3PatchFolder, "imported_patch.yml"); @@ -385,13 +420,9 @@ public void Patch(string url, bool patchDigest) if (!File.Exists(patchesFile)) //Write the header to the patches file File.WriteAllText(patchesFile, "Version: 1.2\n\n"); - - //Trim the PPU- prefix, if its there - if (this.PpuHash.StartsWith("PPU-")) - this.PpuHash = this.PpuHash[4..]; string template = $""" - PPU-{this.PpuHash}: + PPU-{this._ppuHash}: "Refresher Patch ({url})": Games: "{this.GameName}": diff --git a/Refresher/Program.cs b/Refresher/Program.cs index 18ae20f..0152090 100644 --- a/Refresher/Program.cs +++ b/Refresher/Program.cs @@ -2,6 +2,7 @@ using CommandLine; using Eto.Forms; using Refresher.CLI; +using Refresher.Patching; using Refresher.UI; using Sentry; diff --git a/Refresher/UI/EmulatorPatchForm.cs b/Refresher/UI/EmulatorPatchForm.cs index 78236a5..e70a2e0 100644 --- a/Refresher/UI/EmulatorPatchForm.cs +++ b/Refresher/UI/EmulatorPatchForm.cs @@ -9,7 +9,6 @@ namespace Refresher.UI; public class EmulatorPatchForm : IntegratedPatchForm { private FilePicker _folderField = null!; - private TextBox _ppuHash = null!; private Button _filePatchButton = null!; public EmulatorPatchForm() : base("RPCS3 Patch") @@ -33,17 +32,6 @@ public EmulatorPatchForm() : base("RPCS3 Patch") this.LogMessage("RPCS3's path has been detected automatically! You do not need to change the path."); } } - - this._ppuHash.TextChanged += this.UpdateTextFields; - } - - private void UpdateTextFields(object? sender, EventArgs args) - { - if (this.Patcher != null) - { - this.Patcher.PpuHash = this._ppuHash.Text; - this.Reverify(null, EventArgs.Empty); - } } protected override void BeforePatch(object? sender, EventArgs e) @@ -57,11 +45,10 @@ protected override void BeforePatch(object? sender, EventArgs e) if (this.Patcher != null) { this.Patcher.GenerateRpcs3Patch = true; - this.Patcher.PpuHash = this._ppuHash.Text; this.Patcher.GameVersion = game.Version; this.Patcher.Rpcs3PatchFolder = Path.Combine(this._folderField.FilePath, "../patches"); - this.Patcher.GameName = ((GameItem)this.GameDropdown.SelectedValue).Text; - this.Patcher.TitleId = ((GameItem)this.GameDropdown.SelectedValue).TitleId; + this.Patcher.GameName = game.Text; + this.Patcher.TitleId = game.TitleId; try { @@ -95,7 +82,6 @@ protected override IEnumerable AddFields() { return new[] { - AddField("Game PPU hash", out this._ppuHash), AddField("RPCS3 dev_hdd0 folder", out this._folderField), AddField("", out this._filePatchButton), }; @@ -112,7 +98,6 @@ protected override void GameChanged(object? sender, EventArgs ev) } this.Patcher.GenerateRpcs3Patch = true; - this.UpdateTextFields(null, EventArgs.Empty); this.Reverify(null, EventArgs.Empty); } From 0cb2737d85cb58b1c1d597dfc9fdad22c369ec97 Mon Sep 17 00:00:00 2001 From: jvyden Date: Sat, 4 May 2024 22:01:56 -0400 Subject: [PATCH 10/10] I definitely did NOT copy from ChatGPT. Why would I ever do that and then commit it? --- Refresher/Patching/EbootPatcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Refresher/Patching/EbootPatcher.cs b/Refresher/Patching/EbootPatcher.cs index 59e74e0..9adf691 100644 --- a/Refresher/Patching/EbootPatcher.cs +++ b/Refresher/Patching/EbootPatcher.cs @@ -316,7 +316,7 @@ private static void FindDigestAroundCookie(BinaryReader reader, List found public static string GeneratePpuHash(Stream stream) { stream.Position = 0; - ELF elfFile = ELFReader.Load(stream, false); // Assuming ELF32. Change to Elf64 if needed. + ELF elfFile = ELFReader.Load(stream, false); using SHA1 hash = SHA1.Create(); foreach (Segment? segment in elfFile.Segments)