mirror of
https://github.com/LBPUnion/UnionPatcher.git
synced 2025-08-04 15:18:49 +00:00
Use file-scoped namespaces
This commit is contained in:
parent
a90da4e7b3
commit
bccb6749d1
9 changed files with 393 additions and 393 deletions
|
@ -2,53 +2,53 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace LBPUnion.UnionPatcher.Cli {
|
namespace LBPUnion.UnionPatcher.Cli;
|
||||||
public static class Program {
|
|
||||||
public const string Version = "1.0";
|
|
||||||
|
|
||||||
private static string fileName;
|
public static class Program {
|
||||||
|
public const string Version = "1.0";
|
||||||
|
|
||||||
public static string FileName {
|
private static string fileName;
|
||||||
get {
|
|
||||||
if(fileName != null) return fileName;
|
|
||||||
|
|
||||||
return fileName = Path.GetFileName(Process.GetCurrentProcess().MainModule?.FileName);
|
public static string FileName {
|
||||||
}
|
get {
|
||||||
}
|
if(fileName != null) return fileName;
|
||||||
|
|
||||||
public static void Main(string[] args) {
|
return fileName = Path.GetFileName(Process.GetCurrentProcess().MainModule?.FileName);
|
||||||
if(args.Length < 3) {
|
|
||||||
PrintHelp();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ElfFile eboot = new(new FileInfo(args[0]));
|
|
||||||
|
|
||||||
if(!eboot.IsValid) {
|
|
||||||
Console.WriteLine($"{eboot.Name} is not a valid ELF file (magic number mismatch)");
|
|
||||||
Console.WriteLine("The EBOOT must be decrypted before using this tool");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(eboot.Is64Bit == null) {
|
|
||||||
Console.WriteLine($"{eboot.Name} does not target a valid system");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(eboot.Architecture)) {
|
|
||||||
Console.WriteLine($"{eboot.Name} does not target a valid architecture (PowerPC or ARM)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine($"{eboot.Name} targets {eboot.Architecture}");
|
|
||||||
|
|
||||||
Patcher.PatchFile(args[0], args[1], args[2]);
|
|
||||||
Console.WriteLine($"Successfully patched Server URL to {args[1]}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void PrintHelp() {
|
|
||||||
Console.WriteLine($"UnionPatcher {Version}");
|
|
||||||
Console.WriteLine($" Usage: {FileName} <Input EBOOT.elf> <Server URL> <Output filename>");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Main(string[] args) {
|
||||||
|
if(args.Length < 3) {
|
||||||
|
PrintHelp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ElfFile eboot = new(new FileInfo(args[0]));
|
||||||
|
|
||||||
|
if(!eboot.IsValid) {
|
||||||
|
Console.WriteLine($"{eboot.Name} is not a valid ELF file (magic number mismatch)");
|
||||||
|
Console.WriteLine("The EBOOT must be decrypted before using this tool");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(eboot.Is64Bit == null) {
|
||||||
|
Console.WriteLine($"{eboot.Name} does not target a valid system");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(eboot.Architecture)) {
|
||||||
|
Console.WriteLine($"{eboot.Name} does not target a valid architecture (PowerPC or ARM)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"{eboot.Name} targets {eboot.Architecture}");
|
||||||
|
|
||||||
|
Patcher.PatchFile(args[0], args[1], args[2]);
|
||||||
|
Console.WriteLine($"Successfully patched Server URL to {args[1]}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PrintHelp() {
|
||||||
|
Console.WriteLine($"UnionPatcher {Version}");
|
||||||
|
Console.WriteLine($" Usage: {FileName} <Input EBOOT.elf> <Server URL> <Output filename>");
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
namespace LBPUnion.UnionPatcher.Gui.Linux {
|
namespace LBPUnion.UnionPatcher.Gui.Linux;
|
||||||
public static class Program {
|
|
||||||
public static void Main() {
|
public static class Program {
|
||||||
Gui.Show();
|
public static void Main() {
|
||||||
}
|
Gui.Show();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
namespace LBPUnion.UnionPatcher.Gui.MacOS {
|
namespace LBPUnion.UnionPatcher.Gui.MacOS;
|
||||||
public static class Program {
|
|
||||||
public static void Main() {
|
public static class Program {
|
||||||
Gui.Show();
|
public static void Main() {
|
||||||
}
|
Gui.Show();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LBPUnion.UnionPatcher.Gui.Windows {
|
namespace LBPUnion.UnionPatcher.Gui.Windows;
|
||||||
public static class Program {
|
|
||||||
[STAThread]
|
public static class Program {
|
||||||
public static void Main() {
|
[STAThread]
|
||||||
Gui.Show();
|
public static void Main() {
|
||||||
}
|
Gui.Show();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LBPUnion.UnionPatcher.Gui {
|
namespace LBPUnion.UnionPatcher.Gui;
|
||||||
public static class EasterEgg {
|
|
||||||
private static bool? restitch = null;
|
|
||||||
|
|
||||||
public static bool Restitch {
|
public static class EasterEgg {
|
||||||
get {
|
private static bool? restitch = null;
|
||||||
if(restitch == null) {
|
|
||||||
return (bool)(restitch = new Random().Next(1, 10_000) == 1);
|
public static bool Restitch {
|
||||||
}
|
get {
|
||||||
return (bool)restitch;
|
if(restitch == null) {
|
||||||
|
return (bool)(restitch = new Random().Next(1, 10_000) == 1);
|
||||||
}
|
}
|
||||||
|
return (bool)restitch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
using Eto.Forms;
|
using Eto.Forms;
|
||||||
|
|
||||||
namespace LBPUnion.UnionPatcher.Gui {
|
namespace LBPUnion.UnionPatcher.Gui;
|
||||||
public static class Gui {
|
|
||||||
public static void Show() {
|
public static class Gui {
|
||||||
new Application().Run(new MainForm());
|
public static void Show() {
|
||||||
}
|
new Application().Run(new MainForm());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,161 +5,161 @@ using Eto;
|
||||||
using Eto.Drawing;
|
using Eto.Drawing;
|
||||||
using Eto.Forms;
|
using Eto.Forms;
|
||||||
|
|
||||||
namespace LBPUnion.UnionPatcher.Gui {
|
namespace LBPUnion.UnionPatcher.Gui;
|
||||||
public class MainForm : Form {
|
|
||||||
#region UI
|
|
||||||
private readonly FilePicker filePicker;
|
|
||||||
private readonly TextBox serverUrl;
|
|
||||||
private readonly FilePicker outputFileName;
|
|
||||||
public Dialog CreateOkDialog(string title, string errorMessage) {
|
|
||||||
DynamicLayout layout = new();
|
|
||||||
Button button;
|
|
||||||
|
|
||||||
layout.Spacing = new Size(5, 5);
|
public class MainForm : Form {
|
||||||
layout.MinimumSize = new Size(350, 100);
|
#region UI
|
||||||
|
private readonly FilePicker filePicker;
|
||||||
|
private readonly TextBox serverUrl;
|
||||||
|
private readonly FilePicker outputFileName;
|
||||||
|
public Dialog CreateOkDialog(string title, string errorMessage) {
|
||||||
|
DynamicLayout layout = new();
|
||||||
|
Button button;
|
||||||
|
|
||||||
layout.BeginHorizontal();
|
layout.Spacing = new Size(5, 5);
|
||||||
layout.Add(new Label {
|
layout.MinimumSize = new Size(350, 100);
|
||||||
Text = errorMessage,
|
|
||||||
});
|
|
||||||
|
|
||||||
layout.BeginHorizontal();
|
layout.BeginHorizontal();
|
||||||
layout.BeginVertical();
|
layout.Add(new Label {
|
||||||
layout.Add(null);
|
Text = errorMessage,
|
||||||
layout.Add(button = new Button {
|
});
|
||||||
Text = "OK",
|
|
||||||
});
|
|
||||||
|
|
||||||
layout.EndVertical();
|
layout.BeginHorizontal();
|
||||||
layout.EndHorizontal();
|
layout.BeginVertical();
|
||||||
layout.EndHorizontal();
|
layout.Add(null);
|
||||||
|
layout.Add(button = new Button {
|
||||||
|
Text = "OK",
|
||||||
|
});
|
||||||
|
|
||||||
Dialog dialog = new() {
|
layout.EndVertical();
|
||||||
Content = layout,
|
layout.EndHorizontal();
|
||||||
Padding = new Padding(10, 10, 10, 10),
|
layout.EndHorizontal();
|
||||||
Title = title,
|
|
||||||
};
|
|
||||||
|
|
||||||
button.Click += delegate {
|
Dialog dialog = new() {
|
||||||
dialog.Close();
|
Content = layout,
|
||||||
};
|
Padding = new Padding(10, 10, 10, 10),
|
||||||
|
Title = title,
|
||||||
|
};
|
||||||
|
|
||||||
return dialog;
|
button.Click += delegate {
|
||||||
|
dialog.Close();
|
||||||
|
};
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Control CreatePatchButton(int tabIndex = 0) {
|
||||||
|
Button control = new() {
|
||||||
|
Text = EasterEgg.Restitch ? "Restitch!" : "Patch!",
|
||||||
|
TabIndex = tabIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
control.Click += Patch;
|
||||||
|
|
||||||
|
return control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Control CreateHelpButton(int tabIndex = 0) {
|
||||||
|
Button control = new() {
|
||||||
|
Text = "Help",
|
||||||
|
TabIndex = tabIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
control.Click += delegate {
|
||||||
|
Process process = new();
|
||||||
|
|
||||||
|
process.StartInfo.UseShellExecute = true;
|
||||||
|
process.StartInfo.FileName = "https://www.lbpunion.com";
|
||||||
|
process.Start();
|
||||||
|
};
|
||||||
|
|
||||||
|
return control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MainForm() {
|
||||||
|
this.Title = EasterEgg.Restitch ? "Union Restitcher" : "Union Patcher";
|
||||||
|
this.ClientSize = new Size(500, -1);
|
||||||
|
this.Content = new TableLayout {
|
||||||
|
Spacing = new Size(5,5),
|
||||||
|
Padding = new Padding(10, 10, 10, 10),
|
||||||
|
Rows = {
|
||||||
|
new TableRow(
|
||||||
|
new TableCell(new Label { Text = "EBOOT.elf: ", VerticalAlignment = VerticalAlignment.Center }),
|
||||||
|
new TableCell(this.filePicker = new FilePicker { TabIndex = 0 })
|
||||||
|
),
|
||||||
|
new TableRow(
|
||||||
|
new TableCell(new Label { Text = "Server URL: ", VerticalAlignment = VerticalAlignment.Center }),
|
||||||
|
new TableCell(this.serverUrl = new TextBox { TabIndex = 1 })
|
||||||
|
),
|
||||||
|
new TableRow(
|
||||||
|
new TableCell(new Label { Text = "Output filename: ", VerticalAlignment = VerticalAlignment.Center }),
|
||||||
|
new TableCell(this.outputFileName = new FilePicker { TabIndex = 2, FileAction = FileAction.SaveFile })
|
||||||
|
),
|
||||||
|
new TableRow(
|
||||||
|
new TableCell(this.CreateHelpButton(4)),
|
||||||
|
new TableCell(this.CreatePatchButton(3))
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
private void Patch(object sender, EventArgs e) {
|
||||||
|
this.Patch();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Patch() {
|
||||||
|
if(string.IsNullOrWhiteSpace(this.filePicker.FilePath)) {
|
||||||
|
this.CreateOkDialog("Form Error", "No file specified!").ShowModal();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Control CreatePatchButton(int tabIndex = 0) {
|
if(string.IsNullOrWhiteSpace(this.serverUrl.Text)) {
|
||||||
Button control = new() {
|
this.CreateOkDialog("Form Error", "No server URL specified!").ShowModal();
|
||||||
Text = EasterEgg.Restitch ? "Restitch!" : "Patch!",
|
return;
|
||||||
TabIndex = tabIndex,
|
|
||||||
};
|
|
||||||
|
|
||||||
control.Click += Patch;
|
|
||||||
|
|
||||||
return control;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Control CreateHelpButton(int tabIndex = 0) {
|
if(string.IsNullOrWhiteSpace(this.outputFileName.FilePath)) {
|
||||||
Button control = new() {
|
this.CreateOkDialog("Form Error", "No output file specified!").ShowModal();
|
||||||
Text = "Help",
|
return;
|
||||||
TabIndex = tabIndex,
|
|
||||||
};
|
|
||||||
|
|
||||||
control.Click += delegate {
|
|
||||||
Process process = new();
|
|
||||||
|
|
||||||
process.StartInfo.UseShellExecute = true;
|
|
||||||
process.StartInfo.FileName = "https://www.lbpunion.com";
|
|
||||||
process.Start();
|
|
||||||
};
|
|
||||||
|
|
||||||
return control;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MainForm() {
|
if(this.filePicker.FilePath == this.outputFileName.FilePath) {
|
||||||
this.Title = EasterEgg.Restitch ? "Union Restitcher" : "Union Patcher";
|
this.CreateOkDialog("Form Error", "Input and output filename are the same! Please save the patched file with a different name so you have a backup of your the original EBOOT.ELF.").ShowModal();
|
||||||
this.ClientSize = new Size(500, -1);
|
return;
|
||||||
this.Content = new TableLayout {
|
|
||||||
Spacing = new Size(5,5),
|
|
||||||
Padding = new Padding(10, 10, 10, 10),
|
|
||||||
Rows = {
|
|
||||||
new TableRow(
|
|
||||||
new TableCell(new Label { Text = "EBOOT.elf: ", VerticalAlignment = VerticalAlignment.Center }),
|
|
||||||
new TableCell(this.filePicker = new FilePicker { TabIndex = 0 })
|
|
||||||
),
|
|
||||||
new TableRow(
|
|
||||||
new TableCell(new Label { Text = "Server URL: ", VerticalAlignment = VerticalAlignment.Center }),
|
|
||||||
new TableCell(this.serverUrl = new TextBox { TabIndex = 1 })
|
|
||||||
),
|
|
||||||
new TableRow(
|
|
||||||
new TableCell(new Label { Text = "Output filename: ", VerticalAlignment = VerticalAlignment.Center }),
|
|
||||||
new TableCell(this.outputFileName = new FilePicker { TabIndex = 2, FileAction = FileAction.SaveFile })
|
|
||||||
),
|
|
||||||
new TableRow(
|
|
||||||
new TableCell(this.CreateHelpButton(4)),
|
|
||||||
new TableCell(this.CreatePatchButton(3))
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
private void Patch(object sender, EventArgs e) {
|
|
||||||
this.Patch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Patch() {
|
if(!Uri.TryCreate(this.serverUrl.Text, UriKind.Absolute, out _)) {
|
||||||
if(string.IsNullOrWhiteSpace(this.filePicker.FilePath)) {
|
this.CreateOkDialog("Form Error", "Server URL is invalid! Please enter a valid URL.").ShowModal();
|
||||||
this.CreateOkDialog("Form Error", "No file specified!").ShowModal();
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(this.serverUrl.Text)) {
|
|
||||||
this.CreateOkDialog("Form Error", "No server URL specified!").ShowModal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(this.outputFileName.FilePath)) {
|
|
||||||
this.CreateOkDialog("Form Error", "No output file specified!").ShowModal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.filePicker.FilePath == this.outputFileName.FilePath) {
|
|
||||||
this.CreateOkDialog("Form Error", "Input and output filename are the same! Please save the patched file with a different name so you have a backup of your the original EBOOT.ELF.").ShowModal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!Uri.TryCreate(this.serverUrl.Text, UriKind.Absolute, out _)) {
|
|
||||||
this.CreateOkDialog("Form Error", "Server URL is invalid! Please enter a valid URL.").ShowModal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate EBOOT after validating form; more expensive
|
|
||||||
|
|
||||||
ElfFile eboot = new(this.filePicker.FilePath);
|
|
||||||
|
|
||||||
if(eboot.IsValid == false) {
|
|
||||||
this.CreateOkDialog("EBOOT Error", $"{eboot.Name} is not a valid ELF file (magic number mismatch)\n" + "The EBOOT must be decrypted before using this tool").ShowModal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(eboot.Is64Bit == null) {
|
|
||||||
this.CreateOkDialog("EBOOT Error", $"{eboot.Name} does not target a valid system").ShowModal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(eboot.Architecture)) {
|
|
||||||
this.CreateOkDialog("EBOOT Error", $"{eboot.Name} does not target a valid architecture (PowerPC or ARM)").ShowModal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Patcher.PatchFile(this.filePicker.FilePath, this.serverUrl.Text, this.outputFileName.FilePath);
|
|
||||||
}
|
|
||||||
catch(Exception e) {
|
|
||||||
this.CreateOkDialog("Error occurred while patching", "An error occured while patching:\n" + e).ShowModal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.CreateOkDialog("Success!", "The Server URL has been patched to " + this.serverUrl.Text).ShowModal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate EBOOT after validating form; more expensive
|
||||||
|
|
||||||
|
ElfFile eboot = new(this.filePicker.FilePath);
|
||||||
|
|
||||||
|
if(eboot.IsValid == false) {
|
||||||
|
this.CreateOkDialog("EBOOT Error", $"{eboot.Name} is not a valid ELF file (magic number mismatch)\n" + "The EBOOT must be decrypted before using this tool").ShowModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(eboot.Is64Bit == null) {
|
||||||
|
this.CreateOkDialog("EBOOT Error", $"{eboot.Name} does not target a valid system").ShowModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(eboot.Architecture)) {
|
||||||
|
this.CreateOkDialog("EBOOT Error", $"{eboot.Name} does not target a valid architecture (PowerPC or ARM)").ShowModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Patcher.PatchFile(this.filePicker.FilePath, this.serverUrl.Text, this.outputFileName.FilePath);
|
||||||
|
}
|
||||||
|
catch(Exception e) {
|
||||||
|
this.CreateOkDialog("Error occurred while patching", "An error occured while patching:\n" + e).ShowModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.CreateOkDialog("Success!", "The Server URL has been patched to " + this.serverUrl.Text).ShowModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,78 +8,78 @@ Linux ELF header refspec: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eh
|
||||||
Wikipedia entry on ELF: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
Wikipedia entry on ELF: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace LBPUnion.UnionPatcher {
|
namespace LBPUnion.UnionPatcher;
|
||||||
public class ElfFile {
|
|
||||||
internal const int MinimumSize = 52;
|
|
||||||
|
|
||||||
private enum WordSize : byte {
|
public class ElfFile {
|
||||||
ThirtyTwoBits = 0x01,
|
internal const int MinimumSize = 52;
|
||||||
SixtyFourBits = 0x02,
|
|
||||||
}
|
private enum WordSize : byte {
|
||||||
|
ThirtyTwoBits = 0x01,
|
||||||
private enum Endianness : byte {
|
SixtyFourBits = 0x02,
|
||||||
Little = 0x01,
|
}
|
||||||
Big = 0x02,
|
|
||||||
}
|
private enum Endianness : byte {
|
||||||
|
Little = 0x01,
|
||||||
private enum InstructionSetArchitecture : UInt16 {
|
Big = 0x02,
|
||||||
PowerPC = 0x15, //64-bit PowerPC (PS3)
|
}
|
||||||
ARM = 0x28, //32-bit ARM (Vita)
|
|
||||||
}
|
private enum InstructionSetArchitecture : UInt16 {
|
||||||
|
PowerPC = 0x15, //64-bit PowerPC (PS3)
|
||||||
public string Name { get; } = "Binary Blob";
|
ARM = 0x28, //32-bit ARM (Vita)
|
||||||
|
}
|
||||||
public bool IsValid { get; } = false;
|
|
||||||
public bool? Is64Bit { get; } = null;
|
public string Name { get; } = "Binary Blob";
|
||||||
public bool? IsBigEndian { get; } = null;
|
|
||||||
public string Architecture { get; } = null;
|
public bool IsValid { get; } = false;
|
||||||
|
public bool? Is64Bit { get; } = null;
|
||||||
public byte[] Contents { get; } = null;
|
public bool? IsBigEndian { get; } = null;
|
||||||
|
public string Architecture { get; } = null;
|
||||||
public ElfFile(byte[] fileContents) {
|
|
||||||
if(fileContents.Length < MinimumSize)
|
public byte[] Contents { get; } = null;
|
||||||
return;
|
|
||||||
|
public ElfFile(byte[] fileContents) {
|
||||||
IsValid = fileContents[..0x04].SequenceEqual(new byte[] {
|
if(fileContents.Length < MinimumSize)
|
||||||
0x7F,
|
return;
|
||||||
(byte)'E',
|
|
||||||
(byte)'L',
|
IsValid = fileContents[..0x04].SequenceEqual(new byte[] {
|
||||||
(byte)'F',
|
0x7F,
|
||||||
});
|
(byte)'E',
|
||||||
|
(byte)'L',
|
||||||
if(!IsValid) return;
|
(byte)'F',
|
||||||
|
});
|
||||||
byte identClassValue = fileContents[0x04];
|
|
||||||
byte identDataValue = fileContents[0x05];
|
if(!IsValid) return;
|
||||||
|
|
||||||
if(identClassValue == (byte)WordSize.ThirtyTwoBits || identClassValue == (byte)WordSize.SixtyFourBits)
|
byte identClassValue = fileContents[0x04];
|
||||||
Is64Bit = identClassValue == (byte)WordSize.SixtyFourBits;
|
byte identDataValue = fileContents[0x05];
|
||||||
|
|
||||||
if(identDataValue == (byte)Endianness.Little || identDataValue == (byte)Endianness.Big)
|
if(identClassValue == (byte)WordSize.ThirtyTwoBits || identClassValue == (byte)WordSize.SixtyFourBits)
|
||||||
IsBigEndian = identDataValue == (byte)Endianness.Big;
|
Is64Bit = identClassValue == (byte)WordSize.SixtyFourBits;
|
||||||
|
|
||||||
Architecture = GetFileArchitecture(fileContents, IsBigEndian == true);
|
if(identDataValue == (byte)Endianness.Little || identDataValue == (byte)Endianness.Big)
|
||||||
|
IsBigEndian = identDataValue == (byte)Endianness.Big;
|
||||||
Contents = fileContents;
|
|
||||||
}
|
Architecture = GetFileArchitecture(fileContents, IsBigEndian == true);
|
||||||
|
|
||||||
public ElfFile(FileInfo file) : this(File.ReadAllBytes(file.FullName)) {
|
Contents = fileContents;
|
||||||
Name = file.Name;
|
}
|
||||||
}
|
|
||||||
|
public ElfFile(FileInfo file) : this(File.ReadAllBytes(file.FullName)) {
|
||||||
public ElfFile(string fileName) : this(new FileInfo(fileName)) {}
|
Name = file.Name;
|
||||||
|
}
|
||||||
private string GetFileArchitecture(byte[] elfHeader, bool isBigEndian) {
|
|
||||||
byte[] architectureBytes = elfHeader[0x12..0x14];
|
public ElfFile(string fileName) : this(new FileInfo(fileName)) {}
|
||||||
UInt16 fileArch = (isBigEndian) ?
|
|
||||||
BinaryPrimitives.ReadUInt16BigEndian(architectureBytes) :
|
private string GetFileArchitecture(byte[] elfHeader, bool isBigEndian) {
|
||||||
BinaryPrimitives.ReadUInt16LittleEndian(architectureBytes);
|
byte[] architectureBytes = elfHeader[0x12..0x14];
|
||||||
|
UInt16 fileArch = (isBigEndian) ?
|
||||||
foreach(InstructionSetArchitecture arch in Enum.GetValues(typeof(InstructionSetArchitecture))) {
|
BinaryPrimitives.ReadUInt16BigEndian(architectureBytes) :
|
||||||
if(fileArch == (UInt16)arch)
|
BinaryPrimitives.ReadUInt16LittleEndian(architectureBytes);
|
||||||
return arch.ToString();
|
|
||||||
}
|
foreach(InstructionSetArchitecture arch in Enum.GetValues(typeof(InstructionSetArchitecture))) {
|
||||||
return null;
|
if(fileArch == (UInt16)arch)
|
||||||
|
return arch.ToString();
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,104 +2,104 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace LBPUnion.UnionPatcher {
|
namespace LBPUnion.UnionPatcher;
|
||||||
public static class Patcher {
|
|
||||||
private static readonly string[] toBePatched = {
|
|
||||||
// Normal LittleBigPlanet gameserver URLs
|
|
||||||
"https://littlebigplanetps3.online.scee.com:10061/LITTLEBIGPLANETPS3_XML",
|
|
||||||
"http://littlebigplanetps3.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
|
||||||
// LittleBigPlanet 3 Presence URLs
|
|
||||||
"http://live.littlebigplanetps3.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
|
||||||
"http://presence.littlebigplanetps3.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
|
||||||
#region Spinoff URLs
|
|
||||||
// LittleBigPlanet PSP URLs
|
|
||||||
"http://lbppsp.online.scee.com:10060/LITTLEBIGPLANETPSP_XML",
|
|
||||||
"https://lbppsp.online.scee.com:10061/LITTLEBIGPLANETPSP_XML",
|
|
||||||
// LittleBigPlanet Vita URLs
|
|
||||||
"http://lbpvita.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
|
||||||
"https://lbpvita.online.scee.com:10061/LITTLEBIGPLANETPS3_XML",
|
|
||||||
#endregion
|
|
||||||
#region Beta URLS
|
|
||||||
// LittleBigPlanet 2 Beta URLs
|
|
||||||
"http://lbp2ps3-beta.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
|
||||||
"https://lbp2ps3-beta.online.scee.com:10061/LITTLEBIGPLANETPS3_XML",
|
|
||||||
// LittleBigPlanet (3?) Beta URLs
|
|
||||||
"http://littlebigplanetps3-beta.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
|
||||||
"https://littlebigplanetps3-beta.online.scee.com:10061/LITTLEBIGPLANETPS3_XML",
|
|
||||||
// LittleBigPlanet Vita Beta URLs
|
|
||||||
"http://lbpvita-beta.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
|
||||||
"https://lbpvita-beta.online.scee.com:10061/LITTLEBIGPLANETPS3_XML",
|
|
||||||
#endregion
|
|
||||||
};
|
|
||||||
|
|
||||||
public static void PatchFile(string fileName, Uri serverUrl, string outputFileName) {
|
public static class Patcher {
|
||||||
PatchFile(fileName, serverUrl.ToString(), outputFileName);
|
private static readonly string[] toBePatched = {
|
||||||
|
// Normal LittleBigPlanet gameserver URLs
|
||||||
|
"https://littlebigplanetps3.online.scee.com:10061/LITTLEBIGPLANETPS3_XML",
|
||||||
|
"http://littlebigplanetps3.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
||||||
|
// LittleBigPlanet 3 Presence URLs
|
||||||
|
"http://live.littlebigplanetps3.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
||||||
|
"http://presence.littlebigplanetps3.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
||||||
|
#region Spinoff URLs
|
||||||
|
// LittleBigPlanet PSP URLs
|
||||||
|
"http://lbppsp.online.scee.com:10060/LITTLEBIGPLANETPSP_XML",
|
||||||
|
"https://lbppsp.online.scee.com:10061/LITTLEBIGPLANETPSP_XML",
|
||||||
|
// LittleBigPlanet Vita URLs
|
||||||
|
"http://lbpvita.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
||||||
|
"https://lbpvita.online.scee.com:10061/LITTLEBIGPLANETPS3_XML",
|
||||||
|
#endregion
|
||||||
|
#region Beta URLS
|
||||||
|
// LittleBigPlanet 2 Beta URLs
|
||||||
|
"http://lbp2ps3-beta.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
||||||
|
"https://lbp2ps3-beta.online.scee.com:10061/LITTLEBIGPLANETPS3_XML",
|
||||||
|
// LittleBigPlanet (3?) Beta URLs
|
||||||
|
"http://littlebigplanetps3-beta.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
||||||
|
"https://littlebigplanetps3-beta.online.scee.com:10061/LITTLEBIGPLANETPS3_XML",
|
||||||
|
// LittleBigPlanet Vita Beta URLs
|
||||||
|
"http://lbpvita-beta.online.scee.com:10060/LITTLEBIGPLANETPS3_XML",
|
||||||
|
"https://lbpvita-beta.online.scee.com:10061/LITTLEBIGPLANETPS3_XML",
|
||||||
|
#endregion
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void PatchFile(string fileName, Uri serverUrl, string outputFileName) {
|
||||||
|
PatchFile(fileName, serverUrl.ToString(), outputFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PatchFile(string fileName, string serverUrl, string outputFileName) {
|
||||||
|
File.WriteAllBytes(outputFileName, PatchData(File.ReadAllBytes(fileName), serverUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] PatchData(byte[] data, Uri serverUrl) {
|
||||||
|
return PatchData(data, serverUrl.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] PatchData(byte[] data, string serverUrl) {
|
||||||
|
#region Validation
|
||||||
|
if(serverUrl.EndsWith('/')) {
|
||||||
|
throw new ArgumentException("URL must not contain a trailing slash!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PatchFile(string fileName, string serverUrl, string outputFileName) {
|
// Attempt to create URI to see if it's valid
|
||||||
File.WriteAllBytes(outputFileName, PatchData(File.ReadAllBytes(fileName), serverUrl));
|
if(!Uri.TryCreate(serverUrl, UriKind.RelativeOrAbsolute, out _)) {
|
||||||
|
throw new Exception("URL must be valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] PatchData(byte[] data, Uri serverUrl) {
|
if(serverUrl.Length > data.Length) {
|
||||||
return PatchData(data, serverUrl.ToString());
|
throw new ArgumentException("URL cannot be bigger than the file to patch.");
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
string dataAsString = Encoding.ASCII.GetString(data);
|
||||||
|
|
||||||
|
using MemoryStream ms = new(data);
|
||||||
|
using BinaryWriter writer = new(ms);
|
||||||
|
|
||||||
|
// Using writer.Write(string) writes the length as a byte beforehand
|
||||||
|
// This is problematic because LBP (being written in C/C++) uses null-terminated strings,
|
||||||
|
// as opposed to length-text written strings as C# likes to serialize.
|
||||||
|
byte[] serverUrlAsBytes = Encoding.ASCII.GetBytes(serverUrl);
|
||||||
|
|
||||||
|
bool wroteUrl = false;
|
||||||
|
foreach(string url in toBePatched) {
|
||||||
|
if(serverUrl.Length > url.Length) {
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(serverUrl), $"Server URL ({serverUrl.Length} characters long) is above maximum length {url.Length}");
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = dataAsString.IndexOf(url, StringComparison.Ordinal);
|
||||||
|
if(offset < 1) continue;
|
||||||
|
|
||||||
|
writer.BaseStream.Position = offset;
|
||||||
|
for(int i = 0; i < url.Length; i++) {
|
||||||
|
writer.Write((byte)0x00); // Zero out data
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.BaseStream.Position = offset; // Reset position to beginning
|
||||||
|
writer.Write(serverUrlAsBytes);
|
||||||
|
|
||||||
|
wroteUrl = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] PatchData(byte[] data, string serverUrl) {
|
if(!wroteUrl) {
|
||||||
#region Validation
|
throw new Exception("No patchable URLs were detected in the " +
|
||||||
if(serverUrl.EndsWith('/')) {
|
"provided file. Please make sure you are patching " +
|
||||||
throw new ArgumentException("URL must not contain a trailing slash!");
|
"the correct file.");
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to create URI to see if it's valid
|
|
||||||
if(!Uri.TryCreate(serverUrl, UriKind.RelativeOrAbsolute, out _)) {
|
|
||||||
throw new Exception("URL must be valid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(serverUrl.Length > data.Length) {
|
|
||||||
throw new ArgumentException("URL cannot be bigger than the file to patch.");
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
string dataAsString = Encoding.ASCII.GetString(data);
|
|
||||||
|
|
||||||
using MemoryStream ms = new(data);
|
|
||||||
using BinaryWriter writer = new(ms);
|
|
||||||
|
|
||||||
// Using writer.Write(string) writes the length as a byte beforehand
|
|
||||||
// This is problematic because LBP (being written in C/C++) uses null-terminated strings,
|
|
||||||
// as opposed to length-text written strings as C# likes to serialize.
|
|
||||||
byte[] serverUrlAsBytes = Encoding.ASCII.GetBytes(serverUrl);
|
|
||||||
|
|
||||||
bool wroteUrl = false;
|
|
||||||
foreach(string url in toBePatched) {
|
|
||||||
if(serverUrl.Length > url.Length) {
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(serverUrl), $"Server URL ({serverUrl.Length} characters long) is above maximum length {url.Length}");
|
|
||||||
}
|
|
||||||
|
|
||||||
int offset = dataAsString.IndexOf(url, StringComparison.Ordinal);
|
|
||||||
if(offset < 1) continue;
|
|
||||||
|
|
||||||
writer.BaseStream.Position = offset;
|
|
||||||
for(int i = 0; i < url.Length; i++) {
|
|
||||||
writer.Write((byte)0x00); // Zero out data
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.BaseStream.Position = offset; // Reset position to beginning
|
|
||||||
writer.Write(serverUrlAsBytes);
|
|
||||||
|
|
||||||
wroteUrl = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!wroteUrl) {
|
|
||||||
throw new Exception("No patchable URLs were detected in the " +
|
|
||||||
"provided file. Please make sure you are patching " +
|
|
||||||
"the correct file.");
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Flush();
|
|
||||||
writer.Close();
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writer.Flush();
|
||||||
|
writer.Close();
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue