Support reparsing of MPF-processed outputs (#896)

* Have a go at extracting files from existing log

* no null output dir

* Extract log from archive if it is zipped during mediatype detection

* imports

* fix variable names

* fix null output dir

* Final fixes

* changelist

* assign null

* fix
This commit is contained in:
Deterous
2025-10-11 11:03:03 +09:00
committed by GitHub
parent 882243316c
commit c5e01b9578
8 changed files with 187 additions and 9 deletions

View File

@@ -4,6 +4,7 @@
- Move Zstd compression helper to base processor
- Add file merge method in CleanRip
- Implement file merging in CleanRip
- Support reparsing of MPF-processed outputs
### 3.5.0 (2025-10-10)

View File

@@ -59,11 +59,15 @@ namespace MPF.Frontend.Tools
List<string> missingFiles = processor.FoundAllFiles(mediaType, outputDirectory, outputFilename);
if (missingFiles.Count > 0)
{
resultProgress?.Report(ResultEventArgs.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
resultProgress?.Report(ResultEventArgs.Failure($"This may indicate an issue with the hardware or media, including unsupported devices.\nPlease see dumping program documentation for more details."));
return null;
resultProgress?.Report(ResultEventArgs.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}\nThis may indicate an issue with the hardware or media, including unsupported devices.\nPlease see dumping program documentation for more details."));
return null;
}
// Extract files from existing log archive, if it exists
#if NET462_OR_GREATER || NETCOREAPP
processor.ExtractFromLogs(mediaType, outputDirectory, outputFilename);
#endif
// Assemble a base path
string basePath = Path.GetFileNameWithoutExtension(outputFilename);
if (!string.IsNullOrEmpty(outputDirectory))

View File

@@ -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(7, actual.Count);
Assert.Equal(5, actual.Count);
}
[Fact]

View File

@@ -10,6 +10,10 @@ using SabreTools.Data.Models.Logiqx;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using Schemas;
#if NET462_OR_GREATER || NETCOREAPP
using System.Linq;
using SharpCompress.Archives.Zip;
#endif
#pragma warning disable CS0618 // Ignore "Type or member is obsolete"
#pragma warning disable IDE0059 // Unnecessary assignment of a value
@@ -38,6 +42,31 @@ namespace MPF.Processors
if (!string.IsNullOrEmpty(outputDirectory))
basePath = Path.Combine(outputDirectory, basePath);
// Extract sidecar from archive, if it is zipped
#if NET462_OR_GREATER || NETCOREAPP
if (File.Exists($"{basePath}_logs.zip"))
{
ZipArchive? logArchive = null;
try
{
logArchive = ZipArchive.Open($"{basePath}_logs.zip");
string sidecarFilename = $"{Path.GetFileNameWithoutExtension(outputFilename)}.cicm.xml";
var sidecarFile = logArchive.Entries.FirstOrDefault(e => e.Key == sidecarFilename);
if (sidecarFile != null && !sidecarFile.IsDirectory)
{
string sidecarPath = sidecarFilename;
if (!string.IsNullOrEmpty(outputDirectory))
sidecarPath = Path.Combine(outputDirectory, sidecarFilename);
using var entryStream = sidecarFile.OpenEntryStream();
using var fileStream = File.Create(sidecarPath);
entryStream.CopyTo(fileStream);
}
}
catch { }
logArchive?.Dispose();
}
#endif
// Deserialize the sidecar, if possible
var sidecar = GenerateSidecar($"{basePath}.cicm.xml");

View File

@@ -61,7 +61,7 @@ namespace MPF.Processors
/// <param name="redumpCompat">Determines if outputs are processed according to Redump specifications</param>
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, MediaType? mediaType, string basePath, bool redumpCompat);
// <summary>
/// <summary>
/// Generate a list of all output files generated
/// </summary>
/// <param name="mediaType">Media type for controlling expected file sets</param>
@@ -291,6 +291,59 @@ namespace MPF.Processors
return missingFiles;
}
/// <summary>
/// Extracts found files from a found archive if it exists
/// </summary>
/// <param name="mediaType">Media type for controlling expected file sets</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>
/// <remarks>Assumes filename has an extension</remarks>
#if NET462_OR_GREATER || NETCOREAPP
public void ExtractFromLogs(MediaType? mediaType, string? outputDirectory, string outputFilename)
{
// Assemble a base path
string basePath = Path.GetFileNameWithoutExtension(outputFilename);
if (!string.IsNullOrEmpty(outputDirectory))
basePath = Path.Combine(outputDirectory, basePath);
// Get the list of output files
var outputFiles = GetOutputFiles(mediaType, outputDirectory, outputFilename);
if (outputFiles.Count == 0)
return;
// Check for the log file
bool logArchiveExists = false;
ZipArchive? logArchive = null;
if (File.Exists($"{basePath}_logs.zip"))
{
logArchiveExists = true;
try
{
// Try to open the archive
logArchive = ZipArchive.Open($"{basePath}_logs.zip");
}
catch
{
logArchiveExists = false;
}
}
// If no archive exists, do no work
if (!logArchiveExists)
return;
// Extract all found output files from the archive
foreach (var outputFile in outputFiles)
{
outputFile.Extract(logArchive, outputDirectory ?? string.Empty);
}
// Close the log archive, if it exists
logArchive?.Dispose();
}
#endif
/// <summary>
/// Ensures that no potential output files have been created
/// </summary>

View File

@@ -8,6 +8,9 @@ using System.Text.RegularExpressions;
using SabreTools.Data.Models.Logiqx;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
#if NET462_OR_GREATER || NETCOREAPP
using SharpCompress.Archives.Zip;
#endif
/*
If there are no external programs, such as error checking, etc., DIC outputs
@@ -79,6 +82,31 @@ namespace MPF.Processors
if (!string.IsNullOrEmpty(outputDirectory))
basePath = Path.Combine(outputDirectory, basePath);
// Extract _disc.txt from archive, if it is zipped
#if NET462_OR_GREATER || NETCOREAPP
if (File.Exists($"{basePath}_logs.zip"))
{
ZipArchive? logArchive = null;
try
{
logArchive = ZipArchive.Open($"{basePath}_logs.zip");
string disctxtFilename = $"{Path.GetFileNameWithoutExtension(outputFilename)}_disc.txt";
var disctxtFile = logArchive.Entries.FirstOrDefault(e => e.Key == disctxtFilename);
if (disctxtFile != null && !disctxtFile.IsDirectory)
{
string disctxtPath = disctxtFilename;
if (!string.IsNullOrEmpty(outputDirectory))
disctxtPath = Path.Combine(outputDirectory, disctxtFilename);
using var entryStream = disctxtFile.OpenEntryStream();
using var fileStream = File.Create(disctxtPath);
entryStream.CopyTo(fileStream);
}
}
catch { }
logArchive?.Dispose();
}
#endif
// Get the comma-separated list of values
if (GetDiscType($"{basePath}_disc.txt", out var discType) && discType != null)
{

View File

@@ -248,6 +248,45 @@ namespace MPF.Processors
}
#endif
#if NET462_OR_GREATER || NETCOREAPP
/// <summary>
/// Extracts an output file from a zip archive
/// </summary>
/// <param name="archive">Zip archive to check in</param>
/// <param name="outputDirectory">Base directory to extract to</param>
/// <returns>True if file extracted, False otherwise</returns>
public virtual bool Extract(ZipArchive? archive, string outputDirectory)
{
// If the archive is invalid
if (archive == null)
return false;
foreach (string filename in Filenames)
{
// Check for invalid filenames
if (string.IsNullOrEmpty(filename))
continue;
try
{
// Check all entries on filename alone
var outputFile = archive.Entries.FirstOrDefault(e => e.Key == filename);
if (outputFile == null || outputFile.IsDirectory)
continue;
string outputPath = Path.Combine(outputDirectory, filename);
using var entryStream = outputFile.OpenEntryStream();
using var fileStream = File.Create(outputPath);
entryStream.CopyTo(fileStream);
}
catch { }
}
return false;
}
#endif
/// <summary>
/// Get all matching paths for the file
/// </summary>

View File

@@ -7,6 +7,7 @@ using SabreTools.Hashing;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
#if NET462_OR_GREATER || NETCOREAPP
using System.Linq;
using SharpCompress.Archives.Zip;
#endif
@@ -34,6 +35,31 @@ namespace MPF.Processors
if (!string.IsNullOrEmpty(outputDirectory))
basePath = Path.Combine(outputDirectory, basePath);
// Extract log from archive, if it is zipped
#if NET462_OR_GREATER || NETCOREAPP
if (File.Exists($"{basePath}_logs.zip"))
{
ZipArchive? logArchive = null;
try
{
logArchive = ZipArchive.Open($"{basePath}_logs.zip");
string logFilename = $"{Path.GetFileNameWithoutExtension(outputFilename)}.log";
var logFile = logArchive.Entries.FirstOrDefault(e => e.Key == logFilename);
if (logFile != null && !logFile.IsDirectory)
{
string logPath = logFilename;
if (!string.IsNullOrEmpty(outputDirectory))
logPath = Path.Combine(outputDirectory, logFilename);
using var entryStream = logFile.OpenEntryStream();
using var fileStream = File.Create(logPath);
entryStream.CopyTo(fileStream);
}
}
catch { }
logArchive?.Dispose();
}
#endif
// Use the log first, if it exists
if (GetDiscType($"{basePath}.log", out MediaType? mediaType))
return mediaType;
@@ -462,8 +488,7 @@ namespace MPF.Processors
"cdtext"),
new($"{outputFilename}.cue", OutputFileFlags.Required),
new($"{outputFilename}.flip", OutputFileFlags.None),
new($"{outputFilename}.fulltoc", OutputFileFlags.Required
| OutputFileFlags.Binary
new($"{outputFilename}.fulltoc", OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"fulltoc"),
new($"{outputFilename}.log", OutputFileFlags.Required
@@ -475,8 +500,7 @@ namespace MPF.Processors
new($"{outputFilename}.pma", OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"pma"),
new([$"{outputFilename}.scram", $"{outputFilename}.scrap"], OutputFileFlags.Required
| OutputFileFlags.Deleteable),
new([$"{outputFilename}.scram", $"{outputFilename}.scrap"], OutputFileFlags.Deleteable),
// TODO: Required if Zstandard version doesn't exist
new($"{outputFilename}.state", OutputFileFlags.Binary
| OutputFileFlags.Zippable,