mirror of
https://github.com/SabreTools/MPF.git
synced 2026-02-05 13:49:40 +00:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebf393e634 | ||
|
|
3fbd4ea719 | ||
|
|
d09ff6cf1c | ||
|
|
1dc0d57d47 | ||
|
|
a748bd4d3a | ||
|
|
35dec7fe57 | ||
|
|
c22d16349a | ||
|
|
0d77a8950c | ||
|
|
285e94ca69 | ||
|
|
747ac4ea3b | ||
|
|
405ae7c7e4 | ||
|
|
f5ebe968c0 | ||
|
|
06a61b17cb | ||
|
|
9e8e4f6e36 | ||
|
|
fa72211b57 | ||
|
|
d5f66000a9 | ||
|
|
a52ba0aa7a | ||
|
|
eb045928f9 | ||
|
|
440302495b | ||
|
|
0732e9db78 | ||
|
|
a167652b2b | ||
|
|
cfa07c1918 | ||
|
|
53b31f91cf | ||
|
|
01cbd2cff5 | ||
|
|
65ad629ee0 | ||
|
|
06adbde715 | ||
|
|
1e5000bd8a | ||
|
|
8cb0b37e80 | ||
|
|
32c12e1332 | ||
|
|
09b307aa24 | ||
|
|
a5a8fbbf51 | ||
|
|
b366d236c8 | ||
|
|
a833e926f3 | ||
|
|
950be07bf0 | ||
|
|
4c5c1417e9 | ||
|
|
6fdc3412e0 | ||
|
|
807b0c5f9e | ||
|
|
9e0b64a1d1 |
@@ -1,3 +1,44 @@
|
||||
### 3.1.0 (2024-02-06)
|
||||
|
||||
- Update RedumpLib
|
||||
- Update Redumper to build 294
|
||||
- Fix commented out code
|
||||
- Make missing hash data clearer
|
||||
- Get BD PIC Identifier for redumper (Deterous)
|
||||
- Support redumper skeleton and hash files (Deterous)
|
||||
- Support ringcode and PIC for triple/quad-layer (fuzz6001)
|
||||
- Cleanup !protectionInfo.txt (Deterous)
|
||||
- Update Redumper to build 311 (Deterous)
|
||||
- Use PSX/PS2 serial as filename when Volume Label not present (Deterous)
|
||||
- Allow variables in output path (Deterous)
|
||||
- Check for presence of complete dump from other programs (Deterous)
|
||||
- Retrieve volume label from logs (Deterous)
|
||||
- Correct missing space in PVD (fuzz6001)
|
||||
- Prevent crashing on invalid parameters (Deterous)
|
||||
- Detect CDTV discs (Deterous)
|
||||
- Differentiate CD32 from CDTV (Deterous)
|
||||
- Skip warning line in Redumper log
|
||||
- Normalise Disc Titles in Submission Info (Deterous)
|
||||
- Fix information pulling for CleanRip and UIC
|
||||
- Add UMD handling for the disc info window
|
||||
- Detect Photo CD
|
||||
- Parse PSX/PS2/KP2 exe date from logs (Deterous)
|
||||
- Exclude extra tracks when finding disc matches (Deterous)
|
||||
- Verbose Redumper log by default (Deterous)
|
||||
- Retrieve serial from Cleanrip
|
||||
- Fix build from rushed code
|
||||
- Remove `-disc2` from Cleanrip serial
|
||||
- Enable Windows builds on Linux and Mac
|
||||
- Fix compiler warning (Deterous)
|
||||
|
||||
### 3.0.3 (2023-12-04)
|
||||
|
||||
- Fix broken tests
|
||||
- Fix using SHA-1 for track checks
|
||||
- Fix build warning for NRE
|
||||
- Remove .NET Framework 3.5 from build script
|
||||
- Handle or suppress some messages
|
||||
|
||||
### 3.0.2 (2023-12-01)
|
||||
|
||||
- Read CSS for some copy protections
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.0.2</VersionPrefix>
|
||||
<VersionPrefix>3.1.0</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Title>MPF Check</Title>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Validator for various dumping programs</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2023</Copyright>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
@@ -34,7 +34,7 @@
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.2" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.1" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -58,18 +58,36 @@ namespace MPF.Core.Data
|
||||
|
||||
/// <summary>
|
||||
/// Media label as read by Windows, formatted to avoid odd outputs
|
||||
/// If no volume label present, use PSX or PS2 serial if valid
|
||||
/// Otherwise, use "track" as volume label
|
||||
/// </summary>
|
||||
public string? FormattedVolumeLabel
|
||||
{
|
||||
get
|
||||
{
|
||||
string? volumeLabel = Template.DiscNotDetected;
|
||||
if (this.MarkedActive)
|
||||
if (!this.MarkedActive)
|
||||
return volumeLabel;
|
||||
|
||||
if (!string.IsNullOrEmpty(this.VolumeLabel))
|
||||
{
|
||||
if (string.IsNullOrEmpty(this.VolumeLabel))
|
||||
volumeLabel = "track";
|
||||
else
|
||||
volumeLabel = this.VolumeLabel;
|
||||
volumeLabel = this.VolumeLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No Volume Label found, fallback to something sensible
|
||||
switch (this.GetRedumpSystem(null))
|
||||
{
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
InfoTool.GetPlayStationExecutableInfo(this.Name, out string? serial, out _, out _);
|
||||
volumeLabel = serial ?? "track";
|
||||
break;
|
||||
|
||||
default:
|
||||
volumeLabel = "track";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
@@ -259,7 +277,7 @@ namespace MPF.Core.Data
|
||||
return RedumpSystem.IBMPCcompatible;
|
||||
|
||||
// Check volume labels first
|
||||
RedumpSystem? systemFromLabel = GetRedumpSystemFromVolumeLabel();
|
||||
RedumpSystem? systemFromLabel = GetRedumpSystemFromVolumeLabel(this.VolumeLabel);
|
||||
if (systemFromLabel != null)
|
||||
return systemFromLabel;
|
||||
|
||||
@@ -289,6 +307,19 @@ namespace MPF.Core.Data
|
||||
return RedumpSystem.BandaiPippin;
|
||||
}
|
||||
|
||||
// Commodore CDTV/CD32
|
||||
#if NET20 || NET35
|
||||
if (File.Exists(Path.Combine(Path.Combine(this.Name, "S"), "STARTUP-SEQUENCE")))
|
||||
#else
|
||||
if (File.Exists(Path.Combine(this.Name, "S", "STARTUP-SEQUENCE")))
|
||||
#endif
|
||||
{
|
||||
if (File.Exists(Path.Combine(this.Name, "CDTV.TM")))
|
||||
return RedumpSystem.CommodoreAmigaCDTV;
|
||||
else
|
||||
return RedumpSystem.CommodoreAmigaCD32;
|
||||
}
|
||||
|
||||
// Mattel Fisher-Price iXL
|
||||
#if NET20 || NET35
|
||||
if (File.Exists(Path.Combine(Path.Combine(this.Name, "iXL"), "iXLUpdater.exe")))
|
||||
@@ -469,6 +500,21 @@ namespace MPF.Core.Data
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Photo CD
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(this.Name, "PHOTO_CD"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "PHOTO_CD")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "PHOTO_CD")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.PhotoCD;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// VCD
|
||||
try
|
||||
{
|
||||
@@ -494,48 +540,48 @@ namespace MPF.Core.Data
|
||||
/// Get the current system from the drive volume label
|
||||
/// </summary>
|
||||
/// <returns>The system based on volume label, null if none detected</returns>
|
||||
public RedumpSystem? GetRedumpSystemFromVolumeLabel()
|
||||
public static RedumpSystem? GetRedumpSystemFromVolumeLabel(string? volumeLabel)
|
||||
{
|
||||
// If the volume label is empty, we can't do anything
|
||||
if (string.IsNullOrEmpty(this.VolumeLabel))
|
||||
if (string.IsNullOrEmpty(volumeLabel))
|
||||
return null;
|
||||
|
||||
// Audio CD
|
||||
if (this.VolumeLabel!.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel!.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.AudioCD;
|
||||
|
||||
// Microsoft Xbox
|
||||
if (this.VolumeLabel.Equals("SEP13011042", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("SEP13011042", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox;
|
||||
else if (this.VolumeLabel.Equals("SEP13011042072", StringComparison.OrdinalIgnoreCase))
|
||||
else if (volumeLabel.Equals("SEP13011042072", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox;
|
||||
|
||||
// Microsoft Xbox 360
|
||||
if (this.VolumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
else if (this.VolumeLabel.Equals("XGD2DVD_NTSC", StringComparison.OrdinalIgnoreCase))
|
||||
else if (volumeLabel.Equals("XGD2DVD_NTSC", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
|
||||
// Microsoft Xbox 360 - Too overly broad even if a lot of discs use this
|
||||
//if (this.VolumeLabel.Equals("CD_ROM", StringComparison.OrdinalIgnoreCase))
|
||||
//if (volumeLabel.Equals("CD_ROM", StringComparison.OrdinalIgnoreCase))
|
||||
// return RedumpSystem.MicrosoftXbox360; // Also for Xbox One?
|
||||
//if (this.VolumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
|
||||
//if (volumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
|
||||
// return RedumpSystem.MicrosoftXbox360;
|
||||
|
||||
// Sega Mega-CD / Sega-CD
|
||||
if (this.VolumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SegaMegaCDSegaCD;
|
||||
|
||||
// Sony PlayStation 3
|
||||
if (this.VolumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SonyPlayStation3;
|
||||
|
||||
// Sony PlayStation 4
|
||||
if (this.VolumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SonyPlayStation4;
|
||||
|
||||
// Sony PlayStation 5
|
||||
if (this.VolumeLabel.Equals("PS5VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("PS5VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SonyPlayStation5;
|
||||
|
||||
return null;
|
||||
|
||||
@@ -321,7 +321,7 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool RedumperEnableVerbose
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "RedumperEnableVerbose", false); }
|
||||
get { return GetBooleanSetting(Settings, "RedumperEnableVerbose", true); }
|
||||
set { Settings["RedumperEnableVerbose"] = value.ToString(); }
|
||||
}
|
||||
|
||||
@@ -553,6 +553,15 @@ namespace MPF.Core.Data
|
||||
set { Settings["IncludeDebugProtectionInformation"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove drive letters from protection scan output
|
||||
/// </summary>
|
||||
public bool HideDriveLetters
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "HideDriveLetters", false); }
|
||||
set { Settings["HideDriveLetters"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Logging Options
|
||||
@@ -608,7 +617,7 @@ namespace MPF.Core.Data
|
||||
/// <param name="settings"></param>
|
||||
public Options(Dictionary<string, string?>? settings = null)
|
||||
{
|
||||
this.Settings = settings ?? new Dictionary<string, string?>();
|
||||
this.Settings = settings ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -617,7 +626,7 @@ namespace MPF.Core.Data
|
||||
/// <param name="source"></param>
|
||||
public Options(Options? source)
|
||||
{
|
||||
Settings = new Dictionary<string, string?>(source?.Settings ?? new Dictionary<string, string?>());
|
||||
Settings = new Dictionary<string, string?>(source?.Settings ?? []);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -374,7 +374,7 @@ namespace MPF.Core
|
||||
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
|
||||
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo);
|
||||
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo, Options.HideDriveLetters);
|
||||
if (scanSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
|
||||
@@ -384,6 +384,66 @@ namespace MPF.Core
|
||||
return di.Units[0]?.Body?.DiscTypeIdentifier;
|
||||
}
|
||||
|
||||
internal static string? GetPlayStationExecutableName(char? driveLetter)
|
||||
{
|
||||
// If there's no drive letter, we can't get exe name
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// Convert drive letter to drive path
|
||||
string drivePath = driveLetter + ":\\";
|
||||
return GetPlayStationExecutableName(drivePath);
|
||||
}
|
||||
|
||||
internal static string? GetPlayStationExecutableName(string? drivePath)
|
||||
{
|
||||
// If there's no drive path, we can't get exe name
|
||||
if (string.IsNullOrEmpty(drivePath))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't get exe name
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// Get the two paths that we will need to check
|
||||
string psxExePath = Path.Combine(drivePath, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
|
||||
|
||||
// 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.?:\\?(.*)", RegexOptions.Compiled);
|
||||
if (match.Groups.Count > 1)
|
||||
{
|
||||
string? serial = match.Groups[1].Value;
|
||||
|
||||
// Some games may have the EXE in a subfolder
|
||||
serial = Path.GetFileName(serial);
|
||||
|
||||
return serial;
|
||||
}
|
||||
}
|
||||
|
||||
// If the SYSTEM.CNF value can't be found, try PSX.EXE
|
||||
if (File.Exists(psxExePath))
|
||||
return "PSX.EXE";
|
||||
|
||||
// If neither can be found, we return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the EXE date from a PlayStation disc, if possible
|
||||
/// </summary>
|
||||
@@ -400,7 +460,7 @@ namespace MPF.Core
|
||||
if (driveLetter == null)
|
||||
return false;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
// Convert drive letter to drive path
|
||||
string drivePath = driveLetter + ":\\";
|
||||
return GetPlayStationExecutableInfo(drivePath, out serial, out region, out date);
|
||||
}
|
||||
@@ -425,55 +485,24 @@ namespace MPF.Core
|
||||
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");
|
||||
// Get the executable name
|
||||
string? exeName = GetPlayStationExecutableName(drivePath);
|
||||
|
||||
// Try both of the common paths that contain information
|
||||
string? exeName = null;
|
||||
|
||||
// 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.?:\\?(.*)", RegexOptions.Compiled);
|
||||
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.IsNullOrEmpty(exeName) && File.Exists(psxExePath))
|
||||
exeName = "PSX.EXE";
|
||||
|
||||
// If neither can be found, we return false
|
||||
if (string.IsNullOrEmpty(exeName))
|
||||
// If no executable found, we can't do this part
|
||||
if (exeName == null)
|
||||
return false;
|
||||
|
||||
// EXE name may have a trailing `;` after
|
||||
// EXE name should always be in all caps
|
||||
exeName = exeName
|
||||
.Split(';')[0]
|
||||
.ToUpperInvariant();
|
||||
|
||||
// Serial is most of the EXE name normalized
|
||||
serial = exeName
|
||||
.Replace('_', '-')
|
||||
.Replace(".", string.Empty);
|
||||
|
||||
// Get the region, if possible
|
||||
region = GetPlayStationRegion(exeName);
|
||||
|
||||
@@ -1361,8 +1390,9 @@ namespace MPF.Core
|
||||
/// <param name="outputDirectory">Output folder to write to</param>
|
||||
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
|
||||
/// <param name="info">SubmissionInfo object containing the protection information</param>
|
||||
/// <param name="hideDriveLetters">True if drive letters are to be removed from output, false otherwise</param>
|
||||
/// <returns>True on success, false on error</returns>
|
||||
public static bool WriteProtectionData(string? outputDirectory, string? filenameSuffix, SubmissionInfo? info)
|
||||
public static bool WriteProtectionData(string? outputDirectory, string? filenameSuffix, SubmissionInfo? info, bool hideDriveLetters)
|
||||
{
|
||||
// Check to see if the inputs are valid
|
||||
if (info?.CopyProtection?.FullProtections == null || !info.CopyProtection.FullProtections.Any())
|
||||
@@ -1382,12 +1412,20 @@ namespace MPF.Core
|
||||
path = Path.Combine(outputDirectory, $"!protectionInfo{filenameSuffix}.txt");
|
||||
|
||||
using var sw = new StreamWriter(File.Open(path, FileMode.Create, FileAccess.Write));
|
||||
foreach (var kvp in info.CopyProtection.FullProtections)
|
||||
|
||||
List<string> sortedKeys = [.. info.CopyProtection.FullProtections.Keys.OrderBy(k => k)];
|
||||
foreach (string key in sortedKeys)
|
||||
{
|
||||
if (kvp.Value == null)
|
||||
sw.WriteLine($"{kvp.Key}: None");
|
||||
string scanPath = key;
|
||||
if (hideDriveLetters)
|
||||
scanPath = Path.DirectorySeparatorChar + key.Substring((Path.GetPathRoot(key) ?? String.Empty).Length);
|
||||
|
||||
List<string>? scanResult = info.CopyProtection.FullProtections[key];
|
||||
|
||||
if (scanResult == null)
|
||||
sw.WriteLine($"{scanPath}: None");
|
||||
else
|
||||
sw.WriteLine($"{kvp.Key}: {string.Join(", ", [.. kvp.Value])}");
|
||||
sw.WriteLine($"{scanPath}: {string.Join(", ", [.. scanResult])}");
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -1467,7 +1505,7 @@ namespace MPF.Core
|
||||
/// <param name="title">Existing title to potentially reformat</param>
|
||||
/// <param name="languages">Array of languages to use for assuming articles</param>
|
||||
/// <returns>The reformatted title</returns>
|
||||
public static string NormalizeDiscTitle(string title, Language[] languages)
|
||||
public static string? NormalizeDiscTitle(string? title, Language?[]? languages)
|
||||
{
|
||||
// If we have no set languages, then assume English
|
||||
if (languages == null || languages.Length == 0)
|
||||
@@ -1477,8 +1515,8 @@ namespace MPF.Core
|
||||
foreach (var language in languages)
|
||||
{
|
||||
// If the new title is different, assume it was normalized and return it
|
||||
string newTitle = NormalizeDiscTitle(title, language);
|
||||
if (newTitle == title)
|
||||
string? newTitle = NormalizeDiscTitle(title, language);
|
||||
if (newTitle != title)
|
||||
return newTitle;
|
||||
}
|
||||
|
||||
@@ -1500,14 +1538,18 @@ namespace MPF.Core
|
||||
/// If the language of the title is unknown or if it's multilingual,
|
||||
/// pass in Language.English for standardized coverage.
|
||||
/// </remarks>
|
||||
public static string NormalizeDiscTitle(string title, Language language)
|
||||
public static string? NormalizeDiscTitle(string? title, Language? language)
|
||||
{
|
||||
// If we have an invalid title, just return it as-is
|
||||
if (string.IsNullOrEmpty(title))
|
||||
return title;
|
||||
|
||||
// If we have an invalid language, assume Language.English
|
||||
if (language == null)
|
||||
language = Language.English;
|
||||
|
||||
// Get the title split into parts
|
||||
string[] splitTitle = title.Split(' ').Where(s => !string.IsNullOrEmpty(s)).ToArray();
|
||||
string[] splitTitle = title!.Split(' ').Where(s => !string.IsNullOrEmpty(s)).ToArray();
|
||||
|
||||
// If we only have one part, we can't do anything
|
||||
if (splitTitle.Length <= 1)
|
||||
@@ -1578,6 +1620,8 @@ namespace MPF.Core
|
||||
case "die"
|
||||
when language is Language.Afrikaans
|
||||
|| language is Language.German:
|
||||
case "du"
|
||||
when language is Language.French:
|
||||
case "e"
|
||||
when language is Language.Papiamento:
|
||||
case "een"
|
||||
@@ -1820,8 +1864,6 @@ namespace MPF.Core
|
||||
case "אַן"
|
||||
when language is Language.Yiddish:
|
||||
|
||||
// Seen by Redump, unknown origin
|
||||
case "du":
|
||||
break;
|
||||
|
||||
// Otherwise, just return it as-is
|
||||
@@ -1867,8 +1909,10 @@ namespace MPF.Core
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return string.Empty;
|
||||
|
||||
// Remove quotes from path
|
||||
// Remove quotes and angle brackets from path
|
||||
path = path!.Replace("\"", string.Empty);
|
||||
path = path!.Replace("<", string.Empty);
|
||||
path = path!.Replace(">", string.Empty);
|
||||
|
||||
// Try getting the combined path and returning that directly
|
||||
string fullPath = getFullPath ? Path.GetFullPath(path) : path;
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.0.2</VersionPrefix>
|
||||
<VersionPrefix>3.1.0</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Common code for all MPF implementations</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2023</Copyright>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
@@ -56,8 +56,8 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="psxt001z.Library" Version="0.21.0-beta3" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.3.0" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.1" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.3.0" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.2" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -74,6 +75,11 @@ namespace MPF.Core.Modules
|
||||
/// </summary>
|
||||
private Process? process;
|
||||
|
||||
/// <summary>
|
||||
/// All found volume labels and their corresponding file systems
|
||||
/// </summary>
|
||||
public Dictionary<string, List<string>>? VolumeLabels;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual Dumping Information
|
||||
@@ -634,7 +640,12 @@ namespace MPF.Core.Modules
|
||||
i++;
|
||||
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return (sbyte)(sbyte.Parse(value) * factor);
|
||||
if (sbyte.TryParse(value, out sbyte sByteValue))
|
||||
return (sbyte)(sByteValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (sbyte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out sbyte sByteHexValue))
|
||||
return (sbyte)(sByteHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
@@ -649,7 +660,12 @@ namespace MPF.Core.Modules
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (sbyte)(sbyte.Parse(value) * factor);
|
||||
if (sbyte.TryParse(value, out sbyte sByteValue))
|
||||
return (sbyte)(sByteValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (sbyte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out sbyte sByteHexValue))
|
||||
return (sbyte)(sByteHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
|
||||
return SByte.MinValue;
|
||||
@@ -711,7 +727,12 @@ namespace MPF.Core.Modules
|
||||
this[longFlagString] = true;
|
||||
i++;
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return (short)(short.Parse(value) * factor);
|
||||
if (short.TryParse(value, out short shortValue))
|
||||
return (short)(shortValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (short.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out short shortHexValue))
|
||||
return (short)(shortHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
@@ -726,7 +747,12 @@ namespace MPF.Core.Modules
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (short)(short.Parse(value) * factor);
|
||||
if (short.TryParse(value, out short shortValue))
|
||||
return (short)(shortValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (short.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out short shortHexValue))
|
||||
return (short)(shortHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
|
||||
return Int16.MinValue;
|
||||
@@ -788,7 +814,12 @@ namespace MPF.Core.Modules
|
||||
this[longFlagString] = true;
|
||||
i++;
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return (int)(int.Parse(value) * factor);
|
||||
if (int.TryParse(value, out int intValue))
|
||||
return (int)(intValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (int.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out int intHexValue))
|
||||
return (int)(intHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
@@ -803,7 +834,12 @@ namespace MPF.Core.Modules
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (int)(int.Parse(value) * factor);
|
||||
if (int.TryParse(value, out int intValue))
|
||||
return (int)(intValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (int.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out int intHexValue))
|
||||
return (int)(intHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
|
||||
return Int32.MinValue;
|
||||
@@ -865,7 +901,12 @@ namespace MPF.Core.Modules
|
||||
this[longFlagString] = true;
|
||||
i++;
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return long.Parse(value) * factor;
|
||||
if (long.TryParse(value, out long longValue))
|
||||
return (long)(longValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (long.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long longHexValue))
|
||||
return (long)(longHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
@@ -880,7 +921,12 @@ namespace MPF.Core.Modules
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return long.Parse(value) * factor;
|
||||
if (long.TryParse(value, out long longValue))
|
||||
return (long)(longValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (long.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long longHexValue))
|
||||
return (long)(longHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
|
||||
return Int64.MinValue;
|
||||
@@ -1018,7 +1064,12 @@ namespace MPF.Core.Modules
|
||||
i++;
|
||||
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return (byte)(byte.Parse(value) * factor);
|
||||
if (byte.TryParse(value, out byte byteValue))
|
||||
return (byte)(byteValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (byte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out byte byteHexValue))
|
||||
return (byte)(byteHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
@@ -1033,14 +1084,19 @@ namespace MPF.Core.Modules
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (byte)(byte.Parse(value) * factor);
|
||||
if (byte.TryParse(value, out byte byteValue))
|
||||
return (byte)(byteValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (byte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out byte byteHexValue))
|
||||
return (byte)(byteHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
|
||||
return Byte.MinValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get yhe trimmed value and multiplication factor from a value
|
||||
/// Get the trimmed value and multiplication factor from a value
|
||||
/// </summary>
|
||||
/// <param name="value">String value to treat as suffixed number</param>
|
||||
/// <returns>Trimmed value and multiplication factor</returns>
|
||||
@@ -1101,6 +1157,23 @@ namespace MPF.Core.Modules
|
||||
return (value, factor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a leading 0x if it exists, case insensitive
|
||||
/// </summary>
|
||||
/// <param name="value">String with removed leading 0x</param>
|
||||
/// <returns></returns>
|
||||
private static string RemoveHexIdentifier(string value)
|
||||
{
|
||||
if (value.Length <= 2)
|
||||
return value;
|
||||
if (value[0] != '0')
|
||||
return value;
|
||||
if (value[1] != 'x' && value[1] != 'X')
|
||||
return value;
|
||||
|
||||
return value.Substring(2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods to Move
|
||||
|
||||
@@ -69,8 +69,12 @@ namespace MPF.Core.Modules.CleanRip
|
||||
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// Get the Datafile information
|
||||
var datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
|
||||
|
||||
// Fill in the hash data
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
|
||||
// Get the individual hash data, as per internal
|
||||
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
@@ -93,11 +97,12 @@ namespace MPF.Core.Modules.CleanRip
|
||||
if (File.Exists(basePath + ".bca"))
|
||||
info.Extras!.BCA = GetBCA(basePath + ".bca");
|
||||
|
||||
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out var gcVersion, out var gcName))
|
||||
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out var gcVersion, out var gcName, out var gcSerial))
|
||||
{
|
||||
info.CommonDiscInfo!.Region = gcRegion ?? info.CommonDiscInfo.Region;
|
||||
info.VersionAndEditions!.Version = gcVersion ?? info.VersionAndEditions.Version;
|
||||
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalName] = gcName ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = gcSerial ?? string.Empty;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -279,10 +284,11 @@ namespace MPF.Core.Modules.CleanRip
|
||||
/// <param name="region">Output region, if possible</param>
|
||||
/// <param name="version">Output internal version of the game</param>
|
||||
/// <param name="name">Output internal name of the game</param>
|
||||
/// <param name="serial">Output internal serial of the game</param>
|
||||
/// <returns></returns>
|
||||
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string? version, out string? name)
|
||||
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string? version, out string? name, out string? serial)
|
||||
{
|
||||
region = null; version = null; name = null;
|
||||
region = null; version = null; name = null; serial = null;
|
||||
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(dumpinfo))
|
||||
@@ -313,7 +319,9 @@ namespace MPF.Core.Modules.CleanRip
|
||||
}
|
||||
else if (line.StartsWith("Filename"))
|
||||
{
|
||||
string serial = line.Substring("Filename: ".Length);
|
||||
serial = line.Substring("Filename: ".Length);
|
||||
if (serial.EndsWith("-disc2"))
|
||||
serial = serial.Replace("-disc2", string.Empty);
|
||||
|
||||
// char gameType = serial[0];
|
||||
// string gameid = serial[1] + serial[2];
|
||||
|
||||
@@ -409,6 +409,10 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// Fill in the hash data
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
|
||||
// Fill in the volume labels
|
||||
if (GetVolumeLabels($"{basePath}_volDesc.txt", out var volLabels))
|
||||
VolumeLabels = volLabels;
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (this.Type)
|
||||
{
|
||||
@@ -496,7 +500,12 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
trimLength = 264;
|
||||
if (info.SizeAndChecksums!.Layerbreak3 != default)
|
||||
trimLength = 520;
|
||||
else if (info.SizeAndChecksums!.Layerbreak2 != default)
|
||||
trimLength = 392;
|
||||
else
|
||||
trimLength = 264;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -532,12 +541,14 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiPython2:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name));
|
||||
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= pythonTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
@@ -776,12 +787,14 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name), true);
|
||||
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= playstationDate;
|
||||
}
|
||||
|
||||
bool? psEdcStatus = null;
|
||||
@@ -795,12 +808,14 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name));
|
||||
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= playstationTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
@@ -2717,6 +2732,89 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all Volume Identifiers
|
||||
/// </summary>
|
||||
/// <param name="volDesc">_volDesc.txt file location</param>
|
||||
/// <returns>Volume labels (by type), or null if none present</returns>
|
||||
private static bool GetVolumeLabels(string volDesc, out Dictionary<string, List<string>> volLabels)
|
||||
{
|
||||
// If the file doesn't exist, can't get the volume labels
|
||||
volLabels = [];
|
||||
if (!File.Exists(volDesc))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
using var sr = File.OpenText(volDesc);
|
||||
var line = sr.ReadLine();
|
||||
|
||||
string volType = "UNKNOWN";
|
||||
string label;
|
||||
while (line != null)
|
||||
{
|
||||
// Trim the line for later use
|
||||
line = line.Trim();
|
||||
|
||||
// ISO9660 and extensions section
|
||||
if (line.StartsWith("Volume Descriptor Type: "))
|
||||
{
|
||||
Int32.TryParse(line.Substring("Volume Descriptor Type: ".Length), out int volTypeInt);
|
||||
volType = volTypeInt switch
|
||||
{
|
||||
// 0 => "Boot Record" // Should not not contain a Volume Identifier
|
||||
1 => "ISO", // ISO9660
|
||||
2 => "Joliet",
|
||||
// 3 => "Volume Partition Descriptor" // Should not not contain a Volume Identifier
|
||||
// 255 => "???" // Should not not contain a Volume Identifier
|
||||
_ => "UNKNOWN" // Should not contain a Volume Identifier
|
||||
};
|
||||
}
|
||||
// UDF section
|
||||
else if (line.StartsWith("Primary Volume Descriptor Number:"))
|
||||
{
|
||||
volType = "UDF";
|
||||
}
|
||||
// Identifier
|
||||
else if (line.StartsWith("Volume Identifier: "))
|
||||
{
|
||||
label = line.Substring("Volume Identifier: ".Length);
|
||||
|
||||
// Remove leading non-printable character (unsure why DIC outputs this)
|
||||
if (Convert.ToUInt32(label[0]) == 0x7F || Convert.ToUInt32(label[0]) < 0x20)
|
||||
label = label.Substring(1);
|
||||
|
||||
// Skip if label is blank
|
||||
if (label == null || label.Length <= 0)
|
||||
{
|
||||
volType = "UNKNOWN";
|
||||
line = sr.ReadLine();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (volLabels.ContainsKey(label))
|
||||
volLabels[label].Add(volType);
|
||||
else
|
||||
volLabels.Add(label, [volType]);
|
||||
|
||||
// Reset volume type
|
||||
volType = "UNKNOWN";
|
||||
}
|
||||
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
// Return true if a volume label was found
|
||||
return volLabels.Count > 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
volLabels = [];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the DVD protection information, if possible
|
||||
/// </summary>
|
||||
@@ -2879,6 +2977,72 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the PSX/PS2/KP2 EXE Date from the log, if possible
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <param name="serial">Internal serial</param>
|
||||
/// <param name="psx">True if PSX disc, false otherwise</param>
|
||||
/// <returns>EXE date if possible, null otherwise</returns>
|
||||
public static string? GetPlayStationEXEDate(string log, string? exeName, bool psx = false)
|
||||
{
|
||||
// If the file doesn't exist, we can't get the info
|
||||
if (!File.Exists(log))
|
||||
return null;
|
||||
|
||||
// If the EXE name is not valid, we can't get the info
|
||||
if (string.IsNullOrEmpty(exeName))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
string? exeDate = null;
|
||||
using var sr = File.OpenText(log);
|
||||
var line = sr.ReadLine();
|
||||
while (line != null)
|
||||
{
|
||||
// Trim the line for later use
|
||||
line = line.Trim();
|
||||
|
||||
// The exe date is listed in a single line, File Identifier: ABCD_123.45;1
|
||||
if (line.Length >= "File Identifier: ".Length + 11 &&
|
||||
line.StartsWith("File Identifier:") &&
|
||||
line.Substring("File Identifier: ".Length) == exeName)
|
||||
{
|
||||
// Account for Y2K date problem
|
||||
if (exeDate != null && exeDate!.Substring(0, 2) == "19")
|
||||
{
|
||||
string decade = exeDate!.Substring(2, 1);
|
||||
// Does only PSX need to account for 1920s-60s?
|
||||
if (decade == "0" || decade == "1" ||
|
||||
psx && (decade == "2" || decade == "3" || decade == "4" || decade == "5" || decade == "6"))
|
||||
exeDate = $"20{exeDate!.Substring(2)}";
|
||||
}
|
||||
|
||||
// Currently stored date is the EXE date, return it
|
||||
return exeDate;
|
||||
}
|
||||
|
||||
// The exe datetime is listed in a single line
|
||||
if (line.Length >= "Recording Date and Time: ".Length + 10 &&
|
||||
line.StartsWith("Recording Date and Time:"))
|
||||
{
|
||||
// exe date: ISO datetime (yyyy-MM-ddT.....)
|
||||
exeDate = line.Substring("Recording Date and Time: ".Length, 10);
|
||||
}
|
||||
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the build info from a GD-ROM LD area, if possible
|
||||
/// </summary>
|
||||
|
||||
@@ -10,8 +10,11 @@ namespace MPF.Core.Modules.Redumper
|
||||
public const string DVD = "dvd"; // Synonym for CD
|
||||
public const string BluRay = "bd"; // Synonym for CD
|
||||
public const string SACD = "sacd"; // Synonym for CD
|
||||
public const string Rings = "rings";
|
||||
public const string Dump = "dump";
|
||||
public const string DumpNew = "dumpnew"; // Temporary command, to be removed later
|
||||
public const string Refine = "refine";
|
||||
public const string RefineNew = "refinenew"; // Temporary command, to be removed later
|
||||
public const string Verify = "verify";
|
||||
public const string DVDKey = "dvdkey";
|
||||
public const string DVDIsoKey = "dvdisokey";
|
||||
@@ -30,6 +33,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
// General
|
||||
public const string HelpLong = "--help";
|
||||
public const string HelpShort = "-h";
|
||||
public const string Version = "--version";
|
||||
public const string Verbose = "--verbose";
|
||||
public const string Debug = "--debug";
|
||||
public const string Drive = "--drive";
|
||||
@@ -48,7 +52,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
public const string DriveSectorOrder = "--drive-sector-order";
|
||||
|
||||
// Drive Specific
|
||||
public const string PlextorLeadinSkip = "--plextor-leadin-skip";
|
||||
public const string PlextorSkipLeadin = "--plextor-skip-leadin";
|
||||
public const string PlextorLeadinRetries = "--plextor-leadin-retries";
|
||||
public const string AsusSkipLeadout = "--asus-skip-leadout";
|
||||
|
||||
@@ -70,7 +74,10 @@ namespace MPF.Core.Modules.Redumper
|
||||
public const string LBAEnd = "--lba-end";
|
||||
public const string RefineSubchannel = "--refine-subchannel";
|
||||
public const string Skip = "--skip";
|
||||
public const string DumpWriteOffset = "--dump-write-offset";
|
||||
public const string DumpReadSize = "--dump-read-size";
|
||||
public const string OverreadLeadout = "--overread-leadout";
|
||||
public const string LegacySubs = "--legacy-subs";
|
||||
public const string DisableCDText = "--disable-cdtext";
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using SabreTools.Models.CueSheets;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
@@ -145,6 +146,11 @@ namespace MPF.Core.Modules.Redumper
|
||||
/// </summary>
|
||||
public string? SkipValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Write offset for dumps when reading as data
|
||||
/// </summary>
|
||||
public int? DumpWriteOffsetValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of sectors to read at once on initial dump, DVD only (Default 32)
|
||||
/// </summary>
|
||||
@@ -190,7 +196,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
if (!File.Exists($"{basePath}.log"))
|
||||
missingFiles.Add($"{basePath}.log");
|
||||
else if (GetDatfile($"{basePath}.log") == null)
|
||||
missingFiles.Add($"{basePath}.dat");
|
||||
missingFiles.Add($"{basePath}.log (dat section)");
|
||||
if (!File.Exists($"{basePath}.state"))
|
||||
missingFiles.Add($"{basePath}.state");
|
||||
if (!File.Exists($"{basePath}.subcode"))
|
||||
@@ -204,6 +210,14 @@ namespace MPF.Core.Modules.Redumper
|
||||
// // Depends on the disc
|
||||
// if (!File.Exists($"{basePath}.cdtext"))
|
||||
// missingFiles.Add($"{basePath}.cdtext");
|
||||
//
|
||||
// // Not available in all versions
|
||||
// if (!File.Exists($"{basePath}.hash"))
|
||||
// missingFiles.Add($"{basePath}.hash");
|
||||
// // Also: "{basePath} (Track X).hash" (get from cuesheet)
|
||||
// if (!File.Exists($"{basePath}.skeleton"))
|
||||
// missingFiles.Add($"{basePath}.skeleton");
|
||||
// // Also: "{basePath} (Track X).skeleton" (get from cuesheet)
|
||||
//}
|
||||
|
||||
break;
|
||||
@@ -223,6 +237,15 @@ namespace MPF.Core.Modules.Redumper
|
||||
missingFiles.Add($"{basePath}.state");
|
||||
}
|
||||
|
||||
// Removed or inconsistent files
|
||||
//{
|
||||
// // Not available in all versions
|
||||
// if (!File.Exists($"{basePath}.hash"))
|
||||
// missingFiles.Add($"{basePath}.hash");
|
||||
// if (!File.Exists($"{basePath}.skeleton"))
|
||||
// missingFiles.Add($"{basePath}.skeleton");
|
||||
//}
|
||||
|
||||
break;
|
||||
|
||||
case MediaType.HDDVD: // TODO: Verify that this is output
|
||||
@@ -239,6 +262,15 @@ namespace MPF.Core.Modules.Redumper
|
||||
missingFiles.Add($"{basePath}.state");
|
||||
}
|
||||
|
||||
// Removed or inconsistent files
|
||||
//{
|
||||
// // Not available in all versions
|
||||
// if (!File.Exists($"{basePath}.hash"))
|
||||
// missingFiles.Add($"{basePath}.hash");
|
||||
// if (!File.Exists($"{basePath}.skeleton"))
|
||||
// missingFiles.Add($"{basePath}.skeleton");
|
||||
//}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -271,6 +303,10 @@ namespace MPF.Core.Modules.Redumper
|
||||
if (GetDiscType($"{basePath}.log", out var discTypeOrBookType))
|
||||
info.DumpingInfo.ReportedDiscType = discTypeOrBookType;
|
||||
|
||||
// Fill in the volume labels
|
||||
if (GetVolumeLabels($"{basePath}.log", out var volLabels))
|
||||
VolumeLabels = volLabels;
|
||||
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
@@ -340,11 +376,19 @@ namespace MPF.Core.Modules.Redumper
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
trimLength = 264;
|
||||
if (info.SizeAndChecksums!.Layerbreak3 != default)
|
||||
trimLength = 520;
|
||||
else if (info.SizeAndChecksums!.Layerbreak2 != default)
|
||||
trimLength = 392;
|
||||
else
|
||||
trimLength = 264;
|
||||
break;
|
||||
}
|
||||
|
||||
info.Extras!.PIC = GetPIC($"{basePath}.physical", trimLength) ?? string.Empty;
|
||||
|
||||
var di = InfoTool.GetDiscInformation($"{basePath}.physical");
|
||||
info.SizeAndChecksums!.PICIdentifier = InfoTool.GetPICIdentifier(di);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -369,12 +413,13 @@ namespace MPF.Core.Modules.Redumper
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiPython2:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= pythonTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
@@ -433,12 +478,13 @@ namespace MPF.Core.Modules.Redumper
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= playstationDate;
|
||||
}
|
||||
|
||||
info.CopyProtection!.AntiModchip = GetPlayStationAntiModchipDetected($"{basePath}.log").ToYesNo();
|
||||
@@ -448,12 +494,13 @@ namespace MPF.Core.Modules.Redumper
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= playstationTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
@@ -489,6 +536,9 @@ namespace MPF.Core.Modules.Redumper
|
||||
info.Artifacts["cue"] = GetBase64(GetFullFile($"{basePath}.cue")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.fulltoc"))
|
||||
info.Artifacts["fulltoc"] = GetBase64(GetFullFile($"{basePath}.fulltoc")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.hash"))
|
||||
info.Artifacts["hash"] = GetBase64(GetFullFile($"{basePath}.hash")) ?? string.Empty;
|
||||
// TODO: "{basePath} (Track X).hash" (get from cuesheet)
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
info.Artifacts["log"] = GetBase64(GetFullFile($"{basePath}.log")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.manufacturer"))
|
||||
@@ -503,6 +553,9 @@ namespace MPF.Core.Modules.Redumper
|
||||
info.Artifacts["physical1"] = GetBase64(GetFullFile($"{basePath}.1.physical")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.2.physical"))
|
||||
info.Artifacts["physical2"] = GetBase64(GetFullFile($"{basePath}.2.physical")) ?? string.Empty;
|
||||
// if (File.Exists($"{basePath}.skeleton"))
|
||||
// info.Artifacts["skeleton"] = GetBase64(GetFullFile($"{basePath}.skeleton")) ?? string.Empty;
|
||||
// // Also: "{basePath} (Track X).skeleton" (get from cuesheet)
|
||||
// if (File.Exists($"{basePath}.scram"))
|
||||
// info.Artifacts["scram"] = GetBase64(GetFullFile($"{basePath}.scram")) ?? string.Empty;
|
||||
// if (File.Exists($"{basePath}.scrap"))
|
||||
@@ -537,6 +590,10 @@ namespace MPF.Core.Modules.Redumper
|
||||
if (this[FlagStrings.HelpLong] == true)
|
||||
parameters.Add(FlagStrings.HelpLong);
|
||||
|
||||
// Version
|
||||
if (this[FlagStrings.Version] == true)
|
||||
parameters.Add(FlagStrings.Version);
|
||||
|
||||
// Verbose
|
||||
if (this[FlagStrings.Verbose] == true)
|
||||
parameters.Add(FlagStrings.Verbose);
|
||||
@@ -635,8 +692,8 @@ namespace MPF.Core.Modules.Redumper
|
||||
#region Drive Specific
|
||||
|
||||
// Plextor Leadin Skip
|
||||
if (this[FlagStrings.PlextorLeadinSkip] == true)
|
||||
parameters.Add(FlagStrings.PlextorLeadinSkip);
|
||||
if (this[FlagStrings.PlextorSkipLeadin] == true)
|
||||
parameters.Add(FlagStrings.PlextorSkipLeadin);
|
||||
|
||||
// Plextor Leadin Retries
|
||||
if (this[FlagStrings.PlextorLeadinRetries] == true)
|
||||
@@ -731,6 +788,13 @@ namespace MPF.Core.Modules.Redumper
|
||||
parameters.Add($"{FlagStrings.Skip}={SkipValue}");
|
||||
}
|
||||
|
||||
// Dump Write Offset
|
||||
if (this[FlagStrings.DumpWriteOffset] == true)
|
||||
{
|
||||
if (DumpWriteOffsetValue != null)
|
||||
parameters.Add($"{FlagStrings.DumpWriteOffset}={DumpWriteOffsetValue}");
|
||||
}
|
||||
|
||||
// Dump Read Size
|
||||
if (this[FlagStrings.DumpReadSize] == true)
|
||||
{
|
||||
@@ -742,6 +806,14 @@ namespace MPF.Core.Modules.Redumper
|
||||
if (this[FlagStrings.OverreadLeadout] == true)
|
||||
parameters.Add(FlagStrings.OverreadLeadout);
|
||||
|
||||
// Legacy Subs
|
||||
if (this[FlagStrings.LegacySubs] == true)
|
||||
parameters.Add(FlagStrings.LegacySubs);
|
||||
|
||||
// Disable CD Text
|
||||
if (this[FlagStrings.DisableCDText] == true)
|
||||
parameters.Add(FlagStrings.DisableCDText);
|
||||
|
||||
#endregion
|
||||
|
||||
return string.Join(" ", [.. parameters]);
|
||||
@@ -758,6 +830,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
// General
|
||||
FlagStrings.HelpLong,
|
||||
FlagStrings.HelpShort,
|
||||
FlagStrings.Version,
|
||||
FlagStrings.Verbose,
|
||||
FlagStrings.Debug,
|
||||
FlagStrings.Drive,
|
||||
@@ -776,7 +849,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
FlagStrings.DriveSectorOrder,
|
||||
|
||||
// Drive Specific
|
||||
FlagStrings.PlextorLeadinSkip,
|
||||
FlagStrings.PlextorSkipLeadin,
|
||||
FlagStrings.PlextorLeadinRetries,
|
||||
FlagStrings.AsusSkipLeadout,
|
||||
|
||||
@@ -798,8 +871,11 @@ namespace MPF.Core.Modules.Redumper
|
||||
FlagStrings.LBAEnd,
|
||||
FlagStrings.RefineSubchannel,
|
||||
FlagStrings.Skip,
|
||||
FlagStrings.DumpWriteOffset,
|
||||
FlagStrings.DumpReadSize,
|
||||
FlagStrings.OverreadLeadout,
|
||||
FlagStrings.LegacySubs,
|
||||
FlagStrings.DisableCDText,
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -840,9 +916,38 @@ namespace MPF.Core.Modules.Redumper
|
||||
logFiles.Add($"{basePath}.subcode");
|
||||
if (File.Exists($"{basePath}.toc"))
|
||||
logFiles.Add($"{basePath}.toc");
|
||||
|
||||
// Include .hash and .skeleton for all files in cuesheet
|
||||
var cueSheet = new SabreTools.Serialization.Files.CueSheet().Deserialize($"{basePath}.cue");
|
||||
string? baseDir = Path.GetDirectoryName(basePath);
|
||||
if (cueSheet?.Files != null && baseDir != null)
|
||||
{
|
||||
foreach (CueFile? file in cueSheet.Files)
|
||||
{
|
||||
string? trackName = Path.GetFileNameWithoutExtension(file?.FileName);
|
||||
if (trackName == null)
|
||||
continue;
|
||||
|
||||
string trackPath = Path.Combine(baseDir, trackName);
|
||||
if (File.Exists($"{trackPath}.hash"))
|
||||
logFiles.Add($"{trackPath}.hash");
|
||||
if (File.Exists($"{trackPath}.skeleton"))
|
||||
logFiles.Add($"{trackPath}.skeleton");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (File.Exists($"{basePath}.hash"))
|
||||
logFiles.Add($"{basePath}.hash");
|
||||
if (File.Exists($"{basePath}.skeleton"))
|
||||
logFiles.Add($"{basePath}.skeleton");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MediaType.DVD:
|
||||
if (File.Exists($"{basePath}.hash"))
|
||||
logFiles.Add($"{basePath}.hash");
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
logFiles.Add($"{basePath}.log");
|
||||
if (File.Exists($"{basePath}.manufacturer"))
|
||||
@@ -857,12 +962,16 @@ namespace MPF.Core.Modules.Redumper
|
||||
logFiles.Add($"{basePath}.1.physical");
|
||||
if (File.Exists($"{basePath}.2.physical"))
|
||||
logFiles.Add($"{basePath}.2.physical");
|
||||
if (File.Exists($"{basePath}.skeleton"))
|
||||
logFiles.Add($"{basePath}.skeleton");
|
||||
if (File.Exists($"{basePath}.state"))
|
||||
logFiles.Add($"{basePath}.state");
|
||||
break;
|
||||
|
||||
case MediaType.HDDVD: // TODO: Confirm that this information outputs
|
||||
case MediaType.BluRay:
|
||||
if (File.Exists($"{basePath}.hash"))
|
||||
logFiles.Add($"{basePath}.hash");
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
logFiles.Add($"{basePath}.log");
|
||||
if (File.Exists($"{basePath}.physical"))
|
||||
@@ -871,6 +980,8 @@ namespace MPF.Core.Modules.Redumper
|
||||
logFiles.Add($"{basePath}.1.physical");
|
||||
if (File.Exists($"{basePath}.2.physical"))
|
||||
logFiles.Add($"{basePath}.2.physical");
|
||||
if (File.Exists($"{basePath}.skeleton"))
|
||||
logFiles.Add($"{basePath}.skeleton");
|
||||
if (File.Exists($"{basePath}.state"))
|
||||
logFiles.Add($"{basePath}.state");
|
||||
break;
|
||||
@@ -1040,8 +1151,11 @@ namespace MPF.Core.Modules.Redumper
|
||||
case CommandStrings.DVD:
|
||||
case CommandStrings.BluRay:
|
||||
case CommandStrings.SACD:
|
||||
case CommandStrings.Rings:
|
||||
case CommandStrings.Dump:
|
||||
case CommandStrings.DumpNew: // Temporary command, to be removed later
|
||||
case CommandStrings.Refine:
|
||||
case CommandStrings.RefineNew: // Temporary command, to be removed later
|
||||
case CommandStrings.Verify:
|
||||
case CommandStrings.DVDKey:
|
||||
case CommandStrings.DVDIsoKey:
|
||||
@@ -1084,6 +1198,9 @@ namespace MPF.Core.Modules.Redumper
|
||||
// Help
|
||||
ProcessFlagParameter(parts, FlagStrings.HelpShort, FlagStrings.HelpLong, ref i);
|
||||
|
||||
// Version
|
||||
ProcessFlagParameter(parts, FlagStrings.Version, ref i);
|
||||
|
||||
// Verbose
|
||||
ProcessFlagParameter(parts, FlagStrings.Verbose, ref i);
|
||||
|
||||
@@ -1156,8 +1273,8 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
#region Drive Specific
|
||||
|
||||
// Plextor Leadin Skip
|
||||
ProcessFlagParameter(parts, FlagStrings.PlextorLeadinSkip, ref i);
|
||||
// Plextor Skip Leadin
|
||||
ProcessFlagParameter(parts, FlagStrings.PlextorSkipLeadin, ref i);
|
||||
|
||||
// Plextor Leadin Retries
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.PlextorLeadinRetries, ref i);
|
||||
@@ -1230,14 +1347,25 @@ namespace MPF.Core.Modules.Redumper
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
SkipValue = stringValue;
|
||||
|
||||
// Skip
|
||||
// Dump Write Offset
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.DumpWriteOffset, ref i);
|
||||
if (intValue != null && intValue != Int32.MinValue)
|
||||
DumpWriteOffsetValue = intValue;
|
||||
|
||||
// Dump Read Size
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.DumpReadSize, ref i);
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
if (intValue != null && intValue != Int32.MinValue)
|
||||
DumpReadSizeValue = intValue;
|
||||
|
||||
// Overread Leadout
|
||||
ProcessFlagParameter(parts, FlagStrings.OverreadLeadout, ref i);
|
||||
|
||||
// Legacy Subs
|
||||
ProcessFlagParameter(parts, FlagStrings.LegacySubs, ref i);
|
||||
|
||||
// Disable CD Text
|
||||
ProcessFlagParameter(parts, FlagStrings.DisableCDText, ref i);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1331,20 +1459,20 @@ namespace MPF.Core.Modules.Redumper
|
||||
/// <summary>
|
||||
/// Get reported disc type information, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">_disc.txt file location</param>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>True if disc type info was set, false otherwise</returns>
|
||||
private static bool GetDiscType(string drive, out string? discTypeOrBookType)
|
||||
private static bool GetDiscType(string log, out string? discTypeOrBookType)
|
||||
{
|
||||
// Set the default values
|
||||
discTypeOrBookType = null;
|
||||
|
||||
// If the file doesn't exist, we can't get the info
|
||||
if (!File.Exists(drive))
|
||||
if (!File.Exists(log))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
using var sr = File.OpenText(drive);
|
||||
using var sr = File.OpenText(log);
|
||||
var line = sr.ReadLine();
|
||||
while (line != null)
|
||||
{
|
||||
@@ -1371,6 +1499,60 @@ namespace MPF.Core.Modules.Redumper
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all Volume Identifiers
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>Volume labels (by type), or null if none present</returns>
|
||||
private static bool GetVolumeLabels(string log, out Dictionary<string, List<string>> volLabels)
|
||||
{
|
||||
// If the file doesn't exist, can't get the volume labels
|
||||
volLabels = [];
|
||||
if (!File.Exists(log))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
using var sr = File.OpenText(log);
|
||||
var line = sr.ReadLine();
|
||||
|
||||
while (line != null)
|
||||
{
|
||||
// Trim the line for later use
|
||||
line = line.Trim();
|
||||
|
||||
// ISO9660 Volume Identifier
|
||||
if (line.StartsWith("volume identifier: "))
|
||||
{
|
||||
string label = line.Substring("volume identifier: ".Length);
|
||||
|
||||
// Skip if label is blank
|
||||
if (label == null || label.Length <= 0)
|
||||
break;
|
||||
|
||||
if (volLabels.ContainsKey(label))
|
||||
volLabels[label].Add("ISO");
|
||||
else
|
||||
volLabels[label] = ["ISO"];
|
||||
|
||||
// Redumper log currently only outputs ISO9660 label, end here
|
||||
break;
|
||||
}
|
||||
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
// Return true if a volume label was found
|
||||
return volLabels.Count > 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
volLabels = [];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the DVD protection information, if possible
|
||||
/// </summary>
|
||||
@@ -1507,6 +1689,45 @@ namespace MPF.Core.Modules.Redumper
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the EXE Date from the log, if possible
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>EXE date if possible, null otherwise</returns>
|
||||
public static string? GetEXEDate(string log)
|
||||
{
|
||||
// If the file doesn't exist, we can't get the info
|
||||
if (!File.Exists(log))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
using var sr = File.OpenText(log);
|
||||
var line = sr.ReadLine();
|
||||
while (line != null)
|
||||
{
|
||||
// Trim the line for later use
|
||||
line = line.Trim();
|
||||
|
||||
// The exe date is listed in a single line
|
||||
if (line.StartsWith("EXE date:"))
|
||||
{
|
||||
// exe date: yyyy-MM-dd
|
||||
return line.Substring("EXE date: ".Length);
|
||||
}
|
||||
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hardware information from the input file, if possible
|
||||
/// </summary>
|
||||
@@ -1852,11 +2073,11 @@ namespace MPF.Core.Modules.Redumper
|
||||
return null;
|
||||
|
||||
// Now that we're at the relevant entries, read each line in and concatenate
|
||||
string? pvdString = "", line = sr.ReadLine()?.Trim();
|
||||
string? pvdString = "", line = sr.ReadLine();
|
||||
while (line?.StartsWith("03") == true)
|
||||
{
|
||||
pvdString += line + "\n";
|
||||
line = sr.ReadLine()?.Trim();
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
return pvdString.TrimEnd('\n');
|
||||
@@ -2178,6 +2399,11 @@ namespace MPF.Core.Modules.Redumper
|
||||
using var sr = File.OpenText(log);
|
||||
sr.ReadLine();
|
||||
|
||||
// Get the next non-warning line
|
||||
string nextLine = sr.ReadLine()?.Trim() ?? string.Empty;
|
||||
if (nextLine.StartsWith("warning:", StringComparison.OrdinalIgnoreCase))
|
||||
nextLine = sr.ReadLine()?.Trim() ?? string.Empty;
|
||||
|
||||
// Generate regex
|
||||
// Permissive
|
||||
var regex = new Regex(@"^redumper (v.+) \[.+\]", RegexOptions.Compiled);
|
||||
@@ -2185,7 +2411,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
//var regex = new Regex(@"^redumper (v\d{4}\.\d{2}\.\d{2}(| build_\d+)) \[.+\]", RegexOptions.Compiled);
|
||||
|
||||
// Extract the version string
|
||||
var match = regex.Match(sr.ReadLine()?.Trim() ?? string.Empty);
|
||||
var match = regex.Match(nextLine);
|
||||
var version = match.Groups[1].Value;
|
||||
return string.IsNullOrEmpty(version) ? null : version;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,15 @@ namespace MPF.Core.Modules.UmdImageCreator
|
||||
|
||||
if (Hasher.GetFileHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
// Get the Datafile information
|
||||
var datafile = new Datafile
|
||||
{
|
||||
Games = [new Game { Roms = [new Rom { Name = string.Empty, Size = filesize.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1, }] }]
|
||||
};
|
||||
|
||||
// Fill in the hash data
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
|
||||
info.SizeAndChecksums!.Size = filesize;
|
||||
info.SizeAndChecksums.CRC32 = crc32;
|
||||
info.SizeAndChecksums.MD5 = md5;
|
||||
|
||||
@@ -110,8 +110,9 @@ namespace MPF.Core
|
||||
info.TracksAndWriteOffsets.ClrMameProData = null;
|
||||
|
||||
// Add the volume label to comments, if possible or necessary
|
||||
if (drive?.VolumeLabel != null && drive.GetRedumpSystemFromVolumeLabel() == null)
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.VolumeLabel] = drive.VolumeLabel;
|
||||
string? volLabels = FormatVolumeLabels(drive?.VolumeLabel, parameters?.VolumeLabels);
|
||||
if (volLabels != null)
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.VolumeLabel] = volLabels;
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (mediaType)
|
||||
@@ -141,6 +142,38 @@ namespace MPF.Core
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
}
|
||||
// If we have a dual-layer disc
|
||||
else if (info.SizeAndChecksums!.Layerbreak2 == default)
|
||||
{
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
}
|
||||
// If we have a triple-layer disc
|
||||
else if (info.SizeAndChecksums!.Layerbreak3 == default)
|
||||
{
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer2MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer2MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer2ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
}
|
||||
// If we have a quad-layer disc
|
||||
else
|
||||
{
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
@@ -153,6 +186,14 @@ namespace MPF.Core
|
||||
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer2MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer2MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer2ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer3MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer3MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer3ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -503,19 +544,28 @@ namespace MPF.Core
|
||||
|| hashData.Contains("(Track 00).bin")
|
||||
|| hashData.Contains("(Track 00.2).bin")
|
||||
|| hashData.Contains("(Track A).bin")
|
||||
|| hashData.Contains("(Track AA).bin"))
|
||||
|| hashData.Contains("(Track A.2).bin")
|
||||
|| hashData.Contains("(Track AA).bin")
|
||||
|| hashData.Contains("(Track AA.2).bin"))
|
||||
{
|
||||
trackCount--;
|
||||
resultProgress?.Report(Result.Success("Extra track found, skipping!"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the SHA-1 hash
|
||||
if (!InfoTool.GetISOHashValues(hashData, out _, out _, out _, out string? sha1))
|
||||
{
|
||||
resultProgress?.Report(Result.Failure($"Line could not be parsed: {hashData}"));
|
||||
continue;
|
||||
}
|
||||
|
||||
#if NET40
|
||||
var validateTask = Validator.ValidateSingleTrack(wc, info, hashData);
|
||||
var validateTask = Validator.ValidateSingleTrack(wc, info, sha1);
|
||||
validateTask.Wait();
|
||||
(bool singleFound, var foundIds, string? result) = validateTask.Result;
|
||||
#else
|
||||
(bool singleFound, var foundIds, string? result) = await Validator.ValidateSingleTrack(wc, info, hashData);
|
||||
(bool singleFound, var foundIds, string? result) = await Validator.ValidateSingleTrack(wc, info, sha1);
|
||||
#endif
|
||||
if (singleFound)
|
||||
resultProgress?.Report(Result.Success(result));
|
||||
@@ -623,6 +673,64 @@ namespace MPF.Core
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Helper Functions
|
||||
|
||||
/// <summary>
|
||||
/// Formats a list of volume labels and their corresponding filesystems
|
||||
/// </summary>
|
||||
/// <param name="labels">Dictionary of volume labels and their filesystems</param>
|
||||
/// <returns>Formatted string of volume labels and their filesystems</returns>
|
||||
private static string? FormatVolumeLabels(string? driveLabel, Dictionary<string, List<string>>? labels)
|
||||
{
|
||||
// Must have at least one label to format
|
||||
if (driveLabel == null && (labels == null || labels.Count == 0))
|
||||
return null;
|
||||
|
||||
// If no labels given, use drive label
|
||||
if (labels == null || labels.Count == 0)
|
||||
{
|
||||
// Ignore common volume labels
|
||||
if (Drive.GetRedumpSystemFromVolumeLabel(driveLabel) != null)
|
||||
return null;
|
||||
|
||||
return driveLabel;
|
||||
}
|
||||
|
||||
// If only one label, don't mention fs
|
||||
string firstLabel = labels.First().Key;
|
||||
if (labels.Count == 1 && (firstLabel == driveLabel || driveLabel == null))
|
||||
{
|
||||
// Ignore common volume labels
|
||||
if (Drive.GetRedumpSystemFromVolumeLabel(firstLabel) != null)
|
||||
return null;
|
||||
|
||||
return firstLabel;
|
||||
}
|
||||
|
||||
// Otherwise, state filesystem for each label
|
||||
List<string> volLabels = [];
|
||||
|
||||
// Begin formatted output with the label from Windows, if it is unique and not a common volume label
|
||||
if (driveLabel != null && !labels.TryGetValue(driveLabel, out List<string>? value) && Drive.GetRedumpSystemFromVolumeLabel(driveLabel) == null)
|
||||
volLabels.Add(driveLabel);
|
||||
|
||||
// Add remaining labels with their corresponding filesystems
|
||||
foreach (KeyValuePair<string, List<string>> label in labels)
|
||||
{
|
||||
// Ignore common volume labels
|
||||
if (Drive.GetRedumpSystemFromVolumeLabel(label.Key) == null)
|
||||
volLabels.Add($"{label.Key} ({string.Join(", ", [.. label.Value])})");
|
||||
}
|
||||
|
||||
// Print each label separated by a comma and a space
|
||||
if (volLabels.Count == 0)
|
||||
return null;
|
||||
|
||||
return string.Join(", ", [.. volLabels]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
432
MPF.Core/UI/ViewModels/CheckDumpViewModel.cs
Normal file
432
MPF.Core/UI/ViewModels/CheckDumpViewModel.cs
Normal file
@@ -0,0 +1,432 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.UI.ComboBoxItems;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public class CheckDumpViewModel : INotifyPropertyChanged
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Access to the current options
|
||||
/// </summary>
|
||||
public Data.Options Options
|
||||
{
|
||||
get => _options;
|
||||
}
|
||||
private readonly Data.Options _options;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if SelectionChanged events can be executed
|
||||
/// </summary>
|
||||
public bool CanExecuteSelectionChanged { get; private set; } = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected system value
|
||||
/// </summary>
|
||||
public RedumpSystem? CurrentSystem
|
||||
{
|
||||
get => _currentSystem;
|
||||
set
|
||||
{
|
||||
_currentSystem = value;
|
||||
TriggerPropertyChanged(nameof(CurrentSystem));
|
||||
}
|
||||
}
|
||||
private RedumpSystem? _currentSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the system type combo box
|
||||
/// </summary>
|
||||
public bool SystemTypeComboBoxEnabled
|
||||
{
|
||||
get => _systemTypeComboBoxEnabled;
|
||||
set
|
||||
{
|
||||
_systemTypeComboBoxEnabled = value;
|
||||
TriggerPropertyChanged(nameof(SystemTypeComboBoxEnabled));
|
||||
}
|
||||
}
|
||||
private bool _systemTypeComboBoxEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected media type value
|
||||
/// </summary>
|
||||
public MediaType? CurrentMediaType
|
||||
{
|
||||
get => _currentMediaType;
|
||||
set
|
||||
{
|
||||
_currentMediaType = value;
|
||||
TriggerPropertyChanged(nameof(CurrentMediaType));
|
||||
}
|
||||
}
|
||||
private MediaType? _currentMediaType;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the media type combo box
|
||||
/// </summary>
|
||||
public bool MediaTypeComboBoxEnabled
|
||||
{
|
||||
get => _mediaTypeComboBoxEnabled;
|
||||
set
|
||||
{
|
||||
_mediaTypeComboBoxEnabled = value;
|
||||
TriggerPropertyChanged(nameof(MediaTypeComboBoxEnabled));
|
||||
}
|
||||
}
|
||||
private bool _mediaTypeComboBoxEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Currently provided input path
|
||||
/// </summary>
|
||||
public string? InputPath
|
||||
{
|
||||
get => _inputPath;
|
||||
set
|
||||
{
|
||||
_inputPath = value;
|
||||
TriggerPropertyChanged(nameof(InputPath));
|
||||
}
|
||||
}
|
||||
private string? _inputPath;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the input path text box
|
||||
/// </summary>
|
||||
public bool InputPathTextBoxEnabled
|
||||
{
|
||||
get => _inputPathTextBoxEnabled;
|
||||
set
|
||||
{
|
||||
_inputPathTextBoxEnabled = value;
|
||||
TriggerPropertyChanged(nameof(InputPathTextBoxEnabled));
|
||||
}
|
||||
}
|
||||
private bool _inputPathTextBoxEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the input path browse button
|
||||
/// </summary>
|
||||
public bool InputPathBrowseButtonEnabled
|
||||
{
|
||||
get => _inputPathBrowseButtonEnabled;
|
||||
set
|
||||
{
|
||||
_inputPathBrowseButtonEnabled = value;
|
||||
TriggerPropertyChanged(nameof(InputPathBrowseButtonEnabled));
|
||||
}
|
||||
}
|
||||
private bool _inputPathBrowseButtonEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected dumping program
|
||||
/// </summary>
|
||||
public InternalProgram CurrentProgram
|
||||
{
|
||||
get => _currentProgram;
|
||||
set
|
||||
{
|
||||
_currentProgram = value;
|
||||
TriggerPropertyChanged(nameof(CurrentProgram));
|
||||
}
|
||||
}
|
||||
private InternalProgram _currentProgram;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the dumping program combo box
|
||||
/// </summary>
|
||||
public bool DumpingProgramComboBoxEnabled
|
||||
{
|
||||
get => _dumpingProgramComboBoxEnabled;
|
||||
set
|
||||
{
|
||||
_dumpingProgramComboBoxEnabled = value;
|
||||
TriggerPropertyChanged(nameof(DumpingProgramComboBoxEnabled));
|
||||
}
|
||||
}
|
||||
private bool _dumpingProgramComboBoxEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the check dump button
|
||||
/// </summary>
|
||||
public bool CheckDumpButtonEnabled
|
||||
{
|
||||
get => _checkDumpButtonEnabled;
|
||||
set
|
||||
{
|
||||
_checkDumpButtonEnabled = value;
|
||||
TriggerPropertyChanged(nameof(CheckDumpButtonEnabled));
|
||||
}
|
||||
}
|
||||
private bool _checkDumpButtonEnabled;
|
||||
|
||||
#endregion
|
||||
|
||||
#region List Properties
|
||||
|
||||
/// <summary>
|
||||
/// Current list of supported media types
|
||||
/// </summary>
|
||||
public List<Element<MediaType>>? MediaTypes
|
||||
{
|
||||
get => _mediaTypes;
|
||||
set
|
||||
{
|
||||
_mediaTypes = value;
|
||||
TriggerPropertyChanged(nameof(MediaTypes));
|
||||
}
|
||||
}
|
||||
private List<Element<MediaType>>? _mediaTypes;
|
||||
|
||||
/// <summary>
|
||||
/// Current list of supported system profiles
|
||||
/// </summary>
|
||||
public List<RedumpSystemComboBoxItem> Systems
|
||||
{
|
||||
get => _systems;
|
||||
set
|
||||
{
|
||||
_systems = value;
|
||||
TriggerPropertyChanged(nameof(Systems));
|
||||
}
|
||||
}
|
||||
private List<RedumpSystemComboBoxItem> _systems;
|
||||
|
||||
/// <summary>
|
||||
/// List of available internal programs
|
||||
/// </summary>
|
||||
public List<Element<InternalProgram>> InternalPrograms
|
||||
{
|
||||
get => _internalPrograms;
|
||||
set
|
||||
{
|
||||
_internalPrograms = value;
|
||||
TriggerPropertyChanged(nameof(InternalPrograms));
|
||||
}
|
||||
}
|
||||
private List<Element<InternalProgram>> _internalPrograms;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for pure view model
|
||||
/// </summary>
|
||||
public CheckDumpViewModel()
|
||||
{
|
||||
_options = OptionsLoader.LoadFromConfig();
|
||||
_internalPrograms = [];
|
||||
_inputPath = string.Empty;
|
||||
_systems = [];
|
||||
|
||||
SystemTypeComboBoxEnabled = true;
|
||||
InputPathTextBoxEnabled = true;
|
||||
InputPathBrowseButtonEnabled = true;
|
||||
MediaTypeComboBoxEnabled = true;
|
||||
DumpingProgramComboBoxEnabled = true;
|
||||
CheckDumpButtonEnabled = false;
|
||||
|
||||
MediaTypes = [];
|
||||
Systems = RedumpSystemComboBoxItem.GenerateElements().ToList();
|
||||
InternalPrograms = [];
|
||||
|
||||
PopulateMediaType();
|
||||
PopulateInternalPrograms();
|
||||
EnableEventHandlers();
|
||||
}
|
||||
|
||||
#region Property Updates
|
||||
|
||||
/// <summary>
|
||||
/// Trigger a property changed event
|
||||
/// </summary>
|
||||
private void TriggerPropertyChanged(string propertyName)
|
||||
{
|
||||
// Disable event handlers temporarily
|
||||
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
|
||||
DisableEventHandlers();
|
||||
|
||||
// If the property change event is initialized
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
// Reenable event handlers, if necessary
|
||||
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Commands
|
||||
|
||||
/// <summary>
|
||||
/// Change the currently selected system
|
||||
/// </summary>
|
||||
public void ChangeSystem()
|
||||
{
|
||||
PopulateMediaType();
|
||||
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the currently selected media type
|
||||
/// </summary>
|
||||
public void ChangeMediaType()
|
||||
{
|
||||
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the currently selected dumping program
|
||||
/// </summary>
|
||||
public void ChangeDumpingProgram()
|
||||
{
|
||||
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the currently selected input path
|
||||
/// </summary>
|
||||
public void ChangeInputPath()
|
||||
{
|
||||
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Population
|
||||
|
||||
/// <summary>
|
||||
/// Populate media type according to system type
|
||||
/// </summary>
|
||||
private void PopulateMediaType()
|
||||
{
|
||||
// Disable other UI updates
|
||||
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
|
||||
DisableEventHandlers();
|
||||
|
||||
if (this.CurrentSystem != null)
|
||||
{
|
||||
var mediaTypeValues = this.CurrentSystem.MediaTypes();
|
||||
int index = mediaTypeValues.FindIndex(m => m == this.CurrentMediaType);
|
||||
|
||||
MediaTypes = mediaTypeValues.Select(m => new Element<MediaType>(m ?? MediaType.NONE)).ToList();
|
||||
this.MediaTypeComboBoxEnabled = MediaTypes.Count > 1;
|
||||
this.CurrentMediaType = (index > -1 ? MediaTypes[index] : MediaTypes[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.MediaTypeComboBoxEnabled = false;
|
||||
this.MediaTypes = null;
|
||||
this.CurrentMediaType = null;
|
||||
}
|
||||
|
||||
// Reenable event handlers, if necessary
|
||||
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate media type according to system type
|
||||
/// </summary>
|
||||
private void PopulateInternalPrograms()
|
||||
{
|
||||
// Disable other UI updates
|
||||
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
|
||||
DisableEventHandlers();
|
||||
|
||||
// Get the current internal program
|
||||
InternalProgram internalProgram = this.Options.InternalProgram;
|
||||
|
||||
// Create a static list of supported Check programs, not everything
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.CleanRip, InternalProgram.Redumper, InternalProgram.UmdImageCreator };
|
||||
InternalPrograms = internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
|
||||
|
||||
// Select the current default dumping program
|
||||
int currentIndex = InternalPrograms.FindIndex(m => m == internalProgram);
|
||||
this.CurrentProgram = (currentIndex > -1 ? InternalPrograms[currentIndex].Value : InternalPrograms[0].Value);
|
||||
|
||||
// Reenable event handlers, if necessary
|
||||
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Functionality
|
||||
|
||||
private bool ShouldEnableCheckDumpButton()
|
||||
{
|
||||
return this.CurrentSystem != null && this.CurrentMediaType != null && !string.IsNullOrEmpty(this.InputPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable all textbox and combobox event handlers
|
||||
/// </summary>
|
||||
private void EnableEventHandlers()
|
||||
{
|
||||
CanExecuteSelectionChanged = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disable all textbox and combobox event handlers
|
||||
/// </summary>
|
||||
private void DisableEventHandlers()
|
||||
{
|
||||
CanExecuteSelectionChanged = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MPF.Check
|
||||
|
||||
/// <summary>
|
||||
/// Performs MPF.Check functionality
|
||||
/// </summary>
|
||||
/// <returns>An error message if failed, otherwise string.Empty/null</returns>
|
||||
public string? CheckDump(Func<SubmissionInfo?, (bool?, SubmissionInfo?)> processUserInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(InputPath))
|
||||
return "Invalid Input path";
|
||||
|
||||
if (!File.Exists(this.InputPath!.Trim('"')))
|
||||
return "Input Path is not a valid file";
|
||||
|
||||
// Populate an environment
|
||||
var env = new DumpEnvironment(Options, Path.GetFullPath(this.InputPath.Trim('"')), null, this.CurrentSystem, this.CurrentMediaType, this.CurrentProgram, parameters: null);
|
||||
|
||||
// Make new Progress objects
|
||||
var resultProgress = new Progress<Result>();
|
||||
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
|
||||
var protectionProgress = new Progress<ProtectionProgress>();
|
||||
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
|
||||
|
||||
// Finally, attempt to do the output dance
|
||||
#if NET40
|
||||
var resultTask = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress);
|
||||
resultTask.Wait();
|
||||
var result = resultTask.Result;
|
||||
#else
|
||||
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress, processUserInfo).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
#endif
|
||||
|
||||
return result.Message;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -217,11 +217,13 @@ namespace MPF.Core.UI.ViewModels
|
||||
/// TODO: Convert selected list item to binding
|
||||
public void Save()
|
||||
{
|
||||
if (SubmissionInfo.CommonDiscInfo == null) SubmissionInfo.CommonDiscInfo = new CommonDiscInfoSection();
|
||||
if (SubmissionInfo.CommonDiscInfo == null)
|
||||
SubmissionInfo.CommonDiscInfo = new CommonDiscInfoSection();
|
||||
SubmissionInfo.CommonDiscInfo.Languages = Languages.Where(l => l.IsChecked).Select(l => l?.Value).ToArray();
|
||||
if (!SubmissionInfo.CommonDiscInfo.Languages.Any())
|
||||
SubmissionInfo.CommonDiscInfo.Languages = new Language?[] { null };
|
||||
SubmissionInfo.CommonDiscInfo.Languages = [null];
|
||||
SubmissionInfo.CommonDiscInfo.LanguageSelection = LanguageSelections.Where(ls => ls.IsChecked).Select(ls => ls?.Value).ToArray();
|
||||
SubmissionInfo.CommonDiscInfo.Title = InfoTool.NormalizeDiscTitle(SubmissionInfo.CommonDiscInfo.Title, SubmissionInfo.CommonDiscInfo.Languages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// T1 - Title to display to the user
|
||||
/// T1 - Message to display to the user
|
||||
/// T2 - Message to display to the user
|
||||
/// T3 - Number of default options to display
|
||||
/// T4 - true for inquiry, false otherwise
|
||||
/// TResult - true for positive, false for negative, null for neutral
|
||||
@@ -76,6 +76,20 @@ namespace MPF.Core.UI.ViewModels
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the check dump menu item
|
||||
/// </summary>
|
||||
public bool CheckDumpMenuItemEnabled
|
||||
{
|
||||
get => _checkDumpMenuItemEnabled;
|
||||
set
|
||||
{
|
||||
_checkDumpMenuItemEnabled = value;
|
||||
TriggerPropertyChanged(nameof(CheckDumpMenuItemEnabled));
|
||||
}
|
||||
}
|
||||
private bool _checkDumpMenuItemEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the options menu item
|
||||
/// </summary>
|
||||
@@ -148,6 +162,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
|
||||
/// <summary>
|
||||
/// Currently provided output path
|
||||
/// Not guaranteed to be a valid path
|
||||
/// </summary>
|
||||
public string OutputPath
|
||||
{
|
||||
@@ -506,6 +521,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
_systems = [];
|
||||
|
||||
OptionsMenuItemEnabled = true;
|
||||
CheckDumpMenuItemEnabled = true;
|
||||
SystemTypeComboBoxEnabled = true;
|
||||
MediaTypeComboBoxEnabled = true;
|
||||
OutputPathTextBoxEnabled = true;
|
||||
@@ -1190,7 +1206,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
return new DumpEnvironment(
|
||||
this.Options,
|
||||
this.OutputPath,
|
||||
EvaluateOutputPath(this.OutputPath),
|
||||
this.CurrentDrive,
|
||||
this.CurrentSystem,
|
||||
this.CurrentMediaType,
|
||||
@@ -1299,6 +1315,49 @@ namespace MPF.Core.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces %-delimited variables inside a path string with their values
|
||||
/// </summary>
|
||||
/// <param name="outputPath">Path to be evaluated</param>
|
||||
/// <returns>String with %-delimited variables evaluated</returns>
|
||||
public string EvaluateOutputPath(string outputPath)
|
||||
{
|
||||
string systemLong = this._currentSystem.LongName() ?? "Unknown System";
|
||||
if (string.IsNullOrEmpty(systemLong))
|
||||
systemLong = "Unknown System";
|
||||
string systemShort = this._currentSystem.ShortName() ?? "unknown";
|
||||
if (string.IsNullOrEmpty(systemShort))
|
||||
systemShort = "unknown";
|
||||
string mediaLong = this._currentMediaType.LongName() ?? "Unknown Media";
|
||||
if (string.IsNullOrEmpty(mediaLong))
|
||||
mediaLong = "Unknown Media";
|
||||
string program = this._currentProgram.ToString() ?? "Unknown Program";
|
||||
if (string.IsNullOrEmpty(program))
|
||||
program = "Unknown Program";
|
||||
string programShort = program == "DiscImageCreator" ? "DIC" : program;
|
||||
if (string.IsNullOrEmpty(programShort))
|
||||
programShort = "Unknown Program";
|
||||
string label = this._currentDrive?.FormattedVolumeLabel ?? "track";
|
||||
if (string.IsNullOrEmpty(label))
|
||||
label = "track";
|
||||
string date = DateTime.Today.ToString("yyyyMMdd");
|
||||
if (string.IsNullOrEmpty(date))
|
||||
date = "UNKNOWN";
|
||||
string datetime = DateTime.Now.ToString("yyyyMMdd-HHmmss");
|
||||
if (string.IsNullOrEmpty(datetime))
|
||||
datetime = "UNKNOWN";
|
||||
|
||||
return outputPath
|
||||
.Replace("%SYSTEM%", systemLong)
|
||||
.Replace("%SYS%", systemShort)
|
||||
.Replace("%MEDIA%", mediaLong)
|
||||
.Replace("%PROGRAM%", program)
|
||||
.Replace("%PROG%", programShort)
|
||||
.Replace("%LABEL%", label)
|
||||
.Replace("%DATE%", date)
|
||||
.Replace("%DATETIME%", datetime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the default output directory name from the currently selected drive
|
||||
/// </summary>
|
||||
@@ -1730,6 +1789,51 @@ namespace MPF.Core.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
// If a complete dump exists from a different program
|
||||
InternalProgram? programFound = null;
|
||||
if (programFound == null && _environment.InternalProgram != InternalProgram.Aaru)
|
||||
{
|
||||
Modules.Aaru.Parameters parameters = new("")
|
||||
{
|
||||
Type = _environment.Type,
|
||||
System = _environment.System
|
||||
};
|
||||
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
|
||||
if (foundOtherFiles)
|
||||
programFound = InternalProgram.Aaru;
|
||||
}
|
||||
if (programFound == null && _environment.InternalProgram != InternalProgram.DiscImageCreator)
|
||||
{
|
||||
Modules.DiscImageCreator.Parameters parameters = new("")
|
||||
{
|
||||
Type = _environment.Type,
|
||||
System = _environment.System
|
||||
};
|
||||
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
|
||||
if (foundOtherFiles)
|
||||
programFound = InternalProgram.DiscImageCreator;
|
||||
}
|
||||
if (programFound == null && _environment.InternalProgram != InternalProgram.Redumper)
|
||||
{
|
||||
Modules.Redumper.Parameters parameters = new("")
|
||||
{
|
||||
Type = _environment.Type,
|
||||
System = _environment.System
|
||||
};
|
||||
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
|
||||
if (foundOtherFiles)
|
||||
programFound = InternalProgram.Redumper;
|
||||
}
|
||||
if (programFound != null && _displayUserMessage != null)
|
||||
{
|
||||
bool? mbresult = _displayUserMessage("Overwrite?", $"A complete dump from {programFound} already exists! Dumping here may cause issues. Are you sure you want to overwrite?", 2, true);
|
||||
if (mbresult != true)
|
||||
{
|
||||
LogLn("Dumping aborted!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that at least some space exists
|
||||
// TODO: Tie this to the size of the disc, type of disc, etc.
|
||||
string fullPath;
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace MPF.Core.Utilities
|
||||
string? parsedPath = null;
|
||||
|
||||
// These values require multiple parts to be active
|
||||
bool scan = false, protectFile = false;
|
||||
bool scan = false, protectFile = false, hideDriveLetters = false;
|
||||
|
||||
// If we have no arguments, just return
|
||||
if (args == null || args.Length == 0)
|
||||
@@ -174,6 +174,12 @@ namespace MPF.Core.Utilities
|
||||
protectFile = true;
|
||||
}
|
||||
|
||||
// Hide drive letters from scan output (requires --protect-file)
|
||||
else if (args[startIndex].Equals("-g") || args[startIndex].Equals("--hide-drive-letters"))
|
||||
{
|
||||
hideDriveLetters = true;
|
||||
}
|
||||
|
||||
// Include seed info file
|
||||
else if (args[startIndex].StartsWith("-l=") || args[startIndex].StartsWith("--load-seed="))
|
||||
{
|
||||
@@ -221,6 +227,7 @@ namespace MPF.Core.Utilities
|
||||
// Now deal with the complex options
|
||||
options.ScanForProtection = scan && !string.IsNullOrEmpty(parsedPath);
|
||||
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrEmpty(parsedPath);
|
||||
options.HideDriveLetters = hideDriveLetters && scan && protectFile && !string.IsNullOrEmpty(parsedPath);
|
||||
|
||||
return (options, info, parsedPath, startIndex);
|
||||
}
|
||||
@@ -238,6 +245,7 @@ namespace MPF.Core.Utilities
|
||||
"-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)",
|
||||
"-g, --hide-drive-letters Hide drive letters from scan output (requires --protect-file)",
|
||||
"-l, --load-seed <path> Load a seed submission JSON for user information",
|
||||
"-x, --suffix Enable adding filename suffix",
|
||||
"-j, --json Enable submission JSON output",
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace MPF.Test.Library
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null, "")]
|
||||
[InlineData(" ", "")]
|
||||
[InlineData(" ", " ")]
|
||||
[InlineData("super\\blah.bin", "super\\blah.bin")]
|
||||
[InlineData("super\\hero\\blah.bin", "super\\hero\\blah.bin")]
|
||||
[InlineData("super.hero\\blah.bin", "super.hero\\blah.bin")]
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.9.0-preview-23531-01" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0-preview-23531-01" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.1" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.2" />
|
||||
<PackageReference Include="xunit" Version="2.6.2" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="1.6.0" />
|
||||
|
||||
@@ -5,6 +5,8 @@ using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using MPF.UI.Core;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace WPFCustomMessageBox
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -5,19 +5,20 @@
|
||||
<TargetFrameworks>net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0-windows;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<ImportFrameworkWinFXTargets Condition="$(TargetFramework.StartsWith(`net3`))">true</ImportFrameworkWinFXTargets>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.0.2</VersionPrefix>
|
||||
<VersionPrefix>3.1.0</VersionPrefix>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Common code for all MPF UI implementations</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2023</Copyright>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
@@ -41,7 +42,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.1" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -7,6 +7,8 @@ using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using MPF.Core.Data;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace MPF.UI.Core.UserControls
|
||||
{
|
||||
public partial class LogOutput : UserControl
|
||||
@@ -115,18 +117,13 @@ namespace MPF.UI.Core.UserControls
|
||||
/// <returns>Brush representing the color</returns>
|
||||
public Brush GetForegroundColor()
|
||||
{
|
||||
switch (this.LogLevel)
|
||||
return this.LogLevel switch
|
||||
{
|
||||
case LogLevel.SECRET:
|
||||
return Brushes.Blue;
|
||||
case LogLevel.ERROR:
|
||||
return Brushes.Red;
|
||||
case LogLevel.VERBOSE:
|
||||
return Brushes.Yellow;
|
||||
case LogLevel.USER:
|
||||
default:
|
||||
return Brushes.White;
|
||||
}
|
||||
LogLevel.SECRET => Brushes.Blue,
|
||||
LogLevel.ERROR => Brushes.Red,
|
||||
LogLevel.VERBOSE => Brushes.Yellow,
|
||||
_ => Brushes.White,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -206,13 +203,11 @@ namespace MPF.UI.Core.UserControls
|
||||
/// </summary>
|
||||
private void SaveInlines()
|
||||
{
|
||||
using (var sw = new StreamWriter(File.OpenWrite("console.log")))
|
||||
using var sw = new StreamWriter(File.OpenWrite("console.log"));
|
||||
foreach (var inline in _paragraph.Inlines)
|
||||
{
|
||||
foreach (var inline in _paragraph.Inlines)
|
||||
{
|
||||
if (inline is Run run)
|
||||
sw.Write(run.Text);
|
||||
}
|
||||
if (inline is Run run)
|
||||
sw.Write(run.Text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
130
MPF.UI.Core/Windows/CheckDumpWindow.xaml
Normal file
130
MPF.UI.Core/Windows/CheckDumpWindow.xaml
Normal file
@@ -0,0 +1,130 @@
|
||||
<coreWindows:WindowBase x:Class="MPF.UI.Core.Windows.CheckDumpWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:core="clr-namespace:MPF.UI.Core"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
|
||||
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
|
||||
mc:Ignorable="d"
|
||||
Title="Check Existing Dump" Width="600" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<Window.DataContext>
|
||||
<viewModels:CheckDumpViewModel/>
|
||||
</Window.DataContext>
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Grid Margin="0,2,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="25"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
|
||||
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
|
||||
<Label.Content>
|
||||
<Run FontWeight="Bold" Text="Check Existing Dump" />
|
||||
</Label.Content>
|
||||
<Label.ContextMenu>
|
||||
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Style="{DynamicResource CustomContextMenuStyle}">
|
||||
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}"/>
|
||||
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}"/>
|
||||
</ContextMenu>
|
||||
</Label.ContextMenu>
|
||||
</Label>
|
||||
<Grid Grid.Column="5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
|
||||
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
|
||||
</Button>
|
||||
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
|
||||
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Settings">
|
||||
<Grid Margin="5,5,5,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="2.5*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label x:Name="InputPathLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Input Path"/>
|
||||
<TextBox x:Name="InputPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding InputPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding InputPathTextBoxEnabled}" />
|
||||
<Button x:Name="InputPathBrowseButton" Grid.Row="0" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
IsEnabled="{Binding InputPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
|
||||
|
||||
<Label x:Name="SystemMediaTypeLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
|
||||
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="1" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding Systems}" SelectedItem="{Binding Path=CurrentSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding SystemTypeComboBoxEnabled}" 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>
|
||||
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="1" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right"
|
||||
ItemsSource="{Binding MediaTypes}" SelectedItem="{Binding Path=CurrentMediaType, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding MediaTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="DumpingProgramLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Dumping Program"/>
|
||||
<ComboBox x:Name="DumpingProgramComboBox" Grid.Row="2" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding InternalPrograms}" SelectedItem="{Binding Path=CurrentProgram, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding DumpingProgramComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- Check Dump / 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 -->
|
||||
<Button Name="CheckDumpButton" Height="25" Width="80" IsDefault="True" Content="Check Dump"
|
||||
IsEnabled="{Binding CheckDumpButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</coreWindows:WindowBase>
|
||||
282
MPF.UI.Core/Windows/CheckDumpWindow.xaml.cs
Normal file
282
MPF.UI.Core/Windows/CheckDumpWindow.xaml.cs
Normal file
@@ -0,0 +1,282 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using MPF.Core.UI.ViewModels;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using WPFCustomMessageBox;
|
||||
using WinForms = System.Windows.Forms;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for CheckDumpWindow.xaml
|
||||
/// </summary>
|
||||
public partial class CheckDumpWindow : WindowBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Read-only access to the current check dump view model
|
||||
/// </summary>
|
||||
public CheckDumpViewModel CheckDumpViewModel => DataContext as CheckDumpViewModel ?? new CheckDumpViewModel();
|
||||
|
||||
#if NET35
|
||||
|
||||
#region Settings
|
||||
|
||||
private ComboBox? _DumpingProgramComboBox => ItemHelper.FindChild<ComboBox>(this, "DumpingProgramComboBox");
|
||||
private Button? _InputPathBrowseButton => ItemHelper.FindChild<Button>(this, "InputPathBrowseButton");
|
||||
private TextBox? _InputPathTextBox => ItemHelper.FindChild<TextBox>(this, "InputPathTextBox");
|
||||
private ComboBox? _MediaTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "MediaTypeComboBox");
|
||||
private ComboBox? _SystemTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "SystemTypeComboBox");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controls
|
||||
|
||||
private System.Windows.Controls.Button? _CheckDumpButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CheckDumpButton");
|
||||
private System.Windows.Controls.Button? _CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
|
||||
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CheckDumpWindow(MainWindow parent)
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
var chrome = new System.Windows.Shell.WindowChrome
|
||||
{
|
||||
CaptionHeight = 0,
|
||||
ResizeBorderThickness = new Thickness(0),
|
||||
};
|
||||
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckDumpWindow OnContentRendered event
|
||||
/// </summary>
|
||||
protected override void OnContentRendered(EventArgs e)
|
||||
{
|
||||
base.OnContentRendered(e);
|
||||
|
||||
// Add the click handlers to the UI
|
||||
AddEventHandlers();
|
||||
}
|
||||
|
||||
#region UI Functionality
|
||||
|
||||
/// <summary>
|
||||
/// Add all event handlers
|
||||
/// </summary>
|
||||
public void AddEventHandlers()
|
||||
{
|
||||
// Main buttons
|
||||
#if NET35
|
||||
_CheckDumpButton!.Click += OnCheckDumpClick;
|
||||
_CancelButton!.Click += OnCancelClick;
|
||||
#else
|
||||
CheckDumpButton.Click += OnCheckDumpClick;
|
||||
CancelButton.Click += OnCancelClick;
|
||||
#endif
|
||||
|
||||
// User Area Click
|
||||
#if NET35
|
||||
_InputPathBrowseButton!.Click += InputPathBrowseButtonClick;
|
||||
#else
|
||||
InputPathBrowseButton.Click += InputPathBrowseButtonClick;
|
||||
#endif
|
||||
|
||||
// User Area SelectionChanged
|
||||
#if NET35
|
||||
_SystemTypeComboBox!.SelectionChanged += SystemTypeComboBoxSelectionChanged;
|
||||
_MediaTypeComboBox!.SelectionChanged += MediaTypeComboBoxSelectionChanged;
|
||||
_DumpingProgramComboBox!.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
|
||||
#else
|
||||
SystemTypeComboBox.SelectionChanged += SystemTypeComboBoxSelectionChanged;
|
||||
MediaTypeComboBox.SelectionChanged += MediaTypeComboBoxSelectionChanged;
|
||||
DumpingProgramComboBox.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
|
||||
#endif
|
||||
|
||||
// User Area TextChanged
|
||||
#if NET35
|
||||
_InputPathTextBox!.TextChanged += InputPathTextBoxTextChanged;
|
||||
#else
|
||||
InputPathTextBox.TextChanged += InputPathTextBoxTextChanged;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Browse for an input file path
|
||||
/// </summary>
|
||||
public void BrowseFile()
|
||||
{
|
||||
// Get the current path, if possible
|
||||
string? currentPath = CheckDumpViewModel.InputPath;
|
||||
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CheckDumpViewModel.Options.DefaultOutputPath))
|
||||
currentPath = CheckDumpViewModel.Options.DefaultOutputPath!;
|
||||
if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
|
||||
|
||||
// Get the full directory
|
||||
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
|
||||
|
||||
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
|
||||
{
|
||||
InitialDirectory = directory,
|
||||
};
|
||||
WinForms.DialogResult result = fileDialog.ShowDialog();
|
||||
|
||||
if (result == WinForms.DialogResult.OK)
|
||||
{
|
||||
CheckDumpViewModel.InputPath = fileDialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display a user message using a CustomMessageBox
|
||||
/// </summary>
|
||||
/// <param name="title">Title to display to the user</param>
|
||||
/// <param name="message">Message to display to the user</param>
|
||||
/// <param name="optionCount">Number of options to display</param>
|
||||
/// <param name="flag">true for inquiry, false otherwise</param>
|
||||
/// <returns>true for positive, false for negative, null for neutral</returns>
|
||||
public bool? DisplayUserMessage(string title, string message, int optionCount, bool flag)
|
||||
{
|
||||
// Set the correct button style
|
||||
var button = optionCount switch
|
||||
{
|
||||
1 => MessageBoxButton.OK,
|
||||
2 => MessageBoxButton.YesNo,
|
||||
3 => MessageBoxButton.YesNoCancel,
|
||||
|
||||
// This should not happen, but default to "OK"
|
||||
_ => MessageBoxButton.OK,
|
||||
};
|
||||
|
||||
// Set the correct icon
|
||||
MessageBoxImage image = flag ? MessageBoxImage.Question : MessageBoxImage.Exclamation;
|
||||
|
||||
// Display and get the result
|
||||
MessageBoxResult result = CustomMessageBox.Show(this, message, title, button, image);
|
||||
return result switch
|
||||
{
|
||||
MessageBoxResult.OK or MessageBoxResult.Yes => true,
|
||||
MessageBoxResult.No => false,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the disc information window
|
||||
/// </summary>
|
||||
/// <param name="submissionInfo">SubmissionInfo object to display and possibly change</param>
|
||||
/// <returns>Dialog open result</returns>
|
||||
public (bool?, SubmissionInfo?) ShowDiscInformationWindow(SubmissionInfo? submissionInfo)
|
||||
{
|
||||
var discInformationWindow = new DiscInformationWindow(CheckDumpViewModel.Options, submissionInfo)
|
||||
{
|
||||
Focusable = true,
|
||||
Owner = this,
|
||||
ShowActivated = true,
|
||||
ShowInTaskbar = true,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
discInformationWindow.Closed += delegate { this.Activate(); };
|
||||
bool? result = discInformationWindow.ShowDialog();
|
||||
|
||||
// Copy back the submission info changes, if necessary
|
||||
if (result == true)
|
||||
submissionInfo = (discInformationWindow.DiscInformationViewModel.SubmissionInfo.Clone() as SubmissionInfo)!;
|
||||
|
||||
return (result, submissionInfo!);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckDumpButton Click event
|
||||
/// </summary>
|
||||
private void OnCheckDumpClick(object sender, EventArgs e)
|
||||
{
|
||||
string? errorMessage = CheckDumpViewModel.CheckDump(ShowDiscInformationWindow);
|
||||
if (string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
bool? checkAgain = DisplayUserMessage("Check Complete", "The dump has been processed successfully! Would you like to check another dump?", 2, false);
|
||||
if (checkAgain == false)
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayUserMessage("Check Failed", errorMessage!, 1, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CancelButtom Click event
|
||||
/// </summary>
|
||||
private void OnCancelClick(object sender, EventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for DumpingProgramComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void DumpingProgramComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (CheckDumpViewModel.CanExecuteSelectionChanged)
|
||||
CheckDumpViewModel.ChangeDumpingProgram();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for InputPathBrowseButton Click event
|
||||
/// </summary>
|
||||
public void InputPathBrowseButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BrowseFile();
|
||||
if (CheckDumpViewModel.CanExecuteSelectionChanged)
|
||||
CheckDumpViewModel.ChangeInputPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for InputPathTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void InputPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (CheckDumpViewModel.CanExecuteSelectionChanged)
|
||||
CheckDumpViewModel.ChangeInputPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for MediaTypeComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void MediaTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (CheckDumpViewModel.CanExecuteSelectionChanged)
|
||||
CheckDumpViewModel.ChangeMediaType();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for SystemTypeComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void SystemTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (CheckDumpViewModel.CanExecuteSelectionChanged)
|
||||
CheckDumpViewModel.ChangeSystem();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ using MPF.Core.Utilities;
|
||||
using MPF.UI.Core.UserControls;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
@@ -464,6 +466,8 @@ namespace MPF.UI.Core.Windows
|
||||
case DiscType.NintendoWiiOpticalDiscSL:
|
||||
case DiscType.NintendoWiiOpticalDiscDL:
|
||||
case DiscType.NintendoWiiUOpticalDiscSL:
|
||||
case DiscType.UMDSL:
|
||||
case DiscType.UMDDL:
|
||||
// Quad-layer discs
|
||||
if (submissionInfo?.SizeAndChecksums?.Layerbreak3 != default(long))
|
||||
{
|
||||
|
||||
@@ -49,6 +49,11 @@
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}">
|
||||
<MenuItem x:Name="CheckDumpMenuItem" Header="_Check Dump" HorizontalAlignment="Left" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}"
|
||||
IsEnabled="{Binding CheckDumpMenuItemEnabled}"/>
|
||||
<MenuItem x:Name="OptionsMenuItem" Header="_Options" HorizontalAlignment="Left" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
|
||||
@@ -9,6 +9,8 @@ using SabreTools.RedumpLib.Data;
|
||||
using WPFCustomMessageBox;
|
||||
using WinForms = System.Windows.Forms;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
public partial class MainWindow : WindowBase
|
||||
@@ -26,6 +28,7 @@ namespace MPF.UI.Core.Windows
|
||||
private MenuItem? _AppExitMenuItem => ItemHelper.FindChild<MenuItem>(this, "AppExitMenuItem");
|
||||
private MenuItem? _CheckForUpdatesMenuItem => ItemHelper.FindChild<MenuItem>(this, "CheckForUpdatesMenuItem");
|
||||
private MenuItem? _DebugViewMenuItem => ItemHelper.FindChild<MenuItem>(this, "DebugViewMenuItem");
|
||||
private MenuItem? _CheckDumpMenuItem => ItemHelper.FindChild<MenuItem>(this, "CheckDumpMenuItem");
|
||||
private MenuItem? _OptionsMenuItem => ItemHelper.FindChild<MenuItem>(this, "OptionsMenuItem");
|
||||
|
||||
#endregion
|
||||
@@ -137,12 +140,14 @@ namespace MPF.UI.Core.Windows
|
||||
_AppExitMenuItem!.Click += AppExitClick;
|
||||
_CheckForUpdatesMenuItem!.Click += CheckForUpdatesClick;
|
||||
_DebugViewMenuItem!.Click += DebugViewClick;
|
||||
_CheckDumpMenuItem!.Click += CheckDumpMenuItemClick;
|
||||
_OptionsMenuItem!.Click += OptionsMenuItemClick;
|
||||
#else
|
||||
AboutMenuItem.Click += AboutClick;
|
||||
AppExitMenuItem.Click += AppExitClick;
|
||||
CheckForUpdatesMenuItem.Click += CheckForUpdatesClick;
|
||||
DebugViewMenuItem.Click += DebugViewClick;
|
||||
CheckDumpMenuItem.Click += CheckDumpMenuItemClick;
|
||||
OptionsMenuItem.Click += OptionsMenuItemClick;
|
||||
#endif
|
||||
|
||||
@@ -311,6 +316,31 @@ namespace MPF.UI.Core.Windows
|
||||
return (result, submissionInfo!);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the Check Dump window
|
||||
/// </summary>
|
||||
public void ShowCheckDumpWindow()
|
||||
{
|
||||
// Hide MainWindow while Check GUI is open
|
||||
this.Hide();
|
||||
|
||||
var checkDumpWindow = new CheckDumpWindow(this)
|
||||
{
|
||||
Focusable = true,
|
||||
Owner = this,
|
||||
ShowActivated = true,
|
||||
ShowInTaskbar = true,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
checkDumpWindow.Closed += delegate {
|
||||
// Unhide Main window after Check window has been closed
|
||||
this.Show();
|
||||
this.Activate();
|
||||
};
|
||||
checkDumpWindow.Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the Options window
|
||||
/// </summary>
|
||||
@@ -384,6 +414,12 @@ namespace MPF.UI.Core.Windows
|
||||
public void AppExitClick(object sender, RoutedEventArgs e) =>
|
||||
Application.Current.Shutdown();
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckDumpMenuItem Click event
|
||||
/// </summary>
|
||||
public void CheckDumpMenuItemClick(object sender, RoutedEventArgs e) =>
|
||||
ShowCheckDumpWindow();
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckForUpdatesMenuItem Click event
|
||||
/// </summary>
|
||||
|
||||
@@ -181,7 +181,8 @@
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Output Path" />
|
||||
<TextBox x:Name="DefaultOutputPathTextBox" Grid.Row="4" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
Text="{Binding Options.DefaultOutputPath}" VerticalContentAlignment="Center" />
|
||||
Text="{Binding Options.DefaultOutputPath}" VerticalContentAlignment="Center"
|
||||
ToolTip="Variables allowed:
 %SYSTEM%	(System name, long)
 %SYS%		(System name, short)
 %MEDIA%	(Media type)
 %PROGRAM%	(Program name, long)
 %PROG%	(Program name, short)
 %LABEL%	(Volume label)
 %DATE%	(Current date)
 %DATETIME%	(Current date and time)"/>
|
||||
<Button x:Name="DefaultOutputPathButton" Grid.Row="4" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
</Grid>
|
||||
@@ -264,7 +265,7 @@
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Protection">
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<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"
|
||||
@@ -284,6 +285,11 @@
|
||||
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
|
||||
ToolTip="Include debug information during protection scans" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Hide Drive Letters"
|
||||
IsChecked="{Binding Options.HideDriveLetters}"
|
||||
ToolTip="Remove drive letters from protection scan output" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ using MPF.Core.Data;
|
||||
using MPF.Core.UI.ViewModels;
|
||||
using WPFCustomMessageBox;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -7,19 +7,20 @@
|
||||
<OutputType>WinExe</OutputType>
|
||||
<ApplicationIcon>Images\Icon.ico</ApplicationIcon>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<ImportFrameworkWinFXTargets Condition="$(TargetFramework.StartsWith(`net3`))">true</ImportFrameworkWinFXTargets>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.0.2</VersionPrefix>
|
||||
<VersionPrefix>3.1.0</VersionPrefix>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Frontend for various dumping programs</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2023</Copyright>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
@@ -66,15 +67,12 @@
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<Reference Include="PresentationFramework.Aero" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`)) OR $(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
|
||||
<Reference Include="PresentationFramework.Aero2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.2" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.1" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# version format
|
||||
version: 3.0.2-{build}
|
||||
version: 3.1.0-{build}
|
||||
|
||||
# pull request template
|
||||
pull_requests:
|
||||
@@ -48,8 +48,8 @@ after_build:
|
||||
- 7z e DiscImageCreator_20230606.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Creator Release_ANSI\*
|
||||
|
||||
# Redumper
|
||||
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_271/redumper-2023.11.30_build271-win64.zip
|
||||
- 7z e redumper-2023.11.30_build271-win64.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Redumper redumper-2023.11.30_build271-win64\bin\*
|
||||
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_311/redumper-2024.01.08_build311-win64.zip
|
||||
- 7z e redumper-2024.01.08_build311-win64.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Redumper redumper-2024.01.08_build311-win64\bin\*
|
||||
|
||||
# Create MPF Debug archives
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net8.0-windows\win-x64\publish\
|
||||
|
||||
207
publish-nix.sh
207
publish-nix.sh
@@ -10,8 +10,32 @@
|
||||
# If any of these are not satisfied, the operation may fail
|
||||
# in an unpredictable way and result in an incomplete output.
|
||||
|
||||
# TODO: Re-enable MPF building after figuring out how to build Windows desktop applications on Linux
|
||||
# This may require an additional package to be installed?
|
||||
# Optional parameters
|
||||
USE_ALL=false
|
||||
INCLUDE_PROGRAMS=false # TODO: Figure out how to exclude folders with `zip`
|
||||
NO_BUILD=false
|
||||
NO_ARCHIVE=false
|
||||
while getopts "upba" OPTION
|
||||
do
|
||||
case $OPTION in
|
||||
u)
|
||||
USE_ALL=true
|
||||
;;
|
||||
p)
|
||||
INCLUDE_PROGRAMS=true
|
||||
;;
|
||||
b)
|
||||
NO_BUILD=true
|
||||
;;
|
||||
a)
|
||||
NO_ARCHIVE=true
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option provided"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Set the current directory as a variable
|
||||
BUILD_FOLDER=$PWD
|
||||
@@ -19,74 +43,127 @@ BUILD_FOLDER=$PWD
|
||||
# Set the current commit hash
|
||||
COMMIT=`git log --pretty=%H -1`
|
||||
|
||||
# Restore Nuget packages for all builds
|
||||
echo "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
# Create the build matrix arrays
|
||||
UI_FRAMEWORKS=("net6.0-windows" "net8.0-windows")
|
||||
UI_RUNTIMES=("win-x64")
|
||||
CHECK_FRAMEWORKS=("net6.0" "net8.0")
|
||||
CHECK_RUNTIMES=("win-x64" "linux-x64" "osx-x64")
|
||||
|
||||
# .NET 6.0 Debug
|
||||
echo "Building .NET 6.0 debug"
|
||||
#dotnet publish MPF/MPF.csproj -f net6.0-windows -r win-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r win-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r linux-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r osx-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
# Use expanded lists, if requested
|
||||
if [ $USE_ALL = true ]
|
||||
then
|
||||
UI_FRAMEWORKS=("net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0-windows" "net6.0-windows" "net7.0-windows" "net8.0-windows")
|
||||
UI_RUNTIMES=("win-x86" "win-x64")
|
||||
CHECK_FRAMEWORKS=("net20" "net35" "net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0")
|
||||
CHECK_RUNTIMES=("win-x86" "win-x64" "win-arm64" "linux-x64" "linux-arm64" "osx-x64")
|
||||
fi
|
||||
|
||||
# .NET 6.0 Release
|
||||
echo "Building .NET 6.0 release"
|
||||
#dotnet publish MPF/MPF.csproj -f net6.0-windows -r win-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r win-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r linux-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r osx-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
# Create the filter arrays
|
||||
SINGLE_FILE_CAPABLE=("net5.0" "net5.0-windows" "net6.0" "net6.0-windows" "net7.0" "net7.0-windows" "net8.0" "net8.0-windows")
|
||||
VALID_CROSS_PLATFORM_FRAMEWORKS=("netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0")
|
||||
VALID_CROSS_PLATFORM_RUNTIMES=("win-arm64" "linux-x64" "linux-arm64" "osx-x64")
|
||||
|
||||
# .NET 8.0 Debug
|
||||
echo "Building .NET 8.0 debug"
|
||||
#dotnet publish MPF/MPF.csproj -f net8.0-windows -r win-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r win-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r linux-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r osx-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
# Only build if requested
|
||||
if [ $NO_BUILD = false ]
|
||||
then
|
||||
# Restore Nuget packages for all builds
|
||||
echo "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# .NET 8.0 Release
|
||||
echo "Building .NET 8.0 release"
|
||||
#dotnet publish MPF/MPF.csproj -f net8.0-windows -r win-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r win-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r linux-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r osx-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
# Build UI
|
||||
for FRAMEWORK in "${UI_FRAMEWORKS[@]}"
|
||||
do
|
||||
for RUNTIME in "${UI_RUNTIMES[@]}"
|
||||
do
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]
|
||||
then
|
||||
dotnet publish MPF/MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF/MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
else
|
||||
dotnet publish MPF/MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
dotnet publish MPF/MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Create MPF Debug archives
|
||||
#cd $BUILD_FOLDER/MPF/bin/Debug/net6.0-windows/win-x64/publish/
|
||||
#zip -r $BUILD_FOLDER/MPF_net6.0_win-x64_debug.zip .
|
||||
#cd $BUILD_FOLDER/MPF/bin/Debug/net8.0-windows/win-x64/publish/
|
||||
#zip -r $BUILD_FOLDER/MPF_net8.0_win-x64_debug.zip .
|
||||
# Build Check
|
||||
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"
|
||||
do
|
||||
for RUNTIME in "${CHECK_RUNTIMES[@]}"
|
||||
do
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]
|
||||
then
|
||||
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]
|
||||
then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create MPF Release archives
|
||||
#cd $BUILD_FOLDER/MPF/bin/Release/net6.0-windows/win-x64/publish/
|
||||
#zip -r $BUILD_FOLDER/MPF_net6.0_win-x64_release.zip .
|
||||
#cd $BUILD_FOLDER/MPF/bin/Release/net8.0-windows/win-x64/publish/
|
||||
#zip -r $BUILD_FOLDER/MPF_net8.0_win-x64_release.zip .
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]
|
||||
then
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
else
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
# Create MPF.Check Debug archives
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net6.0/win-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_win-x64_debug.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net6.0/linux-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_linux-x64_debug.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net6.0/osx-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_osx-x64_debug.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net8.0/win-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_win-x64_debug.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net8.0/linux-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_linux-x64_debug.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net8.0/osx-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_osx-x64_debug.zip .
|
||||
# Only create archives if requested
|
||||
if [ $NO_ARCHIVE = false ]
|
||||
then
|
||||
# Create UI archives
|
||||
for FRAMEWORK in "${UI_FRAMEWORKS[@]}"
|
||||
do
|
||||
echo "Framework: $FRAMEWORK"
|
||||
for RUNTIME in "${UI_RUNTIMES[@]}"
|
||||
do
|
||||
echo "Runtime: $RUNTIME"
|
||||
cd $BUILD_FOLDER/MPF/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
echo "PWD: $PWD"
|
||||
if [ $INCLUDE_PROGRAMS = true ]
|
||||
then
|
||||
zip -r $BUILD_FOLDER/MPF_${FRAMEWORK}_${RUNTIME}_debug.zip .
|
||||
else
|
||||
zip -r $BUILD_FOLDER/MPF_${FRAMEWORK}_${RUNTIME}_debug.zip . -x 'Programs/\*'
|
||||
fi
|
||||
cd $BUILD_FOLDER/MPF/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
echo "PWD: $PWD"
|
||||
if [ $INCLUDE_PROGRAMS = true ]
|
||||
then
|
||||
zip -r $BUILD_FOLDER/MPF_${FRAMEWORK}_${RUNTIME}_release.zip .
|
||||
else
|
||||
zip -r $BUILD_FOLDER/MPF_${FRAMEWORK}_${RUNTIME}_release.zip . -x 'Programs/\*'
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Create MPF.Check Release archives
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net6.0/win-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_win-x64_release.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net6.0/linux-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_linux-x64_release.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net6.0/osx-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_osx-x64_release.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net8.0/win-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_win-x64_release.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net8.0/linux-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_linux-x64_release.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net8.0/osx-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_osx-x64_release.zip .
|
||||
# Create Check archives
|
||||
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"
|
||||
do
|
||||
for RUNTIME in "${CHECK_RUNTIMES[@]}"
|
||||
do
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]
|
||||
then
|
||||
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]
|
||||
then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_${FRAMEWORK}_${RUNTIME}_debug.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_${FRAMEWORK}_${RUNTIME}_release.zip .
|
||||
done
|
||||
done
|
||||
|
||||
# Reset the directory
|
||||
cd $BUILD_FOLDER
|
||||
fi
|
||||
@@ -1,89 +0,0 @@
|
||||
@echo OFF
|
||||
|
||||
REM This batch file assumes the following:
|
||||
REM - .NET 8.0 (or newer) SDK is installed and in PATH
|
||||
REM - 7-zip commandline (7z.exe) is installed and in PATH
|
||||
REM - Git for Windows is installed and in PATH
|
||||
REM - The relevant commandline programs are already downloaded
|
||||
REM and put into their respective folders
|
||||
REM
|
||||
REM If any of these are not satisfied, the operation may fail
|
||||
REM in an unpredictable way and result in an incomplete output.
|
||||
|
||||
REM Set the current directory as a variable
|
||||
set BUILD_FOLDER=%~dp0
|
||||
|
||||
REM Set the current commit hash
|
||||
for /f %%i in ('git log --pretty^=%%H -1') do set COMMIT=%%i
|
||||
|
||||
REM Restore Nuget packages for all builds
|
||||
echo Restoring Nuget packages
|
||||
dotnet restore
|
||||
|
||||
REM .NET 6.0 Debug
|
||||
echo Building .NET 6.0 debug
|
||||
dotnet publish MPF\MPF.csproj -f net6.0-windows -r win-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r win-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r linux-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r osx-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
|
||||
REM .NET 6.0 Release
|
||||
echo Building .NET 6.0 release
|
||||
dotnet publish MPF\MPF.csproj -f net6.0-windows -r win-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r win-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r linux-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r osx-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
|
||||
REM .NET 8.0 Debug
|
||||
echo Building .NET 8.0 debug
|
||||
dotnet publish MPF\MPF.csproj -f net8.0-windows -r win-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r win-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r linux-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r osx-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
|
||||
REM .NET 8.0 Release
|
||||
echo Building .NET 8.0 release
|
||||
dotnet publish MPF\MPF.csproj -f net8.0-windows -r win-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r win-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r linux-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r osx-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
|
||||
REM Create MPF Debug archives
|
||||
cd %BUILD_FOLDER%\MPF\bin\Debug\net6.0-windows\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF_net6.0_win-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF\bin\Debug\net8.0-windows\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF_net8.0_win-x64_debug.zip *
|
||||
|
||||
REM Create MPF Release archives
|
||||
cd %BUILD_FOLDER%\MPF\bin\Release\net6.0-windows\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF_net6.0_win-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF\bin\Release\net8.0-windows\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF_net8.0_win-x64_release.zip *
|
||||
|
||||
REM Create MPF.Check Debug archives
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_win-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\linux-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_linux-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\osx-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_osx-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_win-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\linux-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_linux-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\osx-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_osx-x64_debug.zip *
|
||||
|
||||
REM Create MPF.Check Release archives
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net6.0\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_win-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net6.0\linux-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_linux-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net6.0\osx-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_osx-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net8.0\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_win-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net8.0\linux-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_linux-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net8.0\osx-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_osx-x64_release.zip *
|
||||
@@ -42,7 +42,7 @@ $CHECK_RUNTIMES = @('win-x64', 'linux-x64', 'osx-x64')
|
||||
# Use expanded lists, if requested
|
||||
if ($USE_ALL.IsPresent)
|
||||
{
|
||||
$UI_FRAMEWORKS = @('net35', 'net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0-windows', 'net6.0-windows', 'net7.0-windows', 'net8.0-windows')
|
||||
$UI_FRAMEWORKS = @('net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0-windows', 'net6.0-windows', 'net7.0-windows', 'net8.0-windows')
|
||||
$UI_RUNTIMES = @('win-x86', 'win-x64')
|
||||
$CHECK_FRAMEWORKS = @('net20', 'net35', 'net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0')
|
||||
$CHECK_RUNTIMES = @('win-x86', 'win-x64', 'win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64')
|
||||
@@ -113,7 +113,7 @@ if (!$NO_ARCHIVE.IsPresent)
|
||||
{
|
||||
foreach ($RUNTIME in $UI_RUNTIMES)
|
||||
{
|
||||
Set-Location -Path $BUILD_FOLDER\MPF\bin\Debug\$FRAMEWORK\$RUNTIME\publish\
|
||||
Set-Location -Path $BUILD_FOLDER\MPF\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
if ($INCLUDE_PROGRAMS.IsPresent)
|
||||
{
|
||||
7z a -tzip $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_debug.zip *
|
||||
@@ -122,7 +122,7 @@ if (!$NO_ARCHIVE.IsPresent)
|
||||
{
|
||||
7z a -tzip -x!Programs\* $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_debug.zip *
|
||||
}
|
||||
Set-Location -Path $BUILD_FOLDER\MPF\bin\Release\$FRAMEWORK\$RUNTIME\publish\
|
||||
Set-Location -Path $BUILD_FOLDER\MPF\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
if ($INCLUDE_PROGRAMS.IsPresent)
|
||||
{
|
||||
7z a -tzip $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_release.zip *
|
||||
@@ -145,9 +145,9 @@ if (!$NO_ARCHIVE.IsPresent)
|
||||
continue
|
||||
}
|
||||
|
||||
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Debug\$FRAMEWORK\$RUNTIME\publish\
|
||||
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
7z a -tzip $BUILD_FOLDER\MPF.Check_${FRAMEWORK}_${RUNTIME}_debug.zip *
|
||||
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Release\$FRAMEWORK\${RUNTIME}\publish\
|
||||
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
7z a -tzip $BUILD_FOLDER\MPF.Check_${FRAMEWORK}_${RUNTIME}_release.zip *
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user