mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
2539 lines
107 KiB
C#
2539 lines
107 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-2021 Natalia Portillo
|
|
// ****************************************************************************/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Aaru.Checksums;
|
|
using Aaru.CommonTypes;
|
|
using Aaru.Console;
|
|
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 DMI = Aaru.Decoders.Xbox.DMI;
|
|
|
|
// ReSharper disable JoinDeclarationAndInitializer
|
|
|
|
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";
|
|
|
|
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 =
|
|
{
|
|
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 =
|
|
{
|
|
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;
|
|
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;
|
|
|
|
sense = dev.GetConfiguration(out cmdBuf, out _, 0, MmcGetConfigurationRt.Current, dev.Timeout, out _);
|
|
|
|
if(!sense)
|
|
{
|
|
Features.SeparatedFeatures ftr = Features.Separate(cmdBuf);
|
|
|
|
AaruConsole.DebugWriteLine("Media-Info command", "GET CONFIGURATION current profile is {0:X4}h",
|
|
ftr.CurrentProfile);
|
|
|
|
switch(ftr.CurrentProfile)
|
|
{
|
|
case 0x0001:
|
|
mediaType = MediaType.GENERIC_HDD;
|
|
|
|
break;
|
|
case 0x0005:
|
|
mediaType = MediaType.CDMO;
|
|
|
|
break;
|
|
case 0x0008:
|
|
mediaType = MediaType.CD;
|
|
|
|
break;
|
|
case 0x0009:
|
|
mediaType = MediaType.CDR;
|
|
|
|
break;
|
|
case 0x000A:
|
|
mediaType = MediaType.CDRW;
|
|
|
|
break;
|
|
case 0x0010:
|
|
mediaType = MediaType.DVDROM;
|
|
|
|
break;
|
|
case 0x0011:
|
|
mediaType = MediaType.DVDR;
|
|
|
|
break;
|
|
case 0x0012:
|
|
mediaType = MediaType.DVDRAM;
|
|
|
|
break;
|
|
case 0x0013:
|
|
case 0x0014:
|
|
mediaType = MediaType.DVDRW;
|
|
|
|
break;
|
|
case 0x0015:
|
|
case 0x0016:
|
|
mediaType = MediaType.DVDRDL;
|
|
|
|
break;
|
|
case 0x0017:
|
|
mediaType = MediaType.DVDRWDL;
|
|
|
|
break;
|
|
case 0x0018:
|
|
mediaType = MediaType.DVDDownload;
|
|
|
|
break;
|
|
case 0x001A:
|
|
mediaType = MediaType.DVDPRW;
|
|
|
|
break;
|
|
case 0x001B:
|
|
mediaType = MediaType.DVDPR;
|
|
|
|
break;
|
|
case 0x0020:
|
|
mediaType = MediaType.DDCD;
|
|
|
|
break;
|
|
case 0x0021:
|
|
mediaType = MediaType.DDCDR;
|
|
|
|
break;
|
|
case 0x0022:
|
|
mediaType = MediaType.DDCDRW;
|
|
|
|
break;
|
|
case 0x002A:
|
|
mediaType = MediaType.DVDPRWDL;
|
|
|
|
break;
|
|
case 0x002B:
|
|
mediaType = MediaType.DVDPRDL;
|
|
|
|
break;
|
|
case 0x0040:
|
|
mediaType = MediaType.BDROM;
|
|
|
|
break;
|
|
case 0x0041:
|
|
case 0x0042:
|
|
mediaType = MediaType.BDR;
|
|
|
|
break;
|
|
case 0x0043:
|
|
mediaType = MediaType.BDRE;
|
|
|
|
break;
|
|
case 0x0050:
|
|
mediaType = MediaType.HDDVDROM;
|
|
|
|
break;
|
|
case 0x0051:
|
|
mediaType = MediaType.HDDVDR;
|
|
|
|
break;
|
|
case 0x0052:
|
|
mediaType = MediaType.HDDVDRAM;
|
|
|
|
break;
|
|
case 0x0053:
|
|
mediaType = MediaType.HDDVDRW;
|
|
|
|
break;
|
|
case 0x0058:
|
|
mediaType = MediaType.HDDVDRDL;
|
|
|
|
break;
|
|
case 0x005A:
|
|
mediaType = MediaType.HDDVDRWDL;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(decodedToc?.TrackDescriptors.Any(t => t.SessionNumber == 2) == true)
|
|
secondSessionFirstTrack = decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == 2).
|
|
Min(t => t.POINT);
|
|
|
|
if(mediaType == MediaType.CD ||
|
|
mediaType == MediaType.CDROMXA ||
|
|
mediaType == 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 == MediaType.CD ||
|
|
mediaType == 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.POINT == 0xA0 && t.ADR == 1);
|
|
|
|
if(a0Track.POINT == 0xA0)
|
|
switch(a0Track.PSEC)
|
|
{
|
|
case 0x10:
|
|
AaruConsole.DebugWriteLine("Media detection", "TOC says disc type is CD-i.");
|
|
mediaType = MediaType.CDI;
|
|
|
|
break;
|
|
case 0x20:
|
|
AaruConsole.DebugWriteLine("Media detection", "TOC says disc type is CD-ROM XA.");
|
|
mediaType = MediaType.CDROMXA;
|
|
|
|
break;
|
|
}
|
|
|
|
foreach(FullTOC.TrackDataDescriptor track in
|
|
decodedToc.Value.TrackDescriptors.Where(t => t.POINT > 0 && t.POINT <= 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)
|
|
{
|
|
if(hasDataTrack &&
|
|
hasAudioTrack &&
|
|
allFirstSessionTracksAreAudio &&
|
|
sessions == 2)
|
|
{
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Disc has audio and data tracks, two sessions, and all data tracks are in second session, setting as CD+.");
|
|
|
|
mediaType = MediaType.CDPLUS;
|
|
}
|
|
|
|
if(!hasDataTrack &&
|
|
hasAudioTrack &&
|
|
sessions == 1)
|
|
{
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Disc has only audio tracks in a single session, setting as CD Digital Audio.");
|
|
|
|
mediaType = MediaType.CDDA;
|
|
}
|
|
|
|
if(hasDataTrack &&
|
|
!hasAudioTrack &&
|
|
sessions == 1)
|
|
{
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Disc has only data tracks in a single session, setting as CD-ROM.");
|
|
|
|
mediaType = MediaType.CDROM;
|
|
}
|
|
|
|
if(hasVideoTrack &&
|
|
!hasDataTrack &&
|
|
sessions == 1)
|
|
{
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Disc has video tracks in a single session, setting as CD Video.");
|
|
|
|
mediaType = MediaType.CDV;
|
|
}
|
|
}
|
|
|
|
if((mediaType == MediaType.CD || mediaType == MediaType.CDROM) && hasDataTrack)
|
|
{
|
|
foreach(uint startAddress in decodedToc.Value.TrackDescriptors.
|
|
Where(t => t.POINT > 0 && t.POINT <= 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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Disc has a mode 2 data track, setting as 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 == 1);
|
|
|
|
if(firstTrack?.POINT == 1)
|
|
{
|
|
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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Disc has a hidden CD-i track in track 1's pregap, setting as 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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Disc has a hidden CD-i track in track 1's pregap, setting as 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 == MediaType.BDROM ||
|
|
mediaType == 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)
|
|
{
|
|
switch(pfi.Value.DiskCategory)
|
|
{
|
|
case DiskCategory.DVDPR:
|
|
mediaType = MediaType.DVDPR;
|
|
|
|
break;
|
|
case DiskCategory.DVDPRDL:
|
|
mediaType = MediaType.DVDPRDL;
|
|
|
|
break;
|
|
case DiskCategory.DVDPRW:
|
|
mediaType = MediaType.DVDPRW;
|
|
|
|
break;
|
|
case DiskCategory.DVDPRWDL:
|
|
mediaType = MediaType.DVDPRWDL;
|
|
|
|
break;
|
|
case DiskCategory.DVDR:
|
|
mediaType = pfi.Value.PartVersion >= 6 ? MediaType.DVDRDL : MediaType.DVDR;
|
|
|
|
break;
|
|
case DiskCategory.DVDRAM:
|
|
mediaType = MediaType.DVDRAM;
|
|
|
|
break;
|
|
case DiskCategory.DVDRW:
|
|
mediaType = pfi.Value.PartVersion >= 15 ? MediaType.DVDRWDL : MediaType.DVDRW;
|
|
|
|
break;
|
|
case DiskCategory.HDDVDR:
|
|
mediaType = MediaType.HDDVDR;
|
|
|
|
break;
|
|
case DiskCategory.HDDVDRAM:
|
|
mediaType = MediaType.HDDVDRAM;
|
|
|
|
break;
|
|
case DiskCategory.HDDVDROM:
|
|
mediaType = MediaType.HDDVDROM;
|
|
|
|
break;
|
|
case DiskCategory.HDDVDRW:
|
|
mediaType = MediaType.HDDVDRW;
|
|
|
|
break;
|
|
case DiskCategory.Nintendo:
|
|
mediaType = pfi.Value.DiscSize == DVDSize.Eighty ? MediaType.GOD
|
|
: MediaType.WOD;
|
|
|
|
break;
|
|
case DiskCategory.UMD:
|
|
mediaType = MediaType.UMD;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0,
|
|
MmcDiscStructureFormat.DiscManufacturingInformation, 0,
|
|
dev.Timeout, out _);
|
|
|
|
if(!sense)
|
|
{
|
|
if(DMI.IsXbox(cmdBuf))
|
|
{
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"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 == 25063 || // Locked (or non compatible drive)
|
|
blocks == 4229664 || // Xtreme unlock
|
|
blocks == 4246304) // Wxripper unlock
|
|
{
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Xbox 360 DMI with {0} blocks, setting disc type to Xbox 360 Game Disc 3 (XGD3).");
|
|
|
|
mediaType = MediaType.XGD3;
|
|
|
|
return;
|
|
}
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"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
|
|
{
|
|
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.Substring(0, name.Length - 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
|
|
{
|
|
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.Substring(0, name.Length - 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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Photo CD description file, setting disc type to Photo CD.");
|
|
|
|
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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Mega/Sega CD IP.BIN, setting disc type to Mega CD.");
|
|
|
|
return;
|
|
}
|
|
|
|
if(Saturn.DecodeIPBin(sector0).HasValue)
|
|
{
|
|
mediaType = MediaType.SATURNCD;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Sega Saturn IP.BIN, setting disc type to Saturn CD.");
|
|
|
|
return;
|
|
}
|
|
|
|
// Are GDR detectable ???
|
|
if(Dreamcast.DecodeIPBin(sector0).HasValue)
|
|
{
|
|
mediaType = MediaType.GDROM;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Sega Dreamcast IP.BIN, setting disc type to GD-ROM.");
|
|
|
|
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 _);
|
|
|
|
AaruConsole.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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Sony PlayStation 2 boot sectors, setting disc type to PS2 CD.");
|
|
|
|
goto hasPs2CdBoot;
|
|
}
|
|
}
|
|
|
|
if(sector0 != null)
|
|
{
|
|
byte[] syncBytes = new byte[7];
|
|
Array.Copy(sector0, 0, syncBytes, 0, 7);
|
|
|
|
if(_operaId.SequenceEqual(syncBytes))
|
|
{
|
|
mediaType = MediaType.ThreeDO;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Opera filesystem, setting disc type to 3DO.");
|
|
|
|
return;
|
|
}
|
|
|
|
if(_fmTownsBootId.SequenceEqual(syncBytes))
|
|
{
|
|
mediaType = MediaType.FMTOWNS;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found FM-Towns boot, setting disc type to FM-Towns.");
|
|
|
|
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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Playdia copyright, setting disc type to Playdia.");
|
|
|
|
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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found PC-Engine CD signature, setting disc type to Super CD-ROM².");
|
|
|
|
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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found PC-FX copyright, setting disc type to PC-FX.");
|
|
|
|
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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Atari signature, setting disc type to Jaguar CD.");
|
|
|
|
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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Sega Dreamcast IP.BIN on second session, setting disc type to MilCD.");
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// TODO: Detect black and white VideoNow
|
|
// TODO: Detect VideoNow XP
|
|
if(IsVideoNowColor(videoNowColorFrame))
|
|
{
|
|
mediaType = MediaType.VideoNowColor;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found VideoNow! Color frame, setting disc type to VideoNow Color.");
|
|
|
|
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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found enhanced graphics RW packet, setting disc type to CD+EG.");
|
|
|
|
return;
|
|
}
|
|
|
|
if(cdg)
|
|
{
|
|
mediaType = MediaType.CDG;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found graphics RW packet, setting disc type to CD+G.");
|
|
|
|
return;
|
|
}
|
|
|
|
if(cdmidi)
|
|
{
|
|
mediaType = MediaType.CDMIDI;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found MIDI RW packet, setting disc type to CD+MIDI.");
|
|
|
|
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
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(isoSector.Length < 2048)
|
|
return;
|
|
|
|
List<string> rootEntries = new List<string>();
|
|
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.Substring(0, name.Length - 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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found CD32.TM file in root, setting disc type to Amiga CD32.");
|
|
|
|
return;
|
|
}
|
|
|
|
if(rootEntries.Contains("CDTV.TM"))
|
|
{
|
|
mediaType = MediaType.CDTV;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found CDTV.TM file in root, setting disc type to Commodore CDTV.");
|
|
|
|
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
|
|
{
|
|
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 < 1 ||
|
|
split[2].Length > 8)
|
|
{
|
|
correctNeoGeoCd = false;
|
|
|
|
break;
|
|
}
|
|
|
|
try
|
|
{
|
|
_ = Convert.ToUInt32(split[2], 16);
|
|
}
|
|
catch
|
|
{
|
|
correctNeoGeoCd = false;
|
|
|
|
break;
|
|
}
|
|
|
|
lineNumber++;
|
|
}
|
|
|
|
if(correctNeoGeoCd)
|
|
{
|
|
mediaType = MediaType.NeoGeoCD;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found correct IPL.TXT file in root, setting disc type to Neo Geo CD.");
|
|
|
|
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
|
|
{
|
|
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.Substring(0, name.Length - 2);
|
|
|
|
if(name == "INFO.VCD" ||
|
|
name == "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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Video CD description file, setting disc type to Video CD.");
|
|
|
|
return;
|
|
case "SUPERVCD":
|
|
mediaType = MediaType.SVCD;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Super Video CD description file, setting disc type to Super Video CD.");
|
|
|
|
break;
|
|
case "HQ-VCD":
|
|
mediaType = MediaType.CVD;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found China Video Disc description file, setting disc type to China Video Disc.");
|
|
|
|
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
|
|
{
|
|
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.Substring(0, name.Length - 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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Photo CD description file, setting disc type to Photo CD.");
|
|
|
|
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
|
|
{
|
|
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.Substring(11);
|
|
|
|
if(ps1BootFile.StartsWith('\\'))
|
|
ps1BootFile = ps1BootFile.Substring(1);
|
|
|
|
if(ps1BootFile.EndsWith(";1", StringComparison.InvariantCultureIgnoreCase))
|
|
ps1BootFile = ps1BootFile.Substring(0, ps1BootFile.Length - 2);
|
|
|
|
break;
|
|
}
|
|
|
|
if(line.StartsWith("BOOT2=cdrom0:", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
ps2BootFile = line.Substring(13);
|
|
|
|
if(ps2BootFile.StartsWith('\\'))
|
|
ps2BootFile = ps2BootFile.Substring(1);
|
|
|
|
if(ps2BootFile.EndsWith(";1", StringComparison.InvariantCultureIgnoreCase))
|
|
ps2BootFile = ps2BootFile.Substring(0, ps2BootFile.Length - 2);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(ps1BootFile != null &&
|
|
rootEntries.Contains(ps1BootFile.ToUpperInvariant()))
|
|
{
|
|
mediaType = MediaType.PS1CD;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found correct SYSTEM.CNF file in root pointing to existing file in root, setting disc type to PlayStation CD.");
|
|
}
|
|
|
|
if(ps2BootFile != null &&
|
|
rootEntries.Contains(ps2BootFile.ToUpperInvariant()))
|
|
{
|
|
mediaType = MediaType.PS2CD;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found correct SYSTEM.CNF file in root pointing to existing file in root, setting disc type to PlayStation 2 CD.");
|
|
}
|
|
}
|
|
}
|
|
|
|
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 _);
|
|
|
|
AaruConsole.DebugWriteLine("Media-info Command", "PlayStation 2 boot sectors SHA256: {0}",
|
|
ps2BootSectorsHash);
|
|
|
|
if(ps2BootSectorsHash == PS2_PAL_HASH ||
|
|
ps2BootSectorsHash == PS2_NTSC_HASH ||
|
|
ps2BootSectorsHash == PS2_JAPANESE_HASH)
|
|
{
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Sony PlayStation 2 boot sectors, setting disc type to PS2 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:
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Sony PlayStation 3 boot sectors, setting disc type to PS3 Blu-ray.");
|
|
|
|
mediaType = MediaType.PS3BD;
|
|
|
|
break;
|
|
case MediaType.DVDROM:
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Sony PlayStation 3 boot sectors, setting disc type to PS3 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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Sony PlayStation 4 boot sectors, setting disc type to PS4 Blu-ray.");
|
|
}
|
|
}
|
|
|
|
if(blurayDi != null &&
|
|
blurayDi?.Units?.Length > 0 &&
|
|
blurayDi?.Units[0].DiscTypeIdentifier != null)
|
|
{
|
|
string blurayType = StringHandlers.CToString(blurayDi?.Units[0].DiscTypeIdentifier);
|
|
|
|
switch(blurayType)
|
|
{
|
|
case "XG4":
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Blu-ray type set to \"XG4\", setting disc type to Xbox One Disc (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;
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Found Sony PlayStation 5 boot sectors, setting disc type to PS5 Ultra HD Blu-ray.");
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
AaruConsole.DebugWriteLine("Media detection",
|
|
"Blu-ray type set to \"BDU\", setting disc type to Ultra HD Blu-ray.");
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
} |