Beispiel #1
0
        public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
                           Dictionary <string, string> options, string @namespace)
        {
            _device  = imagePlugin;
            Encoding = encoding ?? new Apple2();

            options ??= GetDefaultOptions();

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

            if (_device.Info.Sectors < 3)
            {
                return(Errno.InvalidArgument);
            }

            _multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1);

            // Blocks 0 and 1 are boot code
            _catalogBlocks = _device.ReadSectors(_multiplier * 2, _multiplier);

            // On Apple //, it's little endian
            // TODO: Fix
            //BigEndianBitConverter.IsLittleEndian =
            //    multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian;

            _mountedVolEntry.FirstBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x00);
            _mountedVolEntry.LastBlock  = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x02);
            _mountedVolEntry.EntryType  = (PascalFileKind)BigEndianBitConverter.ToInt16(_catalogBlocks, 0x04);
            _mountedVolEntry.VolumeName = new byte[8];
            Array.Copy(_catalogBlocks, 0x06, _mountedVolEntry.VolumeName, 0, 8);
            _mountedVolEntry.Blocks   = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x0E);
            _mountedVolEntry.Files    = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x10);
            _mountedVolEntry.Dummy    = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x12);
            _mountedVolEntry.LastBoot = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x14);
            _mountedVolEntry.Tail     = BigEndianBitConverter.ToInt32(_catalogBlocks, 0x16);

            if (_mountedVolEntry.FirstBlock != 0 ||
                _mountedVolEntry.LastBlock <= _mountedVolEntry.FirstBlock ||
                (ulong)_mountedVolEntry.LastBlock > (_device.Info.Sectors / _multiplier) - 2 ||
                (_mountedVolEntry.EntryType != PascalFileKind.Volume &&
                 _mountedVolEntry.EntryType != PascalFileKind.Secure) ||
                _mountedVolEntry.VolumeName[0] > 7 ||
                _mountedVolEntry.Blocks < 0 ||
                (ulong)_mountedVolEntry.Blocks != _device.Info.Sectors / _multiplier ||
                _mountedVolEntry.Files < 0)
            {
                return(Errno.InvalidArgument);
            }

            _catalogBlocks = _device.ReadSectors(_multiplier * 2,
                                                 (uint)(_mountedVolEntry.LastBlock - _mountedVolEntry.FirstBlock - 2) *
                                                 _multiplier);

            int offset = 26;

            _fileEntries = new List <PascalFileEntry>();

            while (offset + 26 < _catalogBlocks.Length)
            {
                var entry = new PascalFileEntry
                {
                    Filename         = new byte[16],
                    FirstBlock       = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x00),
                    LastBlock        = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x02),
                    EntryType        = (PascalFileKind)BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x04),
                    LastBytes        = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x16),
                    ModificationTime = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x18)
                };

                Array.Copy(_catalogBlocks, offset + 0x06, entry.Filename, 0, 16);

                if (entry.Filename[0] <= 15 &&
                    entry.Filename[0] > 0)
                {
                    _fileEntries.Add(entry);
                }

                offset += 26;
            }

            _bootBlocks = _device.ReadSectors(0, 2 * _multiplier);

            XmlFsType = new FileSystemType
            {
                Bootable       = !ArrayHelpers.ArrayIsNullOrEmpty(_bootBlocks),
                Clusters       = (ulong)_mountedVolEntry.Blocks,
                ClusterSize    = _device.Info.SectorSize,
                Files          = (ulong)_mountedVolEntry.Files,
                FilesSpecified = true,
                Type           = "UCSD Pascal",
                VolumeName     = StringHandlers.PascalToString(_mountedVolEntry.VolumeName, Encoding)
            };

            _mounted = true;

            return(Errno.NoError);
        }
Beispiel #2
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            if (partition.Length < 3)
            {
                return(false);
            }

            multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1);

            // Blocks 0 and 1 are boot code
            byte[] volBlock = imagePlugin.ReadSectors(multiplier * 2 + partition.Start, multiplier);

            PascalVolumeEntry volEntry = new PascalVolumeEntry();

            // On Apple II, it's little endian
            BigEndianBitConverter.IsLittleEndian =
                multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian;

            volEntry.FirstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00);
            volEntry.LastBlock  = BigEndianBitConverter.ToInt16(volBlock, 0x02);
            volEntry.EntryType  = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04);
            volEntry.VolumeName = new byte[8];
            Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8);
            volEntry.Blocks   = BigEndianBitConverter.ToInt16(volBlock, 0x0E);
            volEntry.Files    = BigEndianBitConverter.ToInt16(volBlock, 0x10);
            volEntry.Dummy    = BigEndianBitConverter.ToInt16(volBlock, 0x12);
            volEntry.LastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14);
            volEntry.Tail     = BigEndianBitConverter.ToInt32(volBlock, 0x16);

            DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.firstBlock = {0}", volEntry.FirstBlock);
            DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.lastBlock = {0}", volEntry.LastBlock);
            DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.entryType = {0}", volEntry.EntryType);
            DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.volumeName = {0}", volEntry.VolumeName);
            DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.blocks = {0}", volEntry.Blocks);
            DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.files = {0}", volEntry.Files);
            DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.dummy = {0}", volEntry.Dummy);
            DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.lastBoot = {0}", volEntry.LastBoot);
            DicConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.tail = {0}", volEntry.Tail);

            // First block is always 0 (even is it's sector 2)
            if (volEntry.FirstBlock != 0)
            {
                return(false);
            }

            // Last volume record block must be after first block, and before end of device
            if (volEntry.LastBlock <= volEntry.FirstBlock ||
                (ulong)volEntry.LastBlock > imagePlugin.Info.Sectors / multiplier - 2)
            {
                return(false);
            }

            // Volume record entry type must be volume or secure
            if (volEntry.EntryType != PascalFileKind.Volume && volEntry.EntryType != PascalFileKind.Secure)
            {
                return(false);
            }

            // Volume name is max 7 characters
            if (volEntry.VolumeName[0] > 7)
            {
                return(false);
            }

            // Volume blocks is equal to volume sectors
            if (volEntry.Blocks < 0 || (ulong)volEntry.Blocks != imagePlugin.Info.Sectors / multiplier)
            {
                return(false);
            }

            // There can be not less than zero files
            return(volEntry.Files >= 0);
        }
Beispiel #3
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = Encoding.UTF8;
            information = "";

            uint sbSize = (uint)(Marshal.SizeOf <VolumeHeader>() / imagePlugin.Info.SectorSize);

            if (Marshal.SizeOf <VolumeHeader>() % imagePlugin.Info.SectorSize != 0)
            {
                sbSize++;
            }

            if (partition.Start + sbSize >= partition.End)
            {
                return;
            }

            byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize);

            if (sector.Length < Marshal.SizeOf <VolumeHeader>())
            {
                return;
            }

            VolumeHeader vhdr = Marshal.ByteArrayToStructureLittleEndian <VolumeHeader>(sector);

            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.jump empty? = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(vhdr.jump));

            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.signature = {0}",
                                       StringHandlers.CToString(vhdr.signature));

            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.mustBeZero empty? = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(vhdr.mustBeZero));

            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.identifier = {0}",
                                       StringHandlers.CToString(BitConverter.GetBytes(vhdr.identifier)));

            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.length = {0}", vhdr.length);
            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.checksum = 0x{0:X4}", vhdr.checksum);
            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectors = {0}", vhdr.sectors);
            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.bytesPerSector = {0}", vhdr.bytesPerSector);

            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectorsPerCluster = {0}", vhdr.sectorsPerCluster);

            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown1 zero? = {0}", vhdr.unknown1 == 0);
            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown2 zero? = {0}", vhdr.unknown2 == 0);
            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown3 zero? = {0}", vhdr.unknown3 == 0);
            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown4 zero? = {0}", vhdr.unknown4 == 0);

            AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown5 empty? = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(vhdr.unknown5));

            if (vhdr.identifier != FSRS ||
                !ArrayHelpers.ArrayIsNullOrEmpty(vhdr.mustBeZero) ||
                !vhdr.signature.SequenceEqual(_signature))
            {
                return;
            }

            var sb = new StringBuilder();

            sb.AppendLine("Microsoft Resilient File System");
            sb.AppendFormat("Volume uses {0} bytes per sector", vhdr.bytesPerSector).AppendLine();

            sb.AppendFormat("Volume uses {0} sectors per cluster ({1} bytes)", vhdr.sectorsPerCluster,
                            vhdr.sectorsPerCluster * vhdr.bytesPerSector).AppendLine();

            sb.AppendFormat("Volume has {0} sectors ({1} bytes)", vhdr.sectors, vhdr.sectors * vhdr.bytesPerSector).
            AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Type        = "Resilient File System",
                ClusterSize = vhdr.bytesPerSector * vhdr.sectorsPerCluster,
                Clusters    = vhdr.sectors / vhdr.sectorsPerCluster
            };
        }
Beispiel #4
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            try
            {
                if (imagePlugin.Info.ReadableSectorTags?.Contains(SectorTagType.AppleSectorTag) != true)
                {
                    return(false);
                }

                // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors
                if (imagePlugin.Info.Sectors < 800)
                {
                    return(false);
                }

                int beforeMddf = -1;

                // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors
                for (int i = 0; i < 100; i++)
                {
                    DecodeTag(imagePlugin.ReadSectorTag((ulong)i, SectorTagType.AppleSectorTag),
                              out LisaTag.PriamTag searchTag);

                    AaruConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId);

                    if (beforeMddf == -1 &&
                        searchTag.FileId == FILEID_LOADER_SIGNED)
                    {
                        beforeMddf = i - 1;
                    }

                    if (searchTag.FileId != FILEID_MDDF)
                    {
                        continue;
                    }

                    byte[] sector = imagePlugin.ReadSector((ulong)i);

                    var infoMddf = new MDDF
                    {
                        mddf_block                   = BigEndianBitConverter.ToUInt32(sector, 0x6C),
                        volsize_minus_one            = BigEndianBitConverter.ToUInt32(sector, 0x70),
                        volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74),
                        vol_size  = BigEndianBitConverter.ToUInt32(sector, 0x78),
                        blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C),
                        datasize  = BigEndianBitConverter.ToUInt16(sector, 0x7E)
                    };

                    AaruConsole.DebugWriteLine("LisaFS plugin", "Current sector = {0}", i);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.mddf_block = {0}", infoMddf.mddf_block);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "Disk size = {0} sectors", imagePlugin.Info.Sectors);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size = {0} sectors", infoMddf.vol_size);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size - 1 = {0}", infoMddf.volsize_minus_one);

                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size - mddf.mddf_block -1 = {0}",
                                               infoMddf.volsize_minus_mddf_minus_one);

                    AaruConsole.DebugWriteLine("LisaFS plugin", "Disk sector = {0} bytes", imagePlugin.Info.SectorSize);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.blocksize = {0} bytes", infoMddf.blocksize);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.datasize = {0} bytes", infoMddf.datasize);

                    if (infoMddf.mddf_block != i - beforeMddf)
                    {
                        return(false);
                    }

                    if (infoMddf.vol_size > imagePlugin.Info.Sectors)
                    {
                        return(false);
                    }

                    if (infoMddf.vol_size - 1 != infoMddf.volsize_minus_one)
                    {
                        return(false);
                    }

                    if (infoMddf.vol_size - i - 1 != infoMddf.volsize_minus_mddf_minus_one - beforeMddf)
                    {
                        return(false);
                    }

                    if (infoMddf.datasize > infoMddf.blocksize)
                    {
                        return(false);
                    }

                    if (infoMddf.blocksize < imagePlugin.Info.SectorSize)
                    {
                        return(false);
                    }

                    return(infoMddf.datasize == imagePlugin.Info.SectorSize);
                }

                return(false);
            }
            catch (Exception ex)
            {
                AaruConsole.ErrorWriteLine("Exception {0}, {1}, {2}", ex.Message, ex.InnerException, ex.StackTrace);

                return(false);
            }
        }
Beispiel #5
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";

            var sb = new StringBuilder();

            if (imagePlugin.Info.SectorSize < 512)
            {
                return;
            }

            ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize;
            uint  sbOff       = SB_POS % imagePlugin.Info.SectorSize;

            if (sbSectorOff + partition.Start >= partition.End)
            {
                return;
            }

            byte[] sblock   = imagePlugin.ReadSector(sbSectorOff + partition.Start);
            byte[] sbSector = new byte[512];
            Array.Copy(sblock, sbOff, sbSector, 0, 512);

            var extSb = new SuperBlock
            {
                inodes        = BitConverter.ToUInt32(sbSector, 0x000),
                zones         = BitConverter.ToUInt32(sbSector, 0x004),
                firstfreeblk  = BitConverter.ToUInt32(sbSector, 0x008),
                freecountblk  = BitConverter.ToUInt32(sbSector, 0x00C),
                firstfreeind  = BitConverter.ToUInt32(sbSector, 0x010),
                freecountind  = BitConverter.ToUInt32(sbSector, 0x014),
                firstdatazone = BitConverter.ToUInt32(sbSector, 0x018),
                logzonesize   = BitConverter.ToUInt32(sbSector, 0x01C),
                maxsize       = BitConverter.ToUInt32(sbSector, 0x020)
            };

            sb.AppendLine("ext filesystem");
            sb.AppendFormat("{0} zones on volume", extSb.zones);
            sb.AppendFormat("{0} free blocks ({1} bytes)", extSb.freecountblk, extSb.freecountblk * 1024);

            sb.AppendFormat("{0} inodes on volume, {1} free ({2}%)", extSb.inodes, extSb.freecountind,
                            (extSb.freecountind * 100) / extSb.inodes);

            sb.AppendFormat("First free inode is {0}", extSb.firstfreeind);
            sb.AppendFormat("First free block is {0}", extSb.firstfreeblk);
            sb.AppendFormat("First data zone is {0}", extSb.firstdatazone);
            sb.AppendFormat("Log zone size: {0}", extSb.logzonesize);
            sb.AppendFormat("Max zone size: {0}", extSb.maxsize);

            XmlFsType = new FileSystemType
            {
                Type                  = "ext",
                FreeClusters          = extSb.freecountblk,
                FreeClustersSpecified = true,
                ClusterSize           = 1024,
                Clusters              = (((partition.End - partition.Start) + 1) * imagePlugin.Info.SectorSize) / 1024
            };

            information = sb.ToString();
        }
Beispiel #6
0
        public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset)
        {
            partitions = new List <Partition>();

            byte[] sector = imagePlugin.ReadSector(sectorOffset);

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

            var table = new AtariTable
            {
                boot = new byte[342], icdEntries = new AtariEntry[8], unused = new byte[12], entries = new AtariEntry[4]
            };

            Array.Copy(sector, 0, table.boot, 0, 342);

            for (int i = 0; i < 8; i++)
            {
                table.icdEntries[i].type   = BigEndianBitConverter.ToUInt32(sector, 342 + (i * 12) + 0);
                table.icdEntries[i].start  = BigEndianBitConverter.ToUInt32(sector, 342 + (i * 12) + 4);
                table.icdEntries[i].length = BigEndianBitConverter.ToUInt32(sector, 342 + (i * 12) + 8);
            }

            Array.Copy(sector, 438, table.unused, 0, 12);

            table.size = BigEndianBitConverter.ToUInt32(sector, 450);

            for (int i = 0; i < 4; i++)
            {
                table.entries[i].type   = BigEndianBitConverter.ToUInt32(sector, 454 + (i * 12) + 0);
                table.entries[i].start  = BigEndianBitConverter.ToUInt32(sector, 454 + (i * 12) + 4);
                table.entries[i].length = BigEndianBitConverter.ToUInt32(sector, 454 + (i * 12) + 8);
            }

            table.badStart  = BigEndianBitConverter.ToUInt32(sector, 502);
            table.badLength = BigEndianBitConverter.ToUInt32(sector, 506);
            table.checksum  = BigEndianBitConverter.ToUInt16(sector, 510);

            var sha1Ctx = new Sha1Context();

            sha1Ctx.Update(table.boot);
            AaruConsole.DebugWriteLine("Atari partition plugin", "Boot code SHA1: {0}", sha1Ctx.End());

            for (int i = 0; i < 8; i++)
            {
                AaruConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].flag = 0x{1:X2}", i,
                                           (table.icdEntries[i].type & 0xFF000000) >> 24);

                AaruConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].type = 0x{1:X6}", i,
                                           table.icdEntries[i].type & 0x00FFFFFF);

                AaruConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].start = {1}", i,
                                           table.icdEntries[i].start);

                AaruConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].length = {1}", i,
                                           table.icdEntries[i].length);
            }

            AaruConsole.DebugWriteLine("Atari partition plugin", "table.size = {0}", table.size);

            for (int i = 0; i < 4; i++)
            {
                AaruConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].flag = 0x{1:X2}", i,
                                           (table.entries[i].type & 0xFF000000) >> 24);

                AaruConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].type = 0x{1:X6}", i,
                                           table.entries[i].type & 0x00FFFFFF);

                AaruConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].start = {1}", i,
                                           table.entries[i].start);

                AaruConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].length = {1}", i,
                                           table.entries[i].length);
            }

            AaruConsole.DebugWriteLine("Atari partition plugin", "table.badStart = {0}", table.badStart);
            AaruConsole.DebugWriteLine("Atari partition plugin", "table.badLength = {0}", table.badLength);
            AaruConsole.DebugWriteLine("Atari partition plugin", "table.checksum = 0x{0:X4}", table.checksum);

            bool  validTable        = false;
            ulong partitionSequence = 0;

            for (int i = 0; i < 4; i++)
            {
                uint type = table.entries[i].type & 0x00FFFFFF;

                switch (type)
                {
                case TypeGEMDOS:
                case TypeBigGEMDOS:
                case TypeLinux:
                case TypeSwap:
                case TypeRAW:
                case TypeNetBSD:
                case TypeNetBSDSwap:
                case TypeSysV:
                case TypeMac:
                case TypeMinix:
                case TypeMinix2:
                    validTable = true;

                    if (table.entries[i].start <= imagePlugin.Info.Sectors)
                    {
                        if (table.entries[i].start + table.entries[i].length > imagePlugin.Info.Sectors)
                        {
                            AaruConsole.DebugWriteLine("Atari partition plugin",
                                                       "WARNING: End of partition goes beyond device size");
                        }

                        ulong sectorSize = imagePlugin.Info.SectorSize;

                        if (sectorSize == 2448 ||
                            sectorSize == 2352)
                        {
                            sectorSize = 2048;
                        }

                        byte[] partType = new byte[3];
                        partType[0] = (byte)((type & 0xFF0000) >> 16);
                        partType[1] = (byte)((type & 0x00FF00) >> 8);
                        partType[2] = (byte)(type & 0x0000FF);

                        var part = new Partition
                        {
                            Size     = table.entries[i].length * sectorSize, Length = table.entries[i].length,
                            Sequence = partitionSequence, Name = "",
                            Offset   = table.entries[i].start * sectorSize,
                            Start    = table.entries[i].start,
                            Type     = Encoding.ASCII.GetString(partType), Scheme = Name
                        };

                        switch (type)
                        {
                        case TypeGEMDOS:
                            part.Description = "Atari GEMDOS partition";

                            break;

                        case TypeBigGEMDOS:
                            part.Description = "Atari GEMDOS partition bigger than 32 MiB";

                            break;

                        case TypeLinux:
                            part.Description = "Linux partition";

                            break;

                        case TypeSwap:
                            part.Description = "Swap partition";

                            break;

                        case TypeRAW:
                            part.Description = "RAW partition";

                            break;

                        case TypeNetBSD:
                            part.Description = "NetBSD partition";

                            break;

                        case TypeNetBSDSwap:
                            part.Description = "NetBSD swap partition";

                            break;

                        case TypeSysV:
                            part.Description = "Atari UNIX partition";

                            break;

                        case TypeMac:
                            part.Description = "Macintosh partition";

                            break;

                        case TypeMinix:
                        case TypeMinix2:
                            part.Description = "MINIX partition";

                            break;

                        default:
                            part.Description = "Unknown partition type";

                            break;
                        }

                        partitions.Add(part);
                        partitionSequence++;
                    }

                    break;

                case TypeExtended:
                    byte[] extendedSector = imagePlugin.ReadSector(table.entries[i].start);
                    var    extendedTable  = new AtariTable();
                    extendedTable.entries = new AtariEntry[4];

                    for (int j = 0; j < 4; j++)
                    {
                        extendedTable.entries[j].type =
                            BigEndianBitConverter.ToUInt32(extendedSector, 454 + (j * 12) + 0);

                        extendedTable.entries[j].start =
                            BigEndianBitConverter.ToUInt32(extendedSector, 454 + (j * 12) + 4);

                        extendedTable.entries[j].length =
                            BigEndianBitConverter.ToUInt32(extendedSector, 454 + (j * 12) + 8);
                    }

                    for (int j = 0; j < 4; j++)
                    {
                        uint extendedType = extendedTable.entries[j].type & 0x00FFFFFF;

                        if (extendedType != TypeGEMDOS &&
                            extendedType != TypeBigGEMDOS &&
                            extendedType != TypeLinux &&
                            extendedType != TypeSwap &&
                            extendedType != TypeRAW &&
                            extendedType != TypeNetBSD &&
                            extendedType != TypeNetBSDSwap &&
                            extendedType != TypeSysV &&
                            extendedType != TypeMac &&
                            extendedType != TypeMinix &&
                            extendedType != TypeMinix2)
                        {
                            continue;
                        }

                        validTable = true;

                        if (extendedTable.entries[j].start > imagePlugin.Info.Sectors)
                        {
                            continue;
                        }

                        if (extendedTable.entries[j].start + extendedTable.entries[j].length >
                            imagePlugin.Info.Sectors)
                        {
                            AaruConsole.DebugWriteLine("Atari partition plugin",
                                                       "WARNING: End of partition goes beyond device size");
                        }

                        ulong sectorSize = imagePlugin.Info.SectorSize;

                        if (sectorSize == 2448 ||
                            sectorSize == 2352)
                        {
                            sectorSize = 2048;
                        }

                        byte[] partType = new byte[3];
                        partType[0] = (byte)((extendedType & 0xFF0000) >> 16);
                        partType[1] = (byte)((extendedType & 0x00FF00) >> 8);
                        partType[2] = (byte)(extendedType & 0x0000FF);

                        var part = new Partition
                        {
                            Size   = extendedTable.entries[j].length * sectorSize,
                            Length = extendedTable.entries[j].length, Sequence = partitionSequence, Name = "",
                            Offset = extendedTable.entries[j].start * sectorSize,
                            Start  = extendedTable.entries[j].start, Type = Encoding.ASCII.GetString(partType),
                            Scheme = Name
                        };

                        switch (extendedType)
                        {
                        case TypeGEMDOS:
                            part.Description = "Atari GEMDOS partition";

                            break;

                        case TypeBigGEMDOS:
                            part.Description = "Atari GEMDOS partition bigger than 32 MiB";

                            break;

                        case TypeLinux:
                            part.Description = "Linux partition";

                            break;

                        case TypeSwap:
                            part.Description = "Swap partition";

                            break;

                        case TypeRAW:
                            part.Description = "RAW partition";

                            break;

                        case TypeNetBSD:
                            part.Description = "NetBSD partition";

                            break;

                        case TypeNetBSDSwap:
                            part.Description = "NetBSD swap partition";

                            break;

                        case TypeSysV:
                            part.Description = "Atari UNIX partition";

                            break;

                        case TypeMac:
                            part.Description = "Macintosh partition";

                            break;

                        case TypeMinix:
                        case TypeMinix2:
                            part.Description = "MINIX partition";

                            break;

                        default:
                            part.Description = "Unknown partition type";

                            break;
                        }

                        partitions.Add(part);
                        partitionSequence++;
                    }

                    break;
                }
            }

            if (!validTable)
            {
                return(partitions.Count > 0);
            }

            for (int i = 0; i < 8; i++)
            {
                uint type = table.icdEntries[i].type & 0x00FFFFFF;

                if (type != TypeGEMDOS &&
                    type != TypeBigGEMDOS &&
                    type != TypeLinux &&
                    type != TypeSwap &&
                    type != TypeRAW &&
                    type != TypeNetBSD &&
                    type != TypeNetBSDSwap &&
                    type != TypeSysV &&
                    type != TypeMac &&
                    type != TypeMinix &&
                    type != TypeMinix2)
                {
                    continue;
                }

                if (table.icdEntries[i].start > imagePlugin.Info.Sectors)
                {
                    continue;
                }

                if (table.icdEntries[i].start + table.icdEntries[i].length > imagePlugin.Info.Sectors)
                {
                    AaruConsole.DebugWriteLine("Atari partition plugin",
                                               "WARNING: End of partition goes beyond device size");
                }

                ulong sectorSize = imagePlugin.Info.SectorSize;

                if (sectorSize == 2448 ||
                    sectorSize == 2352)
                {
                    sectorSize = 2048;
                }

                byte[] partType = new byte[3];
                partType[0] = (byte)((type & 0xFF0000) >> 16);
                partType[1] = (byte)((type & 0x00FF00) >> 8);
                partType[2] = (byte)(type & 0x0000FF);

                var part = new Partition
                {
                    Size     = table.icdEntries[i].length * sectorSize, Length = table.icdEntries[i].length,
                    Sequence = partitionSequence, Name = "",
                    Offset   = table.icdEntries[i].start * sectorSize,
                    Start    = table.icdEntries[i].start, Type = Encoding.ASCII.GetString(partType),
                    Scheme   = Name
                };

                switch (type)
                {
                case TypeGEMDOS:
                    part.Description = "Atari GEMDOS partition";

                    break;

                case TypeBigGEMDOS:
                    part.Description = "Atari GEMDOS partition bigger than 32 MiB";

                    break;

                case TypeLinux:
                    part.Description = "Linux partition";

                    break;

                case TypeSwap:
                    part.Description = "Swap partition";

                    break;

                case TypeRAW:
                    part.Description = "RAW partition";

                    break;

                case TypeNetBSD:
                    part.Description = "NetBSD partition";

                    break;

                case TypeNetBSDSwap:
                    part.Description = "NetBSD swap partition";

                    break;

                case TypeSysV:
                    part.Description = "Atari UNIX partition";

                    break;

                case TypeMac:
                    part.Description = "Macintosh partition";

                    break;

                case TypeMinix:
                case TypeMinix2:
                    part.Description = "MINIX partition";

                    break;

                default:
                    part.Description = "Unknown partition type";

                    break;
                }

                partitions.Add(part);
                partitionSequence++;
            }

            return(partitions.Count > 0);
        }
Beispiel #7
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("macintosh");
            information = "";

            StringBuilder sb = new StringBuilder();

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

            bool apmFromHddOnCd = false;

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

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

                    bbSector  = new byte[1024];
                    mdbSector = new byte[512];
                    if (offset >= 0x400)
                    {
                        Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024);
                    }
                    Array.Copy(tmpSector, offset, mdbSector, 0, 512);
                    apmFromHddOnCd = true;
                    break;
                }

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

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

            HfsMasterDirectoryBlock mdb = Marshal.ByteArrayToStructureBigEndian <HfsMasterDirectoryBlock>(mdbSector);
            HfsBootBlock            bb  = Marshal.ByteArrayToStructureBigEndian <HfsBootBlock>(bbSector);

            sb.AppendLine("Apple Hierarchical File System");
            sb.AppendLine();
            if (apmFromHddOnCd)
            {
                sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine();
            }
            sb.AppendLine("Master Directory Block:");
            sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine();
            sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(mdb.drLsMod)).AppendLine();
            if (mdb.drVolBkUp > 0)
            {
                sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drVolBkUp)).AppendLine();
                sb.AppendFormat("Backup sequence number: {0}", mdb.drVSeqNum).AppendLine();
            }
            else
            {
                sb.AppendLine("Volume has never been backed up");
            }

            if ((mdb.drAtrb & 0x80) == 0x80)
            {
                sb.AppendLine("Volume is locked by hardware.");
            }
            sb.AppendLine((mdb.drAtrb & 0x100) == 0x100 ? "Volume was unmonted." : "Volume is mounted.");
            if ((mdb.drAtrb & 0x200) == 0x200)
            {
                sb.AppendLine("Volume has spared bad blocks.");
            }
            if ((mdb.drAtrb & 0x400) == 0x400)
            {
                sb.AppendLine("Volume does not need cache.");
            }
            if ((mdb.drAtrb & 0x800) == 0x800)
            {
                sb.AppendLine("Boot volume is inconsistent.");
            }
            if ((mdb.drAtrb & 0x1000) == 0x1000)
            {
                sb.AppendLine("There are reused CNIDs.");
            }
            if ((mdb.drAtrb & 0x2000) == 0x2000)
            {
                sb.AppendLine("Volume is journaled.");
            }
            if ((mdb.drAtrb & 0x4000) == 0x4000)
            {
                sb.AppendLine("Volume is seriously inconsistent.");
            }
            if ((mdb.drAtrb & 0x8000) == 0x8000)
            {
                sb.AppendLine("Volume is locked by software.");
            }

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

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

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

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

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

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

            if (bb.signature == HFSBB_MAGIC)
            {
                sb.AppendLine("Volume is bootable.");
                sb.AppendLine();
                sb.AppendLine("Boot Block:");
                if ((bb.boot_flags & 0x40) == 0x40)
                {
                    sb.AppendLine("Boot block should be executed.");
                }
                if ((bb.boot_flags & 0x80) == 0x80)
                {
                    sb.AppendLine("Boot block is in new unknown format.");
                }
                else
                {
                    if (bb.boot_flags > 0)
                    {
                        sb.AppendLine("Allocate secondary sound buffer at boot.");
                    }
                    else if (bb.boot_flags < 0)
                    {
                        sb.AppendLine("Allocate secondary sound and video buffers at boot.");
                    }

                    sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(bb.system_name, Encoding))
                    .AppendLine();
                    sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(bb.finder_name, Encoding))
                    .AppendLine();
                    sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(bb.debug_name, Encoding))
                    .AppendLine();
                    sb.AppendFormat("Disassembler filename: {0}",
                                    StringHandlers.PascalToString(bb.disasm_name, Encoding)).AppendLine();
                    sb.AppendFormat("Startup screen filename: {0}",
                                    StringHandlers.PascalToString(bb.stupscr_name, Encoding)).AppendLine();
                    sb.AppendFormat("First program to execute at boot: {0}",
                                    StringHandlers.PascalToString(bb.bootup_name, Encoding)).AppendLine();
                    sb.AppendFormat("Clipboard filename: {0}", StringHandlers.PascalToString(bb.clipbrd_name, Encoding))
                    .AppendLine();
                    sb.AppendFormat("Maximum opened files: {0}", bb.max_files * 4).AppendLine();
                    sb.AppendFormat("Event queue size: {0}", bb.queue_size).AppendLine();
                    sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.heap_128k).AppendLine();
                    sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.heap_256k).AppendLine();
                    sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.heap_512k).AppendLine();
                }
            }
            else if (mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || mdb.drFndrInfo5 != 0)
            {
                sb.AppendLine("Volume is bootable.");
            }
            else
            {
                sb.AppendLine("Volume is not bootable.");
            }

            information = sb.ToString();

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

            XmlFsType.Bootable = bb.signature == HFSBB_MAGIC || mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 ||
                                 mdb.drFndrInfo5 != 0;
            XmlFsType.Clusters    = mdb.drNmAlBlks;
            XmlFsType.ClusterSize = mdb.drAlBlkSiz;
            if (mdb.drCrDate > 0)
            {
                XmlFsType.CreationDate          = DateHandlers.MacToDateTime(mdb.drCrDate);
                XmlFsType.CreationDateSpecified = true;
            }

            XmlFsType.Dirty                 = (mdb.drAtrb & 0x100) != 0x100;
            XmlFsType.Files                 = mdb.drFilCnt;
            XmlFsType.FilesSpecified        = true;
            XmlFsType.FreeClusters          = mdb.drFreeBks;
            XmlFsType.FreeClustersSpecified = true;
            if (mdb.drLsMod > 0)
            {
                XmlFsType.ModificationDate          = DateHandlers.MacToDateTime(mdb.drLsMod);
                XmlFsType.ModificationDateSpecified = true;
            }

            XmlFsType.Type       = "HFS";
            XmlFsType.VolumeName = StringHandlers.PascalToString(mdb.drVN, Encoding);
            if (mdb.drFndrInfo6 != 0 && mdb.drFndrInfo7 != 0)
            {
                XmlFsType.VolumeSerial = $"{mdb.drFndrInfo6:X8}{mdb.drFndrInfo7:X8}";
            }
        }
Beispiel #8
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";

            StringBuilder sb = new StringBuilder();

            HammerSuperBlock hammerSb;

            uint run = HAMMER_VOLHDR_SIZE / imagePlugin.Info.SectorSize;

            if (HAMMER_VOLHDR_SIZE % imagePlugin.Info.SectorSize > 0)
            {
                run++;
            }

            byte[] sbSector = imagePlugin.ReadSectors(partition.Start, run);

            ulong magic = BitConverter.ToUInt64(sbSector, 0);

            if (magic == HAMMER_FSBUF_VOLUME)
            {
                hammerSb = Marshal.ByteArrayToStructureLittleEndian <HammerSuperBlock>(sbSector);
            }
            else
            {
                hammerSb = Marshal.ByteArrayToStructureBigEndian <HammerSuperBlock>(sbSector);
            }

            sb.AppendLine("HAMMER filesystem");

            sb.AppendFormat("Volume version: {0}", hammerSb.vol_version).AppendLine();
            sb.AppendFormat("Volume {0} of {1} on this filesystem", hammerSb.vol_no + 1, hammerSb.vol_count)
            .AppendLine();
            sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(hammerSb.vol_label, Encoding)).AppendLine();
            sb.AppendFormat("Volume serial: {0}", hammerSb.vol_fsid).AppendLine();
            sb.AppendFormat("Filesystem type: {0}", hammerSb.vol_fstype).AppendLine();
            sb.AppendFormat("Boot area starts at {0}", hammerSb.vol_bot_beg).AppendLine();
            sb.AppendFormat("Memory log starts at {0}", hammerSb.vol_mem_beg).AppendLine();
            sb.AppendFormat("First volume buffer starts at {0}", hammerSb.vol_buf_beg).AppendLine();
            sb.AppendFormat("Volume ends at {0}", hammerSb.vol_buf_end).AppendLine();

            XmlFsType = new FileSystemType
            {
                Clusters     = partition.Size / HAMMER_BIGBLOCK_SIZE,
                ClusterSize  = HAMMER_BIGBLOCK_SIZE,
                Dirty        = false,
                Type         = "HAMMER",
                VolumeName   = StringHandlers.CToString(hammerSb.vol_label, Encoding),
                VolumeSerial = hammerSb.vol_fsid.ToString()
            };

            if (hammerSb.vol_no == hammerSb.vol_rootvol)
            {
                sb.AppendFormat("Filesystem contains {0} \"big-blocks\" ({1} bytes)", hammerSb.vol0_stat_bigblocks,
                                hammerSb.vol0_stat_bigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine();
                sb.AppendFormat("Filesystem has {0} \"big-blocks\" free ({1} bytes)", hammerSb.vol0_stat_freebigblocks,
                                hammerSb.vol0_stat_freebigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine();
                sb.AppendFormat("Filesystem has {0} inode used", hammerSb.vol0_stat_inodes).AppendLine();

                XmlFsType.Clusters              = (ulong)hammerSb.vol0_stat_bigblocks;
                XmlFsType.FreeClusters          = (ulong)hammerSb.vol0_stat_freebigblocks;
                XmlFsType.FreeClustersSpecified = true;
                XmlFsType.Files          = (ulong)hammerSb.vol0_stat_inodes;
                XmlFsType.FilesSpecified = true;
            }
            // 0 ?
            //sb.AppendFormat("Volume header CRC: 0x{0:X8}", afs_sb.vol_crc).AppendLine();

            information = sb.ToString();
        }
Beispiel #9
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding = encoding ?? Encoding.GetEncoding("shift_jis");
            var sbInformation = new StringBuilder();

            information = "";
            XmlFsType   = new FileSystemType();

            var fields = new NintendoFields();

            byte[] header = imagePlugin.ReadSectors(0, 0x50000 / imagePlugin.Info.SectorSize);

            bool wii = false;

            uint magicGc  = BigEndianBitConverter.ToUInt32(header, 0x1C);
            uint magicWii = BigEndianBitConverter.ToUInt32(header, 0x18);

            if (magicWii == 0x5D1C9EA3)
            {
                wii = true;
            }
            else if (magicGc != 0xC2339F3D)
            {
                return;
            }

            fields.DiscType         = Encoding.ASCII.GetString(header, 0, 1);
            fields.GameCode         = Encoding.ASCII.GetString(header, 1, 2);
            fields.RegionCode       = Encoding.ASCII.GetString(header, 3, 1);
            fields.PublisherCode    = Encoding.ASCII.GetString(header, 4, 2);
            fields.DiscId           = Encoding.ASCII.GetString(header, 0, 6);
            fields.DiscNumber       = header[6];
            fields.DiscVersion      = header[7];
            fields.Streaming       |= header[8] > 0;
            fields.StreamBufferSize = header[9];
            byte[] temp = new byte[64];
            Array.Copy(header, 0x20, temp, 0, 64);
            fields.Title = StringHandlers.CToString(temp, Encoding);

            if (!wii)
            {
                fields.DebugOff  = BigEndianBitConverter.ToUInt32(header, 0x0400);
                fields.DebugAddr = BigEndianBitConverter.ToUInt32(header, 0x0404);
                fields.DolOff    = BigEndianBitConverter.ToUInt32(header, 0x0420);
                fields.FstOff    = BigEndianBitConverter.ToUInt32(header, 0x0424);
                fields.FstSize   = BigEndianBitConverter.ToUInt32(header, 0x0428);
                fields.FstMax    = BigEndianBitConverter.ToUInt32(header, 0x042C);
            }

            if (wii)
            {
                uint offset1 = BigEndianBitConverter.ToUInt32(header, 0x40004) << 2;
                uint offset2 = BigEndianBitConverter.ToUInt32(header, 0x4000C) << 2;
                uint offset3 = BigEndianBitConverter.ToUInt32(header, 0x40014) << 2;
                uint offset4 = BigEndianBitConverter.ToUInt32(header, 0x4001C) << 2;

                fields.FirstPartitions  = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40000)];
                fields.SecondPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40008)];
                fields.ThirdPartitions  = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40010)];
                fields.FourthPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40018)];

                for (int i = 0; i < fields.FirstPartitions.Length; i++)
                {
                    if (offset1 + (i * 8) + 8 < 0x50000)
                    {
                        fields.FirstPartitions[i].Offset =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 0)) << 2;

                        fields.FirstPartitions[i].Type =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 4));
                    }
                }

                for (int i = 0; i < fields.SecondPartitions.Length; i++)
                {
                    if (offset1 + (i * 8) + 8 < 0x50000)
                    {
                        fields.FirstPartitions[i].Offset =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 0)) << 2;

                        fields.FirstPartitions[i].Type =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 4));
                    }
                }

                for (int i = 0; i < fields.ThirdPartitions.Length; i++)
                {
                    if (offset1 + (i * 8) + 8 < 0x50000)
                    {
                        fields.FirstPartitions[i].Offset =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 0)) << 2;

                        fields.FirstPartitions[i].Type =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 4));
                    }
                }

                for (int i = 0; i < fields.FourthPartitions.Length; i++)
                {
                    if (offset1 + (i * 8) + 8 < 0x50000)
                    {
                        fields.FirstPartitions[i].Offset =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 0)) << 2;

                        fields.FirstPartitions[i].Type =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 4));
                    }
                }

                fields.Region       = header[0x4E000];
                fields.JapanAge     = header[0x4E010];
                fields.UsaAge       = header[0x4E011];
                fields.GermanAge    = header[0x4E013];
                fields.PegiAge      = header[0x4E014];
                fields.FinlandAge   = header[0x4E015];
                fields.PortugalAge  = header[0x4E016];
                fields.UkAge        = header[0x4E017];
                fields.AustraliaAge = header[0x4E018];
                fields.KoreaAge     = header[0x4E019];
            }
            else
            {
                fields.FirstPartitions  = new NintendoPartition[0];
                fields.SecondPartitions = new NintendoPartition[0];
                fields.ThirdPartitions  = new NintendoPartition[0];
                fields.FourthPartitions = new NintendoPartition[0];
            }

            AaruConsole.DebugWriteLine("Nintendo plugin", "discType = {0}", fields.DiscType);
            AaruConsole.DebugWriteLine("Nintendo plugin", "gameCode = {0}", fields.GameCode);
            AaruConsole.DebugWriteLine("Nintendo plugin", "regionCode = {0}", fields.RegionCode);
            AaruConsole.DebugWriteLine("Nintendo plugin", "publisherCode = {0}", fields.PublisherCode);
            AaruConsole.DebugWriteLine("Nintendo plugin", "discID = {0}", fields.DiscId);
            AaruConsole.DebugWriteLine("Nintendo plugin", "discNumber = {0}", fields.DiscNumber);
            AaruConsole.DebugWriteLine("Nintendo plugin", "discVersion = {0}", fields.DiscVersion);
            AaruConsole.DebugWriteLine("Nintendo plugin", "streaming = {0}", fields.Streaming);
            AaruConsole.DebugWriteLine("Nintendo plugin", "streamBufferSize = {0}", fields.StreamBufferSize);
            AaruConsole.DebugWriteLine("Nintendo plugin", "title = \"{0}\"", fields.Title);
            AaruConsole.DebugWriteLine("Nintendo plugin", "debugOff = 0x{0:X8}", fields.DebugOff);
            AaruConsole.DebugWriteLine("Nintendo plugin", "debugAddr = 0x{0:X8}", fields.DebugAddr);
            AaruConsole.DebugWriteLine("Nintendo plugin", "dolOff = 0x{0:X8}", fields.DolOff);
            AaruConsole.DebugWriteLine("Nintendo plugin", "fstOff = 0x{0:X8}", fields.FstOff);
            AaruConsole.DebugWriteLine("Nintendo plugin", "fstSize = {0}", fields.FstSize);
            AaruConsole.DebugWriteLine("Nintendo plugin", "fstMax = {0}", fields.FstMax);

            for (int i = 0; i < fields.FirstPartitions.Length; i++)
            {
                AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].offset = {0}",
                                           fields.FirstPartitions[i].Offset, i);

                AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].type = {0}",
                                           fields.FirstPartitions[i].Type, i);
            }

            for (int i = 0; i < fields.SecondPartitions.Length; i++)
            {
                AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].offset = {0}",
                                           fields.SecondPartitions[i].Offset, i);

                AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].type = {0}",
                                           fields.SecondPartitions[i].Type, i);
            }

            for (int i = 0; i < fields.ThirdPartitions.Length; i++)
            {
                AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].offset = {0}",
                                           fields.ThirdPartitions[i].Offset, i);

                AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].type = {0}",
                                           fields.ThirdPartitions[i].Type, i);
            }

            for (int i = 0; i < fields.FourthPartitions.Length; i++)
            {
                AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].offset = {0}",
                                           fields.FourthPartitions[i].Offset, i);

                AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].type = {0}",
                                           fields.FourthPartitions[i].Type, i);
            }

            AaruConsole.DebugWriteLine("Nintendo plugin", "region = {0}", fields.Region);
            AaruConsole.DebugWriteLine("Nintendo plugin", "japanAge = {0}", fields.JapanAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "usaAge = {0}", fields.UsaAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "germanAge = {0}", fields.GermanAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "pegiAge = {0}", fields.PegiAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "finlandAge = {0}", fields.FinlandAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "portugalAge = {0}", fields.PortugalAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "ukAge = {0}", fields.UkAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "australiaAge = {0}", fields.AustraliaAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "koreaAge = {0}", fields.KoreaAge);

            sbInformation.AppendLine("Nintendo optical filesystem");
            sbInformation.AppendLine(wii ? "Nintendo Wii Optical Disc" : "Nintendo GameCube Optical Disc");
            sbInformation.AppendFormat("Disc ID is {0}", fields.DiscId).AppendLine();
            sbInformation.AppendFormat("Disc is a {0} disc", DiscTypeToString(fields.DiscType)).AppendLine();
            sbInformation.AppendFormat("Disc region is {0}", RegionCodeToString(fields.RegionCode)).AppendLine();
            sbInformation.AppendFormat("Published by {0}", PublisherCodeToString(fields.PublisherCode)).AppendLine();

            if (fields.DiscNumber > 0)
            {
                sbInformation.AppendFormat("Disc number {0} of a multi-disc set", fields.DiscNumber + 1).AppendLine();
            }

            if (fields.Streaming)
            {
                sbInformation.AppendLine("Disc is prepared for audio streaming");
            }

            if (fields.StreamBufferSize > 0)
            {
                sbInformation.AppendFormat("Audio streaming buffer size is {0} bytes", fields.StreamBufferSize).
                AppendLine();
            }

            sbInformation.AppendFormat("Title: {0}", fields.Title).AppendLine();

            if (wii)
            {
                for (int i = 0; i < fields.FirstPartitions.Length; i++)
                {
                    sbInformation.AppendFormat("First {0} partition starts at sector {1}",
                                               PartitionTypeToString(fields.FirstPartitions[i].Type),
                                               fields.FirstPartitions[i].Offset / 2048).AppendLine();
                }

                for (int i = 0; i < fields.SecondPartitions.Length; i++)
                {
                    sbInformation.AppendFormat("Second {0} partition starts at sector {1}",
                                               PartitionTypeToString(fields.SecondPartitions[i].Type),
                                               fields.SecondPartitions[i].Offset / 2048).AppendLine();
                }

                for (int i = 0; i < fields.ThirdPartitions.Length; i++)
                {
                    sbInformation.AppendFormat("Third {0} partition starts at sector {1}",
                                               PartitionTypeToString(fields.ThirdPartitions[i].Type),
                                               fields.ThirdPartitions[i].Offset / 2048).AppendLine();
                }

                for (int i = 0; i < fields.FourthPartitions.Length; i++)
                {
                    sbInformation.AppendFormat("Fourth {0} partition starts at sector {1}",
                                               PartitionTypeToString(fields.FourthPartitions[i].Type),
                                               fields.FourthPartitions[i].Offset / 2048).AppendLine();
                }

                //                sbInformation.AppendFormat("Region byte is {0}", fields.region).AppendLine();
                if ((fields.JapanAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("Japan age rating is {0}", fields.JapanAge).AppendLine();
                }

                if ((fields.UsaAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("ESRB age rating is {0}", fields.UsaAge).AppendLine();
                }

                if ((fields.GermanAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("German age rating is {0}", fields.GermanAge).AppendLine();
                }

                if ((fields.PegiAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("PEGI age rating is {0}", fields.PegiAge).AppendLine();
                }

                if ((fields.FinlandAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("Finland age rating is {0}", fields.FinlandAge).AppendLine();
                }

                if ((fields.PortugalAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("Portugal age rating is {0}", fields.PortugalAge).AppendLine();
                }

                if ((fields.UkAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("UK age rating is {0}", fields.UkAge).AppendLine();
                }

                if ((fields.AustraliaAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("Australia age rating is {0}", fields.AustraliaAge).AppendLine();
                }

                if ((fields.KoreaAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("Korea age rating is {0}", fields.KoreaAge).AppendLine();
                }
            }
            else
            {
                sbInformation.AppendFormat("FST starts at {0} and has {1} bytes", fields.FstOff, fields.FstSize).
                AppendLine();
            }

            information            = sbInformation.ToString();
            XmlFsType.Bootable     = true;
            XmlFsType.Clusters     = (imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize) / 2048;
            XmlFsType.ClusterSize  = 2048;
            XmlFsType.Type         = wii ? "Nintendo Wii filesystem" : "Nintendo Gamecube filesystem";
            XmlFsType.VolumeName   = fields.Title;
            XmlFsType.VolumeSerial = fields.DiscId;
        }
Beispiel #10
0
        public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
                           Dictionary <string, string> options)
        {
            device         = imagePlugin;
            this.partition = partition;
            Encoding       = encoding ?? Encoding.GetEncoding("IBM437");

            // As the identification is so complex, just call Identify() and relay on its findings
            if (!Identify(device, partition) || !cpmFound || workingDefinition == null || dpb == null)
            {
                return(Errno.InvalidArgument);
            }

            // Build the software interleaving sector mask
            if (workingDefinition.sides == 1)
            {
                sectorMask = new int[workingDefinition.side1.sectorIds.Length];
                for (int m = 0; m < sectorMask.Length; m++)
                {
                    sectorMask[m] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0];
                }
            }
            else
            {
                // Head changes after every track
                if (string.Compare(workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0)
                {
                    sectorMask = new int[workingDefinition.side1.sectorIds.Length +
                                         workingDefinition.side2.sectorIds.Length];
                    for (int m = 0; m < workingDefinition.side1.sectorIds.Length; m++)
                    {
                        sectorMask[m] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0];
                    }
                    // Skip first track (first side)
                    for (int m = 0; m < workingDefinition.side2.sectorIds.Length; m++)
                    {
                        sectorMask[m + workingDefinition.side1.sectorIds.Length] =
                            workingDefinition.side2.sectorIds[m] - workingDefinition.side2.sectorIds[0] +
                            workingDefinition.side1.sectorIds.Length;
                    }
                }
                // Head changes after whole side
                else if (string.Compare(workingDefinition.order, "CYLINDERS",
                                        StringComparison.InvariantCultureIgnoreCase) == 0)
                {
                    for (int m = 0; m < workingDefinition.side1.sectorIds.Length; m++)
                    {
                        sectorMask[m] = workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0];
                    }
                    // Skip first track (first side) and first track (second side)
                    for (int m = 0; m < workingDefinition.side1.sectorIds.Length; m++)
                    {
                        sectorMask[m + workingDefinition.side1.sectorIds.Length] =
                            workingDefinition.side1.sectorIds[m] - workingDefinition.side1.sectorIds[0] +
                            workingDefinition.side1.sectorIds.Length +
                            workingDefinition.side2.sectorIds.Length;
                    }

                    // TODO: Implement CYLINDERS ordering
                    DicConsole.DebugWriteLine("CP/M Plugin", "CYLINDERS ordering not yet implemented.");
                    return(Errno.NotImplemented);
                }
                // TODO: Implement COLUMBIA ordering
                else if (string.Compare(workingDefinition.order, "COLUMBIA",
                                        StringComparison.InvariantCultureIgnoreCase) == 0)
                {
                    DicConsole.DebugWriteLine("CP/M Plugin",
                                              "Don't know how to handle COLUMBIA ordering, not proceeding with this definition.");
                    return(Errno.NotImplemented);
                }
                // TODO: Implement EAGLE ordering
                else if (string.Compare(workingDefinition.order, "EAGLE", StringComparison.InvariantCultureIgnoreCase) ==
                         0)
                {
                    DicConsole.DebugWriteLine("CP/M Plugin",
                                              "Don't know how to handle EAGLE ordering, not proceeding with this definition.");
                    return(Errno.NotImplemented);
                }
                else
                {
                    DicConsole.DebugWriteLine("CP/M Plugin",
                                              "Unknown order type \"{0}\", not proceeding with this definition.",
                                              workingDefinition.order);
                    return(Errno.NotSupported);
                }
            }

            // Deinterleave whole volume
            Dictionary <ulong, byte[]> deinterleavedSectors = new Dictionary <ulong, byte[]>();

            if (workingDefinition.sides == 1 ||
                string.Compare(workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0)
            {
                DicConsole.DebugWriteLine("CP/M Plugin", "Deinterleaving whole volume.");

                for (int p = 0; p <= (int)(partition.End - partition.Start); p++)
                {
                    byte[] readSector =
                        device.ReadSector((ulong)((int)partition.Start + p / sectorMask.Length * sectorMask.Length +
                                                  sectorMask[p % sectorMask.Length]));
                    if (workingDefinition.complement)
                    {
                        for (int b = 0; b < readSector.Length; b++)
                        {
                            readSector[b] = (byte)(~readSector[b] & 0xFF);
                        }
                    }

                    deinterleavedSectors.Add((ulong)p, readSector);
                }
            }

            int          blockSize       = 128 << dpb.bsh;
            MemoryStream blockMs         = new MemoryStream();
            ulong        blockNo         = 0;
            int          sectorsPerBlock = 0;
            Dictionary <ulong, byte[]> allocationBlocks = new Dictionary <ulong, byte[]>();

            DicConsole.DebugWriteLine("CP/M Plugin", "Creating allocation blocks.");

            // For each volume sector
            for (ulong a = 0; a < (ulong)deinterleavedSectors.Count; a++)
            {
                deinterleavedSectors.TryGetValue(a, out byte[] sector);

                // May it happen? Just in case, CP/M blocks are smaller than physical sectors
                if (sector.Length > blockSize)
                {
                    for (int i = 0; i < sector.Length / blockSize; i++)
                    {
                        byte[] tmp = new byte[blockSize];
                        Array.Copy(sector, blockSize * i, tmp, 0, blockSize);
                        allocationBlocks.Add(blockNo++, tmp);
                    }
                }
                // CP/M blocks are larger than physical sectors
                else if (sector.Length < blockSize)
                {
                    blockMs.Write(sector, 0, sector.Length);
                    sectorsPerBlock++;

                    if (sectorsPerBlock != blockSize / sector.Length)
                    {
                        continue;
                    }

                    allocationBlocks.Add(blockNo++, blockMs.ToArray());
                    sectorsPerBlock = 0;
                    blockMs         = new MemoryStream();
                }
                // CP/M blocks are same size than physical sectors
                else
                {
                    allocationBlocks.Add(blockNo++, sector);
                }
            }

            DicConsole.DebugWriteLine("CP/M Plugin", "Reading directory.");

            int dirOff;
            int dirSectors = (dpb.drm + 1) * 32 / workingDefinition.bytesPerSector;

            if (workingDefinition.sofs > 0)
            {
                dirOff = workingDefinition.sofs;
            }
            else
            {
                dirOff = workingDefinition.ofs * workingDefinition.sectorsPerTrack;
            }

            // Read the whole directory blocks
            MemoryStream dirMs = new MemoryStream();

            for (int d = 0; d < dirSectors; d++)
            {
                deinterleavedSectors.TryGetValue((ulong)(d + dirOff), out byte[] sector);
                dirMs.Write(sector, 0, sector.Length);
            }

            byte[] directory = dirMs.ToArray();

            if (directory == null)
            {
                return(Errno.InvalidArgument);
            }

            int    dirCnt = 0;
            string file1  = null;
            string file2  = null;
            string file3  = null;
            Dictionary <string, Dictionary <int, List <ushort> > > fileExtents =
                new Dictionary <string, Dictionary <int, List <ushort> > >();

            statCache = new Dictionary <string, FileEntryInfo>();
            cpmStat   = new FileSystemInfo();
            bool atime = false;

            dirList           = new List <string>();
            labelCreationDate = null;
            labelUpdateDate   = null;
            passwordCache     = new Dictionary <string, byte[]>();

            DicConsole.DebugWriteLine("CP/M Plugin", "Traversing directory.");
            IntPtr dirPtr;

            // For each directory entry
            for (int dOff = 0; dOff < directory.Length; dOff += 32)
            {
                // Describes a file (does not support PDOS entries with user >= 16, because they're identical to password entries
                if ((directory[dOff] & 0x7F) < 0x10)
                {
                    if (allocationBlocks.Count > 256)
                    {
                        dirPtr = Marshal.AllocHGlobal(32);
                        Marshal.Copy(directory, dOff, dirPtr, 32);
                        DirectoryEntry16 entry =
                            (DirectoryEntry16)Marshal.PtrToStructure(dirPtr, typeof(DirectoryEntry16));
                        Marshal.FreeHGlobal(dirPtr);

                        bool hidden = (entry.statusUser & 0x80) == 0x80;
                        bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80;
                        bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80;
                        //bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80;
                        int user = entry.statusUser & 0x0F;

                        bool validEntry = true;

                        for (int i = 0; i < 8; i++)
                        {
                            entry.filename[i] &= 0x7F;
                            validEntry        &= entry.filename[i] >= 0x20;
                        }

                        for (int i = 0; i < 3; i++)
                        {
                            entry.extension[i] &= 0x7F;
                            validEntry         &= entry.extension[i] >= 0x20;
                        }

                        if (!validEntry)
                        {
                            continue;
                        }

                        string filename  = Encoding.ASCII.GetString(entry.filename).Trim();
                        string extension = Encoding.ASCII.GetString(entry.extension).Trim();

                        // If user is != 0, append user to name to have identical filenames
                        if (user > 0)
                        {
                            filename = $"{user:X1}:{filename}";
                        }
                        if (!string.IsNullOrEmpty(extension))
                        {
                            filename = filename + "." + extension;
                        }

                        int entryNo = (32 * entry.extentCounter + entry.extentCounterHigh) / (dpb.exm + 1);

                        // Do we have a stat for the file already?
                        if (statCache.TryGetValue(filename, out FileEntryInfo fInfo))
                        {
                            statCache.Remove(filename);
                        }
                        else
                        {
                            fInfo = new FileEntryInfo {
                                Attributes = new FileAttributes()
                            }
                        };

                        // And any extent?
                        if (fileExtents.TryGetValue(filename, out Dictionary <int, List <ushort> > extentBlocks))
                        {
                            fileExtents.Remove(filename);
                        }
                        else
                        {
                            extentBlocks = new Dictionary <int, List <ushort> >();
                        }

                        // Do we already have this extent? Should never happen
                        if (extentBlocks.TryGetValue(entryNo, out List <ushort> blocks))
                        {
                            extentBlocks.Remove(entryNo);
                        }
                        else
                        {
                            blocks = new List <ushort>();
                        }

                        // Attributes
                        if (hidden)
                        {
                            fInfo.Attributes |= FileAttributes.Hidden;
                        }
                        if (rdOnly)
                        {
                            fInfo.Attributes |= FileAttributes.ReadOnly;
                        }
                        if (system)
                        {
                            fInfo.Attributes |= FileAttributes.System;
                        }

                        // Supposedly there is a value in the directory entry telling how many blocks are designated in
                        // this entry. However some implementations tend to do whatever they wish, but none will ever
                        // allocate block 0 for a file because that's where the directory resides.
                        // There is also a field telling how many bytes are used in the last block, but its meaning is
                        // non-standard so we must ignore it.
                        foreach (ushort blk in entry.allocations.Where(blk => !blocks.Contains(blk) && blk != 0))
                        {
                            blocks.Add(blk);
                        }

                        // Save the file
                        fInfo.UID = (ulong)user;
                        extentBlocks.Add(entryNo, blocks);
                        fileExtents.Add(filename, extentBlocks);
                        statCache.Add(filename, fInfo);

                        // Add the file to the directory listing
                        if (!dirList.Contains(filename))
                        {
                            dirList.Add(filename);
                        }

                        // Count entries 3 by 3 for timestamps
                        switch (dirCnt % 3)
                        {
                        case 0:
                            file1 = filename;
                            break;

                        case 1:
                            file2 = filename;
                            break;

                        case 2:
                            file3 = filename;
                            break;
                        }

                        dirCnt++;
                    }
                    else
                    {
                        dirPtr = Marshal.AllocHGlobal(32);
                        Marshal.Copy(directory, dOff, dirPtr, 32);
                        DirectoryEntry entry = (DirectoryEntry)Marshal.PtrToStructure(dirPtr, typeof(DirectoryEntry));
                        Marshal.FreeHGlobal(dirPtr);

                        bool hidden = (entry.statusUser & 0x80) == 0x80;
                        bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80;
                        bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80;
                        //bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80;
                        int user = entry.statusUser & 0x0F;

                        bool validEntry = true;

                        for (int i = 0; i < 8; i++)
                        {
                            entry.filename[i] &= 0x7F;
                            validEntry        &= entry.filename[i] >= 0x20;
                        }

                        for (int i = 0; i < 3; i++)
                        {
                            entry.extension[i] &= 0x7F;
                            validEntry         &= entry.extension[i] >= 0x20;
                        }

                        if (!validEntry)
                        {
                            continue;
                        }

                        string filename  = Encoding.ASCII.GetString(entry.filename).Trim();
                        string extension = Encoding.ASCII.GetString(entry.extension).Trim();

                        // If user is != 0, append user to name to have identical filenames
                        if (user > 0)
                        {
                            filename = $"{user:X1}:{filename}";
                        }
                        if (!string.IsNullOrEmpty(extension))
                        {
                            filename = filename + "." + extension;
                        }

                        int entryNo = (32 * entry.extentCounterHigh + entry.extentCounter) / (dpb.exm + 1);

                        // Do we have a stat for the file already?
                        if (statCache.TryGetValue(filename, out FileEntryInfo fInfo))
                        {
                            statCache.Remove(filename);
                        }
                        else
                        {
                            fInfo = new FileEntryInfo {
                                Attributes = new FileAttributes()
                            }
                        };

                        // And any extent?
                        if (fileExtents.TryGetValue(filename, out Dictionary <int, List <ushort> > extentBlocks))
                        {
                            fileExtents.Remove(filename);
                        }
                        else
                        {
                            extentBlocks = new Dictionary <int, List <ushort> >();
                        }

                        // Do we already have this extent? Should never happen
                        if (extentBlocks.TryGetValue(entryNo, out List <ushort> blocks))
                        {
                            extentBlocks.Remove(entryNo);
                        }
                        else
                        {
                            blocks = new List <ushort>();
                        }

                        // Attributes
                        if (hidden)
                        {
                            fInfo.Attributes |= FileAttributes.Hidden;
                        }
                        if (rdOnly)
                        {
                            fInfo.Attributes |= FileAttributes.ReadOnly;
                        }
                        if (system)
                        {
                            fInfo.Attributes |= FileAttributes.System;
                        }

                        // Supposedly there is a value in the directory entry telling how many blocks are designated in
                        // this entry. However some implementations tend to do whatever they wish, but none will ever
                        // allocate block 0 for a file because that's where the directory resides.
                        // There is also a field telling how many bytes are used in the last block, but its meaning is
                        // non-standard so we must ignore it.
                        foreach (ushort blk in entry.allocations.Cast <ushort>()
                                 .Where(blk => !blocks.Contains(blk) && blk != 0))
                        {
                            blocks.Add(blk);
                        }

                        // Save the file
                        fInfo.UID = (ulong)user;
                        extentBlocks.Add(entryNo, blocks);
                        fileExtents.Add(filename, extentBlocks);
                        statCache.Add(filename, fInfo);

                        // Add the file to the directory listing
                        if (!dirList.Contains(filename))
                        {
                            dirList.Add(filename);
                        }

                        // Count entries 3 by 3 for timestamps
                        switch (dirCnt % 3)
                        {
                        case 0:
                            file1 = filename;
                            break;

                        case 1:
                            file2 = filename;
                            break;

                        case 2:
                            file3 = filename;
                            break;
                        }

                        dirCnt++;
                    }
                }
                // A password entry (or a file entry in PDOS, but this does not handle that case)
                else if ((directory[dOff] & 0x7F) >= 0x10 && (directory[dOff] & 0x7F) < 0x20)
                {
                    dirPtr = Marshal.AllocHGlobal(32);
                    Marshal.Copy(directory, dOff, dirPtr, 32);
                    PasswordEntry entry = (PasswordEntry)Marshal.PtrToStructure(dirPtr, typeof(PasswordEntry));
                    Marshal.FreeHGlobal(dirPtr);

                    int user = entry.userNumber & 0x0F;

                    for (int i = 0; i < 8; i++)
                    {
                        entry.filename[i] &= 0x7F;
                    }
                    for (int i = 0; i < 3; i++)
                    {
                        entry.extension[i] &= 0x7F;
                    }

                    string filename  = Encoding.ASCII.GetString(entry.filename).Trim();
                    string extension = Encoding.ASCII.GetString(entry.extension).Trim();

                    // If user is != 0, append user to name to have identical filenames
                    if (user > 0)
                    {
                        filename = $"{user:X1}:{filename}";
                    }
                    if (!string.IsNullOrEmpty(extension))
                    {
                        filename = filename + "." + extension;
                    }

                    // Do not repeat passwords
                    if (passwordCache.ContainsKey(filename))
                    {
                        passwordCache.Remove(filename);
                    }

                    // Copy whole password entry
                    byte[] tmp = new byte[32];
                    Array.Copy(directory, dOff, tmp, 0, 32);
                    passwordCache.Add(filename, tmp);

                    // Count entries 3 by 3 for timestamps
                    switch (dirCnt % 3)
                    {
                    case 0:
                        file1 = filename;
                        break;

                    case 1:
                        file2 = filename;
                        break;

                    case 2:
                        file3 = filename;
                        break;
                    }

                    dirCnt++;
                }
                // Volume label and password entry. Volume password is ignored.
                else
                {
                    switch (directory[dOff] & 0x7F)
                    {
                    case 0x20:
                        LabelEntry labelEntry;
                        dirPtr = Marshal.AllocHGlobal(32);
                        Marshal.Copy(directory, dOff, dirPtr, 32);
                        labelEntry = (LabelEntry)Marshal.PtrToStructure(dirPtr, typeof(LabelEntry));
                        Marshal.FreeHGlobal(dirPtr);

                        // The volume label defines if one of the fields in CP/M 3 timestamp is a creation or an
                        // access time
                        atime |= (labelEntry.flags & 0x40) == 0x40;

                        label             = Encoding.ASCII.GetString(directory, dOff + 1, 11).Trim();
                        labelCreationDate = new byte[4];
                        labelUpdateDate   = new byte[4];
                        Array.Copy(directory, dOff + 24, labelCreationDate, 0, 4);
                        Array.Copy(directory, dOff + 28, labelUpdateDate, 0, 4);

                        // Count entries 3 by 3 for timestamps
                        switch (dirCnt % 3)
                        {
                        case 0:
                            file1 = null;
                            break;

                        case 1:
                            file2 = null;
                            break;

                        case 2:
                            file3 = null;
                            break;
                        }

                        dirCnt++;
                        break;

                    case 0x21:
                        if (directory[dOff + 10] == 0x00 && directory[dOff + 20] == 0x00 &&
                            directory[dOff + 30] == 0x00 && directory[dOff + 31] == 0x00)
                        {
                            dirPtr = Marshal.AllocHGlobal(32);
                            Marshal.Copy(directory, dOff, dirPtr, 32);
                            DateEntry dateEntry = (DateEntry)Marshal.PtrToStructure(dirPtr, typeof(DateEntry));
                            Marshal.FreeHGlobal(dirPtr);

                            FileEntryInfo fInfo;

                            // Entry contains timestamps for last 3 entries, whatever the kind they are.
                            if (!string.IsNullOrEmpty(file1))
                            {
                                if (statCache.TryGetValue(file1, out fInfo))
                                {
                                    statCache.Remove(file1);
                                }
                                else
                                {
                                    fInfo = new FileEntryInfo();
                                }

                                if (atime)
                                {
                                    fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date1);
                                }
                                else
                                {
                                    fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date1);
                                }

                                fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date2);

                                statCache.Add(file1, fInfo);
                            }

                            if (!string.IsNullOrEmpty(file2))
                            {
                                if (statCache.TryGetValue(file2, out fInfo))
                                {
                                    statCache.Remove(file2);
                                }
                                else
                                {
                                    fInfo = new FileEntryInfo();
                                }

                                if (atime)
                                {
                                    fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date3);
                                }
                                else
                                {
                                    fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date3);
                                }

                                fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date4);

                                statCache.Add(file2, fInfo);
                            }

                            if (!string.IsNullOrEmpty(file3))
                            {
                                if (statCache.TryGetValue(file3, out fInfo))
                                {
                                    statCache.Remove(file3);
                                }
                                else
                                {
                                    fInfo = new FileEntryInfo();
                                }

                                if (atime)
                                {
                                    fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date5);
                                }
                                else
                                {
                                    fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date5);
                                }

                                fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date6);

                                statCache.Add(file3, fInfo);
                            }

                            file1  = null;
                            file2  = null;
                            file3  = null;
                            dirCnt = 0;
                        }
                        // However, if this byte is 0, timestamp is in Z80DOS or DOS+ format
                        else if (directory[dOff + 1] == 0x00)
                        {
                            dirPtr = Marshal.AllocHGlobal(32);
                            Marshal.Copy(directory, dOff, dirPtr, 32);
                            TrdPartyDateEntry trdPartyDateEntry =
                                (TrdPartyDateEntry)Marshal.PtrToStructure(dirPtr, typeof(TrdPartyDateEntry));
                            Marshal.FreeHGlobal(dirPtr);

                            FileEntryInfo fInfo;

                            // Entry contains timestamps for last 3 entries, whatever the kind they are.
                            if (!string.IsNullOrEmpty(file1))
                            {
                                if (statCache.TryGetValue(file1, out fInfo))
                                {
                                    statCache.Remove(file1);
                                }
                                else
                                {
                                    fInfo = new FileEntryInfo();
                                }

                                byte[] ctime = new byte[4];
                                ctime[0] = trdPartyDateEntry.create1[0];
                                ctime[1] = trdPartyDateEntry.create1[1];

                                fInfo.AccessTime    = DateHandlers.CpmToDateTime(trdPartyDateEntry.access1);
                                fInfo.CreationTime  = DateHandlers.CpmToDateTime(ctime);
                                fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify1);

                                statCache.Add(file1, fInfo);
                            }

                            if (!string.IsNullOrEmpty(file2))
                            {
                                if (statCache.TryGetValue(file2, out fInfo))
                                {
                                    statCache.Remove(file2);
                                }
                                else
                                {
                                    fInfo = new FileEntryInfo();
                                }

                                byte[] ctime = new byte[4];
                                ctime[0] = trdPartyDateEntry.create2[0];
                                ctime[1] = trdPartyDateEntry.create2[1];

                                fInfo.AccessTime    = DateHandlers.CpmToDateTime(trdPartyDateEntry.access2);
                                fInfo.CreationTime  = DateHandlers.CpmToDateTime(ctime);
                                fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify2);

                                statCache.Add(file2, fInfo);
                            }

                            if (!string.IsNullOrEmpty(file3))
                            {
                                if (statCache.TryGetValue(file1, out fInfo))
                                {
                                    statCache.Remove(file3);
                                }
                                else
                                {
                                    fInfo = new FileEntryInfo();
                                }

                                byte[] ctime = new byte[4];
                                ctime[0] = trdPartyDateEntry.create3[0];
                                ctime[1] = trdPartyDateEntry.create3[1];

                                fInfo.AccessTime    = DateHandlers.CpmToDateTime(trdPartyDateEntry.access3);
                                fInfo.CreationTime  = DateHandlers.CpmToDateTime(ctime);
                                fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify3);

                                statCache.Add(file3, fInfo);
                            }

                            file1  = null;
                            file2  = null;
                            file3  = null;
                            dirCnt = 0;
                        }

                        break;
                    }
                }
            }

            // Cache all files. As CP/M maximum volume size is 8 Mib
            // this should not be a problem
            DicConsole.DebugWriteLine("CP/M Plugin", "Reading files.");
            long usedBlocks = 0;

            fileCache = new Dictionary <string, byte[]>();
            foreach (string filename in dirList)
            {
                MemoryStream fileMs = new MemoryStream();

                if (statCache.TryGetValue(filename, out FileEntryInfo fInfo))
                {
                    statCache.Remove(filename);
                }

                fInfo.Blocks = 0;

                if (fileExtents.TryGetValue(filename, out Dictionary <int, List <ushort> > extents))
                {
                    for (int ex = 0; ex < extents.Count; ex++)
                    {
                        if (!extents.TryGetValue(ex, out List <ushort> alBlks))
                        {
                            continue;
                        }

                        foreach (ushort alBlk in alBlks)
                        {
                            allocationBlocks.TryGetValue(alBlk, out byte[] blk);
                            fileMs.Write(blk, 0, blk.Length);
                            fInfo.Blocks++;
                        }
                    }
                }

                // If you insist to call CP/M "extent based"
                fInfo.Attributes |= FileAttributes.Extents;
                fInfo.BlockSize   = blockSize;
                fInfo.Length      = fileMs.Length;
                cpmStat.Files++;
                usedBlocks += fInfo.Blocks;

                statCache.Add(filename, fInfo);
                fileCache.Add(filename, fileMs.ToArray());
            }

            decodedPasswordCache = new Dictionary <string, byte[]>();
            // For each stored password, store a decoded version of it
            if (passwordCache.Count > 0)
            {
                foreach (KeyValuePair <string, byte[]> kvp in passwordCache)
                {
                    byte[] tmp = new byte[8];
                    Array.Copy(kvp.Value, 16, tmp, 0, 8);
                    for (int t = 0; t < 8; t++)
                    {
                        tmp[t] ^= kvp.Value[13];
                    }

                    decodedPasswordCache.Add(kvp.Key, tmp);
                }
            }

            // Generate statfs.
            cpmStat.Blocks         = dpb.dsm + 1;
            cpmStat.FilenameLength = 11;
            cpmStat.Files          = (ulong)fileCache.Count;
            cpmStat.FreeBlocks     = cpmStat.Blocks - usedBlocks;
            cpmStat.PluginId       = Id;
            cpmStat.Type           = "CP/M filesystem";

            // Generate XML info
            XmlFsType = new FileSystemType
            {
                Clusters              = cpmStat.Blocks,
                ClusterSize           = blockSize,
                Files                 = fileCache.Count,
                FilesSpecified        = true,
                FreeClusters          = cpmStat.FreeBlocks,
                FreeClustersSpecified = true,
                Type = "CP/M filesystem"
            };
            if (labelCreationDate != null)
            {
                XmlFsType.CreationDate          = DateHandlers.CpmToDateTime(labelCreationDate);
                XmlFsType.CreationDateSpecified = true;
            }

            if (labelUpdateDate != null)
            {
                XmlFsType.ModificationDate          = DateHandlers.CpmToDateTime(labelUpdateDate);
                XmlFsType.ModificationDateSpecified = true;
            }

            if (!string.IsNullOrEmpty(label))
            {
                XmlFsType.VolumeName = label;
            }

            mounted = true;
            return(Errno.NoError);
        }
Beispiel #11
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? new MacRoman();
            information = "";

            StringBuilder sb = new StringBuilder();

            MFS_MasterDirectoryBlock mdb = new MFS_MasterDirectoryBlock();
            MFS_BootBlock            bb  = new MFS_BootBlock();

            byte[] pString = new byte[16];

            byte[] mdbSector = imagePlugin.ReadSector(2 + partition.Start);
            byte[] bbSector  = imagePlugin.ReadSector(0 + partition.Start);

            mdb.drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x000);
            if (mdb.drSigWord != MFS_MAGIC)
            {
                return;
            }

            mdb.drCrDate   = BigEndianBitConverter.ToUInt32(mdbSector, 0x002);
            mdb.drLsBkUp   = BigEndianBitConverter.ToUInt32(mdbSector, 0x006);
            mdb.drAtrb     = BigEndianBitConverter.ToUInt16(mdbSector, 0x00A);
            mdb.drNmFls    = BigEndianBitConverter.ToUInt16(mdbSector, 0x00C);
            mdb.drDirSt    = BigEndianBitConverter.ToUInt16(mdbSector, 0x00E);
            mdb.drBlLen    = BigEndianBitConverter.ToUInt16(mdbSector, 0x010);
            mdb.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbSector, 0x012);
            mdb.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x014);
            mdb.drClpSiz   = BigEndianBitConverter.ToUInt32(mdbSector, 0x018);
            mdb.drAlBlSt   = BigEndianBitConverter.ToUInt16(mdbSector, 0x01C);
            mdb.drNxtFNum  = BigEndianBitConverter.ToUInt32(mdbSector, 0x01E);
            mdb.drFreeBks  = BigEndianBitConverter.ToUInt16(mdbSector, 0x022);
            mdb.drVNSiz    = mdbSector[0x024];
            byte[] variableSize = new byte[mdb.drVNSiz + 1];
            Array.Copy(mdbSector, 0x024, variableSize, 0, mdb.drVNSiz + 1);
            mdb.drVN = StringHandlers.PascalToString(variableSize, Encoding);

            bb.signature = BigEndianBitConverter.ToUInt16(bbSector, 0x000);

            if (bb.signature == MFSBB_MAGIC)
            {
                bb.branch       = BigEndianBitConverter.ToUInt32(bbSector, 0x002);
                bb.boot_flags   = bbSector[0x006];
                bb.boot_version = bbSector[0x007];

                bb.sec_sv_pages = BigEndianBitConverter.ToInt16(bbSector, 0x008);

                Array.Copy(mdbSector, 0x00A, pString, 0, 16);
                bb.system_name = StringHandlers.PascalToString(pString, Encoding);
                Array.Copy(mdbSector, 0x01A, pString, 0, 16);
                bb.finder_name = StringHandlers.PascalToString(pString, Encoding);
                Array.Copy(mdbSector, 0x02A, pString, 0, 16);
                bb.debug_name = StringHandlers.PascalToString(pString, Encoding);
                Array.Copy(mdbSector, 0x03A, pString, 0, 16);
                bb.disasm_name = StringHandlers.PascalToString(pString, Encoding);
                Array.Copy(mdbSector, 0x04A, pString, 0, 16);
                bb.stupscr_name = StringHandlers.PascalToString(pString, Encoding);
                Array.Copy(mdbSector, 0x05A, pString, 0, 16);
                bb.bootup_name = StringHandlers.PascalToString(pString, Encoding);
                Array.Copy(mdbSector, 0x06A, pString, 0, 16);
                bb.clipbrd_name = StringHandlers.PascalToString(pString, Encoding);

                bb.max_files  = BigEndianBitConverter.ToUInt16(bbSector, 0x07A);
                bb.queue_size = BigEndianBitConverter.ToUInt16(bbSector, 0x07C);
                bb.heap_128k  = BigEndianBitConverter.ToUInt32(bbSector, 0x07E);
                bb.heap_256k  = BigEndianBitConverter.ToUInt32(bbSector, 0x082);
                bb.heap_512k  = BigEndianBitConverter.ToUInt32(bbSector, 0x086);
            }
            else
            {
                bb.signature = 0x0000;
            }

            sb.AppendLine("Apple Macintosh File System");
            sb.AppendLine();
            sb.AppendLine("Master Directory Block:");
            sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine();
            sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drLsBkUp)).AppendLine();
            if ((mdb.drAtrb & 0x80) == 0x80)
            {
                sb.AppendLine("Volume is locked by hardware.");
            }
            if ((mdb.drAtrb & 0x8000) == 0x8000)
            {
                sb.AppendLine("Volume is locked by software.");
            }
            sb.AppendFormat("{0} files on volume", mdb.drNmFls).AppendLine();
            sb.AppendFormat("First directory sector: {0}", mdb.drDirSt).AppendLine();
            sb.AppendFormat("{0} sectors in directory.", mdb.drBlLen).AppendLine();
            sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks + 1).AppendLine();
            sb.AppendFormat("Size of allocation blocks: {0} bytes", mdb.drAlBlkSiz).AppendLine();
            sb.AppendFormat("{0} bytes to allocate.", mdb.drClpSiz).AppendLine();
            sb.AppendFormat("First allocation block (#2) starts in sector {0}.", mdb.drAlBlSt).AppendLine();
            sb.AppendFormat("Next unused file number: {0}", mdb.drNxtFNum).AppendLine();
            sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine();
            sb.AppendFormat("Volume name: {0}", mdb.drVN).AppendLine();

            if (bb.signature == MFSBB_MAGIC)
            {
                sb.AppendLine("Volume is bootable.");
                sb.AppendLine();
                sb.AppendLine("Boot Block:");
                if ((bb.boot_flags & 0x40) == 0x40)
                {
                    sb.AppendLine("Boot block should be executed.");
                }
                if ((bb.boot_flags & 0x80) == 0x80)
                {
                    sb.AppendLine("Boot block is in new unknown format.");
                }
                else
                {
                    if (bb.sec_sv_pages > 0)
                    {
                        sb.AppendLine("Allocate secondary sound buffer at boot.");
                    }
                    else if (bb.sec_sv_pages < 0)
                    {
                        sb.AppendLine("Allocate secondary sound and video buffers at boot.");
                    }

                    sb.AppendFormat("System filename: {0}", bb.system_name).AppendLine();
                    sb.AppendFormat("Finder filename: {0}", bb.finder_name).AppendLine();
                    sb.AppendFormat("Debugger filename: {0}", bb.debug_name).AppendLine();
                    sb.AppendFormat("Disassembler filename: {0}", bb.disasm_name).AppendLine();
                    sb.AppendFormat("Startup screen filename: {0}", bb.stupscr_name).AppendLine();
                    sb.AppendFormat("First program to execute at boot: {0}", bb.bootup_name).AppendLine();
                    sb.AppendFormat("Clipboard filename: {0}", bb.clipbrd_name).AppendLine();
                    sb.AppendFormat("Maximum opened files: {0}", bb.max_files * 4).AppendLine();
                    sb.AppendFormat("Event queue size: {0}", bb.queue_size).AppendLine();
                    sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.heap_128k).AppendLine();
                    sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.heap_256k).AppendLine();
                    sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.heap_512k).AppendLine();
                }
            }
            else
            {
                sb.AppendLine("Volume is not bootable.");
            }

            information = sb.ToString();

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

            XmlFsType.Bootable    = bb.signature == MFSBB_MAGIC;
            XmlFsType.Clusters    = mdb.drNmAlBlks;
            XmlFsType.ClusterSize = mdb.drAlBlkSiz;
            if (mdb.drCrDate > 0)
            {
                XmlFsType.CreationDate          = DateHandlers.MacToDateTime(mdb.drCrDate);
                XmlFsType.CreationDateSpecified = true;
            }

            XmlFsType.Files                 = mdb.drNmFls;
            XmlFsType.FilesSpecified        = true;
            XmlFsType.FreeClusters          = mdb.drFreeBks;
            XmlFsType.FreeClustersSpecified = true;
            XmlFsType.Type       = "MFS";
            XmlFsType.VolumeName = mdb.drVN;
        }
Beispiel #12
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            // ZFS is always UTF-8
            Encoding    = Encoding.UTF8;
            information = "";
            if (imagePlugin.Info.SectorSize < 512)
            {
                return;
            }

            byte[] sector;
            ulong  magic;

            ulong nvlistOff = 32;
            uint  nvlistLen = 114688 / imagePlugin.Info.SectorSize;

            if (partition.Start + 31 < partition.End)
            {
                sector = imagePlugin.ReadSector(partition.Start + 31);
                magic  = BitConverter.ToUInt64(sector, 0x1D8);
                if (magic == ZEC_MAGIC || magic == ZEC_CIGAM)
                {
                    nvlistOff = 32;
                }
            }

            if (partition.Start + 16 < partition.End)
            {
                sector = imagePlugin.ReadSector(partition.Start + 16);
                magic  = BitConverter.ToUInt64(sector, 0x1D8);
                if (magic == ZEC_MAGIC || magic == ZEC_CIGAM)
                {
                    nvlistOff = 17;
                }
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendLine("ZFS filesystem");

            byte[] nvlist = imagePlugin.ReadSectors(partition.Start + nvlistOff, nvlistLen);

            sb.AppendLine(!DecodeNvList(nvlist, out Dictionary <string, NVS_Item> decodedNvList)
                              ? "Could not decode nvlist"
                              : PrintNvList(decodedNvList));

            information = sb.ToString();

            XmlFsType = new FileSystemType {
                Type = "ZFS filesystem"
            };
            if (decodedNvList.TryGetValue("name", out NVS_Item tmpObj))
            {
                XmlFsType.VolumeName = (string)tmpObj.value;
            }
            if (decodedNvList.TryGetValue("guid", out tmpObj))
            {
                XmlFsType.VolumeSerial = $"{(ulong)tmpObj.value}";
            }
            if (decodedNvList.TryGetValue("pool_guid", out tmpObj))
            {
                XmlFsType.VolumeSetIdentifier = $"{(ulong)tmpObj.value}";
            }
        }
Beispiel #13
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-1");
            information = "";

            var sb = new StringBuilder();

            byte[] hbSector = imagePlugin.ReadSector(1 + partition.Start);

            HomeBlock homeblock = Marshal.ByteArrayToStructureLittleEndian <HomeBlock>(hbSector);

            // Optical disc
            if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc &&
                StringHandlers.CToString(homeblock.format) != "DECFILE11A  " &&
                StringHandlers.CToString(homeblock.format) != "DECFILE11B  ")
            {
                if (hbSector.Length < 0x400)
                {
                    return;
                }

                byte[] tmp = imagePlugin.ReadSector(partition.Start);
                hbSector = new byte[0x200];
                Array.Copy(tmp, 0x200, hbSector, 0, 0x200);

                homeblock = Marshal.ByteArrayToStructureLittleEndian <HomeBlock>(hbSector);

                if (StringHandlers.CToString(homeblock.format) != "DECFILE11A  " &&
                    StringHandlers.CToString(homeblock.format) != "DECFILE11B  ")
                {
                    return;
                }
            }

            if ((homeblock.struclev & 0xFF00) != 0x0200 ||
                (homeblock.struclev & 0xFF) != 1 ||
                StringHandlers.CToString(homeblock.format) != "DECFILE11B  ")
            {
                sb.AppendLine("The following information may be incorrect for this volume.");
            }

            if (homeblock.resfiles < 5 ||
                homeblock.devtype != 0)
            {
                sb.AppendLine("This volume may be corrupted.");
            }

            sb.AppendFormat("Volume format is {0}", StringHandlers.SpacePaddedToString(homeblock.format, Encoding)).
            AppendLine();

            sb.AppendFormat("Volume is Level {0} revision {1}", (homeblock.struclev & 0xFF00) >> 8,
                            homeblock.struclev & 0xFF).AppendLine();

            sb.AppendFormat("Lowest structure in the volume is Level {0}, revision {1}",
                            (homeblock.lowstruclev & 0xFF00) >> 8, homeblock.lowstruclev & 0xFF).AppendLine();

            sb.AppendFormat("Highest structure in the volume is Level {0}, revision {1}",
                            (homeblock.highstruclev & 0xFF00) >> 8, homeblock.highstruclev & 0xFF).AppendLine();

            sb.AppendFormat("{0} sectors per cluster ({1} bytes)", homeblock.cluster, homeblock.cluster * 512).
            AppendLine();

            sb.AppendFormat("This home block is on sector {0} (VBN {1})", homeblock.homelbn, homeblock.homevbn).
            AppendLine();

            sb.AppendFormat("Secondary home block is on sector {0} (VBN {1})", homeblock.alhomelbn,
                            homeblock.alhomevbn).AppendLine();

            sb.AppendFormat("Volume bitmap starts in sector {0} (VBN {1})", homeblock.ibmaplbn, homeblock.ibmapvbn).
            AppendLine();

            sb.AppendFormat("Volume bitmap runs for {0} sectors ({1} bytes)", homeblock.ibmapsize,
                            homeblock.ibmapsize * 512).AppendLine();

            sb.AppendFormat("Backup INDEXF.SYS;1 is in sector {0} (VBN {1})", homeblock.altidxlbn, homeblock.altidxvbn).
            AppendLine();

            sb.AppendFormat("{0} maximum files on the volume", homeblock.maxfiles).AppendLine();
            sb.AppendFormat("{0} reserved files", homeblock.resfiles).AppendLine();

            if (homeblock.rvn > 0 &&
                homeblock.setcount > 0 &&
                StringHandlers.CToString(homeblock.strucname) != "            ")
            {
                sb.AppendFormat("Volume is {0} of {1} in set \"{2}\".", homeblock.rvn, homeblock.setcount,
                                StringHandlers.SpacePaddedToString(homeblock.strucname, Encoding)).AppendLine();
            }

            sb.AppendFormat("Volume owner is \"{0}\" (ID 0x{1:X8})",
                            StringHandlers.SpacePaddedToString(homeblock.ownername, Encoding), homeblock.volowner).
            AppendLine();

            sb.AppendFormat("Volume label: \"{0}\"", StringHandlers.SpacePaddedToString(homeblock.volname, Encoding)).
            AppendLine();

            sb.AppendFormat("Drive serial number: 0x{0:X8}", homeblock.serialnum).AppendLine();
            sb.AppendFormat("Volume was created on {0}", DateHandlers.VmsToDateTime(homeblock.credate)).AppendLine();

            if (homeblock.revdate > 0)
            {
                sb.AppendFormat("Volume was last modified on {0}", DateHandlers.VmsToDateTime(homeblock.revdate)).
                AppendLine();
            }

            if (homeblock.copydate > 0)
            {
                sb.AppendFormat("Volume copied on {0}", DateHandlers.VmsToDateTime(homeblock.copydate)).AppendLine();
            }

            sb.AppendFormat("Checksums: 0x{0:X4} and 0x{1:X4}", homeblock.checksum1, homeblock.checksum2).AppendLine();
            sb.AppendLine("Flags:");
            sb.AppendFormat("Window: {0}", homeblock.window).AppendLine();
            sb.AppendFormat("Cached directores: {0}", homeblock.lru_lim).AppendLine();
            sb.AppendFormat("Default allocation: {0} blocks", homeblock.extend).AppendLine();

            if ((homeblock.volchar & 0x01) == 0x01)
            {
                sb.AppendLine("Readings should be verified");
            }

            if ((homeblock.volchar & 0x02) == 0x02)
            {
                sb.AppendLine("Writings should be verified");
            }

            if ((homeblock.volchar & 0x04) == 0x04)
            {
                sb.AppendLine("Files should be erased or overwritten when deleted");
            }

            if ((homeblock.volchar & 0x08) == 0x08)
            {
                sb.AppendLine("Highwater mark is to be disabled");
            }

            if ((homeblock.volchar & 0x10) == 0x10)
            {
                sb.AppendLine("Classification checks are enabled");
            }

            sb.AppendLine("Volume permissions (r = read, w = write, c = create, d = delete)");
            sb.AppendLine("System, owner, group, world");

            // System
            sb.Append((homeblock.protect & 0x1000) == 0x1000 ? "-" : "r");
            sb.Append((homeblock.protect & 0x2000) == 0x2000 ? "-" : "w");
            sb.Append((homeblock.protect & 0x4000) == 0x4000 ? "-" : "c");
            sb.Append((homeblock.protect & 0x8000) == 0x8000 ? "-" : "d");

            // Owner
            sb.Append((homeblock.protect & 0x100) == 0x100 ? "-" : "r");
            sb.Append((homeblock.protect & 0x200) == 0x200 ? "-" : "w");
            sb.Append((homeblock.protect & 0x400) == 0x400 ? "-" : "c");
            sb.Append((homeblock.protect & 0x800) == 0x800 ? "-" : "d");

            // Group
            sb.Append((homeblock.protect & 0x10) == 0x10 ? "-" : "r");
            sb.Append((homeblock.protect & 0x20) == 0x20 ? "-" : "w");
            sb.Append((homeblock.protect & 0x40) == 0x40 ? "-" : "c");
            sb.Append((homeblock.protect & 0x80) == 0x80 ? "-" : "d");

            // World (other)
            sb.Append((homeblock.protect & 0x1) == 0x1 ? "-" : "r");
            sb.Append((homeblock.protect & 0x2) == 0x2 ? "-" : "w");
            sb.Append((homeblock.protect & 0x4) == 0x4 ? "-" : "c");
            sb.Append((homeblock.protect & 0x8) == 0x8 ? "-" : "d");

            sb.AppendLine();

            sb.AppendLine("Unknown structures:");
            sb.AppendFormat("Security mask: 0x{0:X8}", homeblock.sec_mask).AppendLine();
            sb.AppendFormat("File protection: 0x{0:X4}", homeblock.fileprot).AppendLine();
            sb.AppendFormat("Record protection: 0x{0:X4}", homeblock.recprot).AppendLine();

            XmlFsType = new FileSystemType
            {
                Type         = "FILES-11",
                ClusterSize  = (uint)(homeblock.cluster * 512),
                Clusters     = partition.Size / (ulong)(homeblock.cluster * 512),
                VolumeName   = StringHandlers.SpacePaddedToString(homeblock.volname, Encoding),
                VolumeSerial = $"{homeblock.serialnum:X8}"
            };

            if (homeblock.credate > 0)
            {
                XmlFsType.CreationDate          = DateHandlers.VmsToDateTime(homeblock.credate);
                XmlFsType.CreationDateSpecified = true;
            }

            if (homeblock.revdate > 0)
            {
                XmlFsType.ModificationDate          = DateHandlers.VmsToDateTime(homeblock.revdate);
                XmlFsType.ModificationDateSpecified = true;
            }

            information = sb.ToString();
        }
Beispiel #14
0
        public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset)
        {
            partitions = new List <Partition>();
            uint nSectors = 2048 / imagePlugin.Info.SectorSize;

            if (2048 % imagePlugin.Info.SectorSize > 0)
            {
                nSectors++;
            }

            if (sectorOffset + nSectors >= imagePlugin.Info.Sectors)
            {
                return(false);
            }

            byte[] sectors = imagePlugin.ReadSectors(sectorOffset, nSectors);
            if (sectors.Length < 2048)
            {
                return(false);
            }

            IntPtr labelPtr = Marshal.AllocHGlobal(2048);

            Marshal.Copy(sectors, 0, labelPtr, 2048);
            Disklabel64 disklabel = (Disklabel64)Marshal.PtrToStructure(labelPtr, typeof(Disklabel64));

            Marshal.FreeHGlobal(labelPtr);

            if (disklabel.d_magic != 0xC4464C59)
            {
                return(false);
            }

            ulong counter = 0;

            foreach (Partition64 entry in disklabel.d_partitions)
            {
                Partition part = new Partition
                {
                    Start  = entry.p_boffset / imagePlugin.Info.SectorSize + sectorOffset,
                    Offset = entry.p_boffset +
                             sectorOffset * imagePlugin.Info.SectorSize,
                    Size     = entry.p_bsize,
                    Length   = entry.p_bsize / imagePlugin.Info.SectorSize,
                    Name     = entry.p_stor_uuid.ToString(),
                    Sequence = counter,
                    Scheme   = Name,
                    Type     = (BSD.fsType)entry.p_fstype == BSD.fsType.Other
                               ? entry.p_type_uuid.ToString()
                               : BSD.fsTypeToString((BSD.fsType)entry.p_fstype)
                };

                if (entry.p_bsize % imagePlugin.Info.SectorSize > 0)
                {
                    part.Length++;
                }

                if (entry.p_bsize <= 0 || entry.p_boffset <= 0)
                {
                    continue;
                }

                partitions.Add(part);
                counter++;
            }

            return(true);
        }
Beispiel #15
0
        public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
                           Dictionary <string, string> options, string @namespace)
        {
            device         = imagePlugin;
            partitionStart = partition.Start;
            Encoding       = encoding ?? Encoding.GetEncoding("macintosh");
            if (options == null)
            {
                options = GetDefaultOptions();
            }
            if (options.TryGetValue("debug", out string debugString))
            {
                bool.TryParse(debugString, out debug);
            }
            volMDB = new MFS_MasterDirectoryBlock();

            mdbBlocks  = device.ReadSector(2 + partitionStart);
            bootBlocks = device.ReadSector(0 + partitionStart);

            BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;

            volMDB.drSigWord = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x000);
            if (volMDB.drSigWord != MFS_MAGIC)
            {
                return(Errno.InvalidArgument);
            }

            volMDB.drCrDate   = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x002);
            volMDB.drLsBkUp   = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x006);
            volMDB.drAtrb     = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00A);
            volMDB.drNmFls    = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00C);
            volMDB.drDirSt    = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00E);
            volMDB.drBlLen    = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x010);
            volMDB.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x012);
            volMDB.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x014);
            volMDB.drClpSiz   = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x018);
            volMDB.drAlBlSt   = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x01C);
            volMDB.drNxtFNum  = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x01E);
            volMDB.drFreeBks  = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x022);
            volMDB.drVNSiz    = mdbBlocks[0x024];
            byte[] variableSize = new byte[volMDB.drVNSiz + 1];
            Array.Copy(mdbBlocks, 0x024, variableSize, 0, volMDB.drVNSiz + 1);
            volMDB.drVN = StringHandlers.PascalToString(variableSize, Encoding);

            directoryBlocks = device.ReadSectors(volMDB.drDirSt + partitionStart, volMDB.drBlLen);
            int       bytesInBlockMap        = volMDB.drNmAlBlks * 12 / 8 + volMDB.drNmAlBlks * 12 % 8;
            const int BYTES_BEFORE_BLOCK_MAP = 64;
            int       bytesInWholeMdb        = bytesInBlockMap + BYTES_BEFORE_BLOCK_MAP;
            int       sectorsInWholeMdb      = bytesInWholeMdb / (int)device.Info.SectorSize +
                                               bytesInWholeMdb % (int)device.Info.SectorSize;

            byte[] wholeMdb = device.ReadSectors(partitionStart + 2, (uint)sectorsInWholeMdb);
            blockMapBytes = new byte[bytesInBlockMap];
            Array.Copy(wholeMdb, BYTES_BEFORE_BLOCK_MAP, blockMapBytes, 0, blockMapBytes.Length);

            int offset = 0;

            blockMap = new uint[volMDB.drNmAlBlks + 2 + 1];
            for (int i = 2; i < volMDB.drNmAlBlks + 2; i += 8)
            {
                uint tmp1 = 0;
                uint tmp2 = 0;
                uint tmp3 = 0;

                if (offset + 4 <= blockMapBytes.Length)
                {
                    tmp1 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset);
                }
                if (offset + 4 + 4 <= blockMapBytes.Length)
                {
                    tmp2 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 4);
                }
                if (offset + 8 + 4 <= blockMapBytes.Length)
                {
                    tmp3 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 8);
                }

                if (i < blockMap.Length)
                {
                    blockMap[i] = (tmp1 & 0xFFF00000) >> 20;
                }
                if (i + 2 < blockMap.Length)
                {
                    blockMap[i + 1] = (tmp1 & 0xFFF00) >> 8;
                }
                if (i + 3 < blockMap.Length)
                {
                    blockMap[i + 2] = ((tmp1 & 0xFF) << 4) + ((tmp2 & 0xF0000000) >> 28);
                }
                if (i + 4 < blockMap.Length)
                {
                    blockMap[i + 3] = (tmp2 & 0xFFF0000) >> 16;
                }
                if (i + 5 < blockMap.Length)
                {
                    blockMap[i + 4] = (tmp2 & 0xFFF0) >> 4;
                }
                if (i + 6 < blockMap.Length)
                {
                    blockMap[i + 5] = ((tmp2 & 0xF) << 8) + ((tmp3 & 0xFF000000) >> 24);
                }
                if (i + 7 < blockMap.Length)
                {
                    blockMap[i + 6] = (tmp3 & 0xFFF000) >> 12;
                }
                if (i + 8 < blockMap.Length)
                {
                    blockMap[i + 7] = tmp3 & 0xFFF;
                }

                offset += 12;
            }

            if (device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag))
            {
                mdbTags       = device.ReadSectorTag(2 + partitionStart, SectorTagType.AppleSectorTag);
                bootTags      = device.ReadSectorTag(0 + partitionStart, SectorTagType.AppleSectorTag);
                directoryTags = device.ReadSectorsTag(volMDB.drDirSt + partitionStart, volMDB.drBlLen,
                                                      SectorTagType.AppleSectorTag);
                bitmapTags = device.ReadSectorsTag(partitionStart + 2, (uint)sectorsInWholeMdb,
                                                   SectorTagType.AppleSectorTag);
            }

            sectorsPerBlock = (int)(volMDB.drAlBlkSiz / device.Info.SectorSize);

            if (!FillDirectory())
            {
                return(Errno.InvalidArgument);
            }

            mounted = true;

            ushort bbSig = BigEndianBitConverter.ToUInt16(bootBlocks, 0x000);

            if (bbSig != MFSBB_MAGIC)
            {
                bootBlocks = null;
            }

            XmlFsType = new FileSystemType();
            if (volMDB.drLsBkUp > 0)
            {
                XmlFsType.BackupDate          = DateHandlers.MacToDateTime(volMDB.drLsBkUp);
                XmlFsType.BackupDateSpecified = true;
            }

            XmlFsType.Bootable    = bbSig == MFSBB_MAGIC;
            XmlFsType.Clusters    = volMDB.drNmAlBlks;
            XmlFsType.ClusterSize = volMDB.drAlBlkSiz;
            if (volMDB.drCrDate > 0)
            {
                XmlFsType.CreationDate          = DateHandlers.MacToDateTime(volMDB.drCrDate);
                XmlFsType.CreationDateSpecified = true;
            }

            XmlFsType.Files                 = volMDB.drNmFls;
            XmlFsType.FilesSpecified        = true;
            XmlFsType.FreeClusters          = volMDB.drFreeBks;
            XmlFsType.FreeClustersSpecified = true;
            XmlFsType.Type       = "MFS";
            XmlFsType.VolumeName = volMDB.drVN;

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

            var sb = new StringBuilder();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            information = sb.ToString();
        }
Beispiel #17
0
        public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset)
        {
            partitions = new List <Partition>();

            ulong sbSector;

            // RISC OS always checks for the partition on 0. Afaik no emulator chains it.
            if (sectorOffset != 0)
            {
                return(false);
            }

            if (imagePlugin.Info.SectorSize > ADFS_SB_POS)
            {
                sbSector = 0;
            }
            else
            {
                sbSector = ADFS_SB_POS / imagePlugin.Info.SectorSize;
            }

            byte[] sector = imagePlugin.ReadSector(sbSector);

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

            IntPtr bbPtr = Marshal.AllocHGlobal(512);

            Marshal.Copy(sector, 0, bbPtr, 512);
            AcornBootBlock bootBlock = (AcornBootBlock)Marshal.PtrToStructure(bbPtr, typeof(AcornBootBlock));

            Marshal.FreeHGlobal(bbPtr);

            int checksum = 0;

            for (int i = 0; i < 0x1FF; i++)
            {
                checksum = (checksum & 0xFF) + (checksum >> 8) + sector[i];
            }

            int heads     = bootBlock.discRecord.heads + ((bootBlock.discRecord.lowsector >> 6) & 1);
            int secCyl    = bootBlock.discRecord.spt * heads;
            int mapSector = bootBlock.startCylinder * secCyl;

            if ((ulong)mapSector >= imagePlugin.Info.Sectors)
            {
                return(false);
            }

            byte[] map = imagePlugin.ReadSector((ulong)mapSector);

            ulong counter = 0;

            if (checksum == bootBlock.checksum)
            {
                Partition part = new Partition
                {
                    Size   = (ulong)bootBlock.discRecord.disc_size_high * 0x100000000 + bootBlock.discRecord.disc_size,
                    Length =
                        ((ulong)bootBlock.discRecord.disc_size_high * 0x100000000 + bootBlock.discRecord.disc_size) /
                        imagePlugin.Info.SectorSize,
                    Type = "ADFS",
                    Name = StringHandlers.CToString(bootBlock.discRecord.disc_name, Encoding.GetEncoding("iso-8859-1"))
                };
                if (part.Size > 0)
                {
                    partitions.Add(part);
                    counter++;
                }
            }

            switch (bootBlock.flags & TYPE_MASK)
            {
            case TYPE_LINUX:
            {
                IntPtr tablePtr = Marshal.AllocHGlobal(512);
                Marshal.Copy(map, 0, tablePtr, 512);
                LinuxTable table = (LinuxTable)Marshal.PtrToStructure(tablePtr, typeof(LinuxTable));
                Marshal.FreeHGlobal(tablePtr);

                foreach (LinuxEntry entry in table.entries)
                {
                    Partition part = new Partition
                    {
                        Start    = (ulong)(mapSector + entry.start),
                        Size     = entry.size,
                        Length   = (ulong)(entry.size * sector.Length),
                        Sequence = counter,
                        Scheme   = Name
                    };
                    part.Offset = part.Start * (ulong)sector.Length;
                    if (entry.magic != LINUX_MAGIC && entry.magic != SWAP_MAGIC)
                    {
                        continue;
                    }

                    partitions.Add(part);
                    counter++;
                }

                break;
            }

            case TYPE_RISCIX_MFM:
            case TYPE_RISCIX_SCSI:
            {
                IntPtr tablePtr = Marshal.AllocHGlobal(512);
                Marshal.Copy(map, 0, tablePtr, 512);
                RiscIxTable table = (RiscIxTable)Marshal.PtrToStructure(tablePtr, typeof(RiscIxTable));
                Marshal.FreeHGlobal(tablePtr);

                if (table.magic == RISCIX_MAGIC)
                {
                    foreach (RiscIxEntry entry in table.partitions)
                    {
                        Partition part = new Partition
                        {
                            Start    = (ulong)(mapSector + entry.start),
                            Size     = entry.length,
                            Length   = (ulong)(entry.length * sector.Length),
                            Name     = StringHandlers.CToString(entry.name, Encoding.GetEncoding("iso-8859-1")),
                            Sequence = counter,
                            Scheme   = Name
                        };
                        part.Offset = part.Start * (ulong)sector.Length;
                        if (entry.length <= 0)
                        {
                            continue;
                        }

                        partitions.Add(part);
                        counter++;
                    }
                }

                break;
            }
            }

            return(partitions.Count != 0);
        }
Beispiel #18
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";

            var sb = new StringBuilder();

            var besb = new SuperBlock();

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

            bool littleEndian;

            besb.magic1 = BigEndianBitConverter.ToUInt32(sbSector, 0x20);

            if (besb.magic1 == BEFS_MAGIC1 ||
                besb.magic1 == BEFS_CIGAM1) // Magic is at offset
            {
                littleEndian = besb.magic1 == BEFS_CIGAM1;
            }
            else
            {
                sbSector    = imagePlugin.ReadSector(1 + partition.Start);
                besb.magic1 = BigEndianBitConverter.ToUInt32(sbSector, 0x20);

                if (besb.magic1 == BEFS_MAGIC1 ||
                    besb.magic1 == BEFS_CIGAM1) // There is a boot sector
                {
                    littleEndian = besb.magic1 == BEFS_CIGAM1;
                }
                else if (sbSector.Length >= 0x400)
                {
                    byte[] temp = imagePlugin.ReadSector(0 + partition.Start);
                    besb.magic1 = BigEndianBitConverter.ToUInt32(temp, 0x220);

                    if (besb.magic1 == BEFS_MAGIC1 ||
                        besb.magic1 == BEFS_CIGAM1) // There is a boot sector
                    {
                        littleEndian = besb.magic1 == BEFS_CIGAM1;
                        sbSector     = new byte[0x200];
                        Array.Copy(temp, 0x200, sbSector, 0, 0x200);
                    }
                    else
                    {
                        return;
                    }
                }
                else
                {
                    return;
                }
            }

            besb = littleEndian ? Marshal.ByteArrayToStructureLittleEndian <SuperBlock>(sbSector)
                       : Marshal.ByteArrayToStructureBigEndian <SuperBlock>(sbSector);

            sb.AppendLine(littleEndian ? "Little-endian BeFS" : "Big-endian BeFS");

            if (besb.magic1 != BEFS_MAGIC1 ||
                besb.fs_byte_order != BEFS_ENDIAN ||
                besb.magic2 != BEFS_MAGIC2 ||
                besb.magic3 != BEFS_MAGIC3 ||
                besb.root_dir_len != 1 ||
                besb.indices_len != 1 ||
                1 << (int)besb.block_shift != besb.block_size)
            {
                sb.AppendLine("Superblock seems corrupt, following information may be incorrect");
                sb.AppendFormat("Magic 1: 0x{0:X8} (Should be 0x42465331)", besb.magic1).AppendLine();
                sb.AppendFormat("Magic 2: 0x{0:X8} (Should be 0xDD121031)", besb.magic2).AppendLine();
                sb.AppendFormat("Magic 3: 0x{0:X8} (Should be 0x15B6830E)", besb.magic3).AppendLine();

                sb.AppendFormat("Filesystem endianness: 0x{0:X8} (Should be 0x42494745)", besb.fs_byte_order).
                AppendLine();

                sb.AppendFormat("Root folder's i-node size: {0} blocks (Should be 1)", besb.root_dir_len).AppendLine();
                sb.AppendFormat("Indices' i-node size: {0} blocks (Should be 1)", besb.indices_len).AppendLine();

                sb.AppendFormat("1 << block_shift == block_size => 1 << {0} == {1} (Should be {2})", besb.block_shift,
                                1 << (int)besb.block_shift, besb.block_size).AppendLine();
            }

            switch (besb.flags)
            {
            case BEFS_CLEAN:
                sb.AppendLine(besb.log_start == besb.log_end ? "Filesystem is clean" : "Filesystem is dirty");

                break;

            case BEFS_DIRTY:
                sb.AppendLine("Filesystem is dirty");

                break;

            default:
                sb.AppendFormat("Unknown flags: {0:X8}", besb.flags).AppendLine();

                break;
            }

            sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(besb.name, Encoding)).AppendLine();
            sb.AppendFormat("{0} bytes per block", besb.block_size).AppendLine();

            sb.AppendFormat("{0} blocks in volume ({1} bytes)", besb.num_blocks, besb.num_blocks * besb.block_size).
            AppendLine();

            sb.AppendFormat("{0} used blocks ({1} bytes)", besb.used_blocks, besb.used_blocks * besb.block_size).
            AppendLine();

            sb.AppendFormat("{0} bytes per i-node", besb.inode_size).AppendLine();

            sb.AppendFormat("{0} blocks per allocation group ({1} bytes)", besb.blocks_per_ag,
                            besb.blocks_per_ag * besb.block_size).AppendLine();

            sb.AppendFormat("{0} allocation groups in volume", besb.num_ags).AppendLine();

            sb.AppendFormat("Journal resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)",
                            besb.log_blocks_start, besb.log_blocks_ag, besb.log_blocks_len,
                            besb.log_blocks_len * besb.block_size).AppendLine();

            sb.AppendFormat("Journal starts in byte {0} and ends in byte {1}", besb.log_start, besb.log_end).
            AppendLine();

            sb.
            AppendFormat("Root folder's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)",
                         besb.root_dir_start, besb.root_dir_ag, besb.root_dir_len,
                         besb.root_dir_len * besb.block_size).AppendLine();

            sb.
            AppendFormat("Indices' i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)",
                         besb.indices_start, besb.indices_ag, besb.indices_len, besb.indices_len * besb.block_size).
            AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Clusters              = (ulong)besb.num_blocks,
                ClusterSize           = besb.block_size,
                Dirty                 = besb.flags == BEFS_DIRTY,
                FreeClusters          = (ulong)(besb.num_blocks - besb.used_blocks),
                FreeClustersSpecified = true,
                Type       = "BeFS",
                VolumeName = StringHandlers.CToString(besb.name, Encoding)
            };
        }
Beispiel #19
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";
            byte[] sector = imagePlugin.ReadSector(partition.Start + 1);
            if (sector.Length < 512)
            {
                return;
            }

            QNX4_Superblock qnxSb = Marshal.ByteArrayToStructureLittleEndian <QNX4_Superblock>(sector);

            // Too much useless information

            /*
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_fname = {0}", CurrentEncoding.GetString(qnxSb.rootDir.di_fname));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_size = {0}", qnxSb.rootDir.di_size);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_first_xtnt.block = {0}", qnxSb.rootDir.di_first_xtnt.block);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_first_xtnt.length = {0}", qnxSb.rootDir.di_first_xtnt.length);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_xblk = {0}", qnxSb.rootDir.di_xblk);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_ftime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_mtime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_atime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_ctime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_num_xtnts = {0}", qnxSb.rootDir.di_num_xtnts);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_mode = {0}", Convert.ToString(qnxSb.rootDir.di_mode, 8));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_uid = {0}", qnxSb.rootDir.di_uid);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_gid = {0}", qnxSb.rootDir.di_gid);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_nlink = {0}", qnxSb.rootDir.di_nlink);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_zero = {0}", qnxSb.rootDir.di_zero);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_type = {0}", qnxSb.rootDir.di_type);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_status = {0}", qnxSb.rootDir.di_status);
             *
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_fname = {0}", CurrentEncoding.GetString(qnxSb.inode.di_fname));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_size = {0}", qnxSb.inode.di_size);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_first_xtnt.block = {0}", qnxSb.inode.di_first_xtnt.block);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_first_xtnt.length = {0}", qnxSb.inode.di_first_xtnt.length);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_xblk = {0}", qnxSb.inode.di_xblk);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_ftime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_mtime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_atime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_ctime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_num_xtnts = {0}", qnxSb.inode.di_num_xtnts);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_mode = {0}", Convert.ToString(qnxSb.inode.di_mode, 8));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_uid = {0}", qnxSb.inode.di_uid);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_gid = {0}", qnxSb.inode.di_gid);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_nlink = {0}", qnxSb.inode.di_nlink);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_zero = {0}", qnxSb.inode.di_zero);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_type = {0}", qnxSb.inode.di_type);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_status = {0}", qnxSb.inode.di_status);
             *
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_fname = {0}", CurrentEncoding.GetString(qnxSb.boot.di_fname));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_size = {0}", qnxSb.boot.di_size);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_first_xtnt.block = {0}", qnxSb.boot.di_first_xtnt.block);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_first_xtnt.length = {0}", qnxSb.boot.di_first_xtnt.length);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_xblk = {0}", qnxSb.boot.di_xblk);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_ftime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_mtime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_atime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_ctime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_num_xtnts = {0}", qnxSb.boot.di_num_xtnts);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_mode = {0}", Convert.ToString(qnxSb.boot.di_mode, 8));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_uid = {0}", qnxSb.boot.di_uid);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_gid = {0}", qnxSb.boot.di_gid);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_nlink = {0}", qnxSb.boot.di_nlink);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_zero = {0}", qnxSb.boot.di_zero);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_type = {0}", qnxSb.boot.di_type);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_status = {0}", qnxSb.boot.di_status);
             *
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_fname = {0}", CurrentEncoding.GetString(qnxSb.altBoot.di_fname));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_size = {0}", qnxSb.altBoot.di_size);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_first_xtnt.block = {0}", qnxSb.altBoot.di_first_xtnt.block);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_first_xtnt.length = {0}", qnxSb.altBoot.di_first_xtnt.length);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_xblk = {0}", qnxSb.altBoot.di_xblk);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_ftime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_mtime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_atime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_ctime));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_num_xtnts = {0}", qnxSb.altBoot.di_num_xtnts);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_mode = {0}", Convert.ToString(qnxSb.altBoot.di_mode, 8));
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_uid = {0}", qnxSb.altBoot.di_uid);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_gid = {0}", qnxSb.altBoot.di_gid);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_nlink = {0}", qnxSb.altBoot.di_nlink);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_zero = {0}", qnxSb.altBoot.di_zero);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_type = {0}", qnxSb.altBoot.di_type);
             * DicConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_status = {0}", qnxSb.altBoot.di_status);
             */

            information =
                $"QNX4 filesystem\nCreated on {DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_ftime)}\n";

            XmlFsType = new FileSystemType
            {
                Type                      = "QNX4 filesystem",
                Clusters                  = partition.Length,
                ClusterSize               = 512,
                CreationDate              = DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_ftime),
                CreationDateSpecified     = true,
                ModificationDate          = DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_mtime),
                ModificationDateSpecified = true
            };
            XmlFsType.Bootable |= qnxSb.boot.di_size != 0 || qnxSb.altBoot.di_size != 0;
        }
Beispiel #20
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("IBM437");
            information = "";

            var 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 = StringHandlers.SpacePaddedToString(fat32Bpb.volume_label, Encoding);
                    XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", "");

                    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;

                for (int i = 0; i < bpbSector.Length; i += 2)
                {
                    sum += BigEndianBitConverter.ToUInt16(bpbSector, i);
                }

                // TODO: Check this
                if (sum == 0x1234)
                {
                    XmlFsType.Bootable = true;
                    var 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 = StringHandlers.SpacePaddedToString(fakeBpb.volume_label, Encoding);
                        XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", "");
                        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)
                {
                    var rootMs = new MemoryStream();

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

                    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();
        }
Beispiel #21
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = new LisaRoman();
            information = "";
            var sb = new StringBuilder();

            try
            {
                if (imagePlugin.Info.ReadableSectorTags?.Contains(SectorTagType.AppleSectorTag) != true)
                {
                    return;
                }

                // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors
                if (imagePlugin.Info.Sectors < 800)
                {
                    return;
                }

                int beforeMddf = -1;

                // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors
                for (int i = 0; i < 100; i++)
                {
                    DecodeTag(imagePlugin.ReadSectorTag((ulong)i, SectorTagType.AppleSectorTag),
                              out LisaTag.PriamTag searchTag);

                    AaruConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId);

                    if (beforeMddf == -1 &&
                        searchTag.FileId == FILEID_LOADER_SIGNED)
                    {
                        beforeMddf = i - 1;
                    }

                    if (searchTag.FileId != FILEID_MDDF)
                    {
                        continue;
                    }

                    byte[] sector   = imagePlugin.ReadSector((ulong)i);
                    var    infoMddf = new MDDF();
                    byte[] pString  = new byte[33];

                    infoMddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00);
                    infoMddf.volid     = BigEndianBitConverter.ToUInt64(sector, 0x02);
                    infoMddf.volnum    = BigEndianBitConverter.ToUInt16(sector, 0x0A);
                    Array.Copy(sector, 0x0C, pString, 0, 33);
                    infoMddf.volname  = StringHandlers.PascalToString(pString, Encoding);
                    infoMddf.unknown1 = sector[0x2D];
                    Array.Copy(sector, 0x2E, pString, 0, 33);

                    // Prevent garbage
                    infoMddf.password       = pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : "";
                    infoMddf.unknown2       = sector[0x4F];
                    infoMddf.machine_id     = BigEndianBitConverter.ToUInt32(sector, 0x50);
                    infoMddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54);
                    uint lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58);
                    infoMddf.dtvc                         = DateHandlers.LisaToDateTime(lisaTime);
                    lisaTime                              = BigEndianBitConverter.ToUInt32(sector, 0x5C);
                    infoMddf.dtcc                         = DateHandlers.LisaToDateTime(lisaTime);
                    lisaTime                              = BigEndianBitConverter.ToUInt32(sector, 0x60);
                    infoMddf.dtvb                         = DateHandlers.LisaToDateTime(lisaTime);
                    lisaTime                              = BigEndianBitConverter.ToUInt32(sector, 0x64);
                    infoMddf.dtvs                         = DateHandlers.LisaToDateTime(lisaTime);
                    infoMddf.unknown3                     = BigEndianBitConverter.ToUInt32(sector, 0x68);
                    infoMddf.mddf_block                   = BigEndianBitConverter.ToUInt32(sector, 0x6C);
                    infoMddf.volsize_minus_one            = BigEndianBitConverter.ToUInt32(sector, 0x70);
                    infoMddf.volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74);
                    infoMddf.vol_size                     = BigEndianBitConverter.ToUInt32(sector, 0x78);
                    infoMddf.blocksize                    = BigEndianBitConverter.ToUInt16(sector, 0x7C);
                    infoMddf.datasize                     = BigEndianBitConverter.ToUInt16(sector, 0x7E);
                    infoMddf.unknown4                     = BigEndianBitConverter.ToUInt16(sector, 0x80);
                    infoMddf.unknown5                     = BigEndianBitConverter.ToUInt32(sector, 0x82);
                    infoMddf.unknown6                     = BigEndianBitConverter.ToUInt32(sector, 0x86);
                    infoMddf.clustersize                  = BigEndianBitConverter.ToUInt16(sector, 0x8A);
                    infoMddf.fs_size                      = BigEndianBitConverter.ToUInt32(sector, 0x8C);
                    infoMddf.unknown7                     = BigEndianBitConverter.ToUInt32(sector, 0x90);
                    infoMddf.srec_ptr                     = BigEndianBitConverter.ToUInt32(sector, 0x94);
                    infoMddf.unknown9                     = BigEndianBitConverter.ToUInt16(sector, 0x98);
                    infoMddf.srec_len                     = BigEndianBitConverter.ToUInt16(sector, 0x9A);
                    infoMddf.unknown10                    = BigEndianBitConverter.ToUInt32(sector, 0x9C);
                    infoMddf.unknown11                    = BigEndianBitConverter.ToUInt32(sector, 0xA0);
                    infoMddf.unknown12                    = BigEndianBitConverter.ToUInt32(sector, 0xA4);
                    infoMddf.unknown13                    = BigEndianBitConverter.ToUInt32(sector, 0xA8);
                    infoMddf.unknown14                    = BigEndianBitConverter.ToUInt32(sector, 0xAC);
                    infoMddf.filecount                    = BigEndianBitConverter.ToUInt16(sector, 0xB0);
                    infoMddf.unknown15                    = BigEndianBitConverter.ToUInt32(sector, 0xB2);
                    infoMddf.unknown16                    = BigEndianBitConverter.ToUInt32(sector, 0xB6);
                    infoMddf.freecount                    = BigEndianBitConverter.ToUInt32(sector, 0xBA);
                    infoMddf.unknown17                    = BigEndianBitConverter.ToUInt16(sector, 0xBE);
                    infoMddf.unknown18                    = BigEndianBitConverter.ToUInt32(sector, 0xC0);
                    infoMddf.overmount_stamp              = BigEndianBitConverter.ToUInt64(sector, 0xC4);
                    infoMddf.serialization                = BigEndianBitConverter.ToUInt32(sector, 0xCC);
                    infoMddf.unknown19                    = BigEndianBitConverter.ToUInt32(sector, 0xD0);
                    infoMddf.unknown_timestamp            = BigEndianBitConverter.ToUInt32(sector, 0xD4);
                    infoMddf.unknown20                    = BigEndianBitConverter.ToUInt32(sector, 0xD8);
                    infoMddf.unknown21                    = BigEndianBitConverter.ToUInt32(sector, 0xDC);
                    infoMddf.unknown22                    = BigEndianBitConverter.ToUInt32(sector, 0xE0);
                    infoMddf.unknown23                    = BigEndianBitConverter.ToUInt32(sector, 0xE4);
                    infoMddf.unknown24                    = BigEndianBitConverter.ToUInt32(sector, 0xE8);
                    infoMddf.unknown25                    = BigEndianBitConverter.ToUInt32(sector, 0xEC);
                    infoMddf.unknown26                    = BigEndianBitConverter.ToUInt32(sector, 0xF0);
                    infoMddf.unknown27                    = BigEndianBitConverter.ToUInt32(sector, 0xF4);
                    infoMddf.unknown28                    = BigEndianBitConverter.ToUInt32(sector, 0xF8);
                    infoMddf.unknown29                    = BigEndianBitConverter.ToUInt32(sector, 0xFC);
                    infoMddf.unknown30                    = BigEndianBitConverter.ToUInt32(sector, 0x100);
                    infoMddf.unknown31                    = BigEndianBitConverter.ToUInt32(sector, 0x104);
                    infoMddf.unknown32                    = BigEndianBitConverter.ToUInt32(sector, 0x108);
                    infoMddf.unknown33                    = BigEndianBitConverter.ToUInt32(sector, 0x10C);
                    infoMddf.unknown34                    = BigEndianBitConverter.ToUInt32(sector, 0x110);
                    infoMddf.unknown35                    = BigEndianBitConverter.ToUInt32(sector, 0x114);
                    infoMddf.backup_volid                 = BigEndianBitConverter.ToUInt64(sector, 0x118);
                    infoMddf.label_size                   = BigEndianBitConverter.ToUInt16(sector, 0x120);
                    infoMddf.fs_overhead                  = BigEndianBitConverter.ToUInt16(sector, 0x122);
                    infoMddf.result_scavenge              = BigEndianBitConverter.ToUInt16(sector, 0x124);
                    infoMddf.boot_code                    = BigEndianBitConverter.ToUInt16(sector, 0x126);
                    infoMddf.boot_environ                 = BigEndianBitConverter.ToUInt16(sector, 0x6C);
                    infoMddf.unknown36                    = BigEndianBitConverter.ToUInt32(sector, 0x12A);
                    infoMddf.unknown37                    = BigEndianBitConverter.ToUInt32(sector, 0x12E);
                    infoMddf.unknown38                    = BigEndianBitConverter.ToUInt32(sector, 0x132);
                    infoMddf.vol_sequence                 = BigEndianBitConverter.ToUInt16(sector, 0x136);
                    infoMddf.vol_left_mounted             = sector[0x138];

                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown1 = 0x{0:X2} ({0})", infoMddf.unknown1);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown2 = 0x{0:X2} ({0})", infoMddf.unknown2);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown3 = 0x{0:X8} ({0})", infoMddf.unknown3);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown4 = 0x{0:X4} ({0})", infoMddf.unknown4);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown5 = 0x{0:X8} ({0})", infoMddf.unknown5);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown6 = 0x{0:X8} ({0})", infoMddf.unknown6);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown7 = 0x{0:X8} ({0})", infoMddf.unknown7);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown9 = 0x{0:X4} ({0})", infoMddf.unknown9);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown10 = 0x{0:X8} ({0})", infoMddf.unknown10);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown11 = 0x{0:X8} ({0})", infoMddf.unknown11);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown12 = 0x{0:X8} ({0})", infoMddf.unknown12);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown13 = 0x{0:X8} ({0})", infoMddf.unknown13);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown14 = 0x{0:X8} ({0})", infoMddf.unknown14);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown15 = 0x{0:X8} ({0})", infoMddf.unknown15);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown16 = 0x{0:X8} ({0})", infoMddf.unknown16);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown17 = 0x{0:X4} ({0})", infoMddf.unknown17);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown18 = 0x{0:X8} ({0})", infoMddf.unknown18);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown19 = 0x{0:X8} ({0})", infoMddf.unknown19);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown20 = 0x{0:X8} ({0})", infoMddf.unknown20);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown21 = 0x{0:X8} ({0})", infoMddf.unknown21);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown22 = 0x{0:X8} ({0})", infoMddf.unknown22);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown23 = 0x{0:X8} ({0})", infoMddf.unknown23);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown24 = 0x{0:X8} ({0})", infoMddf.unknown24);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown25 = 0x{0:X8} ({0})", infoMddf.unknown25);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown26 = 0x{0:X8} ({0})", infoMddf.unknown26);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown27 = 0x{0:X8} ({0})", infoMddf.unknown27);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown28 = 0x{0:X8} ({0})", infoMddf.unknown28);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown29 = 0x{0:X8} ({0})", infoMddf.unknown29);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown30 = 0x{0:X8} ({0})", infoMddf.unknown30);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown31 = 0x{0:X8} ({0})", infoMddf.unknown31);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown32 = 0x{0:X8} ({0})", infoMddf.unknown32);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown33 = 0x{0:X8} ({0})", infoMddf.unknown33);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown34 = 0x{0:X8} ({0})", infoMddf.unknown34);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown35 = 0x{0:X8} ({0})", infoMddf.unknown35);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown36 = 0x{0:X8} ({0})", infoMddf.unknown36);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown37 = 0x{0:X8} ({0})", infoMddf.unknown37);
                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown38 = 0x{0:X8} ({0})", infoMddf.unknown38);

                    AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown_timestamp = 0x{0:X8} ({0}, {1})",
                                               infoMddf.unknown_timestamp,
                                               DateHandlers.LisaToDateTime(infoMddf.unknown_timestamp));

                    if (infoMddf.mddf_block != i - beforeMddf)
                    {
                        return;
                    }

                    if (infoMddf.vol_size > imagePlugin.Info.Sectors)
                    {
                        return;
                    }

                    if (infoMddf.vol_size - 1 != infoMddf.volsize_minus_one)
                    {
                        return;
                    }

                    if (infoMddf.vol_size - i - 1 != infoMddf.volsize_minus_mddf_minus_one - beforeMddf)
                    {
                        return;
                    }

                    if (infoMddf.datasize > infoMddf.blocksize)
                    {
                        return;
                    }

                    if (infoMddf.blocksize < imagePlugin.Info.SectorSize)
                    {
                        return;
                    }

                    if (infoMddf.datasize != imagePlugin.Info.SectorSize)
                    {
                        return;
                    }

                    switch (infoMddf.fsversion)
                    {
                    case LISA_V1:
                        sb.AppendLine("LisaFS v1");

                        break;

                    case LISA_V2:
                        sb.AppendLine("LisaFS v2");

                        break;

                    case LISA_V3:
                        sb.AppendLine("LisaFS v3");

                        break;

                    default:
                        sb.AppendFormat("Unknown LisaFS version {0}", infoMddf.fsversion).AppendLine();

                        break;
                    }

                    sb.AppendFormat("Volume name: \"{0}\"", infoMddf.volname).AppendLine();
                    sb.AppendFormat("Volume password: \"{0}\"", infoMddf.password).AppendLine();
                    sb.AppendFormat("Volume ID: 0x{0:X16}", infoMddf.volid).AppendLine();
                    sb.AppendFormat("Backup volume ID: 0x{0:X16}", infoMddf.backup_volid).AppendLine();

                    sb.AppendFormat("Master copy ID: 0x{0:X8}", infoMddf.master_copy_id).AppendLine();

                    sb.AppendFormat("Volume is number {0} of {1}", infoMddf.volnum, infoMddf.vol_sequence).AppendLine();

                    sb.AppendFormat("Serial number of Lisa computer that created this volume: {0}",
                                    infoMddf.machine_id).AppendLine();

                    sb.AppendFormat("Serial number of Lisa computer that can use this volume's software {0}",
                                    infoMddf.serialization).AppendLine();

                    sb.AppendFormat("Volume created on {0}", infoMddf.dtvc).AppendLine();
                    sb.AppendFormat("Some timestamp, says {0}", infoMddf.dtcc).AppendLine();
                    sb.AppendFormat("Volume backed up on {0}", infoMddf.dtvb).AppendLine();
                    sb.AppendFormat("Volume scavenged on {0}", infoMddf.dtvs).AppendLine();
                    sb.AppendFormat("MDDF is in block {0}", infoMddf.mddf_block + beforeMddf).AppendLine();
                    sb.AppendFormat("There are {0} reserved blocks before volume", beforeMddf).AppendLine();
                    sb.AppendFormat("{0} blocks minus one", infoMddf.volsize_minus_one).AppendLine();

                    sb.AppendFormat("{0} blocks minus one minus MDDF offset", infoMddf.volsize_minus_mddf_minus_one).
                    AppendLine();

                    sb.AppendFormat("{0} blocks in volume", infoMddf.vol_size).AppendLine();
                    sb.AppendFormat("{0} bytes per sector (uncooked)", infoMddf.blocksize).AppendLine();
                    sb.AppendFormat("{0} bytes per sector", infoMddf.datasize).AppendLine();
                    sb.AppendFormat("{0} blocks per cluster", infoMddf.clustersize).AppendLine();
                    sb.AppendFormat("{0} blocks in filesystem", infoMddf.fs_size).AppendLine();
                    sb.AppendFormat("{0} files in volume", infoMddf.filecount).AppendLine();
                    sb.AppendFormat("{0} blocks free", infoMddf.freecount).AppendLine();
                    sb.AppendFormat("{0} bytes in LisaInfo", infoMddf.label_size).AppendLine();
                    sb.AppendFormat("Filesystem overhead: {0}", infoMddf.fs_overhead).AppendLine();
                    sb.AppendFormat("Scavenger result code: 0x{0:X8}", infoMddf.result_scavenge).AppendLine();
                    sb.AppendFormat("Boot code: 0x{0:X8}", infoMddf.boot_code).AppendLine();
                    sb.AppendFormat("Boot environment:  0x{0:X8}", infoMddf.boot_environ).AppendLine();
                    sb.AppendFormat("Overmount stamp: 0x{0:X16}", infoMddf.overmount_stamp).AppendLine();

                    sb.AppendFormat("S-Records start at {0} and spans for {1} blocks",
                                    infoMddf.srec_ptr + infoMddf.mddf_block + beforeMddf, infoMddf.srec_len).
                    AppendLine();

                    sb.AppendLine(infoMddf.vol_left_mounted == 0 ? "Volume is clean" : "Volume is dirty");

                    information = sb.ToString();

                    XmlFsType = new FileSystemType();

                    if (DateTime.Compare(infoMddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0)
                    {
                        XmlFsType.BackupDate          = infoMddf.dtvb;
                        XmlFsType.BackupDateSpecified = true;
                    }

                    XmlFsType.Clusters    = infoMddf.vol_size;
                    XmlFsType.ClusterSize = (uint)(infoMddf.clustersize * infoMddf.datasize);

                    if (DateTime.Compare(infoMddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0)
                    {
                        XmlFsType.CreationDate          = infoMddf.dtvc;
                        XmlFsType.CreationDateSpecified = true;
                    }

                    XmlFsType.Dirty                 = infoMddf.vol_left_mounted != 0;
                    XmlFsType.Files                 = infoMddf.filecount;
                    XmlFsType.FilesSpecified        = true;
                    XmlFsType.FreeClusters          = infoMddf.freecount;
                    XmlFsType.FreeClustersSpecified = true;
                    XmlFsType.Type         = "LisaFS";
                    XmlFsType.VolumeName   = infoMddf.volname;
                    XmlFsType.VolumeSerial = $"{infoMddf.volid:X16}";

                    return;
                }
            }
            catch (Exception ex)
            {
                AaruConsole.ErrorWriteLine("Exception {0}, {1}, {2}", ex.Message, ex.InnerException, ex.StackTrace);
            }
        }
Beispiel #22
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            if (2 + partition.Start >= partition.End)
            {
                return(false);
            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                            break;
                        }
                    }

                    if (!validRootDir)
                    {
                        break;
                    }
                }

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

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

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

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

            ulong fat2SectorNo = 0;

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

                break;

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

                break;

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

                break;

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

                break;

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

                fat2SectorNo = 2;

                break;
            }

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

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

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

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

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

            return(fatId == fat2Sector[0]);
        }
Beispiel #23
0
        public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset)
        {
            partitions = new List <Partition>();
            uint run = (MAX_LABEL_SIZE + labelOffsets.Last()) / imagePlugin.Info.SectorSize;

            if ((MAX_LABEL_SIZE + labelOffsets.Last()) % imagePlugin.Info.SectorSize > 0)
            {
                run++;
            }

            DiskLabel dl    = new DiskLabel();
            bool      found = false;

            foreach (ulong location in labelLocations)
            {
                if (location + run + sectorOffset >= imagePlugin.Info.Sectors)
                {
                    return(false);
                }

                byte[] tmp = imagePlugin.ReadSectors(location + sectorOffset, run);
                foreach (uint offset in labelOffsets)
                {
                    byte[] sector = new byte[MAX_LABEL_SIZE];
                    Array.Copy(tmp, offset, sector, 0, MAX_LABEL_SIZE);
                    dl = GetDiskLabel(sector);
                    DicConsole.DebugWriteLine("BSD plugin",
                                              "dl.magic on sector {0} at offset {1} = 0x{2:X8} (expected 0x{3:X8})",
                                              location + sectorOffset, offset, dl.d_magic, DISKMAGIC);
                    if ((dl.d_magic != DISKMAGIC || dl.d_magic2 != DISKMAGIC) &&
                        (dl.d_magic != DISKCIGAM || dl.d_magic2 != DISKCIGAM))
                    {
                        continue;
                    }

                    found = true;
                    break;
                }

                if (found)
                {
                    break;
                }
            }

            if (!found)
            {
                return(false);
            }

            if (dl.d_magic == DISKCIGAM && dl.d_magic2 == DISKCIGAM)
            {
                dl = SwapDiskLabel(dl);
            }

            DicConsole.DebugWriteLine("BSD plugin", "dl.d_type = {0}", dl.d_type);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_subtype = {0}", dl.d_subtype);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_typename = {0}",
                                      StringHandlers.CToString(dl.d_typename));
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_packname = {0}",
                                      StringHandlers.CToString(dl.d_packname));
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_secsize = {0}", dl.d_secsize);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_nsectors = {0}", dl.d_nsectors);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_ntracks = {0}", dl.d_ntracks);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_ncylinders = {0}", dl.d_ncylinders);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_secpercyl = {0}", dl.d_secpercyl);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_secperunit = {0}", dl.d_secperunit);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_sparespertrack = {0}", dl.d_sparespertrack);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_sparespercyl = {0}", dl.d_sparespercyl);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_acylinders = {0}", dl.d_acylinders);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_rpm = {0}", dl.d_rpm);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_interleave = {0}", dl.d_interleave);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_trackskew = {0}", dl.d_trackskew);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_cylskeew = {0}", dl.d_cylskeew);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_headswitch = {0}", dl.d_headswitch);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_trkseek = {0}", dl.d_trkseek);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_flags = {0}", dl.d_flags);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[0] = {0}", dl.d_drivedata[0]);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[1] = {0}", dl.d_drivedata[1]);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[2] = {0}", dl.d_drivedata[2]);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[3] = {0}", dl.d_drivedata[3]);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[4] = {0}", dl.d_drivedata[4]);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[0] = {0}", dl.d_spare[0]);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[1] = {0}", dl.d_spare[1]);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[2] = {0}", dl.d_spare[2]);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[3] = {0}", dl.d_spare[3]);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[4] = {0}", dl.d_spare[4]);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_magic2 = 0x{0:X8}", dl.d_magic2);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_checksum = 0x{0:X8}", dl.d_checksum);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_npartitions = {0}", dl.d_npartitions);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_bbsize = {0}", dl.d_bbsize);
            DicConsole.DebugWriteLine("BSD plugin", "dl.d_sbsize = {0}", dl.d_sbsize);

            ulong counter         = 0;
            bool  addSectorOffset = false;

            for (int i = 0; i < dl.d_npartitions && i < 22; i++)
            {
                DicConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_offset = {0}",
                                          dl.d_partitions[i].p_offset);
                DicConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_size = {0}", dl.d_partitions[i].p_size);
                DicConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_fstype = {0} ({1})",
                                          dl.d_partitions[i].p_fstype, fsTypeToString(dl.d_partitions[i].p_fstype));
                Partition part = new Partition
                {
                    Start    = dl.d_partitions[i].p_offset * dl.d_secsize / imagePlugin.Info.SectorSize,
                    Offset   = dl.d_partitions[i].p_offset * dl.d_secsize,
                    Length   = dl.d_partitions[i].p_size * dl.d_secsize / imagePlugin.Info.SectorSize,
                    Size     = dl.d_partitions[i].p_size * dl.d_secsize,
                    Type     = fsTypeToString(dl.d_partitions[i].p_fstype),
                    Sequence = counter,
                    Scheme   = Name
                };
                if (dl.d_partitions[i].p_fstype == fsType.Unused)
                {
                    continue;
                }

                // Crude and dirty way to know if the disklabel is relative to its parent partition...
                if (dl.d_partitions[i].p_offset < sectorOffset && !addSectorOffset)
                {
                    addSectorOffset = true;
                }

                if (addSectorOffset)
                {
                    part.Start  += sectorOffset;
                    part.Offset += sectorOffset * imagePlugin.Info.SectorSize;
                }

                DicConsole.DebugWriteLine("BSD plugin", "part.start = {0}", part.Start);
                DicConsole.DebugWriteLine("BSD plugin", "Adding it...");
                partitions.Add(part);
                counter++;
            }

            return(partitions.Count > 0);
        }
Beispiel #24
0
        public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset)
        {
            partitions = new List <Partition>();
            Partition[] parts;

            if (sectorOffset != 0)
            {
                return(false);
            }

            switch (imagePlugin.Info.MediaType)
            {
            case MediaType.RA60:
                parts = RA60;

                break;

            case MediaType.RA80:
                parts = RA80;

                break;

            case MediaType.RA81:
                parts = RA81;

                break;

            case MediaType.RC25:
                parts = RC25;

                break;

            case MediaType.RD31:
                parts = RD31;

                break;

            case MediaType.RD32:
                parts = RD32;

                break;

            case MediaType.RD51:
                parts = RD51;

                break;

            case MediaType.RD52:
                parts = RD52;

                break;

            case MediaType.RD53:
                parts = RD53;

                break;

            case MediaType.RD54:
                parts = RD54;

                break;

            case MediaType.RK06:
                parts = RK06;

                break;

            case MediaType.RK07:
                parts = RK07;

                break;

            case MediaType.RM02:
            case MediaType.RM03:
                parts = RM02;

                break;

            case MediaType.RM05:
                parts = RM05;

                break;

            case MediaType.RP02:
                parts = RP02;

                break;

            case MediaType.RP03:
                parts = RP03;

                break;

            case MediaType.RP04:
            case MediaType.RP05:
                parts = RP04;

                break;

            case MediaType.RP06:
                parts = RP06;

                break;

            default: return(false);
            }

            for (int i = 0; i < parts.Length; i++)
            {
                parts[i].Scheme = "";
            }

            partitions = parts.ToList();

            return(partitions.Count > 0);
        }
Beispiel #25
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding = encoding ?? new Apple2();
            StringBuilder sbInformation = new StringBuilder();

            information = "";
            multiplier  = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1);

            if (imagePlugin.Info.Sectors < 3)
            {
                return;
            }

            // Blocks 0 and 1 are boot code
            byte[] volBlock = imagePlugin.ReadSectors(multiplier * 2 + partition.Start, multiplier);

            PascalVolumeEntry volEntry = new PascalVolumeEntry();

            // On Apple //, it's little endian
            BigEndianBitConverter.IsLittleEndian =
                multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian;

            volEntry.FirstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00);
            volEntry.LastBlock  = BigEndianBitConverter.ToInt16(volBlock, 0x02);
            volEntry.EntryType  = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04);
            volEntry.VolumeName = new byte[8];
            Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8);
            volEntry.Blocks   = BigEndianBitConverter.ToInt16(volBlock, 0x0E);
            volEntry.Files    = BigEndianBitConverter.ToInt16(volBlock, 0x10);
            volEntry.Dummy    = BigEndianBitConverter.ToInt16(volBlock, 0x12);
            volEntry.LastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14);
            volEntry.Tail     = BigEndianBitConverter.ToInt32(volBlock, 0x16);

            // First block is always 0 (even is it's sector 2)
            if (volEntry.FirstBlock != 0)
            {
                return;
            }

            // Last volume record block must be after first block, and before end of device
            if (volEntry.LastBlock <= volEntry.FirstBlock ||
                (ulong)volEntry.LastBlock > imagePlugin.Info.Sectors / multiplier - 2)
            {
                return;
            }

            // Volume record entry type must be volume or secure
            if (volEntry.EntryType != PascalFileKind.Volume && volEntry.EntryType != PascalFileKind.Secure)
            {
                return;
            }

            // Volume name is max 7 characters
            if (volEntry.VolumeName[0] > 7)
            {
                return;
            }

            // Volume blocks is equal to volume sectors
            if (volEntry.Blocks < 0 || (ulong)volEntry.Blocks != imagePlugin.Info.Sectors / multiplier)
            {
                return;
            }

            // There can be not less than zero files
            if (volEntry.Files < 0)
            {
                return;
            }

            sbInformation.AppendFormat("Volume record spans from block {0} to block {1}", volEntry.FirstBlock,
                                       volEntry.LastBlock).AppendLine();
            sbInformation.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(volEntry.VolumeName, Encoding))
            .AppendLine();
            sbInformation.AppendFormat("Volume has {0} blocks", volEntry.Blocks).AppendLine();
            sbInformation.AppendFormat("Volume has {0} files", volEntry.Files).AppendLine();
            sbInformation
            .AppendFormat("Volume last booted at {0}", DateHandlers.UcsdPascalToDateTime(volEntry.LastBoot))
            .AppendLine();

            information = sbInformation.ToString();

            XmlFsType = new FileSystemType
            {
                Bootable =
                    !ArrayHelpers.ArrayIsNullOrEmpty(imagePlugin.ReadSectors(partition.Start, multiplier * 2)),
                Clusters       = (ulong)volEntry.Blocks,
                ClusterSize    = imagePlugin.Info.SectorSize,
                Files          = (ulong)volEntry.Files,
                FilesSpecified = true,
                Type           = "UCSD Pascal",
                VolumeName     = StringHandlers.PascalToString(volEntry.VolumeName, Encoding)
            };
        }
Beispiel #26
0
        internal static void DoVerify(VerifyOptions options)
        {
            DicConsole.DebugWriteLine("Verify command", "--debug={0}", options.Debug);
            DicConsole.DebugWriteLine("Verify command", "--verbose={0}", options.Verbose);
            DicConsole.DebugWriteLine("Verify command", "--input={0}", options.InputFile);
            DicConsole.DebugWriteLine("Verify command", "--verify-disc={0}", options.VerifyDisc);
            DicConsole.DebugWriteLine("Verify command", "--verify-sectors={0}", options.VerifySectors);

            FiltersList filtersList = new FiltersList();
            IFilter     inputFilter = filtersList.GetFilter(options.InputFile);

            if (inputFilter == null)
            {
                DicConsole.ErrorWriteLine("Cannot open specified file.");
                return;
            }

            IMediaImage inputFormat = ImageFormat.Detect(inputFilter);

            if (inputFormat == null)
            {
                DicConsole.ErrorWriteLine("Unable to recognize image format, not verifying");
                return;
            }

            inputFormat.Open(inputFilter);
            Core.Statistics.AddMediaFormat(inputFormat.Format);
            Core.Statistics.AddMedia(inputFormat.Info.MediaType, false);
            Core.Statistics.AddFilter(inputFilter.Name);

            bool?correctDisc    = null;
            long totalSectors   = 0;
            long errorSectors   = 0;
            long correctSectors = 0;
            long unknownSectors = 0;

            if (options.VerifyDisc)
            {
                DateTime startCheck      = DateTime.UtcNow;
                bool?    discCheckStatus = inputFormat.VerifyMediaImage();
                DateTime endCheck        = DateTime.UtcNow;

                TimeSpan checkTime = endCheck - startCheck;

                switch (discCheckStatus)
                {
                case true:
                    DicConsole.WriteLine("Disc image checksums are correct");
                    break;

                case false:
                    DicConsole.WriteLine("Disc image checksums are incorrect");
                    break;

                case null:
                    DicConsole.WriteLine("Disc image does not contain checksums");
                    break;
                }

                correctDisc = discCheckStatus;
                DicConsole.VerboseWriteLine("Checking disc image checksums took {0} seconds", checkTime.TotalSeconds);
            }

            if (options.VerifySectors)
            {
                bool formatHasTracks;
                try
                {
                    List <Track> inputTracks = inputFormat.Tracks;
                    formatHasTracks = inputTracks.Count > 0;
                }
                catch { formatHasTracks = false; }

                DateTime     startCheck;
                DateTime     endCheck;
                List <ulong> failingLbas = new List <ulong>();
                List <ulong> unknownLbas = new List <ulong>();

                if (formatHasTracks)
                {
                    List <Track> inputTracks      = inputFormat.Tracks;
                    ulong        currentSectorAll = 0;

                    startCheck = DateTime.UtcNow;
                    foreach (Track currentTrack in inputTracks)
                    {
                        ulong remainingSectors = currentTrack.TrackEndSector - currentTrack.TrackStartSector;
                        ulong currentSector    = 0;

                        while (remainingSectors > 0)
                        {
                            DicConsole.Write("\rChecking sector {0} of {1}, on track {2}", currentSectorAll,
                                             inputFormat.Info.Sectors, currentTrack.TrackSequence);

                            List <ulong> tempfailingLbas;
                            List <ulong> tempunknownLbas;

                            if (remainingSectors < 512)
                            {
                                inputFormat.VerifySectors(currentSector, (uint)remainingSectors,
                                                          currentTrack.TrackSequence, out tempfailingLbas,
                                                          out tempunknownLbas);
                            }
                            else
                            {
                                inputFormat.VerifySectors(currentSector, 512, currentTrack.TrackSequence,
                                                          out tempfailingLbas, out tempunknownLbas);
                            }

                            failingLbas.AddRange(tempfailingLbas);

                            unknownLbas.AddRange(tempunknownLbas);

                            if (remainingSectors < 512)
                            {
                                currentSector    += remainingSectors;
                                currentSectorAll += remainingSectors;
                                remainingSectors  = 0;
                            }
                            else
                            {
                                currentSector    += 512;
                                currentSectorAll += 512;
                                remainingSectors -= 512;
                            }
                        }
                    }

                    endCheck = DateTime.UtcNow;
                }
                else
                {
                    ulong remainingSectors = inputFormat.Info.Sectors;
                    ulong currentSector    = 0;

                    startCheck = DateTime.UtcNow;
                    while (remainingSectors > 0)
                    {
                        DicConsole.Write("\rChecking sector {0} of {1}", currentSector, inputFormat.Info.Sectors);

                        List <ulong> tempfailingLbas;
                        List <ulong> tempunknownLbas;

                        if (remainingSectors < 512)
                        {
                            inputFormat.VerifySectors(currentSector, (uint)remainingSectors,
                                                      out tempfailingLbas, out tempunknownLbas);
                        }
                        else
                        {
                            inputFormat.VerifySectors(currentSector, 512, out tempfailingLbas, out tempunknownLbas);
                        }

                        failingLbas.AddRange(tempfailingLbas);

                        unknownLbas.AddRange(tempunknownLbas);

                        if (remainingSectors < 512)
                        {
                            currentSector   += remainingSectors;
                            remainingSectors = 0;
                        }
                        else
                        {
                            currentSector    += 512;
                            remainingSectors -= 512;
                        }
                    }

                    endCheck = DateTime.UtcNow;
                }

                TimeSpan checkTime = endCheck - startCheck;

                DicConsole.Write("\r" + new string(' ', System.Console.WindowWidth - 1) + "\r");

                if (unknownSectors > 0)
                {
                    DicConsole.WriteLine("There is at least one sector that does not contain a checksum");
                }
                if (errorSectors > 0)
                {
                    DicConsole.WriteLine("There is at least one sector with incorrect checksum or errors");
                }
                if (unknownSectors == 0 && errorSectors == 0)
                {
                    DicConsole.WriteLine("All sector checksums are correct");
                }

                DicConsole.VerboseWriteLine("Checking sector checksums took {0} seconds", checkTime.TotalSeconds);

                if (options.Verbose)
                {
                    DicConsole.VerboseWriteLine("LBAs with error:");
                    if (failingLbas.Count == (int)inputFormat.Info.Sectors)
                    {
                        DicConsole.VerboseWriteLine("\tall sectors.");
                    }
                    else
                    {
                        foreach (ulong t in failingLbas)
                        {
                            DicConsole.VerboseWriteLine("\t{0}", t);
                        }
                    }

                    DicConsole.WriteLine("LBAs without checksum:");
                    if (unknownLbas.Count == (int)inputFormat.Info.Sectors)
                    {
                        DicConsole.VerboseWriteLine("\tall sectors.");
                    }
                    else
                    {
                        foreach (ulong t in unknownLbas)
                        {
                            DicConsole.VerboseWriteLine("\t{0}", t);
                        }
                    }
                }

                DicConsole.WriteLine("Total sectors........... {0}", inputFormat.Info.Sectors);
                DicConsole.WriteLine("Total errors............ {0}", failingLbas.Count);
                DicConsole.WriteLine("Total unknowns.......... {0}", unknownLbas.Count);
                DicConsole.WriteLine("Total errors+unknowns... {0}", failingLbas.Count + unknownLbas.Count);

                totalSectors   = (long)inputFormat.Info.Sectors;
                errorSectors   = failingLbas.Count;
                unknownSectors = unknownLbas.Count;
                correctSectors = totalSectors - errorSectors - unknownSectors;
            }

            Core.Statistics.AddCommand("verify");
            Core.Statistics.AddVerify(correctDisc, correctSectors, errorSectors, unknownSectors, totalSectors);
        }
Beispiel #27
0
        public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset)
        {
            partitions = new List <Partition>();

            byte[] sector = imagePlugin.ReadSector(sectorOffset);
            if (sector.Length < 512)
            {
                return(false);
            }

            SGILabel dvh = Marshal.ByteArrayToStructureBigEndian <SGILabel>(sector);

            for (int i = 0; i < dvh.volume.Length; i++)
            {
                dvh.volume[i] = (SGIVolume)Marshal.SwapStructureMembersEndian(dvh.volume[i]);
            }

            for (int i = 0; i < dvh.partitions.Length; i++)
            {
                dvh.partitions[i] = (SGIPartition)Marshal.SwapStructureMembersEndian(dvh.partitions[i]);
            }

            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.magic = 0x{0:X8} (should be 0x{1:X8})", dvh.magic,
                                      SGI_MAGIC);

            if (dvh.magic != SGI_MAGIC)
            {
                return(false);
            }

            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.root_part_num = {0}", dvh.root_part_num);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.swap_part_num = {0}", dvh.swap_part_num);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.boot_file = \"{0}\"",
                                      StringHandlers.CToString(dvh.boot_file));
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_skew = {0}", dvh.device_params.dp_skew);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_gap1 = {0}", dvh.device_params.dp_gap1);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_gap2 = {0}", dvh.device_params.dp_gap2);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_spares_cyl = {0}",
                                      dvh.device_params.dp_spares_cyl);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_cyls = {0}", dvh.device_params.dp_cyls);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_shd0 = {0}", dvh.device_params.dp_shd0);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_trks0 = {0}", dvh.device_params.dp_trks0);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_ctq_depth = {0}",
                                      dvh.device_params.dp_ctq_depth);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_cylshi = {0}", dvh.device_params.dp_cylshi);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_secs = {0}", dvh.device_params.dp_secs);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_secbytes = {0}",
                                      dvh.device_params.dp_secbytes);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_interleave = {0}",
                                      dvh.device_params.dp_interleave);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_flags = {0}", dvh.device_params.dp_flags);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_datarate = {0}",
                                      dvh.device_params.dp_datarate);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_nretries = {0}",
                                      dvh.device_params.dp_nretries);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_mspw = {0}", dvh.device_params.dp_mspw);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xgap1 = {0}", dvh.device_params.dp_xgap1);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xsync = {0}", dvh.device_params.dp_xsync);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xrdly = {0}", dvh.device_params.dp_xrdly);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xgap2 = {0}", dvh.device_params.dp_xgap2);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xrgate = {0}", dvh.device_params.dp_xrgate);
            DicConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xwcont = {0}", dvh.device_params.dp_xwcont);

            ulong counter = 0;

            for (int i = 0; i < dvh.partitions.Length; i++)
            {
                DicConsole.DebugWriteLine("SGIVH plugin", "dvh.partitions[{0}].num_blocks = {1}", i,
                                          dvh.partitions[i].num_blocks);
                DicConsole.DebugWriteLine("SGIVH plugin", "dvh.partitions[{0}].first_block = {1}", i,
                                          dvh.partitions[i].first_block);
                // TODO: Solve big endian marshal with enumerations
                dvh.partitions[i].type = (SGIType)Swapping.Swap((uint)dvh.partitions[i].type);
                DicConsole.DebugWriteLine("SGIVH plugin", "dvh.partitions[{0}].type = {1}", i, dvh.partitions[i].type);

                Partition part = new Partition
                {
                    Start =
                        dvh.partitions[i].first_block * dvh.device_params.dp_secbytes / imagePlugin.Info.SectorSize,
                    Offset = dvh.partitions[i].first_block * dvh.device_params.dp_secbytes,
                    Length = dvh.partitions[i].num_blocks * dvh.device_params.dp_secbytes /
                             imagePlugin.Info.SectorSize,
                    Size     = dvh.partitions[i].num_blocks * dvh.device_params.dp_secbytes,
                    Type     = TypeToString(dvh.partitions[i].type),
                    Sequence = counter,
                    Scheme   = Name
                };
                if (part.Size <= 0 || dvh.partitions[i].type == SGIType.Header ||
                    dvh.partitions[i].type == SGIType.Volume)
                {
                    continue;
                }

                partitions.Add(part);
                counter++;
            }

            return(true);
        }
Beispiel #28
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding = new PETSCII();
            byte[] sector;

            StringBuilder sbInformation = new StringBuilder();

            sbInformation.AppendLine("Commodore file system");

            XmlFsType = new FileSystemType
            {
                Type = "Commodore file system", Clusters = (long)imagePlugin.Info.Sectors, ClusterSize = 256
            };

            if (imagePlugin.Info.Sectors == 3200)
            {
                sector = imagePlugin.ReadSector(1560);
                CommodoreHeader cbmHdr    = new CommodoreHeader();
                IntPtr          cbmHdrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cbmHdr));
                Marshal.Copy(sector, 0, cbmHdrPtr, Marshal.SizeOf(cbmHdr));
                cbmHdr = (CommodoreHeader)Marshal.PtrToStructure(cbmHdrPtr, typeof(CommodoreHeader));
                Marshal.FreeHGlobal(cbmHdrPtr);

                sbInformation.AppendFormat("Directory starts at track {0} sector {1}", cbmHdr.directoryTrack,
                                           cbmHdr.directorySector).AppendLine();
                sbInformation
                .AppendFormat("Disk DOS Version: {0}", Encoding.ASCII.GetString(new[] { cbmHdr.diskDosVersion }))
                .AppendLine();
                sbInformation.AppendFormat("DOS Version: {0}", Encoding.ASCII.GetString(new[] { cbmHdr.dosVersion }))
                .AppendLine();
                sbInformation.AppendFormat("Disk Version: {0}", Encoding.ASCII.GetString(new[] { cbmHdr.diskVersion }))
                .AppendLine();
                sbInformation.AppendFormat("Disk ID: {0}", cbmHdr.diskId).AppendLine();
                sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(cbmHdr.name, Encoding))
                .AppendLine();

                XmlFsType.VolumeName   = StringHandlers.CToString(cbmHdr.name, Encoding);
                XmlFsType.VolumeSerial = $"{cbmHdr.diskId}";
            }
            else
            {
                sector = imagePlugin.ReadSector(357);
                CommodoreBam cbmBam    = new CommodoreBam();
                IntPtr       cbmBamPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cbmBam));
                Marshal.Copy(sector, 0, cbmBamPtr, Marshal.SizeOf(cbmBam));
                cbmBam = (CommodoreBam)Marshal.PtrToStructure(cbmBamPtr, typeof(CommodoreBam));
                Marshal.FreeHGlobal(cbmBamPtr);

                sbInformation.AppendFormat("Directory starts at track {0} sector {1}", cbmBam.directoryTrack,
                                           cbmBam.directorySector).AppendLine();
                sbInformation.AppendFormat("Disk DOS type: {0}",
                                           Encoding.ASCII.GetString(BitConverter.GetBytes(cbmBam.dosType)))
                .AppendLine();
                sbInformation.AppendFormat("DOS Version: {0}", Encoding.ASCII.GetString(new[] { cbmBam.dosVersion }))
                .AppendLine();
                sbInformation.AppendFormat("Disk ID: {0}", cbmBam.diskId).AppendLine();
                sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(cbmBam.name, Encoding))
                .AppendLine();

                XmlFsType.VolumeName   = StringHandlers.CToString(cbmBam.name, Encoding);
                XmlFsType.VolumeSerial = $"{cbmBam.diskId}";
            }

            information = sbInformation.ToString();
        }
Beispiel #29
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = Encoding.BigEndianUnicode;
            information = "";

            HfsPlusVolumeHeader vh = new HfsPlusVolumeHeader();

            ulong hfspOffset;
            bool  wrapped;

            uint sectorsToRead = 0x800 / imagePlugin.Info.SectorSize;

            if (0x800 % imagePlugin.Info.SectorSize > 0)
            {
                sectorsToRead++;
            }

            byte[] vhSector = imagePlugin.ReadSectors(partition.Start, sectorsToRead);

            ushort drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x400);

            if (drSigWord == HFS_MAGIC)                                      // "BD"
            {
                drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x47C); // Read embedded HFS+ signature

                if (drSigWord == HFSP_MAGIC)                                 // "H+"
                {
                    ushort xdrStABNt = BigEndianBitConverter.ToUInt16(vhSector, 0x47E);

                    uint drAlBlkSiz = BigEndianBitConverter.ToUInt32(vhSector, 0x414);

                    ushort drAlBlSt = BigEndianBitConverter.ToUInt16(vhSector, 0x41C);

                    hfspOffset = (ulong)((drAlBlSt * 512 + xdrStABNt * drAlBlkSiz) / imagePlugin.Info.SectorSize);
                    wrapped    = true;
                }
                else
                {
                    hfspOffset = 0;
                    wrapped    = false;
                }
            }
            else
            {
                hfspOffset = 0;
                wrapped    = false;
            }

            vhSector = imagePlugin.ReadSectors(partition.Start + hfspOffset, sectorsToRead); // Read volume header

            vh.signature = BigEndianBitConverter.ToUInt16(vhSector, 0x400);

            if (vh.signature != HFSP_MAGIC && vh.signature != HFSX_MAGIC)
            {
                return;
            }

            StringBuilder sb = new StringBuilder();

            if (vh.signature == 0x482B)
            {
                sb.AppendLine("HFS+ filesystem.");
            }
            if (vh.signature == 0x4858)
            {
                sb.AppendLine("HFSX filesystem.");
            }
            if (wrapped)
            {
                sb.AppendLine("Volume is wrapped inside an HFS volume.");
            }

            byte[] tmp = new byte[0x400];
            Array.Copy(vhSector, 0x400, tmp, 0, 0x400);
            vhSector = tmp;

            vh = BigEndianMarshal.ByteArrayToStructureBigEndian <HfsPlusVolumeHeader>(vhSector);

            if (vh.version == 4 || vh.version == 5)
            {
                sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine();

                if ((vh.attributes & 0x80) == 0x80)
                {
                    sb.AppendLine("Volume is locked on hardware.");
                }
                if ((vh.attributes & 0x100) == 0x100)
                {
                    sb.AppendLine("Volume is unmounted.");
                }
                if ((vh.attributes & 0x200) == 0x200)
                {
                    sb.AppendLine("There are bad blocks in the extents file.");
                }
                if ((vh.attributes & 0x400) == 0x400)
                {
                    sb.AppendLine("Volume does not require cache.");
                }
                if ((vh.attributes & 0x800) == 0x800)
                {
                    sb.AppendLine("Volume state is inconsistent.");
                }
                if ((vh.attributes & 0x1000) == 0x1000)
                {
                    sb.AppendLine("CNIDs are reused.");
                }
                if ((vh.attributes & 0x2000) == 0x2000)
                {
                    sb.AppendLine("Volume is journaled.");
                }
                if ((vh.attributes & 0x8000) == 0x8000)
                {
                    sb.AppendLine("Volume is locked on software.");
                }

                sb.AppendFormat("Implementation that last mounted the volume: \"{0}\".",
                                Encoding.ASCII.GetString(vh.lastMountedVersion)).AppendLine();
                if ((vh.attributes & 0x2000) == 0x2000)
                {
                    sb.AppendFormat("Journal starts at allocation block {0}.", vh.journalInfoBlock).AppendLine();
                }
                sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(vh.createDate)).AppendLine();
                sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(vh.modifyDate)).AppendLine();
                if (vh.backupDate > 0)
                {
                    sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(vh.backupDate)).AppendLine();
                }
                else
                {
                    sb.AppendLine("Volume has never been backed up");
                }
                if (vh.backupDate > 0)
                {
                    sb.AppendFormat("Last check date: {0}", DateHandlers.MacToDateTime(vh.checkedDate)).AppendLine();
                }
                else
                {
                    sb.AppendLine("Volume has never been checked up");
                }
                sb.AppendFormat("{0} files on volume.", vh.fileCount).AppendLine();
                sb.AppendFormat("{0} folders on volume.", vh.folderCount).AppendLine();
                sb.AppendFormat("{0} bytes per allocation block.", vh.blockSize).AppendLine();
                sb.AppendFormat("{0} allocation blocks.", vh.totalBlocks).AppendLine();
                sb.AppendFormat("{0} free blocks.", vh.freeBlocks).AppendLine();
                sb.AppendFormat("Next allocation block: {0}.", vh.nextAllocation).AppendLine();
                sb.AppendFormat("Resource fork clump size: {0} bytes.", vh.rsrcClumpSize).AppendLine();
                sb.AppendFormat("Data fork clump size: {0} bytes.", vh.dataClumpSize).AppendLine();
                sb.AppendFormat("Next unused CNID: {0}.", vh.nextCatalogID).AppendLine();
                sb.AppendFormat("Volume has been mounted writable {0} times.", vh.writeCount).AppendLine();
                sb.AppendFormat("Allocation File is {0} bytes.", vh.allocationFile_logicalSize).AppendLine();
                sb.AppendFormat("Extents File is {0} bytes.", vh.extentsFile_logicalSize).AppendLine();
                sb.AppendFormat("Catalog File is {0} bytes.", vh.catalogFile_logicalSize).AppendLine();
                sb.AppendFormat("Attributes File is {0} bytes.", vh.attributesFile_logicalSize).AppendLine();
                sb.AppendFormat("Startup File is {0} bytes.", vh.startupFile_logicalSize).AppendLine();
                sb.AppendLine("Finder info:");
                sb.AppendFormat("CNID of bootable system's directory: {0}", vh.drFndrInfo0).AppendLine();
                sb.AppendFormat("CNID of first-run application's directory: {0}", vh.drFndrInfo1).AppendLine();
                sb.AppendFormat("CNID of previously opened directory: {0}", vh.drFndrInfo2).AppendLine();
                sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", vh.drFndrInfo3).AppendLine();
                sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", vh.drFndrInfo5).AppendLine();
                if (vh.drFndrInfo6 != 0 && vh.drFndrInfo7 != 0)
                {
                    sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", vh.drFndrInfo6, vh.drFndrInfo7).AppendLine();
                }

                XmlFsType = new FileSystemType();
                if (vh.backupDate > 0)
                {
                    XmlFsType.BackupDate          = DateHandlers.MacToDateTime(vh.backupDate);
                    XmlFsType.BackupDateSpecified = true;
                }

                XmlFsType.Bootable   |= vh.drFndrInfo0 != 0 || vh.drFndrInfo3 != 0 || vh.drFndrInfo5 != 0;
                XmlFsType.Clusters    = vh.totalBlocks;
                XmlFsType.ClusterSize = (int)vh.blockSize;
                if (vh.createDate > 0)
                {
                    XmlFsType.CreationDate          = DateHandlers.MacToDateTime(vh.createDate);
                    XmlFsType.CreationDateSpecified = true;
                }

                XmlFsType.Dirty                 = (vh.attributes & 0x100) != 0x100;
                XmlFsType.Files                 = vh.fileCount;
                XmlFsType.FilesSpecified        = true;
                XmlFsType.FreeClusters          = vh.freeBlocks;
                XmlFsType.FreeClustersSpecified = true;
                if (vh.modifyDate > 0)
                {
                    XmlFsType.ModificationDate          = DateHandlers.MacToDateTime(vh.modifyDate);
                    XmlFsType.ModificationDateSpecified = true;
                }

                if (vh.signature == 0x482B)
                {
                    XmlFsType.Type = "HFS+";
                }
                if (vh.signature == 0x4858)
                {
                    XmlFsType.Type = "HFSX";
                }
                if (vh.drFndrInfo6 != 0 && vh.drFndrInfo7 != 0)
                {
                    XmlFsType.VolumeSerial = $"{vh.drFndrInfo6:X8}{vh.drFndrInfo7:X8}";
                }
                XmlFsType.SystemIdentifier = Encoding.ASCII.GetString(vh.lastMountedVersion);
            }
            else
            {
                sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine();
                sb.AppendLine("This version is not supported yet.");
            }

            information = sb.ToString();
        }
Beispiel #30
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = Encoding.Unicode;
            information = "";

            if (imagePlugin.Info.SectorSize < F2FS_MIN_SECTOR ||
                imagePlugin.Info.SectorSize > F2FS_MAX_SECTOR)
            {
                return;
            }

            uint sbAddr = F2FS_SUPER_OFFSET / imagePlugin.Info.SectorSize;

            if (sbAddr == 0)
            {
                sbAddr = 1;
            }

            uint sbSize = (uint)(Marshal.SizeOf <Superblock>() / imagePlugin.Info.SectorSize);

            if (Marshal.SizeOf <Superblock>() % imagePlugin.Info.SectorSize != 0)
            {
                sbSize++;
            }

            byte[] sector = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize);

            if (sector.Length < Marshal.SizeOf <Superblock>())
            {
                return;
            }

            Superblock f2fsSb = Marshal.ByteArrayToStructureLittleEndian <Superblock>(sector);

            if (f2fsSb.magic != F2FS_MAGIC)
            {
                return;
            }

            var sb = new StringBuilder();

            sb.AppendLine("F2FS filesystem");
            sb.AppendFormat("Version {0}.{1}", f2fsSb.major_ver, f2fsSb.minor_ver).AppendLine();
            sb.AppendFormat("{0} bytes per sector", 1 << (int)f2fsSb.log_sectorsize).AppendLine();

            sb.AppendFormat("{0} sectors ({1} bytes) per block", 1 << (int)f2fsSb.log_sectors_per_block,
                            1 << (int)f2fsSb.log_blocksize).AppendLine();

            sb.AppendFormat("{0} blocks per segment", f2fsSb.log_blocks_per_seg).AppendLine();
            sb.AppendFormat("{0} blocks in volume", f2fsSb.block_count).AppendLine();
            sb.AppendFormat("{0} segments per section", f2fsSb.segs_per_sec).AppendLine();
            sb.AppendFormat("{0} sections per zone", f2fsSb.secs_per_zone).AppendLine();
            sb.AppendFormat("{0} sections", f2fsSb.section_count).AppendLine();
            sb.AppendFormat("{0} segments", f2fsSb.segment_count).AppendLine();
            sb.AppendFormat("Root directory resides on inode {0}", f2fsSb.root_ino).AppendLine();
            sb.AppendFormat("Volume UUID: {0}", f2fsSb.uuid).AppendLine();

            sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true)).
            AppendLine();

            sb.AppendFormat("Volume last mounted on kernel version: {0}", StringHandlers.CToString(f2fsSb.version)).
            AppendLine();

            sb.AppendFormat("Volume created on kernel version: {0}", StringHandlers.CToString(f2fsSb.init_version)).
            AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Type                   = "F2FS filesystem",
                SystemIdentifier       = Encoding.ASCII.GetString(f2fsSb.version),
                Clusters               = f2fsSb.block_count,
                ClusterSize            = (uint)(1 << (int)f2fsSb.log_blocksize),
                DataPreparerIdentifier = Encoding.ASCII.GetString(f2fsSb.init_version),
                VolumeName             = StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true),
                VolumeSerial           = f2fsSb.uuid.ToString()
            };
        }