reorg and cleanup of old interfaces, stop populating q_crc for RawTOCEntry instances
This commit is contained in:
parent
962b567f6d
commit
5e71224eda
|
@ -25,16 +25,6 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
sealed public partial class Disc
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns a SectorEntry from which you can retrieve various interesting pieces of information about the sector.
|
||||
/// The SectorEntry's interface is not likely to be stable, though, but it may be more convenient.
|
||||
/// </summary>
|
||||
public SectorEntry ReadLBA_SectorEntry(int lba)
|
||||
{
|
||||
return Sectors[lba + 150];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Main API to determine how many LBAs are available on the disc.
|
||||
/// This counts from LBA 0 to the final sector available.
|
||||
|
|
|
@ -94,7 +94,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
job.Parts = ESectorSynthPart.User2352;
|
||||
job.Disc = disc;
|
||||
|
||||
sector.SectorSynth.Synth(job);
|
||||
sector.Synth(job);
|
||||
|
||||
Buffer.BlockCopy(buf2442, 0, buffer, offset, 2352);
|
||||
|
||||
|
@ -116,7 +116,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
if (Policy.DeinterleavedSubcode)
|
||||
job.Parts |= ESectorSynthPart.SubcodeDeinterleave;
|
||||
|
||||
sector.SectorSynth.Synth(job);
|
||||
sector.Synth(job);
|
||||
|
||||
//we went straight to the caller's buffer, so no need to copy
|
||||
return 2442;
|
||||
|
@ -133,7 +133,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
job.DestOffset = 0;
|
||||
job.Parts = ESectorSynthPart.User2048;
|
||||
|
||||
sector.SectorSynth.Synth(job);
|
||||
sector.Synth(job);
|
||||
Buffer.BlockCopy(buf2442, 16, buffer, offset, 2048);
|
||||
|
||||
return 2048;
|
||||
|
@ -150,7 +150,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
job.DestOffset = 0;
|
||||
job.Parts = ESectorSynthPart.User2336;
|
||||
|
||||
sector.SectorSynth.Synth(job);
|
||||
sector.Synth(job);
|
||||
Buffer.BlockCopy(buf2442, 24, buffer, offset, 2048);
|
||||
|
||||
return 2048;
|
||||
|
@ -170,7 +170,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
job.DestOffset = 0;
|
||||
job.Parts = ESectorSynthPart.SubchannelQ | ESectorSynthPart.SubcodeDeinterleave;
|
||||
|
||||
sector.SectorSynth.Synth(job);
|
||||
sector.Synth(job);
|
||||
Buffer.BlockCopy(buf2442, 2352 + 12, buffer, offset, 12);
|
||||
|
||||
return 12;
|
||||
|
@ -201,7 +201,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
job.DestOffset = 0;
|
||||
job.Parts = ESectorSynthPart.Header16 | ESectorSynthPart.User2048 | ESectorSynthPart.EDC12;
|
||||
|
||||
sector.SectorSynth.Synth(job);
|
||||
sector.Synth(job);
|
||||
|
||||
//now the inspection, based on the mode
|
||||
byte mode = buf2442[15];
|
||||
|
@ -276,7 +276,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
job.Parts = ESectorSynthPart.Header16;
|
||||
job.Disc = disc;
|
||||
|
||||
sector.SectorSynth.Synth(job);
|
||||
sector.Synth(job);
|
||||
|
||||
return buf2442[15];
|
||||
}
|
||||
|
|
|
@ -470,7 +470,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
//subcode comes to us deinterleved; we may still need to interleave it
|
||||
if ((job.Parts & (ESectorSynthPart.SubcodeDeinterleave)) == 0)
|
||||
{
|
||||
SubcodeUtils.InterleaveInplace(job.DestBuffer2448, job.DestOffset + 2352);
|
||||
SynthUtils.InterleaveSubcodeInplace(job.DestBuffer2448, job.DestOffset + 2352);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -501,7 +501,6 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
//generate DiscTOCRaw items from the ones specified in the CCD file
|
||||
//TODO - range validate these (too many truncations to byte)
|
||||
disc.RawTOCEntries = new List<RawTOCEntry>();
|
||||
BufferedSubcodeSector bss = new BufferedSubcodeSector(); //TODO - its hacky that we need this..
|
||||
foreach (var entry in ccdf.TOCEntries)
|
||||
{
|
||||
BCD2 tno, ino;
|
||||
|
@ -530,82 +529,27 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
ap_min = BCD2.FromDecimal(entry.PMin),
|
||||
ap_sec = BCD2.FromDecimal(entry.PSec),
|
||||
ap_frame = BCD2.FromDecimal(entry.PFrame),
|
||||
q_crc = 0, //meainingless
|
||||
};
|
||||
|
||||
//CRC cant be calculated til we've got all the fields setup
|
||||
q.q_crc = bss.Synthesize_SubchannelQ(ref q, true);
|
||||
|
||||
disc.RawTOCEntries.Add(new RawTOCEntry { QData = q });
|
||||
}
|
||||
|
||||
|
||||
//disc.Structure = new DiscStructure();
|
||||
//var ses = new DiscStructure.Session();
|
||||
//disc.Structure.Sessions.Add(ses);
|
||||
|
||||
//for(int i=1;i<=99;i++)
|
||||
//{
|
||||
// if(!ccdf.TracksByNumber.ContainsKey(i))
|
||||
// continue;
|
||||
// var ccdt = ccdf.TracksByNumber[i];
|
||||
|
||||
// DiscStructure.Track track = new DiscStructure.Track() { Number = i };
|
||||
// ses.Tracks.Add(track);
|
||||
|
||||
// //if index 0 is missing, add it
|
||||
// if (!ccdt.Indexes.ContainsKey(0))
|
||||
// track.Indexes.Add(new DiscStructure.Index { Number = 0, LBA = ccdt.Indexes[1] });
|
||||
// for(int j=1;j<=99;j++)
|
||||
// if (ccdt.Indexes.ContainsKey(j))
|
||||
// track.Indexes.Add(new DiscStructure.Index { Number = j, LBA = ccdt.Indexes[j] });
|
||||
|
||||
// //TODO - this should only be used in case the .sub needs reconstructing
|
||||
// //determination should be done from heuristics.
|
||||
// //if we keep this, it should just be as a memo that later heuristics can use. For example: 'use guidance from original disc image'
|
||||
// track.ModeHeuristic = ccdt.Mode;
|
||||
|
||||
// //TODO - this should be deleted anyway (
|
||||
// switch (ccdt.Mode)
|
||||
// {
|
||||
// case 0:
|
||||
// track.TrackType = DiscStructure.ETrackType.Audio; //for CCD, this means audio, apparently.
|
||||
// break;
|
||||
// case 1:
|
||||
// case 2:
|
||||
// track.TrackType = DiscStructure.ETrackType.Data;
|
||||
// break;
|
||||
// default:
|
||||
// throw new InvalidOperationException("Unsupported CCD mode");
|
||||
// }
|
||||
//}
|
||||
|
||||
//add sectors for the "mandatory track 1 pregap", which isn't stored in the CCD file
|
||||
//THIS IS JUNK. MORE CORRECTLY SYNTHESIZE IT
|
||||
//add sectors for the mandatory track 1 pregap, which isn't stored in the CCD file
|
||||
//TODO - THIS IS JUNK. MORE CORRECTLY SYNTHESIZE IT
|
||||
var leadin_sector_zero = new Sector_Zero();
|
||||
var leadin_subcode_zero = new ZeroSubcodeSector();
|
||||
for (int i = 0; i < 150; i++)
|
||||
{
|
||||
var se = new SectorEntry(leadin_sector_zero);
|
||||
disc.Sectors.Add(se);
|
||||
se.SubcodeSector = leadin_subcode_zero;
|
||||
//TODO - YIKES!
|
||||
disc.Sectors.Add(null);
|
||||
}
|
||||
|
||||
//build the sectors:
|
||||
//set up as many sectors as we have img/sub for, even if the TOC doesnt reference them (TOC is unreliable, although the tracks should have covered it all)
|
||||
//set up as many sectors as we have img/sub for, even if the TOC doesnt reference them
|
||||
//(TOC is unreliable, although the tracks should have covered it all)
|
||||
for (int i = 0; i < loadResults.NumImgSectors; i++)
|
||||
{
|
||||
var isec = new Sector_RawBlob();
|
||||
isec.Offset = ((long)i) * 2352;
|
||||
isec.Blob = imgBlob;
|
||||
|
||||
var se = new SectorEntry(isec);
|
||||
disc.Sectors.Add(se);
|
||||
|
||||
var scsec = new BlobSubcodeSectorPreDeinterleaved();
|
||||
scsec.Offset = ((long)i) * 96;
|
||||
scsec.Blob = subBlob;
|
||||
se.SubcodeSector = scsec;
|
||||
se.SectorSynth = synth;
|
||||
disc.Sectors.Add(synth);
|
||||
}
|
||||
|
||||
return disc;
|
||||
|
|
|
@ -331,10 +331,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
if (curr_index == 0)
|
||||
ss.Pause = true;
|
||||
|
||||
//make the SectorEntry (some temporary bullshit here)
|
||||
var se = new SectorEntry(null);
|
||||
se.SectorSynth = ss;
|
||||
OUT_Disc.Sectors.Add(se);
|
||||
OUT_Disc.Sectors.Add(ss);
|
||||
relMSF++;
|
||||
|
||||
if (cct.IsFinalInFile)
|
||||
|
@ -360,7 +357,6 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
int specifiedPostgapLength = cct.PostgapLength.Sector;
|
||||
for (int s = 0; s < specifiedPostgapLength; s++)
|
||||
{
|
||||
var se= new SectorEntry(null);
|
||||
var ss = new SS_Gap();
|
||||
ss.TrackType = cct.TrackType; //TODO - old track type in some < -150 cases?
|
||||
|
||||
|
@ -376,8 +372,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
//always paused--is this good enough?
|
||||
ss.Pause = true;
|
||||
|
||||
se.SectorSynth = ss;
|
||||
OUT_Disc.Sectors.Add(se);
|
||||
OUT_Disc.Sectors.Add(ss);
|
||||
relMSF++;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,11 +31,10 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
}
|
||||
|
||||
//synth Q if needed
|
||||
//TODO - why not already have it serialized? Into a disc resource, even.
|
||||
if ((job.Parts & ESectorSynthPart.SubchannelQ) != 0)
|
||||
{
|
||||
var subcode = new BufferedSubcodeSector();
|
||||
subcode.Synthesize_SubchannelQ(ref sq, true);
|
||||
Buffer.BlockCopy(subcode.SubcodeDeinterleaved, 12, job.DestBuffer2448, job.DestOffset + 2352 + 12, 12);
|
||||
SynthUtils.SubQ_Serialize(job.DestBuffer2448, job.DestOffset + 2352 + 12, ref sq);
|
||||
}
|
||||
|
||||
//clear R-W if needed
|
||||
|
@ -47,7 +46,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
//subcode has been generated deinterleaved; we may still need to interleave it
|
||||
if ((job.Parts & (ESectorSynthPart.SubcodeDeinterleave)) == 0)
|
||||
{
|
||||
SubcodeUtils.InterleaveInplace(job.DestBuffer2448, job.DestOffset + 2352);
|
||||
SynthUtils.InterleaveSubcodeInplace(job.DestBuffer2448, job.DestOffset + 2352);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
/// <summary>
|
||||
/// The sectors on the disc
|
||||
/// </summary>
|
||||
public List<SectorEntry> Sectors = new List<SectorEntry>();
|
||||
internal List<ISectorSynthJob2448> Sectors = new List<ISectorSynthJob2448>();
|
||||
|
||||
internal SectorSynthParams SynthParams = new SectorSynthParams();
|
||||
|
||||
|
@ -137,6 +137,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
|
||||
/// <summary>
|
||||
/// generates lead-out sectors according to very crude approximations
|
||||
/// TODO - this isnt being used right now
|
||||
/// </summary>
|
||||
public class SynthesizeLeadoutJob
|
||||
{
|
||||
|
@ -159,8 +160,8 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
|
||||
for (int i = 0; i < Length; i++)
|
||||
{
|
||||
var se = new SectorEntry(sz);
|
||||
Disc.Sectors.Add(se);
|
||||
//var se = new SectorEntry(sz);
|
||||
//Disc.Sectors.Add(se);
|
||||
SubchannelQ sq = new SubchannelQ();
|
||||
|
||||
int track_relative_msf = i;
|
||||
|
@ -179,9 +180,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
byte ADR = 1;
|
||||
sq.SetStatus(ADR, leadoutFlags);
|
||||
|
||||
var subcode = new BufferedSubcodeSector();
|
||||
subcode.Synthesize_SubchannelQ(ref sq, true);
|
||||
se.SubcodeSector = subcode;
|
||||
//TODO - actually stash the subQ
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -233,14 +232,14 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
int lba = sbi.ABAs[i] - 150;
|
||||
|
||||
//create a synthesizer which can return the patched data
|
||||
var ss_patchq = new SS_PatchQ() { Original = this.Sectors[lba+150].SectorSynth };
|
||||
var ss_patchq = new SS_PatchQ() { Original = this.Sectors[lba+150] };
|
||||
byte[] subQbuf = ss_patchq.Buffer_SubQ;
|
||||
|
||||
//read the old subcode
|
||||
dsr.ReadLBA_SubQ(lba, subQbuf, 0);
|
||||
|
||||
//insert patch
|
||||
Sectors[lba + 150].SectorSynth = ss_patchq;
|
||||
Sectors[lba + 150] = ss_patchq;
|
||||
|
||||
//apply SBI patch
|
||||
for (int j = 0; j < 12; j++)
|
||||
|
@ -255,7 +254,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
//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)
|
||||
{
|
||||
SynthUtils.SubQ_Checksum(subQbuf, 0);
|
||||
SynthUtils.SubQ_SynthChecksum(subQbuf, 0);
|
||||
subQbuf[10] ^= 0xFF;
|
||||
subQbuf[11] ^= 0xFF;
|
||||
}
|
||||
|
@ -422,73 +421,199 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
|
||||
static class SynthUtils
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the checksum of the provided Q subchannel
|
||||
/// Calculates the checksum of the provided Q subchannel buffer and emplaces it
|
||||
/// </summary>
|
||||
/// <param name="buffer">12 byte Q subchannel: input and output buffer for operation</param>
|
||||
/// <param name="buffer">12 byte Q subchannel buffer: 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)
|
||||
public static ushort SubQ_SynthChecksum(byte[] buf12, int offset)
|
||||
{
|
||||
ushort crc16 = CRC16_CCITT.Calculate(buffer, offset, 10);
|
||||
ushort crc16 = CRC16_CCITT.Calculate(buf12, offset, 10);
|
||||
|
||||
//CRC is stored inverted and big endian
|
||||
buffer[offset + 10] = (byte)(~(crc16 >> 8));
|
||||
buffer[offset + 11] = (byte)(~(crc16));
|
||||
buf12[offset + 10] = (byte)(~(crc16 >> 8));
|
||||
buf12[offset + 11] = (byte)(~(crc16));
|
||||
|
||||
return crc16;
|
||||
}
|
||||
|
||||
public static void SubP(byte[] buffer, int offset, bool pause)
|
||||
/// <summary>
|
||||
/// Caclulates the checksum of the provided Q subchannel buffer
|
||||
/// </summary>
|
||||
public static ushort SubQ_CalcChecksum(byte[] buf12, int offset)
|
||||
{
|
||||
return CRC16_CCITT.Calculate(buf12, offset, 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the provided SubchannelQ structure into a buffer
|
||||
/// Returns the crc, calculated or otherwise.
|
||||
/// </summary>
|
||||
public static ushort SubQ_Serialize(byte[] buf12, int offset, ref SubchannelQ sq)
|
||||
{
|
||||
buf12[offset + 0] = sq.q_status;
|
||||
buf12[offset + 1] = sq.q_tno.BCDValue;
|
||||
buf12[offset + 2] = sq.q_index.BCDValue;
|
||||
buf12[offset + 3] = sq.min.BCDValue;
|
||||
buf12[offset + 4] = sq.sec.BCDValue;
|
||||
buf12[offset + 5] = sq.frame.BCDValue;
|
||||
buf12[offset + 6] = sq.zero;
|
||||
buf12[offset + 7] = sq.ap_min.BCDValue;
|
||||
buf12[offset + 8] = sq.ap_sec.BCDValue;
|
||||
buf12[offset + 9] = sq.ap_frame.BCDValue;
|
||||
|
||||
return SubQ_SynthChecksum(buf12, offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synthesizes the typical subP data into the provided buffer depending on the indicated pause flag
|
||||
/// </summary>
|
||||
public static void SubP(byte[] buffer12, int offset, bool pause)
|
||||
{
|
||||
byte val = (byte)(pause ? 0xFF : 0x00);
|
||||
for (int i = 0; i < 12; i++)
|
||||
buffer[offset + i] = val;
|
||||
buffer12[offset + i] = val;
|
||||
}
|
||||
|
||||
public static void SectorHeader(byte[] buffer, int offset, int LBA, byte mode)
|
||||
/// <summary>
|
||||
/// Synthesizes a data sector header
|
||||
/// </summary>
|
||||
public static void SectorHeader(byte[] buffer16, 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;
|
||||
buffer16[offset + 0] = 0x00;
|
||||
for (int i = 1; i < 11; i++) buffer16[offset + i] = 0xFF;
|
||||
buffer16[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;
|
||||
buffer16[offset + 12] = BCD2.IntToBCD(ts.MIN);
|
||||
buffer16[offset + 13] = BCD2.IntToBCD(ts.SEC);
|
||||
buffer16[offset + 14] = BCD2.IntToBCD(ts.FRAC);
|
||||
buffer16[offset + 15] = mode;
|
||||
}
|
||||
|
||||
public static void EDC_Mode2_Form1(byte[] buffer, int offset)
|
||||
/// <summary>
|
||||
/// Synthesizes the EDC checksum for a Mode 2 Form 1 data sector (and puts it in place)
|
||||
/// </summary>
|
||||
public static void EDC_Mode2_Form1(byte[] buf2352, int offset)
|
||||
{
|
||||
uint edc = ECM.EDC_Calc(buffer, offset + 16, 2048 + 8);
|
||||
ECM.PokeUint(buffer, offset + 2072, edc);
|
||||
uint edc = ECM.EDC_Calc(buf2352, offset + 16, 2048 + 8);
|
||||
ECM.PokeUint(buf2352, offset + 2072, edc);
|
||||
}
|
||||
|
||||
public static void EDC_Mode2_Form2(byte[] buffer, int offset)
|
||||
/// <summary>
|
||||
/// Synthesizes the EDC checksum for a Mode 2 Form 2 data sector (and puts it in place)
|
||||
/// </summary>
|
||||
public static void EDC_Mode2_Form2(byte[] buf2352, int offset)
|
||||
{
|
||||
uint edc = ECM.EDC_Calc(buffer, offset + 16, 2324 + 8);
|
||||
ECM.PokeUint(buffer, offset + 2348, edc);
|
||||
uint edc = ECM.EDC_Calc(buf2352, offset + 16, 2324 + 8);
|
||||
ECM.PokeUint(buf2352, offset + 2348, edc);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Synthesizes the complete ECM data (EDC + ECC) for a Mode 1 data sector (and puts it in place)
|
||||
/// 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)
|
||||
public static void ECM_Mode1(byte[] buf2352, int offset, int LBA)
|
||||
{
|
||||
//EDC
|
||||
uint edc = ECM.EDC_Calc(buffer, offset, 2064);
|
||||
ECM.PokeUint(buffer, offset + 2064, edc);
|
||||
uint edc = ECM.EDC_Calc(buf2352, offset, 2064);
|
||||
ECM.PokeUint(buf2352, offset + 2064, edc);
|
||||
|
||||
//reserved, zero
|
||||
for (int i = 0; i < 8; i++) buffer[offset + 2068 + i] = 0;
|
||||
for (int i = 0; i < 8; i++) buf2352[offset + 2068 + i] = 0;
|
||||
|
||||
//ECC
|
||||
ECM.ECC_Populate(buffer, offset, buffer, offset, false);
|
||||
ECM.ECC_Populate(buf2352, offset, buf2352, offset, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the useful (but unrealistic) deinterleaved subchannel data into the useless (but realistic) interleaved format.
|
||||
/// in_buf and out_buf should not overlap
|
||||
/// </summary>
|
||||
public static void InterleaveSubcode(byte[] in_buf, int in_buf_index, byte[] out_buf, int out_buf_index)
|
||||
{
|
||||
for (int d = 0; d < 12; d++)
|
||||
{
|
||||
for (int bitpoodle = 0; bitpoodle < 8; bitpoodle++)
|
||||
{
|
||||
int rawb = 0;
|
||||
|
||||
for (int ch = 0; ch < 8; ch++)
|
||||
{
|
||||
rawb |= ((in_buf[ch * 12 + d + in_buf_index] >> (7 - bitpoodle)) & 1) << (7 - ch);
|
||||
}
|
||||
out_buf[(d << 3) + bitpoodle + out_buf_index] = (byte)rawb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the useless (but realistic) interleaved subchannel data into a useful (but unrealistic) deinterleaved format.
|
||||
/// in_buf and out_buf should not overlap
|
||||
/// </summary>
|
||||
public static void DeinterleaveSubcode(byte[] in_buf, int in_buf_index, byte[] out_buf, int out_buf_index)
|
||||
{
|
||||
for (int i = 0; i < 96; i++)
|
||||
out_buf[i] = 0;
|
||||
|
||||
for (int ch = 0; ch < 8; ch++)
|
||||
{
|
||||
for (int i = 0; i < 96; i++)
|
||||
{
|
||||
out_buf[(ch * 12) + (i >> 3) + out_buf_index] |= (byte)(((in_buf[i + in_buf_index] >> (7 - ch)) & 0x1) << (7 - (i & 0x7)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the useful (but unrealistic) deinterleaved data into the useless (but realistic) interleaved subchannel format.
|
||||
/// </summary>
|
||||
public unsafe static void InterleaveSubcodeInplace(byte[] buf, int buf_index)
|
||||
{
|
||||
byte* out_buf = stackalloc byte[96];
|
||||
|
||||
for (int i = 0; i < 96; i++)
|
||||
out_buf[i] = 0;
|
||||
|
||||
for (int d = 0; d < 12; d++)
|
||||
{
|
||||
for (int bitpoodle = 0; bitpoodle < 8; bitpoodle++)
|
||||
{
|
||||
int rawb = 0;
|
||||
|
||||
for (int ch = 0; ch < 8; ch++)
|
||||
{
|
||||
rawb |= ((buf[ch * 12 + d + buf_index] >> (7 - bitpoodle)) & 1) << (7 - ch);
|
||||
}
|
||||
out_buf[(d << 3) + bitpoodle] = (byte)rawb;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 96; i++)
|
||||
buf[i + buf_index] = out_buf[i];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the useless (but realistic) interleaved subchannel data into a useful (but unrealistic) deinterleaved format.
|
||||
/// </summary>
|
||||
public unsafe static void DeinterleaveSubcodeInplace(byte[] buf, int buf_index)
|
||||
{
|
||||
byte* out_buf = stackalloc byte[96];
|
||||
|
||||
for (int i = 0; i < 96; i++)
|
||||
out_buf[i] = 0;
|
||||
|
||||
for (int ch = 0; ch < 8; ch++)
|
||||
{
|
||||
for (int i = 0; i < 96; i++)
|
||||
{
|
||||
out_buf[(ch * 12) + (i >> 3)] |= (byte)(((buf[i + buf_index] >> (7 - ch)) & 0x1) << (7 - (i & 0x7)));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 96; i++)
|
||||
buf[i + buf_index] = out_buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
//not being used yet
|
||||
class DiscPreferences
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -19,29 +19,4 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
{
|
||||
BizHawk, MednaDisc, LibMirage
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Main unit of organization for reading data from the disc. Represents one physical disc sector.
|
||||
/// </summary>
|
||||
public class SectorEntry
|
||||
{
|
||||
public SectorEntry(ISector sec) { Sector = sec; }
|
||||
|
||||
internal ISectorSynthJob2448 SectorSynth;
|
||||
|
||||
/// <summary>
|
||||
/// Access the --whatsitcalled-- normal data for the sector with this
|
||||
/// </summary>
|
||||
public ISector Sector;
|
||||
|
||||
/// <summary>
|
||||
/// Access the subcode data for the sector
|
||||
/// </summary>
|
||||
public ISubcodeSector SubcodeSector;
|
||||
|
||||
//todo - add a PARAMETER fields to this (a long, maybe) so that the ISector can use them (so that each ISector doesnt have to be constructed also)
|
||||
//also then, maybe this could be a struct
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
//we may still need to deinterleave it if subcode was requested and it needs deinterleaving
|
||||
if ((job.Parts & (ESectorSynthPart.SubcodeDeinterleave | ESectorSynthPart.SubcodeAny)) != 0)
|
||||
{
|
||||
SubcodeUtils.DeinterleaveInplace(job.DestBuffer2448, 2352);
|
||||
SynthUtils.DeinterleaveSubcodeInplace(job.DestBuffer2448, 2352);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
void RunMednaDisc()
|
||||
{
|
||||
var disc = new Disc();
|
||||
OUT_Disc = disc;
|
||||
|
||||
//create a MednaDisc and give it to the disc for ownership
|
||||
var md = new MednaDisc(IN_FromPath);
|
||||
|
@ -43,27 +44,17 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
|
||||
//make sector interfaces:
|
||||
var pregap_sector_zero = new Sector_Zero();
|
||||
var pregap_subcode_zero = new ZeroSubcodeSector();
|
||||
for (int i = 0; i < 150; i++)
|
||||
{
|
||||
var se = new SectorEntry(pregap_sector_zero);
|
||||
se.SectorSynth = synth;
|
||||
disc.Sectors.Add(se);
|
||||
se.SubcodeSector = pregap_subcode_zero;
|
||||
disc.Sectors.Add(synth);
|
||||
}
|
||||
|
||||
//2. actual sectors
|
||||
for (int i = 0; i < nSectors; i++)
|
||||
{
|
||||
//var sectorInterface = new MednaDiscSectorInterface() { LBA = i, md = md };
|
||||
var se = new SectorEntry(null);
|
||||
//se.SubcodeSector = new MednaDiscSubcodeSectorInterface() { LBA = i, md = md };
|
||||
se.SectorSynth = synth;
|
||||
disc.Sectors.Add(se);
|
||||
disc.Sectors.Add(synth);
|
||||
}
|
||||
|
||||
BufferedSubcodeSector bss = new BufferedSubcodeSector(); //TODO - its hacky that we need this..
|
||||
|
||||
//ADR (q-Mode) is necessarily 0x01 for a RawTOCEntry
|
||||
const int kADR = 1;
|
||||
const int kUnknownControl = 0;
|
||||
|
@ -96,15 +87,13 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
ap_min = BCD2.FromDecimal(m_ts.MIN),
|
||||
ap_sec = BCD2.FromDecimal(m_ts.SEC),
|
||||
ap_frame = BCD2.FromDecimal(m_ts.FRAC),
|
||||
q_crc = 0 //meaningless
|
||||
};
|
||||
|
||||
//a special fixup: mednafen's entry 100 is the lead-out track
|
||||
if (i == 100)
|
||||
q.q_index.BCDValue = 0xA2;
|
||||
|
||||
//CRC cant be calculated til we've got all the fields setup
|
||||
q.q_crc = bss.Synthesize_SubchannelQ(ref q, true);
|
||||
|
||||
disc.RawTOCEntries.Add(new RawTOCEntry { QData = q });
|
||||
}
|
||||
|
||||
|
@ -121,8 +110,8 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
ap_min = BCD2.FromDecimal(md.TOC.first_track),
|
||||
ap_sec = BCD2.FromDecimal(0),
|
||||
ap_frame = BCD2.FromDecimal(0),
|
||||
q_crc = 0, //meaningless
|
||||
};
|
||||
qA1.q_crc = bss.Synthesize_SubchannelQ(ref qA1, true);
|
||||
disc.RawTOCEntries.Add(new RawTOCEntry { QData = qA1 });
|
||||
var qA2 = new SubchannelQ
|
||||
{
|
||||
|
@ -136,44 +125,10 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
ap_min = BCD2.FromDecimal(md.TOC.last_track),
|
||||
ap_sec = BCD2.FromDecimal(0),
|
||||
ap_frame = BCD2.FromDecimal(0),
|
||||
q_crc = 0, //meaningless
|
||||
};
|
||||
qA2.q_crc = bss.Synthesize_SubchannelQ(ref qA2, true);
|
||||
disc.RawTOCEntries.Add(new RawTOCEntry { QData = qA2 });
|
||||
|
||||
//generate the toc from the entries. still not sure we're liking this idea
|
||||
var tocSynth = new DiscTOCRaw.SynthesizeFromRawTOCEntriesJob() { Entries = disc.RawTOCEntries };
|
||||
tocSynth.Run();
|
||||
disc.TOCRaw = tocSynth.Result;
|
||||
|
||||
//DO THIS IN A MORE UNIFORM WAY PLEASE
|
||||
//setup the DiscStructure
|
||||
//disc.Structure = new DiscStructure();
|
||||
//var ses = new DiscStructure.Session();
|
||||
//disc.Structure.Sessions.Add(ses);
|
||||
//for (int i = 1; i < 100; i++)
|
||||
//{
|
||||
// var m_te = md.TOCTracks[i];
|
||||
// if (!m_te.Valid) continue;
|
||||
|
||||
// DiscStructure.Track track = new DiscStructure.Track() { Number = i };
|
||||
// ses.Tracks.Add(track);
|
||||
// if ((m_te.control & (int)EControlQ.DATA) == 0)
|
||||
// track.IsData = false;
|
||||
// else
|
||||
// track.IsData = true;
|
||||
|
||||
// track.Start_LBA = (int)m_te.lba;
|
||||
// track.
|
||||
|
||||
// //from mednafen, we couldnt build the index 0, and that's OK, since that's not really a sensible thing in CD terms anyway.
|
||||
// //I need to refactor this thing to oblivion
|
||||
// //track.Indexes.Add(new DiscStructure.Index { Number = 0, LBA = (int)m_te.lba }); //<-- not accurate, but due for deletion
|
||||
// //track.Indexes.Add(new DiscStructure.Index { Number = 1, LBA = (int)m_te.lba });
|
||||
//}
|
||||
|
||||
//NOT FULLY COMPLETE
|
||||
|
||||
OUT_Disc = disc;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,8 +41,6 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
{
|
||||
SubchannelQ sq = new SubchannelQ();
|
||||
|
||||
BufferedSubcodeSector bss = new BufferedSubcodeSector(); //TODO - its hacky that we need this.. (NOT USED YET)
|
||||
|
||||
//ADR (q-Mode) is necessarily 0x01 for a RawTOCEntry
|
||||
const int kADR = 1;
|
||||
const int kUnknownControl = 0;
|
||||
|
|
|
@ -17,224 +17,6 @@
|
|||
|
||||
namespace BizHawk.Emulation.DiscSystem
|
||||
{
|
||||
//YET ANOTHER BAD IDEA
|
||||
public interface ISubcodeSector
|
||||
{
|
||||
/// <summary>
|
||||
/// reads 96 bytes of subcode data (deinterleaved) for this sector into the supplied buffer
|
||||
/// </summary>
|
||||
void ReadSubcodeDeinterleaved(byte[] buffer, int offset);
|
||||
|
||||
/// <summary>
|
||||
/// Reads just one of the channels. p=0, q=1, etc.
|
||||
/// </summary>
|
||||
void ReadSubcodeChannel(int number, byte[] buffer, int offset);
|
||||
}
|
||||
|
||||
public static class SubcodeUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the useful (but unrealistic) deinterleaved data into the useless (but realistic) interleaved subchannel format.
|
||||
/// in_buf and out_buf should not overlap
|
||||
/// </summary>
|
||||
public static void Interleave(byte[] in_buf, int in_buf_index, byte[] out_buf, int out_buf_index)
|
||||
{
|
||||
for (int d = 0; d < 12; d++)
|
||||
{
|
||||
for (int bitpoodle = 0; bitpoodle < 8; bitpoodle++)
|
||||
{
|
||||
int rawb = 0;
|
||||
|
||||
for (int ch = 0; ch < 8; ch++)
|
||||
{
|
||||
rawb |= ((in_buf[ch * 12 + d + in_buf_index] >> (7 - bitpoodle)) & 1) << (7 - ch);
|
||||
}
|
||||
out_buf[(d << 3) + bitpoodle + out_buf_index] = (byte)rawb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the useless (but realistic) interleaved subchannel data into a useful (but unrealistic) deinterleaved format.
|
||||
/// in_buf and out_buf should not overlap
|
||||
/// </summary>
|
||||
public static void Deinterleave(byte[] in_buf, int in_buf_index, byte[] out_buf, int out_buf_index)
|
||||
{
|
||||
for (int i = 0; i < 96; i++)
|
||||
out_buf[i] = 0;
|
||||
|
||||
for (int ch = 0; ch < 8; ch++)
|
||||
{
|
||||
for (int i = 0; i < 96; i++)
|
||||
{
|
||||
out_buf[(ch * 12) + (i >> 3) + out_buf_index] |= (byte)(((in_buf[i + in_buf_index] >> (7 - ch)) & 0x1) << (7 - (i & 0x7)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the useful (but unrealistic) deinterleaved data into the useless (but realistic) interleaved subchannel format.
|
||||
/// </summary>
|
||||
public unsafe static void InterleaveInplace(byte[] buf, int buf_index)
|
||||
{
|
||||
byte* out_buf = stackalloc byte[96];
|
||||
|
||||
for (int i = 0; i < 96; i++)
|
||||
out_buf[i] = 0;
|
||||
|
||||
for (int d = 0; d < 12; d++)
|
||||
{
|
||||
for (int bitpoodle = 0; bitpoodle < 8; bitpoodle++)
|
||||
{
|
||||
int rawb = 0;
|
||||
|
||||
for (int ch = 0; ch < 8; ch++)
|
||||
{
|
||||
rawb |= ((buf[ch * 12 + d + buf_index] >> (7 - bitpoodle)) & 1) << (7 - ch);
|
||||
}
|
||||
out_buf[(d << 3) + bitpoodle] = (byte)rawb;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 96; i++)
|
||||
buf[i + buf_index] = out_buf[i];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the useless (but realistic) interleaved subchannel data into a useful (but unrealistic) deinterleaved format.
|
||||
/// </summary>
|
||||
public unsafe static void DeinterleaveInplace(byte[] buf, int buf_index)
|
||||
{
|
||||
byte* out_buf = stackalloc byte[96];
|
||||
|
||||
for (int i = 0; i < 96; i++)
|
||||
out_buf[i] = 0;
|
||||
|
||||
for (int ch = 0; ch < 8; ch++)
|
||||
{
|
||||
for (int i = 0; i < 96; i++)
|
||||
{
|
||||
out_buf[(ch * 12) + (i >> 3)] |= (byte)(((buf[i + buf_index] >> (7 - ch)) & 0x1) << (7 - (i & 0x7)));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 96; i++)
|
||||
buf[i + buf_index] = out_buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads subcode from an internally-managed buffer
|
||||
/// </summary>
|
||||
class BufferedSubcodeSector : ISubcodeSector
|
||||
{
|
||||
public void Synthesize_SubchannelP(bool pause)
|
||||
{
|
||||
byte val = pause ? (byte)0xFF : (byte)0x00;
|
||||
for (int i = 0; i < 12; i++)
|
||||
SubcodeDeinterleaved[i] = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills this subcode buffer with subchannel Q data. calculates the required CRC, as well.
|
||||
/// Returns the crc, calculated or otherwise.
|
||||
/// </summary>
|
||||
public ushort Synthesize_SubchannelQ(ref SubchannelQ sq, bool calculateCRC)
|
||||
{
|
||||
int offset = 12; //Q subchannel begins after P, 12 bytes in
|
||||
SubcodeDeinterleaved[offset + 0] = sq.q_status;
|
||||
SubcodeDeinterleaved[offset + 1] = sq.q_tno.BCDValue;
|
||||
SubcodeDeinterleaved[offset + 2] = sq.q_index.BCDValue;
|
||||
SubcodeDeinterleaved[offset + 3] = sq.min.BCDValue;
|
||||
SubcodeDeinterleaved[offset + 4] = sq.sec.BCDValue;
|
||||
SubcodeDeinterleaved[offset + 5] = sq.frame.BCDValue;
|
||||
SubcodeDeinterleaved[offset + 6] = sq.zero;
|
||||
SubcodeDeinterleaved[offset + 7] = sq.ap_min.BCDValue;
|
||||
SubcodeDeinterleaved[offset + 8] = sq.ap_sec.BCDValue;
|
||||
SubcodeDeinterleaved[offset + 9] = sq.ap_frame.BCDValue;
|
||||
|
||||
ushort crc16;
|
||||
if (calculateCRC)
|
||||
crc16 = CRC16_CCITT.Calculate(SubcodeDeinterleaved, offset, 10);
|
||||
else crc16 = sq.q_crc;
|
||||
|
||||
//CRC is stored inverted and big endian
|
||||
SubcodeDeinterleaved[offset + 10] = (byte)(~(crc16 >> 8));
|
||||
SubcodeDeinterleaved[offset + 11] = (byte)(~(crc16));
|
||||
|
||||
return crc16;
|
||||
}
|
||||
|
||||
public void Synthesize_SunchannelQ_Checksum()
|
||||
{
|
||||
int offset = 12; //Q subchannel begins after P, 12 bytes in
|
||||
|
||||
ushort crc16 = CRC16_CCITT.Calculate(SubcodeDeinterleaved, offset, 10);
|
||||
|
||||
//CRC is stored inverted and big endian
|
||||
SubcodeDeinterleaved[offset + 10] = (byte)(~(crc16 >> 8));
|
||||
SubcodeDeinterleaved[offset + 11] = (byte)(~(crc16));
|
||||
}
|
||||
|
||||
public void ReadSubcodeDeinterleaved(byte[] buffer, int offset)
|
||||
{
|
||||
Buffer.BlockCopy(SubcodeDeinterleaved, 0, buffer, offset, 96);
|
||||
}
|
||||
|
||||
public void ReadSubcodeChannel(int number, byte[] buffer, int offset)
|
||||
{
|
||||
Buffer.BlockCopy(SubcodeDeinterleaved, number * 12, buffer, offset, 12);
|
||||
}
|
||||
|
||||
public BufferedSubcodeSector()
|
||||
{
|
||||
SubcodeDeinterleaved = new byte[96];
|
||||
}
|
||||
|
||||
public static BufferedSubcodeSector CloneFromBytesDeinterleaved(byte[] buffer)
|
||||
{
|
||||
var ret = new BufferedSubcodeSector();
|
||||
Buffer.BlockCopy(buffer, 0, ret.SubcodeDeinterleaved, 0, 96);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public byte[] SubcodeDeinterleaved;
|
||||
}
|
||||
|
||||
public class ZeroSubcodeSector : ISubcodeSector
|
||||
{
|
||||
public void ReadSubcodeDeinterleaved(byte[] buffer, int offset)
|
||||
{
|
||||
for (int i = 0; i < 96; i++) buffer[i + offset] = 0;
|
||||
}
|
||||
|
||||
public void ReadSubcodeChannel(int number, byte[] buffer, int offset)
|
||||
{
|
||||
for (int i = 0; i < 12; i++)
|
||||
buffer[i + offset] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads subcode data from a blob, assuming it was already stored in deinterleaved format
|
||||
/// </summary>
|
||||
public class BlobSubcodeSectorPreDeinterleaved : ISubcodeSector
|
||||
{
|
||||
public void ReadSubcodeDeinterleaved(byte[] buffer, int offset)
|
||||
{
|
||||
Blob.Read(Offset, buffer, offset, 96);
|
||||
}
|
||||
|
||||
public void ReadSubcodeChannel(int number, byte[] buffer, int offset)
|
||||
{
|
||||
Blob.Read(Offset + number * 12, buffer, offset, 12);
|
||||
}
|
||||
|
||||
public IBlob Blob;
|
||||
public long Offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Control bit flags for the Q Subchannel.
|
||||
/// </summary>
|
||||
|
@ -277,8 +59,8 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
/// <summary>
|
||||
/// These are the initial set of timestamps. Meaning varies:
|
||||
/// check yellowbook 22.3.3 and 22.3.4
|
||||
/// normal track: relative timestamp
|
||||
/// leadin track: unknown
|
||||
/// user information track: relative timestamp
|
||||
/// leadout: relative timestamp
|
||||
/// TODO - why are these BCD2? having things in BCD2 is freaking annoying, I should only make them BCD2 when serializing into a subchannel Q buffer
|
||||
/// EDIT - elsewhere I rambled "why not BCD2?". geh. need to make a final organized approach
|
||||
|
@ -293,17 +75,16 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
/// <summary>
|
||||
/// These are the second set of timestamps. Meaning varies:
|
||||
/// check yellowbook 22.3.3 and 22.3.4
|
||||
/// normal track: absolute timestamp
|
||||
/// leadin track q-mode 1: TOC entry, absolute MSF of track
|
||||
/// user information track: absolute timestamp
|
||||
/// leadout: absolute timestamp
|
||||
/// </summary>
|
||||
public BCD2 ap_min, ap_sec, ap_frame;
|
||||
|
||||
/// <summary>
|
||||
/// The CRC. This is the actual CRC value as would be calculated from our library (it is inverted and written big endian to the disc)
|
||||
/// Don't assume this CRC is correct-- If this SubchannelQ was read from a dumped disc, the CRC might be wrong.
|
||||
/// CCD doesnt specify this for TOC entries, so it will be wrong. It may or may not be right for data track sectors from a CCD file.
|
||||
/// Or we may have computed this SubchannelQ data and generated the correct CRC at that time.
|
||||
/// Don't assume this CRC is correct, in the case of some copy protections it is intended to be wrong.
|
||||
/// Furthermore, it is meaningless (and in BizHawk, unpopulated) for a TOC Entry
|
||||
/// (since an invalid CRC on a [theyre redundantly/duplicately stored] toc entry would cause it to get discarded in favor of another one with a correct CRC)
|
||||
/// </summary>
|
||||
public ushort q_crc;
|
||||
|
||||
|
@ -390,24 +171,4 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
}
|
||||
}
|
||||
|
||||
public class SubcodeDataDecoder
|
||||
{
|
||||
/// <summary>
|
||||
/// This seems to deinterleave Q from a subcode buffer? Not sure.. it isn't getting used anywhere right now, as you can see.
|
||||
/// </summary>
|
||||
public static void Unpack_Q(byte[] output, int out_ofs, byte[] input, int in_ofs)
|
||||
{
|
||||
for (int i = 0; i < 12; i++)
|
||||
output[out_ofs + i] = 0;
|
||||
for (int i = 0; i < 96; i++)
|
||||
{
|
||||
int bytenum = i >> 3;
|
||||
int bitnum = i & 7;
|
||||
bitnum = 7 - bitnum;
|
||||
int bitval = (byte)((input[in_ofs + i] >> 6) & 1);
|
||||
bitval <<= bitnum;
|
||||
output[out_ofs + bytenum] |= (byte)bitval;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue