Esempio n. 1
0
        public bool Identify(IFilter imageFilter)
        {
            if (!imageFilter.HasResourceFork() || imageFilter.GetResourceForkLength() == 0)
            {
                return(false);
            }

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

                Resource rsrc = rsrcFork.GetResource(NDIF_RESOURCE);

                if (rsrc.ContainsId(NDIF_RESOURCEID))
                {
                    return(true);
                }
            }
            catch (InvalidCastException) { return(false); }

            return(false);
        }
Esempio n. 2
0
        public bool Identify(IFilter imageFilter)
        {
            if (!imageFilter.HasResourceFork() ||
                imageFilter.GetResourceForkLength() == 0)
            {
                return(false);
            }

            try
            {
                var rsrcFork = new ResourceFork(imageFilter.GetResourceForkStream());

                if (!rsrcFork.ContainsKey(NDIF_RESOURCE))
                {
                    return(false);
                }

                Resource rsrc = rsrcFork.GetResource(NDIF_RESOURCE);

                Stream dataFork  = imageFilter.GetDataForkStream();
                byte[] udifMagic = new byte[4];
                dataFork.Read(udifMagic, 0, 4);

                if (BitConverter.ToUInt32(udifMagic, 0) == 0x796C6F6B)
                {
                    return(false);
                }

                if (rsrc.ContainsId(NDIF_RESOURCEID))
                {
                    return(true);
                }
            }
            catch (InvalidCastException)
            {
                return(false);
            }

            return(false);
        }
Esempio n. 3
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            bool fakeBlockChunks = false;

            byte[] vers = null;

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

                var rsrc = new ResourceFork(rsrcB);

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

                Resource blkxRez = rsrc.GetResource(BLOCK_OS_TYPE);

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

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

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

                Resource versRez = rsrc.GetResource(0x76657273);

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

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

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

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

                var rsrc = (NSDictionary)rsrcObj;

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

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

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

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

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

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

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

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

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

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

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

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

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

                    break;

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

                    break;

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

                    break;
                }

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

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

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

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

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

            imageInfo.Sectors = 0;

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

                buffersize = 0;

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

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

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

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

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

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

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

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

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

                        imageInfo.Sectors += bChnk.sectors;

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

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

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

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

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

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

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

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

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

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

            return(true);
        }
Esempio n. 4
0
        public bool Open(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

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

            DartHeader header = new DartHeader();

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

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

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

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

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

                break;

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

                break;

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

                break;

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

                expectedMaxSize += 64;
                break;

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

                break;

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

                expectedMaxSize += 64;
                break;

            default: return(false);
            }

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

            short[] bLength;

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

            BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            return(true);
        }
Esempio n. 5
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];

            AaruConsole.DebugWriteLine("DC42 plugin", "header.diskName = \"{0}\"", header.DiskName);
            AaruConsole.DebugWriteLine("DC42 plugin", "header.dataSize = {0} bytes", header.DataSize);
            AaruConsole.DebugWriteLine("DC42 plugin", "header.tagSize = {0} bytes", header.TagSize);
            AaruConsole.DebugWriteLine("DC42 plugin", "header.dataChecksum = 0x{0:X8}", header.DataChecksum);
            AaruConsole.DebugWriteLine("DC42 plugin", "header.tagChecksum = 0x{0:X8}", header.TagChecksum);
            AaruConsole.DebugWriteLine("DC42 plugin", "header.format = 0x{0:X2}", header.Format);
            AaruConsole.DebugWriteLine("DC42 plugin", "header.fmtByte = 0x{0:X2}", header.FmtByte);
            AaruConsole.DebugWriteLine("DC42 plugin", "header.valid = {0}", header.Valid);
            AaruConsole.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)
            {
                AaruConsole.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)
            {
                AaruConsole.DebugWriteLine("DC42 plugin", "Unknown tmp_header.fmtByte = 0x{0:X2} value",
                                           header.FmtByte);

                return(false);
            }

            if (header.FmtByte == kInvalidFmtByte)
            {
                AaruConsole.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);
                AaruConsole.DebugWriteLine("DC42 plugin", "bptag = {0} bytes", bptag);

                if (bptag != 12 &&
                    bptag != 20 &&
                    bptag != 24)
                {
                    AaruConsole.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)
                {
                    AaruConsole.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
                {
                    AaruConsole.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())
                {
                    var rsrcFork = new ResourceFork(imageFilter.GetResourceForkStream());

                    if (rsrcFork.ContainsKey(0x76657273))
                    {
                        Resource versRsrc = rsrcFork.GetResource(0x76657273);

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

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

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

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

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

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

                                break;

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

                                break;

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

                                break;
                            }

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

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

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

                    if (rsrcFork.ContainsKey(0x64437079))
                    {
                        Resource dCpyRsrc = rsrcFork.GetResource(0x64437079);

                        if (dCpyRsrc != null)
                        {
                            string dCpy = StringHandlers.PascalToString(dCpyRsrc.GetResource(dCpyRsrc.GetIds()[0]),
                                                                        Encoding.GetEncoding("macintosh"));

                            var   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) {}

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

            imageInfo.XmlMediaType = XmlMediaType.BlockMedia;
            AaruConsole.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);
        }
Esempio n. 6
0
        public bool Open(IFilter imageFilter)
        {
            if (!imageFilter.HasResourceFork() || imageFilter.GetResourceForkLength() == 0)
            {
                return(false);
            }

            ResourceFork rsrcFork;
            Resource     rsrc;

            short[] bcems;

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

                rsrc = rsrcFork.GetResource(NDIF_RESOURCE);

                bcems = rsrc.GetIds();

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

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

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

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

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

                BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;

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

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

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

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

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

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

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

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

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

                imageInfo.Sectors += header.sectors;
            }

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

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

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

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

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

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

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

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

                    Version version = new Version(vers);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            return(true);
        }