// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename       : ExtendedCSD.cs
// Author(s)      : Natalia Portillo <claunia@claunia.com>
//
// Component      : Device structures decoders.
//
// --[ Description ] ----------------------------------------------------------
//
//     Decodes MultiMediaCard extended CSD.
//
// --[ 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 <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/

// ReSharper disable InconsistentNaming

using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;

namespace Aaru.Decoders.MMC;

[SuppressMessage("ReSharper", "MemberCanBeInternal")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
[SuppressMessage("ReSharper", "UnassignedField.Global")]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[SuppressMessage("ReSharper", "NotAccessedField.Global")]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public class ExtendedCSD
{
    public byte                            AccessSize;
    public byte                            BackgroundOperationsStatus;
    public BackgroundOperationsSupport     BackgroundOperationsSupport;
    public byte                            BadBlockManagementMode;
    public byte                            BarrierControl;
    public byte                            BarrierSupport;
    public BootAreaWriteProtectionRegister BootAreaWriteProtectionRegister;
    public byte                            BootBusConditions;
    public BootConfigProtection            BootConfigProtection;
    public BootInformation                 BootInformation;
    public byte                            BootPartitionSize;
    public byte                            BootWriteProtectionStatus;
    public byte                            BusWidth;
    public byte                            CacheControl;
    public byte                            CacheFlushing;
    public CacheFlushingPolicy             CacheFlushingPolicy;
    public uint                            CacheSize;
    public byte                            Class6CommandsControl;
    public byte                            CMDQueuingDepth;
    public CMDQueuingSupport               CMDQueuingSupport;
    public byte                            CommandQueueModeEnable;
    public byte                            CommandSet;
    public byte                            CommandSetRevision;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)]
    public byte[] ContextConfiguration;
    public byte           ContextManagementCaps;
    public uint           CorrectlyProgrammedSectors;
    public DataTagSupport DataTagSupport;
    public byte           DeviceLifeEstimationTypeA;
    public byte           DeviceLifeEstimationTypeB;
    public DeviceType     DeviceType;
    public ushort         DeviceVersion;
    public DriverStrength DriverStrength;
    public byte           DyncapNeeded;
    public byte           EnableBackgroundOperationsHandshake;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] EnhancedUserDataAreaSize;
    public uint                      EnhancedUserDataStartAddress;
    public byte                      ErasedMemoryContent;
    public ushort                    ExceptionEventsControl;
    public ushort                    ExceptionEventsStatus;
    public ushort                    ExtendedPartitionsAttribute;
    public ExtendedPartitionsSupport ExtendedPartitionsSupport;
    public byte                      ExtendedSecurityCommandsError;
    public uint                      FFUArgument;
    public FFUFeatures               FFUFeatures;
    public byte                      FFUStatus;
    public byte                      FirmwareConfiguration;
    public ulong                     FirmwareVersion;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
    public byte[] GeneralPurposePartitionSize;
    public byte                             GenericCMD6Timeout;
    public HighCapacityEraseGroupDefinition HighCapacityEraseGroupDefinition;
    public byte                             HighCapacityEraseTimeout;
    public byte                             HighCapacityEraseUnitSize;
    public byte                             HighCapacityWriteProtectGroupSize;
    public byte                             HighSpeedInterfaceTiming;
    public HPIFeatures                      HPIFeatures;
    public byte                             HPIManagement;
    public byte                             HWResetFunction;
    public byte                             InitializationTimeAfterPartition;
    public byte                             InitializationTimeoutAfterEmulationChange;
    public byte                             LargeUnitSize;
    public byte                             ManuallyStartBackgroundOperations;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] MaxEnhancedAreaSize;
    public byte                MaxPackedReadCommands;
    public byte                MaxPackedWriteCommands;
    public uint                MaxPreLoadingDataSize;
    public byte                MinimumReadPerformance26;
    public byte                MinimumReadPerformance26_4;
    public byte                MinimumReadPerformance52;
    public byte                MinimumReadPerformanceDDR52;
    public byte                MinimumWritePerformance26;
    public byte                MinimumWritePerformance26_4;
    public byte                MinimumWritePerformance52;
    public byte                MinimumWritePerformanceDDR52;
    public byte                ModeConfig;
    public byte                ModeOperationCodes;
    public byte                NativeSectorSize;
    public uint                NumberOfFWSectorsCorrectlyProgrammed;
    public byte                OperationCodesTimeout;
    public byte                OptimalReadSize;
    public byte                OptimalTrimUnitSize;
    public byte                OptimalWriteSize;
    public byte                OutOfInterruptBusyTiming;
    public byte                PackageCaseTemperatureControl;
    public byte                PackedCommandFailureIndex;
    public byte                PackedCommandStatus;
    public byte                PartitionConfiguration;
    public byte                PartitioningSetting;
    public PartitioningSupport PartitioningSupport;
    public byte                PartitionsAttribute;
    public byte                PartitionSwitchingTime;
    public byte                PeriodicWakeUp;
    public byte                PowerClass;
    public byte                PowerClass26;
    public byte                PowerClass26_195;
    public byte                PowerClass52;
    public byte                PowerClass52_195;
    public byte                PowerClassDDR200;
    public byte                PowerClassDDR200_130;
    public byte                PowerClassDDR200_195;
    public byte                PowerClassDDR52;
    public byte                PowerClassDDR52_195;
    public byte                PowerOffNotification;
    public byte                PowerOffNotificationTimeout;
    public byte                PreEOLInformation;
    public uint                PreLoadingDataSize;
    public byte                ProductionStateAwareness;
    public byte                ProductionStateAwarenessTimeout;
    public byte                ProductStateAwarenessEnablement;
    public byte                ReliableWriteSectorCount;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)]
    public byte[] Reserved0;
    public ushort Reserved1;
    public byte   Reserved10;
    public byte   Reserved11;
    public byte   Reserved12;
    public byte   Reserved13;
    public byte   Reserved14;
    public byte   Reserved15;
    public byte   Reserved16;
    public byte   Reserved17;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 177)]
    public byte[] Reserved18;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public byte[] Reserved19;
    public ushort                          Reserved2;
    public byte                            Reserved3;
    public byte                            Reserved4;
    public byte                            Reserved5;
    public byte                            Reserved6;
    public byte                            Reserved7;
    public byte                            Reserved8;
    public byte                            Reserved9;
    public byte                            Revision;
    public byte                            RPMBSize;
    public uint                            SectorCount;
    public byte                            SectorSize;
    public byte                            SectorSizeEmulation;
    public byte                            SecureEraseMultiplier;
    public SecureFeatureSupport            SecureFeatureSupport;
    public byte                            SecureRemovalType;
    public byte                            SecureTRIMMultiplier;
    public SecureWriteProtectInformation   SecureWriteProtectInformation;
    public byte                            SleepAwakeTimeout;
    public byte                            SleepCurrentVcc;
    public byte                            SleepCurrentVccQ;
    public byte                            SleepNotificationTimeout;
    public byte                            StartSanitizeOperation;
    public byte                            StrobeSupport;
    public byte                            Structure;
    public DeviceSupportedCommandSets      SupportedCommandSets;
    public SupportedModes                  SupportedModes;
    public byte                            SupportsProgramCxDInDDR;
    public byte                            TagResourcesSize;
    public byte                            TagUnitSize;
    public byte                            TRIMMultiplier;
    public UserAreaWriteProtectionRegister UserAreaWriteProtectionRegister;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
    public byte[] VendorHealthReport;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
    public byte[] VendorSpecific;
    public byte WriteReliabilityParameterRegister;
    public byte WriteReliabilitySettingRegister;
}

[Flags]
public enum DeviceSupportedCommandSets : byte
{
    Standard = 1 << 0
}

[Flags]
public enum HPIFeatures : byte
{
    Supported = 1 << 0,
    CMD12     = 1 << 1
}

[Flags]
public enum BackgroundOperationsSupport : byte
{
    Supported = 1 << 0
}

[Flags]
public enum DataTagSupport : byte
{
    Supported = 1 << 0
}

[Flags]
public enum ExtendedPartitionsSupport : byte
{
    SystemCode    = 1 << 0,
    NonPersistent = 1 << 1
}

[Flags]
public enum SupportedModes : byte
{
    FFU            = 1 << 0,
    VendorSpecific = 1 << 1
}

[Flags]
public enum FFUFeatures : byte
{
    SupportedModeOperationCodes = 1 << 0
}

[Flags]
public enum CMDQueuingSupport : byte
{
    Supported = 1 << 0
}

[Flags]
public enum CacheFlushingPolicy : byte
{
    FIFO = 1 << 0
}

[Flags]
public enum SecureFeatureSupport : byte
{
    Purge     = 1 << 0,
    Defective = 1 << 2,
    Trim      = 1 << 4,
    Sanitize  = 1 << 6
}

[Flags]
public enum BootInformation : byte
{
    Alternative = 1 << 0,
    DDR         = 1 << 1,
    HighSpeed   = 1 << 2
}

[Flags]
public enum SecureWriteProtectInformation : byte
{
    Supported = 1 << 0,
    Enabled   = 1 << 1
}

[Flags]
public enum DriverStrength : byte
{
    Type0 = 1 << 0,
    Type1 = 1 << 1,
    Type2 = 1 << 2,
    Type3 = 1 << 3,
    Type4 = 1 << 4
}

[Flags]
public enum DeviceType : byte
{
    HS_26        = 1 << 0,
    HS_52        = 1 << 1,
    HS_DDR_52    = 1 << 2,
    HS_DDR_52_LV = 1 << 3,
    HS200_18     = 1 << 4,
    HS200_12     = 1 << 5,
    HS400_18     = 1 << 6,
    HS400_12     = 1 << 7
}

[Flags]
public enum BootConfigProtection : byte
{
    PowerCycle = 1 << 0,
    Permanent  = 1 << 4
}

[Flags]
public enum HighCapacityEraseGroupDefinition : byte
{
    Enabled = 1 << 0
}

[Flags]
public enum BootAreaWriteProtectionRegister : byte
{
    PowerOn          = 1 << 0,
    PowerOnArea2     = 1 << 1,
    Permanent        = 1 << 2,
    PermanentArea2   = 1 << 3,
    PermanentDisable = 1 << 4,
    PowerOnDisable   = 1 << 6,
    Selected         = 1 << 7
}

[Flags]
public enum UserAreaWriteProtectionRegister : byte
{
    ApplyPowerOn        = 1 << 0,
    ApplyPermanent      = 1 << 2,
    DisablePowerOn      = 1 << 3,
    DisablePermanent    = 1 << 4,
    DisableWriteProtect = 1 << 6,
    DisablePassword     = 1 << 7
}

[Flags]
public enum PartitioningSupport : byte
{
    Supported = 1 << 0,
    Enhanced  = 1 << 1,
    Extended  = 1 << 2
}

[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public static partial class Decoders
{
    public static ExtendedCSD DecodeExtendedCSD(byte[] response)
    {
        if(response?.Length != 512) return null;

        var handle = GCHandle.Alloc(response, GCHandleType.Pinned);
        var csd    = (ExtendedCSD)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(ExtendedCSD));
        handle.Free();

        return csd;
    }

    public static string PrettifyExtendedCSD(ExtendedCSD csd)
    {
        if(csd == null) return null;

        var sb = new StringBuilder();
        sb.AppendLine(Localization.MultiMediaCard_Extended_Device_Specific_Data_Register);

        double unit;

        if(csd.ExtendedSecurityCommandsError != 0)
        {
            sb.AppendFormat("\t" + Localization.Last_extended_security_error_was_0, csd.ExtendedSecurityCommandsError)
              .AppendLine();
        }

        if(csd.SupportedCommandSets.HasFlag(DeviceSupportedCommandSets.Standard))
            sb.AppendLine("\t" + Localization.Device_supports_standard_MMC_command_set);

        if(((int)csd.SupportedCommandSets & 0xFE) != 0)
            sb.AppendFormat("\t" + Localization.Device_supports_unknown_command_sets_0, (int)csd.SupportedCommandSets);

        if(csd.HPIFeatures.HasFlag(HPIFeatures.Supported))
        {
            sb.AppendLine(csd.HPIFeatures.HasFlag(HPIFeatures.CMD12)
                              ? "\t" + Localization.Device_implements_HPI_using_CMD12
                              : "\t" + Localization.Device_implements_HPI_using_CMD13);
        }

        if(csd.BackgroundOperationsSupport.HasFlag(BackgroundOperationsSupport.Supported))
            sb.AppendLine("\t" + Localization.Device_supports_background_operations);

        sb.AppendFormat("\t" + Localization.Device_supports_a_maximum_of_0_packed_reads_and_1_packed_writes,
                        csd.MaxPackedReadCommands,
                        csd.MaxPackedWriteCommands)
          .AppendLine();

        if(csd.DataTagSupport.HasFlag(DataTagSupport.Supported))
        {
            sb.AppendLine("\t" + Localization.Device_supports_Data_Tag);

            sb.AppendFormat("\t" + Localization.Tags_must_be_in_units_of_0_sectors, Math.Pow(2, csd.TagUnitSize))
              .AppendLine();

            sb.AppendFormat("\t" + Localization.Tag_resources_size_is_0, csd.TagResourcesSize).AppendLine();
        }

        if(csd.ContextManagementCaps != 0)
        {
            sb.AppendFormat("\t" + Localization.Max_context_ID_is_0, csd.ContextManagementCaps & 0xF).AppendLine();

            sb.AppendFormat("\t"                                      + Localization.Large_unit_maximum_multiplier_is_0,
                            ((csd.ContextManagementCaps & 0x70) >> 4) + 1)
              .AppendLine();
        }

        sb.AppendFormat("\t" + Localization.Large_unit_size_is_0_MiB, csd.LargeUnitSize + 1).AppendLine();

        if(csd.ExtendedPartitionsSupport.HasFlag(ExtendedPartitionsSupport.NonPersistent))
            sb.AppendLine("\t" + Localization.Device_supports_non_persistent_extended_partitions);

        if(csd.ExtendedPartitionsSupport.HasFlag(ExtendedPartitionsSupport.SystemCode))
            sb.AppendLine("\t" + Localization.Device_supports_system_code_extended_partitions);

        if(csd.SupportedModes.HasFlag(SupportedModes.FFU))
        {
            sb.AppendLine("\t" + Localization.Device_supports_FFU);

            if(csd.FFUFeatures.HasFlag(FFUFeatures.SupportedModeOperationCodes))

                // todo public byte ModeOperationCodes

            {
                if(csd.OperationCodesTimeout > 0)
                {
                    unit = Math.Pow(2, csd.OperationCodesTimeout) * 100;

                    switch(unit)
                    {
                        case > 1000000:
                            sb.AppendFormat("\t" +
                                            Localization
                                               .Maximum_timeout_for_switch_command_when_setting_a_value_to_the_mode_operation_codes_field_is_0_s,
                                            unit / 1000000)
                              .AppendLine();

                            break;
                        case > 1000:
                            sb.AppendFormat("\t" +
                                            Localization
                                               .Maximum_timeout_for_switch_command_when_setting_a_value_to_the_mode_operation_codes_field_is_0_ms,
                                            unit / 1000)
                              .AppendLine();

                            break;
                        default:
                            sb.AppendFormat("\t" +
                                            Localization
                                               .Maximum_timeout_for_switch_command_when_setting_a_value_to_the_mode_operation_codes_field_is_0_µs,
                                            unit)
                              .AppendLine();

                            break;
                    }
                }
            }
        }

        if(csd.SupportedModes.HasFlag(SupportedModes.VendorSpecific))
            sb.AppendLine("\t" + Localization.Device_supports_Vendor_Specific_Mode);

        if(csd.BarrierSupport == 0x01) sb.AppendLine("\t" + Localization.Device_supports_the_barrier_command);

        if(csd.CMDQueuingSupport.HasFlag(CMDQueuingSupport.Supported))
        {
            sb.AppendFormat("\t"                + Localization.Device_supports_command_queuing_with_a_depth_of_0,
                            csd.CMDQueuingDepth + 1)
              .AppendLine();
        }

        sb.AppendFormat("\t" + Localization._0_firmware_sectors_correctly_programmed,
                        csd.NumberOfFWSectorsCorrectlyProgrammed)
          .AppendLine();

        switch(csd.DeviceLifeEstimationTypeB)
        {
            case 1:
                sb.AppendLine("\t" + Localization.Device_used_between_zero_and_10_of_its_estimated_life_time);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.Device_used_between_10_and_20_of_its_estimated_life_time);

                break;
            case 3:
                sb.AppendLine("\t" + Localization.Device_used_between_20_and_30_of_its_estimated_life_time);

                break;
            case 4:
                sb.AppendLine("\t" + Localization.Device_used_between_30_and_40_of_its_estimated_life_time);

                break;
            case 5:
                sb.AppendLine("\t" + Localization.Device_used_between_40_and_50_of_its_estimated_life_time);

                break;
            case 6:
                sb.AppendLine("\t" + Localization.Device_used_between_50_and_60_of_its_estimated_life_time);

                break;
            case 7:
                sb.AppendLine("\t" + Localization.Device_used_between_60_and_70_of_its_estimated_life_time);

                break;
            case 8:
                sb.AppendLine("\t" + Localization.Device_used_between_70_and_80_of_its_estimated_life_time);

                break;
            case 9:
                sb.AppendLine("\t" + Localization.Device_used_between_80_and_90_of_its_estimated_life_time);

                break;
            case 10:
                sb.AppendLine("\t" + Localization.Device_used_between_90_and_100_of_its_estimated_life_time);

                break;
            case 11:
                sb.AppendLine("\t" + Localization.Device_exceeded_its_maximum_estimated_life_time);

                break;
        }

        switch(csd.DeviceLifeEstimationTypeA)
        {
            case 1:
                sb.AppendLine("\t" + Localization.Device_used_between_zero_and_10_of_its_estimated_life_time);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.Device_used_between_10_and_20_of_its_estimated_life_time);

                break;
            case 3:
                sb.AppendLine("\t" + Localization.Device_used_between_20_and_30_of_its_estimated_life_time);

                break;
            case 4:
                sb.AppendLine("\t" + Localization.Device_used_between_30_and_40_of_its_estimated_life_time);

                break;
            case 5:
                sb.AppendLine("\t" + Localization.Device_used_between_40_and_50_of_its_estimated_life_time);

                break;
            case 6:
                sb.AppendLine("\t" + Localization.Device_used_between_50_and_60_of_its_estimated_life_time);

                break;
            case 7:
                sb.AppendLine("\t" + Localization.Device_used_between_60_and_70_of_its_estimated_life_time);

                break;
            case 8:
                sb.AppendLine("\t" + Localization.Device_used_between_70_and_80_of_its_estimated_life_time);

                break;
            case 9:
                sb.AppendLine("\t" + Localization.Device_used_between_80_and_90_of_its_estimated_life_time);

                break;
            case 10:
                sb.AppendLine("\t" + Localization.Device_used_between_90_and_100_of_its_estimated_life_time);

                break;
            case 11:
                sb.AppendLine("\t" + Localization.Device_exceeded_its_maximum_estimated_life_time);

                break;
        }

        switch(csd.PreEOLInformation)
        {
            case 1:
                sb.AppendLine("\t" + Localization.Device_informs_its_in_good_health);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.Device_informs_it_should_be_replaced_soon);

                break;
            case 3:
                sb.AppendLine("\t" + Localization.Device_informs_it_should_be_replace_immediately);

                break;
        }

        if(csd.OptimalReadSize == 0)
            sb.AppendLine("\t" + Localization.Device_does_not_report_an_optimal_read_size);
        else
            sb.AppendFormat("\t" + Localization.Optimal_read_size_is_0_KiB, 4 * csd.OptimalReadSize).AppendLine();

        if(csd.OptimalWriteSize == 0)
            sb.AppendLine("\t" + Localization.Device_does_not_report_an_optimal_write_size);
        else
            sb.AppendFormat("\t" + Localization.Optimal_write_size_is_0_KiB, 4 * csd.OptimalWriteSize).AppendLine();

        if(csd.OptimalTrimUnitSize == 0)
            sb.AppendLine("\t" + Localization.Device_does_not_report_an_optimal_trim_size);
        else
        {
            sb.AppendFormat("\t" + Localization.Optimal_trim_size_is_0_KiB,
                            4 * Math.Pow(2, csd.OptimalTrimUnitSize - 1))
              .AppendLine();
        }

        sb.AppendFormat("\t" + Localization.Device_version_0,   csd.DeviceVersion).AppendLine();
        sb.AppendFormat("\t" + Localization.Firmware_version_0, csd.FirmwareVersion).AppendLine();

        if(csd.CacheSize == 0)
            sb.AppendLine("\t" + Localization.Device_has_no_cache);
        else
            sb.AppendFormat("\t" + Localization.Device_has_0_KiB_of_cache, csd.CacheSize / 8).AppendLine();

        if(csd.GenericCMD6Timeout > 0)
        {
            sb.AppendFormat("\t" + Localization.Device_takes_a_maximum_of_0_ms_by_default_for_a_SWITCH_command,
                            csd.GenericCMD6Timeout * 10)
              .AppendLine();
        }

        if(csd.PowerOffNotificationTimeout > 0)
        {
            sb.AppendFormat("\t" +
                            Localization
                               .Device_takes_a_maximum_of_0_by_default_to_power_off_from_a_SWITCH_command_notification,
                            csd.PowerOffNotificationTimeout * 10)
              .AppendLine();
        }

        switch(csd.BackgroundOperationsStatus & 0x03)
        {
            case 0:
                sb.AppendLine("\t" + Localization.Device_has_no_pending_background_operations);

                break;
            case 1:
                sb.AppendLine("\t" + Localization.Device_has_non_critical_operations_outstanding);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.Device_has_performance_impacted_operations_outstanding);

                break;
            case 3:
                sb.AppendLine("\t" + Localization.Device_has_critical_operations_outstanding);

                break;
        }

        sb.AppendFormat("\t" + Localization.Last_WRITE_MULTIPLE_command_correctly_programmed_0_sectors,
                        csd.CorrectlyProgrammedSectors)
          .AppendLine();

        if(csd.InitializationTimeAfterPartition > 0)
        {
            sb.AppendFormat("\t" + Localization.Device_takes_a_maximum_of_0_ms_for_initialization_after_partition,
                            csd.InitializationTimeAfterPartition * 100)
              .AppendLine();
        }

        if(csd.CacheFlushingPolicy.HasFlag(CacheFlushingPolicy.FIFO))
            sb.AppendLine("\t" + Localization.Device_uses_a_FIFO_policy_for_cache_flushing);

        if(csd.TRIMMultiplier > 0)
        {
            sb.AppendFormat("\t" + Localization.Device_takes_a_maximum_of_0_ms_for_trimming_a_single_erase_group,
                            csd.TRIMMultiplier * 300)
              .AppendLine();
        }

        if(csd.SecureFeatureSupport.HasFlag(SecureFeatureSupport.Sanitize))
            sb.AppendLine("\t" + Localization.Device_supports_the_sanitize_operation);

        if(csd.SecureFeatureSupport.HasFlag(SecureFeatureSupport.Trim))
            sb.AppendLine("\t" + Localization.Device_supports_supports_the_secure_and_insecure_trim_operations);

        if(csd.SecureFeatureSupport.HasFlag(SecureFeatureSupport.Defective))
            sb.AppendLine("\t" + Localization.Device_supports_automatic_erase_on_retired_defective_blocks);

        if(csd.SecureFeatureSupport.HasFlag(SecureFeatureSupport.Purge))
            sb.AppendLine("\t" + Localization.Device_supports_secure_purge_operations);

        if(csd.SecureEraseMultiplier > 0)
        {
            sb.AppendFormat("\t" +
                            Localization.Device_takes_a_maximum_of_0_ms_for_securely_erasing_a_single_erase_group,
                            csd.SecureEraseMultiplier * 300)
              .AppendLine();
        }

        if(csd.SecureTRIMMultiplier > 0)
        {
            sb.AppendFormat("\t" +
                            Localization.Device_takes_a_maximum_of_0_ms_for_securely_trimming_a_single_erase_group,
                            csd.SecureTRIMMultiplier * 300)
              .AppendLine();
        }

        if(csd.BootInformation.HasFlag(BootInformation.HighSpeed))
            sb.AppendLine("\t" + Localization.Device_supports_high_speed_timing_on_boot);

        if(csd.BootInformation.HasFlag(BootInformation.DDR))
            sb.AppendLine("\t" + Localization.Device_supports_dual_data_rate_on_boot);

        if(csd.BootInformation.HasFlag(BootInformation.Alternative))
            sb.AppendLine("\t" + Localization.Device_supports_alternative_boot_method);

        if(csd.BootPartitionSize > 0)
        {
            sb.AppendFormat("\t" + Localization.Device_has_a_0_KiB_boot_partition, csd.BootPartitionSize * 128)
              .AppendLine();
        }

        if((csd.AccessSize & 0x0F) > 0)
        {
            sb.AppendFormat("\t" + Localization.Device_has_a_page_size_of_0_KiB,
                            512 * Math.Pow(2, (csd.AccessSize & 0x0F) - 1) / 1024.0)
              .AppendLine();
        }

        if(csd.HighCapacityEraseUnitSize > 0)
        {
            sb.AppendFormat("\t" + Localization.Device_erase_groups_are_0_KiB, csd.HighCapacityEraseUnitSize * 512)
              .AppendLine();
        }

        if(csd.HighCapacityEraseTimeout > 0)
        {
            sb.AppendFormat("\t" + Localization.Device_takes_a_maximum_of_0_ms_for_erasing_a_single_erase_group,
                            csd.HighCapacityEraseTimeout * 300)
              .AppendLine();
        }

        if(csd.HighCapacityWriteProtectGroupSize > 0)
        {
            sb.AppendFormat("\t" + Localization.Device_smallest_write_protect_group_is_made_of_0_erase_groups,
                            csd.HighCapacityWriteProtectGroupSize)
              .AppendLine();
        }

        if(csd.SleepCurrentVcc > 0)
        {
            unit = Math.Pow(2, csd.SleepCurrentVcc);

            if(unit > 1000)
                sb.AppendFormat("\t" + Localization.Device_uses_0_mA_on_Vcc_when_sleeping, unit / 1000).AppendLine();
            else
                sb.AppendFormat("\t" + Localization.Device_uses_0_μA_on_Vcc_when_sleeping, unit).AppendLine();
        }

        if(csd.SleepCurrentVccQ > 0)
        {
            unit = Math.Pow(2, csd.SleepCurrentVccQ);

            if(unit > 1000)
                sb.AppendFormat("\t" + Localization.Device_uses_0_mA_on_Vccq_when_sleeping, unit / 1000).AppendLine();
            else
                sb.AppendFormat("\t" + Localization.Device_uses_0_μA_on_Vccq_when_sleeping, unit).AppendLine();
        }

        if(csd.ProductionStateAwarenessTimeout > 0)
        {
            unit = Math.Pow(2, csd.ProductionStateAwareness) * 100;

            switch(unit)
            {
                case > 1000000:
                    sb.AppendFormat("\t" +
                                    Localization.Device_takes_a_maximum_of_0_s_to_switch_production_state_awareness,
                                    unit / 1000000)
                      .AppendLine();

                    break;
                case > 1000:
                    sb.AppendFormat("\t" +
                                    Localization.Device_takes_a_maximum_of_0_ms_to_switch_production_state_awareness,
                                    unit / 1000)
                      .AppendLine();

                    break;
                default:
                    sb.AppendFormat("\t" +
                                    Localization.Device_takes_a_maximum_of_0_μs_to_switch_production_state_awareness,
                                    unit)
                      .AppendLine();

                    break;
            }
        }

        if(csd.SleepAwakeTimeout > 0)
        {
            unit = Math.Pow(2, csd.SleepAwakeTimeout) * 100;

            switch(unit)
            {
                case > 1000000:
                    sb.AppendFormat("\t" +
                                    Localization
                                       .Device_takes_a_maximum_of_0_ms_to_transition_between_sleep_and_standby_states,
                                    unit / 1000000)
                      .AppendLine();

                    break;
                case > 1000:
                    sb.AppendFormat("\t" +
                                    Localization
                                       .Device_takes_a_maximum_of_0_μs_to_transition_between_sleep_and_standby_states,
                                    unit / 1000)
                      .AppendLine();

                    break;
                default:
                    sb.AppendFormat("\t" +
                                    Localization
                                       .Device_takes_a_maximum_of_0_ns_to_transition_between_sleep_and_standby_states,
                                    unit)
                      .AppendLine();

                    break;
            }
        }

        if(csd.SleepNotificationTimeout > 0)
        {
            unit = Math.Pow(2, csd.SleepNotificationTimeout) * 10;

            switch(unit)
            {
                case > 1000000:
                    sb.AppendFormat("\t" + Localization.Device_takes_a_maximum_of_0_s_to_move_to_sleep_state,
                                    unit / 1000000)
                      .AppendLine();

                    break;
                case > 1000:
                    sb.AppendFormat("\t" + Localization.Device_takes_a_maximum_of_0_ms_to_move_to_sleep_state,
                                    unit / 1000)
                      .AppendLine();

                    break;
                default:
                    sb.AppendFormat("\t" + Localization.Device_takes_a_maximum_of_0_μs_to_move_to_sleep_state, unit)
                      .AppendLine();

                    break;
            }
        }

        sb.AppendFormat("\t" + Localization.Device_has_0_sectors, csd.SectorCount).AppendLine();

        if(csd.SecureWriteProtectInformation.HasFlag(SecureWriteProtectInformation.Supported))
        {
            sb.AppendLine("\t" + Localization.Device_supports_secure_write_protection);

            if(csd.SecureWriteProtectInformation.HasFlag(SecureWriteProtectInformation.Enabled))
                sb.AppendLine("\t" + Localization.Device_has_secure_write_protection_enabled);
        }

        unit = csd.MinimumReadPerformance26 * 300;

        if(csd.MinimumReadPerformance26 == 0)
            sb.AppendLine("\t" + Localization.Device_cannot_achieve_2_4MB_s_reading_in_SDR_26Mhz_mode);
        else
        {
            sb.AppendFormat("\t" + Localization.Device_can_achieve_a_minimum_of_0_MB_s_reading_in_SDR_26Mhz_mode,
                            unit / 1000)
              .AppendLine();
        }

        unit = csd.MinimumReadPerformance26_4 * 300;

        if(csd.MinimumReadPerformance26_4 == 0)
            sb.AppendLine("\t" + Localization.Device_cannot_achieve_2_4MB_s_reading_in_SDR_26Mhz_4_bit_mode);
        else
        {
            sb.AppendFormat("\t" + Localization.Device_can_achieve_a_minimum_of_0_MB_s_reading_in_SDR_26Mhz_4_bit_mode,
                            unit / 1000)
              .AppendLine();
        }

        unit = csd.MinimumReadPerformance52 * 300;

        if(csd.MinimumReadPerformance52 == 0)
            sb.AppendLine("\t" + Localization.Device_cannot_achieve_2_4MB_s_reading_in_SDR_52Mhz_mode);
        else
        {
            sb.AppendFormat("\t" + Localization.Device_can_achieve_a_minimum_of_0_MB_s_reading_in_SDR_52Mhz_mode,
                            unit / 1000)
              .AppendLine();
        }

        unit = csd.MinimumReadPerformanceDDR52 * 600;

        if(csd.MinimumReadPerformanceDDR52 == 0)
            sb.AppendLine("\t" + Localization.Device_cannot_achieve_4_8MB_s_reading_in_DDR_52Mhz_mode);
        else
        {
            sb.AppendFormat("\t" + Localization.Device_can_achieve_a_minimum_of_0_MB_s_reading_in_DDR_52Mhz_mode,
                            unit / 1000)
              .AppendLine();
        }

        unit = csd.MinimumWritePerformance26 * 300;

        if(csd.MinimumWritePerformance26 == 0)
            sb.AppendLine("\t" + Localization.Device_cannot_achieve_2_4MB_s_writing_in_SDR_26Mhz_mode);
        else
        {
            sb.AppendFormat("\t" + Localization.Device_can_achieve_a_minimum_of_0_MB_s_writing_in_SDR_26Mhz_mode,
                            unit / 1000)
              .AppendLine();
        }

        unit = csd.MinimumWritePerformance26_4 * 300;

        if(csd.MinimumWritePerformance26_4 == 0)
            sb.AppendLine("\t" + Localization.Device_cannot_achieve_2_4MB_s_writing_in_SDR_26Mhz_4_bit_mode);
        else
        {
            sb.AppendFormat("\t" + Localization.Device_can_achieve_a_minimum_of_0_MB_s_writing_in_SDR_26Mhz_4_bit_mode,
                            unit / 1000)
              .AppendLine();
        }

        unit = csd.MinimumWritePerformance52 * 300;

        if(csd.MinimumWritePerformance52 == 0)
            sb.AppendLine("\t" + Localization.Device_cannot_achieve_2_4MB_s_writing_in_SDR_52Mhz_mode);
        else
        {
            sb.AppendFormat("\t" + Localization.Device_can_achieve_a_minimum_of_0_MB_s_writing_in_SDR_52Mhz_mode,
                            unit / 1000)
              .AppendLine();
        }

        unit = csd.MinimumWritePerformanceDDR52 * 600;

        if(csd.MinimumWritePerformanceDDR52 == 0)
            sb.AppendLine("\t" + Localization.Device_cannot_achieve_4_8MB_s_writing_in_DDR_52Mhz_mode);
        else
        {
            sb.AppendFormat("\t" + Localization.Device_can_achieve_a_minimum_of_0_MB_s_writing_in_DDR_52Mhz_mode,
                            unit / 1000)
              .AppendLine();
        }

        if(csd.PartitionSwitchingTime > 0)
        {
            sb.AppendFormat("\t" + Localization.Device_can_take_a_maximum_of_0_ms_when_switching_partitions,
                            csd.PartitionSwitchingTime * 10)
              .AppendLine();
        }

        if(csd.OutOfInterruptBusyTiming > 0)
        {
            sb.AppendFormat("\t" + Localization.Device_can_take_a_maximum_of_0_ms_when_releasing_from_an_interrupt,
                            csd.OutOfInterruptBusyTiming * 10)
              .AppendLine();
        }

        if(csd.DriverStrength.HasFlag(DriverStrength.Type0))
            sb.AppendLine("\t" + Localization.Device_supports_IO_driver_strength_type_zero);

        if(csd.DriverStrength.HasFlag(DriverStrength.Type1))
            sb.AppendLine("\t" + Localization.Device_supports_IO_driver_strength_type_one);

        if(csd.DriverStrength.HasFlag(DriverStrength.Type2))
            sb.AppendLine("\t" + Localization.Device_supports_IO_driver_strength_type_two);

        if(csd.DriverStrength.HasFlag(DriverStrength.Type3))
            sb.AppendLine("\t" + Localization.Device_supports_IO_driver_strength_type_three);

        if(csd.DriverStrength.HasFlag(DriverStrength.Type4))
            sb.AppendLine("\t" + Localization.Device_supports_IO_driver_strength_type_four);

        if(csd.DeviceType.HasFlag(DeviceType.HS_26)) sb.AppendLine("\t" + Localization.Device_supports_26_Mhz_mode);

        if(csd.DeviceType.HasFlag(DeviceType.HS_52)) sb.AppendLine("\t" + Localization.Device_supports_52_Mhz_mode);

        if(csd.DeviceType.HasFlag(DeviceType.HS_DDR_52))
            sb.AppendLine("\t" + Localization.Device_supports_DDR_52_Mhz_mode_at_1_8V_or_3V);

        if(csd.DeviceType.HasFlag(DeviceType.HS_DDR_52_LV))
            sb.AppendLine("\t" + Localization.Device_supports_DDR_52_Mhz_mode_1_2V);

        if(csd.DeviceType.HasFlag(DeviceType.HS200_18))
            sb.AppendLine("\t" + Localization.Device_supports_HS_200_mode_SDR_200Mhz_at_1_8V);

        if(csd.DeviceType.HasFlag(DeviceType.HS200_12))
            sb.AppendLine("\t" + Localization.Device_supports_HS_200_mode_SDR_200Mhz_at_1_2V);

        if(csd.DeviceType.HasFlag(DeviceType.HS400_18))
            sb.AppendLine("\t" + Localization.Device_supports_HS_400_mode_DDR_200Mhz_at_1_8V);

        if(csd.DeviceType.HasFlag(DeviceType.HS400_12))
            sb.AppendLine("\t" + Localization.Device_supports_HS_400_mode_DDR_200Mhz_at_1_2V);

        sb.AppendFormat("\t" + Localization.CSD_version_one_0_revision_one_1, csd.Structure, csd.Revision).AppendLine();

        switch(csd.CommandSet)
        {
            case 0:
                sb.AppendLine("\t" + Localization.Device_follows_compatibility_MMC_command_set);

                break;
            case 1:
                switch(csd.CommandSetRevision)
                {
                    case 0:
                        sb.AppendLine("\t" + Localization.Device_follows_standard_MMC_command_set_v4_0);

                        break;
                    default:
                        sb.AppendFormat("\t" +
                                        Localization
                                           .Device_follows_standard_MMC_command_set_with_unknown_version_code_0,
                                        csd.CommandSetRevision)
                          .AppendLine();

                        break;
                }

                break;
            default:
                sb.AppendFormat("\t" + Localization.Device_follows_unknown_MMC_command_set_code_0_with_revision_code_1,
                                csd.CommandSet,
                                csd.CommandSetRevision)
                  .AppendLine();

                break;
        }

        switch(csd.HighSpeedInterfaceTiming & 0x0F)
        {
            case 0:
                break;
            case 1:
                sb.AppendLine("\t" + Localization.Device_is_in_High_Speed_mode);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.Device_is_in_HS200_mode);

                break;
            case 3:
                sb.AppendLine("\t" + Localization.Device_is_in_HS400_mode);

                break;
            default:
                sb.AppendFormat("\t" + Localization.Device_has_unknown_timing_mode_0,
                                csd.HighSpeedInterfaceTiming & 0x0F)
                  .AppendLine();

                break;
        }

        sb.AppendFormat("\t" + Localization.Selected_driver_strength_is_type_0,
                        (csd.HighSpeedInterfaceTiming & 0xF0) >> 4)
          .AppendLine();

        if((csd.StrobeSupport & 0x01) == 0x01)
        {
            sb.AppendLine("\t" + Localization.Device_supports_enhanced_strobe_mode);

            sb.AppendLine((csd.BusWidth & 0x80) == 0x80
                              ? "\t" + Localization.Device_uses_strobe_during_Data_Out_CRC_and_CMD_responses
                              : "\t" + Localization.Device_uses_strobe_during_Data_Out_and_CRC_responses);
        }

        switch(csd.BusWidth & 0x0F)
        {
            case 0:
                sb.AppendLine("\t" + Localization.Device_is_using_1bit_data_bus);

                break;
            case 1:
                sb.AppendLine("\t" + Localization.Device_is_using_4bit_data_bus);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.Device_is_using_8bit_data_bus);

                break;
            case 5:
                sb.AppendLine("\t" + Localization.Device_is_using_4bit_DDR_data_bus);

                break;
            case 6:
                sb.AppendLine("\t" + Localization.Device_is_using_8bit_DDR_data_bus);

                break;
            default:
                sb.AppendFormat("\t" + Localization.Device_is_using_unknown_data_bus_code_0, csd.BusWidth & 0x0F)
                  .AppendLine();

                break;
        }

        switch(csd.ErasedMemoryContent)
        {
            case 0:
            case 1:
                sb.AppendFormat("\t" + Localization.Erased_memory_range_shall_be_0, csd.ErasedMemoryContent)
                  .AppendLine();

                break;
            default:
                sb.AppendFormat("\t" + Localization.Unknown_erased_memory_content_code_0, csd.ErasedMemoryContent)
                  .AppendLine();

                break;
        }

        if((csd.PartitionConfiguration & 0x40) == 0x40)
            sb.AppendLine("\t" + Localization.Device_sends_boot_acknowledge);

        switch((csd.PartitionConfiguration & 0x38) >> 3)
        {
            case 0:
                sb.AppendLine("\t" + Localization.Device_is_not_boot_enabled);

                break;
            case 1:
                sb.AppendLine("\t" + Localization.Device_boot_partition_one_is_enabled);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.Device_boot_partition_two_is_enabled);

                break;
            case 7:
                sb.AppendLine("\t" + Localization.Device_user_area_is_enable_for_boot);

                break;
            default:
                sb.AppendFormat("\t" + Localization.Unknown_enabled_boot_partition_code_0,
                                (csd.PartitionConfiguration & 0x38) >> 3)
                  .AppendLine();

                break;
        }

        switch(csd.PartitionConfiguration & 0x07)
        {
            case 0:
                sb.AppendLine("\t" + Localization.There_is_no_access_to_boot_partition);

                break;
            case 1:
                sb.AppendLine("\t" + Localization.There_is_read_write_access_to_boot_partition_one);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.There_is_read_write_access_to_boot_partition_two);

                break;
            case 3:
                sb.AppendLine("\t" + Localization.There_is_read_write_access_to_replay_protected_memory_block);

                break;
            default:
                sb.AppendFormat("\t" + Localization.There_is_access_to_general_purpose_partition_0,
                                (csd.PartitionConfiguration & 0x07) - 3)
                  .AppendLine();

                break;
        }

        if(csd.BootConfigProtection.HasFlag(BootConfigProtection.Permanent))
            sb.AppendLine("\t" + Localization.Change_of_the_boot_configuration_register_bits_is_permanently_disabled);
        else if(csd.BootConfigProtection.HasFlag(BootConfigProtection.PowerCycle))
        {
            sb.AppendLine("\t" +
                          Localization
                             .Change_of_the_boot_configuration_register_bits_is_disabled_until_the_next_power_cycle);
        }

        switch(csd.BootBusConditions & 0x03)
        {
            case 0:
                sb.AppendLine("\t" + Localization.Device_will_boot_up_in_x1_SDR_or_x4_DDR_bus_width);

                break;
            case 1:
                sb.AppendLine("\t" + Localization.Device_will_boot_up_in_x4_SDR_or_DDR_bus_width);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.Device_will_boot_up_in_x8_SDR_or_DDR_bus_width);

                break;
            case 3:
                sb.AppendLine("\t" + Localization.Unknown_boot_condition_for_bus_width_with_code_three);

                break;
        }

        sb.AppendLine((csd.BootBusConditions & 4) == 4
                          ? "\t" + Localization.Device_will_retain_boot_conditions_after_boot_operation
                          : "\t" +
                            Localization.Device_will_reset_boot_conditions_to_compatibility_mode_after_boot_operation);

        switch((csd.BootBusConditions & 0x24) >> 3)
        {
            case 0:
                sb.AppendLine("\t" +
                              Localization.Device_will_use_single_data_rate_with_compatible_timings_in_boot_operation);

                break;
            case 1:
                sb.AppendLine("\t" +
                              Localization.Device_will_use_single_data_rate_with_high_speed_timings_in_boot_operation);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.Device_will_use_dual_data_rate_in_boot_operation);

                break;
            case 3:
                sb.AppendLine("\t" + Localization.Device_will_use_unknown_boot_mode_with_code_three);

                break;
        }

        if(csd.HighCapacityEraseGroupDefinition.HasFlag(HighCapacityEraseGroupDefinition.Enabled))
        {
            sb.AppendLine("\t" +
                          Localization
                             .Device_will_use_high_capacity_erase_unit_size__timeout_and_write_protect_group_size_definitions);
        }

        switch(csd.BootWriteProtectionStatus & 0x03)
        {
            case 0:
                sb.AppendLine("\t" + Localization.Boot_area_one_is_not_protected);

                break;
            case 1:
                sb.AppendLine("\t" + Localization.Boot_area_one_is_power_on_protected);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.Boot_area_one_is_permanently_protected);

                break;
        }

        switch((csd.BootWriteProtectionStatus & 0x0C) >> 2)
        {
            case 0:
                sb.AppendLine("\t" + Localization.Boot_area_two_is_not_protected);

                break;
            case 1:
                sb.AppendLine("\t" + Localization.Boot_area_two_is_power_on_protected);

                break;
            case 2:
                sb.AppendLine("\t" + Localization.Boot_area_two_is_permanently_protected);

                break;
        }

        if(csd.BootAreaWriteProtectionRegister.HasFlag(BootAreaWriteProtectionRegister.Permanent))
        {
            if(csd.BootAreaWriteProtectionRegister.HasFlag(BootAreaWriteProtectionRegister.Selected))
            {
                sb.AppendLine(csd.BootAreaWriteProtectionRegister.HasFlag(BootAreaWriteProtectionRegister
                                                                             .PermanentArea2)
                                  ? "\t" + Localization.Boot_area_two_is_permanently_write_protected
                                  : "\t" + Localization.Boot_area_one_is_permanently_write_protected);
            }
            else
                sb.AppendLine("\t" + Localization.Both_boot_areas_are_permanently_write_protected);
        }
        else if(csd.BootAreaWriteProtectionRegister.HasFlag(BootAreaWriteProtectionRegister.PowerOn))
        {
            if(csd.BootAreaWriteProtectionRegister.HasFlag(BootAreaWriteProtectionRegister.Selected))
            {
                sb.AppendLine(csd.BootAreaWriteProtectionRegister.HasFlag(BootAreaWriteProtectionRegister.PowerOnArea2)
                                  ? "\t" + Localization.Boot_area_two_is_write_protected_until_next_power_cycle
                                  : "\t" + Localization.Boot_area_one_is_write_protected_until_next_power_cycle);
            }
            else
                sb.AppendLine("\t" + Localization.Both_boot_areas_are_write_protected_until_next_power_cycle);
        }

        if(csd.BootAreaWriteProtectionRegister.HasFlag(BootAreaWriteProtectionRegister.PermanentDisable))
            sb.AppendLine("\t" + Localization.Permanent_write_protection_of_boot_areas_is_disabled);

        if(csd.BootAreaWriteProtectionRegister.HasFlag(BootAreaWriteProtectionRegister.PowerOnDisable))
            sb.AppendLine("\t" + Localization.Power_cycled_write_protection_of_boot_areas_is_disabled);

        if(csd.UserAreaWriteProtectionRegister.HasFlag(UserAreaWriteProtectionRegister.DisablePassword))
            sb.AppendLine("\t" + Localization.Use_of_password_protection_features_is_permanently_disabled);

        if(csd.UserAreaWriteProtectionRegister.HasFlag(UserAreaWriteProtectionRegister.DisableWriteProtect))
            sb.AppendLine("\t" + Localization.Use_of_permanent_write_protection_is_disabled);

        if(csd.UserAreaWriteProtectionRegister.HasFlag(UserAreaWriteProtectionRegister.DisablePermanent))
            sb.AppendLine("\t" + Localization.Permanent_write_protection_is_disabled);

        if(csd.UserAreaWriteProtectionRegister.HasFlag(UserAreaWriteProtectionRegister.DisablePowerOn))
            sb.AppendLine("\t" + Localization.Power_cycled_write_protection_is_disabled);

        if(csd.UserAreaWriteProtectionRegister.HasFlag(UserAreaWriteProtectionRegister.ApplyPermanent))
            sb.AppendLine("\t" + Localization.Permanent_write_protection_will_be_applied_to_selected_group);

        if(csd.UserAreaWriteProtectionRegister.HasFlag(UserAreaWriteProtectionRegister.ApplyPowerOn))
            sb.AppendLine("\t" + Localization.Power_cycled_write_protection_will_be_applied_to_selected_group);

        if((csd.FirmwareConfiguration & 0x01) == 0x01)
            sb.AppendLine("\t" + Localization.Firmware_updates_are_permanently_disabled);

        if(csd.RPMBSize > 0)
        {
            sb.AppendFormat("\t" + Localization.Device_has_a_0_KiB_replay_protected_memory_block, csd.RPMBSize * 128)
              .AppendLine();
        }

        if(csd.PartitioningSupport.HasFlag(PartitioningSupport.Supported))
        {
            sb.AppendLine("\t" + Localization.Device_supports_partitioning_features);

            if(csd.PartitioningSupport.HasFlag(PartitioningSupport.Enhanced))
            {
                sb.AppendLine("\t" +
                              Localization
                                 .Device_can_have_enhanced_technological_features_in_partitions_and_user_data_area);
            }

            if(csd.PartitioningSupport.HasFlag(PartitioningSupport.Extended))
                sb.AppendLine("\t" + Localization.Device_can_have_extended_partitions_attribute);
        }

        switch(csd.NativeSectorSize)
        {
            case 0:
                sb.AppendLine("\t" + Localization.Device_natively_uses_512_byte_sectors);

                break;
            case 1:
                sb.AppendLine("\t" + Localization.Device_natively_uses_4096_byte_sectors);

                break;
            default:
                sb.AppendFormat("\t" + Localization.Device_natively_uses_unknown_sector_size_indicated_by_code_0,
                                csd.NativeSectorSize)
                  .AppendLine();

                break;
        }

        switch(csd.SectorSizeEmulation)
        {
            case 0:
                sb.AppendLine("\t" + Localization.Device_is_emulating_512_byte_sectors);

                break;
            case 1:
                sb.AppendLine("\t" + Localization.Device_is_using_natively_sized_sectors);

                break;
            default:
                sb.AppendFormat("\t" + Localization.Device_emulates_unknown_sector_size_indicated_by_code_0,
                                csd.NativeSectorSize)
                  .AppendLine();

                break;
        }

        switch(csd.SectorSize)
        {
            case 0:
                sb.AppendLine("\t" + Localization.Device_currently_addresses_512_byte_sectors);

                break;
            case 1:
                sb.AppendLine("\t" + Localization.Device_currently_addresses_4096_byte_sectors);

                break;
            default:
                sb.AppendFormat("\t" + Localization.Device_currently_addresses_unknown_sector_size_indicated_by_code_0,
                                csd.NativeSectorSize)
                  .AppendLine();

                break;
        }

        if((csd.CacheControl & 0x01) == 0x01) sb.AppendLine("\t" + Localization.Devices_cache_is_enabled);

        if((csd.CommandQueueModeEnable & 0x01) == 0x01)
            sb.AppendLine("\t" + Localization.Device_has_enabled_command_queuing);

        return sb.ToString();
    }

    public static string PrettifyExtendedCSD(byte[] response) => PrettifyExtendedCSD(DecodeExtendedCSD(response));
}