Compare commits

...

22 commits

Author SHA1 Message Date
Zaprit
c45b9ec37e
Remove macOS staging directory requirement, and update to .NET 8.0 (#41)
* Code cleanup and update to .NET 8

* Fix macOS requiring staging directory to be selected.
Also places eboots on Desktop on macos, to avoid them being inside the app bundle.

* Fix keys discovery on mac

* Removed debug logging, made GetExecutablePath more robust.

* Created a script for building on mac, as creating a universal binary is somewhat involved

* Update UnionPatcher.Gui/Forms/ModeSelectionForm.cs

Co-authored-by: sudokoko <koko@sudokoko.xyz>

* Apply suggestions from code review

Co-authored-by: Josh <josh@slendy.pw>

---------

Co-authored-by: sudokoko <koko@sudokoko.xyz>
Co-authored-by: Josh <josh@slendy.pw>
2024-01-19 00:05:42 +00:00
Dagg
7b88e27843
Require staging directory as fallback (#34)
* Test requiring staging directory on Windows

* Special workaround v1

* Remove variable since its only used once
2023-03-18 23:38:21 -05:00
Dagg
47af7d6929
macOS Hotfix: Require staging directory (#31)
* macOS: Require staging directory

* Grammar
2022-11-09 16:36:26 -06:00
Josh
58d9dd3a67
Make the file picker only show valid files (#28)
* Make the file picker only show valid files

* Fix file wildcard patterns
2022-11-02 13:26:30 -06:00
Dagg
7d77decf17
Swap out the custom message boxes with the stock Eto MessageBox (#29)
* Use regex to discover existing server urls

- Supports LBP PSP and PS3
- Hardcoded length limit, will fix soonTM

* Get maximum length for server URL from EBOOT

* Potential fix for LBP3
- Matches all but NULL instead of just all

* Potentially extend maximum length
Thanks slendy

* Leave a NULL character of padding

* Use stock eto message boxes instead of custom.

* Use Information message box type
Ding may be strange but this process takes a while sometimes
2022-11-02 13:26:15 -06:00
Dagg
3d8473129a
Added regex matching to find index of LBP server URLs (#27)
* Use regex to discover existing server urls

- Supports LBP PSP and PS3
- Hardcoded length limit, will fix soonTM

* Get maximum length for server URL from EBOOT

* Potential fix for LBP3
- Matches all but NULL instead of just all

* Potentially extend maximum length
Thanks slendy

* Leave a NULL character of padding
2022-09-13 17:15:04 -05:00
jvyden
8f6ba8b535
Make zip function recursive when building 2022-08-06 13:49:27 -04:00
jvyden
98dd90a9d5
Merge remote-tracking branch 'origin/master'
# Conflicts:
#	.gitignore
2022-07-28 17:07:17 -04:00
jvyden
fea80d4485
Add shell script to build all UP versions 2022-07-28 17:06:56 -04:00
Zaprit
bb4d691364
Added ARM Linux support (#23)
* Ignored .DS_Store

* Add arm64 linux scetool and code to work with it

* added linux scetool for arm

* Left some vestigial code, whoops
2022-07-28 16:46:37 -04:00
jvyden
159dd9f3cc
Fix psp architecture value 2022-07-26 21:50:15 -04:00
jvyden
4b566c35fa
Add PSP support to ELF checking 2022-07-26 21:41:03 -04:00
Zaprit
7b45a190c3
Update Eto.Forms and add arm64 macOS build support (#22)
* Updated Eto.Forms to 2.7.0 and added build support for osx-arm64

* update to .NET 6.0
2022-07-25 14:31:48 -04:00
Logan Lowe
2872f8b2f1
Merge pull request #21 from Zaprit/code-cleanup
Cleaned up warnings in code
2022-06-23 11:33:30 -06:00
Henry Asbridge
b4d28966c7 Cleaned up warnings in code 2022-06-23 15:50:26 +01:00
Logan Lowe
1c39468343
Merge pull request #20 from Zaprit/master
Added macOS SCETool
2022-06-22 17:18:51 -06:00
Henry Asbridge
412ff21844 Added code to use SCETool for macOS 2022-06-22 01:26:51 +01:00
Henry Asbridge
eed59004f0 Added SCEtool for macOS 2022-06-22 01:25:30 +01:00
jvyden
4cea9710e3
Include scetool and data directories 2022-06-15 17:47:10 -04:00
Jayden
8c8aba3287
Merge pull request #19 from AShifter/master
Fix application closing when choosing mode on Windows
2022-06-15 16:58:55 -04:00
Logan Lowe
b872ebaee6 Fix application closing when choosing mode on Windows
When a new sub form is opened, the main form's ``Close()`` method is called, which halts the entire application on Windows. Instead, we create an instance of the sub form, and bind its ``Closed`` event to an event handler that will halt the entire application.

We can then hide the main form, rather than closing it completely, which resolves the issue and allows the app to run as intended.
2022-06-15 14:55:59 -06:00
Jayden
7191d00d26
[skip ci] Update obtaining build guide 2022-06-14 22:26:12 -04:00
34 changed files with 5182 additions and 194 deletions

View file

@ -14,17 +14,17 @@ jobs:
fail-fast: false
matrix:
os:
- { prettyName: Windows, platform: windows-latest, configurationName: Windows, extraArgs: "", buildPath: "Release/net6.0-windows/publish"}
- { prettyName: Linux, platform: ubuntu-latest, configurationName: Linux, extraArgs: "", buildPath: "Release/net6.0/publish"}
- { prettyName: Windows, platform: windows-latest, configurationName: Windows, extraArgs: "", buildPath: "Release/net8.0-windows/publish"}
- { prettyName: Linux, platform: ubuntu-latest, configurationName: Linux, extraArgs: "", buildPath: "Release/net8.0/publish"}
# - { prettyName: MacOS, platform: ubuntu-latest, configurationName: Release, platform: osx-x64 }
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install .NET 6.0
- name: Install .NET 8.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: "6.0.x"
dotnet-version: "8.0.x"
- name: Compile for ${{ matrix.os.prettyName }}
run: dotnet publish -c ${{ matrix.os.configurationName }} ${{ matrix.os.extraArgs }}

3
.gitignore vendored
View file

@ -396,3 +396,6 @@ FodyWeavers.xsd
# JetBrains Rider
*.sln.iml
.DS_Store
builds/

View file

@ -9,6 +9,7 @@
<Path>README.md</Path>
<Path>UnionPatcher.sln.DotSettings</Path>
<Path>UnionPatcher.sln.DotSettings.user</Path>
<Path>build-all.sh</Path>
<Path>global.json</Path>
</explicitIncludes>
<explicitExcludes />

View file

@ -1,15 +1,13 @@
# UnionPatcher
A tool that will take the official EBOOT of a LittleBIGPlanet title and replace the server URLs with a custom one
A tool that will take the official EBOOT of a LittleBigPlanet title and replace the server URLs with a custom one
## Prerequisites
* For running the console application, you will need the [.NET 6 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/6.0/runtime?utm_source=getdotnetcore&utm_medium=referral)
* You will need to know the server URL you wish to use
## Getting the latest build
1. Access the [CI builds for UnionPatcher](https://github.com/LBPUnion/UnionPatcher/actions)
2. In the "workflow runs" grid, select the first in the list with a green check mark (✔️)
3. Scroll to the bottom of this build and find the correct zip for your platform
You can obtain UnionPatcher builds via GitHub releases: https://github.com/LBPUnion/UnionPatcher/releases
## Building manually (Required for MacOS)
You will need the [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>UnionPatcher</AssemblyName>

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifiers>linux-x64</RuntimeIdentifiers>
<AssemblyName>LBPUnion.UnionPatcher.Gui.Linux</AssemblyName>
<RootNamespace>LBPUnion.UnionPatcher.Gui.Linux</RootNamespace>
@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Eto.Platform.Gtk" Version="2.5.11" />
<PackageReference Include="Eto.Platform.Gtk" Version="2.7.0" />
</ItemGroup>
<ItemGroup>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.lbpunion.unionpatcher</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright LBP Union 2024©</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleIconFile</key>
<string>Icon.icns</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>CFBundleName</key>
<string>UnionPatcher</string>
<key>CFBundleExecutable</key>
<string>LBPUnion.UnionPatcher.Gui.MacOS</string>
<key>LSMinimumSystemVersion</key>
<string>10.14</string>
<key>NSRequiresAquaSystemAppearance</key>
<string>False</string>
</dict>
</plist>

View file

@ -2,11 +2,13 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>osx-x64</RuntimeIdentifiers>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifiers>osx-x64;osx-arm64</RuntimeIdentifiers>
<AssemblyName>LBPUnion.UnionPatcher.Gui.MacOS</AssemblyName>
<RootNamespace>LBPUnion.UnionPatcher.Gui.MacOS</RootNamespace>
<ApplicationIcon>Icon64.ico</ApplicationIcon>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
</PropertyGroup>
<ItemGroup>
@ -16,6 +18,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Eto.Platform.Mac64" Version="2.5.11" />
<PackageReference Include="Eto.Platform.Mac64" Version="2.7.0" />
</ItemGroup>
</Project>

View file

@ -2,21 +2,24 @@
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<RootNamespace>LBPUnion.UnionPatcher.Gui.Windows</RootNamespace>
<AssemblyName>LBPUnion.UnionPatcher.Gui.Windows</AssemblyName>
<ApplicationIcon>Icon64.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Eto.Platform.Direct2D" Version="2.5.11" />
<PackageReference Include="Eto.Platform.Direct2D" Version="2.7.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UnionPatcher.Gui\UnionPatcher.Gui.csproj" />
<ProjectReference Include="..\UnionPatcher.Gui\UnionPatcher.Gui.csproj">
<ReferenceSourceTarget></ReferenceSourceTarget>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<KnownFrameworkReference Update="Microsoft.WindowsDesktop.App.WindowsForms" IsWindowsOnly="false" />
<KnownFrameworkReference Update="Microsoft.WindowsDesktop.App" IsWindowsOnly="false"/>
</ItemGroup>
</Project>

View file

@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
using Eto;
using Eto.Drawing;
using Eto.Forms;
@ -49,7 +50,7 @@ public class FilePatchForm : Form {
Rows = {
new TableRow(
new TableCell(new Label { Text = "EBOOT.elf: ", VerticalAlignment = VerticalAlignment.Center }),
new TableCell(this.filePicker = new FilePicker { TabIndex = 0 })
new TableCell(this.filePicker = new FilePicker { TabIndex = 0 , FileAction = FileAction.OpenFile, Filters = { new FileFilter("ELF files", "*.elf", "*.ELF"), new FileFilter("All Files", "*.*") }})
),
new TableRow(
new TableCell(new Label { Text = "Server URL: ", VerticalAlignment = VerticalAlignment.Center }),
@ -57,7 +58,7 @@ public class FilePatchForm : Form {
),
new TableRow(
new TableCell(new Label { Text = "Output filename: ", VerticalAlignment = VerticalAlignment.Center }),
new TableCell(this.outputFileName = new FilePicker { TabIndex = 2, FileAction = FileAction.SaveFile })
new TableCell(this.outputFileName = new FilePicker { TabIndex = 2, FileAction = FileAction.SaveFile, Filters = { new FileFilter("ELF files", "*.elf", "*.ELF"), new FileFilter("All Files", "*.*") }})
),
new TableRow(
new TableCell(this.CreateHelpButton(4)),
@ -73,46 +74,54 @@ public class FilePatchForm : Form {
private void Patch() {
if(string.IsNullOrWhiteSpace(this.filePicker.FilePath)) {
Gui.CreateOkDialog("Form Error", "No file specified!").ShowModal();
Gui.CreateOkDialog("Form Error", "No file specified!");
return;
}
if(string.IsNullOrWhiteSpace(this.serverUrl.Text)) {
Gui.CreateOkDialog("Form Error", "No server URL specified!").ShowModal();
Gui.CreateOkDialog("Form Error", "No server URL specified!");
return;
}
if(string.IsNullOrWhiteSpace(this.outputFileName.FilePath)) {
Gui.CreateOkDialog("Form Error", "No output file specified!").ShowModal();
Gui.CreateOkDialog("Form Error", "No output file specified!");
return;
}
if(this.filePicker.FilePath == this.outputFileName.FilePath) {
Gui.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();
Gui.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.");
return;
}
if(!Uri.TryCreate(this.serverUrl.Text, UriKind.Absolute, out _)) {
Gui.CreateOkDialog("Form Error", "Server URL is invalid! Please enter a valid URL.").ShowModal();
Gui.CreateOkDialog("Form Error", "Server URL is invalid! Please enter a valid URL.");
return;
}
if(!Regex.IsMatch(this.serverUrl.Text, "LITTLEBIGPLANETPS3_XML")) {
bool userCertain = Gui.CreateConfirmationDialog("URL Mistype", $"Server URL {this.serverUrl.Text} does not match LITTLEBIGPLANETPS3_XML, are you sure you want to use this?");
if (!userCertain) {
return;
}
// else, godspeed, captain
}
// Validate EBOOT after validating form; more expensive
ElfFile eboot = new(this.filePicker.FilePath);
if(eboot.IsValid == false) {
Gui.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();
Gui.CreateOkDialog("EBOOT Error", $"{eboot.Name} is not a valid ELF file (magic number mismatch)\n" + "The EBOOT must be decrypted before using this tool");
return;
}
if(eboot.Is64Bit == null) {
Gui.CreateOkDialog("EBOOT Error", $"{eboot.Name} does not target a valid system").ShowModal();
Gui.CreateOkDialog("EBOOT Error", $"{eboot.Name} does not target a valid system");
return;
}
if(string.IsNullOrWhiteSpace(eboot.Architecture)) {
Gui.CreateOkDialog("EBOOT Error", $"{eboot.Name} does not target a valid architecture (PowerPC or ARM)").ShowModal();
Gui.CreateOkDialog("EBOOT Error", $"{eboot.Name} does not target a valid architecture (PowerPC or ARM)");
return;
}
@ -120,10 +129,10 @@ public class FilePatchForm : Form {
Patcher.PatchFile(this.filePicker.FilePath, this.serverUrl.Text, this.outputFileName.FilePath);
}
catch(Exception e) {
Gui.CreateOkDialog("Error occurred while patching", "An error occured while patching:\n" + e).ShowModal();
Gui.CreateOkDialog("Error occurred while patching", "An error occured while patching:\n" + e);
return;
}
Gui.CreateOkDialog("Success!", "The Server URL has been patched to " + this.serverUrl.Text).ShowModal();
Gui.CreateOkDialog("Success!", "The Server URL has been patched to " + this.serverUrl.Text);
}
}
}

View file

@ -1,8 +1,12 @@
using System;
using System.IO;
using System.Reflection;
using System.Text;
using Eto;
using Eto.Drawing;
using Eto.Forms;
namespace LBPUnion.UnionPatcher.Gui.Forms;
namespace LBPUnion.UnionPatcher.Gui.Forms;
public class ModeSelectionForm : Form {
#region UI
@ -23,18 +27,50 @@ public class ModeSelectionForm : Form {
new TableCell(new Button(openFilePatcher) { Text = "File Patch (PS3/RPCS3)" })
),
},
};
};
}
private void openRemotePatcher(object sender, EventArgs e) {
new RemotePatchForm().Show();
this.Close();
private void openRemotePatcher(object sender, EventArgs e)
{
// If we're on macOS then set the CWD to the app bundle MacOS folder, so that SCETool can be found.
if (OSUtil.GetPlatform() == OSPlatform.OSX) Directory.SetCurrentDirectory(OSUtil.GetExecutablePath());
if (!Directory.Exists($"{OSUtil.GetExecutablePath()}/scetool"))
{
// This will always occur on macOS, so don't show this message for macOS users.
if (OSUtil.GetPlatform() != OSPlatform.OSX) Gui.CreateOkDialog("Workaround Triggered", ".NET could not locate the required files, triggering workaround.");
Gui.CreateOkDialog("Workaround", "UnionPatcher RemotePatcher requires a staging folder on macOS or in special circumstances on Windows, please set this to the directory of the UnionPatcher app or executable!");
SelectFolderDialog dialog = new SelectFolderDialog();
if (dialog.ShowDialog(this) != DialogResult.Ok)
{
Gui.CreateOkDialog("Workaround", "User did not specify a staging folder, aborting!");
return;
}
Directory.SetCurrentDirectory(dialog.Directory);
if (!Directory.Exists("scetool"))
{
Gui.CreateOkDialog("Workaround", "Invalid folder, remember to set the folder to the directory of the UnionPatcher app or executable!");
return;
}
}
RemotePatchForm rpForm = new RemotePatchForm();
rpForm.Show();
rpForm.Closed += OnSubFormClose;
this.Visible = false;
}
private void openLocalPatcher(object sender, EventArgs e) {
throw new NotImplementedException();
}
private void openFilePatcher(object sender, EventArgs e) {
new FilePatchForm().Show();
FilePatchForm fpForm = new FilePatchForm();
fpForm.Show();
fpForm.Closed += OnSubFormClose;
this.Visible = false;
}
private void OnSubFormClose(object sender, EventArgs e)
{
this.Close();
}

View file

@ -34,25 +34,25 @@ public class RemotePatchForm : Form
control.Click += delegate {
if (string.IsNullOrEmpty(this.ps3LocalIP.Text))
{
Gui.CreateOkDialog("Error", "No PS3 IP address specified!").ShowModal();
Gui.CreateOkDialog("Error", "No PS3 IP address specified!");
return;
}
if (string.IsNullOrEmpty(this.lbpGameID.Text))
{
Gui.CreateOkDialog("Error", "No title ID specified!").ShowModal();
Gui.CreateOkDialog("Error", "No title ID specified!");
return;
}
if (string.IsNullOrEmpty(this.serverUrl.Text))
{
Gui.CreateOkDialog("Error", "No server URL specified!").ShowModal();
Gui.CreateOkDialog("Error", "No server URL specified!");
return;
}
if (!Uri.TryCreate(this.serverUrl.Text, UriKind.Absolute, out _))
{
Gui.CreateOkDialog("Error", "Server URL is invalid! Please enter a valid URL.").ShowModal();
Gui.CreateOkDialog("Error", "Server URL is invalid! Please enter a valid URL.");
return;
}
@ -69,11 +69,11 @@ public class RemotePatchForm : Form
}
catch (Exception e)
{
Gui.CreateOkDialog("Error occurred while patching", "An error occured while patching:\n" + e).ShowModal();
Gui.CreateOkDialog("Error occurred while patching", "An error occured while patching:\n" + e);
return;
}
Gui.CreateOkDialog("Success!", $"The Server URL for {this.lbpGameID.Text} on the PS3 at {this.ps3LocalIP.Text} has been patched to {this.serverUrl.Text}").ShowModal();
Gui.CreateOkDialog("Success!", $"The Server URL for {this.lbpGameID.Text} on the PS3 at {this.ps3LocalIP.Text} has been patched to {this.serverUrl.Text}");
};
return control;
@ -91,13 +91,13 @@ public class RemotePatchForm : Form
control.Click += delegate {
if (string.IsNullOrEmpty(this.ps3LocalIP.Text))
{
Gui.CreateOkDialog("Form Error", "No PS3 IP address specified!").ShowModal();
Gui.CreateOkDialog("Form Error", "No PS3 IP address specified!");
return;
}
if (string.IsNullOrEmpty(this.lbpGameID.Text))
{
Gui.CreateOkDialog("Form Error", "No game ID specified!").ShowModal();
Gui.CreateOkDialog("Form Error", "No game ID specified!");
return;
}
@ -107,11 +107,11 @@ public class RemotePatchForm : Form
}
catch (Exception e)
{
Gui.CreateOkDialog("Error occurred while reverting EBOOT", "An error occured while patching:\n" + e).ShowModal();
Gui.CreateOkDialog("Error occurred while reverting EBOOT", "An error occured while patching:\n" + e);
return;
}
Gui.CreateOkDialog("Success!", $"UnionRemotePatcher reverted your the EBOOT for {this.lbpGameID.Text} to stock. You're ready to patch your EBOOT again.").ShowModal();
Gui.CreateOkDialog("Success!", $"UnionRemotePatcher reverted your the EBOOT for {this.lbpGameID.Text} to stock. You're ready to patch your EBOOT again.");
};
return control;

View file

@ -1,5 +1,4 @@
using Eto.Drawing;
using Eto.Forms;
using Eto.Forms;
using LBPUnion.UnionPatcher.Gui.Forms;
namespace LBPUnion.UnionPatcher.Gui;
@ -9,39 +8,11 @@ public static class Gui {
new Application().Run(new ModeSelectionForm());
}
public static Dialog CreateOkDialog(string title, string errorMessage) {
DynamicLayout layout = new();
Button button;
layout.Spacing = new Size(5, 5);
layout.MinimumSize = new Size(350, 100);
layout.BeginHorizontal();
layout.Add(new Label {
Text = errorMessage,
});
layout.BeginHorizontal();
layout.BeginVertical();
layout.Add(null);
layout.Add(button = new Button {
Text = "OK",
});
layout.EndVertical();
layout.EndHorizontal();
layout.EndHorizontal();
Dialog dialog = new() {
Content = layout,
Padding = new Padding(10, 10, 10, 10),
Title = title,
};
button.Click += delegate {
dialog.Close();
};
return dialog;
public static void CreateOkDialog(string title, string errorMessage) {
MessageBox.Show(errorMessage, title, MessageBoxButtons.OK);
}
public static bool CreateConfirmationDialog(string title, string errorMessage) {
DialogResult result = MessageBox.Show(errorMessage, title, MessageBoxButtons.YesNo, MessageBoxType.Question);
return result == DialogResult.Yes;
}
}

View file

@ -7,15 +7,15 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' != 'Windows' ">
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Windows' ">
<TargetFramework>net6.0-windows</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Eto.Forms" Version="2.5.11" />
<PackageReference Include="Eto.Forms" Version="2.7.0" />
</ItemGroup>
<ItemGroup>

View file

@ -5,13 +5,15 @@ using System.IO;
using System.Linq;
using System.Net;
namespace LBPUnion.UnionPatcher.Communication;
#pragma warning disable SYSLIB0014 // the FtpWebRequest is needed in this case
namespace LBPUnion.UnionPatcher.Communication;
public static class FTP
{
public static bool FileExists(string url, string user, string pass)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.Credentials = new NetworkCredential(user, pass);
request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
@ -36,6 +38,7 @@ public static class FTP
public static string[] ListDirectory(string url, string user, string pass)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.Credentials = new NetworkCredential(user, pass);
request.Method = WebRequestMethods.Ftp.ListDirectory;
@ -53,7 +56,7 @@ public static class FTP
.Where(dir => !string.IsNullOrWhiteSpace(dir) && dir != "." && dir != "..")
.ToList();
foreach(string dir in dirs.ToArray())
foreach (string dir in dirs.ToArray())
{
Console.WriteLine($"/{dir}");
}
@ -128,7 +131,7 @@ public static class FTP
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
using StreamReader reader = new(responseStream);
return reader.ReadToEnd();

View file

@ -12,7 +12,7 @@ using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
namespace LBPUnion.UnionPatcher.Communication;
namespace LBPUnion.UnionPatcher.Communication;
public class PS3MAPI
{
@ -102,7 +102,7 @@ public class PS3MAPI
Double = 2,
Triple = 3,
}
/// <summary>Ring PS3 Buzzer.</summary>
/// <param name="mode">Simple, Double, Continuous</param>
public void RingBuzzer(BuzzerMode mode)
@ -234,7 +234,7 @@ public class PS3MAPI
}
}
main_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
main_ipEndPoint = new IPEndPoint(Dns.GetHostByName(sServerIP).AddressList[0], Port);
main_ipEndPoint = new IPEndPoint(Dns.GetHostAddresses(sServerIP)[0], Port);
try
{
main_sock.Connect(main_ipEndPoint);
@ -781,12 +781,12 @@ public class PS3MAPI
SetBinaryMode(false);
}
}
catch (Exception e)
catch
{
CloseDataSocket();
ReadResponse();
SetBinaryMode(false);
throw e;
throw;
}
}
}
@ -847,12 +847,12 @@ public class PS3MAPI
SetBinaryMode(false);
}
}
catch (Exception e)
catch
{
CloseDataSocket();
ReadResponse();
SetBinaryMode(false);
throw e;
throw;
}
}
}
@ -1088,13 +1088,13 @@ public class PS3MAPI
Fail(new Exception("Malformed PASV response: " + sResponse));
}
sServer = String.Format("{0}.{1}.{2}.{3}", pasv[0], pasv[1], pasv[2], pasv[3]);
sServer = string.Format("{0}.{1}.{2}.{3}", pasv[0], pasv[1], pasv[2], pasv[3]);
iPort = (int.Parse(pasv[4]) << 8) + int.Parse(pasv[5]);
try
{
CloseDataSocket();
data_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
data_ipEndPoint = new IPEndPoint(Dns.GetHostByName(sServerIP).AddressList[0], iPort);
data_ipEndPoint = new IPEndPoint(Dns.GetHostAddresses(sServerIP)[0], iPort);
data_sock.Connect(data_ipEndPoint);
}
catch (Exception e)
@ -1159,13 +1159,13 @@ public class PS3MAPI
{
sLog = sLog + "COMMAND: " + sCommand + Environment.NewLine;
Connect();
Byte[] byCommand = Encoding.ASCII.GetBytes((sCommand + "\r\n").ToCharArray());
byte[] byCommand = Encoding.ASCII.GetBytes((sCommand + "\r\n").ToCharArray());
main_sock.Send(byCommand, byCommand.Length, 0);
ReadResponse();
}
internal static void FillBucket()
{
Byte[] bytes = new Byte[512];
byte[] bytes = new byte[512];
long lBytesRecieved;
int iMilliSecondsPassed = 0;
while (main_sock.Available < 1)

View file

@ -23,9 +23,10 @@ public class ElfFile {
Big = 0x02,
}
private enum InstructionSetArchitecture : UInt16 {
private enum InstructionSetArchitecture : ushort {
PowerPC = 0x15, //64-bit PowerPC (PS3)
ARM = 0x28, //32-bit ARM (Vita)
MipsRS3000 = 0x08, // MIPS RS3000 Big-Endian 32bit (PSP)
}
public string Name { get; } = "Binary Blob";
@ -72,12 +73,12 @@ public class ElfFile {
private string GetFileArchitecture(byte[] elfHeader, bool isBigEndian) {
byte[] architectureBytes = elfHeader[0x12..0x14];
UInt16 fileArch = (isBigEndian) ?
ushort fileArch = (isBigEndian) ?
BinaryPrimitives.ReadUInt16BigEndian(architectureBytes) :
BinaryPrimitives.ReadUInt16LittleEndian(architectureBytes);
foreach(InstructionSetArchitecture arch in Enum.GetValues(typeof(InstructionSetArchitecture))) {
if(fileArch == (UInt16)arch)
if(fileArch == (ushort)arch)
return arch.ToString();
}
return null;

48
UnionPatcher/OSUtil.cs Normal file
View file

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
namespace LBPUnion.UnionPatcher
{
using RuntimeOSPlatform = System.Runtime.InteropServices.OSPlatform;
public enum OSPlatform
{
NotSupported,
Windows,
OSX,
Linux,
}
public class OSUtil
{
private static IEnumerable
<(OSPlatform Platform, RuntimeOSPlatform RuntimePlatform)?> EnumeratePlatforms()
{
yield return (OSPlatform.Windows, RuntimeOSPlatform.Windows);
yield return (OSPlatform.OSX, RuntimeOSPlatform.OSX);
yield return (OSPlatform.Linux, RuntimeOSPlatform.Linux);
}
public static OSPlatform GetPlatform()
{
return EnumeratePlatforms().FirstOrDefault(p
=> RuntimeInformation.IsOSPlatform(p.Value.RuntimePlatform))?.Platform ?? default;
}
public static string GetExecutablePath()
{
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
if (string.IsNullOrEmpty(path))
path = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule?.FileName);
if (string.IsNullOrEmpty(path))
path = AppContext.BaseDirectory;
return path;
}
}
}

View file

@ -1,37 +1,11 @@
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
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) {
PatchFile(fileName, serverUrl.ToString(), outputFileName);
@ -72,13 +46,18 @@ public static class Patcher {
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}");
// Find a string including http or https and LITTLEBIGPLANETPS3_XML or LITTLEBIGPLANETPSP_XML,
// then match any additional NULL characters to dynamically gague the maximum length on a per-title basis
// without a hardcoded array of known server URLs
MatchCollection urls = Regex.Matches(dataAsString, "http?[^\x00]*?LITTLEBIGPLANETPS(3|P)_XML\x00*");
foreach(Match urlMatch in urls) {
string url = urlMatch.Value;
if(serverUrl.Length > url.Length - 1) {
throw new ArgumentOutOfRangeException(nameof(serverUrl), $"Server URL ({serverUrl.Length} characters long) is above maximum length {url.Length - 1}");
}
int offset = dataAsString.IndexOf(url, StringComparison.Ordinal);
if(offset < 1) continue;
int offset = urlMatch.Index;
writer.BaseStream.Position = offset;
for(int i = 0; i < url.Length; i++) {

View file

@ -38,51 +38,76 @@ public class RemotePatch
public static void LaunchSCETool(string args)
{
string platformExecutable = "";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
platformExecutable = "scetool/win64/scetool.exe";
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
platformExecutable = "scetool/linux64/scetool";
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) platformExecutable = "";
if (platformExecutable != "")
switch (OSUtil.GetPlatform())
{
ProcessStartInfo startInfo = new();
startInfo.UseShellExecute = false;
startInfo.FileName = Path.GetFullPath(platformExecutable);
startInfo.WorkingDirectory = Path.GetFullPath(".");
startInfo.Arguments = args;
startInfo.RedirectStandardOutput = true;
case OSPlatform.Windows:
platformExecutable = "scetool/win64/scetool.exe";
break;
case OSPlatform.Linux:
if(RuntimeInformation.ProcessArchitecture == Architecture.X64)
{
platformExecutable = "scetool/linux64/scetool";
} else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm)
{
platformExecutable = "scetool/linuxarm/scetool";
} else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
platformExecutable = "scetool/linuxarm64/scetool";
}
break;
case OSPlatform.OSX:
if (RuntimeInformation.OSArchitecture == Architecture.Arm64)
{
platformExecutable = "scetool/macarm64/scetool"; // For Apple Silicon Macs
}
else
{
platformExecutable = "scetool/mac64/scetool";
}
break;
default:
throw new Exception("Error starting SCETool. Your platform may not be supported yet.");
Console.WriteLine("\n\n===== START SCETOOL =====\n");
using (Process proc = Process.Start(startInfo))
{
while (!proc.StandardOutput.EndOfStream) Console.WriteLine(proc.StandardOutput.ReadLine());
proc.WaitForExit();
}
Console.WriteLine("\n===== END SCETOOL =====\n\n");
}
else
ProcessStartInfo startInfo = new();
startInfo.UseShellExecute = false;
startInfo.FileName = Path.GetFullPath(platformExecutable);
startInfo.WorkingDirectory = Path.GetFullPath(".");
startInfo.Arguments = args;
startInfo.RedirectStandardOutput = true;
Console.WriteLine("\n\n===== START SCETOOL =====\n");
using (Process proc = Process.Start(startInfo))
{
throw new Exception("Error starting SCETool. Your platform may not be supported yet.");
while (!proc.StandardOutput.EndOfStream) Console.WriteLine(proc.StandardOutput.ReadLine());
proc.WaitForExit();
}
Console.WriteLine("\n===== END SCETOOL =====\n\n");
}
public void RevertEBOOT(string ps3ip, string gameID, string serverURL, string user, string pass)
{
Console.WriteLine("Restoring original EBOOT.BIN from EBOOT.BIN.BAK");
string workingDir = ".";
if (OSUtil.GetPlatform() == OSPlatform.OSX)
{
workingDir = $"{Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)}/UnionPatcher";
Directory.CreateDirectory(workingDir);
}
// Create a simple directory structure
Directory.CreateDirectory(@"eboot");
Directory.CreateDirectory($@"eboot/{gameID}");
Directory.CreateDirectory($@"eboot/{gameID}/original");
Directory.CreateDirectory($@"{workingDir}/eboot");
Directory.CreateDirectory($@"{workingDir}/eboot/{gameID}");
Directory.CreateDirectory($@"{workingDir}/eboot/{gameID}/original");
// Now we'll check and see if a backup exists on the server, if so download it and then upload it back as EBOOT.BIN
if (FTP.FileExists($"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN.BAK", user, pass))
{
FTP.DownloadFile($"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN.BAK", @$"eboot/{gameID}/original/EBOOT.BIN.BAK", user, pass);
FTP.UploadFile(@$"eboot/{gameID}/original/EBOOT.BIN.BAK", $"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN", user, pass);
FTP.DownloadFile($"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN.BAK", @$"{workingDir}/eboot/{gameID}/original/EBOOT.BIN.BAK", user, pass);
FTP.UploadFile(@$"{workingDir}/eboot/{gameID}/original/EBOOT.BIN.BAK", $"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN", user, pass);
}
else
{
@ -93,6 +118,13 @@ public class RemotePatch
public void PSNEBOOTRemotePatch(string ps3ip, string gameID, string serverURL, string user, string pass)
{
Console.WriteLine("Detected Digital Copy - Running in Full Mode");
string workingDir = ".";
if (OSUtil.GetPlatform() == OSPlatform.OSX)
{
workingDir = $"{Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)}/UnionPatcher";
Directory.CreateDirectory(workingDir);
}
string idps = "";
string contentID = "";
@ -103,25 +135,25 @@ public class RemotePatch
this._ps3Mapi.PS3.Notify("UnionRemotePatcher Connected! Patching...");
// Create simple directory structure
Directory.CreateDirectory(@"rifs");
Directory.CreateDirectory(@"eboot");
Directory.CreateDirectory($@"eboot/{gameID}");
Directory.CreateDirectory($@"eboot/{gameID}/original");
Directory.CreateDirectory($@"eboot/{gameID}/patched");
Directory.CreateDirectory($@"{workingDir}/rifs");
Directory.CreateDirectory($@"{workingDir}/eboot");
Directory.CreateDirectory($@"{workingDir}/eboot/{gameID}");
Directory.CreateDirectory($@"{workingDir}/eboot/{gameID}/original");
Directory.CreateDirectory($@"{workingDir}/eboot/{gameID}/patched");
// Let's grab and backup our EBOOT
FTP.DownloadFile($"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN",
@$"eboot/{gameID}/original/EBOOT.BIN", user, pass);
@$"{workingDir}/eboot/{gameID}/original/EBOOT.BIN", user, pass);
// Now we'll check and see if a backup exists on the server or not, if we don't have one on the server, then upload one
if (!FTP.FileExists($"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN.BAK", user, pass))
FTP.UploadFile(@$"eboot/{gameID}/original/EBOOT.BIN",
FTP.UploadFile(@$"{workingDir}/eboot/{gameID}/original/EBOOT.BIN",
$"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN.BAK", user, pass);
// Start getting idps and act.dat - these will help us decrypt a PSN eboot
idps = PS3MAPI.PS3MAPIClientServer.PS3_GetIDPS();
File.WriteAllBytes(@"data/idps", IDPSHelper.StringToByteArray(idps));
File.WriteAllBytes($@"data/idps", IDPSHelper.StringToByteArray(idps));
// Scan the users on the system
users = GetUsers(ps3ip, user, pass);
@ -135,12 +167,12 @@ public class RemotePatch
$"ftp://{ps3ip}/dev_hdd0/home/{currentUser}/exdata/", user, pass))
if (fileName.Contains(gameID))
{
FTP.DownloadFile($"ftp://{ps3ip}/dev_hdd0/home/{currentUser}/exdata/act.dat", @"data/act.dat",
FTP.DownloadFile($"ftp://{ps3ip}/dev_hdd0/home/{currentUser}/exdata/act.dat", $@"{workingDir}/data/act.dat",
user,
pass);
FTP.DownloadFile($"ftp://{ps3ip}/dev_hdd0/home/{currentUser}/exdata/{fileName}",
@$"rifs/{fileName}", user, pass);
@$"{workingDir}/rifs/{fileName}", user, pass);
contentID = fileName.Substring(0, fileName.Length - 4);
@ -150,10 +182,10 @@ public class RemotePatch
}
// Finally, let's decrypt the EBOOT.BIN
LaunchSCETool($" -v -d \"{Path.GetFullPath(@$"eboot/{gameID}/original/EBOOT.BIN")}\" \"{Path.GetFullPath(@$"eboot/{gameID}/original/EBOOT.ELF")}\"");
LaunchSCETool($" -v -d \"{Path.GetFullPath(@$"{workingDir}/eboot/{gameID}/original/EBOOT.BIN")}\" \"{Path.GetFullPath(@$"{workingDir}/eboot/{gameID}/original/EBOOT.ELF")}\"");
// Now, patch the EBOOT;
Patcher.PatchFile($"eboot/{gameID}/original/EBOOT.ELF", serverURL, $"eboot/{gameID}/patched/EBOOT.ELF");
Patcher.PatchFile($"{workingDir}/eboot/{gameID}/original/EBOOT.ELF", serverURL, $"{workingDir}/eboot/{gameID}/patched/EBOOT.ELF");
// Encrypt the EBOOT (PSN)
LaunchSCETool($"--verbose " +
@ -173,10 +205,10 @@ public class RemotePatch
$" --np-app-type=SPRX" +
$" --np-content-id={contentID}" +
$" --np-real-fname=EBOOT.BIN" +
$" --encrypt eboot/{gameID}/patched/EBOOT.ELF eboot/{gameID}/patched/EBOOT.BIN");
$" --encrypt {workingDir}/eboot/{gameID}/patched/EBOOT.ELF {workingDir}/eboot/{gameID}/patched/EBOOT.BIN");
// And upload the encrypted, patched EBOOT to the system.
FTP.UploadFile(@$"eboot/{gameID}/patched/EBOOT.BIN",
FTP.UploadFile(@$"{workingDir}/eboot/{gameID}/patched/EBOOT.BIN",
$"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN", user, pass);
}
@ -185,38 +217,45 @@ public class RemotePatch
{
Console.WriteLine("Detected Disc Copy - Running in Simplified Mode");
string workingDir = ".";
if (OSUtil.GetPlatform() == OSPlatform.OSX)
{
workingDir = $"{Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)}/UnionPatcher";
Directory.CreateDirectory(workingDir);
}
// Create a simple directory structure
Directory.CreateDirectory(@"eboot");
Directory.CreateDirectory($@"eboot/{gameID}");
Directory.CreateDirectory($@"eboot/{gameID}/original");
Directory.CreateDirectory($@"eboot/{gameID}/patched");
Directory.CreateDirectory($@"{workingDir}/eboot");
Directory.CreateDirectory($@"{workingDir}/eboot/{gameID}");
Directory.CreateDirectory($@"{workingDir}/eboot/{gameID}/original");
Directory.CreateDirectory($@"{workingDir}/eboot/{gameID}/patched");
// Let's grab and backup our EBOOT
FTP.DownloadFile($"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN",
@$"eboot/{gameID}/original/EBOOT.BIN", user, pass);
@$"{workingDir}/eboot/{gameID}/original/EBOOT.BIN", user, pass);
// Now we'll check and see if a backup exists on the server or not, if we don't have one on the server, then upload one
if (!FTP.FileExists($"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN.BAK", user, pass))
FTP.UploadFile(@$"eboot/{gameID}/original/EBOOT.BIN",
FTP.UploadFile(@$"{workingDir}/eboot/{gameID}/original/EBOOT.BIN",
$"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN.BAK", user, pass);
// Check for keys in the data directory
if (!File.Exists("data/keys"))
if (!File.Exists($"./data/keys"))
throw new FileNotFoundException(
"UnionRemotePatcher cannot find the keys, ldr_curves, or vsh_curves files required to continue. Please make sure you have copies of these files placed in the data directory where you found the executable to run UnionRemotePatcher. Without them, we can't patch your game.");
// Decrypt the EBOOT
LaunchSCETool($"-v -d eboot/{gameID}/original/EBOOT.BIN eboot/{gameID}/original/EBOOT.ELF");
LaunchSCETool($"-v -d {workingDir}/eboot/{gameID}/original/EBOOT.BIN {workingDir}/eboot/{gameID}/original/EBOOT.ELF");
// Now, patch the EBOOT;
Patcher.PatchFile($"eboot/{gameID}/original/EBOOT.ELF", serverURL, $"eboot/{gameID}/patched/EBOOT.ELF");
Patcher.PatchFile($"{workingDir}/eboot/{gameID}/original/EBOOT.ELF", serverURL, $"{workingDir}/eboot/{gameID}/patched/EBOOT.ELF");
// Encrypt the EBOOT (Disc)
LaunchSCETool(
$" -v --sce-type=SELF --skip-sections=FALSE --key-revision=0A --self-app-version=0001000000000000 --self-auth-id=1010000001000003 --self-vendor-id=01000002 --self-ctrl-flags=0000000000000000000000000000000000000000000000000000000000000000 --self-cap-flags=00000000000000000000000000000000000000000000003B0000000100040000 --self-type=APP --self-fw-version=0003005500000000 --compress-data true --encrypt \"{Path.GetFullPath(@$"eboot/{gameID}/patched/EBOOT.ELF")}\" \"{Path.GetFullPath(@$"eboot/{gameID}/patched/EBOOT.BIN")}\"");
$" -v --sce-type=SELF --skip-sections=FALSE --key-revision=0A --self-app-version=0001000000000000 --self-auth-id=1010000001000003 --self-vendor-id=01000002 --self-ctrl-flags=0000000000000000000000000000000000000000000000000000000000000000 --self-cap-flags=00000000000000000000000000000000000000000000003B0000000100040000 --self-type=APP --self-fw-version=0003005500000000 --compress-data true --encrypt \"{Path.GetFullPath(@$"{workingDir}/eboot/{gameID}/patched/EBOOT.ELF")}\" \"{Path.GetFullPath(@$"{workingDir}/eboot/{gameID}/patched/EBOOT.BIN")}\"");
// And upload the encrypted, patched EBOOT to the system.
FTP.UploadFile(@$"eboot/{gameID}/patched/EBOOT.BIN",
FTP.UploadFile(@$"{workingDir}/eboot/{gameID}/patched/EBOOT.BIN",
$"ftp://{ps3ip}/dev_hdd0/game/{gameID}/USRDIR/EBOOT.BIN", user, pass);
}
}
}

View file

@ -3,7 +3,7 @@
<AssemblyName>LBPUnion.UnionPatcher</AssemblyName>
<RootNamespace>LBPUnion.UnionPatcher</RootNamespace>
<Configurations>Debug;Release;Windows</Configurations>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Platforms>AnyCPU</Platforms>
<ApplicationIcon>Icon64.ico</ApplicationIcon>
</PropertyGroup>

4809
UnionPatcher/data/keys Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

31
build-all.sh Executable file
View file

@ -0,0 +1,31 @@
mkdir -p builds
#dotnet clean;
dotnet publish -c Windows -r win-x64 --self-contained
dotnet publish -c Linux -r linux-x64 --self-contained
dotnet publish -c Linux -r linux-arm --self-contained
dotnet publish -c Linux -r linux-arm64 --self-contained
dotnet publish -c MacOS -r osx-x64 --self-contained
dotnet publish -c MacOS -r osx-arm64 --self-contained
# $1: Name.zip
# $2: Path to zip
function createBuild() {
currentDirectory=$(pwd)
cd $2 || return 1
zip -r "$1" *
cd $currentDirectory || return 1
mv "$2/$1" builds/
}
createBuild "UnionPatcher-Windows-x64.zip" "UnionPatcher.Gui.Windows/bin/Release/net8.0-windows/win-x64/publish/"
createBuild "UnionPatcher-Linux-x64.zip" "UnionPatcher.Gui.Linux/bin/Release/net8.0/linux-x64/publish/"
createBuild "UnionPatcher-Linux-arm.zip" "UnionPatcher.Gui.Linux/bin/Release/net8.0/linux-arm/publish/"
createBuild "UnionPatcher-Linux-arm64.zip" "UnionPatcher.Gui.Linux/bin/Release/net8.0/linux-arm64/publish/"
# CODESIGN_IDENTITY is the certificate that you want to use for codesigning for mac, if not present then will not be signed
./build-mac.sh $CODESIGN_IDENTITY

21
build-mac.sh Executable file
View file

@ -0,0 +1,21 @@
# Script to build UnionPatcher for mac, builds a universal binary, and zips it up. also codesigns if $1 is specified
dotnet clean
dotnet publish UnionPatcher.Gui.MacOS --configuration Release /p:Platform="Any CPU" --self-contained -o macbuild
dotnet publish UnionPatcher.Gui.MacOS --configuration Release /p:Platform="Any CPU" --arch x64 --self-contained -o macbuildx86
rm -rf macbuilduniversal
mkdir macbuilduniversal
cp -r macbuild/UnionPatcher.Gui.MacOS.app macbuilduniversal/UnionPatcher.app
cp UnionPatcher.Gui.MacOS/Info.plist macbuilduniversal/UnionPatcher.app/Contents/Info.plist
rm -rf macbuilduniversal/UnionPatcher.app/Contents/MacOS/scetool/linux*
rm -rf macbuilduniversal/UnionPatcher.app/Contents/MacOS/scetool/win*
lipo -create -output macbuilduniversal/UnionPatcher.app/Contents/MacOS/LBPUnion.UnionPatcher.Gui.MacOS macbuildx86/LBPUnion.UnionPatcher.Gui.MacOS macbuild/LBPUnion.UnionPatcher.Gui.MacOS
touch macbuilduniversal/UnionPatcher.app
if [ -z ${1+x} ]; then
codesign -f --deep -s "$1" macbuilduniversal/UnionPatcher.app
fi
cd macbuilduniversal
zip -r UnionPatcher-macOS-universal.zip UnionPatcher.app

View file

@ -1,7 +1,7 @@
{
"sdk": {
"version": "5.0",
"version": "6.0",
"rollForward": "latestMajor",
"allowPrerelease": false
}
}
}