From 2012b083f6f5f3131d6ae872d9cdbe0e6793ca79 Mon Sep 17 00:00:00 2001
From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com>
Date: Fri, 12 May 2023 02:44:05 -0700
Subject: [PATCH] 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
---
.../DiscFormats/CDI_format.cs | 50 +----
.../DiscFormats/CUE/CUE_SynthExtras.cs | 127 ++++++++++++
.../DiscFormats/CUE/CUE_Synths.cs | 35 ++--
.../DiscFormats/MDS_Format.cs | 183 +++++++++++++-----
...nthesize_DiscTOC_From_RawTOCEntries_Job.cs | 11 +-
.../Internal/SectorSynth.cs | 2 +-
.../Internal/SynthUtils.cs | 62 +++++-
7 files changed, 343 insertions(+), 127 deletions(-)
create mode 100644 src/BizHawk.Emulation.DiscSystem/DiscFormats/CUE/CUE_SynthExtras.cs
diff --git a/src/BizHawk.Emulation.DiscSystem/DiscFormats/CDI_format.cs b/src/BizHawk.Emulation.DiscSystem/DiscFormats/CDI_format.cs
index bbc0f48240..c228caaedf 100644
--- a/src/BizHawk.Emulation.DiscSystem/DiscFormats/CDI_format.cs
+++ b/src/BizHawk.Emulation.DiscSystem/DiscFormats/CDI_format.cs
@@ -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);
- }
- }
- }
-
/// file not found
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;
diff --git a/src/BizHawk.Emulation.DiscSystem/DiscFormats/CUE/CUE_SynthExtras.cs b/src/BizHawk.Emulation.DiscSystem/DiscFormats/CUE/CUE_SynthExtras.cs
new file mode 100644
index 0000000000..b42e494af0
--- /dev/null
+++ b/src/BizHawk.Emulation.DiscSystem/DiscFormats/CUE/CUE_SynthExtras.cs
@@ -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
+
+ ///
+ /// Represents a Mode2 Form1 2048-byte sector
+ /// Only used by MDS
+ ///
+ 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);
+ }
+ }
+
+ ///
+ /// Represents a Mode2 Form1 2324-byte sector
+ /// Only used by MDS
+ ///
+ 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);
+ }
+ }
+
+ ///
+ /// Represents a Mode2 Form1 2328-byte sector
+ /// Only used by MDS
+ ///
+ 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);
+ }
+ }
+
+ ///
+ /// Represents a full 2448-byte sector with interleaved subcode
+ /// Only used by MDS and CDI
+ ///
+ 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);
+ }
+ }
+
+ ///
+ /// Represents a 2364-byte (2352 + 12) sector with deinterleaved Q subcode
+ /// Only used by CDI
+ ///
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/BizHawk.Emulation.DiscSystem/DiscFormats/CUE/CUE_Synths.cs b/src/BizHawk.Emulation.DiscSystem/DiscFormats/CUE/CUE_Synths.cs
index a5b278587a..299ddd5e8d 100644
--- a/src/BizHawk.Emulation.DiscSystem/DiscFormats/CUE/CUE_Synths.cs
+++ b/src/BizHawk.Emulation.DiscSystem/DiscFormats/CUE/CUE_Synths.cs
@@ -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);
}
}
-
-
}
\ No newline at end of file
diff --git a/src/BizHawk.Emulation.DiscSystem/DiscFormats/MDS_Format.cs b/src/BizHawk.Emulation.DiscSystem/DiscFormats/MDS_Format.cs
index 9924c718f0..025858a569 100644
--- a/src/BizHawk.Emulation.DiscSystem/DiscFormats/MDS_Format.cs
+++ b/src/BizHawk.Emulation.DiscSystem/DiscFormats/MDS_Format.cs
@@ -154,9 +154,9 @@ namespace BizHawk.Emulation.DiscSystem
public class ATrack
{
///
- /// 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;
+ ///
+ /// 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
+ ///
+ 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();
@@ -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;
diff --git a/src/BizHawk.Emulation.DiscSystem/Internal/Jobs/Synthesize_DiscTOC_From_RawTOCEntries_Job.cs b/src/BizHawk.Emulation.DiscSystem/Internal/Jobs/Synthesize_DiscTOC_From_RawTOCEntries_Job.cs
index a9af429704..3afe135445 100644
--- a/src/BizHawk.Emulation.DiscSystem/Internal/Jobs/Synthesize_DiscTOC_From_RawTOCEntries_Job.cs
+++ b/src/BizHawk.Emulation.DiscSystem/Internal/Jobs/Synthesize_DiscTOC_From_RawTOCEntries_Job.cs
@@ -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;
diff --git a/src/BizHawk.Emulation.DiscSystem/Internal/SectorSynth.cs b/src/BizHawk.Emulation.DiscSystem/Internal/SectorSynth.cs
index 17c5235f30..723c6e01bd 100644
--- a/src/BizHawk.Emulation.DiscSystem/Internal/SectorSynth.cs
+++ b/src/BizHawk.Emulation.DiscSystem/Internal/SectorSynth.cs
@@ -50,7 +50,7 @@ namespace BizHawk.Emulation.DiscSystem
///
/// The complete sector userdata (2352 bytes) is required
///
- UserComplete = 15,
+ UserComplete = (Header16 | User2048 | ECM288Complete),
///
/// An alias for UserComplete
diff --git a/src/BizHawk.Emulation.DiscSystem/Internal/SynthUtils.cs b/src/BizHawk.Emulation.DiscSystem/Internal/SynthUtils.cs
index 90394208cd..a873e24faa 100644
--- a/src/BizHawk.Emulation.DiscSystem/Internal/SynthUtils.cs
+++ b/src/BizHawk.Emulation.DiscSystem/Internal/SynthUtils.cs
@@ -71,6 +71,30 @@ namespace BizHawk.Emulation.DiscSystem
buffer16[offset + 15] = mode;
}
+ ///
+ /// Synthesizes a Mode2 sector subheader
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Synthesizes the EDC checksum for a Mode 1 data sector (and puts it in place)
+ ///
+ public static void EDC_Mode1(byte[] buf2352, int offset)
+ {
+ var edc = ECM.EDC_Calc(buf2352, offset, 2064);
+ ECM.PokeUint(buf2352, offset + 2064, edc);
+ }
+
///
/// Synthesizes the EDC checksum for a Mode 2 Form 1 data sector (and puts it in place)
///
@@ -92,13 +116,12 @@ namespace BizHawk.Emulation.DiscSystem
///
/// 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
///
- 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);
}
+ ///
+ /// 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
+ ///
+ 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);
+ }
+
+ ///
+ /// 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
+ ///
+ 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
+ }
+
///
/// Converts the useful (but unrealistic) deinterleaved subchannel data into the useless (but realistic) interleaved format.
/// in_buf and out_buf should not overlap