diff --git a/ChovySign-CLI/ChovySign-CLI.csproj.user b/ChovySign-CLI/ChovySign-CLI.csproj.user index ea87748..dea5502 100644 --- a/ChovySign-CLI/ChovySign-CLI.csproj.user +++ b/ChovySign-CLI/ChovySign-CLI.csproj.user @@ -1,6 +1,6 @@  - <_LastSelectedProfileId>C:\Users\Li\Documents\git\Chovy-Sign-v2\ChovySign-CLI\Properties\PublishProfiles\FolderProfile.pubxml + <_LastSelectedProfileId>C:\Users\Li\Documents\git\Chovy-Sign-v2\ChovySign-CLI\Properties\PublishProfiles\Win64.pubxml \ No newline at end of file diff --git a/ChovySign-CLI/Program.cs b/ChovySign-CLI/Program.cs index c77fa1c..23e5bf6 100644 --- a/ChovySign-CLI/Program.cs +++ b/ChovySign-CLI/Program.cs @@ -4,6 +4,8 @@ using GameBuilder.Psp; using LibChovy; using LibChovy.VersionKey; using System.Text; +using Vita.ContentManager; +using PspCrypto; namespace ChovySign_CLI { @@ -11,19 +13,29 @@ namespace ChovySign_CLI { private static ArgumentParsingMode mode = ArgumentParsingMode.ARG; private static List parameters = new List(); - private static string[] discs; + private static string[] discs = new string[] { }; private static bool pspCompress = false; - private static bool devKit = false; private static string? popsDiscName; - private static string? popsIcon0File; - private static string? popsPic0File; + private static byte[]? popsIcon0File; + private static byte[]? popsPic0File; private static PbpMode? pbpMode = null; private static NpDrmRif? rifFile = null; private static NpDrmInfo? drmInfo = null; + // cma + private static bool devKit = false; + private static bool packagePsvImg = true; + private static string? outputFolder = null; + + // --vkey-gen private static byte[]? actDat = null; private static byte[]? idps = null; private static string? rifFolder = null; + + // --pops-eboot-sign + private static byte[]? ebootElf = null; + private static byte[]? configBin = null; + enum PbpMode { PSP = 0, @@ -33,15 +45,23 @@ namespace ChovySign_CLI } enum ArgumentParsingMode { - ARG = 0, - POPS_DISC = 1, - PSP_UMD = 2, - VERSIONKEY = 3, - VERSIONKEY_EXTRACT = 4, - VERSIONKEY_GENERATOR = 5, - POPS_INFO = 6, - KEYS_TXT_GEN = 7, - RIF = 8 + ARG, + POPS_DISC, + PSP_UMD, + + VERSIONKEY, + VERSIONKEY_EXTRACT, + VERSIONKEY_GENERATOR, + + CMA_DEVKIT, + CMA_OUTPUT_FOLDER, + CMA_PACKAGE_PSVIMG, + + POPS_INFO, + POPS_EBOOT, + + KEYS_TXT_GEN, + RIF } public static int Error(string errorMsg, int ret) { @@ -102,13 +122,13 @@ namespace ChovySign_CLI switch (mode) { case ArgumentParsingMode.POPS_DISC: - if (parameters.Count > 5) return Error("--pops: no more than 5 disc images allowed in a single game (sony's rules, not mine)", 5); - if (parameters.Count < 1) return Error("--pops: at least 1 disc image file is required.", 5); + if (parameters.Count > 5) return Error("--pops: no more than 5 disc images allowed in a single game (sony's rules, not mine)", 4); + if (parameters.Count < 1) return Error("--pops: at least 1 disc image file is required.", 4); discs = parameters.ToArray(); break; case ArgumentParsingMode.PSP_UMD: - if (parameters.Count < 1) return Error("--psp: a path to a disc image is required", 5); - if (parameters.Count > 2) return Error("--psp: no more than 2 arguments. ("+parameters.Count+" given)", 5); + if (parameters.Count < 1) return Error("--psp: a path to a disc image is required", 4); + if (parameters.Count > 2) return Error("--psp: no more than 2 arguments. ("+parameters.Count+" given)", 4); discs = new string[1]; discs[0] = parameters[0]; @@ -134,10 +154,10 @@ namespace ChovySign_CLI if (parameters.Count < 2) return Error("--pops-info takes at least 1 arguments ("+parameters.Count+" given)", 4); if (parameters.Count > 3) return Error("--pops-info takes no more than 3 arguments("+parameters.Count+" given)", 4); popsDiscName = parameters[0]; - if (parameters.Count > 1) - popsIcon0File = parameters[1]; - if (parameters.Count > 2) - popsPic0File = parameters[2]; + if (parameters.Count > 1 && File.Exists(parameters[1])) + popsIcon0File = File.ReadAllBytes(parameters[1]); + if (parameters.Count > 2 && File.Exists(parameters[2])) + popsPic0File = File.ReadAllBytes(parameters[2]); break; case ArgumentParsingMode.KEYS_TXT_GEN: if (parameters.Count != 3) return Error("--keys-txt-gen takes 3 arguments, (" + parameters.Count + " given)", 4); @@ -145,6 +165,23 @@ namespace ChovySign_CLI idps = StringToByteArray(parameters[1]); rifFolder = parameters[2]; break; + case ArgumentParsingMode.POPS_EBOOT: + if (parameters.Count < 1) return Error("--pops-eboot-sign expects at most 1 arguments", 4); + if (!File.Exists(parameters[0])) return Error("--pops-eboot-sign: file not found", 4); + ebootElf = File.ReadAllBytes(parameters[0]); + + if (parameters.Count >= 2 && File.Exists(parameters[1])) + configBin = File.ReadAllBytes(parameters[1]); + else + configBin = GameBuilder.Resources.DATAPSPSDCFG; + + break; + case ArgumentParsingMode.CMA_OUTPUT_FOLDER: + if (parameters.Count < 1) return Error("--output-folder expects 1 output", 4); + if (!Directory.Exists(parameters[0])) return Error("--output-folder: directory not found", 4); + + SettingsReader.BackupsFolder = parameters[0]; + break; case ArgumentParsingMode.RIF: if (parameters.Count != 1) return Error("--rif expects only 1 argument,", 4); rifFile = new NpDrmRif(File.ReadAllBytes(parameters[0])); @@ -163,12 +200,20 @@ namespace ChovySign_CLI Console.WriteLine("Chovy-Sign v2 (CLI)"); Console.WriteLine("--pops [disc1.cue] [disc2.cue] [disc3.cue] ... (up to 5)"); Console.WriteLine("--pops-info [game title] [icon0.png] (optional) [pic1.png] (optional)"); + Console.WriteLine("--pops-eboot [eboot.elf] [config.bin] (optional)"); + Console.WriteLine("--psp [umd.iso] [compress; true/false] (optional)"); + Console.WriteLine("--rif [GAME.RIF]"); + Console.WriteLine("--devkit (Use 000000000000 account id)"); + Console.WriteLine("--no-psvimg (Disable creating a .psvimg file)"); + Console.WriteLine("--output-folder [output_folder]"); + Console.WriteLine("--vkey [versionkey] [contentid] [key_index]"); Console.WriteLine("--vkey-extract [eboot.pbp]"); Console.WriteLine("--vkey-gen [act.dat] [license.rif] [console_id] [key_index]"); + Console.WriteLine("--keys-txt-gen [act.dat] [console_id] [psp_license_folder]"); } @@ -236,6 +281,17 @@ namespace ChovySign_CLI return Error("rif is already set", 3); break; + case "--pops-eboot": + mode = ArgumentParsingMode.POPS_EBOOT; + break; + case "--output-folder": + mode = ArgumentParsingMode.CMA_OUTPUT_FOLDER; + break; + + case "--no-psvimg": + packagePsvImg = false; + break; + case "--devkit": devKit = true; break; @@ -248,6 +304,7 @@ namespace ChovySign_CLI case ArgumentParsingMode.VERSIONKEY_EXTRACT: case ArgumentParsingMode.PSP_UMD: case ArgumentParsingMode.POPS_DISC: + case ArgumentParsingMode.POPS_EBOOT: case ArgumentParsingMode.POPS_INFO: case ArgumentParsingMode.RIF: default: @@ -267,14 +324,14 @@ namespace ChovySign_CLI if (pbpMode is null) return Error("no pbp mode was set, exiting", 7); if (pbpMode == PbpMode.PSP && drmInfo.KeyIndex != 2) - return Error("KeyType is "+drmInfo.KeyIndex+", but PBP mode is PSP, you cant do that .. please use a type 1 versionkey.", 8); + return Error("KeyType is "+drmInfo.KeyIndex+", but PBP mode is PSP, you cant do that .. please use a type 2 versionkey.", 8); if (pbpMode == PbpMode.POPS && drmInfo.KeyIndex != 1) return Error("KeyType is " + drmInfo.KeyIndex + ", but PBP mode is POPS, you cant do that .. please use a type 1 versionkey.", 8); if (rifFile is null) return Error("Rif is not set, use --rif to specify base game RIF", 8); - //if (pbpMode == PbpMode.POPS && (popsDiscName is null || popsIcon0File is null)) return Error("pbp mode is POPS, but you have not specified a disc title or icon file using --pops-info.", 9); + ChovySign csign = new ChovySign(); csign.RegisterCallback(onProgress); if (pbpMode == PbpMode.POPS) @@ -287,11 +344,17 @@ namespace ChovySign_CLI if(popsDiscName is not null) popsParameters.Name = popsDiscName; - if(File.Exists(popsIcon0File)) - popsParameters.Icon0 = File.ReadAllBytes(popsIcon0File); + if(popsIcon0File is not null) + popsParameters.Icon0 = popsIcon0File; + + popsParameters.CreatePsvImg = packagePsvImg; popsParameters.Account.Devkit = devKit; + // Allow for custom eboot.elf and configs + popsParameters.ConfigBinOverride = configBin; + popsParameters.EbootElfOverride = ebootElf; + csign.Go(popsParameters); } @@ -299,6 +362,8 @@ namespace ChovySign_CLI { PspParameters pspParameters = new PspParameters(drmInfo, rifFile); pspParameters.Account.Devkit = devKit; + pspParameters.CreatePsvImg = packagePsvImg; + pspParameters.Compress = pspCompress; pspParameters.Umd = new UmdInfo(discs.First()); diff --git a/ChovySign-CLI/Properties/PublishProfiles/FolderProfile.pubxml.user b/ChovySign-CLI/Properties/PublishProfiles/FolderProfile.pubxml.user deleted file mode 100644 index b3e5516..0000000 --- a/ChovySign-CLI/Properties/PublishProfiles/FolderProfile.pubxml.user +++ /dev/null @@ -1,10 +0,0 @@ - - - - - True|2023-05-01T17:56:05.9738806Z;True|2023-04-26T15:20:05.7988009+12:00;True|2023-04-24T08:18:55.4774877+12:00;True|2023-04-20T08:33:00.3404616+12:00;False|2023-04-20T08:29:02.1306599+12:00;True|2023-04-19T21:53:45.1116925+12:00;True|2023-04-19T20:46:20.2756012+12:00;True|2023-04-19T19:58:40.3825010+12:00;True|2023-04-18T00:00:51.4131559+12:00;True|2023-04-17T09:56:35.5065135+12:00;True|2023-04-17T09:22:54.8607008+12:00;True|2023-04-17T08:27:16.5281469+12:00;True|2023-04-17T08:22:02.0531219+12:00; - - - \ No newline at end of file diff --git a/ChovySign-CLI/Properties/PublishProfiles/Linux64.pubxml b/ChovySign-CLI/Properties/PublishProfiles/Linux64.pubxml new file mode 100644 index 0000000..862ba73 --- /dev/null +++ b/ChovySign-CLI/Properties/PublishProfiles/Linux64.pubxml @@ -0,0 +1,19 @@ + + + + + Release + Any CPU + bin\Release\Linux + FileSystem + <_TargetId>Folder + net6.0 + linux-x64 + true + true + true + true + + \ No newline at end of file diff --git a/ChovySign-CLI/Properties/PublishProfiles/FolderProfile.pubxml b/ChovySign-CLI/Properties/PublishProfiles/Win64.pubxml similarity index 90% rename from ChovySign-CLI/Properties/PublishProfiles/FolderProfile.pubxml rename to ChovySign-CLI/Properties/PublishProfiles/Win64.pubxml index d1d5c25..fc063c7 100644 --- a/ChovySign-CLI/Properties/PublishProfiles/FolderProfile.pubxml +++ b/ChovySign-CLI/Properties/PublishProfiles/Win64.pubxml @@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. Release Any CPU - bin\Release\ + bin\Release\Windows FileSystem <_TargetId>Folder net6.0 diff --git a/ChovySign-GUI/Global/ProgressStatus.axaml.cs b/ChovySign-GUI/Global/ProgressStatus.axaml.cs index bc175a2..2633203 100644 --- a/ChovySign-GUI/Global/ProgressStatus.axaml.cs +++ b/ChovySign-GUI/Global/ProgressStatus.axaml.cs @@ -42,12 +42,21 @@ namespace ChovySign_GUI.Global if (currentWindow is not Window) throw new Exception("could not find current window"); this.goButton.IsEnabled = false; - + OnBeforeStart(new EventArgs()); if(Parameters is null) { await MessageBox.Show(currentWindow, "ChovySignParameters was null, cannot start!", "Invalid Parameters", MessageBoxButtons.Ok); return; } - await Task.Run(() => { chovySign.Go(Parameters); }); + await Task.Run(() => { + try + { + chovySign.Go(Parameters); + } + catch (Exception e) + { + Dispatcher.UIThread.Post(() => { _ = MessageBox.Show(currentWindow, "Error building: " + e.Message + "\n\nSTACKTRACE: " + e.StackTrace, "ERROR", MessageBoxButtons.Ok); }); + } + }); OnFinished(new EventArgs()); diff --git a/ChovySign-GUI/MainWindow.axaml.cs b/ChovySign-GUI/MainWindow.axaml.cs index c5dcc0d..c8cc7b5 100644 --- a/ChovySign-GUI/MainWindow.axaml.cs +++ b/ChovySign-GUI/MainWindow.axaml.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Controls; namespace ChovySign_GUI diff --git a/ChovySign-GUI/Ps1/GameInfoSelector.axaml.cs b/ChovySign-GUI/Ps1/GameInfoSelector.axaml.cs index 9ad59c2..a1cf6c0 100644 --- a/ChovySign-GUI/Ps1/GameInfoSelector.axaml.cs +++ b/ChovySign-GUI/Ps1/GameInfoSelector.axaml.cs @@ -103,7 +103,12 @@ namespace ChovySign_GUI.Ps1 loadIcon(newCover); iconCache = newCover; } - catch (Exception) { } + catch (Exception e) { + Window? currentWindow = this.VisualRoot as Window; + if (currentWindow is not Window) throw new Exception("could not find current window"); + + await MessageBox.Show(currentWindow, "unable to read cue sheet: " + Path.GetFileName(cueFile) + "\n" + e.Message + "\n\nSTACKTRACE: " + e.StackTrace, "cannot load cue sheet", MessageBox.MessageBoxButtons.Ok); + } } private async Task doLoad(BrowseButton imgFile, int width, int height) diff --git a/ChovySign-GUI/Psp/PspTab.axaml.cs b/ChovySign-GUI/Psp/PspTab.axaml.cs index 530a393..3a6c9cf 100644 --- a/ChovySign-GUI/Psp/PspTab.axaml.cs +++ b/ChovySign-GUI/Psp/PspTab.axaml.cs @@ -38,7 +38,7 @@ namespace ChovySign_GUI.Psp { keySelector.IsEnabled = true; isoSelector.IsEnabled = true; - SettingsTab.Settings.IsEnabled = false; + SettingsTab.Settings.IsEnabled = true; Window? currentWindow = this.VisualRoot as Window; if (currentWindow is not Window) throw new Exception("could not find current window"); diff --git a/GameBuilder/Atrac3/Atrac3ToolEncoder.cs b/GameBuilder/Atrac3/Atrac3ToolEncoder.cs index 421def3..e37d250 100644 --- a/GameBuilder/Atrac3/Atrac3ToolEncoder.cs +++ b/GameBuilder/Atrac3/Atrac3ToolEncoder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Channels; using System.Threading.Tasks; @@ -12,6 +13,9 @@ namespace GameBuilder.Atrac3 { public class Atrac3ToolEncoder : IAtracEncoderBase { + [DllImport("libc")] + private static extern int setenv(string name, string value, bool overwrite); + private static Random rng = new Random(); private static string TOOLS_DIRECTORY = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tools"); @@ -19,6 +23,7 @@ namespace GameBuilder.Atrac3 private static string AT3TOOL_LINUX = Path.Combine(TOOLS_DIRECTORY, "at3tool.elf"); private static string TEMP_DIRECTORY = Path.Combine(Path.GetTempPath(), "at3tool_tmp"); + private static string LD_LIBRARY_PATH = "LD_LIBRARY_PATH"; // random name so that can generate multiple at once if wanted .. private string TEMP_WAV; @@ -45,10 +50,24 @@ namespace GameBuilder.Atrac3 } } + private string setupLibaryPath() + { + string? libaryPath = Environment.GetEnvironmentVariable(LD_LIBRARY_PATH); + if (libaryPath is null) libaryPath = TOOLS_DIRECTORY; + else libaryPath += ";" + TOOLS_DIRECTORY; + + Environment.SetEnvironmentVariable(libaryPath, libaryPath); + setenv(LD_LIBRARY_PATH, libaryPath, true); + + return libaryPath; + } private void runAtrac3Tool() { using(Process proc = new Process()) { + if (OperatingSystem.IsLinux()) + proc.StartInfo.Environment.Add(LD_LIBRARY_PATH, setupLibaryPath()); + proc.StartInfo.FileName = AT3TOOL_LOCATION; proc.StartInfo.Arguments = "-br 132 -e \"" + TEMP_WAV + "\" \"" + TEMP_AT3 + "\""; diff --git a/GameBuilder/Cue/CueReader.cs b/GameBuilder/Cue/CueReader.cs index f94e41f..6f5e1cf 100644 --- a/GameBuilder/Cue/CueReader.cs +++ b/GameBuilder/Cue/CueReader.cs @@ -300,6 +300,19 @@ namespace GameBuilder.Cue openTracks.Clear(); } + private string getFilename(string str) + { + if (!str.Contains(' ')) throw new Exception("cue specifies no bin file."); + if (!str.Contains('"')) return str.Split(' ')[1]; + + int start = str.IndexOf('"'); + str = str.Substring(start + 1); + + + int end = str.IndexOf('"'); + str = str.Substring(0, end); + return str; + } public CueReader(string cueFile) { openTracks = new Dictionary(); @@ -310,10 +323,11 @@ namespace GameBuilder.Cue CueTrack? curTrack = null; for (string? cueData = cueReader.ReadLine(); - cueData != null; + cueData is not null; cueData = cueReader.ReadLine()) { - string[] cueLn = cueData.Trim().Replace("\r", "").Replace("\n", "").Split(' '); + cueData = cueData.Trim().Replace("\r", "").Replace("\n", ""); + string[] cueLn = cueData.Split(' '); if (cueLn[0] == "INDEX") { @@ -350,17 +364,17 @@ namespace GameBuilder.Cue if (curTrack != null) setTrackNumber(curTrack.TrackNo, ref curTrack); // parse out filename.. - string[] cueFnameParts = new string[cueLn.Length - 2]; - Array.ConstrainedCopy(cueLn, 1, cueFnameParts, 0, cueFnameParts.Length); - string cueFname = String.Join(' ', cueFnameParts); - - // open file .. - string binFileName = cueFname.Substring(1, cueFname.Length - 2); + string binFileName = getFilename(cueData); string? folderContainingCue = Path.GetDirectoryName(cueFile); if (folderContainingCue != null) binFileName = Path.Combine(folderContainingCue, binFileName); + if(!File.Exists(binFileName)) + binFileName = Path.ChangeExtension(cueFile, ".bin"); + + if (!File.Exists(binFileName)) throw new FileNotFoundException("unable to find bin file."); + curTrack = new CueTrack(binFileName); } } diff --git a/GameBuilder/Pops/PopsImg.cs b/GameBuilder/Pops/PopsImg.cs index 806338c..de30472 100644 --- a/GameBuilder/Pops/PopsImg.cs +++ b/GameBuilder/Pops/PopsImg.cs @@ -24,6 +24,9 @@ namespace GameBuilder.Pops this.createSimpleDat(); this.SimplePgd = CreatePgd(simple.ToArray()); + this.EbootElf = Resources.DATAPSPSD; + this.ConfigBin = Resources.DATAPSPSDCFG; + this.PatchEboot = true; } internal void createSimpleDat() { @@ -54,28 +57,42 @@ namespace GameBuilder.Pops public override byte[] GenerateDataPsp() { Span loaderEnc = new byte[0x9B13]; + + //byte[] dataPspElf = Resources.DATAPSPSD; - byte[] dataPspElf = Resources.DATAPSPSD; + if (this.PatchEboot) + { + // calculate size low and high part .. + uint szLow = Convert.ToUInt32(Psar.Length) >> 16; + uint szHigh = Convert.ToUInt32(Psar.Length) & 0xFFFF; - // calculate size low and high part .. - uint szLow = Convert.ToUInt32(Psar.Length) >> 16; - uint szHigh = Convert.ToUInt32(Psar.Length) & 0xFFFF; + // convert to big endain bytes + byte[] lowBits = BitConverter.GetBytes(Convert.ToUInt16(szLow)).ToArray(); + byte[] highBits = BitConverter.GetBytes(Convert.ToUInt16(szHigh)).ToArray(); - // convert to big endain bytes - byte[] lowBits = BitConverter.GetBytes(Convert.ToUInt16(szLow)).ToArray(); - byte[] highBits = BitConverter.GetBytes(Convert.ToUInt16(szHigh)).ToArray(); + // overwrite data.psar size check .. + Array.ConstrainedCopy(lowBits, 0, this.EbootElf, 0x68C, 0x2); + Array.ConstrainedCopy(highBits, 0, this.EbootElf, 0x694, 0x2); + } - // overwrite data.psar size check .. - Array.ConstrainedCopy(lowBits, 0, dataPspElf, 0x68C, 0x2); - Array.ConstrainedCopy(highBits, 0, dataPspElf, 0x694, 0x2); - - SceMesgLed.Encrypt(loaderEnc, dataPspElf, 0x0DAA06F0, SceExecFileDecryptMode.DECRYPT_MODE_POPS_EXEC, DrmInfo.VersionKey, DrmInfo.ContentId, Resources.DATAPSPSDCFG); + SceMesgLed.Encrypt( + loaderEnc, + this.EbootElf, + 0x0DAA06F0, + SceExecFileDecryptMode.DECRYPT_MODE_POPS_EXEC, + DrmInfo.VersionKey, + DrmInfo.ContentId, + this.ConfigBin); return loaderEnc.ToArray(); } private MemoryStream simple; private StreamUtil simpleUtil; + public byte[] EbootElf; + public byte[] ConfigBin; + public bool PatchEboot; + public byte[] StartDat; public byte[] SimplePgd; diff --git a/GameBuilder/Psp/NpUmdImg.cs b/GameBuilder/Psp/NpUmdImg.cs index 725866f..1d78315 100644 --- a/GameBuilder/Psp/NpUmdImg.cs +++ b/GameBuilder/Psp/NpUmdImg.cs @@ -43,8 +43,8 @@ namespace GameBuilder.Psp public void PatchSfo() { Sfo sfoKeys = Sfo.ReadSfo(umdImage.DataFiles["PARAM.SFO"]); - if ((sfoKeys["CATEGORY"] as String) == "UG") // "UMD Game" - sfoKeys["CATEGORY"] = "EG"; // set it to "Eboot Game" + //if ((sfoKeys["CATEGORY"] as String) == "UG") // "UMD Game" + sfoKeys["CATEGORY"] = "EG"; // set it to "Eboot Game" umdImage.DataFiles["PARAM.SFO"] = sfoKeys.WriteSfo(); } private void createNpHdr() diff --git a/GameBuilder/Psp/UmdInfo.cs b/GameBuilder/Psp/UmdInfo.cs index 2962b0b..fe79227 100644 --- a/GameBuilder/Psp/UmdInfo.cs +++ b/GameBuilder/Psp/UmdInfo.cs @@ -12,12 +12,12 @@ namespace GameBuilder.Psp { private string[] filesList = new string[] { - "PSP_GAME\\ICON0.PNG", - "PSP_GAME\\ICON1.PMF", - "PSP_GAME\\PARAM.SFO", - "PSP_GAME\\PIC0.PNG", - "PSP_GAME\\PIC1.PNG", - "PSP_GAME\\SND0.AT3" + Path.Combine("PSP_GAME","ICON0.PNG"), + Path.Combine("PSP_GAME","ICON1.PMF"), + Path.Combine("PSP_GAME","PARAM.SFO"), + Path.Combine("PSP_GAME","PIC0.PNG"), + Path.Combine("PSP_GAME","PIC1.PNG"), + Path.Combine("PSP_GAME","SND0.AT3") }; public Dictionary DataFiles = new Dictionary(); @@ -27,12 +27,12 @@ namespace GameBuilder.Psp this.IsoStream = File.OpenRead(isoFile); using (CDReader cdReader = new CDReader(this.IsoStream, true, true)) { - foreach (string file in filesList) + foreach (string file in this.filesList) { string fname = Path.GetFileName(file).ToUpperInvariant(); - if (cdReader.FileExists(file)) + if (cdReader.FileExists(file.Replace('/', '\\'))) { - using (SparseStream s = cdReader.OpenFile(file, FileMode.Open)) + using (SparseStream s = cdReader.OpenFile(file.Replace('/', '\\'), FileMode.Open)) { byte[] data = new byte[s.Length]; @@ -48,11 +48,14 @@ namespace GameBuilder.Psp } } - - if (DataFiles["PARAM.SFO"] is null) throw new Exception("ISO contains no PARAM.SFO file, so this is not a valid PSP game."); + byte[]? paramFile = DataFiles["PARAM.SFO"]; + if (paramFile is null) throw new Exception("ISO contains no PARAM.SFO file, so this is not a valid PSP game."); - Sfo sfo = Sfo.ReadSfo(DataFiles["PARAM.SFO"]); - this.DiscId = sfo["DISC_ID"] as String; + Sfo sfo = Sfo.ReadSfo(paramFile); + string? discId = sfo["DISC_ID"] as String; + if (discId is null) throw new Exception("PARAM.SFO does not contain \"DISC_ID\""); + + this.DiscId = discId; // check minis if (sfo["ATTRIBUTE"] is UInt32) diff --git a/LibChovy/Art/Downloader.cs b/LibChovy/Art/Downloader.cs index 2102fe1..4f60d1c 100644 --- a/LibChovy/Art/Downloader.cs +++ b/LibChovy/Art/Downloader.cs @@ -30,7 +30,7 @@ namespace LibChovy.Art using (Image psnBorder = Image.Load(Resources.ICON0)) { - coverImage.Mutate(x => x.Crop(new Rectangle(80, 0, coverImage.Width - 80, coverImage.Height))); + //coverImage.Mutate(x => x.Crop(new Rectangle(80, 0, coverImage.Width - 80, coverImage.Height))); coverImage.Mutate(x => x.Resize(58, 58)); psnBorder.Mutate(x => x.DrawImage(coverImage, new Point(13, 11), 1.0f)); diff --git a/LibChovy/ChovySign.cs b/LibChovy/ChovySign.cs index 8ee2526..2e09249 100644 --- a/LibChovy/ChovySign.cs +++ b/LibChovy/ChovySign.cs @@ -62,6 +62,17 @@ namespace LibChovy else img = new PsIsoImg(parameters.DrmInfo, parameters.FirstDisc); + // apply eboot elf overrides + if(parameters.EbootElfOverride is not null) + { + img.EbootElf = parameters.EbootElfOverride; + img.PatchEboot = false; + } + if (parameters.ConfigBinOverride is not null) + { + img.ConfigBin = parameters.ConfigBinOverride; + } + using (PbpBuilder pbpBuilder = new PbpBuilder(sfo, parameters.Icon0, null, parameters.Pic0, parameters.Pic1, null, img, 0)) { pbpBuilder.RegisterCallback(onProgress); diff --git a/LibChovy/PopsParameters.cs b/LibChovy/PopsParameters.cs index 7fb4fe7..da09211 100644 --- a/LibChovy/PopsParameters.cs +++ b/LibChovy/PopsParameters.cs @@ -19,6 +19,9 @@ namespace LibChovy Type = ChovyTypes.POPS; discList = new List(); + EbootElfOverride = null; + ConfigBinOverride = null; + discIdOverride = null; nameOverride = null; libCryptMethod = LibCryptMethod.METHOD_MAGIC_WORD; @@ -32,6 +35,10 @@ namespace LibChovy private byte[]? pic1; private byte[]? icon0; + + public byte[]? EbootElfOverride; + public byte[]? ConfigBinOverride; + public PSInfo FirstDisc { get diff --git a/Vita/ContentManager/SettingsReader.cs b/Vita/ContentManager/SettingsReader.cs index 7742f2c..f0490df 100644 --- a/Vita/ContentManager/SettingsReader.cs +++ b/Vita/ContentManager/SettingsReader.cs @@ -75,7 +75,50 @@ namespace Vita.ContentManager private static string getDefaultCmaPSVitaFolder() { - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "PS Vita"); + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "PS Vita"); + } + + private static string getQcmaConfFile() + { + if (OperatingSystem.IsLinux()) + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "codestation", "qcma.conf"); + else if (OperatingSystem.IsMacOS()) + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Preferences", "com.codestation.qcma.plist"); + else + throw new PlatformNotSupportedException("cannot open qcma config as i dont know where it is."); + } + private static string? getQcmaConfigSetting(string file, string key) + { + if (!File.Exists(file)) return null; + + if (OperatingSystem.IsLinux()) + { + using (TextReader confFile = File.OpenText(file)) + { + for (string? ln = confFile.ReadLine(); + ln is not null; + ln = confFile.ReadLine()) + { + ln = ln.Trim(); + if (ln.StartsWith("[")) continue; + + string[] kvp = ln.Split('='); + if (kvp.Length < 2) continue; + + string settingKey = kvp[0].Trim(); + string settingValue = kvp[1].Trim(); + + + if (settingKey == key) + return settingValue; + } + } + } + else if (OperatingSystem.IsMacOS()) + { + throw new PlatformNotSupportedException("TODO: Implement reading bplist file from mac os"); + } + return null; } private static string? getRegistryKey(string registryPath, string keyName) @@ -116,15 +159,12 @@ namespace Vita.ContentManager { if (OperatingSystem.IsWindows()) { - using(RegistryKey? qcmaKey = Registry.CurrentUser.OpenSubKey(@"Software\codestation\qcma")) - { - return getRegistryKey(@"Software\codestation\qcma", "appsPath"); - } + return getRegistryKey(@"Software\codestation\qcma", "appsPath"); } else if (OperatingSystem.IsLinux()) { - string qcmaConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "codestation", "qcma.conf"); - // TODO: read file + string qcmaConf = getQcmaConfFile(); + return getQcmaConfigSetting(qcmaConf, "appsPath"); } else if (OperatingSystem.IsMacOS()) {