Esempio n. 1
0
        public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
                           Dictionary <string, string> options, string @namespace)
        {
            Encoding = encoding ?? Encoding.GetEncoding(1252);
            byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001"
            byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM"

            options ??= GetDefaultOptions();

            if (options.TryGetValue("debug", out string debugString))
            {
                bool.TryParse(debugString, out _debug);
            }

            if (options.TryGetValue("use_path_table", out string usePathTableString))
            {
                bool.TryParse(usePathTableString, out _usePathTable);
            }

            if (options.TryGetValue("use_trans_tbl", out string useTransTblString))
            {
                bool.TryParse(useTransTblString, out _useTransTbl);
            }

            if (options.TryGetValue("use_evd", out string useEvdString))
            {
                bool.TryParse(useEvdString, out _useEvd);
            }

            // Default namespace
            @namespace ??= "joliet";

            switch (@namespace.ToLowerInvariant())
            {
            case "normal":
                _namespace = Namespace.Normal;

                break;

            case "vms":
                _namespace = Namespace.Vms;

                break;

            case "joliet":
                _namespace = Namespace.Joliet;

                break;

            case "rrip":
                _namespace = Namespace.Rrip;

                break;

            case "romeo":
                _namespace = Namespace.Romeo;

                break;

            default: return(Errno.InvalidArgument);
            }

            PrimaryVolumeDescriptor?pvd      = null;
            PrimaryVolumeDescriptor?jolietvd = null;
            BootRecord?bvd = null;
            HighSierraPrimaryVolumeDescriptor?hsvd = null;
            FileStructureVolumeDescriptor?    fsvd = null;

            // ISO9660 is designed for 2048 bytes/sector devices
            if (imagePlugin.Info.SectorSize < 2048)
            {
                return(Errno.InvalidArgument);
            }

            // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size.
            if (partition.End < 16)
            {
                return(Errno.InvalidArgument);
            }

            ulong counter = 0;

            byte[] vdSector = imagePlugin.ReadSector(16 + counter + partition.Start);
            int    xaOff    = vdSector.Length == 2336 ? 8 : 0;

            Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5);
            _highSierra = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC;
            int hsOff = 0;

            if (_highSierra)
            {
                hsOff = 8;
            }

            _cdi = false;
            List <ulong> bvdSectors = new List <ulong>();
            List <ulong> pvdSectors = new List <ulong>();
            List <ulong> svdSectors = new List <ulong>();
            List <ulong> evdSectors = new List <ulong>();
            List <ulong> vpdSectors = new List <ulong>();

            while (true)
            {
                AaruConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter);

                // Seek to Volume Descriptor
                AaruConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start);
                byte[] vdSectorTmp = imagePlugin.ReadSector(16 + counter + partition.Start);
                vdSector = new byte[vdSectorTmp.Length - xaOff];
                Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length);

                byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2.
                AaruConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType);

                if (vdType == 255) // Supposedly we are in the PVD.
                {
                    if (counter == 0)
                    {
                        return(Errno.InvalidArgument);
                    }

                    break;
                }

                Array.Copy(vdSector, 0x001, vdMagic, 0, 5);
                Array.Copy(vdSector, 0x009, hsMagic, 0, 5);

                if (Encoding.GetString(vdMagic) != ISO_MAGIC &&
                    Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC &&
                    Encoding.GetString(vdMagic) != CDI_MAGIC
                    ) // Recognized, it is an ISO9660, now check for rest of data.
                {
                    if (counter == 0)
                    {
                        return(Errno.InvalidArgument);
                    }

                    break;
                }

                _cdi |= Encoding.GetString(vdMagic) == CDI_MAGIC;

                switch (vdType)
                {
                case 0:
                {
                    if (_debug)
                    {
                        bvdSectors.Add(16 + counter + partition.Start);
                    }

                    break;
                }

                case 1:
                {
                    if (_highSierra)
                    {
                        hsvd = Marshal.
                               ByteArrayToStructureLittleEndian <HighSierraPrimaryVolumeDescriptor>(vdSector);
                    }
                    else if (_cdi)
                    {
                        fsvd = Marshal.ByteArrayToStructureBigEndian <FileStructureVolumeDescriptor>(vdSector);
                    }
                    else
                    {
                        pvd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector);
                    }

                    if (_debug)
                    {
                        pvdSectors.Add(16 + counter + partition.Start);
                    }

                    break;
                }

                case 2:
                {
                    PrimaryVolumeDescriptor svd =
                        Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector);

                    // TODO: Other escape sequences
                    // Check if this is Joliet
                    if (svd.version == 1)
                    {
                        if (svd.escape_sequences[0] == '%' &&
                            svd.escape_sequences[1] == '/')
                        {
                            if (svd.escape_sequences[2] == '@' ||
                                svd.escape_sequences[2] == 'C' ||
                                svd.escape_sequences[2] == 'E')
                            {
                                jolietvd = svd;
                            }
                            else
                            {
                                AaruConsole.DebugWriteLine("ISO9660 plugin",
                                                           "Found unknown supplementary volume descriptor");
                            }
                        }

                        if (_debug)
                        {
                            svdSectors.Add(16 + counter + partition.Start);
                        }
                    }
                    else
                    {
                        if (_debug)
                        {
                            evdSectors.Add(16 + counter + partition.Start);
                        }

                        if (_useEvd)
                        {
                            // Basically until escape sequences are implemented, let the user chose the encoding.
                            // This is the same as user choosing Romeo namespace, but using the EVD instead of the PVD
                            _namespace = Namespace.Romeo;
                            pvd        = svd;
                        }
                    }

                    break;
                }

                case 3:
                {
                    if (_debug)
                    {
                        vpdSectors.Add(16 + counter + partition.Start);
                    }

                    break;
                }
                }

                counter++;
            }

            DecodedVolumeDescriptor decodedVd;
            var decodedJolietVd = new DecodedVolumeDescriptor();

            XmlFsType = new FileSystemType();

            if (pvd == null &&
                hsvd == null &&
                fsvd == null)
            {
                AaruConsole.ErrorWriteLine("ERROR: Could not find primary volume descriptor");

                return(Errno.InvalidArgument);
            }

            if (_highSierra)
            {
                decodedVd = DecodeVolumeDescriptor(hsvd.Value);
            }
            else if (_cdi)
            {
                decodedVd = DecodeVolumeDescriptor(fsvd.Value);
            }
            else
            {
                decodedVd = DecodeVolumeDescriptor(pvd.Value);
            }

            if (jolietvd != null)
            {
                decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value);
            }

            if (_namespace != Namespace.Romeo)
            {
                Encoding = Encoding.ASCII;
            }

            string fsFormat;

            byte[] pathTableData;

            uint pathTableMsbLocation;
            uint pathTableLsbLocation = 0; // Initialize to 0 as ignored in CD-i

            _image = imagePlugin;

            if (_highSierra)
            {
                pathTableData = ReadSingleExtent(0, hsvd.Value.path_table_size,
                                                 Swapping.Swap(hsvd.Value.mandatory_path_table_msb));

                fsFormat = "High Sierra Format";

                pathTableMsbLocation = hsvd.Value.mandatory_path_table_msb;
                pathTableLsbLocation = hsvd.Value.mandatory_path_table_lsb;
            }
            else if (_cdi)
            {
                pathTableData = ReadSingleExtent(0, fsvd.Value.path_table_size, fsvd.Value.path_table_addr);

                fsFormat = "CD-i";

                pathTableMsbLocation = fsvd.Value.path_table_addr;

                // TODO: Until escape sequences are implemented this is the default CD-i encoding.
                Encoding = Encoding.GetEncoding("iso8859-1");
            }
            else
            {
                pathTableData =
                    ReadSingleExtent(0, pvd.Value.path_table_size, Swapping.Swap(pvd.Value.type_m_path_table));

                fsFormat = "ISO9660";

                pathTableMsbLocation = pvd.Value.type_m_path_table;
                pathTableLsbLocation = pvd.Value.type_l_path_table;
            }

            _pathTable = _highSierra ? DecodeHighSierraPathTable(pathTableData) : DecodePathTable(pathTableData);

            // High Sierra and CD-i do not support Joliet or RRIP
            if ((_highSierra || _cdi) &&
                _namespace != Namespace.Normal &&
                _namespace != Namespace.Vms)
            {
                _namespace = Namespace.Normal;
            }

            if (jolietvd is null &&
                _namespace == Namespace.Joliet)
            {
                _namespace = Namespace.Normal;
            }

            uint rootLocation;
            uint rootSize;
            byte rootXattrLength = 0;

            if (!_cdi)
            {
                rootLocation = _highSierra ? hsvd.Value.root_directory_record.extent
                                   : pvd.Value.root_directory_record.extent;

                rootXattrLength = _highSierra ? hsvd.Value.root_directory_record.xattr_len
                                      : pvd.Value.root_directory_record.xattr_len;

                rootSize = _highSierra ? hsvd.Value.root_directory_record.size : pvd.Value.root_directory_record.size;

                if (pathTableData.Length > 1 &&
                    rootLocation != _pathTable[0].Extent)
                {
                    AaruConsole.DebugWriteLine("ISO9660 plugin",
                                               "Path table and PVD do not point to the same location for the root directory!");

                    byte[] firstRootSector = ReadSector(rootLocation);

                    bool pvdWrongRoot = false;

                    if (_highSierra)
                    {
                        HighSierraDirectoryRecord rootEntry =
                            Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector);

                        if (rootEntry.extent != rootLocation)
                        {
                            pvdWrongRoot = true;
                        }
                    }
                    else
                    {
                        DirectoryRecord rootEntry =
                            Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector);

                        if (rootEntry.extent != rootLocation)
                        {
                            pvdWrongRoot = true;
                        }
                    }

                    if (pvdWrongRoot)
                    {
                        AaruConsole.DebugWriteLine("ISO9660 plugin",
                                                   "PVD does not point to correct root directory, checking path table...");

                        bool pathTableWrongRoot = false;

                        rootLocation = _pathTable[0].Extent;

                        firstRootSector = ReadSector(_pathTable[0].Extent);

                        if (_highSierra)
                        {
                            HighSierraDirectoryRecord rootEntry =
                                Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector);

                            if (rootEntry.extent != rootLocation)
                            {
                                pathTableWrongRoot = true;
                            }
                        }
                        else
                        {
                            DirectoryRecord rootEntry =
                                Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector);

                            if (rootEntry.extent != rootLocation)
                            {
                                pathTableWrongRoot = true;
                            }
                        }

                        if (pathTableWrongRoot)
                        {
                            AaruConsole.ErrorWriteLine("Cannot find root directory...");

                            return(Errno.InvalidArgument);
                        }

                        _usePathTable = true;
                    }
                }
            }
            else
            {
                rootLocation = _pathTable[0].Extent;

                byte[] firstRootSector = ReadSector(rootLocation);

                CdiDirectoryRecord rootEntry =
                    Marshal.ByteArrayToStructureBigEndian <CdiDirectoryRecord>(firstRootSector);

                rootSize = rootEntry.size;

                _usePathTable = _usePathTable || _pathTable.Length == 1;
                _useTransTbl  = false;
            }

            // In case the path table is incomplete
            if (_usePathTable && pathTableData.Length == 1)
            {
                _usePathTable = false;
            }

            if (_usePathTable && !_cdi)
            {
                rootLocation = _pathTable[0].Extent;

                byte[] firstRootSector = ReadSector(rootLocation);

                if (_highSierra)
                {
                    HighSierraDirectoryRecord rootEntry =
                        Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector);

                    rootSize = rootEntry.size;
                }
                else
                {
                    DirectoryRecord rootEntry =
                        Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector);

                    rootSize = rootEntry.size;
                }

                rootXattrLength = _pathTable[0].XattrLength;
            }

            try
            {
                _ = ReadSingleExtent(0, rootSize, rootLocation);
            }
            catch
            {
                return(Errno.InvalidArgument);
            }

            byte[]          ipbinSector = ReadSector(partition.Start);
            CD.IPBin?       segaCd      = CD.DecodeIPBin(ipbinSector);
            Saturn.IPBin?   saturn      = Saturn.DecodeIPBin(ipbinSector);
            Dreamcast.IPBin?dreamcast   = Dreamcast.DecodeIPBin(ipbinSector);

            if (_namespace == Namespace.Joliet ||
                _namespace == Namespace.Rrip)
            {
                _usePathTable = false;
                _useTransTbl  = false;
            }

            // Cannot traverse path table if we substitute the names for the ones in TRANS.TBL
            if (_useTransTbl)
            {
                _usePathTable = false;
            }

            if (_namespace != Namespace.Joliet)
            {
                _rootDirectoryCache = _cdi
                                          ? DecodeCdiDirectory(rootLocation, rootSize, rootXattrLength)
                                          : _highSierra
                                              ? DecodeHighSierraDirectory(rootLocation, rootSize, rootXattrLength)
                                              : DecodeIsoDirectory(rootLocation, rootSize, rootXattrLength);
            }

            XmlFsType.Type = fsFormat;

            if (jolietvd != null &&
                (_namespace == Namespace.Joliet || _namespace == Namespace.Rrip))
            {
                rootLocation    = jolietvd.Value.root_directory_record.extent;
                rootXattrLength = jolietvd.Value.root_directory_record.xattr_len;

                rootSize = jolietvd.Value.root_directory_record.size;

                _joliet = true;

                _rootDirectoryCache = DecodeIsoDirectory(rootLocation, rootSize, rootXattrLength);

                XmlFsType.VolumeName = decodedJolietVd.VolumeIdentifier;

                if (string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ||
                    decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length)
                {
                    XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier;
                }
                else
                {
                    XmlFsType.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null
                                                     : decodedJolietVd.SystemIdentifier;
                }

                if (string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ||
                    decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length)
                {
                    XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier;
                }
                else
                {
                    XmlFsType.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null
                                                        : decodedJolietVd.VolumeSetIdentifier;
                }

                if (string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ||
                    decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length)
                {
                    XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier;
                }
                else
                {
                    XmlFsType.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null
                                                        : decodedJolietVd.PublisherIdentifier;
                }

                if (string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) ||
                    decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length)
                {
                    XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier;
                }
                else
                {
                    XmlFsType.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier)
                                                           ? null : decodedJolietVd.DataPreparerIdentifier;
                }

                if (string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ||
                    decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length)
                {
                    XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier;
                }
                else
                {
                    XmlFsType.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null
                                                          : decodedJolietVd.ApplicationIdentifier;
                }

                XmlFsType.CreationDate          = decodedJolietVd.CreationTime;
                XmlFsType.CreationDateSpecified = true;

                if (decodedJolietVd.HasModificationTime)
                {
                    XmlFsType.ModificationDate          = decodedJolietVd.ModificationTime;
                    XmlFsType.ModificationDateSpecified = true;
                }

                if (decodedJolietVd.HasExpirationTime)
                {
                    XmlFsType.ExpirationDate          = decodedJolietVd.ExpirationTime;
                    XmlFsType.ExpirationDateSpecified = true;
                }

                if (decodedJolietVd.HasEffectiveTime)
                {
                    XmlFsType.EffectiveDate          = decodedJolietVd.EffectiveTime;
                    XmlFsType.EffectiveDateSpecified = true;
                }

                decodedVd = decodedJolietVd;
            }
            else
            {
                XmlFsType.SystemIdentifier       = decodedVd.SystemIdentifier;
                XmlFsType.VolumeName             = decodedVd.VolumeIdentifier;
                XmlFsType.VolumeSetIdentifier    = decodedVd.VolumeSetIdentifier;
                XmlFsType.PublisherIdentifier    = decodedVd.PublisherIdentifier;
                XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier;
                XmlFsType.ApplicationIdentifier  = decodedVd.ApplicationIdentifier;
                XmlFsType.CreationDate           = decodedVd.CreationTime;
                XmlFsType.CreationDateSpecified  = true;

                if (decodedVd.HasModificationTime)
                {
                    XmlFsType.ModificationDate          = decodedVd.ModificationTime;
                    XmlFsType.ModificationDateSpecified = true;
                }

                if (decodedVd.HasExpirationTime)
                {
                    XmlFsType.ExpirationDate          = decodedVd.ExpirationTime;
                    XmlFsType.ExpirationDateSpecified = true;
                }

                if (decodedVd.HasEffectiveTime)
                {
                    XmlFsType.EffectiveDate          = decodedVd.EffectiveTime;
                    XmlFsType.EffectiveDateSpecified = true;
                }
            }

            if (_debug)
            {
                _rootDirectoryCache.Add("$", new DecodedDirectoryEntry
                {
                    Extents = new List <(uint extent, uint size)>
                    {
                        (rootLocation, rootSize)
                    },
                    Filename  = "$",
                    Size      = rootSize,
                    Timestamp = decodedVd.CreationTime
                });
Esempio n. 2
0
        public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
                           Dictionary <string, string> options, string @namespace)
        {
            Encoding = encoding ?? Encoding.GetEncoding(1252);
            byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001"
            byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM"

            if (options == null)
            {
                options = GetDefaultOptions();
            }
            if (options.TryGetValue("debug", out string debugString))
            {
                bool.TryParse(debugString, out debug);
            }
            if (options.TryGetValue("use_path_table", out string usePathTableString))
            {
                bool.TryParse(usePathTableString, out usePathTable);
            }
            if (options.TryGetValue("use_trans_tbl", out string useTransTblString))
            {
                bool.TryParse(useTransTblString, out useTransTbl);
            }
            if (options.TryGetValue("use_evd", out string useEvdString))
            {
                bool.TryParse(useEvdString, out useEvd);
            }

            // Default namespace
            if (@namespace is null)
            {
                @namespace = "joliet";
            }

            switch (@namespace.ToLowerInvariant())
            {
            case "normal":
                this.@namespace = Namespace.Normal;
                break;

            case "vms":
                this.@namespace = Namespace.Vms;
                break;

            case "joliet":
                this.@namespace = Namespace.Joliet;
                break;

            case "rrip":
                this.@namespace = Namespace.Rrip;
                break;

            case "romeo":
                this.@namespace = Namespace.Romeo;
                break;

            default: return(Errno.InvalidArgument);
            }

            PrimaryVolumeDescriptor?pvd      = null;
            PrimaryVolumeDescriptor?jolietvd = null;
            BootRecord?bvd = null;
            HighSierraPrimaryVolumeDescriptor?hsvd = null;
            FileStructureVolumeDescriptor?    fsvd = null;

            // ISO9660 is designed for 2048 bytes/sector devices
            if (imagePlugin.Info.SectorSize < 2048)
            {
                return(Errno.InvalidArgument);
            }

            // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size.
            if (partition.End < 16)
            {
                return(Errno.InvalidArgument);
            }

            ulong counter = 0;

            byte[] vdSector = imagePlugin.ReadSector(16 + counter + partition.Start);
            int    xaOff    = vdSector.Length == 2336 ? 8 : 0;

            Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5);
            highSierra = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC;
            int hsOff = 0;

            if (highSierra)
            {
                hsOff = 8;
            }
            cdi = false;
            List <ulong> bvdSectors = new List <ulong>();
            List <ulong> pvdSectors = new List <ulong>();
            List <ulong> svdSectors = new List <ulong>();
            List <ulong> evdSectors = new List <ulong>();
            List <ulong> vpdSectors = new List <ulong>();

            while (true)
            {
                DicConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter);
                // Seek to Volume Descriptor
                DicConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start);
                byte[] vdSectorTmp = imagePlugin.ReadSector(16 + counter + partition.Start);
                vdSector = new byte[vdSectorTmp.Length - xaOff];
                Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length);

                byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2.
                DicConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType);

                if (vdType == 255) // Supposedly we are in the PVD.
                {
                    if (counter == 0)
                    {
                        return(Errno.InvalidArgument);
                    }

                    break;
                }

                Array.Copy(vdSector, 0x001, vdMagic, 0, 5);
                Array.Copy(vdSector, 0x009, hsMagic, 0, 5);

                if (Encoding.GetString(vdMagic) != ISO_MAGIC && Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC &&
                    Encoding.GetString(vdMagic) != CDI_MAGIC
                    ) // Recognized, it is an ISO9660, now check for rest of data.
                {
                    if (counter == 0)
                    {
                        return(Errno.InvalidArgument);
                    }

                    break;
                }

                cdi |= Encoding.GetString(vdMagic) == CDI_MAGIC;

                switch (vdType)
                {
                case 0:
                {
                    if (debug)
                    {
                        bvdSectors.Add(16 + counter + partition.Start);
                    }

                    break;
                }

                case 1:
                {
                    if (highSierra)
                    {
                        hsvd = Marshal
                               .ByteArrayToStructureLittleEndian <HighSierraPrimaryVolumeDescriptor>(vdSector);
                    }
                    else if (cdi)
                    {
                        fsvd = Marshal.ByteArrayToStructureBigEndian <FileStructureVolumeDescriptor>(vdSector);
                    }
                    else
                    {
                        pvd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector);
                    }

                    if (debug)
                    {
                        pvdSectors.Add(16 + counter + partition.Start);
                    }

                    break;
                }

                case 2:
                {
                    PrimaryVolumeDescriptor svd =
                        Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector);

                    // TODO: Other escape sequences
                    // Check if this is Joliet
                    if (svd.version == 1)
                    {
                        if (svd.escape_sequences[0] == '%' && svd.escape_sequences[1] == '/')
                        {
                            if (svd.escape_sequences[2] == '@' || svd.escape_sequences[2] == 'C' ||
                                svd.escape_sequences[2] == 'E')
                            {
                                jolietvd = svd;
                            }
                            else
                            {
                                DicConsole.WriteLine("ISO9660 plugin",
                                                     "Found unknown supplementary volume descriptor");
                            }
                        }
                        if (debug)
                        {
                            svdSectors.Add(16 + counter + partition.Start);
                        }
                    }
                    else
                    {
                        if (debug)
                        {
                            evdSectors.Add(16 + counter + partition.Start);
                        }

                        if (useEvd)
                        {
                            // Basically until escape sequences are implemented, let the user chose the encoding.
                            // This is the same as user chosing Romeo namespace, but using the EVD instead of the PVD
                            this.@namespace = Namespace.Romeo;
                            pvd             = svd;
                        }
                    }

                    break;
                }

                case 3:
                {
                    if (debug)
                    {
                        vpdSectors.Add(16 + counter + partition.Start);
                    }

                    break;
                }
                }

                counter++;
            }

            DecodedVolumeDescriptor decodedVd;
            DecodedVolumeDescriptor decodedJolietVd = new DecodedVolumeDescriptor();

            XmlFsType = new FileSystemType();

            if (pvd == null && hsvd == null && fsvd == null)
            {
                DicConsole.ErrorWriteLine("ERROR: Could not find primary volume descriptor");
                return(Errno.InvalidArgument);
            }

            if (highSierra)
            {
                decodedVd = DecodeVolumeDescriptor(hsvd.Value);
            }
            else if (cdi)
            {
                decodedVd = DecodeVolumeDescriptor(fsvd.Value);
            }
            else
            {
                decodedVd = DecodeVolumeDescriptor(pvd.Value);
            }

            if (jolietvd != null)
            {
                decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value);
            }

            if (this.@namespace != Namespace.Romeo)
            {
                Encoding = Encoding.ASCII;
            }

            string fsFormat;

            byte[] pathTableData;
            uint   pathTableSizeInSectors;

            uint pathTableMsbLocation;
            uint pathTableLsbLocation = 0; // Initialize to 0 as ignored in CD-i

            image = imagePlugin;

            if (highSierra)
            {
                pathTableSizeInSectors = hsvd.Value.path_table_size / 2048;
                if (hsvd.Value.path_table_size % 2048 > 0)
                {
                    pathTableSizeInSectors++;
                }

                pathTableData = ReadSectors(Swapping.Swap(hsvd.Value.mandatory_path_table_msb), pathTableSizeInSectors);

                fsFormat = "High Sierra Format";

                pathTableMsbLocation = hsvd.Value.mandatory_path_table_msb;
                pathTableLsbLocation = hsvd.Value.mandatory_path_table_lsb;
            }
            else if (cdi)
            {
                pathTableSizeInSectors = fsvd.Value.path_table_size / 2048;
                if (fsvd.Value.path_table_size % 2048 > 0)
                {
                    pathTableSizeInSectors++;
                }

                pathTableData = ReadSectors(fsvd.Value.path_table_addr, pathTableSizeInSectors);

                fsFormat = "CD-i";

                pathTableMsbLocation = fsvd.Value.path_table_addr;

                // TODO: Until escape sequences are implemented this is the default CD-i encoding.
                Encoding = Encoding.GetEncoding("iso8859-1");
            }
            else
            {
                pathTableSizeInSectors = pvd.Value.path_table_size / 2048;
                if (pvd.Value.path_table_size % 2048 > 0)
                {
                    pathTableSizeInSectors++;
                }

                pathTableData = ReadSectors(Swapping.Swap(pvd.Value.type_m_path_table), pathTableSizeInSectors);

                fsFormat = "ISO9660";

                pathTableMsbLocation = pvd.Value.type_m_path_table;
                pathTableLsbLocation = pvd.Value.type_l_path_table;
            }

            pathTable = highSierra ? DecodeHighSierraPathTable(pathTableData) : DecodePathTable(pathTableData);

            // High Sierra and CD-i do not support Joliet or RRIP
            if ((highSierra || cdi) && this.@namespace != Namespace.Normal && this.@namespace != Namespace.Vms)
            {
                this.@namespace = Namespace.Normal;
            }

            if (jolietvd is null && this.@namespace == Namespace.Joliet)
            {
                this.@namespace = Namespace.Normal;
            }

            uint rootLocation;
            uint rootSize;
            byte rootXattrLength = 0;

            if (!cdi)
            {
                rootLocation = highSierra
                                   ? hsvd.Value.root_directory_record.extent
                                   : pvd.Value.root_directory_record.extent;

                rootXattrLength = highSierra
                                      ? hsvd.Value.root_directory_record.xattr_len
                                      : pvd.Value.root_directory_record.xattr_len;

                if (highSierra)
                {
                    rootSize = hsvd.Value.root_directory_record.size / hsvd.Value.logical_block_size;
                    if (hsvd.Value.root_directory_record.size % hsvd.Value.logical_block_size > 0)
                    {
                        rootSize++;
                    }
                }
                else
                {
                    rootSize = pvd.Value.root_directory_record.size / pvd.Value.logical_block_size;
                    if (pvd.Value.root_directory_record.size % pvd.Value.logical_block_size > 0)
                    {
                        rootSize++;
                    }
                }
            }
            else
            {
                rootLocation = pathTable[0].Extent;

                byte[]             firstRootSector = ReadSectors(rootLocation, 1);
                CdiDirectoryRecord rootEntry       =
                    Marshal.ByteArrayToStructureBigEndian <CdiDirectoryRecord>(firstRootSector);
                rootSize = rootEntry.size / fsvd.Value.logical_block_size;
                if (rootEntry.size % fsvd.Value.logical_block_size > 0)
                {
                    rootSize++;
                }

                usePathTable = usePathTable || pathTable.Length == 1;
                useTransTbl  = false;
            }

            // In case the path table is incomplete
            if (usePathTable && pathTableData.Length == 1)
            {
                usePathTable = false;
            }

            if (rootLocation + rootSize >= imagePlugin.Info.Sectors)
            {
                return(Errno.InvalidArgument);
            }

            byte[] rootDir = ReadSectors(rootLocation, rootSize);

            byte[]          ipbinSector = ReadSectors(partition.Start, 1);
            CD.IPBin?       segaCd      = CD.DecodeIPBin(ipbinSector);
            Saturn.IPBin?   saturn      = Saturn.DecodeIPBin(ipbinSector);
            Dreamcast.IPBin?dreamcast   = Dreamcast.DecodeIPBin(ipbinSector);

            if (this.@namespace == Namespace.Joliet || this.@namespace == Namespace.Rrip)
            {
                usePathTable = false;
                useTransTbl  = false;
            }

            // Cannot traverse path table if we substitute the names for the ones in TRANS.TBL
            if (useTransTbl)
            {
                usePathTable = false;
            }

            if (this.@namespace != Namespace.Joliet)
            {
                rootDirectoryCache = cdi
                                         ? DecodeCdiDirectory(rootLocation, rootSize, rootXattrLength)
                                         : highSierra
                                             ? DecodeHighSierraDirectory(rootLocation, rootSize, rootXattrLength)
                                             : DecodeIsoDirectory(rootLocation, rootSize, rootXattrLength);
            }

            XmlFsType.Type = fsFormat;

            if (debug)
            {
                rootDirectoryCache.Add("$",
                                       new DecodedDirectoryEntry
                {
                    Extents =
                        new List <(uint extent, uint size)>
                    {
                        (rootLocation, (uint)rootDir.Length)
                    },
                    Filename  = "$",
                    Size      = (uint)rootDir.Length,
                    Timestamp = decodedVd.CreationTime
                });