Fixup multisession handling with MDS and improve its track/sector size handling, also maybe fix it entirely? (not sure if this ever worked)

Move the various synths using the CUE SS_Base to a file, mostly just a nicer organizational change as MDS and CDI use one of these but not CUE
Misc improvements elsewhere
This commit is contained in:
CasualPokePlayer 2023-05-12 02:44:05 -07:00
parent 1f3068fe77
commit 2012b083f6
7 changed files with 343 additions and 127 deletions

View File

@ -443,52 +443,6 @@ namespace BizHawk.Emulation.DiscSystem
return ret;
}
private class SS_CDI_RawQ : SS_Base
{
public override void Synth(SectorSynthJob job)
{
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset, 2352);
if ((job.Parts & ESectorSynthPart.SubchannelP) != 0)
{
SynthUtils.SubP(job.DestBuffer2448, job.DestOffset + 2352, Pause);
}
// Q is present in the blob and non-interleaved
if ((job.Parts & ESectorSynthPart.SubchannelQ) != 0)
{
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset + 2352 + 12, 12);
}
//clear R-W if needed
if ((job.Parts & ESectorSynthPart.Subchannel_RSTUVW) != 0)
{
Array.Clear(job.DestBuffer2448, job.DestOffset + 2352 + 12 + 12, 12 * 6);
}
//subcode has been generated deinterleaved; we may still need to interleave it
if ((job.Parts & ESectorSynthPart.SubcodeAny) != 0 && (job.Parts & ESectorSynthPart.SubcodeDeinterleave) == 0)
{
SynthUtils.InterleaveSubcodeInplace(job.DestBuffer2448, job.DestOffset + 2352);
}
}
}
private class SS_CDI_RawPQRSTUVW : SS_Base
{
public override void Synth(SectorSynthJob job)
{
// all subcode is present and interleaved, just read it all
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset, 2448);
// deinterleave it if needed
if ((job.Parts & ESectorSynthPart.SubcodeDeinterleave) != 0)
{
SynthUtils.DeinterleaveSubcodeInplace(job.DestBuffer2448, job.DestOffset + 2352);
}
}
}
/// <exception cref="CDIParseException">file <paramref name="cdiPath"/> not found</exception>
public static Disc LoadCDIToDisc(string cdiPath, DiscMountPolicy IN_DiscMountPolicy)
{
@ -559,8 +513,8 @@ namespace BizHawk.Emulation.DiscSystem
0 => new SS_Mode1_2048(),
1 => new SS_Mode2_2336(),
2 => new SS_2352(),
3 => new SS_CDI_RawQ(),
4 => new SS_CDI_RawPQRSTUVW(),
3 => new SS_2364_DeinterleavedQ(),
4 => new SS_2448_Interleaved(),
_ => throw new InvalidOperationException()
};
synth.Blob = cdiBlob;

View File

@ -0,0 +1,127 @@
using System;
namespace BizHawk.Emulation.DiscSystem.CUE
{
// extra synths using SS_Base, not used by CUEs but used for other formats
/// <summary>
/// Represents a Mode2 Form1 2048-byte sector
/// Only used by MDS
/// </summary>
internal class SS_Mode2_Form1_2048 : SS_Base
{
public override void Synth(SectorSynthJob job)
{
var ecm = (job.Parts & ESectorSynthPart.ECMAny) != 0;
if (ecm)
{
// ecm needs these parts for synth
job.Parts |= ESectorSynthPart.User2048;
job.Parts |= ESectorSynthPart.Header16;
}
//read the sector user data
if ((job.Parts & ESectorSynthPart.User2048) != 0)
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset + 24, 2048);
if ((job.Parts & ESectorSynthPart.Header16) != 0)
SynthUtils.SectorHeader(job.DestBuffer2448, job.DestOffset + 0, job.LBA, 2);
if (ecm)
SynthUtils.ECM_Mode2_Form1(job.DestBuffer2448, job.DestOffset);
SynthSubchannelAsNeed(job);
}
}
/// <summary>
/// Represents a Mode2 Form1 2324-byte sector
/// Only used by MDS
/// </summary>
internal class SS_Mode2_Form2_2324 : SS_Base
{
public override void Synth(SectorSynthJob job)
{
//read the sector userdata (note: ECC data is now userdata in this regard)
if ((job.Parts & ESectorSynthPart.User2336) != 0)
{
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset + 24, 2324);
// only needs userdata for synth
SynthUtils.ECM_Mode2_Form2(job.DestBuffer2448, job.DestOffset);
}
if ((job.Parts & ESectorSynthPart.Header16) != 0)
SynthUtils.SectorHeader(job.DestBuffer2448, job.DestOffset + 0, job.LBA, 2);
SynthSubchannelAsNeed(job);
}
}
/// <summary>
/// Represents a Mode2 Form1 2328-byte sector
/// Only used by MDS
/// </summary>
internal class SS_Mode2_Form2_2328 : SS_Base
{
public override void Synth(SectorSynthJob job)
{
//read the sector userdata (note: ECC data is now userdata in this regard)
if ((job.Parts & ESectorSynthPart.User2336) != 0)
{
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset + 24, 2328);
// only subheader needs to be synthed
SynthUtils.SectorSubHeader(job.DestBuffer2448, job.DestOffset + 16, 2);
}
if ((job.Parts & ESectorSynthPart.Header16) != 0)
SynthUtils.SectorHeader(job.DestBuffer2448, job.DestOffset + 0, job.LBA, 2);
SynthSubchannelAsNeed(job);
}
}
/// <summary>
/// Represents a full 2448-byte sector with interleaved subcode
/// Only used by MDS and CDI
/// </summary>
internal class SS_2448_Interleaved : SS_Base
{
public override void Synth(SectorSynthJob job)
{
// all subcode is present and interleaved, just read it all
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset, 2448);
// deinterleave it if needed
if ((job.Parts & ESectorSynthPart.SubcodeDeinterleave) != 0)
SynthUtils.DeinterleaveSubcodeInplace(job.DestBuffer2448, job.DestOffset + 2352);
}
}
/// <summary>
/// Represents a 2364-byte (2352 + 12) sector with deinterleaved Q subcode
/// Only used by CDI
/// </summary>
internal class SS_2364_DeinterleavedQ : SS_Base
{
public override void Synth(SectorSynthJob job)
{
if ((job.Parts & ESectorSynthPart.User2352) != 0)
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset, 2352);
if ((job.Parts & ESectorSynthPart.SubchannelP) != 0)
SynthUtils.SubP(job.DestBuffer2448, job.DestOffset + 2352, Pause);
// Q is present in the blob and non-interleaved
if ((job.Parts & ESectorSynthPart.SubchannelQ) != 0)
Blob.Read(BlobOffset + 2352, job.DestBuffer2448, job.DestOffset + 2352 + 12, 12);
// clear R-W if needed
if ((job.Parts & ESectorSynthPart.Subchannel_RSTUVW) != 0)
Array.Clear(job.DestBuffer2448, job.DestOffset + 2352 + 12 + 12, 12 * 6);
// subcode has been generated deinterleaved; we may still need to interleave it
if ((job.Parts & ESectorSynthPart.SubcodeAny) != 0 && (job.Parts & ESectorSynthPart.SubcodeDeinterleave) == 0)
SynthUtils.InterleaveSubcodeInplace(job.DestBuffer2448, job.DestOffset + 2352);
}
}
}

View File

@ -39,11 +39,10 @@ namespace BizHawk.Emulation.DiscSystem.CUE
}
//subcode has been generated deinterleaved; we may still need to interleave it
if((job.Parts & ESectorSynthPart.SubcodeAny) != 0)
if ((job.Parts & (ESectorSynthPart.SubcodeDeinterleave)) == 0)
{
SynthUtils.InterleaveSubcodeInplace(job.DestBuffer2448, job.DestOffset + 2352);
}
if ((job.Parts & ESectorSynthPart.SubcodeAny) != 0 && (job.Parts & ESectorSynthPart.SubcodeDeinterleave) == 0)
{
SynthUtils.InterleaveSubcodeInplace(job.DestBuffer2448, job.DestOffset + 2352);
}
}
}
@ -54,15 +53,23 @@ namespace BizHawk.Emulation.DiscSystem.CUE
{
public override void Synth(SectorSynthJob job)
{
var ecm = (job.Parts & ESectorSynthPart.ECMAny) != 0;
if (ecm)
{
// ecm needs these parts for synth
job.Parts |= ESectorSynthPart.User2048;
job.Parts |= ESectorSynthPart.Header16;
}
//read the sector user data
if((job.Parts & ESectorSynthPart.User2048) != 0)
if ((job.Parts & ESectorSynthPart.User2048) != 0)
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset + 16, 2048);
if ((job.Parts & ESectorSynthPart.Header16) != 0)
SynthUtils.SectorHeader(job.DestBuffer2448, job.DestOffset + 0, job.LBA, 1);
if ((job.Parts & ESectorSynthPart.ECMAny) != 0)
SynthUtils.ECM_Mode1(job.DestBuffer2448, job.DestOffset + 0, job.LBA);
if (ecm)
SynthUtils.ECM_Mode1(job.DestBuffer2448, job.DestOffset + 0);
SynthSubchannelAsNeed(job);
}
@ -75,8 +82,9 @@ namespace BizHawk.Emulation.DiscSystem.CUE
{
public override void Synth(SectorSynthJob job)
{
//read the sector user data
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset + 16, 2336);
//read the sector sector user data + ECM data
if ((job.Parts & ESectorSynthPart.User2336) != 0)
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset + 16, 2336);
if ((job.Parts & ESectorSynthPart.Header16) != 0)
SynthUtils.SectorHeader(job.DestBuffer2448, job.DestOffset + 0, job.LBA, 2);
@ -94,7 +102,8 @@ namespace BizHawk.Emulation.DiscSystem.CUE
public override void Synth(SectorSynthJob job)
{
//read the sector user data
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset, 2352);
if ((job.Parts & ESectorSynthPart.User2352) != 0)
Blob.Read(BlobOffset, job.DestBuffer2448, job.DestOffset, 2352);
//if subcode is needed, synthesize it
SynthSubchannelAsNeed(job);
@ -159,7 +168,7 @@ namespace BizHawk.Emulation.DiscSystem.CUE
case 1:
{
if ((job.Parts & ESectorSynthPart.ECMAny) != 0)
SynthUtils.ECM_Mode1(job.DestBuffer2448, job.DestOffset + 0, job.LBA);
SynthUtils.ECM_Mode1(job.DestBuffer2448, job.DestOffset + 0);
break;
}
case 2 when form == 2:
@ -170,6 +179,4 @@ namespace BizHawk.Emulation.DiscSystem.CUE
SynthSubchannelAsNeed(job);
}
}
}

View File

@ -154,9 +154,9 @@ namespace BizHawk.Emulation.DiscSystem
public class ATrack
{
/// <summary>
/// The specified data mode
/// The specified data mode (only lower 3 bits are actually meaningful)
/// 0x00 - None (no data)
/// 0x02 - DVD
/// 0x02 - DVD (when header specifies DVD, Mode1 otherwise)
/// 0xA9 - Audio
/// 0xAA - Mode1
/// 0xAB - Mode2
@ -271,6 +271,19 @@ namespace BizHawk.Emulation.DiscSystem
public int PSec;
public int PFrame;
/// <summary>
/// Lower 3 bits of ATrack Mode
/// Upper 5 bits are meaningless (see mirage_parser_mds_convert_track_mode)
/// 0x0 - None or Mode2 (Depends on sector size)
/// 0x1 - Audio
/// 0x2 - DVD or Mode1 (Depends on medium)
/// 0x3 - Mode2
/// 0x4 - Mode2 Form1
/// 0x5 - Mode2 Form2
/// 0x6 - UNKNOWN
/// 0x7 - Mode2
/// </summary>
public int TrackMode;
public int SectorSize;
public long TrackOffset;
@ -313,6 +326,12 @@ namespace BizHawk.Emulation.DiscSystem
throw new MDSParseException($"MDS Parse Error: Only MDS version 1.x is supported!\nDetected version: {aFile.Header.Version[0]}.{aFile.Header.Version[1]}");
}
isDvd = aFile.Header.Medium is 0x10 or 0x12;
if (isDvd)
{
throw new MDSParseException("DVD Detected. Not currently supported!");
}
// parse sessions
var aSessions = new Dictionary<int, ASession>();
@ -377,12 +396,6 @@ namespace BizHawk.Emulation.DiscSystem
track.Files = bc.ToInt32(trackHeader.Skip(48).Take(4).ToArray());
track.FooterOffset = bc.ToInt32(trackHeader.Skip(52).Take(4).ToArray());
if (track.Mode == 0x02)
{
isDvd = true;
throw new MDSParseException("DVD Detected. Not currently supported!");
}
// check for track extra block - this can probably be handled in a separate loop,
// but I'll just store the current stream position then seek forward to the extra block for this track
var currPos = stream.Position;
@ -484,8 +497,15 @@ namespace BizHawk.Emulation.DiscSystem
stream.Position = currPos;
var point = track.Point;
// each session has its own 0xA0/0xA1/0xA3 track
// so this can't be used directly as a key
if (point is 0xA0 or 0xA1 or 0xA2)
{
point |= session.SessionNumber << 8;
}
aTracks.Add(track.Point, track);
aTracks.Add(point, track);
aFile.Tracks.Add(track);
if (footerOffset == 0)
@ -521,32 +541,57 @@ namespace BizHawk.Emulation.DiscSystem
// now build the TOC object
foreach (var se in aFile.ParsedSession)
foreach (var t in aTracks.Values
.Where(a => se.StartTrack <= a.TrackNo && a.TrackNo <= se.EndTrack)
.OrderBy(a => a.TrackNo))
{
ATOCEntry CreateTOCEntryFromTrack(ATrack track)
{
aFile.TOCEntries.Add(new(t.Point)
return new(track.Point)
{
ADR_Control = t.ADR_Control,
AFrame = t.AFrame,
AMin = t.AMin,
ASec = t.ASec,
BlobIndex = t.BlobIndex,
EntryNum = t.TrackNo,
ExtraBlock = t.ExtraBlock,
ImageFileNamePaths = t.ImageFileNamePaths,
PFrame = t.PFrame,
PLBA = Convert.ToInt32(t.PLBA),
PMin = t.PMin,
Point = t.Point,
PSec = t.PSec,
SectorSize = t.SectorSize,
ADR_Control = track.ADR_Control,
AFrame = track.AFrame,
AMin = track.AMin,
ASec = track.ASec,
BlobIndex = track.BlobIndex,
EntryNum = track.TrackNo,
ExtraBlock = track.ExtraBlock,
ImageFileNamePaths = track.ImageFileNamePaths,
PFrame = track.PFrame,
PLBA = Convert.ToInt32(track.PLBA),
PMin = track.PMin,
Point = track.Point,
PSec = track.PSec,
TrackMode = track.Mode & 0x7,
SectorSize = track.SectorSize,
Session = se.SessionSequence,
TrackOffset = Convert.ToInt64(t.StartOffset),
Zero = t.Zero
});
TrackOffset = Convert.ToInt64(track.StartOffset),
Zero = track.Zero
};
}
void AddAXTrack(int x)
{
if (aTracks.TryGetValue(se.SessionSequence << 8 | 0xA0 | x, out var axTrack))
{
aFile.TOCEntries.Add(CreateTOCEntryFromTrack(axTrack));
}
}
// add in the 0xA0/0xA1/0xA2 tracks
AddAXTrack(0);
AddAXTrack(1);
AddAXTrack(2);
// add in the rest of the tracks
foreach (var t in aTracks
.Where(a => se.StartTrack <= a.Key && a.Key <= se.EndTrack)
.OrderBy(a => a.Key)
.Select(a => a.Value))
{
aFile.TOCEntries.Add(CreateTOCEntryFromTrack(t));
}
// TODO: first session might have 0xB0/0xC0 tracks... not sure how to handle these
}
return aFile;
}
@ -714,6 +759,32 @@ namespace BizHawk.Emulation.DiscSystem
var currBlobIndex = 0;
foreach (var session in mdsf.ParsedSession)
{
// leadin track
// we create this only for session 2+, not session 1
var leadinSize = session.SessionSequence == 1 ? 0 : 4500;
for (var i = 0; i < leadinSize; i++)
{
// this is most certainly wrong
// nothing relies on the exact contents for now (only multisession core is VirtualJaguar which doesn't touch leadin)
// just needs sectors to be present due to track info LBAs of session 2+ accounting for this being present
var pregapTrackType = CUE.CueTrackType.Audio;
if (tocSynth.Result.TOCItems[1].IsData)
{
pregapTrackType = tocSynth.Result.SessionFormat switch
{
SessionFormat.Type20_CDXA => CUE.CueTrackType.Mode2_2352,
SessionFormat.Type10_CDI => CUE.CueTrackType.CDI_2352,
SessionFormat.Type00_CDROM_CDDA => CUE.CueTrackType.Mode1_2352,
_ => pregapTrackType
};
}
disc._Sectors.Add(new CUE.SS_Gap()
{
Policy = IN_DiscMountPolicy,
TrackType = pregapTrackType
});
}
for (var i = session.StartTrack; i <= session.EndTrack; i++)
{
var relMSF = -1;
@ -800,8 +871,6 @@ namespace BizHawk.Emulation.DiscSystem
var currBlobOffset = track.TrackOffset;
for (var sector = session.StartSector; sector <= session.EndSector; sector++)
{
CUE.SS_Base sBase;
// get the current blob from the BlobIndex
var currBlob = (Blob_RawFile) BlobIndex[currBlobIndex];
var currBlobLength = currBlob.Length;
@ -809,33 +878,29 @@ namespace BizHawk.Emulation.DiscSystem
currBlobIndex++;
var mdfBlob = (IBlob) disc.DisposableResources[currBlobIndex];
//int userSector = 2048;
switch (track.SectorSize)
CUE.SS_Base sBase = track.SectorSize switch
{
case 2448:
sBase = new CUE.SS_2352()
{
Policy = IN_DiscMountPolicy
};
//userSector = 2352;
break;
case 2048:
default:
sBase = new CUE.SS_Mode1_2048()
{
Policy = IN_DiscMountPolicy
};
//userSector = 2048;
break;
//throw new Exception($"Not supported: Sector Size {track.SectorSize}");
}
2352 when track.TrackMode is 1 => new CUE.SS_2352(),
2048 when track.TrackMode is 2 => new CUE.SS_Mode1_2048(),
2336 when track.TrackMode is 0 or 3 or 7 => new CUE.SS_Mode2_2336(),
2048 when track.TrackMode is 4 => new CUE.SS_Mode2_Form1_2048(),
2324 when track.TrackMode is 5 => new CUE.SS_Mode2_Form2_2324(),
2328 when track.TrackMode is 5 => new CUE.SS_Mode2_Form2_2328(),
// best guesses
2048 => new CUE.SS_Mode1_2048(),
2336 => new CUE.SS_Mode2_2336(),
2352 => new CUE.SS_2352(),
2448 => new CUE.SS_2448_Interleaved(),
_ => throw new InvalidOperationException($"Not supported: Sector Size {track.SectorSize}, Track Mode {track.TrackMode}")
};
sBase.Policy = IN_DiscMountPolicy;
// configure blob
sBase.Blob = mdfBlob;
sBase.BlobOffset = currBlobOffset;
currBlobOffset += track.SectorSize; // userSector;
currBlobOffset += track.SectorSize;
// add subchannel data
relMSF++;
@ -873,6 +938,18 @@ namespace BizHawk.Emulation.DiscSystem
disc._Sectors.Add(sBase);
}
}
// leadout track
// first leadout is 6750 sectors, later ones are 2250 sectors
var leadoutSize = session.SessionSequence == 1 ? 6750 : 2250;
for (var i = 0; i < leadoutSize; i++)
{
disc._Sectors.Add(new SS_Leadout
{
SessionNumber = session.SessionSequence,
Policy = IN_DiscMountPolicy
});
}
}
return disc;

View File

@ -29,11 +29,8 @@ namespace BizHawk.Emulation.DiscSystem
Result.TOCItems[0].Control = 0;
Result.TOCItems[0].Exists = false;
//just in case this doesn't get set...
Result.FirstRecordedTrackNumber = 0;
Result.LastRecordedTrackNumber = 0;
var maxFoundTrack = 0;
var minFoundTrack = 1;
var maxFoundTrack = 1;
foreach (var te in Entries)
{
@ -50,6 +47,7 @@ namespace BizHawk.Emulation.DiscSystem
case 255:
throw new InvalidOperationException("point == 255");
case <= 99:
minFoundTrack = Math.Min(minFoundTrack, point);
maxFoundTrack = Math.Max(maxFoundTrack, point);
Result.TOCItems[point].LBA = q.AP_Timestamp - 150; //RawTOCEntries contained an absolute time
Result.TOCItems[point].Control = q.CONTROL;
@ -96,8 +94,7 @@ namespace BizHawk.Emulation.DiscSystem
}
//this is speculative:
//well, nothing to be done here..
// if (ret.FirstRecordedTrackNumber == -1) { }
if (Result.FirstRecordedTrackNumber == -1) { Result.FirstRecordedTrackNumber = minFoundTrack; }
if (Result.LastRecordedTrackNumber == -1) { Result.LastRecordedTrackNumber = maxFoundTrack; }
if (Result.SessionFormat == SessionFormat.None) Result.SessionFormat = SessionFormat.Type00_CDROM_CDDA;

View File

@ -50,7 +50,7 @@ namespace BizHawk.Emulation.DiscSystem
/// <summary>
/// The complete sector userdata (2352 bytes) is required
/// </summary>
UserComplete = 15,
UserComplete = (Header16 | User2048 | ECM288Complete),
/// <summary>
/// An alias for UserComplete

View File

@ -71,6 +71,30 @@ namespace BizHawk.Emulation.DiscSystem
buffer16[offset + 15] = mode;
}
/// <summary>
/// Synthesizes a Mode2 sector subheader
/// </summary>
public static void SectorSubHeader(byte[] buffer8, int offset, byte form)
{
// see mirage_sector_generate_subheader
for (var i = 0; i < 8; i++) buffer8[offset + i] = 0;
if (form == 2)
{
// these are just 0 in form 1
buffer8[offset + 2] = 0x20;
buffer8[offset + 5] = 0x20;
}
}
/// <summary>
/// Synthesizes the EDC checksum for a Mode 1 data sector (and puts it in place)
/// </summary>
public static void EDC_Mode1(byte[] buf2352, int offset)
{
var edc = ECM.EDC_Calc(buf2352, offset, 2064);
ECM.PokeUint(buf2352, offset + 2064, edc);
}
/// <summary>
/// Synthesizes the EDC checksum for a Mode 2 Form 1 data sector (and puts it in place)
/// </summary>
@ -92,13 +116,12 @@ namespace BizHawk.Emulation.DiscSystem
/// <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
/// Make sure everything else in the sector header and userdata is done before calling this
/// </summary>
public static void ECM_Mode1(byte[] buf2352, int offset, int LBA)
public static void ECM_Mode1(byte[] buf2352, int offset)
{
//EDC
var edc = ECM.EDC_Calc(buf2352, offset, 2064);
ECM.PokeUint(buf2352, offset + 2064, edc);
EDC_Mode1(buf2352, offset);
//reserved, zero
for (var i = 0; i < 8; i++) buf2352[offset + 2068 + i] = 0;
@ -107,6 +130,37 @@ namespace BizHawk.Emulation.DiscSystem
ECM.ECC_Populate(buf2352, offset, buf2352, offset, false);
}
/// <summary>
/// Synthesizes the complete ECM data (Subheader + EDC + ECC) for a Mode 2 Form 1 data sector (and puts it in place)
/// Make sure everything else in the sector header and userdata is done before calling this
/// </summary>
public static void ECM_Mode2_Form1(byte[] buf2352, int offset)
{
//Subheader
SectorSubHeader(buf2352, offset + 16, 1);
//EDC
EDC_Mode2_Form1(buf2352, offset);
//ECC
ECM.ECC_Populate(buf2352, offset, buf2352, offset, false);
}
/// <summary>
/// Synthesizes the complete ECM data (Subheader + EDC) for a Mode 2 Form 2 data sector (and puts it in place)
/// Make sure everything else in the userdata is done before calling this
/// </summary>
public static void ECM_Mode2_Form2(byte[] buf2352, int offset)
{
//Subheader
SectorSubHeader(buf2352, offset + 16, 2);
//EDC
EDC_Mode2_Form2(buf2352, offset);
//note that Mode 2 Form 2 does not have ECC
}
/// <summary>
/// Converts the useful (but unrealistic) deinterleaved subchannel data into the useless (but realistic) interleaved format.
/// in_buf and out_buf should not overlap