From de65c71d2c776aeb67b1687be97c730a2a06440d Mon Sep 17 00:00:00 2001
From: Li
Date: Mon, 1 May 2023 23:51:00 +1200
Subject: [PATCH] Implement Sub Channel Generation.
---
ChovySign-GUI/Ps1/GameInfoSelector.axaml.cs | 2 +-
GameBuilder/Cue/CueReader.cs | 4 +-
GameBuilder/Cue/CueTrack.cs | 34 +--------
GameBuilder/Cue/DiscTrack.cs | 68 +++++++++++++++++
GameBuilder/Cue/SbiEntry.cs | 4 +-
GameBuilder/Cue/SbiReader.cs | 25 ++++---
GameBuilder/Pops/DiscCompressor.cs | 81 +++++++++------------
GameBuilder/Pops/LibCrypt/Constants.cs | 31 ++++++++
GameBuilder/Pops/LibCrypt/LibCryptInfo.cs | 52 +++++++++++++
GameBuilder/Pops/LibCrypt/LibCryptMethod.cs | 14 ++++
GameBuilder/Pops/LibCrypt/MagicWord.cs | 28 ++-----
GameBuilder/Pops/LibCrypt/SubChannel.cs | 69 ++++++++++++++++++
GameBuilder/Pops/{DiscInfo.cs => PSInfo.cs} | 17 ++++-
GameBuilder/Pops/PopsImg.cs | 27 +++----
GameBuilder/Pops/PsIsoImg.cs | 9 ++-
GameBuilder/Pops/PsTitleImg.cs | 4 +-
LiLib/StreamUtil.cs | 15 ++++
LibChovy/Art/Downloader.cs | 2 +-
LibChovy/PopsParameters.cs | 10 +--
19 files changed, 356 insertions(+), 140 deletions(-)
create mode 100644 GameBuilder/Cue/DiscTrack.cs
create mode 100644 GameBuilder/Pops/LibCrypt/Constants.cs
create mode 100644 GameBuilder/Pops/LibCrypt/LibCryptInfo.cs
create mode 100644 GameBuilder/Pops/LibCrypt/LibCryptMethod.cs
create mode 100644 GameBuilder/Pops/LibCrypt/SubChannel.cs
rename GameBuilder/Pops/{DiscInfo.cs => PSInfo.cs} (86%)
diff --git a/ChovySign-GUI/Ps1/GameInfoSelector.axaml.cs b/ChovySign-GUI/Ps1/GameInfoSelector.axaml.cs
index 899a570..da9b669 100644
--- a/ChovySign-GUI/Ps1/GameInfoSelector.axaml.cs
+++ b/ChovySign-GUI/Ps1/GameInfoSelector.axaml.cs
@@ -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);
diff --git a/GameBuilder/Cue/CueReader.cs b/GameBuilder/Cue/CueReader.cs
index 0323884..f94e41f 100644
--- a/GameBuilder/Cue/CueReader.cs
+++ b/GameBuilder/Cue/CueReader.cs
@@ -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;
diff --git a/GameBuilder/Cue/CueTrack.cs b/GameBuilder/Cue/CueTrack.cs
index c50db76..530f1e9 100644
--- a/GameBuilder/Cue/CueTrack.cs
+++ b/GameBuilder/Cue/CueTrack.cs
@@ -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;
- }
+
}
}
diff --git a/GameBuilder/Cue/DiscTrack.cs b/GameBuilder/Cue/DiscTrack.cs
new file mode 100644
index 0000000..5ebdbd5
--- /dev/null
+++ b/GameBuilder/Cue/DiscTrack.cs
@@ -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;
+ }
+ }
+}
diff --git a/GameBuilder/Cue/SbiEntry.cs b/GameBuilder/Cue/SbiEntry.cs
index 0c29270..e6c258a 100644
--- a/GameBuilder/Cue/SbiEntry.cs
+++ b/GameBuilder/Cue/SbiEntry.cs
@@ -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;
diff --git a/GameBuilder/Cue/SbiReader.cs b/GameBuilder/Cue/SbiReader.cs
index 29f8f04..2b3817a 100644
--- a/GameBuilder/Cue/SbiReader.cs
+++ b/GameBuilder/Cue/SbiReader.cs
@@ -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();
@@ -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);
}
diff --git a/GameBuilder/Pops/DiscCompressor.cs b/GameBuilder/Pops/DiscCompressor.cs
index 24100bd..9b16c94 100644
--- a/GameBuilder/Pops/DiscCompressor.cs
+++ b/GameBuilder/Pops/DiscCompressor.cs
@@ -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;
diff --git a/GameBuilder/Pops/LibCrypt/Constants.cs b/GameBuilder/Pops/LibCrypt/Constants.cs
new file mode 100644
index 0000000..f17bebc
--- /dev/null
+++ b/GameBuilder/Pops/LibCrypt/Constants.cs
@@ -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 }
+ };
+ }
+}
diff --git a/GameBuilder/Pops/LibCrypt/LibCryptInfo.cs b/GameBuilder/Pops/LibCrypt/LibCryptInfo.cs
new file mode 100644
index 0000000..77afba8
--- /dev/null
+++ b/GameBuilder/Pops/LibCrypt/LibCryptInfo.cs
@@ -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;
+
+ }
+ }
+}
diff --git a/GameBuilder/Pops/LibCrypt/LibCryptMethod.cs b/GameBuilder/Pops/LibCrypt/LibCryptMethod.cs
new file mode 100644
index 0000000..14cebdc
--- /dev/null
+++ b/GameBuilder/Pops/LibCrypt/LibCryptMethod.cs
@@ -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,
+ }
+}
diff --git a/GameBuilder/Pops/LibCrypt/MagicWord.cs b/GameBuilder/Pops/LibCrypt/MagicWord.cs
index e749518..7ab37ec 100644
--- a/GameBuilder/Pops/LibCrypt/MagicWord.cs
+++ b/GameBuilder/Pops/LibCrypt/MagicWord.cs
@@ -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 sbiSectors = new HashSet();
- 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;
diff --git a/GameBuilder/Pops/LibCrypt/SubChannel.cs b/GameBuilder/Pops/LibCrypt/SubChannel.cs
new file mode 100644
index 0000000..2809517
--- /dev/null
+++ b/GameBuilder/Pops/LibCrypt/SubChannel.cs
@@ -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();
+ }
+ }
+ }
+}
diff --git a/GameBuilder/Pops/DiscInfo.cs b/GameBuilder/Pops/PSInfo.cs
similarity index 86%
rename from GameBuilder/Pops/DiscInfo.cs
rename to GameBuilder/Pops/PSInfo.cs
index 9cb424c..e92d59b 100644
--- a/GameBuilder/Pops/DiscInfo.cs
+++ b/GameBuilder/Pops/PSInfo.cs
@@ -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);
}
}
}
diff --git a/GameBuilder/Pops/PopsImg.cs b/GameBuilder/Pops/PopsImg.cs
index e2c261e..806338c 100644
--- a/GameBuilder/Pops/PopsImg.cs
+++ b/GameBuilder/Pops/PopsImg.cs
@@ -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;
}
}
diff --git a/GameBuilder/Pops/PsIsoImg.cs b/GameBuilder/Pops/PsIsoImg.cs
index be10098..eb05ad4 100644
--- a/GameBuilder/Pops/PsIsoImg.cs
+++ b/GameBuilder/Pops/PsIsoImg.cs
@@ -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();
diff --git a/GameBuilder/Pops/PsTitleImg.cs b/GameBuilder/Pops/PsTitleImg.cs
index aa75941..932f4dc 100644
--- a/GameBuilder/Pops/PsTitleImg.cs
+++ b/GameBuilder/Pops/PsTitleImg.cs
@@ -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;
diff --git a/LiLib/StreamUtil.cs b/LiLib/StreamUtil.cs
index 5dcd0c7..751723b 100644
--- a/LiLib/StreamUtil.cs
+++ b/LiLib/StreamUtil.cs
@@ -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));
diff --git a/LibChovy/Art/Downloader.cs b/LibChovy/Art/Downloader.cs
index fd7db12..2102fe1 100644
--- a/LibChovy/Art/Downloader.cs
+++ b/LibChovy/Art/Downloader.cs
@@ -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 DownloadCover(DiscInfo game)
+ public static async Task DownloadCover(PSInfo game)
{
string discIdDash = game.DiscId.Substring(0, 4) + "-" + game.DiscId.Substring(4, 5);
diff --git a/LibChovy/PopsParameters.cs b/LibChovy/PopsParameters.cs
index 298ccd4..9baa801 100644
--- a/LibChovy/PopsParameters.cs
+++ b/LibChovy/PopsParameters.cs
@@ -16,16 +16,16 @@ namespace LibChovy
public PopsParameters(NpDrmInfo drmInfo, NpDrmRif rif) : base(drmInfo, rif)
{
Type = ChovyTypes.POPS;
- discList = new List();
+ discList = new List();
}
private string? nameOverride;
- private List discList;
+ private List 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
{