コード例 #1
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = Encoding.UTF8;
            information = "";
            if (imagePlugin.Info.SectorSize < 512)
            {
                return;
            }

            bool bigEndian = true;

            byte[] sector = imagePlugin.ReadSector(partition.Start);

            Superblock fatxSb = Marshal.ByteArrayToStructureBigEndian <Superblock>(sector);

            if (fatxSb.magic == FATX_CIGAM)
            {
                fatxSb    = Marshal.ByteArrayToStructureLittleEndian <Superblock>(sector);
                bigEndian = false;
            }

            if (fatxSb.magic != FATX_MAGIC)
            {
                return;
            }

            int logicalSectorsPerPhysicalSectors = partition.Offset == 0 && !bigEndian ? 8 : 1;

            StringBuilder sb = new StringBuilder();

            sb.AppendLine("FATX filesystem");
            sb.AppendFormat("{0} logical sectors ({1} bytes) per physical sector", logicalSectorsPerPhysicalSectors,
                            logicalSectorsPerPhysicalSectors * imagePlugin.Info.SectorSize).AppendLine();
            sb.AppendFormat("{0} sectors ({1} bytes) per cluster", fatxSb.sectorsPerCluster,
                            fatxSb.sectorsPerCluster * logicalSectorsPerPhysicalSectors * imagePlugin.Info.SectorSize)
            .AppendLine();
            sb.AppendFormat("Root directory starts on cluster {0}", fatxSb.rootDirectoryCluster).AppendLine();

            string volumeLabel = StringHandlers.CToString(fatxSb.volumeLabel,
                                                          bigEndian ? Encoding.BigEndianUnicode : Encoding.Unicode,
                                                          true);

            sb.AppendFormat("Volume label: {0}", volumeLabel).AppendLine();
            sb.AppendFormat("Volume serial: {0:X8}", fatxSb.id).AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Type        = "FATX filesystem",
                ClusterSize =
                    (uint)(fatxSb.sectorsPerCluster * logicalSectorsPerPhysicalSectors *
                           imagePlugin.Info.SectorSize),
                VolumeName   = volumeLabel,
                VolumeSerial = $"{fatxSb.id:X8}"
            };
            XmlFsType.Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize /
                                 XmlFsType.ClusterSize;
        }
コード例 #2
0
        public bool Identify(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            if (stream.Length < 512)
            {
                return(false);
            }

            stream.Seek(-Marshal.SizeOf <Footer>(), SeekOrigin.End);
            byte[] footerB = new byte[Marshal.SizeOf <Footer>()];

            stream.Read(footerB, 0, Marshal.SizeOf <Footer>());
            _footer = Marshal.ByteArrayToStructureBigEndian <Footer>(footerB);

            if (_footer.signature == UDIF_SIGNATURE)
            {
                return(true);
            }

            // Old UDIF as created by DiskCopy 6.5 using "OBSOLETE" format. (DiskCopy 5 rumored format?)
            stream.Seek(0, SeekOrigin.Begin);
            byte[] headerB = new byte[Marshal.SizeOf <Footer>()];

            stream.Read(headerB, 0, Marshal.SizeOf <Footer>());
            _footer = Marshal.ByteArrayToStructureBigEndian <Footer>(headerB);

            return(_footer.signature == UDIF_SIGNATURE);
        }
コード例 #3
0
ファイル: Helpers.cs プロジェクト: arrowsmith001/Aaru
        bool TryReadHeader(Stream stream, ref FileHeader fhdr, ref TrackInfo[] tmap, ref long[] toffsets)
        {
            int numTracks;
            int trackLen; // the length of a single track, in bytes

            TrackInfo[] trackMap;
            byte[]      buffer = new byte[6];
            FileHeader  fHeader;

            long[] trackOffsets;

            stream.Seek(0, SeekOrigin.Begin);

            if (stream.Length < 256)
            {
                return(false);
            }

            // read and check signature
            fHeader.signature = new byte[10];
            stream.Read(fHeader.signature, 0, 10);
            if (!fHeader.signature.SequenceEqual(_headerMagic))
            {
                return(false);
            }

            // read and check disk type byte
            fHeader.diskType = (byte)stream.ReadByte();
            if ((fHeader.diskType < 1) || (fHeader.diskType > 4))
            {
                return(false);
            }

            // seek to start of the trackmap
            stream.Seek(TRACKMAP_OFFSET, SeekOrigin.Begin);
            numTracks    = diskTypes[fHeader.diskType].cyl * diskTypes[fHeader.diskType].hd;
            trackLen     = 512 * diskTypes[fHeader.diskType].spt;
            trackMap     = new TrackInfo[numTracks];
            trackOffsets = new long[numTracks];

            AaruConsole.DebugWriteLine("DiskDupe plugin", "Identified image with C/H/S = {0}/{1}/{2}",
                                       diskTypes[fHeader.diskType].cyl, diskTypes[fHeader.diskType].hd, diskTypes[fHeader.diskType].spt);

            // read the trackmap and store the track offsets
            for (int i = 0; i < numTracks; i++)
            {
                stream.Read(buffer, 0, 6);
                trackMap[i]     = Marshal.ByteArrayToStructureBigEndian <TrackInfo>(buffer);
                trackOffsets[i] = trackLen * trackMap[i].trackNumber;
            }

            fhdr     = fHeader;
            tmap     = trackMap;
            toffsets = trackOffsets;
            return(true);
        }
コード例 #4
0
        Dictionary <string, DirectoryEntryWithPointers> DecodeDirectory(int firstBlock)
        {
            Dictionary <string, DirectoryEntryWithPointers> entries =
                new Dictionary <string, DirectoryEntryWithPointers>();

            int nextBlock = firstBlock;

            DirectoryHeader header;

            do
            {
                byte[] data = _image.ReadSectors((ulong)(nextBlock * _volumeBlockSizeRatio), _volumeBlockSizeRatio);
                header    = Marshal.ByteArrayToStructureBigEndian <DirectoryHeader>(data);
                nextBlock = header.next_block + firstBlock;

                int off = (int)header.first_used;

                var entry = new DirectoryEntry();

                while (off + _directoryEntrySize < data.Length)
                {
                    entry = Marshal.ByteArrayToStructureBigEndian <DirectoryEntry>(data, off, _directoryEntrySize);
                    string name = StringHandlers.CToString(entry.name, Encoding);

                    var entryWithPointers = new DirectoryEntryWithPointers
                    {
                        entry    = entry,
                        pointers = new uint[entry.last_copy + 1]
                    };

                    for (int i = 0; i <= entry.last_copy; i++)
                    {
                        entryWithPointers.pointers[i] =
                            BigEndianBitConverter.ToUInt32(data, off + _directoryEntrySize + (i * 4));
                    }

                    entries.Add(name, entryWithPointers);

                    if ((entry.flags & (uint)FileFlags.LastEntry) != 0 ||
                        (entry.flags & (uint)FileFlags.LastEntryInBlock) != 0)
                    {
                        break;
                    }

                    off += (int)(_directoryEntrySize + ((entry.last_copy + 1) * 4));
                }

                if ((entry.flags & (uint)FileFlags.LastEntry) != 0)
                {
                    break;
                }
            } while(header.next_block != -1);

            return(entries);
        }
コード例 #5
0
        PathTableEntryInternal[] DecodePathTable(byte[] data)
        {
            if (data is null)
            {
                return(null);
            }

            List <PathTableEntryInternal> table = new List <PathTableEntryInternal>();

            int off = 0;

            PathTableEntry entry =
                Marshal.ByteArrayToStructureBigEndian <PathTableEntry>(data, off, Marshal.SizeOf <PathTableEntry>());

            if (entry.name_len != 1 ||
                entry.parent_dirno != 1 ||
                data.Length <= Marshal.SizeOf <PathTableEntry>() ||
                data[Marshal.SizeOf <PathTableEntry>()] != 0x00)
            {
                return(null);
            }

            while (off < data.Length)
            {
                entry =
                    Marshal.ByteArrayToStructureBigEndian <PathTableEntry>(data, off, Marshal.SizeOf <PathTableEntry>());

                if (entry.name_len == 0)
                {
                    break;
                }

                off += Marshal.SizeOf <PathTableEntry>();

                string name = Encoding.GetString(data, off, entry.name_len);

                table.Add(new PathTableEntryInternal
                {
                    Extent      = entry.start_lbn,
                    Name        = name,
                    Parent      = entry.parent_dirno,
                    XattrLength = entry.xattr_len
                });

                off += entry.name_len;

                if (entry.name_len % 2 != 0)
                {
                    off++;
                }
            }

            return(table.ToArray());
        }
コード例 #6
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            if (imagePlugin.Info.SectorSize < 512)
            {
                return(false);
            }

            byte[] sector = imagePlugin.ReadSector(partition.Start);

            Superblock sb = Marshal.ByteArrayToStructureBigEndian <Superblock>(sector);

            return(sb.magic == FATX_MAGIC || sb.magic == FATX_CIGAM);
        }
コード例 #7
0
ファイル: Identify.cs プロジェクト: KailoKyra/DiscImageChef
        public bool Identify(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            if (stream.Length < 84)
            {
                return(false);
            }

            stream.Seek(0, SeekOrigin.Begin);
            byte[] headerB = new byte[Marshal.SizeOf <DartHeader>()];

            stream.Read(headerB, 0, Marshal.SizeOf <DartHeader>());
            DartHeader header = Marshal.ByteArrayToStructureBigEndian <DartHeader>(headerB);

            if (header.srcCmp > COMPRESS_NONE)
            {
                return(false);
            }

            int expectedMaxSize = 84 + (header.srcSize * 2 * 524);

            switch (header.srcType)
            {
            case DISK_MAC:
                if (header.srcSize != SIZE_MAC_SS &&
                    header.srcSize != SIZE_MAC)
                {
                    return(false);
                }

                break;

            case DISK_LISA:
                if (header.srcSize != SIZE_LISA)
                {
                    return(false);
                }

                break;

            case DISK_APPLE2:
                if (header.srcSize != SIZE_APPLE2)
                {
                    return(false);
                }

                break;

            case DISK_MAC_HD:
                if (header.srcSize != SIZE_MAC_HD)
                {
                    return(false);
                }

                expectedMaxSize += 64;

                break;

            case DISK_DOS:
                if (header.srcSize != SIZE_DOS)
                {
                    return(false);
                }

                break;

            case DISK_DOS_HD:
                if (header.srcSize != SIZE_DOS_HD)
                {
                    return(false);
                }

                expectedMaxSize += 64;

                break;

            default: return(false);
            }

            return(stream.Length <= expectedMaxSize);
        }
コード例 #8
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
                });
コード例 #9
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            // TODO: Find correct default encoding
            Encoding    = Encoding.ASCII;
            information = "";
            StringBuilder superBlockMetadata = new StringBuilder();

            byte[] sbSector = imagePlugin.ReadSector(0 + partition.Start);

            SuperBlock sb = Marshal.ByteArrayToStructureBigEndian <SuperBlock>(sbSector);

            if (sb.record_type != 1 || sb.record_version != 1)
            {
                return;
            }
            if (Encoding.ASCII.GetString(sb.sync_bytes) != SYNC)
            {
                return;
            }

            superBlockMetadata.AppendFormat("Opera filesystem disc.").AppendLine();
            if (!string.IsNullOrEmpty(StringHandlers.CToString(sb.volume_label, Encoding)))
            {
                superBlockMetadata
                .AppendFormat("Volume label: {0}", StringHandlers.CToString(sb.volume_label, Encoding)).AppendLine();
            }
            if (!string.IsNullOrEmpty(StringHandlers.CToString(sb.volume_comment, Encoding)))
            {
                superBlockMetadata
                .AppendFormat("Volume comment: {0}", StringHandlers.CToString(sb.volume_comment, Encoding))
                .AppendLine();
            }
            superBlockMetadata.AppendFormat("Volume identifier: 0x{0:X8}", sb.volume_id).AppendLine();
            superBlockMetadata.AppendFormat("Block size: {0} bytes", sb.block_size).AppendLine();
            if (imagePlugin.Info.SectorSize == 2336 || imagePlugin.Info.SectorSize == 2352 ||
                imagePlugin.Info.SectorSize == 2448)
            {
                if (sb.block_size != 2048)
                {
                    superBlockMetadata
                    .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/block",
                                  sb.block_size, 2048);
                }
            }
            else if (imagePlugin.Info.SectorSize != sb.block_size)
            {
                superBlockMetadata
                .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/block",
                              sb.block_size, imagePlugin.Info.SectorSize);
            }

            superBlockMetadata
            .AppendFormat("Volume size: {0} blocks, {1} bytes", sb.block_count, sb.block_size * sb.block_count)
            .AppendLine();
            if (sb.block_count > imagePlugin.Info.Sectors)
            {
                superBlockMetadata
                .AppendFormat("WARNING: Filesystem indicates {0} blocks while device indicates {1} blocks",
                              sb.block_count, imagePlugin.Info.Sectors);
            }
            superBlockMetadata.AppendFormat("Root directory identifier: 0x{0:X8}", sb.root_dirid).AppendLine();
            superBlockMetadata.AppendFormat("Root directory block size: {0} bytes", sb.rootdir_bsize).AppendLine();
            superBlockMetadata.AppendFormat("Root directory size: {0} blocks, {1} bytes", sb.rootdir_blocks,
                                            sb.rootdir_bsize * sb.rootdir_blocks).AppendLine();
            superBlockMetadata.AppendFormat("Last root directory copy: {0}", sb.last_root_copy).AppendLine();

            information = superBlockMetadata.ToString();

            XmlFsType = new FileSystemType
            {
                Type        = "Opera",
                VolumeName  = StringHandlers.CToString(sb.volume_label, Encoding),
                ClusterSize = sb.block_size,
                Clusters    = sb.block_count
            };
        }
コード例 #10
0
ファイル: Info.cs プロジェクト: paulyc/Aaru
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("ibm850");
            information = "";

            var sb = new StringBuilder();

            byte[] hpofsBpbSector =
                imagePlugin.ReadSector(0 + partition.Start); // Seek to BIOS parameter block, on logical sector 0

            byte[] medInfoSector =
                imagePlugin.ReadSector(13 + partition.Start); // Seek to media information block, on logical sector 13

            byte[] volInfoSector =
                imagePlugin.ReadSector(14 + partition.Start); // Seek to volume information block, on logical sector 14

            BiosParameterBlock     bpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock>(hpofsBpbSector);
            MediaInformationBlock  mib = Marshal.ByteArrayToStructureBigEndian <MediaInformationBlock>(medInfoSector);
            VolumeInformationBlock vib = Marshal.ByteArrayToStructureBigEndian <VolumeInformationBlock>(volInfoSector);

            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.oem_name = \"{0}\"",
                                       StringHandlers.CToString(bpb.oem_name));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.bps = {0}", bpb.bps);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.spc = {0}", bpb.spc);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.rsectors = {0}", bpb.rsectors);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.fats_no = {0}", bpb.fats_no);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.root_ent = {0}", bpb.root_ent);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.sectors = {0}", bpb.sectors);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.media = 0x{0:X2}", bpb.media);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.spfat = {0}", bpb.spfat);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.sptrk = {0}", bpb.sptrk);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.heads = {0}", bpb.heads);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.hsectors = {0}", bpb.hsectors);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.big_sectors = {0}", bpb.big_sectors);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.drive_no = 0x{0:X2}", bpb.drive_no);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.nt_flags = {0}", bpb.nt_flags);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.signature = 0x{0:X2}", bpb.signature);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.serial_no = 0x{0:X8}", bpb.serial_no);

            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.volume_label = \"{0}\"",
                                       StringHandlers.SpacePaddedToString(bpb.volume_label));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.fs_type = \"{0}\"", StringHandlers.CToString(bpb.fs_type));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.boot_code is empty? = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(bpb.boot_code));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.unknown = {0}", bpb.unknown);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.unknown2 = {0}", bpb.unknown2);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.signature2 = {0}", bpb.signature2);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.blockId = \"{0}\"", StringHandlers.CToString(mib.blockId));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.volumeLabel = \"{0}\"",
                                       StringHandlers.SpacePaddedToString(mib.volumeLabel));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.comment = \"{0}\"",
                                       StringHandlers.SpacePaddedToString(mib.comment));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.serial = 0x{0:X8}", mib.serial);

            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.creationTimestamp = {0}",
                                       DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.codepageType = {0}", mib.codepageType);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.codepage = {0}", mib.codepage);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.rps = {0}", mib.rps);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.bps = {0}", mib.bps);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.bpc = {0}", mib.bpc);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown2 = {0}", mib.unknown2);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.sectors = {0}", mib.sectors);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown3 = {0}", mib.unknown3);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown4 = {0}", mib.unknown4);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.major = {0}", mib.major);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.minor = {0}", mib.minor);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown5 = {0}", mib.unknown5);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown6 = {0}", mib.unknown6);

            AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.filler is empty? = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(mib.filler));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.blockId = \"{0}\"", StringHandlers.CToString(vib.blockId));
            AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown = {0}", vib.unknown);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown2 = {0}", vib.unknown2);

            AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown3 is empty? = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(vib.unknown3));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown4 = \"{0}\"",
                                       StringHandlers.SpacePaddedToString(vib.unknown4));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.owner = \"{0}\"",
                                       StringHandlers.SpacePaddedToString(vib.owner));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown5 = \"{0}\"",
                                       StringHandlers.SpacePaddedToString(vib.unknown5));

            AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown6 = {0}", vib.unknown6);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.percentFull = {0}", vib.percentFull);
            AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown7 = {0}", vib.unknown7);

            AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.filler is empty? = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(vib.filler));

            sb.AppendLine("High Performance Optical File System");
            sb.AppendFormat("OEM name: {0}", StringHandlers.SpacePaddedToString(bpb.oem_name)).AppendLine();
            sb.AppendFormat("{0} bytes per sector", bpb.bps).AppendLine();
            sb.AppendFormat("{0} sectors per cluster", bpb.spc).AppendLine();
            sb.AppendFormat("Media descriptor: 0x{0:X2}", bpb.media).AppendLine();
            sb.AppendFormat("{0} sectors per track", bpb.sptrk).AppendLine();
            sb.AppendFormat("{0} heads", bpb.heads).AppendLine();
            sb.AppendFormat("{0} sectors hidden before BPB", bpb.hsectors).AppendLine();
            sb.AppendFormat("{0} sectors on volume ({1} bytes)", mib.sectors, mib.sectors * bpb.bps).AppendLine();
            sb.AppendFormat("BIOS Drive Number: 0x{0:X2}", bpb.drive_no).AppendLine();
            sb.AppendFormat("Serial number: 0x{0:X8}", mib.serial).AppendLine();

            sb.AppendFormat("Volume label: {0}", StringHandlers.SpacePaddedToString(mib.volumeLabel, Encoding)).
            AppendLine();

            sb.AppendFormat("Volume comment: {0}", StringHandlers.SpacePaddedToString(mib.comment, Encoding)).
            AppendLine();

            sb.AppendFormat("Volume owner: {0}", StringHandlers.SpacePaddedToString(vib.owner, Encoding)).AppendLine();

            sb.AppendFormat("Volume created on {0}", DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime)).
            AppendLine();

            sb.AppendFormat("Volume uses {0} codepage {1}", mib.codepageType > 0 && mib.codepageType < 3
                                                                ? mib.codepageType == 2
                                                                      ? "EBCDIC"
                                                                      : "ASCII" : "Unknown", mib.codepage).AppendLine();

            sb.AppendFormat("RPS level: {0}", mib.rps).AppendLine();
            sb.AppendFormat("Filesystem version: {0}.{1}", mib.major, mib.minor).AppendLine();
            sb.AppendFormat("Volume can be filled up to {0}%", vib.percentFull).AppendLine();

            XmlFsType = new FileSystemType
            {
                Clusters               = mib.sectors / bpb.spc,
                ClusterSize            = (uint)(bpb.bps * bpb.spc),
                CreationDate           = DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime),
                CreationDateSpecified  = true,
                DataPreparerIdentifier = StringHandlers.SpacePaddedToString(vib.owner, Encoding),
                Type             = "HPOFS",
                VolumeName       = StringHandlers.SpacePaddedToString(mib.volumeLabel, Encoding),
                VolumeSerial     = $"{mib.serial:X8}",
                SystemIdentifier = StringHandlers.SpacePaddedToString(bpb.oem_name)
            };

            information = sb.ToString();
        }
コード例 #11
0
        bool FillDirectory()
        {
            idToFilename = new Dictionary <uint, string>();
            idToEntry    = new Dictionary <uint, FileEntry>();
            filenameToId = new Dictionary <string, uint>();

            int offset = 0;

            while (offset + 51 < directoryBlocks.Length)
            {
                var entry = new FileEntry
                {
                    flFlags = (FileFlags)directoryBlocks[offset + 0]
                };

                if (!entry.flFlags.HasFlag(FileFlags.Used))
                {
                    break;
                }

                entry.flTyp = directoryBlocks[offset + 1];

                entry.flUsrWds =
                    Marshal.ByteArrayToStructureBigEndian <AppleCommon.FInfo>(directoryBlocks, offset + 2, 16);

                entry.flFlNum  = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 18);
                entry.flStBlk  = BigEndianBitConverter.ToUInt16(directoryBlocks, offset + 22);
                entry.flLgLen  = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 24);
                entry.flPyLen  = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 28);
                entry.flRStBlk = BigEndianBitConverter.ToUInt16(directoryBlocks, offset + 32);
                entry.flRLgLen = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 34);
                entry.flRPyLen = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 38);
                entry.flCrDat  = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 42);
                entry.flMdDat  = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 46);
                entry.flNam    = new byte[directoryBlocks[offset + 50] + 1];
                Array.Copy(directoryBlocks, offset + 50, entry.flNam, 0, entry.flNam.Length);

                string lowerFilename = StringHandlers.
                                       PascalToString(entry.flNam, Encoding).ToLowerInvariant().Replace('/', ':');

                if (entry.flFlags.HasFlag(FileFlags.Used) &&
                    !idToFilename.ContainsKey(entry.flFlNum) &&
                    !idToEntry.ContainsKey(entry.flFlNum) &&
                    !filenameToId.ContainsKey(lowerFilename) &&
                    entry.flFlNum > 0)
                {
                    idToEntry.Add(entry.flFlNum, entry);

                    idToFilename.Add(entry.flFlNum,
                                     StringHandlers.PascalToString(entry.flNam, Encoding).Replace('/', ':'));

                    filenameToId.Add(lowerFilename, entry.flFlNum);

                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlags = {0}", entry.flFlags);
                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flTyp = {0}", entry.flTyp);
                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlNum = {0}", entry.flFlNum);
                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flStBlk = {0}", entry.flStBlk);
                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flLgLen = {0}", entry.flLgLen);
                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flPyLen = {0}", entry.flPyLen);
                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRStBlk = {0}", entry.flRStBlk);
                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRLgLen = {0}", entry.flRLgLen);
                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRPyLen = {0}", entry.flRPyLen);

                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flCrDat = {0}",
                                               DateHandlers.MacToDateTime(entry.flCrDat));

                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flMdDat = {0}",
                                               DateHandlers.MacToDateTime(entry.flMdDat));

                    AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flNam0 = {0}",
                                               StringHandlers.PascalToString(entry.flNam, Encoding));
                }

                offset += 50 + entry.flNam.Length;

                // "Entries are always an integral number of words"
                if (offset % 2 != 0)
                {
                    offset++;
                }

                // TODO: "Entries don't cross logical block boundaries"
            }

            return(true);
        }
コード例 #12
0
ファイル: Read.cs プロジェクト: Surfndez/DiscImageChef
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            if (stream.Length < 84)
            {
                return(false);
            }

            stream.Seek(0, SeekOrigin.Begin);
            byte[] headerB = new byte[Marshal.SizeOf <DartHeader>()];

            stream.Read(headerB, 0, Marshal.SizeOf <DartHeader>());
            DartHeader header = Marshal.ByteArrayToStructureBigEndian <DartHeader>(headerB);

            if (header.srcCmp > COMPRESS_NONE)
            {
                return(false);
            }

            int expectedMaxSize = 84 + header.srcSize * 2 * 524;

            switch (header.srcType)
            {
            case DISK_MAC:
                if (header.srcSize != SIZE_MAC_SS && header.srcSize != SIZE_MAC)
                {
                    return(false);
                }

                break;

            case DISK_LISA:
                if (header.srcSize != SIZE_LISA)
                {
                    return(false);
                }

                break;

            case DISK_APPLE2:
                if (header.srcSize != DISK_APPLE2)
                {
                    return(false);
                }

                break;

            case DISK_MAC_HD:
                if (header.srcSize != SIZE_MAC_HD)
                {
                    return(false);
                }

                expectedMaxSize += 64;
                break;

            case DISK_DOS:
                if (header.srcSize != SIZE_DOS)
                {
                    return(false);
                }

                break;

            case DISK_DOS_HD:
                if (header.srcSize != SIZE_DOS_HD)
                {
                    return(false);
                }

                expectedMaxSize += 64;
                break;

            default: return(false);
            }

            if (stream.Length > expectedMaxSize)
            {
                return(false);
            }

            short[] bLength;

            if (header.srcType == DISK_MAC_HD || header.srcType == DISK_DOS_HD)
            {
                bLength = new short[BLOCK_ARRAY_LEN_HIGH];
            }
            else
            {
                bLength = new short[BLOCK_ARRAY_LEN_LOW];
            }

            BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;

            for (int i = 0; i < bLength.Length; i++)
            {
                byte[] tmpShort = new byte[2];
                stream.Read(tmpShort, 0, 2);
                bLength[i] = BigEndianBitConverter.ToInt16(tmpShort, 0);
            }

            MemoryStream dataMs = new MemoryStream();
            MemoryStream tagMs  = new MemoryStream();

            foreach (short l in bLength)
            {
                if (l != 0)
                {
                    byte[] buffer = new byte[BUFFER_SIZE];
                    if (l == -1)
                    {
                        stream.Read(buffer, 0, BUFFER_SIZE);
                        dataMs.Write(buffer, 0, DATA_SIZE);
                        tagMs.Write(buffer, DATA_SIZE, TAG_SIZE);
                    }
                    else
                    {
                        byte[] temp;
                        if (header.srcCmp == COMPRESS_RLE)
                        {
                            temp = new byte[l * 2];
                            stream.Read(temp, 0, temp.Length);
                            AppleRle rle = new AppleRle(new MemoryStream(temp));
                            buffer = new byte[BUFFER_SIZE];
                            for (int i = 0; i < BUFFER_SIZE; i++)
                            {
                                buffer[i] = (byte)rle.ProduceByte();
                            }
                            dataMs.Write(buffer, 0, DATA_SIZE);
                            tagMs.Write(buffer, DATA_SIZE, TAG_SIZE);
                        }
                        else
                        {
                            temp = new byte[l];
                            stream.Read(temp, 0, temp.Length);
                            throw new ImageNotSupportedException("LZH Compressed images not yet supported");
                        }
                    }
                }
            }

            dataCache = dataMs.ToArray();
            if (header.srcType == DISK_LISA || header.srcType == DISK_MAC || header.srcType == DISK_APPLE2)
            {
                imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag);
                tagCache = tagMs.ToArray();
            }

            try
            {
                if (imageFilter.HasResourceFork())
                {
                    ResourceFork rsrcFork = new ResourceFork(imageFilter.GetResourceForkStream());
                    // "vers"
                    if (rsrcFork.ContainsKey(0x76657273))
                    {
                        Resource versRsrc = rsrcFork.GetResource(0x76657273);

                        byte[] vers = versRsrc?.GetResource(versRsrc.GetIds()[0]);

                        if (vers != null)
                        {
                            Version version = new Version(vers);

                            string release = null;
                            string dev     = null;
                            string pre     = null;

                            string major = $"{version.MajorVersion}";
                            string minor = $".{version.MinorVersion / 10}";
                            if (version.MinorVersion % 10 > 0)
                            {
                                release = $".{version.MinorVersion % 10}";
                            }
                            switch (version.DevStage)
                            {
                            case Version.DevelopmentStage.Alpha:
                                dev = "a";
                                break;

                            case Version.DevelopmentStage.Beta:
                                dev = "b";
                                break;

                            case Version.DevelopmentStage.PreAlpha:
                                dev = "d";
                                break;
                            }

                            if (dev == null && version.PreReleaseVersion > 0)
                            {
                                dev = "f";
                            }

                            if (dev != null)
                            {
                                pre = $"{version.PreReleaseVersion}";
                            }

                            imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}";
                            imageInfo.Application        = version.VersionString;
                            imageInfo.Comments           = version.VersionMessage;
                        }
                    }

                    // "dart"
                    if (rsrcFork.ContainsKey(0x44415254))
                    {
                        Resource dartRsrc = rsrcFork.GetResource(0x44415254);
                        if (dartRsrc != null)
                        {
                            string dArt = StringHandlers.PascalToString(dartRsrc.GetResource(dartRsrc.GetIds()[0]),
                                                                        Encoding.GetEncoding("macintosh"));
                            const string DART_REGEX =
                                @"(?<version>\S+), tag checksum=\$(?<tagchk>[0123456789ABCDEF]{8}), data checksum=\$(?<datachk>[0123456789ABCDEF]{8})$";
                            Regex dArtEx    = new Regex(DART_REGEX);
                            Match dArtMatch = dArtEx.Match(dArt);

                            if (dArtMatch.Success)
                            {
                                imageInfo.Application        = "DART";
                                imageInfo.ApplicationVersion = dArtMatch.Groups["version"].Value;
                                dataChecksum = Convert.ToUInt32(dArtMatch.Groups["datachk"].Value, 16);
                                tagChecksum  = Convert.ToUInt32(dArtMatch.Groups["tagchk"].Value, 16);
                            }
                        }
                    }

                    // "cksm"
                    if (rsrcFork.ContainsKey(0x434B534D))
                    {
                        Resource cksmRsrc = rsrcFork.GetResource(0x434B534D);
                        if (cksmRsrc?.ContainsId(1) == true)
                        {
                            byte[] tagChk = cksmRsrc.GetResource(1);
                            tagChecksum = BigEndianBitConverter.ToUInt32(tagChk, 0);
                        }

                        if (cksmRsrc?.ContainsId(2) == true)
                        {
                            byte[] dataChk = cksmRsrc.GetResource(1);
                            dataChecksum = BigEndianBitConverter.ToUInt32(dataChk, 0);
                        }
                    }
                }
            }
            catch (InvalidCastException) { }

            DicConsole.DebugWriteLine("DART plugin", "Image application = {0} version {1}", imageInfo.Application,
                                      imageInfo.ApplicationVersion);

            imageInfo.Sectors              = (ulong)(header.srcSize * 2);
            imageInfo.CreationTime         = imageFilter.GetCreationTime();
            imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
            imageInfo.MediaTitle           = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
            imageInfo.SectorSize           = SECTOR_SIZE;
            imageInfo.XmlMediaType         = XmlMediaType.BlockMedia;
            imageInfo.ImageSize            = imageInfo.Sectors * SECTOR_SIZE;
            imageInfo.Version              = header.srcCmp == COMPRESS_NONE ? "1.4" : "1.5";

            switch (header.srcSize)
            {
            case SIZE_MAC_SS:
                imageInfo.Cylinders       = 80;
                imageInfo.Heads           = 1;
                imageInfo.SectorsPerTrack = 10;
                imageInfo.MediaType       = MediaType.AppleSonySS;
                break;

            case SIZE_MAC:
                imageInfo.Cylinders       = 80;
                imageInfo.Heads           = 2;
                imageInfo.SectorsPerTrack = 10;
                imageInfo.MediaType       = MediaType.AppleSonyDS;
                break;

            case SIZE_DOS:
                imageInfo.Cylinders       = 80;
                imageInfo.Heads           = 2;
                imageInfo.SectorsPerTrack = 9;
                imageInfo.MediaType       = MediaType.DOS_35_DS_DD_9;
                break;

            case SIZE_MAC_HD:
                imageInfo.Cylinders       = 80;
                imageInfo.Heads           = 2;
                imageInfo.SectorsPerTrack = 18;
                imageInfo.MediaType       = MediaType.DOS_35_HD;
                break;
            }

            return(true);
        }
コード例 #13
0
ファイル: Info.cs プロジェクト: paulyc/Aaru
        internal static string GetBootBlockInformation(byte[] bbSector, Encoding encoding)
        {
            if (bbSector is null ||
                bbSector.Length < 0x100)
            {
                return(null);
            }

            BootBlock bb = Marshal.ByteArrayToStructureBigEndian <BootBlock>(bbSector);

            if (bb.bbID != BB_MAGIC)
            {
                return(null);
            }

            var sb = new StringBuilder();

            sb.AppendLine("Boot Block:");

            if ((bb.bbVersion & 0x8000) > 0)
            {
                sb.AppendLine("Boot block is in new format.");

                if ((bb.bbVersion & 0x4000) > 0)
                {
                    sb.AppendLine("Boot block should be executed.");

                    if ((bb.bbVersion & 0x2000) > 0)
                    {
                        sb.
                        AppendFormat("System heap will be extended by {0} bytes and a {1} fraction of the available RAM",
                                     bb.bbSysHeapExtra, bb.bbSysHeapFract).AppendLine();
                    }
                }
            }
            else if ((bb.bbVersion & 0xFF) == 0x0D)
            {
                sb.AppendLine("Boot block should be executed.");
            }

            if (bb.bbPageFlags > 0)
            {
                sb.AppendLine("Allocate secondary sound buffer at boot.");
            }
            else if (bb.bbPageFlags < 0)
            {
                sb.AppendLine("Allocate secondary sound and video buffers at boot.");
            }

            sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(bb.bbSysName, encoding)).AppendLine();

            sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(bb.bbShellName, encoding)).
            AppendLine();

            sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(bb.bbDbg1Name, encoding)).
            AppendLine();

            sb.AppendFormat("Disassembler filename: {0}", StringHandlers.PascalToString(bb.bbDbg2Name, encoding)).
            AppendLine();

            sb.AppendFormat("Startup screen filename: {0}", StringHandlers.PascalToString(bb.bbScreenName, encoding)).
            AppendLine();

            sb.AppendFormat("First program to execute at boot: {0}",
                            StringHandlers.PascalToString(bb.bbHelloName, encoding)).AppendLine();

            sb.AppendFormat("Clipboard filename: {0}", StringHandlers.PascalToString(bb.bbScrapName, encoding)).
            AppendLine();

            sb.AppendFormat("Maximum opened files: {0}", bb.bbCntFCBs * 4).AppendLine();
            sb.AppendFormat("Event queue size: {0}", bb.bbCntEvts).AppendLine();
            sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.bb128KSHeap).AppendLine();
            sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.bb256KSHeap).AppendLine();
            sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.bbSysHeapSize).AppendLine();

            return(sb.ToString());
        }
コード例 #14
0
ファイル: Info.cs プロジェクト: KailoKyra/DiscImageChef
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("macintosh");
            information = "";

            var sb = new StringBuilder();

            byte[] bbSector  = null;
            byte[] mdbSector = null;
            ushort drSigWord;

            bool apmFromHddOnCd = false;

            if (imagePlugin.Info.SectorSize == 2352 ||
                imagePlugin.Info.SectorSize == 2448 ||
                imagePlugin.Info.SectorSize == 2048)
            {
                byte[] tmpSector = imagePlugin.ReadSectors(partition.Start, 2);

                foreach (int offset in new[]
                {
                    0, 0x200, 0x400, 0x600, 0x800, 0xA00
                })
                {
                    drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset);

                    if (drSigWord != AppleCommon.HFS_MAGIC)
                    {
                        continue;
                    }

                    bbSector  = new byte[1024];
                    mdbSector = new byte[512];

                    if (offset >= 0x400)
                    {
                        Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024);
                    }

                    Array.Copy(tmpSector, offset, mdbSector, 0, 512);
                    apmFromHddOnCd = true;

                    break;
                }

                if (!apmFromHddOnCd)
                {
                    return;
                }
            }
            else
            {
                mdbSector = imagePlugin.ReadSector(2 + partition.Start);
                drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0);

                if (drSigWord == AppleCommon.HFS_MAGIC)
                {
                    bbSector = imagePlugin.ReadSector(partition.Start);
                }
                else
                {
                    return;
                }
            }

            MasterDirectoryBlock mdb = Marshal.ByteArrayToStructureBigEndian <MasterDirectoryBlock>(mdbSector);

            sb.AppendLine("Apple Hierarchical File System");
            sb.AppendLine();

            if (apmFromHddOnCd)
            {
                sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine();
            }

            sb.AppendLine("Master Directory Block:");
            sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine();
            sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(mdb.drLsMod)).AppendLine();

            if (mdb.drVolBkUp > 0)
            {
                sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drVolBkUp)).AppendLine();
                sb.AppendFormat("Backup sequence number: {0}", mdb.drVSeqNum).AppendLine();
            }
            else
            {
                sb.AppendLine("Volume has never been backed up");
            }

            if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.HardwareLock))
            {
                sb.AppendLine("Volume is locked by hardware.");
            }

            sb.AppendLine(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted) ? "Volume was unmonted."
                              : "Volume is mounted.");

            if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SparedBadBlocks))
            {
                sb.AppendLine("Volume has spared bad blocks.");
            }

            if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.DoesNotNeedCache))
            {
                sb.AppendLine("Volume does not need cache.");
            }

            if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.BootInconsistent))
            {
                sb.AppendLine("Boot volume is inconsistent.");
            }

            if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.ReusedIds))
            {
                sb.AppendLine("There are reused CNIDs.");
            }

            if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Journaled))
            {
                sb.AppendLine("Volume is journaled.");
            }

            if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Inconsistent))
            {
                sb.AppendLine("Volume is seriously inconsistent.");
            }

            if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SoftwareLock))
            {
                sb.AppendLine("Volume is locked by software.");
            }

            sb.AppendFormat("{0} files on root directory", mdb.drNmFls).AppendLine();
            sb.AppendFormat("{0} directories on root directory", mdb.drNmRtDirs).AppendLine();
            sb.AppendFormat("{0} files on volume", mdb.drFilCnt).AppendLine();
            sb.AppendFormat("{0} directories on volume", mdb.drDirCnt).AppendLine();
            sb.AppendFormat("Volume write count: {0}", mdb.drWrCnt).AppendLine();

            sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", mdb.drVBMSt).AppendLine();
            sb.AppendFormat("Next allocation block: {0}.", mdb.drAllocPtr).AppendLine();
            sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks).AppendLine();
            sb.AppendFormat("{0} bytes per allocation block.", mdb.drAlBlkSiz).AppendLine();
            sb.AppendFormat("{0} bytes to allocate when extending a file.", mdb.drClpSiz).AppendLine();
            sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", mdb.drXTClpSiz).AppendLine();
            sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", mdb.drCTClpSiz).AppendLine();
            sb.AppendFormat("Sector of first allocation block: {0}", mdb.drAlBlSt).AppendLine();
            sb.AppendFormat("Next unused CNID: {0}", mdb.drNxtCNID).AppendLine();
            sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine();

            sb.AppendFormat("{0} bytes in the Extents B-Tree", mdb.drXTFlSize).AppendLine();
            sb.AppendFormat("{0} bytes in the Catalog B-Tree", mdb.drCTFlSize).AppendLine();

            sb.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(mdb.drVN, Encoding)).AppendLine();

            sb.AppendLine("Finder info:");
            sb.AppendFormat("CNID of bootable system's directory: {0}", mdb.drFndrInfo0).AppendLine();
            sb.AppendFormat("CNID of first-run application's directory: {0}", mdb.drFndrInfo1).AppendLine();
            sb.AppendFormat("CNID of previously opened directory: {0}", mdb.drFndrInfo2).AppendLine();
            sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", mdb.drFndrInfo3).AppendLine();
            sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", mdb.drFndrInfo5).AppendLine();

            if (mdb.drFndrInfo6 != 0 &&
                mdb.drFndrInfo7 != 0)
            {
                sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", mdb.drFndrInfo6, mdb.drFndrInfo7).AppendLine();
            }

            if (mdb.drEmbedSigWord == AppleCommon.HFSP_MAGIC)
            {
                sb.AppendLine("Volume wraps a HFS+ volume.");
                sb.AppendFormat("Starting block of the HFS+ volume: {0}", mdb.xdrStABNt).AppendLine();
                sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", mdb.xdrNumABlks).AppendLine();
            }
            else
            {
                sb.AppendFormat("{0} blocks in volume cache", mdb.drVCSize).AppendLine();
                sb.AppendFormat("{0} blocks in volume bitmap cache", mdb.drVBMCSize).AppendLine();
                sb.AppendFormat("{0} blocks in volume common cache", mdb.drCtlCSize).AppendLine();
            }

            string bootBlockInfo = AppleCommon.GetBootBlockInformation(bbSector, Encoding);

            if (bootBlockInfo != null)
            {
                sb.AppendLine("Volume is bootable.");
                sb.AppendLine();
                sb.AppendLine(bootBlockInfo);
            }
            else if (mdb.drFndrInfo0 != 0 ||
                     mdb.drFndrInfo3 != 0 ||
                     mdb.drFndrInfo5 != 0)
            {
                sb.AppendLine("Volume is bootable.");
            }
            else
            {
                sb.AppendLine("Volume is not bootable.");
            }

            information = sb.ToString();

            XmlFsType = new FileSystemType();

            if (mdb.drVolBkUp > 0)
            {
                XmlFsType.BackupDate          = DateHandlers.MacToDateTime(mdb.drVolBkUp);
                XmlFsType.BackupDateSpecified = true;
            }

            XmlFsType.Bootable = bootBlockInfo != null || mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 ||
                                 mdb.drFndrInfo5 != 0;

            XmlFsType.Clusters    = mdb.drNmAlBlks;
            XmlFsType.ClusterSize = mdb.drAlBlkSiz;

            if (mdb.drCrDate > 0)
            {
                XmlFsType.CreationDate          = DateHandlers.MacToDateTime(mdb.drCrDate);
                XmlFsType.CreationDateSpecified = true;
            }

            XmlFsType.Dirty                 = !mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted);
            XmlFsType.Files                 = mdb.drFilCnt;
            XmlFsType.FilesSpecified        = true;
            XmlFsType.FreeClusters          = mdb.drFreeBks;
            XmlFsType.FreeClustersSpecified = true;

            if (mdb.drLsMod > 0)
            {
                XmlFsType.ModificationDate          = DateHandlers.MacToDateTime(mdb.drLsMod);
                XmlFsType.ModificationDateSpecified = true;
            }

            XmlFsType.Type       = "HFS";
            XmlFsType.VolumeName = StringHandlers.PascalToString(mdb.drVN, Encoding);

            if (mdb.drFndrInfo6 != 0 &&
                mdb.drFndrInfo7 != 0)
            {
                XmlFsType.VolumeSerial = $"{mdb.drFndrInfo6:X8}{mdb.drFndrInfo7:X8}";
            }
        }
コード例 #15
0
        public bool Open(IFilter imageFilter)
        {
            byte[] magicB = new byte[4];
            Stream stream = imageFilter.GetDataForkStream();

            stream.Read(magicB, 0, 4);
            uint magic = BitConverter.ToUInt32(magicB, 0);

            if (magic != DFI_MAGIC &&
                magic != DFI_MAGIC2)
            {
                return(false);
            }

            TrackOffsets = new SortedDictionary <int, long>();
            TrackLengths = new SortedDictionary <int, long>();
            int    t = -1;
            ushort lastCylinder = 0, lastHead = 0;
            long   offset = 0;

            while (stream.Position < stream.Length)
            {
                long thisOffset = stream.Position;

                byte[] blk = new byte[Marshal.SizeOf <DfiBlockHeader>()];
                stream.Read(blk, 0, Marshal.SizeOf <DfiBlockHeader>());
                DfiBlockHeader blockHeader = Marshal.ByteArrayToStructureBigEndian <DfiBlockHeader>(blk);

                AaruConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.cylinder = {1}", thisOffset,
                                           blockHeader.cylinder);

                AaruConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.head = {1}", thisOffset, blockHeader.head);

                AaruConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.sector = {1}", thisOffset,
                                           blockHeader.sector);

                AaruConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.length = {1}", thisOffset,
                                           blockHeader.length);

                if (stream.Position + blockHeader.length > stream.Length)
                {
                    AaruConsole.DebugWriteLine("DiscFerret plugin", "Invalid track block found at {0}", thisOffset);

                    break;
                }

                stream.Position += blockHeader.length;

                if (blockHeader.cylinder > 0 &&
                    blockHeader.cylinder > lastCylinder)
                {
                    lastCylinder = blockHeader.cylinder;
                    lastHead     = 0;
                    TrackOffsets.Add(t, offset);
                    TrackLengths.Add(t, (thisOffset - offset) + 1);
                    offset = thisOffset;
                    t++;
                }
                else if (blockHeader.head > 0 &&
                         blockHeader.head > lastHead)
                {
                    lastHead = blockHeader.head;
                    TrackOffsets.Add(t, offset);
                    TrackLengths.Add(t, (thisOffset - offset) + 1);
                    offset = thisOffset;
                    t++;
                }

                if (blockHeader.cylinder > _imageInfo.Cylinders)
                {
                    _imageInfo.Cylinders = blockHeader.cylinder;
                }

                if (blockHeader.head > _imageInfo.Heads)
                {
                    _imageInfo.Heads = blockHeader.head;
                }
            }

            _imageInfo.Heads++;
            _imageInfo.Cylinders++;

            _imageInfo.Application        = "DiscFerret";
            _imageInfo.ApplicationVersion = magic == DFI_MAGIC2 ? "2.0" : "1.0";

            throw new NotImplementedException("Flux decoding is not yet implemented.");
        }
コード例 #16
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            if (stream.Length < 512)
            {
                return(false);
            }

            stream.Seek(-Marshal.SizeOf <UdifFooter>(), SeekOrigin.End);
            byte[] footerB = new byte[Marshal.SizeOf <UdifFooter>()];

            stream.Read(footerB, 0, Marshal.SizeOf <UdifFooter>());
            footer = Marshal.ByteArrayToStructureBigEndian <UdifFooter>(footerB);

            if (footer.signature != UDIF_SIGNATURE)
            {
                stream.Seek(0, SeekOrigin.Begin);
                footerB = new byte[Marshal.SizeOf <UdifFooter>()];

                stream.Read(footerB, 0, Marshal.SizeOf <UdifFooter>());
                footer = Marshal.ByteArrayToStructureBigEndian <UdifFooter>(footerB);

                if (footer.signature != UDIF_SIGNATURE)
                {
                    throw new Exception("Unable to find UDIF signature.");
                }

                AaruConsole.VerboseWriteLine("Found obsolete UDIF format.");
            }

            AaruConsole.DebugWriteLine("UDIF plugin", "footer.signature = 0x{0:X8}", footer.signature);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.version = {0}", footer.version);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.headerSize = {0}", footer.headerSize);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.flags = {0}", footer.flags);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.runningDataForkOff = {0}", footer.runningDataForkOff);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkOff = {0}", footer.dataForkOff);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkLen = {0}", footer.dataForkLen);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.rsrcForkOff = {0}", footer.rsrcForkOff);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.rsrcForkLen = {0}", footer.rsrcForkLen);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.segmentNumber = {0}", footer.segmentNumber);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.segmentCount = {0}", footer.segmentCount);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.segmentId = {0}", footer.segmentId);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkChkType = {0}", footer.dataForkChkType);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkLen = {0}", footer.dataForkLen);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.dataForkChk = 0x{0:X8}", footer.dataForkChk);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.plistOff = {0}", footer.plistOff);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.plistLen = {0}", footer.plistLen);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.masterChkType = {0}", footer.masterChkType);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.masterChkLen = {0}", footer.masterChkLen);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.masterChk = 0x{0:X8}", footer.masterChk);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.imageVariant = {0}", footer.imageVariant);
            AaruConsole.DebugWriteLine("UDIF plugin", "footer.sectorCount = {0}", footer.sectorCount);

            AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved1 is empty? = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved1));

            AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved2 is empty? = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved2));

            AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved3 is empty? = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved3));

            AaruConsole.DebugWriteLine("UDIF plugin", "footer.reserved4 is empty? = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(footer.reserved4));

            // Block chunks and headers
            List <byte[]> blkxList = new List <byte[]>();

            chunks = new Dictionary <ulong, BlockChunk>();

            bool fakeBlockChunks = false;

            byte[] vers = null;

            if (footer.plistLen == 0 &&
                footer.rsrcForkLen != 0)
            {
                AaruConsole.DebugWriteLine("UDIF plugin", "Reading resource fork.");
                byte[] rsrcB = new byte[footer.rsrcForkLen];
                stream.Seek((long)footer.rsrcForkOff, SeekOrigin.Begin);
                stream.Read(rsrcB, 0, rsrcB.Length);

                var rsrc = new ResourceFork(rsrcB);

                if (!rsrc.ContainsKey(BLOCK_OS_TYPE))
                {
                    throw new
                          ImageNotSupportedException("Image resource fork doesn't contain UDIF block chunks. Please fill an issue and send it to us.");
                }

                Resource blkxRez = rsrc.GetResource(BLOCK_OS_TYPE);

                if (blkxRez == null)
                {
                    throw new
                          ImageNotSupportedException("Image resource fork doesn't contain UDIF block chunks. Please fill an issue and send it to us.");
                }

                if (blkxRez.GetIds().Length == 0)
                {
                    throw new
                          ImageNotSupportedException("Image resource fork doesn't contain UDIF block chunks. Please fill an issue and send it to us.");
                }

                blkxList.AddRange(blkxRez.GetIds().Select(blkxId => blkxRez.GetResource(blkxId)));

                Resource versRez = rsrc.GetResource(0x76657273);

                if (versRez != null)
                {
                    vers = versRez.GetResource(versRez.GetIds()[0]);
                }
            }
            else if (footer.plistLen != 0)
            {
                AaruConsole.DebugWriteLine("UDIF plugin", "Reading property list.");
                byte[] plistB = new byte[footer.plistLen];
                stream.Seek((long)footer.plistOff, SeekOrigin.Begin);
                stream.Read(plistB, 0, plistB.Length);

                AaruConsole.DebugWriteLine("UDIF plugin", "Parsing property list.");
                var plist = (NSDictionary)XmlPropertyListParser.Parse(plistB);

                if (plist == null)
                {
                    throw new Exception("Could not parse property list.");
                }

                if (!plist.TryGetValue(RESOURCE_FORK_KEY, out NSObject rsrcObj))
                {
                    throw new Exception("Could not retrieve resource fork.");
                }

                var rsrc = (NSDictionary)rsrcObj;

                if (!rsrc.TryGetValue(BLOCK_KEY, out NSObject blkxObj))
                {
                    throw new Exception("Could not retrieve block chunks array.");
                }

                NSObject[] blkx = ((NSArray)blkxObj).GetArray();

                foreach (NSDictionary part in blkx.Cast <NSDictionary>())
                {
                    if (!part.TryGetValue("Name", out _))
                    {
                        throw new Exception("Could not retrieve Name");
                    }

                    if (!part.TryGetValue("Data", out NSObject dataObj))
                    {
                        throw new Exception("Could not retrieve Data");
                    }

                    blkxList.Add(((NSData)dataObj).Bytes);
                }

                if (rsrc.TryGetValue("vers", out NSObject versObj))
                {
                    NSObject[] versArray = ((NSArray)versObj).GetArray();

                    if (versArray.Length >= 1)
                    {
                        vers = ((NSData)versArray[0]).Bytes;
                    }
                }
            }
            else
            {
                // Obsolete read-only UDIF only prepended the header and then put the image without any kind of block references.
                // So let's falsify a block chunk
                var bChnk = new BlockChunk
                {
                    length = footer.dataForkLen, offset = footer.dataForkOff, sector = 0, sectors = footer.sectorCount,
                    type   = CHUNK_TYPE_COPY
                };

                imageInfo.Sectors = footer.sectorCount;
                chunks.Add(bChnk.sector, bChnk);
                buffersize      = 2048 * SECTOR_SIZE;
                fakeBlockChunks = true;
            }

            if (vers != null)
            {
                var version = new Version(vers);

                string release = null;
                string dev     = null;
                string pre     = null;

                string major = $"{version.MajorVersion}";
                string minor = $".{version.MinorVersion / 10}";

                if (version.MinorVersion % 10 > 0)
                {
                    release = $".{version.MinorVersion % 10}";
                }

                switch (version.DevStage)
                {
                case Version.DevelopmentStage.Alpha:
                    dev = "a";

                    break;

                case Version.DevelopmentStage.Beta:
                    dev = "b";

                    break;

                case Version.DevelopmentStage.PreAlpha:
                    dev = "d";

                    break;
                }

                if (dev == null &&
                    version.PreReleaseVersion > 0)
                {
                    dev = "f";
                }

                if (dev != null)
                {
                    pre = $"{version.PreReleaseVersion}";
                }

                imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}";
                imageInfo.Application        = version.VersionString;
                imageInfo.Comments           = version.VersionMessage;

                if (version.MajorVersion == 3)
                {
                    imageInfo.Application = "ShrinkWrap™";
                }
                else if (version.MajorVersion == 6)
                {
                    imageInfo.Application = "DiskCopy";
                }
            }
            else
            {
                imageInfo.Application = "DiskCopy";
            }

            AaruConsole.DebugWriteLine("UDIF plugin", "Image application = {0} version {1}", imageInfo.Application,
                                       imageInfo.ApplicationVersion);

            imageInfo.Sectors = 0;

            if (!fakeBlockChunks)
            {
                if (blkxList.Count == 0)
                {
                    throw new
                          ImageNotSupportedException("Could not retrieve block chunks. Please fill an issue and send it to us.");
                }

                buffersize = 0;

                foreach (byte[] blkxBytes in blkxList)
                {
                    var    bHdr  = new BlockHeader();
                    byte[] bHdrB = new byte[Marshal.SizeOf <BlockHeader>()];
                    Array.Copy(blkxBytes, 0, bHdrB, 0, Marshal.SizeOf <BlockHeader>());
                    bHdr = Marshal.ByteArrayToStructureBigEndian <BlockHeader>(bHdrB);

                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.signature = 0x{0:X8}", bHdr.signature);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.version = {0}", bHdr.version);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.sectorStart = {0}", bHdr.sectorStart);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.sectorCount = {0}", bHdr.sectorCount);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.dataOffset = {0}", bHdr.dataOffset);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.buffers = {0}", bHdr.buffers);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.descriptor = 0x{0:X8}", bHdr.descriptor);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved1 = {0}", bHdr.reserved1);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved2 = {0}", bHdr.reserved2);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved3 = {0}", bHdr.reserved3);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved4 = {0}", bHdr.reserved4);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved5 = {0}", bHdr.reserved5);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reserved6 = {0}", bHdr.reserved6);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.checksumType = {0}", bHdr.checksumType);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.checksumLen = {0}", bHdr.checksumLen);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.checksum = 0x{0:X8}", bHdr.checksum);
                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunks = {0}", bHdr.chunks);

                    AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.reservedChk is empty? = {0}",
                                               ArrayHelpers.ArrayIsNullOrEmpty(bHdr.reservedChk));

                    if (bHdr.buffers > buffersize)
                    {
                        buffersize = bHdr.buffers * SECTOR_SIZE;
                    }

                    for (int i = 0; i < bHdr.chunks; i++)
                    {
                        var    bChnk  = new BlockChunk();
                        byte[] bChnkB = new byte[Marshal.SizeOf <BlockChunk>()];

                        Array.Copy(blkxBytes, Marshal.SizeOf <BlockHeader>() + (Marshal.SizeOf <BlockChunk>() * i),
                                   bChnkB, 0, Marshal.SizeOf <BlockChunk>());

                        bChnk = Marshal.ByteArrayToStructureBigEndian <BlockChunk>(bChnkB);

                        AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].type = 0x{1:X8}", i, bChnk.type);
                        AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].comment = {1}", i, bChnk.comment);
                        AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].sector = {1}", i, bChnk.sector);
                        AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].sectors = {1}", i, bChnk.sectors);
                        AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].offset = {1}", i, bChnk.offset);
                        AaruConsole.DebugWriteLine("UDIF plugin", "bHdr.chunk[{0}].length = {1}", i, bChnk.length);

                        if (bChnk.type == CHUNK_TYPE_END)
                        {
                            break;
                        }

                        imageInfo.Sectors += bChnk.sectors;

                        // Chunk offset is relative
                        bChnk.sector += bHdr.sectorStart;
                        bChnk.offset += bHdr.dataOffset;

                        switch (bChnk.type)
                        {
                        // TODO: Handle comments
                        case CHUNK_TYPE_COMMNT: continue;

                        // TODO: Handle compressed chunks
                        case CHUNK_TYPE_KENCODE:
                            throw new
                                  ImageNotSupportedException("Chunks compressed with KenCode are not yet supported.");

                        case CHUNK_TYPE_LZH:
                            throw new
                                  ImageNotSupportedException("Chunks compressed with LZH are not yet supported.");

                        case CHUNK_TYPE_LZFSE:
                            throw new
                                  ImageNotSupportedException("Chunks compressed with lzfse are not yet supported.");
                        }

                        if ((bChnk.type > CHUNK_TYPE_NOCOPY && bChnk.type < CHUNK_TYPE_COMMNT) ||
                            (bChnk.type > CHUNK_TYPE_LZFSE && bChnk.type < CHUNK_TYPE_END))
                        {
                            throw new ImageNotSupportedException($"Unsupported chunk type 0x{bChnk.type:X8} found");
                        }

                        if (bChnk.sectors > 0)
                        {
                            chunks.Add(bChnk.sector, bChnk);
                        }
                    }
                }
            }

            sectorCache           = new Dictionary <ulong, byte[]>();
            chunkCache            = new Dictionary <ulong, byte[]>();
            currentChunkCacheSize = 0;
            imageStream           = stream;

            imageInfo.CreationTime         = imageFilter.GetCreationTime();
            imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
            imageInfo.MediaTitle           = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
            imageInfo.SectorSize           = SECTOR_SIZE;
            imageInfo.XmlMediaType         = XmlMediaType.BlockMedia;
            imageInfo.MediaType            = MediaType.GENERIC_HDD;
            imageInfo.ImageSize            = imageInfo.Sectors * SECTOR_SIZE;
            imageInfo.Version = $"{footer.version}";

            imageInfo.Cylinders       = (uint)(imageInfo.Sectors / 16 / 63);
            imageInfo.Heads           = 16;
            imageInfo.SectorsPerTrack = 63;

            return(true);
        }
コード例 #17
0
ファイル: Info.cs プロジェクト: paulyc/Aaru
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.ASCII;
            information = "";
            var isoMetadata = new StringBuilder();

            byte[] vdMagic = new byte[5];     // Volume Descriptor magic "CD001"
            byte[] hsMagic = new byte[5];     // Volume Descriptor magic "CDROM"

            string bootSpec = "";

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

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

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

            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);
            bool highSierraInfo = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC;
            int  hsOff          = 0;

            if (highSierraInfo)
            {
                hsOff = 8;
            }

            bool cdiInfo = false;
            bool evd     = false;
            bool vpd     = false;

            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;
                    }

                    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;
                    }

                    break;
                }

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

                switch (vdType)
                {
                case 0:
                {
                    bvd = Marshal.ByteArrayToStructureLittleEndian <BootRecord>(vdSector, hsOff, 2048 - hsOff);

                    bootSpec = "Unknown";

                    if (Encoding.GetString(bvd.Value.system_id).Substring(0, 23) == "EL TORITO SPECIFICATION")
                    {
                        bootSpec = "El Torito";

                        torito =
                            Marshal.ByteArrayToStructureLittleEndian <ElToritoBootRecord>(vdSector, hsOff,
                                                                                          2048 - hsOff);
                    }

                    break;
                }

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

                    break;
                }

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

                    // 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.WriteLine("ISO9660 plugin",
                                                      "Found unknown supplementary volume descriptor");
                            }
                        }
                    }
                    else
                    {
                        evd = true;
                    }

                    break;
                }

                case 3:
                {
                    vpd = true;

                    break;
                }
                }

                counter++;
            }

            DecodedVolumeDescriptor decodedVd;
            var decodedJolietVd = new DecodedVolumeDescriptor();

            XmlFsType = new FileSystemType();

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

                return;
            }

            if (highSierraInfo)
            {
                decodedVd = DecodeVolumeDescriptor(hsvd.Value);
            }
            else if (cdiInfo)
            {
                decodedVd = DecodeVolumeDescriptor(fsvd.Value);
            }
            else
            {
                decodedVd = DecodeVolumeDescriptor(pvd.Value);
            }

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

            uint rootLocation = 0;
            uint rootSize     = 0;

            // No need to read root on CD-i, as extensions are not supported...
            if (!cdiInfo)
            {
                rootLocation = highSierraInfo ? hsvd.Value.root_directory_record.extent
                                   : pvd.Value.root_directory_record.extent;

                if (highSierraInfo)
                {
                    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++;
                    }
                }
            }

            byte[] rootDir      = new byte[0];
            int    rootOff      = 0;
            bool   xaExtensions = false;
            bool   apple        = false;
            bool   susp         = false;
            bool   rrip         = false;
            bool   ziso         = false;
            bool   amiga        = false;
            bool   aaip         = false;
            List <ContinuationArea> contareas = new List <ContinuationArea>();
            List <byte[]>           refareas  = new List <byte[]>();
            var suspInformation = new StringBuilder();

            if (rootLocation + rootSize < imagePlugin.Info.Sectors)
            {
                rootDir = imagePlugin.ReadSectors(rootLocation, rootSize);
            }

            // Walk thru root directory to see system area extensions in use
            while (rootOff + Marshal.SizeOf <DirectoryRecord>() < rootDir.Length &&
                   !cdiInfo)
            {
                DirectoryRecord record =
                    Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(rootDir, rootOff,
                                                                               Marshal.SizeOf <DirectoryRecord>());

                int saOff = Marshal.SizeOf <DirectoryRecord>() + record.name_len;
                saOff += saOff % 2;
                int saLen = record.length - saOff;

                if (saLen > 0 &&
                    rootOff + saOff + saLen <= rootDir.Length)
                {
                    byte[] sa = new byte[saLen];
                    Array.Copy(rootDir, rootOff + saOff, sa, 0, saLen);
                    saOff = 0;

                    while (saOff < saLen)
                    {
                        bool noneFound = true;

                        if (Marshal.SizeOf <CdromXa>() + saOff <= saLen)
                        {
                            CdromXa xa = Marshal.ByteArrayToStructureBigEndian <CdromXa>(sa);

                            if (xa.signature == XA_MAGIC)
                            {
                                xaExtensions = true;
                                saOff       += Marshal.SizeOf <CdromXa>();
                                noneFound    = false;
                            }
                        }

                        if (saOff + 2 >= saLen)
                        {
                            break;
                        }

                        ushort nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff);

                        switch (nextSignature)
                        {
                        // Easy, contains size field
                        case APPLE_MAGIC:
                            apple     = true;
                            saOff    += sa[saOff + 2];
                            noneFound = false;

                            break;

                        // Not easy, contains size field
                        case APPLE_MAGIC_OLD:
                            apple = true;
                            var appleId = (AppleOldId)sa[saOff + 2];
                            noneFound = false;

                            switch (appleId)
                            {
                            case AppleOldId.ProDOS:
                                saOff += Marshal.SizeOf <AppleProDOSOldSystemUse>();

                                break;

                            case AppleOldId.TypeCreator:
                            case AppleOldId.TypeCreatorBundle:
                                saOff += Marshal.SizeOf <AppleHFSTypeCreatorSystemUse>();

                                break;

                            case AppleOldId.TypeCreatorIcon:
                            case AppleOldId.TypeCreatorIconBundle:
                                saOff += Marshal.SizeOf <AppleHFSIconSystemUse>();

                                break;

                            case AppleOldId.HFS:
                                saOff += Marshal.SizeOf <AppleHFSOldSystemUse>();

                                break;
                            }

                            break;

                        // IEEE-P1281 aka SUSP 1.12
                        case SUSP_INDICATOR:
                            susp      = true;
                            saOff    += sa[saOff + 2];
                            noneFound = false;

                            while (saOff + 2 < saLen)
                            {
                                nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff);

                                switch (nextSignature)
                                {
                                case APPLE_MAGIC:
                                    if (sa[saOff + 3] == 1 &&
                                        sa[saOff + 2] == 7)
                                    {
                                        apple = true;
                                    }
                                    else
                                    {
                                        apple |= sa[saOff + 3] != 1;
                                    }

                                    break;

                                case SUSP_CONTINUATION when saOff + sa[saOff + 2] <= saLen:
                                    byte[] ce = new byte[sa[saOff + 2]];
                                    Array.Copy(sa, saOff, ce, 0, ce.Length);

                                    ContinuationArea ca =
                                        Marshal.ByteArrayToStructureBigEndian <ContinuationArea>(ce);

                                    contareas.Add(ca);

                                    break;

                                case SUSP_REFERENCE when saOff + sa[saOff + 2] <= saLen:
                                    byte[] er = new byte[sa[saOff + 2]];
                                    Array.Copy(sa, saOff, er, 0, er.Length);
                                    refareas.Add(er);

                                    break;
                                }

                                rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES ||
                                        nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK ||
                                        nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK ||
                                        nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR ||
                                        nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE;

                                ziso  |= nextSignature == ZISO_MAGIC;
                                amiga |= nextSignature == AMIGA_MAGIC;

                                aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD &&
                                                                        sa[saOff + 3] == 1 && sa[saOff + 2] >= 9);

                                saOff += sa[saOff + 2];

                                if (nextSignature == SUSP_TERMINATOR)
                                {
                                    break;
                                }
                            }

                            break;
                        }

                        if (noneFound)
                        {
                            break;
                        }
                    }
                }

                rootOff += record.length;

                if (record.length == 0)
                {
                    break;
                }
            }

            foreach (ContinuationArea ca in contareas)
            {
                uint caLen = (ca.ca_length_be + ca.offset_be) /
                             (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size);

                if ((ca.ca_length_be + ca.offset_be) %
                    (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size) > 0)
                {
                    caLen++;
                }

                byte[] caSectors = imagePlugin.ReadSectors(ca.block_be, caLen);
                byte[] caData    = new byte[ca.ca_length_be];
                Array.Copy(caSectors, ca.offset_be, caData, 0, ca.ca_length_be);
                int caOff = 0;

                while (caOff < ca.ca_length_be)
                {
                    ushort nextSignature = BigEndianBitConverter.ToUInt16(caData, caOff);

                    switch (nextSignature)
                    {
                    // Apple never said to include its extensions inside a continuation area, but just in case
                    case APPLE_MAGIC:
                        if (caData[caOff + 3] == 1 &&
                            caData[caOff + 2] == 7)
                        {
                            apple = true;
                        }
                        else
                        {
                            apple |= caData[caOff + 3] != 1;
                        }

                        break;

                    case SUSP_REFERENCE when caOff + caData[caOff + 2] <= ca.ca_length_be:
                        byte[] er = new byte[caData[caOff + 2]];
                        Array.Copy(caData, caOff, er, 0, er.Length);
                        refareas.Add(er);

                        break;
                    }

                    rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES ||
                            nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK ||
                            nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK ||
                            nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR ||
                            nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE;

                    ziso  |= nextSignature == ZISO_MAGIC;
                    amiga |= nextSignature == AMIGA_MAGIC;

                    aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD && caData[caOff + 3] == 1 &&
                                                            caData[caOff + 2] >= 9);

                    caOff += caData[caOff + 2];
                }
            }

            if (refareas.Count > 0)
            {
                suspInformation.AppendLine("----------------------------------------");
                suspInformation.AppendLine("SYSTEM USE SHARING PROTOCOL INFORMATION:");
                suspInformation.AppendLine("----------------------------------------");

                counter = 1;

                foreach (byte[] erb in refareas)
                {
                    ReferenceArea er    = Marshal.ByteArrayToStructureBigEndian <ReferenceArea>(erb);
                    string        extId = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>(), er.id_len);

                    string extDes = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>() + er.id_len, er.des_len);

                    string extSrc = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>() + er.id_len + er.des_len,
                                                       er.src_len);

                    suspInformation.AppendFormat("Extension: {0}", counter).AppendLine();
                    suspInformation.AppendFormat("\tID: {0}, version {1}", extId, er.ext_ver).AppendLine();
                    suspInformation.AppendFormat("\tDescription: {0}", extDes).AppendLine();
                    suspInformation.AppendFormat("\tSource: {0}", extSrc).AppendLine();
                    counter++;
                }
            }

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

            string fsFormat;

            if (highSierraInfo)
            {
                fsFormat = "High Sierra Format";
            }
            else if (cdiInfo)
            {
                fsFormat = "CD-i";
            }
            else
            {
                fsFormat = "ISO9660";
            }

            isoMetadata.AppendFormat("{0} file system", fsFormat).AppendLine();

            if (xaExtensions)
            {
                isoMetadata.AppendLine("CD-ROM XA extensions present.");
            }

            if (amiga)
            {
                isoMetadata.AppendLine("Amiga extensions present.");
            }

            if (apple)
            {
                isoMetadata.AppendLine("Apple extensions present.");
            }

            if (jolietvd != null)
            {
                isoMetadata.AppendLine("Joliet extensions present.");
            }

            if (susp)
            {
                isoMetadata.AppendLine("System Use Sharing Protocol present.");
            }

            if (rrip)
            {
                isoMetadata.AppendLine("Rock Ridge Interchange Protocol present.");
            }

            if (aaip)
            {
                isoMetadata.AppendLine("Arbitrary Attribute Interchange Protocol present.");
            }

            if (ziso)
            {
                isoMetadata.AppendLine("zisofs compression present.");
            }

            if (evd)
            {
                isoMetadata.AppendLine("Contains Enhanved Volume Descriptor.");
            }

            if (vpd)
            {
                isoMetadata.AppendLine("Contains Volume Partition Descriptor.");
            }

            if (bvd != null)
            {
                isoMetadata.AppendFormat("Disc bootable following {0} specifications.", bootSpec).AppendLine();
            }

            if (segaCd != null)
            {
                isoMetadata.AppendLine("This is a SegaCD / MegaCD disc.");
                isoMetadata.AppendLine(CD.Prettify(segaCd));
            }

            if (saturn != null)
            {
                isoMetadata.AppendLine("This is a Sega Saturn disc.");
                isoMetadata.AppendLine(Saturn.Prettify(saturn));
            }

            if (dreamcast != null)
            {
                isoMetadata.AppendLine("This is a Sega Dreamcast disc.");
                isoMetadata.AppendLine(Dreamcast.Prettify(dreamcast));
            }

            isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : "").
            AppendLine();

            isoMetadata.AppendFormat("{0}VOLUME DESCRIPTOR INFORMATION:", cdiInfo ? "FILE STRUCTURE " : "").
            AppendLine();

            isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : "").
            AppendLine();

            isoMetadata.AppendFormat("System identifier: {0}", decodedVd.SystemIdentifier).AppendLine();
            isoMetadata.AppendFormat("Volume identifier: {0}", decodedVd.VolumeIdentifier).AppendLine();
            isoMetadata.AppendFormat("Volume set identifier: {0}", decodedVd.VolumeSetIdentifier).AppendLine();
            isoMetadata.AppendFormat("Publisher identifier: {0}", decodedVd.PublisherIdentifier).AppendLine();
            isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedVd.DataPreparerIdentifier).AppendLine();
            isoMetadata.AppendFormat("Application identifier: {0}", decodedVd.ApplicationIdentifier).AppendLine();
            isoMetadata.AppendFormat("Volume creation date: {0}", decodedVd.CreationTime).AppendLine();

            if (decodedVd.HasModificationTime)
            {
                isoMetadata.AppendFormat("Volume modification date: {0}", decodedVd.ModificationTime).AppendLine();
            }
            else
            {
                isoMetadata.AppendFormat("Volume has not been modified.").AppendLine();
            }

            if (decodedVd.HasExpirationTime)
            {
                isoMetadata.AppendFormat("Volume expiration date: {0}", decodedVd.ExpirationTime).AppendLine();
            }
            else
            {
                isoMetadata.AppendFormat("Volume does not expire.").AppendLine();
            }

            if (decodedVd.HasEffectiveTime)
            {
                isoMetadata.AppendFormat("Volume effective date: {0}", decodedVd.EffectiveTime).AppendLine();
            }
            else
            {
                isoMetadata.AppendFormat("Volume has always been effective.").AppendLine();
            }

            isoMetadata.AppendFormat("Volume has {0} blocks of {1} bytes each", decodedVd.Blocks, decodedVd.BlockSize).
            AppendLine();

            if (jolietvd != null)
            {
                isoMetadata.AppendLine("-------------------------------------");
                isoMetadata.AppendLine("JOLIET VOLUME DESCRIPTOR INFORMATION:");
                isoMetadata.AppendLine("-------------------------------------");
                isoMetadata.AppendFormat("System identifier: {0}", decodedJolietVd.SystemIdentifier).AppendLine();
                isoMetadata.AppendFormat("Volume identifier: {0}", decodedJolietVd.VolumeIdentifier).AppendLine();

                isoMetadata.AppendFormat("Volume set identifier: {0}", decodedJolietVd.VolumeSetIdentifier).
                AppendLine();

                isoMetadata.AppendFormat("Publisher identifier: {0}", decodedJolietVd.PublisherIdentifier).AppendLine();

                isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedJolietVd.DataPreparerIdentifier).
                AppendLine();

                isoMetadata.AppendFormat("Application identifier: {0}", decodedJolietVd.ApplicationIdentifier).
                AppendLine();

                isoMetadata.AppendFormat("Volume creation date: {0}", decodedJolietVd.CreationTime).AppendLine();

                if (decodedJolietVd.HasModificationTime)
                {
                    isoMetadata.AppendFormat("Volume modification date: {0}", decodedJolietVd.ModificationTime).
                    AppendLine();
                }
                else
                {
                    isoMetadata.AppendFormat("Volume has not been modified.").AppendLine();
                }

                if (decodedJolietVd.HasExpirationTime)
                {
                    isoMetadata.AppendFormat("Volume expiration date: {0}", decodedJolietVd.ExpirationTime).
                    AppendLine();
                }
                else
                {
                    isoMetadata.AppendFormat("Volume does not expire.").AppendLine();
                }

                if (decodedJolietVd.HasEffectiveTime)
                {
                    isoMetadata.AppendFormat("Volume effective date: {0}", decodedJolietVd.EffectiveTime).AppendLine();
                }
                else
                {
                    isoMetadata.AppendFormat("Volume has always been effective.").AppendLine();
                }
            }

            if (torito != null)
            {
                vdSector = imagePlugin.ReadSector(torito.Value.catalog_sector + partition.Start);

                int toritoOff = 0;

                if (vdSector[toritoOff] != 1)
                {
                    goto exit_torito;
                }

                ElToritoValidationEntry valentry =
                    Marshal.ByteArrayToStructureLittleEndian <ElToritoValidationEntry>(vdSector, toritoOff,
                                                                                       EL_TORITO_ENTRY_SIZE);

                if (valentry.signature != EL_TORITO_MAGIC)
                {
                    goto exit_torito;
                }

                toritoOff = EL_TORITO_ENTRY_SIZE;

                ElToritoInitialEntry initialEntry =
                    Marshal.ByteArrayToStructureLittleEndian <ElToritoInitialEntry>(vdSector, toritoOff,
                                                                                    EL_TORITO_ENTRY_SIZE);

                initialEntry.boot_type = (ElToritoEmulation)((byte)initialEntry.boot_type & 0xF);

                AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.load_rba = {0}",
                                           initialEntry.load_rba);

                AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.sector_count = {0}",
                                           initialEntry.sector_count);

                byte[] bootImage =
                    (initialEntry.load_rba + partition.Start + initialEntry.sector_count) - 1 <= partition.End
                        ? imagePlugin.ReadSectors(initialEntry.load_rba + partition.Start, initialEntry.sector_count)
                        : null;

                isoMetadata.AppendLine("----------------------");
                isoMetadata.AppendLine("EL TORITO INFORMATION:");
                isoMetadata.AppendLine("----------------------");

                isoMetadata.AppendLine("Initial entry:");
                isoMetadata.AppendFormat("\tDeveloper ID: {0}", Encoding.GetString(valentry.developer_id)).AppendLine();

                if (initialEntry.bootable == ElToritoIndicator.Bootable)
                {
                    isoMetadata.AppendFormat("\tBootable on {0}", valentry.platform_id).AppendLine();

                    isoMetadata.AppendFormat("\tBootable image starts at sector {0} and runs for {1} sectors",
                                             initialEntry.load_rba, initialEntry.sector_count).AppendLine();

                    if (valentry.platform_id == ElToritoPlatform.x86)
                    {
                        isoMetadata.AppendFormat("\tBootable image will be loaded at segment {0:X4}h",
                                                 initialEntry.load_seg == 0 ? 0x7C0 : initialEntry.load_seg).
                        AppendLine();
                    }
                    else
                    {
                        isoMetadata.AppendFormat("\tBootable image will be loaded at 0x{0:X8}",
                                                 (uint)initialEntry.load_seg * 10).AppendLine();
                    }

                    switch (initialEntry.boot_type)
                    {
                    case ElToritoEmulation.None:
                        isoMetadata.AppendLine("\tImage uses no emulation");

                        break;

                    case ElToritoEmulation.Md2Hd:
                        isoMetadata.AppendLine("\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy");

                        break;

                    case ElToritoEmulation.Mf2Hd:
                        isoMetadata.AppendLine("\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy");

                        break;

                    case ElToritoEmulation.Mf2Ed:
                        isoMetadata.AppendLine("\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy");

                        break;

                    default:
                        isoMetadata.AppendFormat("\tImage uses unknown emulation type {0}",
                                                 (byte)initialEntry.boot_type).AppendLine();

                        break;
                    }

                    isoMetadata.AppendFormat("\tSystem type: 0x{0:X2}", initialEntry.system_type).AppendLine();

                    if (bootImage != null)
                    {
                        isoMetadata.AppendFormat("\tBootable image's SHA1: {0}", Sha1Context.Data(bootImage, out _)).
                        AppendLine();
                    }
                }
                else
                {
                    isoMetadata.AppendLine("\tNot bootable");
                }

                toritoOff += EL_TORITO_ENTRY_SIZE;

                const int sectionCounter = 2;

                while (toritoOff < vdSector.Length &&
                       (vdSector[toritoOff] == (byte)ElToritoIndicator.Header ||
                        vdSector[toritoOff] == (byte)ElToritoIndicator.LastHeader))
                {
                    ElToritoSectionHeaderEntry sectionHeader =
                        Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionHeaderEntry>(vdSector, toritoOff,
                                                                                              EL_TORITO_ENTRY_SIZE);

                    toritoOff += EL_TORITO_ENTRY_SIZE;

                    isoMetadata.AppendFormat("Boot section {0}:", sectionCounter);

                    isoMetadata.AppendFormat("\tSection ID: {0}", Encoding.GetString(sectionHeader.identifier)).
                    AppendLine();

                    for (int entryCounter = 1; entryCounter <= sectionHeader.entries && toritoOff < vdSector.Length;
                         entryCounter++)
                    {
                        ElToritoSectionEntry sectionEntry =
                            Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionEntry>(vdSector, toritoOff,
                                                                                            EL_TORITO_ENTRY_SIZE);

                        toritoOff += EL_TORITO_ENTRY_SIZE;

                        isoMetadata.AppendFormat("\tEntry {0}:", entryCounter);

                        if (sectionEntry.bootable == ElToritoIndicator.Bootable)
                        {
                            bootImage =
                                (sectionEntry.load_rba + partition.Start + sectionEntry.sector_count) - 1 <=
                                partition.End
                                    ? imagePlugin.ReadSectors(sectionEntry.load_rba + partition.Start,
                                                              sectionEntry.sector_count) : null;

                            isoMetadata.AppendFormat("\t\tBootable on {0}", sectionHeader.platform_id).AppendLine();

                            isoMetadata.AppendFormat("\t\tBootable image starts at sector {0} and runs for {1} sectors",
                                                     sectionEntry.load_rba, sectionEntry.sector_count).AppendLine();

                            if (valentry.platform_id == ElToritoPlatform.x86)
                            {
                                isoMetadata.AppendFormat("\t\tBootable image will be loaded at segment {0:X4}h",
                                                         sectionEntry.load_seg == 0 ? 0x7C0 : sectionEntry.load_seg).
                                AppendLine();
                            }
                            else
                            {
                                isoMetadata.AppendFormat("\t\tBootable image will be loaded at 0x{0:X8}",
                                                         (uint)sectionEntry.load_seg * 10).AppendLine();
                            }

                            switch ((ElToritoEmulation)((byte)sectionEntry.boot_type & 0xF))
                            {
                            case ElToritoEmulation.None:
                                isoMetadata.AppendLine("\t\tImage uses no emulation");

                                break;

                            case ElToritoEmulation.Md2Hd:
                                isoMetadata.
                                AppendLine("\t\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy");

                                break;

                            case ElToritoEmulation.Mf2Hd:
                                isoMetadata.
                                AppendLine("\t\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy");

                                break;

                            case ElToritoEmulation.Mf2Ed:
                                isoMetadata.
                                AppendLine("\t\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy");

                                break;

                            default:
                                isoMetadata.AppendFormat("\t\tImage uses unknown emulation type {0}",
                                                         (byte)initialEntry.boot_type).AppendLine();

                                break;
                            }

                            isoMetadata.AppendFormat("\t\tSelection criteria type: {0}",
                                                     sectionEntry.selection_criteria_type).AppendLine();

                            isoMetadata.AppendFormat("\t\tSystem type: 0x{0:X2}", sectionEntry.system_type).
                            AppendLine();

                            if (bootImage != null)
                            {
                                isoMetadata.AppendFormat("\t\tBootable image's SHA1: {0}",
                                                         Sha1Context.Data(bootImage, out _)).AppendLine();
                            }
                        }
                        else
                        {
                            isoMetadata.AppendLine("\t\tNot bootable");
                        }

                        var flags = (ElToritoFlags)((byte)sectionEntry.boot_type & 0xF0);

                        if (flags.HasFlag(ElToritoFlags.ATAPI))
                        {
                            isoMetadata.AppendLine("\t\tImage contains ATAPI drivers");
                        }

                        if (flags.HasFlag(ElToritoFlags.SCSI))
                        {
                            isoMetadata.AppendLine("\t\tImage contains SCSI drivers");
                        }

                        if (!flags.HasFlag(ElToritoFlags.Continued))
                        {
                            continue;
                        }

                        while (toritoOff < vdSector.Length)
                        {
                            ElToritoSectionEntryExtension sectionExtension =
                                Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionEntryExtension>(vdSector,
                                                                                                         toritoOff, EL_TORITO_ENTRY_SIZE);

                            toritoOff += EL_TORITO_ENTRY_SIZE;

                            if (!sectionExtension.extension_flags.HasFlag(ElToritoFlags.Continued))
                            {
                                break;
                            }
                        }
                    }

                    if (sectionHeader.header_id == ElToritoIndicator.LastHeader)
                    {
                        break;
                    }
                }
            }

exit_torito:

            if (refareas.Count > 0)
            {
                isoMetadata.Append(suspInformation);
            }

            XmlFsType.Type = fsFormat;

            if (jolietvd != null)
            {
                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;
                }
            }
            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;
                }
            }

            XmlFsType.Bootable   |= bvd != null || segaCd != null || saturn != null || dreamcast != null;
            XmlFsType.Clusters    = decodedVd.Blocks;
            XmlFsType.ClusterSize = decodedVd.BlockSize;

            information = isoMetadata.ToString();
        }
コード例 #18
0
        public Errno ReadDir(string path, out List <string> contents)
        {
            contents = null;

            if (!_mounted)
            {
                return(Errno.AccessDenied);
            }

            if (string.IsNullOrWhiteSpace(path) ||
                path == "/")
            {
                contents = _rootDirectory.Keys.ToList();

                return(Errno.NoError);
            }

            string cutPath = path.StartsWith('/') ? path.Substring(1).ToLower(_cultureInfo)
                                 : path.ToLower(_cultureInfo);

            if (_directoryCache.TryGetValue(cutPath, out Dictionary <string, DirectoryEntry> currentDirectory))
            {
                contents = currentDirectory.Keys.ToList();

                return(Errno.NoError);
            }

            string[] pieces = cutPath.Split(new[]
            {
                '/'
            }, StringSplitOptions.RemoveEmptyEntries);

            KeyValuePair <string, DirectoryEntry> entry =
                _rootDirectory.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[0]);

            if (string.IsNullOrEmpty(entry.Key))
            {
                return(Errno.NoSuchFile);
            }

            if (!entry.Value.attributes.HasFlag(Attributes.Directory))
            {
                return(Errno.NotDirectory);
            }

            string currentPath = pieces[0];

            currentDirectory = _rootDirectory;

            for (int p = 0; p < pieces.Length; p++)
            {
                entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[p]);

                if (string.IsNullOrEmpty(entry.Key))
                {
                    return(Errno.NoSuchFile);
                }

                if (!entry.Value.attributes.HasFlag(Attributes.Directory))
                {
                    return(Errno.NotDirectory);
                }

                currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}";
                uint currentCluster = entry.Value.firstCluster;

                if (_directoryCache.TryGetValue(currentPath, out currentDirectory))
                {
                    continue;
                }

                uint[] clusters = GetClusters(currentCluster);

                if (clusters is null)
                {
                    return(Errno.InvalidArgument);
                }

                byte[] directoryBuffer = new byte[_bytesPerCluster * clusters.Length];

                for (int i = 0; i < clusters.Length; i++)
                {
                    byte[] buffer =
                        _imagePlugin.ReadSectors(_firstClusterSector + ((clusters[i] - 1) * _sectorsPerCluster),
                                                 _sectorsPerCluster);

                    Array.Copy(buffer, 0, directoryBuffer, i * _bytesPerCluster, _bytesPerCluster);
                }

                currentDirectory = new Dictionary <string, DirectoryEntry>();

                int pos = 0;

                while (pos < directoryBuffer.Length)
                {
                    DirectoryEntry dirent = _littleEndian
                                                ? Marshal.
                                            ByteArrayToStructureLittleEndian <DirectoryEntry
                                                                              >(directoryBuffer, pos, Marshal.SizeOf <DirectoryEntry>())
                                                : Marshal.ByteArrayToStructureBigEndian <DirectoryEntry>(directoryBuffer,
                                                                                                         pos, Marshal.SizeOf <DirectoryEntry>());

                    pos += Marshal.SizeOf <DirectoryEntry>();

                    if (dirent.filenameSize == UNUSED_DIRENTRY ||
                        dirent.filenameSize == FINISHED_DIRENTRY)
                    {
                        break;
                    }

                    if (dirent.filenameSize == DELETED_DIRENTRY ||
                        dirent.filenameSize > MAX_FILENAME)
                    {
                        continue;
                    }

                    string filename = Encoding.GetString(dirent.filename, 0, dirent.filenameSize);

                    currentDirectory.Add(filename, dirent);
                }

                _directoryCache.Add(currentPath, currentDirectory);
            }

            contents = currentDirectory?.Keys.ToList();

            return(Errno.NoError);
        }
コード例 #19
0
ファイル: Read.cs プロジェクト: Surfndez/DiscImageChef
        public bool Open(IFilter imageFilter)
        {
            if (!imageFilter.HasResourceFork() || imageFilter.GetResourceForkLength() == 0)
            {
                return(false);
            }

            ResourceFork rsrcFork;
            Resource     rsrc;

            short[] bcems;

            try
            {
                rsrcFork = new ResourceFork(imageFilter.GetResourceForkStream());
                if (!rsrcFork.ContainsKey(NDIF_RESOURCE))
                {
                    return(false);
                }

                rsrc = rsrcFork.GetResource(NDIF_RESOURCE);

                bcems = rsrc.GetIds();

                if (bcems == null || bcems.Length == 0)
                {
                    return(false);
                }
            }
            catch (InvalidCastException) { return(false); }

            imageInfo.Sectors = 0;
            foreach (byte[] bcem in bcems.Select(id => rsrc.GetResource(NDIF_RESOURCEID)))
            {
                if (bcem.Length < 128)
                {
                    return(false);
                }

                header = Marshal.ByteArrayToStructureBigEndian <ChunkHeader>(bcem);

                DicConsole.DebugWriteLine("NDIF plugin", "footer.type = {0}", header.version);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.driver = {0}", header.driver);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.name = {0}",
                                          StringHandlers.PascalToString(header.name,
                                                                        Encoding.GetEncoding("macintosh")));
                DicConsole.DebugWriteLine("NDIF plugin", "footer.sectors = {0}", header.sectors);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.maxSectorsPerChunk = {0}", header.maxSectorsPerChunk);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.dataOffset = {0}", header.dataOffset);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.crc = 0x{0:X7}", header.crc);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.segmented = {0}", header.segmented);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.p1 = 0x{0:X8}", header.p1);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.p2 = 0x{0:X8}", header.p2);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.unknown[0] = 0x{0:X8}", header.unknown[0]);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.unknown[1] = 0x{0:X8}", header.unknown[1]);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.unknown[2] = 0x{0:X8}", header.unknown[2]);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.unknown[3] = 0x{0:X8}", header.unknown[3]);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.unknown[4] = 0x{0:X8}", header.unknown[4]);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.encrypted = {0}", header.encrypted);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.hash = 0x{0:X8}", header.hash);
                DicConsole.DebugWriteLine("NDIF plugin", "footer.chunks = {0}", header.chunks);

                // Block chunks and headers
                chunks = new Dictionary <ulong, BlockChunk>();

                BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;

                for (int i = 0; i < header.chunks; i++)
                {
                    // Obsolete read-only NDIF only prepended the header and then put the image without any kind of block references.
                    // So let's falsify a block chunk
                    BlockChunk bChnk  = new BlockChunk();
                    byte[]     sector = new byte[4];
                    Array.Copy(bcem, 128 + 0 + i * 12, sector, 1, 3);
                    bChnk.sector = BigEndianBitConverter.ToUInt32(sector, 0);
                    bChnk.type   = bcem[128 + 3 + i * 12];
                    bChnk.offset = BigEndianBitConverter.ToUInt32(bcem, 128 + 4 + i * 12);
                    bChnk.length = BigEndianBitConverter.ToUInt32(bcem, 128 + 8 + i * 12);

                    DicConsole.DebugWriteLine("NDIF plugin", "bHdr.chunk[{0}].type = 0x{1:X2}", i, bChnk.type);
                    DicConsole.DebugWriteLine("NDIF plugin", "bHdr.chunk[{0}].sector = {1}", i, bChnk.sector);
                    DicConsole.DebugWriteLine("NDIF plugin", "bHdr.chunk[{0}].offset = {1}", i, bChnk.offset);
                    DicConsole.DebugWriteLine("NDIF plugin", "bHdr.chunk[{0}].length = {1}", i, bChnk.length);

                    if (bChnk.type == CHUNK_TYPE_END)
                    {
                        break;
                    }

                    bChnk.offset += header.dataOffset;
                    bChnk.sector += (uint)imageInfo.Sectors;

                    // TODO: Handle compressed chunks
                    switch (bChnk.type)
                    {
                    case CHUNK_TYPE_KENCODE:
                        throw new
                              ImageNotSupportedException("Chunks compressed with KenCode are not yet supported.");

                    case CHUNK_TYPE_LZH:
                        throw new ImageNotSupportedException("Chunks compressed with LZH are not yet supported.");

                    case CHUNK_TYPE_STUFFIT:
                        throw new
                              ImageNotSupportedException("Chunks compressed with StuffIt! are not yet supported.");
                    }

                    // TODO: Handle compressed chunks
                    if (bChnk.type > CHUNK_TYPE_COPY && bChnk.type < CHUNK_TYPE_KENCODE ||
                        bChnk.type > CHUNK_TYPE_ADC && bChnk.type < CHUNK_TYPE_STUFFIT ||
                        bChnk.type > CHUNK_TYPE_STUFFIT && bChnk.type < CHUNK_TYPE_END || bChnk.type == 1)
                    {
                        throw new ImageNotSupportedException($"Unsupported chunk type 0x{bChnk.type:X8} found");
                    }

                    chunks.Add(bChnk.sector, bChnk);
                }

                imageInfo.Sectors += header.sectors;
            }

            if (header.segmented > 0)
            {
                throw new ImageNotSupportedException("Segmented images are not yet supported.");
            }

            if (header.encrypted > 0)
            {
                throw new ImageNotSupportedException("Encrypted images are not yet supported.");
            }

            switch (imageInfo.Sectors)
            {
            case 1440:
                imageInfo.MediaType = MediaType.DOS_35_DS_DD_9;
                break;

            case 1600:
                imageInfo.MediaType = MediaType.AppleSonyDS;
                break;

            case 2880:
                imageInfo.MediaType = MediaType.DOS_35_HD;
                break;

            case 3360:
                imageInfo.MediaType = MediaType.DMF;
                break;

            default:
                imageInfo.MediaType = MediaType.GENERIC_HDD;
                break;
            }

            if (rsrcFork.ContainsKey(0x76657273))
            {
                Resource versRsrc = rsrcFork.GetResource(0x76657273);
                if (versRsrc != null)
                {
                    byte[] vers = versRsrc.GetResource(versRsrc.GetIds()[0]);

                    Version version = new Version(vers);

                    string release = null;
                    string dev     = null;
                    string pre     = null;

                    string major = $"{version.MajorVersion}";
                    string minor = $".{version.MinorVersion / 10}";
                    if (version.MinorVersion % 10 > 0)
                    {
                        release = $".{version.MinorVersion % 10}";
                    }
                    switch (version.DevStage)
                    {
                    case Version.DevelopmentStage.Alpha:
                        dev = "a";
                        break;

                    case Version.DevelopmentStage.Beta:
                        dev = "b";
                        break;

                    case Version.DevelopmentStage.PreAlpha:
                        dev = "d";
                        break;
                    }

                    if (dev == null && version.PreReleaseVersion > 0)
                    {
                        dev = "f";
                    }

                    if (dev != null)
                    {
                        pre = $"{version.PreReleaseVersion}";
                    }

                    imageInfo.ApplicationVersion = $"{major}{minor}{release}{dev}{pre}";
                    imageInfo.Application        = version.VersionString;
                    imageInfo.Comments           = version.VersionMessage;

                    if (version.MajorVersion == 3)
                    {
                        imageInfo.Application = "ShrinkWrap™";
                    }
                    else if (version.MajorVersion == 6)
                    {
                        imageInfo.Application = "DiskCopy";
                    }
                }
            }

            DicConsole.DebugWriteLine("NDIF plugin", "Image application = {0} version {1}", imageInfo.Application,
                                      imageInfo.ApplicationVersion);

            sectorCache           = new Dictionary <ulong, byte[]>();
            chunkCache            = new Dictionary <ulong, byte[]>();
            currentChunkCacheSize = 0;
            imageStream           = imageFilter.GetDataForkStream();
            buffersize            = header.maxSectorsPerChunk * SECTOR_SIZE;

            imageInfo.CreationTime         = imageFilter.GetCreationTime();
            imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
            imageInfo.MediaTitle           =
                StringHandlers.PascalToString(header.name, Encoding.GetEncoding("macintosh"));
            imageInfo.SectorSize         = SECTOR_SIZE;
            imageInfo.XmlMediaType       = XmlMediaType.BlockMedia;
            imageInfo.ImageSize          = imageInfo.Sectors * SECTOR_SIZE;
            imageInfo.ApplicationVersion = "6";
            imageInfo.Application        = "Apple DiskCopy";

            switch (imageInfo.MediaType)
            {
            case MediaType.AppleSonyDS:
                imageInfo.Cylinders       = 80;
                imageInfo.Heads           = 2;
                imageInfo.SectorsPerTrack = 10;
                break;

            case MediaType.DOS_35_DS_DD_9:
                imageInfo.Cylinders       = 80;
                imageInfo.Heads           = 2;
                imageInfo.SectorsPerTrack = 9;
                break;

            case MediaType.DOS_35_HD:
                imageInfo.Cylinders       = 80;
                imageInfo.Heads           = 2;
                imageInfo.SectorsPerTrack = 18;
                break;

            case MediaType.DMF:
                imageInfo.Cylinders       = 80;
                imageInfo.Heads           = 2;
                imageInfo.SectorsPerTrack = 21;
                break;

            default:
                imageInfo.MediaType       = MediaType.GENERIC_HDD;
                imageInfo.Cylinders       = (uint)(imageInfo.Sectors / 16 / 63);
                imageInfo.Heads           = 16;
                imageInfo.SectorsPerTrack = 63;
                break;
            }

            return(true);
        }
コード例 #20
0
        static BpbKind DetectBpbKind(byte[] bpbSector, IMediaImage imagePlugin, Partition partition,
                                     out BiosParameterBlockEbpb fakeBpb, out HumanParameterBlock humanBpb,
                                     out AtariParameterBlock atariBpb, out byte minBootNearJump,
                                     out bool andosOemCorrect, out bool bootable)
        {
            fakeBpb         = new BiosParameterBlockEbpb();
            minBootNearJump = 0;
            andosOemCorrect = false;
            bootable        = false;

            humanBpb = Marshal.ByteArrayToStructureBigEndian <HumanParameterBlock>(bpbSector);
            atariBpb = Marshal.ByteArrayToStructureLittleEndian <AtariParameterBlock>(bpbSector);

            ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0;

            // Check clusters for Human68k are correct
            bool humanClustersCorrect = humanBpb.clusters == 0 ? humanBpb.big_clusters == expectedClusters
                                            : humanBpb.clusters == expectedClusters;

            // Check OEM for Human68k is correct
            bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 &&
                                   bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 &&
                                   bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 &&
                                   bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 &&
                                   bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 &&
                                   bpbSector[17] >= 0x20;

            // Check correct branch for Human68k
            bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x1C && bpbSector[1] < 0xFE;

            AaruConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect);
            AaruConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect);
            AaruConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect);

            // If all Human68k checks are correct, it is a Human68k FAT16
            bool useHumanBpb = humanClustersCorrect && humanOemCorrect && humanBranchCorrect && expectedClusters > 0;

            if (useHumanBpb)
            {
                AaruConsole.DebugWriteLine("FAT plugin", "Using Human68k BPB");

                fakeBpb.jump        = humanBpb.jump;
                fakeBpb.oem_name    = humanBpb.oem_name;
                fakeBpb.bps         = (ushort)imagePlugin.Info.SectorSize;
                fakeBpb.spc         = (byte)(humanBpb.bpc / fakeBpb.bps);
                fakeBpb.fats_no     = 2;
                fakeBpb.root_ent    = humanBpb.root_ent;
                fakeBpb.media       = humanBpb.media;
                fakeBpb.spfat       = (ushort)(humanBpb.cpfat * fakeBpb.spc);
                fakeBpb.boot_code   = humanBpb.boot_code;
                fakeBpb.sectors     = humanBpb.clusters;
                fakeBpb.big_sectors = humanBpb.big_clusters;
                fakeBpb.rsectors    = 1;

                return(BpbKind.Human);
            }

            var msxBpb     = new MsxParameterBlock();
            var dos2Bpb    = new BiosParameterBlock2();
            var dos30Bpb   = new BiosParameterBlock30();
            var dos32Bpb   = new BiosParameterBlock32();
            var dos33Bpb   = new BiosParameterBlock33();
            var shortEbpb  = new BiosParameterBlockShortEbpb();
            var ebpb       = new BiosParameterBlockEbpb();
            var apricotBpb = new ApricotLabel();

            bool useAtariBpb          = false;
            bool useMsxBpb            = false;
            bool useDos2Bpb           = false;
            bool useDos3Bpb           = false;
            bool useDos32Bpb          = false;
            bool useDos33Bpb          = false;
            bool userShortExtendedBpb = false;
            bool useExtendedBpb       = false;
            bool useShortFat32        = false;
            bool useLongFat32         = false;
            bool useApricotBpb        = false;
            bool useDecRainbowBpb     = false;

            if (imagePlugin.Info.SectorSize >= 256)
            {
                msxBpb    = Marshal.ByteArrayToStructureLittleEndian <MsxParameterBlock>(bpbSector);
                dos2Bpb   = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock2>(bpbSector);
                dos30Bpb  = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock30>(bpbSector);
                dos32Bpb  = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock32>(bpbSector);
                dos33Bpb  = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock33>(bpbSector);
                shortEbpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlockShortEbpb>(bpbSector);
                ebpb      = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlockEbpb>(bpbSector);

                Fat32ParameterBlockShort shortFat32Bpb =
                    Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlockShort>(bpbSector);

                Fat32ParameterBlock fat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlock>(bpbSector);
                apricotBpb = Marshal.ByteArrayToStructureLittleEndian <ApricotLabel>(bpbSector);

                int bitsInBpsMsx        = CountBits.Count(msxBpb.bps);
                int bitsInBpsDos33      = CountBits.Count(dos33Bpb.bps);
                int bitsInBpsDos40      = CountBits.Count(ebpb.bps);
                int bitsInBpsFat32Short = CountBits.Count(shortFat32Bpb.bps);
                int bitsInBpsFat32      = CountBits.Count(fat32Bpb.bps);
                int bitsInBpsApricot    = CountBits.Count(apricotBpb.mainBPB.bps);

                bool correctSpcMsx = msxBpb.spc == 1 || msxBpb.spc == 2 || msxBpb.spc == 4 || msxBpb.spc == 8 ||
                                     msxBpb.spc == 16 || msxBpb.spc == 32 || msxBpb.spc == 64;

                bool correctSpcDos33 = dos33Bpb.spc == 1 || dos33Bpb.spc == 2 || dos33Bpb.spc == 4 ||
                                       dos33Bpb.spc == 8 || dos33Bpb.spc == 16 || dos33Bpb.spc == 32 ||
                                       dos33Bpb.spc == 64;

                bool correctSpcDos40 = ebpb.spc == 1 || ebpb.spc == 2 || ebpb.spc == 4 || ebpb.spc == 8 ||
                                       ebpb.spc == 16 || ebpb.spc == 32 || ebpb.spc == 64;

                bool correctSpcFat32Short = shortFat32Bpb.spc == 1 || shortFat32Bpb.spc == 2 ||
                                            shortFat32Bpb.spc == 4 || shortFat32Bpb.spc == 8 ||
                                            shortFat32Bpb.spc == 16 || shortFat32Bpb.spc == 32 ||
                                            shortFat32Bpb.spc == 64;

                bool correctSpcFat32 = fat32Bpb.spc == 1 || fat32Bpb.spc == 2 || fat32Bpb.spc == 4 ||
                                       fat32Bpb.spc == 8 || fat32Bpb.spc == 16 || fat32Bpb.spc == 32 ||
                                       fat32Bpb.spc == 64;

                bool correctSpcApricot = apricotBpb.mainBPB.spc == 1 || apricotBpb.mainBPB.spc == 2 ||
                                         apricotBpb.mainBPB.spc == 4 || apricotBpb.mainBPB.spc == 8 ||
                                         apricotBpb.mainBPB.spc == 16 || apricotBpb.mainBPB.spc == 32 ||
                                         apricotBpb.mainBPB.spc == 64;

                // This is to support FAT partitions on hybrid ISO/USB images
                if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc)
                {
                    atariBpb.sectors           /= 4;
                    msxBpb.sectors             /= 4;
                    dos2Bpb.sectors            /= 4;
                    dos30Bpb.sectors           /= 4;
                    dos32Bpb.sectors           /= 4;
                    dos33Bpb.sectors           /= 4;
                    dos33Bpb.big_sectors       /= 4;
                    shortEbpb.sectors          /= 4;
                    shortEbpb.big_sectors      /= 4;
                    ebpb.sectors               /= 4;
                    ebpb.big_sectors           /= 4;
                    shortFat32Bpb.sectors      /= 4;
                    shortFat32Bpb.big_sectors  /= 4;
                    shortFat32Bpb.huge_sectors /= 4;
                    fat32Bpb.sectors           /= 4;
                    fat32Bpb.big_sectors       /= 4;
                    apricotBpb.mainBPB.sectors /= 4;
                }

                andosOemCorrect = dos33Bpb.oem_name[0] < 0x20 && dos33Bpb.oem_name[1] >= 0x20 &&
                                  dos33Bpb.oem_name[2] >= 0x20 && dos33Bpb.oem_name[3] >= 0x20 &&
                                  dos33Bpb.oem_name[4] >= 0x20 && dos33Bpb.oem_name[5] >= 0x20 &&
                                  dos33Bpb.oem_name[6] >= 0x20 && dos33Bpb.oem_name[7] >= 0x20;

                if (bitsInBpsFat32 == 1 &&
                    correctSpcFat32 &&
                    fat32Bpb.fats_no <= 2 &&
                    fat32Bpb.sectors == 0 &&
                    fat32Bpb.spfat == 0 &&
                    fat32Bpb.signature == 0x29 &&
                    Encoding.ASCII.GetString(fat32Bpb.fs_type) == "FAT32   ")
                {
                    AaruConsole.DebugWriteLine("FAT plugin", "Using FAT32 BPB");
                    minBootNearJump = 0x58;

                    return(BpbKind.LongFat32);
                }

                if (bitsInBpsFat32Short == 1 &&
                    correctSpcFat32Short &&
                    shortFat32Bpb.fats_no <= 2 &&
                    shortFat32Bpb.sectors == 0 &&
                    shortFat32Bpb.spfat == 0 &&
                    shortFat32Bpb.signature == 0x28)
                {
                    AaruConsole.DebugWriteLine("FAT plugin", "Using short FAT32 BPB");

                    minBootNearJump = 0x57;

                    return(BpbKind.ShortFat32);
                }

                if (bitsInBpsMsx == 1 &&
                    correctSpcMsx &&
                    msxBpb.fats_no <= 2 &&
                    msxBpb.root_ent > 0 &&
                    msxBpb.sectors <= (partition.End - partition.Start) + 1 &&
                    msxBpb.spfat > 0 &&
                    Encoding.ASCII.GetString(msxBpb.vol_id) == "VOL_ID")
                {
                    AaruConsole.DebugWriteLine("FAT plugin", "Using MSX BPB");
                    useMsxBpb = true;
                }
                else if (bitsInBpsApricot == 1 &&
                         correctSpcApricot &&
                         apricotBpb.mainBPB.fats_no <= 2 &&
                         apricotBpb.mainBPB.root_ent > 0 &&
                         apricotBpb.mainBPB.sectors <= (partition.End - partition.Start) + 1 &&
                         apricotBpb.mainBPB.spfat > 0 &&
                         apricotBpb.partitionCount == 0)
                {
                    AaruConsole.DebugWriteLine("FAT plugin", "Using Apricot BPB");
                    useApricotBpb = true;
                }
                else if (bitsInBpsDos40 == 1 &&
                         correctSpcDos40 &&
                         ebpb.fats_no <= 2 &&
                         ebpb.root_ent > 0 &&
                         ebpb.spfat > 0 &&
                         (ebpb.signature == 0x28 || ebpb.signature == 0x29 || andosOemCorrect))
                {
                    if (ebpb.sectors == 0)
                    {
                        if (ebpb.big_sectors <= (partition.End - partition.Start) + 1)
                        {
                            if (ebpb.signature == 0x29 || andosOemCorrect)
                            {
                                AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB");
                                useExtendedBpb  = true;
                                minBootNearJump = 0x3C;
                            }
                            else
                            {
                                AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB");
                                userShortExtendedBpb = true;
                                minBootNearJump      = 0x29;
                            }
                        }
                    }
                    else if (ebpb.sectors <= (partition.End - partition.Start) + 1)
                    {
                        if (ebpb.signature == 0x29 || andosOemCorrect)
                        {
                            AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB");
                            useExtendedBpb  = true;
                            minBootNearJump = 0x3C;
                        }
                        else
                        {
                            AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB");
                            userShortExtendedBpb = true;
                            minBootNearJump      = 0x29;
                        }
                    }
                }
                else if (bitsInBpsDos33 == 1 &&
                         correctSpcDos33 &&
                         dos33Bpb.rsectors < partition.End - partition.Start &&
                         dos33Bpb.fats_no <= 2 &&
                         dos33Bpb.root_ent > 0 &&
                         dos33Bpb.spfat > 0)
                {
                    if (dos33Bpb.sectors == 0 &&
                        dos33Bpb.hsectors <= partition.Start &&
                        dos33Bpb.big_sectors > 0 &&
                        dos33Bpb.big_sectors <= (partition.End - partition.Start) + 1)
                    {
                        AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB");
                        useDos33Bpb     = true;
                        minBootNearJump = 0x22;
                    }
                    else if (dos33Bpb.big_sectors == 0 &&
                             dos33Bpb.hsectors <= partition.Start &&
                             dos33Bpb.sectors > 0 &&
                             dos33Bpb.sectors <= (partition.End - partition.Start) + 1)
                    {
                        if (atariBpb.jump[0] == 0x60 ||
                            (atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 &&
                             Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT    "))
                        {
                            AaruConsole.DebugWriteLine("FAT plugin", "Using Atari BPB");
                            useAtariBpb = true;
                        }
                        else
                        {
                            AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB");
                            useDos33Bpb     = true;
                            minBootNearJump = 0x22;
                        }
                    }
                    else
                    {
                        if (dos32Bpb.hsectors <= partition.Start &&
                            dos32Bpb.hsectors + dos32Bpb.sectors == dos32Bpb.total_sectors)
                        {
                            AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.2 BPB");
                            useDos32Bpb     = true;
                            minBootNearJump = 0x1E;
                        }
                        else if (dos30Bpb.sptrk > 0 &&
                                 dos30Bpb.sptrk < 64 &&
                                 dos30Bpb.heads > 0 &&
                                 dos30Bpb.heads < 256)
                        {
                            if (atariBpb.jump[0] == 0x60 ||
                                (atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 &&
                                 Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT    "))
                            {
                                AaruConsole.DebugWriteLine("FAT plugin", "Using Atari BPB");
                                useAtariBpb = true;
                            }
                            else
                            {
                                AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.0 BPB");
                                useDos3Bpb      = true;
                                minBootNearJump = 0x1C;
                            }
                        }
                        else
                        {
                            if (atariBpb.jump[0] == 0x60 ||
                                (atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 &&
                                 Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT    "))
                            {
                                AaruConsole.DebugWriteLine("FAT plugin", "Using Atari BPB");
                                useAtariBpb = true;
                            }
                            else
                            {
                                AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 2.0 BPB");
                                useDos2Bpb      = true;
                                minBootNearJump = 0x16;
                            }
                        }
                    }
                }
            }

            // DEC Rainbow, lacks a BPB but has a very concrete structure...
            if (imagePlugin.Info.Sectors == 800 &&
                imagePlugin.Info.SectorSize == 512 &&
                !useAtariBpb &&
                !useMsxBpb &&
                !useDos2Bpb &&
                !useDos3Bpb &&
                !useDos32Bpb &&
                !useDos33Bpb &&
                !userShortExtendedBpb &&
                !useExtendedBpb &&
                !useShortFat32 &&
                !useLongFat32 &&
                !useApricotBpb)
            {
                // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts)
                byte z80Di = bpbSector[0];

                // First FAT1 sector resides at LBA 0x14
                byte[] fat1Sector0 = imagePlugin.ReadSector(0x14);

                // First FAT2 sector resides at LBA 0x1A
                byte[] fat2Sector0 = imagePlugin.ReadSector(0x1A);
                bool   equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1];

                // Volume is software interleaved 2:1
                var rootMs = new MemoryStream();

                foreach (byte[] tmp in from ulong rootSector in new[]
                {
                    0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20
                } select imagePlugin.ReadSector(rootSector))
                {
                    rootMs.Write(tmp, 0, tmp.Length);
                }

                byte[] rootDir      = rootMs.ToArray();
                bool   validRootDir = true;

                // Iterate all root directory
                for (int e = 0; e < 96 * 32; e += 32)
                {
                    for (int c = 0; c < 11; c++)
                    {
                        if ((rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05) ||
                            rootDir[c + e] == 0xFF ||
                            rootDir[c + e] == 0x2E)
                        {
                            validRootDir = false;

                            break;
                        }
                    }

                    if (!validRootDir)
                    {
                        break;
                    }
                }

                if (z80Di == 0xF3 &&
                    equalFatIds &&
                    (fat1Sector0[0] & 0xF0) == 0xF0 &&
                    fat1Sector0[1] == 0xFF &&
                    validRootDir)
                {
                    useDecRainbowBpb = true;
                    AaruConsole.DebugWriteLine("FAT plugin", "Using DEC Rainbow hardcoded BPB.");
                    fakeBpb.bps       = 512;
                    fakeBpb.spc       = 1;
                    fakeBpb.rsectors  = 20;
                    fakeBpb.fats_no   = 2;
                    fakeBpb.root_ent  = 96;
                    fakeBpb.sectors   = 800;
                    fakeBpb.media     = 0xFA;
                    fakeBpb.sptrk     = 10;
                    fakeBpb.heads     = 1;
                    fakeBpb.hsectors  = 0;
                    fakeBpb.spfat     = 3;
                    bootable          = true;
                    fakeBpb.boot_code = bpbSector;

                    return(BpbKind.DecRainbow);
                }
            }

            if (!useAtariBpb &&
                !useMsxBpb &&
                !useDos2Bpb &&
                !useDos3Bpb &&
                !useDos32Bpb &&
                !useDos33Bpb &&
                !useHumanBpb &&
                !userShortExtendedBpb &&
                !useExtendedBpb &&
                !useShortFat32 &&
                !useLongFat32 &&
                !useApricotBpb &&
                !useDecRainbowBpb)
            {
                byte[] fatSector = imagePlugin.ReadSector(1 + partition.Start);

                switch (fatSector[0])
                {
                case 0xE5:
                    if (imagePlugin.Info.Sectors == 2002 &&
                        imagePlugin.Info.SectorSize == 128)
                    {
                        AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 128;
                        fakeBpb.spc      = 4;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 64;
                        fakeBpb.sectors  = 2002;
                        fakeBpb.media    = 0xE5;
                        fakeBpb.sptrk    = 26;
                        fakeBpb.heads    = 1;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 1;
                    }

                    break;

                case 0xFD:
                    if (imagePlugin.Info.Sectors == 4004 &&
                        imagePlugin.Info.SectorSize == 128)
                    {
                        AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 128;
                        fakeBpb.spc      = 4;
                        fakeBpb.rsectors = 4;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 68;
                        fakeBpb.sectors  = 4004;
                        fakeBpb.media    = 0xFD;
                        fakeBpb.sptrk    = 26;
                        fakeBpb.heads    = 2;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 6;
                    }
                    else if (imagePlugin.Info.Sectors == 2002 &&
                             imagePlugin.Info.SectorSize == 128)
                    {
                        AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 128;
                        fakeBpb.spc      = 4;
                        fakeBpb.rsectors = 4;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 68;
                        fakeBpb.sectors  = 2002;
                        fakeBpb.media    = 0xFD;
                        fakeBpb.sptrk    = 26;
                        fakeBpb.heads    = 1;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 6;
                    }

                    break;

                case 0xFE:
                    if (imagePlugin.Info.Sectors == 320 &&
                        imagePlugin.Info.SectorSize == 512)
                    {
                        AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" SSDD.");
                        fakeBpb.bps      = 512;
                        fakeBpb.spc      = 1;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 64;
                        fakeBpb.sectors  = 320;
                        fakeBpb.media    = 0xFE;
                        fakeBpb.sptrk    = 8;
                        fakeBpb.heads    = 1;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 1;
                    }
                    else if (imagePlugin.Info.Sectors == 2002 &&
                             imagePlugin.Info.SectorSize == 128)
                    {
                        AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 128;
                        fakeBpb.spc      = 4;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 68;
                        fakeBpb.sectors  = 2002;
                        fakeBpb.media    = 0xFE;
                        fakeBpb.sptrk    = 26;
                        fakeBpb.heads    = 1;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 6;
                    }
                    else if (imagePlugin.Info.Sectors == 1232 &&
                             imagePlugin.Info.SectorSize == 1024)
                    {
                        AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 1024;
                        fakeBpb.spc      = 1;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 192;
                        fakeBpb.sectors  = 1232;
                        fakeBpb.media    = 0xFE;
                        fakeBpb.sptrk    = 8;
                        fakeBpb.heads    = 2;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 2;
                    }
                    else if (imagePlugin.Info.Sectors == 616 &&
                             imagePlugin.Info.SectorSize == 1024)
                    {
                        AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 1024;
                        fakeBpb.spc      = 1;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 6192;
                        fakeBpb.sectors  = 616;
                        fakeBpb.media    = 0xFE;
                        fakeBpb.sptrk    = 8;
                        fakeBpb.heads    = 2;
                        fakeBpb.hsectors = 0;
                    }
                    else if (imagePlugin.Info.Sectors == 720 &&
                             imagePlugin.Info.SectorSize == 128)
                    {
                        AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 128;
                        fakeBpb.spc      = 2;
                        fakeBpb.rsectors = 54;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 64;
                        fakeBpb.sectors  = 720;
                        fakeBpb.media    = 0xFE;
                        fakeBpb.sptrk    = 18;
                        fakeBpb.heads    = 1;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 4;
                    }
                    else if (imagePlugin.Info.Sectors == 640 &&
                             imagePlugin.Info.SectorSize == 512)
                    {
                        AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD.");
                        fakeBpb.bps      = 512;
                        fakeBpb.spc      = 2;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 112;
                        fakeBpb.sectors  = 640;
                        fakeBpb.media    = 0xFF;
                        fakeBpb.sptrk    = 8;
                        fakeBpb.heads    = 2;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 1;
                    }

                    break;

                case 0xFF:
                    if (imagePlugin.Info.Sectors == 640 &&
                        imagePlugin.Info.SectorSize == 512)
                    {
                        AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD.");
                        fakeBpb.bps      = 512;
                        fakeBpb.spc      = 2;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 112;
                        fakeBpb.sectors  = 640;
                        fakeBpb.media    = 0xFF;
                        fakeBpb.sptrk    = 8;
                        fakeBpb.heads    = 2;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 1;
                    }

                    break;
                }

                // This assumes a bootable sector will jump somewhere or disable interrupts in x86 code
                bootable |= bpbSector[0] == 0xFA || (bpbSector[0] == 0xEB && bpbSector[1] <= 0x7F) ||
                            (bpbSector[0] == 0xE9 && BitConverter.ToUInt16(bpbSector, 1) <= 0x1FC);

                fakeBpb.boot_code = bpbSector;

                return(BpbKind.Hardcoded);
            }

            if (useExtendedBpb)
            {
                fakeBpb = ebpb;

                return(BpbKind.Extended);
            }

            if (userShortExtendedBpb)
            {
                fakeBpb.jump           = shortEbpb.jump;
                fakeBpb.oem_name       = shortEbpb.oem_name;
                fakeBpb.bps            = shortEbpb.bps;
                fakeBpb.spc            = shortEbpb.spc;
                fakeBpb.rsectors       = shortEbpb.rsectors;
                fakeBpb.fats_no        = shortEbpb.fats_no;
                fakeBpb.root_ent       = shortEbpb.root_ent;
                fakeBpb.sectors        = shortEbpb.sectors;
                fakeBpb.media          = shortEbpb.media;
                fakeBpb.spfat          = shortEbpb.spfat;
                fakeBpb.sptrk          = shortEbpb.sptrk;
                fakeBpb.heads          = shortEbpb.heads;
                fakeBpb.hsectors       = shortEbpb.hsectors;
                fakeBpb.big_sectors    = shortEbpb.big_sectors;
                fakeBpb.drive_no       = shortEbpb.drive_no;
                fakeBpb.flags          = shortEbpb.flags;
                fakeBpb.signature      = shortEbpb.signature;
                fakeBpb.serial_no      = shortEbpb.serial_no;
                fakeBpb.boot_code      = shortEbpb.boot_code;
                fakeBpb.boot_signature = shortEbpb.boot_signature;

                return(BpbKind.ShortExtended);
            }

            if (useDos33Bpb)
            {
                fakeBpb.jump           = dos33Bpb.jump;
                fakeBpb.oem_name       = dos33Bpb.oem_name;
                fakeBpb.bps            = dos33Bpb.bps;
                fakeBpb.spc            = dos33Bpb.spc;
                fakeBpb.rsectors       = dos33Bpb.rsectors;
                fakeBpb.fats_no        = dos33Bpb.fats_no;
                fakeBpb.root_ent       = dos33Bpb.root_ent;
                fakeBpb.sectors        = dos33Bpb.sectors;
                fakeBpb.media          = dos33Bpb.media;
                fakeBpb.spfat          = dos33Bpb.spfat;
                fakeBpb.sptrk          = dos33Bpb.sptrk;
                fakeBpb.heads          = dos33Bpb.heads;
                fakeBpb.hsectors       = dos33Bpb.hsectors;
                fakeBpb.big_sectors    = dos33Bpb.big_sectors;
                fakeBpb.boot_code      = dos33Bpb.boot_code;
                fakeBpb.boot_signature = dos33Bpb.boot_signature;

                return(BpbKind.Dos33);
            }

            if (useDos32Bpb)
            {
                fakeBpb.jump           = dos32Bpb.jump;
                fakeBpb.oem_name       = dos32Bpb.oem_name;
                fakeBpb.bps            = dos32Bpb.bps;
                fakeBpb.spc            = dos32Bpb.spc;
                fakeBpb.rsectors       = dos32Bpb.rsectors;
                fakeBpb.fats_no        = dos32Bpb.fats_no;
                fakeBpb.root_ent       = dos32Bpb.root_ent;
                fakeBpb.sectors        = dos32Bpb.sectors;
                fakeBpb.media          = dos32Bpb.media;
                fakeBpb.spfat          = dos32Bpb.spfat;
                fakeBpb.sptrk          = dos32Bpb.sptrk;
                fakeBpb.heads          = dos32Bpb.heads;
                fakeBpb.hsectors       = dos32Bpb.hsectors;
                fakeBpb.boot_code      = dos32Bpb.boot_code;
                fakeBpb.boot_signature = dos32Bpb.boot_signature;

                return(BpbKind.Dos32);
            }

            if (useDos3Bpb)
            {
                fakeBpb.jump           = dos30Bpb.jump;
                fakeBpb.oem_name       = dos30Bpb.oem_name;
                fakeBpb.bps            = dos30Bpb.bps;
                fakeBpb.spc            = dos30Bpb.spc;
                fakeBpb.rsectors       = dos30Bpb.rsectors;
                fakeBpb.fats_no        = dos30Bpb.fats_no;
                fakeBpb.root_ent       = dos30Bpb.root_ent;
                fakeBpb.sectors        = dos30Bpb.sectors;
                fakeBpb.media          = dos30Bpb.media;
                fakeBpb.spfat          = dos30Bpb.spfat;
                fakeBpb.sptrk          = dos30Bpb.sptrk;
                fakeBpb.heads          = dos30Bpb.heads;
                fakeBpb.hsectors       = dos30Bpb.hsectors;
                fakeBpb.boot_code      = dos30Bpb.boot_code;
                fakeBpb.boot_signature = dos30Bpb.boot_signature;

                return(BpbKind.Dos3);
            }

            if (useDos2Bpb)
            {
                fakeBpb.jump           = dos2Bpb.jump;
                fakeBpb.oem_name       = dos2Bpb.oem_name;
                fakeBpb.bps            = dos2Bpb.bps;
                fakeBpb.spc            = dos2Bpb.spc;
                fakeBpb.rsectors       = dos2Bpb.rsectors;
                fakeBpb.fats_no        = dos2Bpb.fats_no;
                fakeBpb.root_ent       = dos2Bpb.root_ent;
                fakeBpb.sectors        = dos2Bpb.sectors;
                fakeBpb.media          = dos2Bpb.media;
                fakeBpb.spfat          = dos2Bpb.spfat;
                fakeBpb.boot_code      = dos2Bpb.boot_code;
                fakeBpb.boot_signature = dos2Bpb.boot_signature;

                return(BpbKind.Dos2);
            }

            if (useMsxBpb)
            {
                fakeBpb.jump           = msxBpb.jump;
                fakeBpb.oem_name       = msxBpb.oem_name;
                fakeBpb.bps            = msxBpb.bps;
                fakeBpb.spc            = msxBpb.spc;
                fakeBpb.rsectors       = msxBpb.rsectors;
                fakeBpb.fats_no        = msxBpb.fats_no;
                fakeBpb.root_ent       = msxBpb.root_ent;
                fakeBpb.sectors        = msxBpb.sectors;
                fakeBpb.media          = msxBpb.media;
                fakeBpb.spfat          = msxBpb.spfat;
                fakeBpb.sptrk          = msxBpb.sptrk;
                fakeBpb.heads          = msxBpb.heads;
                fakeBpb.hsectors       = msxBpb.hsectors;
                fakeBpb.boot_code      = msxBpb.boot_code;
                fakeBpb.boot_signature = msxBpb.boot_signature;
                fakeBpb.serial_no      = msxBpb.serial_no;

                // TODO: Is there any way to check this?
                bootable = true;

                return(BpbKind.Msx);
            }

            if (useAtariBpb)
            {
                fakeBpb.jump      = atariBpb.jump;
                fakeBpb.oem_name  = atariBpb.oem_name;
                fakeBpb.bps       = atariBpb.bps;
                fakeBpb.spc       = atariBpb.spc;
                fakeBpb.rsectors  = atariBpb.rsectors;
                fakeBpb.fats_no   = atariBpb.fats_no;
                fakeBpb.root_ent  = atariBpb.root_ent;
                fakeBpb.sectors   = atariBpb.sectors;
                fakeBpb.media     = atariBpb.media;
                fakeBpb.spfat     = atariBpb.spfat;
                fakeBpb.sptrk     = atariBpb.sptrk;
                fakeBpb.heads     = atariBpb.heads;
                fakeBpb.boot_code = atariBpb.boot_code;

                return(BpbKind.Atari);
            }

            if (useApricotBpb)
            {
                fakeBpb.bps      = apricotBpb.mainBPB.bps;
                fakeBpb.spc      = apricotBpb.mainBPB.spc;
                fakeBpb.rsectors = apricotBpb.mainBPB.rsectors;
                fakeBpb.fats_no  = apricotBpb.mainBPB.fats_no;
                fakeBpb.root_ent = apricotBpb.mainBPB.root_ent;
                fakeBpb.sectors  = apricotBpb.mainBPB.sectors;
                fakeBpb.media    = apricotBpb.mainBPB.media;
                fakeBpb.spfat    = apricotBpb.mainBPB.spfat;
                fakeBpb.sptrk    = apricotBpb.spt;
                bootable         = apricotBpb.bootType > 0;

                if (apricotBpb.bootLocation > 0 &&
                    apricotBpb.bootLocation + apricotBpb.bootSize < imagePlugin.Info.Sectors)
                {
                    fakeBpb.boot_code = imagePlugin.ReadSectors(apricotBpb.bootLocation,
                                                                (uint)(apricotBpb.sectorSize * apricotBpb.bootSize) /
                                                                imagePlugin.Info.SectorSize);
                }

                return(BpbKind.Apricot);
            }

            return(BpbKind.None);
        }
コード例 #21
0
ファイル: Super.cs プロジェクト: KailoKyra/DiscImageChef
        public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
                           Dictionary <string, string> options, string @namespace)
        {
            // TODO: Find correct default encoding
            Encoding = Encoding.ASCII;

            if (options == null)
            {
                options = GetDefaultOptions();
            }

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

            byte[] sbSector = imagePlugin.ReadSector(0 + partition.Start);

            SuperBlock sb = Marshal.ByteArrayToStructureBigEndian <SuperBlock>(sbSector);

            if (sb.record_type != 1 ||
                sb.record_version != 1)
            {
                return(Errno.InvalidArgument);
            }

            if (Encoding.ASCII.GetString(sb.sync_bytes) != SYNC)
            {
                return(Errno.InvalidArgument);
            }

            if (imagePlugin.Info.SectorSize == 2336 ||
                imagePlugin.Info.SectorSize == 2352 ||
                imagePlugin.Info.SectorSize == 2448)
            {
                volumeBlockSizeRatio = sb.block_size / 2048;
            }
            else
            {
                volumeBlockSizeRatio = sb.block_size / imagePlugin.Info.SectorSize;
            }

            XmlFsType = new FileSystemType
            {
                Type         = "Opera", VolumeName = StringHandlers.CToString(sb.volume_label, Encoding),
                ClusterSize  = sb.block_size, Clusters = sb.block_count, Bootable = true,
                VolumeSerial = $"{sb.volume_id:X8}"
            };

            statfs = new FileSystemInfo
            {
                Blocks = sb.block_count, FilenameLength = MAX_NAME, FreeBlocks = 0, Id = new FileSystemId
                {
                    IsInt = true, Serial32 = sb.volume_id
                },
                PluginId = Id, Type = "Opera"
            };

            image = imagePlugin;
            int firstRootBlock = BigEndianBitConverter.ToInt32(sbSector, Marshal.SizeOf <SuperBlock>());

            rootDirectoryCache = DecodeDirectory(firstRootBlock);
            directoryCache     = new Dictionary <string, Dictionary <string, DirectoryEntryWithPointers> >();
            mounted            = true;

            return(Errno.NoError);
        }
コード例 #22
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            if (2 + partition.Start >= partition.End)
            {
                return(false);
            }

            ushort bps;
            byte   spc;
            byte   numberOfFats;
            ushort reservedSecs;
            ushort rootEntries;
            ushort sectors;
            byte   mediaDescriptor;
            ushort fatSectors;
            uint   bigSectors;
            byte   bpbSignature;
            byte   fat32Signature;
            ulong  hugeSectors;

            byte[] fat32Id = new byte[8];
            byte[] msxId   = new byte[6];
            byte   fatId;

            byte[] dosOem   = new byte[8];
            byte[] atariOem = new byte[6];
            ushort bootable = 0;

            uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1;

            byte[] bpbSector = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb);
            byte[] fatSector = imagePlugin.ReadSector(sectorsPerBpb + partition.Start);

            HumanParameterBlock humanBpb = Marshal.ByteArrayToStructureBigEndian <HumanParameterBlock>(bpbSector);

            ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0;

            AaruConsole.DebugWriteLine("FAT plugin", "Human bpc = {0}", humanBpb.bpc);
            AaruConsole.DebugWriteLine("FAT plugin", "Human clusters = {0}", humanBpb.clusters);
            AaruConsole.DebugWriteLine("FAT plugin", "Human big_clusters = {0}", humanBpb.big_clusters);
            AaruConsole.DebugWriteLine("FAT plugin", "Human expected clusters = {0}", expectedClusters);

            // Check clusters for Human68k are correct
            bool humanClustersCorrect = humanBpb.clusters == 0 ? humanBpb.big_clusters == expectedClusters
                                            : humanBpb.clusters == expectedClusters;

            // Check OEM for Human68k is correct
            bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 &&
                                   bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 &&
                                   bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 &&
                                   bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 &&
                                   bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 &&
                                   bpbSector[17] >= 0x20;

            // Check correct branch for Human68k
            bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x20 && bpbSector[1] < 0xFE;

            AaruConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect);
            AaruConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect);
            AaruConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect);

            // If all Human68k checks are correct, it is a Human68k FAT16
            if (humanClustersCorrect &&
                humanOemCorrect &&
                humanBranchCorrect &&
                expectedClusters > 0)
            {
                return(true);
            }

            Array.Copy(bpbSector, 0x02, atariOem, 0, 6);
            Array.Copy(bpbSector, 0x03, dosOem, 0, 8);
            bps             = BitConverter.ToUInt16(bpbSector, 0x00B);
            spc             = bpbSector[0x00D];
            reservedSecs    = BitConverter.ToUInt16(bpbSector, 0x00E);
            numberOfFats    = bpbSector[0x010];
            rootEntries     = BitConverter.ToUInt16(bpbSector, 0x011);
            sectors         = BitConverter.ToUInt16(bpbSector, 0x013);
            mediaDescriptor = bpbSector[0x015];
            fatSectors      = BitConverter.ToUInt16(bpbSector, 0x016);
            Array.Copy(bpbSector, 0x052, msxId, 0, 6);
            bigSectors     = BitConverter.ToUInt32(bpbSector, 0x020);
            bpbSignature   = bpbSector[0x026];
            fat32Signature = bpbSector[0x042];
            Array.Copy(bpbSector, 0x052, fat32Id, 0, 8);
            hugeSectors = BitConverter.ToUInt64(bpbSector, 0x052);
            fatId       = fatSector[0];
            int bitsInBps = CountBits.Count(bps);

            if (imagePlugin.Info.SectorSize >= 512)
            {
                bootable = BitConverter.ToUInt16(bpbSector, 0x1FE);
            }

            bool   correctSpc  = spc == 1 || spc == 2 || spc == 4 || spc == 8 || spc == 16 || spc == 32 || spc == 64;
            string msxString   = Encoding.ASCII.GetString(msxId);
            string fat32String = Encoding.ASCII.GetString(fat32Id);

            bool atariOemCorrect = atariOem[0] >= 0x20 && atariOem[1] >= 0x20 && atariOem[2] >= 0x20 &&
                                   atariOem[3] >= 0x20 && atariOem[4] >= 0x20 && atariOem[5] >= 0x20;

            bool dosOemCorrect = dosOem[0] >= 0x20 && dosOem[1] >= 0x20 && dosOem[2] >= 0x20 && dosOem[3] >= 0x20 &&
                                 dosOem[4] >= 0x20 && dosOem[5] >= 0x20 && dosOem[6] >= 0x20 && dosOem[7] >= 0x20;

            string atariString = Encoding.ASCII.GetString(atariOem);
            string oemString   = Encoding.ASCII.GetString(dosOem);

            AaruConsole.DebugWriteLine("FAT plugin", "atari_oem_correct = {0}", atariOemCorrect);
            AaruConsole.DebugWriteLine("FAT plugin", "dos_oem_correct = {0}", dosOemCorrect);
            AaruConsole.DebugWriteLine("FAT plugin", "bps = {0}", bps);
            AaruConsole.DebugWriteLine("FAT plugin", "bits in bps = {0}", bitsInBps);
            AaruConsole.DebugWriteLine("FAT plugin", "spc = {0}", spc);
            AaruConsole.DebugWriteLine("FAT plugin", "correct_spc = {0}", correctSpc);
            AaruConsole.DebugWriteLine("FAT plugin", "reserved_secs = {0}", reservedSecs);
            AaruConsole.DebugWriteLine("FAT plugin", "fats_no = {0}", numberOfFats);
            AaruConsole.DebugWriteLine("FAT plugin", "root_entries = {0}", rootEntries);
            AaruConsole.DebugWriteLine("FAT plugin", "sectors = {0}", sectors);
            AaruConsole.DebugWriteLine("FAT plugin", "media_descriptor = 0x{0:X2}", mediaDescriptor);
            AaruConsole.DebugWriteLine("FAT plugin", "fat_sectors = {0}", fatSectors);
            AaruConsole.DebugWriteLine("FAT plugin", "msx_id = \"{0}\"", msxString);
            AaruConsole.DebugWriteLine("FAT plugin", "big_sectors = {0}", bigSectors);
            AaruConsole.DebugWriteLine("FAT plugin", "bpb_signature = 0x{0:X2}", bpbSignature);
            AaruConsole.DebugWriteLine("FAT plugin", "fat32_signature = 0x{0:X2}", fat32Signature);
            AaruConsole.DebugWriteLine("FAT plugin", "fat32_id = \"{0}\"", fat32String);
            AaruConsole.DebugWriteLine("FAT plugin", "huge_sectors = {0}", hugeSectors);
            AaruConsole.DebugWriteLine("FAT plugin", "fat_id = 0x{0:X2}", fatId);

            ushort apricotBps             = BitConverter.ToUInt16(bpbSector, 0x50);
            byte   apricotSpc             = bpbSector[0x52];
            ushort apricotReservedSecs    = BitConverter.ToUInt16(bpbSector, 0x53);
            byte   apricotFatsNo          = bpbSector[0x55];
            ushort apricotRootEntries     = BitConverter.ToUInt16(bpbSector, 0x56);
            ushort apricotSectors         = BitConverter.ToUInt16(bpbSector, 0x58);
            byte   apricotMediaDescriptor = bpbSector[0x5A];
            ushort apricotFatSectors      = BitConverter.ToUInt16(bpbSector, 0x5B);

            bool apricotCorrectSpc = apricotSpc == 1 || apricotSpc == 2 || apricotSpc == 4 || apricotSpc == 8 ||
                                     apricotSpc == 16 || apricotSpc == 32 || apricotSpc == 64;

            int  bitsInApricotBps  = CountBits.Count(apricotBps);
            byte apricotPartitions = bpbSector[0x0C];

            AaruConsole.DebugWriteLine("FAT plugin", "apricot_bps = {0}", apricotBps);
            AaruConsole.DebugWriteLine("FAT plugin", "apricot_spc = {0}", apricotSpc);
            AaruConsole.DebugWriteLine("FAT plugin", "apricot_correct_spc = {0}", apricotCorrectSpc);
            AaruConsole.DebugWriteLine("FAT plugin", "apricot_reserved_secs = {0}", apricotReservedSecs);
            AaruConsole.DebugWriteLine("FAT plugin", "apricot_fats_no = {0}", apricotFatsNo);
            AaruConsole.DebugWriteLine("FAT plugin", "apricot_root_entries = {0}", apricotRootEntries);
            AaruConsole.DebugWriteLine("FAT plugin", "apricot_sectors = {0}", apricotSectors);
            AaruConsole.DebugWriteLine("FAT plugin", "apricot_media_descriptor = 0x{0:X2}", apricotMediaDescriptor);
            AaruConsole.DebugWriteLine("FAT plugin", "apricot_fat_sectors = {0}", apricotFatSectors);

            // This is to support FAT partitions on hybrid ISO/USB images
            if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc)
            {
                sectors     /= 4;
                bigSectors  /= 4;
                hugeSectors /= 4;
            }

            switch (oemString)
            {
            // exFAT
            case "EXFAT   ": return(false);

            // NTFS
            case "NTFS    " when bootable == 0xAA55 && numberOfFats == 0 && fatSectors == 0: return(false);

            // QNX4
            case "FQNX4FS ": return(false);
            }

            // HPFS
            if (16 + partition.Start <= partition.End)
            {
                byte[] hpfsSbSector =
                    imagePlugin.ReadSector(16 + partition.Start); // Seek to superblock, on logical sector 16

                uint hpfsMagic1 = BitConverter.ToUInt32(hpfsSbSector, 0x000);
                uint hpfsMagic2 = BitConverter.ToUInt32(hpfsSbSector, 0x004);

                if (hpfsMagic1 == 0xF995E849 &&
                    hpfsMagic2 == 0xFA53E9C5)
                {
                    return(false);
                }
            }

            switch (bitsInBps)
            {
            // FAT32 for sure
            case 1 when correctSpc && numberOfFats <= 2 && sectors == 0 && fatSectors == 0 &&
                fat32Signature == 0x29 && fat32String == "FAT32   ": return(true);

            // short FAT32
            case 1
                when correctSpc && numberOfFats <= 2 && sectors == 0 && fatSectors == 0 && fat32Signature == 0x28:
                return(bigSectors == 0 ? hugeSectors <= (partition.End - partition.Start) + 1
                               : bigSectors <= (partition.End - partition.Start) + 1);

            // MSX-DOS FAT12
            case 1 when correctSpc && numberOfFats <= 2 && rootEntries > 0 &&
                sectors <= (partition.End - partition.Start) + 1 && fatSectors > 0 &&
                msxString == "VOL_ID": return(true);

            // EBPB
            case 1 when correctSpc && numberOfFats <= 2 && rootEntries > 0 && fatSectors > 0 &&
                (bpbSignature == 0x28 || bpbSignature == 0x29):
                return(sectors == 0 ? bigSectors <= (partition.End - partition.Start) + 1
                               : sectors <= (partition.End - partition.Start) + 1);

            // BPB
            case 1 when correctSpc && reservedSecs < partition.End - partition.Start && numberOfFats <= 2 &&
                rootEntries > 0 && fatSectors > 0:
                return(sectors == 0 ? bigSectors <= (partition.End - partition.Start) + 1
                               : sectors <= (partition.End - partition.Start) + 1);
            }

            // Apricot BPB
            if (bitsInApricotBps == 1 &&
                apricotCorrectSpc &&
                apricotReservedSecs < partition.End - partition.Start &&
                apricotFatsNo <= 2 &&
                apricotRootEntries > 0 &&
                apricotFatSectors > 0 &&
                apricotSectors <= (partition.End - partition.Start) + 1 &&
                apricotPartitions == 0)
            {
                return(true);
            }

            // All FAT12 without BPB can only be used on floppies, without partitions.
            if (partition.Start != 0)
            {
                return(false);
            }

            // DEC Rainbow, lacks a BPB but has a very concrete structure...
            if (imagePlugin.Info.Sectors == 800 &&
                imagePlugin.Info.SectorSize == 512)
            {
                // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts)
                byte z80Di = bpbSector[0];

                // First FAT1 sector resides at LBA 0x14
                byte[] fat1Sector0 = imagePlugin.ReadSector(0x14);

                // First FAT2 sector resides at LBA 0x1A
                byte[] fat2Sector0 = imagePlugin.ReadSector(0x1A);
                bool   equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1];

                // Volume is software interleaved 2:1
                var rootMs = new MemoryStream();

                foreach (byte[] tmp in from ulong rootSector in new ulong[]
                {
                    0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20
                } select imagePlugin.ReadSector(rootSector))
                {
                    rootMs.Write(tmp, 0, tmp.Length);
                }

                byte[] rootDir      = rootMs.ToArray();
                bool   validRootDir = true;

                // Iterate all root directory
                for (int e = 0; e < 96 * 32; e += 32)
                {
                    for (int c = 0; c < 11; c++)
                    {
                        if ((rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05) ||
                            rootDir[c + e] == 0xFF ||
                            rootDir[c + e] == 0x2E)
                        {
                            validRootDir = false;

                            break;
                        }
                    }

                    if (!validRootDir)
                    {
                        break;
                    }
                }

                if (z80Di == 0xF3 &&
                    equalFatIds &&
                    (fat1Sector0[0] & 0xF0) == 0xF0 &&
                    fat1Sector0[1] == 0xFF &&
                    validRootDir)
                {
                    return(true);
                }
            }

            byte   fat2          = fatSector[1];
            byte   fat3          = fatSector[2];
            ushort fat2ndCluster = (ushort)(((fat2 << 8) + fat3) & 0xFFF);

            AaruConsole.DebugWriteLine("FAT plugin", "1st fat cluster 1 = {0:X3}", fat2ndCluster);

            if (fat2ndCluster < 0xFF0)
            {
                return(false);
            }

            ulong fat2SectorNo = 0;

            switch (fatId)
            {
            case 0xE5:
                if (imagePlugin.Info.Sectors == 2002 &&
                    imagePlugin.Info.SectorSize == 128)
                {
                    fat2SectorNo = 2;
                }

                break;

            case 0xFD:
                if (imagePlugin.Info.Sectors == 4004 &&
                    imagePlugin.Info.SectorSize == 128)
                {
                    fat2SectorNo = 7;
                }
                else if (imagePlugin.Info.Sectors == 2002 &&
                         imagePlugin.Info.SectorSize == 128)
                {
                    fat2SectorNo = 7;
                }

                break;

            case 0xFE:
                if (imagePlugin.Info.Sectors == 320 &&
                    imagePlugin.Info.SectorSize == 512)
                {
                    fat2SectorNo = 2;
                }
                else if (imagePlugin.Info.Sectors == 2002 &&
                         imagePlugin.Info.SectorSize == 128)
                {
                    fat2SectorNo = 7;
                }
                else if (imagePlugin.Info.Sectors == 1232 &&
                         imagePlugin.Info.SectorSize == 1024)
                {
                    fat2SectorNo = 3;
                }
                else if (imagePlugin.Info.Sectors == 616 &&
                         imagePlugin.Info.SectorSize == 1024)
                {
                    fat2SectorNo = 2;
                }
                else if (imagePlugin.Info.Sectors == 720 &&
                         imagePlugin.Info.SectorSize == 128)
                {
                    fat2SectorNo = 5;
                }
                else if (imagePlugin.Info.Sectors == 640 &&
                         imagePlugin.Info.SectorSize == 512)
                {
                    fat2SectorNo = 2;
                }

                break;

            case 0xFF:
                if (imagePlugin.Info.Sectors == 640 &&
                    imagePlugin.Info.SectorSize == 512)
                {
                    fat2SectorNo = 2;
                }

                break;

            default:
                if (fatId < 0xE8)
                {
                    return(false);
                }

                fat2SectorNo = 2;

                break;
            }

            if (fat2SectorNo > partition.End || fat2SectorNo == 0)
            {
                return(false);
            }

            AaruConsole.DebugWriteLine("FAT plugin", "2nd fat starts at = {0}", fat2SectorNo);

            byte[] fat2Sector = imagePlugin.ReadSector(fat2SectorNo);

            fat2          = fat2Sector[1];
            fat3          = fat2Sector[2];
            fat2ndCluster = (ushort)(((fat2 << 8) + fat3) & 0xFFF);

            if (fat2ndCluster < 0xFF0)
            {
                return(false);
            }

            return(fatId == fat2Sector[0]);
        }
コード例 #23
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
                });