Files
Aaru/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs

1244 lines
44 KiB
C#
Raw Normal View History

// /***************************************************************************
2020-02-27 12:31:25 +00:00
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
2020-03-11 21:56:55 +00:00
// Filename : Subchannel.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
2020-03-11 21:56:55 +00:00
// Component : CompactDisc dumping.
//
// --[ Description ] ----------------------------------------------------------
//
2020-03-11 21:56:55 +00:00
// Handles CompactDisc subchannel data.
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
2020-01-03 17:51:30 +00:00
// Copyright © 2011-2020 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using Aaru.Checksums;
using Aaru.CommonTypes.Enums;
2020-06-13 19:15:27 +01:00
using Aaru.CommonTypes.Extents;
using Aaru.CommonTypes.Structs;
2020-02-27 00:33:26 +00:00
using Aaru.Core.Logging;
using Aaru.Decoders.CD;
2020-02-27 00:33:26 +00:00
using Aaru.Devices;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
2020-02-27 00:33:26 +00:00
namespace Aaru.Core.Devices.Dumping
{
partial class Dump
{
public static bool SupportsRwSubchannel(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus)
{
dumpLog?.WriteLine("Checking if drive supports full raw subchannel reading...");
updateStatus?.Invoke("Checking if drive supports full raw subchannel reading...");
2020-02-29 18:03:35 +00:00
return !dev.ReadCd(out _, out _, 0, 2352 + 96, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Raw,
dev.Timeout, out _);
}
public static bool SupportsPqSubchannel(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus)
{
dumpLog?.WriteLine("Checking if drive supports PQ subchannel reading...");
updateStatus?.Invoke("Checking if drive supports PQ subchannel reading...");
2020-02-29 18:03:35 +00:00
return !dev.ReadCd(out _, out _, 0, 2352 + 16, 1, MmcSectorTypes.AllTypes, false, false, true,
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,
2020-06-13 19:15:27 +01:00
Dictionary<byte, string> isrcs, byte currentTrack, ref string mcn, Track[] tracks,
ExtentsInt subchannelExtents)
{
if(supportedSubchannel == MmcSubchannel.Q16)
sub = Subchannel.ConvertQToRaw(sub);
if(!_fixSubchannelPosition &&
desiredSubchannel != MmcSubchannel.None)
2020-05-05 20:37:25 +01:00
_outputPlugin.WriteSectorsTag(sub, sectorAddress, length, SectorTagType.CdSectorSubchannel);
2020-05-05 20:37:25 +01:00
subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)sectorAddress, length);
byte[] deSub = Subchannel.Deinterleave(sub);
bool indexesChanged = CheckIndexesFromSubchannel(deSub, isrcs, currentTrack, ref mcn, tracks);
2020-06-13 19:15:27 +01:00
if(!_fixSubchannelPosition ||
desiredSubchannel == MmcSubchannel.None)
return indexesChanged;
int prePos = int.MinValue;
// Check subchannel
for(int subPos = 0; subPos < deSub.Length; subPos += 96)
{
2020-06-13 23:38:35 +01:00
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];
2020-06-13 19:47:51 +01:00
bool pOk = true;
int pWeight = 0;
2020-06-13 20:29:30 +01:00
bool rwOk = true;
2020-06-13 19:47:51 +01:00
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++;
}
}
2020-06-13 20:29:30 +01:00
for(int rw = subPos + 24; rw < subPos + 96; rw++)
if(deSub[rw] != 0)
rwOk = false;
2020-06-13 23:38:35 +01:00
bool rwPacket = false;
bool cdtextPacket = false;
2020-06-13 20:29:30 +01:00
if(!rwOk)
{
byte[] sectorSub = new byte[96];
Array.Copy(sub, subPos, sectorSub, 0, 96);
2020-06-13 23:38:35 +01:00
DetectRwPackets(sectorSub, out _, out rwPacket, out cdtextPacket);
2020-06-13 20:29:30 +01:00
// TODO: CD+G reed solomon
if(rwPacket && !cdtextPacket)
rwOk = true;
if(cdtextPacket)
rwOk = CheckCdTextPackets(sectorSub);
}
2020-06-13 23:38:35 +01:00
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();
}
if(!rwOk &&
!rwPacket &&
!cdtextPacket &&
_fixSubchannel)
{
for(int rw = subPos + 24; rw < subPos + 96; rw++)
deSub[rw] = 0;
rwOk = true;
@fixed = true;
subLog.WriteRwFix();
}
byte smin, ssec, sframe, amin, asec, aframe, pmin, psec, pframe;
byte rmin, rsec, rframe;
int aPos;
if(!crcOk &&
_fixSubchannel &&
subPos > 0 &&
subPos < deSub.Length - 96)
{
crcOk = FixQSubchannel(deSub, q, subPos, sectorAddress, _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);
if(crcOk)
{
Array.Copy(q, 0, deSub, subPos + 12, 12);
@fixed = true;
if(fixedAdr)
subLog.WriteQAdrFix();
if(controlFix)
subLog.WriteQCtrlFix();
if(fixedZero)
subLog.WriteQZeroFix();
if(fixedTno)
subLog.WriteQTnoFix();
if(fixedIndex)
subLog.WriteQIndexFix();
if(fixedRelPos)
subLog.WriteQRelPosFix();
if(fixedAbsPos)
subLog.WriteQAbsPosFix();
if(fixedCrc)
subLog.WriteQCrcFix();
}
}
2020-06-13 20:29:30 +01:00
if(!pOk ||
!crcOk ||
!rwOk)
continue;
2020-06-13 23:38:35 +01:00
aPos = int.MinValue;
2020-06-13 23:38:35 +01:00
aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F));
if((q[0] & 0x3) == 1)
{
2020-06-13 23:38:35 +01:00
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;
2020-06-13 23:38:35 +01:00
smin = (byte)(expectedSectorAddress / 60 / 75);
expectedSectorAddress -= (ulong)(smin * 60 * 75);
ssec = (byte)(expectedSectorAddress / 75);
expectedSectorAddress -= (ulong)(smin * 75);
sframe = (byte)(expectedSectorAddress - ((ulong)ssec * 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);
2020-06-13 19:15:27 +01:00
_outputPlugin.WriteSectorTag(posSub, (ulong)aPos, SectorTagType.CdSectorSubchannel);
2020-06-13 19:15:27 +01:00
subchannelExtents.Remove(aPos);
2020-06-13 23:38:35 +01:00
if(@fixed)
subLog?.WriteEntry(posSub, supportedSubchannel == MmcSubchannel.Raw,
(long)sectorAddress + (subPos / 96), 1);
}
return indexesChanged;
}
bool CheckIndexesFromSubchannel(byte[] deSub, Dictionary<byte, string> 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)
{
// 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;
}
2020-06-13 20:29:30 +01:00
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;
}
2020-06-13 23:38:35 +01:00
bool FixQSubchannel(byte[] deSub, byte[] q, int subPos, ulong sectorAddress, 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)
{
byte smin, ssec, sframe, 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;
ulong expectedSectorAddress = sectorAddress + (ulong)(subPos / 96) + 150;
smin = (byte)(expectedSectorAddress / 60 / 75);
expectedSectorAddress -= (ulong)(smin * 60 * 75);
ssec = (byte)(expectedSectorAddress / 75);
expectedSectorAddress -= (ulong)(smin * 75);
sframe = (byte)(expectedSectorAddress - ((ulong)ssec * 75));
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;
}
else 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)
{
// TODO: MCN
}
else if((q[0] & 0x3) == 3)
{
// TODO: ISRC
}
return false;
}
}
}