Skip to content

Commit

Permalink
Merge pull request #30 from LittleBigRefresh/psp-setup
Browse files Browse the repository at this point in the history
Automated setup of Allefresher
  • Loading branch information
jvyden authored Sep 22, 2023
2 parents 3c06797 + 8fcdce5 commit 420f10b
Show file tree
Hide file tree
Showing 15 changed files with 289 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .idea/.idea.Refresher/.idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Refresher.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LBP/@EntryIndexedValue">LBP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PSP/@EntryIndexedValue">PSP</s:String>
<s:Boolean x:Key="/Default/Environment/Editor/UseCamelHumps/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Allefresher/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Asdf/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Autodiscover/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=autoresponse/@EntryIndexedValue">True</s:Boolean>
Expand Down
6 changes: 3 additions & 3 deletions Refresher/CLI/CommandLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ void DeleteTempFile(string? s)
}

//Create a new patcher with the temp file stream
Patcher patcher = new(mappedFile.CreateViewStream());
List<Message> messages = patcher.Verify(options.ServerUrl, options.Digest ?? false).ToList();
EbootPatcher ebootPatcher = new(mappedFile.CreateViewStream());
List<Message> messages = ebootPatcher.Verify(options.ServerUrl, options.Digest ?? false).ToList();

//Write the messages to the console
foreach (Message message in messages) Console.WriteLine($"{message.Level}: {message.Content}");
Expand Down Expand Up @@ -103,7 +103,7 @@ void DeleteTempFile(string? s)
try
{
//Patch the file
patcher.Patch(options.ServerUrl, options.Digest ?? false);
ebootPatcher.Patch(options.ServerUrl, options.Digest ?? false);

//TODO: warn the user if they are overwriting the file
File.Move(tempFile, options.OutputFile, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

namespace Refresher.Patching;

public partial class Patcher
public partial class EbootPatcher : IPatcher
{
private readonly Lazy<List<PatchTargetInfo>> _targets;

public Patcher(Stream stream)
public EbootPatcher(Stream stream)
{
if (!stream.CanRead || !stream.CanSeek || !stream.CanWrite)
throw new ArgumentException("Stream must be readable, seekable and writable", nameof(stream));
Expand Down
10 changes: 10 additions & 0 deletions Refresher/Patching/IPatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Refresher.Verification;

namespace Refresher.Patching;

public interface IPatcher
{
public List<Message> Verify(string url, bool patchDigest);

public void Patch(string url, bool patchDigest);
}
13 changes: 13 additions & 0 deletions Refresher/Patching/PSP/PSPPluginListEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Refresher.Patching.PSP;

public class PSPPluginListEntry
{
public string Path;
public int? Type;

public PSPPluginListEntry(string path, int? type = null)
{
this.Path = path;
this.Type = type;
}
}
44 changes: 44 additions & 0 deletions Refresher/Patching/PSP/PSPPluginListParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace Refresher.Patching.PSP;

public static class PSPPluginListParser
{
public static List<PSPPluginListEntry> Parse(TextReader reader)
{
List<PSPPluginListEntry> list = new();

while (reader.ReadLine() is { } line)
{
//Skip blank lines
if (string.IsNullOrWhiteSpace(line)) continue;

string[] parts = line.Split(" ");

PSPPluginListEntry entry = new(parts[0]);

if (parts.Length > 1)
{
entry.Type = int.Parse(parts[1]);
}

list.Add(entry);
}

return list;
}

public static void Write(List<PSPPluginListEntry> list, TextWriter writer)
{
foreach (PSPPluginListEntry entry in list)
{
writer.Write(entry.Path);
if (entry.Type.HasValue)
{
writer.Write(' ');
writer.Write(entry.Type.Value);
}
writer.Write('\n');
}

writer.Flush();
}
}
118 changes: 118 additions & 0 deletions Refresher/Patching/PSPPatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using System.Reflection;
using Refresher.Patching.PSP;
using Refresher.Verification;

namespace Refresher.Patching;

public class PSPPatcher : IPatcher
{
public string? PSPDrivePath;

private readonly Stream _allefresher;

public PSPPatcher()
{
Assembly assembly = Assembly.GetExecutingAssembly();
this._allefresher = assembly.GetManifestResourceStream("Refresher.Resources.Allefresher.prx")!;
}

public List<Message> Verify(string url, bool patchDigest)
{
List<Message> messages = new();

if (!Uri.TryCreate(url, UriKind.Absolute, out _))
messages.Add(new Message(MessageLevel.Error, "URL failed to parse!"));

if (string.IsNullOrEmpty(this.PSPDrivePath) || !Directory.Exists(this.PSPDrivePath))
messages.Add(new Message(MessageLevel.Error, "Invalid PSP Drive path!"));

return messages;
}

public void Patch(string url, bool patchDigest)
{
Uri uri = new(url);

string domain = uri.Host;
string format = $"{uri.Scheme}://%s:{uri.Port}{uri.AbsolutePath}%s";

string pluginsDir = Path.Combine(this.PSPDrivePath!, "SEPLUGINS");

//If the plugins directory does not exist
if (!Directory.Exists(pluginsDir))
{
//Create it
Directory.CreateDirectory(pluginsDir);
}

string domainPath = Path.Combine(pluginsDir, "Allefresher_domain.txt");
string formatPath = Path.Combine(pluginsDir, "Allefresher_format.txt");

//Delete the existing domain and format configuration files
File.Delete(domainPath);
File.Delete(formatPath);

//Write the new domain and format configuration
File.WriteAllText(domainPath, domain);
File.WriteAllText(formatPath, format);

//Match for all files called "game.txt" in the plugins directory
//NOTE: we do this because the PSP filesystem is case insensitive, and the .NET STL is case sensitive on linux
List<string> possibleMatches = Directory.EnumerateFiles(pluginsDir, "game.txt", new EnumerationOptions
{
MatchCasing = MatchCasing.CaseInsensitive,
}).ToList();

FileStream gamePluginsFileStream;

const string allefresherPath = "ms0:/SEPLUGINS/Allefresher.prx";

List<PSPPluginListEntry> entries;
if (possibleMatches.Any())
{
//Open the first match
FileStream stream = File.OpenRead(possibleMatches[0]);

//Read out the matches
entries = PSPPluginListParser.Parse(new StreamReader(stream));

//If Allefresher is not in the list,
if (!entries.Any(entry => entry.Path.Contains("Allefresher.prx", StringComparison.InvariantCultureIgnoreCase)))
{
//Add Allefresher to the game plugin list
entries.Add(new PSPPluginListEntry(allefresherPath, 1));
}

//Dispose the read stream
stream.Dispose();

//Open a new write stream to the game.txt file
gamePluginsFileStream = File.Open(possibleMatches[0], FileMode.Truncate);
}
else
{
//Create a new list, with the only entry being Allefresher
entries = new List<PSPPluginListEntry>
{
new(allefresherPath, 1),
};

//Create a new game.txt file
gamePluginsFileStream = File.Open(Path.Combine(pluginsDir, "game.txt"), FileMode.CreateNew);
}

//Write the plugin list to the file
PSPPluginListParser.Write(entries, new StreamWriter(gamePluginsFileStream));

//Flush and dispose the stream
gamePluginsFileStream.Flush();
gamePluginsFileStream.Dispose();

using FileStream allefresherOutput = File.Open(Path.Combine(pluginsDir, "Allefresher.prx"), FileMode.Create);

this._allefresher.Seek(0, SeekOrigin.Begin);

//Copy the Allefresher embedded resource to the output file
this._allefresher.CopyTo(allefresherOutput);
}
}
1 change: 1 addition & 0 deletions Refresher/Refresher.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<PackageReference Include="FluentFTP" Version="47.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SCEToolSharp" Version="1.0.9" />
<EmbeddedResource Include="Resources\Allefresher.prx" />
</ItemGroup>


Expand Down
Binary file added Refresher/Resources/Allefresher.prx
Binary file not shown.
4 changes: 2 additions & 2 deletions Refresher/UI/FilePatchForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Refresher.UI;

public class FilePatchForm : PatchForm<Patcher>
public class FilePatchForm : PatchForm<EbootPatcher>
{
private readonly FilePicker _inputFileField;
private readonly FilePicker _outputFileField;
Expand Down Expand Up @@ -92,7 +92,7 @@ private void FileUpdated(object? sender, EventArgs ev)
{
this._mappedFile?.Dispose();
this._mappedFile = MemoryMappedFile.CreateFromFile(this._tempFile, FileMode.Open, null, 0, MemoryMappedFileAccess.ReadWrite);
this.Patcher = new Patcher(this._mappedFile.CreateViewStream());
this.Patcher = new EbootPatcher(this._mappedFile.CreateViewStream());
}
catch(Exception e)
{
Expand Down
18 changes: 12 additions & 6 deletions Refresher/UI/IntegratedPatchForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Refresher.UI;

public abstract class IntegratedPatchForm : PatchForm<Patcher>
public abstract class IntegratedPatchForm : PatchForm<EbootPatcher>
{
private readonly DropDown _gameDropdown;
private readonly TextBox? _outputField;
Expand All @@ -29,8 +29,10 @@ protected IntegratedPatchForm(string subtitle) : base(subtitle)
{
this.AddRemoteField(),
AddField("Game to patch", out this._gameDropdown, forceHeight: 56),
AddField("Server URL", out this.UrlField),
AddField("Server URL", out this.UrlField),
};

this._gameDropdown.SelectedValueChanged += this.GameChanged;

if (!this.ShouldReplaceExecutable)
{
Expand All @@ -40,8 +42,6 @@ protected IntegratedPatchForm(string subtitle) : base(subtitle)

this.FormPanel = new TableLayout(rows);

this._gameDropdown.SelectedValueChanged += this.GameChanged;

this.InitializePatcher();
}

Expand Down Expand Up @@ -148,7 +148,7 @@ protected virtual void GameChanged(object? sender, EventArgs ev)

this.LogMessage($"The EBOOT has been successfully decrypted. It's stored at {this._tempFile}.");

this.Patcher = new Patcher(File.Open(this._tempFile, FileMode.Open, FileAccess.ReadWrite));
this.Patcher = new EbootPatcher(File.Open(this._tempFile, FileMode.Open, FileAccess.ReadWrite));

this.Reverify(sender, ev);
}
Expand Down Expand Up @@ -191,7 +191,7 @@ public override void CompletePatch(object? sender, EventArgs e) {
Thread.Sleep(1000); // TODO: don't. block. the. main. thread.

this.Accessor.UploadFile(fileToUpload, destination);
MessageBox.Show($"Successfully patched EBOOT! It was saved to '{destination}'.");
MessageBox.Show(this, $"Successfully patched EBOOT! It was saved to '{destination}'.", "Success!");

// Re-initialize patcher so we can patch with the same parameters again
// Probably slow but prevents crash
Expand Down Expand Up @@ -228,6 +228,12 @@ protected virtual void RevertToOriginalExecutable(object? sender, EventArgs e)
}

protected abstract TableRow AddRemoteField();
/// <summary>
/// Whether the target platform requires the executable to be resigned or not
/// </summary>
protected abstract bool NeedsResign { get; }
/// <summary>
/// Whether the target platform requires the executable to be named <c>EBOOT.BIN</c>
/// </summary>
protected abstract bool ShouldReplaceExecutable { get; }
}
3 changes: 2 additions & 1 deletion Refresher/UI/MainForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public class MainForm : RefresherForm
new Label { Text = "Welcome to Refresher! Please pick a patching method to continue." },
new Button((_, _) => this.ShowChild<FilePatchForm>()) { Text = "File Patch (using a .ELF)" },
new Button((_, _) => this.ShowChild<EmulatorPatchForm>()) { Text = "RPCS3 Patch" },
new Button((_, _) => this.ShowChild<ConsolePatchForm>()) { Text = "PS3 Patch" }
new Button((_, _) => this.ShowChild<ConsolePatchForm>()) { Text = "PS3 Patch" },
new Button((_, _) => this.ShowChild<PSPSetupForm>()) { Text = "PSP Setup" }
);

layout.Spacing = 5;
Expand Down
Loading

0 comments on commit 420f10b

Please sign in to comment.