Compare commits

...

16 Commits

Author SHA1 Message Date
Li 2ef6e0c65d add new setting n stuff 2024-04-12 16:50:25 +12:00
Li 58b6de3d00 Get it working on .net 8.0 and latest avalonia! 2024-04-12 14:47:18 +12:00
Li dd3431c312 Fix bugs 2024-01-11 21:29:23 +13:00
Li 97c5959caf Fix an issue where yuo cant enable using content manager again 2024-01-11 19:52:35 +13:00
Li fb8c379751 Update 'README.md' 2023-06-10 09:03:03 +00:00
Li b433e30fcc Update 'README.md' 2023-06-10 09:02:24 +00:00
Li 1c1711d844 Update 'README.md' 2023-06-10 08:59:50 +00:00
Li 2b1154a140 Update 'README.md' 2023-06-10 08:58:53 +00:00
Li fec8c58ae5 Update 'README.md' 2023-06-10 08:53:35 +00:00
Li dad692f9dd Update 'README.md' 2023-06-10 08:48:52 +00:00
Li 434c3304b7 Update info2 2023-06-10 20:22:52 +12:00
Li 1779f2f7c2 Update readme again 2023-06-10 20:22:06 +12:00
Li c837fddb9f Update readme, fix some stuff 2023-06-10 20:20:11 +12:00
Li f087076653 Put new discoveries into practice 2023-06-10 19:20:27 +12:00
Li c15e345b30 make Chovy-Sign-CLI and GUI version run on linux 2023-06-09 21:06:44 +12:00
Li 5aa34528ea Update slight 2023-05-02 06:15:49 +12:00
62 changed files with 1003 additions and 383 deletions

View File

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

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_LastSelectedProfileId>C:\Users\Li\Documents\git\Chovy-Sign-v2\ChovySign-CLI\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
<_LastSelectedProfileId>C:\Users\Li\Desktop\git\chovy-sign\ChovySign-CLI\Properties\PublishProfiles\Linux64.pubxml</_LastSelectedProfileId>
</PropertyGroup>
</Project>

View File

@ -4,6 +4,9 @@ using GameBuilder.Psp;
using LibChovy;
using LibChovy.VersionKey;
using System.Text;
using Vita.ContentManager;
using PspCrypto;
using System.ComponentModel;
namespace ChovySign_CLI
{
@ -11,19 +14,29 @@ namespace ChovySign_CLI
{
private static ArgumentParsingMode mode = ArgumentParsingMode.ARG;
private static List<string> parameters = new List<string>();
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 +46,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)
{
@ -85,6 +106,7 @@ namespace ChovySign_CLI
if(rif.AccountId != accountId) { Error(rif.ContentId + " account id does not match: " + accountId.ToString("X") + " (was " + rif.AccountId.ToString("X") + ")", 10); continue; }
string[] keys = new string[4];
for (int i = 0; i < keys.Length; i++)
keys[i] = BitConverter.ToString(ActRifMethod.GetVersionKey(actDat, rif.Rif, idps, i).VersionKey).Replace("-", "");
@ -92,7 +114,7 @@ namespace ChovySign_CLI
string keysTxt = String.Join(' ', keysTxtLine);
addKeys.AppendLine(keysTxt);
Console.WriteLine(keysTxt);
//Console.WriteLine(keysTxt);
}
File.AppendAllText("KEYS.TXT", addKeys.ToString());
}
@ -102,13 +124,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];
@ -123,8 +145,8 @@ namespace ChovySign_CLI
drmInfo = new NpDrmInfo(StringToByteArray(parameters[0]), parameters[1], int.Parse(parameters[2]));
break;
case ArgumentParsingMode.VERSIONKEY_EXTRACT:
if (parameters.Count != 1) return Error("--vkey-extract: expect 1 arguments. ("+parameters.Count+" given)", 4);
drmInfo = EbootPbpMethod.GetVersionKey(File.OpenRead(parameters[0]));
if (parameters.Count != 2) return Error("--vkey-extract: expect 2 arguments. ("+parameters.Count+" given)", 4);
drmInfo = EbootPbpMethod.GetVersionKey(File.OpenRead(parameters[0]), int.Parse(parameters[1]));
break;
case ArgumentParsingMode.VERSIONKEY_GENERATOR:
if(parameters.Count != 4) return Error("--vkey-gen: expect 4 arguments. ("+parameters.Count+" given)", 4);
@ -134,10 +156,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 +167,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]));
@ -155,20 +194,77 @@ namespace ChovySign_CLI
parameters.Clear();
return 0;
}
/*
public static void generateRif(byte[] idps, byte[] actBuf, byte[] versionKey, int versionKeyType, ulong accountId, string contentId)
{
byte[] vkey2 = new byte[versionKey.Length];
Array.Copy(versionKey, vkey2, versionKey.Length);
byte[] rkey = Rng.RandomBytes(0x10);
int keyId = 0x10; // (Int32)(Rng.RandomUInt() % 0x80);
Array.ConstrainedCopy(BitConverter.GetBytes(keyId).Reverse().ToArray(), 0, rkey, 0xC, 0x4);
byte[] encKey1 = new byte[0x10];
AesHelper.AesEncrypt(rkey, encKey1, KeyVault.drmRifKey);
// get the act key
byte[] actKey = new byte[0x10];
SceNpDrm.SetPSID(idps);
SceNpDrm.Aid = accountId;
Act act = MemoryMarshal.AsRef<Act>(actBuf);
GetActKey(actKey, act.PrimKeyTable[(keyId * 0x10)..], 1);
// reverse version key back to main version key
sceNpDrmTransformVersionKey(vkey2, versionKeyType, 0);
byte[] encKey2 = new byte[0x10];
AesHelper.AesEncrypt(vkey2, encKey2, actKey);
using (MemoryStream rifStream = new MemoryStream())
{
StreamUtil rifUtil = new StreamUtil(rifStream);
rifUtil.WriteInt16(0x0);
rifUtil.WriteInt16(0x1);
rifUtil.WriteInt32(0x2);
rifUtil.WriteUInt64(accountId);
rifUtil.WriteStrWithPadding(contentId, 0x00, 0x30);
rifUtil.WriteBytes(encKey1); // enckey1
rifUtil.WriteBytes(encKey2); // enckey2
rifUtil.WriteUInt64(SceRtc.ksceRtcGetCurrentSecureTick());
rifUtil.WriteUInt64(0x00); // expiry
rifUtil.WritePadding(0xFF, 0x28);
}
}
*/
public static int Main(string[] args)
{
if (args.Length == 0)
{
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-extract [eboot.pbp] [key_index]");
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 +332,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 +355,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:
@ -261,20 +369,17 @@ namespace ChovySign_CLI
generateKeysTxt();
if (drmInfo is null) return Error("no versionkey was found, exiting", 6);
//Console.WriteLine("Version Key: " + BitConverter.ToString(drmInfo.VersionKey).Replace("-", "") + ", " + drmInfo.KeyIndex);
if (pbpMode is null) return Error("no pbp mode was set, exiting", 7);
int targetKeyIndex = (pbpMode == PbpMode.PSP) ? 2 : 1;
if (drmInfo.KeyIndex != targetKeyIndex)
{
SceNpDrm.sceNpDrmTransformVersionKey(drmInfo.VersionKey, drmInfo.KeyIndex, 2);
drmInfo.KeyIndex = targetKeyIndex;
}
if (rifFile is null) return Error("Rif is not set, use --rif to specify base game RIF", 8);
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);
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 +392,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 +410,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());

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<History>True|2023-04-26T03:20:05.7988009Z;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;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\Linux</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>true</PublishReadyToRun>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
</Project>

View File

@ -6,10 +6,10 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\</PublishDir>
<PublishDir>bin\Release\Windows</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>

View File

@ -1,153 +1,123 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ChovySign_GUI.App">
x:Class="ChovySign_GUI.App"
RequestedThemeVariant="Dark">
<Application.Styles>
<FluentTheme Mode="Light"/>
<FluentTheme/>
<!-- Checkbox Styling -->
<Style Selector="CheckBox:checked /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground">
<Setter.Value>LightGreen</Setter.Value>
</Setter>
<!-- checked -->
<Style Selector="CheckBox:checked">
<Setter Property="BorderBrush" Value="LightGreen" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="LightGreen" />
</Style>
<Style Selector="^ /template/ Border#NormalRectangle">
<Setter Property="BorderBrush" Value="LightGreen" />
<Setter Property="Background" Value="#3f3f3f" />
</Style>
</Style>
<Style Selector="CheckBox:checked /template/ Border#NormalRectangle">
<Setter Property="BorderBrush">
<Setter.Value>LightGreen</Setter.Value>
</Setter>
<Setter Property="Background">
<Setter.Value>#3f3f3f</Setter.Value>
</Setter>
<!-- unchecked -->
<Style Selector="CheckBox:unchecked">
<Setter Property="BorderBrush" Value="LightGreen" />
<Setter Property="Foreground" Value="LightGreen" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="LightGreen" />
</Style>
<Style Selector="^ /template/ Border#NormalRectangle">
<Setter Property="BorderBrush" Value="LightGreen" />
<Setter Property="Background" Value="#3f3f3f" />
</Style>
</Style>
<Style Selector="CheckBox:pointerover /template/ Border#NormalRectangle">
<Setter Property="BorderBrush">
<Setter.Value>Green</Setter.Value>
</Setter>
<Setter Property="Background">
<Setter.Value>#1f1f1f</Setter.Value>
</Setter>
</Style>
<!-- pointerover -->
<Style Selector="CheckBox /template/ Border#NormalRectangle">
<Setter Property="BorderBrush">
<Setter.Value>Green</Setter.Value>
</Setter>
<Setter Property="Background">
<Setter.Value>#3f3f3f</Setter.Value>
</Setter>
</Style>
<Style Selector="CheckBox:pointerover">
<Setter Property="BorderBrush" Value="Green" />
<Style Selector="CheckBox:pointerover /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground">
<Setter.Value>Green</Setter.Value>
</Setter>
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="Green" />
</Style>
<Style Selector="^ /template/ Border#NormalRectangle">
<Setter Property="BorderBrush" Value="Green" />
<Setter Property="Background" Value="#1f1f1f" />
</Style>
</Style>
<Style Selector="CheckBox">
<Setter Property="BorderBrush">
<Setter.Value>LightGreen</Setter.Value>
</Setter>
<Setter Property="Foreground">
<Setter.Value>LightGreen</Setter.Value>
</Setter>
</Style>
<!-- Button styles -->
<Style Selector="Button:disabled /template/ ContentPresenter">
<Setter Property="Background">
<Setter.Value>Black</Setter.Value>
</Setter>
<Setter Property="BorderBrush">
<Setter.Value>DarkRed</Setter.Value>
</Setter>
<Setter Property="TextBlock.Foreground">
<Setter.Value>DarkRed</Setter.Value>
</Setter>
<!-- disabled -->
<Style Selector="Button:disabled">
<Style Selector="^:disabled /template/ ContentPresenter">
<Setter Property="Background" Value="Black" />
<Setter Property="BorderBrush" Value="DarkRed" />
<Setter Property="Foreground" Value="DarkRed" />
</Style>
</Style>
<Style Selector="Button:pointerover /template/ ContentPresenter">
<Setter Property="Background">
<Setter.Value>Red</Setter.Value>
</Setter>
<Setter Property="BorderBrush">
<Setter.Value>DarkRed</Setter.Value>
</Setter>
<Setter Property="TextBlock.Foreground">
<Setter.Value>Black</Setter.Value>
</Setter>
<!-- pointerover -->
<Style Selector="Button:pointerover">
<Style Selector="^:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="Red" />
<Setter Property="BorderBrush" Value="DarkRed" />
<Setter Property="Foreground" Value="Black" />
</Style>
</Style>
<!-- regular -->
<Style Selector="Button">
<Setter Property="BorderThickness">
<Setter.Value>1</Setter.Value>
</Setter>
<Setter Property="Foreground">
<Setter.Value>Red</Setter.Value>
</Setter>
<Setter Property="BorderBrush">
<Setter.Value>Red</Setter.Value>
</Setter>
<Setter Property="Background">
<Setter.Value>Black</Setter.Value>
</Setter>
<Setter Property="Background" Value="Black" />
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="Foreground" Value="Red" />
<Setter Property="BorderThickness" Value="1" />
</Style>
<!-- Textbox Styling -->
<Style Selector="TextBox:focus">
<Setter Property="Foreground">
<Setter.Value>LightGreen</Setter.Value>
</Setter>
</Style>
<Setter Property="Foreground" Value="LightGreen" />
<Style Selector="^ /template/ TextBlock">
<Setter Property="Foreground" Value="LightGreen" />
</Style>
<Style Selector="TextBox:focus /template/ TextBlock">
<Setter Property="Foreground">
<Setter.Value>LightGreen</Setter.Value>
</Setter>
</Style>
<Style Selector="TextBox:focus /template/ Border">
<Setter Property="Background">
<Setter.Value>#3f3f3f</Setter.Value>
</Setter>
<Setter Property="BorderBrush">
<Setter.Value>LightGreen</Setter.Value>
</Setter>
<Style Selector="^ /template/ Border">
<Setter Property="Background" Value="#3f3f3f" />
<Setter Property="BorderBrush" Value="LightGreen" />
</Style>
</Style>
<Style Selector="TextBox:pointerover">
<Setter Property="Foreground">
<Setter.Value>Green</Setter.Value>
</Setter>
</Style>
<Setter Property="Foreground" Value="Green" />
<Style Selector="TextBox:pointerover /template/ TextBlock">
<Setter Property="Foreground">
<Setter.Value>Green</Setter.Value>
</Setter>
<Style Selector="^ /template/ TextBlock">
<Setter Property="Foreground" Value="Green" />
</Style>
<Style Selector="^ /template/ Border">
<Setter Property="Background" Value="#1f1f1f" />
<Setter Property="BorderBrush" Value="Green" />
</Style>
</Style>
<Style Selector="TextBox:pointerover /template/ Border">
<Setter Property="Background">
<Setter.Value>#1f1f1f</Setter.Value>
</Setter>
<Setter Property="BorderBrush">
<Setter.Value>Green</Setter.Value>
</Setter>
</Style>
<Style Selector="TextBox">
<Setter Property="Foreground">
<Setter.Value>LightGreen</Setter.Value>
</Setter>
<Setter Property="Background">
<Setter.Value>#3f3f3f</Setter.Value>
</Setter>
<Setter Property="BorderBrush">
<Setter.Value>LightGreen</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="LightGreen" />
<Setter Property="Background" Value="#3f3f3f" />
<Setter Property="BorderBrush" Value="LightGreen" />
</Style>
<!-- ComboBox -->

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
@ -16,8 +16,6 @@
<None Remove="Icon.png" />
<None Remove="Popup\Global\KeySelector\ACTRIFMETHOD.PNG" />
<None Remove="Popup\Global\KeySelector\EBOOTMETHOD.PNG" />
<None Remove="Popup\Global\KeySelector\EBOOTMETHOD1.png" />
<None Remove="Popup\Global\KeySelector\EBOOTMETHOD2.png" />
<None Remove="Popup\Global\KeySelector\KEYSTXTMETHOD.PNG" />
<None Remove="PS1CD.PNG" />
<None Remove="UMD.png" />
@ -25,8 +23,7 @@
<ItemGroup>
<AvaloniaResource Include="ICON.PNG" />
<AvaloniaResource Include="Popup\Global\KeySelector\ACTRIFMETHOD.PNG" />
<AvaloniaResource Include="Popup\Global\KeySelector\EBOOTMETHOD1.PNG" />
<AvaloniaResource Include="Popup\Global\KeySelector\EBOOTMETHOD2.PNG" />
<AvaloniaResource Include="Popup\Global\KeySelector\EBOOTMETHOD.PNG" />
<AvaloniaResource Include="Popup\Global\KeySelector\KEYSTXTMETHOD.PNG" />
<AvaloniaResource Include="Ps1\PS1CD.PNG" />
<AvaloniaResource Include="Psp\UMD.PNG">
@ -42,11 +39,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.19" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.19" />
<PackageReference Include="Avalonia" Version="11.1.0-beta1" />
<PackageReference Include="Avalonia.Desktop" Version="11.1.0-beta1" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.19" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.6.1" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.0-beta1" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.0-beta1" />
</ItemGroup>
<ItemGroup>

View File

@ -176,19 +176,28 @@ namespace ChovySign_GUI.Global
}
}
public BrowseButton()
{
InitializeComponent();
this.filePath.KeyUp += onKeyPress;
this.filePath.PropertyChanged += onPropertyChanged;
this.extension = "";
this.fileTypeName = "All Files";
}
private void onKeyPress(object? sender, KeyEventArgs e)
private void onPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
OnFileChanged(new EventArgs());
TextBox? txt = sender as TextBox;
if (txt is null) return;
if (e.Property.Name == "Text")
{
if (txt.Text is null) return;
if (!ContainsFile) return;
OnFileChanged(new EventArgs());
}
}
}
}

View File

@ -153,19 +153,19 @@ namespace ChovySign_GUI.Global
break;
case VersionKeyMethod.EBOOT_PBP_METHOD:
CmaBackupPicker ebootBackupSelector = new CmaBackupPicker();
ebootBackupSelector.BackupType = ((keyIndex == 1) ? "PSGAME" : "PGAME");
ebootBackupSelector.BackupType = new string[] { "PGAME", "PSGAME" };
string? gameBackupFolder = await ebootBackupSelector.ShowDialog<string>(currentWindow);
string accountId = ebootBackupSelector.AccountId;
if (gameBackupFolder is null) break;
if (accountId == "") break;
key = CMAVersionKeyHelper.GetKeyFromGamePsvimg(gameBackupFolder, accountId);
key = CMAVersionKeyHelper.GetKeyFromGamePsvimg(gameBackupFolder, accountId, this.keyIndex);
rif = CMAVersionKeyHelper.GetRifFromLicensePsvimg(gameBackupFolder, accountId);
break;
case VersionKeyMethod.KEYS_TXT_METHOD:
CmaBackupPicker pspLicenseBackupSelector = new CmaBackupPicker();
pspLicenseBackupSelector.BackupType = "PGAME";
pspLicenseBackupSelector.BackupType = new string[] { "PGAME", "PSGAME" };
pspLicenseBackupSelector.Filter = KeysTxtMethod.TitleIds;
gameBackupFolder = await pspLicenseBackupSelector.ShowDialog<string>(currentWindow);
@ -186,10 +186,8 @@ namespace ChovySign_GUI.Global
if (key is not null)
{
if (key.KeyIndex != this.keyIndex)
{
await MessageBox.Show(currentWindow, "VersionKey obtained, but had keyindex: " + key.KeyIndex + " however keyindex " + this.keyIndex + " was required.", "KeyIndex mismatch!", MessageBoxButtons.Ok);
return;
}
sceNpDrmTransformVersionKey(key.VersionKey, key.KeyIndex, this.keyIndex);
// its a revoolution~
VersionKey = key.VersionKey;
}

View File

@ -54,13 +54,13 @@ namespace ChovySign_GUI.Global
{
get
{
string[]? strings = this.comboBox.Items as string[];
string[]? strings = this.comboBox.ItemsSource as string[];
if (strings is null) return new string[0];
return strings;
}
set
{
this.comboBox.Items = value;
this.comboBox.ItemsSource = value;
}
}

View File

@ -2,6 +2,7 @@ using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Threading;
using ChovySign_GUI.Popup.Global;
using ChovySign_GUI.Settings;
using Li.Progress;
using LibChovy;
using System;
@ -13,6 +14,8 @@ namespace ChovySign_GUI.Global
public partial class ProgressStatus : UserControl
{
public ChovySignParameters? Parameters = null;
public event EventHandler<EventArgs>? Finished;
public event EventHandler<EventArgs>? BeforeStart;
private ChovySign chovySign;
public ProgressStatus()
{
@ -21,14 +24,13 @@ namespace ChovySign_GUI.Global
chovySign = new ChovySign();
chovySign.RegisterCallback(onProgress);
}
public event EventHandler<EventArgs>? BeforeStart;
protected virtual void OnBeforeStart(EventArgs e)
{
if (BeforeStart is not null)
BeforeStart(this, e);
}
public event EventHandler<EventArgs>? Finished;
protected virtual void OnFinished(EventArgs e)
{
if (Finished is not null)
@ -42,15 +44,25 @@ namespace ChovySign_GUI.Global
if (currentWindow is not Window) throw new Exception("could not find current window");
this.goButton.IsEnabled = false;
OnBeforeStart(new EventArgs());
// sanity check it
if(Parameters is null) { await MessageBox.Show(currentWindow, "ChovySignParameters was null, cannot start!", "Invalid Parameters", MessageBoxButtons.Ok); return; }
// apply settings that are global to all signs
if(SettingsTab.Settings is not null) Parameters.BuildStreamType = SettingsTab.Settings.BuildStreamType;
await Task.Run(() => { chovySign.Go(Parameters); });
try
{
await Task.Run(() => {
chovySign.Go(Parameters);
});
}
catch (Exception ex)
{
await MessageBox.Show(currentWindow, "Error building: " + ex.Message + "\n\nSTACKTRACE: " + ex.StackTrace, "ERROR", MessageBoxButtons.Ok);
return;
}
OnFinished(new EventArgs());
this.goButton.IsEnabled = true;
}

View File

@ -1,3 +1,4 @@
using Avalonia;
using Avalonia.Controls;
namespace ChovySign_GUI

View File

@ -3,7 +3,8 @@ using Avalonia.Interactivity;
using ChovySign_GUI.Global;
using GameBuilder.Psp;
using LibChovy.Config;
using Org.BouncyCastle.Utilities.Bzip2;
using Org.BouncyCastle.Asn1;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -16,7 +17,7 @@ namespace ChovySign_GUI.Popup.Global
private const string lookingInLabelText = "Looking in: ";
private string[]? gameDirectories;
private string backupSubFolder = "";
private string[] backupSubFolders;
private string[]? filter;
@ -69,50 +70,80 @@ namespace ChovySign_GUI.Popup.Global
}
}
private string accountIdSearchFolder
private string[] accountIdSearchFolders
{
get
{
string searchIn = Path.Combine(BackupDir, this.BackupType);
string[] searchIn = new string[this.BackupType.Length];
for (int i = 0; i < this.BackupType.Length; i++)
{
searchIn[i] = Path.Combine(BackupDir, this.BackupType[i]);
}
return searchIn;
}
}
private string backupSearchFolder
private string[] backupSearchFolders
{
get
{
if (AccountId == "") return accountIdSearchFolder;
return Path.Combine(accountIdSearchFolder, AccountId);
string[] backupFolders = new string[this.BackupType.Length];
string[] searchFolders = this.accountIdSearchFolders;
for (int i = 0; i < this.BackupType.Length; i++)
{
if (this.AccountId == "") backupFolders[i] = searchFolders[i];
backupFolders[i] = Path.Combine(searchFolders[i], this.AccountId);
}
return backupFolders;
}
}
public string BackupType
public string[] BackupType
{
get
{
return backupSubFolder;
return backupSubFolders;
}
set
{
backupSubFolder = value;
lookingInLbl.Content = lookingInLabelText + backupSubFolder;
backupSubFolders = value;
lookingInLbl.Content = lookingInLabelText + String.Join(", ", backupSubFolders);
reloadAccountIdsList();
reloadBackupsList();
}
}
private string[] GetAllDriectories(string[] dirList)
{
List<string> foundDir = new List<string>();
foreach(string dirSearch in dirList)
{
if (!Directory.Exists(dirSearch)) continue;
foreach(string dir in Directory.GetDirectories(dirSearch))
{
if (!foundDir.Contains(dir))
{
foundDir.Add(dir);
}
}
}
return foundDir.ToArray();
}
private void reloadAccountIdsList()
{
try
{
string[] usedAccountIds = Directory.GetDirectories(accountIdSearchFolder);
string[] usedAccountIds = GetAllDriectories(accountIdSearchFolders);
List<string> accountIdLst = new List<string>();
foreach (string accountId in usedAccountIds)
{
string aid = Path.GetFileName(accountId);
if (accountIdLst.Contains(aid)) continue;
if (aid.Length != 16) continue;
accountIdLst.Add(aid);
}
this.accId.Items = accountIdLst.ToArray();
@ -139,11 +170,11 @@ namespace ChovySign_GUI.Popup.Global
private void reloadBackupsList()
{
this.selectBtn.IsEnabled = false;
this.backupList.Items = new string[0];
this.backupList.ItemsSource = new string[0];
try
{
if(!Directory.Exists(backupSearchFolder)) { return; }
string[] gameBackupDirectories = Directory.GetDirectories(backupSearchFolder);
string[] gameBackupDirectories = GetAllDriectories(backupSearchFolders);
List<string> filteredGameDirectories = new List<string>();
List<string> gameList = new List<string>();
foreach (string gameDirectory in gameBackupDirectories)
@ -171,7 +202,7 @@ namespace ChovySign_GUI.Popup.Global
}
this.gameDirectories = filteredGameDirectories.ToArray();
this.backupList.Items = gameList;
this.backupList.ItemsSource = gameList;
}
catch { }
}
@ -180,7 +211,7 @@ namespace ChovySign_GUI.Popup.Global
{
InitializeComponent();
this.cmaDir.FilePath = BackupDir;
this.backupSubFolder = "APP";
this.backupSubFolders = new string[] { "APP" };
this.accId.SelectionChanged += onAccountSelectionChanged;
this.cmaDir.FileChanged += onCmaDirChanged;
this.backupList.SelectionChanged += onSelectedBackupChanged;
@ -191,6 +222,7 @@ namespace ChovySign_GUI.Popup.Global
private void onSelectedBackupChanged(object? sender, SelectionChangedEventArgs e)
{
ListBox? lstBox = sender as ListBox;
if (lstBox is null) return;
if (lstBox.SelectedIndex == -1) selectBtn.IsEnabled = false;
else selectBtn.IsEnabled = true;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@ -21,8 +21,7 @@
<Button Content="IDPS+RIF+ACT Method" Click="actRifMethodClick" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button Content="KEYS.TXT Method" Click="keysTxtMethodClick" Grid.Row="0" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Image Name="ebootMethodPs1Graphic" Source="/Popup/Global/KeySelector/EBOOTMETHOD1.PNG" Grid.Row="1" Grid.Column="0" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
<Image Name="ebootMethodPspGraphic" Source="/Popup/Global/KeySelector/EBOOTMETHOD2.PNG" Grid.Row="1" Grid.Column="0" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
<Image Name="ebootMethodGraphic" Source="/Popup/Global/KeySelector/EBOOTMETHOD.PNG" Grid.Row="1" Grid.Column="0" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
<Image Name="actRifMethodGraphic" Source="/Popup/Global/KeySelector/ACTRIFMETHOD.PNG" Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
<Image Name="keysTxtMethodGraphic" Source="/Popup/Global/KeySelector/KEYSTXTMETHOD.PNG" Grid.Row="1" Grid.Column="2" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
</Grid>

View File

@ -20,8 +20,6 @@ namespace ChovySign_GUI.Popup.Global.KeySelector
set
{
keyIndex = value;
if (keyIndex == 1) { ebootMethodPspGraphic.IsVisible = false; ebootMethodPs1Graphic.IsVisible = true; }
else { ebootMethodPspGraphic.IsVisible = true; ebootMethodPs1Graphic.IsVisible = false; }
}
}

View File

@ -99,11 +99,19 @@ namespace ChovySign_GUI.Ps1
Title = disc.DiscName;
DiscId = disc.DiscId;
byte[] newCover = await Downloader.DownloadCover(disc);
loadIcon(newCover);
iconCache = newCover;
if (!File.Exists(this.iconFile.FilePath))
{
byte[] newCover = await Downloader.DownloadCover(disc);
loadIcon(newCover);
iconCache = newCover;
}
}
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);
}
catch (Exception) { }
}
private async Task<byte[]?> doLoad(BrowseButton imgFile, int width, int height)

View File

@ -1,11 +1,9 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using ChovySign_GUI.Global;
using ChovySign_GUI.Popup.Global;
using ChovySign_GUI.Settings;
using GameBuilder.Psp;
using LibChovy;
using LibChovy.Config;
using System;
using System.Linq;
using Vita.ContentManager;
@ -70,8 +68,10 @@ namespace ChovySign_GUI.Ps1
// read settings from settings tab.
if (SettingsTab.Settings.DevkitMode) popsParameters.Account = new Account(0);
popsParameters.CrackMethod = SettingsTab.Settings.LibcryptMode;
SettingsReader.BackupsFolder = SettingsTab.Settings.CmaDirectory;
popsParameters.CreatePsvImg = SettingsTab.Settings.PackagePsvimg;
progressStatus.Parameters = popsParameters;

View File

@ -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");
@ -67,8 +67,9 @@ namespace ChovySign_GUI.Psp
pspParameters.Compress = isoSelector.Compress;
// read settings from settings tab.
if (SettingsTab.Settings.DevkitMode) pspParameters.Account = new Account(0);
pspParameters.Account.Devkit = SettingsTab.Settings.DevkitMode;
SettingsReader.BackupsFolder = SettingsTab.Settings.CmaDirectory;
pspParameters.CreatePsvImg = SettingsTab.Settings.PackagePsvimg;
progressStatus.Parameters = pspParameters;
}

View File

@ -3,7 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Global="clr-namespace:ChovySign_GUI.Global"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="80"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="85"
x:Class="ChovySign_GUI.Settings.ConfigDropDown">
<Grid>
<Global:LabeledComboBox Name="configComboBox" Label="Label"/>

View File

@ -13,12 +13,30 @@ namespace ChovySign_GUI.Settings
internal bool disableEvents = false;
private string? promptMsg = null;
private bool defaultSetting = false;
internal override void init()
{
bool? isToggleChecked = ChovyConfig.CurrentConfig.GetBool(ConfigKey);
if (isToggleChecked is null) isToggleChecked = false;
if(isToggleChecked is null) isToggleChecked = defaultSetting;
this.disableEvents = true;
configCheckbox.IsChecked = isToggleChecked;
this.disableEvents = false;
}
public bool Default
{
get
{
return defaultSetting;
}
set
{
defaultSetting = value;
if (ChovyConfig.CurrentConfig.GetBool(ConfigKey) is null)
this.IsToggled = defaultSetting;
}
}
public string Label
{
@ -67,6 +85,9 @@ namespace ChovySign_GUI.Settings
else
IsToggled = false;
}
else{
IsToggled = true;
}
}

View File

@ -16,14 +16,42 @@
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Settings:ConfigToggle Name="devkitAccount" ConfigKey="USE_DEVKIT_ACCOUNT_ID" Label="Devkit Mode (Use 0x0000000000000000 Account ID)"
Prompt="This option will force the CMA Account ID to be all 0's&#x0a;Which is how it is on Devkit, Testkit and IDU Firmware&#x0a;Enabling this if you have a retail firmware will result in the games just *not* showing up&#x0a;&#x0a;If you DON'T know what this means, DON'T enable this.&#x0a;do you want to continue?"
HorizontalAlignment="Stretch" Grid.Row="0"/>
<Settings:ConfigToggle Name="devkitAccount"
ConfigKey="USE_DEVKIT_ACCOUNT_ID"
Label="Devkit Mode (Use 0x0000000000000000 Account ID)"
Prompt="This option will force the CMA Account ID to be all 0's&#x0a;Which is how it is on Devkit, Testkit and IDU Firmware&#x0a;Enabling this if you have a retail firmware will result in the games just *not* showing up&#x0a;&#x0a;If you DON'T know what this means, DON'T enable this.&#x0a;do you want to continue?"
HorizontalAlignment="Stretch"
Default="false"
Grid.Row="0"/>
<Settings:ConfigPath Name="cmaDirectory" ConfigKey="USE_CMA_DIRECTORY" IsDirectory="True" Label="Output Folder:" HorizontalAlignment="Stretch" Grid.Row="1"/>
<Settings:ConfigToggle Name="packagePsvimg"
ConfigKey="USE_CMA_PSVIMG"
Label="Use Content Manager (Package to PSVIMG)"
Default="true"
HorizontalAlignment="Stretch"
Grid.Row="1"/>
<Settings:ConfigDropDown Name="libCryptMode" ConfigKey="USE_LIBCRYPT_METHOD" Label="LibCrypt Method:" HorizontalAlignment="Stretch" Grid.Row="2"/>
<Settings:ConfigPath Name="cmaDirectory"
ConfigKey="USE_CMA_DIRECTORY"
IsDirectory="true"
Label="Output Folder:"
HorizontalAlignment="Stretch"
Grid.Row="2"/>
<Settings:ConfigDropDown Name="libCryptMode"
ConfigKey="USE_LIBCRYPT_METHOD"
Label="LibCrypt Method:"
HorizontalAlignment="Stretch"
Grid.Row="3"/>
<Settings:ConfigDropDown Name="streamType"
ConfigKey="USE_STREAM_TYPE"
Label="Build Stream Type:"
HorizontalAlignment="Stretch"
Grid.Row="4"/>
</Grid>
</Grid>

View File

@ -1,5 +1,6 @@
using Avalonia.Controls;
using GameBuilder.Pops.LibCrypt;
using GameBuilder;
using System.IO;
using Vita.ContentManager;
@ -7,7 +8,15 @@ namespace ChovySign_GUI.Settings
{
public partial class SettingsTab : UserControl
{
public static SettingsTab Settings;
public static SettingsTab? Settings;
public StreamType BuildStreamType
{
get
{
return (StreamType) this.streamType.SelectedIndex;
}
}
public LibCryptMethod LibcryptMode
{
@ -37,6 +46,13 @@ namespace ChovySign_GUI.Settings
return devkitAccount.IsToggled;
}
}
public bool PackagePsvimg
{
get
{
return packagePsvimg.IsToggled;
}
}
public SettingsTab()
@ -44,10 +60,12 @@ namespace ChovySign_GUI.Settings
InitializeComponent();
libCryptMode.Items = new string[2] { "Magic Word in ISO Header", "Sub Channel PGD" };
streamType.Items = new string[2] { "MemoryStream - Create EBOOT in memory, faster, but high memory usage", "FileStream - Create EBOOT with temporary files, slower, but less memory usage" };
if (!Directory.Exists(this.CmaDirectory))
cmaDirectory.Value = SettingsReader.BackupsFolder;
Settings = this;
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -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 + "\"";

133
GameBuilder/BuildStream.cs Normal file
View File

@ -0,0 +1,133 @@
using DiscUtils.Streams;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameBuilder
{
public class BuildStream : Stream
{
public static StreamType BuildUsingStreamType = StreamType.TYPE_MEMORY_STREAM;
private Stream underylingStream;
private StreamType uStreamType;
private string? filename = null;
private void init()
{
this.uStreamType = BuildUsingStreamType;
if (this.uStreamType == StreamType.TYPE_MEMORY_STREAM)
{
this.underylingStream = new MemoryStream();
}
else if (this.uStreamType == StreamType.TYPE_FILE_STREAM)
{
string tmpFolder = Path.Combine(Path.GetTempPath(), "chovysign2");
Directory.CreateDirectory(tmpFolder);
this.filename = Path.Combine(tmpFolder, Guid.NewGuid().ToString());
this.underylingStream = File.Create(this.filename);
}
else
{
throw new Exception("unknown stream type");
}
}
public BuildStream(byte[] data)
{
init();
this.Write(data, 0, data.Length);
this.Seek(0x00, SeekOrigin.Begin);
}
public BuildStream()
{
init();
}
public override bool CanRead {
get {
return underylingStream.CanRead;
}
}
public override bool CanSeek {
get {
return underylingStream.CanSeek;
}
}
public override bool CanWrite {
get {
return underylingStream.CanWrite;
}
}
public override long Length {
get {
return underylingStream.Length;
}
}
public override long Position {
get {
return underylingStream.Position;
}
set {
underylingStream.Position = value;
}
}
public override void Close()
{
this.underylingStream.Close();
if(this.uStreamType == StreamType.TYPE_FILE_STREAM && this.filename is not null)
File.Delete(this.filename);
base.Close();
}
public override void Flush()
{
underylingStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return underylingStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return underylingStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
underylingStream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
underylingStream.Write(buffer, offset, count);
}
public byte[] ToArray()
{
long oldLocation = this.Position;
this.Seek(0x00, SeekOrigin.Begin);
byte[] rdData = new byte[this.Length];
this.Read(rdData, 0x00, rdData.Length);
this.Seek(oldLocation, SeekOrigin.Begin);
return rdData;
}
}
}

View File

@ -269,7 +269,7 @@ namespace GameBuilder.Cue
public byte[] CreateToc()
{
using (MemoryStream toc = new MemoryStream())
using (BuildStream toc = new BuildStream())
{
StreamUtil tocUtil = new StreamUtil(toc);
tocUtil.WriteBytes(createDummyTracks());
@ -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<int, CueStream>();
@ -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);
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -26,8 +26,8 @@ namespace GameBuilder.Pops
this.disc = disc;
this.cue = new CueReader(disc.CueFile);
this.IsoHeader = new MemoryStream();
this.CompressedIso = new MemoryStream();
this.IsoHeader = new BuildStream();
this.CompressedIso = new BuildStream();
this.isoHeaderUtil = new StreamUtil(IsoHeader);
this.atrac3Encoder = encoder;
@ -189,9 +189,9 @@ namespace GameBuilder.Pops
writeCDAEntry(Convert.ToInt32(CompressedIso.Position), atracData.Length, key);
using (MemoryStream atracStream = new MemoryStream(atracData))
using (BuildStream atracStream = new BuildStream(atracData))
{
using (MemoryStream encryptedAtracStream = new MemoryStream())
using (BuildStream encryptedAtracStream = new BuildStream())
{
AtracCrypto.ScrambleAtracData(atracStream, encryptedAtracStream, key);
encryptedAtracStream.Seek(0x00, SeekOrigin.Begin);
@ -241,8 +241,8 @@ namespace GameBuilder.Pops
private CueReader cue;
private PopsImg srcImg;
public MemoryStream IsoHeader;
public MemoryStream CompressedIso;
public BuildStream IsoHeader;
public BuildStream CompressedIso;
private StreamUtil isoHeaderUtil;
private IAtracEncoderBase atrac3Encoder;

View File

@ -12,7 +12,7 @@ namespace GameBuilder.Pops.LibCrypt
{
public static byte[] CreateSubchannelDat(int magicWord)
{
using(MemoryStream subChannels = new MemoryStream())
using(BuildStream subChannels = new BuildStream())
{
StreamUtil subChannelsUtil = new StreamUtil(subChannels);
// this header seems to mark the start of the sub channel data.

View File

@ -17,13 +17,16 @@ namespace GameBuilder.Pops
public PopsImg(NpDrmInfo versionKey) : base(versionKey)
{
simple = new MemoryStream();
simple = new BuildStream();
simpleUtil = new StreamUtil(simple);
this.StartDat = NpDrmPsar.CreateStartDat(Resources.STARTDATPOPS);
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<byte> 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 BuildStream simple;
private StreamUtil simpleUtil;
public byte[] EbootElf;
public byte[] ConfigBin;
public bool PatchEboot;
public byte[] StartDat;
public byte[] SimplePgd;

View File

@ -50,10 +50,10 @@ namespace GameBuilder.Pops
}
isoMap = new MemoryStream();
isoMap = new BuildStream();
isoMapUtil = new StreamUtil(isoMap);
isoPart = new MemoryStream();
isoPart = new BuildStream();
isoPartUtil = new StreamUtil(isoPart);
@ -174,10 +174,10 @@ namespace GameBuilder.Pops
private PSInfo[] discs;
private DiscCompressor[] compressors;
private MemoryStream isoPart;
private BuildStream isoPart;
private StreamUtil isoPartUtil;
private MemoryStream isoMap;
private BuildStream isoMap;
private StreamUtil isoMapUtil;
}
}

View File

@ -17,19 +17,19 @@ namespace GameBuilder.Psp
{
DrmInfo = npDrmInfo;
Psar = new MemoryStream();
Psar = new BuildStream();
psarUtil = new StreamUtil(Psar);
}
public NpDrmInfo DrmInfo;
public MemoryStream Psar;
public BuildStream Psar;
internal StreamUtil psarUtil;
public abstract void CreatePsar();
public abstract byte[] GenerateDataPsp();
public static byte[] CreateStartDat(byte[] image)
{
using(MemoryStream startDatStream = new MemoryStream())
using(BuildStream startDatStream = new BuildStream())
{
StreamUtil startDatUtil = new StreamUtil(startDatStream);

View File

@ -21,16 +21,16 @@ namespace GameBuilder.Psp
{
this.compress = compress;
this.npHdr = new MemoryStream();
this.npHdr = new BuildStream();
this.npHdrUtil = new StreamUtil(npHdr);
this.npHdrBody = new MemoryStream();
this.npHdrBody = new BuildStream();
this.npHdrBodyUtil = new StreamUtil(npHdrBody);
this.npTbl = new MemoryStream();
this.npTbl = new BuildStream();
this.npTblUtil = new StreamUtil(npTbl);
this.isoData = new MemoryStream();
this.isoData = new BuildStream();
this.isoDataUtil = new StreamUtil(isoData);
this.headerKey = Rng.RandomBytes(0x10);
@ -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()
@ -101,7 +101,7 @@ namespace GameBuilder.Psp
public override byte[] GenerateDataPsp()
{
byte[] startDat = CreateStartDat(umdImage.Minis ? Resources.STARTDATMINIS : Resources.STARTDATPSP);
using (MemoryStream dataPsp = new MemoryStream())
using (BuildStream dataPsp = new BuildStream())
{
StreamUtil dataPspUtil = new StreamUtil(dataPsp);
byte[] signature = signParamSfo(umdImage.DataFiles["PARAM.SFO"]);
@ -288,16 +288,16 @@ namespace GameBuilder.Psp
private byte[] dataKey;
private MemoryStream npHdr;
private BuildStream npHdr;
private StreamUtil npHdrUtil;
private MemoryStream npHdrBody;
private BuildStream npHdrBody;
private StreamUtil npHdrBodyUtil;
private MemoryStream isoData;
private BuildStream isoData;
private StreamUtil isoDataUtil;
private MemoryStream npTbl;
private BuildStream npTbl;
private StreamUtil npTblUtil;
}
}

View File

@ -18,7 +18,7 @@ namespace GameBuilder.Psp
private byte[]? snd0At3;
private NpDrmPsar psar;
private short pbpVersion;
private MemoryStream pbpStream;
private BuildStream pbpStream;
public PbpBuilder(byte[] paramSfo, byte[] icon0Png, byte[]? icon1Pmf,
byte[]? pic0Png, byte[]? pic1Png, byte[]? snd0At3,
@ -33,11 +33,11 @@ namespace GameBuilder.Psp
this.psar = dataPsar;
this.pbpVersion = version;
pbpStream = new MemoryStream();
pbpStream = new BuildStream();
psar.RegisterCallback(onProgress);
}
public MemoryStream PbpStream
public BuildStream PbpStream
{
get
{

View File

@ -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<string, byte[]?> DataFiles = new Dictionary<string, byte[]?>();
@ -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)

14
GameBuilder/StreamType.cs Normal file
View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameBuilder
{
public enum StreamType : uint
{
TYPE_MEMORY_STREAM,
TYPE_FILE_STREAM
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -117,6 +117,10 @@ namespace Li.Utilities
{
WriteBytesWithPadding(Encoding.UTF8.GetBytes(str), b, len);
}
public void WriteUInt64(UInt64 v)
{
WriteBytes(BitConverter.GetBytes(v));
}
public void WriteInt64(Int64 v)
{
WriteBytes(BitConverter.GetBytes(v));

View File

@ -1,4 +1,5 @@
using GameBuilder.Pops;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using System;
using System.Collections.Generic;
@ -30,7 +31,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));

View File

@ -1,4 +1,6 @@
using System;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

View File

@ -1,4 +1,5 @@
using GameBuilder.Pops;
using GameBuilder;
using GameBuilder.Pops;
using GameBuilder.Psp;
using Li.Progress;
using PspCrypto;
@ -62,6 +63,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);
@ -138,6 +150,8 @@ namespace LibChovy
SceNpDrm.Aid = parameters.DrmRif.AccountId;
BuildStream.BuildUsingStreamType = parameters.BuildStreamType;
if (!Directory.Exists(parameters.OutputFolder))
Directory.CreateDirectory(parameters.OutputFolder);

View File

@ -1,4 +1,5 @@
using GameBuilder.Psp;
using GameBuilder;
using GameBuilder.Psp;
using LibChovy.Config;
using System;
using System.Collections.Generic;
@ -19,6 +20,7 @@ namespace LibChovy
this.Account = new Account(DrmRif.AccountId);
this.CreatePsvImg = true;
this.FirmwareVersion = 0x3600000;
this.BuildStreamType = StreamType.TYPE_MEMORY_STREAM;
}
public int FirmwareVersion;
@ -27,7 +29,8 @@ namespace LibChovy
public NpDrmRif DrmRif;
public Account Account;
public ChovyTypes Type;
public StreamType BuildStreamType;
protected string? outputFolderOverride;

77
LibChovy/ChovyStream.cs Normal file
View File

@ -0,0 +1,77 @@
using DiscUtils.Streams;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LiLib
{
public class ChovyStream : Stream
{
private Stream underylingStream;
public ChovyStream()
{
underylingStream = new MemoryStream();
}
public override bool CanRead {
get {
return underylingStream.CanRead;
}
}
public override bool CanSeek {
get {
return underylingStream.CanSeek;
}
}
public override bool CanWrite {
get {
return underylingStream.CanWrite;
}
}
public override long Length {
get {
return underylingStream.Length;
}
}
public override long Position {
get {
return underylingStream.Position;
}
set {
underylingStream.Position = value;
}
}
public override void Flush()
{
underylingStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return underylingStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return underylingStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
underylingStream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
underylingStream.Write(buffer, offset, count);
}
}
}

View File

@ -2,13 +2,13 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
</ItemGroup>
<ItemGroup>

View File

@ -19,6 +19,9 @@ namespace LibChovy
Type = ChovyTypes.POPS;
discList = new List<PSInfo>();
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

View File

@ -137,3 +137,17 @@ EP9000-UCES01242_00-PRESELLADDONPCK0 FE1E0A3B419BB9376D084ABEC54A526B DBD40050D8
EP9000-UCES01242_00-PRESELLADDONPCK1 45331A2B5F214CD4E1297227126FAE95 FC1310D9604F5DC4FFF1015299A33524 A46821B0D75A1EA5932A9C6E6CC41E9B 70C67F9198F5105DFE70AA867679E0D3
EP9000-UCES01242_00-PRESELLADDONPCK3 3C67064205AF3B6F5516E1008F7E9578 3DA0A4525880F6E65D921B406291D0E0 FDB5E4B9E8FE2EBED8BE487F8B585ED9 C07F639AC4C4E5E3E3DED4054F600514
EP9000-UCES01242_00-PRESELLADDONPCK2 211625C7ACF3AD87FB80C088B5A816D0 731870DDA5D2B6A454FC32B196B39BB4 034B72CB73F41ECE183E17C5F1F5DB2F 01BDE1B45251E03597DE2D00F745E0E0
EP0001-NPEH00029_00-GPCASSASSINPGO02 3256531B6287569F75A12C24DB1D92F5 779E4C9FA20A2DC30DBBFDC05B6F8CE9 0E446F48989AF17569671B0BC24EE951 CBFD89B5ED02344EC13485B31E32DAB1
EP0006-NPEW00073_00-TPCFIFA11P000001 266993E225B9FE02F6784CF706624989 F19D7EDF0BDC5889A96DACB9B8D1AC68 C12E67459716BACCC208707B65793E1D 5C2104AB3DDB9974E444259EA0C75C2A
EP1004-ULES00502_00-GPCGRANDTH000001 0E39CF82632FFF40CB6431ED3D612C55 80C952D1EAE9B0486080B9A34E0F7C26 FD1233140909948C810391716D4730C9 9A1B891F7885430FBF0E2AD3B1AA15EE
EP1063-NPEH00101_00-PRAM001122334455 30CDDC33A4E18D8C9335278B7E05BB9B 4A1FBE67782A0324E4C7756F3854FE2B 60C5698A260CD64F8EF4ABFC936A354F D46F9CFBC83E2BCAE3C0AEB44D678756
EP1063-NPEH00101_00-TICKET0011223344 891C6358BCD5FA5822125170C9CFDC88 67128C1321601EB91F7FCF0D643A5A83 C0713C15C91ABF06278CC0A9437F1F27 53FAB2836B5DC5C89A2CC9D68CE5C23E
EP4293-ULES01515_00-GBPSPGAMERG00100 2CD73C4EFC809558543F1B3E3C272AB9 64794493FD2B4B960FB73AD6B1E140F7 766898B4E6BD91A138C242B4D56B1EE1 E849705F7C20FA3E4A963A01A632B8AA
EP9000-NPEG90013_00-DPCRESISTA000001 D9621D80C5BEF35B42DC2A18E8C1C1F2 D27AC3D28A9D564F8EE713B1C485C1B7 9BBB0AD3A6BDA78720ADBC73E30018BD 4EEE59B99F8DD06D97DFF7E196B317C0
EP9000-NPEG90019_00-LBPPSPEPSN000001 C0F7AD7D7012202CF27BBE670265BF05 1DCF8692689FB9CC0E010DE98EF77C23 8957E078A33C249D6CFEBF4A69CD21EE 6ADCB170B8A861E2035BAA20CB5BAA2C
EP9000-NPEW00068_00-TPCPSPGOPR000001 C3602E7EE9CA1B5D4600C5AA54BB1055 B53C53352F887C2DA84E5BDE29EB72AF 8CFA5E61BE013AFCFE418126CEF4D809 36644FDB1346637D6AF6C21426692FB8
EP9000-UCED90042_00-DPCSOCOMFTDEMO01 47D6208DF789C4ECD86692CF4D60A322 ECA7BAEC11B35B8F54280FE0D389B3B6 78DA407B23310875D52648802F7E4974 1878AC0652D24B8AEFB286358D2B5E60
EP9000-UCES00422_00-RIDGERACER2DEMO1 D5041BB4CBFBDFD6470F9F9EE6FE5A12 8A9833DB42B4848FEA6BF46E0CCCBB6D F09D093506D205CAE121D641C0AAB354 33B9BB2EE725BF8AD6AED826447C6BE9
EP9000-UCES00694_00-PFXTRMJUSTPSPGO2 0A4368759887952E157998EA5EA27396 71539E955A231AA6AB7044DA6E3D80AD FCCFB57EA5C15E1E5554811ECC7203E3 3041033FCD90D2ABA50545F6227410EB
EP9000-UCES01264_00-LBPPSPEFULL00001 297DBC7870E72EF195D85611A10B94E7 A9F0F0D181C264D15CB3AFC553EB63A7 0D905696883327C8F4CE4DC45B185FB8 900132071EB7175615C1CA822C804BA0
EP9001-NPEW00051_00-TPCGRANTUR000001 FEA1848910E05C9D45C9FBFA8863C1BE DE374F786E6CBF4E057B1C9EB64B3124 D50C141C8A7B0B840903F60A3796FB5B A346DCD09D173B36E524B29D776066C3

View File

@ -26,7 +26,7 @@ namespace LibChovy.VersionKey
catch { return null; }
}
public static NpDrmInfo? GetKeyFromGamePsvimg(string gameBackupFolder, string accountId)
public static NpDrmInfo? GetKeyFromGamePsvimg(string gameBackupFolder, string accountId, int keyIndex)
{
string gamePsvimgFile = Path.Combine(gameBackupFolder, "game", "game.psvimg");
if (!File.Exists(gamePsvimgFile)) return null;
@ -36,7 +36,7 @@ namespace LibChovy.VersionKey
using (PSVIMGFileStream? pbp = GetFileFromPsvImg(gamePsvimgFile, "/EBOOT.PBP", cmaKey))
{
if (pbp is null) return null;
return EbootPbpMethod.GetVersionKey(pbp);
return EbootPbpMethod.GetVersionKey(pbp, keyIndex);
}
}

View File

@ -21,7 +21,7 @@ namespace LibChovy.VersionKey
AMCTRL.bbmac_getkey(mkey, bbmac, versionKey);
return versionKey;
}
public static NpDrmInfo GetVersionKey(Stream ebootStream)
public static NpDrmInfo GetVersionKey(Stream ebootStream, int keyIndex)
{
using (ebootStream)
{
@ -39,32 +39,35 @@ namespace LibChovy.VersionKey
switch (psarMagic)
{
case "NPUMDIMG":
int keyType = ebootUtil.ReadInt32();
int orginalKeyIndex = ebootUtil.ReadInt32();
string contentId = ebootUtil.ReadStringAt(dataPsarLocation + 0x10);
byte[] npUmdHdr = ebootUtil.ReadBytesAt(dataPsarLocation, 0xC0);
byte[] npUmdHeaderHash = ebootUtil.ReadBytesAt(dataPsarLocation + 0xC0, 0x10);
byte[] versionkey = getKey(npUmdHeaderHash, npUmdHdr);
return new NpDrmInfo(versionkey, contentId, keyType);
SceNpDrm.sceNpDrmTransformVersionKey(versionkey, orginalKeyIndex, keyIndex);
return new NpDrmInfo(versionkey, contentId, keyIndex);
case "PSISOIMG":
using (DNASStream dnas = new DNASStream(ebootStream, dataPsarLocation + 0x400))
{
contentId = ebootUtil.ReadStringAt(dataPspLocation + 0x560);
keyType = dnas.KeyIndex;
orginalKeyIndex = dnas.KeyIndex;
versionkey = dnas.VersionKey;
return new NpDrmInfo(versionkey, contentId, keyType);
SceNpDrm.sceNpDrmTransformVersionKey(versionkey, orginalKeyIndex, keyIndex);
return new NpDrmInfo(versionkey, contentId, keyIndex);
}
case "PSTITLEI":
using (DNASStream dnas = new DNASStream(ebootStream, dataPsarLocation + 0x200))
{
contentId = ebootUtil.ReadStringAt(dataPspLocation + 0x560);
keyType = dnas.KeyIndex;
orginalKeyIndex = dnas.KeyIndex;
versionkey = dnas.VersionKey;
return new NpDrmInfo(versionkey, contentId, keyType);
SceNpDrm.sceNpDrmTransformVersionKey(versionkey, orginalKeyIndex, keyIndex);
return new NpDrmInfo(versionkey, contentId, keyIndex);
}
default:
throw new Exception("Cannot obtain versionkey from this EBOOT.PBP (magic:" + psarMagic + ")");

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -2,12 +2,12 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.1.0" />
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.2.2" />
</ItemGroup>
<ItemGroup>

View File

@ -6,10 +6,10 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net6.0\publish\win-x64\</PublishDir>
<PublishDir>bin\Release\net8.0\publish\win-x64\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>

View File

@ -197,6 +197,7 @@ namespace PspCrypto
var encData = encryptor.TransformFinalBlock(buffer, 0, buffer.Length);
encData.AsSpan().CopyTo(oubput);
}
#if false
public static byte[] AesDecrypt(Aes aes, byte[] data, int offset, int length)
{

View File

@ -1080,7 +1080,7 @@ namespace PspCrypto
unsafe
{
var ecdsa = ECDsaHelper.Create(curve, sig.public_key.x, sig.public_key.y);
ECDsa ecdsa = ECDsaHelper.Create(curve, sig.public_key.x, sig.public_key.y);
if (ecdsa.VerifyHash(sig.message_hash, sig.signature.sig))
{

View File

@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.3.0" />
<PackageReference Include="DotNetZip" Version="1.16.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
</ItemGroup>

View File

@ -3,6 +3,7 @@ using System;
using System.Buffers.Binary;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
@ -433,6 +434,14 @@ namespace PspCrypto
}
}
public static int sceNpDrmTransformVersionKey(Span<byte> versionKey, int srcKeyType, int dstKeyType)
{
// reverse the previous "GenVersionKey" step ..
int ret = ReverseGenVersionKey(versionKey, srcKeyType);
if (ret >= 0)
ret = GenVersionKey(versionKey, dstKeyType); // generate a new version key of this type
return ret;
}
public static int sceNpDrmGetIDps(Span<byte> idps)
{
@ -444,7 +453,7 @@ namespace PspCrypto
return -0x7faaf6ff;
}
private static int GetActKey(Span<byte> key, ReadOnlySpan<byte> keyTable, int count)
public static int GetActKey(Span<byte> key, ReadOnlySpan<byte> keyTable, int count)
{
Span<byte> idps = stackalloc byte[16];
Span<byte> decKey = stackalloc byte[16];
@ -525,6 +534,22 @@ namespace PspCrypto
return 0;
}
private static int ReverseGenVersionKey(Span<byte> versionkey, int type)
{
type &= 0xffffff;
int ret = 0;
if (type != 0)
{
ret = unchecked((int)0x80550901);
if (type < 4)
{
ret = AesHelper.AesDecrypt(versionkey, versionkey, KeyVault.drmVersionKeyKey.AsSpan().Slice(type * 0x10, 0x10));
ret = ret == 16 ? 0 : unchecked((int)0x80550902);
}
}
return ret;
}
private static int GenVersionKey(Span<byte> versionKey, int type)
{
type &= 0xffffff;

103
README.md
View File

@ -1,65 +1,83 @@
#ChovyProject v2
Now with PS1 Support !
\- chovy-sign is an application to allow you to convert PSP and PS1 ISO's to be playable on unmodified PSVita's
**chovy-sign is an application to allow you to convert PSP and PS1 ISO's to be playable on unmodified PSVita***
Setups:
You need a valid (offical) PSP or PS1 license,
You need an (offical) PSP or PS1 license;
i recommend getting; Petz Saddle Club, LocoRoco Midnight Carnival, or Ape Quest Starter Pack
atleast one of these are free in *most* regions;
because atleast one of these are free in *most* regions;
however failing that, you can also use a PSP DLC license-
-- if you don't have a hacked PSVita:
Note: If the game you are using to do this with any games besides PETZ SADDLE CLUB, LOCOROCO MIDNIGHT CARNIVAL or APE QUEST STARTER PACK;
you will be limited to the same game type as the license is for,
i.e a PS1 game will only be usable to create a PS1 game,
and a PSP game will only be usable to create a PSP game
(this is different' if you use the henkaku method)
----
**-- If you don't have a modded PSVita:**
Copy the game to your PC using Content Manager,
open chovy-sign2 and click the "Get Keys" button
-- if you are using "APE QUEST, LOCOROCO MIDNIGHT CARNIVAL, or PETZ SADDLE CLUB, and want to make a PS1 game select "KEYS.TXT" instead-)
select "EBOOT.PBP" method, click on the game
(note: there may be some issues if you try this while having DLC for the game installed ...)
*you will need:*
- Any PS1 or PSP game (or demo) from the PS Store. (Petz Saddle Club, LocoRoco Midnight Carnival, etc..)
- Ability to connect PSVita to PC Content Manager (USB or WiFi)
you should see the RIF and KEY fields populate, and your good to go!
*obtaining keys:*
- Copy an offical PS1 or PSP game to your PC using Content Manager,
- open chovy-sign2
- select either PSP or PS1 and click the "Get Keys" button
- click "EBOOT.PBP method",
- select the offical PS1 or PSP game you copied;
- click on select "Content Manager" backup
-- If you have a hacked PSVita:
note: you can still use the unhacked vita method if you would like
(i.e if your only wanting to make PSP games, or are using PETZ SADDLE CLUB, LOCOROCO MIDNIGHT CARNIVAL or APE QUEST STARTER PACK)
- you should see the RIF and KEY fields populate,
note2: bubbles created using the hacked vita method will still *work* on any unhacked vita,
using the same PSN Account; however it requires a hacked vita to *obtain* some of the files,
that are required for this method;
*chovy-sign2 will remember this information for later so you only have to do this part one time;*
the main advantage to doing it this way is you can use any psp license to create both ps1 and psp bubbles;
*getting psp or ps1 games:*
- select either a PS1 BIN/CUE image or a UMD ISO image.
- click "go"
- once it finishes, you should be able to find the game availible to be copied over from Content Manager.
you will need:
note: *there may be some issues if you try this while having DLC for the game installed .*
----
**-- If you have a modded PSVita:**
note: *you can still use the unmodified vita method on a hacked vita; if you would like *
note2: *bubbles created using the hacked vita method will still *work* on any unmodified vita, *
*using the same PSN Account; however it requires a hacked vita to *obtain* some of the files, *
*that are required for this method;*
*you will need:*
- Your ConsoleID/IDPS, Yoti has a tool to dump this availible [here](https://github.com/Yoti/psv_idpsdump/releases/)
- Your Consoles activation data file (located at tm0:/npdrm/act.dat)
- Any PSP/PS1 License file for your account (located at ux0:/pspemu/PSP/LICENSE/*.rif, or ux0:/bgdl/t/XXXXXX/temp.dat if you have a pending download)
- Your Consoles activation data file (located at ``tm0:/npdrm/act.dat``)
- Any offical PSP/PS1 License file for your account (located at ``ux0:/pspemu/PSP/LICENSE/*.rif``, or ``ux0:/bgdl/t/XXXXXX/temp.dat`` for downloads)
*you can also use PocketStation license or a PSP DLC license*
YOUTUBERS: if you are planning on making a youtube tutorial on this, please note that your consoles IDPS is
also used to identify your console on the playstation network,
and sharing it would allow anyone to impersonate your console on the PSN!,
do not just blindly give this out to people.
(this would not give them access to your PSN Account however)
note: *your IDPS is unique to your PSVita and is used to identify your console on the PSN, so do not share it with other people*
Open the CHOVY-SIGN2 application, under PSP click GET KEYS
put in your IDPS into the ConsoleID/IDPS field; in hex (if you used yotis tool, its in ux0:/data/idps.txt)
browse to your act.dat file, and rif file, and click Generate.
this will populate the key and rif field;
now do the same thing for the PS1 Option, using the same license and activation file as before.
*obtaining keys:*
- open chovy-sign2
- select either PSP or PS1 and click the "Get Keys" button
- select "IDPS+RIF+ACT Method",
- copy in your IDPS
- browse for your license rif file
- browse for our act.dat file
- click generate keys
*chovy-sign2 will remember this information for later so you only have to do this part one time;*
*getting psp or ps1 games:*
- select either a PS1 BIN/CUE image or a UMD ISO image.
- click "go"
- once it finishes, you should be able to find the game availible to be copied over from Content Manager.
- alternatively, you can disable the "Use CMA" option in settings, and then copy the resulting **TITLEID** folder to ``ux0:/pspemu/PSP/GAME``, then delete ``ux0:/id.dat``, and reboot to trigger an app database update.
a diagram explaining the 3 key obtaining methods is below:
-----
so overall there are 3 methods to obtain keys,
![image](https://silica.codes/SilicaAndPina/chovy-sign/raw/branch/master/Methods.png)
--
note: *keys.txt is basically pointless now, in the bast it wasn't known how to use psp eboot to create ps1 games*
----
Credits:
SquallATF (for "PspCrypto" and "PBPResigner", and making PS1 Game signing possible,
@ -71,7 +89,6 @@ Developing the GUI, finding the original psp bubbles method and,
Writing the PS1 Disc Compresison algorithm, making it possible to use custom ISOs,
Writing C# Implementation of PSVIMGTOOLS,
Being transgender)
dots-tb (for [chovy-gen](https://github.com/dots-tb/chovy-gen) (\_\_sce_ebootpbp signing)
@ -82,16 +99,14 @@ yifanlu & xyz for the original [psvimgtools](https://github.com/yifanlu/psvimgto
Mathieulh (Found psp signing keys?)
Motoharu (For helping dots with \_\_sce_ebootpbp)
Davee (For finding the PS1 Signing Key)
xlenore (For [PS1 Cover Art](https://github.com/xlenore/psx-covers))
RupertAvery (For their fork of DiscUtils to add PS1 Support)
SquallATF's PBPResigner and PSPCrypto were derived from :
xdotnano ([PSXTract](https://github.com/xdotnano/PSXtract))
swarzesherz ([sign_np](https://github.com/swarzesherz/sign_np))
tpunix ([kirk_engine](https://github.com/tpunix/kirk_engine))

View File

@ -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())
{

View File

@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.0" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.3.0" />
<PackageReference Include="DotNetZip" Version="1.16.0" />
</ItemGroup>