304 lines
10 KiB
C#
304 lines
10 KiB
C#
using GameBuilder.Pops;
|
|
using PspCrypto;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net.Security;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Li.Utilities;
|
|
|
|
namespace GameBuilder.Psp
|
|
{
|
|
public class NpUmdImg : NpDrmPsar
|
|
{
|
|
const int RATIO_LIMIT = 90;
|
|
const int BLOCK_BASIS = 0x10;
|
|
const int SECTOR_SZ = 2048;
|
|
const int BLOCK_SZ = BLOCK_BASIS * SECTOR_SZ;
|
|
public NpUmdImg(NpDrmInfo drmInfo, UmdInfo umdImage, bool compress) : base(drmInfo)
|
|
{
|
|
this.compress = compress;
|
|
|
|
this.npHdr = new MemoryStream();
|
|
this.npHdrUtil = new StreamUtil(npHdr);
|
|
|
|
this.npHdrBody = new MemoryStream();
|
|
this.npHdrBodyUtil = new StreamUtil(npHdrBody);
|
|
|
|
this.npTbl = new MemoryStream();
|
|
this.npTblUtil = new StreamUtil(npTbl);
|
|
|
|
this.isoData = new MemoryStream();
|
|
this.isoDataUtil = new StreamUtil(isoData);
|
|
|
|
this.headerKey = Rng.RandomBytes(0x10);
|
|
|
|
this.umdImage = umdImage;
|
|
isoBlocks = Convert.ToInt64((umdImage.IsoStream.Length + BLOCK_SZ - 1) / BLOCK_SZ);
|
|
|
|
}
|
|
|
|
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"
|
|
umdImage.DataFiles["PARAM.SFO"] = sfoKeys.WriteSfo();
|
|
}
|
|
private void createNpHdr()
|
|
{
|
|
npHdrUtil.WriteStr("NPUMDIMG");
|
|
npHdrUtil.WriteInt32(DrmInfo.KeyIndex);
|
|
npHdrUtil.WriteInt32(BLOCK_BASIS);
|
|
npHdrUtil.WriteStrWithPadding(DrmInfo.ContentId, 0x00, 0x30);
|
|
|
|
createNpUmdBody();
|
|
byte[] npumdDec = npHdrBody.ToArray();
|
|
byte[] npumdEnc = encryptHeader(npumdDec);
|
|
|
|
npHdrUtil.WriteBytes(npumdEnc);
|
|
npHdrUtil.WriteBytes(this.headerKey);
|
|
npHdrUtil.WriteBytes(this.dataKey);
|
|
|
|
byte[] npumdhdr = npHdr.ToArray();
|
|
byte[] npumdheaderHash = hashBlock(npumdhdr);
|
|
|
|
npHdrUtil.WriteBytes(npumdheaderHash);
|
|
npHdrUtil.WriteBytes(Rng.RandomBytes(0x8)); // padding
|
|
npHdrUtil.PadUntil(0x00, 0x100);
|
|
}
|
|
|
|
public override void CreatePsar()
|
|
{
|
|
PatchSfo();
|
|
createNpUmdTbl();
|
|
byte[] tbl = encryptTable();
|
|
this.dataKey = hashBlock(tbl);
|
|
createNpHdr();
|
|
|
|
byte[] npHdrBuf = npHdr.ToArray();
|
|
ECDsaHelper.SignNpImageHeader(npHdrBuf);
|
|
psarUtil.WriteBytes(npHdrBuf);
|
|
psarUtil.WriteBytes(tbl);
|
|
|
|
copyToProgress(isoData, Psar, "Copy UMD Image to NPUMDIMG");
|
|
}
|
|
|
|
private byte[] signParamSfo(byte[] paramSfo)
|
|
{
|
|
int paramSfoLen = paramSfo.Length;
|
|
byte[] contentIdBytes = Encoding.UTF8.GetBytes(DrmInfo.ContentId);
|
|
Array.Resize(ref paramSfo, paramSfoLen + 0x30);
|
|
Array.ConstrainedCopy(contentIdBytes, 0, paramSfo, paramSfoLen, contentIdBytes.Length);
|
|
byte[] signature = new byte[0x30];
|
|
|
|
ECDsaHelper.SignParamSfo(paramSfo, signature);
|
|
return signature;
|
|
}
|
|
|
|
public override byte[] GenerateDataPsp()
|
|
{
|
|
byte[] startDat = CreateStartDat(umdImage.Minis ? Resources.STARTDATMINIS : Resources.STARTDATPSP);
|
|
using (MemoryStream dataPsp = new MemoryStream())
|
|
{
|
|
StreamUtil dataPspUtil = new StreamUtil(dataPsp);
|
|
byte[] signature = signParamSfo(umdImage.DataFiles["PARAM.SFO"]);
|
|
dataPspUtil.WriteBytes(signature);
|
|
dataPspUtil.WritePadding(0x00, 0x530);
|
|
dataPspUtil.WriteStrWithPadding(DrmInfo.ContentId, 0x00, 0x30);
|
|
dataPspUtil.WriteInt32BE(DrmInfo.KeyIndex);
|
|
dataPspUtil.WriteInt32(0);
|
|
dataPspUtil.WriteInt32(0);
|
|
dataPspUtil.WriteInt32(0);
|
|
dataPspUtil.WriteBytes(startDat);
|
|
return dataPsp.ToArray();
|
|
}
|
|
}
|
|
|
|
private int compressAndWriteBlock(Int64 isoOffset)
|
|
{
|
|
int wsize = 0;
|
|
byte[] isoBuf = new byte[BLOCK_SZ];
|
|
wsize = umdImage.IsoStream.Read(isoBuf, 0x00, BLOCK_SZ);
|
|
|
|
byte[] wbuf = isoBuf;
|
|
|
|
if (this.compress) // Compress data.
|
|
{
|
|
byte[] lzRcBuf = Lz.compress(isoBuf, true);
|
|
//memset(lzrc_buf + lzrc_size, 0, 16);
|
|
|
|
int ratio = (lzRcBuf.Length * 100) / BLOCK_SZ;
|
|
|
|
if (ratio < RATIO_LIMIT)
|
|
{
|
|
wbuf = lzRcBuf;
|
|
|
|
wsize = lzRcBuf.Length;
|
|
wsize += MathUtil.CalculatePaddingAmount(wsize, 16);
|
|
Array.Resize(ref lzRcBuf, wsize);
|
|
}
|
|
}
|
|
|
|
int unpaddedSz = wsize;
|
|
wsize += MathUtil.CalculatePaddingAmount(wsize, 16);
|
|
Array.Resize(ref wbuf, wsize);
|
|
encryptBlock(wbuf, Convert.ToInt32(isoOffset));
|
|
byte[] hash = hashBlock(wbuf);
|
|
|
|
npTblUtil.WriteBytes(hash);
|
|
npTblUtil.WriteUInt32(Convert.ToUInt32(isoOffset));
|
|
npTblUtil.WriteUInt32(Convert.ToUInt32(unpaddedSz));
|
|
npTblUtil.WriteInt32(0);
|
|
npTblUtil.WriteInt32(0);
|
|
|
|
isoData.Write(wbuf, 0, wsize);
|
|
|
|
return wsize;
|
|
|
|
}
|
|
|
|
private void createNpUmdTbl()
|
|
{
|
|
Int64 tableSz = isoBlocks * 0x20;
|
|
Int64 isoSz = umdImage.IsoStream.Length;
|
|
Int64 isoOffset = 0x100 + tableSz;
|
|
|
|
for (int i = 0; i < isoBlocks; i++)
|
|
{
|
|
isoOffset += compressAndWriteBlock(isoOffset);
|
|
updateProgress(Convert.ToInt32(umdImage.IsoStream.Position), Convert.ToInt32(umdImage.IsoStream.Length), "Compress & Encrypt UMD Image");
|
|
}
|
|
|
|
}
|
|
|
|
private byte[] encryptTable()
|
|
{
|
|
byte[] table = npTbl.ToArray();
|
|
|
|
// Encrypt Table
|
|
var tp = MemoryMarshal.Cast<byte, uint>(table);
|
|
|
|
for (int i = 0; i < (table.Length / 0x20); i++)
|
|
XorTable(tp[(i * 8)..]);
|
|
|
|
var tpbyt = MemoryMarshal.Cast<uint, byte>(tp);
|
|
|
|
return tpbyt.ToArray();
|
|
}
|
|
private static void XorTable(Span<uint> tp)
|
|
{
|
|
tp[4] ^= tp[3] ^ tp[2];
|
|
tp[5] ^= tp[2] ^ tp[1];
|
|
tp[6] ^= tp[0] ^ tp[3];
|
|
tp[7] ^= tp[1] ^ tp[0];
|
|
}
|
|
|
|
|
|
private byte[] encryptBlock(byte[] blockData, int offset)
|
|
{
|
|
AMCTRL.CIPHER_KEY ckey = new AMCTRL.CIPHER_KEY();
|
|
AMCTRL.sceDrmBBCipherInit(out ckey, 1, DrmInfo.KeyIndex, headerKey, DrmInfo.VersionKey, offset >> 4);
|
|
AMCTRL.sceDrmBBCipherUpdate(ref ckey, blockData, blockData.Length);
|
|
AMCTRL.sceDrmBBCipherFinal(ref ckey);
|
|
return blockData;
|
|
}
|
|
|
|
private byte[] hashBlock(byte[] blockData)
|
|
{
|
|
byte[] hash = new byte[0x10];
|
|
Span<byte> mkey = stackalloc byte[Marshal.SizeOf<PspCrypto.AMCTRL.MAC_KEY>()];
|
|
AMCTRL.sceDrmBBMacInit(mkey, 3);
|
|
AMCTRL.sceDrmBBMacUpdate(mkey, blockData, blockData.Length);
|
|
AMCTRL.sceDrmBBMacFinal(mkey, hash, DrmInfo.VersionKey);
|
|
Utils.BuildDrmBBMacFinal2(hash);
|
|
return hash;
|
|
}
|
|
|
|
|
|
private void createNpUmdBody()
|
|
{
|
|
npHdrBodyUtil.WriteUInt16(SECTOR_SZ); // sector_sz
|
|
|
|
if (umdImage.IsoStream.Length > 0x40000000)
|
|
npHdrBodyUtil.WriteUInt16(0xE001); // unk_2
|
|
else
|
|
npHdrBodyUtil.WriteUInt16(0xE000); //unk_2
|
|
|
|
npHdrBodyUtil.WriteUInt32(0); // unk_4
|
|
npHdrBodyUtil.WriteUInt32(0x1010); // unk_8
|
|
npHdrBodyUtil.WriteUInt32(0); // unk_12
|
|
npHdrBodyUtil.WriteUInt32(0); // unk_16
|
|
|
|
npHdrBodyUtil.WriteUInt32(0x00); // LBA START
|
|
npHdrBodyUtil.WriteUInt32(0); // unk_24
|
|
|
|
|
|
npHdrBodyUtil.WriteUInt32(Math.Min(Convert.ToUInt32((isoBlocks * BLOCK_BASIS) - 1), 0x6C0BF)); // nsectors
|
|
npHdrBodyUtil.WriteUInt32(0); // unk_32
|
|
|
|
npHdrBodyUtil.WriteUInt32(Convert.ToUInt32((isoBlocks * BLOCK_BASIS) - 1));
|
|
npHdrBodyUtil.WriteUInt32(0x01003FFE); // unk_40
|
|
npHdrBodyUtil.WriteUInt32(0x100); // block_entry_offset
|
|
|
|
npHdrBodyUtil.WriteStrWithPadding(umdImage.DiscIdSeperated, 0x00, 0x10);
|
|
|
|
npHdrBodyUtil.WriteInt32(0); // header_start_offset
|
|
npHdrBodyUtil.WriteInt32(0); // unk_68
|
|
|
|
npHdrBodyUtil.WriteByte(0x00); // unk_72
|
|
npHdrBodyUtil.WriteByte(0x00); // bbmac param
|
|
npHdrBodyUtil.WriteByte(0x00); // unk_74
|
|
npHdrBodyUtil.WriteByte(0x00); // unk_75
|
|
|
|
npHdrBodyUtil.WriteInt32(0); // unk_76
|
|
npHdrBodyUtil.WriteInt32(0); // unk_80
|
|
npHdrBodyUtil.WriteInt32(0); // unk_84
|
|
npHdrBodyUtil.WriteInt32(0); // unk_88
|
|
npHdrBodyUtil.WriteInt32(0); // unk_92
|
|
|
|
}
|
|
|
|
private byte[] encryptHeader(byte[] headerBytes)
|
|
{
|
|
AMCTRL.CIPHER_KEY ckey = new AMCTRL.CIPHER_KEY();
|
|
AMCTRL.sceDrmBBCipherInit(out ckey, 1, DrmInfo.KeyIndex, headerKey, DrmInfo.VersionKey, 0);
|
|
AMCTRL.sceDrmBBCipherUpdate(ref ckey, headerBytes, headerBytes.Length);
|
|
AMCTRL.sceDrmBBCipherFinal(ref ckey);
|
|
return headerBytes;
|
|
}
|
|
|
|
public override void Dispose()
|
|
{
|
|
npHdr.Dispose();
|
|
npHdrBody.Dispose();
|
|
isoData.Dispose();
|
|
npTbl.Dispose();
|
|
base.Dispose();
|
|
}
|
|
|
|
private Int64 isoBlocks;
|
|
private bool compress;
|
|
|
|
UmdInfo umdImage;
|
|
|
|
private byte[] headerKey;
|
|
private byte[] dataKey;
|
|
|
|
|
|
private MemoryStream npHdr;
|
|
private StreamUtil npHdrUtil;
|
|
|
|
private MemoryStream npHdrBody;
|
|
private StreamUtil npHdrBodyUtil;
|
|
|
|
private MemoryStream isoData;
|
|
private StreamUtil isoDataUtil;
|
|
|
|
private MemoryStream npTbl;
|
|
private StreamUtil npTblUtil;
|
|
}
|
|
}
|