// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // Filename : frmImageConvert.xeto.cs // Author(s) : Natalia Portillo // // Component : Image conversion window. // // --[ Description ] ---------------------------------------------------------- // // Implements converting media image. // // --[ 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-2019 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.IO; using System.Linq; using System.Threading; using System.Xml.Serialization; using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes.Enums; using DiscImageChef.CommonTypes.Interfaces; using DiscImageChef.CommonTypes.Metadata; using DiscImageChef.CommonTypes.Structs; using DiscImageChef.Console; using DiscImageChef.Core; using Eto.Forms; using Eto.Serialization.Xaml; using Schemas; using ImageInfo = DiscImageChef.CommonTypes.Structs.ImageInfo; using Version = DiscImageChef.CommonTypes.Interop.Version; namespace DiscImageChef.Gui.Forms { public class frmImageConvert : Form { bool cancel; CICMMetadataType cicmMetadata; List dumpHardware; IMediaImage inputFormat; public frmImageConvert(IMediaImage inputFormat, string imageSource) { this.inputFormat = inputFormat; XamlReader.Load(this); cancel = false; txtSource.Text = imageSource; btnCreator.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.Creator); btnMediaTitle.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaTitle); btnComments.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.Comments); btnMediaManufacturer.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaManufacturer); btnMediaModel.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaModel); btnMediaSerialNumber.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaSerialNumber); btnMediaBarcode.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaBarcode); btnMediaPartNumber.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaPartNumber); btnMediaSequence.Visible = inputFormat.Info.MediaSequence != 0 && inputFormat.Info.LastMediaSequence != 0; btnLastMediaSequence.Visible = inputFormat.Info.MediaSequence != 0 && inputFormat.Info.LastMediaSequence != 0; btnDriveManufacturer.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveManufacturer); btnDriveModel.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveModel); btnDriveSerialNumber.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveSerialNumber); btnDriveFirmwareRevision.Visible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveFirmwareRevision); ObservableCollection lstPlugins = new ObservableCollection(); PluginBase plugins = GetPluginBase.Instance; foreach(IWritableImage plugin in plugins.WritableImages.Values.Where(p => p.SupportedMediaTypes.Contains(inputFormat.Info.MediaType))) lstPlugins.Add(plugin); cmbFormat.ItemTextBinding = Binding.Property((IWritableImage p) => p.Name); cmbFormat.ItemKeyBinding = Binding.Property((IWritableImage p) => p.Id.ToString()); cmbFormat.DataStore = lstPlugins; btnCicmXmlFromImage.Visible = inputFormat.CicmMetadata != null; btnResumeFileFromImage.Visible = inputFormat.DumpHardware != null && inputFormat.DumpHardware.Any(); cicmMetadata = inputFormat.CicmMetadata; dumpHardware = inputFormat.DumpHardware != null && inputFormat.DumpHardware.Any() ? inputFormat.DumpHardware : null; txtCicmXml.Text = cicmMetadata == null ? "" : ""; txtResumeFile.Text = dumpHardware == null ? "" : ""; } protected void OnBtnStart(object sender, EventArgs e) { if(!(cmbFormat.SelectedValue is IWritableImage plugin)) { MessageBox.Show("Error trying to find selected plugin", MessageBoxType.Error); return; } new Thread(DoWork).Start(plugin); } void DoWork(object plugin) { bool warning = false; if(!(plugin is IWritableImage outputFormat)) { MessageBox.Show("Error trying to find selected plugin", MessageBoxType.Error); return; } IOpticalMediaImage inputOptical = inputFormat as IOpticalMediaImage; IWritableOpticalImage outputOptical = outputFormat as IWritableOpticalImage; List tracks; try { tracks = inputOptical?.Tracks; } catch(Exception) { tracks = null; } // Prepare UI Application.Instance.Invoke(() => { btnClose.Visible = false; btnStart.Visible = false; btnStop.Visible = true; stkProgress.Visible = true; stkOptions.Visible = false; btnStop.Enabled = true; cmbFormat.ReadOnly = true; btnDestination.Visible = false; prgProgress.MaxValue = 1; prgProgress.MaxValue += inputFormat.Info.ReadableMediaTags.Count; prgProgress.MaxValue++; if(tracks != null) prgProgress.MaxValue++; if(tracks == null) { prgProgress.MaxValue += 2; foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags) { switch(tag) { case SectorTagType.AppleSectorTag: case SectorTagType.CdSectorSync: case SectorTagType.CdSectorHeader: case SectorTagType.CdSectorSubHeader: case SectorTagType.CdSectorEdc: case SectorTagType.CdSectorEccP: case SectorTagType.CdSectorEccQ: case SectorTagType.CdSectorEcc: // This tags are inline in long sector continue; } if(chkForce.Checked == true && !outputFormat.SupportedSectorTags.Contains(tag)) continue; prgProgress.MaxValue++; } } else { prgProgress.MaxValue += tracks.Count; foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags.OrderBy(t => t)) { switch(tag) { case SectorTagType.AppleSectorTag: case SectorTagType.CdSectorSync: case SectorTagType.CdSectorHeader: case SectorTagType.CdSectorSubHeader: case SectorTagType.CdSectorEdc: case SectorTagType.CdSectorEccP: case SectorTagType.CdSectorEccQ: case SectorTagType.CdSectorEcc: // This tags are inline in long sector continue; } if(chkForce.Checked == true && !outputFormat.SupportedSectorTags.Contains(tag)) continue; prgProgress.MaxValue += tracks.Count; } } if(dumpHardware != null) prgProgress.MaxValue++; if(cicmMetadata != null) prgProgress.MaxValue++; prgProgress.MaxValue++; }); foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags) { if(outputFormat.SupportedMediaTags.Contains(mediaTag) || chkForce.Checked == true) continue; Application.Instance.Invoke(() => { MessageBox .Show($"Converting image will lose media tag {mediaTag}, not continuing...", MessageBoxType.Error); }); return; } bool useLong = inputFormat.Info.ReadableSectorTags.Count != 0; foreach(SectorTagType sectorTag in inputFormat.Info.ReadableSectorTags) { if(outputFormat.SupportedSectorTags.Contains(sectorTag)) continue; if(chkForce.Checked == true) { if(sectorTag != SectorTagType.CdTrackFlags && sectorTag != SectorTagType.CdTrackIsrc && sectorTag != SectorTagType.CdSectorSubchannel) useLong = false; continue; } Application.Instance.Invoke(() => { MessageBox .Show($"Converting image will lose sector tag {sectorTag}, not continuing...", MessageBoxType.Error); }); return; } Dictionary parsedOptions = new Dictionary(); if(grpOptions.Content is StackLayout stkImageOptions) foreach(Control option in stkImageOptions.Children) { if(cancel) break; 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); } Application.Instance.Invoke(() => { lblProgress.Text = "Creating output image"; lblProgress2.Text = ""; prgProgress2.Indeterminate = true; }); if(!outputFormat.Create(txtDestination.Text, inputFormat.Info.MediaType, parsedOptions, inputFormat.Info.Sectors, inputFormat.Info.SectorSize)) { Application.Instance.Invoke(() => { MessageBox .Show($"Error {outputFormat.ErrorMessage} creating output image.", MessageBoxType.Error); }); DicConsole.ErrorWriteLine("Error {0} creating output image.", outputFormat.ErrorMessage); return; } Application.Instance.Invoke(() => { lblProgress.Text = "Setting image metadata"; prgProgress.Value++; lblProgress2.Text = ""; prgProgress2.Indeterminate = true; }); ImageInfo metadata = new ImageInfo { Application = "DiscImageChef", ApplicationVersion = Version.GetVersion(), Comments = txtComments.Text, Creator = txtCreator.Text, DriveFirmwareRevision = txtDriveFirmwareRevision.Text, DriveManufacturer = txtDriveManufacturer.Text, DriveModel = txtDriveModel.Text, DriveSerialNumber = txtDriveSerialNumber.Text, LastMediaSequence = (int)numLastMediaSequence.Value, MediaBarcode = txtMediaBarcode.Text, MediaManufacturer = txtMediaManufacturer.Text, MediaModel = txtMediaModel.Text, MediaPartNumber = txtMediaPartNumber.Text, MediaSequence = (int)numMediaSequence.Value, MediaSerialNumber = txtMediaSerialNumber.Text, MediaTitle = txtMediaTitle.Text }; if(!cancel) if(!outputFormat.SetMetadata(metadata)) { DicConsole.ErrorWrite("Error {0} setting metadata, ", outputFormat.ErrorMessage); if(chkForce.Checked != true) { Application.Instance.Invoke(() => { MessageBox .Show($"Error {outputFormat.ErrorMessage} setting metadata, not continuing...", MessageBoxType.Error); }); DicConsole.ErrorWriteLine("not continuing..."); return; } warning = true; DicConsole.ErrorWriteLine("continuing..."); } if(tracks != null && !cancel && outputOptical != null) { Application.Instance.Invoke(() => { lblProgress.Text = "Setting tracks list"; prgProgress.Value++; lblProgress2.Text = ""; prgProgress2.Indeterminate = true; }); if(!outputOptical.SetTracks(tracks)) { Application.Instance.Invoke(() => { MessageBox .Show($"Error {outputFormat.ErrorMessage} sending tracks list to output image.", MessageBoxType.Error); }); DicConsole.ErrorWriteLine("Error {0} sending tracks list to output image.", outputFormat.ErrorMessage); return; } } foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags) { if(cancel) break; Application.Instance.Invoke(() => { lblProgress.Text = $"Converting media tag {mediaTag}"; prgProgress.Value++; lblProgress2.Text = ""; prgProgress2.Indeterminate = true; }); if(chkForce.Checked == true && !outputFormat.SupportedMediaTags.Contains(mediaTag)) continue; byte[] tag = inputFormat.ReadDiskTag(mediaTag); if(outputFormat.WriteMediaTag(tag, mediaTag)) continue; if(chkForce.Checked == true) { warning = true; DicConsole.ErrorWriteLine("Error {0} writing media tag, continuing...", outputFormat.ErrorMessage); } else { Application.Instance.Invoke(() => { MessageBox .Show($"Error {outputFormat.ErrorMessage} writing media tag, not continuing...", MessageBoxType.Error); }); DicConsole.ErrorWriteLine("Error {0} writing media tag, not continuing...", outputFormat.ErrorMessage); return; } } ulong doneSectors = 0; if(tracks == null && !cancel) { Application.Instance.Invoke(() => { lblProgress.Text = $"Setting geometry to {inputFormat.Info.Cylinders} cylinders, {inputFormat.Info.Heads} heads and {inputFormat.Info.SectorsPerTrack} sectors per track"; prgProgress.Value++; lblProgress2.Text = ""; prgProgress2.Indeterminate = true; }); if(!outputFormat.SetGeometry(inputFormat.Info.Cylinders, inputFormat.Info.Heads, inputFormat.Info.SectorsPerTrack)) { warning = true; DicConsole.ErrorWriteLine("Error {0} setting geometry, image may be incorrect, continuing...", outputFormat.ErrorMessage); } Application.Instance.Invoke(() => { lblProgress.Text = "Converting sectors"; prgProgress.Value++; lblProgress2.Text = ""; prgProgress2.Indeterminate = false; prgProgress2.MaxValue = (int)(inputFormat.Info.Sectors / numCount.Value); }); while(doneSectors < inputFormat.Info.Sectors) { if(cancel) break; byte[] sector; uint sectorsToDo; if(inputFormat.Info.Sectors - doneSectors >= (ulong)numCount.Value) sectorsToDo = (uint)numCount.Value; else sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors); ulong sectors = doneSectors; Application.Instance.Invoke(() => { lblProgress2.Text = $"Converting sectors {sectors} to {sectors + sectorsToDo} ({sectors / (double)inputFormat.Info.Sectors:P2} done)"; ; prgProgress2.Value = (int)(sectors / numCount.Value); }); bool result; if(useLong) if(sectorsToDo == 1) { sector = inputFormat.ReadSectorLong(doneSectors); result = outputFormat.WriteSectorLong(sector, doneSectors); } else { sector = inputFormat.ReadSectorsLong(doneSectors, sectorsToDo); result = outputFormat.WriteSectorsLong(sector, doneSectors, sectorsToDo); } else { if(sectorsToDo == 1) { sector = inputFormat.ReadSector(doneSectors); result = outputFormat.WriteSector(sector, doneSectors); } else { sector = inputFormat.ReadSectors(doneSectors, sectorsToDo); result = outputFormat.WriteSectors(sector, doneSectors, sectorsToDo); } } if(!result) if(chkForce.Checked == true) { warning = true; DicConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...", outputFormat.ErrorMessage, doneSectors); } else { Application.Instance.Invoke(() => { MessageBox .Show($"Error {outputFormat.ErrorMessage} writing sector {doneSectors}, not continuing...", MessageBoxType.Error); }); DicConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...", outputFormat.ErrorMessage, doneSectors); return; } doneSectors += sectorsToDo; } Application.Instance.Invoke(() => { lblProgress2.Text = $"Converting sectors {inputFormat.Info.Sectors} to {inputFormat.Info.Sectors} ({1.0:P2} done)"; ; prgProgress2.Value = prgProgress2.MaxValue; }); foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags) { if(!useLong || cancel) break; switch(tag) { case SectorTagType.AppleSectorTag: case SectorTagType.CdSectorSync: case SectorTagType.CdSectorHeader: case SectorTagType.CdSectorSubHeader: case SectorTagType.CdSectorEdc: case SectorTagType.CdSectorEccP: case SectorTagType.CdSectorEccQ: case SectorTagType.CdSectorEcc: // This tags are inline in long sector continue; } if(chkForce.Checked == true && !outputFormat.SupportedSectorTags.Contains(tag)) continue; Application.Instance.Invoke(() => { lblProgress.Text = $"Converting tag {tag}"; prgProgress.Value++; lblProgress2.Text = ""; prgProgress2.Indeterminate = false; prgProgress2.MaxValue = (int)(inputFormat.Info.Sectors / numCount.Value); }); doneSectors = 0; while(doneSectors < inputFormat.Info.Sectors) { if(cancel) break; byte[] sector; uint sectorsToDo; if(inputFormat.Info.Sectors - doneSectors >= (ulong)numCount.Value) sectorsToDo = (uint)numCount.Value; else sectorsToDo = (uint)(inputFormat.Info.Sectors - doneSectors); ulong sectors = doneSectors; Application.Instance.Invoke(() => { lblProgress2.Text = $"Converting tag {sectors / (double)inputFormat.Info.Sectors} for sectors {sectors} to {sectors + sectorsToDo} ({sectors / (double)inputFormat.Info.Sectors:P2} done)"; prgProgress2.Value = (int)(sectors / numCount.Value); }); bool result; if(sectorsToDo == 1) { sector = inputFormat.ReadSectorTag(doneSectors, tag); result = outputFormat.WriteSectorTag(sector, doneSectors, tag); } else { sector = inputFormat.ReadSectorsTag(doneSectors, sectorsToDo, tag); result = outputFormat.WriteSectorsTag(sector, doneSectors, sectorsToDo, tag); } if(!result) if(chkForce.Checked == true) { warning = true; DicConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...", outputFormat.ErrorMessage, doneSectors); } else { Application.Instance.Invoke(() => { MessageBox .Show($"Error {outputFormat.ErrorMessage} writing sector {doneSectors}, not continuing...", MessageBoxType.Error); }); DicConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...", outputFormat.ErrorMessage, doneSectors); return; } doneSectors += sectorsToDo; } Application.Instance.Invoke(() => { lblProgress2.Text = $"Converting tag {tag} for sectors {inputFormat.Info.Sectors} to {inputFormat.Info.Sectors} ({1.0:P2} done)"; prgProgress2.Value = prgProgress2.MaxValue; }); } } else { foreach(Track track in tracks) { if(cancel) break; doneSectors = 0; ulong trackSectors = track.TrackEndSector - track.TrackStartSector + 1; Application.Instance.Invoke(() => { lblProgress.Text = $"Converting sectors in track {track.TrackSequence}"; prgProgress.Value++; lblProgress2.Text = ""; prgProgress2.Indeterminate = false; prgProgress2.MaxValue = (int)(trackSectors / numCount.Value); }); while(doneSectors < trackSectors) { if(cancel) break; byte[] sector; uint sectorsToDo; if(trackSectors - doneSectors >= (ulong)numCount.Value) sectorsToDo = (uint)numCount.Value; else sectorsToDo = (uint)(trackSectors - doneSectors); ulong sectors = doneSectors; Application.Instance.Invoke(() => { lblProgress2.Text = $"Converting sectors {sectors + track.TrackStartSector} to {sectors + sectorsToDo + track.TrackStartSector} in track {track.TrackSequence} ({(sectors + track.TrackStartSector) / (double)inputFormat.Info.Sectors:P2} done)"; prgProgress2.Value = (int)(sectors / numCount.Value); }); bool result; if(useLong) if(sectorsToDo == 1) { sector = inputFormat.ReadSectorLong(doneSectors + track.TrackStartSector); result = outputFormat.WriteSectorLong(sector, doneSectors + track.TrackStartSector); } else { sector = inputFormat.ReadSectorsLong(doneSectors + track.TrackStartSector, sectorsToDo); result = outputFormat.WriteSectorsLong(sector, doneSectors + track.TrackStartSector, sectorsToDo); } else { if(sectorsToDo == 1) { sector = inputFormat.ReadSector(doneSectors + track.TrackStartSector); result = outputFormat.WriteSector(sector, doneSectors + track.TrackStartSector); } else { sector = inputFormat.ReadSectors(doneSectors + track.TrackStartSector, sectorsToDo); result = outputFormat.WriteSectors(sector, doneSectors + track.TrackStartSector, sectorsToDo); } } if(!result) if(chkForce.Checked == true) { warning = true; DicConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...", outputFormat.ErrorMessage, doneSectors); } else { Application.Instance.Invoke(() => { MessageBox .Show($"Error {outputFormat.ErrorMessage} writing sector {doneSectors}, not continuing...", MessageBoxType.Error); }); return; } doneSectors += sectorsToDo; } } Application.Instance.Invoke(() => { lblProgress2.Text = $"Converting sectors {inputFormat.Info.Sectors} to {inputFormat.Info.Sectors} in track {tracks.Count} ({1.0:P2} done)"; prgProgress2.Value = prgProgress2.MaxValue; }); foreach(SectorTagType tag in inputFormat.Info.ReadableSectorTags.OrderBy(t => t)) { if(!useLong || cancel) break; switch(tag) { case SectorTagType.AppleSectorTag: case SectorTagType.CdSectorSync: case SectorTagType.CdSectorHeader: case SectorTagType.CdSectorSubHeader: case SectorTagType.CdSectorEdc: case SectorTagType.CdSectorEccP: case SectorTagType.CdSectorEccQ: case SectorTagType.CdSectorEcc: // This tags are inline in long sector continue; } if(chkForce.Checked == true && !outputFormat.SupportedSectorTags.Contains(tag)) continue; foreach(Track track in tracks) { if(cancel) break; doneSectors = 0; ulong trackSectors = track.TrackEndSector - track.TrackStartSector + 1; byte[] sector; bool result; Application.Instance.Invoke(() => { lblProgress.Text = $"Converting tag {tag} in track {track.TrackSequence}."; prgProgress.Value++; lblProgress2.Text = ""; prgProgress2.Indeterminate = false; prgProgress2.MaxValue = (int)(trackSectors / numCount.Value); }); switch(tag) { case SectorTagType.CdTrackFlags: case SectorTagType.CdTrackIsrc: sector = inputFormat.ReadSectorTag(track.TrackStartSector, tag); result = outputFormat.WriteSectorTag(sector, track.TrackStartSector, tag); if(!result) if(chkForce.Checked == true) { warning = true; DicConsole.ErrorWriteLine("Error {0} writing tag, continuing...", outputFormat.ErrorMessage); } else { Application.Instance.Invoke(() => { MessageBox .Show($"Error {outputFormat.ErrorMessage} writing tag, not continuing...", MessageBoxType.Error); }); return; } continue; } while(doneSectors < trackSectors) { if(cancel) break; uint sectorsToDo; if(trackSectors - doneSectors >= (ulong)numCount.Value) sectorsToDo = (uint)numCount.Value; else sectorsToDo = (uint)(trackSectors - doneSectors); ulong sectors = doneSectors; Application.Instance.Invoke(() => { lblProgress2.Text = $"Converting tag {tag} for sectors {sectors + track.TrackStartSector} to {sectors + sectorsToDo + track.TrackStartSector} in track {track.TrackSequence} ({(sectors + track.TrackStartSector) / (double)inputFormat.Info.Sectors:P2} done)"; prgProgress2.Value = (int)(sectors / numCount.Value); }); if(sectorsToDo == 1) { sector = inputFormat.ReadSectorTag(doneSectors + track.TrackStartSector, tag); result = outputFormat.WriteSectorTag(sector, doneSectors + track.TrackStartSector, tag); } else { sector = inputFormat.ReadSectorsTag(doneSectors + track.TrackStartSector, sectorsToDo, tag); result = outputFormat.WriteSectorsTag(sector, doneSectors + track.TrackStartSector, sectorsToDo, tag); } if(!result) if(chkForce.Checked == true) { warning = true; DicConsole.ErrorWriteLine("Error {0} writing tag for sector {1}, continuing...", outputFormat.ErrorMessage, doneSectors); } else { Application.Instance.Invoke(() => { MessageBox .Show($"Error {outputFormat.ErrorMessage} writing tag for sector {doneSectors}, not continuing...", MessageBoxType.Error); }); return; } doneSectors += sectorsToDo; } } } } Application.Instance.Invoke(() => { lblProgress2.Visible = false; prgProgress2.Visible = false; }); bool ret = false; if(dumpHardware != null && !cancel) { Application.Instance.Invoke(() => { lblProgress.Text = "Writing dump hardware list to output image."; prgProgress.Value++; }); ret = outputFormat.SetDumpHardware(dumpHardware); if(!ret) DicConsole.WriteLine("Error {0} writing dump hardware list to output image.", outputFormat.ErrorMessage); } ret = false; if(cicmMetadata != null && !cancel) { Application.Instance.Invoke(() => { lblProgress.Text = "Writing CICM XML metadata to output image."; prgProgress.Value++; }); outputFormat.SetCicmMetadata(cicmMetadata); if(!ret) DicConsole.WriteLine("Error {0} writing CICM XML metadata to output image.", outputFormat.ErrorMessage); } Application.Instance.Invoke(() => { lblProgress.Text = "Closing output image."; prgProgress.Indeterminate = true; }); if(cancel) { Application.Instance.Invoke(() => { MessageBox.Show("Operation canceled, the output file is not correct.", MessageBoxType.Error); btnClose.Visible = true; btnStop.Visible = false; stkProgress.Visible = false; }); return; } if(!outputFormat.Close()) { Application.Instance.Invoke(() => { MessageBox .Show($"Error {outputFormat.ErrorMessage} closing output image... Contents are not correct.", MessageBoxType.Error); }); return; } Application.Instance.Invoke(() => { MessageBox.Show(warning ? "Some warnings happened. Check console for more information. Image should be correct." : "Image converted successfully."); btnClose.Visible = true; btnStop.Visible = false; stkProgress.Visible = false; }); Statistics.AddCommand("convert-image"); } protected void OnBtnClose(object sender, EventArgs e) { Close(); } protected void OnBtnStop(object sender, EventArgs e) { cancel = true; btnStop.Enabled = false; } 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; } chkForce.Visible = false; foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags) { if(plugin.SupportedMediaTags.Contains(mediaTag)) continue; chkForce.Visible = true; chkForce.Checked = true; break; } foreach(SectorTagType sectorTag in inputFormat.Info.ReadableSectorTags) { if(plugin.SupportedSectorTags.Contains(sectorTag)) continue; chkForce.Visible = true; chkForce.Checked = true; break; } grpOptions.Visible = true; StackLayout stkImageOptions = 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": CheckBox optBoolean = new CheckBox(); optBoolean.ID = "opt" + option.name; optBoolean.Text = option.description; optBoolean.Checked = (bool)option.@default; stkImageOptions.Items.Add(optBoolean); break; case "System.SByte": case "System.Int16": case "System.Int32": case "System.Int64": StackLayout stkNumber = new StackLayout(); stkNumber.Orientation = Orientation.Horizontal; NumericStepper optNumber = new NumericStepper(); optNumber.ID = "opt" + option.name; optNumber.Value = Convert.ToDouble(option.@default); stkNumber.Items.Add(optNumber); Label lblNumber = new Label(); lblNumber.Text = option.description; stkNumber.Items.Add(lblNumber); stkImageOptions.Items.Add(stkNumber); break; case "System.Byte": case "System.UInt16": case "System.UInt32": case "System.UInt64": StackLayout stkUnsigned = new StackLayout(); stkUnsigned.Orientation = Orientation.Horizontal; NumericStepper optUnsigned = new NumericStepper(); optUnsigned.ID = "opt" + option.name; optUnsigned.MinValue = 0; optUnsigned.Value = Convert.ToDouble(option.@default); stkUnsigned.Items.Add(optUnsigned); Label lblUnsigned = new Label(); lblUnsigned.Text = option.description; stkUnsigned.Items.Add(lblUnsigned); stkImageOptions.Items.Add(stkUnsigned); break; case "System.Single": case "System.Double": StackLayout stkFloat = new StackLayout(); stkFloat.Orientation = Orientation.Horizontal; NumericStepper optFloat = new NumericStepper(); optFloat.ID = "opt" + option.name; optFloat.DecimalPlaces = 2; optFloat.Value = Convert.ToDouble(option.@default); stkFloat.Items.Add(optFloat); Label lblFloat = new Label(); lblFloat.Text = option.description; stkFloat.Items.Add(lblFloat); stkImageOptions.Items.Add(stkFloat); break; case "System.Guid": // TODO break; case "System.String": StackLayout stkString = new StackLayout(); stkString.Orientation = Orientation.Horizontal; Label lblString = new Label(); lblString.Text = option.description; stkString.Items.Add(lblString); TextBox optString = new TextBox(); optString.ID = "opt" + option.name; optString.Text = (string)option.@default; stkString.Items.Add(optString); stkImageOptions.Items.Add(stkString); break; } grpOptions.Content = stkImageOptions; } void OnBtnDestinationClick(object sender, EventArgs e) { if(!(cmbFormat.SelectedValue is IWritableImage plugin)) return; SaveFileDialog 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 = ""; return; } if(string.IsNullOrEmpty(Path.GetExtension(dlgDestination.FileName))) dlgDestination.FileName += plugin.KnownExtensions.First(); txtDestination.Text = dlgDestination.FileName; } void OnBtnCreator(object sender, EventArgs e) { txtCreator.Text = inputFormat.Info.Creator; } void OnBtnMediaTitle(object sender, EventArgs e) { txtMediaTitle.Text = inputFormat.Info.MediaTitle; } void OnBtnComments(object sender, EventArgs e) { txtComments.Text = inputFormat.Info.Comments; } void OnBtnMediaManufacturer(object sender, EventArgs e) { txtMediaManufacturer.Text = inputFormat.Info.MediaManufacturer; } void OnBtnMediaModel(object sender, EventArgs e) { txtMediaModel.Text = inputFormat.Info.MediaModel; } void OnBtnMediaSerialNumber(object sender, EventArgs e) { txtMediaSerialNumber.Text = inputFormat.Info.MediaSerialNumber; } void OnBtnMediaBarcode(object sender, EventArgs e) { txtMediaBarcode.Text = inputFormat.Info.MediaBarcode; } void OnBtnMediaPartNumber(object sender, EventArgs e) { txtMediaPartNumber.Text = inputFormat.Info.MediaPartNumber; } void OnBtnMediaSequence(object sender, EventArgs e) { numMediaSequence.Value = inputFormat.Info.MediaSequence; } void OnBtnLastMediaSequence(object sender, EventArgs e) { numLastMediaSequence.Value = inputFormat.Info.LastMediaSequence; } void OnBtnDriveManufacturer(object sender, EventArgs e) { txtDriveManufacturer.Text = inputFormat.Info.DriveManufacturer; } void OnBtnDriveModel(object sender, EventArgs e) { txtDriveModel.Text = inputFormat.Info.DriveModel; } void OnBtnDriveSerialNumber(object sender, EventArgs e) { txtDriveSerialNumber.Text = inputFormat.Info.DriveSerialNumber; } void OnBtnDriveFirmwareRevision(object sender, EventArgs e) { txtDriveFirmwareRevision.Text = inputFormat.Info.DriveFirmwareRevision; } void OnBtnCicmXmlFromImageClick(object sender, EventArgs e) { txtCicmXml.Text = ""; cicmMetadata = inputFormat.CicmMetadata; } void OnBtnCicmXmlClick(object sender, EventArgs e) { cicmMetadata = null; txtCicmXml.Text = ""; OpenFileDialog 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) return; XmlSerializer sidecarXs = new XmlSerializer(typeof(CICMMetadataType)); try { StreamReader sr = new StreamReader(dlgMetadata.FileName); cicmMetadata = (CICMMetadataType)sidecarXs.Deserialize(sr); sr.Close(); txtCicmXml.Text = dlgMetadata.FileName; } catch { MessageBox.Show("Incorrect metadata sidecar file...", MessageBoxType.Error); } } void OnBtnResumeFileFromImageClick(object sender, EventArgs e) { txtResumeFile.Text = ""; dumpHardware = inputFormat.DumpHardware; } void OnBtnResumeFileClick(object sender, EventArgs e) { dumpHardware = null; txtResumeFile.Text = ""; OpenFileDialog dlgMetadata = new OpenFileDialog {Title = "Choose existing resume file", CheckFileExists = true}; dlgMetadata.Filters.Add(new FileFilter("CICM XML metadata", ".xml")); DialogResult result = dlgMetadata.ShowDialog(this); if(result != DialogResult.Ok) return; XmlSerializer sidecarXs = new XmlSerializer(typeof(Resume)); try { StreamReader sr = new StreamReader(dlgMetadata.FileName); Resume resume = (Resume)sidecarXs.Deserialize(sr); if(resume.Tries != null && !resume.Tries.Any()) { dumpHardware = resume.Tries; txtResumeFile.Text = dlgMetadata.FileName; } else MessageBox.Show("Resume file does not contain dump hardware information...", MessageBoxType.Error); sr.Close(); } catch { MessageBox.Show("Incorrect resume file...", MessageBoxType.Error); } } #region XAML IDs TextBox txtSource; ComboBox cmbFormat; TextBox txtDestination; Button btnDestination; StackLayout stkOptions; NumericStepper numCount; Label txtCount; CheckBox chkForce; Label lblCreator; TextBox txtCreator; Button btnCreator; GroupBox grpMetadata; Label lblMediaTitle; TextBox txtMediaTitle; Button btnMediaTitle; Label lblMediaManufacturer; TextBox txtMediaManufacturer; Button btnMediaManufacturer; Label lblMediaModel; TextBox txtMediaModel; Button btnMediaModel; Label lblMediaSerialNumber; TextBox txtMediaSerialNumber; Button btnMediaSerialNumber; Label lblMediaBarcode; TextBox txtMediaBarcode; Button btnMediaBarcode; Label lblMediaPartNumber; TextBox txtMediaPartNumber; Button btnMediaPartNumber; Label lblMediaSequence; NumericStepper numMediaSequence; Button btnMediaSequence; Label lblLastMediaSequence; NumericStepper numLastMediaSequence; Button btnLastMediaSequence; Label lblDriveManufacturer; TextBox txtDriveManufacturer; Button btnDriveManufacturer; Label lblDriveModel; TextBox txtDriveModel; Button btnDriveModel; Label lblDriveSerialNumber; TextBox txtDriveSerialNumber; Button btnDriveSerialNumber; Label lblDriveFirmwareRevision; TextBox txtDriveFirmwareRevision; Button btnDriveFirmwareRevision; TextArea txtComments; Button btnComments; TextBox txtCicmXml; Button btnCicmXmlFromImage; Button btnCicmXml; TextBox txtResumeFile; Button btnResumeFileFromImage; Button btnResumeFile; GroupBox grpOptions; StackLayout stkProgress; StackLayout stkProgress1; Label lblProgress; ProgressBar prgProgress; StackLayout stkProgress2; Label lblProgress2; ProgressBar prgProgress2; Button btnStart; Button btnClose; Button btnStop; #endregion } }