mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-07-08 18:16:24 +00:00
Migrate media dump window from Eto.Forms to Avalonia.
This commit is contained in:
492
.idea/.idea.Aaru/.idea/contentModel.xml
generated
492
.idea/.idea.Aaru/.idea/contentModel.xml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,111 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!--
|
||||
// /***************************************************************************
|
||||
// The Disc Image Chef
|
||||
// ============================================================================
|
||||
//
|
||||
// Filename : frmDump.xeto
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Media dump window.
|
||||
//
|
||||
// ==[ Description ] ==========================================================
|
||||
//
|
||||
// Defines the structure for the media dump 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ============================================================================
|
||||
// Copyright © 2011-2020 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
-->
|
||||
<Form xmlns="http://schema.picoe.ca/eto.forms" Title="Aaru" ClientSize="600, 450" Padding="10">
|
||||
<StackLayout Orientation="Vertical" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
|
||||
<StackLayout Orientation="Vertical" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
|
||||
ID="stkOptions">
|
||||
<Label Text="Output format"/>
|
||||
<ComboBox ID="cmbFormat" ReadOnly="True" SelectedIndexChanged="OnCmbFormatSelectedIndexChanged"/>
|
||||
<StackLayout Orientation="Horizontal" HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch">
|
||||
<StackLayoutItem Expand="True">
|
||||
<TextBox ID="txtDestination" ReadOnly="True"/>
|
||||
</StackLayoutItem>
|
||||
<Button ID="btnDestination" Text="Choose..." Click="OnBtnDestinationClick" Enabled="False"/>
|
||||
</StackLayout>
|
||||
<CheckBox ID="chkStopOnError" Text="Stop media dump on first error"/>
|
||||
<CheckBox ID="chkForce" Text="Continue dumping whatever happens"/>
|
||||
<StackLayout Orientation="Horizontal" HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch">
|
||||
<NumericStepper ID="stpRetries" MinValue="0"/>
|
||||
<Label Text="Retry passes"/>
|
||||
</StackLayout>
|
||||
<CheckBox ID="chkPersistent" Text="Try to recover partial or incorrect data"/>
|
||||
<CheckBox ID="chkResume" Text="Create/use resume mapfile" CheckedChanged="OnChkResumeCheckedChanged"/>
|
||||
<CheckBox ID="chkTrack1Pregap" Text="Try to read track 1 pregap"/>
|
||||
<StackLayout Orientation="Horizontal" HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch">
|
||||
<NumericStepper ID="stpSkipped" MinValue="1"/>
|
||||
<Label Text="Skipped sectors on error"/>
|
||||
</StackLayout>
|
||||
<CheckBox ID="chkSidecar" Text="Create CICM XML metadata sidecar"
|
||||
CheckedChanged="OnChkSidecarCheckedChanged"/>
|
||||
<CheckBox ID="chkTrim" Text="Trim errors from skipped sectors"/>
|
||||
<CheckBox ID="chkExistingMetadata" Text="Take metadata from existing CICM XML sidecar"
|
||||
CheckedChanged="OnChkExistingMetadataCheckedChanged"/>
|
||||
<Label ID="lblEncoding" Text="Encoding to use on metadata sidecar creation"/>
|
||||
<ComboBox ID="cmbEncoding" ReadOnly="True"/>
|
||||
<GroupBox ID="grpOptions" Text="Options" Visible="False"/>
|
||||
</StackLayout>
|
||||
<StackLayout Orientation="Vertical" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
|
||||
ID="stkProgress" Visible="False">
|
||||
<StackLayoutItem HorizontalAlignment="Left" Expand="True">
|
||||
<StackLayout Orientation="Horizontal">
|
||||
<Label ID="lblDestinationLabel">Writing image to:</Label>
|
||||
<Label ID="lblDestination"/>
|
||||
</StackLayout>
|
||||
</StackLayoutItem>
|
||||
<StackLayoutItem Expand="True">
|
||||
<GroupBox Text="Log">
|
||||
<TextArea ID="txtLog" ReadOnly="True"/>
|
||||
</GroupBox>
|
||||
</StackLayoutItem>
|
||||
<StackLayout Orientation="Vertical" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
|
||||
ID="stkProgress1" Visible="False">
|
||||
<StackLayoutItem HorizontalAlignment="Center" Expand="True">
|
||||
<Label ID="lblProgress"/>
|
||||
</StackLayoutItem>
|
||||
<StackLayoutItem HorizontalAlignment="Center" Expand="True">
|
||||
<ProgressBar ID="prgProgress"/>
|
||||
</StackLayoutItem>
|
||||
</StackLayout>
|
||||
<StackLayout Orientation="Vertical" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
|
||||
ID="stkProgress2" Visible="False">
|
||||
<StackLayoutItem HorizontalAlignment="Center" Expand="True">
|
||||
<Label ID="lblProgress2"/>
|
||||
</StackLayoutItem>
|
||||
<StackLayoutItem HorizontalAlignment="Center" Expand="True">
|
||||
<ProgressBar ID="prgProgress2"/>
|
||||
</StackLayoutItem>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<StackLayoutItem HorizontalAlignment="Right" Expand="True">
|
||||
<StackLayout Orientation="Horizontal" HorizontalContentAlignment="Right" VerticalContentAlignment="Bottom">
|
||||
<Button ID="btnStart" Text="Start" Click="OnBtnDumpClick"/>
|
||||
<Button ID="btnClose" Text="Close" Click="OnBtnCloseClick"/>
|
||||
<Button ID="btnStop" Text="Stop" Visible="False" Click="OnBtnStopClick"/>
|
||||
</StackLayout>
|
||||
</StackLayoutItem>
|
||||
</StackLayout>
|
||||
</Form>
|
||||
@@ -1,684 +0,0 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : frmDump.xeto.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Media dump window.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Implements the media dump 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2020 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Xml.Serialization;
|
||||
using Aaru.CommonTypes;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Metadata;
|
||||
using Aaru.Core;
|
||||
using Aaru.Core.Devices.Dumping;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Core.Media.Info;
|
||||
using Aaru.Devices;
|
||||
using Eto.Forms;
|
||||
using Eto.Serialization.Xaml;
|
||||
using Schemas;
|
||||
using DeviceInfo = Aaru.Core.Devices.Info.DeviceInfo;
|
||||
using MediaType = Aaru.CommonTypes.MediaType;
|
||||
|
||||
// ReSharper disable UnusedMember.Local
|
||||
|
||||
namespace Aaru.Gui.Forms
|
||||
{
|
||||
public class frmDump : Form
|
||||
{
|
||||
readonly string _devicePath;
|
||||
Device _dev;
|
||||
Dump _dumper;
|
||||
string _outputPrefix;
|
||||
Resume _resume;
|
||||
CICMMetadataType _sidecar;
|
||||
|
||||
public frmDump(string devicePath, DeviceInfo deviceInfo, ScsiInfo scsiInfo = null)
|
||||
{
|
||||
MediaType mediaType;
|
||||
XamlReader.Load(this);
|
||||
|
||||
// Defaults
|
||||
chkStopOnError.Checked = false;
|
||||
chkForce.Checked = false;
|
||||
chkPersistent.Checked = true;
|
||||
chkResume.Checked = true;
|
||||
chkTrack1Pregap.Checked = false;
|
||||
chkSidecar.Checked = true;
|
||||
chkTrim.Checked = true;
|
||||
chkExistingMetadata.Checked = false;
|
||||
stpRetries.Value = 5;
|
||||
stpSkipped.Value = 512;
|
||||
|
||||
if(scsiInfo != null)
|
||||
mediaType = scsiInfo.MediaType;
|
||||
else
|
||||
switch(deviceInfo.Type)
|
||||
{
|
||||
case DeviceType.SecureDigital:
|
||||
mediaType = MediaType.SecureDigital;
|
||||
|
||||
break;
|
||||
case DeviceType.MMC:
|
||||
mediaType = MediaType.MMC;
|
||||
|
||||
break;
|
||||
default:
|
||||
if(deviceInfo.IsPcmcia)
|
||||
mediaType = MediaType.PCCardTypeII;
|
||||
else if(deviceInfo.IsCompactFlash)
|
||||
mediaType = MediaType.CompactFlash;
|
||||
else
|
||||
mediaType = MediaType.GENERIC_HDD;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ObservableCollection<IWritableImage> lstPlugins = new ObservableCollection<IWritableImage>();
|
||||
PluginBase plugins = GetPluginBase.Instance;
|
||||
|
||||
foreach(IWritableImage plugin in
|
||||
plugins.WritableImages.Values.Where(p => p.SupportedMediaTypes.Contains(mediaType)))
|
||||
lstPlugins.Add(plugin);
|
||||
|
||||
cmbFormat.ItemTextBinding = Binding.Property((IWritableImage p) => p.Name);
|
||||
cmbFormat.ItemKeyBinding = Binding.Property((IWritableImage p) => p.Id.ToString());
|
||||
cmbFormat.DataStore = lstPlugins;
|
||||
|
||||
List<CommonEncodingInfo> encodings = Encoding.GetEncodings().Select(info => new CommonEncodingInfo
|
||||
{
|
||||
Name = info.Name, DisplayName = info.GetEncoding().EncodingName
|
||||
}).ToList();
|
||||
|
||||
encodings.AddRange(Claunia.Encoding.Encoding.GetEncodings().Select(info => new CommonEncodingInfo
|
||||
{
|
||||
Name = info.Name, DisplayName = info.DisplayName
|
||||
}));
|
||||
|
||||
ObservableCollection<CommonEncodingInfo> lstEncodings = new ObservableCollection<CommonEncodingInfo>();
|
||||
|
||||
foreach(CommonEncodingInfo info in encodings.OrderBy(t => t.DisplayName))
|
||||
lstEncodings.Add(info);
|
||||
|
||||
cmbEncoding.ItemTextBinding = Binding.Property((CommonEncodingInfo p) => p.DisplayName);
|
||||
cmbEncoding.ItemKeyBinding = Binding.Property((CommonEncodingInfo p) => p.Name);
|
||||
cmbEncoding.DataStore = lstEncodings;
|
||||
|
||||
switch(mediaType)
|
||||
{
|
||||
case MediaType.CD:
|
||||
case MediaType.CDDA:
|
||||
case MediaType.CDG:
|
||||
case MediaType.CDEG:
|
||||
case MediaType.CDI:
|
||||
case MediaType.CDROM:
|
||||
case MediaType.CDROMXA:
|
||||
case MediaType.CDPLUS:
|
||||
case MediaType.CDMO:
|
||||
case MediaType.CDR:
|
||||
case MediaType.CDRW:
|
||||
case MediaType.CDMRW:
|
||||
case MediaType.VCD:
|
||||
case MediaType.SVCD:
|
||||
case MediaType.PCD:
|
||||
case MediaType.DDCD:
|
||||
case MediaType.DDCDR:
|
||||
case MediaType.DDCDRW:
|
||||
case MediaType.DTSCD:
|
||||
case MediaType.CDMIDI:
|
||||
case MediaType.CDV:
|
||||
case MediaType.CDIREADY:
|
||||
case MediaType.FMTOWNS:
|
||||
case MediaType.PS1CD:
|
||||
case MediaType.PS2CD:
|
||||
case MediaType.MEGACD:
|
||||
case MediaType.SATURNCD:
|
||||
case MediaType.GDROM:
|
||||
case MediaType.GDR:
|
||||
case MediaType.MilCD:
|
||||
case MediaType.SuperCDROM2:
|
||||
case MediaType.JaguarCD:
|
||||
case MediaType.ThreeDO:
|
||||
case MediaType.PCFX:
|
||||
case MediaType.NeoGeoCD:
|
||||
case MediaType.CDTV:
|
||||
case MediaType.CD32:
|
||||
case MediaType.Playdia:
|
||||
case MediaType.Pippin:
|
||||
case MediaType.VideoNow:
|
||||
case MediaType.VideoNowColor:
|
||||
case MediaType.VideoNowXp:
|
||||
chkTrack1Pregap.Visible = true;
|
||||
|
||||
break;
|
||||
default:
|
||||
chkTrack1Pregap.Visible = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_devicePath = devicePath;
|
||||
}
|
||||
|
||||
void OnCmbFormatSelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
txtDestination.Text = "";
|
||||
|
||||
if(!(cmbFormat.SelectedValue is IWritableImage plugin))
|
||||
{
|
||||
grpOptions.Visible = false;
|
||||
btnDestination.Enabled = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
btnDestination.Enabled = true;
|
||||
|
||||
if(!plugin.SupportedOptions.Any())
|
||||
{
|
||||
grpOptions.Content = null;
|
||||
grpOptions.Visible = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
grpOptions.Visible = true;
|
||||
|
||||
var stkOptions = new StackLayout
|
||||
{
|
||||
Orientation = Orientation.Vertical
|
||||
};
|
||||
|
||||
foreach((string name, Type type, string description, object @default) option in plugin.SupportedOptions)
|
||||
switch(option.type.ToString())
|
||||
{
|
||||
case "System.Boolean":
|
||||
var optBoolean = new CheckBox();
|
||||
optBoolean.ID = "opt" + option.name;
|
||||
optBoolean.Text = option.description;
|
||||
optBoolean.Checked = (bool)option.@default;
|
||||
stkOptions.Items.Add(optBoolean);
|
||||
|
||||
break;
|
||||
case "System.SByte":
|
||||
case "System.Int16":
|
||||
case "System.Int32":
|
||||
case "System.Int64":
|
||||
var stkNumber = new StackLayout();
|
||||
stkNumber.Orientation = Orientation.Horizontal;
|
||||
var optNumber = new NumericStepper();
|
||||
optNumber.ID = "opt" + option.name;
|
||||
optNumber.Value = Convert.ToDouble(option.@default);
|
||||
stkNumber.Items.Add(optNumber);
|
||||
var lblNumber = new Label();
|
||||
lblNumber.Text = option.description;
|
||||
stkNumber.Items.Add(lblNumber);
|
||||
stkOptions.Items.Add(stkNumber);
|
||||
|
||||
break;
|
||||
case "System.Byte":
|
||||
case "System.UInt16":
|
||||
case "System.UInt32":
|
||||
case "System.UInt64":
|
||||
var stkUnsigned = new StackLayout();
|
||||
stkUnsigned.Orientation = Orientation.Horizontal;
|
||||
var optUnsigned = new NumericStepper();
|
||||
optUnsigned.ID = "opt" + option.name;
|
||||
optUnsigned.MinValue = 0;
|
||||
optUnsigned.Value = Convert.ToDouble(option.@default);
|
||||
stkUnsigned.Items.Add(optUnsigned);
|
||||
var lblUnsigned = new Label();
|
||||
lblUnsigned.Text = option.description;
|
||||
stkUnsigned.Items.Add(lblUnsigned);
|
||||
stkOptions.Items.Add(stkUnsigned);
|
||||
|
||||
break;
|
||||
case "System.Single":
|
||||
case "System.Double":
|
||||
var stkFloat = new StackLayout();
|
||||
stkFloat.Orientation = Orientation.Horizontal;
|
||||
var optFloat = new NumericStepper();
|
||||
optFloat.ID = "opt" + option.name;
|
||||
optFloat.DecimalPlaces = 2;
|
||||
optFloat.Value = Convert.ToDouble(option.@default);
|
||||
stkFloat.Items.Add(optFloat);
|
||||
var lblFloat = new Label();
|
||||
lblFloat.Text = option.description;
|
||||
stkFloat.Items.Add(lblFloat);
|
||||
stkOptions.Items.Add(stkFloat);
|
||||
|
||||
break;
|
||||
case "System.Guid":
|
||||
// TODO
|
||||
break;
|
||||
case "System.String":
|
||||
var stkString = new StackLayout();
|
||||
stkString.Orientation = Orientation.Horizontal;
|
||||
var lblString = new Label();
|
||||
lblString.Text = option.description;
|
||||
stkString.Items.Add(lblString);
|
||||
var optString = new TextBox();
|
||||
optString.ID = "opt" + option.name;
|
||||
optString.Text = (string)option.@default;
|
||||
stkString.Items.Add(optString);
|
||||
stkOptions.Items.Add(stkString);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
grpOptions.Content = stkOptions;
|
||||
}
|
||||
|
||||
void OnBtnDestinationClick(object sender, EventArgs e)
|
||||
{
|
||||
if(!(cmbFormat.SelectedValue is IWritableImage plugin))
|
||||
return;
|
||||
|
||||
var dlgDestination = new SaveFileDialog
|
||||
{
|
||||
Title = "Choose destination file"
|
||||
};
|
||||
|
||||
dlgDestination.Filters.Add(new FileFilter(plugin.Name, plugin.KnownExtensions.ToArray()));
|
||||
|
||||
DialogResult result = dlgDestination.ShowDialog(this);
|
||||
|
||||
if(result != DialogResult.Ok)
|
||||
{
|
||||
txtDestination.Text = "";
|
||||
_outputPrefix = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(string.IsNullOrEmpty(Path.GetExtension(dlgDestination.FileName)))
|
||||
dlgDestination.FileName += plugin.KnownExtensions.First();
|
||||
|
||||
txtDestination.Text = dlgDestination.FileName;
|
||||
|
||||
_outputPrefix = Path.Combine(Path.GetDirectoryName(dlgDestination.FileName),
|
||||
Path.GetFileNameWithoutExtension(dlgDestination.FileName));
|
||||
|
||||
chkResume.Checked = true;
|
||||
}
|
||||
|
||||
void OnChkSidecarCheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
cmbEncoding.Visible = chkSidecar.Checked.Value;
|
||||
lblEncoding.Visible = chkSidecar.Checked.Value;
|
||||
}
|
||||
|
||||
void OnChkExistingMetadataCheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if(chkExistingMetadata.Checked == false)
|
||||
{
|
||||
_sidecar = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var dlgMetadata = new OpenFileDialog
|
||||
{
|
||||
Title = "Choose existing metadata sidecar", CheckFileExists = true
|
||||
};
|
||||
|
||||
dlgMetadata.Filters.Add(new FileFilter("CICM XML metadata", ".xml"));
|
||||
|
||||
DialogResult result = dlgMetadata.ShowDialog(this);
|
||||
|
||||
if(result != DialogResult.Ok)
|
||||
{
|
||||
chkExistingMetadata.Checked = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var sidecarXs = new XmlSerializer(typeof(CICMMetadataType));
|
||||
|
||||
try
|
||||
{
|
||||
var sr = new StreamReader(dlgMetadata.FileName);
|
||||
_sidecar = (CICMMetadataType)sidecarXs.Deserialize(sr);
|
||||
sr.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Eto.Forms.MessageBox.Show("Incorrect metadata sidecar file...", MessageBoxType.Error);
|
||||
chkExistingMetadata.Checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
void OnChkResumeCheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if(chkResume.Checked == false)
|
||||
return;
|
||||
|
||||
if(_outputPrefix != null)
|
||||
CheckResumeFile();
|
||||
}
|
||||
|
||||
void CheckResumeFile()
|
||||
{
|
||||
_resume = null;
|
||||
var xs = new XmlSerializer(typeof(Resume));
|
||||
|
||||
try
|
||||
{
|
||||
var sr = new StreamReader(_outputPrefix + ".resume.xml");
|
||||
_resume = (Resume)xs.Deserialize(sr);
|
||||
sr.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Eto.Forms.MessageBox.Show("Incorrect resume file, cannot use it...", MessageBoxType.Error);
|
||||
chkResume.Checked = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(_resume == null ||
|
||||
_resume.NextBlock <= _resume.LastBlock ||
|
||||
(_resume.BadBlocks.Count != 0 && !_resume.Tape))
|
||||
return;
|
||||
|
||||
Eto.Forms.MessageBox.Show("Media already dumped correctly, please choose another destination...",
|
||||
MessageBoxType.Warning);
|
||||
|
||||
chkResume.Checked = false;
|
||||
}
|
||||
|
||||
void OnBtnCloseClick(object sender, EventArgs e) => Close();
|
||||
|
||||
void OnBtnStopClick(object sender, EventArgs e)
|
||||
{
|
||||
btnStop.Enabled = false;
|
||||
_dumper.Abort();
|
||||
}
|
||||
|
||||
void OnBtnDumpClick(object sender, EventArgs e)
|
||||
{
|
||||
txtLog.Text = "";
|
||||
btnClose.Visible = false;
|
||||
btnStart.Visible = false;
|
||||
btnStop.Visible = true;
|
||||
btnStop.Enabled = true;
|
||||
stkProgress.Visible = true;
|
||||
btnDestination.Visible = false;
|
||||
stkOptions.Visible = false;
|
||||
|
||||
UpdateStatus("Opening device...");
|
||||
|
||||
try
|
||||
{
|
||||
_dev = new Device(_devicePath);
|
||||
|
||||
if(_dev.IsRemote)
|
||||
Statistics.AddRemote(_dev.RemoteApplication, _dev.RemoteVersion, _dev.RemoteOperatingSystem,
|
||||
_dev.RemoteOperatingSystemVersion, _dev.RemoteArchitecture);
|
||||
|
||||
if(_dev.Error)
|
||||
{
|
||||
StoppingErrorMessage($"Error {_dev.LastError} opening device.");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(Exception exception)
|
||||
{
|
||||
StoppingErrorMessage($"Exception {exception.Message} opening device.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Statistics.AddDevice(_dev);
|
||||
Statistics.AddCommand("dump-media");
|
||||
|
||||
if(!(cmbFormat.SelectedValue is IWritableImage outputFormat))
|
||||
{
|
||||
StoppingErrorMessage("Cannot open output plugin.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Encoding encoding = null;
|
||||
|
||||
if(cmbEncoding.SelectedValue is CommonEncodingInfo encodingInfo)
|
||||
try
|
||||
{
|
||||
encoding = Claunia.Encoding.Encoding.GetEncoding(encodingInfo.Name);
|
||||
}
|
||||
catch(ArgumentException)
|
||||
{
|
||||
StoppingErrorMessage("Specified encoding is not supported.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<string, string> parsedOptions = new Dictionary<string, string>();
|
||||
|
||||
if(grpOptions.Content is StackLayout stkFormatOptions)
|
||||
foreach(Control option in stkFormatOptions.Children)
|
||||
{
|
||||
string value;
|
||||
|
||||
switch(option)
|
||||
{
|
||||
case CheckBox optBoolean:
|
||||
value = optBoolean.Checked?.ToString();
|
||||
|
||||
break;
|
||||
case NumericStepper optNumber:
|
||||
value = optNumber.Value.ToString(CultureInfo.CurrentCulture);
|
||||
|
||||
break;
|
||||
case TextBox optString:
|
||||
value = optString.Text;
|
||||
|
||||
break;
|
||||
default: continue;
|
||||
}
|
||||
|
||||
string key = option.ID.Substring(3);
|
||||
|
||||
parsedOptions.Add(key, value);
|
||||
}
|
||||
|
||||
var dumpLog = new DumpLog(_outputPrefix + ".log", _dev, false);
|
||||
|
||||
dumpLog.WriteLine("Output image format: {0}.", outputFormat.Name);
|
||||
|
||||
_dumper = new Dump(chkResume.Checked == true, _dev, _devicePath, outputFormat,
|
||||
(ushort)stpRetries.Value,
|
||||
chkForce.Checked == true, false, chkPersistent.Checked == true,
|
||||
chkStopOnError.Checked == true, _resume, dumpLog, encoding, _outputPrefix,
|
||||
txtDestination.Text, parsedOptions, _sidecar, (uint)stpSkipped.Value,
|
||||
chkExistingMetadata.Checked == false, chkTrim.Checked == false,
|
||||
chkTrack1Pregap.Checked == true, true, false, DumpSubchannel.Any, 0, false);
|
||||
|
||||
new Thread(DoWork).Start();
|
||||
}
|
||||
|
||||
void DoWork()
|
||||
{
|
||||
_dumper.UpdateStatus += UpdateStatus;
|
||||
_dumper.ErrorMessage += ErrorMessage;
|
||||
_dumper.StoppingErrorMessage += StoppingErrorMessage;
|
||||
_dumper.PulseProgress += PulseProgress;
|
||||
_dumper.InitProgress += InitProgress;
|
||||
_dumper.UpdateProgress += UpdateProgress;
|
||||
_dumper.EndProgress += EndProgress;
|
||||
_dumper.InitProgress2 += InitProgress2;
|
||||
_dumper.UpdateProgress2 += UpdateProgress2;
|
||||
_dumper.EndProgress2 += EndProgress2;
|
||||
|
||||
_dumper.Start();
|
||||
|
||||
_dev.Close();
|
||||
|
||||
WorkFinished();
|
||||
}
|
||||
|
||||
void WorkFinished() => Application.Instance.Invoke(() =>
|
||||
{
|
||||
btnClose.Visible = true;
|
||||
btnStop.Visible = false;
|
||||
stkProgress1.Visible = false;
|
||||
stkProgress2.Visible = false;
|
||||
});
|
||||
|
||||
void EndProgress2() => Application.Instance.Invoke(() =>
|
||||
{
|
||||
stkProgress2.Visible = false;
|
||||
});
|
||||
|
||||
void UpdateProgress2(string text, long current, long maximum) => Application.Instance.Invoke(() =>
|
||||
{
|
||||
lblProgress2.Text = text;
|
||||
prgProgress2.Indeterminate = false;
|
||||
prgProgress2.MinValue = 0;
|
||||
|
||||
if(maximum > int.MaxValue)
|
||||
{
|
||||
prgProgress2.MaxValue = (int)(maximum / int.MaxValue);
|
||||
prgProgress2.Value = (int)(current / int.MaxValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
prgProgress2.MaxValue = (int)maximum;
|
||||
prgProgress2.Value = (int)current;
|
||||
}
|
||||
});
|
||||
|
||||
void InitProgress2() => Application.Instance.Invoke(() =>
|
||||
{
|
||||
stkProgress2.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(() =>
|
||||
{
|
||||
ErrorMessage(text);
|
||||
Eto.Forms.MessageBox.Show(text, MessageBoxType.Error);
|
||||
WorkFinished();
|
||||
});
|
||||
|
||||
void ErrorMessage(string text) => Application.Instance.Invoke(() =>
|
||||
{
|
||||
txtLog.Append(text + Environment.NewLine, true);
|
||||
});
|
||||
|
||||
void UpdateStatus(string text) => Application.Instance.Invoke(() =>
|
||||
{
|
||||
txtLog.Append(text + Environment.NewLine, true);
|
||||
});
|
||||
|
||||
class CommonEncodingInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
}
|
||||
|
||||
#region XAML IDs
|
||||
// ReSharper disable InconsistentNaming
|
||||
ComboBox cmbFormat;
|
||||
TextBox txtDestination;
|
||||
Button btnDestination;
|
||||
CheckBox chkStopOnError;
|
||||
CheckBox chkForce;
|
||||
CheckBox chkPersistent;
|
||||
CheckBox chkResume;
|
||||
CheckBox chkTrack1Pregap;
|
||||
CheckBox chkSidecar;
|
||||
CheckBox chkTrim;
|
||||
CheckBox chkExistingMetadata;
|
||||
ComboBox cmbEncoding;
|
||||
GroupBox grpOptions;
|
||||
NumericStepper stpRetries;
|
||||
NumericStepper stpSkipped;
|
||||
Label lblEncoding;
|
||||
Button btnClose;
|
||||
Button btnStart;
|
||||
Button btnStop;
|
||||
StackLayout stkProgress;
|
||||
Label lblDestinationLabel;
|
||||
Label lblDestination;
|
||||
TextArea txtLog;
|
||||
StackLayout stkProgress1;
|
||||
Label lblProgress;
|
||||
ProgressBar prgProgress;
|
||||
StackLayout stkProgress2;
|
||||
Label lblProgress2;
|
||||
ProgressBar prgProgress2;
|
||||
StackLayout stkOptions;
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
870
Aaru.Gui/ViewModels/MediaDumpViewModel.cs
Normal file
870
Aaru.Gui/ViewModels/MediaDumpViewModel.cs
Normal file
@@ -0,0 +1,870 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Xml.Serialization;
|
||||
using Aaru.CommonTypes;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Metadata;
|
||||
using Aaru.Core;
|
||||
using Aaru.Core.Devices.Dumping;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Core.Media.Info;
|
||||
using Aaru.Devices;
|
||||
using Aaru.Gui.Models;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using DynamicData;
|
||||
using MessageBox.Avalonia;
|
||||
using MessageBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using Schemas;
|
||||
using DeviceInfo = Aaru.Core.Devices.Info.DeviceInfo;
|
||||
using MediaType = Aaru.CommonTypes.MediaType;
|
||||
|
||||
namespace Aaru.Gui.ViewModels
|
||||
{
|
||||
public class MediaDumpViewModel : ViewModelBase
|
||||
{
|
||||
readonly string _devicePath;
|
||||
readonly Window _view;
|
||||
bool _closeVisible;
|
||||
string _destination;
|
||||
bool _destinationEnabled;
|
||||
Device _dev;
|
||||
Dump _dumper;
|
||||
string _encodingEnabled;
|
||||
bool _encodingVisible;
|
||||
bool _existingMetadata;
|
||||
bool _force;
|
||||
string _formatReadOnly;
|
||||
string _log;
|
||||
bool _optionsVisible;
|
||||
string _outputPrefix;
|
||||
bool _persistent;
|
||||
bool _progress1Visible;
|
||||
bool _progress2Indeterminate;
|
||||
double _progress2MaxValue;
|
||||
string _progress2Text;
|
||||
double _progress2Value;
|
||||
bool _progress2Visible;
|
||||
bool _progressIndeterminate;
|
||||
double _progressMaxValue;
|
||||
string _progressText;
|
||||
double _progressValue;
|
||||
bool _progressVisible;
|
||||
Resume _resume;
|
||||
double _retries;
|
||||
EncodingModel _selectedEncoding;
|
||||
ImagePluginModel _selectedPlugin;
|
||||
CICMMetadataType _sidecar;
|
||||
double _skipped;
|
||||
bool _startVisible;
|
||||
bool _stopEnabled;
|
||||
bool _stopOnError;
|
||||
bool _stopVisible;
|
||||
bool _track1Pregap;
|
||||
bool _track1PregapVisible;
|
||||
bool _trim;
|
||||
bool _useResume;
|
||||
bool _useSidecar;
|
||||
|
||||
public MediaDumpViewModel(string devicePath, DeviceInfo deviceInfo, Window view, ScsiInfo scsiInfo = null)
|
||||
{
|
||||
_view = view;
|
||||
DestinationEnabled = true;
|
||||
StartVisible = true;
|
||||
CloseVisible = true;
|
||||
OptionsVisible = true;
|
||||
StartCommand = ReactiveCommand.Create(ExecuteStartCommand);
|
||||
CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand);
|
||||
StopCommand = ReactiveCommand.Create(ExecuteStopCommand);
|
||||
DestinationCommand = ReactiveCommand.Create(ExecuteDestinationCommand);
|
||||
PluginsList = new ObservableCollection<ImagePluginModel>();
|
||||
Encodings = new ObservableCollection<EncodingModel>();
|
||||
|
||||
// Defaults
|
||||
StopOnError = false;
|
||||
Force = false;
|
||||
Persistent = true;
|
||||
Resume = true;
|
||||
Track1Pregap = false;
|
||||
Sidecar = true;
|
||||
Trim = true;
|
||||
ExistingMetadata = false;
|
||||
Retries = 5;
|
||||
Skipped = 512;
|
||||
|
||||
MediaType mediaType;
|
||||
|
||||
if(scsiInfo != null)
|
||||
mediaType = scsiInfo.MediaType;
|
||||
else
|
||||
switch(deviceInfo.Type)
|
||||
{
|
||||
case DeviceType.SecureDigital:
|
||||
mediaType = MediaType.SecureDigital;
|
||||
|
||||
break;
|
||||
case DeviceType.MMC:
|
||||
mediaType = MediaType.MMC;
|
||||
|
||||
break;
|
||||
default:
|
||||
if(deviceInfo.IsPcmcia)
|
||||
mediaType = MediaType.PCCardTypeII;
|
||||
else if(deviceInfo.IsCompactFlash)
|
||||
mediaType = MediaType.CompactFlash;
|
||||
else
|
||||
mediaType = MediaType.GENERIC_HDD;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
PluginBase plugins = GetPluginBase.Instance;
|
||||
|
||||
foreach(IWritableImage plugin in
|
||||
plugins.WritableImages.Values.Where(p => p.SupportedMediaTypes.Contains(mediaType)))
|
||||
PluginsList.Add(new ImagePluginModel
|
||||
{
|
||||
Plugin = plugin
|
||||
});
|
||||
|
||||
Encodings.AddRange(Encoding.GetEncodings().Select(info => new EncodingModel
|
||||
{
|
||||
Name = info.Name, DisplayName = info.GetEncoding().EncodingName
|
||||
}));
|
||||
|
||||
Encodings.AddRange(Claunia.Encoding.Encoding.GetEncodings().Select(info => new EncodingModel
|
||||
{
|
||||
Name = info.Name, DisplayName = info.DisplayName
|
||||
}));
|
||||
|
||||
switch(mediaType)
|
||||
{
|
||||
case MediaType.CD:
|
||||
case MediaType.CDDA:
|
||||
case MediaType.CDG:
|
||||
case MediaType.CDEG:
|
||||
case MediaType.CDI:
|
||||
case MediaType.CDROM:
|
||||
case MediaType.CDROMXA:
|
||||
case MediaType.CDPLUS:
|
||||
case MediaType.CDMO:
|
||||
case MediaType.CDR:
|
||||
case MediaType.CDRW:
|
||||
case MediaType.CDMRW:
|
||||
case MediaType.VCD:
|
||||
case MediaType.SVCD:
|
||||
case MediaType.PCD:
|
||||
case MediaType.DDCD:
|
||||
case MediaType.DDCDR:
|
||||
case MediaType.DDCDRW:
|
||||
case MediaType.DTSCD:
|
||||
case MediaType.CDMIDI:
|
||||
case MediaType.CDV:
|
||||
case MediaType.CDIREADY:
|
||||
case MediaType.FMTOWNS:
|
||||
case MediaType.PS1CD:
|
||||
case MediaType.PS2CD:
|
||||
case MediaType.MEGACD:
|
||||
case MediaType.SATURNCD:
|
||||
case MediaType.GDROM:
|
||||
case MediaType.GDR:
|
||||
case MediaType.MilCD:
|
||||
case MediaType.SuperCDROM2:
|
||||
case MediaType.JaguarCD:
|
||||
case MediaType.ThreeDO:
|
||||
case MediaType.PCFX:
|
||||
case MediaType.NeoGeoCD:
|
||||
case MediaType.CDTV:
|
||||
case MediaType.CD32:
|
||||
case MediaType.Playdia:
|
||||
case MediaType.Pippin:
|
||||
case MediaType.VideoNow:
|
||||
case MediaType.VideoNowColor:
|
||||
case MediaType.VideoNowXp:
|
||||
Track1PregapVisible = true;
|
||||
|
||||
break;
|
||||
default:
|
||||
Track1PregapVisible = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_devicePath = devicePath;
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> StartCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> CloseCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> StopCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> DestinationCommand { get; }
|
||||
|
||||
public ObservableCollection<ImagePluginModel> PluginsList { get; }
|
||||
public ObservableCollection<EncodingModel> Encodings { get; }
|
||||
|
||||
public string Title { get; }
|
||||
|
||||
public bool OptionsVisible
|
||||
{
|
||||
get => _optionsVisible;
|
||||
set => this.RaiseAndSetIfChanged(ref _optionsVisible, value);
|
||||
}
|
||||
|
||||
public ImagePluginModel SelectedPlugin
|
||||
{
|
||||
get => _selectedPlugin;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _selectedPlugin, value);
|
||||
|
||||
Destination = "";
|
||||
|
||||
if(value is null)
|
||||
{
|
||||
DestinationEnabled = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DestinationEnabled = true;
|
||||
|
||||
if(!value.Plugin.SupportedOptions.Any())
|
||||
{
|
||||
// Hide options
|
||||
}
|
||||
|
||||
/* TODO: Plugin options
|
||||
grpOptions.Visible = true;
|
||||
|
||||
var stkOptions = new StackLayout
|
||||
{
|
||||
Orientation = Orientation.Vertical
|
||||
};
|
||||
|
||||
foreach((string name, Type type, string description, object @default) option in plugin.SupportedOptions)
|
||||
switch(option.type.ToString())
|
||||
{
|
||||
case "System.Boolean":
|
||||
var optBoolean = new CheckBox();
|
||||
optBoolean.ID = "opt" + option.name;
|
||||
optBoolean.Text = option.description;
|
||||
optBoolean.Checked = (bool)option.@default;
|
||||
stkOptions.Items.Add(optBoolean);
|
||||
|
||||
break;
|
||||
case "System.SByte":
|
||||
case "System.Int16":
|
||||
case "System.Int32":
|
||||
case "System.Int64":
|
||||
var stkNumber = new StackLayout();
|
||||
stkNumber.Orientation = Orientation.Horizontal;
|
||||
var optNumber = new NumericStepper();
|
||||
optNumber.ID = "opt" + option.name;
|
||||
optNumber.Value = Convert.ToDouble(option.@default);
|
||||
stkNumber.Items.Add(optNumber);
|
||||
var lblNumber = new Label();
|
||||
lblNumber.Text = option.description;
|
||||
stkNumber.Items.Add(lblNumber);
|
||||
stkOptions.Items.Add(stkNumber);
|
||||
|
||||
break;
|
||||
case "System.Byte":
|
||||
case "System.UInt16":
|
||||
case "System.UInt32":
|
||||
case "System.UInt64":
|
||||
var stkUnsigned = new StackLayout();
|
||||
stkUnsigned.Orientation = Orientation.Horizontal;
|
||||
var optUnsigned = new NumericStepper();
|
||||
optUnsigned.ID = "opt" + option.name;
|
||||
optUnsigned.MinValue = 0;
|
||||
optUnsigned.Value = Convert.ToDouble(option.@default);
|
||||
stkUnsigned.Items.Add(optUnsigned);
|
||||
var lblUnsigned = new Label();
|
||||
lblUnsigned.Text = option.description;
|
||||
stkUnsigned.Items.Add(lblUnsigned);
|
||||
stkOptions.Items.Add(stkUnsigned);
|
||||
|
||||
break;
|
||||
case "System.Single":
|
||||
case "System.Double":
|
||||
var stkFloat = new StackLayout();
|
||||
stkFloat.Orientation = Orientation.Horizontal;
|
||||
var optFloat = new NumericStepper();
|
||||
optFloat.ID = "opt" + option.name;
|
||||
optFloat.DecimalPlaces = 2;
|
||||
optFloat.Value = Convert.ToDouble(option.@default);
|
||||
stkFloat.Items.Add(optFloat);
|
||||
var lblFloat = new Label();
|
||||
lblFloat.Text = option.description;
|
||||
stkFloat.Items.Add(lblFloat);
|
||||
stkOptions.Items.Add(stkFloat);
|
||||
|
||||
break;
|
||||
case "System.Guid":
|
||||
// TODO
|
||||
break;
|
||||
case "System.String":
|
||||
var stkString = new StackLayout();
|
||||
stkString.Orientation = Orientation.Horizontal;
|
||||
var lblString = new Label();
|
||||
lblString.Text = option.description;
|
||||
stkString.Items.Add(lblString);
|
||||
var optString = new TextBox();
|
||||
optString.ID = "opt" + option.name;
|
||||
optString.Text = (string)option.@default;
|
||||
stkString.Items.Add(optString);
|
||||
stkOptions.Items.Add(stkString);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
grpOptions.Content = stkOptions;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
public string FormatReadOnly
|
||||
{
|
||||
get => _formatReadOnly;
|
||||
set => this.RaiseAndSetIfChanged(ref _formatReadOnly, value);
|
||||
}
|
||||
|
||||
public string Destination
|
||||
{
|
||||
get => _destination;
|
||||
set => this.RaiseAndSetIfChanged(ref _destination, value);
|
||||
}
|
||||
|
||||
public bool DestinationEnabled
|
||||
{
|
||||
get => _destinationEnabled;
|
||||
set => this.RaiseAndSetIfChanged(ref _destinationEnabled, value);
|
||||
}
|
||||
|
||||
public bool StopOnError
|
||||
{
|
||||
get => _stopOnError;
|
||||
set => this.RaiseAndSetIfChanged(ref _stopOnError, value);
|
||||
}
|
||||
|
||||
public bool Force
|
||||
{
|
||||
get => _force;
|
||||
set => this.RaiseAndSetIfChanged(ref _force, value);
|
||||
}
|
||||
|
||||
public double Retries
|
||||
{
|
||||
get => _retries;
|
||||
set => this.RaiseAndSetIfChanged(ref _retries, value);
|
||||
}
|
||||
|
||||
public bool Persistent
|
||||
{
|
||||
get => _persistent;
|
||||
set => this.RaiseAndSetIfChanged(ref _persistent, value);
|
||||
}
|
||||
|
||||
public bool Resume
|
||||
{
|
||||
get => _useResume;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _useResume, value);
|
||||
|
||||
if(value == false)
|
||||
return;
|
||||
|
||||
if(_outputPrefix != null)
|
||||
CheckResumeFile();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Track1Pregap
|
||||
{
|
||||
get => _track1Pregap;
|
||||
set => this.RaiseAndSetIfChanged(ref _track1Pregap, value);
|
||||
}
|
||||
|
||||
public bool Track1PregapVisible
|
||||
{
|
||||
get => _track1PregapVisible;
|
||||
set => this.RaiseAndSetIfChanged(ref _track1PregapVisible, value);
|
||||
}
|
||||
|
||||
public double Skipped
|
||||
{
|
||||
get => _skipped;
|
||||
set => this.RaiseAndSetIfChanged(ref _skipped, value);
|
||||
}
|
||||
|
||||
public bool Sidecar
|
||||
{
|
||||
get => _useSidecar;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _useSidecar, value);
|
||||
EncodingVisible = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool EncodingVisible
|
||||
{
|
||||
get => _encodingVisible;
|
||||
set => this.RaiseAndSetIfChanged(ref _encodingVisible, value);
|
||||
}
|
||||
|
||||
public bool Trim
|
||||
{
|
||||
get => _trim;
|
||||
set => this.RaiseAndSetIfChanged(ref _trim, value);
|
||||
}
|
||||
|
||||
public bool ExistingMetadata
|
||||
{
|
||||
get => _existingMetadata;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _existingMetadata, value);
|
||||
|
||||
if(value == false)
|
||||
{
|
||||
_sidecar = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var dlgMetadata = new OpenFileDialog
|
||||
{
|
||||
Title = "Choose existing metadata sidecar"
|
||||
};
|
||||
|
||||
dlgMetadata.Filters.Add(new FileDialogFilter
|
||||
{
|
||||
Name = "CICM XML metadata", Extensions = new List<string>(new[]
|
||||
{
|
||||
".xml"
|
||||
})
|
||||
});
|
||||
|
||||
string[] result = dlgMetadata.ShowAsync(_view).Result;
|
||||
|
||||
if(result?.Length != 1)
|
||||
{
|
||||
ExistingMetadata = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var sidecarXs = new XmlSerializer(typeof(CICMMetadataType));
|
||||
|
||||
try
|
||||
{
|
||||
var sr = new StreamReader(result[0]);
|
||||
_sidecar = (CICMMetadataType)sidecarXs.Deserialize(sr);
|
||||
sr.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ReSharper disable AssignmentIsFullyDiscarded
|
||||
_ = MessageBoxManager.
|
||||
|
||||
// ReSharper restore AssignmentIsFullyDiscarded
|
||||
GetMessageBoxStandardWindow("Error", "Incorrect metadata sidecar file...", ButtonEnum.Ok,
|
||||
Icon.Error).ShowDialog(_view).Result;
|
||||
|
||||
ExistingMetadata = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EncodingModel SelectedEncoding
|
||||
{
|
||||
get => _selectedEncoding;
|
||||
set => this.RaiseAndSetIfChanged(ref _selectedEncoding, value);
|
||||
}
|
||||
|
||||
public string EncodingEnabled
|
||||
{
|
||||
get => _encodingEnabled;
|
||||
set => this.RaiseAndSetIfChanged(ref _encodingEnabled, value);
|
||||
}
|
||||
|
||||
public bool ProgressVisible
|
||||
{
|
||||
get => _progressVisible;
|
||||
set => this.RaiseAndSetIfChanged(ref _progressVisible, value);
|
||||
}
|
||||
|
||||
public string Log
|
||||
{
|
||||
get => _log;
|
||||
set => this.RaiseAndSetIfChanged(ref _log, 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 ProgressValue
|
||||
{
|
||||
get => _progressValue;
|
||||
set => this.RaiseAndSetIfChanged(ref _progressValue, value);
|
||||
}
|
||||
|
||||
public double ProgressMaxValue
|
||||
{
|
||||
get => _progressMaxValue;
|
||||
set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value);
|
||||
}
|
||||
|
||||
public bool ProgressIndeterminate
|
||||
{
|
||||
get => _progressIndeterminate;
|
||||
set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value);
|
||||
}
|
||||
|
||||
public bool Progress2Visible
|
||||
{
|
||||
get => _progress2Visible;
|
||||
set => this.RaiseAndSetIfChanged(ref _progress2Visible, value);
|
||||
}
|
||||
|
||||
public string Progress2Text
|
||||
{
|
||||
get => _progress2Text;
|
||||
set => this.RaiseAndSetIfChanged(ref _progress2Text, value);
|
||||
}
|
||||
|
||||
public double Progress2Value
|
||||
{
|
||||
get => _progress2Value;
|
||||
set => this.RaiseAndSetIfChanged(ref _progress2Value, value);
|
||||
}
|
||||
|
||||
public double Progress2MaxValue
|
||||
{
|
||||
get => _progress2MaxValue;
|
||||
set => this.RaiseAndSetIfChanged(ref _progress2MaxValue, value);
|
||||
}
|
||||
|
||||
public bool Progress2Indeterminate
|
||||
{
|
||||
get => _progress2Indeterminate;
|
||||
set => this.RaiseAndSetIfChanged(ref _progress2Indeterminate, 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 bool StopEnabled
|
||||
{
|
||||
get => _stopEnabled;
|
||||
set => this.RaiseAndSetIfChanged(ref _stopEnabled, value);
|
||||
}
|
||||
|
||||
async void ExecuteDestinationCommand()
|
||||
{
|
||||
if(SelectedPlugin is null)
|
||||
return;
|
||||
|
||||
var dlgDestination = new SaveFileDialog
|
||||
{
|
||||
Title = "Choose destination file"
|
||||
};
|
||||
|
||||
dlgDestination.Filters.Add(new FileDialogFilter
|
||||
{
|
||||
Name = SelectedPlugin.Plugin.Name, Extensions = SelectedPlugin.Plugin.KnownExtensions.ToList()
|
||||
});
|
||||
|
||||
string result = await dlgDestination.ShowAsync(_view);
|
||||
|
||||
if(result is null)
|
||||
{
|
||||
Destination = "";
|
||||
_outputPrefix = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(string.IsNullOrEmpty(Path.GetExtension(result)))
|
||||
result += SelectedPlugin.Plugin.KnownExtensions.First();
|
||||
|
||||
Destination = result;
|
||||
|
||||
_outputPrefix = Path.Combine(Path.GetDirectoryName(result), Path.GetFileNameWithoutExtension(result));
|
||||
|
||||
Resume = true;
|
||||
}
|
||||
|
||||
async void CheckResumeFile()
|
||||
{
|
||||
_resume = null;
|
||||
var xs = new XmlSerializer(typeof(Resume));
|
||||
|
||||
try
|
||||
{
|
||||
var sr = new StreamReader(_outputPrefix + ".resume.xml");
|
||||
_resume = (Resume)xs.Deserialize(sr);
|
||||
sr.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageBoxManager.
|
||||
GetMessageBoxStandardWindow("Error", "Incorrect resume file, cannot use it...", ButtonEnum.Ok,
|
||||
Icon.Error).ShowDialog(_view);
|
||||
|
||||
Resume = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(_resume == null ||
|
||||
_resume.NextBlock <= _resume.LastBlock ||
|
||||
(_resume.BadBlocks.Count != 0 && !_resume.Tape))
|
||||
return;
|
||||
|
||||
await MessageBoxManager.
|
||||
GetMessageBoxStandardWindow("Warning",
|
||||
"Media already dumped correctly, please choose another destination...",
|
||||
ButtonEnum.Ok, Icon.Warning).ShowDialog(_view);
|
||||
|
||||
Resume = false;
|
||||
}
|
||||
|
||||
void ExecuteCloseCommand() => _view.Close();
|
||||
|
||||
internal void ExecuteStopCommand()
|
||||
{
|
||||
StopEnabled = false;
|
||||
_dumper?.Abort();
|
||||
}
|
||||
|
||||
void ExecuteStartCommand()
|
||||
{
|
||||
Log = "";
|
||||
CloseVisible = false;
|
||||
StartVisible = false;
|
||||
StopVisible = true;
|
||||
StopEnabled = true;
|
||||
ProgressVisible = true;
|
||||
DestinationEnabled = false;
|
||||
OptionsVisible = false;
|
||||
|
||||
UpdateStatus("Opening device...");
|
||||
|
||||
try
|
||||
{
|
||||
_dev = new Device(_devicePath);
|
||||
|
||||
if(_dev.IsRemote)
|
||||
Statistics.AddRemote(_dev.RemoteApplication, _dev.RemoteVersion, _dev.RemoteOperatingSystem,
|
||||
_dev.RemoteOperatingSystemVersion, _dev.RemoteArchitecture);
|
||||
|
||||
if(_dev.Error)
|
||||
{
|
||||
StoppingErrorMessage($"Error {_dev.LastError} opening device.");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(Exception exception)
|
||||
{
|
||||
StoppingErrorMessage($"Exception {exception.Message} opening device.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Statistics.AddDevice(_dev);
|
||||
Statistics.AddCommand("dump-media");
|
||||
|
||||
if(SelectedPlugin is null)
|
||||
{
|
||||
StoppingErrorMessage("Cannot open output plugin.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Encoding encoding = null;
|
||||
|
||||
if(!(SelectedEncoding is null))
|
||||
try
|
||||
{
|
||||
encoding = Claunia.Encoding.Encoding.GetEncoding(SelectedEncoding.Name);
|
||||
}
|
||||
catch(ArgumentException)
|
||||
{
|
||||
StoppingErrorMessage("Specified encoding is not supported.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<string, string> parsedOptions = new Dictionary<string, string>();
|
||||
|
||||
/* TODO: Options
|
||||
if(grpOptions.Content is StackLayout stkFormatOptions)
|
||||
foreach(Control option in stkFormatOptions.Children)
|
||||
{
|
||||
string value;
|
||||
|
||||
switch(option)
|
||||
{
|
||||
case CheckBox optBoolean:
|
||||
value = optBoolean.Checked?.ToString();
|
||||
|
||||
break;
|
||||
case NumericStepper optNumber:
|
||||
value = optNumber.Value.ToString(CultureInfo.CurrentCulture);
|
||||
|
||||
break;
|
||||
case TextBox optString:
|
||||
value = optString.Text;
|
||||
|
||||
break;
|
||||
default: continue;
|
||||
}
|
||||
|
||||
string key = option.ID.Substring(3);
|
||||
|
||||
parsedOptions.Add(key, value);
|
||||
}
|
||||
*/
|
||||
|
||||
var dumpLog = new DumpLog(_outputPrefix + ".log", _dev, false);
|
||||
|
||||
dumpLog.WriteLine("Output image format: {0}.", SelectedPlugin.Name);
|
||||
|
||||
_dumper = new Dump(Resume, _dev, _devicePath, SelectedPlugin.Plugin, (ushort)Retries, Force, false,
|
||||
Persistent, StopOnError, _resume, dumpLog, encoding, _outputPrefix, Destination,
|
||||
parsedOptions, _sidecar, (uint)Skipped, ExistingMetadata == false, Trim == false,
|
||||
Track1Pregap, true, false, DumpSubchannel.Any, 0, false);
|
||||
|
||||
new Thread(DoWork).Start();
|
||||
}
|
||||
|
||||
void DoWork()
|
||||
{
|
||||
_dumper.UpdateStatus += UpdateStatus;
|
||||
_dumper.ErrorMessage += ErrorMessage;
|
||||
_dumper.StoppingErrorMessage += StoppingErrorMessage;
|
||||
_dumper.PulseProgress += PulseProgress;
|
||||
_dumper.InitProgress += InitProgress;
|
||||
_dumper.UpdateProgress += UpdateProgress;
|
||||
_dumper.EndProgress += EndProgress;
|
||||
_dumper.InitProgress2 += InitProgress2;
|
||||
_dumper.UpdateProgress2 += UpdateProgress2;
|
||||
_dumper.EndProgress2 += EndProgress2;
|
||||
|
||||
_dumper.Start();
|
||||
|
||||
_dev.Close();
|
||||
|
||||
WorkFinished();
|
||||
}
|
||||
|
||||
async void WorkFinished() => await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
CloseVisible = true;
|
||||
StopVisible = false;
|
||||
Progress1Visible = false;
|
||||
Progress2Visible = false;
|
||||
});
|
||||
|
||||
async void EndProgress2() => await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Progress2Visible = false;
|
||||
});
|
||||
|
||||
async void UpdateProgress2(string text, long current, long maximum) =>
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Progress2Text = text;
|
||||
Progress2Indeterminate = false;
|
||||
|
||||
Progress2MaxValue = maximum;
|
||||
Progress2Value = current;
|
||||
});
|
||||
|
||||
async void InitProgress2() => await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Progress2Visible = true;
|
||||
});
|
||||
|
||||
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(async () =>
|
||||
{
|
||||
ErrorMessage(text);
|
||||
|
||||
await MessageBoxManager.GetMessageBoxStandardWindow("Error", $"{text}", ButtonEnum.Ok, Icon.Error).
|
||||
ShowDialog(_view);
|
||||
|
||||
WorkFinished();
|
||||
});
|
||||
|
||||
async void ErrorMessage(string text) => await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Log += text + Environment.NewLine;
|
||||
});
|
||||
|
||||
async void UpdateStatus(string text) => await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Log += text + Environment.NewLine;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -336,25 +336,35 @@ namespace Aaru.Gui.ViewModels
|
||||
|
||||
void ExecuteSaveMediumSupportCommand() => SaveElement(_scsiInfo.MediaTypeSupport);
|
||||
|
||||
void ExecuteDumpCommand()
|
||||
async void ExecuteDumpCommand()
|
||||
{
|
||||
/* TODO: frmDump
|
||||
if(scsiInfo.MediaType == MediaType.GDR ||
|
||||
scsiInfo.MediaType == MediaType.GDROM)
|
||||
if(_scsiInfo.MediaType == CommonTypes.MediaType.GDR ||
|
||||
_scsiInfo.MediaType == CommonTypes.MediaType.GDROM)
|
||||
{
|
||||
Eto.Forms.MessageBox.Show("GD-ROM dump support is not yet implemented.", MessageBoxType.Error);
|
||||
await MessageBoxManager.
|
||||
GetMessageBoxStandardWindow("Error", "GD-ROM dump support is not yet implemented.", ButtonEnum.Ok,
|
||||
Icon.Error).ShowDialog(_view);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if((scsiInfo.MediaType == MediaType.XGD || scsiInfo.MediaType == MediaType.XGD2 ||
|
||||
scsiInfo.MediaType == MediaType.XGD3) &&
|
||||
scsiInfo.DeviceInfo.ScsiInquiry?.KreonPresent != true)
|
||||
Eto.Forms.MessageBox.Show("Dumping Xbox discs require a Kreon drive.", MessageBoxType.Error);
|
||||
if((_scsiInfo.MediaType == CommonTypes.MediaType.XGD || _scsiInfo.MediaType == CommonTypes.MediaType.XGD2 ||
|
||||
_scsiInfo.MediaType == CommonTypes.MediaType.XGD3) &&
|
||||
_scsiInfo.DeviceInfo.ScsiInquiry?.KreonPresent != true)
|
||||
{
|
||||
await MessageBoxManager.
|
||||
GetMessageBoxStandardWindow("Error", "Dumping Xbox discs require a Kreon drive.", ButtonEnum.Ok,
|
||||
Icon.Error).ShowDialog(_view);
|
||||
|
||||
var dumpForm = new frmDump(devicePath, scsiInfo.DeviceInfo, scsiInfo);
|
||||
dumpForm.Show();
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
var mediaDumpWindow = new MediaDumpWindow();
|
||||
|
||||
mediaDumpWindow.DataContext =
|
||||
new MediaDumpViewModel(_devicePath, _scsiInfo.DeviceInfo, mediaDumpWindow, _scsiInfo);
|
||||
|
||||
mediaDumpWindow.Show();
|
||||
}
|
||||
|
||||
async void ExecuteScanCommand()
|
||||
|
||||
94
Aaru.Gui/Views/MediaDumpWindow.xaml
Normal file
94
Aaru.Gui/Views/MediaDumpWindow.xaml
Normal file
@@ -0,0 +1,94 @@
|
||||
<Window xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="clr-namespace:Aaru.Gui.ViewModels;assembly=Aaru.Gui"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800"
|
||||
d:DesignHeight="450" x:Class="Aaru.Gui.Views.MediaDumpWindow" Icon="/Assets/aaru-logo.png"
|
||||
Title="{Binding Title}">
|
||||
<Design.DataContext>
|
||||
<vm:MediaDumpViewModel />
|
||||
</Design.DataContext>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<StackPanel Orientation="Vertical" IsVisible="{Binding OptionsVisible}">
|
||||
<TextBlock Text="Output format" />
|
||||
<ComboBox Items="{Binding PluginsList}" SelectedItem="{Binding SelectedPlugin}"
|
||||
IsEnabled="{Binding FormatReadOnly}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding Destination}" IsReadOnly="True" />
|
||||
<Button Command="{Binding DestinationCommand}" IsEnabled="{Binding DestinationEnabled}">
|
||||
<TextBlock Text="Choose..." />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding StopOnError}">
|
||||
<TextBlock Text="Stop media dump on first error" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding Force}">
|
||||
<TextBlock Text="Continue dumping whatever happens" />
|
||||
</CheckBox>
|
||||
<NumericUpDown Text="Retry passes" Value="{Binding Retries}" Increment="1" Minimum="0" />
|
||||
<CheckBox IsChecked="{Binding Persistent}">
|
||||
<TextBlock Text="Try to recover partial or incorrect data" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding Resume}">
|
||||
<TextBlock Text="Create/use resume mapfile" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding Track1Pregap}" IsVisible="{Binding Track1PregapVisible}">
|
||||
<TextBlock Text="Try to read track 1 pregap" />
|
||||
</CheckBox>
|
||||
<NumericUpDown Text="Skipped sectors on error" Increment="1" Minimum="1" Value="{Binding Skipped}" />
|
||||
<CheckBox IsChecked="{Binding Sidecar}">
|
||||
<TextBlock Text="Create CICM XML metadata sidecar" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding Trim}">
|
||||
<TextBlock Text="Trim errors from skipped sectors" />
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding ExistingMetadata}">
|
||||
<TextBlock Text="Take metadata from existing CICM XML sidecar" />
|
||||
</CheckBox>
|
||||
<StackPanel IsVisible="{Binding EncodingVisible}">
|
||||
<TextBox Text="Encoding to use on metadata sidecar creation" />
|
||||
<ComboBox Items="{Binding Encodings}" SelectedItem="{Binding SelectedEncoding}"
|
||||
IsEnabled="{Binding EncodingEnabled}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<!-- TODO: Image options -->
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" IsVisible="{Binding ProgressVisible}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Writing image to:" /> <TextBlock Text="{Binding Destination}" />
|
||||
</StackPanel>
|
||||
<TextBlock Text="Log" /> <TextBox Text="{Binding Log}" IsReadOnly="True" />
|
||||
<StackPanel Orientation="Vertical" IsVisible="{Binding Progress1Visible}">
|
||||
<TextBox Text="{Binding ProgressText}" />
|
||||
<ProgressBar Value="{Binding ProgressValue}" Maximum="{Binding ProgressMaxValue}"
|
||||
IsIndeterminate="{Binding ProgressIndeterminate}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" IsVisible="{Binding Progress2Visible}">
|
||||
<TextBox Text="{Binding Progress2Text}" />
|
||||
<ProgressBar Value="{Binding Progress2Value}" Maximum="{Binding Progress2MaxValue}"
|
||||
IsIndeterminate="{Binding Progress2Indeterminate}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Command="{Binding StartCommand}" IsVisible="{Binding StartVisible}">
|
||||
<TextBlock Text="Start" />
|
||||
</Button>
|
||||
<Button Command="{Binding CloseCommand}" IsVisible="{Binding CloseVisible}">
|
||||
<TextBlock Text="Close" />
|
||||
</Button>
|
||||
<Button Command="{Binding StopCommand}" IsVisible="{Binding StopVisible}" IsEnabled="{Binding StopEnabled}">
|
||||
<TextBlock Text="Stop" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Window>
|
||||
27
Aaru.Gui/Views/MediaDumpWindow.xaml.cs
Normal file
27
Aaru.Gui/Views/MediaDumpWindow.xaml.cs
Normal file
@@ -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 MediaDumpWindow : Window
|
||||
{
|
||||
public MediaDumpWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitializeComponent() => AvaloniaXamlLoader.Load(this);
|
||||
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
(DataContext as MediaDumpViewModel)?.ExecuteStopCommand();
|
||||
base.OnClosing(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user