mirror of
https://github.com/SabreTools/MPF.git
synced 2026-07-02 17:24:48 +00:00
Goodbye drive speed finders (#101)
* Goodbye drive speed finders * More accurate scaling * Ensure disposal
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user