From f1244d543cba48b6001daf0c2da8c47f91600a23 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Tue, 14 Jul 2020 01:06:23 +0100 Subject: [PATCH] Move subchannel generation and fixing to a separate class. --- .idea/.idea.Aaru/.idea/contentModel.xml | 596 ++++--- .idea/.idea.Aaru/riderModule.iml | 2 + Aaru.Core/Aaru.Core.csproj | 1 + .../Devices/Dumping/CompactDisc/CdiReady.cs | 14 +- Aaru.Core/Devices/Dumping/CompactDisc/Data.cs | 16 +- Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs | 7 +- .../Devices/Dumping/CompactDisc/Error.cs | 21 +- .../Devices/Dumping/CompactDisc/LeadOuts.cs | 14 +- .../Devices/Dumping/CompactDisc/Subchannel.cs | 1424 ---------------- Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs | 7 +- Aaru.Core/Media/CompactDisc.cs | 1441 +++++++++++++++++ 11 files changed, 1782 insertions(+), 1761 deletions(-) create mode 100644 Aaru.Core/Media/CompactDisc.cs diff --git a/.idea/.idea.Aaru/.idea/contentModel.xml b/.idea/.idea.Aaru/.idea/contentModel.xml index ce5a26acc..774eeb336 100644 --- a/.idea/.idea.Aaru/.idea/contentModel.xml +++ b/.idea/.idea.Aaru/.idea/contentModel.xml @@ -1,36 +1,28 @@ - - - - - - + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -78,13 +70,6 @@ - - - - - - - @@ -92,12 +77,18 @@ + + + + + + @@ -106,14 +97,6 @@ - - - - - - - - @@ -121,11 +104,19 @@ + + + + + + + + @@ -191,6 +182,13 @@ + + + + + + + @@ -211,19 +209,10 @@ - - - - - - - - - @@ -232,6 +221,7 @@ + @@ -249,6 +239,7 @@ + @@ -270,9 +261,9 @@ - + @@ -282,8 +273,8 @@ - + @@ -301,17 +292,17 @@ - - + + - + @@ -330,6 +321,7 @@ + @@ -339,6 +331,13 @@ + + + + + + + @@ -355,82 +354,75 @@ - - - - - - - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + @@ -455,7 +447,6 @@ - @@ -467,11 +458,12 @@ + - + @@ -495,9 +487,9 @@ + - @@ -527,6 +519,13 @@ + + + + + + + @@ -591,11 +590,11 @@ + - @@ -615,17 +614,10 @@ - - - - - - - - + @@ -654,9 +646,9 @@ - + @@ -685,6 +677,13 @@ + + + + + + + @@ -697,28 +696,17 @@ - + - - - - - - - - + - - - - @@ -726,13 +714,16 @@ + + + - - + + @@ -768,11 +759,13 @@ + - + + @@ -780,11 +773,14 @@ - + + + + @@ -824,8 +820,8 @@ - + @@ -834,8 +830,8 @@ - + @@ -844,8 +840,8 @@ - + @@ -872,8 +868,15 @@ - + + + + + + + + @@ -891,11 +894,11 @@ - + @@ -918,34 +921,17 @@ - - - - - - - - - - - - + - - - - - - @@ -953,12 +939,18 @@ + + + + + + @@ -1052,6 +1044,12 @@ + + + + + + @@ -1076,12 +1074,6 @@ - - - - - - @@ -1164,6 +1156,8 @@ + + @@ -1179,6 +1173,18 @@ + + + + + + + + + + + + @@ -1191,18 +1197,6 @@ - - - - - - - - - - - - @@ -1211,14 +1205,14 @@ - - + + @@ -1241,12 +1235,10 @@ - - - + @@ -1256,8 +1248,8 @@ - + @@ -1280,6 +1272,16 @@ + + + + + + + + + + @@ -1402,17 +1404,6 @@ - - - - - - - - - - - @@ -1421,6 +1412,7 @@ + @@ -1428,10 +1420,6 @@ - - - - @@ -1439,6 +1427,9 @@ + + + @@ -1522,16 +1513,7 @@ - - - - - - - - - - + @@ -1553,6 +1535,16 @@ + + + + + + + + + + @@ -1586,16 +1578,6 @@ - - - - - - - - - - @@ -1633,6 +1615,16 @@ + + + + + + + + + + @@ -1723,8 +1715,8 @@ - + @@ -1758,16 +1750,6 @@ - - - - - - - - - - @@ -1779,6 +1761,23 @@ + + + + + + + + + + + + + + + + + @@ -1961,16 +1960,6 @@ - - - - - - - - - - @@ -1981,6 +1970,16 @@ + + + + + + + + + + @@ -1999,14 +1998,6 @@ - - - - - - - - @@ -2014,6 +2005,7 @@ + @@ -2021,6 +2013,13 @@ + + + + + + + @@ -2030,20 +2029,11 @@ - - - - - - - - - + - @@ -2052,9 +2042,12 @@ + + + @@ -2080,11 +2073,14 @@ - + + + + @@ -2123,9 +2119,6 @@ - - - @@ -2154,6 +2147,13 @@ + + + + + + + @@ -2169,16 +2169,12 @@ - - - - - - - - + + + + @@ -2189,12 +2185,19 @@ - + + + + + + + + @@ -2226,30 +2229,12 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -2258,6 +2243,12 @@ + + + + + + @@ -2268,12 +2259,6 @@ - - - - - - @@ -2282,14 +2267,21 @@ - + + + + + + + + @@ -2304,20 +2296,13 @@ - - - - - - - - + + - @@ -2325,6 +2310,13 @@ + + + + + + + @@ -2338,16 +2330,16 @@ - - - - - - - + - + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.Aaru/riderModule.iml b/.idea/.idea.Aaru/riderModule.iml index 211519d95..00372ce79 100644 --- a/.idea/.idea.Aaru/riderModule.iml +++ b/.idea/.idea.Aaru/riderModule.iml @@ -2,6 +2,8 @@ + + diff --git a/Aaru.Core/Aaru.Core.csproj b/Aaru.Core/Aaru.Core.csproj index 9cb70a684..c2da51ad7 100644 --- a/Aaru.Core/Aaru.Core.csproj +++ b/Aaru.Core/Aaru.Core.csproj @@ -79,6 +79,7 @@ + diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs b/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs index 6d8614e5e..802107e9f 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs @@ -269,9 +269,10 @@ namespace Aaru.Core.Devices.Dumping _outputPlugin.WriteSectorsLong(data, i + r, 1); - bool indexesChanged = - WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i + r, 1, - subLog, isrcs, 1, ref mcn, tracks, subchannelExtents); + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i + r, 1, subLog, isrcs, 1, ref mcn, tracks, + subchannelExtents, _fixSubchannelPosition, _outputPlugin, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus); // Set tracks and go back if(indexesChanged) @@ -353,9 +354,10 @@ namespace Aaru.Core.Devices.Dumping _outputPlugin.WriteSectorsLong(data, i, blocksToRead); - bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i, - blocksToRead, subLog, isrcs, 1, ref mcn, tracks, - subchannelExtents); + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i, blocksToRead, subLog, isrcs, 1, ref mcn, tracks, + subchannelExtents, _fixSubchannelPosition, _outputPlugin, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus); // Set tracks and go back if(indexesChanged) diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs index 1f3d731cd..eddb45586 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs @@ -393,10 +393,10 @@ namespace Aaru.Core.Devices.Dumping _outputPlugin.WriteSectorsLong(data, i + r, 1); - bool indexesChanged = - WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i + r, 1, - subLog, isrcs, (byte)track.TrackSequence, ref mcn, tracks, - subchannelExtents); + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i + r, 1, subLog, isrcs, (byte)track.TrackSequence, + ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, _outputPlugin, + _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus); // Set tracks and go back if(indexesChanged) @@ -526,10 +526,10 @@ namespace Aaru.Core.Devices.Dumping _outputPlugin.WriteSectorsLong(data, i, blocksToRead); - bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i, - blocksToRead, subLog, isrcs, - (byte)track.TrackSequence, ref mcn, tracks, - subchannelExtents); + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i, blocksToRead, subLog, isrcs, (byte)track.TrackSequence, + ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, _outputPlugin, + _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus); // Set tracks and go back if(indexesChanged) diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs index 7c1570ed3..f9c2e9297 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs @@ -406,7 +406,7 @@ namespace Aaru.Core.Devices.Dumping } if(!(_outputPlugin as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. - CanStorePregaps) && + CanStorePregaps) && tracks.Where(track => track.TrackSequence != tracks.First(t => t.TrackSession == track.TrackSession).TrackSequence). Any(track => track.TrackPregap > 0)) @@ -793,7 +793,7 @@ namespace Aaru.Core.Devices.Dumping // If a subchannel is supported, check if output plugin allows us to write it. if(desiredSubchannel != MmcSubchannel.None && !(_outputPlugin as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. - CanStoreSubchannelRw)) + CanStoreSubchannelRw)) { _dumpLog.WriteLine("Output image does not support subchannels, {0}continuing...", _force ? "" : "not "); @@ -1219,7 +1219,8 @@ namespace Aaru.Core.Devices.Dumping _resume.BadSubchannels.Sort(); if(_generateSubchannels && _outputPlugin.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel)) - GenerateSubchannels(subchannelExtents, tracks, trackFlags, blocks, subLog); + Media.CompactDisc.GenerateSubchannels(subchannelExtents, tracks, trackFlags, blocks, subLog, _dumpLog, + InitProgress, UpdateProgress, EndProgress, _outputPlugin); // TODO: Disc ID var metadata = new CommonTypes.Structs.ImageInfo diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs index c1614a5d8..12c757e17 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs @@ -292,9 +292,10 @@ namespace Aaru.Core.Devices.Dumping Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); _outputPlugin.WriteSectorLong(data, badSector); - bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector, - 1, subLog, isrcs, (byte)track.TrackSequence, ref mcn, - tracks, subchannelExtents); + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.TrackSequence, ref mcn, + tracks, subchannelExtents, _fixSubchannelPosition, _outputPlugin, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus); // Set tracks and go back if(indexesChanged) @@ -410,10 +411,10 @@ namespace Aaru.Core.Devices.Dumping Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); _outputPlugin.WriteSectorLong(data, badSector); - bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, - badSector, 1, subLog, isrcs, - (byte)track.TrackSequence, ref mcn, tracks, - subchannelExtents); + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.TrackSequence, + ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, _outputPlugin, + _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus); // Set tracks and go back if(indexesChanged) @@ -555,8 +556,10 @@ namespace Aaru.Core.Devices.Dumping continue; } - WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, cmdBuf, badSector, 5, subLog, isrcs, - (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents); + Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, cmdBuf, badSector, 5, + subLog, isrcs, (byte)track.TrackSequence, ref mcn, tracks, + subchannelExtents, _fixSubchannelPosition, _outputPlugin, + _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus); if(subchannelExtents.Contains(tmpArray[i])) continue; diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs b/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs index c8eaf92e1..88ed249ff 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs @@ -160,9 +160,10 @@ namespace Aaru.Core.Devices.Dumping _outputPlugin.WriteSectorsLong(data, i, _maximumReadable); - bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i, - _maximumReadable, subLog, isrcs, 0xAA, ref mcn, - tracks, subchannelExtents); + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i, _maximumReadable, subLog, isrcs, 0xAA, ref mcn, tracks, + subchannelExtents, _fixSubchannelPosition, _outputPlugin, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus); // Set tracks and go back if(indexesChanged) @@ -328,9 +329,10 @@ namespace Aaru.Core.Devices.Dumping _outputPlugin.WriteSectorsLong(data, i, _maximumReadable); - bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i, - _maximumReadable, subLog, isrcs, 0xAA, ref mcn, - tracks, subchannelExtents); + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i, _maximumReadable, subLog, isrcs, 0xAA, ref mcn, tracks, + subchannelExtents, _fixSubchannelPosition, _outputPlugin, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus); // Set tracks and go back if(indexesChanged) diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs index 6d8307827..b2216a846 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs @@ -30,14 +30,7 @@ // Copyright © 2011-2020 Natalia Portillo // ****************************************************************************/ -using System; -using System.Collections.Generic; -using System.Linq; -using Aaru.Checksums; -using Aaru.CommonTypes.Enums; -using Aaru.CommonTypes.Structs; using Aaru.Core.Logging; -using Aaru.Decoders.CD; using Aaru.Devices; // ReSharper disable JoinDeclarationAndInitializer @@ -67,1422 +60,5 @@ namespace Aaru.Core.Devices.Dumping MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); } - - // Return true if indexes have changed - bool WriteSubchannelToImage(MmcSubchannel supportedSubchannel, MmcSubchannel desiredSubchannel, byte[] sub, - ulong sectorAddress, uint length, SubchannelLog subLog, - Dictionary isrcs, byte currentTrack, ref string mcn, Track[] tracks, - HashSet subchannelExtents) - { - if(supportedSubchannel == MmcSubchannel.Q16) - sub = Subchannel.ConvertQToRaw(sub); - - if(!_fixSubchannelPosition && - desiredSubchannel != MmcSubchannel.None) - _outputPlugin.WriteSectorsTag(sub, sectorAddress, length, SectorTagType.CdSectorSubchannel); - - subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)sectorAddress, length, false, - false); - - byte[] deSub = Subchannel.Deinterleave(sub); - - bool indexesChanged = CheckIndexesFromSubchannel(deSub, isrcs, currentTrack, ref mcn, tracks); - - if(!_fixSubchannelPosition || - desiredSubchannel == MmcSubchannel.None) - return indexesChanged; - - int prePos = int.MinValue; - - // Check subchannel - for(int subPos = 0; subPos < deSub.Length; subPos += 96) - { - long lba = (long)sectorAddress + (subPos / 96); - bool @fixed = false; - 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]; - - bool pOk = true; - int pWeight = 0; - - bool rwOk = true; - - for(int p = subPos; p < subPos + 12; p++) - { - if(deSub[p] != 0 && - deSub[p] != 255) - pOk = false; - - for(int w = 0; w < 8; w++) - if(((deSub[p] >> w) & 1) > 0) - pWeight++; - } - - for(int rw = subPos + 24; rw < subPos + 96; rw++) - { - if(deSub[rw] == 0) - continue; - - rwOk = false; - - break; - } - - bool rwPacket = false; - bool cdtextPacket = false; - - if(!rwOk) - { - byte[] sectorSub = new byte[96]; - Array.Copy(sub, subPos, sectorSub, 0, 96); - - DetectRwPackets(sectorSub, out _, out rwPacket, out cdtextPacket); - - // TODO: CD+G reed solomon - if(rwPacket && !cdtextPacket) - rwOk = true; - - if(cdtextPacket) - rwOk = CheckCdTextPackets(sectorSub); - } - - if(!pOk && _fixSubchannel) - { - if(pWeight >= 48) - for(int p = subPos; p < subPos + 12; p++) - deSub[p] = 255; - else - for(int p = subPos; p < subPos + 12; p++) - deSub[p] = 0; - - pOk = true; - @fixed = true; - - subLog.WritePFix(lba); - } - - if(!rwOk && - !rwPacket && - !cdtextPacket && - _fixSubchannel) - { - for(int rw = subPos + 24; rw < subPos + 96; rw++) - deSub[rw] = 0; - - rwOk = true; - @fixed = true; - - subLog.WriteRwFix(lba); - } - - byte smin, ssec, amin, asec, aframe; - int aPos; - - if(!crcOk && - _fixSubchannel && - subPos > 0 && - subPos < deSub.Length - 96) - { - isrcs.TryGetValue(currentTrack, out string knownGoodIsrc); - - crcOk = FixQSubchannel(deSub, q, subPos, mcn, knownGoodIsrc, _fixSubchannelCrc, out bool fixedAdr, - out bool controlFix, out bool fixedZero, out bool fixedTno, - out bool fixedIndex, out bool fixedRelPos, out bool fixedAbsPos, - out bool fixedCrc, out bool fixedMcn, out bool fixedIsrc); - - if(crcOk) - { - Array.Copy(q, 0, deSub, subPos + 12, 12); - @fixed = true; - - if(fixedAdr) - subLog.WriteQAdrFix(lba); - - if(controlFix) - subLog.WriteQCtrlFix(lba); - - if(fixedZero) - subLog.WriteQZeroFix(lba); - - if(fixedTno) - subLog.WriteQTnoFix(lba); - - if(fixedIndex) - subLog.WriteQIndexFix(lba); - - if(fixedRelPos) - subLog.WriteQRelPosFix(lba); - - if(fixedAbsPos) - subLog.WriteQAbsPosFix(lba); - - if(fixedCrc) - subLog.WriteQCrcFix(lba); - - if(fixedMcn) - subLog.WriteQMcnFix(lba); - - if(fixedIsrc) - subLog.WriteQIsrcFix(lba); - } - } - - if(!pOk || - !crcOk || - !rwOk) - continue; - - aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); - - if((q[0] & 0x3) == 1) - { - amin = (byte)(((q[7] / 16) * 10) + (q[7] & 0x0F)); - asec = (byte)(((q[8] / 16) * 10) + (q[8] & 0x0F)); - aPos = ((amin * 60 * 75) + (asec * 75) + aframe) - 150; - } - else - { - ulong expectedSectorAddress = sectorAddress + (ulong)(subPos / 96) + 150; - smin = (byte)(expectedSectorAddress / 60 / 75); - expectedSectorAddress -= (ulong)(smin * 60 * 75); - ssec = (byte)(expectedSectorAddress / 75); - - aPos = ((smin * 60 * 75) + (ssec * 75) + aframe) - 150; - - // Next second - if(aPos < prePos) - aPos += 75; - } - - // TODO: Negative sectors - if(aPos < 0) - continue; - - prePos = aPos; - - byte[] posSub = new byte[96]; - Array.Copy(deSub, subPos, posSub, 0, 96); - posSub = Subchannel.Interleave(posSub); - _outputPlugin.WriteSectorTag(posSub, (ulong)aPos, SectorTagType.CdSectorSubchannel); - - subchannelExtents.Remove(aPos); - - if(@fixed) - subLog?.WriteEntry(posSub, supportedSubchannel == MmcSubchannel.Raw, lba, 1, false, true); - } - - return indexesChanged; - } - - bool CheckIndexesFromSubchannel(byte[] deSub, Dictionary isrcs, byte currentTrack, ref string mcn, - Track[] tracks) - { - // 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) - { - 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; - - // Pregap - if(q[2] == 0) - { - 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) - continue; - - ulong oldPregap = tracks[i].TrackPregap; - - tracks[i].TrackPregap = (ulong)qPos; - tracks[i].TrackStartSector -= tracks[i].TrackPregap - oldPregap; - - 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; - } - - byte amin = (byte)(((q[7] / 16) * 10) + (q[7] & 0x0F)); - byte asec = (byte)(((q[8] / 16) * 10) + (q[8] & 0x0F)); - byte aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); - int aPos = ((amin * 60 * 75) + (asec * 75) + aframe) - 150; - - if(tracks[i].Indexes.ContainsKey(q[2]) && - aPos >= tracks[i].Indexes[q[2]]) - return false; - - _dumpLog?.WriteLine($"Setting index {q[2]} for track {trackNo} to LBA {aPos}."); - UpdateStatus?.Invoke($"Setting index {q[2]} for track {trackNo} to LBA {aPos}."); - - tracks[i].Indexes[q[2]] = aPos; - - return true; - } - } - } - - return false; - } - - void DetectRwPackets(byte[] subchannel, out bool zero, out bool rwPacket, out bool cdtextPacket) - { - zero = false; - rwPacket = false; - cdtextPacket = false; - - byte[] cdTextPack1 = new byte[18]; - byte[] cdTextPack2 = new byte[18]; - byte[] cdTextPack3 = new byte[18]; - byte[] cdTextPack4 = new byte[18]; - byte[] cdSubRwPack1 = new byte[24]; - byte[] cdSubRwPack2 = new byte[24]; - byte[] cdSubRwPack3 = new byte[24]; - byte[] cdSubRwPack4 = new byte[24]; - - int i = 0; - - for(int j = 0; j < 18; j++) - { - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | (subchannel[i++] & 0x3F)); - } - - i = 0; - - for(int j = 0; j < 24; j++) - cdSubRwPack1[j] = (byte)(subchannel[i++] & 0x3F); - - for(int j = 0; j < 24; j++) - cdSubRwPack2[j] = (byte)(subchannel[i++] & 0x3F); - - for(int j = 0; j < 24; j++) - cdSubRwPack3[j] = (byte)(subchannel[i++] & 0x3F); - - for(int j = 0; j < 24; j++) - cdSubRwPack4[j] = (byte)(subchannel[i++] & 0x3F); - - switch(cdSubRwPack1[0]) - { - case 0x00: - zero = true; - - break; - case 0x08: - case 0x09: - case 0x0A: - case 0x18: - case 0x38: - rwPacket = true; - - break; - case 0x14: - cdtextPacket = true; - - break; - } - - switch(cdSubRwPack2[0]) - { - case 0x00: - zero = true; - - break; - case 0x08: - case 0x09: - case 0x0A: - case 0x18: - case 0x38: - rwPacket = true; - - break; - case 0x14: - cdtextPacket = true; - - break; - } - - switch(cdSubRwPack3[0]) - { - case 0x00: - zero = true; - - break; - case 0x08: - case 0x09: - case 0x0A: - case 0x18: - case 0x38: - rwPacket = true; - - break; - case 0x14: - cdtextPacket = true; - - break; - } - - switch(cdSubRwPack4[0]) - { - case 0x00: - zero = true; - - break; - case 0x08: - case 0x09: - case 0x0A: - case 0x18: - case 0x38: - rwPacket = true; - - break; - case 0x14: - cdtextPacket = true; - - break; - } - - if((cdTextPack1[0] & 0x80) == 0x80) - cdtextPacket = true; - - if((cdTextPack2[0] & 0x80) == 0x80) - cdtextPacket = true; - - if((cdTextPack3[0] & 0x80) == 0x80) - cdtextPacket = true; - - if((cdTextPack4[0] & 0x80) == 0x80) - cdtextPacket = true; - } - - bool CheckCdTextPackets(byte[] subchannel) - { - byte[] cdTextPack1 = new byte[18]; - byte[] cdTextPack2 = new byte[18]; - byte[] cdTextPack3 = new byte[18]; - byte[] cdTextPack4 = new byte[18]; - - int i = 0; - - for(int j = 0; j < 18; j++) - { - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | (subchannel[i++] & 0x3F)); - } - - bool status = true; - - if((cdTextPack1[0] & 0x80) == 0x80) - { - ushort cdTextPack1Crc = BigEndianBitConverter.ToUInt16(cdTextPack1, 16); - byte[] cdTextPack1ForCrc = new byte[16]; - Array.Copy(cdTextPack1, 0, cdTextPack1ForCrc, 0, 16); - ushort calculatedCdtp1Crc = CRC16CCITTContext.Calculate(cdTextPack1ForCrc); - - if(cdTextPack1Crc != calculatedCdtp1Crc && - cdTextPack1Crc != 0) - status = false; - } - - if((cdTextPack2[0] & 0x80) == 0x80) - { - ushort cdTextPack2Crc = BigEndianBitConverter.ToUInt16(cdTextPack2, 16); - byte[] cdTextPack2ForCrc = new byte[16]; - Array.Copy(cdTextPack2, 0, cdTextPack2ForCrc, 0, 16); - ushort calculatedCdtp2Crc = CRC16CCITTContext.Calculate(cdTextPack2ForCrc); - - if(cdTextPack2Crc != calculatedCdtp2Crc && - cdTextPack2Crc != 0) - status = false; - } - - if((cdTextPack3[0] & 0x80) == 0x80) - { - ushort cdTextPack3Crc = BigEndianBitConverter.ToUInt16(cdTextPack3, 16); - byte[] cdTextPack3ForCrc = new byte[16]; - Array.Copy(cdTextPack3, 0, cdTextPack3ForCrc, 0, 16); - ushort calculatedCdtp3Crc = CRC16CCITTContext.Calculate(cdTextPack3ForCrc); - - if(cdTextPack3Crc != calculatedCdtp3Crc && - cdTextPack3Crc != 0) - status = false; - } - - if((cdTextPack4[0] & 0x80) != 0x80) - return status; - - ushort cdTextPack4Crc = BigEndianBitConverter.ToUInt16(cdTextPack4, 16); - byte[] cdTextPack4ForCrc = new byte[16]; - Array.Copy(cdTextPack4, 0, cdTextPack4ForCrc, 0, 16); - ushort calculatedCdtp4Crc = CRC16CCITTContext.Calculate(cdTextPack4ForCrc); - - if(cdTextPack4Crc == calculatedCdtp4Crc || - cdTextPack4Crc == 0) - return status; - - return false; - } - - bool FixQSubchannel(byte[] deSub, byte[] q, int subPos, string mcn, string isrc, bool fixCrc, out bool fixedAdr, - out bool controlFix, out bool fixedZero, out bool fixedTno, out bool fixedIndex, - out bool fixedRelPos, out bool fixedAbsPos, out bool fixedCrc, out bool fixedMcn, - out bool fixedIsrc) - { - byte amin, asec, aframe, pmin, psec, pframe; - byte rmin, rsec, rframe; - int aPos, rPos, pPos, dPos; - controlFix = false; - fixedZero = false; - fixedTno = false; - fixedIndex = false; - fixedRelPos = false; - fixedAbsPos = false; - fixedCrc = false; - fixedMcn = false; - fixedIsrc = false; - - byte[] preQ = new byte[12]; - byte[] nextQ = new byte[12]; - Array.Copy(deSub, (subPos + 12) - 96, preQ, 0, 12); - Array.Copy(deSub, subPos + 12 + 96, nextQ, 0, 12); - bool status; - - CRC16CCITTContext.Data(preQ, 10, out byte[] preCrc); - bool preCrcOk = preCrc[0] == preQ[10] && preCrc[1] == preQ[11]; - - CRC16CCITTContext.Data(nextQ, 10, out byte[] nextCrc); - bool nextCrcOk = nextCrc[0] == nextQ[10] && nextCrc[1] == nextQ[11]; - - fixedAdr = false; - - // Extraneous bits in ADR - if((q[0] & 0xC) != 0) - { - q[0] &= 0xF3; - fixedAdr = true; - } - - CRC16CCITTContext.Data(q, 10, out byte[] qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(fixedAdr && status) - return true; - - int oldAdr = q[0] & 0x3; - - // Try Q-Mode 1 - q[0] = (byte)((q[0] & 0xF0) + 1); - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - { - fixedAdr = true; - - return true; - } - - // Try Q-Mode 2 - q[0] = (byte)((q[0] & 0xF0) + 2); - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - { - fixedAdr = true; - - return true; - } - - // Try Q-Mode 3 - q[0] = (byte)((q[0] & 0xF0) + 3); - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - { - fixedAdr = true; - - return true; - } - - q[0] = (byte)((q[0] & 0xF0) + oldAdr); - - oldAdr = q[0]; - - // Try using previous control - if(preCrcOk && (q[0] & 0xF0) != (preQ[0] & 0xF0)) - { - q[0] = (byte)((q[0] & 0x03) + (preQ[0] & 0xF0)); - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - { - controlFix = true; - - return true; - } - - q[0] = (byte)oldAdr; - } - - // Try using next control - if(nextCrcOk && (q[0] & 0xF0) != (nextQ[0] & 0xF0)) - { - q[0] = (byte)((q[0] & 0x03) + (nextQ[0] & 0xF0)); - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - { - controlFix = true; - - return true; - } - - q[0] = (byte)oldAdr; - } - - if(preCrcOk && - nextCrcOk && - (nextQ[0] & 0xF0) == (preQ[0] & 0xF0) && - (q[0] & 0xF0) != (nextQ[0] & 0xF0)) - { - q[0] = (byte)((q[0] & 0x03) + (nextQ[0] & 0xF0)); - - controlFix = true; - } - - if((q[0] & 0x3) == 1) - { - // ZERO not zero - if(q[6] != 0) - { - q[6] = 0; - fixedZero = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - - if(preCrcOk && nextCrcOk) - { - if(preQ[1] == nextQ[1] && - preQ[1] != q[1]) - { - q[1] = preQ[1]; - fixedTno = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - if(preCrcOk && nextCrcOk) - { - if(preQ[2] == nextQ[2] && - preQ[2] != q[2]) - { - q[2] = preQ[2]; - fixedIndex = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - amin = (byte)(((q[7] / 16) * 10) + (q[7] & 0x0F)); - asec = (byte)(((q[8] / 16) * 10) + (q[8] & 0x0F)); - aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); - aPos = ((amin * 60 * 75) + (asec * 75) + aframe) - 150; - - pmin = (byte)(((q[3] / 16) * 10) + (q[3] & 0x0F)); - psec = (byte)(((q[4] / 16) * 10) + (q[4] & 0x0F)); - pframe = (byte)(((q[5] / 16) * 10) + (q[5] & 0x0F)); - pPos = (pmin * 60 * 75) + (psec * 75) + pframe; - - // TODO: pregap - // Not pregap - if(q[2] > 0) - { - // Previous was not pregap either - if(preQ[2] > 0 && preCrcOk) - { - rmin = (byte)(((preQ[3] / 16) * 10) + (preQ[3] & 0x0F)); - rsec = (byte)(((preQ[4] / 16) * 10) + (preQ[4] & 0x0F)); - rframe = (byte)(((preQ[5] / 16) * 10) + (preQ[5] & 0x0F)); - rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; - - dPos = pPos - rPos; - - if(dPos != 1) - { - q[3] = preQ[3]; - q[4] = preQ[4]; - q[5] = preQ[5]; - - // BCD add 1, so 0x39 becomes 0x40 - if((q[5] & 0xF) == 9) - q[5] += 7; - else - q[5]++; - - // 74 frames, so from 0x00 to 0x74, BCD - if(q[5] >= 0x74) - { - // 0 frames - q[5] = 0; - - // Add 1 second - if((q[4] & 0xF) == 9) - q[4] += 7; - else - q[4]++; - - // 60 seconds, so from 0x00 to 0x59, BCD - if(q[4] >= 0x59) - { - // 0 seconds - q[4] = 0; - - // Add 1 minute - q[3]++; - } - } - - fixedRelPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - // Next is not pregap and we didn't fix relative position with previous - if(nextQ[2] > 0 && - nextCrcOk && - !fixedRelPos) - { - rmin = (byte)(((nextQ[3] / 16) * 10) + (nextQ[3] & 0x0F)); - rsec = (byte)(((nextQ[4] / 16) * 10) + (nextQ[4] & 0x0F)); - rframe = (byte)(((nextQ[5] / 16) * 10) + (nextQ[5] & 0x0F)); - rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; - - dPos = rPos - pPos; - - if(dPos != 1) - { - q[3] = nextQ[3]; - q[4] = nextQ[4]; - q[5] = nextQ[5]; - - // If frames is 0 - if(q[5] == 0) - { - // If seconds is 0 - if(q[4] == 0) - { - // BCD decrease minutes - if((q[3] & 0xF) == 0) - q[3] = (byte)((q[3] & 0xF0) - 0x10); - else - q[3]--; - - q[4] = 0x59; - q[5] = 0x73; - } - else - { - // BCD decrease seconds - if((q[4] & 0xF) == 0) - q[4] = (byte)((q[4] & 0xF0) - 0x10); - else - q[4]--; - - q[5] = 0x73; - } - } - - // BCD decrease frames - else if((q[5] & 0xF) == 0) - q[5] = (byte)((q[5] & 0xF0) - 0x10); - else - q[5]--; - - fixedRelPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - } - - if(preCrcOk) - { - rmin = (byte)(((preQ[7] / 16) * 10) + (preQ[7] & 0x0F)); - rsec = (byte)(((preQ[8] / 16) * 10) + (preQ[8] & 0x0F)); - rframe = (byte)(((preQ[9] / 16) * 10) + (preQ[9] & 0x0F)); - rPos = ((rmin * 60 * 75) + (rsec * 75) + rframe) - 150; - - dPos = aPos - rPos; - - if(dPos != 1) - { - q[7] = preQ[7]; - q[8] = preQ[8]; - q[9] = preQ[9]; - - // BCD add 1, so 0x39 becomes 0x40 - if((q[9] & 0xF) == 9) - q[9] += 7; - else - q[9]++; - - // 74 frames, so from 0x00 to 0x74, BCD - if(q[9] >= 0x74) - { - // 0 frames - q[9] = 0; - - // Add 1 second - if((q[8] & 0xF) == 9) - q[8] += 7; - else - q[8]++; - - // 60 seconds, so from 0x00 to 0x59, BCD - if(q[8] >= 0x59) - { - // 0 seconds - q[8] = 0; - - // Add 1 minute - q[7]++; - } - } - - fixedAbsPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - // Next is not pregap and we didn't fix relative position with previous - if(nextQ[2] > 0 && - nextCrcOk && - !fixedAbsPos) - { - rmin = (byte)(((nextQ[7] / 16) * 10) + (nextQ[7] & 0x0F)); - rsec = (byte)(((nextQ[8] / 16) * 10) + (nextQ[8] & 0x0F)); - rframe = (byte)(((nextQ[9] / 16) * 10) + (nextQ[9] & 0x0F)); - rPos = ((rmin * 60 * 75) + (rsec * 75) + rframe) - 150; - - dPos = rPos - pPos; - - if(dPos != 1) - { - q[7] = nextQ[7]; - q[8] = nextQ[8]; - q[9] = nextQ[9]; - - // If frames is 0 - if(q[9] == 0) - { - // If seconds is 0 - if(q[8] == 0) - { - // BCD decrease minutes - if((q[7] & 0xF) == 0) - q[7] = (byte)((q[7] & 0xF0) - 0x10); - else - q[7]--; - - q[8] = 0x59; - q[9] = 0x73; - } - else - { - // BCD decrease seconds - if((q[8] & 0xF) == 0) - q[8] = (byte)((q[8] & 0xF0) - 0x10); - else - q[8]--; - - q[9] = 0x73; - } - } - - // BCD decrease frames - else if((q[9] & 0xF) == 0) - q[9] = (byte)((q[9] & 0xF0) - 0x10); - else - q[9]--; - - fixedAbsPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - // Game Over - if(!fixCrc || status) - return false; - - if(preCrcOk) - { - rmin = (byte)(((preQ[7] / 16) * 10) + (preQ[7] & 0x0F)); - rsec = (byte)(((preQ[8] / 16) * 10) + (preQ[8] & 0x0F)); - rframe = (byte)(((preQ[9] / 16) * 10) + (preQ[9] & 0x0F)); - rPos = ((rmin * 60 * 75) + (rsec * 75) + rframe) - 150; - - dPos = aPos - rPos; - - bool absOk = dPos == 1; - - rmin = (byte)(((preQ[3] / 16) * 10) + (preQ[3] & 0x0F)); - rsec = (byte)(((preQ[4] / 16) * 10) + (preQ[4] & 0x0F)); - rframe = (byte)(((preQ[5] / 16) * 10) + (preQ[5] & 0x0F)); - rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; - - dPos = pPos - rPos; - - bool relOk = dPos == 1; - - if(q[0] != preQ[0] || - q[1] != preQ[1] || - q[2] != preQ[2] || - q[6] != 0 || - !absOk || - !relOk) - return false; - - CRC16CCITTContext.Data(q, 10, out qCrc); - q[10] = qCrc[0]; - q[11] = qCrc[1]; - - fixedCrc = true; - - return true; - } - - if(nextCrcOk) - { - rmin = (byte)(((nextQ[7] / 16) * 10) + (nextQ[7] & 0x0F)); - rsec = (byte)(((nextQ[8] / 16) * 10) + (nextQ[8] & 0x0F)); - rframe = (byte)(((nextQ[9] / 16) * 10) + (nextQ[9] & 0x0F)); - rPos = ((rmin * 60 * 75) + (rsec * 75) + rframe) - 150; - - dPos = rPos - aPos; - - bool absOk = dPos == 1; - - rmin = (byte)(((nextQ[3] / 16) * 10) + (nextQ[3] & 0x0F)); - rsec = (byte)(((nextQ[4] / 16) * 10) + (nextQ[4] & 0x0F)); - rframe = (byte)(((nextQ[5] / 16) * 10) + (nextQ[5] & 0x0F)); - rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; - - dPos = rPos - pPos; - - bool relOk = dPos == 1; - - if(q[0] != nextQ[0] || - q[1] != nextQ[1] || - q[2] != nextQ[2] || - q[6] != 0 || - !absOk || - !relOk) - return false; - - CRC16CCITTContext.Data(q, 10, out qCrc); - q[10] = qCrc[0]; - q[11] = qCrc[1]; - - fixedCrc = true; - - return true; - } - - // Ok if previous and next are both BAD I won't rewrite the CRC at all - } - else if((q[0] & 0x3) == 2) - { - if(preCrcOk) - { - rframe = (byte)(((preQ[9] / 16) * 10) + (preQ[9] & 0x0F)); - aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); - - if(aframe - rframe != 1) - { - q[9] = preQ[9]; - - if((q[9] & 0xF) == 9) - q[9] += 7; - else - q[9]++; - - if(q[9] >= 0x74) - q[9] = 0; - - fixedAbsPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - else if(nextCrcOk) - { - rframe = (byte)(((nextQ[9] / 16) * 10) + (nextQ[9] & 0x0F)); - aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); - - if(aframe - rframe != 1) - { - q[9] = nextQ[9]; - - if(q[9] == 0) - q[9] = 0x73; - else if((q[9] & 0xF) == 0) - q[9] = (byte)((q[9] & 0xF0) - 0x10); - else - q[9]--; - - fixedAbsPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - if(mcn != null) - { - q[1] = (byte)((((mcn[0] - 0x30) & 0x0F) * 16) + ((mcn[1] - 0x30) & 0x0F)); - q[2] = (byte)((((mcn[2] - 0x30) & 0x0F) * 16) + ((mcn[3] - 0x30) & 0x0F)); - q[3] = (byte)((((mcn[4] - 0x30) & 0x0F) * 16) + ((mcn[5] - 0x30) & 0x0F)); - q[4] = (byte)((((mcn[6] - 0x30) & 0x0F) * 16) + ((mcn[7] - 0x30) & 0x0F)); - q[5] = (byte)((((mcn[8] - 0x30) & 0x0F) * 16) + ((mcn[9] - 0x30) & 0x0F)); - q[6] = (byte)((((mcn[10] - 0x30) & 0x0F) * 16) + ((mcn[11] - 0x30) & 0x0F)); - q[7] = (byte)(((mcn[12] - 0x30) & 0x0F) * 8); - q[8] = 0; - - fixedMcn = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - - if(!fixCrc || - !nextCrcOk || - !preCrcOk) - return false; - - CRC16CCITTContext.Data(q, 10, out qCrc); - q[10] = qCrc[0]; - q[11] = qCrc[1]; - - fixedCrc = true; - - return true; - } - else if((q[0] & 0x3) == 3) - { - if(preCrcOk) - { - rframe = (byte)(((preQ[9] / 16) * 10) + (preQ[9] & 0x0F)); - aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); - - if(aframe - rframe != 1) - { - q[9] = preQ[9]; - - if((q[9] & 0xF) == 9) - q[9] += 7; - else - q[9]++; - - if(q[9] >= 0x74) - q[9] = 0; - - fixedAbsPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - else if(nextCrcOk) - { - rframe = (byte)(((nextQ[9] / 16) * 10) + (nextQ[9] & 0x0F)); - aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); - - if(aframe - rframe != 1) - { - q[9] = nextQ[9]; - - if(q[9] == 0) - q[9] = 0x73; - else if((q[9] & 0xF) == 0) - q[9] = (byte)((q[9] & 0xF0) - 0x10); - else - q[9]--; - - fixedAbsPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - if(isrc != null) - { - byte i1 = Subchannel.GetIsrcCode(isrc[0]); - byte i2 = Subchannel.GetIsrcCode(isrc[1]); - byte i3 = Subchannel.GetIsrcCode(isrc[2]); - byte i4 = Subchannel.GetIsrcCode(isrc[3]); - byte i5 = Subchannel.GetIsrcCode(isrc[4]); - - q[1] = (byte)((i1 << 2) + ((i2 & 0x30) >> 4)); - q[2] = (byte)(((i2 & 0xF) << 4) + (i3 >> 2)); - q[3] = (byte)(((i3 & 0x3) << 6) + i4); - q[4] = (byte)(i5 << 2); - q[5] = (byte)((((isrc[5] - 0x30) & 0x0F) * 16) + ((isrc[6] - 0x30) & 0x0F)); - q[6] = (byte)((((isrc[7] - 0x30) & 0x0F) * 16) + ((isrc[8] - 0x30) & 0x0F)); - q[7] = (byte)((((isrc[9] - 0x30) & 0x0F) * 16) + ((isrc[10] - 0x30) & 0x0F)); - q[8] = (byte)(((isrc[11] - 0x30) & 0x0F) * 16); - - fixedIsrc = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - - if(!fixCrc || - !nextCrcOk || - !preCrcOk) - return false; - - CRC16CCITTContext.Data(q, 10, out qCrc); - q[10] = qCrc[0]; - q[11] = qCrc[1]; - - fixedCrc = true; - - return true; - } - - return false; - } - - void GenerateSubchannels(HashSet subchannelExtents, Track[] tracks, Dictionary trackFlags, - ulong blocks, SubchannelLog subLog) - { - InitProgress?.Invoke(); - - foreach(int sector in subchannelExtents) - { - Track track = tracks.LastOrDefault(t => (int)t.TrackStartSector <= sector); - byte trkFlags; - byte flags; - ulong trackStart; - ulong pregap; - - // Hidden track - if(track.TrackSequence == 0) - { - track = tracks.FirstOrDefault(t => (int)t.TrackSequence == 1); - trackStart = 0; - pregap = track.TrackStartSector; - } - else - { - trackStart = track.TrackStartSector; - pregap = track.TrackPregap; - } - - if(!trackFlags.TryGetValue((byte)track.TrackSequence, out trkFlags) && - track.TrackType != TrackType.Audio) - flags = (byte)CdFlags.DataTrack; - else - flags = trkFlags; - - byte index; - - if(track.Indexes?.Count > 0) - index = (byte)track.Indexes.LastOrDefault(i => i.Value >= sector).Key; - else - index = 0; - - UpdateProgress?.Invoke($"Generating subchannel for sector {sector}...", sector, (long)blocks); - _dumpLog?.WriteLine($"Generating subchannel for sector {sector}."); - - byte[] sub = Subchannel.Generate(sector, track.TrackSequence, (int)pregap, (int)trackStart, flags, - index); - - _outputPlugin.WriteSectorsTag(sub, (ulong)sector, 1, SectorTagType.CdSectorSubchannel); - - subLog.WriteEntry(sub, true, sector, 1, true, false); - } - - EndProgress?.Invoke(); - } } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs index a0a098285..a16c98757 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs @@ -193,9 +193,10 @@ namespace Aaru.Core.Devices.Dumping Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); _outputPlugin.WriteSectorLong(data, badSector); - bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector, - 1, subLog, isrcs, (byte)track.TrackSequence, ref mcn, - tracks, subchannelExtents); + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.TrackSequence, ref mcn, + tracks, subchannelExtents, _fixSubchannelPosition, _outputPlugin, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus); // Set tracks and go back if(!indexesChanged) diff --git a/Aaru.Core/Media/CompactDisc.cs b/Aaru.Core/Media/CompactDisc.cs new file mode 100644 index 000000000..397989f90 --- /dev/null +++ b/Aaru.Core/Media/CompactDisc.cs @@ -0,0 +1,1441 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Aaru.Checksums; +using Aaru.CommonTypes.Enums; +using Aaru.CommonTypes.Interfaces; +using Aaru.CommonTypes.Structs; +using Aaru.Core.Logging; +using Aaru.Decoders.CD; +using Aaru.Devices; + +namespace Aaru.Core.Media +{ + public static class CompactDisc + { + // Return true if indexes have changed + public static bool WriteSubchannelToImage(MmcSubchannel supportedSubchannel, MmcSubchannel desiredSubchannel, + byte[] sub, ulong sectorAddress, uint length, SubchannelLog subLog, + Dictionary isrcs, byte currentTrack, ref string mcn, + Track[] tracks, HashSet subchannelExtents, + bool fixSubchannelPosition, IWritableImage outputPlugin, + bool fixSubchannel, bool fixSubchannelCrc, DumpLog dumpLog, + UpdateStatusHandler updateStatus) + { + if(supportedSubchannel == MmcSubchannel.Q16) + sub = Subchannel.ConvertQToRaw(sub); + + if(!fixSubchannelPosition && + desiredSubchannel != MmcSubchannel.None) + outputPlugin.WriteSectorsTag(sub, sectorAddress, length, SectorTagType.CdSectorSubchannel); + + subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)sectorAddress, length, false, + false); + + byte[] deSub = Subchannel.Deinterleave(sub); + + bool indexesChanged = + CheckIndexesFromSubchannel(deSub, isrcs, currentTrack, ref mcn, tracks, dumpLog, updateStatus); + + if(!fixSubchannelPosition || + desiredSubchannel == MmcSubchannel.None) + return indexesChanged; + + int prePos = int.MinValue; + + // Check subchannel + for(int subPos = 0; subPos < deSub.Length; subPos += 96) + { + long lba = (long)sectorAddress + (subPos / 96); + bool @fixed = false; + 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]; + + bool pOk = true; + int pWeight = 0; + + bool rwOk = true; + + for(int p = subPos; p < subPos + 12; p++) + { + if(deSub[p] != 0 && + deSub[p] != 255) + pOk = false; + + for(int w = 0; w < 8; w++) + if(((deSub[p] >> w) & 1) > 0) + pWeight++; + } + + for(int rw = subPos + 24; rw < subPos + 96; rw++) + { + if(deSub[rw] == 0) + continue; + + rwOk = false; + + break; + } + + bool rwPacket = false; + bool cdtextPacket = false; + + if(!rwOk) + { + byte[] sectorSub = new byte[96]; + Array.Copy(sub, subPos, sectorSub, 0, 96); + + DetectRwPackets(sectorSub, out _, out rwPacket, out cdtextPacket); + + // TODO: CD+G reed solomon + if(rwPacket && !cdtextPacket) + rwOk = true; + + if(cdtextPacket) + rwOk = CheckCdTextPackets(sectorSub); + } + + if(!pOk && fixSubchannel) + { + if(pWeight >= 48) + for(int p = subPos; p < subPos + 12; p++) + deSub[p] = 255; + else + for(int p = subPos; p < subPos + 12; p++) + deSub[p] = 0; + + pOk = true; + @fixed = true; + + subLog.WritePFix(lba); + } + + if(!rwOk && + !rwPacket && + !cdtextPacket && + fixSubchannel) + { + for(int rw = subPos + 24; rw < subPos + 96; rw++) + deSub[rw] = 0; + + rwOk = true; + @fixed = true; + + subLog.WriteRwFix(lba); + } + + byte smin, ssec, amin, asec, aframe; + int aPos; + + if(!crcOk && + fixSubchannel && + subPos > 0 && + subPos < deSub.Length - 96) + { + isrcs.TryGetValue(currentTrack, out string knownGoodIsrc); + + crcOk = FixQSubchannel(deSub, q, subPos, mcn, knownGoodIsrc, fixSubchannelCrc, out bool fixedAdr, + out bool controlFix, out bool fixedZero, out bool fixedTno, + out bool fixedIndex, out bool fixedRelPos, out bool fixedAbsPos, + out bool fixedCrc, out bool fixedMcn, out bool fixedIsrc); + + if(crcOk) + { + Array.Copy(q, 0, deSub, subPos + 12, 12); + @fixed = true; + + if(fixedAdr) + subLog.WriteQAdrFix(lba); + + if(controlFix) + subLog.WriteQCtrlFix(lba); + + if(fixedZero) + subLog.WriteQZeroFix(lba); + + if(fixedTno) + subLog.WriteQTnoFix(lba); + + if(fixedIndex) + subLog.WriteQIndexFix(lba); + + if(fixedRelPos) + subLog.WriteQRelPosFix(lba); + + if(fixedAbsPos) + subLog.WriteQAbsPosFix(lba); + + if(fixedCrc) + subLog.WriteQCrcFix(lba); + + if(fixedMcn) + subLog.WriteQMcnFix(lba); + + if(fixedIsrc) + subLog.WriteQIsrcFix(lba); + } + } + + if(!pOk || + !crcOk || + !rwOk) + continue; + + aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); + + if((q[0] & 0x3) == 1) + { + amin = (byte)(((q[7] / 16) * 10) + (q[7] & 0x0F)); + asec = (byte)(((q[8] / 16) * 10) + (q[8] & 0x0F)); + aPos = ((amin * 60 * 75) + (asec * 75) + aframe) - 150; + } + else + { + ulong expectedSectorAddress = sectorAddress + (ulong)(subPos / 96) + 150; + smin = (byte)(expectedSectorAddress / 60 / 75); + expectedSectorAddress -= (ulong)(smin * 60 * 75); + ssec = (byte)(expectedSectorAddress / 75); + + aPos = ((smin * 60 * 75) + (ssec * 75) + aframe) - 150; + + // Next second + if(aPos < prePos) + aPos += 75; + } + + // TODO: Negative sectors + if(aPos < 0) + continue; + + prePos = aPos; + + byte[] posSub = new byte[96]; + Array.Copy(deSub, subPos, posSub, 0, 96); + posSub = Subchannel.Interleave(posSub); + outputPlugin.WriteSectorTag(posSub, (ulong)aPos, SectorTagType.CdSectorSubchannel); + + subchannelExtents.Remove(aPos); + + if(@fixed) + subLog?.WriteEntry(posSub, supportedSubchannel == MmcSubchannel.Raw, lba, 1, false, true); + } + + return indexesChanged; + } + + static bool CheckIndexesFromSubchannel(byte[] deSub, Dictionary isrcs, byte currentTrack, + ref string mcn, Track[] tracks, DumpLog dumpLog, + UpdateStatusHandler updateStatus) + { + // 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) + { + 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; + + // Pregap + if(q[2] == 0) + { + 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) + continue; + + ulong oldPregap = tracks[i].TrackPregap; + + tracks[i].TrackPregap = (ulong)qPos; + tracks[i].TrackStartSector -= tracks[i].TrackPregap - oldPregap; + + 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; + } + + byte amin = (byte)(((q[7] / 16) * 10) + (q[7] & 0x0F)); + byte asec = (byte)(((q[8] / 16) * 10) + (q[8] & 0x0F)); + byte aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); + int aPos = ((amin * 60 * 75) + (asec * 75) + aframe) - 150; + + if(tracks[i].Indexes.ContainsKey(q[2]) && + aPos >= tracks[i].Indexes[q[2]]) + return false; + + dumpLog?.WriteLine($"Setting index {q[2]} for track {trackNo} to LBA {aPos}."); + updateStatus?.Invoke($"Setting index {q[2]} for track {trackNo} to LBA {aPos}."); + + tracks[i].Indexes[q[2]] = aPos; + + return true; + } + } + } + + return false; + } + + static void DetectRwPackets(byte[] subchannel, out bool zero, out bool rwPacket, out bool cdtextPacket) + { + zero = false; + rwPacket = false; + cdtextPacket = false; + + byte[] cdTextPack1 = new byte[18]; + byte[] cdTextPack2 = new byte[18]; + byte[] cdTextPack3 = new byte[18]; + byte[] cdTextPack4 = new byte[18]; + byte[] cdSubRwPack1 = new byte[24]; + byte[] cdSubRwPack2 = new byte[24]; + byte[] cdSubRwPack3 = new byte[24]; + byte[] cdSubRwPack4 = new byte[24]; + + int i = 0; + + for(int j = 0; j < 18; j++) + { + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | (subchannel[i++] & 0x3F)); + } + + i = 0; + + for(int j = 0; j < 24; j++) + cdSubRwPack1[j] = (byte)(subchannel[i++] & 0x3F); + + for(int j = 0; j < 24; j++) + cdSubRwPack2[j] = (byte)(subchannel[i++] & 0x3F); + + for(int j = 0; j < 24; j++) + cdSubRwPack3[j] = (byte)(subchannel[i++] & 0x3F); + + for(int j = 0; j < 24; j++) + cdSubRwPack4[j] = (byte)(subchannel[i++] & 0x3F); + + switch(cdSubRwPack1[0]) + { + case 0x00: + zero = true; + + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x18: + case 0x38: + rwPacket = true; + + break; + case 0x14: + cdtextPacket = true; + + break; + } + + switch(cdSubRwPack2[0]) + { + case 0x00: + zero = true; + + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x18: + case 0x38: + rwPacket = true; + + break; + case 0x14: + cdtextPacket = true; + + break; + } + + switch(cdSubRwPack3[0]) + { + case 0x00: + zero = true; + + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x18: + case 0x38: + rwPacket = true; + + break; + case 0x14: + cdtextPacket = true; + + break; + } + + switch(cdSubRwPack4[0]) + { + case 0x00: + zero = true; + + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x18: + case 0x38: + rwPacket = true; + + break; + case 0x14: + cdtextPacket = true; + + break; + } + + if((cdTextPack1[0] & 0x80) == 0x80) + cdtextPacket = true; + + if((cdTextPack2[0] & 0x80) == 0x80) + cdtextPacket = true; + + if((cdTextPack3[0] & 0x80) == 0x80) + cdtextPacket = true; + + if((cdTextPack4[0] & 0x80) == 0x80) + cdtextPacket = true; + } + + static bool CheckCdTextPackets(byte[] subchannel) + { + byte[] cdTextPack1 = new byte[18]; + byte[] cdTextPack2 = new byte[18]; + byte[] cdTextPack3 = new byte[18]; + byte[] cdTextPack4 = new byte[18]; + + int i = 0; + + for(int j = 0; j < 18; j++) + { + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | (subchannel[i++] & 0x3F)); + } + + bool status = true; + + if((cdTextPack1[0] & 0x80) == 0x80) + { + ushort cdTextPack1Crc = BigEndianBitConverter.ToUInt16(cdTextPack1, 16); + byte[] cdTextPack1ForCrc = new byte[16]; + Array.Copy(cdTextPack1, 0, cdTextPack1ForCrc, 0, 16); + ushort calculatedCdtp1Crc = CRC16CCITTContext.Calculate(cdTextPack1ForCrc); + + if(cdTextPack1Crc != calculatedCdtp1Crc && + cdTextPack1Crc != 0) + status = false; + } + + if((cdTextPack2[0] & 0x80) == 0x80) + { + ushort cdTextPack2Crc = BigEndianBitConverter.ToUInt16(cdTextPack2, 16); + byte[] cdTextPack2ForCrc = new byte[16]; + Array.Copy(cdTextPack2, 0, cdTextPack2ForCrc, 0, 16); + ushort calculatedCdtp2Crc = CRC16CCITTContext.Calculate(cdTextPack2ForCrc); + + if(cdTextPack2Crc != calculatedCdtp2Crc && + cdTextPack2Crc != 0) + status = false; + } + + if((cdTextPack3[0] & 0x80) == 0x80) + { + ushort cdTextPack3Crc = BigEndianBitConverter.ToUInt16(cdTextPack3, 16); + byte[] cdTextPack3ForCrc = new byte[16]; + Array.Copy(cdTextPack3, 0, cdTextPack3ForCrc, 0, 16); + ushort calculatedCdtp3Crc = CRC16CCITTContext.Calculate(cdTextPack3ForCrc); + + if(cdTextPack3Crc != calculatedCdtp3Crc && + cdTextPack3Crc != 0) + status = false; + } + + if((cdTextPack4[0] & 0x80) != 0x80) + return status; + + ushort cdTextPack4Crc = BigEndianBitConverter.ToUInt16(cdTextPack4, 16); + byte[] cdTextPack4ForCrc = new byte[16]; + Array.Copy(cdTextPack4, 0, cdTextPack4ForCrc, 0, 16); + ushort calculatedCdtp4Crc = CRC16CCITTContext.Calculate(cdTextPack4ForCrc); + + if(cdTextPack4Crc == calculatedCdtp4Crc || + cdTextPack4Crc == 0) + return status; + + return false; + } + + static bool FixQSubchannel(byte[] deSub, byte[] q, int subPos, string mcn, string isrc, bool fixCrc, + out bool fixedAdr, out bool controlFix, out bool fixedZero, out bool fixedTno, + out bool fixedIndex, out bool fixedRelPos, out bool fixedAbsPos, out bool fixedCrc, + out bool fixedMcn, out bool fixedIsrc) + { + byte amin, asec, aframe, pmin, psec, pframe; + byte rmin, rsec, rframe; + int aPos, rPos, pPos, dPos; + controlFix = false; + fixedZero = false; + fixedTno = false; + fixedIndex = false; + fixedRelPos = false; + fixedAbsPos = false; + fixedCrc = false; + fixedMcn = false; + fixedIsrc = false; + + byte[] preQ = new byte[12]; + byte[] nextQ = new byte[12]; + Array.Copy(deSub, (subPos + 12) - 96, preQ, 0, 12); + Array.Copy(deSub, subPos + 12 + 96, nextQ, 0, 12); + bool status; + + CRC16CCITTContext.Data(preQ, 10, out byte[] preCrc); + bool preCrcOk = preCrc[0] == preQ[10] && preCrc[1] == preQ[11]; + + CRC16CCITTContext.Data(nextQ, 10, out byte[] nextCrc); + bool nextCrcOk = nextCrc[0] == nextQ[10] && nextCrc[1] == nextQ[11]; + + fixedAdr = false; + + // Extraneous bits in ADR + if((q[0] & 0xC) != 0) + { + q[0] &= 0xF3; + fixedAdr = true; + } + + CRC16CCITTContext.Data(q, 10, out byte[] qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(fixedAdr && status) + return true; + + int oldAdr = q[0] & 0x3; + + // Try Q-Mode 1 + q[0] = (byte)((q[0] & 0xF0) + 1); + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + { + fixedAdr = true; + + return true; + } + + // Try Q-Mode 2 + q[0] = (byte)((q[0] & 0xF0) + 2); + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + { + fixedAdr = true; + + return true; + } + + // Try Q-Mode 3 + q[0] = (byte)((q[0] & 0xF0) + 3); + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + { + fixedAdr = true; + + return true; + } + + q[0] = (byte)((q[0] & 0xF0) + oldAdr); + + oldAdr = q[0]; + + // Try using previous control + if(preCrcOk && (q[0] & 0xF0) != (preQ[0] & 0xF0)) + { + q[0] = (byte)((q[0] & 0x03) + (preQ[0] & 0xF0)); + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + { + controlFix = true; + + return true; + } + + q[0] = (byte)oldAdr; + } + + // Try using next control + if(nextCrcOk && (q[0] & 0xF0) != (nextQ[0] & 0xF0)) + { + q[0] = (byte)((q[0] & 0x03) + (nextQ[0] & 0xF0)); + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + { + controlFix = true; + + return true; + } + + q[0] = (byte)oldAdr; + } + + if(preCrcOk && + nextCrcOk && + (nextQ[0] & 0xF0) == (preQ[0] & 0xF0) && + (q[0] & 0xF0) != (nextQ[0] & 0xF0)) + { + q[0] = (byte)((q[0] & 0x03) + (nextQ[0] & 0xF0)); + + controlFix = true; + } + + if((q[0] & 0x3) == 1) + { + // ZERO not zero + if(q[6] != 0) + { + q[6] = 0; + fixedZero = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + + if(preCrcOk && nextCrcOk) + { + if(preQ[1] == nextQ[1] && + preQ[1] != q[1]) + { + q[1] = preQ[1]; + fixedTno = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + + if(preCrcOk && nextCrcOk) + { + if(preQ[2] == nextQ[2] && + preQ[2] != q[2]) + { + q[2] = preQ[2]; + fixedIndex = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + + amin = (byte)(((q[7] / 16) * 10) + (q[7] & 0x0F)); + asec = (byte)(((q[8] / 16) * 10) + (q[8] & 0x0F)); + aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); + aPos = ((amin * 60 * 75) + (asec * 75) + aframe) - 150; + + pmin = (byte)(((q[3] / 16) * 10) + (q[3] & 0x0F)); + psec = (byte)(((q[4] / 16) * 10) + (q[4] & 0x0F)); + pframe = (byte)(((q[5] / 16) * 10) + (q[5] & 0x0F)); + pPos = (pmin * 60 * 75) + (psec * 75) + pframe; + + // TODO: pregap + // Not pregap + if(q[2] > 0) + { + // Previous was not pregap either + if(preQ[2] > 0 && preCrcOk) + { + rmin = (byte)(((preQ[3] / 16) * 10) + (preQ[3] & 0x0F)); + rsec = (byte)(((preQ[4] / 16) * 10) + (preQ[4] & 0x0F)); + rframe = (byte)(((preQ[5] / 16) * 10) + (preQ[5] & 0x0F)); + rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; + + dPos = pPos - rPos; + + if(dPos != 1) + { + q[3] = preQ[3]; + q[4] = preQ[4]; + q[5] = preQ[5]; + + // BCD add 1, so 0x39 becomes 0x40 + if((q[5] & 0xF) == 9) + q[5] += 7; + else + q[5]++; + + // 74 frames, so from 0x00 to 0x74, BCD + if(q[5] >= 0x74) + { + // 0 frames + q[5] = 0; + + // Add 1 second + if((q[4] & 0xF) == 9) + q[4] += 7; + else + q[4]++; + + // 60 seconds, so from 0x00 to 0x59, BCD + if(q[4] >= 0x59) + { + // 0 seconds + q[4] = 0; + + // Add 1 minute + q[3]++; + } + } + + fixedRelPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + + // Next is not pregap and we didn't fix relative position with previous + if(nextQ[2] > 0 && + nextCrcOk && + !fixedRelPos) + { + rmin = (byte)(((nextQ[3] / 16) * 10) + (nextQ[3] & 0x0F)); + rsec = (byte)(((nextQ[4] / 16) * 10) + (nextQ[4] & 0x0F)); + rframe = (byte)(((nextQ[5] / 16) * 10) + (nextQ[5] & 0x0F)); + rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; + + dPos = rPos - pPos; + + if(dPos != 1) + { + q[3] = nextQ[3]; + q[4] = nextQ[4]; + q[5] = nextQ[5]; + + // If frames is 0 + if(q[5] == 0) + { + // If seconds is 0 + if(q[4] == 0) + { + // BCD decrease minutes + if((q[3] & 0xF) == 0) + q[3] = (byte)((q[3] & 0xF0) - 0x10); + else + q[3]--; + + q[4] = 0x59; + q[5] = 0x73; + } + else + { + // BCD decrease seconds + if((q[4] & 0xF) == 0) + q[4] = (byte)((q[4] & 0xF0) - 0x10); + else + q[4]--; + + q[5] = 0x73; + } + } + + // BCD decrease frames + else if((q[5] & 0xF) == 0) + q[5] = (byte)((q[5] & 0xF0) - 0x10); + else + q[5]--; + + fixedRelPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + } + + if(preCrcOk) + { + rmin = (byte)(((preQ[7] / 16) * 10) + (preQ[7] & 0x0F)); + rsec = (byte)(((preQ[8] / 16) * 10) + (preQ[8] & 0x0F)); + rframe = (byte)(((preQ[9] / 16) * 10) + (preQ[9] & 0x0F)); + rPos = ((rmin * 60 * 75) + (rsec * 75) + rframe) - 150; + + dPos = aPos - rPos; + + if(dPos != 1) + { + q[7] = preQ[7]; + q[8] = preQ[8]; + q[9] = preQ[9]; + + // BCD add 1, so 0x39 becomes 0x40 + if((q[9] & 0xF) == 9) + q[9] += 7; + else + q[9]++; + + // 74 frames, so from 0x00 to 0x74, BCD + if(q[9] >= 0x74) + { + // 0 frames + q[9] = 0; + + // Add 1 second + if((q[8] & 0xF) == 9) + q[8] += 7; + else + q[8]++; + + // 60 seconds, so from 0x00 to 0x59, BCD + if(q[8] >= 0x59) + { + // 0 seconds + q[8] = 0; + + // Add 1 minute + q[7]++; + } + } + + fixedAbsPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + + // Next is not pregap and we didn't fix relative position with previous + if(nextQ[2] > 0 && + nextCrcOk && + !fixedAbsPos) + { + rmin = (byte)(((nextQ[7] / 16) * 10) + (nextQ[7] & 0x0F)); + rsec = (byte)(((nextQ[8] / 16) * 10) + (nextQ[8] & 0x0F)); + rframe = (byte)(((nextQ[9] / 16) * 10) + (nextQ[9] & 0x0F)); + rPos = ((rmin * 60 * 75) + (rsec * 75) + rframe) - 150; + + dPos = rPos - pPos; + + if(dPos != 1) + { + q[7] = nextQ[7]; + q[8] = nextQ[8]; + q[9] = nextQ[9]; + + // If frames is 0 + if(q[9] == 0) + { + // If seconds is 0 + if(q[8] == 0) + { + // BCD decrease minutes + if((q[7] & 0xF) == 0) + q[7] = (byte)((q[7] & 0xF0) - 0x10); + else + q[7]--; + + q[8] = 0x59; + q[9] = 0x73; + } + else + { + // BCD decrease seconds + if((q[8] & 0xF) == 0) + q[8] = (byte)((q[8] & 0xF0) - 0x10); + else + q[8]--; + + q[9] = 0x73; + } + } + + // BCD decrease frames + else if((q[9] & 0xF) == 0) + q[9] = (byte)((q[9] & 0xF0) - 0x10); + else + q[9]--; + + fixedAbsPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + // Game Over + if(!fixCrc || status) + return false; + + if(preCrcOk) + { + rmin = (byte)(((preQ[7] / 16) * 10) + (preQ[7] & 0x0F)); + rsec = (byte)(((preQ[8] / 16) * 10) + (preQ[8] & 0x0F)); + rframe = (byte)(((preQ[9] / 16) * 10) + (preQ[9] & 0x0F)); + rPos = ((rmin * 60 * 75) + (rsec * 75) + rframe) - 150; + + dPos = aPos - rPos; + + bool absOk = dPos == 1; + + rmin = (byte)(((preQ[3] / 16) * 10) + (preQ[3] & 0x0F)); + rsec = (byte)(((preQ[4] / 16) * 10) + (preQ[4] & 0x0F)); + rframe = (byte)(((preQ[5] / 16) * 10) + (preQ[5] & 0x0F)); + rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; + + dPos = pPos - rPos; + + bool relOk = dPos == 1; + + if(q[0] != preQ[0] || + q[1] != preQ[1] || + q[2] != preQ[2] || + q[6] != 0 || + !absOk || + !relOk) + return false; + + CRC16CCITTContext.Data(q, 10, out qCrc); + q[10] = qCrc[0]; + q[11] = qCrc[1]; + + fixedCrc = true; + + return true; + } + + if(nextCrcOk) + { + rmin = (byte)(((nextQ[7] / 16) * 10) + (nextQ[7] & 0x0F)); + rsec = (byte)(((nextQ[8] / 16) * 10) + (nextQ[8] & 0x0F)); + rframe = (byte)(((nextQ[9] / 16) * 10) + (nextQ[9] & 0x0F)); + rPos = ((rmin * 60 * 75) + (rsec * 75) + rframe) - 150; + + dPos = rPos - aPos; + + bool absOk = dPos == 1; + + rmin = (byte)(((nextQ[3] / 16) * 10) + (nextQ[3] & 0x0F)); + rsec = (byte)(((nextQ[4] / 16) * 10) + (nextQ[4] & 0x0F)); + rframe = (byte)(((nextQ[5] / 16) * 10) + (nextQ[5] & 0x0F)); + rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; + + dPos = rPos - pPos; + + bool relOk = dPos == 1; + + if(q[0] != nextQ[0] || + q[1] != nextQ[1] || + q[2] != nextQ[2] || + q[6] != 0 || + !absOk || + !relOk) + return false; + + CRC16CCITTContext.Data(q, 10, out qCrc); + q[10] = qCrc[0]; + q[11] = qCrc[1]; + + fixedCrc = true; + + return true; + } + + // Ok if previous and next are both BAD I won't rewrite the CRC at all + } + else if((q[0] & 0x3) == 2) + { + if(preCrcOk) + { + rframe = (byte)(((preQ[9] / 16) * 10) + (preQ[9] & 0x0F)); + aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); + + if(aframe - rframe != 1) + { + q[9] = preQ[9]; + + if((q[9] & 0xF) == 9) + q[9] += 7; + else + q[9]++; + + if(q[9] >= 0x74) + q[9] = 0; + + fixedAbsPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + else if(nextCrcOk) + { + rframe = (byte)(((nextQ[9] / 16) * 10) + (nextQ[9] & 0x0F)); + aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); + + if(aframe - rframe != 1) + { + q[9] = nextQ[9]; + + if(q[9] == 0) + q[9] = 0x73; + else if((q[9] & 0xF) == 0) + q[9] = (byte)((q[9] & 0xF0) - 0x10); + else + q[9]--; + + fixedAbsPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + + if(mcn != null) + { + q[1] = (byte)((((mcn[0] - 0x30) & 0x0F) * 16) + ((mcn[1] - 0x30) & 0x0F)); + q[2] = (byte)((((mcn[2] - 0x30) & 0x0F) * 16) + ((mcn[3] - 0x30) & 0x0F)); + q[3] = (byte)((((mcn[4] - 0x30) & 0x0F) * 16) + ((mcn[5] - 0x30) & 0x0F)); + q[4] = (byte)((((mcn[6] - 0x30) & 0x0F) * 16) + ((mcn[7] - 0x30) & 0x0F)); + q[5] = (byte)((((mcn[8] - 0x30) & 0x0F) * 16) + ((mcn[9] - 0x30) & 0x0F)); + q[6] = (byte)((((mcn[10] - 0x30) & 0x0F) * 16) + ((mcn[11] - 0x30) & 0x0F)); + q[7] = (byte)(((mcn[12] - 0x30) & 0x0F) * 8); + q[8] = 0; + + fixedMcn = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + + if(!fixCrc || + !nextCrcOk || + !preCrcOk) + return false; + + CRC16CCITTContext.Data(q, 10, out qCrc); + q[10] = qCrc[0]; + q[11] = qCrc[1]; + + fixedCrc = true; + + return true; + } + else if((q[0] & 0x3) == 3) + { + if(preCrcOk) + { + rframe = (byte)(((preQ[9] / 16) * 10) + (preQ[9] & 0x0F)); + aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); + + if(aframe - rframe != 1) + { + q[9] = preQ[9]; + + if((q[9] & 0xF) == 9) + q[9] += 7; + else + q[9]++; + + if(q[9] >= 0x74) + q[9] = 0; + + fixedAbsPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + else if(nextCrcOk) + { + rframe = (byte)(((nextQ[9] / 16) * 10) + (nextQ[9] & 0x0F)); + aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F)); + + if(aframe - rframe != 1) + { + q[9] = nextQ[9]; + + if(q[9] == 0) + q[9] = 0x73; + else if((q[9] & 0xF) == 0) + q[9] = (byte)((q[9] & 0xF0) - 0x10); + else + q[9]--; + + fixedAbsPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + + if(isrc != null) + { + byte i1 = Subchannel.GetIsrcCode(isrc[0]); + byte i2 = Subchannel.GetIsrcCode(isrc[1]); + byte i3 = Subchannel.GetIsrcCode(isrc[2]); + byte i4 = Subchannel.GetIsrcCode(isrc[3]); + byte i5 = Subchannel.GetIsrcCode(isrc[4]); + + q[1] = (byte)((i1 << 2) + ((i2 & 0x30) >> 4)); + q[2] = (byte)(((i2 & 0xF) << 4) + (i3 >> 2)); + q[3] = (byte)(((i3 & 0x3) << 6) + i4); + q[4] = (byte)(i5 << 2); + q[5] = (byte)((((isrc[5] - 0x30) & 0x0F) * 16) + ((isrc[6] - 0x30) & 0x0F)); + q[6] = (byte)((((isrc[7] - 0x30) & 0x0F) * 16) + ((isrc[8] - 0x30) & 0x0F)); + q[7] = (byte)((((isrc[9] - 0x30) & 0x0F) * 16) + ((isrc[10] - 0x30) & 0x0F)); + q[8] = (byte)(((isrc[11] - 0x30) & 0x0F) * 16); + + fixedIsrc = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + + if(!fixCrc || + !nextCrcOk || + !preCrcOk) + return false; + + CRC16CCITTContext.Data(q, 10, out qCrc); + q[10] = qCrc[0]; + q[11] = qCrc[1]; + + fixedCrc = true; + + return true; + } + + return false; + } + + public static void GenerateSubchannels(HashSet subchannelExtents, Track[] tracks, + Dictionary trackFlags, ulong blocks, SubchannelLog subLog, + DumpLog dumpLog, InitProgressHandler initProgress, + UpdateProgressHandler updateProgress, EndProgressHandler endProgress, + IWritableImage outputPlugin) + { + initProgress?.Invoke(); + + foreach(int sector in subchannelExtents) + { + Track track = tracks.LastOrDefault(t => (int)t.TrackStartSector <= sector); + byte trkFlags; + byte flags; + ulong trackStart; + ulong pregap; + + // Hidden track + if(track.TrackSequence == 0) + { + track = tracks.FirstOrDefault(t => (int)t.TrackSequence == 1); + trackStart = 0; + pregap = track.TrackStartSector; + } + else + { + trackStart = track.TrackStartSector; + pregap = track.TrackPregap; + } + + if(!trackFlags.TryGetValue((byte)track.TrackSequence, out trkFlags) && + track.TrackType != TrackType.Audio) + flags = (byte)CdFlags.DataTrack; + else + flags = trkFlags; + + byte index; + + if(track.Indexes?.Count > 0) + index = (byte)track.Indexes.LastOrDefault(i => i.Value >= sector).Key; + else + index = 0; + + updateProgress?.Invoke($"Generating subchannel for sector {sector}...", sector, (long)blocks); + dumpLog?.WriteLine($"Generating subchannel for sector {sector}."); + + byte[] sub = Subchannel.Generate(sector, track.TrackSequence, (int)pregap, (int)trackStart, flags, + index); + + outputPlugin.WriteSectorsTag(sub, (ulong)sector, 1, SectorTagType.CdSectorSubchannel); + + subLog.WriteEntry(sub, true, sector, 1, true, false); + } + + endProgress?.Invoke(); + } + } +} \ No newline at end of file