Move media detection to common class.

This commit is contained in:
2020-01-01 18:06:31 +00:00
parent 3b9649a4a1
commit c2a3c7bffe
4 changed files with 1559 additions and 1321 deletions

View File

@@ -79,6 +79,7 @@ namespace DiscImageChef.Core.Devices.Dumping
DateTime dumpStart = DateTime.UtcNow; // Time of dump start
DateTime end; // Time of operation end
ExtentsULong extents = null; // Extents
bool hiddenData; // Hidden track is data
IbgLog ibgLog; // IMGBurn log
double imageWriteDuration = 0; // Duration of image write
long lastSector; // Last sector number
@@ -98,8 +99,8 @@ namespace DiscImageChef.Core.Devices.Dumping
const uint sectorSize = 2352; // Full sector size
int sectorsForOffset = 0; // Sectors needed to fix offset
ulong sectorSpeedStart = 0; // Used to calculate correct speed
bool sense=true; // Sense indicator
byte[] senseBuf = null; // Sense buffer
bool sense = true; // Sense indicator
byte[] senseBuf = null; // Sense buffer
int sessions; // Number of sessions in disc
DateTime start; // Start of operation
uint subSize; // Subchannel size in bytes
@@ -113,16 +114,14 @@ namespace DiscImageChef.Core.Devices.Dumping
Dictionary<byte, byte> trackFlags = new Dictionary<byte, byte>(); // Track flags
Track[] tracks; // Tracks in disc
bool nextData; // Next cluster of sectors is all data;
int firstTrackLastSession; // Number of first track in last session
bool hiddenTrack; // Disc has a hidden track before track 1
bool nextData; // Next cluster of sectors is all data;
MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel
DateTime timeSpeedStart; // Time of start for speed calculation
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>(); // Media tags
int firstTrackLastSession; // Number of first track in last session
MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel
DateTime timeSpeedStart; // Time of start for speed calculation
dskType = MediaType.CD;
if(_dumpRaw)
@@ -428,157 +427,31 @@ namespace DiscImageChef.Core.Devices.Dumping
leadOutExtents.Add(dataExtentsArray[i].Item2 + 1, dataExtentsArray[i + 1].Item1 - 1);
}
// Check for hidden data before start of track 1
if(tracks.First(t => t.TrackSequence == 1).TrackStartSector > 0 && readcd)
_dumpLog.WriteLine("Detecting disc type...");
UpdateStatus?.Invoke("Detecting disc type...");
MMC.DetectDiscType(ref dskType, sessions, toc, _dev, out hiddenTrack, out hiddenData,
firstTrackLastSession);
if(hiddenTrack)
{
_dumpLog.WriteLine("First track starts after sector 0, checking for a hidden track...");
UpdateStatus?.Invoke("First track starts after sector 0, checking for a hidden track...");
_dumpLog.WriteLine("Disc contains a hidden track...");
UpdateStatus?.Invoke("Disc contains a hidden track...");
sense = _dev.ReadCd(out cmdBuf, out senseBuf, 0, blockSize, 1, MmcSectorTypes.AllTypes, false, false,
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
supportedSubchannel, _dev.Timeout, out _);
if(_dev.Error || sense)
List<Track> trkList = new List<Track>
{
_dumpLog.WriteLine("Could not read sector 0, continuing...");
UpdateStatus?.Invoke("Could not read sector 0, continuing...");
}
else
{
byte[] syncMark =
new Track
{
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
};
byte[] cdiMark =
{
0x01, 0x43, 0x44, 0x2D
};
byte[] testMark = new byte[12];
Array.Copy(cmdBuf, 0, testMark, 0, 12);
bool hiddenData = syncMark.SequenceEqual(testMark) &&
(cmdBuf[0xF] == 0 || cmdBuf[0xF] == 1 || cmdBuf[0xF] == 2);
if(hiddenData && cmdBuf[0xF] == 2)
{
sense = _dev.ReadCd(out cmdBuf, out senseBuf, 16, blockSize, 1, MmcSectorTypes.AllTypes, false,
false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
supportedSubchannel, _dev.Timeout, out _);
if(!_dev.Error &&
!sense)
{
testMark = new byte[4];
Array.Copy(cmdBuf, 24, testMark, 0, 4);
if(cdiMark.SequenceEqual(testMark))
dskType = MediaType.CDIREADY;
}
List<Track> trkList = new List<Track>
{
new Track
{
TrackSequence = 0,
TrackSession = 1,
TrackType = hiddenData ? TrackType.Data : TrackType.Audio,
TrackStartSector = 0,
TrackBytesPerSector = (int)sectorSize,
TrackRawBytesPerSector = (int)sectorSize,
TrackSubchannelType = subType,
TrackEndSector = tracks.First(t => t.TrackSequence == 1).TrackStartSector - 1
}
};
trkList.AddRange(tracks);
tracks = trkList.ToArray();
TrackSequence = 0, TrackSession = 1,
TrackType = hiddenData ? TrackType.Data : TrackType.Audio,
TrackStartSector = 0, TrackBytesPerSector = (int)sectorSize,
TrackRawBytesPerSector = (int)sectorSize, TrackSubchannelType = subType,
TrackEndSector = tracks.First(t => t.TrackSequence == 1).TrackStartSector - 1
}
}
}
};
if(dskType == MediaType.CD ||
dskType == MediaType.CDROMXA)
{
// TODO: Add other detectors here
_dumpLog.WriteLine("Detecting disc type...");
UpdateStatus?.Invoke("Detecting disc type...");
bool hasDataTrack = false;
bool hasAudioTrack = false;
bool allFirstSessionTracksAreAudio = true;
bool hasVideoTrack = false;
foreach(FullTOC.TrackDataDescriptor track in toc.Value.TrackDescriptors)
{
if(track.TNO == 1 &&
((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental))
allFirstSessionTracksAreAudio &= firstTrackLastSession != 1;
if((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental)
{
hasDataTrack = true;
allFirstSessionTracksAreAudio &= track.POINT >= firstTrackLastSession;
}
else
{
hasAudioTrack = true;
}
hasVideoTrack |= track.ADR == 4;
}
if(hasDataTrack &&
hasAudioTrack &&
allFirstSessionTracksAreAudio &&
sessions == 2)
dskType = MediaType.CDPLUS;
if(!hasDataTrack &&
hasAudioTrack &&
sessions == 1)
dskType = MediaType.CDDA;
if(hasDataTrack &&
!hasAudioTrack &&
sessions == 1)
dskType = MediaType.CDROM;
if(hasVideoTrack &&
!hasDataTrack &&
sessions == 1)
dskType = MediaType.CDV;
byte[] videoNowColorFrame = new byte[9 * sectorSize];
for(int i = 0; i < 9; i++)
{
sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, sectorSize, 1, 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 senseBuf, (uint)i, sectorSize, 1, MmcSectorTypes.Cdda,
false, false, true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
MmcSubchannel.None, _dev.Timeout, out _);
if(sense || _dev.Error)
{
videoNowColorFrame = null;
break;
}
}
Array.Copy(cmdBuf, 0, videoNowColorFrame, i * sectorSize, sectorSize);
}
if(MMC.IsVideoNowColor(videoNowColorFrame))
dskType = MediaType.VideoNowColor;
trkList.AddRange(tracks);
tracks = trkList.ToArray();
}
// Check mode for tracks

View File

@@ -31,14 +31,79 @@
// ****************************************************************************/
using System;
using System.IO;
using System.Linq;
using DiscImageChef.Checksums;
using DiscImageChef.CommonTypes;
using DiscImageChef.Console;
using DiscImageChef.Decoders.CD;
using DiscImageChef.Decoders.Sega;
using DiscImageChef.Devices;
// ReSharper disable JoinDeclarationAndInitializer
namespace DiscImageChef.Core.Media.Detection
{
public static class MMC
{
/// <summary>SHA256 of PlayStation 2 boot sectors, seen in PAL discs</summary>
const string PS2_PAL_HASH = "5d04ff236613e1d8adcf9c201874acd6f6deed1e04306558b86f91cfb626f39d";
/// <summary>SHA256 of PlayStation 2 boot sectors, seen in Japanese, American, Malaysian and Korean discs</summary>
const string PS2_NTSC_HASH = "0bada1426e2c0351b872ef2a9ad2e5a0ac3918f4c53aa53329cb2911a8e16c23";
/// <summary>SHA256 of PlayStation 2 boot sectors, seen in Japanese discs</summary>
const string PS2_JAPANESE_HASH = "b82bffb809070d61fe050b7e1545df53d8f3cc648257cdff7502bc0ba6b38870";
static readonly byte[] _ps3Id =
{
0x50, 0x6C, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x33, 0x00, 0x00, 0x00, 0x00
};
static readonly byte[] _ps4Id =
{
0x50, 0x6C, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x34, 0x00, 0x00, 0x00, 0x00
};
static readonly byte[] _operaId =
{
0x01, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x01
};
// Only present on bootable CDs, but those make more than 99% of all available
static readonly byte[] _fmTownsBootId =
{
0x49, 0x50, 0x4C, 0x34, 0xEB, 0x55, 0x06
};
/// <summary>Present on first two seconds of second track, says "COPYRIGHT BANDAI"</summary>
static readonly byte[] _playdiaCopyright =
{
0x43, 0x4F, 0x50, 0x59, 0x52, 0x49, 0x47, 0x48, 0x54, 0x20, 0x42, 0x41, 0x4E, 0x44, 0x41, 0x49
};
static readonly byte[] _pcEngineSignature =
{
0x50, 0x43, 0x20, 0x45, 0x6E, 0x67, 0x69, 0x6E, 0x65, 0x20, 0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D, 0x20, 0x53,
0x59, 0x53, 0x54, 0x45, 0x4D
};
static readonly byte[] _pcFxSignature =
{
0x50, 0x43, 0x2D, 0x46, 0x58, 0x3A, 0x48, 0x75, 0x5F, 0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D
};
static readonly byte[] _atariSignature =
{
0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41,
0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52,
0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41,
0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x52, 0x41, 0x20, 0x49, 0x50, 0x41,
0x52, 0x50, 0x56, 0x4F, 0x44, 0x45, 0x44, 0x20, 0x54, 0x41, 0x20, 0x41, 0x45, 0x48, 0x44, 0x41, 0x52, 0x45,
0x41, 0x20, 0x52, 0x54
};
/// <summary>This is some kind of header. Every 10 bytes there's an audio byte.</summary>
static readonly byte[] VideoNowColorFrameMarker =
static readonly byte[] _videoNowColorFrameMarker =
{
0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3,
0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81,
@@ -65,14 +130,9 @@ namespace DiscImageChef.Core.Media.Detection
0xFF, 0xFF, 0xFF, 0x00
};
/// <summary>Checks if the media corresponds to CD-i.</summary>
/// <param name="sector0">Contents of LBA 0, with all headers.</param>
/// <param name="sector16">Contents of LBA 0, with all headers.</param>
/// <returns><c>true</c> if it corresponds to a CD-i, <c>false</c>otherwise.</returns>
public static bool IsCdi(byte[] sector0, byte[] sector16)
static bool IsData(byte[] sector)
{
if(sector0?.Length != 2352 ||
sector16?.Length != 2352)
if(sector?.Length != 2352)
return false;
byte[] syncMark =
@@ -80,34 +140,45 @@ namespace DiscImageChef.Core.Media.Detection
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
};
byte[] testMark = new byte[12];
Array.Copy(sector, 0, testMark, 0, 12);
return syncMark.SequenceEqual(testMark) && (sector[0xF] == 0 || sector[0xF] == 1 || sector[0xF] == 2);
}
/// <summary>Checks if the media corresponds to CD-i.</summary>
/// <param name="sector0">Contents of LBA 0, with all headers.</param>
/// <param name="sector16">Contents of LBA 0, with all headers.</param>
/// <returns><c>true</c> if it corresponds to a CD-i, <c>false</c>otherwise.</returns>
static bool IsCdi(byte[] sector0, byte[] sector16)
{
if(sector16?.Length != 2352)
return false;
byte[] cdiMark =
{
0x01, 0x43, 0x44, 0x2D
};
byte[] testMark = new byte[12];
Array.Copy(sector0, 0, testMark, 0, 12);
bool isData = IsData(sector0);
bool hiddenData = syncMark.SequenceEqual(testMark) &&
(sector0[0xF] == 0 || sector0[0xF] == 1 || sector0[0xF] == 2);
if(!hiddenData ||
if(!isData ||
sector0[0xF] != 2)
return false;
testMark = new byte[4];
byte[] testMark = new byte[4];
Array.Copy(sector16, 24, testMark, 0, 4);
return cdiMark.SequenceEqual(testMark);
}
public static bool IsVideoNowColor(byte[] videoFrame)
static bool IsVideoNowColor(byte[] videoFrame)
{
if(videoFrame is null ||
videoFrame.Length < VideoNowColorFrameMarker.Length)
videoFrame.Length < _videoNowColorFrameMarker.Length)
return false;
byte[] buffer = new byte[VideoNowColorFrameMarker.Length];
byte[] buffer = new byte[_videoNowColorFrameMarker.Length];
for(int framePosition = 0; framePosition + buffer.Length < videoFrame.Length; framePosition++)
{
@@ -116,7 +187,7 @@ namespace DiscImageChef.Core.Media.Detection
for(int ab = 9; ab < buffer.Length; ab += 10)
buffer[ab] = 0;
if(!VideoNowColorFrameMarker.SequenceEqual(buffer))
if(!_videoNowColorFrameMarker.SequenceEqual(buffer))
continue;
return true;
@@ -127,7 +198,7 @@ namespace DiscImageChef.Core.Media.Detection
public static int GetVideoNowColorOffset(byte[] data)
{
byte[] buffer = new byte[VideoNowColorFrameMarker.Length];
byte[] buffer = new byte[_videoNowColorFrameMarker.Length];
for(int framePosition = 0; framePosition + buffer.Length < data.Length; framePosition++)
{
@@ -136,7 +207,7 @@ namespace DiscImageChef.Core.Media.Detection
for(int ab = 9; ab < buffer.Length; ab += 10)
buffer[ab] = 0;
if(!VideoNowColorFrameMarker.SequenceEqual(buffer))
if(!_videoNowColorFrameMarker.SequenceEqual(buffer))
continue;
return 18032 - framePosition;
@@ -144,5 +215,765 @@ namespace DiscImageChef.Core.Media.Detection
return 0;
}
public static void DetectDiscType(ref MediaType mediaType, int sessions, FullTOC.CDFullTOC? decodedToc,
Device dev, out bool hiddenTrack, out bool hiddenData,
int firstTrackLastSession)
{
uint startOfFirstDataTrack = uint.MaxValue;
byte[] cmdBuf;
bool sense;
byte secondSessionFirstTrack = 0;
byte[] sector0;
byte[] sector1 = null;
byte[] ps2BootSectors = null;
byte[] playdia1 = null;
byte[] playdia2 = null;
byte[] firstDataSectorNotZero = null;
byte[] secondDataSectorNotZero = null;
byte[] firstTrackSecondSession = null;
byte[] firstTrackSecondSessionAudio = null;
byte[] videoNowColorFrame;
hiddenTrack = false;
hiddenData = false;
if(decodedToc.HasValue)
if(decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == 2))
secondSessionFirstTrack =
decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == 2).Min(t => t.POINT);
if(mediaType == MediaType.CD ||
mediaType == MediaType.CDROMXA)
{
bool hasDataTrack = false;
bool hasAudioTrack = false;
bool allFirstSessionTracksAreAudio = true;
bool hasVideoTrack = false;
if(decodedToc.HasValue)
foreach(FullTOC.TrackDataDescriptor track in decodedToc.Value.TrackDescriptors)
{
if(track.TNO == 1 &&
((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental))
allFirstSessionTracksAreAudio &= firstTrackLastSession != 1;
if((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental)
{
uint startAddress =
(uint)(((track.PHOUR * 3600 * 75) + (track.PMIN * 60 * 75) + (track.PSEC * 75) +
track.PFRAME) - 150);
if(startAddress < startOfFirstDataTrack)
startOfFirstDataTrack = startAddress;
hasDataTrack = true;
allFirstSessionTracksAreAudio &= track.POINT >= firstTrackLastSession;
}
else
{
hasAudioTrack = true;
}
hasVideoTrack |= track.ADR == 4;
}
if(hasDataTrack &&
hasAudioTrack &&
allFirstSessionTracksAreAudio &&
sessions == 2)
mediaType = MediaType.CDPLUS;
if(!hasDataTrack &&
hasAudioTrack &&
sessions == 1)
mediaType = MediaType.CDDA;
if(hasDataTrack &&
!hasAudioTrack &&
sessions == 1)
mediaType = MediaType.CDROM;
if(hasVideoTrack &&
!hasDataTrack &&
sessions == 1)
mediaType = MediaType.CDV;
}
if(secondSessionFirstTrack != 0 &&
decodedToc.HasValue &&
decodedToc.Value.TrackDescriptors.Any(t => t.POINT == secondSessionFirstTrack))
{
FullTOC.TrackDataDescriptor secondSessionFirstTrackTrack =
decodedToc.Value.TrackDescriptors.First(t => t.POINT == secondSessionFirstTrack);
uint firstSectorSecondSessionFirstTrack =
(uint)(((secondSessionFirstTrackTrack.PHOUR * 3600 * 75) +
(secondSessionFirstTrackTrack.PMIN * 60 * 75) +
(secondSessionFirstTrackTrack.PSEC * 75) +
secondSessionFirstTrackTrack.PFRAME) - 150);
sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack, 2352, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
firstTrackSecondSession = cmdBuf;
}
else
{
sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack, 2352, 1,
MmcSectorTypes.Cdda, false, false, true, MmcHeaderCodes.None, true, true,
MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
firstTrackSecondSession = cmdBuf;
}
sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack - 1, 2352, 3,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
firstTrackSecondSessionAudio = cmdBuf;
}
else
{
sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack - 1, 2352, 3,
MmcSectorTypes.Cdda, false, false, true, MmcHeaderCodes.None, true, true,
MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
firstTrackSecondSessionAudio = cmdBuf;
}
}
videoNowColorFrame = new byte[9 * 2352];
for(int i = 0; i < 9; i++)
{
sense = dev.ReadCd(out cmdBuf, out _, (uint)i, 2352, 1, 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 _, (uint)i, 2352, 1, MmcSectorTypes.Cdda, false, false, true,
MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(sense || !dev.Error)
{
videoNowColorFrame = null;
break;
}
}
Array.Copy(cmdBuf, 0, videoNowColorFrame, i * 2352, 2352);
}
if(decodedToc.HasValue)
{
FullTOC.TrackDataDescriptor firstTrack = decodedToc.Value.TrackDescriptors.First(t => t.POINT == 1);
uint firstTrackSector = (uint)(((firstTrack.PHOUR * 3600 * 75) + (firstTrack.PMIN * 60 * 75) +
(firstTrack.PSEC * 75) + firstTrack.PFRAME) - 150);
// Check for hidden data before start of track 1
if(firstTrackSector > 0)
{
sense = dev.ReadCd(out sector0, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(!dev.Error &&
!sense)
{
hiddenTrack = true;
hiddenData = IsData(sector0);
if(hiddenData)
{
sense = dev.ReadCd(out byte[] sector16, out _, 16, 2352, 1, MmcSectorTypes.AllTypes, false, false,
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(IsCdi(sector0, sector16))
{
mediaType = MediaType.CDIREADY;
}
}
}
}
}
sector0 = null;
switch(mediaType)
{
case MediaType.CD:
case MediaType.CDDA:
case MediaType.CDPLUS:
case MediaType.CDROM:
case MediaType.CDROMXA:
{
sense = dev.ReadCd(out cmdBuf, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = new byte[2048];
Array.Copy(cmdBuf, 16, sector0, 0, 2048);
sense = dev.ReadCd(out cmdBuf, out _, 1, 2352, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector1 = new byte[2048];
Array.Copy(cmdBuf, 16, sector1, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, 4200, 2352, 1, MmcSectorTypes.AllTypes, false, false,
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
playdia1 = new byte[2048];
Array.Copy(cmdBuf, 24, playdia1, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, 4201, 2352, 1, MmcSectorTypes.AllTypes, false, false,
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
playdia2 = new byte[2048];
Array.Copy(cmdBuf, 24, playdia2, 0, 2048);
}
if(startOfFirstDataTrack != uint.MaxValue)
{
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2352, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
firstDataSectorNotZero = new byte[2048];
Array.Copy(cmdBuf, 16, firstDataSectorNotZero, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2352, 1,
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
secondDataSectorNotZero = new byte[2048];
Array.Copy(cmdBuf, 16, secondDataSectorNotZero, 0, 2048);
}
}
var ps2Ms = new MemoryStream();
for(uint p = 0; p < 12; p++)
{
sense = dev.ReadCd(out cmdBuf, out _, p, 2352, 1, MmcSectorTypes.AllTypes, false, false,
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(sense || dev.Error)
break;
ps2Ms.Write(cmdBuf, cmdBuf[0x0F] == 0x02 ? 24 : 16, 2048);
}
if(ps2Ms.Length == 0x6000)
ps2BootSectors = ps2Ms.ToArray();
}
else
{
sense = dev.ReadCd(out cmdBuf, out _, 0, 2324, 1, MmcSectorTypes.Mode2, false, false, true,
MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = new byte[2048];
Array.Copy(cmdBuf, 0, sector0, 0, 2048);
sense = dev.ReadCd(out cmdBuf, out _, 1, 2324, 1, MmcSectorTypes.Mode2, false, false, true,
MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector1 = new byte[2048];
Array.Copy(cmdBuf, 1, sector0, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, 4200, 2324, 1, MmcSectorTypes.Mode2, false, false,
true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
playdia1 = new byte[2048];
Array.Copy(cmdBuf, 0, playdia1, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, 4201, 2324, 1, MmcSectorTypes.Mode2, false, false,
true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
playdia2 = new byte[2048];
Array.Copy(cmdBuf, 0, playdia2, 0, 2048);
}
if(startOfFirstDataTrack != uint.MaxValue)
{
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2324, 1,
MmcSectorTypes.Mode2, false, false, true, MmcHeaderCodes.None, true,
true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
firstDataSectorNotZero = new byte[2048];
Array.Copy(cmdBuf, 0, firstDataSectorNotZero, 0, 2048);
}
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2324, 1,
MmcSectorTypes.Mode2, false, false, true, MmcHeaderCodes.None, true,
true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
secondDataSectorNotZero = new byte[2048];
Array.Copy(cmdBuf, 0, secondDataSectorNotZero, 0, 2048);
}
}
var ps2Ms = new MemoryStream();
for(uint p = 0; p < 12; p++)
{
sense = dev.ReadCd(out cmdBuf, out _, p, 2324, 1, MmcSectorTypes.Mode2, false, false,
true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(sense || dev.Error)
break;
ps2Ms.Write(cmdBuf, 0, 2048);
}
if(ps2Ms.Length == 0x6000)
ps2BootSectors = ps2Ms.ToArray();
}
else
{
sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 1, MmcSectorTypes.Mode1, false, false, true,
MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = cmdBuf;
sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 1, MmcSectorTypes.Mode1, false, false,
true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
sector1 = cmdBuf;
sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 12, MmcSectorTypes.Mode1, false, false,
true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
MmcSubchannel.None, dev.Timeout, out _);
if(!sense &&
!dev.Error)
ps2BootSectors = cmdBuf;
if(startOfFirstDataTrack != uint.MaxValue)
{
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2048, 1,
MmcSectorTypes.Mode1, false, false, true, MmcHeaderCodes.None,
true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout,
out _);
if(!sense &&
!dev.Error)
firstDataSectorNotZero = cmdBuf;
sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2048, 1,
MmcSectorTypes.Mode1, false, false, true, MmcHeaderCodes.None,
true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout,
out _);
if(!sense &&
!dev.Error)
secondDataSectorNotZero = cmdBuf;
}
}
else
{
goto case MediaType.DVDROM;
}
}
}
break;
}
// TODO: Check for CD-i Ready
case MediaType.CDI: break;
case MediaType.DVDROM:
case MediaType.HDDVDROM:
case MediaType.BDROM:
case MediaType.Unknown:
sense = dev.Read16(out cmdBuf, out _, 0, false, true, false, 0, 2048, 0, 1, false, dev.Timeout,
out _);
if(!sense &&
!dev.Error)
{
sector0 = cmdBuf;
sense = dev.Read16(out cmdBuf, out _, 0, false, true, false, 1, 2048, 0, 1, false, dev.Timeout,
out _);
if(!sense &&
!dev.Error)
sector1 = cmdBuf;
sense = dev.Read16(out cmdBuf, out _, 0, false, true, false, 0, 2048, 0, 12, false, dev.Timeout,
out _);
if(!sense &&
!dev.Error &&
cmdBuf.Length == 0x6000)
ps2BootSectors = cmdBuf;
}
else
{
sense = dev.Read12(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 1, false,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = cmdBuf;
sense = dev.Read12(out cmdBuf, out _, 0, false, true, false, false, 1, 2048, 0, 1, false,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
sector1 = cmdBuf;
sense = dev.Read12(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 12, false,
dev.Timeout, out _);
if(!sense &&
!dev.Error &&
cmdBuf.Length == 0x6000)
ps2BootSectors = cmdBuf;
}
else
{
sense = dev.Read10(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 1,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = cmdBuf;
sense = dev.Read10(out cmdBuf, out _, 0, false, true, false, false, 1, 2048, 0, 1,
dev.Timeout, out _);
if(!sense &&
!dev.Error)
sector1 = cmdBuf;
sense = dev.Read10(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 12,
dev.Timeout, out _);
if(!sense &&
!dev.Error &&
cmdBuf.Length == 0x6000)
ps2BootSectors = cmdBuf;
}
else
{
sense = dev.Read6(out cmdBuf, out _, 0, 2048, 1, dev.Timeout, out _);
if(!sense &&
!dev.Error)
{
sector0 = cmdBuf;
sense = dev.Read6(out cmdBuf, out _, 1, 2048, 1, dev.Timeout, out _);
if(!sense &&
!dev.Error)
sector1 = cmdBuf;
sense = dev.Read6(out cmdBuf, out _, 0, 2048, 12, dev.Timeout, out _);
if(!sense &&
!dev.Error &&
cmdBuf.Length == 0x6000)
ps2BootSectors = cmdBuf;
}
}
}
}
break;
// Recordables will not be checked
case MediaType.CDR:
case MediaType.CDRW:
case MediaType.CDMRW:
case MediaType.DDCDR:
case MediaType.DDCDRW:
case MediaType.DVDR:
case MediaType.DVDRW:
case MediaType.DVDPR:
case MediaType.DVDPRW:
case MediaType.DVDPRWDL:
case MediaType.DVDRDL:
case MediaType.DVDPRDL:
case MediaType.DVDRAM:
case MediaType.DVDRWDL:
case MediaType.DVDDownload:
case MediaType.HDDVDRAM:
case MediaType.HDDVDR:
case MediaType.HDDVDRW:
case MediaType.HDDVDRDL:
case MediaType.HDDVDRWDL:
case MediaType.BDR:
case MediaType.BDRE:
case MediaType.BDRXL:
case MediaType.BDREXL: return;
}
if(sector0 == null)
return;
switch(mediaType)
{
case MediaType.CD:
case MediaType.CDDA:
case MediaType.CDPLUS:
case MediaType.CDROM:
case MediaType.CDROMXA:
// TODO: CDTV requires reading the filesystem, searching for a file called "/CDTV.TM"
// TODO: CD32 requires reading the filesystem, searching for a file called "/CD32.TM"
// TODO: Neo-Geo CD requires reading the filesystem and checking that the file "/IPL.TXT" is correct
// TODO: Pippin requires interpreting Apple Partition Map, reading HFS and checking for Pippin signatures
{
if(CD.DecodeIPBin(sector0).HasValue)
{
mediaType = MediaType.MEGACD;
return;
}
if(Saturn.DecodeIPBin(sector0).HasValue)
mediaType = MediaType.SATURNCD;
// Are GDR detectable ???
if(Dreamcast.DecodeIPBin(sector0).HasValue)
mediaType = MediaType.GDROM;
if(ps2BootSectors != null &&
ps2BootSectors.Length == 0x6000)
{
// The decryption key is applied as XOR. As first byte is originally always NULL, it gives us the key :)
byte decryptByte = ps2BootSectors[0];
for(int i = 0; i < 0x6000; i++)
ps2BootSectors[i] ^= decryptByte;
string ps2BootSectorsHash = Sha256Context.Data(ps2BootSectors, out _);
DicConsole.DebugWriteLine("Media-info Command", "PlayStation 2 boot sectors SHA256: {0}",
ps2BootSectorsHash);
if(ps2BootSectorsHash == PS2_PAL_HASH ||
ps2BootSectorsHash == PS2_NTSC_HASH ||
ps2BootSectorsHash == PS2_JAPANESE_HASH)
mediaType = MediaType.PS2CD;
}
if(sector0 != null)
{
byte[] syncBytes = new byte[7];
Array.Copy(sector0, 0, syncBytes, 0, 7);
if(_operaId.SequenceEqual(syncBytes))
mediaType = MediaType.ThreeDO;
if(_fmTownsBootId.SequenceEqual(syncBytes))
mediaType = MediaType.FMTOWNS;
}
if(playdia1 != null &&
playdia2 != null)
{
byte[] pd1 = new byte[_playdiaCopyright.Length];
byte[] pd2 = new byte[_playdiaCopyright.Length];
Array.Copy(playdia1, 38, pd1, 0, pd1.Length);
Array.Copy(playdia2, 0, pd2, 0, pd1.Length);
if(_playdiaCopyright.SequenceEqual(pd1) &&
_playdiaCopyright.SequenceEqual(pd2))
mediaType = MediaType.Playdia;
}
if(secondDataSectorNotZero != null)
{
byte[] pce = new byte[_pcEngineSignature.Length];
Array.Copy(secondDataSectorNotZero, 32, pce, 0, pce.Length);
if(_pcEngineSignature.SequenceEqual(pce))
mediaType = MediaType.SuperCDROM2;
}
if(firstDataSectorNotZero != null)
{
byte[] pcfx = new byte[_pcFxSignature.Length];
Array.Copy(firstDataSectorNotZero, 0, pcfx, 0, pcfx.Length);
if(_pcFxSignature.SequenceEqual(pcfx))
mediaType = MediaType.PCFX;
}
if(firstTrackSecondSessionAudio != null)
{
byte[] jaguar = new byte[_atariSignature.Length];
for(int i = 0; i + jaguar.Length <= firstTrackSecondSessionAudio.Length; i += 2)
{
Array.Copy(firstTrackSecondSessionAudio, i, jaguar, 0, jaguar.Length);
if(!_atariSignature.SequenceEqual(jaguar))
continue;
mediaType = MediaType.JaguarCD;
break;
}
}
if(firstTrackSecondSession != null)
if(firstTrackSecondSession.Length >= 2336)
{
byte[] milcd = new byte[2048];
Array.Copy(firstTrackSecondSession, 24, milcd, 0, 2048);
if(Dreamcast.DecodeIPBin(milcd).HasValue)
mediaType = MediaType.MilCD;
}
// TODO: Detect black and white VideoNow
// TODO: Detect VideoNow XP
if(IsVideoNowColor(videoNowColorFrame))
mediaType = MediaType.VideoNowColor;
break;
}
// TODO: Check for CD-i Ready
case MediaType.CDI: break;
case MediaType.DVDROM:
case MediaType.HDDVDROM:
case MediaType.BDROM:
case MediaType.Unknown:
// TODO: Nuon requires reading the filesystem, searching for a file called "/NUON/NUON.RUN"
if(ps2BootSectors != null &&
ps2BootSectors.Length == 0x6000)
{
// The decryption key is applied as XOR. As first byte is originally always NULL, it gives us the key :)
byte decryptByte = ps2BootSectors[0];
for(int i = 0; i < 0x6000; i++)
ps2BootSectors[i] ^= decryptByte;
string ps2BootSectorsHash = Sha256Context.Data(ps2BootSectors, out _);
DicConsole.DebugWriteLine("Media-info Command", "PlayStation 2 boot sectors SHA256: {0}",
ps2BootSectorsHash);
if(ps2BootSectorsHash == PS2_PAL_HASH ||
ps2BootSectorsHash == PS2_NTSC_HASH ||
ps2BootSectorsHash == PS2_JAPANESE_HASH)
mediaType = MediaType.PS2DVD;
}
if(sector1 != null)
{
byte[] tmp = new byte[_ps3Id.Length];
Array.Copy(sector1, 0, tmp, 0, tmp.Length);
if(tmp.SequenceEqual(_ps3Id))
switch(mediaType)
{
case MediaType.BDROM:
mediaType = MediaType.PS3BD;
break;
case MediaType.DVDROM:
mediaType = MediaType.PS3DVD;
break;
}
tmp = new byte[_ps4Id.Length];
Array.Copy(sector1, 512, tmp, 0, tmp.Length);
if(tmp.SequenceEqual(_ps4Id) &&
mediaType == MediaType.BDROM)
mediaType = MediaType.PS4BD;
}
// TODO: Identify discs that require reading tracks (PC-FX, PlayStation, Sega, etc)
break;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -185,8 +185,10 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=ASCQ/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ATAPI/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ATIP/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=BANDAI/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bluray/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=CDDA/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=CDTV/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=cdrom/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=certance/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=checksumming/@EntryIndexedValue">True</s:Boolean>
@@ -203,13 +205,17 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=leadout/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=mhdd/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=mhddlog/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=milcd/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=MINIX/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=NTFS/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=NTSC/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nuon/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=opticals/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PCMCIA/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Plextor/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pframe/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=phour/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=playdia/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Plextor/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pmin/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Portillo/@EntryIndexedValue">True</s:Boolean>
@@ -217,6 +223,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=pregaps/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=psec/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=readcd/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Recordables/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Reiser/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=SDHCI/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=subchannels/@EntryIndexedValue">True</s:Boolean>