public void EmptyFile() { byte[] result = CRC16CCITTContext.File(Path.Combine(Consts.TEST_FILES_ROOT, "Checksum test files", "empty")); result.Should().BeEquivalentTo(_expectedEmpty); }
public void Crc16EmptyFile() { byte[] result = CRC16CCITTContext.File(Path.Combine(Consts.TEST_FILES_ROOT, "Checksum test files", "empty")); Assert.AreEqual(_expectedEmpty, result); }
public void Crc16CcittRandomFile() { byte[] result = CRC16CCITTContext.File(Path.Combine(Consts.TEST_FILES_ROOT, "Checksum test files", "random")); Assert.AreEqual(_expectedRandom, result); }
public void RandomData() { byte[] data = new byte[1048576]; var fs = new FileStream(Path.Combine(Consts.TEST_FILES_ROOT, "Checksum test files", "random"), FileMode.Open, FileAccess.Read); fs.Read(data, 0, 1048576); fs.Close(); fs.Dispose(); CRC16CCITTContext.Data(data, out byte[] result); result.Should().BeEquivalentTo(_expectedRandom); }
public void Crc16EmptyData() { byte[] data = new byte[1048576]; var fs = new FileStream(Path.Combine(Consts.TEST_FILES_ROOT, "Checksum test files", "empty"), FileMode.Open, FileAccess.Read); fs.Read(data, 0, 1048576); fs.Close(); fs.Dispose(); CRC16CCITTContext.Data(data, out byte[] result); Assert.AreEqual(_expectedEmpty, result); }
public void Crc16RandomData() { byte[] data = new byte[1048576]; var fs = new FileStream(Path.Combine(Consts.TestFilesRoot, "checksums", "random"), FileMode.Open, FileAccess.Read); fs.Read(data, 0, 1048576); fs.Close(); fs.Dispose(); CRC16CCITTContext.Data(data, out byte[] result); Assert.AreEqual(ExpectedRandom, result); }
public void EmptyInstance() { byte[] data = new byte[1048576]; var fs = new FileStream(Path.Combine(Consts.TEST_FILES_ROOT, "Checksum test files", "empty"), FileMode.Open, FileAccess.Read); fs.Read(data, 0, 1048576); fs.Close(); fs.Dispose(); var ctx = new CRC16CCITTContext(); ctx.Update(data); byte[] result = ctx.Final(); result.Should().BeEquivalentTo(_expectedEmpty); }
public void Crc16EmptyInstance() { byte[] data = new byte[1048576]; var fs = new FileStream(Path.Combine(Consts.TEST_FILES_ROOT, "Checksum test files", "empty"), FileMode.Open, FileAccess.Read); fs.Read(data, 0, 1048576); fs.Close(); fs.Dispose(); IChecksum ctx = new CRC16CCITTContext(); ctx.Update(data); byte[] result = ctx.Final(); Assert.AreEqual(_expectedEmpty, result); }
public void Crc16RandomInstance() { byte[] data = new byte[1048576]; var fs = new FileStream(Path.Combine(Consts.TestFilesRoot, "checksums", "random"), FileMode.Open, FileAccess.Read); fs.Read(data, 0, 1048576); fs.Close(); fs.Dispose(); IChecksum ctx = new CRC16CCITTContext(); ctx.Update(data); byte[] result = ctx.Final(); Assert.AreEqual(ExpectedRandom, result); }
public static string PrettifyQ(byte[] subBuf, bool bcd, long lba, bool corruptedPause, bool pause, bool rwEmpty) { CRC16CCITTContext.Data(subBuf, 10, out byte[] crc); bool crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; long minute = (lba + 150) / 4500; long second = (lba + 150) % 4500 / 75; long frame = (lba + 150) % 4500 % 75; string area; int control = (subBuf[0] & 0xF0) / 16; int adr = subBuf[0] & 0x0F; string controlInfo = ((control & 0xC) / 4) switch { 0 => $"stereo audio {((control & 0x01) == 1 ? "with" : "without")} pre-emphasis", 1 => $"{((control & 0x01) == 1 ? "incremental" : "uninterrupted")} data", 2 => $"quadraphonic audio {((control & 0x01) == 1 ? "with" : "without")} pre-emphasis", _ => $"reserved control value {control & 0x01}" }; string copy = (control & 0x02) > 0 ? "copy permitted" : "copy prohibited"; if (bcd) { BcdToBinaryQ(subBuf); } int qPos = (subBuf[3] * 60 * 75) + (subBuf[4] * 75) + subBuf[5] - 150; byte pmin = subBuf[7]; byte psec = subBuf[8]; int qStart = (subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9] - 150; int nextPos = (subBuf[3] * 60 * 75) + (subBuf[4] * 75) + subBuf[5] - 150; byte zero = subBuf[6]; int maxOut = (subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9] - 150; bool final = subBuf[3] == 0xFF && subBuf[4] == 0xFF && subBuf[5] == 0xFF; BinaryToBcdQ(subBuf); if (lba < 0) { area = "Lead-In"; switch (adr) { case 1 when subBuf[2] < 0xA0:
public static void SolveTrackPregaps(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, Track[] tracks, bool supportsPqSubchannel, bool supportsRwSubchannel, Database.Models.Device dbDev, out bool inexactPositioning) { bool sense = true; // Sense indicator byte[] subBuf = null; int posQ; uint retries; bool? bcd = null; byte[] crc; Dictionary<uint, int> pregaps = new Dictionary<uint, int>(); inexactPositioning = false; if(!supportsPqSubchannel && !supportsRwSubchannel) return; // Check if subchannel is BCD for(retries = 0; retries < 10; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, 11, dbDev, out subBuf, false) : GetSectorForPregapQ16(dev, 11, dbDev, out subBuf, false); if(sense) continue; bcd = (subBuf[9] & 0x10) > 0; break; } AaruConsole.DebugWriteLine("Pregap calculator", bcd == true ? "Subchannel is BCD" : bcd == false ? "Subchannel is not BCD" : "Could not detect drive subchannel BCD"); if(bcd is null) { dumpLog?.WriteLine("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); updateStatus?. Invoke("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); return; } // Initialize the dictionary for(int i = 0; i < tracks.Length; i++) pregaps[tracks[i].TrackSequence] = 0; for(int t = 0; t < tracks.Length; t++) { Track track = tracks[t]; int trackRetries = 0; // First track of each session has at least 150 sectors of pregap and is not readable always if(tracks.Where(t => t.TrackSession == track.TrackSession).OrderBy(t => t.TrackSequence). FirstOrDefault().TrackSequence == track.TrackSequence) { AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0}", track.TrackSequence); if(track.TrackSequence > 1) pregaps[track.TrackSequence] = 150; continue; } if(t > 0 && tracks[t - 1].TrackType == tracks[t].TrackType) { AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0}", track.TrackSequence); continue; } AaruConsole.DebugWriteLine("Pregap calculator", "Track {0}", track.TrackSequence); int lba = (int)track.TrackStartSector - 1; bool pregapFound = false; Track previousTrack = tracks.FirstOrDefault(t => t.TrackSequence == track.TrackSequence - 1); bool goneBack = false; bool goFront = false; bool forward = false; bool crcOk = false; bool previousPregapIsPreviousTrack = false; // Check if pregap is 0 for(retries = 0; retries < 10 && !pregapFound; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf, track.TrackType == TrackType.Audio) : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf, track.TrackType == TrackType.Audio); if(sense) { AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}", lba, retries + 1, sense); continue; } if(bcd == false) BinaryToBcdQ(subBuf); CRC16CCITTContext.Data(subBuf, 10, out crc); AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}, Q: {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9], subBuf[10], subBuf[11], crc[0], crc[1]); crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; // Try to do a simple correction if(!crcOk) { // Data track cannot have 11xxb in CONTROL if((subBuf[0] & 0x40) > 0) subBuf[0] &= 0x7F; // ADR only uses two bits subBuf[0] &= 0xF3; // Don't care about other Q modes if((subBuf[0] & 0xF) == 1) { // ZERO only used in DDCD subBuf[6] = 0; // Fix BCD numbering for(int i = 1; i < 10; i++) { if((subBuf[i] & 0xF0) > 0xA0) subBuf[i] &= 0x7F; if((subBuf[i] & 0x0F) > 0x0A) subBuf[i] &= 0xF7; } } CRC16CCITTContext.Data(subBuf, 10, out crc); crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; if(crcOk) { AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}, Q (FIXED): {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9], subBuf[10], subBuf[11], crc[0], crc[1]); } else continue; } BcdToBinaryQ(subBuf); // Q position if((subBuf[0] & 0xF) != 1) continue; posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150; if(subBuf[1] != track.TrackSequence - 1 || subBuf[2] == 0 || posQ != lba) break; pregaps[track.TrackSequence] = 0; pregapFound = true; } if(pregapFound) continue; // Calculate pregap lba = (int)track.TrackStartSector - 150; while(lba > (int)previousTrack.TrackStartSector && lba <= (int)track.TrackStartSector) { // Some drives crash if you try to read just before the previous read, so seek away first if(!forward) sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba - 10, dbDev, out subBuf, track.TrackType == TrackType.Audio) : GetSectorForPregapQ16(dev, (uint)lba - 10, dbDev, out subBuf, track.TrackType == TrackType.Audio); for(retries = 0; retries < 10; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf, track.TrackType == TrackType.Audio) : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf, track.TrackType == TrackType.Audio); if(sense) continue; if(bcd == false) BinaryToBcdQ(subBuf); CRC16CCITTContext.Data(subBuf, 10, out crc); AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}, Q: {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9], subBuf[10], subBuf[11], crc[0], crc[1]); crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; // Try to do a simple correction if(!crcOk) { // Data track cannot have 11xxb in CONTROL if((subBuf[0] & 0x40) > 0) subBuf[0] &= 0x7F; // ADR only uses two bits subBuf[0] &= 0xF3; // Don't care about other Q modes if((subBuf[0] & 0xF) == 1) { // ZERO only used in DDCD subBuf[6] = 0; // Fix BCD numbering for(int i = 1; i < 10; i++) { if((subBuf[i] & 0xF0) > 0xA0) subBuf[i] &= 0x7F; if((subBuf[i] & 0x0F) > 0x0A) subBuf[i] &= 0xF7; } } CRC16CCITTContext.Data(subBuf, 10, out crc); crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; if(crcOk) { AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}, Q (FIXED): {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9], subBuf[10], subBuf[11], crc[0], crc[1]); break; } } if(crcOk) break; } if(retries == 10) { if(sense) { trackRetries++; if(trackRetries >= 10) { if(pregaps[track.TrackSequence] == 0) { if((previousTrack.TrackType == TrackType.Audio && track.TrackType != TrackType.Audio) || (previousTrack.TrackType != TrackType.Audio && track.TrackType == TrackType.Audio)) { dumpLog?. WriteLine("Could not read subchannel for this track, supposing 150 sectors."); updateStatus?. Invoke("Could not read subchannel for this track, supposing 150 sectors."); } else { dumpLog?. WriteLine("Could not read subchannel for this track, supposing 0 sectors."); updateStatus?. Invoke("Could not read subchannel for this track, supposing 0 sectors."); } } else { dumpLog?. WriteLine($"Could not read subchannel for this track, supposing {pregaps[track.TrackSequence]} sectors."); updateStatus?. Invoke($"Could not read subchannel for this track, supposing {pregaps[track.TrackSequence]} sectors."); } break; } dumpLog?.WriteLine($"Could not read subchannel for sector {lba}"); updateStatus?.Invoke($"Could not read subchannel for sector {lba}"); lba++; forward = true; continue; } dumpLog?.WriteLine($"Could not get correct subchannel for sector {lba}"); updateStatus?.Invoke($"Could not get correct subchannel for sector {lba}"); } if(subBuf.All(b => b == 0)) { inexactPositioning = true; AaruConsole.DebugWriteLine("Pregap calculator", "All Q empty for LBA {0}", lba); break; } BcdToBinaryQ(subBuf); // If it's not Q position if((subBuf[0] & 0xF) != 1) { // This means we already searched back, so search forward if(goFront) { lba++; forward = true; if(lba == (int)previousTrack.TrackStartSector) break; continue; } // Search back goneBack = true; lba--; forward = false; continue; } // Previous track if(subBuf[1] < track.TrackSequence) { lba++; forward = true; previousPregapIsPreviousTrack = true; // Already gone back, so go forward if(goneBack) goFront = true; continue; } // Same track, but not pregap if(subBuf[1] == track.TrackSequence && subBuf[2] > 0) { lba--; forward = false; if(previousPregapIsPreviousTrack) break; continue; } previousPregapIsPreviousTrack = false; // Pregap according to Q position posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150; int diff = posQ - lba; int pregapQ = (int)track.TrackStartSector - lba; if(diff != 0) { AaruConsole.DebugWriteLine("Pregap calculator", "Invalid Q position for LBA {0}, got {1}", lba, posQ); inexactPositioning = true; } // Received a Q post the LBA we wanted, just go back. If we are already going forward, break if(posQ > lba) { if(forward) break; lba--; continue; } // Bigger than known change, otherwise we found it if(pregapQ > pregaps[track.TrackSequence]) { // If CRC is not OK, only accept pregaps less than 10 sectors longer than previously now if(crcOk || pregapQ - pregaps[track.TrackSequence] < 10) { AaruConsole.DebugWriteLine("Pregap calculator", "Pregap for track {0}: {1}", track.TrackSequence, pregapQ); pregaps[track.TrackSequence] = pregapQ; } // We are going forward, so we have already been in the previous track, so add 1 to pregap and get out of here else if(forward) { pregaps[track.TrackSequence]++; break; } } else if(pregapQ == pregaps[track.TrackSequence]) break; lba--; forward = false; } } for(int i = 0; i < tracks.Length; i++) { tracks[i].TrackPregap = (ulong)pregaps[tracks[i].TrackSequence]; tracks[i].TrackStartSector -= tracks[i].TrackPregap; #if DEBUG dumpLog?.WriteLine($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors"); updateStatus?.Invoke($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors"); #endif } }
public void Crc16EmptyFile() { byte[] result = CRC16CCITTContext.File(Path.Combine(Consts.TestFilesRoot, "checksums", "empty")); Assert.AreEqual(ExpectedEmpty, result); }
public void Crc16RandomFile() { byte[] result = CRC16CCITTContext.File(Path.Combine(Consts.TestFilesRoot, "checksums", "random")); Assert.AreEqual(ExpectedRandom, result); }
public static void SolveTrackPregaps(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, Track[] tracks, bool supportsPqSubchannel, bool supportsRwSubchannel, Database.Models.Device dbDev, out bool inexactPositioning) { bool sense; // Sense indicator byte[] subBuf; int posQ; uint retries; bool? bcd = null; byte[] crc; Dictionary <uint, int> pregaps = new Dictionary <uint, int>(); inexactPositioning = false; if (!supportsPqSubchannel && !supportsRwSubchannel) { return; } // Check if subchannel is BCD for (retries = 0; retries < 10; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, 11, dbDev, out subBuf) : GetSectorForPregapQ16(dev, 11, dbDev, out subBuf); if (sense) { continue; } bcd = (subBuf[9] & 0x10) > 0; break; } if (bcd is null) { dumpLog?.WriteLine("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); updateStatus?. Invoke("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); return; } // Initialize the dictionary for (int i = 0; i < tracks.Length; i++) { pregaps[tracks[i].TrackSequence] = 0; } foreach (Track track in tracks) { if (track.TrackSequence <= 1) { continue; } int lba = (int)track.TrackStartSector - 1; bool pregapFound = false; Track previousTrack = tracks.FirstOrDefault(t => t.TrackSequence == track.TrackSequence - 1); bool goneBack = false; bool goFront = false; // Check if pregap is 0 for (retries = 0; retries < 10; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf) : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf); if (sense) { continue; } if (bcd == false) { BinaryToBcdQ(subBuf); } CRC16CCITTContext.Data(subBuf, 10, out crc); if (crc[0] != subBuf[10] || crc[1] != subBuf[11]) { continue; } BcdToBinaryQ(subBuf); // Q position if ((subBuf[0] & 0xF) != 1) { continue; } posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150; if (subBuf[1] != track.TrackSequence - 1 || subBuf[2] == 0 || posQ != lba) { break; } pregaps[track.TrackSequence] = 0; pregapFound = true; } if (pregapFound) { continue; } // Calculate pregap lba = (int)track.TrackStartSector - 150; while (lba > (int)previousTrack.TrackStartSector) { // Some drives crash if you try to read just before the previous read, so seek away first sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba - 10, dbDev, out subBuf) : GetSectorForPregapQ16(dev, (uint)lba - 10, dbDev, out subBuf); for (retries = 0; retries < 10; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf) : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf); if (sense) { continue; } if (bcd == false) { BinaryToBcdQ(subBuf); } CRC16CCITTContext.Data(subBuf, 10, out crc); if (crc[0] == subBuf[10] && crc[1] == subBuf[11]) { break; } } if (retries == 10) { dumpLog?.WriteLine($"Could not get correct subchannel for sector {lba}"); updateStatus?.Invoke($"Could not get correct subchannel for sector {lba}"); } BcdToBinaryQ(subBuf); // If it's not Q position if ((subBuf[0] & 0xF) != 1) { // This means we already searched back, so search forward if (goFront) { lba++; if (lba == (int)previousTrack.TrackStartSector) { break; } continue; } // Search back goneBack = true; lba--; continue; } // Previous track if (subBuf[1] < track.TrackSequence) { lba++; // Already gone back, so go forward if (goneBack) { goFront = true; } continue; } // Same track, but not pregap if (subBuf[1] == track.TrackSequence && subBuf[2] > 0) { lba--; continue; } // Pregap according to Q position int pregapQ = (subBuf[3] * 60 * 75) + (subBuf[4] * 75) + subBuf[5] + 1; posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150; int diff = posQ - lba; if (diff != 0) { inexactPositioning = true; } // Bigger than known change, otherwise we found it if (pregapQ > pregaps[track.TrackSequence]) { pregaps[track.TrackSequence] = pregapQ; } else if (pregapQ == pregaps[track.TrackSequence]) { break; } lba--; } } for (int i = 0; i < tracks.Length; i++) { tracks[i].TrackPregap = (ulong)pregaps[tracks[i].TrackSequence]; tracks[i].TrackStartSector -= tracks[i].TrackPregap; #if DEBUG dumpLog?.WriteLine($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors"); updateStatus?.Invoke($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors"); #endif } }
// Return true if indexes have changed bool WriteSubchannelToImage(MmcSubchannel supportedSubchannel, MmcSubchannel desiredSubchannel, byte[] sub, ulong sectorAddress, uint length, SubchannelLog subLog, Dictionary <byte, string> isrcs, byte currentTrack, ref string mcn, Track[] tracks) { if (supportedSubchannel == MmcSubchannel.Q16) { sub = Subchannel.ConvertQToRaw(sub); } if (desiredSubchannel != MmcSubchannel.None) { _outputPlugin.WriteSectorsTag(sub, sectorAddress, length, SectorTagType.CdSectorSubchannel); } subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)sectorAddress, length); byte[] deSub = Subchannel.Deinterleave(sub); // Check subchannel for (int subPos = 0; subPos < deSub.Length; subPos += 96) { byte[] q = new byte[12]; Array.Copy(deSub, subPos + 12, q, 0, 12); CRC16CCITTContext.Data(q, 10, out byte[] crc); bool crcOk = crc[0] == q[10] && crc[1] == q[11]; // ISRC if ((q[0] & 0x3) == 3) { string isrc = Subchannel.DecodeIsrc(q); if (isrc == null || isrc == "000000000000") { continue; } if (!crcOk) { continue; } if (!isrcs.ContainsKey(currentTrack)) { _dumpLog?.WriteLine($"Found new ISRC {isrc} for track {currentTrack}."); UpdateStatus?.Invoke($"Found new ISRC {isrc} for track {currentTrack}."); } else if (isrcs[currentTrack] != isrc) { _dumpLog?. WriteLine($"ISRC for track {currentTrack} changed from {isrcs[currentTrack]} to {isrc}."); UpdateStatus?. Invoke($"ISRC for track {currentTrack} changed from {isrcs[currentTrack]} to {isrc}."); } isrcs[currentTrack] = isrc; } else if ((q[0] & 0x3) == 2) { string newMcn = Subchannel.DecodeMcn(q); if (newMcn == null || newMcn == "0000000000000") { continue; } if (!crcOk) { continue; } if (mcn is null) { _dumpLog?.WriteLine($"Found new MCN {newMcn}."); UpdateStatus?.Invoke($"Found new MCN {newMcn}."); } else if (mcn != newMcn) { _dumpLog?.WriteLine($"MCN changed from {mcn} to {newMcn}."); UpdateStatus?.Invoke($"MCN changed from {mcn} to {newMcn}."); } mcn = newMcn; } else if ((q[0] & 0x3) == 1) { // TODO: Indexes // Pregap if (q[2] != 0) { continue; } if (!crcOk) { continue; } byte trackNo = (byte)(((q[1] / 16) * 10) + (q[1] & 0x0F)); for (int i = 0; i < tracks.Length; i++) { if (tracks[i].TrackSequence != trackNo || trackNo == 1) { continue; } byte pmin = (byte)(((q[3] / 16) * 10) + (q[3] & 0x0F)); byte psec = (byte)(((q[4] / 16) * 10) + (q[4] & 0x0F)); byte pframe = (byte)(((q[5] / 16) * 10) + (q[5] & 0x0F)); int qPos = (pmin * 60 * 75) + (psec * 75) + pframe; if (tracks[i].TrackPregap >= (ulong)(qPos + 1)) { continue; } tracks[i].TrackPregap = (ulong)(qPos + 1); tracks[i].TrackStartSector -= tracks[i].TrackPregap; if (i > 0) { tracks[i - 1].TrackEndSector = tracks[i].TrackStartSector - 1; } _dumpLog?.WriteLine($"Pregap for track {trackNo} set to {tracks[i].TrackPregap} sectors."); UpdateStatus?.Invoke($"Pregap for track {trackNo} set to {tracks[i].TrackPregap} sectors."); return(true); } } } return(false); }