From 19cef20cebcbe14b3ddbe28ac06b07bb73a29e44 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sat, 7 Oct 2023 01:02:21 -0400 Subject: [PATCH] Allow nullability for modern .NET --- CHANGELIST.md | 2 + MPF.Core/Converters/EnumConverter.cs | 20 +- MPF.Core/Data/Drive.cs | 56 +- MPF.Core/Data/IniFile.cs | 16 +- MPF.Core/Data/Options.cs | 130 ++-- MPF.Core/Data/ProcessingQueue.cs | 4 +- MPF.Core/Data/Result.cs | 8 + MPF.Core/Data/XgdInfo.cs | 22 +- MPF.Core/DumpEnvironment.cs | 116 ++- MPF.Core/Hashing/Hasher.cs | 38 +- MPF.Core/Hashing/ThreadLoadBuffer.cs | 7 +- MPF.Core/InfoTool.cs | 600 ++++++++++----- MPF.Core/MPF.Core.csproj | 4 + MPF.Core/Modules/Aaru/Parameters.cs | 347 ++++++++- MPF.Core/Modules/BaseParameters.cs | 203 ++++- MPF.Core/Modules/CleanRIp/Parameters.cs | 78 +- MPF.Core/Modules/Datafile.cs | 72 ++ .../Modules/DiscImageCreator/Converters.cs | 8 + .../Modules/DiscImageCreator/Parameters.cs | 691 ++++++++++++++---- MPF.Core/Modules/Redumper/Converters.cs | 4 + MPF.Core/Modules/Redumper/Parameters.cs | 395 ++++++++-- .../Modules/UmdImageCreator/Parameters.cs | 41 +- MPF.Core/Protection.cs | 10 +- MPF.Core/Utilities/Logging.cs | 12 + MPF.Core/Utilities/OptionsLoader.cs | 12 + MPF.Core/Utilities/Tools.cs | 41 +- 26 files changed, 2312 insertions(+), 625 deletions(-) diff --git a/CHANGELIST.md b/CHANGELIST.md index 72f97d7e..9812ef05 100644 --- a/CHANGELIST.md +++ b/CHANGELIST.md @@ -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) diff --git a/MPF.Core/Converters/EnumConverter.cs b/MPF.Core/Converters/EnumConverter.cs index 771bf2b9..8c91efbf 100644 --- a/MPF.Core/Converters/EnumConverter.cs +++ b/MPF.Core/Converters/EnumConverter.cs @@ -38,7 +38,11 @@ namespace MPF.Core.Converters /// /// Long name method cache /// +#if NET48 private static readonly ConcurrentDictionary LongNameMethods = new ConcurrentDictionary(); +#else + private static readonly ConcurrentDictionary LongNameMethods = new ConcurrentDictionary(); +#endif /// /// 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 /// /// String value to convert /// InternalProgram represented by the string, if possible +#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": diff --git a/MPF.Core/Data/Drive.cs b/MPF.Core/Data/Drive.cs index 8918c6b7..d4061268 100644 --- a/MPF.Core/Data/Drive.cs +++ b/MPF.Core/Data/Drive.cs @@ -29,12 +29,20 @@ namespace MPF.Core.Data /// /// Drive partition format /// +#if NET48 public string DriveFormat { get; private set; } = null; +#else + public string? DriveFormat { get; private set; } = null; +#endif /// /// Windows drive path /// +#if NET48 public string Name { get; private set; } = null; +#else + public string? Name { get; private set; } = null; +#endif /// /// Represents if Windows has marked the drive as active @@ -50,7 +58,11 @@ namespace MPF.Core.Data /// Media label as read by Windows /// /// The try/catch is needed because Windows will throw an exception if the drive is not marked as active +#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 /// /// Media label as read by Windows, formatted to avoid odd outputs /// +#if NET48 public string FormattedVolumeLabel +#else + public string? FormattedVolumeLabel +#endif { get { @@ -96,7 +112,11 @@ namespace MPF.Core.Data /// /// InternalDriveType value representing the drive type /// Path to the device according to the local machine +#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 /// /// DriveInfo object to populate from +#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 /// /// True to ignore fixed drives from population, false otherwise /// Active drives, matched to labels, if possible +#if NET48 public static List CreateListOfDrives(bool ignoreFixedDrives) +#else + public static List 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 /// /// /// +#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 /// Sector number, non-negative /// Size of a sector in bytes /// Byte array representing the sector, null on error +#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 /// +#if NET48 private static List GetDriveList(bool ignoreFixedDrives) +#else + private static List GetDriveList(bool ignoreFixedDrives) +#endif { var desiredDriveTypes = new List() { 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(); +#else + var drives = new List(); +#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; } }); } } } diff --git a/MPF.Core/Data/IniFile.cs b/MPF.Core/Data/IniFile.cs index 953c09a9..40c082fc 100644 --- a/MPF.Core/Data/IniFile.cs +++ b/MPF.Core/Data/IniFile.cs @@ -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)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out value); + bool result = ((IDictionary)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out var temp); + value = temp ?? string.Empty; + return result; } public void Add(KeyValuePair item) diff --git a/MPF.Core/Data/Options.cs b/MPF.Core/Data/Options.cs index cfdab2d1..fbc3b55b 100644 --- a/MPF.Core/Data/Options.cs +++ b/MPF.Core/Data/Options.cs @@ -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 +#if NET48 + public class Options +#else + public class Options +#endif { /// /// All settings in the form of a dictionary /// +#if NET48 public Dictionary Settings { get; private set; } +#else + public Dictionary Settings { get; private set; } +#endif #region Internal Program /// /// Path to Aaru /// +#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 /// /// Path to DiscImageCreator /// +#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 /// /// Path to Redumper /// +#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 /// /// Default output path for dumps /// +#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 /// /// +#if NET48 public Options(Dictionary settings = null) +#else + public Options(Dictionary? settings = null) +#endif { +#if NET48 this.Settings = settings ?? new Dictionary(); +#else + this.Settings = settings ?? new Dictionary(); +#endif } /// @@ -571,7 +612,24 @@ namespace MPF.Core.Data /// public Options(Options source) { +#if NET48 Settings = new Dictionary(source.Settings); +#else + Settings = new Dictionary(source.Settings); +#endif + } + + /// + /// Accessor for the internal dictionary + /// +#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 /// Setting key to get a value for /// Default value to return if no value is found /// Setting value if possible, default value otherwise +#if NET48 private bool GetBooleanSetting(Dictionary settings, string key, bool defaultValue) +#else + private bool GetBooleanSetting(Dictionary 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 /// Setting key to get a value for /// Default value to return if no value is found /// Setting value if possible, default value otherwise +#if NET48 private int GetInt32Setting(Dictionary settings, string key, int defaultValue) +#else + private int GetInt32Setting(Dictionary 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 /// Setting key to get a value for /// Default value to return if no value is found /// Setting value if possible, default value otherwise +#if NET48 private string GetStringSetting(Dictionary settings, string key, string defaultValue) +#else + private string? GetStringSetting(Dictionary 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 Keys => Settings.Keys; - - public ICollection Values => Settings.Values; - - public int Count => Settings.Count; - - public bool IsReadOnly => ((IDictionary)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 item) => Settings.Add(item.Key, item.Value); - - public void Clear() => Settings.Clear(); - - public bool Contains(KeyValuePair item) => ((IDictionary)Settings).Contains(item); - - public void CopyTo(KeyValuePair[] array, int arrayIndex) => ((IDictionary)Settings).CopyTo(array, arrayIndex); - - public bool Remove(KeyValuePair item) => ((IDictionary)Settings).Remove(item); - - public IEnumerator> GetEnumerator() => Settings.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => Settings.GetEnumerator(); - - #endregion } } diff --git a/MPF.Core/Data/ProcessingQueue.cs b/MPF.Core/Data/ProcessingQueue.cs index f2522722..128e3ec3 100644 --- a/MPF.Core/Data/ProcessingQueue.cs +++ b/MPF.Core/Data/ProcessingQueue.cs @@ -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 diff --git a/MPF.Core/Data/Result.cs b/MPF.Core/Data/Result.cs index e6dacea1..d025838d 100644 --- a/MPF.Core/Data/Result.cs +++ b/MPF.Core/Data/Result.cs @@ -30,7 +30,11 @@ /// Create a success result with a custom message /// /// String to add as a message +#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 /// /// Create a default failure result with no message @@ -42,7 +46,11 @@ /// Create a failure result with a custom message /// /// String to add as a message +#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 /// /// Results can be compared to boolean values based on the success value diff --git a/MPF.Core/Data/XgdInfo.cs b/MPF.Core/Data/XgdInfo.cs index ecb943fe..aafd24ae 100644 --- a/MPF.Core/Data/XgdInfo.cs +++ b/MPF.Core/Data/XgdInfo.cs @@ -17,17 +17,29 @@ namespace MPF.Core.Data /// /// Raw XMID/XeMID string that all other information is derived from /// +#if NET48 public string RawXMID { get; private set; } +#else + public string? RawXMID { get; private set; } +#endif /// /// XGD1 XMID /// +#if NET48 public SabreTools.Models.Xbox.XMID XMID { get; private set; } +#else + public SabreTools.Models.Xbox.XMID? XMID { get; private set; } +#endif /// /// XGD2/3 XeMID /// +#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 /// /// Formatted serial string, null on error +#if NET48 public string GetSerial() +#else + public string? GetSerial() +#endif { if (!this.Initialized) return null; @@ -86,7 +102,11 @@ namespace MPF.Core.Data /// /// Formatted version string, null on error /// This may differ for XGD2/3 in the future +#if NET48 public string GetVersion() +#else + public string? GetVersion() +#endif { if (!this.Initialized) return null; @@ -160,7 +180,7 @@ namespace MPF.Core.Data /// /// Character denoting the region /// Region, if possible - public static Region? GetRegion(char region) + public static Region? GetRegion(char? region) { switch (region) { diff --git a/MPF.Core/DumpEnvironment.cs b/MPF.Core/DumpEnvironment.cs index f1884505..850f9f89 100644 --- a/MPF.Core/DumpEnvironment.cs +++ b/MPF.Core/DumpEnvironment.cs @@ -51,36 +51,52 @@ namespace MPF.Core /// /// Options object representing user-defined options /// - public Core.Data.Options Options { get; private set; } + public Data.Options Options { get; private set; } /// /// Parameters object representing what to send to the internal program /// +#if NET48 public BaseParameters Parameters { get; private set; } +#else + public BaseParameters? Parameters { get; private set; } +#endif #endregion - + #region Event Handlers /// /// Generic way of reporting a message /// +#if NET48 public EventHandler ReportStatus; +#else + public EventHandler? ReportStatus; +#endif /// /// Queue of items that need to be logged /// +#if NET48 private ProcessingQueue outputQueue; +#else + private ProcessingQueue? outputQueue; +#endif /// /// Event handler for data returned from a process /// - 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 /// /// Process the outputs in the queue /// - 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 /// /// /// - 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; + } } /// @@ -207,7 +236,11 @@ namespace MPF.Core /// /// Nullable int representing the drive speed /// String representing the params, null on error +#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 /// /// Cancel an in-progress dumping process /// - public void CancelDumping() => Parameters.KillInternalProgram(); + public void CancelDumping() => Parameters?.KillInternalProgram(); /// /// Eject the disc using DiscImageCreator /// +#if NET48 public async Task EjectDisc() => - await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject); +#else + public async Task EjectDisc() => +#endif + await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject); /// /// Reset the current drive using DiscImageCreator /// +#if NET48 public async Task ResetDrive() => +#else + public async Task ResetDrive() => +#endif await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Reset); /// /// Execute the initial invocation of the dumping programs /// /// Optional result progress callback +#if NET48 public async Task Run(IProgress progress = null) +#else + public async Task Run(IProgress? 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(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 /// Optional user prompt to deal with submission information /// Result instance with the outcome public async Task VerifyAndSaveDumpOutput( +#if NET48 IProgress resultProgress = null, IProgress protectionProgress = null, Func processUserInfo = null) +#else + IProgress? resultProgress = null, + IProgress? protectionProgress = null, + Func? 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 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 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 /// True if the configuration is valid, false otherwise 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 /// /// Command string to run /// The output of the command on success, null on error +#if NET48 private async Task RunStandaloneDiscImageCreatorCommand(string command) +#else + private async Task 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 } } diff --git a/MPF.Core/Hashing/Hasher.cs b/MPF.Core/Hashing/Hasher.cs index 80d52ff0..d179a9db 100644 --- a/MPF.Core/Hashing/Hasher.cs +++ b/MPF.Core/Hashing/Hasher.cs @@ -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 /// /// Get internal hash as a byte array /// +#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 /// /// Get internal hash as a string /// +#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 /// Byte array to convert /// Hex string representing the byte array /// http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa +#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) diff --git a/MPF.Core/Hashing/ThreadLoadBuffer.cs b/MPF.Core/Hashing/ThreadLoadBuffer.cs index 535c95b7..00ac15df 100644 --- a/MPF.Core/Hashing/ThreadLoadBuffer.cs +++ b/MPF.Core/Hashing/ThreadLoadBuffer.cs @@ -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) { diff --git a/MPF.Core/InfoTool.cs b/MPF.Core/InfoTool.cs index 6dcc2c8f..e8220078 100644 --- a/MPF.Core/InfoTool.cs +++ b/MPF.Core/InfoTool.cs @@ -35,30 +35,40 @@ namespace MPF.Core /// Optional result progress callback /// Optional protection progress callback /// SubmissionInfo populated based on outputs, null on error +#if NET48 public static async Task ExtractOutputInformation( - string outputPath, +#else + public static async Task ExtractOutputInformation( +#endif + string outputPath, Drive drive, RedumpSystem? system, MediaType? mediaType, - Core.Data.Options options, + Data.Options options, +#if NET48 BaseParameters parameters, IProgress resultProgress = null, IProgress protectionProgress = null) +#else + BaseParameters? parameters, + IProgress? resultProgress = null, + IProgress? protectionProgress = null) +#endif { // Ensure the current disc combination should exist if (!system.MediaTypes().Contains(mediaType)) return null; // Split the output path for easier use - string outputDirectory = Path.GetDirectoryName(outputPath); + var outputDirectory = Path.GetDirectoryName(outputPath); string outputFilename = Path.GetFileName(outputPath); // Check that all of the relevant files are there (bool foundFiles, List missingFiles) = FoundAllFiles(outputDirectory, outputFilename, parameters, false); if (!foundFiles) { - resultProgress.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}")); - resultProgress.Report(Result.Failure($"This may indicate an issue with the hardware or media, including unsupported devices.\nPlease see dumping program documentation for more details.")); + resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}")); + resultProgress?.Report(Result.Failure($"This may indicate an issue with the hardware or media, including unsupported devices.\nPlease see dumping program documentation for more details.")); return null; } @@ -66,7 +76,12 @@ namespace MPF.Core outputFilename = Path.GetFileNameWithoutExtension(outputFilename); // Create the SubmissionInfo object with all user-inputted values by default - string combinedBase = Path.Combine(outputDirectory, outputFilename); + string combinedBase; + if (string.IsNullOrWhiteSpace(outputDirectory)) + combinedBase = outputFilename; + else + combinedBase = Path.Combine(outputDirectory, outputFilename); + var info = new SubmissionInfo() { CommonDiscInfo = new CommonDiscInfoSection() @@ -104,7 +119,7 @@ namespace MPF.Core }; // Get specific tool output handling - parameters.GenerateSubmissionInfo(info, options, combinedBase, drive, options.IncludeArtifacts); + parameters?.GenerateSubmissionInfo(info, options, combinedBase, drive, options.IncludeArtifacts); // Get a list of matching IDs for each line in the DAT if (!string.IsNullOrEmpty(info.TracksAndWriteOffsets.ClrMameProData) && options.HasRedumpLogin) @@ -115,11 +130,11 @@ namespace MPF.Core #endif // If we have both ClrMamePro and Size and Checksums data, remove the ClrMamePro - if (!string.IsNullOrWhiteSpace(info.SizeAndChecksums.CRC32)) + if (!string.IsNullOrWhiteSpace(info.SizeAndChecksums?.CRC32)) info.TracksAndWriteOffsets.ClrMameProData = null; // Add the volume label to comments, if possible or necessary - if (drive != null && drive.GetRedumpSystemFromVolumeLabel() == null) + if (drive?.VolumeLabel != null && drive.GetRedumpSystemFromVolumeLabel() == null) info.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel] = drive.VolumeLabel; // Extract info based generically on MediaType @@ -138,6 +153,8 @@ namespace MPF.Core case MediaType.DVD: case MediaType.HDDVD: case MediaType.BluRay: + if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection(); + // If we have a single-layer disc if (info.SizeAndChecksums.Layerbreak == default) { @@ -172,10 +189,13 @@ namespace MPF.Core info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty; info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty; info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty; + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.BCA = info.Extras.BCA ?? (options.AddPlaceholders ? Template.RequiredValue : string.Empty); break; case MediaType.NintendoWiiOpticalDisc: + if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection(); + // If we have a single-layer disc if (info.SizeAndChecksums.Layerbreak == default) { @@ -201,6 +221,7 @@ namespace MPF.Core info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty; } + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.DiscKey = options.AddPlaceholders ? Template.RequiredValue : string.Empty; info.Extras.BCA = info.Extras.BCA ?? (options.AddPlaceholders ? Template.RequiredValue : string.Empty); @@ -217,6 +238,7 @@ namespace MPF.Core info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty; info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty; + if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection(); info.SizeAndChecksums.CRC32 = info.SizeAndChecksums.CRC32 ?? (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty); info.SizeAndChecksums.MD5 = info.SizeAndChecksums.MD5 ?? (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty); info.SizeAndChecksums.SHA1 = info.SizeAndChecksums.SHA1 ?? (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty); @@ -239,9 +261,15 @@ namespace MPF.Core case RedumpSystem.RainbowDisc: case RedumpSystem.SonyElectronicBook: resultProgress?.Report(Result.Success("Running copy protection scan... this might take a while!")); - (string protectionString, Dictionary> fullProtections) = await GetCopyProtection(drive, options, protectionProgress); + var (protectionString, fullProtections) = await GetCopyProtection(drive, options, protectionProgress); + + if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection(); info.CopyProtection.Protection = protectionString; - info.CopyProtection.FullProtections = fullProtections; +#if NET48 + info.CopyProtection.FullProtections = fullProtections ?? new Dictionary>(); +#else + info.CopyProtection.FullProtections = fullProtections as Dictionary?> ?? new Dictionary?>(); +#endif resultProgress?.Report(Result.Success("Copy protection scan complete!")); break; @@ -259,6 +287,7 @@ namespace MPF.Core case RedumpSystem.BDVideo: info.CommonDiscInfo.Category = info.CommonDiscInfo.Category ?? DiscCategory.BonusDiscs; + if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection(); info.CopyProtection.Protection = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty; break; @@ -318,21 +347,27 @@ namespace MPF.Core break; case RedumpSystem.MicrosoftXboxOne: - string xboxOneMsxcPath = Path.Combine($"{drive.Letter}:\\", "MSXC"); - if (drive != null && Directory.Exists(xboxOneMsxcPath)) + if (drive != null) { - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename] = string.Join("\n", - Directory.GetFiles(xboxOneMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)); + string xboxOneMsxcPath = Path.Combine($"{drive.Letter}:\\", "MSXC"); + if (drive != null && Directory.Exists(xboxOneMsxcPath)) + { + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename] = string.Join("\n", + Directory.GetFiles(xboxOneMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)); + } } break; case RedumpSystem.MicrosoftXboxSeriesXS: - string xboxSeriesXMsxcPath = Path.Combine($"{drive.Letter}:\\", "MSXC"); - if (drive != null && Directory.Exists(xboxSeriesXMsxcPath)) + if (drive != null) { - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename] = string.Join("\n", - Directory.GetFiles(xboxSeriesXMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)); + string xboxSeriesXMsxcPath = Path.Combine($"{drive.Letter}:\\", "MSXC"); + if (drive != null && Directory.Exists(xboxSeriesXMsxcPath)) + { + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename] = string.Join("\n", + Directory.GetFiles(xboxSeriesXMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)); + } } break; @@ -389,6 +424,7 @@ namespace MPF.Core case RedumpSystem.SonyPlayStation: // Only check the disc if the dumping program couldn't detect + if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection(); if (drive != null && info.CopyProtection.AntiModchip == YesNo.NULL) { resultProgress?.Report(Result.Success("Checking for anti-modchip strings... this might take a while!")); @@ -397,7 +433,7 @@ namespace MPF.Core } // Special case for DIC only - if (parameters.InternalProgram == InternalProgram.DiscImageCreator) + if (parameters?.InternalProgram == InternalProgram.DiscImageCreator) { resultProgress?.Report(Result.Success("Checking for LibCrypt status... this might take a while!")); GetLibCryptDetected(info, combinedBase); @@ -411,6 +447,7 @@ namespace MPF.Core break; case RedumpSystem.SonyPlayStation3: + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.DiscKey = options.AddPlaceholders ? Template.RequiredValue : string.Empty; info.Extras.DiscID = options.AddPlaceholders ? Template.RequiredValue : string.Empty; break; @@ -420,6 +457,7 @@ namespace MPF.Core break; case RedumpSystem.ZAPiTGamesGameWaveFamilyEntertainmentSystem: + if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection(); info.CopyProtection.Protection = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty; break; } @@ -447,13 +485,25 @@ namespace MPF.Core /// Parameters object representing what to send to the internal program /// True if this is a check done before a dump, false if done after /// Tuple of true if all required files exist, false otherwise and a list representing missing files +#if NET48 public static (bool, List) FoundAllFiles(string outputDirectory, string outputFilename, BaseParameters parameters, bool preCheck) +#else + public static (bool, List) FoundAllFiles(string? outputDirectory, string outputFilename, BaseParameters? parameters, bool preCheck) +#endif { + // If there are no parameters set + if (parameters == null) + return (false, new List()); + // First, sanitized the output filename to strip off any potential extension outputFilename = Path.GetFileNameWithoutExtension(outputFilename); // Then get the base path for all checking - string basePath = Path.Combine(outputDirectory, outputFilename); + string basePath; + if (string.IsNullOrWhiteSpace(outputDirectory)) + basePath = outputFilename; + else + basePath = Path.Combine(outputDirectory, outputFilename); // Finally, let the parameters say if all files exist return parameters.CheckAllOutputFilesExist(basePath, preCheck); @@ -474,11 +524,15 @@ namespace MPF.Core /// Options object that determines what to scan /// Optional progress callback /// Detected copy protection(s) if possible, null on error - private static async Task<(string, Dictionary>)> GetCopyProtection(Drive drive, Core.Data.Options options, IProgress progress = null) +#if NET48 + private static async Task<(string, Dictionary>)> GetCopyProtection(Drive drive, Data.Options options, IProgress progress = null) +#else + private static async Task<(string?, Dictionary>?)> GetCopyProtection(Drive? drive, Data.Options options, IProgress? progress = null) +#endif { if (options.ScanForProtection && drive != null) { - (var protection, string _) = await Protection.RunProtectionScanOnPath($"{drive.Letter}:\\", options, progress); + (var protection, _) = await Protection.RunProtectionScanOnPath($"{drive.Letter}:\\", options, progress); return (Protection.FormatProtections(protection), protection); } @@ -491,7 +545,11 @@ namespace MPF.Core /// file location /// True if should read as binary, false otherwise (default) /// Full text of the file, null on error +#if NET48 private static string GetFullFile(string filename, bool binary = false) +#else + private 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)) @@ -512,7 +570,11 @@ namespace MPF.Core /// /// String representing the combined hash data /// True if extraction was successful, false otherwise +#if NET48 private static bool GetISOHashValues(string hashData, out long size, out string crc32, out string md5, out string sha1) +#else + private 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; @@ -544,6 +606,7 @@ namespace MPF.Core private static void GetLibCryptDetected(SubmissionInfo info, string basePath) { bool? psLibCryptStatus = Protection.GetLibCryptDetected(basePath + ".sub"); + if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection(); if (psLibCryptStatus == true) { // Guard against false positives @@ -587,11 +650,24 @@ namespace MPF.Core /// Output filename to use as the base path /// Parameters object to use to derive log file paths /// True if the process succeeded, false otherwise +#if NET48 public static (bool, string) CompressLogFiles(string outputDirectory, string outputFilename, BaseParameters parameters) +#else + public static (bool, string) CompressLogFiles(string? outputDirectory, string outputFilename, BaseParameters? parameters) +#endif { + // If there are no parameters + if (parameters == null) + return (false, "No parameters provided!"); + // Prepare the necessary paths outputFilename = Path.GetFileNameWithoutExtension(outputFilename); - string combinedBase = Path.Combine(outputDirectory, outputFilename); + string combinedBase; + if (string.IsNullOrWhiteSpace(outputDirectory)) + combinedBase = outputFilename; + else + combinedBase = Path.Combine(outputDirectory, outputFilename); + string archiveName = combinedBase + "_logs.zip"; // Get the list of log files from the parameters object @@ -616,19 +692,30 @@ namespace MPF.Core } // Add the log files to the archive and delete the uncompressed file after +#if NET48 ZipArchive zf = null; +#else + ZipArchive? zf = null; +#endif try { zf = ZipFile.Open(archiveName, ZipArchiveMode.Create); foreach (string file in files) { + if (string.IsNullOrWhiteSpace(outputDirectory)) + { + zf.CreateEntryFromFile(file, file, CompressionLevel.Optimal); + } + else + { #if NET48 - string entryName = file.Substring(outputDirectory.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - zf.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal); + string entryName = file.Substring(outputDirectory.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + zf.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal); #else - string entryName = file[outputDirectory.Length..].TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - zf.CreateEntryFromFile(file, entryName, CompressionLevel.SmallestSize); + string entryName = file[outputDirectory.Length..].TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + zf.CreateEntryFromFile(file, entryName, CompressionLevel.SmallestSize); #endif + } // If the file is MPF-specific, don't delete if (mpfFiles.Contains(file)) @@ -659,7 +746,11 @@ namespace MPF.Core /// Information object that should contain normalized values /// Options object representing user-defined options /// List of strings representing each line of an output file, null on error - public static (List, string) FormatOutputData(SubmissionInfo info, Core.Data.Options options) +#if NET48 + public static (List, string) FormatOutputData(SubmissionInfo info, Data.Options options) +#else + public static (List?, string?) FormatOutputData(SubmissionInfo? info, Data.Options options) +#endif { // Check to see if the inputs are valid if (info == null) @@ -668,7 +759,7 @@ namespace MPF.Core try { // Sony-printed discs have layers in the opposite order - var system = info.CommonDiscInfo.System; + var system = info.CommonDiscInfo?.System; bool reverseOrder = system.HasReversedRingcodes(); // Preamble for submission @@ -682,120 +773,120 @@ namespace MPF.Core // Common Disc Info section output.Add("Common Disc Info:"); - AddIfExists(output, Template.TitleField, info.CommonDiscInfo.Title, 1); - AddIfExists(output, Template.ForeignTitleField, info.CommonDiscInfo.ForeignTitleNonLatin, 1); - AddIfExists(output, Template.DiscNumberField, info.CommonDiscInfo.DiscNumberLetter, 1); - AddIfExists(output, Template.DiscTitleField, info.CommonDiscInfo.DiscTitle, 1); - AddIfExists(output, Template.SystemField, info.CommonDiscInfo.System.LongName(), 1); + AddIfExists(output, Template.TitleField, info.CommonDiscInfo?.Title, 1); + AddIfExists(output, Template.ForeignTitleField, info.CommonDiscInfo?.ForeignTitleNonLatin, 1); + AddIfExists(output, Template.DiscNumberField, info.CommonDiscInfo?.DiscNumberLetter, 1); + AddIfExists(output, Template.DiscTitleField, info.CommonDiscInfo?.DiscTitle, 1); + AddIfExists(output, Template.SystemField, info.CommonDiscInfo?.System.LongName(), 1); AddIfExists(output, Template.MediaTypeField, GetFixedMediaType( - info.CommonDiscInfo.Media.ToMediaType(), - info.SizeAndChecksums.PICIdentifier, - info.SizeAndChecksums.Size, - info.SizeAndChecksums.Layerbreak, - info.SizeAndChecksums.Layerbreak2, - info.SizeAndChecksums.Layerbreak3), + info.CommonDiscInfo?.Media.ToMediaType(), + info.SizeAndChecksums?.PICIdentifier, + info.SizeAndChecksums?.Size, + info.SizeAndChecksums?.Layerbreak, + info.SizeAndChecksums?.Layerbreak2, + info.SizeAndChecksums?.Layerbreak3), 1); - AddIfExists(output, Template.CategoryField, info.CommonDiscInfo.Category.LongName(), 1); + AddIfExists(output, Template.CategoryField, info.CommonDiscInfo?.Category.LongName(), 1); AddIfExists(output, Template.FullyMatchingIDField, info.FullyMatchedID?.ToString(), 1); AddIfExists(output, Template.PartiallyMatchingIDsField, info.PartiallyMatchedIDs, 1); - AddIfExists(output, Template.RegionField, info.CommonDiscInfo.Region.LongName() ?? "SPACE! (CHANGE THIS)", 1); - AddIfExists(output, Template.LanguagesField, (info.CommonDiscInfo.Languages ?? new Language?[] { null }).Select(l => l.LongName() ?? "SILENCE! (CHANGE THIS)").ToArray(), 1); - AddIfExists(output, Template.PlaystationLanguageSelectionViaField, (info.CommonDiscInfo.LanguageSelection ?? Array.Empty()).Select(l => l.LongName()).ToArray(), 1); - AddIfExists(output, Template.DiscSerialField, info.CommonDiscInfo.Serial, 1); + AddIfExists(output, Template.RegionField, info.CommonDiscInfo?.Region.LongName() ?? "SPACE! (CHANGE THIS)", 1); + AddIfExists(output, Template.LanguagesField, (info.CommonDiscInfo?.Languages ?? new Language?[] { null }).Select(l => l.LongName() ?? "SILENCE! (CHANGE THIS)").ToArray(), 1); + AddIfExists(output, Template.PlaystationLanguageSelectionViaField, (info.CommonDiscInfo?.LanguageSelection ?? Array.Empty()).Select(l => l.LongName()).ToArray(), 1); + AddIfExists(output, Template.DiscSerialField, info.CommonDiscInfo?.Serial, 1); // All ringcode information goes in an indented area output.Add(""); output.Add("\tRingcode Information:"); output.Add(""); // If we have a triple-layer disc - if (info.SizeAndChecksums.Layerbreak3 != default) + if (info.SizeAndChecksums?.Layerbreak3 != default && info.SizeAndChecksums?.Layerbreak3 != default(long)) { - AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer0MasteringRing, 0); - AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer0MasteringSID, 0); - AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.ToolstampField, info.CommonDiscInfo.Layer0ToolstampMasteringCode, 0); - AddIfExists(output, "Data Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer0MouldSID, 0); - AddIfExists(output, "Data Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer0AdditionalMould, 0); + AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringRingField, info.CommonDiscInfo?.Layer0MasteringRing, 0); + AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringSIDField, info.CommonDiscInfo?.Layer0MasteringSID, 0); + AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.ToolstampField, info.CommonDiscInfo?.Layer0ToolstampMasteringCode, 0); + AddIfExists(output, "Data Side " + Template.MouldSIDField, info.CommonDiscInfo?.Layer0MouldSID, 0); + AddIfExists(output, "Data Side " + Template.AdditionalMouldField, info.CommonDiscInfo?.Layer0AdditionalMould, 0); - AddIfExists(output, "Layer 1 " + Template.MasteringRingField, info.CommonDiscInfo.Layer1MasteringRing, 0); - AddIfExists(output, "Layer 1 " + Template.MasteringSIDField, info.CommonDiscInfo.Layer1MasteringSID, 0); - AddIfExists(output, "Layer 1 " + Template.ToolstampField, info.CommonDiscInfo.Layer1ToolstampMasteringCode, 0); - AddIfExists(output, "Label Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer1MouldSID, 0); - AddIfExists(output, "Label Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer1AdditionalMould, 0); + AddIfExists(output, "Layer 1 " + Template.MasteringRingField, info.CommonDiscInfo?.Layer1MasteringRing, 0); + AddIfExists(output, "Layer 1 " + Template.MasteringSIDField, info.CommonDiscInfo?.Layer1MasteringSID, 0); + AddIfExists(output, "Layer 1 " + Template.ToolstampField, info.CommonDiscInfo?.Layer1ToolstampMasteringCode, 0); + AddIfExists(output, "Label Side " + Template.MouldSIDField, info.CommonDiscInfo?.Layer1MouldSID, 0); + AddIfExists(output, "Label Side " + Template.AdditionalMouldField, info.CommonDiscInfo?.Layer1AdditionalMould, 0); - AddIfExists(output, "Layer 2 " + Template.MasteringRingField, info.CommonDiscInfo.Layer2MasteringRing, 0); - AddIfExists(output, "Layer 2 " + Template.MasteringSIDField, info.CommonDiscInfo.Layer2MasteringSID, 0); - AddIfExists(output, "Layer 2 " + Template.ToolstampField, info.CommonDiscInfo.Layer2ToolstampMasteringCode, 0); + AddIfExists(output, "Layer 2 " + Template.MasteringRingField, info.CommonDiscInfo?.Layer2MasteringRing, 0); + AddIfExists(output, "Layer 2 " + Template.MasteringSIDField, info.CommonDiscInfo?.Layer2MasteringSID, 0); + AddIfExists(output, "Layer 2 " + Template.ToolstampField, info.CommonDiscInfo?.Layer2ToolstampMasteringCode, 0); - AddIfExists(output, (reverseOrder ? "Layer 3 (Inner) " : "Layer 3 (Outer) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer3MasteringRing, 0); - AddIfExists(output, (reverseOrder ? "Layer 3 (Inner) " : "Layer 3 (Outer) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer3MasteringSID, 0); - AddIfExists(output, (reverseOrder ? "Layer 3 (Inner) " : "Layer 3 (Outer) ") + Template.ToolstampField, info.CommonDiscInfo.Layer3ToolstampMasteringCode, 0); + AddIfExists(output, (reverseOrder ? "Layer 3 (Inner) " : "Layer 3 (Outer) ") + Template.MasteringRingField, info.CommonDiscInfo?.Layer3MasteringRing, 0); + AddIfExists(output, (reverseOrder ? "Layer 3 (Inner) " : "Layer 3 (Outer) ") + Template.MasteringSIDField, info.CommonDiscInfo?.Layer3MasteringSID, 0); + AddIfExists(output, (reverseOrder ? "Layer 3 (Inner) " : "Layer 3 (Outer) ") + Template.ToolstampField, info.CommonDiscInfo?.Layer3ToolstampMasteringCode, 0); } // If we have a triple-layer disc - else if (info.SizeAndChecksums.Layerbreak2 != default) + else if (info.SizeAndChecksums?.Layerbreak2 != default && info.SizeAndChecksums?.Layerbreak2 != default(long)) { - AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer0MasteringRing, 0); - AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer0MasteringSID, 0); - AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.ToolstampField, info.CommonDiscInfo.Layer0ToolstampMasteringCode, 0); - AddIfExists(output, "Data Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer0MouldSID, 0); - AddIfExists(output, "Data Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer0AdditionalMould, 0); + AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringRingField, info.CommonDiscInfo?.Layer0MasteringRing, 0); + AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringSIDField, info.CommonDiscInfo?.Layer0MasteringSID, 0); + AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.ToolstampField, info.CommonDiscInfo?.Layer0ToolstampMasteringCode, 0); + AddIfExists(output, "Data Side " + Template.MouldSIDField, info.CommonDiscInfo?.Layer0MouldSID, 0); + AddIfExists(output, "Data Side " + Template.AdditionalMouldField, info.CommonDiscInfo?.Layer0AdditionalMould, 0); - AddIfExists(output, "Layer 1 " + Template.MasteringRingField, info.CommonDiscInfo.Layer1MasteringRing, 0); - AddIfExists(output, "Layer 1 " + Template.MasteringSIDField, info.CommonDiscInfo.Layer1MasteringSID, 0); - AddIfExists(output, "Layer 1 " + Template.ToolstampField, info.CommonDiscInfo.Layer1ToolstampMasteringCode, 0); - AddIfExists(output, "Label Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer1MouldSID, 0); - AddIfExists(output, "Label Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer1AdditionalMould, 0); + AddIfExists(output, "Layer 1 " + Template.MasteringRingField, info.CommonDiscInfo?.Layer1MasteringRing, 0); + AddIfExists(output, "Layer 1 " + Template.MasteringSIDField, info.CommonDiscInfo?.Layer1MasteringSID, 0); + AddIfExists(output, "Layer 1 " + Template.ToolstampField, info.CommonDiscInfo?.Layer1ToolstampMasteringCode, 0); + AddIfExists(output, "Label Side " + Template.MouldSIDField, info.CommonDiscInfo?.Layer1MouldSID, 0); + AddIfExists(output, "Label Side " + Template.AdditionalMouldField, info.CommonDiscInfo?.Layer1AdditionalMould, 0); - AddIfExists(output, (reverseOrder ? "Layer 2 (Inner) " : "Layer 2 (Outer) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer2MasteringRing, 0); - AddIfExists(output, (reverseOrder ? "Layer 2 (Inner) " : "Layer 2 (Outer) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer2MasteringSID, 0); - AddIfExists(output, (reverseOrder ? "Layer 2 (Inner) " : "Layer 2 (Outer) ") + Template.ToolstampField, info.CommonDiscInfo.Layer2ToolstampMasteringCode, 0); + AddIfExists(output, (reverseOrder ? "Layer 2 (Inner) " : "Layer 2 (Outer) ") + Template.MasteringRingField, info.CommonDiscInfo?.Layer2MasteringRing, 0); + AddIfExists(output, (reverseOrder ? "Layer 2 (Inner) " : "Layer 2 (Outer) ") + Template.MasteringSIDField, info.CommonDiscInfo?.Layer2MasteringSID, 0); + AddIfExists(output, (reverseOrder ? "Layer 2 (Inner) " : "Layer 2 (Outer) ") + Template.ToolstampField, info.CommonDiscInfo?.Layer2ToolstampMasteringCode, 0); } // If we have a dual-layer disc - else if (info.SizeAndChecksums.Layerbreak != default) + else if (info.SizeAndChecksums?.Layerbreak != default && info.SizeAndChecksums?.Layerbreak != default(long)) { - AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer0MasteringRing, 0); - AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer0MasteringSID, 0); - AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.ToolstampField, info.CommonDiscInfo.Layer0ToolstampMasteringCode, 0); - AddIfExists(output, "Data Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer0MouldSID, 0); - AddIfExists(output, "Data Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer0AdditionalMould, 0); + AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringRingField, info.CommonDiscInfo?.Layer0MasteringRing, 0); + AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringSIDField, info.CommonDiscInfo?.Layer0MasteringSID, 0); + AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.ToolstampField, info.CommonDiscInfo?.Layer0ToolstampMasteringCode, 0); + AddIfExists(output, "Data Side " + Template.MouldSIDField, info.CommonDiscInfo?.Layer0MouldSID, 0); + AddIfExists(output, "Data Side " + Template.AdditionalMouldField, info.CommonDiscInfo?.Layer0AdditionalMould, 0); - AddIfExists(output, (reverseOrder ? "Layer 1 (Inner) " : "Layer 1 (Outer) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer1MasteringRing, 0); - AddIfExists(output, (reverseOrder ? "Layer 1 (Inner) " : "Layer 1 (Outer) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer1MasteringSID, 0); - AddIfExists(output, (reverseOrder ? "Layer 1 (Inner) " : "Layer 1 (Outer) ") + Template.ToolstampField, info.CommonDiscInfo.Layer1ToolstampMasteringCode, 0); - AddIfExists(output, "Label Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer1MouldSID, 0); - AddIfExists(output, "Label Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer1AdditionalMould, 0); + AddIfExists(output, (reverseOrder ? "Layer 1 (Inner) " : "Layer 1 (Outer) ") + Template.MasteringRingField, info.CommonDiscInfo?.Layer1MasteringRing, 0); + AddIfExists(output, (reverseOrder ? "Layer 1 (Inner) " : "Layer 1 (Outer) ") + Template.MasteringSIDField, info.CommonDiscInfo?.Layer1MasteringSID, 0); + AddIfExists(output, (reverseOrder ? "Layer 1 (Inner) " : "Layer 1 (Outer) ") + Template.ToolstampField, info.CommonDiscInfo?.Layer1ToolstampMasteringCode, 0); + AddIfExists(output, "Label Side " + Template.MouldSIDField, info.CommonDiscInfo?.Layer1MouldSID, 0); + AddIfExists(output, "Label Side " + Template.AdditionalMouldField, info.CommonDiscInfo?.Layer1AdditionalMould, 0); } // If we have a single-layer disc else { - AddIfExists(output, "Data Side " + Template.MasteringRingField, info.CommonDiscInfo.Layer0MasteringRing, 0); - AddIfExists(output, "Data Side " + Template.MasteringSIDField, info.CommonDiscInfo.Layer0MasteringSID, 0); - AddIfExists(output, "Data Side " + Template.ToolstampField, info.CommonDiscInfo.Layer0ToolstampMasteringCode, 0); - AddIfExists(output, "Data Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer0MouldSID, 0); - AddIfExists(output, "Data Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer0AdditionalMould, 0); + AddIfExists(output, "Data Side " + Template.MasteringRingField, info.CommonDiscInfo?.Layer0MasteringRing, 0); + AddIfExists(output, "Data Side " + Template.MasteringSIDField, info.CommonDiscInfo?.Layer0MasteringSID, 0); + AddIfExists(output, "Data Side " + Template.ToolstampField, info.CommonDiscInfo?.Layer0ToolstampMasteringCode, 0); + AddIfExists(output, "Data Side " + Template.MouldSIDField, info.CommonDiscInfo?.Layer0MouldSID, 0); + AddIfExists(output, "Data Side " + Template.AdditionalMouldField, info.CommonDiscInfo?.Layer0AdditionalMould, 0); - AddIfExists(output, "Label Side " + Template.MasteringRingField, info.CommonDiscInfo.Layer1MasteringRing, 0); - AddIfExists(output, "Label Side " + Template.MasteringSIDField, info.CommonDiscInfo.Layer1MasteringSID, 0); - AddIfExists(output, "Label Side " + Template.ToolstampField, info.CommonDiscInfo.Layer1ToolstampMasteringCode, 0); - AddIfExists(output, "Label Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer1MouldSID, 0); - AddIfExists(output, "Label Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer1AdditionalMould, 0); + AddIfExists(output, "Label Side " + Template.MasteringRingField, info.CommonDiscInfo?.Layer1MasteringRing, 0); + AddIfExists(output, "Label Side " + Template.MasteringSIDField, info.CommonDiscInfo?.Layer1MasteringSID, 0); + AddIfExists(output, "Label Side " + Template.ToolstampField, info.CommonDiscInfo?.Layer1ToolstampMasteringCode, 0); + AddIfExists(output, "Label Side " + Template.MouldSIDField, info.CommonDiscInfo?.Layer1MouldSID, 0); + AddIfExists(output, "Label Side " + Template.AdditionalMouldField, info.CommonDiscInfo?.Layer1AdditionalMould, 0); } output.Add(""); - AddIfExists(output, Template.BarcodeField, info.CommonDiscInfo.Barcode, 1); - AddIfExists(output, Template.EXEDateBuildDate, info.CommonDiscInfo.EXEDateBuildDate, 1); - AddIfExists(output, Template.ErrorCountField, info.CommonDiscInfo.ErrorsCount, 1); - AddIfExists(output, Template.CommentsField, info.CommonDiscInfo.Comments.Trim(), 1); - AddIfExists(output, Template.ContentsField, info.CommonDiscInfo.Contents.Trim(), 1); + AddIfExists(output, Template.BarcodeField, info.CommonDiscInfo?.Barcode, 1); + AddIfExists(output, Template.EXEDateBuildDate, info.CommonDiscInfo?.EXEDateBuildDate, 1); + AddIfExists(output, Template.ErrorCountField, info.CommonDiscInfo?.ErrorsCount, 1); + AddIfExists(output, Template.CommentsField, info.CommonDiscInfo?.Comments?.Trim(), 1); + AddIfExists(output, Template.ContentsField, info.CommonDiscInfo?.Contents?.Trim(), 1); // Version and Editions section output.Add(""); output.Add("Version and Editions:"); - AddIfExists(output, Template.VersionField, info.VersionAndEditions.Version, 1); - AddIfExists(output, Template.EditionField, info.VersionAndEditions.OtherEditions, 1); + AddIfExists(output, Template.VersionField, info.VersionAndEditions?.Version, 1); + AddIfExists(output, Template.EditionField, info.VersionAndEditions?.OtherEditions, 1); // EDC section - if (info.CommonDiscInfo.System == RedumpSystem.SonyPlayStation) + if (info.CommonDiscInfo?.System == RedumpSystem.SonyPlayStation) { output.Add(""); output.Add("EDC:"); - AddIfExists(output, Template.PlayStationEDCField, info.EDC.EDC.LongName(), 1); + AddIfExists(output, Template.PlayStationEDCField, info.EDC?.EDC.LongName(), 1); } // Parent/Clone Relationship section @@ -804,7 +895,7 @@ namespace MPF.Core // AddIfExists(output, Template.RegionalParentField, info.RegionalParent.ToString()); // Extras section - if (info.Extras.PVD != null || info.Extras.PIC != null || info.Extras.BCA != null || info.Extras.SecuritySectorRanges != null) + if (info.Extras?.PVD != null || info.Extras?.PIC != null || info.Extras?.BCA != null || info.Extras?.SecuritySectorRanges != null) { output.Add(""); output.Add("Extras:"); AddIfExists(output, Template.PVDField, info.Extras.PVD?.Trim(), 1); @@ -817,14 +908,14 @@ namespace MPF.Core } // Copy Protection section - if (!string.IsNullOrWhiteSpace(info.CopyProtection.Protection) - || (info.CopyProtection.AntiModchip != null && info.CopyProtection.AntiModchip != YesNo.NULL) - || (info.CopyProtection.LibCrypt != null && info.CopyProtection.LibCrypt != YesNo.NULL) - || !string.IsNullOrWhiteSpace(info.CopyProtection.LibCryptData) - || !string.IsNullOrWhiteSpace(info.CopyProtection.SecuROMData)) + if (!string.IsNullOrWhiteSpace(info.CopyProtection?.Protection) + || (info.CopyProtection?.AntiModchip != null && info.CopyProtection.AntiModchip != YesNo.NULL) + || (info.CopyProtection?.LibCrypt != null && info.CopyProtection.LibCrypt != YesNo.NULL) + || !string.IsNullOrWhiteSpace(info.CopyProtection?.LibCryptData) + || !string.IsNullOrWhiteSpace(info.CopyProtection?.SecuROMData)) { output.Add(""); output.Add("Copy Protection:"); - if (info.CommonDiscInfo.System == RedumpSystem.SonyPlayStation) + if (info.CommonDiscInfo?.System == RedumpSystem.SonyPlayStation) { AddIfExists(output, Template.PlayStationAntiModchipField, info.CopyProtection.AntiModchip.LongName(), 1); AddIfExists(output, Template.PlayStationLibCryptField, info.CopyProtection.LibCrypt.LongName(), 1); @@ -841,12 +932,12 @@ namespace MPF.Core // AddIfExists(output, Template.OtherDumpersField, info.OtherDumpers); // Tracks and Write Offsets section - if (!string.IsNullOrWhiteSpace(info.TracksAndWriteOffsets.ClrMameProData)) + if (!string.IsNullOrWhiteSpace(info.TracksAndWriteOffsets?.ClrMameProData)) { output.Add(""); output.Add("Tracks and Write Offsets:"); AddIfExists(output, Template.DATField, info.TracksAndWriteOffsets.ClrMameProData + "\n", 1); AddIfExists(output, Template.CuesheetField, info.TracksAndWriteOffsets.Cuesheet, 1); - string offset = info.TracksAndWriteOffsets.OtherWriteOffsets; + var offset = info.TracksAndWriteOffsets.OtherWriteOffsets; if (Int32.TryParse(offset, out int i)) offset = i.ToString("+#;-#;0"); @@ -859,29 +950,33 @@ namespace MPF.Core // Gross hack because of automatic layerbreaks in Redump if (!options.EnableRedumpCompatibility - || (info.CommonDiscInfo.Media.ToMediaType() != MediaType.BluRay - && !info.CommonDiscInfo.System.IsXGD())) + || (info.CommonDiscInfo?.Media.ToMediaType() != MediaType.BluRay + && info.CommonDiscInfo?.System.IsXGD() == false)) { - AddIfExists(output, Template.LayerbreakField, (info.SizeAndChecksums.Layerbreak == default ? null : info.SizeAndChecksums.Layerbreak.ToString()), 1); + AddIfExists(output, Template.LayerbreakField, (info.SizeAndChecksums?.Layerbreak == default && info.SizeAndChecksums?.Layerbreak != default(long) ? null : info.SizeAndChecksums?.Layerbreak.ToString()), 1); } - AddIfExists(output, Template.SizeField, info.SizeAndChecksums.Size.ToString(), 1); - AddIfExists(output, Template.CRC32Field, info.SizeAndChecksums.CRC32, 1); - AddIfExists(output, Template.MD5Field, info.SizeAndChecksums.MD5, 1); - AddIfExists(output, Template.SHA1Field, info.SizeAndChecksums.SHA1, 1); + AddIfExists(output, Template.SizeField, info.SizeAndChecksums?.Size.ToString(), 1); + AddIfExists(output, Template.CRC32Field, info.SizeAndChecksums?.CRC32, 1); + AddIfExists(output, Template.MD5Field, info.SizeAndChecksums?.MD5, 1); + AddIfExists(output, Template.SHA1Field, info.SizeAndChecksums?.SHA1, 1); } // Dumping Info section output.Add(""); output.Add("Dumping Info:"); - AddIfExists(output, Template.DumpingProgramField, info.DumpingInfo.DumpingProgram, 1); - AddIfExists(output, Template.DumpingDateField, info.DumpingInfo.DumpingDate, 1); - AddIfExists(output, Template.DumpingDriveManufacturer, info.DumpingInfo.Manufacturer, 1); - AddIfExists(output, Template.DumpingDriveModel, info.DumpingInfo.Model, 1); - AddIfExists(output, Template.DumpingDriveFirmware, info.DumpingInfo.Firmware, 1); - AddIfExists(output, Template.ReportedDiscType, info.DumpingInfo.ReportedDiscType, 1); + AddIfExists(output, Template.DumpingProgramField, info.DumpingInfo?.DumpingProgram, 1); + AddIfExists(output, Template.DumpingDateField, info.DumpingInfo?.DumpingDate, 1); + AddIfExists(output, Template.DumpingDriveManufacturer, info.DumpingInfo?.Manufacturer, 1); + AddIfExists(output, Template.DumpingDriveModel, info.DumpingInfo?.Model, 1); + AddIfExists(output, Template.DumpingDriveFirmware, info.DumpingInfo?.Firmware, 1); + AddIfExists(output, Template.ReportedDiscType, info.DumpingInfo?.ReportedDiscType, 1); // Make sure there aren't any instances of two blank lines in a row +#if NET48 string last = null; +#else + string? last = null; +#endif for (int i = 0; i < output.Count;) { if (output[i] == last && string.IsNullOrWhiteSpace(last)) @@ -914,7 +1009,11 @@ namespace MPF.Core /// Third layerbreak value, as applicable /// String representation of the media, including layer specification /// TODO: Figure out why we have this and NormalizeDiscType as well - public static string GetFixedMediaType(MediaType? mediaType, string picIdentifier, long size, long layerbreak, long layerbreak2, long layerbreak3) +#if NET48 + public static string GetFixedMediaType(MediaType? mediaType, string picIdentifier, long? size, long? layerbreak, long? layerbreak2, long? layerbreak3) +#else + public static string? GetFixedMediaType(MediaType? mediaType, string? picIdentifier, long? size, long? layerbreak, long? layerbreak2, long? layerbreak3) +#endif { switch (mediaType) { @@ -925,15 +1024,15 @@ namespace MPF.Core return $"{mediaType.LongName()}-5"; case MediaType.BluRay: - if (layerbreak3 != default) + if (layerbreak3 != default && layerbreak3 != default(long)) return $"{mediaType.LongName()}-128"; - else if (layerbreak2 != default) + else if (layerbreak2 != default && layerbreak2 != default(long)) return $"{mediaType.LongName()}-100"; - else if (layerbreak != default && picIdentifier == SabreTools.Models.PIC.Constants.DiscTypeIdentifierROMUltra) + else if (layerbreak != default && layerbreak != default(long) && picIdentifier == SabreTools.Models.PIC.Constants.DiscTypeIdentifierROMUltra) return $"{mediaType.LongName()}-66"; - else if (layerbreak != default && size > 53_687_063_712) + else if (layerbreak != default && layerbreak != default(long) && size > 53_687_063_712) return $"{mediaType.LongName()}-66"; - else if (layerbreak != default) + else if (layerbreak != default && layerbreak != default(long)) return $"{mediaType.LongName()}-50"; else if (picIdentifier == SabreTools.Models.PIC.Constants.DiscTypeIdentifierROMUltra) return $"{mediaType.LongName()}-33"; @@ -943,7 +1042,7 @@ namespace MPF.Core return $"{mediaType.LongName()}-25"; case MediaType.UMD: - if (layerbreak != default) + if (layerbreak != default && layerbreak != default(long)) return $"{mediaType.LongName()}-DL"; else return $"{mediaType.LongName()}-SL"; @@ -957,8 +1056,16 @@ namespace MPF.Core /// Process any fields that have to be combined /// /// Information object to normalize +#if NET48 public static void ProcessSpecialFields(SubmissionInfo info) +#else + public static void ProcessSpecialFields(SubmissionInfo? info) +#endif { + // If there is no submission info + if (info == null) + return; + // Process the comments field if (info.CommonDiscInfo?.CommentsSpecialFields != null && info.CommonDiscInfo.CommentsSpecialFields?.Any() == true) { @@ -1016,7 +1123,11 @@ namespace MPF.Core /// Output folder to write to /// Preformatted list of lines to write out to the file /// True on success, false on error +#if NET48 public static (bool, string) WriteOutputData(string outputDirectory, List lines) +#else + public static (bool, string) WriteOutputData(string? outputDirectory, List? lines) +#endif { // Check to see if the inputs are valid if (lines == null) @@ -1025,7 +1136,14 @@ namespace MPF.Core // Now write out to a generic file try { - using (var sw = new StreamWriter(File.Open(Path.Combine(outputDirectory, "!submissionInfo.txt"), FileMode.Create, FileAccess.Write))) + // Get the file path + string path; + if (string.IsNullOrWhiteSpace(outputDirectory)) + path = "!submissionInfo.txt"; + else + path = Path.Combine(outputDirectory, "!submissionInfo.txt"); + + using (var sw = new StreamWriter(File.Open(path, FileMode.Create, FileAccess.Write))) { foreach (string line in lines) { @@ -1048,7 +1166,11 @@ namespace MPF.Core /// SubmissionInfo object representing the JSON to write out to the file /// True if artifacts were included, false otherwise /// True on success, false on error +#if NET48 public static bool WriteOutputData(string outputDirectory, SubmissionInfo info, bool includedArtifacts) +#else + public static bool WriteOutputData(string? outputDirectory, SubmissionInfo? info, bool includedArtifacts) +#endif { // Check to see if the input is valid if (info == null) @@ -1063,7 +1185,13 @@ namespace MPF.Core // If we included artifacts, write to a GZip-compressed file if (includedArtifacts) { - using (var fs = File.Create(Path.Combine(outputDirectory, "!submissionInfo.json.gz"))) + string file; + if (string.IsNullOrWhiteSpace(outputDirectory)) + file = "!submissionInfo.json.gz"; + else + file = Path.Combine(outputDirectory, "!submissionInfo.json.gz"); + + using (var fs = File.Create(file)) using (var gs = new GZipStream(fs, CompressionMode.Compress)) { gs.Write(jsonBytes, 0, jsonBytes.Length); @@ -1073,7 +1201,13 @@ namespace MPF.Core // Otherwise, write out to a normal JSON else { - using (var fs = File.Create(Path.Combine(outputDirectory, "!submissionInfo.json"))) + string file; + if (string.IsNullOrWhiteSpace(outputDirectory)) + file = "!submissionInfo.json"; + else + file = Path.Combine(outputDirectory, "!submissionInfo.json"); + + using (var fs = File.Create(file)) { fs.Write(jsonBytes, 0, jsonBytes.Length); } @@ -1094,7 +1228,11 @@ namespace MPF.Core /// Output folder to write to /// SubmissionInfo object containing the protection information /// True on success, false on error +#if NET48 public static bool WriteProtectionData(string outputDirectory, SubmissionInfo info) +#else + public static bool WriteProtectionData(string? outputDirectory, SubmissionInfo? info) +#endif { // Check to see if the inputs are valid if (info?.CopyProtection?.FullProtections == null || !info.CopyProtection.FullProtections.Any()) @@ -1103,11 +1241,20 @@ namespace MPF.Core // Now write out to a generic file try { - using (var sw = new StreamWriter(File.Open(Path.Combine(outputDirectory, "!protectionInfo.txt"), FileMode.Create, FileAccess.Write))) + string file; + if (string.IsNullOrWhiteSpace(outputDirectory)) + file = "!protectionInfo.txt"; + else + file = Path.Combine(outputDirectory, "!protectionInfo.txt"); + + using (var sw = new StreamWriter(File.Open(file, FileMode.Create, FileAccess.Write))) { foreach (var kvp in info.CopyProtection.FullProtections) { - sw.WriteLine($"{kvp.Key}: {string.Join(", ", kvp.Value)}"); + if (kvp.Value == null) + sw.WriteLine($"{kvp.Key}: None"); + else + sw.WriteLine($"{kvp.Key}: {string.Join(", ", kvp.Value)}"); } } } @@ -1127,7 +1274,11 @@ namespace MPF.Core /// Name of the output key to write /// Name of the output value to write /// Number of tabs to indent the line +#if NET48 private static void AddIfExists(List output, string key, string value, int indent) +#else + private static void AddIfExists(List output, string key, string? value, int indent) +#endif { // If there's no valid value to write if (value == null) @@ -1181,7 +1332,11 @@ namespace MPF.Core /// Name of the output key to write /// Name of the output value to write /// Number of tabs to indent the line +#if NET48 private static void AddIfExists(List output, string key, string[] value, int indent) +#else + private static void AddIfExists(List output, string key, string?[]? value, int indent) +#endif { // If there's no valid value to write if (value == null || value.Length == 0) @@ -1197,7 +1352,11 @@ namespace MPF.Core /// Name of the output key to write /// Name of the output value to write /// Number of tabs to indent the line +#if NET48 private static void AddIfExists(List output, string key, List value, int indent) +#else + private static void AddIfExists(List output, string key, List? value, int indent) +#endif { // If there's no valid value to write if (value == null || value.Count == 0) @@ -1211,18 +1370,36 @@ namespace MPF.Core /// /// Output folder to write to /// List of all log file paths, empty otherwise +#if NET48 private static List GetGeneratedFilePaths(string outputDirectory) +#else + private static List GetGeneratedFilePaths(string? outputDirectory) +#endif { var files = new List(); - if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.txt"))) - files.Add(Path.Combine(outputDirectory, "!submissionInfo.txt")); - if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.json"))) - files.Add(Path.Combine(outputDirectory, "!submissionInfo.json")); - if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.json.gz"))) - files.Add(Path.Combine(outputDirectory, "!submissionInfo.json.gz")); - if (File.Exists(Path.Combine(outputDirectory, "!protectionInfo.txt"))) - files.Add(Path.Combine(outputDirectory, "!protectionInfo.txt")); + if (string.IsNullOrWhiteSpace(outputDirectory)) + { + if (File.Exists("!submissionInfo.txt")) + files.Add("!submissionInfo.txt"); + if (File.Exists("!submissionInfo.json")) + files.Add("!submissionInfo.json"); + if (File.Exists("!submissionInfo.json.gz")) + files.Add("!submissionInfo.json.gz"); + if (File.Exists("!protectionInfo.txt")) + files.Add("!protectionInfo.txt"); + } + else + { + if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.txt"))) + files.Add(Path.Combine(outputDirectory, "!submissionInfo.txt")); + if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.json"))) + files.Add(Path.Combine(outputDirectory, "!submissionInfo.json")); + if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.json.gz"))) + files.Add(Path.Combine(outputDirectory, "!submissionInfo.json.gz")); + if (File.Exists(Path.Combine(outputDirectory, "!protectionInfo.txt"))) + files.Add(Path.Combine(outputDirectory, "!protectionInfo.txt")); + } return files; } @@ -1632,7 +1809,7 @@ namespace MPF.Core public static void NormalizeDiscType(SubmissionInfo info) { // If we have nothing valid, do nothing - if (info?.CommonDiscInfo?.Media == null) + if (info?.CommonDiscInfo?.Media == null || info?.SizeAndChecksums == null) return; switch (info.CommonDiscInfo.Media) @@ -1709,18 +1886,24 @@ namespace MPF.Core // Try getting the combined path and returning that directly string fullPath = getFullPath ? Path.GetFullPath(path) : path; - string fullDirectory = Path.GetDirectoryName(fullPath); + var fullDirectory = Path.GetDirectoryName(fullPath); string fullFile = Path.GetFileName(fullPath); // Remove invalid path characters - foreach (char c in Path.GetInvalidPathChars()) - fullDirectory = fullDirectory.Replace(c, '_'); + if (fullDirectory != null) + { + foreach (char c in Path.GetInvalidPathChars()) + fullDirectory = fullDirectory.Replace(c, '_'); + } // Remove invalid filename characters foreach (char c in Path.GetInvalidFileNameChars()) fullFile = fullFile.Replace(c, '_'); - return Path.Combine(fullDirectory, fullFile); + if (string.IsNullOrWhiteSpace(fullDirectory)) + return fullFile; + else + return Path.Combine(fullDirectory, fullFile); } catch { } @@ -1737,7 +1920,11 @@ namespace MPF.Core /// String containing the HTML disc data /// Filled SubmissionInfo object on success, null on error /// Not currently working +#if NET48 private static SubmissionInfo CreateFromID(string discData) +#else + private static SubmissionInfo? CreateFromID(string discData) +#endif { var info = new SubmissionInfo() { @@ -1760,12 +1947,16 @@ namespace MPF.Core return null; // Get the body node, if possible - XmlNode bodyNode = redumpPage["html"]?["body"]; + var bodyNode = redumpPage["html"]?["body"]; if (bodyNode == null || !bodyNode.HasChildNodes) return null; // Loop through and get the main node, if possible +#if NET48 XmlNode mainNode = null; +#else + XmlNode? mainNode = null; +#endif foreach (XmlNode tempNode in bodyNode.ChildNodes) { // We only care about div elements @@ -1836,12 +2027,16 @@ namespace MPF.Core if (gameInfoNode["th"] == null || gameInfoNode["td"] == null) continue; - XmlNode gameInfoNodeHeader = gameInfoNode["th"]; - XmlNode gameInfoNodeData = gameInfoNode["td"]; + var gameInfoNodeHeader = gameInfoNode["th"]; + var gameInfoNodeData = gameInfoNode["td"]; - if (string.Equals(gameInfoNodeHeader.InnerText, "System", StringComparison.OrdinalIgnoreCase)) + if (gameInfoNodeHeader == null || gameInfoNodeData == null) { - info.CommonDiscInfo.System = Extensions.ToRedumpSystem(gameInfoNodeData["a"]?.InnerText); + // No-op for invalid data + } + else if (string.Equals(gameInfoNodeHeader.InnerText, "System", StringComparison.OrdinalIgnoreCase)) + { + info.CommonDiscInfo.System = Extensions.ToRedumpSystem(gameInfoNodeData["a"]?.InnerText ?? string.Empty); } else if (string.Equals(gameInfoNodeHeader.InnerText, "Media", StringComparison.OrdinalIgnoreCase)) { @@ -1924,7 +2119,14 @@ namespace MPF.Core #else private async static Task FillFromId(RedumpHttpClient wc, SubmissionInfo info, int id, bool includeAllData) { - string discData = await wc.DownloadSingleSiteID(id); + // Ensure that required sections exist + if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection(); + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); + if (info.CommonDiscInfo.ContentsSpecialFields == null) info.CommonDiscInfo.ContentsSpecialFields = new Dictionary(); + if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection(); + if (info.DumpersAndStatus == null) info.DumpersAndStatus = new DumpersAndStatusSection(); + + var discData = await wc.DownloadSingleSiteID(id); if (string.IsNullOrEmpty(discData)) return false; #endif @@ -2031,7 +2233,7 @@ namespace MPF.Core { // Start with any currently listed dumpers var tempDumpers = new List(); - if (info.DumpersAndStatus.Dumpers.Length > 0) + if (info.DumpersAndStatus.Dumpers != null && info.DumpersAndStatus.Dumpers.Length > 0) { foreach (string dumper in info.DumpersAndStatus.Dumpers) tempDumpers.Add(dumper); @@ -2094,7 +2296,8 @@ namespace MPF.Core continue; // If the line doesn't contain this tag, just skip - if (!commentLine.Contains(siteCode.ShortName())) + var shortName = siteCode.ShortName(); + if (shortName == null || !commentLine.Contains(shortName)) continue; // Mark as having found a tag @@ -2140,11 +2343,11 @@ namespace MPF.Core // If we don't already have this site code, add it to the dictionary if (!info.CommonDiscInfo.CommentsSpecialFields.ContainsKey(siteCode.Value)) - info.CommonDiscInfo.CommentsSpecialFields[siteCode.Value] = $"(VERIFY THIS) {commentLine.Replace(siteCode.ShortName(), string.Empty).Trim()}"; + info.CommonDiscInfo.CommentsSpecialFields[siteCode.Value] = $"(VERIFY THIS) {commentLine.Replace(shortName, string.Empty).Trim()}"; // Otherwise, append the value to the existing key else - info.CommonDiscInfo.CommentsSpecialFields[siteCode.Value] += $", {commentLine.Replace(siteCode.ShortName(), string.Empty).Trim()}"; + info.CommonDiscInfo.CommentsSpecialFields[siteCode.Value] += $", {commentLine.Replace(shortName, string.Empty).Trim()}"; break; } @@ -2218,7 +2421,8 @@ namespace MPF.Core continue; // If the line doesn't contain this tag, just skip - if (!contentLine.Contains(siteCode.ShortName())) + var shortName = siteCode.ShortName(); + if (shortName == null || !contentLine.Contains(shortName)) continue; // Cache the current site code @@ -2226,7 +2430,7 @@ namespace MPF.Core // If we don't already have this site code, add it to the dictionary if (!info.CommonDiscInfo.ContentsSpecialFields.ContainsKey(siteCode.Value)) - info.CommonDiscInfo.ContentsSpecialFields[siteCode.Value] = $"(VERIFY THIS) {contentLine.Replace(siteCode.ShortName(), string.Empty).Trim()}"; + info.CommonDiscInfo.ContentsSpecialFields[siteCode.Value] = $"(VERIFY THIS) {contentLine.Replace(shortName, string.Empty).Trim()}"; // A subset of tags can be multiline addToLast = IsMultiLine(siteCode); @@ -2288,12 +2492,17 @@ namespace MPF.Core /// Existing SubmissionInfo object to fill /// Optional result progress callback #if NET48 - private static bool FillFromRedump(Core.Data.Options options, SubmissionInfo info, IProgress resultProgress = null) + private static bool FillFromRedump(Data.Options options, SubmissionInfo info, IProgress resultProgress = null) #else - private async static Task FillFromRedump(Core.Data.Options options, SubmissionInfo info, IProgress resultProgress = null) + private async static Task FillFromRedump(Data.Options options, SubmissionInfo info, IProgress? resultProgress = null) #endif { + // If no username is provided + if (string.IsNullOrWhiteSpace(options.RedumpUsername) || string.IsNullOrWhiteSpace(options.RedumpPassword)) + return false; + // Set the current dumper based on username + if (info.DumpersAndStatus == null) info.DumpersAndStatus = new DumpersAndStatusSection(); info.DumpersAndStatus.Dumpers = new string[] { options.RedumpUsername }; info.PartiallyMatchedIDs = new List(); @@ -2322,13 +2531,17 @@ namespace MPF.Core // Setup the full-track checks bool allFound = true; +#if NET48 List fullyMatchedIDs = null; +#else + List? fullyMatchedIDs = null; +#endif // Loop through all of the hashdata to find matching IDs resultProgress?.Report(Result.Success("Finding disc matches on Redump...")); - string[] splitData = info.TracksAndWriteOffsets.ClrMameProData.TrimEnd('\n').Split('\n'); - int trackCount = splitData.Length; - foreach (string hashData in splitData) + var splitData = info.TracksAndWriteOffsets?.ClrMameProData?.TrimEnd('\n')?.Split('\n'); + int trackCount = splitData?.Length ?? 0; + foreach (string hashData in splitData ?? Array.Empty()) { // Catch any errant blank lines if (string.IsNullOrWhiteSpace(hashData)) @@ -2354,7 +2567,7 @@ namespace MPF.Core #if NET48 (bool singleFound, List foundIds) = ValidateSingleTrack(wc, info, hashData, resultProgress); #else - (bool singleFound, List foundIds) = await ValidateSingleTrack(wc, info, hashData, resultProgress); + (bool singleFound, var foundIds) = await ValidateSingleTrack(wc, info, hashData, resultProgress); #endif // Ensure that all tracks are found @@ -2376,12 +2589,12 @@ namespace MPF.Core } // If we don't have any matches but we have a universal hash - if (!info.PartiallyMatchedIDs.Any() && info.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.UniversalHash)) + if (!info.PartiallyMatchedIDs.Any() && info.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) == true) { #if NET48 (bool singleFound, List foundIds) = ValidateUniversalHash(wc, info, resultProgress); #else - (bool singleFound, List foundIds) = await ValidateUniversalHash(wc, info, resultProgress); + (bool singleFound, var foundIds) = await ValidateUniversalHash(wc, info, resultProgress); #endif // Ensure that the hash is found @@ -2405,12 +2618,12 @@ namespace MPF.Core .OrderBy(id => id) .ToList(); - resultProgress?.Report(Result.Success("Match finding complete! " + (fullyMatchedIDs.Count > 0 + resultProgress?.Report(Result.Success("Match finding complete! " + (fullyMatchedIDs != null && fullyMatchedIDs.Count > 0 ? "Fully Matched IDs: " + string.Join(",", fullyMatchedIDs) : "No matches found"))); // Exit early if one failed or there are no matched IDs - if (!allFound || fullyMatchedIDs.Count == 0) + if (!allFound || fullyMatchedIDs == null || fullyMatchedIDs.Count == 0) return false; // Find the first matched ID where the track count matches, we can grab a bunch of info from it @@ -2465,7 +2678,9 @@ namespace MPF.Core foreach (SiteCode? siteCode in Enum.GetValues(typeof(SiteCode))) { - text = text.Replace(siteCode.LongName(), siteCode.ShortName()); + var longname = siteCode.LongName(); + if (!string.IsNullOrEmpty(longname)) + text = text.Replace(longname, siteCode.ShortName()); } // For some outdated tags, we need to use alternate names @@ -2496,9 +2711,13 @@ namespace MPF.Core #if NET48 private static List ListSearchResults(RedumpWebClient wc, string query, bool filterForwardSlashes = true) #else - private async static Task> ListSearchResults(RedumpHttpClient wc, string query, bool filterForwardSlashes = true) + private async static Task?> ListSearchResults(RedumpHttpClient wc, string? query, bool filterForwardSlashes = true) #endif { + // If there is an invalid query + if (string.IsNullOrWhiteSpace(query)) + return null; + var ids = new List(); // Strip quotes @@ -2549,11 +2768,11 @@ namespace MPF.Core #if NET48 private static (bool, List) ValidateSingleTrack(RedumpWebClient wc, SubmissionInfo info, string hashData, IProgress resultProgress = null) #else - private async static Task<(bool, List)> ValidateSingleTrack(RedumpHttpClient wc, SubmissionInfo info, string hashData, IProgress resultProgress = null) + private async static Task<(bool, List?)> ValidateSingleTrack(RedumpHttpClient wc, SubmissionInfo info, string hashData, IProgress? resultProgress = null) #endif { // If the line isn't parseable, we can't validate - if (!GetISOHashValues(hashData, out long _, out string _, out string _, out string sha1)) + if (!GetISOHashValues(hashData, out long _, out var _, out var _, out var sha1)) { resultProgress?.Report(Result.Failure("Line could not be parsed for hash data")); return (false, null); @@ -2563,7 +2782,7 @@ namespace MPF.Core #if NET48 List newIds = ListSearchResults(wc, sha1); #else - List newIds = await ListSearchResults(wc, sha1); + var newIds = await ListSearchResults(wc, sha1); #endif // If we got null back, there was an error @@ -2578,7 +2797,7 @@ namespace MPF.Core return (false, null); // Join the list of found IDs to the existing list, if possible - if (info.PartiallyMatchedIDs.Any()) + if (info.PartiallyMatchedIDs != null && info.PartiallyMatchedIDs.Any()) info.PartiallyMatchedIDs.AddRange(newIds); else info.PartiallyMatchedIDs = newIds; @@ -2596,11 +2815,18 @@ namespace MPF.Core #if NET48 private static (bool, List) ValidateUniversalHash(RedumpWebClient wc, SubmissionInfo info, IProgress resultProgress = null) #else - private async static Task<(bool, List)> ValidateUniversalHash(RedumpHttpClient wc, SubmissionInfo info, IProgress resultProgress = null) + private async static Task<(bool, List?)> ValidateUniversalHash(RedumpHttpClient wc, SubmissionInfo info, IProgress? resultProgress = null) #endif { + // If we don't have special fields + if (info.CommonDiscInfo?.CommentsSpecialFields == null) + { + resultProgress?.Report(Result.Failure("Universal hash was missing")); + return (false, null); + } + // If we don't have a universal hash - string universalHash = info.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash]; + var universalHash = info.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash]; if (string.IsNullOrEmpty(universalHash)) { resultProgress?.Report(Result.Failure("Universal hash was missing")); @@ -2618,7 +2844,7 @@ namespace MPF.Core #if NET48 List newIds = ListSearchResults(wc, universalHash, filterForwardSlashes: false); #else - List newIds = await ListSearchResults(wc, universalHash, filterForwardSlashes: false); + var newIds = await ListSearchResults(wc, universalHash, filterForwardSlashes: false); #endif // If we got null back, there was an error @@ -2633,7 +2859,7 @@ namespace MPF.Core return (false, null); // Join the list of found IDs to the existing list, if possible - if (info.PartiallyMatchedIDs.Any()) + if (info.PartiallyMatchedIDs != null && info.PartiallyMatchedIDs.Any()) info.PartiallyMatchedIDs.AddRange(newIds); else info.PartiallyMatchedIDs = newIds; @@ -2658,7 +2884,7 @@ namespace MPF.Core #if NET48 string discData = wc.DownloadSingleSiteID(id); #else - string discData = await wc.DownloadSingleSiteID(id); + string? discData = await wc.DownloadSingleSiteID(id); #endif if (string.IsNullOrEmpty(discData)) return false; diff --git a/MPF.Core/MPF.Core.csproj b/MPF.Core/MPF.Core.csproj index 885c66d7..0a1b9734 100644 --- a/MPF.Core/MPF.Core.csproj +++ b/MPF.Core/MPF.Core.csproj @@ -9,6 +9,10 @@ true + + enable + + diff --git a/MPF.Core/Modules/Aaru/Parameters.cs b/MPF.Core/Modules/Aaru/Parameters.cs index 8ff145b4..14f77292 100644 --- a/MPF.Core/Modules/Aaru/Parameters.cs +++ b/MPF.Core/Modules/Aaru/Parameters.cs @@ -26,10 +26,18 @@ namespace MPF.Core.Modules.Aaru #region Generic Dumping Information /// +#if NET48 public override string InputPath => InputValue; +#else + public override string? InputPath => InputValue; +#endif /// +#if NET48 public override string OutputPath => OutputValue; +#else + public override string? OutputPath => OutputValue; +#endif /// 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); 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; } } /// +#if NET48 public override string GenerateParameters() +#else + public override string? GenerateParameters() +#endif { List parameters = new List(); @@ -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 /// /// Command string to normalize /// Normalized command +#if NET48 private string NormalizeCommand(List parts, ref int start) +#else + private string? NormalizeCommand(List 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 /// /// Command string to normalize /// Normalized command +#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 /// CICM Sidecar data generated by Aaru /// Base path for determining file names /// String containing the cuesheet, null on error +#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 /// CICM Sidecar data generated by Aaru /// Base path for determining file names /// String containing the datfile, null on error +#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 /// CICM Sidecar data generated by Aaru /// Base path for determining file names /// Datafile containing the hash information, null on error +#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 /// /// CICM Sidecar data generated by Aaru /// String containing the PVD, null on error +#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 /// /// OpticalDisc type from CICM Sidecar data /// Byte array representing the PVD, null on error +#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 /// Row ID for outputting /// Byte span representing the data to write /// Formatted string representing the sector line +#if NET48 private static string GenerateSectorOutputLine(string row, ReadOnlySpan bytes) +#else + private static string? GenerateSectorOutputLine(string row, ReadOnlySpan 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 /// /// CICM Sidecar data generated by Aaru /// Object containing the data, null on error +#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; } /// @@ -3049,7 +3292,11 @@ namespace MPF.Core.Modules.Aaru /// /// CICM Sidecar data generated by Aaru /// True if disc type info was set, false otherwise +#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 /// /// CICM Sidecar data generated by Aaru /// Formatted string representing the DVD protection, null on error +#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("")) + if (string.IsNullOrWhiteSpace(line)) + continue; + else if (line.StartsWith("")) totalErrors = 0; else if (line.StartsWith("")) return totalErrors ?? -1; @@ -3173,7 +3426,11 @@ namespace MPF.Core.Modules.Aaru /// /// CICM Sidecar data generated by Aaru /// True if hardware info was set, false otherwise +#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 /// /// CICM Sidecar data generated by Aaru /// Layerbreak if possible, null on error +#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 /// /// CICM Sidecar data generated by Aaru /// Sample write offset if possible, null on error +#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 /// /// CICM Sidecar data generated by Aaru /// True on successful extraction of info, false otherwise +#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 /// /// CICM Sidecar data generated by Aaru /// True on successful extraction of info, false otherwise +#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 /// /// CICM Sidecar data generated by Aaru /// True on successful extraction of info, false otherwise +#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; diff --git a/MPF.Core/Modules/BaseParameters.cs b/MPF.Core/Modules/BaseParameters.cs index 389ccb61..757bd883 100644 --- a/MPF.Core/Modules/BaseParameters.cs +++ b/MPF.Core/Modules/BaseParameters.cs @@ -25,7 +25,11 @@ namespace MPF.Core.Modules /// Geneeic way of reporting a message /// /// String value to report +#if NET48 public EventHandler ReportStatus; +#else + public EventHandler? ReportStatus; +#endif #endregion @@ -34,7 +38,11 @@ namespace MPF.Core.Modules /// /// Base command to run /// +#if NET48 public string BaseCommand { get; set; } +#else + public string? BaseCommand { get; set; } +#endif /// /// Set of flags to pass to the executable @@ -63,7 +71,11 @@ namespace MPF.Core.Modules /// /// Process to track external program /// +#if NET48 private Process process; +#else + private Process? process; +#endif #endregion @@ -72,18 +84,30 @@ namespace MPF.Core.Modules /// /// Command to flag support mappings /// +#if NET48 public Dictionary> CommandSupport => GetCommandSupport(); +#else + public Dictionary>? CommandSupport => GetCommandSupport(); +#endif /// /// Input path for operations /// +#if NET48 public virtual string InputPath => null; +#else + public virtual string? InputPath => null; +#endif /// /// Output path for operations /// /// String representing the path, null on error +#if NET48 public virtual string OutputPath => null; +#else + public virtual string? OutputPath => null; +#endif /// /// Get the processing speed from the implementation @@ -97,7 +121,11 @@ namespace MPF.Core.Modules /// /// Path to the executable /// +#if NET48 public string ExecutablePath { get; set; } +#else + public string? ExecutablePath { get; set; } +#endif /// /// Program that this set of parameters represents @@ -171,20 +199,32 @@ namespace MPF.Core.Modules /// Get all commands mapped to the supported flags /// /// Mappings from command to supported flags +#if NET48 public virtual Dictionary> GetCommandSupport() => null; +#else + public virtual Dictionary>? GetCommandSupport() => null; +#endif /// /// Blindly generate a parameter string based on the inputs /// /// Parameter string for invocation, null on error +#if NET48 public virtual string GenerateParameters() => null; +#else + public virtual string? GenerateParameters() => null; +#endif /// /// Get the default extension for a given media type /// /// MediaType value to check /// String representing the media type, null on error +#if NET48 public virtual string GetDefaultExtension(MediaType? mediaType) => null; +#else + public virtual string? GetDefaultExtension(MediaType? mediaType) => null; +#endif /// /// Generate a list of all log files generated @@ -320,7 +360,11 @@ namespace MPF.Core.Modules /// /// String content to encode /// Base64-encoded contents, if possible +#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 /// file location /// True if should read as binary, false otherwise (default) /// Full text of the file, null on error +#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 /// Long flag string, if available /// Reference to the position in the parts /// True if the parameter was processed successfully or skipped, false otherwise +#if NET48 protected bool ProcessFlagParameter(List parts, string shortFlagString, string longFlagString, ref int i) +#else + protected bool ProcessFlagParameter(List parts, string? shortFlagString, string longFlagString, ref int i) +#endif { if (parts == null) return false; @@ -501,7 +553,11 @@ namespace MPF.Core.Modules /// Reference to the position in the parts /// True if missing values are allowed, false otherwise /// True if the parameter was processed successfully or skipped, false otherwise +#if NET48 protected bool ProcessBooleanParameter(List parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false) +#else + protected bool ProcessBooleanParameter(List 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 /// Reference to the position in the parts /// True if missing values are allowed, false otherwise /// SByte value if success, SByte.MinValue if skipped, null on error/returns> +#if NET48 protected sbyte? ProcessInt8Parameter(List parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false) +#else + protected sbyte? ProcessInt8Parameter(List 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 /// Reference to the position in the parts /// True if missing values are allowed, false otherwise /// Int16 value if success, Int16.MinValue if skipped, null on error/returns> +#if NET48 protected short? ProcessInt16Parameter(List parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false) +#else + protected short? ProcessInt16Parameter(List 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 /// Reference to the position in the parts /// True if missing values are allowed, false otherwise /// Int32 value if success, Int32.MinValue if skipped, null on error/returns> +#if NET48 protected int? ProcessInt32Parameter(List parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false) +#else + protected int? ProcessInt32Parameter(List 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 /// Reference to the position in the parts /// True if missing values are allowed, false otherwise /// Int64 value if success, Int64.MinValue if skipped, null on error/returns> +#if NET48 protected long? ProcessInt64Parameter(List parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false) +#else + protected long? ProcessInt64Parameter(List 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 /// Reference to the position in the parts /// True if missing values are allowed, false otherwise /// String value if possible, string.Empty on missing, null on error +#if NET48 protected string ProcessStringParameter(List parts, string flagString, ref int i, bool missingAllowed = false) - => ProcessStringParameter(parts, null, flagString, ref i, missingAllowed); +#else + protected string? ProcessStringParameter(List parts, string flagString, ref int i, bool missingAllowed = false) +#endif + => ProcessStringParameter(parts, null, flagString, ref i, missingAllowed); /// /// Process a string parameter @@ -885,7 +961,11 @@ namespace MPF.Core.Modules /// Reference to the position in the parts /// True if missing values are allowed, false otherwise /// String value if possible, string.Empty on missing, null on error +#if NET48 protected string ProcessStringParameter(List parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false) +#else + protected string? ProcessStringParameter(List 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 /// Reference to the position in the parts /// True if missing values are allowed, false otherwise /// Byte value if success, Byte.MinValue if skipped, null on error/returns> +#if NET48 protected byte? ProcessUInt8Parameter(List parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false) +#else + protected byte? ProcessUInt8Parameter(List 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 /// /// .dat file location /// Relevant pieces of the datfile, null on error +#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 /// /// Path to the DAT file to parse /// Filled Datafile on success, null on error +#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 /// Path to a PIC.bin file /// Filled DiscInformation on success, null on error /// This omits the emergency brake information, if it exists +#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 /// /// Path to the input file /// True if hashing was successful, false otherwise +#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 /// /// Path to the input file /// Filled DateTime on success, null on failure +#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 /// /// String representing the combined hash data /// True if extraction was successful, false otherwise +#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 /// /// Datafile represenging the hash data /// True if extraction was successful, false otherwise +#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 /// /// Disc information containing unformatted data /// True if layerbreak info was set, false otherwise +#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(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 /// /// Disc information containing the data /// String representing the PIC identifier, null on error +#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; } /// @@ -1428,7 +1555,11 @@ namespace MPF.Core.Modules /// Output region, if possible /// Output EXE date in "yyyy-mm-dd" format if possible, null on error /// +#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 /// /// Drive letter to use to check /// Game version if possible, null on error +#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 /// /// Drive letter to use to check /// Internal disc serial if possible, null on error +#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 /// /// Drive letter to use to check /// Game version if possible, null on error +#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 /// /// Drive letter to use to check /// Internal disc serial if possible, null on error +#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; } } - + /// /// Get the version from a PlayStation 4 disc, if possible /// /// Drive letter to use to check /// Game version if possible, null on error +#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 /// /// Drive letter to use to check /// Internal disc serial if possible, null on error +#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 /// /// Drive letter to use to check /// Game version if possible, null on error +#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]) { diff --git a/MPF.Core/Modules/CleanRIp/Parameters.cs b/MPF.Core/Modules/CleanRIp/Parameters.cs index e9e8f7ab..dc0ea013 100644 --- a/MPF.Core/Modules/CleanRIp/Parameters.cs +++ b/MPF.Core/Modules/CleanRIp/Parameters.cs @@ -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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); 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 /// Path to ISO file /// Path to discinfo file /// +#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 /// Path to the BCA file associated with the dump /// BCA data as a hex string if possible, null on error /// https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters +#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 /// Path to ISO file /// Path to discinfo file /// +#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 /// Output internal version of the game /// Output internal name of the game /// +#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); } diff --git a/MPF.Core/Modules/Datafile.cs b/MPF.Core/Modules/Datafile.cs index c42679c2..02836741 100644 --- a/MPF.Core/Modules/Datafile.cs +++ b/MPF.Core/Modules/Datafile.cs @@ -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 } diff --git a/MPF.Core/Modules/DiscImageCreator/Converters.cs b/MPF.Core/Modules/DiscImageCreator/Converters.cs index a544b38e..577de4a0 100644 --- a/MPF.Core/Modules/DiscImageCreator/Converters.cs +++ b/MPF.Core/Modules/DiscImageCreator/Converters.cs @@ -48,7 +48,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// Command value to check /// MediaType if possible, null on error /// This takes the "safe" route by assuming the larger of any given format +#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 /// /// MediaType value to check /// Valid extension (with leading '.'), null on error +#if NET48 public static string Extension(MediaType? type) +#else + public static string? Extension(MediaType? type) +#endif { switch (type) { diff --git a/MPF.Core/Modules/DiscImageCreator/Parameters.cs b/MPF.Core/Modules/DiscImageCreator/Parameters.cs index e9781094..09331996 100644 --- a/MPF.Core/Modules/DiscImageCreator/Parameters.cs +++ b/MPF.Core/Modules/DiscImageCreator/Parameters.cs @@ -19,10 +19,18 @@ namespace MPF.Core.Modules.DiscImageCreator #region Generic Dumping Information /// +#if NET48 public override string InputPath => DriveLetter; +#else + public override string? InputPath => DriveLetter; +#endif /// +#if NET48 public override string OutputPath => Filename; +#else + public override string? OutputPath => Filename; +#endif /// /// @@ -46,7 +54,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// /// Drive letter or path to pass to DiscImageCreator /// +#if NET48 public string DriveLetter { get; set; } +#else + public string? DriveLetter { get; set; } +#endif /// /// Drive speed to set, if applicable @@ -56,12 +68,20 @@ namespace MPF.Core.Modules.DiscImageCreator /// /// Destination filename for DiscImageCreator output /// +#if NET48 public string Filename { get; set; } +#else + public string? Filename { get; set; } +#endif /// /// Optiarc drive output filename for merging /// +#if NET48 public string OptiarcFilename { get; set; } +#else + public string? OptiarcFilename { get; set; } +#endif /// /// Start LBA value for dumping specific sectors @@ -87,7 +107,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// Possible values: raw (default), pack /// /// TODO: Make this an enum +#if NET48 public string BEOpcodeValue { get; set; } +#else + public string? BEOpcodeValue { get; set; } +#endif /// /// C2 reread options for dumping [CD only] @@ -248,7 +272,7 @@ namespace MPF.Core.Modules.DiscImageCreator if (!File.Exists($"{basePath}.ccd")) missingFiles.Add($"{basePath}.ccd"); } - + if (!File.Exists($"{basePath}.dat")) missingFiles.Add($"{basePath}.dat"); if (!File.Exists($"{basePath}.sub") && !File.Exists($"{basePath}.subtmp")) @@ -328,7 +352,7 @@ namespace MPF.Core.Modules.DiscImageCreator missingFiles.Add($"{basePath}_mainInfo.txt"); if (!File.Exists($"{basePath}_volDesc.txt")) missingFiles.Add($"{basePath}_volDesc.txt"); - } + } // Removed or inconsistent files //{ @@ -382,15 +406,16 @@ namespace MPF.Core.Modules.DiscImageCreator /// public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts) { - string outputDirectory = Path.GetDirectoryName(basePath); + var outputDirectory = Path.GetDirectoryName(basePath); // Get the dumping program and version - (string dicCmd, string dicVersion) = GetCommandFilePathAndVersion(basePath); + var (dicCmd, dicVersion) = GetCommandFilePathAndVersion(basePath); + if (info.DumpingInfo == null) info.DumpingInfo = new DumpingInfoSection(); info.DumpingInfo.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {dicVersion ?? "Unknown Version"}"; info.DumpingInfo.DumpingDate = GetFileModifiedDate(dicCmd)?.ToString("yyyy-MM-dd HH:mm:ss"); // Fill in the hardware data - if (GetHardwareInfo($"{basePath}_drive.txt", out string manufacturer, out string model, out string firmware)) + if (GetHardwareInfo($"{basePath}_drive.txt", out var manufacturer, out var model, out var firmware)) { info.DumpingInfo.Manufacturer = manufacturer; info.DumpingInfo.Model = model; @@ -398,13 +423,14 @@ namespace MPF.Core.Modules.DiscImageCreator } // Fill in the disc type data - if (GetDiscType($"{basePath}_disc.txt", out string discTypeOrBookType)) + if (GetDiscType($"{basePath}_disc.txt", out var discTypeOrBookType)) info.DumpingInfo.ReportedDiscType = discTypeOrBookType; // Get the Datafile information - Datafile datafile = GetDatafile($"{basePath}.dat"); + var datafile = GetDatafile($"{basePath}.dat"); // Fill in the hash data + if (info.TracksAndWriteOffsets == null) info.TracksAndWriteOffsets = new TracksAndWriteOffsetsSection(); info.TracksAndWriteOffsets.ClrMameProData = GenerateDatfile(datafile); // Extract info based generically on MediaType @@ -412,7 +438,15 @@ namespace MPF.Core.Modules.DiscImageCreator { case MediaType.CDROM: case MediaType.GDROM: // TODO: Verify GD-ROM outputs this - info.Extras.PVD = GetPVD($"{basePath}_mainInfo.txt") ?? "Disc has no PVD"; ; + if (info.Extras == null) info.Extras = new ExtrasSection(); + info.Extras.PVD = GetPVD($"{basePath}_mainInfo.txt") ?? "Disc has no PVD"; + + if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection(); +#if NET48 + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif // Audio-only discs will fail if there are any C2 errors, so they would never get here if (this.System.IsAudio()) @@ -456,7 +490,8 @@ namespace MPF.Core.Modules.DiscImageCreator case MediaType.HDDVD: case MediaType.BluRay: // Get the individual hash data, as per internal - if (GetISOHashValues(datafile, out long size, out string crc32, out string md5, out string sha1)) + if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection(); + if (GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1)) { info.SizeAndChecksums.Size = size; info.SizeAndChecksums.CRC32 = crc32; @@ -488,6 +523,7 @@ namespace MPF.Core.Modules.DiscImageCreator } // Read the PVD + if (info.Extras == null) info.Extras = new ExtrasSection(); if (!options.EnableRedumpCompatibility || System != RedumpSystem.MicrosoftXbox) info.Extras.PVD = GetPVD($"{basePath}_mainInfo.txt") ?? string.Empty; @@ -522,65 +558,99 @@ namespace MPF.Core.Modules.DiscImageCreator { FileInfo fi = new FileInfo($"{basePath}_subIntention.txt"); if (fi.Length > 0) + { + if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection(); info.CopyProtection.SecuROMData = GetFullFile($"{basePath}_subIntention.txt") ?? string.Empty; + } } break; case RedumpSystem.DVDAudio: case RedumpSystem.DVDVideo: + if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection(); info.CopyProtection.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt") ?? 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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: - string xgd1XMID = GetXGD1XMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin")); + if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection(); +#if NET48 + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif + + string xgd1XMID; + if (string.IsNullOrWhiteSpace(outputDirectory)) + xgd1XMID = GetXGD1XMID($"{basePath}_DMI.bin"); + else + xgd1XMID = GetXGD1XMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin")); + XgdInfo xgd1Info = new XgdInfo(xgd1XMID); if (xgd1Info?.Initialized == true) { - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = xgd1Info.RawXMID; +#if NET48 + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = xgd1Info.RawXMID ?? string.Empty; info.CommonDiscInfo.Serial = xgd1Info.GetSerial() ?? string.Empty; if (!options.EnableRedumpCompatibility) + { + if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection(); info.VersionAndEditions.Version = xgd1Info.GetVersion() ?? string.Empty; - info.CommonDiscInfo.Region = XgdInfo.GetRegion(xgd1Info.XMID.RegionIdentifier); + } + info.CommonDiscInfo.Region = XgdInfo.GetRegion(xgd1Info.XMID?.RegionIdentifier); } // If we have the new, external DAT if (File.Exists($"{basePath}_suppl.dat")) { - Datafile suppl = GetDatafile($"{basePath}_suppl.dat"); - if (GetXGDAuxHashInfo(suppl, out string xgd1DMIHash, out string xgd1PFIHash, out string xgd1SSHash)) + var suppl = GetDatafile($"{basePath}_suppl.dat"); + if (GetXGDAuxHashInfo(suppl, out var xgd1DMIHash, out var xgd1PFIHash, out var xgd1SSHash)) { - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd1DMIHash; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash; + + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd1DMIHash ?? string.Empty; + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash ?? string.Empty; + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash ?? string.Empty; } - if (GetXGDAuxSSInfo($"{basePath}_disc.txt", out string xgd1SS, out string xgd1SSVer)) + if (GetXGDAuxSSInfo($"{basePath}_disc.txt", out var xgd1SS, out var xgd1SSVer)) { - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd1SSVer; + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd1SSVer ?? string.Empty; + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.SecuritySectorRanges = xgd1SS ?? string.Empty; } } else { - if (GetXGDAuxInfo($"{basePath}_disc.txt", out string xgd1DMIHash, out string xgd1PFIHash, out string xgd1SSHash, out string xgd1SS, out string xgd1SSVer)) + if (GetXGDAuxInfo($"{basePath}_disc.txt", out var xgd1DMIHash, out var xgd1PFIHash, out var xgd1SSHash, out var xgd1SS, 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; + 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 = xgd1SS ?? string.Empty; } } @@ -588,42 +658,59 @@ namespace MPF.Core.Modules.DiscImageCreator break; case RedumpSystem.MicrosoftXbox360: - string xgd23XeMID = GetXGD23XeMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin")); + if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection(); +#if NET48 + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif + + string xgd23XeMID; + if (string.IsNullOrWhiteSpace(outputDirectory)) + xgd23XeMID = GetXGD23XeMID($"{basePath}_DMI.bin"); + else + xgd23XeMID = GetXGD23XeMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin")); + XgdInfo xgd23Info = new XgdInfo(xgd23XeMID); if (xgd23Info?.Initialized == true) { - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = xgd23Info.RawXMID; + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = xgd23Info.RawXMID ?? string.Empty; info.CommonDiscInfo.Serial = xgd23Info.GetSerial() ?? string.Empty; if (!options.EnableRedumpCompatibility) + { + if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection(); info.VersionAndEditions.Version = xgd23Info.GetVersion() ?? string.Empty; - info.CommonDiscInfo.Region = XgdInfo.GetRegion(xgd23Info.XeMID.RegionIdentifier); + } + info.CommonDiscInfo.Region = XgdInfo.GetRegion(xgd23Info.XeMID?.RegionIdentifier); } // If we have the new, external DAT if (File.Exists($"{basePath}_suppl.dat")) { - Datafile suppl = GetDatafile($"{basePath}_suppl.dat"); - if (GetXGDAuxHashInfo(suppl, out string xgd23DMIHash, out string xgd23PFIHash, out string xgd23SSHash)) + var suppl = GetDatafile($"{basePath}_suppl.dat"); + if (GetXGDAuxHashInfo(suppl, out var xgd23DMIHash, out var xgd23PFIHash, out var xgd23SSHash)) { - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd23DMIHash; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash; - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash; + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd23DMIHash ?? string.Empty; + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash ?? string.Empty; + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash ?? string.Empty; } - if (GetXGDAuxSSInfo($"{basePath}_disc.txt", out string xgd23SS, out string xgd23SSVer)) + if (GetXGDAuxSSInfo($"{basePath}_disc.txt", out var xgd23SS, out var xgd23SSVer)) { - info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd23SSVer; + info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd23SSVer ?? string.Empty; + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.SecuritySectorRanges = xgd23SS ?? string.Empty; } } else { - if (GetXGDAuxInfo($"{basePath}_disc.txt", out string xgd23DMIHash, out string xgd23PFIHash, out string xgd23SSHash, out string xgd23SS, out string xgd23SSVer)) + if (GetXGDAuxInfo($"{basePath}_disc.txt", out var xgd23DMIHash, out var xgd23PFIHash, out var xgd23SSHash, out var xgd23SS, 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; + 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 = xgd23SS ?? string.Empty; } } @@ -633,16 +720,24 @@ namespace MPF.Core.Modules.DiscImageCreator case RedumpSystem.NamcoSegaNintendoTriforce: if (this.Type == MediaType.CDROM) { + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; // Take only the first 16 lines for GD-ROM if (!string.IsNullOrEmpty(info.Extras.Header)) info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16)); - if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate)) + if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate)) { // 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty; + if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection(); info.VersionAndEditions.Version = gdVersion ?? string.Empty; info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? string.Empty; } @@ -651,15 +746,22 @@ namespace MPF.Core.Modules.DiscImageCreator break; case RedumpSystem.SegaMegaCDSegaCD: + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; // Take only the last 16 lines for Sega CD if (!string.IsNullOrEmpty(info.Extras.Header)) info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Skip(16)); - if (GetSegaCDBuildInfo(info.Extras.Header, out string scdSerial, out string fixedDate)) + if (GetSegaCDBuildInfo(info.Extras.Header, out var scdSerial, out var fixedDate)) { // 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = scdSerial ?? string.Empty; info.CommonDiscInfo.EXEDateBuildDate = fixedDate ?? string.Empty; } @@ -669,16 +771,24 @@ namespace MPF.Core.Modules.DiscImageCreator case RedumpSystem.SegaChihiro: if (this.Type == MediaType.CDROM) { + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; // Take only the first 16 lines for GD-ROM if (!string.IsNullOrEmpty(info.Extras.Header)) info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16)); - if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate)) + if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate)) { // 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty; + if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection(); info.VersionAndEditions.Version = gdVersion ?? string.Empty; info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? string.Empty; } @@ -689,16 +799,24 @@ namespace MPF.Core.Modules.DiscImageCreator case RedumpSystem.SegaDreamcast: if (this.Type == MediaType.CDROM) { + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; // Take only the first 16 lines for GD-ROM if (!string.IsNullOrEmpty(info.Extras.Header)) info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16)); - if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate)) + if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate)) { // 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty; + if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection(); info.VersionAndEditions.Version = gdVersion ?? string.Empty; info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? string.Empty; } @@ -709,16 +827,24 @@ namespace MPF.Core.Modules.DiscImageCreator case RedumpSystem.SegaNaomi: if (this.Type == MediaType.CDROM) { + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; // Take only the first 16 lines for GD-ROM if (!string.IsNullOrEmpty(info.Extras.Header)) info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16)); - if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate)) + if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate)) { // 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty; + if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection(); info.VersionAndEditions.Version = gdVersion ?? string.Empty; info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? string.Empty; } @@ -729,16 +855,24 @@ namespace MPF.Core.Modules.DiscImageCreator case RedumpSystem.SegaNaomi2: if (this.Type == MediaType.CDROM) { + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? string.Empty; // Take only the first 16 lines for GD-ROM if (!string.IsNullOrEmpty(info.Extras.Header)) info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16)); - if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate)) + if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate)) { // 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty; + if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection(); info.VersionAndEditions.Version = gdVersion ?? string.Empty; info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? string.Empty; } @@ -747,16 +881,24 @@ namespace MPF.Core.Modules.DiscImageCreator break; case RedumpSystem.SegaSaturn: + if (info.Extras == null) info.Extras = new ExtrasSection(); info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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; } @@ -764,9 +906,15 @@ namespace MPF.Core.Modules.DiscImageCreator 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty; info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion; info.CommonDiscInfo.EXEDateBuildDate = playstationDate; @@ -778,34 +926,64 @@ namespace MPF.Core.Modules.DiscImageCreator else if (File.Exists($"{basePath}.img_EccEdc.txt")) psEdcStatus = GetPlayStationEDCStatus($"{basePath}.img_EccEdc.txt"); + if (info.EDC == null) info.EDC = new EDCSection(); info.EDC.EDC = psEdcStatus.ToYesNo(); + if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection(); info.CopyProtection.AntiModchip = GetPlayStationAntiModchipDetected($"{basePath}_disc.txt").ToYesNo(); 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#endif info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty; break; } @@ -813,61 +991,67 @@ namespace MPF.Core.Modules.DiscImageCreator // Fill in any artifacts that exist, Base64-encoded, if we need to if (includeArtifacts) { + if (info.Artifacts == null) info.Artifacts = new Dictionary(); + //if (File.Exists($"{basePath}.c2")) - // info.Artifacts["c2"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.c2")); + // info.Artifacts["c2"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.c2")) ?? string.Empty; if (File.Exists($"{basePath}_c2Error.txt")) - info.Artifacts["c2Error"] = GetBase64(GetFullFile($"{basePath}_c2Error.txt")); + info.Artifacts["c2Error"] = GetBase64(GetFullFile($"{basePath}_c2Error.txt")) ?? string.Empty; if (File.Exists($"{basePath}.ccd")) - info.Artifacts["ccd"] = GetBase64(GetFullFile($"{basePath}.ccd")); + info.Artifacts["ccd"] = GetBase64(GetFullFile($"{basePath}.ccd")) ?? string.Empty; if (File.Exists($"{basePath}_cmd.txt")) // TODO: Figure out how to read in the timestamp-named file - info.Artifacts["cmd"] = GetBase64(GetFullFile($"{basePath}_cmd.txt")); + info.Artifacts["cmd"] = GetBase64(GetFullFile($"{basePath}_cmd.txt")) ?? string.Empty; if (File.Exists($"{basePath}_CSSKey.txt")) - info.Artifacts["csskey"] = GetBase64(GetFullFile($"{basePath}_CSSKey.txt")); + info.Artifacts["csskey"] = GetBase64(GetFullFile($"{basePath}_CSSKey.txt")) ?? 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}.dat")) - info.Artifacts["dat"] = GetBase64(GetFullFile($"{basePath}.dat")); + info.Artifacts["dat"] = GetBase64(GetFullFile($"{basePath}.dat")) ?? string.Empty; 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(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"))) - // info.Artifacts["dmi"] = Convert.ToBase64String(File.ReadAllBytes(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"))); + // info.Artifacts["dmi"] = Convert.ToBase64String(File.ReadAllBytes(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"))) ?? 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}_img.cue")) - info.Artifacts["img_cue"] = GetBase64(GetFullFile($"{basePath}_img.cue")); + info.Artifacts["img_cue"] = GetBase64(GetFullFile($"{basePath}_img.cue")) ?? string.Empty; if (File.Exists($"{basePath}.img_EdcEcc.txt")) - info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile($"{basePath}.img_EdcEcc.txt")); + info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile($"{basePath}.img_EdcEcc.txt")) ?? string.Empty; if (File.Exists($"{basePath}.img_EccEdc.txt")) - info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile($"{basePath}.img_EccEdc.txt")); + info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile($"{basePath}.img_EccEdc.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}_PFI.bin")) - // info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin")); + // info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin")) ?? string.Empty; //if (File.Exists($"{basePath}_PIC.bin")) - // info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin")); + // info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin")) ?? string.Empty; //if (File.Exists($"{basePath}_SS.bin")) - // info.Artifacts["ss"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_SS.bin")); + // info.Artifacts["ss"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_SS.bin")) ?? string.Empty; if (File.Exists($"{basePath}.sub")) - info.Artifacts["sub"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.sub")); + info.Artifacts["sub"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.sub")) ?? string.Empty; if (File.Exists($"{basePath}_subError.txt")) - info.Artifacts["subError"] = GetBase64(GetFullFile($"{basePath}_subError.txt")); + info.Artifacts["subError"] = GetBase64(GetFullFile($"{basePath}_subError.txt")) ?? string.Empty; if (File.Exists($"{basePath}_subInfo.txt")) - info.Artifacts["subInfo"] = GetBase64(GetFullFile($"{basePath}_subInfo.txt")); + info.Artifacts["subInfo"] = GetBase64(GetFullFile($"{basePath}_subInfo.txt")) ?? string.Empty; if (File.Exists($"{basePath}_subIntention.txt")) - info.Artifacts["subIntention"] = GetBase64(GetFullFile($"{basePath}_subIntention.txt")); + info.Artifacts["subIntention"] = GetBase64(GetFullFile($"{basePath}_subIntention.txt")) ?? string.Empty; //if (File.Exists($"{basePath}_sub.txt")) - // info.Artifacts["subReadable"] = GetBase64(GetFullFile($"{basePath}_sub.txt")); + // info.Artifacts["subReadable"] = GetBase64(GetFullFile($"{basePath}_sub.txt")) ?? string.Empty; //if (File.Exists($"{basePath}_subReadable.txt")) - // info.Artifacts["subReadable"] = GetBase64(GetFullFile($"{basePath}_subReadable.txt")); + // info.Artifacts["subReadable"] = GetBase64(GetFullFile($"{basePath}_subReadable.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; } } /// +#if NET48 public override string GenerateParameters() +#else + public override string? GenerateParameters() +#endif { List parameters = new List(); @@ -957,7 +1141,7 @@ namespace MPF.Core.Modules.DiscImageCreator || BaseCommand == CommandStrings.XGD3Swap) { if (DriveSpeed != null) - parameters.Add(DriveSpeed.ToString()); + parameters.Add(DriveSpeed.ToString() ?? string.Empty); else return null; } @@ -968,8 +1152,8 @@ namespace MPF.Core.Modules.DiscImageCreator { if (StartLBAValue != null && EndLBAValue != null) { - parameters.Add(StartLBAValue.ToString()); - parameters.Add(EndLBAValue.ToString()); + parameters.Add(StartLBAValue.ToString() ?? string.Empty); + parameters.Add(EndLBAValue.ToString() ?? string.Empty); } else return null; @@ -982,7 +1166,7 @@ namespace MPF.Core.Modules.DiscImageCreator { parameters.Add(FlagStrings.AddOffset); if (AddOffsetValue != null) - parameters.Add(AddOffsetValue.ToString()); + parameters.Add(AddOffsetValue.ToString() ?? string.Empty); } } @@ -1003,7 +1187,7 @@ namespace MPF.Core.Modules.DiscImageCreator // BE Opcode if (IsFlagSupported(FlagStrings.BEOpcode)) { - if (this[FlagStrings.BEOpcode] == true && this[FlagStrings.D8Opcode] != true) + if (this[FlagStrings.BEOpcode] == true && this[FlagStrings.D8Opcode]== false) { parameters.Add(FlagStrings.BEOpcode); if (BEOpcodeValue != null @@ -1021,29 +1205,29 @@ namespace MPF.Core.Modules.DiscImageCreator if (C2OpcodeValue[0] != null) { if (C2OpcodeValue[0] > 0) - parameters.Add(C2OpcodeValue[0].ToString()); + parameters.Add(C2OpcodeValue[0].ToString() ?? string.Empty); else return null; } if (C2OpcodeValue[1] != null) { - parameters.Add(C2OpcodeValue[1].ToString()); + parameters.Add(C2OpcodeValue[1].ToString() ?? string.Empty); } if (C2OpcodeValue[2] != null) { if (C2OpcodeValue[2] == 0) { - parameters.Add(C2OpcodeValue[2].ToString()); + parameters.Add(C2OpcodeValue[2].ToString() ?? string.Empty); } else if (C2OpcodeValue[2] == 1) { - parameters.Add(C2OpcodeValue[2].ToString()); + parameters.Add(C2OpcodeValue[2].ToString() ?? string.Empty); if (C2OpcodeValue[3] != null && C2OpcodeValue[4] != null) { if (C2OpcodeValue[3] > 0 && C2OpcodeValue[4] > 0) { - parameters.Add(C2OpcodeValue[3].ToString()); - parameters.Add(C2OpcodeValue[4].ToString()); + parameters.Add(C2OpcodeValue[3].ToString() ?? string.Empty); + parameters.Add(C2OpcodeValue[4].ToString() ?? string.Empty); } else { @@ -1094,7 +1278,7 @@ namespace MPF.Core.Modules.DiscImageCreator { parameters.Add(FlagStrings.DVDReread); if (DVDRereadValue != null) - parameters.Add(DVDRereadValue.ToString()); + parameters.Add(DVDRereadValue.ToString() ?? string.Empty); } } @@ -1112,7 +1296,7 @@ namespace MPF.Core.Modules.DiscImageCreator { parameters.Add(FlagStrings.Fix); if (FixValue != null) - parameters.Add(FixValue.ToString()); + parameters.Add(FixValue.ToString() ?? string.Empty); else return null; } @@ -1125,7 +1309,7 @@ namespace MPF.Core.Modules.DiscImageCreator { parameters.Add(FlagStrings.ForceUnitAccess); if (ForceUnitAccessValue != null) - parameters.Add(ForceUnitAccessValue.ToString()); + parameters.Add(ForceUnitAccessValue.ToString() ?? string.Empty); } } @@ -1136,7 +1320,7 @@ namespace MPF.Core.Modules.DiscImageCreator { parameters.Add(FlagStrings.MultiSectorRead); if (MultiSectorReadValue != null) - parameters.Add(MultiSectorReadValue.ToString()); + parameters.Add(MultiSectorReadValue.ToString() ?? string.Empty); } } @@ -1182,7 +1366,7 @@ namespace MPF.Core.Modules.DiscImageCreator { parameters.Add(FlagStrings.NoSkipSS); if (NoSkipSecuritySectorValue != null) - parameters.Add(NoSkipSecuritySectorValue.ToString()); + parameters.Add(NoSkipSecuritySectorValue.ToString() ?? string.Empty); } } @@ -1193,7 +1377,7 @@ namespace MPF.Core.Modules.DiscImageCreator { parameters.Add(FlagStrings.PadSector); if (PadSectorValue != null) - parameters.Add(PadSectorValue.ToString()); + parameters.Add(PadSectorValue.ToString() ?? string.Empty); } } @@ -1230,8 +1414,8 @@ namespace MPF.Core.Modules.DiscImageCreator if (ReverseStartLBAValue == null || ReverseEndLBAValue == null) return null; - parameters.Add(ReverseStartLBAValue.ToString()); - parameters.Add(ReverseEndLBAValue.ToString()); + parameters.Add(ReverseStartLBAValue.ToString() ?? string.Empty); + parameters.Add(ReverseEndLBAValue.ToString() ?? string.Empty); } } } @@ -1252,7 +1436,7 @@ namespace MPF.Core.Modules.DiscImageCreator if (ScanFileProtectValue != null) { if (ScanFileProtectValue > 0) - parameters.Add(ScanFileProtectValue.ToString()); + parameters.Add(ScanFileProtectValue.ToString() ?? string.Empty); else return null; } @@ -1282,14 +1466,14 @@ namespace MPF.Core.Modules.DiscImageCreator if (SkipSectorValue[0] != null) { if (SkipSectorValue[0] > 0) - parameters.Add(SkipSectorValue[0].ToString()); + parameters.Add(SkipSectorValue[0].ToString() ?? string.Empty); else return null; } if (SkipSectorValue[1] != null) { if (SkipSectorValue[1] == 0) - parameters.Add(SkipSectorValue[1].ToString()); + parameters.Add(SkipSectorValue[1].ToString() ?? string.Empty); } } } @@ -1303,7 +1487,7 @@ namespace MPF.Core.Modules.DiscImageCreator if (SubchannelReadLevelValue != null) { if (SubchannelReadLevelValue >= 0 && SubchannelReadLevelValue <= 2) - parameters.Add(SubchannelReadLevelValue.ToString()); + parameters.Add(SubchannelReadLevelValue.ToString() ?? string.Empty); else return null; } @@ -1326,7 +1510,7 @@ namespace MPF.Core.Modules.DiscImageCreator if (VideoNowValue != null) { if (VideoNowValue >= 0) - parameters.Add(VideoNowValue.ToString()); + parameters.Add(VideoNowValue.ToString() ?? string.Empty); else return null; } @@ -1582,12 +1766,16 @@ namespace MPF.Core.Modules.DiscImageCreator } /// +#if NET48 public override string GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType); +#else + public override string? GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType); +#endif /// public override List GetLogFilePaths(string basePath) { - (string cmdPath, _) = GetCommandFilePathAndVersion(basePath); + (var cmdPath, _) = GetCommandFilePathAndVersion(basePath); List logFiles = new List(); switch (this.Type) @@ -2316,7 +2504,11 @@ namespace MPF.Core.Modules.DiscImageCreator // Flag read-out values byte? byteValue = null; int? intValue = null; +#if NET48 string stringValue = null; +#else + string? stringValue = null; +#endif // Add Offset intValue = ProcessInt32Parameter(parts, FlagStrings.AddOffset, ref i, missingAllowed: true); @@ -2527,7 +2719,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// /// Base filename and path to use for checking /// Tuple of file path and version as strings, both null on error +#if NET48 private static (string, string) GetCommandFilePathAndVersion(string basePath) +#else + private static (string?, string?) GetCommandFilePathAndVersion(string basePath) +#endif { // If we have an invalid base path, we can do nothing if (string.IsNullOrWhiteSpace(basePath)) @@ -2538,9 +2734,12 @@ namespace MPF.Core.Modules.DiscImageCreator Regex cmdFilenameRegex = new Regex(Regex.Escape(basePathFileName) + @"_(\d{8})T\d{6}\.txt"); // Find the first match for the command file - string parentDirectory = Path.GetDirectoryName(basePath); + var parentDirectory = Path.GetDirectoryName(basePath); + if (string.IsNullOrWhiteSpace(parentDirectory)) + return (null, null); + var currentFiles = Directory.GetFiles(parentDirectory); - string commandPath = currentFiles.FirstOrDefault(f => cmdFilenameRegex.IsMatch(f)); + var commandPath = currentFiles.FirstOrDefault(f => cmdFilenameRegex.IsMatch(f)); if (string.IsNullOrWhiteSpace(commandPath)) return (null, null); @@ -2621,7 +2820,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// /// _disc.txt file location /// True if disc type info was set, false otherwise +#if NET48 private static bool GetDiscType(string drive, out string discTypeOrBookType) +#else + private static bool GetDiscType(string drive, out string? discTypeOrBookType) +#endif { // Set the default values discTypeOrBookType = null; @@ -2637,7 +2840,7 @@ namespace MPF.Core.Modules.DiscImageCreator // Create a hashset to contain all of the found values var discTypeOrBookTypeSet = new HashSet(); - string line = sr.ReadLine(); + var line = sr.ReadLine(); while (line != null) { // Trim the line for later use @@ -2693,14 +2896,22 @@ namespace MPF.Core.Modules.DiscImageCreator /// _CSSKey.txt file location /// _disc.txt file location /// Formatted string representing the DVD protection, null on error +#if NET48 private static string GetDVDProtection(string cssKey, string disc) +#else + private static string? GetDVDProtection(string cssKey, string disc) +#endif { // If one of the files doesn't exist, we can't get info from them if (!File.Exists(disc)) 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 // Get everything from _disc.txt first using (StreamReader sr = File.OpenText(disc)) @@ -2708,18 +2919,21 @@ namespace MPF.Core.Modules.DiscImageCreator try { // Fast forward to the copyright information - while (!sr.ReadLine().Trim().StartsWith("========== CopyrightInformation ==========")) ; + while (sr.ReadLine()?.Trim()?.StartsWith("========== CopyrightInformation ==========")== false) ; // Now read until we hit the manufacturing information - string line = sr.ReadLine().Trim(); - while (!line.StartsWith("========== ManufacturingInformation ==========")) + var line = sr.ReadLine()?.Trim(); + while (line?.StartsWith("========== ManufacturingInformation ==========")== false) { + if (line == null) + break; + if (line.StartsWith("CopyrightProtectionType")) copyrightProtectionSystemType = line.Substring("CopyrightProtectionType: ".Length); else if (line.StartsWith("RegionManagementInformation")) region = line.Substring("RegionManagementInformation: ".Length); - line = sr.ReadLine().Trim(); + line = sr.ReadLine()?.Trim(); } } catch { } @@ -2735,7 +2949,9 @@ namespace MPF.Core.Modules.DiscImageCreator // Read until the end while (!sr.EndOfStream) { - string line = sr.ReadLine().Trim(); + var line = sr.ReadLine()?.Trim(); + if (line == null) + break; if (line.StartsWith("DecryptedDiscKey")) { @@ -2813,7 +3029,9 @@ namespace MPF.Core.Modules.DiscImageCreator // Read in the error count whenever we find it while (!sr.EndOfStream) { - string line = sr.ReadLine().Trim(); + var line = sr.ReadLine()?.Trim(); + if (line == null) + break; if (line.StartsWith("[NO ERROR]")) { @@ -2854,7 +3072,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// /// <String representing a formatter variant of the GD-ROM header /// True on successful extraction of info, false otherwise +#if NET48 private static bool GetGDROMBuildInfo(string segaHeader, out string serial, out string version, out string date) +#else + private static bool GetGDROMBuildInfo(string? segaHeader, out string? serial, out string? version, out string? date) +#endif { serial = null; version = null; date = null; @@ -2885,7 +3107,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// /// _drive.txt file location /// True if hardware info was set, false otherwise +#if NET48 private static bool GetHardwareInfo(string drive, out string manufacturer, out string model, out string firmware) +#else + private static bool GetHardwareInfo(string drive, out string? manufacturer, out string? model, out string? firmware) +#endif { // Set the default values manufacturer = null; model = null; firmware = null; @@ -2898,7 +3124,7 @@ namespace MPF.Core.Modules.DiscImageCreator { try { - string line = sr.ReadLine(); + var line = sr.ReadLine(); while (line != null) { // Trim the line for later use @@ -2940,7 +3166,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// _disc.txt file location /// True if XGD layerbreak info should be used, false otherwise /// Layerbreak if possible, null on error +#if NET48 private static string GetLayerbreak(string disc, bool xgd) +#else + private static string? GetLayerbreak(string disc, bool xgd) +#endif { // If the file doesn't exist, we can't get info from it if (!File.Exists(disc)) @@ -2950,7 +3180,7 @@ namespace MPF.Core.Modules.DiscImageCreator { try { - string line = sr.ReadLine(); + var line = sr.ReadLine(); while (line != null) { // Trim the line for later use @@ -2997,7 +3227,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// /// _disc.txt file location /// Formatted multisession information, null on error +#if NET48 private static string GetMultisessionInformation(string disc) +#else + private static string? GetMultisessionInformation(string disc) +#endif { // If the file doesn't exist, we can't get info from it if (!File.Exists(disc)) @@ -3008,33 +3242,49 @@ namespace MPF.Core.Modules.DiscImageCreator try { // Seek to the TOC data - string line = sr.ReadLine(); + var line = sr.ReadLine(); + if (line == null) + return null; + if (!line.StartsWith("========== TOC")) - while (!(line = sr.ReadLine()).StartsWith("========== TOC")) ; + while ((line = sr.ReadLine())?.StartsWith("========== TOC")== false) ; + if (line == null) + return null; // Create the required regex Regex trackLengthRegex = new Regex(@"^\s*.*?Track\s*([0-9]{1,2}), LBA\s*[0-9]{1,8} - \s*[0-9]{1,8}, Length\s*([0-9]{1,8})$"); // Read in the track length data var trackLengthMapping = new Dictionary(); - while ((line = sr.ReadLine()).Contains("Track")) + while ((line = sr.ReadLine())?.Contains("Track") == true) { var match = trackLengthRegex.Match(line); trackLengthMapping[match.Groups[1].Value] = match.Groups[2].Value; } + if (line == null) + return null; + // Seek to the FULL TOC data line = sr.ReadLine(); + if (line == null) + return null; + if (!line.StartsWith("========== FULL TOC")) - while (!(line = sr.ReadLine()).StartsWith("========== FULL TOC")) ; + while ((line = sr.ReadLine())?.StartsWith("========== FULL TOC")== false) ; + if (line == null) + return null; // Create the required regex Regex trackSessionRegex = new Regex(@"^\s*Session\s*([0-9]{1,2}),.*?,\s*Track\s*([0-9]{1,2}).*?$"); // Read in the track session data var trackSessionMapping = new Dictionary(); - while (!(line = sr.ReadLine()).StartsWith("========== OpCode")) + while ((line = sr.ReadLine())?.StartsWith("========== OpCode")== false) { + if (line == null) + return null; + var match = trackSessionRegex.Match(line); if (!match.Success) continue; @@ -3047,26 +3297,35 @@ namespace MPF.Core.Modules.DiscImageCreator return null; // Seek to the multisession data - line = sr.ReadLine().Trim(); + line = sr.ReadLine()?.Trim(); + if (line == null) + return null; + if (!line.StartsWith("Lead-out length")) - while (!(line = sr.ReadLine().Trim()).StartsWith("Lead-out length")) ; + while ((line = sr.ReadLine()?.Trim())?.StartsWith("Lead-out length")== false) ; // TODO: Are there any examples of 3+ session discs? // Read the first session lead-out - string firstSessionLeadOutLengthString = line.Substring("Lead-out length of 1st session: ".Length); - line = sr.ReadLine().Trim(); - + var firstSessionLeadOutLengthString = line?.Substring("Lead-out length of 1st session: ".Length); + line = sr.ReadLine()?.Trim(); + if (line == null) + return null; + // Read the second session lead-in, if it exists +#if NET48 string secondSessionLeadInLengthString = null; - while (line.StartsWith("Lead-in length")) +#else + string? secondSessionLeadInLengthString = null; +#endif + while (line?.StartsWith("Lead-in length")== false) { - secondSessionLeadInLengthString = line.Substring("Lead-in length of 2nd session: ".Length); - line = sr.ReadLine().Trim(); + secondSessionLeadInLengthString = line?.Substring("Lead-in length of 2nd session: ".Length); + line = sr.ReadLine()?.Trim(); } - + // Read the second session pregap - string secondSessionPregapLengthString = line.Substring("Pregap length of 1st track of 2nd session: ".Length); + var secondSessionPregapLengthString = line?.Substring("Pregap length of 1st track of 2nd session: ".Length); // Calculate the session gap total if (!int.TryParse(firstSessionLeadOutLengthString, out int firstSessionLeadOutLength)) @@ -3084,7 +3343,7 @@ namespace MPF.Core.Modules.DiscImageCreator if (!int.TryParse(lengthMapping.Value, out int trackLength)) trackLength = 0; - if (trackSessionMapping.TryGetValue(lengthMapping.Key, out string session)) + if (trackSessionMapping.TryGetValue(lengthMapping.Key, out var session)) firstSessionLength += session == "1" ? trackLength : 0; totalLength += trackLength; @@ -3116,7 +3375,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// Number of characters to trim the PIC to, if -1, ignored /// PIC data as a hex string if possible, null on error /// https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters +#if NET48 private static string GetPIC(string picPath, int trimLength = -1) +#else + private static string? GetPIC(string picPath, int trimLength = -1) +#endif { // If the file doesn't exist, we can't get the info if (!File.Exists(picPath)) @@ -3124,7 +3387,10 @@ namespace MPF.Core.Modules.DiscImageCreator try { - string hex = GetFullFile(picPath, true); + var hex = GetFullFile(picPath, true); + if (hex == null) + return null; + if (trimLength > -1) hex = hex.Substring(0, trimLength); @@ -3153,15 +3419,21 @@ namespace MPF.Core.Modules.DiscImageCreator try { // Check for either antimod string - string line = sr.ReadLine().Trim(); + var line = sr.ReadLine()?.Trim(); + if (line == null) + return null; + while (!sr.EndOfStream) { + if (line == null) + return false; + if (line.StartsWith("Detected anti-mod string")) return true; else if (line.StartsWith("No anti-mod string")) return false; - line = sr.ReadLine().Trim(); + line = sr.ReadLine()?.Trim(); } return false; @@ -3194,7 +3466,10 @@ namespace MPF.Core.Modules.DiscImageCreator { while (!sr.EndOfStream) { - string line = sr.ReadLine(); + var line = sr.ReadLine(); + if (line == null) + break; + if (line.Contains("mode 2 form 2")) modeTwoFormTwo++; else if (line.Contains("mode 2 no edc")) @@ -3233,7 +3508,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// /// _mainInfo.txt file location /// Newline-delimited PVD if possible, null on error +#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)) @@ -3244,33 +3523,45 @@ namespace MPF.Core.Modules.DiscImageCreator try { // If we're in a new mainInfo, the location of the header changed - string line = sr.ReadLine(); + var line = sr.ReadLine(); + if (line == null) + return null; + if (line.StartsWith("========== OpCode") || line.StartsWith("========== TOC (Binary)") || line.StartsWith("========== FULL TOC (Binary)")) { // Seek to unscrambled data - while (!(line = sr.ReadLine()).StartsWith("========== Check Volume Descriptor ==========")) ; + while ((line = sr.ReadLine())?.StartsWith("========== Check Volume Descriptor ==========")== false) ; // Read the next line so the search goes properly line = sr.ReadLine(); } + if (line == null) + return null; + // Make sure we're in the area - if (!line.StartsWith("========== LBA")) - while (!(line = sr.ReadLine()).StartsWith("========== LBA")) ; + if (line.StartsWith("========== LBA") == true) + while ((line = sr.ReadLine())?.StartsWith("========== LBA")== false) ; + if (line == null) + return null; // If we have a Sega disc, skip sector 0 if (line.StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) - while (!(line = sr.ReadLine()).StartsWith("========== LBA")) ; + while ((line = sr.ReadLine())?.StartsWith("========== LBA")== false) ; + if (line == null) + return null; // If we have a PlayStation disc, skip sector 4 if (line.StartsWith("========== LBA[000004, 0x00004]: Main Channel ==========")) - while (!(line = sr.ReadLine()).StartsWith("========== LBA")) ; + while ((line = sr.ReadLine())?.StartsWith("========== LBA")== false) ; + if (line == null) + return null; // We assume the first non-LBA0/4 sector listed is the proper one // Fast forward to the PVD - while (!(line = sr.ReadLine()).StartsWith("0310")) ; + while ((line = sr.ReadLine())?.StartsWith("0310")== false) ; // Now that we're at the PVD, read each line in and concatenate string pvd = ""; @@ -3292,7 +3583,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// /// <String representing a formatter variant of the Saturn header /// True on successful extraction of info, false otherwise +#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; @@ -3325,7 +3620,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// <String representing a formatter variant of the Sega CD header /// True on successful extraction of info, false otherwise /// Note that this works for MOST headers, except ones where the copyright stretches > 1 line +#if NET48 private static bool GetSegaCDBuildInfo(string segaHeader, out string serial, out string date) +#else + private static bool GetSegaCDBuildInfo(string? segaHeader, out string? serial, out string? date) +#endif { serial = null; date = null; @@ -3408,7 +3707,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// /// _mainInfo.txt file location /// Header as a byte array if possible, null on error +#if NET48 private static string GetSegaHeader(string mainInfo) +#else + private static string? GetSegaHeader(string mainInfo) +#endif { // If the file doesn't exist, we can't get info from it if (!File.Exists(mainInfo)) @@ -3419,28 +3722,42 @@ namespace MPF.Core.Modules.DiscImageCreator try { // If we're in a new mainInfo, the location of the header changed - string line = sr.ReadLine(); + var line = sr.ReadLine(); + if (line == null) + return null; + if (line.StartsWith("========== OpCode") || line.StartsWith("========== TOC (Binary)") || line.StartsWith("========== FULL TOC (Binary)")) { // Seek to unscrambled data - while (!(line = sr.ReadLine()).Contains("Check MCN and/or ISRC")) ; + while ((line = sr.ReadLine())?.Contains("Check MCN and/or ISRC")== false) ; + if (line == null) + return null; // Read the next line so the search goes properly line = sr.ReadLine(); } + if (line == null) + return null; + // Make sure we're in the area if (!line.StartsWith("========== LBA")) - while (!(line = sr.ReadLine()).StartsWith("========== LBA")) ; + while ((line = sr.ReadLine())?.StartsWith("========== LBA")== false) ; + if (line == null) + return null; // Make sure we're in the right sector if (!line.StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) - while (!(line = sr.ReadLine()).StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) ; + while ((line = sr.ReadLine())?.StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")== false) ; + if (line == null) + return null; // Fast forward to the header - while (!(line = sr.ReadLine()).Trim().StartsWith("+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F")) ; + while ((line = sr.ReadLine())?.Trim()?.StartsWith("+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F")== false) ; + if (line == null) + return null; // Now that we're at the Header, read each line in and concatenate string header = ""; @@ -3462,7 +3779,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// /// _disc.txt file location /// Universal hash if possible, null on error +#if NET48 private static string GetUniversalHash(string disc) +#else + private static string? GetUniversalHash(string disc) +#endif { // If the file doesn't exist, we can't get info from it if (!File.Exists(disc)) @@ -3473,16 +3794,23 @@ namespace MPF.Core.Modules.DiscImageCreator try { // Fast forward to the universal hash information - while (!sr.ReadLine().Trim().StartsWith("========== Hash(Universal Whole image) ==========")) ; + while (sr.ReadLine()?.Trim().StartsWith("========== Hash(Universal Whole image) ==========")== false) ; // If we find the universal hash line, return the SHA-1 hash only +#if NET48 string line; +#else + string? line; +#endif while (!sr.EndOfStream) { - line = sr.ReadLine().TrimStart(); + line = sr.ReadLine()?.TrimStart(); + if (line == null) + return null; + if (line.StartsWith(" /// _disc.txt file location /// Sample write offset if possible, null on error +#if NET48 private static string GetWriteOffset(string disc) +#else + private static string? GetWriteOffset(string disc) +#endif { // If the file doesn't exist, we can't get info from it if (!File.Exists(disc)) @@ -3514,13 +3846,13 @@ namespace MPF.Core.Modules.DiscImageCreator try { // Fast forward to the offsets - while (!sr.ReadLine().Trim().StartsWith("========== Offset")) ; + while (sr.ReadLine()?.Trim()?.StartsWith("========== Offset")== false) ; sr.ReadLine(); // Combined Offset sr.ReadLine(); // Drive Offset sr.ReadLine(); // Separator line // Now that we're at the offsets, attempt to get the sample offset - return sr.ReadLine().Split(' ').LastOrDefault(); + return sr.ReadLine()?.Split(' ')?.LastOrDefault(); } catch { @@ -3539,20 +3871,27 @@ namespace MPF.Core.Modules.DiscImageCreator /// Extracted SS.bin CRC32 hash (upper-cased) /// True on successful extraction of info, false otherwise /// Currently only the CRC32 values are returned for each, this may change in the future +#if NET48 private static bool GetXGDAuxHashInfo(Datafile suppl, out string dmihash, out string pfihash, out string sshash) +#else + private static bool GetXGDAuxHashInfo(Datafile? suppl, out string? dmihash, out string? pfihash, out string? sshash) +#endif { // Assign values to all outputs first dmihash = null; pfihash = null; sshash = null; // If we don't have a valid datafile, we can't do anything - if (suppl == null || suppl.Games.Length == 0 || suppl.Games[0].Roms.Length == 0) + if (suppl?.Games == null) return false; // Try to extract the hash information var roms = suppl.Games[0].Roms; - dmihash = roms.FirstOrDefault(r => r.Name.EndsWith("DMI.bin"))?.Crc?.ToUpperInvariant(); - pfihash = roms.FirstOrDefault(r => r.Name.EndsWith("PFI.bin"))?.Crc?.ToUpperInvariant(); - sshash = roms.FirstOrDefault(r => r.Name.EndsWith("SS.bin"))?.Crc?.ToUpperInvariant(); + if (roms == null || roms.Length == 0) + return false; + + dmihash = roms.FirstOrDefault(r => r.Name?.EndsWith("DMI.bin") == true)?.Crc?.ToUpperInvariant(); + pfihash = roms.FirstOrDefault(r => r.Name?.EndsWith("PFI.bin") == true)?.Crc?.ToUpperInvariant(); + sshash = roms.FirstOrDefault(r => r.Name?.EndsWith("SS.bin") == true)?.Crc?.ToUpperInvariant(); return true; } @@ -3567,7 +3906,11 @@ namespace MPF.Core.Modules.DiscImageCreator /// Extracted security sector data /// Extracted security sector version /// True on successful extraction of info, false otherwise +#if NET48 private static bool GetXGDAuxInfo(string disc, out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver) +#else + private static bool GetXGDAuxInfo(string disc, 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; @@ -3582,9 +3925,11 @@ namespace MPF.Core.Modules.DiscImageCreator { try { - while(!sr.EndOfStream) + while (!sr.EndOfStream) { - string line = sr.ReadLine().Trim(); + var line = sr.ReadLine()?.Trim(); + if (line == null) + break; // Security Sector version if (line.StartsWith("Version of challenge table")) @@ -3600,7 +3945,10 @@ namespace MPF.Core.Modules.DiscImageCreator Regex layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)"); - line = sr.ReadLine().Trim(); + line = sr.ReadLine()?.Trim(); + if (line == null) + break; + while (!line.StartsWith("========== TotalLength ==========") && !line.StartsWith("========== Unlock 2 state(wxripper) ==========")) { @@ -3611,21 +3959,26 @@ namespace MPF.Core.Modules.DiscImageCreator ss += $"{match.Groups[1]}-{match.Groups[2]}\n"; } - line = sr.ReadLine().Trim(); + line = sr.ReadLine()?.Trim(); + if (line == null) + break; } + + if (line == null) + break; } // Special File Hashes else if (line.StartsWith("Extracted security sector data /// Extracted security sector version /// True on successful extraction of info, false otherwise +#if NET48 private static bool GetXGDAuxSSInfo(string disc, out string ss, out string ssver) +#else + private static bool GetXGDAuxSSInfo(string disc, out string? ss, out string? ssver) +#endif { ss = null; ssver = null; @@ -3664,7 +4021,9 @@ namespace MPF.Core.Modules.DiscImageCreator { while (!sr.EndOfStream) { - string line = sr.ReadLine().Trim(); + var line = sr.ReadLine()?.Trim(); + if (line == null) + break; // Security Sector version if (line.StartsWith("Version of challenge table")) @@ -3680,7 +4039,10 @@ namespace MPF.Core.Modules.DiscImageCreator Regex layerRegex = new Regex(@"Layer [01].*, startLBA-endLBA:\s*(\d+)-\s*(\d+)"); - line = sr.ReadLine().Trim(); + line = sr.ReadLine()?.Trim(); + if (line == null) + break; + while (!line.StartsWith("========== TotalLength ==========") && !line.StartsWith("========== Unlock 2 state(wxripper) ==========")) { @@ -3691,8 +4053,13 @@ namespace MPF.Core.Modules.DiscImageCreator ss += $"{match.Groups[1]}-{match.Groups[2]}\n"; } - line = sr.ReadLine().Trim(); + line = sr.ReadLine()?.Trim(); + if (line == null) + break; } + + if (line == null) + break; } } diff --git a/MPF.Core/Modules/Redumper/Converters.cs b/MPF.Core/Modules/Redumper/Converters.cs index 83fd3319..7f8ac9d8 100644 --- a/MPF.Core/Modules/Redumper/Converters.cs +++ b/MPF.Core/Modules/Redumper/Converters.cs @@ -11,7 +11,11 @@ namespace MPF.Core.Modules.Redumper /// /// MediaType value to check /// Valid extension (with leading '.'), null on error +#if NET48 public static string Extension(MediaType? type) +#else + public static string? Extension(MediaType? type) +#endif { switch (type) { diff --git a/MPF.Core/Modules/Redumper/Parameters.cs b/MPF.Core/Modules/Redumper/Parameters.cs index 631bc694..01eb45e6 100644 --- a/MPF.Core/Modules/Redumper/Parameters.cs +++ b/MPF.Core/Modules/Redumper/Parameters.cs @@ -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 /// +#if NET48 public override string InputPath => DriveValue; +#else + public override string? InputPath => DriveValue; +#endif /// +#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 /// public override int? Speed => SpeedValue; @@ -48,14 +57,22 @@ namespace MPF.Core.Modules.Redumper /// /// List of all modes being run /// +#if NET48 public List ModeValues { get; set; } +#else + public List? ModeValues { get; set; } +#endif #region General /// /// Drive to use, first available drive with disc, if not provided /// +#if NET48 public string DriveValue { get; set; } +#else + public string? DriveValue { get; set; } +#endif /// /// Drive read speed, optimal drive speed will be used if not provided @@ -70,12 +87,20 @@ namespace MPF.Core.Modules.Redumper /// /// Dump files base directory /// +#if NET48 public string ImagePathValue { get; set; } +#else + public string? ImagePathValue { get; set; } +#endif /// /// Dump files prefix, autogenerated in dump mode, if not provided /// +#if NET48 public string ImageNameValue { get; set; } +#else + public string? ImageNameValue { get; set; } +#endif #endregion @@ -84,7 +109,11 @@ namespace MPF.Core.Modules.Redumper /// /// Override drive type, possible values: GENERIC, PLEXTOR, LG_ASUS /// +#if NET48 public string DriveTypeValue { get; set; } +#else + public string? DriveTypeValue { get; set; } +#endif /// /// Override drive read offset @@ -104,12 +133,20 @@ namespace MPF.Core.Modules.Redumper /// /// Override drive read method, possible values: BE, D8, BE_CDDA /// +#if NET48 public string DriveReadMethodValue { get; set; } +#else + public string? DriveReadMethodValue { get; set; } +#endif /// /// Override drive sector order, possible values: DATA_C2_SUB, DATA_SUB_C2 /// +#if NET48 public string DriveSectorOrderValue { get; set; } +#else + public string? DriveSectorOrderValue { get; set; } +#endif #endregion @@ -151,7 +188,11 @@ namespace MPF.Core.Modules.Redumper /// /// LBA ranges of sectors to skip /// +#if NET48 public string SkipValue { get; set; } +#else + public string? SkipValue { get; set; } +#endif /// /// 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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); +#else + if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary(); +#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(); 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 } /// +#if NET48 public override string GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType); +#else + public override string? GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType); +#endif /// public override List 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; } /// @@ -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 /// /// Log file location /// Newline-delimited cuesheet if possible, null on error +#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 /// /// Log file location /// Newline-delimited datfile if possible, null on error +#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(" /// Log file location /// Formatted string representing the DVD protection, null on error +#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: @@ -1405,7 +1548,11 @@ namespace MPF.Core.Modules.Redumper /// /// Log file location /// Layerbreak if possible, null on error +#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 /// /// Log file location /// Formatted multisession information, null on error +#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 /// /// Log file location /// PS1 LibCrypt data, if possible +#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 /// /// Log file location /// Newline-delimited PVD if possible, null on error +#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 /// /// Log file location /// Non-zero dta start if possible, null on error +#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 /// /// <String representing a formatter variant of the Saturn header /// True on successful extraction of info, false otherwise - /// 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 /// /// Log file location /// Header as a byte array if possible, null on error +#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 /// /// Log file location /// Header as a byte array if possible, null on error +#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 /// /// Log file location /// Universal hash if possible, null on error +#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 /// /// Log file location /// Sample write offset if possible, null on error +#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 /// /// Log file location /// Version if possible, null on error +#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 /// /// Log file location /// True if hardware info was set, false otherwise +#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: - (revision level: , 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()); diff --git a/MPF.Core/Modules/UmdImageCreator/Parameters.cs b/MPF.Core/Modules/UmdImageCreator/Parameters.cs index 9a729c05..8d839c37 100644 --- a/MPF.Core/Modules/UmdImageCreator/Parameters.cs +++ b/MPF.Core/Modules/UmdImageCreator/Parameters.cs @@ -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(); 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 /// /// _mainInfo.txt file location /// Newline-deliminated PVD if possible, null on error +#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 /// /// _disc.txt file location /// True on successful extraction of info, false otherwise +#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; diff --git a/MPF.Core/Protection.cs b/MPF.Core/Protection.cs index 5b34f20c..86ef1a01 100644 --- a/MPF.Core/Protection.cs +++ b/MPF.Core/Protection.cs @@ -19,7 +19,11 @@ namespace MPF.Core /// Options object that determines what to scan /// Optional progress callback /// Set of all detected copy protections with an optional error string - public static async Task<(Dictionary>, string)> RunProtectionScanOnPath(string path, Core.Data.Options options, IProgress progress = null) +#if NET48 + public static async Task<(Dictionary>, string)> RunProtectionScanOnPath(string path, Data.Options options, IProgress progress = null) +#else + public static async Task<(Dictionary>?, string?)> RunProtectionScanOnPath(string path, Data.Options options, IProgress? progress = null) +#endif { try { @@ -62,7 +66,11 @@ namespace MPF.Core /// /// Dictionary of file to list of protection mappings /// Detected protections, if any +#if NET48 public static string FormatProtections(Dictionary> protections) +#else + public static string? FormatProtections(Dictionary>? protections) +#endif { // If the filtered list is empty in some way, return if (protections == null || !protections.Any()) diff --git a/MPF.Core/Utilities/Logging.cs b/MPF.Core/Utilities/Logging.cs index b897b6b7..d2e93d51 100644 --- a/MPF.Core/Utilities/Logging.cs +++ b/MPF.Core/Utilities/Logging.cs @@ -14,7 +14,11 @@ namespace MPF.Core.Utilities /// TextReader representing the input /// Invoking class, passed on to the event handler /// Event handler to be invoked to write to log +#if NET48 public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler handler) +#else + public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler? handler) +#endif { // Initialize the required variables char[] buffer = new char[256]; @@ -63,7 +67,11 @@ namespace MPF.Core.Utilities /// Current line to process /// Invoking class, passed on to the event handler /// Event handler to be invoked to write to log +#if NET48 private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler handler) +#else + private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler? handler) +#endif { line = line.Replace("\r\n", "\n"); var split = line.Split('\n'); @@ -105,7 +113,11 @@ namespace MPF.Core.Utilities /// Current line to process /// Invoking class, passed on to the event handler /// Event handler to be invoked to write to log +#if NET48 private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler handler) +#else + private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler? handler) +#endif { var split = line.Split('\r'); diff --git a/MPF.Core/Utilities/OptionsLoader.cs b/MPF.Core/Utilities/OptionsLoader.cs index 48e00352..1fb6b13c 100644 --- a/MPF.Core/Utilities/OptionsLoader.cs +++ b/MPF.Core/Utilities/OptionsLoader.cs @@ -15,7 +15,11 @@ namespace MPF.Core.Utilities /// /// Load the current set of options from application arguments /// +#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)) as Dictionary; +#else + var settings = serializer.Deserialize(reader, typeof(Dictionary)) as Dictionary; +#endif return new Options(settings); } diff --git a/MPF.Core/Utilities/Tools.cs b/MPF.Core/Utilities/Tools.cs index 1bc6978f..a2999576 100644 --- a/MPF.Core/Utilities/Tools.cs +++ b/MPF.Core/Utilities/Tools.cs @@ -130,16 +130,23 @@ namespace MPF.Core.Utilities /// String representing the message to display the the user. /// String representing the new release URL. /// +#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 /// /// Get the current informational version formatted as a string /// +#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 /// /// Get the latest version of MPF from GitHub and the release URL /// +#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); }