Add preliminary MPF.CLI

This commit is contained in:
Matt Nadareski
2024-06-24 10:37:05 -04:00
parent 1f92ff08d6
commit cf32f38c0e
9 changed files with 439 additions and 1 deletions

50
.github/workflows/build_cli.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: MPF CLI
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
project: [MPF.CLI]
runtime: [win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, osx-arm64]
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c Debug --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
- name: Archive build
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip ${{ matrix.project }}/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

View File

@@ -15,6 +15,7 @@
- Purple
- Separate themes into own namespace and files
- Seal all theme classes
- Add preliminary MPF.CLI
### 3.2.0 (2024-06-20)

25
MPF.CLI/ConsoleLogger.cs Normal file
View File

@@ -0,0 +1,25 @@
using System;
using BinaryObjectScanner;
using MPF.Frontend;
namespace MPF.CLI
{
public static class ConsoleLogger
{
/// <summary>
/// Simple process counter to write to console
/// </summary>
public static void ProgressUpdated(object? sender, ResultEventArgs value)
{
Console.WriteLine(value.Message);
}
/// <summary>
/// Simple process counter to write to console
/// </summary>
public static void ProgressUpdated(object? sender, ProtectionProgress value)
{
Console.WriteLine($"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}");
}
}
}

54
MPF.CLI/MPF.CLI.csproj Normal file
View File

@@ -0,0 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<OutputType>Exe</OutputType>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.2.0</VersionPrefix>
<!-- Package Properties -->
<Title>MPF CLI</Title>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>CLI frontend for various dumping programs</Description>
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<!-- Support All Frameworks -->
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.13" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
</Project>

167
MPF.CLI/Program.cs Normal file
View File

@@ -0,0 +1,167 @@
using System;
using System.IO;
using BinaryObjectScanner;
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
namespace MPF.CLI
{
public class Program
{
public static void Main(string[] args)
{
// Try processing the standalone arguments
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
if (standaloneProcessed != false)
{
if (standaloneProcessed == null)
DisplayHelp();
return;
}
// Check for the minimum number of arguments
if (args.Length < 5)
{
DisplayHelp("Not enough arguments have been provided, exiting...");
return;
}
// Try processing the common arguments
(bool success, MediaType mediaType, RedumpSystem? knownSystem, var error) = OptionsLoader.ProcessCommonArguments(args);
if (!success)
{
DisplayHelp(error);
return;
}
// Load options from the config file
var options = OptionsLoader.LoadFromConfig();
if (options.FirstRun)
{
// Application paths
options.AaruPath = "FILL ME IN";
options.DiscImageCreatorPath = "FILL ME IN";
options.RedumperPath = "FILL ME IN";
options.InternalProgram = InternalProgram.NONE;
// Reset first run
options.FirstRun = false;
OptionsLoader.SaveToConfig(options);
}
// Validate the internal program
switch (options.InternalProgram)
{
case InternalProgram.Aaru:
if (!File.Exists(options.AaruPath))
{
DisplayHelp("A path needs to be supplied for Aaru, exiting...");
return;
}
break;
case InternalProgram.DiscImageCreator:
if (!File.Exists(options.DiscImageCreatorPath))
{
DisplayHelp("A path needs to be supplied for DIC, exiting...");
return;
}
break;
case InternalProgram.Redumper:
if (!File.Exists(options.RedumperPath))
{
DisplayHelp("A path needs to be supplied for Redumper, exiting...");
return;
}
break;
default:
DisplayHelp($"{options.InternalProgram} is not a supported dumping program, exiting...");
break;
}
// Make new Progress objects
var resultProgress = new Progress<ResultEventArgs>();
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
// Validate the supplied credentials
#if NETFRAMEWORK
(bool? _, string? message) = RedumpWebClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty);
#else
(bool? _, string? message) = RedumpHttpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
if (!string.IsNullOrEmpty(message))
Console.WriteLine(message);
// Get the explicit output options
string path = args[2].Trim('"');
string speedStr = args[3].Trim('"');
if (!int.TryParse(speedStr, out int speed))
speed = 8; // Reasonable default for most media types
string filepath = args[4].Trim('"');
// TODO: Additional arguments get passed to dumping program?
// - Should it be an explicit option, like --params?
// - Should it be that everything after is treated like an addition to default?
// - Should it be that everything after is a replacement of default?
// - Two separate options for replace and add params?
// Now populate an environment
var drive = Drive.Create(null, path);
var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, options.InternalProgram, parameters: null);
string? paramStr = env.GetFullParameters(speed);
if (string.IsNullOrEmpty(paramStr))
{
DisplayHelp("No valid environment could be created, exiting...");
return;
}
env.SetExecutionContext(paramStr);
// Invoke the dumping program
#if NET40
var dumpResult = env.Run(resultProgress);
#else
var dumpResult = env.Run(resultProgress).GetAwaiter().GetResult();
#endif
Console.WriteLine(dumpResult.Message);
if (!dumpResult)
return;
// Finally, attempt to do the output dance
#if NET40
var verifyTask = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress);
verifyTask.Wait();
var verifyResult = verifyTask.Result;
#else
var verifyResult = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
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> <drivepath> <speed> </path/to/output.cue/iso>");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");
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();
}
}
}

View File

@@ -94,7 +94,7 @@ namespace MPF.Check
Console.WriteLine(error);
Console.WriteLine("Usage:");
Console.WriteLine("MPF.Check.exe <mediatype> <system> [options] </path/to/output.cue/iso> ...");
Console.WriteLine("MPF.Check <mediatype> <system> [options] </path/to/output.cue/iso> ...");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");

View File

@@ -38,6 +38,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Processors", "MPF.Proce
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.ExecutionContexts", "MPF.ExecutionContexts\MPF.ExecutionContexts.csproj", "{75609610-CE85-486E-8F42-9525A52B80BF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MPF.CLI", "MPF.CLI\MPF.CLI.csproj", "{A825771B-98AC-44A5-8076-C9AB3D469751}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -68,6 +70,10 @@ Global
{75609610-CE85-486E-8F42-9525A52B80BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75609610-CE85-486E-8F42-9525A52B80BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75609610-CE85-486E-8F42-9525A52B80BF}.Release|Any CPU.Build.0 = Release|Any CPU
{A825771B-98AC-44A5-8076-C9AB3D469751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A825771B-98AC-44A5-8076-C9AB3D469751}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A825771B-98AC-44A5-8076-C9AB3D469751}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A825771B-98AC-44A5-8076-C9AB3D469751}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -117,6 +117,45 @@ if [ $NO_BUILD = false ]; then
done
done
# Build CLI
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
# Output the current build
echo "===== Build CLI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
echo "Skipped due to invalid combination"
continue
fi
fi
# If we have Apple silicon but an unsupported framework
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [ $RUNTIME = "osx-arm64" ]; then
echo "Skipped due to no Apple Silicon support"
continue
fi
fi
# Only .NET 5 and above can publish to a single file
if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]; then
# Only include Debug if building all
if [ $USE_ALL = true ]; then
dotnet publish MPF.CLI/MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
fi
dotnet publish MPF.CLI/MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
else
# Only include Debug if building all
if [ $USE_ALL = true ]; then
dotnet publish MPF.CLI/MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
fi
dotnet publish MPF.CLI/MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
fi
done
done
# Build Check
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
@@ -199,6 +238,38 @@ if [ $NO_ARCHIVE = false ]; then
done
done
# Create CLI archives
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
# Output the current build
echo "===== Archive CLI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
echo "Skipped due to invalid combination"
continue
fi
fi
# If we have Apple silicon but an unsupported framework
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [ $RUNTIME = "osx-arm64" ]; then
echo "Skipped due to no Apple Silicon support"
continue
fi
fi
# Only include Debug if building all
if [ $USE_ALL = true ]; then
cd $BUILD_FOLDER/MPF.CLI/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
zip -r $BUILD_FOLDER/MPF.CLI_${FRAMEWORK}_${RUNTIME}_debug.zip .
fi
cd $BUILD_FOLDER/MPF.CLI/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
zip -r $BUILD_FOLDER/MPF.CLI_${FRAMEWORK}_${RUNTIME}_release.zip .
done
done
# Create Check archives
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do

View File

@@ -105,6 +105,42 @@ if (!$NO_BUILD.IsPresent) {
}
}
# Build CLI
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
foreach ($RUNTIME in $CHECK_RUNTIMES) {
# Output the current build
Write-Host "===== Build CLI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
Write-Host "Skipped due to invalid combination"
continue
}
# If we have Apple silicon but an unsupported framework
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
Write-Host "Skipped due to no Apple Silicon support"
continue
}
# Only .NET 5 and above can publish to a single file
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK) {
# Only include Debug if building all
if ($USE_ALL.IsPresent) {
dotnet publish MPF.CLI\MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
}
dotnet publish MPF.CLI\MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
}
else {
# Only include Debug if building all
if ($USE_ALL.IsPresent) {
dotnet publish MPF.CLI\MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
}
dotnet publish MPF.CLI\MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
}
}
}
# Build Check
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
foreach ($RUNTIME in $CHECK_RUNTIMES) {
@@ -183,6 +219,34 @@ if (!$NO_ARCHIVE.IsPresent) {
}
}
# Create CLI archives
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
foreach ($RUNTIME in $CHECK_RUNTIMES) {
# Output the current build
Write-Host "===== Archive CLI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
Write-Host "Skipped due to invalid combination"
continue
}
# If we have Apple silicon but an unsupported framework
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
Write-Host "Skipped due to no Apple Silicon support"
continue
}
# Only include Debug if building all
if ($USE_ALL.IsPresent) {
Set-Location -Path $BUILD_FOLDER\MPF.CLI\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
7z a -tzip $BUILD_FOLDER\MPF.CLI_${FRAMEWORK}_${RUNTIME}_debug.zip *
}
Set-Location -Path $BUILD_FOLDER\MPF.CLI\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
7z a -tzip $BUILD_FOLDER\MPF.CLI_${FRAMEWORK}_${RUNTIME}_release.zip *
}
}
# Create Check archives
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
foreach ($RUNTIME in $CHECK_RUNTIMES) {