Files
Aaru/DiscImageChef.Core/Media/Info/CompactDisc.cs

278 lines
12 KiB
C#
Raw Normal View History

using System;
2020-01-06 22:29:01 +00:00
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Enums;
using DiscImageChef.CommonTypes.Structs;
using DiscImageChef.Core.Logging;
using DiscImageChef.Core.Media.Detection;
using DiscImageChef.Database.Models;
using DiscImageChef.Decoders.CD;
using DiscImageChef.Devices;
using Device = DiscImageChef.Database.Models.Device;
namespace DiscImageChef.Core.Media.Info
{
public static class CompactDisc
{
/// <summary>Gets the offset bytes from a Compact Disc</summary>
/// <param name="cdOffset">Offset entry from database</param>
/// <param name="dbDev">Device entry from database</param>
/// <param name="debug">Debug</param>
/// <param name="dev">Opened device</param>
/// <param name="dskType">Detected disk type</param>
/// <param name="dumpLog">Dump log if applicable</param>
/// <param name="tracks">Disc track list</param>
/// <param name="updateStatus">UpdateStatus event</param>
2020-01-06 22:29:01 +00:00
/// <param name="driveOffset">Drive offset</param>
/// <param name="combinedOffset">Combined offset</param>
/// <returns><c>true</c> if offset could be found, <c>false</c> otherwise</returns>
2020-01-06 22:29:01 +00:00
[SuppressMessage("ReSharper", "TooWideLocalVariableScope")]
public static void GetOffset(CdOffset cdOffset, Device dbDev, bool debug, DiscImageChef.Devices.Device dev,
MediaType dskType, DumpLog dumpLog, Track[] tracks,
UpdateStatusHandler updateStatus, out int? driveOffset, out int? combinedOffset)
{
byte[] cmdBuf;
bool sense;
2020-01-06 22:29:01 +00:00
ulong minute;
ulong second;
ulong frame;
ulong remainder;
byte[] sectorSync;
byte[] tmpBuf;
Track dataTrack = default;
Track audioTrack = default;
bool offsetFound = false;
const uint sectorSize = 2352;
2020-01-06 22:29:01 +00:00
driveOffset = cdOffset?.Offset * 4;
combinedOffset = null;
if(dskType != MediaType.VideoNowColor)
{
if(tracks.Any(t => t.TrackType != TrackType.Audio))
{
2020-01-06 22:29:01 +00:00
dataTrack = tracks.FirstOrDefault(t => t.TrackType != TrackType.Audio);
if(dataTrack.TrackSequence != 0)
{
dataTrack.TrackStartSector += 151;
// Calculate MSF
2020-01-06 22:29:01 +00:00
minute = dataTrack.TrackStartSector / 4500;
second = (dataTrack.TrackStartSector - (minute * 4500)) / 75;
frame = dataTrack.TrackStartSector - (minute * 4500) - (second * 75);
dataTrack.TrackStartSector -= 151;
// Convert to BCD
2020-01-06 22:29:01 +00:00
remainder = minute % 10;
minute = ((minute / 10) * 16) + remainder;
remainder = second % 10;
second = ((second / 10) * 16) + remainder;
remainder = frame % 10;
frame = ((frame / 10) * 16) + remainder;
// Scramble M and S
minute ^= 0x01;
second ^= 0x80;
// Build sync
2020-01-06 22:29:01 +00:00
sectorSync = new byte[]
{
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, (byte)minute,
(byte)second, (byte)frame
};
2020-01-06 22:29:01 +00:00
tmpBuf = new byte[sectorSync.Length];
// Plextor READ CDDA
if(dbDev?.ATAPI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true ||
dbDev?.SCSI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true ||
dev.Manufacturer.ToLowerInvariant() == "plextor")
{
sense = dev.PlextorReadCdDa(out cmdBuf, out _, (uint)dataTrack.TrackStartSector, sectorSize,
3, PlextorSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
for(int i = 0; i < cmdBuf.Length - sectorSync.Length; i++)
{
Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length);
if(!tmpBuf.SequenceEqual(sectorSync))
continue;
2020-01-06 22:29:01 +00:00
combinedOffset = i - 2352;
offsetFound = true;
break;
}
}
}
if(debug ||
dbDev?.ATAPI?.RemovableMedias?.Any(d => d.CanReadCdScrambled == true) == true ||
dbDev?.SCSI?.RemovableMedias?.Any(d => d.CanReadCdScrambled == true) == true ||
dev.Manufacturer.ToLowerInvariant() == "hl-dt-st")
{
sense = dev.ReadCd(out cmdBuf, out _, (uint)dataTrack.TrackStartSector, sectorSize, 3,
MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true,
false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
for(int i = 0; i < cmdBuf.Length - sectorSync.Length; i++)
{
Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length);
if(!tmpBuf.SequenceEqual(sectorSync))
continue;
2020-01-06 22:29:01 +00:00
combinedOffset = i - 2352;
offsetFound = true;
break;
}
}
}
}
}
2020-01-06 22:29:01 +00:00
if(offsetFound)
return;
// Try to get another the offset some other way, we need an audio track just after a data track, same session
2020-01-06 22:29:01 +00:00
for(int i = 1; i < tracks.Length; i++)
{
if(tracks[i - 1].TrackType == TrackType.Audio ||
tracks[i].TrackType != TrackType.Audio)
continue;
2020-01-06 22:29:01 +00:00
dataTrack = tracks[i - 1];
audioTrack = tracks[i];
2020-01-06 22:29:01 +00:00
break;
}
2020-01-06 22:29:01 +00:00
if(dataTrack.TrackSequence == 0 ||
audioTrack.TrackSequence == 0)
return;
2020-01-06 22:29:01 +00:00
// Found them
sense = dev.ReadCd(out cmdBuf, out _, (uint)audioTrack.TrackStartSector, sectorSize, 3,
MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false,
MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
2020-01-06 22:29:01 +00:00
if(sense || dev.Error)
return;
2020-01-06 22:29:01 +00:00
dataTrack.TrackEndSector += 150;
2020-01-06 22:29:01 +00:00
// Calculate MSF
minute = dataTrack.TrackEndSector / 4500;
second = (dataTrack.TrackEndSector - (minute * 4500)) / 75;
frame = dataTrack.TrackEndSector - (minute * 4500) - (second * 75);
2020-01-06 22:29:01 +00:00
dataTrack.TrackEndSector -= 150;
2020-01-06 22:29:01 +00:00
// Convert to BCD
remainder = minute % 10;
minute = ((minute / 10) * 16) + remainder;
remainder = second % 10;
second = ((second / 10) * 16) + remainder;
remainder = frame % 10;
frame = ((frame / 10) * 16) + remainder;
2020-01-06 22:29:01 +00:00
// Scramble M and S
minute ^= 0x01;
second ^= 0x80;
2020-01-06 22:29:01 +00:00
// Build sync
sectorSync = new byte[]
{
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, (byte)minute, (byte)second,
(byte)frame
};
2020-01-06 22:29:01 +00:00
tmpBuf = new byte[sectorSync.Length];
2020-01-06 22:29:01 +00:00
for(int i = 0; i < cmdBuf.Length - sectorSync.Length; i++)
{
Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length);
2020-01-06 22:29:01 +00:00
if(!tmpBuf.SequenceEqual(sectorSync))
continue;
2020-01-06 22:29:01 +00:00
combinedOffset = i + 2352;
offsetFound = true;
2020-01-06 22:29:01 +00:00
break;
}
2020-01-06 22:29:01 +00:00
if(offsetFound || audioTrack.TrackPregap <= 0)
return;
2020-01-06 22:29:01 +00:00
sense = dev.ReadCd(out byte[] dataBuf, out _, (uint)dataTrack.TrackEndSector, sectorSize, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
2020-01-06 22:29:01 +00:00
if(sense || dev.Error)
return;
2020-01-06 22:29:01 +00:00
for(int i = 0; i < dataBuf.Length; i++)
dataBuf[i] ^= Sector.ScrambleTable[i];
2020-01-06 22:29:01 +00:00
for(int i = 0; i < 2352; i++)
{
2020-01-06 22:29:01 +00:00
byte[] dataSide = new byte[2352 - i];
byte[] audioSide = new byte[2352 - i];
2020-01-06 22:29:01 +00:00
Array.Copy(dataBuf, i, dataSide, 0, dataSide.Length);
Array.Copy(cmdBuf, 0, audioSide, 0, audioSide.Length);
2020-01-06 22:29:01 +00:00
if(!dataSide.SequenceEqual(audioSide))
continue;
2020-01-06 22:29:01 +00:00
combinedOffset = audioSide.Length;
2020-01-06 22:29:01 +00:00
break;
}
}
else
{
byte[] videoNowColorFrame = new byte[9 * sectorSize];
sense = dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 9, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(sense || dev.Error)
{
sense = dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 9, MmcSectorTypes.Cdda, false, false, true,
MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(sense || dev.Error)
{
videoNowColorFrame = null;
}
}
if(videoNowColorFrame is null)
{
dumpLog?.WriteLine("Could not find VideoNow Color frame offset, dump may not be correct.");
updateStatus?.Invoke("Could not find VideoNow Color frame offset, dump may not be correct.");
}
else
{
2020-01-06 22:29:01 +00:00
combinedOffset = MMC.GetVideoNowColorOffset(videoNowColorFrame);
dumpLog?.WriteLine($"VideoNow Color frame is offset {combinedOffset} bytes.");
updateStatus?.Invoke($"VideoNow Color frame is offset {combinedOffset} bytes.");
}
}
}
}
}