Exemple #1
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            stream.Seek(0, SeekOrigin.Begin);

            byte[] header = new byte[2 + (2 * 82)];
            stream.Read(header, 0, 2 + (2 * 82));

            FileHeader fheader = Marshal.ByteArrayToStructureLittleEndian <FileHeader>(header);

            AaruConsole.DebugWriteLine("HDCP plugin",
                                       "Detected HD-Copy image with {0} tracks and {1} sectors per track.",
                                       fheader.lastCylinder + 1, fheader.sectorsPerTrack);

            _imageInfo.Cylinders       = (uint)fheader.lastCylinder + 1;
            _imageInfo.SectorsPerTrack = fheader.sectorsPerTrack;
            _imageInfo.SectorSize      = 512; // only 512 bytes per sector supported
            _imageInfo.Heads           = 2;   // only 2-sided floppies are supported
            _imageInfo.Sectors         = 2 * _imageInfo.Cylinders * _imageInfo.SectorsPerTrack;
            _imageInfo.ImageSize       = _imageInfo.Sectors * _imageInfo.SectorSize;

            _imageInfo.XmlMediaType = XmlMediaType.BlockMedia;

            _imageInfo.CreationTime         = imageFilter.GetCreationTime();
            _imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
            _imageInfo.MediaTitle           = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());

            _imageInfo.MediaType = Geometry.GetMediaType(((ushort)_imageInfo.Cylinders, 2,
                                                          (ushort)_imageInfo.SectorsPerTrack, 512, MediaEncoding.MFM,
                                                          false));

            // the start offset of the track data
            long currentOffset = 2 + (2 * 82);

            // build table of track offsets
            for (int i = 0; i < _imageInfo.Cylinders * 2; i++)
            {
                if (fheader.trackMap[i] == 0)
                {
                    _trackOffset[i] = -1;
                }
                else
                {
                    // track is present, read the block header
                    if (currentOffset + 3 >= stream.Length)
                    {
                        return(false);
                    }

                    byte[] blkHeader = new byte[2];
                    stream.Read(blkHeader, 0, 2);
                    short blkLength = BitConverter.ToInt16(blkHeader, 0);

                    // assume block sizes are positive
                    if (blkLength < 0)
                    {
                        return(false);
                    }

                    AaruConsole.DebugWriteLine("HDCP plugin", "Track {0} offset 0x{1:x8}, size={2:x4}", i,
                                               currentOffset, blkLength);

                    _trackOffset[i] = currentOffset;

                    currentOffset += 2 + blkLength;

                    // skip the block data
                    stream.Seek(blkLength, SeekOrigin.Current);
                }
            }

            // ensure that the last track is present completely
            if (currentOffset > stream.Length)
            {
                return(false);
            }

            // save some variables for later use
            _fileHeader      = fheader;
            _hdcpImageFilter = imageFilter;

            return(true);
        }
Exemple #2
0
        public Errno Stat(string path, out FileEntryInfo stat)
        {
            stat = null;

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

            Errno err = GetFileEntry(path, out DecodedDirectoryEntry entry);

            if (err != Errno.NoError)
            {
                return(err);
            }

            stat = new FileEntryInfo
            {
                Attributes       = new FileAttributes(),
                Blocks           = (long)(entry.Size / 2048), // TODO: XA
                BlockSize        = 2048,
                Length           = (long)entry.Size,
                Links            = 1,
                LastWriteTimeUtc = entry.Timestamp
            };

            if (entry.Extents?.Count > 0)
            {
                stat.Inode = entry.Extents[0].extent;
            }

            if (entry.Size % 2048 > 0)
            {
                stat.Blocks++;
            }

            if (entry.Flags.HasFlag(FileFlags.Directory))
            {
                stat.Attributes |= FileAttributes.Directory;
            }

            if (entry.Flags.HasFlag(FileFlags.Hidden))
            {
                stat.Attributes |= FileAttributes.Hidden;
            }

            //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsAlias) == true)
            //    stat.Attributes |= FileAttributes.Alias;

            //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsInvisible) == true)
            //    stat.Attributes |= FileAttributes.Hidden;

            //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBeenInited) == true)
            //    stat.Attributes |= FileAttributes.HasBeenInited;

            //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasCustomIcon) == true)
            //    stat.Attributes |= FileAttributes.HasCustomIcon;

            //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasNoINITs) == true)
            //    stat.Attributes |= FileAttributes.HasNoINITs;

            //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsOnDesk) == true)
            //    stat.Attributes |= FileAttributes.IsOnDesk;

            //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsShared) == true)
            //    stat.Attributes |= FileAttributes.Shared;

            //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsStationery) == true)
            //    stat.Attributes |= FileAttributes.Stationery;

            //if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBundle) == true)
            //    stat.Attributes |= FileAttributes.Bundle;

            //if(entry.AppleIcon != null)
            //    stat.Attributes |= FileAttributes.HasCustomIcon;

            if (entry.XA != null)
            {
                if (entry.XA.Value.attributes.HasFlag(XaAttributes.GroupExecute))
                {
                    stat.Mode |= 8;
                }

                if (entry.XA.Value.attributes.HasFlag(XaAttributes.GroupRead))
                {
                    stat.Mode |= 32;
                }

                if (entry.XA.Value.attributes.HasFlag(XaAttributes.OwnerExecute))
                {
                    stat.Mode |= 64;
                }

                if (entry.XA.Value.attributes.HasFlag(XaAttributes.OwnerRead))
                {
                    stat.Mode |= 256;
                }

                if (entry.XA.Value.attributes.HasFlag(XaAttributes.SystemExecute))
                {
                    stat.Mode |= 1;
                }

                if (entry.XA.Value.attributes.HasFlag(XaAttributes.SystemRead))
                {
                    stat.Mode |= 4;
                }

                stat.UID   = entry.XA.Value.user;
                stat.GID   = entry.XA.Value.group;
                stat.Inode = entry.XA.Value.filenumber;
            }

            if (entry.PosixAttributes != null)
            {
                stat.Mode = (uint?)entry.PosixAttributes.Value.st_mode & 0x0FFF;

                if (entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Block))
                {
                    stat.Attributes |= FileAttributes.BlockDevice;
                }

                if (entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Character))
                {
                    stat.Attributes |= FileAttributes.CharDevice;
                }

                if (entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Pipe))
                {
                    stat.Attributes |= FileAttributes.Pipe;
                }

                if (entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Socket))
                {
                    stat.Attributes |= FileAttributes.Socket;
                }

                if (entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Symlink))
                {
                    stat.Attributes |= FileAttributes.Symlink;
                }

                stat.Links = entry.PosixAttributes.Value.st_nlink;
                stat.UID   = entry.PosixAttributes.Value.st_uid;
                stat.GID   = entry.PosixAttributes.Value.st_gid;
                stat.Inode = entry.PosixAttributes.Value.st_ino;
            }
            else if (entry.PosixAttributesOld != null)
            {
                stat.Mode = (uint?)entry.PosixAttributesOld.Value.st_mode & 0x0FFF;

                if (entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Block))
                {
                    stat.Attributes |= FileAttributes.BlockDevice;
                }

                if (entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Character))
                {
                    stat.Attributes |= FileAttributes.CharDevice;
                }

                if (entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Pipe))
                {
                    stat.Attributes |= FileAttributes.Pipe;
                }

                if (entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Socket))
                {
                    stat.Attributes |= FileAttributes.Socket;
                }

                if (entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Symlink))
                {
                    stat.Attributes |= FileAttributes.Symlink;
                }

                stat.Links = entry.PosixAttributesOld.Value.st_nlink;
                stat.UID   = entry.PosixAttributesOld.Value.st_uid;
                stat.GID   = entry.PosixAttributesOld.Value.st_gid;
            }

            //if(entry.AmigaProtection != null)
            //{
            //    if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupExec))
            //        stat.Mode |= 8;

            //    if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupRead))
            //        stat.Mode |= 32;

            //    if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupWrite))
            //        stat.Mode |= 16;

            //    if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherExec))
            //        stat.Mode |= 1;

            //    if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherRead))
            //        stat.Mode |= 4;

            //    if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherWrite))
            //        stat.Mode |= 2;

            //    if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerExec))
            //        stat.Mode |= 64;

            //    if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerRead))
            //        stat.Mode |= 256;

            //    if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerWrite))
            //        stat.Mode |= 128;

            //    if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.Archive))
            //        stat.Attributes |= FileAttributes.Archive;
            //}

            if (entry.PosixDeviceNumber != null)
            {
                stat.DeviceNo = ((ulong)entry.PosixDeviceNumber.Value.dev_t_high << 32) +
                                entry.PosixDeviceNumber.Value.dev_t_low;
            }

            if (entry.RripModify != null)
            {
                stat.LastWriteTimeUtc = DecodeIsoDateTime(entry.RripModify);
            }

            if (entry.RripAccess != null)
            {
                stat.AccessTimeUtc = DecodeIsoDateTime(entry.RripAccess);
            }

            if (entry.RripAttributeChange != null)
            {
                stat.StatusChangeTimeUtc = DecodeIsoDateTime(entry.RripAttributeChange);
            }

            if (entry.RripBackup != null)
            {
                stat.BackupTimeUtc = DecodeIsoDateTime(entry.RripBackup);
            }

            if (entry.SymbolicLink != null)
            {
                stat.Attributes |= FileAttributes.Symlink;
            }

            if (entry.XattrLength == 0)
            {
                return(Errno.NoError);
            }

            //if(entry.CdiSystemArea != null)
            //{
            //    stat.UID = entry.CdiSystemArea.Value.owner;
            //    stat.GID = entry.CdiSystemArea.Value.group;

            //    if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.GroupExecute))
            //        stat.Mode |= 8;

            //    if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.GroupRead))
            //        stat.Mode |= 32;

            //    if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OtherExecute))
            //        stat.Mode |= 1;

            //    if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OtherRead))
            //        stat.Mode |= 4;

            //    if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OwnerExecute))
            //        stat.Mode |= 64;

            //    if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OwnerRead))
            //        stat.Mode |= 256;
            //}

            byte[] ea = ReadSingleExtent(entry.XattrLength * _blockSize, entry.Extents[0].extent);

            ExtendedAttributeRecord ear = Marshal.ByteArrayToStructureLittleEndian <ExtendedAttributeRecord>(ea);

            stat.UID = ear.owner;
            stat.GID = ear.group;

            stat.Mode = 0;

            if (ear.permissions.HasFlag(Permissions.GroupExecute))
            {
                stat.Mode |= 8;
            }

            if (ear.permissions.HasFlag(Permissions.GroupRead))
            {
                stat.Mode |= 32;
            }

            if (ear.permissions.HasFlag(Permissions.OwnerExecute))
            {
                stat.Mode |= 64;
            }

            if (ear.permissions.HasFlag(Permissions.OwnerRead))
            {
                stat.Mode |= 256;
            }

            if (ear.permissions.HasFlag(Permissions.OtherExecute))
            {
                stat.Mode |= 1;
            }

            if (ear.permissions.HasFlag(Permissions.OtherRead))
            {
                stat.Mode |= 4;
            }

            stat.CreationTimeUtc  = DateHandlers.Iso9660ToDateTime(ear.creation_date);
            stat.LastWriteTimeUtc = DateHandlers.Iso9660ToDateTime(ear.modification_date);

            return(Errno.NoError);
        }
Exemple #3
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("IBM437");
            information = "";

            StringBuilder sb = new StringBuilder();

            XmlFsType = new FileSystemType();

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

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

            BpbKind bpbKind = DetectBpbKind(bpbSector, imagePlugin, partition,
                                            out BiosParameterBlockEbpb fakeBpb,
                                            out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb,
                                            out byte minBootNearJump, out bool andosOemCorrect,
                                            out bool bootable);

            bool   isFat12             = false;
            bool   isFat16             = false;
            bool   isFat32             = false;
            ulong  rootDirectorySector = 0;
            string extraInfo           = null;
            string bootChk             = null;

            XmlFsType.Bootable = bootable;

            // This is needed because for FAT16, GEMDOS increases bytes per sector count instead of using big_sectors field.
            uint sectorsPerRealSector;
            // This is needed because some OSes don't put volume label as first entry in the root directory
            uint sectorsForRootDirectory = 0;

            switch (bpbKind)
            {
            case BpbKind.DecRainbow:
            case BpbKind.Hardcoded:
            case BpbKind.Msx:
            case BpbKind.Apricot:
                isFat12 = true;
                break;

            case BpbKind.ShortFat32:
            case BpbKind.LongFat32:
            {
                isFat32 = true;
                Fat32ParameterBlock fat32Bpb =
                    Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlock>(bpbSector);
                Fat32ParameterBlockShort shortFat32Bpb =
                    Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlockShort>(bpbSector);

                // This is to support FAT partitions on hybrid ISO/USB images
                if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc)
                {
                    fat32Bpb.bps       *= 4;
                    fat32Bpb.spc       /= 4;
                    fat32Bpb.big_spfat /= 4;
                    fat32Bpb.hsectors  /= 4;
                    fat32Bpb.sptrk     /= 4;
                }

                if (fat32Bpb.version != 0)
                {
                    sb.AppendLine("FAT+");
                    XmlFsType.Type = "FAT+";
                }
                else
                {
                    sb.AppendLine("Microsoft FAT32");
                    XmlFsType.Type = "FAT32";
                }

                if (fat32Bpb.oem_name != null)
                {
                    if (fat32Bpb.oem_name[5] == 0x49 && fat32Bpb.oem_name[6] == 0x48 && fat32Bpb.oem_name[7] == 0x43)
                    {
                        sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker.");
                    }
                    else
                    {
                        XmlFsType.SystemIdentifier = StringHandlers.CToString(fat32Bpb.oem_name);
                    }
                }

                if (!string.IsNullOrEmpty(XmlFsType.SystemIdentifier))
                {
                    sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine();
                }
                sb.AppendFormat("{0} bytes per sector.", fat32Bpb.bps).AppendLine();
                sb.AppendFormat("{0} sectors per cluster.", fat32Bpb.spc).AppendLine();
                XmlFsType.ClusterSize = (uint)(fat32Bpb.bps * fat32Bpb.spc);
                sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fat32Bpb.rsectors).AppendLine();
                if (fat32Bpb.big_sectors == 0 && fat32Bpb.signature == 0x28)
                {
                    sb.AppendFormat("{0} sectors on volume ({1} bytes).", shortFat32Bpb.huge_sectors,
                                    shortFat32Bpb.huge_sectors * shortFat32Bpb.bps).AppendLine();
                    XmlFsType.Clusters = shortFat32Bpb.huge_sectors / shortFat32Bpb.spc;
                }
                else
                {
                    sb.AppendFormat("{0} sectors on volume ({1} bytes).", fat32Bpb.big_sectors,
                                    fat32Bpb.big_sectors * fat32Bpb.bps).AppendLine();
                    XmlFsType.Clusters = fat32Bpb.big_sectors / fat32Bpb.spc;
                }

                sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine();
                sb.AppendFormat("Media descriptor: 0x{0:X2}", fat32Bpb.media).AppendLine();
                sb.AppendFormat("{0} sectors per FAT.", fat32Bpb.big_spfat).AppendLine();
                sb.AppendFormat("{0} sectors per track.", fat32Bpb.sptrk).AppendLine();
                sb.AppendFormat("{0} heads.", fat32Bpb.heads).AppendLine();
                sb.AppendFormat("{0} hidden sectors before BPB.", fat32Bpb.hsectors).AppendLine();
                sb.AppendFormat("Cluster of root directory: {0}", fat32Bpb.root_cluster).AppendLine();
                sb.AppendFormat("Sector of FSINFO structure: {0}", fat32Bpb.fsinfo_sector).AppendLine();
                sb.AppendFormat("Sector of backup FAT32 parameter block: {0}", fat32Bpb.backup_sector).AppendLine();
                sb.AppendFormat("Drive number: 0x{0:X2}", fat32Bpb.drive_no).AppendLine();
                sb.AppendFormat("Volume Serial Number: 0x{0:X8}", fat32Bpb.serial_no).AppendLine();
                XmlFsType.VolumeSerial = $"{fat32Bpb.serial_no:X8}";

                if ((fat32Bpb.flags & 0xF8) == 0x00)
                {
                    if ((fat32Bpb.flags & 0x01) == 0x01)
                    {
                        sb.AppendLine("Volume should be checked on next mount.");
                        XmlFsType.Dirty = true;
                    }

                    if ((fat32Bpb.flags & 0x02) == 0x02)
                    {
                        sb.AppendLine("Disk surface should be on next mount.");
                    }
                }

                if ((fat32Bpb.mirror_flags & 0x80) == 0x80)
                {
                    sb.AppendFormat("FATs are out of sync. FAT #{0} is in use.", fat32Bpb.mirror_flags & 0xF)
                    .AppendLine();
                }
                else
                {
                    sb.AppendLine("All copies of FAT are the same.");
                }

                if ((fat32Bpb.mirror_flags & 0x6F20) == 0x6F20)
                {
                    sb.AppendLine("DR-DOS will boot this FAT32 using CHS.");
                }
                else if ((fat32Bpb.mirror_flags & 0x4F20) == 0x4F20)
                {
                    sb.AppendLine("DR-DOS will boot this FAT32 using LBA.");
                }

                if (fat32Bpb.signature == 0x29)
                {
                    XmlFsType.VolumeName = Encoding.ASCII.GetString(fat32Bpb.volume_label);
                    sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fat32Bpb.fs_type))
                    .AppendLine();
                    bootChk = Sha1Context.Data(fat32Bpb.boot_code, out _);
                }
                else
                {
                    bootChk = Sha1Context.Data(shortFat32Bpb.boot_code, out _);
                }

                // Check that jumps to a correct boot code position and has boot signature set.
                // This will mean that the volume will boot, even if just to say "this is not bootable change disk"......
                XmlFsType.Bootable =
                    fat32Bpb.jump[0] == 0xEB && fat32Bpb.jump[1] >= minBootNearJump && fat32Bpb.jump[1] < 0x80 ||
                    fat32Bpb.jump[0] == 0xE9 && fat32Bpb.jump.Length >= 3 &&
                    BitConverter.ToUInt16(fat32Bpb.jump, 1) >= minBootNearJump &&
                    BitConverter.ToUInt16(fat32Bpb.jump, 1) <= 0x1FC;

                sectorsPerRealSector = fat32Bpb.bps / imagePlugin.Info.SectorSize;
                // First root directory sector
                rootDirectorySector =
                    (ulong)((fat32Bpb.root_cluster - 2) * fat32Bpb.spc + fat32Bpb.big_spfat * fat32Bpb.fats_no +
                            fat32Bpb.rsectors) * sectorsPerRealSector;
                sectorsForRootDirectory = 1;

                if (fat32Bpb.fsinfo_sector + partition.Start <= partition.End)
                {
                    byte[]       fsinfoSector = imagePlugin.ReadSector(fat32Bpb.fsinfo_sector + partition.Start);
                    FsInfoSector fsInfo       =
                        Marshal.ByteArrayToStructureLittleEndian <FsInfoSector>(fsinfoSector);

                    if (fsInfo.signature1 == FSINFO_SIGNATURE1 && fsInfo.signature2 == FSINFO_SIGNATURE2 &&
                        fsInfo.signature3 == FSINFO_SIGNATURE3)
                    {
                        if (fsInfo.free_clusters < 0xFFFFFFFF)
                        {
                            sb.AppendFormat("{0} free clusters", fsInfo.free_clusters).AppendLine();
                            XmlFsType.FreeClusters          = fsInfo.free_clusters;
                            XmlFsType.FreeClustersSpecified = true;
                        }

                        if (fsInfo.last_cluster > 2 && fsInfo.last_cluster < 0xFFFFFFFF)
                        {
                            sb.AppendFormat("Last allocated cluster {0}", fsInfo.last_cluster).AppendLine();
                        }
                    }
                }

                break;
            }

            // Some fields could overflow fake BPB, those will be handled below
            case BpbKind.Atari:
            {
                ushort sum = 0;
                BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
                for (int i = 0; i < bpbSector.Length; i += 2)
                {
                    sum += BigEndianBitConverter.ToUInt16(bpbSector, i);
                }

                // TODO: Check this
                if (sum == 0x1234)
                {
                    XmlFsType.Bootable = true;
                    StringBuilder atariSb = new StringBuilder();
                    atariSb.AppendFormat("cmdload will be loaded with value {0:X4}h",
                                         BigEndianBitConverter.ToUInt16(bpbSector, 0x01E)).AppendLine();
                    atariSb.AppendFormat("Boot program will be loaded at address {0:X4}h", atariBpb.ldaaddr)
                    .AppendLine();
                    atariSb.AppendFormat("FAT and directory will be cached at address {0:X4}h", atariBpb.fatbuf)
                    .AppendLine();
                    if (atariBpb.ldmode == 0)
                    {
                        byte[] tmp = new byte[8];
                        Array.Copy(atariBpb.fname, 0, tmp, 0, 8);
                        string fname = Encoding.ASCII.GetString(tmp).Trim();
                        tmp = new byte[3];
                        Array.Copy(atariBpb.fname, 8, tmp, 0, 3);
                        string extension = Encoding.ASCII.GetString(tmp).Trim();
                        string filename;

                        if (string.IsNullOrEmpty(extension))
                        {
                            filename = fname;
                        }
                        else
                        {
                            filename = fname + "." + extension;
                        }

                        atariSb.AppendFormat("Boot program resides in file \"{0}\"", filename).AppendLine();
                    }
                    else
                    {
                        atariSb
                        .AppendFormat("Boot program starts in sector {0} and is {1} sectors long ({2} bytes)",
                                      atariBpb.ssect, atariBpb.sectcnt, atariBpb.sectcnt * atariBpb.bps)
                        .AppendLine();
                    }

                    extraInfo = atariSb.ToString();
                }

                break;
            }

            case BpbKind.Human:
                XmlFsType.Bootable = true;
                break;
            }

            if (!isFat32)
            {
                // This is to support FAT partitions on hybrid ISO/USB images
                if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc)
                {
                    fakeBpb.bps      *= 4;
                    fakeBpb.spc      /= 4;
                    fakeBpb.spfat    /= 4;
                    fakeBpb.hsectors /= 4;
                    fakeBpb.sptrk    /= 4;
                    fakeBpb.rsectors /= 4;

                    if (fakeBpb.spc == 0)
                    {
                        fakeBpb.spc = 1;
                    }
                }

                // This assumes no sane implementation will violate cluster size rules
                // However nothing prevents this to happen
                // If first file on disk uses only one cluster there is absolutely no way to differentiate between FAT12 and FAT16,
                // so let's hope implementations use common sense?
                if (!isFat12 && !isFat16)
                {
                    ulong clusters;

                    if (fakeBpb.sectors == 0)
                    {
                        clusters = fakeBpb.spc == 0 ? fakeBpb.big_sectors : fakeBpb.big_sectors / fakeBpb.spc;
                    }
                    else
                    {
                        clusters = fakeBpb.spc == 0 ? fakeBpb.sectors : (ulong)fakeBpb.sectors / fakeBpb.spc;
                    }

                    if (clusters < 4089)
                    {
                        isFat12 = true;
                    }
                    else
                    {
                        isFat16 = true;
                    }
                }

                if (isFat12)
                {
                    switch (bpbKind)
                    {
                    case BpbKind.Atari:
                        sb.AppendLine("Atari FAT12");
                        break;

                    case BpbKind.Apricot:
                        sb.AppendLine("Apricot FAT12");
                        break;

                    case BpbKind.Human:
                        sb.AppendLine("Human68k FAT12");
                        break;

                    default:
                        sb.AppendLine("Microsoft FAT12");
                        break;
                    }

                    XmlFsType.Type = "FAT12";
                }
                else if (isFat16)
                {
                    sb.AppendLine(bpbKind == BpbKind.Atari
                                      ? "Atari FAT16"
                                      : bpbKind == BpbKind.Human
                                          ? "Human68k FAT16"
                                          : "Microsoft FAT16");
                    XmlFsType.Type = "FAT16";
                }

                if (bpbKind == BpbKind.Atari)
                {
                    if (atariBpb.serial_no[0] == 0x49 && atariBpb.serial_no[1] == 0x48 && atariBpb.serial_no[2] == 0x43)
                    {
                        sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker.");
                    }
                    else
                    {
                        XmlFsType.VolumeSerial =
                            $"{atariBpb.serial_no[0]:X2}{atariBpb.serial_no[1]:X2}{atariBpb.serial_no[2]:X2}";
                    }

                    XmlFsType.SystemIdentifier = StringHandlers.CToString(atariBpb.oem_name);
                    if (string.IsNullOrEmpty(XmlFsType.SystemIdentifier))
                    {
                        XmlFsType.SystemIdentifier = null;
                    }
                }
                else if (fakeBpb.oem_name != null)
                {
                    if (fakeBpb.oem_name[5] == 0x49 && fakeBpb.oem_name[6] == 0x48 && fakeBpb.oem_name[7] == 0x43)
                    {
                        sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker.");
                    }
                    else
                    {
                        // Later versions of Windows create a DOS 3 BPB without OEM name on 8 sectors/track floppies
                        // OEM ID should be ASCII, otherwise ignore it
                        if (fakeBpb.oem_name[0] >= 0x20 && fakeBpb.oem_name[0] <= 0x7F && fakeBpb.oem_name[1] >= 0x20 &&
                            fakeBpb.oem_name[1] <= 0x7F && fakeBpb.oem_name[2] >= 0x20 && fakeBpb.oem_name[2] <= 0x7F &&
                            fakeBpb.oem_name[3] >= 0x20 && fakeBpb.oem_name[3] <= 0x7F && fakeBpb.oem_name[4] >= 0x20 &&
                            fakeBpb.oem_name[4] <= 0x7F && fakeBpb.oem_name[5] >= 0x20 && fakeBpb.oem_name[5] <= 0x7F &&
                            fakeBpb.oem_name[6] >= 0x20 && fakeBpb.oem_name[6] <= 0x7F && fakeBpb.oem_name[7] >= 0x20 &&
                            fakeBpb.oem_name[7] <= 0x7F)
                        {
                            XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name);
                        }
                        else if (fakeBpb.oem_name[0] < 0x20 && fakeBpb.oem_name[1] >= 0x20 &&
                                 fakeBpb.oem_name[1] <= 0x7F && fakeBpb.oem_name[2] >= 0x20 &&
                                 fakeBpb.oem_name[2] <= 0x7F && fakeBpb.oem_name[3] >= 0x20 &&
                                 fakeBpb.oem_name[3] <= 0x7F && fakeBpb.oem_name[4] >= 0x20 &&
                                 fakeBpb.oem_name[4] <= 0x7F && fakeBpb.oem_name[5] >= 0x20 &&
                                 fakeBpb.oem_name[5] <= 0x7F && fakeBpb.oem_name[6] >= 0x20 &&
                                 fakeBpb.oem_name[6] <= 0x7F && fakeBpb.oem_name[7] >= 0x20 &&
                                 fakeBpb.oem_name[7] <= 0x7F)
                        {
                            XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name, Encoding, start: 1);
                        }
                    }

                    if (fakeBpb.signature == 0x28 || fakeBpb.signature == 0x29)
                    {
                        XmlFsType.VolumeSerial = $"{fakeBpb.serial_no:X8}";
                    }
                }

                if (XmlFsType.SystemIdentifier != null)
                {
                    sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine();
                }

                sb.AppendFormat("{0} bytes per sector.", fakeBpb.bps).AppendLine();
                if (bpbKind != BpbKind.Human)
                {
                    if (fakeBpb.sectors == 0)
                    {
                        sb.AppendFormat("{0} sectors on volume ({1} bytes).", fakeBpb.big_sectors,
                                        fakeBpb.big_sectors * fakeBpb.bps).AppendLine();
                        XmlFsType.Clusters = fakeBpb.spc == 0 ? fakeBpb.big_sectors : fakeBpb.big_sectors / fakeBpb.spc;
                    }
                    else
                    {
                        sb.AppendFormat("{0} sectors on volume ({1} bytes).", fakeBpb.sectors,
                                        fakeBpb.sectors * fakeBpb.bps).AppendLine();
                        XmlFsType.Clusters =
                            (ulong)(fakeBpb.spc == 0 ? fakeBpb.sectors : fakeBpb.sectors / fakeBpb.spc);
                    }
                }
                else
                {
                    XmlFsType.Clusters = humanBpb.clusters == 0 ? humanBpb.big_clusters : humanBpb.clusters;
                    sb.AppendFormat("{0} sectors on volume ({1} bytes).",
                                    XmlFsType.Clusters * humanBpb.bpc / imagePlugin.Info.SectorSize,
                                    XmlFsType.Clusters * humanBpb.bpc).AppendLine();
                }

                sb.AppendFormat("{0} sectors per cluster.", fakeBpb.spc).AppendLine();
                sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine();
                XmlFsType.ClusterSize = (uint)(fakeBpb.bps * fakeBpb.spc);
                sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fakeBpb.rsectors).AppendLine();
                sb.AppendFormat("{0} FATs.", fakeBpb.fats_no).AppendLine();
                sb.AppendFormat("{0} entries on root directory.", fakeBpb.root_ent).AppendLine();

                if (fakeBpb.media > 0)
                {
                    sb.AppendFormat("Media descriptor: 0x{0:X2}", fakeBpb.media).AppendLine();
                }

                sb.AppendFormat("{0} sectors per FAT.", fakeBpb.spfat).AppendLine();

                if (fakeBpb.sptrk > 0 && fakeBpb.sptrk < 64 && fakeBpb.heads > 0 && fakeBpb.heads < 256)
                {
                    sb.AppendFormat("{0} sectors per track.", fakeBpb.sptrk).AppendLine();
                    sb.AppendFormat("{0} heads.", fakeBpb.heads).AppendLine();
                }

                if (fakeBpb.hsectors <= partition.Start)
                {
                    sb.AppendFormat("{0} hidden sectors before BPB.", fakeBpb.hsectors).AppendLine();
                }

                if (fakeBpb.signature == 0x28 || fakeBpb.signature == 0x29 || andosOemCorrect)
                {
                    sb.AppendFormat("Drive number: 0x{0:X2}", fakeBpb.drive_no).AppendLine();

                    if (XmlFsType.VolumeSerial != null)
                    {
                        sb.AppendFormat("Volume Serial Number: {0}", XmlFsType.VolumeSerial).AppendLine();
                    }

                    if ((fakeBpb.flags & 0xF8) == 0x00)
                    {
                        if ((fakeBpb.flags & 0x01) == 0x01)
                        {
                            sb.AppendLine("Volume should be checked on next mount.");
                            XmlFsType.Dirty = true;
                        }

                        if ((fakeBpb.flags & 0x02) == 0x02)
                        {
                            sb.AppendLine("Disk surface should be on next mount.");
                        }
                    }

                    if (fakeBpb.signature == 0x29 || andosOemCorrect)
                    {
                        XmlFsType.VolumeName = Encoding.ASCII.GetString(fakeBpb.volume_label);
                        sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fakeBpb.fs_type)).AppendLine();
                    }
                }
                else if (bpbKind == BpbKind.Atari && XmlFsType.VolumeSerial != null)
                {
                    sb.AppendFormat("Volume Serial Number: {0}", XmlFsType.VolumeSerial).AppendLine();
                }

                bootChk = Sha1Context.Data(fakeBpb.boot_code, out _);

                // Workaround that PCExchange jumps into "FAT16   "...
                if (XmlFsType.SystemIdentifier == "PCX 2.0 ")
                {
                    fakeBpb.jump[1] += 8;
                }

                // Check that jumps to a correct boot code position and has boot signature set.
                // This will mean that the volume will boot, even if just to say "this is not bootable change disk"......
                if (XmlFsType.Bootable == false && fakeBpb.jump != null)
                {
                    XmlFsType.Bootable |=
                        fakeBpb.jump[0] == 0xEB && fakeBpb.jump[1] >= minBootNearJump && fakeBpb.jump[1] < 0x80 ||
                        fakeBpb.jump[0] == 0xE9 && fakeBpb.jump.Length >= 3 &&
                        BitConverter.ToUInt16(fakeBpb.jump, 1) >= minBootNearJump &&
                        BitConverter.ToUInt16(fakeBpb.jump, 1) <= 0x1FC;
                }

                sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize;
                // First root directory sector
                rootDirectorySector =
                    (ulong)(fakeBpb.spfat * fakeBpb.fats_no + fakeBpb.rsectors) * sectorsPerRealSector;
                sectorsForRootDirectory = (uint)(fakeBpb.root_ent * 32 / imagePlugin.Info.SectorSize);
            }

            if (extraInfo != null)
            {
                sb.Append(extraInfo);
            }

            if (rootDirectorySector + partition.Start < partition.End &&
                imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc)
            {
                byte[] rootDirectory =
                    imagePlugin.ReadSectors(rootDirectorySector + partition.Start, sectorsForRootDirectory);

                if (bpbKind == BpbKind.DecRainbow)
                {
                    MemoryStream 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);
                    }

                    rootDirectory = rootMs.ToArray();
                }

                for (int i = 0; i < rootDirectory.Length; i += 32)
                {
                    // Not a correct entry
                    if (rootDirectory[i] < DIRENT_MIN && rootDirectory[i] != DIRENT_E5)
                    {
                        continue;
                    }

                    // Deleted or subdirectory entry
                    if (rootDirectory[i] == DIRENT_SUBDIR || rootDirectory[i] == DIRENT_DELETED)
                    {
                        continue;
                    }

                    // Not a volume label
                    if (rootDirectory[i + 0x0B] != 0x08 && rootDirectory[i + 0x0B] != 0x28)
                    {
                        continue;
                    }

                    DirectoryEntry entry =
                        Marshal.ByteArrayToStructureLittleEndian <DirectoryEntry>(rootDirectory, i, 32);

                    byte[] fullname = new byte[11];
                    Array.Copy(entry.filename, 0, fullname, 0, 8);
                    Array.Copy(entry.extension, 0, fullname, 8, 3);
                    string volname = Encoding.GetString(fullname).Trim();
                    if (!string.IsNullOrEmpty(volname))
                    {
                        XmlFsType.VolumeName =
                            entry.caseinfo.HasFlag(CaseInfo.AllLowerCase) ? volname.ToLower() : volname;
                    }

                    if (entry.ctime > 0 && entry.cdate > 0)
                    {
                        XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime);
                        if (entry.ctime_ms > 0)
                        {
                            XmlFsType.CreationDate = XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10);
                        }
                        XmlFsType.CreationDateSpecified = true;
                        sb.AppendFormat("Volume created on {0}", XmlFsType.CreationDate).AppendLine();
                    }

                    if (entry.mtime > 0 && entry.mdate > 0)
                    {
                        XmlFsType.ModificationDate          = DateHandlers.DosToDateTime(entry.mdate, entry.mtime);
                        XmlFsType.ModificationDateSpecified = true;
                        sb.AppendFormat("Volume last modified on {0}", XmlFsType.ModificationDate).AppendLine();
                    }

                    if (entry.adate > 0)
                    {
                        sb.AppendFormat("Volume last accessed on {0:d}", DateHandlers.DosToDateTime(entry.adate, 0))
                        .AppendLine();
                    }

                    break;
                }
            }

            if (!string.IsNullOrEmpty(XmlFsType.VolumeName))
            {
                sb.AppendFormat("Volume label: {0}", XmlFsType.VolumeName).AppendLine();
            }
            if (XmlFsType.Bootable)
            {
                // Intel short jump
                if (bpbSector[0] == 0xEB && bpbSector[1] < 0x80)
                {
                    int    sigSize  = bpbSector[510] == 0x55 && bpbSector[511] == 0xAA ? 2 : 0;
                    byte[] bootCode = new byte[512 - sigSize - bpbSector[1] - 2];
                    Array.Copy(bpbSector, bpbSector[1] + 2, bootCode, 0, bootCode.Length);
                    Sha1Context.Data(bootCode, out _);
                }
                // Intel big jump
                else if (bpbSector[0] == 0xE9 && BitConverter.ToUInt16(bpbSector, 1) < 0x1FC)
                {
                    int    sigSize  = bpbSector[510] == 0x55 && bpbSector[511] == 0xAA ? 2 : 0;
                    byte[] bootCode = new byte[512 - sigSize - BitConverter.ToUInt16(bpbSector, 1) - 3];
                    Array.Copy(bpbSector, BitConverter.ToUInt16(bpbSector, 1) + 3, bootCode, 0, bootCode.Length);
                    Sha1Context.Data(bootCode, out _);
                }

                sb.AppendLine("Volume is bootable");
                sb.AppendFormat("Boot code's SHA1: {0}", bootChk).AppendLine();
                string bootName = knownBootHashes.FirstOrDefault(t => t.hash == bootChk).name;
                if (string.IsNullOrWhiteSpace(bootName))
                {
                    sb.AppendLine("Unknown boot code.");
                }
                else
                {
                    sb.AppendFormat("Boot code corresponds to {0}", bootName).AppendLine();
                }
            }

            information = sb.ToString();
        }
Exemple #4
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            stream.Seek(0, SeekOrigin.Begin);

            byte[] hdr = new byte[40];

            stream.Read(hdr, 0, 40);
            header = Marshal.ByteArrayToStructureLittleEndian <SaveDskFHeader>(hdr);

            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.magic = 0x{0:X4}", header.magic);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.mediaType = 0x{0:X2}", header.mediaType);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.sectorSize = {0}", header.sectorSize);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.clusterMask = {0}", header.clusterMask);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.clusterShift = {0}", header.clusterShift);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.reservedSectors = {0}", header.reservedSectors);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.fatCopies = {0}", header.fatCopies);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.rootEntries = {0}", header.rootEntries);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.firstCluster = {0}", header.firstCluster);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.clustersCopied = {0}", header.clustersCopied);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.sectorsPerFat = {0}", header.sectorsPerFat);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.checksum = 0x{0:X8}", header.checksum);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.cylinders = {0}", header.cylinders);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.heads = {0}", header.heads);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.sectorsPerTrack = {0}", header.sectorsPerTrack);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.padding = {0}", header.padding);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.sectorsCopied = {0}", header.sectorsCopied);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.commentOffset = {0}", header.commentOffset);
            AaruConsole.DebugWriteLine("SaveDskF plugin", "header.dataOffset = {0}", header.dataOffset);

            if (header.dataOffset == 0 &&
                header.magic == SDF_MAGIC_OLD)
            {
                header.dataOffset = 512;
            }

            byte[] cmt = new byte[header.dataOffset - header.commentOffset];
            stream.Seek(header.commentOffset, SeekOrigin.Begin);
            stream.Read(cmt, 0, cmt.Length);

            if (cmt.Length > 1)
            {
                imageInfo.Comments = StringHandlers.CToString(cmt, Encoding.GetEncoding("ibm437"));
            }

            calculatedChk = 0;
            stream.Seek(0, SeekOrigin.Begin);

            int b;

            do
            {
                b = stream.ReadByte();

                if (b >= 0)
                {
                    calculatedChk += (uint)b;
                }
            } while(b >= 0);

            AaruConsole.DebugWriteLine("SaveDskF plugin", "Calculated checksum = 0x{0:X8}, {1}", calculatedChk,
                                       calculatedChk == header.checksum);

            imageInfo.Application          = "SaveDskF";
            imageInfo.CreationTime         = imageFilter.GetCreationTime();
            imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
            imageInfo.MediaTitle           = imageFilter.GetFilename();
            imageInfo.ImageSize            = (ulong)(stream.Length - header.dataOffset);
            imageInfo.Sectors    = (ulong)(header.sectorsPerTrack * header.heads * header.cylinders);
            imageInfo.SectorSize = header.sectorSize;

            imageInfo.MediaType = Geometry.GetMediaType((header.cylinders, (byte)header.heads, header.sectorsPerTrack,
                                                         header.sectorSize, MediaEncoding.MFM, false));

            imageInfo.XmlMediaType = XmlMediaType.BlockMedia;

            AaruConsole.VerboseWriteLine("SaveDskF image contains a disk of type {0}", imageInfo.MediaType);

            if (!string.IsNullOrEmpty(imageInfo.Comments))
            {
                AaruConsole.VerboseWriteLine("SaveDskF comments: {0}", imageInfo.Comments);
            }

            // TODO: Support compressed images
            if (header.magic == SDF_MAGIC_COMPRESSED)
            {
                throw new
                      FeatureSupportedButNotImplementedImageException("Compressed SaveDskF images are not supported.");
            }

            // SaveDskF only ommits ending clusters, leaving no gaps behind, so reading all data we have...
            stream.Seek(header.dataOffset, SeekOrigin.Begin);
            decodedDisk = new byte[imageInfo.Sectors * imageInfo.SectorSize];
            stream.Read(decodedDisk, 0, (int)(stream.Length - header.dataOffset));

            imageInfo.Cylinders       = header.cylinders;
            imageInfo.Heads           = header.heads;
            imageInfo.SectorsPerTrack = header.sectorsPerTrack;

            return(true);
        }
Exemple #5
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            stream.Seek(0, SeekOrigin.Begin);

            if (stream.Length < Marshal.SizeOf <Header>())
            {
                return(false);
            }

            byte[] hdrB = new byte[Marshal.SizeOf <Header>()];
            stream.Read(hdrB, 0, hdrB.Length);

            Header hdr = Marshal.ByteArrayToStructureLittleEndian <Header>(hdrB);

            AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.addInfoLen = {0}", hdr.addInfoLen);
            AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.cylinders = {0}", hdr.cylinders);
            AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.dataOff = {0}", hdr.dataOff);
            AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.descOff = {0}", hdr.descOff);
            AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.flags = {0}", hdr.flags);
            AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.heads = {0}", hdr.heads);

            stream.Seek(hdr.descOff, SeekOrigin.Begin);
            byte[] description = new byte[hdr.dataOff - hdr.descOff];
            stream.Read(description, 0, description.Length);
            _imageInfo.Comments = StringHandlers.CToString(description);

            AaruConsole.DebugWriteLine("UkvFdi plugin", "hdr.description = \"{0}\"", _imageInfo.Comments);

            stream.Seek(0xE + hdr.addInfoLen, SeekOrigin.Begin);

            long spt = long.MaxValue;

            uint[][][] sectorsOff = new uint[hdr.cylinders][][];
            _sectorsData = new byte[hdr.cylinders][][][];

            _imageInfo.Cylinders = hdr.cylinders;
            _imageInfo.Heads     = hdr.heads;

            // Read track descriptors
            for (ushort cyl = 0; cyl < hdr.cylinders; cyl++)
            {
                sectorsOff[cyl]   = new uint[hdr.heads][];
                _sectorsData[cyl] = new byte[hdr.heads][][];

                for (ushort head = 0; head < hdr.heads; head++)
                {
                    byte[] sctB = new byte[4];
                    stream.Read(sctB, 0, 4);
                    stream.Seek(2, SeekOrigin.Current);
                    byte sectors = (byte)stream.ReadByte();
                    uint trkOff  = BitConverter.ToUInt32(sctB, 0);

                    AaruConsole.DebugWriteLine("UkvFdi plugin", "trkhdr.c = {0}", cyl);
                    AaruConsole.DebugWriteLine("UkvFdi plugin", "trkhdr.h = {0}", head);
                    AaruConsole.DebugWriteLine("UkvFdi plugin", "trkhdr.sectors = {0}", sectors);
                    AaruConsole.DebugWriteLine("UkvFdi plugin", "trkhdr.off = {0}", trkOff);

                    sectorsOff[cyl][head]   = new uint[sectors];
                    _sectorsData[cyl][head] = new byte[sectors][];

                    if (sectors < spt &&
                        sectors > 0)
                    {
                        spt = sectors;
                    }

                    for (ushort sec = 0; sec < sectors; sec++)
                    {
                        byte   c    = (byte)stream.ReadByte();
                        byte   h    = (byte)stream.ReadByte();
                        byte   r    = (byte)stream.ReadByte();
                        byte   n    = (byte)stream.ReadByte();
                        var    f    = (SectorFlags)stream.ReadByte();
                        byte[] offB = new byte[2];
                        stream.Read(offB, 0, 2);
                        ushort secOff = BitConverter.ToUInt16(offB, 0);

                        AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.c = {0}", c);
                        AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.h = {0}", h);
                        AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.r = {0}", r);
                        AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.n = {0} ({1})", n, 128 << n);
                        AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.f = {0}", f);

                        AaruConsole.DebugWriteLine("UkvFdi plugin", "sechdr.off = {0} ({1})", secOff,
                                                   secOff + trkOff + hdr.dataOff);

                        // TODO: This assumes sequential sectors.
                        sectorsOff[cyl][head][sec]   = secOff + trkOff + hdr.dataOff;
                        _sectorsData[cyl][head][sec] = new byte[128 << n];

                        if (128 << n > _imageInfo.SectorSize)
                        {
                            _imageInfo.SectorSize = (uint)(128 << n);
                        }
                    }
                }
            }

            // Read sectors
            for (ushort cyl = 0; cyl < hdr.cylinders; cyl++)
            {
                bool emptyCyl = false;

                for (ushort head = 0; head < hdr.heads; head++)
                {
                    for (ushort sec = 0; sec < sectorsOff[cyl][head].Length; sec++)
                    {
                        stream.Seek(sectorsOff[cyl][head][sec], SeekOrigin.Begin);
                        stream.Read(_sectorsData[cyl][head][sec], 0, _sectorsData[cyl][head][sec].Length);
                    }

                    // For empty cylinders
                    if (sectorsOff[cyl][head].Length != 0)
                    {
                        continue;
                    }

                    if (cyl + 1 == hdr.cylinders ||

                        // Next cylinder is also empty
                        sectorsOff[cyl + 1][head].Length == 0)
                    {
                        emptyCyl = true;
                    }

                    // Create empty sectors
                    else
                    {
                        _sectorsData[cyl][head] = new byte[spt][];

                        for (int i = 0; i < spt; i++)
                        {
                            _sectorsData[cyl][head][i] = new byte[_imageInfo.SectorSize];
                        }
                    }
                }

                if (emptyCyl)
                {
                    _imageInfo.Cylinders--;
                }
            }

            // TODO: What about double sided, half track pitch compact floppies?
            _imageInfo.MediaType            = MediaType.CompactFloppy;
            _imageInfo.ImageSize            = (ulong)stream.Length - hdr.dataOff;
            _imageInfo.CreationTime         = imageFilter.GetCreationTime();
            _imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
            _imageInfo.MediaTitle           = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
            _imageInfo.SectorsPerTrack      = (uint)spt;
            _imageInfo.Sectors      = _imageInfo.Cylinders * _imageInfo.Heads * _imageInfo.SectorsPerTrack;
            _imageInfo.XmlMediaType = XmlMediaType.BlockMedia;

            return(true);
        }
Exemple #6
0
        /// <inheritdoc />
        /// <summary>Lists contents from a directory.</summary>
        /// <param name="path">Directory path.</param>
        /// <param name="contents">Directory contents.</param>
        public Errno ReadDir(string path, out List <string> contents)
        {
            contents = null;

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

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

                return(Errno.NoError);
            }

            string cutPath = path.StartsWith("/", StringComparison.Ordinal) ? path.Substring(1).ToLower(_cultureInfo)
                                 : path.ToLower(_cultureInfo);

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

                return(Errno.NoError);
            }

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

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

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

            if (!entry.Value.Dirent.attributes.HasFlag(FatAttributes.Subdirectory))
            {
                return(Errno.NotDirectory);
            }

            string currentPath = pieces[0];

            currentDirectory = _rootDirectoryCache;

            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.Dirent.attributes.HasFlag(FatAttributes.Subdirectory))
                {
                    return(Errno.NotDirectory);
                }

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

                if (_fat32)
                {
                    currentCluster += (uint)(entry.Value.Dirent.ea_handle << 16);
                }

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

                // Reserved unallocated directory, seen in Atari ST
                if (currentCluster == 0)
                {
                    _directoryCache[currentPath] = new Dictionary <string, CompleteDirectoryEntry>();
                    contents = new List <string>();

                    return(Errno.NoError);
                }

                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 = _image.ReadSectors(_firstClusterSector + (clusters[i] * _sectorsPerCluster),
                                                       _sectorsPerCluster);

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

                currentDirectory = new Dictionary <string, CompleteDirectoryEntry>();
                byte[] lastLfnName     = null;
                byte   lastLfnChecksum = 0;

                for (int pos = 0; pos < directoryBuffer.Length; pos += Marshal.SizeOf <DirectoryEntry>())
                {
                    DirectoryEntry dirent =
                        Marshal.ByteArrayToStructureLittleEndian <DirectoryEntry>(directoryBuffer, pos,
                                                                                  Marshal.SizeOf <DirectoryEntry>());

                    if (dirent.filename[0] == DIRENT_FINISHED)
                    {
                        break;
                    }

                    if (dirent.attributes.HasFlag(FatAttributes.LFN))
                    {
                        if (_namespace != Namespace.Lfn &&
                            _namespace != Namespace.Ecs)
                        {
                            continue;
                        }

                        LfnEntry lfnEntry =
                            Marshal.ByteArrayToStructureLittleEndian <LfnEntry>(directoryBuffer, pos,
                                                                                Marshal.SizeOf <LfnEntry>());

                        int lfnSequence = lfnEntry.sequence & LFN_MASK;

                        if ((lfnEntry.sequence & LFN_ERASED) > 0)
                        {
                            continue;
                        }

                        if ((lfnEntry.sequence & LFN_LAST) > 0)
                        {
                            lastLfnName     = new byte[lfnSequence * 26];
                            lastLfnChecksum = lfnEntry.checksum;
                        }

                        if (lastLfnName is null)
                        {
                            continue;
                        }

                        if (lfnEntry.checksum != lastLfnChecksum)
                        {
                            continue;
                        }

                        lfnSequence--;

                        Array.Copy(lfnEntry.name1, 0, lastLfnName, lfnSequence * 26, 10);
                        Array.Copy(lfnEntry.name2, 0, lastLfnName, (lfnSequence * 26) + 10, 12);
                        Array.Copy(lfnEntry.name3, 0, lastLfnName, (lfnSequence * 26) + 22, 4);

                        continue;
                    }

                    // Not a correct entry
                    if (dirent.filename[0] < DIRENT_MIN &&
                        dirent.filename[0] != DIRENT_E5)
                    {
                        continue;
                    }

                    // Self
                    if (Encoding.GetString(dirent.filename).TrimEnd() == ".")
                    {
                        continue;
                    }

                    // Parent
                    if (Encoding.GetString(dirent.filename).TrimEnd() == "..")
                    {
                        continue;
                    }

                    // Deleted
                    if (dirent.filename[0] == DIRENT_DELETED)
                    {
                        continue;
                    }

                    string filename;

                    if (dirent.attributes.HasFlag(FatAttributes.VolumeLabel))
                    {
                        continue;
                    }

                    var completeEntry = new CompleteDirectoryEntry
                    {
                        Dirent = dirent
                    };

                    if ((_namespace == Namespace.Lfn || _namespace == Namespace.Ecs) &&
                        lastLfnName != null)
                    {
                        byte calculatedLfnChecksum = LfnChecksum(dirent.filename, dirent.extension);

                        if (calculatedLfnChecksum == lastLfnChecksum)
                        {
                            filename = StringHandlers.CToString(lastLfnName, Encoding.Unicode, true);

                            completeEntry.Lfn = filename;
                            lastLfnName       = null;
                            lastLfnChecksum   = 0;
                        }
                    }

                    if (dirent.filename[0] == DIRENT_E5)
                    {
                        dirent.filename[0] = DIRENT_DELETED;
                    }

                    string name      = Encoding.GetString(dirent.filename).TrimEnd();
                    string extension = Encoding.GetString(dirent.extension).TrimEnd();

                    if (name == "" &&
                        extension == "")
                    {
                        AaruConsole.DebugWriteLine("FAT filesystem", "Found empty filename in {0}", path);

                        if (!_debug ||
                            (dirent.size > 0 && dirent.start_cluster == 0))
                        {
                            continue; // Skip invalid name
                        }
                        // If debug, add it
                        name = ":{EMPTYNAME}:";

                        // Try to create a unique filename with an extension from 000 to 999
                        for (int uniq = 0; uniq < 1000; uniq++)
                        {
                            extension = $"{uniq:D03}";

                            if (!currentDirectory.ContainsKey($"{name}.{extension}"))
                            {
                                break;
                            }
                        }

                        // If we couldn't find it, just skip over
                        if (currentDirectory.ContainsKey($"{name}.{extension}"))
                        {
                            continue;
                        }
                    }

                    if (_namespace == Namespace.Nt)
                    {
                        if (dirent.caseinfo.HasFlag(CaseInfo.LowerCaseExtension))
                        {
                            extension = extension.ToLower(CultureInfo.CurrentCulture);
                        }

                        if (dirent.caseinfo.HasFlag(CaseInfo.LowerCaseBasename))
                        {
                            name = name.ToLower(CultureInfo.CurrentCulture);
                        }
                    }

                    if (extension != "")
                    {
                        filename = name + "." + extension;
                    }
                    else
                    {
                        filename = name;
                    }

                    if (_namespace == Namespace.Human)
                    {
                        HumanDirectoryEntry humanEntry =
                            Marshal.ByteArrayToStructureLittleEndian <HumanDirectoryEntry>(directoryBuffer, pos,
                                                                                           Marshal.SizeOf <HumanDirectoryEntry>());

                        completeEntry.HumanDirent = humanEntry;

                        name      = StringHandlers.CToString(humanEntry.name1, Encoding).TrimEnd();
                        extension = StringHandlers.CToString(humanEntry.extension, Encoding).TrimEnd();
                        string name2 = StringHandlers.CToString(humanEntry.name2, Encoding).TrimEnd();

                        if (extension != "")
                        {
                            filename = name + name2 + "." + extension;
                        }
                        else
                        {
                            filename = name + name2;
                        }

                        completeEntry.HumanName = filename;
                    }

                    // Atari ST allows slash AND colon so cannot simply substitute one for the other like in Mac filesystems
                    filename = filename.Replace('/', '\u2215');

                    // Using array accessor ensures that repeated entries just get substituted.
                    // Repeated entries are not allowed but some bad implementations (e.g. FAT32.IFS)allow to create them
                    // when using spaces
                    completeEntry.Shortname = filename;
                    currentDirectory[completeEntry.ToString()] = completeEntry;
                }

                // Check OS/2 .LONGNAME
                if (_eaCache != null &&
                    (_namespace == Namespace.Os2 || _namespace == Namespace.Ecs) &&
                    !_fat32)
                {
                    List <KeyValuePair <string, CompleteDirectoryEntry> > filesWithEas =
                        currentDirectory.Where(t => t.Value.Dirent.ea_handle != 0).ToList();

                    foreach (KeyValuePair <string, CompleteDirectoryEntry> fileWithEa in filesWithEas)
                    {
                        Dictionary <string, byte[]> eas = GetEas(fileWithEa.Value.Dirent.ea_handle);

                        if (eas is null)
                        {
                            continue;
                        }

                        if (!eas.TryGetValue("com.microsoft.os2.longname", out byte[] longnameEa))
Exemple #7
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.ASCII;
            information = "";
            StringBuilder 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)
            {
                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;
                    }

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

                    break;
                }

                case 3:
                {
                    vpd = true;
                    break;
                }
                }

                counter++;
            }

            DecodedVolumeDescriptor decodedVd;
            DecodedVolumeDescriptor 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[]>();
            StringBuilder           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;
                            AppleOldId 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);

                DicConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.load_rba = {0}",
                                          initialEntry.load_rba);
                DicConsole.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 SECTION_COUNTER = 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}:", SECTION_COUNTER);
                    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");
                        }

                        ElToritoFlags 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();
        }
Exemple #8
0
        public bool Open(IFilter imageFilter)
        {
            string comments = string.Empty;
            Stream stream   = imageFilter.GetDataForkStream();

            stream.Seek(0, SeekOrigin.Begin);

            byte[] header = new byte[32];
            stream.Read(header, 0, 32);

            WCDiskImageFileHeader fheader = Marshal.ByteArrayToStructureLittleEndian <WCDiskImageFileHeader>(header);

            DicConsole.DebugWriteLine("d2f plugin",
                                      "Detected WC DISK IMAGE with {0} heads, {1} tracks and {2} sectors per track.",
                                      fheader.heads, fheader.cylinders, fheader.sectorsPerTrack);

            imageInfo.Cylinders       = fheader.cylinders;
            imageInfo.SectorsPerTrack = fheader.sectorsPerTrack;
            imageInfo.SectorSize      = 512; // only 512 bytes per sector supported
            imageInfo.Heads           = fheader.heads;
            imageInfo.Sectors         = imageInfo.Heads * imageInfo.Cylinders * imageInfo.SectorsPerTrack;
            imageInfo.ImageSize       = imageInfo.Sectors * imageInfo.SectorSize;

            imageInfo.XmlMediaType = XmlMediaType.BlockMedia;

            imageInfo.CreationTime         = imageFilter.GetCreationTime();
            imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
            imageInfo.MediaTitle           = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());

            imageInfo.MediaType = Geometry.GetMediaType(((ushort)imageInfo.Cylinders, (byte)imageInfo.Heads,
                                                         (ushort)imageInfo.SectorsPerTrack, 512, MediaEncoding.MFM,
                                                         false));

            /* buffer the entire disk in memory */
            for (int cyl = 0; cyl < imageInfo.Cylinders; cyl++)
            {
                for (int head = 0; head < imageInfo.Heads; head++)
                {
                    ReadTrack(stream, cyl, head);
                }
            }

            /* if there are extra tracks, read them as well */
            if (fheader.extraTracks[0] == 1)
            {
                DicConsole.DebugWriteLine("d2f plugin", "Extra track 1 (head 0) present, reading");
                ReadTrack(stream, (int)imageInfo.Cylinders, 0);
            }

            if (fheader.extraTracks[1] == 1)
            {
                DicConsole.DebugWriteLine("d2f plugin", "Extra track 1 (head 1) present, reading");
                ReadTrack(stream, (int)imageInfo.Cylinders, 1);
            }

            if (fheader.extraTracks[2] == 1)
            {
                DicConsole.DebugWriteLine("d2f plugin", "Extra track 2 (head 0) present, reading");
                ReadTrack(stream, (int)imageInfo.Cylinders + 1, 0);
            }

            if (fheader.extraTracks[3] == 1)
            {
                DicConsole.DebugWriteLine("d2f plugin", "Extra track 2 (head 1) present, reading");
                ReadTrack(stream, (int)imageInfo.Cylinders + 1, 1);
            }

            /* adjust number of cylinders */
            if (fheader.extraTracks[0] == 1 ||
                fheader.extraTracks[1] == 1)
            {
                imageInfo.Cylinders++;
            }

            if (fheader.extraTracks[2] == 1 ||
                fheader.extraTracks[3] == 1)
            {
                imageInfo.Cylinders++;
            }

            /* read the comment and directory data if present */
            if (fheader.extraFlags.HasFlag(ExtraFlag.Comment))
            {
                DicConsole.DebugWriteLine("d2f plugin", "Comment present, reading");
                byte[] sheaderBuffer = new byte[6];
                stream.Read(sheaderBuffer, 0, 6);

                WCDiskImageSectorHeader sheader =
                    Marshal.ByteArrayToStructureLittleEndian <WCDiskImageSectorHeader>(sheaderBuffer);

                if (sheader.flag != SectorFlag.Comment)
                {
                    throw new InvalidDataException(string.Format("Invalid sector type '{0}' encountered",
                                                                 sheader.flag.ToString()));
                }

                byte[] comm = new byte[sheader.crc];
                stream.Read(comm, 0, sheader.crc);
                comments += Encoding.ASCII.GetString(comm) + Environment.NewLine;
            }

            if (fheader.extraFlags.HasFlag(ExtraFlag.Directory))
            {
                DicConsole.DebugWriteLine("d2f plugin", "Directory listing present, reading");
                byte[] sheaderBuffer = new byte[6];
                stream.Read(sheaderBuffer, 0, 6);

                WCDiskImageSectorHeader sheader =
                    Marshal.ByteArrayToStructureLittleEndian <WCDiskImageSectorHeader>(sheaderBuffer);

                if (sheader.flag != SectorFlag.Directory)
                {
                    throw new InvalidDataException(string.Format("Invalid sector type '{0}' encountered",
                                                                 sheader.flag.ToString()));
                }

                byte[] dir = new byte[sheader.crc];
                stream.Read(dir, 0, sheader.crc);
                comments += Encoding.ASCII.GetString(dir);
            }

            if (comments.Length > 0)
            {
                imageInfo.Comments = comments;
            }

            // save some variables for later use
            fileHeader    = fheader;
            wcImageFilter = imageFilter;

            return(true);
        }
Exemple #9
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            stream.Seek(0, SeekOrigin.Begin);

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

            byte[] pHdrB = new byte[Marshal.SizeOf <Header>()];
            stream.Read(pHdrB, 0, Marshal.SizeOf <Header>());
            _pHdr = Marshal.ByteArrayToStructureLittleEndian <Header>(pHdrB);

            AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.magic = {0}", StringHandlers.CToString(_pHdr.magic));

            AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.filesystem = {0}",
                                       StringHandlers.CToString(_pHdr.filesystem));

            AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.version = {0}",
                                       StringHandlers.CToString(_pHdr.version));

            AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.blockSize = {0}", _pHdr.blockSize);
            AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.deviceSize = {0}", _pHdr.deviceSize);
            AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.totalBlocks = {0}", _pHdr.totalBlocks);
            AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.usedBlocks = {0}", _pHdr.usedBlocks);

            _byteMap = new byte[_pHdr.totalBlocks];
            AaruConsole.DebugWriteLine("PartClone plugin", "Reading bytemap {0} bytes", _byteMap.Length);
            stream.Read(_byteMap, 0, _byteMap.Length);

            byte[] bitmagic = new byte[8];
            stream.Read(bitmagic, 0, 8);

            AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.bitmagic = {0}", StringHandlers.CToString(bitmagic));

            if (!_biTmAgIc.SequenceEqual(bitmagic))
            {
                throw new ImageNotSupportedException("Could not find partclone BiTmAgIc, not continuing...");
            }

            _dataOff = stream.Position;
            AaruConsole.DebugWriteLine("PartClone plugin", "pHdr.dataOff = {0}", _dataOff);

            AaruConsole.DebugWriteLine("PartClone plugin", "Filling extents");
            DateTime start = DateTime.Now;

            _extents    = new ExtentsULong();
            _extentsOff = new Dictionary <ulong, ulong>();
            bool  current     = _byteMap[0] > 0;
            ulong blockOff    = 0;
            ulong extentStart = 0;

            for (ulong i = 1; i < _pHdr.totalBlocks; i++)
            {
                bool next = _byteMap[i] > 0;

                // Flux
                if (next != current)
                {
                    if (next)
                    {
                        extentStart = i;
                        _extentsOff.Add(i, ++blockOff);
                    }
                    else
                    {
                        _extents.Add(extentStart, i);
                        _extentsOff.TryGetValue(extentStart, out _);
                    }
                }

                if (next && current)
                {
                    blockOff++;
                }

                current = next;
            }

            DateTime end = DateTime.Now;

            AaruConsole.DebugWriteLine("PartClone plugin", "Took {0} seconds to fill extents",
                                       (end - start).TotalSeconds);

            _sectorCache = new Dictionary <ulong, byte[]>();

            _imageInfo.CreationTime         = imageFilter.GetCreationTime();
            _imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
            _imageInfo.MediaTitle           = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
            _imageInfo.Sectors      = _pHdr.totalBlocks;
            _imageInfo.SectorSize   = _pHdr.blockSize;
            _imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
            _imageInfo.MediaType    = MediaType.GENERIC_HDD;
            _imageInfo.ImageSize    = (ulong)(stream.Length - (4096 + 0x40 + (long)_pHdr.totalBlocks));
            _imageStream            = stream;

            return(true);
        }
Exemple #10
0
        Errno CacheFile(string path)
        {
            string[] pathElements = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            if (pathElements.Length != 1)
            {
                return(Errno.NotSupported);
            }

            string filename = pathElements[0].ToUpperInvariant();

            if (filename.Length > 30)
            {
                return(Errno.NameTooLong);
            }

            if (!catalogCache.TryGetValue(filename, out ushort ts))
            {
                return(Errno.NoSuchFile);
            }

            ulong        lba           = (ulong)(((ts & 0xFF00) >> 8) * sectorsPerTrack + (ts & 0xFF));
            MemoryStream fileMs        = new MemoryStream();
            MemoryStream tsListMs      = new MemoryStream();
            ushort       expectedBlock = 0;

            while (lba != 0)
            {
                usedSectors++;
                byte[] tsSectorB = device.ReadSector(lba);
                if (debug)
                {
                    tsListMs.Write(tsSectorB, 0, tsSectorB.Length);
                }

                // Read the track/sector list sector
                TrackSectorList tsSector = Marshal.ByteArrayToStructureLittleEndian <TrackSectorList>(tsSectorB);

                if (tsSector.sectorOffset > expectedBlock)
                {
                    byte[] hole = new byte[(tsSector.sectorOffset - expectedBlock) * vtoc.bytesPerSector];
                    fileMs.Write(hole, 0, hole.Length);
                    expectedBlock = tsSector.sectorOffset;
                }

                foreach (TrackSectorListEntry entry in tsSector.entries)
                {
                    track1UsedByFiles |= entry.track == 1;
                    track2UsedByFiles |= entry.track == 2;
                    usedSectors++;

                    ulong blockLba = (ulong)(entry.track * sectorsPerTrack + entry.sector);
                    if (blockLba == 0)
                    {
                        break;
                    }

                    byte[] fileBlock = device.ReadSector(blockLba);
                    fileMs.Write(fileBlock, 0, fileBlock.Length);
                    expectedBlock++;
                }

                lba = (ulong)(tsSector.nextListTrack * sectorsPerTrack + tsSector.nextListSector);
            }

            if (fileCache.ContainsKey(filename))
            {
                fileCache.Remove(filename);
            }
            if (extentCache.ContainsKey(filename))
            {
                extentCache.Remove(filename);
            }

            fileCache.Add(filename, fileMs.ToArray());
            extentCache.Add(filename, tsListMs.ToArray());

            return(Errno.NoError);
        }
Exemple #11
0
        Errno ReadCatalog()
        {
            var   catalogMs = new MemoryStream();
            ulong lba       = (ulong)((_vtoc.catalogTrack * _sectorsPerTrack) + _vtoc.catalogSector);

            _totalFileEntries = 0;
            _catalogCache     = new Dictionary <string, ushort>();
            _fileTypeCache    = new Dictionary <string, byte>();
            _fileSizeCache    = new Dictionary <string, int>();
            _lockedFiles      = new List <string>();

            if (lba == 0 ||
                lba > _device.Info.Sectors)
            {
                return(Errno.InvalidArgument);
            }

            while (lba != 0)
            {
                _usedSectors++;
                byte[] catSectorB = _device.ReadSector(lba);
                _totalFileEntries += 7;

                if (_debug)
                {
                    catalogMs.Write(catSectorB, 0, catSectorB.Length);
                }

                // Read the catalog sector
                CatalogSector catSector = Marshal.ByteArrayToStructureLittleEndian <CatalogSector>(catSectorB);

                foreach (FileEntry entry in catSector.entries.Where(entry => entry.extentTrack > 0))
                {
                    _track1UsedByFiles |= entry.extentTrack == 1;
                    _track2UsedByFiles |= entry.extentTrack == 2;

                    byte[] filenameB = new byte[30];
                    ushort ts        = (ushort)((entry.extentTrack << 8) | entry.extentSector);

                    // Apple DOS has high byte set over ASCII.
                    for (int i = 0; i < 30; i++)
                    {
                        filenameB[i] = (byte)(entry.filename[i] & 0x7F);
                    }

                    string filename = StringHandlers.SpacePaddedToString(filenameB, Encoding);

                    if (!_catalogCache.ContainsKey(filename))
                    {
                        _catalogCache.Add(filename, ts);
                    }

                    if (!_fileTypeCache.ContainsKey(filename))
                    {
                        _fileTypeCache.Add(filename, (byte)(entry.typeAndFlags & 0x7F));
                    }

                    if (!_fileSizeCache.ContainsKey(filename))
                    {
                        _fileSizeCache.Add(filename, entry.length * _vtoc.bytesPerSector);
                    }

                    if ((entry.typeAndFlags & 0x80) == 0x80 &&
                        !_lockedFiles.Contains(filename))
                    {
                        _lockedFiles.Add(filename);
                    }
                }

                lba = (ulong)((catSector.trackOfNext * _sectorsPerTrack) + catSector.sectorOfNext);

                if (lba > _device.Info.Sectors)
                {
                    break;
                }
            }

            if (_debug)
            {
                _catalogBlocks = catalogMs.ToArray();
            }

            return(Errno.NoError);
        }
Exemple #12
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            stream.Seek(0, SeekOrigin.Begin);

            if (stream.Length < Marshal.SizeOf <OobBlock>())
            {
                return(false);
            }

            byte[] hdr = new byte[Marshal.SizeOf <OobBlock>()];
            stream.Read(hdr, 0, Marshal.SizeOf <OobBlock>());

            OobBlock header = Marshal.ByteArrayToStructureLittleEndian <OobBlock>(hdr);

            stream.Seek(-Marshal.SizeOf <OobBlock>(), SeekOrigin.End);

            hdr = new byte[Marshal.SizeOf <OobBlock>()];
            stream.Read(hdr, 0, Marshal.SizeOf <OobBlock>());

            OobBlock footer = Marshal.ByteArrayToStructureLittleEndian <OobBlock>(hdr);

            if (header.blockId != BlockIds.Oob ||
                header.blockType != OobTypes.KFInfo ||
                footer.blockId != BlockIds.Oob ||
                footer.blockType != OobTypes.EOF ||
                footer.length != 0x0D0D)
            {
                return(false);
            }

            // TODO: This is supposing NoFilter, shouldn't
            tracks = new SortedDictionary <byte, IFilter>();
            byte step    = 1;
            byte heads   = 2;
            bool topHead = false;

            string basename = Path.Combine(imageFilter.GetParentFolder(),
                                           imageFilter.
                                           GetFilename().Substring(0, imageFilter.GetFilename().Length - 8));

            for (byte t = 0; t < 166; t += step)
            {
                int cylinder = t / heads;
                int head     = topHead ? 1 : t % heads;

                string trackfile = Directory.Exists(basename) ? Path.Combine(basename, $"{cylinder:D2}.{head:D1}.raw")
                                       : $"{basename}{cylinder:D2}.{head:D1}.raw";

                if (!File.Exists(trackfile))
                {
                    if (cylinder == 0)
                    {
                        if (head == 0)
                        {
                            AaruConsole.DebugWriteLine("KryoFlux plugin",
                                                       "Cannot find cyl 0 hd 0, supposing only top head was dumped");

                            topHead = true;
                            heads   = 1;

                            continue;
                        }

                        AaruConsole.DebugWriteLine("KryoFlux plugin",
                                                   "Cannot find cyl 0 hd 1, supposing only bottom head was dumped");

                        heads = 1;

                        continue;
                    }
                    else if (cylinder == 1)
                    {
                        AaruConsole.DebugWriteLine("KryoFlux plugin", "Cannot find cyl 1, supposing double stepping");
                        step = 2;

                        continue;
                    }
                    else
                    {
                        AaruConsole.DebugWriteLine("KryoFlux plugin", "Arrived end of disk at cylinder {0}", cylinder);

                        break;
                    }
                }

                var trackFilter = new ZZZNoFilter();
                trackFilter.Open(trackfile);

                if (!trackFilter.IsOpened())
                {
                    throw new IOException("Could not open KryoFlux track file.");
                }

                imageInfo.CreationTime         = DateTime.MaxValue;
                imageInfo.LastModificationTime = DateTime.MinValue;

                Stream trackStream = trackFilter.GetDataForkStream();

                while (trackStream.Position < trackStream.Length)
                {
                    byte blockId = (byte)trackStream.ReadByte();

                    switch (blockId)
                    {
                    case (byte)BlockIds.Oob:
                    {
                        trackStream.Position--;

                        byte[] oob = new byte[Marshal.SizeOf <OobBlock>()];
                        trackStream.Read(oob, 0, Marshal.SizeOf <OobBlock>());

                        OobBlock oobBlk = Marshal.ByteArrayToStructureLittleEndian <OobBlock>(oob);

                        if (oobBlk.blockType == OobTypes.EOF)
                        {
                            trackStream.Position = trackStream.Length;

                            break;
                        }

                        if (oobBlk.blockType != OobTypes.KFInfo)
                        {
                            trackStream.Position += oobBlk.length;

                            break;
                        }

                        byte[] kfinfo = new byte[oobBlk.length];
                        trackStream.Read(kfinfo, 0, oobBlk.length);
                        string kfinfoStr = StringHandlers.CToString(kfinfo);

                        string[] lines = kfinfoStr.Split(new[]
                            {
                                ','
                            }, StringSplitOptions.RemoveEmptyEntries);

                        DateTime blockDate = DateTime.Now;
                        DateTime blockTime = DateTime.Now;
                        bool     foundDate = false;

                        foreach (string[] kvp in lines.Select(line => line.Split('=')).Where(kvp => kvp.Length == 2))
                        {
                            kvp[0] = kvp[0].Trim();
                            kvp[1] = kvp[1].Trim();
                            AaruConsole.DebugWriteLine("KryoFlux plugin", "\"{0}\" = \"{1}\"", kvp[0], kvp[1]);

                            switch (kvp[0])
                            {
                            case hostDate:
                                if (DateTime.TryParseExact(kvp[1], "yyyy.MM.dd", CultureInfo.InvariantCulture,
                                                           DateTimeStyles.AssumeLocal, out blockDate))
                                {
                                    foundDate = true;
                                }

                                break;

                            case hostTime:
                                DateTime.TryParseExact(kvp[1], "HH:mm:ss", CultureInfo.InvariantCulture,
                                                       DateTimeStyles.AssumeLocal, out blockTime);

                                break;

                            case kfName:
                                imageInfo.Application = kvp[1];

                                break;

                            case kfVersion:
                                imageInfo.ApplicationVersion = kvp[1];

                                break;
                            }
                        }

                        if (foundDate)
                        {
                            var blockTimestamp = new DateTime(blockDate.Year, blockDate.Month, blockDate.Day,
                                                              blockTime.Hour, blockTime.Minute, blockTime.Second);

                            AaruConsole.DebugWriteLine("KryoFlux plugin", "Found timestamp: {0}", blockTimestamp);

                            if (blockTimestamp < Info.CreationTime)
                            {
                                imageInfo.CreationTime = blockTimestamp;
                            }

                            if (blockTimestamp > Info.LastModificationTime)
                            {
                                imageInfo.LastModificationTime = blockTimestamp;
                            }
                        }

                        break;
                    }

                    case (byte)BlockIds.Flux2:
                    case (byte)BlockIds.Flux2_1:
                    case (byte)BlockIds.Flux2_2:
                    case (byte)BlockIds.Flux2_3:
                    case (byte)BlockIds.Flux2_4:
                    case (byte)BlockIds.Flux2_5:
                    case (byte)BlockIds.Flux2_6:
                    case (byte)BlockIds.Flux2_7:
                    case (byte)BlockIds.Nop2:
                        trackStream.Position++;

                        continue;

                    case (byte)BlockIds.Nop3:
                    case (byte)BlockIds.Flux3:
                        trackStream.Position += 2;

                        continue;

                    default: continue;
                    }
                }

                tracks.Add(t, trackFilter);
            }

            imageInfo.Heads     = heads;
            imageInfo.Cylinders = (uint)(tracks.Count / heads);

            throw new NotImplementedException("Flux decoding is not yet implemented.");
        }
Exemple #13
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)
            {
                _blockSize = hsvd.Value.logical_block_size;

                pathTableData = ReadSingleExtent(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)
            {
                _blockSize = fsvd.Value.logical_block_size;

                pathTableData = ReadSingleExtent(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
            {
                _blockSize = pvd.Value.logical_block_size;

                pathTableData = ReadSingleExtent(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(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 + rootXattrLength, rootSize)
                                          : _highSierra
                                              ? DecodeHighSierraDirectory(rootLocation + rootXattrLength, rootSize)
                                              : DecodeIsoDirectory(rootLocation + rootXattrLength, rootSize);
            }

            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 + rootXattrLength, rootSize);

                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
                });
Exemple #14
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            if (stream.Length < Marshal.SizeOf <RayHdr>())
            {
                return(false);
            }

            byte[] buffer = new byte[Marshal.SizeOf <RayHdr>()];
            stream.Seek(0, SeekOrigin.Begin);
            stream.Read(buffer, 0, buffer.Length);

            RayHdr header = Marshal.ByteArrayToStructureLittleEndian <RayHdr>(buffer);

            string signature = StringHandlers.CToString(header.signature);

            var   sx = new Regex(REGEX_SIGNATURE);
            Match sm = sx.Match(signature);

            if (!sm.Success)
            {
                return(false);
            }

            _imageInfo.ApplicationVersion = $"{sm.Groups["major"].Value}.{sm.Groups["minor"].Value}";

            _imageInfo.Cylinders       = (uint)(header.cylinders + 1);
            _imageInfo.Heads           = (uint)(header.heads + 1);
            _imageInfo.SectorsPerTrack = header.sectorsPerTrack;
            _imageInfo.Sectors         = _imageInfo.Cylinders * _imageInfo.Heads * _imageInfo.SectorsPerTrack;
            _imageInfo.SectorSize      = 512;

            byte[] sectors = new byte[_imageInfo.SectorsPerTrack * _imageInfo.SectorSize];
            _disk = new MemoryStream();

            for (int i = 0; i < _imageInfo.SectorsPerTrack * _imageInfo.SectorSize; i++)
            {
                stream.Read(sectors, 0, sectors.Length);
                stream.Seek(_imageInfo.SectorsPerTrack, SeekOrigin.Current);
                _disk.Write(sectors, 0, sectors.Length);
            }

            _imageInfo.MediaType = Geometry.GetMediaType(((ushort)_imageInfo.Cylinders, (byte)_imageInfo.Heads,
                                                          (ushort)_imageInfo.SectorsPerTrack, 512, MediaEncoding.MFM,
                                                          false));

            switch (_imageInfo.MediaType)
            {
            case MediaType.NEC_525_HD
                when header.diskType == RayDiskTypes.Mf2hd || header.diskType == RayDiskTypes.Mf2ed:
                _imageInfo.MediaType = MediaType.NEC_35_HD_8;

                break;

            case MediaType.DOS_525_HD
                when header.diskType == RayDiskTypes.Mf2hd || header.diskType == RayDiskTypes.Mf2ed:
                _imageInfo.MediaType = MediaType.NEC_35_HD_15;

                break;

            case MediaType.RX50 when header.diskType == RayDiskTypes.Md2dd || header.diskType == RayDiskTypes.Md2hd:
                _imageInfo.MediaType = MediaType.ATARI_35_SS_DD;

                break;
            }

            _imageInfo.XmlMediaType = XmlMediaType.BlockMedia;

            return(true);
        }