Diff command for comparing IRDs

This commit is contained in:
Deterous
2024-01-31 16:56:35 +09:00
parent 1a74e8fed0
commit df9bb8c7f2
2 changed files with 182 additions and 5 deletions
+179 -2
View File
@@ -6,6 +6,7 @@ using SabreTools.RedumpLib.Web;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.IO.Hashing;
using System.Linq;
using System.Security.Cryptography;
@@ -15,6 +16,8 @@ namespace IRDKit
{
internal class Program
{
#region Options
/// <summary>
/// IRD Creation command
/// </summary>
@@ -62,6 +65,26 @@ namespace IRDKit
public bool Recurse { get; set; }
}
/// <summary>
/// IRD diff command
/// </summary>
[Verb("diff", HelpText = "Compare two IRDs and print their differences")]
public class DiffOptions
{
[Value(0, Required = true, HelpText = "Path to the first IRD to compare against")]
public string InPath1 { get; set; }
[Value(1, Required = true, HelpText = "Path to the second IRD file to compare")]
public string InPath2 { get; set; }
[Option('o', "output", HelpText = "Path to the text or json file to be created (will overwrite)")]
public string OutPath { get; set; }
}
#endregion
#region Program
/// <summary>
/// Parse command line arguments
/// </summary>
@@ -72,7 +95,7 @@ namespace IRDKit
Console.OutputEncoding = Encoding.UTF8;
// Parse arguments
var result = Parser.Default.ParseArguments<CreateOptions, InfoOptions>(args).WithParsed(Run);
var result = Parser.Default.ParseArguments<CreateOptions, InfoOptions, DiffOptions>(args).WithParsed(Run);
}
/// <summary>
@@ -151,12 +174,17 @@ namespace IRDKit
// Process options from an `info` command
case InfoOptions opt:
// Validate required parameter
ArgumentNullException.ThrowIfNull(opt.InPath);
// Clear the output file path if it exists
if (opt.OutPath != null && opt.OutPath != "")
File.Delete(opt.OutPath);
foreach (string filePath in opt.InPath)
{
// Validate path
ArgumentNullException.ThrowIfNull(filePath);
// If directory, search for all ISOs in current directory
if (Directory.Exists(filePath))
@@ -248,12 +276,37 @@ namespace IRDKit
break;
// Process options from a `diff` command
case DiffOptions opt:
// Validate required parameter
ArgumentNullException.ThrowIfNull(opt.InPath1);
ArgumentNullException.ThrowIfNull(opt.InPath2);
if (!File.Exists(opt.InPath1)) throw new ArgumentException($"{opt.InPath1} is not a valid file or directory");
if (!File.Exists(opt.InPath2)) throw new ArgumentException($"{opt.InPath2} is not a valid file or directory");
// Clear the output file path if it exists
if (opt.OutPath != null && opt.OutPath != "")
File.Delete(opt.OutPath);
// Compare the two IRDs
PrintDiff(opt.InPath1, opt.InPath2, opt.OutPath);
if (opt.OutPath != null && opt.OutPath != "")
Console.WriteLine($"Diff saved to {opt.OutPath}");
break;
// Unknown command
default:
break;
}
}
#endregion
#region Functionality
/// <summary>
/// Prints info about a file
/// </summary>
@@ -329,7 +382,7 @@ namespace IRDKit
// Write PS3_DISC.SFB info
try
{
using DiscUtils.Streams.SparseStream s = reader.OpenFile("PS3_DISC.SFB", FileMode.Open, FileAccess.Read);
using DiscUtils.Streams.SparseStream s = reader.OpenFile(Path.Combine($"{Path.DirectorySeparatorChar}", "PS3_DISC.SFB"), FileMode.Open, FileAccess.Read);
PS3_DiscSFB ps3_DiscSFB = new(s);
if (json)
{
@@ -405,6 +458,110 @@ namespace IRDKit
}
}
/// <summary>
/// Prints the differences between two IRD files
/// </summary>
/// <param name="irdPath1">First IRD path to compare against</param>
/// <param name="irdPath2">Second IRD path to compare against</param>
/// <param name="outPath">File to write comparison to, null if print to Console</param>
public static void PrintDiff(string irdPath1, string irdPath2, string outPath = null)
{
// Check they are different IRDs
if (Path.GetFullPath(irdPath1) == Path.GetFullPath(irdPath2))
{
Console.WriteLine("Provide two different IRDs for a diff");
return;
}
// Parse each IRD
IRD IRD1 = IRD.Read(irdPath1);
IRD IRD2 = IRD.Read(irdPath2);
// Build a formatted diff
StringBuilder printText = new();
if (IRD1.Version != IRD2.Version)
printText.AppendLine($"Version: {IRD1.Version} vs {IRD2.Version}");
if (IRD1.TitleID != IRD2.TitleID)
printText.AppendLine($"TitleID: {IRD1.TitleID} vs {IRD2.TitleID}");
if (IRD1.Title != IRD2.Title)
printText.AppendLine($"Title: {IRD1.Title} vs {IRD2.Title}");
if (IRD1.SystemVersion != IRD2.SystemVersion)
printText.AppendLine($"PUP Version: {IRD1.SystemVersion} vs {IRD2.SystemVersion}");
if (IRD1.DiscVersion != IRD2.DiscVersion)
printText.AppendLine($"Disc Version: {IRD1.DiscVersion} vs {IRD2.DiscVersion}");
if (IRD1.AppVersion != IRD2.AppVersion)
printText.AppendLine($"App Version: {IRD1.AppVersion} vs {IRD2.AppVersion}");
byte[] header1 = Decompress(IRD1.Header);
byte[] header2 = Decompress(IRD2.Header);
if (header1.Length != header2.Length)
printText.AppendLine($"Header Length: {header1.Length} vs {header2.Length}");
if (!header1.SequenceEqual(header2))
printText.AppendLine($"Header: Differs");
byte[] footer1 = Decompress(IRD1.Footer);
byte[] footer2 = Decompress(IRD2.Footer);
if (footer1.Length != footer2.Length)
printText.AppendLine($"Footer Length: {footer1.Length} vs {footer2.Length}");
if (!footer1.SequenceEqual(footer2))
printText.AppendLine($"Footer: Differs");
if (IRD1.RegionCount != IRD2.RegionCount)
printText.AppendLine($"Region Count: {IRD1.RegionCount} vs {IRD2.RegionCount}");
int regionCount = IRD2.RegionCount < IRD1.RegionCount ? IRD2.RegionCount : IRD1.RegionCount;
for (int i = 0; i < regionCount; i++)
{
if (!IRD1.RegionHashes[i].SequenceEqual(IRD2.RegionHashes[i]))
printText.AppendLine($"Region {i} Hash: {Convert.ToHexString(IRD1.RegionHashes[i])} vs {Convert.ToHexString(IRD2.RegionHashes[i])}");
}
if (IRD1.FileCount != IRD2.FileCount)
printText.AppendLine($"File Count: {IRD1.FileCount} vs {IRD2.FileCount}");
uint fileCount = IRD2.FileCount < IRD1.FileCount ? IRD2.FileCount : IRD1.FileCount;
for (int i = 0; i < fileCount; i++)
{
if (IRD1.FileKeys[i] != IRD2.FileKeys[i])
printText.AppendLine($"File {i} Offset: {IRD1.FileKeys[i]} vs {IRD2.FileKeys[i]}");
if (!IRD1.FileHashes[i].SequenceEqual(IRD2.FileHashes[i]))
printText.AppendLine($"File {i} Hash: {Convert.ToHexString(IRD1.FileHashes[i])} vs {Convert.ToHexString(IRD2.FileHashes[i])}");
}
if (IRD1.ExtraConfig != IRD2.ExtraConfig)
printText.AppendLine($"Extra Config: {IRD1.ExtraConfig:X4} vs {IRD2.ExtraConfig:X4}");
if (IRD1.Attachments != IRD2.Attachments)
printText.AppendLine($"Attachments: {IRD1.Attachments:X4} vs {IRD2.Attachments:X4}");
if (IRD1.UID != IRD2.UID)
printText.AppendLine($"Unique ID: {IRD1.UID:X8} vs {IRD2.UID:X8}");
if (!IRD1.Data1Key.SequenceEqual(IRD2.Data1Key))
printText.AppendLine($"Data 1 Key: {Convert.ToHexString(IRD1.Data1Key)} vs {Convert.ToHexString(IRD2.Data1Key)}");
if (!IRD1.Data2Key.SequenceEqual(IRD2.Data2Key))
printText.AppendLine($"Data 2 Key: {Convert.ToHexString(IRD1.Data2Key)} vs {Convert.ToHexString(IRD2.Data2Key)}");
if (!IRD1.PIC.SequenceEqual(IRD2.PIC))
printText.AppendLine($"PIC: {Convert.ToHexString(IRD1.PIC)} vs {Convert.ToHexString(IRD2.PIC)}");
if (outPath != null)
File.AppendAllText(outPath, printText.ToString());
else
Console.WriteLine(printText.ToString());
}
/// <summary>
/// Creates an IRD file from an ISO file
/// </summary>
@@ -615,5 +772,25 @@ namespace IRDKit
ird.Print();
return irdPath;
}
#endregion
#region Helper Functions
/// <summary>
/// Decompress a gzipped byte array
/// </summary>
/// <param name="data">Gzipped byte array</param>
/// <returns>Un-gzipped byte array</returns>
static byte[] Decompress(byte[] data)
{
using var compressedStream = new MemoryStream(data);
using var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress);
using var resultStream = new MemoryStream();
zipStream.CopyTo(resultStream);
return resultStream.ToArray();
}
#endregion
}
}