Compare commits

...

75 Commits
2.7.0 ... 2.7.3

Author SHA1 Message Date
Matt Nadareski
33b4be8b24 Bump version 2023-10-26 09:35:25 -04:00
Matt Nadareski
71a4edc8ba Update changelog 2023-10-26 09:18:13 -04:00
Deterous
ceb305eb54 Use relative path internally (#589) 2023-10-26 06:17:17 -07:00
Matt Nadareski
0b0d13dcf3 Use Array.Empty in hasher 2023-10-26 01:25:34 -04:00
Matt Nadareski
9f02368622 Fix two small nullability issues 2023-10-26 01:00:09 -04:00
Matt Nadareski
152010ee14 Update to MMI 3.0.0-preview.4 2023-10-26 00:58:44 -04:00
Matt Nadareski
c6d5f0aea5 Update to BurnOutSharp 2.9.0 2023-10-26 00:57:23 -04:00
Matt Nadareski
8c2ad64bb8 Update XUnit packages 2023-10-25 23:19:44 -04:00
Matt Nadareski
fa54d694b6 Fix CRC32 hashing (fixes #587) 2023-10-25 22:38:30 -04:00
Matt Nadareski
dc35b08624 Attempt to parse out PS5 params.json 2023-10-25 16:56:13 -04:00
Matt Nadareski
4429515ba2 Update package versions (fixes #586) 2023-10-25 16:22:32 -04:00
Matt Nadareski
fdbc7b34e5 Update changelog 2023-10-19 20:58:15 -04:00
Deterous
a1ab442cf0 Set UDF CD threshold at 800MB (#585)
* Set UDF CD threshold at 800MB

* Condense DriveFormat checks
2023-10-19 17:56:58 -07:00
Matt Nadareski
9ed5c297f6 Expose suffix setting (fixes #428) 2023-10-18 22:23:40 -04:00
Matt Nadareski
4ce9b214b0 Wire through filename suffix 2023-10-18 22:19:25 -04:00
Matt Nadareski
7dcdadda75 Add filename suffix setting (nw) 2023-10-18 22:04:15 -04:00
Matt Nadareski
f87a4d9fe2 Fix up DumpEnvironment a bit 2023-10-18 21:54:04 -04:00
Matt Nadareski
e4e5d173f0 Rearrange OptionsWindow to be easier to navigate 2023-10-18 16:35:34 -04:00
Matt Nadareski
115b242d59 Add optional file deletion (fixes #126) 2023-10-18 15:49:57 -04:00
Matt Nadareski
706bf8a431 Add deleteable file lists for Redumper and DIC 2023-10-18 15:41:51 -04:00
Matt Nadareski
87ecf1aa99 Add framework for deleteable files 2023-10-18 15:33:32 -04:00
Matt Nadareski
b5cf274333 Simply theme application 2023-10-18 10:31:58 -04:00
Matt Nadareski
4f4b6879b6 Fix drive letter check (fixes #584) 2023-10-18 10:22:05 -04:00
Matt Nadareski
3b19463913 Create method for applying theme 2023-10-18 10:21:40 -04:00
Matt Nadareski
37386cd182 Fix multiple handler invocation 2023-10-18 03:28:41 -04:00
Matt Nadareski
e04bdad16c Add first-run Options title, fix saving bug 2023-10-18 02:59:41 -04:00
Matt Nadareski
e37f12705d Fix drive letter issue in UI 2023-10-18 02:30:38 -04:00
Matt Nadareski
5c8dc2c23a Show options window on first run (fixes #467) 2023-10-18 02:10:29 -04:00
Matt Nadareski
e9121f3b03 Create skeleton for first-run 2023-10-18 01:59:55 -04:00
Matt Nadareski
d68175db4e Convert Drive to use paths internally 2023-10-18 01:47:42 -04:00
Matt Nadareski
9d8181b0e2 Add path variants for PlayStation info 2023-10-18 01:29:26 -04:00
Matt Nadareski
6d657f268a Split InfoTool into 2 classes 2023-10-18 01:17:12 -04:00
Matt Nadareski
3b3c5f823d Bump version 2023-10-17 20:53:07 -04:00
Matt Nadareski
09fc313492 Support Redumper 231 outputs 2023-10-17 20:51:48 -04:00
Matt Nadareski
316d0f6e54 Clean up issue templates 2023-10-16 00:44:57 -04:00
Matt Nadareski
a0033238bd Get SecuROM data from Redumper (fixes #583) 2023-10-16 00:38:56 -04:00
Matt Nadareski
5b1c6a7f46 Update changlog 2023-10-15 21:51:51 -04:00
fuzzball
8c0dff6552 Correct the condition (#582) 2023-10-15 18:51:23 -07:00
Matt Nadareski
43b230c84a Update Redumper to build 230 2023-10-15 21:42:45 -04:00
Matt Nadareski
f1b657011d Enable parameters checkbox by default (fixes #580) 2023-10-15 15:33:18 -04:00
Matt Nadareski
e4d8ac8e1c Fix Redumper retry count not showing 2023-10-14 23:04:05 -04:00
Matt Nadareski
08f44173dd Update changelog 2023-10-13 10:56:10 -04:00
Deterous
54765c71fd Remove code for getting UH from DIC logs (#577) 2023-10-13 07:55:52 -07:00
Matt Nadareski
01f8b49214 Update changelog 2023-10-13 10:54:57 -04:00
Deterous
e8b0b3efaa Improve check for which program supports which media (#578)
* Move ProgramSupportsMedia to MPF.Core.Utilities.Tools

* Implement ProgramSupportsMedia slightly less naively

* ProgramSupportsMedia only considers MPF-supported types
2023-10-13 07:54:10 -07:00
Matt Nadareski
f637938858 Update changelog 2023-10-12 15:47:13 -04:00
Deterous
ae326c1d2f Disable dumping button when Redumper selected with unsupported media type (#576)
* Disable dumping button when HDDVD + Redumper selected

* Exclude Bluray+Redumper as well

* Move program-supports-mediatype logic to helper function

* Fix for net48

* Fix for net48

* Undo fixes for net48
2023-10-12 12:46:44 -07:00
Matt Nadareski
a4a1e6bf0a Suppress some unnecessary messages 2023-10-12 01:27:54 -04:00
Matt Nadareski
ecee44966e Gate some switch expressions 2023-10-12 01:19:47 -04:00
Matt Nadareski
83437977ba Update changelog 2023-10-11 23:56:00 -04:00
Matt Nadareski
8fcac1d425 Cleanup and gated code 2023-10-11 23:55:50 -04:00
Matt Nadareski
705b5f1049 Fix options loading for Check 2023-10-11 16:37:10 -04:00
Matt Nadareski
367d0c104b Bump version 2023-10-11 13:48:32 -04:00
Matt Nadareski
5d4bed2d9e Remove unnecessary namespacing 2023-10-11 13:41:42 -04:00
Matt Nadareski
63756192d8 Enable path browse button by default 2023-10-11 13:13:36 -04:00
Matt Nadareski
b68ec78184 Version-gate some more newer syntax 2023-10-11 13:07:45 -04:00
Matt Nadareski
c55d5183fb Fix Check always showing Help text 2023-10-11 12:54:40 -04:00
Matt Nadareski
f82e925944 Version-gate some newer syntax 2023-10-11 12:37:31 -04:00
Matt Nadareski
c19f9ea173 Var-ify many instances 2023-10-11 12:29:06 -04:00
Matt Nadareski
76b2dd79ab Handle some suggested changes 2023-10-11 12:26:24 -04:00
Matt Nadareski
c96ff23ad1 Update changelog 2023-10-11 12:14:56 -04:00
Matt Nadareski
cd68b55b93 Enable nullability in MPF.Test 2023-10-11 12:14:42 -04:00
Matt Nadareski
cad14d96f7 Enable nullability in MPF.UI.Core 2023-10-11 11:49:56 -04:00
Matt Nadareski
daaf9f1932 Enable nullability in MPF 2023-10-11 11:16:34 -04:00
Matt Nadareski
cb7502b450 Update changelog 2023-10-11 10:34:00 -04:00
Deterous
ece142bbf1 Place message boxes at center of main window (#574) 2023-10-11 07:33:26 -07:00
Matt Nadareski
611fee4605 Remove all but .NET 6 for AppVeyor packaging (fixes #573) 2023-10-10 23:44:30 -04:00
Matt Nadareski
791e2d0272 Enable nullability in Check 2023-10-10 23:22:21 -04:00
Matt Nadareski
81742a4676 Unify handling of enable/disable events 2023-10-10 22:05:39 -04:00
Matt Nadareski
1ff3f2210c Handle numeric disc titles as issue numbers (fixes #543) 2023-10-10 21:55:51 -04:00
Matt Nadareski
be9e4b91d5 Add generic drive flag for Redumper (fixes #546) 2023-10-10 21:45:17 -04:00
Matt Nadareski
854dcc5f95 Add BE read flag for Redumper (fixes #545) 2023-10-10 21:42:16 -04:00
Matt Nadareski
29b71db33a Allow user-supplied information in Check (fixes #572) 2023-10-10 21:27:36 -04:00
Matt Nadareski
2ee64b222a Fix errant version detection issue 2023-10-10 20:56:53 -04:00
Matt Nadareski
afda54f97b Add pull-all flag for Check (fixes #571) 2023-10-10 20:51:39 -04:00
49 changed files with 6029 additions and 4074 deletions

View File

@@ -10,7 +10,6 @@ assignees: mnadareski
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
- .NET 6.0 has known limitations, so make sure that what you're asking for isn't already in another build.
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
If none of those apply, then continue...

View File

@@ -10,7 +10,6 @@ assignees: mnadareski
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
- .NET 6.0 has known limitations, so make sure that what you're giving information on isn't already in another build.
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
If none of those apply, then continue...

View File

@@ -10,7 +10,6 @@ assignees: mnadareski
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the issue has already been addressed.
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
- .NET 6.0 has known issues, please try using another build to reproduce the error
- Check multiple discs to help narrow down the issue
- Check the Options to see if changing any of those affects your issue.
@@ -27,6 +26,7 @@ What runtime version are you using?
- [ ] .NET Framework 4.8 running on (Operating System)
- [ ] .NET 6.0 running on (Operating System)
- [ ] .NET 7.0 running on (Operating System)
**Describe the issue**
A clear and concise description of what the bug is.

View File

@@ -1,3 +1,75 @@
### 2.7.3 (2023-10-26)
- Split InfoTool into 2 classes
- Add path variants for PlayStation info
- Convert Drive to use paths internally
- Create skeleton for first-run
- Show options window on first run
- Fix drive letter issue in UI
- Add first-run Options title, fix saving bug
- Fix multiple handler invocation
- Create method for applying theme
- Fix drive letter check
- Simply theme application
- Add framework for deleteable files
- Add deleteable file lists for Redumper and DIC
- Add optional file deletion
- Rearrange OptionsWindow to be easier to navigate
- Fix up DumpEnvironment a bit
- Add filename suffix setting (nw)
- Wire through filename suffix
- Expose suffix setting
- Set UDF CD threshold at 800MB (Deterous)
- Update package versions
- Attempt to parse out PS5 params.json
- Fix CRC32 hashing
- Update XUnit packages
- Update to BurnOutSharp 2.9.0
- Update to MMI 3.0.0-preview.4
- Fix two small nullability issues
- Use Array.Empty in hasher
- Always use relative path internally (Deterous)
### 2.7.2 (2023-10-17)
- Fix options loading for Check
- Cleanup and gated code
- Gate some switch expressions
- Suppress some unnecessary messages
- Disable dumping button when Redumper selected with unsupported media type (Deterous)
- Improve check for which program supports which media (Deterous)
- Remove code for getting Universal Hash from DIC logs (Deterous)
- Fix Redumper retry count not showing
- Enable parameters checkbox by default
- Update Redumper to build 230
- Fix GetPVD in dic (fuzz6001)
- Get SecuROM data from Redumper
- Clean up issue templates
- Support Redumper 231 outputs
### 2.7.1 (2023-10-11)
- Add pull-all flag for Check
- Fix errant version detection issue
- Allow user-supplied information in Check
- Add BE read flag for Redumper
- Add generic drive flag for Redumper
- Handle numeric disc titles as issue numbers
- Unify handling of enable/disable events
- Enable nullability in Check
- Remove all but .NET 6 for AppVeyor packaging
- Place message boxes at center of main window (Deterous)
- Enable nullability in MPF
- Enable nullability in MPF.UI.Core
- Enable nullability in MPF.Test
- Handle some suggested changes
- Var-ify many instances
- Version-gate some newer syntax
- Fix Check always showing Help text
- Version-gate some more newer syntax
- Enable path browse button by default
- Remove unnecessary namespacing
### 2.7.0 (2023-10-11)
- Attempt to replace NRT

View File

@@ -8,7 +8,11 @@
<Description>Validator for various dumping programs</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<VersionPrefix>2.7.0</VersionPrefix>
<VersionPrefix>2.7.3</VersionPrefix>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
@@ -20,7 +24,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.9.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />

View File

@@ -1,6 +1,6 @@
using System;
using System.IO;
using BurnOutSharp;
using BinaryObjectScanner;
using MPF.Core;
using MPF.Core.Data;
using MPF.Core.Utilities;
@@ -14,14 +14,16 @@ namespace MPF.Check
public static void Main(string[] args)
{
// Try processing the standalone arguments
if (!OptionsLoader.ProcessStandaloneArguments(args))
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
if (standaloneProcessed != false)
{
DisplayHelp();
if (standaloneProcessed == null)
DisplayHelp();
return;
}
// Try processing the common arguments
(bool success, MediaType mediaType, RedumpSystem? knownSystem, string error) = OptionsLoader.ProcessCommonArguments(args);
(bool success, MediaType mediaType, RedumpSystem? knownSystem, var error) = OptionsLoader.ProcessCommonArguments(args);
if (!success)
{
DisplayHelp(error);
@@ -29,7 +31,7 @@ namespace MPF.Check
}
// Loop through and process options
(Core.Data.Options options, string path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
(var options, var seedInfo, var path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
if (options.InternalProgram == InternalProgram.NONE)
{
DisplayHelp("A program name needs to be provided");
@@ -46,7 +48,7 @@ namespace MPF.Check
#if NET48
(bool? _, string message) = RedumpWebClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword);
#else
(bool? _, string message) = RedumpHttpClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword).ConfigureAwait(false).GetAwaiter().GetResult();
(bool? _, string? message) = RedumpHttpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
if (!string.IsNullOrWhiteSpace(message))
Console.WriteLine(message);
@@ -65,7 +67,11 @@ namespace MPF.Check
string filepath = Path.GetFullPath(args[i].Trim('"'));
// Now populate an environment
#if NET48
Drive drive = null;
#else
Drive? drive = null;
#endif
if (!string.IsNullOrWhiteSpace(path))
drive = Drive.Create(null, path);
@@ -81,7 +87,11 @@ namespace MPF.Check
/// Display help for MPF.Check
/// </summary>
/// <param name="error">Error string to prefix the help text with</param>
#if NET48
private static void DisplayHelp(string error = null)
#else
private static void DisplayHelp(string? error = null)
#endif
{
if (error != null)
Console.WriteLine(error);

View File

@@ -1,5 +1,5 @@
using System;
using BurnOutSharp;
using BinaryObjectScanner;
using MPF.Core.Data;
namespace MPF.Core
@@ -9,7 +9,11 @@ namespace MPF.Core
/// <summary>
/// Simple process counter to write to console
/// </summary>
#if NET48
public static void ProgressUpdated(object sender, Result value)
#else
public static void ProgressUpdated(object? sender, Result value)
#endif
{
Console.WriteLine(value.Message);
}
@@ -17,7 +21,11 @@ namespace MPF.Core
/// <summary>
/// Simple process counter to write to console
/// </summary>
#if NET48
public static void ProgressUpdated(object sender, ProtectionProgress value)
#else
public static void ProgressUpdated(object? sender, ProtectionProgress value)
#endif
{
Console.WriteLine($"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}");
}

View File

@@ -96,9 +96,10 @@ namespace MPF.Core.Data
}
/// <summary>
/// Windows drive letter
/// Read-only access to the drive letter
/// </summary>
public char Letter => this.Name == null || this.Name.Length == 0 ? '\0' : this.Name[0];
/// <remarks>Should only be used in UI applications</remarks>
public char? Letter => this.Name?[0] ?? '\0';
#endregion
@@ -188,7 +189,7 @@ namespace MPF.Core.Data
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
{
var drives = GetDriveList(ignoreFixedDrives);
drives = drives.OrderBy(i => i == null ? '\0' : i.Letter).ToList();
drives = drives.OrderBy(i => i == null ? "\0" : i.Name).ToList();
return drives;
}
@@ -264,13 +265,9 @@ namespace MPF.Core.Data
}
// Handle optical media by size and filesystem
if (this.TotalSize >= 0 && this.TotalSize < 800_000_000 && this.DriveFormat == "CDFS")
if (this.TotalSize >= 0 && this.TotalSize <= 800_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
return (MediaType.CDROM, null);
else if (this.TotalSize >= 0 && this.TotalSize < 400_000_000 && this.DriveFormat == "UDF")
return (MediaType.CDROM, null);
else if (this.TotalSize >= 800_000_000 && this.TotalSize <= 8_540_000_000 && this.DriveFormat == "CDFS")
return (MediaType.DVD, null);
else if (this.TotalSize >= 400_000_000 && this.TotalSize <= 8_540_000_000 && this.DriveFormat == "UDF")
else if (this.TotalSize > 800_000_000 && this.TotalSize <= 8_540_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
return (MediaType.DVD, null);
else if (this.TotalSize > 8_540_000_000)
return (MediaType.BluRay, null);
@@ -285,10 +282,8 @@ namespace MPF.Core.Data
/// <returns></returns>
public RedumpSystem? GetRedumpSystem(RedumpSystem? defaultValue)
{
string drivePath = $"{this.Letter}:\\";
// If we can't read the media in that drive, we can't do anything
if (!Directory.Exists(drivePath))
if (!Directory.Exists(this.Name))
return defaultValue;
// We're going to assume for floppies, HDDs, and removable drives
@@ -307,7 +302,7 @@ namespace MPF.Core.Data
// Bandai Playdia Quick Interactive System
try
{
List<string> files = Directory.EnumerateFiles(drivePath, "*", SearchOption.TopDirectoryOnly).ToList();
List<string> files = Directory.EnumerateFiles(this.Name, "*", SearchOption.TopDirectoryOnly).ToList();
if (files.Any(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
&& files.Any(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
@@ -318,7 +313,7 @@ namespace MPF.Core.Data
catch { }
// Mattel Fisher-Price iXL
if (File.Exists(Path.Combine(drivePath, "iXL", "iXLUpdater.exe")))
if (File.Exists(Path.Combine(this.Name, "iXL", "iXLUpdater.exe")))
{
return RedumpSystem.MattelFisherPriceiXL;
}
@@ -326,8 +321,8 @@ namespace MPF.Core.Data
// Microsoft Xbox 360
try
{
if (Directory.Exists(Path.Combine(drivePath, "$SystemUpdate"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "$SystemUpdate")).Any()
if (Directory.Exists(Path.Combine(this.Name, "$SystemUpdate"))
&& Directory.EnumerateFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
&& this.TotalSize <= 500_000_000)
{
return RedumpSystem.MicrosoftXbox360;
@@ -338,8 +333,8 @@ namespace MPF.Core.Data
// Microsoft Xbox One
try
{
if (Directory.Exists(Path.Combine(drivePath, "MSXC"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "MSXC")).Any())
if (Directory.Exists(Path.Combine(this.Name, "MSXC"))
&& Directory.EnumerateFiles(Path.Combine(this.Name, "MSXC")).Any())
{
return RedumpSystem.MicrosoftXboxOne;
}
@@ -347,35 +342,23 @@ namespace MPF.Core.Data
catch { }
// Sega Dreamcast
if (File.Exists(Path.Combine(drivePath, "IP.BIN")))
if (File.Exists(Path.Combine(this.Name, "IP.BIN")))
{
return RedumpSystem.SegaDreamcast;
}
// Sega Mega-CD / Sega-CD
if (File.Exists(Path.Combine(drivePath, "_BOOT", "IP.BIN"))
|| File.Exists(Path.Combine(drivePath, "_BOOT", "SP.BIN"))
|| File.Exists(Path.Combine(drivePath, "_BOOT", "SP_AS.BIN"))
|| File.Exists(Path.Combine(drivePath, "FILESYSTEM.BIN")))
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")))
{
return RedumpSystem.SegaMegaCDSegaCD;
}
// Sega Saturn
try
{
var sector = ReadSector(0);
if (sector != null)
{
if (sector.StartsWith(Interface.SaturnSectorZeroStart))
return RedumpSystem.SegaSaturn;
}
}
catch { }
// Sony PlayStation and Sony PlayStation 2
string psxExePath = Path.Combine(drivePath, "PSX.EXE");
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
string psxExePath = Path.Combine(this.Name, "PSX.EXE");
string systemCnfPath = Path.Combine(this.Name, "SYSTEM.CNF");
if (File.Exists(systemCnfPath))
{
// Check for either BOOT or BOOT2
@@ -393,9 +376,9 @@ namespace MPF.Core.Data
// Sony PlayStation 3
try
{
if (Directory.Exists(Path.Combine(drivePath, "PS3_GAME"))
|| Directory.Exists(Path.Combine(drivePath, "PS3_UPDATE"))
|| File.Exists(Path.Combine(drivePath, "PS3_DISC.SFB")))
if (Directory.Exists(Path.Combine(this.Name, "PS3_GAME"))
|| Directory.Exists(Path.Combine(this.Name, "PS3_UPDATE"))
|| File.Exists(Path.Combine(this.Name, "PS3_DISC.SFB")))
{
return RedumpSystem.SonyPlayStation3;
}
@@ -414,13 +397,13 @@ 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 (File.Exists(Path.Combine(drivePath, "PS4", "UPDATE", "PS4UPDATE.PUP")))
if (File.Exists(Path.Combine(this.Name, "PS4", "UPDATE", "PS4UPDATE.PUP")))
{
return RedumpSystem.SonyPlayStation4;
}
// V.Tech V.Flash / V.Smile Pro
if (File.Exists(Path.Combine(drivePath, "0SYSTEM")))
if (File.Exists(Path.Combine(this.Name, "0SYSTEM")))
{
return RedumpSystem.VTechVFlashVSmilePro;
}
@@ -430,7 +413,7 @@ namespace MPF.Core.Data
#region Computers
// Sharp X68000
if (File.Exists(Path.Combine(drivePath, "COMMAND.X")))
if (File.Exists(Path.Combine(this.Name, "COMMAND.X")))
{
return RedumpSystem.SharpX68000;
}
@@ -440,7 +423,7 @@ namespace MPF.Core.Data
#region Video Formats
// BD-Video
if (Directory.Exists(Path.Combine(drivePath, "BDMV")))
if (Directory.Exists(Path.Combine(this.Name, "BDMV")))
{
// Technically BD-Audio has this as well, but it's hard to split that out right now
return RedumpSystem.BDVideo;
@@ -449,14 +432,14 @@ namespace MPF.Core.Data
// DVD-Audio and DVD-Video
try
{
if (Directory.Exists(Path.Combine(drivePath, "AUDIO_TS"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "AUDIO_TS")).Any())
if (Directory.Exists(Path.Combine(this.Name, "AUDIO_TS"))
&& Directory.EnumerateFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
{
return RedumpSystem.DVDAudio;
}
else if (Directory.Exists(Path.Combine(drivePath, "VIDEO_TS"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VIDEO_TS")).Any())
else if (Directory.Exists(Path.Combine(this.Name, "VIDEO_TS"))
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
{
return RedumpSystem.DVDVideo;
}
@@ -466,8 +449,8 @@ namespace MPF.Core.Data
// HD-DVD-Video
try
{
if (Directory.Exists(Path.Combine(drivePath, "HVDVD_TS"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "HVDVD_TS")).Any())
if (Directory.Exists(Path.Combine(this.Name, "HVDVD_TS"))
&& Directory.EnumerateFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
{
return RedumpSystem.HDDVDVideo;
}
@@ -477,8 +460,8 @@ namespace MPF.Core.Data
// VCD
try
{
if (Directory.Exists(Path.Combine(drivePath, "VCD"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VCD")).Any())
if (Directory.Exists(Path.Combine(this.Name, "VCD"))
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VCD")).Any())
{
return RedumpSystem.VideoCD;
}
@@ -542,56 +525,6 @@ namespace MPF.Core.Data
return null;
}
/// <summary>
/// Read a sector with a specified size from the drive
/// </summary>
/// <param name="num">Sector number, non-negative</param>
/// <param name="size">Size of a sector in bytes</param>
/// <returns>Byte array representing the sector, null on error</returns>
#if NET48
public byte[] ReadSector(long num, int size = 2048)
#else
public byte[]? ReadSector(long num, int size = 2048)
#endif
{
// Missing drive leter is not supported
if (string.IsNullOrEmpty(this.Name))
return null;
// We don't support negative sectors
if (num < 0)
return null;
// Wrap the following in case of device access errors
#if NET48
Stream fs = null;
#else
Stream? fs = null;
#endif
try
{
// Open the drive as a device
fs = File.OpenRead($"\\\\?\\{this.Letter}:");
// Seek to the start of the sector, if possible
long start = num * size;
fs.Seek(start, SeekOrigin.Begin);
// Read and return the sector
byte[] buffer = new byte[size];
fs.Read(buffer, 0, size);
return buffer;
}
catch
{
return null;
}
finally
{
fs?.Dispose();
}
}
/// <summary>
/// Refresh the current drive information based on path
/// </summary>
@@ -655,7 +588,7 @@ namespace MPF.Core.Data
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
{
char devId = (properties["Caption"].Value as string ?? string.Empty)[0];
drives.ForEach(d => { if (d?.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
drives.ForEach(d => { if (d?.Name != null && d.Name[0] == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
}
}
}

View File

@@ -8,14 +8,22 @@ namespace MPF.Core.Data
{
public class IniFile : IDictionary<string, string>
{
#if NET48
private Dictionary<string, string> _keyValuePairs = new Dictionary<string, string>();
#else
private Dictionary<string, string> _keyValuePairs = new();
#endif
public string this[string key]
{
get
{
#if NET48
if (_keyValuePairs == null)
_keyValuePairs = new Dictionary<string, string>();
#else
_keyValuePairs ??= new Dictionary<string, string>();
#endif
key = key.ToLowerInvariant();
if (_keyValuePairs.ContainsKey(key))
@@ -25,8 +33,12 @@ namespace MPF.Core.Data
}
set
{
#if NET48
if (_keyValuePairs == null)
_keyValuePairs = new Dictionary<string, string>();
#else
_keyValuePairs ??= new Dictionary<string, string>();
#endif
key = key.ToLowerInvariant();
_keyValuePairs[key] = value;
@@ -99,7 +111,7 @@ namespace MPF.Core.Data
// Keys are case-insensitive by default
try
{
using (StreamReader sr = new StreamReader(stream))
using (var sr = new StreamReader(stream))
{
string section = string.Empty;
while (!sr.EndOfStream)
@@ -125,7 +137,11 @@ namespace MPF.Core.Data
}
// Valid INI lines are in the format key=value
#if NET48
else if (line.Contains("="))
#else
else if (line.Contains('='))
#endif
{
// Split the line by '=' for key-value pairs
string[] data = line.Split('=');
@@ -185,7 +201,7 @@ namespace MPF.Core.Data
try
{
using (StreamWriter sw = new StreamWriter(stream))
using (var sw = new StreamWriter(stream))
{
// Order the dictionary by keys to link sections together
var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);

View File

@@ -4,11 +4,7 @@ using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
#if NET48
public class Options
#else
public class Options
#endif
{
/// <summary>
/// All settings in the form of a dictionary
@@ -19,6 +15,15 @@ namespace MPF.Core.Data
public Dictionary<string, string?> Settings { get; private set; }
#endif
/// <summary>
/// Indicate if the program is being run with a clean configuration
/// </summary>
public bool FirstRun
{
get { return GetBooleanSetting(Settings, "FirstRun", true); }
set { Settings["FirstRun"] = value.ToString(); }
}
#region Internal Program
/// <summary>
@@ -340,6 +345,24 @@ namespace MPF.Core.Data
set { Settings["RedumperEnableVerbose"] = value.ToString(); }
}
/// <summary>
/// Enable BE reading by default with Redumper
/// </summary>
public bool RedumperUseBEReading
{
get { return GetBooleanSetting(Settings, "RedumperUseBEReading", false); }
set { Settings["RedumperUseBEReading"] = value.ToString(); }
}
/// <summary>
/// Enable generic drive type by default with Redumper
/// </summary>
public bool RedumperUseGenericDriveType
{
get { return GetBooleanSetting(Settings, "RedumperUseGenericDriveType", false); }
set { Settings["RedumperUseGenericDriveType"] = value.ToString(); }
}
/// <summary>
/// Default number of rereads
/// </summary>
@@ -452,6 +475,15 @@ namespace MPF.Core.Data
set { Settings["ToolsInSeparateWindow"] = value.ToString(); }
}
/// <summary>
/// Add the dump filename as a suffix to the auto-generated files
/// </summary>
public bool AddFilenameSuffix
{
get { return GetBooleanSetting(Settings, "AddFilenameSuffix", false); }
set { Settings["AddFilenameSuffix"] = value.ToString(); }
}
/// <summary>
/// Output the compressed JSON version of the submission info
/// </summary>
@@ -479,6 +511,15 @@ namespace MPF.Core.Data
set { Settings["CompressLogFiles"] = value.ToString(); }
}
/// <summary>
/// Delete unnecessary files to reduce space
/// </summary>
public bool DeleteUnnecessaryFiles
{
get { return GetBooleanSetting(Settings, "DeleteUnnecessaryFiles", false); }
set { Settings["DeleteUnnecessaryFiles"] = value.ToString(); }
}
#endregion
#region Skip Options
@@ -610,12 +651,16 @@ namespace MPF.Core.Data
/// Constructor taking an existing Options object
/// </summary>
/// <param name="source"></param>
#if NET48
public Options(Options source)
#else
public Options(Options? source)
#endif
{
#if NET48
Settings = new Dictionary<string, string>(source.Settings);
Settings = new Dictionary<string, string>(source?.Settings ?? new Dictionary<string, string>());
#else
Settings = new Dictionary<string, string?>(source.Settings);
Settings = new Dictionary<string, string?>(source?.Settings ?? new Dictionary<string, string?>());
#endif
}
@@ -642,9 +687,9 @@ namespace MPF.Core.Data
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
#if NET48
private bool GetBooleanSetting(Dictionary<string, string> settings, string key, bool defaultValue)
private static bool GetBooleanSetting(Dictionary<string, string> settings, string key, bool defaultValue)
#else
private bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
private static bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
#endif
{
if (settings.ContainsKey(key))
@@ -668,9 +713,9 @@ namespace MPF.Core.Data
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
#if NET48
private int GetInt32Setting(Dictionary<string, string> settings, string key, int defaultValue)
private static int GetInt32Setting(Dictionary<string, string> settings, string key, int defaultValue)
#else
private int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
private static int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
#endif
{
if (settings.ContainsKey(key))
@@ -694,9 +739,9 @@ namespace MPF.Core.Data
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
#if NET48
private string GetStringSetting(Dictionary<string, string> settings, string key, string defaultValue)
private static string GetStringSetting(Dictionary<string, string> settings, string key, string defaultValue)
#else
private string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
private static string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
#endif
{
if (settings.ContainsKey(key))

View File

@@ -1,200 +0,0 @@
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
/// <summary>
/// Contains information specific to an XGD disc
/// </summary>
public class XgdInfo
{
#region Fields
/// <summary>
/// Indicates whether the information in this object is fully instantiated or not
/// </summary>
public bool Initialized { get; private set; }
/// <summary>
/// Raw XMID/XeMID string that all other information is derived from
/// </summary>
#if NET48
public string RawXMID { get; private set; }
#else
public string? RawXMID { get; private set; }
#endif
/// <summary>
/// XGD1 XMID
/// </summary>
#if NET48
public SabreTools.Models.Xbox.XMID XMID { get; private set; }
#else
public SabreTools.Models.Xbox.XMID? XMID { get; private set; }
#endif
/// <summary>
/// XGD2/3 XeMID
/// </summary>
#if NET48
public SabreTools.Models.Xbox.XeMID XeMID { get; private set; }
#else
public SabreTools.Models.Xbox.XeMID? XeMID { get; private set; }
#endif
#endregion
/// <summary>
/// Populate a set of XGD information from a Master ID (XMID/XeMID) string
/// </summary>
/// <param name="xmid">XMID/XeMID string representing the DMI information</param>
public XgdInfo(string xmid)
{
this.Initialized = false;
if (string.IsNullOrWhiteSpace(xmid))
return;
this.RawXMID = xmid.TrimEnd('\0');
if (string.IsNullOrWhiteSpace(this.RawXMID))
return;
// XGD1 information is 8 characters
if (this.RawXMID.Length == 8)
this.Initialized = ParseXGD1XMID(this.RawXMID);
// XGD2/3 information is semi-variable length
else if (this.RawXMID.Length == 13 || this.RawXMID.Length == 14 || this.RawXMID.Length == 21 || this.RawXMID.Length == 22)
this.Initialized = ParseXGD23XeMID(this.RawXMID);
}
/// <summary>
/// Get the human-readable serial string
/// </summary>
/// <returns>Formatted serial string, null on error</returns>
#if NET48
public string GetSerial()
#else
public string? GetSerial()
#endif
{
if (!this.Initialized)
return null;
try
{
// XGD1 doesn't use PlatformIdentifier
if (XMID != null)
return $"{XMID.PublisherIdentifier}-{XMID.GameID}";
// XGD2/3 uses a specific identifier
else if (XeMID?.PlatformIdentifier == '2')
return $"{XeMID.PublisherIdentifier}-{XeMID.PlatformIdentifier}{XeMID.GameID}";
return null;
}
catch
{
return null;
}
}
/// <summary>
/// Get the human-readable version string
/// </summary>
/// <returns>Formatted version string, null on error</returns>
/// <remarks>This may differ for XGD2/3 in the future</remarks>
#if NET48
public string GetVersion()
#else
public string? GetVersion()
#endif
{
if (!this.Initialized)
return null;
try
{
// XGD1 doesn't use PlatformIdentifier
if (XMID != null)
return $"1.{XMID.VersionNumber}";
// XGD2/3 uses a specific identifier
else if (XeMID?.PlatformIdentifier == '2')
return $"1.{XeMID.SKU}";
return null;
}
catch
{
return null;
}
}
/// <summary>
/// Parse an XGD1 XMID string
/// </summary>
/// <param name="rawXmid">XMID string to attempt to parse</param>
/// <returns>True if the XMID could be parsed, false otherwise</returns>
private bool ParseXGD1XMID(string rawXmid)
{
try
{
var xmid = new SabreTools.Serialization.Files.XMID().Deserialize(rawXmid);
if (xmid == null)
return false;
this.XMID = xmid;
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Parse an XGD2/3 XeMID string
/// </summary>
/// <param name="rawXemid">XeMID string to attempt to parse</param>
/// <returns>True if the XeMID could be parsed, false otherwise</returns>
private bool ParseXGD23XeMID(string rawXemid)
{
try
{
var xemid = new SabreTools.Serialization.Files.XeMID().Deserialize(rawXemid);
if (xemid == null)
return false;
this.XeMID = xemid;
return true;
}
catch
{
return false;
}
}
#region Helpers
/// <summary>
/// Determine the region based on the XGD serial character
/// </summary>
/// <param name="region">Character denoting the region</param>
/// <returns>Region, if possible</returns>
public static Region? GetRegion(char? region)
{
switch (region)
{
case 'W': return Region.World;
case 'A': return Region.UnitedStatesOfAmerica;
case 'J': return Region.JapanAsia;
case 'E': return Region.Europe;
case 'K': return Region.USAJapan;
case 'L': return Region.USAEurope;
case 'H': return Region.JapanEurope;
default: return null;
}
}
#endregion
}
}

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using BurnOutSharp;
using BinaryObjectScanner;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.Core.Modules;
@@ -65,7 +65,7 @@ namespace MPF.Core
public BaseParameters? Parameters { get; private set; }
#endif
#endregion
#endregion
#region Event Handlers
@@ -123,19 +123,23 @@ namespace MPF.Core
RedumpSystem? system,
MediaType? type,
InternalProgram? internalProgram,
#if NET48
string parameters)
#else
string? parameters)
#endif
{
// Set options object
this.Options = options;
Options = options;
// Output paths
this.OutputPath = InfoTool.NormalizeOutputPaths(outputPath, true);
OutputPath = InfoTool.NormalizeOutputPaths(outputPath, false);
// UI information
this.Drive = drive;
this.System = system ?? options.DefaultSystem;
this.Type = type ?? MediaType.NONE;
this.InternalProgram = internalProgram ?? options.InternalProgram;
Drive = drive;
System = system ?? options.DefaultSystem;
Type = type ?? MediaType.NONE;
InternalProgram = internalProgram ?? options.InternalProgram;
// Dumping program
SetParameters(parameters);
@@ -143,98 +147,73 @@ namespace MPF.Core
#region Public Functionality
/// <summary>
/// Adjust output paths if we're using DiscImageCreator
/// </summary>
public void AdjustPathsForDiscImageCreator()
{
// Only DiscImageCreator has issues with paths
if (this.Parameters?.InternalProgram != InternalProgram.DiscImageCreator)
return;
try
{
// Normalize the output path
string outputPath = InfoTool.NormalizeOutputPaths(this.OutputPath, true);
// Replace all instances in the output directory
var outputDirectory = Path.GetDirectoryName(outputPath);
outputDirectory = outputDirectory?.Replace(".", "_");
// Replace all instances in the output filename
string outputFilename = Path.GetFileNameWithoutExtension(outputPath);
outputFilename = outputFilename.Replace(".", "_");
// Get the extension for recreating the path
string outputExtension = Path.GetExtension(outputPath).TrimStart('.');
// Rebuild the output path
if (string.IsNullOrWhiteSpace(outputDirectory))
{
if (string.IsNullOrWhiteSpace(outputExtension))
this.OutputPath = outputFilename;
else
this.OutputPath = $"{outputFilename}.{outputExtension}";
}
else
{
if (string.IsNullOrWhiteSpace(outputExtension))
this.OutputPath = Path.Combine(outputDirectory, outputFilename);
else
this.OutputPath = Path.Combine(outputDirectory, $"{outputFilename}.{outputExtension}");
}
// Assign the path to the filename as well for dumping
((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = this.OutputPath;
}
catch { }
}
/// <summary>
/// Set the parameters object based on the internal program and parameters string
/// </summary>
/// <param name="parameters">String representation of the parameters</param>
#if NET48
public void SetParameters(string parameters)
#else
public void SetParameters(string? parameters)
#endif
{
switch (this.InternalProgram)
#if NET48
switch (InternalProgram)
{
// Dumping support
case InternalProgram.Aaru:
this.Parameters = new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath };
Parameters = new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath };
break;
case InternalProgram.DiscImageCreator:
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
break;
case InternalProgram.Redumper:
this.Parameters = new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath };
Parameters = new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath };
break;
// Verification support only
case InternalProgram.CleanRip:
this.Parameters = new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null };
Parameters = new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null };
break;
case InternalProgram.DCDumper:
this.Parameters = null; // TODO: Create correct parameter type when supported
Parameters = null; // TODO: Create correct parameter type when supported
break;
case InternalProgram.UmdImageCreator:
this.Parameters = new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null };
Parameters = new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null };
break;
// This should never happen, but it needs a fallback
default:
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
break;
}
#else
Parameters = InternalProgram switch
{
// Dumping support
InternalProgram.Aaru => new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath },
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath },
InternalProgram.Redumper => new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath },
// Verification support only
InternalProgram.CleanRip => new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null },
InternalProgram.DCDumper => null, // TODO: Create correct parameter type when supported
InternalProgram.UmdImageCreator => new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null },
// This should never happen, but it needs a fallback
_ => new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath },
};
#endif
// Set system and type
if (this.Parameters != null)
if (Parameters != null)
{
this.Parameters.System = this.System;
this.Parameters.Type = this.Type;
Parameters.System = System;
Parameters.Type = Type;
}
}
@@ -257,25 +236,37 @@ namespace MPF.Core
return null;
// Set the proper parameters
switch (this.InternalProgram)
#if NET48
switch (InternalProgram)
{
case InternalProgram.Aaru:
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options);
break;
case InternalProgram.DiscImageCreator:
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options);
break;
case InternalProgram.Redumper:
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options);
break;
// This should never happen, but it needs a fallback
default:
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options);
break;
}
#else
Parameters = InternalProgram switch
{
InternalProgram.Aaru => new Modules.Aaru.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
InternalProgram.Redumper => new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
// This should never happen, but it needs a fallback
_ => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
};
#endif
// Generate and return the param string
return Parameters.GenerateParameters();
@@ -301,7 +292,7 @@ namespace MPF.Core
#else
public async Task<string?> EjectDisc() =>
#endif
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject);
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject);
/// <summary>
/// Reset the current drive using DiscImageCreator
@@ -324,7 +315,7 @@ namespace MPF.Core
#endif
{
// If we don't have parameters
if (this.Parameters == null)
if (Parameters == null)
return Result.Failure("Error! Current configuration is not supported!");
// Check that we have the basics for dumping
@@ -341,19 +332,14 @@ namespace MPF.Core
}
// Execute internal tool
progress?.Report(Result.Success($"Executing {this.InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
progress?.Report(Result.Success($"Executing {InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
var directoryName = Path.GetDirectoryName(this.OutputPath);
var directoryName = Path.GetDirectoryName(OutputPath);
if (!string.IsNullOrWhiteSpace(directoryName))
Directory.CreateDirectory(directoryName);
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
progress?.Report(Result.Success($"{this.InternalProgram} has finished!"));
// Execute additional tools
progress?.Report(Result.Success("Running any additional tools... see log for output!"));
result = await Task.Run(() => ExecuteAdditionalTools());
progress?.Report(result);
progress?.Report(Result.Success($"{InternalProgram} has finished!"));
// Remove event handler if needed
if (!Options.ToolsInSeparateWindow)
@@ -371,26 +357,29 @@ namespace MPF.Core
/// <param name="resultProgress">Optional result progress callback</param>
/// <param name="protectionProgress">Optional protection progress callback</param>
/// <param name="processUserInfo">Optional user prompt to deal with submission information</param>
/// <param name="seedInfo">A seed SubmissionInfo object that contains user data</param>
/// <returns>Result instance with the outcome</returns>
public async Task<Result> VerifyAndSaveDumpOutput(
#if NET48
IProgress<Result> resultProgress = null,
IProgress<ProtectionProgress> protectionProgress = null,
Func<SubmissionInfo, (bool?, SubmissionInfo)> processUserInfo = null)
Func<SubmissionInfo, (bool?, SubmissionInfo)> processUserInfo = null,
SubmissionInfo seedInfo = null)
#else
IProgress<Result>? resultProgress = null,
IProgress<ProtectionProgress>? protectionProgress = null,
Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? processUserInfo = null)
Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? processUserInfo = null,
SubmissionInfo? seedInfo = null)
#endif
{
resultProgress?.Report(Result.Success("Gathering submission information... please wait!"));
// Get the output directory and filename separately
var outputDirectory = Path.GetDirectoryName(this.OutputPath);
var outputFilename = Path.GetFileName(this.OutputPath);
var outputDirectory = Path.GetDirectoryName(OutputPath);
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, this.Parameters, false);
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, Parameters, false);
if (!foundFiles)
{
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}"));
@@ -399,28 +388,36 @@ namespace MPF.Core
// Extract the information from the output files
resultProgress?.Report(Result.Success("Extracting output information from output files..."));
var submissionInfo = await InfoTool.ExtractOutputInformation(
this.OutputPath,
this.Drive,
this.System,
this.Type,
this.Options,
this.Parameters,
var submissionInfo = await SubmissionInfoTool.ExtractOutputInformation(
OutputPath,
Drive,
System,
Type,
Options,
Parameters,
resultProgress,
protectionProgress);
resultProgress?.Report(Result.Success("Extracting information complete!"));
// Inject seed submission info data, if necessary
if (seedInfo != null)
{
resultProgress?.Report(Result.Success("Injecting user-supplied information..."));
SubmissionInfoTool.InjectSubmissionInformation(submissionInfo, seedInfo);
resultProgress?.Report(Result.Success("Information injection complete!"));
}
// Eject the disc automatically if configured to
if (Options.EjectAfterDump == true)
{
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive?.Letter}"));
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive?.Name}"));
await EjectDisc();
}
// Reset the drive automatically if configured to
if (this.InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
if (InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
{
resultProgress?.Report(Result.Success($"Resetting drive {Drive?.Letter}"));
resultProgress?.Report(Result.Success($"Resetting drive {Drive?.Name}"));
await ResetDrive();
}
@@ -445,15 +442,18 @@ namespace MPF.Core
// Format the information for the text output
resultProgress?.Report(Result.Success("Formatting information..."));
(var formattedValues, var formatResult) = InfoTool.FormatOutputData(submissionInfo, this.Options);
(var formattedValues, var formatResult) = InfoTool.FormatOutputData(submissionInfo, Options);
if (formattedValues == null)
resultProgress?.Report(Result.Success(formatResult));
else
resultProgress?.Report(Result.Failure(formatResult));
// Get the filename suffix for auto-generated files
var filenameSuffix = Options.AddFilenameSuffix ? Path.GetFileNameWithoutExtension(outputFilename) : null;
// Write the text output
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, formattedValues);
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, formattedValues);
if (txtSuccess)
resultProgress?.Report(Result.Success(txtResult));
else
@@ -463,7 +463,7 @@ namespace MPF.Core
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
{
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, submissionInfo);
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo);
if (scanSuccess)
resultProgress?.Report(Result.Success("Writing complete!"));
else
@@ -474,7 +474,7 @@ namespace MPF.Core
if (Options.OutputSubmissionJSON)
{
resultProgress?.Report(Result.Success($"Writing information to !submissionInfo.json{(Options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, submissionInfo, Options.IncludeArtifacts);
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, submissionInfo, Options.IncludeArtifacts);
if (jsonSuccess)
resultProgress?.Report(Result.Success("Writing complete!"));
else
@@ -485,13 +485,24 @@ namespace MPF.Core
if (Options.CompressLogFiles)
{
resultProgress?.Report(Result.Success("Compressing log files..."));
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, outputFilename, this.Parameters);
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename, Parameters);
if (compressSuccess)
resultProgress?.Report(Result.Success(compressResult));
else
resultProgress?.Report(Result.Failure(compressResult));
}
// Delete unnecessary files, if required
if (Options.DeleteUnnecessaryFiles)
{
resultProgress?.Report(Result.Success("Deleting unnecessary files..."));
(bool deleteSuccess, string deleteResult) = InfoTool.DeleteUnnecessaryFiles(outputDirectory, outputFilename, Parameters);
if (deleteSuccess)
resultProgress?.Report(Result.Success(deleteResult));
else
resultProgress?.Report(Result.Failure(deleteResult));
}
resultProgress?.Report(Result.Success("Submission information process complete!"));
return Result.Success();
}
@@ -516,18 +527,12 @@ namespace MPF.Core
return parametersValid && floppyValid && removableDiskValid;
}
/// <summary>
/// Run any additional tools given a DumpEnvironment
/// </summary>
/// <returns>Result instance with the outcome</returns>
private Result ExecuteAdditionalTools() => Result.Success("No external tools needed!");
/// <summary>
/// Run internal program async with an input set of parameters
/// </summary>
/// <param name="parameters"></param>
/// <returns>Standard output from commandline window</returns>
private async Task<string> ExecuteInternalProgram(BaseParameters parameters)
private static async Task<string> ExecuteInternalProgram(BaseParameters parameters)
{
Process childProcess;
string output = await Task.Run(() =>
@@ -566,14 +571,14 @@ namespace MPF.Core
private Result IsValidForDump()
{
// Validate that everything is good
if (this.Parameters == null || !ParametersValid())
if (Parameters == null || !ParametersValid())
return Result.Failure("Error! Current configuration is not supported!");
// Fix the output paths, just in case
this.OutputPath = InfoTool.NormalizeOutputPaths(this.OutputPath, true);
OutputPath = InfoTool.NormalizeOutputPaths(OutputPath, false);
// Validate that the output path isn't on the dumping drive
if (Drive != null && this.OutputPath[0] == Drive.Letter)
if (Drive?.Name != null && OutputPath.StartsWith(Drive.Name))
return Result.Failure("Error! Cannot output to same drive that is being dumped!");
// Validate that the required program exists
@@ -582,7 +587,7 @@ namespace MPF.Core
// Validate that the dumping drive doesn't contain the executable
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath);
if (Drive != null && fullExecutablePath[0] == Drive.Letter)
if (Drive?.Name != null && fullExecutablePath.StartsWith(Drive.Name))
return Result.Failure("Error! Cannot dump same drive that executable resides on!");
// Validate that the current configuration is supported
@@ -630,13 +635,13 @@ namespace MPF.Core
var parameters = new Modules.DiscImageCreator.Parameters(string.Empty)
{
BaseCommand = command,
DriveLetter = Drive.Letter.ToString(),
DrivePath = Drive.Name,
ExecutablePath = Options.DiscImageCreatorPath,
};
return await ExecuteInternalProgram(parameters);
}
#endregion
#endregion
}
}

View File

@@ -89,7 +89,8 @@ namespace MPF.Core.Hashing
switch (HashType)
{
case Hash.CRC32:
(_hasher as NonCryptographicHashAlgorithm)?.Append(buffer);
var bufferSpan = new ReadOnlySpan<byte>(buffer, 0, size);
(_hasher as NonCryptographicHashAlgorithm)?.Append(bufferSpan);
break;
case Hash.MD5:
@@ -107,11 +108,11 @@ namespace MPF.Core.Hashing
/// </summary>
public void Terminate()
{
byte[] emptyBuffer = new byte[0];
byte[] emptyBuffer = Array.Empty<byte>();
switch (HashType)
{
case Hash.CRC32:
(_hasher as NonCryptographicHashAlgorithm)?.Append(emptyBuffer);
// No finalization is needed
break;
case Hash.MD5:
@@ -139,7 +140,7 @@ namespace MPF.Core.Hashing
switch (HashType)
{
case Hash.CRC32:
return (_hasher as NonCryptographicHashAlgorithm)?.GetCurrentHash();
return (_hasher as NonCryptographicHashAlgorithm)?.GetCurrentHash()?.Reverse().ToArray();
case Hash.MD5:
case Hash.SHA1:

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<VersionPrefix>2.7.0</VersionPrefix>
<VersionPrefix>2.7.3</VersionPrefix>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
@@ -17,14 +17,15 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.9.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0-preview.2" />
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0-preview.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.Models" Version="1.1.4" />
<PackageReference Include="psxt001z" Version="0.21.0-beta1" />
<PackageReference Include="SabreTools.Models" Version="1.1.5" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="SabreTools.Serialization" Version="1.1.6" />
<PackageReference Include="SabreTools.Serialization" Version="1.1.7" />
<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="7.0.0" />

View File

@@ -13,8 +13,9 @@ using SabreTools.Models.CueSheets;
using SabreTools.RedumpLib.Data;
using Schemas;
// Ignore "Type or member is obsolete"
#pragma warning disable CS0618
#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
{
@@ -256,11 +257,19 @@ namespace MPF.Core.Modules.Aaru
#endregion
/// <inheritdoc/>
#if NET48
public Parameters(string parameters) : base(parameters) { }
#else
public Parameters(string? parameters) : base(parameters) { }
#endif
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
#if NET48
public Parameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
#else
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
#endif
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
@@ -269,7 +278,7 @@ namespace MPF.Core.Modules.Aaru
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
List<string> missingFiles = new List<string>();
var missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.CDROM:
@@ -328,10 +337,16 @@ namespace MPF.Core.Modules.Aaru
// TODO: Fill in submission info specifics for Aaru
var outputDirectory = Path.GetDirectoryName(basePath);
// Ensure that required sections exist
info = SubmissionInfoTool.EnsureAllSections(info);
// TODO: Determine if there's an Aaru version anywhere
if (info.DumpingInfo == null) info.DumpingInfo = new DumpingInfoSection();
#if NET48
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + ".cicm.xml")?.ToString("yyyy-MM-dd HH:mm:ss");
#else
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
#endif
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + ".cicm.xml")?.ToString("yyyy-MM-dd HH:mm:ss");
// Deserialize the sidecar, if possible
var sidecar = GenerateSidecar(basePath + ".cicm.xml");
@@ -362,8 +377,11 @@ namespace MPF.Core.Modules.Aaru
var datafile = GenerateDatafile(sidecar, basePath);
// Fill in the hash data
if (info.TracksAndWriteOffsets == null) info.TracksAndWriteOffsets = new TracksAndWriteOffsetsSection();
info.TracksAndWriteOffsets.ClrMameProData = GenerateDatfile(datafile);
#if NET48
info.TracksAndWriteOffsets.ClrMameProData = InfoTool.GenerateDatfile(datafile);
#else
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
#endif
switch (this.Type)
{
@@ -377,8 +395,11 @@ namespace MPF.Core.Modules.Aaru
if (File.Exists(basePath + ".resume.xml"))
errorCount = GetErrorCount(basePath + ".resume.xml");
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
info.CommonDiscInfo.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString());
#else
info.CommonDiscInfo!.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString());
#endif
info.TracksAndWriteOffsets.Cuesheet = GenerateCuesheet(sidecar, basePath) ?? string.Empty;
@@ -390,12 +411,15 @@ namespace MPF.Core.Modules.Aaru
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection();
// Get the individual hash data, as per internal
if (GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
{
#if NET48
info.SizeAndChecksums.Size = size;
#else
info.SizeAndChecksums!.CRC32 = crc32;
#endif
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
@@ -414,7 +438,11 @@ namespace MPF.Core.Modules.Aaru
if (this.Type == MediaType.DVD)
layerbreak = GetLayerbreak(sidecar) ?? string.Empty;
else if (this.Type == MediaType.BluRay)
#if NET48
layerbreak = info.SizeAndChecksums.Size > 25_025_314_816 ? "25025314816" : null;
#else
layerbreak = info.SizeAndChecksums!.Size > 25_025_314_816 ? "25025314816" : null;
#endif
// If we have a single-layer disc
if (string.IsNullOrWhiteSpace(layerbreak))
@@ -424,7 +452,11 @@ namespace MPF.Core.Modules.Aaru
// If we have a dual-layer disc
else
{
#if NET48
info.SizeAndChecksums.Layerbreak = Int64.Parse(layerbreak);
#else
info.SizeAndChecksums!.Layerbreak = Int64.Parse(layerbreak);
#endif
}
// TODO: Investigate XGD disc outputs
@@ -444,52 +476,60 @@ namespace MPF.Core.Modules.Aaru
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection();
#if NET48
info.CopyProtection.Protection = GetDVDProtection(sidecar) ?? string.Empty;
#else
info.CopyProtection!.Protection = GetDVDProtection(sidecar) ?? string.Empty;
#endif
break;
case RedumpSystem.KonamiPython2:
if (GetPlayStationExecutableInfo(drive?.Letter, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
#else
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
#endif
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
}
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
#if NET48
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
#else
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
#endif
break;
case RedumpSystem.MicrosoftXbox:
if (GetXgdAuxInfo(sidecar, out var xgd1DMIHash, out var xgd1PFIHash, out var xgd1SSHash, out var ss, out var xgd1SSVer))
{
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd1DMIHash ?? string.Empty;
#else
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = xgd1DMIHash ?? string.Empty;
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd1SSVer ?? string.Empty;
if (info.Extras == null) info.Extras = new ExtrasSection();
#if NET48
info.Extras.SecuritySectorRanges = ss ?? string.Empty;
#else
info.Extras!.SecuritySectorRanges = ss ?? string.Empty;
#endif
}
if (GetXboxDMIInfo(sidecar, out var serial, out var version, out Region? region))
{
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
info.CommonDiscInfo.Serial = serial ?? string.Empty;
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = version ?? string.Empty;
#else
info.CommonDiscInfo!.Serial = serial ?? string.Empty;
info.VersionAndEditions!.Version = version ?? string.Empty;
#endif
info.CommonDiscInfo.Region = region;
}
@@ -498,41 +538,43 @@ namespace MPF.Core.Modules.Aaru
case RedumpSystem.MicrosoftXbox360:
if (GetXgdAuxInfo(sidecar, out var xgd23DMIHash, out var xgd23PFIHash, out var xgd23SSHash, out var ss360, out var xgd23SSVer))
{
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd23DMIHash ?? string.Empty;
#else
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = xgd23DMIHash ?? string.Empty;
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd23SSVer ?? string.Empty;
if (info.Extras == null) info.Extras = new ExtrasSection();
#if NET48
info.Extras.SecuritySectorRanges = ss360 ?? string.Empty;
#else
info.Extras!.SecuritySectorRanges = ss360 ?? string.Empty;
#endif
}
if (GetXbox360DMIInfo(sidecar, out var serial360, out var version360, out Region? region360))
{
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
info.CommonDiscInfo.Serial = serial360 ?? string.Empty;
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = version360 ?? string.Empty;
#else
info.CommonDiscInfo!.Serial = serial360 ?? string.Empty;
info.VersionAndEditions!.Version = version360 ?? string.Empty;
#endif
info.CommonDiscInfo.Region = region360;
}
break;
case RedumpSystem.SonyPlayStation:
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
#else
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
#endif
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
}
@@ -540,65 +582,64 @@ namespace MPF.Core.Modules.Aaru
break;
case RedumpSystem.SonyPlayStation2:
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
#else
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
#endif
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
}
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
#if NET48
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
#else
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
#endif
break;
case RedumpSystem.SonyPlayStation3:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
info.VersionAndEditions.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation4:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
info.VersionAndEditions.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
info.VersionAndEditions!.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation5:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
info.VersionAndEditions.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
info.VersionAndEditions!.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
break;
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
#if NET48
if (info.Artifacts == null) info.Artifacts = new Dictionary<string, string>();
#else
info.Artifacts ??= new Dictionary<string, string>();
#endif
if (File.Exists(basePath + ".cicm.xml"))
info.Artifacts["cicm"] = GetBase64(GetFullFile(basePath + ".cicm.xml")) ?? string.Empty;
if (File.Exists(basePath + ".ibg"))
@@ -621,7 +662,7 @@ namespace MPF.Core.Modules.Aaru
public override string? GenerateParameters()
#endif
{
List<string> parameters = new List<string>();
var parameters = new List<string>();
#region Pre-command flags
@@ -647,8 +688,12 @@ namespace MPF.Core.Modules.Aaru
#endregion
#if NET48
if (BaseCommand == null)
BaseCommand = CommandStrings.NONE;
#else
BaseCommand ??= CommandStrings.NONE;
#endif
if (!string.IsNullOrWhiteSpace(BaseCommand))
parameters.Add(BaseCommand);
@@ -1637,7 +1682,7 @@ namespace MPF.Core.Modules.Aaru
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
List<string> logFiles = new List<string>();
var logFiles = new List<string>();
switch (this.Type)
{
case MediaType.CDROM:
@@ -1737,11 +1782,15 @@ namespace MPF.Core.Modules.Aaru
}
/// <inheritdoc/>
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
#if NET48
protected override void SetDefaultParameters(string drivePath, string filename, int? driveSpeed, Options options)
#else
protected override void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Options options)
#endif
{
BaseCommand = $"{CommandStrings.MediaPrefixLong} {CommandStrings.MediaDump}";
InputValue = $"{driveLetter}:";
InputValue = drivePath;
OutputValue = filename;
if (driveSpeed != null)
@@ -1814,7 +1863,11 @@ namespace MPF.Core.Modules.Aaru
}
/// <inheritdoc/>
#if NET48
protected override bool ValidateAndSetParameters(string parameters)
#else
protected override bool ValidateAndSetParameters(string? parameters)
#endif
{
BaseCommand = CommandStrings.NONE;
@@ -2332,7 +2385,7 @@ namespace MPF.Core.Modules.Aaru
return true;
}
#endregion
#endregion
#region Private Extra Methods
@@ -2342,9 +2395,9 @@ namespace MPF.Core.Modules.Aaru
/// <param name="baseCommand">Command string to normalize</param>
/// <returns>Normalized command</returns>
#if NET48
private string NormalizeCommand(List<string> parts, ref int start)
private static string NormalizeCommand(List<string> parts, ref int start)
#else
private string? NormalizeCommand(List<string> parts, ref int start)
private static string? NormalizeCommand(List<string> parts, ref int start)
#endif
{
// Invalid start means invalid command
@@ -2375,9 +2428,9 @@ namespace MPF.Core.Modules.Aaru
/// <param name="baseCommand">Command string to normalize</param>
/// <returns>Normalized command</returns>
#if NET48
private string NormalizeCommand(string baseCommand)
private static string NormalizeCommand(string baseCommand)
#else
private string? NormalizeCommand(string baseCommand)
private static string? NormalizeCommand(string baseCommand)
#endif
{
// If the base command is inavlid, just return nulls
@@ -2402,6 +2455,7 @@ namespace MPF.Core.Modules.Aaru
case CommandStrings.ArchivePrefixShort:
case CommandStrings.ArchivePrefixLong:
family = CommandStrings.ArchivePrefixLong;
#if NET48
switch (splitCommand[1])
{
case CommandStrings.ArchiveInfo:
@@ -2411,11 +2465,19 @@ namespace MPF.Core.Modules.Aaru
command = null;
break;
}
#else
command = splitCommand[1] switch
{
CommandStrings.ArchiveInfo => CommandStrings.ArchiveInfo,
_ => null,
};
#endif
break;
case CommandStrings.DatabasePrefixShort:
case CommandStrings.DatabasePrefixLong:
family = CommandStrings.DatabasePrefixLong;
#if NET48
switch (splitCommand[1])
{
case CommandStrings.DatabaseStats:
@@ -2428,12 +2490,21 @@ namespace MPF.Core.Modules.Aaru
command = null;
break;
}
#else
command = splitCommand[1] switch
{
CommandStrings.DatabaseStats => CommandStrings.DatabaseStats,
CommandStrings.DatabaseUpdate => CommandStrings.DatabaseUpdate,
_ => null,
};
#endif
break;
case CommandStrings.DevicePrefixShort:
case CommandStrings.DevicePrefixLong:
family = CommandStrings.DevicePrefixLong;
#if NET48
switch (splitCommand[1])
{
case CommandStrings.DeviceInfo:
@@ -2449,6 +2520,15 @@ namespace MPF.Core.Modules.Aaru
command = null;
break;
}
#else
command = splitCommand[1] switch
{
CommandStrings.DeviceInfo => CommandStrings.DeviceInfo,
CommandStrings.DeviceList => CommandStrings.DeviceList,
CommandStrings.DeviceReport => CommandStrings.DeviceReport,
_ => null,
};
#endif
break;
@@ -2456,6 +2536,7 @@ namespace MPF.Core.Modules.Aaru
case CommandStrings.FilesystemPrefixShortAlt:
case CommandStrings.FilesystemPrefixLong:
family = CommandStrings.FilesystemPrefixLong;
#if NET48
switch (splitCommand[1])
{
case CommandStrings.FilesystemExtract:
@@ -2475,12 +2556,24 @@ namespace MPF.Core.Modules.Aaru
command = null;
break;
}
#else
command = splitCommand[1] switch
{
CommandStrings.FilesystemExtract => CommandStrings.FilesystemExtract,
CommandStrings.FilesystemInfo => CommandStrings.FilesystemInfo,
CommandStrings.FilesystemListShort => CommandStrings.FilesystemListLong,
CommandStrings.FilesystemListLong => CommandStrings.FilesystemListLong,
CommandStrings.FilesystemOptions => CommandStrings.FilesystemOptions,
_ => null,
};
#endif
break;
case CommandStrings.ImagePrefixShort:
case CommandStrings.ImagePrefixLong:
family = CommandStrings.ImagePrefixLong;
#if NET48
switch (splitCommand[1])
{
case CommandStrings.ImageChecksumShort:
@@ -2519,12 +2612,31 @@ namespace MPF.Core.Modules.Aaru
command = null;
break;
}
#else
command = splitCommand[1] switch
{
CommandStrings.ImageChecksumShort => CommandStrings.ImageChecksumLong,
CommandStrings.ImageChecksumLong => CommandStrings.ImageChecksumLong,
CommandStrings.ImageCompareShort => CommandStrings.ImageCompareLong,
CommandStrings.ImageCompareLong => CommandStrings.ImageCompareLong,
CommandStrings.ImageConvert => CommandStrings.ImageConvert,
CommandStrings.ImageCreateSidecar => CommandStrings.ImageCreateSidecar,
CommandStrings.ImageDecode => CommandStrings.ImageDecode,
CommandStrings.ImageEntropy => CommandStrings.ImageEntropy,
CommandStrings.ImageInfo => CommandStrings.ImageInfo,
CommandStrings.ImageOptions => CommandStrings.ImageOptions,
CommandStrings.ImagePrint => CommandStrings.ImagePrint,
CommandStrings.ImageVerify => CommandStrings.ImageVerify,
_ => null,
};
#endif
break;
case CommandStrings.MediaPrefixShort:
case CommandStrings.MediaPrefixLong:
family = CommandStrings.MediaPrefixLong;
#if NET48
switch (splitCommand[1])
{
case CommandStrings.MediaDump:
@@ -2540,6 +2652,15 @@ namespace MPF.Core.Modules.Aaru
command = null;
break;
}
#else
command = splitCommand[1] switch
{
CommandStrings.MediaDump => CommandStrings.MediaDump,
CommandStrings.MediaInfo => CommandStrings.MediaInfo,
CommandStrings.MediaScan => CommandStrings.MediaScan,
_ => null,
};
#endif
break;
@@ -2554,6 +2675,7 @@ namespace MPF.Core.Modules.Aaru
else
{
family = null;
#if NET48
switch (splitCommand[0])
{
case CommandStrings.Configure:
@@ -2575,6 +2697,17 @@ namespace MPF.Core.Modules.Aaru
command = null;
break;
}
#else
command = splitCommand[0] switch
{
CommandStrings.Configure => CommandStrings.Configure,
CommandStrings.Formats => CommandStrings.Formats,
CommandStrings.ListEncodings => CommandStrings.ListEncodings,
CommandStrings.ListNamespaces => CommandStrings.ListNamespaces,
CommandStrings.Remote => CommandStrings.Remote,
_ => null,
};
#endif
}
// If the command itself is invalid, then return null
@@ -2598,7 +2731,7 @@ namespace MPF.Core.Modules.Aaru
/// <param name="trackType">TrackTypeTrackType to convert</param>
/// <param name="bytesPerSector">Sector size to help with specific subtypes</param>
/// <returns>CueTrackDataType representing the input data</returns>
private CueTrackDataType ConvertToDataType(TrackTypeTrackType trackType, uint bytesPerSector)
private static CueTrackDataType ConvertToDataType(TrackTypeTrackType trackType, uint bytesPerSector)
{
switch (trackType)
{
@@ -2629,7 +2762,7 @@ namespace MPF.Core.Modules.Aaru
/// </summary>
/// <param name="trackFlagsType">TrackFlagsType containing flag data</param>
/// <returns>CueTrackFlag representing the flags</returns>
private CueTrackFlag ConvertToTrackFlag(TrackFlagsType trackFlagsType)
private static CueTrackFlag ConvertToTrackFlag(TrackFlagsType trackFlagsType)
{
if (trackFlagsType == null)
return 0;
@@ -2655,9 +2788,9 @@ namespace MPF.Core.Modules.Aaru
/// <param name="basePath">Base path for determining file names</param>
/// <returns>String containing the cuesheet, null on error</returns>
#if NET48
private string GenerateCuesheet(CICMMetadataType cicmSidecar, string basePath)
private static string GenerateCuesheet(CICMMetadataType cicmSidecar, string basePath)
#else
private string? GenerateCuesheet(CICMMetadataType? cicmSidecar, string basePath)
private static string? GenerateCuesheet(CICMMetadataType? cicmSidecar, string basePath)
#endif
{
// If the object is null, we can't get information from it
@@ -2669,7 +2802,7 @@ namespace MPF.Core.Modules.Aaru
var cueFiles = new List<CueFile>();
var cueSheet = new CueSheet
{
Performer = string.Join(", ", cicmSidecar.Performer ?? new string[0]),
Performer = string.Join(", ", cicmSidecar.Performer ?? Array.Empty<string>()),
};
// Only care about OpticalDisc types
@@ -2694,7 +2827,7 @@ namespace MPF.Core.Modules.Aaru
foreach (TrackType track in opticalDisc.Track)
{
// Create cue track entry
CueTrack cueTrack = new CueTrack
var cueTrack = new CueTrack
{
Number = (int)(track.Sequence?.TrackNumber ?? 0),
DataType = ConvertToDataType(track.TrackType1, track.BytesPerSector),
@@ -2703,7 +2836,7 @@ namespace MPF.Core.Modules.Aaru
};
// Create cue file entry
CueFile cueFile = new CueFile
var cueFile = new CueFile
{
FileName = GenerateTrackName(basePath, (int)totalTracks, cueTrack.Number, opticalDisc.DiscType),
FileType = CueFileType.BINARY,
@@ -2922,8 +3055,8 @@ namespace MPF.Core.Modules.Aaru
return null;
// Required variables
Datafile datafile = new Datafile();
List<Rom> roms = new List<Rom>();
var datafile = new Datafile();
var roms = new List<Rom>();
// Process OpticalDisc, if possible
if (cicmSidecar.OpticalDisc != null && cicmSidecar.OpticalDisc.Length > 0)
@@ -3176,7 +3309,7 @@ namespace MPF.Core.Modules.Aaru
}
// Now generate the byte array data
List<byte> pvdData = new List<byte>();
var pvdData = new List<byte>();
pvdData.AddRange(new string(' ', 13).ToCharArray().Select(c => (byte)c));
pvdData.AddRange(GeneratePVDDateTimeBytes(creation));
pvdData.AddRange(GeneratePVDDateTimeBytes(modification));
@@ -3247,7 +3380,11 @@ namespace MPF.Core.Modules.Aaru
return null;
string pvdLine = $"{row} : ";
#if NET48
pvdLine += BitConverter.ToString(bytes.Slice(0, 8).ToArray()).Replace("-", " ");
#else
pvdLine += BitConverter.ToString(bytes[..8].ToArray()).Replace("-", " ");
#endif
pvdLine += " ";
pvdLine += BitConverter.ToString(bytes.Slice(8, 8).ToArray().ToArray()).Replace("-", " ");
pvdLine += " ";
@@ -3287,7 +3424,7 @@ namespace MPF.Core.Modules.Aaru
if (xtr == null)
return null;
XmlSerializer serializer = new XmlSerializer(typeof(CICMMetadataType));
var serializer = new XmlSerializer(typeof(CICMMetadataType));
return serializer.Deserialize(xtr) as CICMMetadataType;
}
@@ -3394,7 +3531,7 @@ namespace MPF.Core.Modules.Aaru
long? totalErrors = null;
// Parse the resume XML file
using (StreamReader sr = File.OpenText(resume))
using (var sr = File.OpenText(resume))
{
try
{

View File

@@ -2,17 +2,10 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Schema;
using System.Xml;
using System.Xml.Serialization;
using MPF.Core.Data;
using MPF.Core.Hashing;
using MPF.Core.Utilities;
using SabreTools.Models.PIC;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules
@@ -47,7 +40,11 @@ namespace MPF.Core.Modules
/// <summary>
/// Set of flags to pass to the executable
/// </summary>
#if NET48
protected Dictionary<string, bool?> flags = new Dictionary<string, bool?>();
#else
protected Dictionary<string, bool?> flags = new();
#endif
protected internal IEnumerable<string> Keys => flags.Keys;
/// <summary>
@@ -148,7 +145,11 @@ namespace MPF.Core.Modules
/// Populate a Parameters object from a param string
/// </summary>
/// <param name="parameters">String possibly representing a set of parameters</param>
#if NET48
public BaseParameters(string parameters)
#else
public BaseParameters(string? parameters)
#endif
{
// If any parameters are not valid, wipe out everything
if (!ValidateAndSetParameters(parameters))
@@ -160,15 +161,19 @@ namespace MPF.Core.Modules
/// </summary>
/// <param name="system">RedumpSystem value to use</param>
/// <param name="type">MediaType value to use</param>
/// <param name="driveLetter">Drive letter to use</param>
/// <param name="drivePath">Drive path to use</param>
/// <param name="filename">Filename to use</param>
/// <param name="driveSpeed">Drive speed to use</param>
/// <param name="options">Options object containing all settings that may be used for setting parameters</param>
public BaseParameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
#if NET48
public BaseParameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
#else
public BaseParameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
#endif
{
this.System = system;
this.Type = type;
SetDefaultParameters(driveLetter, filename, driveSpeed, options);
SetDefaultParameters(drivePath, filename, driveSpeed, options);
}
#region Abstract Methods
@@ -230,12 +235,27 @@ namespace MPF.Core.Modules
public virtual string? GetDefaultExtension(MediaType? mediaType) => null;
#endif
/// <summary>
/// Generate a list of all deleteable files generated
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <returns>List of all deleteable file paths, empty otherwise</returns>
#if NET48
public virtual List<string> GetDeleteableFilePaths(string basePath) => new List<string>();
#else
public virtual List<string> GetDeleteableFilePaths(string basePath) => new();
#endif
/// <summary>
/// Generate a list of all log files generated
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <returns>List of all log file paths, empty otherwise</returns>
#if NET48
public virtual List<string> GetLogFilePaths(string basePath) => new List<string>();
#else
public virtual List<string> GetLogFilePaths(string basePath) => new();
#endif
/// <summary>
/// Get the MediaType from the current set of parameters
@@ -279,18 +299,26 @@ namespace MPF.Core.Modules
/// <summary>
/// Set default parameters for a given system and media type
/// </summary>
/// <param name="driveLetter">Drive letter to use</param>
/// <param name="drivePath">Drive path to use</param>
/// <param name="filename">Filename to use</param>
/// <param name="driveSpeed">Drive speed to use</param>
/// <param name="options">Options object containing all settings that may be used for setting parameters</param>
protected virtual void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options) { }
#if NET48
protected virtual void SetDefaultParameters(string drivePath, string filename, int? driveSpeed, Options options) { }
#else
protected virtual void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Options options) { }
#endif
/// <summary>
/// Scan a possible parameter string and populate whatever possible
/// </summary>
/// <param name="parameters">String possibly representing parameters</param>
/// <returns>True if the parameters were set correctly, false otherwise</returns>
#if NET48
protected virtual bool ValidateAndSetParameters(string parameters) => !string.IsNullOrWhiteSpace(parameters);
#else
protected virtual bool ValidateAndSetParameters(string? parameters) => !string.IsNullOrWhiteSpace(parameters);
#endif
#endregion
@@ -1169,867 +1197,5 @@ namespace MPF.Core.Modules
}
#endregion
#region Common Information Extraction
/// <summary>
/// Generate the proper datfile from the input Datafile, if possible
/// </summary>
/// <param name="datafile">.dat file location</param>
/// <returns>Relevant pieces of the datfile, null on error</returns>
#if NET48
protected static string GenerateDatfile(Datafile datafile)
#else
protected static string? GenerateDatfile(Datafile? datafile)
#endif
{
// If we don't have a valid datafile, we can't do anything
if (datafile?.Games == null || datafile.Games.Length == 0)
return null;
var roms = datafile.Games[0].Roms;
if (roms == null || roms.Length == 0)
return null;
// Otherwise, reconstruct the hash data with only the required info
try
{
string datString = string.Empty;
for (int i = 0; i < roms.Length; i++)
{
var rom = roms[i];
datString += $"<rom name=\"{rom.Name}\" size=\"{rom.Size}\" crc=\"{rom.Crc}\" md5=\"{rom.Md5}\" sha1=\"{rom.Sha1}\" />\n";
}
return datString.TrimEnd('\n');
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get Datafile from a standard DAT
/// </summary>
/// <param name="dat">Path to the DAT file to parse</param>
/// <returns>Filled Datafile on success, null on error</returns>
#if NET48
protected static Datafile GetDatafile(string dat)
#else
protected static Datafile? GetDatafile(string? dat)
#endif
{
// If there's no path, we can't read the file
if (string.IsNullOrWhiteSpace(dat))
return null;
// If the file doesn't exist, we can't read it
if (!File.Exists(dat))
return null;
try
{
// Open and read in the XML file
XmlReader xtr = XmlReader.Create(dat, new XmlReaderSettings
{
CheckCharacters = false,
DtdProcessing = DtdProcessing.Ignore,
IgnoreComments = true,
IgnoreWhitespace = true,
ValidationFlags = XmlSchemaValidationFlags.None,
ValidationType = ValidationType.None,
});
// If the reader is null for some reason, we can't do anything
if (xtr == null)
return null;
var serializer = new XmlSerializer(typeof(Datafile));
return serializer.Deserialize(xtr) as Datafile;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Gets disc information from a PIC file
/// </summary>
/// <param name="pic">Path to a PIC.bin file</param>
/// <returns>Filled DiscInformation on success, null on error</returns>
/// <remarks>This omits the emergency brake information, if it exists</remarks>
#if NET48
protected static DiscInformation GetDiscInformation(string pic)
#else
protected static DiscInformation? GetDiscInformation(string pic)
#endif
{
try
{
return new SabreTools.Serialization.Files.PIC().Deserialize(pic);
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get hashes from an input file path
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <returns>True if hashing was successful, false otherwise</returns>
#if NET48
protected static bool GetFileHashes(string filename, out long size, out string crc32, out string md5, out string sha1)
#else
protected static bool GetFileHashes(string filename, out long size, out string? crc32, out string? md5, out string? sha1)
#endif
{
// Set all initial values
size = -1; crc32 = null; md5 = null; sha1 = null;
// If the file doesn't exist, we can't do anything
if (!File.Exists(filename))
return false;
// Set the file size
size = new FileInfo(filename).Length;
// Open the input file
var input = File.OpenRead(filename);
try
{
// Get a list of hashers to run over the buffer
var hashers = new List<Hasher>
{
new Hasher(Hash.CRC32),
new Hasher(Hash.MD5),
new Hasher(Hash.SHA1),
new Hasher(Hash.SHA256),
new Hasher(Hash.SHA384),
new Hasher(Hash.SHA512),
};
// Initialize the hashing helpers
var loadBuffer = new ThreadLoadBuffer(input);
int buffersize = 3 * 1024 * 1024;
byte[] buffer0 = new byte[buffersize];
byte[] buffer1 = new byte[buffersize];
/*
Please note that some of the following code is adapted from
RomVault. This is a modified version of how RomVault does
threaded hashing. As such, some of the terminology and code
is the same, though variable names and comments may have
been tweaked to better fit this code base.
*/
// Pre load the first buffer
long refsize = size;
int next = refsize > buffersize ? buffersize : (int)refsize;
input.Read(buffer0, 0, next);
int current = next;
refsize -= next;
bool bufferSelect = true;
while (current > 0)
{
// Trigger the buffer load on the second buffer
next = refsize > buffersize ? buffersize : (int)refsize;
if (next > 0)
loadBuffer.Trigger(bufferSelect ? buffer1 : buffer0, next);
byte[] buffer = bufferSelect ? buffer0 : buffer1;
// Run hashes in parallel
Parallel.ForEach(hashers, h => h.Process(buffer, current));
// Wait for the load buffer worker, if needed
if (next > 0)
loadBuffer.Wait();
// Setup for the next hashing step
current = next;
refsize -= next;
bufferSelect = !bufferSelect;
}
// Finalize all hashing helpers
loadBuffer.Finish();
Parallel.ForEach(hashers, h => h.Terminate());
// Get the results
crc32 = hashers.First(h => h.HashType == Hash.CRC32).GetHashString();
md5 = hashers.First(h => h.HashType == Hash.MD5).GetHashString();
sha1 = hashers.First(h => h.HashType == Hash.SHA1).GetHashString();
//sha256 = hashers.First(h => h.HashType == Hash.SHA256).GetHashString();
//sha384 = hashers.First(h => h.HashType == Hash.SHA384).GetHashString();
//sha512 = hashers.First(h => h.HashType == Hash.SHA512).GetHashString();
// Dispose of the hashers
loadBuffer.Dispose();
hashers.ForEach(h => h.Dispose());
return true;
}
catch (IOException)
{
return false;
}
finally
{
input.Dispose();
}
}
/// <summary>
/// Get the last modified date from a file path, if possible
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <returns>Filled DateTime on success, null on failure</returns>
#if NET48
protected static DateTime? GetFileModifiedDate(string filename, bool fallback = false)
#else
protected static DateTime? GetFileModifiedDate(string? filename, bool fallback = false)
#endif
{
if (string.IsNullOrWhiteSpace(filename))
return fallback ? (DateTime?)DateTime.UtcNow : null;
else if (!File.Exists(filename))
return fallback ? (DateTime?)DateTime.UtcNow : null;
var fi = new FileInfo(filename);
return fi.LastWriteTimeUtc;
}
/// <summary>
/// Get the split values for ISO-based media
/// </summary>
/// <param name="hashData">String representing the combined hash data</param>
/// <returns>True if extraction was successful, false otherwise</returns>
#if NET48
protected static bool GetISOHashValues(string hashData, out long size, out string crc32, out string md5, out string sha1)
#else
protected static bool GetISOHashValues(string? hashData, out long size, out string? crc32, out string? md5, out string? sha1)
#endif
{
size = -1; crc32 = null; md5 = null; sha1 = null;
if (string.IsNullOrWhiteSpace(hashData))
return false;
// TODO: Use deserialization to Rom instead of Regex
var hashreg = new Regex(@"<rom name="".*?"" size=""(.*?)"" crc=""(.*?)"" md5=""(.*?)"" sha1=""(.*?)""");
Match m = hashreg.Match(hashData);
if (m.Success)
{
_ = Int64.TryParse(m.Groups[1].Value, out size);
crc32 = m.Groups[2].Value;
md5 = m.Groups[3].Value;
sha1 = m.Groups[4].Value;
return true;
}
else
{
return false;
}
}
/// <summary>
/// Get the split values for ISO-based media
/// </summary>
/// <param name="datafile">Datafile represenging the hash data</param>
/// <returns>True if extraction was successful, false otherwise</returns>
#if NET48
protected static bool GetISOHashValues(Datafile datafile, out long size, out string crc32, out string md5, out string sha1)
#else
protected static bool GetISOHashValues(Datafile? datafile, out long size, out string? crc32, out string? md5, out string? sha1)
#endif
{
size = -1; crc32 = null; md5 = null; sha1 = null;
if (datafile?.Games == null || datafile.Games.Length == 0)
return false;
var roms = datafile.Games[0].Roms;
if (roms == null || roms.Length == 0)
return false;
var rom = roms[0];
_ = Int64.TryParse(rom.Size, out size);
crc32 = rom.Crc;
md5 = rom.Md5;
sha1 = rom.Sha1;
return true;
}
/// <summary>
/// Get the layerbreak info associated from the disc information
/// </summary>
/// <param name="di">Disc information containing unformatted data</param>
/// <returns>True if layerbreak info was set, false otherwise</returns>
#if NET48
protected static bool GetLayerbreaks(DiscInformation di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)
#else
protected static bool GetLayerbreaks(DiscInformation? di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)
#endif
{
// Set the default values
layerbreak1 = null; layerbreak2 = null; layerbreak3 = null;
// If we don't have valid disc information, we can't do anything
if (di?.Units == null || di.Units.Length <= 1)
return false;
#if NET48
int ReadFromArrayBigEndian(byte[] bytes, int offset)
#else
static int ReadFromArrayBigEndian(byte[]? bytes, int offset)
#endif
{
if (bytes == null)
return default;
var span = new ReadOnlySpan<byte>(bytes, offset, 0x04);
byte[] rev = span.ToArray();
Array.Reverse(rev);
return BitConverter.ToInt32(rev, 0);
}
// Layerbreak 1 (2+ layers)
if (di.Units.Length >= 2)
{
long offset = ReadFromArrayBigEndian(di.Units[0]?.Body?.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[0]?.Body?.FormatDependentContents, 0x10);
layerbreak1 = value - offset + 2;
}
// Layerbreak 2 (3+ layers)
if (di.Units.Length >= 3)
{
long offset = ReadFromArrayBigEndian(di.Units[1]?.Body?.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[1]?.Body?.FormatDependentContents, 0x10);
layerbreak2 = layerbreak1 + value - offset + 2;
}
// Layerbreak 3 (4 layers)
if (di.Units.Length >= 4)
{
long offset = ReadFromArrayBigEndian(di.Units[2]?.Body?.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[2]?.Body?.FormatDependentContents, 0x10);
layerbreak3 = layerbreak2 + value - offset + 2;
}
return true;
}
/// <summary>
/// Get the PIC identifier from the first disc information unit, if possible
/// </summary>
/// <param name="di">Disc information containing the data</param>
/// <returns>String representing the PIC identifier, null on error</returns>
#if NET48
protected static string GetPICIdentifier(DiscInformation di)
#else
protected static string? GetPICIdentifier(DiscInformation? di)
#endif
{
// If we don't have valid disc information, we can't do anything
if (di?.Units == null || di.Units.Length <= 1)
return null;
// We assume the identifier is consistent across all units
return di.Units[0]?.Body?.DiscTypeIdentifier;
}
/// <summary>
/// Get the EXE date from a PlayStation disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <param name="serial">Internal disc serial, if possible</param>
/// <param name="region">Output region, if possible</param>
/// <param name="date">Output EXE date in "yyyy-mm-dd" format if possible, null on error</param>
/// <returns></returns>
#if NET48
protected static bool GetPlayStationExecutableInfo(char? driveLetter, out string serial, out Region? region, out string date)
#else
protected static bool GetPlayStationExecutableInfo(char? driveLetter, out string? serial, out Region? region, out string? date)
#endif
{
serial = null; region = null; date = null;
// If there's no drive letter, we can't do this part
if (driveLetter == null)
return false;
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
return false;
// Get the two paths that we will need to check
string psxExePath = Path.Combine(drivePath, "PSX.EXE");
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
// Try both of the common paths that contain information
#if NET48
string exeName = null;
#else
string? exeName = null;
#endif
// Read the CNF file as an INI file
var systemCnf = new IniFile(systemCnfPath);
string bootValue = string.Empty;
// PlayStation uses "BOOT" as the key
if (systemCnf.ContainsKey("BOOT"))
bootValue = systemCnf["BOOT"];
// PlayStation 2 uses "BOOT2" as the key
if (systemCnf.ContainsKey("BOOT2"))
bootValue = systemCnf["BOOT2"];
// If we had any boot value, parse it and get the executable name
if (!string.IsNullOrEmpty(bootValue))
{
var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)");
if (match.Groups.Count > 1)
{
// EXE name may have a trailing `;` after
// EXE name should always be in all caps
exeName = match.Groups[1].Value
.Split(';')[0]
.ToUpperInvariant();
// Serial is most of the EXE name normalized
serial = exeName
.Replace('_', '-')
.Replace(".", string.Empty);
// Some games may have the EXE in a subfolder
serial = Path.GetFileName(serial);
}
}
// If the SYSTEM.CNF value can't be found, try PSX.EXE
if (string.IsNullOrWhiteSpace(exeName) && File.Exists(psxExePath))
exeName = "PSX.EXE";
// If neither can be found, we return false
if (string.IsNullOrWhiteSpace(exeName))
return false;
// Get the region, if possible
region = GetPlayStationRegion(exeName);
// Now that we have the EXE name, try to get the fileinfo for it
string exePath = Path.Combine(drivePath, exeName);
if (!File.Exists(exePath))
return false;
// Fix the Y2K timestamp issue
var fi = new FileInfo(exePath);
var dt = new DateTime(fi.LastWriteTimeUtc.Year >= 1900 && fi.LastWriteTimeUtc.Year < 1920 ? 2000 + fi.LastWriteTimeUtc.Year % 100 : fi.LastWriteTimeUtc.Year,
fi.LastWriteTimeUtc.Month, fi.LastWriteTimeUtc.Day);
date = dt.ToString("yyyy-MM-dd");
return true;
}
/// <summary>
/// Get the version from a PlayStation 2 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Game version if possible, null on error</returns>
#if NET48
protected static string GetPlayStation2Version(char? driveLetter)
#else
protected static string? GetPlayStation2Version(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
return null;
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
return null;
// Get the SYSTEM.CNF path to check
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
// Try to parse the SYSTEM.CNF file
var systemCnf = new IniFile(systemCnfPath);
if (systemCnf.ContainsKey("VER"))
return systemCnf["VER"];
// If "VER" can't be found, we can't do much
return null;
}
/// <summary>
/// Get the internal serial from a PlayStation 3 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Internal disc serial if possible, null on error</returns>
#if NET48
protected static string GetPlayStation3Serial(char? driveLetter)
#else
protected static string? GetPlayStation3Serial(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
return null;
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
return null;
// If we can't find PARAM.SFO, we don't have a PlayStation 3 disc
string paramSfoPath = Path.Combine(drivePath, "PS3_GAME", "PARAM.SFO");
if (!File.Exists(paramSfoPath))
return null;
// Let's try reading PARAM.SFO to find the serial at the end of the file
try
{
using (var br = new BinaryReader(File.OpenRead(paramSfoPath)))
{
br.BaseStream.Seek(-0x18, SeekOrigin.End);
return new string(br.ReadChars(9));
}
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the version from a PlayStation 3 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Game version if possible, null on error</returns>
#if NET48
protected static string GetPlayStation3Version(char? driveLetter)
#else
protected static string? GetPlayStation3Version(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
return null;
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
return null;
// If we can't find PARAM.SFO, we don't have a PlayStation 3 disc
string paramSfoPath = Path.Combine(drivePath, "PS3_GAME", "PARAM.SFO");
if (!File.Exists(paramSfoPath))
return null;
// Let's try reading PARAM.SFO to find the version at the end of the file
try
{
using (var br = new BinaryReader(File.OpenRead(paramSfoPath)))
{
br.BaseStream.Seek(-0x08, SeekOrigin.End);
return new string(br.ReadChars(5));
}
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the internal serial from a PlayStation 4 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Internal disc serial if possible, null on error</returns>
#if NET48
protected static string GetPlayStation4Serial(char? driveLetter)
#else
protected static string? GetPlayStation4Serial(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
return null;
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
return null;
// If we can't find param.sfo, we don't have a PlayStation 4 disc
string paramSfoPath = Path.Combine(drivePath, "bd", "param.sfo");
if (!File.Exists(paramSfoPath))
return null;
// Let's try reading param.sfo to find the serial at the end of the file
try
{
using (var br = new BinaryReader(File.OpenRead(paramSfoPath)))
{
br.BaseStream.Seek(-0x14, SeekOrigin.End);
return new string(br.ReadChars(9));
}
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the version from a PlayStation 4 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Game version if possible, null on error</returns>
#if NET48
protected static string GetPlayStation4Version(char? driveLetter)
#else
protected static string? GetPlayStation4Version(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
return null;
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
return null;
// If we can't find param.sfo, we don't have a PlayStation 4 disc
string paramSfoPath = Path.Combine(drivePath, "bd", "param.sfo");
if (!File.Exists(paramSfoPath))
return null;
// Let's try reading param.sfo to find the version at the end of the file
try
{
using (var br = new BinaryReader(File.OpenRead(paramSfoPath)))
{
br.BaseStream.Seek(-0x08, SeekOrigin.End);
return new string(br.ReadChars(5));
}
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the internal serial from a PlayStation 5 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Internal disc serial if possible, null on error</returns>
#if NET48
protected static string GetPlayStation5Serial(char? driveLetter)
#else
protected static string? GetPlayStation5Serial(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
return null;
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
return null;
// If we can't find param.json, we don't have a PlayStation 5 disc
string paramJsonPath = Path.Combine(drivePath, "bd", "param.json");
if (!File.Exists(paramJsonPath))
return null;
// Let's try reading param.json to find the serial in the unencrypted JSON
try
{
using (var br = new BinaryReader(File.OpenRead(paramJsonPath)))
{
br.BaseStream.Seek(0x82E, SeekOrigin.Begin);
return new string(br.ReadChars(9));
}
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the version from a PlayStation 5 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Game version if possible, null on error</returns>
#if NET48
protected static string GetPlayStation5Version(char? driveLetter)
#else
protected static string? GetPlayStation5Version(char? driveLetter)
#endif
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
return null;
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
return null;
// If we can't find param.json, we don't have a PlayStation 5 disc
string paramJsonPath = Path.Combine(drivePath, "bd", "param.json");
if (!File.Exists(paramJsonPath))
return null;
// Let's try reading param.json to find the version in the unencrypted JSON
try
{
using (var br = new BinaryReader(File.OpenRead(paramJsonPath)))
{
br.BaseStream.Seek(0x89E, SeekOrigin.Begin);
return new string(br.ReadChars(5));
}
}
catch
{
// We don't care what the error was
return null;
}
}
#endregion
#region Category Extraction
/// <summary>
/// Determine the category based on the UMDImageCreator string
/// </summary>
/// <param name="region">String representing the category</param>
/// <returns>Category, if possible</returns>
protected static DiscCategory? GetUMDCategory(string category)
{
switch (category)
{
case "GAME": return DiscCategory.Games;
case "VIDEO": return DiscCategory.Video;
case "AUDIO": return DiscCategory.Audio;
default: return null;
}
}
#endregion
#region Region Extraction
/// <summary>
/// Determine the region based on the PlayStation serial code
/// </summary>
/// <param name="serial">PlayStation serial code</param>
/// <returns>Region mapped from name, if possible</returns>
protected static Region? GetPlayStationRegion(string serial)
{
// Standardized "S" serials
if (serial.StartsWith("S"))
{
// string publisher = serial[0] + serial[1];
// char secondRegion = serial[3];
switch (serial[2])
{
case 'A': return Region.Asia;
case 'C': return Region.China;
case 'E': return Region.Europe;
case 'K': return Region.SouthKorea;
case 'U': return Region.UnitedStatesOfAmerica;
case 'P':
// Region of S_P_ serials may be Japan, Asia, or SouthKorea
switch (serial[3])
{
case 'S':
// Check first two digits of S_PS serial
switch (serial.Substring(5, 2))
{
case "46": return Region.SouthKorea;
case "56": return Region.SouthKorea;
case "51": return Region.Asia;
case "55": return Region.Asia;
default: return Region.Japan;
}
case 'M':
// Check first three digits of S_PM serial
switch (serial.Substring(5, 3))
{
case "645": return Region.SouthKorea;
case "675": return Region.SouthKorea;
case "885": return Region.SouthKorea;
default: return Region.Japan; // Remaining S_PM serials may be Japan or Asia
}
default: return Region.Japan;
}
}
}
// Japan-only special serial
else if (serial.StartsWith("PAPX"))
return Region.Japan;
// Region appears entirely random
else if (serial.StartsWith("PABX"))
return null;
// Region appears entirely random
else if (serial.StartsWith("PBPX"))
return null;
// Japan-only special serial
else if (serial.StartsWith("PCBX"))
return Region.Japan;
// Japan-only special serial
else if (serial.StartsWith("PCXC"))
return Region.Japan;
// Single disc known, Japan
else if (serial.StartsWith("PDBX"))
return Region.Japan;
// Single disc known, Europe
else if (serial.StartsWith("PEBX"))
return Region.Europe;
// Single disc known, USA
else if (serial.StartsWith("PUBX"))
return Region.UnitedStatesOfAmerica;
return null;
}
#endregion
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
@@ -7,6 +6,8 @@ using MPF.Core.Converters;
using MPF.Core.Data;
using SabreTools.RedumpLib.Data;
#pragma warning disable IDE0051 // Remove unused private members
namespace MPF.Core.Modules.CleanRip
{
/// <summary>
@@ -22,11 +23,19 @@ namespace MPF.Core.Modules.CleanRip
#endregion
/// <inheritdoc/>
#if NET48
public Parameters(string parameters) : base(parameters) { }
#else
public Parameters(string? parameters) : base(parameters) { }
#endif
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
#if NET48
public Parameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
#else
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
#endif
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
@@ -35,7 +44,7 @@ namespace MPF.Core.Modules.CleanRip
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
List<string> missingFiles = new List<string>();
var missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
@@ -66,18 +75,27 @@ namespace MPF.Core.Modules.CleanRip
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
#endif
{
// Ensure that required sections exist
info = SubmissionInfoTool.EnsureAllSections(info);
// TODO: Determine if there's a CleanRip version anywhere
if (info.DumpingInfo == null) info.DumpingInfo = new DumpingInfoSection();
#if NET48
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
#else
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
#endif
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
var datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
// Get the individual hash data, as per internal
if (GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
{
if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection();
#if NET48
info.SizeAndChecksums.Size = size;
#else
info.SizeAndChecksums!.Size = size;
#endif
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
@@ -94,23 +112,23 @@ namespace MPF.Core.Modules.CleanRip
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (File.Exists(basePath + ".bca"))
{
if (info.Extras == null) info.Extras = new ExtrasSection();
#if NET48
info.Extras.BCA = GetBCA(basePath + ".bca");
}
#else
info.Extras!.BCA = GetBCA(basePath + ".bca");
#endif
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out var gcVersion, out var gcName))
{
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
info.CommonDiscInfo.Region = gcRegion ?? info.CommonDiscInfo.Region;
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = gcVersion ?? info.VersionAndEditions.Version;
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.Region = gcRegion ?? info.CommonDiscInfo.Region;
info.VersionAndEditions.Version = gcVersion ?? info.VersionAndEditions.Version;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName] = gcName ?? string.Empty;
#else
info.CommonDiscInfo!.Region = gcRegion ?? info.CommonDiscInfo.Region;
info.VersionAndEditions!.Version = gcVersion ?? info.VersionAndEditions.Version;
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalName] = gcName ?? string.Empty;
#endif
}
break;
@@ -119,7 +137,12 @@ namespace MPF.Core.Modules.CleanRip
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
#if NET48
if (info.Artifacts == null) info.Artifacts = new Dictionary<string, string>();
#else
info.Artifacts ??= new Dictionary<string, string>();
#endif
if (File.Exists(basePath + ".bca"))
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true)) ?? string.Empty;
if (File.Exists(basePath + "-dumpinfo.txt"))
@@ -130,7 +153,7 @@ namespace MPF.Core.Modules.CleanRip
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
List<string> logFiles = new List<string>();
var logFiles = new List<string>();
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
@@ -167,7 +190,7 @@ namespace MPF.Core.Modules.CleanRip
if (!File.Exists(dumpinfo))
return null;
using (StreamReader sr = File.OpenText(dumpinfo))
using (var sr = File.OpenText(dumpinfo))
{
long size = new FileInfo(iso).Length;
string crc = string.Empty;
@@ -186,12 +209,21 @@ namespace MPF.Core.Modules.CleanRip
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(line))
continue;
#if NET48
else if (line.StartsWith("CRC32"))
crc = line.Substring(7).ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line.Substring(5);
else if (line.StartsWith("SHA-1"))
sha1 = line.Substring(7);
#else
else if (line.StartsWith("CRC32"))
crc = line[7..].ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line[5..];
else if (line.StartsWith("SHA-1"))
sha1 = line[7..];
#endif
}
return new Datafile
@@ -263,7 +295,7 @@ namespace MPF.Core.Modules.CleanRip
if (!File.Exists(dumpinfo))
return null;
using (StreamReader sr = File.OpenText(dumpinfo))
using (var sr = File.OpenText(dumpinfo))
{
long size = new FileInfo(iso).Length;
string crc = string.Empty;
@@ -282,12 +314,21 @@ namespace MPF.Core.Modules.CleanRip
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(line))
continue;
#if NET48
else if (line.StartsWith("CRC32"))
crc = line.Substring(7).ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line.Substring(5);
else if (line.StartsWith("SHA-1"))
sha1 = line.Substring(7);
#else
else if (line.StartsWith("CRC32"))
crc = line[7..].ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line[5..];
else if (line.StartsWith("SHA-1"))
sha1 = line[7..];
#endif
}
return $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
@@ -320,7 +361,7 @@ namespace MPF.Core.Modules.CleanRip
if (!File.Exists(dumpinfo))
return false;
using (StreamReader sr = File.OpenText(dumpinfo))
using (var sr = File.OpenText(dumpinfo))
{
try
{
@@ -338,15 +379,27 @@ namespace MPF.Core.Modules.CleanRip
}
else if (line.StartsWith("Version"))
{
#if NET48
version = line.Substring("Version: ".Length);
#else
version = line["Version: ".Length..];
#endif
}
else if (line.StartsWith("Internal Name"))
{
#if NET48
name = line.Substring("Internal Name: ".Length);
#else
name = line["Internal Name: ".Length..];
#endif
}
else if (line.StartsWith("Filename"))
{
#if NET48
string serial = line.Substring("Filename: ".Length);
#else
string serial = line["Filename: ".Length..];
#endif
// char gameType = serial[0];
// string gameid = serial[1] + serial[2];

File diff suppressed because it is too large Load Diff

View File

@@ -3,12 +3,13 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using BinaryObjectScanner.Protection;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
using SabreTools.RedumpLib.Data;
#pragma warning disable IDE0051 // Remove unused private members
namespace MPF.Core.Modules.Redumper
{
/// <summary>
@@ -209,11 +210,19 @@ namespace MPF.Core.Modules.Redumper
#endregion
/// <inheritdoc/>
#if NET48
public Parameters(string parameters) : base(parameters) { }
#else
public Parameters(string? parameters) : base(parameters) { }
#endif
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
#if NET48
public Parameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
#else
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
#endif
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
@@ -289,10 +298,16 @@ namespace MPF.Core.Modules.Redumper
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
#endif
{
// Ensure that required sections exist
info = SubmissionInfoTool.EnsureAllSections(info);
// Get the dumping program and version
if (info.DumpingInfo == null) info.DumpingInfo = new DumpingInfoSection();
#if NET48
info.DumpingInfo.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {GetVersion($"{basePath}.log") ?? "Unknown Version"}";
info.DumpingInfo.DumpingDate = GetFileModifiedDate($"{basePath}.log")?.ToString("yyyy-MM-dd HH:mm:ss");
#else
info.DumpingInfo!.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {GetVersion($"{basePath}.log") ?? "Unknown Version"}";
#endif
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate($"{basePath}.log")?.ToString("yyyy-MM-dd HH:mm:ss");
// Fill in the hardware data
if (GetHardwareInfo($"{basePath}.log", out var manufacturer, out var model, out var firmware))
@@ -305,103 +320,133 @@ namespace MPF.Core.Modules.Redumper
switch (this.Type)
{
case MediaType.CDROM:
if (info.Extras == null) info.Extras = new ExtrasSection();
#if NET48
info.Extras.PVD = GetPVD($"{basePath}.log") ?? "Disc has no PVD";
if (info.TracksAndWriteOffsets == null) info.TracksAndWriteOffsets = new TracksAndWriteOffsetsSection();
info.TracksAndWriteOffsets.ClrMameProData = GetDatfile($"{basePath}.log");
#else
info.Extras!.PVD = GetPVD($"{basePath}.log") ?? "Disc has no PVD";
info.TracksAndWriteOffsets!.ClrMameProData = GetDatfile($"{basePath}.log");
#endif
info.TracksAndWriteOffsets.Cuesheet = GetFullFile($"{basePath}.cue") ?? string.Empty;
// Attempt to get the write offset
string cdWriteOffset = GetWriteOffset($"{basePath}.log") ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
info.CommonDiscInfo.RingWriteOffset = cdWriteOffset;
#else
info.CommonDiscInfo!.RingWriteOffset = cdWriteOffset;
#endif
info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset;
// Attempt to get the error count
long errorCount = GetErrorCount($"{basePath}.log");
info.CommonDiscInfo.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString());
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
// Attempt to get multisession data
string cdMultiSessionInfo = GetMultisessionInformation($"{basePath}.log") ?? string.Empty;
if (!string.IsNullOrWhiteSpace(cdMultiSessionInfo))
#if NET48
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession] = cdMultiSessionInfo;
#else
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.Multisession] = cdMultiSessionInfo;
#endif
// Attempt to get the universal hash, if it's an audio disc
if (this.System.IsAudio())
{
string universalHash = GetUniversalHash($"{basePath}.log") ?? string.Empty;
#if NET48
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash] = universalHash;
#else
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.UniversalHash] = universalHash;
#endif
}
// Attempt to get the non-zero data start, if it's an audio disc
if (this.System.IsAudio())
{
string ringNonZeroDataStart = GetRingNonZeroDataStart($"{basePath}.log") ?? string.Empty;
#if NET48
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.RingNonZeroDataStart] = ringNonZeroDataStart;
#else
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.RingNonZeroDataStart] = ringNonZeroDataStart;
#endif
}
break;
case MediaType.DVD:
if (info.Extras == null) info.Extras = new ExtrasSection();
#if NET48
info.Extras.PVD = GetPVD($"{basePath}.log") ?? "Disc has no PVD";
if (info.TracksAndWriteOffsets == null) info.TracksAndWriteOffsets = new TracksAndWriteOffsetsSection();
info.TracksAndWriteOffsets.ClrMameProData = GetDatfile($"{basePath}.log");
#else
info.Extras!.PVD = GetPVD($"{basePath}.log") ?? "Disc has no PVD";
info.TracksAndWriteOffsets!.ClrMameProData = GetDatfile($"{basePath}.log");
#endif
// Get the individual hash data, as per internal
if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection();
if (GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out var crc32, out var md5, out var sha1))
if (InfoTool.GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out var crc32, out var md5, out var sha1))
{
#if NET48
info.SizeAndChecksums.Size = size;
#else
info.SizeAndChecksums!.Size = size;
#endif
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
}
string layerbreak = GetLayerbreak($"{basePath}.log") ?? string.Empty;
#if NET48
info.SizeAndChecksums.Layerbreak = !string.IsNullOrEmpty(layerbreak) ? Int64.Parse(layerbreak) : default;
#else
info.SizeAndChecksums!.Layerbreak = !string.IsNullOrEmpty(layerbreak) ? Int64.Parse(layerbreak) : default;
#endif
break;
}
switch (this.System)
{
// case RedumpSystem.AppleMacintosh:
case RedumpSystem.AppleMacintosh:
case RedumpSystem.EnhancedCD:
case RedumpSystem.IBMPCcompatible:
case RedumpSystem.RainbowDisc:
case RedumpSystem.SonyElectronicBook:
// TODO: Support SecuROM data when generated
#if NET48
info.CopyProtection.SecuROMData = GetSecuROMData($"{basePath}.log") ?? string.Empty;
#else
info.CopyProtection!.SecuROMData = GetSecuROMData($"{basePath}.log") ?? string.Empty;
#endif
break;
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection();
#if NET48
info.CopyProtection.Protection = GetDVDProtection($"{basePath}.log") ?? string.Empty;
#else
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}.log") ?? string.Empty;
#endif
break;
case RedumpSystem.KonamiPython2:
if (GetPlayStationExecutableInfo(drive?.Letter, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
#else
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
#endif
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
}
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
#if NET48
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
#else
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
#endif
break;
case RedumpSystem.MicrosoftXbox:
@@ -417,15 +462,13 @@ namespace MPF.Core.Modules.Redumper
break;
case RedumpSystem.SegaMegaCDSegaCD:
if (info.Extras == null) info.Extras = new ExtrasSection();
info.Extras.Header = GetSegaCDHeader($"{basePath}.log", out var scdBuildDate, out var scdSerial, out _) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.Extras.Header = GetSegaCDHeader($"{basePath}.log", out var scdBuildDate, out var scdSerial, out _) ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = scdSerial ?? string.Empty;
#else
info.Extras!.Header = GetSegaCDHeader($"{basePath}.log", out var scdBuildDate, out var scdSerial, out _) ?? string.Empty;
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = scdSerial ?? string.Empty;
#endif
info.CommonDiscInfo.EXEDateBuildDate = scdBuildDate ?? string.Empty;
// TODO: Support region setting from parsed value
break;
@@ -447,8 +490,11 @@ namespace MPF.Core.Modules.Redumper
break;
case RedumpSystem.SegaSaturn:
if (info.Extras == null) info.Extras = new ExtrasSection();
#if NET48
info.Extras.Header = GetSaturnHeader($"{basePath}.log") ?? string.Empty;
#else
info.Extras!.Header = GetSaturnHeader($"{basePath}.log") ?? string.Empty;
#endif
// Take only the first 16 lines for Saturn
if (!string.IsNullOrEmpty(info.Extras.Header))
@@ -457,103 +503,102 @@ namespace MPF.Core.Modules.Redumper
if (GetSaturnBuildInfo(info.Extras.Header, out var saturnSerial, out var saturnVersion, out var buildDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = saturnSerial ?? string.Empty;
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = saturnVersion ?? string.Empty;
#else
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = saturnSerial ?? string.Empty;
info.VersionAndEditions!.Version = saturnVersion ?? string.Empty;
#endif
info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? string.Empty;
}
break;
case RedumpSystem.SonyPlayStation:
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
#else
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
#endif
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
}
if (info.CopyProtection == null) info.CopyProtection = new CopyProtectionSection();
#if NET48
info.CopyProtection.AntiModchip = GetPlayStationAntiModchipDetected($"{basePath}.log").ToYesNo();
if (info.EDC == null) info.EDC = new EDCSection();
info.EDC.EDC = GetPlayStationEDCStatus($"{basePath}.log").ToYesNo();
#else
info.CopyProtection!.AntiModchip = GetPlayStationAntiModchipDetected($"{basePath}.log").ToYesNo();
info.EDC!.EDC = GetPlayStationEDCStatus($"{basePath}.log").ToYesNo();
#endif
info.CopyProtection.LibCrypt = GetPlayStationLibCryptStatus($"{basePath}.log").ToYesNo();
info.CopyProtection.LibCryptData = GetPlayStationLibCryptData($"{basePath}.log");
break;
case RedumpSystem.SonyPlayStation2:
if (GetPlayStationExecutableInfo(drive?.Letter, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
{
// Ensure internal serial is pulled from local data
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
#else
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
#endif
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
}
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
#if NET48
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
#else
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
#endif
break;
case RedumpSystem.SonyPlayStation3:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
info.VersionAndEditions.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation4:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
info.VersionAndEditions.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
info.VersionAndEditions!.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation5:
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
info.VersionAndEditions.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
#else
if (info.CommonDiscInfo.CommentsSpecialFields == null) info.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
info.VersionAndEditions!.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
#endif
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
break;
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
#if NET48
if (info.Artifacts == null) info.Artifacts = new Dictionary<string, string>();
#else
info.Artifacts ??= new Dictionary<string, string>();
#endif
if (File.Exists($"{basePath}.cdtext"))
info.Artifacts["cdtext"] = GetBase64(GetFullFile($"{basePath}.cdtext")) ?? string.Empty;
if (File.Exists($"{basePath}.cue"))
@@ -887,6 +932,19 @@ namespace MPF.Core.Modules.Redumper
public override string? GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
#endif
/// <inheritdoc/>
public override List<string> GetDeleteableFilePaths(string basePath)
{
var deleteableFiles = new List<string>();
if (File.Exists($"{basePath}.scram"))
deleteableFiles.Add($"{basePath}.scram");
if (File.Exists($"{basePath}.scrap"))
deleteableFiles.Add($"{basePath}.scrap");
return deleteableFiles;
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
@@ -901,10 +959,6 @@ namespace MPF.Core.Modules.Redumper
logFiles.Add($"{basePath}.fulltoc");
if (File.Exists($"{basePath}.log"))
logFiles.Add($"{basePath}.log");
// if (File.Exists($"{basePath}.scram"))
// logFiles.Add($"{basePath}.scram");
// if (File.Exists($"{basePath}.scrap"))
// logFiles.Add($"{basePath}.scrap");
if (File.Exists($"{basePath}.state"))
logFiles.Add($"{basePath}.state");
if (File.Exists($"{basePath}.subcode"))
@@ -984,7 +1038,11 @@ namespace MPF.Core.Modules.Redumper
}
/// <inheritdoc/>
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
#if NET48
protected override void SetDefaultParameters(string drivePath, string filename, int? driveSpeed, Options options)
#else
protected override void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Options options)
#endif
{
// If we don't have a CD or DVD, we can't dump using redumper
if (this.Type != MediaType.CDROM && this.Type != MediaType.DVD)
@@ -994,6 +1052,7 @@ namespace MPF.Core.Modules.Redumper
switch (this.Type)
{
case MediaType.CDROM:
#if NET48
switch (this.System)
{
case RedumpSystem.SuperAudioCD:
@@ -1003,6 +1062,13 @@ namespace MPF.Core.Modules.Redumper
ModeValues = new List<string> { CommandStrings.CD };
break;
}
#else
ModeValues = this.System switch
{
RedumpSystem.SuperAudioCD => new List<string> { CommandStrings.SACD },
_ => new List<string> { CommandStrings.CD },
};
#endif
break;
case MediaType.DVD:
ModeValues = new List<string> { CommandStrings.DVD };
@@ -1016,7 +1082,7 @@ namespace MPF.Core.Modules.Redumper
}
this[FlagStrings.Drive] = true;
DriveValue = driveLetter.ToString();
DriveValue = drivePath;
this[FlagStrings.Speed] = true;
SpeedValue = driveSpeed;
@@ -1026,6 +1092,16 @@ namespace MPF.Core.Modules.Redumper
this[FlagStrings.Verbose] = options.RedumperEnableVerbose;
if (options.RedumperEnableDebug)
this[FlagStrings.Debug] = options.RedumperEnableDebug;
if (options.RedumperUseBEReading)
{
this[FlagStrings.DriveReadMethod] = true;
DriveReadMethodValue = "BE_CDDA";
}
if (options.RedumperUseGenericDriveType)
{
this[FlagStrings.DriveType] = true;
DriveTypeValue = "Generic";
}
// Set the output paths
if (!string.IsNullOrWhiteSpace(filename))
@@ -1050,7 +1126,11 @@ namespace MPF.Core.Modules.Redumper
}
/// <inheritdoc/>
#if NET48
protected override bool ValidateAndSetParameters(string parameters)
#else
protected override bool ValidateAndSetParameters(string? parameters)
#endif
{
BaseCommand = CommandStrings.NONE;
@@ -1312,7 +1392,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -1358,7 +1438,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -1410,7 +1490,7 @@ namespace MPF.Core.Modules.Redumper
#else
string? region = null, rceProtection = null, copyrightProtectionSystemType = null, vobKeys = null, decryptedDiscKey = null;
#endif
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -1511,7 +1591,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return -1;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -1562,7 +1642,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -1641,7 +1721,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -1707,7 +1787,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -1747,7 +1827,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -1791,7 +1871,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -1833,7 +1913,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -1877,7 +1957,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -1923,7 +2003,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -2015,7 +2095,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -2057,6 +2137,56 @@ namespace MPF.Core.Modules.Redumper
}
}
/// <summary>
/// Get the header from a Saturn, if possible
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Header as a byte array if possible, null on error</returns>
#if NET48
private static string GetSecuROMData(string log)
#else
private static string? GetSecuROMData(string log)
#endif
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
return null;
using (var sr = File.OpenText(log))
{
try
{
// Fast forward to the SecuROM line
while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("SecuROM [") == false) ;
if (sr.EndOfStream)
return null;
var lines = new List<string>();
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.TrimStart();
// Skip the "version"/"scheme" line
if (line?.StartsWith("version:") == true || line?.StartsWith("scheme:") == true)
continue;
// Only read until while there are MSF lines
if (line?.StartsWith("MSF:") != true)
break;
lines.Add(line);
}
return string.Join("\n", lines).TrimEnd('\n');
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
/// <summary>
/// Get the header from a Sega CD / Mega CD, if possible
/// </summary>
@@ -2075,7 +2205,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -2167,7 +2297,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -2214,7 +2344,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return null;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -2265,7 +2395,7 @@ namespace MPF.Core.Modules.Redumper
// redumper v2022.10.28 [Oct 28 2022, 05:41:43] (print usage: --help,-h)
// redumper v2022.12.22 build_87 [Dec 22 2022, 01:56:26]
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{
@@ -2309,7 +2439,7 @@ namespace MPF.Core.Modules.Redumper
if (!File.Exists(log))
return false;
using (StreamReader sr = File.OpenText(log))
using (var sr = File.OpenText(log))
{
try
{

View File

@@ -21,11 +21,19 @@ namespace MPF.Core.Modules.UmdImageCreator
#endregion
/// <inheritdoc/>
#if NET48
public Parameters(string parameters) : base(parameters) { }
#else
public Parameters(string? parameters) : base(parameters) { }
#endif
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
#if NET48
public Parameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
#else
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
#endif
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
@@ -34,7 +42,7 @@ namespace MPF.Core.Modules.UmdImageCreator
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
List<string> missingFiles = new List<string>();
var missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.UMD:
@@ -67,22 +75,34 @@ namespace MPF.Core.Modules.UmdImageCreator
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
#endif
{
// Ensure that required sections exist
info = SubmissionInfoTool.EnsureAllSections(info);
// TODO: Determine if there's a UMDImageCreator version anywhere
if (info.DumpingInfo == null) info.DumpingInfo = new DumpingInfoSection();
#if NET48
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
#else
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
#endif
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.UMD:
if (info.Extras == null) info.Extras = new ExtrasSection();
#if NET48
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty;
#else
info.Extras!.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty;
#endif
if (GetFileHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
if (InfoTool.GetFileHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
{
if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection();
#if NET48
info.SizeAndChecksums.Size = filesize;
#else
info.SizeAndChecksums!.Size = filesize;
#endif
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
@@ -90,13 +110,17 @@ namespace MPF.Core.Modules.UmdImageCreator
if (GetUMDAuxInfo(basePath + "_disc.txt", out var title, out DiscCategory? umdcat, out var umdversion, out var umdlayer, out long umdsize))
{
if (info.CommonDiscInfo == null) info.CommonDiscInfo = new CommonDiscInfoSection();
#if NET48
info.CommonDiscInfo.Title = title ?? string.Empty;
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
if (info.VersionAndEditions == null) info.VersionAndEditions = new VersionAndEditionsSection();
info.VersionAndEditions.Version = umdversion ?? string.Empty;
if (info.SizeAndChecksums == null) info.SizeAndChecksums = new SizeAndChecksumsSection();
info.SizeAndChecksums.Size = umdsize;
#else
info.CommonDiscInfo!.Title = title ?? string.Empty;
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
info.VersionAndEditions!.Version = umdversion ?? string.Empty;
info.SizeAndChecksums!.Size = umdsize;
#endif
if (!string.IsNullOrWhiteSpace(umdlayer))
info.SizeAndChecksums.Layerbreak = Int64.Parse(umdlayer ?? "-1");
@@ -108,7 +132,12 @@ namespace MPF.Core.Modules.UmdImageCreator
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
#if NET48
if (info.Artifacts == null) info.Artifacts = new Dictionary<string, string>();
#else
info.Artifacts ??= new Dictionary<string, string>();
#endif
if (File.Exists(basePath + "_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt")) ?? string.Empty;
if (File.Exists(basePath + "_drive.txt"))
@@ -125,7 +154,7 @@ namespace MPF.Core.Modules.UmdImageCreator
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
List<string> logFiles = new List<string>();
var logFiles = new List<string>();
switch (this.Type)
{
case MediaType.UMD:
@@ -165,7 +194,7 @@ namespace MPF.Core.Modules.UmdImageCreator
if (!File.Exists(mainInfo))
return null;
using (StreamReader sr = File.OpenText(mainInfo))
using (var sr = File.OpenText(mainInfo))
{
try
{
@@ -207,7 +236,7 @@ namespace MPF.Core.Modules.UmdImageCreator
if (!File.Exists(disc))
return false;
using (StreamReader sr = File.OpenText(disc))
using (var sr = File.OpenText(disc))
{
try
{
@@ -220,11 +249,15 @@ namespace MPF.Core.Modules.UmdImageCreator
break;
if (line.StartsWith("TITLE") && title == null)
#if NET48
title = line.Substring("TITLE: ".Length);
#else
title = line["TITLE: ".Length..];
#endif
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
umdversion = line.Split(' ')[1];
else if (line.StartsWith("pspUmdTypes"))
umdcat = GetUMDCategory(line.Split(' ')[1]);
umdcat = InfoTool.GetUMDCategory(line.Split(' ')[1]);
else if (line.StartsWith("L0 length"))
umdlayer = line.Split(' ')[2];
else if (line.StartsWith("FileSize:"))
@@ -232,7 +265,7 @@ namespace MPF.Core.Modules.UmdImageCreator
}
// If the L0 length is the size of the full disc, there's no layerbreak
if (Int64.TryParse(umdlayer, out long umdlayerValue) && umdlayerValue * 2048 == umdsize)
if (Int64.TryParse(umdlayer, out long umdlayerValue) && umdlayerValue * 2048 == umdsize)
umdlayer = null;
return true;

View File

@@ -5,7 +5,7 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BinaryObjectScanner.Protection;
using BurnOutSharp;
using BinaryObjectScanner;
using psxt001z;
namespace MPF.Core
@@ -95,8 +95,16 @@ namespace MPF.Core
/// </summary>
/// <param name="path">Path to scan for anti-modchip strings</param>
/// <returns>Anti-modchip existence if possible, false on error</returns>
#if NET48
public static async Task<bool> GetPlayStationAntiModchipDetected(string path)
#else
public static async Task<bool> GetPlayStationAntiModchipDetected(string? path)
#endif
{
// If there is no valid path
if (string.IsNullOrEmpty(path))
return false;
return await Task.Run(() =>
{
try
@@ -107,7 +115,7 @@ namespace MPF.Core
try
{
byte[] fileContent = File.ReadAllBytes(file);
string protection = antiModchip.CheckContents(file, fileContent, false);
var protection = antiModchip.CheckContents(file, fileContent, false);
if (!string.IsNullOrWhiteSpace(protection))
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -38,7 +38,11 @@ namespace MPF.Core.UI.ViewModels
/// <summary>
/// List of Redump-supported Regions
/// </summary>
#if NET48
private static readonly List<Region> RedumpRegions = new List<Region>
#else
private static readonly List<Region> RedumpRegions = new()
#endif
{
Region.Argentina,
Region.Asia,
@@ -129,7 +133,11 @@ namespace MPF.Core.UI.ViewModels
/// <summary>
/// List of Redump-supported Languages
/// </summary>
#if NET48
private static readonly List<Language> RedumpLanguages = new List<Language>
#else
private static readonly List<Language> RedumpLanguages = new()
#endif
{
Language.Afrikaans,
Language.Albanian,
@@ -191,10 +199,14 @@ namespace MPF.Core.UI.ViewModels
/// <summary>
/// Constructor
/// </summary>
#if NET48
public DiscInformationViewModel(Options options, SubmissionInfo submissionInfo)
#else
public DiscInformationViewModel(Options options, SubmissionInfo? submissionInfo)
#endif
{
Options = options;
SubmissionInfo = submissionInfo.Clone() as SubmissionInfo ?? new SubmissionInfo();
SubmissionInfo = submissionInfo?.Clone() as SubmissionInfo ?? new SubmissionInfo();
}
#region Helpers

View File

@@ -4,7 +4,7 @@ using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BurnOutSharp;
using BinaryObjectScanner;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
@@ -104,7 +104,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_optionsMenuItemEnabled = value;
TriggerPropertyChanged("OptionsMenuItemEnabled");
TriggerPropertyChanged(nameof(OptionsMenuItemEnabled));
}
}
private bool _optionsMenuItemEnabled;
@@ -118,7 +118,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_currentSystem = value;
TriggerPropertyChanged("CurrentSystem");
TriggerPropertyChanged(nameof(CurrentSystem));
}
}
private RedumpSystem? _currentSystem;
@@ -132,7 +132,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_systemTypeComboBoxEnabled = value;
TriggerPropertyChanged("SystemTypeComboBoxEnabled");
TriggerPropertyChanged(nameof(SystemTypeComboBoxEnabled));
}
}
private bool _systemTypeComboBoxEnabled;
@@ -146,7 +146,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_currentMediaType = value;
TriggerPropertyChanged("CurrentMediaType");
TriggerPropertyChanged(nameof(CurrentMediaType));
}
}
private MediaType? _currentMediaType;
@@ -160,7 +160,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_mediaTypeComboBoxEnabled = value;
TriggerPropertyChanged("MediaTypeComboBoxEnabled");
TriggerPropertyChanged(nameof(MediaTypeComboBoxEnabled));
}
}
private bool _mediaTypeComboBoxEnabled;
@@ -174,7 +174,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_outputPath = value;
TriggerPropertyChanged("OutputPath");
TriggerPropertyChanged(nameof(OutputPath));
}
}
private string _outputPath;
@@ -188,7 +188,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_outputPathTextBoxEnabled = value;
TriggerPropertyChanged("OutputPathTextBoxEnabled");
TriggerPropertyChanged(nameof(OutputPathTextBoxEnabled));
}
}
private bool _outputPathTextBoxEnabled;
@@ -202,7 +202,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_outputPathBrowseButtonEnabled = value;
TriggerPropertyChanged("OutputPathBrowseButtonEnabled");
TriggerPropertyChanged(nameof(OutputPathBrowseButtonEnabled));
}
}
private bool _outputPathBrowseButtonEnabled;
@@ -220,7 +220,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_currentDrive = value;
TriggerPropertyChanged("CurrentDrive");
TriggerPropertyChanged(nameof(CurrentDrive));
}
}
#if NET48
@@ -238,7 +238,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_driveLetterComboBoxEnabled = value;
TriggerPropertyChanged("DriveLetterComboBoxEnabled");
TriggerPropertyChanged(nameof(DriveLetterComboBoxEnabled));
}
}
private bool _driveLetterComboBoxEnabled;
@@ -252,7 +252,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_driveSpeed = value;
TriggerPropertyChanged("DriveSpeed");
TriggerPropertyChanged(nameof(DriveSpeed));
}
}
private int _driveSpeed;
@@ -266,7 +266,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_driveSpeedComboBoxEnabled = value;
TriggerPropertyChanged("DriveSpeedComboBoxEnabled");
TriggerPropertyChanged(nameof(DriveSpeedComboBoxEnabled));
}
}
private bool _driveSpeedComboBoxEnabled;
@@ -280,7 +280,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_currentProgram = value;
TriggerPropertyChanged("CurrentProgram");
TriggerPropertyChanged(nameof(CurrentProgram));
}
}
private InternalProgram _currentProgram;
@@ -294,7 +294,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_dumpingProgramComboBoxEnabled = value;
TriggerPropertyChanged("DumpingProgramComboBoxEnabled");
TriggerPropertyChanged(nameof(DumpingProgramComboBoxEnabled));
}
}
private bool _dumpingProgramComboBoxEnabled;
@@ -308,7 +308,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_parameters = value;
TriggerPropertyChanged("Parameters");
TriggerPropertyChanged(nameof(Parameters));
}
}
private string _parameters;
@@ -322,7 +322,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_parametersCheckBoxEnabled = value;
TriggerPropertyChanged("ParametersCheckBoxEnabled");
TriggerPropertyChanged(nameof(ParametersCheckBoxEnabled));
}
}
private bool _parametersCheckBoxEnabled;
@@ -336,7 +336,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_enableParametersCheckBoxEnabled = value;
TriggerPropertyChanged("EnableParametersCheckBoxEnabled");
TriggerPropertyChanged(nameof(EnableParametersCheckBoxEnabled));
}
}
private bool _enableParametersCheckBoxEnabled;
@@ -350,7 +350,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_startStopButtonEnabled = value;
TriggerPropertyChanged("StartStopButtonEnabled");
TriggerPropertyChanged(nameof(StartStopButtonEnabled));
}
}
private bool _startStopButtonEnabled;
@@ -364,7 +364,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_startStopButtonText = (value as string) ?? string.Empty;
TriggerPropertyChanged("StartStopButtonText");
TriggerPropertyChanged(nameof(StartStopButtonText));
}
}
private string _startStopButtonText;
@@ -378,7 +378,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_mediaScanButtonEnabled = value;
TriggerPropertyChanged("MediaScanButtonEnabled");
TriggerPropertyChanged(nameof(MediaScanButtonEnabled));
}
}
private bool _mediaScanButtonEnabled;
@@ -392,7 +392,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_updateVolumeLabelEnabled = value;
TriggerPropertyChanged("UpdateVolumeLabelEnabled");
TriggerPropertyChanged(nameof(UpdateVolumeLabelEnabled));
}
}
private bool _updateVolumeLabelEnabled;
@@ -406,7 +406,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_copyProtectScanButtonEnabled = value;
TriggerPropertyChanged("CopyProtectScanButtonEnabled");
TriggerPropertyChanged(nameof(CopyProtectScanButtonEnabled));
}
}
private bool _copyProtectScanButtonEnabled;
@@ -420,7 +420,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_status = value;
TriggerPropertyChanged("Status");
TriggerPropertyChanged(nameof(Status));
}
}
private string _status;
@@ -434,7 +434,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_logPanelExpanded = value;
TriggerPropertyChanged("LogPanelExpanded");
TriggerPropertyChanged(nameof(LogPanelExpanded));
}
}
private bool _logPanelExpanded;
@@ -452,7 +452,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_drives = value;
TriggerPropertyChanged("Drives");
TriggerPropertyChanged(nameof(Drives));
}
}
private List<Drive> _drives;
@@ -466,7 +466,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_driveSpeeds = value;
TriggerPropertyChanged("DriveSpeeds");
TriggerPropertyChanged(nameof(DriveSpeeds));
}
}
private List<int> _driveSpeeds;
@@ -484,7 +484,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_mediaTypes = value;
TriggerPropertyChanged("MediaTypes");
TriggerPropertyChanged(nameof(MediaTypes));
}
}
#if NET48
@@ -502,7 +502,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_systems = value;
TriggerPropertyChanged("Systems");
TriggerPropertyChanged(nameof(Systems));
}
}
private List<RedumpSystemComboBoxItem> _systems;
@@ -516,7 +516,7 @@ namespace MPF.Core.UI.ViewModels
set
{
_internalPrograms = value;
TriggerPropertyChanged("InternalPrograms");
TriggerPropertyChanged(nameof(InternalPrograms));
}
}
private List<Element<InternalProgram>> _internalPrograms;
@@ -544,11 +544,13 @@ namespace MPF.Core.UI.ViewModels
SystemTypeComboBoxEnabled = true;
MediaTypeComboBoxEnabled = true;
OutputPathTextBoxEnabled = true;
OutputPathBrowseButtonEnabled = true;
DriveLetterComboBoxEnabled = true;
DumpingProgramComboBoxEnabled = true;
StartStopButtonEnabled = true;
StartStopButtonText = Interface.StartDumping;
MediaScanButtonEnabled = true;
EnableParametersCheckBoxEnabled = true;
LogPanelExpanded = _options.OpenLogWindowAtStartup;
MediaTypes = new List<Element<MediaType>>();
@@ -585,14 +587,14 @@ namespace MPF.Core.UI.ViewModels
private void TriggerPropertyChanged(string propertyName)
{
// Disable event handlers temporarily
CanExecuteSelectionChanged = false;
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
// If the property change event is initialized
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// Reenable event handlers
CanExecuteSelectionChanged = true;
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
#endregion
@@ -606,7 +608,8 @@ namespace MPF.Core.UI.ViewModels
private void PopulateDrives()
{
// Disable other UI updates
CanExecuteSelectionChanged = false;
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
VerboseLogLn("Scanning for drives..");
@@ -615,19 +618,19 @@ namespace MPF.Core.UI.ViewModels
this.UpdateVolumeLabelEnabled = true;
// If we have a selected drive, keep track of it
char? lastSelectedDrive = this.CurrentDrive?.Letter;
char? lastSelectedDrive = this.CurrentDrive?.Name?[0] ?? null;
// Populate the list of drives and add it to the combo box
Drives = Drive.CreateListOfDrives(this.Options.IgnoreFixedDrives);
if (Drives.Count > 0)
{
VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", Drives.Select(d => d.Letter))}");
VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", Drives.Select(d => d.Name))}");
// Check for the last selected drive, if possible
int index = -1;
if (lastSelectedDrive != null)
index = Drives.FindIndex(d => d.MarkedActive && d.Letter == lastSelectedDrive);
index = Drives.FindIndex(d => d.MarkedActive && (d.Name?[0] ?? '\0') == lastSelectedDrive);
// Check for active optical drives
if (index == -1)
@@ -662,8 +665,8 @@ namespace MPF.Core.UI.ViewModels
this.CopyProtectScanButtonEnabled = false;
}
// Reenable UI updates
CanExecuteSelectionChanged = true;
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
/// <summary>
@@ -672,7 +675,8 @@ namespace MPF.Core.UI.ViewModels
private void PopulateMediaType()
{
// Disable other UI updates
CanExecuteSelectionChanged = false;
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
if (this.CurrentSystem != null)
{
@@ -692,8 +696,8 @@ namespace MPF.Core.UI.ViewModels
this.CurrentMediaType = null;
}
// Reenable UI updates
CanExecuteSelectionChanged = true;
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
/// <summary>
@@ -702,7 +706,8 @@ namespace MPF.Core.UI.ViewModels
private void PopulateInternalPrograms()
{
// Disable other UI updates
CanExecuteSelectionChanged = false;
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
// Get the current internal program
InternalProgram internalProgram = this.Options.InternalProgram;
@@ -715,8 +720,8 @@ namespace MPF.Core.UI.ViewModels
int currentIndex = InternalPrograms.FindIndex(m => m == internalProgram);
this.CurrentProgram = (currentIndex > -1 ? InternalPrograms[currentIndex].Value : InternalPrograms[0].Value);
// Reenable UI updates
CanExecuteSelectionChanged = true;
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
#endregion
@@ -797,7 +802,7 @@ namespace MPF.Core.UI.ViewModels
/// <summary>
/// Build a dummy SubmissionInfo
/// </summary>
public SubmissionInfo CreateDebugSubmissionInfo()
public static SubmissionInfo CreateDebugSubmissionInfo()
{
return new SubmissionInfo()
{
@@ -960,13 +965,13 @@ namespace MPF.Core.UI.ViewModels
if (_environment != null && _environment.Options.EjectAfterDump)
{
VerboseLogLn($"Ejecting disc in drive {_environment.Drive?.Letter}");
VerboseLogLn($"Ejecting disc in drive {_environment.Drive?.Name}");
await _environment.EjectDisc();
}
if (_environment != null && this.Options.DICResetDriveAfterDump)
{
VerboseLogLn($"Resetting drive {_environment.Drive?.Letter}");
VerboseLogLn($"Resetting drive {_environment.Drive?.Name}");
await _environment.ResetDrive();
}
}
@@ -977,13 +982,23 @@ namespace MPF.Core.UI.ViewModels
/// </summary>
/// <param name="savedSettings">Indicates if the settings were saved or not</param>
/// <param name="newOptions">Options representing the new, saved values</param>
#if NET48
public void UpdateOptions(bool savedSettings, Data.Options newOptions)
#else
public void UpdateOptions(bool savedSettings, Data.Options? newOptions)
#endif
{
// Get which options to save
var optionsToSave = savedSettings ? newOptions : Options;
// Ensure the first run flag is unset
var continuingOptions = new Data.Options(optionsToSave);
continuingOptions.FirstRun = false;
this.Options = continuingOptions;
// If settings were changed, reinitialize the UI
if (savedSettings)
{
this.Options = new Data.Options(newOptions);
InitializeUIValues(removeEventHandlers: true, rescanDrives: true);
}
}
#endregion
@@ -998,14 +1013,15 @@ namespace MPF.Core.UI.ViewModels
public void InitializeUIValues(bool removeEventHandlers, bool rescanDrives)
{
// Disable the dumping button
this.StartStopButtonEnabled = false;
StartStopButtonEnabled = false;
// Safely uncheck the parameters box, just in case
if (this.ParametersCheckBoxEnabled == true)
if (ParametersCheckBoxEnabled == true)
{
this.CanExecuteSelectionChanged = false;
this.ParametersCheckBoxEnabled = false;
this.CanExecuteSelectionChanged = true;
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
ParametersCheckBoxEnabled = false;
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
// Remove event handlers to ensure ordering
@@ -1015,7 +1031,7 @@ namespace MPF.Core.UI.ViewModels
// Populate the list of drives and determine the system
if (rescanDrives)
{
this.Status = "Creating drive list, please wait!";
Status = "Creating drive list, please wait!";
PopulateDrives();
}
else
@@ -1041,7 +1057,7 @@ namespace MPF.Core.UI.ViewModels
EnableEventHandlers();
// Enable the dumping button, if necessary
this.StartStopButtonEnabled = ShouldEnableDumpingButton();
StartStopButtonEnabled = ShouldEnableDumpingButton();
}
/// <summary>
@@ -1056,9 +1072,10 @@ namespace MPF.Core.UI.ViewModels
// Safely uncheck the parameters box, just in case
if (this.ParametersCheckBoxEnabled == true)
{
this.CanExecuteSelectionChanged = false;
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
this.DisableEventHandlers();
this.ParametersCheckBoxEnabled = false;
this.CanExecuteSelectionChanged = true;
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
// Remove event handlers to ensure ordering
@@ -1106,8 +1123,7 @@ namespace MPF.Core.UI.ViewModels
/// <param name="text">Text to write to the log</param>
private void Log(string text)
{
if (_logger != null)
_logger(LogLevel.USER, text);
_logger?.Invoke(LogLevel.USER, text);
}
/// <summary>
@@ -1122,8 +1138,7 @@ namespace MPF.Core.UI.ViewModels
/// <param name="text">Text to write to the log</param>
private void ErrorLog(string text)
{
if (_logger != null)
_logger(LogLevel.ERROR, text);
_logger?.Invoke(LogLevel.ERROR, text);
}
/// <summary>
@@ -1138,8 +1153,7 @@ namespace MPF.Core.UI.ViewModels
/// <param name="text">Text to write to the log</param>
private void SecretLog(string text)
{
if (_logger != null)
_logger(LogLevel.SECRET, text);
_logger?.Invoke(LogLevel.SECRET, text);
}
/// <summary>
@@ -1195,7 +1209,7 @@ namespace MPF.Core.UI.ViewModels
// If the drive is marked active, try to read from it
else if (this.CurrentDrive.MarkedActive)
{
VerboseLog($"Trying to detect media type for drive {this.CurrentDrive.Letter} [{this.CurrentDrive.DriveFormat}] using size and filesystem.. ");
VerboseLog($"Trying to detect media type for drive {this.CurrentDrive.Name} [{this.CurrentDrive.DriveFormat}] using size and filesystem.. ");
(MediaType? detectedMediaType, var errorMessage) = this.CurrentDrive.GetMediaType(this.CurrentSystem);
// If we got an error message, post it to the log
@@ -1255,7 +1269,7 @@ namespace MPF.Core.UI.ViewModels
}
else if (!this.Options.SkipSystemDetection)
{
VerboseLog($"Trying to detect system for drive {this.CurrentDrive.Letter}.. ");
VerboseLog($"Trying to detect system for drive {this.CurrentDrive.Name}.. ");
var currentSystem = this.CurrentDrive?.GetRedumpSystem(this.Options.DefaultSystem) ?? this.Options.DefaultSystem;
VerboseLogLn(currentSystem == null ? "unable to detect." : ($"detected {currentSystem.LongName()}."));
@@ -1423,7 +1437,7 @@ namespace MPF.Core.UI.ViewModels
// Catch this in case there's an input path issue
try
{
int driveIndex = Drives.Select(d => d.Letter).ToList().IndexOf(_environment.Parameters?.InputPath?[0] ?? default(char));
int driveIndex = Drives.Select(d => d.Name?[0] ?? '\0').ToList().IndexOf(_environment.Parameters?.InputPath?[0] ?? default);
this.CurrentDrive = (driveIndex != -1 ? Drives[driveIndex] : Drives[0]);
}
catch { }
@@ -1437,7 +1451,7 @@ namespace MPF.Core.UI.ViewModels
// Disable change handling
DisableEventHandlers();
this.OutputPath = InfoTool.NormalizeOutputPaths(_environment.Parameters?.OutputPath, true);
this.OutputPath = InfoTool.NormalizeOutputPaths(_environment.Parameters?.OutputPath, false);
if (MediaTypes != null)
{
@@ -1460,14 +1474,18 @@ namespace MPF.Core.UI.ViewModels
#endif
{
// Determine current environment, just in case
#if NET48
if (_environment == null)
_environment = DetermineEnvironment();
#else
_environment ??= DetermineEnvironment();
#endif
// If we don't have a valid drive
if (this.CurrentDrive == null || this.CurrentDrive.Letter == default(char))
if (this.CurrentDrive?.Name == null)
return (null, "No valid drive found!");
VerboseLogLn($"Scanning for copy protection in {this.CurrentDrive.Letter}");
VerboseLogLn($"Scanning for copy protection in {this.CurrentDrive.Name}");
var tempContent = this.Status;
this.Status = "Scanning for copy protection... this might take a while!";
@@ -1478,7 +1496,7 @@ namespace MPF.Core.UI.ViewModels
var progress = new Progress<ProtectionProgress>();
progress.ProgressChanged += ProgressUpdated;
var (protections, error) = await Protection.RunProtectionScanOnPath(this.CurrentDrive.Letter + ":\\", this.Options, progress);
var (protections, error) = await Protection.RunProtectionScanOnPath(this.CurrentDrive.Name, this.Options, progress);
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
@@ -1490,7 +1508,7 @@ namespace MPF.Core.UI.ViewModels
//}
if (string.IsNullOrEmpty(error))
LogLn($"Detected the following protections in {this.CurrentDrive.Letter}:\r\n\r\n{output}");
LogLn($"Detected the following protections in {this.CurrentDrive.Name}:\r\n\r\n{output}");
else
ErrorLogLn($"Path could not be scanned! Exception information:\r\n\r\n{error}");
@@ -1580,7 +1598,8 @@ namespace MPF.Core.UI.ViewModels
return Drives != null
&& Drives.Count > 0
&& this.CurrentSystem != null
&& this.CurrentMediaType != null;
&& this.CurrentMediaType != null
&& Tools.ProgramSupportsMedia(this.CurrentProgram, this.CurrentMediaType);
}
/// <summary>
@@ -1779,7 +1798,7 @@ namespace MPF.Core.UI.ViewModels
return true;
}
#endregion
#endregion
#region Progress Reporting
@@ -1794,7 +1813,11 @@ namespace MPF.Core.UI.ViewModels
{
try
{
#if NET48
value = value ?? string.Empty;
#else
value ??= string.Empty;
#endif
LogLn(value);
}
catch { }
@@ -1812,7 +1835,11 @@ namespace MPF.Core.UI.ViewModels
var message = value?.Message;
// Update the label with only the first line of output
#if NET48
if (message != null && message.Contains("\n"))
#else
if (message != null && message.Contains('\n'))
#endif
this.Status = value?.Message?.Split('\n')[0] + " (See log output)";
else
this.Status = value?.Message ?? string.Empty;
@@ -1838,6 +1865,6 @@ namespace MPF.Core.UI.ViewModels
VerboseLogLn(message);
}
#endregion
#endregion
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using MPF.Core.Data;
@@ -7,10 +8,32 @@ using SabreTools.RedumpLib.Web;
namespace MPF.Core.UI.ViewModels
{
public class OptionsViewModel
public class OptionsViewModel : INotifyPropertyChanged
{
#region Fields
/// <summary>
/// Title for the window
/// </summary>
#if NET48
public string Title
#else
public string? Title
#endif
{
get => _title;
set
{
_title = value;
TriggerPropertyChanged(nameof(Title));
}
}
#if NET48
private string _title;
#else
private string? _title;
#endif
/// <summary>
/// Current set of options
/// </summary>
@@ -21,7 +44,14 @@ namespace MPF.Core.UI.ViewModels
/// </summary>
public bool SavedSettings { get; set; }
#endregion
/// <inheritdoc/>
#if NET48
public event PropertyChangedEventHandler PropertyChanged;
#else
public event PropertyChangedEventHandler? PropertyChanged;
#endif
#endregion
#region Lists
@@ -77,5 +107,18 @@ namespace MPF.Core.UI.ViewModels
}
#endregion
#region Property Updates
/// <summary>
/// Trigger a property changed event
/// </summary>
private void TriggerPropertyChanged(string propertyName)
{
// If the property change event is initialized
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}

View File

@@ -18,7 +18,7 @@ namespace MPF.Core.Utilities
/// Process any standalone arguments for the program
/// </summary>
/// <returns>True if one of the arguments was processed, false otherwise</returns>
public static bool ProcessStandaloneArguments(string[] args)
public static bool? ProcessStandaloneArguments(string[] args)
{
// Help options
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
@@ -90,9 +90,9 @@ namespace MPF.Core.Utilities
/// Load the current set of options from application arguments
/// </summary>
#if NET48
public static (Options, string, int) LoadFromArguments(string[] args, int startIndex = 0)
public static (Options, SubmissionInfo, string, int) LoadFromArguments(string[] args, int startIndex = 0)
#else
public static (Options, string?, int) LoadFromArguments(string[] args, int startIndex = 0)
public static (Options, SubmissionInfo?, string?, int) LoadFromArguments(string[] args, int startIndex = 0)
#endif
{
// Create the output values with defaults
@@ -101,13 +101,18 @@ namespace MPF.Core.Utilities
RedumpUsername = null,
RedumpPassword = null,
InternalProgram = InternalProgram.NONE,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
CompressLogFiles = false,
DeleteUnnecessaryFiles = false,
};
// Create the submission info to return, if necessary
#if NET48
SubmissionInfo info = null;
string parsedPath = null;
#else
SubmissionInfo? info = null;
string? parsedPath = null;
#endif
@@ -116,17 +121,30 @@ namespace MPF.Core.Utilities
// If we have no arguments, just return
if (args == null || args.Length == 0)
return (options, null, 0);
return (options, null, null, 0);
// If we have an invalid start index, just return
if (startIndex < 0 || startIndex >= args.Length)
return (options, null, startIndex);
return (options, null, null, startIndex);
// Loop through the arguments and parse out values
for (; startIndex < args.Length; startIndex++)
{
// Use specific program
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
{
string internalProgram = args[startIndex].Split('=')[1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
startIndex++;
}
// Redump login
if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
{
string[] credentials = args[startIndex].Split('=')[1].Split(';');
options.RedumpUsername = credentials[0];
@@ -139,17 +157,10 @@ namespace MPF.Core.Utilities
startIndex += 2;
}
// Use specific program
else if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
// Pull all information (requires Redump login)
else if (args[startIndex].Equals("-a") || args[startIndex].Equals("--pull-all"))
{
string internalProgram = args[startIndex].Split('=')[1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
startIndex++;
options.PullAllInformation = true;
}
// Use a device path for physical checks
@@ -175,6 +186,25 @@ namespace MPF.Core.Utilities
protectFile = true;
}
// Include seed info file
else if (args[startIndex].StartsWith("-l=") || args[startIndex].StartsWith("--load-seed="))
{
string seedInfo = args[startIndex].Split('=')[1];
info = SubmissionInfoTool.CreateFromFile(seedInfo);
}
else if (args[startIndex] == "-l" || args[startIndex] == "--load-seed")
{
string seedInfo = args[startIndex + 1];
info = SubmissionInfoTool.CreateFromFile(seedInfo);
startIndex++;
}
// Add filename suffix
else if (args[startIndex].Equals("-x") || args[startIndex].Equals("--suffix"))
{
options.AddFilenameSuffix = true;
}
// Output submission JSON
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
{
@@ -187,6 +217,12 @@ namespace MPF.Core.Utilities
options.CompressLogFiles = true;
}
// Delete unnecessary files files
else if (args[startIndex].Equals("-d") || args[startIndex].Equals("--delete"))
{
options.DeleteUnnecessaryFiles = true;
}
// Default, we fall out
else
{
@@ -198,7 +234,7 @@ namespace MPF.Core.Utilities
options.ScanForProtection = scan && !string.IsNullOrWhiteSpace(parsedPath);
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrWhiteSpace(parsedPath);
return (options, parsedPath, startIndex);
return (options, info, parsedPath, startIndex);
}
/// <summary>
@@ -206,15 +242,20 @@ namespace MPF.Core.Utilities
/// </summary>
public static List<string> PrintSupportedArguments()
{
var supportedArguments = new List<string>();
supportedArguments.Add("-u, --use <program> Dumping program output type [REQUIRED]");
supportedArguments.Add("-c, --credentials <user> <pw> Redump username and password");
supportedArguments.Add("-p, --path <drivepath> Physical drive path for additional checks");
supportedArguments.Add("-s, --scan Enable copy protection scan (requires --path)");
supportedArguments.Add("-f, --protect-file Output protection to separate file (requires --scan)");
supportedArguments.Add("-j, --json Enable submission JSON output");
supportedArguments.Add("-z, --zip Enable log file compression");
var supportedArguments = new List<string>
{
"-u, --use <program> Dumping program output type [REQUIRED]",
"-c, --credentials <user> <pw> Redump username and password",
"-a, --pull-all Pull all information from Redump (requires --credentials)",
"-p, --path <drivepath> Physical drive path for additional checks",
"-s, --scan Enable copy protection scan (requires --path)",
"-f, --protect-file Output protection to separate file (requires --scan)",
"-l, --load-seed <path> Load a seed submission JSON for user information",
"-x, --suffix Enable adding filename suffix",
"-j, --json Enable submission JSON output",
"-z, --zip Enable log file compression",
"-d, --delete Enable unnecessary file deletion",
};
return supportedArguments;
}

View File

@@ -84,6 +84,7 @@ namespace MPF.Core.Utilities
return Result.Failure("Please select a valid system");
// If we're on an unsupported type, update the status accordingly
#if NET48
switch (type)
{
// Fully supported types
@@ -116,6 +117,128 @@ namespace MPF.Core.Utilities
default:
return Result.Failure($"{type.LongName()} media are not supported for dumping");
}
#else
return type switch
{
// Fully supported types
MediaType.BluRay
or MediaType.CDROM
or MediaType.DVD
or MediaType.FloppyDisk
or MediaType.HardDisk
or MediaType.CompactFlash
or MediaType.SDCard
or MediaType.FlashDrive
or MediaType.HDDVD => Result.Success($"{type.LongName()} ready to dump"),
// Partially supported types
MediaType.GDROM
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => Result.Success($"{type.LongName()} partially supported for dumping"),
// Special case for other supported tools
MediaType.UMD => Result.Failure($"{type.LongName()} supported for submission info parsing"),
// Specifically unknown type
MediaType.NONE => Result.Failure($"Please select a valid media type"),
// Undumpable but recognized types
_ => Result.Failure($"{type.LongName()} media are not supported for dumping"),
};
#endif
}
/// <summary>
/// Returns false if a given InternalProgram does not support a given MediaType
/// </summary>
public static bool ProgramSupportsMedia(InternalProgram program, MediaType? type)
{
// If the media type is not set, return false
if (type == null || type == MediaType.NONE)
return false;
#if NET48
switch (program)
{
case InternalProgram.Redumper:
switch (type)
{
// Formats considered at least partially dumpable by Redumper
case MediaType.CDROM:
case MediaType.DVD:
case MediaType.GDROM:
return true;
// All other formats considered unsupported
default:
return false;
}
case InternalProgram.Aaru:
case InternalProgram.DiscImageCreator:
switch (type)
{
// Formats considered at least partially supported
case MediaType.BluRay:
case MediaType.CDROM:
case MediaType.DVD:
case MediaType.FloppyDisk:
case MediaType.HardDisk:
case MediaType.CompactFlash:
case MediaType.SDCard:
case MediaType.FlashDrive:
case MediaType.HDDVD:
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
return true;
// All other formats considered unsupported
default:
return false;
}
// All other InternalPrograms are not used for dumping
default:
return false;
}
#else
switch (program)
{
case InternalProgram.Redumper:
return type switch
{
// Formats considered at least partially supported by Redumper
MediaType.CDROM
or MediaType.DVD
or MediaType.GDROM => true,
// All other formats considered unsupported
_ => false,
};
case InternalProgram.Aaru:
case InternalProgram.DiscImageCreator:
return type switch
{
// Formats considered at least partially supported by MPF
MediaType.BluRay
or MediaType.CDROM
or MediaType.DVD
or MediaType.GDROM
or MediaType.FloppyDisk
or MediaType.CompactFlash
or MediaType.SDCard
or MediaType.FlashDrive
or MediaType.HardDisk
or MediaType.HDDVD
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => true,
// All other formats considered unsupported
_ => false,
};
// All other InternalPrograms are not supported for dumping
default:
return false;
}
#endif
}
#endregion
@@ -143,7 +266,7 @@ namespace MPF.Core.Utilities
if (assemblyVersion == null)
return (false, "Assembly version could not be determined", null);
string version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}" + (assemblyVersion.Build != 0 ? $".{assemblyVersion.Build}" : string.Empty);
string version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}.{assemblyVersion.Build}";
// Get the latest tag from GitHub
var (tag, url) = GetRemoteVersionAndUrl();
@@ -197,7 +320,7 @@ namespace MPF.Core.Utilities
#endif
{
#if NET48
using (System.Net.WebClient wc = new System.Net.WebClient())
using (var wc = new System.Net.WebClient())
{
wc.Headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0";
@@ -211,7 +334,7 @@ namespace MPF.Core.Utilities
return (latestTag, releaseUrl);
}
#else
using (System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient())
using (var hc = new System.Net.Http.HttpClient())
{
// TODO: Figure out a better way than having this hardcoded...
string url = "https://api.github.com/repos/SabreTools/MPF/releases/latest";

View File

@@ -43,21 +43,37 @@ namespace MPF.Test.Core.Converters
/// Generate a test set of DriveType values
/// </summary>
/// <returns>MemberData-compatible list of DriveType values</returns>
#if NET48
public static List<object[]> GenerateDriveTypeMappingTestData()
#else
public static List<object?[]> GenerateDriveTypeMappingTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (DriveType driveType in Enum.GetValues(typeof(DriveType)))
{
if (_mappableDriveTypes.Contains(driveType))
#if NET48
testData.Add(new object[] { driveType, false });
#else
testData.Add(new object?[] { driveType, false });
#endif
else
#if NET48
testData.Add(new object[] { driveType, true });
#else
testData.Add(new object?[] { driveType, true });
#endif
}
return testData;
}
#endregion
#endregion
#region Convert to Long Name
@@ -79,18 +95,30 @@ namespace MPF.Test.Core.Converters
/// Generate a test set of InternalProgram values
/// </summary>
/// <returns>MemberData-compatible list of InternalProgram values</returns>
#if NET48
public static List<object[]> GenerateInternalProgramTestData()
#else
public static List<object?[]> GenerateInternalProgramTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null } };
#else
var testData = new List<object?[]>() { new object?[] { null } };
#endif
foreach (InternalProgram? internalProgram in Enum.GetValues(typeof(InternalProgram)))
{
#if NET48
testData.Add(new object[] { internalProgram });
#else
testData.Add(new object?[] { internalProgram });
#endif
}
return testData;
}
#endregion
#endregion
// TODO: Add from-string tests
}

View File

@@ -1,115 +0,0 @@
using MPF.Core.Data;
using Xunit;
namespace MPF.Test.Core.Data
{
public class XgdInfoTests
{
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("1234567")]
[InlineData("1234567\0")]
[InlineData("123456789")]
public void UnmatchedStringTests(string invalidString)
{
XgdInfo xgdInfo = new XgdInfo(invalidString);
Assert.False(xgdInfo.Initialized);
}
[Theory]
[InlineData("AV00100W", "AV", "001", "00", 'W')]
[InlineData("AV00100W\0", "AV", "001", "00", 'W')]
public void XGD1ValidTests(string validString, string publisher, string gameId, string version, char regionIdentifier)
{
XgdInfo xgdInfo = new XgdInfo(validString);
Assert.True(xgdInfo.Initialized);
Assert.Equal(publisher, xgdInfo.XMID.PublisherIdentifier);
Assert.Equal(gameId, xgdInfo.XMID.GameID);
Assert.Equal(version, xgdInfo.XMID.VersionNumber);
Assert.Equal(regionIdentifier, xgdInfo.XMID.RegionIdentifier);
}
[Theory]
// Invalid publisher identifier
[InlineData("ZZ000000")]
[InlineData("ZZ000000\0")]
// Invalid region identifier
[InlineData("AV00000Z")]
[InlineData("AV00000Z\0")]
public void XGD1InvalidTests(string invalidString)
{
XgdInfo xgdInfo = new XgdInfo(invalidString);
Assert.False(xgdInfo.Initialized);
}
[Theory]
[InlineData("AV200100W0F11", "AV", "001", "00", 'W', "0", 'F', "11", null)]
[InlineData("AV200100W0F11\0", "AV", "001", "00", 'W', "0", 'F', "11", null)]
[InlineData("AV200100W01F11", "AV", "001", "00", 'W', "01", 'F', "11", null)]
[InlineData("AV200100W01F11\0", "AV", "001", "00", 'W', "01", 'F', "11", null)]
[InlineData("AV200100W0F11DEADBEEF", "AV", "001", "00", 'W', "0", 'F', "11", "DEADBEEF")]
[InlineData("AV200100W0F11DEADBEEF\0", "AV", "001", "00", 'W', "0", 'F', "11", "DEADBEEF")]
[InlineData("AV200100W01F11DEADBEEF", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
[InlineData("AV200100W01F11DEADBEEF\0", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
public void XGD23ValidTests(string validString, string publisher, string gameId, string sku, char regionIdentifier, string baseVersion, char mediaSubtype, string discNumber, string cert)
{
XgdInfo xgdInfo = new XgdInfo(validString);
Assert.True(xgdInfo.Initialized);
Assert.Equal(publisher, xgdInfo.XeMID.PublisherIdentifier);
Assert.Equal('2', xgdInfo.XeMID.PlatformIdentifier);
Assert.Equal(gameId, xgdInfo.XeMID.GameID);
Assert.Equal(sku, xgdInfo.XeMID.SKU);
Assert.Equal(regionIdentifier, xgdInfo.XeMID.RegionIdentifier);
Assert.Equal(baseVersion, xgdInfo.XeMID.BaseVersion);
Assert.Equal(mediaSubtype, xgdInfo.XeMID.MediaSubtypeIdentifier);
Assert.Equal(discNumber, xgdInfo.XeMID.DiscNumberIdentifier);
Assert.Equal(cert, xgdInfo.XeMID.CertificationSubmissionIdentifier);
}
[Theory]
// Invalid publisher identifier
[InlineData("ZZ00000000000")]
[InlineData("ZZ00000000000\0")]
[InlineData("ZZ000000000000")]
[InlineData("ZZ000000000000\0")]
[InlineData("ZZ0000000000000000000")]
[InlineData("ZZ0000000000000000000\0")]
[InlineData("ZZ00000000000000000000")]
[InlineData("ZZ00000000000000000000\0")]
// Invalid platform identifier
[InlineData("AV90000000000")]
[InlineData("AV90000000000\0")]
[InlineData("AV900000000000")]
[InlineData("AV900000000000\0")]
[InlineData("AV9000000000000000000")]
[InlineData("AV9000000000000000000\0")]
[InlineData("AV90000000000000000000")]
[InlineData("AV90000000000000000000\0")]
// Invalid region identifier
[InlineData("AV200000Z0000")]
[InlineData("AV200000Z0000\0")]
[InlineData("AV200000Z00000")]
[InlineData("AV200000Z00000\0")]
[InlineData("AV200000Z000000000000")]
[InlineData("AV200000Z000000000000\0")]
[InlineData("AV200000Z0000000000000")]
[InlineData("AV200000Z0000000000000\0")]
// Invalid media subtype identifier
[InlineData("AV200000W0A00")]
[InlineData("AV200000W0A00\0")]
[InlineData("AV200000W00A00")]
[InlineData("AV200000W00A00\0")]
[InlineData("AV200000W00A000000000")]
[InlineData("AV200000W00A000000000\0")]
[InlineData("AV200000W00A0000000000")]
[InlineData("AV200000W00A0000000000\0")]
public void XGD23InvalidTests(string invalidString)
{
XgdInfo xgdInfo = new XgdInfo(invalidString);
Assert.False(xgdInfo.Initialized);
}
}
}

View File

@@ -143,15 +143,31 @@ namespace MPF.Test.Core.Utilities
/// Generate a test set of MediaType values that support drive speeds
/// </summary>
/// <returns>MemberData-compatible list of MediaType values</returns>
#if NET48
public static List<object[]> GenerateSupportDriveSpeedsTestData()
#else
public static List<object?[]> GenerateSupportDriveSpeedsTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, false } };
#else
var testData = new List<object?[]>() { new object?[] { null, false } };
#endif
foreach (MediaType mediaType in Enum.GetValues(typeof(MediaType)))
{
if (_supportDriveSpeeds.Contains(mediaType))
#if NET48
testData.Add(new object[] { mediaType, true });
#else
testData.Add(new object?[] { mediaType, true });
#endif
else
#if NET48
testData.Add(new object[] { mediaType, false });
#else
testData.Add(new object?[] { mediaType, false });
#endif
}
return testData;
@@ -161,15 +177,31 @@ namespace MPF.Test.Core.Utilities
/// Generate a test set of RedumpSystem values that are considered Audio
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
#if NET48
public static List<object[]> GenerateAudioSystemsTestData()
#else
public static List<object?[]> GenerateAudioSystemsTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, false } };
#else
var testData = new List<object?[]>() { new object?[] { null, false } };
#endif
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_audioSystems.Contains(redumpSystem))
#if NET48
testData.Add(new object[] { redumpSystem, true });
#else
testData.Add(new object?[] { redumpSystem, true });
#endif
else
#if NET48
testData.Add(new object[] { redumpSystem, false });
#else
testData.Add(new object?[] { redumpSystem, false });
#endif
}
return testData;
@@ -179,15 +211,31 @@ namespace MPF.Test.Core.Utilities
/// Generate a test set of RedumpSystem values that are considered markers
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
#if NET48
public static List<object[]> GenerateMarkerSystemsTestData()
#else
public static List<object?[]> GenerateMarkerSystemsTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, false } };
#else
var testData = new List<object?[]>() { new object?[] { null, false } };
#endif
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_markerSystems.Contains(redumpSystem))
#if NET48
testData.Add(new object[] { redumpSystem, true });
#else
testData.Add(new object?[] { redumpSystem, true });
#endif
else
#if NET48
testData.Add(new object[] { redumpSystem, false });
#else
testData.Add(new object?[] { redumpSystem, false });
#endif
}
return testData;
@@ -197,15 +245,31 @@ namespace MPF.Test.Core.Utilities
/// Generate a test set of RedumpSystem values that are considered markers
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
#if NET48
public static List<object[]> GenerateReversedRingcodeSystemsTestData()
#else
public static List<object?[]> GenerateReversedRingcodeSystemsTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, false } };
#else
var testData = new List<object?[]>() { new object?[] { null, false } };
#endif
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_reverseRingcodeSystems.Contains(redumpSystem))
#if NET48
testData.Add(new object[] { redumpSystem, true });
#else
testData.Add(new object?[] { redumpSystem, true });
#endif
else
#if NET48
testData.Add(new object[] { redumpSystem, false });
#else
testData.Add(new object?[] { redumpSystem, false });
#endif
}
return testData;
@@ -215,15 +279,31 @@ namespace MPF.Test.Core.Utilities
/// Generate a test set of RedumpSystem values that are considered XGD
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
#if NET48
public static List<object[]> GenerateXGDSystemsTestData()
#else
public static List<object?[]> GenerateXGDSystemsTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, false } };
#else
var testData = new List<object?[]>() { new object?[] { null, false } };
#endif
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_xgdSystems.Contains(redumpSystem))
#if NET48
testData.Add(new object[] { redumpSystem, true });
#else
testData.Add(new object?[] { redumpSystem, true });
#endif
else
#if NET48
testData.Add(new object[] { redumpSystem, false });
#else
testData.Add(new object?[] { redumpSystem, false });
#endif
}
return testData;

View File

@@ -42,10 +42,14 @@ namespace MPF.Test.Library
long layerbreak,
long layerbreak2,
long layerbreak3,
#if NET48
string expected)
#else
string? expected)
#endif
{
// TODO: Add tests around BDU
string actual = InfoTool.GetFixedMediaType(mediaType, null, size, layerbreak, layerbreak2, layerbreak3);
var actual = InfoTool.GetFixedMediaType(mediaType, null, size, layerbreak, layerbreak2, layerbreak3);
Assert.Equal(expected, actual);
}
@@ -58,7 +62,11 @@ namespace MPF.Test.Library
[InlineData("superhero\\blah.rev.bin", "superhero\\blah.rev.bin")]
[InlineData("super&hero\\blah.bin", "super&hero\\blah.bin")]
[InlineData("superhero\\blah&foo.bin", "superhero\\blah&foo.bin")]
#if NET48
public void NormalizeOutputPathsTest(string outputPath, string expectedPath)
#else
public void NormalizeOutputPathsTest(string? outputPath, string? expectedPath)
#endif
{
if (!string.IsNullOrWhiteSpace(expectedPath))
expectedPath = Path.GetFullPath(expectedPath);
@@ -71,7 +79,7 @@ namespace MPF.Test.Library
public void ProcessSpecialFieldsCompleteTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
var info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
@@ -119,7 +127,7 @@ namespace MPF.Test.Library
public void ProcessSpecialFieldsNullObjectTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
var info = new SubmissionInfo()
{
CommonDiscInfo = null,
};
@@ -135,7 +143,7 @@ namespace MPF.Test.Library
public void ProcessSpecialFieldsNullCommentsContentsTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
var info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
@@ -183,7 +191,7 @@ namespace MPF.Test.Library
public void ProcessSpecialFieldsNullDictionariesTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
var info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{

View File

@@ -5,6 +5,10 @@
<IsPackable>false</IsPackable>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF\MPF.csproj" />
</ItemGroup>
@@ -13,19 +17,20 @@
<PackageReference Include="Microsoft.CodeCoverage" Version="17.7.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="psxt001z" Version="0.21.0-beta1" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="xunit" Version="2.5.1" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="1.3.0" />
<PackageReference Include="xunit.assert" Version="2.5.1" />
<PackageReference Include="xunit.core" Version="2.5.1" />
<PackageReference Include="xunit.extensibility.core" Version="2.5.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.5.1" />
<PackageReference Include="xunit.runner.console" Version="2.5.1">
<PackageReference Include="xunit.analyzers" Version="1.4.0" />
<PackageReference Include="xunit.assert" Version="2.5.3" />
<PackageReference Include="xunit.core" Version="2.5.3" />
<PackageReference Include="xunit.extensibility.core" Version="2.5.3" />
<PackageReference Include="xunit.extensibility.execution" Version="2.5.3" />
<PackageReference Include="xunit.runner.console" Version="2.5.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.1">
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using MPF.Core.Data;
using MPF.Core.Modules.DiscImageCreator;
using SabreTools.RedumpLib.Data;
@@ -20,7 +21,7 @@ namespace MPF.Test.Modules
public void ParametersFromSystemAndTypeTest(RedumpSystem? knownSystem, MediaType? mediaType, string expected)
{
var options = new Options();
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
Assert.Equal(expected, actual.BaseCommand);
}
@@ -31,9 +32,9 @@ namespace MPF.Test.Modules
public void ParametersFromOptionsSpecialDefaultTest(RedumpSystem? knownSystem, MediaType? mediaType,string[] expected)
{
var options = new Options();
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
}
@@ -44,9 +45,9 @@ namespace MPF.Test.Modules
public void ParametersFromOptionsC2RereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadC2, string[] expected)
{
var options = new Options { DICRereadCount = rereadC2 };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
@@ -64,9 +65,9 @@ namespace MPF.Test.Modules
public void ParametersFromOptionsDVDRereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadDVDBD, string[] expected)
{
var options = new Options { DICDVDRereadCount = rereadDVDBD };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
@@ -88,9 +89,9 @@ namespace MPF.Test.Modules
public void ParametersFromOptionsMultiSectorReadTest(RedumpSystem? knownSystem, MediaType? mediaType, bool multiSectorRead, string[] expected)
{
var options = new Options { DICMultiSectorRead = multiSectorRead };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (expectedSet.Count != 1 && multiSectorRead)
@@ -109,9 +110,9 @@ namespace MPF.Test.Modules
public void ParametersFromOptionsParanoidModeTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoidMode, string[] expected)
{
var options = new Options { DICParanoidMode = paranoidMode };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (paranoidMode)
@@ -160,9 +161,13 @@ namespace MPF.Test.Modules
[InlineData(MediaType.FloppyDisk, ".img")]
[InlineData(MediaType.Cassette, ".wav")]
[InlineData(MediaType.NONE, null)]
#if NET48
public void MediaTypeToExtensionTest(MediaType? mediaType, string expected)
#else
public void MediaTypeToExtensionTest(MediaType? mediaType, string? expected)
#endif
{
string actual = Converters.Extension(mediaType);
var actual = Converters.Extension(mediaType);
Assert.Equal(expected, actual);
}
@@ -214,7 +219,7 @@ namespace MPF.Test.Modules
Assert.Equal(expected, actual);
}
#endregion
#endregion
[Fact]
public void DiscImageCreatorAudioParametersTest()
@@ -226,7 +231,7 @@ namespace MPF.Test.Modules
Assert.NotNull(parametersObject);
// Validate that the same set of parameters are generated on the output
string newParameters = parametersObject.GenerateParameters();
var newParameters = parametersObject.GenerateParameters();
Assert.NotNull(newParameters);
Assert.Equal(originalParameters, newParameters);
}
@@ -241,7 +246,7 @@ namespace MPF.Test.Modules
Assert.NotNull(parametersObject);
// Validate that the same set of parameters are generated on the output
string newParameters = parametersObject.GenerateParameters();
var newParameters = parametersObject.GenerateParameters();
Assert.NotNull(newParameters);
Assert.Equal(originalParameters, newParameters);
}
@@ -253,7 +258,7 @@ namespace MPF.Test.Modules
/// <returns>HashSet representing the strings</returns>
private static HashSet<string> GenerateUsedKeys(Parameters parameters)
{
HashSet<string> usedKeys = new HashSet<string>();
var usedKeys = new HashSet<string>();
if (parameters?.Keys == null)
return usedKeys;

View File

@@ -95,15 +95,31 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of DiscType values
/// </summary>
/// <returns>MemberData-compatible list of DiscType values</returns>
#if NET48
public static List<object[]> GenerateDiscTypeMappingTestData()
#else
public static List<object?[]> GenerateDiscTypeMappingTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (DiscType? discType in Enum.GetValues(typeof(DiscType)))
{
if (_mappableDiscTypes.Contains(discType))
#if NET48
testData.Add(new object[] { discType, false });
#else
testData.Add(new object?[] { discType, false });
#endif
else
#if NET48
testData.Add(new object[] { discType, true });
#else
testData.Add(new object?[] { discType, true });
#endif
}
return testData;
@@ -113,12 +129,24 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of RedumpSystem values
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
#if NET48
public static List<object[]> GenerateRedumpSystemMappingTestData()
#else
public static List<object?[]> GenerateRedumpSystemMappingTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null } };
#else
var testData = new List<object?[]>() { new object?[] { null } };
#endif
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
#if NET48
testData.Add(new object[] { redumpSystem });
#else
testData.Add(new object?[] { redumpSystem });
#endif
}
return testData;
@@ -128,16 +156,32 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of mappable media types
/// </summary>
/// <returns>MemberData-compatible list of MediaTypes</returns>
#if NET48
public static List<object[]> GenerateMediaTypeMappingTestData()
#else
public static List<object?[]> GenerateMediaTypeMappingTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (MediaType? mediaType in Enum.GetValues(typeof(MediaType)))
{
if (_mappableMediaTypes.Contains(mediaType))
#if NET48
testData.Add(new object[] { mediaType, false });
#else
testData.Add(new object?[] { mediaType, false });
#endif
else
#if NET48
testData.Add(new object[] { mediaType, true });
#else
testData.Add(new object?[] { mediaType, true });
#endif
}
return testData;
@@ -156,7 +200,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateDiscCategoryTestData))]
public void DiscCategoryLongNameTest(DiscCategory? discCategory, bool expectNull)
{
string actual = discCategory.LongName();
var actual = discCategory.LongName();
if (expectNull)
Assert.Null(actual);
@@ -168,12 +212,24 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of DiscCategory values
/// </summary>
/// <returns>MemberData-compatible list of DiscCategory values</returns>
#if NET48
public static List<object[]> GenerateDiscCategoryTestData()
#else
public static List<object?[]> GenerateDiscCategoryTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (DiscCategory? discCategory in Enum.GetValues(typeof(DiscCategory)))
{
#if NET48
testData.Add(new object[] { discCategory, false });
#else
testData.Add(new object?[] { discCategory, false });
#endif
}
return testData;
@@ -192,7 +248,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateDiscTypeTestData))]
public void DiscTypeLongNameTest(DiscType? discType, bool expectNull)
{
string actual = discType.LongName();
var actual = discType.LongName();
if (expectNull)
Assert.Null(actual);
@@ -204,15 +260,31 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of DiscType values
/// </summary>
/// <returns>MemberData-compatible list of DiscType values</returns>
#if NET48
public static List<object[]> GenerateDiscTypeTestData()
#else
public static List<object?[]> GenerateDiscTypeTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (DiscType? discType in Enum.GetValues(typeof(DiscType)))
{
if (discType == DiscType.NONE)
#if NET48
testData.Add(new object[] { discType, true });
#else
testData.Add(new object?[] { discType, true });
#endif
else
#if NET48
testData.Add(new object[] { discType, false });
#else
testData.Add(new object?[] { discType, false });
#endif
}
return testData;
@@ -231,7 +303,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateLanguageTestData))]
public void LanguageLongNameTest(Language? language, bool expectNull)
{
string actual = language.LongName();
var actual = language.LongName();
if (expectNull)
Assert.Null(actual);
@@ -248,7 +320,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateLanguageTestData))]
public void LanguageShortNameTest(Language? language, bool expectNull)
{
string actual = language.ShortName();
var actual = language.ShortName();
if (expectNull)
Assert.Null(actual);
@@ -268,7 +340,7 @@ namespace MPF.Test.RedumpLib
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
string code = language.TwoLetterCode();
var code = language.TwoLetterCode();
if (string.IsNullOrWhiteSpace(code))
continue;
@@ -295,7 +367,7 @@ namespace MPF.Test.RedumpLib
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
string code = language.ThreeLetterCode();
var code = language.ThreeLetterCode();
if (string.IsNullOrWhiteSpace(code))
continue;
@@ -322,7 +394,7 @@ namespace MPF.Test.RedumpLib
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
string code = language.ThreeLetterCodeAlt();
var code = language.ThreeLetterCodeAlt();
if (string.IsNullOrWhiteSpace(code))
continue;
@@ -341,12 +413,24 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of Language values
/// </summary>
/// <returns>MemberData-compatible list of Language values</returns>
#if NET48
public static List<object[]> GenerateLanguageTestData()
#else
public static List<object?[]> GenerateLanguageTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (Language? language in Enum.GetValues(typeof(Language)))
{
#if NET48
testData.Add(new object[] { language, false });
#else
testData.Add(new object?[] { language, false });
#endif
}
return testData;
@@ -365,7 +449,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateLanguageSelectionTestData))]
public void LanguageSelectionLongNameTest(LanguageSelection? languageSelection, bool expectNull)
{
string actual = languageSelection.LongName();
var actual = languageSelection.LongName();
if (expectNull)
Assert.Null(actual);
@@ -377,12 +461,24 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of LanguageSelection values
/// </summary>
/// <returns>MemberData-compatible list of LanguageSelection values</returns>
#if NET48
public static List<object[]> GenerateLanguageSelectionTestData()
#else
public static List<object?[]> GenerateLanguageSelectionTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (LanguageSelection? languageSelection in Enum.GetValues(typeof(LanguageSelection)))
{
#if NET48
testData.Add(new object[] { languageSelection, false });
#else
testData.Add(new object?[] { languageSelection, false });
#endif
}
return testData;
@@ -401,7 +497,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateMediaTypeTestData))]
public void MediaTypeLongNameTest(MediaType? mediaType, bool expectNull)
{
string actual = mediaType.LongName();
var actual = mediaType.LongName();
if (expectNull)
Assert.Null(actual);
@@ -418,7 +514,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateMediaTypeTestData))]
public void MediaTypeShortNameTest(MediaType? mediaType, bool expectNull)
{
string actual = mediaType.ShortName();
var actual = mediaType.ShortName();
if (expectNull)
Assert.Null(actual);
@@ -430,12 +526,24 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of MediaType values
/// </summary>
/// <returns>MemberData-compatible list of MediaType values</returns>
#if NET48
public static List<object[]> GenerateMediaTypeTestData()
#else
public static List<object?[]> GenerateMediaTypeTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (MediaType? mediaType in Enum.GetValues(typeof(MediaType)))
{
#if NET48
testData.Add(new object[] { mediaType, false });
#else
testData.Add(new object?[] { mediaType, false });
#endif
}
return testData;
@@ -454,7 +562,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateRegionTestData))]
public void RegionLongNameTest(Region? region, bool expectNull)
{
string actual = region.LongName();
var actual = region.LongName();
if (expectNull)
Assert.Null(actual);
@@ -471,7 +579,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateRegionTestData))]
public void RegionShortNameTest(Region? region, bool expectNull)
{
string actual = region.ShortName();
var actual = region.ShortName();
if (expectNull)
Assert.Null(actual);
@@ -491,7 +599,7 @@ namespace MPF.Test.RedumpLib
int totalCount = 0;
foreach (Region? region in fullRegions)
{
string code = region.ShortName();
var code = region.ShortName();
if (string.IsNullOrWhiteSpace(code))
continue;
@@ -510,12 +618,24 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of Region values
/// </summary>
/// <returns>MemberData-compatible list of Region values</returns>
#if NET48
public static List<object[]> GenerateRegionTestData()
#else
public static List<object?[]> GenerateRegionTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (Region? region in Enum.GetValues(typeof(Region)))
{
#if NET48
testData.Add(new object[] { region, false });
#else
testData.Add(new object?[] { region, false });
#endif
}
return testData;
@@ -534,7 +654,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateSiteCodeTestData))]
public void SiteCodeLongNameTest(SiteCode? siteCode, bool expectNull)
{
string actual = siteCode.LongName();
var actual = siteCode.LongName();
if (expectNull)
Assert.Null(actual);
@@ -551,7 +671,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateSiteCodeTestData))]
public void SiteCodeShortNameTest(SiteCode? siteCode, bool expectNull)
{
string actual = siteCode.ShortName();
var actual = siteCode.ShortName();
if (expectNull)
Assert.Null(actual);
@@ -563,12 +683,24 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of SiteCode values
/// </summary>
/// <returns>MemberData-compatible list of SiteCode values</returns>
#if NET48
public static List<object[]> GenerateSiteCodeTestData()
#else
public static List<object?[]> GenerateSiteCodeTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (SiteCode? siteCode in Enum.GetValues(typeof(SiteCode)))
{
#if NET48
testData.Add(new object[] { siteCode, false });
#else
testData.Add(new object?[] { siteCode, false });
#endif
}
return testData;
@@ -587,7 +719,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateRedumpSystemTestData))]
public void RedumpSystemLongNameTest(RedumpSystem? redumpSystem, bool expectNull)
{
string actual = redumpSystem.LongName();
var actual = redumpSystem.LongName();
if (expectNull)
Assert.Null(actual);
@@ -622,16 +754,28 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of RedumpSystem values
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
#if NET48
public static List<object[]> GenerateRedumpSystemTestData()
#else
public static List<object?[]> GenerateRedumpSystemTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
// We want to skip all markers for this
if (redumpSystem.IsMarker())
continue;
#if NET48
testData.Add(new object[] { redumpSystem, false });
#else
testData.Add(new object?[] { redumpSystem, false });
#endif
}
return testData;
@@ -650,7 +794,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateSystemCategoryTestData))]
public void SystemCategoryLongNameTest(SystemCategory? systemCategory, bool expectNull)
{
string actual = systemCategory.LongName();
var actual = systemCategory.LongName();
if (expectNull)
Assert.Null(actual);
@@ -662,15 +806,31 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of SystemCategory values
/// </summary>
/// <returns>MemberData-compatible list of SystemCategory values</returns>
#if NET48
public static List<object[]> GenerateSystemCategoryTestData()
#else
public static List<object?[]> GenerateSystemCategoryTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, true } };
#else
var testData = new List<object?[]>() { new object?[] { null, true } };
#endif
foreach (SystemCategory? systemCategory in Enum.GetValues(typeof(SystemCategory)))
{
if (systemCategory == SystemCategory.NONE)
#if NET48
testData.Add(new object[] { systemCategory, true });
#else
testData.Add(new object?[] { systemCategory, true });
#endif
else
#if NET48
testData.Add(new object[] { systemCategory, false });
#else
testData.Add(new object?[] { systemCategory, false });
#endif
}
return testData;
@@ -701,12 +861,24 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of YesNo values
/// </summary>
/// <returns>MemberData-compatible list of YesNo values</returns>
#if NET48
public static List<object[]> GenerateYesNoTestData()
#else
public static List<object?[]> GenerateYesNoTestData()
#endif
{
#if NET48
var testData = new List<object[]>() { new object[] { null, false } };
#else
var testData = new List<object?[]>() { new object?[] { null, false } };
#endif
foreach (YesNo? yesNo in Enum.GetValues(typeof(YesNo)))
{
#if NET48
testData.Add(new object[] { yesNo, false });
#else
testData.Add(new object?[] { yesNo, false });
#endif
}
return testData;

View File

@@ -30,11 +30,18 @@ namespace MPF.UI.Core
}
}
#if NET48
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
#else
public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
#endif
{
// If it's an IElement but ends up null
var element = value as IElement;
if (element == null)
#if NET48
if (!(value is IElement element))
#else
if (value is not IElement element)
#endif
return null;
switch (element)

View File

@@ -18,7 +18,7 @@ namespace WPFCustomMessageBox
/// Global parameter to enable (true) or disable (false) the removal of the title bar icon.
/// If you are using a custom window style, the icon removal may cause issues like displaying two title bar (the default windows one and the custom one).
/// </summary>
public static bool RemoveTitleBarIcon = true;
private static readonly bool RemoveTitleBarIcon = true;
/// <summary>
/// Displays a message box that has a message and returns a result.
@@ -173,7 +173,11 @@ namespace WPFCustomMessageBox
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
#if NET48
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
#else
public static MessageBoxResult Show(Window owner, string? messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
#endif
{
switch (button)
{
@@ -515,11 +519,15 @@ namespace WPFCustomMessageBox
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
#if NET48
private static MessageBoxResult ShowOKMessage(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
#else
private static MessageBoxResult ShowOKMessage(Window? owner, string? messageBoxText, string caption, string? okButtonText, string? cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
#endif
{
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.OK : MessageBoxButton.OKCancel;
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
var msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
if (!string.IsNullOrEmpty(okButtonText))
msg.OkButtonText = okButtonText;
if (!string.IsNullOrEmpty(cancelButtonText))
@@ -545,11 +553,15 @@ namespace WPFCustomMessageBox
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
#if NET48
private static MessageBoxResult ShowYesNoMessage(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
#else
private static MessageBoxResult ShowYesNoMessage(Window? owner, string? messageBoxText, string caption, string? yesButtonText, string? noButtonText, string? cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
#endif
{
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.YesNo : MessageBoxButton.YesNoCancel;
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
var msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
if (!string.IsNullOrEmpty(yesButtonText))
msg.YesButtonText = yesButtonText;
if (!string.IsNullOrEmpty(noButtonText))
@@ -566,14 +578,14 @@ namespace WPFCustomMessageBox
{
if (timeout.HasValue && timeout.Value <= 0)
{
throw new ArgumentOutOfRangeException("timeout", string.Format("Timeout must be greater than 0."));
throw new ArgumentOutOfRangeException(nameof(timeout), string.Format("Timeout must be greater than 0."));
}
if (timeout.HasValue)
{
//System.Threading.Timer timer = null;
//timer = new System.Threading.Timer(s => { msg.Close(); timer.Dispose(); }, null, timeout.Value, System.Threading.Timeout.Infinite);
System.Timers.Timer timer = new System.Timers.Timer(timeout.Value) { AutoReset = false };
var timer = new System.Timers.Timer(timeout.Value) { AutoReset = false };
timer.Elapsed += delegate {
Application.Current.Dispatcher.Invoke(new Action(() =>
{

View File

@@ -10,9 +10,13 @@ namespace WPFCustomMessageBox
/// </summary>
internal partial class CustomMessageBoxWindow : Window
{
private bool _removeTitleBarIcon = true;
private readonly bool _removeTitleBarIcon = true;
#if NET48
public string Caption
#else
public string? Caption
#endif
{
get
{
@@ -24,7 +28,11 @@ namespace WPFCustomMessageBox
}
}
#if NET48
public string Message
#else
public string? Message
#endif
{
get
{
@@ -36,7 +44,11 @@ namespace WPFCustomMessageBox
}
}
#if NET48
public string OkButtonText
#else
public string? OkButtonText
#endif
{
get
{
@@ -48,7 +60,11 @@ namespace WPFCustomMessageBox
}
}
#if NET48
public string CancelButtonText
#else
public string? CancelButtonText
#endif
{
get
{
@@ -60,7 +76,11 @@ namespace WPFCustomMessageBox
}
}
#if NET48
public string YesButtonText
#else
public string? YesButtonText
#endif
{
get
{
@@ -72,7 +92,11 @@ namespace WPFCustomMessageBox
}
}
#if NET48
public string NoButtonText
#else
public string? NoButtonText
#endif
{
get
{
@@ -86,7 +110,11 @@ namespace WPFCustomMessageBox
public MessageBoxResult Result { get; set; }
#if NET48
internal CustomMessageBoxWindow(Window owner, string message, string caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
#else
internal CustomMessageBoxWindow(Window? owner, string? message, string? caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
#endif
{
InitializeComponent();
@@ -109,7 +137,7 @@ namespace WPFCustomMessageBox
if (image.HasValue)
DisplayImage(image.Value);
else
Image_MessageBox.Visibility = System.Windows.Visibility.Collapsed;
Image_MessageBox.Visibility = Visibility.Collapsed;
}
protected override void OnSourceInitialized(EventArgs e)
@@ -126,39 +154,39 @@ namespace WPFCustomMessageBox
{
case MessageBoxButton.OKCancel:
// Hide all but OK, Cancel
Button_OK.Visibility = System.Windows.Visibility.Visible;
Button_OK.Visibility = Visibility.Visible;
Button_OK.Focus();
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
Button_Cancel.Visibility = Visibility.Visible;
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
Button_No.Visibility = System.Windows.Visibility.Collapsed;
Button_Yes.Visibility = Visibility.Collapsed;
Button_No.Visibility = Visibility.Collapsed;
break;
case MessageBoxButton.YesNo:
// Hide all but Yes, No
Button_Yes.Visibility = System.Windows.Visibility.Visible;
Button_Yes.Visibility = Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = System.Windows.Visibility.Visible;
Button_No.Visibility = Visibility.Visible;
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
Button_OK.Visibility = Visibility.Collapsed;
Button_Cancel.Visibility = Visibility.Collapsed;
break;
case MessageBoxButton.YesNoCancel:
// Hide only OK
Button_Yes.Visibility = System.Windows.Visibility.Visible;
Button_Yes.Visibility = Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = System.Windows.Visibility.Visible;
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
Button_No.Visibility = Visibility.Visible;
Button_Cancel.Visibility = Visibility.Visible;
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
Button_OK.Visibility = Visibility.Collapsed;
break;
default:
// Hide all but OK
Button_OK.Visibility = System.Windows.Visibility.Visible;
Button_OK.Visibility = Visibility.Visible;
Button_OK.Focus();
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
Button_No.Visibility = System.Windows.Visibility.Collapsed;
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
Button_Yes.Visibility = Visibility.Collapsed;
Button_No.Visibility = Visibility.Collapsed;
Button_Cancel.Visibility = Visibility.Collapsed;
break;
}
}
@@ -187,7 +215,7 @@ namespace WPFCustomMessageBox
}
Image_MessageBox.Source = icon.ToImageSource();
Image_MessageBox.Visibility = System.Windows.Visibility.Visible;
Image_MessageBox.Visibility = Visibility.Visible;
}
private void Button_OK_Click(object sender, RoutedEventArgs e)

View File

@@ -37,7 +37,7 @@ namespace WPFCustomMessageBox
const int SWP_NOMOVE = 0x0002;
const int SWP_NOZORDER = 0x0004;
const int SWP_FRAMECHANGED = 0x0020;
const uint WM_SETICON = 0x0080;
//const uint WM_SETICON = 0x0080;
internal static ImageSource ToImageSource(this Icon icon)
@@ -60,7 +60,11 @@ namespace WPFCustomMessageBox
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
#if NET48
internal static string TryAddKeyboardAccellerator(this string input)
#else
internal static string? TryAddKeyboardAccellerator(this string? input)
#endif
{
if (input == null)
return input;
@@ -84,7 +88,7 @@ namespace WPFCustomMessageBox
IntPtr hwnd = new WindowInteropHelper(window).Handle;
// Change the extended window style to not show a window icon
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
_ = SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
// Update the window's non-client area to reflect the changes
SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}

View File

@@ -7,7 +7,11 @@
<UseWPF>true</UseWPF>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<VersionPrefix>2.7.0</VersionPrefix>
<VersionPrefix>2.7.3</VersionPrefix>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>

View File

@@ -6,39 +6,63 @@ namespace MPF.UI.Core
/// <summary>
/// Represents all required mapping values for the UI
/// </summary>
public class Theme
public abstract class Theme
{
#region Application-Wide
/// <summary>
/// SolidColorBrush used to paint the active window's border.
/// </summary>
public SolidColorBrush ActiveBorderBrush { get; set; }
#if NET48
public SolidColorBrush ActiveBorderBrush { get; protected set; }
#else
public SolidColorBrush? ActiveBorderBrush { get; protected set; }
#endif
/// <summary>
/// SolidColorBrush that paints the face of a three-dimensional display element.
/// </summary>
public SolidColorBrush ControlBrush { get; set; }
#if NET48
public SolidColorBrush ControlBrush { get; protected set; }
#else
public SolidColorBrush? ControlBrush { get; protected set; }
#endif
/// <summary>
/// SolidColorBrush that paints text in a three-dimensional display element.
/// </summary>
public SolidColorBrush ControlTextBrush { get; set; }
#if NET48
public SolidColorBrush ControlTextBrush { get; protected set; }
#else
public SolidColorBrush? ControlTextBrush { get; protected set; }
#endif
/// <summary>
/// SolidColorBrush that paints disabled text.
/// </summary>
public SolidColorBrush GrayTextBrush { get; set; }
#if NET48
public SolidColorBrush GrayTextBrush { get; protected set; }
#else
public SolidColorBrush? GrayTextBrush { get; protected set; }
#endif
/// <summary>
/// SolidColorBrush that paints the background of a window's client area.
/// </summary>
public SolidColorBrush WindowBrush { get; set; }
#if NET48
public SolidColorBrush WindowBrush { get; protected set; }
#else
public SolidColorBrush? WindowBrush { get; protected set; }
#endif
/// <summary>
/// SolidColorBrush that paints the text in the client area of a window.
/// </summary>
public SolidColorBrush WindowTextBrush { get; set; }
#if NET48
public SolidColorBrush WindowTextBrush { get; protected set; }
#else
public SolidColorBrush? WindowTextBrush { get; protected set; }
#endif
#endregion
@@ -47,22 +71,38 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the Button.Disabled.Background resource
/// </summary>
public Brush Button_Disabled_Background { get; set; }
#if NET48
public Brush Button_Disabled_Background { get; protected set; }
#else
public Brush? Button_Disabled_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the Button.MouseOver.Background resource
/// </summary>
public Brush Button_MouseOver_Background { get; set; }
#if NET48
public Brush Button_MouseOver_Background { get; protected set; }
#else
public Brush? Button_MouseOver_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the Button.Pressed.Background resource
/// </summary>
public Brush Button_Pressed_Background { get; set; }
#if NET48
public Brush Button_Pressed_Background { get; protected set; }
#else
public Brush? Button_Pressed_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the Button.Static.Background resource
/// </summary>
public Brush Button_Static_Background { get; set; }
#if NET48
public Brush Button_Static_Background { get; protected set; }
#else
public Brush? Button_Static_Background { get; protected set; }
#endif
#endregion
@@ -71,62 +111,110 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the ComboBox.Disabled.Background resource
/// </summary>
public Brush ComboBox_Disabled_Background { get; set; }
#if NET48
public Brush ComboBox_Disabled_Background { get; protected set; }
#else
public Brush? ComboBox_Disabled_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the ComboBox.Disabled.Editable.Background resource
/// </summary>
public Brush ComboBox_Disabled_Editable_Background { get; set; }
#if NET48
public Brush ComboBox_Disabled_Editable_Background { get; protected set; }
#else
public Brush? ComboBox_Disabled_Editable_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the ComboBox.Disabled.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_Disabled_Editable_Button_Background { get; set; }
#if NET48
public Brush ComboBox_Disabled_Editable_Button_Background { get; protected set; }
#else
public Brush? ComboBox_Disabled_Editable_Button_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the ComboBox.MouseOver.Background resource
/// </summary>
public Brush ComboBox_MouseOver_Background { get; set; }
#if NET48
public Brush ComboBox_MouseOver_Background { get; protected set; }
#else
public Brush? ComboBox_MouseOver_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the ComboBox.MouseOver.Editable.Background resource
/// </summary>
public Brush ComboBox_MouseOver_Editable_Background { get; set; }
#if NET48
public Brush ComboBox_MouseOver_Editable_Background { get; protected set; }
#else
public Brush? ComboBox_MouseOver_Editable_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the ComboBox.MouseOver.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_MouseOver_Editable_Button_Background { get; set; }
#if NET48
public Brush ComboBox_MouseOver_Editable_Button_Background { get; protected set; }
#else
public Brush? ComboBox_MouseOver_Editable_Button_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the ComboBox.Pressed.Background resource
/// </summary>
public Brush ComboBox_Pressed_Background { get; set; }
#if NET48
public Brush ComboBox_Pressed_Background { get; protected set; }
#else
public Brush? ComboBox_Pressed_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the ComboBox.Pressed.Editable.Background resource
/// </summary>
public Brush ComboBox_Pressed_Editable_Background { get; set; }
#if NET48
public Brush ComboBox_Pressed_Editable_Background { get; protected set; }
#else
public Brush? ComboBox_Pressed_Editable_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the ComboBox.Pressed.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_Pressed_Editable_Button_Background { get; set; }
#if NET48
public Brush ComboBox_Pressed_Editable_Button_Background { get; protected set; }
#else
public Brush? ComboBox_Pressed_Editable_Button_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the ComboBox.Static.Background resource
/// </summary>
public Brush ComboBox_Static_Background { get; set; }
#if NET48
public Brush ComboBox_Static_Background { get; protected set; }
#else
public Brush? ComboBox_Static_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the ComboBox.Static.Editable.Background resource
/// </summary>
public Brush ComboBox_Static_Editable_Background { get; set; }
#if NET48
public Brush ComboBox_Static_Editable_Background { get; protected set; }
#else
public Brush? ComboBox_Static_Editable_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the ComboBox.Static.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_Static_Editable_Button_Background { get; set; }
#if NET48
public Brush ComboBox_Static_Editable_Button_Background { get; protected set; }
#else
public Brush? ComboBox_Static_Editable_Button_Background { get; protected set; }
#endif
#endregion
@@ -135,7 +223,11 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the CustomMessageBox.Static.Background resource
/// </summary>
public Brush CustomMessageBox_Static_Background { get; set; }
#if NET48
public Brush CustomMessageBox_Static_Background { get; protected set; }
#else
public Brush? CustomMessageBox_Static_Background { get; protected set; }
#endif
#endregion
@@ -144,12 +236,20 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the MenuItem.SubMenu.Background resource
/// </summary>
public Brush MenuItem_SubMenu_Background { get; set; }
#if NET48
public Brush MenuItem_SubMenu_Background { get; protected set; }
#else
public Brush? MenuItem_SubMenu_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the MenuItem.SubMenu.Border resource
/// </summary>
public Brush MenuItem_SubMenu_Border { get; set; }
#if NET48
public Brush MenuItem_SubMenu_Border { get; protected set; }
#else
public Brush? MenuItem_SubMenu_Border { get; protected set; }
#endif
#endregion
@@ -158,7 +258,11 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the ProgressBar.Background resource
/// </summary>
public Brush ProgressBar_Background { get; set; }
#if NET48
public Brush ProgressBar_Background { get; protected set; }
#else
public Brush? ProgressBar_Background { get; protected set; }
#endif
#endregion
@@ -167,7 +271,11 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the ScrollViewer.ScrollBar.Background resource
/// </summary>
public Brush ScrollViewer_ScrollBar_Background { get; set; }
#if NET48
public Brush ScrollViewer_ScrollBar_Background { get; protected set; }
#else
public Brush? ScrollViewer_ScrollBar_Background { get; protected set; }
#endif
#endregion
@@ -176,17 +284,29 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the TabItem.Selected.Background resource
/// </summary>
public Brush TabItem_Selected_Background { get; set; }
#if NET48
public Brush TabItem_Selected_Background { get; protected set; }
#else
public Brush? TabItem_Selected_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the TabItem.Static.Background resource
/// </summary>
public Brush TabItem_Static_Background { get; set; }
#if NET48
public Brush TabItem_Static_Background { get; protected set; }
#else
public Brush? TabItem_Static_Background { get; protected set; }
#endif
/// <summary>
/// Brush for the TabItem.Static.Border resource
/// </summary>
public Brush TabItem_Static_Border { get; set; }
#if NET48
public Brush TabItem_Static_Border { get; protected set; }
#else
public Brush? TabItem_Static_Border { get; protected set; }
#endif
#endregion
@@ -195,7 +315,11 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the TextBox.Static.Background resource
/// </summary>
public Brush TextBox_Static_Background { get; set; }
#if NET48
public Brush TextBox_Static_Background { get; protected set; }
#else
public Brush? TextBox_Static_Background { get; protected set; }
#endif
#endregion

View File

@@ -29,7 +29,11 @@ namespace MPF.UI.Core.UserControls
/// <summary>
/// Cached value of the last line written
/// </summary>
#if NET48
private Run lastLine = null;
#else
private Run? lastLine = null;
#endif
public LogOutput()
{
@@ -164,12 +168,17 @@ namespace MPF.UI.Core.UserControls
{
Dispatcher.Invoke(() =>
{
#if NET48
if (lastLine == null) lastLine = new Run();
#else
lastLine ??= new Run();
#endif
lastLine.Text = logLine.Text;
lastLine.Foreground = logLine.GetForegroundColor();
});
}
#endregion
#endregion
#region Helpers
@@ -183,12 +192,12 @@ namespace MPF.UI.Core.UserControls
/// </summary>
private void SaveInlines()
{
using (StreamWriter tw = new StreamWriter(File.OpenWrite("console.log")))
using (var sw = new StreamWriter(File.OpenWrite("console.log")))
{
foreach (var inline in _paragraph.Inlines)
{
if (inline is Run run)
tw.Write(run.Text);
sw.Write(run.Text);
}
}
}

View File

@@ -15,12 +15,16 @@ namespace MPF.UI.Core.Windows
/// <summary>
/// Read-only access to the current disc information view model
/// </summary>
public DiscInformationViewModel DiscInformationViewModel => DataContext as DiscInformationViewModel;
public DiscInformationViewModel DiscInformationViewModel => DataContext as DiscInformationViewModel ?? new DiscInformationViewModel(new Options(), new SubmissionInfo());
/// <summary>
/// Constructor
/// </summary>
#if NET48
public DiscInformationWindow(Options options, SubmissionInfo submissionInfo)
#else
public DiscInformationWindow(Options options, SubmissionInfo? submissionInfo)
#endif
{
InitializeComponent();
DataContext = new DiscInformationViewModel(options, submissionInfo);
@@ -47,7 +51,11 @@ namespace MPF.UI.Core.Windows
/// <summary>
/// Manipulate fields based on the current disc
/// </summary>
#if NET48
private void ManipulateFields(Options options, SubmissionInfo submissionInfo)
#else
private void ManipulateFields(Options options, SubmissionInfo? submissionInfo)
#endif
{
// Enable tabs in all fields, if required
if (options.EnableTabsInInputFields)
@@ -115,63 +123,71 @@ namespace MPF.UI.Core.Windows
/// </summary>
/// TODO: Figure out how to bind the PartiallyMatchedIDs array to a text box
/// TODO: Convert visibility to a binding
#if NET48
private void HideReadOnlyFields(SubmissionInfo submissionInfo)
#else
private void HideReadOnlyFields(SubmissionInfo? submissionInfo)
#endif
{
if (submissionInfo?.FullyMatchedID == null)
// If there's no submission information
if (submissionInfo == null)
return;
if (submissionInfo.FullyMatchedID == null)
FullyMatchedID.Visibility = Visibility.Collapsed;
if (submissionInfo?.PartiallyMatchedIDs == null)
if (submissionInfo.PartiallyMatchedIDs == null)
PartiallyMatchedIDs.Visibility = Visibility.Collapsed;
else
PartiallyMatchedIDs.Text = string.Join(", ", submissionInfo.PartiallyMatchedIDs);
if (submissionInfo?.CopyProtection?.AntiModchip == null)
if (submissionInfo.CopyProtection?.AntiModchip == null)
AntiModchip.Visibility = Visibility.Collapsed;
if (submissionInfo?.TracksAndWriteOffsets?.OtherWriteOffsets == null)
if (submissionInfo.TracksAndWriteOffsets?.OtherWriteOffsets == null)
DiscOffset.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.DMIHash) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.Keys?.Contains(SiteCode.DMIHash) != true)
DMIHash.Visibility = Visibility.Collapsed;
if (submissionInfo?.EDC?.EDC == null)
if (submissionInfo.EDC?.EDC == null)
EDC.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo?.CommonDiscInfo?.ErrorsCount))
if (string.IsNullOrWhiteSpace(submissionInfo.CommonDiscInfo?.ErrorsCount))
ErrorsCount.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo?.CommonDiscInfo?.EXEDateBuildDate))
if (string.IsNullOrWhiteSpace(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
EXEDateBuildDate.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Filename) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Filename) != true)
Filename.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo?.Extras?.Header))
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.Header))
Header.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.InternalName) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalName) != true)
InternalName.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.InternalSerialName) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalSerialName) != true)
InternalSerialName.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Multisession) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Multisession) != true)
Multisession.Visibility = Visibility.Collapsed;
if (submissionInfo?.CopyProtection?.LibCrypt == null)
if (submissionInfo.CopyProtection?.LibCrypt == null)
LibCrypt.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo?.CopyProtection?.LibCryptData))
if (string.IsNullOrWhiteSpace(submissionInfo.CopyProtection?.LibCryptData))
LibCryptData.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.PFIHash) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.PFIHash) != true)
PFIHash.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo?.Extras?.PIC))
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.PIC))
PIC.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo?.Extras?.PVD))
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.PVD))
PVD.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.RingNonZeroDataStart) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.RingNonZeroDataStart) != true)
RingNonZeroDataStart.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo?.CopyProtection?.SecuROMData))
if (string.IsNullOrWhiteSpace(submissionInfo.CopyProtection?.SecuROMData))
SecuROMData.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSHash) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSHash) != true)
SSHash.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(submissionInfo?.Extras?.SecuritySectorRanges))
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.SecuritySectorRanges))
SecuritySectorRanges.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSVersion) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSVersion) != true)
SSVersion.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.UniversalHash) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) != true)
UniversalHash.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.VolumeLabel) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.VolumeLabel) != true)
VolumeLabel.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.XeMID) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XeMID) != true)
XeMID.Visibility = Visibility.Collapsed;
if (submissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.XMID) != true)
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XMID) != true)
XMID.Visibility = Visibility.Collapsed;
}
@@ -179,7 +195,11 @@ namespace MPF.UI.Core.Windows
/// Update visible fields and sections based on the media type
/// </summary>
/// TODO: See if these can be done by binding
#if NET48
private void UpdateFromDiscType(SubmissionInfo submissionInfo)
#else
private void UpdateFromDiscType(SubmissionInfo? submissionInfo)
#endif
{
// Sony-printed discs have layers in the opposite order
var system = submissionInfo?.CommonDiscInfo?.System;
@@ -326,7 +346,11 @@ namespace MPF.UI.Core.Windows
/// Update visible fields and sections based on the system type
/// </summary>
/// TODO: See if these can be done by binding
#if NET48
private void UpdateFromSystemType(SubmissionInfo submissionInfo)
#else
private void UpdateFromSystemType(SubmissionInfo? submissionInfo)
#endif
{
var system = submissionInfo?.CommonDiscInfo?.System;
switch (system)
@@ -337,7 +361,7 @@ namespace MPF.UI.Core.Windows
}
}
#endregion
#endregion
#region Event Handlers

View File

@@ -15,7 +15,7 @@ namespace MPF.UI.Core.Windows
/// <summary>
/// Read-only access to the current main view model
/// </summary>
public MainViewModel MainViewModel => DataContext as MainViewModel;
public MainViewModel MainViewModel => DataContext as MainViewModel ?? new MainViewModel();
/// <summary>
/// Constructor
@@ -45,14 +45,18 @@ namespace MPF.UI.Core.Windows
MainViewModel.Init(LogOutput.EnqueueLog, DisplayUserMessage, ShowDiscInformationWindow);
// Set the UI color scheme according to the options
if (MainViewModel.Options.EnableDarkMode)
EnableDarkMode();
else
EnableLightMode();
ApplyTheme();
// Check for updates, if necessary
if (MainViewModel.Options.CheckForUpdatesOnStartup)
CheckForUpdates(showIfSame: false);
// Handle first-run, if necessary
if (MainViewModel.Options.FirstRun)
{
// Show the options window
ShowOptionsWindow("Welcome to MPF, Explore the Options");
}
}
#region UI Functionality
@@ -95,8 +99,10 @@ namespace MPF.UI.Core.Windows
{
// Get the current path, if possible
string currentPath = MainViewModel.OutputPath;
if (string.IsNullOrWhiteSpace(currentPath))
if (string.IsNullOrWhiteSpace(currentPath) && !string.IsNullOrWhiteSpace(MainViewModel.Options.DefaultOutputPath))
currentPath = Path.Combine(MainViewModel.Options.DefaultOutputPath, "track.bin");
else if (string.IsNullOrWhiteSpace(currentPath))
currentPath = "track.bin";
if (string.IsNullOrWhiteSpace(currentPath))
currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "track.bin");
@@ -104,7 +110,7 @@ namespace MPF.UI.Core.Windows
currentPath = Path.GetFullPath(currentPath);
// Get the directory
string directory = Path.GetDirectoryName(currentPath);
var directory = Path.GetDirectoryName(currentPath);
// Get the filename
string filename = Path.GetFileName(currentPath);
@@ -128,14 +134,14 @@ namespace MPF.UI.Core.Windows
/// <param name="showIfSame">True to show the box even if it's the same, false to only show if it's different</param>
public void CheckForUpdates(bool showIfSame)
{
(bool different, string message, string url) = MainViewModel.CheckForUpdates();
(bool different, string message, var url) = MainViewModel.CheckForUpdates();
// If we have a new version, put it in the clipboard
if (different)
Clipboard.SetText(url);
if (showIfSame || different)
CustomMessageBox.Show(message, "Version Update Check", MessageBoxButton.OK, different ? MessageBoxImage.Exclamation : MessageBoxImage.Information);
CustomMessageBox.Show(this, message, "Version Update Check", MessageBoxButton.OK, different ? MessageBoxImage.Exclamation : MessageBoxImage.Information);
}
/// <summary>
@@ -204,10 +210,14 @@ namespace MPF.UI.Core.Windows
/// </summary>
/// <param name="submissionInfo">SubmissionInfo object to display and possibly change</param>
/// <returns>Dialog open result</returns>
#if NET48
public (bool?, SubmissionInfo) ShowDiscInformationWindow(SubmissionInfo submissionInfo)
#else
public (bool?, SubmissionInfo?) ShowDiscInformationWindow(SubmissionInfo? submissionInfo)
#endif
{
if (MainViewModel.Options.ShowDiscEjectReminder)
CustomMessageBox.Show("It is now safe to eject the disc", "Eject", MessageBoxButton.OK, MessageBoxImage.Information);
CustomMessageBox.Show(this, "It is now safe to eject the disc", "Eject", MessageBoxButton.OK, MessageBoxImage.Information);
var discInformationWindow = new DiscInformationWindow(MainViewModel.Options, submissionInfo)
{
@@ -221,15 +231,27 @@ namespace MPF.UI.Core.Windows
// Copy back the submission info changes, if necessary
if (result == true)
#if NET48
submissionInfo = discInformationWindow.DiscInformationViewModel.SubmissionInfo.Clone() as SubmissionInfo;
#else
submissionInfo = (discInformationWindow.DiscInformationViewModel.SubmissionInfo.Clone() as SubmissionInfo)!;
#endif
#if NET48
return (result, submissionInfo);
#else
return (result, submissionInfo!);
#endif
}
/// <summary>
/// Show the Options window
/// </summary>
public void ShowOptionsWindow()
#if NET48
public void ShowOptionsWindow(string title = null)
#else
public void ShowOptionsWindow(string? title = null)
#endif
{
var optionsWindow = new OptionsWindow(MainViewModel.Options)
{
@@ -237,27 +259,25 @@ namespace MPF.UI.Core.Windows
Owner = this,
ShowActivated = true,
ShowInTaskbar = true,
Title = title ?? "Options",
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
optionsWindow.Closed += OnOptionsUpdated;
optionsWindow.Show();
}
/// <summary>
/// Recolor all UI elements for light mode
/// Set the UI color scheme according to the options
/// </summary>
private static void EnableLightMode()
private void ApplyTheme()
{
var theme = new LightModeTheme();
theme.Apply();
}
Theme theme;
if (MainViewModel.Options.EnableDarkMode)
theme = new DarkModeTheme();
else
theme = new LightModeTheme();
/// <summary>
/// Recolor all UI elements for dark mode
/// </summary>
private static void EnableDarkMode()
{
var theme = new DarkModeTheme();
theme.Apply();
}
@@ -268,11 +288,23 @@ namespace MPF.UI.Core.Windows
/// <summary>
/// Handler for OptionsWindow OnUpdated event
/// </summary>
#if NET48
public void OnOptionsUpdated(object sender, EventArgs e)
#else
public void OnOptionsUpdated(object? sender, EventArgs e)
#endif
{
bool savedSettings = (sender as OptionsWindow)?.OptionsViewModel?.SavedSettings ?? false;
var options = (sender as OptionsWindow).OptionsViewModel.Options;
// Get the options window
var optionsWindow = (sender as OptionsWindow);
if (optionsWindow?.OptionsViewModel == null)
return;
bool savedSettings = optionsWindow.OptionsViewModel.SavedSettings;
var options = optionsWindow.OptionsViewModel.Options;
MainViewModel.UpdateOptions(savedSettings, options);
// Set the UI color scheme according to the options
ApplyTheme();
}
#region Menu Bar
@@ -283,7 +315,7 @@ namespace MPF.UI.Core.Windows
public void AboutClick(object sender, RoutedEventArgs e)
{
string aboutText = MainViewModel.CreateAboutText();
CustomMessageBox.Show(aboutText, "About", MessageBoxButton.OK, MessageBoxImage.Information);
CustomMessageBox.Show(this, aboutText, "About", MessageBoxButton.OK, MessageBoxImage.Information);
}
/// <summary>
@@ -319,14 +351,14 @@ namespace MPF.UI.Core.Windows
/// </summary>
public async void CopyProtectScanButtonClick(object sender, RoutedEventArgs e)
{
(string output, string error) = await MainViewModel.ScanAndShowProtection();
var (output, error) = await MainViewModel.ScanAndShowProtection();
if (!MainViewModel.LogPanelExpanded)
{
if (string.IsNullOrEmpty(error))
CustomMessageBox.Show(output, "Detected Protection(s)", MessageBoxButton.OK, MessageBoxImage.Information);
if (!string.IsNullOrEmpty(output) && string.IsNullOrEmpty(error))
CustomMessageBox.Show(this, output, "Detected Protection(s)", MessageBoxButton.OK, MessageBoxImage.Information);
else
CustomMessageBox.Show("An exception occurred, see the log for details", "Error!", MessageBoxButton.OK, MessageBoxImage.Error);
CustomMessageBox.Show(this, "An exception occurred, see the log for details", "Error!", MessageBoxButton.OK, MessageBoxImage.Error);
}
}

View File

@@ -6,7 +6,7 @@
xmlns:core="clr-namespace:MPF.UI.Core"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
mc:Ignorable="d"
Title="Options" Width="515.132" WindowStyle="None"
Width="515.132" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
@@ -32,7 +32,11 @@
<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>Options</Bold></TextBlock>
<TextBlock>
<TextBlock.Inlines>
<Run FontWeight="Bold" Text="{Binding Title, Mode=OneWay}"/>
</TextBlock.Inlines>
</TextBlock>
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
@@ -63,28 +67,84 @@
</Button>
</Grid>
</Grid>
<TabControl Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomTabControlStyle}">
<TabItem Header="User Interface" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="3">
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
IsChecked="{Binding Options.EnableDarkMode}"
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Check for Updates on Startup"
IsChecked="{Binding Options.CheckForUpdatesOnStartup}"
ToolTip="Check for updates when the application starts" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Fast Update Label"
IsChecked="{Binding Options.FastUpdateLabel}"
ToolTip="Bypasses disc checks to quickly update the output path. Use with caution!" Margin="0,4"
/>
</UniformGrid>
<TabItem Header="General" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="User Interface">
<UniformGrid Columns="2" Rows="2">
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
IsChecked="{Binding Options.EnableDarkMode}"
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Check for Updates on Startup"
IsChecked="{Binding Options.CheckForUpdatesOnStartup}"
ToolTip="Check for updates when the application starts" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Fast Update Label"
IsChecked="{Binding Options.FastUpdateLabel}"
ToolTip="Bypasses disc checks to quickly update the output path. Use with caution!" Margin="0,4"
/>
</UniformGrid>
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Detection">
<UniformGrid Columns="2" Rows="3">
<CheckBox x:Name="SkipMediaTypeDetectionCheckBox" VerticalAlignment="Center" Content="Skip Type Detect"
IsChecked="{Binding Options.SkipMediaTypeDetection}"
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Skip System Detect"
IsChecked="{Binding Options.SkipSystemDetection}"
ToolTip="Disable trying to guess system (may improve performance at startup)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="No Fixed Drives"
IsChecked="{Binding Options.IgnoreFixedDrives}"
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
/>
<Label/>
<!-- Empty label for padding -->
<Label VerticalAlignment="Center" Content="Default System:" HorizontalAlignment="Right" />
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left"
ItemsSource="{Binding Systems}" SelectedItem="{Binding Options.DefaultSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHeader}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</UniformGrid>
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Logging">
<UniformGrid Columns="2" Rows="1">
<CheckBox VerticalAlignment="Center" Content="Verbose Logging"
IsChecked="{Binding Options.VerboseLogging}"
ToolTip="Display all logging statements" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Auto-Open Log"
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
ToolTip="Open the log panel when the program launches" Margin="0,4"
/>
</UniformGrid>
</GroupBox>
</StackPanel>
</TabItem>
<TabItem Header="Paths" Style="{DynamicResource CustomTabItemStyle}">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
@@ -135,47 +195,12 @@
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
<UniformGrid Columns="2" Rows="9">
<CheckBox x:Name="SkipMediaTypeDetectionCheckBox" VerticalAlignment="Center" Content="Skip Type Detect"
IsChecked="{Binding Options.SkipMediaTypeDetection}"
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Skip System Detect"
IsChecked="{Binding Options.SkipSystemDetection}"
ToolTip="Disable trying to guess system (may improve performance at startup)" Margin="0,4"
/>
<Label VerticalAlignment="Center" Content="Default System:" HorizontalAlignment="Right" />
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left"
ItemsSource="{Binding Systems}" SelectedItem="{Binding Options.DefaultSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHeader}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<CheckBox VerticalAlignment="Center" Content="No Fixed Drives"
IsChecked="{Binding Options.IgnoreFixedDrives}"
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
/>
<UniformGrid Columns="2" Rows="7">
<CheckBox VerticalAlignment="Center" Content="Show Separate Window"
IsChecked="{Binding Options.ToolsInSeparateWindow}"
ToolTip="Show program output in separate command window instead of in the log. Enable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Protection Scan"
IsChecked="{Binding Options.ScanForProtection}"
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
/>
<CheckBox VerticalAlignment="Center" Content="Output Protection File"
IsChecked="{Binding Options.OutputSeparateProtectionFile}" IsEnabled="{Binding Options.ScanForProtection}"
ToolTip="Output protection information to a separate file" Margin="0,4,0,0"
@@ -216,6 +241,11 @@
ToolTip="Enable adding placeholder text in the output for required and optional fields" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Add Filename Suffix"
IsChecked="{Binding Options.AddFilenameSuffix}"
ToolTip="Enable appending the output filename to the generated files" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Output Submission JSON"
IsChecked="{Binding Options.OutputSubmissionJSON}"
ToolTip="Enable outputting a compressed JSON version of the submission info" Margin="0,4"
@@ -230,9 +260,38 @@
IsChecked="{Binding Options.CompressLogFiles}"
ToolTip="Compress output log files to reduce space" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Delete Unnecessary Files"
IsChecked="{Binding Options.DeleteUnnecessaryFiles}"
ToolTip="Delete unnecesary output files to reduce space" Margin="0,4"
/>
</UniformGrid>
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Protection">
<UniformGrid Columns="2" Rows="2">
<CheckBox VerticalAlignment="Center" Content="Enable Protection Scan"
IsChecked="{Binding Options.ScanForProtection}"
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
/>
<CheckBox VerticalAlignment="Center" Content="Scan Archive Contents"
IsChecked="{Binding Options.ScanArchivesForProtection}"
ToolTip="Enable scanning archive contents during protection scanning (may drastically increase scanning time but is more accurate)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Include Executable Packers"
IsChecked="{Binding Options.ScanPackersForProtection}"
ToolTip="Include executable packers in outputted protections" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
ToolTip="Include debug information during protection scans" Margin="0,4"
/>
</UniformGrid>
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Default Speeds" Grid.ColumnSpan="2">
<Grid>
<Grid.ColumnDefinitions>
@@ -247,7 +306,7 @@
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="CD" />
<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}}"
@@ -280,142 +339,121 @@
</StackPanel>
</TabItem>
<TabItem Header="Protection Scanning" Style="{DynamicResource CustomTabItemStyle}">
<TabItem Header="Programs" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<UniformGrid Columns="2" Rows="2">
<CheckBox VerticalAlignment="Center" Content="Scan Archive Contents"
IsChecked="{Binding Options.ScanArchivesForProtection}"
ToolTip="Enable scanning archive contents during protection scanning (may drastically increase scanning time but is more accurate)" Margin="0,4"
/>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Aaru">
<UniformGrid Columns="2" Rows="3">
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
IsChecked="{Binding Options.AaruEnableDebug}"
ToolTip="Enable debug output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Include Executable Packers"
IsChecked="{Binding Options.ScanPackersForProtection}"
ToolTip="Include executable packers in outputted protections" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
IsChecked="{Binding Options.AaruEnableVerbose}"
ToolTip="Enable verbose output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
ToolTip="Include debug information during protection scans" Margin="0,4"
/>
</UniformGrid>
<CheckBox VerticalAlignment="Center" Content="Force Dumping"
IsChecked="{Binding Options.AaruForceDumping}"
ToolTip="Enable forcing dump even if there are issues" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Strip Personal Data"
IsChecked="{Binding Options.AaruStripPersonalData}"
ToolTip="Enable stripping of personally identifiable information from metadata" Margin="0,4,0,0"
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.AaruRereadCount}"
ToolTip="Specifies how many rereads are attempted for sector and subchannel errors"
/>
</UniformGrid>
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="DiscImageCreator">
<UniformGrid Columns="2" Rows="6">
<CheckBox VerticalAlignment="Center" Content="Quiet Mode"
IsChecked="{Binding Options.DICQuietMode}"
ToolTip="Disable sounds (beeps) during and after operations" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Paranoid Mode"
IsChecked="{Binding Options.DICParanoidMode}"
ToolTip="Enable pedantic and super-safe flags" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Use CMI Flag"
IsChecked="{Binding Options.DICUseCMIFlag}"
ToolTip="Enable the CMI flag for supported disc types (DVD/HD-DVD only)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Reset After Dump"
IsChecked="{Binding Options.DICResetDriveAfterDump}"
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Multi-Sector Read"
IsChecked="{Binding Options.DICMultiSectorRead}"
ToolTip="Enable the /mr flag for BD drive dumping" Margin="0,4"
/>
<Label/> <!-- Empty label for padding -->
<Label Content="Multi-Sector Read Value:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.DICMultiSectorReadValue}" IsEnabled="{Binding Options.DICMultiSectorRead}"
ToolTip="Set the default value for the /mr flag"
/>
<Label Content="CD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.DICRereadCount}"
ToolTip="Specifies how many rereads are attempted on C2 error [CD only]"
/>
<Label Content="DVD/HD-DVD/BD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.DICDVDRereadCount}"
ToolTip="Specifies how many rereads are attempted on read error [DVD/HD-DVD/BD only]"
/>
</UniformGrid>
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redumper">
<UniformGrid Columns="2" Rows="3">
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
IsChecked="{Binding Options.RedumperEnableDebug}"
ToolTip="Enable debug output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
IsChecked="{Binding Options.RedumperEnableVerbose}"
ToolTip="Enable verbose output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable BE Drive Reading"
IsChecked="{Binding Options.RedumperUseBEReading}"
ToolTip="Enable setting drive read method to BE_CDDA by default" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Set Generic Drive Type"
IsChecked="{Binding Options.RedumperUseGenericDriveType}"
ToolTip="Enable setting drive type to Generic by default" Margin="0,4"
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.RedumperRereadCount}"
ToolTip="Specifies how many rereads are attempted on read error"
/>
</UniformGrid>
</GroupBox>
</StackPanel>
</TabItem>
<TabItem Header="Aaru" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="3">
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
IsChecked="{Binding Options.AaruEnableDebug}"
ToolTip="Enable debug output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
IsChecked="{Binding Options.AaruEnableVerbose}"
ToolTip="Enable verbose output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Force Dumping"
IsChecked="{Binding Options.AaruForceDumping}"
ToolTip="Enable forcing dump even if there are issues" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Strip Personal Data"
IsChecked="{Binding Options.AaruStripPersonalData}"
ToolTip="Enable stripping of personally identifiable information from metadata" Margin="0,4,0,0"
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.AaruRereadCount}"
ToolTip="Specifies how many rereads are attempted for sector and subchannel errors"
/>
</UniformGrid>
</TabItem>
<TabItem Header="DiscImageCreator" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="6">
<CheckBox VerticalAlignment="Center" Content="Quiet Mode"
IsChecked="{Binding Options.DICQuietMode}"
ToolTip="Disable sounds (beeps) during and after operations" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Paranoid Mode"
IsChecked="{Binding Options.DICParanoidMode}"
ToolTip="Enable pedantic and super-safe flags" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Use CMI Flag"
IsChecked="{Binding Options.DICUseCMIFlag}"
ToolTip="Enable the CMI flag for supported disc types (DVD/HD-DVD only)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Reset After Dump"
IsChecked="{Binding Options.DICResetDriveAfterDump}"
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Multi-Sector Read"
IsChecked="{Binding Options.DICMultiSectorRead}"
ToolTip="Enable the /mr flag for BD drive dumping" Margin="0,4"
/>
<Label/> <!-- Empty label for padding -->
<Label Content="Multi-Sector Read Value:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.DICMultiSectorReadValue}" IsEnabled="{Binding Options.DICMultiSectorRead}"
ToolTip="Set the default value for the /mr flag"
/>
<Label Content="CD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.DICRereadCount}"
ToolTip="Specifies how many rereads are attempted on C2 error [CD only]"
/>
<Label Content="DVD/HD-DVD/BD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.DICDVDRereadCount}"
ToolTip="Specifies how many rereads are attempted on read error [DVD/HD-DVD/BD only]"
/>
</UniformGrid>
</TabItem>
<TabItem Header="Redumper" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="2">
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
IsChecked="{Binding Options.RedumperEnableDebug}"
ToolTip="Enable debug output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
IsChecked="{Binding Options.RedumperEnableVerbose}"
ToolTip="Enable verbose output in logs" Margin="0,4"
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.RedumperRereadCount}"
ToolTip="Specifies how many rereads are attempted on read error"
/>
</UniformGrid>
</TabItem>
<TabItem Header="Logging" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2">
<CheckBox VerticalAlignment="Center" Content="Verbose Logging"
IsChecked="{Binding Options.VerboseLogging}"
ToolTip="Display all logging statements" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Auto-Open Log"
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
ToolTip="Open the log panel when the program launches" Margin="0,4"
/>
</UniformGrid>
</TabItem>
<TabItem Header="Login Info" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump Credentials">
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump">
<UniformGrid Columns="5">
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Username" />
<TextBox x:Name="RedumpUsernameTextBox" Height="22" HorizontalAlignment="Stretch"
@@ -441,12 +479,14 @@
<!-- Accept / Cancel -->
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
<Label/> <!-- Empty label for padding -->
<Label/>
<!-- Empty label for padding -->
<Button Name="AcceptButton" Height="25" Width="80" IsDefault="True" Content="Accept"
Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
Style="{DynamicResource CustomButtonStyle}" />
<Label/> <!-- Empty label for padding -->
<Label/>
<!-- Empty label for padding -->
</UniformGrid>
</GroupBox>
</StackPanel>

View File

@@ -17,7 +17,7 @@ namespace MPF.UI.Core.Windows
/// <summary>
/// Read-only access to the current options view model
/// </summary>
public OptionsViewModel OptionsViewModel => DataContext as OptionsViewModel;
public OptionsViewModel OptionsViewModel => DataContext as OptionsViewModel ?? new OptionsViewModel(new Options());
/// <summary>
/// Constructor
@@ -41,25 +41,44 @@ namespace MPF.UI.Core.Windows
RedumpLoginTestButton.Click += OnRedumpTestClick;
}
/// <summary>
/// Handler for OptionsWindow OnContentRendered event
/// </summary>
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
// Set the window title
OptionsViewModel.Title = this.Title;
}
#region UI Commands
/// <summary>
/// Browse and set a path based on the invoking button
/// </summary>
#if NET48
private void BrowseForPath(Window parent, System.Windows.Controls.Button button)
#else
private void BrowseForPath(Window parent, System.Windows.Controls.Button? button)
#endif
{
// If the button is null, we can't do anything
if (button == null)
return;
// Strips button prefix to obtain the setting name
#if NET48
string pathSettingName = button.Name.Substring(0, button.Name.IndexOf("Button"));
#else
string pathSettingName = button.Name[..button.Name.IndexOf("Button")];
#endif
// TODO: hack for now, then we'll see
bool shouldBrowseForPath = pathSettingName == "DefaultOutputPath";
string currentPath = TextBoxForPathSetting(parent, pathSettingName)?.Text;
string initialDirectory = AppDomain.CurrentDomain.BaseDirectory;
var currentPath = TextBoxForPathSetting(parent, pathSettingName)?.Text;
var initialDirectory = AppDomain.CurrentDomain.BaseDirectory;
if (!shouldBrowseForPath && !string.IsNullOrEmpty(currentPath))
initialDirectory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
@@ -88,7 +107,9 @@ namespace MPF.UI.Core.Windows
if (exists)
{
OptionsViewModel.Options[pathSettingName] = path;
TextBoxForPathSetting(parent, pathSettingName).Text = path;
var textBox = TextBoxForPathSetting(parent, pathSettingName);
if (textBox != null)
textBox.Text = path;
}
else
{
@@ -108,18 +129,30 @@ namespace MPF.UI.Core.Windows
/// </summary>
/// <param name="name">Setting name to find</param>
/// <returns>TextBox for that setting</returns>
private System.Windows.Controls.TextBox TextBoxForPathSetting(Window parent, string name) =>
#if NET48
private static System.Windows.Controls.TextBox TextBoxForPathSetting(Window parent, string name) =>
#else
private static System.Windows.Controls.TextBox? TextBoxForPathSetting(Window parent, string name) =>
#endif
parent.FindName(name + "TextBox") as System.Windows.Controls.TextBox;
/// <summary>
/// Create an open folder dialog box
/// </summary>
#if NET48
private static FolderBrowserDialog CreateFolderBrowserDialog() => new FolderBrowserDialog();
#else
private static FolderBrowserDialog CreateFolderBrowserDialog() => new();
#endif
/// <summary>
/// Create an open file dialog box
/// </summary>
#if NET48
private static OpenFileDialog CreateOpenFileDialog(string initialDirectory)
#else
private static OpenFileDialog CreateOpenFileDialog(string? initialDirectory)
#endif
{
return new OpenFileDialog()
{
@@ -142,7 +175,7 @@ namespace MPF.UI.Core.Windows
#if NET48
(bool? success, string message) = OptionsViewModel.TestRedumpLogin(RedumpUsernameTextBox.Text, RedumpPasswordBox.Password);
#else
(bool? success, string message) = await OptionsViewModel.TestRedumpLogin(RedumpUsernameTextBox.Text, RedumpPasswordBox.Password);
(bool? success, string? message) = await OptionsViewModel.TestRedumpLogin(RedumpUsernameTextBox.Text, RedumpPasswordBox.Password);
#endif
if (success == true)

View File

@@ -1,16 +1,7 @@
using System.Windows;
namespace MPF
namespace MPF
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
/// <remarks>
/// This application is not fully MVVM. The following steps are needed to get there:
/// - Use commands instead of event handlers, where at all possible
/// - Reduce the amount of manual UI adjustments needed, instead binding to the view models
/// </remarks>
public partial class App : Application
{
}
public partial class App : System.Windows.Application { }
}

View File

@@ -11,10 +11,14 @@
<Description>Frontend for various dumping programs</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<VersionPrefix>2.7.0</VersionPrefix>
<VersionPrefix>2.7.3</VersionPrefix>
<InternalsVisibleTo>MPF.Test</InternalsVisibleTo>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Resource Include="Images\Icon.ico" />
</ItemGroup>
@@ -29,7 +33,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.9.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />

View File

@@ -1,5 +1,5 @@
# version format
version: 2.7.0-{build}
version: 2.7.3-{build}
# pull request template
pull_requests:
@@ -49,78 +49,27 @@ after_build:
# Aaru
- ps: appveyor DownloadFile https://github.com/aaru-dps/Aaru/releases/download/v5.3.2/aaru-5.3.2_windows_x64.zip
- 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Debug\net48\win7-x64\publish\Programs\Aaru *
- 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Debug\net6.0-windows\win-x64\publish\Programs\Aaru *
- 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Debug\net7.0-windows\win-x64\publish\Programs\Aaru *
# - 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Release\net48\win7-x64\publish\Programs\Aaru *
# - 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Release\net6.0-windows\win-x64\publish\Programs\Aaru *
# - 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Release\net7.0-windows\win-x64\publish\Programs\Aaru *
# DiscImageCreator
- ps: appveyor DownloadFile https://github.com/saramibreak/DiscImageCreator/files/11660558/DiscImageCreator_20230606.zip
- 7z e DiscImageCreator_20230606.zip -oMPF\bin\Debug\net48\win7-x64\publish\Programs\Creator Release_ANSI\*
- 7z e DiscImageCreator_20230606.zip -oMPF\bin\Debug\net6.0-windows\win-x64\publish\Programs\Creator Release_ANSI\*
- 7z e DiscImageCreator_20230606.zip -oMPF\bin\Debug\net7.0-windows\win-x64\publish\Programs\Creator Release_ANSI\*
# - 7z e DiscImageCreator_20230606.zip -oMPF\bin\Release\net48\win7-x64\publish\Programs\Creator Release_ANSI\*
# - 7z e DiscImageCreator_20230606.zip -oMPF\bin\Release\net6.0-windows\win-x64\publish\Programs\Creator Release_ANSI\*
# - 7z e DiscImageCreator_20230606.zip -oMPF\bin\Release\net7.0-windows\win-x64\publish\Programs\Creator Release_ANSI\*
# Redumper
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_221/redumper-2023.10.02_build221-win64.zip
- 7z e redumper-2023.10.02_build221-win64.zip -oMPF\bin\Debug\net48\win7-x64\publish\Programs\Redumper redumper-2023.10.02_build221-win64\bin\*
- 7z e redumper-2023.10.02_build221-win64.zip -oMPF\bin\Debug\net6.0-windows\win-x64\publish\Programs\Redumper redumper-2023.10.02_build221-win64\bin\*
- 7z e redumper-2023.10.02_build221-win64.zip -oMPF\bin\Debug\net7.0-windows\win-x64\publish\Programs\Redumper redumper-2023.10.02_build221-win64\bin\*
# - 7z e redumper-2023.10.02_build221-win64.zip -oMPF\bin\Release\net48\win7-x64\publish\Programs\Redumper redumper-2023.10.02_build221-win64\bin\*
# - 7z e redumper-2023.10.02_build221-win64.zip -oMPF\bin\Release\net6.0-windows\win-x64\publish\Programs\Redumper redumper-2023.10.02_build221-win64\bin\*
# - 7z e redumper-2023.10.02_build221-win64.zip -oMPF\bin\Release\net7.0-windows\win-x64\publish\Programs\Redumper redumper-2023.10.02_build221-win64\bin\*
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_230/redumper-2023.10.14_build230-win64.zip
- 7z e redumper-2023.10.14_build230-win64.zip -oMPF\bin\Debug\net6.0-windows\win-x64\publish\Programs\Redumper redumper-2023.10.14_build230-win64\bin\*
# Create MPF Debug archives
- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net48\win7-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_%APPVEYOR_BUILD_NUMBER%_net48_debug.zip *
- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net6.0-windows\win-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_%APPVEYOR_BUILD_NUMBER%_net6.0_win-x64_debug.zip *
- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net7.0-windows\win-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_%APPVEYOR_BUILD_NUMBER%_net7.0_win-x64_debug.zip *
# # Create MPF Release archives
# - cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Release\net48\win7-x64\publish\
# - 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_%APPVEYOR_BUILD_NUMBER%_net48_release.zip *
# - cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Release\net6.0-windows\win-x64\publish\
# - 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_%APPVEYOR_BUILD_NUMBER%_net6.0_win-x64_release.zip *
# - cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Release\net7.0-windows\win-x64\publish\
# - 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_%APPVEYOR_BUILD_NUMBER%_net7.0_win-x64_release.zip *
# Create MPF.Check Debug archives
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net48\win7-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net48_debug.zip *
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\win-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_win-x64_debug.zip *
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\linux-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_linux-x64_debug.zip *
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\osx-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_osx-x64_debug.zip *
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net7.0\win-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_win-x64_debug.zip *
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net7.0\linux-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_linux-x64_debug.zip *
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net7.0\osx-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_osx-x64_debug.zip *
# # Create MPF.Check Release archives
# - cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Release\net48\win7-x64\publish\
# - 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net48_release.zip *
# - cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Release\net6.0\win-x64\publish\
# - 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_win-x64_release.zip *
# - cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Release\net6.0\linux-x64\publish\
# - 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_linux-x64_release.zip *
# - cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Release\net6.0\osx-x64\publish\
# - 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_osx-x64_release.zip *
# - cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Release\net7.0\win-x64\publish\
# - 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_win-x64_release.zip *
# - cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Release\net7.0\linux-x64\publish\
# - 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_linux-x64_release.zip *
# - cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Release\net7.0\osx-x64\publish\
# - 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_osx-x64_release.zip *
# success/failure tracking
on_success:
@@ -132,46 +81,12 @@ on_failure:
# artifact linking
artifacts:
- path: MPF_%APPVEYOR_BUILD_NUMBER%_net48_debug.zip
name: MPF (.NET Framework 4.8, Debug)
- path: MPF_%APPVEYOR_BUILD_NUMBER%_net6.0_win-x64_debug.zip
name: MPF (.NET 6.0, Debug, Windows x64)
- path: MPF_%APPVEYOR_BUILD_NUMBER%_net7.0_win-x64_debug.zip
name: MPF (.NET 7.0, Debug, Windows x64)
# - path: MPF_%APPVEYOR_BUILD_NUMBER%_net48_release.zip
# name: MPF (.NET Framework 4.8, Release)
# - path: MPF_%APPVEYOR_BUILD_NUMBER%_net6.0_win-x64_release.zip
# name: MPF (.NET 6.0, Release, Windows x64)
# - path: MPF_%APPVEYOR_BUILD_NUMBER%_net7.0_win-x64_release.zip
# name: MPF (.NET 7.0, Release, Windows x64)
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net48_debug.zip
name: MPF Check (.NET Framework 4.8, Debug)
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_win-x64_debug.zip
name: MPF.Check (.NET 6.0, Debug, Windows x64)
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_linux-x64_debug.zip
name: MPF.Check (.NET 6.0, Debug, Linux x64)
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_osx-x64_debug.zip
name: MPF.Check (.NET 6.0, Debug, OSX x64)
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_win-x64_debug.zip
name: MPF.Check (.NET 7.0, Debug, Windows x64)
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_linux-x64_debug.zip
name: MPF.Check (.NET 7.0, Debug, Linux x64)
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_osx-x64_debug.zip
name: MPF.Check (.NET 7.0, Debug, OSX x64)
# - path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net48_release.zip
# name: MPF Check (.NET Framework 4.8, Release)
# - path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_win-x64_release.zip
# name: MPF.Check (.NET 6.0, Release, Windows x64)
# - path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_linux-x64_release.zip
# name: MPF.Check (.NET 6.0, Release, Linux x64)
# - path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net6.0_osx-x64_release.zip
# name: MPF.Check (.NET 6.0, Release, OSX x64)
# - path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_win-x64_release.zip
# name: MPF.Check (.NET 7.0, Release, Windows x64)
# - path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_linux-x64_release.zip
# name: MPF.Check (.NET 7.0, Release, Linux x64)
# - path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net7.0_osx-x64_release.zip
# name: MPF.Check (.NET 7.0, Release, OSX x64)