some shit got corrupted, will probably have to revert some stuff
This commit is contained in:
parent
f14b166dec
commit
03be164a0e
|
@ -1,8 +1,10 @@
|
|||
using GameBuilder.Pops;
|
||||
using GameBuilder.Progress;
|
||||
using Li.Progress;
|
||||
using GameBuilder.Pops;
|
||||
using GameBuilder.Psp;
|
||||
using GameBuilder.VersionKey;
|
||||
using PspCrypto;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.IO.Pipes;
|
||||
|
||||
namespace ChovySign_CLI
|
||||
{
|
||||
|
@ -36,7 +38,11 @@ namespace ChovySign_CLI
|
|||
}
|
||||
public static int Error(string errorMsg, int ret)
|
||||
{
|
||||
ConsoleColor prevColor = Console.ForegroundColor;
|
||||
Console.ForegroundColor = ConsoleColor.DarkRed;
|
||||
Console.Error.WriteLine("ERROR: "+errorMsg);
|
||||
Console.ForegroundColor = prevColor;
|
||||
|
||||
return ret;
|
||||
}
|
||||
public static byte[] StringToByteArray(string hex)
|
||||
|
@ -46,7 +52,12 @@ namespace ChovySign_CLI
|
|||
|
||||
private static void onProgress(ProgressInfo info)
|
||||
{
|
||||
Console.Write(info.CurrentProcess + " " + info.ProgressInt.ToString() + "% (" + info.Done + "/" + info.Remain + ") \r");
|
||||
string msg = info.CurrentProcess + " " + info.ProgressInt.ToString() + "% (" + info.Done + "/" + info.Remain + ")";
|
||||
int spaceLen = (Console.WindowWidth - msg.Length) - 2;
|
||||
string emptySpace = " ";
|
||||
for (int i = 0; i < spaceLen; i++)
|
||||
emptySpace += " ";
|
||||
Console.Write(msg + emptySpace + "\r");
|
||||
}
|
||||
|
||||
private static int complete()
|
||||
|
@ -98,7 +109,8 @@ namespace ChovySign_CLI
|
|||
}
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
if(args.Length == 0)
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
Console.WriteLine("Chovy-Sign v2 (CLI)");
|
||||
Console.WriteLine("--pops [disc1.cue] [disc2.cue] [disc3.cue] ... (up to 5)");
|
||||
|
@ -209,49 +221,33 @@ namespace ChovySign_CLI
|
|||
psfo.AddKey("PSP_SYSTEM_VER", "6.60", 8);
|
||||
psfo.AddKey("REGION", 32768, 4);
|
||||
psfo.AddKey("TITLE", popsDiscName, 128);
|
||||
|
||||
byte[] sfo = psfo.WriteSfo();
|
||||
|
||||
if (discs.Length == 1)
|
||||
{
|
||||
using (PsIsoImg psIsoImg = new PsIsoImg(drmInfo, discInfs.First()))
|
||||
{
|
||||
psIsoImg.RegisterCallback(onProgress);
|
||||
psIsoImg.CreatePsar();
|
||||
|
||||
PbpBuilder.CreatePbp(psfo.WriteSfo(),
|
||||
File.ReadAllBytes(popsIcon0File),
|
||||
null,
|
||||
(popsPic0File is not null) ? File.ReadAllBytes(popsPic0File) : null,
|
||||
Resources.PIC1,
|
||||
null,
|
||||
psIsoImg,
|
||||
"EBOOT.PBP",
|
||||
0);
|
||||
using (PbpBuilder pbpBuilder = new PbpBuilder(sfo, File.ReadAllBytes(popsIcon0File), null, (popsPic0File is not null) ? File.ReadAllBytes(popsPic0File) : null, Resources.PIC1, null, new PsIsoImg(drmInfo, discInfs.First()), 0))
|
||||
{
|
||||
pbpBuilder.RegisterCallback(onProgress);
|
||||
pbpBuilder.CreatePsarAndPbp();
|
||||
pbpBuilder.PbpStream.Seek(0x00, SeekOrigin.Begin);
|
||||
|
||||
byte[] ebootsig = new byte[0x200];
|
||||
SceNpDrm.KsceNpDrmEbootSigGenPs1("EBOOT.PBP", ebootsig, 0x3600000);
|
||||
SceNpDrm.KsceNpDrmEbootSigGenPs1(pbpBuilder.PbpStream, ebootsig, 0x3674000);
|
||||
File.WriteAllBytes("__sce_ebootpbp", ebootsig.ToArray());
|
||||
pbpBuilder.WritePbpToFile("EBOOT.PBP");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (PsTitleImg psTitleImg = new PsTitleImg(drmInfo, discInfs))
|
||||
using (PbpBuilder pbpBuilder = new PbpBuilder(sfo, File.ReadAllBytes(popsIcon0File), null, (popsPic0File is not null) ? File.ReadAllBytes(popsPic0File) : null, Resources.PIC1, null, new PsTitleImg(drmInfo, discInfs), 0))
|
||||
{
|
||||
psTitleImg.RegisterCallback(onProgress);
|
||||
psTitleImg.CreatePsar();
|
||||
|
||||
PbpBuilder.CreatePbp(psfo.WriteSfo(),
|
||||
File.ReadAllBytes(popsIcon0File),
|
||||
null,
|
||||
(popsPic0File is not null) ? File.ReadAllBytes(popsPic0File) : null,
|
||||
Resources.PIC1,
|
||||
null,
|
||||
psTitleImg,
|
||||
"EBOOT.PBP",
|
||||
0);
|
||||
pbpBuilder.RegisterCallback(onProgress);
|
||||
pbpBuilder.CreatePsarAndPbp();
|
||||
pbpBuilder.PbpStream.Seek(0x00, SeekOrigin.Begin);
|
||||
|
||||
byte[] ebootsig = new byte[0x200];
|
||||
SceNpDrm.KsceNpDrmEbootSigGenPs1("EBOOT.PBP", ebootsig, 0x3600000);
|
||||
pbpBuilder.WritePbpToFile("EBOOT.PBP");
|
||||
SceNpDrm.KsceNpDrmEbootSigGenPs1(pbpBuilder.PbpStream, ebootsig, 0x3674000);
|
||||
File.WriteAllBytes("__sce_ebootpbp", ebootsig.ToArray());
|
||||
}
|
||||
}
|
||||
|
@ -260,25 +256,16 @@ namespace ChovySign_CLI
|
|||
{
|
||||
using (UmdInfo umd = new UmdInfo(discs.First()))
|
||||
{
|
||||
using (NpUmdImg npUmd = new NpUmdImg(drmInfo, umd, pspCompress))
|
||||
using (PbpBuilder pbpBuilder = new PbpBuilder(umd.DataFiles["PARAM.SFO"], umd.DataFiles["ICON0.PNG"], umd.DataFiles["ICON1.PMF"], umd.DataFiles["PIC0.PNG"], umd.DataFiles["PIC1.PNG"], umd.DataFiles["SND0.AT3"], new NpUmdImg(drmInfo, umd, pspCompress), 1))
|
||||
{
|
||||
npUmd.RegisterCallback(onProgress);
|
||||
npUmd.CreatePsar();
|
||||
|
||||
PbpBuilder.CreatePbp(umd.DataFiles["PARAM.SFO"],
|
||||
umd.DataFiles["ICON0.PNG"],
|
||||
umd.DataFiles["ICON1.PMF"],
|
||||
umd.DataFiles["PIC0.PNG"],
|
||||
umd.DataFiles["PIC1.PNG"],
|
||||
umd.DataFiles["SND0.AT3"],
|
||||
npUmd,
|
||||
"EBOOT.PBP",
|
||||
1);
|
||||
pbpBuilder.RegisterCallback(onProgress);
|
||||
pbpBuilder.CreatePsarAndPbp();
|
||||
pbpBuilder.PbpStream.Seek(0x00, SeekOrigin.Begin);
|
||||
|
||||
byte[] ebootsig = new byte[0x200];
|
||||
SceNpDrm.KsceNpDrmEbootSigGenPsp("EBOOT.PBP", ebootsig, 0x3600000);
|
||||
SceNpDrm.KsceNpDrmEbootSigGenPsp(pbpBuilder.PbpStream, ebootsig, 0x3674000);
|
||||
File.WriteAllBytes("__sce_ebootpbp", ebootsig.ToArray());
|
||||
|
||||
pbpBuilder.WritePbpToFile("EBOOT.PBP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder
|
||||
namespace Li.Utilities
|
||||
{
|
||||
public static class MathUtil
|
||||
{
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder.Progress
|
||||
namespace Li.Progress
|
||||
{
|
||||
public class ProgressInfo
|
||||
{
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder.Progress
|
||||
namespace Li.Progress
|
||||
{
|
||||
public class ProgressTracker
|
||||
{
|
||||
|
@ -15,9 +15,9 @@ namespace GameBuilder.Progress
|
|||
progressCallbacks.Add(cb);
|
||||
}
|
||||
|
||||
public void UpdateProgress(int total, int remain, string what)
|
||||
protected void updateProgress(int done, int remain, string what)
|
||||
{
|
||||
ProgressInfo inf = new ProgressInfo(total, remain, what);
|
||||
ProgressInfo inf = new ProgressInfo(done, remain, what);
|
||||
foreach (Action<ProgressInfo> cb in progressCallbacks)
|
||||
cb(inf);
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
using PspCrypto;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder
|
||||
namespace Li.Utilities
|
||||
{
|
||||
public class StreamUtil
|
||||
{
|
|
@ -13,7 +13,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\PopsBuilder\GameBuilder.csproj" />
|
||||
<ProjectReference Include="..\PspCrypto\PspCrypto.csproj" />
|
||||
<ProjectReference Include="..\PsvImage\PsvImage.csproj" />
|
||||
<ProjectReference Include="..\PsvImgTools\Vita.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
Binary file not shown.
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using Li.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DiscUtils\DiscUtils.csproj" />
|
||||
<ProjectReference Include="..\LiGeneralUtilities\LiLib.csproj" />
|
||||
<ProjectReference Include="..\PspCrypto\PspCrypto.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using GameBuilder.Atrac3;
|
||||
using Li.Progress;
|
||||
using GameBuilder.Cue;
|
||||
using GameBuilder.Progress;
|
||||
using GameBuilder.Psp;
|
||||
using PspCrypto;
|
||||
using System;
|
||||
|
@ -9,10 +9,11 @@ using System.Linq;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Li.Utilities;
|
||||
|
||||
namespace GameBuilder.Pops
|
||||
{
|
||||
public class DiscCompressor : ProgressTracker
|
||||
public class DiscCompressor : ProgressTracker, IDisposable
|
||||
{
|
||||
const int COMPRESS_BLOCK_SZ = 0x9300;
|
||||
const int DEFAULT_ISO_OFFSET = 0x100000;
|
||||
|
@ -103,7 +104,7 @@ namespace GameBuilder.Pops
|
|||
while (eccRem.Position < eccRem.Length)
|
||||
{
|
||||
writeCompressedIsoBlock(eccRem);
|
||||
UpdateProgress(Convert.ToInt32(eccRem.Position), Convert.ToInt32(eccRem.Length), "Compress & Encrypt Disc");
|
||||
updateProgress(Convert.ToInt32(eccRem.Position), Convert.ToInt32(eccRem.Length), "Compress & Encrypt Disc");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +164,7 @@ namespace GameBuilder.Pops
|
|||
for (int i = 1; i <= totalTracks; i++)
|
||||
{
|
||||
if (cue.GetTrackNumber(i).TrackType != TrackType.TRACK_CDDA) continue;
|
||||
UpdateProgress(i, totalTracks, "Convert CD Audio tracks to ATRAC3");
|
||||
updateProgress(i, totalTracks, "Convert CD Audio tracks to ATRAC3");
|
||||
|
||||
using (CueStream audioStream = cue.OpenTrack(i))
|
||||
{
|
||||
|
@ -219,6 +220,12 @@ namespace GameBuilder.Pops
|
|||
isoHeaderUtil.WriteBytes(cue.CreateToc());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsoHeader.Dispose();
|
||||
CompressedIso.Dispose();
|
||||
cue.Dispose();
|
||||
}
|
||||
|
||||
private DiscInfo disc;
|
||||
private CueReader cue;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using GameBuilder;
|
||||
using GameBuilder.Psp;
|
||||
using Li.Utilities;
|
||||
using PspCrypto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -10,7 +11,7 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace GameBuilder.Pops
|
||||
{
|
||||
public class PopsImg : NpDrmPsar
|
||||
public abstract class PopsImg : NpDrmPsar
|
||||
{
|
||||
|
||||
public PopsImg(NpDrmInfo versionKey) : base(versionKey)
|
||||
|
@ -28,6 +29,7 @@ namespace GameBuilder.Pops
|
|||
private StreamUtil simpleUtil;
|
||||
public byte[] StartDat;
|
||||
public byte[] SimplePgd;
|
||||
|
||||
private void createSimpleDat()
|
||||
{
|
||||
simpleUtil.WriteStr("SIMPLE ");
|
||||
|
@ -39,9 +41,6 @@ namespace GameBuilder.Pops
|
|||
|
||||
simpleUtil.WriteBytes(Resources.SIMPLE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private byte[] generateSimplePgd()
|
||||
{
|
||||
simple.Seek(0x0, SeekOrigin.Begin);
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
using GameBuilder.Atrac3;
|
||||
using Li.Progress;
|
||||
using GameBuilder.Atrac3;
|
||||
using GameBuilder.Cue;
|
||||
using GameBuilder.Psp;
|
||||
using PspCrypto;
|
||||
using System;
|
||||
using System.Net;
|
||||
using GameBuilder.Progress;
|
||||
|
||||
namespace GameBuilder.Pops
|
||||
{
|
||||
public class PsIsoImg : PopsImg
|
||||
public class PsIsoImg : PopsImg, IDisposable
|
||||
{
|
||||
internal PsIsoImg(NpDrmInfo versionKey, DiscCompressor discCompressor) : base(versionKey)
|
||||
{
|
||||
|
@ -26,11 +26,9 @@ namespace GameBuilder.Pops
|
|||
this.compressor = new DiscCompressor(this, disc, new Atrac3ToolEncoder());
|
||||
this.compressor.RegisterCallback(onProgress);
|
||||
}
|
||||
public void CreatePsar(bool isPartOfMultiDisc=false)
|
||||
{
|
||||
compressor.GenerateIsoHeaderAndCompress();
|
||||
if (!isPartOfMultiDisc) compressor.WriteSimpleDatLocation((compressor.IsoOffset + compressor.CompressedIso.Length) + StartDat.Length);
|
||||
|
||||
internal void generatePsIsoHeader()
|
||||
{
|
||||
psarUtil.WriteStr("PSISOIMG0000");
|
||||
psarUtil.WriteInt64(0x00); // location of STARTDAT
|
||||
|
||||
|
@ -39,12 +37,23 @@ namespace GameBuilder.Pops
|
|||
byte[] isoHdrPgd = compressor.GenerateIsoPgd();
|
||||
psarUtil.WriteBytes(isoHdrPgd);
|
||||
psarUtil.PadUntil(0x00, compressor.IsoOffset);
|
||||
}
|
||||
|
||||
public override void CreatePsar()
|
||||
{
|
||||
// compress ISO and generate header.
|
||||
compressor.GenerateIsoHeaderAndCompress();
|
||||
|
||||
// write STARTDAT location
|
||||
compressor.WriteSimpleDatLocation((compressor.IsoOffset + compressor.CompressedIso.Length) + StartDat.Length);
|
||||
|
||||
// write general PSISO header
|
||||
generatePsIsoHeader();
|
||||
|
||||
// Copy compressed ISO to PSAR stream..
|
||||
compressor.CompressedIso.Seek(0x00, SeekOrigin.Begin);
|
||||
compressor.CompressedIso.CopyTo(Psar);
|
||||
|
||||
if (isPartOfMultiDisc) return;
|
||||
|
||||
// write STARTDAT
|
||||
Int64 startDatLocation = Psar.Position;
|
||||
psarUtil.WriteBytes(StartDat);
|
||||
|
@ -57,10 +66,14 @@ namespace GameBuilder.Pops
|
|||
psarUtil.WriteInt64(startDatLocation);
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
compressor.Dispose();
|
||||
base.Dispose();
|
||||
}
|
||||
private void onProgress(ProgressInfo inf)
|
||||
{
|
||||
this.UpdateProgress(inf.Done, inf.Remain, inf.CurrentProcess);
|
||||
this.updateProgress(inf.Done, inf.Remain, inf.CurrentProcess);
|
||||
}
|
||||
|
||||
private DiscCompressor compressor;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using GameBuilder.Atrac3;
|
||||
using GameBuilder.Progress;
|
||||
using Li.Progress;
|
||||
using Li.Utilities;
|
||||
using GameBuilder.Atrac3;
|
||||
using GameBuilder.Psp;
|
||||
using PspCrypto;
|
||||
using System;
|
||||
|
@ -20,7 +21,7 @@ namespace GameBuilder.Pops
|
|||
|
||||
private void onProgress(ProgressInfo inf)
|
||||
{
|
||||
this.UpdateProgress(inf.Done, inf.Remain, inf.CurrentProcess + " (disc " + discNumber + ")");
|
||||
this.updateProgress(inf.Done, inf.Remain, inf.CurrentProcess + " (disc " + discNumber + ")");
|
||||
}
|
||||
|
||||
public PsTitleImg(NpDrmInfo drmInfo, DiscInfo[] discs) : base(drmInfo)
|
||||
|
@ -52,7 +53,7 @@ namespace GameBuilder.Pops
|
|||
|
||||
}
|
||||
|
||||
public void CreatePsar()
|
||||
public override void CreatePsar()
|
||||
{
|
||||
createIsoMap();
|
||||
|
||||
|
@ -119,20 +120,31 @@ namespace GameBuilder.Pops
|
|||
|
||||
using (PsIsoImg psIsoImg = new PsIsoImg(this.DrmInfo, compressors[i]))
|
||||
{
|
||||
// write location of iso
|
||||
isoMapUtil.WriteUInt32(Convert.ToUInt32(PSISO_ALIGN + isoPart.Position));
|
||||
|
||||
psIsoImg.CreatePsar(true);
|
||||
// compress current iso and generate headers
|
||||
compressors[i].GenerateIsoHeaderAndCompress();
|
||||
|
||||
// generate PSISOIMG header
|
||||
psIsoImg.generatePsIsoHeader();
|
||||
|
||||
// Copy compressed ISO to PSISOIMG
|
||||
compressors[i].CompressedIso.Seek(0x00, SeekOrigin.Begin);
|
||||
compressors[i].CompressedIso.CopyTo(psIsoImg.Psar);
|
||||
|
||||
// read 0x400 bytes from PSAR copy iso header after that,.
|
||||
psIsoImg.Psar.Seek(0x0, SeekOrigin.Begin);
|
||||
compressors[i].IsoHeader.Seek(0x00, SeekOrigin.Begin);
|
||||
|
||||
byte[] isoHdr = new byte[compressors[i].IsoHeader.Length + 0x400];
|
||||
psIsoImg.Psar.Read(isoHdr, 0x00, 0x400);
|
||||
compressors[i].IsoHeader.Read(isoHdr, 0x400, Convert.ToInt32(compressors[i].IsoHeader.Length));
|
||||
|
||||
// Calculate checksum
|
||||
byte[] checksum = calculateChecksumForIsoImgTitle(isoHdr);
|
||||
Array.ConstrainedCopy(checksum, 0, checksums, i * 0x10, 0x10);
|
||||
|
||||
// copy psiso to TITLE ..
|
||||
psIsoImg.Psar.Seek(0x00, SeekOrigin.Begin);
|
||||
psIsoImg.Psar.CopyTo(isoPart);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using GameBuilder.Progress;
|
||||
using Li.Progress;
|
||||
using Li.Utilities;
|
||||
using PspCrypto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -24,6 +25,7 @@ namespace GameBuilder.Psp
|
|||
public NpDrmInfo DrmInfo;
|
||||
public MemoryStream Psar;
|
||||
internal StreamUtil psarUtil;
|
||||
public abstract void CreatePsar();
|
||||
public abstract byte[] GenerateDataPsp();
|
||||
public static byte[] CreateStartDat(byte[] image)
|
||||
{
|
||||
|
|
|
@ -9,6 +9,7 @@ using System.Net.Security;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Li.Utilities;
|
||||
|
||||
namespace GameBuilder.Psp
|
||||
{
|
||||
|
@ -71,7 +72,7 @@ namespace GameBuilder.Psp
|
|||
npHdrUtil.PadUntil(0x00, 0x100);
|
||||
}
|
||||
|
||||
public void CreatePsar()
|
||||
public override void CreatePsar()
|
||||
{
|
||||
patchSfo();
|
||||
createNpUmdTbl();
|
||||
|
@ -167,7 +168,7 @@ namespace GameBuilder.Psp
|
|||
|
||||
isoOffset += wsize;
|
||||
|
||||
UpdateProgress(Convert.ToInt32(umdImage.IsoStream.Position), Convert.ToInt32(umdImage.IsoStream.Length), "Compress & Encrypt UMD Image");
|
||||
updateProgress(Convert.ToInt32(umdImage.IsoStream.Position), Convert.ToInt32(umdImage.IsoStream.Length), "Compress & Encrypt UMD Image");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using GameBuilder.Progress;
|
||||
using Li.Progress;
|
||||
using Li.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
@ -7,75 +8,141 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace GameBuilder.Psp
|
||||
{
|
||||
public static class PbpBuilder
|
||||
public class PbpBuilder : ProgressTracker , IDisposable
|
||||
{
|
||||
public static void CreatePbp(byte[]? paramSfo, byte[]? icon0Png, byte[]? icon1Png,
|
||||
byte[]? pic0Png, byte[]? pic1Png, byte[]? snd0At3,
|
||||
NpDrmPsar dataPsar, string outputFile, short version = 1)
|
||||
private byte[] paramSfo;
|
||||
private byte[] icon0Png;
|
||||
private byte[]? icon1Pmf;
|
||||
private byte[]? pic0Png;
|
||||
private byte[]? pic1Png;
|
||||
private byte[]? snd0At3;
|
||||
private NpDrmPsar psar;
|
||||
private short pbpVersion;
|
||||
private MemoryStream pbpStream;
|
||||
|
||||
public PbpBuilder(byte[] paramSfo, byte[] icon0Png, byte[]? icon1Pmf,
|
||||
byte[]? pic0Png, byte[]? pic1Png, byte[]? snd0At3,
|
||||
NpDrmPsar dataPsar, short version = 1)
|
||||
{
|
||||
this.paramSfo = paramSfo;
|
||||
this.icon0Png = icon0Png;
|
||||
this.icon1Pmf = icon1Pmf;
|
||||
this.pic0Png = pic0Png;
|
||||
this.pic1Png = pic1Png;
|
||||
this.snd0At3 = snd0At3;
|
||||
this.psar = dataPsar;
|
||||
this.pbpVersion = version;
|
||||
|
||||
using (FileStream pbpStream = File.Open(outputFile, FileMode.Create))
|
||||
{
|
||||
byte[] dataPsp = dataPsar.GenerateDataPsp();
|
||||
|
||||
int padLen = MathUtil.CalculatePaddingAmount(dataPsp.Length, 0x100);
|
||||
if(version == 1)
|
||||
Array.Resize(ref dataPsp, dataPsp.Length + padLen);
|
||||
|
||||
StreamUtil pbpUtil = new StreamUtil(pbpStream);
|
||||
pbpUtil.WriteByte(0x00);
|
||||
pbpUtil.WriteStr("PBP");
|
||||
pbpUtil.WriteInt16(version);
|
||||
pbpUtil.WriteInt16(1);
|
||||
|
||||
// param location
|
||||
uint loc = 0x28;
|
||||
if (paramSfo is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(paramSfo.Length); }
|
||||
|
||||
// icon0 location
|
||||
if (icon0Png is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(icon0Png.Length); }
|
||||
|
||||
// icon1 location
|
||||
if (icon1Png is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(icon1Png.Length); }
|
||||
|
||||
// pic0 location
|
||||
if (pic0Png is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(pic0Png.Length); }
|
||||
|
||||
// pic1 location
|
||||
if (pic1Png is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(pic1Png.Length); }
|
||||
|
||||
// snd0 location
|
||||
if (snd0At3 is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(snd0At3.Length); }
|
||||
|
||||
// datapsp location
|
||||
pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(dataPsp.Length);
|
||||
|
||||
// psar location
|
||||
pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(dataPsar.Psar.Length);
|
||||
|
||||
// write pbp metadata
|
||||
if (paramSfo is not null) pbpUtil.WriteBytes(paramSfo);
|
||||
if (icon0Png is not null) pbpUtil.WriteBytes(icon0Png);
|
||||
if (icon1Png is not null) pbpUtil.WriteBytes(icon1Png);
|
||||
if (pic0Png is not null) pbpUtil.WriteBytes(pic0Png);
|
||||
if (pic1Png is not null) pbpUtil.WriteBytes(pic1Png);
|
||||
if (snd0At3 is not null) pbpUtil.WriteBytes(snd0At3);
|
||||
|
||||
// write DATA.PSP
|
||||
pbpUtil.WriteBytes(dataPsp);
|
||||
|
||||
// write DATA.PSAR
|
||||
dataPsar.Psar.Seek(0x00, SeekOrigin.Begin);
|
||||
dataPsar.Psar.CopyTo(pbpStream);
|
||||
}
|
||||
|
||||
pbpStream = new MemoryStream();
|
||||
psar.RegisterCallback(onProgress);
|
||||
}
|
||||
|
||||
public MemoryStream PbpStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return pbpStream;
|
||||
}
|
||||
}
|
||||
|
||||
private void onProgress(ProgressInfo inf)
|
||||
{
|
||||
updateProgress(inf.Done, inf.Remain, inf.CurrentProcess);
|
||||
}
|
||||
|
||||
public void CreatePsarAndPbp()
|
||||
{
|
||||
psar.CreatePsar();
|
||||
CreatePbp();
|
||||
}
|
||||
|
||||
public void WritePbpToFile(string file)
|
||||
{
|
||||
using(FileStream pbpFile = File.OpenWrite(file))
|
||||
{
|
||||
pbpStream.Seek(0x00, SeekOrigin.Begin);
|
||||
copyPsarWithProgress(pbpStream, pbpFile, "Write to Disk");
|
||||
}
|
||||
}
|
||||
|
||||
public void CreatePbp()
|
||||
{
|
||||
byte[] dataPsp = psar.GenerateDataPsp();
|
||||
|
||||
int padLen = MathUtil.CalculatePaddingAmount(dataPsp.Length, 0x100);
|
||||
if(pbpVersion == 1)
|
||||
Array.Resize(ref dataPsp, dataPsp.Length + padLen);
|
||||
|
||||
StreamUtil pbpUtil = new StreamUtil(pbpStream);
|
||||
pbpUtil.WriteByte(0x00);
|
||||
pbpUtil.WriteStr("PBP");
|
||||
pbpUtil.WriteInt16(pbpVersion);
|
||||
pbpUtil.WriteInt16(1);
|
||||
|
||||
// param location
|
||||
uint loc = 0x28;
|
||||
if (paramSfo is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(paramSfo.Length); }
|
||||
|
||||
// icon0 location
|
||||
if (icon0Png is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(icon0Png.Length); }
|
||||
|
||||
// icon1 location
|
||||
if (icon1Pmf is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(icon1Pmf.Length); }
|
||||
|
||||
// pic0 location
|
||||
if (pic0Png is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(pic0Png.Length); }
|
||||
|
||||
// pic1 location
|
||||
if (pic1Png is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(pic1Png.Length); }
|
||||
|
||||
// snd0 location
|
||||
if (snd0At3 is null) { pbpUtil.WriteUInt32(loc); }
|
||||
else { pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(snd0At3.Length); }
|
||||
|
||||
// datapsp location
|
||||
pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(dataPsp.Length);
|
||||
|
||||
// psar location
|
||||
pbpUtil.WriteUInt32(loc); loc += Convert.ToUInt32(psar.Psar.Length);
|
||||
|
||||
// write pbp metadata
|
||||
if (paramSfo is not null) pbpUtil.WriteBytes(paramSfo);
|
||||
if (icon0Png is not null) pbpUtil.WriteBytes(icon0Png);
|
||||
if (icon1Pmf is not null) pbpUtil.WriteBytes(icon1Pmf);
|
||||
if (pic0Png is not null) pbpUtil.WriteBytes(pic0Png);
|
||||
if (pic1Png is not null) pbpUtil.WriteBytes(pic1Png);
|
||||
if (snd0At3 is not null) pbpUtil.WriteBytes(snd0At3);
|
||||
|
||||
// write DATA.PSP
|
||||
pbpUtil.WriteBytes(dataPsp);
|
||||
|
||||
// write DATA.PSAR
|
||||
copyPsarWithProgress(psar.Psar, pbpStream);
|
||||
}
|
||||
|
||||
|
||||
private void copyPsarWithProgress(Stream src, Stream dst, string msg = "Build PBP")
|
||||
{
|
||||
src.Seek(0, SeekOrigin.Begin);
|
||||
while (src.Position < src.Length)
|
||||
{
|
||||
byte[] readBuffer = new byte[0x30000];
|
||||
int readAmt = src.Read(readBuffer, 0x00, readBuffer.Length);
|
||||
dst.Write(readBuffer, 0x00, readAmt);
|
||||
|
||||
updateProgress(Convert.ToInt32(src.Position), Convert.ToInt32(src.Length), msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
psar.Dispose();
|
||||
pbpStream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
using System;
|
||||
using Li.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// A Sfo Parser Written by SilicaAndPina
|
||||
// A Sfo Parser Written by Li
|
||||
// Because all the others are overly-complicated for no reason!
|
||||
// MIT Licensed.
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using GameBuilder.Psp;
|
||||
using Li.Utilities;
|
||||
using PspCrypto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder
|
||||
{
|
||||
public class xXEccD3str0yerXx
|
||||
{
|
||||
const int SECTOR_SZ = 2352;
|
||||
const int COMPRESS_BLOCK_SZ = 0x9300;
|
||||
|
||||
public static MemoryStream RemoveEcc(string iso)
|
||||
{
|
||||
using(FileStream eccIso = File.OpenRead(iso))
|
||||
{
|
||||
MemoryStream noEccIso = new MemoryStream();
|
||||
while (eccIso.Position < eccIso.Length)
|
||||
{
|
||||
byte[] sector = new byte[SECTOR_SZ];
|
||||
|
||||
eccIso.Read(sector, 0, sector.Length);
|
||||
|
||||
// clear current sync
|
||||
Array.Fill(sector, (byte)0x00, 0x1, 0x0A);
|
||||
|
||||
// remove MSF ..
|
||||
sector[0x0C] = 0x00; // M
|
||||
sector[0x0D] = 0x00; // S
|
||||
sector[0x0E] = 0x00; // F
|
||||
|
||||
// remove ecc
|
||||
|
||||
// (only if this is not form2mode2 sector!)
|
||||
if (!(sector[0xF] == 0x2 && (sector[0x12] & 0x20) == 0x20))
|
||||
Array.Fill(sector, (byte)0x00, 0x818, 0x118);
|
||||
else if(eccIso.Position > 0x9300) // only clear if its past the system section ..
|
||||
Array.Fill(sector, (byte)0x00, 0x92C, 0x4);
|
||||
|
||||
noEccIso.Write(sector, 0, sector.Length);
|
||||
}
|
||||
|
||||
// extend ISO image to compress block sz
|
||||
int padLen = MathUtil.CalculatePaddingAmount(Convert.ToInt32(noEccIso.Length), COMPRESS_BLOCK_SZ);
|
||||
byte[] padding = new byte[padLen];
|
||||
noEccIso.Write(padding, 0x00, padding.Length);
|
||||
|
||||
noEccIso.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return noEccIso;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.1.1" />
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.0" />
|
||||
<PackageReference Include="DotNetZip" Version="1.16.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -721,6 +721,20 @@ namespace PspCrypto
|
|||
{
|
||||
return SceNpDrmEbootSigGenMultiDisc(fileName, sceDiscInfo, ebootSig, swVer, _memory.Span, BLK_SIZE);
|
||||
}
|
||||
public static int KsceNpDrmEbootSigGenPs1(Stream file, Span<byte> ebootSig, int swVer)
|
||||
{
|
||||
return SceNpDrmEbootSigGen(file, 3, ebootSig, swVer, _memory.Span, BLK_SIZE);
|
||||
}
|
||||
|
||||
public static int KsceNpDrmEbootSigGenPsp(Stream file, Span<byte> ebootSig, int swVer)
|
||||
{
|
||||
return SceNpDrmEbootSigGen(file, 2, ebootSig, swVer, _memory.Span, BLK_SIZE);
|
||||
}
|
||||
|
||||
public static int KsceNpDrmPspEbootSigGen(Stream file, Span<byte> ebootSig)
|
||||
{
|
||||
return SceNpDrmPspEbootSigGen(file, ebootSig, _memory.Span, BLK_SIZE);
|
||||
}
|
||||
|
||||
public static int KsceNpDrmEbootSigGenPs1(string fileName, Span<byte> ebootSig, int swVer)
|
||||
{
|
||||
|
@ -996,8 +1010,20 @@ namespace PspCrypto
|
|||
return ret;
|
||||
}
|
||||
|
||||
private static int SceNpDrmEbootSigGen(string fileName, int type, Span<byte> ebootSig, int swVer, Span<byte> buffer,
|
||||
int blockSize)
|
||||
private static int SceNpDrmEbootSigGen(string fileName, int type, Span<byte> ebootSig, int swVer, Span<byte> buffer, int blockSize)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(fileName)) return -0x7f78ffff;
|
||||
var fi = new FileInfo(fileName);
|
||||
if (!fi.Exists)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
using(FileStream fstream = fi.OpenRead()){
|
||||
return SceNpDrmEbootSigGen(fstream, type, ebootSig, swVer, buffer, blockSize);
|
||||
}
|
||||
|
||||
}
|
||||
private static int SceNpDrmEbootSigGen(Stream ebootStream, int type, Span<byte> ebootSig, int swVer, Span<byte> buffer, int blockSize)
|
||||
{
|
||||
Span<byte> pbpHdrDigest = stackalloc byte[32];
|
||||
Span<byte> npUmdImgDigest = stackalloc byte[32];
|
||||
|
@ -1008,8 +1034,7 @@ namespace PspCrypto
|
|||
blockSize &= unchecked((int)0xFFFFFFC0);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(fileName) || buffer == null || ebootSig == null || blockSize < 0x400 ||
|
||||
type > 3)
|
||||
if (ebootStream == null || buffer == null || ebootSig == null || blockSize < 0x400 || type > 3)
|
||||
{
|
||||
return -0x7f78ffff;
|
||||
}
|
||||
|
@ -1021,21 +1046,16 @@ namespace PspCrypto
|
|||
sceEbootPbp.SwVer = swVer;
|
||||
sceEbootPbp.Aid = Aid;
|
||||
sceEbootPbp.SecureTick = Utils.AsRef<ulong>(secureTick);
|
||||
var fi = new FileInfo(fileName);
|
||||
if (!fi.Exists)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
using var stream = fi.OpenRead();
|
||||
var ret = SceEbootPbpDigest(stream, fi.Length, pbpHdrDigest, npUmdImgDigest, buffer, blockSize);
|
||||
long flen = ebootStream.Length;
|
||||
int ret = SceEbootPbpDigest(ebootStream, flen, pbpHdrDigest, npUmdImgDigest, buffer, blockSize);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
stream.Close();
|
||||
|
||||
sceEbootPbp.KeyType = 1;
|
||||
sceEbootPbp.PbpSize = fi.Length;
|
||||
sceEbootPbp.PbpSize = flen;
|
||||
sceEbootPbp.Type = type;
|
||||
var psarSig = Encoding.ASCII.GetString(buffer.Slice(0, 8));
|
||||
if (type == 3)
|
||||
|
@ -1178,8 +1198,23 @@ namespace PspCrypto
|
|||
return ret;
|
||||
}
|
||||
|
||||
private static int SceNpDrmPspEbootSigGen(string fileName, Span<byte> ebootSig, Span<byte> buffer,
|
||||
int blockSize)
|
||||
private static int SceNpDrmPspEbootSigGen(string fileName, Span<byte> ebootSig, Span<byte> buffer, int blockSize)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(fileName))
|
||||
return -0x7f78ffff;
|
||||
|
||||
var fi = new FileInfo(fileName);
|
||||
if (!fi.Exists)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
using(FileStream fs = fi.OpenRead())
|
||||
{
|
||||
return SceNpDrmPspEbootSigGen(fs, ebootSig, buffer, blockSize);
|
||||
}
|
||||
}
|
||||
|
||||
private static int SceNpDrmPspEbootSigGen(Stream ebootStream, Span<byte> ebootSig, Span<byte> buffer,int blockSize)
|
||||
{
|
||||
Span<byte> pbpHdrDigest = stackalloc byte[32];
|
||||
Span<byte> npUmdImgDigest = stackalloc byte[32];
|
||||
|
@ -1190,23 +1225,17 @@ namespace PspCrypto
|
|||
blockSize &= unchecked((int)0xFFFFFFC0);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(fileName) || buffer == null || ebootSig == null || blockSize < 0x400)
|
||||
if (ebootStream == null || buffer == null || ebootSig == null || blockSize < 0x400)
|
||||
{
|
||||
return -0x7f78ffff;
|
||||
}
|
||||
var fi = new FileInfo(fileName);
|
||||
if (!fi.Exists)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
using var stream = fi.OpenRead();
|
||||
var ret = SceEbootPbpDigest(stream, fi.Length, pbpHdrDigest, npUmdImgDigest, buffer, blockSize);
|
||||
long fileSize = ebootStream.Length;
|
||||
int ret = SceEbootPbpDigest(ebootStream, fileSize, pbpHdrDigest, npUmdImgDigest, buffer, blockSize);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
stream.Close();
|
||||
|
||||
if (Encoding.ASCII.GetString(buffer.Slice(0, 8)) != "NPUMDIMG")
|
||||
{
|
||||
|
|
27
PspTest.sln
27
PspTest.sln
|
@ -6,11 +6,13 @@ MinimumVisualStudioVersion = 10.0.40219.1
|
|||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PspCrypto", "PspCrypto\PspCrypto.csproj", "{A60BE4C2-C0D2-42FD-BCFF-631B9AFC62FE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PbpResign", "PbpResign\PbpResign.csproj", "{834B9F97-1B63-48EB-922E-0FC155BB2481}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PsvImage", "PsvImage\PsvImage.csproj", "{0F4A59D9-FF92-4B4F-BDC4-4BC473F9AA5C}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{75BBF537-8ED8-42A3-AA8B-80A730F1B3F2} = {75BBF537-8ED8-42A3-AA8B-80A730F1B3F2}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GameBuilder", "PopsBuilder\GameBuilder.csproj", "{D1DF66DB-52BE-489C-A292-525BC71F2BB2}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{63475285-AAD2-4DE9-B698-AFC9FAA58AC8} = {63475285-AAD2-4DE9-B698-AFC9FAA58AC8}
|
||||
{BF155D74-2923-432F-8566-26D83B15EEE8} = {BF155D74-2923-432F-8566-26D83B15EEE8}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
|
@ -19,7 +21,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChovySign-CLI", "ChovySign-
|
|||
{A60BE4C2-C0D2-42FD-BCFF-631B9AFC62FE} = {A60BE4C2-C0D2-42FD-BCFF-631B9AFC62FE}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscUtils", "DiscUtils\DiscUtils.csproj", "{BF155D74-2923-432F-8566-26D83B15EEE8}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscUtils", "DiscUtils\DiscUtils.csproj", "{BF155D74-2923-432F-8566-26D83B15EEE8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vita", "PsvImgTools\Vita.csproj", "{75BBF537-8ED8-42A3-AA8B-80A730F1B3F2}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{63475285-AAD2-4DE9-B698-AFC9FAA58AC8} = {63475285-AAD2-4DE9-B698-AFC9FAA58AC8}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiLib", "LiGeneralUtilities\LiLib.csproj", "{63475285-AAD2-4DE9-B698-AFC9FAA58AC8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -35,10 +44,6 @@ Global
|
|||
{834B9F97-1B63-48EB-922E-0FC155BB2481}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{834B9F97-1B63-48EB-922E-0FC155BB2481}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{834B9F97-1B63-48EB-922E-0FC155BB2481}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0F4A59D9-FF92-4B4F-BDC4-4BC473F9AA5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0F4A59D9-FF92-4B4F-BDC4-4BC473F9AA5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0F4A59D9-FF92-4B4F-BDC4-4BC473F9AA5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0F4A59D9-FF92-4B4F-BDC4-4BC473F9AA5C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D1DF66DB-52BE-489C-A292-525BC71F2BB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D1DF66DB-52BE-489C-A292-525BC71F2BB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D1DF66DB-52BE-489C-A292-525BC71F2BB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
@ -51,6 +56,14 @@ Global
|
|||
{BF155D74-2923-432F-8566-26D83B15EEE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BF155D74-2923-432F-8566-26D83B15EEE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BF155D74-2923-432F-8566-26D83B15EEE8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{75BBF537-8ED8-42A3-AA8B-80A730F1B3F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{75BBF537-8ED8-42A3-AA8B-80A730F1B3F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{75BBF537-8ED8-42A3-AA8B-80A730F1B3F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{75BBF537-8ED8-42A3-AA8B-80A730F1B3F2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{63475285-AAD2-4DE9-B698-AFC9FAA58AC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{63475285-AAD2-4DE9-B698-AFC9FAA58AC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{63475285-AAD2-4DE9-B698-AFC9FAA58AC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{63475285-AAD2-4DE9-B698-AFC9FAA58AC8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
static class AesHelper
|
||||
{
|
||||
public static byte[] CbcEncrypt(byte[] plainText, byte[] iv, byte[] key, int size = -1)
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
size = plainText.Length;
|
||||
}
|
||||
|
||||
using var aes = Aes.Create();
|
||||
aes.Padding = PaddingMode.None;
|
||||
aes.Key = key;
|
||||
aes.IV = iv;
|
||||
using var encrypt = aes.CreateEncryptor();
|
||||
var cipherText = encrypt.TransformFinalBlock(plainText, 0, size);
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
public static byte[] AesEcbDecrypt(byte[] cipherText, byte[] key, int size = -1)
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
size = cipherText.Length;
|
||||
}
|
||||
|
||||
using var aes = Aes.Create();
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Padding = PaddingMode.None;
|
||||
aes.Key = key;
|
||||
using var decrypt = aes.CreateDecryptor();
|
||||
var plainText = decrypt.TransformFinalBlock(cipherText, 0, size);
|
||||
return plainText;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
public class CmaKeys
|
||||
{
|
||||
private static readonly byte[] Passphrase = Encoding.ASCII.GetBytes("Sri Jayewardenepura Kotte");
|
||||
private static readonly byte[] Key = { 0xA9, 0xFA, 0x5A, 0x62, 0x79, 0x9F, 0xCC, 0x4C, 0x72, 0x6B, 0x4E, 0x2C, 0xE3, 0x50, 0x6D, 0x38 };
|
||||
|
||||
public static byte[] GenerateKey(string aid)
|
||||
{
|
||||
var longlong = Convert.ToUInt64(aid, 16);
|
||||
var aidBytes = BitConverter.GetBytes(longlong);
|
||||
Array.Reverse(aidBytes);
|
||||
return GenerateKey(aidBytes);
|
||||
}
|
||||
|
||||
public static byte[] GenerateKey(byte[] aid)
|
||||
{
|
||||
var derviedKey = aid.Concat(Passphrase).ToArray();
|
||||
|
||||
using var sha = SHA256.Create();
|
||||
derviedKey = sha.ComputeHash(derviedKey);
|
||||
using var aes = Aes.Create();
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Padding = PaddingMode.None;
|
||||
aes.Key = Key;
|
||||
using var decryptor = aes.CreateDecryptor();
|
||||
derviedKey = decryptor.TransformFinalBlock(derviedKey, 0, derviedKey.Length);
|
||||
return derviedKey;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
class PSVIMGBuilder
|
||||
{
|
||||
private byte[] iv;
|
||||
private byte[] key;
|
||||
private Stream mainStream;
|
||||
private SHA256 sha256;
|
||||
private Memory<byte> blockData;
|
||||
private int blockPosition;
|
||||
private long contentSize = 0;
|
||||
|
||||
private int blocksWritten = 0;
|
||||
private bool finished = false;
|
||||
|
||||
public long ContentSize => contentSize;
|
||||
|
||||
public int BlocksWritten => blocksWritten;
|
||||
|
||||
public bool HasFinished => finished;
|
||||
|
||||
//Footer
|
||||
private long totalBytes = 0;
|
||||
|
||||
public PSVIMGBuilder(Stream dst, byte[] Key)
|
||||
{
|
||||
|
||||
totalBytes = 0;
|
||||
contentSize = 0;
|
||||
sha256 = SHA256.Create();
|
||||
mainStream = dst;
|
||||
key = Key;
|
||||
|
||||
RandomNumberGenerator.Fill(iv);
|
||||
iv = AesHelper.AesEcbDecrypt(iv, key);
|
||||
|
||||
mainStream.Write(iv.AsSpan());
|
||||
totalBytes += iv.Length;
|
||||
|
||||
startNewBlock();
|
||||
}
|
||||
|
||||
public void AddFile(string FilePath, string ParentPath, string PathRel)
|
||||
{
|
||||
var sz = Utils.ToSceIoStat(FilePath).Size;
|
||||
}
|
||||
|
||||
private void startNewBlock()
|
||||
{
|
||||
blockData = new byte[PSVIMGConstants.FULL_PSVIMG_SIZE];
|
||||
blockPosition = 0;
|
||||
}
|
||||
|
||||
private byte[] shaBlock(int length = PSVIMGConstants.PSVIMG_BLOCK_SIZE)
|
||||
{
|
||||
return sha256.ComputeHash(blockData.ToArray(), 0, length);
|
||||
}
|
||||
|
||||
private void finishBlock(bool final = false)
|
||||
{
|
||||
int len = blockPosition;
|
||||
var shaBytes = shaBlock(len);
|
||||
shaBytes.CopyTo(blockData.Slice(blockPosition));
|
||||
len += PSVIMGConstants.SHA256_BLOCK_SIZE;
|
||||
|
||||
//Get next IV
|
||||
var encryptedBlock = AesHelper.CbcEncrypt(blockData.ToArray(), iv, key, len);
|
||||
for (int i = 0; i < iv.Length; i++)
|
||||
{
|
||||
int encBlockOffset = (encryptedBlock.Length - iv.Length) + i;
|
||||
iv[i] = encryptedBlock[encBlockOffset];
|
||||
}
|
||||
|
||||
mainStream.Write(encryptedBlock.AsSpan());
|
||||
totalBytes += encryptedBlock.Length;
|
||||
}
|
||||
|
||||
private int remainingBlockSize()
|
||||
{
|
||||
return (int)(PSVIMGConstants.PSVIMG_BLOCK_SIZE - blockPosition);
|
||||
}
|
||||
|
||||
private void writeBlock(Span<byte> date, bool update = false)
|
||||
{
|
||||
var dLen = date.Length;
|
||||
var writeTotal = 0;
|
||||
while (dLen > 0)
|
||||
{
|
||||
int remaining = remainingBlockSize();
|
||||
|
||||
if (dLen > remaining)
|
||||
{
|
||||
date.Slice(writeTotal, remaining).CopyTo(blockData.Span.Slice(blockPosition));
|
||||
blockPosition += remaining;
|
||||
writeTotal += remaining;
|
||||
dLen -= remaining;
|
||||
|
||||
finishBlock();
|
||||
startNewBlock();
|
||||
if (update)
|
||||
{
|
||||
blocksWritten += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
class PsvImgStream : Stream
|
||||
{
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool CanRead { get; }
|
||||
public override bool CanSeek { get; }
|
||||
public override bool CanWrite { get; }
|
||||
public override long Length { get; }
|
||||
public override long Position { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
class PsvmdBuilder
|
||||
{
|
||||
|
||||
public static void CreatePsvmd(Stream OutputStream, Stream EncryptedPsvimg, long ContentSize, string BackupType,
|
||||
byte[] Key)
|
||||
{
|
||||
Span<byte> iv = new byte[PSVIMGConstants.AES_BLOCK_SIZE];
|
||||
EncryptedPsvimg.Seek(0, SeekOrigin.Begin);
|
||||
EncryptedPsvimg.Read(iv);
|
||||
iv = AesHelper.AesEcbDecrypt(iv.ToArray(), Key);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
{
|
||||
static class Utils
|
||||
{
|
||||
public static SceDateTime ToSceDateTime(this DateTime dateTime)
|
||||
{
|
||||
var sceDateTime = new SceDateTime();
|
||||
sceDateTime.Year = (ushort)dateTime.Year;
|
||||
sceDateTime.Month = (ushort)dateTime.Month;
|
||||
sceDateTime.Day = (ushort)dateTime.Day;
|
||||
sceDateTime.Hour = (ushort)dateTime.Hour;
|
||||
sceDateTime.Minute = (ushort)dateTime.Minute;
|
||||
sceDateTime.Second = (ushort)dateTime.Second;
|
||||
sceDateTime.Microsecond = (uint)dateTime.Millisecond * 1000;
|
||||
return sceDateTime;
|
||||
}
|
||||
|
||||
public static SceIoStat ToSceIoStat(string path)
|
||||
{
|
||||
var stats = new SceIoStat();
|
||||
var attributes = File.GetAttributes(path);
|
||||
|
||||
if (attributes.HasFlag(FileAttributes.Directory))
|
||||
{
|
||||
stats.Mode |= SceIoStat.Modes.Directory;
|
||||
stats.Size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats.Mode |= SceIoStat.Modes.File;
|
||||
stats.Size = (ulong)(new FileInfo(path).Length);
|
||||
}
|
||||
|
||||
if (attributes.HasFlag(FileAttributes.ReadOnly))
|
||||
{
|
||||
stats.Mode |= SceIoStat.Modes.GroupRead;
|
||||
stats.Mode |= SceIoStat.Modes.OthersRead;
|
||||
stats.Mode |= SceIoStat.Modes.UserRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats.Mode |= SceIoStat.Modes.GroupRead;
|
||||
stats.Mode |= SceIoStat.Modes.GroupWrite;
|
||||
stats.Mode |= SceIoStat.Modes.OthersRead;
|
||||
stats.Mode |= SceIoStat.Modes.OthersWrite;
|
||||
stats.Mode |= SceIoStat.Modes.UserRead;
|
||||
stats.Mode |= SceIoStat.Modes.UserWrite;
|
||||
}
|
||||
|
||||
stats.CreationTime = File.GetCreationTimeUtc(path).ToSceDateTime();
|
||||
stats.AccessTime = File.GetLastAccessTimeUtc(path).ToSceDateTime();
|
||||
stats.ModificaionTime = File.GetLastWriteTimeUtc(path).ToSceDateTime();
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,516 @@
|
|||
using Li.Progress;
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using static Vita.PsvImgTools.SceIoStat;
|
||||
|
||||
namespace Vita.PsvImgTools
|
||||
{
|
||||
public class PSVIMGBuilder : ProgressTracker
|
||||
{
|
||||
private const Int64 BUFFER_SZ = 0x33554432;
|
||||
private byte[] IV = new byte[0x10];
|
||||
private byte[] KEY;
|
||||
private Random rnd = new Random();
|
||||
private Stream mainStream;
|
||||
private Sha256Digest shaCtx;
|
||||
private byte[] blockData;
|
||||
private MemoryStream blockStream;
|
||||
private long contentSize = 0;
|
||||
|
||||
//async
|
||||
private int blocksWritten = 0;
|
||||
private bool finished = false;
|
||||
|
||||
public Int64 ContentSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return contentSize;
|
||||
}
|
||||
}
|
||||
public Int32 BlocksWritten
|
||||
{
|
||||
get
|
||||
{
|
||||
return blocksWritten;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean HasFinished
|
||||
{
|
||||
get
|
||||
{
|
||||
return finished;
|
||||
}
|
||||
}
|
||||
|
||||
//Footer
|
||||
private long totalBytes = 0;
|
||||
|
||||
private byte[] aes_cbc_encrypt(byte[] plainText, byte[] IV, byte[] KEY, int size=-1)
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
size = plainText.Length;
|
||||
}
|
||||
|
||||
MemoryStream ms = new MemoryStream();
|
||||
/* DEBUG Disable Encryption
|
||||
ms.Write(plainText, 0x00, size);
|
||||
ms.Seek(0x00,SeekOrigin.Begin);
|
||||
return ms.ToArray();*/
|
||||
|
||||
Aes alg = Aes.Create();
|
||||
alg.Mode = CipherMode.CBC;
|
||||
alg.Padding = PaddingMode.None;
|
||||
alg.KeySize = 256;
|
||||
alg.BlockSize = 128;
|
||||
alg.Key = KEY;
|
||||
alg.IV = IV;
|
||||
CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
|
||||
cs.Write(plainText, 0, size);
|
||||
cs.Close();
|
||||
byte[] cipherText = ms.ToArray();
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
private byte[] aes_ecb_encrypt(byte[] plainText, byte[] KEY, int size = -1)
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
size = plainText.Length;
|
||||
}
|
||||
|
||||
MemoryStream ms = new MemoryStream();
|
||||
/* DEBUG Disable Encryption
|
||||
ms.Write(plainText, 0x00, size);
|
||||
ms.Seek(0x00,SeekOrigin.Begin);
|
||||
return ms.ToArray();*/
|
||||
|
||||
Aes alg = Aes.Create();
|
||||
alg.Mode = CipherMode.ECB;
|
||||
alg.Padding = PaddingMode.None;
|
||||
alg.KeySize = 256;
|
||||
alg.BlockSize = 128;
|
||||
alg.Key = KEY;
|
||||
CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
|
||||
cs.Write(plainText, 0, size);
|
||||
cs.Close();
|
||||
byte[] cipherText = ms.ToArray();
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
// TODO: Switch to Li.Utilities.StreamUtil for this .
|
||||
private void writeUInt64(Stream dst,UInt64 value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x8);
|
||||
}
|
||||
private void writeInt64(Stream dst,Int64 value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x8);
|
||||
}
|
||||
private void writeUInt16(Stream dst, UInt16 value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x2);
|
||||
}
|
||||
private void writeInt16(Stream dst, Int16 value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x2);
|
||||
}
|
||||
|
||||
private void writeInt32(Stream dst, Int32 value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x4);
|
||||
}
|
||||
private void writeUInt32(Stream dst, UInt32 value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x4);
|
||||
}
|
||||
|
||||
internal virtual SceDateTime dateTimeToSceDateTime(DateTime dt)
|
||||
{
|
||||
SceDateTime sdt = new SceDateTime();
|
||||
sdt.Day = Convert.ToUInt16(dt.Day);
|
||||
sdt.Month = Convert.ToUInt16(dt.Month);
|
||||
sdt.Year = Convert.ToUInt16(dt.Year);
|
||||
|
||||
|
||||
sdt.Hour = Convert.ToUInt16(dt.Hour);
|
||||
sdt.Minute = Convert.ToUInt16(dt.Minute);
|
||||
sdt.Second = Convert.ToUInt16(dt.Second);
|
||||
sdt.Microsecond = Convert.ToUInt32(dt.Millisecond * 1000);
|
||||
return sdt;
|
||||
}
|
||||
|
||||
internal virtual SceIoStat sceIoStat(Stream str)
|
||||
{
|
||||
SceIoStat stats = new SceIoStat();
|
||||
|
||||
// streams being a directory doesnt really make sense ..
|
||||
stats.Mode |= Modes.File;
|
||||
|
||||
// set size..
|
||||
stats.Size = Convert.ToUInt64(str.Length);
|
||||
|
||||
// fake the rest--
|
||||
stats.Mode |= Modes.GroupRead;
|
||||
stats.Mode |= Modes.GroupWrite;
|
||||
|
||||
stats.Mode |= Modes.OthersRead;
|
||||
stats.Mode |= Modes.OthersWrite;
|
||||
|
||||
stats.Mode |= Modes.UserRead;
|
||||
stats.Mode |= Modes.UserWrite;
|
||||
|
||||
stats.CreationTime = dateTimeToSceDateTime(DateTime.Now);
|
||||
stats.AccessTime = dateTimeToSceDateTime(DateTime.Now);
|
||||
stats.ModificaionTime = dateTimeToSceDateTime(DateTime.Now);
|
||||
|
||||
return stats;
|
||||
}
|
||||
internal virtual SceIoStat sceIoStat(string path)
|
||||
{
|
||||
SceIoStat stats = new SceIoStat();
|
||||
FileAttributes attrbutes = File.GetAttributes(path);
|
||||
|
||||
if (attrbutes.HasFlag(FileAttributes.Directory))
|
||||
{
|
||||
stats.Mode |= Modes.Directory;
|
||||
stats.Size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats.Mode |= Modes.File;
|
||||
stats.Size = Convert.ToUInt64(new FileInfo(path).Length);
|
||||
}
|
||||
|
||||
if(attrbutes.HasFlag(FileAttributes.ReadOnly))
|
||||
{
|
||||
stats.Mode |= Modes.GroupRead;
|
||||
|
||||
stats.Mode |= Modes.OthersRead;
|
||||
|
||||
stats.Mode |= Modes.UserRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats.Mode |= Modes.GroupRead;
|
||||
stats.Mode |= Modes.GroupWrite;
|
||||
|
||||
stats.Mode |= Modes.OthersRead;
|
||||
stats.Mode |= Modes.OthersWrite;
|
||||
|
||||
stats.Mode |= Modes.UserRead;
|
||||
stats.Mode |= Modes.UserWrite;
|
||||
}
|
||||
|
||||
stats.CreationTime = dateTimeToSceDateTime(File.GetCreationTimeUtc(path));
|
||||
stats.AccessTime = dateTimeToSceDateTime(File.GetLastAccessTimeUtc(path));
|
||||
stats.ModificaionTime = dateTimeToSceDateTime(File.GetLastWriteTimeUtc(path));
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
internal virtual void writeSceDateTime(Stream dst,SceDateTime time)
|
||||
{
|
||||
writeUInt16(dst, time.Year);
|
||||
writeUInt16(dst, time.Month);
|
||||
writeUInt16(dst, time.Day);
|
||||
|
||||
writeUInt16(dst, time.Hour);
|
||||
writeUInt16(dst, time.Minute);
|
||||
writeUInt16(dst, time.Second);
|
||||
writeUInt32(dst, time.Microsecond);
|
||||
}
|
||||
|
||||
internal virtual void writeSceIoStat(Stream dst, SceIoStat stats)
|
||||
{
|
||||
writeUInt32(dst, Convert.ToUInt32(stats.Mode));
|
||||
writeUInt32(dst, Convert.ToUInt32(stats.Attributes));
|
||||
writeUInt64(dst, stats.Size);
|
||||
writeSceDateTime(dst, stats.CreationTime);
|
||||
writeSceDateTime(dst, stats.AccessTime);
|
||||
writeSceDateTime(dst, stats.ModificaionTime);
|
||||
foreach(UInt32 i in stats.Private)
|
||||
{
|
||||
writeUInt32(dst,i);
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void memset(byte[] buf, byte content, long length)
|
||||
{
|
||||
for(int i = 0; i < length; i++)
|
||||
{
|
||||
buf[i] = content;
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void writeStringWithPadding(Stream dst, string str, int padSize, byte padByte = 0x78)
|
||||
{
|
||||
int StrLen = str.Length;
|
||||
if(StrLen > padSize)
|
||||
{
|
||||
StrLen = padSize;
|
||||
}
|
||||
|
||||
int PaddingLen = (padSize - StrLen)-1;
|
||||
writeString(dst, str, StrLen);
|
||||
dst.WriteByte(0x00);
|
||||
writePadding(dst, padByte, PaddingLen);
|
||||
}
|
||||
|
||||
internal virtual void writeString(Stream dst, string str, int len=-1)
|
||||
{
|
||||
if(len < 0)
|
||||
{
|
||||
len = str.Length;
|
||||
}
|
||||
|
||||
byte[] StrBytes = Encoding.UTF8.GetBytes(str);
|
||||
dst.Write(StrBytes, 0x00, len);
|
||||
}
|
||||
|
||||
internal virtual void writePadding(Stream dst, byte paddingByte, long paddingLen)
|
||||
{
|
||||
byte[] paddingData = new byte[paddingLen];
|
||||
memset(paddingData, paddingByte, paddingLen);
|
||||
dst.Write(paddingData, 0x00, paddingData.Length);
|
||||
}
|
||||
internal virtual byte[] getHeader(SceIoStat Stat, string ParentPath, string PathRel)
|
||||
{
|
||||
using (MemoryStream Header = new MemoryStream())
|
||||
{
|
||||
writeInt64(Header, DateTime.UtcNow.Ticks); // SysTime
|
||||
writeInt64(Header, 0); // Flags
|
||||
writeSceIoStat(Header, Stat);
|
||||
writeStringWithPadding(Header, ParentPath, 256); // Parent Path
|
||||
writeUInt32(Header, 1); //unk_16C
|
||||
writeStringWithPadding(Header, PathRel, 256); //Relative Path
|
||||
writePadding(Header, 0x78, 904); //'x'
|
||||
writeString(Header, PSVIMGConstants.PSVIMG_HEADER_END); //EndOfHeader
|
||||
Header.Seek(0x00, SeekOrigin.Begin);
|
||||
return Header.ToArray();
|
||||
}
|
||||
}
|
||||
internal virtual byte[] getHeader(string FilePath, string ParentPath, string PathRel)
|
||||
{
|
||||
using (MemoryStream Header = new MemoryStream())
|
||||
{
|
||||
writeInt64(Header, DateTime.UtcNow.Ticks); // SysTime
|
||||
writeInt64(Header, 0); // Flags
|
||||
writeSceIoStat(Header, sceIoStat(FilePath));
|
||||
writeStringWithPadding(Header, ParentPath, 256); // Parent Path
|
||||
writeUInt32(Header, 1); //unk_16C
|
||||
writeStringWithPadding(Header, PathRel, 256); //Relative Path
|
||||
writePadding(Header, 0x78, 904); //'x'
|
||||
writeString(Header, PSVIMGConstants.PSVIMG_HEADER_END); //EndOfHeader
|
||||
Header.Seek(0x00, SeekOrigin.Begin);
|
||||
return Header.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void startNewBlock()
|
||||
{
|
||||
blockData = new byte[PSVIMGConstants.FULL_PSVIMG_SIZE];
|
||||
blockStream = new MemoryStream(blockData, 0x00, PSVIMGConstants.FULL_PSVIMG_SIZE);
|
||||
}
|
||||
|
||||
|
||||
internal virtual byte[] shaBlock(int length = PSVIMGConstants.PSVIMG_BLOCK_SIZE,bool final=false)
|
||||
{
|
||||
byte[] outbytes = new byte[PSVIMGConstants.SHA256_BLOCK_SIZE];
|
||||
shaCtx.BlockUpdate(blockData, 0x00, length);
|
||||
Sha256Digest shaTmp = (Sha256Digest)shaCtx.Copy();
|
||||
shaTmp.DoFinal(outbytes,0x00);
|
||||
return outbytes;
|
||||
}
|
||||
|
||||
internal virtual void finishBlock(bool final = false)
|
||||
{
|
||||
int len = Convert.ToInt32(blockStream.Position);
|
||||
byte[] shaBytes = shaBlock(len, final);
|
||||
blockStream.Write(shaBytes, 0x00, PSVIMGConstants.SHA256_BLOCK_SIZE);
|
||||
len += PSVIMGConstants.SHA256_BLOCK_SIZE;
|
||||
|
||||
//Get next IV
|
||||
byte[] encryptedBlock = aes_cbc_encrypt(blockData, IV, KEY, len);
|
||||
for (int i = 0; i < IV.Length; i++)
|
||||
{
|
||||
int encBlockOffset = (encryptedBlock.Length - IV.Length)+i;
|
||||
IV[i] = encryptedBlock[encBlockOffset];
|
||||
}
|
||||
|
||||
mainStream.Write(encryptedBlock, 0x00, encryptedBlock.Length);
|
||||
totalBytes += encryptedBlock.Length;
|
||||
|
||||
blockStream.Dispose();
|
||||
}
|
||||
|
||||
internal virtual int remainingBlockSize()
|
||||
{
|
||||
return Convert.ToInt32((PSVIMGConstants.PSVIMG_BLOCK_SIZE - blockStream.Position));
|
||||
}
|
||||
|
||||
internal virtual void writeBlock(byte[] data, bool update=false)
|
||||
{
|
||||
long dLen = data.Length;
|
||||
long writeTotal = 0;
|
||||
while (dLen > 0)
|
||||
{
|
||||
int remaining = remainingBlockSize();
|
||||
|
||||
if (dLen > remaining)
|
||||
{
|
||||
byte[] dataRemains = new byte[remaining];
|
||||
Array.Copy(data, writeTotal, dataRemains, 0, remaining);
|
||||
blockStream.Write(dataRemains, 0x00, remaining);
|
||||
|
||||
writeTotal += remaining;
|
||||
dLen -= remaining;
|
||||
|
||||
|
||||
finishBlock();
|
||||
startNewBlock();
|
||||
if (update)
|
||||
blocksWritten += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] dataRemains = new byte[dLen];
|
||||
Array.Copy(data, writeTotal, dataRemains, 0, dLen);
|
||||
blockStream.Write(dataRemains, 0x00, Convert.ToInt32(dLen));
|
||||
|
||||
writeTotal += dLen;
|
||||
dLen -= dLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal virtual byte[] getPadding(long size)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
long paddingSize = PSVIMGPadding.GetPadding(size);
|
||||
if(paddingSize != 0)
|
||||
{
|
||||
writePadding(ms, 0x2B, paddingSize-PSVIMGConstants.PSVIMG_PADDING_END.Length);
|
||||
writeString(ms, PSVIMGConstants.PSVIMG_PADDING_END);
|
||||
}
|
||||
ms.Seek(0x00, SeekOrigin.Begin);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual byte[] getTailer()
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
writeUInt64(ms, 0x00);
|
||||
writePadding(ms, 0x7a, 1004);
|
||||
writeString(ms, PSVIMGConstants.PSVIMG_TAILOR_END);
|
||||
|
||||
ms.Seek(0x00, SeekOrigin.Begin);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void writeStream(Stream dst)
|
||||
{
|
||||
while(dst.Position < dst.Length)
|
||||
{
|
||||
byte[] work_buf;
|
||||
Int64 bytes_remain = (dst.Length - dst.Position);
|
||||
if (bytes_remain > BUFFER_SZ)
|
||||
work_buf = new byte[BUFFER_SZ];
|
||||
else
|
||||
work_buf = new byte[bytes_remain];
|
||||
dst.Read(work_buf, 0x00, work_buf.Length);
|
||||
writeBlock(work_buf, true);
|
||||
|
||||
updateProgress(Convert.ToInt32(dst.Position), Convert.ToInt32(dst.Length), "PSVIMG Creation");
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getFooter()
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
totalBytes += 0x10; //number of bytes used by this footer.
|
||||
|
||||
writeInt32(ms, 0x00); // int padding (idk wht this is)
|
||||
writeUInt32(ms, 0x00);
|
||||
writeInt64(ms, totalBytes);
|
||||
ms.Seek(0x00, SeekOrigin.Begin);
|
||||
return aes_cbc_encrypt(ms.ToArray(), IV, KEY);
|
||||
}
|
||||
|
||||
}
|
||||
public void AddFile(Stream sData, string ParentPath, string PathRel)
|
||||
{
|
||||
SceIoStat stat = sceIoStat(sData);
|
||||
long sz = Convert.ToInt64(stat.Size);
|
||||
writeBlock(getHeader(stat, ParentPath, PathRel));
|
||||
writeStream(sData);
|
||||
writeBlock(getPadding(sz));
|
||||
writeBlock(getTailer());
|
||||
contentSize += sz;
|
||||
finished = true;
|
||||
}
|
||||
public void AddFile(string FilePath, string ParentPath, string PathRel)
|
||||
{
|
||||
|
||||
long sz = Convert.ToInt64(sceIoStat(FilePath).Size);
|
||||
writeBlock(getHeader(FilePath, ParentPath, PathRel));
|
||||
using (FileStream fs = File.OpenRead(FilePath))
|
||||
writeStream(fs);
|
||||
writeBlock(getPadding(sz));
|
||||
writeBlock(getTailer());
|
||||
contentSize += sz;
|
||||
finished = true;
|
||||
}
|
||||
|
||||
public void AddDir(string DirPath, string ParentPath, string PathRel)
|
||||
{
|
||||
writeBlock(getHeader(DirPath, ParentPath, PathRel));
|
||||
writeBlock(getPadding(0));
|
||||
writeBlock(getTailer());
|
||||
}
|
||||
public long Finish()
|
||||
{
|
||||
finishBlock(true);
|
||||
byte[] footer = getFooter();
|
||||
mainStream.Write(footer, 0x00, footer.Length);
|
||||
|
||||
blockStream.Dispose();
|
||||
mainStream.Dispose();
|
||||
return contentSize;
|
||||
}
|
||||
|
||||
|
||||
public PSVIMGBuilder(Stream dst, byte[] Key)
|
||||
{
|
||||
totalBytes = 0;
|
||||
contentSize = 0;
|
||||
shaCtx = new Sha256Digest();
|
||||
mainStream = dst;
|
||||
KEY = Key;
|
||||
|
||||
rnd.NextBytes(IV);
|
||||
IV = aes_ecb_encrypt(IV, Key);
|
||||
|
||||
mainStream.Write(IV, 0x00, IV.Length);
|
||||
totalBytes += IV.Length;
|
||||
|
||||
startNewBlock();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,314 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using static Vita.PsvImgTools.SceIoStat;
|
||||
|
||||
namespace Vita.PsvImgTools
|
||||
{
|
||||
class PSVIMGFileStream : Stream
|
||||
{
|
||||
|
||||
long length = 0;
|
||||
long startPos = 0;
|
||||
long endPos = 0;
|
||||
long position = 0;
|
||||
|
||||
PSVIMGStream psvStream;
|
||||
public PSVIMGFileStream(PSVIMGStream psv, string Path)
|
||||
{
|
||||
psvStream = psv;
|
||||
findFile(Path);
|
||||
}
|
||||
|
||||
public void WriteToFile(string FilePath)
|
||||
{
|
||||
using (FileStream fs = File.OpenWrite(FilePath))
|
||||
{
|
||||
fs.SetLength(0);
|
||||
int written = 0;
|
||||
long left = length - written;
|
||||
byte[] work_buf;
|
||||
Seek(0x00, SeekOrigin.Begin);
|
||||
while (left > 0)
|
||||
{
|
||||
left = length - written;
|
||||
if (left < 0x10000)
|
||||
{
|
||||
work_buf = new byte[left];
|
||||
}
|
||||
else
|
||||
{
|
||||
work_buf = new byte[0x10000];
|
||||
}
|
||||
Read(work_buf, 0x00, work_buf.Length);
|
||||
fs.Write(work_buf, 0x00, work_buf.Length);
|
||||
written += work_buf.Length;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return position;
|
||||
}
|
||||
set
|
||||
{
|
||||
Seek(value, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
psvStream.Flush();
|
||||
}
|
||||
private int _read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
int read = 0;
|
||||
while (true)
|
||||
{
|
||||
int remainBlock = Convert.ToInt32(psvStream.BlockRemaining - PSVIMGConstants.SHA256_BLOCK_SIZE);
|
||||
int remainRead = count - read;
|
||||
|
||||
if (remainRead < remainBlock)
|
||||
{
|
||||
byte[] tmp = new byte[remainRead];
|
||||
psvStream.Read(tmp, 0x00, remainRead);
|
||||
ms.Write(tmp, 0x00, tmp.Length);
|
||||
read += remainRead;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] tmp = new byte[remainBlock];
|
||||
psvStream.Read(tmp, 0x00, remainBlock);
|
||||
ms.Write(tmp, 0x00, tmp.Length);
|
||||
psvStream.Seek(PSVIMGConstants.SHA256_BLOCK_SIZE, SeekOrigin.Current);
|
||||
read += Convert.ToInt32(remainBlock);
|
||||
}
|
||||
}
|
||||
ms.Seek(0x00, SeekOrigin.Begin);
|
||||
return ms.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private long _seek(long amount, SeekOrigin orig)
|
||||
{
|
||||
long ToSeek = 0;
|
||||
long SeekAdd = 0;
|
||||
long remainBlock = psvStream.BlockRemaining - PSVIMGConstants.SHA256_BLOCK_SIZE;
|
||||
while (true)
|
||||
{
|
||||
long remainSeek = amount - ToSeek;
|
||||
|
||||
if (remainSeek < remainBlock)
|
||||
{
|
||||
ToSeek += remainSeek;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ToSeek += remainBlock;
|
||||
SeekAdd += PSVIMGConstants.SHA256_BLOCK_SIZE;
|
||||
}
|
||||
remainBlock = PSVIMGConstants.PSVIMG_BLOCK_SIZE;
|
||||
}
|
||||
ToSeek += SeekAdd;
|
||||
return psvStream.Seek(ToSeek, orig);
|
||||
}
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (startPos + count > endPos)
|
||||
{
|
||||
count = Convert.ToInt32(endPos);
|
||||
}
|
||||
return _read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
if (origin == SeekOrigin.Begin)
|
||||
{
|
||||
if (offset <= endPos)
|
||||
{
|
||||
psvStream.Seek(startPos, SeekOrigin.Begin);
|
||||
_seek(offset, SeekOrigin.Current);
|
||||
position = offset;
|
||||
return position;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IndexOutOfRangeException("Offset is out of range of file");
|
||||
}
|
||||
|
||||
}
|
||||
else if (origin == SeekOrigin.Current)
|
||||
{
|
||||
if (offset <= endPos)
|
||||
{
|
||||
_seek(offset, SeekOrigin.Current);
|
||||
position += offset;
|
||||
return position;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IndexOutOfRangeException("Offset is out of range of file");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
long realOffset = endPos + offset;
|
||||
if (realOffset <= endPos)
|
||||
{
|
||||
_seek(realOffset, SeekOrigin.Begin);
|
||||
return Position;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IndexOutOfRangeException("Offset is out of range of file");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException("PSVFileStream is Read-Only");
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException("PSVFileStream is Read-Only");
|
||||
}
|
||||
|
||||
private ushort readUInt16()
|
||||
{
|
||||
byte[] intBuf = new byte[0x2];
|
||||
_read(intBuf, 0x00, 0x02);
|
||||
return BitConverter.ToUInt16(intBuf, 0);
|
||||
}
|
||||
private uint readUInt32()
|
||||
{
|
||||
byte[] intBuf = new byte[0x4];
|
||||
_read(intBuf, 0x00, 0x04);
|
||||
return BitConverter.ToUInt32(intBuf, 0);
|
||||
}
|
||||
private ulong readUInt64()
|
||||
{
|
||||
byte[] intBuf = new byte[0x8];
|
||||
_read(intBuf, 0x00, 0x08);
|
||||
return BitConverter.ToUInt64(intBuf, 0);
|
||||
}
|
||||
private SceDateTime readDatetime()
|
||||
{
|
||||
SceDateTime dateTime = new SceDateTime();
|
||||
dateTime.Year = readUInt16();
|
||||
dateTime.Month = readUInt16();
|
||||
dateTime.Day = readUInt16();
|
||||
dateTime.Hour = readUInt16();
|
||||
dateTime.Minute = readUInt16();
|
||||
dateTime.Second = readUInt16();
|
||||
dateTime.Microsecond = readUInt32();
|
||||
return dateTime;
|
||||
}
|
||||
private SceIoStat readStats()
|
||||
{
|
||||
SceIoStat stat = new SceIoStat();
|
||||
stat.Mode = (Modes)readUInt32();
|
||||
stat.Attributes = (AttributesEnum)readUInt32();
|
||||
stat.Size = readUInt64();
|
||||
stat.CreationTime = readDatetime();
|
||||
stat.AccessTime = readDatetime();
|
||||
stat.ModificaionTime = readDatetime();
|
||||
for (int i = 0; i < stat.Private.Length; i++)
|
||||
{
|
||||
stat.Private[i] = readUInt32();
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
private PsvImgHeader readHeader()
|
||||
{
|
||||
PsvImgHeader header = new PsvImgHeader();
|
||||
header.SysTime = readUInt64();
|
||||
header.Flags = readUInt64();
|
||||
header.Statistics = readStats();
|
||||
_read(header.bParentPath, 0x00, 256);
|
||||
header.unk_16C = readUInt32();
|
||||
_read(header.bPath, 0x00, 256);
|
||||
_read(header.Padding, 0x00, 904);
|
||||
_read(header.bEnd, 0x00, 12);
|
||||
return header;
|
||||
}
|
||||
|
||||
private PsvImgTailer readTailer()
|
||||
{
|
||||
PsvImgTailer tailer = new PsvImgTailer();
|
||||
tailer.Flags = readUInt64();
|
||||
_read(tailer.Padding, 0x00, 1004);
|
||||
_read(tailer.bEnd, 0x00, 12);
|
||||
return tailer;
|
||||
}
|
||||
private void findFile(string path)
|
||||
{
|
||||
_seek(0x00, SeekOrigin.Begin);
|
||||
while (psvStream.Position < psvStream.Length)
|
||||
{
|
||||
PsvImgHeader header = readHeader();
|
||||
long size = (long)header.Statistics.Size;
|
||||
long padding = PSVIMGPadding.GetPadding(size);
|
||||
|
||||
if (header.Path == path)
|
||||
{
|
||||
length = size;
|
||||
startPos = psvStream.Position;
|
||||
endPos = startPos + length;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_seek(size + padding, SeekOrigin.Current);
|
||||
PsvImgTailer tailer = readTailer();
|
||||
}
|
||||
|
||||
}
|
||||
throw new FileNotFoundException("Cannot find file specified");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,367 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
|
||||
namespace Vita.PsvImgTools
|
||||
{
|
||||
class PSVIMGStream : Stream
|
||||
{
|
||||
private Stream baseStream;
|
||||
private MemoryStream blockStream;
|
||||
private byte[] key;
|
||||
public Stream BaseStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Key
|
||||
{
|
||||
get
|
||||
{
|
||||
return key;
|
||||
}
|
||||
set
|
||||
{
|
||||
key = value;
|
||||
}
|
||||
}
|
||||
|
||||
public long BlockNo
|
||||
{
|
||||
get
|
||||
{
|
||||
return getBlockIndex();
|
||||
}
|
||||
set
|
||||
{
|
||||
seekToBlock(value);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
public long BlockRemaining
|
||||
{
|
||||
get
|
||||
{
|
||||
return getRemainingBlock();
|
||||
}
|
||||
}
|
||||
|
||||
public long BlockPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return blockStream.Position;
|
||||
}
|
||||
set
|
||||
{
|
||||
blockStream.Seek(value, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.Length - PSVIMGConstants.AES_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.Position - PSVIMGConstants.AES_BLOCK_SIZE;
|
||||
}
|
||||
set
|
||||
{
|
||||
Seek(value, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public PSVIMGStream(Stream file, byte[] KEY)
|
||||
{
|
||||
baseStream = file;
|
||||
key = KEY;
|
||||
if (!verifyFooter())
|
||||
{
|
||||
throw new Exception("Invalid KEY!");
|
||||
}
|
||||
blockStream = new MemoryStream();
|
||||
baseStream.Seek(PSVIMGConstants.AES_BLOCK_SIZE, SeekOrigin.Begin);
|
||||
update();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int remaining = (int)getRemainingBlock();
|
||||
int read = 0;
|
||||
|
||||
if (count < remaining)
|
||||
{
|
||||
read += blockStream.Read(buffer, offset, count);
|
||||
baseStream.Seek(count, SeekOrigin.Current);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
update();
|
||||
remaining = (int)getRemainingBlock();
|
||||
int curPos = count - read;
|
||||
|
||||
if (curPos > remaining)
|
||||
{
|
||||
read += remaining;
|
||||
blockStream.CopyTo(ms, remaining);
|
||||
baseStream.Seek(remaining, SeekOrigin.Current);
|
||||
}
|
||||
else
|
||||
{
|
||||
read += curPos;
|
||||
blockStream.CopyTo(ms, curPos);
|
||||
baseStream.Seek(curPos, SeekOrigin.Current);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
ms.Seek(0x00, SeekOrigin.Begin);
|
||||
ms.Read(buffer, offset, count);
|
||||
}
|
||||
}
|
||||
return read;
|
||||
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
update();
|
||||
baseStream.Flush();
|
||||
blockStream.Flush();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
long ret = 0;
|
||||
if (origin == SeekOrigin.Begin)
|
||||
{
|
||||
ret = baseStream.Seek(offset + PSVIMGConstants.AES_BLOCK_SIZE, SeekOrigin.Begin);
|
||||
}
|
||||
else if (origin == SeekOrigin.Current)
|
||||
{
|
||||
long pos = baseStream.Position;
|
||||
if (pos + offset >= PSVIMGConstants.AES_BLOCK_SIZE)
|
||||
{
|
||||
ret = baseStream.Seek(offset, SeekOrigin.Current);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = baseStream.Seek(offset + PSVIMGConstants.AES_BLOCK_SIZE, SeekOrigin.Current);
|
||||
}
|
||||
}
|
||||
else if (origin == SeekOrigin.End)
|
||||
{
|
||||
long pos = baseStream.Length;
|
||||
if (pos + offset >= PSVIMGConstants.AES_BLOCK_SIZE)
|
||||
{
|
||||
ret = baseStream.Seek(offset, SeekOrigin.End);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = baseStream.Seek(offset + PSVIMGConstants.AES_BLOCK_SIZE, SeekOrigin.End);
|
||||
}
|
||||
}
|
||||
update();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException("PSVIMGStream is Read-Only");
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException("PSVIMGStream is Read-Only");
|
||||
}
|
||||
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
blockStream.Close();
|
||||
blockStream.Dispose();
|
||||
baseStream.Close();
|
||||
baseStream.Dispose();
|
||||
Dispose();
|
||||
}
|
||||
private void update()
|
||||
{
|
||||
long offset = Position % PSVIMGConstants.FULL_PSVIMG_SIZE;
|
||||
long blockIndex = getBlockIndex();
|
||||
byte[] decryptedBlock = getBlock(blockIndex);
|
||||
blockStream.Seek(0x00, SeekOrigin.Begin);
|
||||
blockStream.SetLength(decryptedBlock.Length);
|
||||
blockStream.Write(decryptedBlock, 0x00, decryptedBlock.Length);
|
||||
seekToBlock(blockIndex);
|
||||
baseStream.Seek(offset, SeekOrigin.Current);
|
||||
blockStream.Seek(offset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
private long getBlockIndex()
|
||||
{
|
||||
long i = 0;
|
||||
long curPos = baseStream.Position;
|
||||
long fullBlock;
|
||||
long blockOffset;
|
||||
|
||||
while (true)
|
||||
{
|
||||
blockOffset = i * PSVIMGConstants.FULL_PSVIMG_SIZE + PSVIMGConstants.AES_BLOCK_SIZE;
|
||||
long remaining = getRemainingBase();
|
||||
if (remaining < PSVIMGConstants.FULL_PSVIMG_SIZE)
|
||||
{
|
||||
fullBlock = blockOffset + remaining;
|
||||
}
|
||||
else
|
||||
{
|
||||
fullBlock = blockOffset + PSVIMGConstants.FULL_PSVIMG_SIZE;
|
||||
}
|
||||
if (curPos >= blockOffset && curPos < fullBlock)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (blockOffset > baseStream.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
|
||||
|
||||
}
|
||||
private long getRemainingBase()
|
||||
{
|
||||
return baseStream.Length - baseStream.Position;
|
||||
}
|
||||
private long getRemainingBlock()
|
||||
{
|
||||
return blockStream.Length - blockStream.Position;
|
||||
}
|
||||
private byte[] getIV(long blockindex)
|
||||
{
|
||||
byte[] iv = new byte[0x10];
|
||||
seekToBlock(blockindex);
|
||||
baseStream.Seek(baseStream.Position - PSVIMGConstants.AES_BLOCK_SIZE, SeekOrigin.Begin);
|
||||
baseStream.Read(iv, 0x00, iv.Length);
|
||||
return iv;
|
||||
}
|
||||
private byte[] aes_cbc_decrypt(byte[] cipherData, byte[] IV)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
Aes alg = Aes.Create();
|
||||
alg.Mode = CipherMode.CBC;
|
||||
alg.Padding = PaddingMode.None;
|
||||
alg.KeySize = 256;
|
||||
alg.BlockSize = 128;
|
||||
alg.Key = key;
|
||||
alg.IV = IV;
|
||||
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
|
||||
cs.Write(cipherData, 0, cipherData.Length);
|
||||
cs.Close();
|
||||
byte[] decryptedData = ms.ToArray();
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
private void seekToBlock(long blockIndex)
|
||||
{
|
||||
long blockOffset;
|
||||
blockOffset = blockIndex * PSVIMGConstants.FULL_PSVIMG_SIZE + PSVIMGConstants.AES_BLOCK_SIZE;
|
||||
|
||||
if (blockOffset > baseStream.Length)
|
||||
{
|
||||
blockOffset = baseStream.Length;
|
||||
}
|
||||
|
||||
baseStream.Seek(blockOffset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
private bool verifyFooter()
|
||||
{
|
||||
byte[] Footer = new byte[0x10];
|
||||
byte[] IV = new byte[PSVIMGConstants.AES_BLOCK_SIZE];
|
||||
|
||||
baseStream.Seek(baseStream.Length - (Footer.Length + IV.Length), SeekOrigin.Begin);
|
||||
baseStream.Read(IV, 0x00, PSVIMGConstants.AES_BLOCK_SIZE);
|
||||
baseStream.Read(Footer, 0x00, 0x10);
|
||||
|
||||
byte[] FooterDec = aes_cbc_decrypt(Footer, IV);
|
||||
ulong FooterLen;
|
||||
using (MemoryStream ms = new MemoryStream(FooterDec))
|
||||
{
|
||||
ms.Seek(0x4, SeekOrigin.Current);
|
||||
ms.Seek(0x4, SeekOrigin.Current);
|
||||
byte[] LenInt = new byte[0x8];
|
||||
ms.Read(LenInt, 0x00, 0x8);
|
||||
FooterLen = BitConverter.ToUInt64(LenInt, 0x00);
|
||||
}
|
||||
if (Convert.ToUInt64(baseStream.Length) == FooterLen)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getBlock(long blockIndex)
|
||||
{
|
||||
byte[] iv = getIV(blockIndex);
|
||||
long remaining = getRemainingBase();
|
||||
byte[] encryptedBlock;
|
||||
if (PSVIMGConstants.FULL_PSVIMG_SIZE < remaining)
|
||||
{
|
||||
encryptedBlock = new byte[PSVIMGConstants.FULL_PSVIMG_SIZE];
|
||||
}
|
||||
else
|
||||
{
|
||||
encryptedBlock = new byte[remaining];
|
||||
}
|
||||
|
||||
|
||||
baseStream.Read(encryptedBlock, 0x00, encryptedBlock.Length);
|
||||
byte[] decryptedBlock = aes_cbc_decrypt(encryptedBlock, iv);
|
||||
return decryptedBlock;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace PsvImage
|
||||
namespace Vita.PsvImgTools
|
||||
{
|
||||
|
||||
internal class PSVIMGConstants
|
||||
{
|
||||
public const int AES_BLOCK_SIZE = 0x10;
|
||||
|
@ -19,8 +17,27 @@ namespace PsvImage
|
|||
public const int FULL_PSVIMG_SIZE = PSVIMG_BLOCK_SIZE + SHA256_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct SceDateTime
|
||||
internal class StringReader
|
||||
{
|
||||
internal static string ReadUntilTerminator(byte[] StringBytes)
|
||||
{
|
||||
string str = "";
|
||||
foreach (byte sByte in StringBytes)
|
||||
{
|
||||
if (sByte != 0x00)
|
||||
{
|
||||
str += (char)sByte;
|
||||
}
|
||||
else
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
internal class SceDateTime
|
||||
{
|
||||
public ushort Year;
|
||||
public ushort Month;
|
||||
|
@ -29,13 +46,15 @@ namespace PsvImage
|
|||
public ushort Minute;
|
||||
public ushort Second;
|
||||
public uint Microsecond;
|
||||
|
||||
public SceDateTime()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct SceIoStat
|
||||
internal class SceIoStat
|
||||
{
|
||||
[Flags]
|
||||
public enum Modes
|
||||
{
|
||||
/** Format bits mask */
|
||||
|
@ -103,7 +122,7 @@ namespace PsvImage
|
|||
public Modes Mode;
|
||||
public AttributesEnum Attributes;
|
||||
/** Size of the file in bytes. */
|
||||
public UInt64 Size;
|
||||
public ulong Size;
|
||||
/** Creation time. */
|
||||
public SceDateTime CreationTime;
|
||||
/** Access time. */
|
||||
|
@ -111,26 +130,38 @@ namespace PsvImage
|
|||
/** Modification time. */
|
||||
public SceDateTime ModificaionTime;
|
||||
/** Device-specific data. */
|
||||
public fixed uint Private[6];
|
||||
}
|
||||
public uint[] Private = new uint[6];
|
||||
public SceIoStat()
|
||||
{
|
||||
for (int i = 0; i < Private.Length; i++)
|
||||
{
|
||||
Private[i] = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct PsvImgTailer
|
||||
internal class PsvImgTailer
|
||||
{
|
||||
|
||||
public ulong Flags;
|
||||
public fixed byte Padding[1004];
|
||||
public fixed byte bEnd[12];
|
||||
}
|
||||
public byte[] Padding = new byte[1004];
|
||||
public byte[] bEnd = new byte[12];
|
||||
|
||||
public string End
|
||||
{
|
||||
get
|
||||
{
|
||||
return StringReader.ReadUntilTerminator(bEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
internal class PSVIMGPadding
|
||||
{
|
||||
public static long GetPadding(long size)
|
||||
{
|
||||
long padding;
|
||||
if ((size & (PSVIMGConstants.PSVIMG_ENTRY_ALIGN - 1)) >= 1)
|
||||
if ((size & PSVIMGConstants.PSVIMG_ENTRY_ALIGN - 1) >= 1)
|
||||
{
|
||||
padding = (PSVIMGConstants.PSVIMG_ENTRY_ALIGN - (size & (PSVIMGConstants.PSVIMG_ENTRY_ALIGN - 1)));
|
||||
padding = PSVIMGConstants.PSVIMG_ENTRY_ALIGN - (size & PSVIMGConstants.PSVIMG_ENTRY_ALIGN - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -140,15 +171,43 @@ namespace PsvImage
|
|||
}
|
||||
}
|
||||
|
||||
internal unsafe struct PsvImgHeader
|
||||
internal class PsvImgHeader
|
||||
{
|
||||
public ulong SysTime;
|
||||
public ulong Flags;
|
||||
public SceIoStat Statistics;
|
||||
public fixed byte bParentPath[256];
|
||||
public uint unk_16C;
|
||||
public fixed byte bPath[256];
|
||||
public fixed byte Padding[904];
|
||||
public fixed byte bEnd[12];
|
||||
public byte[] bParentPath = new byte[256];
|
||||
public uint unk_16C; // set to 1
|
||||
public byte[] bPath = new byte[256];
|
||||
public byte[] Padding = new byte[904];
|
||||
public byte[] bEnd = new byte[12];
|
||||
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
return StringReader.ReadUntilTerminator(bPath);
|
||||
}
|
||||
}
|
||||
|
||||
public string End
|
||||
{
|
||||
get
|
||||
{
|
||||
return StringReader.ReadUntilTerminator(bEnd);
|
||||
}
|
||||
}
|
||||
|
||||
public string ParentPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return StringReader.ReadUntilTerminator(bParentPath);
|
||||
}
|
||||
}
|
||||
public PsvImgHeader()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
using Ionic.Zlib;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
namespace Vita.PsvImgTools
|
||||
{
|
||||
class PSVMDBuilder
|
||||
{
|
||||
private static void memset(byte[] buf, byte content, long length)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
buf[i] = content;
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeUInt64(Stream dst, ulong value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x8);
|
||||
}
|
||||
private static void writeInt64(Stream dst, long value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x8);
|
||||
}
|
||||
private static void writeUInt16(Stream dst, ushort value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x2);
|
||||
}
|
||||
private static void writeInt16(Stream dst, short value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x2);
|
||||
}
|
||||
|
||||
private static void writeInt32(Stream dst, int value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x4);
|
||||
}
|
||||
private static void writeUInt32(Stream dst, uint value)
|
||||
{
|
||||
byte[] ValueBytes = BitConverter.GetBytes(value);
|
||||
dst.Write(ValueBytes, 0x00, 0x4);
|
||||
}
|
||||
|
||||
|
||||
private static void writeString(Stream dst, string str, int len = -1)
|
||||
{
|
||||
if (len < 0)
|
||||
{
|
||||
len = str.Length;
|
||||
}
|
||||
|
||||
byte[] StrBytes = Encoding.UTF8.GetBytes(str);
|
||||
dst.Write(StrBytes, 0x00, len);
|
||||
}
|
||||
|
||||
private static void writePadding(Stream dst, byte paddingByte, long paddingLen)
|
||||
{
|
||||
byte[] paddingData = new byte[paddingLen];
|
||||
memset(paddingData, paddingByte, paddingLen);
|
||||
dst.Write(paddingData, 0x00, paddingData.Length);
|
||||
}
|
||||
private static void writeStringWithPadding(Stream dst, string str, int padSize, byte padByte = 0x78)
|
||||
{
|
||||
int StrLen = str.Length;
|
||||
if (StrLen > padSize)
|
||||
{
|
||||
StrLen = padSize;
|
||||
}
|
||||
|
||||
int PaddingLen = padSize - StrLen - 1;
|
||||
writeString(dst, str, StrLen);
|
||||
dst.WriteByte(0x00);
|
||||
writePadding(dst, padByte, PaddingLen);
|
||||
}
|
||||
|
||||
private static byte[] aes_ecb_decrypt(byte[] cipherText, byte[] KEY, int size = -1)
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
size = cipherText.Length;
|
||||
}
|
||||
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
Aes alg = Aes.Create();
|
||||
alg.Mode = CipherMode.ECB;
|
||||
alg.Padding = PaddingMode.None;
|
||||
alg.KeySize = 256;
|
||||
alg.BlockSize = 128;
|
||||
alg.Key = KEY;
|
||||
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
|
||||
cs.Write(cipherText, 0, size);
|
||||
cs.Close();
|
||||
byte[] plainText = ms.ToArray();
|
||||
return plainText;
|
||||
}
|
||||
|
||||
private static byte[] aes_cbc_decrypt(byte[] cipherData, byte[] IV, byte[] Key)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
Aes alg = Aes.Create();
|
||||
alg.Mode = CipherMode.CBC;
|
||||
alg.Padding = PaddingMode.None;
|
||||
alg.KeySize = 256;
|
||||
alg.BlockSize = 128;
|
||||
alg.Key = Key;
|
||||
alg.IV = IV;
|
||||
CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
|
||||
cs.Write(cipherData, 0, cipherData.Length);
|
||||
cs.Close();
|
||||
byte[] decryptedData = ms.ToArray();
|
||||
return decryptedData;
|
||||
}
|
||||
private static byte[] aes_cbc_encrypt(byte[] plainText, byte[] IV, byte[] KEY, int size = -1)
|
||||
{
|
||||
if (size < 0)
|
||||
{
|
||||
size = plainText.Length;
|
||||
}
|
||||
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
Aes alg = Aes.Create();
|
||||
alg.Mode = CipherMode.CBC;
|
||||
alg.Padding = PaddingMode.None;
|
||||
alg.KeySize = 256;
|
||||
alg.BlockSize = 128;
|
||||
alg.Key = KEY;
|
||||
alg.IV = IV;
|
||||
CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
|
||||
cs.Write(plainText, 0, size);
|
||||
cs.Close();
|
||||
byte[] cipherText = ms.ToArray();
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
|
||||
public static void CreatePsvmd(Stream OutputStream, Stream EncryptedPsvimg, long ContentSize, string BackupType, byte[] Key)
|
||||
{
|
||||
byte[] IV = new byte[PSVIMGConstants.AES_BLOCK_SIZE];
|
||||
EncryptedPsvimg.Seek(0x00, SeekOrigin.Begin);
|
||||
EncryptedPsvimg.Read(IV, 0x00, IV.Length);
|
||||
IV = aes_ecb_decrypt(IV, Key);
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
writeUInt32(ms, 0xFEE1900D); // magic
|
||||
writeUInt32(ms, 0x2); // type
|
||||
writeUInt64(ms, 0x03000000); // fw ver
|
||||
ms.Write(new byte[0x10], 0x00, 0x10); // PSID
|
||||
writeStringWithPadding(ms, BackupType, 0x40, 0x00); //backup type
|
||||
writeInt64(ms, EncryptedPsvimg.Length); // total size
|
||||
writeUInt64(ms, 0x2); //version
|
||||
writeInt64(ms, ContentSize); // content size
|
||||
ms.Write(IV, 0x00, 0x10); // IV
|
||||
writeUInt64(ms, 0x00); //ux0 info
|
||||
writeUInt64(ms, 0x00); //ur0 info
|
||||
writeUInt64(ms, 0x00); //unused 98
|
||||
writeUInt64(ms, 0x00); //unused A0
|
||||
writeUInt32(ms, 0x1); //add data
|
||||
|
||||
ms.Seek(0x00, SeekOrigin.Begin);
|
||||
byte[] psvMd = ms.ToArray();
|
||||
ms.Close();
|
||||
|
||||
ms = new MemoryStream();
|
||||
byte[] psvMdCompressed = ZlibStream.CompressBuffer(psvMd);
|
||||
psvMdCompressed[0x1] = 0x9C;
|
||||
ms.Write(psvMdCompressed, 0x00, psvMdCompressed.Length);
|
||||
|
||||
SHA256 sha = SHA256.Create();
|
||||
byte[] shadata = sha.ComputeHash(psvMdCompressed);
|
||||
ms.Write(shadata, 0x00, shadata.Length);
|
||||
|
||||
int PaddingLen = Convert.ToInt32(PSVIMGConstants.AES_BLOCK_SIZE - (ms.Length & PSVIMGConstants.AES_BLOCK_SIZE - 1));
|
||||
writePadding(ms, 0x00, PaddingLen);
|
||||
|
||||
writeInt32(ms, PaddingLen); //Padding Length
|
||||
writeUInt32(ms, 0x00);
|
||||
writeInt64(ms, ms.Length + 0x8 + IV.Length);
|
||||
ms.Seek(0x00, SeekOrigin.Begin);
|
||||
byte[] toEncrypt = ms.ToArray();
|
||||
ms.Close();
|
||||
|
||||
byte[] EncryptedData = aes_cbc_encrypt(toEncrypt, IV, Key);
|
||||
OutputStream.Write(IV, 0x00, IV.Length);
|
||||
OutputStream.Write(EncryptedData, 0x00, EncryptedData.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
public static byte[] DecryptPsvmd(Stream PsvMdFile, byte[] Key)
|
||||
{
|
||||
byte[] IV = new byte[PSVIMGConstants.AES_BLOCK_SIZE];
|
||||
PsvMdFile.Read(IV, 0x00, IV.Length);
|
||||
byte[] remaining = new byte[PsvMdFile.Length - IV.Length];
|
||||
PsvMdFile.Read(remaining, 0x00, remaining.Length);
|
||||
byte[] zlibCompressed = aes_cbc_decrypt(remaining, IV, Key);
|
||||
return zlibCompressed;
|
||||
// return ZlibStream.UncompressBuffer(zlibCompressed);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.0" />
|
||||
<PackageReference Include="DotNetZip" Version="1.16.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LiGeneralUtilities\LiLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
Reference in New Issue