Compare commits

...

60 Commits
3.0.0 ... 3.0.3

Author SHA1 Message Date
Matt Nadareski
a833e926f3 Bump version 2023-12-04 12:10:19 -05:00
Matt Nadareski
950be07bf0 Handle or suppress some messages 2023-12-01 23:58:01 -05:00
Matt Nadareski
4c5c1417e9 Remove .NET Framework 3.5 from build script 2023-12-01 23:31:59 -05:00
Matt Nadareski
6fdc3412e0 Fix build warning for NRE 2023-12-01 23:28:45 -05:00
Matt Nadareski
807b0c5f9e Fix using SHA-1 for track checks (fixes #613) 2023-12-01 20:46:38 -05:00
Matt Nadareski
9e0b64a1d1 Fix broken tests 2023-12-01 20:39:36 -05:00
Matt Nadareski
8cfbf2d9f1 Bump version 2023-12-01 12:49:43 -05:00
Matt Nadareski
0064737130 Handle most VS and dotnet differences 2023-11-30 23:34:44 -05:00
Matt Nadareski
292e3999c5 Reference .NET Framework 3.0 for 3.5 2023-11-30 20:51:04 -05:00
Matt Nadareski
5ed1e94d84 Import WinFX for .NET Framework 3.5 2023-11-30 18:00:45 -05:00
Matt Nadareski
5b094f57cb Update USE_ALL in Powershell script 2023-11-30 13:43:20 -05:00
Matt Nadareski
8066b5541e Update Redumper to build 271 2023-11-30 13:39:32 -05:00
Matt Nadareski
921d0207c2 Fix cross-framework UI styles 2023-11-30 13:12:56 -05:00
Matt Nadareski
4374ff7f74 Fix most .NET Framework 3.5 issues 2023-11-30 12:58:06 -05:00
Matt Nadareski
0be5825b5e Fix cross-framework UI rendering 2023-11-30 11:54:40 -05:00
Matt Nadareski
14c630bea7 Fix Powershell build script 2023-11-30 03:19:39 -05:00
Matt Nadareski
9a66c685fd Replace build script with Powershell
Thanks to Deterous for the original script conversion.
2023-11-30 00:48:20 -05:00
Matt Nadareski
5e0fa1ad47 Add Disc ID and Key fields in info window (fixes #609) 2023-11-30 00:08:39 -05:00
Matt Nadareski
79065dcc69 Read CSS for some copy protections (fixes #608) 2023-11-29 23:57:58 -05:00
Matt Nadareski
3d7355aee1 Bump version 2023-11-29 23:36:57 -05:00
Matt Nadareski
6e9a6724c3 Update Redumper to build 268 2023-11-29 17:30:46 -05:00
Matt Nadareski
be35acfb48 Support .NET Framework 3.5 in UI 2023-11-23 03:37:00 -05:00
Matt Nadareski
f1a46c2e82 Get UI building with Framework 4.0 again 2023-11-23 03:05:52 -05:00
Matt Nadareski
872959c889 Temporarily remove .NET Framework 4.0 from UI 2023-11-23 02:32:57 -05:00
Matt Nadareski
b848a401f8 Get UI building with Framework 4.0 2023-11-23 01:19:17 -05:00
Matt Nadareski
ee4762f8b3 Support .NET Framework 3.5 in UI.Core 2023-11-22 23:50:37 -05:00
Matt Nadareski
d68bcfb96a Support .NET Framework 2.0
This does not include the UI, as per the same reasons why .NET Framework 4.0 doesn't support it.
2023-11-22 23:38:59 -05:00
Matt Nadareski
d2433e4749 Update changelog 2023-11-22 23:28:57 -05:00
Matt Nadareski
56ec0e7057 Update compatibility libraries 2023-11-22 23:28:24 -05:00
Matt Nadareski
26e5d33d17 Support .NET Framework 3.5
This does not include the UI, as per the same reasons why .NET Framework 4.0 doesn't support it.
2023-11-22 16:26:31 -05:00
Matt Nadareski
8b8b51ace4 Re-enable .NET Framework 4.0 building in Check 2023-11-22 16:02:08 -05:00
Matt Nadareski
f350904441 Re-enable .NET Framework 4.0 building in UI.Core 2023-11-22 16:00:18 -05:00
Matt Nadareski
8e8e3368d0 Re-enable .NET Framework 4.0 building in Core 2023-11-22 15:56:43 -05:00
Matt Nadareski
4d8153dba1 Update to BinaryObjectScanner 3.0.2 2023-11-22 13:46:39 -05:00
Matt Nadareski
e4e4b5ecde Temporarily remove .NET Framework 4.0 2023-11-21 11:13:58 -05:00
Matt Nadareski
8373a6b8f5 Support proper async in .NET Framework 4.0 2023-11-20 16:46:54 -05:00
Matt Nadareski
45c51ebc80 Use TryGetValue on dictionaries 2023-11-20 13:24:17 -05:00
Matt Nadareski
af27085cc1 Get Check building with Framework 4.0 2023-11-20 13:20:48 -05:00
Matt Nadareski
82e3707dce Get UI.Core building with Framework 4.0 2023-11-20 13:19:30 -05:00
Matt Nadareski
85192e8d3e Get Core building with Framework 4.0 2023-11-20 13:15:06 -05:00
Matt Nadareski
6f784a352e Update Xunit packages 2023-11-20 12:29:33 -05:00
Matt Nadareski
ee707cf1af Update to BinaryObjectScanner 3.0.1 2023-11-20 12:28:52 -05:00
Matt Nadareski
a14c998b3b More C# 12 cleanup in Core 2023-11-16 15:33:37 -05:00
Matt Nadareski
004208df6a Perform some post-move cleanup 2023-11-16 15:20:19 -05:00
Matt Nadareski
2f765146d1 Update RedumpLib and use moved methods 2023-11-16 12:45:47 -05:00
Matt Nadareski
a7d548f7ce Trim PS3 serial and add unrelated notes (fixes #607) 2023-11-16 12:18:07 -05:00
Matt Nadareski
fbdb9875f3 Add C#12 syntax to tests 2023-11-15 16:39:52 -05:00
Matt Nadareski
39a524e3cc Fix reversed ringcode test 2023-11-15 16:32:23 -05:00
Matt Nadareski
9740ca3a7a Suppress deprecation warnings 2023-11-15 16:29:47 -05:00
Matt Nadareski
f8d81972bf Prepare XAML for ancient .NET support 2023-11-15 12:10:54 -05:00
Matt Nadareski
fe9302a553 Perform more ancient .NET support work 2023-11-15 11:58:53 -05:00
Matt Nadareski
c0ed7a7a0e Fix TLS for older .NET 2023-11-15 00:18:52 -05:00
Matt Nadareski
b0b48743ac Support ancient .NET in UI 2023-11-15 00:08:43 -05:00
Matt Nadareski
47e79dab31 Support ancient .NET in Check 2023-11-14 23:53:15 -05:00
Matt Nadareski
90edc42fdf Support C# 12 syntax 2023-11-14 23:40:41 -05:00
Matt Nadareski
45db365705 Support ancient .NET in UI Core 2023-11-14 23:14:33 -05:00
Matt Nadareski
952828dddd Support ancient .NET in Core
This does not include .NET Framework 4.0 due to some issues with libraries.
2023-11-14 23:09:55 -05:00
Matt Nadareski
4a1e953ffd Fix BE flag logic bug in DIC (fixes #606) 2023-11-14 21:27:09 -05:00
Matt Nadareski
25740c2936 Zip manufacturer files for Redumper (fixes #604) 2023-11-14 21:19:52 -05:00
Matt Nadareski
3696257940 Add Bandai Pippin detection (fixes #600) 2023-11-14 21:16:47 -05:00
59 changed files with 3147 additions and 2838 deletions

View File

@@ -1,3 +1,68 @@
### 3.0.3 (2023-12-04)
- Fix broken tests
- Fix using SHA-1 for track checks
- Fix build warning for NRE
- Remove .NET Framework 3.5 from build script
- Handle or suppress some messages
### 3.0.2 (2023-12-01)
- Read CSS for some copy protections
- Add Disc ID and Key fields in info window
- Replace build script with Powershell
- Fix Powershell build script
- Fix cross-framework UI rendering
- Fix most .NET Framework 3.5 issues
- Fix cross-framework UI styles
- Update Redumper to build 271
- Update USE_ALL in Powershell script
- Import WinFX for .NET Framework 3.5
- Reference .NET Framework 3.0 for 3.5
- Handle most VS and dotnet differences
### 3.0.1 (2023-11-30)
- Add Bandai Pippin detection
- Zip manufacturer files for Redumper
- Fix BE flag logic bug in DIC
- Support ancient .NET in Core
- Support ancient .NET in UI Core
- Support C# 12 syntax
- Support ancient .NET in Check
- Support ancient .NET in UI
- Fix TLS for older .NET
- Perform more ancient .NET support work
- Prepare XAML for ancient .NET support
- Suppress deprecation warnings
- Fix reversed ringcode test
- Add C#12 syntax to tests
- Trim PS3 serial and add unrelated notes
- Update RedumpLib and use moved methods
- Perform some post-move cleanup
- More C# 12 cleanup in Core
- Update to BinaryObjectScanner 3.0.1
- Update Xunit packages
- Get Core building with Framework 4.0
- Get UI.Core building with Framework 4.0
- Get Check building with Framework 4.0
- Use TryGetValue on dictionaries
- Support proper async in .NET Framework 4.0
- Temporarily remove .NET Framework 4.0
- Update to BinaryObjectScanner 3.0.2
- Re-enable .NET Framework 4.0 building in Core
- Re-enable .NET Framework 4.0 building in UI.Core
- Re-enable .NET Framework 4.0 building in Check
- Support .NET Framework 3.5
- Update compatibility libraries
- Support .NET Framework 2.0
- Support .NET Framework 3.5 in UI.Core
- Get UI building with Framework 4.0
- Temporarily remove .NET Framework 4.0 from UI
- Get UI building with Framework 4.0 again
- Support .NET Framework 3.5 in UI
- Update Redumper to build 268
### 3.0.0 (2023-11-14)
- Remove .NET Framework 4.8 from build

View File

@@ -1,15 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;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</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.0.3</VersionPrefix>
<!-- Package Properties -->
<Title>MPF Check</Title>
<Description>Validator for various dumping programs</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<VersionPrefix>3.0.0</VersionPrefix>
<Description>Validator for various dumping programs</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>
@@ -21,10 +31,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.0" GeneratePathProperty="true">
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.2" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.2.0" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -45,8 +45,12 @@ namespace MPF.Check
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
// Validate the supplied credentials
#if NETFRAMEWORK
(bool? _, string? message) = RedumpWebClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty);
#else
(bool? _, string? message) = RedumpHttpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).ConfigureAwait(false).GetAwaiter().GetResult();
if (!string.IsNullOrWhiteSpace(message))
#endif
if (!string.IsNullOrEmpty(message))
Console.WriteLine(message);
// Loop through all the rest of the args
@@ -64,13 +68,19 @@ namespace MPF.Check
// Now populate an environment
Drive? drive = null;
if (!string.IsNullOrWhiteSpace(path))
drive = Drive.Create(null, path);
if (!string.IsNullOrEmpty(path))
drive = Drive.Create(null, path!);
var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, internalProgram: null, parameters: null);
// Finally, attempt to do the output dance
#if NET40
var resultTask = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress);
resultTask.Wait();
var result = resultTask.Result;
#else
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
Console.WriteLine(result.Message);
}
}

View File

@@ -1,5 +1,9 @@
using System;
#if NET20 || NET35
using System.Collections.Generic;
#else
using System.Collections.Concurrent;
#endif
using System.IO;
using System.Reflection;
using MPF.Core.Data;
@@ -34,7 +38,11 @@ namespace MPF.Core.Converters
/// <summary>
/// Long name method cache
/// </summary>
private static readonly ConcurrentDictionary<Type, MethodInfo?> LongNameMethods = new();
#if NET20 || NET35
private static readonly Dictionary<Type, MethodInfo?> LongNameMethods = [];
#else
private static readonly ConcurrentDictionary<Type, MethodInfo?> LongNameMethods = [];
#endif
/// <summary>
/// Get the string representation of a generic enumerable value
@@ -50,11 +58,14 @@ namespace MPF.Core.Converters
if (!LongNameMethods.TryGetValue(sourceType, out var method))
{
method = typeof(Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
if (method == null)
method = typeof(EnumConverter).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
method = typeof(Extensions).GetMethod("LongName", [typeof(Nullable<>).MakeGenericType(sourceType)]);
method ??= typeof(EnumConverter).GetMethod("LongName", [typeof(Nullable<>).MakeGenericType(sourceType)]);
#if NET20 || NET35
LongNameMethods[sourceType] = method;
#else
LongNameMethods.TryAdd(sourceType, method);
#endif
}
if (method != null)
@@ -99,7 +110,7 @@ namespace MPF.Core.Converters
};
}
#endregion
#endregion
#region Convert From String

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 NET20 || NET35 || 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 NET20 || NET35 || NET40
public static IList<int> GetSpeedsForMediaType(MediaType? type)
#else
public static IReadOnlyList<int> GetSpeedsForMediaType(MediaType? type)
#endif
{
return type switch
{
@@ -49,65 +61,6 @@ namespace MPF.Core.Data
/// </summary>
public static class Template
{
// Manual information
public const string TitleField = "Title";
public const string ForeignTitleField = "Foreign Title (Non-latin)";
public const string DiscNumberField = "Disc Number / Letter";
public const string DiscTitleField = "Disc Title";
public const string SystemField = "System";
public const string MediaTypeField = "Media Type";
public const string CategoryField = "Category";
public const string RegionField = "Region";
public const string LanguagesField = "Languages";
public const string PlaystationLanguageSelectionViaField = "Language Selection Via";
public const string DiscSerialField = "Disc Serial";
public const string BarcodeField = "Barcode";
public const string CommentsField = "Comments";
public const string ContentsField = "Contents";
public const string VersionField = "Version";
public const string EditionField = "Edition/Release";
public const string PlayStation3WiiDiscKeyField = "Disc Key";
public const string PlayStation3DiscIDField = "Disc ID";
public const string GameCubeWiiBCAField = "BCA";
public const string CopyProtectionField = "Copy Protection";
public const string MasteringRingField = "Mastering Code (laser branded/etched)";
public const string MasteringSIDField = "Mastering SID Code";
public const string MouldSIDField = "Mould SID Code";
public const string AdditionalMouldField = "Additional Mould";
public const string ToolstampField = "Toolstamp or Mastering Code (engraved/stamped)";
// Automatic Information
public const string DumpingProgramField = "Dumping Program";
public const string DumpingDateField = "Date";
public const string DumpingDriveManufacturer = "Manufacturer";
public const string DumpingDriveModel = "Model";
public const string DumpingDriveFirmware = "Firmware";
public const string ReportedDiscType = "Reported Disc Type";
public const string PVDField = "Primary Volume Descriptor (PVD)";
public const string DATField = "DAT";
public const string SizeField = "Size";
public const string CRC32Field = "CRC32";
public const string MD5Field = "MD5";
public const string SHA1Field = "SHA1";
public const string FullyMatchingIDField = "Fully Matching ID";
public const string PartiallyMatchingIDsField = "Partially Matching IDs";
public const string ErrorCountField = "Error Count";
public const string CuesheetField = "Cuesheet";
public const string SubIntentionField = "SubIntention Data (SecuROM/LibCrypt)";
public const string WriteOffsetField = "Write Offset";
public const string LayerbreakField = "Layerbreak";
public const string EXEDateBuildDate = "EXE/Build Date";
public const string HeaderField = "Header";
public const string PICField = "Permanent Information & Control (PIC)";
public const string PlayStationEDCField = "EDC";
public const string PlayStationAntiModchipField = "Anti-modchip";
public const string PlayStationLibCryptField = "LibCrypt";
public const string XBOXSSRanges = "Security Sector Ranges";
// Default values
public const string RequiredValue = "(REQUIRED)";
public const string RequiredIfExistsValue = "(REQUIRED, IF EXISTS)";
public const string OptionalValue = "(OPTIONAL)";

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,17 +63,17 @@ namespace MPF.Core.Data
{
get
{
string volumeLabel = Template.DiscNotDetected;
string? volumeLabel = Template.DiscNotDetected;
if (this.MarkedActive)
{
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
if (string.IsNullOrEmpty(this.VolumeLabel))
volumeLabel = "track";
else
volumeLabel = this.VolumeLabel;
}
foreach (char c in Path.GetInvalidFileNameChars())
volumeLabel = volumeLabel.Replace(c, '_');
volumeLabel = volumeLabel?.Replace(c, '_');
return volumeLabel;
}
@@ -104,12 +106,12 @@ namespace MPF.Core.Data
};
// If we have an invalid device path, return null
if (string.IsNullOrWhiteSpace(devicePath))
if (string.IsNullOrEmpty(devicePath))
return null;
// 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);
@@ -159,7 +161,7 @@ namespace MPF.Core.Data
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
{
var drives = GetDriveList(ignoreFixedDrives);
drives = drives.OrderBy(i => i == null ? "\0" : i.Name).ToList();
drives = [.. drives.OrderBy(i => i == null ? "\0" : i.Name)];
return drives;
}
@@ -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.IsNullOrEmpty(this.Name) || !Directory.Exists(this.Name))
return defaultValue;
// We're going to assume for floppies, HDDs, and removable drives
@@ -267,7 +269,11 @@ namespace MPF.Core.Data
// Bandai Playdia Quick Interactive System
try
{
#if NET20 || NET35
List<string> files = [.. Directory.GetFiles(this.Name, "*", SearchOption.TopDirectoryOnly)];
#else
List<string> files = Directory.EnumerateFiles(this.Name, "*", SearchOption.TopDirectoryOnly).ToList();
#endif
if (files.Any(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
&& files.Any(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
@@ -277,8 +283,18 @@ namespace MPF.Core.Data
}
catch { }
// Bandai Pippin
if (File.Exists(Path.Combine(this.Name, "PippinAuthenticationFile")))
{
return RedumpSystem.BandaiPippin;
}
// Mattel Fisher-Price iXL
#if NET20 || NET35
if (File.Exists(Path.Combine(Path.Combine(this.Name, "iXL"), "iXLUpdater.exe")))
#else
if (File.Exists(Path.Combine(this.Name, "iXL", "iXLUpdater.exe")))
#endif
{
return RedumpSystem.MattelFisherPriceiXL;
}
@@ -287,7 +303,11 @@ namespace MPF.Core.Data
try
{
if (Directory.Exists(Path.Combine(this.Name, "$SystemUpdate"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
#endif
&& this.TotalSize <= 500_000_000)
{
return RedumpSystem.MicrosoftXbox360;
@@ -299,7 +319,11 @@ namespace MPF.Core.Data
try
{
if (Directory.Exists(Path.Combine(this.Name, "MSXC"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "MSXC")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "MSXC")).Any())
#endif
{
return RedumpSystem.MicrosoftXboxOne;
}
@@ -313,10 +337,17 @@ namespace MPF.Core.Data
}
// Sega Mega-CD / Sega-CD
#if NET20 || NET35
if (File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "IP.BIN"))
|| File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "SP.BIN"))
|| File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "SP_AS.BIN"))
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
#else
if (File.Exists(Path.Combine(this.Name, "_BOOT", "IP.BIN"))
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP.BIN"))
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP_AS.BIN"))
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
#endif
{
return RedumpSystem.SegaMegaCDSegaCD;
}
@@ -362,7 +393,11 @@ namespace MPF.Core.Data
// Is used as an on-disc update for the base game app without needing to get update from the internet.
// "/addcont/GAME_SERIAL/CONTENT_ID/ac.pkg" can be found in Redump entry 97619.
// Originally on disc as "/addcont/CUSA00288/FFXIVEXPS400001A/ac.pkg".
#if NET20 || NET35
if (File.Exists(Path.Combine(Path.Combine(Path.Combine(this.Name, "PS4"), "UPDATE"), "PS4UPDATE.PUP")))
#else
if (File.Exists(Path.Combine(this.Name, "PS4", "UPDATE", "PS4UPDATE.PUP")))
#endif
{
return RedumpSystem.SonyPlayStation4;
}
@@ -373,7 +408,7 @@ namespace MPF.Core.Data
return RedumpSystem.VTechVFlashVSmilePro;
}
#endregion
#endregion
#region Computers
@@ -398,13 +433,21 @@ namespace MPF.Core.Data
try
{
if (Directory.Exists(Path.Combine(this.Name, "AUDIO_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
#endif
{
return RedumpSystem.DVDAudio;
}
else if (Directory.Exists(Path.Combine(this.Name, "VIDEO_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
#endif
{
return RedumpSystem.DVDVideo;
}
@@ -415,7 +458,11 @@ namespace MPF.Core.Data
try
{
if (Directory.Exists(Path.Combine(this.Name, "HVDVD_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
#endif
{
return RedumpSystem.HDDVDVideo;
}
@@ -426,14 +473,18 @@ namespace MPF.Core.Data
try
{
if (Directory.Exists(Path.Combine(this.Name, "VCD"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "VCD")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VCD")).Any())
#endif
{
return RedumpSystem.VideoCD;
}
}
catch { }
#endregion
#endregion
// Default return
return defaultValue;
@@ -446,11 +497,11 @@ namespace MPF.Core.Data
public RedumpSystem? GetRedumpSystemFromVolumeLabel()
{
// If the volume label is empty, we can't do anything
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
if (string.IsNullOrEmpty(this.VolumeLabel))
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
@@ -499,7 +550,7 @@ namespace MPF.Core.Data
this.PopulateFromDriveInfo(driveInfo);
}
#endregion
#endregion
#region Helpers
@@ -541,6 +592,7 @@ namespace MPF.Core.Data
}
// Find and update all floppy drives
#if NET462_OR_GREATER || NETCOREAPP
try
{
CimSession session = CimSession.Create(null);
@@ -561,6 +613,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

@@ -8,23 +8,23 @@ namespace MPF.Core.Data
{
public class IniFile : IDictionary<string, string>
{
private Dictionary<string, string> _keyValuePairs = new();
private Dictionary<string, string> _keyValuePairs = [];
public string this[string key]
{
get
{
_keyValuePairs ??= new Dictionary<string, string>();
_keyValuePairs ??= [];
key = key.ToLowerInvariant();
if (_keyValuePairs.ContainsKey(key))
return _keyValuePairs[key];
if (_keyValuePairs.TryGetValue(key, out string? val))
return val;
return string.Empty;
}
set
{
_keyValuePairs ??= new Dictionary<string, string>();
_keyValuePairs ??= [];
key = key.ToLowerInvariant();
_keyValuePairs[key] = value;
@@ -102,13 +102,13 @@ namespace MPF.Core.Data
var line = sr.ReadLine()?.Trim();
// Empty lines are skipped
if (string.IsNullOrWhiteSpace(line))
if (string.IsNullOrEmpty(line))
{
// No-op, we don't process empty lines
}
// Comments start with ';'
else if (line.StartsWith(";"))
else if (line!.StartsWith(";"))
{
// No-op, we don't process comments
}
@@ -127,7 +127,7 @@ namespace MPF.Core.Data
// If the value field contains an '=', we need to put them back in
string key = data[0].Trim();
string value = string.Join("=", data.Skip(1)).Trim();
string value = string.Join("=", data.Skip(1).ToArray()).Trim();
// Section names are prepended to the key with a '.' separating
if (!string.IsNullOrEmpty(section))
@@ -196,7 +196,7 @@ namespace MPF.Core.Data
// If the key contains an '.', we need to put them back in
string newSection = data[0].Trim();
key = string.Join(".", data.Skip(1)).Trim();
key = string.Join(".", data.Skip(1).ToArray()).Trim();
// If we have a new section, write it out
if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))

View File

@@ -598,7 +598,7 @@ namespace MPF.Core.Data
/// <summary>
/// Determine if a complete set of Redump credentials might exist
/// </summary>
public bool HasRedumpLogin { get => !string.IsNullOrWhiteSpace(RedumpUsername) && !string.IsNullOrWhiteSpace(RedumpPassword); }
public bool HasRedumpLogin { get => !string.IsNullOrEmpty(RedumpUsername) && !string.IsNullOrEmpty(RedumpPassword); }
#endregion
@@ -608,7 +608,7 @@ namespace MPF.Core.Data
/// <param name="settings"></param>
public Options(Dictionary<string, string?>? settings = null)
{
this.Settings = settings ?? new Dictionary<string, string?>();
this.Settings = settings ?? [];
}
/// <summary>
@@ -617,7 +617,7 @@ namespace MPF.Core.Data
/// <param name="source"></param>
public Options(Options? source)
{
Settings = new Dictionary<string, string?>(source?.Settings ?? new Dictionary<string, string?>());
Settings = new Dictionary<string, string?>(source?.Settings ?? []);
}
/// <summary>

View File

@@ -1,5 +1,9 @@
using System;
#if NET20 || NET35
using System.Collections.Generic;
#else
using System.Collections.Concurrent;
#endif
using System.Threading;
using System.Threading.Tasks;
@@ -10,7 +14,11 @@ namespace MPF.Core.Data
/// <summary>
/// Internal queue to hold data to process
/// </summary>
#if NET20 || NET35
private readonly Queue<T> InternalQueue;
#else
private readonly ConcurrentQueue<T> InternalQueue;
#endif
/// <summary>
/// Custom processing step for dequeued data
@@ -24,10 +32,20 @@ namespace MPF.Core.Data
public ProcessingQueue(Action<T> customProcessing)
{
#if NET20 || NET35
this.InternalQueue = new Queue<T>();
#else
this.InternalQueue = new ConcurrentQueue<T>();
#endif
this.CustomProcessing = customProcessing;
this.TokenSource = new CancellationTokenSource();
#if NET20 || NET35
Task.Run(() => ProcessQueue());
#elif NET40
Task.Factory.StartNew(() => ProcessQueue());
#else
Task.Run(() => ProcessQueue(), this.TokenSource.Token);
#endif
}
/// <summary>
@@ -39,7 +57,7 @@ namespace MPF.Core.Data
/// Enqueue a new item for processing
/// </summary>
/// <param name="item"></param>
public void Enqueue(T item)
public void Enqueue(T? item)
{
// Only accept new data when not cancelled
if (item != null && !this.TokenSource.IsCancellationRequested)
@@ -54,7 +72,11 @@ namespace MPF.Core.Data
while (true)
{
// Nothing in the queue means we get to idle
#if NET20 || NET35
if (InternalQueue.Count == 0)
#else
if (InternalQueue.IsEmpty)
#endif
{
if (this.TokenSource.IsCancellationRequested)
break;
@@ -63,12 +85,17 @@ namespace MPF.Core.Data
continue;
}
#if NET20 || NET35
// Get the next item from the queue and invoke the lambda, if possible
this.CustomProcessing?.Invoke(this.InternalQueue.Dequeue());
#else
// Get the next item from the queue
if (!this.InternalQueue.TryDequeue(out var nextItem))
continue;
// Invoke the lambda, if possible
this.CustomProcessing?.Invoke(nextItem);
#endif
}
}
}

View File

@@ -1,9 +1,11 @@
namespace MPF.Core.Data
using System;
namespace MPF.Core.Data
{
/// <summary>
/// Generic success/failure result object, with optional message
/// </summary>
public class Result
public class Result : System.EventArgs
{
/// <summary>
/// Internal representation of success
@@ -44,6 +46,11 @@
/// <param name="message">String to add as a message</param>
public static Result Failure(string? message) => new(false, message ?? string.Empty);
internal static Result Success(object value)
{
throw new NotImplementedException();
}
/// <summary>
/// Results can be compared to boolean values based on the success value
/// </summary>

View File

@@ -4,10 +4,10 @@ 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;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core
@@ -65,7 +65,11 @@ namespace MPF.Core
/// <summary>
/// Generic way of reporting a message
/// </summary>
#if NET20 || NET35 || NET40
public EventHandler<BaseParameters.StringEventArgs>? ReportStatus;
#else
public EventHandler<string>? ReportStatus;
#endif
/// <summary>
/// Queue of items that need to be logged
@@ -75,12 +79,20 @@ namespace MPF.Core
/// <summary>
/// Event handler for data returned from a process
/// </summary>
#if NET20 || NET35 || 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>
#if NET20 || NET35 || NET40
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, new BaseParameters.StringEventArgs { Value = nextOutput });
#else
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, nextOutput);
#endif
#endregion
@@ -207,7 +219,11 @@ namespace MPF.Core
/// Execute the initial invocation of the dumping programs
/// </summary>
/// <param name="progress">Optional result progress callback</param>
#if NET40
public Result Run(IProgress<Result>? progress = null)
#else
public async Task<Result> Run(IProgress<Result>? progress = null)
#endif
{
// If we don't have parameters
if (Parameters == null)
@@ -230,10 +246,15 @@ namespace MPF.Core
progress?.Report(Result.Success($"Executing {InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
var directoryName = Path.GetDirectoryName(OutputPath);
if (!string.IsNullOrWhiteSpace(directoryName))
if (!string.IsNullOrEmpty(directoryName))
Directory.CreateDirectory(directoryName);
#if NET40
var executeTask = Task.Factory.StartNew(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
executeTask.Wait();
#else
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
#endif
progress?.Report(Result.Success($"{InternalProgram} has finished!"));
// Remove event handler if needed
@@ -256,7 +277,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)
{
@@ -267,10 +288,10 @@ namespace MPF.Core
var outputFilename = Path.GetFileName(OutputPath);
// Check to make sure that the output had all the correct files
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, Parameters, false);
(bool foundFiles, List<string> missingFiles) = Parameters.FoundAllFiles(outputDirectory, outputFilename, false);
if (!foundFiles)
{
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}"));
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
return Result.Failure("Error! Please check output directory as dump may be incomplete!");
}
@@ -291,7 +312,7 @@ namespace MPF.Core
if (seedInfo != null)
{
resultProgress?.Report(Result.Success("Injecting user-supplied information..."));
SubmissionInfoTool.InjectSubmissionInformation(submissionInfo, seedInfo);
Builder.InjectSubmissionInformation(submissionInfo, seedInfo);
resultProgress?.Report(Result.Success("Information injection complete!"));
}
@@ -325,12 +346,12 @@ namespace MPF.Core
// Process special fields for site codes
resultProgress?.Report(Result.Success("Processing site codes..."));
InfoTool.ProcessSpecialFields(submissionInfo);
Formatter.ProcessSpecialFields(submissionInfo);
resultProgress?.Report(Result.Success("Processing complete!"));
// Format the information for the text output
resultProgress?.Report(Result.Success("Formatting information..."));
(var formattedValues, var formatResult) = InfoTool.FormatOutputData(submissionInfo, Options);
(var formattedValues, var formatResult) = Formatter.FormatOutputData(submissionInfo, Options.EnableRedumpCompatibility);
if (formattedValues == null)
resultProgress?.Report(Result.Success(formatResult));
else
@@ -426,14 +447,18 @@ namespace MPF.Core
private static async Task<string> ExecuteInternalProgram(BaseParameters parameters)
{
Process childProcess;
#if NET40
string output = await Task.Factory.StartNew(() =>
#else
string output = await Task.Run(() =>
#endif
{
childProcess = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = parameters.ExecutablePath,
Arguments = parameters.GenerateParameters(),
FileName = parameters.ExecutablePath!,
Arguments = parameters.GenerateParameters()!,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
@@ -477,7 +502,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!");
@@ -492,7 +517,7 @@ namespace MPF.Core
private bool RequiredProgramsExist()
{
// Validate that the path is configured
if (string.IsNullOrWhiteSpace(Options.DiscImageCreatorPath))
if (string.IsNullOrEmpty(Options.DiscImageCreatorPath))
return false;
// Validate that the required program exists

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
@@ -197,8 +211,16 @@ namespace MPF.Core.Hashing
byte[] buffer = bufferSelect ? buffer0 : buffer1;
// Run hashes in parallel
#if NET20 || NET35
// Run hashers sequentially on each chunk
foreach (var h in hashers)
{
h.Value.Process(buffer, current);
}
#else
// Run hashers in parallel on each chunk
Parallel.ForEach(hashers, h => h.Value.Process(buffer, current));
#endif
// Wait for the load buffer worker, if needed
if (next > 0)
@@ -212,19 +234,29 @@ namespace MPF.Core.Hashing
// Finalize all hashing helpers
loadBuffer.Finish();
#if NET20 || NET35
foreach (var h in hashers)
{
h.Value.Terminate();
}
#else
Parallel.ForEach(hashers, h => h.Value.Terminate());
#endif
// 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();
@@ -245,7 +277,7 @@ namespace MPF.Core.Hashing
}
}
#endregion
#endregion
#region Hashing
@@ -260,7 +292,12 @@ namespace MPF.Core.Hashing
ha.TransformBlock(buffer, 0, size, null, 0);
break;
case NonCryptographicHashAlgorithm ncha:
#if NET20 || NET35 || NET40
byte[] bufferSpan = new byte[size];
Array.Copy(buffer, bufferSpan, size);
#else
var bufferSpan = new ReadOnlySpan<byte>(buffer, 0, size);
#endif
ncha.Append(bufferSpan);
break;
}
@@ -272,7 +309,7 @@ namespace MPF.Core.Hashing
/// <remarks>NonCryptographicHashAlgorithm implementations do not need finalization</remarks>
public void Terminate()
{
byte[] emptyBuffer = Array.Empty<byte>();
byte[] emptyBuffer = [];
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 NET20 || NET35 || 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 NET20 || NET35 || 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

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,63 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;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</RuntimeIdentifiers>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.0.3</VersionPrefix>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<VersionPrefix>3.0.0</VersionPrefix>
<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>
<InternalsVisibleTo Include="MPF.Test" />
</ItemGroup>
<!-- Support for old .NET versions -->
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="MinAsyncBridge" Version="0.12.4" />
<PackageReference Include="MinTasksExtensionsBridge" Version="0.3.4" />
<PackageReference Include="MinThreadingBridge" Version="0.11.4" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="IndexRange" Version="1.0.3" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="Microsoft.Net.Http" Version="2.2.29" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(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.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>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.0" GeneratePathProperty="true">
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.2" 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" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="psxt001z.Library" Version="0.21.0-beta3" />
<PackageReference Include="SabreTools.Models" Version="1.3.0" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.1" />
<PackageReference Include="SabreTools.Serialization" Version="1.3.0" />
</ItemGroup>
</Project>

View File

@@ -10,11 +10,11 @@ using System.Xml.Serialization;
using MPF.Core.Converters;
using MPF.Core.Data;
using SabreTools.Models.CueSheets;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using Schemas;
#pragma warning disable CS0618 // Ignore "Type or member is obsolete"
#pragma warning disable IDE0051 // Remove unused private members
#pragma warning disable IDE0059 // Unnecessary assignment of a value
namespace MPF.Core.Modules.Aaru
@@ -202,7 +202,7 @@ namespace MPF.Core.Modules.Aaru
var outputDirectory = Path.GetDirectoryName(basePath);
// Ensure that required sections exist
info = SubmissionInfoTool.EnsureAllSections(info);
info = Builder.EnsureAllSections(info);
// TODO: Determine if there's an Aaru version anywhere
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
@@ -223,12 +223,12 @@ namespace MPF.Core.Modules.Aaru
if (GetDiscType(sidecar, out var discType, out var discSubType))
{
string fullDiscType = string.Empty;
if (!string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
if (!string.IsNullOrEmpty(discType) && !string.IsNullOrEmpty(discSubType))
fullDiscType = $"{discType} ({discSubType})";
else if (!string.IsNullOrWhiteSpace(discType) && string.IsNullOrWhiteSpace(discSubType))
fullDiscType = discType;
else if (string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
fullDiscType = discSubType;
else if (!string.IsNullOrEmpty(discType) && string.IsNullOrEmpty(discSubType))
fullDiscType = discType!;
else if (string.IsNullOrEmpty(discType) && !string.IsNullOrEmpty(discSubType))
fullDiscType = discSubType!;
info.DumpingInfo.ReportedDiscType = fullDiscType;
}
@@ -285,7 +285,7 @@ namespace MPF.Core.Modules.Aaru
layerbreak = info.SizeAndChecksums!.Size > 25_025_314_816 ? "25025314816" : null;
// If we have a single-layer disc
if (string.IsNullOrWhiteSpace(layerbreak))
if (string.IsNullOrEmpty(layerbreak))
{
// Currently no-op
}
@@ -409,7 +409,7 @@ namespace MPF.Core.Modules.Aaru
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= new Dictionary<string, string>();
info.Artifacts ??= [];
if (File.Exists(basePath + ".cicm.xml"))
info.Artifacts["cicm"] = GetBase64(GetFullFile(basePath + ".cicm.xml")) ?? string.Empty;
if (File.Exists(basePath + ".ibg"))
@@ -456,7 +456,7 @@ namespace MPF.Core.Modules.Aaru
BaseCommand ??= CommandStrings.NONE;
if (!string.IsNullOrWhiteSpace(BaseCommand))
if (!string.IsNullOrEmpty(BaseCommand))
parameters.Add(BaseCommand);
else
return null;
@@ -1082,7 +1082,7 @@ namespace MPF.Core.Modules.Aaru
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageVerify:
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaInfo:
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaScan:
if (string.IsNullOrWhiteSpace(InputValue))
if (string.IsNullOrEmpty(InputValue))
return null;
parameters.Add($"\"{InputValue}\"");
@@ -1090,7 +1090,7 @@ namespace MPF.Core.Modules.Aaru
// Two input values
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageCompareLong:
if (string.IsNullOrWhiteSpace(Input1Value) || string.IsNullOrWhiteSpace(Input2Value))
if (string.IsNullOrEmpty(Input1Value) || string.IsNullOrEmpty(Input2Value))
return null;
parameters.Add($"\"{Input1Value}\"");
@@ -1101,7 +1101,7 @@ namespace MPF.Core.Modules.Aaru
case CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemExtract:
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageConvert:
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaDump:
if (string.IsNullOrWhiteSpace(InputValue) || string.IsNullOrWhiteSpace(OutputValue))
if (string.IsNullOrEmpty(InputValue) || string.IsNullOrEmpty(OutputValue))
return null;
parameters.Add($"\"{InputValue}\"");
@@ -1111,14 +1111,14 @@ namespace MPF.Core.Modules.Aaru
// Remote host value only
case CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceList:
case CommandStrings.Remote:
if (string.IsNullOrWhiteSpace(RemoteHostValue))
if (string.IsNullOrEmpty(RemoteHostValue))
return null;
parameters.Add($"\"{RemoteHostValue}\"");
break;
}
return string.Join(" ", parameters);
return string.Join(" ", [.. parameters]);
}
/// <inheritdoc/>
@@ -1128,48 +1128,42 @@ namespace MPF.Core.Modules.Aaru
{
#region Archive Family
[CommandStrings.ArchivePrefixLong + " " + CommandStrings.ArchiveInfo] = new List<string>()
{
},
[CommandStrings.ArchivePrefixLong + " " + CommandStrings.ArchiveInfo] = [],
#endregion
#region Database Family
[CommandStrings.DatabasePrefixLong + " " + CommandStrings.DatabaseStats] = new List<string>()
{
},
[CommandStrings.DatabasePrefixLong + " " + CommandStrings.DatabaseStats] = [],
[CommandStrings.DatabasePrefixLong + " " + CommandStrings.DatabaseUpdate] = new List<string>()
{
[CommandStrings.DatabasePrefixLong + " " + CommandStrings.DatabaseUpdate] =
[
FlagStrings.ClearLong,
FlagStrings.ClearAllLong,
},
],
#endregion
#region Device Family
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceInfo] = new List<string>()
{
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceInfo] =
[
FlagStrings.OutputPrefixLong,
},
],
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceList] = new List<string>()
{
},
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceList] = [],
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceReport] = new List<string>()
{
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceReport] =
[
FlagStrings.TrapDiscLong,
},
],
#endregion
#region Filesystem Family
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemExtract] = new List<string>()
{
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemExtract] =
[
FlagStrings.EncodingLong,
FlagStrings.EncodingShort,
FlagStrings.ExtendedAttributesLong,
@@ -1178,10 +1172,10 @@ namespace MPF.Core.Modules.Aaru
FlagStrings.NamespaceShort,
FlagStrings.OptionsLong,
FlagStrings.OptionsShort,
},
],
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemInfo] = new List<string>()
{
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemInfo] =
[
FlagStrings.EncodingLong,
FlagStrings.EncodingShort,
FlagStrings.ExtendedAttributesLong,
@@ -1190,10 +1184,10 @@ namespace MPF.Core.Modules.Aaru
FlagStrings.NamespaceShort,
FlagStrings.OptionsLong,
FlagStrings.OptionsShort,
},
],
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemListLong] = new List<string>()
{
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemListLong] =
[
FlagStrings.EncodingLong,
FlagStrings.EncodingShort,
FlagStrings.FilesystemsLong,
@@ -1202,18 +1196,16 @@ namespace MPF.Core.Modules.Aaru
FlagStrings.LongFormatShort,
FlagStrings.PartitionsLong,
FlagStrings.PartitionsShort,
},
],
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemOptions] = new List<string>()
{
},
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemOptions] = [],
#endregion
#region Image Family
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageChecksumLong] = new List<string>()
{
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageChecksumLong] =
[
FlagStrings.Adler32Long,
FlagStrings.Adler32Short,
FlagStrings.CRC16Long,
@@ -1235,14 +1227,12 @@ namespace MPF.Core.Modules.Aaru
FlagStrings.SpamSumShort,
FlagStrings.WholeDiscLong,
FlagStrings.WholeDiscShort,
},
],
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageCompareLong] = new List<string>()
{
},
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageCompareLong] = [],
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageConvert] = new List<string>()
{
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageConvert] =
[
FlagStrings.CommentsLong,
FlagStrings.CountLong,
FlagStrings.CountShort,
@@ -1275,20 +1265,20 @@ namespace MPF.Core.Modules.Aaru
FlagStrings.ResumeFileShort,
FlagStrings.XMLSidecarLong,
FlagStrings.XMLSidecarShort,
},
],
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageCreateSidecar] = new List<string>()
{
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageCreateSidecar] =
[
FlagStrings.BlockSizeLong,
FlagStrings.BlockSizeShort,
FlagStrings.EncodingLong,
FlagStrings.EncodingShort,
FlagStrings.TapeLong,
FlagStrings.TapeShort,
},
],
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageDecode] = new List<string>()
{
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageDecode] =
[
FlagStrings.DiskTagsLong,
FlagStrings.DiskTagsShort,
FlagStrings.LengthLong,
@@ -1297,28 +1287,24 @@ namespace MPF.Core.Modules.Aaru
FlagStrings.SectorTagsShort,
FlagStrings.StartLong,
FlagStrings.StartShort,
},
],
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageEntropy] = new List<string>()
{
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageEntropy] =
[
FlagStrings.DuplicatedSectorsLong,
FlagStrings.DuplicatedSectorsShort,
FlagStrings.SeparatedTracksLong,
FlagStrings.SeparatedTracksShort,
FlagStrings.WholeDiscLong,
FlagStrings.WholeDiscShort,
},
],
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageInfo] = new List<string>()
{
},
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageInfo] = [],
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageOptions] = new List<string>()
{
},
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageOptions] = [],
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImagePrint] = new List<string>()
{
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImagePrint] =
[
FlagStrings.LengthLong,
FlagStrings.LengthShort,
FlagStrings.LongSectorsLong,
@@ -1327,22 +1313,22 @@ namespace MPF.Core.Modules.Aaru
FlagStrings.StartShort,
FlagStrings.WidthLong,
FlagStrings.WidthShort,
},
],
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageVerify] = new List<string>()
{
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageVerify] =
[
FlagStrings.VerifyDiscLong,
FlagStrings.VerifyDiscShort,
FlagStrings.VerifySectorsLong,
FlagStrings.VerifySectorsShort,
},
],
#endregion
#region Media Family
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaDump] = new List<string>()
{
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaDump] =
[
FlagStrings.EjectLong,
FlagStrings.EncodingLong,
FlagStrings.EncodingShort,
@@ -1380,29 +1366,29 @@ namespace MPF.Core.Modules.Aaru
FlagStrings.UseBufferedReadsLong,
FlagStrings.XMLSidecarLong,
FlagStrings.XMLSidecarShort,
},
],
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaInfo] = new List<string>()
{
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaInfo] =
[
FlagStrings.OutputPrefixLong,
FlagStrings.OutputPrefixShort,
},
],
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaScan] = new List<string>()
{
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaScan] =
[
FlagStrings.ImgBurnLogLong,
FlagStrings.ImgBurnLogShort,
FlagStrings.MHDDLogLong,
FlagStrings.MHDDLogShort,
FlagStrings.UseBufferedReadsLong,
},
],
#endregion
#region Standalone Commands
[CommandStrings.NONE] = new List<string>()
{
[CommandStrings.NONE] =
[
FlagStrings.DebugLong,
FlagStrings.DebugShort,
FlagStrings.HelpLong,
@@ -1411,27 +1397,17 @@ namespace MPF.Core.Modules.Aaru
FlagStrings.VerboseLong,
FlagStrings.VerboseShort,
FlagStrings.VersionLong,
},
],
[CommandStrings.Configure] = new List<string>()
{
},
[CommandStrings.Configure] = [],
[CommandStrings.Formats] = new List<string>()
{
},
[CommandStrings.Formats] = [],
[CommandStrings.ListEncodings] = new List<string>()
{
},
[CommandStrings.ListEncodings] = [],
[CommandStrings.ListNamespaces] = new List<string>()
{
},
[CommandStrings.ListNamespaces] = [],
[CommandStrings.Remote] = new List<string>()
{
},
[CommandStrings.Remote] = [],
#endregion
};
@@ -1500,7 +1476,7 @@ namespace MPF.Core.Modules.Aaru
{
BaseCommand = CommandStrings.NONE;
flags = new Dictionary<string, bool?>();
flags = [];
BlockSizeValue = null;
CommentsValue = null;
@@ -1625,12 +1601,12 @@ namespace MPF.Core.Modules.Aaru
BaseCommand = CommandStrings.NONE;
// The string has to be valid by itself first
if (string.IsNullOrWhiteSpace(parameters))
if (string.IsNullOrEmpty(parameters))
return false;
// 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)
@@ -1670,7 +1646,7 @@ namespace MPF.Core.Modules.Aaru
// Determine what the commandline should look like given the first item
BaseCommand = NormalizeCommand(parts, ref start);
if (string.IsNullOrWhiteSpace(BaseCommand))
if (string.IsNullOrEmpty(BaseCommand))
return false;
// Set the start position
@@ -2175,7 +2151,7 @@ namespace MPF.Core.Modules.Aaru
private static string? NormalizeCommand(string baseCommand)
{
// If the base command is inavlid, just return nulls
if (string.IsNullOrWhiteSpace(baseCommand))
if (string.IsNullOrEmpty(baseCommand))
return null;
// Split the command otherwise
@@ -2297,11 +2273,11 @@ namespace MPF.Core.Modules.Aaru
}
// If the command itself is invalid, then return null
if (string.IsNullOrWhiteSpace(command))
if (string.IsNullOrEmpty(command))
return null;
// Combine the result
if (!string.IsNullOrWhiteSpace(family))
if (!string.IsNullOrEmpty(family))
return $"{family} {command}";
else
return command;
@@ -2384,7 +2360,7 @@ namespace MPF.Core.Modules.Aaru
var cueFiles = new List<CueFile>();
var cueSheet = new CueSheet
{
Performer = string.Join(", ", cicmSidecar.Performer ?? Array.Empty<string>()),
Performer = string.Join(", ", cicmSidecar.Performer ?? []),
};
// Only care about OpticalDisc types
@@ -2466,14 +2442,14 @@ namespace MPF.Core.Modules.Aaru
}
}
cueTrack.Indices = cueIndicies.ToArray();
cueTrack.Indices = [.. cueIndicies];
}
else
{
// Default if index data missing from sidecar
cueTrack.Indices = new CueIndex[]
{
new CueIndex
new()
{
Index = 1,
Minutes = 0,
@@ -2492,7 +2468,7 @@ namespace MPF.Core.Modules.Aaru
}
// If we have a cuesheet to write out, do so
cueSheet.Files = cueFiles.ToArray();
cueSheet.Files = [.. cueFiles];
if (cueSheet != null && cueSheet != default)
{
var ms = new SabreTools.Serialization.Streams.CueSheet().Serialize(cueSheet);
@@ -2722,7 +2698,7 @@ namespace MPF.Core.Modules.Aaru
// Assign the roms to a new game
datafile.Games = new Game[1];
datafile.Games[0] = new Game { Roms = roms.ToArray() };
datafile.Games[0] = new Game { Roms = [.. roms] };
return datafile;
}
@@ -2777,12 +2753,28 @@ namespace MPF.Core.Modules.Aaru
// Build each row in consecutive order
string pvd = string.Empty;
pvd += GenerateSectorOutputLine("0320", new ReadOnlySpan<byte>(pvdData, 0, 16));
pvd += GenerateSectorOutputLine("0330", new ReadOnlySpan<byte>(pvdData, 16, 16));
pvd += GenerateSectorOutputLine("0340", new ReadOnlySpan<byte>(pvdData, 32, 16));
pvd += GenerateSectorOutputLine("0350", new ReadOnlySpan<byte>(pvdData, 48, 16));
pvd += GenerateSectorOutputLine("0360", new ReadOnlySpan<byte>(pvdData, 64, 16));
pvd += GenerateSectorOutputLine("0370", new ReadOnlySpan<byte>(pvdData, 80, 16));
#if NET20 || NET35 || NET40
byte[] pvdLine = new byte[16];
Array.Copy(pvdData, 0, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0320", pvdLine);
Array.Copy(pvdData, 16, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0330", pvdLine);
Array.Copy(pvdData, 32, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0340", pvdLine);
Array.Copy(pvdData, 48, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0350", pvdLine);
Array.Copy(pvdData, 64, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0360", pvdLine);
Array.Copy(pvdData, 80, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0370", pvdLine);
#else
pvd += GenerateSectorOutputLine("0320", new ReadOnlySpan<byte>(pvdData, 0, 16).ToArray());
pvd += GenerateSectorOutputLine("0330", new ReadOnlySpan<byte>(pvdData, 16, 16).ToArray());
pvd += GenerateSectorOutputLine("0340", new ReadOnlySpan<byte>(pvdData, 32, 16).ToArray());
pvd += GenerateSectorOutputLine("0350", new ReadOnlySpan<byte>(pvdData, 48, 16).ToArray());
pvd += GenerateSectorOutputLine("0360", new ReadOnlySpan<byte>(pvdData, 64, 16).ToArray());
pvd += GenerateSectorOutputLine("0370", new ReadOnlySpan<byte>(pvdData, 80, 16).ToArray());
#endif
return pvd;
}
@@ -2883,7 +2875,7 @@ namespace MPF.Core.Modules.Aaru
pvdData.AddRange(new string((char)0, 14).ToCharArray().Select(c => (byte)c));
// Return the filled array
return pvdData.ToArray();
return [.. pvdData];
}
/// <summary>
@@ -2924,27 +2916,27 @@ namespace MPF.Core.Modules.Aaru
// Get and return the byte array
List<byte> dateTimeList = dateTimeString.ToCharArray().Select(c => (byte)c).ToList();
dateTimeList.Add(timeZoneNumber);
return dateTimeList.ToArray();
return [.. dateTimeList];
}
/// <summary>
/// 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>
private static string? GenerateSectorOutputLine(string row, ReadOnlySpan<byte> bytes)
private static string? GenerateSectorOutputLine(string row, byte[] bytes)
{
// If the data isn't correct, return null
if (bytes == null || bytes.Length != 16)
return null;
string pvdLine = $"{row} : ";
pvdLine += BitConverter.ToString(bytes[..8].ToArray()).Replace("-", " ");
pvdLine += BitConverter.ToString(bytes.Take(8).ToArray()).Replace("-", " ");
pvdLine += " ";
pvdLine += BitConverter.ToString(bytes.Slice(8, 8).ToArray().ToArray()).Replace("-", " ");
pvdLine += BitConverter.ToString(bytes.Skip(8).Take(8).ToArray()).Replace("-", " ");
pvdLine += " ";
pvdLine += Encoding.ASCII.GetString(bytes.ToArray()).Replace((char)0, '.').Replace('?', '.');
pvdLine += Encoding.ASCII.GetString([.. bytes]).Replace((char)0, '.').Replace('?', '.');
pvdLine += "\n";
return pvdLine;
@@ -2965,7 +2957,9 @@ namespace MPF.Core.Modules.Aaru
XmlReader xtr = XmlReader.Create(cicmSidecar, new XmlReaderSettings
{
CheckCharacters = false,
#if NET40_OR_GREATER || NETCOREAPP
DtdProcessing = DtdProcessing.Ignore,
#endif
IgnoreComments = true,
IgnoreWhitespace = true,
ValidationFlags = XmlSchemaValidationFlags.None,
@@ -3032,7 +3026,7 @@ namespace MPF.Core.Modules.Aaru
// Loop through each OpticalDisc in the metadata
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
{
if (!string.IsNullOrWhiteSpace(opticalDisc.CopyProtection))
if (!string.IsNullOrEmpty(opticalDisc.CopyProtection))
copyrightProtectionSystemType += $", {opticalDisc.CopyProtection}";
}
@@ -3084,9 +3078,9 @@ namespace MPF.Core.Modules.Aaru
var line = sr.ReadLine()?.Trim();
// Initialize on seeing the open tag
if (string.IsNullOrWhiteSpace(line))
if (string.IsNullOrEmpty(line))
continue;
else if (line.StartsWith("<BadBlocks>"))
else if (line!.StartsWith("<BadBlocks>"))
totalErrors = 0;
else if (line.StartsWith("</BadBlocks>"))
return totalErrors ?? -1;
@@ -3400,6 +3394,6 @@ namespace MPF.Core.Modules.Aaru
return false;
}
#endregion
#endregion
}
}

View File

@@ -14,11 +14,27 @@ namespace MPF.Core.Modules
{
#region Event Handlers
#if NET20 || NET35 || 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
@@ -32,7 +48,7 @@ namespace MPF.Core.Modules
/// <summary>
/// Set of flags to pass to the executable
/// </summary>
protected Dictionary<string, bool?> flags = new();
protected Dictionary<string, bool?> flags = [];
protected internal IEnumerable<string> Keys => flags.Keys;
/// <summary>
@@ -42,8 +58,8 @@ namespace MPF.Core.Modules
{
get
{
if (flags.ContainsKey(key))
return flags[key];
if (flags.TryGetValue(key, out bool? val))
return val;
return null;
}
@@ -216,9 +232,9 @@ namespace MPF.Core.Modules
return false;
if (this.BaseCommand == null)
return false;
if (!CommandSupport.ContainsKey(this.BaseCommand))
if (!CommandSupport.TryGetValue(this.BaseCommand, out var supported))
return false;
return CommandSupport[this.BaseCommand].Contains(flag);
return supported.Contains(flag);
}
/// <summary>
@@ -246,7 +262,7 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="parameters">String possibly representing parameters</param>
/// <returns>True if the parameters were set correctly, false otherwise</returns>
protected virtual bool ValidateAndSetParameters(string? parameters) => !string.IsNullOrWhiteSpace(parameters);
protected virtual bool ValidateAndSetParameters(string? parameters) => !string.IsNullOrEmpty(parameters);
#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,
@@ -278,8 +294,13 @@ namespace MPF.Core.Modules
// Start processing tasks, if necessary
if (!separateWindow)
{
#if NET40
Logging.OutputToLog(process.StandardOutput, this, ReportStatus);
Logging.OutputToLog(process.StandardError, this, ReportStatus);
#else
_ = Logging.OutputToLog(process.StandardOutput, this, ReportStatus);
_ = Logging.OutputToLog(process.StandardError, this, ReportStatus);
#endif
}
process.WaitForExit();
@@ -302,7 +323,7 @@ namespace MPF.Core.Modules
{ }
}
#endregion
#endregion
#region Parameter Parsing
@@ -910,7 +931,7 @@ namespace MPF.Core.Modules
return null;
}
else if (string.IsNullOrWhiteSpace(parts[i + 1]))
else if (string.IsNullOrEmpty(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
@@ -1104,7 +1125,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);
}
@@ -1115,8 +1136,6 @@ namespace MPF.Core.Modules
}
}
#endregion
}
}

View File

@@ -4,10 +4,9 @@ using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
#pragma warning disable IDE0051 // Remove unused private members
namespace MPF.Core.Modules.CleanRip
{
/// <summary>
@@ -64,7 +63,7 @@ namespace MPF.Core.Modules.CleanRip
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
{
// Ensure that required sections exist
info = SubmissionInfoTool.EnsureAllSections(info);
info = Builder.EnsureAllSections(info);
// TODO: Determine if there's a CleanRip version anywhere
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
@@ -107,7 +106,7 @@ namespace MPF.Core.Modules.CleanRip
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= new Dictionary<string, string>();
info.Artifacts ??= [];
if (File.Exists(basePath + ".bca"))
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true)) ?? string.Empty;
@@ -168,28 +167,28 @@ namespace MPF.Core.Modules.CleanRip
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(line))
if (string.IsNullOrEmpty(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
{
Games = new Game[]
{
new Game
Games =
[
new()
{
Roms = new Rom[]
{
Roms =
[
new Rom { Name = Path.GetFileName(iso), Size = size.ToString(), Crc = crc, Md5 = md5, Sha1 = sha1 },
}
]
}
}
]
};
}
catch
@@ -254,14 +253,14 @@ namespace MPF.Core.Modules.CleanRip
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(line))
if (string.IsNullOrEmpty(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}\" />";
@@ -300,21 +299,21 @@ namespace MPF.Core.Modules.CleanRip
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(line))
if (string.IsNullOrEmpty(line))
{
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

@@ -5,7 +5,7 @@ using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.DiscImageCreator
@@ -384,7 +384,7 @@ namespace MPF.Core.Modules.DiscImageCreator
var outputDirectory = Path.GetDirectoryName(basePath);
// Ensure that required sections exist
info = SubmissionInfoTool.EnsureAllSections(info);
info = Builder.EnsureAllSections(info);
// Get the dumping program and version
var (dicCmd, dicVersion) = GetCommandFilePathAndVersion(basePath);
@@ -442,7 +442,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// Attempt to get multisession data
string cdMultiSessionInfo = GetMultisessionInformation($"{basePath}_disc.txt") ?? string.Empty;
if (!string.IsNullOrWhiteSpace(cdMultiSessionInfo))
if (!string.IsNullOrEmpty(cdMultiSessionInfo))
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.Multisession] = cdMultiSessionInfo;
break;
@@ -521,6 +521,9 @@ namespace MPF.Core.Modules.DiscImageCreator
info.CopyProtection!.SecuROMData = GetFullFile($"{basePath}_subIntention.txt") ?? string.Empty;
}
// Needed for some odd copy protections
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt") ?? string.Empty;
break;
case RedumpSystem.DVDAudio:
@@ -543,7 +546,7 @@ namespace MPF.Core.Modules.DiscImageCreator
case RedumpSystem.MicrosoftXbox:
string xmidString;
if (string.IsNullOrWhiteSpace(outputDirectory))
if (string.IsNullOrEmpty(outputDirectory))
xmidString = GetXGD1XMID($"{basePath}_DMI.bin");
else
xmidString = GetXGD1XMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
@@ -592,7 +595,7 @@ namespace MPF.Core.Modules.DiscImageCreator
case RedumpSystem.MicrosoftXbox360:
string xemidString;
if (string.IsNullOrWhiteSpace(outputDirectory))
if (string.IsNullOrEmpty(outputDirectory))
xemidString = GetXGD23XeMID($"{basePath}_DMI.bin");
else
xemidString = GetXGD23XeMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
@@ -646,7 +649,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// 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));
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate))
{
@@ -664,7 +667,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// 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));
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Skip(16).ToArray());
if (GetSegaCDBuildInfo(info.Extras.Header, out var scdSerial, out var fixedDate))
{
@@ -682,7 +685,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// 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));
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate))
{
@@ -702,7 +705,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// 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));
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate))
{
@@ -722,7 +725,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// 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));
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate))
{
@@ -742,7 +745,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// 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));
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate))
{
@@ -760,7 +763,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// 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));
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
if (GetSaturnBuildInfo(info.Extras.Header, out var saturnSerial, out var saturnVersion, out var buildDate))
{
@@ -825,7 +828,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= new Dictionary<string, string>();
info.Artifacts ??= [];
//if (File.Exists($"{basePath}.c2"))
// info.Artifacts["c2"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.c2")) ?? string.Empty;
@@ -887,7 +890,7 @@ namespace MPF.Core.Modules.DiscImageCreator
BaseCommand ??= CommandStrings.NONE;
if (!string.IsNullOrWhiteSpace(BaseCommand))
if (!string.IsNullOrEmpty(BaseCommand))
parameters.Add(BaseCommand);
else
return null;
@@ -1016,7 +1019,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// BE Opcode
if (IsFlagSupported(FlagStrings.BEOpcode))
{
if (this[FlagStrings.BEOpcode] == true && this[FlagStrings.D8Opcode] == false)
if (this[FlagStrings.BEOpcode] == true && this[FlagStrings.D8Opcode] != true)
{
parameters.Add(FlagStrings.BEOpcode);
if (BEOpcodeValue != null
@@ -1360,7 +1363,7 @@ namespace MPF.Core.Modules.DiscImageCreator
parameters.Add(FlagStrings.VideoNowXP);
}
return string.Join(" ", parameters);
return string.Join(" ", [.. parameters]);
}
/// <inheritdoc/>
@@ -1368,8 +1371,8 @@ namespace MPF.Core.Modules.DiscImageCreator
{
return new Dictionary<string, List<string>>()
{
[CommandStrings.Audio] = new List<string>()
{
[CommandStrings.Audio] =
[
FlagStrings.BEOpcode,
FlagStrings.C2Opcode,
FlagStrings.D8Opcode,
@@ -1385,23 +1388,21 @@ namespace MPF.Core.Modules.DiscImageCreator
FlagStrings.ScanSectorProtect,
FlagStrings.SkipSector,
FlagStrings.SubchannelReadLevel,
},
],
[CommandStrings.BluRay] = new List<string>()
{
[CommandStrings.BluRay] =
[
FlagStrings.DatExpand,
FlagStrings.DisableBeep,
FlagStrings.DVDReread,
FlagStrings.ForceUnitAccess,
FlagStrings.UseAnchorVolumeDescriptorPointer,
},
],
[CommandStrings.Close] = new List<string>()
{
},
[CommandStrings.Close] = [],
[CommandStrings.CompactDisc] = new List<string>()
{
[CommandStrings.CompactDisc] =
[
FlagStrings.AddOffset,
FlagStrings.AMSF,
FlagStrings.AtariJaguar,
@@ -1426,10 +1427,10 @@ namespace MPF.Core.Modules.DiscImageCreator
FlagStrings.VideoNow,
FlagStrings.VideoNowColor,
FlagStrings.VideoNowXP,
},
],
[CommandStrings.Data] = new List<string>()
{
[CommandStrings.Data] =
[
FlagStrings.BEOpcode,
FlagStrings.C2Opcode,
FlagStrings.D8Opcode,
@@ -1445,10 +1446,10 @@ namespace MPF.Core.Modules.DiscImageCreator
FlagStrings.ScanSectorProtect,
FlagStrings.SkipSector,
FlagStrings.SubchannelReadLevel,
},
],
[CommandStrings.DigitalVideoDisc] = new List<string>()
{
[CommandStrings.DigitalVideoDisc] =
[
FlagStrings.CopyrightManagementInformation,
FlagStrings.DatExpand,
FlagStrings.DisableBeep,
@@ -1463,28 +1464,24 @@ namespace MPF.Core.Modules.DiscImageCreator
FlagStrings.ScanFileProtect,
FlagStrings.SkipSector,
FlagStrings.UseAnchorVolumeDescriptorPointer,
},
],
[CommandStrings.Disk] = new List<string>()
{
[CommandStrings.Disk] =
[
FlagStrings.DatExpand,
},
],
[CommandStrings.DriveSpeed] = new List<string>()
{
},
[CommandStrings.DriveSpeed] = [],
[CommandStrings.Eject] = new List<string>()
{
},
[CommandStrings.Eject] = [],
[CommandStrings.Floppy] = new List<string>()
{
[CommandStrings.Floppy] =
[
FlagStrings.DatExpand,
},
],
[CommandStrings.GDROM] = new List<string>()
{
[CommandStrings.GDROM] =
[
FlagStrings.BEOpcode,
FlagStrings.C2Opcode,
FlagStrings.D8Opcode,
@@ -1495,40 +1492,28 @@ namespace MPF.Core.Modules.DiscImageCreator
FlagStrings.NoFixSubQ,
FlagStrings.NoFixSubRtoW,
FlagStrings.SubchannelReadLevel,
},
],
[CommandStrings.MDS] = new List<string>()
{
},
[CommandStrings.MDS] = [],
[CommandStrings.Merge] = new List<string>()
{
},
[CommandStrings.Merge] = [],
[CommandStrings.Reset] = new List<string>()
{
},
[CommandStrings.Reset] = [],
[CommandStrings.SACD] = new List<string>()
{
[CommandStrings.SACD] =
[
FlagStrings.DatExpand,
FlagStrings.DisableBeep,
},
],
[CommandStrings.Start] = new List<string>()
{
},
[CommandStrings.Start] = [],
[CommandStrings.Stop] = new List<string>()
{
},
[CommandStrings.Stop] = [],
[CommandStrings.Sub] = new List<string>()
{
},
[CommandStrings.Sub] = [],
[CommandStrings.Swap] = new List<string>()
{
[CommandStrings.Swap] =
[
FlagStrings.AddOffset,
FlagStrings.BEOpcode,
FlagStrings.C2Opcode,
@@ -1549,48 +1534,44 @@ namespace MPF.Core.Modules.DiscImageCreator
FlagStrings.VideoNow,
FlagStrings.VideoNowColor,
FlagStrings.VideoNowXP,
},
],
[CommandStrings.Tape] = new List<string>()
{
},
[CommandStrings.Tape] = [],
[CommandStrings.Version] = new List<string>()
{
},
[CommandStrings.Version] = [],
[CommandStrings.XBOX] = new List<string>()
{
[CommandStrings.XBOX] =
[
FlagStrings.DatExpand,
FlagStrings.DisableBeep,
FlagStrings.DVDReread,
FlagStrings.ForceUnitAccess,
FlagStrings.NoSkipSS,
},
],
[CommandStrings.XBOXSwap] = new List<string>()
{
[CommandStrings.XBOXSwap] =
[
FlagStrings.DatExpand,
FlagStrings.DisableBeep,
FlagStrings.ForceUnitAccess,
FlagStrings.NoSkipSS,
},
],
[CommandStrings.XGD2Swap] = new List<string>()
{
[CommandStrings.XGD2Swap] =
[
FlagStrings.DatExpand,
FlagStrings.DisableBeep,
FlagStrings.ForceUnitAccess,
FlagStrings.NoSkipSS,
},
],
[CommandStrings.XGD3Swap] = new List<string>()
{
[CommandStrings.XGD3Swap] =
[
FlagStrings.DatExpand,
FlagStrings.DisableBeep,
FlagStrings.ForceUnitAccess,
FlagStrings.NoSkipSS,
},
],
};
}
@@ -1813,7 +1794,7 @@ namespace MPF.Core.Modules.DiscImageCreator
StartLBAValue = null;
EndLBAValue = null;
flags = new Dictionary<string, bool?>();
flags = [];
AddOffsetValue = null;
BEOpcodeValue = null;
@@ -1935,12 +1916,12 @@ namespace MPF.Core.Modules.DiscImageCreator
BaseCommand = CommandStrings.NONE;
// The string has to be valid by itself first
if (string.IsNullOrWhiteSpace(parameters))
if (string.IsNullOrEmpty(parameters))
return false;
// 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)
@@ -2578,7 +2559,7 @@ namespace MPF.Core.Modules.DiscImageCreator
private static (string?, string?) GetCommandFilePathAndVersion(string basePath)
{
// If we have an invalid base path, we can do nothing
if (string.IsNullOrWhiteSpace(basePath))
if (string.IsNullOrEmpty(basePath))
return (null, null);
// Generate the matching regex based on the base path
@@ -2587,12 +2568,12 @@ namespace MPF.Core.Modules.DiscImageCreator
// Find the first match for the command file
var parentDirectory = Path.GetDirectoryName(basePath);
if (string.IsNullOrWhiteSpace(parentDirectory))
if (string.IsNullOrEmpty(parentDirectory))
return (null, null);
var currentFiles = Directory.GetFiles(parentDirectory);
var commandPath = currentFiles.FirstOrDefault(f => cmdFilenameRegex.IsMatch(f));
if (string.IsNullOrWhiteSpace(commandPath))
if (string.IsNullOrEmpty(commandPath))
return (null, null);
// Extract the version string
@@ -2697,25 +2678,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);
}
@@ -2724,7 +2705,7 @@ namespace MPF.Core.Modules.DiscImageCreator
// Create the output string
if (discTypeOrBookTypeSet.Any())
discTypeOrBookType = string.Join(", ", discTypeOrBookTypeSet.OrderBy(s => s));
discTypeOrBookType = string.Join(", ", [.. discTypeOrBookTypeSet.OrderBy(s => s)]);
return true;
}
@@ -2767,9 +2748,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 +2773,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 +2786,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 +2795,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 +2857,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;
}
}
@@ -2908,18 +2889,18 @@ namespace MPF.Core.Modules.DiscImageCreator
serial = null; version = null; date = null;
// If the input header is null, we can't do a thing
if (string.IsNullOrWhiteSpace(segaHeader))
if (string.IsNullOrEmpty(segaHeader))
return false;
// 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 +2937,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 +3098,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 +3107,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))
@@ -3348,18 +3329,18 @@ namespace MPF.Core.Modules.DiscImageCreator
serial = null; version = null; date = null;
// If the input header is null, we can't do a thing
if (string.IsNullOrWhiteSpace(segaHeader))
if (string.IsNullOrEmpty(segaHeader))
return false;
// 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;
}
@@ -3381,23 +3362,23 @@ namespace MPF.Core.Modules.DiscImageCreator
serial = null; date = null;
// If the input header is null, we can't do a thing
if (string.IsNullOrWhiteSpace(segaHeader))
if (string.IsNullOrEmpty(segaHeader))
return false;
// 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

@@ -11,13 +11,15 @@ namespace MPF.Core.Modules.Redumper
public const string BluRay = "bd"; // Synonym for CD
public const string SACD = "sacd"; // Synonym for CD
public const string Dump = "dump";
public const string Info = "info";
public const string Protection = "protection";
public const string Refine = "refine";
public const string Split = "split";
public const string Verify = "verify";
public const string DVDKey = "dvdkey";
public const string DVDIsoKey = "dvdisokey";
public const string Protection = "protection";
public const string Split = "split";
public const string Hash = "hash";
public const string Info = "info";
public const string Skeleton = "skeleton";
}
/// <summary>

View File

@@ -5,11 +5,9 @@ using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
#pragma warning disable IDE0051 // Remove unused private members
namespace MPF.Core.Modules.Redumper
{
/// <summary>
@@ -255,7 +253,7 @@ namespace MPF.Core.Modules.Redumper
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
{
// Ensure that required sections exist
info = SubmissionInfoTool.EnsureAllSections(info);
info = Builder.EnsureAllSections(info);
// Get the dumping program and version
info.DumpingInfo!.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {GetVersion($"{basePath}.log") ?? "Unknown Version"}";
@@ -291,7 +289,7 @@ namespace MPF.Core.Modules.Redumper
// Attempt to get multisession data
string cdMultiSessionInfo = GetMultisessionInformation($"{basePath}.log") ?? string.Empty;
if (!string.IsNullOrWhiteSpace(cdMultiSessionInfo))
if (!string.IsNullOrEmpty(cdMultiSessionInfo))
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.Multisession] = cdMultiSessionInfo;
// Attempt to get the universal hash, if it's an audio disc
@@ -360,6 +358,9 @@ namespace MPF.Core.Modules.Redumper
case RedumpSystem.RainbowDisc:
case RedumpSystem.SonyElectronicBook:
info.CopyProtection!.SecuROMData = GetSecuROMData($"{basePath}.log") ?? string.Empty;
// Needed for some odd copy protections
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}.log") ?? string.Empty;
break;
case RedumpSystem.DVDAudio:
@@ -419,7 +420,7 @@ namespace MPF.Core.Modules.Redumper
// 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));
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
if (GetSaturnBuildInfo(info.Extras.Header, out var saturnSerial, out var saturnVersion, out var buildDate))
{
@@ -480,7 +481,7 @@ namespace MPF.Core.Modules.Redumper
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= new Dictionary<string, string>();
info.Artifacts ??= [];
if (File.Exists($"{basePath}.cdtext"))
info.Artifacts["cdtext"] = GetBase64(GetFullFile($"{basePath}.cdtext")) ?? string.Empty;
@@ -525,7 +526,7 @@ namespace MPF.Core.Modules.Redumper
{
var parameters = new List<string>();
ModeValues ??= new List<string> { CommandStrings.NONE };
ModeValues ??= [CommandStrings.NONE];
// Modes
parameters.AddRange(ModeValues);
@@ -726,7 +727,7 @@ namespace MPF.Core.Modules.Redumper
// Skip
if (this[FlagStrings.Skip] == true)
{
if (!string.IsNullOrWhiteSpace(SkipValue))
if (!string.IsNullOrEmpty(SkipValue))
parameters.Add($"{FlagStrings.Skip}={SkipValue}");
}
@@ -743,7 +744,7 @@ namespace MPF.Core.Modules.Redumper
#endregion
return string.Join(" ", parameters);
return string.Join(" ", [.. parameters]);
}
/// <inheritdoc/>
@@ -752,8 +753,8 @@ namespace MPF.Core.Modules.Redumper
{
return new Dictionary<string, List<string>>()
{
[CommandStrings.NONE] = new List<string>
{
[CommandStrings.NONE] =
[
// General
FlagStrings.HelpLong,
FlagStrings.HelpShort,
@@ -799,7 +800,7 @@ namespace MPF.Core.Modules.Redumper
FlagStrings.Skip,
FlagStrings.DumpReadSize,
FlagStrings.OverreadLeadout,
},
],
};
}
@@ -844,6 +845,12 @@ namespace MPF.Core.Modules.Redumper
case MediaType.DVD:
if (File.Exists($"{basePath}.log"))
logFiles.Add($"{basePath}.log");
if (File.Exists($"{basePath}.manufacturer"))
logFiles.Add($"{basePath}.manufacturer");
if (File.Exists($"{basePath}.1.manufacturer"))
logFiles.Add($"{basePath}.1.manufacturer");
if (File.Exists($"{basePath}.2.manufacturer"))
logFiles.Add($"{basePath}.2.manufacturer");
if (File.Exists($"{basePath}.physical"))
logFiles.Add($"{basePath}.physical");
if (File.Exists($"{basePath}.1.physical"))
@@ -888,7 +895,7 @@ namespace MPF.Core.Modules.Redumper
{
BaseCommand = CommandStrings.NONE;
flags = new Dictionary<string, bool?>();
flags = [];
// General
DriveValue = null;
@@ -937,18 +944,18 @@ namespace MPF.Core.Modules.Redumper
case MediaType.CDROM:
ModeValues = this.System switch
{
RedumpSystem.SuperAudioCD => new List<string> { CommandStrings.SACD },
_ => new List<string> { CommandStrings.CD },
RedumpSystem.SuperAudioCD => [CommandStrings.SACD],
_ => [CommandStrings.CD],
};
break;
case MediaType.DVD:
ModeValues = new List<string> { CommandStrings.DVD };
ModeValues = [CommandStrings.DVD];
break;
case MediaType.HDDVD: // TODO: Keep in sync if another command string shows up
ModeValues = new List<string> { CommandStrings.DVD };
ModeValues = [CommandStrings.DVD];
break;
case MediaType.BluRay:
ModeValues = new List<string> { CommandStrings.BluRay };
ModeValues = [CommandStrings.BluRay];
break;
default:
BaseCommand = null;
@@ -978,17 +985,17 @@ namespace MPF.Core.Modules.Redumper
}
// Set the output paths
if (!string.IsNullOrWhiteSpace(filename))
if (!string.IsNullOrEmpty(filename))
{
var imagePath = Path.GetDirectoryName(filename);
if (!string.IsNullOrWhiteSpace(imagePath))
if (!string.IsNullOrEmpty(imagePath))
{
this[FlagStrings.ImagePath] = true;
ImagePathValue = $"\"{imagePath}\"";
}
string imageName = Path.GetFileNameWithoutExtension(filename);
if (!string.IsNullOrWhiteSpace(imageName))
if (!string.IsNullOrEmpty(imageName))
{
this[FlagStrings.ImageName] = true;
ImageNameValue = $"\"{imageName}\"";
@@ -1005,19 +1012,19 @@ namespace MPF.Core.Modules.Redumper
BaseCommand = CommandStrings.NONE;
// The string has to be valid by itself first
if (string.IsNullOrWhiteSpace(parameters))
if (string.IsNullOrEmpty(parameters))
return false;
// 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)
.ToList();
// Setup the modes
ModeValues = new List<string>();
ModeValues = [];
// All modes should be cached separately
int index = 0;
@@ -1034,13 +1041,15 @@ namespace MPF.Core.Modules.Redumper
case CommandStrings.BluRay:
case CommandStrings.SACD:
case CommandStrings.Dump:
case CommandStrings.Protection:
case CommandStrings.Refine:
case CommandStrings.Split:
case CommandStrings.Verify:
case CommandStrings.DVDKey:
case CommandStrings.DVDIsoKey:
case CommandStrings.Protection:
case CommandStrings.Split:
case CommandStrings.Hash:
case CommandStrings.Info:
case CommandStrings.Skeleton:
ModeValues.Add(part);
break;
@@ -1083,7 +1092,7 @@ namespace MPF.Core.Modules.Redumper
// Drive
stringValue = ProcessStringParameter(parts, FlagStrings.Drive, ref i);
if (!string.IsNullOrWhiteSpace(stringValue))
if (!string.IsNullOrEmpty(stringValue))
DriveValue = stringValue;
// Speed
@@ -1098,13 +1107,13 @@ namespace MPF.Core.Modules.Redumper
// Image Path
stringValue = ProcessStringParameter(parts, FlagStrings.ImagePath, ref i);
if (!string.IsNullOrWhiteSpace(stringValue))
ImagePathValue = $"\"{stringValue.Trim('"')}\"";
if (!string.IsNullOrEmpty(stringValue))
ImagePathValue = $"\"{stringValue!.Trim('"')}\"";
// Image Name
stringValue = ProcessStringParameter(parts, FlagStrings.ImageName, ref i);
if (!string.IsNullOrWhiteSpace(stringValue))
ImageNameValue = $"\"{stringValue.Trim('"')}\"";
if (!string.IsNullOrEmpty(stringValue))
ImageNameValue = $"\"{stringValue!.Trim('"')}\"";
// Overwrite
ProcessFlagParameter(parts, FlagStrings.Overwrite, ref i);
@@ -1115,7 +1124,7 @@ namespace MPF.Core.Modules.Redumper
// Drive Type
stringValue = ProcessStringParameter(parts, FlagStrings.DriveType, ref i);
if (!string.IsNullOrWhiteSpace(stringValue))
if (!string.IsNullOrEmpty(stringValue))
DriveTypeValue = stringValue;
// Drive Read Offset
@@ -1135,12 +1144,12 @@ namespace MPF.Core.Modules.Redumper
// Drive Read Method
stringValue = ProcessStringParameter(parts, FlagStrings.DriveReadMethod, ref i);
if (!string.IsNullOrWhiteSpace(stringValue))
if (!string.IsNullOrEmpty(stringValue))
DriveReadMethodValue = stringValue;
// Drive Sector Order
stringValue = ProcessStringParameter(parts, FlagStrings.DriveSectorOrder, ref i);
if (!string.IsNullOrWhiteSpace(stringValue))
if (!string.IsNullOrEmpty(stringValue))
DriveSectorOrderValue = stringValue;
#endregion
@@ -1218,12 +1227,12 @@ namespace MPF.Core.Modules.Redumper
// Skip
stringValue = ProcessStringParameter(parts, FlagStrings.Skip, ref i);
if (!string.IsNullOrWhiteSpace(stringValue))
if (!string.IsNullOrEmpty(stringValue))
SkipValue = stringValue;
// Skip
intValue = ProcessInt32Parameter(parts, FlagStrings.DumpReadSize, ref i);
if (!string.IsNullOrWhiteSpace(stringValue))
if (!string.IsNullOrEmpty(stringValue))
DumpReadSizeValue = intValue;
// Overread Leadout
@@ -1233,7 +1242,7 @@ namespace MPF.Core.Modules.Redumper
}
// If the image name was not set, set it with a default value
if (string.IsNullOrWhiteSpace(this.ImageNameValue))
if (string.IsNullOrEmpty(this.ImageNameValue))
this.ImageNameValue = "track";
return true;
@@ -1264,7 +1273,7 @@ namespace MPF.Core.Modules.Redumper
// Now that we're at the relevant entries, read each line in and concatenate
string? cueString = string.Empty, line = sr.ReadLine()?.Trim();
while (!string.IsNullOrWhiteSpace(line))
while (!string.IsNullOrEmpty(line))
{
cueString += line + "\n";
line = sr.ReadLine()?.Trim();
@@ -1346,7 +1355,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();
@@ -1388,24 +1397,24 @@ 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"))
{
vobKeys = string.Empty;
line = sr.ReadLine()?.Trim();
while (!string.IsNullOrWhiteSpace(line))
while (!string.IsNullOrEmpty(line))
{
var match = Regex.Match(line, @"^(.*?): (.*?)$", RegexOptions.Compiled);
if (match.Success)
@@ -1482,7 +1491,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
@@ -1590,24 +1599,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();
}
}
@@ -1652,11 +1661,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
@@ -1878,7 +1887,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
@@ -1902,18 +1911,18 @@ namespace MPF.Core.Modules.Redumper
serial = null; version = null; date = null;
// If the input header is null, we can't do a thing
if (string.IsNullOrWhiteSpace(segaHeader))
if (string.IsNullOrEmpty(segaHeader))
return false;
// 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;
}
@@ -2006,7 +2015,7 @@ namespace MPF.Core.Modules.Redumper
lines.Add(line);
}
return string.Join("\n", lines).TrimEnd('\n');
return string.Join("\n", [.. lines]).TrimEnd('\n');
}
catch
{
@@ -2046,19 +2055,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:"))
{
@@ -2103,7 +2112,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
@@ -2135,7 +2144,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
@@ -2178,7 +2187,7 @@ namespace MPF.Core.Modules.Redumper
// Extract the version string
var match = regex.Match(sr.ReadLine()?.Trim() ?? string.Empty);
var version = match.Groups[1].Value;
return string.IsNullOrWhiteSpace(version) ? null : version;
return string.IsNullOrEmpty(version) ? null : version;
}
catch
{

View File

@@ -5,6 +5,7 @@ using System.Linq;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Hashing;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.UmdImageCreator
@@ -65,7 +66,7 @@ namespace MPF.Core.Modules.UmdImageCreator
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
{
// Ensure that required sections exist
info = SubmissionInfoTool.EnsureAllSections(info);
info = Builder.EnsureAllSections(info);
// TODO: Determine if there's a UMDImageCreator version anywhere
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
@@ -92,7 +93,7 @@ namespace MPF.Core.Modules.UmdImageCreator
info.VersionAndEditions!.Version = umdversion ?? string.Empty;
info.SizeAndChecksums!.Size = umdsize;
if (!string.IsNullOrWhiteSpace(umdlayer))
if (!string.IsNullOrEmpty(umdlayer))
info.SizeAndChecksums.Layerbreak = Int64.Parse(umdlayer ?? "-1");
}
@@ -102,7 +103,7 @@ namespace MPF.Core.Modules.UmdImageCreator
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= new Dictionary<string, string>();
info.Artifacts ??= [];
if (File.Exists(basePath + "_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt")) ?? string.Empty;
@@ -203,7 +204,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,14 @@ 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(() =>
#if NET40
var found = await Task.Factory.StartNew(() =>
{
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
@@ -38,14 +37,37 @@ namespace MPF.Core
return scanner.GetProtections(path);
});
#else
var found = await Task.Run(() =>
{
var scanner = new BinaryObjectScanner.Scanner(
options.ScanArchivesForProtection,
scanContents: true, // Hardcoded value to avoid issues
scanGameEngines: false, // Hardcoded value to avoid issues
options.ScanPackersForProtection,
scanPaths: true, // Hardcoded value to avoid issues
options.IncludeDebugProtectionInformation,
progress);
return scanner.GetProtections(path);
});
#endif
// If nothing was returned, return
if (found == null || !found.Any())
#if NET20 || NET35
if (found == null || found.Count == 0)
#else
if (found == null || found.IsEmpty)
#endif
return (null, null);
// Filter out any empty protections
var filteredProtections = found
.Where(kvp => kvp.Value != null && kvp.Value.Any())
#if NET20 || NET35
.Where(kvp => kvp.Value != null && kvp.Value.Count > 0)
#else
.Where(kvp => kvp.Value != null && !kvp.Value.IsEmpty)
#endif
.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.OrderBy(s => s).ToList());
@@ -78,7 +100,7 @@ namespace MPF.Core
// Sanitize and join protections for writing
string protectionString = SanitizeFoundProtections(orderedDistinctProtections);
if (string.IsNullOrWhiteSpace(protectionString))
if (string.IsNullOrEmpty(protectionString))
return "None found [OMIT FROM SUBMISSION]";
return protectionString;
@@ -95,18 +117,19 @@ namespace MPF.Core
if (string.IsNullOrEmpty(path))
return false;
return await Task.Run(() =>
#if NET40
return await Task.Factory.StartNew(() =>
{
try
{
var antiModchip = new PSXAntiModchip();
var antiModchip = new BinaryObjectScanner.Protection.PSXAntiModchip();
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
{
try
{
byte[] fileContent = File.ReadAllBytes(file);
var protection = antiModchip.CheckContents(file, fileContent, false);
if (!string.IsNullOrWhiteSpace(protection))
if (!string.IsNullOrEmpty(protection))
return true;
}
catch { }
@@ -116,6 +139,33 @@ namespace MPF.Core
return false;
});
#else
return await Task.Run(() =>
{
try
{
var antiModchip = new BinaryObjectScanner.Protection.PSXAntiModchip();
#if NET20 || NET35
foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories))
#else
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
#endif
{
try
{
byte[] fileContent = File.ReadAllBytes(file);
var protection = antiModchip.CheckContents(file, fileContent, false);
if (!string.IsNullOrEmpty(protection))
return true;
}
catch { }
}
}
catch { }
return false;
});
#endif
}
/// <summary>
@@ -129,7 +179,7 @@ namespace MPF.Core
if (!File.Exists(sub))
return null;
return LibCrypt.CheckSubfile(sub);
return LibCrypt.DetectLibCrypt([sub]);
}
/// <summary>
@@ -142,9 +192,15 @@ namespace MPF.Core
if (foundProtections.Any(p => p.StartsWith("[Exception opening file")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("[Exception opening file"));
#if NET20 || NET35 || 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 +281,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 NET20 || NET35 || 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
@@ -327,7 +392,7 @@ namespace MPF.Core
if (foundProtections.Any(p => p == "XCP") && foundProtections.Any(p => p.StartsWith("XCP") && p.Length > "XCP".Length))
foundProtections = foundProtections.Where(p => p != "XCP");
return string.Join(", ", foundProtections);
return string.Join(", ", foundProtections.ToArray());
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using BinaryObjectScanner;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Modules;
using MPF.Core.UI.ComboBoxItems;
using MPF.Core.Utilities;
using SabreTools.RedumpLib.Data;
@@ -495,14 +496,14 @@ namespace MPF.Core.UI.ViewModels
_options = OptionsLoader.LoadFromConfig();
// Added to clear warnings, all are set externally
_drives = new List<Drive>();
_driveSpeeds = new List<int>();
_internalPrograms = new List<Element<InternalProgram>>();
_drives = [];
_driveSpeeds = [];
_internalPrograms = [];
_outputPath = string.Empty;
_parameters = string.Empty;
_startStopButtonText = string.Empty;
_status = string.Empty;
_systems = new List<RedumpSystemComboBoxItem>();
_systems = [];
OptionsMenuItemEnabled = true;
SystemTypeComboBoxEnabled = true;
@@ -517,9 +518,9 @@ namespace MPF.Core.UI.ViewModels
EnableParametersCheckBoxEnabled = true;
LogPanelExpanded = _options.OpenLogWindowAtStartup;
MediaTypes = new List<Element<MediaType>>();
MediaTypes = [];
Systems = RedumpSystemComboBoxItem.GenerateElements().ToList();
InternalPrograms = new List<Element<InternalProgram>>();
InternalPrograms = [];
}
/// <summary>
@@ -585,7 +586,7 @@ namespace MPF.Core.UI.ViewModels
if (Drives.Count > 0)
{
VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", Drives.Select(d => d.Name))}");
VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", Drives.Select(d => d.Name).ToArray())}");
// Check for the last selected drive, if possible
int index = -1;
@@ -764,7 +765,7 @@ namespace MPF.Core.UI.ViewModels
{
SchemaVersion = 1,
FullyMatchedID = 3,
PartiallyMatchedIDs = new List<int> { 0, 1, 2, 3 },
PartiallyMatchedIDs = [0, 1, 2, 3],
Added = DateTime.UtcNow,
LastModified = DateTime.UtcNow,
@@ -778,8 +779,8 @@ namespace MPF.Core.UI.ViewModels
DiscTitle = "Install Disc",
Category = DiscCategory.Games,
Region = Region.World,
Languages = new Language?[] { Language.English, Language.Spanish, Language.French },
LanguageSelection = new LanguageSelection?[] { LanguageSelection.BiosSettings },
Languages = [Language.English, Language.Spanish, Language.French],
LanguageSelection = [LanguageSelection.BiosSettings],
Serial = "Disc Serial",
Layer0MasteringRing = "L0 Mastering Ring",
Layer0MasteringSID = "L0 Mastering SID",
@@ -817,7 +818,7 @@ namespace MPF.Core.UI.ViewModels
{
Version = "Original",
VersionDatfile = "Alt",
CommonEditions = new string[] { "Taikenban" },
CommonEditions = ["Taikenban"],
OtherEditions = "Rerelease",
},
@@ -855,7 +856,7 @@ namespace MPF.Core.UI.ViewModels
DumpersAndStatus = new DumpersAndStatusSection()
{
Status = DumpStatus.TwoOrMoreGreen,
Dumpers = new string[] { "Dumper1", "Dumper2" },
Dumpers = ["Dumper1", "Dumper2"],
OtherDumpers = "Dumper3",
},
@@ -863,7 +864,7 @@ namespace MPF.Core.UI.ViewModels
{
ClrMameProData = "Datfile",
Cuesheet = "Cuesheet",
CommonWriteOffsets = new int[] { 0, 12, -12 },
CommonWriteOffsets = [0, 12, -12],
OtherWriteOffsets = "-2",
},
@@ -1325,7 +1326,11 @@ namespace MPF.Core.UI.ViewModels
directory = Path.GetDirectoryName(directory);
if (directory != null && label != null)
#if NET20 || NET35
this.OutputPath = Path.Combine(Path.Combine(directory, label), filename);
#else
this.OutputPath = Path.Combine(directory, label, filename);
#endif
else
this.OutputPath = filename;
}
@@ -1348,7 +1353,11 @@ namespace MPF.Core.UI.ViewModels
directory = Path.GetDirectoryName(directory);
if (directory != null && label != null)
#if NET20 || NET35
this.OutputPath = Path.Combine(Path.Combine(directory, label), filename);
#else
this.OutputPath = Path.Combine(directory, label, filename);
#endif
else
this.OutputPath = filename;
}
@@ -1410,7 +1419,11 @@ namespace MPF.Core.UI.ViewModels
/// <summary>
/// Scan and show copy protection for the current disc
/// </summary>
#if NET40
public (string?, string?) ScanAndShowProtection()
#else
public async Task<(string?, string?)> ScanAndShowProtection()
#endif
{
// Determine current environment, just in case
_environment ??= DetermineEnvironment();
@@ -1430,7 +1443,13 @@ namespace MPF.Core.UI.ViewModels
var progress = new Progress<ProtectionProgress>();
progress.ProgressChanged += ProgressUpdated;
#if NET40
var protectionTask = Protection.RunProtectionScanOnPath(this.CurrentDrive.Name, this.Options, progress);
protectionTask.Wait();
var (protections, error) = protectionTask.Result;
#else
var (protections, error) = await Protection.RunProtectionScanOnPath(this.CurrentDrive.Name, this.Options, progress);
#endif
var output = Protection.FormatProtections(protections);
// If SmartE is detected on the current disc, remove `/sf` from the flags for DIC only -- Disabled until further notice
@@ -1494,7 +1513,7 @@ namespace MPF.Core.UI.ViewModels
{
// Set the drive speed list that's appropriate
this.DriveSpeeds = (List<int>)Interface.GetSpeedsForMediaType(CurrentMediaType);
VerboseLogLn($"Supported media speeds: {string.Join(", ", this.DriveSpeeds)}");
VerboseLogLn($"Supported media speeds: {string.Join(", ", this.DriveSpeeds.Select(ds => ds.ToString()).ToArray())}");
// Set the selected speed
int speed = (CurrentMediaType) switch
@@ -1592,7 +1611,11 @@ namespace MPF.Core.UI.ViewModels
_environment.ReportStatus += ProgressUpdated;
// Run the program with the parameters
#if NET40
Result result = _environment.Run(resultProgress);
#else
Result result = await _environment.Run(resultProgress);
#endif
// If we didn't execute a dumping command we cannot get submission output
if (_environment.Parameters?.IsDumpingCommand() != true)
@@ -1668,7 +1691,7 @@ namespace MPF.Core.UI.ViewModels
private bool ValidateBeforeDumping()
{
// Validate that we have an output path of any sort
if (string.IsNullOrWhiteSpace(_environment?.OutputPath))
if (string.IsNullOrEmpty(_environment?.OutputPath))
{
if (_displayUserMessage != null)
_ = _displayUserMessage("Missing Path", "No output path was provided so dumping cannot continue.", 1, false);
@@ -1677,10 +1700,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,11 +1715,11 @@ 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
(bool foundFiles, List<string> _) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, _environment.Parameters, true);
(bool foundFiles, List<string> _) = _environment.Parameters.FoundAllFiles(outputDirectory, outputFilename, true);
if (foundFiles && _displayUserMessage != null)
{
bool? mbresult = _displayUserMessage("Overwrite?", "A complete dump already exists! Are you sure you want to overwrite?", 2, true);
@@ -1710,7 +1733,7 @@ namespace MPF.Core.UI.ViewModels
// Validate that at least some space exists
// TODO: Tie this to the size of the disc, type of disc, etc.
string fullPath;
if (string.IsNullOrWhiteSpace(outputDirectory))
if (string.IsNullOrEmpty(outputDirectory))
fullPath = Path.GetFullPath(_environment.OutputPath);
else
fullPath = Path.GetFullPath(outputDirectory);
@@ -1730,19 +1753,28 @@ namespace MPF.Core.UI.ViewModels
return true;
}
#endregion
#endregion
#region Progress Reporting
/// <summary>
/// Handler for Result ProgressChanged event
/// </summary>
#if NET20 || NET35 || NET40
private void ProgressUpdated(object? sender, BaseParameters.StringEventArgs value)
#else
private void ProgressUpdated(object? sender, string value)
#endif
{
try
{
#if NET20 || NET35 || NET40
value.Value ??= string.Empty;
LogLn(value.Value);
#else
value ??= string.Empty;
LogLn(value);
#endif
}
catch { }
}

View File

@@ -8,6 +8,9 @@ using SabreTools.RedumpLib.Web;
namespace MPF.Core.UI.ViewModels
{
/// <summary>
/// Constructor
/// </summary>
public class OptionsViewModel : INotifyPropertyChanged
{
#region Fields
@@ -56,7 +59,15 @@ namespace MPF.Core.UI.ViewModels
#endregion
/// <summary>
/// Constructor
/// Constructor for pure view model
/// </summary>
public OptionsViewModel()
{
Options = new Options();
}
/// <summary>
/// Constructor for in-code
/// </summary>
public OptionsViewModel(Options baseOptions)
{
@@ -81,9 +92,19 @@ namespace MPF.Core.UI.ViewModels
/// <summary>
/// Test Redump login credentials
/// </summary>
#if NET40
public static Task<(bool?, string?)> TestRedumpLogin(string username, string password)
#else
public static async Task<(bool?, string?)> TestRedumpLogin(string username, string password)
#endif
{
#if NET40
return Task.Factory.StartNew(() => RedumpWebClient.ValidateCredentials(username, password));
#elif NETFRAMEWORK
return await Task.Run(() => RedumpWebClient.ValidateCredentials(username, password));
#else
return await RedumpHttpClient.ValidateCredentials(username, password);
#endif
}
#endregion

View File

@@ -8,33 +8,6 @@ namespace MPF.Core.Utilities
{
public static class EnumExtensions
{
/// <summary>
/// Determine if a system is okay if it's not detected by Windows
/// </summary>
/// <param name="system">RedumpSystem value to check</param>
/// <returns>True if Windows show see a disc when dumping, false otherwise</returns>
public static bool DetectedByWindows(this RedumpSystem? system)
{
return system switch
{
RedumpSystem.AmericanLaserGames3DO
or RedumpSystem.AppleMacintosh
or RedumpSystem.Atari3DO
or RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem
or RedumpSystem.NewJatreCDi
or RedumpSystem.NintendoGameCube
or RedumpSystem.NintendoWii
or RedumpSystem.NintendoWiiU
or RedumpSystem.PhilipsCDi
or RedumpSystem.PhilipsCDiDigitalVideo
or RedumpSystem.Panasonic3DOInteractiveMultiplayer
or RedumpSystem.PanasonicM2
or RedumpSystem.PioneerLaserActive
or RedumpSystem.SuperAudioCD => false,
_ => true,
};
}
/// <summary>
/// Determine if the media supports drive speeds
/// </summary>
@@ -55,69 +28,6 @@ namespace MPF.Core.Utilities
};
}
/// <summary>
/// Determine if a system has reversed ringcodes
/// </summary>
/// <param name="system">RedumpSystem value to check</param>
/// <returns>True if the system has reversed ringcodes, false otherwise</returns>
public static bool HasReversedRingcodes(this RedumpSystem? system)
{
return system switch
{
RedumpSystem.SonyPlayStation2
or RedumpSystem.SonyPlayStation3
or RedumpSystem.SonyPlayStation4
or RedumpSystem.SonyPlayStation5
or RedumpSystem.SonyPlayStationPortable => true,
_ => false,
};
}
/// <summary>
/// Determine if a system is considered audio-only
/// </summary>
/// <param name="system">RedumpSystem value to check</param>
/// <returns>True if the system is audio-only, false otherwise</returns>
/// <remarks>
/// Philips CD-i should NOT be in this list. It's being included until there's a
/// reasonable distinction between CD-i and CD-i ready on the database side.
/// </remarks>
public static bool IsAudio(this RedumpSystem? system)
{
return system switch
{
RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem
or RedumpSystem.AudioCD
or RedumpSystem.DVDAudio
or RedumpSystem.HasbroiONEducationalGamingSystem
or RedumpSystem.HasbroVideoNow
or RedumpSystem.HasbroVideoNowColor
or RedumpSystem.HasbroVideoNowJr
or RedumpSystem.HasbroVideoNowXP
or RedumpSystem.PhilipsCDi
or RedumpSystem.PlayStationGameSharkUpdates
or RedumpSystem.SuperAudioCD => true,
_ => false,
};
}
/// <summary>
/// Determine if a system is considered XGD
/// </summary>
/// <param name="system">RedumpSystem value to check</param>
/// <returns>True if the system is XGD, false otherwise</returns>
public static bool IsXGD(this RedumpSystem? system)
{
return system switch
{
RedumpSystem.MicrosoftXbox
or RedumpSystem.MicrosoftXbox360
or RedumpSystem.MicrosoftXboxOne
or RedumpSystem.MicrosoftXboxSeriesXS => true,
_ => false,
};
}
/// <summary>
/// List all programs with their short usable names
/// </summary>
@@ -127,7 +37,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,13 @@ 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 NET20 || NET35
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#elif NET40
public static void 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];
@@ -26,7 +32,15 @@ namespace MPF.Core.Utilities
while (true)
{
// Try to read the next chunk of characters
#if NET20 || NET35
read = await Task.Run(() => reader.Read(buffer, 0, buffer.Length));
#elif NET40
var readTask = Task.Factory.StartNew(() => reader.Read(buffer, 0, buffer.Length));
readTask.Wait();
read = readTask.Result;
#else
read = await reader.ReadAsync(buffer, 0, buffer.Length);
#endif
if (read == 0)
{
Thread.Sleep(10);
@@ -37,22 +51,38 @@ 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);
}
}
catch { }
finally
{
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
#else
handler?.Invoke(baseClass, sb.ToString());
#endif
}
}
@@ -63,14 +93,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 NET20 || NET35 || 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;
@@ -80,8 +118,13 @@ namespace MPF.Core.Utilities
if (i == 0)
{
sb.Append(split[i]);
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
sb = new();
#else
handler?.Invoke(baseClass, sb.ToString());
sb.Clear();
#endif
}
// For the last item, just append so it's dealt with the next time
@@ -93,7 +136,11 @@ namespace MPF.Core.Utilities
// For everything else, directly write out
else
{
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = split[i] });
#else
handler?.Invoke(baseClass, split[i]);
#endif
}
}
}
@@ -105,17 +152,26 @@ 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 NET20 || NET35 || 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');
// Append and log the first
sb.Append(split[0]);
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
sb = new();
#else
handler?.Invoke(baseClass, sb.ToString());
sb.Clear();
#endif
// Append the last
sb.Clear();
sb.Append($"\r{split[^1]}");
sb.Append($"\r{split[split.Length - 1]}");
}
}
}

View File

@@ -4,6 +4,7 @@ using System.IO;
using MPF.Core.Converters;
using MPF.Core.Data;
using Newtonsoft.Json;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Utilities
@@ -177,12 +178,12 @@ namespace MPF.Core.Utilities
else if (args[startIndex].StartsWith("-l=") || args[startIndex].StartsWith("--load-seed="))
{
string seedInfo = args[startIndex].Split('=')[1];
info = SubmissionInfoTool.CreateFromFile(seedInfo);
info = Builder.CreateFromFile(seedInfo);
}
else if (args[startIndex] == "-l" || args[startIndex] == "--load-seed")
{
string seedInfo = args[startIndex + 1];
info = SubmissionInfoTool.CreateFromFile(seedInfo);
info = Builder.CreateFromFile(seedInfo);
startIndex++;
}
@@ -218,8 +219,8 @@ namespace MPF.Core.Utilities
}
// Now deal with the complex options
options.ScanForProtection = scan && !string.IsNullOrWhiteSpace(parsedPath);
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrWhiteSpace(parsedPath);
options.ScanForProtection = scan && !string.IsNullOrEmpty(parsedPath);
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrEmpty(parsedPath);
return (options, info, parsedPath, startIndex);
}
@@ -247,7 +248,7 @@ namespace MPF.Core.Utilities
return supportedArguments;
}
#endregion
#endregion
#region Configuration

View File

@@ -1,4 +1,5 @@
using System;
using System.Net;
using System.Reflection;
using MPF.Core.Data;
using Newtonsoft.Json.Linq;
@@ -189,7 +190,7 @@ namespace MPF.Core.Utilities
// Get the latest tag from GitHub
var (tag, url) = GetRemoteVersionAndUrl();
bool different = version != tag;
bool different = version != tag && tag != null;
string message = $"Local version: {version}"
+ $"{Environment.NewLine}Remote version: {tag}"
@@ -230,13 +231,21 @@ namespace MPF.Core.Utilities
/// </summary>
private static (string? tag, string? url) GetRemoteVersionAndUrl()
{
#if NET20 || NET35 || NET40
// Not supported in .NET Frameworks 2.0, 3.5, or 4.0
return (null, null);
#else
using var hc = new System.Net.Http.HttpClient();
#if NET452
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
#endif
// TODO: Figure out a better way than having this hardcoded...
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);
@@ -248,6 +257,7 @@ namespace MPF.Core.Utilities
var releaseUrl = latestReleaseJson["html_url"]?.ToString();
return (latestTag, releaseUrl);
#endif
}
#endregion

View File

@@ -15,12 +15,12 @@ namespace MPF.Test.Core.Converters
/// <summary>
/// DiscType values that map to InternalDriveType
/// </summary>
private static readonly DriveType[] _mappableDriveTypes = new DriveType[]
{
private static readonly DriveType[] _mappableDriveTypes =
[
DriveType.CDRom,
DriveType.Fixed,
DriveType.Removable,
};
];
/// <summary>
/// Check that every supported DriveType maps to an InternalDriveType
@@ -49,9 +49,9 @@ namespace MPF.Test.Core.Converters
foreach (DriveType driveType in Enum.GetValues(typeof(DriveType)))
{
if (_mappableDriveTypes.Contains(driveType))
testData.Add(new object?[] { driveType, false });
testData.Add([driveType, false]);
else
testData.Add(new object?[] { driveType, true });
testData.Add([driveType, true]);
}
return testData;
@@ -84,7 +84,7 @@ namespace MPF.Test.Core.Converters
var testData = new List<object?[]>() { new object?[] { null } };
foreach (InternalProgram? internalProgram in Enum.GetValues(typeof(InternalProgram)))
{
testData.Add(new object?[] { internalProgram });
testData.Add([internalProgram]);
}
return testData;

View File

@@ -12,8 +12,8 @@ namespace MPF.Test.Core.Utilities
/// <summary>
/// MediaType values that support drive speeds
/// </summary>
private static readonly MediaType?[] _supportDriveSpeeds = new MediaType?[]
{
private static readonly MediaType?[] _supportDriveSpeeds =
[
MediaType.CDROM,
MediaType.DVD,
MediaType.GDROM,
@@ -21,13 +21,13 @@ namespace MPF.Test.Core.Utilities
MediaType.BluRay,
MediaType.NintendoGameCubeGameDisc,
MediaType.NintendoWiiOpticalDisc,
};
];
/// <summary>
/// RedumpSystem values that are considered Audio
/// </summary>
private static readonly RedumpSystem?[] _audioSystems = new RedumpSystem?[]
{
private static readonly RedumpSystem?[] _audioSystems =
[
RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem,
RedumpSystem.AudioCD,
RedumpSystem.DVDAudio,
@@ -39,40 +39,41 @@ namespace MPF.Test.Core.Utilities
RedumpSystem.PlayStationGameSharkUpdates,
RedumpSystem.PhilipsCDi,
RedumpSystem.SuperAudioCD,
};
];
/// <summary>
/// RedumpSystem values that are considered markers
/// </summary>
private static readonly RedumpSystem?[] _markerSystems = new RedumpSystem?[]
{
private static readonly RedumpSystem?[] _markerSystems =
[
RedumpSystem.MarkerArcadeEnd,
RedumpSystem.MarkerComputerEnd,
RedumpSystem.MarkerDiscBasedConsoleEnd,
RedumpSystem.MarkerOtherEnd,
};
];
/// <summary>
/// RedumpSystem values that are have reversed ringcodes
/// </summary>
private static readonly RedumpSystem?[] _reverseRingcodeSystems = new RedumpSystem?[]
{
private static readonly RedumpSystem?[] _reverseRingcodeSystems =
[
RedumpSystem.SonyPlayStation2,
RedumpSystem.SonyPlayStation3,
RedumpSystem.SonyPlayStation4,
RedumpSystem.SonyPlayStation5,
RedumpSystem.SonyPlayStationPortable,
};
];
/// <summary>
/// RedumpSystem values that are considered XGD
/// </summary>
private static readonly RedumpSystem?[] _xgdSystems = new RedumpSystem?[]
{
private static readonly RedumpSystem?[] _xgdSystems =
[
RedumpSystem.MicrosoftXbox,
RedumpSystem.MicrosoftXbox360,
RedumpSystem.MicrosoftXboxOne,
RedumpSystem.MicrosoftXboxSeriesXS,
};
];
/// <summary>
/// Check that all optical media support drive speeds
@@ -149,9 +150,9 @@ namespace MPF.Test.Core.Utilities
foreach (MediaType mediaType in Enum.GetValues(typeof(MediaType)))
{
if (_supportDriveSpeeds.Contains(mediaType))
testData.Add(new object?[] { mediaType, true });
testData.Add([mediaType, true]);
else
testData.Add(new object?[] { mediaType, false });
testData.Add([mediaType, false]);
}
return testData;
@@ -167,9 +168,9 @@ namespace MPF.Test.Core.Utilities
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_audioSystems.Contains(redumpSystem))
testData.Add(new object?[] { redumpSystem, true });
testData.Add([redumpSystem, true]);
else
testData.Add(new object?[] { redumpSystem, false });
testData.Add([redumpSystem, false]);
}
return testData;
@@ -185,9 +186,9 @@ namespace MPF.Test.Core.Utilities
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_markerSystems.Contains(redumpSystem))
testData.Add(new object?[] { redumpSystem, true });
testData.Add([redumpSystem, true]);
else
testData.Add(new object?[] { redumpSystem, false });
testData.Add([redumpSystem, false]);
}
return testData;
@@ -203,9 +204,9 @@ namespace MPF.Test.Core.Utilities
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_reverseRingcodeSystems.Contains(redumpSystem))
testData.Add(new object?[] { redumpSystem, true });
testData.Add([redumpSystem, true]);
else
testData.Add(new object?[] { redumpSystem, false });
testData.Add([redumpSystem, false]);
}
return testData;
@@ -221,9 +222,9 @@ namespace MPF.Test.Core.Utilities
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_xgdSystems.Contains(redumpSystem))
testData.Add(new object?[] { redumpSystem, true });
testData.Add([redumpSystem, true]);
else
testData.Add(new object?[] { redumpSystem, false });
testData.Add([redumpSystem, false]);
}
return testData;

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.IO;
using MPF.Core;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using Xunit;
@@ -8,50 +9,9 @@ namespace MPF.Test.Library
{
public class InfoToolTests
{
[Theory]
[InlineData(null, 0, 0, 0, 0, null)]
[InlineData(null, 12345, 0, 0, 0, null)]
[InlineData(null, 12345, 1, 0, 0, null)]
[InlineData(null, 12345, 1, 2, 0, null)]
[InlineData(null, 12345, 1, 2, 3, null)]
[InlineData(MediaType.CDROM, 0, 0, 0, 0, "CD-ROM")]
[InlineData(MediaType.CDROM, 12345, 0, 0, 0, "CD-ROM")]
[InlineData(MediaType.CDROM, 12345, 1, 0, 0, "CD-ROM")]
[InlineData(MediaType.CDROM, 12345, 1, 2, 0, "CD-ROM")]
[InlineData(MediaType.CDROM, 12345, 1, 2, 3, "CD-ROM")]
[InlineData(MediaType.DVD, 0, 0, 0, 0, "DVD-ROM-5")]
[InlineData(MediaType.DVD, 12345, 0, 0, 0, "DVD-ROM-5")]
[InlineData(MediaType.DVD, 12345, 1, 0, 0, "DVD-ROM-9")]
[InlineData(MediaType.DVD, 12345, 1, 2, 0, "DVD-ROM-9")]
[InlineData(MediaType.DVD, 12345, 1, 2, 3, "DVD-ROM-9")]
[InlineData(MediaType.BluRay, 0, 0, 0, 0, "BD-ROM-25")]
[InlineData(MediaType.BluRay, 12345, 0, 0, 0, "BD-ROM-25")]
[InlineData(MediaType.BluRay, 26_843_531_857, 0, 0, 0, "BD-ROM-33")]
[InlineData(MediaType.BluRay, 12345, 1, 0, 0, "BD-ROM-50")]
[InlineData(MediaType.BluRay, 53_687_063_713, 1, 0, 0, "BD-ROM-66")]
[InlineData(MediaType.BluRay, 12345, 1, 2, 0, "BD-ROM-100")]
[InlineData(MediaType.BluRay, 12345, 1, 2, 3, "BD-ROM-128")]
[InlineData(MediaType.UMD, 0, 0, 0, 0, "UMD-SL")]
[InlineData(MediaType.UMD, 12345, 0, 0, 0, "UMD-SL")]
[InlineData(MediaType.UMD, 12345, 1, 0, 0, "UMD-DL")]
[InlineData(MediaType.UMD, 12345, 1, 2, 0, "UMD-DL")]
[InlineData(MediaType.UMD, 12345, 1, 2, 3, "UMD-DL")]
public void GetFixedMediaTypeTest(
MediaType? mediaType,
long size,
long layerbreak,
long layerbreak2,
long layerbreak3,
string? expected)
{
// TODO: Add tests around BDU
var actual = InfoTool.GetFixedMediaType(mediaType, null, size, layerbreak, layerbreak2, layerbreak3);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(null, "")]
[InlineData(" ", "")]
[InlineData(" ", " ")]
[InlineData("super\\blah.bin", "super\\blah.bin")]
[InlineData("super\\hero\\blah.bin", "super\\hero\\blah.bin")]
[InlineData("super.hero\\blah.bin", "super.hero\\blah.bin")]
@@ -60,7 +20,7 @@ namespace MPF.Test.Library
[InlineData("superhero\\blah&foo.bin", "superhero\\blah&foo.bin")]
public void NormalizeOutputPathsTest(string? outputPath, string? expectedPath)
{
if (!string.IsNullOrWhiteSpace(expectedPath))
if (!string.IsNullOrEmpty(expectedPath))
expectedPath = Path.GetFullPath(expectedPath);
string actualPath = InfoTool.NormalizeOutputPaths(outputPath, true);
@@ -90,7 +50,7 @@ namespace MPF.Test.Library
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
Formatter.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
@@ -117,7 +77,7 @@ namespace MPF.Test.Library
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
Formatter.ProcessSpecialFields(info);
// Validate
Assert.Null(info.CommonDiscInfo);
@@ -146,7 +106,7 @@ namespace MPF.Test.Library
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
Formatter.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
@@ -180,7 +140,7 @@ namespace MPF.Test.Library
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
Formatter.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);

View File

@@ -11,11 +11,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsActiveMARKTest()
{
List<string> protections = new()
{
List<string> protections =
[
"ActiveMARK",
"ActiveMARK 5",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("ActiveMARK 5", sanitized);
@@ -24,11 +24,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsCactusDataShieldTest()
{
List<string> protections = new()
{
List<string> protections =
[
"Cactus Data Shield 200",
"Cactus Data Shield 200 (Build 3.0.100a)",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Cactus Data Shield 200 (Build 3.0.100a)", sanitized);
@@ -37,11 +37,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsCDCheckTest()
{
List<string> protections = new()
{
List<string> protections =
[
"Anything Else Protection",
"Executable-Based CD Check",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
@@ -50,11 +50,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsCDCopsTest()
{
List<string> protections = new()
{
List<string> protections =
[
"CD-Cops",
"CD-Cops v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("CD-Cops v1.2.0", sanitized);
@@ -63,11 +63,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsCDKeyTest()
{
List<string> protections = new()
{
List<string> protections =
[
"Anything Else Protection",
"CD-Key / Serial",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
@@ -76,11 +76,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsEACdKeyTest()
{
List<string> protections = new()
{
List<string> protections =
[
"EA CdKey Registration Module",
"EA CdKey Registration Module v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("EA CdKey Registration Module v1.2.0", sanitized);
@@ -89,11 +89,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsEADRMTest()
{
List<string> protections = new()
{
List<string> protections =
[
"EA DRM Protection",
"EA DRM Protection v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("EA DRM Protection v1.2.0", sanitized);
@@ -102,11 +102,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsGFWLTest()
{
List<string> protections = new()
{
List<string> protections =
[
"Games for Windows LIVE",
"Games for Windows LIVE v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Games for Windows LIVE v1.2.0", sanitized);
@@ -115,11 +115,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsGFWLZDPPTest()
{
List<string> protections = new()
{
List<string> protections =
[
"Games for Windows LIVE",
"Games for Windows LIVE Zero Day Piracy Protection",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Games for Windows LIVE, Games for Windows LIVE Zero Day Piracy Protection", sanitized);
@@ -128,11 +128,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsImpulseReactorTest()
{
List<string> protections = new()
{
List<string> protections =
[
"Impulse Reactor",
"Impulse Reactor Core Module v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Impulse Reactor Core Module v1.2.0", sanitized);
@@ -145,14 +145,14 @@ namespace MPF.Test.Library
[InlineData(3)]
public void SanitizeFoundProtectionsJoWoodXProtTest(int skip)
{
List<string> protections = new()
{
List<string> protections =
[
"JoWood X-Prot 1.2.0.00",
"JoWood X-Prot v2",
"JoWood X-Prot v1.4+",
"JoWood X-Prot v1.0-v1.3",
"JoWood X-Prot",
};
];
// Safeguard for the future
if (skip >= protections.Count)
@@ -168,11 +168,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsOnlineRegistrationTest()
{
List<string> protections = new()
{
List<string> protections =
[
"Anything Else Protection",
"Executable-Based Online Registration",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
@@ -185,14 +185,14 @@ namespace MPF.Test.Library
[InlineData(3)]
public void SanitizeFoundProtectionStarForceTest(int skip)
{
List<string> protections = new()
{
List<string> protections =
[
"StarForce 1.20.000.000",
"StarForce 5 [Protected Module]",
"StarForce 5",
"StarForce 3-5",
"StarForce",
};
];
// Safeguard for the future
if (skip >= protections.Count)
@@ -208,11 +208,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsSysiphusTest()
{
List<string> protections = new()
{
List<string> protections =
[
"Sysiphus",
"Sysiphus v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Sysiphus v1.2.0", sanitized);
@@ -221,11 +221,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsXCPTest()
{
List<string> protections = new()
{
List<string> protections =
[
"XCP",
"XCP v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("XCP v1.2.0", sanitized);

View File

@@ -2,8 +2,11 @@
<PropertyGroup>
<TargetFrameworks>net6.0-windows;net8.0-windows</TargetFrameworks>
<IsPackable>false</IsPackable>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
@@ -14,19 +17,19 @@
<PackageReference Include="Microsoft.CodeCoverage" Version="17.9.0-preview-23531-01" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0-preview-23531-01" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.2.0" />
<PackageReference Include="xunit" Version="2.6.1" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.1" />
<PackageReference Include="xunit" Version="2.6.2" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="1.5.0" />
<PackageReference Include="xunit.assert" Version="2.6.1" />
<PackageReference Include="xunit.core" Version="2.6.1" />
<PackageReference Include="xunit.extensibility.core" Version="2.6.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.6.1" />
<PackageReference Include="xunit.runner.console" Version="2.6.1">
<PackageReference Include="xunit.analyzers" Version="1.6.0" />
<PackageReference Include="xunit.assert" Version="2.6.2" />
<PackageReference Include="xunit.core" Version="2.6.2" />
<PackageReference Include="xunit.extensibility.core" Version="2.6.2" />
<PackageReference Include="xunit.extensibility.execution" Version="2.6.2" />
<PackageReference Include="xunit.runner.console" Version="2.6.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -269,7 +269,7 @@ namespace MPF.Test.RedumpLib
foreach (Language? language in fullLanguages)
{
var code = language.TwoLetterCode();
if (string.IsNullOrWhiteSpace(code))
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
@@ -296,7 +296,7 @@ namespace MPF.Test.RedumpLib
foreach (Language? language in fullLanguages)
{
var code = language.ThreeLetterCode();
if (string.IsNullOrWhiteSpace(code))
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
@@ -323,7 +323,7 @@ namespace MPF.Test.RedumpLib
foreach (Language? language in fullLanguages)
{
var code = language.ThreeLetterCodeAlt();
if (string.IsNullOrWhiteSpace(code))
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
@@ -492,7 +492,7 @@ namespace MPF.Test.RedumpLib
foreach (Region? region in fullRegions)
{
var code = region.ShortName();
if (string.IsNullOrWhiteSpace(code))
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists

View File

@@ -16,7 +16,12 @@ namespace MPF.UI.Core
public static DoubleCollection SpeedsForDVDAsCollection { get; } = GetDoubleCollectionFromIntList(DVD);
public static DoubleCollection SpeedsForHDDVDAsCollection { get; } = GetDoubleCollectionFromIntList(HDDVD);
public static DoubleCollection SpeedsForBDAsCollection { get; } = GetDoubleCollectionFromIntList(BD);
#if NET20 || NET35 || NET40
private static DoubleCollection GetDoubleCollectionFromIntList(IList<int> list)
#else
private static DoubleCollection GetDoubleCollectionFromIntList(IReadOnlyList<int> list)
#endif
=> new(list.Select(i => Convert.ToDouble(i)).ToList());
}
}

View File

@@ -5,14 +5,9 @@
WindowStartupLocation="CenterScreen"
WindowStyle="None"
ResizeMode="NoResize" SizeToContent="WidthAndHeight"
TextOptions.TextFormattingMode="Display" TextOptions.TextRenderingMode="ClearType" UseLayoutRounding="True"
Title="" MinHeight="155" MaxWidth="470" MinWidth="154"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />

View File

@@ -1,7 +1,11 @@
using System;
using System.Drawing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using MPF.UI.Core;
#pragma warning disable IDE1006 // Naming Styles
namespace WPFCustomMessageBox
{
@@ -28,11 +32,19 @@ namespace WPFCustomMessageBox
{
get
{
#if NET35
return _TextBlock_Message!.Text;
#else
return TextBlock_Message.Text;
#endif
}
set
{
#if NET35
_TextBlock_Message!.Text = value;
#else
TextBlock_Message.Text = value;
#endif
}
}
@@ -40,11 +52,19 @@ namespace WPFCustomMessageBox
{
get
{
#if NET35
return _Label_Ok!.Content.ToString();
#else
return Label_Ok.Content.ToString();
#endif
}
set
{
#if NET35
_Label_Ok!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Ok.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
@@ -52,11 +72,19 @@ namespace WPFCustomMessageBox
{
get
{
#if NET35
return _Label_Cancel!.Content.ToString();
#else
return Label_Cancel.Content.ToString();
#endif
}
set
{
#if NET35
_Label_Cancel!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Cancel.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
@@ -64,11 +92,19 @@ namespace WPFCustomMessageBox
{
get
{
#if NET35
return _Label_Yes!.Content.ToString();
#else
return Label_Yes.Content.ToString();
#endif
}
set
{
#if NET35
_Label_Yes!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Yes.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
@@ -76,19 +112,59 @@ namespace WPFCustomMessageBox
{
get
{
#if NET35
return _Label_No!.Content.ToString();
#else
return Label_No.Content.ToString();
#endif
}
set
{
#if NET35
_Label_No!.Content = value.TryAddKeyboardAccellerator();
#else
Label_No.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
public MessageBoxResult Result { get; set; }
#if NET35
private Button? _Button_Cancel => ItemHelper.FindChild<Button>(this, "Button_Cancel");
private Button? _Button_No => ItemHelper.FindChild<Button>(this, "Button_No");
private Button? _Button_OK => ItemHelper.FindChild<Button>(this, "Button_OK");
private Button? _Button_Yes => ItemHelper.FindChild<Button>(this, "Button_Yes");
private System.Windows.Controls.Image? _Image_MessageBox => ItemHelper.FindChild<System.Windows.Controls.Image>(this, "Image_MessageBox");
private Label? _Label_Cancel => ItemHelper.FindChild<Label>(this, "Label_Cancel");
private Label? _Label_No => ItemHelper.FindChild<Label>(this, "Label_No");
private Label? _Label_Ok => ItemHelper.FindChild<Label>(this, "Label_Ok");
private Label? _Label_Yes => ItemHelper.FindChild<Label>(this, "Label_Yes");
private TextBlock? _TextBlock_Message => ItemHelper.FindChild<TextBlock>(this, "TextBlock_Message");
#endif
internal CustomMessageBoxWindow(Window? owner, string? message, string? caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
{
#if NET40_OR_GREATER || NETCOREAPP
System.Windows.Media.TextOptions.SetTextFormattingMode(this, System.Windows.Media.TextFormattingMode.Display);
System.Windows.Media.TextOptions.SetTextRenderingMode(this, System.Windows.Media.TextRenderingMode.ClearType);
UseLayoutRounding = true;
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
_removeTitleBarIcon = removeTitleBarIcon;
Focusable = true;
@@ -109,7 +185,11 @@ namespace WPFCustomMessageBox
if (image.HasValue)
DisplayImage(image.Value);
else
#if NET35
_Image_MessageBox!.Visibility = Visibility.Collapsed;
#else
Image_MessageBox.Visibility = Visibility.Collapsed;
#endif
}
protected override void OnSourceInitialized(EventArgs e)
@@ -126,39 +206,75 @@ namespace WPFCustomMessageBox
{
case MessageBoxButton.OKCancel:
// Hide all but OK, Cancel
#if NET35
_Button_OK!.Visibility = Visibility.Visible;
_Button_OK.Focus();
_Button_Cancel!.Visibility = Visibility.Visible;
_Button_Yes!.Visibility = Visibility.Collapsed;
_Button_No!.Visibility = Visibility.Collapsed;
#else
Button_OK.Visibility = Visibility.Visible;
Button_OK.Focus();
Button_Cancel.Visibility = Visibility.Visible;
Button_Yes.Visibility = Visibility.Collapsed;
Button_No.Visibility = Visibility.Collapsed;
#endif
break;
case MessageBoxButton.YesNo:
// Hide all but Yes, No
#if NET35
_Button_Yes!.Visibility = Visibility.Visible;
_Button_Yes.Focus();
_Button_No!.Visibility = Visibility.Visible;
_Button_OK!.Visibility = Visibility.Collapsed;
_Button_Cancel!.Visibility = Visibility.Collapsed;
#else
Button_Yes.Visibility = Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = Visibility.Visible;
Button_OK.Visibility = Visibility.Collapsed;
Button_Cancel.Visibility = Visibility.Collapsed;
#endif
break;
case MessageBoxButton.YesNoCancel:
// Hide only OK
#if NET35
_Button_Yes!.Visibility = Visibility.Visible;
_Button_Yes.Focus();
_Button_No!.Visibility = Visibility.Visible;
_Button_Cancel!.Visibility = Visibility.Visible;
_Button_OK!.Visibility = Visibility.Collapsed;
#else
Button_Yes.Visibility = Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = Visibility.Visible;
Button_Cancel.Visibility = Visibility.Visible;
Button_OK.Visibility = Visibility.Collapsed;
#endif
break;
default:
// Hide all but OK
#if NET35
_Button_OK!.Visibility = Visibility.Visible;
_Button_OK.Focus();
_Button_Yes!.Visibility = Visibility.Collapsed;
_Button_No!.Visibility = Visibility.Collapsed;
_Button_Cancel!.Visibility = Visibility.Collapsed;
#else
Button_OK.Visibility = Visibility.Visible;
Button_OK.Focus();
Button_Yes.Visibility = Visibility.Collapsed;
Button_No.Visibility = Visibility.Collapsed;
Button_Cancel.Visibility = Visibility.Collapsed;
#endif
break;
}
}
@@ -186,8 +302,13 @@ namespace WPFCustomMessageBox
break;
}
#if NET35
_Image_MessageBox!.Source = icon.ToImageSource();
_Image_MessageBox.Visibility = Visibility.Visible;
#else
Image_MessageBox.Source = icon.ToImageSource();
Image_MessageBox.Visibility = Visibility.Visible;
#endif
}
private void Button_OK_Click(object sender, RoutedEventArgs e)

124
MPF.UI.Core/ItemHelper.cs Normal file
View File

@@ -0,0 +1,124 @@
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
namespace MPF.UI.Core
{
internal static class ItemHelper
{
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T? FindChild<T>(DependencyObject? parent, string childName) where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T? foundChild = null;
if (parent is ItemsControl itemsControl && itemsControl.Items != null)
{
int childrenCount = itemsControl.Items.Count;
for (int i = 0; i < childrenCount; i++)
{
var child = itemsControl.Items[i] as DependencyObject;
// If the child is not of the request child type child
if (child is not T)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null)
break;
}
else if (!string.IsNullOrEmpty(childName))
{
// If the child's name is set for search
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
}
else if (parent is ContentControl contentControl && contentControl.Content != null)
{
var child = contentControl.Content as DependencyObject;
// If the child is not of the request child type child
if (child is not T)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
}
else if (!string.IsNullOrEmpty(childName))
{
// If the child's name is set for search
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
}
}
else
{
// child element found.
foundChild = (T)child;
}
}
else if (parent is Visual)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
if (child is not T)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null)
break;
}
else if (!string.IsNullOrEmpty(childName))
{
// If the child's name is set for search
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
}
return foundChild;
}
}
}

View File

@@ -1,14 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFrameworks>net6.0-windows;net8.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
<!-- Assembly Properties -->
<TargetFrameworks>net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0-windows;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<ImportFrameworkWinFXTargets Condition="$(TargetFramework.StartsWith(`net3`))">true</ImportFrameworkWinFXTargets>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.0.3</VersionPrefix>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<Nullable>enable</Nullable>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<VersionPrefix>3.0.0</VersionPrefix>
<Description>Common code for all MPF UI 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>
@@ -20,8 +32,16 @@
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
</ItemGroup>
<!-- Support for old .NET versions -->
<ItemGroup Condition="$(TargetFramework.StartsWith(`net3`))">
<Reference Include="PresentationBuildTasks" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationBuildTasks.dll" />
<Reference Include="PresentationCore" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll" />
<Reference Include="PresentationFramework" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll" />
<Reference Include="WindowsBase" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.RedumpLib" Version="1.2.0" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -7,6 +7,8 @@ using System.Windows.Media;
using System.Windows.Threading;
using MPF.Core.Data;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.UserControls
{
public partial class LogOutput : UserControl
@@ -31,9 +33,20 @@ namespace MPF.UI.Core.UserControls
/// </summary>
private Run? lastLine = null;
#if NET35
private Button? _ClearButton => ItemHelper.FindChild<Button>(this, "ClearButton");
private RichTextBox? _Output => ItemHelper.FindChild<RichTextBox>(this, "Output");
private ScrollViewer? _OutputViewer => ItemHelper.FindChild<ScrollViewer>(this, "OutputViewer");
private Button? _SaveButton => ItemHelper.FindChild<Button>(this, "SaveButton");
#endif
public LogOutput()
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
// Update the internal state
Document = new FlowDocument()
@@ -47,13 +60,24 @@ namespace MPF.UI.Core.UserControls
LogQueue = new ProcessingQueue<LogLine>(ProcessLogLine);
// Add handlers
#if NET35
_OutputViewer!.SizeChanged += OutputViewerSizeChanged;
_Output!.TextChanged += OnTextChanged;
_ClearButton!.Click += OnClearButton;
_SaveButton!.Click += OnSaveButton;
#else
OutputViewer.SizeChanged += OutputViewerSizeChanged;
Output.TextChanged += OnTextChanged;
ClearButton.Click += OnClearButton;
SaveButton.Click += OnSaveButton;
#endif
// Update the internal state
#if NET35
_Output.Document = Document;
#else
Output.Document = Document;
#endif
}
#region Logging
@@ -93,18 +117,13 @@ namespace MPF.UI.Core.UserControls
/// <returns>Brush representing the color</returns>
public Brush GetForegroundColor()
{
switch (this.LogLevel)
return this.LogLevel switch
{
case LogLevel.SECRET:
return Brushes.Blue;
case LogLevel.ERROR:
return Brushes.Red;
case LogLevel.VERBOSE:
return Brushes.Yellow;
case LogLevel.USER:
default:
return Brushes.White;
}
LogLevel.SECRET => Brushes.Blue,
LogLevel.ERROR => Brushes.Red,
LogLevel.VERBOSE => Brushes.Yellow,
_ => Brushes.White,
};
}
/// <summary>
@@ -170,7 +189,7 @@ namespace MPF.UI.Core.UserControls
});
}
#endregion
#endregion
#region Helpers
@@ -184,20 +203,22 @@ namespace MPF.UI.Core.UserControls
/// </summary>
private void SaveInlines()
{
using (var sw = new StreamWriter(File.OpenWrite("console.log")))
using var sw = new StreamWriter(File.OpenWrite("console.log"));
foreach (var inline in _paragraph.Inlines)
{
foreach (var inline in _paragraph.Inlines)
{
if (inline is Run run)
sw.Write(run.Text);
}
if (inline is Run run)
sw.Write(run.Text);
}
}
/// <summary>
/// Scroll the current view to the bottom
/// </summary>
#if NET35
public void ScrollToBottom() => _OutputViewer!.ScrollToBottom();
#else
public void ScrollToBottom() => OutputViewer.ScrollToBottom();
#endif
#endregion

View File

@@ -39,7 +39,7 @@ namespace MPF.UI.Core.UserControls
public static readonly DependencyProperty VerticalScrollBarVisibilityProperty =
DependencyProperty.Register("VerticalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(UserInput));
#endregion
#region Properties
@@ -118,7 +118,9 @@ namespace MPF.UI.Core.UserControls
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
}
}
}

View File

@@ -15,10 +15,7 @@
<Window.Resources>
<core:ElementConverter x:Key="ElementConverter" />
</Window.Resources>
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical" Width="500" MaxHeight="650">
<Grid Margin="0,10,0,0">
@@ -34,7 +31,7 @@
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<TextBlock TextAlignment="Center"><Bold>Disc Information</Bold></TextBlock>
<Run FontWeight="Bold" Text="Disc Information" />
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
@@ -175,6 +172,10 @@
<controls:UserInput x:Name="CommentsTextBox" Label="Comments"
Text="{Binding SubmissionInfo.CommonDiscInfo.Comments, Mode=TwoWay}" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top" />
<controls:UserInput x:Name="DiscKeyTextBox" Label="Disc Key" Visibility="Collapsed"
Text="{Binding Path=SubmissionInfo.Extras.DiscKey, Mode=TwoWay}"/>
<controls:UserInput x:Name="DiscIDTextBox" Label="Disc ID" Visibility="Collapsed"
Text="{Binding Path=SubmissionInfo.Extras.DiscID, Mode=TwoWay}"/>
<controls:UserInput x:Name="GenreTextBox" Label="Genre"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Genre], Mode=TwoWay}"/>
<controls:UserInput x:Name="ProtectionTextBox" Label="Protection" ToolTip="CAUTION: Only edit if you know what you are doing!"

View File

@@ -1,10 +1,14 @@
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using MPF.Core.Data;
using MPF.Core.UI.ViewModels;
using MPF.Core.Utilities;
using MPF.UI.Core.UserControls;
using SabreTools.RedumpLib.Data;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.Windows
{
/// <summary>
@@ -12,6 +16,105 @@ namespace MPF.UI.Core.Windows
/// </summary>
public partial class DiscInformationWindow : WindowBase
{
#if NET35
#region Common Info
private Grid? _LanguageSelectionGrid => ItemHelper.FindChild<Grid>(this, "LanguageSelectionGrid");
#endregion
#region Additional Info
private UserInput? _CommentsTextBox => ItemHelper.FindChild<UserInput>(this, "CommentsTextBox");
private UserInput? _DiscIDTextBox => ItemHelper.FindChild<UserInput>(this, "DiscIDTextBox");
private UserInput? _DiscKeyTextBox => ItemHelper.FindChild<UserInput>(this, "DiscKeyTextBox");
#endregion
#region Contents
private UserInput? _ExtrasTextBox => ItemHelper.FindChild<UserInput>(this, "ExtrasTextBox");
private UserInput? _GameFootageTextBox => ItemHelper.FindChild<UserInput>(this, "GameFootageTextBox");
private UserInput? _GamesTextBox => ItemHelper.FindChild<UserInput>(this, "GamesTextBox");
private UserInput? _GeneralContent => ItemHelper.FindChild<UserInput>(this, "GeneralContent");
private UserInput? _NetYarozeGamesTextBox => ItemHelper.FindChild<UserInput>(this, "NetYarozeGamesTextBox");
private UserInput? _PatchesTextBox => ItemHelper.FindChild<UserInput>(this, "PatchesTextBox");
private UserInput? _PlayableDemosTextBox => ItemHelper.FindChild<UserInput>(this, "PlayableDemosTextBox");
private UserInput? _RollingDemosTextBox => ItemHelper.FindChild<UserInput>(this, "RollingDemosTextBox");
private UserInput? _SavegamesTextBox => ItemHelper.FindChild<UserInput>(this, "SavegamesTextBox");
private UserInput? _TechDemosTextBox => ItemHelper.FindChild<UserInput>(this, "TechDemosTextBox");
private UserInput? _VideosTextBox => ItemHelper.FindChild<UserInput>(this, "VideosTextBox");
#endregion
#region Ringcodes
private GroupBox? _L0Info => ItemHelper.FindChild<GroupBox>(this, "L0Info");
private UserInput? _L0MasteringRing => ItemHelper.FindChild<UserInput>(this, "L0MasteringRing");
private UserInput? _L0MasteringSID => ItemHelper.FindChild<UserInput>(this, "L0MasteringSID");
private UserInput? _L0Toolstamp => ItemHelper.FindChild<UserInput>(this, "L0Toolstamp");
private UserInput? _L0MouldSID => ItemHelper.FindChild<UserInput>(this, "L0MouldSID");
private UserInput? _L0AdditionalMould => ItemHelper.FindChild<UserInput>(this, "L0AdditionalMould");
private GroupBox? _L1Info => ItemHelper.FindChild<GroupBox>(this, "L1Info");
private UserInput? _L1MasteringRing => ItemHelper.FindChild<UserInput>(this, "L1MasteringRing");
private UserInput? _L1MasteringSID => ItemHelper.FindChild<UserInput>(this, "L1MasteringSID");
private UserInput? _L1Toolstamp => ItemHelper.FindChild<UserInput>(this, "L1Toolstamp");
private UserInput? _L1MouldSID => ItemHelper.FindChild<UserInput>(this, "L1MouldSID");
private UserInput? _L1AdditionalMould => ItemHelper.FindChild<UserInput>(this, "L1AdditionalMould");
private GroupBox? _L2Info => ItemHelper.FindChild<GroupBox>(this, "L2Info");
private UserInput? _L2MasteringRing => ItemHelper.FindChild<UserInput>(this, "L2MasteringRing");
private UserInput? _L2MasteringSID => ItemHelper.FindChild<UserInput>(this, "L2MasteringSID");
private UserInput? _L2Toolstamp => ItemHelper.FindChild<UserInput>(this, "L2Toolstamp");
private GroupBox? _L3Info => ItemHelper.FindChild<GroupBox>(this, "L3Info");
private UserInput? _L3MasteringRing => ItemHelper.FindChild<UserInput>(this, "L3MasteringRing");
private UserInput? _L3MasteringSID => ItemHelper.FindChild<UserInput>(this, "L3MasteringSID");
private UserInput? _L3Toolstamp => ItemHelper.FindChild<UserInput>(this, "L3Toolstamp");
#endregion
#region Read-Only Info
private UserInput? _FullyMatchedID => ItemHelper.FindChild<UserInput>(this, "FullyMatchedID");
private UserInput? _PartiallyMatchedIDs => ItemHelper.FindChild<UserInput>(this, "PartiallyMatchedIDs");
private UserInput? _AntiModchip => ItemHelper.FindChild<UserInput>(this, "AntiModchip");
private UserInput? _DiscOffset => ItemHelper.FindChild<UserInput>(this, "DiscOffset");
private UserInput? _DMIHash => ItemHelper.FindChild<UserInput>(this, "DMIHash");
private UserInput? _EDC => ItemHelper.FindChild<UserInput>(this, "EDC");
private UserInput? _ErrorsCount => ItemHelper.FindChild<UserInput>(this, "ErrorsCount");
private UserInput? _EXEDateBuildDate => ItemHelper.FindChild<UserInput>(this, "EXEDateBuildDate");
private UserInput? _Filename => ItemHelper.FindChild<UserInput>(this, "Filename");
private UserInput? _Header => ItemHelper.FindChild<UserInput>(this, "Header");
private UserInput? _InternalName => ItemHelper.FindChild<UserInput>(this, "InternalName");
private UserInput? _InternalSerialName => ItemHelper.FindChild<UserInput>(this, "InternalSerialName");
private UserInput? _Multisession => ItemHelper.FindChild<UserInput>(this, "Multisession");
private UserInput? _LibCrypt => ItemHelper.FindChild<UserInput>(this, "LibCrypt");
private UserInput? _LibCryptData => ItemHelper.FindChild<UserInput>(this, "LibCryptData");
private UserInput? _PFIHash => ItemHelper.FindChild<UserInput>(this, "PFIHash");
private UserInput? _PIC => ItemHelper.FindChild<UserInput>(this, "PIC");
private UserInput? _PVD => ItemHelper.FindChild<UserInput>(this, "PVD");
private UserInput? _RingNonZeroDataStart => ItemHelper.FindChild<UserInput>(this, "RingNonZeroDataStart");
private UserInput? _SecuROMData => ItemHelper.FindChild<UserInput>(this, "SecuROMData");
private UserInput? _SSHash => ItemHelper.FindChild<UserInput>(this, "SSHash");
private UserInput? _SecuritySectorRanges => ItemHelper.FindChild<UserInput>(this, "SecuritySectorRanges");
private UserInput? _SSVersion => ItemHelper.FindChild<UserInput>(this, "SSVersion");
private UserInput? _UniversalHash => ItemHelper.FindChild<UserInput>(this, "UniversalHash");
private UserInput? _VolumeLabel => ItemHelper.FindChild<UserInput>(this, "VolumeLabel");
private UserInput? _XeMID => ItemHelper.FindChild<UserInput>(this, "XeMID");
private UserInput? _XMID => ItemHelper.FindChild<UserInput>(this, "XMID");
#endregion
#region Accept / Cancel
private Button? _AcceptButton => ItemHelper.FindChild<Button>(this, "AcceptButton");
private Button? _CancelButton => ItemHelper.FindChild<Button>(this, "CancelButton");
private Button? _RingCodeGuideButton => ItemHelper.FindChild<Button>(this, "RingCodeGuideButton");
#endregion
#endif
/// <summary>
/// Read-only access to the current disc information view model
/// </summary>
@@ -22,7 +125,19 @@ namespace MPF.UI.Core.Windows
/// </summary>
public DiscInformationWindow(Options options, SubmissionInfo? submissionInfo)
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
DataContext = new DiscInformationViewModel(options, submissionInfo);
DiscInformationViewModel.Load();
@@ -34,9 +149,15 @@ namespace MPF.UI.Core.Windows
}
// Add handlers
#if NET35
_AcceptButton!.Click += OnAcceptClick;
_CancelButton!.Click += OnCancelClick;
_RingCodeGuideButton!.Click += OnRingCodeGuideClick;
#else
AcceptButton.Click += OnAcceptClick;
CancelButton.Click += OnCancelClick;
RingCodeGuideButton.Click += OnRingCodeGuideClick;
#endif
// Update UI with new values
ManipulateFields(options, submissionInfo);
@@ -70,9 +191,26 @@ namespace MPF.UI.Core.Windows
private void EnableTabsInInputFields()
{
// Additional Information
#if NET35
_CommentsTextBox!.Tab = true;
#else
CommentsTextBox.Tab = true;
#endif
// Contents
#if NET35
_GeneralContent!.Tab = true;
_GamesTextBox!.Tab = true;
_NetYarozeGamesTextBox!.Tab = true;
_PlayableDemosTextBox!.Tab = true;
_RollingDemosTextBox!.Tab = true;
_TechDemosTextBox!.Tab = true;
_GameFootageTextBox!.Tab = true;
_VideosTextBox!.Tab = true;
_PatchesTextBox!.Tab = true;
_SavegamesTextBox!.Tab = true;
_ExtrasTextBox!.Tab = true;
#else
GeneralContent.Tab = true;
GamesTextBox.Tab = true;
NetYarozeGamesTextBox.Tab = true;
@@ -84,30 +222,59 @@ namespace MPF.UI.Core.Windows
PatchesTextBox.Tab = true;
SavegamesTextBox.Tab = true;
ExtrasTextBox.Tab = true;
#endif
// L0
#if NET35
_L0MasteringRing!.Tab = true;
_L0MasteringSID!.Tab = true;
_L0Toolstamp!.Tab = true;
_L0MouldSID!.Tab = true;
_L0AdditionalMould!.Tab = true;
#else
L0MasteringRing.Tab = true;
L0MasteringSID.Tab = true;
L0Toolstamp.Tab = true;
L0MouldSID.Tab = true;
L0AdditionalMould.Tab = true;
#endif
// L1
#if NET35
_L1MasteringRing!.Tab = true;
_L1MasteringSID!.Tab = true;
_L1Toolstamp!.Tab = true;
_L1MouldSID!.Tab = true;
_L1AdditionalMould!.Tab = true;
#else
L1MasteringRing.Tab = true;
L1MasteringSID.Tab = true;
L1Toolstamp.Tab = true;
L1MouldSID.Tab = true;
L1AdditionalMould.Tab = true;
#endif
// L2
#if NET35
_L2MasteringRing!.Tab = true;
_L2MasteringSID!.Tab = true;
_L2Toolstamp!.Tab = true;
#else
L2MasteringRing.Tab = true;
L2MasteringSID.Tab = true;
L2Toolstamp.Tab = true;
#endif
// L3
#if NET35
_L3MasteringRing!.Tab = true;
_L3MasteringSID!.Tab = true;
_L3Toolstamp!.Tab = true;
#else
L3MasteringRing.Tab = true;
L3MasteringSID.Tab = true;
L3Toolstamp.Tab = true;
#endif
}
/// <summary>
@@ -121,12 +288,70 @@ namespace MPF.UI.Core.Windows
if (submissionInfo == null)
return;
#if NET35
if (submissionInfo.FullyMatchedID == null)
_FullyMatchedID!.Visibility = Visibility.Collapsed;
if (submissionInfo.PartiallyMatchedIDs == null)
_PartiallyMatchedIDs!.Visibility = Visibility.Collapsed;
else
_PartiallyMatchedIDs!.Text = string.Join(", ", submissionInfo.PartiallyMatchedIDs.Select(i => i.ToString()).ToArray());
if (submissionInfo.CopyProtection?.AntiModchip == null)
_AntiModchip!.Visibility = Visibility.Collapsed;
if (submissionInfo.TracksAndWriteOffsets?.OtherWriteOffsets == null)
_DiscOffset!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.Keys?.Contains(SiteCode.DMIHash) != true)
_DMIHash!.Visibility = Visibility.Collapsed;
if (submissionInfo.EDC?.EDC == null)
_EDC!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.ErrorsCount))
_ErrorsCount!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
_EXEDateBuildDate!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Filename) != true)
_Filename!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.Header))
_Header!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalName) != true)
_InternalName!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalSerialName) != true)
_InternalSerialName!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Multisession) != true)
_Multisession!.Visibility = Visibility.Collapsed;
if (submissionInfo.CopyProtection?.LibCrypt == null)
_LibCrypt!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.LibCryptData))
_LibCryptData!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.PFIHash) != true)
_PFIHash!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.PIC))
_PIC!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.PVD))
_PVD!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.RingNonZeroDataStart) != true)
_RingNonZeroDataStart!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.SecuROMData))
_SecuROMData!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSHash) != true)
_SSHash!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.SecuritySectorRanges))
_SecuritySectorRanges!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSVersion) != true)
_SSVersion!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) != true)
_UniversalHash!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.VolumeLabel) != true)
_VolumeLabel!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XeMID) != true)
_XeMID!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XMID) != true)
_XMID!.Visibility = Visibility.Collapsed;
#else
if (submissionInfo.FullyMatchedID == null)
FullyMatchedID.Visibility = Visibility.Collapsed;
if (submissionInfo.PartiallyMatchedIDs == null)
PartiallyMatchedIDs.Visibility = Visibility.Collapsed;
else
PartiallyMatchedIDs.Text = string.Join(", ", submissionInfo.PartiallyMatchedIDs);
PartiallyMatchedIDs.Text = string.Join(", ", submissionInfo.PartiallyMatchedIDs.Select(i => i.ToString()).ToArray());
if (submissionInfo.CopyProtection?.AntiModchip == null)
AntiModchip.Visibility = Visibility.Collapsed;
if (submissionInfo.TracksAndWriteOffsets?.OtherWriteOffsets == null)
@@ -135,13 +360,13 @@ namespace MPF.UI.Core.Windows
DMIHash.Visibility = Visibility.Collapsed;
if (submissionInfo.EDC?.EDC == null)
EDC.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo.CommonDiscInfo?.ErrorsCount))
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.ErrorsCount))
ErrorsCount.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
EXEDateBuildDate.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Filename) != true)
Filename.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.Header))
if (string.IsNullOrEmpty(submissionInfo.Extras?.Header))
Header.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalName) != true)
InternalName.Visibility = Visibility.Collapsed;
@@ -151,21 +376,21 @@ namespace MPF.UI.Core.Windows
Multisession.Visibility = Visibility.Collapsed;
if (submissionInfo.CopyProtection?.LibCrypt == null)
LibCrypt.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo.CopyProtection?.LibCryptData))
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.LibCryptData))
LibCryptData.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.PFIHash) != true)
PFIHash.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.PIC))
if (string.IsNullOrEmpty(submissionInfo.Extras?.PIC))
PIC.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.PVD))
if (string.IsNullOrEmpty(submissionInfo.Extras?.PVD))
PVD.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.RingNonZeroDataStart) != true)
RingNonZeroDataStart.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo.CopyProtection?.SecuROMData))
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.SecuROMData))
SecuROMData.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSHash) != true)
SSHash.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.SecuritySectorRanges))
if (string.IsNullOrEmpty(submissionInfo.Extras?.SecuritySectorRanges))
SecuritySectorRanges.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSVersion) != true)
SSVersion.Visibility = Visibility.Collapsed;
@@ -177,6 +402,7 @@ namespace MPF.UI.Core.Windows
XeMID.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XMID) != true)
XMID.Visibility = Visibility.Collapsed;
#endif
}
/// <summary>
@@ -193,19 +419,37 @@ namespace MPF.UI.Core.Windows
{
case DiscType.CD:
case DiscType.GDROM:
#if NET35
_L0Info!.Header = "Data Side";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Mould SID";
_L0AdditionalMould!.Label = "Additional Mould";
#else
L0Info.Header = "Data Side";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Mould SID";
L0AdditionalMould.Label = "Additional Mould";
#endif
#if NET35
_L1Info!.Header = "Label Side";
_L1MasteringRing!.Visibility = Visibility.Collapsed;
_L1MasteringSID!.Visibility = Visibility.Collapsed;
_L1Toolstamp!.Visibility = Visibility.Collapsed;
_L1MouldSID!.Label = "Mould SID";
_L1AdditionalMould!.Label = "Additional Mould";
#else
L1Info.Header = "Label Side";
L1MasteringRing.Visibility = Visibility.Collapsed;
L1MasteringSID.Visibility = Visibility.Collapsed;
L1Toolstamp.Visibility = Visibility.Collapsed;
L1MouldSID.Label = "Mould SID";
L1AdditionalMould.Label = "Additional Mould";
#endif
break;
case DiscType.DVD5:
@@ -225,103 +469,212 @@ namespace MPF.UI.Core.Windows
// Quad-layer discs
if (submissionInfo?.SizeAndChecksums?.Layerbreak3 != default(long))
{
#if NET35
_L2Info!.Visibility = Visibility.Visible;
_L3Info!.Visibility = Visibility.Visible;
#else
L2Info.Visibility = Visibility.Visible;
L3Info.Visibility = Visibility.Visible;
#endif
#if NET35
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Data Side Mould SID";
_L0AdditionalMould!.Label = "Data Side Additional Mould";
#else
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
#endif
#if NET35
_L1Info!.Header = "Layer 1";
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
_L1MouldSID!.Label = "Label Side Mould SID";
_L1AdditionalMould!.Label = "Label Side Additional Mould";
#else
L1Info.Header = "Layer 1";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
#endif
#if NET35
_L2Info!.Header = "Layer 2";
_L2MasteringRing!.Label = "Mastering Ring";
_L2MasteringSID!.Label = "Mastering SID";
_L2Toolstamp!.Label = "Toolstamp/Mastering Code";
#else
L2Info.Header = "Layer 2";
L2MasteringRing.Label = "Mastering Ring";
L2MasteringSID.Label = "Mastering SID";
L2Toolstamp.Label = "Toolstamp/Mastering Code";
#endif
#if NET35
_L3Info!.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
_L3MasteringRing!.Label = "Mastering Ring";
_L3MasteringSID!.Label = "Mastering SID";
_L3Toolstamp!.Label = "Toolstamp/Mastering Code";
#else
L3Info.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
L3MasteringRing.Label = "Mastering Ring";
L3MasteringSID.Label = "Mastering SID";
L3Toolstamp.Label = "Toolstamp/Mastering Code";
#endif
}
// Triple-layer discs
else if (submissionInfo?.SizeAndChecksums?.Layerbreak2 != default(long))
{
#if NET35
_L2Info!.Visibility = Visibility.Visible;
#else
L2Info.Visibility = Visibility.Visible;
#endif
#if NET35
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Data Side Mould SID";
_L0AdditionalMould!.Label = "Data Side Additional Mould";
#else
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
#endif
#if NET35
_L1Info!.Header = "Layer 1";
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
_L1MouldSID!.Label = "Label Side Mould SID";
_L1AdditionalMould!.Label = "Label Side Additional Mould";
#else
L1Info.Header = "Layer 1";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
#endif
#if NET35
_L2Info!.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
_L2MasteringRing!.Label = "Mastering Ring";
_L2MasteringSID!.Label = "Mastering SID";
_L2Toolstamp!.Label = "Toolstamp/Mastering Code";
#else
L2Info.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
L2MasteringRing.Label = "Mastering Ring";
L2MasteringSID.Label = "Mastering SID";
L2Toolstamp.Label = "Toolstamp/Mastering Code";
#endif
}
// Double-layer discs
else if (submissionInfo?.SizeAndChecksums?.Layerbreak != default(long))
{
#if NET35
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Data Side Mould SID";
_L0AdditionalMould!.Label = "Data Side Additional Mould";
#else
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
#endif
#if NET35
_L1Info!.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
_L1MouldSID!.Label = "Label Side Mould SID";
_L1AdditionalMould!.Label = "Label Side Additional Mould";
#else
L1Info.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
#endif
}
// Single-layer discs
else
{
#if NET35
_L0Info!.Header = "Data Side";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Mould SID";
_L0AdditionalMould!.Label = "Additional Mould";
#else
L0Info.Header = "Data Side";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Mould SID";
L0AdditionalMould.Label = "Additional Mould";
#endif
#if NET35
_L1Info!.Header = "Label Side";
_L1MasteringRing!.Visibility = Visibility.Collapsed;
_L1MasteringSID!.Visibility = Visibility.Collapsed;
_L1Toolstamp!.Visibility = Visibility.Collapsed;
_L1MouldSID!.Label = "Mould SID";
_L1AdditionalMould!.Label = "Additional Mould";
#else
L1Info.Header = "Label Side";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MasteringRing.Visibility = Visibility.Collapsed;
L1MasteringSID.Visibility = Visibility.Collapsed;
L1Toolstamp.Visibility = Visibility.Collapsed;
L1MouldSID.Label = "Mould SID";
L1AdditionalMould.Label = "Additional Mould";
#endif
}
break;
// All other media we assume to have no rings
default:
#if NET35
_L0Info!.Visibility = Visibility.Collapsed;
_L1Info!.Visibility = Visibility.Collapsed;
_L2Info!.Visibility = Visibility.Collapsed;
_L3Info!.Visibility = Visibility.Collapsed;
#else
L0Info.Visibility = Visibility.Collapsed;
L1Info.Visibility = Visibility.Collapsed;
L2Info.Visibility = Visibility.Collapsed;
L3Info.Visibility = Visibility.Collapsed;
#endif
break;
}
}
@@ -335,13 +688,35 @@ namespace MPF.UI.Core.Windows
var system = submissionInfo?.CommonDiscInfo?.System;
switch (system)
{
case RedumpSystem.NintendoWiiU:
#if NET35
_DiscKeyTextBox!.Visibility = Visibility.Visible;
#else
DiscKeyTextBox.Visibility = Visibility.Visible;
#endif
break;
case RedumpSystem.SonyPlayStation2:
#if NET35
_LanguageSelectionGrid!.Visibility = Visibility.Visible;
#else
LanguageSelectionGrid.Visibility = Visibility.Visible;
#endif
break;
case RedumpSystem.SonyPlayStation3:
#if NET35
_DiscKeyTextBox!.Visibility = Visibility.Visible;
_DiscIDTextBox!.Visibility = Visibility.Visible;
#else
DiscKeyTextBox.Visibility = Visibility.Visible;
DiscIDTextBox.Visibility = Visibility.Visible;
#endif
break;
}
}
#endregion
#endregion
#region Event Handlers

View File

@@ -18,9 +18,6 @@
<Window.Resources>
<core:ElementConverter x:Key="ElementConverter" />
</Window.Resources>
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical">
@@ -79,7 +76,7 @@
</StackPanel>
<Label Panel.ZIndex="0" Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<TextBlock TextAlignment="Center"><Bold>Media Preservation Frontend</Bold></TextBlock>
<TextBlock TextAlignment="Center"><Run FontWeight="Bold" Text="Media Preservation Frontend" /></TextBlock>
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"

View File

@@ -2,12 +2,15 @@ using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using MPF.Core;
using MPF.Core.UI.ViewModels;
using MPF.UI.Core.UserControls;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using WPFCustomMessageBox;
using WinForms = System.Windows.Forms;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.Windows
{
public partial class MainWindow : WindowBase
@@ -17,10 +20,66 @@ namespace MPF.UI.Core.Windows
/// </summary>
public MainViewModel MainViewModel => DataContext as MainViewModel ?? new MainViewModel();
#if NET35
#region Top Menu Bar
private MenuItem? _AboutMenuItem => ItemHelper.FindChild<MenuItem>(this, "AboutMenuItem");
private MenuItem? _AppExitMenuItem => ItemHelper.FindChild<MenuItem>(this, "AppExitMenuItem");
private MenuItem? _CheckForUpdatesMenuItem => ItemHelper.FindChild<MenuItem>(this, "CheckForUpdatesMenuItem");
private MenuItem? _DebugViewMenuItem => ItemHelper.FindChild<MenuItem>(this, "DebugViewMenuItem");
private MenuItem? _OptionsMenuItem => ItemHelper.FindChild<MenuItem>(this, "OptionsMenuItem");
#endregion
#region Settings
private ComboBox? _DriveLetterComboBox => ItemHelper.FindChild<ComboBox>(this, "DriveLetterComboBox");
private ComboBox? _DriveSpeedComboBox => ItemHelper.FindChild<ComboBox>(this, "DriveSpeedComboBox");
private ComboBox? _DumpingProgramComboBox => ItemHelper.FindChild<ComboBox>(this, "DumpingProgramComboBox");
private CheckBox? _EnableParametersCheckBox => ItemHelper.FindChild<CheckBox>(this, "EnableParametersCheckBox");
private ComboBox? _MediaTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "MediaTypeComboBox");
private Button? _OutputPathBrowseButton => ItemHelper.FindChild<Button>(this, "OutputPathBrowseButton");
private TextBox? _OutputPathTextBox => ItemHelper.FindChild<TextBox>(this, "OutputPathTextBox");
private ComboBox? _SystemTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "SystemTypeComboBox");
#endregion
#region Controls
private Button? _CopyProtectScanButton => ItemHelper.FindChild<Button>(this, "CopyProtectScanButton");
private Button? _MediaScanButton => ItemHelper.FindChild<Button>(this, "MediaScanButton");
private Button? _StartStopButton => ItemHelper.FindChild<Button>(this, "StartStopButton");
private Button? _UpdateVolumeLabel => ItemHelper.FindChild<Button>(this, "UpdateVolumeLabel");
#endregion
#region Status
private LogOutput? _LogOutput => ItemHelper.FindChild<LogOutput>(this, "LogOutput");
#endregion
#endif
/// <summary>
/// Constructor
/// </summary>
public MainWindow() => InitializeComponent();
public MainWindow()
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
}
/// <summary>
/// Handler for MainWindow OnContentRendered event
@@ -40,9 +99,17 @@ namespace MPF.UI.Core.Windows
// Display the debug option in the menu, if necessary
if (MainViewModel.Options.ShowDebugViewMenuItem)
#if NET35
_DebugViewMenuItem!.Visibility = Visibility.Visible;
#else
DebugViewMenuItem.Visibility = Visibility.Visible;
#endif
#if NET35
MainViewModel.Init(_LogOutput!.EnqueueLog, DisplayUserMessage, ShowDiscInformationWindow);
#else
MainViewModel.Init(LogOutput.EnqueueLog, DisplayUserMessage, ShowDiscInformationWindow);
#endif
// Set the UI color scheme according to the options
ApplyTheme();
@@ -67,29 +134,58 @@ namespace MPF.UI.Core.Windows
public void AddEventHandlers()
{
// Menu Bar Click
#if NET35
_AboutMenuItem!.Click += AboutClick;
_AppExitMenuItem!.Click += AppExitClick;
_CheckForUpdatesMenuItem!.Click += CheckForUpdatesClick;
_DebugViewMenuItem!.Click += DebugViewClick;
_OptionsMenuItem!.Click += OptionsMenuItemClick;
#else
AboutMenuItem.Click += AboutClick;
AppExitMenuItem.Click += AppExitClick;
CheckForUpdatesMenuItem.Click += CheckForUpdatesClick;
DebugViewMenuItem.Click += DebugViewClick;
OptionsMenuItem.Click += OptionsMenuItemClick;
#endif
// User Area Click
#if NET35
_CopyProtectScanButton!.Click += CopyProtectScanButtonClick;
_EnableParametersCheckBox!.Click += EnableParametersCheckBoxClick;
_MediaScanButton!.Click += MediaScanButtonClick;
_UpdateVolumeLabel!.Click += UpdateVolumeLabelClick;
_OutputPathBrowseButton!.Click += OutputPathBrowseButtonClick;
_StartStopButton!.Click += StartStopButtonClick;
#else
CopyProtectScanButton.Click += CopyProtectScanButtonClick;
EnableParametersCheckBox.Click += EnableParametersCheckBoxClick;
MediaScanButton.Click += MediaScanButtonClick;
UpdateVolumeLabel.Click += UpdateVolumeLabelClick;
OutputPathBrowseButton.Click += OutputPathBrowseButtonClick;
StartStopButton.Click += StartStopButtonClick;
#endif
// User Area SelectionChanged
#if NET35
_SystemTypeComboBox!.SelectionChanged += SystemTypeComboBoxSelectionChanged;
_MediaTypeComboBox!.SelectionChanged += MediaTypeComboBoxSelectionChanged;
_DriveLetterComboBox!.SelectionChanged += DriveLetterComboBoxSelectionChanged;
_DriveSpeedComboBox!.SelectionChanged += DriveSpeedComboBoxSelectionChanged;
_DumpingProgramComboBox!.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
#else
SystemTypeComboBox.SelectionChanged += SystemTypeComboBoxSelectionChanged;
MediaTypeComboBox.SelectionChanged += MediaTypeComboBoxSelectionChanged;
DriveLetterComboBox.SelectionChanged += DriveLetterComboBoxSelectionChanged;
DriveSpeedComboBox.SelectionChanged += DriveSpeedComboBoxSelectionChanged;
DumpingProgramComboBox.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
#endif
// User Area TextChanged
#if NET35
_OutputPathTextBox!.TextChanged += OutputPathTextBoxTextChanged;
#else
OutputPathTextBox.TextChanged += OutputPathTextBoxTextChanged;
#endif
}
/// <summary>
@@ -99,12 +195,12 @@ namespace MPF.UI.Core.Windows
{
// Get the current path, if possible
string currentPath = MainViewModel.OutputPath;
if (string.IsNullOrWhiteSpace(currentPath) && !string.IsNullOrWhiteSpace(MainViewModel.Options.DefaultOutputPath))
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(MainViewModel.Options.DefaultOutputPath))
currentPath = Path.Combine(MainViewModel.Options.DefaultOutputPath, "track.bin");
else if (string.IsNullOrWhiteSpace(currentPath))
else if (string.IsNullOrEmpty(currentPath))
currentPath = "track.bin";
if (string.IsNullOrWhiteSpace(currentPath))
currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "track.bin");
if (string.IsNullOrEmpty(currentPath))
currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory!, "track.bin");
// Get the full path
currentPath = Path.GetFullPath(currentPath);
@@ -137,7 +233,7 @@ namespace MPF.UI.Core.Windows
(bool different, string message, var url) = MainViewModel.CheckForUpdates();
// If we have a new version, put it in the clipboard
if (different)
if (different && !string.IsNullOrEmpty(url))
Clipboard.SetText(url);
if (showIfSame || different)
@@ -185,7 +281,7 @@ namespace MPF.UI.Core.Windows
{
var submissionInfo = MainViewModel.CreateDebugSubmissionInfo();
var result = ShowDiscInformationWindow(submissionInfo);
InfoTool.ProcessSpecialFields(result.Item2);
Formatter.ProcessSpecialFields(result.Item2);
}
/// <summary>
@@ -251,7 +347,7 @@ namespace MPF.UI.Core.Windows
theme.Apply();
}
#endregion
#endregion
#region Event Handlers
@@ -315,9 +411,17 @@ namespace MPF.UI.Core.Windows
/// <summary>
/// Handler for CopyProtectScanButton Click event
/// </summary>
#if NET40
public void CopyProtectScanButtonClick(object sender, RoutedEventArgs e)
#else
public async void CopyProtectScanButtonClick(object sender, RoutedEventArgs e)
#endif
{
#if NET40
var (output, error) = MainViewModel.ScanAndShowProtection();
#else
var (output, error) = await MainViewModel.ScanAndShowProtection();
#endif
if (!MainViewModel.LogPanelExpanded)
{

View File

@@ -5,17 +5,18 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:core="clr-namespace:MPF.UI.Core"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
mc:Ignorable="d"
Width="515.132" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<Window.DataContext>
<viewModels:OptionsViewModel/>
</Window.DataContext>
<Window.Resources>
<core:ElementConverter x:Key="ElementConverter" />
</Window.Resources>
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical">
@@ -30,14 +31,8 @@
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<TextBlock>
<TextBlock.Inlines>
<Run FontWeight="Bold" Text="{Binding Title, Mode=OneWay}"/>
</TextBlock.Inlines>
</TextBlock>
</Label.Content>
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown"
Content="{Binding Path=Title, Mode=OneWay}" FontWeight="Bold">
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
@@ -311,28 +306,28 @@
<Slider x:Name="DumpSpeedCDSlider" Grid.Row="0" Grid.Column="1" Minimum="1" Maximum="72" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static core:Constants.SpeedsForCDAsCollection}}"
Value="{Binding Options.PreferredDumpSpeedCD}" />
<TextBox x:Name="DumpSpeedCDTextBox" Grid.Row="0" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
<TextBox x:Name="DumpSpeedCDTextBox" Grid.Row="0" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedCDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
<Label Grid.Row="1" Grid.Column="0" Content="DVD" />
<Slider x:Name="DumpSpeedDVDSlider" Grid.Row="1" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static core:Constants.SpeedsForDVDAsCollection}}"
Value="{Binding Options.PreferredDumpSpeedDVD}" />
<TextBox x:Name="DumpSpeedDVDTextBox" Grid.Row="1" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
<TextBox x:Name="DumpSpeedDVDTextBox" Grid.Row="1" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
<Label Grid.Row="2" Grid.Column="0" Content="HD-DVD" />
<Slider x:Name="DumpSpeedHDDVDSlider" Grid.Row="2" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static core:Constants.SpeedsForHDDVDAsCollection}}"
Value="{Binding Options.PreferredDumpSpeedHDDVD}" />
<TextBox x:Name="DumpSpeedHDDVDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
<TextBox x:Name="DumpSpeedHDDVDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedHDDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
<Label Grid.Row="3" Grid.Column="0" Content="BD" />
<Slider x:Name="DumpSpeedBDSlider" Grid.Row="3" Grid.Column="1" Minimum="1" Maximum="16" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static core:Constants.SpeedsForBDAsCollection}}"
Value="{Binding Options.PreferredDumpSpeedBD}" />
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="3" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="3" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedBDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
</Grid>
</GroupBox>
@@ -469,7 +464,10 @@
<Label>
<Label.Content>
<TextBlock TextWrapping="Wrap"><Bold Foreground="Red">WARNING:</Bold> If you choose to enable validation and information retrieval, you are responsible for ensuring that all data populated matches your actual media. Some information may be marked to check for validity as a reminder, but all information should be subject to the same scrutiny.</TextBlock>
<TextBlock TextWrapping="Wrap">
<Run FontWeight="Bold" Foreground="Red" Text="WARNING:" />
<Run Text="If you choose to enable validation and information retrieval, you are responsible for ensuring that all data populated matches your actual media. Some information may be marked to check for validity as a reminder, but all information should be subject to the same scrutiny." />
</TextBlock>
</Label.Content>
</Label>
</StackPanel>

View File

@@ -2,11 +2,14 @@
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using MPF.Core.Data;
using MPF.Core.UI.ViewModels;
using WPFCustomMessageBox;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.Windows
{
/// <summary>
@@ -19,18 +22,65 @@ namespace MPF.UI.Core.Windows
/// </summary>
public OptionsViewModel OptionsViewModel => DataContext as OptionsViewModel ?? new OptionsViewModel(new Options());
#if NET35
private System.Windows.Controls.Button? _AaruPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "AaruPathButton");
private System.Windows.Controls.Button? _AcceptButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "AcceptButton");
private System.Windows.Controls.Button? _CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
private System.Windows.Controls.Button? _DefaultOutputPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "DefaultOutputPathButton");
private System.Windows.Controls.Button? _DiscImageCreatorPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "DiscImageCreatorPathButton");
private System.Windows.Controls.Button? _RedumperPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "RedumperPathButton");
private System.Windows.Controls.Button? _RedumpLoginTestButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "RedumpLoginTestButton");
private PasswordBox? _RedumpPasswordBox => ItemHelper.FindChild<PasswordBox>(this, "RedumpPasswordBox");
private System.Windows.Controls.TextBox? _RedumpUsernameTextBox => ItemHelper.FindChild<System.Windows.Controls.TextBox>(this, "RedumpUsernameTextBox");
#endif
/// <summary>
/// Constructor
/// </summary>
public OptionsWindow(Options options)
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
#if NET40_OR_GREATER || NETCOREAPP
DumpSpeedCDTextBox.IsReadOnlyCaretVisible = false;
DumpSpeedDVDTextBox.IsReadOnlyCaretVisible = false;
DumpSpeedHDDVDTextBox.IsReadOnlyCaretVisible = false;
DumpSpeedBDTextBox.IsReadOnlyCaretVisible = false;
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
DataContext = new OptionsViewModel(options);
// Set initial value for binding
#if NET35
_RedumpPasswordBox!.Password = options.RedumpPassword;
#else
RedumpPasswordBox.Password = options.RedumpPassword;
#endif
// Add handlers
#if NET35
_AaruPathButton!.Click += BrowseForPathClick;
_DiscImageCreatorPathButton!.Click += BrowseForPathClick;
_RedumperPathButton!.Click += BrowseForPathClick;
_DefaultOutputPathButton!.Click += BrowseForPathClick;
_AcceptButton!.Click += OnAcceptClick;
_CancelButton!.Click += OnCancelClick;
_RedumpPasswordBox!.PasswordChanged += OnPasswordChanged;
_RedumpLoginTestButton!.Click += OnRedumpTestClick;
#else
AaruPathButton.Click += BrowseForPathClick;
DiscImageCreatorPathButton.Click += BrowseForPathClick;
RedumperPathButton.Click += BrowseForPathClick;
@@ -40,6 +90,7 @@ namespace MPF.UI.Core.Windows
CancelButton.Click += OnCancelClick;
RedumpPasswordBox.PasswordChanged += OnPasswordChanged;
RedumpLoginTestButton.Click += OnRedumpTestClick;
#endif
}
/// <summary>
@@ -65,7 +116,7 @@ namespace MPF.UI.Core.Windows
return;
// Strips button prefix to obtain the setting name
string pathSettingName = button.Name[..button.Name.IndexOf("Button")];
string pathSettingName = button.Name.Substring(0, button.Name.IndexOf("Button"));
// TODO: hack for now, then we'll see
bool shouldBrowseForPath = pathSettingName == "DefaultOutputPath";
@@ -149,7 +200,11 @@ namespace MPF.UI.Core.Windows
/// </summary>
private async Task ValidateRedumpCredentials()
{
(bool? success, string? message) = await MPF.Core.UI.ViewModels.OptionsViewModel.TestRedumpLogin(RedumpUsernameTextBox.Text, RedumpPasswordBox.Password);
#if NET35
(bool? success, string? message) = await OptionsViewModel.TestRedumpLogin(_RedumpUsernameTextBox!.Text, _RedumpPasswordBox!.Password);
#else
(bool? success, string? message) = await OptionsViewModel.TestRedumpLogin(RedumpUsernameTextBox.Text, RedumpPasswordBox.Password);
#endif
if (success == true)
CustomMessageBox.Show(this, message, "Success", MessageBoxButton.OK, MessageBoxImage.Information);
@@ -192,14 +247,26 @@ namespace MPF.UI.Core.Windows
/// </summary>
private void OnPasswordChanged(object sender, EventArgs e)
{
#if NET35
OptionsViewModel.Options.RedumpPassword = _RedumpPasswordBox!.Password;
#else
OptionsViewModel.Options.RedumpPassword = RedumpPasswordBox.Password;
#endif
}
/// <summary>
/// Test Redump credentials for validity
/// </summary>
#if NET40
private void OnRedumpTestClick(object sender, EventArgs e)
{
var validateTask = ValidateRedumpCredentials();
validateTask.Wait();
}
#else
private async void OnRedumpTestClick(object sender, EventArgs e) => await ValidateRedumpCredentials();
#endif
#endregion
#endregion
}
}

View File

@@ -10,10 +10,6 @@
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,10,0,0">
@@ -29,7 +25,7 @@
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<TextBlock TextAlignment="Center"><Bold>Ring Code Guide</Bold></TextBlock>
<Run FontWeight="Bold" BaselineAlignment="Center" Text="Ring Code Guide" />
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
@@ -85,22 +81,34 @@
RenderOptions.BitmapScalingMode="HighQuality" />
<Label Grid.Row="1">
<Label.Content>
<TextBlock><Bold Foreground="Red">1. Mastering Ring:</Bold> Sony DADC&lt;tab&gt;A0100368905-0101&lt;tab&gt;13</TextBlock>
<TextBlock>
<Run FontWeight="Bold" Foreground="Red" Text="1. Mastering Ring:" />
<Run Text=" Sony DADC&lt;tab&gt;A0100368905-0101&lt;tab&gt;13" />
</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="2">
<Label.Content>
<TextBlock><Bold Foreground="Green">2. Mastering SID:</Bold> IFPI L553</TextBlock>
<TextBlock>
<Run FontWeight="Bold" Foreground="Green" Text="2. Mastering SID:" />
<Run Text=" IFPI L553" />
</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="3">
<Label.Content>
<TextBlock><Bold Foreground="Purple">3. Toolstamp/Mastering Code:</Bold> A2</TextBlock>
<TextBlock>
<Run FontWeight="Bold" Foreground="Purple" Text="3. Toolstamp/Mastering Code:" />
<Run Text=" A2" />
</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="4">
<Label.Content>
<TextBlock><Bold Foreground="LightBlue">4. Mould SID:</Bold> IFPI 94V1</TextBlock>
<TextBlock>
<Run FontWeight="Bold" Foreground="LightBlue" Text="4. Mould SID:" />
<Run Text=" IFPI 94V1" />
</TextBlock>
</Label.Content>
</Label>
</Grid>
@@ -126,27 +134,42 @@
RenderOptions.BitmapScalingMode="HighQuality" />
<Label Grid.Row="1">
<Label.Content>
<TextBlock><Bold Foreground="Red">1. Outer Mastering Ring:</Bold> IM01501A-L1&lt;tab&gt;03 +&lt;tab&gt;+</TextBlock>
<TextBlock>
<Run FontWeight="Bold" Foreground="Red" Text="1. Outer Mastering Ring:" />
<Run Text=" IM01501A-L1&lt;tab&gt;03 +&lt;tab&gt;+" />
</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="2">
<Label.Content>
<TextBlock><Bold Foreground="Green">2. Inner Mastering Ring:</Bold> IM01501A-L0&lt;tab&gt;01 +&lt;tab&gt;+</TextBlock>
<TextBlock>
<Run FontWeight="Bold" Foreground="Green" Text="2. Inner Mastering Ring:" />
<Run Text=" IM01501A-L0&lt;tab&gt;01 +&lt;tab&gt;+" />
</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="3">
<Label.Content>
<TextBlock><Bold Foreground="Purple">3. Outer Mastering SID:</Bold> IFPI LB48</TextBlock>
<TextBlock>
<Run FontWeight="Bold" Foreground="Purple" Text="3. Outer Mastering SID:" />
<Run Text=" IFPI LB48" />
</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="4">
<Label.Content>
<TextBlock><Bold Foreground="LightBlue">4. Inner Mastering SID:</Bold> IFPI LB48</TextBlock>
<TextBlock>
<Run FontWeight="Bold" Foreground="LightBlue" Text="4. Inner Mastering SID:" />
<Run Text=" IFPI LB48" />
</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="5">
<Label.Content>
<TextBlock><Bold>Note:</Bold> See the 1-Layer guide for more details on additional fields</TextBlock>
<TextBlock>
<Run FontWeight="Bold" Text="Note:" />
<Run Text=" See the 1-Layer guide for more details on additional fields" />
</TextBlock>
</Label.Content>
</Label>
</Grid>

View File

@@ -5,6 +5,20 @@
/// </summary>
public partial class RingCodeGuideWindow : WindowBase
{
public RingCodeGuideWindow() => InitializeComponent();
public RingCodeGuideWindow()
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new System.Windows.Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
}
}
}

View File

@@ -17,7 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\ISSUE_TEMPLATE\informational.md = .github\ISSUE_TEMPLATE\informational.md
.github\ISSUE_TEMPLATE\issue-report.md = .github\ISSUE_TEMPLATE\issue-report.md
publish-nix.sh = publish-nix.sh
publish-win.bat = publish-win.bat
publish-win.ps1 = publish-win.ps1
README.md = README.md
EndProjectSection
EndProject

View File

@@ -1,7 +1,6 @@
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
xmlns:windows="clr-namespace:MPF.UI.Core.Windows;assembly=MPF.UI.Core"
x:Class="MPF.App">
<Application.MainWindow>
@@ -205,140 +204,7 @@
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
<ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{DynamicResource ComboBoxToggleButton}"/>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="shadow" Value="#71000000"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<SolidColorBrush x:Key="TextBox.Static.Background" Color="#FFFFFFFF"/>
<Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="MinWidth" Value="0"/>
<Setter Property="MinHeight" Value="0"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer x:Name="PART_ContentHost" Background="Transparent" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="ComboBoxEditableTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
<ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{DynamicResource ComboBoxToggleButton}"/>
<Border x:Name="border" Background="{DynamicResource TextBox.Static.Background}" Margin="{TemplateBinding BorderThickness}">
<TextBox x:Name="PART_EditableTextBox" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" Margin="{TemplateBinding Padding}" Style="{DynamicResource ComboBoxEditableTextBox}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="shadow" Value="#71000000"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="CustomComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource FocusVisual}"/>
<Setter Property="Background" Value="{DynamicResource ComboBox.Static.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ComboBox.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Padding" Value="6,3,5,3"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template" Value="{DynamicResource ComboBoxTemplate}"/>
<Style.Triggers>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Template" Value="{DynamicResource ComboBoxEditableTemplate}"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- ContextMenu -->
<SolidColorBrush x:Key="ContextMenu.Static.Border" Color="#FF888888"/>
@@ -384,7 +250,8 @@
<Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom">
<Border x:Name="SubMenuBorder" BorderBrush="{DynamicResource MenuItem.SubMenu.Border}" BorderThickness="1" Background="{DynamicResource MenuItem.SubMenu.Background}" Padding="2">
<ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<!--<Grid RenderOptions.ClearTypeHint="Enabled">--> <!-- Not supported in .NET Framework 3.5 -->
<Grid> <!-- Not supported in .NET Framework 3.5 -->
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
</Canvas>
@@ -426,66 +293,6 @@
<SolidColorBrush x:Key="ProgressBar.Progress" Color="#FF06B025"/>
<SolidColorBrush x:Key="ProgressBar.Background" Color="#FFE6E6E6"/>
<SolidColorBrush x:Key="ProgressBar.Border" Color="#FFBCBCBC"/>
<Style x:Key="CustomProgressBarStyle" TargetType="{x:Type ProgressBar}">
<Setter Property="Foreground" Value="{DynamicResource ProgressBar.Progress}"/>
<Setter Property="Background" Value="{DynamicResource ProgressBar.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ProgressBar.Border}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid x:Name="TemplateRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Determinate"/>
<VisualState x:Name="Indeterminate">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="Animation">
<EasingDoubleKeyFrame KeyTime="0" Value="0.25"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.25"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="0.25"/>
</DoubleAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="Animation">
<EasingPointKeyFrame KeyTime="0" Value="-0.5,0.5"/>
<EasingPointKeyFrame KeyTime="0:0:1" Value="0.5,0.5"/>
<EasingPointKeyFrame KeyTime="0:0:2" Value="1.5,0.5"/>
</PointAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"/>
<Rectangle x:Name="PART_Track"/>
<Grid x:Name="PART_Indicator" ClipToBounds="true" HorizontalAlignment="Left">
<Rectangle x:Name="Indicator" Fill="{TemplateBinding Foreground}"/>
<Rectangle x:Name="Animation" Fill="{TemplateBinding Foreground}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="LayoutTransform" TargetName="TemplateRoot">
<Setter.Value>
<RotateTransform Angle="-90"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsIndeterminate" Value="true">
<Setter Property="Visibility" TargetName="Indicator" Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ScrollViewer -->
<SolidColorBrush x:Key="ScrollViewer.ScrollBar.Background" Color="LightGray"/>

View File

@@ -1,7 +1,496 @@
namespace MPF
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
namespace MPF
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : System.Windows.Application { }
public partial class App : Application
{
#if NET35_OR_GREATER || NETCOREAPP
#region ControlTemplates
/// <summary>
/// ComboBoxTemplate ControlTemplate XAML (.NET Framework 4.0 and above)
/// </summary>
private const string _comboBoxTemplateDefault = @"<ControlTemplate TargetType=""{x:Type ComboBox}"">
<Grid x:Name=""templateRoot"" SnapsToDevicePixels=""true"">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=""*""/>
<ColumnDefinition MinWidth=""{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"" Width=""0""/>
</Grid.ColumnDefinitions>
<Popup x:Name=""PART_Popup"" AllowsTransparency=""true"" Grid.ColumnSpan=""2"" IsOpen=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Margin=""1"" PopupAnimation=""{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"" Placement=""Bottom"">
<themes:SystemDropShadowChrome x:Name=""shadow"" Color=""Transparent"" MaxHeight=""{TemplateBinding MaxDropDownHeight}"" MinWidth=""{Binding ActualWidth, ElementName=templateRoot}"">
<Border x:Name=""dropDownBorder"" BorderBrush=""{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"" BorderThickness=""1"" Background=""{DynamicResource {x:Static SystemColors.WindowBrushKey}}"">
<ScrollViewer x:Name=""DropDownScrollViewer"">
<Grid x:Name=""grid"" RenderOptions.ClearTypeHint=""Enabled"">
<Canvas x:Name=""canvas"" HorizontalAlignment=""Left"" Height=""0"" VerticalAlignment=""Top"" Width=""0"">
<Rectangle x:Name=""opaqueRect"" Fill=""{Binding Background, ElementName=dropDownBorder}"" Height=""{Binding ActualHeight, ElementName=dropDownBorder}"" Width=""{Binding ActualWidth, ElementName=dropDownBorder}""/>
</Canvas>
<ItemsPresenter x:Name=""ItemsPresenter"" KeyboardNavigation.DirectionalNavigation=""Contained"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}""/>
</Grid>
</ScrollViewer>
</Border>
</themes:SystemDropShadowChrome>
</Popup>
<ToggleButton x:Name=""toggleButton"" BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}"" Grid.ColumnSpan=""2"" IsChecked=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Style=""{DynamicResource ComboBoxToggleButton}""/>
<ContentPresenter x:Name=""contentPresenter"" ContentTemplate=""{TemplateBinding SelectionBoxItemTemplate}"" ContentTemplateSelector=""{TemplateBinding ItemTemplateSelector}"" Content=""{TemplateBinding SelectionBoxItem}"" ContentStringFormat=""{TemplateBinding SelectionBoxItemStringFormat}"" HorizontalAlignment=""{TemplateBinding HorizontalContentAlignment}"" IsHitTestVisible=""false"" Margin=""{TemplateBinding Padding}"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}"" VerticalAlignment=""{TemplateBinding VerticalContentAlignment}""/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property=""HasDropShadow"" SourceName=""PART_Popup"" Value=""true"">
<Setter Property=""Margin"" TargetName=""shadow"" Value=""0,0,5,5""/>
<Setter Property=""Color"" TargetName=""shadow"" Value=""#71000000""/>
</Trigger>
<Trigger Property=""HasItems"" Value=""false"">
<Setter Property=""Height"" TargetName=""dropDownBorder"" Value=""95""/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property=""IsGrouping"" Value=""true""/>
<Condition Property=""VirtualizingPanel.IsVirtualizingWhenGrouping"" Value=""false""/>
</MultiTrigger.Conditions>
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""false""/>
</MultiTrigger>
<Trigger Property=""ScrollViewer.CanContentScroll"" SourceName=""DropDownScrollViewer"" Value=""false"">
<Setter Property=""Canvas.Top"" TargetName=""opaqueRect"" Value=""{Binding VerticalOffset, ElementName=DropDownScrollViewer}""/>
<Setter Property=""Canvas.Left"" TargetName=""opaqueRect"" Value=""{Binding HorizontalOffset, ElementName=DropDownScrollViewer}""/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>";
/// <summary>
/// ComboBoxTemplate ControlTemplate XAML (.NET Framework 3.5)
/// </summary>
private const string _comboBoxTemplateNet35 = @"<ControlTemplate TargetType=""{x:Type ComboBox}"">
<Grid x:Name=""templateRoot"" SnapsToDevicePixels=""true"">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=""*""/>
<ColumnDefinition MinWidth=""{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"" Width=""0""/>
</Grid.ColumnDefinitions>
<Popup x:Name=""PART_Popup"" AllowsTransparency=""true"" Grid.ColumnSpan=""2"" IsOpen=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Margin=""1"" PopupAnimation=""{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"" Placement=""Bottom"">
<Border x:Name=""dropDownBorder"" BorderBrush=""{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"" BorderThickness=""1"" Background=""{DynamicResource {x:Static SystemColors.WindowBrushKey}}"">
<ScrollViewer x:Name=""DropDownScrollViewer"">
<Grid x:Name=""grid"">
<Canvas x:Name=""canvas"" HorizontalAlignment=""Left"" Height=""0"" VerticalAlignment=""Top"" Width=""0"">
<Rectangle x:Name=""opaqueRect"" Fill=""{Binding Background, ElementName=dropDownBorder}"" Height=""{Binding ActualHeight, ElementName=dropDownBorder}"" Width=""{Binding ActualWidth, ElementName=dropDownBorder}""/>
</Canvas>
<ItemsPresenter x:Name=""ItemsPresenter"" KeyboardNavigation.DirectionalNavigation=""Contained"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}""/>
</Grid>
</ScrollViewer>
</Border>
</Popup>
<ToggleButton x:Name=""toggleButton"" BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}"" Grid.ColumnSpan=""2"" IsChecked=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Style=""{DynamicResource ComboBoxToggleButton}""/>
<ContentPresenter x:Name=""contentPresenter"" ContentTemplate=""{TemplateBinding SelectionBoxItemTemplate}"" ContentTemplateSelector=""{TemplateBinding ItemTemplateSelector}"" Content=""{TemplateBinding SelectionBoxItem}"" ContentStringFormat=""{TemplateBinding SelectionBoxItemStringFormat}"" HorizontalAlignment=""{TemplateBinding HorizontalContentAlignment}"" IsHitTestVisible=""false"" Margin=""{TemplateBinding Padding}"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}"" VerticalAlignment=""{TemplateBinding VerticalContentAlignment}""/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property=""HasItems"" Value=""false"">
<Setter Property=""Height"" TargetName=""dropDownBorder"" Value=""95""/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property=""IsGrouping"" Value=""true""/>
</MultiTrigger.Conditions>
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""false""/>
</MultiTrigger>
<Trigger Property=""ScrollViewer.CanContentScroll"" SourceName=""DropDownScrollViewer"" Value=""false"">
<Setter Property=""Canvas.Top"" TargetName=""opaqueRect"" Value=""{Binding VerticalOffset, ElementName=DropDownScrollViewer}""/>
<Setter Property=""Canvas.Left"" TargetName=""opaqueRect"" Value=""{Binding HorizontalOffset, ElementName=DropDownScrollViewer}""/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>";
/// <summary>
/// ComboBoxEditableTemplate ControlTemplate XAML (.NET Framework 4.0 and above)
/// </summary>
private const string _comboBoxEditableTemplateDefault = @"<ControlTemplate TargetType=""{x:Type ComboBox}"">
<Grid x:Name=""templateRoot"" SnapsToDevicePixels=""true"">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=""*""/>
<ColumnDefinition MinWidth=""{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"" Width=""0""/>
</Grid.ColumnDefinitions>
<Popup x:Name=""PART_Popup"" AllowsTransparency=""true"" Grid.ColumnSpan=""2"" IsOpen=""{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"" PopupAnimation=""{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"" Placement=""Bottom"">
<themes:SystemDropShadowChrome x:Name=""shadow"" Color=""Transparent"" MaxHeight=""{TemplateBinding MaxDropDownHeight}"" MinWidth=""{Binding ActualWidth, ElementName=templateRoot}"">
<Border x:Name=""dropDownBorder"" BorderBrush=""{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"" BorderThickness=""1"" Background=""{DynamicResource {x:Static SystemColors.WindowBrushKey}}"">
<ScrollViewer x:Name=""DropDownScrollViewer"">
<Grid x:Name=""grid"" RenderOptions.ClearTypeHint=""Enabled"">
<Canvas x:Name=""canvas"" HorizontalAlignment=""Left"" Height=""0"" VerticalAlignment=""Top"" Width=""0"">
<Rectangle x:Name=""opaqueRect"" Fill=""{Binding Background, ElementName=dropDownBorder}"" Height=""{Binding ActualHeight, ElementName=dropDownBorder}"" Width=""{Binding ActualWidth, ElementName=dropDownBorder}""/>
</Canvas>
<ItemsPresenter x:Name=""ItemsPresenter"" KeyboardNavigation.DirectionalNavigation=""Contained"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}""/>
</Grid>
</ScrollViewer>
</Border>
</themes:SystemDropShadowChrome>
</Popup>
<ToggleButton x:Name=""toggleButton"" BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}"" Grid.ColumnSpan=""2"" IsChecked=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Style=""{DynamicResource ComboBoxToggleButton}""/>
<Border x:Name=""border"" Background=""{DynamicResource TextBox.Static.Background}"" Margin=""{TemplateBinding BorderThickness}"">
<TextBox x:Name=""PART_EditableTextBox"" HorizontalContentAlignment=""{TemplateBinding HorizontalContentAlignment}"" IsReadOnly=""{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"" Margin=""{TemplateBinding Padding}"" Style=""{DynamicResource ComboBoxEditableTextBox}"" VerticalContentAlignment=""{TemplateBinding VerticalContentAlignment}""/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property=""IsEnabled"" Value=""false"">
<Setter Property=""Opacity"" TargetName=""border"" Value=""0.56""/>
</Trigger>
<Trigger Property=""IsKeyboardFocusWithin"" Value=""true"">
<Setter Property=""Foreground"" Value=""Black""/>
</Trigger>
<Trigger Property=""HasDropShadow"" SourceName=""PART_Popup"" Value=""true"">
<Setter Property=""Margin"" TargetName=""shadow"" Value=""0,0,5,5""/>
<Setter Property=""Color"" TargetName=""shadow"" Value=""#71000000""/>
</Trigger>
<Trigger Property=""HasItems"" Value=""false"">
<Setter Property=""Height"" TargetName=""dropDownBorder"" Value=""95""/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property=""IsGrouping"" Value=""true""/>
<Condition Property=""VirtualizingPanel.IsVirtualizingWhenGrouping"" Value=""false""/>
</MultiTrigger.Conditions>
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""false""/>
</MultiTrigger>
<Trigger Property=""ScrollViewer.CanContentScroll"" SourceName=""DropDownScrollViewer"" Value=""false"">
<Setter Property=""Canvas.Top"" TargetName=""opaqueRect"" Value=""{Binding VerticalOffset, ElementName=DropDownScrollViewer}""/>
<Setter Property=""Canvas.Left"" TargetName=""opaqueRect"" Value=""{Binding HorizontalOffset, ElementName=DropDownScrollViewer}""/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>";
/// <summary>
/// ComboBoxEditableTemplate ControlTemplate XAML (.NET Framework 3.5)
/// </summary>
private const string _comboBoxEditableTemplateNet35 = @"<ControlTemplate TargetType=""{x:Type ComboBox}"">
<Grid x:Name=""templateRoot"" SnapsToDevicePixels=""true"">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=""*""/>
<ColumnDefinition MinWidth=""{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"" Width=""0""/>
</Grid.ColumnDefinitions>
<Popup x:Name=""PART_Popup"" AllowsTransparency=""true"" Grid.ColumnSpan=""2"" IsOpen=""{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"" PopupAnimation=""{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"" Placement=""Bottom"">
<Border x:Name=""dropDownBorder"" BorderBrush=""{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"" BorderThickness=""1"" Background=""{DynamicResource {x:Static SystemColors.WindowBrushKey}}"">
<ScrollViewer x:Name=""DropDownScrollViewer"">
<Grid x:Name=""grid"">
<Canvas x:Name=""canvas"" HorizontalAlignment=""Left"" Height=""0"" VerticalAlignment=""Top"" Width=""0"">
<Rectangle x:Name=""opaqueRect"" Fill=""{Binding Background, ElementName=dropDownBorder}"" Height=""{Binding ActualHeight, ElementName=dropDownBorder}"" Width=""{Binding ActualWidth, ElementName=dropDownBorder}""/>
</Canvas>
<ItemsPresenter x:Name=""ItemsPresenter"" KeyboardNavigation.DirectionalNavigation=""Contained"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}""/>
</Grid>
</ScrollViewer>
</Border>
</Popup>
<ToggleButton x:Name=""toggleButton"" BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}"" Grid.ColumnSpan=""2"" IsChecked=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Style=""{DynamicResource ComboBoxToggleButton}""/>
<Border x:Name=""border"" Background=""{DynamicResource TextBox.Static.Background}"" Margin=""{TemplateBinding BorderThickness}"">
<TextBox x:Name=""PART_EditableTextBox"" HorizontalContentAlignment=""{TemplateBinding HorizontalContentAlignment}"" IsReadOnly=""{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"" Margin=""{TemplateBinding Padding}"" Style=""{DynamicResource ComboBoxEditableTextBox}"" VerticalContentAlignment=""{TemplateBinding VerticalContentAlignment}""/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property=""IsEnabled"" Value=""false"">
<Setter Property=""Opacity"" TargetName=""border"" Value=""0.56""/>
</Trigger>
<Trigger Property=""IsKeyboardFocusWithin"" Value=""true"">
<Setter Property=""Foreground"" Value=""Black""/>
</Trigger>
<Trigger Property=""HasItems"" Value=""false"">
<Setter Property=""Height"" TargetName=""dropDownBorder"" Value=""95""/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property=""IsGrouping"" Value=""true""/>
</MultiTrigger.Conditions>
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""false""/>
</MultiTrigger>
<Trigger Property=""ScrollViewer.CanContentScroll"" SourceName=""DropDownScrollViewer"" Value=""false"">
<Setter Property=""Canvas.Top"" TargetName=""opaqueRect"" Value=""{Binding VerticalOffset, ElementName=DropDownScrollViewer}""/>
<Setter Property=""Canvas.Left"" TargetName=""opaqueRect"" Value=""{Binding HorizontalOffset, ElementName=DropDownScrollViewer}""/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>";
#endregion
#region Styles
/// <summary>
/// ComboBoxEditableTextBox Style XAML (.NET Framework 4.0 and above)
/// </summary>
private const string _comboBoxEditableTextBoxStyleDefault = @"<Style TargetType=""{x:Type TextBox}"">
<Setter Property=""OverridesDefaultStyle"" Value=""true""/>
<Setter Property=""AllowDrop"" Value=""true""/>
<Setter Property=""MinWidth"" Value=""0""/>
<Setter Property=""MinHeight"" Value=""0""/>
<Setter Property=""FocusVisualStyle"" Value=""{x:Null}""/>
<Setter Property=""ScrollViewer.PanningMode"" Value=""VerticalFirst""/>
<Setter Property=""Stylus.IsFlicksEnabled"" Value=""False""/>
<Setter Property=""Template"">
<Setter.Value>
<ControlTemplate TargetType=""{x:Type TextBox}"">
<ScrollViewer x:Name=""PART_ContentHost"" Background=""Transparent"" Focusable=""false"" HorizontalScrollBarVisibility=""Hidden"" VerticalScrollBarVisibility=""Hidden""/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>";
/// <summary>
/// ComboBoxEditableTextBox Style XAML (.NET Framework 3.5)
/// </summary>
private const string _comboBoxEditableTextBoxStyleNet35 = @"<Style TargetType=""{x:Type TextBox}"">
<Setter Property=""OverridesDefaultStyle"" Value=""true""/>
<Setter Property=""AllowDrop"" Value=""true""/>
<Setter Property=""MinWidth"" Value=""0""/>
<Setter Property=""MinHeight"" Value=""0""/>
<Setter Property=""FocusVisualStyle"" Value=""{x:Null}""/>
<Setter Property=""Stylus.IsFlicksEnabled"" Value=""False""/>
<Setter Property=""Template"">
<Setter.Value>
<ControlTemplate TargetType=""{x:Type TextBox}"">
<ScrollViewer x:Name=""PART_ContentHost"" Background=""Transparent"" Focusable=""false"" HorizontalScrollBarVisibility=""Hidden"" VerticalScrollBarVisibility=""Hidden""/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>";
/// <summary>
/// CustomComboBoxStyle Style XAML (.NET Framework 4.0 and above)
/// </summary>
private const string _customComboBoxStyleDefault = @"<Style x:Key=""CustomComboBoxStyle"" TargetType=""{x:Type ComboBox}"">
<Setter Property=""FocusVisualStyle"" Value=""{DynamicResource FocusVisual}""/>
<Setter Property=""Background"" Value=""{DynamicResource ComboBox.Static.Background}""/>
<Setter Property=""BorderBrush"" Value=""{DynamicResource ComboBox.Static.Border}""/>
<Setter Property=""Foreground"" Value=""{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}""/>
<Setter Property=""BorderThickness"" Value=""1""/>
<Setter Property=""ScrollViewer.HorizontalScrollBarVisibility"" Value=""Auto""/>
<Setter Property=""ScrollViewer.VerticalScrollBarVisibility"" Value=""Auto""/>
<Setter Property=""Padding"" Value=""6,3,5,3""/>
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""true""/>
<Setter Property=""ScrollViewer.PanningMode"" Value=""VerticalFirst""/>
<Setter Property=""Stylus.IsFlicksEnabled"" Value=""False""/>
<Setter Property=""Template"" Value=""{DynamicResource ComboBoxTemplate}""/>
<Style.Triggers>
<Trigger Property=""IsEditable"" Value=""true"">
<Setter Property=""IsTabStop"" Value=""false""/>
<Setter Property=""Padding"" Value=""2""/>
<Setter Property=""Template"" Value=""{DynamicResource ComboBoxEditableTemplate}""/>
</Trigger>
</Style.Triggers>
</Style>";
/// <summary>
/// CustomComboBoxStyle Style XAML (.NET Framework 3.5)
/// </summary>
private const string _customComboBoxStyleNet35 = @"<Style TargetType=""{x:Type ComboBox}"">
<Setter Property=""FocusVisualStyle"" Value=""{DynamicResource FocusVisual}""/>
<Setter Property=""Background"" Value=""{DynamicResource ComboBox.Static.Background}""/>
<Setter Property=""BorderBrush"" Value=""{DynamicResource ComboBox.Static.Border}""/>
<Setter Property=""Foreground"" Value=""{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}""/>
<Setter Property=""BorderThickness"" Value=""1""/>
<Setter Property=""ScrollViewer.HorizontalScrollBarVisibility"" Value=""Auto""/>
<Setter Property=""ScrollViewer.VerticalScrollBarVisibility"" Value=""Auto""/>
<Setter Property=""Padding"" Value=""6,3,5,3""/>
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""true""/>
<Setter Property=""Stylus.IsFlicksEnabled"" Value=""False""/>
<Setter Property=""Template"" Value=""{DynamicResource ComboBoxTemplate}""/>
<Style.Triggers>
<Trigger Property=""IsEditable"" Value=""true"">
<Setter Property=""IsTabStop"" Value=""false""/>
<Setter Property=""Padding"" Value=""2""/>
<Setter Property=""Template"" Value=""{DynamicResource ComboBoxEditableTemplate}""/>
</Trigger>
</Style.Triggers>
</Style>";
/// <summary>
/// CustomProgressBarStyle Style XAML (.NET Framework 4.0 and above)
/// </summary>
private const string _customProgressBarStyleDefault = @"<Style x:Key=""CustomProgressBarStyle"" TargetType=""{x:Type ProgressBar}"">
<Setter Property=""Foreground"" Value=""{DynamicResource ProgressBar.Progress}""/>
<Setter Property=""Background"" Value=""{DynamicResource ProgressBar.Background}""/>
<Setter Property=""BorderBrush"" Value=""{DynamicResource ProgressBar.Border}""/>
<Setter Property=""BorderThickness"" Value=""1""/>
<Setter Property=""Template"">
<Setter.Value>
<ControlTemplate TargetType=""{x:Type ProgressBar}"">
<Grid x:Name=""TemplateRoot"">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name=""CommonStates"">
<VisualState x:Name=""Determinate""/>
<VisualState x:Name=""Indeterminate"">
<Storyboard RepeatBehavior=""Forever"">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"" Storyboard.TargetName=""Animation"">
<EasingDoubleKeyFrame KeyTime=""0"" Value=""0.25""/>
<EasingDoubleKeyFrame KeyTime=""0:0:1"" Value=""0.25""/>
<EasingDoubleKeyFrame KeyTime=""0:0:2"" Value=""0.25""/>
</DoubleAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransformOrigin)"" Storyboard.TargetName=""Animation"">
<EasingPointKeyFrame KeyTime=""0"" Value=""-0.5,0.5""/>
<EasingPointKeyFrame KeyTime=""0:0:1"" Value=""0.5,0.5""/>
<EasingPointKeyFrame KeyTime=""0:0:2"" Value=""1.5,0.5""/>
</PointAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}""/>
<Rectangle x:Name=""PART_Track""/>
<Grid x:Name=""PART_Indicator"" ClipToBounds=""true"" HorizontalAlignment=""Left"">
<Rectangle x:Name=""Indicator"" Fill=""{TemplateBinding Foreground}""/>
<Rectangle x:Name=""Animation"" Fill=""{TemplateBinding Foreground}"" RenderTransformOrigin=""0.5,0.5"">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property=""Orientation"" Value=""Vertical"">
<Setter Property=""LayoutTransform"" TargetName=""TemplateRoot"">
<Setter.Value>
<RotateTransform Angle=""-90""/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property=""IsIndeterminate"" Value=""true"">
<Setter Property=""Visibility"" TargetName=""Indicator"" Value=""Collapsed""/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>";
/// <summary>
/// CustomProgressBarStyle Style XAML (.NET Framework 3.5)
/// </summary>
private const string _customProgressBarStyleNet35 = @"<Style TargetType=""{x:Type ProgressBar}"">
<Setter Property=""Foreground"" Value=""{DynamicResource ProgressBar.Progress}""/>
<Setter Property=""Background"" Value=""{DynamicResource ProgressBar.Background}""/>
<Setter Property=""BorderBrush"" Value=""{DynamicResource ProgressBar.Border}""/>
<Setter Property=""BorderThickness"" Value=""1""/>
<Setter Property=""Template"">
<Setter.Value>
<ControlTemplate TargetType=""{x:Type ProgressBar}"">
<Grid x:Name=""TemplateRoot"">
<Border BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}""/>
<Rectangle x:Name=""PART_Track""/>
<Grid x:Name=""PART_Indicator"" ClipToBounds=""true"" HorizontalAlignment=""Left"">
<Rectangle x:Name=""Indicator"" Fill=""{TemplateBinding Foreground}""/>
<Rectangle x:Name=""Animation"" Fill=""{TemplateBinding Foreground}"" RenderTransformOrigin=""0.5,0.5"">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property=""Orientation"" Value=""Vertical"">
<Setter Property=""LayoutTransform"" TargetName=""TemplateRoot"">
<Setter.Value>
<RotateTransform Angle=""-90""/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property=""IsIndeterminate"" Value=""true"">
<Setter Property=""Visibility"" TargetName=""Indicator"" Value=""Collapsed""/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>";
#endregion
public App()
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
// Create control templates
CreateControlTemplate("ComboBoxTemplate");
CreateControlTemplate("ComboBoxEditableTemplate");
// Create styles
CreateStyle("ComboBoxEditableTextBox");
CreateStyle("CustomComboBoxStyle");
CreateStyle("CustomProgressBarStyle");
}
/// <summary>
/// Create an XAML parser context with the required namespaces
/// </summary>
private ParserContext CreateParserContext()
{
var context = new ParserContext();
context.XmlnsDictionary[""] = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
context.XmlnsDictionary["x"] = "http://schemas.microsoft.com/winfx/2006/xaml";
#if NETFRAMEWORK
context.XmlnsDictionary["themes"] = "clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero";
#else
context.XmlnsDictionary["themes"] = "clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2";
#endif
context.XamlTypeMapper = new XamlTypeMapper([]);
return context;
}
/// <summary>
/// Create a named control template and add it to the current set of resources
/// </summary>
private void CreateControlTemplate(string resourceName)
{
var parserContext = CreateParserContext();
var controlTemplate = resourceName switch
{
#if NET35
"ComboBoxTemplate" => XamlReader.Parse(_comboBoxTemplateNet35, parserContext) as ControlTemplate,
"ComboBoxEditableTemplate" => XamlReader.Parse(_comboBoxEditableTemplateNet35, parserContext) as ControlTemplate,
#else
"ComboBoxTemplate" => XamlReader.Parse(_comboBoxTemplateDefault, parserContext) as ControlTemplate,
"ComboBoxEditableTemplate" => XamlReader.Parse(_comboBoxEditableTemplateDefault, parserContext) as ControlTemplate,
#endif
_ => throw new ArgumentException($"'{resourceName}' is not a recognized control template", nameof(resourceName)),
};
// Add the control template
Resources[resourceName] = controlTemplate;
}
/// <summary>
/// Create a named style and add it to the current set of resources
/// </summary>
private void CreateStyle(string resourceName)
{
var parserContext = CreateParserContext();
var style = resourceName switch
{
#if NET35
"ComboBoxEditableTextBox" => XamlReader.Parse(_comboBoxEditableTextBoxStyleNet35, parserContext) as Style,
"CustomComboBoxStyle" => XamlReader.Parse(_customComboBoxStyleNet35, parserContext) as Style,
"CustomProgressBarStyle" => XamlReader.Parse(_customProgressBarStyleNet35, parserContext) as Style,
#else
"ComboBoxEditableTextBox" => XamlReader.Parse(_comboBoxEditableTextBoxStyleDefault, parserContext) as Style,
"CustomComboBoxStyle" => XamlReader.Parse(_customComboBoxStyleDefault, parserContext) as Style,
"CustomProgressBarStyle" => XamlReader.Parse(_customProgressBarStyleDefault, parserContext) as Style,
#endif
_ => throw new ArgumentException($"'{resourceName}' is not a recognized style", nameof(resourceName)),
};
// Add the style
Resources[resourceName] = style;
}
#endif
}
}

View File

@@ -1,21 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFrameworks>net6.0-windows;net8.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
<!-- Assembly Properties -->
<TargetFrameworks>net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0-windows;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
<OutputType>WinExe</OutputType>
<ApplicationIcon>Images\Icon.ico</ApplicationIcon>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<ImportFrameworkWinFXTargets Condition="$(TargetFramework.StartsWith(`net3`))">true</ImportFrameworkWinFXTargets>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.0.3</VersionPrefix>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<Nullable>enable</Nullable>
<ApplicationIcon>Images\Icon.ico</ApplicationIcon>
<Title>MPF</Title>
<Description>Frontend for various dumping programs</Description>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<VersionPrefix>3.0.0</VersionPrefix>
<InternalsVisibleTo>MPF.Test</InternalsVisibleTo>
<Description>Frontend for various dumping programs</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>
<!-- Special handling for Aero2 on non-Framework .NET -->
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net4`))">
<ReferenceWpfAeroTheme>true</ReferenceWpfAeroTheme>
<ReferenceWpfAero2Theme>false</ReferenceWpfAero2Theme>
<ReferenceWpfAeroLiteTheme>false</ReferenceWpfAeroLiteTheme>
<ReferenceWpfClassicTheme>false</ReferenceWpfClassicTheme>
<ReferenceWpfLunaTheme>false</ReferenceWpfLunaTheme>
<ReferenceWpfRoyaleTheme>false</ReferenceWpfRoyaleTheme>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`)) OR $(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
<ReferenceWpfAeroTheme>false</ReferenceWpfAeroTheme>
<ReferenceWpfAero2Theme>true</ReferenceWpfAero2Theme>
<ReferenceWpfAeroLiteTheme>false</ReferenceWpfAeroLiteTheme>
<ReferenceWpfClassicTheme>false</ReferenceWpfClassicTheme>
<ReferenceWpfLunaTheme>false</ReferenceWpfLunaTheme>
<ReferenceWpfRoyaleTheme>false</ReferenceWpfRoyaleTheme>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="MPF.Test" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Icon.ico" />
</ItemGroup>
@@ -25,15 +56,62 @@
<ProjectReference Include="..\MPF.UI.Core\MPF.UI.Core.csproj" />
</ItemGroup>
<!-- Support for old .NET versions -->
<ItemGroup Condition="$(TargetFramework.StartsWith(`net3`))">
<Reference Include="PresentationBuildTasks" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationBuildTasks.dll" />
<Reference Include="PresentationCore" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll" />
<Reference Include="PresentationFramework" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll" />
<Reference Include="WindowsBase" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
<Reference Include="PresentationFramework.Aero" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`)) OR $(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
<Reference Include="PresentationFramework.Aero2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.0" GeneratePathProperty="true">
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.2" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.2.0" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.1" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
<!-- Special handling for Aero2 on non-Framework .NET -->
<Target Name="SelectWpfThemeAssembly" AfterTargets="ResolveAssemblyReferences" Condition="'$(ReferenceWpfAeroTheme)' == 'true' OR '$(ReferenceWpfAero2Theme)' == 'true' OR '$(ReferenceWpfAeroLiteTheme)' == 'true' OR '$(ReferenceWpfClassicTheme)' == 'true' OR '$(ReferenceWpfLunaTheme)' == 'true' OR '$(ReferenceWpfRoyaleTheme)' == 'true'">
<ItemGroup>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Aero'">
<Aliases Condition="'$(WpfAeroThemeAliases)'!=''">$(WpfAeroThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Aero2'">
<Aliases Condition="'$(WpfAero2ThemeAliases)'!=''">$(WpfAero2ThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.AeroLite'">
<Aliases Condition="'$(WpfAeroLiteThemeAliases)'!=''">$(WpfAeroLiteThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Classic'">
<Aliases Condition="'$(WpfClassicThemeAliases)'!=''">$(WpfClassicThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Luna'">
<Aliases Condition="'$(WpfLunaThemeAliases)'!=''">$(WpfLunaThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Royale'">
<Aliases Condition="'$(WpfRoyaleThemeAliases)'!=''">$(WpfRoyaleThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<ReferencePath Remove="@(_WpfThemeAssemblies)" />
<ReferencePath Include="@(_WpfThemeAssemblies)" Condition="('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Aero' AND'$(ReferenceWpfAeroTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Aero2' AND '$(ReferenceWpfAero2Theme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.AeroLite' AND '$(ReferenceWpfAeroLiteTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Classic' AND '$(ReferenceWpfClassicTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Luna' AND '$(ReferenceWpfLunaTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Royale' AND '$(ReferenceWpfRoyaleTheme)'=='true')" />
</ItemGroup>
</Target>
</Project>

View File

@@ -60,9 +60,9 @@ Choose one of `win-x64`, `linux-x64`, or `osx-x64` depending on the machine you
### Build Scripts
Windows users may run `publish-win.bat` and Linux users may run `publish-nix.sh` to perform a full release build.
Windows users may run `publish-win.ps1` and Linux users may run `publish-nix.sh` to perform a full release build.
- `publish-win.bat` will build and package all variants of MPF UI and MPF.Check
- `publish-win.ps1` will build and package all variants of MPF UI and MPF.Check
- The script requires [7-zip commandline](https://www.7-zip.org/download.html) and [Git for Windows](https://git-scm.com/downloads) to be installed and in `PATH`
- `publish-nix.sh` will _only_ build and package all variants of MPF.Check
- The script requires `zip` and `git` to be installed and in `PATH`

View File

@@ -1,5 +1,5 @@
# version format
version: 3.0.0-{build}
version: 3.0.3-{build}
# pull request template
pull_requests:
@@ -48,8 +48,8 @@ after_build:
- 7z e DiscImageCreator_20230606.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Creator Release_ANSI\*
# Redumper
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_247/redumper-2023.11.01_build247-win64.zip
- 7z e redumper-2023.11.01_build247-win64.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Redumper redumper-2023.11.01_build247-win64\bin\*
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_271/redumper-2023.11.30_build271-win64.zip
- 7z e redumper-2023.11.30_build271-win64.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Redumper redumper-2023.11.30_build271-win64\bin\*
# Create MPF Debug archives
- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net8.0-windows\win-x64\publish\

157
publish-win.ps1 Normal file
View File

@@ -0,0 +1,157 @@
# This batch file assumes the following:
# - .NET 8.0 (or newer) SDK is installed and in PATH
# - 7-zip commandline (7z.exe) is installed and in PATH
# - Git for Windows is installed and in PATH
# - The relevant commandline programs are already downloaded
# and put into their respective folders
#
# If any of these are not satisfied, the operation may fail
# in an unpredictable way and result in an incomplete output.
# Optional parameters
param(
[Parameter(Mandatory = $false)]
[Alias("UseAll")]
[switch]$USE_ALL,
[Parameter(Mandatory = $false)]
[Alias("IncludePrograms")]
[switch]$INCLUDE_PROGRAMS,
[Parameter(Mandatory = $false)]
[Alias("NoBuild")]
[switch]$NO_BUILD,
[Parameter(Mandatory = $false)]
[Alias("NoArchive")]
[switch]$NO_ARCHIVE
)
# Set the current directory as a variable
$BUILD_FOLDER = $PSScriptRoot
# Set the current commit hash
$COMMIT = git log --pretty=format:"%H" -1
# Create the build matrix arrays
$UI_FRAMEWORKS = @('net6.0-windows', 'net8.0-windows')
$UI_RUNTIMES = @('win-x64')
$CHECK_FRAMEWORKS = @('net6.0', 'net8.0')
$CHECK_RUNTIMES = @('win-x64', 'linux-x64', 'osx-x64')
# Use expanded lists, if requested
if ($USE_ALL.IsPresent)
{
$UI_FRAMEWORKS = @('net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0-windows', 'net6.0-windows', 'net7.0-windows', 'net8.0-windows')
$UI_RUNTIMES = @('win-x86', 'win-x64')
$CHECK_FRAMEWORKS = @('net20', 'net35', 'net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0')
$CHECK_RUNTIMES = @('win-x86', 'win-x64', 'win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64')
}
# Create the filter arrays
$SINGLE_FILE_CAPABLE = @('net5.0', 'net5.0-windows', 'net6.0', 'net6.0-windows', 'net7.0', 'net7.0-windows', 'net8.0', 'net8.0-windows')
$VALID_CROSS_PLATFORM_FRAMEWORKS = @('netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0')
$VALID_CROSS_PLATFORM_RUNTIMES = @('win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64')
# Only build if requested
if (!$NO_BUILD.IsPresent)
{
# Restore Nuget packages for all builds
Write-Host "Restoring Nuget packages"
dotnet restore
# Build UI
foreach ($FRAMEWORK in $UI_FRAMEWORKS)
{
foreach ($RUNTIME in $UI_RUNTIMES)
{
# Only .NET 5 and above can publish to a single file
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK)
{
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
}
else
{
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
}
}
}
# Build Check
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS)
{
foreach ($RUNTIME in $CHECK_RUNTIMES)
{
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME)
{
continue
}
# Only .NET 5 and above can publish to a single file
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK)
{
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
}
else
{
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
}
}
}
}
# Only create archives if requested
if (!$NO_ARCHIVE.IsPresent)
{
# Create UI archives
foreach ($FRAMEWORK in $UI_FRAMEWORKS)
{
foreach ($RUNTIME in $UI_RUNTIMES)
{
Set-Location -Path $BUILD_FOLDER\MPF\bin\Debug\$FRAMEWORK\$RUNTIME\publish\
if ($INCLUDE_PROGRAMS.IsPresent)
{
7z a -tzip $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_debug.zip *
}
else
{
7z a -tzip -x!Programs\* $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_debug.zip *
}
Set-Location -Path $BUILD_FOLDER\MPF\bin\Release\$FRAMEWORK\$RUNTIME\publish\
if ($INCLUDE_PROGRAMS.IsPresent)
{
7z a -tzip $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_release.zip *
}
else
{
7z a -tzip -x!Programs\* $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_release.zip *
}
}
}
# Create Check archives
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS)
{
foreach ($RUNTIME in $CHECK_RUNTIMES)
{
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME)
{
continue
}
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Debug\$FRAMEWORK\$RUNTIME\publish\
7z a -tzip $BUILD_FOLDER\MPF.Check_${FRAMEWORK}_${RUNTIME}_debug.zip *
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Release\$FRAMEWORK\${RUNTIME}\publish\
7z a -tzip $BUILD_FOLDER\MPF.Check_${FRAMEWORK}_${RUNTIME}_release.zip *
}
}
# Reset the directory
Set-Location -Path $PSScriptRoot
}