Implement Sub Channel Generation.
This commit is contained in:
parent
dd6707d5ef
commit
de65c71d2c
|
@ -84,7 +84,7 @@ namespace ChovySign_GUI.Ps1
|
|||
{
|
||||
try
|
||||
{
|
||||
DiscInfo disc = new DiscInfo(cueFile);
|
||||
PSInfo disc = new PSInfo(cueFile);
|
||||
Title = disc.DiscName;
|
||||
|
||||
byte[] newCover = await Downloader.DownloadCover(disc);
|
||||
|
|
|
@ -49,9 +49,9 @@ namespace GameBuilder.Cue
|
|||
return offset;
|
||||
}
|
||||
|
||||
public static DiscIndex SectorToIdx(int sector)
|
||||
public static DiscIndex SectorToIdx(int sector, byte index=1)
|
||||
{
|
||||
DiscIndex idx = new DiscIndex(1);
|
||||
DiscIndex idx = new DiscIndex(index);
|
||||
|
||||
int x = sector;
|
||||
int f = sector % 75;
|
||||
|
|
|
@ -6,16 +6,12 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace GameBuilder.Cue
|
||||
{
|
||||
public class CueTrack
|
||||
public class CueTrack : DiscTrack
|
||||
{
|
||||
|
||||
public const int MODE2_SECTOR_SZ = 2352;
|
||||
public const int CDDA_SECTOR_SZ = 2352;
|
||||
|
||||
public TrackType TrackType;
|
||||
public byte TrackNo;
|
||||
public DiscIndex[] TrackIndex;
|
||||
|
||||
public int TrackLength;
|
||||
public int SectorSz
|
||||
{
|
||||
|
@ -30,36 +26,14 @@ namespace GameBuilder.Cue
|
|||
|
||||
internal CueTrack(string binFile)
|
||||
{
|
||||
TrackIndex = new DiscIndex[2];
|
||||
for (int i = 0; i < TrackIndex.Length; i++)
|
||||
TrackIndex[i] = new DiscIndex(Convert.ToByte(i));
|
||||
|
||||
binFileName = binFile;
|
||||
binFileSz = new FileInfo(binFileName).Length;
|
||||
|
||||
TrackType = TrackType.TRACK_MODE2_2352;
|
||||
TrackNo = 0xFF;
|
||||
this.TrackType = TrackType.TRACK_MODE2_2352;
|
||||
this.TrackNo = 0xFF;
|
||||
}
|
||||
|
||||
public byte[] ToTocEntry()
|
||||
{
|
||||
byte[] tocEntry = new byte[10];
|
||||
|
||||
tocEntry[0] = Convert.ToByte(this.TrackType);
|
||||
tocEntry[1] = 0;
|
||||
tocEntry[2] = CueReader.DecimalToBinaryDecimal(this.TrackNo);
|
||||
|
||||
|
||||
tocEntry[3] = this.TrackIndex[0].M;
|
||||
tocEntry[4] = this.TrackIndex[0].S;
|
||||
tocEntry[5] = this.TrackIndex[0].F;
|
||||
tocEntry[6] = 0;
|
||||
tocEntry[7] = this.TrackIndex[1].M;
|
||||
tocEntry[8] = this.TrackIndex[1].S;
|
||||
tocEntry[9] = this.TrackIndex[1].F;
|
||||
|
||||
return tocEntry;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder.Cue
|
||||
{
|
||||
public class DiscTrack
|
||||
{
|
||||
private byte unk1;
|
||||
private byte unk6;
|
||||
public TrackType TrackType;
|
||||
public byte TrackNo;
|
||||
public DiscIndex[] TrackIndex;
|
||||
|
||||
public DiscTrack()
|
||||
{
|
||||
TrackIndex = new DiscIndex[2];
|
||||
for (int i = 0; i < TrackIndex.Length; i++)
|
||||
TrackIndex[i] = new DiscIndex(Convert.ToByte(i));
|
||||
|
||||
unk1 = 0;
|
||||
unk6 = 0;
|
||||
|
||||
}
|
||||
|
||||
public static DiscTrack FromTocEntry(byte[] tocEntry)
|
||||
{
|
||||
if (tocEntry.Length != 0xA) throw new Exception("Invalid TOC Entry.");
|
||||
|
||||
DiscTrack track = new DiscTrack();
|
||||
track.TrackType = (TrackType)tocEntry[0];
|
||||
track.unk1 = tocEntry[1];
|
||||
track.TrackNo = CueReader.BinaryDecimalToDecimal(tocEntry[2]);
|
||||
|
||||
track.TrackIndex[0].Mrel = Convert.ToInt16(CueReader.BinaryDecimalToDecimal(tocEntry[3]) - track.TrackIndex[0].Mdelta);
|
||||
track.TrackIndex[0].Srel = Convert.ToInt16(CueReader.BinaryDecimalToDecimal(tocEntry[4]) - track.TrackIndex[0].Sdelta);
|
||||
track.TrackIndex[0].Frel = Convert.ToInt16(CueReader.BinaryDecimalToDecimal(tocEntry[5]) - track.TrackIndex[0].Fdelta);
|
||||
track.unk6 = tocEntry[6];
|
||||
track.TrackIndex[1].Mrel = Convert.ToInt16(CueReader.BinaryDecimalToDecimal(tocEntry[7]) - track.TrackIndex[1].Mdelta);
|
||||
track.TrackIndex[1].Srel = Convert.ToInt16(CueReader.BinaryDecimalToDecimal(tocEntry[8]) - track.TrackIndex[1].Sdelta);
|
||||
track.TrackIndex[1].Frel = Convert.ToInt16(CueReader.BinaryDecimalToDecimal(tocEntry[9]) - track.TrackIndex[1].Fdelta);
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
public byte[] ToTocEntry()
|
||||
{
|
||||
byte[] tocEntry = new byte[0xA];
|
||||
|
||||
tocEntry[0] = Convert.ToByte(this.TrackType);
|
||||
tocEntry[1] = unk1;
|
||||
tocEntry[2] = CueReader.DecimalToBinaryDecimal(this.TrackNo);
|
||||
|
||||
|
||||
tocEntry[3] = this.TrackIndex[0].M;
|
||||
tocEntry[4] = this.TrackIndex[0].S;
|
||||
tocEntry[5] = this.TrackIndex[0].F;
|
||||
tocEntry[6] = unk6;
|
||||
tocEntry[7] = this.TrackIndex[1].M;
|
||||
tocEntry[8] = this.TrackIndex[1].S;
|
||||
tocEntry[9] = this.TrackIndex[1].F;
|
||||
|
||||
return tocEntry;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ namespace GameBuilder.Cue
|
|||
public class SbiEntry
|
||||
{
|
||||
public DiscIndex MSF;
|
||||
byte[] TOC;
|
||||
public DiscTrack TOC;
|
||||
public int Sector
|
||||
{
|
||||
get
|
||||
|
@ -18,7 +18,7 @@ namespace GameBuilder.Cue
|
|||
}
|
||||
}
|
||||
|
||||
public SbiEntry(DiscIndex Msf, byte[] Toc)
|
||||
public SbiEntry(DiscIndex Msf, DiscTrack Toc)
|
||||
{
|
||||
MSF = Msf;
|
||||
TOC = Toc;
|
||||
|
|
|
@ -19,6 +19,20 @@ namespace GameBuilder.Cue
|
|||
}
|
||||
}
|
||||
|
||||
private DiscIndex readMsfi()
|
||||
{
|
||||
byte m = CueReader.BinaryDecimalToDecimal(sbiUtil.ReadByte());
|
||||
byte s = CueReader.BinaryDecimalToDecimal(sbiUtil.ReadByte());
|
||||
byte f = CueReader.BinaryDecimalToDecimal(sbiUtil.ReadByte());
|
||||
byte i = CueReader.BinaryDecimalToDecimal(sbiUtil.ReadByte());
|
||||
|
||||
DiscIndex idx = new DiscIndex(i);
|
||||
idx.Mrel = m;
|
||||
idx.Srel = s;
|
||||
idx.Frel = f;
|
||||
return idx;
|
||||
}
|
||||
|
||||
private void init(Stream sbiFile)
|
||||
{
|
||||
sbiEntries = new List<SbiEntry>();
|
||||
|
@ -30,16 +44,9 @@ namespace GameBuilder.Cue
|
|||
|
||||
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 = readMsfi();
|
||||
DiscTrack toc = DiscTrack.FromTocEntry(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);
|
||||
}
|
||||
|
|
|
@ -20,15 +20,12 @@ namespace GameBuilder.Pops
|
|||
const int DEFAULT_ISO_OFFSET = 0x100000;
|
||||
public int IsoOffset;
|
||||
|
||||
internal DiscCompressor(PopsImg srcImg, DiscInfo disc, IAtracEncoderBase encoder, int offset = DEFAULT_ISO_OFFSET)
|
||||
internal DiscCompressor(PopsImg srcImg, PSInfo disc, IAtracEncoderBase encoder, int offset = DEFAULT_ISO_OFFSET)
|
||||
{
|
||||
this.srcImg = srcImg;
|
||||
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();
|
||||
|
||||
|
@ -71,23 +68,6 @@ namespace GameBuilder.Pops
|
|||
{
|
||||
isoHeaderUtil.WriteStrWithPadding(disc.DiscIdHdr, 0x00, 0x400);
|
||||
}
|
||||
|
||||
|
||||
public byte[] GenerateIsoPgd()
|
||||
{
|
||||
IsoHeader.Seek(0x0, SeekOrigin.Begin);
|
||||
byte[] isoHdr = IsoHeader.ToArray();
|
||||
|
||||
int headerSize = DNASHelper.CalculateSize(isoHdr.Length, 0x400);
|
||||
byte[] headerEnc = new byte[headerSize];
|
||||
|
||||
int sz = DNASHelper.Encrypt(headerEnc, isoHdr, srcImg.DrmInfo.VersionKey, isoHdr.Length, srcImg.DrmInfo.KeyIndex, 1, blockSize: 0x400);
|
||||
byte[] isoHdrPgd = headerEnc.ToArray();
|
||||
Array.Resize(ref isoHdrPgd, sz);
|
||||
|
||||
return isoHdrPgd;
|
||||
}
|
||||
|
||||
private void writeIsoLocation()
|
||||
{
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
|
@ -96,7 +76,7 @@ namespace GameBuilder.Pops
|
|||
|
||||
isoHeaderUtil.WriteInt32(IsoOffset); // always 0x100000 on single disc game
|
||||
|
||||
isoHeaderUtil.WritePadding(0x00, 0x620);
|
||||
isoHeaderUtil.WritePadding(0x00, 0x628);
|
||||
}
|
||||
|
||||
private void writeCompressedIso()
|
||||
|
@ -113,59 +93,68 @@ namespace GameBuilder.Pops
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSubChannelPgd()
|
||||
{
|
||||
if(disc.LibCrypt.Method == LibCryptMethod.METHOD_SUB_CHANNEL)
|
||||
{
|
||||
byte[] subChannelsData = disc.LibCrypt.Subchannels;
|
||||
|
||||
int sz = subChannelsData.Length / 0xC;
|
||||
uint location = Convert.ToUInt32(IsoOffset + CompressedIso.Position);
|
||||
writeSubchannelDatLocation(location, sz);
|
||||
|
||||
byte[] pgdData = srcImg.CreatePgd(subChannelsData);
|
||||
|
||||
CompressedIso.Write(pgdData, 0, pgdData.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateIsoHeaderAndCompress()
|
||||
{
|
||||
writeHeader();
|
||||
writeTOC();
|
||||
writeIsoLocation();
|
||||
writeName();
|
||||
writeDiscInfo();
|
||||
writeLibCryptData();
|
||||
|
||||
writeCompressedIso();
|
||||
|
||||
isoHeaderUtil.PadUntil(0x0, 0xb3880);
|
||||
|
||||
// now write CD-Audio data.
|
||||
// write CD Audio data.
|
||||
writeCompressedCDATracks();
|
||||
|
||||
// write subchannels
|
||||
writeSubChannelPgd();
|
||||
}
|
||||
|
||||
public void WriteSimpleDatLocation(Int64 location)
|
||||
private void writeSubchannelDatLocation(uint location, int totalSubchannels)
|
||||
{
|
||||
IsoHeader.Seek(0xE20, SeekOrigin.Begin);
|
||||
isoHeaderUtil.WriteInt64(location);
|
||||
isoHeaderUtil.WriteUInt32At(location, 0xED4);
|
||||
isoHeaderUtil.WriteInt32At(totalSubchannels, 0xED8);
|
||||
}
|
||||
|
||||
private int obfuscateMagicWord()
|
||||
public void WriteSimpleDatLocation(uint location)
|
||||
{
|
||||
int magicWord = 0;
|
||||
|
||||
if (sbi is not null) magicWord = MagicWord.GenMagicWord(sbi.Entries);
|
||||
|
||||
return magicWord ^ 0x72d0ee59;
|
||||
isoHeaderUtil.WriteUInt32At(location, 0xE20);
|
||||
}
|
||||
|
||||
private void writeLibCryptData()
|
||||
{
|
||||
// obfuscated libcrypt magic word
|
||||
isoHeaderUtil.WriteInt32(obfuscateMagicWord());
|
||||
isoHeaderUtil.WriteInt32(disc.LibCrypt.ObfuscatedMagicWord);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
isoHeaderUtil.WriteInt32(0);
|
||||
|
||||
|
||||
isoHeaderUtil.WritePadding(0, 0x2D40);
|
||||
|
||||
}
|
||||
private void writeName()
|
||||
private void writeDiscInfo()
|
||||
{
|
||||
// copied from crash bandicoot warped
|
||||
|
||||
isoHeaderUtil.WriteInt64(0x00); // SIMPLE.DAT location
|
||||
|
||||
isoHeaderUtil.WriteInt32(2047); // unk
|
||||
isoHeaderUtil.WriteStrWithPadding(disc.DiscName, 0x00, 0x80);
|
||||
isoHeaderUtil.WriteUInt32(Convert.ToUInt32(disc.LibCrypt.Method)); // libcrypt method
|
||||
isoHeaderUtil.WriteStrWithPadding(disc.DiscName, 0x00, 0x80); // disc title
|
||||
isoHeaderUtil.WriteInt32(3); // PARENTAL_LEVEL ?
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void writeCDAEntry(int position, int length, uint key)
|
||||
|
@ -248,7 +237,7 @@ namespace GameBuilder.Pops
|
|||
cue.Dispose();
|
||||
}
|
||||
|
||||
private DiscInfo disc;
|
||||
private PSInfo disc;
|
||||
private CueReader cue;
|
||||
private SbiReader sbi;
|
||||
private PopsImg srcImg;
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder.Pops.LibCrypt
|
||||
{
|
||||
public class Constants
|
||||
{
|
||||
public const int MAGIC_WORD_KEY = 0x72D0EE59;
|
||||
|
||||
public 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 }
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
using GameBuilder.Cue;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using static GameBuilder.Pops.LibCrypt.MagicWord;
|
||||
using static GameBuilder.Pops.LibCrypt.SubChannel;
|
||||
|
||||
namespace GameBuilder.Pops.LibCrypt
|
||||
{
|
||||
public class LibCryptInfo
|
||||
{
|
||||
public LibCryptMethod Method;
|
||||
private SbiReader? sbiReader;
|
||||
public int MagicWord
|
||||
{
|
||||
get
|
||||
{
|
||||
if (sbiReader is null) return 0;
|
||||
return GenerateMagicWord(sbiReader.Entries);
|
||||
}
|
||||
}
|
||||
|
||||
public int ObfuscatedMagicWord
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Method == LibCryptMethod.METHOD_SUB_CHANNEL) return 0;
|
||||
return ObfuscateMagicWord(this.MagicWord);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Subchannels
|
||||
{
|
||||
get
|
||||
{
|
||||
if (sbiReader is null) throw new Exception("Cannot create subchannels, if there is no SBI data.");
|
||||
return CreateSubchannelDat(MagicWord);
|
||||
}
|
||||
}
|
||||
public LibCryptInfo(SbiReader? sbi, LibCryptMethod method)
|
||||
{
|
||||
this.sbiReader = sbi;
|
||||
|
||||
if (sbi is null) Method = LibCryptMethod.METHOD_MAGIC_WORD;
|
||||
else Method = method;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder.Pops.LibCrypt
|
||||
{
|
||||
public enum LibCryptMethod : uint
|
||||
{
|
||||
METHOD_MAGIC_WORD = 0x7ff,
|
||||
METHOD_SUB_CHANNEL = 0x8000,
|
||||
}
|
||||
}
|
|
@ -9,34 +9,20 @@ 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)
|
||||
public static int ObfuscateMagicWord(int magicWord)
|
||||
{
|
||||
return magicWord ^ Constants.MAGIC_WORD_KEY;
|
||||
}
|
||||
public static int GenerateMagicWord(SbiEntry[] sbiEntries)
|
||||
{
|
||||
bool[] bits = new bool[16];
|
||||
|
||||
HashSet<int> sbiSectors = new HashSet<int>();
|
||||
foreach(SbiEntry sbiEntry in Sbi)
|
||||
foreach(SbiEntry sbiEntry in sbiEntries)
|
||||
sbiSectors.Add(sbiEntry.Sector);
|
||||
|
||||
for (int i = 0; i < bits.Length; i++)
|
||||
bits[i] = (sbiSectors.Contains(LIBCRYPT_PAIRS[i][0]) && sbiSectors.Contains(LIBCRYPT_PAIRS[i][1]));
|
||||
bits[i] = (sbiSectors.Contains(Constants.LIBCRYPT_PAIRS[i][0]) && sbiSectors.Contains(Constants.LIBCRYPT_PAIRS[i][1]));
|
||||
|
||||
|
||||
int magicWord = 0;
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
using GameBuilder.Cue;
|
||||
using Li.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameBuilder.Pops.LibCrypt
|
||||
{
|
||||
public class SubChannel
|
||||
{
|
||||
public static byte[] CreateSubchannelDat(int magicWord)
|
||||
{
|
||||
using(MemoryStream subChannels = new MemoryStream())
|
||||
{
|
||||
StreamUtil subChannelsUtil = new StreamUtil(subChannels);
|
||||
// this header seems to mark the start of the sub channel data.
|
||||
subChannelsUtil.WriteUInt32(0xFFFFFFFF);
|
||||
subChannelsUtil.WriteUInt32(0x00000000);
|
||||
subChannelsUtil.WriteUInt32(0xFFFFFFFF);
|
||||
|
||||
|
||||
for (int i = 0; i < Constants.LIBCRYPT_PAIRS.Length; i++)
|
||||
{
|
||||
if ((magicWord & (1 << ((Constants.LIBCRYPT_PAIRS.Length - 1) - i))) != 0)
|
||||
{
|
||||
int[] pair = Constants.LIBCRYPT_PAIRS[i];
|
||||
|
||||
foreach (int lcSector in pair)
|
||||
{
|
||||
DiscIndex sidx = CueReader.SectorToIdx(lcSector, 0);
|
||||
sidx.Sdelta = -2;
|
||||
int adjustedSector = CueReader.IdxToSector(sidx);
|
||||
|
||||
// write sector number offset by 2s
|
||||
subChannelsUtil.WriteInt32(adjustedSector);
|
||||
subChannelsUtil.WriteByte(0x01);
|
||||
subChannelsUtil.WriteByte(0x01);
|
||||
|
||||
sidx.Fdelta = -1;
|
||||
sidx.Sdelta = -2;
|
||||
|
||||
// write sector index 1 but corrupted
|
||||
subChannelsUtil.WriteByte(sidx.M);
|
||||
subChannelsUtil.WriteByte(sidx.S);
|
||||
subChannelsUtil.WriteByte(sidx.F);
|
||||
|
||||
sidx.Sdelta = 0;
|
||||
|
||||
// write sector index 0 but corrupted
|
||||
subChannelsUtil.WriteByte(sidx.M);
|
||||
subChannelsUtil.WriteByte(sidx.S);
|
||||
subChannelsUtil.WriteByte(sidx.F);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// this header seems to mark the end of the sub channel data.
|
||||
subChannelsUtil.WriteUInt32(0xFFFFFFFF);
|
||||
subChannelsUtil.WriteUInt32(0xFFFFFFFF);
|
||||
subChannelsUtil.WriteUInt32(0xFFFFFFFF);
|
||||
|
||||
return subChannels.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
using DiscUtils.Raw;
|
||||
using DiscUtils.Streams;
|
||||
using GameBuilder.Cue;
|
||||
using GameBuilder.Pops.LibCrypt;
|
||||
using Li.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -11,12 +12,20 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace GameBuilder.Pops
|
||||
{
|
||||
public class DiscInfo
|
||||
public class PSInfo
|
||||
{
|
||||
private string cueFile;
|
||||
private string discName;
|
||||
private string discId;
|
||||
private LibCryptInfo lc;
|
||||
|
||||
public LibCryptInfo LibCrypt
|
||||
{
|
||||
get
|
||||
{
|
||||
return lc;
|
||||
}
|
||||
}
|
||||
public string? SbiFile
|
||||
{
|
||||
get
|
||||
|
@ -62,7 +71,7 @@ namespace GameBuilder.Pops
|
|||
}
|
||||
}
|
||||
|
||||
public DiscInfo(string cueFile)
|
||||
public PSInfo(string cueFile)
|
||||
{
|
||||
this.cueFile = cueFile;
|
||||
|
||||
|
@ -102,6 +111,10 @@ namespace GameBuilder.Pops
|
|||
if (discName == "") discName = Path.GetFileNameWithoutExtension(cueFile);
|
||||
if (discId is null) discId = "SLUS00001";
|
||||
|
||||
if (this.SbiFile is not null)
|
||||
this.lc = new LibCryptInfo(new SbiReader(this.SbiFile), LibCryptMethod.METHOD_MAGIC_WORD);
|
||||
else
|
||||
this.lc = new LibCryptInfo(null, LibCryptMethod.METHOD_MAGIC_WORD);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using GameBuilder;
|
||||
using GameBuilder.Cue;
|
||||
using GameBuilder.Psp;
|
||||
using Li.Utilities;
|
||||
using PspCrypto;
|
||||
|
@ -21,16 +22,10 @@ namespace GameBuilder.Pops
|
|||
|
||||
this.StartDat = NpDrmPsar.CreateStartDat(Resources.STARTDATPOPS);
|
||||
this.createSimpleDat();
|
||||
this.SimplePgd = generateSimplePgd();
|
||||
this.SimplePgd = CreatePgd(simple.ToArray());
|
||||
|
||||
}
|
||||
|
||||
private MemoryStream simple;
|
||||
private StreamUtil simpleUtil;
|
||||
public byte[] StartDat;
|
||||
public byte[] SimplePgd;
|
||||
|
||||
private void createSimpleDat()
|
||||
internal void createSimpleDat()
|
||||
{
|
||||
simpleUtil.WriteStr("SIMPLE ");
|
||||
simpleUtil.WriteInt32(100);
|
||||
|
@ -41,17 +36,15 @@ namespace GameBuilder.Pops
|
|||
|
||||
simpleUtil.WriteBytes(Resources.SIMPLE);
|
||||
}
|
||||
private byte[] generateSimplePgd()
|
||||
public byte[] CreatePgd(byte[] buffer)
|
||||
{
|
||||
simple.Seek(0x0, SeekOrigin.Begin);
|
||||
byte[] simpleData = simple.ToArray();
|
||||
|
||||
int simpleSz = DNASHelper.CalculateSize(simpleData.Length, 0x400);
|
||||
byte[] simpleEnc = new byte[simpleSz];
|
||||
int bufferSz = DNASHelper.CalculateSize(buffer.Length, 0x400);
|
||||
byte[] bufferEnc = new byte[bufferSz];
|
||||
|
||||
// get pgd
|
||||
int sz = DNASHelper.Encrypt(simpleEnc, simpleData, DrmInfo.VersionKey, simpleData.Length, DrmInfo.KeyIndex, 1, blockSize: 0x400);
|
||||
byte[] pgd = simpleEnc.ToArray();
|
||||
int sz = DNASHelper.Encrypt(bufferEnc, buffer, DrmInfo.VersionKey, buffer.Length, DrmInfo.KeyIndex, 1, blockSize: 0x400);
|
||||
byte[] pgd = bufferEnc.ToArray();
|
||||
Array.Resize(ref pgd, sz);
|
||||
|
||||
return pgd;
|
||||
|
@ -80,7 +73,11 @@ namespace GameBuilder.Pops
|
|||
return loaderEnc.ToArray();
|
||||
}
|
||||
|
||||
private MemoryStream simple;
|
||||
private StreamUtil simpleUtil;
|
||||
|
||||
public byte[] StartDat;
|
||||
public byte[] SimplePgd;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,13 @@ namespace GameBuilder.Pops
|
|||
this.compressor = discCompressor;
|
||||
}
|
||||
|
||||
public PsIsoImg(NpDrmInfo versionKey, DiscInfo disc, IAtracEncoderBase encoder) : base(versionKey)
|
||||
public PsIsoImg(NpDrmInfo versionKey, PSInfo disc, IAtracEncoderBase encoder) : base(versionKey)
|
||||
{
|
||||
this.compressor = new DiscCompressor(this, disc, encoder);
|
||||
this.compressor.RegisterCallback(onProgress);
|
||||
}
|
||||
|
||||
public PsIsoImg(NpDrmInfo versionKey, DiscInfo disc) : base(versionKey)
|
||||
public PsIsoImg(NpDrmInfo versionKey, PSInfo disc) : base(versionKey)
|
||||
{
|
||||
this.compressor = new DiscCompressor(this, disc, new Atrac3ToolEncoder());
|
||||
this.compressor.RegisterCallback(onProgress);
|
||||
|
@ -34,7 +34,7 @@ namespace GameBuilder.Pops
|
|||
|
||||
psarUtil.WritePadding(0x00, 0x3ec); // Skip forwards
|
||||
|
||||
byte[] isoHdrPgd = compressor.GenerateIsoPgd();
|
||||
byte[] isoHdrPgd = this.CreatePgd(compressor.IsoHeader.ToArray());
|
||||
psarUtil.WriteBytes(isoHdrPgd);
|
||||
psarUtil.PadUntil(0x00, compressor.IsoOffset);
|
||||
}
|
||||
|
@ -45,7 +45,8 @@ namespace GameBuilder.Pops
|
|||
compressor.GenerateIsoHeaderAndCompress();
|
||||
|
||||
// write STARTDAT location
|
||||
compressor.WriteSimpleDatLocation((compressor.IsoOffset + compressor.CompressedIso.Length) + StartDat.Length);
|
||||
UInt32 simpleDatLocation = Convert.ToUInt32((compressor.IsoOffset + compressor.CompressedIso.Length) + StartDat.Length);
|
||||
compressor.WriteSimpleDatLocation(simpleDatLocation);
|
||||
|
||||
// write general PSISO header
|
||||
generatePsIsoHeader();
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace GameBuilder.Pops
|
|||
this.updateProgress(inf.Done, inf.Remain, inf.CurrentProcess + " (disc " + discNumber + ")");
|
||||
}
|
||||
|
||||
public PsTitleImg(NpDrmInfo drmInfo, DiscInfo[] discs) : base(drmInfo)
|
||||
public PsTitleImg(NpDrmInfo drmInfo, PSInfo[] discs) : base(drmInfo)
|
||||
{
|
||||
if (discs.Length > MAX_DISCS) throw new Exception("Sorry, multi disc games only support up to 5 discs... (i dont make the rules)");
|
||||
this.compressors = new DiscCompressor[MAX_DISCS];
|
||||
|
@ -171,7 +171,7 @@ namespace GameBuilder.Pops
|
|||
base.Dispose();
|
||||
}
|
||||
|
||||
private DiscInfo[] discs;
|
||||
private PSInfo[] discs;
|
||||
private DiscCompressor[] compressors;
|
||||
|
||||
private MemoryStream isoPart;
|
||||
|
|
|
@ -138,6 +138,21 @@ namespace Li.Utilities
|
|||
{
|
||||
WriteBytes(BitConverter.GetBytes(v).Reverse().ToArray());
|
||||
}
|
||||
public void WriteInt32At(Int32 v, long location)
|
||||
{
|
||||
long oldPos = s.Position;
|
||||
s.Seek(location, SeekOrigin.Begin);
|
||||
WriteInt32(v);
|
||||
s.Seek(oldPos, SeekOrigin.Begin);
|
||||
}
|
||||
public void WriteUInt32At(UInt32 v, long location)
|
||||
{
|
||||
long oldPos = s.Position;
|
||||
s.Seek(location, SeekOrigin.Begin);
|
||||
WriteUInt32(v);
|
||||
s.Seek(oldPos, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
public void WriteInt32(Int32 v)
|
||||
{
|
||||
WriteBytes(BitConverter.GetBytes(v));
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace LibChovy.Art
|
|||
private const string coverApi = "https://raw.githubusercontent.com/xlenore/psx-covers/main/covers/";
|
||||
private static HttpClient httpClient = new HttpClient();
|
||||
|
||||
public static async Task<byte[]> DownloadCover(DiscInfo game)
|
||||
public static async Task<byte[]> DownloadCover(PSInfo game)
|
||||
{
|
||||
string discIdDash = game.DiscId.Substring(0, 4) + "-" + game.DiscId.Substring(4, 5);
|
||||
|
||||
|
|
|
@ -16,16 +16,16 @@ namespace LibChovy
|
|||
public PopsParameters(NpDrmInfo drmInfo, NpDrmRif rif) : base(drmInfo, rif)
|
||||
{
|
||||
Type = ChovyTypes.POPS;
|
||||
discList = new List<DiscInfo>();
|
||||
discList = new List<PSInfo>();
|
||||
}
|
||||
private string? nameOverride;
|
||||
private List<DiscInfo> discList;
|
||||
private List<PSInfo> discList;
|
||||
|
||||
private byte[]? pic0;
|
||||
private byte[]? pic1;
|
||||
private byte[]? icon0;
|
||||
|
||||
public DiscInfo FirstDisc
|
||||
public PSInfo FirstDisc
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -101,7 +101,7 @@ namespace LibChovy
|
|||
|
||||
public void AddCd(string cd)
|
||||
{
|
||||
DiscInfo disc = new DiscInfo(cd);
|
||||
PSInfo disc = new PSInfo(cd);
|
||||
if (nameOverride is not null) disc.DiscName = nameOverride;
|
||||
else discList.Add(disc);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ namespace LibChovy
|
|||
}
|
||||
}
|
||||
}
|
||||
public DiscInfo[] Discs
|
||||
public PSInfo[] Discs
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue