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 {