diff --git a/Refresher/Patching/Patcher.cs b/Refresher/Patching/Patcher.cs
index fd90705..bdba022 100644
--- a/Refresher/Patching/Patcher.cs
+++ b/Refresher/Patching/Patcher.cs
@@ -39,12 +39,16 @@ public Patcher(Stream stream)
/// A list of the URLs and Digest keys
private static List FindPatchableElements(Stream file)
{
+ long start = Stopwatch.GetTimestamp();
+
file.Position = 0;
using IELF? elf = ELFReader.Load(file, false);
file.Position = 0;
- BinaryReader reader = new(file);
-
+ //Buffer the stream with a size of 4096
+ BufferedStream bufferedStream = new(file, 4096);
+ BinaryReader reader = new(bufferedStream);
+
// The string "http" in ASCII, as an int
int httpInt = BitConverter.ToInt32("http"u8);
@@ -56,65 +60,56 @@ private static List FindPatchableElements(Stream file)
//Found positions of `cook` in the binary
List cookiePositions = new();
- foreach (ISection section in elf.Sections)
+ long read = 0;
+
+ //Create an array twice the size of the data we are wanting to check
+ Span arr = new byte[8];
+ while (reader.Read(arr) == arr.Length)
{
- // Assume a word size of 4, i would use `ProgBitsSection.Alignment`
- // but that seems to be unrelated to the actual alignment and offset chosen for the string constants specifically (the section with all the strings is marked as 32-byte alignment[?????])
- // so we'll just assume 4 byte alignment since that seems universal here
- int wordSize = 4;
- ulong sectionLength;
- switch (section)
- {
- case ProgBitsSection progBitsSectionUlong:
- file.Position = (long)progBitsSectionUlong.Offset;
- sectionLength = progBitsSectionUlong.Size;
- break;
- case ProgBitsSection progBitsSectionUint:
- file.Position = progBitsSectionUint.Offset;
- sectionLength = progBitsSectionUint.Size;
- break;
- default:
- continue;
- }
-
- int read = 0;
-
- byte[] buf = new byte[wordSize];
- //While we are not at the end of the file, read each word in
- while (read < (int)sectionLength - wordSize)
+ long? found = null;
+ for (int i = 0; i < 5; i++)
{
- read += file.Read(buf);
+ int check = BitConverter.ToInt32(arr[i..(i + 4)]);
- int bufInt = BitConverter.ToInt32(buf);
-
- //If they are equal, we found an instance of HTTP
- if (bufInt == httpInt)
+ if (check == httpInt)
{
- //Mark the position of the found instance
- possibleUrls.Add(reader.BaseStream.Position - wordSize);
+ possibleUrls.Add(read + i - 4);
+ found = read + i - 4;
+ break;
}
- if (bufInt == cookieStartInt)
+ // ReSharper disable once InvertIf
+ if (check == cookieStartInt)
{
- cookiePositions.Add(reader.BaseStream.Position - wordSize);
+ cookiePositions.Add(read + i - 4);
+ found = read + i - 4;
+ break;
}
}
- }
+ //Seek 4 bytes after the position we started at, or 4 bytes after the starting index of a match
+ reader.BaseStream.Seek(found == null ? read + 4 : found.Value + 4, SeekOrigin.Begin);
+
+ read += arr.Length;
+ }
+
List foundItems = new();
- FilterValidUrls(file, possibleUrls, reader, foundItems);
- FindDigestAroundCookie(file, cookiePositions, reader, foundItems);
+ FilterValidUrls(reader, possibleUrls, foundItems);
+ FindDigestAroundCookie(reader, cookiePositions, foundItems);
+
+ long end = Stopwatch.GetTimestamp();
+ Console.WriteLine($"Detecting patchables took {(double)(end - start) / (double)Stopwatch.Frequency} seconds!");
return foundItems;
}
- private static void FilterValidUrls(Stream file, List foundPossibleUrlPositions, BinaryReader reader, List foundItems)
+ private static void FilterValidUrls(BinaryReader reader, List foundPossibleUrlPositions, List foundItems)
{
bool tooLong = false;
foreach (long foundPosition in foundPossibleUrlPositions)
{
int len = 0;
- file.Position = foundPosition;
+ reader.BaseStream.Position = foundPosition;
//Find the first null byte
while (reader.ReadByte() != 0)
@@ -137,13 +132,16 @@ private static void FilterValidUrls(Stream file, List foundPossibleUrlPosi
//Keep reading until we arent at a null byte
while (reader.ReadByte() == 0) len++;
- file.Position = foundPosition;
+ //Remove one from length to make sure to leave a single null byte after
+ len -= 1;
+
+ reader.BaseStream.Position = foundPosition;
//`len` at this point is the amount of bytes that are actually available to repurpose
//This includes all extra null bytes except for the last one
byte[] match = new byte[len];
- if (file.Read(match) < len) continue;
+ if (reader.Read(match) < len) continue;
string str = Encoding.UTF8.GetString(match).TrimEnd('\0');
if (str.Contains('%')) continue; // Ignore printf strings, e.g. %s
@@ -159,11 +157,11 @@ private static void FilterValidUrls(Stream file, List foundPossibleUrlPosi
}
}
- private static void FindDigestAroundCookie(Stream file, List foundPossibleCookiePositions, BinaryReader reader, List foundItems)
+ private static void FindDigestAroundCookie(BinaryReader reader, List foundPossibleCookiePositions, List foundItems)
{
foreach (long foundPosition in foundPossibleCookiePositions)
{
- file.Position = foundPosition;
+ reader.BaseStream.Position = foundPosition;
byte[] cookieBuf = new byte[8];
//If we didnt read enough or what we read isnt "cookie\0\0"
@@ -176,7 +174,7 @@ private static void FindDigestAroundCookie(Stream file, List foundPossible
const int checkSize = 1000;
//Go back half the check size in bytes (so that `cookie` is in the middle)
- file.Position -= checkSize / 2;
+ reader.BaseStream.Position -= checkSize / 2;
byte[] checkArr = new byte[checkSize];
Span toCheck = checkArr.AsSpan().Slice(0, reader.Read(checkArr));
@@ -209,7 +207,7 @@ private static void FindDigestAroundCookie(Stream file, List foundPossible
foundItems.Add(new PatchTargetInfo
{
Length = len,
- Offset = file.Position - checkSize + start,
+ Offset = reader.BaseStream.Position - checkSize + start,
Data = str,
Type = PatchTargetType.Digest,
});
diff --git a/Refresher/UI/IntegratedPatchForm.cs b/Refresher/UI/IntegratedPatchForm.cs
index e88beaf..49011ae 100644
--- a/Refresher/UI/IntegratedPatchForm.cs
+++ b/Refresher/UI/IntegratedPatchForm.cs
@@ -25,19 +25,21 @@ public abstract class IntegratedPatchForm : PatchForm
[SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
protected IntegratedPatchForm(string subtitle) : base(subtitle)
{
- this.FormPanel = new TableLayout(new List
+ List rows = new()
{
this.AddRemoteField(),
AddField("Game to patch", out this._gameDropdown, forceHeight: 56),
- AddField("Server URL", out this.UrlField),
- });
+ AddField("Server URL", out this.UrlField),
+ };
if (!this.ShouldReplaceExecutable)
{
- this.FormPanel.Rows.Add(AddField("Identifier (EBOOT..elf)", out this._outputField));
+ rows.Add(AddField("Identifier (EBOOT..elf)", out this._outputField));
this._outputField!.PlaceholderText = "refresh";
}
+ this.FormPanel = new TableLayout(rows);
+
this._gameDropdown.SelectedValueChanged += this.GameChanged;
this.InitializePatcher();
@@ -171,7 +173,7 @@ public override void CompletePatch(object? sender, EventArgs e) {
fileToUpload = this._tempFile;
}
- string destinationFile = this.ShouldReplaceExecutable ? "EBOOT.BIN" : $"EBOOT.{identifier}.BIN";
+ string destinationFile = this.ShouldReplaceExecutable ? "EBOOT.BIN" : this.NeedsResign ? $"EBOOT.{identifier}.BIN" : $"EBOOT.{identifier}.elf";
string destination = Path.Combine(this._usrDir, destinationFile);
// if we're replacing the executable, back it up to EBOOT.BIN.ORIG before we do so