mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
2927 lines
119 KiB
C#
2927 lines
119 KiB
C#
// /***************************************************************************
|
|
// Aaru Data Preservation Suite
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Filename : MMC.cs
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
//
|
|
// Component : Core.
|
|
//
|
|
// --[ Description ] ----------------------------------------------------------
|
|
//
|
|
// Detects media types in MultiMediaCommand devices
|
|
//
|
|
// --[ 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/>.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
// Copyright © 2011-2025 Natalia Portillo
|
|
// ****************************************************************************/
|
|
|
|
// ReSharper disable JoinDeclarationAndInitializer
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Aaru.Checksums;
|
|
using Aaru.CommonTypes;
|
|
using Aaru.Decoders.Bluray;
|
|
using Aaru.Decoders.CD;
|
|
using Aaru.Decoders.DVD;
|
|
using Aaru.Decoders.SCSI.MMC;
|
|
using Aaru.Decoders.Sega;
|
|
using Aaru.Devices;
|
|
using Aaru.Helpers;
|
|
using Aaru.Logging;
|
|
using Sentry;
|
|
using DMI = Aaru.Decoders.Xbox.DMI;
|
|
using Sector = Aaru.Decoders.CD.Sector;
|
|
|
|
namespace Aaru.Core.Media.Detection;
|
|
|
|
/// <summary>Detects media type for MMC class devices</summary>
|
|
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";
|
|
const string MODULE_NAME = "Media detection";
|
|
|
|
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[] _ps5Id =
|
|
[
|
|
0x50, 0x6C, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x35, 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 = "COPYRIGHT BANDAI"u8.ToArray();
|
|
static readonly byte[] _pcEngineSignature = "PC Engine CD-ROM SYSTEM"u8.ToArray();
|
|
static readonly byte[] _pcFxSignature = "PC-FX:Hu_CD-ROM"u8.ToArray();
|
|
static readonly byte[] _atariSignature =
|
|
"TAIRTAIRTAIRTAIRTAIRTAIRTAIRTAIRTAIRTAIRTAIRTAIRTAIRTAIRTAIRTAIRTARA IPARPVODED TA AEHDAREA RT"u8.ToArray();
|
|
|
|
/// <summary>This is some kind of header. Every 10 bytes there's an audio byte.</summary>
|
|
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,
|
|
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, 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, 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,
|
|
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, 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, 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,
|
|
0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7,
|
|
0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x00, 0x00, 0x02, 0x01, 0x04, 0x02, 0x06, 0x03, 0xFF, 0x00, 0x08, 0x04,
|
|
0x0A, 0x05, 0x0C, 0x06, 0x0E, 0x07, 0xFF, 0x00, 0x11, 0x08, 0x13, 0x09, 0x15, 0x0A, 0x17, 0x0B, 0xFF, 0x00,
|
|
0x19, 0x0C, 0x1B, 0x0D, 0x1D, 0x0E, 0x1F, 0x0F, 0xFF, 0x00, 0x00, 0x28, 0x02, 0x29, 0x04, 0x2A, 0x06, 0x2B,
|
|
0xFF, 0x00, 0x08, 0x2C, 0x0A, 0x2D, 0x0C, 0x2E, 0x0E, 0x2F, 0xFF, 0x00, 0x11, 0x30, 0x13, 0x31, 0x15, 0x32,
|
|
0x17, 0x33, 0xFF, 0x00, 0x19, 0x34, 0x1B, 0x35, 0x1D, 0x36, 0x1F, 0x37, 0xFF, 0x00, 0x00, 0x38, 0x02, 0x39,
|
|
0x04, 0x3A, 0x06, 0x3B, 0xFF, 0x00, 0x08, 0x3C, 0x0A, 0x3D, 0x0C, 0x3E, 0x0E, 0x3F, 0xFF, 0x00, 0x11, 0x40,
|
|
0x13, 0x41, 0x15, 0x42, 0x17, 0x43, 0xFF, 0x00, 0x19, 0x44, 0x1B, 0x45, 0x1D, 0x46, 0x1F, 0x47, 0xFF, 0x00,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0x00
|
|
];
|
|
|
|
static bool IsData(byte[] sector)
|
|
{
|
|
if(sector?.Length != 2352) return false;
|
|
|
|
byte[] syncMark = [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);
|
|
}
|
|
|
|
static bool IsScrambledData(byte[] sector, int wantedLba, out int offset)
|
|
{
|
|
offset = 0;
|
|
|
|
if(sector?.Length != 2352) return false;
|
|
|
|
byte[] syncMark = [0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00];
|
|
|
|
byte[] testMark = new byte[12];
|
|
|
|
for(int i = 0; i <= 2336; i++)
|
|
{
|
|
Array.Copy(sector, i, testMark, 0, 12);
|
|
|
|
if(!syncMark.SequenceEqual(testMark) ||
|
|
sector[i + 0xF] != 0x60 && sector[i + 0xF] != 0x61 && sector[i + 0xF] != 0x62)
|
|
continue;
|
|
|
|
// De-scramble M and S
|
|
int minute = sector[i + 12] ^ 0x01;
|
|
int second = sector[i + 13] ^ 0x80;
|
|
int frame = sector[i + 14];
|
|
|
|
// Convert to binary
|
|
minute = minute / 16 * 10 + (minute & 0x0F);
|
|
second = second / 16 * 10 + (second & 0x0F);
|
|
frame = frame / 16 * 10 + (frame & 0x0F);
|
|
|
|
// Calculate the first found LBA
|
|
int lba = minute * 60 * 75 + second * 75 + frame - 150;
|
|
|
|
// Calculate the difference between the found LBA and the requested one
|
|
int diff = wantedLba - lba;
|
|
|
|
offset = i + 2352 * diff;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static byte[] DescrambleAndFixOffset(byte[] sector, int offsetBytes, int sectorsForOffset)
|
|
{
|
|
byte[] descrambled = new byte[2352];
|
|
|
|
int offsetFix = offsetBytes < 0 ? 2352 * sectorsForOffset + offsetBytes : offsetBytes;
|
|
|
|
Array.Copy(sector, offsetFix, descrambled, 0, 2352);
|
|
|
|
return Sector.Scramble(descrambled);
|
|
}
|
|
|
|
/// <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];
|
|
|
|
bool isData = IsData(sector0);
|
|
|
|
if(!isData || sector0[0xF] != 2 && sector0[0xF] != 1) return false;
|
|
|
|
byte[] testMark = new byte[4];
|
|
Array.Copy(sector16, 24, testMark, 0, 4);
|
|
|
|
return cdiMark.SequenceEqual(testMark);
|
|
}
|
|
|
|
static bool IsVideoNowColor(byte[] videoFrame)
|
|
{
|
|
if(videoFrame is null || videoFrame.Length < _videoNowColorFrameMarker.Length) return false;
|
|
|
|
byte[] buffer = new byte[_videoNowColorFrameMarker.Length];
|
|
|
|
for(int framePosition = 0; framePosition + buffer.Length < videoFrame.Length; framePosition++)
|
|
{
|
|
Array.Copy(videoFrame, framePosition, buffer, 0, buffer.Length);
|
|
|
|
for(int ab = 9; ab < buffer.Length; ab += 10) buffer[ab] = 0;
|
|
|
|
if(!_videoNowColorFrameMarker.SequenceEqual(buffer)) continue;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static int GetVideoNowColorOffset(byte[] data)
|
|
{
|
|
byte[] buffer = new byte[_videoNowColorFrameMarker.Length];
|
|
|
|
for(int framePosition = 0; framePosition + buffer.Length < data.Length; framePosition++)
|
|
{
|
|
Array.Copy(data, framePosition, buffer, 0, buffer.Length);
|
|
|
|
for(int ab = 9; ab < buffer.Length; ab += 10) buffer[ab] = 0;
|
|
|
|
if(!_videoNowColorFrameMarker.SequenceEqual(buffer)) continue;
|
|
|
|
return 18032 - framePosition;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
internal static void DetectDiscType(ref MediaType mediaType, int sessions, FullTOC.CDFullTOC? decodedToc,
|
|
Device dev, out bool hiddenTrack, out bool hiddenData,
|
|
int firstTrackLastSession, ulong blocks)
|
|
{
|
|
uint startOfFirstDataTrack = uint.MaxValue;
|
|
DI.DiscInformation? blurayDi = null;
|
|
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;
|
|
|
|
sense = dev.GetConfiguration(out byte[] cmdBuf, out _, 0, MmcGetConfigurationRt.Current, dev.Timeout, out _);
|
|
|
|
if(!sense)
|
|
{
|
|
Features.SeparatedFeatures ftr = Features.Separate(cmdBuf);
|
|
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Core.GET_CONFIGURATION_current_profile_is_0,
|
|
ftr.CurrentProfile);
|
|
|
|
mediaType = ftr.CurrentProfile switch
|
|
{
|
|
0x0001 => MediaType.GENERIC_HDD,
|
|
0x0005 => MediaType.CDMO,
|
|
0x0008 => MediaType.CD,
|
|
0x0009 => MediaType.CDR,
|
|
0x000A => MediaType.CDRW,
|
|
0x0010 => MediaType.DVDROM,
|
|
0x0011 => MediaType.DVDR,
|
|
0x0012 => MediaType.DVDRAM,
|
|
0x0013 or 0x0014 => MediaType.DVDRW,
|
|
0x0015 or 0x0016 => MediaType.DVDRDL,
|
|
0x0017 => MediaType.DVDRWDL,
|
|
0x0018 => MediaType.DVDDownload,
|
|
0x001A => MediaType.DVDPRW,
|
|
0x001B => MediaType.DVDPR,
|
|
0x0020 => MediaType.DDCD,
|
|
0x0021 => MediaType.DDCDR,
|
|
0x0022 => MediaType.DDCDRW,
|
|
0x002A => MediaType.DVDPRWDL,
|
|
0x002B => MediaType.DVDPRDL,
|
|
0x0040 => MediaType.BDROM,
|
|
0x0041 or 0x0042 => MediaType.BDR,
|
|
0x0043 => MediaType.BDRE,
|
|
0x0050 => MediaType.HDDVDROM,
|
|
0x0051 => MediaType.HDDVDR,
|
|
0x0052 => MediaType.HDDVDRAM,
|
|
0x0053 => MediaType.HDDVDRW,
|
|
0x0058 => MediaType.HDDVDRDL,
|
|
0x005A => MediaType.HDDVDRWDL,
|
|
_ => mediaType
|
|
};
|
|
}
|
|
|
|
if(decodedToc?.TrackDescriptors.Any(t => t.SessionNumber == 2) == true)
|
|
{
|
|
secondSessionFirstTrack =
|
|
decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == 2).Min(t => t.POINT);
|
|
}
|
|
|
|
if(mediaType is MediaType.CD or MediaType.CDROMXA or MediaType.CDI)
|
|
{
|
|
sense = dev.ReadAtip(out cmdBuf, out _, dev.Timeout, out _);
|
|
|
|
if(!sense)
|
|
{
|
|
ATIP.CDATIP atip = ATIP.Decode(cmdBuf);
|
|
|
|
if(atip != null)
|
|
|
|
// Only CD-R and CD-RW have ATIP
|
|
mediaType = atip.DiscType ? MediaType.CDRW : MediaType.CDR;
|
|
}
|
|
}
|
|
|
|
if(mediaType is MediaType.CD or MediaType.CDROMXA)
|
|
{
|
|
bool hasDataTrack = false;
|
|
bool hasAudioTrack = false;
|
|
bool allFirstSessionTracksAreAudio = true;
|
|
bool hasVideoTrack = false;
|
|
|
|
if(decodedToc.HasValue)
|
|
{
|
|
FullTOC.TrackDataDescriptor a0Track =
|
|
decodedToc.Value.TrackDescriptors.FirstOrDefault(t => t is { POINT: 0xA0, ADR: 1 });
|
|
|
|
if(a0Track.POINT == 0xA0)
|
|
{
|
|
switch(a0Track.PSEC)
|
|
{
|
|
case 0x10:
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.TOC_says_disc_type_is_CD_i);
|
|
mediaType = MediaType.CDI;
|
|
|
|
break;
|
|
case 0x20:
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.TOC_says_disc_type_is_CD_ROM_XA);
|
|
|
|
mediaType = MediaType.CDROMXA;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach(FullTOC.TrackDataDescriptor track in
|
|
decodedToc.Value.TrackDescriptors.Where(t => t.POINT is > 0 and <= 0x99))
|
|
{
|
|
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(mediaType != MediaType.CDI)
|
|
{
|
|
switch(hasDataTrack)
|
|
{
|
|
case true when hasAudioTrack && allFirstSessionTracksAreAudio && sessions == 2:
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Core
|
|
.Disc_has_audio_and_data_two_sessions_all_data_second_CD_Plus);
|
|
|
|
mediaType = MediaType.CDPLUS;
|
|
|
|
break;
|
|
case false when hasAudioTrack && sessions == 1:
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Core.Disc_has_only_audio_in_a_session_CD_Digital_Audio);
|
|
|
|
mediaType = MediaType.CDDA;
|
|
|
|
break;
|
|
}
|
|
|
|
if(hasDataTrack && !hasAudioTrack && sessions == 1)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Disc_has_only_data_in_a_session_CD_ROM);
|
|
|
|
mediaType = MediaType.CDROM;
|
|
}
|
|
|
|
if(hasVideoTrack && !hasDataTrack && sessions == 1)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Disc_has_video_tracks_CD_Video);
|
|
|
|
mediaType = MediaType.CDV;
|
|
}
|
|
}
|
|
|
|
if(mediaType is MediaType.CD or MediaType.CDROM && hasDataTrack)
|
|
{
|
|
foreach(uint startAddress in decodedToc.Value.TrackDescriptors
|
|
.Where(t => t.POINT is > 0 and <= 0x99 &&
|
|
((TocControl)(t.CONTROL & 0x0D) ==
|
|
TocControl.DataTrack ||
|
|
(TocControl)(t.CONTROL & 0x0D) ==
|
|
TocControl.DataTrackIncremental))
|
|
.Select(track => (uint)(track.PHOUR * 3600 * 75 +
|
|
track.PMIN * 60 * 75 +
|
|
track.PSEC * 75 +
|
|
track.PFRAME -
|
|
150) +
|
|
16))
|
|
{
|
|
sense = dev.ReadCd(out cmdBuf,
|
|
out _,
|
|
startAddress,
|
|
2352,
|
|
1,
|
|
MmcSectorTypes.AllTypes,
|
|
false,
|
|
false,
|
|
true,
|
|
MmcHeaderCodes.AllHeaders,
|
|
true,
|
|
true,
|
|
MmcErrorField.None,
|
|
MmcSubchannel.None,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense || dev.Error) continue;
|
|
|
|
if(cmdBuf[0] != 0x00 ||
|
|
cmdBuf[1] != 0xFF ||
|
|
cmdBuf[2] != 0xFF ||
|
|
cmdBuf[3] != 0xFF ||
|
|
cmdBuf[4] != 0xFF ||
|
|
cmdBuf[5] != 0xFF ||
|
|
cmdBuf[6] != 0xFF ||
|
|
cmdBuf[7] != 0xFF ||
|
|
cmdBuf[8] != 0xFF ||
|
|
cmdBuf[9] != 0xFF ||
|
|
cmdBuf[10] != 0xFF ||
|
|
cmdBuf[11] != 0x00 ||
|
|
cmdBuf[15] != 0x02)
|
|
continue;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Disc_has_a_mode_two_data_track_CD_ROM_XA);
|
|
|
|
mediaType = MediaType.CDROMXA;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(secondSessionFirstTrack != 0 &&
|
|
decodedToc?.TrackDescriptors.Any(t => t.POINT == secondSessionFirstTrack) == true)
|
|
{
|
|
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.AllHeaders,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
MmcErrorField.None,
|
|
MmcSubchannel.None,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense || !dev.Error)
|
|
{
|
|
videoNowColorFrame = null;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Array.Copy(cmdBuf, 0, videoNowColorFrame, i * 2352, 2352);
|
|
}
|
|
|
|
FullTOC.TrackDataDescriptor? firstTrack =
|
|
decodedToc?.TrackDescriptors.FirstOrDefault(t => t.POINT ==
|
|
decodedToc.Value.TrackDescriptors.Min(m => m.POINT));
|
|
|
|
if(firstTrack?.POINT is >= 1 and < 0xA0)
|
|
{
|
|
uint firstTrackSector = (uint)(firstTrack.Value.PHOUR * 3600 * 75 +
|
|
firstTrack.Value.PMIN * 60 * 75 +
|
|
firstTrack.Value.PSEC * 75 +
|
|
firstTrack.Value.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(!sense && IsCdi(sector0, sector16))
|
|
{
|
|
mediaType = MediaType.CDIREADY;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Disc_has_hidden_CD_i_pregap_CD_i_Ready);
|
|
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hiddenData = IsScrambledData(sector0, 0, out int combinedOffset);
|
|
|
|
if(hiddenData)
|
|
{
|
|
int sectorsForOffset = combinedOffset / 2352;
|
|
|
|
if(sectorsForOffset < 0) sectorsForOffset *= -1;
|
|
|
|
if(combinedOffset % 2352 != 0) sectorsForOffset++;
|
|
|
|
int lba0 = 0;
|
|
int lba16 = 16;
|
|
|
|
if(combinedOffset < 0)
|
|
{
|
|
lba0 -= sectorsForOffset;
|
|
lba16 -= sectorsForOffset;
|
|
}
|
|
|
|
sense = dev.ReadCd(out sector0,
|
|
out _,
|
|
(uint)lba0,
|
|
2352,
|
|
(uint)sectorsForOffset + 1,
|
|
MmcSectorTypes.AllTypes,
|
|
false,
|
|
false,
|
|
true,
|
|
MmcHeaderCodes.AllHeaders,
|
|
true,
|
|
true,
|
|
MmcErrorField.None,
|
|
MmcSubchannel.None,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
// Drive does not support reading negative sectors?
|
|
if(sense && lba0 < 0)
|
|
{
|
|
dev.ReadCd(out sector0,
|
|
out _,
|
|
0,
|
|
2352,
|
|
2,
|
|
MmcSectorTypes.AllTypes,
|
|
false,
|
|
false,
|
|
true,
|
|
MmcHeaderCodes.AllHeaders,
|
|
true,
|
|
true,
|
|
MmcErrorField.None,
|
|
MmcSubchannel.None,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
sector0 = DescrambleAndFixOffset(sector0, combinedOffset, sectorsForOffset);
|
|
}
|
|
else
|
|
sector0 = DescrambleAndFixOffset(sector0, combinedOffset, sectorsForOffset);
|
|
|
|
dev.ReadCd(out byte[] sector16,
|
|
out _,
|
|
(uint)lba16,
|
|
2352,
|
|
(uint)sectorsForOffset + 1,
|
|
MmcSectorTypes.AllTypes,
|
|
false,
|
|
false,
|
|
true,
|
|
MmcHeaderCodes.AllHeaders,
|
|
true,
|
|
true,
|
|
MmcErrorField.None,
|
|
MmcSubchannel.None,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
sector16 = DescrambleAndFixOffset(sector16, combinedOffset, sectorsForOffset);
|
|
|
|
if(IsCdi(sector0, sector16))
|
|
{
|
|
mediaType = MediaType.CDIREADY;
|
|
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Core.Disc_has_hidden_CD_i_pregap_CD_i_Ready);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
true,
|
|
false,
|
|
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.UHDBD:
|
|
case MediaType.Unknown:
|
|
if(mediaType is MediaType.BDROM or MediaType.UHDBD)
|
|
{
|
|
sense = dev.ReadDiscStructure(out cmdBuf,
|
|
out _,
|
|
MmcDiscStructureMediaType.Bd,
|
|
0,
|
|
0,
|
|
MmcDiscStructureFormat.DiscInformation,
|
|
0,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(!sense) blurayDi = DI.Decode(cmdBuf);
|
|
}
|
|
|
|
sense = dev.Read16(out cmdBuf, out _, 0, false, false, false, 0, 2048, 0, 1, false, dev.Timeout, out _);
|
|
|
|
if(!sense && !dev.Error)
|
|
{
|
|
sector0 = cmdBuf;
|
|
|
|
sense = dev.Read16(out cmdBuf,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
1,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(!sense && !dev.Error) sector1 = cmdBuf;
|
|
|
|
sense = dev.Read16(out cmdBuf,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
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,
|
|
false,
|
|
false,
|
|
false,
|
|
0,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(!sense && !dev.Error)
|
|
{
|
|
sector0 = cmdBuf;
|
|
|
|
sense = dev.Read12(out cmdBuf,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
1,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(!sense && !dev.Error) sector1 = cmdBuf;
|
|
|
|
sense = dev.Read12(out cmdBuf,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
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,
|
|
false,
|
|
false,
|
|
false,
|
|
0,
|
|
2048,
|
|
0,
|
|
1,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(!sense && !dev.Error)
|
|
{
|
|
sector0 = cmdBuf;
|
|
|
|
sense = dev.Read10(out cmdBuf,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
1,
|
|
2048,
|
|
0,
|
|
1,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(!sense && !dev.Error) sector1 = cmdBuf;
|
|
|
|
sense = dev.Read10(out cmdBuf,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(mediaType == MediaType.DVDROM)
|
|
{
|
|
sense = dev.ReadDiscStructure(out cmdBuf,
|
|
out _,
|
|
MmcDiscStructureMediaType.Dvd,
|
|
0,
|
|
0,
|
|
MmcDiscStructureFormat.PhysicalInformation,
|
|
0,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(!sense)
|
|
{
|
|
PFI.PhysicalFormatInformation? pfi = PFI.Decode(cmdBuf, mediaType);
|
|
|
|
if(pfi != null)
|
|
{
|
|
mediaType = pfi.Value.DiskCategory switch
|
|
{
|
|
DiskCategory.DVDPR => MediaType.DVDPR,
|
|
DiskCategory.DVDPRDL => MediaType.DVDPRDL,
|
|
DiskCategory.DVDPRW => MediaType.DVDPRW,
|
|
DiskCategory.DVDPRWDL => MediaType.DVDPRWDL,
|
|
DiskCategory.DVDR => pfi.Value.PartVersion >= 6
|
|
? MediaType.DVDRDL
|
|
: MediaType.DVDR,
|
|
DiskCategory.DVDRAM => MediaType.DVDRAM,
|
|
DiskCategory.DVDRW => pfi.Value.PartVersion >= 15
|
|
? MediaType.DVDRWDL
|
|
: MediaType.DVDRW,
|
|
DiskCategory.HDDVDR => MediaType.HDDVDR,
|
|
DiskCategory.HDDVDRAM => MediaType.HDDVDRAM,
|
|
DiskCategory.HDDVDROM => MediaType.HDDVDROM,
|
|
DiskCategory.HDDVDRW => MediaType.HDDVDRW,
|
|
DiskCategory.Nintendo => pfi.Value.DiscSize == DVDSize.Eighty
|
|
? MediaType.GOD
|
|
: MediaType.WOD,
|
|
DiskCategory.UMD => MediaType.UMD,
|
|
_ => mediaType
|
|
};
|
|
}
|
|
}
|
|
|
|
sense = dev.ReadDiscStructure(out cmdBuf,
|
|
out _,
|
|
MmcDiscStructureMediaType.Dvd,
|
|
0,
|
|
0,
|
|
MmcDiscStructureFormat.DiscManufacturingInformation,
|
|
0,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(!sense)
|
|
{
|
|
if(DMI.IsXbox(cmdBuf))
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
"Found Xbox DMI, setting disc type to Xbox Game Disc (XGD).");
|
|
|
|
mediaType = MediaType.XGD;
|
|
|
|
return;
|
|
}
|
|
|
|
if(DMI.IsXbox360(cmdBuf))
|
|
{
|
|
// All XGD3 all have the same number of blocks
|
|
if(blocks is 25063 or 4229664 or 4246304) // Wxripper unlock
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
"Found Xbox 360 DMI with {0} blocks, setting disc type to Xbox 360 Game Disc 3 (XGD3).");
|
|
|
|
mediaType = MediaType.XGD3;
|
|
|
|
return;
|
|
}
|
|
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
"Found Xbox 360 DMI with {0} blocks, setting disc type to Xbox 360 Game Disc 2 (XGD2).");
|
|
|
|
mediaType = MediaType.XGD2;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
// Recordables will be checked for PhotoCD only
|
|
case MediaType.CDR:
|
|
// Check if ISO9660
|
|
sense = dev.Read12(out byte[] isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
16,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
// Sector 16 reads, and contains "CD001" magic?
|
|
if(sense ||
|
|
isoSector[1] != 0x43 ||
|
|
isoSector[2] != 0x44 ||
|
|
isoSector[3] != 0x30 ||
|
|
isoSector[4] != 0x30 ||
|
|
isoSector[5] != 0x31)
|
|
return;
|
|
|
|
// From sectors 16 to 31
|
|
uint isoSectorPosition = 16;
|
|
|
|
while(isoSectorPosition < 32)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
isoSectorPosition,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
// If sector cannot be read, break here
|
|
if(sense) break;
|
|
|
|
// If sector does not contain "CD001" magic, break
|
|
if(isoSector[1] != 0x43 ||
|
|
isoSector[2] != 0x44 ||
|
|
isoSector[3] != 0x30 ||
|
|
isoSector[4] != 0x30 ||
|
|
isoSector[5] != 0x31)
|
|
break;
|
|
|
|
// If it is PVD or end of descriptor chain, break
|
|
if(isoSector[0] == 1 || isoSector[0] == 255) break;
|
|
|
|
isoSectorPosition++;
|
|
}
|
|
|
|
// If it's not an ISO9660 PVD, return
|
|
if(isoSector[0] != 1 ||
|
|
isoSector[1] != 0x43 ||
|
|
isoSector[2] != 0x44 ||
|
|
isoSector[3] != 0x30 ||
|
|
isoSector[4] != 0x30 ||
|
|
isoSector[5] != 0x31)
|
|
return;
|
|
|
|
uint rootStart = BitConverter.ToUInt32(isoSector, 158);
|
|
uint rootLength = BitConverter.ToUInt32(isoSector, 166);
|
|
|
|
if(rootStart == 0 || rootLength == 0) return;
|
|
|
|
rootLength /= 2048;
|
|
|
|
try
|
|
{
|
|
using var rootMs = new MemoryStream();
|
|
|
|
for(uint i = 0; i < rootLength; i++)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
rootStart + i,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense) break;
|
|
|
|
rootMs.Write(isoSector, 0, 2048);
|
|
}
|
|
|
|
isoSector = rootMs.ToArray();
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
SentrySdk.CaptureException(ex);
|
|
|
|
return;
|
|
}
|
|
|
|
if(isoSector.Length < 2048) return;
|
|
|
|
int rootPos = 0;
|
|
uint pcdStart = 0;
|
|
uint pcdLength = 0;
|
|
|
|
while(isoSector[rootPos] > 0 &&
|
|
rootPos < isoSector.Length &&
|
|
rootPos + isoSector[rootPos] <= isoSector.Length)
|
|
{
|
|
int nameLen = isoSector[rootPos + 32];
|
|
byte[] tmpName = new byte[nameLen];
|
|
Array.Copy(isoSector, rootPos + 33, tmpName, 0, nameLen);
|
|
string name = StringHandlers.CToString(tmpName).ToUpperInvariant();
|
|
|
|
if(name.EndsWith(";1", StringComparison.InvariantCulture)) name = name[..^2];
|
|
|
|
if(name == "PHOTO_CD" && (isoSector[rootPos + 25] & 0x02) == 0x02)
|
|
{
|
|
pcdStart = BitConverter.ToUInt32(isoSector, rootPos + 2);
|
|
pcdLength = BitConverter.ToUInt32(isoSector, rootPos + 10) / 2048;
|
|
}
|
|
|
|
rootPos += isoSector[rootPos];
|
|
}
|
|
|
|
if(pcdLength > 0)
|
|
{
|
|
try
|
|
{
|
|
using var pcdMs = new MemoryStream();
|
|
|
|
for(uint i = 0; i < pcdLength; i++)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
pcdStart + i,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense) break;
|
|
|
|
pcdMs.Write(isoSector, 0, 2048);
|
|
}
|
|
|
|
isoSector = pcdMs.ToArray();
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
SentrySdk.CaptureException(ex);
|
|
|
|
return;
|
|
}
|
|
|
|
if(isoSector.Length < 2048) return;
|
|
|
|
for(int pi = 0; pi < pcdLength; pi++)
|
|
{
|
|
int pcdPos = pi * 2048;
|
|
uint infoPos = 0;
|
|
|
|
while(isoSector[pcdPos] > 0 &&
|
|
pcdPos < isoSector.Length &&
|
|
pcdPos + isoSector[pcdPos] <= isoSector.Length)
|
|
{
|
|
int nameLen = isoSector[pcdPos + 32];
|
|
byte[] tmpName = new byte[nameLen];
|
|
Array.Copy(isoSector, pcdPos + 33, tmpName, 0, nameLen);
|
|
string name = StringHandlers.CToString(tmpName).ToUpperInvariant();
|
|
|
|
if(name.EndsWith(";1", StringComparison.InvariantCulture)) name = name[..^2];
|
|
|
|
if(name == "INFO.PCD")
|
|
{
|
|
infoPos = BitConverter.ToUInt32(isoSector, pcdPos + 2);
|
|
|
|
break;
|
|
}
|
|
|
|
pcdPos += isoSector[pcdPos];
|
|
}
|
|
|
|
if(infoPos > 0)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
infoPos,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense) break;
|
|
|
|
byte[] systemId = new byte[8];
|
|
Array.Copy(isoSector, 0, systemId, 0, 8);
|
|
|
|
string id = StringHandlers.CToString(systemId).TrimEnd();
|
|
|
|
switch(id)
|
|
{
|
|
case "PHOTO_CD":
|
|
mediaType = MediaType.PCD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Photo_CD_description_file);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
// Other recordables will not be checked
|
|
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: Pippin requires interpreting Apple Partition Map, reading HFS and checking for Pippin signatures
|
|
{
|
|
if(CD.DecodeIPBin(sector0).HasValue)
|
|
{
|
|
mediaType = MediaType.MEGACD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Mega_Sega_CD_IP_BIN);
|
|
|
|
return;
|
|
}
|
|
|
|
if(Saturn.DecodeIPBin(sector0).HasValue)
|
|
{
|
|
mediaType = MediaType.SATURNCD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Sega_Saturn_IP_BIN);
|
|
|
|
return;
|
|
}
|
|
|
|
// Are GDR detectable ???
|
|
if(Dreamcast.DecodeIPBin(sector0).HasValue)
|
|
{
|
|
mediaType = MediaType.GDROM;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Sega_Dreamcast_IP_BIN);
|
|
|
|
return;
|
|
}
|
|
|
|
if(ps2BootSectors is { 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 _);
|
|
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Core.PlayStation_2_boot_sectors_SHA256_0,
|
|
ps2BootSectorsHash);
|
|
|
|
if(ps2BootSectorsHash is PS2_PAL_HASH or PS2_NTSC_HASH or PS2_JAPANESE_HASH)
|
|
{
|
|
mediaType = MediaType.PS2CD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Sony_PlayStation_2_boot_sectors);
|
|
|
|
goto hasPs2CdBoot;
|
|
}
|
|
}
|
|
|
|
if(sector0 != null)
|
|
{
|
|
byte[] syncBytes = new byte[7];
|
|
Array.Copy(sector0, 0, syncBytes, 0, 7);
|
|
|
|
if(_operaId.SequenceEqual(syncBytes))
|
|
{
|
|
mediaType = MediaType.ThreeDO;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Opera_filesystem);
|
|
|
|
return;
|
|
}
|
|
|
|
if(_fmTownsBootId.SequenceEqual(syncBytes))
|
|
{
|
|
mediaType = MediaType.FMTOWNS;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_FM_Towns_boot);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Playdia_copyright);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(secondDataSectorNotZero != null)
|
|
{
|
|
byte[] pce = new byte[_pcEngineSignature.Length];
|
|
Array.Copy(secondDataSectorNotZero, 32, pce, 0, pce.Length);
|
|
|
|
if(_pcEngineSignature.SequenceEqual(pce))
|
|
{
|
|
mediaType = MediaType.SuperCDROM2;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_PC_Engine_CD_signature);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(firstDataSectorNotZero != null)
|
|
{
|
|
byte[] pcfx = new byte[_pcFxSignature.Length];
|
|
Array.Copy(firstDataSectorNotZero, 0, pcfx, 0, pcfx.Length);
|
|
|
|
if(_pcFxSignature.SequenceEqual(pcfx))
|
|
{
|
|
mediaType = MediaType.PCFX;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_PC_FX_copyright);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Atari_signature);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(firstTrackSecondSession?.Length >= 2336)
|
|
{
|
|
byte[] milcd = new byte[2048];
|
|
Array.Copy(firstTrackSecondSession, 24, milcd, 0, 2048);
|
|
|
|
if(Dreamcast.DecodeIPBin(milcd).HasValue)
|
|
{
|
|
mediaType = MediaType.MilCD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Sega_Dreamcast_IP_BIN_on_second_session);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// TODO: Detect black and white VideoNow
|
|
// TODO: Detect VideoNow XP
|
|
if(IsVideoNowColor(videoNowColorFrame))
|
|
{
|
|
mediaType = MediaType.VideoNowColor;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_VideoNow_Color_frame);
|
|
|
|
return;
|
|
}
|
|
|
|
// Check CD+G, CD+EG and CD+MIDI
|
|
if(mediaType == MediaType.CDDA)
|
|
{
|
|
sense = dev.ReadCd(out byte[] subBuf,
|
|
out _,
|
|
150,
|
|
96,
|
|
8,
|
|
MmcSectorTypes.Cdda,
|
|
false,
|
|
false,
|
|
false,
|
|
MmcHeaderCodes.None,
|
|
false,
|
|
false,
|
|
MmcErrorField.None,
|
|
MmcSubchannel.Raw,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(!sense)
|
|
{
|
|
bool cdg = false;
|
|
bool cdeg = false;
|
|
bool cdmidi = false;
|
|
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
byte[] tmpSub = new byte[96];
|
|
Array.Copy(subBuf, i * 96, tmpSub, 0, 96);
|
|
DetectRwPackets(tmpSub, out bool cdgPacket, out bool cdegPacket, out bool cdmidiPacket);
|
|
|
|
if(cdgPacket) cdg = true;
|
|
|
|
if(cdegPacket) cdeg = true;
|
|
|
|
if(cdmidiPacket) cdmidi = true;
|
|
}
|
|
|
|
if(cdeg)
|
|
{
|
|
mediaType = MediaType.CDEG;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_enhanced_graphics_RW_packet);
|
|
|
|
return;
|
|
}
|
|
|
|
if(cdg)
|
|
{
|
|
mediaType = MediaType.CDG;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_graphics_RW_packet);
|
|
|
|
return;
|
|
}
|
|
|
|
if(cdmidi)
|
|
{
|
|
mediaType = MediaType.CDMIDI;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_MIDI_RW_packet);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If it has a PS2 boot area it can still be PS1, so check for SYSTEM.CNF below
|
|
hasPs2CdBoot:
|
|
|
|
// Check if ISO9660
|
|
sense = dev.Read12(out byte[] isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
16,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
// Sector 16 reads, and contains "CD001" magic?
|
|
if(sense ||
|
|
isoSector[1] != 0x43 ||
|
|
isoSector[2] != 0x44 ||
|
|
isoSector[3] != 0x30 ||
|
|
isoSector[4] != 0x30 ||
|
|
isoSector[5] != 0x31)
|
|
return;
|
|
|
|
// From sectors 16 to 31
|
|
uint isoSectorPosition = 16;
|
|
|
|
while(isoSectorPosition < 32)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
isoSectorPosition,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
// If sector cannot be read, break here
|
|
if(sense) break;
|
|
|
|
// If sector does not contain "CD001" magic, break
|
|
if(isoSector[1] != 0x43 ||
|
|
isoSector[2] != 0x44 ||
|
|
isoSector[3] != 0x30 ||
|
|
isoSector[4] != 0x30 ||
|
|
isoSector[5] != 0x31)
|
|
break;
|
|
|
|
// If it is PVD or end of descriptor chain, break
|
|
if(isoSector[0] == 1 || isoSector[0] == 255) break;
|
|
|
|
isoSectorPosition++;
|
|
}
|
|
|
|
// If it's not an ISO9660 PVD, return
|
|
if(isoSector[0] != 1 ||
|
|
isoSector[1] != 0x43 ||
|
|
isoSector[2] != 0x44 ||
|
|
isoSector[3] != 0x30 ||
|
|
isoSector[4] != 0x30 ||
|
|
isoSector[5] != 0x31)
|
|
return;
|
|
|
|
uint rootStart = BitConverter.ToUInt32(isoSector, 158);
|
|
uint rootLength = BitConverter.ToUInt32(isoSector, 166);
|
|
|
|
if(rootStart == 0 || rootLength == 0) return;
|
|
|
|
rootLength /= 2048;
|
|
|
|
try
|
|
{
|
|
using var rootMs = new MemoryStream();
|
|
|
|
for(uint i = 0; i < rootLength; i++)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
rootStart + i,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense) break;
|
|
|
|
rootMs.Write(isoSector, 0, 2048);
|
|
}
|
|
|
|
isoSector = rootMs.ToArray();
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
SentrySdk.CaptureException(ex);
|
|
|
|
return;
|
|
}
|
|
|
|
if(isoSector.Length < 2048) return;
|
|
|
|
List<string> rootEntries = [];
|
|
uint ngcdIplStart = 0;
|
|
uint ngcdIplLength = 0;
|
|
uint vcdStart = 0;
|
|
uint vcdLength = 0;
|
|
uint pcdStart = 0;
|
|
uint pcdLength = 0;
|
|
uint ps1Start = 0;
|
|
uint ps1Length = 0;
|
|
|
|
for(int ri = 0; ri < rootLength; ri++)
|
|
{
|
|
int rootPos = ri * 2048;
|
|
|
|
while(rootPos < isoSector.Length &&
|
|
isoSector[rootPos] > 0 &&
|
|
rootPos + isoSector[rootPos] <= isoSector.Length)
|
|
{
|
|
int nameLen = isoSector[rootPos + 32];
|
|
byte[] tmpName = new byte[nameLen];
|
|
Array.Copy(isoSector, rootPos + 33, tmpName, 0, nameLen);
|
|
string name = StringHandlers.CToString(tmpName).ToUpperInvariant();
|
|
|
|
if(name.EndsWith(";1", StringComparison.InvariantCulture)) name = name[..^2];
|
|
|
|
rootEntries.Add(name);
|
|
|
|
switch(name)
|
|
{
|
|
case "IPL.TXT":
|
|
ngcdIplStart = BitConverter.ToUInt32(isoSector, rootPos + 2);
|
|
ngcdIplLength = BitConverter.ToUInt32(isoSector, rootPos + 10);
|
|
|
|
break;
|
|
|
|
case "VCD" when (isoSector[rootPos + 25] & 0x02) == 0x02:
|
|
case "SVCD" when (isoSector[rootPos + 25] & 0x02) == 0x02:
|
|
case "HQVCD" when (isoSector[rootPos + 25] & 0x02) == 0x02:
|
|
vcdStart = BitConverter.ToUInt32(isoSector, rootPos + 2);
|
|
vcdLength = BitConverter.ToUInt32(isoSector, rootPos + 10) / 2048;
|
|
|
|
break;
|
|
case "PHOTO_CD" when (isoSector[rootPos + 25] & 0x02) == 0x02:
|
|
pcdStart = BitConverter.ToUInt32(isoSector, rootPos + 2);
|
|
pcdLength = BitConverter.ToUInt32(isoSector, rootPos + 10) / 2048;
|
|
|
|
break;
|
|
case "SYSTEM.CNF":
|
|
ps1Start = BitConverter.ToUInt32(isoSector, rootPos + 2);
|
|
ps1Length = BitConverter.ToUInt32(isoSector, rootPos + 10);
|
|
|
|
break;
|
|
}
|
|
|
|
rootPos += isoSector[rootPos];
|
|
}
|
|
}
|
|
|
|
if(rootEntries.Count == 0) return;
|
|
|
|
if(rootEntries.Contains("CD32.TM"))
|
|
{
|
|
mediaType = MediaType.CD32;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_CD32_TM_file_in_root);
|
|
|
|
return;
|
|
}
|
|
|
|
if(rootEntries.Contains("CDTV.TM"))
|
|
{
|
|
mediaType = MediaType.CDTV;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_CDTV_TM_file_in_root);
|
|
|
|
return;
|
|
}
|
|
|
|
// "IPL.TXT" length
|
|
if(ngcdIplLength > 0)
|
|
{
|
|
uint ngcdSectors = ngcdIplLength / 2048;
|
|
|
|
if(ngcdIplLength % 2048 > 0) ngcdSectors++;
|
|
|
|
string iplTxt;
|
|
|
|
// Read "IPL.TXT"
|
|
try
|
|
{
|
|
using var ngcdMs = new MemoryStream();
|
|
|
|
for(uint i = 0; i < ngcdSectors; i++)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
ngcdIplStart + i,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense) break;
|
|
|
|
ngcdMs.Write(isoSector, 0, 2048);
|
|
}
|
|
|
|
isoSector = ngcdMs.ToArray();
|
|
iplTxt = Encoding.ASCII.GetString(isoSector);
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
SentrySdk.CaptureException(ex);
|
|
|
|
iplTxt = null;
|
|
}
|
|
|
|
// Check "IPL.TXT" lines
|
|
if(iplTxt != null)
|
|
{
|
|
using var sr = new StringReader(iplTxt);
|
|
|
|
bool correctNeoGeoCd = true;
|
|
int lineNumber = 0;
|
|
|
|
while(sr.Peek() > 0)
|
|
{
|
|
string line = sr.ReadLine();
|
|
|
|
// End of file
|
|
if(line is null || line.Length == 0)
|
|
{
|
|
if(lineNumber == 0) correctNeoGeoCd = false;
|
|
|
|
break;
|
|
}
|
|
|
|
// Split line by comma
|
|
string[] split = line.Split(',');
|
|
|
|
// Empty line
|
|
if(split.Length == 0) continue;
|
|
|
|
// More than 3 entries
|
|
if(split.Length != 3)
|
|
{
|
|
if(line[0] < 0x20) break;
|
|
|
|
correctNeoGeoCd = false;
|
|
|
|
break;
|
|
}
|
|
|
|
// Split filename
|
|
string[] split2 = split[0].Split('.');
|
|
|
|
// Filename must have two parts, name and extension
|
|
if(split2.Length != 2)
|
|
{
|
|
correctNeoGeoCd = false;
|
|
|
|
break;
|
|
}
|
|
|
|
// Name must be smaller or equal to 8 characters
|
|
if(split2[0].Length > 8)
|
|
{
|
|
correctNeoGeoCd = false;
|
|
|
|
break;
|
|
}
|
|
|
|
// Extension must be smaller or equal to 8 characters
|
|
if(split2[1].Length > 3)
|
|
{
|
|
correctNeoGeoCd = false;
|
|
|
|
break;
|
|
}
|
|
|
|
// Second part must be a single digit
|
|
if(split[1].Length != 1 || !byte.TryParse(split[1], out _))
|
|
{
|
|
correctNeoGeoCd = false;
|
|
|
|
break;
|
|
}
|
|
|
|
// Third part must be bigger or equal to 1 and smaller or equal to 8
|
|
if(split[2].Length is < 1 or > 8)
|
|
{
|
|
correctNeoGeoCd = false;
|
|
|
|
break;
|
|
}
|
|
|
|
try
|
|
{
|
|
_ = Convert.ToUInt32(split[2], 16);
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
SentrySdk.CaptureException(ex);
|
|
|
|
correctNeoGeoCd = false;
|
|
|
|
break;
|
|
}
|
|
|
|
lineNumber++;
|
|
}
|
|
|
|
if(correctNeoGeoCd)
|
|
{
|
|
mediaType = MediaType.NeoGeoCD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_correct_IPL_TXT_file_in_root);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(vcdLength > 0)
|
|
{
|
|
try
|
|
{
|
|
using var vcdMs = new MemoryStream();
|
|
|
|
for(uint i = 0; i < vcdLength; i++)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
vcdStart + i,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense) break;
|
|
|
|
vcdMs.Write(isoSector, 0, 2048);
|
|
}
|
|
|
|
isoSector = vcdMs.ToArray();
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
SentrySdk.CaptureException(ex);
|
|
|
|
return;
|
|
}
|
|
|
|
if(isoSector.Length < 2048) return;
|
|
|
|
uint infoPos = 0;
|
|
|
|
for(int vi = 0; vi < vcdLength; vi++)
|
|
{
|
|
int vcdPos = vi * 2048;
|
|
|
|
while(vcdPos < isoSector.Length &&
|
|
isoSector[vcdPos] > 0 &&
|
|
vcdPos + isoSector[vcdPos] <= isoSector.Length)
|
|
{
|
|
int nameLen = isoSector[vcdPos + 32];
|
|
byte[] tmpName = new byte[nameLen];
|
|
Array.Copy(isoSector, vcdPos + 33, tmpName, 0, nameLen);
|
|
string name = StringHandlers.CToString(tmpName).ToUpperInvariant();
|
|
|
|
if(name.EndsWith(";1", StringComparison.InvariantCulture)) name = name[..^2];
|
|
|
|
if(name is "INFO.VCD" or "INFO.SVD")
|
|
{
|
|
infoPos = BitConverter.ToUInt32(isoSector, vcdPos + 2);
|
|
|
|
break;
|
|
}
|
|
|
|
vcdPos += isoSector[vcdPos];
|
|
}
|
|
}
|
|
|
|
if(infoPos > 0)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
infoPos,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense) break;
|
|
|
|
byte[] systemId = new byte[8];
|
|
Array.Copy(isoSector, 0, systemId, 0, 8);
|
|
|
|
string id = StringHandlers.CToString(systemId).TrimEnd();
|
|
|
|
switch(id)
|
|
{
|
|
case "VIDEO_CD":
|
|
mediaType = MediaType.VCD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Video_CD_description_file);
|
|
|
|
return;
|
|
case "SUPERVCD":
|
|
mediaType = MediaType.SVCD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Super_Video_CD_description_file);
|
|
|
|
break;
|
|
case "HQ-VCD":
|
|
mediaType = MediaType.CVD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Core.Found_China_Video_Disc_description_file);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pcdLength > 0)
|
|
{
|
|
try
|
|
{
|
|
using var pcdMs = new MemoryStream();
|
|
|
|
for(uint i = 0; i < pcdLength; i++)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
pcdStart + i,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense) break;
|
|
|
|
pcdMs.Write(isoSector, 0, 2048);
|
|
}
|
|
|
|
isoSector = pcdMs.ToArray();
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
SentrySdk.CaptureException(ex);
|
|
|
|
return;
|
|
}
|
|
|
|
if(isoSector.Length < 2048) return;
|
|
|
|
uint infoPos = 0;
|
|
|
|
for(int pi = 0; pi < pcdLength; pi++)
|
|
{
|
|
int pcdPos = pi * 2048;
|
|
|
|
while(pcdPos < isoSector.Length &&
|
|
isoSector[pcdPos] > 0 &&
|
|
pcdPos + isoSector[pcdPos] <= isoSector.Length)
|
|
{
|
|
int nameLen = isoSector[pcdPos + 32];
|
|
byte[] tmpName = new byte[nameLen];
|
|
Array.Copy(isoSector, pcdPos + 33, tmpName, 0, nameLen);
|
|
string name = StringHandlers.CToString(tmpName).ToUpperInvariant();
|
|
|
|
if(name.EndsWith(";1", StringComparison.InvariantCulture)) name = name[..^2];
|
|
|
|
if(name == "INFO.PCD")
|
|
{
|
|
infoPos = BitConverter.ToUInt32(isoSector, pcdPos + 2);
|
|
|
|
break;
|
|
}
|
|
|
|
pcdPos += isoSector[pcdPos];
|
|
}
|
|
}
|
|
|
|
if(infoPos > 0)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
infoPos,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense) break;
|
|
|
|
byte[] systemId = new byte[8];
|
|
Array.Copy(isoSector, 0, systemId, 0, 8);
|
|
|
|
string id = StringHandlers.CToString(systemId).TrimEnd();
|
|
|
|
switch(id)
|
|
{
|
|
case "PHOTO_CD":
|
|
mediaType = MediaType.PCD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Photo_CD_description_file);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// "SYSTEM.CNF" length
|
|
if(ps1Length > 0)
|
|
{
|
|
uint ps1Sectors = ps1Length / 2048;
|
|
|
|
if(ps1Length % 2048 > 0) ps1Sectors++;
|
|
|
|
string ps1Txt;
|
|
|
|
// Read "SYSTEM.CNF"
|
|
try
|
|
{
|
|
using var ps1Ms = new MemoryStream();
|
|
|
|
for(uint i = 0; i < ps1Sectors; i++)
|
|
{
|
|
sense = dev.Read12(out isoSector,
|
|
out _,
|
|
0,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
ps1Start + i,
|
|
2048,
|
|
0,
|
|
1,
|
|
false,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(sense) break;
|
|
|
|
ps1Ms.Write(isoSector, 0, 2048);
|
|
}
|
|
|
|
isoSector = ps1Ms.ToArray();
|
|
ps1Txt = Encoding.ASCII.GetString(isoSector);
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
SentrySdk.CaptureException(ex);
|
|
|
|
ps1Txt = null;
|
|
}
|
|
|
|
// Check "SYSTEM.CNF" lines
|
|
if(ps1Txt != null)
|
|
{
|
|
using var sr = new StringReader(ps1Txt);
|
|
|
|
string ps1BootFile = null;
|
|
string ps2BootFile = null;
|
|
|
|
while(sr.Peek() > 0)
|
|
{
|
|
string line = sr.ReadLine();
|
|
|
|
// End of file
|
|
if(line is null || line.Length == 0) break;
|
|
|
|
line = line.Replace(" ", "");
|
|
|
|
if(line.StartsWith("BOOT=cdrom:", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
ps1BootFile = line[11..];
|
|
|
|
if(ps1BootFile.StartsWith('\\')) ps1BootFile = ps1BootFile[1..];
|
|
|
|
if(ps1BootFile.EndsWith(";1", StringComparison.InvariantCultureIgnoreCase))
|
|
ps1BootFile = ps1BootFile[..^2];
|
|
|
|
break;
|
|
}
|
|
|
|
if(!line.StartsWith("BOOT2=cdrom0:", StringComparison.InvariantCultureIgnoreCase)) continue;
|
|
|
|
ps2BootFile = line[13..];
|
|
|
|
if(ps2BootFile.StartsWith('\\')) ps2BootFile = ps2BootFile[1..];
|
|
|
|
if(ps2BootFile.EndsWith(";1", StringComparison.InvariantCultureIgnoreCase))
|
|
ps2BootFile = ps2BootFile[..^2];
|
|
|
|
break;
|
|
}
|
|
|
|
if(ps1BootFile != null && rootEntries.Contains(ps1BootFile.ToUpperInvariant()))
|
|
{
|
|
mediaType = MediaType.PS1CD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_correct_SYSTEM_CNF_file_in_root_PS1);
|
|
}
|
|
|
|
if(ps2BootFile != null && rootEntries.Contains(ps2BootFile.ToUpperInvariant()))
|
|
{
|
|
mediaType = MediaType.PS2CD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_correct_SYSTEM_CNF_file_in_root_PS2);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// TODO: Check for CD-i Ready
|
|
case MediaType.CDI:
|
|
break;
|
|
case MediaType.DVDROM:
|
|
case MediaType.HDDVDROM:
|
|
case MediaType.BDROM:
|
|
case MediaType.UHDBD:
|
|
case MediaType.Unknown:
|
|
// TODO: Nuon requires reading the filesystem, searching for a file called "/NUON/NUON.RUN"
|
|
if(ps2BootSectors is { 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 _);
|
|
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Core.PlayStation_2_boot_sectors_SHA256_0,
|
|
ps2BootSectorsHash);
|
|
|
|
if(ps2BootSectorsHash is PS2_PAL_HASH or PS2_NTSC_HASH or PS2_JAPANESE_HASH)
|
|
{
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Sony_PlayStation_2_boot_sectors_DVD);
|
|
|
|
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:
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Sony_PlayStation_3_boot_sectors);
|
|
|
|
mediaType = MediaType.PS3BD;
|
|
|
|
break;
|
|
case MediaType.DVDROM:
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Core.Found_Sony_PlayStation_3_boot_sectors_DVD);
|
|
|
|
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;
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Found_Sony_PlayStation_4_boot_sectors);
|
|
}
|
|
}
|
|
|
|
if(blurayDi is { Units.Length: > 0 } && blurayDi?.Units[0].DiscTypeIdentifier != null)
|
|
{
|
|
string blurayType = StringHandlers.CToString(blurayDi?.Units[0].DiscTypeIdentifier);
|
|
|
|
switch(blurayType)
|
|
{
|
|
case "BDR":
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Blu_ray_type_BDR_setting_disc_type_BDR);
|
|
|
|
mediaType = MediaType.BDR;
|
|
|
|
break;
|
|
case "XG4":
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Blu_ray_type_XG4_setting_disc_type_XGD4);
|
|
|
|
mediaType = MediaType.XGD4;
|
|
|
|
break;
|
|
|
|
// TODO: PS5
|
|
case "BDU":
|
|
if(sector1 != null)
|
|
{
|
|
byte[] tmp = new byte[_ps5Id.Length];
|
|
Array.Copy(sector1, 1024, tmp, 0, tmp.Length);
|
|
|
|
if(tmp.SequenceEqual(_ps5Id))
|
|
{
|
|
mediaType = MediaType.PS5BD;
|
|
|
|
AaruLogging.Debug(MODULE_NAME,
|
|
Localization.Core.Found_Sony_PlayStation_5_boot_sectors);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
AaruLogging.Debug(MODULE_NAME, Localization.Core.Blu_ray_type_BDU_setting_disc_type_UHD);
|
|
|
|
mediaType = MediaType.UHDBD;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void DetectRwPackets(byte[] subchannel, out bool cdgPacket, out bool cdegPacket, out bool cdmidiPacket)
|
|
{
|
|
cdgPacket = false;
|
|
cdegPacket = false;
|
|
cdmidiPacket = false;
|
|
|
|
byte[] cdSubRwPack1 = new byte[24];
|
|
byte[] cdSubRwPack2 = new byte[24];
|
|
byte[] cdSubRwPack3 = new byte[24];
|
|
byte[] cdSubRwPack4 = new byte[24];
|
|
|
|
int i = 0;
|
|
|
|
for(int j = 0; j < 24; j++) cdSubRwPack1[j] = (byte)(subchannel[i++] & 0x3F);
|
|
|
|
for(int j = 0; j < 24; j++) cdSubRwPack2[j] = (byte)(subchannel[i++] & 0x3F);
|
|
|
|
for(int j = 0; j < 24; j++) cdSubRwPack3[j] = (byte)(subchannel[i++] & 0x3F);
|
|
|
|
for(int j = 0; j < 24; j++) cdSubRwPack4[j] = (byte)(subchannel[i++] & 0x3F);
|
|
|
|
switch(cdSubRwPack1[0])
|
|
{
|
|
case 0x08:
|
|
case 0x09:
|
|
cdgPacket = true;
|
|
|
|
break;
|
|
case 0x0A:
|
|
cdegPacket = true;
|
|
|
|
break;
|
|
case 0x38:
|
|
cdmidiPacket = true;
|
|
|
|
break;
|
|
}
|
|
|
|
switch(cdSubRwPack2[0])
|
|
{
|
|
case 0x08:
|
|
case 0x09:
|
|
cdgPacket = true;
|
|
|
|
break;
|
|
case 0x0A:
|
|
cdegPacket = true;
|
|
|
|
break;
|
|
case 0x38:
|
|
cdmidiPacket = true;
|
|
|
|
break;
|
|
}
|
|
|
|
switch(cdSubRwPack3[0])
|
|
{
|
|
case 0x08:
|
|
case 0x09:
|
|
cdgPacket = true;
|
|
|
|
break;
|
|
case 0x0A:
|
|
cdegPacket = true;
|
|
|
|
break;
|
|
case 0x38:
|
|
cdmidiPacket = true;
|
|
|
|
break;
|
|
}
|
|
|
|
switch(cdSubRwPack4[0])
|
|
{
|
|
case 0x08:
|
|
case 0x09:
|
|
cdgPacket = true;
|
|
|
|
break;
|
|
case 0x0A:
|
|
cdegPacket = true;
|
|
|
|
break;
|
|
case 0x38:
|
|
cdmidiPacket = true;
|
|
|
|
break;
|
|
}
|
|
}
|
|
} |