diff --git a/.gitignore b/.gitignore
index 77208fa..254b4a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -68,3 +68,4 @@ fastlane/test_output
bin
obj
packages
+Tools
diff --git a/Linux/makePSClassicsfPKG.sln b/Linux/makePSClassicsfPKG.sln
new file mode 100644
index 0000000..370db74
--- /dev/null
+++ b/Linux/makePSClassicsfPKG.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.11.35219.272
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "makePSClassicsfPKG", "makePSClassicsfPKG\makePSClassicsfPKG.csproj", "{673E992E-C1E7-47F1-9B7C-3387623B3914}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {673E992E-C1E7-47F1-9B7C-3387623B3914}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {673E992E-C1E7-47F1-9B7C-3387623B3914}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {673E992E-C1E7-47F1-9B7C-3387623B3914}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {673E992E-C1E7-47F1-9B7C-3387623B3914}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {EA192E80-5CFB-4874-A996-EA7F4EED9048}
+ EndGlobalSection
+EndGlobal
diff --git a/Linux/makePSClassicsfPKG/App.axaml b/Linux/makePSClassicsfPKG/App.axaml
new file mode 100644
index 0000000..c3e6a23
--- /dev/null
+++ b/Linux/makePSClassicsfPKG/App.axaml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Linux/makePSClassicsfPKG/App.axaml.cs b/Linux/makePSClassicsfPKG/App.axaml.cs
new file mode 100644
index 0000000..bf315e6
--- /dev/null
+++ b/Linux/makePSClassicsfPKG/App.axaml.cs
@@ -0,0 +1,24 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+
+namespace makePSClassicsfPKG
+{
+ public partial class App : Application
+ {
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ desktop.MainWindow = new MainWindow();
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Linux/makePSClassicsfPKG/MainWindow.axaml b/Linux/makePSClassicsfPKG/MainWindow.axaml
new file mode 100644
index 0000000..258db0c
--- /dev/null
+++ b/Linux/makePSClassicsfPKG/MainWindow.axaml
@@ -0,0 +1,226 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Linux/makePSClassicsfPKG/MainWindow.axaml.cs b/Linux/makePSClassicsfPKG/MainWindow.axaml.cs
new file mode 100644
index 0000000..25f1d99
--- /dev/null
+++ b/Linux/makePSClassicsfPKG/MainWindow.axaml.cs
@@ -0,0 +1,2083 @@
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using LibOrbisPkg.SFO;
+using MsBox.Avalonia;
+using MsBox.Avalonia.Enums;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace makePSClassicsfPKG
+{
+
+#pragma warning disable CS0618 // Type or member is obsolete (but working as intended).
+
+ public partial class MainWindow : Window
+ {
+ public string homePath = "";
+ public string wineDriveC = "";
+ public string PS4ToolsPath = "";
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ Opened += MainWindow_Opened;
+ }
+
+ private async void MainWindow_Opened(object? sender, EventArgs e)
+ {
+ // Set paths
+ homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
+ wineDriveC = homePath + "/.wine/drive_c";
+ PS4ToolsPath = wineDriveC + "/PS4";
+
+ Console.WriteLine(Directory.GetCurrentDirectory());
+
+ // Prevent working in the wrong directory
+ if (!Directory.Exists(Directory.GetCurrentDirectory() + "/Tools/PS4"))
+ {
+ Directory.SetCurrentDirectory("/usr/lib/makePSClassicsfPKG");
+ Console.WriteLine(Directory.GetCurrentDirectory());
+ }
+
+ await CheckWinePrefixAndSetup();
+ }
+
+ #region PS1
+
+ private string CurrentPS1GameID = "";
+
+ private async void BrowsePS1IconButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "Select a PNG icon file", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "PNG files", Extensions = { "png" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedPS1IconTextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS1StartupImageButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "Select a PNG background file", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "PNG files", Extensions = { "png" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedPS1BGTextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS1TXTConfigButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "Select a TXT file", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "TXT files", Extensions = { "txt" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedPS1TXTConfigTextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS1LUAConfigButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "Select a LUA file", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "LUA files", Extensions = { "lua" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedPS1LUAConfigTextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS1Disc1Button_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "BIN files", Extensions = { "bin" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+ if (selectedFile[0] is not null)
+ {
+ ReadPS1BIN(selectedFile[0]);
+ }
+ else
+ {
+ var box = MessageBoxManager.GetMessageBoxStandard("PS Classics fPKG Builder", "Aborted", ButtonEnum.Ok);
+ await box.ShowWindowDialogAsync(window);
+ }
+ }
+
+ private async void BrowsePS1Disc2Button_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "BIN files", Extensions = { "bin" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ PS1SelectedDisc2TextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS1Disc3Button_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "BIN files", Extensions = { "bin" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ PS1SelectedDisc3TextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS1Disc4Button_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "BIN files", Extensions = { "bin" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ PS1SelectedDisc4TextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BuildPS1fPKGButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ // Checks before fPKG creation
+ if (string.IsNullOrEmpty(PS1SelectedDisc1TextBox.Text))
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "No disc 1 specified, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+ if (string.IsNullOrEmpty(PS1TitleTextBox.Text))
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "No game title specified, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+ if (string.IsNullOrEmpty(PS1NPTitleTextBox.Text))
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "No NP title specified, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+ if (PS1NPTitleTextBox.Text.Length != 9)
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "'NP Title' length mismatching, only 9 characters are allowed, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+
+ // Select output folder
+ var newOpenFolderDialog = new OpenFolderDialog() { Title = "Please select an output folder" };
+ var selectedFolder = await newOpenFolderDialog.ShowAsync(window);
+
+ // Continue if selection is not empty
+ if (!string.IsNullOrWhiteSpace(selectedFolder))
+ {
+ string PKGOutputFolder = selectedFolder;
+ string CacheDirectory = wineDriveC + @"/Cache";
+ string GameCacheDirectory = wineDriveC + @"/Cache/PS1fPKG";
+
+ // Remove previous fPKG creation & re-create the PS1fPKG cache folder
+ if (Directory.Exists(GameCacheDirectory))
+ {
+ Directory.Delete(GameCacheDirectory, true);
+ }
+ if (File.Exists(Directory.GetCurrentDirectory() + @"/Cache/PS1fPKG.gp4"))
+ {
+ File.Delete(Directory.GetCurrentDirectory() + @"/Cache/PS1fPKG.gp4");
+ }
+ Directory.CreateDirectory(GameCacheDirectory);
+
+ // Copy the PS1 emulator to the cache directory
+ CopyFilesRecursively(Directory.GetCurrentDirectory() + @"/Tools/PS4/emus/ps1hd", GameCacheDirectory);
+
+ // Copy the selected icon and background to the cache directory
+ if (!Directory.Exists(GameCacheDirectory + @"/sce_sys"))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/sce_sys");
+ }
+ if (!string.IsNullOrEmpty(SelectedPS1IconTextBox.Text))
+ {
+ File.Copy(SelectedPS1IconTextBox.Text, GameCacheDirectory + @"/sce_sys/icon0.png", true);
+ }
+ if (!string.IsNullOrEmpty(SelectedPS1BGTextBox.Text))
+ {
+ File.Copy(SelectedPS1BGTextBox.Text, GameCacheDirectory + @"/sce_sys/pic0.png", true);
+ }
+
+ // PS1 Emulator configuration
+ string Disc1CueFile = "";
+ using (var ConfigWriter = new StreamWriter(GameCacheDirectory + @"/config-title.txt", false))
+ {
+ ConfigWriter.WriteLine("--ps4-trophies=0");
+ ConfigWriter.WriteLine("--ps5-uds=0");
+ ConfigWriter.WriteLine("--trophies=0");
+
+ // Setup discs
+ Disc1CueFile = PS1SelectedDisc1TextBox.Text.Replace(".bin", ".cue");
+ ConfigWriter.WriteLine("--image=\"data/disc1.bin\"");
+
+ string Disc1BINFileName = Path.GetFileName(PS1SelectedDisc1TextBox.Text);
+ string Disc1CUEFileName = Path.GetFileName(Disc1CueFile);
+
+ // Copy disc 1 to the wine 'fPKG' folder
+ if (File.Exists(PS1SelectedDisc1TextBox.Text) && !File.Exists(wineDriveC + "/fPKG/" + Disc1BINFileName))
+ {
+ File.Copy(PS1SelectedDisc1TextBox.Text, wineDriveC + "/fPKG/" + Disc1BINFileName, true);
+ File.Copy(Disc1CueFile, wineDriveC + "/fPKG/" + Disc1CUEFileName, true);
+ }
+
+ // Copy other discs (if not empty) to the wine 'fPKG' folder
+ if (!string.IsNullOrEmpty(PS1SelectedDisc2TextBox.Text))
+ {
+ string Disc2CueFile = PS1SelectedDisc2TextBox.Text.Replace(".bin", ".cue");
+ ConfigWriter.WriteLine("--image=\"data/disc2.bin\"");
+
+ string Disc2BINFileName = Path.GetFileName(PS1SelectedDisc2TextBox.Text);
+ string Disc2CUEFileName = Path.GetFileName(Disc2CueFile);
+
+ if (File.Exists(PS1SelectedDisc2TextBox.Text) && !File.Exists(wineDriveC + "/fPKG/" + Disc2BINFileName))
+ {
+ File.Copy(PS1SelectedDisc2TextBox.Text, wineDriveC + "/fPKG/" + Disc2BINFileName, true);
+ File.Copy(Disc2CueFile, wineDriveC + "/fPKG/" + Disc2CUEFileName, true);
+ }
+ }
+ if (!string.IsNullOrEmpty(PS1SelectedDisc3TextBox.Text))
+ {
+ string Disc3CueFile = PS1SelectedDisc3TextBox.Text.Replace(".bin", ".cue");
+ ConfigWriter.WriteLine("--image=\"data/disc3.bin\"");
+
+ string Disc3BINFileName = Path.GetFileName(PS1SelectedDisc3TextBox.Text);
+ string Disc3CUEFileName = Path.GetFileName(Disc3CueFile);
+
+ if (File.Exists(PS1SelectedDisc3TextBox.Text) && !File.Exists(wineDriveC + "/fPKG/" + Disc3BINFileName))
+ {
+ File.Copy(PS1SelectedDisc3TextBox.Text, wineDriveC + "/fPKG/" + Disc3BINFileName, true);
+ File.Copy(Disc3CueFile, wineDriveC + "/fPKG/" + Disc3CUEFileName, true);
+ }
+ }
+ if (!string.IsNullOrEmpty(PS1SelectedDisc4TextBox.Text))
+ {
+ string Disc4CueFile = PS1SelectedDisc4TextBox.Text.Replace(".bin", ".cue");
+ ConfigWriter.WriteLine("--image=\"data/disc4.bin\"");
+
+ string Disc4BINFileName = Path.GetFileName(PS1SelectedDisc4TextBox.Text);
+ string Disc4CUEFileName = Path.GetFileName(Disc4CueFile);
+
+ if (File.Exists(PS1SelectedDisc4TextBox.Text) && !File.Exists(wineDriveC + "/fPKG/" + Disc4BINFileName))
+ {
+ File.Copy(PS1SelectedDisc4TextBox.Text, wineDriveC + "/fPKG/" + Disc4BINFileName, true);
+ File.Copy(Disc4CueFile, wineDriveC + "/fPKG/" + Disc4CUEFileName, true);
+ }
+ }
+
+ // Check for libcrypt protection
+ string FormattedGameID = "";
+ if (string.IsNullOrEmpty(CurrentPS1GameID))
+ {
+ FormattedGameID = PS1NPTitleTextBox.Text.Insert(4, "_").Insert(8, ".");
+ }
+ else
+ {
+ FormattedGameID = CurrentPS1GameID.Replace("-", "_").Insert(8, ".");
+ }
+ string ProtectionValue = PS1Game.IsGameProtected(FormattedGameID);
+ if (!string.IsNullOrEmpty(ProtectionValue))
+ {
+ ConfigWriter.WriteLine("--libcrypt=" + ProtectionValue);
+ }
+
+ // Check for LUA config
+ if (!string.IsNullOrEmpty(SelectedPS1LUAConfigTextBox.Text))
+ {
+ ConfigWriter.WriteLine("--ps1-title-id=" + PS1NPTitleTextBox.Text);
+ File.Copy(SelectedPS1LUAConfigTextBox.Text, GameCacheDirectory + @"/scripts/" + PS1NPTitleTextBox.Text + ".lua", true);
+ }
+
+ // Graphics & other configs
+ if (PS1UpscalingComboBox.SelectedItem is ComboBoxItem PS1UpscalingValue && PS1UpscalingValue.Content != null)
+ {
+ ConfigWriter.WriteLine("--scale=" + PS1UpscalingValue.Content.ToString());
+ }
+
+ if (PS1SkipBootlogoCheckBox.IsChecked == true)
+ {
+ ConfigWriter.WriteLine("--bios-hide-sce-osd=1");
+ }
+ if (PS1GunconCheckBox.IsChecked == true)
+ {
+ ConfigWriter.WriteLine("--guncon");
+ }
+ if (PS1Force60HzCheckBox.IsChecked == true)
+ {
+ ConfigWriter.WriteLine("--gpu-scanout-fps-override=60");
+ }
+ if (PS1EmulateAnalogSticksCheckBox.IsChecked == true)
+ {
+ ConfigWriter.WriteLine("--sim-analog-pad=0x2020");
+ }
+
+ // Check for TXT config
+ if (!string.IsNullOrEmpty(SelectedPS1TXTConfigTextBox.Text))
+ {
+ ConfigWriter.WriteLine("#User imported config");
+ ConfigWriter.WriteLine(File.ReadAllText(SelectedPS1TXTConfigTextBox.Text));
+ }
+ }
+
+ // Create a new PARAM.SFO file
+ var NewPS4ParamSFO = new ParamSfo();
+ NewPS4ParamSFO.SetValue("APP_TYPE", SfoEntryType.Integer, "1", 4);
+ NewPS4ParamSFO.SetValue("APP_VER", SfoEntryType.Utf8, "01.00", 8);
+ NewPS4ParamSFO.SetValue("ATTRIBUTE", SfoEntryType.Integer, "0", 4);
+ NewPS4ParamSFO.SetValue("CATEGORY", SfoEntryType.Utf8, "gd", 4);
+ NewPS4ParamSFO.SetValue("CONTENT_ID", SfoEntryType.Utf8, "UP9000-" + PS1NPTitleTextBox.Text + "_00-" + PS1NPTitleTextBox.Text + "PS1FPKG", 48);
+ NewPS4ParamSFO.SetValue("DOWNLOAD_DATA_SIZE", SfoEntryType.Integer, "0", 4);
+ NewPS4ParamSFO.SetValue("FORMAT", SfoEntryType.Utf8, "obs", 4);
+ NewPS4ParamSFO.SetValue("PARENTAL_LEVEL", SfoEntryType.Integer, "5", 4);
+ NewPS4ParamSFO.SetValue("SYSTEM_VER", SfoEntryType.Integer, "0", 4);
+ NewPS4ParamSFO.SetValue("TITLE", SfoEntryType.Utf8, PS1TitleTextBox.Text, 128);
+ NewPS4ParamSFO.SetValue("TITLE_ID", SfoEntryType.Utf8, PS1NPTitleTextBox.Text, 12);
+ NewPS4ParamSFO.SetValue("VERSION", SfoEntryType.Utf8, "01.00", 8);
+
+ File.WriteAllBytes(GameCacheDirectory + @"/sce_sys/param.sfo", NewPS4ParamSFO.Serialize());
+
+ // Create Disc TOC file
+ var GameBinFileInfo = new FileInfo(PS1SelectedDisc1TextBox.Text);
+ string CUE2TOCCMD = "\"" + Directory.GetCurrentDirectory() + @"/Tools/cue2toc" + "\" \"" + Disc1CueFile + "\" --size " + GameBinFileInfo.Length.ToString();
+ var EscapedArgs = CUE2TOCCMD.Replace("\"", "\\\"");
+ var CUE2TOCProcess = new Process();
+ CUE2TOCProcess.StartInfo.FileName = "/bin/bash";
+ CUE2TOCProcess.StartInfo.Arguments = $"-c \"{EscapedArgs}\"";
+ CUE2TOCProcess.StartInfo.WorkingDirectory = Path.GetDirectoryName(Disc1CueFile);
+ CUE2TOCProcess.StartInfo.UseShellExecute = false;
+ CUE2TOCProcess.StartInfo.CreateNoWindow = true;
+ CUE2TOCProcess.Start();
+ CUE2TOCProcess.WaitForExit();
+
+ // Move Disc TOC file
+ if (!Directory.Exists(GameCacheDirectory + @"/data"))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/data");
+ }
+ File.Move(Path.GetDirectoryName(PS1SelectedDisc1TextBox.Text) + @"/" + Path.GetFileNameWithoutExtension(PS1SelectedDisc1TextBox.Text) + ".TOC", GameCacheDirectory + @"/data/disc1.toc");
+
+ // Generate a GP4 project
+ string GenGP4CCMD = "wine \"c:\\PS4\\gengp4_patch.exe\" \"c:\\Cache\\PS1fPKG\"";
+ EscapedArgs = GenGP4CCMD.Replace("\"", "\\\"");
+ var NewProcess = new Process();
+ NewProcess.StartInfo.FileName = "/bin/bash";
+ NewProcess.StartInfo.Arguments = $"-c \"{EscapedArgs}\"";
+ NewProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ NewProcess.StartInfo.CreateNoWindow = true;
+ NewProcess.Start();
+ NewProcess.WaitForExit();
+
+ // Modify the GP4 project and add disc info
+ var BaseFileNameWithExt = Path.GetFileName(PS1SelectedDisc1TextBox.Text);
+ var BaseFileName = BaseFileNameWithExt.Replace(".bin", "");
+ string Disc1CUEFilePath = "C:\\fPKG\\" + BaseFileName + ".cue";
+ string Disc1BINFilePath = "C:\\fPKG\\" + BaseFileName + ".bin";
+
+ string XMLDisc1CuePath = $"\n ";
+ string XMLDisc1BinPath = $"\n ";
+
+ var fileContents = File.ReadAllText(CacheDirectory + "/PS1fPKG.gp4");
+ fileContents = fileContents.Replace("", "");
+ fileContents = fileContents.Replace("", $"{XMLDisc1CuePath}{XMLDisc1BinPath}\n");
+ File.WriteAllText(CacheDirectory + "/PS1fPKG.gp4", fileContents);
+
+ if (!string.IsNullOrEmpty(PS1SelectedDisc2TextBox.Text))
+ {
+ BaseFileNameWithExt = Path.GetFileName(PS1SelectedDisc2TextBox.Text);
+ BaseFileName = BaseFileNameWithExt.Replace(".bin", "");
+ string Disc2CUEFilePath = "C:\\fPKG\\" + BaseFileName + ".cue";
+ string Disc2BINFilePath = "C:\\fPKG\\" + BaseFileName + ".bin";
+
+ string XMLDisc2CuePath = $"\n ";
+ string XMLDisc2BinPath = $"\n ";
+
+ fileContents = File.ReadAllText(CacheDirectory + "/PS1fPKG.gp4");
+ fileContents = fileContents.Replace("", $"{XMLDisc2CuePath}{XMLDisc2BinPath}\n");
+ File.WriteAllText(CacheDirectory + "/PS1fPKG.gp4", fileContents);
+ }
+ if (!string.IsNullOrEmpty(PS1SelectedDisc3TextBox.Text))
+ {
+ BaseFileNameWithExt = Path.GetFileName(PS1SelectedDisc3TextBox.Text);
+ BaseFileName = BaseFileNameWithExt.Replace(".bin", "");
+ string Disc3CUEFilePath = "C:\\fPKG\\" + BaseFileName + ".cue";
+ string Disc3BINFilePath = "C:\\fPKG\\" + BaseFileName + ".bin";
+
+ string XMLDisc3CuePath = $"\n ";
+ string XMLDisc3BinPath = $"\n ";
+
+ fileContents = File.ReadAllText(CacheDirectory + "/PS1fPKG.gp4");
+ fileContents = fileContents.Replace("", $"{XMLDisc3CuePath}{XMLDisc3BinPath}\n");
+ File.WriteAllText(CacheDirectory + "/PS1fPKG.gp4", fileContents);
+ }
+ if (!string.IsNullOrEmpty(PS1SelectedDisc4TextBox.Text))
+ {
+ BaseFileNameWithExt = Path.GetFileName(PS1SelectedDisc4TextBox.Text);
+ BaseFileName = BaseFileNameWithExt.Replace(".bin", "");
+ string Disc4CUEFilePath = "C:\\fPKG\\" + BaseFileName + ".cue";
+ string Disc4BINFilePath = "C:\\fPKG\\" + BaseFileName + ".bin";
+
+ string XMLDisc4CuePath = $"\n ";
+ string XMLDisc4BinPath = $"\n ";
+
+ fileContents = File.ReadAllText(CacheDirectory + "/PS1fPKG.gp4");
+ fileContents = fileContents.Replace("", $"{XMLDisc4CuePath}{XMLDisc4BinPath}\n");
+ File.WriteAllText(CacheDirectory + "/PS1fPKG.gp4", fileContents);
+ }
+
+ var DebugMessageBox = MessageBoxManager.GetMessageBoxStandard("PS fPKG Classics Builder", "All files ready!\r\nfPKG can be build.", ButtonEnum.Ok);
+ await DebugMessageBox.ShowWindowDialogAsync(window);
+
+ // Create the fPKG
+ string PUBCMD = "wine \"c:\\PS4\\orbis-pub-cmd-3.38.exe\" img_create --oformat pkg --skip_digest --no_progress_bar \"c:\\Cache\\PS1fPKG.gp4\" \"c:\\fPKG\"";
+ EscapedArgs = PUBCMD.Replace("\"", "\\\"");
+ string PKGBuilderProcessOutput;
+ var PKGBuilderProcess = new Process();
+ PKGBuilderProcess.StartInfo.FileName = "/bin/bash";
+ PKGBuilderProcess.StartInfo.Arguments = $"-c \"{EscapedArgs}\"";
+ PKGBuilderProcess.StartInfo.UseShellExecute = false;
+ PKGBuilderProcess.StartInfo.RedirectStandardOutput = true;
+ PKGBuilderProcess.StartInfo.CreateNoWindow = true;
+ PKGBuilderProcess.Start();
+ PKGBuilderProcess.WaitForExit();
+
+ // Read the process output
+ using (var NewStreamReader = PKGBuilderProcess.StandardOutput)
+ {
+ PKGBuilderProcessOutput = NewStreamReader.ReadToEnd();
+ }
+ if (PKGBuilderProcessOutput.Contains("Create image Process finished with warning"))
+ {
+ string PKGFileName = "UP9000-" + PS1NPTitleTextBox.Text + "_00-" + PS1NPTitleTextBox.Text + "PS1FPKG-A0100-V0100.pkg";
+ string PKGFilePath = wineDriveC + "/fPKG/" + PKGFileName;
+ if (File.Exists(PKGFilePath))
+ {
+ try
+ {
+ File.Move(PKGFilePath, PKGOutputFolder + "/" + PKGFileName);
+ }
+ catch (Exception error)
+ {
+ Console.WriteLine(error);
+ }
+ }
+
+ var PKGBuildMessageBox = MessageBoxManager.GetMessageBoxStandard("DEBUG", "fPKG created with success!", ButtonEnum.Ok);
+ await PKGBuildMessageBox.ShowWindowDialogAsync(window);
+ }
+ else
+ {
+ var PKGBuildMessageBox = MessageBoxManager.GetMessageBoxStandard("DEBUG", "Error creating fPKG", ButtonEnum.Ok);
+ await PKGBuildMessageBox.ShowWindowDialogAsync(window);
+ }
+
+ }
+ else { return; }
+
+ }
+
+ public void ReadPS1BIN(string GameBIN)
+ {
+ string GameID = "";
+ string GameTitle = "";
+
+ using var Bash = new Process();
+
+ var StringsCMD = $"strings \"{GameBIN}\" | fgrep BOOT";
+ var EscapedArgs = StringsCMD.Replace("\"", "\\\"");
+
+ Bash.StartInfo.FileName = "/bin/bash";
+ Bash.StartInfo.Arguments = $"-c \"{EscapedArgs}\"";
+ Bash.StartInfo.RedirectStandardOutput = true;
+ Bash.StartInfo.RedirectStandardError = true;
+ Bash.StartInfo.UseShellExecute = false;
+ Bash.StartInfo.CreateNoWindow = false;
+ Bash.Start();
+ Bash.WaitForExit();
+
+ var OutputReader = Bash.StandardOutput;
+ string[] ProcessOutput = OutputReader.ReadToEnd().Split(["\r\n"], StringSplitOptions.RemoveEmptyEntries);
+
+ if (ProcessOutput.Length > 0)
+ {
+ foreach (var OutputLine in ProcessOutput)
+ {
+ if (OutputLine.Contains("BOOT =") | OutputLine.Contains("BOOT="))
+ {
+ GameID = OutputLine.Replace(@"BOOT = cdrom:\", "").Replace(@"BOOT=cdrom:\", "").Replace("BOOT = cdrom:", "").Replace(";1", "").Replace("_", "-").Replace(".", "").Replace(@"MGS\", "").Trim();
+ GameID = GameID.ToUpper().Trim();
+
+ // Try to get a game title from the master list
+ if (GameID.Length == 10)
+ {
+ GameTitle = PS1Game.GetPS1GameTitleFromDatabaseList(GameID);
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (!string.IsNullOrEmpty(GameID))
+ {
+ CurrentPS1GameID = GameID.Trim();
+ PS1NPTitleTextBox.Text = GameID.Replace("-", "").Trim();
+ PS1SelectedDisc1TextBox.Text = GameBIN;
+ }
+ if (!string.IsNullOrEmpty(GameTitle))
+ {
+ PS1TitleTextBox.Text = GameTitle;
+ }
+
+ }
+
+ #endregion
+
+ #region PS2
+
+ private string CurrentPS2GameID = "";
+ private string CurrentPS2GameCRC = "";
+
+ private async void BrowsePS2Disc1Button_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "Select a PS2 ISO file.", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "ISO files", Extensions = { "iso" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+ if (selectedFile[0] is not null)
+ {
+ string PS2GameID = PS2Game.GetPS2GameID(selectedFile[0]);
+ Console.WriteLine(PS2GameID);
+ string ExtractedPS2GameELFPath = GetELFfromISO(selectedFile[0], PS2GameID);
+ CurrentPS2GameID = PS2GameID;
+
+ string PS2GameCRC = GetGameCRC(ExtractedPS2GameELFPath);
+ PS2GameCRC = CRCRegex().Replace(PS2GameCRC, "");
+ CurrentPS2GameCRC = PS2GameCRC;
+
+ if (!string.IsNullOrEmpty(PS2GameID) && !string.IsNullOrEmpty(PS2GameCRC))
+ {
+ // Check for existing TXT config in DB
+ if (IsConfigAvailable(PS2GameID, Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/configs_txt.dat"))
+ {
+ PS2AddTXTConfigFromDatabaseCheckBox.IsChecked = true;
+ PS2AddTXTConfigFromDatabaseCheckBox.IsVisible = true;
+ }
+ else
+ {
+ PS2AddTXTConfigFromDatabaseCheckBox.IsChecked = false;
+ PS2AddTXTConfigFromDatabaseCheckBox.IsVisible = false;
+ }
+ // Check for existing LUA config in DB
+ if (IsConfigAvailable(PS2GameID, Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/configs_lua.dat"))
+ {
+ PS2AddLUAConfigFromDatabaseCheckBox.IsChecked = true;
+ PS2AddLUAConfigFromDatabaseCheckBox.IsVisible = true;
+ }
+ else
+ {
+ PS2AddLUAConfigFromDatabaseCheckBox.IsChecked = false;
+ PS2AddLUAConfigFromDatabaseCheckBox.IsVisible = false;
+ }
+ // Check for existing PS3 config in DB
+ if (IsConfigAvailable(PS2GameID + ".CONFIG", Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/configs_ps3.dat"))
+ {
+ PS2AddPS3ConfigFromDatabaseCheckBox.IsChecked = true;
+ PS2AddPS3ConfigFromDatabaseCheckBox.IsVisible = true;
+ }
+ else
+ {
+ PS2AddPS3ConfigFromDatabaseCheckBox.IsChecked = false;
+ PS2AddPS3ConfigFromDatabaseCheckBox.IsVisible = false;
+ }
+ // Check for existing Widescreen patch in DB
+ if (IsConfigAvailable(PS2GameCRC + ".lua", Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/widescreen.dat"))
+ {
+ PS2UseWidescreenPatchCheckBox.IsChecked = true;
+ PS2UseWidescreenPatchCheckBox.IsEnabled = true;
+ }
+ else
+ {
+ PS2UseWidescreenPatchCheckBox.IsEnabled = false;
+ }
+
+ PS2TitleTextBox.Text = PS2Game.GetPS2GameTitleFromDatabaseList(PS2GameID.Replace(".", "").Replace("_", "-").Trim());
+ PS2NPTitleTextBox.Text = PS2GameID.Replace(".", "").Replace("_", "").Trim();
+ SelectedDisc1TextBox.Text = selectedFile[0];
+ }
+ else
+ {
+ var NoGameIDMessage = MessageBoxManager.GetMessageBoxStandard("PS Classics fPKG Builder", "Could not find the PS2 game ID within the ISO file.\r\nDo you want to use this file anyway ?", ButtonEnum.YesNo);
+ if (NoGameIDMessage.ShowWindowDialogAsync(window).Result == ButtonResult.Yes)
+ {
+ SelectedDisc1TextBox.Text = selectedFile[0];
+ }
+ }
+ }
+ else
+ {
+ var box = MessageBoxManager.GetMessageBoxStandard("PS Classics fPKG Builder", "Aborted", ButtonEnum.Ok);
+ await box.ShowWindowDialogAsync(window);
+ }
+
+ }
+
+ private async void BrowsePS2Disc2Button_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "ISO files", Extensions = { "iso" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedDisc2TextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS2Disc3Button_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "ISO files", Extensions = { "iso" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedDisc3TextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS2Disc4Button_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "ISO files", Extensions = { "iso" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedDisc4TextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS2Disc5Button_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "ISO files", Extensions = { "iso" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedDisc5TextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS2TXTConfigButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "TXT files", Extensions = { "txt" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedDisc5TextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS2LUAConfigButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "LUA files", Extensions = { "lua" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedDisc5TextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS2MCButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "BIN files", Extensions = { "bin" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedDisc5TextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS2IconButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "Select a PNG icon file", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "PNG files", Extensions = { "png" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedIconTextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePS2StartupImageButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "Select a PNG background file", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "PNG files", Extensions = { "png" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedStartupImageTextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BuildPS2fPKGButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ // Checks before fPKG creation
+ if (string.IsNullOrEmpty(SelectedDisc1TextBox.Text))
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "No disc 1 specified, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+ if (string.IsNullOrEmpty(PS2TitleTextBox.Text))
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "No game title specified, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+ if (string.IsNullOrEmpty(PS2NPTitleTextBox.Text))
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "No NP title specified, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+ if (PS2NPTitleTextBox.Text.Length != 9)
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "'NP Title' length mismatching, only 9 characters are allowed, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+
+ // Get disc count
+ int DiscCount = 0;
+ if (!string.IsNullOrEmpty(SelectedDisc1TextBox.Text))
+ {
+ DiscCount += 1;
+ }
+ if (!string.IsNullOrEmpty(SelectedDisc2TextBox.Text))
+ {
+ DiscCount += 1;
+ }
+ if (!string.IsNullOrEmpty(SelectedDisc3TextBox.Text))
+ {
+ DiscCount += 1;
+ }
+ if (!string.IsNullOrEmpty(SelectedDisc4TextBox.Text))
+ {
+ DiscCount += 1;
+ }
+ if (!string.IsNullOrEmpty(SelectedDisc5TextBox.Text))
+ {
+ DiscCount += 1;
+ }
+
+ // Select output folder
+ var newOpenFolderDialog = new OpenFolderDialog() { Title = "Please select an output folder" };
+ var selectedFolder = await newOpenFolderDialog.ShowAsync(window);
+
+ if (selectedFolder != null)
+ {
+ string SelectedPS2Emulator = "";
+ if (PS2EmulatorComboBox.SelectedItem is ComboBoxItem SelectedPS2EmulatorComboBoxItem)
+ {
+ if (SelectedPS2EmulatorComboBoxItem.Content != null)
+ {
+ SelectedPS2Emulator = SelectedPS2EmulatorComboBoxItem.Content.ToString() ?? "";
+ }
+ }
+
+ string FullPS2GameID = CurrentPS2GameID.Replace(".", "").Replace("_", "-").Trim();
+ string PKGOutputFolder = selectedFolder;
+ string CacheDirectory = wineDriveC + @"/Cache";
+ string GameCacheDirectory = wineDriveC + @"/Cache/PS2fPKG";
+
+ // Remove previous fPKG creation & re-create the PS1fPKG cache folder
+ if (Directory.Exists(GameCacheDirectory))
+ {
+ Directory.Delete(GameCacheDirectory, true);
+ }
+ if (File.Exists(Directory.GetCurrentDirectory() + @"/Cache/PS2fPKG.gp4"))
+ {
+ File.Delete(Directory.GetCurrentDirectory() + @"/Cache/PS2fPKG.gp4");
+ }
+ Directory.CreateDirectory(GameCacheDirectory);
+
+ // Copy the PS2 emulator to the cache directory
+ CopyFilesRecursively(Directory.GetCurrentDirectory() + @"/Tools/PS4/emus/" + SelectedPS2Emulator, GameCacheDirectory);
+
+ // Copy the selected icon and background to the cache directory
+ if (!Directory.Exists(GameCacheDirectory + @"/sce_sys"))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/sce_sys");
+ }
+ if (!string.IsNullOrEmpty(SelectedPS1IconTextBox.Text))
+ {
+ var IconBytes = File.ReadAllBytes(SelectedPS1IconTextBox.Text);
+ var ConvertedIcon = Utils.ConvertTo24bppPNG(IconBytes, 512, 512);
+ ConvertedIcon.Save(GameCacheDirectory + @"/sce_sys/icon0.png");
+
+ //File.Copy(SelectedPS1IconTextBox.Text, GameCacheDirectory + @"/sce_sys/icon0.png", true);
+ }
+ if (!string.IsNullOrEmpty(SelectedPS1BGTextBox.Text))
+ {
+ var BGBytes = File.ReadAllBytes(SelectedPS1BGTextBox.Text);
+ var ConvertedBG = Utils.ConvertTo24bppPNG(BGBytes, 1920, 1080);
+ ConvertedBG.Save(GameCacheDirectory + @"/sce_sys/pic0.png");
+
+ //File.Copy(SelectedPS1BGTextBox.Text, GameCacheDirectory + @"/sce_sys/pic0.png", true);
+ }
+
+ // Create a new PARAM.SFO file
+ var NewPS4ParamSFO = new ParamSfo();
+ NewPS4ParamSFO.SetValue("APP_TYPE", SfoEntryType.Integer, "1", 4);
+ NewPS4ParamSFO.SetValue("APP_VER", SfoEntryType.Utf8, "01.00", 8);
+ NewPS4ParamSFO.SetValue("ATTRIBUTE", SfoEntryType.Integer, "0", 4);
+ NewPS4ParamSFO.SetValue("CATEGORY", SfoEntryType.Utf8, "gd", 4);
+ NewPS4ParamSFO.SetValue("CONTENT_ID", SfoEntryType.Utf8, "UP9000-" + PS2NPTitleTextBox.Text + "_00-" + CurrentPS2GameID.Replace(".", "").Replace("_", "").Trim() + "0000001", 48);
+ NewPS4ParamSFO.SetValue("DOWNLOAD_DATA_SIZE", SfoEntryType.Integer, "0", 4);
+ NewPS4ParamSFO.SetValue("FORMAT", SfoEntryType.Utf8, "obs", 4);
+ NewPS4ParamSFO.SetValue("PARENTAL_LEVEL", SfoEntryType.Integer, "5", 4);
+ NewPS4ParamSFO.SetValue("REMOTE_PLAY_KEY_ASSIGN", SfoEntryType.Integer, "0", 4);
+ NewPS4ParamSFO.SetValue("SYSTEM_VER", SfoEntryType.Integer, "0", 4);
+ NewPS4ParamSFO.SetValue("TITLE", SfoEntryType.Utf8, PS2TitleTextBox.Text, 128);
+ NewPS4ParamSFO.SetValue("TITLE_ID", SfoEntryType.Utf8, PS2NPTitleTextBox.Text, 12);
+ NewPS4ParamSFO.SetValue("VERSION", SfoEntryType.Utf8, "01.00", 8);
+
+ File.WriteAllBytes(GameCacheDirectory + @"/sce_sys/param.sfo", NewPS4ParamSFO.Serialize());
+
+ // Create a new PS2 emulator configuration file
+ string UprenderValue = "";
+ if (PS2EmulatorComboBox.SelectedItem is ComboBoxItem UprenderComboBoxItem)
+ {
+ if (UprenderComboBoxItem.Content != null)
+ {
+ UprenderValue = UprenderComboBoxItem.Content.ToString() ?? "";
+ }
+ }
+ string UpscalingValue = "";
+ if (PS2EmulatorComboBox.SelectedItem is ComboBoxItem UpscalingComboBoxItem)
+ {
+ if (UpscalingComboBoxItem.Content != null)
+ {
+ UprenderValue = UpscalingComboBoxItem.Content.ToString() ?? "";
+ }
+ }
+ string DisplayModeValue = "";
+ if (PS2EmulatorComboBox.SelectedItem is ComboBoxItem DisplayModeComboBoxItem)
+ {
+ if (DisplayModeComboBoxItem.Content != null)
+ {
+ UprenderValue = DisplayModeComboBoxItem.Content.ToString() ?? "";
+ }
+ }
+
+ string[] NewPS2EmulatorConfig = [ "--path-vmc=\"/tmp/vmc\"\r\n--config-local-lua=\"\"\r\n--ps2-title-id=",
+ FullPS2GameID, "\r\n--max-disc-num=",
+ DiscCount.ToString(),
+ "\r\n--gs-uprender=",
+ UprenderValue.ToLower(),
+ "\r\n--gs-upscale=",
+ UpscalingValue.ToLower(),
+ "\r\n--host-audio=1\r\n--rom=\"PS20220WD20050620.crack\"\r\n--verbose-cdvd-reads=0\r\n--host-display-mode=",
+ DisplayModeValue.ToLower() ];
+ string NewPS2EmulatorConfigContent = string.Concat(NewPS2EmulatorConfig);
+
+ // Reset on disc change config
+ if (PS2RestartEmulatorOnDiscChangeCheckBox.IsChecked == false)
+ {
+ NewPS2EmulatorConfigContent += "\r\n#Disable emu reset on disc change\r\n--switch-disc-reset=0";
+ }
+
+ // Multitap config
+ if (PS2MultitapComboBox.SelectedIndex == 1)
+ {
+ NewPS2EmulatorConfigContent += "\r\n#Enable Multitap\r\n--mtap1=always";
+ }
+ else if (PS2MultitapComboBox.SelectedIndex == 2)
+ {
+ NewPS2EmulatorConfigContent += "\r\n#Enable Multitap\r\n--mtap2=always";
+ }
+ else if (PS2MultitapComboBox.SelectedIndex == 3)
+ {
+ NewPS2EmulatorConfigContent += "\r\n#Enable Multitap\r\n--mtap1=always\r\n--mtap2=always";
+ }
+
+ // Check for PS3 config file
+ if (PS2AddPS3ConfigFromDatabaseCheckBox.IsChecked == true)
+ {
+ if (File.Exists(Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/ps3/" + CurrentPS2GameID + ".CONFIG"))
+ {
+ NewPS2EmulatorConfigContent += "\r\n--lopnor-config=1";
+
+ // Create patches directory
+ if (!Directory.Exists(GameCacheDirectory + @"/patches/" + FullPS2GameID))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/patches/" + FullPS2GameID);
+ }
+
+ File.Copy(Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/ps3/" + CurrentPS2GameID + ".CONFIG", GameCacheDirectory + @"/patches/" + FullPS2GameID + @"/" + FullPS2GameID + "_lopnor.cfgbin", true);
+ }
+
+ else if (IsConfigAvailable(CurrentPS2GameID + ".CONFIG", Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/configs_ps3.dat"))
+ {
+ if (!Directory.Exists(GameCacheDirectory + @"/patches/" + FullPS2GameID))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/patches/" + FullPS2GameID);
+ }
+
+ ExtractFileFromISO(Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/configs_ps3.dat", CurrentPS2GameID + ".CONFIG", GameCacheDirectory + @"/patches/" + FullPS2GameID + @"/" + FullPS2GameID + "_lopnor.cfgbin");
+ }
+ }
+
+ // Widescreen Patch cnofig
+ if (PS2UseWidescreenPatchCheckBox.IsChecked == true)
+ {
+ string WidescreenPatch = "";
+
+ if (File.Exists(Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/widescreen/" + CurrentPS2GameCRC + ".lua"))
+ {
+ WidescreenPatch = File.ReadAllText(Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/widescreen/" + CurrentPS2GameCRC + ".lua");
+ }
+ else if (IsConfigAvailable(CurrentPS2GameCRC + ".lua", Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/widescreen.dat"))
+ {
+ WidescreenPatch = GetPNACHFromDAT(Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/widescreen.dat", CurrentPS2GameCRC + ".lua");
+ }
+
+ if (!string.IsNullOrEmpty(WidescreenPatch))
+ {
+ NewPS2EmulatorConfigContent += "\r\n--path-trophydata=\"/app0/trophy_data\"";
+
+ if (!Directory.Exists(GameCacheDirectory + @"/trophy_data/"))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/trophy_data/");
+ }
+
+ File.WriteAllText(GameCacheDirectory + @"/trophy_data/" + FullPS2GameID + "_trophies.lua", WidescreenPatch);
+ }
+ }
+
+ // Copy lua_include to cache directory
+ CopyFilesRecursively(Directory.GetCurrentDirectory() + @"/Tools/PS4/lua_include", GameCacheDirectory + @"/lua_include");
+
+ // Check for LUA config
+ if (!string.IsNullOrEmpty(SelectedPS2LUAConfigTextBox.Text))
+ {
+ NewPS2EmulatorConfigContent += "\r\n--path-patches=\"/app0/patches";
+
+ // Create patches directory if it doesn't exist yet
+ if (!Directory.Exists(GameCacheDirectory + @"/patches/" + FullPS2GameID))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/patches/" + FullPS2GameID);
+ }
+
+ File.Copy(SelectedPS2LUAConfigTextBox.Text, GameCacheDirectory + @"/patches/" + FullPS2GameID + "_config.lua", true);
+ }
+ else if (PS2AddLUAConfigFromDatabaseCheckBox.IsChecked == true)
+ {
+ if (IsConfigAvailable(CurrentPS2GameID, Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/configs_lua.dat"))
+ {
+ NewPS2EmulatorConfigContent += "\r\n--path-patches=\"/app0/patches";
+
+ if (!Directory.Exists(GameCacheDirectory + @"/patches/" + FullPS2GameID))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/patches/" + FullPS2GameID);
+ }
+
+ ExtractFileFromISO(Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/configs_lua.dat", CurrentPS2GameID, GameCacheDirectory + @"/patches/" + FullPS2GameID + "_config.lua");
+ }
+ }
+
+ // Emulator fixes
+ string PS2EmulatorFixes = "";
+ if (PS2ImproveSpeedCheckBox.IsChecked == true)
+ {
+ PS2EmulatorFixes += "\r\n#Improve Speed\r\n-vu0-opt-flags=1\r\n--vu1-opt-flags=1\r\n--cop2-opt-flags=1\r\n--vu0-const-prop=0\r\n--vu1-const-prop=0\r\n--vu1-jr-cache-policy=newprog\r\n--vu1-jalr-cache-policy=newprog\r\n--vu0-jr-cache-policy=newprog\r\n--vu0-jalr-cache-policy=newprog";
+ }
+ if (PS2FixGraphicsCheckBox.IsChecked == true)
+ {
+ PS2EmulatorFixes += "\r\n#Fix Graphics\r\n--fpu-no-clamping=0\r\n--fpu-clamp-results=1\r\n--vu0-no-clamping=0\r\n--vu0-clamp-results=1\r\n--vu1-no-clamping=0\r\n--vu1-clamp-results=1\r\n--cop2-no-clamping=0\r\n--cop2-clamp-results=1";
+ }
+ if (PS2DisableMTVUCheckBox.IsChecked == true)
+ {
+ PS2EmulatorFixes += "\r\n#Disable MTVU\r\n--vu1=jit-sync";
+ }
+ if (PS2DisableInstantVIF1TransferCheckBox.IsChecked == true)
+ {
+ PS2EmulatorFixes += "\r\n#Disable Instant VIF1 Transfer\r\n--vif1-instant-xfer=0";
+ }
+
+ // Check for TXT config
+ if (!string.IsNullOrEmpty(SelectedPS2TXTConfigTextBox.Text))
+ {
+ string[] ModifiedPS2EmulatorConfigContent = [NewPS2EmulatorConfigContent, "\r\n", PS2EmulatorFixes, "\r\n#User Config\r\n", File.ReadAllText(SelectedPS2TXTConfigTextBox.Text)];
+ NewPS2EmulatorConfigContent = string.Concat(ModifiedPS2EmulatorConfigContent);
+ }
+ else if (PS2AddTXTConfigFromDatabaseCheckBox.IsChecked == true)
+ {
+ if (IsConfigAvailable(CurrentPS2GameID, Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/configs_txt.dat"))
+ {
+ NewPS2EmulatorConfigContent = NewPS2EmulatorConfigContent + "\r\n#" + CurrentPS2GameID + "\r\n" + GetPNACHFromDAT(Directory.GetCurrentDirectory() + @"/Tools/PS4/ps2-configs/configs_txt.dat", CurrentPS2GameID);
+ }
+ }
+ else
+ {
+ // Append only PS2EmulatorFixes
+ NewPS2EmulatorConfigContent = NewPS2EmulatorConfigContent + "\r\n" + PS2EmulatorFixes;
+ }
+
+ // Write config-emu-ps4.txt
+ if (File.Exists(GameCacheDirectory + @"/config-emu-ps4.txt"))
+ {
+ File.Delete(GameCacheDirectory + @"/config-emu-ps4.txt");
+ }
+ File.WriteAllText(GameCacheDirectory + @"/config-emu-ps4.txt", NewPS2EmulatorConfigContent);
+
+ if (!Directory.Exists(GameCacheDirectory + @"/feature_data/"))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/feature_data/");
+ }
+ if (!Directory.Exists(GameCacheDirectory + @"/feature_data/" + FullPS2GameID))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/feature_data/" + FullPS2GameID);
+ }
+
+ // Check for PS2 Memory Card file
+ if (!string.IsNullOrEmpty(SelectedPS2MemoryCardTextBox.Text))
+ {
+ File.Copy(SelectedPS2MemoryCardTextBox.Text, GameCacheDirectory + @"/feature_data/" + FullPS2GameID + @"/custom.card", true);
+ }
+
+ if (!Directory.Exists(GameCacheDirectory + @"/image"))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/image");
+ }
+
+ // Generate a GP4 project
+ string GenGP4CCMD = "wine \"c:\\PS4\\gengp4_patch.exe\" \"c:\\Cache\\PS2fPKG\"";
+ var EscapedArgs = GenGP4CCMD.Replace("\"", "\\\"");
+ var NewProcess = new Process();
+ NewProcess.StartInfo.FileName = "/bin/bash";
+ NewProcess.StartInfo.Arguments = $"-c \"{EscapedArgs}\"";
+ NewProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ NewProcess.StartInfo.CreateNoWindow = true;
+ NewProcess.Start();
+ NewProcess.WaitForExit();
+
+ // Modify the GP4 project and add disc info
+ File.WriteAllText(CacheDirectory + "/PS2fPKG.gp4", File.ReadAllText(CacheDirectory + "/PS2fPKG.gp4").Replace("", ""));
+
+ // Copy selected discs to the inner fPKG folder (if not exists) and add to GP4 project file
+ string FullDiscInfo = "";
+ string Disc1FileNameWithExtension = Path.GetFileName(SelectedDisc1TextBox.Text);
+ string Disc1FileNameWithoutExtension = Path.GetFileNameWithoutExtension(Disc1FileNameWithExtension);
+ string Disc1ISOFilePath = "C:\\fPKG\\" + Disc1FileNameWithoutExtension + ".iso";
+
+ string[] Disc1Info = [FullDiscInfo, $"\n "];
+ FullDiscInfo = string.Join("", Disc1Info);
+
+ if (!File.Exists(wineDriveC + "/fPKG/" + Disc1FileNameWithExtension))
+ {
+ File.Copy(SelectedDisc1TextBox.Text, wineDriveC + "/fPKG/" + Disc1FileNameWithExtension);
+ }
+
+ if (!string.IsNullOrEmpty(SelectedDisc2TextBox.Text))
+ {
+ string Disc2FileNameWithExtension = Path.GetFileName(SelectedDisc2TextBox.Text);
+ string Disc2FileNameWithoutExtension = Path.GetFileNameWithoutExtension(Disc2FileNameWithExtension);
+ string Disc2ISOFilePath = "C:\\fPKG\\" + Disc2FileNameWithoutExtension + ".iso";
+
+ string[] Disc2Info = [FullDiscInfo, $"\n "];
+ FullDiscInfo = string.Join("", Disc2Info);
+
+ if (!File.Exists(wineDriveC + "/fPKG/" + Disc2FileNameWithExtension))
+ {
+ File.Copy(SelectedDisc2TextBox.Text, wineDriveC + "/fPKG/" + Disc2FileNameWithExtension);
+ }
+ }
+ if (!string.IsNullOrEmpty(SelectedDisc3TextBox.Text))
+ {
+ string Disc3FileNameWithExtension = Path.GetFileName(SelectedDisc3TextBox.Text);
+ string Disc3FileNameWithoutExtension = Path.GetFileNameWithoutExtension(Disc3FileNameWithExtension);
+ string Disc3ISOFilePath = "C:\\fPKG\\" + Disc3FileNameWithoutExtension + ".iso";
+
+ string[] Disc3Info = [FullDiscInfo, $"\n "];
+ FullDiscInfo = string.Join("", Disc3Info);
+
+ if (!File.Exists(wineDriveC + "/fPKG/" + Disc3FileNameWithExtension))
+ {
+ File.Copy(SelectedDisc3TextBox.Text, wineDriveC + "/fPKG/" + Disc3FileNameWithExtension);
+ }
+ }
+ if (!string.IsNullOrEmpty(SelectedDisc4TextBox.Text))
+ {
+ string Disc4FileNameWithExtension = Path.GetFileName(SelectedDisc4TextBox.Text);
+ string Disc4FileNameWithoutExtension = Path.GetFileNameWithoutExtension(Disc4FileNameWithExtension);
+ string Disc4ISOFilePath = "C:\\fPKG\\" + Disc4FileNameWithoutExtension + ".iso";
+
+ string[] Disc4Info = [FullDiscInfo, $"\n "];
+ FullDiscInfo = string.Join("", Disc4Info);
+
+ if (!File.Exists(wineDriveC + "/fPKG/" + Disc4FileNameWithExtension))
+ {
+ File.Copy(SelectedDisc4TextBox.Text, wineDriveC + "/fPKG/" + Disc4FileNameWithExtension);
+ }
+ }
+ if (!string.IsNullOrEmpty(SelectedDisc5TextBox.Text))
+ {
+ string Disc5FileNameWithExtension = Path.GetFileName(SelectedDisc5TextBox.Text);
+ string Disc5FileNameWithoutExtension = Path.GetFileNameWithoutExtension(Disc5FileNameWithExtension);
+ string Disc5ISOFilePath = "C:\\fPKG\\" + Disc5FileNameWithoutExtension + ".iso";
+
+ string[] Disc5Info = [FullDiscInfo, $"\n "];
+ FullDiscInfo = string.Join("", Disc5Info);
+
+ if (!File.Exists(wineDriveC + "/fPKG/" + Disc5FileNameWithExtension))
+ {
+ File.Copy(SelectedDisc5TextBox.Text, wineDriveC + "/fPKG/" + Disc5FileNameWithExtension);
+ }
+ }
+
+ File.WriteAllText(CacheDirectory + "/PS2fPKG.gp4", File.ReadAllText(CacheDirectory + "/PS2fPKG.gp4").Replace("", $"{FullDiscInfo}\n"));
+
+ var DebugMessageBox = MessageBoxManager.GetMessageBoxStandard("PS fPKG Classics Builder", "All files ready! \r\n fPKG can be build.", ButtonEnum.Ok);
+ await DebugMessageBox.ShowWindowDialogAsync(window);
+
+ // Create the fPKG
+ string PUBCMD = "wine \"c:\\PS4\\orbis-pub-cmd-3.38.exe\" img_create --oformat pkg --skip_digest --no_progress_bar \"c:\\Cache\\PS2fPKG.gp4\" \"c:\\fPKG\"";
+ EscapedArgs = PUBCMD.Replace("\"", "\\\"");
+ string PKGBuilderProcessOutput;
+ var PKGBuilderProcess = new Process();
+ PKGBuilderProcess.StartInfo.FileName = "/bin/bash";
+ PKGBuilderProcess.StartInfo.Arguments = $"-c \"{EscapedArgs}\"";
+ PKGBuilderProcess.StartInfo.UseShellExecute = false;
+ PKGBuilderProcess.StartInfo.RedirectStandardOutput = true;
+ PKGBuilderProcess.StartInfo.CreateNoWindow = true;
+ PKGBuilderProcess.Start();
+ PKGBuilderProcess.WaitForExit();
+
+ // Read the process output
+ using (var NewStreamReader = PKGBuilderProcess.StandardOutput)
+ {
+ PKGBuilderProcessOutput = NewStreamReader.ReadToEnd();
+ }
+ if (PKGBuilderProcessOutput.Contains("Create image Process finished with warning"))
+ {
+ string PKGFileName = "UP9000-" + PS2NPTitleTextBox.Text + "_00-" + CurrentPS2GameID.Replace(".", "").Replace("_", "").Trim() + "0000001" + "-A0100-V0100.pkg";
+ string PKGFilePath = wineDriveC + "/fPKG/" + PKGFileName;
+ if (File.Exists(PKGFilePath))
+ {
+ try
+ {
+ File.Move(PKGFilePath, PKGOutputFolder + "/" + PKGFileName);
+ }
+ catch (Exception error)
+ {
+ Console.WriteLine(error);
+ }
+ }
+
+ var PKGBuildMessageBox = MessageBoxManager.GetMessageBoxStandard("DEBUG", "fPKG created with success!", ButtonEnum.Ok);
+ await PKGBuildMessageBox.ShowWindowDialogAsync(window);
+ }
+ else
+ {
+ DebugMessageBox = MessageBoxManager.GetMessageBoxStandard("DEBUG", "Error creating fPKG", ButtonEnum.Ok);
+ await DebugMessageBox.ShowWindowDialogAsync(window);
+ }
+ }
+ else { return; }
+
+ }
+
+ private static bool IsConfigAvailable(string GameID, string ConfigDatabaseFile)
+ {
+ bool Exists;
+ try
+ {
+ using var NewFileStream = new FileStream(ConfigDatabaseFile, FileMode.Open, FileAccess.Read);
+ var NewUdfReader = new DiscUtils.Udf.UdfReader(NewFileStream, 2048);
+ try
+ {
+ NewUdfReader.OpenFile(GameID, FileMode.Open);
+ Exists = true;
+ }
+ catch (Exception)
+ {
+ Exists = false;
+ }
+ }
+ catch (Exception)
+ {
+ Exists = false;
+ }
+ return Exists;
+ }
+
+ private static string GetELFfromISO(string GameISOFile, string GameELFName)
+ {
+ string ExtractedELFPath = "";
+
+ string CacheDir = Directory.GetCurrentDirectory() + @"/Cache";
+ if (!Directory.Exists(CacheDir))
+ {
+ Directory.CreateDirectory(CacheDir);
+ }
+
+ try
+ {
+ using var NewFileStream = new FileStream(GameISOFile, FileMode.Open, FileAccess.Read);
+ var NewCDReader = new DiscUtils.Iso9660.CDReader(NewFileStream, true);
+ try
+ {
+ var ELFExtractionFileStream = new FileStream(CacheDir + @"/" + GameELFName, FileMode.Create);
+ NewCDReader.OpenFile(GameELFName, FileMode.Open).CopyTo(ELFExtractionFileStream);
+
+ ExtractedELFPath = CacheDir + @"/" + GameELFName;
+ }
+ catch (Exception)
+ {
+ ExtractedELFPath = "";
+ }
+ }
+ catch (Exception)
+ {
+ return ExtractedELFPath;
+ }
+ return ExtractedELFPath;
+ }
+
+ private static string GetGameCRC(string PS2GamePath)
+ {
+ // Get ELF crc
+ string CRCCCMD = "\"" + Directory.GetCurrentDirectory() + @"/Tools/crc" + "\" \"" + PS2GamePath + "\"";
+ var EscapedArgs = CRCCCMD.Replace("\"", "\\\"");
+ var CRCProcess = new Process();
+
+ CRCProcess.StartInfo.FileName = "/bin/bash";
+ CRCProcess.StartInfo.Arguments = $"-c \"{EscapedArgs}\"";
+ CRCProcess.StartInfo.UseShellExecute = false;
+ CRCProcess.StartInfo.CreateNoWindow = true;
+ CRCProcess.StartInfo.RedirectStandardOutput = true;
+ CRCProcess.Start();
+ CRCProcess.WaitForExit();
+
+ using var NewStreamReader = CRCProcess.StandardOutput;
+ return NewStreamReader.ReadToEnd().Replace("crc:", "").Trim();
+ }
+
+ private static string ExtractFileFromISO(string GameISOFile, string FileToExtract, string FileDestinationPath)
+ {
+ string ReturnedFileDestinationPath;
+ try
+ {
+ using var NewFileStream = new FileStream(GameISOFile, FileMode.Open, FileAccess.Read);
+ var NewUdfReader = new DiscUtils.Udf.UdfReader(NewFileStream, 2048);
+ try
+ {
+ var NewFileStream2 = new FileStream(FileDestinationPath, FileMode.Create);
+ NewUdfReader.OpenFile(FileToExtract, FileMode.Open).CopyTo(NewFileStream2);
+ NewFileStream2.Close();
+ ReturnedFileDestinationPath = FileDestinationPath;
+ }
+ catch (Exception)
+ {
+ ReturnedFileDestinationPath = "";
+ }
+ }
+ catch (Exception)
+ {
+ ReturnedFileDestinationPath = "";
+ }
+ return ReturnedFileDestinationPath;
+ }
+
+ private static string GetPNACHFromDAT(string DATFile, string FileToExtract)
+ {
+ string PNACHString;
+ try
+ {
+ using var NewFileStream = new FileStream(DATFile, FileMode.Open, FileAccess.Read);
+ var NewUdfReader = new DiscUtils.Udf.UdfReader(NewFileStream, 2048);
+ try
+ {
+ PNACHString = new StreamReader(NewUdfReader.OpenFile(FileToExtract, FileMode.Open)).ReadToEnd();
+ }
+ catch (Exception)
+ {
+ PNACHString = "";
+ }
+ }
+ catch (Exception)
+ {
+ PNACHString = "";
+ }
+ return PNACHString;
+ }
+
+ #endregion
+
+ #region PSP
+
+ private async void BrowsePSPIconButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "Select a PNG icon file", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "PNG files", Extensions = { "png" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedPSPIconTextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePSPBGButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "Select a PNG background file", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "PNG files", Extensions = { "png" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedPSPBGImageTextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BrowsePSPDiscButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "Select a PSP ISO file.", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "ISO files", Extensions = { "iso" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+ if (selectedFile[0] is not null)
+ {
+ if (FileExistInISO(selectedFile[0], @"\PSP_GAME\PARAM.SFO"))
+ {
+ string CacheDir = Directory.GetCurrentDirectory() + @"/Cache";
+ string ExtractedUMDDataPath = ExtractFileFromPSPISO(selectedFile[0], "UMD_DATA.BIN", CacheDir + @"/temp_umd_data.bin");
+
+ if (!string.IsNullOrEmpty(ExtractedUMDDataPath))
+ {
+ PSPNPTitleTextBox.Text = System.Text.Encoding.ASCII.GetString(ReadUMDData(CacheDir + @"/temp_umd_data.bin", 0L, 10)).Replace("-", "");
+ PSPTitleTextBox.Text = PSPTitleRegex1().Replace(Path.GetFileNameWithoutExtension(selectedFile[0]), "");
+ PSPTitleTextBox.Text = PSPTitleRegex2().Replace(PSPTitleTextBox.Text, "");
+ }
+
+ SelectedPSPDiscTextBox.Text = selectedFile[0];
+ }
+ else
+ {
+ var NoValidFileMessage = MessageBoxManager.GetMessageBoxStandard("PS fPKG Classics Builder", "Could not find any PSP game information within the ISO file.\r\nDo you want to use this file anyway ?", ButtonEnum.YesNo);
+ var NoValidFileMessageValue = await NoValidFileMessage.ShowWindowDialogAsync(window);
+ if (NoValidFileMessageValue == ButtonResult.Yes)
+ {
+ SelectedPSPDiscTextBox.Text = selectedFile[0];
+ }
+ }
+ }
+ }
+
+ private async void BrowsePSPConfigButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var newOpenFileDialog = new OpenFileDialog() { Title = "", AllowMultiple = false, Filters = [new FileDialogFilter() { Name = "TXT files", Extensions = { "txt" } }] };
+ var selectedFile = await newOpenFileDialog.ShowAsync(window);
+
+ if (selectedFile is null || selectedFile.Length == 0)
+ return;
+
+ if (selectedFile[0] is not null)
+ {
+ SelectedDisc5TextBox.Text = selectedFile[0];
+ }
+ }
+
+ private async void BuildPSPfPKGButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ // Checks before fPKG creation
+ if (string.IsNullOrEmpty(SelectedPSPDiscTextBox.Text))
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "No disc specified, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+ if (string.IsNullOrEmpty(PSPTitleTextBox.Text))
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "No game title specified, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+ if (string.IsNullOrEmpty(PSPNPTitleTextBox.Text))
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "No NP title specified, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+ if (PSPNPTitleTextBox.Text.Length != 9)
+ {
+ var ErrorMessageBox = MessageBoxManager.GetMessageBoxStandard("Cannot create fPKG", "'NP Title' length mismatching, only 9 characters are allowed, fPKG creation will be aborted.", ButtonEnum.Ok);
+ await ErrorMessageBox.ShowWindowDialogAsync(window);
+ return;
+ }
+
+ // Select output folder
+ var newOpenFolderDialog = new OpenFolderDialog() { Title = "Please select an output folder" };
+ var selectedFolder = await newOpenFolderDialog.ShowAsync(window);
+ string SelectedISOFile = SelectedPSPDiscTextBox.Text;
+
+ // Continue if selection is not empty
+ if (!string.IsNullOrWhiteSpace(selectedFolder))
+ {
+ string PKGOutputFolder = selectedFolder;
+ string CacheDirectory = wineDriveC + @"/Cache";
+ string GameCacheDirectory = wineDriveC + @"/Cache/PSPfPKG";
+
+ // Remove previous fPKG creation & re-create the PS1fPKG cache folder
+ if (Directory.Exists(GameCacheDirectory))
+ {
+ Directory.Delete(GameCacheDirectory, true);
+ }
+ if (File.Exists(Directory.GetCurrentDirectory() + @"/Cache/PSPfPKG.gp4"))
+ {
+ File.Delete(Directory.GetCurrentDirectory() + @"/Cache/PSPfPKG.gp4");
+ }
+ Directory.CreateDirectory(GameCacheDirectory);
+
+ // Copy the PSP emulator to the cache directory
+ CopyFilesRecursively(Directory.GetCurrentDirectory() + @"/Tools/PS4/emus/psphd", GameCacheDirectory);
+
+ // Copy the selected icon and background to the cache directory
+ if (!Directory.Exists(GameCacheDirectory + @"/sce_sys"))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/sce_sys");
+ }
+ if (!string.IsNullOrEmpty(SelectedPSPIconTextBox.Text))
+ {
+ File.Copy(SelectedPSPIconTextBox.Text, GameCacheDirectory + @"/sce_sys/icon0.png", true);
+ }
+ if (!string.IsNullOrEmpty(SelectedPSPBGImageTextBox.Text))
+ {
+ File.Copy(SelectedPSPBGImageTextBox.Text, GameCacheDirectory + @"/sce_sys/pic0.png", true);
+ }
+
+ // Get PSP EBOOT
+ if (!File.Exists(ExtractFileFromPSPISO(SelectedISOFile, @"\PSP_GAME\SYSDIR\EBOOT.BIN", CacheDirectory + @"/temp_eboot.bin")))
+ {
+ File.Copy(SelectedISOFile, GameCacheDirectory + @"/data/USER_L0.IMG", true);
+ var NoValidFileMessage = MessageBoxManager.GetMessageBoxStandard("PS fPKG Classics Builder", "Cannot read the EBOOT.BIN file from the ISO.\r\nWarning: This game may not work!", ButtonEnum.Ok);
+ await NoValidFileMessage.ShowWindowDialogAsync(window);
+ }
+ else
+ {
+ string PSPDecryptCMD = "\"" + Directory.GetCurrentDirectory() + @"/Tools/pspdecrypt" + "\"" + " \"" + CacheDirectory + @"/temp_eboot.bin" + "\"";
+ var EscapedArgs = PSPDecryptCMD.Replace("\"", "\\\"");
+ var PSPDecryptProcess = new Process();
+ PSPDecryptProcess.StartInfo.FileName = "/bin/bash";
+ PSPDecryptProcess.StartInfo.Arguments = $"-c \"{EscapedArgs}\"";
+ PSPDecryptProcess.StartInfo.CreateNoWindow = true;
+ PSPDecryptProcess.Start();
+ PSPDecryptProcess.WaitForExit();
+
+ if (!Directory.Exists(GameCacheDirectory + @"/data"))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/data");
+ }
+
+ File.Copy(SelectedISOFile, GameCacheDirectory + @"/data/USER_L0.IMG", true);
+
+ var NewFileInfo = new FileInfo(CacheDirectory + @"/temp_eboot.bin");
+ long FileLength = NewFileInfo.Length;
+ if (FileLength > 512320L)
+ {
+ FileLength = 512320L;
+ }
+
+ byte[] TempEBOOTByteArray = ReadUMDData(CacheDirectory + @"/temp_eboot.bin", 0L, (int)FileLength);
+ var OffsetValue = RuntimeHelpers.GetObjectValue(FindOffset(GameCacheDirectory + @"/data/USER_L0.IMG", TempEBOOTByteArray));
+ byte[] DecTempEBOOTByteArray = ReadUMDData(CacheDirectory + @"/temp_eboot.bin.dec", 0L, (int)NewFileInfo.Length);
+
+ WriteData(GameCacheDirectory + @"/data/USER_L0.IMG", Convert.ToInt64(OffsetValue), BitConverter.ToString(DecTempEBOOTByteArray));
+ }
+
+ // Remove temp files
+ if (File.Exists(CacheDirectory + @"/temp_eboot.bin"))
+ {
+ File.Delete(CacheDirectory + @"/temp_eboot.bin");
+ }
+ if (File.Exists(CacheDirectory + @"/temp_eboot.bin.dec"))
+ {
+ File.Delete(CacheDirectory + @"/temp_eboot.bin.dec");
+ }
+
+ // Copy the selected icon and background to the cache directory
+ if (!Directory.Exists(GameCacheDirectory + @"/sce_sys"))
+ {
+ Directory.CreateDirectory(GameCacheDirectory + @"/sce_sys");
+ }
+ if (!string.IsNullOrEmpty(SelectedPSPIconTextBox.Text))
+ {
+ var IconBytes = File.ReadAllBytes(SelectedPSPIconTextBox.Text);
+ var ConvertedIcon = Utils.ConvertTo24bppPNG(IconBytes, 512, 512);
+ ConvertedIcon.Save(GameCacheDirectory + @"/sce_sys/icon0.png");
+
+ //File.Copy(SelectedPSPIconTextBox.Text, GameCacheDirectory + @"/sce_sys/icon0.png", true);
+ }
+ if (!string.IsNullOrEmpty(SelectedPSPBGImageTextBox.Text))
+ {
+ var BGBytes = File.ReadAllBytes(SelectedPSPBGImageTextBox.Text);
+ var ConvertedBG = Utils.ConvertTo24bppPNG(BGBytes, 1920, 1080);
+ ConvertedBG.Save(GameCacheDirectory + @"/sce_sys/pic0.png");
+
+ //File.Copy(SelectedPSPBGImageTextBox.Text, GameCacheDirectory + @"/sce_sys/pic0.png", true);
+ }
+
+ // PSP Emulator configuration
+ string EmulatorConfig = string.Concat("--ps4-trophies=0\r\n--ps5-uds=0\r\n--trophies=0\r\n--image=\"data/USER_L0.IMG\"\r\n--antialias=SSAA4x\r\n--multisaves=true\r\n--notrophies=true\r\n\r\n");
+
+ if (File.Exists(GameCacheDirectory + "/config-title.txt"))
+ {
+ File.Delete(GameCacheDirectory + "/config-title.txt");
+ }
+ File.WriteAllText(GameCacheDirectory + "/config-title.txt", EmulatorConfig);
+ if (!string.IsNullOrEmpty(SelectedPSPConfigTextBox.Text))
+ {
+ File.AppendAllText(GameCacheDirectory + "/config-title.txt", File.ReadAllText(SelectedPSPConfigTextBox.Text));
+ }
+
+ // Create a new PARAM.SFO file
+ var NewPS4ParamSFO = new ParamSfo();
+ NewPS4ParamSFO.SetValue("APP_TYPE", SfoEntryType.Integer, "1", 4);
+ NewPS4ParamSFO.SetValue("APP_VER", SfoEntryType.Utf8, "01.00", 8);
+ NewPS4ParamSFO.SetValue("ATTRIBUTE", SfoEntryType.Integer, "0", 4);
+ NewPS4ParamSFO.SetValue("CATEGORY", SfoEntryType.Utf8, "gd", 4);
+ NewPS4ParamSFO.SetValue("CONTENT_ID", SfoEntryType.Utf8, "UP9000-" + PSPNPTitleTextBox.Text + "_00-" + PSPNPTitleTextBox.Text + "PSPFPKG", 48);
+ NewPS4ParamSFO.SetValue("DOWNLOAD_DATA_SIZE", SfoEntryType.Integer, "0", 4);
+ NewPS4ParamSFO.SetValue("FORMAT", SfoEntryType.Utf8, "obs", 4);
+ NewPS4ParamSFO.SetValue("PARENTAL_LEVEL", SfoEntryType.Integer, "5", 4);
+ NewPS4ParamSFO.SetValue("SYSTEM_VER", SfoEntryType.Integer, "0", 4);
+ NewPS4ParamSFO.SetValue("TITLE", SfoEntryType.Utf8, PSPTitleTextBox.Text, 128);
+ NewPS4ParamSFO.SetValue("TITLE_ID", SfoEntryType.Utf8, PSPNPTitleTextBox.Text, 12);
+ NewPS4ParamSFO.SetValue("VERSION", SfoEntryType.Utf8, "01.00", 8);
+
+ File.WriteAllBytes(GameCacheDirectory + @"/sce_sys/param.sfo", NewPS4ParamSFO.Serialize());
+
+ // Generate a GP4 project
+ string GenGP4CCMD = "wine \"c:\\PS4\\gengp4_patch.exe\" \"c:\\Cache\\PSPfPKG\"";
+ var EscapedArgs2 = GenGP4CCMD.Replace("\"", "\\\"");
+ var NewProcess = new Process();
+ NewProcess.StartInfo.FileName = "/bin/bash";
+ NewProcess.StartInfo.Arguments = $"-c \"{EscapedArgs2}\"";
+ NewProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ NewProcess.StartInfo.CreateNoWindow = true;
+ NewProcess.Start();
+ NewProcess.WaitForExit();
+
+ // Modify the GP4 project
+ File.WriteAllText(CacheDirectory + "/PSPfPKG.gp4", File.ReadAllText(CacheDirectory + "/PSPfPKG.gp4").Replace("", ""));
+
+ var DebugMessageBox = MessageBoxManager.GetMessageBoxStandard("PS fPKG Classics Builder", "All files ready! \r\n fPKG can be build.", ButtonEnum.Ok);
+ await DebugMessageBox.ShowWindowDialogAsync(window);
+
+ // Create the fPKG
+ string PUBCMD = "wine \"c:\\PS4\\orbis-pub-cmd-3.38.exe\" img_create --oformat pkg --skip_digest --no_progress_bar \"c:\\Cache\\PSPfPKG.gp4\" \"c:\\fPKG\"";
+ EscapedArgs2 = PUBCMD.Replace("\"", "\\\"");
+ string PKGBuilderProcessOutput;
+ var PKGBuilderProcess = new Process();
+ PKGBuilderProcess.StartInfo.FileName = "/bin/bash";
+ PKGBuilderProcess.StartInfo.Arguments = $"-c \"{EscapedArgs2}\"";
+ PKGBuilderProcess.StartInfo.UseShellExecute = false;
+ PKGBuilderProcess.StartInfo.RedirectStandardOutput = true;
+ PKGBuilderProcess.StartInfo.CreateNoWindow = true;
+ PKGBuilderProcess.Start();
+ PKGBuilderProcess.WaitForExit();
+
+ // Read the process output
+ using (var NewStreamReader = PKGBuilderProcess.StandardOutput)
+ {
+ PKGBuilderProcessOutput = NewStreamReader.ReadToEnd();
+ }
+ if (PKGBuilderProcessOutput.Contains("Create image Process finished with warning"))
+ {
+ string PKGFileName = "UP9000-" + PSPNPTitleTextBox.Text + "_00-" + PSPNPTitleTextBox.Text + "PSPFPKG-A0100-V0100.pkg";
+ string PKGFilePath = wineDriveC + "/fPKG/" + PKGFileName;
+ if (File.Exists(PKGFilePath))
+ {
+ try
+ {
+ File.Move(PKGFilePath, PKGOutputFolder + "/" + PKGFileName);
+ }
+ catch (Exception error)
+ {
+ Console.WriteLine(error);
+ }
+ }
+
+ var PKGBuildMessageBox = MessageBoxManager.GetMessageBoxStandard("DEBUG", "fPKG created with success!", ButtonEnum.Ok);
+ await PKGBuildMessageBox.ShowWindowDialogAsync(window);
+ }
+ else
+ {
+ DebugMessageBox = MessageBoxManager.GetMessageBoxStandard("DEBUG", "Error creating fPKG", ButtonEnum.Ok);
+ await DebugMessageBox.ShowWindowDialogAsync(window);
+ }
+ }
+
+ }
+
+ public static bool FileExistInISO(string GameISOPath, string FileToSearch)
+ {
+ bool Exists = false;
+ try
+ {
+ using var NewFileStream = new FileStream(GameISOPath, FileMode.Open, FileAccess.Read);
+ var NewIso9660CDReader = new DiscUtils.Iso9660.CDReader(NewFileStream, true);
+ try
+ {
+ NewIso9660CDReader.OpenFile(FileToSearch, FileMode.Open);
+ Exists = true;
+ }
+ catch (Exception)
+ {
+ Exists = false;
+ }
+ }
+ catch (Exception)
+ {
+ Exists = false;
+ }
+ return Exists;
+ }
+
+ public static string ExtractFileFromPSPISO(string path, string fileName, string DestinationPath)
+ {
+ string OutputDestination = "";
+ string DesinationDirectoryName = Path.GetDirectoryName(DestinationPath) ?? "";
+ if (!string.IsNullOrEmpty(DestinationPath) && !Directory.Exists(DesinationDirectoryName))
+ {
+ Directory.CreateDirectory(DesinationDirectoryName);
+ }
+ try
+ {
+ using var NewFileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
+ var NewIso9660CDReader = new DiscUtils.Iso9660.CDReader(NewFileStream, true);
+ try
+ {
+ var NewSparseStream = NewIso9660CDReader.OpenFile(fileName, FileMode.Open);
+ var OutputFileStream = new FileStream(DestinationPath, FileMode.Create);
+ NewSparseStream.CopyTo(OutputFileStream);
+ OutputFileStream.Close();
+ OutputDestination = DestinationPath;
+ }
+ catch (Exception)
+ {
+ OutputDestination = "";
+ }
+ }
+ catch (Exception)
+ {
+ OutputDestination = "";
+ }
+ return OutputDestination;
+ }
+
+ public static byte[] ReadUMDData(string DataFile, long Offset, int Lenght)
+ {
+ var NewByte = new byte[(Lenght - 1 + 1)];
+ using (var NewBinaryReader = new BinaryReader(File.Open(DataFile, FileMode.Open)))
+ {
+ long BaseStreamLenght = NewBinaryReader.BaseStream.Length;
+ int Num = 0;
+ NewBinaryReader.BaseStream.Seek(Offset, SeekOrigin.Begin);
+ while (Offset < BaseStreamLenght & Num < Lenght)
+ {
+ NewByte[Num] = NewBinaryReader.ReadByte();
+ Offset += 1L;
+ Num += 1;
+ }
+ }
+ return NewByte;
+ }
+
+ public static object FindOffset(string FileName, byte[] Query)
+ {
+ object ReturnLenght;
+ using (var NewBinaryReader = new BinaryReader(File.Open(FileName, FileMode.Open)))
+ {
+ double BaseStreamLength = NewBinaryReader.BaseStream.Length;
+ if (Query.Length <= BaseStreamLength)
+ {
+
+ byte[] NewByteArray = NewBinaryReader.ReadBytes(Query.Length);
+ bool Flag = false;
+ int NewQueryLenght = Query.Length - 1;
+ int WhileInt = 0;
+
+ while (WhileInt <= NewQueryLenght)
+ {
+ if (NewByteArray[WhileInt] == Query[WhileInt])
+ {
+ Flag = true;
+ WhileInt += 1;
+ }
+ else
+ {
+ Flag = false;
+ break;
+ }
+ }
+
+ if (!Flag)
+ {
+ double NewBaseStreamLength = BaseStreamLength - 1d;
+ double QueryLenght = Query.Length;
+ while (QueryLenght <= NewBaseStreamLength)
+ {
+ Array.Copy(NewByteArray, 1, NewByteArray, 0, NewByteArray.Length - 1);
+ NewByteArray[^1] = NewBinaryReader.ReadByte();
+ int length3 = Query.Length - 1;
+ int num3 = 0;
+ while (num3 <= length3)
+ {
+ if (NewByteArray[num3] == Query[num3])
+ {
+ Flag = true;
+ num3 += 1;
+ }
+ else
+ {
+ Flag = false;
+ break;
+ }
+ }
+ if (!Flag)
+ {
+ QueryLenght += 1d;
+ }
+ else
+ {
+ ReturnLenght = QueryLenght - (Query.Length - 1);
+ return ReturnLenght;
+ }
+ }
+ }
+ else
+ {
+ ReturnLenght = 0;
+ return ReturnLenght;
+ }
+ }
+ }
+ ReturnLenght = -1;
+ return ReturnLenght;
+ }
+
+ public static void WriteData(string FileToWrite, long Offset, string DataToWrite)
+ {
+ var NewFileStream = new FileStream(FileToWrite, FileMode.Open, FileAccess.Write, FileShare.Write);
+ string[] NewStringArray = DataToWrite.Split(['-']);
+ NewFileStream.Seek(Offset, SeekOrigin.Begin);
+ int DoInt = 0;
+ do
+ {
+ string str = NewStringArray[DoInt];
+ NewFileStream.WriteByte(Convert.ToByte(Convert.ToInt32(str, 16)));
+ DoInt += 1;
+ }
+ while (DoInt < NewStringArray.Length);
+ NewFileStream.Close();
+ }
+
+ #endregion
+
+ public async Task CheckWinePrefixAndSetup()
+ {
+ // Check if a wine prefix exists
+ if (!Directory.Exists(wineDriveC))
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var WineNotInstalledMessage = MessageBoxManager.GetMessageBoxStandard("Wine installation not complete", "A wine prefix will be created, please close the Wine Configuration Tool when the initialization finished.", ButtonEnum.Ok);
+ await WineNotInstalledMessage.ShowWindowDialogAsync(window);
+
+ // Check if winetricks is updated if previously installed
+ using var BashProcess = new Process();
+ var WineCFG = "winecfg";
+ BashProcess.StartInfo.FileName = "/bin/bash";
+ BashProcess.StartInfo.Arguments = $"-c \"{WineCFG}\"";
+ BashProcess.StartInfo.RedirectStandardOutput = true;
+ BashProcess.StartInfo.RedirectStandardError = true;
+ BashProcess.StartInfo.UseShellExecute = false;
+ BashProcess.StartInfo.CreateNoWindow = false;
+ BashProcess.Start();
+ BashProcess.WaitForExit();
+ }
+
+ // Check if wine prefix is 64bit
+ if (!Directory.Exists(wineDriveC + "/windows/syswow64"))
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var Wine64NotInstalledMessage = MessageBoxManager.GetMessageBoxStandard("Wine prefix mismatch", "Current wine prefix is 32bit only, please change to 64bit mode before continuing.", ButtonEnum.Ok);
+ await Wine64NotInstalledMessage.ShowWindowDialogAsync(window);
+ return;
+ }
+
+ // Check if PS4 tools exist in current wine prefix
+ if (!Directory.Exists(wineDriveC + "/PS4"))
+ {
+ // Copy PS4 tools to the wine C:\ drive
+ CopyFilesRecursively(Directory.GetCurrentDirectory() + @"/Tools/PS4", PS4ToolsPath);
+ }
+
+ // Create fPKG output folder if not exists in current wine prefix
+ if (!Directory.Exists(wineDriveC + "/fPKG"))
+ {
+ Directory.CreateDirectory(wineDriveC + "/fPKG");
+ }
+
+ // Check if winetricks is updated if previously installed
+ using var Bash = new Process();
+ var WinetricksCMD = $"winetricks -V";
+ Bash.StartInfo.FileName = "/bin/bash";
+ Bash.StartInfo.Arguments = $"-c \"{WinetricksCMD}\"";
+ Bash.StartInfo.RedirectStandardOutput = true;
+ Bash.StartInfo.RedirectStandardError = true;
+ Bash.StartInfo.UseShellExecute = false;
+ Bash.StartInfo.CreateNoWindow = false;
+ Bash.Start();
+ Bash.WaitForExit();
+
+ var UpdateRequired = false;
+ var OutputReader = Bash.StandardOutput;
+ string[] ProcessOutput = OutputReader.ReadToEnd().Split(["\r\n"], StringSplitOptions.RemoveEmptyEntries);
+
+ if (ProcessOutput.Length > 0)
+ {
+ foreach (var OutputLine in ProcessOutput)
+ {
+ if (OutputLine.Contains("sha256sum:"))
+ {
+ var currentWinetricksVersion = OutputLine.Split(["-"], StringSplitOptions.None)[0].Trim();
+ var buildYear = currentWinetricksVersion.Remove(4, 4);
+ Console.WriteLine(currentWinetricksVersion);
+ Console.WriteLine(buildYear);
+
+ if (int.TryParse(buildYear, out int x))
+ {
+ if (x < 2024)
+ {
+ UpdateRequired = true;
+ Console.WriteLine("Update required");
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ if (UpdateRequired == true)
+ {
+ if (VisualRoot is not Window window)
+ return;
+
+ var WineNotInstalledMessage = MessageBoxManager.GetMessageBoxStandard("Winetricks Update Required", "Winetricks will be updated, please close the terminal window when the installation finished.", ButtonEnum.Ok);
+ await WineNotInstalledMessage.ShowWindowDialogAsync(window);
+
+ // Update winetricks
+ string WinetricksUpdateCMD = "gnome-terminal sudo winetricks --self-update";
+ var WinetricksProcess = new Process();
+ WinetricksProcess.StartInfo.FileName = "/bin/bash";
+ WinetricksProcess.StartInfo.Arguments = $"-c \"{WinetricksUpdateCMD}\"";
+ WinetricksProcess.StartInfo.UseShellExecute = false;
+ WinetricksProcess.StartInfo.CreateNoWindow = false;
+ WinetricksProcess.Start();
+ WinetricksProcess.WaitForExit();
+ }
+
+ // Setup required vcrun2008 in current wine prefix (skips if already installed)
+ string WinetricksInstallCMD = "winetricks vcrun2008";
+ var Winetricks = new Process();
+ Winetricks.StartInfo.FileName = "/bin/bash";
+ Winetricks.StartInfo.Arguments = $"-c \"{WinetricksInstallCMD}\"";
+ Winetricks.StartInfo.UseShellExecute = false;
+ Winetricks.StartInfo.RedirectStandardOutput = true;
+ Winetricks.StartInfo.CreateNoWindow = true;
+ Winetricks.Start();
+ Winetricks.WaitForExit();
+ }
+
+ private static void CopyFilesRecursively(string sourcePath, string targetPath)
+ {
+ foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
+ {
+ Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath));
+ }
+ foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories))
+ {
+ File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true);
+ }
+ }
+
+ #region RegexStuff
+
+ [GeneratedRegex("[^a-zA-Z0-9]")]
+ private static partial Regex CRCRegex();
+
+ [GeneratedRegex(@"\((.*?)\)")]
+ private static partial Regex PSPTitleRegex1();
+
+ [GeneratedRegex(" {2,}")]
+ private static partial Regex PSPTitleRegex2();
+
+ #endregion
+ }
+
+
+#pragma warning restore CS0618 // Type or member is obsolete
+
+}
\ No newline at end of file
diff --git a/Linux/makePSClassicsfPKG/PS1Game.cs b/Linux/makePSClassicsfPKG/PS1Game.cs
new file mode 100644
index 0000000..bfecbf8
--- /dev/null
+++ b/Linux/makePSClassicsfPKG/PS1Game.cs
@@ -0,0 +1,48 @@
+using System.IO;
+
+namespace makePSClassicsfPKG
+{
+ public class PS1Game
+ {
+
+ public static string GetPS1GameTitleFromDatabaseList(string GameID)
+ {
+ string FoundGameTitle = "";
+
+ foreach (string GameTitle in File.ReadLines(Directory.GetCurrentDirectory() + @"/Tools/ps1ids.txt"))
+ {
+ if (GameTitle.Contains(GameID))
+ {
+ FoundGameTitle = GameTitle.Split(';')[1];
+ break;
+ }
+ }
+
+ if (string.IsNullOrEmpty(FoundGameTitle))
+ {
+ return "";
+ }
+ else
+ {
+ return FoundGameTitle;
+ }
+ }
+
+ public static string IsGameProtected(string GameID)
+ {
+ string FoundValue = "";
+
+ foreach (string GameIDInFile in File.ReadLines(Directory.GetCurrentDirectory() + @"/Tools/libcrypt.txt"))
+ {
+ if (GameIDInFile.Contains(GameID))
+ {
+ FoundValue = GameIDInFile.Split(' ')[1];
+ break;
+ }
+ }
+
+ return FoundValue;
+ }
+
+ }
+}
diff --git a/Linux/makePSClassicsfPKG/PS2Game.cs b/Linux/makePSClassicsfPKG/PS2Game.cs
new file mode 100644
index 0000000..19c203c
--- /dev/null
+++ b/Linux/makePSClassicsfPKG/PS2Game.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace makePSClassicsfPKG
+{
+
+ public class PS2Game
+ {
+
+ public static string GetPS2GameID(string GameISO)
+ {
+ string GameID = "";
+ using var Bash = new Process();
+
+ var StringsCMD = $"7z l -ba \"{GameISO}\"";
+ var EscapedArgs = StringsCMD.Replace("\"", "\\\"");
+
+ Bash.StartInfo.FileName = "/bin/bash";
+ Bash.StartInfo.Arguments = $"-c \"{EscapedArgs}\"";
+ Bash.StartInfo.RedirectStandardOutput = true;
+ Bash.StartInfo.RedirectStandardError = true;
+ Bash.StartInfo.UseShellExecute = false;
+ Bash.StartInfo.CreateNoWindow = false;
+ Bash.Start();
+ Bash.WaitForExit();
+
+ var OutputReader = Bash.StandardOutput;
+ string[] ProcessOutput = OutputReader.ReadToEnd().Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
+
+ if (ProcessOutput.Length > 0)
+ {
+ foreach (string Line in ProcessOutput)
+ {
+ if (Line.Contains("SLES_") | Line.Contains("SLUS_") | Line.Contains("SCES_") | Line.Contains("SCUS_"))
+ {
+ if (Line.Contains("Volume:")) // ID found in the ISO Header
+ {
+ if (Line.Split(new string[] { "Volume: " }, StringSplitOptions.RemoveEmptyEntries).Length > 0)
+ {
+ GameID = Line.Split(new string[] { "Volume: " }, StringSplitOptions.RemoveEmptyEntries)[1];
+ break;
+ }
+ }
+ else if (string.Join(" ", Line.Split(new char[] { }, StringSplitOptions.RemoveEmptyEntries)).Split(' ').Length > 4) // ID found in the ISO files
+ {
+ GameID = string.Join(" ", Line.Split(new char[] { }, StringSplitOptions.RemoveEmptyEntries)).Split(' ')[5].Trim();
+ break;
+ }
+ }
+ }
+ }
+
+ return GameID;
+ }
+
+ public static string GetPS2GameTitleFromDatabaseList(string GameID)
+ {
+ string FoundGameTitle = "";
+ GameID = GameID.Replace("-", "");
+
+ foreach (string GameTitle in File.ReadLines(Directory.GetCurrentDirectory() + @"/Tools/ps2ids.txt"))
+ {
+ if (GameTitle.Contains(GameID))
+ {
+ FoundGameTitle = GameTitle.Split(';')[1];
+ break;
+ }
+ }
+
+ if (string.IsNullOrEmpty(FoundGameTitle))
+ {
+ return "Unknown PS2 game";
+ }
+ else
+ {
+ return FoundGameTitle;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Linux/makePSClassicsfPKG/Program.cs b/Linux/makePSClassicsfPKG/Program.cs
new file mode 100644
index 0000000..5970139
--- /dev/null
+++ b/Linux/makePSClassicsfPKG/Program.cs
@@ -0,0 +1,22 @@
+using Avalonia;
+using System;
+
+namespace makePSClassicsfPKG
+{
+ internal class Program
+ {
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args) => BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .WithInterFont()
+ .LogToTrace();
+ }
+}
diff --git a/Linux/makePSClassicsfPKG/Properties/launchSettings.json b/Linux/makePSClassicsfPKG/Properties/launchSettings.json
new file mode 100644
index 0000000..2440531
--- /dev/null
+++ b/Linux/makePSClassicsfPKG/Properties/launchSettings.json
@@ -0,0 +1,16 @@
+{
+ "profiles": {
+ "WSL (Default)": {
+ "commandName": "WSL2",
+ "distributionName": ""
+ },
+ "makePSClassicsfPKG - Ubuntu": {
+ "commandName": "WSL2",
+ "distributionName": "Ubuntu"
+ },
+ "makePSClassicsfPKG - Debian": {
+ "commandName": "WSL2",
+ "distributionName": "Debian"
+ }
+ }
+}
\ No newline at end of file
diff --git a/Linux/makePSClassicsfPKG/Utils.cs b/Linux/makePSClassicsfPKG/Utils.cs
new file mode 100644
index 0000000..c76053b
--- /dev/null
+++ b/Linux/makePSClassicsfPKG/Utils.cs
@@ -0,0 +1,20 @@
+using Avalonia;
+using Avalonia.Media.Imaging;
+using System.Runtime.InteropServices;
+
+namespace makePSClassicsfPKG
+{
+ public class Utils
+ {
+ public static Bitmap ConvertTo24bppPNG(byte[] rgbPixelData, int width, int height)
+ {
+ Vector dpi = new(96, 96);
+ var NewBitmap = new WriteableBitmap(new PixelSize(width, height), dpi, Avalonia.Platform.PixelFormats.Rgb24);
+ using (var frameBuffer = NewBitmap.Lock())
+ {
+ Marshal.Copy(rgbPixelData, 0, frameBuffer.Address, rgbPixelData.Length);
+ }
+ return NewBitmap;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Linux/makePSClassicsfPKG/app.manifest b/Linux/makePSClassicsfPKG/app.manifest
new file mode 100644
index 0000000..3900960
--- /dev/null
+++ b/Linux/makePSClassicsfPKG/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Linux/makePSClassicsfPKG/makePSClassicsfPKG.csproj b/Linux/makePSClassicsfPKG/makePSClassicsfPKG.csproj
new file mode 100644
index 0000000..1aa1fa6
--- /dev/null
+++ b/Linux/makePSClassicsfPKG/makePSClassicsfPKG.csproj
@@ -0,0 +1,1301 @@
+
+
+ WinExe
+ net8.0
+ enable
+ true
+ app.manifest
+ true
+
+
+ True
+ ps128.png
+ git
+ psicon.ico
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\..\..\Desktop\Classics\PS2\LibOrbisPkg.dll
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ \
+
+
+
diff --git a/Linux/makePSClassicsfPKG/psicon.ico b/Linux/makePSClassicsfPKG/psicon.ico
new file mode 100644
index 0000000..5de5be1
Binary files /dev/null and b/Linux/makePSClassicsfPKG/psicon.ico differ