public void GetTrackSectorByMousePosition(int x, int y, out int track, out int sectorIndex) { track = x >= 0 ? x / CellWidth : -1; if (track > MaxTrack) { track = MaxTrack; } sectorIndex = -1; if (track < 0 || Image == null || track >= Image.Tracks.Cnt || y < headerHeight) { return; } TrackFormat tf = Image?.Tracks[track]; if (tf == null || tf.Layout.Cnt == 0) { return; } int position = y - headerHeight; for (int i = 0; i < tf.Layout.Cnt; i++) { if (position >= tf.Layout.Data[i].MapPoint1 && position < tf.Layout.Data[i].MapPoint2) { sectorIndex = i; return; } } return; }
public unsafe void AssignLayout(TrackFormat layout, int track, int syncSectorNumber = int.MinValue, int firstIndex = 0) { int minSectorIndex = 0; int minSectorNumber = int.MaxValue; for (int i = firstIndex; i < layout.Layout.Cnt; i++) { if (layout.Layout.Data[i].SectorNumber < minSectorNumber) { minSectorNumber = layout.Layout.Data[i].SectorNumber; minSectorIndex = i; } } int len = Math.Max(0, layout.Layout.Cnt - firstIndex); Layout.EnsureCapacity(len); Layout.Cnt = len; for (int i = 0, j = minSectorIndex; i < len; i++) { Layout.Data[i] = layout.Layout.Data[j]; if (syncSectorNumber != int.MinValue && Layout.Data[i].SectorNumber == syncSectorNumber) { SyncByHeader(track, syncSectorNumber); } j++; if (j >= layout.Layout.Cnt) { j = firstIndex; } } FormatName = GetFormatName(); SpinTime = GetSpinTime(); LayoutTrack = track; }
public object Clone() { TrackFormat r = (TrackFormat)MemberwiseClone(); r.Layout = new MList <SectorInfo>(Layout, true, false); r.Timer = (Timer)Timer.Clone(); return(r); }
public void WriteNoHeader(IntPtr memoryHandle, TrackFormat trackF, int sectorIndex) { trackF.Layout.Data[sectorIndex].WriteData(memoryHandle); trackF.Layout.Data[sectorIndex].ProcessResult = SectorProcessResult.NoHeader; Modified = true; // %%% Здесь должен быть вызов Map и функции SectorsChanged(sectorNum, sectorCount); trackF.Layout.Data[sectorIndex].MapCellValue = MapCell.NoHeader; // это наверное надо перенести в Map. trackF.MapModified = true; }
public void UnmarkTrack(int track, MapCell mask) { TrackFormat tf = Image.Tracks.Data[track]; for (int i = 0; i < tf.Layout.Cnt; i++) { tf.Layout.Data[i].MapCellValue &= ~mask; } tf.MapModified = true; }
public void Assign(TrackFormat source) { Layout.CopyArray(source.Layout); Timer.Assign(source.Timer); LayoutTrack = source.LayoutTrack; LongSectorIndex = source.LongSectorIndex; syncSectorIndex = source.syncSectorIndex; syncTrack = source.syncTrack; SpinTime = source.SpinTime; FormatName = source.FormatName; }
public bool ContainsOtherSectors(TrackFormat trackFormat, int cylinder) { for (int i = 0; i < Layout.Cnt; i++) { int index = trackFormat.FindSectorIndex(Layout.Data[i].SectorNumber); if ((index < 0) || (cylinder >= 0 && Layout.Data[i].Cylinder != cylinder)) { return(true); } } return(false); }
public void MarkSectorRange(int firstTrack, int lastTrack, SectorProcessResult processResult, MapCell mapCell) { for (int i = firstTrack; i < lastTrack; i++) { TrackFormat tf = Tracks[i]; for (int j = 0; j < tf.Layout.Cnt; j++) { tf.Layout.Data[j].ProcessResult = processResult; tf.Layout.Data[j].MapCellValue = mapCell; } tf.MapModified = true; } Modified = true; }
public bool ContainsSectorsFrom(TrackFormat trackFormat, int cylinder) { for (int i = 0; i < Layout.Cnt; i++) { int index = trackFormat.FindSectorIndex(Layout.Data[i].SectorNumber); if (index >= 0 && Layout.Data[i].SizeCode == trackFormat.Layout.Data[index].SizeCode && (cylinder < 0 || Layout.Data[i].Cylinder == cylinder)) { return(true); } } return(false); }
public void WriteGoodSector(IntPtr memoryHandle, TrackFormat trackF, int sectorIndex) { trackF.Layout.Data[sectorIndex].WriteData(memoryHandle); trackF.Layout.Data[sectorIndex].ProcessResult = SectorProcessResult.Good; Modified = true; // %%% Здесь должен быть вызов Map и функции SectorsChanged(sectorNum, sectorCount); if (DiskImage.AllBytes(trackF.Layout.Data[sectorIndex].Data, 0, trackF.Layout.Data[sectorIndex].SizeBytes, 0)) { trackF.Layout.Data[sectorIndex].MapCellValue = MapCell.Zero; } else { trackF.Layout.Data[sectorIndex].MapCellValue = MapCell.Good; // это наверное надо перенести в Map. } trackF.MapModified = true; }
/// <summary> /// Синхронизация сектора с другим объектом TrackFormat. /// </summary> /// <param name="trackFormat"></param> public void Sync(TrackFormat trackFormat) { if (trackFormat == null || trackFormat.Layout.Cnt == 0) { return; } int sectorNum = trackFormat.Layout.Data[trackFormat.syncSectorIndex].SectorNumber; int newSyncIndex = FindSectorIndex(sectorNum); if (newSyncIndex < 0) { return; } Timer.Assign(trackFormat.Timer); syncSectorIndex = newSyncIndex; syncTrack = trackFormat.syncTrack; }
private void ChartArea_MouseDown(object sender, MouseEventArgs e) { GetTrackSectorByMousePosition(e.X, e.Y, out track, out sectorIndex); if (e.Button == MouseButtons.Right) { ClearHighlight(MapCell.Highlighted | MapCell.Hover); TrackFormat tf = Image.Tracks.Data[track]; bool sectorSelected = sectorIndex >= 0 && sectorIndex < tf.Layout.Cnt && track >= 0 && track < MainForm.MaxTrack; if (sectorSelected) { tf.Layout.Data[sectorIndex].MapCellValue |= MapCell.Hover; tf.MapModified = true; int diskSector = tf.Layout.Data[sectorIndex].SectorNumber; contextMenuTopItem.Text = $"Track: {track} Sector: {diskSector}"; } else { contextMenuTopItem.Text = $"Track: {track}"; } markAsUnprocessed.Enabled = sectorSelected; markAsGood.Enabled = sectorSelected; viewSectorContents.Enabled = sectorSelected && (tf[sectorIndex].ProcessResult == SectorProcessResult.Good || tf[sectorIndex].ProcessResult == SectorProcessResult.Bad); Repaint(); mapMouseLeaveIgnore = true; contextMenu.Show(ChartArea, new Point(e.X, e.Y)); return; } if (e.Button != MouseButtons.Left) { return; } ClearHighlight(); if (CanEditReadBounds) { selecting = true; } ChartArea_MouseMove(sender, e); }
/// <summary> /// /// </summary> /// <param name="result">0 - head определен. 1 - Заголовки не найдены. 2 - Заголовок найден, но он не TR-DOS</param> /// <param name="track"></param> /// <param name="trackFormat"></param> /// <returns></returns> private int ScanHeadParameter(ref UpperSideHead result, int track, TrackFormat trackFormat) { tagFD_READ_ID_PARAMS pars = new tagFD_READ_ID_PARAMS() { flags = Driver.FD_OPTION_MFM, head = (byte)(track & 1) }; tagFD_CMD_RESULT cmdResult = new tagFD_CMD_RESULT(); if (!Driver.ReadId(DriverHandle, pars, out cmdResult)) { Log.Trace?.Out($"Функция ReadId вернула false. Cylinder: {cmdResult.cyl} | Sector: {cmdResult.sector} | Size: {cmdResult.size} | Head: {cmdResult.head} | LastError: {Marshal.GetLastWin32Error()}"); return(1); } //if (cmdResult.sector < 1 || cmdResult.sector > 16 || cmdResult.size != 1) //{ // Log.Trace?.Out($"Формат не TR-DOS. Cylinder: {cmdResult.cyl} | Sector: {cmdResult.sector} | Size: {cmdResult.size} | Head: {cmdResult.head}"); // return 2; //} trackFormat.SyncByHeader(track, cmdResult.sector); Log.Trace?.Out($"Cylinder: {cmdResult.cyl} | Sector: {cmdResult.sector} | Size: {cmdResult.size} | Head: {cmdResult.head}"); result = (UpperSideHead)cmdResult.head; return(0); }
public unsafe int ReadRandomSectors(TimeSpan timeout, int stopOnNthFail = 0) { int goodSectors = Params.Image.GoodSectors; int imageTracks = Params.Image.SizeTracks; try { bool useTimeout = timeout > TimeSpan.Zero; DateTime timeoutTime = DateTime.Now.Add(timeout); Timer sectorTimer = new Timer(); Random random = new Random(); MList <Point> sectorArray = new MList <Point>(Params.Image.SizeSectors); for (int track = Params.FirstTrack; track < Params.LastTrack; track++) { if (Params.Side == DiskSide.Side0 && track % 2 != 0) { continue; } if (Params.Side == DiskSide.Side1 && track % 2 != 1) { continue; } for (int sector = 0; sector < Params.Image[track].Layout.Cnt; sector++) { if (Params.Image[track][sector].ProcessResult != SectorProcessResult.Good) { sectorArray.Add(new Point(track, sector)); } } } int prevCylinder = -1; int failCounter = 0; while (sectorArray.Cnt > 0 && ((useTimeout && DateTime.Now < timeoutTime) || !useTimeout) && (stopOnNthFail == 0 || (failCounter < stopOnNthFail))) { int index = random.Next(sectorArray.Cnt); int track = sectorArray[index].X; int sectorIndex = sectorArray[index].Y; SectorInfo sector = Params.Image[track][sectorIndex]; TrackFormat tf = Params.Image[track]; int error = 23; Params.Map.MarkSectorAsProcessing(track, sectorIndex); bool badWritten = sector.ProcessResult == SectorProcessResult.Bad; for (int attempt = 0; attempt < Params.SectorReadAttempts; attempt++) { if (Aborted) { goto abort; } int cylinder = track / 2; if (cylinder == prevCylinder) { int tempCylinder = cylinder + (random.Next(2) == 0 ? -1 : 1); tempCylinder = Math.Max(0, tempCylinder); tempCylinder = Math.Min(tempCylinder, Params.LastTrack / 2); Driver.Seek(DriverHandle, tempCylinder * 2); if (Aborted) { goto abort; } Thread.Sleep(random.Next((int)TrackFormat.SpinTimeStandard)); // Ждем случайное время чтобы приехать на нужный цилиндр в случайной точке. } prevCylinder = cylinder; Driver.Seek(DriverHandle, track); if (Aborted) { goto abort; } WinApi.RtlZeroMemory(memoryHandle, (UIntPtr)sector.SizeBytes); sectorTimer.Start(); error = Driver.ReadSectorF(DriverHandle, memoryHandle, sector.Cylinder, sector.SectorNumber, sector.SizeCode, track & 1, sector.Head, 0x0a, 0xff); sectorTimer.Stop(); if (error == 0) { Params.Image.WriteGoodSector(memoryHandle, tf, sectorIndex); sectorArray.RemoveAt(index); failCounter = 0; timeoutTime = DateTime.Now.Add(timeout); break; } // Ошибка 27 может быть как в случае отсутствия заголовка, так и в случае когда заголовок имеет ошибку CRC. // Тут сложный вопрос что с этим делать: писать сектор как CrcError или как NoHeader. Пишется как NoHeader. // (проверить была ошибка CRC в заголовке или заголовок вообще не был найден можно сравнив sectorTimer.ElapsedMs с временем вращения tf.SpinTime) bool noHeader = error == 21 || error == 1112 || error == 27; Make30HeadPositionings(error, track); if (error == 23) { Params.Image.WriteBadSector(memoryHandle, tf, sectorIndex); badWritten = true; } else if (noHeader && !badWritten) { Params.Image.WriteNoHeader(memoryHandle, tf, sectorIndex); } } failCounter++; } return(Params.Image.GoodSectors - goodSectors); abort: Log.Info?.Out("Чтение прервано."); } finally { Params.Map?.ClearHighlight(MapCell.Processing); } return(Params.Image.GoodSectors - goodSectors); }
private void ChartArea_MouseMove(object sender, MouseEventArgs e) { if (Image == null) { return; } int track; int sectorIndex; GetTrackSectorByMousePosition(e.X, e.Y, out track, out sectorIndex); TrackFormat tf = (track >= 0 && track < Image.Tracks.Cnt) ? Image.Tracks[track] : null; if (selecting) { int minTrack = Math.Min(track, this.track); minTrack = Math.Max(0, minTrack); int maxTrack = Math.Max(track, this.track); maxTrack = Math.Min(maxTrack, MainForm.MaxTrack); if (maxTrack == minTrack) { maxTrack++; } if (maxTrack > MainForm.MaxTrack) { maxTrack--; minTrack--; if (minTrack < 0) { minTrack = 0; } } Select(minTrack, maxTrack); ReadBoundsChanged?.Invoke(this, null); } TrackLV.Text = track.ToString(); if (sectorIndex >= 0) { int diskSector = tf.Layout.Data[sectorIndex].SectorNumber; SectorLV.Text = diskSector.ToString(); StatusLV.Text = tf.Layout.Data[sectorIndex].ProcessResult.ToString(); SectorSizeLV.Text = tf.Layout.Data[sectorIndex].SizeBytes.ToString(); CylLV.Text = tf.Layout.Data[sectorIndex].Cylinder.ToString(); HeadLV.Text = tf.Layout.Data[sectorIndex].Head.ToString(); if (!selecting) { ClearHighlight(MapCell.Highlighted | MapCell.Hover); tf.Layout.Data[sectorIndex].MapCellValue |= MapCell.Hover; tf.MapModified = true; } } else { if (track >= 0 && track < Image.Tracks.Cnt) { if (Image[track].FormatName == TrackFormatName.Unscanned) { StatusLV.Text = TrackFormatName.Unscanned.ToString(); } else if (Image[track].Scanning) { StatusLV.Text = "Scanning"; } else { StatusLV.Text = ""; } ClearSectorInfo(false, false); } else { ClearSectorInfo(false, true); } ClearHighlight(MapCell.Hover); } Repaint(); }
private bool PaintMap() { bool paintMap = false; for (int i = 0; i < Image.Tracks.Cnt; i++) { if (Image.Tracks.Data[i].MapModified) { paintMap = true; break; } } bool paintHeader = FirstTrack != firstTrackOld || LastTrack != lastTrackOld; if (!paintMap && !paintHeader) { return(false); } IntPtr DC = ChartArea.bDC; IntPtr penNull = WinApi.CreatePen(WinApi.PS_NULL, 1, (uint)ColorTranslator.ToWin32(Color.White)); IntPtr oldBrush = WinApi.SelectObject(DC, WinApi.GetStockObject(WinApi.StockObjects.DC_BRUSH)); IntPtr oldPen = WinApi.SelectObject(DC, penNull); IntPtr penBlack = WinApi.CreatePen(WinApi.PS_SOLID, 1, (uint)ColorTranslator.ToWin32(Color.Black)); if (paintMap) { int top = headerHeight + upperBoundHeight; for (int track = 0; track < Image.Tracks.Cnt; track++) { TrackFormat tf = Image.Tracks.Data[track]; if (!tf.MapModified) { continue; } tf.MapModified = false; if (tf.Scanning) { WinApi.SetDCBrushColor(DC, GetCellColor(MapCell.Scanning)); WinApi.Rectangle(DC, CellWidth * track, top, CellWidth * (track + 1) + 1, top + TrackHeight + 1); continue; } if (tf.FormatName == TrackFormatName.Unscanned) { WinApi.SetDCBrushColor(DC, GetCellColor(MapCell.Unprocessed)); WinApi.Rectangle(DC, CellWidth * track, top, CellWidth * (track + 1) + 1, top + TrackHeight + 1); continue; } double prevSectorEnd = 0; double cumulativeTime = 0; double spinTime = tf.SpinTime; if (spinTime == 0) { spinTime = TrackFormat.SpinTimeStandard; } bool gapPainted = true; int prevColor = 0; for (int j = 0; j < tf.Layout.Cnt; j++) { double sectorStart = j == 0 ? 0 : cumulativeTime + tf.Layout.Data[j].TimeMs - TrackFormat.NormalSectorHeaderSize / tf.BytesPerMs; cumulativeTime += j == 0 ? 59 / tf.BytesPerMs : tf.Layout.Data[j].TimeMs; double sectorEnd = sectorStart + (tf.Layout.Data[j].SizeBytes + TrackFormat.NormalSectorHeaderSize) / tf.BytesPerMs; double prevSectorEndRem = prevSectorEnd; prevSectorEnd = sectorEnd; MapCell cell = tf.Layout.Data[j].MapCellValue; if (j != 0) { int gapPoint1 = (int)Math.Round(prevSectorEndRem / spinTime * TrackHeight); int gapPoint2 = (int)Math.Round(sectorStart / spinTime * TrackHeight); gapPainted = gapPoint2 > gapPoint1; if (gapPainted) { WinApi.SetDCBrushColor(DC, ColorTranslator.ToWin32(Color.WhiteSmoke)); WinApi.Rectangle(DC, CellWidth * track, top + gapPoint1, CellWidth * (track + 1) + 1, top + gapPoint2 + 1); } } int point1 = (int)Math.Round(sectorStart / spinTime * TrackHeight); int point2 = (int)Math.Round(sectorEnd / spinTime * TrackHeight); tf.Layout.Data[j].MapPoint1 = point1; tf.Layout.Data[j].MapPoint2 = point2; int color = GetCellColor(cell); if (!gapPainted && color == prevColor) { color += 0x00202020; } prevColor = color; WinApi.SetDCBrushColor(DC, color); if ((cell & MapCell.Hover) != 0) { IntPtr penX = WinApi.SelectObject(DC, penBlack); WinApi.Rectangle(DC, CellWidth * track, top + point1, CellWidth * (track + 1), top + point2); WinApi.SelectObject(DC, penX); } else { WinApi.Rectangle(DC, CellWidth * track, top + point1, CellWidth * (track + 1) + 1, top + point2 + 1); } } int gapPoint1x = (int)Math.Round(prevSectorEnd / spinTime * TrackHeight); WinApi.SetDCBrushColor(DC, ColorTranslator.ToWin32(Color.WhiteSmoke)); WinApi.Rectangle(DC, CellWidth * track, top + gapPoint1x, CellWidth * (track + 1) + 1, top + TrackHeight + 1); } } if (paintHeader) { firstTrackOld = FirstTrack; lastTrackOld = LastTrack; WinApi.SetDCBrushColor(DC, ColorTranslator.ToWin32(backColor)); WinApi.Rectangle(DC, 0, stripHeight, MaxTrack * CellWidth + 1, headerHeight + 1); if (FirstTrack > 0) { WinApi.Rectangle(DC, 0, 0, FirstTrack * CellWidth + 1, stripHeight + 1); } if (LastTrack < MaxTrack) { WinApi.Rectangle(DC, LastTrack * CellWidth, 0, MaxTrack * CellWidth + 1, stripHeight + 1); } WinApi.SetDCBrushColor(DC, ColorTranslator.ToWin32(Color.Black)); WinApi.Rectangle(DC, FirstTrack * CellWidth, 0, LastTrack * CellWidth + 1, stripHeight + 1); WinApi.Rectangle(DC, 160 * CellWidth, stripHeight + 1, 172 * CellWidth + 1, headerHeight); // Upper and bottom lines painting. WinApi.Rectangle(DC, 0, headerHeight, MaxTrack * CellWidth + 1, headerHeight + upperBoundHeight); WinApi.Rectangle(DC, 0, headerHeight + upperBoundHeight + TrackHeight, MaxTrack * CellWidth + 1, headerHeight + upperBoundHeight + TrackHeight + bottomBoundHeight); } WinApi.SelectObject(DC, oldPen); WinApi.SelectObject(DC, oldBrush); WinApi.DeleteObject(penNull); WinApi.DeleteObject(penBlack); return(true); }
public bool DoesSatisfyFormat(TrackFormat trackFormat, int cylinder) { return(ContainsSectorsFrom(trackFormat, cylinder) && !ContainsOtherSectors(trackFormat, cylinder)); }
public void LoadFormatFromXml(string fileName) { int track = 0; MList <SectorInfo> layout = new MList <SectorInfo>(50); MList <SectorInfo> temp = new MList <SectorInfo>(50); using (XmlTextReader xml = new XmlTextReader(fileName)) { while (xml.Read()) { if (xml.NodeType == XmlNodeType.EndElement) { break; } if (xml.NodeType != XmlNodeType.Element) { continue; } if (xml.Name == "DiskFormat") { string name = xml.GetAttribute("Name"); if (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(name)) { Name = name; } while (xml.Read()) { if (xml.NodeType == XmlNodeType.EndElement) { break; } if (xml.NodeType != XmlNodeType.Element) { continue; } switch (xml.Name) { case "Track": TrackFormat tf = Tracks[track]; tf.Layout.EnsureCapacity(Int32.Parse(xml.GetAttribute("SectorCount"))); layout.Cnt = 0; if (!xml.IsEmptyElement) { while (xml.Read()) { if (xml.NodeType == XmlNodeType.EndElement) { break; } if (xml.NodeType != XmlNodeType.Element) { continue; } switch (xml.Name) { case "Sector": SectorInfo s = new SectorInfo() { Cylinder = Int32.Parse(xml.GetAttribute("Cylinder")), Head = Int32.Parse(xml.GetAttribute("Head")), SectorNumber = Int32.Parse(xml.GetAttribute("Number")), SizeCode = Int32.Parse(xml.GetAttribute("Size")), TimeMs = Double.Parse(xml.GetAttribute("Time"), NumberStyles.Any, CultureInfo.InvariantCulture) }; layout.Add(s); break; } } } if (tf.Layout.Cnt > 0) { tf.Layout.CopyTo(temp); layout.CopyTo(tf.Layout); for (int i = 0; i < temp.Cnt; i++) { int index = tf.FindSectorIndex(temp[i].SectorNumber); if (index < 0) { Log.Info?.Out($"Сектор не найден. Трек: {track} | Сектор: {temp[i].SectorNumber}"); continue; } tf.Layout.Data[index].Data = temp[i].Data; tf.Layout.Data[index].ProcessResult = temp[i].ProcessResult; tf.Layout.Data[index].MapCellValue = temp[i].MapCellValue; tf.Layout.Data[index].TimeCalculated = false; } } else { layout.CopyTo(tf.Layout); for (int i = 0; i < layout.Cnt; i++) { tf.Layout.Data[i].MapCellValue = MapCell.Unprocessed; tf.Layout.Data[i].TimeCalculated = false; } } tf.FormatName = tf.GetFormatName(); tf.SpinTime = tf.GetSpinTime(); tf.MapModified = true; track++; break; } } } } } }
public unsafe byte[] ToFdi(string text) { int trackCount = SizeTracks; int sizeSectors = SizeSectors; int cylCount = (int)Math.Ceiling(trackCount / 2.0); int textLength = text != null ? text.Length : 0; int dataSize = SizeBytes + textLength + 1 + 7 * cylCount * 2 + sizeSectors * 7 + 14; byte[] data = new byte[dataSize]; data[0] = (byte)'F'; data[1] = (byte)'D'; data[2] = (byte)'I'; data[4] = (byte)cylCount; data[6] = 2; //data[8] = 0; // Смещение текста (комментария), 2 байта. Пишется ниже. //data[10] = 0; // Смещение данных, 2 байта. Пишется ниже. //data[12] = 0; // Длина дополнительной информации в заголовке, 2 байта. В этой версии - 0. //data[14] = 0; // Дополнительная информация ("Резерв для дальнейшей модернизации"). В текущей версии формата отсутствует. int index = 14; int trackOffset = 0; for (int track = 0; track < cylCount * 2; track++) { TrackFormat tf = Tracks[track]; fixed(byte *numRef = &data[index]) { *((int *)numRef) = trackOffset; } //data[index + 4] = 0; // 2 байта, всегда содержит 0 (резерв для модернизации формата). data[index + 6] = tf != null ? (byte)tf.Layout.Cnt : (byte)0; index += 7; int sectorOffset = 0; if (tf != null) { for (int sector = 0; sector < tf.Layout.Cnt; sector++) { data[index] = (byte)(track / 2); data[index + 1] = (byte)tf.Layout.Data[sector].Head; data[index + 2] = (byte)tf.Layout.Data[sector].SectorNumber; data[index + 3] = (byte)tf.Layout.Data[sector].SizeCode; data[index + 4] = (byte)(tf.Layout.Data[sector].ProcessResult == SectorProcessResult.Good ? 1 << tf.Layout.Data[sector].SizeCode : 0); fixed(byte *numRef = &data[index + 5]) { *((ushort *)numRef) = (ushort)sectorOffset; } sectorOffset += tf.Layout.Data[sector].SizeBytes; index += 7; } trackOffset += sectorOffset; } } int textOffset = index; if (textLength > 0) { Encoding.ASCII.GetBytes(text, 0, text.Length, data, textOffset); } int dataOffset = textOffset + textLength + 1; fixed(byte *numRef = &data[8]) { *((ushort *)numRef) = (ushort)textOffset; *((ushort *)(numRef + 2)) = (ushort)dataOffset; } int currentOffset = 0; for (int track = 0; track < trackCount; track++) { TrackFormat tf = Tracks[track]; if (tf == null) { continue; } for (int sector = 0; sector < tf.Layout.Cnt; sector++) { if (tf.Layout.Data[sector].Data == null) { continue; } Array.Copy(tf.Layout.Data[sector].Data, 0, data, currentOffset + dataOffset, tf.Layout.Data[sector].SizeBytes); currentOffset += tf.Layout.Data[sector].SizeBytes; } } return(data); }
/// <summary> /// Чтение вперед или назад. Возвращает количество успешно прочитанных секторов. /// </summary> /// <returns>Возвращает количество успешно прочитанных секторов.</returns> public int Read(ScanMode scanMode, bool forward) { int goodSectors = Params.Image.GoodSectors; GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GCLatencyMode oldGCLatencyMode = GCSettings.LatencyMode; try { GCSettings.LatencyMode = GCLatencyMode.LowLatency; int firstTrack = Params.FirstTrack; int prevCylinder = -1; int lastTrack = Params.LastTrack; trackTimer.Restart(); int track = forward ? firstTrack : lastTrack - 1; for (int p = firstTrack; p < lastTrack; p++, track = forward ? track + 1 : track - 1) { if (Aborted) { goto abort; } if (Params.Side == DiskSide.Side0 && (track % 2 != 0)) { continue; } if (Params.Side == DiskSide.Side1 && (track % 2 != 1)) { continue; } TrackFormat trackF = Params.Image[track]; bool scan = ((scanMode == ScanMode.Once) || (scanMode == ScanMode.UnscannedOnly && trackF.FormatName == TrackFormatName.Unscanned) || scanMode == ScanMode.EachTrackRead) && (trackF.MaxGap() > 128 + TrackFormat.MinSectorHeaderSize || trackF.ContainsCalculatedTime()); if (!scan && trackF.NotGoodSectors == 0) { continue; } int cylinder = track / 2; if (cylinder != prevCylinder) { Driver.Seek(DriverHandle, track); if (Aborted) { goto abort; } prevCylinder = cylinder; } ReadTrack(track, Params, scanMode); } return(Params.Image.GoodSectors - goodSectors); abort: Log.Info?.Out("Чтение прервано."); } finally { GCSettings.LatencyMode = oldGCLatencyMode; Params.Map?.ClearHighlight(MapCell.Processing); } return(Params.Image.GoodSectors - goodSectors); }
/// <summary> /// 0 - Нет ошибок. /// 1 - Размер файла слишком мал. /// 5 - Размер файла слишком мал и содержит не все данные содержимого секторов. /// 6 - Нет сигнатуры FDI. /// </summary> /// <param name="fileName"></param> /// <param name="map"></param> /// <returns></returns> public unsafe int LoadFdi(string fileName, byte[] data, out string text) { Clear(); text = null; if (data.Length < 14) { return(1); } if (data[0] != 'F' || data[1] != 'D' || data[2] != 'I') { return(6); } int cylCount; int textOffset; int dataOffset; fixed(byte *d = &data[0]) { cylCount = *(ushort *)(d + 4); textOffset = *(ushort *)(d + 8); dataOffset = *(ushort *)(d + 10); } int minFileSize = 7 * cylCount * 2 + 14; if (data.Length < minFileSize || data.Length < textOffset + 1) { return(1); } int tt = textOffset; for (; tt < data.Length; tt++) { if (data[tt] == 0) { break; } } int textLen = tt - textOffset; text = textLen > 0 ? Encoding.ASCII.GetString(data, textOffset, tt - textOffset) : null; int index = 14; for (int track = 0; track < cylCount * 2; track++) { int trackOffset; fixed(byte *d = &data[index]) { trackOffset = *((int *)d); } int sectorsOnTrack = data[index + 6]; TrackFormat tf = Tracks.Data[track]; tf.Layout.EnsureCapacity(sectorsOnTrack); tf.Layout.Cnt = sectorsOnTrack; tf.SpinTime = TrackFormat.SpinTimeStandard; tf.MapModified = true; index += 7; double totalTimeMs = 0; for (int sector = 0; sector < sectorsOnTrack; sector++) { tf.Layout.Data[sector].Cylinder = data[index]; tf.Layout.Data[sector].Head = data[index + 1]; tf.Layout.Data[sector].SectorNumber = data[index + 2]; tf.Layout.Data[sector].SizeCode = data[index + 3]; int code = data[index + 4] & 63; tf.Layout.Data[sector].ProcessResult = code == 0 ? SectorProcessResult.Bad : SectorProcessResult.Good; int sectorOffset; fixed(byte *d = &data[index + 5]) { sectorOffset = *((ushort *)d); } int sizeBytes = tf.Layout.Data[sector].SizeBytes; if (data.Length < dataOffset + trackOffset + sectorOffset + sizeBytes) { return(5); } if (tf.Layout.Data[sector].Data == null || tf.Layout.Data[sector].Data.Length < tf.Layout.Data[sector].SizeBytes) { tf.Layout.Data[sector].Data = new byte[tf.Layout.Data[sector].SizeBytes]; } tf.Layout.Data[sector].MapCellValue = tf.Layout.Data[sector].ProcessResult == SectorProcessResult.Good ? MapCell.Good : MapCell.CrcError; Array.Copy(data, dataOffset + trackOffset + sectorOffset, tf.Layout.Data[sector].Data, 0, sizeBytes); if (tf.Layout.Data[sector].ProcessResult == SectorProcessResult.Good && DiskImage.AllBytes(tf.Layout.Data[sector].Data, 0, tf.Layout.Data[sector].SizeBytes, 0)) { tf.Layout.Data[sector].MapCellValue = MapCell.Zero; } index += 7; tf.Layout.Data[sector].TimeCalculated = true; if (sector != 0) { tf.Layout.Data[sector].TimeMs = (tf.Layout.Data[sector - 1].SizeBytes + TrackFormat.NormalSectorHeaderSize + 1) / TrackFormat.BytesPerMsStandard; totalTimeMs += tf.Layout.Data[sector].TimeMs; } } tf.FormatName = tf.GetFormatName(); if (sectorsOnTrack > 0) { tf.Layout.Data[0].TimeMs = Math.Max(0, TrackFormat.SpinTimeStandard - totalTimeMs); } } FileName = fileName; Name = Path.GetFileNameWithoutExtension(fileName); Modified = false; return(0); }
public int LoadTrd(string fileName, byte[] data, bool modifiedTrd) { Clear(); const int sectorSize = 256; int sizeSectors = data.Length / sectorSize; if (sizeSectors * sectorSize != data.Length) { return(1); } int sizeTracks = sizeSectors / 16; FileName = fileName; Name = Path.GetFileNameWithoutExtension(fileName); for (int track = 0; track < sizeTracks; track++) { TrackFormat tf = Tracks[track]; tf.Layout.Cnt = 16; for (int sector = 0; sector < 16; sector++) { tf.Layout.Data[sector].Cylinder = track / 2; tf.Layout.Data[sector].Head = track & 1; tf.Layout.Data[sector].SectorNumber = sector + 1; tf.Layout.Data[sector].SizeCode = 1; tf.Layout.Data[sector].TimeMs = 12.5; tf.FormatName = TrackFormatName.TrDosSequential; tf.Layout.Cnt = 16; tf.SpinTime = TrackFormat.SpinTimeStandard; int adr = track * 16 * 256 + sector * 256; if (tf.Layout.Data[sector].Data == null || tf.Layout.Data[sector].Data.Length < 256) { tf.Layout.Data[sector].Data = new byte[256]; } Array.Copy(data, adr, tf.Layout.Data[sector].Data, 0, 256); tf.Layout.Data[sector].ProcessResult = SectorProcessResult.Good; tf.Layout.Data[sector].MapCellValue = MapCell.Good; if (modifiedTrd) { if (DiskImage.AllBytes(tf.Layout.Data[sector].Data, 0, 256, (byte)'B')) { tf.Layout.Data[sector].ProcessResult = SectorProcessResult.Bad; tf.Layout.Data[sector].MapCellValue = MapCell.CrcError; } else if (DiskImage.AllBytes(tf.Layout.Data[sector].Data, 0, 256, (byte)'N')) { tf.Layout.Data[sector].ProcessResult = SectorProcessResult.Unprocessed; tf.Layout.Data[sector].MapCellValue = MapCell.Unprocessed; } } if (tf.Layout.Data[sector].MapCellValue == MapCell.Good && DiskImage.AllBytes(tf.Layout.Data[sector].Data, 0, 256, 0)) { tf.Layout.Data[sector].MapCellValue = MapCell.Zero; } tf.Layout.Data[sector].TimeCalculated = true; } tf.MapModified = true; } Modified = false; return(0); }
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; } } }
/// <summary> /// Сканирование трека. Сканировать может в двух режимах: по времени оборота диска и по зацикливанию потока секторов. /// При сканировании по времени оборота диска скорость диска должна быть в районе 300 об/мин, т.е. замедлять диск не надо, иначе будут ошибки. /// Если byLooping == true, то конец трека определяется по зацикливанию потока секторов, но сектора сканируются не менее 150 мс и не более 1000 мс. /// </summary> /// <param name="track"></param> /// <returns></returns> public unsafe bool ScanFormat(TrackFormat result, int track, bool byLooping) { int ilayoutCnt = 0; scanFormatBuffer.Layout.Cnt = 0; SectorInfo[] layout = scanFormatBuffer.Layout.Data; tagFD_READ_ID_PARAMS pars = new tagFD_READ_ID_PARAMS() { flags = Driver.FD_OPTION_MFM, head = (byte)(track & 1) }; tagFD_CMD_RESULT cmdResult = new tagFD_CMD_RESULT(); scanFormatStopwatch.Restart(); scanFormatTotalTime.Stop(); int firstIndex = 1; for (int i = 0; i < scanFormatBuffer.Layout.Capacity; i++) { if (Aborted) { return(false); } if (!Driver.ReadId(DriverHandle, pars, out cmdResult)) { int error = Marshal.GetLastWin32Error(); Log.Trace?.Out($"Функция ReadId вернула false. i={i}. LastError={error}"); if (Aborted) { return(false); } Make30HeadPositionings(error, track); if (scanFormatBuffer.Layout.Cnt == 0) { goto success; } return(false); } double timeMs = scanFormatStopwatch.ElapsedMs; scanFormatStopwatch.Restart(); if (!scanFormatTotalTime.IsRunning) { scanFormatTotalTime.Restart(); } if (!byLooping && scanFormatTotalTime.ElapsedMs > 209) { goto success; } layout[ilayoutCnt] = new SectorInfo() { Cylinder = cmdResult.cyl, Head = cmdResult.head, SectorNumber = cmdResult.sector, SizeCode = cmdResult.size, TimeMs = timeMs }; ilayoutCnt++; scanFormatBuffer.Layout.Cnt = ilayoutCnt; // Бывают случаи когда заголовок сектора читается на первом обороте и не читается на втором. // Из-за этого алгоритм, в котором повтор сектора определяется сравнением с первым прочитанным заголовком, иногда даёт сбои. // Поэтому ищем встечался ли ранее только что прочитанный сектор среди всех прочитанных заголовков, а не только сравниваем с первым. // По отношению к найденному сектору замеряем время вращения. Если оно больше 250 мс, значит этот сектор был пропущен на одном из оборотов // и такую последовательность брать нельзя. Если время меньше 150, значит на треке есть сектора с одинаковыми параметрами. if (byLooping) { for (int u = 0; u < ilayoutCnt - 1; u++) { if (layout[u].Cylinder == cmdResult.cyl && layout[u].Head == cmdResult.head && layout[u].SectorNumber == cmdResult.sector && layout[u].SizeCode == cmdResult.size) { double spinTime = 0; for (int p = u + 1; p < ilayoutCnt; p++) { spinTime += layout[p].TimeMs; } if (spinTime > 250 || spinTime < 150) { continue; } firstIndex = u + 1; goto success; } } if (scanFormatTotalTime.ElapsedMs > 1000) { Log.Trace?.Out($"Не удалось найти цикл в последовательности секторов из-за нестабильного чтения. Сканироваие прервано по таймауту."); return(false); } } } return(false); success: result.AssignLayout(scanFormatBuffer, track, cmdResult.sector, firstIndex); Log.Trace?.Out($"Время сканирования трека {track}: {GP.ToString(scanFormatTotalTime.ElapsedMs, 2)}"); return(true); }
/// <summary> /// Объединение форматов двух результатов сканирования одного трека. У треков main и add будут изменены тайминги при работе этой функции! /// </summary> /// <param name="track"></param> /// <param name="main">Основной трек.</param> /// <param name="add">Добавляемый трек.</param> /// <param name="result"></param> /// <returns>true - объединение успешно, false - объединение не удалось по какой-то причине (невозможность синхронизации, несовместимость треков).</returns> public bool CombineFormats(int track, TrackFormat main, TrackFormat add, TrackFormat result) { if (add.Layout.Cnt == 0) { result.Assign(main); if (result.FormatName == TrackFormatName.Unscanned) { result.FormatName = TrackFormatName.NoHeaders; } return(true); } if (main.Layout.Cnt == 0 && add.Layout.Cnt > 0) { result.AssignLayout(add, track); return(true); } int indexM = 0; int indexA = 0; for (int i = 0; i < main.Layout.Cnt; i++) { for (int j = 0; j < add.Layout.Cnt; j++) { if (main.Layout.Data[i].Parameters == add.Layout.Data[j].Parameters) { indexM = i; indexA = j; goto outside; } } } return(false); outside: int iM = indexM; int iA = indexA; int iR = 0; scanFormatBuffer.Layout.EnsureCapacity(main.Layout.Cnt + add.Layout.Cnt); scanFormatBuffer.Layout.Cnt = 0; int totalPassedInMain = 0; double timeM = main.Layout.Data[(iM + 1) % main.Layout.Cnt].TimeMs; double timeA = add.Layout.Data[(iA + 1) % add.Layout.Cnt].TimeMs; while (totalPassedInMain < main.Layout.Cnt) { int iM1 = (iM + 1) % main.Layout.Cnt; int iA1 = (iA + 1) % add.Layout.Cnt; if (main.Layout.Data[iM1].TimeCalculated) { if (main.Layout.Data[iM1].Parameters == add.Layout.Data[iA1].Parameters) { iM = iM1; totalPassedInMain++; iA = iA1; scanFormatBuffer.Layout.Data[iR] = main.Layout.Data[iM]; scanFormatBuffer.Layout.Data[iR].TimeMs = add.Layout.Data[iA].TimeMs; scanFormatBuffer.Layout.Data[iR].TimeCalculated = false; iR++; } else { return(false); //totalPassedInMain++; //for (int t = 0, r = iM1, tlast = main.Layout.Cnt - totalPassedInMain; t < tlast; t++, totalPassedInMain++, r = r % main.Layout.Cnt) //{ // for (int y = 0; y < add.Layout.Cnt; y++) // { // if (main.Layout.Data[r].Parameters == add.Layout.Data[y].Parameters) // { // iM = r; // iA = y; // goto outOfLoop; // } // } //} //outOfLoop:; } } else if (Math.Abs(timeM - timeA) / timeM < 0.1) { iM = iM1; totalPassedInMain++; iA = iA1; if (main.Layout.Data[iM].Parameters != add.Layout.Data[iA].Parameters) { Log.Info?.Out($"Несовпадение параметров секторов при совмещении форматов. Основной сектор: {main[iM]} | Добавляемый сектор: {add[iA]}"); return(false); } scanFormatBuffer.Layout.Data[iR] = main.Layout.Data[iM]; iR++; timeM = main.Layout.Data[(iM + 1) % main.Layout.Cnt].TimeMs; timeA = add.Layout.Data[(iA + 1) % add.Layout.Cnt].TimeMs; } else if (timeA < timeM) { timeM -= timeA; main.Layout.Data[iM1].TimeMs -= timeA; iA = iA1; scanFormatBuffer.Layout.Data[iR] = add.Layout.Data[iA]; // Проверки ниже закомментированы, т.к. стало понятно что заголовки могут быть без данных и быть как следствие с более высокой плотностью. // Проверка выхода конца добавляемого сектора дальше начала следующего сектора в массиве main. if (timeM < (add.Layout.Data[iA].SizeBytes + TrackFormat.MinSectorHeaderSize) / add.BytesPerMs) { return(false); } // Проверка выхода начала добавляемого сектора влево от конца предыдущего сектора в массиве main. if (timeA < (main.Layout.Data[iM].SizeBytes + TrackFormat.MinSectorHeaderSize) / main.BytesPerMs) { return(false); } iR++; timeA = add.Layout.Data[(iA + 1) % add.Layout.Cnt].TimeMs; } else { timeA -= timeM; add.Layout.Data[iA1].TimeMs -= timeM; iM = iM1; totalPassedInMain++; scanFormatBuffer.Layout.Data[iR] = main.Layout.Data[iM]; iR++; timeM = main.Layout.Data[(iM + 1) % main.Layout.Cnt].TimeMs; } } scanFormatBuffer.Layout.Cnt = iR; result.AssignLayout(scanFormatBuffer, track); return(true); }