示例#1
0
        public bool Identify(IFilter imageFilter)
        {
            header = new TeleDiskHeader();
            byte[] headerBytes = new byte[12];
            Stream stream      = imageFilter.GetDataForkStream();

            stream.Seek(0, SeekOrigin.Begin);

            stream.Read(headerBytes, 0, 12);

            header.Signature = BitConverter.ToUInt16(headerBytes, 0);

            if (header.Signature != TD_MAGIC && header.Signature != TD_ADV_COMP_MAGIC)
            {
                return(false);
            }

            header.Sequence      = headerBytes[2];
            header.DiskSet       = headerBytes[3];
            header.Version       = headerBytes[4];
            header.DataRate      = headerBytes[5];
            header.DriveType     = headerBytes[6];
            header.Stepping      = headerBytes[7];
            header.DosAllocation = headerBytes[8];
            header.Sides         = headerBytes[9];
            header.Crc           = BitConverter.ToUInt16(headerBytes, 10);

            byte[] headerBytesForCrc = new byte[10];
            Array.Copy(headerBytes, headerBytesForCrc, 10);
            ushort calculatedHeaderCrc = TeleDiskCrc(0x0000, headerBytesForCrc);

            DicConsole.DebugWriteLine("TeleDisk plugin", "header.signature = 0x{0:X4}", header.Signature);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.sequence = 0x{0:X2}", header.Sequence);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.diskSet = 0x{0:X2}", header.DiskSet);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.version = 0x{0:X2}", header.Version);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.dataRate = 0x{0:X2}", header.DataRate);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.driveType = 0x{0:X2}", header.DriveType);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.stepping = 0x{0:X2}", header.Stepping);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.dosAllocation = 0x{0:X2}", header.DosAllocation);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.sides = 0x{0:X2}", header.Sides);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.crc = 0x{0:X4}", header.Crc);
            DicConsole.DebugWriteLine("TeleDisk plugin", "calculated header crc = 0x{0:X4}", calculatedHeaderCrc);

            // We need more checks as the magic is too simply.
            // This may deny legal images

            // That would be much of a coincidence
            if (header.Crc == calculatedHeaderCrc)
            {
                return(true);
            }

            if (header.Sequence != 0x00)
            {
                return(false);
            }

            if (header.DataRate != DATA_RATE_250KBPS && header.DataRate != DATA_RATE_300KBPS &&
                header.DataRate != DATA_RATE_500KBPS)
            {
                return(false);
            }

            return(header.DriveType == DRIVE_TYPE_35_DD || header.DriveType == DRIVE_TYPE_35_ED ||
                   header.DriveType == DRIVE_TYPE_35_HD || header.DriveType == DRIVE_TYPE_525_DD ||
                   header.DriveType == DRIVE_TYPE_525_HD || header.DriveType == DRIVE_TYPE_525_HD_DD_DISK ||
                   header.DriveType == DRIVE_TYPE_8_INCH);
        }
示例#2
0
        public bool Open(IFilter imageFilter)
        {
            header = new TeleDiskHeader();
            byte[] headerBytes = new byte[12];
            inStream = imageFilter.GetDataForkStream();
            MemoryStream stream = new MemoryStream();
            inStream.Seek(0, SeekOrigin.Begin);

            inStream.Read(headerBytes, 0, 12);
            stream.Write(headerBytes, 0, 12);

            header.Signature = BitConverter.ToUInt16(headerBytes, 0);

            if(header.Signature != TD_MAGIC && header.Signature != TD_ADV_COMP_MAGIC) return false;

            header.Sequence      = headerBytes[2];
            header.DiskSet       = headerBytes[3];
            header.Version       = headerBytes[4];
            header.DataRate      = headerBytes[5];
            header.DriveType     = headerBytes[6];
            header.Stepping      = headerBytes[7];
            header.DosAllocation = headerBytes[8];
            header.Sides         = headerBytes[9];
            header.Crc           = BitConverter.ToUInt16(headerBytes, 10);

            imageInfo.MediaTitle  = Path.GetFileNameWithoutExtension(imageFilter.GetFilename());
            imageInfo.Version     = $"{(header.Version & 0xF0) >> 4}.{header.Version & 0x0F}";
            imageInfo.Application = imageInfo.Version;

            byte[] headerBytesForCrc = new byte[10];
            Array.Copy(headerBytes, headerBytesForCrc, 10);
            ushort calculatedHeaderCrc = TeleDiskCrc(0x0000, headerBytesForCrc);

            DicConsole.DebugWriteLine("TeleDisk plugin", "header.signature = 0x{0:X4}",      header.Signature);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.sequence = 0x{0:X2}",       header.Sequence);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.diskSet = 0x{0:X2}",        header.DiskSet);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.version = 0x{0:X2}",        header.Version);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.dataRate = 0x{0:X2}",       header.DataRate);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.driveType = 0x{0:X2}",      header.DriveType);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.stepping = 0x{0:X2}",       header.Stepping);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.dosAllocation = 0x{0:X2}",  header.DosAllocation);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.sides = 0x{0:X2}",          header.Sides);
            DicConsole.DebugWriteLine("TeleDisk plugin", "header.crc = 0x{0:X4}",            header.Crc);
            DicConsole.DebugWriteLine("TeleDisk plugin", "calculated header crc = 0x{0:X4}", calculatedHeaderCrc);

            // We need more checks as the magic is too simply.
            // This may deny legal images

            // That would be much of a coincidence
            if(header.Crc != calculatedHeaderCrc)
            {
                aDiskCrcHasFailed = true;
                DicConsole.DebugWriteLine("TeleDisk plugin", "Calculated CRC does not coincide with stored one.");
            }

            if(header.Sequence != 0x00) return false;

            if(header.DataRate != DATA_RATE_250KBPS && header.DataRate != DATA_RATE_300KBPS &&
               header.DataRate != DATA_RATE_500KBPS) return false;

            if(header.DriveType != DRIVE_TYPE_35_DD  && header.DriveType != DRIVE_TYPE_35_ED          &&
               header.DriveType != DRIVE_TYPE_35_HD  && header.DriveType != DRIVE_TYPE_525_DD         &&
               header.DriveType != DRIVE_TYPE_525_HD && header.DriveType != DRIVE_TYPE_525_HD_DD_DISK &&
               header.DriveType != DRIVE_TYPE_8_INCH) return false;

            if(header.Signature == TD_ADV_COMP_MAGIC)
            {
                int rd;
                inStream.Seek(12, SeekOrigin.Begin);
                stream.Seek(12, SeekOrigin.Begin);
                TeleDiskLzh lzh = new TeleDiskLzh(inStream);
                do
                    if((rd = lzh.Decode(out byte[] obuf, BUFSZ)) > 0)
                        stream.Write(obuf, 0, rd);
                while(rd == BUFSZ);
            }
            else
            {
                // Not using Stream.CopyTo() because it's failing with LZIP
                byte[] copybuf = new byte[inStream.Length];
                inStream.Seek(0, SeekOrigin.Begin);
                inStream.Read(copybuf, 0, copybuf.Length);
                stream.Seek(0, SeekOrigin.Begin);
                stream.Write(copybuf, 0, copybuf.Length);
            }

            stream.Seek(12, SeekOrigin.Begin);

            imageInfo.CreationTime = DateTime.MinValue;

            if((header.Stepping & COMMENT_BLOCK_PRESENT) == COMMENT_BLOCK_PRESENT)
            {
                commentHeader = new TeleDiskCommentBlockHeader();

                byte[] commentHeaderBytes = new byte[10];

                stream.Read(commentHeaderBytes, 0, 10);
                commentHeader.Crc    = BitConverter.ToUInt16(commentHeaderBytes, 0);
                commentHeader.Length = BitConverter.ToUInt16(commentHeaderBytes, 2);
                commentHeader.Year   = commentHeaderBytes[4];
                commentHeader.Month  = commentHeaderBytes[5];
                commentHeader.Day    = commentHeaderBytes[6];
                commentHeader.Hour   = commentHeaderBytes[7];
                commentHeader.Minute = commentHeaderBytes[8];
                commentHeader.Second = commentHeaderBytes[9];

                commentBlock = new byte[commentHeader.Length];
                stream.Read(commentBlock, 0, commentHeader.Length);

                byte[] commentBlockForCrc = new byte[commentHeader.Length + 8];
                Array.Copy(commentHeaderBytes, 2, commentBlockForCrc, 0, 8);
                Array.Copy(commentBlock,       0, commentBlockForCrc, 8, commentHeader.Length);

                ushort cmtcrc = TeleDiskCrc(0, commentBlockForCrc);

                DicConsole.DebugWriteLine("TeleDisk plugin", "Comment header");
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.crc = 0x{0:X4}", commentHeader.Crc);
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tCalculated CRC = 0x{0:X4}",    cmtcrc);
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.length = {0} bytes",
                                          commentHeader.Length);
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.year = {0}",   commentHeader.Year);
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.month = {0}",  commentHeader.Month);
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.day = {0}",    commentHeader.Day);
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.hour = {0}",   commentHeader.Hour);
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.minute = {0}", commentHeader.Minute);
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tcommentheader.second = {0}", commentHeader.Second);

                aDiskCrcHasFailed |= cmtcrc != commentHeader.Crc;

                for(int i = 0; i < commentBlock.Length; i++)
                    // Replace NULLs, used by TeleDisk as newline markers, with UNIX newline marker
                    if(commentBlock[i] == 0x00)
                        commentBlock[i] = 0x0A;

                imageInfo.Comments = Encoding.ASCII.GetString(commentBlock);

                DicConsole.DebugWriteLine("TeleDisk plugin", "Comment");
                DicConsole.DebugWriteLine("TeleDisk plugin", "{0}", imageInfo.Comments);

                imageInfo.CreationTime = new DateTime(commentHeader.Year + 1900, commentHeader.Month + 1,
                                                      commentHeader.Day, commentHeader.Hour, commentHeader.Minute,
                                                      commentHeader.Second, DateTimeKind.Unspecified);
            }

            if(imageInfo.CreationTime == DateTime.MinValue) imageInfo.CreationTime = imageFilter.GetCreationTime();
            imageInfo.LastModificationTime = imageFilter.GetLastWriteTime();

            DicConsole.DebugWriteLine("TeleDisk plugin", "Image created on {0}",  imageInfo.CreationTime);
            DicConsole.DebugWriteLine("TeleDisk plugin", "Image modified on {0}", imageInfo.LastModificationTime);

            DicConsole.DebugWriteLine("TeleDisk plugin", "Parsing image");

            totalDiskSize       = 0;
            imageInfo.ImageSize = 0;

            int  totalCylinders = -1;
            int  totalHeads     = -1;
            int  maxSector      = -1;
            int  totalSectors   = 0;
            long currentPos     = stream.Position;
            imageInfo.SectorSize      = uint.MaxValue;
            imageInfo.SectorsPerTrack = uint.MaxValue;

            // Count cylinders
            while(true)
            {
                TeleDiskTrackHeader teleDiskTrack = new TeleDiskTrackHeader
                {
                    Sectors  = (byte)stream.ReadByte(),
                    Cylinder = (byte)stream.ReadByte(),
                    Head     = (byte)stream.ReadByte(),
                    Crc      = (byte)stream.ReadByte()
                };

                if(teleDiskTrack.Cylinder > totalCylinders) totalCylinders = teleDiskTrack.Cylinder;
                if(teleDiskTrack.Head     > totalHeads) totalHeads         = teleDiskTrack.Head;

                if(teleDiskTrack.Sectors == 0xFF) // End of disk image
                    break;

                for(byte processedSectors = 0; processedSectors < teleDiskTrack.Sectors; processedSectors++)
                {
                    TeleDiskSectorHeader teleDiskSector = new TeleDiskSectorHeader();
                    TeleDiskDataHeader   teleDiskData   = new TeleDiskDataHeader();
                    byte[]               dataSizeBytes  = new byte[2];

                    teleDiskSector.Cylinder     = (byte)stream.ReadByte();
                    teleDiskSector.Head         = (byte)stream.ReadByte();
                    teleDiskSector.SectorNumber = (byte)stream.ReadByte();
                    teleDiskSector.SectorSize   = (byte)stream.ReadByte();
                    teleDiskSector.Flags        = (byte)stream.ReadByte();
                    teleDiskSector.Crc          = (byte)stream.ReadByte();

                    if(teleDiskSector.SectorNumber > maxSector) maxSector = teleDiskSector.SectorNumber;

                    if((teleDiskSector.Flags & FLAGS_SECTOR_DATALESS) != FLAGS_SECTOR_DATALESS &&
                       (teleDiskSector.Flags & FLAGS_SECTOR_SKIPPED)  != FLAGS_SECTOR_SKIPPED)
                    {
                        stream.Read(dataSizeBytes, 0, 2);
                        teleDiskData.DataSize = BitConverter.ToUInt16(dataSizeBytes, 0);
                        teleDiskData.DataSize--; // Sydex decided to including dataEncoding byte as part of it
                        teleDiskData.DataEncoding = (byte)stream.ReadByte();
                        byte[] data = new byte[teleDiskData.DataSize];
                        stream.Read(data, 0, teleDiskData.DataSize);
                    }

                    if(128 << teleDiskSector.SectorSize < imageInfo.SectorSize)
                        imageInfo.SectorSize = (uint)(128 << teleDiskSector.SectorSize);

                    totalSectors++;
                }
            }

            totalCylinders++;
            totalHeads++;

            if(totalCylinders <= 0 || totalHeads <= 0)
                throw new ImageNotSupportedException("No cylinders or heads found");

            bool hasLeadOutOnHead0 = false;
            bool hasLeadOutOnHead1 = false;
            imageInfo.Cylinders = (ushort)totalCylinders;
            imageInfo.Heads     = (byte)totalHeads;

            // Count sectors per track
            stream.Seek(currentPos, SeekOrigin.Begin);
            while(true)
            {
                TeleDiskTrackHeader teleDiskTrack = new TeleDiskTrackHeader
                {
                    Sectors  = (byte)stream.ReadByte(),
                    Cylinder = (byte)stream.ReadByte(),
                    Head     = (byte)stream.ReadByte(),
                    Crc      = (byte)stream.ReadByte()
                };

                if(teleDiskTrack.Sectors == 0xFF) // End of disk image
                    break;

                if(teleDiskTrack.Sectors < imageInfo.SectorsPerTrack)
                    if(teleDiskTrack.Cylinder + 1 == totalCylinders)
                    {
                        hasLeadOutOnHead0 |= teleDiskTrack.Head == 0;
                        hasLeadOutOnHead1 |= teleDiskTrack.Head == 1;
                        if(imageInfo.Cylinders == totalCylinders) imageInfo.Cylinders--;
                    }
                    else
                        imageInfo.SectorsPerTrack = teleDiskTrack.Sectors;

                for(byte processedSectors = 0; processedSectors < teleDiskTrack.Sectors; processedSectors++)
                {
                    TeleDiskSectorHeader teleDiskSector = new TeleDiskSectorHeader();
                    TeleDiskDataHeader   teleDiskData   = new TeleDiskDataHeader();
                    byte[]               dataSizeBytes  = new byte[2];

                    teleDiskSector.Cylinder     = (byte)stream.ReadByte();
                    teleDiskSector.Head         = (byte)stream.ReadByte();
                    teleDiskSector.SectorNumber = (byte)stream.ReadByte();
                    teleDiskSector.SectorSize   = (byte)stream.ReadByte();
                    teleDiskSector.Flags        = (byte)stream.ReadByte();
                    teleDiskSector.Crc          = (byte)stream.ReadByte();

                    if((teleDiskSector.Flags & FLAGS_SECTOR_DATALESS) == FLAGS_SECTOR_DATALESS ||
                       (teleDiskSector.Flags & FLAGS_SECTOR_SKIPPED)  == FLAGS_SECTOR_SKIPPED) continue;

                    stream.Read(dataSizeBytes, 0, 2);
                    teleDiskData.DataSize = BitConverter.ToUInt16(dataSizeBytes, 0);
                    teleDiskData.DataSize--; // Sydex decided to including dataEncoding byte as part of it
                    teleDiskData.DataEncoding = (byte)stream.ReadByte();
                    byte[] data = new byte[teleDiskData.DataSize];
                    stream.Read(data, 0, teleDiskData.DataSize);
                }
            }

            sectorsData = new byte[totalCylinders][][][];
            // Total sectors per track
            uint[][] spts = new uint[totalCylinders][];

            DicConsole.DebugWriteLine("TeleDisk plugin",
                                      "Found {0} cylinders and {1} heads with a maximum sector number of {2}",
                                      totalCylinders, totalHeads, maxSector);

            // Create heads
            for(int i = 0; i < totalCylinders; i++)
            {
                sectorsData[i] = new byte[totalHeads][][];
                spts[i]        = new uint[totalHeads];

                for(int j = 0; j < totalHeads; j++) sectorsData[i][j] = new byte[maxSector + 1][];
            }

            // Decode the image
            stream.Seek(currentPos, SeekOrigin.Begin);
            while(true)
            {
                TeleDiskTrackHeader teleDiskTrack = new TeleDiskTrackHeader();
                byte[]              tdTrackForCrc = new byte[3];

                teleDiskTrack.Sectors  = (byte)stream.ReadByte();
                teleDiskTrack.Cylinder = (byte)stream.ReadByte();
                teleDiskTrack.Head     = (byte)stream.ReadByte();
                teleDiskTrack.Crc      = (byte)stream.ReadByte();

                tdTrackForCrc[0] = teleDiskTrack.Sectors;
                tdTrackForCrc[1] = teleDiskTrack.Cylinder;
                tdTrackForCrc[2] = teleDiskTrack.Head;

                byte tdTrackCalculatedCrc = (byte)(TeleDiskCrc(0, tdTrackForCrc) & 0xFF);

                DicConsole.DebugWriteLine("TeleDisk plugin", "Track follows");
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tTrack cylinder: {0}\t",   teleDiskTrack.Cylinder);
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tTrack head: {0}\t",       teleDiskTrack.Head);
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tSectors in track: {0}\t", teleDiskTrack.Sectors);
                DicConsole.DebugWriteLine("TeleDisk plugin", "\tTrack header CRC: 0x{0:X2} (calculated 0x{1:X2})\t",
                                          teleDiskTrack.Crc, tdTrackCalculatedCrc);

                aDiskCrcHasFailed |= tdTrackCalculatedCrc != teleDiskTrack.Crc;

                if(teleDiskTrack.Sectors == 0xFF) // End of disk image
                {
                    DicConsole.DebugWriteLine("TeleDisk plugin", "End of disk image arrived");
                    DicConsole.DebugWriteLine("TeleDisk plugin", "Total of {0} data sectors, for {1} bytes",
                                              totalSectors, totalDiskSize);

                    break;
                }

                for(byte processedSectors = 0; processedSectors < teleDiskTrack.Sectors; processedSectors++)
                {
                    TeleDiskSectorHeader teleDiskSector = new TeleDiskSectorHeader();
                    TeleDiskDataHeader   teleDiskData   = new TeleDiskDataHeader();
                    byte[]               dataSizeBytes  = new byte[2];
                    byte[]               decodedData;

                    teleDiskSector.Cylinder     = (byte)stream.ReadByte();
                    teleDiskSector.Head         = (byte)stream.ReadByte();
                    teleDiskSector.SectorNumber = (byte)stream.ReadByte();
                    teleDiskSector.SectorSize   = (byte)stream.ReadByte();
                    teleDiskSector.Flags        = (byte)stream.ReadByte();
                    teleDiskSector.Crc          = (byte)stream.ReadByte();

                    DicConsole.DebugWriteLine("TeleDisk plugin", "\tSector follows");
                    DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tAddressMark cylinder: {0}",
                                              teleDiskSector.Cylinder);
                    DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tAddressMark head: {0}", teleDiskSector.Head);
                    DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tAddressMark sector number: {0}",
                                              teleDiskSector.SectorNumber);
                    DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector size: {0}",
                                              teleDiskSector.SectorSize);
                    DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector flags: 0x{0:X2}", teleDiskSector.Flags);
                    DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tSector CRC (plus headers): 0x{0:X2}",
                                              teleDiskSector.Crc);

                    uint lba = (uint)(teleDiskSector.Cylinder * header.Sides * imageInfo.SectorsPerTrack +
                                      teleDiskSector.Head                    * imageInfo.SectorsPerTrack +
                                      (teleDiskSector.SectorNumber - 1));
                    if((teleDiskSector.Flags & FLAGS_SECTOR_DATALESS) != FLAGS_SECTOR_DATALESS &&
                       (teleDiskSector.Flags & FLAGS_SECTOR_SKIPPED)  != FLAGS_SECTOR_SKIPPED)
                    {
                        stream.Read(dataSizeBytes, 0, 2);
                        teleDiskData.DataSize = BitConverter.ToUInt16(dataSizeBytes, 0);
                        teleDiskData.DataSize--; // Sydex decided to including dataEncoding byte as part of it
                        imageInfo.ImageSize       += teleDiskData.DataSize;
                        teleDiskData.DataEncoding =  (byte)stream.ReadByte();
                        byte[] data = new byte[teleDiskData.DataSize];
                        stream.Read(data, 0, teleDiskData.DataSize);
                        DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tData size (in-image): {0}",
                                                  teleDiskData.DataSize);
                        DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tData encoding: 0x{0:X2}",
                                                  teleDiskData.DataEncoding);

                        decodedData = DecodeTeleDiskData(teleDiskSector.SectorSize, teleDiskData.DataEncoding, data);

                        byte tdSectorCalculatedCrc = (byte)(TeleDiskCrc(0, decodedData) & 0xFF);

                        if(tdSectorCalculatedCrc != teleDiskSector.Crc)
                        {
                            DicConsole.DebugWriteLine("TeleDisk plugin",
                                                      "Sector {0}:{3}:{4} calculated CRC 0x{1:X2} differs from stored CRC 0x{2:X2}",
                                                      teleDiskTrack.Cylinder, tdSectorCalculatedCrc, teleDiskSector.Crc,
                                                      teleDiskTrack.Cylinder, teleDiskSector.SectorNumber);
                            if((teleDiskSector.Flags & FLAGS_SECTOR_NO_ID) != FLAGS_SECTOR_NO_ID)
                                sectorsWhereCrcHasFailed.Add(lba);
                        }
                    }
                    else decodedData = new byte[128 << teleDiskSector.SectorSize];

                    DicConsole.DebugWriteLine("TeleDisk plugin", "\t\tLBA: {0}", lba);

                    if((teleDiskSector.Flags & FLAGS_SECTOR_NO_ID) == FLAGS_SECTOR_NO_ID) continue;

                    if(sectorsData[teleDiskTrack.Cylinder][teleDiskTrack.Head][teleDiskSector.SectorNumber] != null)
                        DicConsole.DebugWriteLine("TeleDisk plugin",
                                                  (teleDiskSector.Flags & FLAGS_SECTOR_DUPLICATE) ==
                                                  FLAGS_SECTOR_DUPLICATE
                                                      ? "\t\tSector {0} on cylinder {1} head {2} is duplicate, and marked so"
                                                      : "\t\tSector {0} on cylinder {1} head {2} is duplicate, but is not marked so",
                                                  teleDiskSector.SectorNumber, teleDiskSector.Cylinder,
                                                  teleDiskSector.Head);
                    else
                    {
                        sectorsData[teleDiskTrack.Cylinder][teleDiskTrack.Head][teleDiskSector.SectorNumber] =
                            decodedData;
                        totalDiskSize += (uint)decodedData.Length;
                    }
                }
            }

            MemoryStream leadOutMs = new MemoryStream();
            if(hasLeadOutOnHead0)
                for(int i = 0; i < sectorsData[totalCylinders - 1][0].Length; i++)
                    if(sectorsData[totalCylinders - 1][0][i] != null)
                        leadOutMs.Write(sectorsData[totalCylinders - 1][0][i], 0,
                                        sectorsData[totalCylinders - 1][0][i].Length);
            if(hasLeadOutOnHead1)
                for(int i = 0; i < sectorsData[totalCylinders - 1][1].Length; i++)
                    if(sectorsData[totalCylinders - 1][1][i] != null)
                        leadOutMs.Write(sectorsData[totalCylinders - 1][1][i], 0,
                                        sectorsData[totalCylinders - 1][1][i].Length);

            if(leadOutMs.Length != 0)
            {
                leadOut = leadOutMs.ToArray();
                imageInfo.ReadableMediaTags.Add(MediaTagType.Floppy_LeadOut);
            }

            imageInfo.Sectors   = imageInfo.Cylinders * imageInfo.Heads * imageInfo.SectorsPerTrack;
            imageInfo.MediaType = DecodeTeleDiskDiskType();

            imageInfo.XmlMediaType = XmlMediaType.BlockMedia;

            DicConsole.VerboseWriteLine("TeleDisk image contains a disk of type {0}", imageInfo.MediaType);
            if(!string.IsNullOrEmpty(imageInfo.Comments))
                DicConsole.VerboseWriteLine("TeleDisk comments: {0}", imageInfo.Comments);

            inStream.Dispose();
            stream.Dispose();

            return true;
        }