Goodbye drive speed finders (#101)

* Goodbye drive speed finders

* More accurate scaling

* Ensure disposal
This commit is contained in:
Matt Nadareski
2018-07-17 16:42:38 -07:00
committed by GitHub
parent 87ecc7588e
commit 94c0564fe8
3 changed files with 57 additions and 269 deletions

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows;
@@ -8,25 +9,12 @@ using WinForms = System.Windows.Forms;
using DICUI.Data;
using DICUI.Utilities;
using DICUI.UI;
using System.Runtime.InteropServices;
namespace DICUI
{
public partial class MainWindow : Window
{
[DllImport("gdi32.dll")]
public static extern int GetDeviceCaps(IntPtr hDc, int nIndex);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDc);
public const int LOGPIXELSX = 88;
public const int LOGPIXELSY = 90;
// Private UI-related variables
private List<Drive> _drives;
private MediaType? _currentMediaType;
@@ -42,27 +30,6 @@ namespace DICUI
private LogWindow _logWindow;
public void TransformToUnit(double pixelX,
double pixelY,
out int unitX,
out int unitY)
{
IntPtr hDc = GetDC(IntPtr.Zero);
if (hDc != IntPtr.Zero)
{
int dpiX = GetDeviceCaps(hDc, LOGPIXELSX);
int dpiY = GetDeviceCaps(hDc, LOGPIXELSY);
ReleaseDC(IntPtr.Zero, hDc);
unitX = (int)(pixelX / ((double)dpiX / 96));
unitY = (int)(pixelY / ((double)dpiY / 96));
}
else
throw new ArgumentNullException("Failed to get DC.");
}
public MainWindow()
{
InitializeComponent();
@@ -82,17 +49,13 @@ namespace DICUI
if (_options.OpenLogWindowAtStartup)
{
System.Drawing.Rectangle bounds = WinForms.Screen.PrimaryScreen.WorkingArea;
this.WindowStartupLocation = WindowStartupLocation.Manual;
double combinedHeight = this.Height + _logWindow.Height + UIElements.LogWindowMarginFromMainWindow;
TransformToUnit(bounds.Left, bounds.Top, out int unitLeft, out int unitTop);
TransformToUnit(bounds.Width, bounds.Height, out int unitWidth, out int unitHeight);
Rectangle bounds = GetScaledCoordinates(WinForms.Screen.PrimaryScreen.WorkingArea);
this.Left = unitLeft + (unitWidth - this.Width) / 2;
this.Top = unitTop + (unitHeight - combinedHeight) / 2;
this.Left = bounds.Left + (bounds.Width - this.Width) / 2;
this.Top = bounds.Top + (bounds.Height - combinedHeight) / 2;
}
}
#region Events
@@ -422,6 +385,11 @@ namespace DICUI
{
_env = DetermineEnvironment();
// Check for the firmware first
// TODO: Remove this (and method) once DIC end-to-end logging becomes a thing
if (!await _env.DriveHasLatestFimrware())
return;
StartStopButton.Content = UIElements.StopDumping;
CopyProtectScanButton.IsEnabled = false;
StatusLabel.Content = "Beginning dumping process";
@@ -549,36 +517,20 @@ namespace DICUI
/// <summary>
/// Set the drive speed based on reported maximum and user-defined option
/// </summary>
private async void SetSupportedDriveSpeed()
private void SetSupportedDriveSpeed()
{
// Set the drive speed list that's appropriate
var values = AllowedSpeeds.GetForMediaType(_currentMediaType);
// Get the current environment
_env = DetermineEnvironment();
// Get the drive speed
int speed = await _env.GetDiscSpeed();
// If we have an invalid speed, we need to jump out
if (speed == -1)
{
DriveSpeedComboBox.ItemsSource = values;
DriveSpeedComboBox.SelectedIndex = values.Count / 2;
return;
}
ViewModels.LoggerViewModel.VerboseLogLn("Determined max drive speed for {0} ({1}): {2}", _env.Drive.Letter, _currentMediaType.Name(), speed);
DriveSpeedComboBox.ItemsSource = values.Where(s => s <= speed);
ViewModels.LoggerViewModel.VerboseLogLn("Supported drive speeds: {0}", string.Join(",", values.Where(s => s <= speed)));
DriveSpeedComboBox.ItemsSource = values;
ViewModels.LoggerViewModel.VerboseLogLn("Supported media speeds: {0}", string.Join(",", values));
// Choose the lower of the two speeds between the allowed speeds and the user-defined one
int chosenSpeed = Math.Min(
values.Where(s => s <= speed).Last(),
values.Where(s => s <= values[values.Count / 2]).Last(),
_options.preferredDumpSpeedCD
);
// Set the selected speed
ViewModels.LoggerViewModel.VerboseLogLn("Setting drive speed to: {0}", chosenSpeed);
DriveSpeedComboBox.SelectedValue = chosenSpeed;
}
@@ -620,5 +572,37 @@ namespace DICUI
}
#endregion
#region UI Helpers
/// <summary>
/// Get pixel coordinates based on DPI scaling
/// </summary>
/// <param name="bounds">Rectangle representing the bounds to transform</param>
/// <returns>Rectangle representing the scaled bounds</returns>
private Rectangle GetScaledCoordinates(Rectangle bounds)
{
using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
{
return new Rectangle(
TransformCoordinate(bounds.Left, g.DpiX),
TransformCoordinate(bounds.Top, g.DpiY),
TransformCoordinate(bounds.Width, g.DpiX),
TransformCoordinate(bounds.Height, g.DpiY));
}
}
/// <summary>
/// Transform an individual coordinate using DPI scaling
/// </summary>
/// <param name="coord">Current integer coordinate</param>
/// <param name="dpi">DPI scaling factor</param>
/// <returns>Scaled integer coordinate</returns>
private int TransformCoordinate(int coord, float dpi)
{
return (int)(coord / ((double)dpi / 96));
}
#endregion
}
}

View File

@@ -125,32 +125,16 @@ namespace DICUI.Utilities
}
/// <summary>
/// Get disc speed using DIC
/// Gets if the current drive has the latest firmware
/// </summary>
/// <returns>Drive speed if possible, -1 on error</returns>
public async Task<int> GetDiscSpeed()
/// <returns></returns>
public async Task<bool> DriveHasLatestFimrware()
{
// Validate that the required program exists
if (!File.Exists(DICPath))
return -1;
return false;
// Validate that the drive is set up
if (Drive == null)
return -1;
// Validate we're not trying to get the speed for a floppy disk
if (IsFloppy)
return -1;
// Make sure that the current drive is active
if (!Drive.MarkedActive)
return -1;
// Get the drive speed directly
//int speed = Validators.GetDriveSpeed(Drive);
//int speed = Validators.GetDriveSpeedEx(Drive, _currentMediaType);
// Get the drive speed from DIC, if possible
// Use the drive speed command as a quick test
Process childProcess;
string output = await Task.Run(() =>
{
@@ -178,31 +162,15 @@ namespace DICUI.Utilities
return stdout;
});
// If a drive or argument was invalid, just exit
if (output.Contains("Invalid argument"))
{
return -1;
}
// If we get that the firmware is out of date, tell the user
else if (output.Contains("[ERROR] This drive isn't latest firmware. Please update."))
// If we get the firmware message
if (output.Contains("[ERROR] This drive isn't latest firmware. Please update."))
{
MessageBox.Show($"DiscImageCreator has reported that drive {Drive.Letter} is not updated to the most recent firmware. Please update the firmware for your drive and try again.", "Outdated Firmware", MessageBoxButton.OK, MessageBoxImage.Error);
return -1;
}
// Otherwise, if we find the maximum read speed as reported
else if (output.Contains("ReadSpeedMaximum:"))
{
int index = output.IndexOf("ReadSpeedMaximum:");
string readspeed = Regex.Match(output.Substring(index), @"ReadSpeedMaximum: [0-9]+KB/sec \(([0-9]*)x\)").Groups[1].Value;
if (!Int32.TryParse(readspeed, out int speed) || speed <= 0)
{
return -1;
}
return speed;
return false;
}
return -1;
// Otherwise, we know the firmware's good
return true;
}
/// <summary>

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using BurnOutSharp;
using IMAPI2;
@@ -513,169 +512,6 @@ namespace DICUI.Utilities
return null;
}
/// <summary>
/// Get the drive speed of the currently selected drive
/// </summary>
/// <returns>Speed of the drive converted from kbps</returns>
/// <remarks>
/// DIC uses the SCSI_MODE_SENSE command to check this, so does QPXTool (a different one, but still)
/// See if SCSI_MODE_SENSE can be used here
/// Currently, the calculations get something that is technically accurate, but is different than the advertisised
/// capabilities of the drives (according to QPXTool)
/// TransferRate appears to be the CURRENT transfer rate, not the maximum... basically making that flag useless
/// </remarks>
public static int GetDriveSpeed(Drive drive)
{
// If the current drive is not active or optical
if (drive.IsFloppy || !drive.MarkedActive)
return -1;
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_CDROMDrive WHERE Id = '" + drive.Letter + ":\'");
var collection = searcher.Get();
double? transferRate = -1;
foreach (ManagementObject queryObj in collection)
{
var obj = queryObj["TransferRate"];
transferRate = (double?)queryObj["TransferRate"];
}
// Transfer Rates (kBps)
double cdTransfer = 153.6;
double dvdTransfer = 1385;
double cdTransferTest = ((transferRate ?? -1)) / cdTransfer;
double dvdTransferTest = ((transferRate ?? -1)) / dvdTransfer;
return 0;
}
public unsafe static int GetDriveSpeedEx(Drive drive, MediaType? mediaType)
{
// If the current drive is not active or optical
if (drive.IsFloppy || !drive.MarkedActive)
return -1;
// Get the DeviceID from the current drive letter
string deviceId = null;
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_CDROMDrive WHERE Id = '" + drive.Letter + ":\'");
var collection = searcher.Get();
foreach (ManagementObject queryObj in collection)
{
deviceId = (string)queryObj["DeviceID"];
}
}
catch
{
// We don't care what the error was
return -1;
}
// If we got no valid device, we don't care and just return
if (deviceId == null)
{
return -1;
}
// Get all relevant disc information
MsftDiscMaster2 discMaster = new MsftDiscMaster2();
deviceId = deviceId.ToLower().Replace('\\', '#');
string id = null;
foreach (var disc in discMaster)
{
if (disc.ToString().Contains(deviceId))
{
id = disc.ToString();
}
}
// If we couldn't find the drive, we don't care and return
if (id == null)
{
return -1;
}
// Now we initialize the recorder to get disc info
MsftDiscRecorder2 recorder = new MsftDiscRecorder2();
recorder.InitializeDiscRecorder(id);
IDiscRecorder2Ex recorderEx = recorder as IDiscRecorder2Ex;
IMAPI_FEATURE_PAGE_TYPE ifpt = IMAPI_FEATURE_PAGE_TYPE.IMAPI_FEATURE_PAGE_TYPE_PROFILE_LIST;
switch(mediaType)
{
case MediaType.CD:
ifpt = IMAPI_FEATURE_PAGE_TYPE.IMAPI_FEATURE_PAGE_TYPE_CD_READ;
break;
case MediaType.DVD:
ifpt = IMAPI_FEATURE_PAGE_TYPE.IMAPI_FEATURE_PAGE_TYPE_DVD_READ;
break;
case MediaType.HDDVD:
ifpt = IMAPI_FEATURE_PAGE_TYPE.IMAPI_FEATURE_PAGE_TYPE_HD_DVD_READ;
break;
case MediaType.BluRay:
ifpt = IMAPI_FEATURE_PAGE_TYPE.IMAPI_FEATURE_PAGE_TYPE_BD_READ;
break;
}
// If we couldn't determine the media type properly, we don't care and return
if (ifpt == IMAPI_FEATURE_PAGE_TYPE.IMAPI_FEATURE_PAGE_TYPE_PROFILE_LIST)
{
return -1;
}
// Ones that haven't worked:
// recorderEx.GetAdapterDescriptor
// recorderEx.GetDeviceDescriptor
// recorderEx.GetDiscInformation
// recorderEx.GetTrackInformation
// Now we get the requested feature page
// TODO: Figure out structure of returned data
IntPtr featureData = Marshal.AllocHGlobal(32 * sizeof(byte));
recorderEx.GetFeaturePage(
ifpt,
(sbyte)0,
featureData,
out uint byteSize);
byte[] outFeatureArray = new byte[byteSize];
Marshal.Copy(featureData, outFeatureArray, 0, (int)byteSize);
// Now we get the requested mode data
// TODO: Figure out structure of returned data
IntPtr modeData = Marshal.AllocHGlobal(256 * sizeof(byte));
recorderEx.GetModePage(
(IMAPI_MODE_PAGE_TYPE)0x2A,
IMAPI_MODE_PAGE_REQUEST_TYPE.IMAPI_MODE_PAGE_REQUEST_TYPE_CURRENT_VALUES,
modeData,
out uint modeDataSize);
byte[] outModeArray = new byte[modeDataSize];
Marshal.Copy(modeData, outModeArray, 0, (int)modeDataSize);
// Now we send the command to get sense data from the device
// TODO: This seems like the best option, but how is this data structured properly?
byte[] cdbArray = new byte[] { 0x5a, 0x0, 0x2a, 0x00, 0xff, 0x0 };
byte[] senseBuffer = new byte[256];
byte[] buffer = new byte[256];
uint bufferSize = 256;
recorderEx.SendCommandGetDataFromDevice(
ref cdbArray[0],
(uint)6,
senseBuffer,
(uint)60,
out buffer[0],
bufferSize,
out uint BufferFetched);
return -1;
}
/// <summary>
/// Verify that, given a system and a media type, they are correct
/// </summary>