Implement Sub Channel Generation.

This commit is contained in:
Li 2023-05-01 23:51:00 +12:00
parent dd6707d5ef
commit de65c71d2c
19 changed files with 356 additions and 140 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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 }
};
}
}

View File

@ -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;
}
}
}

View File

@ -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,
}
}

View File

@ -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;

View File

@ -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();
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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;

View File

@ -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));

View File

@ -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);

View File

@ -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
{