Beispiel #1
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("macintosh");
            information = "";

            var sb = new StringBuilder();

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

            bool apmFromHddOnCd = false;

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

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

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

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

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

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

                    break;
                }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            information = sb.ToString();

            XmlFsType = new FileSystemType();

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

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

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

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

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

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

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

            if (mdb.drFndrInfo6 != 0 &&
                mdb.drFndrInfo7 != 0)
            {
                XmlFsType.VolumeSerial = $"{mdb.drFndrInfo6:X8}{mdb.drFndrInfo7:X8}";
            }
        }
Beispiel #2
0
 public static void UInt32ToBytesTest(uint n, byte[] expected)
 {
     Assert.Equal(expected, BigEndianBitConverter.GetBytes(n));
 }
Beispiel #3
0
        public bool WriteSector(byte[] data, ulong sectorAddress)
        {
            if (!IsWriting)
            {
                ErrorMessage = "Tried to write on a non-writable image";

                return(false);
            }

            if (data.Length != imageInfo.SectorSize)
            {
                ErrorMessage = "Incorrect data size";

                return(false);
            }

            if (sectorAddress >= imageInfo.Sectors)
            {
                ErrorMessage = "Tried to write past image size";

                return(false);
            }

            // Ignore empty sectors
            if (ArrayHelpers.ArrayIsNullOrEmpty(data))
            {
                return(true);
            }

            ulong byteAddress = sectorAddress * 512;

            ulong l1Off = (byteAddress & l1Mask) >> l1Shift;

            if ((long)l1Off >= l1Table.LongLength)
            {
                throw new ArgumentOutOfRangeException(nameof(l1Off),
                                                      $"Trying to write past L1 table, position {l1Off} of a max {l1Table.LongLength}");
            }

            if (l1Table[l1Off] == 0)
            {
                writingStream.Seek(0, SeekOrigin.End);
                l1Table[l1Off] = (ulong)writingStream.Position;
                byte[] l2TableB = new byte[l2Size * 8];
                writingStream.Seek(0, SeekOrigin.End);
                writingStream.Write(l2TableB, 0, l2TableB.Length);
            }

            writingStream.Position = (long)l1Table[l1Off];

            ulong l2Off = (byteAddress & l2Mask) >> qHdr.cluster_bits;

            writingStream.Seek((long)(l1Table[l1Off] + (l2Off * 8)), SeekOrigin.Begin);

            byte[] entry = new byte[8];
            writingStream.Read(entry, 0, 8);
            ulong offset = BigEndianBitConverter.ToUInt64(entry, 0);

            if (offset == 0)
            {
                offset = (ulong)writingStream.Length;
                byte[] cluster = new byte[clusterSize];
                entry = BigEndianBitConverter.GetBytes(offset);
                writingStream.Seek((long)(l1Table[l1Off] + (l2Off * 8)), SeekOrigin.Begin);
                writingStream.Write(entry, 0, 8);
                writingStream.Seek(0, SeekOrigin.End);
                writingStream.Write(cluster, 0, cluster.Length);
            }

            writingStream.Seek((long)(offset + (byteAddress & sectorMask)), SeekOrigin.Begin);
            writingStream.Write(data, 0, data.Length);

            ErrorMessage = "";

            return(true);
        }
Beispiel #4
0
        public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset)
        {
            bool magicFound = false;

            byte[] labelSector;

            uint sectorSize;

            if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448)
            {
                sectorSize = 2048;
            }
            else
            {
                sectorSize =
                    imagePlugin.Info.SectorSize;
            }

            partitions = new List <Partition>();

            BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;

            ulong labelPosition = 0;

            foreach (ulong i in new ulong[] { 0, 4, 15, 16 }.TakeWhile(i => i + sectorOffset < imagePlugin.Info.Sectors))
            {
                labelSector = imagePlugin.ReadSector(i + sectorOffset);
                uint magic = BigEndianBitConverter.ToUInt32(labelSector, 0x00);
                if (magic != NEXT_MAGIC1 && magic != NEXT_MAGIC2 && magic != NEXT_MAGIC3)
                {
                    continue;
                }

                magicFound    = true;
                labelPosition = i + sectorOffset;
                break;
            }

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

            uint sectorsToRead = 7680 / imagePlugin.Info.SectorSize;

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

            labelSector = imagePlugin.ReadSectors(labelPosition, sectorsToRead);

            NeXTLabel label = BigEndianMarshal.ByteArrayToStructureBigEndian <NeXTLabel>(labelSector);

            byte[] disktabB = new byte[498];
            Array.Copy(labelSector, 44, disktabB, 0, 498);
            label.dl_dt = BigEndianMarshal.ByteArrayToStructureBigEndian <NeXTDiskTab>(disktabB);
            label.dl_dt.d_partitions = new NeXTEntry[8];

            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_version = 0x{0:X8}", label.dl_version);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_label_blkno = {0}", label.dl_label_blkno);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_size = {0}", label.dl_size);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_label = \"{0}\"",
                                      StringHandlers.CToString(label.dl_label));
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_flags = {0}", label.dl_flags);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_tag = 0x{0:X8}", label.dl_tag);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_name = \"{0}\"",
                                      StringHandlers.CToString(label.dl_dt.d_name));
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_type = \"{0}\"",
                                      StringHandlers.CToString(label.dl_dt.d_type));
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_secsize = {0}", label.dl_dt.d_secsize);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ntracks = {0}", label.dl_dt.d_ntracks);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_nsectors = {0}", label.dl_dt.d_nsectors);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ncylinders = {0}", label.dl_dt.d_ncylinders);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_rpm = {0}", label.dl_dt.d_rpm);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_front = {0}", label.dl_dt.d_front);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_back = {0}", label.dl_dt.d_back);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ngroups = {0}", label.dl_dt.d_ngroups);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ag_size = {0}", label.dl_dt.d_ag_size);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ag_alts = {0}", label.dl_dt.d_ag_alts);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_ag_off = {0}", label.dl_dt.d_ag_off);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_boot0_blkno[0] = {0}",
                                      label.dl_dt.d_boot0_blkno[0]);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_boot0_blkno[1] = {0}",
                                      label.dl_dt.d_boot0_blkno[1]);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_bootfile = \"{0}\"",
                                      StringHandlers.CToString(label.dl_dt.d_bootfile));
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_hostname = \"{0}\"",
                                      StringHandlers.CToString(label.dl_dt.d_hostname));
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_rootpartition = {0}", label.dl_dt.d_rootpartition);
            DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_rwpartition = {0}", label.dl_dt.d_rwpartition);

            for (int i = 0; i < 8; i++)
            {
                byte[] partB = new byte[44];
                Array.Copy(labelSector, 44 + 146 + 44 * i, partB, 0, 44);
                label.dl_dt.d_partitions[i] = BigEndianMarshal.ByteArrayToStructureBigEndian <NeXTEntry>(partB);
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_base = {1}", i,
                                          label.dl_dt.d_partitions[i].p_base);
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_size = {1}", i,
                                          label.dl_dt.d_partitions[i].p_size);
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_bsize = {1}", i,
                                          label.dl_dt.d_partitions[i].p_bsize);
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_fsize = {1}", i,
                                          label.dl_dt.d_partitions[i].p_fsize);
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_opt = {1}", i,
                                          label.dl_dt.d_partitions[i].p_opt);
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_cpg = {1}", i,
                                          label.dl_dt.d_partitions[i].p_cpg);
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_density = {1}", i,
                                          label.dl_dt.d_partitions[i].p_density);
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_minfree = {1}", i,
                                          label.dl_dt.d_partitions[i].p_minfree);
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_newfs = {1}", i,
                                          label.dl_dt.d_partitions[i].p_newfs);
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_mountpt = \"{1}\"", i,
                                          StringHandlers.CToString(label.dl_dt.d_partitions[i].p_mountpt));
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_automnt = {1}", i,
                                          label.dl_dt.d_partitions[i].p_automnt);
                DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_type = \"{1}\"", i,
                                          StringHandlers.CToString(label.dl_dt.d_partitions[i].p_type));

                if (label.dl_dt.d_partitions[i].p_size <= 0 || label.dl_dt.d_partitions[i].p_base < 0 ||
                    label.dl_dt.d_partitions[i].p_bsize < 0)
                {
                    continue;
                }

                StringBuilder sb = new StringBuilder();

                Partition part = new Partition
                {
                    Size   = (ulong)(label.dl_dt.d_partitions[i].p_size * label.dl_dt.d_secsize),
                    Offset =
                        (ulong)((label.dl_dt.d_partitions[i].p_base + label.dl_dt.d_front) * label.dl_dt.d_secsize),
                    Type     = StringHandlers.CToString(label.dl_dt.d_partitions[i].p_type),
                    Sequence = (ulong)i,
                    Name     = StringHandlers.CToString(label.dl_dt.d_partitions[i].p_mountpt),
                    Length   = (ulong)(label.dl_dt.d_partitions[i].p_size * label.dl_dt.d_secsize / sectorSize),
                    Start    = (ulong)((label.dl_dt.d_partitions[i].p_base + label.dl_dt.d_front) *
                                       label.dl_dt.d_secsize / sectorSize),
                    Scheme = Name
                };

                if (part.Start + part.Length > imagePlugin.Info.Sectors)
                {
                    DicConsole.DebugWriteLine("NeXT Plugin", "Partition bigger than device, reducing...");
                    part.Length = imagePlugin.Info.Sectors - part.Start;
                    part.Size   = part.Length * sectorSize;
                    DicConsole.DebugWriteLine("NeXT Plugin", "label.dl_dt.d_partitions[{0}].p_size = {1}", i,
                                              part.Length);
                }

                sb.AppendFormat("{0} bytes per block", label.dl_dt.d_partitions[i].p_bsize).AppendLine();
                sb.AppendFormat("{0} bytes per fragment", label.dl_dt.d_partitions[i].p_fsize).AppendLine();
                if (label.dl_dt.d_partitions[i].p_opt == 's')
                {
                    sb.AppendLine("Space optimized");
                }
                else if (label.dl_dt.d_partitions[i].p_opt == 't')
                {
                    sb.AppendLine("Time optimized");
                }
                else
                {
                    sb.AppendFormat("Unknown optimization {0:X2}", label.dl_dt.d_partitions[i].p_opt).AppendLine();
                }
                sb.AppendFormat("{0} cylinders per group", label.dl_dt.d_partitions[i].p_cpg).AppendLine();
                sb.AppendFormat("{0} bytes per inode", label.dl_dt.d_partitions[i].p_density).AppendLine();
                sb.AppendFormat("{0}% of space must be free at minimum", label.dl_dt.d_partitions[i].p_minfree)
                .AppendLine();
                if (label.dl_dt.d_partitions[i].p_newfs != 1)
                {
                    sb.AppendLine("Filesystem should be formatted at start");
                }
                if (label.dl_dt.d_partitions[i].p_automnt == 1)
                {
                    sb.AppendLine("Filesystem should be automatically mounted");
                }

                part.Description = sb.ToString();

                partitions.Add(part);
            }

            return(true);
        }
Beispiel #5
0
        /// <summary>
        ///     Reads, interprets and caches the Catalog File
        /// </summary>
        Errno ReadCatalog()
        {
            if (!mounted)
            {
                return(Errno.AccessDenied);
            }

            catalogCache = new List <CatalogEntry>();

            // Do differently for V1 and V2
            if (mddf.fsversion == LISA_V2 || mddf.fsversion == LISA_V1)
            {
                Errno error = ReadFile((short)FILEID_CATALOG, out byte[] buf);
                if (error != Errno.NoError)
                {
                    return(error);
                }

                int offset = 0;
                List <CatalogEntryV2> catalogV2 = new List <CatalogEntryV2>();

                // For each entry on the catalog
                while (offset + 54 < buf.Length)
                {
                    CatalogEntryV2 entV2 = new CatalogEntryV2
                    {
                        filenameLen = buf[offset],
                        filename    = new byte[E_NAME],
                        unknown1    = buf[offset + 0x21],
                        fileType    = buf[offset + 0x22],
                        unknown2    = buf[offset + 0x23],
                        fileID      = BigEndianBitConverter.ToInt16(buf, offset + 0x24),
                        unknown3    = new byte[16]
                    };
                    Array.Copy(buf, offset + 0x01, entV2.filename, 0, E_NAME);
                    Array.Copy(buf, offset + 0x26, entV2.unknown3, 0, 16);

                    offset += 54;

                    // Check that the entry is correct, not empty or garbage
                    if (entV2.filenameLen != 0 && entV2.filenameLen <= E_NAME && entV2.fileType != 0 && entV2.fileID > 0)
                    {
                        catalogV2.Add(entV2);
                    }
                }

                // Convert entries to V3 format
                foreach (CatalogEntryV2 entV2 in catalogV2)
                {
                    error = ReadExtentsFile(entV2.fileID, out ExtentFile ext);
                    if (error != Errno.NoError)
                    {
                        continue;
                    }

                    CatalogEntry entV3 = new CatalogEntry
                    {
                        fileID   = entV2.fileID,
                        filename = new byte[32],
                        fileType = entV2.fileType,
                        length   = (int)srecords[entV2.fileID].filesize,
                        dtc      = ext.dtc,
                        dtm      = ext.dtm
                    };
                    Array.Copy(entV2.filename, 0, entV3.filename, 0, entV2.filenameLen);

                    catalogCache.Add(entV3);
                }

                return(Errno.NoError);
            }

            byte[] firstCatalogBlock = null;

            // Search for the first sector describing the catalog
            // While root catalog is not stored in S-Records, probably rest are? (unchecked)
            // If root catalog is not pointed in MDDF (unchecked) maybe it's always following S-Records File?
            for (ulong i = 0; i < device.Info.Sectors; i++)
            {
                DecodeTag(device.ReadSectorTag(i, SectorTagType.AppleSectorTag), out LisaTag.PriamTag catTag);

                if (catTag.FileId != FILEID_CATALOG || catTag.RelPage != 0)
                {
                    continue;
                }

                firstCatalogBlock = device.ReadSectors(i, 4);
                break;
            }

            // Catalog not found
            if (firstCatalogBlock == null)
            {
                return(Errno.NoSuchFile);
            }

            ulong prevCatalogPointer;

            prevCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7F6);

            // Traverse double-linked list until first catalog block
            while (prevCatalogPointer != 0xFFFFFFFF)
            {
                DecodeTag(device.ReadSectorTag(prevCatalogPointer + mddf.mddf_block + volumePrefix, SectorTagType.AppleSectorTag),
                          out LisaTag.PriamTag prevTag);

                if (prevTag.FileId != FILEID_CATALOG)
                {
                    return(Errno.InvalidArgument);
                }

                firstCatalogBlock  = device.ReadSectors(prevCatalogPointer + mddf.mddf_block + volumePrefix, 4);
                prevCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7F6);
            }

            ulong nextCatalogPointer;

            nextCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7FA);

            List <byte[]> catalogBlocks = new List <byte[]> {
                firstCatalogBlock
            };

            // Traverse double-linked list to read full catalog
            while (nextCatalogPointer != 0xFFFFFFFF)
            {
                DecodeTag(device.ReadSectorTag(nextCatalogPointer + mddf.mddf_block + volumePrefix, SectorTagType.AppleSectorTag),
                          out LisaTag.PriamTag nextTag);

                if (nextTag.FileId != FILEID_CATALOG)
                {
                    return(Errno.InvalidArgument);
                }

                byte[] nextCatalogBlock = device.ReadSectors(nextCatalogPointer + mddf.mddf_block + volumePrefix, 4);
                nextCatalogPointer = BigEndianBitConverter.ToUInt32(nextCatalogBlock, 0x7FA);
                catalogBlocks.Add(nextCatalogBlock);
            }

            // Foreach catalog block
            foreach (byte[] buf in catalogBlocks)
            {
                int offset = 0;

                // Traverse all entries
                while (offset + 64 <= buf.Length)
                {
                    // Catalog block header
                    if (buf[offset + 0x24] == 0x08)
                    {
                        offset += 78;
                    }
                    // Maybe just garbage? Found in more than 1 disk
                    else if (buf[offset + 0x24] == 0x7C)
                    {
                        offset += 50;
                    }
                    // Apparently reserved to indicate end of catalog?
                    else if (buf[offset + 0x24] == 0xFF)
                    {
                        break;
                    }
                    // Normal entry
                    else if (buf[offset + 0x24] == 0x03 && buf[offset] == 0x24)
                    {
                        CatalogEntry entry = new CatalogEntry
                        {
                            marker     = buf[offset],
                            parentID   = BigEndianBitConverter.ToUInt16(buf, offset + 0x01),
                            filename   = new byte[E_NAME],
                            terminator = buf[offset + 0x23],
                            fileType   = buf[offset + 0x24],
                            unknown    = buf[offset + 0x25],
                            fileID     = BigEndianBitConverter.ToInt16(buf, offset + 0x26),
                            dtc        = BigEndianBitConverter.ToUInt32(buf, offset + 0x28),
                            dtm        = BigEndianBitConverter.ToUInt32(buf, offset + 0x2C),
                            length     = BigEndianBitConverter.ToInt32(buf, offset + 0x30),
                            wasted     = BigEndianBitConverter.ToInt32(buf, offset + 0x34),
                            tail       = new byte[8]
                        };
                        Array.Copy(buf, offset + 0x03, entry.filename, 0, E_NAME);
                        Array.Copy(buf, offset + 0x38, entry.tail, 0, 8);

                        if (ReadExtentsFile(entry.fileID, out _) == Errno.NoError)
                        {
                            if (!fileSizeCache.ContainsKey(entry.fileID))
                            {
                                catalogCache.Add(entry);
                                fileSizeCache.Add(entry.fileID, entry.length);
                            }
                        }

                        offset += 64;
                    }
                    // Subdirectory entry
                    else if (buf[offset + 0x24] == 0x01 && buf[offset] == 0x24)
                    {
                        CatalogEntry entry = new CatalogEntry
                        {
                            marker     = buf[offset],
                            parentID   = BigEndianBitConverter.ToUInt16(buf, offset + 0x01),
                            filename   = new byte[E_NAME],
                            terminator = buf[offset + 0x23],
                            fileType   = buf[offset + 0x24],
                            unknown    = buf[offset + 0x25],
                            fileID     = BigEndianBitConverter.ToInt16(buf, offset + 0x26),
                            dtc        = BigEndianBitConverter.ToUInt32(buf, offset + 0x28),
                            dtm        = BigEndianBitConverter.ToUInt32(buf, offset + 0x2C),
                            length     = 0,
                            wasted     = 0,
                            tail       = null
                        };
                        Array.Copy(buf, offset + 0x03, entry.filename, 0, E_NAME);

                        if (!directoryDtcCache.ContainsKey(entry.fileID))
                        {
                            directoryDtcCache.Add(entry.fileID, DateHandlers.LisaToDateTime(entry.dtc));
                        }

                        catalogCache.Add(entry);

                        offset += 48;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            return(Errno.NoError);
        }
Beispiel #6
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);

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

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

            Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8);

            // 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 #7
0
 /// <inheritdoc />
 /// <summary>Returns a byte array of the hash value.</summary>
 public byte[] Final() => BigEndianBitConverter.GetBytes(_hashInt ^ _finalSeed);
Beispiel #8
0
        // TODO: Decode native nvlist
        static bool DecodeNvList(byte[] nvlist, out Dictionary <string, NVS_Item> decodedNvList, bool xdr,
                                 bool littleEndian)
        {
            decodedNvList = new Dictionary <string, NVS_Item>();

            if (nvlist == null ||
                nvlist.Length < 16)
            {
                return(false);
            }

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

            int offset = 8;

            while (offset < nvlist.Length)
            {
                var item    = new NVS_Item();
                int currOff = offset;

                item.encodedSize = BigEndianBitConverter.ToUInt32(nvlist, offset);

                // Finished
                if (item.encodedSize == 0)
                {
                    break;
                }

                offset          += 4;
                item.decodedSize = BigEndianBitConverter.ToUInt32(nvlist, offset);
                offset          += 4;
                uint nameLength = BigEndianBitConverter.ToUInt32(nvlist, offset);
                offset += 4;

                if (nameLength % 4 > 0)
                {
                    nameLength += 4 - (nameLength % 4);
                }

                byte[] nameBytes = new byte[nameLength];
                Array.Copy(nvlist, offset, nameBytes, 0, nameLength);
                item.name     = StringHandlers.CToString(nameBytes);
                offset       += (int)nameLength;
                item.dataType = (NVS_DataTypes)BigEndianBitConverter.ToUInt32(nvlist, offset);
                offset       += 4;
                item.elements = BigEndianBitConverter.ToUInt32(nvlist, offset);
                offset       += 4;

                if (item.elements == 0)
                {
                    decodedNvList.Add(item.name, item);

                    continue;
                }

                switch (item.dataType)
                {
                case NVS_DataTypes.DATA_TYPE_BOOLEAN:
                case NVS_DataTypes.DATA_TYPE_BOOLEAN_ARRAY:
                case NVS_DataTypes.DATA_TYPE_BOOLEAN_VALUE:
                    if (item.elements > 1)
                    {
                        bool[] boolArray = new bool[item.elements];

                        for (int i = 0; i < item.elements; i++)
                        {
                            uint temp = BigEndianBitConverter.ToUInt32(nvlist, offset);
                            boolArray[i] = temp > 0;
                            offset      += 4;
                        }

                        item.value = boolArray;
                    }
                    else
                    {
                        uint temp = BigEndianBitConverter.ToUInt32(nvlist, offset);
                        item.value = temp > 0;
                        offset    += 4;
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_BYTE:
                case NVS_DataTypes.DATA_TYPE_BYTE_ARRAY:
                case NVS_DataTypes.DATA_TYPE_UINT8:
                case NVS_DataTypes.DATA_TYPE_UINT8_ARRAY:
                    if (item.elements > 1)
                    {
                        byte[] byteArray = new byte[item.elements];
                        Array.Copy(nvlist, offset, byteArray, 0, item.elements);
                        offset += (int)item.elements;

                        if (item.elements % 4 > 0)
                        {
                            offset += 4 - (int)(item.elements % 4);
                        }

                        item.value = byteArray;
                    }
                    else
                    {
                        item.value = nvlist[offset];
                        offset    += 4;
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_DOUBLE:
                    if (item.elements > 1)
                    {
                        double[] doubleArray = new double[item.elements];

                        for (int i = 0; i < item.elements; i++)
                        {
                            double temp = BigEndianBitConverter.ToDouble(nvlist, offset);
                            doubleArray[i] = temp;
                            offset        += 8;
                        }

                        item.value = doubleArray;
                    }
                    else
                    {
                        item.value = BigEndianBitConverter.ToDouble(nvlist, offset);
                        offset    += 8;
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_HRTIME:
                    if (item.elements > 1)
                    {
                        DateTime[] hrtimeArray = new DateTime[item.elements];

                        for (int i = 0; i < item.elements; i++)
                        {
                            DateTime temp =
                                DateHandlers.UnixHrTimeToDateTime(BigEndianBitConverter.ToUInt64(nvlist, offset));

                            hrtimeArray[i] = temp;
                            offset        += 8;
                        }

                        item.value = hrtimeArray;
                    }
                    else
                    {
                        item.value =
                            DateHandlers.UnixHrTimeToDateTime(BigEndianBitConverter.ToUInt64(nvlist, offset));

                        offset += 8;
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_INT16:
                case NVS_DataTypes.DATA_TYPE_INT16_ARRAY:
                    if (item.elements > 1)
                    {
                        short[] shortArray = new short[item.elements];

                        for (int i = 0; i < item.elements; i++)
                        {
                            short temp = BigEndianBitConverter.ToInt16(nvlist, offset);
                            shortArray[i] = temp;
                            offset       += 4;
                        }

                        item.value = shortArray;
                    }
                    else
                    {
                        item.value = BigEndianBitConverter.ToInt16(nvlist, offset);
                        offset    += 4;
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_INT32:
                case NVS_DataTypes.DATA_TYPE_INT32_ARRAY:
                    if (item.elements > 1)
                    {
                        int[] intArray = new int[item.elements];

                        for (int i = 0; i < item.elements; i++)
                        {
                            int temp = BigEndianBitConverter.ToInt32(nvlist, offset);
                            intArray[i] = temp;
                            offset     += 4;
                        }

                        item.value = intArray;
                    }
                    else
                    {
                        item.value = BigEndianBitConverter.ToInt32(nvlist, offset);
                        offset    += 4;
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_INT64:
                case NVS_DataTypes.DATA_TYPE_INT64_ARRAY:
                    if (item.elements > 1)
                    {
                        long[] longArray = new long[item.elements];

                        for (int i = 0; i < item.elements; i++)
                        {
                            long temp = BigEndianBitConverter.ToInt64(nvlist, offset);
                            longArray[i] = temp;
                            offset      += 8;
                        }

                        item.value = longArray;
                    }
                    else
                    {
                        item.value = BigEndianBitConverter.ToInt64(nvlist, offset);
                        offset    += 8;
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_INT8:
                case NVS_DataTypes.DATA_TYPE_INT8_ARRAY:
                    if (item.elements > 1)
                    {
                        sbyte[] sbyteArray = new sbyte[item.elements];

                        for (int i = 0; i < item.elements; i++)
                        {
                            sbyte temp = (sbyte)nvlist[offset];
                            sbyteArray[i] = temp;
                            offset++;
                        }

                        item.value = sbyteArray;

                        if (sbyteArray.Length % 4 > 0)
                        {
                            offset += 4 - (sbyteArray.Length % 4);
                        }
                    }
                    else
                    {
                        item.value = BigEndianBitConverter.ToInt64(nvlist, offset);
                        offset    += 4;
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_STRING:
                case NVS_DataTypes.DATA_TYPE_STRING_ARRAY:
                    if (item.elements > 1)
                    {
                        string[] stringArray = new string[item.elements];

                        for (int i = 0; i < item.elements; i++)
                        {
                            uint strLength = BigEndianBitConverter.ToUInt32(nvlist, offset);
                            offset += 4;
                            byte[] strBytes = new byte[strLength];
                            Array.Copy(nvlist, offset, strBytes, 0, strLength);
                            stringArray[i] = StringHandlers.CToString(strBytes);
                            offset        += (int)strLength;

                            if (strLength % 4 > 0)
                            {
                                offset += 4 - (int)(strLength % 4);
                            }
                        }

                        item.value = stringArray;
                    }
                    else
                    {
                        uint strLength = BigEndianBitConverter.ToUInt32(nvlist, offset);
                        offset += 4;
                        byte[] strBytes = new byte[strLength];
                        Array.Copy(nvlist, offset, strBytes, 0, strLength);
                        item.value = StringHandlers.CToString(strBytes);
                        offset    += (int)strLength;

                        if (strLength % 4 > 0)
                        {
                            offset += 4 - (int)(strLength % 4);
                        }
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_UINT16:
                case NVS_DataTypes.DATA_TYPE_UINT16_ARRAY:
                    if (item.elements > 1)
                    {
                        ushort[] ushortArray = new ushort[item.elements];

                        for (int i = 0; i < item.elements; i++)
                        {
                            ushort temp = BigEndianBitConverter.ToUInt16(nvlist, offset);
                            ushortArray[i] = temp;
                            offset        += 4;
                        }

                        item.value = ushortArray;
                    }
                    else
                    {
                        item.value = BigEndianBitConverter.ToUInt16(nvlist, offset);
                        offset    += 4;
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_UINT32:
                case NVS_DataTypes.DATA_TYPE_UINT32_ARRAY:
                    if (item.elements > 1)
                    {
                        uint[] uintArray = new uint[item.elements];

                        for (int i = 0; i < item.elements; i++)
                        {
                            uint temp = BigEndianBitConverter.ToUInt32(nvlist, offset);
                            uintArray[i] = temp;
                            offset      += 4;
                        }

                        item.value = uintArray;
                    }
                    else
                    {
                        item.value = BigEndianBitConverter.ToUInt32(nvlist, offset);
                        offset    += 4;
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_UINT64:
                case NVS_DataTypes.DATA_TYPE_UINT64_ARRAY:
                    if (item.elements > 1)
                    {
                        ulong[] ulongArray = new ulong[item.elements];

                        for (int i = 0; i < item.elements; i++)
                        {
                            ulong temp = BigEndianBitConverter.ToUInt64(nvlist, offset);
                            ulongArray[i] = temp;
                            offset       += 8;
                        }

                        item.value = ulongArray;
                    }
                    else
                    {
                        item.value = BigEndianBitConverter.ToUInt64(nvlist, offset);
                        offset    += 8;
                    }

                    break;

                case NVS_DataTypes.DATA_TYPE_NVLIST:
                    if (item.elements > 1)
                    {
                        goto default;
                    }

                    byte[] subListBytes = new byte[item.encodedSize - (offset - currOff)];
                    Array.Copy(nvlist, offset, subListBytes, 0, subListBytes.Length);

                    if (DecodeNvList(subListBytes, out Dictionary <string, NVS_Item> subList, true, littleEndian))
                    {
                        item.value = subList;
                    }
                    else
                    {
                        goto default;
                    }

                    offset = (int)(currOff + item.encodedSize);

                    break;

                default:
                    byte[] unknown = new byte[item.encodedSize - (offset - currOff)];
                    Array.Copy(nvlist, offset, unknown, 0, unknown.Length);
                    item.value = unknown;
                    offset     = (int)(currOff + item.encodedSize);

                    break;
                }

                decodedNvList.Add(item.name, item);
            }

            return(decodedNvList.Count > 0);
        }
        private void ParseMp3Frames(byte[] buffer)
        {
            var mpeg1BitRate     = new[] { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 };
            var mpeg2XBitRate    = new[] { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 };
            var mpeg1SampleRate  = new[] { 44100, 48000, 32000, 0 };
            var mpeg20SampleRate = new[] { 22050, 24000, 16000, 0 };
            var mpeg25SampleRate = new[] { 11025, 12000, 8000, 0 };

            int offset = 0;
            int length = buffer.Length;

            while (length >= 4)
            {
                int mpegVersion, sampleRate, channelMode;

                ulong header = (ulong)BigEndianBitConverter.ToUInt32(buffer, offset) << 32;

                if (BitHelper.Read(ref header, 11) != 0x7FF)
                {
                    break;
                }

                mpegVersion = BitHelper.Read(ref header, 2);
                int layer = BitHelper.Read(ref header, 2);
                BitHelper.Read(ref header, 1);
                int bitRate = BitHelper.Read(ref header, 4);
                sampleRate = BitHelper.Read(ref header, 2);
                int padding = BitHelper.Read(ref header, 1);
                BitHelper.Read(ref header, 1);
                channelMode = BitHelper.Read(ref header, 2);

                if (mpegVersion == 1 || layer != 1 || bitRate == 0 || bitRate == 15 || sampleRate == 3)
                {
                    break;
                }

                bitRate = (mpegVersion == 3 ? mpeg1BitRate[bitRate] : mpeg2XBitRate[bitRate]) * 1000;

                switch (mpegVersion)
                {
                case 2:
                    sampleRate = mpeg20SampleRate[sampleRate];
                    break;

                case 3:
                    sampleRate = mpeg1SampleRate[sampleRate];
                    break;

                default:
                    sampleRate = mpeg25SampleRate[sampleRate];
                    break;
                }

                int frameLenght = GetFrameLength(mpegVersion, bitRate, sampleRate, padding);

                if (frameLenght > length)
                {
                    break;
                }

                bool isVbrHeaderFrame = false;

                if (_frameOffsets.Count == 0)
                {
                    // Check for an existing VBR header just to be safe (I haven't seen any in FLVs)
                    int o = offset + GetFrameDataOffset(mpegVersion, channelMode);

                    if (BigEndianBitConverter.ToUInt32(buffer, o) == 0x58696E67)
                    {
                        // "Xing"
                        isVbrHeaderFrame = true;
                        _delayWrite      = false;
                        _hasVbrHeader    = true;
                    }
                }

                if (!isVbrHeaderFrame)
                {
                    if (_firstBitRate == 0)
                    {
                        _firstBitRate     = bitRate;
                        _mpegVersion      = mpegVersion;
                        _sampleRate       = sampleRate;
                        _channelMode      = channelMode;
                        _firstFrameHeader = BigEndianBitConverter.ToUInt32(buffer, offset);
                    }

                    else if (!_isVbr && bitRate != _firstBitRate)
                    {
                        _isVbr = true;

                        if (!_hasVbrHeader)
                        {
                            if (_delayWrite)
                            {
                                WriteVbrHeader(true);
                                _writeVbrHeader = true;
                                _delayWrite     = false;
                            }

                            else
                            {
                                _warnings.Add("Detected VBR too late, cannot add VBR header.");
                            }
                        }
                    }
                }

                _frameOffsets.Add(_totalFrameLength + (uint)offset);

                offset += frameLenght;
                length -= frameLenght;
            }

            _totalFrameLength += (uint)buffer.Length;
        }
Beispiel #10
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("IBM437");
            information = "";

            StringBuilder sb = new StringBuilder();

            XmlFsType = new FileSystemType();

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

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

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

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

            XmlFsType.Bootable = bootable;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                break;
            }

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

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

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

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

                    extraInfo = atariSb.ToString();
                }

                break;
            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                    rootDirectory = rootMs.ToArray();
                }

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

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

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

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

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

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

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

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

                    break;
                }
            }

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

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

            information = sb.ToString();
        }
Beispiel #11
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");

            options ??= GetDefaultOptions();

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

            _volMdb = new MasterDirectoryBlock();

            _mdbBlocks  = _device.ReadSector(2 + _partitionStart);
            _bootBlocks = _device.ReadSector(0 + _partitionStart);

            _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     = (AppleCommon.VolumeAttributes)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);
            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 != AppleCommon.BB_MAGIC)
            {
                _bootBlocks = null;
            }

            XmlFsType = new FileSystemType();

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

            XmlFsType.Bootable    = bbSig == AppleCommon.BB_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 #12
0
 public override byte[] encode(object data, BigEndianBitConverter converter)
 {
     //TODO: World encoding
     return(new byte[] {});
 }
Beispiel #13
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            stream.Seek(0, SeekOrigin.Begin);
            byte[] buffer  = new byte[0x58];
            byte[] pString = new byte[64];
            stream.Read(buffer, 0, 0x58);
            IsWriting = false;

            // Incorrect pascal string length, not DC42
            if (buffer[0] > 63)
            {
                return(false);
            }

            header = new Dc42Header();

            Array.Copy(buffer, 0, pString, 0, 64);
            header.DiskName     = StringHandlers.PascalToString(pString, Encoding.GetEncoding("macintosh"));
            header.DataSize     = BigEndianBitConverter.ToUInt32(buffer, 0x40);
            header.TagSize      = BigEndianBitConverter.ToUInt32(buffer, 0x44);
            header.DataChecksum = BigEndianBitConverter.ToUInt32(buffer, 0x48);
            header.TagChecksum  = BigEndianBitConverter.ToUInt32(buffer, 0x4C);
            header.Format       = buffer[0x50];
            header.FmtByte      = buffer[0x51];
            header.Valid        = buffer[0x52];
            header.Reserved     = buffer[0x53];

            DicConsole.DebugWriteLine("DC42 plugin", "header.diskName = \"{0}\"", header.DiskName);
            DicConsole.DebugWriteLine("DC42 plugin", "header.dataSize = {0} bytes", header.DataSize);
            DicConsole.DebugWriteLine("DC42 plugin", "header.tagSize = {0} bytes", header.TagSize);
            DicConsole.DebugWriteLine("DC42 plugin", "header.dataChecksum = 0x{0:X8}", header.DataChecksum);
            DicConsole.DebugWriteLine("DC42 plugin", "header.tagChecksum = 0x{0:X8}", header.TagChecksum);
            DicConsole.DebugWriteLine("DC42 plugin", "header.format = 0x{0:X2}", header.Format);
            DicConsole.DebugWriteLine("DC42 plugin", "header.fmtByte = 0x{0:X2}", header.FmtByte);
            DicConsole.DebugWriteLine("DC42 plugin", "header.valid = {0}", header.Valid);
            DicConsole.DebugWriteLine("DC42 plugin", "header.reserved = {0}", header.Reserved);

            if (header.Valid != 1 || header.Reserved != 0)
            {
                return(false);
            }

            // Some versions seem to incorrectly create little endian fields
            if (header.DataSize + header.TagSize + 0x54 != imageFilter.GetDataForkLength() &&
                header.Format != kSigmaFormatTwiggy)
            {
                header.DataSize     = BitConverter.ToUInt32(buffer, 0x40);
                header.TagSize      = BitConverter.ToUInt32(buffer, 0x44);
                header.DataChecksum = BitConverter.ToUInt32(buffer, 0x48);
                header.TagChecksum  = BitConverter.ToUInt32(buffer, 0x4C);

                if (header.DataSize + header.TagSize + 0x54 != imageFilter.GetDataForkLength() &&
                    header.Format != kSigmaFormatTwiggy)
                {
                    return(false);
                }
            }

            if (header.Format != kSonyFormat400K && header.Format != kSonyFormat800K &&
                header.Format != kSonyFormat720K && header.Format != kSonyFormat1440K &&
                header.Format != kSonyFormat1680K && header.Format != kSigmaFormatTwiggy &&
                header.Format != kNotStandardFormat)
            {
                DicConsole.DebugWriteLine("DC42 plugin", "Unknown header.format = 0x{0:X2} value", header.Format);

                return(false);
            }

            if (header.FmtByte != kSonyFmtByte400K && header.FmtByte != kSonyFmtByte800K &&
                header.FmtByte != kSonyFmtByte800KIncorrect && header.FmtByte != kSonyFmtByteProDos &&
                header.FmtByte != kInvalidFmtByte && header.FmtByte != kSigmaFmtByteTwiggy &&
                header.FmtByte != kFmtNotStandard && header.FmtByte != kMacOSXFmtByte)
            {
                DicConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.fmtByte = 0x{0:X2} value", header.FmtByte);

                return(false);
            }

            if (header.FmtByte == kInvalidFmtByte)
            {
                DicConsole.DebugWriteLine("DC42 plugin", "Image says it's unformatted");

                return(false);
            }

            dataOffset           = 0x54;
            tagOffset            = header.TagSize != 0 ? 0x54 + header.DataSize : 0;
            imageInfo.SectorSize = 512;
            bptag           = (uint)(header.TagSize != 0 ? 12 : 0);
            dc42ImageFilter = imageFilter;

            imageInfo.Sectors = header.DataSize / 512;

            if (header.TagSize != 0)
            {
                bptag = (uint)(header.TagSize / imageInfo.Sectors);
                DicConsole.DebugWriteLine("DC42 plugin", "bptag = {0} bytes", bptag);

                if (bptag != 12 && bptag != 20 && bptag != 24)
                {
                    DicConsole.DebugWriteLine("DC42 plugin", "Unknown tag size");
                    return(false);
                }

                imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag);
            }

            imageInfo.ImageSize            = imageInfo.Sectors * imageInfo.SectorSize + imageInfo.Sectors * bptag;
            imageInfo.CreationTime         = imageFilter.GetCreationTime();
            imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();
            imageInfo.MediaTitle           = header.DiskName;

            switch (header.Format)
            {
            case kSonyFormat400K:
                imageInfo.MediaType = imageInfo.Sectors == 1600 ? MediaType.AppleSonyDS : MediaType.AppleSonySS;
                break;

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

            case kSonyFormat720K:
                imageInfo.MediaType = MediaType.DOS_35_DS_DD_9;
                break;

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

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

            case kSigmaFormatTwiggy:
                imageInfo.MediaType = MediaType.AppleFileWare;
                break;

            case kNotStandardFormat:
                switch (imageInfo.Sectors)
                {
                case 9728:
                    imageInfo.MediaType = MediaType.AppleProfile;
                    break;

                case 19456:
                    imageInfo.MediaType = MediaType.AppleProfile;
                    break;

                case 38912:
                    imageInfo.MediaType = MediaType.AppleWidget;
                    break;

                case 39040:
                    imageInfo.MediaType = MediaType.AppleHD20;
                    break;

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

                break;

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

            if (imageInfo.MediaType == MediaType.AppleFileWare)
            {
                byte[] data = new byte[header.DataSize];
                byte[] tags = new byte[header.TagSize];

                twiggyCache     = new byte[header.DataSize];
                twiggyCacheTags = new byte[header.TagSize];
                twiggy          = true;

                Stream datastream = imageFilter.GetDataForkStream();
                datastream.Seek(dataOffset, SeekOrigin.Begin);
                datastream.Read(data, 0, (int)header.DataSize);

                Stream tagstream = imageFilter.GetDataForkStream();
                tagstream.Seek(tagOffset, SeekOrigin.Begin);
                tagstream.Read(tags, 0, (int)header.TagSize);

                ushort mfsMagic     = BigEndianBitConverter.ToUInt16(data, data.Length / 2 + 0x400);
                ushort mfsAllBlocks = BigEndianBitConverter.ToUInt16(data, data.Length / 2 + 0x412);

                // Detect a Macintosh Twiggy
                if (mfsMagic == 0xD2D7 && mfsAllBlocks == 422)
                {
                    DicConsole.DebugWriteLine("DC42 plugin", "Macintosh Twiggy detected, reversing disk sides");
                    Array.Copy(data, header.DataSize / 2, twiggyCache, 0, header.DataSize / 2);
                    Array.Copy(tags, header.TagSize / 2, twiggyCacheTags, 0, header.TagSize / 2);
                    Array.Copy(data, 0, twiggyCache, header.DataSize / 2, header.DataSize / 2);
                    Array.Copy(tags, 0, twiggyCacheTags, header.TagSize / 2, header.TagSize / 2);
                }
                else
                {
                    DicConsole.DebugWriteLine("DC42 plugin", "Lisa Twiggy detected, reversing second half of disk");
                    Array.Copy(data, 0, twiggyCache, 0, header.DataSize / 2);
                    Array.Copy(tags, 0, twiggyCacheTags, 0, header.TagSize / 2);

                    int copiedSectors = 0;
                    int sectorsToCopy = 0;

                    for (int i = 0; i < 46; i++)
                    {
                        if (i >= 0 && i <= 3)
                        {
                            sectorsToCopy = 22;
                        }
                        if (i >= 4 && i <= 10)
                        {
                            sectorsToCopy = 21;
                        }
                        if (i >= 11 && i <= 16)
                        {
                            sectorsToCopy = 20;
                        }
                        if (i >= 17 && i <= 22)
                        {
                            sectorsToCopy = 19;
                        }
                        if (i >= 23 && i <= 28)
                        {
                            sectorsToCopy = 18;
                        }
                        if (i >= 29 && i <= 34)
                        {
                            sectorsToCopy = 17;
                        }
                        if (i >= 35 && i <= 41)
                        {
                            sectorsToCopy = 16;
                        }
                        if (i >= 42 && i <= 45)
                        {
                            sectorsToCopy = 15;
                        }

                        Array.Copy(data, header.DataSize / 2 + copiedSectors * 512, twiggyCache,
                                   twiggyCache.Length - copiedSectors * 512 - sectorsToCopy * 512, sectorsToCopy * 512);
                        Array.Copy(tags, header.TagSize / 2 + copiedSectors * bptag,
                                   twiggyCacheTags,
                                   twiggyCacheTags.Length - copiedSectors * bptag - sectorsToCopy * bptag,
                                   sectorsToCopy * bptag);

                        copiedSectors += sectorsToCopy;
                    }
                }
            }

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

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

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

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

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

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

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

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

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

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

                    if (rsrcFork.ContainsKey(0x64437079))
                    {
                        Resource dCpyRsrc = rsrcFork.GetResource(0x64437079);
                        if (dCpyRsrc != null)
                        {
                            string dCpy = StringHandlers.PascalToString(dCpyRsrc.GetResource(dCpyRsrc.GetIds()[0]),
                                                                        Encoding.GetEncoding("macintosh"));
                            Regex dCpyEx    = new Regex(REGEX_DCPY);
                            Match dCpyMatch = dCpyEx.Match(dCpy);

                            if (dCpyMatch.Success)
                            {
                                imageInfo.Application        = dCpyMatch.Groups["application"].Value;
                                imageInfo.ApplicationVersion = dCpyMatch.Groups["version"].Value;
                            }
                        }
                    }
                }
            }
            catch (InvalidCastException) { }

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

            imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
            DicConsole.VerboseWriteLine("DiskCopy 4.2 image contains a disk of type {0}", imageInfo.MediaType);

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

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

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

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

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

            case MediaType.AppleProfile:
                switch (imageInfo.Sectors)
                {
                case 9728:
                    imageInfo.Cylinders = 152;
                    break;

                case 19456:
                    imageInfo.Cylinders = 304;
                    break;
                }

                imageInfo.Heads           = 4;
                imageInfo.SectorsPerTrack = 16;
                break;

            case MediaType.AppleWidget:
                imageInfo.Cylinders       = 608;
                imageInfo.Heads           = 2;
                imageInfo.SectorsPerTrack = 16;
                break;

            case MediaType.AppleHD20:
                imageInfo.Cylinders       = 610;
                imageInfo.Heads           = 2;
                imageInfo.SectorsPerTrack = 16;
                break;

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

            return(true);
        }
Beispiel #14
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            if (2 + partition.Start >= partition.End)
            {
                return(false);
            }

            byte[] mdbSector;
            ushort drSigWord;

            if (imagePlugin.Info.SectorSize == 2352 ||
                imagePlugin.Info.SectorSize == 2448 ||
                imagePlugin.Info.SectorSize == 2048)
            {
                mdbSector = imagePlugin.ReadSectors(partition.Start, 2);

                foreach (int offset in new[]
                {
                    0, 0x200, 0x400, 0x600, 0x800, 0xA00
                })
                {
                    if (mdbSector.Length < offset + 0x7C + 2)
                    {
                        continue;
                    }

                    drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, offset);

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

                    drSigWord =
                        BigEndianBitConverter.ToUInt16(mdbSector, offset + 0x7C); // Seek to embedded HFS+ signature

                    return(drSigWord != AppleCommon.HFSP_MAGIC);
                }
            }
            else
            {
                mdbSector = imagePlugin.ReadSector(2 + partition.Start);

                if (mdbSector.Length < 0x7C + 2)
                {
                    return(false);
                }

                drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0);

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

                drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x7C); // Seek to embedded HFS+ signature

                return(drSigWord != AppleCommon.HFSP_MAGIC);
            }

            return(false);
        }
Beispiel #15
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            if (partition.Start >= partition.End)
            {
                return(false);
            }

            BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;

            // Boot block is unless defined otherwise, 2 blocks
            // Funny, you may need boot block to find root block if it's not in standard place just to know size of
            // block size and then read the whole boot block.
            // However while you can set a block size different from the sector size on formatting, the bootblock block
            // size for floppies is the sector size, and for RDB is usually is the hard disk sector size,
            // so this is not entirely wrong...
            byte[]    sector = imagePlugin.ReadSectors(0 + partition.Start, 2);
            BootBlock bblk   = BigEndianMarshal.ByteArrayToStructureBigEndian <BootBlock>(sector);

            // AROS boot floppies...
            if (sector.Length >= 512 && sector[510] == 0x55 && sector[511] == 0xAA &&
                (bblk.diskType & FFS_MASK) != FFS_MASK && (bblk.diskType & MUFS_MASK) != MUFS_MASK)
            {
                sector = imagePlugin.ReadSectors(1 + partition.Start, 2);
                bblk   = BigEndianMarshal.ByteArrayToStructureBigEndian <BootBlock>(sector);
            }

            // Not FFS or MuFS?
            if ((bblk.diskType & FFS_MASK) != FFS_MASK && (bblk.diskType & MUFS_MASK) != MUFS_MASK)
            {
                return(false);
            }

            // Clear checksum on sector
            sector[4] = sector[5] = sector[6] = sector[7] = 0;
            uint bsum = AmigaBootChecksum(sector);

            DicConsole.DebugWriteLine("AmigaDOS plugin", "bblk.checksum = 0x{0:X8}", bblk.checksum);
            DicConsole.DebugWriteLine("AmigaDOS plugin", "bsum = 0x{0:X8}", bsum);

            ulong bRootPtr = 0;

            // If bootblock is correct, let's take its rootblock pointer
            if (bsum == bblk.checksum)
            {
                bRootPtr = bblk.root_ptr + partition.Start;
                DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr);
            }

            ulong[] rootPtrs =
            {
                bRootPtr + partition.Start,                                      (partition.End - partition.Start + 1) / 2 + partition.Start - 2,
                (partition.End - partition.Start + 1) / 2 + partition.Start - 1,
                (partition.End - partition.Start + 1) / 2 + partition.Start,
                (partition.End - partition.Start + 1) / 2 + partition.Start + 4
            };

            RootBlock rblk = new RootBlock();

            // So to handle even number of sectors
            foreach (ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start))
            {
                DicConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr);

                sector = imagePlugin.ReadSector(rootPtr);

                rblk.type = BigEndianBitConverter.ToUInt32(sector, 0x00);
                DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.type = {0}", rblk.type);
                if (rblk.type != TYPE_HEADER)
                {
                    continue;
                }

                rblk.hashTableSize = BigEndianBitConverter.ToUInt32(sector, 0x0C);

                DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.hashTableSize = {0}", rblk.hashTableSize);

                uint blockSize       = (rblk.hashTableSize + 56) * 4;
                uint sectorsPerBlock = (uint)(blockSize / sector.Length);

                DicConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize);
                DicConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock);

                if (blockSize % sector.Length > 0)
                {
                    sectorsPerBlock++;
                }

                if (rootPtr + sectorsPerBlock >= partition.End)
                {
                    continue;
                }

                sector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock);

                // Clear checksum on sector
                rblk.checksum = BigEndianBitConverter.ToUInt32(sector, 20);
                sector[20]    = sector[21] = sector[22] = sector[23] = 0;
                uint rsum = AmigaChecksum(sector);

                DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.checksum = 0x{0:X8}", rblk.checksum);
                DicConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum);

                rblk.sec_type = BigEndianBitConverter.ToUInt32(sector, sector.Length - 4);
                DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.sec_type = {0}", rblk.sec_type);

                if (rblk.sec_type == SUBTYPE_ROOT && rblk.checksum == rsum)
                {
                    return(true);
                }
            }

            return(false);
        }
Beispiel #16
0
        /// <summary>Mounts an Apple Lisa filesystem</summary>
        public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
                           Dictionary <string, string> options, string @namespace)
        {
            try
            {
                device   = imagePlugin;
                Encoding = new LisaRoman();

                // Lisa OS is unable to work on disks without tags.
                // This code is designed like that.
                // However with some effort the code may be modified to ignore them.
                if (device.Info.ReadableSectorTags == null ||
                    !device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag))
                {
                    AaruConsole.DebugWriteLine("LisaFS plugin", "Underlying device does not support Lisa tags");

                    return(Errno.InOutError);
                }

                // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors
                if (device.Info.Sectors < 800)
                {
                    AaruConsole.DebugWriteLine("LisaFS plugin", "Device is too small");

                    return(Errno.InOutError);
                }

                // MDDF cannot be at end of device, of course
                volumePrefix = device.Info.Sectors;

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

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

                    if (volumePrefix == device.Info.Sectors &&
                        searchTag.FileId == FILEID_LOADER_SIGNED)
                    {
                        volumePrefix = i - 1;
                    }

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

                    devTagSize = device.ReadSectorTag(i, SectorTagType.AppleSectorTag).Length;

                    byte[] sector = device.ReadSector(i);
                    mddf = new MDDF();
                    byte[] pString = new byte[33];

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

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

                    // Check that the MDDF is correct
                    if (mddf.mddf_block != i - volumePrefix ||
                        mddf.vol_size > device.Info.Sectors ||
                        mddf.vol_size - 1 != mddf.volsize_minus_one ||
                        mddf.vol_size - i - 1 != mddf.volsize_minus_mddf_minus_one - volumePrefix ||
                        mddf.datasize > mddf.blocksize ||
                        mddf.blocksize < device.Info.SectorSize ||
                        mddf.datasize != device.Info.SectorSize)
                    {
                        AaruConsole.DebugWriteLine("LisaFS plugin", "Incorrect MDDF found");

                        return(Errno.InvalidArgument);
                    }

                    // Check MDDF version
                    switch (mddf.fsversion)
                    {
                    case LISA_V1:
                        AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v1");

                        break;

                    case LISA_V2:
                        AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v2");

                        break;

                    case LISA_V3:
                        AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v3");

                        break;

                    default:
                        AaruConsole.ErrorWriteLine("Cannot mount LisaFS version {0}", mddf.fsversion.ToString());

                        return(Errno.NotSupported);
                    }

                    // Initialize caches
                    extentCache     = new Dictionary <short, ExtentFile>();
                    systemFileCache = new Dictionary <short, byte[]>();
                    fileCache       = new Dictionary <short, byte[]>();

                    //catalogCache = new Dictionary<short, List<CatalogEntry>>();
                    fileSizeCache = new Dictionary <short, int>();

                    mounted = true;

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

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

                    if (debug)
                    {
                        printedExtents = new List <short>();
                    }

                    // Read the S-Records file
                    Errno error = ReadSRecords();

                    if (error != Errno.NoError)
                    {
                        AaruConsole.ErrorWriteLine("Error {0} reading S-Records file.", error);

                        return(error);
                    }

                    directoryDtcCache = new Dictionary <short, DateTime>
                    {
                        {
                            DIRID_ROOT, mddf.dtcc
                        }
                    };

                    // Read the Catalog File
                    error = ReadCatalog();

                    if (error != Errno.NoError)
                    {
                        AaruConsole.DebugWriteLine("LisaFS plugin", "Cannot read Catalog File, error {0}",
                                                   error.ToString());

                        mounted = false;

                        return(error);
                    }

                    // If debug, cache system files
                    if (debug)
                    {
                        error = ReadSystemFile(FILEID_BOOT_SIGNED, out _);

                        if (error != Errno.NoError)
                        {
                            AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read boot blocks");
                            mounted = false;

                            return(error);
                        }

                        error = ReadSystemFile(FILEID_LOADER_SIGNED, out _);

                        if (error != Errno.NoError)
                        {
                            AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read boot loader");
                            mounted = false;

                            return(error);
                        }

                        error = ReadSystemFile((short)FILEID_MDDF, out _);

                        if (error != Errno.NoError)
                        {
                            AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read MDDF");
                            mounted = false;

                            return(error);
                        }

                        error = ReadSystemFile((short)FILEID_BITMAP, out _);

                        if (error != Errno.NoError)
                        {
                            AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read volume bitmap");
                            mounted = false;

                            return(error);
                        }

                        error = ReadSystemFile((short)FILEID_SRECORD, out _);

                        if (error != Errno.NoError)
                        {
                            AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read S-Records file");
                            mounted = false;

                            return(error);
                        }
                    }

                    // Create XML metadata for mounted filesystem
                    XmlFsType = new FileSystemType();

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

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

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

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

                    return(Errno.NoError);
                }

                AaruConsole.DebugWriteLine("LisaFS plugin", "Not a Lisa filesystem");

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

                return(Errno.InOutError);
            }
        }
Beispiel #17
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;
                }
            }

            HFS_MasterDirectoryBlock MDB =
                BigEndianMarshal.ByteArrayToStructureBigEndian <HFS_MasterDirectoryBlock>(mdbSector);
            HFS_BootBlock BB = BigEndianMarshal.ByteArrayToStructureBigEndian <HFS_BootBlock>(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 = (int)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 #18
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();

            BeSuperBlock besb = new BeSuperBlock();

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

            if (littleEndian)
            {
                besb = Marshal.ByteArrayToStructureLittleEndian <BeSuperBlock>(sbSector);
            }
            else
            {
                besb = Marshal.ByteArrayToStructureBigEndian <BeSuperBlock>(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 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);

            // On Apple II, it's little endian
            // TODO: Fix

            /*BigEndianBitConverter.IsLittleEndian =
             *  multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian;*/

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

            Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8);

            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 #20
0
        public bool Close()
        {
            if (!IsWriting)
            {
                ErrorMessage = "Image is not opened for writing";

                return(false);
            }

            if (currentChunk.type != CHUNK_TYPE_NOCOPY)
            {
                currentChunk.length = currentChunk.sectors * 512;
            }

            chunks.Add(currentChunk.sector, currentChunk);

            chunks.Add(imageInfo.Sectors, new BlockChunk
            {
                type = CHUNK_TYPE_END, sector = imageInfo.Sectors
            });

            var bHdr = new BlockHeader
            {
                signature    = CHUNK_SIGNATURE, version = 1, sectorCount = imageInfo.Sectors,
                checksumType = UDIF_CHECKSUM_TYPE_CRC32, checksumLen = 32,
                checksum     = BitConverter.ToUInt32(dataForkChecksum.Final().Reverse().ToArray(), 0),
                chunks       = (uint)chunks.Count
            };

            var chunkMs = new MemoryStream();

            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.signature), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.version), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.sectorStart), 0, 8);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.sectorCount), 0, 8);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.dataOffset), 0, 8);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.buffers), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.descriptor), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved1), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved2), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved3), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved4), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved5), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.reserved6), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.checksumType), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.checksumLen), 0, 4);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.checksum), 0, 4);
            chunkMs.Write(new byte[124], 0, 124);
            chunkMs.Write(BigEndianBitConverter.GetBytes(bHdr.chunks), 0, 4);

            foreach (BlockChunk chunk in chunks.Values)
            {
                chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.type), 0, 4);
                chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.comment), 0, 4);
                chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.sector), 0, 8);
                chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.sectors), 0, 8);
                chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.offset), 0, 8);
                chunkMs.Write(BigEndianBitConverter.GetBytes(chunk.length), 0, 8);
            }

            byte[] plist = Encoding.UTF8.GetBytes(new NSDictionary
            {
                {
                    "resource-fork", new NSDictionary
                    {
                        {
                            "blkx", new NSArray
                            {
                                new NSDictionary
                                {
                                    {
                                        "Attributes", "0x0050"
                                    },
                                    {
                                        "CFName", "whole disk (Aaru : 0)"
                                    },
                                    {
                                        "Data", chunkMs.ToArray()
                                    },
                                    {
                                        "ID", "0"
                                    },
                                    {
                                        "Name", "whole disk (Aaru : 0)"
                                    }
                                }
                            }
                        }
                    }
                }
            }.ToXmlPropertyList());

            footer = new UdifFooter
            {
                signature      = UDIF_SIGNATURE, version = 4,
                headerSize     = 512, flags = 1,
                dataForkLen    = (ulong)writingStream.Length, segmentNumber = 1, segmentCount = 1,
                segmentId      = Guid.NewGuid(), dataForkChkType = UDIF_CHECKSUM_TYPE_CRC32,
                dataForkChkLen = 32,
                dataForkChk    = BitConverter.ToUInt32(dataForkChecksum.Final().Reverse().ToArray(), 0),
                plistOff       = (ulong)writingStream.Length, plistLen = (ulong)plist.Length,

                // TODO: Find how is this calculated

                /*masterChkType   = 2,
                 * masterChkLen    = 32,
                 * masterChk       = BitConverter.ToUInt32(masterChecksum.Final().Reverse().ToArray(), 0),*/
                imageVariant = 2, sectorCount = imageInfo.Sectors
            };

            writingStream.Seek(0, SeekOrigin.End);
            writingStream.Write(plist, 0, plist.Length);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.signature), 0, 4);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.version), 0, 4);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.headerSize), 0, 4);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.flags), 0, 4);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.runningDataForkOff), 0, 8);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkOff), 0, 8);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkLen), 0, 8);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.rsrcForkOff), 0, 8);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.rsrcForkLen), 0, 8);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.segmentNumber), 0, 4);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.segmentCount), 0, 4);
            writingStream.Write(footer.segmentId.ToByteArray(), 0, 16);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkChkType), 0, 4);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkChkLen), 0, 4);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.dataForkChk), 0, 4);
            writingStream.Write(new byte[124], 0, 124);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.plistOff), 0, 8);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.plistLen), 0, 8);
            writingStream.Write(new byte[120], 0, 120);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.masterChkType), 0, 4);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.masterChkLen), 0, 4);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.masterChk), 0, 4);
            writingStream.Write(new byte[124], 0, 124);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.imageVariant), 0, 4);
            writingStream.Write(BigEndianBitConverter.GetBytes(footer.sectorCount), 0, 8);
            writingStream.Write(new byte[12], 0, 12);

            writingStream.Flush();
            writingStream.Close();

            IsWriting    = false;
            ErrorMessage = "";

            return(true);
        }
        /// <summary>
        ///     Returns a byte array of the hash value.
        /// </summary>
        public byte[] Final()
        {
            uint finalSum = (uint)((sum2 << 16) | sum1);

            return(BigEndianBitConverter.GetBytes(finalSum));
        }
Beispiel #22
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = Encoding.BigEndianUnicode;
            information = "";

            var vh = new VolumeHeader();

            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 == AppleCommon.HFS_MAGIC)                          // "BD"
            {
                drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x47C); // Read embedded HFS+ signature

                if (drSigWord == AppleCommon.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 != AppleCommon.HFSP_MAGIC &&
                vh.signature != AppleCommon.HFSX_MAGIC)
            {
                return;
            }

            var 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 = Marshal.ByteArrayToStructureBigEndian <VolumeHeader>(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 = 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 #23
0
        public bool Close()
        {
            if (!IsWriting)
            {
                ErrorMessage = "Image is not opened for writing";
                return(false);
            }

            Version thisVersion = GetType().Assembly.GetName().Version;

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

                while (imageInfo.Cylinders == 0)
                {
                    imageInfo.Heads--;

                    if (imageInfo.Heads == 0)
                    {
                        imageInfo.SectorsPerTrack--;
                        imageInfo.Heads = 16;
                    }

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

                    if (imageInfo.Cylinders == 0 && imageInfo.Heads == 0 && imageInfo.SectorsPerTrack == 0)
                    {
                        break;
                    }
                }
            }

            HardDiskFooter footer = new HardDiskFooter
            {
                Cookie    = IMAGE_COOKIE,
                Features  = FEATURES_RESERVED,
                Version   = VERSION1,
                Timestamp =
                    (uint)(DateTime.Now - new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds,
                CreatorApplication = CREATOR_DISCIMAGECHEF,
                CreatorVersion     =
                    (uint)(((thisVersion.Major & 0xFF) << 24) + ((thisVersion.Minor & 0xFF) << 16) +
                           ((thisVersion.Build & 0xFF) << 8) + (thisVersion.Revision & 0xFF)),
                CreatorHostOs =
                    DetectOS.GetRealPlatformID() == PlatformID.MacOSX ? CREATOR_MACINTOSH : CREATOR_WINDOWS,
                DiskType     = TYPE_FIXED,
                UniqueId     = Guid.NewGuid(),
                DiskGeometry =
                    ((imageInfo.Cylinders & 0xFFFF) << 16) + ((imageInfo.Heads & 0xFF) << 8) +
                    (imageInfo.SectorsPerTrack & 0xFF),
                OriginalSize = imageInfo.Sectors * 512,
                CurrentSize  = imageInfo.Sectors * 512
            };

            footer.Offset = footer.DiskType == TYPE_FIXED ? ulong.MaxValue : 512;

            byte[] footerBytes = new byte[512];
            Array.Copy(BigEndianBitConverter.GetBytes(footer.Cookie), 0, footerBytes, 0x00, 8);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.Features), 0, footerBytes, 0x08, 4);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.Version), 0, footerBytes, 0x0C, 4);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.Offset), 0, footerBytes, 0x10, 8);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.Timestamp), 0, footerBytes, 0x18, 4);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.CreatorApplication), 0, footerBytes, 0x1C, 4);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.CreatorVersion), 0, footerBytes, 0x20, 4);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.CreatorHostOs), 0, footerBytes, 0x24, 4);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.OriginalSize), 0, footerBytes, 0x28, 8);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.CurrentSize), 0, footerBytes, 0x30, 8);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.DiskGeometry), 0, footerBytes, 0x38, 4);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.DiskType), 0, footerBytes, 0x3C, 4);
            Array.Copy(footer.UniqueId.ToByteArray(), 0, footerBytes, 0x44, 4);

            footer.Checksum = VhdChecksum(footerBytes);
            Array.Copy(BigEndianBitConverter.GetBytes(footer.Checksum), 0, footerBytes, 0x40, 4);

            writingStream.Seek((long)(footer.DiskType == TYPE_FIXED ? footer.OriginalSize : 0), SeekOrigin.Begin);
            writingStream.Write(footerBytes, 0, 512);

            writingStream.Flush();
            writingStream.Close();

            IsWriting    = false;
            ErrorMessage = "";
            return(true);
        }
Beispiel #24
0
        public DeviceInfo(Device dev)
        {
            Type                  = dev.Type;
            Manufacturer          = dev.Manufacturer;
            Model                 = dev.Model;
            FirmwareRevision      = dev.FirmwareRevision;
            Serial                = dev.Serial;
            ScsiType              = dev.ScsiType;
            IsRemovable           = dev.IsRemovable;
            IsUsb                 = dev.IsUsb;
            UsbVendorId           = dev.UsbVendorId;
            UsbProductId          = dev.UsbProductId;
            UsbDescriptors        = dev.UsbDescriptors;
            UsbManufacturerString = dev.UsbManufacturerString;
            UsbProductString      = dev.UsbProductString;
            UsbSerialString       = dev.UsbSerialString;
            IsFireWire            = dev.IsFireWire;
            FireWireGuid          = dev.FireWireGuid;
            FireWireModel         = dev.FireWireModel;
            FireWireModelName     = dev.FireWireModelName;
            FireWireVendor        = dev.FireWireVendor;
            FireWireVendorName    = dev.FireWireVendorName;
            IsCompactFlash        = dev.IsCompactFlash;
            IsPcmcia              = dev.IsPcmcia;
            Cis = dev.Cis;

            switch (dev.Type)
            {
            case DeviceType.ATA:
            {
                bool sense = dev.AtaIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters);

                if (sense)
                {
                    AaruConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status);
                    AaruConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error);

                    AaruConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}",
                                               errorRegisters.SectorCount);

                    AaruConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector);

                    AaruConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}",
                                               errorRegisters.CylinderHigh);

                    AaruConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}",
                                               errorRegisters.CylinderLow);

                    AaruConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}",
                                               errorRegisters.DeviceHead);

                    AaruConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError);

                    break;
                }

                if (dev.Error)
                {
                    AaruConsole.ErrorWriteLine("Error {0} querying ATA IDENTIFY", dev.LastError);

                    break;
                }

                AtaIdentify = ataBuf;

                dev.EnableMediaCardPassThrough(out errorRegisters, dev.Timeout, out _);

                if (errorRegisters.Sector == 0xAA &&
                    errorRegisters.SectorCount == 0x55)
                {
                    AtaMcptError = errorRegisters;
                }

                break;
            }

            case DeviceType.ATAPI:
            {
                bool sense = dev.AtapiIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters);

                if (sense)
                {
                    AaruConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status);
                    AaruConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error);

                    AaruConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}",
                                               errorRegisters.SectorCount);

                    AaruConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector);

                    AaruConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}",
                                               errorRegisters.CylinderHigh);

                    AaruConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}",
                                               errorRegisters.CylinderLow);

                    AaruConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}",
                                               errorRegisters.DeviceHead);

                    AaruConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError);

                    break;
                }

                if (!dev.Error)
                {
                    AtapiIdentify = ataBuf;
                }
                else
                {
                    AaruConsole.ErrorWriteLine("Error {0} querying ATA PACKET IDENTIFY", dev.LastError);
                }

                // ATAPI devices are also SCSI devices
                goto case DeviceType.SCSI;
            }

            case DeviceType.SCSI:
            {
                bool sense = dev.ScsiInquiry(out byte[] inqBuf, out byte[] senseBuf);

                if (sense)
                {
                    AaruConsole.ErrorWriteLine("SCSI error:\n{0}", Sense.PrettifySense(senseBuf));

                    break;
                }

                ScsiInquiryData = inqBuf;
                ScsiInquiry     = Inquiry.Decode(inqBuf);

                sense = dev.ScsiInquiry(out inqBuf, out senseBuf, 0x00);

                if (!sense)
                {
                    ScsiEvpdPages = new Dictionary <byte, byte[]>();

                    byte[] pages = EVPD.DecodePage00(inqBuf);

                    if (pages != null)
                    {
                        foreach (byte page in pages)
                        {
                            sense = dev.ScsiInquiry(out inqBuf, out senseBuf, page);

                            if (sense)
                            {
                                continue;
                            }

                            ScsiEvpdPages.Add(page, inqBuf);
                        }
                    }
                }

                var devType = (PeripheralDeviceTypes)ScsiInquiry.Value.PeripheralDeviceType;

                sense = dev.ModeSense10(out byte[] modeBuf, out senseBuf, false, true,
                                        ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out _);

                if (!sense &&
                    !dev.Error)
                {
                    ScsiModeSense10 = modeBuf;
                }

                if (sense || dev.Error)
                {
                    sense = dev.ModeSense10(out modeBuf, out senseBuf, false, true,
                                            ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _);

                    if (!sense &&
                        !dev.Error)
                    {
                        ScsiModeSense10 = modeBuf;
                    }
                }

                if (!sense &&
                    !dev.Error)
                {
                    ScsiMode = Modes.DecodeMode10(modeBuf, devType);
                }

                bool useMode10 = !(sense || dev.Error || !ScsiMode.HasValue);

                sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F,
                                       0xFF, 5, out _);

                if (!sense &&
                    !dev.Error)
                {
                    ScsiModeSense6 = modeBuf;
                }

                if (sense || dev.Error)
                {
                    sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F,
                                           0x00, 5, out _);

                    if (!sense &&
                        !dev.Error)
                    {
                        ScsiModeSense6 = modeBuf;
                    }
                }

                if (sense || dev.Error)
                {
                    sense = dev.ModeSense(out modeBuf, out senseBuf, 5, out _);

                    if (!sense &&
                        !dev.Error)
                    {
                        ScsiModeSense6 = modeBuf;
                    }
                }

                if (!sense &&
                    !dev.Error &&
                    !useMode10)
                {
                    ScsiMode = Modes.DecodeMode6(modeBuf, devType);
                }

                switch (devType)
                {
                case PeripheralDeviceTypes.MultiMediaDevice:
                {
                    sense = dev.GetConfiguration(out byte[] confBuf, out senseBuf, dev.Timeout, out _);

                    if (!sense)
                    {
                        MmcConfiguration = confBuf;
                    }

                    var dvdDecrypt = new DVDDecryption(dev);

                    sense = dvdDecrypt.ReadRpc(out byte[] cmdBuf, out _, DvdCssKeyClass.DvdCssCppmOrCprm,
                                               dev.Timeout, out _);

                    if (!sense)
                    {
                        CSS_CPRM.RegionalPlaybackControlState?rpc =
                            CSS_CPRM.DecodeRegionalPlaybackControlState(cmdBuf);

                        if (rpc.HasValue)
                        {
                            RPC = rpc;
                        }
                    }

                    // TODO: DVD drives respond correctly to BD status.
                    // While specification says if no medium is present
                    // it should inform all possible capabilities,
                    // testing drives show only supported media capabilities.

                    /*
                     * byte[] strBuf;
                     * sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _);
                     *
                     * if (!sense)
                     * {
                     *  Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf);
                     *  if (caps != null)
                     *  {
                     *      foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps)
                     *      {
                     *          if (cap.SDS && cap.RDS)
                     *              AaruConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.SDS)
                     *              AaruConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.RDS)
                     *              AaruConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *      }
                     *  }
                     * }
                     *
                     * sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _);
                     *
                     * if (!sense)
                     * {
                     *  Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf);
                     *  if (caps != null)
                     *  {
                     *      foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps)
                     *      {
                     *          if (cap.SDS && cap.RDS)
                     *              AaruConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.SDS)
                     *              AaruConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *          else if (cap.RDS)
                     *              AaruConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode);
                     *      }
                     *  }
                     * }
                     */

                    #region Plextor
                    if (dev.Manufacturer == "PLEXTOR")
                    {
                        bool   plxtSense = true;
                        bool   plxtDvd   = false;
                        byte[] plxtBuf   = null;

                        switch (dev.Model)
                        {
                        case "DVDR   PX-708A":
                        case "DVDR   PX-708A2":
                        case "DVDR   PX-712A":
                            plxtDvd = true;

                            plxtSense = dev.PlextorReadEeprom(out plxtBuf, out senseBuf, dev.Timeout,
                                                              out _);

                            break;

                        case "DVDR   PX-714A":
                        case "DVDR   PX-716A":
                        case "DVDR   PX-716AL":
                        case "DVDR   PX-755A":
                        case "DVDR   PX-760A":
                        {
                            plxtBuf = new byte[256 * 4];

                            for (byte i = 0; i < 4; i++)
                            {
                                plxtSense = dev.PlextorReadEepromBlock(out byte[] plxtBufSmall,
                                                                       out senseBuf, i, 256, dev.Timeout, out _);

                                if (plxtSense)
                                {
                                    break;
                                }

                                Array.Copy(plxtBufSmall, 0, plxtBuf, i * 256, 256);
                            }

                            plxtDvd = true;

                            break;
                        }

                        default:
                        {
                            if (dev.Model.StartsWith("CD-R   ", StringComparison.Ordinal))
                            {
                                plxtSense = dev.PlextorReadEepromCdr(out plxtBuf, out senseBuf, dev.Timeout,
                                                                     out _);
                            }

                            break;
                        }
                        }

                        PlextorFeatures = new Plextor
                        {
                            IsDvd = plxtDvd
                        };

                        if (!plxtSense)
                        {
                            PlextorFeatures.Eeprom = plxtBuf;

                            if (plxtDvd)
                            {
                                PlextorFeatures.Discs        = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0120);
                                PlextorFeatures.CdReadTime   = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0122);
                                PlextorFeatures.CdWriteTime  = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0126);
                                PlextorFeatures.DvdReadTime  = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012A);
                                PlextorFeatures.DvdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012E);
                            }
                            else
                            {
                                PlextorFeatures.Discs       = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0078);
                                PlextorFeatures.CdReadTime  = BigEndianBitConverter.ToUInt32(plxtBuf, 0x006C);
                                PlextorFeatures.CdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x007A);
                            }
                        }

                        plxtSense = dev.PlextorGetPoweRec(out senseBuf, out bool plxtPwrRecEnabled,
                                                          out ushort plxtPwrRecSpeed, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.PoweRec = true;

                            if (plxtPwrRecEnabled)
                            {
                                PlextorFeatures.PoweRecEnabled          = true;
                                PlextorFeatures.PoweRecRecommendedSpeed = plxtPwrRecSpeed;

                                plxtSense = dev.PlextorGetSpeeds(out senseBuf, out ushort plxtPwrRecSelected,
                                                                 out ushort plxtPwrRecMax,
                                                                 out ushort plxtPwrRecLast, dev.Timeout, out _);

                                if (!plxtSense)
                                {
                                    PlextorFeatures.PoweRecSelected = plxtPwrRecSelected;
                                    PlextorFeatures.PoweRecMax      = plxtPwrRecMax;
                                    PlextorFeatures.PoweRecLast     = plxtPwrRecLast;
                                }
                            }
                        }

                        // TODO: Check it with a drive
                        plxtSense = dev.PlextorGetSilentMode(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            if (plxtBuf[0] == 1)
                            {
                                PlextorFeatures.SilentModeEnabled = true;
                                PlextorFeatures.AccessTimeLimit   = plxtBuf[1];

                                PlextorFeatures.CdReadSpeedLimit  = plxtBuf[2];
                                PlextorFeatures.DvdReadSpeedLimit = plxtBuf[3];
                                PlextorFeatures.CdWriteSpeedLimit = plxtBuf[4];

                                // TODO: Check which one is each one

                                /*
                                 *  if(plxtBuf[6] > 0)
                                 *      AaruConsole.WriteLine("\tTray eject speed limited to {0}",
                                 *                           -(plxtBuf[6] + 48));
                                 *  if(plxtBuf[7] > 0)
                                 *      AaruConsole.WriteLine("\tTray eject speed limited to {0}",
                                 *                           plxtBuf[7] - 47);
                                 */
                            }
                        }

                        plxtSense = dev.PlextorGetGigaRec(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.GigaRec = true;
                        }

                        plxtSense = dev.PlextorGetSecuRec(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.SecuRec = true;
                        }

                        plxtSense = dev.PlextorGetSpeedRead(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.SpeedRead = true;

                            if ((plxtBuf[2] & 0x01) == 0x01)
                            {
                                PlextorFeatures.SpeedReadEnabled = true;
                            }
                        }

                        plxtSense = dev.PlextorGetHiding(out plxtBuf, out senseBuf, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.Hiding = true;

                            if ((plxtBuf[2] & 0x02) == 0x02)
                            {
                                PlextorFeatures.HidesRecordables = true;
                            }

                            if ((plxtBuf[2] & 0x01) == 0x01)
                            {
                                PlextorFeatures.HidesSessions = true;
                            }
                        }

                        plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, false, dev.Timeout, out _);

                        if (!plxtSense)
                        {
                            PlextorFeatures.VariRec = true;
                        }

                        if (plxtDvd)
                        {
                            plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, true, dev.Timeout,
                                                              out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.VariRecDvd = true;
                            }

                            plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, false, dev.Timeout,
                                                                 out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.BitSetting = true;
                            }

                            plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, true, dev.Timeout,
                                                                 out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.BitSettingDl = true;
                            }

                            plxtSense = dev.PlextorGetTestWriteDvdPlus(out plxtBuf, out senseBuf, dev.Timeout,
                                                                       out _);

                            if (!plxtSense)
                            {
                                PlextorFeatures.DvdPlusWriteTest = true;
                            }
                        }
                    }
                    #endregion Plextor

                    if (ScsiInquiry.Value.KreonPresent)
                    {
                        if (!dev.KreonGetFeatureList(out senseBuf, out KreonFeatures krFeatures, dev.Timeout,
                                                     out _))
                        {
                            KreonFeatures = krFeatures;
                        }
                    }

                    break;
                }

                case PeripheralDeviceTypes.SequentialAccess:
                {
                    sense = dev.ReadBlockLimits(out byte[] seqBuf, out senseBuf, dev.Timeout, out _);

                    if (sense)
                    {
                        AaruConsole.ErrorWriteLine("READ BLOCK LIMITS:\n{0}", Sense.PrettifySense(senseBuf));
                    }
                    else
                    {
                        BlockLimits = seqBuf;
                    }

                    sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, dev.Timeout, out _);

                    if (sense)
                    {
                        AaruConsole.ErrorWriteLine("REPORT DENSITY SUPPORT:\n{0}",
                                                   Sense.PrettifySense(senseBuf));
                    }
                    else
                    {
                        DensitySupport       = seqBuf;
                        DensitySupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeDensity(seqBuf);
                    }

                    sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, true, false, dev.Timeout, out _);

                    if (sense)
                    {
                        AaruConsole.ErrorWriteLine("REPORT DENSITY SUPPORT (MEDIUM):\n{0}",
                                                   Sense.PrettifySense(senseBuf));
                    }
                    else
                    {
                        MediumDensitySupport   = seqBuf;
                        MediaTypeSupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeMediumType(seqBuf);
                    }

                    break;
                }
                }

                break;
            }

            case DeviceType.MMC:
            {
                bool sense = dev.ReadCid(out byte[] mmcBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CID = mmcBuf;
                }

                sense = dev.ReadCsd(out mmcBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CSD = mmcBuf;
                }

                sense = dev.ReadOcr(out mmcBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    OCR = mmcBuf;
                }

                sense = dev.ReadExtendedCsd(out mmcBuf, out _, dev.Timeout, out _);

                if (!sense &&
                    !ArrayHelpers.ArrayIsNullOrEmpty(mmcBuf))
                {
                    ExtendedCSD = mmcBuf;
                }
            }

            break;

            case DeviceType.SecureDigital:
            {
                bool sense = dev.ReadCid(out byte[] sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CID = sdBuf;
                }

                sense = dev.ReadCsd(out sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    CSD = sdBuf;
                }

                sense = dev.ReadSdocr(out sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    OCR = sdBuf;
                }

                sense = dev.ReadScr(out sdBuf, out _, dev.Timeout, out _);

                if (!sense)
                {
                    SCR = sdBuf;
                }
            }

            break;

            default:
                AaruConsole.ErrorWriteLine("Unknown device type {0}, cannot get information.", dev.Type);

                break;
            }
        }
Beispiel #25
0
 /// <summary>
 /// Returns a byte array of the hash value.
 /// </summary>
 public byte[] Final()
 {
     hashInt ^= crc32Seed;
     BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
     return(BigEndianBitConverter.GetBytes(hashInt));
 }
Beispiel #26
0
        public bool Close()
        {
            if (!IsWriting)
            {
                ErrorMessage = "Image is not opened for writing";

                return(false);
            }

            byte sessions = byte.MinValue;

            foreach (Track t in writingTracks)
            {
                if (t.TrackSession > byte.MinValue)
                {
                    sessions = (byte)t.TrackSession;
                }
            }

            var header = new AlcoholHeader
            {
                signature = alcoholSignature, version = new byte[]
                {
                    1, 5
                },
                type             = MediaTypeToAlcohol(imageInfo.MediaType), sessions = sessions,
                structuresOffset = (uint)(pfi == null ? 0 : 96), sessionOffset = (uint)(pfi == null ? 96 : 4196),
                unknown1         = new ushort[2], unknown2 = new uint[2],
                unknown3         = new uint[6], unknown4 = new uint[3]
            };

            // Alcohol sets this always, Daemon Tool expects this
            header.unknown1[0] = 2;

            alcSessions    = new Dictionary <int, AlcoholSession>();
            alcTracks      = new Dictionary <int, AlcoholTrack>();
            alcToc         = new Dictionary <int, Dictionary <int, AlcoholTrack> >();
            writingTracks  = writingTracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence).ToList();
            alcTrackExtras = new Dictionary <int, AlcoholTrackExtra>();
            long currentTrackOffset = header.sessionOffset + (Marshal.SizeOf <AlcoholSession>() * sessions);

            byte[] tmpToc = null;

            if (fullToc != null)
            {
                byte[] fullTocSize = BigEndianBitConverter.GetBytes((short)fullToc.Length);
                tmpToc = new byte[fullToc.Length + 2];
                Array.Copy(fullToc, 0, tmpToc, 2, fullToc.Length);
                tmpToc[0] = fullTocSize[0];
                tmpToc[1] = fullTocSize[1];
            }

            FullTOC.CDFullTOC?decodedToc = FullTOC.Decode(tmpToc);

            long currentExtraOffset = currentTrackOffset;
            int  extraCount         = 0;

            for (int i = 1; i <= sessions; i++)
            {
                if (decodedToc.HasValue)
                {
                    extraCount += decodedToc.Value.TrackDescriptors.Count(t => t.SessionNumber == i);

                    currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() *
                                          decodedToc.Value.TrackDescriptors.Count(t => t.SessionNumber == i);
                }
                else
                {
                    currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() * 3;
                    extraCount         += 3;

                    currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() *
                                          writingTracks.Count(t => t.TrackSession == i);

                    extraCount += writingTracks.Count(t => t.TrackSession == i);

                    if (i < sessions)
                    {
                        currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() * 2;
                        extraCount         += 2;
                    }
                }
            }

            long footerOffset = currentExtraOffset + (Marshal.SizeOf <AlcoholTrackExtra>() * extraCount);

            if (bca != null)
            {
                header.bcaOffset = (uint)footerOffset;
                footerOffset    += bca.Length;
            }

            if (isDvd)
            {
                alcSessions.Add(1, new AlcoholSession
                {
                    sessionEnd      = (int)((writingTracks[0].TrackEndSector - writingTracks[0].TrackStartSector) + 1),
                    sessionSequence = 1, allBlocks = 1, nonTrackBlocks = 3, firstTrack = 1,
                    lastTrack       = 1, trackOffset = 4220
                });

                footerOffset = 4300;

                if (bca != null)
                {
                    footerOffset += bca.Length;
                }

                alcTracks.Add(1, new AlcoholTrack
                {
                    mode        = AlcoholTrackMode.DVD, adrCtl = 20, point = 1,
                    extraOffset = (uint)((writingTracks[0].TrackEndSector - writingTracks[0].TrackStartSector) + 1),
                    sectorSize  = 2048, files = 1, footerOffset = (uint)footerOffset, unknown = new byte[18],
                    unknown2    = new byte[24]
                });

                alcToc.Add(1, alcTracks);
            }
            else
            {
                for (int i = 1; i <= sessions; i++)
                {
                    Track firstTrack = writingTracks.First(t => t.TrackSession == i);
                    Track lastTrack  = writingTracks.Last(t => t.TrackSession == i);

                    alcSessions.Add(i, new AlcoholSession
                    {
                        sessionStart = (int)firstTrack.TrackStartSector - 150,
                        sessionEnd   = (int)lastTrack.TrackEndSector + 1, sessionSequence = (ushort)i,
                        allBlocks    = (byte)(decodedToc?.TrackDescriptors.Count(t => t.SessionNumber == i) ??
                                              writingTracks.Count(t => t.TrackSession == i) + 3),
                        nonTrackBlocks =
                            (byte)(decodedToc?.TrackDescriptors.Count(t => t.SessionNumber == i && t.POINT >= 0xA0 &&
                                                                      t.POINT <= 0xAF) ?? 3),
                        firstTrack  = (ushort)firstTrack.TrackSequence, lastTrack = (ushort)lastTrack.TrackSequence,
                        trackOffset = (uint)currentTrackOffset
                    });

                    Dictionary <int, AlcoholTrack> thisSessionTracks = new Dictionary <int, AlcoholTrack>();
                    trackFlags.TryGetValue((byte)firstTrack.TrackSequence, out byte firstTrackControl);
                    trackFlags.TryGetValue((byte)lastTrack.TrackSequence, out byte lastTrackControl);

                    if (firstTrackControl == 0 &&
                        firstTrack.TrackType != TrackType.Audio)
                    {
                        firstTrackControl = (byte)CdFlags.DataTrack;
                    }

                    if (lastTrackControl == 0 &&
                        lastTrack.TrackType != TrackType.Audio)
                    {
                        lastTrackControl = (byte)CdFlags.DataTrack;
                    }

                    (byte minute, byte second, byte frame)leadinPmsf = LbaToMsf(lastTrack.TrackEndSector + 1);

                    if (decodedToc.HasValue &&
                        decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i && t.POINT >= 0xA0 &&
                                                              t.POINT <= 0xAF))
                    {
                        foreach (FullTOC.TrackDataDescriptor tocTrk in
                                 decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == i && t.POINT >= 0xA0 &&
                                                                         t.POINT <= 0xAF))
                        {
                            thisSessionTracks.Add(tocTrk.POINT, new AlcoholTrack
                            {
                                adrCtl      = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL), tno = tocTrk.TNO,
                                point       = tocTrk.POINT, min = tocTrk.Min,
                                sec         = tocTrk.Sec, frame = tocTrk.Frame,
                                zero        = tocTrk.Zero, pmin = tocTrk.PMIN,
                                psec        = tocTrk.PSEC, pframe = tocTrk.PFRAME,
                                mode        = AlcoholTrackMode.NoData, unknown = new byte[18],
                                unknown2    = new byte[24],
                                extraOffset = (uint)currentExtraOffset
                            });

                            currentTrackOffset += Marshal.SizeOf <AlcoholTrack>();
                            currentExtraOffset += Marshal.SizeOf <AlcoholTrackExtra>();
                        }
                    }
                    else
                    {
                        thisSessionTracks.Add(0xA0, new AlcoholTrack
                        {
                            adrCtl   = (byte)((1 << 4) + firstTrackControl), pmin = (byte)firstTrack.TrackSequence,
                            mode     = AlcoholTrackMode.NoData, point = 0xA0, unknown = new byte[18],
                            unknown2 = new byte[24], psec = (byte)(imageInfo.MediaType == MediaType.CDI
                                                                       ? 0x10
                                                                       : writingTracks.Any(t => t.TrackType ==
                                                                                           TrackType.
                                                                                           CdMode2Form1 ||
                                                                                           t.TrackType ==
                                                                                           TrackType.
                                                                                           CdMode2Form2 ||
                                                                                           t.TrackType ==
                                                                                           TrackType.
                                                                                           CdMode2Formless)
                                                                           ? 0x20
                                                                           : 0),
                            extraOffset = (uint)currentExtraOffset
                        });

                        thisSessionTracks.Add(0xA1, new AlcoholTrack
                        {
                            adrCtl   = (byte)((1 << 4) + lastTrackControl), pmin = (byte)lastTrack.TrackSequence,
                            mode     = AlcoholTrackMode.NoData, point = 0xA1, unknown = new byte[18],
                            unknown2 = new byte[24], extraOffset = (uint)currentExtraOffset
                        });

                        thisSessionTracks.Add(0xA2, new AlcoholTrack
                        {
                            adrCtl      = (byte)((1 << 4) + firstTrackControl), zero = 0,
                            pmin        = leadinPmsf.minute,
                            psec        = leadinPmsf.second, pframe = leadinPmsf.frame,
                            mode        = AlcoholTrackMode.NoData,
                            point       = 0xA2, unknown = new byte[18],
                            unknown2    = new byte[24],
                            extraOffset = (uint)currentExtraOffset
                        });

                        currentExtraOffset += Marshal.SizeOf <AlcoholTrackExtra>() * 3;
                        currentTrackOffset += Marshal.SizeOf <AlcoholTrack>() * 3;
                    }

                    foreach (Track track in writingTracks.Where(t => t.TrackSession == i).OrderBy(t => t.TrackSequence))
                    {
                        var alcTrk = new AlcoholTrack();

                        if (decodedToc.HasValue &&
                            decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i &&
                                                                  t.POINT == track.TrackSequence))
                        {
                            FullTOC.TrackDataDescriptor tocTrk =
                                decodedToc.Value.TrackDescriptors.First(t => t.SessionNumber == i &&
                                                                        t.POINT == track.TrackSequence);

                            alcTrk.adrCtl = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL);
                            alcTrk.tno    = tocTrk.TNO;
                            alcTrk.point  = tocTrk.POINT;
                            alcTrk.min    = tocTrk.Min;
                            alcTrk.sec    = tocTrk.Sec;
                            alcTrk.frame  = tocTrk.Frame;
                            alcTrk.zero   = tocTrk.Zero;
                            alcTrk.pmin   = tocTrk.PMIN;
                            alcTrk.psec   = tocTrk.PSEC;
                            alcTrk.pframe = tocTrk.PFRAME;
                        }
                        else
                        {
                            (byte minute, byte second, byte frame)msf = LbaToMsf(track.TrackStartSector);
                            trackFlags.TryGetValue((byte)track.TrackSequence, out byte trackControl);

                            if (trackControl == 0 &&
                                track.TrackType != TrackType.Audio)
                            {
                                trackControl = (byte)CdFlags.DataTrack;
                            }

                            alcTrk.adrCtl = (byte)((1 << 4) + trackControl);
                            alcTrk.point  = (byte)track.TrackSequence;
                            alcTrk.zero   = 0;
                            alcTrk.pmin   = msf.minute;
                            alcTrk.psec   = msf.second;
                            alcTrk.pframe = msf.frame;
                        }

                        alcTrk.mode = TrackTypeToAlcohol(track.TrackType);

                        alcTrk.subMode = track.TrackSubchannelType != TrackSubchannelType.None
                                             ? AlcoholSubchannelMode.Interleaved : AlcoholSubchannelMode.None;

                        alcTrk.sectorSize = (ushort)(track.TrackRawBytesPerSector +
                                                     (track.TrackSubchannelType != TrackSubchannelType.None ? 96 : 0));

                        alcTrk.startLba     = (uint)(track.TrackStartSector + track.TrackPregap);
                        alcTrk.startOffset  = track.TrackFileOffset + (alcTrk.sectorSize * track.TrackPregap);
                        alcTrk.files        = 1;
                        alcTrk.extraOffset  = (uint)currentExtraOffset;
                        alcTrk.footerOffset = (uint)footerOffset;

                        // Alcohol seems to set that for all CD tracks
                        // Daemon Tools expect it to be like this
                        alcTrk.unknown = new byte[]
                        {
                            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                            0x00, 0x00, 0x00
                        };

                        alcTrk.unknown2 = new byte[24];

                        thisSessionTracks.Add((int)track.TrackSequence, alcTrk);

                        currentTrackOffset += Marshal.SizeOf <AlcoholTrack>();
                        currentExtraOffset += Marshal.SizeOf <AlcoholTrackExtra>();

                        var trkExtra = new AlcoholTrackExtra
                        {
                            sectors = (uint)((track.TrackEndSector - track.TrackStartSector) + 1)
                        };

                        if (track.TrackSequence == firstTrack.TrackSequence)
                        {
                            trkExtra.pregap   = 150;
                            trkExtra.sectors += 150;
                        }

                        // When track mode changes there's a mandatory gap, Alcohol needs it
                        else if (thisSessionTracks.TryGetValue((int)(track.TrackSequence - 1),
                                                               out AlcoholTrack previousTrack) &&
                                 alcTrackExtras.TryGetValue((int)(track.TrackSequence - 1),
                                                            out AlcoholTrackExtra previousExtra) &&
                                 previousTrack.mode != alcTrk.mode)
                        {
                            previousExtra.sectors -= 150;
                            trkExtra.pregap        = 150;
                            alcTrackExtras.Remove((int)(track.TrackSequence - 1));
                            alcTrackExtras.Add((int)(track.TrackSequence - 1), previousExtra);
                        }
                        else
                        {
                            trkExtra.pregap = 0;
                        }

                        if (track.TrackSequence == lastTrack.TrackSequence)
                        {
                            trkExtra.sectors -= 150;
                        }

                        alcTrackExtras.Add((int)track.TrackSequence, trkExtra);
                    }

                    if (decodedToc.HasValue &&
                        decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i && t.POINT >= 0xB0))
                    {
                        foreach (FullTOC.TrackDataDescriptor tocTrk in
                                 decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == i && t.POINT >= 0xB0))
                        {
                            thisSessionTracks.Add(tocTrk.POINT, new AlcoholTrack
                            {
                                adrCtl      = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL), tno = tocTrk.TNO,
                                point       = tocTrk.POINT, min = tocTrk.Min,
                                sec         = tocTrk.Sec, frame = tocTrk.Frame,
                                zero        = tocTrk.Zero, pmin = tocTrk.PMIN,
                                psec        = tocTrk.PSEC, pframe = tocTrk.PFRAME,
                                mode        = AlcoholTrackMode.NoData, unknown = new byte[18],
                                unknown2    = new byte[24],
                                extraOffset = (uint)currentExtraOffset
                            });

                            currentExtraOffset += Marshal.SizeOf <AlcoholTrackExtra>();
                            currentTrackOffset += Marshal.SizeOf <AlcoholTrack>();
                        }
                    }
                    else if (i < sessions)
                    {
                        (byte minute, byte second, byte frame)leadoutAmsf =
                            LbaToMsf(writingTracks.First(t => t.TrackSession == i + 1).TrackStartSector - 150);

                        (byte minute, byte second, byte frame)leadoutPmsf =
                            LbaToMsf(writingTracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence).Last().
                                     TrackStartSector);

                        thisSessionTracks.Add(0xB0, new AlcoholTrack
                        {
                            point    = 0xB0, adrCtl = 0x50, zero = 0,
                            min      = leadoutAmsf.minute,
                            sec      = leadoutAmsf.second, frame = leadoutAmsf.frame, pmin = leadoutPmsf.minute,
                            psec     = leadoutPmsf.second, pframe = leadoutPmsf.frame, unknown = new byte[18],
                            unknown2 = new byte[24]
                        });

                        thisSessionTracks.Add(0xC0, new AlcoholTrack
                        {
                            point = 0xC0, adrCtl = 0x50, min = 128, pmin = 97,
                            psec  = 25, unknown = new byte[18], unknown2 = new byte[24]
                        });

                        currentTrackOffset += Marshal.SizeOf <AlcoholTrack>() * 2;
                    }

                    alcToc.Add(i, thisSessionTracks);
                }
            }

            alcFooter = new AlcoholFooter
            {
                filenameOffset = (uint)(footerOffset + Marshal.SizeOf <AlcoholFooter>()), widechar = 1
            };

            byte[] filename = Encoding.Unicode.GetBytes("*.mdf"); // Yup, Alcohol stores no filename but a wildcard.

            IntPtr blockPtr;

            // Write header
            descriptorStream.Seek(0, SeekOrigin.Begin);
            byte[] block = new byte[Marshal.SizeOf <AlcoholHeader>()];
            blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholHeader>());
            System.Runtime.InteropServices.Marshal.StructureToPtr(header, blockPtr, true);
            System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length);
            System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr);
            descriptorStream.Write(block, 0, block.Length);

            // Write DVD structures if pressent
            if (header.structuresOffset != 0)
            {
                if (dmi != null)
                {
                    descriptorStream.Seek(header.structuresOffset, SeekOrigin.Begin);

                    if (dmi.Length == 2052)
                    {
                        descriptorStream.Write(dmi, 0, 2052);
                    }
                    else if (dmi.Length == 2048)
                    {
                        descriptorStream.Write(new byte[]
                        {
                            0x08, 0x02, 0x00, 0x00
                        }, 0, 4);

                        descriptorStream.Write(dmi, 0, 2048);
                    }
                }

                // TODO: Create fake PFI if none present
                if (pfi != null)
                {
                    descriptorStream.Seek(header.structuresOffset + 2052, SeekOrigin.Begin);
                    descriptorStream.Write(pfi, pfi.Length - 2048, 2048);
                }
            }

            // Write sessions
            descriptorStream.Seek(header.sessionOffset, SeekOrigin.Begin);

            foreach (AlcoholSession session in alcSessions.Values)
            {
                block    = new byte[Marshal.SizeOf <AlcoholSession>()];
                blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholSession>());
                System.Runtime.InteropServices.Marshal.StructureToPtr(session, blockPtr, true);
                System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length);
                System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr);
                descriptorStream.Write(block, 0, block.Length);
            }

            // Write tracks
            foreach (KeyValuePair <int, Dictionary <int, AlcoholTrack> > kvp in alcToc)
            {
                descriptorStream.Seek(alcSessions.First(t => t.Key == kvp.Key).Value.trackOffset, SeekOrigin.Begin);

                foreach (AlcoholTrack track in kvp.Value.Values)
                {
                    AlcoholTrack alcoholTrack = track;

                    // Write extra
                    if (!isDvd)
                    {
                        long position = descriptorStream.Position;
                        descriptorStream.Seek(alcoholTrack.extraOffset, SeekOrigin.Begin);

                        block = new byte[Marshal.SizeOf <AlcoholTrackExtra>()];

                        if (alcTrackExtras.TryGetValue(alcoholTrack.point, out AlcoholTrackExtra extra))
                        {
                            blockPtr =
                                System.Runtime.InteropServices.Marshal.
                                AllocHGlobal(Marshal.SizeOf <AlcoholTrackExtra>());

                            System.Runtime.InteropServices.Marshal.StructureToPtr(extra, blockPtr, true);
                            System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length);
                            System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr);
                        }
                        else
                        {
                            alcoholTrack.extraOffset = 0;
                        }

                        descriptorStream.Write(block, 0, block.Length);

                        descriptorStream.Seek(position, SeekOrigin.Begin);
                    }

                    block    = new byte[Marshal.SizeOf <AlcoholTrack>()];
                    blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholTrack>());
                    System.Runtime.InteropServices.Marshal.StructureToPtr(alcoholTrack, blockPtr, true);
                    System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length);
                    System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr);
                    descriptorStream.Write(block, 0, block.Length);
                }
            }

            // Write BCA
            if (bca != null)
            {
                descriptorStream.Seek(header.bcaOffset, SeekOrigin.Begin);
                descriptorStream.Write(bca, 0, bca.Length);
            }

            // Write footer
            descriptorStream.Seek(footerOffset, SeekOrigin.Begin);
            block    = new byte[Marshal.SizeOf <AlcoholFooter>()];
            blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholFooter>());
            System.Runtime.InteropServices.Marshal.StructureToPtr(alcFooter, blockPtr, true);
            System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length);
            System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr);
            descriptorStream.Write(block, 0, block.Length);

            // Write filename
            descriptorStream.Write(filename, 0, filename.Length);

            // Write filename null termination
            descriptorStream.Write(new byte[]
            {
                0, 0
            }, 0, 2);

            descriptorStream.Flush();
            descriptorStream.Close();
            imageStream.Flush();
            imageStream.Close();

            IsWriting    = false;
            ErrorMessage = "";

            return(true);
        }
Beispiel #27
0
 public static void BytesToUInt32Test(uint expected, byte[] n)
 {
     Assert.Equal(expected, BigEndianBitConverter.ToUInt32(n));
 }
Beispiel #28
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1");
            StringBuilder sbInformation = new StringBuilder();

            XmlFsType   = new FileSystemType();
            information = null;
            BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;

            byte[] bootBlockSectors = imagePlugin.ReadSectors(0 + partition.Start, 2);

            BootBlock bootBlk = BigEndianMarshal.ByteArrayToStructureBigEndian <BootBlock>(bootBlockSectors);

            bootBlk.bootCode = new byte[bootBlockSectors.Length - 12];
            Array.Copy(bootBlockSectors, 12, bootBlk.bootCode, 0, bootBlk.bootCode.Length);
            bootBlockSectors[4] = bootBlockSectors[5] = bootBlockSectors[6] = bootBlockSectors[7] = 0;
            uint bsum = AmigaBootChecksum(bootBlockSectors);

            ulong bRootPtr = 0;

            // If bootblock is correct, let's take its rootblock pointer
            if (bsum == bootBlk.checksum)
            {
                bRootPtr = bootBlk.root_ptr + partition.Start;
                DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr);
            }

            ulong[] rootPtrs =
            {
                bRootPtr + partition.Start,                                      (partition.End - partition.Start + 1) / 2 + partition.Start - 2,
                (partition.End - partition.Start + 1) / 2 + partition.Start - 1,
                (partition.End - partition.Start + 1) / 2 + partition.Start,
                (partition.End - partition.Start + 1) / 2 + partition.Start + 4
            };

            RootBlock rootBlk = new RootBlock();

            byte[] rootBlockSector = null;

            bool rootFound = false;
            uint blockSize = 0;

            // So to handle even number of sectors
            foreach (ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start))
            {
                DicConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr);

                rootBlockSector = imagePlugin.ReadSector(rootPtr);

                rootBlk.type = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x00);
                DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.type = {0}", rootBlk.type);
                if (rootBlk.type != TYPE_HEADER)
                {
                    continue;
                }

                rootBlk.hashTableSize = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x0C);

                DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.hashTableSize = {0}", rootBlk.hashTableSize);

                blockSize = (rootBlk.hashTableSize + 56) * 4;
                uint sectorsPerBlock = (uint)(blockSize / rootBlockSector.Length);

                DicConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize);
                DicConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock);

                if (blockSize % rootBlockSector.Length > 0)
                {
                    sectorsPerBlock++;
                }

                if (rootPtr + sectorsPerBlock >= partition.End)
                {
                    continue;
                }

                rootBlockSector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock);

                // Clear checksum on sector
                rootBlk.checksum    = BigEndianBitConverter.ToUInt32(rootBlockSector, 20);
                rootBlockSector[20] = rootBlockSector[21] = rootBlockSector[22] = rootBlockSector[23] = 0;
                uint rsum = AmigaChecksum(rootBlockSector);

                DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.checksum = 0x{0:X8}", rootBlk.checksum);
                DicConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum);

                rootBlk.sec_type = BigEndianBitConverter.ToUInt32(rootBlockSector, rootBlockSector.Length - 4);
                DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.sec_type = {0}", rootBlk.sec_type);

                if (rootBlk.sec_type != SUBTYPE_ROOT || rootBlk.checksum != rsum)
                {
                    continue;
                }

                rootBlockSector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock);
                rootFound       = true;
                break;
            }

            if (!rootFound)
            {
                return;
            }

            rootBlk = MarshalRootBlock(rootBlockSector);

            string diskName = StringHandlers.PascalToString(rootBlk.diskName, Encoding);

            switch (bootBlk.diskType & 0xFF)
            {
            case 0:
                sbInformation.Append("Amiga Original File System");
                XmlFsType.Type = "Amiga OFS";
                break;

            case 1:
                sbInformation.Append("Amiga Fast File System");
                XmlFsType.Type = "Amiga FFS";
                break;

            case 2:
                sbInformation.Append("Amiga Original File System with international characters");
                XmlFsType.Type = "Amiga OFS";
                break;

            case 3:
                sbInformation.Append("Amiga Fast File System with international characters");
                XmlFsType.Type = "Amiga FFS";
                break;

            case 4:
                sbInformation.Append("Amiga Original File System with directory cache");
                XmlFsType.Type = "Amiga OFS";
                break;

            case 5:
                sbInformation.Append("Amiga Fast File System with directory cache");
                XmlFsType.Type = "Amiga FFS";
                break;

            case 6:
                sbInformation.Append("Amiga Original File System with long filenames");
                XmlFsType.Type = "Amiga OFS2";
                break;

            case 7:
                sbInformation.Append("Amiga Fast File System with long filenames");
                XmlFsType.Type = "Amiga FFS2";
                break;
            }

            if ((bootBlk.diskType & 0x6D754600) == 0x6D754600)
            {
                sbInformation.Append(", with multi-user patches");
            }

            sbInformation.AppendLine();

            sbInformation.AppendFormat("Volume name: {0}", diskName).AppendLine();

            if (bootBlk.checksum == bsum)
            {
                Sha1Context sha1Ctx = new Sha1Context();
                sha1Ctx.Update(bootBlk.bootCode);
                sbInformation.AppendLine("Volume is bootable");
                sbInformation.AppendFormat("Boot code SHA1 is {0}", sha1Ctx.End()).AppendLine();
            }

            if (rootBlk.bitmapFlag == 0xFFFFFFFF)
            {
                sbInformation.AppendLine("Volume bitmap is valid");
            }

            if (rootBlk.bitmapExtensionBlock != 0x00000000 && rootBlk.bitmapExtensionBlock != 0xFFFFFFFF)
            {
                sbInformation.AppendFormat("Bitmap extension at block {0}", rootBlk.bitmapExtensionBlock).AppendLine();
            }

            if ((bootBlk.diskType & 0xFF) == 4 || (bootBlk.diskType & 0xFF) == 5)
            {
                sbInformation.AppendFormat("Directory cache starts at block {0}", rootBlk.extension).AppendLine();
            }

            long blocks = (long)((partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / blockSize);

            sbInformation.AppendFormat("Volume block size is {0} bytes", blockSize).AppendLine();
            sbInformation.AppendFormat("Volume has {0} blocks", blocks).AppendLine();
            sbInformation.AppendFormat("Volume created on {0}",
                                       DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks))
            .AppendLine();
            sbInformation.AppendFormat("Volume last modified on {0}",
                                       DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks))
            .AppendLine();
            sbInformation.AppendFormat("Volume root directory last modified on on {0}",
                                       DateHandlers.AmigaToDateTime(rootBlk.rDays, rootBlk.rMins, rootBlk.rTicks))
            .AppendLine();
            sbInformation.AppendFormat("Root block checksum is 0x{0:X8}", rootBlk.checksum).AppendLine();
            information = sbInformation.ToString();

            XmlFsType.CreationDate =
                DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks);
            XmlFsType.CreationDateSpecified = true;
            XmlFsType.ModificationDate      =
                DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks);
            XmlFsType.ModificationDateSpecified = true;
            XmlFsType.Dirty       = rootBlk.bitmapFlag != 0xFFFFFFFF;
            XmlFsType.Clusters    = blocks;
            XmlFsType.ClusterSize = (int)blockSize;
            XmlFsType.VolumeName  = diskName;
            XmlFsType.Bootable    = bsum == bootBlk.checksum;
            // Useful as a serial
            XmlFsType.VolumeSerial = $"{rootBlk.checksum:X8}";
        }
        public void ToXXX_ThrowsOnNull()
        {
            var sut = new BigEndianBitConverter();

            Check.ThatCode(() => sut.ToInt32(null, 0)).Throws <ArgumentNullException>();
        }
Beispiel #30
0
        /// <summary>Gets the hash of the specified data buffer.</summary>
        /// <param name="data">Data buffer.</param>
        /// <param name="len">Length of the data buffer to hash.</param>
        /// <param name="hash">Byte array of the hash value.</param>
        /// <param name="polynomial">CRC polynomial</param>
        /// <param name="seed">CRC seed</param>
        /// <param name="table">CRC lookup table</param>
        /// <param name="inverse">Is CRC inverted?</param>
        public static string Data(byte[] data, uint len, out byte[] hash, ushort polynomial, ushort seed,
                                  ushort[][] table, bool inverse)
        {
            bool useNative = Native.IsSupported;

            bool useCcitt = polynomial == CRC16CCITTContext.CRC16_CCITT_POLY &&
                            seed == CRC16CCITTContext.CRC16_CCITT_SEED && inverse;

            bool useIbm = polynomial == CRC16IBMContext.CRC16_IBM_POLY && seed == CRC16IBMContext.CRC16_IBM_SEED &&
                          !inverse;

            IntPtr nativeContext = IntPtr.Zero;

            ushort localHashInt = seed;

            switch (useNative)
            {
            case true when useCcitt:
                nativeContext = crc16_ccitt_init();
                useNative     = nativeContext != IntPtr.Zero;

                break;

            case true when useIbm:
                nativeContext = crc16_init();
                useNative     = nativeContext != IntPtr.Zero;

                break;
            }

            ushort[][] localTable = table ?? GenerateTable(polynomial, inverse);

            switch (useNative)
            {
            case true when useCcitt:
                crc16_ccitt_update(nativeContext, data, len);

                break;

            case true when useIbm:
                crc16_update(nativeContext, data, len);

                break;

            default:
            {
                if (inverse)
                {
                    StepInverse(ref localHashInt, localTable, data, len);
                }
                else
                {
                    Step(ref localHashInt, localTable, data, len);
                }

                break;
            }
            }

            localHashInt ^= seed;

            switch (useNative)
            {
            case true when useCcitt:
                crc16_ccitt_final(nativeContext, ref localHashInt);
                crc16_ccitt_free(nativeContext);

                break;

            case true when useIbm:
                crc16_final(nativeContext, ref localHashInt);
                crc16_free(nativeContext);

                break;

            default:
            {
                if (inverse)
                {
                    localHashInt = (ushort)~localHashInt;
                }

                break;
            }
            }

            hash = BigEndianBitConverter.GetBytes(localHashInt);

            var crc16Output = new StringBuilder();

            foreach (byte h in hash)
            {
                crc16Output.Append(h.ToString("x2"));
            }

            return(crc16Output.ToString());
        }