diff --git a/Aaru.Decoders.csproj b/Aaru.Decoders.csproj
index 0f6174cf1..8642ed267 100644
--- a/Aaru.Decoders.csproj
+++ b/Aaru.Decoders.csproj
@@ -212,7 +212,7 @@
-
+
diff --git a/DVD/PRI.cs b/DVD/PRI.cs
index eba12d521..89357b4e8 100644
--- a/DVD/PRI.cs
+++ b/DVD/PRI.cs
@@ -30,7 +30,10 @@
// Copyright © 2011-2021 Natalia Portillo
// ****************************************************************************/
+using System;
using System.Diagnostics.CodeAnalysis;
+using System.Text;
+using Aaru.Helpers;
namespace Aaru.Decoders.DVD
{
@@ -52,6 +55,433 @@ namespace Aaru.Decoders.DVD
SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public static class PRI
{
+ public static PreRecordedInformation? Decode(byte[] response)
+ {
+ if(response == null)
+ return null;
+
+ if(response.Length < 67)
+ return null;
+
+ var pri = new PreRecordedInformation();
+ byte[] tmp;
+
+ pri.DataLength = (ushort)((response[0] << 8) + response[1]);
+ pri.Reserved1 = response[2];
+ pri.Reserved2 = response[3];
+
+ pri.FieldId1 = response[4];
+ pri.FieldId2 = response[12];
+ pri.FieldId3 = response[20];
+ pri.FieldId4 = response[28];
+ pri.FieldId5 = response[36];
+
+ if(pri.FieldId1 != 1 ||
+ pri.FieldId2 != 2 ||
+ pri.FieldId3 != 3 ||
+ pri.FieldId4 != 4 ||
+ pri.FieldId5 != 5)
+ return null;
+
+ pri.DiscApplicationCode = response[5];
+ pri.DiscPhysicalCode = response[6];
+ pri.LastAddressOfDataRecordableArea = (uint)((response[7] << 16) + (response[8] << 8) + response[9]);
+ pri.PartVersion = (byte)(response[10] >> 4);
+ pri.ExtensionCode = (byte)(response[10] & 0xF);
+ pri.Reserved3 = response[11];
+ pri.OPCSuggestedCode = response[13];
+ pri.WaveLengthCode = response[14];
+
+ pri.WriteStrategyCode =
+ (uint)((response[15] << 24) + (response[16] << 16) + (response[17] << 8) + response[18]);
+
+ pri.Reserved4 = response[19];
+ pri.ManufacturerId1 = new byte[6];
+ pri.Reserved5 = response[27];
+ pri.ManufacturerId2 = new byte[6];
+ pri.Reserved6 = response[35];
+ pri.ManufacturerId3 = new byte[6];
+ pri.Reserved7 = response[43];
+ pri.Reserved8 = new byte[response.Length - 44];
+
+ Array.Copy(response, 21, pri.ManufacturerId1, 0, 6);
+ Array.Copy(response, 29, pri.ManufacturerId2, 0, 6);
+ Array.Copy(response, 37, pri.ManufacturerId3, 0, 6);
+ Array.Copy(response, 44, pri.Reserved8, 0, pri.Reserved8.Length);
+
+ tmp = new byte[18];
+
+ Array.Copy(response, 21, tmp, 0, 6);
+ Array.Copy(response, 29, tmp, 6, 6);
+
+ // If RW or has part version or has extension code, 3rd manufacturer ID is a write strategy code
+ if((pri.DiscPhysicalCode & 0x2) > 0 ||
+ pri.PartVersion > 0 ||
+ pri.ExtensionCode > 0)
+ pri.WriteStrategyCode2 =
+ (uint)((response[37] << 24) + (response[38] << 16) + (response[39] << 8) + response[40]);
+ else
+ Array.Copy(response, 37, tmp, 12, 6);
+
+ pri.ManufacturerId = StringHandlers.CToString(tmp, Encoding.ASCII).Trim();
+
+ return pri;
+ }
+
+ public static string Prettify(PreRecordedInformation? pri)
+ {
+ if(pri == null)
+ return null;
+
+ PreRecordedInformation decoded = pri.Value;
+ var sb = new StringBuilder();
+
+ if((decoded.DiscApplicationCode & 0x40) > 0)
+ {
+ sb.AppendLine("Disc for unrestricted use.");
+
+ if((decoded.DiscApplicationCode & 0x3F) > 0)
+ sb.AppendFormat("Invalid purpose field with value {0}", decoded.DiscApplicationCode & 0x3F).
+ AppendLine();
+ else
+ sb.AppendLine("Consumer purpose disc for use in consumer purpose drives");
+ }
+ else
+ {
+ sb.AppendLine("Disc for restricted use.");
+
+ if((decoded.DiscApplicationCode & 0x3F) > 0)
+ sb.AppendFormat("Disc for use in special drives according with purpose value {0}",
+ decoded.DiscApplicationCode & 0x3F).AppendLine();
+ else
+ sb.AppendLine("General purpose disc for use in general purpose drives");
+ }
+
+ sb.AppendLine((decoded.DiscPhysicalCode & 0x80) > 0 ? "Disc track pitch is 0,74 μm"
+ : "Unknown track pitch");
+
+ sb.AppendLine((decoded.DiscPhysicalCode & 0x40) > 0 ? "Reference velocity is 3,49 m/s"
+ : "Unknown reference velocity");
+
+ sb.AppendLine((decoded.DiscPhysicalCode & 0x20) > 0 ? "Disc has 80mm diameter" : "Disc has 120mm diameter");
+
+ sb.AppendLine((decoded.DiscPhysicalCode & 0x10) > 0 ? "Disc reflectivity is between 18% and 30%"
+ : "Disc reflectivity is between 45% and 85%");
+
+ sb.AppendLine((decoded.DiscPhysicalCode & 0x04) > 0 ? "Dye is organic" : "Dye is phase change");
+
+ sb.AppendLine((decoded.DiscPhysicalCode & 0x02) > 0 ? "Disc is RW (rewritable)" : "Disc is R (recordable)");
+
+ sb.AppendLine((decoded.DiscPhysicalCode & 0x01) > 0 ? "Wavelength is 650nm" : "Unknown wavelength");
+
+ sb.AppendFormat("Last writable ECC block address: 0x{0:X6}", decoded.LastAddressOfDataRecordableArea).
+ AppendLine();
+
+ if(decoded.PartVersion > 0)
+ sb.AppendFormat("Part version {0}", decoded.PartVersion).AppendLine();
+
+ bool rw = (decoded.DiscPhysicalCode & 0x02) > 0;
+
+ if(rw)
+ {
+ if((decoded.OPCSuggestedCode & 0xF) > 0)
+ {
+ double recordingPower = 0;
+
+ switch(decoded.OPCSuggestedCode & 0xF)
+ {
+ case 1:
+ recordingPower = 7.0;
+
+ break;
+ case 2:
+ recordingPower = 7.5;
+
+ break;
+ case 3:
+ recordingPower = 8.0;
+
+ break;
+ case 4:
+ recordingPower = 8.5;
+
+ break;
+ case 5:
+ recordingPower = 9.0;
+
+ break;
+ case 6:
+ recordingPower = 9.5;
+
+ break;
+ case 7:
+ recordingPower = 10.0;
+
+ break;
+ case 8:
+ recordingPower = 10.5;
+
+ break;
+ case 9:
+ recordingPower = 11.0;
+
+ break;
+ case 10:
+ recordingPower = 11.5;
+
+ break;
+ case 11:
+ recordingPower = 12.0;
+
+ break;
+ case 12:
+ recordingPower = 12.5;
+
+ break;
+ case 13:
+ recordingPower = 13.0;
+
+ break;
+ case 14:
+ recordingPower = 13.5;
+
+ break;
+ case 15:
+ recordingPower = 14.0;
+
+ break;
+ }
+
+ sb.AppendFormat("Recommended recording power is {0} mW", recordingPower).AppendLine();
+ }
+ else
+ {
+ sb.AppendLine("Recording power is not specified");
+ }
+
+ if((decoded.WaveLengthCode & 0xF) > 0)
+ {
+ double erasingPower = 0;
+
+ switch(decoded.WaveLengthCode & 0xF)
+ {
+ case 1:
+ erasingPower = 0.38;
+
+ break;
+ case 2:
+ erasingPower = 0.40;
+
+ break;
+ case 3:
+ erasingPower = 0.42;
+
+ break;
+ case 4:
+ erasingPower = 0.44;
+
+ break;
+ case 5:
+ erasingPower = 0.46;
+
+ break;
+ case 6:
+ erasingPower = 0.48;
+
+ break;
+ case 7:
+ erasingPower = 0.50;
+
+ break;
+ case 8:
+ erasingPower = 0.52;
+
+ break;
+ case 9:
+ erasingPower = 0.54;
+
+ break;
+ case 10:
+ erasingPower = 0.56;
+
+ break;
+ case 11:
+ erasingPower = 0.58;
+
+ break;
+ case 12:
+ erasingPower = 0.60;
+
+ break;
+ case 13:
+ erasingPower = 0.62;
+
+ break;
+ case 14:
+ erasingPower = 0.64;
+
+ break;
+ case 15:
+ erasingPower = 0.66;
+
+ break;
+ }
+
+ sb.AppendFormat("Recommended erasing power ratio is {0} ε", erasingPower).AppendLine();
+ }
+ else
+ {
+ sb.AppendLine("Erasing power ratio is not specified");
+ }
+ }
+ else
+ {
+ if((decoded.OPCSuggestedCode & 0xF) > 0)
+ {
+ double recordingPower = 0;
+
+ switch(decoded.OPCSuggestedCode & 0xF)
+ {
+ case 1:
+ recordingPower = 6.0;
+
+ break;
+ case 2:
+ recordingPower = 6.5;
+
+ break;
+ case 3:
+ recordingPower = 7.0;
+
+ break;
+ case 4:
+ recordingPower = 7.5;
+
+ break;
+ case 5:
+ recordingPower = 8.0;
+
+ break;
+ case 6:
+ recordingPower = 8.5;
+
+ break;
+ case 7:
+ recordingPower = 9.0;
+
+ break;
+ case 8:
+ recordingPower = 9.5;
+
+ break;
+ case 9:
+ recordingPower = 10.0;
+
+ break;
+ case 10:
+ recordingPower = 10.5;
+
+ break;
+ case 11:
+ recordingPower = 11.0;
+
+ break;
+ case 12:
+ recordingPower = 11.5;
+
+ break;
+ case 13:
+ recordingPower = 12.0;
+
+ break;
+ }
+
+ sb.AppendFormat("Recommended recording power is {0} mW", recordingPower).AppendLine();
+ }
+
+ if(decoded.WaveLengthCode > 0)
+ {
+ int wavelength = 0;
+
+ switch(decoded.WaveLengthCode)
+ {
+ case 1:
+ wavelength = 645;
+
+ break;
+ case 2:
+ wavelength = 646;
+
+ break;
+ case 3:
+ wavelength = 647;
+
+ break;
+ case 4:
+ wavelength = 648;
+
+ break;
+ case 5:
+ wavelength = 649;
+
+ break;
+ case 6:
+ wavelength = 650;
+
+ break;
+ case 7:
+ wavelength = 651;
+
+ break;
+ case 8:
+ wavelength = 652;
+
+ break;
+ case 9:
+ wavelength = 653;
+
+ break;
+ case 10:
+ wavelength = 654;
+
+ break;
+ case 11:
+ wavelength = 655;
+
+ break;
+ case 12:
+ wavelength = 656;
+
+ break;
+ case 13:
+ wavelength = 657;
+
+ break;
+ case 14:
+ wavelength = 658;
+
+ break;
+ case 15:
+ wavelength = 659;
+
+ break;
+ case 16:
+ wavelength = 660;
+
+ break;
+ }
+
+ sb.AppendFormat("Recommended recording power is {0} mW", wavelength).AppendLine();
+ }
+ }
+
+ sb.AppendFormat("Disc manufacturer is {0}", decoded.ManufacturerId).AppendLine();
+
+ return sb.ToString();
+ }
+
+ public static string Prettify(byte[] response) => Prettify(Decode(response));
+
public struct PreRecordedInformation
{
/// Bytes 0 to 1 Data length
@@ -60,8 +490,53 @@ namespace Aaru.Decoders.DVD
public byte Reserved1;
/// Byte 3 Reserved
public byte Reserved2;
- /// Bytes 4 to end Pre-recorded Information in Lead-in for writable media
- public byte[] PRI;
+ /// Byte 4 == 1
+ public byte FieldId1;
+ /// Byte 5 disc application code
+ public byte DiscApplicationCode;
+ /// Byte 6 disc physical code
+ public byte DiscPhysicalCode;
+ /// Bytes 7 to 9 last address of data recordable area
+ public uint LastAddressOfDataRecordableArea;
+ /// Byte 10, bits 7 to 4 part version
+ public byte PartVersion;
+ /// Byte 10, bits 3 to 0 extension code
+ public byte ExtensionCode;
+ /// Byte 11 reserved
+ public byte Reserved3;
+ /// Byte 12 == 2
+ public byte FieldId2;
+ /// Byte 13 OPC suggested code
+ public byte OPCSuggestedCode;
+ /// Byte 14 wavelength code or second part of OPC suggested code
+ public byte WaveLengthCode;
+ /// Bytes 15 to 18 write strategy code
+ public uint WriteStrategyCode;
+ /// Byte 19 reserved
+ public byte Reserved4;
+ /// Byte 20 == 3
+ public byte FieldId3;
+ /// Bytes 21 to 26 first part of manufacturer ID
+ public byte[] ManufacturerId1;
+ /// Byte 27
+ public byte Reserved5;
+ /// Byte 28 == 4
+ public byte FieldId4;
+ /// Bytes 29 to 34 second part of manufacturer ID
+ public byte[] ManufacturerId2;
+ /// Byte 35 reserved
+ public byte Reserved6;
+ /// Byte 36 == 5
+ public byte FieldId5;
+ /// Bytes 37 to 42, third part of manufacturer code or write strategy code for RW and R later versions
+ public byte[] ManufacturerId3;
+ /// Byte 43 reserved
+ public byte Reserved7;
+ /// Bytes 44 to 68 reserved
+ public byte[] Reserved8;
+
+ public string ManufacturerId;
+ public uint WriteStrategyCode2;
}
}
}
\ No newline at end of file