Allow nullability for modern .NET

This commit is contained in:
Matt Nadareski
2023-10-07 01:02:21 -04:00
parent 058a1aeeaa
commit 19cef20ceb
26 changed files with 2312 additions and 625 deletions

View File

@@ -22,6 +22,8 @@
- Be smarter about media type based on system
- Consolidate into MPF.Core
- Fix failing tests
- Remove debug symbols in release builds (Deterous)
- Allow nullability for modern .NET
### 2.6.6 (2023-10-04)

View File

@@ -38,7 +38,11 @@ namespace MPF.Core.Converters
/// <summary>
/// Long name method cache
/// </summary>
#if NET48
private static readonly ConcurrentDictionary<Type, MethodInfo> LongNameMethods = new ConcurrentDictionary<Type, MethodInfo>();
#else
private static readonly ConcurrentDictionary<Type, MethodInfo?> LongNameMethods = new ConcurrentDictionary<Type, MethodInfo?>();
#endif
/// <summary>
/// Get the string representation of a generic enumerable value
@@ -49,12 +53,12 @@ namespace MPF.Core.Converters
{
try
{
var sourceType = value?.GetType();
var sourceType = value.GetType();
sourceType = Nullable.GetUnderlyingType(sourceType) ?? sourceType;
if (!LongNameMethods.TryGetValue(sourceType, out MethodInfo method))
if (!LongNameMethods.TryGetValue(sourceType, out var method))
{
method = typeof(SabreTools.RedumpLib.Data.Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
method = typeof(Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
if (method == null)
method = typeof(EnumConverter).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
@@ -62,7 +66,7 @@ namespace MPF.Core.Converters
}
if (method != null)
return method.Invoke(null, new[] { value }) as string;
return method.Invoke(null, new[] { value }) as string ?? string.Empty;
else
return string.Empty;
}
@@ -112,7 +116,7 @@ namespace MPF.Core.Converters
}
}
#endregion
#endregion
#region Convert From String
@@ -121,9 +125,13 @@ namespace MPF.Core.Converters
/// </summary>
/// <param name="internalProgram">String value to convert</param>
/// <returns>InternalProgram represented by the string, if possible</returns>
#if NET48
public static InternalProgram ToInternalProgram(string internalProgram)
#else
public static InternalProgram ToInternalProgram(string? internalProgram)
#endif
{
switch (internalProgram.ToLowerInvariant())
switch (internalProgram?.ToLowerInvariant())
{
// Dumping support
case "aaru":

View File

@@ -29,12 +29,20 @@ namespace MPF.Core.Data
/// <summary>
/// Drive partition format
/// </summary>
#if NET48
public string DriveFormat { get; private set; } = null;
#else
public string? DriveFormat { get; private set; } = null;
#endif
/// <summary>
/// Windows drive path
/// </summary>
#if NET48
public string Name { get; private set; } = null;
#else
public string? Name { get; private set; } = null;
#endif
/// <summary>
/// Represents if Windows has marked the drive as active
@@ -50,7 +58,11 @@ namespace MPF.Core.Data
/// Media label as read by Windows
/// </summary>
/// <remarks>The try/catch is needed because Windows will throw an exception if the drive is not marked as active</remarks>
#if NET48
public string VolumeLabel { get; private set; } = null;
#else
public string? VolumeLabel { get; private set; } = null;
#endif
#endregion
@@ -59,7 +71,11 @@ namespace MPF.Core.Data
/// <summary>
/// Media label as read by Windows, formatted to avoid odd outputs
/// </summary>
#if NET48
public string FormattedVolumeLabel
#else
public string? FormattedVolumeLabel
#endif
{
get
{
@@ -96,7 +112,11 @@ namespace MPF.Core.Data
/// </summary>
/// <param name="driveType">InternalDriveType value representing the drive type</param>
/// <param name="devicePath">Path to the device according to the local machine</param>
#if NET48
public static Drive Create(InternalDriveType? driveType, string devicePath)
#else
public static Drive? Create(InternalDriveType? driveType, string devicePath)
#endif
{
// Create a new, empty drive object
var drive = new Drive()
@@ -131,7 +151,11 @@ namespace MPF.Core.Data
/// Populate all fields from a DriveInfo object
/// </summary>
/// <param name="driveInfo">DriveInfo object to populate from</param>
#if NET48
private void PopulateFromDriveInfo(DriveInfo driveInfo)
#else
private void PopulateFromDriveInfo(DriveInfo? driveInfo)
#endif
{
// If we have an invalid DriveInfo, just return
if (driveInfo == null || driveInfo == default)
@@ -161,10 +185,14 @@ namespace MPF.Core.Data
/// </summary>
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
/// <returns>Active drives, matched to labels, if possible</returns>
#if NET48
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
#else
public static List<Drive?> CreateListOfDrives(bool ignoreFixedDrives)
#endif
{
var drives = GetDriveList(ignoreFixedDrives);
drives = drives?.OrderBy(i => i.Letter)?.ToList();
drives = drives.OrderBy(i => i == null ? '\0' : i.Letter).ToList();
return drives;
}
@@ -173,7 +201,11 @@ namespace MPF.Core.Data
/// </summary>
/// <param name="system"></param>
/// <returns></returns>
#if NET48
public (MediaType?, string) GetMediaType(RedumpSystem? system)
#else
public (MediaType?, string?) GetMediaType(RedumpSystem? system)
#endif
{
// Take care of the non-optical stuff first
switch (this.InternalDriveType)
@@ -336,7 +368,7 @@ namespace MPF.Core.Data
// Sega Saturn
try
{
byte[] sector = ReadSector(0);
var sector = ReadSector(0);
if (sector != null)
{
if (sector.StartsWith(Interface.SaturnSectorZeroStart))
@@ -520,7 +552,11 @@ namespace MPF.Core.Data
/// <param name="num">Sector number, non-negative</param>
/// <param name="size">Size of a sector in bytes</param>
/// <returns>Byte array representing the sector, null on error</returns>
#if NET48
public byte[] ReadSector(long num, int size = 2048)
#else
public byte[]? ReadSector(long num, int size = 2048)
#endif
{
// Missing drive leter is not supported
if (string.IsNullOrEmpty(this.Name))
@@ -531,7 +567,11 @@ namespace MPF.Core.Data
return null;
// Wrap the following in case of device access errors
#if NET48
Stream fs = null;
#else
Stream? fs = null;
#endif
try
{
// Open the drive as a device
@@ -578,7 +618,11 @@ namespace MPF.Core.Data
/// https://stackoverflow.com/questions/3060796/how-to-distinguish-between-usb-and-floppy-devices?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
/// https://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx
/// </remarks>
#if NET48
private static List<Drive> GetDriveList(bool ignoreFixedDrives)
#else
private static List<Drive?> GetDriveList(bool ignoreFixedDrives)
#endif
{
var desiredDriveTypes = new List<DriveType>() { DriveType.CDRom };
if (!ignoreFixedDrives)
@@ -591,7 +635,11 @@ namespace MPF.Core.Data
// https://github.com/aaru-dps/Aaru/blob/5164a154e2145941472f2ee0aeb2eff3338ecbb3/Aaru.Devices/Windows/ListDevices.cs#L66
// Create an output drive list
#if NET48
var drives = new List<Drive>();
#else
var drives = new List<Drive?>();
#endif
// Get all standard supported drive types
try
@@ -618,8 +666,8 @@ namespace MPF.Core.Data
uint? mediaType = properties["MediaType"]?.Value as uint?;
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
{
char devId = (properties["Caption"].Value as string)[0];
drives.ForEach(d => { if (d.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
char devId = (properties["Caption"].Value as string ?? string.Empty)[0];
drives.ForEach(d => { if (d?.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
}
}
}

View File

@@ -21,7 +21,7 @@ namespace MPF.Core.Data
if (_keyValuePairs.ContainsKey(key))
return _keyValuePairs[key];
return null;
return string.Empty;
}
set
{
@@ -104,10 +104,16 @@ namespace MPF.Core.Data
string section = string.Empty;
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
var line = sr.ReadLine()?.Trim();
// Empty lines are skipped
if (string.IsNullOrWhiteSpace(line))
{
// No-op, we don't process empty lines
}
// Comments start with ';'
if (line.StartsWith(";"))
else if (line.StartsWith(";"))
{
// No-op, we don't process comments
}
@@ -245,7 +251,9 @@ namespace MPF.Core.Data
public bool TryGetValue(string key, out string value)
{
return ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out value);
bool result = ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out var temp);
value = temp ?? string.Empty;
return result;
}
public void Add(KeyValuePair<string, string> item)

View File

@@ -1,24 +1,34 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using MPF.Core.Converters;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
public class Options : IDictionary<string, string>
#if NET48
public class Options
#else
public class Options
#endif
{
/// <summary>
/// All settings in the form of a dictionary
/// </summary>
#if NET48
public Dictionary<string, string> Settings { get; private set; }
#else
public Dictionary<string, string?> Settings { get; private set; }
#endif
#region Internal Program
/// <summary>
/// Path to Aaru
/// </summary>
#if NET48
public string AaruPath
#else
public string? AaruPath
#endif
{
get { return GetStringSetting(Settings, "AaruPath", "Programs\\Aaru\\Aaru.exe"); }
set { Settings["AaruPath"] = value; }
@@ -27,7 +37,11 @@ namespace MPF.Core.Data
/// <summary>
/// Path to DiscImageCreator
/// </summary>
#if NET48
public string DiscImageCreatorPath
#else
public string? DiscImageCreatorPath
#endif
{
get { return GetStringSetting(Settings, "DiscImageCreatorPath", "Programs\\Creator\\DiscImageCreator.exe"); }
set { Settings["DiscImageCreatorPath"] = value; }
@@ -36,7 +50,11 @@ namespace MPF.Core.Data
/// <summary>
/// Path to Redumper
/// </summary>
#if NET48
public string RedumperPath
#else
public string? RedumperPath
#endif
{
get { return GetStringSetting(Settings, "RedumperPath", "Programs\\Redumper\\redumper.exe"); }
set { Settings["RedumperPath"] = value; }
@@ -49,7 +67,7 @@ namespace MPF.Core.Data
{
get
{
string valueString = GetStringSetting(Settings, "InternalProgram", InternalProgram.DiscImageCreator.ToString());
var valueString = GetStringSetting(Settings, "InternalProgram", InternalProgram.DiscImageCreator.ToString());
var valueEnum = EnumConverter.ToInternalProgram(valueString);
return valueEnum == InternalProgram.NONE ? InternalProgram.DiscImageCreator : valueEnum;
}
@@ -93,7 +111,11 @@ namespace MPF.Core.Data
/// <summary>
/// Default output path for dumps
/// </summary>
#if NET48
public string DefaultOutputPath
#else
public string? DefaultOutputPath
#endif
{
get { return GetStringSetting(Settings, "DefaultOutputPath", "ISO"); }
set { Settings["DefaultOutputPath"] = value; }
@@ -106,8 +128,8 @@ namespace MPF.Core.Data
{
get
{
string valueString = GetStringSetting(Settings, "DefaultSystem", null);
var valueEnum = Extensions.ToRedumpSystem(valueString);
var valueString = GetStringSetting(Settings, "DefaultSystem", null);
var valueEnum = Extensions.ToRedumpSystem(valueString ?? string.Empty);
return valueEnum;
}
set
@@ -536,16 +558,27 @@ namespace MPF.Core.Data
#region Redump Login Information
#if NET48
public string RedumpUsername
#else
public string? RedumpUsername
#endif
{
get { return GetStringSetting(Settings, "RedumpUsername", ""); }
set { Settings["RedumpUsername"] = value; }
}
// TODO: Figure out a way to keep this encrypted in some way, BASE64 to start?
#if NET48
public string RedumpPassword
#else
public string? RedumpPassword
#endif
{
get { return GetStringSetting(Settings, "RedumpPassword", ""); }
get
{
return GetStringSetting(Settings, "RedumpPassword", "");
}
set { Settings["RedumpPassword"] = value; }
}
@@ -560,9 +593,17 @@ namespace MPF.Core.Data
/// Constructor taking a dictionary for settings
/// </summary>
/// <param name="settings"></param>
#if NET48
public Options(Dictionary<string, string> settings = null)
#else
public Options(Dictionary<string, string?>? settings = null)
#endif
{
#if NET48
this.Settings = settings ?? new Dictionary<string, string>();
#else
this.Settings = settings ?? new Dictionary<string, string?>();
#endif
}
/// <summary>
@@ -571,7 +612,24 @@ namespace MPF.Core.Data
/// <param name="source"></param>
public Options(Options source)
{
#if NET48
Settings = new Dictionary<string, string>(source.Settings);
#else
Settings = new Dictionary<string, string?>(source.Settings);
#endif
}
/// <summary>
/// Accessor for the internal dictionary
/// </summary>
#if NET48
public string this[string key]
#else
public string? this[string key]
#endif
{
get => this.Settings[key];
set => this.Settings[key] = value;
}
#region Helpers
@@ -583,11 +641,15 @@ namespace MPF.Core.Data
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
#if NET48
private bool GetBooleanSetting(Dictionary<string, string> settings, string key, bool defaultValue)
#else
private bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
#endif
{
if (settings.ContainsKey(key))
{
if (Boolean.TryParse(settings[key], out bool value))
if (bool.TryParse(settings[key], out bool value))
return value;
else
return defaultValue;
@@ -605,11 +667,15 @@ namespace MPF.Core.Data
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
#if NET48
private int GetInt32Setting(Dictionary<string, string> settings, string key, int defaultValue)
#else
private int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
#endif
{
if (settings.ContainsKey(key))
{
if (Int32.TryParse(settings[key], out int value))
if (int.TryParse(settings[key], out int value))
return value;
else
return defaultValue;
@@ -627,7 +693,11 @@ namespace MPF.Core.Data
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
#if NET48
private string GetStringSetting(Dictionary<string, string> settings, string key, string defaultValue)
#else
private string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
#endif
{
if (settings.ContainsKey(key))
return settings[key];
@@ -636,45 +706,5 @@ namespace MPF.Core.Data
}
#endregion
#region IDictionary implementations
public ICollection<string> Keys => Settings.Keys;
public ICollection<string> Values => Settings.Values;
public int Count => Settings.Count;
public bool IsReadOnly => ((IDictionary<string, string>)Settings).IsReadOnly;
public string this[string key]
{
get { return (Settings.ContainsKey(key) ? Settings[key] : null); }
set { Settings[key] = value; }
}
public bool ContainsKey(string key) => Settings.ContainsKey(key);
public void Add(string key, string value) => Settings.Add(key, value);
public bool Remove(string key) => Settings.Remove(key);
public bool TryGetValue(string key, out string value) => Settings.TryGetValue(key, out value);
public void Add(KeyValuePair<string, string> item) => Settings.Add(item.Key, item.Value);
public void Clear() => Settings.Clear();
public bool Contains(KeyValuePair<string, string> item) => ((IDictionary<string, string>)Settings).Contains(item);
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) => ((IDictionary<string, string>)Settings).CopyTo(array, arrayIndex);
public bool Remove(KeyValuePair<string, string> item) => ((IDictionary<string, string>)Settings).Remove(item);
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() => Settings.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => Settings.GetEnumerator();
#endregion
}
}

View File

@@ -42,7 +42,7 @@ namespace MPF.Core.Data
public void Enqueue(T item)
{
// Only accept new data when not cancelled
if (!this.TokenSource.IsCancellationRequested)
if (item != null && !this.TokenSource.IsCancellationRequested)
this.InternalQueue.Enqueue(item);
}
@@ -64,7 +64,7 @@ namespace MPF.Core.Data
}
// Get the next item from the queue
if (!this.InternalQueue.TryDequeue(out T nextItem))
if (!this.InternalQueue.TryDequeue(out var nextItem))
continue;
// Invoke the lambda, if possible

View File

@@ -30,7 +30,11 @@
/// Create a success result with a custom message
/// </summary>
/// <param name="message">String to add as a message</param>
#if NET48
public static Result Success(string message) => new Result(true, message);
#else
public static Result Success(string? message) => new Result(true, message ?? string.Empty);
#endif
/// <summary>
/// Create a default failure result with no message
@@ -42,7 +46,11 @@
/// Create a failure result with a custom message
/// </summary>
/// <param name="message">String to add as a message</param>
#if NET48
public static Result Failure(string message) => new Result(false, message);
#else
public static Result Failure(string? message) => new Result(false, message ?? string.Empty);
#endif
/// <summary>
/// Results can be compared to boolean values based on the success value

View File

@@ -17,17 +17,29 @@ namespace MPF.Core.Data
/// <summary>
/// Raw XMID/XeMID string that all other information is derived from
/// </summary>
#if NET48
public string RawXMID { get; private set; }
#else
public string? RawXMID { get; private set; }
#endif
/// <summary>
/// XGD1 XMID
/// </summary>
#if NET48
public SabreTools.Models.Xbox.XMID XMID { get; private set; }
#else
public SabreTools.Models.Xbox.XMID? XMID { get; private set; }
#endif
/// <summary>
/// XGD2/3 XeMID
/// </summary>
#if NET48
public SabreTools.Models.Xbox.XeMID XeMID { get; private set; }
#else
public SabreTools.Models.Xbox.XeMID? XeMID { get; private set; }
#endif
#endregion
@@ -58,7 +70,11 @@ namespace MPF.Core.Data
/// Get the human-readable serial string
/// </summary>
/// <returns>Formatted serial string, null on error</returns>
#if NET48
public string GetSerial()
#else
public string? GetSerial()
#endif
{
if (!this.Initialized)
return null;
@@ -86,7 +102,11 @@ namespace MPF.Core.Data
/// </summary>
/// <returns>Formatted version string, null on error</returns>
/// <remarks>This may differ for XGD2/3 in the future</remarks>
#if NET48
public string GetVersion()
#else
public string? GetVersion()
#endif
{
if (!this.Initialized)
return null;
@@ -160,7 +180,7 @@ namespace MPF.Core.Data
/// </summary>
/// <param name="region">Character denoting the region</param>
/// <returns>Region, if possible</returns>
public static Region? GetRegion(char region)
public static Region? GetRegion(char? region)
{
switch (region)
{

View File

@@ -51,36 +51,52 @@ namespace MPF.Core
/// <summary>
/// Options object representing user-defined options
/// </summary>
public Core.Data.Options Options { get; private set; }
public Data.Options Options { get; private set; }
/// <summary>
/// Parameters object representing what to send to the internal program
/// </summary>
#if NET48
public BaseParameters Parameters { get; private set; }
#else
public BaseParameters? Parameters { get; private set; }
#endif
#endregion
#region Event Handlers
/// <summary>
/// Generic way of reporting a message
/// </summary>
#if NET48
public EventHandler<string> ReportStatus;
#else
public EventHandler<string>? ReportStatus;
#endif
/// <summary>
/// Queue of items that need to be logged
/// </summary>
#if NET48
private ProcessingQueue<string> outputQueue;
#else
private ProcessingQueue<string>? outputQueue;
#endif
/// <summary>
/// Event handler for data returned from a process
/// </summary>
private void OutputToLog(object proc, string args) => outputQueue.Enqueue(args);
#if NET48
private void OutputToLog(object proc, string args) => outputQueue?.Enqueue(args);
#else
private void OutputToLog(object? proc, string args) => outputQueue?.Enqueue(args);
#endif
/// <summary>
/// Process the outputs in the queue
/// </summary>
private void ProcessOutputs(string nextOutput) => ReportStatus.Invoke(this, nextOutput);
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, nextOutput);
#endregion
@@ -94,7 +110,7 @@ namespace MPF.Core
/// <param name="type"></param>
/// <param name="internalProgram"></param>
/// <param name="parameters"></param>
public DumpEnvironment(Core.Data.Options options,
public DumpEnvironment(Data.Options options,
string outputPath,
Drive drive,
RedumpSystem? system,
@@ -113,7 +129,7 @@ namespace MPF.Core
this.System = system ?? options.DefaultSystem;
this.Type = type ?? MediaType.NONE;
this.InternalProgram = internalProgram ?? options.InternalProgram;
// Dumping program
SetParameters(parameters);
}
@@ -126,7 +142,7 @@ namespace MPF.Core
public void AdjustPathsForDiscImageCreator()
{
// Only DiscImageCreator has issues with paths
if (this.Parameters.InternalProgram != InternalProgram.DiscImageCreator)
if (this.Parameters?.InternalProgram != InternalProgram.DiscImageCreator)
return;
try
@@ -135,8 +151,8 @@ namespace MPF.Core
string outputPath = InfoTool.NormalizeOutputPaths(this.OutputPath, true);
// Replace all instances in the output directory
string outputDirectory = Path.GetDirectoryName(outputPath);
outputDirectory = outputDirectory.Replace(".", "_");
var outputDirectory = Path.GetDirectoryName(outputPath);
outputDirectory = outputDirectory?.Replace(".", "_");
// Replace all instances in the output filename
string outputFilename = Path.GetFileNameWithoutExtension(outputPath);
@@ -146,10 +162,20 @@ namespace MPF.Core
string outputExtension = Path.GetExtension(outputPath).TrimStart('.');
// Rebuild the output path
if (!string.IsNullOrWhiteSpace(outputExtension))
this.OutputPath = Path.Combine(outputDirectory, $"{outputFilename}.{outputExtension}");
if (string.IsNullOrWhiteSpace(outputDirectory))
{
if (string.IsNullOrWhiteSpace(outputExtension))
this.OutputPath = outputFilename;
else
this.OutputPath = $"{outputFilename}.{outputExtension}";
}
else
this.OutputPath = Path.Combine(outputDirectory, outputFilename);
{
if (string.IsNullOrWhiteSpace(outputExtension))
this.OutputPath = Path.Combine(outputDirectory, outputFilename);
else
this.OutputPath = Path.Combine(outputDirectory, $"{outputFilename}.{outputExtension}");
}
// Assign the path to the filename as well for dumping
((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = this.OutputPath;
@@ -198,8 +224,11 @@ namespace MPF.Core
}
// Set system and type
this.Parameters.System = this.System;
this.Parameters.Type = this.Type;
if (this.Parameters != null)
{
this.Parameters.System = this.System;
this.Parameters.Type = this.Type;
}
}
/// <summary>
@@ -207,7 +236,11 @@ namespace MPF.Core
/// </summary>
/// <param name="driveSpeed">Nullable int representing the drive speed</param>
/// <returns>String representing the params, null on error</returns>
#if NET48
public string GetFullParameters(int? driveSpeed)
#else
public string? GetFullParameters(int? driveSpeed)
#endif
{
// Populate with the correct params for inputs (if we're not on the default option)
if (System != null && Type != MediaType.NONE)
@@ -251,26 +284,42 @@ namespace MPF.Core
/// <summary>
/// Cancel an in-progress dumping process
/// </summary>
public void CancelDumping() => Parameters.KillInternalProgram();
public void CancelDumping() => Parameters?.KillInternalProgram();
/// <summary>
/// Eject the disc using DiscImageCreator
/// </summary>
#if NET48
public async Task<string> EjectDisc() =>
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject);
#else
public async Task<string?> EjectDisc() =>
#endif
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject);
/// <summary>
/// Reset the current drive using DiscImageCreator
/// </summary>
#if NET48
public async Task<string> ResetDrive() =>
#else
public async Task<string?> ResetDrive() =>
#endif
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Reset);
/// <summary>
/// Execute the initial invocation of the dumping programs
/// </summary>
/// <param name="progress">Optional result progress callback</param>
#if NET48
public async Task<Result> Run(IProgress<Result> progress = null)
#else
public async Task<Result> Run(IProgress<Result>? progress = null)
#endif
{
// If we don't have parameters
if (this.Parameters == null)
return Result.Failure("Error! Current configuration is not supported!");
// Check that we have the basics for dumping
Result result = IsValidForDump();
if (!result)
@@ -280,12 +329,17 @@ namespace MPF.Core
if (!Options.ToolsInSeparateWindow)
{
outputQueue = new ProcessingQueue<string>(ProcessOutputs);
Parameters.ReportStatus += OutputToLog;
if (Parameters.ReportStatus != null)
Parameters.ReportStatus += OutputToLog;
}
// Execute internal tool
progress?.Report(Result.Success($"Executing {this.InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
var directoryName = Path.GetDirectoryName(this.OutputPath);
if (!string.IsNullOrWhiteSpace(directoryName))
Directory.CreateDirectory(directoryName);
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
progress?.Report(Result.Success($"{this.InternalProgram} has finished!"));
@@ -297,7 +351,7 @@ namespace MPF.Core
// Remove event handler if needed
if (!Options.ToolsInSeparateWindow)
{
outputQueue.Dispose();
outputQueue?.Dispose();
Parameters.ReportStatus -= OutputToLog;
}
@@ -312,15 +366,21 @@ namespace MPF.Core
/// <param name="processUserInfo">Optional user prompt to deal with submission information</param>
/// <returns>Result instance with the outcome</returns>
public async Task<Result> VerifyAndSaveDumpOutput(
#if NET48
IProgress<Result> resultProgress = null,
IProgress<ProtectionProgress> protectionProgress = null,
Func<SubmissionInfo, (bool?, SubmissionInfo)> processUserInfo = null)
#else
IProgress<Result>? resultProgress = null,
IProgress<ProtectionProgress>? protectionProgress = null,
Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? processUserInfo = null)
#endif
{
resultProgress?.Report(Result.Success("Gathering submission information... please wait!"));
// Get the output directory and filename separately
string outputDirectory = Path.GetDirectoryName(this.OutputPath);
string outputFilename = Path.GetFileName(this.OutputPath);
var outputDirectory = Path.GetDirectoryName(this.OutputPath);
var outputFilename = Path.GetFileName(this.OutputPath);
// Check to make sure that the output had all the correct files
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, this.Parameters, false);
@@ -332,7 +392,7 @@ namespace MPF.Core
// Extract the information from the output files
resultProgress?.Report(Result.Success("Extracting output information from output files..."));
SubmissionInfo submissionInfo = await InfoTool.ExtractOutputInformation(
var submissionInfo = await InfoTool.ExtractOutputInformation(
this.OutputPath,
this.Drive,
this.System,
@@ -378,7 +438,7 @@ namespace MPF.Core
// Format the information for the text output
resultProgress?.Report(Result.Success("Formatting information..."));
(List<string> formattedValues, string formatResult) = InfoTool.FormatOutputData(submissionInfo, this.Options);
(var formattedValues, var formatResult) = InfoTool.FormatOutputData(submissionInfo, this.Options);
if (formattedValues == null)
resultProgress?.Report(Result.Success(formatResult));
else
@@ -435,7 +495,7 @@ namespace MPF.Core
/// <returns>True if the configuration is valid, false otherwise</returns>
internal bool ParametersValid()
{
bool parametersValid = Parameters.IsValid();
bool parametersValid = Parameters?.IsValid() ?? false;
bool floppyValid = !(Drive.InternalDriveType == InternalDriveType.Floppy ^ Type == MediaType.FloppyDisk);
// TODO: HardDisk being in the Removable category is a hack, fix this later
@@ -495,7 +555,7 @@ namespace MPF.Core
private Result IsValidForDump()
{
// Validate that everything is good
if (!ParametersValid())
if (this.Parameters == null || !ParametersValid())
return Result.Failure("Error! Current configuration is not supported!");
// Fix the output paths, just in case
@@ -540,7 +600,11 @@ namespace MPF.Core
/// </summary>
/// <param name="command">Command string to run</param>
/// <returns>The output of the command on success, null on error</returns>
#if NET48
private async Task<string> RunStandaloneDiscImageCreatorCommand(string command)
#else
private async Task<string?> RunStandaloneDiscImageCreatorCommand(string command)
#endif
{
// Validate that DiscImageCreator is all set
if (!RequiredProgramsExist())
@@ -562,6 +626,6 @@ namespace MPF.Core
return await ExecuteInternalProgram(parameters);
}
#endregion
#endregion
}
}

View File

@@ -29,7 +29,12 @@ namespace MPF.Core.Hashing
public class Hasher
{
public Hash HashType { get; private set; }
private object _hasher;
#if NET48
private object _hasher;
#else
private object? _hasher;
#endif
public Hasher(Hash hashType)
{
@@ -84,7 +89,7 @@ namespace MPF.Core.Hashing
switch (HashType)
{
case Hash.CRC32:
(_hasher as NonCryptographicHashAlgorithm).Append(buffer);
(_hasher as NonCryptographicHashAlgorithm)?.Append(buffer);
break;
case Hash.MD5:
@@ -92,7 +97,7 @@ namespace MPF.Core.Hashing
case Hash.SHA256:
case Hash.SHA384:
case Hash.SHA512:
(_hasher as HashAlgorithm).TransformBlock(buffer, 0, size, null, 0);
(_hasher as HashAlgorithm)?.TransformBlock(buffer, 0, size, null, 0);
break;
}
}
@@ -106,7 +111,7 @@ namespace MPF.Core.Hashing
switch (HashType)
{
case Hash.CRC32:
(_hasher as NonCryptographicHashAlgorithm).Append(emptyBuffer);
(_hasher as NonCryptographicHashAlgorithm)?.Append(emptyBuffer);
break;
case Hash.MD5:
@@ -114,7 +119,7 @@ namespace MPF.Core.Hashing
case Hash.SHA256:
case Hash.SHA384:
case Hash.SHA512:
(_hasher as HashAlgorithm).TransformFinalBlock(emptyBuffer, 0, 0);
(_hasher as HashAlgorithm)?.TransformFinalBlock(emptyBuffer, 0, 0);
break;
}
}
@@ -122,19 +127,26 @@ namespace MPF.Core.Hashing
/// <summary>
/// Get internal hash as a byte array
/// </summary>
#if NET48
public byte[] GetHash()
#else
public byte[]? GetHash()
#endif
{
if (_hasher == null)
return null;
switch (HashType)
{
case Hash.CRC32:
return (_hasher as NonCryptographicHashAlgorithm).GetCurrentHash();
return (_hasher as NonCryptographicHashAlgorithm)?.GetCurrentHash();
case Hash.MD5:
case Hash.SHA1:
case Hash.SHA256:
case Hash.SHA384:
case Hash.SHA512:
return (_hasher as HashAlgorithm).Hash;
return (_hasher as HashAlgorithm)?.Hash;
}
return null;
@@ -143,12 +155,16 @@ namespace MPF.Core.Hashing
/// <summary>
/// Get internal hash as a string
/// </summary>
#if NET48
public string GetHashString()
#else
public string? GetHashString()
#endif
{
byte[] hash = GetHash();
var hash = GetHash();
if (hash == null)
return null;
return ByteArrayToString(hash);
}
@@ -158,7 +174,11 @@ namespace MPF.Core.Hashing
/// <param name="bytes">Byte array to convert</param>
/// <returns>Hex string representing the byte array</returns>
/// <link>http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa</link>
#if NET48
private static string ByteArrayToString(byte[] bytes)
#else
private static string? ByteArrayToString(byte[]? bytes)
#endif
{
// If we get null in, we send null out
if (bytes == null)

View File

@@ -11,7 +11,11 @@ namespace MPF.Core.Hashing
private readonly AutoResetEvent _outEvent;
private readonly Thread _tWorker;
#if NET48
private byte[] _buffer;
#else
private byte[]? _buffer;
#endif
private int _size;
private readonly Stream _ds;
private bool _finished;
@@ -48,7 +52,8 @@ namespace MPF.Core.Hashing
}
try
{
SizeRead = _ds.Read(_buffer, 0, _size);
if (_buffer != null)
SizeRead = _ds.Read(_buffer, 0, _size);
}
catch (Exception)
{

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,10 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="MPF.Test" />
</ItemGroup>

View File

@@ -26,10 +26,18 @@ namespace MPF.Core.Modules.Aaru
#region Generic Dumping Information
/// <inheritdoc/>
#if NET48
public override string InputPath => InputValue;
#else
public override string? InputPath => InputValue;
#endif
/// <inheritdoc/>
#if NET48
public override string OutputPath => OutputValue;
#else
public override string? OutputPath => OutputValue;
#endif
/// <inheritdoc/>
public override int? Speed
@@ -51,69 +59,177 @@ namespace MPF.Core.Modules.Aaru
public int? BlockSizeValue { get; set; }
#if NET48
public string CommentsValue { get; set; }
#else
public string? CommentsValue { get; set; }
#endif
#if NET48
public string CreatorValue { get; set; }
#else
public string? CreatorValue { get; set; }
#endif
public int? CountValue { get; set; }
#if NET48
public string DriveManufacturerValue { get; set; }
#else
public string? DriveManufacturerValue { get; set; }
#endif
#if NET48
public string DriveModelValue { get; set; }
#else
public string? DriveModelValue { get; set; }
#endif
#if NET48
public string DriveRevisionValue { get; set; }
#else
public string? DriveRevisionValue { get; set; }
#endif
#if NET48
public string DriveSerialValue { get; set; }
#else
public string? DriveSerialValue { get; set; }
#endif
#if NET48
public string EncodingValue { get; set; }
#else
public string? EncodingValue { get; set; }
#endif
#if NET48
public string FormatConvertValue { get; set; }
#else
public string? FormatConvertValue { get; set; }
#endif
#if NET48
public string FormatDumpValue { get; set; }
#else
public string? FormatDumpValue { get; set; }
#endif
#if NET48
public string GeometryValue { get; set; }
#else
public string? GeometryValue { get; set; }
#endif
#if NET48
public string ImgBurnLogValue { get; set; }
#else
public string? ImgBurnLogValue { get; set; }
#endif
#if NET48
public string InputValue { get; set; }
#else
public string? InputValue { get; set; }
#endif
#if NET48
public string Input1Value { get; set; }
#else
public string? Input1Value { get; set; }
#endif
#if NET48
public string Input2Value { get; set; }
#else
public string? Input2Value { get; set; }
#endif
public long? LengthValue { get; set; }
public int? MaxBlocksValue { get; set; }
#if NET48
public string MediaBarcodeValue { get; set; }
#else
public string? MediaBarcodeValue { get; set; }
#endif
public int? MediaLastSequenceValue { get; set; }
#if NET48
public string MediaManufacturerValue { get; set; }
#else
public string? MediaManufacturerValue { get; set; }
#endif
#if NET48
public string MediaModelValue { get; set; }
#else
public string? MediaModelValue { get; set; }
#endif
#if NET48
public string MediaPartNumberValue { get; set; }
#else
public string? MediaPartNumberValue { get; set; }
#endif
public int? MediaSequenceValue { get; set; }
#if NET48
public string MediaSerialValue { get; set; }
#else
public string? MediaSerialValue { get; set; }
#endif
#if NET48
public string MediaTitleValue { get; set; }
#else
public string? MediaTitleValue { get; set; }
#endif
#if NET48
public string MHDDLogValue { get; set; }
#else
public string? MHDDLogValue { get; set; }
#endif
#if NET48
public string NamespaceValue { get; set; }
#else
public string? NamespaceValue { get; set; }
#endif
#if NET48
public string OptionsValue { get; set; }
#else
public string? OptionsValue { get; set; }
#endif
#if NET48
public string OutputValue { get; set; }
#else
public string? OutputValue { get; set; }
#endif
#if NET48
public string OutputPrefixValue { get; set; }
#else
public string? OutputPrefixValue { get; set; }
#endif
#if NET48
public string RemoteHostValue { get; set; }
#else
public string? RemoteHostValue { get; set; }
#endif
#if NET48
public string ResumeFileValue { get; set; }
#else
public string? ResumeFileValue { get; set; }
#endif
public short? RetryPassesValue { get; set; }
@@ -123,11 +239,19 @@ namespace MPF.Core.Modules.Aaru
public long? StartValue { get; set; }
#if NET48
public string SubchannelValue { get; set; }
#else
public string? SubchannelValue { get; set; }
#endif
public short? WidthValue { get; set; }
#if NET48
public string XMLSidecarValue { get; set; }
#else
public string? XMLSidecarValue { get; set; }
#endif
#endregion
@@ -198,9 +322,10 @@ namespace MPF.Core.Modules.Aaru
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
{
// TODO: Fill in submission info specifics for Aaru
string outputDirectory = Path.GetDirectoryName(basePath);
var outputDirectory = Path.GetDirectoryName(basePath);
// TODO: Determine if there's an Aaru version anywhere
if (info.DumpingInfo == null) info.DumpingInfo = new DumpingInfoSection();
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + ".cicm.xml")?.ToString("yyyy-MM-dd HH:mm:ss");
@@ -208,7 +333,7 @@ namespace MPF.Core.Modules.Aaru
var sidecar = GenerateSidecar(basePath + ".cicm.xml");
// Fill in the hardware data
if (GetHardwareInfo(sidecar, out string manufacturer, out string model, out string firmware))
if (GetHardwareInfo(sidecar, out var manufacturer, out var model, out var firmware))
{
info.DumpingInfo.Manufacturer = manufacturer;
info.DumpingInfo.Model = model;
@@ -216,7 +341,7 @@ namespace MPF.Core.Modules.Aaru
}
// Fill in the disc type data
if (GetDiscType(sidecar, out string discType, out string discSubType))
if (GetDiscType(sidecar, out var discType, out var discSubType))
{
string fullDiscType = string.Empty;
if (!string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
@@ -230,9 +355,10 @@ namespace MPF.Core.Modules.Aaru
}
// Get the Datafile information
Datafile datafile = GenerateDatafile(sidecar, basePath);
var datafile = GenerateDatafile(sidecar, basePath);
// Fill in the hash data
if (info.TracksAndWriteOffsets == null) info.TracksAndWriteOffsets = new TracksAndWriteOffsetsSection();
info.TracksAndWriteOffsets.ClrMameProData = GenerateDatfile(datafile);
switch (this.Type)
@@ -247,6 +373,7 @@ namespace MPF.Core.Modules.Aaru
if (File.Exists(basePath + ".resume.xml"))
errorCount = GetErrorCount(basePath + ".resume.xml");
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
info.CommonDiscInfo.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString());
info.TracksAndWriteOffsets.Cuesheet = GenerateCuesheet(sidecar, basePath) ?? string.Empty;
@@ -259,8 +386,10 @@ namespace MPF.Core.Modules.Aaru
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection();
// Get the individual hash data, as per internal
if (GetISOHashValues(datafile, out long size, out string crc32, out string md5, out string sha1))
if (GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
{
info.SizeAndChecksums.Size = size;
info.SizeAndChecksums.CRC32 = crc32;
@@ -273,7 +402,11 @@ namespace MPF.Core.Modules.Aaru
//info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
// Deal with the layerbreak
#if NET48
string layerbreak = null;
#else
string? layerbreak = null;
#endif
if (this.Type == MediaType.DVD)
layerbreak = GetLayerbreak(sidecar) ?? string.Empty;
else if (this.Type == MediaType.BluRay)
@@ -307,34 +440,51 @@ namespace MPF.Core.Modules.Aaru
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection();
info.CopyProtection.Protection = GetDVDProtection(sidecar) ?? string.Empty;
break;
case RedumpSystem.KonamiPython2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate))
if (GetPlayStationExecutableInfo(drive?.Letter, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
}
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.MicrosoftXbox:
if (GetXgdAuxInfo(sidecar, out string xgd1DMIHash, out string xgd1PFIHash, out string xgd1SSHash, out string ss, out string xgd1SSVer))
if (GetXgdAuxInfo(sidecar, out var xgd1DMIHash, out var xgd1PFIHash, out var xgd1SSHash, out var ss, out var xgd1SSVer))
{
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd1DMIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd1SSVer;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd1DMIHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd1SSVer ?? string.Empty;
if (info.Extras == null) info.Extras = new ExtrasSection();
info.Extras.SecuritySectorRanges = ss ?? string.Empty;
}
if (GetXboxDMIInfo(sidecar, out string serial, out string version, out Region? region))
if (GetXboxDMIInfo(sidecar, out var serial, out var version, out Region? region))
{
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
info.CommonDiscInfo.Serial = serial ?? string.Empty;
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = version ?? string.Empty;
info.CommonDiscInfo.Region = region;
}
@@ -342,27 +492,42 @@ namespace MPF.Core.Modules.Aaru
break;
case RedumpSystem.MicrosoftXbox360:
if (GetXgdAuxInfo(sidecar, out string xgd23DMIHash, out string xgd23PFIHash, out string xgd23SSHash, out string ss360, out string xgd23SSVer))
if (GetXgdAuxInfo(sidecar, out var xgd23DMIHash, out var xgd23PFIHash, out var xgd23SSHash, out var ss360, out var xgd23SSVer))
{
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd23DMIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd23SSVer;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd23DMIHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd23SSVer ?? string.Empty;
if (info.Extras == null) info.Extras = new ExtrasSection();
info.Extras.SecuritySectorRanges = ss360 ?? string.Empty;
}
if (GetXbox360DMIInfo(sidecar, out string serial360, out string version360, out Region? region360))
if (GetXbox360DMIInfo(sidecar, out var serial360, out var version360, out Region? region360))
{
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
info.CommonDiscInfo.Serial = serial360 ?? string.Empty;
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = version360 ?? string.Empty;
info.CommonDiscInfo.Region = region360;
}
break;
case RedumpSystem.SonyPlayStation:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate))
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
@@ -371,29 +536,57 @@ namespace MPF.Core.Modules.Aaru
break;
case RedumpSystem.SonyPlayStation2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate))
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
}
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation3:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation4:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation5:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
break;
}
@@ -401,23 +594,28 @@ namespace MPF.Core.Modules.Aaru
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
if (info.Artifacts == null) info.Artifacts = new Dictionary<string, string>();
if (File.Exists(basePath + ".cicm.xml"))
info.Artifacts["cicm"] = GetBase64(GetFullFile(basePath + ".cicm.xml"));
info.Artifacts["cicm"] = GetBase64(GetFullFile(basePath + ".cicm.xml")) ?? string.Empty;
if (File.Exists(basePath + ".ibg"))
info.Artifacts["ibg"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".ibg"));
if (File.Exists(basePath + ".log"))
info.Artifacts["log"] = GetBase64(GetFullFile(basePath + ".log"));
info.Artifacts["log"] = GetBase64(GetFullFile(basePath + ".log")) ?? string.Empty;
if (File.Exists(basePath + ".mhddlog.bin"))
info.Artifacts["mhddlog_bin"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".mhddlog.bin"));
if (File.Exists(basePath + ".resume.xml"))
info.Artifacts["resume"] = GetBase64(GetFullFile(basePath + ".resume.xml"));
info.Artifacts["resume"] = GetBase64(GetFullFile(basePath + ".resume.xml")) ?? string.Empty;
if (File.Exists(basePath + ".sub.log"))
info.Artifacts["sub_log"] = GetBase64(GetFullFile(basePath + ".sub.log"));
info.Artifacts["sub_log"] = GetBase64(GetFullFile(basePath + ".sub.log")) ?? string.Empty;
}
}
/// <inheritdoc/>
#if NET48
public override string GenerateParameters()
#else
public override string? GenerateParameters()
#endif
{
List<string> parameters = new List<string>();
@@ -1677,7 +1875,11 @@ namespace MPF.Core.Modules.Aaru
short? shortValue = null;
int? intValue = null;
long? longValue = null;
#if NET48
string stringValue = null;
#else
string? stringValue = null;
#endif
// Keep a count of keys to determine if we should break out to filename handling or not
int keyCount = Keys.Count();
@@ -2135,7 +2337,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="baseCommand">Command string to normalize</param>
/// <returns>Normalized command</returns>
#if NET48
private string NormalizeCommand(List<string> parts, ref int start)
#else
private string? NormalizeCommand(List<string> parts, ref int start)
#endif
{
// Invalid start means invalid command
if (start < 0 || start >= parts.Count)
@@ -2146,7 +2352,7 @@ namespace MPF.Core.Modules.Aaru
if (start + 1 < parts.Count)
partTwo = parts[start + 1];
string normalized = NormalizeCommand($"{partOne} {partTwo}".Trim());
var normalized = NormalizeCommand($"{partOne} {partTwo}".Trim());
// Null normalization means invalid command
if (normalized == null)
@@ -2164,7 +2370,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="baseCommand">Command string to normalize</param>
/// <returns>Normalized command</returns>
#if NET48
private string NormalizeCommand(string baseCommand)
#else
private string? NormalizeCommand(string baseCommand)
#endif
{
// If the base command is inavlid, just return nulls
if (string.IsNullOrWhiteSpace(baseCommand))
@@ -2172,7 +2382,11 @@ namespace MPF.Core.Modules.Aaru
// Split the command otherwise
string[] splitCommand = baseCommand.Split(' ');
#if NET48
string family, command;
#else
string? family, command;
#endif
// For commands with a family
@@ -2436,7 +2650,11 @@ namespace MPF.Core.Modules.Aaru
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <param name="basePath">Base path for determining file names</param>
/// <returns>String containing the cuesheet, null on error</returns>
#if NET48
private string GenerateCuesheet(CICMMetadataType cicmSidecar, string basePath)
#else
private string? GenerateCuesheet(CICMMetadataType? cicmSidecar, string basePath)
#endif
{
// If the object is null, we can't get information from it
if (cicmSidecar == null)
@@ -2559,6 +2777,9 @@ namespace MPF.Core.Modules.Aaru
if (cueSheet != null && cueSheet != default)
{
var ms = new SabreTools.Serialization.Streams.CueSheet().Serialize(cueSheet);
if (ms == null)
return null;
using (var sr = new StreamReader(ms))
{
return sr.ReadToEnd();
@@ -2574,7 +2795,11 @@ namespace MPF.Core.Modules.Aaru
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <param name="basePath">Base path for determining file names</param>
/// <returns>String containing the datfile, null on error</returns>
#if NET48
private static string GenerateDatfile(CICMMetadataType cicmSidecar, string basePath)
#else
private static string? GenerateDatfile(CICMMetadataType? cicmSidecar, string basePath)
#endif
{
// If the object is null, we can't get information from it
if (cicmSidecar == null)
@@ -2682,7 +2907,11 @@ namespace MPF.Core.Modules.Aaru
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <param name="basePath">Base path for determining file names</param>
/// <returns>Datafile containing the hash information, null on error</returns>
#if NET48
private static Datafile GenerateDatafile(CICMMetadataType cicmSidecar, string basePath)
#else
private static Datafile? GenerateDatafile(CICMMetadataType? cicmSidecar, string basePath)
#endif
{
// If the object is null, we can't get information from it
if (cicmSidecar == null)
@@ -2819,19 +3048,23 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>String containing the PVD, null on error</returns>
#if NET48
private static string GeneratePVD(CICMMetadataType cicmSidecar)
#else
private static string? GeneratePVD(CICMMetadataType? cicmSidecar)
#endif
{
// If the object is null, we can't get information from it
if (cicmSidecar == null)
return null;
// Process OpticalDisc, if possible
if (cicmSidecar.OpticalDisc != null || cicmSidecar.OpticalDisc.Length > 0)
if (cicmSidecar.OpticalDisc != null && cicmSidecar.OpticalDisc.Length > 0)
{
// Loop through each OpticalDisc in the metadata
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
{
byte[] pvdData = GeneratePVDData(opticalDisc);
var pvdData = GeneratePVDData(opticalDisc);
// If we got a null value, we skip this disc
if (pvdData == null)
@@ -2858,7 +3091,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="opticalDisc">OpticalDisc type from CICM Sidecar data</param>
/// <returns>Byte array representing the PVD, null on error</returns>
#if NET48
private static byte[] GeneratePVDData(OpticalDiscType opticalDisc)
#else
private static byte[]? GeneratePVDData(OpticalDiscType? opticalDisc)
#endif
{
// Required variables
DateTime creation = DateTime.MinValue;
@@ -2867,7 +3104,7 @@ namespace MPF.Core.Modules.Aaru
DateTime effective = DateTime.MinValue;
// If there are no tracks, we can't get a PVD
if (opticalDisc.Track == null || opticalDisc.Track.Length == 0)
if (opticalDisc?.Track == null || opticalDisc.Track.Length == 0)
return null;
// Take the first track only
@@ -2995,7 +3232,11 @@ namespace MPF.Core.Modules.Aaru
/// <param name="row">Row ID for outputting</param>
/// <param name="bytes">Byte span representing the data to write</param>
/// <returns>Formatted string representing the sector line</returns>
#if NET48
private static string GenerateSectorOutputLine(string row, ReadOnlySpan<byte> bytes)
#else
private static string? GenerateSectorOutputLine(string row, ReadOnlySpan<byte> bytes)
#endif
{
// If the data isn't correct, return null
if (bytes == null || bytes.Length != 16)
@@ -3017,7 +3258,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>Object containing the data, null on error</returns>
#if NET48
private static CICMMetadataType GenerateSidecar(string cicmSidecar)
#else
private static CICMMetadataType? GenerateSidecar(string cicmSidecar)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(cicmSidecar))
@@ -3039,9 +3284,7 @@ namespace MPF.Core.Modules.Aaru
return null;
XmlSerializer serializer = new XmlSerializer(typeof(CICMMetadataType));
CICMMetadataType obj = serializer.Deserialize(xtr) as CICMMetadataType;
return obj;
return serializer.Deserialize(xtr) as CICMMetadataType;
}
/// <summary>
@@ -3049,7 +3292,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>True if disc type info was set, false otherwise</returns>
#if NET48
private static bool GetDiscType(CICMMetadataType cicmSidecar, out string discType, out string discSubType)
#else
private static bool GetDiscType(CICMMetadataType? cicmSidecar, out string? discType, out string? discSubType)
#endif
{
// Set the default values
discType = null; discSubType = null;
@@ -3080,7 +3327,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>Formatted string representing the DVD protection, null on error</returns>
#if NET48
private static string GetDVDProtection(CICMMetadataType cicmSidecar)
#else
private static string? GetDVDProtection(CICMMetadataType? cicmSidecar)
#endif
{
// If the object is null, we can't get information from it
if (cicmSidecar == null)
@@ -3146,10 +3397,12 @@ namespace MPF.Core.Modules.Aaru
// Read in the error count whenever we find it
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
var line = sr.ReadLine()?.Trim();
// Initialize on seeing the open tag
if (line.StartsWith("<BadBlocks>"))
if (string.IsNullOrWhiteSpace(line))
continue;
else if (line.StartsWith("<BadBlocks>"))
totalErrors = 0;
else if (line.StartsWith("</BadBlocks>"))
return totalErrors ?? -1;
@@ -3173,7 +3426,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>True if hardware info was set, false otherwise</returns>
#if NET48
private static bool GetHardwareInfo(CICMMetadataType cicmSidecar, out string manufacturer, out string model, out string firmware)
#else
private static bool GetHardwareInfo(CICMMetadataType? cicmSidecar, out string? manufacturer, out string? model, out string? firmware)
#endif
{
// Set the default values
manufacturer = null; model = null; firmware = null;
@@ -3217,7 +3474,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>Layerbreak if possible, null on error</returns>
#if NET48
private static string GetLayerbreak(CICMMetadataType cicmSidecar)
#else
private static string? GetLayerbreak(CICMMetadataType? cicmSidecar)
#endif
{
// If the object is null, we can't get information from it
if (cicmSidecar == null)
@@ -3228,7 +3489,11 @@ namespace MPF.Core.Modules.Aaru
return null;
// Setup the layerbreak
#if NET48
string layerbreak = null;
#else
string? layerbreak = null;
#endif
// Find and return the layerbreak, if possible
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
@@ -3248,7 +3513,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>Sample write offset if possible, null on error</returns>
#if NET48
private static string GetWriteOffset(CICMMetadataType cicmSidecar)
#else
private static string? GetWriteOffset(CICMMetadataType? cicmSidecar)
#endif
{
// If the object is null, we can't get information from it
if (cicmSidecar == null)
@@ -3276,7 +3545,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
#if NET48
private static bool GetXgdAuxInfo(CICMMetadataType cicmSidecar, out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver)
#else
private static bool GetXgdAuxInfo(CICMMetadataType? cicmSidecar, out string? dmihash, out string? pfihash, out string? sshash, out string? ss, out string? ssver)
#endif
{
dmihash = null; pfihash = null; sshash = null; ss = null; ssver = null;
@@ -3371,7 +3644,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
#if NET48
private static bool GetXboxDMIInfo(CICMMetadataType cicmSidecar, out string serial, out string version, out Region? region)
#else
private static bool GetXboxDMIInfo(CICMMetadataType? cicmSidecar, out string? serial, out string? version, out Region? region)
#endif
{
serial = null; version = null; region = Region.World;
@@ -3419,7 +3696,11 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
#if NET48
private static bool GetXbox360DMIInfo(CICMMetadataType cicmSidecar, out string serial, out string version, out Region? region)
#else
private static bool GetXbox360DMIInfo(CICMMetadataType? cicmSidecar, out string? serial, out string? version, out Region? region)
#endif
{
serial = null; version = null; region = Region.World;

View File

@@ -25,7 +25,11 @@ namespace MPF.Core.Modules
/// Geneeic way of reporting a message
/// </summary>
/// <param name="message">String value to report</param>
#if NET48
public EventHandler<string> ReportStatus;
#else
public EventHandler<string>? ReportStatus;
#endif
#endregion
@@ -34,7 +38,11 @@ namespace MPF.Core.Modules
/// <summary>
/// Base command to run
/// </summary>
#if NET48
public string BaseCommand { get; set; }
#else
public string? BaseCommand { get; set; }
#endif
/// <summary>
/// Set of flags to pass to the executable
@@ -63,7 +71,11 @@ namespace MPF.Core.Modules
/// <summary>
/// Process to track external program
/// </summary>
#if NET48
private Process process;
#else
private Process? process;
#endif
#endregion
@@ -72,18 +84,30 @@ namespace MPF.Core.Modules
/// <summary>
/// Command to flag support mappings
/// </summary>
#if NET48
public Dictionary<string, List<string>> CommandSupport => GetCommandSupport();
#else
public Dictionary<string, List<string>>? CommandSupport => GetCommandSupport();
#endif
/// <summary>
/// Input path for operations
/// </summary>
#if NET48
public virtual string InputPath => null;
#else
public virtual string? InputPath => null;
#endif
/// <summary>
/// Output path for operations
/// </summary>
/// <returns>String representing the path, null on error</returns>
#if NET48
public virtual string OutputPath => null;
#else
public virtual string? OutputPath => null;
#endif
/// <summary>
/// Get the processing speed from the implementation
@@ -97,7 +121,11 @@ namespace MPF.Core.Modules
/// <summary>
/// Path to the executable
/// </summary>
#if NET48
public string ExecutablePath { get; set; }
#else
public string? ExecutablePath { get; set; }
#endif
/// <summary>
/// Program that this set of parameters represents
@@ -171,20 +199,32 @@ namespace MPF.Core.Modules
/// Get all commands mapped to the supported flags
/// </summary>
/// <returns>Mappings from command to supported flags</returns>
#if NET48
public virtual Dictionary<string, List<string>> GetCommandSupport() => null;
#else
public virtual Dictionary<string, List<string>>? GetCommandSupport() => null;
#endif
/// <summary>
/// Blindly generate a parameter string based on the inputs
/// </summary>
/// <returns>Parameter string for invocation, null on error</returns>
#if NET48
public virtual string GenerateParameters() => null;
#else
public virtual string? GenerateParameters() => null;
#endif
/// <summary>
/// Get the default extension for a given media type
/// </summary>
/// <param name="mediaType">MediaType value to check</param>
/// <returns>String representing the media type, null on error</returns>
#if NET48
public virtual string GetDefaultExtension(MediaType? mediaType) => null;
#else
public virtual string? GetDefaultExtension(MediaType? mediaType) => null;
#endif
/// <summary>
/// Generate a list of all log files generated
@@ -320,7 +360,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="content">String content to encode</param>
/// <returns>Base64-encoded contents, if possible</returns>
#if NET48
protected static string GetBase64(string content)
#else
protected static string? GetBase64(string? content)
#endif
{
if (string.IsNullOrEmpty(content))
return null;
@@ -335,7 +379,11 @@ namespace MPF.Core.Modules
/// <param name="filename">file location</param>
/// <param name="binary">True if should read as binary, false otherwise (default)</param>
/// <returns>Full text of the file, null on error</returns>
#if NET48
protected static string GetFullFile(string filename, bool binary = false)
#else
protected static string? GetFullFile(string filename, bool binary = false)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(filename))
@@ -465,7 +513,11 @@ namespace MPF.Core.Modules
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
#if NET48
protected bool ProcessFlagParameter(List<string> parts, string shortFlagString, string longFlagString, ref int i)
#else
protected bool ProcessFlagParameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i)
#endif
{
if (parts == null)
return false;
@@ -501,7 +553,11 @@ namespace MPF.Core.Modules
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
#if NET48
protected bool ProcessBooleanParameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#else
protected bool ProcessBooleanParameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#endif
{
if (parts == null)
return false;
@@ -576,7 +632,11 @@ namespace MPF.Core.Modules
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>SByte value if success, SByte.MinValue if skipped, null on error/returns>
#if NET48
protected sbyte? ProcessInt8Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#else
protected sbyte? ProcessInt8Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#endif
{
if (parts == null)
return null;
@@ -654,7 +714,11 @@ namespace MPF.Core.Modules
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int16 value if success, Int16.MinValue if skipped, null on error/returns>
#if NET48
protected short? ProcessInt16Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#else
protected short? ProcessInt16Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#endif
{
if (parts == null)
return null;
@@ -731,7 +795,11 @@ namespace MPF.Core.Modules
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int32 value if success, Int32.MinValue if skipped, null on error/returns>
#if NET48
protected int? ProcessInt32Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#else
protected int? ProcessInt32Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#endif
{
if (parts == null)
return null;
@@ -808,7 +876,11 @@ namespace MPF.Core.Modules
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int64 value if success, Int64.MinValue if skipped, null on error/returns>
#if NET48
protected long? ProcessInt64Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#else
protected long? ProcessInt64Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#endif
{
if (parts == null)
return null;
@@ -873,8 +945,12 @@ namespace MPF.Core.Modules
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
#if NET48
protected string ProcessStringParameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessStringParameter(parts, null, flagString, ref i, missingAllowed);
#else
protected string? ProcessStringParameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
#endif
=> ProcessStringParameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process a string parameter
@@ -885,7 +961,11 @@ namespace MPF.Core.Modules
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
#if NET48
protected string ProcessStringParameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#else
protected string? ProcessStringParameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#endif
{
if (parts == null)
return null;
@@ -960,7 +1040,11 @@ namespace MPF.Core.Modules
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Byte value if success, Byte.MinValue if skipped, null on error/returns>
#if NET48
protected byte? ProcessUInt8Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#else
protected byte? ProcessUInt8Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
#endif
{
if (parts == null)
return null;
@@ -1089,17 +1173,23 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="datafile">.dat file location</param>
/// <returns>Relevant pieces of the datfile, null on error</returns>
#if NET48
protected static string GenerateDatfile(Datafile datafile)
#else
protected static string? GenerateDatfile(Datafile? datafile)
#endif
{
// If we don't have a valid datafile, we can't do anything
if (datafile?.Games == null || datafile.Games.Length == 0 || datafile.Games[0]?.Roms == null || datafile.Games[0].Roms.Length == 0)
if (datafile?.Games == null || datafile.Games.Length == 0)
return null;
var roms = datafile.Games[0].Roms;
if (roms == null || roms.Length == 0)
return null;
// Otherwise, reconstruct the hash data with only the required info
try
{
var roms = datafile.Games[0].Roms;
string datString = string.Empty;
for (int i = 0; i < roms.Length; i++)
{
@@ -1121,7 +1211,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="dat">Path to the DAT file to parse</param>
/// <returns>Filled Datafile on success, null on error</returns>
#if NET48
protected static Datafile GetDatafile(string dat)
#else
protected static Datafile? GetDatafile(string? dat)
#endif
{
// If there's no path, we can't read the file
if (string.IsNullOrWhiteSpace(dat))
@@ -1149,9 +1243,7 @@ namespace MPF.Core.Modules
return null;
var serializer = new XmlSerializer(typeof(Datafile));
Datafile obj = serializer.Deserialize(xtr) as Datafile;
return obj;
return serializer.Deserialize(xtr) as Datafile;
}
catch
{
@@ -1166,7 +1258,11 @@ namespace MPF.Core.Modules
/// <param name="pic">Path to a PIC.bin file</param>
/// <returns>Filled DiscInformation on success, null on error</returns>
/// <remarks>This omits the emergency brake information, if it exists</remarks>
#if NET48
protected static DiscInformation GetDiscInformation(string pic)
#else
protected static DiscInformation? GetDiscInformation(string pic)
#endif
{
try
{
@@ -1184,7 +1280,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <returns>True if hashing was successful, false otherwise</returns>
#if NET48
protected static bool GetFileHashes(string filename, out long size, out string crc32, out string md5, out string sha1)
#else
protected static bool GetFileHashes(string filename, out long size, out string? crc32, out string? md5, out string? sha1)
#endif
{
// Set all initial values
size = -1; crc32 = null; md5 = null; sha1 = null;
@@ -1289,7 +1389,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <returns>Filled DateTime on success, null on failure</returns>
#if NET48
protected static DateTime? GetFileModifiedDate(string filename, bool fallback = false)
#else
protected static DateTime? GetFileModifiedDate(string? filename, bool fallback = false)
#endif
{
if (string.IsNullOrWhiteSpace(filename))
return fallback ? (DateTime?)DateTime.UtcNow : null;
@@ -1305,7 +1409,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="hashData">String representing the combined hash data</param>
/// <returns>True if extraction was successful, false otherwise</returns>
#if NET48
protected static bool GetISOHashValues(string hashData, out long size, out string crc32, out string md5, out string sha1)
#else
protected static bool GetISOHashValues(string? hashData, out long size, out string? crc32, out string? md5, out string? sha1)
#endif
{
size = -1; crc32 = null; md5 = null; sha1 = null;
@@ -1335,14 +1443,22 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="datafile">Datafile represenging the hash data</param>
/// <returns>True if extraction was successful, false otherwise</returns>
#if NET48
protected static bool GetISOHashValues(Datafile datafile, out long size, out string crc32, out string md5, out string sha1)
#else
protected static bool GetISOHashValues(Datafile? datafile, out long size, out string? crc32, out string? md5, out string? sha1)
#endif
{
size = -1; crc32 = null; md5 = null; sha1 = null;
if (datafile?.Games == null || datafile.Games.Length == 0 || datafile.Games[0].Roms.Length == 0)
if (datafile?.Games == null || datafile.Games.Length == 0)
return false;
var rom = datafile.Games[0].Roms[0];
var roms = datafile.Games[0].Roms;
if (roms == null || roms.Length == 0)
return false;
var rom = roms[0];
_ = Int64.TryParse(rom.Size, out size);
crc32 = rom.Crc;
@@ -1357,7 +1473,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="di">Disc information containing unformatted data</param>
/// <returns>True if layerbreak info was set, false otherwise</returns>
#if NET48
protected static bool GetLayerbreaks(DiscInformation di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)
#else
protected static bool GetLayerbreaks(DiscInformation? di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)
#endif
{
// Set the default values
layerbreak1 = null; layerbreak2 = null; layerbreak3 = null;
@@ -1369,9 +1489,12 @@ namespace MPF.Core.Modules
#if NET48
int ReadFromArrayBigEndian(byte[] bytes, int offset)
#else
static int ReadFromArrayBigEndian(byte[] bytes, int offset)
static int ReadFromArrayBigEndian(byte[]? bytes, int offset)
#endif
{
if (bytes == null)
return default;
var span = new ReadOnlySpan<byte>(bytes, offset, 0x04);
byte[] rev = span.ToArray();
Array.Reverse(rev);
@@ -1381,24 +1504,24 @@ namespace MPF.Core.Modules
// Layerbreak 1 (2+ layers)
if (di.Units.Length >= 2)
{
long offset = ReadFromArrayBigEndian(di.Units[0].Body.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[0].Body.FormatDependentContents, 0x10);
long offset = ReadFromArrayBigEndian(di.Units[0]?.Body?.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[0]?.Body?.FormatDependentContents, 0x10);
layerbreak1 = value - offset + 2;
}
// Layerbreak 2 (3+ layers)
if (di.Units.Length >= 3)
{
long offset = ReadFromArrayBigEndian(di.Units[1].Body.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[1].Body.FormatDependentContents, 0x10);
long offset = ReadFromArrayBigEndian(di.Units[1]?.Body?.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[1]?.Body?.FormatDependentContents, 0x10);
layerbreak2 = layerbreak1 + value - offset + 2;
}
// Layerbreak 3 (4 layers)
if (di.Units.Length >= 4)
{
long offset = ReadFromArrayBigEndian(di.Units[2].Body.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[2].Body.FormatDependentContents, 0x10);
long offset = ReadFromArrayBigEndian(di.Units[2]?.Body?.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[2]?.Body?.FormatDependentContents, 0x10);
layerbreak3 = layerbreak2 + value - offset + 2;
}
@@ -1410,14 +1533,18 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="di">Disc information containing the data</param>
/// <returns>String representing the PIC identifier, null on error</returns>
#if NET48
protected static string GetPICIdentifier(DiscInformation di)
#else
protected static string? GetPICIdentifier(DiscInformation? di)
#endif
{
// If we don't have valid disc information, we can't do anything
if (di?.Units == null || di.Units.Length <= 1)
return null;
// We assume the identifier is consistent across all units
return di.Units[0].Body.DiscTypeIdentifier;
return di.Units[0]?.Body?.DiscTypeIdentifier;
}
/// <summary>
@@ -1428,7 +1555,11 @@ namespace MPF.Core.Modules
/// <param name="region">Output region, if possible</param>
/// <param name="date">Output EXE date in "yyyy-mm-dd" format if possible, null on error</param>
/// <returns></returns>
#if NET48
protected static bool GetPlayStationExecutableInfo(char? driveLetter, out string serial, out Region? region, out string date)
#else
protected static bool GetPlayStationExecutableInfo(char? driveLetter, out string? serial, out Region? region, out string? date)
#endif
{
serial = null; region = null; date = null;
@@ -1446,7 +1577,11 @@ namespace MPF.Core.Modules
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
// Try both of the common paths that contain information
#if NET48
string exeName = null;
#else
string? exeName = null;
#endif
// Read the CNF file as an INI file
var systemCnf = new IniFile(systemCnfPath);
@@ -1512,7 +1647,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Game version if possible, null on error</returns>
#if NET48
protected static string GetPlayStation2Version(char? driveLetter)
#else
protected static string? GetPlayStation2Version(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
@@ -1540,7 +1679,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Internal disc serial if possible, null on error</returns>
#if NET48
protected static string GetPlayStation3Serial(char? driveLetter)
#else
protected static string? GetPlayStation3Serial(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
@@ -1577,7 +1720,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Game version if possible, null on error</returns>
#if NET48
protected static string GetPlayStation3Version(char? driveLetter)
#else
protected static string? GetPlayStation3Version(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
@@ -1614,7 +1761,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Internal disc serial if possible, null on error</returns>
#if NET48
protected static string GetPlayStation4Serial(char? driveLetter)
#else
protected static string? GetPlayStation4Serial(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
@@ -1645,13 +1796,17 @@ namespace MPF.Core.Modules
return null;
}
}
/// <summary>
/// Get the version from a PlayStation 4 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Game version if possible, null on error</returns>
#if NET48
protected static string GetPlayStation4Version(char? driveLetter)
#else
protected static string? GetPlayStation4Version(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
@@ -1688,7 +1843,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Internal disc serial if possible, null on error</returns>
#if NET48
protected static string GetPlayStation5Serial(char? driveLetter)
#else
protected static string? GetPlayStation5Serial(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
@@ -1725,7 +1884,11 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Game version if possible, null on error</returns>
#if NET48
protected static string GetPlayStation5Version(char? driveLetter)
#else
protected static string? GetPlayStation5Version(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
@@ -1757,7 +1920,7 @@ namespace MPF.Core.Modules
}
}
#endregion
#endregion
#region Category Extraction
@@ -1800,7 +1963,7 @@ namespace MPF.Core.Modules
case 'E': return Region.Europe;
case 'K': return Region.SouthKorea;
case 'U': return Region.UnitedStatesOfAmerica;
case 'P':
case 'P':
// Region of S_P_ serials may be Japan, Asia, or SouthKorea
switch (serial[3])
{

View File

@@ -63,14 +63,16 @@ namespace MPF.Core.Modules.CleanRip
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
{
// TODO: Determine if there's a CleanRip version anywhere
if (info.DumpingInfo == null) info.DumpingInfo = new DumpingInfoSection();
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
Datafile datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
var datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
// Get the individual hash data, as per internal
if (GetISOHashValues(datafile, out long size, out string crc32, out string md5, out string sha1))
if (GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
{
if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection();
info.SizeAndChecksums.Size = size;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
@@ -79,7 +81,7 @@ namespace MPF.Core.Modules.CleanRip
// Dual-layer discs have the same size and layerbreak
if (size == 8511160320)
info.SizeAndChecksums.Layerbreak = 2084960;
}
}
// Extract info based generically on MediaType
switch (this.Type)
@@ -88,12 +90,22 @@ namespace MPF.Core.Modules.CleanRip
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (File.Exists(basePath + ".bca"))
info.Extras.BCA = GetBCA(basePath + ".bca");
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out string gcVersion, out string gcName))
{
if (info.Extras == null) info.Extras = new ExtrasSection();
info.Extras.BCA = GetBCA(basePath + ".bca");
}
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out var gcVersion, out var gcName))
{
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
info.CommonDiscInfo.Region = gcRegion ?? info.CommonDiscInfo.Region;
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = gcVersion ?? info.VersionAndEditions.Version;
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName] = gcName ?? string.Empty;
}
@@ -103,10 +115,11 @@ namespace MPF.Core.Modules.CleanRip
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
if (info.Artifacts == null) info.Artifacts = new Dictionary<string, string>();
if (File.Exists(basePath + ".bca"))
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true));
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true)) ?? string.Empty;
if (File.Exists(basePath + "-dumpinfo.txt"))
info.Artifacts["dumpinfo"] = GetBase64(GetFullFile(basePath + "-dumpinfo.txt"));
info.Artifacts["dumpinfo"] = GetBase64(GetFullFile(basePath + "-dumpinfo.txt")) ?? string.Empty;
}
}
@@ -140,7 +153,11 @@ namespace MPF.Core.Modules.CleanRip
/// <param name="iso">Path to ISO file</param>
/// <param name="dumpinfo">Path to discinfo file</param>
/// <returns></returns>
#if NET48
private static Datafile GenerateCleanripDatafile(string iso, string dumpinfo)
#else
private static Datafile? GenerateCleanripDatafile(string iso, string dumpinfo)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(dumpinfo))
@@ -156,14 +173,16 @@ namespace MPF.Core.Modules.CleanRip
try
{
// Make sure this file is a dumpinfo
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
return null;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
if (line.StartsWith("CRC32"))
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(line))
continue;
else if (line.StartsWith("CRC32"))
crc = line.Substring(7).ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line.Substring(5);
@@ -199,7 +218,11 @@ namespace MPF.Core.Modules.CleanRip
/// <param name="bcaPath">Path to the BCA file associated with the dump</param>
/// <returns>BCA data as a hex string if possible, null on error</returns>
/// <remarks>https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters</remarks>
#if NET48
private static string GetBCA(string bcaPath)
#else
private static string? GetBCA(string bcaPath)
#endif
{
// If the file doesn't exist, we can't get the info
if (!File.Exists(bcaPath))
@@ -207,7 +230,10 @@ namespace MPF.Core.Modules.CleanRip
try
{
string hex = GetFullFile(bcaPath, true);
var hex = GetFullFile(bcaPath, true);
if (hex == null)
return null;
return Regex.Replace(hex, ".{32}", "$0\n");
}
catch
@@ -223,7 +249,11 @@ namespace MPF.Core.Modules.CleanRip
/// <param name="iso">Path to ISO file</param>
/// <param name="dumpinfo">Path to discinfo file</param>
/// <returns></returns>
#if NET48
private static string GetCleanripDatfile(string iso, string dumpinfo)
#else
private static string? GetCleanripDatfile(string iso, string dumpinfo)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(dumpinfo))
@@ -239,14 +269,16 @@ namespace MPF.Core.Modules.CleanRip
try
{
// Make sure this file is a dumpinfo
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
return null;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
if (line.StartsWith("CRC32"))
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(line))
continue;
else if (line.StartsWith("CRC32"))
crc = line.Substring(7).ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line.Substring(5);
@@ -272,7 +304,11 @@ namespace MPF.Core.Modules.CleanRip
/// <param name="version">Output internal version of the game</param>
/// <param name="name">Output internal name of the game</param>
/// <returns></returns>
#if NET48
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string version, out string name)
#else
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string? version, out string? name)
#endif
{
region = null; version = null; name = null;
@@ -285,14 +321,18 @@ namespace MPF.Core.Modules.CleanRip
try
{
// Make sure this file is a dumpinfo
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
return false;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
if (line.StartsWith("Version"))
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
else if (line.StartsWith("Version"))
{
version = line.Substring("Version: ".Length);
}

View File

@@ -6,67 +6,139 @@ namespace MPF.Core.Modules
public class Datafile
{
[XmlElement("header")]
#if NET48
public Header Header;
#else
public Header? Header;
#endif
[XmlElement("game")]
#if NET48
public Game[] Games;
#else
public Game[]? Games;
#endif
}
public class Header
{
[XmlElement("name")]
#if NET48
public string Name;
#else
public string? Name;
#endif
[XmlElement("description")]
#if NET48
public string Description;
#else
public string? Description;
#endif
[XmlElement("version")]
#if NET48
public string Version;
#else
public string? Version;
#endif
[XmlElement("date")]
#if NET48
public string Date;
#else
public string? Date;
#endif
[XmlElement("author")]
#if NET48
public string Author;
#else
public string? Author;
#endif
[XmlElement("homepage")]
#if NET48
public string Homepage;
#else
public string? Homepage;
#endif
[XmlElement("url")]
#if NET48
public string Url;
#else
public string? Url;
#endif
}
public class Game
{
[XmlAttribute("name")]
#if NET48
public string Name;
#else
public string? Name;
#endif
[XmlElement("category")]
#if NET48
public string Category;
#else
public string? Category;
#endif
[XmlElement("description")]
#if NET48
public string Description;
#else
public string? Description;
#endif
[XmlElement("rom")]
#if NET48
public Rom[] Roms;
#else
public Rom[]? Roms;
#endif
}
public class Rom
{
[XmlAttribute("name")]
#if NET48
public string Name;
#else
public string? Name;
#endif
[XmlAttribute("size")]
#if NET48
public string Size;
#else
public string? Size;
#endif
[XmlAttribute("crc")]
#if NET48
public string Crc;
#else
public string? Crc;
#endif
[XmlAttribute("md5")]
#if NET48
public string Md5;
#else
public string? Md5;
#endif
[XmlAttribute("sha1")]
#if NET48
public string Sha1;
#else
public string? Sha1;
#endif
// TODO: Add extended hashes here
}

View File

@@ -48,7 +48,11 @@ namespace MPF.Core.Modules.DiscImageCreator
/// <param name="baseCommand">Command value to check</param>
/// <returns>MediaType if possible, null on error</returns>
/// <remarks>This takes the "safe" route by assuming the larger of any given format</remarks>
#if NET48
public static MediaType? ToMediaType(string baseCommand)
#else
public static MediaType? ToMediaType(string? baseCommand)
#endif
{
switch (baseCommand)
{
@@ -86,7 +90,11 @@ namespace MPF.Core.Modules.DiscImageCreator
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>Valid extension (with leading '.'), null on error</returns>
#if NET48
public static string Extension(MediaType? type)
#else
public static string? Extension(MediaType? type)
#endif
{
switch (type)
{

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>Valid extension (with leading '.'), null on error</returns>
#if NET48
public static string Extension(MediaType? type)
#else
public static string? Extension(MediaType? type)
#endif
{
switch (type)
{

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using BinaryObjectScanner.Protection;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
@@ -26,10 +27,18 @@ namespace MPF.Core.Modules.Redumper
#region Generic Dumping Information
/// <inheritdoc/>
#if NET48
public override string InputPath => DriveValue;
#else
public override string? InputPath => DriveValue;
#endif
/// <inheritdoc/>
#if NET48
public override string OutputPath => Path.Combine(ImagePathValue?.Trim('"') ?? string.Empty, ImageNameValue?.Trim('"') ?? string.Empty) + GetDefaultExtension(this.Type);
#else
public override string? OutputPath => Path.Combine(ImagePathValue?.Trim('"') ?? string.Empty, ImageNameValue?.Trim('"') ?? string.Empty) + GetDefaultExtension(this.Type);
#endif
/// <inheritdoc/>
public override int? Speed => SpeedValue;
@@ -48,14 +57,22 @@ namespace MPF.Core.Modules.Redumper
/// <summary>
/// List of all modes being run
/// </summary>
#if NET48
public List<string> ModeValues { get; set; }
#else
public List<string>? ModeValues { get; set; }
#endif
#region General
/// <summary>
/// Drive to use, first available drive with disc, if not provided
/// </summary>
#if NET48
public string DriveValue { get; set; }
#else
public string? DriveValue { get; set; }
#endif
/// <summary>
/// Drive read speed, optimal drive speed will be used if not provided
@@ -70,12 +87,20 @@ namespace MPF.Core.Modules.Redumper
/// <summary>
/// Dump files base directory
/// </summary>
#if NET48
public string ImagePathValue { get; set; }
#else
public string? ImagePathValue { get; set; }
#endif
/// <summary>
/// Dump files prefix, autogenerated in dump mode, if not provided
/// </summary>
#if NET48
public string ImageNameValue { get; set; }
#else
public string? ImageNameValue { get; set; }
#endif
#endregion
@@ -84,7 +109,11 @@ namespace MPF.Core.Modules.Redumper
/// <summary>
/// Override drive type, possible values: GENERIC, PLEXTOR, LG_ASUS
/// </summary>
#if NET48
public string DriveTypeValue { get; set; }
#else
public string? DriveTypeValue { get; set; }
#endif
/// <summary>
/// Override drive read offset
@@ -104,12 +133,20 @@ namespace MPF.Core.Modules.Redumper
/// <summary>
/// Override drive read method, possible values: BE, D8, BE_CDDA
/// </summary>
#if NET48
public string DriveReadMethodValue { get; set; }
#else
public string? DriveReadMethodValue { get; set; }
#endif
/// <summary>
/// Override drive sector order, possible values: DATA_C2_SUB, DATA_SUB_C2
/// </summary>
#if NET48
public string DriveSectorOrderValue { get; set; }
#else
public string? DriveSectorOrderValue { get; set; }
#endif
#endregion
@@ -151,7 +188,11 @@ namespace MPF.Core.Modules.Redumper
/// <summary>
/// LBA ranges of sectors to skip
/// </summary>
#if NET48
public string SkipValue { get; set; }
#else
public string? SkipValue { get; set; }
#endif
/// <summary>
/// Number of sectors to read at once on initial dump, DVD only (Default 32)
@@ -245,11 +286,12 @@ namespace MPF.Core.Modules.Redumper
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
{
// Get the dumping program and version
if (info.DumpingInfo == null) info.DumpingInfo = new DumpingInfoSection();
info.DumpingInfo.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {GetVersion($"{basePath}.log") ?? "Unknown Version"}";
info.DumpingInfo.DumpingDate = GetFileModifiedDate($"{basePath}.log")?.ToString("yyyy-MM-dd HH:mm:ss");
// Fill in the hardware data
if (GetHardwareInfo($"{basePath}.log", out string manufacturer, out string model, out string firmware))
if (GetHardwareInfo($"{basePath}.log", out var manufacturer, out var model, out var firmware))
{
info.DumpingInfo.Manufacturer = manufacturer;
info.DumpingInfo.Model = model;
@@ -259,12 +301,15 @@ namespace MPF.Core.Modules.Redumper
switch (this.Type)
{
case MediaType.CDROM:
info.Extras.PVD = GetPVD($"{basePath}.log") ?? "Disc has no PVD"; ;
if (info.Extras == null) info.Extras = new ExtrasSection();
info.Extras.PVD = GetPVD($"{basePath}.log") ?? "Disc has no PVD";
if (info.TracksAndWriteOffsets == null) info.TracksAndWriteOffsets = new TracksAndWriteOffsetsSection();
info.TracksAndWriteOffsets.ClrMameProData = GetDatfile($"{basePath}.log");
info.TracksAndWriteOffsets.Cuesheet = GetFullFile($"{basePath}.cue") ?? string.Empty;
// Attempt to get the write offset
string cdWriteOffset = GetWriteOffset($"{basePath}.log") ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
info.CommonDiscInfo.RingWriteOffset = cdWriteOffset;
info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset;
@@ -272,6 +317,12 @@ namespace MPF.Core.Modules.Redumper
long errorCount = GetErrorCount($"{basePath}.log");
info.CommonDiscInfo.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString());
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
// Attempt to get multisession data
string cdMultiSessionInfo = GetMultisessionInformation($"{basePath}.log") ?? string.Empty;
if (!string.IsNullOrWhiteSpace(cdMultiSessionInfo))
@@ -294,11 +345,14 @@ namespace MPF.Core.Modules.Redumper
break;
case MediaType.DVD:
info.Extras.PVD = GetPVD($"{basePath}.log") ?? "Disc has no PVD"; ;
if (info.Extras == null) info.Extras = new ExtrasSection();
info.Extras.PVD = GetPVD($"{basePath}.log") ?? "Disc has no PVD";
if (info.TracksAndWriteOffsets == null) info.TracksAndWriteOffsets = new TracksAndWriteOffsetsSection();
info.TracksAndWriteOffsets.ClrMameProData = GetDatfile($"{basePath}.log");
// Get the individual hash data, as per internal
if (GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out string crc32, out string md5, out string sha1))
if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection();
if (GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out var crc32, out var md5, out var sha1))
{
info.SizeAndChecksums.Size = size;
info.SizeAndChecksums.CRC32 = crc32;
@@ -323,18 +377,26 @@ namespace MPF.Core.Modules.Redumper
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection();
info.CopyProtection.Protection = GetDVDProtection($"{basePath}.log") ?? string.Empty;
break;
case RedumpSystem.KonamiPython2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate))
if (GetPlayStationExecutableInfo(drive?.Letter, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
}
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
break;
@@ -351,7 +413,14 @@ namespace MPF.Core.Modules.Redumper
break;
case RedumpSystem.SegaMegaCDSegaCD:
info.Extras.Header = GetSegaCDHeader($"{basePath}.log", out string scdBuildDate, out string scdSerial, out string _) ?? string.Empty;
if (info.Extras == null) info.Extras = new ExtrasSection();
info.Extras.Header = GetSegaCDHeader($"{basePath}.log", out var scdBuildDate, out var scdSerial, out _) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = scdSerial ?? string.Empty;
info.CommonDiscInfo.EXEDateBuildDate = scdBuildDate ?? string.Empty;
// TODO: Support region setting from parsed value
@@ -374,16 +443,24 @@ namespace MPF.Core.Modules.Redumper
break;
case RedumpSystem.SegaSaturn:
if (info.Extras == null) info.Extras = new ExtrasSection();
info.Extras.Header = GetSaturnHeader($"{basePath}.log") ?? string.Empty;
// Take only the first 16 lines for Saturn
if (!string.IsNullOrEmpty(info.Extras.Header))
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
if (GetSaturnBuildInfo(info.Extras.Header, out string saturnSerial, out string saturnVersion, out string buildDate))
if (GetSaturnBuildInfo(info.Extras.Header, out var saturnSerial, out var saturnVersion, out var buildDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = saturnSerial ?? string.Empty;
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = saturnVersion ?? string.Empty;
info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? string.Empty;
}
@@ -391,44 +468,80 @@ namespace MPF.Core.Modules.Redumper
break;
case RedumpSystem.SonyPlayStation:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate))
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
}
if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection();
info.CopyProtection.AntiModchip = GetPlayStationAntiModchipDetected($"{basePath}.log").ToYesNo();
if (info.EDC == null) info.EDC = new EDCSection();
info.EDC.EDC = GetPlayStationEDCStatus($"{basePath}.log").ToYesNo();
info.CopyProtection.LibCrypt = GetPlayStationLibCryptStatus($"{basePath}.log").ToYesNo();
info.CopyProtection.LibCryptData = GetPlayStationLibCryptData($"{basePath}.log");
break;
case RedumpSystem.SonyPlayStation2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate))
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
}
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation3:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation4:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation5:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
break;
}
@@ -436,36 +549,37 @@ namespace MPF.Core.Modules.Redumper
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
if (info.Artifacts == null) info.Artifacts = new Dictionary<string, string>();
if (File.Exists($"{basePath}.cdtext"))
info.Artifacts["cdtext"] = GetBase64(GetFullFile($"{basePath}.cdtext"));
info.Artifacts["cdtext"] = GetBase64(GetFullFile($"{basePath}.cdtext")) ?? string.Empty;
if (File.Exists($"{basePath}.cue"))
info.Artifacts["cue"] = GetBase64(GetFullFile($"{basePath}.cue"));
info.Artifacts["cue"] = GetBase64(GetFullFile($"{basePath}.cue")) ?? string.Empty;
if (File.Exists($"{basePath}.fulltoc"))
info.Artifacts["fulltoc"] = GetBase64(GetFullFile($"{basePath}.fulltoc"));
info.Artifacts["fulltoc"] = GetBase64(GetFullFile($"{basePath}.fulltoc")) ?? string.Empty;
if (File.Exists($"{basePath}.log"))
info.Artifacts["log"] = GetBase64(GetFullFile($"{basePath}.log"));
info.Artifacts["log"] = GetBase64(GetFullFile($"{basePath}.log")) ?? string.Empty;
if (File.Exists($"{basePath}.manufacturer"))
info.Artifacts["manufacturer"] = GetBase64(GetFullFile($"{basePath}.manufacturer"));
info.Artifacts["manufacturer"] = GetBase64(GetFullFile($"{basePath}.manufacturer")) ?? string.Empty;
if (File.Exists($"{basePath}.1.manufacturer"))
info.Artifacts["manufacturer1"] = GetBase64(GetFullFile($"{basePath}.1.manufacturer"));
info.Artifacts["manufacturer1"] = GetBase64(GetFullFile($"{basePath}.1.manufacturer")) ?? string.Empty;
if (File.Exists($"{basePath}.2.manufacturer"))
info.Artifacts["manufacturer2"] = GetBase64(GetFullFile($"{basePath}.2.manufacturer"));
info.Artifacts["manufacturer2"] = GetBase64(GetFullFile($"{basePath}.2.manufacturer")) ?? string.Empty;
if (File.Exists($"{basePath}.physical"))
info.Artifacts["physical"] = GetBase64(GetFullFile($"{basePath}.physical"));
info.Artifacts["physical"] = GetBase64(GetFullFile($"{basePath}.physical")) ?? string.Empty;
if (File.Exists($"{basePath}.1.physical"))
info.Artifacts["physical1"] = GetBase64(GetFullFile($"{basePath}.1.physical"));
info.Artifacts["physical1"] = GetBase64(GetFullFile($"{basePath}.1.physical")) ?? string.Empty;
if (File.Exists($"{basePath}.2.physical"))
info.Artifacts["physical2"] = GetBase64(GetFullFile($"{basePath}.2.physical"));
info.Artifacts["physical2"] = GetBase64(GetFullFile($"{basePath}.2.physical")) ?? string.Empty;
// if (File.Exists($"{basePath}.scram"))
// info.Artifacts["scram"] = GetBase64(GetFullFile($"{basePath}.scram"));
// info.Artifacts["scram"] = GetBase64(GetFullFile($"{basePath}.scram")) ?? string.Empty;
// if (File.Exists($"{basePath}.scrap"))
// info.Artifacts["scrap"] = GetBase64(GetFullFile($"{basePath}.scrap"));
// info.Artifacts["scrap"] = GetBase64(GetFullFile($"{basePath}.scrap")) ?? string.Empty;
if (File.Exists($"{basePath}.state"))
info.Artifacts["state"] = GetBase64(GetFullFile($"{basePath}.state"));
info.Artifacts["state"] = GetBase64(GetFullFile($"{basePath}.state")) ?? string.Empty;
if (File.Exists($"{basePath}.subcode"))
info.Artifacts["subcode"] = GetBase64(GetFullFile($"{basePath}.subcode"));
info.Artifacts["subcode"] = GetBase64(GetFullFile($"{basePath}.subcode")) ?? string.Empty;
if (File.Exists($"{basePath}.toc"))
info.Artifacts["toc"] = GetBase64(GetFullFile($"{basePath}.toc"));
info.Artifacts["toc"] = GetBase64(GetFullFile($"{basePath}.toc")) ?? string.Empty;
}
}
@@ -763,7 +877,11 @@ namespace MPF.Core.Modules.Redumper
}
/// <inheritdoc/>
#if NET48
public override string GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
#else
public override string? GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
#endif
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
@@ -818,11 +936,11 @@ namespace MPF.Core.Modules.Redumper
public override bool IsDumpingCommand()
{
return this.BaseCommand == CommandStrings.NONE
|| this.BaseCommand.Contains(CommandStrings.CD)
|| this.BaseCommand.Contains(CommandStrings.DVD)
|| this.BaseCommand.Contains(CommandStrings.BluRay)
|| this.BaseCommand.Contains(CommandStrings.SACD)
|| this.BaseCommand.Contains(CommandStrings.Dump);
|| this.BaseCommand?.Contains(CommandStrings.CD) == true
|| this.BaseCommand?.Contains(CommandStrings.DVD) == true
|| this.BaseCommand?.Contains(CommandStrings.BluRay) == true
|| this.BaseCommand?.Contains(CommandStrings.SACD) == true
|| this.BaseCommand?.Contains(CommandStrings.Dump) == true;
}
/// <inheritdoc/>
@@ -908,7 +1026,7 @@ namespace MPF.Core.Modules.Redumper
// Set the output paths
if (!string.IsNullOrWhiteSpace(filename))
{
string imagePath = Path.GetDirectoryName(filename);
var imagePath = Path.GetDirectoryName(filename);
if (!string.IsNullOrWhiteSpace(imagePath))
{
this[FlagStrings.ImagePath] = true;
@@ -996,7 +1114,11 @@ namespace MPF.Core.Modules.Redumper
// Flag read-out values
byte? byteValue = null;
int? intValue = null;
#if NET48
string stringValue = null;
#else
string? stringValue = null;
#endif
#region General
@@ -1176,7 +1298,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Newline-delimited cuesheet if possible, null on error</returns>
#if NET48
private static string GetCuesheet(string log)
#else
private static string? GetCuesheet(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
@@ -1187,16 +1313,20 @@ namespace MPF.Core.Modules.Redumper
try
{
// Fast forward to the dat line
while (!sr.EndOfStream && !sr.ReadLine().TrimStart().StartsWith("CUE [")) ;
while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("CUE [") == false) ;
if (sr.EndOfStream)
return null;
// Now that we're at the relevant entries, read each line in and concatenate
string cueString = "", line = sr.ReadLine().Trim();
#if NET48
string cueString = string.Empty, line = sr.ReadLine()?.Trim();
#else
string? cueString = string.Empty, line = sr.ReadLine()?.Trim();
#endif
while (!string.IsNullOrWhiteSpace(line))
{
cueString += line + "\n";
line = sr.ReadLine().Trim();
line = sr.ReadLine()?.Trim();
}
return cueString.TrimEnd('\n');
@@ -1214,7 +1344,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Newline-delimited datfile if possible, null on error</returns>
#if NET48
private static string GetDatfile(string log)
#else
private static string? GetDatfile(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
@@ -1225,13 +1359,14 @@ namespace MPF.Core.Modules.Redumper
try
{
// Fast forward to the dat line
while (!sr.EndOfStream && !sr.ReadLine().TrimStart().StartsWith("dat:")) ;
while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("dat:") == false) ;
if (sr.EndOfStream)
return null;
// Now that we're at the relevant entries, read each line in and concatenate
string datString = "", line = sr.ReadLine().Trim();
while (line.StartsWith("<rom"))
var datString = string.Empty;
var line = sr.ReadLine()?.Trim();
while (line?.StartsWith("<rom") == true)
{
datString += line + "\n";
if (sr.EndOfStream)
@@ -1255,23 +1390,31 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Formatted string representing the DVD protection, null on error</returns>
#if NET48
private static string GetDVDProtection(string log)
#else
private static string? GetDVDProtection(string log)
#endif
{
// If one of the files doesn't exist, we can't get info from them
if (!File.Exists(log))
return null;
// Setup all of the individual pieces
#if NET48
string region = null, rceProtection = null, copyrightProtectionSystemType = null, vobKeys = null, decryptedDiscKey = null;
#else
string? region = null, rceProtection = null, copyrightProtectionSystemType = null, vobKeys = null, decryptedDiscKey = null;
#endif
using (StreamReader sr = File.OpenText(log))
{
try
{
// Fast forward to the copyright information
while (!sr.ReadLine().Trim().StartsWith("copyright:")) ;
while (sr.ReadLine()?.Trim().StartsWith("copyright:") == false) ;
// Now read until we hit the manufacturing information
string line = sr.ReadLine()?.Trim();
var line = sr.ReadLine()?.Trim();
while (line != null && !sr.EndOfStream)
{
if (line.StartsWith("protection system type"))
@@ -1369,7 +1512,7 @@ namespace MPF.Core.Modules.Redumper
try
{
// Fast forward to the errors lines
while (!sr.EndOfStream && !sr.ReadLine().Trim().StartsWith("CD-ROM [")) ;
while (!sr.EndOfStream && sr.ReadLine()?.Trim()?.StartsWith("CD-ROM [") == false) ;
if (sr.EndOfStream)
return 0;
@@ -1377,9 +1520,9 @@ namespace MPF.Core.Modules.Redumper
while (!sr.EndOfStream)
{
// Skip forward to the "REDUMP.ORG" line
string line = string.Empty;
while (!sr.EndOfStream && !(line = sr.ReadLine().Trim()).StartsWith("REDUMP.ORG errors")) ;
if (line == string.Empty)
var line = string.Empty;
while (!sr.EndOfStream && (line = sr.ReadLine()?.Trim())?.StartsWith("REDUMP.ORG errors") == false) ;
if (string.IsNullOrEmpty(line))
break;
// REDUMP.ORG errors: <error count>
@@ -1405,7 +1548,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Layerbreak if possible, null on error</returns>
#if NET48
private static string GetLayerbreak(string log)
#else
private static string? GetLayerbreak(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
@@ -1416,15 +1563,19 @@ namespace MPF.Core.Modules.Redumper
try
{
// Fast forward to the disc structure lines
while (!sr.EndOfStream && !sr.ReadLine().Trim().StartsWith("layer 0")) ;
while (!sr.EndOfStream && sr.ReadLine()?.Trim()?.StartsWith("layer 0") == false) ;
if (sr.EndOfStream)
return null;
// Now that we're at the relevant lines, find the layerbreak
#if NET48
string layerbreak = null;
#else
string? layerbreak = null;
#endif
while (!sr.EndOfStream)
{
string line = sr.ReadLine()?.Trim();
var line = sr.ReadLine()?.Trim();
// If we have a null line, just break
if (line == null)
@@ -1476,7 +1627,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Formatted multisession information, null on error</returns>
#if NET48
private static string GetMultisessionInformation(string log)
#else
private static string? GetMultisessionInformation(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
@@ -1487,15 +1642,19 @@ namespace MPF.Core.Modules.Redumper
try
{
// Fast forward to the multisession lines
while (!sr.EndOfStream && !sr.ReadLine().Trim().StartsWith("multisession:")) ;
while (!sr.EndOfStream && sr.ReadLine()?.Trim()?.StartsWith("multisession:") == false) ;
if (sr.EndOfStream)
return null;
// Now that we're at the relevant lines, find the session info
#if NET48
string firstSession = null, secondSession = null;
#else
string? firstSession = null, secondSession = null;
#endif
while (!sr.EndOfStream)
{
string line = sr.ReadLine()?.Trim();
var line = sr.ReadLine()?.Trim();
// If we have a null line, just break
if (line == null)
@@ -1549,7 +1708,7 @@ namespace MPF.Core.Modules.Redumper
try
{
// Check for the anti-modchip strings
string line = sr.ReadLine().Trim();
var line = sr.ReadLine()?.Trim();
while (!sr.EndOfStream)
{
if (line == null)
@@ -1560,7 +1719,7 @@ namespace MPF.Core.Modules.Redumper
else if (line.StartsWith("anti-modchip: yes"))
return true;
line = sr.ReadLine().Trim();
line = sr.ReadLine()?.Trim();
}
return false;
@@ -1589,7 +1748,7 @@ namespace MPF.Core.Modules.Redumper
try
{
// Check for the EDC strings
string line = sr.ReadLine().Trim();
var line = sr.ReadLine()?.Trim();
while (!sr.EndOfStream)
{
if (line == null)
@@ -1600,7 +1759,7 @@ namespace MPF.Core.Modules.Redumper
else if (line.Contains("EDC: yes"))
return true;
line = sr.ReadLine().Trim();
line = sr.ReadLine()?.Trim();
}
return false;
@@ -1618,7 +1777,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>PS1 LibCrypt data, if possible</returns>
#if NET48
private static string GetPlayStationLibCryptData(string log)
#else
private static string? GetPlayStationLibCryptData(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
@@ -1629,16 +1792,20 @@ namespace MPF.Core.Modules.Redumper
try
{
// Fast forward to the LibCrypt line
while (!sr.EndOfStream && !sr.ReadLine().TrimStart().StartsWith("libcrypt:")) ;
while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("libcrypt:") == false) ;
if (sr.EndOfStream)
return null;
// Now that we're at the relevant entries, read each line in and concatenate
string libCryptString = "", line = sr.ReadLine().Trim();
while (line.StartsWith("MSF:"))
#if NET48
string libCryptString = "", line = sr.ReadLine()?.Trim();
#else
string? libCryptString = "", line = sr.ReadLine()?.Trim();
#endif
while (line?.StartsWith("MSF:") == true)
{
libCryptString += line + "\n";
line = sr.ReadLine().Trim();
line = sr.ReadLine()?.Trim();
}
return libCryptString.TrimEnd('\n');
@@ -1667,7 +1834,7 @@ namespace MPF.Core.Modules.Redumper
try
{
// Check for the libcrypt strings
string line = sr.ReadLine().Trim();
var line = sr.ReadLine()?.Trim();
while (!sr.EndOfStream)
{
if (line == null)
@@ -1678,7 +1845,7 @@ namespace MPF.Core.Modules.Redumper
else if (line.StartsWith("libcrypt: yes"))
return true;
line = sr.ReadLine().Trim();
line = sr.ReadLine()?.Trim();
}
return false;
@@ -1696,7 +1863,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Newline-delimited PVD if possible, null on error</returns>
#if NET48
private static string GetPVD(string log)
#else
private static string? GetPVD(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
@@ -1707,16 +1878,20 @@ namespace MPF.Core.Modules.Redumper
try
{
// Fast forward to the PVD line
while (!sr.EndOfStream && !sr.ReadLine().TrimStart().StartsWith("PVD:")) ;
while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("PVD:") == false) ;
if (sr.EndOfStream)
return null;
// Now that we're at the relevant entries, read each line in and concatenate
string pvdString = "", line = sr.ReadLine().Trim();
while (line.StartsWith("03"))
#if NET48
string pvdString = "", line = sr.ReadLine()?.Trim();
#else
string? pvdString = "", line = sr.ReadLine()?.Trim();
#endif
while (line?.StartsWith("03") == true)
{
pvdString += line + "\n";
line = sr.ReadLine().Trim();
line = sr.ReadLine()?.Trim();
}
return pvdString.TrimEnd('\n');
@@ -1734,7 +1909,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Non-zero dta start if possible, null on error</returns>
#if NET48
private static string GetRingNonZeroDataStart(string log)
#else
private static string? GetRingNonZeroDataStart(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
@@ -1745,11 +1924,15 @@ namespace MPF.Core.Modules.Redumper
try
{
// If we find the sample range, return the start value only
#if NET48
string line;
#else
string? line;
#endif
while (!sr.EndOfStream)
{
line = sr.ReadLine().TrimStart();
if (line.StartsWith("non-zero data sample range"))
line = sr.ReadLine()?.TrimStart();
if (line?.StartsWith("non-zero data sample range") == true)
#if NET48
return line.Substring("non-zero data sample range: [".Length).Trim().Split(' ')[0];
#else
@@ -1773,8 +1956,12 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <<param name="segaHeader">String representing a formatter variant of the Saturn header</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
/// TODO: Remove when Redumpe gets native reading support
/// TODO: Remove when Redumper gets native reading support
#if NET48
private static bool GetSaturnBuildInfo(string segaHeader, out string serial, out string version, out string date)
#else
private static bool GetSaturnBuildInfo(string? segaHeader, out string? serial, out string? version, out string? date)
#endif
{
serial = null; version = null; date = null;
@@ -1814,7 +2001,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Header as a byte array if possible, null on error</returns>
#if NET48
private static string GetSaturnHeader(string log)
#else
private static string? GetSaturnHeader(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
@@ -1825,21 +2016,25 @@ namespace MPF.Core.Modules.Redumper
try
{
// Fast forward to the SS line
while (!sr.EndOfStream && !sr.ReadLine().TrimStart().StartsWith("SS [")) ;
while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("SS [") == false) ;
if (sr.EndOfStream)
return null;
#if NET48
string line, headerString = "";
#else
string? line, headerString = "";
#endif
while (!sr.EndOfStream)
{
line = sr.ReadLine().TrimStart();
if (line.StartsWith("header:"))
line = sr.ReadLine()?.TrimStart();
if (line?.StartsWith("header:") == true)
{
line = sr.ReadLine().TrimStart();
while (line.StartsWith("00"))
line = sr.ReadLine()?.TrimStart();
while (line?.StartsWith("00") == true)
{
headerString += line + "\n";
line = sr.ReadLine().Trim();
line = sr.ReadLine()?.Trim();
}
}
else
@@ -1863,7 +2058,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Header as a byte array if possible, null on error</returns>
#if NET48
private static string GetSegaCDHeader(string log, out string buildDate, out string serial, out string region)
#else
private static string? GetSegaCDHeader(string log, out string? buildDate, out string? serial, out string? region)
#endif
{
// Set the default values
buildDate = null; serial = null; region = null;
@@ -1877,14 +2076,21 @@ namespace MPF.Core.Modules.Redumper
try
{
// Fast forward to the MCD line
while (!sr.EndOfStream && !sr.ReadLine().TrimStart().StartsWith("MCD [")) ;
while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("MCD [") == false) ;
if (sr.EndOfStream)
return null;
string line, headerString = "";
#if NET48
string line, headerString = string.Empty;
#else
string? line, headerString = string.Empty;
#endif
while (!sr.EndOfStream)
{
line = sr.ReadLine().TrimStart();
line = sr.ReadLine()?.TrimStart();
if (line == null)
break;
if (line.StartsWith("build date:"))
{
#if NET48
@@ -1919,11 +2125,11 @@ namespace MPF.Core.Modules.Redumper
}
else if (line.StartsWith("header:"))
{
line = sr.ReadLine().TrimStart();
while (line.StartsWith("01"))
line = sr.ReadLine()?.TrimStart();
while (line?.StartsWith("01") == true)
{
headerString += line + "\n";
line = sr.ReadLine().Trim();
line = sr.ReadLine()?.Trim();
}
}
else
@@ -1947,7 +2153,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Universal hash if possible, null on error</returns>
#if NET48
private static string GetUniversalHash(string log)
#else
private static string? GetUniversalHash(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
@@ -1958,11 +2168,15 @@ namespace MPF.Core.Modules.Redumper
try
{
// If we find the universal hash line, return the hash only
#if NET48
string line;
#else
string? line;
#endif
while (!sr.EndOfStream)
{
line = sr.ReadLine().TrimStart();
if (line.StartsWith("Universal Hash"))
line = sr.ReadLine()?.TrimStart();
if (line?.StartsWith("Universal Hash") == true)
#if NET48
return line.Substring("Universal Hash (SHA-1): ".Length).Trim();
#else
@@ -1986,7 +2200,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Sample write offset if possible, null on error</returns>
#if NET48
private static string GetWriteOffset(string log)
#else
private static string? GetWriteOffset(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
@@ -1997,11 +2215,15 @@ namespace MPF.Core.Modules.Redumper
try
{
// If we find the disc write offset line, return the offset
#if NET48
string line;
#else
string? line;
#endif
while (!sr.EndOfStream)
{
line = sr.ReadLine().TrimStart();
if (line.StartsWith("disc write offset"))
line = sr.ReadLine()?.TrimStart();
if (line?.StartsWith("disc write offset") == true)
#if NET48
return line.Substring("disc write offset: ".Length).Trim();
#else
@@ -2025,7 +2247,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Version if possible, null on error</returns>
#if NET48
private static string GetVersion(string log)
#else
private static string? GetVersion(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
@@ -2049,7 +2275,7 @@ namespace MPF.Core.Modules.Redumper
//var regex = new Regex(@"^redumper (v\d{4}\.\d{2}\.\d{2}(| build_\d+)) \[.+\]");
// Extract the version string
var match = regex.Match(sr.ReadLine().Trim());
var match = regex.Match(sr.ReadLine()?.Trim() ?? string.Empty);
var version = match.Groups[1].Value;
return string.IsNullOrWhiteSpace(version) ? null : version;
}
@@ -2066,7 +2292,11 @@ namespace MPF.Core.Modules.Redumper
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>True if hardware info was set, false otherwise</returns>
#if NET48
private static bool GetHardwareInfo(string log, out string manufacturer, out string model, out string firmware)
#else
private static bool GetHardwareInfo(string log, out string? manufacturer, out string? model, out string? firmware)
#endif
{
// Set the default values
manufacturer = null; model = null; firmware = null;
@@ -2085,7 +2315,12 @@ namespace MPF.Core.Modules.Redumper
// If we find the hardware info line, return each value
// drive: <vendor_id> - <product_id> (revision level: <product_revision_level>, vendor specific: <vendor_specific>)
var regex = new Regex(@"drive: (.+) - (.+) \(revision level: (.+), vendor specific: (.+)\)");
#if NET48
string line;
#else
string? line;
#endif
while ((line = sr.ReadLine()) != null)
{
var match = regex.Match(line.Trim());

View File

@@ -64,6 +64,7 @@ namespace MPF.Core.Modules.UmdImageCreator
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
{
// TODO: Determine if there's a UMDImageCreator version anywhere
if (info.DumpingInfo == null) info.DumpingInfo = new DumpingInfoSection();
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
@@ -71,21 +72,26 @@ namespace MPF.Core.Modules.UmdImageCreator
switch (this.Type)
{
case MediaType.UMD:
if (info.Extras == null) info.Extras = new ExtrasSection();
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty;
if (GetFileHashes(basePath + ".iso", out long filesize, out string crc32, out string md5, out string sha1))
if (GetFileHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
{
if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection();
info.SizeAndChecksums.Size = filesize;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
}
if (GetUMDAuxInfo(basePath + "_disc.txt", out string title, out DiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize))
if (GetUMDAuxInfo(basePath + "_disc.txt", out var title, out DiscCategory? umdcat, out var umdversion, out var umdlayer, out long umdsize))
{
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
info.CommonDiscInfo.Title = title ?? string.Empty;
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = umdversion ?? string.Empty;
if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection();
info.SizeAndChecksums.Size = umdsize;
if (!string.IsNullOrWhiteSpace(umdlayer))
@@ -98,16 +104,17 @@ namespace MPF.Core.Modules.UmdImageCreator
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
if (info.Artifacts == null) info.Artifacts = new Dictionary<string, string>();
if (File.Exists(basePath + "_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt"));
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt")) ?? string.Empty;
if (File.Exists(basePath + "_drive.txt"))
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt"));
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt")) ?? string.Empty;
if (File.Exists(basePath + "_mainError.txt"))
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt"));
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt")) ?? string.Empty;
if (File.Exists(basePath + "_mainInfo.txt"))
info.Artifacts["mainInfo"] = GetBase64(GetFullFile(basePath + "_mainInfo.txt"));
info.Artifacts["mainInfo"] = GetBase64(GetFullFile(basePath + "_mainInfo.txt")) ?? string.Empty;
if (File.Exists(basePath + "_volDesc.txt"))
info.Artifacts["volDesc"] = GetBase64(GetFullFile(basePath + "_volDesc.txt"));
info.Artifacts["volDesc"] = GetBase64(GetFullFile(basePath + "_volDesc.txt")) ?? string.Empty;
}
}
@@ -144,7 +151,11 @@ namespace MPF.Core.Modules.UmdImageCreator
/// </summary>
/// <param name="mainInfo">_mainInfo.txt file location</param>
/// <returns>Newline-deliminated PVD if possible, null on error</returns>
#if NET48
private static string GetPVD(string mainInfo)
#else
private static string? GetPVD(string mainInfo)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(mainInfo))
@@ -155,10 +166,10 @@ namespace MPF.Core.Modules.UmdImageCreator
try
{
// Make sure we're in the right sector
while (!sr.ReadLine().StartsWith("========== LBA[000016, 0x0000010]: Main Channel ==========")) ;
while (sr.ReadLine()?.StartsWith("========== LBA[000016, 0x0000010]: Main Channel ==========") == false) ;
// Fast forward to the PVD
while (!sr.ReadLine().StartsWith("0310")) ;
while (sr.ReadLine()?.StartsWith("0310") == false) ;
// Now that we're at the PVD, read each line in and concatenate
string pvd = "";
@@ -180,7 +191,11 @@ namespace MPF.Core.Modules.UmdImageCreator
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
#if NET48
private static bool GetUMDAuxInfo(string disc, out string title, out DiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize)
#else
private static bool GetUMDAuxInfo(string disc, out string? title, out DiscCategory? umdcat, out string? umdversion, out string? umdlayer, out long umdsize)
#endif
{
title = null; umdcat = null; umdversion = null; umdlayer = null; umdsize = -1;
@@ -193,10 +208,12 @@ namespace MPF.Core.Modules.UmdImageCreator
try
{
// Loop through everything to get the first instance of each required field
string line = string.Empty;
var line = string.Empty;
while (!sr.EndOfStream)
{
line = sr.ReadLine().Trim();
line = sr.ReadLine()?.Trim();
if (line == null)
break;
if (line.StartsWith("TITLE") && title == null)
title = line.Substring("TITLE: ".Length);
@@ -211,7 +228,7 @@ namespace MPF.Core.Modules.UmdImageCreator
}
// If the L0 length is the size of the full disc, there's no layerbreak
if (Int64.Parse(umdlayer) * 2048 == umdsize)
if (Int64.TryParse(umdlayer, out long umdlayerValue) && umdlayerValue * 2048 == umdsize)
umdlayer = null;
return true;

View File

@@ -19,7 +19,11 @@ namespace MPF.Core
/// <param name="options">Options object that determines what to scan</param>
/// <param name="progress">Optional progress callback</param>
/// <returns>Set of all detected copy protections with an optional error string</returns>
public static async Task<(Dictionary<string, List<string>>, string)> RunProtectionScanOnPath(string path, Core.Data.Options options, IProgress<ProtectionProgress> progress = null)
#if NET48
public static async Task<(Dictionary<string, List<string>>, string)> RunProtectionScanOnPath(string path, Data.Options options, IProgress<ProtectionProgress> progress = null)
#else
public static async Task<(Dictionary<string, List<string>>?, string?)> RunProtectionScanOnPath(string path, Data.Options options, IProgress<ProtectionProgress>? progress = null)
#endif
{
try
{
@@ -62,7 +66,11 @@ namespace MPF.Core
/// </summary>
/// <param name="protections">Dictionary of file to list of protection mappings</param>
/// <returns>Detected protections, if any</returns>
#if NET48
public static string FormatProtections(Dictionary<string, List<string>> protections)
#else
public static string? FormatProtections(Dictionary<string, List<string>>? protections)
#endif
{
// If the filtered list is empty in some way, return
if (protections == null || !protections.Any())

View File

@@ -14,7 +14,11 @@ namespace MPF.Core.Utilities
/// <param name="reader">TextReader representing the input</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
#if NET48
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string> handler)
#else
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string>? handler)
#endif
{
// Initialize the required variables
char[] buffer = new char[256];
@@ -63,7 +67,11 @@ namespace MPF.Core.Utilities
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
#if NET48
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
#else
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
#endif
{
line = line.Replace("\r\n", "\n");
var split = line.Split('\n');
@@ -105,7 +113,11 @@ namespace MPF.Core.Utilities
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
#if NET48
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
#else
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
#endif
{
var split = line.Split('\r');

View File

@@ -15,7 +15,11 @@ namespace MPF.Core.Utilities
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
#if NET48
public static (Options, string, int) LoadFromArguments(string[] args, int startIndex = 0)
#else
public static (Options, string?, int) LoadFromArguments(string[] args, int startIndex = 0)
#endif
{
// Create the output values with defaults
var options = new Options()
@@ -27,7 +31,11 @@ namespace MPF.Core.Utilities
CompressLogFiles = false,
};
#if NET48
string parsedPath = null;
#else
string? parsedPath = null;
#endif
// These values require multiple parts to be active
bool scan = false, protectFile = false;
@@ -154,7 +162,11 @@ namespace MPF.Core.Utilities
var serializer = JsonSerializer.Create();
var reader = new StreamReader(ConfigurationPath);
#if NET48
var settings = serializer.Deserialize(reader, typeof(Dictionary<string, string>)) as Dictionary<string, string>;
#else
var settings = serializer.Deserialize(reader, typeof(Dictionary<string, string?>)) as Dictionary<string, string?>;
#endif
return new Options(settings);
}

View File

@@ -130,16 +130,23 @@ namespace MPF.Core.Utilities
/// String representing the message to display the the user.
/// String representing the new release URL.
/// </returns>
#if NET48
public static (bool different, string message, string url) CheckForNewVersion()
#else
public static (bool different, string message, string? url) CheckForNewVersion()
#endif
{
try
{
// Get current assembly version
var assemblyVersion = Assembly.GetEntryAssembly().GetName().Version;
var assemblyVersion = Assembly.GetEntryAssembly()?.GetName()?.Version;
if (assemblyVersion == null)
return (false, "Assembly version could not be determined", null);
string version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}" + (assemblyVersion.Build != 0 ? $".{assemblyVersion.Build}" : string.Empty);
// Get the latest tag from GitHub
(string tag, string url) = GetRemoteVersionAndUrl();
var (tag, url) = GetRemoteVersionAndUrl();
bool different = version != tag;
string message = $"Local version: {version}"
@@ -159,12 +166,20 @@ namespace MPF.Core.Utilities
/// <summary>
/// Get the current informational version formatted as a string
/// </summary>
#if NET48
public static string GetCurrentVersion()
#else
public static string? GetCurrentVersion()
#endif
{
try
{
var assemblyVersion = Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
return assemblyVersion.InformationalVersion;
var assembly = Assembly.GetEntryAssembly();
if (assembly == null)
return null;
var assemblyVersion = Attribute.GetCustomAttribute(assembly, typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
return assemblyVersion?.InformationalVersion;
}
catch (Exception ex)
{
@@ -175,9 +190,13 @@ namespace MPF.Core.Utilities
/// <summary>
/// Get the latest version of MPF from GitHub and the release URL
/// </summary>
#if NET48
private static (string tag, string url) GetRemoteVersionAndUrl()
#else
private static (string? tag, string? url) GetRemoteVersionAndUrl()
#endif
{
#if NETFRAMEWORK
#if NET48
using (System.Net.WebClient wc = new System.Net.WebClient())
{
wc.Headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0";
@@ -198,10 +217,16 @@ namespace MPF.Core.Utilities
string url = "https://api.github.com/repos/SabreTools/MPF/releases/latest";
var message = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
message.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0");
string latestReleaseJsonString = hc.Send(message)?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
var latestReleaseJsonString = hc.Send(message)?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
if (latestReleaseJsonString == null)
return (null, null);
var latestReleaseJson = JObject.Parse(latestReleaseJsonString);
string latestTag = latestReleaseJson["tag_name"].ToString();
string releaseUrl = latestReleaseJson["html_url"].ToString();
if (latestReleaseJson == null)
return (null, null);
var latestTag = latestReleaseJson["tag_name"]?.ToString();
var releaseUrl = latestReleaseJson["html_url"]?.ToString();
return (latestTag, releaseUrl);
}