Support ancient .NET in Core

This does not include .NET Framework 4.0 due to some issues with libraries.
This commit is contained in:
Matt Nadareski
2023-11-14 23:09:55 -05:00
parent 4a1e953ffd
commit 952828dddd
23 changed files with 522 additions and 136 deletions

View File

@@ -3,6 +3,7 @@
- Add Bandai Pippin detection
- Zip manufacturer files for Redumper
- Fix BE flag logic bug in DIC
- Support ancient .NET in Core
### 3.0.0 (2023-11-14)

View File

@@ -14,21 +14,33 @@ namespace MPF.Core.Data
public const string StopDumping = "Stop Dumping";
// Byte arrays for signatures
public static readonly byte[] SaturnSectorZeroStart = new byte[] { 0x53, 0x45, 0x47, 0x41, 0x20, 0x53, 0x45, 0x47, 0x41, 0x53, 0x41, 0x54, 0x55, 0x52, 0x4E, 0x20 };
public static readonly byte[] SaturnSectorZeroStart = [0x53, 0x45, 0x47, 0x41, 0x20, 0x53, 0x45, 0x47, 0x41, 0x53, 0x41, 0x54, 0x55, 0x52, 0x4E, 0x20];
// Lists of known drive speed ranges
#if NET40
public static IList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
public static IList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
public static IList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
public static IList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
public static IList<int> Unknown { get; } = new List<int> { 1 };
#else
public static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
public static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
public static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
public static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
public static IReadOnlyList<int> Unknown { get; } = new List<int> { 1 };
#endif
/// <summary>
/// Get list of all drive speeds for a given MediaType
/// </summary>
/// <param name="type">MediaType? that represents the current item</param>
/// <returns>Read-only list of drive speeds</returns>
#if NET40
public static IList<int> GetSpeedsForMediaType(MediaType? type)
#else
public static IReadOnlyList<int> GetSpeedsForMediaType(MediaType? type)
#endif
{
return type switch
{

View File

@@ -2,8 +2,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if NET462_OR_GREATER || NETCOREAPP
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Generic;
#endif
using MPF.Core.Converters;
using SabreTools.RedumpLib.Data;
@@ -61,7 +63,7 @@ namespace MPF.Core.Data
{
get
{
string volumeLabel = Template.DiscNotDetected;
string? volumeLabel = Template.DiscNotDetected;
if (this.MarkedActive)
{
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
@@ -71,7 +73,7 @@ namespace MPF.Core.Data
}
foreach (char c in Path.GetInvalidFileNameChars())
volumeLabel = volumeLabel.Replace(c, '_');
volumeLabel = volumeLabel?.Replace(c, '_');
return volumeLabel;
}
@@ -109,7 +111,7 @@ namespace MPF.Core.Data
// Sanitize a Windows-formatted long device path
if (devicePath.StartsWith("\\\\.\\"))
devicePath = devicePath["\\\\.\\".Length..];
devicePath = devicePath.Substring("\\\\.\\".Length);
// Create and validate the drive info object
var driveInfo = new DriveInfo(devicePath);
@@ -249,7 +251,7 @@ namespace MPF.Core.Data
public RedumpSystem? GetRedumpSystem(RedumpSystem? defaultValue)
{
// If we can't read the media in that drive, we can't do anything
if (!Directory.Exists(this.Name))
if (string.IsNullOrWhiteSpace(this.Name) || !Directory.Exists(this.Name))
return defaultValue;
// We're going to assume for floppies, HDDs, and removable drives
@@ -456,7 +458,7 @@ namespace MPF.Core.Data
return null;
// Audio CD
if (this.VolumeLabel.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
if (this.VolumeLabel!.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.AudioCD;
// Microsoft Xbox
@@ -547,6 +549,7 @@ namespace MPF.Core.Data
}
// Find and update all floppy drives
#if NET462_OR_GREATER
try
{
CimSession session = CimSession.Create(null);
@@ -567,6 +570,7 @@ namespace MPF.Core.Data
{
// No-op
}
#endif
return drives;
}

View File

@@ -6,14 +6,18 @@
public enum Hash
{
CRC32,
#if NET6_0_OR_GREATER
CRC64,
#endif
MD5,
SHA1,
SHA256,
SHA384,
SHA512,
#if NET6_0_OR_GREATER
XxHash32,
XxHash64,
#endif
}
/// <summary>

View File

@@ -108,7 +108,7 @@ namespace MPF.Core.Data
}
// Comments start with ';'
else if (line.StartsWith(";"))
else if (line!.StartsWith(";"))
{
// No-op, we don't process comments
}

View File

@@ -4,7 +4,6 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BinaryObjectScanner;
using MPF.Core.Data;
using MPF.Core.Modules;
using MPF.Core.Utilities;
@@ -75,14 +74,18 @@ namespace MPF.Core
/// <summary>
/// Event handler for data returned from a process
/// </summary>
#if NET40
private void OutputToLog(object? proc, BaseParameters.StringEventArgs args) => outputQueue?.Enqueue(args.Value);
#else
private void OutputToLog(object? proc, string args) => outputQueue?.Enqueue(args);
#endif
/// <summary>
/// Process the outputs in the queue
/// </summary>
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, nextOutput);
#endregion
#endregion
/// <summary>
/// Constructor for a full DumpEnvironment object from user information
@@ -256,7 +259,7 @@ namespace MPF.Core
/// <returns>Result instance with the outcome</returns>
public async Task<Result> VerifyAndSaveDumpOutput(
IProgress<Result>? resultProgress = null,
IProgress<ProtectionProgress>? protectionProgress = null,
IProgress<BinaryObjectScanner.ProtectionProgress>? protectionProgress = null,
Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? processUserInfo = null,
SubmissionInfo? seedInfo = null)
{
@@ -432,8 +435,8 @@ namespace MPF.Core
{
StartInfo = new ProcessStartInfo()
{
FileName = parameters.ExecutablePath,
Arguments = parameters.GenerateParameters(),
FileName = parameters.ExecutablePath!,
Arguments = parameters.GenerateParameters()!,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
@@ -477,7 +480,7 @@ namespace MPF.Core
return Result.Failure($"Error! {Parameters.ExecutablePath} does not exist!");
// Validate that the dumping drive doesn't contain the executable
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath);
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath!);
if (Drive?.Name != null && fullExecutablePath.StartsWith(Drive.Name))
return Result.Failure("Error! Cannot dump same drive that executable resides on!");

View File

@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
#if NET6_0_OR_GREATER
using System.IO.Hashing;
#endif
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
@@ -16,7 +18,11 @@ namespace MPF.Core.Hashing
/// <summary>
/// Hash type associated with the current state
/// </summary>
#if NETFRAMEWORK || NETCOREAPP3_1
public Hash HashType { get; private set; }
#else
public Hash HashType { get; init; }
#endif
/// <summary>
/// Current hash in bytes
@@ -71,14 +77,18 @@ namespace MPF.Core.Hashing
_hasher = HashType switch
{
Hash.CRC32 => new Crc32(),
#if NET6_0_OR_GREATER
Hash.CRC64 => new Crc64(),
#endif
Hash.MD5 => MD5.Create(),
Hash.SHA1 => SHA1.Create(),
Hash.SHA256 => SHA256.Create(),
Hash.SHA384 => SHA384.Create(),
Hash.SHA512 => SHA512.Create(),
#if NET6_0_OR_GREATER
Hash.XxHash32 => new XxHash32(),
Hash.XxHash64 => new XxHash64(),
#endif
_ => null,
};
}
@@ -156,14 +166,18 @@ namespace MPF.Core.Hashing
var hashers = new Dictionary<Hash, Hasher>
{
{ Hash.CRC32, new Hasher(Hash.CRC32) },
#if NET6_0_OR_GREATER
{ Hash.CRC64, new Hasher(Hash.CRC64) },
#endif
{ Hash.MD5, new Hasher(Hash.MD5) },
{ Hash.SHA1, new Hasher(Hash.SHA1) },
{ Hash.SHA256, new Hasher(Hash.SHA256) },
{ Hash.SHA384, new Hasher(Hash.SHA384) },
{ Hash.SHA512, new Hasher(Hash.SHA512) },
#if NET6_0_OR_GREATER
{ Hash.XxHash32, new Hasher(Hash.XxHash32) },
{ Hash.XxHash64, new Hasher(Hash.XxHash64) },
#endif
};
// Initialize the hashing helpers
@@ -216,15 +230,18 @@ namespace MPF.Core.Hashing
// Get the results
hashDict[Hash.CRC32] = hashers[Hash.CRC32].CurrentHashString;
#if NET6_0_OR_GREATER
hashDict[Hash.CRC64] = hashers[Hash.CRC64].CurrentHashString;
#endif
hashDict[Hash.MD5] = hashers[Hash.MD5].CurrentHashString;
hashDict[Hash.SHA1] = hashers[Hash.SHA1].CurrentHashString;
hashDict[Hash.SHA256] = hashers[Hash.SHA256].CurrentHashString;
hashDict[Hash.SHA384] = hashers[Hash.SHA384].CurrentHashString;
hashDict[Hash.SHA512] = hashers[Hash.SHA512].CurrentHashString;
#if NET6_0_OR_GREATER
hashDict[Hash.XxHash32] = hashers[Hash.XxHash32].CurrentHashString;
hashDict[Hash.XxHash64] = hashers[Hash.XxHash64].CurrentHashString;
hashDict[Hash.CRC64] = hashers[Hash.CRC64].CurrentHashString;
#endif
// Dispose of the hashers
loadBuffer.Dispose();
@@ -272,7 +289,11 @@ namespace MPF.Core.Hashing
/// <remarks>NonCryptographicHashAlgorithm implementations do not need finalization</remarks>
public void Terminate()
{
#if NET40 || NET452
byte[] emptyBuffer = [];
#else
byte[] emptyBuffer = Array.Empty<byte>();
#endif
switch (_hasher)
{
case HashAlgorithm ha:

View File

@@ -0,0 +1,184 @@
#if NETFRAMEWORK || NETCOREAPP3_1 || NET5_0
/*
Copyright (c) 2012-2015 Eugene Larchenko (spct@mail.ru)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
using System;
//namespace OptimizedCRC
namespace MPF.Core.Hashing
{
/// <summary>
/// Shell class to trick older versions into using CRC-32 properly
/// </summary>
internal abstract class NonCryptographicHashAlgorithm
{
#if NET40
/// <summary>
/// When overridden in a derived class, appends the contents of source to
/// the data already processed for the current hash computation.
/// </summary>
/// <param name="source">The data to process.</param>
public abstract void Append(byte[] source);
#else
/// <summary>
/// When overridden in a derived class, appends the contents of source to
/// the data already processed for the current hash computation.
/// </summary>
/// <param name="source">The data to process.</param>
public abstract void Append(ReadOnlySpan<byte> source);
#endif
/// <summary>
/// Gets the current computed hash value without modifying accumulated state.
/// </summary>
/// <returns>The hash value for the data already provided.</returns>
public abstract byte[] GetCurrentHash();
}
/// <remarks>
/// Some changes have been made to this code to make it more similar to the System.IO.Hashing implementations
/// </remarks>
internal class Crc32 : NonCryptographicHashAlgorithm, IDisposable
{
private const uint kCrcPoly = 0xEDB88320;
private const uint kInitial = 0xFFFFFFFF;
private const int CRC_NUM_TABLES = 8;
private static readonly uint[] Table;
static Crc32()
{
unchecked
{
Table = new uint[256 * CRC_NUM_TABLES];
int i;
for (i = 0; i < 256; i++)
{
uint r = (uint)i;
for (int j = 0; j < 8; j++)
{
r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
}
Table[i] = r;
}
for (; i < 256 * CRC_NUM_TABLES; i++)
{
uint r = Table[i - 256];
Table[i] = Table[r & 0xFF] ^ (r >> 8);
}
}
}
public uint UnsignedValue;
public Crc32()
{
Init();
}
/// <summary>
/// Reset CRC
/// </summary>
public void Init()
{
UnsignedValue = kInitial;
}
/// <inheritdoc/>
public override byte[] GetCurrentHash()
{
return BitConverter.GetBytes(~UnsignedValue);
}
/// <inheritdoc/>
#if NET40
public override void Append(byte[] source)
{
Update(source, 0, source.Length);
}
#else
public override void Append(ReadOnlySpan<byte> source)
{
byte[] sourceBytes = source.ToArray();
Update(sourceBytes, 0, sourceBytes.Length);
}
#endif
private void Update(byte[] data, int offset, int count)
{
_ = new ArraySegment<byte>(data, offset, count); // check arguments
if (count == 0)
{
return;
}
var table = Table;
uint crc = UnsignedValue;
for (; (offset & 7) != 0 && count != 0; count--)
{
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
}
if (count >= 8)
{
/*
* Idea from 7-zip project sources (http://7-zip.org/sdk.html)
*/
int end = (count - 8) & ~7;
count -= end;
end += offset;
while (offset != end)
{
crc ^= (uint)(data[offset] + (data[offset + 1] << 8) + (data[offset + 2] << 16) + (data[offset + 3] << 24));
uint high = (uint)(data[offset + 4] + (data[offset + 5] << 8) + (data[offset + 6] << 16) + (data[offset + 7] << 24));
offset += 8;
crc = table[(byte)crc + 0x700]
^ table[(byte)(crc >>= 8) + 0x600]
^ table[(byte)(crc >>= 8) + 0x500]
^ table[/*(byte)*/(crc >> 8) + 0x400]
^ table[(byte)(high) + 0x300]
^ table[(byte)(high >>= 8) + 0x200]
^ table[(byte)(high >>= 8) + 0x100]
^ table[/*(byte)*/(high >> 8) + 0x000];
}
}
while (count-- != 0)
{
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
}
UnsignedValue = crc;
}
public void Dispose()
{
UnsignedValue = 0;
}
}
}
#endif

View File

@@ -9,7 +9,6 @@ using System.Threading.Tasks;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using BinaryObjectScanner;
using MPF.Core.Data;
using MPF.Core.Modules;
using MPF.Core.Utilities;
@@ -104,7 +103,7 @@ namespace MPF.Core
/// <param name="options">Options object that determines what to scan</param>
/// <param name="progress">Optional progress callback</param>
/// <returns>Detected copy protection(s) if possible, null on error</returns>
internal static async Task<(string?, Dictionary<string, List<string>>?)> GetCopyProtection(Drive? drive, Data.Options options, IProgress<ProtectionProgress>? progress = null)
internal static async Task<(string?, Dictionary<string, List<string>>?)> GetCopyProtection(Drive? drive, Data.Options options, IProgress<BinaryObjectScanner.ProtectionProgress>? progress = null)
{
if (options.ScanForProtection && drive?.Name != null)
{
@@ -980,7 +979,7 @@ namespace MPF.Core
return null;
// Standardized "S" serials
if (serial.StartsWith("S"))
if (serial!.StartsWith("S"))
{
// string publisher = serial[0] + serial[1];
// char secondRegion = serial[3];
@@ -1135,8 +1134,13 @@ namespace MPF.Core
}
else
{
string entryName = file[outputDirectory.Length..].TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
string entryName = file.Substring(outputDirectory!.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
#if NETFRAMEWORK || NETCOREAPP3_1 || NET5_0
zf.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal);
#else
zf.CreateEntryFromFile(file, entryName, CompressionLevel.SmallestSize);
#endif
}
// If the file is MPF-specific, don't delete
@@ -1256,8 +1260,8 @@ namespace MPF.Core
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<LanguageSelection?>()).Select(l => l.LongName()).ToArray(), 1);
AddIfExists(output, Template.LanguagesField, (info.CommonDiscInfo?.Languages ?? [null]).Select(l => l.LongName() ?? "SILENCE! (CHANGE THIS)").ToArray(), 1);
AddIfExists(output, Template.PlaystationLanguageSelectionViaField, (info.CommonDiscInfo?.LanguageSelection ?? []).Select(l => l.LongName()).ToArray(), 1);
AddIfExists(output, Template.DiscSerialField, info.CommonDiscInfo?.Serial, 1);
// All ringcode information goes in an indented area
@@ -1383,12 +1387,12 @@ namespace MPF.Core
output.Add(""); output.Add("Copy Protection:");
if (info.CommonDiscInfo?.System == RedumpSystem.SonyPlayStation)
{
AddIfExists(output, Template.PlayStationAntiModchipField, info.CopyProtection.AntiModchip.LongName(), 1);
AddIfExists(output, Template.PlayStationAntiModchipField, info.CopyProtection!.AntiModchip.LongName(), 1);
AddIfExists(output, Template.PlayStationLibCryptField, info.CopyProtection.LibCrypt.LongName(), 1);
AddIfExists(output, Template.SubIntentionField, info.CopyProtection.LibCryptData, 1);
}
AddIfExists(output, Template.CopyProtectionField, info.CopyProtection.Protection, 1);
AddIfExists(output, Template.CopyProtectionField, info.CopyProtection!.Protection, 1);
AddIfExists(output, Template.SubIntentionField, info.CopyProtection.SecuROMData, 1);
}
@@ -1401,7 +1405,7 @@ namespace MPF.Core
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.DATField, info.TracksAndWriteOffsets!.ClrMameProData + "\n", 1);
AddIfExists(output, Template.CuesheetField, info.TracksAndWriteOffsets.Cuesheet, 1);
var offset = info.TracksAndWriteOffsets.OtherWriteOffsets;
if (Int32.TryParse(offset, out int i))
@@ -1880,7 +1884,7 @@ namespace MPF.Core
return files;
}
#endregion
#endregion
#region Normalization
@@ -1894,7 +1898,7 @@ namespace MPF.Core
{
// If we have no set languages, then assume English
if (languages == null || languages.Length == 0)
languages = new Language[] { Language.English };
languages = [Language.English];
// Loop through all of the given languages
foreach (var language in languages)
@@ -2358,7 +2362,7 @@ namespace MPF.Core
return string.Empty;
// Remove quotes from path
path = path.Replace("\"", string.Empty);
path = path!.Replace("\"", string.Empty);
// Try getting the combined path and returning that directly
string fullPath = getFullPath ? Path.GetFullPath(path) : path;

View File

@@ -1,12 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<!-- Assembly Properties -->
<TargetFrameworks>net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.0.0</VersionPrefix>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Common code for all MPF implementations</Description>
<Copyright>Copyright (c) Matt Nadareski 2019-2023</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<ItemGroup>
@@ -17,16 +26,30 @@
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="psxt001z" Version="0.21.0-beta2" />
<PackageReference Include="SabreTools.Models" Version="1.2.0" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.2.0" />
<PackageReference Include="SabreTools.Serialization" Version="1.2.0" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net40`)) OR $(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="Microsoft.Net.Http" Version="2.2.29" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
</ItemGroup>
</Project>

View File

@@ -226,9 +226,9 @@ namespace MPF.Core.Modules.Aaru
if (!string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
fullDiscType = $"{discType} ({discSubType})";
else if (!string.IsNullOrWhiteSpace(discType) && string.IsNullOrWhiteSpace(discSubType))
fullDiscType = discType;
fullDiscType = discType!;
else if (string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
fullDiscType = discSubType;
fullDiscType = discSubType!;
info.DumpingInfo.ReportedDiscType = fullDiscType;
}
@@ -1630,7 +1630,7 @@ namespace MPF.Core.Modules.Aaru
// Now split the string into parts for easier validation
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
parameters = parameters.Trim();
parameters = parameters!.Trim();
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
.Cast<Match>()
.Select(m => m.Value)
@@ -2384,7 +2384,11 @@ namespace MPF.Core.Modules.Aaru
var cueFiles = new List<CueFile>();
var cueSheet = new CueSheet
{
#if NET40 || NET452
Performer = string.Join(", ", cicmSidecar.Performer ?? []),
#else
Performer = string.Join(", ", cicmSidecar.Performer ?? Array.Empty<string>()),
#endif
};
// Only care about OpticalDisc types
@@ -2931,16 +2935,24 @@ namespace MPF.Core.Modules.Aaru
/// Generate a single 16-byte sector line from a byte array
/// </summary>
/// <param name="row">Row ID for outputting</param>
/// <param name="bytes">Byte span representing the data to write</param>
/// <param name="bytes">Bytes representing the data to write</param>
/// <returns>Formatted string representing the sector line</returns>
#if NET40
private static string? GenerateSectorOutputLine(string row, byte[] bytes)
#else
private static string? GenerateSectorOutputLine(string row, ReadOnlySpan<byte> bytes)
#endif
{
// If the data isn't correct, return null
if (bytes == null || bytes.Length != 16)
return null;
string pvdLine = $"{row} : ";
#if NETFRAMEWORK || NETCOREAPP3_1 || NET5_0
pvdLine += BitConverter.ToString(bytes.Slice(0, 8).ToArray()).Replace("-", " ");
#else
pvdLine += BitConverter.ToString(bytes[..8].ToArray()).Replace("-", " ");
#endif
pvdLine += " ";
pvdLine += BitConverter.ToString(bytes.Slice(8, 8).ToArray().ToArray()).Replace("-", " ");
pvdLine += " ";
@@ -3086,7 +3098,7 @@ namespace MPF.Core.Modules.Aaru
// Initialize on seeing the open tag
if (string.IsNullOrWhiteSpace(line))
continue;
else if (line.StartsWith("<BadBlocks>"))
else if (line!.StartsWith("<BadBlocks>"))
totalErrors = 0;
else if (line.StartsWith("</BadBlocks>"))
return totalErrors ?? -1;
@@ -3400,6 +3412,6 @@ namespace MPF.Core.Modules.Aaru
return false;
}
#endregion
#endregion
}
}

View File

@@ -14,11 +14,27 @@ namespace MPF.Core.Modules
{
#region Event Handlers
#if NET40
/// <summary>
/// Wrapper event args class for old .NET
/// </summary>
public class StringEventArgs : EventArgs
{
public string Value { get; set; }
}
/// <summary>
/// Geneeic way of reporting a message
/// </summary>
/// <param name="message">String value to report</param>
public EventHandler<StringEventArgs>? ReportStatus;
#else
/// <summary>
/// Geneeic way of reporting a message
/// </summary>
/// <param name="message">String value to report</param>
public EventHandler<string>? ReportStatus;
#endif
#endregion
@@ -261,7 +277,7 @@ namespace MPF.Core.Modules
// Create the start info
var startInfo = new ProcessStartInfo()
{
FileName = ExecutablePath,
FileName = ExecutablePath!,
Arguments = GenerateParameters() ?? "",
CreateNoWindow = !separateWindow,
UseShellExecute = separateWindow,
@@ -1104,7 +1120,7 @@ namespace MPF.Core.Modules
return null;
if (trimLength > -1)
hex = hex[..trimLength];
hex = hex.Substring(0, trimLength);
return Regex.Replace(hex, ".{32}", "$0\n", RegexOptions.Compiled);
}
@@ -1117,6 +1133,6 @@ namespace MPF.Core.Modules
#endregion
#endregion
}
}

View File

@@ -170,12 +170,12 @@ namespace MPF.Core.Modules.CleanRip
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(line))
continue;
else if (line.StartsWith("CRC32"))
crc = line[7..].ToLowerInvariant();
else if (line!.StartsWith("CRC32"))
crc = line.Substring(7).ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line[5..];
md5 = line.Substring(5);
else if (line.StartsWith("SHA-1"))
sha1 = line[7..];
sha1 = line.Substring(7);
}
return new Datafile
@@ -256,12 +256,12 @@ namespace MPF.Core.Modules.CleanRip
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(line))
continue;
else if (line.StartsWith("CRC32"))
crc = line[7..].ToLowerInvariant();
else if (line!.StartsWith("CRC32"))
crc = line.Substring(7).ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line[5..];
md5 = line.Substring(5);
else if (line.StartsWith("SHA-1"))
sha1 = line[7..];
sha1 = line.Substring(7);
}
return $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
@@ -304,17 +304,17 @@ namespace MPF.Core.Modules.CleanRip
{
continue;
}
else if (line.StartsWith("Version"))
else if (line!.StartsWith("Version"))
{
version = line["Version: ".Length..];
version = line.Substring("Version: ".Length);
}
else if (line.StartsWith("Internal Name"))
{
name = line["Internal Name: ".Length..];
name = line.Substring("Internal Name: ".Length);
}
else if (line.StartsWith("Filename"))
{
string serial = line["Filename: ".Length..];
string serial = line.Substring("Filename: ".Length);
// char gameType = serial[0];
// string gameid = serial[1] + serial[2];

View File

@@ -1940,7 +1940,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// Now split the string into parts for easier validation
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
parameters = parameters.Trim();
parameters = parameters!.Trim();
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
.Cast<Match>()
.Select(m => m.Value)
@@ -2697,25 +2697,25 @@ namespace MPF.Core.Modules.DiscImageCreator
if (line.StartsWith("DiscType:"))
{
// DiscType: <discType>
string identifier = line["DiscType: ".Length..];
string identifier = line.Substring("DiscType: ".Length);
discTypeOrBookTypeSet.Add(identifier);
}
else if (line.StartsWith("DiscTypeIdentifier:"))
{
// DiscTypeIdentifier: <discType>
string identifier = line["DiscTypeIdentifier: ".Length..];
string identifier = line.Substring("DiscTypeIdentifier: ".Length);
discTypeOrBookTypeSet.Add(identifier);
}
else if (line.StartsWith("DiscTypeSpecific:"))
{
// DiscTypeSpecific: <discType>
string identifier = line["DiscTypeSpecific: ".Length..];
string identifier = line.Substring("DiscTypeSpecific: ".Length);
discTypeOrBookTypeSet.Add(identifier);
}
else if (line.StartsWith("BookType:"))
{
// BookType: <discType>
string identifier = line["BookType: ".Length..];
string identifier = line.Substring("BookType: ".Length);
discTypeOrBookTypeSet.Add(identifier);
}
@@ -2767,9 +2767,9 @@ namespace MPF.Core.Modules.DiscImageCreator
break;
if (line.StartsWith("CopyrightProtectionType"))
copyrightProtectionSystemType = line["CopyrightProtectionType: ".Length..];
copyrightProtectionSystemType = line.Substring("CopyrightProtectionType: ".Length);
else if (line.StartsWith("RegionManagementInformation"))
region = line["RegionManagementInformation: ".Length..];
region = line.Substring("RegionManagementInformation: ".Length);
line = sr.ReadLine()?.Trim();
}
@@ -2792,7 +2792,7 @@ namespace MPF.Core.Modules.DiscImageCreator
if (line.StartsWith("DecryptedDiscKey"))
{
decryptedDiscKey = line["DecryptedDiscKey[020]: ".Length..];
decryptedDiscKey = line.Substring("DecryptedDiscKey[020]: ".Length);
}
else if (line.StartsWith("LBA:"))
{
@@ -2805,7 +2805,7 @@ namespace MPF.Core.Modules.DiscImageCreator
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), No TitleKey$", RegexOptions.Compiled);
string matchedFilename = match.Groups[1].Value;
if (matchedFilename.EndsWith(";1"))
matchedFilename = matchedFilename[..^2];
matchedFilename = matchedFilename.Substring(0, matchedFilename.Length - 2);
vobKeys += $"{matchedFilename} Title Key: No Title Key\n";
}
@@ -2814,7 +2814,7 @@ namespace MPF.Core.Modules.DiscImageCreator
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), EncryptedTitleKey: .*?, DecryptedTitleKey: (.*?)$", RegexOptions.Compiled);
string matchedFilename = match.Groups[1].Value;
if (matchedFilename.EndsWith(";1"))
matchedFilename = matchedFilename[..^2];
matchedFilename = matchedFilename.Substring(0, matchedFilename.Length - 2);
vobKeys += $"{matchedFilename} Title Key: {match.Groups[2].Value}\n";
}
@@ -2876,14 +2876,14 @@ namespace MPF.Core.Modules.DiscImageCreator
{
totalErrors ??= 0;
if (Int64.TryParse(line["Total errors: ".Length..].Trim(), out long te))
if (Int64.TryParse(line.Substring("Total errors: ".Length).Trim(), out long te))
totalErrors += te;
}
else if (line.StartsWith("Total warnings"))
{
totalErrors ??= 0;
if (Int64.TryParse(line["Total warnings: ".Length..].Trim(), out long tw))
if (Int64.TryParse(line.Substring("Total warnings: ".Length).Trim(), out long tw))
totalErrors += tw;
}
}
@@ -2914,12 +2914,12 @@ namespace MPF.Core.Modules.DiscImageCreator
// Now read it in cutting it into lines for easier parsing
try
{
string[] header = segaHeader.Split('\n');
string versionLine = header[4][58..];
string dateLine = header[5][58..];
serial = versionLine[..10].TrimEnd();
string[] header = segaHeader!.Split('\n');
string versionLine = header[4].Substring(58);
string dateLine = header[5].Substring(58);
serial = versionLine.Substring(0, 10).TrimEnd();
version = versionLine.Substring(10, 6).TrimStart('V', 'v');
date = dateLine[..8];
date = dateLine.Substring(0, 8);
return true;
}
catch
@@ -2956,17 +2956,17 @@ namespace MPF.Core.Modules.DiscImageCreator
if (string.IsNullOrEmpty(manufacturer) && line.StartsWith("VendorId"))
{
// VendorId: <manufacturer>
manufacturer = line["VendorId: ".Length..];
manufacturer = line.Substring("VendorId: ".Length);
}
else if (string.IsNullOrEmpty(model) && line.StartsWith("ProductId"))
{
// ProductId: <model>
model = line["ProductId: ".Length..];
model = line.Substring("ProductId: ".Length);
}
else if (string.IsNullOrEmpty(firmware) && line.StartsWith("ProductRevisionLevel"))
{
// ProductRevisionLevel: <firmware>
firmware = line["ProductRevisionLevel: ".Length..];
firmware = line.Substring("ProductRevisionLevel: ".Length);
}
line = sr.ReadLine();
@@ -3117,7 +3117,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// TODO: Are there any examples of 3+ session discs?
// Read the first session lead-out
var firstSessionLeadOutLengthString = line?["Lead-out length of 1st session: ".Length..];
var firstSessionLeadOutLengthString = line?.Substring("Lead-out length of 1st session: ".Length);
line = sr.ReadLine()?.Trim();
if (line == null)
return null;
@@ -3126,12 +3126,12 @@ namespace MPF.Core.Modules.DiscImageCreator
string? secondSessionLeadInLengthString = null;
while (line?.StartsWith("Lead-in length") == false)
{
secondSessionLeadInLengthString = line?["Lead-in length of 2nd session: ".Length..];
secondSessionLeadInLengthString = line?.Substring("Lead-in length of 2nd session: ".Length);
line = sr.ReadLine()?.Trim();
}
// Read the second session pregap
var secondSessionPregapLengthString = line?["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))
@@ -3354,12 +3354,12 @@ namespace MPF.Core.Modules.DiscImageCreator
// Now read it in cutting it into lines for easier parsing
try
{
string[] header = segaHeader.Split('\n');
string serialVersionLine = header[2][58..];
string dateLine = header[3][58..];
serial = serialVersionLine[..10].Trim();
string[] header = segaHeader!.Split('\n');
string serialVersionLine = header[2].Substring(58);
string dateLine = header[3].Substring(58);
serial = serialVersionLine.Substring(0, 10).Trim();
version = serialVersionLine.Substring(10, 6).TrimStart('V', 'v');
date = dateLine[..8];
date = dateLine.Substring(0, 8);
date = $"{date[0]}{date[1]}{date[2]}{date[3]}-{date[4]}{date[5]}-{date[6]}{date[7]}";
return true;
}
@@ -3387,17 +3387,17 @@ namespace MPF.Core.Modules.DiscImageCreator
// Now read it in cutting it into lines for easier parsing
try
{
string[] header = segaHeader.Split('\n');
string serialVersionLine = header[8][58..];
string dateLine = header[1][58..];
string[] header = segaHeader!.Split('\n');
string serialVersionLine = header[8].Substring(58);
string dateLine = header[1].Substring(58);
serial = serialVersionLine.Substring(3, 8).TrimEnd('-', ' ');
date = dateLine[8..].Trim();
date = dateLine.Substring(8).Trim();
// Properly format the date string, if possible
string[] dateSplit = date.Split('.');
if (dateSplit.Length == 1)
dateSplit = new string[] { date[..4], date[4..] };
dateSplit = [date.Substring(0, 4), date.Substring(4)];
string month = dateSplit[1];
dateSplit[1] = month switch

View File

@@ -1016,7 +1016,7 @@ namespace MPF.Core.Modules.Redumper
// Now split the string into parts for easier validation
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
parameters = parameters.Trim();
parameters = parameters!.Trim();
List<string> parts = Regex.Matches(parameters, @"([a-zA-Z\-]*=)?[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
.Cast<Match>()
.Select(m => m.Value)
@@ -1105,12 +1105,12 @@ namespace MPF.Core.Modules.Redumper
// Image Path
stringValue = ProcessStringParameter(parts, FlagStrings.ImagePath, ref i);
if (!string.IsNullOrWhiteSpace(stringValue))
ImagePathValue = $"\"{stringValue.Trim('"')}\"";
ImagePathValue = $"\"{stringValue!.Trim('"')}\"";
// Image Name
stringValue = ProcessStringParameter(parts, FlagStrings.ImageName, ref i);
if (!string.IsNullOrWhiteSpace(stringValue))
ImageNameValue = $"\"{stringValue.Trim('"')}\"";
ImageNameValue = $"\"{stringValue!.Trim('"')}\"";
// Overwrite
ProcessFlagParameter(parts, FlagStrings.Overwrite, ref i);
@@ -1352,7 +1352,7 @@ namespace MPF.Core.Modules.Redumper
if (line.StartsWith("current profile:"))
{
// current profile: <discType>
discTypeOrBookType = line["current profile: ".Length..];
discTypeOrBookType = line.Substring("current profile: ".Length);
}
line = sr.ReadLine();
@@ -1394,17 +1394,17 @@ namespace MPF.Core.Modules.Redumper
{
if (line.StartsWith("protection system type"))
{
copyrightProtectionSystemType = line["protection system type: ".Length..];
copyrightProtectionSystemType = line.Substring("protection system type: ".Length);
if (copyrightProtectionSystemType == "none" || copyrightProtectionSystemType == "<none>")
copyrightProtectionSystemType = "No";
}
else if (line.StartsWith("region management information:"))
{
region = line["region management information: ".Length..];
region = line.Substring("region management information: ".Length);
}
else if (line.StartsWith("disc key"))
{
decryptedDiscKey = line["disc key: ".Length..].Replace(':', ' ');
decryptedDiscKey = line.Substring("disc key: ".Length).Replace(':', ' ');
}
else if (line.StartsWith("title keys"))
{
@@ -1488,7 +1488,7 @@ namespace MPF.Core.Modules.Redumper
break;
// REDUMP.ORG errors: <error count>
string[] parts = line.Split(' ');
string[] parts = line!.Split(' ');
if (long.TryParse(parts[2], out long redump))
return redump;
else
@@ -1596,24 +1596,24 @@ namespace MPF.Core.Modules.Redumper
else if (line.StartsWith("layer break:"))
{
// layer break: <layerbreak>
layerbreak1 = line["layer break: ".Length..].Trim();
layerbreak1 = line.Substring("layer break: ".Length).Trim();
}
// Multi-layer discs have the layer in the name
else if (line.StartsWith("layer break (layer: 0):"))
{
// layer break (layer: 0): <layerbreak>
layerbreak1 = line["layer break (layer: 0): ".Length..].Trim();
layerbreak1 = line.Substring("layer break (layer: 0): ".Length).Trim();
}
else if (line.StartsWith("layer break (layer: 1):"))
{
// layer break (layer: 1): <layerbreak>
layerbreak2 = line["layer break (layer: 1): ".Length..].Trim();
layerbreak2 = line.Substring("layer break (layer: 1): ".Length).Trim();
}
else if (line.StartsWith("layer break (layer: 2):"))
{
// layer break (layer: 2): <layerbreak>
layerbreak3 = line["layer break (layer: 2): ".Length..].Trim();
layerbreak3 = line.Substring("layer break (layer: 2): ".Length).Trim();
}
}
@@ -1658,11 +1658,11 @@ namespace MPF.Core.Modules.Redumper
// Store the first session range
if (line.Contains("session 1:"))
firstSession = line["session 1: ".Length..].Trim();
firstSession = line.Substring("session 1: ".Length).Trim();
// Store the secomd session range
else if (line.Contains("session 2:"))
secondSession = line["session 2: ".Length..].Trim();
secondSession = line.Substring("session 2: ".Length).Trim();
}
// If either is blank, we don't have multisession
@@ -1884,7 +1884,7 @@ namespace MPF.Core.Modules.Redumper
{
string? line = sr.ReadLine()?.TrimStart();
if (line?.StartsWith("non-zero data sample range") == true)
return line["non-zero data sample range: [".Length..].Trim().Split(' ')[0];
return line.Substring("non-zero data sample range: [".Length).Trim().Split(' ')[0];
}
// We couldn't detect it then
@@ -1914,12 +1914,12 @@ namespace MPF.Core.Modules.Redumper
// Now read it in cutting it into lines for easier parsing
try
{
string[] header = segaHeader.Split('\n');
string serialVersionLine = header[2][58..];
string dateLine = header[3][58..];
serial = serialVersionLine[..10].Trim();
string[] header = segaHeader!.Split('\n');
string serialVersionLine = header[2].Substring(58);
string dateLine = header[3].Substring(58);
serial = serialVersionLine.Substring(0, 10).Trim();
version = serialVersionLine.Substring(10, 6).TrimStart('V', 'v');
date = dateLine[..8];
date = dateLine.Substring(0, 8);
date = $"{date[0]}{date[1]}{date[2]}{date[3]}-{date[4]}{date[5]}-{date[6]}{date[7]}";
return true;
}
@@ -2052,19 +2052,19 @@ namespace MPF.Core.Modules.Redumper
if (line.StartsWith("build date:"))
{
buildDate = line["build date: ".Length..].Trim();
buildDate = line.Substring("build date: ".Length).Trim();
}
else if (line.StartsWith("serial:"))
{
serial = line["serial: ".Length..].Trim();
serial = line.Substring("serial: ".Length).Trim();
}
else if (line.StartsWith("region:"))
{
region = line["region: ".Length..].Trim();
region = line.Substring("region: ".Length).Trim();
}
else if (line.StartsWith("regions:"))
{
region = line["regions: ".Length..].Trim();
region = line.Substring("regions: ".Length).Trim();
}
else if (line.StartsWith("header:"))
{
@@ -2109,7 +2109,7 @@ namespace MPF.Core.Modules.Redumper
{
string? line = sr.ReadLine()?.TrimStart();
if (line?.StartsWith("Universal Hash") == true)
return line["Universal Hash (SHA-1): ".Length..].Trim();
return line.Substring("Universal Hash (SHA-1): ".Length).Trim();
}
// We couldn't detect it then
@@ -2141,7 +2141,7 @@ namespace MPF.Core.Modules.Redumper
{
string? line = sr.ReadLine()?.TrimStart();
if (line?.StartsWith("disc write offset") == true)
return line["disc write offset: ".Length..].Trim();
return line.Substring("disc write offset: ".Length).Trim();
}
// We couldn't detect it then

View File

@@ -203,7 +203,7 @@ namespace MPF.Core.Modules.UmdImageCreator
break;
if (line.StartsWith("TITLE") && title == null)
title = line["TITLE: ".Length..];
title = line.Substring("TITLE: ".Length);
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
umdversion = line.Split(' ')[1];
else if (line.StartsWith("pspUmdTypes"))

View File

@@ -4,8 +4,6 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BinaryObjectScanner;
using BinaryObjectScanner.Protection;
using psxt001z;
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'.
@@ -21,13 +19,13 @@ namespace MPF.Core
/// <param name="options">Options object that determines what to scan</param>
/// <param name="progress">Optional progress callback</param>
/// <returns>Set of all detected copy protections with an optional error string</returns>
public static async Task<(Dictionary<string, List<string>>?, string?)> RunProtectionScanOnPath(string path, Data.Options options, IProgress<ProtectionProgress>? progress = null)
public static async Task<(Dictionary<string, List<string>>?, string?)> RunProtectionScanOnPath(string path, Data.Options options, IProgress<BinaryObjectScanner.ProtectionProgress>? progress = null)
{
try
{
var found = await Task.Run(() =>
{
var scanner = new Scanner(
var scanner = new BinaryObjectScanner.Scanner(
options.ScanArchivesForProtection,
scanContents: true, // Hardcoded value to avoid issues
scanGameEngines: false, // Hardcoded value to avoid issues
@@ -99,7 +97,7 @@ namespace MPF.Core
{
try
{
var antiModchip = new PSXAntiModchip();
var antiModchip = new BinaryObjectScanner.Protection.PSXAntiModchip();
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
{
try
@@ -142,9 +140,15 @@ namespace MPF.Core
if (foundProtections.Any(p => p.StartsWith("[Exception opening file")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("[Exception opening file"));
#if NET40 || NET452 || NET462
var tempList = new List<string> { "Exception occurred while scanning [RESCAN NEEDED]" };
tempList.AddRange(foundProtections);
foundProtections = tempList.OrderBy(p => p);
#else
foundProtections = foundProtections
.Prepend("Exception occurred while scanning [RESCAN NEEDED]")
.OrderBy(p => p);
#endif
}
// ActiveMARK
@@ -225,7 +229,16 @@ namespace MPF.Core
.Where(p => p != "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)");
if (foundProtections.Any(p => !p.StartsWith("SafeDisc")))
{
#if NET40 || NET452 || NET462
var tempList = new List<string>();
tempList.AddRange(foundProtections);
tempList.Add("Cactus Data Shield 300");
foundProtections = tempList;
#else
foundProtections = foundProtections.Append("Cactus Data Shield 300");
#endif
}
}
// SafeDisc

View File

@@ -7,7 +7,6 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using BinaryObjectScanner;
using MPF.Core.Data;
using MPF.Core.Modules;
using Newtonsoft.Json;
@@ -90,8 +89,12 @@ namespace MPF.Core
// Loop through and get the main node, if possible
XmlNode? mainNode = null;
foreach (XmlNode tempNode in bodyNode.ChildNodes)
foreach (XmlNode? tempNode in bodyNode.ChildNodes)
{
// Invalid nodes are skipped
if (tempNode == null)
continue;
// We only care about div elements
if (!string.Equals(tempNode.Name, "div", StringComparison.OrdinalIgnoreCase))
continue;
@@ -113,8 +116,12 @@ namespace MPF.Core
return null;
// Try to find elements as we're going
foreach (XmlNode childNode in mainNode.ChildNodes)
foreach (XmlNode? childNode in mainNode.ChildNodes)
{
// Invalid nodes are skipped
if (childNode == null)
continue;
// The title is the only thing in h1 tags
if (string.Equals(childNode.Name, "h1", StringComparison.OrdinalIgnoreCase))
info.CommonDiscInfo.Title = childNode.InnerText;
@@ -132,8 +139,12 @@ namespace MPF.Core
continue;
// The game node contains multiple other elements
foreach (XmlNode gameNode in childNode.ChildNodes)
foreach (XmlNode? gameNode in childNode.ChildNodes)
{
// Invalid nodes are skipped
if (gameNode == null)
continue;
// Table elements contain multiple other parts of information
if (string.Equals(gameNode.Name, "table", StringComparison.OrdinalIgnoreCase))
{
@@ -150,8 +161,12 @@ namespace MPF.Core
continue;
// Loop through each of the rows
foreach (XmlNode gameInfoNode in gameNode.ChildNodes)
foreach (XmlNode? gameInfoNode in gameNode.ChildNodes)
{
// Invalid nodes are skipped
if (gameInfoNode == null)
continue;
// If we run into anything not a row, ignore it
if (!string.Equals(gameInfoNode.Name, "tr", StringComparison.OrdinalIgnoreCase))
continue;
@@ -288,7 +303,7 @@ namespace MPF.Core
Data.Options options,
BaseParameters? parameters,
IProgress<Result>? resultProgress = null,
IProgress<ProtectionProgress>? protectionProgress = null)
IProgress<BinaryObjectScanner.ProtectionProgress>? protectionProgress = null)
{
// Ensure the current disc combination should exist
if (!system.MediaTypes().Contains(mediaType))
@@ -692,12 +707,20 @@ namespace MPF.Core
/// <param name="info">Existing SubmissionInfo object to fill</param>
/// <param name="id">Redump disc ID to retrieve</param>
/// <param name="includeAllData">True to include all pullable information, false to do bare minimum</param>
#if NETFRAMEWORK
public async static Task<bool> FillFromId(RedumpWebClient wc, SubmissionInfo info, int id, bool includeAllData)
#else
public async static Task<bool> FillFromId(RedumpHttpClient wc, SubmissionInfo info, int id, bool includeAllData)
#endif
{
// Ensure that required sections exist
info = EnsureAllSections(info);
#if NETFRAMEWORK
var discData = await Task.Run(() => wc.DownloadSingleSiteID(id));
#else
var discData = await wc.DownloadSingleSiteID(id);
#endif
if (string.IsNullOrEmpty(discData))
return false;
@@ -711,7 +734,7 @@ namespace MPF.Core
int firstParenLocation = title.IndexOf(" (");
if (firstParenLocation >= 0)
{
info.CommonDiscInfo!.Title = title[..firstParenLocation];
info.CommonDiscInfo!.Title = title.Substring(0, firstParenLocation);
var subMatches = Constants.DiscNumberLetterRegex.Matches(title);
foreach (Match subMatch in subMatches.Cast<Match>())
{
@@ -1069,12 +1092,17 @@ namespace MPF.Core
// Set the current dumper based on username
info.DumpersAndStatus ??= new DumpersAndStatusSection();
info.DumpersAndStatus.Dumpers = new string[] { options.RedumpUsername };
info.DumpersAndStatus.Dumpers = [options.RedumpUsername!];
info.PartiallyMatchedIDs = new List<int>();
// Login to Redump
#if NETFRAMEWORK
using var wc = new RedumpWebClient();
bool? loggedIn = wc.Login(options.RedumpUsername!, options.RedumpPassword!);
#else
using var wc = new RedumpHttpClient();
bool? loggedIn = await wc.Login(options.RedumpUsername, options.RedumpPassword);
#endif
if (loggedIn == null)
{
resultProgress?.Report(Result.Failure("There was an unknown error connecting to Redump"));
@@ -1094,7 +1122,11 @@ namespace MPF.Core
resultProgress?.Report(Result.Success("Finding disc matches on Redump..."));
var splitData = info.TracksAndWriteOffsets?.ClrMameProData?.TrimEnd('\n')?.Split('\n');
int trackCount = splitData?.Length ?? 0;
#if NET40 || NET452
foreach (string hashData in splitData ?? [])
#else
foreach (string hashData in splitData ?? Array.Empty<string>())
#endif
{
// Catch any errant blank lines
if (string.IsNullOrWhiteSpace(hashData))
@@ -1270,7 +1302,7 @@ namespace MPF.Core
}
}
#endregion
#endregion
#region Helpers
@@ -1342,7 +1374,11 @@ namespace MPF.Core
/// <param name="query">Query string to attempt to search for</param>
/// <param name="filterForwardSlashes">True to filter forward slashes, false otherwise</param>
/// <returns>All disc IDs for the given query, null on error</returns>
#if NETFRAMEWORK
private async static Task<List<int>?> ListSearchResults(RedumpWebClient wc, string? query, bool filterForwardSlashes = true)
#else
private async static Task<List<int>?> ListSearchResults(RedumpHttpClient wc, string? query, bool filterForwardSlashes = true)
#endif
{
// If there is an invalid query
if (string.IsNullOrWhiteSpace(query))
@@ -1351,7 +1387,7 @@ namespace MPF.Core
var ids = new List<int>();
// Strip quotes
query = query.Trim('"', '\'');
query = query!.Trim('"', '\'');
// Special characters become dashes
query = query.Replace(' ', '-');
@@ -1368,7 +1404,11 @@ namespace MPF.Core
int pageNumber = 1;
while (true)
{
#if NETFRAMEWORK
List<int> pageIds = await Task.Run(() => wc.CheckSingleSitePage(string.Format(Constants.QuickSearchUrl, query, pageNumber++)));
#else
List<int> pageIds = await wc.CheckSingleSitePage(string.Format(Constants.QuickSearchUrl, query, pageNumber++));
#endif
ids.AddRange(pageIds);
if (pageIds.Count <= 1)
break;
@@ -1391,7 +1431,11 @@ namespace MPF.Core
/// <param name="hashData">DAT-formatted hash data to parse out</param>
/// <param name="resultProgress">Optional result progress callback</param>
/// <returns>True if the track was found, false otherwise; List of found values, if possible</returns>
#if NETFRAMEWORK
private async static Task<(bool, List<int>?)> ValidateSingleTrack(RedumpWebClient wc, SubmissionInfo info, string hashData, IProgress<Result>? resultProgress = null)
#else
private async static Task<(bool, List<int>?)> ValidateSingleTrack(RedumpHttpClient wc, SubmissionInfo info, string hashData, IProgress<Result>? resultProgress = null)
#endif
{
// If the line isn't parseable, we can't validate
if (!InfoTool.GetISOHashValues(hashData, out long _, out var _, out var _, out var sha1))
@@ -1430,7 +1474,11 @@ namespace MPF.Core
/// <param name="info">Existing SubmissionInfo object to fill</param>
/// <param name="resultProgress">Optional result progress callback</param>
/// <returns>True if the track was found, false otherwise; List of found values, if possible</returns>
#if NETFRAMEWORK
private async static Task<(bool, List<int>?)> ValidateUniversalHash(RedumpWebClient wc, SubmissionInfo info, IProgress<Result>? resultProgress = null)
#else
private async static Task<(bool, List<int>?)> ValidateUniversalHash(RedumpHttpClient wc, SubmissionInfo info, IProgress<Result>? resultProgress = null)
#endif
{
// If we don't have special fields
if (info.CommonDiscInfo?.CommentsSpecialFields == null)
@@ -1448,7 +1496,7 @@ namespace MPF.Core
}
// Format the universal hash for finding within the comments
universalHash = $"{universalHash[..^1]}/comments/only";
universalHash = $"{universalHash.Substring(0, universalHash.Length - 1)}/comments/only";
// Get all matching IDs for the hash
var newIds = await ListSearchResults(wc, universalHash, filterForwardSlashes: false);
@@ -1480,10 +1528,18 @@ namespace MPF.Core
/// <param name="id">Redump disc ID to retrieve</param>
/// <param name="localCount">Local count of tracks for the current disc</param>
/// <returns>True if the track count matches, false otherwise</returns>
#if NETFRAMEWORK
private async static Task<bool> ValidateTrackCount(RedumpWebClient wc, int id, int localCount)
#else
private async static Task<bool> ValidateTrackCount(RedumpHttpClient wc, int id, int localCount)
#endif
{
// If we can't pull the remote data, we can't match
#if NETFRAMEWORK
string? discData = await Task.Run(() => wc.DownloadSingleSiteID(id));
#else
string? discData = await wc.DownloadSingleSiteID(id);
#endif
if (string.IsNullOrEmpty(discData))
return false;
@@ -1502,6 +1558,6 @@ namespace MPF.Core
return localCount == remoteCount;
}
#endregion
#endregion
}
}

View File

@@ -1677,10 +1677,10 @@ namespace MPF.Core.UI.ViewModels
}
// Validate that the user explicitly wants an inactive drive to be considered for dumping
if (_environment.Drive?.MarkedActive != true && _displayUserMessage != null)
if (_environment?.Drive?.MarkedActive != true && _displayUserMessage != null)
{
string message = "The currently selected drive does not appear to contain a disc! "
+ (!_environment.System.DetectedByWindows() ? $"This is normal for {_environment.System.LongName()} as the discs may not be readable on Windows. " : string.Empty)
+ (!_environment!.System.DetectedByWindows() ? $"This is normal for {_environment.System.LongName()} as the discs may not be readable on Windows. " : string.Empty)
+ "Do you want to continue?";
bool? mbresult = _displayUserMessage("No Disc Detected", message, 2, false);
@@ -1692,7 +1692,7 @@ namespace MPF.Core.UI.ViewModels
}
// Pre-split the output path
var outputDirectory = Path.GetDirectoryName(_environment.OutputPath);
var outputDirectory = Path.GetDirectoryName(_environment!.OutputPath);
string outputFilename = Path.GetFileName(_environment.OutputPath);
// If a complete dump already exists

View File

@@ -83,7 +83,11 @@ namespace MPF.Core.UI.ViewModels
/// </summary>
public static async Task<(bool?, string?)> TestRedumpLogin(string username, string password)
{
#if NETFRAMEWORK
return await Task.Run(() => RedumpWebClient.ValidateCredentials(username, password));
#else
return await RedumpHttpClient.ValidateCredentials(username, password);
#endif
}
#endregion

View File

@@ -127,7 +127,7 @@ namespace MPF.Core.Utilities
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
{
if (((InternalProgram)val) == InternalProgram.NONE)
if (((InternalProgram)val!) == InternalProgram.NONE)
continue;
programs.Add($"{((InternalProgram?)val).LongName()}");

View File

@@ -14,7 +14,11 @@ namespace MPF.Core.Utilities
/// <param name="reader">TextReader representing the input</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
#if NET40
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#else
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string>? handler)
#endif
{
// Initialize the required variables
char[] buffer = new char[256];
@@ -37,15 +41,27 @@ namespace MPF.Core.Utilities
string line = new(buffer, 0, read);
// If we have no newline characters, store in the string builder
#if NETFRAMEWORK
if (!line.Contains("\r") && !line.Contains("\n"))
#else
if (!line.Contains('\r') && !line.Contains('\n'))
#endif
sb.Append(line);
// If we have a newline, append and log
#if NETFRAMEWORK
else if (line.Contains("\n") || line.Contains("\r\n"))
#else
else if (line.Contains('\n') || line.Contains("\r\n"))
#endif
ProcessNewLines(sb, line, baseClass, handler);
// If we have a carriage return only, append and log first and last instances
#if NETFRAMEWORK
else if (line.Contains("\r"))
#else
else if (line.Contains('\r'))
#endif
ProcessCarriageReturns(sb, line, baseClass, handler);
}
}
@@ -63,14 +79,22 @@ namespace MPF.Core.Utilities
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
#if NET40
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#else
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
#endif
{
line = line.Replace("\r\n", "\n");
var split = line.Split('\n');
for (int i = 0; i < split.Length; i++)
{
// If the chunk contains a carriage return, handle it like a separate line
#if NETFRAMEWORK
if (split[i].Contains("\r"))
#else
if (split[i].Contains('\r'))
#endif
{
ProcessCarriageReturns(sb, split[i], baseClass, handler);
continue;
@@ -105,7 +129,11 @@ namespace MPF.Core.Utilities
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
#if NET40
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#else
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
#endif
{
var split = line.Split('\r');
@@ -115,7 +143,7 @@ namespace MPF.Core.Utilities
// Append the last
sb.Clear();
sb.Append($"\r{split[^1]}");
sb.Append($"\r{split[split.Length - 1]}");
}
}
}

View File

@@ -236,7 +236,8 @@ 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");
var latestReleaseJsonString = hc.Send(message)?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
var latestReleaseJsonString = hc.SendAsync(message)?.ConfigureAwait(false).GetAwaiter().GetResult()
.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
if (latestReleaseJsonString == null)
return (null, null);