2020-07-19 22:01:25 +01:00
|
|
|
// /***************************************************************************
|
|
|
|
|
// Aaru Data Preservation Suite
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Filename : CompactDisc.cs
|
|
|
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
|
|
|
//
|
|
|
|
|
// Component : Core algorithms.
|
|
|
|
|
//
|
|
|
|
|
// --[ 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/>.
|
|
|
|
|
//
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2022-02-18 10:02:53 +00:00
|
|
|
// Copyright © 2011-2022 Natalia Portillo
|
2020-07-19 22:01:25 +01:00
|
|
|
// ****************************************************************************/
|
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
namespace Aaru.Core.Media;
|
|
|
|
|
|
2020-07-14 01:06:23 +01:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using Aaru.Checksums;
|
2020-12-31 19:28:47 +00:00
|
|
|
using Aaru.CommonTypes;
|
2020-07-14 01:06:23 +01:00
|
|
|
using Aaru.CommonTypes.Enums;
|
|
|
|
|
using Aaru.CommonTypes.Interfaces;
|
|
|
|
|
using Aaru.CommonTypes.Structs;
|
|
|
|
|
using Aaru.Core.Logging;
|
|
|
|
|
using Aaru.Decoders.CD;
|
|
|
|
|
using Aaru.Devices;
|
2020-07-20 15:43:52 +01:00
|
|
|
using Aaru.Helpers;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
/// <summary>Operations over CD based media</summary>
|
|
|
|
|
public static class CompactDisc
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
/// <summary>Writes subchannel data to an image</summary>
|
|
|
|
|
/// <param name="supportedSubchannel">Subchannel read by drive</param>
|
|
|
|
|
/// <param name="desiredSubchannel">Subchannel user wants written to image</param>
|
|
|
|
|
/// <param name="sub">Subchannel data</param>
|
|
|
|
|
/// <param name="sectorAddress">Starting sector</param>
|
|
|
|
|
/// <param name="length">How many sectors were read</param>
|
|
|
|
|
/// <param name="subLog">Subchannel log</param>
|
|
|
|
|
/// <param name="isrcs">List of ISRCs</param>
|
|
|
|
|
/// <param name="currentTrack">Current track number</param>
|
|
|
|
|
/// <param name="mcn">Disc's MCN</param>
|
|
|
|
|
/// <param name="tracks">List of tracks</param>
|
|
|
|
|
/// <param name="subchannelExtents">List of subchannel extents</param>
|
|
|
|
|
/// <param name="fixSubchannelPosition">If we want to fix subchannel position</param>
|
|
|
|
|
/// <param name="outputPlugin">Output image</param>
|
|
|
|
|
/// <param name="fixSubchannel">If we want to fix subchannel contents</param>
|
|
|
|
|
/// <param name="fixSubchannelCrc">If we want to fix Q subchannel CRC if the contents look sane</param>
|
|
|
|
|
/// <param name="dumpLog">Dumping log</param>
|
|
|
|
|
/// <param name="updateStatus">Status update callback</param>
|
|
|
|
|
/// <param name="smallestPregapLbaPerTrack">List of smallest known pregap per track</param>
|
|
|
|
|
/// <param name="dumping">Set if we are dumping, otherwise converting</param>
|
|
|
|
|
/// <returns><c>true</c> if indexes have changed, <c>false</c> otherwise</returns>
|
|
|
|
|
public static bool WriteSubchannelToImage(MmcSubchannel supportedSubchannel, MmcSubchannel desiredSubchannel,
|
|
|
|
|
byte[] sub, ulong sectorAddress, uint length, SubchannelLog subLog,
|
|
|
|
|
Dictionary<byte, string> isrcs, byte currentTrack, ref string mcn,
|
|
|
|
|
Track[] tracks, HashSet<int> subchannelExtents,
|
|
|
|
|
bool fixSubchannelPosition, IWritableOpticalImage outputPlugin,
|
|
|
|
|
bool fixSubchannel, bool fixSubchannelCrc, DumpLog dumpLog,
|
|
|
|
|
UpdateStatusHandler updateStatus,
|
2022-04-10 14:10:41 +01:00
|
|
|
Dictionary<byte, int> smallestPregapLbaPerTrack, bool dumping,
|
|
|
|
|
out List<ulong> newPregapSectors)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
// We need to work in PW raw subchannels
|
|
|
|
|
if(supportedSubchannel == MmcSubchannel.Q16)
|
|
|
|
|
sub = Subchannel.ConvertQToRaw(sub);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// If not desired to fix, or to save, the subchannel, just save as is (or none)
|
|
|
|
|
if(!fixSubchannelPosition &&
|
|
|
|
|
desiredSubchannel != MmcSubchannel.None)
|
|
|
|
|
outputPlugin.WriteSectorsTag(sub, sectorAddress, length, SectorTagType.CdSectorSubchannel);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)sectorAddress, length, false, false);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
byte[] deSub = Subchannel.Deinterleave(sub);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
bool indexesChanged = CheckIndexesFromSubchannel(deSub, isrcs, currentTrack, ref mcn, tracks, dumpLog,
|
2022-04-10 14:10:41 +01:00
|
|
|
updateStatus, smallestPregapLbaPerTrack, dumping,
|
|
|
|
|
out newPregapSectors);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(!fixSubchannelPosition ||
|
|
|
|
|
desiredSubchannel == MmcSubchannel.None)
|
|
|
|
|
return indexesChanged;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
int prePos = int.MinValue;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Check subchannel
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var subPos = 0; subPos < deSub.Length; subPos += 96)
|
2022-03-06 13:29:38 +00:00
|
|
|
{
|
|
|
|
|
// Expected LBA
|
2022-03-07 07:36:44 +00:00
|
|
|
long lba = (long)sectorAddress + subPos / 96;
|
2020-11-11 04:19:18 +00:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// We fixed the subchannel
|
2022-03-07 07:36:44 +00:00
|
|
|
var @fixed = false;
|
2020-11-11 04:19:18 +00:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
var q = new byte[12];
|
2022-03-06 13:29:38 +00:00
|
|
|
Array.Copy(deSub, subPos + 12, q, 0, 12);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Check Q CRC
|
|
|
|
|
CRC16CCITTContext.Data(q, 10, out byte[] crc);
|
|
|
|
|
bool crcOk = crc[0] == q[10] && crc[1] == q[11];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Start considering P to be OK
|
2022-03-07 07:36:44 +00:00
|
|
|
var pOk = true;
|
|
|
|
|
var pWeight = 0;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Check P and weight
|
|
|
|
|
for(int p = subPos; p < subPos + 12; p++)
|
|
|
|
|
{
|
|
|
|
|
if(deSub[p] != 0 &&
|
|
|
|
|
deSub[p] != 255)
|
|
|
|
|
pOk = false;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var w = 0; w < 8; w++)
|
2022-03-06 13:29:38 +00:00
|
|
|
if(((deSub[p] >> w) & 1) > 0)
|
|
|
|
|
pWeight++;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// This seems to be a somewhat common pattern
|
|
|
|
|
bool rOk = deSub.Skip(subPos + 24).Take(12).All(r => r == 0x00) ||
|
|
|
|
|
deSub.Skip(subPos + 24).Take(12).All(r => r == 0xFF);
|
2020-11-09 21:03:04 +00:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
bool sOk = deSub.Skip(subPos + 36).Take(12).All(s => s == 0x00) ||
|
|
|
|
|
deSub.Skip(subPos + 36).Take(12).All(s => s == 0xFF);
|
2020-11-09 21:03:04 +00:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
bool tOk = deSub.Skip(subPos + 48).Take(12).All(t => t == 0x00) ||
|
|
|
|
|
deSub.Skip(subPos + 48).Take(12).All(t => t == 0xFF);
|
2020-11-09 21:03:04 +00:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
bool uOk = deSub.Skip(subPos + 60).Take(12).All(u => u == 0x00) ||
|
|
|
|
|
deSub.Skip(subPos + 60).Take(12).All(u => u == 0xFF);
|
2020-11-09 21:03:04 +00:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
bool vOk = deSub.Skip(subPos + 72).Take(12).All(v => v == 0x00) ||
|
|
|
|
|
deSub.Skip(subPos + 72).Take(12).All(v => v == 0xFF);
|
2020-11-09 21:03:04 +00:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
bool wOk = deSub.Skip(subPos + 84).Take(12).All(w => w == 0x00) ||
|
|
|
|
|
deSub.Skip(subPos + 84).Take(12).All(w => w == 0xFF);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
bool rwOk = rOk && sOk && tOk && uOk && vOk && wOk;
|
2022-03-07 07:36:44 +00:00
|
|
|
var rwPacket = false;
|
|
|
|
|
var cdtextPacket = false;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Check RW contents
|
|
|
|
|
if(!rwOk)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
var sectorSub = new byte[96];
|
2022-03-06 13:29:38 +00:00
|
|
|
Array.Copy(sub, subPos, sectorSub, 0, 96);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
DetectRwPackets(sectorSub, out _, out rwPacket, out cdtextPacket);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// TODO: CD+G reed solomon
|
|
|
|
|
if(rwPacket && !cdtextPacket)
|
|
|
|
|
rwOk = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(cdtextPacket)
|
|
|
|
|
rwOk = CheckCdTextPackets(sectorSub);
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Fix P
|
|
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
pOk = true;
|
|
|
|
|
@fixed = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
subLog?.WritePFix(lba);
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// RW is not a known pattern or packet, fix it
|
|
|
|
|
if(!rwOk &&
|
|
|
|
|
!rwPacket &&
|
|
|
|
|
!cdtextPacket &&
|
|
|
|
|
fixSubchannel)
|
|
|
|
|
{
|
|
|
|
|
for(int rw = subPos + 24; rw < subPos + 96; rw++)
|
|
|
|
|
deSub[rw] = 0;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
rwOk = true;
|
|
|
|
|
@fixed = true;
|
|
|
|
|
|
|
|
|
|
subLog.WriteRwFix(lba);
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
byte smin, ssec, amin, asec, aframe;
|
|
|
|
|
int aPos;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Fix Q
|
|
|
|
|
if(!crcOk &&
|
|
|
|
|
fixSubchannel &&
|
|
|
|
|
subPos > 0 &&
|
|
|
|
|
subPos < deSub.Length - 96)
|
|
|
|
|
{
|
|
|
|
|
isrcs.TryGetValue(currentTrack, out string knownGoodIsrc);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
crcOk = FixQSubchannel(deSub, q, subPos, mcn, knownGoodIsrc, fixSubchannelCrc, out bool fixedAdr,
|
2022-03-07 07:36:44 +00:00
|
|
|
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);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(crcOk)
|
|
|
|
|
{
|
|
|
|
|
Array.Copy(q, 0, deSub, subPos + 12, 12);
|
|
|
|
|
@fixed = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(fixedAdr)
|
|
|
|
|
subLog?.WriteQAdrFix(lba);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(controlFix)
|
|
|
|
|
subLog?.WriteQCtrlFix(lba);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(fixedZero)
|
|
|
|
|
subLog?.WriteQZeroFix(lba);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(fixedTno)
|
|
|
|
|
subLog?.WriteQTnoFix(lba);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(fixedIndex)
|
|
|
|
|
subLog?.WriteQIndexFix(lba);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(fixedRelPos)
|
|
|
|
|
subLog?.WriteQRelPosFix(lba);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(fixedAbsPos)
|
|
|
|
|
subLog?.WriteQAbsPosFix(lba);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(fixedCrc)
|
|
|
|
|
subLog?.WriteQCrcFix(lba);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(fixedMcn)
|
|
|
|
|
subLog?.WriteQMcnFix(lba);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(fixedIsrc)
|
|
|
|
|
subLog?.WriteQIsrcFix(lba);
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
2022-03-06 13:29:38 +00:00
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(!pOk ||
|
|
|
|
|
!crcOk ||
|
|
|
|
|
!rwOk)
|
|
|
|
|
continue;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
aframe = (byte)(q[9] / 16 * 10 + (q[9] & 0x0F));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if((q[0] & 0x3) == 1)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00: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;
|
2022-03-06 13:29:38 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ulong expectedSectorAddress = sectorAddress + (ulong)(subPos / 96) + 150;
|
|
|
|
|
smin = (byte)(expectedSectorAddress / 60 / 75);
|
|
|
|
|
expectedSectorAddress -= (ulong)(smin * 60 * 75);
|
|
|
|
|
ssec = (byte)(expectedSectorAddress / 75);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
aPos = smin * 60 * 75 + ssec * 75 + aframe - 150;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Next second
|
|
|
|
|
if(aPos < prePos)
|
|
|
|
|
aPos += 75;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// TODO: Negative sectors
|
|
|
|
|
if(aPos < 0)
|
|
|
|
|
continue;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
prePos = aPos;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
var posSub = new byte[96];
|
2022-03-06 13:29:38 +00:00
|
|
|
Array.Copy(deSub, subPos, posSub, 0, 96);
|
|
|
|
|
posSub = Subchannel.Interleave(posSub);
|
|
|
|
|
outputPlugin.WriteSectorTag(posSub, (ulong)aPos, SectorTagType.CdSectorSubchannel);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
subchannelExtents.Remove(aPos);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(@fixed)
|
|
|
|
|
subLog?.WriteEntry(posSub, supportedSubchannel == MmcSubchannel.Raw, lba, 1, false, true);
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
return indexesChanged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Check subchannel for indexes</summary>
|
|
|
|
|
/// <param name="deSub">De-interleaved subchannel</param>
|
|
|
|
|
/// <param name="isrcs">List of ISRCs</param>
|
|
|
|
|
/// <param name="currentTrack">Current track number</param>
|
|
|
|
|
/// <param name="mcn">Disc's MCN</param>
|
|
|
|
|
/// <param name="tracks">List of tracks</param>
|
|
|
|
|
/// <param name="dumpLog">Dumping log</param>
|
|
|
|
|
/// <param name="updateStatus">Status update callback</param>
|
|
|
|
|
/// <param name="smallestPregapLbaPerTrack">List of smallest known pregap per track</param>
|
|
|
|
|
/// <param name="dumping">Set if we are dumping, otherwise converting</param>
|
|
|
|
|
/// <returns><c>true</c> if indexes have changed, <c>false</c> otherwise</returns>
|
|
|
|
|
static bool CheckIndexesFromSubchannel(byte[] deSub, Dictionary<byte, string> isrcs, byte currentTrack,
|
|
|
|
|
ref string mcn, Track[] tracks, DumpLog dumpLog,
|
|
|
|
|
UpdateStatusHandler updateStatus,
|
2022-04-10 14:10:41 +01:00
|
|
|
Dictionary<byte, int> smallestPregapLbaPerTrack, bool dumping,
|
|
|
|
|
out List<ulong> newPregapSectors)
|
2022-03-06 13:29:38 +00:00
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
var status = false;
|
2022-04-09 20:27:05 +01:00
|
|
|
newPregapSectors = new List<ulong>();
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
// Check subchannel
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var subPos = 0; subPos < deSub.Length; subPos += 96)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
var q = new byte[12];
|
2022-03-06 13:29:38 +00:00
|
|
|
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-07-14 13:42:31 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
switch(q[0] & 0x3)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
// ISRC
|
|
|
|
|
case 3:
|
|
|
|
|
{
|
|
|
|
|
string isrc = Subchannel.DecodeIsrc(q);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-16 11:47:00 +00:00
|
|
|
if(isrc is null or "000000000000")
|
2022-03-06 13:29:38 +00:00
|
|
|
continue;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(!crcOk)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if(!isrcs.ContainsKey(currentTrack))
|
2020-11-11 04:19:18 +00:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
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}.");
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
updateStatus?.
|
|
|
|
|
Invoke($"ISRC for track {currentTrack} changed from {isrcs[currentTrack]} to {isrc}.");
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
isrcs[currentTrack] = isrc;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2020-11-11 04:19:18 +00:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// MCN
|
|
|
|
|
case 2:
|
|
|
|
|
{
|
|
|
|
|
string newMcn = Subchannel.DecodeMcn(q);
|
2020-11-11 04:19:18 +00:00
|
|
|
|
2022-03-16 11:47:00 +00:00
|
|
|
if(newMcn is null or "0000000000000")
|
2022-03-06 13:29:38 +00:00
|
|
|
continue;
|
2020-11-11 04:19:18 +00:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(!crcOk)
|
|
|
|
|
continue;
|
2020-11-11 04:19:18 +00:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(mcn is null)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
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}.");
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
mcn = newMcn;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Positioning
|
|
|
|
|
case 1 when !crcOk: continue;
|
|
|
|
|
case 1:
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
var trackNo = (byte)(q[1] / 16 * 10 + (q[1] & 0x0F));
|
2020-11-11 04:19:18 +00:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var i = 0; i < tracks.Length; i++)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
if(tracks[i].Sequence != trackNo)
|
|
|
|
|
continue;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Pregap
|
|
|
|
|
if(q[2] == 0 &&
|
|
|
|
|
trackNo > 1)
|
2020-11-11 04:19:18 +00:00
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
var pmin = (byte)(q[3] / 16 * 10 + (q[3] & 0x0F));
|
|
|
|
|
var psec = (byte)(q[4] / 16 * 10 + (q[4] & 0x0F));
|
|
|
|
|
var pframe = (byte)(q[5] / 16 * 10 + (q[5] & 0x0F));
|
|
|
|
|
int qPos = pmin * 60 * 75 + psec * 75 + pframe;
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
// When we are dumping we calculate the pregap in reverse from index 1 back.
|
|
|
|
|
// When we are not, we go from index 0.
|
|
|
|
|
if(!smallestPregapLbaPerTrack.ContainsKey(trackNo))
|
|
|
|
|
smallestPregapLbaPerTrack[trackNo] = dumping ? 1 : 0;
|
|
|
|
|
|
|
|
|
|
uint firstTrackNumberInSameSession = tracks.
|
2022-03-07 07:36:44 +00:00
|
|
|
Where(t => t.Session == tracks[i].Session).
|
2022-03-06 13:29:38 +00:00
|
|
|
Min(t => t.Sequence);
|
|
|
|
|
|
|
|
|
|
if(tracks[i].Sequence == firstTrackNumberInSameSession)
|
2020-11-11 04:19:18 +00:00
|
|
|
continue;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(qPos < smallestPregapLbaPerTrack[trackNo])
|
2020-11-11 04:19:18 +00:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
int dif = smallestPregapLbaPerTrack[trackNo] - qPos;
|
|
|
|
|
tracks[i].Pregap += (ulong)dif;
|
|
|
|
|
tracks[i].StartSector -= (ulong)dif;
|
|
|
|
|
smallestPregapLbaPerTrack[trackNo] = qPos;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(i > 0 &&
|
2021-09-14 21:18:28 +01:00
|
|
|
tracks[i - 1].EndSector >= tracks[i].StartSector)
|
|
|
|
|
tracks[i - 1].EndSector = tracks[i].StartSector - 1;
|
2020-07-18 20:43:37 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
dumpLog?.WriteLine($"Pregap for track {trackNo} set to {tracks[i].Pregap} sectors.");
|
2020-07-18 20:43:37 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
updateStatus?.Invoke($"Pregap for track {trackNo} set to {tracks[i].Pregap} sectors.");
|
2020-07-18 20:43:37 +01:00
|
|
|
|
2022-04-09 20:27:05 +01:00
|
|
|
for(var p = 0; p < dif; p++)
|
2022-04-10 14:10:41 +01:00
|
|
|
newPregapSectors.Add(tracks[i].StartSector + (ulong)p);
|
|
|
|
|
|
2020-07-18 20:43:37 +01:00
|
|
|
status = true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(tracks[i].Pregap >= (ulong)qPos)
|
2020-07-14 01:06:23 +01:00
|
|
|
continue;
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
ulong oldPregap = tracks[i].Pregap;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
tracks[i].Pregap = (ulong)qPos;
|
|
|
|
|
tracks[i].StartSector -= tracks[i].Pregap - oldPregap;
|
|
|
|
|
|
|
|
|
|
if(i > 0 &&
|
|
|
|
|
tracks[i - 1].EndSector >= tracks[i].StartSector)
|
|
|
|
|
tracks[i - 1].EndSector = tracks[i].StartSector - 1;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
dumpLog?.WriteLine($"Pregap for track {trackNo} set to {tracks[i].Pregap} sectors.");
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
updateStatus?.Invoke($"Pregap for track {trackNo} set to {tracks[i].Pregap} sectors.");
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-04-09 20:27:05 +01:00
|
|
|
for(var p = 0; p < (int)(tracks[i].Pregap - oldPregap); p++)
|
2022-04-10 14:10:41 +01:00
|
|
|
newPregapSectors.Add(tracks[i].StartSector + (ulong)p);
|
|
|
|
|
|
2020-07-14 13:42:31 +01:00
|
|
|
status = true;
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
continue;
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(q[2] == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
var amin = (byte)(q[7] / 16 * 10 + (q[7] & 0x0F));
|
|
|
|
|
var asec = (byte)(q[8] / 16 * 10 + (q[8] & 0x0F));
|
|
|
|
|
var aframe = (byte)(q[9] / 16 * 10 + (q[9] & 0x0F));
|
|
|
|
|
int aPos = amin * 60 * 75 + asec * 75 + aframe - 150;
|
2022-03-06 13:29:38 +00:00
|
|
|
|
2022-04-10 14:10:41 +01:00
|
|
|
// Do not set INDEX 1 to a value higher than what the TOC already said.
|
|
|
|
|
if(q[2] == 1 &&
|
|
|
|
|
aPos > (int)tracks[i].StartSector)
|
|
|
|
|
continue;
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(tracks[i].Indexes.ContainsKey(q[2]) &&
|
|
|
|
|
aPos >= tracks[i].Indexes[q[2]])
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
status = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
break;
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Detect RW packets</summary>
|
|
|
|
|
/// <param name="subchannel">Subchannel data</param>
|
|
|
|
|
/// <param name="zero">Set if it contains a ZERO packet</param>
|
|
|
|
|
/// <param name="rwPacket">Set if it contains a GRAPHICS, EXTENDED GRAPHICS or MIDI packet</param>
|
|
|
|
|
/// <param name="cdtextPacket">Set if it contains a TEXT packet</param>
|
|
|
|
|
static void DetectRwPackets(byte[] subchannel, out bool zero, out bool rwPacket, out bool cdtextPacket)
|
|
|
|
|
{
|
|
|
|
|
zero = false;
|
|
|
|
|
rwPacket = false;
|
|
|
|
|
cdtextPacket = false;
|
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
var cdTextPack1 = new byte[18];
|
|
|
|
|
var cdTextPack2 = new byte[18];
|
|
|
|
|
var cdTextPack3 = new byte[18];
|
|
|
|
|
var cdTextPack4 = new byte[18];
|
|
|
|
|
var cdSubRwPack1 = new byte[24];
|
|
|
|
|
var cdSubRwPack2 = new byte[24];
|
|
|
|
|
var cdSubRwPack3 = new byte[24];
|
|
|
|
|
var cdSubRwPack4 = new byte[24];
|
2022-03-06 13:29:38 +00:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
var i = 0;
|
2022-03-06 13:29:38 +00:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 18; j++)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x3F) << 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0xC0) >> 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x0F) << 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0x3C) >> 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x03) << 6));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j] | (subchannel[i++] & 0x3F));
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 18; j++)
|
2022-03-06 13:29:38 +00:00
|
|
|
{
|
|
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x3F) << 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0xC0) >> 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x0F) << 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0x3C) >> 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x03) << 6));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j] | (subchannel[i++] & 0x3F));
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 18; j++)
|
2022-03-06 13:29:38 +00:00
|
|
|
{
|
|
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x3F) << 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0xC0) >> 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x0F) << 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0x3C) >> 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x03) << 6));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j] | (subchannel[i++] & 0x3F));
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 18; j++)
|
2022-03-06 13:29:38 +00:00
|
|
|
{
|
|
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x3F) << 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0xC0) >> 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x0F) << 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0x3C) >> 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x03) << 6));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j] | (subchannel[i++] & 0x3F));
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
i = 0;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 24; j++)
|
2022-03-06 13:29:38 +00:00
|
|
|
cdSubRwPack1[j] = (byte)(subchannel[i++] & 0x3F);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 24; j++)
|
2022-03-06 13:29:38 +00:00
|
|
|
cdSubRwPack2[j] = (byte)(subchannel[i++] & 0x3F);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 24; j++)
|
2022-03-06 13:29:38 +00:00
|
|
|
cdSubRwPack3[j] = (byte)(subchannel[i++] & 0x3F);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 24; j++)
|
2022-03-06 13:29:38 +00:00
|
|
|
cdSubRwPack4[j] = (byte)(subchannel[i++] & 0x3F);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if((cdTextPack1[0] & 0x80) == 0x80)
|
|
|
|
|
cdtextPacket = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if((cdTextPack2[0] & 0x80) == 0x80)
|
|
|
|
|
cdtextPacket = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if((cdTextPack3[0] & 0x80) == 0x80)
|
|
|
|
|
cdtextPacket = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if((cdTextPack4[0] & 0x80) == 0x80)
|
|
|
|
|
cdtextPacket = true;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
/// <summary>Checks if subchannel contains a TEXT packet</summary>
|
|
|
|
|
/// <param name="subchannel">Subchannel data</param>
|
|
|
|
|
/// <returns><c>true</c> if subchannel contains a TEXT packet, <c>false</c> otherwise</returns>
|
|
|
|
|
static bool CheckCdTextPackets(byte[] subchannel)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
var cdTextPack1 = new byte[18];
|
|
|
|
|
var cdTextPack2 = new byte[18];
|
|
|
|
|
var cdTextPack3 = new byte[18];
|
|
|
|
|
var cdTextPack4 = new byte[18];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
var i = 0;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 18; j++)
|
2022-03-06 13:29:38 +00:00
|
|
|
{
|
|
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x3F) << 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0xC0) >> 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x0F) << 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0x3C) >> 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x03) << 6));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack1[j] = (byte)(cdTextPack1[j] | (subchannel[i++] & 0x3F));
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 18; j++)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x3F) << 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0xC0) >> 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x0F) << 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0x3C) >> 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x03) << 6));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack2[j] = (byte)(cdTextPack2[j] | (subchannel[i++] & 0x3F));
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 18; j++)
|
2022-03-06 13:29:38 +00:00
|
|
|
{
|
|
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x3F) << 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0xC0) >> 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x0F) << 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0x3C) >> 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x03) << 6));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack3[j] = (byte)(cdTextPack3[j] | (subchannel[i++] & 0x3F));
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
for(var j = 0; j < 18; j++)
|
2022-03-06 13:29:38 +00:00
|
|
|
{
|
|
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x3F) << 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0xC0) >> 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x0F) << 4));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0x3C) >> 2));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x03) << 6));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(j < 18)
|
|
|
|
|
cdTextPack4[j] = (byte)(cdTextPack4[j] | (subchannel[i++] & 0x3F));
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
var status = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if((cdTextPack1[0] & 0x80) == 0x80)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
var cdTextPack1Crc = BigEndianBitConverter.ToUInt16(cdTextPack1, 16);
|
|
|
|
|
var cdTextPack1ForCrc = new byte[16];
|
2022-03-06 13:29:38 +00:00
|
|
|
Array.Copy(cdTextPack1, 0, cdTextPack1ForCrc, 0, 16);
|
|
|
|
|
ushort calculatedCdtp1Crc = CRC16CCITTContext.Calculate(cdTextPack1ForCrc);
|
|
|
|
|
|
|
|
|
|
if(cdTextPack1Crc != calculatedCdtp1Crc &&
|
|
|
|
|
cdTextPack1Crc != 0)
|
|
|
|
|
status = false;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if((cdTextPack2[0] & 0x80) == 0x80)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
var cdTextPack2Crc = BigEndianBitConverter.ToUInt16(cdTextPack2, 16);
|
|
|
|
|
var cdTextPack2ForCrc = new byte[16];
|
2022-03-06 13:29:38 +00:00
|
|
|
Array.Copy(cdTextPack2, 0, cdTextPack2ForCrc, 0, 16);
|
|
|
|
|
ushort calculatedCdtp2Crc = CRC16CCITTContext.Calculate(cdTextPack2ForCrc);
|
|
|
|
|
|
|
|
|
|
if(cdTextPack2Crc != calculatedCdtp2Crc &&
|
|
|
|
|
cdTextPack2Crc != 0)
|
|
|
|
|
status = false;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if((cdTextPack3[0] & 0x80) == 0x80)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
var cdTextPack3Crc = BigEndianBitConverter.ToUInt16(cdTextPack3, 16);
|
|
|
|
|
var cdTextPack3ForCrc = new byte[16];
|
2022-03-06 13:29:38 +00:00
|
|
|
Array.Copy(cdTextPack3, 0, cdTextPack3ForCrc, 0, 16);
|
|
|
|
|
ushort calculatedCdtp3Crc = CRC16CCITTContext.Calculate(cdTextPack3ForCrc);
|
|
|
|
|
|
|
|
|
|
if(cdTextPack3Crc != calculatedCdtp3Crc &&
|
|
|
|
|
cdTextPack3Crc != 0)
|
|
|
|
|
status = false;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if((cdTextPack4[0] & 0x80) != 0x80)
|
|
|
|
|
return status;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
var cdTextPack4Crc = BigEndianBitConverter.ToUInt16(cdTextPack4, 16);
|
|
|
|
|
var cdTextPack4ForCrc = new byte[16];
|
2022-03-06 13:29:38 +00:00
|
|
|
Array.Copy(cdTextPack4, 0, cdTextPack4ForCrc, 0, 16);
|
|
|
|
|
ushort calculatedCdtp4Crc = CRC16CCITTContext.Calculate(cdTextPack4ForCrc);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(cdTextPack4Crc == calculatedCdtp4Crc ||
|
|
|
|
|
cdTextPack4Crc == 0)
|
|
|
|
|
return status;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
/// <summary>Fixes Q subchannel</summary>
|
|
|
|
|
/// <param name="deSub">Deinterleaved subchannel data</param>
|
|
|
|
|
/// <param name="q">Q subchannel</param>
|
|
|
|
|
/// <param name="subPos">Position in <see>deSub</see></param>
|
|
|
|
|
/// <param name="mcn">Disc's MCN</param>
|
|
|
|
|
/// <param name="isrc">Track ISRC</param>
|
|
|
|
|
/// <param name="fixCrc">Set to <c>true</c> if we should fix the CRC, <c>false</c> otherwise</param>
|
|
|
|
|
/// <param name="fixedAdr">Set to <c>true</c> if we fixed the ADR, <c>false</c> otherwise</param>
|
|
|
|
|
/// <param name="controlFix">Set to <c>true</c> if we fixed the CONTROL, <c>false</c> otherwise</param>
|
|
|
|
|
/// <param name="fixedZero">Set to <c>true</c> if we fixed the ZERO, <c>false</c> otherwise</param>
|
|
|
|
|
/// <param name="fixedTno">Set to <c>true</c> if we fixed the TNO, <c>false</c> otherwise</param>
|
|
|
|
|
/// <param name="fixedIndex">Set to <c>true</c> if we fixed the INDEX, <c>false</c> otherwise</param>
|
|
|
|
|
/// <param name="fixedRelPos">Set to <c>true</c> if we fixed the PMIN, PSEC and/or PFRAME, <c>false</c> otherwise</param>
|
|
|
|
|
/// <param name="fixedAbsPos">Set to <c>true</c> if we fixed the AMIN, ASEC and/or AFRAME, <c>false</c> otherwise</param>
|
|
|
|
|
/// <param name="fixedCrc">Set to <c>true</c> if we fixed the CRC, <c>false</c> otherwise</param>
|
|
|
|
|
/// <param name="fixedMcn">Set to <c>true</c> if we fixed the MCN, <c>false</c> otherwise</param>
|
|
|
|
|
/// <param name="fixedIsrc">Set to <c>true</c> if we fixed the ISRC, <c>false</c> otherwise</param>
|
|
|
|
|
/// <returns><c>true</c> if it was fixed correctly, <c>false</c> otherwise</returns>
|
|
|
|
|
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;
|
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
var preQ = new byte[12];
|
|
|
|
|
var nextQ = new byte[12];
|
2022-03-06 13:29:38 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
CRC16CCITTContext.Data(q, 10, out byte[] qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(fixedAdr && status)
|
|
|
|
|
return true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
int oldAdr = q[0] & 0x3;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// 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];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(status)
|
|
|
|
|
{
|
|
|
|
|
fixedAdr = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
return true;
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// 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)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
fixedAdr = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// 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];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(status)
|
|
|
|
|
{
|
|
|
|
|
fixedAdr = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
q[0] = (byte)((q[0] & 0xF0) + oldAdr);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
oldAdr = q[0];
|
|
|
|
|
|
|
|
|
|
// Try using previous control
|
|
|
|
|
if(preCrcOk && (q[0] & 0xF0) != (preQ[0] & 0xF0))
|
|
|
|
|
{
|
|
|
|
|
q[0] = (byte)((q[0] & 0x03) + (preQ[0] & 0xF0));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
|
|
|
|
|
|
|
|
|
if(status)
|
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
controlFix = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
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));
|
|
|
|
|
|
2020-07-14 01:06:23 +01:00
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
|
|
|
|
|
|
|
|
|
if(status)
|
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
controlFix = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
q[0] = (byte)oldAdr;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(preCrcOk &&
|
|
|
|
|
nextCrcOk &&
|
|
|
|
|
(nextQ[0] & 0xF0) == (preQ[0] & 0xF0) &&
|
|
|
|
|
(q[0] & 0xF0) != (nextQ[0] & 0xF0))
|
|
|
|
|
{
|
|
|
|
|
q[0] = (byte)((q[0] & 0x03) + (nextQ[0] & 0xF0));
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
controlFix = true;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
switch(q[0] & 0x3)
|
|
|
|
|
{
|
|
|
|
|
// Positioning
|
|
|
|
|
case 1:
|
|
|
|
|
{
|
|
|
|
|
// ZERO not zero
|
|
|
|
|
if(q[6] != 0)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
q[6] = 0;
|
|
|
|
|
fixedZero = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
|
|
|
|
|
|
|
|
|
if(status)
|
|
|
|
|
return true;
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(preCrcOk && nextCrcOk)
|
|
|
|
|
if(preQ[1] == nextQ[1] &&
|
|
|
|
|
preQ[1] != q[1])
|
|
|
|
|
{
|
|
|
|
|
q[1] = preQ[1];
|
|
|
|
|
fixedTno = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(status)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(preCrcOk && nextCrcOk)
|
|
|
|
|
if(preQ[2] == nextQ[2] &&
|
|
|
|
|
preQ[2] != q[2])
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
q[2] = preQ[2];
|
|
|
|
|
fixedIndex = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
|
|
|
|
|
|
|
|
|
if(status)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
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;
|
2020-11-11 04:19:18 +00:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
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;
|
2020-11-11 04:19:18 +00:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// TODO: pregap
|
|
|
|
|
// Not pregap
|
|
|
|
|
if(q[2] > 0)
|
|
|
|
|
{
|
|
|
|
|
// Previous was not pregap either
|
|
|
|
|
if(preQ[2] > 0 && preCrcOk)
|
2020-11-11 04:19:18 +00:00
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
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;
|
2020-11-11 04:19:18 +00:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
dPos = pPos - rPos;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
if(dPos != 1)
|
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
q[3] = preQ[3];
|
|
|
|
|
q[4] = preQ[4];
|
|
|
|
|
q[5] = preQ[5];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
// BCD add 1, so 0x39 becomes 0x40
|
2022-03-06 13:29:38 +00:00
|
|
|
if((q[5] & 0xF) == 9)
|
|
|
|
|
q[5] += 7;
|
2020-07-14 01:06:23 +01:00
|
|
|
else
|
2022-03-06 13:29:38 +00:00
|
|
|
q[5]++;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
// 74 frames, so from 0x00 to 0x74, BCD
|
2022-03-06 13:29:38 +00:00
|
|
|
if(q[5] >= 0x74)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
|
|
|
|
// 0 frames
|
2022-03-06 13:29:38 +00:00
|
|
|
q[5] = 0;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
// Add 1 second
|
2022-03-06 13:29:38 +00:00
|
|
|
if((q[4] & 0xF) == 9)
|
|
|
|
|
q[4] += 7;
|
2020-07-14 01:06:23 +01:00
|
|
|
else
|
2022-03-06 13:29:38 +00:00
|
|
|
q[4]++;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
// 60 seconds, so from 0x00 to 0x59, BCD
|
2022-03-06 13:29:38 +00:00
|
|
|
if(q[4] >= 0x59)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
|
|
|
|
// 0 seconds
|
2022-03-06 13:29:38 +00:00
|
|
|
q[4] = 0;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
// Add 1 minute
|
2022-03-06 13:29:38 +00:00
|
|
|
q[3]++;
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
fixedRelPos = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
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 &&
|
2022-03-06 13:29:38 +00:00
|
|
|
!fixedRelPos)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
dPos = rPos - pPos;
|
|
|
|
|
|
|
|
|
|
if(dPos != 1)
|
|
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
q[3] = nextQ[3];
|
|
|
|
|
q[4] = nextQ[4];
|
|
|
|
|
q[5] = nextQ[5];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
// If frames is 0
|
2022-03-06 13:29:38 +00:00
|
|
|
if(q[5] == 0)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
|
|
|
|
// If seconds is 0
|
2022-03-06 13:29:38 +00:00
|
|
|
if(q[4] == 0)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
|
|
|
|
// BCD decrease minutes
|
2022-03-06 13:29:38 +00:00
|
|
|
if((q[3] & 0xF) == 0)
|
|
|
|
|
q[3] = (byte)((q[3] & 0xF0) - 0x10);
|
2020-07-14 01:06:23 +01:00
|
|
|
else
|
2022-03-06 13:29:38 +00:00
|
|
|
q[3]--;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
q[4] = 0x59;
|
|
|
|
|
q[5] = 0x73;
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// BCD decrease seconds
|
2022-03-06 13:29:38 +00:00
|
|
|
if((q[4] & 0xF) == 0)
|
|
|
|
|
q[4] = (byte)((q[4] & 0xF0) - 0x10);
|
2020-07-14 01:06:23 +01:00
|
|
|
else
|
2022-03-06 13:29:38 +00:00
|
|
|
q[4]--;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
q[5] = 0x73;
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BCD decrease frames
|
2022-03-06 13:29:38 +00:00
|
|
|
else if((q[5] & 0xF) == 0)
|
|
|
|
|
q[5] = (byte)((q[5] & 0xF0) - 0x10);
|
2020-07-14 01:06:23 +01:00
|
|
|
else
|
2022-03-06 13:29:38 +00:00
|
|
|
q[5]--;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
fixedRelPos = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
|
|
|
|
|
|
|
|
|
if(status)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-06 13:29:38 +00:00
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Previous Q's CRC is correct
|
|
|
|
|
if(preCrcOk)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
dPos = aPos - rPos;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(dPos != 1)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Add 1 second
|
|
|
|
|
if((q[8] & 0xF) == 9)
|
|
|
|
|
q[8] += 7;
|
|
|
|
|
else
|
|
|
|
|
q[8]++;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// 60 seconds, so from 0x00 to 0x59, BCD
|
|
|
|
|
if(q[8] >= 0x59)
|
|
|
|
|
{
|
|
|
|
|
// 0 seconds
|
|
|
|
|
q[8] = 0;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Add 1 minute
|
|
|
|
|
q[7]++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
fixedAbsPos = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
2022-03-06 13:29:38 +00:00
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(status)
|
|
|
|
|
return true;
|
2020-11-11 04:19:18 +00:00
|
|
|
}
|
2022-03-06 13:29:38 +00:00
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Next is not pregap and we didn't fix relative position with previous
|
|
|
|
|
if(nextQ[2] > 0 &&
|
|
|
|
|
nextCrcOk &&
|
|
|
|
|
!fixedAbsPos)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
dPos = rPos - pPos;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(dPos != 1)
|
|
|
|
|
{
|
|
|
|
|
q[7] = nextQ[7];
|
|
|
|
|
q[8] = nextQ[8];
|
|
|
|
|
q[9] = nextQ[9];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// 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]--;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
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]--;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
q[9] = 0x73;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// BCD decrease frames
|
|
|
|
|
else if((q[9] & 0xF) == 0)
|
|
|
|
|
q[9] = (byte)((q[9] & 0xF0) - 0x10);
|
|
|
|
|
else
|
|
|
|
|
q[9]--;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
fixedAbsPos = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(status)
|
|
|
|
|
return true;
|
2020-11-11 04:19:18 +00:00
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Game Over
|
|
|
|
|
if(!fixCrc || status)
|
|
|
|
|
return false;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Previous Q's CRC is correct
|
|
|
|
|
if(preCrcOk)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
dPos = aPos - rPos;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
bool absOk = dPos == 1;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
dPos = pPos - rPos;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
bool relOk = dPos == 1;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(q[0] != preQ[0] ||
|
|
|
|
|
q[1] != preQ[1] ||
|
|
|
|
|
q[2] != preQ[2] ||
|
|
|
|
|
q[6] != 0 ||
|
|
|
|
|
!absOk ||
|
|
|
|
|
!relOk)
|
|
|
|
|
return false;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
q[10] = qCrc[0];
|
|
|
|
|
q[11] = qCrc[1];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
fixedCrc = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Next Q's CRC is correct
|
|
|
|
|
if(nextCrcOk)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
dPos = rPos - aPos;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
bool absOk = dPos == 1;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
dPos = rPos - pPos;
|
|
|
|
|
|
|
|
|
|
bool relOk = dPos == 1;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(q[0] != nextQ[0] ||
|
|
|
|
|
q[1] != nextQ[1] ||
|
|
|
|
|
q[2] != nextQ[2] ||
|
|
|
|
|
q[6] != 0 ||
|
|
|
|
|
!absOk ||
|
|
|
|
|
!relOk)
|
2020-11-11 04:19:18 +00:00
|
|
|
return false;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2020-11-11 04:19:18 +00:00
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
q[10] = qCrc[0];
|
|
|
|
|
q[11] = qCrc[1];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2020-11-11 04:19:18 +00:00
|
|
|
fixedCrc = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2020-11-11 04:19:18 +00:00
|
|
|
return true;
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Ok if previous and next are both BAD I won't rewrite the CRC at all
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MCN
|
|
|
|
|
case 2:
|
|
|
|
|
{
|
|
|
|
|
// Previous Q's CRC is correct
|
|
|
|
|
if(preCrcOk)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
rframe = (byte)(preQ[9] / 16 * 10 + (preQ[9] & 0x0F));
|
|
|
|
|
aframe = (byte)(q[9] / 16 * 10 + (q[9] & 0x0F));
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
if(aframe - rframe != 1)
|
2020-11-11 04:19:18 +00:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
q[9] = preQ[9];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if((q[9] & 0xF) == 9)
|
|
|
|
|
q[9] += 7;
|
|
|
|
|
else
|
|
|
|
|
q[9]++;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(q[9] >= 0x74)
|
|
|
|
|
q[9] = 0;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
fixedAbsPos = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(status)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Next Q's CRC is correct
|
|
|
|
|
else if(nextCrcOk)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
rframe = (byte)(nextQ[9] / 16 * 10 + (nextQ[9] & 0x0F));
|
|
|
|
|
aframe = (byte)(q[9] / 16 * 10 + (q[9] & 0x0F));
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
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;
|
2020-11-11 04:19:18 +00:00
|
|
|
}
|
2022-03-06 13:29:38 +00:00
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// We know the MCN
|
|
|
|
|
if(mcn != null)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
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);
|
2022-03-06 13:29:38 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ISRC
|
|
|
|
|
case 3:
|
|
|
|
|
{
|
|
|
|
|
// Previous Q's CRC is correct
|
|
|
|
|
if(preCrcOk)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
rframe = (byte)(preQ[9] / 16 * 10 + (preQ[9] & 0x0F));
|
|
|
|
|
aframe = (byte)(q[9] / 16 * 10 + (q[9] & 0x0F));
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
if(aframe - rframe != 1)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
q[9] = preQ[9];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if((q[9] & 0xF) == 9)
|
|
|
|
|
q[9] += 7;
|
|
|
|
|
else
|
|
|
|
|
q[9]++;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(q[9] >= 0x74)
|
|
|
|
|
q[9] = 0;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
fixedAbsPos = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(status)
|
|
|
|
|
return true;
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
2022-03-06 13:29:38 +00:00
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Next Q's CRC is correct
|
|
|
|
|
else if(nextCrcOk)
|
|
|
|
|
{
|
2022-03-07 07:36:44 +00:00
|
|
|
rframe = (byte)(nextQ[9] / 16 * 10 + (nextQ[9] & 0x0F));
|
|
|
|
|
aframe = (byte)(q[9] / 16 * 10 + (q[9] & 0x0F));
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
if(aframe - rframe != 1)
|
2020-07-14 01:06:23 +01:00
|
|
|
{
|
2022-03-06 13:29:38 +00:00
|
|
|
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;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
|
|
|
|
|
|
|
|
|
if(status)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-03-06 13:29:38 +00:00
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// We know the ISRC
|
|
|
|
|
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);
|
2022-03-07 07:36:44 +00:00
|
|
|
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);
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
fixedIsrc = true;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
|
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
2022-03-06 13:29:38 +00:00
|
|
|
status = qCrc[0] == q[10] && qCrc[1] == q[11];
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(status)
|
|
|
|
|
return true;
|
2020-11-11 04:19:18 +00:00
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(!fixCrc ||
|
|
|
|
|
!nextCrcOk ||
|
|
|
|
|
!preCrcOk)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
CRC16CCITTContext.Data(q, 10, out qCrc);
|
|
|
|
|
q[10] = qCrc[0];
|
|
|
|
|
q[11] = qCrc[1];
|
|
|
|
|
|
|
|
|
|
fixedCrc = true;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
/// <summary>Generates a correct subchannel all the missing ones</summary>
|
|
|
|
|
/// <param name="subchannelExtents">List of missing subchannels</param>
|
|
|
|
|
/// <param name="tracks">List of tracks</param>
|
|
|
|
|
/// <param name="trackFlags">Flags of tracks</param>
|
|
|
|
|
/// <param name="blocks">Disc size</param>
|
|
|
|
|
/// <param name="subLog">Subchannel log</param>
|
|
|
|
|
/// <param name="dumpLog">Dump log</param>
|
|
|
|
|
/// <param name="initProgress">Progress initialization callback</param>
|
|
|
|
|
/// <param name="updateProgress">Progress update callback</param>
|
|
|
|
|
/// <param name="endProgress">Progress finalization callback</param>
|
|
|
|
|
/// <param name="outputPlugin">Output image</param>
|
|
|
|
|
public static void GenerateSubchannels(HashSet<int> subchannelExtents, Track[] tracks,
|
|
|
|
|
Dictionary<byte, byte> trackFlags, ulong blocks, SubchannelLog subLog,
|
|
|
|
|
DumpLog dumpLog, InitProgressHandler initProgress,
|
|
|
|
|
UpdateProgressHandler updateProgress, EndProgressHandler endProgress,
|
|
|
|
|
IWritableImage outputPlugin)
|
|
|
|
|
{
|
|
|
|
|
initProgress?.Invoke();
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
foreach(int sector in subchannelExtents)
|
|
|
|
|
{
|
|
|
|
|
Track track = tracks.LastOrDefault(t => (int)t.StartSector <= sector);
|
|
|
|
|
byte trkFlags;
|
|
|
|
|
byte flags;
|
|
|
|
|
ulong trackStart;
|
|
|
|
|
ulong pregap;
|
2020-07-22 13:20:25 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(track == null)
|
|
|
|
|
continue;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
// Hidden track
|
|
|
|
|
if(track.Sequence == 0)
|
|
|
|
|
{
|
|
|
|
|
track = tracks.FirstOrDefault(t => (int)t.Sequence == 1);
|
|
|
|
|
trackStart = 0;
|
|
|
|
|
pregap = track?.StartSector ?? 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
trackStart = track.StartSector;
|
|
|
|
|
pregap = track.Pregap;
|
|
|
|
|
}
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(!trackFlags.TryGetValue((byte)(track?.Sequence ?? 0), out trkFlags) &&
|
|
|
|
|
track?.Type != TrackType.Audio)
|
|
|
|
|
flags = (byte)CdFlags.DataTrack;
|
|
|
|
|
else
|
|
|
|
|
flags = trkFlags;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
byte index;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
if(track?.Indexes?.Count > 0)
|
|
|
|
|
index = (byte)track.Indexes.LastOrDefault(i => i.Value >= sector).Key;
|
|
|
|
|
else
|
|
|
|
|
index = 0;
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
updateProgress?.Invoke($"Generating subchannel for sector {sector}...", sector, (long)blocks);
|
|
|
|
|
dumpLog?.WriteLine($"Generating subchannel for sector {sector}.");
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-07 07:36:44 +00:00
|
|
|
byte[] sub = Subchannel.Generate(sector, track?.Sequence ?? 0, (int)pregap, (int)trackStart, flags, index);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
outputPlugin.WriteSectorsTag(sub, (ulong)sector, 1, SectorTagType.CdSectorSubchannel);
|
2020-07-14 01:06:23 +01:00
|
|
|
|
2022-03-06 13:29:38 +00:00
|
|
|
subLog?.WriteEntry(sub, true, sector, 1, true, false);
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
2022-03-06 13:29:38 +00:00
|
|
|
|
|
|
|
|
endProgress?.Invoke();
|
2020-07-14 01:06:23 +01:00
|
|
|
}
|
2020-11-04 23:55:24 +00:00
|
|
|
}
|