Add code to read SBI File (Subchannel information, for libcrypt), and include magic word inside ISO HEADER
This commit is contained in:
parent
b13fa203a8
commit
6647b319a2
|
@ -30,24 +30,28 @@ namespace GameBuilder.Cue
|
|||
return totalTracks;
|
||||
}
|
||||
|
||||
public static byte BinaryDecimalConv(int i)
|
||||
public static byte BinaryDecimalToDecimal(int i)
|
||||
{
|
||||
return Convert.ToByte(Convert.ToInt32(10 * (i - i % 16) / 16 + i % 16));
|
||||
}
|
||||
public static byte DecimalToBinaryDecimal(int i)
|
||||
{
|
||||
return Convert.ToByte(Convert.ToInt32((i % 10) + 16 * ((i / 10) % 10)));
|
||||
}
|
||||
public int IdxToSectorRel(CueIndex index)
|
||||
public static int IdxToSectorRel(DiscIndex index)
|
||||
{
|
||||
int offset = (((index.Mrel * 60) + index.Srel) * 75 + index.Frel);
|
||||
return offset;
|
||||
}
|
||||
public int IdxToSector(CueIndex index)
|
||||
public static int IdxToSector(DiscIndex index)
|
||||
{
|
||||
int offset = (((index.m * 60) + index.s) * 75 + index.f);
|
||||
return offset;
|
||||
}
|
||||
|
||||
public CueIndex SectorToIdx(int sector)
|
||||
public static DiscIndex SectorToIdx(int sector)
|
||||
{
|
||||
CueIndex idx = new CueIndex(1);
|
||||
DiscIndex idx = new DiscIndex(1);
|
||||
|
||||
int x = sector;
|
||||
int f = sector % 75;
|
||||
|
@ -56,11 +60,6 @@ namespace GameBuilder.Cue
|
|||
int s = x % 60;
|
||||
int m = Convert.ToInt32(Math.Floor(Convert.ToDouble(x) / 60.0));
|
||||
|
||||
//idx.Mrel = Convert.ToByte(Convert.ToInt32(((sector / 75) / 60)));
|
||||
//idx.Srel = Convert.ToByte(Convert.ToInt32(((sector / 75) % 60)));
|
||||
//idx.Frel = Convert.ToByte(Convert.ToInt32(((sector % 75))));
|
||||
//idx.Sdelta = 2; // why?
|
||||
|
||||
idx.Mrel = Convert.ToInt16(m);
|
||||
idx.Srel = Convert.ToInt16(s);
|
||||
idx.Frel = Convert.ToInt16(f);
|
||||
|
@ -185,11 +184,11 @@ namespace GameBuilder.Cue
|
|||
byte[] tocA0Entry = new byte[10] { 0x41, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00 };
|
||||
|
||||
// And an A1 track (determines how many tracks there are)
|
||||
byte[] tocA1Entry = new byte[10] { 0x41, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, BinaryDecimalConv(GetTotalTracks()), 0x00, 0x00 };
|
||||
byte[] tocA1Entry = new byte[10] { 0x41, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, DecimalToBinaryDecimal(GetTotalTracks()), 0x00, 0x00 };
|
||||
|
||||
// the A2 track is a bit more complicated ..
|
||||
int totalSectors = getTotalSectorSz();
|
||||
CueIndex idx = SectorToIdx(totalSectors);
|
||||
DiscIndex idx = SectorToIdx(totalSectors);
|
||||
idx.Sdelta = 2;
|
||||
|
||||
byte[] tocA2Entry = new byte[10] { 0x41, 0x00, 0xA2, 0x00, 0x00, 0x00, 0x00, idx.M, idx.S, idx.F };
|
||||
|
@ -246,7 +245,7 @@ namespace GameBuilder.Cue
|
|||
if (tracks[i] is null) continue;
|
||||
int pos = positions[tracks[i].binFileName];
|
||||
|
||||
CueIndex idx = this.SectorToIdx(pos);
|
||||
DiscIndex idx = SectorToIdx(pos);
|
||||
// pregap not included on first track
|
||||
|
||||
if (tracks[i].TrackNo == 1) tracks[i].TrackIndex[0].Sdelta = 0;
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace GameBuilder.Cue
|
|||
|
||||
public TrackType TrackType;
|
||||
public byte TrackNo;
|
||||
public CueIndex[] TrackIndex;
|
||||
public DiscIndex[] TrackIndex;
|
||||
|
||||
public int TrackLength;
|
||||
public int SectorSz
|
||||
|
@ -30,9 +30,9 @@ namespace GameBuilder.Cue
|
|||
|
||||
internal CueTrack(string binFile)
|
||||
{
|
||||
TrackIndex = new CueIndex[2];
|
||||
TrackIndex = new DiscIndex[2];
|
||||
for (int i = 0; i < TrackIndex.Length; i++)
|
||||
TrackIndex[i] = new CueIndex(Convert.ToByte(i));
|
||||
TrackIndex[i] = new DiscIndex(Convert.ToByte(i));
|
||||
|
||||
binFileName = binFile;
|
||||
binFileSz = new FileInfo(binFileName).Length;
|
||||
|
@ -47,7 +47,7 @@ namespace GameBuilder.Cue
|
|||
|
||||
tocEntry[0] = Convert.ToByte(this.TrackType);
|
||||
tocEntry[1] = 0;
|
||||
tocEntry[2] = CueReader.BinaryDecimalConv(this.TrackNo);
|
||||
tocEntry[2] = CueReader.DecimalToBinaryDecimal(this.TrackNo);
|
||||
|
||||
|
||||
tocEntry[3] = this.TrackIndex[0].M;
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace GameBuilder.Cue
|
||||
{
|
||||
public class CueIndex
|
||||
public class DiscIndex
|
||||
{
|
||||
public byte IndexNumber;
|
||||
public short Mrel;
|
||||
|
@ -73,7 +73,7 @@ namespace GameBuilder.Cue
|
|||
{
|
||||
get
|
||||
{
|
||||
return CueReader.BinaryDecimalConv(m);
|
||||
return CueReader.DecimalToBinaryDecimal(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ namespace GameBuilder.Cue
|
|||
{
|
||||
get
|
||||
{
|
||||
return CueReader.BinaryDecimalConv(s);
|
||||
return CueReader.DecimalToBinaryDecimal(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,11 +89,11 @@ namespace GameBuilder.Cue
|
|||
{
|
||||
get
|
||||
{
|
||||
return CueReader.BinaryDecimalConv(f);
|
||||
return CueReader.DecimalToBinaryDecimal(f);
|
||||
}
|
||||
}
|
||||
|
||||
internal CueIndex(byte indexNumber)
|
||||
internal DiscIndex(byte indexNumber)
|
||||
{
|
||||
IndexNumber = indexNumber;
|
||||
Mrel = 0;
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder.Cue
|
||||
{
|
||||
public class SbiEntry
|
||||
{
|
||||
public DiscIndex MSF;
|
||||
byte[] TOC;
|
||||
public int Sector
|
||||
{
|
||||
get
|
||||
{
|
||||
return CueReader.IdxToSector(MSF);
|
||||
}
|
||||
}
|
||||
|
||||
public SbiEntry(DiscIndex Msf, byte[] Toc)
|
||||
{
|
||||
MSF = Msf;
|
||||
TOC = Toc;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
using Li.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder.Cue
|
||||
{
|
||||
public class SbiReader
|
||||
{
|
||||
private StreamUtil sbiUtil;
|
||||
private List<SbiEntry> sbiEntries;
|
||||
public SbiEntry[] Entries
|
||||
{
|
||||
get
|
||||
{
|
||||
return sbiEntries.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private void init(Stream sbiFile)
|
||||
{
|
||||
sbiEntries = new List<SbiEntry>();
|
||||
sbiUtil = new StreamUtil(sbiFile);
|
||||
string magic = sbiUtil.ReadStrLen(3);
|
||||
if (magic != "SBI")
|
||||
throw new Exception("Invalid SBI Sub Channel file.");
|
||||
sbiUtil.ReadByte();
|
||||
|
||||
do
|
||||
{
|
||||
byte m = CueReader.BinaryDecimalToDecimal(sbiUtil.ReadByte());
|
||||
byte s = CueReader.BinaryDecimalToDecimal(sbiUtil.ReadByte());
|
||||
byte f = CueReader.BinaryDecimalToDecimal(sbiUtil.ReadByte());
|
||||
byte i = CueReader.BinaryDecimalToDecimal(sbiUtil.ReadByte());
|
||||
byte[] toc = sbiUtil.ReadBytes(0xA);
|
||||
|
||||
DiscIndex idx = new DiscIndex(i);
|
||||
idx.Mrel = m;
|
||||
idx.Srel = s;
|
||||
idx.Frel = f;
|
||||
sbiEntries.Add(new SbiEntry(idx, toc));
|
||||
} while (sbiFile.Position < sbiFile.Length);
|
||||
}
|
||||
public SbiReader(string sbiFileName)
|
||||
{
|
||||
using (FileStream fsbi = File.OpenRead(sbiFileName))
|
||||
{
|
||||
init(fsbi);
|
||||
}
|
||||
}
|
||||
public SbiReader(Stream sbiFile)
|
||||
{
|
||||
init(sbiFile);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ using System.Runtime.InteropServices;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Li.Utilities;
|
||||
using GameBuilder.Pops.LibCrypt;
|
||||
|
||||
namespace GameBuilder.Pops
|
||||
{
|
||||
|
@ -25,6 +26,9 @@ namespace GameBuilder.Pops
|
|||
this.disc = disc;
|
||||
this.cue = new CueReader(disc.CueFile);
|
||||
|
||||
if (disc.SbiFile is not null)
|
||||
this.sbi = new SbiReader(disc.SbiFile);
|
||||
|
||||
this.IsoHeader = new MemoryStream();
|
||||
this.CompressedIso = new MemoryStream();
|
||||
|
||||
|
@ -115,6 +119,7 @@ namespace GameBuilder.Pops
|
|||
writeTOC();
|
||||
writeIsoLocation();
|
||||
writeName();
|
||||
writeLibCryptData();
|
||||
|
||||
writeCompressedIso();
|
||||
|
||||
|
@ -129,6 +134,28 @@ namespace GameBuilder.Pops
|
|||
IsoHeader.Seek(0xE20, SeekOrigin.Begin);
|
||||
isoHeaderUtil.WriteInt64(location);
|
||||
}
|
||||
|
||||
private int obfuscateMagicWord()
|
||||
{
|
||||
int magicWord = 0;
|
||||
|
||||
if (sbi is not null) magicWord = MagicWord.GenMagicWord(sbi.Entries);
|
||||
|
||||
return magicWord ^ 0x72d0ee59;
|
||||
}
|
||||
private void writeLibCryptData()
|
||||
{
|
||||
// obfuscated libcrypt magic word
|
||||
int obfuscatedMagicWord = obfuscateMagicWord();
|
||||
isoHeaderUtil.WriteInt32(obfuscatedMagicWord);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
|
||||
|
||||
isoHeaderUtil.WritePadding(0, 0x2D40);
|
||||
|
||||
}
|
||||
private void writeName()
|
||||
{
|
||||
// copied from crash bandicoot warped
|
||||
|
@ -137,14 +164,9 @@ namespace GameBuilder.Pops
|
|||
|
||||
isoHeaderUtil.WriteInt32(2047); // unk
|
||||
isoHeaderUtil.WriteStrWithPadding(disc.DiscName, 0x00, 0x80);
|
||||
isoHeaderUtil.WriteInt32(3); // unk
|
||||
isoHeaderUtil.WriteInt32(3); // PARENTAL_LEVEL ?
|
||||
|
||||
isoHeaderUtil.WriteInt32(0x72d0ee59); // appears to be constant?
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
|
||||
isoHeaderUtil.WritePadding(0, 0x2D40);
|
||||
}
|
||||
|
||||
private void writeCDAEntry(int position, int length, uint key)
|
||||
|
@ -229,6 +251,7 @@ namespace GameBuilder.Pops
|
|||
|
||||
private DiscInfo disc;
|
||||
private CueReader cue;
|
||||
private SbiReader sbi;
|
||||
private PopsImg srcImg;
|
||||
|
||||
public MemoryStream IsoHeader;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using DiscUtils.Iso9660Ps1;
|
||||
using DiscUtils.Raw;
|
||||
using DiscUtils.Streams;
|
||||
using GameBuilder.Cue;
|
||||
using Li.Utilities;
|
||||
|
@ -16,6 +17,15 @@ namespace GameBuilder.Pops
|
|||
private string discName;
|
||||
private string discId;
|
||||
|
||||
public string? SbiFile
|
||||
{
|
||||
get
|
||||
{
|
||||
string sbiFileName = Path.ChangeExtension(CueFile, ".sbi");
|
||||
if(File.Exists(sbiFileName)) return sbiFileName;
|
||||
else return null;
|
||||
}
|
||||
}
|
||||
public string CueFile
|
||||
{
|
||||
get
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
using GameBuilder.Cue;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder.Pops.LibCrypt
|
||||
{
|
||||
public class MagicWord
|
||||
{
|
||||
private static int[][] LIBCRYPT_PAIRS = new int[16][] { new int[2] { 14105, 14110 },
|
||||
new int[2] { 14231, 14236 },
|
||||
new int[2] { 14485, 14490 },
|
||||
new int[2] { 14579, 14584 },
|
||||
new int[2] { 14649, 14654 },
|
||||
new int[2] { 14899, 14904 },
|
||||
new int[2] { 15056, 15061 },
|
||||
new int[2] { 15130, 15135 },
|
||||
new int[2] { 15242, 15247 },
|
||||
new int[2] { 15312, 15317 },
|
||||
new int[2] { 15378, 15383 },
|
||||
new int[2] { 15628, 15633 },
|
||||
new int[2] { 15919, 15924 },
|
||||
new int[2] { 16031, 16036 },
|
||||
new int[2] { 16101, 16106 },
|
||||
new int[2] { 16167, 16172 }
|
||||
};
|
||||
|
||||
public static int GenMagicWord(SbiEntry[] Sbi)
|
||||
{
|
||||
bool[] bits = new bool[16];
|
||||
int sbiI = 0;
|
||||
|
||||
if (Sbi.Length % 2 != 0) return 0;
|
||||
|
||||
int magicWord = 0;
|
||||
|
||||
for (int i = 0; i < bits.Length; i++)
|
||||
{
|
||||
bool isSet = (Sbi[sbiI].Sector == LIBCRYPT_PAIRS[i][0] && Sbi[sbiI + 1].Sector == LIBCRYPT_PAIRS[i][1]);
|
||||
if (isSet) sbiI += 2;
|
||||
bits[i] = isSet;
|
||||
|
||||
if (isSet) magicWord |= 1;
|
||||
if (i + 1 < bits.Length)
|
||||
magicWord <<= 1;
|
||||
}
|
||||
|
||||
return magicWord;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1038,7 +1038,7 @@ namespace PspCrypto
|
|||
return -0x7f78ffff;
|
||||
}
|
||||
|
||||
Span<byte> secureTick = BitConverter.GetBytes(ksceRtcGetCurrentSecureTick());
|
||||
Span<byte> secureTick = BitConverter.GetBytes(SceRtc.ksceRtcGetCurrentSecureTick());
|
||||
|
||||
ebootSig.Fill(0);
|
||||
sceEbootPbp.SwVer = swVer;
|
||||
|
@ -1281,14 +1281,6 @@ namespace PspCrypto
|
|||
return 0;
|
||||
}
|
||||
|
||||
private static ulong ksceRtcGetCurrentSecureTick()
|
||||
{
|
||||
DateTime epoch = new DateTime(1, 1, 1, 0, 0, 0);
|
||||
DateTime now = DateTime.UtcNow;
|
||||
TimeSpan ts = now.Subtract(epoch);
|
||||
return Convert.ToUInt64(Math.Floor(ts.TotalMilliseconds)) * 1000;
|
||||
}
|
||||
|
||||
private static int SceSblGcAuthMgrDrmBBForDriver_4B506BE7(ReadOnlySpan<byte> digest, ReadOnlySpan<byte> sig, int keyType)
|
||||
{
|
||||
byte[] pubx;
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PspCrypto
|
||||
{
|
||||
public class SceRtc
|
||||
{
|
||||
public static ulong ksceRtcGetCurrentTick()
|
||||
{
|
||||
return ksceRtcGetCurrentSecureTick();
|
||||
}
|
||||
|
||||
public static ulong ksceRtcGetCurrentNetworkTick()
|
||||
{
|
||||
return ksceRtcGetCurrentSecureTick();
|
||||
}
|
||||
public static ulong ksceRtcGetCurrentSecureTick()
|
||||
{
|
||||
DateTime epoch = new DateTime(1, 1, 1, 0, 0, 0);
|
||||
DateTime now = DateTime.UtcNow;
|
||||
TimeSpan ts = now.Subtract(epoch);
|
||||
return Convert.ToUInt64(Math.Floor(ts.TotalMilliseconds)) * 1000;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue