diff --git a/ChangeLog b/ChangeLog index 9f476ed..a15f93f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2017-05-23 Natalia Portillo + + * SS.cs: + * DiscImageChef.Decoders.csproj: Added support for Xbox + security sector. + 2017-05-23 Natalia Portillo * Inquiry.cs: Detect Kreon firmware. diff --git a/DiscImageChef.Decoders.csproj b/DiscImageChef.Decoders.csproj index f8ac4c0..a48b28c 100644 --- a/DiscImageChef.Decoders.csproj +++ b/DiscImageChef.Decoders.csproj @@ -108,6 +108,7 @@ + diff --git a/Xbox/SS.cs b/Xbox/SS.cs new file mode 100644 index 0000000..50262fe --- /dev/null +++ b/Xbox/SS.cs @@ -0,0 +1,448 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : SS.cs +// Author(s) : Natalia Portillo +// +// Component : Component +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2017 Natalia Portillo +// ****************************************************************************/ +using System; +using System.Text; +using DiscImageChef.Decoders.DVD; + +namespace DiscImageChef.Decoders.Xbox +{ + public static class SS + { + public struct SecuritySector + { + /// + /// Byte 0, bits 7 to 4 + /// Disk category field + /// + public DiskCategory DiskCategory; + /// + /// Byte 0, bits 3 to 0 + /// Media version + /// + public byte PartVersion; + /// + /// Byte 1, bits 7 to 4 + /// 120mm if 0, 80mm if 1. If UMD (60mm) 0 also. Reserved rest of values + /// + public DVDSize DiscSize; + /// + /// Byte 1, bits 3 to 0 + /// Maximum data rate + /// + public MaximumRateField MaximumRate; + /// + /// Byte 2, bit 7 + /// Reserved + /// + public bool Reserved3; + /// + /// Byte 2, bits 6 to 5 + /// Number of layers + /// + public byte Layers; + /// + /// Byte 2, bit 4 + /// Track path + /// + public bool TrackPath; + /// + /// Byte 2, bits 3 to 0 + /// Layer type + /// + public LayerTypeFieldMask LayerType; + /// + /// Byte 3, bits 7 to 4 + /// Linear density field + /// + public LinearDensityField LinearDensity; + /// + /// Byte 3, bits 3 to 0 + /// Track density field + /// + public TrackDensityField TrackDensity; + /// + /// Bytes 4 to 7 + /// PSN where Data Area starts + /// + public uint DataAreaStartPSN; + /// + /// Bytes 8 to 11 + /// PSN where Data Area ends + /// + public uint DataAreaEndPSN; + /// + /// Bytes 12 to 15 + /// PSN where Data Area ends in Layer 0 + /// + public uint Layer0EndPSN; + + /// + /// Byte 27 + /// Always 0x06 on XGD3 + /// + public byte Unknown1; + /// + /// Bytes 256 to 283 + /// Unknown, XGD2 and XGD3 + /// + public byte[] Unknown2; + /// + /// Bytes 284 to 719 + /// Unknown, XGD3 + /// + public byte[] Unknown3; + /// + /// Bytes 720 to 723 + /// Unknown + /// + public byte[] Unknown4; + /// + /// Bytes 724 to 767 + /// Unknown, XGD3 + /// + public byte[] Unknown5; + /// + /// Byte 768 + /// Version of challenge table + /// + public byte ChallengeTableVersion; + /// + /// Byte 769 + /// Number of challenge entries + /// + public byte NoChallengeEntries; + /// + /// Bytes 770 to 1022 + /// Unknown + /// + public ChallengeEntry[] ChallengeEntries; + /// + /// Byte 1023 + /// Unknown + /// + public byte Unknown6; + /// + /// Bytes 1052 to 1099 + /// Unknown, XGD1 only + /// + public byte[] Unknown7; + /// + /// Bytes 1120 to 1135 + /// Unknown, XGD2 and XGD3 + /// + public byte[] Unknown8; + /// + /// Bytes 1180 to 1195 + /// Unknown + /// + public byte[] Unknown9; + /// + /// Bytes 1208 to 1511 + /// Unknown + /// + public byte[] Unknown10; + /// + /// Bytes 1528 to 1632 + /// + public byte[] Unknown11; + /// + /// Bytes 1633 to 1839 + /// Security extents, 23 entries of 9 bytes + /// + public SecuritySectorExtent[] Extents; + /// + /// Bytes 1840 to 2047 + /// Copy of the security extents, 23 entries of 9 bytes + /// + public SecuritySectorExtent[] ExtentsCopy; + } + + public struct SecuritySectorExtent + { + /// + /// Bytes 0 to 2 + /// Unknown + /// + public uint Unknown; + /// + /// Bytes 3 to 5 + /// Start PSN of this security extent + /// + public uint StartPSN; + /// + /// Bytes 6 to 8 + /// End PSN of this security extent + /// + public uint EndPSN; + } + + public struct ChallengeEntry + { + public byte Level; + public byte ChallengeId; + public uint ChallengeValue; + public byte ResponseModifier; + public uint ResponseValue; + } + + public static SecuritySector? Decode(byte[] response) + { + if(response == null) + return null; + + if(response.Length < 2048) + return null; + + SecuritySector ss = new SecuritySector(); + byte[] tmp; + + // Common + ss.DiskCategory = (DiskCategory)((response[0] & 0xF0) >> 4); + ss.PartVersion = (byte)(response[0] & 0x0F); + ss.DiscSize = (DVDSize)((response[1] & 0xF0) >> 4); + ss.MaximumRate = (MaximumRateField)(response[1] & 0x0F); + ss.Reserved3 |= (response[2] & 0x80) == 0x80; + ss.Layers = (byte)((response[2] & 0x60) >> 5); + ss.TrackPath |= (response[2] & 0x08) == 0x08; + ss.LayerType = (LayerTypeFieldMask)(response[2] & 0x07); + ss.LinearDensity = (LinearDensityField)((response[3] & 0xF0) >> 4); + ss.TrackDensity = (TrackDensityField)(response[3] & 0x0F); + ss.DataAreaStartPSN = (uint)((response[4] << 24) + (response[5] << 16) + (response[6] << 8) + response[7]); + ss.DataAreaEndPSN = (uint)((response[8] << 24) + (response[9] << 16) + (response[10] << 8) + response[11]); + ss.Layer0EndPSN = (uint)((response[12] << 24) + (response[13] << 16) + (response[14] << 8) + response[15]); + + ss.Unknown1 = response[27]; + ss.Unknown2 = new byte[28]; + Array.Copy(response, 256, ss.Unknown2, 0, 28); + ss.Unknown3 = new byte[436]; + Array.Copy(response, 284, ss.Unknown3, 0, 436); + ss.Unknown4 = new byte[4]; + Array.Copy(response, 720, ss.Unknown4, 0, 4); + ss.Unknown5 = new byte[43]; + Array.Copy(response, 724, ss.Unknown5, 0, 43); + ss.ChallengeTableVersion = response[768]; + ss.NoChallengeEntries = response[769]; + ss.ChallengeEntries = new ChallengeEntry[23]; + for(int i = 0; i < 23; i++) + { + ss.ChallengeEntries[i] = new ChallengeEntry(); + ss.ChallengeEntries[i].Level = response[770 + i * 11 + 0]; + ss.ChallengeEntries[i].ChallengeId = response[770 + i * 11 + 1]; + ss.ChallengeEntries[i].ChallengeValue = (uint)((response[770 + i * 11 + 2] << 24) + (response[770 + i * 11 + 3] << 16) + (response[770 + i * 11 + 4] << 8) + response[770 + i * 11 + 5]); + ss.ChallengeEntries[i].ResponseModifier = response[770 + i * 11 + 6]; + ss.ChallengeEntries[i].ResponseValue = (uint)((response[770 + i * 11 + 7] << 24) + (response[770 + i * 11 + 8] << 16) + (response[770 + i * 11 + 9] << 8) + response[770 + i * 11 + 10]); + } + ss.Unknown6 = response[1023]; + ss.Unknown7 = new byte[48]; + Array.Copy(response, 1052, ss.Unknown7, 0, 48); + ss.Unknown8 = new byte[16]; + Array.Copy(response, 1120, ss.Unknown8, 0, 16); + ss.Unknown9 = new byte[16]; + Array.Copy(response, 1180, ss.Unknown9, 0, 16); + ss.Unknown10 = new byte[303]; + Array.Copy(response, 1208, ss.Unknown10, 0, 303); + ss.Unknown11 = new byte[104]; + Array.Copy(response, 1528, ss.Unknown11, 0, 104); + ss.Extents = new SecuritySectorExtent[23]; + for(int i = 0; i < 23; i++) + { + ss.Extents[i] = new SecuritySectorExtent(); + ss.Extents[i].Unknown = (uint)((response[1633 + i * 9 + 0] << 16) + (response[1633 + i * 9 + 1] << 8) + response[1633 + i * 9 + 2]); + ss.Extents[i].StartPSN = (uint)((response[1633 + i * 9 + 3] << 16) + (response[1633 + i * 9 + 4] << 8) + response[1633 + i * 9 + 5]); + ss.Extents[i].EndPSN = (uint)((response[1633 + i * 9 + 6] << 16) + (response[1633 + i * 9 + 7] << 8) + response[1633 + i * 9 + 8]); + } + ss.ExtentsCopy = new SecuritySectorExtent[23]; + for(int i = 0; i < 23; i++) + { + ss.ExtentsCopy[i] = new SecuritySectorExtent(); + ss.ExtentsCopy[i].Unknown = (uint)((response[1840 + i * 9 + 0] << 16) + (response[1840 + i * 9 + 1] << 8) + response[1840 + i * 9 + 2]); + ss.ExtentsCopy[i].StartPSN = (uint)((response[1840 + i * 9 + 3] << 16) + (response[1840 + i * 9 + 4] << 8) + response[1840 + i * 9 + 5]); + ss.ExtentsCopy[i].EndPSN = (uint)((response[1840 + i * 9 + 6] << 16) + (response[1840 + i * 9 + 7] << 8) + response[1840 + i * 9 + 8]); + } + return ss; + } + + public static string Prettify(SecuritySector? ss) + { + if(ss == null) + return null; + + SecuritySector decoded = ss.Value; + StringBuilder sb = new StringBuilder(); + + string sizeString; + switch(decoded.DiscSize) + { + case DVDSize.Eighty: + sizeString = "80mm"; + break; + case DVDSize.OneTwenty: + sizeString = "120mm"; + break; + default: + sizeString = string.Format("unknown size identifier {0}", decoded.DiscSize); + break; + } + + string categorySentence = "Disc is a {0} {1} version {2}"; + + switch(decoded.DiskCategory) + { + case DiskCategory.DVDPRWDL: + sb.AppendFormat(categorySentence, sizeString, "Xbox Game Disc", decoded.PartVersion).AppendLine(); + break; + case DiskCategory.DVDPRDL: + sb.AppendFormat(categorySentence, sizeString, "Xbox 360 Game Disc", decoded.PartVersion).AppendLine(); + break; + default: + sb.AppendFormat(categorySentence, sizeString, "unknown disc type", decoded.PartVersion).AppendLine(); + break; + } + + switch(decoded.MaximumRate) + { + case MaximumRateField.TwoMbps: + sb.AppendLine("Disc maximum transfer rate is 2,52 Mbit/sec."); + break; + case MaximumRateField.FiveMbps: + sb.AppendLine("Disc maximum transfer rate is 5,04 Mbit/sec."); + break; + case MaximumRateField.TenMbps: + sb.AppendLine("Disc maximum transfer rate is 10,08 Mbit/sec."); + break; + case MaximumRateField.TwentyMbps: + sb.AppendLine("Disc maximum transfer rate is 20,16 Mbit/sec."); + break; + case MaximumRateField.ThirtyMbps: + sb.AppendLine("Disc maximum transfer rate is 30,24 Mbit/sec."); + break; + case MaximumRateField.Unspecified: + sb.AppendLine("Disc maximum transfer rate is unspecified."); + break; + default: + sb.AppendFormat("Disc maximum transfer rate is specified by unknown key {0}", decoded.MaximumRate).AppendLine(); + break; + } + + sb.AppendFormat("Disc has {0} layers", decoded.Layers + 1).AppendLine(); + if(decoded.TrackPath && decoded.Layers == 1) + sb.AppendLine("Layers are in parallel track path"); + else if(!decoded.TrackPath && decoded.Layers == 1) + sb.AppendLine("Layers are in opposite track path"); + + switch(decoded.LinearDensity) + { + case LinearDensityField.TwoSix: + sb.AppendLine("Pitch size is 0,267 μm/bit"); + break; + case LinearDensityField.TwoNine: + sb.AppendLine("Pitch size is 0,147 μm/bit"); + break; + case LinearDensityField.FourZero: + sb.AppendLine("Pitch size is between 0,409 μm/bit and 0,435 μm/bit"); + break; + case LinearDensityField.TwoEight: + sb.AppendLine("Pitch size is between 0,140 μm/bit and 0,148 μm/bit"); + break; + case LinearDensityField.OneFive: + sb.AppendLine("Pitch size is 0,153 μm/bit"); + break; + case LinearDensityField.OneThree: + sb.AppendLine("Pitch size is between 0,130 μm/bit and 0,140 μm/bit"); + break; + case LinearDensityField.ThreeFive: + sb.AppendLine("Pitch size is 0,353 μm/bit"); + break; + default: + sb.AppendFormat("Unknown pitch size key {0}", decoded.LinearDensity).AppendLine(); + break; + } + + switch(decoded.TrackDensity) + { + case TrackDensityField.Seven: + sb.AppendLine("Track size is 0,74 μm"); + break; + case TrackDensityField.Eight: + sb.AppendLine("Track size is 0,80 μm"); + break; + case TrackDensityField.Six: + sb.AppendLine("Track size is 0,615 μm"); + break; + case TrackDensityField.Four: + sb.AppendLine("Track size is 0,40 μm"); + break; + case TrackDensityField.Three: + sb.AppendLine("Track size is 0,34 μm"); + break; + default: + sb.AppendFormat("Unknown track size key {0}", decoded.LinearDensity).AppendLine(); + break; + } + + if(decoded.DataAreaStartPSN > 0) + { + if(decoded.DataAreaEndPSN > 0) + { + sb.AppendFormat("Data area starts at PSN {0:X}h", decoded.DataAreaStartPSN).AppendLine(); + sb.AppendFormat("Data area ends at PSN {0:X}h", decoded.DataAreaEndPSN).AppendLine(); + if(decoded.Layers == 1 && !decoded.TrackPath) + sb.AppendFormat("Layer 0 ends at PSN {0:X}h", decoded.Layer0EndPSN).AppendLine(); + } + else + sb.AppendLine("Disc is empty"); + } + else + sb.AppendLine("Disc is empty"); + + sb.AppendLine("Challenges:"); + foreach(ChallengeEntry entry in decoded.ChallengeEntries) + { + sb.AppendFormat("\tChallenge ID: {0}", entry.ChallengeId).AppendLine(); + sb.AppendFormat("\tChallenge level: {0}", entry.Level).AppendLine(); + sb.AppendFormat("\tChallenge value: 0x{0:X8}", entry.ChallengeValue).AppendLine(); + sb.AppendFormat("\tResponse modifier: {0}", entry.ResponseModifier).AppendLine(); + sb.AppendFormat("\tResponse value: 0x{0:X8}", entry.ResponseValue).AppendLine(); + } + + for(int i = 0; i < 16; i++) + sb.AppendFormat("Extent starts at PSN {0:X6}h and ends at PSN {1:X6}h", decoded.Extents[i].StartPSN, decoded.Extents[i].EndPSN).AppendLine(); + + return sb.ToString(); + } + + public static string Prettify(byte[] response) + { + return Prettify(Decode(response)); + } + } +}