public DiskReader2(DiskReaderParams2 pars)
 {
     Params = pars;
 }
        public object Clone()
        {
            DiskReaderParams2 r = (DiskReaderParams2)MemberwiseClone();

            return(r);
        }
        public unsafe void ReadTrack(int track, DiskReaderParams2 pars, ScanMode scanMode)
        {
            // Массив sectors:
            // D0 - Признак обработанного сектора при чтении трека в одной попытке. Обнуляется перед каждой попыткой чтения трека.
            // D1 - Используется в блоке чтения заголовков. Отмечаются сектора заголовки которых были найдены. Вне этого блока не используется.
            // D2 - Запрет чтения сектора из-за того что его заголовок был прочитан SectorReadAttempts раз и не был найден. Параметр сохраняется между попытками чтения трека.

            TrackFormat trackF = pars.Image.Tracks[track];

            const int sectorArrayLen = 50;
            int *     sectors        = stackalloc int[sectorArrayLen];

            for (int i = 0; i < sectorArrayLen; i++)
            {
                sectors[i] = 0;
            }
            bool trackScanned = false;

            for (int attempt = 0; attempt < pars.SectorReadAttempts; attempt++)
            {
                if (Aborted)
                {
                    return;
                }

                // Сканирование трека.

                if (
                    ((scanMode == ScanMode.Once && !trackScanned) ||
                     (scanMode == ScanMode.UnscannedOnly && trackF.FormatName == TrackFormatName.Unscanned) ||
                     scanMode == ScanMode.EachTrackRead)

                    && (trackF.MaxGap() > 128 + TrackFormat.MinSectorHeaderSize || trackF.ContainsCalculatedTime())
                    )
                {
                    // workTrackFormat и longestTrackFormat используются чтобы не плодить объекты.
                    // Объект scanFormatBuffer используется функциями ScanFormat и CombineFormats, поэтому его брать нельзя.

                    trackF.Scanning    = true;
                    trackF.MapModified = true;
                    if (ScanFormat(workTrackFormat, track, true))
                    {
                        for (int i = 0; i < workTrackFormat.Layout.Cnt; i++)
                        {
                            workTrackFormat.Layout.Data[i].MapCellValue = MapCell.Unprocessed;
                        }
                        if (CombineFormats(track, trackF, workTrackFormat, longestTrackFormat))
                        {
                            trackF.Assign(longestTrackFormat);
                            trackF.MapModified = true;
                        }
                    }
                    trackF.Scanning    = false;
                    trackF.MapModified = true;
                    trackScanned       = true;
                    if (Aborted)
                    {
                        return;
                    }
                }

                if (trackF.Layout.Cnt > sectorArrayLen)
                {
                    Log.Error?.Out($"Число секторов превышает размер рабочего массива: {trackF.Layout.Cnt}");
                    throw new Exception();
                }
                for (int i = 0; i < trackF.Layout.Cnt; i++)
                {
                    sectors[i] &= ~1;
                }
                bool wasError = false;
                trackTimer.Restart();
                int        skip             = 0;
                int        processedSectors = 0;
                int        sectorCounter    = 0;
                SectorInfo diskSector;
                while (processedSectors < trackF.Layout.Cnt)
                {
                    diskSector = trackF.Layout.Data[sectorCounter];
                    int sectorIndex = sectorCounter;
                    sectorCounter++;
                    skip++;
                    if ((sectors[sectorIndex] & 1) != 0)
                    {
                        continue;
                    }
                    sectors[sectorIndex] |= 1;
                    processedSectors++;
                    if ((sectors[sectorIndex] & 4) != 0)
                    {
                        continue;
                    }
                    if (trackF.Layout.Data[sectorIndex].ProcessResult == SectorProcessResult.Good)
                    {
                        continue;
                    }
                    skip = 0;
                    pars.Map.MarkSectorAsProcessing(track, sectorIndex);
                    WinApi.RtlZeroMemory(memoryHandle, (UIntPtr)diskSector.SizeBytes);
                    int    error            = Driver.ReadSectorF(DriverHandle, memoryHandle, diskSector.Cylinder, diskSector.SectorNumber, diskSector.SizeCode, track & 1, diskSector.Head, 0x0a, 0xff);
                    double curTimeSinceSync = trackF.Timer.ElapsedMs;
                    bool   badWritten       = diskSector.ProcessResult == SectorProcessResult.Bad;
                    if (error == 0)
                    {
                        pars.Image.WriteGoodSector(memoryHandle, trackF, sectorIndex);
                    }
                    else
                    {
                        wasError = true;
                        // Если надо проверить CRC-Error заголовка: (error == 27 && curTimeSinceSync > trackF.SpinTime)
                        bool noHeader = error == 21 || error == 1112 || error == 27;
                        if (noHeader)
                        {
                            if (!badWritten)
                            {
                                pars.Image.WriteNoHeader(memoryHandle, trackF, sectorIndex);
                            }
                            Make30HeadPositionings(error, track);
                        }
                        else if (error == 23)
                        {
                            pars.Image.WriteBadSector(memoryHandle, trackF, sectorIndex);
                            badWritten = true;
                        }
                        else
                        {
                            Log.Info?.Out($"Необработанная ошибка при чтении сектора: {error}");
                        }
                    }
                    if (Aborted)
                    {
                        return;
                    }
                }
                Log.Trace?.Out($"Время чтения трека: {GP.ToString(trackTimer.ElapsedMs, 2)}");
                trackTimer.Restart();
                if (!wasError)
                {
                    break;
                }
            }
        }