reimplement cue SBI loading
This commit is contained in:
parent
0d029b4030
commit
377f8e8b1c
|
@ -42,8 +42,7 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
public bool ThrowExceptions2048 = true;
|
public bool ThrowExceptions2048 = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates whether subcode should be delivered deinterleaved. It isn't stored that way on actual discs.
|
/// Indicates whether subcode should be delivered deinterleaved. It isn't stored that way on actual discs. But it is in .sub files
|
||||||
/// How about .sub files? Can't remember right now.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool DeinterleavedSubcode = true;
|
public bool DeinterleavedSubcode = true;
|
||||||
|
|
||||||
|
@ -161,6 +160,26 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
return 2048;
|
return 2048;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads 12 bytes of subQ data from a sector.
|
||||||
|
/// This is necessarily deinterleaved.
|
||||||
|
/// </summary>
|
||||||
|
public int ReadLBA_SubQ(int lba, byte[] buffer, int offset)
|
||||||
|
{
|
||||||
|
var sector = disc.Sectors[lba + 150];
|
||||||
|
|
||||||
|
PrepareBuffer(buffer, offset, 12);
|
||||||
|
PrepareJob(lba);
|
||||||
|
job.DestBuffer2448 = buf2442;
|
||||||
|
job.DestOffset = 0;
|
||||||
|
job.Parts = ESectorSynthPart.SubchannelQ | ESectorSynthPart.SubcodeDeinterleave;
|
||||||
|
|
||||||
|
sector.SectorSynth.Synth(job);
|
||||||
|
Buffer.BlockCopy(buf2442, 2352 + 12, buffer, offset, 12);
|
||||||
|
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// reads 2048 bytes of user data from a sector.
|
/// reads 2048 bytes of user data from a sector.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -52,57 +52,6 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class SynthUtils
|
|
||||||
{
|
|
||||||
public static void SubP(byte[] buffer, int offset, bool pause)
|
|
||||||
{
|
|
||||||
byte val = (byte)(pause?0xFF:0x00);
|
|
||||||
for (int i = 0; i < 12; i++)
|
|
||||||
buffer[offset + i] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SectorHeader(byte[] buffer, int offset, int LBA, byte mode)
|
|
||||||
{
|
|
||||||
buffer[offset + 0] = 0x00;
|
|
||||||
for (int i = 1; i < 11; i++) buffer[offset + i] = 0xFF;
|
|
||||||
buffer[offset + 11] = 0x00;
|
|
||||||
Timestamp ts = new Timestamp(LBA + 150);
|
|
||||||
buffer[offset + 12] = BCD2.IntToBCD(ts.MIN);
|
|
||||||
buffer[offset + 13] = BCD2.IntToBCD(ts.SEC);
|
|
||||||
buffer[offset + 14] = BCD2.IntToBCD(ts.FRAC);
|
|
||||||
buffer[offset + 15] = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EDC_Mode2_Form1(byte[] buffer, int offset)
|
|
||||||
{
|
|
||||||
uint edc = ECM.EDC_Calc(buffer, offset + 16, 2048 + 8);
|
|
||||||
ECM.PokeUint(buffer, offset + 2072, edc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EDC_Mode2_Form2(byte[] buffer, int offset)
|
|
||||||
{
|
|
||||||
uint edc = ECM.EDC_Calc(buffer, offset + 16, 2324+8);
|
|
||||||
ECM.PokeUint(buffer, offset + 2348, edc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Make sure everything else in the sector userdata is done before calling this
|
|
||||||
/// </summary>
|
|
||||||
public static void ECM_Mode1(byte[] buffer, int offset, int LBA)
|
|
||||||
{
|
|
||||||
//EDC
|
|
||||||
uint edc = ECM.EDC_Calc(buffer, offset, 2064);
|
|
||||||
ECM.PokeUint(buffer, offset + 2064, edc);
|
|
||||||
|
|
||||||
//reserved, zero
|
|
||||||
for (int i = 0; i < 8; i++) buffer[offset + 2068 + i] = 0;
|
|
||||||
|
|
||||||
//ECC
|
|
||||||
ECM.ECC_Populate(buffer, offset, buffer, offset, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a Mode1 2048-byte sector
|
/// Represents a Mode1 2048-byte sector
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -224,37 +224,67 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SS_PatchQ : ISectorSynthJob2448
|
||||||
|
{
|
||||||
|
public ISectorSynthJob2448 Original;
|
||||||
|
public byte[] Buffer_SubQ = new byte[12];
|
||||||
|
public void Synth(SectorSynthJob job)
|
||||||
|
{
|
||||||
|
Original.Synth(job);
|
||||||
|
|
||||||
|
if ((job.Parts & ESectorSynthPart.SubchannelQ) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//apply patched subQ
|
||||||
|
for (int i = 0; i < 12; i++)
|
||||||
|
job.DestBuffer2448[2352 + 12 + i] = Buffer_SubQ[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// applies an SBI file to the disc
|
/// applies an SBI file to the disc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ApplySBI(SBI.SubQPatchData sbi, bool asMednafen)
|
public void ApplySBI(SBI.SubQPatchData sbi, bool asMednafen)
|
||||||
{
|
{
|
||||||
|
//TODO - could implement as a blob, to avoid allocating so many byte buffers
|
||||||
|
|
||||||
//save this, it's small, and we'll want it for disc processing a/b checks
|
//save this, it's small, and we'll want it for disc processing a/b checks
|
||||||
Memos["sbi"] = sbi;
|
Memos["sbi"] = sbi;
|
||||||
|
|
||||||
|
DiscSectorReader dsr = new DiscSectorReader(this);
|
||||||
|
|
||||||
int n = sbi.ABAs.Count;
|
int n = sbi.ABAs.Count;
|
||||||
byte[] subcode = new byte[96];
|
|
||||||
int b=0;
|
int b=0;
|
||||||
for (int i = 0; i < n; i++)
|
for (int i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
int aba = sbi.ABAs[i];
|
int lba = sbi.ABAs[i] - 150;
|
||||||
var oldSubcode = this.Sectors[aba].SubcodeSector;
|
|
||||||
oldSubcode.ReadSubcodeDeinterleaved(subcode, 0);
|
//create a synthesizer which can return the patched data
|
||||||
|
var ss_patchq = new SS_PatchQ() { Original = this.Sectors[lba+150].SectorSynth };
|
||||||
|
byte[] subQbuf = ss_patchq.Buffer_SubQ;
|
||||||
|
|
||||||
|
//read the old subcode
|
||||||
|
dsr.ReadLBA_SubQ(lba, subQbuf, 0);
|
||||||
|
|
||||||
|
//insert patch
|
||||||
|
Sectors[lba + 150].SectorSynth = ss_patchq;
|
||||||
|
|
||||||
|
//apply SBI patch
|
||||||
for (int j = 0; j < 12; j++)
|
for (int j = 0; j < 12; j++)
|
||||||
{
|
{
|
||||||
short patch = sbi.subq[b++];
|
short patch = sbi.subq[b++];
|
||||||
if (patch == -1) continue;
|
if (patch == -1) continue;
|
||||||
else subcode[12 + j] = (byte)patch;
|
else subQbuf[j] = (byte)patch;
|
||||||
}
|
}
|
||||||
var bss = BufferedSubcodeSector.CloneFromBytesDeinterleaved(subcode);
|
|
||||||
Sectors[aba].SubcodeSector = bss;
|
|
||||||
|
|
||||||
//not fully sure what the basis is for this, but here we go
|
//Apply mednafen hacks
|
||||||
|
//The reasoning here is that we know we expect these sectors to have a wrong checksum. therefore, generate a checksum, and make it wrong
|
||||||
|
//However, this seems senseless to me. The whole point of the SBI data is that it stores the patches needed to generate an acceptable subQ, right?
|
||||||
if (asMednafen)
|
if (asMednafen)
|
||||||
{
|
{
|
||||||
bss.Synthesize_SunchannelQ_Checksum();
|
SynthUtils.SubQ_Checksum(subQbuf, 0);
|
||||||
bss.SubcodeDeinterleaved[12 + 10] ^= 0xFF;
|
subQbuf[10] ^= 0xFF;
|
||||||
bss.SubcodeDeinterleaved[12 + 11] ^= 0xFF;
|
subQbuf[11] ^= 0xFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -489,7 +519,72 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class SynthUtils
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the checksum of the provided Q subchannel
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">12 byte Q subchannel: input and output buffer for operation</param>
|
||||||
|
/// <param name="offset">location within buffer of Q subchannel</param>
|
||||||
|
public static void SubQ_Checksum(byte[] buffer, int offset)
|
||||||
|
{
|
||||||
|
ushort crc16 = CRC16_CCITT.Calculate(buffer, offset, 10);
|
||||||
|
|
||||||
|
//CRC is stored inverted and big endian
|
||||||
|
buffer[offset + 10] = (byte)(~(crc16 >> 8));
|
||||||
|
buffer[offset + 11] = (byte)(~(crc16));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SubP(byte[] buffer, int offset, bool pause)
|
||||||
|
{
|
||||||
|
byte val = (byte)(pause ? 0xFF : 0x00);
|
||||||
|
for (int i = 0; i < 12; i++)
|
||||||
|
buffer[offset + i] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SectorHeader(byte[] buffer, int offset, int LBA, byte mode)
|
||||||
|
{
|
||||||
|
buffer[offset + 0] = 0x00;
|
||||||
|
for (int i = 1; i < 11; i++) buffer[offset + i] = 0xFF;
|
||||||
|
buffer[offset + 11] = 0x00;
|
||||||
|
Timestamp ts = new Timestamp(LBA + 150);
|
||||||
|
buffer[offset + 12] = BCD2.IntToBCD(ts.MIN);
|
||||||
|
buffer[offset + 13] = BCD2.IntToBCD(ts.SEC);
|
||||||
|
buffer[offset + 14] = BCD2.IntToBCD(ts.FRAC);
|
||||||
|
buffer[offset + 15] = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EDC_Mode2_Form1(byte[] buffer, int offset)
|
||||||
|
{
|
||||||
|
uint edc = ECM.EDC_Calc(buffer, offset + 16, 2048 + 8);
|
||||||
|
ECM.PokeUint(buffer, offset + 2072, edc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EDC_Mode2_Form2(byte[] buffer, int offset)
|
||||||
|
{
|
||||||
|
uint edc = ECM.EDC_Calc(buffer, offset + 16, 2324 + 8);
|
||||||
|
ECM.PokeUint(buffer, offset + 2348, edc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Make sure everything else in the sector userdata is done before calling this
|
||||||
|
/// </summary>
|
||||||
|
public static void ECM_Mode1(byte[] buffer, int offset, int LBA)
|
||||||
|
{
|
||||||
|
//EDC
|
||||||
|
uint edc = ECM.EDC_Calc(buffer, offset, 2064);
|
||||||
|
ECM.PokeUint(buffer, offset + 2064, edc);
|
||||||
|
|
||||||
|
//reserved, zero
|
||||||
|
for (int i = 0; i < 8; i++) buffer[offset + 2068 + i] = 0;
|
||||||
|
|
||||||
|
//ECC
|
||||||
|
ECM.ECC_Populate(buffer, offset, buffer, offset, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//not being used yet
|
//not being used yet
|
||||||
class DiscPreferences
|
class DiscPreferences
|
||||||
|
|
|
@ -33,7 +33,12 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CUE_PregapMode2_As_XA_Form2 = true;
|
public bool CUE_PregapMode2_As_XA_Form2 = true;
|
||||||
|
|
||||||
public void SetForPlaystation()
|
/// <summary>
|
||||||
|
/// Mednafen loads SBI files oddly
|
||||||
|
/// </summary>
|
||||||
|
public bool SBO_As_Mednafen = true;
|
||||||
|
|
||||||
|
public void SetForPSX()
|
||||||
{
|
{
|
||||||
//probably set CUE_PauseContradictionModeA to follow mednafen, but not proven yet
|
//probably set CUE_PauseContradictionModeA to follow mednafen, but not proven yet
|
||||||
//almost surely set CUE_PregapMode2_As_XA_Form2 to follow mednafen
|
//almost surely set CUE_PregapMode2_As_XA_Form2 to follow mednafen
|
||||||
|
@ -163,7 +168,7 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
var sbiJob = new SBI.LoadSBIJob();
|
var sbiJob = new SBI.LoadSBIJob();
|
||||||
sbiJob.IN_Path = sbiPath;
|
sbiJob.IN_Path = sbiPath;
|
||||||
sbiJob.Run();
|
sbiJob.Run();
|
||||||
OUT_Disc.ApplySBI(sbiJob.OUT_Data, true);
|
OUT_Disc.ApplySBI(sbiJob.OUT_Data, IN_DiscMountPolicy.SBO_As_Mednafen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ext == ".ccd")
|
else if (ext == ".ccd")
|
||||||
|
|
Loading…
Reference in New Issue