mirror of
https://github.com/SabreTools/MPF.git
synced 2026-02-04 05:35:52 +00:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9012ff85a9 | ||
|
|
ccc33bebbd | ||
|
|
57b07aee02 | ||
|
|
069d676492 | ||
|
|
a26dfb7e7a | ||
|
|
a9ea457808 | ||
|
|
41bc410452 | ||
|
|
bbfdf462d0 | ||
|
|
5a1d51c05f | ||
|
|
16e80f75cf | ||
|
|
2a6e066707 | ||
|
|
d6102107fb | ||
|
|
79163dcb35 | ||
|
|
c1aa863c91 | ||
|
|
dbd4b55dda | ||
|
|
1f58521f51 | ||
|
|
4da1ab9c29 | ||
|
|
5771add8c0 | ||
|
|
977a71d9cf | ||
|
|
b2d09d04ea | ||
|
|
78df92e5d3 | ||
|
|
9770b7c917 | ||
|
|
1ddb287977 | ||
|
|
b9e4bbf744 | ||
|
|
08829ed811 | ||
|
|
bf181e2294 | ||
|
|
49571c6bfc | ||
|
|
9a0bc868f8 | ||
|
|
bc2c08690d | ||
|
|
493cb80624 | ||
|
|
49b8ecf6c3 | ||
|
|
c9b7ad7819 | ||
|
|
fe76387f6a | ||
|
|
0a60fe5a37 | ||
|
|
baffdb8b29 | ||
|
|
4a3c585a8d | ||
|
|
1ee7ea1948 | ||
|
|
ee08bfe0fc | ||
|
|
896caec9cd | ||
|
|
68932ab473 | ||
|
|
d105d04146 | ||
|
|
7023c78d40 | ||
|
|
81156a3c63 | ||
|
|
7b2b06a36f | ||
|
|
3f974ab336 | ||
|
|
b238616685 | ||
|
|
2984459823 | ||
|
|
ce979b3c3f | ||
|
|
cb8e1fd34b | ||
|
|
cc148735f8 | ||
|
|
8238d14f7b | ||
|
|
a56676501e | ||
|
|
79716ea0b5 | ||
|
|
43477a133f | ||
|
|
8889beef1d | ||
|
|
ac4be751b3 | ||
|
|
69f855fc93 | ||
|
|
cf01095623 | ||
|
|
3236223e3f | ||
|
|
36450cd22b | ||
|
|
6a9b6748d2 | ||
|
|
3bf83378a2 | ||
|
|
bc938fd58c | ||
|
|
f50a110acd | ||
|
|
fcda2a6e3b | ||
|
|
d33526b27e | ||
|
|
0b0427e9c1 | ||
|
|
c34b92bad8 | ||
|
|
89145df0fa |
@@ -1,3 +1,76 @@
|
||||
### 3.5.0 (2025-10-10)
|
||||
|
||||
- Add failure if media type could not be determined
|
||||
- Tweaks to how failure cases are reported
|
||||
- Rename log zip on collision
|
||||
- Update packages
|
||||
- Use CommandLine library for CLI executables
|
||||
- Create interactive mode features
|
||||
- Minor cleanup around last added
|
||||
- Add placeholder command set creation
|
||||
- Reduce unnecessary shared code
|
||||
- Create and use main features for CLI and Check
|
||||
- Create and use base feature in Check
|
||||
- Create and use base feature in CLI
|
||||
- Assign inputs for interactive modes
|
||||
- Remove duplicate input declarations
|
||||
- Fix strange invocations of extension methods
|
||||
- Remove CommandOptions implementations
|
||||
- Exit early on parsing failures
|
||||
- Fix minor typo in verify inputs check
|
||||
- Allow but do not require config for Check
|
||||
- Update packages
|
||||
- More consistency in commandline programs
|
||||
- Use GC.SharpCompress as archive handling library
|
||||
- Start wiring through log compression changes
|
||||
- Fix minor issues with options loading
|
||||
- Finalize wire-through and clean up
|
||||
- Minor cleanup on interactive modes
|
||||
- Add more useful credentials inputs for Check
|
||||
- More gracefully handle "missing" media types
|
||||
- Use null or empty instead of just null
|
||||
- Guard against unzippable files
|
||||
- Fix incorrect flagging of a failed check
|
||||
- Use ZipWriterOptions instead of generic
|
||||
- Allow skeleton creation for all media types
|
||||
- Fix default value tests
|
||||
- Only allow skeleton creation for CD and DVD
|
||||
- Add preemptive new file support
|
||||
- Add preemptive helper for Zstd handling
|
||||
- Pre-compress skeleton files with Zstd
|
||||
- Fix broken file count tests
|
||||
- Pre-compress state files with Zstd
|
||||
- Use block-based reading instead of CopyTo
|
||||
- Enable skeleton output for all CLI runs
|
||||
- Fix test broken by last commit
|
||||
- Support detecting split Wii for CleanRip
|
||||
- Try to handle Windows-specific compression issue
|
||||
|
||||
### 3.4.2 (2025-09-30)
|
||||
|
||||
- Fix starting index for CLI
|
||||
- Fix missed package update
|
||||
- Require exact versions for build
|
||||
|
||||
### 3.4.1 (2025-09-29)
|
||||
|
||||
- Experiment with only showing media type box for DIC
|
||||
- Default media type box to hidden to avoid visual issues
|
||||
- Limit media type visibility further
|
||||
- Set media type visibility when options changed
|
||||
- Change label with media type visibility
|
||||
- Update media type visibility on system change
|
||||
- Return full result from dump checks
|
||||
- Fix logic from around Macrovision security driver filtering
|
||||
- Remove SkipMediaTypeDetection option, cleanup options window
|
||||
- Minor tweaks to frontend code
|
||||
- Default to CD speed range
|
||||
- Skip trying to set speeds if no drives
|
||||
- Fix tests
|
||||
- Make media type a named parameter for CLI
|
||||
- Reduction in media type use for dumping
|
||||
- Update BinaryObjectScanner to 3.4.3
|
||||
|
||||
### 3.4.0 (2025-09-25)
|
||||
|
||||
- Remove media type from Check Dump UI
|
||||
|
||||
269
MPF.CLI/Features/BaseFeature.cs
Normal file
269
MPF.CLI/Features/BaseFeature.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
#if NET40
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using MPF.Frontend;
|
||||
using MPF.Frontend.Tools;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
using LogCompression = MPF.Processors.LogCompression;
|
||||
|
||||
namespace MPF.CLI.Features
|
||||
{
|
||||
internal abstract class BaseFeature : SabreTools.CommandLine.Feature
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// User-defined options
|
||||
/// </summary>
|
||||
public Options Options { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Currently-selected system
|
||||
/// </summary>
|
||||
public RedumpSystem? System { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Media type to dump
|
||||
/// </summary>
|
||||
/// <remarks>Required for DIC and if custom parameters not set</remarks>
|
||||
public MediaType? MediaType { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to the device to dump
|
||||
/// </summary>
|
||||
/// <remarks>Required if custom parameters are not set</remarks>
|
||||
public string? DevicePath { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to the mounted filesystem to check
|
||||
/// </summary>
|
||||
/// <remarks>Should only be used when the device path is not readable</remarks>
|
||||
public string? MountedPath { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to the output file
|
||||
/// </summary>
|
||||
/// <remarks>Required if custom parameters are not set</remarks>
|
||||
public string? FilePath { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Override drive speed
|
||||
/// </summary>
|
||||
public int? DriveSpeed { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Custom parameters for dumping
|
||||
/// </summary>
|
||||
public string? CustomParams { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
protected BaseFeature(string name, string[] flags, string description, string? detailed = null)
|
||||
: base(name, flags, description, detailed)
|
||||
{
|
||||
Options = new Options()
|
||||
{
|
||||
// Internal Program
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
|
||||
// Extra Dumping Options
|
||||
ScanForProtection = false,
|
||||
AddPlaceholders = true,
|
||||
PullAllInformation = false,
|
||||
AddFilenameSuffix = false,
|
||||
OutputSubmissionJSON = false,
|
||||
IncludeArtifacts = false,
|
||||
CompressLogFiles = false,
|
||||
LogCompression = LogCompression.DeflateMaximum,
|
||||
DeleteUnnecessaryFiles = false,
|
||||
CreateIRDAfterDumping = false,
|
||||
|
||||
// Protection Scanning Options
|
||||
ScanArchivesForProtection = true,
|
||||
IncludeDebugProtectionInformation = false,
|
||||
HideDriveLetters = false,
|
||||
|
||||
// Redump Login Information
|
||||
RetrieveMatchInformation = true,
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Execute()
|
||||
{
|
||||
// Validate the supplied credentials
|
||||
if (Options.RetrieveMatchInformation
|
||||
&& !string.IsNullOrEmpty(Options.RedumpUsername)
|
||||
&& !string.IsNullOrEmpty(Options.RedumpPassword))
|
||||
{
|
||||
bool? validated = RedumpClient.ValidateCredentials(Options.RedumpUsername!, Options.RedumpPassword!).GetAwaiter().GetResult();
|
||||
string message = validated switch
|
||||
{
|
||||
true => "Redump username and password accepted!",
|
||||
false => "Redump username and password denied!",
|
||||
null => "An error occurred validating your credentials!",
|
||||
};
|
||||
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
|
||||
// Validate the internal program
|
||||
switch (Options.InternalProgram)
|
||||
{
|
||||
case InternalProgram.Aaru:
|
||||
if (!File.Exists(Options.AaruPath))
|
||||
{
|
||||
Console.Error.WriteLine("A path needs to be supplied in config.json for Aaru, exiting...");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case InternalProgram.DiscImageCreator:
|
||||
if (!File.Exists(Options.DiscImageCreatorPath))
|
||||
{
|
||||
Console.Error.WriteLine("A path needs to be supplied in config.json for DIC, exiting...");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case InternalProgram.Redumper:
|
||||
if (!File.Exists(Options.RedumperPath))
|
||||
{
|
||||
Console.Error.WriteLine("A path needs to be supplied in config.json for Redumper, exiting...");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.Error.WriteLine($"{Options.InternalProgram} is not a supported dumping program, exiting...");
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure we have the values we need
|
||||
if (CustomParams == null && (DevicePath == null || FilePath == null))
|
||||
{
|
||||
Console.Error.WriteLine("Both a device path and file path need to be supplied, exiting...");
|
||||
return false;
|
||||
}
|
||||
if (Options.InternalProgram == InternalProgram.DiscImageCreator
|
||||
&& CustomParams == null
|
||||
&& (MediaType == null || MediaType == SabreTools.RedumpLib.Data.MediaType.NONE))
|
||||
{
|
||||
Console.Error.WriteLine("Media type is required for DiscImageCreator, exiting...");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Normalize the file path
|
||||
if (FilePath != null)
|
||||
FilePath = FrontendTool.NormalizeOutputPaths(FilePath, getFullPath: true);
|
||||
|
||||
// Get the speed from the options
|
||||
int speed = DriveSpeed ?? FrontendTool.GetDefaultSpeedForMediaType(MediaType, Options);
|
||||
|
||||
// Populate an environment
|
||||
var drive = Drive.Create(null, DevicePath ?? string.Empty);
|
||||
var env = new DumpEnvironment(Options,
|
||||
FilePath,
|
||||
drive,
|
||||
System,
|
||||
Options.InternalProgram);
|
||||
env.SetExecutionContext(MediaType, null);
|
||||
env.SetProcessor();
|
||||
|
||||
// Process the parameters
|
||||
string? paramStr = CustomParams ?? env.GetFullParameters(MediaType, speed);
|
||||
if (string.IsNullOrEmpty(paramStr))
|
||||
{
|
||||
Console.Error.WriteLine("No valid environment could be created, exiting...");
|
||||
return false;
|
||||
}
|
||||
|
||||
env.SetExecutionContext(MediaType, paramStr);
|
||||
|
||||
// Invoke the dumping program
|
||||
Console.WriteLine($"Invoking {Options.InternalProgram} using '{paramStr}'");
|
||||
var dumpResult = env.Run(MediaType).GetAwaiter().GetResult();
|
||||
Console.WriteLine(dumpResult.Message);
|
||||
if (!dumpResult)
|
||||
return false;
|
||||
|
||||
// If it was not a dumping command
|
||||
if (!env.IsDumpingCommand())
|
||||
{
|
||||
Console.Error.WriteLine();
|
||||
Console.WriteLine("Execution not recognized as dumping command, skipping processing...");
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we have a mounted path, replace the environment
|
||||
if (MountedPath != null && Directory.Exists(MountedPath))
|
||||
{
|
||||
drive = Drive.Create(null, MountedPath);
|
||||
env = new DumpEnvironment(Options,
|
||||
FilePath,
|
||||
drive,
|
||||
System,
|
||||
internalProgram: null);
|
||||
env.SetExecutionContext(MediaType, null);
|
||||
env.SetProcessor();
|
||||
}
|
||||
|
||||
// Finally, attempt to do the output dance
|
||||
var verifyResult = env.VerifyAndSaveDumpOutput()
|
||||
.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
Console.WriteLine(verifyResult.Message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display help for MPF.CLI
|
||||
/// </summary>
|
||||
/// <param name="error">Error string to prefix the help text with</param>
|
||||
public static void DisplayHelp()
|
||||
{
|
||||
Console.WriteLine("Usage:");
|
||||
Console.WriteLine("MPF.CLI <system> [options]");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Standalone Options:");
|
||||
Console.WriteLine("?, h, help Show this help text");
|
||||
Console.WriteLine("version Print the program version");
|
||||
Console.WriteLine("lc, listcodes List supported comment/content site codes");
|
||||
Console.WriteLine("lm, listmedia List supported media types");
|
||||
Console.WriteLine("ls, listsystems List supported system types");
|
||||
Console.WriteLine("lp, listprograms List supported dumping program outputs");
|
||||
Console.WriteLine("i, interactive Enable interactive mode");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("CLI Options:");
|
||||
Console.WriteLine("-u, --use <program> Override configured dumping program name");
|
||||
Console.WriteLine("-t, --mediatype <mediatype> Set media type for dumping (Required for DIC)");
|
||||
Console.WriteLine("-d, --device <devicepath> Physical drive path (Required if no custom parameters set)");
|
||||
Console.WriteLine("-m, --mounted <dirpath> Mounted filesystem path for additional checks");
|
||||
Console.WriteLine("-f, --file \"<filepath>\" Output file path (Required if no custom parameters set)");
|
||||
Console.WriteLine("-s, --speed <speed> Override default dumping speed");
|
||||
Console.WriteLine("-c, --custom \"<params>\" Custom parameters to use");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Dumping program paths and other settings can be found in the config.json file");
|
||||
Console.WriteLine("generated next to the program by default. Ensure that all settings are to user");
|
||||
Console.WriteLine("preference before running MPF.CLI.");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Custom dumping parameters, if used, will fully replace the default parameters.");
|
||||
Console.WriteLine("All dumping parameters need to be supplied if doing this.");
|
||||
Console.WriteLine("Otherwise, both a drive path and output file path are required.");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Mounted filesystem path is only recommended on OSes that require block");
|
||||
Console.WriteLine("device dumping, usually Linux and macOS.");
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
190
MPF.CLI/Features/InteractiveFeature.cs
Normal file
190
MPF.CLI/Features/InteractiveFeature.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using MPF.Frontend;
|
||||
using MPF.Frontend.Tools;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.CLI.Features
|
||||
{
|
||||
internal sealed class InteractiveFeature : BaseFeature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "interactive";
|
||||
|
||||
private static readonly string[] _flags = ["i", "interactive"];
|
||||
|
||||
private const string _description = "Enable interactive mode";
|
||||
|
||||
#endregion
|
||||
|
||||
public InteractiveFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool ProcessArgs(string[] args, int index)
|
||||
{
|
||||
// Cache all args as inputs
|
||||
for (int i = 1; i < args.Length; i++)
|
||||
{
|
||||
Inputs.Add(args[i]);
|
||||
}
|
||||
|
||||
// Read the options from config, if possible
|
||||
Options = OptionsLoader.LoadFromConfig();
|
||||
|
||||
// Create return values
|
||||
MediaType = SabreTools.RedumpLib.Data.MediaType.NONE;
|
||||
FilePath = Path.Combine(Options.DefaultOutputPath ?? "ISO", "track.bin");
|
||||
System = Options.DefaultSystem;
|
||||
|
||||
// Create state values
|
||||
string? result = string.Empty;
|
||||
|
||||
root:
|
||||
Console.Clear();
|
||||
Console.WriteLine("MPF.CLI Interactive Mode - Main Menu");
|
||||
Console.WriteLine("-------------------------");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"1) Set system (Currently '{System}')");
|
||||
Console.WriteLine($"2) Set dumping program (Currently '{Options.InternalProgram}')");
|
||||
Console.WriteLine($"3) Set media type (Currently '{MediaType}')");
|
||||
Console.WriteLine($"4) Set device path (Currently '{DevicePath}')");
|
||||
Console.WriteLine($"5) Set mounted path (Currently '{MountedPath}')");
|
||||
Console.WriteLine($"6) Set file path (Currently '{FilePath}')");
|
||||
Console.WriteLine($"7) Set override speed (Currently '{DriveSpeed}')");
|
||||
Console.WriteLine($"8) Set custom parameters (Currently '{CustomParams}')");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"Q) Exit the program");
|
||||
Console.WriteLine($"X) Start dumping");
|
||||
Console.Write("> ");
|
||||
|
||||
result = Console.ReadLine();
|
||||
switch (result)
|
||||
{
|
||||
case "1":
|
||||
goto system;
|
||||
case "2":
|
||||
goto dumpingProgram;
|
||||
case "3":
|
||||
goto mediaType;
|
||||
case "4":
|
||||
goto devicePath;
|
||||
case "5":
|
||||
goto mountedPath;
|
||||
case "6":
|
||||
goto filePath;
|
||||
case "7":
|
||||
goto overrideSpeed;
|
||||
case "8":
|
||||
goto customParams;
|
||||
|
||||
case "q":
|
||||
case "Q":
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
case "x":
|
||||
case "X":
|
||||
Console.Clear();
|
||||
goto exit;
|
||||
case "z":
|
||||
case "Z":
|
||||
Console.WriteLine("It is pitch black. You are likely to be eaten by a grue.");
|
||||
Console.Write("> ");
|
||||
Console.ReadLine();
|
||||
goto root;
|
||||
default:
|
||||
Console.WriteLine($"Invalid selection: {result}");
|
||||
Console.ReadLine();
|
||||
goto root;
|
||||
}
|
||||
|
||||
system:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("For possible inputs, use the List Systems commandline option");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the system and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
System = result.ToRedumpSystem();
|
||||
goto root;
|
||||
|
||||
dumpingProgram:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Options:");
|
||||
Console.WriteLine($"{InternalProgram.Redumper.ToString().ToLowerInvariant().PadRight(15)} => {InternalProgram.Redumper.LongName()}");
|
||||
Console.WriteLine($"{InternalProgram.DiscImageCreator.ToString().ToLowerInvariant().PadRight(15)} => {InternalProgram.DiscImageCreator.LongName()}");
|
||||
Console.WriteLine($"{InternalProgram.Aaru.ToString().ToLowerInvariant().PadRight(15)} => {InternalProgram.Aaru.LongName()}");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the dumping program and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
Options.InternalProgram = result.ToInternalProgram();
|
||||
goto root;
|
||||
|
||||
mediaType:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("For possible inputs, use the List Media commandline option");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the media type and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
MediaType = OptionsLoader.ToMediaType(result);
|
||||
goto root;
|
||||
|
||||
devicePath:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the device path and press Enter:");
|
||||
Console.Write("> ");
|
||||
DevicePath = Console.ReadLine();
|
||||
goto root;
|
||||
|
||||
mountedPath:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the mounted path and press Enter:");
|
||||
Console.Write("> ");
|
||||
MountedPath = Console.ReadLine();
|
||||
goto root;
|
||||
|
||||
filePath:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the file path and press Enter:");
|
||||
Console.Write("> ");
|
||||
|
||||
result = Console.ReadLine();
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
result = Path.GetFullPath(result!);
|
||||
|
||||
FilePath = result;
|
||||
goto root;
|
||||
|
||||
overrideSpeed:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the override speed and press Enter:");
|
||||
Console.Write("> ");
|
||||
|
||||
result = Console.ReadLine();
|
||||
if (!int.TryParse(result, out int speed))
|
||||
speed = -1;
|
||||
|
||||
DriveSpeed = speed;
|
||||
goto root;
|
||||
|
||||
customParams:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the custom parameters and press Enter:");
|
||||
Console.Write("> ");
|
||||
CustomParams = Console.ReadLine();
|
||||
goto root;
|
||||
|
||||
exit:
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
}
|
||||
}
|
||||
115
MPF.CLI/Features/MainFeature.cs
Normal file
115
MPF.CLI/Features/MainFeature.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
|
||||
using MPF.Frontend;
|
||||
using MPF.Frontend.Tools;
|
||||
using SabreTools.CommandLine.Inputs;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.CLI.Features
|
||||
{
|
||||
internal sealed class MainFeature : BaseFeature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "main";
|
||||
|
||||
/// <remarks>Flags are unused</remarks>
|
||||
private static readonly string[] _flags = [];
|
||||
|
||||
/// <remarks>Description is unused</remarks>
|
||||
private const string _description = "";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Inputs
|
||||
|
||||
private const string _customName = "custom";
|
||||
internal readonly StringInput CustomInput = new(_customName, ["-c", "--custom"], "Custom parameters to use");
|
||||
|
||||
private const string _deviceName = "device";
|
||||
internal readonly StringInput DeviceInput = new(_deviceName, ["-d", "--device"], "Physical drive path (Required if no custom parameters set)");
|
||||
|
||||
private const string _fileName = "file";
|
||||
internal readonly StringInput FileInput = new(_fileName, ["-f", "--file"], "Output file path (Required if no custom parameters set)");
|
||||
|
||||
private const string _mediaTypeName = "media-type";
|
||||
internal readonly StringInput MediaTypeInput = new(_mediaTypeName, ["-t", "--mediatype"], "Set media type for dumping (Required for DIC)");
|
||||
|
||||
private const string _mountedName = "mounted";
|
||||
internal readonly StringInput MountedInput = new(_mountedName, ["-m", "--mounted"], "Mounted filesystem path for additional checks");
|
||||
|
||||
private const string _speedName = "speed";
|
||||
internal readonly Int32Input SpeedInput = new(_speedName, ["-s", "--speed"], "Override default dumping speed");
|
||||
|
||||
private const string _useName = "use";
|
||||
internal readonly StringInput UseInput = new(_useName, ["-u", "--use"], "Override configured dumping program name");
|
||||
|
||||
#endregion
|
||||
|
||||
public MainFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
Add(UseInput);
|
||||
Add(MediaTypeInput);
|
||||
Add(DeviceInput);
|
||||
Add(MountedInput);
|
||||
Add(FileInput);
|
||||
Add(SpeedInput);
|
||||
Add(CustomInput);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool ProcessArgs(string[] args, int index)
|
||||
{
|
||||
// If we have no arguments, just return
|
||||
if (args == null || args.Length == 0)
|
||||
return true;
|
||||
|
||||
// Read the options from config, if possible
|
||||
Options = OptionsLoader.LoadFromConfig();
|
||||
|
||||
// The first argument is the system type
|
||||
System = args[0].Trim('"').ToRedumpSystem();
|
||||
|
||||
// Loop through the arguments and parse out values
|
||||
for (index = 1; index < args.Length; index++)
|
||||
{
|
||||
// Use specific program
|
||||
if (UseInput.ProcessInput(args, ref index))
|
||||
Options.InternalProgram = UseInput.Value.ToInternalProgram();
|
||||
|
||||
// Set a media type
|
||||
else if (MediaTypeInput.ProcessInput(args, ref index))
|
||||
MediaType = OptionsLoader.ToMediaType(MediaTypeInput.Value?.Trim('"'));
|
||||
|
||||
// Use a device path
|
||||
else if (DeviceInput.ProcessInput(args, ref index))
|
||||
DevicePath = DeviceInput.Value;
|
||||
|
||||
// Use a mounted path for physical checks
|
||||
else if (MountedInput.ProcessInput(args, ref index))
|
||||
MountedPath = MountedInput.Value;
|
||||
|
||||
// Use a file path
|
||||
else if (FileInput.ProcessInput(args, ref index))
|
||||
FilePath = FileInput.Value;
|
||||
|
||||
// Set an override speed
|
||||
else if (SpeedInput.ProcessInput(args, ref index))
|
||||
DriveSpeed = SpeedInput.Value;
|
||||
|
||||
// Use a custom parameters
|
||||
else if (CustomInput.ProcessInput(args, ref index))
|
||||
CustomParams = CustomInput.Value;
|
||||
|
||||
// Default, add to inputs
|
||||
else
|
||||
Inputs.Add(args[index]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.4.0</VersionPrefix>
|
||||
<VersionPrefix>3.5.0</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Title>MPF CLI</Title>
|
||||
@@ -43,7 +43,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
|
||||
<PackageReference Include="SabreTools.CommandLine" Version="[1.3.2]" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="[1.7.4]" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,12 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
#if NET40
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using MPF.CLI.Features;
|
||||
using MPF.Frontend;
|
||||
using MPF.Frontend.Features;
|
||||
using MPF.Frontend.Tools;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
using SabreTools.CommandLine;
|
||||
using SabreTools.CommandLine.Features;
|
||||
|
||||
namespace MPF.CLI
|
||||
{
|
||||
@@ -29,492 +31,117 @@ namespace MPF.CLI
|
||||
OptionsLoader.SaveToConfig(options);
|
||||
|
||||
// Display non-error message
|
||||
DisplayHelp("First-run detected! Please fill out config.json and run again.");
|
||||
Console.WriteLine("First-run detected! Please fill out config.json and run again.");
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Try processing the standalone arguments
|
||||
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
|
||||
if (standaloneProcessed != false)
|
||||
{
|
||||
if (standaloneProcessed == null)
|
||||
DisplayHelp();
|
||||
return;
|
||||
}
|
||||
// Create the command set
|
||||
var mainFeature = new MainFeature();
|
||||
var commandSet = CreateCommands(mainFeature);
|
||||
|
||||
// Setup common outputs
|
||||
CommandOptions opts;
|
||||
MediaType mediaType;
|
||||
RedumpSystem? knownSystem;
|
||||
|
||||
// Use interactive mode
|
||||
if (args.Length > 0 && (args[0] == "-i" || args[0] == "--interactive"))
|
||||
{
|
||||
opts = InteractiveMode(options, out mediaType, out knownSystem);
|
||||
}
|
||||
|
||||
// Use normal commandline parameters
|
||||
else
|
||||
{
|
||||
// Try processing the common arguments
|
||||
bool success = OptionsLoader.ProcessCommonArguments(args, out mediaType, out knownSystem, out var error);
|
||||
if (!success)
|
||||
{
|
||||
DisplayHelp(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate the supplied credentials
|
||||
if (options.RetrieveMatchInformation
|
||||
&& !string.IsNullOrEmpty(options.RedumpUsername)
|
||||
&& !string.IsNullOrEmpty(options.RedumpPassword))
|
||||
{
|
||||
bool? validated = RedumpClient.ValidateCredentials(options.RedumpUsername!, options.RedumpPassword!).GetAwaiter().GetResult();
|
||||
string message = validated switch
|
||||
{
|
||||
true => "Redump username and password accepted!",
|
||||
false => "Redump username and password denied!",
|
||||
null => "An error occurred validating your credentials!",
|
||||
};
|
||||
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
|
||||
// Process any custom parameters
|
||||
int startIndex = 2;
|
||||
opts = LoadFromArguments(args, options, ref startIndex);
|
||||
}
|
||||
|
||||
// Validate the internal program
|
||||
switch (options.InternalProgram)
|
||||
{
|
||||
case InternalProgram.Aaru:
|
||||
if (!File.Exists(options.AaruPath))
|
||||
{
|
||||
DisplayHelp("A path needs to be supplied in config.json for Aaru, exiting...");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case InternalProgram.DiscImageCreator:
|
||||
if (!File.Exists(options.DiscImageCreatorPath))
|
||||
{
|
||||
DisplayHelp("A path needs to be supplied in config.json for DIC, exiting...");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case InternalProgram.Redumper:
|
||||
if (!File.Exists(options.RedumperPath))
|
||||
{
|
||||
DisplayHelp("A path needs to be supplied in config.json for Redumper, exiting...");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
DisplayHelp($"{options.InternalProgram} is not a supported dumping program, exiting...");
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure we have the values we need
|
||||
if (opts.CustomParams == null && (opts.DevicePath == null || opts.FilePath == null))
|
||||
{
|
||||
DisplayHelp("Both a device path and file path need to be supplied, exiting...");
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize the file path
|
||||
if (opts.FilePath != null)
|
||||
opts.FilePath = FrontendTool.NormalizeOutputPaths(opts.FilePath, getFullPath: true);
|
||||
|
||||
// Get the speed from the options
|
||||
int speed = opts.DriveSpeed ?? FrontendTool.GetDefaultSpeedForMediaType(mediaType, options);
|
||||
|
||||
// Populate an environment
|
||||
var drive = Drive.Create(null, opts.DevicePath ?? string.Empty);
|
||||
var env = new DumpEnvironment(options,
|
||||
opts.FilePath,
|
||||
drive,
|
||||
knownSystem,
|
||||
options.InternalProgram);
|
||||
env.SetExecutionContext(mediaType, null);
|
||||
env.SetProcessor();
|
||||
|
||||
// Process the parameters
|
||||
string? paramStr = opts.CustomParams ?? env.GetFullParameters(mediaType, speed);
|
||||
if (string.IsNullOrEmpty(paramStr))
|
||||
{
|
||||
DisplayHelp("No valid environment could be created, exiting...");
|
||||
return;
|
||||
}
|
||||
|
||||
env.SetExecutionContext(mediaType, paramStr);
|
||||
|
||||
// Invoke the dumping program
|
||||
Console.WriteLine($"Invoking {options.InternalProgram} using '{paramStr}'");
|
||||
var dumpResult = env.Run(mediaType).GetAwaiter().GetResult();
|
||||
Console.WriteLine(dumpResult.Message);
|
||||
if (!dumpResult)
|
||||
return;
|
||||
|
||||
// If it was not a dumping command
|
||||
if (!env.IsDumpingCommand())
|
||||
{
|
||||
Console.WriteLine("Execution not recognized as dumping command, skipping processing...");
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a mounted path, replace the environment
|
||||
if (opts.MountedPath != null && Directory.Exists(opts.MountedPath))
|
||||
{
|
||||
drive = Drive.Create(null, opts.MountedPath);
|
||||
env = new DumpEnvironment(options,
|
||||
opts.FilePath,
|
||||
drive,
|
||||
knownSystem,
|
||||
internalProgram: null);
|
||||
env.SetExecutionContext(mediaType, null);
|
||||
env.SetProcessor();
|
||||
}
|
||||
|
||||
// Finally, attempt to do the output dance
|
||||
var verifyResult = env.VerifyAndSaveDumpOutput()
|
||||
.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
Console.WriteLine(verifyResult.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display help for MPF.CLI
|
||||
/// </summary>
|
||||
/// <param name="error">Error string to prefix the help text with</param>
|
||||
private static void DisplayHelp(string? error = null)
|
||||
{
|
||||
if (error != null)
|
||||
Console.WriteLine(error);
|
||||
|
||||
Console.WriteLine("Usage:");
|
||||
Console.WriteLine("MPF.CLI <mediatype> <system> [options]");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Standalone Options:");
|
||||
Console.WriteLine("-h, -?, --help Show this help text");
|
||||
Console.WriteLine("--version Print the program version");
|
||||
Console.WriteLine("-lc, --listcodes List supported comment/content site codes");
|
||||
Console.WriteLine("-lm, --listmedia List supported media types");
|
||||
Console.WriteLine("-ls, --listsystems List supported system types");
|
||||
Console.WriteLine("-lp, --listprograms List supported dumping program outputs");
|
||||
Console.WriteLine("-i, --interactive Enable interactive mode");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("CLI Options:");
|
||||
Console.WriteLine("-u, --use <program> Override configured dumping program name");
|
||||
Console.WriteLine("-d, --device <devicepath> Physical drive path (Required if no custom parameters set)");
|
||||
Console.WriteLine("-m, --mounted <dirpath> Mounted filesystem path for additional checks");
|
||||
Console.WriteLine("-f, --file \"<filepath>\" Output file path (Required if no custom parameters set)");
|
||||
Console.WriteLine("-s, --speed <speed> Override default dumping speed");
|
||||
Console.WriteLine("-c, --custom \"<params>\" Custom parameters to use");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Dumping program paths and other settings can be found in the config.json file");
|
||||
Console.WriteLine("generated next to the program by default. Ensure that all settings are to user");
|
||||
Console.WriteLine("preference before running MPF.CLI.");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Custom dumping parameters, if used, will fully replace the default parameters.");
|
||||
Console.WriteLine("All dumping parameters need to be supplied if doing this.");
|
||||
Console.WriteLine("Otherwise, both a drive path and output file path are required.");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Mounted filesystem path is only recommended on OSes that require block");
|
||||
Console.WriteLine("device dumping, usually Linux and macOS.");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable interactive mode for entering information
|
||||
/// </summary>
|
||||
private static CommandOptions InteractiveMode(Options options, out MediaType mediaType, out RedumpSystem? system)
|
||||
{
|
||||
// Create return values
|
||||
var opts = new CommandOptions
|
||||
{
|
||||
FilePath = Path.Combine(options.DefaultOutputPath ?? "ISO", "track.bin"),
|
||||
};
|
||||
mediaType = MediaType.NONE;
|
||||
system = options.DefaultSystem;
|
||||
|
||||
// Create state values
|
||||
string? result = string.Empty;
|
||||
|
||||
root:
|
||||
Console.Clear();
|
||||
Console.WriteLine("MPF.CLI Interactive Mode - Main Menu");
|
||||
Console.WriteLine("-------------------------");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"1) Set media type (Currently '{mediaType}')");
|
||||
Console.WriteLine($"2) Set system (Currently '{system}')");
|
||||
Console.WriteLine($"3) Set dumping program (Currently '{options.InternalProgram}')");
|
||||
Console.WriteLine($"4) Set device path (Currently '{opts.DevicePath}')");
|
||||
Console.WriteLine($"5) Set mounted path (Currently '{opts.MountedPath}')");
|
||||
Console.WriteLine($"6) Set file path (Currently '{opts.FilePath}')");
|
||||
Console.WriteLine($"7) Set override speed (Currently '{opts.DriveSpeed}')");
|
||||
Console.WriteLine($"8) Set custom parameters (Currently '{opts.CustomParams}')");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"Q) Exit the program");
|
||||
Console.WriteLine($"X) Start dumping");
|
||||
Console.Write("> ");
|
||||
|
||||
result = Console.ReadLine();
|
||||
switch (result)
|
||||
{
|
||||
case "1":
|
||||
goto mediaType;
|
||||
case "2":
|
||||
goto system;
|
||||
case "3":
|
||||
goto dumpingProgram;
|
||||
case "4":
|
||||
goto devicePath;
|
||||
case "5":
|
||||
goto mountedPath;
|
||||
case "6":
|
||||
goto filePath;
|
||||
case "7":
|
||||
goto overrideSpeed;
|
||||
case "8":
|
||||
goto customParams;
|
||||
|
||||
case "q":
|
||||
case "Q":
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
case "x":
|
||||
case "X":
|
||||
Console.Clear();
|
||||
goto exit;
|
||||
case "z":
|
||||
case "Z":
|
||||
Console.WriteLine("It is pitch black. You are likely to be eaten by a grue.");
|
||||
Console.Write("> ");
|
||||
Console.ReadLine();
|
||||
goto root;
|
||||
default:
|
||||
Console.WriteLine($"Invalid selection: {result}");
|
||||
Console.ReadLine();
|
||||
goto root;
|
||||
}
|
||||
|
||||
mediaType:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the media type and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
mediaType = OptionsLoader.ToMediaType(result);
|
||||
goto root;
|
||||
|
||||
system:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the system and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
system = Extensions.ToRedumpSystem(result);
|
||||
goto root;
|
||||
|
||||
dumpingProgram:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the dumping program and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
options.InternalProgram = result.ToInternalProgram();
|
||||
goto root;
|
||||
|
||||
devicePath:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the device path and press Enter:");
|
||||
Console.Write("> ");
|
||||
opts.DevicePath = Console.ReadLine();
|
||||
goto root;
|
||||
|
||||
mountedPath:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the mounted path and press Enter:");
|
||||
Console.Write("> ");
|
||||
opts.MountedPath = Console.ReadLine();
|
||||
goto root;
|
||||
|
||||
filePath:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the file path and press Enter:");
|
||||
Console.Write("> ");
|
||||
|
||||
result = Console.ReadLine();
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
result = Path.GetFullPath(result!);
|
||||
|
||||
opts.FilePath = result;
|
||||
goto root;
|
||||
|
||||
overrideSpeed:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the override speed and press Enter:");
|
||||
Console.Write("> ");
|
||||
|
||||
result = Console.ReadLine();
|
||||
if (!int.TryParse(result, out int speed))
|
||||
speed = -1;
|
||||
|
||||
opts.DriveSpeed = speed;
|
||||
goto root;
|
||||
|
||||
customParams:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the custom parameters and press Enter:");
|
||||
Console.Write("> ");
|
||||
opts.CustomParams = Console.ReadLine();
|
||||
goto root;
|
||||
|
||||
exit:
|
||||
return opts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the current set of options from application arguments
|
||||
/// </summary>
|
||||
private static CommandOptions LoadFromArguments(string[] args, Options options, ref int startIndex)
|
||||
{
|
||||
// Create return values
|
||||
var opts = new CommandOptions();
|
||||
|
||||
// If we have no arguments, just return
|
||||
// If we have no args, show the help and quit
|
||||
if (args == null || args.Length == 0)
|
||||
{
|
||||
startIndex = 0;
|
||||
return opts;
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have an invalid start index, just return
|
||||
if (startIndex < 0 || startIndex >= args.Length)
|
||||
return opts;
|
||||
// Get the first argument as a feature flag
|
||||
string featureName = args[0];
|
||||
|
||||
// Loop through the arguments and parse out values
|
||||
for (; startIndex < args.Length; startIndex++)
|
||||
// Try processing the standalone arguments
|
||||
var topLevel = commandSet.GetTopLevel(featureName);
|
||||
switch (topLevel)
|
||||
{
|
||||
// Use specific program
|
||||
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
{
|
||||
string internalProgram = args[startIndex].Split('=')[1];
|
||||
options.InternalProgram = internalProgram.ToInternalProgram();
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
string internalProgram = args[startIndex + 1];
|
||||
options.InternalProgram = internalProgram.ToInternalProgram();
|
||||
startIndex++;
|
||||
}
|
||||
// Standalone Options
|
||||
case Help: BaseFeature.DisplayHelp(); return;
|
||||
case VersionFeature version: version.Execute(); return;
|
||||
case ListCodesFeature lc: lc.Execute(); return;
|
||||
case ListMediaTypesFeature lm: lm.Execute(); return;
|
||||
case ListProgramsFeature lp: lp.Execute(); return;
|
||||
case ListSystemsFeature ls: ls.Execute(); return;
|
||||
|
||||
// Use a device path
|
||||
else if (args[startIndex].StartsWith("-d=") || args[startIndex].StartsWith("--device="))
|
||||
{
|
||||
opts.DevicePath = args[startIndex].Split('=')[1].Trim('"');
|
||||
}
|
||||
else if (args[startIndex] == "-d" || args[startIndex] == "--device")
|
||||
{
|
||||
opts.DevicePath = args[startIndex + 1].Trim('"');
|
||||
startIndex++;
|
||||
}
|
||||
// Interactive Mode
|
||||
case InteractiveFeature interactive:
|
||||
if (!interactive.ProcessArgs(args, 0))
|
||||
{
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
if (!interactive.Execute())
|
||||
{
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Use a mounted path for physical checks
|
||||
else if (args[startIndex].StartsWith("-m=") || args[startIndex].StartsWith("--mounted="))
|
||||
{
|
||||
opts.MountedPath = args[startIndex].Split('=')[1];
|
||||
}
|
||||
else if (args[startIndex] == "-m" || args[startIndex] == "--mounted")
|
||||
{
|
||||
opts.MountedPath = args[startIndex + 1];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Use a file path
|
||||
else if (args[startIndex].StartsWith("-f=") || args[startIndex].StartsWith("--file="))
|
||||
{
|
||||
opts.FilePath = args[startIndex].Split('=')[1].Trim('"');
|
||||
}
|
||||
else if (args[startIndex] == "-f" || args[startIndex] == "--file")
|
||||
{
|
||||
opts.FilePath = args[startIndex + 1].Trim('"');
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Set an override speed
|
||||
else if (args[startIndex].StartsWith("-s=") || args[startIndex].StartsWith("--speed="))
|
||||
{
|
||||
if (!int.TryParse(args[startIndex].Split('=')[1].Trim('"'), out int speed))
|
||||
speed = -1;
|
||||
|
||||
opts.DriveSpeed = speed;
|
||||
}
|
||||
else if (args[startIndex] == "-s" || args[startIndex] == "--speed")
|
||||
{
|
||||
if (!int.TryParse(args[startIndex + 1].Trim('"'), out int speed))
|
||||
speed = -1;
|
||||
|
||||
opts.DriveSpeed = speed;
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Use a custom parameters
|
||||
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--custom="))
|
||||
{
|
||||
opts.CustomParams = args[startIndex].Split('=')[1].Trim('"');
|
||||
}
|
||||
else if (args[startIndex] == "-c" || args[startIndex] == "--custom")
|
||||
{
|
||||
opts.CustomParams = args[startIndex + 1].Trim('"');
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return opts;
|
||||
// Default Behavior
|
||||
default:
|
||||
if (!mainFeature.ProcessArgs(args, 0))
|
||||
{
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
if (!mainFeature.Execute())
|
||||
{
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents commandline options
|
||||
/// Create the command set for the program
|
||||
/// </summary>
|
||||
private class CommandOptions
|
||||
private static CommandSet CreateCommands(MainFeature mainFeature)
|
||||
{
|
||||
/// <summary>
|
||||
/// Path to the device to dump
|
||||
/// </summary>
|
||||
/// <remarks>Required if custom parameters are not set</remarks>
|
||||
public string? DevicePath { get; set; } = null;
|
||||
List<string> header = [
|
||||
"MPF.CLI [standalone|system] [options] <path> ...",
|
||||
string.Empty,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Path to the mounted filesystem to check
|
||||
/// </summary>
|
||||
/// <remarks>Should only be used when the device path is not readable</remarks>
|
||||
public string? MountedPath { get; set; } = null;
|
||||
List<string> footer = [
|
||||
string.Empty,
|
||||
"Dumping program paths and other settings can be found in the config.json file",
|
||||
"generated next to the program by default. Ensure that all settings are to user",
|
||||
"preference before running MPF.CLI.",
|
||||
string.Empty,
|
||||
|
||||
/// <summary>
|
||||
/// Path to the output file
|
||||
/// </summary>
|
||||
/// <remarks>Required if custom parameters are not set</remarks>
|
||||
public string? FilePath { get; set; } = null;
|
||||
"Custom dumping parameters, if used, will fully replace the default parameters.",
|
||||
"All dumping parameters need to be supplied if doing this.",
|
||||
"Otherwise, both a drive path and output file path are required.",
|
||||
string.Empty,
|
||||
|
||||
/// <summary>
|
||||
/// Override drive speed
|
||||
/// </summary>
|
||||
public int? DriveSpeed { get; set; } = null;
|
||||
"Mounted filesystem path is only recommended on OSes that require block",
|
||||
"device dumping, usually Linux and macOS.",
|
||||
string.Empty,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Custom parameters for dumping
|
||||
/// </summary>
|
||||
public string? CustomParams { get; set; } = null;
|
||||
var commandSet = new CommandSet(header, footer);
|
||||
|
||||
// Standalone Options
|
||||
commandSet.Add(new Help());
|
||||
commandSet.Add(new VersionFeature());
|
||||
commandSet.Add(new ListCodesFeature());
|
||||
commandSet.Add(new ListMediaTypesFeature());
|
||||
commandSet.Add(new ListSystemsFeature());
|
||||
commandSet.Add(new ListProgramsFeature());
|
||||
commandSet.Add(new InteractiveFeature());
|
||||
|
||||
// CLI Options
|
||||
commandSet.Add(mainFeature.UseInput);
|
||||
commandSet.Add(mainFeature.MediaTypeInput);
|
||||
commandSet.Add(mainFeature.DeviceInput);
|
||||
commandSet.Add(mainFeature.MountedInput);
|
||||
commandSet.Add(mainFeature.FileInput);
|
||||
commandSet.Add(mainFeature.SpeedInput);
|
||||
commandSet.Add(mainFeature.CustomInput);
|
||||
|
||||
return commandSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
177
MPF.Check/Features/BaseFeature.cs
Normal file
177
MPF.Check/Features/BaseFeature.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
#if NET40
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using MPF.Frontend;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
using LogCompression = MPF.Processors.LogCompression;
|
||||
|
||||
namespace MPF.Check.Features
|
||||
{
|
||||
internal abstract class BaseFeature : SabreTools.CommandLine.Feature
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// User-defined options
|
||||
/// </summary>
|
||||
public Options Options { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Currently-selected system
|
||||
/// </summary>
|
||||
public RedumpSystem? System { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Seed submission info from an input file
|
||||
/// </summary>
|
||||
public SubmissionInfo? Seed { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to the device to scan
|
||||
/// </summary>
|
||||
public string? DevicePath { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
protected BaseFeature(string name, string[] flags, string description, string? detailed = null)
|
||||
: base(name, flags, description, detailed)
|
||||
{
|
||||
Options = new Options()
|
||||
{
|
||||
// Internal Program
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
|
||||
// Extra Dumping Options
|
||||
ScanForProtection = false,
|
||||
AddPlaceholders = true,
|
||||
PullAllInformation = false,
|
||||
AddFilenameSuffix = false,
|
||||
OutputSubmissionJSON = false,
|
||||
IncludeArtifacts = false,
|
||||
CompressLogFiles = false,
|
||||
LogCompression = LogCompression.DeflateMaximum,
|
||||
DeleteUnnecessaryFiles = false,
|
||||
CreateIRDAfterDumping = false,
|
||||
|
||||
// Protection Scanning Options
|
||||
ScanArchivesForProtection = true,
|
||||
IncludeDebugProtectionInformation = false,
|
||||
HideDriveLetters = false,
|
||||
|
||||
// Redump Login Information
|
||||
RetrieveMatchInformation = true,
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Execute()
|
||||
{
|
||||
if (Options.InternalProgram == InternalProgram.NONE)
|
||||
{
|
||||
Console.Error.WriteLine("A program name needs to be provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate the supplied credentials
|
||||
if (Options.RetrieveMatchInformation
|
||||
&& !string.IsNullOrEmpty(Options.RedumpUsername)
|
||||
&& !string.IsNullOrEmpty(Options.RedumpPassword))
|
||||
{
|
||||
bool? validated = RedumpClient.ValidateCredentials(Options.RedumpUsername!, Options.RedumpPassword!).GetAwaiter().GetResult();
|
||||
string message = validated switch
|
||||
{
|
||||
true => "Redump username and password accepted!",
|
||||
false => "Redump username and password denied!",
|
||||
null => "An error occurred validating your credentials!",
|
||||
};
|
||||
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
|
||||
// Loop through all the rest of the args
|
||||
for (int i = 0; i < Inputs.Count; i++)
|
||||
{
|
||||
// Check for a file
|
||||
if (!File.Exists(Inputs[i].Trim('"')))
|
||||
{
|
||||
Console.Error.WriteLine($"{Inputs[i].Trim('"')} does not exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the full file path
|
||||
string filepath = Path.GetFullPath(Inputs[i].Trim('"'));
|
||||
|
||||
// Now populate an environment
|
||||
Drive? drive = null;
|
||||
if (!string.IsNullOrEmpty(DevicePath))
|
||||
drive = Drive.Create(null, DevicePath!);
|
||||
|
||||
var env = new DumpEnvironment(Options,
|
||||
filepath,
|
||||
drive,
|
||||
System,
|
||||
internalProgram: null);
|
||||
env.SetProcessor();
|
||||
|
||||
// Finally, attempt to do the output dance
|
||||
var result = env.VerifyAndSaveDumpOutput(seedInfo: Seed)
|
||||
.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
Console.WriteLine(result.Message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display help for MPF.Check
|
||||
/// </summary>
|
||||
/// <param name="error">Error string to prefix the help text with</param>
|
||||
public static void DisplayHelp()
|
||||
{
|
||||
Console.WriteLine("Usage:");
|
||||
Console.WriteLine("MPF.Check <system> [options] </path/to/output.cue/iso> ...");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Standalone Options:");
|
||||
Console.WriteLine("?, h, help Show this help text");
|
||||
Console.WriteLine("version Print the program version");
|
||||
Console.WriteLine("lc, listcodes List supported comment/content site codes");
|
||||
Console.WriteLine("lm, listmedia List supported media types");
|
||||
Console.WriteLine("ls, listsystems List supported system types");
|
||||
Console.WriteLine("lp, listprograms List supported dumping program outputs");
|
||||
Console.WriteLine("i, interactive Enable interactive mode");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Check Options:");
|
||||
Console.WriteLine("-u, --use <program> Dumping program output type [REQUIRED]");
|
||||
Console.WriteLine(" --load-seed <path> Load a seed submission JSON for user information");
|
||||
Console.WriteLine(" --no-placeholders Disable placeholder values in submission info");
|
||||
Console.WriteLine(" --create-ird Create IRD from output files (PS3 only)");
|
||||
Console.WriteLine(" --no-retrieve Disable retrieving match information from Redump");
|
||||
Console.WriteLine("-c, --credentials <user> <pw> Redump username and password (incompatible with --no-retrieve) [WILL BE REMOVED]");
|
||||
Console.WriteLine("-U, --username <user> Redump username (incompatible with --no-retrieve)");
|
||||
Console.WriteLine("-P, --password <pw> Redump password (incompatible with --no-retrieve)");
|
||||
Console.WriteLine(" --pull-all Pull all information from Redump (requires --username and --password)");
|
||||
Console.WriteLine("-p, --path <drivepath> Physical drive path for additional checks");
|
||||
Console.WriteLine("-s, --scan Enable copy protection scan (requires --path)");
|
||||
Console.WriteLine(" --disable-archives Disable scanning archives (requires --scan)");
|
||||
Console.WriteLine(" --enable-debug Enable debug protection information (requires --scan)");
|
||||
Console.WriteLine(" --hide-drive-letters Hide drive letters from scan output (requires --scan)");
|
||||
Console.WriteLine("-x, --suffix Enable adding filename suffix");
|
||||
Console.WriteLine("-j, --json Enable submission JSON output");
|
||||
Console.WriteLine(" --include-artifacts Include artifacts in JSON (requires --json)");
|
||||
Console.WriteLine("-z, --zip Enable log file compression");
|
||||
Console.WriteLine(" --log-compression Set the log compression type (requires --zip)");
|
||||
Console.WriteLine("-d, --delete Enable unnecessary file deletion");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("WARNING: Check will overwrite both any existing submission information files as well");
|
||||
Console.WriteLine("as any log archives. Please make backups of those if you need to before running Check.");
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
288
MPF.Check/Features/InteractiveFeature.cs
Normal file
288
MPF.Check/Features/InteractiveFeature.cs
Normal file
@@ -0,0 +1,288 @@
|
||||
|
||||
using System;
|
||||
using MPF.Frontend;
|
||||
using MPF.Frontend.Tools;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using LogCompression = MPF.Processors.LogCompression;
|
||||
|
||||
namespace MPF.Check.Features
|
||||
{
|
||||
internal sealed class InteractiveFeature : BaseFeature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "interactive";
|
||||
|
||||
private static readonly string[] _flags = ["i", "interactive"];
|
||||
|
||||
private const string _description = "Enable interactive mode";
|
||||
|
||||
#endregion
|
||||
|
||||
public InteractiveFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool ProcessArgs(string[] args, int index)
|
||||
{
|
||||
// Cache all args as inputs
|
||||
for (int i = 1; i < args.Length; i++)
|
||||
{
|
||||
Inputs.Add(args[i]);
|
||||
}
|
||||
|
||||
// Read the options from config, if possible
|
||||
Options = OptionsLoader.LoadFromConfig();
|
||||
if (Options.FirstRun)
|
||||
{
|
||||
Options = new Options()
|
||||
{
|
||||
// Internal Program
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
|
||||
// Extra Dumping Options
|
||||
ScanForProtection = false,
|
||||
AddPlaceholders = true,
|
||||
PullAllInformation = false,
|
||||
AddFilenameSuffix = false,
|
||||
OutputSubmissionJSON = false,
|
||||
IncludeArtifacts = false,
|
||||
CompressLogFiles = false,
|
||||
LogCompression = LogCompression.DeflateMaximum,
|
||||
DeleteUnnecessaryFiles = false,
|
||||
CreateIRDAfterDumping = false,
|
||||
|
||||
// Protection Scanning Options
|
||||
ScanArchivesForProtection = true,
|
||||
IncludeDebugProtectionInformation = false,
|
||||
HideDriveLetters = false,
|
||||
|
||||
// Redump Login Information
|
||||
RetrieveMatchInformation = true,
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
};
|
||||
}
|
||||
|
||||
// Create return values
|
||||
System = null;
|
||||
|
||||
// These values require multiple parts to be active
|
||||
bool scan = false,
|
||||
enableArchives = true,
|
||||
enableDebug = false,
|
||||
hideDriveLetters = false;
|
||||
|
||||
// Create state values
|
||||
string? result = string.Empty;
|
||||
|
||||
root:
|
||||
Console.Clear();
|
||||
Console.WriteLine("MPF.Check Interactive Mode - Main Menu");
|
||||
Console.WriteLine("-------------------------");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"1) Set system (Currently '{System}')");
|
||||
Console.WriteLine($"2) Set dumping program (Currently '{Options.InternalProgram}')");
|
||||
Console.WriteLine($"3) Set seed path (Currently '{Seed}')");
|
||||
Console.WriteLine($"4) Add placeholders (Currently '{Options.AddPlaceholders}')");
|
||||
Console.WriteLine($"5) Create IRD (Currently '{Options.CreateIRDAfterDumping}')");
|
||||
Console.WriteLine($"6) Attempt Redump matches (Currently '{Options.RetrieveMatchInformation}')");
|
||||
Console.WriteLine($"7) Redump credentials (Currently '{Options.RedumpUsername}')");
|
||||
Console.WriteLine($"8) Pull all information (Currently '{Options.PullAllInformation}')");
|
||||
Console.WriteLine($"9) Set device path (Currently '{DevicePath}')");
|
||||
Console.WriteLine($"A) Scan for protection (Currently '{scan}')");
|
||||
Console.WriteLine($"B) Scan archives for protection (Currently '{enableArchives}')");
|
||||
Console.WriteLine($"C) Debug protection scan output (Currently '{enableDebug}')");
|
||||
Console.WriteLine($"D) Hide drive letters in protection output (Currently '{hideDriveLetters}')");
|
||||
Console.WriteLine($"E) Hide filename suffix (Currently '{Options.AddFilenameSuffix}')");
|
||||
Console.WriteLine($"F) Output submission JSON (Currently '{Options.OutputSubmissionJSON}')");
|
||||
Console.WriteLine($"G) Include JSON artifacts (Currently '{Options.IncludeArtifacts}')");
|
||||
Console.WriteLine($"H) Compress logs (Currently '{Options.CompressLogFiles}')");
|
||||
Console.WriteLine($"I) Log compression (Currently '{Options.LogCompression.LongName()}')");
|
||||
Console.WriteLine($"J) Delete unnecessary files (Currently '{Options.DeleteUnnecessaryFiles}')");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"Q) Exit the program");
|
||||
Console.WriteLine($"X) Start checking");
|
||||
Console.Write("> ");
|
||||
|
||||
result = Console.ReadLine();
|
||||
switch (result)
|
||||
{
|
||||
case "1":
|
||||
goto system;
|
||||
case "2":
|
||||
goto dumpingProgram;
|
||||
case "3":
|
||||
goto seedPath;
|
||||
case "4":
|
||||
Options.AddPlaceholders = !Options.AddPlaceholders;
|
||||
goto root;
|
||||
case "5":
|
||||
Options.CreateIRDAfterDumping = !Options.CreateIRDAfterDumping;
|
||||
goto root;
|
||||
case "6":
|
||||
Options.RetrieveMatchInformation = !Options.RetrieveMatchInformation;
|
||||
goto root;
|
||||
case "7":
|
||||
goto redumpCredentials;
|
||||
case "8":
|
||||
Options.PullAllInformation = !Options.PullAllInformation;
|
||||
goto root;
|
||||
case "9":
|
||||
goto devicePath;
|
||||
case "a":
|
||||
case "A":
|
||||
scan = !scan;
|
||||
goto root;
|
||||
case "b":
|
||||
case "B":
|
||||
enableArchives = !enableArchives;
|
||||
goto root;
|
||||
case "c":
|
||||
case "C":
|
||||
enableDebug = !enableDebug;
|
||||
goto root;
|
||||
case "d":
|
||||
case "D":
|
||||
hideDriveLetters = !hideDriveLetters;
|
||||
goto root;
|
||||
case "e":
|
||||
case "E":
|
||||
Options.AddFilenameSuffix = !Options.AddFilenameSuffix;
|
||||
goto root;
|
||||
case "f":
|
||||
case "F":
|
||||
Options.OutputSubmissionJSON = !Options.OutputSubmissionJSON;
|
||||
goto root;
|
||||
case "g":
|
||||
case "G":
|
||||
Options.IncludeArtifacts = !Options.IncludeArtifacts;
|
||||
goto root;
|
||||
case "h":
|
||||
case "H":
|
||||
Options.CompressLogFiles = !Options.CompressLogFiles;
|
||||
goto root;
|
||||
case "i":
|
||||
case "I":
|
||||
goto logCompression;
|
||||
case "j":
|
||||
case "J":
|
||||
Options.DeleteUnnecessaryFiles = !Options.DeleteUnnecessaryFiles;
|
||||
goto root;
|
||||
|
||||
case "q":
|
||||
case "Q":
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
case "x":
|
||||
case "X":
|
||||
Console.Clear();
|
||||
goto exit;
|
||||
case "z":
|
||||
case "Z":
|
||||
Console.WriteLine("It is pitch black. You are likely to be eaten by a grue.");
|
||||
Console.Write("> ");
|
||||
Console.ReadLine();
|
||||
goto root;
|
||||
default:
|
||||
Console.WriteLine($"Invalid selection: {result}");
|
||||
Console.ReadLine();
|
||||
goto root;
|
||||
}
|
||||
|
||||
system:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("For possible inputs, use the List Systems commandline option");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the system and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
System = result.ToRedumpSystem();
|
||||
goto root;
|
||||
|
||||
dumpingProgram:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Options:");
|
||||
foreach (var program in (InternalProgram[])Enum.GetValues(typeof(InternalProgram)))
|
||||
{
|
||||
// Skip the placeholder values
|
||||
if (program == InternalProgram.NONE)
|
||||
continue;
|
||||
|
||||
Console.WriteLine($"{program.ToString().ToLowerInvariant().PadRight(15)} => {program.LongName()}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the dumping program and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
Options.InternalProgram = result.ToInternalProgram();
|
||||
goto root;
|
||||
|
||||
seedPath:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the seed path and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
Seed = Builder.CreateFromFile(result);
|
||||
goto root;
|
||||
|
||||
redumpCredentials:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Enter your Redump username and press Enter:");
|
||||
Console.Write("> ");
|
||||
Options.RedumpUsername = Console.ReadLine();
|
||||
|
||||
Console.WriteLine("Enter your Redump password (hidden) and press Enter:");
|
||||
Console.Write("> ");
|
||||
Options.RedumpPassword = string.Empty;
|
||||
while (true)
|
||||
{
|
||||
var key = Console.ReadKey(true);
|
||||
if (key.Key == ConsoleKey.Enter)
|
||||
break;
|
||||
|
||||
Options.RedumpPassword += key.KeyChar;
|
||||
}
|
||||
|
||||
goto root;
|
||||
|
||||
devicePath:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the device path and press Enter:");
|
||||
Console.Write("> ");
|
||||
DevicePath = Console.ReadLine();
|
||||
goto root;
|
||||
|
||||
logCompression:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Options:");
|
||||
foreach (var compressionType in (LogCompression[])Enum.GetValues(typeof(LogCompression)))
|
||||
{
|
||||
Console.WriteLine($"{compressionType.ToString().ToLowerInvariant().PadRight(15)} => {compressionType.LongName()}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the log compression type and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
Options.LogCompression = result.ToLogCompression();
|
||||
goto root;
|
||||
|
||||
exit:
|
||||
// Now deal with the complex options
|
||||
Options.ScanForProtection = scan && !string.IsNullOrEmpty(DevicePath);
|
||||
Options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(DevicePath);
|
||||
Options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(DevicePath);
|
||||
Options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(DevicePath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => Inputs.Count > 0;
|
||||
}
|
||||
}
|
||||
267
MPF.Check/Features/MainFeature.cs
Normal file
267
MPF.Check/Features/MainFeature.cs
Normal file
@@ -0,0 +1,267 @@
|
||||
using MPF.Frontend;
|
||||
using MPF.Frontend.Tools;
|
||||
using SabreTools.CommandLine.Inputs;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using LogCompression = MPF.Processors.LogCompression;
|
||||
|
||||
namespace MPF.Check.Features
|
||||
{
|
||||
internal sealed class MainFeature : BaseFeature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "main";
|
||||
|
||||
/// <remarks>Flags are unused</remarks>
|
||||
private static readonly string[] _flags = [];
|
||||
|
||||
/// <remarks>Description is unused</remarks>
|
||||
private const string _description = "";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Inputs
|
||||
|
||||
private const string _createIrdName = "create-ird";
|
||||
internal readonly FlagInput CreateIrdInput = new(_createIrdName, "--create-ird", "Create IRD from output files (PS3 only)");
|
||||
|
||||
private const string _deleteName = "delete";
|
||||
internal readonly FlagInput DeleteInput = new(_deleteName, ["-d", "--delete"], "Enable unnecessary file deletion");
|
||||
|
||||
private const string _disableArchivesName = "disable-archives";
|
||||
internal readonly FlagInput DisableArchivesInput = new(_disableArchivesName, "--disable-archives", "Disable scanning archives (requires --scan)");
|
||||
|
||||
private const string _enableDebugName = "enable-debug";
|
||||
internal readonly FlagInput EnableDebugInput = new(_enableDebugName, "--enable-debug", "Enable debug protection information (requires --scan)");
|
||||
|
||||
private const string _hideDriveLettersName = "hide-drive-letters";
|
||||
internal readonly FlagInput HideDriveLettersInput = new(_hideDriveLettersName, "--hide-drive-letters", "Hide drive letters from scan output (requires --scan)");
|
||||
|
||||
private const string _includeArtifactsName = "include-artifacts";
|
||||
internal readonly FlagInput IncludeArtifactsInput = new(_includeArtifactsName, "--include-artifacts", "Include artifacts in JSON (requires --json)");
|
||||
|
||||
private const string _jsonName = "json";
|
||||
internal readonly FlagInput JsonInput = new(_jsonName, ["-j", "--json"], "Enable submission JSON output");
|
||||
|
||||
private const string _loadSeedName = "load-seed";
|
||||
internal readonly StringInput LoadSeedInput = new(_loadSeedName, "--load-seed", "Load a seed submission JSON for user information");
|
||||
|
||||
private const string _logCompressionName = "log-compression";
|
||||
internal readonly StringInput LogCompressionInput = new(_logCompressionName, "--log-compression", "Set the log compression type (requires --zip)");
|
||||
|
||||
private const string _noPlaceholdersName = "no-placeholders";
|
||||
internal readonly FlagInput NoPlaceholdersInput = new(_noPlaceholdersName, "--no-placeholders", "Disable placeholder values in submission info");
|
||||
|
||||
private const string _noRetrieveName = "no-retrieve";
|
||||
internal readonly FlagInput NoRetrieveInput = new(_noRetrieveName, "--no-retrieve", "Disable retrieving match information from Redump");
|
||||
|
||||
private const string _passwordName = "password";
|
||||
internal readonly StringInput PasswordInput = new(_passwordName, ["-P", "--password"], "Redump password (incompatible with --no-retrieve)");
|
||||
|
||||
private const string _pathName = "path";
|
||||
internal readonly StringInput PathInput = new(_pathName, ["-p", "--path"], "Physical drive path for additional checks");
|
||||
|
||||
private const string _pullAllName = "pull-all";
|
||||
internal readonly FlagInput PullAllInput = new(_pullAllName, "--pull-all", "Pull all information from Redump (requires --username and --password)");
|
||||
|
||||
private const string _scanName = "scan";
|
||||
internal readonly FlagInput ScanInput = new(_scanName, ["-s", "--scan"], "Enable copy protection scan (requires --path)");
|
||||
|
||||
private const string _suffixName = "suffix";
|
||||
internal readonly FlagInput SuffixInput = new(_suffixName, ["-x", "--suffix"], "Enable adding filename suffix");
|
||||
|
||||
private const string _useName = "use";
|
||||
internal readonly StringInput UseInput = new(_useName, ["-u", "--use"], "Override configured dumping program name");
|
||||
|
||||
private const string _usernameName = "username";
|
||||
internal readonly StringInput UsernameInput = new(_usernameName, ["-U", "--username"], "Redump username (incompatible with --no-retrieve)");
|
||||
|
||||
private const string _zipName = "zip";
|
||||
internal readonly FlagInput ZipInput = new(_zipName, ["-z", "--zip"], "Enable log file compression");
|
||||
|
||||
#endregion
|
||||
|
||||
public MainFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
Add(UseInput);
|
||||
Add(LoadSeedInput);
|
||||
Add(NoPlaceholdersInput);
|
||||
Add(CreateIrdInput);
|
||||
Add(NoRetrieveInput);
|
||||
// TODO: Figure out how to work with the credentials input
|
||||
Add(PullAllInput);
|
||||
Add(PathInput);
|
||||
Add(ScanInput);
|
||||
Add(DisableArchivesInput);
|
||||
Add(EnableDebugInput);
|
||||
Add(HideDriveLettersInput);
|
||||
Add(SuffixInput);
|
||||
Add(JsonInput);
|
||||
Add(IncludeArtifactsInput);
|
||||
Add(ZipInput);
|
||||
Add(LogCompressionInput);
|
||||
Add(DeleteInput);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool ProcessArgs(string[] args, int index)
|
||||
{
|
||||
// These values require multiple parts to be active
|
||||
bool scan = false,
|
||||
enableArchives = true,
|
||||
enableDebug = false,
|
||||
hideDriveLetters = false;
|
||||
|
||||
// If we have no arguments, just return
|
||||
if (args == null || args.Length == 0)
|
||||
return true;
|
||||
|
||||
// Read the options from config, if possible
|
||||
Options = OptionsLoader.LoadFromConfig();
|
||||
if (Options.FirstRun)
|
||||
{
|
||||
Options = new Options()
|
||||
{
|
||||
// Internal Program
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
|
||||
// Extra Dumping Options
|
||||
ScanForProtection = false,
|
||||
AddPlaceholders = true,
|
||||
PullAllInformation = false,
|
||||
AddFilenameSuffix = false,
|
||||
OutputSubmissionJSON = false,
|
||||
IncludeArtifacts = false,
|
||||
CompressLogFiles = false,
|
||||
LogCompression = LogCompression.DeflateMaximum,
|
||||
DeleteUnnecessaryFiles = false,
|
||||
CreateIRDAfterDumping = false,
|
||||
|
||||
// Protection Scanning Options
|
||||
ScanArchivesForProtection = true,
|
||||
IncludeDebugProtectionInformation = false,
|
||||
HideDriveLetters = false,
|
||||
|
||||
// Redump Login Information
|
||||
RetrieveMatchInformation = true,
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
};
|
||||
}
|
||||
|
||||
// The first argument is the system type
|
||||
System = args[0].Trim('"').ToRedumpSystem();
|
||||
|
||||
// Loop through the arguments and parse out values
|
||||
for (index = 1; index < args.Length; index++)
|
||||
{
|
||||
// Use specific program
|
||||
if (UseInput.ProcessInput(args, ref index))
|
||||
Options.InternalProgram = UseInput.Value.ToInternalProgram();
|
||||
|
||||
// Include seed info file
|
||||
else if (LoadSeedInput.ProcessInput(args, ref index))
|
||||
Seed = Builder.CreateFromFile(LoadSeedInput.Value);
|
||||
|
||||
// Disable placeholder values in submission info
|
||||
else if (NoPlaceholdersInput.ProcessInput(args, ref index))
|
||||
Options.AddPlaceholders = false;
|
||||
|
||||
// Create IRD from output files (PS3 only)
|
||||
else if (CreateIrdInput.ProcessInput(args, ref index))
|
||||
Options.CreateIRDAfterDumping = true;
|
||||
|
||||
// Set the log compression type (requires --zip)
|
||||
else if (LogCompressionInput.ProcessInput(args, ref index))
|
||||
Options.LogCompression = LogCompressionInput.Value.ToLogCompression();
|
||||
|
||||
// Retrieve Redump match information
|
||||
else if (NoRetrieveInput.ProcessInput(args, ref index))
|
||||
Options.RetrieveMatchInformation = false;
|
||||
|
||||
// Redump login
|
||||
else if (args[index].StartsWith("-c=") || args[index].StartsWith("--credentials="))
|
||||
{
|
||||
string[] credentials = args[index].Split('=')[1].Split(';');
|
||||
Options.RedumpUsername = credentials[0];
|
||||
Options.RedumpPassword = credentials[1];
|
||||
}
|
||||
else if (args[index] == "-c" || args[index] == "--credentials")
|
||||
{
|
||||
Options.RedumpUsername = args[index + 1];
|
||||
Options.RedumpPassword = args[index + 2];
|
||||
index += 2;
|
||||
}
|
||||
|
||||
// Redump username
|
||||
else if (UsernameInput.ProcessInput(args, ref index))
|
||||
Options.RedumpUsername = UsernameInput.Value;
|
||||
|
||||
// Redump password
|
||||
else if (PasswordInput.ProcessInput(args, ref index))
|
||||
Options.RedumpPassword = PasswordInput.Value;
|
||||
|
||||
// Pull all information (requires Redump login)
|
||||
else if (PullAllInput.ProcessInput(args, ref index))
|
||||
Options.PullAllInformation = true;
|
||||
|
||||
// Use a device path for physical checks
|
||||
else if (PathInput.ProcessInput(args, ref index))
|
||||
DevicePath = PathInput.Value;
|
||||
|
||||
// Scan for protection (requires device path)
|
||||
else if (ScanInput.ProcessInput(args, ref index))
|
||||
scan = true;
|
||||
|
||||
// Disable scanning archives (requires --scan)
|
||||
else if (ScanInput.ProcessInput(args, ref index))
|
||||
enableArchives = false;
|
||||
|
||||
// Enable debug protection information (requires --scan)
|
||||
else if (EnableDebugInput.ProcessInput(args, ref index))
|
||||
enableDebug = true;
|
||||
|
||||
// Hide drive letters from scan output (requires --scan)
|
||||
else if (HideDriveLettersInput.ProcessInput(args, ref index))
|
||||
hideDriveLetters = true;
|
||||
|
||||
// Add filename suffix
|
||||
else if (SuffixInput.ProcessInput(args, ref index))
|
||||
Options.AddFilenameSuffix = true;
|
||||
|
||||
// Output submission JSON
|
||||
else if (JsonInput.ProcessInput(args, ref index))
|
||||
Options.OutputSubmissionJSON = true;
|
||||
|
||||
// Include JSON artifacts
|
||||
else if (IncludeArtifactsInput.ProcessInput(args, ref index))
|
||||
Options.IncludeArtifacts = true;
|
||||
|
||||
// Compress log and extraneous files
|
||||
else if (ZipInput.ProcessInput(args, ref index))
|
||||
Options.CompressLogFiles = true;
|
||||
|
||||
// Delete unnecessary files
|
||||
else if (DeleteInput.ProcessInput(args, ref index))
|
||||
Options.DeleteUnnecessaryFiles = true;
|
||||
|
||||
// Default, add to inputs
|
||||
else
|
||||
Inputs.Add(args[index]);
|
||||
}
|
||||
|
||||
// Now deal with the complex options
|
||||
Options.ScanForProtection = scan && !string.IsNullOrEmpty(DevicePath);
|
||||
Options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(DevicePath);
|
||||
Options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(DevicePath);
|
||||
Options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(DevicePath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => Inputs.Count > 0;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.4.0</VersionPrefix>
|
||||
<VersionPrefix>3.5.0</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Title>MPF Check</Title>
|
||||
@@ -43,7 +43,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
|
||||
<PackageReference Include="SabreTools.CommandLine" Version="[1.3.2]" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="[1.7.4]" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
#if NET40
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using MPF.Frontend;
|
||||
using MPF.Frontend.Tools;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
using MPF.Check.Features;
|
||||
using MPF.Frontend.Features;
|
||||
using SabreTools.CommandLine;
|
||||
using SabreTools.CommandLine.Features;
|
||||
|
||||
namespace MPF.Check
|
||||
{
|
||||
@@ -15,547 +14,126 @@ namespace MPF.Check
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Create a default options object
|
||||
var options = new Options()
|
||||
{
|
||||
// Internal Program
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
// Create the command set
|
||||
var mainFeature = new MainFeature();
|
||||
var commandSet = CreateCommands(mainFeature);
|
||||
|
||||
// Extra Dumping Options
|
||||
ScanForProtection = false,
|
||||
AddPlaceholders = true,
|
||||
PullAllInformation = false,
|
||||
AddFilenameSuffix = false,
|
||||
OutputSubmissionJSON = false,
|
||||
IncludeArtifacts = false,
|
||||
CompressLogFiles = false,
|
||||
DeleteUnnecessaryFiles = false,
|
||||
CreateIRDAfterDumping = false,
|
||||
|
||||
// Protection Scanning Options
|
||||
ScanArchivesForProtection = true,
|
||||
IncludeDebugProtectionInformation = false,
|
||||
HideDriveLetters = false,
|
||||
|
||||
// Redump Login Information
|
||||
RetrieveMatchInformation = true,
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
};
|
||||
|
||||
// Try processing the standalone arguments
|
||||
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
|
||||
if (standaloneProcessed != false)
|
||||
{
|
||||
if (standaloneProcessed == null)
|
||||
DisplayHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup common outputs
|
||||
CommandOptions opts;
|
||||
RedumpSystem? knownSystem;
|
||||
int startIndex;
|
||||
|
||||
// Use interactive mode
|
||||
if (args.Length > 0 && (args[0] == "-i" || args[0] == "--interactive"))
|
||||
{
|
||||
startIndex = 1;
|
||||
opts = InteractiveMode(options, out knownSystem);
|
||||
}
|
||||
|
||||
// Use normal commandline parameters
|
||||
else
|
||||
{
|
||||
// Try processing the common arguments
|
||||
bool success = OptionsLoader.ProcessCommonArguments(args, out knownSystem, out var error);
|
||||
if (!success)
|
||||
{
|
||||
DisplayHelp(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through and process options
|
||||
startIndex = 1;
|
||||
opts = LoadFromArguments(args, options, ref startIndex);
|
||||
}
|
||||
|
||||
if (options.InternalProgram == InternalProgram.NONE)
|
||||
{
|
||||
DisplayHelp("A program name needs to be provided");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate the supplied credentials
|
||||
if (options.RetrieveMatchInformation
|
||||
&& !string.IsNullOrEmpty(options.RedumpUsername)
|
||||
&& !string.IsNullOrEmpty(options.RedumpPassword))
|
||||
{
|
||||
bool? validated = RedumpClient.ValidateCredentials(options.RedumpUsername!, options.RedumpPassword!).GetAwaiter().GetResult();
|
||||
string message = validated switch
|
||||
{
|
||||
true => "Redump username and password accepted!",
|
||||
false => "Redump username and password denied!",
|
||||
null => "An error occurred validating your credentials!",
|
||||
};
|
||||
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
|
||||
// Loop through all the rest of the args
|
||||
for (int i = startIndex; i < args.Length; i++)
|
||||
{
|
||||
// Check for a file
|
||||
if (!File.Exists(args[i].Trim('"')))
|
||||
{
|
||||
DisplayHelp($"{args[i].Trim('"')} does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the full file path
|
||||
string filepath = Path.GetFullPath(args[i].Trim('"'));
|
||||
|
||||
// Now populate an environment
|
||||
Drive? drive = null;
|
||||
if (!string.IsNullOrEmpty(opts.DevicePath))
|
||||
drive = Drive.Create(null, opts.DevicePath!);
|
||||
|
||||
var env = new DumpEnvironment(options,
|
||||
filepath,
|
||||
drive,
|
||||
knownSystem,
|
||||
internalProgram: null);
|
||||
env.SetProcessor();
|
||||
|
||||
// Finally, attempt to do the output dance
|
||||
var result = env.VerifyAndSaveDumpOutput(seedInfo: opts.Seed)
|
||||
.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
Console.WriteLine(result.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display help for MPF.Check
|
||||
/// </summary>
|
||||
/// <param name="error">Error string to prefix the help text with</param>
|
||||
private static void DisplayHelp(string? error = null)
|
||||
{
|
||||
if (error != null)
|
||||
Console.WriteLine(error);
|
||||
|
||||
Console.WriteLine("Usage:");
|
||||
Console.WriteLine("MPF.Check <system> [options] </path/to/output.cue/iso> ...");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Standalone Options:");
|
||||
Console.WriteLine("-h, -?, --help Show this help text");
|
||||
Console.WriteLine("--version Print the program version");
|
||||
Console.WriteLine("-lc, --listcodes List supported comment/content site codes");
|
||||
Console.WriteLine("-lm, --listmedia List supported media types");
|
||||
Console.WriteLine("-ls, --listsystems List supported system types");
|
||||
Console.WriteLine("-lp, --listprograms List supported dumping program outputs");
|
||||
Console.WriteLine("-i, --interactive Enable interactive mode");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Check Options:");
|
||||
Console.WriteLine("-u, --use <program> Dumping program output type [REQUIRED]");
|
||||
Console.WriteLine(" --load-seed <path> Load a seed submission JSON for user information");
|
||||
Console.WriteLine(" --no-placeholders Disable placeholder values in submission info");
|
||||
Console.WriteLine(" --create-ird Create IRD from output files (PS3 only)");
|
||||
Console.WriteLine(" --no-retrieve Disable retrieving match information from Redump");
|
||||
Console.WriteLine("-c, --credentials <user> <pw> Redump username and password (incompatible with --no-retrieve)");
|
||||
Console.WriteLine(" --pull-all Pull all information from Redump (requires --credentials)");
|
||||
Console.WriteLine("-p, --path <drivepath> Physical drive path for additional checks");
|
||||
Console.WriteLine("-s, --scan Enable copy protection scan (requires --path)");
|
||||
Console.WriteLine(" --disable-archives Disable scanning archives (requires --scan)");
|
||||
Console.WriteLine(" --enable-debug Enable debug protection information (requires --scan)");
|
||||
Console.WriteLine(" --hide-drive-letters Hide drive letters from scan output (requires --scan)");
|
||||
Console.WriteLine("-x, --suffix Enable adding filename suffix");
|
||||
Console.WriteLine("-j, --json Enable submission JSON output");
|
||||
Console.WriteLine(" --include-artifacts Include artifacts in JSON (requires --json)");
|
||||
Console.WriteLine("-z, --zip Enable log file compression");
|
||||
Console.WriteLine("-d, --delete Enable unnecessary file deletion");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("WARNING: Check will overwrite both any existing submission information files as well");
|
||||
Console.WriteLine("as any log archives. Please make backups of those if you need to before running Check.");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable interactive mode for entering information
|
||||
/// </summary>
|
||||
private static CommandOptions InteractiveMode(Options options, out RedumpSystem? system)
|
||||
{
|
||||
// Create return values
|
||||
var opts = new CommandOptions();
|
||||
system = null;
|
||||
|
||||
// These values require multiple parts to be active
|
||||
bool scan = false,
|
||||
enableArchives = true,
|
||||
enableDebug = false,
|
||||
hideDriveLetters = false;
|
||||
|
||||
// Create state values
|
||||
string? result = string.Empty;
|
||||
|
||||
root:
|
||||
Console.Clear();
|
||||
Console.WriteLine("MPF.Check Interactive Mode - Main Menu");
|
||||
Console.WriteLine("-------------------------");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"1) Set system (Currently '{system}')");
|
||||
Console.WriteLine($"2) Set dumping program (Currently '{options.InternalProgram}')");
|
||||
Console.WriteLine($"3) Set seed path (Currently '{opts.Seed}')");
|
||||
Console.WriteLine($"4) Add placeholders (Currently '{options.AddPlaceholders}')");
|
||||
Console.WriteLine($"5) Create IRD (Currently '{options.CreateIRDAfterDumping}')");
|
||||
Console.WriteLine($"6) Attempt Redump matches (Currently '{options.RetrieveMatchInformation}')");
|
||||
Console.WriteLine($"7) Redump credentials (Currently '{options.RedumpUsername}')");
|
||||
Console.WriteLine($"8) Pull all information (Currently '{options.PullAllInformation}')");
|
||||
Console.WriteLine($"9) Set device path (Currently '{opts.DevicePath}')");
|
||||
Console.WriteLine($"A) Scan for protection (Currently '{scan}')");
|
||||
Console.WriteLine($"B) Scan archives for protection (Currently '{enableArchives}')");
|
||||
Console.WriteLine($"C) Debug protection scan output (Currently '{enableDebug}')");
|
||||
Console.WriteLine($"D) Hide drive letters in protection output (Currently '{hideDriveLetters}')");
|
||||
Console.WriteLine($"E) Hide filename suffix (Currently '{options.AddFilenameSuffix}')");
|
||||
Console.WriteLine($"F) Output submission JSON (Currently '{options.OutputSubmissionJSON}')");
|
||||
Console.WriteLine($"G) Include JSON artifacts (Currently '{options.IncludeArtifacts}')");
|
||||
Console.WriteLine($"H) Compress logs (Currently '{options.CompressLogFiles}')");
|
||||
Console.WriteLine($"I) Delete unnecessary files (Currently '{options.DeleteUnnecessaryFiles}')");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"Q) Exit the program");
|
||||
Console.WriteLine($"X) Start checking");
|
||||
Console.Write("> ");
|
||||
|
||||
result = Console.ReadLine();
|
||||
switch (result)
|
||||
{
|
||||
case "1":
|
||||
goto system;
|
||||
case "2":
|
||||
goto dumpingProgram;
|
||||
case "3":
|
||||
goto seedPath;
|
||||
case "4":
|
||||
options.AddPlaceholders = !options.AddPlaceholders;
|
||||
goto root;
|
||||
case "5":
|
||||
options.CreateIRDAfterDumping = !options.CreateIRDAfterDumping;
|
||||
goto root;
|
||||
case "6":
|
||||
options.RetrieveMatchInformation = !options.RetrieveMatchInformation;
|
||||
goto root;
|
||||
case "7":
|
||||
goto redumpCredentials;
|
||||
case "8":
|
||||
options.PullAllInformation = !options.PullAllInformation;
|
||||
goto root;
|
||||
case "9":
|
||||
goto devicePath;
|
||||
case "a":
|
||||
case "A":
|
||||
scan = !scan;
|
||||
goto root;
|
||||
case "b":
|
||||
case "B":
|
||||
enableArchives = !enableArchives;
|
||||
goto root;
|
||||
case "c":
|
||||
case "C":
|
||||
enableDebug = !enableDebug;
|
||||
goto root;
|
||||
case "d":
|
||||
case "D":
|
||||
hideDriveLetters = !hideDriveLetters;
|
||||
goto root;
|
||||
case "e":
|
||||
case "E":
|
||||
options.AddFilenameSuffix = !options.AddFilenameSuffix;
|
||||
goto root;
|
||||
case "f":
|
||||
case "F":
|
||||
options.OutputSubmissionJSON = !options.OutputSubmissionJSON;
|
||||
goto root;
|
||||
case "g":
|
||||
case "G":
|
||||
options.IncludeArtifacts = !options.IncludeArtifacts;
|
||||
goto root;
|
||||
case "h":
|
||||
case "H":
|
||||
options.CompressLogFiles = !options.CompressLogFiles;
|
||||
goto root;
|
||||
case "i":
|
||||
case "I":
|
||||
options.DeleteUnnecessaryFiles = !options.DeleteUnnecessaryFiles;
|
||||
goto root;
|
||||
|
||||
case "q":
|
||||
case "Q":
|
||||
Environment.Exit(0);
|
||||
break;
|
||||
case "x":
|
||||
case "X":
|
||||
Console.Clear();
|
||||
goto exit;
|
||||
case "z":
|
||||
case "Z":
|
||||
Console.WriteLine("It is pitch black. You are likely to be eaten by a grue.");
|
||||
Console.Write("> ");
|
||||
Console.ReadLine();
|
||||
goto root;
|
||||
default:
|
||||
Console.WriteLine($"Invalid selection: {result}");
|
||||
Console.ReadLine();
|
||||
goto root;
|
||||
}
|
||||
|
||||
system:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the system and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
system = Extensions.ToRedumpSystem(result);
|
||||
goto root;
|
||||
|
||||
dumpingProgram:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the dumping program and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
options.InternalProgram = result.ToInternalProgram();
|
||||
goto root;
|
||||
|
||||
seedPath:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the seed path and press Enter:");
|
||||
Console.Write("> ");
|
||||
result = Console.ReadLine();
|
||||
opts.Seed = Builder.CreateFromFile(result);
|
||||
goto root;
|
||||
|
||||
redumpCredentials:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Enter your Redumper username and press Enter:");
|
||||
Console.Write("> ");
|
||||
options.RedumpUsername = Console.ReadLine();
|
||||
|
||||
Console.WriteLine("Enter your Redumper password (hidden) and press Enter:");
|
||||
Console.Write("> ");
|
||||
options.RedumpPassword = string.Empty;
|
||||
while (true)
|
||||
{
|
||||
var key = Console.ReadKey(true);
|
||||
if (key.Key == ConsoleKey.Enter)
|
||||
break;
|
||||
|
||||
options.RedumpPassword += key.KeyChar;
|
||||
}
|
||||
|
||||
goto root;
|
||||
|
||||
devicePath:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Input the device path and press Enter:");
|
||||
Console.Write("> ");
|
||||
opts.DevicePath = Console.ReadLine();
|
||||
goto root;
|
||||
|
||||
exit:
|
||||
// Now deal with the complex options
|
||||
options.ScanForProtection = scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the current set of options from application arguments
|
||||
/// </summary>
|
||||
private static CommandOptions LoadFromArguments(string[] args, Options options, ref int startIndex)
|
||||
{
|
||||
// Create return values
|
||||
var opts = new CommandOptions();
|
||||
|
||||
// These values require multiple parts to be active
|
||||
bool scan = false,
|
||||
enableArchives = true,
|
||||
enableDebug = false,
|
||||
hideDriveLetters = false;
|
||||
|
||||
// If we have no arguments, just return
|
||||
// If we have no args, show the help and quit
|
||||
if (args == null || args.Length == 0)
|
||||
{
|
||||
startIndex = 0;
|
||||
return opts;
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have an invalid start index, just return
|
||||
if (startIndex < 0 || startIndex >= args.Length)
|
||||
return opts;
|
||||
// Get the first argument as a feature flag
|
||||
string featureName = args[0];
|
||||
|
||||
// Loop through the arguments and parse out values
|
||||
for (; startIndex < args.Length; startIndex++)
|
||||
// Try processing the standalone arguments
|
||||
var topLevel = commandSet.GetTopLevel(featureName);
|
||||
switch (topLevel)
|
||||
{
|
||||
// Use specific program
|
||||
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
{
|
||||
string internalProgram = args[startIndex].Split('=')[1];
|
||||
options.InternalProgram = internalProgram.ToInternalProgram();
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
string internalProgram = args[startIndex + 1];
|
||||
options.InternalProgram = internalProgram.ToInternalProgram();
|
||||
startIndex++;
|
||||
}
|
||||
// Standalone Options
|
||||
case Help: BaseFeature.DisplayHelp(); return;
|
||||
case VersionFeature version: version.Execute(); return;
|
||||
case ListCodesFeature lc: lc.Execute(); return;
|
||||
case ListMediaTypesFeature lm: lm.Execute(); return;
|
||||
case ListProgramsFeature lp: lp.Execute(); return;
|
||||
case ListSystemsFeature ls: ls.Execute(); return;
|
||||
|
||||
// Include seed info file
|
||||
else if (args[startIndex].StartsWith("--load-seed="))
|
||||
{
|
||||
string seedInfo = args[startIndex].Split('=')[1];
|
||||
opts.Seed = Builder.CreateFromFile(seedInfo);
|
||||
}
|
||||
else if (args[startIndex] == "--load-seed")
|
||||
{
|
||||
string seedInfo = args[startIndex + 1];
|
||||
opts.Seed = Builder.CreateFromFile(seedInfo);
|
||||
startIndex++;
|
||||
}
|
||||
// Interactive Mode
|
||||
case InteractiveFeature interactive:
|
||||
if (!interactive.ProcessArgs(args, 0))
|
||||
{
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
if (!interactive.VerifyInputs())
|
||||
{
|
||||
Console.Error.WriteLine("At least one input is required");
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
if (!interactive.Execute())
|
||||
{
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Default Behavior
|
||||
default:
|
||||
if (!mainFeature.ProcessArgs(args, 0))
|
||||
{
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
if (!mainFeature.VerifyInputs())
|
||||
{
|
||||
Console.Error.WriteLine("At least one input is required");
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
if (!mainFeature.Execute())
|
||||
{
|
||||
BaseFeature.DisplayHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable placeholder values in submission info
|
||||
else if (args[startIndex].Equals("--no-placeholders"))
|
||||
{
|
||||
options.AddPlaceholders = false;
|
||||
}
|
||||
|
||||
// Create IRD from output files (PS3 only)
|
||||
else if (args[startIndex].Equals("--create-ird"))
|
||||
{
|
||||
options.CreateIRDAfterDumping = true;
|
||||
}
|
||||
|
||||
// Retrieve Redump match information
|
||||
else if (args[startIndex] == "--no-retrieve")
|
||||
{
|
||||
options.RetrieveMatchInformation = false;
|
||||
}
|
||||
|
||||
// Redump login
|
||||
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
|
||||
{
|
||||
string[] credentials = args[startIndex].Split('=')[1].Split(';');
|
||||
options.RedumpUsername = credentials[0];
|
||||
options.RedumpPassword = credentials[1];
|
||||
}
|
||||
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
|
||||
{
|
||||
options.RedumpUsername = args[startIndex + 1];
|
||||
options.RedumpPassword = args[startIndex + 2];
|
||||
startIndex += 2;
|
||||
}
|
||||
|
||||
// Pull all information (requires Redump login)
|
||||
else if (args[startIndex].Equals("--pull-all"))
|
||||
{
|
||||
options.PullAllInformation = true;
|
||||
}
|
||||
|
||||
// Use a device path for physical checks
|
||||
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
|
||||
{
|
||||
opts.DevicePath = args[startIndex].Split('=')[1];
|
||||
}
|
||||
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
|
||||
{
|
||||
opts.DevicePath = args[startIndex + 1];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Scan for protection (requires device path)
|
||||
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
|
||||
{
|
||||
scan = true;
|
||||
}
|
||||
|
||||
// Disable scanning archives (requires --scan)
|
||||
else if (args[startIndex].Equals("--disable-archives"))
|
||||
{
|
||||
enableArchives = false;
|
||||
}
|
||||
|
||||
// Enable debug protection information (requires --scan)
|
||||
else if (args[startIndex].Equals("--enable-debug"))
|
||||
{
|
||||
enableDebug = true;
|
||||
}
|
||||
|
||||
// Hide drive letters from scan output (requires --scan)
|
||||
else if (args[startIndex].Equals("--hide-drive-letters"))
|
||||
{
|
||||
hideDriveLetters = true;
|
||||
}
|
||||
|
||||
// Add filename suffix
|
||||
else if (args[startIndex].Equals("-x") || args[startIndex].Equals("--suffix"))
|
||||
{
|
||||
options.AddFilenameSuffix = true;
|
||||
}
|
||||
|
||||
// Output submission JSON
|
||||
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
|
||||
{
|
||||
options.OutputSubmissionJSON = true;
|
||||
}
|
||||
|
||||
// Include JSON artifacts
|
||||
else if (args[startIndex].Equals("--include-artifacts"))
|
||||
{
|
||||
options.IncludeArtifacts = true;
|
||||
}
|
||||
|
||||
// Compress log and extraneous files
|
||||
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
|
||||
{
|
||||
options.CompressLogFiles = true;
|
||||
}
|
||||
|
||||
// Delete unnecessary files
|
||||
else if (args[startIndex].Equals("-d") || args[startIndex].Equals("--delete"))
|
||||
{
|
||||
options.DeleteUnnecessaryFiles = true;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now deal with the complex options
|
||||
options.ScanForProtection = scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents commandline options
|
||||
/// Create the command set for the program
|
||||
/// </summary>
|
||||
private class CommandOptions
|
||||
private static CommandSet CreateCommands(MainFeature mainFeature)
|
||||
{
|
||||
/// <summary>
|
||||
/// Seed submission info from an input file
|
||||
/// </summary>
|
||||
public SubmissionInfo? Seed { get; set; } = null;
|
||||
List<string> header = [
|
||||
"MPF.CLI [standalone|system] [options] <path> ...",
|
||||
string.Empty,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Path to the device to scan
|
||||
/// </summary>
|
||||
public string? DevicePath { get; set; } = null;
|
||||
List<string> footer = [
|
||||
string.Empty,
|
||||
"WARNING: Check will overwrite both any existing submission information files as well",
|
||||
"as any log archives. Please make backups of those if you need to before running Check.",
|
||||
string.Empty,
|
||||
];
|
||||
|
||||
var commandSet = new CommandSet(header, footer);
|
||||
|
||||
// Standalone Options
|
||||
commandSet.Add(new Help());
|
||||
commandSet.Add(new VersionFeature());
|
||||
commandSet.Add(new ListCodesFeature());
|
||||
commandSet.Add(new ListMediaTypesFeature());
|
||||
commandSet.Add(new ListSystemsFeature());
|
||||
commandSet.Add(new ListProgramsFeature());
|
||||
commandSet.Add(new InteractiveFeature());
|
||||
|
||||
// Check Options
|
||||
commandSet.Add(mainFeature.UseInput);
|
||||
commandSet.Add(mainFeature.LoadSeedInput);
|
||||
commandSet.Add(mainFeature.NoPlaceholdersInput);
|
||||
commandSet.Add(mainFeature.CreateIrdInput);
|
||||
commandSet.Add(mainFeature.NoRetrieveInput);
|
||||
commandSet.Add(mainFeature.UsernameInput);
|
||||
commandSet.Add(mainFeature.PasswordInput);
|
||||
commandSet.Add(mainFeature.PullAllInput);
|
||||
commandSet.Add(mainFeature.PathInput);
|
||||
commandSet.Add(mainFeature.ScanInput);
|
||||
commandSet.Add(mainFeature.DisableArchivesInput);
|
||||
commandSet.Add(mainFeature.EnableDebugInput);
|
||||
commandSet.Add(mainFeature.HideDriveLettersInput);
|
||||
commandSet.Add(mainFeature.SuffixInput);
|
||||
commandSet.Add(mainFeature.JsonInput);
|
||||
commandSet.Add(mainFeature.IncludeArtifactsInput);
|
||||
commandSet.Add(mainFeature.ZipInput);
|
||||
commandSet.Add(mainFeature.LogCompressionInput);
|
||||
commandSet.Add(mainFeature.DeleteInput);
|
||||
|
||||
return commandSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,15 +40,15 @@ namespace MPF.ExecutionContexts.Test
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, null, "filename.bin", null, null)]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.NintendoWii, MediaType.NintendoWiiOpticalDisc, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.NintendoWiiU, MediaType.NintendoWiiUOpticalDisc, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.FloppyDisk, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.NintendoWii, MediaType.NintendoWiiOpticalDisc, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.NintendoWiiU, MediaType.NintendoWiiUOpticalDisc, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.FloppyDisk, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
|
||||
public void DefaultValueTest(RedumpSystem? system,
|
||||
MediaType? type,
|
||||
string? drivePath,
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.14.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="[1.7.4]" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="1.24.0" />
|
||||
@@ -29,7 +29,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -41,9 +41,9 @@ namespace MPF.ExecutionContexts.Test
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, null, "filename.bin", null, "")]
|
||||
[InlineData(null, null, null, "filename.bin", null, "disc --verbose --skeleton --retries=1000 --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --skeleton --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --skeleton --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
|
||||
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
|
||||
[InlineData(RedumpSystem.NintendoWii, MediaType.NintendoWiiOpticalDisc, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
|
||||
|
||||
@@ -638,11 +638,6 @@ namespace MPF.ExecutionContexts.Aaru
|
||||
(_inputs[FlagStrings.SpeedLong] as Int8Input)?.SetValue((sbyte)driveSpeed);
|
||||
}
|
||||
|
||||
// First check to see if the combination of system and MediaType is valid
|
||||
var validTypes = RedumpSystem.MediaTypes();
|
||||
if (!validTypes.Contains(MediaType))
|
||||
return;
|
||||
|
||||
// Set retry count
|
||||
int rereadCount = GetInt32Setting(options, SettingConstants.RereadCount, SettingConstants.RereadCountDefault);
|
||||
if (rereadCount > 0)
|
||||
@@ -673,57 +668,13 @@ namespace MPF.ExecutionContexts.Aaru
|
||||
(_inputs[FlagStrings.PrivateLong] as BooleanInput)?.SetValue(true);
|
||||
}
|
||||
|
||||
// TODO: Look at dump-media formats and the like and see what options there are there to fill in defaults
|
||||
// Now sort based on disc type
|
||||
switch (MediaType)
|
||||
{
|
||||
case SabreTools.RedumpLib.Data.MediaType.CDROM:
|
||||
// Currently no defaults set
|
||||
break;
|
||||
case SabreTools.RedumpLib.Data.MediaType.DVD:
|
||||
this[FlagStrings.StoreEncryptedLong] = true; // TODO: Make this configurable
|
||||
(_inputs[FlagStrings.StoreEncryptedLong] as BooleanInput)?.SetValue(true);
|
||||
this[FlagStrings.TitleKeysLong] = false; // TODO: Make this configurable
|
||||
(_inputs[FlagStrings.TitleKeysLong] as BooleanInput)?.SetValue(false);
|
||||
this[FlagStrings.TrimLong] = true; // TODO: Make this configurable
|
||||
(_inputs[FlagStrings.TrimLong] as BooleanInput)?.SetValue(true);
|
||||
break;
|
||||
case SabreTools.RedumpLib.Data.MediaType.GDROM:
|
||||
// Currently no defaults set
|
||||
break;
|
||||
case SabreTools.RedumpLib.Data.MediaType.HDDVD:
|
||||
this[FlagStrings.StoreEncryptedLong] = true; // TODO: Make this configurable
|
||||
(_inputs[FlagStrings.StoreEncryptedLong] as BooleanInput)?.SetValue(true);
|
||||
this[FlagStrings.TitleKeysLong] = false; // TODO: Make this configurable
|
||||
(_inputs[FlagStrings.TitleKeysLong] as BooleanInput)?.SetValue(false);
|
||||
this[FlagStrings.TrimLong] = true; // TODO: Make this configurable
|
||||
(_inputs[FlagStrings.TrimLong] as BooleanInput)?.SetValue(true);
|
||||
break;
|
||||
case SabreTools.RedumpLib.Data.MediaType.BluRay:
|
||||
this[FlagStrings.StoreEncryptedLong] = true; // TODO: Make this configurable
|
||||
(_inputs[FlagStrings.StoreEncryptedLong] as BooleanInput)?.SetValue(true);
|
||||
this[FlagStrings.TitleKeysLong] = false; // TODO: Make this configurable
|
||||
(_inputs[FlagStrings.TitleKeysLong] as BooleanInput)?.SetValue(false);
|
||||
this[FlagStrings.TrimLong] = true; // TODO: Make this configurable
|
||||
(_inputs[FlagStrings.TrimLong] as BooleanInput)?.SetValue(true);
|
||||
break;
|
||||
|
||||
// Special Formats
|
||||
case SabreTools.RedumpLib.Data.MediaType.NintendoGameCubeGameDisc:
|
||||
// Currently no defaults set
|
||||
break;
|
||||
case SabreTools.RedumpLib.Data.MediaType.NintendoWiiOpticalDisc:
|
||||
// Currently no defaults set
|
||||
break;
|
||||
case SabreTools.RedumpLib.Data.MediaType.NintendoWiiUOpticalDisc:
|
||||
// Currently no defaults set
|
||||
break;
|
||||
|
||||
// Non-optical
|
||||
case SabreTools.RedumpLib.Data.MediaType.FloppyDisk:
|
||||
// Currently no defaults set
|
||||
break;
|
||||
}
|
||||
// Set generic, sane defaults to cover all bases
|
||||
this[FlagStrings.StoreEncryptedLong] = true;
|
||||
(_inputs[FlagStrings.StoreEncryptedLong] as BooleanInput)?.SetValue(true);
|
||||
this[FlagStrings.TitleKeysLong] = false;
|
||||
(_inputs[FlagStrings.TitleKeysLong] as BooleanInput)?.SetValue(false);
|
||||
this[FlagStrings.TrimLong] = true;
|
||||
(_inputs[FlagStrings.TrimLong] as BooleanInput)?.SetValue(true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.4.0</VersionPrefix>
|
||||
<VersionPrefix>3.5.0</VersionPrefix>
|
||||
<WarningsNotAsErrors>NU5104</WarningsNotAsErrors>
|
||||
|
||||
<!-- Package Properties -->
|
||||
@@ -32,7 +32,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="[1.7.4]" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -49,11 +49,6 @@ namespace MPF.ExecutionContexts.Redumper
|
||||
|
||||
#region Flag Values
|
||||
|
||||
/// <summary>
|
||||
/// Mode being run
|
||||
/// </summary>
|
||||
public string? ModeValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set of all command flags
|
||||
/// </summary>
|
||||
@@ -241,9 +236,9 @@ namespace MPF.ExecutionContexts.Redumper
|
||||
var parameters = new StringBuilder();
|
||||
|
||||
// Command Mode
|
||||
ModeValue ??= CommandStrings.NONE;
|
||||
if (ModeValue != CommandStrings.NONE)
|
||||
parameters.Append($"{ModeValue} ");
|
||||
BaseCommand ??= CommandStrings.NONE;
|
||||
if (BaseCommand != CommandStrings.NONE)
|
||||
parameters.Append($"{BaseCommand} ");
|
||||
|
||||
// Loop though and append all existing
|
||||
foreach (var kvp in _inputs)
|
||||
@@ -270,8 +265,8 @@ namespace MPF.ExecutionContexts.Redumper
|
||||
public override bool IsDumpingCommand()
|
||||
{
|
||||
// `dump` command does not provide hashes so will error out after dump if run via MPF
|
||||
return ModeValue == CommandStrings.NONE
|
||||
|| ModeValue == CommandStrings.Disc;
|
||||
return BaseCommand == CommandStrings.NONE
|
||||
|| BaseCommand == CommandStrings.Disc;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -291,25 +286,13 @@ namespace MPF.ExecutionContexts.Redumper
|
||||
int? driveSpeed,
|
||||
Dictionary<string, string?> options)
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
switch (MediaType)
|
||||
{
|
||||
case SabreTools.RedumpLib.Data.MediaType.CDROM:
|
||||
case SabreTools.RedumpLib.Data.MediaType.DVD:
|
||||
case SabreTools.RedumpLib.Data.MediaType.NintendoGameCubeGameDisc:
|
||||
case SabreTools.RedumpLib.Data.MediaType.NintendoWiiOpticalDisc:
|
||||
case SabreTools.RedumpLib.Data.MediaType.HDDVD:
|
||||
case SabreTools.RedumpLib.Data.MediaType.BluRay:
|
||||
case SabreTools.RedumpLib.Data.MediaType.NintendoWiiUOpticalDisc:
|
||||
ModeValue = CommandStrings.Disc;
|
||||
break;
|
||||
default:
|
||||
BaseCommand = null;
|
||||
return;
|
||||
}
|
||||
BaseCommand = CommandStrings.Disc;
|
||||
|
||||
this[FlagStrings.Drive] = true;
|
||||
(_inputs[FlagStrings.Drive] as StringInput)?.SetValue(drivePath ?? string.Empty);
|
||||
if (drivePath != null)
|
||||
{
|
||||
this[FlagStrings.Drive] = true;
|
||||
(_inputs[FlagStrings.Drive] as StringInput)?.SetValue(drivePath);
|
||||
}
|
||||
|
||||
if (driveSpeed != null && driveSpeed > 0)
|
||||
{
|
||||
@@ -330,20 +313,21 @@ namespace MPF.ExecutionContexts.Redumper
|
||||
}
|
||||
if (GetBooleanSetting(options, SettingConstants.EnableSkeleton, SettingConstants.EnableSkeletonDefault))
|
||||
{
|
||||
// Enable skeleton for CD dumps only by default
|
||||
// Enable skeleton for CD and DVD only, by default
|
||||
switch (MediaType)
|
||||
{
|
||||
case SabreTools.RedumpLib.Data.MediaType.CDROM:
|
||||
switch (RedumpSystem)
|
||||
{
|
||||
case SabreTools.RedumpLib.Data.RedumpSystem.SuperAudioCD:
|
||||
break;
|
||||
default:
|
||||
this[FlagStrings.Skeleton] = true;
|
||||
(_inputs[FlagStrings.Skeleton] as FlagInput)?.SetValue(true);
|
||||
break;
|
||||
}
|
||||
case SabreTools.RedumpLib.Data.MediaType.DVD:
|
||||
this[FlagStrings.Skeleton] = true;
|
||||
(_inputs[FlagStrings.Skeleton] as FlagInput)?.SetValue(true);
|
||||
break;
|
||||
|
||||
// If the type is unknown, also enable
|
||||
case null:
|
||||
this[FlagStrings.Skeleton] = true;
|
||||
(_inputs[FlagStrings.Skeleton] as FlagInput)?.SetValue(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -412,8 +396,6 @@ namespace MPF.ExecutionContexts.Redumper
|
||||
/// <inheritdoc/>
|
||||
protected override bool ValidateAndSetParameters(string? parameters)
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
// The string has to be valid by itself first
|
||||
if (string.IsNullOrEmpty(parameters))
|
||||
return false;
|
||||
@@ -422,7 +404,7 @@ namespace MPF.ExecutionContexts.Redumper
|
||||
string[] parts = SplitParameterString(parameters!);
|
||||
|
||||
// Setup the modes
|
||||
ModeValue = null;
|
||||
BaseCommand = null;
|
||||
|
||||
// All modes should be cached separately
|
||||
int index = 0;
|
||||
@@ -457,10 +439,10 @@ namespace MPF.ExecutionContexts.Redumper
|
||||
case CommandStrings.DebugFlip:
|
||||
case CommandStrings.DriveTest:
|
||||
// Only allow one mode per command
|
||||
if (ModeValue != null)
|
||||
if (BaseCommand != null)
|
||||
continue;
|
||||
|
||||
ModeValue = part;
|
||||
BaseCommand = part;
|
||||
break;
|
||||
|
||||
// Default is either a flag or an invalid mode
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
using LogCompression = MPF.Processors.LogCompression;
|
||||
using RedumperDriveType = MPF.ExecutionContexts.Redumper.DriveType;
|
||||
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
|
||||
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
|
||||
@@ -27,6 +28,29 @@ namespace MPF.Frontend.Test
|
||||
{
|
||||
string? actual = prog.LongName();
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
if (prog != null)
|
||||
{
|
||||
actual = EnumExtensions.GetLongName(prog);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, "Unknown")]
|
||||
[InlineData(LogCompression.DeflateDefault, "ZIP using Deflate (Level 5)")]
|
||||
[InlineData(LogCompression.DeflateMaximum, "ZIP using Deflate (Level 9)")]
|
||||
[InlineData(LogCompression.Zstd19, "ZIP using Zstd (Level 19)")]
|
||||
public void LongName_LogCompression(LogCompression? comp, string? expected)
|
||||
{
|
||||
string? actual = comp.LongName();
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
if (comp != null)
|
||||
{
|
||||
actual = EnumExtensions.GetLongName(comp);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -38,6 +62,12 @@ namespace MPF.Frontend.Test
|
||||
{
|
||||
string? actual = method.LongName();
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
actual = EnumExtensions.GetLongName(method);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -51,6 +81,12 @@ namespace MPF.Frontend.Test
|
||||
{
|
||||
string? actual = order.LongName();
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
if (order != null)
|
||||
{
|
||||
actual = EnumExtensions.GetLongName(order);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -67,6 +103,12 @@ namespace MPF.Frontend.Test
|
||||
{
|
||||
string? actual = type.LongName();
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
if (type != null)
|
||||
{
|
||||
actual = EnumExtensions.GetLongName(type);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace MPF.Frontend.Test
|
||||
[InlineData(MediaType.HDDVD, 24)]
|
||||
[InlineData(MediaType.BluRay, 16)]
|
||||
[InlineData(MediaType.NintendoWiiUOpticalDisc, 16)]
|
||||
[InlineData(MediaType.LaserDisc, 1)]
|
||||
[InlineData(null, 1)]
|
||||
[InlineData(MediaType.LaserDisc, 72)]
|
||||
[InlineData(null, 72)]
|
||||
public void GetAllowedDriveSpeedForMediaTypeTest(MediaType? mediaType, int maxExpected)
|
||||
{
|
||||
var actual = InterfaceConstants.GetSpeedsForMediaType(mediaType);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.14.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="[1.7.4]" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="1.24.0" />
|
||||
@@ -29,7 +29,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -683,9 +683,9 @@ namespace MPF.Frontend.Test.Tools
|
||||
[Theory]
|
||||
[InlineData(0, "Macrovision Protected Application [SafeDisc 0.00.000], SafeDisc 0.00.000, SafeDisc Lite")]
|
||||
[InlineData(1, "Macrovision Protected Application [SafeDisc 0.00.000 / SRV Tool APP], SafeDisc 0.00.000, SafeDisc Lite")]
|
||||
[InlineData(2, "Macrovision Security Driver, Macrovision Security Driver [SafeDisc 1.11.111], SafeDisc 0.00.000, SafeDisc 0.00.000-1.11.111, SafeDisc Lite")]
|
||||
[InlineData(3, "Macrovision Security Driver, Macrovision Security Driver [SafeDisc 1.11.111], SafeDisc 0.00.000, SafeDisc Lite")]
|
||||
[InlineData(4, "Macrovision Security Driver, Macrovision Security Driver [SafeDisc 1.11.111], SafeDisc Lite")]
|
||||
[InlineData(2, "SafeDisc 0.00.000, SafeDisc 0.00.000-1.11.111, SafeDisc 3+ (DVD), SafeDisc Lite")]
|
||||
[InlineData(3, "SafeDisc 0.00.000, SafeDisc 3+ (DVD), SafeDisc Lite")]
|
||||
[InlineData(4, "SafeDisc 3+ (DVD), SafeDisc Lite")]
|
||||
[InlineData(5, "Macrovision Security Driver, SafeDisc Lite")]
|
||||
[InlineData(6, "Macrovision Protection File, SafeDisc 2+, SafeDisc 3+ (DVD), SafeDisc Lite")]
|
||||
[InlineData(7, "SafeDisc 3+ (DVD)")]
|
||||
@@ -694,11 +694,11 @@ namespace MPF.Frontend.Test.Tools
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"Macrovision Protected Application [SafeDisc 0.00.000]",
|
||||
"Macrovision Protected Application [SafeDisc 0.00.000]",
|
||||
"Macrovision Protected Application [SafeDisc 0.00.000 / SRV Tool APP]",
|
||||
"SafeDisc 0.00.000-1.11.111",
|
||||
"SafeDisc 0.00.000",
|
||||
"Macrovision Security Driver [SafeDisc 1.11.111]",
|
||||
"Macrovision Security Driver 1.11.111 / SafeDisc 1.11.111",
|
||||
"Macrovision Security Driver",
|
||||
"SafeDisc Lite",
|
||||
"SafeDisc 3+ (DVD)",
|
||||
@@ -717,6 +717,23 @@ namespace MPF.Frontend.Test.Tools
|
||||
Assert.Equal(expected, sanitized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectsion_SafeDisc_MacrovisionSecurityDriver()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"Macrovision Protection File [Likely indicates either SafeDisc 1.45.011+ (CD) or CDS-300]",
|
||||
"Macrovision Security Driver 4.00.060 / SafeDisc 4.00.000-4.70.000",
|
||||
"SafeDisc 4.00.000-4.00.003",
|
||||
"SafeDisc 4.00.002, Macrovision Protected Application"
|
||||
];
|
||||
|
||||
string expected = "SafeDisc 4.00.002";
|
||||
|
||||
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
|
||||
Assert.Equal(expected, sanitized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SanitizeFoundProtections_SecuROM()
|
||||
{
|
||||
|
||||
@@ -356,6 +356,9 @@ namespace MPF.Frontend
|
||||
// If we're on an unsupported type, update the status accordingly
|
||||
return mediaType switch
|
||||
{
|
||||
// Null means it will be handled by the program
|
||||
null => ResultEventArgs.Success("Ready to dump"),
|
||||
|
||||
// Fully supported types
|
||||
MediaType.BluRay
|
||||
or MediaType.CDROM
|
||||
@@ -377,7 +380,7 @@ namespace MPF.Frontend
|
||||
MediaType.UMD => ResultEventArgs.Failure($"{mediaType.LongName()} supported for submission info parsing"),
|
||||
|
||||
// Specifically unknown type
|
||||
MediaType.NONE => ResultEventArgs.Failure($"Please select a valid media type"),
|
||||
MediaType.NONE => ResultEventArgs.Failure("Please select a valid media type"),
|
||||
|
||||
// Undumpable but recognized types
|
||||
_ => ResultEventArgs.Failure($"{mediaType.LongName()} media are not supported for dumping"),
|
||||
@@ -485,6 +488,8 @@ namespace MPF.Frontend
|
||||
|
||||
// Determine the media type from the processor
|
||||
MediaType? mediaType = _processor.DetermineMediaType(outputDirectory, outputFilename);
|
||||
if (mediaType == null)
|
||||
return ResultEventArgs.Failure("Could not determine the media type from output files...");
|
||||
|
||||
// Extract the information from the output files
|
||||
resultProgress.Report(ResultEventArgs.Success("Extracting output information from output files..."));
|
||||
@@ -498,14 +503,9 @@ namespace MPF.Frontend
|
||||
resultProgress,
|
||||
protectionProgress);
|
||||
if (submissionInfo == null)
|
||||
{
|
||||
resultProgress.Report(ResultEventArgs.Failure("There was an issue extracting information!"));
|
||||
return ResultEventArgs.Failure();
|
||||
}
|
||||
return ResultEventArgs.Failure("There was an issue extracting information!");
|
||||
else
|
||||
{
|
||||
resultProgress.Report(ResultEventArgs.Success("Extracting information complete!"));
|
||||
}
|
||||
|
||||
// Inject seed submission info data, if necessary
|
||||
if (seedInfo != null)
|
||||
@@ -585,7 +585,7 @@ namespace MPF.Frontend
|
||||
await Task.Run(() =>
|
||||
#endif
|
||||
{
|
||||
bool compressSuccess = _processor.CompressLogFiles(mediaType, outputDirectory, outputFilename, filenameSuffix, out string compressResult);
|
||||
bool compressSuccess = _processor.CompressLogFiles(mediaType, _options.LogCompression, outputDirectory, outputFilename, filenameSuffix, out string compressResult);
|
||||
if (compressSuccess)
|
||||
resultProgress.Report(ResultEventArgs.Success(compressResult));
|
||||
else
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.Reflection;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using LogCompression = MPF.Processors.LogCompression;
|
||||
using RedumperDriveType = MPF.ExecutionContexts.Redumper.DriveType;
|
||||
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
|
||||
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
|
||||
@@ -61,6 +62,14 @@ namespace MPF.Frontend
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string representation of the InternalProgram enum values
|
||||
/// </summary>
|
||||
/// <param name="prog">InternalProgram value to convert</param>
|
||||
/// <returns>String representing the value, if possible</returns>
|
||||
public static string LongName(this InternalProgram prog)
|
||||
=> ((InternalProgram?)prog).LongName();
|
||||
|
||||
/// <summary>
|
||||
/// Get the string representation of the InternalProgram enum values
|
||||
/// </summary>
|
||||
@@ -92,6 +101,31 @@ namespace MPF.Frontend
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string representation of the LogCompression enum values
|
||||
/// </summary>
|
||||
/// <param name="comp">LogCompression value to convert</param>
|
||||
/// <returns>String representing the value, if possible</returns>
|
||||
public static string LongName(this LogCompression comp)
|
||||
=> ((LogCompression?)comp).LongName();
|
||||
|
||||
/// <summary>
|
||||
/// Get the string representation of the LogCompression enum values
|
||||
/// </summary>
|
||||
/// <param name="comp">LogCompression value to convert</param>
|
||||
/// <returns>String representing the value, if possible</returns>
|
||||
public static string LongName(this LogCompression? comp)
|
||||
{
|
||||
return comp switch
|
||||
{
|
||||
LogCompression.DeflateDefault => "ZIP using Deflate (Level 5)",
|
||||
LogCompression.DeflateMaximum => "ZIP using Deflate (Level 9)",
|
||||
LogCompression.Zstd19 => "ZIP using Zstd (Level 19)",
|
||||
|
||||
_ => "Unknown",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string representation of the RedumperReadMethod enum values
|
||||
/// </summary>
|
||||
@@ -232,6 +266,28 @@ namespace MPF.Frontend
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the LogCompression enum value for a given string
|
||||
/// </summary>
|
||||
/// <param name="logCompression">String value to convert</param>
|
||||
/// <returns>LogCompression represented by teh string, if possible</returns>
|
||||
public static LogCompression ToLogCompression(this string? logCompression)
|
||||
{
|
||||
return (logCompression?.ToLowerInvariant()) switch
|
||||
{
|
||||
"deflate"
|
||||
or "deflatedefault"
|
||||
or "zip" => LogCompression.DeflateDefault,
|
||||
"deflatemaximum"
|
||||
or "max"
|
||||
or "maximum" => LogCompression.DeflateMaximum,
|
||||
"zstd"
|
||||
or "zstd19" => LogCompression.Zstd19,
|
||||
|
||||
_ => LogCompression.DeflateDefault,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the RedumperReadMethod enum value for a given string
|
||||
/// </summary>
|
||||
|
||||
38
MPF.Frontend/Features/ListCodesFeature.cs
Normal file
38
MPF.Frontend/Features/ListCodesFeature.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using SabreTools.CommandLine;
|
||||
|
||||
namespace MPF.Frontend.Features
|
||||
{
|
||||
public class ListCodesFeature : Feature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "listcodes";
|
||||
|
||||
private static readonly string[] _flags = ["lc", "listcodes"];
|
||||
|
||||
private const string _description = "List supported comment/content site codes";
|
||||
|
||||
#endregion
|
||||
|
||||
public ListCodesFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Execute()
|
||||
{
|
||||
Console.WriteLine("Supported Site Codes:");
|
||||
foreach (string siteCode in SabreTools.RedumpLib.Data.Extensions.ListSiteCodes())
|
||||
{
|
||||
Console.WriteLine(siteCode);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
}
|
||||
}
|
||||
38
MPF.Frontend/Features/ListMediaTypesFeature.cs
Normal file
38
MPF.Frontend/Features/ListMediaTypesFeature.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using SabreTools.CommandLine;
|
||||
|
||||
namespace MPF.Frontend.Features
|
||||
{
|
||||
public class ListMediaTypesFeature : Feature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "listmedia";
|
||||
|
||||
private static readonly string[] _flags = ["lm", "listmedia"];
|
||||
|
||||
private const string _description = "List supported media types";
|
||||
|
||||
#endregion
|
||||
|
||||
public ListMediaTypesFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Execute()
|
||||
{
|
||||
Console.WriteLine("Supported Media Types:");
|
||||
foreach (string mediaType in SabreTools.RedumpLib.Data.Extensions.ListMediaTypes())
|
||||
{
|
||||
Console.WriteLine(mediaType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
}
|
||||
}
|
||||
57
MPF.Frontend/Features/ListProgramsFeature.cs
Normal file
57
MPF.Frontend/Features/ListProgramsFeature.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.CommandLine;
|
||||
|
||||
namespace MPF.Frontend.Features
|
||||
{
|
||||
public class ListProgramsFeature : Feature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "listprograms";
|
||||
|
||||
private static readonly string[] _flags = ["lp", "listprograms"];
|
||||
|
||||
private const string _description = "List supported dumping program outputs";
|
||||
|
||||
#endregion
|
||||
|
||||
public ListProgramsFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Execute()
|
||||
{
|
||||
Console.WriteLine("Supported Programs:");
|
||||
foreach (string program in ListPrograms())
|
||||
{
|
||||
Console.WriteLine(program);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
|
||||
/// <summary>
|
||||
/// List all programs with their short usable names
|
||||
/// </summary>
|
||||
private static List<string> ListPrograms()
|
||||
{
|
||||
var programs = new List<string>();
|
||||
|
||||
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
|
||||
{
|
||||
if (((InternalProgram)val!) == InternalProgram.NONE)
|
||||
continue;
|
||||
|
||||
programs.Add($"{((InternalProgram?)val).ShortName()} - {((InternalProgram?)val).LongName()}");
|
||||
}
|
||||
|
||||
return programs;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
MPF.Frontend/Features/ListSystemsFeature.cs
Normal file
38
MPF.Frontend/Features/ListSystemsFeature.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using SabreTools.CommandLine;
|
||||
|
||||
namespace MPF.Frontend.Features
|
||||
{
|
||||
public class ListSystemsFeature : Feature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "listsystems";
|
||||
|
||||
private static readonly string[] _flags = ["ls", "listsystems"];
|
||||
|
||||
private const string _description = "List supported system types";
|
||||
|
||||
#endregion
|
||||
|
||||
public ListSystemsFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Execute()
|
||||
{
|
||||
Console.WriteLine("Supported Systems:");
|
||||
foreach (string system in SabreTools.RedumpLib.Data.Extensions.ListSystems())
|
||||
{
|
||||
Console.WriteLine(system);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
}
|
||||
}
|
||||
34
MPF.Frontend/Features/VersionFeature.cs
Normal file
34
MPF.Frontend/Features/VersionFeature.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using MPF.Frontend.Tools;
|
||||
using SabreTools.CommandLine;
|
||||
|
||||
namespace MPF.Frontend.Features
|
||||
{
|
||||
public class VersionFeature : Feature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "version";
|
||||
|
||||
private static readonly string[] _flags = ["version"];
|
||||
|
||||
private const string _description = "Display the program version";
|
||||
|
||||
#endregion
|
||||
|
||||
public VersionFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Execute()
|
||||
{
|
||||
Console.WriteLine(FrontendTool.GetCurrentVersion() ?? "Unknown version");
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => true;
|
||||
}
|
||||
}
|
||||
@@ -33,11 +33,6 @@ namespace MPF.Frontend
|
||||
/// </summary>
|
||||
public static List<int> BD => _speedValues.FindAll(s => s <= 16);
|
||||
|
||||
/// <summary>
|
||||
/// Set of accepted speeds for all other media
|
||||
/// </summary>
|
||||
public static List<int> Unknown => [1];
|
||||
|
||||
/// <summary>
|
||||
/// Get list of all drive speeds for a given MediaType
|
||||
/// </summary>
|
||||
@@ -55,7 +50,9 @@ namespace MPF.Frontend
|
||||
MediaType.HDDVD => HDDVD,
|
||||
MediaType.BluRay
|
||||
or MediaType.NintendoWiiUOpticalDisc => BD,
|
||||
_ => Unknown,
|
||||
|
||||
// Default to CD speed range
|
||||
_ => CD,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.4.0</VersionPrefix>
|
||||
<VersionPrefix>3.5.0</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
@@ -31,13 +31,14 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BinaryObjectScanner" Version="3.4.2" />
|
||||
<PackageReference Include="LibIRD" Version="1.0.0" />
|
||||
<PackageReference Include="BinaryObjectScanner" Version="[3.4.5]" />
|
||||
<PackageReference Include="LibIRD" Version="[1.0.0]" />
|
||||
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
|
||||
<PackageReference Include="Microsoft.Net.Http" Version="2.2.29" Condition="$(TargetFramework.StartsWith(`net452`))" />
|
||||
<PackageReference Include="MinThreadingBridge" Version="0.11.4" Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
|
||||
<PackageReference Include="SabreTools.CommandLine" Version="[1.3.2]" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="[1.7.4]" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using AaruSettings = MPF.ExecutionContexts.Aaru.SettingConstants;
|
||||
using DICSettings = MPF.ExecutionContexts.DiscImageCreator.SettingConstants;
|
||||
using LogCompression = MPF.Processors.LogCompression;
|
||||
using RedumperDriveType = MPF.ExecutionContexts.Redumper.DriveType;
|
||||
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
|
||||
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
|
||||
@@ -158,7 +159,7 @@ namespace MPF.Frontend
|
||||
get
|
||||
{
|
||||
var valueString = GetStringSetting(Settings, "DefaultSystem", RedumpSystem.IBMPCcompatible.LongName());
|
||||
var valueEnum = Extensions.ToRedumpSystem(valueString ?? string.Empty);
|
||||
var valueEnum = (valueString ?? string.Empty).ToRedumpSystem();
|
||||
return valueEnum;
|
||||
}
|
||||
set
|
||||
@@ -557,6 +558,22 @@ namespace MPF.Frontend
|
||||
set { Settings["CompressLogFiles"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compression type used during log compression
|
||||
/// </summary>
|
||||
public LogCompression LogCompression
|
||||
{
|
||||
get
|
||||
{
|
||||
var valueString = GetStringSetting(Settings, "LogCompression", LogCompression.DeflateMaximum.ToString());
|
||||
return valueString.ToLogCompression();
|
||||
}
|
||||
set
|
||||
{
|
||||
Settings["LogCompression"] = value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete unnecessary files to reduce space
|
||||
/// </summary>
|
||||
@@ -579,15 +596,6 @@ namespace MPF.Frontend
|
||||
|
||||
#region Skip Options
|
||||
|
||||
/// <summary>
|
||||
/// Skip detecting media type on disc scan
|
||||
/// </summary>
|
||||
public bool SkipMediaTypeDetection
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "SkipMediaTypeDetection", false); }
|
||||
set { Settings["SkipMediaTypeDetection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skip detecting known system on disc scan
|
||||
/// </summary>
|
||||
|
||||
@@ -32,129 +32,6 @@ namespace MPF.Frontend.Tools
|
||||
|
||||
#region Arguments
|
||||
|
||||
/// <summary>
|
||||
/// Process any standalone arguments for the program
|
||||
/// </summary>
|
||||
/// <returns>True if one of the arguments was processed, false otherwise</returns>
|
||||
public static bool? ProcessStandaloneArguments(string[] args)
|
||||
{
|
||||
// Help options
|
||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?" || args[0] == "--help")
|
||||
return null;
|
||||
|
||||
if (args[0] == "--version")
|
||||
{
|
||||
Console.WriteLine(FrontendTool.GetCurrentVersion() ?? "Unknown version");
|
||||
return true;
|
||||
}
|
||||
|
||||
// List options
|
||||
if (args[0] == "-lc" || args[0] == "--listcodes")
|
||||
{
|
||||
Console.WriteLine("Supported Site Codes:");
|
||||
foreach (string siteCode in Extensions.ListSiteCodes())
|
||||
{
|
||||
Console.WriteLine(siteCode);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-lm" || args[0] == "--listmedia")
|
||||
{
|
||||
Console.WriteLine("Supported Media Types:");
|
||||
foreach (string mediaType in Extensions.ListMediaTypes())
|
||||
{
|
||||
Console.WriteLine(mediaType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-lp" || args[0] == "--listprograms")
|
||||
{
|
||||
Console.WriteLine("Supported Programs:");
|
||||
foreach (string program in ListPrograms())
|
||||
{
|
||||
Console.WriteLine(program);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-ls" || args[0] == "--listsystems")
|
||||
{
|
||||
Console.WriteLine("Supported Systems:");
|
||||
foreach (string system in Extensions.ListSystems())
|
||||
{
|
||||
Console.WriteLine(system);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process common arguments for all functionality
|
||||
/// </summary>
|
||||
/// <returns>True if all arguments pass, false otherwise</returns>
|
||||
public static bool ProcessCommonArguments(string[] args, out RedumpSystem? system, out string? message)
|
||||
{
|
||||
// All other use requires at least 3 arguments
|
||||
if (args.Length < 2)
|
||||
{
|
||||
system = null;
|
||||
message = "Invalid number of arguments";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the RedumpSystem
|
||||
system = Extensions.ToRedumpSystem(args[0].Trim('"'));
|
||||
if (system == null)
|
||||
{
|
||||
message = $"{args[0]} is not a recognized system";
|
||||
return false;
|
||||
}
|
||||
|
||||
message = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process common arguments for all functionality
|
||||
/// </summary>
|
||||
/// <returns>True if all arguments pass, false otherwise</returns>
|
||||
public static bool ProcessCommonArguments(string[] args, out MediaType mediaType, out RedumpSystem? system, out string? message)
|
||||
{
|
||||
// All other use requires at least 3 arguments
|
||||
if (args.Length < 3)
|
||||
{
|
||||
mediaType = MediaType.NONE;
|
||||
system = null;
|
||||
message = "Invalid number of arguments";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the MediaType
|
||||
mediaType = ToMediaType(args[0].Trim('"'));
|
||||
if (mediaType == MediaType.NONE)
|
||||
{
|
||||
system = null;
|
||||
message = $"{args[0]} is not a recognized media type";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the RedumpSystem
|
||||
system = Extensions.ToRedumpSystem(args[1].Trim('"'));
|
||||
if (system == null)
|
||||
{
|
||||
message = $"{args[1]} is not a recognized system";
|
||||
return false;
|
||||
}
|
||||
|
||||
message = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the MediaType enum value for a given string
|
||||
/// </summary>
|
||||
@@ -298,24 +175,6 @@ namespace MPF.Frontend.Tools
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all programs with their short usable names
|
||||
/// </summary>
|
||||
private static List<string> ListPrograms()
|
||||
{
|
||||
var programs = new List<string>();
|
||||
|
||||
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
|
||||
{
|
||||
if (((InternalProgram)val!) == InternalProgram.NONE)
|
||||
continue;
|
||||
|
||||
programs.Add($"{((InternalProgram?)val).ShortName()} - {((InternalProgram?)val).LongName()}");
|
||||
}
|
||||
|
||||
return programs;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Configuration
|
||||
@@ -329,12 +188,9 @@ namespace MPF.Frontend.Tools
|
||||
if (string.IsNullOrEmpty(ConfigurationPath))
|
||||
return new Options();
|
||||
|
||||
// Ensure the file exists
|
||||
// If the file does not exist
|
||||
if (!File.Exists(ConfigurationPath) || new FileInfo(ConfigurationPath).Length == 0)
|
||||
{
|
||||
File.Create(ConfigurationPath).Dispose();
|
||||
return new Options();
|
||||
}
|
||||
|
||||
var serializer = JsonSerializer.Create();
|
||||
var stream = File.Open(ConfigurationPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
@@ -545,7 +545,7 @@ namespace MPF.Frontend.Tools
|
||||
|
||||
// Read the app.pkg header
|
||||
using var fileStream = new FileStream(appPkgPath, FileMode.Open, FileAccess.Read);
|
||||
var appPkgHeaderDeserializer = new SabreTools.Serialization.Deserializers.AppPkgHeader();
|
||||
var appPkgHeaderDeserializer = new SabreTools.Serialization.Readers.AppPkgHeader();
|
||||
var appPkgHeader = appPkgHeaderDeserializer.Deserialize(fileStream);
|
||||
|
||||
if (appPkgHeader != null)
|
||||
@@ -696,7 +696,7 @@ namespace MPF.Frontend.Tools
|
||||
|
||||
// Read the app_sc.pkg header
|
||||
using var fileStream = new FileStream(appPkgPath, FileMode.Open, FileAccess.Read);
|
||||
var appPkgHeaderDeserializer = new SabreTools.Serialization.Deserializers.AppPkgHeader();
|
||||
var appPkgHeaderDeserializer = new SabreTools.Serialization.Readers.AppPkgHeader();
|
||||
var appPkgHeader = appPkgHeaderDeserializer.Deserialize(fileStream);
|
||||
|
||||
if (appPkgHeader != null)
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace MPF.Frontend.Tools
|
||||
try
|
||||
{
|
||||
var antiModchip = new BinaryObjectScanner.Protection.PSXAntiModchip();
|
||||
foreach (string file in IOExtensions.SafeGetFiles(path!, "*", SearchOption.AllDirectories))
|
||||
foreach (string file in path!.SafeGetFiles("*", SearchOption.AllDirectories))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -375,9 +375,8 @@ namespace MPF.Frontend.Tools
|
||||
|
||||
// Best case scenario for SafeDisc 2+: A full SafeDisc version is found in a line starting with "Macrovision Protected Application".
|
||||
// All other SafeDisc detections can be safely scrubbed.
|
||||
// TODO: Scrub "Macrovision Protected Application, " from before the SafeDisc version.
|
||||
if (foundProtections.Exists(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)
|
||||
&& p.StartsWith("Macrovision Protected Application")
|
||||
&& p.Contains("Macrovision Protected Application")
|
||||
&& !p.Contains("SRV Tool APP")))
|
||||
{
|
||||
foundProtections = foundProtections.FindAll(p => !p.StartsWith("Macrovision Protection File"))
|
||||
@@ -391,11 +390,14 @@ namespace MPF.Frontend.Tools
|
||||
.FindAll(p => p != "SafeDisc 1/Lite")
|
||||
.FindAll(p => p != "SafeDisc 2+")
|
||||
.FindAll(p => p != "SafeDisc 3+ (DVD)");
|
||||
foundProtections = foundProtections.ConvertAll(p => p
|
||||
.Replace("Macrovision Protected Application, ", string.Empty)
|
||||
.Replace(", Macrovision Protected Application", string.Empty));
|
||||
}
|
||||
|
||||
// Next best case for SafeDisc 2+: A full SafeDisc version is found from the "SafeDisc SRV Tool APP".
|
||||
else if (foundProtections.Exists(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)
|
||||
&& p.StartsWith("Macrovision Protected Application")
|
||||
&& p.Contains("Macrovision Protected Application")
|
||||
&& p.Contains("SRV Tool APP")))
|
||||
{
|
||||
foundProtections = foundProtections.FindAll(p => !p.StartsWith("Macrovision Protection File"))
|
||||
@@ -439,7 +441,6 @@ namespace MPF.Frontend.Tools
|
||||
}
|
||||
|
||||
// Next best case for SafeDisc 1: A SafeDisc version range is found from "SECDRV.SYS".
|
||||
// TODO: Scrub "Macrovision Security Driver {Version}" from before the SafeDisc version.
|
||||
else if (foundProtections.Exists(p => p.StartsWith("Macrovision Security Driver")
|
||||
&& Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}-[1-2]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)
|
||||
|| Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}$")))
|
||||
@@ -451,10 +452,12 @@ namespace MPF.Frontend.Tools
|
||||
.FindAll(p => p != "SafeDisc")
|
||||
.FindAll(p => p != "SafeDisc 1")
|
||||
.FindAll(p => p != "SafeDisc 1/Lite");
|
||||
foundProtections = foundProtections.ConvertAll(p => p.StartsWith("Macrovision Security Driver") && p.Split('/').Length > 1
|
||||
? p.Split('/')[1].TrimStart()
|
||||
: p);
|
||||
}
|
||||
|
||||
// Next best case for SafeDisc 2+: A SafeDisc version range is found from "SECDRV.SYS".
|
||||
// TODO: Scrub "Macrovision Security Driver {Version}" from before the SafeDisc version.
|
||||
else if (foundProtections.Exists(p => p.StartsWith("Macrovision Security Driver")))
|
||||
{
|
||||
foundProtections = foundProtections.FindAll(p => !p.StartsWith("Macrovision Protection File"))
|
||||
@@ -466,6 +469,9 @@ namespace MPF.Frontend.Tools
|
||||
.FindAll(p => p != "SafeDisc 1/Lite")
|
||||
.FindAll(p => p != "SafeDisc 2+")
|
||||
.FindAll(p => p != "SafeDisc 3+ (DVD)");
|
||||
foundProtections = foundProtections.ConvertAll(p => p.StartsWith("Macrovision Security Driver") && p.Split('/').Length > 1
|
||||
? p.Split('/')[1].TrimStart()
|
||||
: p);
|
||||
}
|
||||
|
||||
// Only SafeDisc Lite is found.
|
||||
|
||||
@@ -167,9 +167,9 @@ namespace MPF.Frontend.Tools
|
||||
|
||||
// Login to Redump, if possible
|
||||
var wc = new RedumpClient();
|
||||
if (options.RedumpUsername != null && options.RedumpPassword != null)
|
||||
if (!string.IsNullOrEmpty(options.RedumpUsername) && !string.IsNullOrEmpty(options.RedumpPassword))
|
||||
{
|
||||
bool? loggedIn = await wc.Login(options.RedumpUsername, options.RedumpPassword);
|
||||
bool? loggedIn = await wc.Login(options.RedumpUsername!, options.RedumpPassword!);
|
||||
if (loggedIn == null)
|
||||
{
|
||||
resultProgress?.Report(ResultEventArgs.Failure("There was an unknown error connecting to Redump, skipping..."));
|
||||
|
||||
@@ -393,13 +393,13 @@ namespace MPF.Frontend.ViewModels
|
||||
/// Performs MPF.Check functionality
|
||||
/// </summary>
|
||||
/// <returns>An error message if failed, otherwise string.Empty/null</returns>
|
||||
public async Task<string?> CheckDump(ProcessUserInfoDelegate processUserInfo)
|
||||
public async Task<ResultEventArgs> CheckDump(ProcessUserInfoDelegate processUserInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(InputPath))
|
||||
return "Invalid Input path";
|
||||
return ResultEventArgs.Failure("Invalid Input path");
|
||||
|
||||
if (!File.Exists(InputPath!.Trim('"')))
|
||||
return "Input Path is not a valid file";
|
||||
return ResultEventArgs.Failure("Input Path is not a valid file");
|
||||
|
||||
// Disable UI while Check is running
|
||||
DisableUIElements();
|
||||
@@ -431,7 +431,7 @@ namespace MPF.Frontend.ViewModels
|
||||
if (cachedCanExecuteSelectionChanged)
|
||||
EnableEventHandlers();
|
||||
|
||||
return result.Message;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -674,7 +674,7 @@ namespace MPF.Frontend.ViewModels
|
||||
|
||||
// Set the selected index
|
||||
CurrentDrive = (index != -1 ? Drives[index] : Drives[0]);
|
||||
Status = "Valid drive found! Choose your Media Type";
|
||||
Status = "Valid drive found!";
|
||||
CopyProtectScanButtonEnabled = true;
|
||||
|
||||
// Get the current system type
|
||||
@@ -1213,14 +1213,8 @@ namespace MPF.Frontend.ViewModels
|
||||
if (defaultMediaType == MediaType.NONE)
|
||||
defaultMediaType = MediaType.CDROM;
|
||||
|
||||
// If we're skipping detection, set the default value
|
||||
if (Options.SkipMediaTypeDetection)
|
||||
{
|
||||
VerboseLogLn($"Media type detection disabled, defaulting to {defaultMediaType.LongName()}.");
|
||||
CurrentMediaType = defaultMediaType;
|
||||
}
|
||||
// If the drive is marked active, try to read from it
|
||||
else if (CurrentDrive.MarkedActive)
|
||||
if (CurrentDrive.MarkedActive)
|
||||
{
|
||||
VerboseLog($"Trying to detect media type for drive {CurrentDrive.Name} [{CurrentDrive.DriveFormat}] using size and filesystem.. ");
|
||||
MediaType? detectedMediaType = CurrentDrive.GetMediaType(CurrentSystem);
|
||||
@@ -1238,8 +1232,7 @@ namespace MPF.Frontend.ViewModels
|
||||
CurrentMediaType = detectedMediaType;
|
||||
}
|
||||
}
|
||||
|
||||
// All other cases, just use the default
|
||||
// Otherwise just use the default
|
||||
else
|
||||
{
|
||||
VerboseLogLn($"Drive marked as empty, defaulting to {defaultMediaType.LongName()}.");
|
||||
@@ -1366,6 +1359,10 @@ namespace MPF.Frontend.ViewModels
|
||||
/// </summary>
|
||||
public void EnsureMediaInformation()
|
||||
{
|
||||
// If the drive list is empty, ignore updates
|
||||
if (Drives.Count == 0)
|
||||
return;
|
||||
|
||||
// Get the current environment information
|
||||
_environment = DetermineEnvironment();
|
||||
|
||||
@@ -1616,7 +1613,7 @@ namespace MPF.Frontend.ViewModels
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drive.Name, "$SystemUpdate"))
|
||||
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "$SystemUpdate")).Length > 0
|
||||
&& Path.Combine(drive.Name, "$SystemUpdate").SafeGetFiles().Length > 0
|
||||
&& drive.TotalSize <= 500_000_000)
|
||||
{
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
@@ -1639,7 +1636,7 @@ namespace MPF.Frontend.ViewModels
|
||||
if (!File.Exists(catalogjs))
|
||||
return RedumpSystem.MicrosoftXboxOne;
|
||||
|
||||
SabreTools.Models.Xbox.Catalog? catalog = new SabreTools.Serialization.Deserializers.Catalog().Deserialize(catalogjs);
|
||||
SabreTools.Data.Models.Xbox.Catalog? catalog = new SabreTools.Serialization.Readers.Catalog().Deserialize(catalogjs);
|
||||
if (catalog != null && catalog.Version != null && catalog.Packages != null)
|
||||
{
|
||||
if (!double.TryParse(catalog.Version, out double version))
|
||||
@@ -1831,13 +1828,13 @@ namespace MPF.Frontend.ViewModels
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drive.Name, "AUDIO_TS"))
|
||||
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "AUDIO_TS")).Length > 0)
|
||||
&& Path.Combine(drive.Name, "AUDIO_TS").SafeGetFiles().Length > 0)
|
||||
{
|
||||
return RedumpSystem.DVDAudio;
|
||||
}
|
||||
|
||||
else if (Directory.Exists(Path.Combine(drive.Name, "VIDEO_TS"))
|
||||
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "VIDEO_TS")).Length > 0)
|
||||
&& Path.Combine(drive.Name, "VIDEO_TS").SafeGetFiles().Length > 0)
|
||||
{
|
||||
return RedumpSystem.DVDVideo;
|
||||
}
|
||||
@@ -1848,7 +1845,7 @@ namespace MPF.Frontend.ViewModels
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drive.Name, "HVDVD_TS"))
|
||||
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "HVDVD_TS")).Length > 0)
|
||||
&& Path.Combine(drive.Name, "HVDVD_TS").SafeGetFiles().Length > 0)
|
||||
{
|
||||
return RedumpSystem.HDDVDVideo;
|
||||
}
|
||||
@@ -1859,7 +1856,7 @@ namespace MPF.Frontend.ViewModels
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drive.Name, "PHOTO_CD"))
|
||||
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "PHOTO_CD")).Length > 0)
|
||||
&& Path.Combine(drive.Name, "PHOTO_CD").SafeGetFiles().Length > 0)
|
||||
{
|
||||
return RedumpSystem.PhotoCD;
|
||||
}
|
||||
@@ -1870,7 +1867,7 @@ namespace MPF.Frontend.ViewModels
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drive.Name, "VCD"))
|
||||
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "VCD")).Length > 0)
|
||||
&& Path.Combine(drive.Name, "VCD").SafeGetFiles().Length > 0)
|
||||
{
|
||||
return RedumpSystem.VideoCD;
|
||||
}
|
||||
@@ -2106,6 +2103,10 @@ namespace MPF.Frontend.ViewModels
|
||||
/// </summary>
|
||||
public void SetSupportedDriveSpeed()
|
||||
{
|
||||
// Skip trying to set speeds if no drives
|
||||
if (Drives.Count == 0)
|
||||
return;
|
||||
|
||||
// Set the drive speed list that's appropriate
|
||||
DriveSpeeds = InterfaceConstants.GetSpeedsForMediaType(CurrentMediaType);
|
||||
VerboseLogLn($"Supported media speeds: {string.Join(", ", [.. DriveSpeeds.ConvertAll(ds => ds.ToString())])}");
|
||||
@@ -2119,8 +2120,7 @@ namespace MPF.Frontend.ViewModels
|
||||
/// </summary>
|
||||
private bool ShouldEnableDumpingButton()
|
||||
{
|
||||
return Drives != null
|
||||
&& Drives.Count > 0
|
||||
return Drives.Count > 0
|
||||
&& CurrentSystem != null
|
||||
&& CurrentMediaType != null
|
||||
&& ProgramSupportsMedia();
|
||||
@@ -2248,6 +2248,9 @@ namespace MPF.Frontend.ViewModels
|
||||
resultProgress: resultProgress,
|
||||
protectionProgress: protectionProgress,
|
||||
processUserInfo: _processUserInfo);
|
||||
|
||||
if (!result)
|
||||
ErrorLogLn(result.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using MPF.Frontend.ComboBoxItems;
|
||||
using LogCompression = MPF.Processors.LogCompression;
|
||||
using RedumperDriveType = MPF.ExecutionContexts.Redumper.DriveType;
|
||||
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
|
||||
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
|
||||
@@ -50,6 +51,11 @@ namespace MPF.Frontend.ViewModels
|
||||
/// </summary>
|
||||
public static List<Element<InternalProgram>> InternalPrograms => PopulateInternalPrograms();
|
||||
|
||||
/// <summary>
|
||||
/// List of available log compression methods
|
||||
/// </summary>
|
||||
public static List<Element<LogCompression>> LogCompressions => PopulateLogCompressions();
|
||||
|
||||
/// <summary>
|
||||
/// Current list of supported Redumper read methods
|
||||
/// </summary>
|
||||
@@ -99,6 +105,16 @@ namespace MPF.Frontend.ViewModels
|
||||
return internalPrograms.ConvertAll(ip => new Element<InternalProgram>(ip));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a complete list of supported log compression methods
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static List<Element<LogCompression>> PopulateLogCompressions()
|
||||
{
|
||||
var logCompressions = new List<LogCompression> { LogCompression.DeflateDefault, LogCompression.DeflateMaximum, LogCompression.Zstd19 };
|
||||
return logCompressions.ConvertAll(lc => new Element<LogCompression>(lc));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a complete list of supported redumper drive read methods
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Processors.Test
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.14.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="[1.7.4]" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="1.24.0" />
|
||||
@@ -39,7 +39,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.Models.PIC;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using SabreTools.Data.Models.PIC;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace MPF.Processors.Test
|
||||
var processor = new Redumper(RedumpSystem.IBMPCcompatible);
|
||||
|
||||
var actual = processor.GetOutputFiles(MediaType.CDROM, outputDirectory, outputFilename);
|
||||
Assert.Equal(17, actual.Count);
|
||||
Assert.Equal(19, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -114,7 +114,7 @@ namespace MPF.Processors.Test
|
||||
var processor = new Redumper(RedumpSystem.IBMPCcompatible);
|
||||
|
||||
var actual = processor.GetOutputFiles(MediaType.DVD, outputDirectory, outputFilename);
|
||||
Assert.Equal(15, actual.Count);
|
||||
Assert.Equal(17, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -125,7 +125,7 @@ namespace MPF.Processors.Test
|
||||
var processor = new Redumper(RedumpSystem.IBMPCcompatible);
|
||||
|
||||
var actual = processor.GetOutputFiles(MediaType.HDDVD, outputDirectory, outputFilename);
|
||||
Assert.Equal(10, actual.Count);
|
||||
Assert.Equal(12, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -136,7 +136,7 @@ namespace MPF.Processors.Test
|
||||
var processor = new Redumper(RedumpSystem.IBMPCcompatible);
|
||||
|
||||
var actual = processor.GetOutputFiles(MediaType.BluRay, outputDirectory, outputFilename);
|
||||
Assert.Equal(10, actual.Count);
|
||||
Assert.Equal(12, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -161,7 +161,7 @@ namespace MPF.Processors.Test
|
||||
string outputFilename = string.Empty;
|
||||
var processor = new Redumper(RedumpSystem.IBMPCcompatible);
|
||||
var actual = processor.FoundAllFiles(MediaType.CDROM, outputDirectory, outputFilename);
|
||||
Assert.Equal(8, actual.Count);
|
||||
Assert.Equal(7, actual.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -5,8 +5,8 @@ using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml.Serialization;
|
||||
using SabreTools.Models.CueSheets;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.Data.Models.CueSheets;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Schemas;
|
||||
@@ -409,7 +409,7 @@ namespace MPF.Processors
|
||||
cueSheet.Files = [.. cueFiles];
|
||||
if (cueSheet != null && cueSheet != default)
|
||||
{
|
||||
var ms = new SabreTools.Serialization.Serializers.CueSheet().Serialize(cueSheet);
|
||||
var ms = new SabreTools.Serialization.Writers.CueSheet().SerializeStream(cueSheet);
|
||||
if (ms == null)
|
||||
return null;
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
using System.Text;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.Writers.Zip;
|
||||
#endif
|
||||
|
||||
namespace MPF.Processors
|
||||
{
|
||||
@@ -73,6 +77,7 @@ namespace MPF.Processors
|
||||
/// Compress log files to save space
|
||||
/// </summary>
|
||||
/// <param name="mediaType">Media type for controlling expected file sets</param>
|
||||
/// <param name="logCompression">Compression type to use for logs</param>
|
||||
/// <param name="outputDirectory">Output folder to use as the base path</param>
|
||||
/// <param name="outputFilename">Output filename to use as the base path</param>
|
||||
/// <param name="filenameSuffix">Output filename to use as the base path</param>
|
||||
@@ -80,12 +85,13 @@ namespace MPF.Processors
|
||||
/// <returns>True if the process succeeded, false otherwise</returns>
|
||||
/// <remarks>Assumes filename has an extension</remarks>
|
||||
public bool CompressLogFiles(MediaType? mediaType,
|
||||
LogCompression logCompression,
|
||||
string? outputDirectory,
|
||||
string outputFilename,
|
||||
string? filenameSuffix,
|
||||
out string status)
|
||||
{
|
||||
#if NET20 || NET35 || NET40
|
||||
#if NET20 || NET35 || NET40 || NET452
|
||||
status = "Log compression is not available for this framework version";
|
||||
return false;
|
||||
#else
|
||||
@@ -108,26 +114,46 @@ namespace MPF.Processors
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the file already exists, we want to delete the old one
|
||||
try
|
||||
// If the file already exists, change the archive name
|
||||
if (File.Exists(archiveName))
|
||||
{
|
||||
if (File.Exists(archiveName))
|
||||
File.Delete(archiveName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
status = "Could not delete old archive!";
|
||||
return false;
|
||||
string now = DateTime.Now.ToString("yyyyMMdd-HHmmss");
|
||||
archiveName = $"{basePath}_logs_{now}.zip";
|
||||
}
|
||||
|
||||
// Add the log files to the archive and delete the uncompressed file after
|
||||
ZipArchive? zf = null;
|
||||
bool disposed = false;
|
||||
try
|
||||
{
|
||||
zf = ZipFile.Open(archiveName, ZipArchiveMode.Create);
|
||||
zf = ZipArchive.Create();
|
||||
|
||||
_ = AddToArchive(zf, zippableFiles, outputDirectory, true);
|
||||
_ = AddToArchive(zf, generatedFiles, outputDirectory, false);
|
||||
List<string> successful = AddToArchive(zf, zippableFiles, outputDirectory);
|
||||
_ = AddToArchive(zf, generatedFiles, outputDirectory);
|
||||
|
||||
switch (logCompression)
|
||||
{
|
||||
case LogCompression.DeflateMaximum:
|
||||
zf.SaveTo(archiveName, new ZipWriterOptions(CompressionType.Deflate, CompressionLevel.BestCompression) { UseZip64 = true });
|
||||
break;
|
||||
case LogCompression.Zstd19:
|
||||
zf.SaveTo(archiveName, new ZipWriterOptions(CompressionType.ZStandard, (CompressionLevel)19) { UseZip64 = true });
|
||||
break;
|
||||
case LogCompression.DeflateDefault:
|
||||
default:
|
||||
zf.SaveTo(archiveName, new ZipWriterOptions(CompressionType.Deflate, CompressionLevel.Default) { UseZip64 = true });
|
||||
break;
|
||||
}
|
||||
|
||||
// Dispose the archive
|
||||
zf?.Dispose();
|
||||
disposed = true;
|
||||
|
||||
// Delete all successful files
|
||||
foreach (string file in successful)
|
||||
{
|
||||
try { File.Delete(file); } catch { }
|
||||
}
|
||||
|
||||
status = "Compression complete!";
|
||||
return true;
|
||||
@@ -139,7 +165,8 @@ namespace MPF.Processors
|
||||
}
|
||||
finally
|
||||
{
|
||||
zf?.Dispose();
|
||||
if (!disposed)
|
||||
zf?.Dispose();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -203,17 +230,17 @@ namespace MPF.Processors
|
||||
|
||||
// Check for the log file
|
||||
bool logArchiveExists = false;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
ZipArchive? logArchive = null;
|
||||
#endif
|
||||
if (File.Exists($"{basePath}_logs.zip"))
|
||||
{
|
||||
logArchiveExists = true;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
try
|
||||
{
|
||||
// Try to open the archive
|
||||
logArchive = ZipFile.OpenRead($"{basePath}_logs.zip");
|
||||
logArchive = ZipArchive.Open($"{basePath}_logs.zip");
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -241,7 +268,7 @@ namespace MPF.Processors
|
||||
continue;
|
||||
}
|
||||
|
||||
#if NET20 || NET35 || NET40
|
||||
#if NET20 || NET35 || NET40 || NET452
|
||||
// Assume the zipfile has the file in it
|
||||
continue;
|
||||
#else
|
||||
@@ -254,7 +281,7 @@ namespace MPF.Processors
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
// Close the log archive, if it exists
|
||||
logArchive?.Dispose();
|
||||
#endif
|
||||
@@ -358,29 +385,29 @@ namespace MPF.Processors
|
||||
}
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
/// <summary>
|
||||
/// Try to add a set of files to an existing archive
|
||||
/// </summary>
|
||||
/// <param name="archive">Archive to add the file to</param>
|
||||
/// <param name="files">Full path to a set of existing files</param>
|
||||
/// <param name="outputDirectory">Directory that the existing files live in</param>
|
||||
/// <param name="delete">Indicates if the files should be deleted after adding</param>
|
||||
/// <returns>True if all files were added successfully, false otherwise</returns>
|
||||
private static bool AddToArchive(ZipArchive archive, List<string> files, string? outputDirectory, bool delete)
|
||||
/// <returns>List of all files that were successfully added</returns>
|
||||
private static List<string> AddToArchive(ZipArchive archive, List<string> files, string? outputDirectory)
|
||||
{
|
||||
// An empty list means success
|
||||
if (files.Count == 0)
|
||||
return true;
|
||||
return [];
|
||||
|
||||
// Loop through and add all files
|
||||
bool allAdded = true;
|
||||
List<string> added = [];
|
||||
foreach (string file in files)
|
||||
{
|
||||
allAdded &= AddToArchive(archive, file, outputDirectory, delete);
|
||||
if (AddToArchive(archive, file, outputDirectory))
|
||||
added.Add(file);
|
||||
}
|
||||
|
||||
return allAdded;
|
||||
return added;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -389,9 +416,8 @@ namespace MPF.Processors
|
||||
/// <param name="archive">Archive to add the file to</param>
|
||||
/// <param name="file">Full path to an existing file</param>
|
||||
/// <param name="outputDirectory">Directory that the existing file lives in</param>
|
||||
/// <param name="delete">Indicates if the file should be deleted after adding</param>
|
||||
/// <returns>True if the file was added successfully, false otherwise</returns>
|
||||
private static bool AddToArchive(ZipArchive archive, string file, string? outputDirectory, bool delete)
|
||||
private static bool AddToArchive(ZipArchive archive, string file, string? outputDirectory)
|
||||
{
|
||||
// Check if the file exists
|
||||
if (!File.Exists(file))
|
||||
@@ -408,23 +434,13 @@ namespace MPF.Processors
|
||||
// Create and add the entry
|
||||
try
|
||||
{
|
||||
#if NETFRAMEWORK || NETCOREAPP3_1 || NET5_0
|
||||
archive.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal);
|
||||
#else
|
||||
archive.CreateEntryFromFile(file, entryName, CompressionLevel.SmallestSize);
|
||||
#endif
|
||||
archive.AddEntry(entryName, file);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to delete the file if requested
|
||||
if (delete)
|
||||
{
|
||||
try { File.Delete(file); } catch { }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace MPF.Processors
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"bca"),
|
||||
new($"{outputFilename}.iso", OutputFileFlags.Required),
|
||||
new([$"{outputFilename}.iso", $"{outputFilename}.part0.iso"], OutputFileFlags.Required),
|
||||
|
||||
new($"{outputFilename}-dumpinfo.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
using System.IO.Compression;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives.Zip;
|
||||
#endif
|
||||
|
||||
namespace MPF.Processors
|
||||
@@ -78,7 +78,7 @@ namespace MPF.Processors
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
/// <inheritdoc/>
|
||||
public override bool Exists(ZipArchive? archive)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
#endif
|
||||
using System.Text.RegularExpressions;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
namespace MPF.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the type of compression used for logs
|
||||
/// </summary>
|
||||
public enum LogCompression
|
||||
{
|
||||
/// <summary>
|
||||
/// PKZIP using DEFLATE level 5
|
||||
/// </summary>
|
||||
DeflateDefault,
|
||||
|
||||
/// <summary>
|
||||
/// PKZIP using DEFLATE level 9
|
||||
/// </summary>
|
||||
DeflateMaximum,
|
||||
|
||||
/// <summary>
|
||||
/// PKZIP using Zstd level 19
|
||||
/// </summary>
|
||||
Zstd19,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum for SecuROM scheme type
|
||||
/// </summary>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.4.0</VersionPrefix>
|
||||
<VersionPrefix>3.5.0</VersionPrefix>
|
||||
<WarningsNotAsErrors>NU5104</WarningsNotAsErrors>
|
||||
|
||||
<!-- Package Properties -->
|
||||
@@ -33,13 +33,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.7.5" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.7.2" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.9.6" />
|
||||
<PackageReference Include="System.IO.Compression" Version="4.3.0" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`))" />
|
||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
|
||||
<PackageReference Include="GrindCore.SharpCompress" Version="0.40.4-alpha" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="[1.5.1]" />
|
||||
<PackageReference Include="SabreTools.IO" Version="[1.7.6]" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="[1.7.4]" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="[2.0.2]" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.9" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
using System.IO.Compression;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using System.Linq;
|
||||
using SharpCompress.Archives.Zip;
|
||||
#endif
|
||||
|
||||
namespace MPF.Processors
|
||||
@@ -218,7 +218,7 @@ namespace MPF.Processors
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
/// <summary>
|
||||
/// Indicates if an output file exists in an archive
|
||||
/// </summary>
|
||||
@@ -238,7 +238,7 @@ namespace MPF.Processors
|
||||
try
|
||||
{
|
||||
// Check all entries on filename alone
|
||||
if (archive.Entries.Any(e => e.Name == filename))
|
||||
if (archive.Entries.Any(e => e.Key == filename))
|
||||
return true;
|
||||
}
|
||||
catch { }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ using System.Xml.Schema;
|
||||
using System.Xml.Serialization;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.Models.PIC;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using SabreTools.Data.Models.PIC;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Processors
|
||||
@@ -132,7 +132,7 @@ namespace MPF.Processors
|
||||
{
|
||||
try
|
||||
{
|
||||
return new SabreTools.Serialization.Deserializers.PIC().Deserialize(pic);
|
||||
return new SabreTools.Serialization.Readers.PIC().Deserialize(pic);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.ZStandard;
|
||||
#endif
|
||||
|
||||
namespace MPF.Processors
|
||||
{
|
||||
@@ -97,6 +99,12 @@ namespace MPF.Processors
|
||||
VolumeLabels = volLabels;
|
||||
}
|
||||
|
||||
// Pre-compress the skeleton and state using Zstandard
|
||||
if (File.Exists($"{basePath}.skeleton"))
|
||||
_ = CompressZstandard($"{basePath}.skeleton");
|
||||
if (File.Exists($"{basePath}.state"))
|
||||
_ = CompressZstandard($"{basePath}.state");
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (mediaType)
|
||||
{
|
||||
@@ -471,10 +479,13 @@ namespace MPF.Processors
|
||||
"pma"),
|
||||
new([$"{outputFilename}.scram", $"{outputFilename}.scrap"], OutputFileFlags.Required
|
||||
| OutputFileFlags.Deleteable),
|
||||
new($"{outputFilename}.state", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
// TODO: Required if Zstandard version doesn't exist
|
||||
new($"{outputFilename}.state", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"state"),
|
||||
new($"{outputFilename}.state.zst", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"state_zst"),
|
||||
new($"{outputFilename}.subcode", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
@@ -515,6 +526,9 @@ namespace MPF.Processors
|
||||
cdrom.Add(new($"{trackName}.skeleton", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
$"skeleton_{trackNumber}"));
|
||||
cdrom.Add(new($"{trackName}.skeleton.zst", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
$"skeleton_zst_{trackNumber}"));
|
||||
trackNumber++;
|
||||
}
|
||||
}
|
||||
@@ -526,6 +540,9 @@ namespace MPF.Processors
|
||||
cdrom.Add(new($"{outputFilename}.skeleton", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"skeleton"));
|
||||
cdrom.Add(new($"{outputFilename}.skeleton.zst", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"skeleton_zst"));
|
||||
}
|
||||
|
||||
return cdrom;
|
||||
@@ -570,6 +587,9 @@ namespace MPF.Processors
|
||||
new($"{outputFilename}.skeleton", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"skeleton"),
|
||||
new($"{outputFilename}.skeleton.zst", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"skeleton_zst"),
|
||||
new($"{outputFilename}.ss", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"ss"),
|
||||
@@ -580,10 +600,13 @@ namespace MPF.Processors
|
||||
new($"{outputFilename}.ssv2", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"ssv2"),
|
||||
new($"{outputFilename}.state", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
// TODO: Required if Zstandard version doesn't exist
|
||||
new($"{outputFilename}.state", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"state"),
|
||||
new($"{outputFilename}.state.zst", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"state_zst"),
|
||||
];
|
||||
|
||||
case MediaType.HDDVD: // TODO: Confirm that this information outputs
|
||||
@@ -618,10 +641,16 @@ namespace MPF.Processors
|
||||
new($"{outputFilename}.skeleton", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"skeleton"),
|
||||
new($"{outputFilename}.state", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
new($"{outputFilename}.skeleton.zst", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"skeleton_zst"),
|
||||
// TODO: Required if Zstandard version doesn't exist
|
||||
new($"{outputFilename}.state", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"state"),
|
||||
new($"{outputFilename}.state.zst", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"state_zst"),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -632,6 +661,57 @@ namespace MPF.Processors
|
||||
|
||||
#region Private Extra Methods
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to compress a file to Zstandard, removing the original on success
|
||||
/// </summary>
|
||||
/// <param name="file">Full path to an existing file</param>
|
||||
/// <returns>True if the compression was a success, false otherwise</returns>
|
||||
private static bool CompressZstandard(string file)
|
||||
{
|
||||
#if NET20 || NET35 || NET40 || NET452
|
||||
// Compression is not available for this framework version
|
||||
return false;
|
||||
#else
|
||||
// Ensure the file exists
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// Prepare the input and output streams
|
||||
using var ifs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using var ofs = File.Open($"{file}.zst", FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
using var zst = new ZStandardStream(ofs, CompressionMode.Compress, compressionLevel: 19, leaveOpen: true);
|
||||
|
||||
// Compress and write in blocks
|
||||
int read = 0;
|
||||
do
|
||||
{
|
||||
byte[] buffer = new byte[3 * 1024 * 1024];
|
||||
|
||||
read = ifs.Read(buffer, 0, buffer.Length);
|
||||
if (read == 0)
|
||||
break;
|
||||
|
||||
zst.Write(buffer, 0, read);
|
||||
zst.Flush();
|
||||
} while (read > 0);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Try to delete the incomplete
|
||||
try { File.Delete($"{file}.zst"); } catch { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to delete the file
|
||||
try { File.Delete(file); } catch { }
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get if the datfile exists in the log
|
||||
/// </summary>
|
||||
@@ -649,7 +729,7 @@ namespace MPF.Processors
|
||||
if (!string.IsNullOrEmpty(outputDirectory))
|
||||
basePath = Path.Combine(outputDirectory, basePath);
|
||||
|
||||
#if NET20 || NET35 || NET40
|
||||
#if NET20 || NET35 || NET40 || NET452
|
||||
// Assume the zipfile has the file in it
|
||||
return File.Exists($"{basePath}_logs.zip");
|
||||
#else
|
||||
@@ -660,14 +740,23 @@ namespace MPF.Processors
|
||||
try
|
||||
{
|
||||
// Try to open the archive
|
||||
using ZipArchive archive = ZipFile.OpenRead($"{basePath}_logs.zip");
|
||||
using ZipArchive archive = ZipArchive.Open($"{basePath}_logs.zip");
|
||||
|
||||
// Get the log entry and check it, if possible
|
||||
var entry = archive.GetEntry(outputFilename);
|
||||
if (entry == null)
|
||||
ZipArchiveEntry? logEntry = null;
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
if (entry.Key == outputFilename)
|
||||
{
|
||||
logEntry = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (logEntry == null)
|
||||
return false;
|
||||
|
||||
using var sr = new StreamReader(entry.Open());
|
||||
using var sr = new StreamReader(logEntry.OpenEntryStream());
|
||||
return GetDatfile(sr) != null;
|
||||
}
|
||||
catch
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
#endif
|
||||
using System.Text.RegularExpressions;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives.Zip;
|
||||
#endif
|
||||
|
||||
namespace MPF.Processors
|
||||
{
|
||||
@@ -52,7 +51,7 @@ namespace MPF.Processors
|
||||
// Ensure the directory exists
|
||||
if (!Directory.Exists(outputDirectory))
|
||||
return false;
|
||||
|
||||
|
||||
// Get list of all files in directory
|
||||
var directoryFiles = Directory.GetFiles(outputDirectory);
|
||||
foreach (string file in directoryFiles)
|
||||
@@ -64,7 +63,7 @@ namespace MPF.Processors
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
/// <inheritdoc/>
|
||||
public override bool Exists(ZipArchive? archive)
|
||||
{
|
||||
@@ -73,10 +72,12 @@ namespace MPF.Processors
|
||||
return false;
|
||||
|
||||
// Get list of all files in archive
|
||||
var archiveFiles = archive.Entries.Select(e => e.Name).ToList();
|
||||
foreach (string file in archiveFiles)
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
if (Array.Exists(Filenames, pattern => Regex.IsMatch(file, pattern)))
|
||||
if (entry.Key == null)
|
||||
continue;
|
||||
|
||||
if (Array.Exists(Filenames, pattern => Regex.IsMatch(entry.Key, pattern)))
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -92,7 +93,7 @@ namespace MPF.Processors
|
||||
return [];
|
||||
|
||||
List<string> paths = [];
|
||||
|
||||
|
||||
// Get list of all files in directory
|
||||
var directoryFiles = Directory.GetFiles(outputDirectory);
|
||||
foreach (string file in directoryFiles)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.Logiqx;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Windows.Data;
|
||||
using MPF.Frontend;
|
||||
using MPF.Frontend.ComboBoxItems;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using LogCompression = MPF.Processors.LogCompression;
|
||||
using RedumperDriveType = MPF.ExecutionContexts.Redumper.DriveType;
|
||||
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
|
||||
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
|
||||
@@ -18,6 +19,7 @@ namespace MPF.UI
|
||||
{
|
||||
DiscCategory discCategory => new Element<DiscCategory>(discCategory),
|
||||
InternalProgram internalProgram => new Element<InternalProgram>(internalProgram),
|
||||
LogCompression logCompression => new Element<LogCompression>(logCompression),
|
||||
MediaType mediaType => new Element<MediaType>(mediaType),
|
||||
RedumperReadMethod readMethod => new Element<RedumperReadMethod>(readMethod),
|
||||
RedumperSectorOrder sectorOrder => new Element<RedumperSectorOrder>(sectorOrder),
|
||||
@@ -40,6 +42,7 @@ namespace MPF.UI
|
||||
{
|
||||
Element<DiscCategory> dcElement => dcElement.Value,
|
||||
Element<InternalProgram> ipElement => ipElement.Value,
|
||||
Element<LogCompression> lcElement => lcElement.Value,
|
||||
Element<MediaType> mtElement => mtElement.Value,
|
||||
Element<RedumperReadMethod> rmElement => rmElement.Value,
|
||||
Element<RedumperSectorOrder> soElement => soElement.Value,
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
<VersionPrefix>3.4.0</VersionPrefix>
|
||||
<VersionPrefix>3.5.0</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<AssemblyName>MPF</AssemblyName>
|
||||
@@ -70,7 +70,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="[1.7.4]" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -158,8 +158,8 @@ namespace MPF.UI.Windows
|
||||
/// </summary>
|
||||
private async void OnCheckDumpClick(object sender, EventArgs e)
|
||||
{
|
||||
string? errorMessage = await CheckDumpViewModel.CheckDump(ShowMediaInformationWindow);
|
||||
if (string.IsNullOrEmpty(errorMessage))
|
||||
var result = await CheckDumpViewModel.CheckDump(ShowMediaInformationWindow);
|
||||
if (result)
|
||||
{
|
||||
bool? checkAgain = DisplayUserMessage("Check Complete", "The dump has been processed successfully! Would you like to check another dump?", 2, false);
|
||||
if (checkAgain == false)
|
||||
@@ -169,7 +169,8 @@ namespace MPF.UI.Windows
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayUserMessage("Check Failed", errorMessage!, 1, false);
|
||||
string message = result.Message.Length > 0 ? result.Message : "Please check all files exist and try again!";
|
||||
DisplayUserMessage("Check Failed", message, 1, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label x:Name="SystemMediaTypeLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
|
||||
<Label x:Name="SystemMediaTypeLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="System" />
|
||||
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="0" 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}">
|
||||
@@ -150,7 +150,8 @@
|
||||
</ComboBox>
|
||||
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" 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}" />
|
||||
IsEnabled="{Binding MediaTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}"
|
||||
Visibility="Hidden" />
|
||||
|
||||
<Label x:Name="OutputPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="Output Path"/>
|
||||
<TextBox x:Name="OutputPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace MPF.UI.Windows
|
||||
private ComboBox? MediaTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "MediaTypeComboBox");
|
||||
private Button? OutputPathBrowseButton => ItemHelper.FindChild<Button>(this, "OutputPathBrowseButton");
|
||||
private TextBox? OutputPathTextBox => ItemHelper.FindChild<TextBox>(this, "OutputPathTextBox");
|
||||
private Label? SystemMediaTypeLabel => ItemHelper.FindChild<Label>(this, "SystemMediaTypeLabel");
|
||||
private ComboBox? SystemTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "SystemTypeComboBox");
|
||||
|
||||
#endregion
|
||||
@@ -113,6 +114,9 @@ namespace MPF.UI.Windows
|
||||
// Set the UI color scheme according to the options
|
||||
ApplyTheme();
|
||||
|
||||
// Hide or show the media type box based on program
|
||||
SetMediaTypeVisibility();
|
||||
|
||||
// Check for updates, if necessary
|
||||
if (MainViewModel.Options.CheckForUpdatesOnStartup)
|
||||
CheckForUpdates(showIfSame: false);
|
||||
@@ -343,6 +347,37 @@ namespace MPF.UI.Windows
|
||||
optionsWindow.Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set media type combo box visibility based on current program
|
||||
/// </summary>
|
||||
public void SetMediaTypeVisibility()
|
||||
{
|
||||
// Only DiscImageCreator uses the media type box
|
||||
if (MainViewModel.CurrentProgram != InternalProgram.DiscImageCreator)
|
||||
{
|
||||
SystemMediaTypeLabel!.Content = "System";
|
||||
MediaTypeComboBox!.Visibility = Visibility.Hidden;
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are no media types defined
|
||||
if (MainViewModel.MediaTypes == null)
|
||||
{
|
||||
SystemMediaTypeLabel!.Content = "System";
|
||||
MediaTypeComboBox!.Visibility = Visibility.Hidden;
|
||||
return;
|
||||
}
|
||||
|
||||
// Only systems with more than one media type should show the box
|
||||
bool visible = MainViewModel.MediaTypes.Count > 1;
|
||||
SystemMediaTypeLabel!.Content = visible
|
||||
? "System/Media Type"
|
||||
: "System";
|
||||
MediaTypeComboBox!.Visibility = visible
|
||||
? Visibility.Visible
|
||||
: Visibility.Hidden;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the UI color scheme according to the options
|
||||
/// </summary>
|
||||
@@ -437,6 +472,9 @@ namespace MPF.UI.Windows
|
||||
|
||||
// Set the UI color scheme according to the options
|
||||
ApplyTheme();
|
||||
|
||||
// Hide or show the media type box based on program
|
||||
SetMediaTypeVisibility();
|
||||
}
|
||||
|
||||
#region Menu Bar
|
||||
@@ -530,7 +568,10 @@ namespace MPF.UI.Windows
|
||||
public void DumpingProgramComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
{
|
||||
MainViewModel.ChangeDumpingProgram();
|
||||
SetMediaTypeVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -587,7 +628,10 @@ namespace MPF.UI.Windows
|
||||
public void SystemTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
{
|
||||
MainViewModel.ChangeSystem();
|
||||
SetMediaTypeVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -95,22 +95,16 @@
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Detection">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox x:Name="SkipMediaTypeDetectionCheckBox" VerticalAlignment="Center" Content="Skip Type Detect"
|
||||
IsChecked="{Binding Options.SkipMediaTypeDetection}"
|
||||
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Skip System Detect"
|
||||
IsChecked="{Binding Options.SkipSystemDetection}"
|
||||
ToolTip="Disable trying to guess system (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="No Fixed Drives"
|
||||
IsChecked="{Binding Options.IgnoreFixedDrives}"
|
||||
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
|
||||
IsChecked="{Binding Options.IgnoreFixedDrives}"
|
||||
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
|
||||
/>
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
|
||||
<Label VerticalAlignment="Center" Content="Default System:" HorizontalAlignment="Right" />
|
||||
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left"
|
||||
@@ -197,7 +191,7 @@
|
||||
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
|
||||
<UniformGrid Columns="2" Rows="6">
|
||||
<UniformGrid Columns="2" Rows="7">
|
||||
<CheckBox VerticalAlignment="Center" Content="Show Disc Info"
|
||||
IsChecked="{Binding Options.PromptForDiscInformation}"
|
||||
ToolTip="Enable showing the media information output after dumping" Margin="0,4"
|
||||
@@ -249,6 +243,12 @@
|
||||
ToolTip="Compress output log files to reduce space" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label VerticalAlignment="Center" Content="Log Compression:" HorizontalAlignment="Right" />
|
||||
<ComboBox x:Name="LogCompressionComboBox" Height="22" Width="200" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding LogCompressions}" SelectedItem="{Binding Options.LogCompression, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
|
||||
Style="{DynamicResource CustomComboBoxStyle}" IsEnabled="{Binding Options.CompressLogFiles}"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Delete Unnecessary Files"
|
||||
IsChecked="{Binding Options.DeleteUnnecessaryFiles}"
|
||||
ToolTip="Delete unnecesary output files to reduce space" Margin="0,4"
|
||||
@@ -413,7 +413,11 @@
|
||||
IsChecked="{Binding Options.RedumperEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Refine Sector Mode Flag"
|
||||
IsChecked="{Binding Options.RedumperRefineSectorMode}"
|
||||
ToolTip="Enable the refine sector mode flag by default" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center" Width="200" HorizontalAlignment="Left"
|
||||
@@ -428,12 +432,6 @@
|
||||
ToolTipService.ShowOnDisabled="True"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Refine Sector Mode Flag"
|
||||
IsChecked="{Binding Options.RedumperRefineSectorMode}"
|
||||
ToolTip="Enable the refine sector mode flag by default" Margin="0,4"
|
||||
/>
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Non-Redump Options" Click="NonRedumpModeClicked"
|
||||
IsChecked="{Binding Options.RedumperNonRedumpMode}"
|
||||
ToolTip="Enable non-redump options" Margin="0,4"
|
||||
@@ -483,7 +481,8 @@
|
||||
IsEnabled="{Binding Options.RetrieveMatchInformation}" />
|
||||
|
||||
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Password" />
|
||||
<PasswordBox x:Name="RedumpPasswordBox" Height="22" HorizontalAlignment="Stretch" PasswordChar="*" />
|
||||
<PasswordBox x:Name="RedumpPasswordBox" Height="22" HorizontalAlignment="Stretch" PasswordChar="*"
|
||||
IsEnabled="{Binding Options.RetrieveMatchInformation}"/>
|
||||
|
||||
<Button x:Name="RedumpLoginTestButton" Height="22" Width="80" Content="Test Login"
|
||||
Style="{DynamicResource CustomButtonStyle}"
|
||||
|
||||
Reference in New Issue
Block a user