diff --git a/.idea/.idea.Aaru/.idea/contentModel.xml b/.idea/.idea.Aaru/.idea/contentModel.xml
index 1bc147c12..9a7a69f85 100644
--- a/.idea/.idea.Aaru/.idea/contentModel.xml
+++ b/.idea/.idea.Aaru/.idea/contentModel.xml
@@ -1,28 +1,27 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -71,13 +70,6 @@
-
-
-
-
-
-
-
@@ -88,12 +80,18 @@
+
+
+
+
+
+
@@ -102,14 +100,6 @@
-
-
-
-
-
-
-
-
@@ -117,11 +107,19 @@
+
+
+
+
+
+
+
+
@@ -180,6 +178,13 @@
+
+
+
+
+
+
+
@@ -200,19 +205,10 @@
-
-
-
-
-
-
-
-
-
@@ -221,6 +217,7 @@
+
@@ -238,6 +235,7 @@
+
@@ -258,8 +256,8 @@
-
+
@@ -269,11 +267,11 @@
-
+
-
+
@@ -287,17 +285,17 @@
-
-
+
+
-
+
@@ -317,12 +315,19 @@
-
+
+
+
+
+
+
+
+
@@ -339,68 +344,61 @@
-
-
-
-
-
-
-
-
+
-
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
@@ -425,7 +423,6 @@
-
@@ -437,11 +434,12 @@
+
-
+
@@ -463,9 +461,9 @@
+
-
@@ -495,6 +493,13 @@
+
+
+
+
+
+
+
@@ -558,11 +563,11 @@
+
-
@@ -582,17 +587,10 @@
-
-
-
-
-
-
-
-
+
@@ -649,6 +647,13 @@
+
+
+
+
+
+
+
@@ -661,28 +666,17 @@
-
+
-
-
-
-
-
-
-
-
+
-
-
-
-
@@ -690,13 +684,16 @@
+
+
+
-
-
+
+
@@ -732,11 +729,13 @@
+
-
+
+
@@ -744,11 +743,14 @@
-
+
+
+
+
@@ -788,8 +790,8 @@
-
+
@@ -798,8 +800,8 @@
-
+
@@ -808,8 +810,8 @@
-
+
@@ -836,8 +838,15 @@
-
+
+
+
+
+
+
+
+
@@ -855,11 +864,11 @@
-
+
@@ -882,34 +891,17 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
@@ -917,12 +909,18 @@
+
+
+
+
+
+
@@ -1016,6 +1014,12 @@
+
+
+
+
+
+
@@ -1040,12 +1044,6 @@
-
-
-
-
-
-
@@ -1128,6 +1126,8 @@
+
+
@@ -1143,6 +1143,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1155,18 +1167,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1175,14 +1175,14 @@
-
-
+
+
@@ -1205,12 +1205,10 @@
-
-
-
+
@@ -1221,15 +1219,13 @@
-
-
-
+
@@ -1252,6 +1248,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -1309,6 +1315,7 @@
+
@@ -1345,6 +1352,8 @@
+
+
@@ -1356,17 +1365,6 @@
-
-
-
-
-
-
-
-
-
-
-
@@ -1375,6 +1373,7 @@
+
@@ -1382,10 +1381,6 @@
-
-
-
-
@@ -1393,6 +1388,9 @@
+
+
+
@@ -1475,16 +1473,7 @@
-
-
-
-
-
-
-
-
-
-
+
@@ -1506,6 +1495,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -1539,16 +1538,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -1586,6 +1575,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -1676,8 +1675,8 @@
-
+
@@ -1711,16 +1710,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -1732,6 +1721,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1914,16 +1920,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -1934,6 +1930,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -1952,14 +1958,6 @@
-
-
-
-
-
-
-
-
@@ -1967,6 +1965,7 @@
+
@@ -1974,6 +1973,13 @@
+
+
+
+
+
+
+
@@ -1983,20 +1989,11 @@
-
-
-
-
-
-
-
-
-
+
-
@@ -2005,19 +2002,13 @@
+
-
-
-
-
-
-
-
-
-
-
+
+
+
@@ -2051,8 +2042,8 @@
-
+
@@ -2060,7 +2051,7 @@
-
+
@@ -2083,7 +2074,14 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Aaru.Gui/Forms/frmMediaScan.xeto b/Aaru.Gui/Forms/frmMediaScan.xeto
deleted file mode 100644
index e9367bd06..000000000
--- a/Aaru.Gui/Forms/frmMediaScan.xeto
+++ /dev/null
@@ -1,94 +0,0 @@
-
-
\ No newline at end of file
diff --git a/Aaru.Gui/Forms/frmMediaScan.xeto.cs b/Aaru.Gui/Forms/frmMediaScan.xeto.cs
deleted file mode 100644
index ddeca9c99..000000000
--- a/Aaru.Gui/Forms/frmMediaScan.xeto.cs
+++ /dev/null
@@ -1,425 +0,0 @@
-// /***************************************************************************
-// Aaru Data Preservation Suite
-// ----------------------------------------------------------------------------
-//
-// Filename : frmMediaScan.xeto.cs
-// Author(s) : Natalia Portillo
-//
-// Component : Media surface scan window.
-//
-// --[ Description ] ----------------------------------------------------------
-//
-// Implements media scan GUI window.
-//
-// --[ License ] --------------------------------------------------------------
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General public License as
-// published by the Free Software Foundation, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General public License for more details.
-//
-// You should have received a copy of the GNU General public License
-// along with this program. If not, see .
-//
-// ----------------------------------------------------------------------------
-// Copyright © 2011-2020 Natalia Portillo
-// ****************************************************************************/
-
-using System;
-using System.Threading;
-using Aaru.Core;
-using Aaru.Core.Devices.Scanning;
-using Aaru.Core.Media.Info;
-using Aaru.Devices;
-using Aaru.Gui.Controls;
-using Eto.Drawing;
-using Eto.Forms;
-using Eto.Serialization.Xaml;
-using DeviceInfo = Aaru.Core.Devices.Info.DeviceInfo;
-
-namespace Aaru.Gui.Forms
-{
- public class frmMediaScan : Form
- {
- static readonly Color LightGreen = Color.FromRgb(0x00FF00);
- static readonly Color Green = Color.FromRgb(0x006400);
- static readonly Color DarkGreen = Color.FromRgb(0x003200);
- static readonly Color Yellow = Color.FromRgb(0xFFA500);
- static readonly Color Orange = Color.FromRgb(0xFF4500);
- static readonly Color Red = Color.FromRgb(0x800000);
- static Color LightRed = Color.FromRgb(0xFF0000);
- ulong blocksToRead;
- string devicePath;
- ScanResults localResults;
- MediaScan scanner;
-
- public frmMediaScan(string devicePath, DeviceInfo deviceInfo, ScsiInfo scsiInfo = null)
- {
- XamlReader.Load(this);
-
- this.devicePath = devicePath;
- btnStop.Visible = false;
-
- lineChart.AbsoluteMargins = true;
- lineChart.MarginX = 5;
- lineChart.MarginY = 5;
- lineChart.DrawAxes = true;
- lineChart.AxesColor = Colors.Black;
- lineChart.ColorX = Colors.Gray;
- lineChart.ColorY = Colors.Gray;
- lineChart.BackgroundColor = Color.FromRgb(0x2974c1);
- lineChart.LineColor = Colors.Yellow;
- }
-
- void OnBtnCancelClick(object sender, EventArgs e) => Close();
-
- void OnBtnStopClick(object sender, EventArgs e) => scanner.Abort();
-
- void OnBtnScanClick(object sender, EventArgs e)
- {
- btnStop.Visible = true;
- btnScan.Visible = false;
- btnCancel.Visible = false;
- stkProgress.Visible = true;
- tabResults.Visible = true;
- new Thread(DoWork).Start();
- }
-
- // TODO: Allow to save MHDD and ImgBurn log files
- void DoWork()
- {
- if(devicePath.Length == 2 &&
- devicePath[1] == ':' &&
- devicePath[0] != '/' &&
- char.IsLetter(devicePath[0]))
- devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
-
- var dev = new Device(devicePath);
-
- if(dev.IsRemote)
- Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem,
- dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture);
-
- if(dev.Error)
- {
- Eto.Forms.MessageBox.Show($"Error {dev.LastError} opening device.", MessageBoxType.Error);
- btnStop.Visible = false;
- btnScan.Visible = true;
- btnCancel.Visible = true;
- stkProgress.Visible = false;
-
- return;
- }
-
- Statistics.AddDevice(dev);
-
- localResults = new ScanResults();
- scanner = new MediaScan(null, null, devicePath, dev);
- scanner.ScanTime += OnScanTime;
- scanner.ScanUnreadable += OnScanUnreadable;
- scanner.UpdateStatus += UpdateStatus;
- scanner.StoppingErrorMessage += StoppingErrorMessage;
- scanner.PulseProgress += PulseProgress;
- scanner.InitProgress += InitProgress;
- scanner.UpdateProgress += UpdateProgress;
- scanner.EndProgress += EndProgress;
- scanner.InitBlockMap += InitBlockMap;
- scanner.ScanSpeed += ScanSpeed;
-
- ScanResults results = scanner.Scan();
-
- Application.Instance.Invoke(() =>
- {
- lblTotalTime.Text = lblTotalTime.Text =
- $"Took a total of {results.TotalTime} seconds ({results.ProcessingTime} processing commands).";
-
- lblAvgSpeed.Text = $"Average speed: {results.AvgSpeed:F3} MiB/sec.";
- lblMaxSpeed.Text = $"Fastest speed burst: {results.MaxSpeed:F3} MiB/sec.";
- lblMinSpeed.Text = $"Slowest speed burst: {results.MinSpeed:F3} MiB/sec.";
- lblA.Text = $"{results.A} sectors took less than 3 ms.";
- lblB.Text = $"{results.B} sectors took less than 10 ms but more than 3 ms.";
- lblC.Text = $"{results.C} sectors took less than 50 ms but more than 10 ms.";
- lblD.Text = $"{results.D} sectors took less than 150 ms but more than 50 ms.";
- lblE.Text = $"{results.E} sectors took less than 500 ms but more than 150 ms.";
- lblF.Text = $"{results.F} sectors took more than 500 ms.";
- lblUnreadableSectors.Text = $"{results.UnreadableSectors.Count} sectors could not be read.";
- });
-
- // TODO: Show list of unreadable sectors
- /*
- if(results.UnreadableSectors.Count > 0)
- foreach(ulong bad in results.UnreadableSectors)
- string.Format("Sector {0} could not be read", bad);
-*/
-
- // TODO: Show results
- /*
- #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
- if(results.SeekTotal != 0 || results.SeekMin != double.MaxValue || results.SeekMax != double.MinValue)
- #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
- string.Format("Testing {0} seeks, longest seek took {1:F3} ms, fastest one took {2:F3} ms. ({3:F3} ms average)",
- results.SeekTimes, results.SeekMax, results.SeekMin, results.SeekTotal / 1000);
- */
-
- Statistics.AddCommand("media-scan");
-
- dev.Close();
- WorkFinished();
- }
-
- void ScanSpeed(ulong sector, double currentspeed) => Application.Instance.Invoke(() =>
- {
- if(currentspeed > lineChart.MaxY)
- lineChart.MaxY = (float)(currentspeed + (currentspeed / 10));
-
- lineChart.Values.Add(new PointF(sector, (float)currentspeed));
- });
-
- void InitBlockMap(ulong blocks, ulong blocksize, ulong blockstoread, ushort currentProfile) =>
- Application.Instance.Invoke(() =>
- {
- blockMap.Sectors = blocks;
- blockMap.SectorsToRead = (uint)blockstoread;
- blocksToRead = blockstoread;
- lineChart.MinX = 0;
- lineChart.MinY = 0;
-
- switch(currentProfile)
- {
- case 0x0005: // CD and DDCD
- case 0x0008:
- case 0x0009:
- case 0x000A:
- case 0x0020:
- case 0x0021:
- case 0x0022:
- if(blocks <= 360000)
- lineChart.MaxX = 360000;
- else if(blocks <= 405000)
- lineChart.MaxX = 405000;
- else if(blocks <= 445500)
- lineChart.MaxX = 445500;
- else
- lineChart.MaxX = blocks;
-
- lineChart.StepsX = lineChart.MaxX / 10f;
- lineChart.StepsY = 150 * 4;
- lineChart.MaxY = lineChart.StepsY * 12.5f;
-
- break;
- case 0x0010: // DVD SL
- case 0x0011:
- case 0x0012:
- case 0x0013:
- case 0x0014:
- case 0x0018:
- case 0x001A:
- case 0x001B:
- lineChart.MaxX = 2298496;
- lineChart.StepsX = lineChart.MaxX / 10f;
- lineChart.StepsY = 1352.5f;
- lineChart.MaxY = lineChart.StepsY * 26;
-
- break;
- case 0x0015: // DVD DL
- case 0x0016:
- case 0x0017:
- case 0x002A:
- case 0x002B:
- lineChart.MaxX = 4173824;
- lineChart.StepsX = lineChart.MaxX / 10f;
- lineChart.StepsY = 1352.5f;
- lineChart.MaxY = lineChart.StepsY * 26;
-
- break;
- case 0x0041:
- case 0x0042:
- case 0x0043:
- case 0x0040: // BD
- if(blocks <= 12219392)
- lineChart.MaxX = 12219392;
- else if(blocks <= 24438784)
- lineChart.MaxX = 24438784;
- else if(blocks <= 48878592)
- lineChart.MaxX = 48878592;
- else if(blocks <= 62500864)
- lineChart.MaxX = 62500864;
- else
- lineChart.MaxX = blocks;
-
- lineChart.StepsX = lineChart.MaxX / 10f;
- lineChart.StepsY = 4394.5f;
- lineChart.MaxY = lineChart.StepsY * 18;
-
- break;
- case 0x0050: // HD DVD
- case 0x0051:
- case 0x0052:
- case 0x0053:
- case 0x0058:
- case 0x005A:
- if(blocks <= 7361599)
- lineChart.MaxX = 7361599;
- else if(blocks <= 16305407)
- lineChart.MaxX = 16305407;
- else
- lineChart.MaxX = blocks;
-
- lineChart.StepsX = lineChart.MaxX / 10f;
- lineChart.StepsY = 4394.5f;
- lineChart.MaxY = lineChart.StepsY * 8;
-
- break;
- default:
- lineChart.MaxX = blocks;
- lineChart.StepsX = lineChart.MaxX / 10f;
- lineChart.StepsY = 625f;
- lineChart.MaxY = lineChart.StepsY;
-
- break;
- }
- });
-
- void WorkFinished() => Application.Instance.Invoke(() =>
- {
- btnStop.Visible = false;
- btnScan.Visible = true;
- btnCancel.Visible = true;
- stkProgress.Visible = false;
- lblTotalTime.Visible = true;
- lblAvgSpeed.Visible = true;
- lblMaxSpeed.Visible = true;
- lblMinSpeed.Visible = true;
- });
-
- void EndProgress() => Application.Instance.Invoke(() =>
- {
- stkProgress1.Visible = false;
- });
-
- void UpdateProgress(string text, long current, long maximum) => Application.Instance.Invoke(() =>
- {
- lblProgress.Text = text;
- prgProgress.Indeterminate = false;
- prgProgress.MinValue = 0;
-
- if(maximum > int.MaxValue)
- {
- prgProgress.MaxValue = (int)(maximum / int.MaxValue);
- prgProgress.Value = (int)(current / int.MaxValue);
- }
- else
- {
- prgProgress.MaxValue = (int)maximum;
- prgProgress.Value = (int)current;
- }
- });
-
- void InitProgress() => Application.Instance.Invoke(() =>
- {
- stkProgress1.Visible = true;
- });
-
- void PulseProgress(string text) => Application.Instance.Invoke(() =>
- {
- lblProgress.Text = text;
- prgProgress.Indeterminate = true;
- });
-
- void StoppingErrorMessage(string text) => Application.Instance.Invoke(() =>
- {
- lblProgress.Text = text;
- Eto.Forms.MessageBox.Show(text, MessageBoxType.Error);
- WorkFinished();
- });
-
- void UpdateStatus(string text) => Application.Instance.Invoke(() =>
- {
- lblProgress.Text = text;
- });
-
- void OnScanUnreadable(ulong sector) => Application.Instance.Invoke(() =>
- {
- localResults.Errored += blocksToRead;
- lblUnreadableSectors.Text = $"{localResults.Errored} sectors could not be read.";
- blockMap.ColoredSectors.Add(new ColoredBlock(sector, LightGreen));
- });
-
- void OnScanTime(ulong sector, double duration) => Application.Instance.Invoke(() =>
- {
- if(duration < 3)
- {
- localResults.A += blocksToRead;
- blockMap.ColoredSectors.Add(new ColoredBlock(sector, LightGreen));
- }
- else if(duration >= 3 &&
- duration < 10)
- {
- localResults.B += blocksToRead;
- blockMap.ColoredSectors.Add(new ColoredBlock(sector, Green));
- }
- else if(duration >= 10 &&
- duration < 50)
- {
- localResults.C += blocksToRead;
- blockMap.ColoredSectors.Add(new ColoredBlock(sector, DarkGreen));
- }
- else if(duration >= 50 &&
- duration < 150)
- {
- localResults.D += blocksToRead;
- blockMap.ColoredSectors.Add(new ColoredBlock(sector, Yellow));
- }
- else if(duration >= 150 &&
- duration < 500)
- {
- localResults.E += blocksToRead;
- blockMap.ColoredSectors.Add(new ColoredBlock(sector, Orange));
- }
- else if(duration >= 500)
- {
- localResults.F += blocksToRead;
- blockMap.ColoredSectors.Add(new ColoredBlock(sector, Red));
- }
-
- lblA.Text = $"{localResults.A} sectors took less than 3 ms.";
- lblB.Text = $"{localResults.B} sectors took less than 10 ms but more than 3 ms.";
- lblC.Text = $"{localResults.C} sectors took less than 50 ms but more than 10 ms.";
- lblD.Text = $"{localResults.D} sectors took less than 150 ms but more than 50 ms.";
- lblE.Text = $"{localResults.E} sectors took less than 500 ms but more than 150 ms.";
- lblF.Text = $"{localResults.F} sectors took more than 500 ms.";
- });
-
- #region XAML IDs
- Label lblTotalTime;
- Label lblAvgSpeed;
- Label lblMaxSpeed;
- Label lblMinSpeed;
- Label lblA;
- Label lblB;
- Label lblC;
- Label lblD;
- Label lblE;
- Label lblF;
- Label lblUnreadableSectors;
- Button btnCancel;
- Button btnStop;
- Button btnScan;
- StackLayout stkProgress;
- StackLayout stkProgress1;
- Label lblProgress;
- ProgressBar prgProgress;
- StackLayout stkProgress2;
- Label lblProgress2;
- ProgressBar prgProgress2;
- TabControl tabResults;
- BlockMap blockMap;
- LineChart lineChart;
- #endregion
- }
-}
\ No newline at end of file
diff --git a/Aaru.Gui/ViewModels/MediaInfoViewModel.cs b/Aaru.Gui/ViewModels/MediaInfoViewModel.cs
index 3eef867dc..c01a9dc00 100644
--- a/Aaru.Gui/ViewModels/MediaInfoViewModel.cs
+++ b/Aaru.Gui/ViewModels/MediaInfoViewModel.cs
@@ -5,10 +5,13 @@ using System.Reactive;
using System.Text;
using Aaru.Core.Media.Info;
using Aaru.Gui.Tabs;
+using Aaru.Gui.Views;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
+using MessageBox.Avalonia;
+using MessageBox.Avalonia.Enums;
using ReactiveUI;
namespace Aaru.Gui.ViewModels
@@ -354,25 +357,36 @@ namespace Aaru.Gui.ViewModels
*/
}
- void ExecuteScanCommand()
+ async void ExecuteScanCommand()
{
- /* TODO: frmMediaScan
- if(scsiInfo.MediaType == MediaType.GDR ||
- scsiInfo.MediaType == MediaType.GDROM)
+ switch(_scsiInfo.MediaType)
{
- Eto.Forms.MessageBox.Show("GD-ROM scan support is not yet implemented.", MessageBoxType.Error);
+ // TODO: GD-ROM
+ case CommonTypes.MediaType.GDR:
+ case CommonTypes.MediaType.GDROM:
+ await MessageBoxManager.
+ GetMessageBoxStandardWindow("Error", "GD-ROM scan support is not yet implemented.",
+ ButtonEnum.Ok, Icon.Error).ShowDialog(_view);
- return;
+ return;
+
+ // TODO: Xbox
+ case CommonTypes.MediaType.XGD:
+ case CommonTypes.MediaType.XGD2:
+ case CommonTypes.MediaType.XGD3:
+ await MessageBoxManager.
+ GetMessageBoxStandardWindow("Error", "Scanning Xbox discs is not yet supported.",
+ ButtonEnum.Ok, Icon.Error).ShowDialog(_view);
+
+ return;
}
- if(scsiInfo.MediaType == MediaType.XGD ||
- scsiInfo.MediaType == MediaType.XGD2 ||
- scsiInfo.MediaType == MediaType.XGD3)
- Eto.Forms.MessageBox.Show("Scanning Xbox discs is not yet supported.", MessageBoxType.Error);
+ var mediaScanWindow = new MediaScanWindow();
- var scanForm = new frmMediaScan(devicePath, scsiInfo.DeviceInfo, scsiInfo);
- scanForm.Show();
- */
+ mediaScanWindow.DataContext =
+ new MediaScanViewModel(_devicePath, _scsiInfo.DeviceInfo, mediaScanWindow, _scsiInfo);
+
+ mediaScanWindow.Show();
}
}
}
\ No newline at end of file
diff --git a/Aaru.Gui/ViewModels/MediaScanViewModel.cs b/Aaru.Gui/ViewModels/MediaScanViewModel.cs
new file mode 100644
index 000000000..fc81a9cd8
--- /dev/null
+++ b/Aaru.Gui/ViewModels/MediaScanViewModel.cs
@@ -0,0 +1,553 @@
+using System.Reactive;
+using System.Threading;
+using Aaru.Core;
+using Aaru.Core.Devices.Scanning;
+using Aaru.Core.Media.Info;
+using Aaru.Devices;
+using Avalonia.Controls;
+using Avalonia.Threading;
+using MessageBox.Avalonia;
+using MessageBox.Avalonia.Enums;
+using ReactiveUI;
+using DeviceInfo = Aaru.Core.Devices.Info.DeviceInfo;
+
+namespace Aaru.Gui.ViewModels
+{
+ public class MediaScanViewModel : ViewModelBase
+ {
+ readonly Window _view;
+ string _a;
+ string _avgSpeed;
+ string _b;
+ ulong _blocksToRead;
+ string _c;
+ bool _closeVisible;
+ string _d;
+ string _devicePath;
+ string _e;
+ string _f;
+ ScanResults _localResults;
+ string _maxSpeed;
+ string _minSpeed;
+ bool _progress1Visible;
+ string _progress2Indeterminate;
+ string _progress2MaxValue;
+ string _progress2Text;
+ string _progress2Value;
+ string _progress2Visible;
+ bool _progressIndeterminate;
+ double _progressMaxValue;
+ string _progressText;
+ double _progressValue;
+ bool _progressVisible;
+ bool _resultsVisible;
+ MediaScan _scanner;
+ bool _startVisible;
+ string _stopEnabled;
+ bool _stopVisible;
+ string _totalTime;
+ string _unreadableSectors;
+ /*
+ static readonly Color LightGreen = Color.FromRgb(0x00FF00);
+ static readonly Color Green = Color.FromRgb(0x006400);
+ static readonly Color DarkGreen = Color.FromRgb(0x003200);
+ static readonly Color Yellow = Color.FromRgb(0xFFA500);
+ static readonly Color Orange = Color.FromRgb(0xFF4500);
+ static readonly Color Red = Color.FromRgb(0x800000);
+ static Color LightRed = Color.FromRgb(0xFF0000);
+ */
+
+ public MediaScanViewModel(string devicePath, DeviceInfo deviceInfo, Window view, ScsiInfo scsiInfo = null)
+ {
+ _devicePath = devicePath;
+ _view = view;
+ StopVisible = false;
+ StartCommand = ReactiveCommand.Create(ExecuteStartCommand);
+ CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
+ StopCommand = ReactiveCommand.Create(ExecuteStopCommand);
+ StartVisible = true;
+ CloseVisible = true;
+
+ /*
+ lineChart.AbsoluteMargins = true;
+ lineChart.MarginX = 5;
+ lineChart.MarginY = 5;
+ lineChart.DrawAxes = true;
+ lineChart.AxesColor = Colors.Black;
+ lineChart.ColorX = Colors.Gray;
+ lineChart.ColorY = Colors.Gray;
+ lineChart.BackgroundColor = Color.FromRgb(0x2974c1);
+ lineChart.LineColor = Colors.Yellow;
+ */
+ }
+
+ public string A
+ {
+ get => _a;
+ set => this.RaiseAndSetIfChanged(ref _a, value);
+ }
+
+ public string B
+ {
+ get => _b;
+ set => this.RaiseAndSetIfChanged(ref _b, value);
+ }
+
+ public string C
+ {
+ get => _c;
+ set => this.RaiseAndSetIfChanged(ref _c, value);
+ }
+
+ public string D
+ {
+ get => _d;
+ set => this.RaiseAndSetIfChanged(ref _d, value);
+ }
+
+ public string E
+ {
+ get => _e;
+ set => this.RaiseAndSetIfChanged(ref _e, value);
+ }
+
+ public string F
+ {
+ get => _f;
+ set => this.RaiseAndSetIfChanged(ref _f, value);
+ }
+
+ public string UnreadableSectors
+ {
+ get => _unreadableSectors;
+ set => this.RaiseAndSetIfChanged(ref _unreadableSectors, value);
+ }
+
+ public string TotalTime
+ {
+ get => _totalTime;
+ set => this.RaiseAndSetIfChanged(ref _totalTime, value);
+ }
+
+ public string AvgSpeed
+ {
+ get => _avgSpeed;
+ set => this.RaiseAndSetIfChanged(ref _avgSpeed, value);
+ }
+
+ public string MaxSpeed
+ {
+ get => _maxSpeed;
+ set => this.RaiseAndSetIfChanged(ref _maxSpeed, value);
+ }
+
+ public string MinSpeed
+ {
+ get => _minSpeed;
+ set => this.RaiseAndSetIfChanged(ref _minSpeed, value);
+ }
+
+ public bool ProgressVisible
+ {
+ get => _progressVisible;
+ set => this.RaiseAndSetIfChanged(ref _progressVisible, value);
+ }
+
+ public bool Progress1Visible
+ {
+ get => _progress1Visible;
+ set => this.RaiseAndSetIfChanged(ref _progress1Visible, value);
+ }
+
+ public string ProgressText
+ {
+ get => _progressText;
+ set => this.RaiseAndSetIfChanged(ref _progressText, value);
+ }
+
+ public double ProgressMaxValue
+ {
+ get => _progressMaxValue;
+ set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value);
+ }
+
+ public bool ProgressIndeterminate
+ {
+ get => _progressIndeterminate;
+ set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value);
+ }
+
+ public double ProgressValue
+ {
+ get => _progressValue;
+ set => this.RaiseAndSetIfChanged(ref _progressValue, value);
+ }
+
+ public bool StartVisible
+ {
+ get => _startVisible;
+ set => this.RaiseAndSetIfChanged(ref _startVisible, value);
+ }
+
+ public bool CloseVisible
+ {
+ get => _closeVisible;
+ set => this.RaiseAndSetIfChanged(ref _closeVisible, value);
+ }
+
+ public bool StopVisible
+ {
+ get => _stopVisible;
+ set => this.RaiseAndSetIfChanged(ref _stopVisible, value);
+ }
+
+ public string StopEnabled
+ {
+ get => _stopEnabled;
+ set => this.RaiseAndSetIfChanged(ref _stopEnabled, value);
+ }
+
+ public bool ResultsVisible
+ {
+ get => _resultsVisible;
+ set => this.RaiseAndSetIfChanged(ref _resultsVisible, value);
+ }
+
+ public string Title { get; }
+
+ public ReactiveCommand StartCommand { get; }
+ public ReactiveCommand CloseCommand { get; }
+ public ReactiveCommand StopCommand { get; }
+
+ void ExecuteCloseCommand() => _view.Close();
+
+ internal void ExecuteStopCommand() => _scanner?.Abort();
+
+ void ExecuteStartCommand()
+ {
+ StopVisible = true;
+ StartVisible = false;
+ CloseVisible = false;
+ ProgressVisible = true;
+ ResultsVisible = true;
+ new Thread(DoWork).Start();
+ }
+
+ // TODO: Allow to save MHDD and ImgBurn log files
+ async void DoWork()
+ {
+ if(_devicePath.Length == 2 &&
+ _devicePath[1] == ':' &&
+ _devicePath[0] != '/' &&
+ char.IsLetter(_devicePath[0]))
+ _devicePath = "\\\\.\\" + char.ToUpper(_devicePath[0]) + ':';
+
+ var dev = new Device(_devicePath);
+
+ if(dev.IsRemote)
+ Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem,
+ dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture);
+
+ if(dev.Error)
+ {
+ await MessageBoxManager.
+ GetMessageBoxStandardWindow("Error", $"Error {dev.LastError} opening device.", ButtonEnum.Ok,
+ Icon.Error).ShowDialog(_view);
+
+ StopVisible = false;
+ StartVisible = true;
+ CloseVisible = true;
+ ProgressVisible = false;
+
+ return;
+ }
+
+ Statistics.AddDevice(dev);
+
+ _localResults = new ScanResults();
+ _scanner = new MediaScan(null, null, _devicePath, dev);
+ _scanner.ScanTime += OnScanTime;
+ _scanner.ScanUnreadable += OnScanUnreadable;
+ _scanner.UpdateStatus += UpdateStatus;
+ _scanner.StoppingErrorMessage += StoppingErrorMessage;
+ _scanner.PulseProgress += PulseProgress;
+ _scanner.InitProgress += InitProgress;
+ _scanner.UpdateProgress += UpdateProgress;
+ _scanner.EndProgress += EndProgress;
+ _scanner.InitBlockMap += InitBlockMap;
+ _scanner.ScanSpeed += ScanSpeed;
+
+ ScanResults results = _scanner.Scan();
+
+ await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ TotalTime =
+ $"Took a total of {results.TotalTime} seconds ({results.ProcessingTime} processing commands).";
+
+ AvgSpeed = $"Average speed: {results.AvgSpeed:F3} MiB/sec.";
+ MaxSpeed = $"Fastest speed burst: {results.MaxSpeed:F3} MiB/sec.";
+ MinSpeed = $"Slowest speed burst: {results.MinSpeed:F3} MiB/sec.";
+ A = $"{results.A} sectors took less than 3 ms.";
+ B = $"{results.B} sectors took less than 10 ms but more than 3 ms.";
+ C = $"{results.C} sectors took less than 50 ms but more than 10 ms.";
+ D = $"{results.D} sectors took less than 150 ms but more than 50 ms.";
+ E = $"{results.E} sectors took less than 500 ms but more than 150 ms.";
+ F = $"{results.F} sectors took more than 500 ms.";
+ UnreadableSectors = $"{results.UnreadableSectors.Count} sectors could not be read.";
+ });
+
+ // TODO: Show list of unreadable sectors
+ /*
+ if(results.UnreadableSectors.Count > 0)
+ foreach(ulong bad in results.UnreadableSectors)
+ string.Format("Sector {0} could not be read", bad);
+*/
+
+ // TODO: Show results
+ /*
+ #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
+ if(results.SeekTotal != 0 || results.SeekMin != double.MaxValue || results.SeekMax != double.MinValue)
+ #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
+ string.Format("Testing {0} seeks, longest seek took {1:F3} ms, fastest one took {2:F3} ms. ({3:F3} ms average)",
+ results.SeekTimes, results.SeekMax, results.SeekMin, results.SeekTotal / 1000);
+ */
+
+ Statistics.AddCommand("media-scan");
+
+ dev.Close();
+ WorkFinished();
+ }
+
+ async void ScanSpeed(ulong sector, double currentspeed) => await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ /* TODO: Chart
+ if(currentspeed > lineChart.MaxY)
+ lineChart.MaxY = (float)(currentspeed + (currentspeed / 10));
+
+ lineChart.Values.Add(new PointF(sector, (float)currentspeed));
+ */
+ });
+
+ async void InitBlockMap(ulong blocks, ulong blocksize, ulong blockstoread, ushort currentProfile) =>
+ await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ /* TODO: BlockMap
+ blockMap.Sectors = blocks;
+ blockMap.SectorsToRead = (uint)blockstoread;
+ blocksToRead = blockstoread;
+ lineChart.MinX = 0;
+ lineChart.MinY = 0;
+
+ switch(currentProfile)
+ {
+ case 0x0005: // CD and DDCD
+ case 0x0008:
+ case 0x0009:
+ case 0x000A:
+ case 0x0020:
+ case 0x0021:
+ case 0x0022:
+ if(blocks <= 360000)
+ lineChart.MaxX = 360000;
+ else if(blocks <= 405000)
+ lineChart.MaxX = 405000;
+ else if(blocks <= 445500)
+ lineChart.MaxX = 445500;
+ else
+ lineChart.MaxX = blocks;
+
+ lineChart.StepsX = lineChart.MaxX / 10f;
+ lineChart.StepsY = 150 * 4;
+ lineChart.MaxY = lineChart.StepsY * 12.5f;
+
+ break;
+ case 0x0010: // DVD SL
+ case 0x0011:
+ case 0x0012:
+ case 0x0013:
+ case 0x0014:
+ case 0x0018:
+ case 0x001A:
+ case 0x001B:
+ lineChart.MaxX = 2298496;
+ lineChart.StepsX = lineChart.MaxX / 10f;
+ lineChart.StepsY = 1352.5f;
+ lineChart.MaxY = lineChart.StepsY * 26;
+
+ break;
+ case 0x0015: // DVD DL
+ case 0x0016:
+ case 0x0017:
+ case 0x002A:
+ case 0x002B:
+ lineChart.MaxX = 4173824;
+ lineChart.StepsX = lineChart.MaxX / 10f;
+ lineChart.StepsY = 1352.5f;
+ lineChart.MaxY = lineChart.StepsY * 26;
+
+ break;
+ case 0x0041:
+ case 0x0042:
+ case 0x0043:
+ case 0x0040: // BD
+ if(blocks <= 12219392)
+ lineChart.MaxX = 12219392;
+ else if(blocks <= 24438784)
+ lineChart.MaxX = 24438784;
+ else if(blocks <= 48878592)
+ lineChart.MaxX = 48878592;
+ else if(blocks <= 62500864)
+ lineChart.MaxX = 62500864;
+ else
+ lineChart.MaxX = blocks;
+
+ lineChart.StepsX = lineChart.MaxX / 10f;
+ lineChart.StepsY = 4394.5f;
+ lineChart.MaxY = lineChart.StepsY * 18;
+
+ break;
+ case 0x0050: // HD DVD
+ case 0x0051:
+ case 0x0052:
+ case 0x0053:
+ case 0x0058:
+ case 0x005A:
+ if(blocks <= 7361599)
+ lineChart.MaxX = 7361599;
+ else if(blocks <= 16305407)
+ lineChart.MaxX = 16305407;
+ else
+ lineChart.MaxX = blocks;
+
+ lineChart.StepsX = lineChart.MaxX / 10f;
+ lineChart.StepsY = 4394.5f;
+ lineChart.MaxY = lineChart.StepsY * 8;
+
+ break;
+ default:
+ lineChart.MaxX = blocks;
+ lineChart.StepsX = lineChart.MaxX / 10f;
+ lineChart.StepsY = 625f;
+ lineChart.MaxY = lineChart.StepsY;
+
+ break;
+ }
+ */
+ });
+
+ async void WorkFinished() => await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ StopVisible = false;
+ StartVisible = true;
+ CloseVisible = true;
+ ProgressVisible = false;
+ });
+
+ async void EndProgress() => await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ Progress1Visible = false;
+ });
+
+ async void UpdateProgress(string text, long current, long maximum) =>
+ await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ ProgressText = text;
+ ProgressIndeterminate = false;
+
+ ProgressMaxValue = maximum;
+ ProgressValue = current;
+ });
+
+ async void InitProgress() => await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ Progress1Visible = true;
+ });
+
+ async void PulseProgress(string text) => await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ ProgressText = text;
+ ProgressIndeterminate = true;
+ });
+
+ async void StoppingErrorMessage(string text) => await Dispatcher.UIThread.InvokeAsync(action: async () =>
+ {
+ ProgressText = text;
+
+ await MessageBoxManager.GetMessageBoxStandardWindow("Error", $"{text}", ButtonEnum.Ok, Icon.Error).
+ ShowDialog(_view);
+
+ WorkFinished();
+ });
+
+ async void UpdateStatus(string text) => await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ ProgressText = text;
+ });
+
+ async void OnScanUnreadable(ulong sector) => await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ _localResults.Errored += _blocksToRead;
+ UnreadableSectors = $"{_localResults.Errored} sectors could not be read.";
+ /* TODO: Blockmap
+ blockMap.ColoredSectors.Add(new ColoredBlock(sector, LightGreen));
+ */
+ });
+
+ async void OnScanTime(ulong sector, double duration) => await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ if(duration < 3)
+ {
+ _localResults.A += _blocksToRead;
+ /* TODO: Blockmap
+ blockMap.ColoredSectors.Add(new ColoredBlock(sector, LightGreen));
+ */
+ }
+ else if(duration >= 3 &&
+ duration < 10)
+ {
+ _localResults.B += _blocksToRead;
+ /* TODO: Blockmap
+ blockMap.ColoredSectors.Add(new ColoredBlock(sector, Green));
+ */
+ }
+ else if(duration >= 10 &&
+ duration < 50)
+ {
+ _localResults.C += _blocksToRead;
+ /* TODO: Blockmap
+ blockMap.ColoredSectors.Add(new ColoredBlock(sector, DarkGreen));
+ */
+ }
+ else if(duration >= 50 &&
+ duration < 150)
+ {
+ _localResults.D += _blocksToRead;
+ /* TODO: Blockmap
+ blockMap.ColoredSectors.Add(new ColoredBlock(sector, Yellow));
+ */
+ }
+ else if(duration >= 150 &&
+ duration < 500)
+ {
+ _localResults.E += _blocksToRead;
+ /* TODO: Blockmap
+ blockMap.ColoredSectors.Add(new ColoredBlock(sector, Orange));
+ */
+ }
+ else if(duration >= 500)
+ {
+ _localResults.F += _blocksToRead;
+ /* TODO: Blockmap
+ blockMap.ColoredSectors.Add(new ColoredBlock(sector, Red));
+ */
+ }
+
+ A = $"{_localResults.A} sectors took less than 3 ms.";
+ B = $"{_localResults.B} sectors took less than 10 ms but more than 3 ms.";
+ C = $"{_localResults.C} sectors took less than 50 ms but more than 10 ms.";
+ D = $"{_localResults.D} sectors took less than 150 ms but more than 50 ms.";
+ E = $"{_localResults.E} sectors took less than 500 ms but more than 150 ms.";
+ F = $"{_localResults.F} sectors took more than 500 ms.";
+ });
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Gui/Views/MediaScanWindow.xaml b/Aaru.Gui/Views/MediaScanWindow.xaml
new file mode 100644
index 000000000..b1cc39d38
--- /dev/null
+++ b/Aaru.Gui/Views/MediaScanWindow.xaml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Aaru.Gui/Views/MediaScanWindow.xaml.cs b/Aaru.Gui/Views/MediaScanWindow.xaml.cs
new file mode 100644
index 000000000..c321b14c9
--- /dev/null
+++ b/Aaru.Gui/Views/MediaScanWindow.xaml.cs
@@ -0,0 +1,27 @@
+using System.ComponentModel;
+using Aaru.Gui.ViewModels;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Aaru.Gui.Views
+{
+ public class MediaScanWindow : Window
+ {
+ public MediaScanWindow()
+ {
+ InitializeComponent();
+ #if DEBUG
+ this.AttachDevTools();
+ #endif
+ }
+
+ void InitializeComponent() => AvaloniaXamlLoader.Load(this);
+
+ protected override void OnClosing(CancelEventArgs e)
+ {
+ (DataContext as MediaScanViewModel)?.ExecuteStopCommand();
+ base.OnClosing(e);
+ }
+ }
+}
\ No newline at end of file