Files
Aaru/Aaru.Gui/ViewModels/Windows/MhddLogViewModel.cs

211 lines
7.4 KiB
C#

// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : MhddLogViewModel.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : GUI view models.
//
// --[ Description ] ----------------------------------------------------------
//
// View model for MHDD log viewer.
//
// --[ 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 <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Aaru.Gui.Views.Windows;
using Aaru.Localization;
using CommunityToolkit.Mvvm.ComponentModel;
using JetBrains.Annotations;
using MsBox.Avalonia;
using MsBox.Avalonia.Enums;
using Sentry;
namespace Aaru.Gui.ViewModels.Windows;
public partial class MhddLogViewModel : ViewModelBase
{
readonly MhddLogView _window;
[ObservableProperty]
string _device;
[ObservableProperty]
string _filePath;
[ObservableProperty]
string _firmware;
[ObservableProperty]
string _mhddVersion;
[ObservableProperty]
string _scanBlockSize;
[ObservableProperty]
ObservableCollection<(ulong startingSector, double duration)> _sectorData;
[ObservableProperty]
string _sectorSize;
[ObservableProperty]
string _serialNumber;
[ObservableProperty]
string _totalSectors;
public MhddLogViewModel(MhddLogView window, [NotNull] string filePath)
{
_window = window;
FilePath = filePath;
_sectorData = [];
}
public void LoadData()
{
Stream stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
stream.Seek(0, SeekOrigin.Begin);
var buffer = new byte[4];
stream.ReadExactly(buffer, 0, 4);
var pointer = BitConverter.ToInt32(buffer, 0);
int d = stream.ReadByte();
int a = stream.ReadByte();
stream.ReadExactly(buffer, 0, 4);
string ver = Encoding.ASCII.GetString(buffer, 0, 4);
if(pointer > stream.Length || d != 0x0D || a != 0x0A || ver != "VER:")
{
stream.Close();
_ = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI.The_specified_file_is_not_a_correct_MHDD_log_file,
ButtonEnum.Ok,
Icon.Error)
.ShowWindowDialogAsync(_window);
_window.Close();
}
stream.Position = 4;
buffer = new byte[pointer - 4];
stream.ReadExactly(buffer, 0, buffer.Length);
string header = Encoding.ASCII.GetString(buffer, 0, buffer.Length);
try
{
// Parse VER field
Match versionMatch = VersionRegex().Match(header);
if(versionMatch.Success) MhddVersion = $"[green]{versionMatch.Groups[1].Value}[/]";
// Parse DEVICE field
Match deviceMatch = DeviceRegex().Match(header);
if(deviceMatch.Success) Device = $"[pink]{deviceMatch.Groups[1].Value.Trim()}[/]";
// Parse F/W field
Match firmwareMatch = FirmwareRegex().Match(header);
if(firmwareMatch.Success) Firmware = $"[rosybrown]{firmwareMatch.Groups[1].Value}[/]";
// Parse S/N field
Match serialMatch = SerialNumberRegex().Match(header);
if(serialMatch.Success) SerialNumber = $"[purple]{serialMatch.Groups[1].Value}[/]";
// Parse SECTORS field
Match sectorsMatch = SectorsRegex().Match(header);
if(sectorsMatch.Success)
TotalSectors = $"[teal]{ParseNumberWithSeparator(sectorsMatch.Groups[1].Value)}[/]";
// Parse SECTOR SIZE field
Match sectorSizeMatch = SectorSizeRegex().Match(header);
if(sectorSizeMatch.Success)
{
SectorSize = string.Format(UI._0_bytes_markup,
ParseNumberWithSeparator(sectorSizeMatch.Groups[1].Value));
}
// Parse SCAN BLOCK SIZE field
Match scanBlockMatch = ScanBlockSizeRegex().Match(header);
if(scanBlockMatch.Success)
{
ScanBlockSize = string.Format(UI._0_sectors_markup,
ParseNumberWithSeparator(scanBlockMatch.Groups[1].Value));
}
}
catch(Exception ex)
{
SentrySdk.CaptureException(ex);
_ = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
string.Format(UI.Error_parsing_MHDD_log_header_0, ex.Message),
ButtonEnum.Ok,
Icon.Error)
.ShowWindowDialogAsync(_window);
_window.Close();
}
stream.Position = pointer;
buffer = new byte[8];
SectorData.Clear();
while(stream.Position < stream.Length)
{
stream.ReadExactly(buffer, 0, 8);
var sector = BitConverter.ToUInt64(buffer, 0);
stream.ReadExactly(buffer, 0, 8);
double duration = BitConverter.ToUInt64(buffer, 0) / 1000.0;
SectorData.Add((sector, duration));
}
stream.Close();
}
/// <summary>
/// Parses a number string that may contain thousands separators (en-US culture).
/// </summary>
/// <param name="value">The number string (e.g., "243,587" or "2,448")</param>
/// <returns>The parsed number without separators</returns>
static ulong ParseNumberWithSeparator(string value) => ulong.Parse(value.Replace(",", ""));
[GeneratedRegex(@"VER:\s*(\S+)")]
private static partial Regex VersionRegex();
[GeneratedRegex(@"DEVICE:\s*(.+?)(?=\n|$)")]
private static partial Regex DeviceRegex();
[GeneratedRegex(@"F/W:\s*(\S+)")]
private static partial Regex FirmwareRegex();
[GeneratedRegex(@"S/N:\s*(\S+)")]
private static partial Regex SerialNumberRegex();
[GeneratedRegex(@"SECTORS:\s*([\d,]+)")]
private static partial Regex SectorsRegex();
[GeneratedRegex(@"SECTOR SIZE:\s*([\d,]+)\s*bytes")]
private static partial Regex SectorSizeRegex();
[GeneratedRegex(@"SCAN BLOCK SIZE:\s*([\d,]+)\s*sectors")]
private static partial Regex ScanBlockSizeRegex();
}