// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : SS.cs // Author(s) : Natalia Portillo // // Component : Device structures decoders. // // --[ Description ] ---------------------------------------------------------- // // Decodes Xbox security sectors // // --[ 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-2025 Natalia Portillo // ****************************************************************************/ using System; using System.Diagnostics.CodeAnalysis; using System.Text; using Aaru.Decoders.DVD; namespace Aaru.Decoders.Xbox; [SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "MemberCanBeInternal")] [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] [SuppressMessage("ReSharper", "NotAccessedField.Global")] public static class SS { public static SecuritySector? Decode(byte[] response) { if(response == null) return null; if(response.Length < 2048) return null; var ss = new SecuritySector { DiskCategory = (DiskCategory)((response[0] & 0xF0) >> 4), PartVersion = (byte)(response[0] & 0x0F), DiscSize = (DVDSize)((response[1] & 0xF0) >> 4), MaximumRate = (MaximumRateField)(response[1] & 0x0F), Reserved3 = (response[2] & 0x80) == 0x80, Layers = (byte)((response[2] & 0x60) >> 5), TrackPath = (response[2] & 0x08) == 0x08, LayerType = (LayerTypeFieldMask)(response[2] & 0x07), LinearDensity = (LinearDensityField)((response[3] & 0xF0) >> 4), TrackDensity = (TrackDensityField)(response[3] & 0x0F), DataAreaStartPSN = (uint)((response[4] << 24) + (response[5] << 16) + (response[6] << 8) + response[7]), DataAreaEndPSN = (uint)((response[8] << 24) + (response[9] << 16) + (response[10] << 8) + response[11]), Layer0EndPSN = (uint)((response[12] << 24) + (response[13] << 16) + (response[14] << 8) + response[15]), Unknown1 = response[27], Unknown2 = new byte[28], Unknown3 = new byte[436], Unknown4 = new byte[4], Unknown5 = new byte[43], ChallengeTableVersion = response[768], NoChallengeEntries = response[769], ChallengeEntries = new ChallengeEntry[23], Unknown6 = response[1023], Unknown7 = new byte[48], Unknown8 = new byte[16], Unknown9 = new byte[16], Unknown10 = new byte[303], Unknown11 = new byte[104], Extents = new SecuritySectorExtent[23], ExtentsCopy = new SecuritySectorExtent[23] }; Array.Copy(response, 256, ss.Unknown2, 0, 28); Array.Copy(response, 284, ss.Unknown3, 0, 436); Array.Copy(response, 720, ss.Unknown4, 0, 4); Array.Copy(response, 724, ss.Unknown5, 0, 43); for(var i = 0; i < 23; i++) { ss.ChallengeEntries[i] = new ChallengeEntry { Level = response[770 + i * 11 + 0], ChallengeId = response[770 + i * 11 + 1], 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]), ResponseModifier = response[770 + i * 11 + 6], 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]) }; } Array.Copy(response, 1052, ss.Unknown7, 0, 48); Array.Copy(response, 1120, ss.Unknown8, 0, 16); Array.Copy(response, 1180, ss.Unknown9, 0, 16); Array.Copy(response, 1208, ss.Unknown10, 0, 303); Array.Copy(response, 1528, ss.Unknown11, 0, 104); for(var i = 0; i < 23; i++) { ss.Extents[i] = new SecuritySectorExtent { Unknown = (uint)((response[1633 + i * 9 + 0] << 16) + (response[1633 + i * 9 + 1] << 8) + response[1633 + i * 9 + 2]), StartPSN = (uint)((response[1633 + i * 9 + 3] << 16) + (response[1633 + i * 9 + 4] << 8) + response[1633 + i * 9 + 5]), EndPSN = (uint)((response[1633 + i * 9 + 6] << 16) + (response[1633 + i * 9 + 7] << 8) + response[1633 + i * 9 + 8]) }; } for(var i = 0; i < 23; i++) { ss.ExtentsCopy[i] = new SecuritySectorExtent { Unknown = (uint)((response[1840 + i * 9 + 0] << 16) + (response[1840 + i * 9 + 1] << 8) + response[1840 + i * 9 + 2]), StartPSN = (uint)((response[1840 + i * 9 + 3] << 16) + (response[1840 + i * 9 + 4] << 8) + response[1840 + i * 9 + 5]), 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; var sb = new StringBuilder(); string sizeString = decoded.DiscSize switch { DVDSize.Eighty => Localization._80mm, DVDSize.OneTwenty => Localization._120mm, _ => string.Format(Localization.unknown_size_identifier_0, decoded.DiscSize) }; string categorySentence = Localization.Disc_is_a_0_1_version_2; switch(decoded.DiskCategory) { case DiskCategory.DVDPRWDL: sb.AppendFormat(categorySentence, sizeString, Localization.Xbox_Game_Disc, decoded.PartVersion) .AppendLine(); break; case DiskCategory.DVDPRDL: sb.AppendFormat(categorySentence, sizeString, Localization.Xbox_360_Game_Disc, decoded.PartVersion) .AppendLine(); break; default: sb.AppendFormat(categorySentence, sizeString, Localization.unknown_disc_type, decoded.PartVersion) .AppendLine(); break; } switch(decoded.MaximumRate) { case MaximumRateField.TwoMbps: sb.AppendLine(Localization.Disc_maximum_transfer_rate_is_2_52_Mbit_sec); break; case MaximumRateField.FiveMbps: sb.AppendLine(Localization.Disc_maximum_transfer_rate_is_5_04_Mbit_sec); break; case MaximumRateField.TenMbps: sb.AppendLine(Localization.Disc_maximum_transfer_rate_is_10_08_Mbit_sec); break; case MaximumRateField.TwentyMbps: sb.AppendLine(Localization.Disc_maximum_transfer_rate_is_20_16_Mbit_sec); break; case MaximumRateField.ThirtyMbps: sb.AppendLine(Localization.Disc_maximum_transfer_rate_is_30_24_Mbit_sec); break; case MaximumRateField.Unspecified: sb.AppendLine(Localization.Disc_maximum_transfer_rate_is_unspecified); break; default: sb.AppendFormat(Localization.Disc_maximum_transfer_rate_is_specified_by_unknown_key_0, decoded.MaximumRate) .AppendLine(); break; } sb.AppendFormat(Localization.Disc_has_0_layers, decoded.Layers + 1).AppendLine(); switch(decoded.TrackPath) { case true when decoded.Layers == 1: sb.AppendLine(Localization.Layers_are_in_parallel_track_path); break; case false when decoded.Layers == 1: sb.AppendLine(Localization.Layers_are_in_opposite_track_path); break; } switch(decoded.LinearDensity) { case LinearDensityField.TwoSix: sb.AppendLine(Localization.Pitch_size_is_0_267_μm_bit); break; case LinearDensityField.TwoNine: sb.AppendLine(Localization.Pitch_size_is_0_147_μm_bit); break; case LinearDensityField.FourZero: sb.AppendLine(Localization.Pitch_size_is_between_0_409_μm_bit_and_0_435_μm_bit); break; case LinearDensityField.TwoEight: sb.AppendLine(Localization.Pitch_size_is_between_0_140_μm_bit_and_0_148_μm_bit); break; case LinearDensityField.OneFive: sb.AppendLine(Localization.Pitch_size_is_0_153_μm_bit); break; case LinearDensityField.OneThree: sb.AppendLine(Localization.Pitch_size_is_between_0_130_μm_bit_and_0_140_μm_bit); break; case LinearDensityField.ThreeFive: sb.AppendLine(Localization.Pitch_size_is_0_353_μm_bit); break; default: sb.AppendFormat(Localization.Unknown_pitch_size_key_0, decoded.LinearDensity).AppendLine(); break; } switch(decoded.TrackDensity) { case TrackDensityField.Seven: sb.AppendLine(Localization.Track_size_is_0_74_μm); break; case TrackDensityField.Eight: sb.AppendLine(Localization.Track_size_is_0_80_μm); break; case TrackDensityField.Six: sb.AppendLine(Localization.Track_size_is_0_615_μm); break; case TrackDensityField.Four: sb.AppendLine(Localization.Track_size_is_0_40_μm); break; case TrackDensityField.Three: sb.AppendLine(Localization.Track_size_is_0_34_μm); break; default: sb.AppendFormat(Localization.Unknown_track_size_key__0_, decoded.LinearDensity).AppendLine(); break; } if(decoded.DataAreaStartPSN > 0) { if(decoded.DataAreaEndPSN > 0) { sb.AppendFormat(Localization.Data_area_starts_at_PSN_0, decoded.DataAreaStartPSN).AppendLine(); sb.AppendFormat(Localization.Data_area_ends_at_PSN_0, decoded.DataAreaEndPSN).AppendLine(); if(decoded is { Layers: 1, TrackPath: false }) sb.AppendFormat(Localization.Layer_zero_ends_at_PSN_0, decoded.Layer0EndPSN).AppendLine(); } else sb.AppendLine(Localization.Disc_is_empty); } else sb.AppendLine(Localization.Disc_is_empty); sb.AppendLine("Challenges:"); foreach(ChallengeEntry entry in decoded.ChallengeEntries) { sb.AppendFormat("\t" + Localization.Challenge_ID_0, entry.ChallengeId).AppendLine(); sb.AppendFormat("\t" + Localization.Challenge_level_0, entry.Level).AppendLine(); sb.AppendFormat("\t" + Localization.Challenge_value_0, entry.ChallengeValue).AppendLine(); sb.AppendFormat("\t" + Localization.Response_modifier_0, entry.ResponseModifier).AppendLine(); sb.AppendFormat("\t" + Localization.Response_value_0, entry.ResponseValue).AppendLine(); } for(var i = 0; i < 16; i++) { sb.AppendFormat(Localization.Extent_starts_at_PSN_0_and_ends_at_PSN_1, decoded.Extents[i].StartPSN, decoded.Extents[i].EndPSN) .AppendLine(); } return sb.ToString(); } public static string Prettify(byte[] response) => Prettify(Decode(response)); #region Nested type: ChallengeEntry public struct ChallengeEntry { public byte Level; public byte ChallengeId; public uint ChallengeValue; public byte ResponseModifier; public uint ResponseValue; } #endregion #region Nested type: SecuritySector 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; } #endregion #region Nested type: SecuritySectorExtent 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; } #endregion }