From 2379e7ef5a13094501a82e1c4cc4495b80fb9f5a Mon Sep 17 00:00:00 2001
From: Deterous <138427222+Deterous@users.noreply.github.com>
Date: Tue, 19 Aug 2025 21:27:28 +0900
Subject: [PATCH] Verify command
---
.github/workflows/build.yml | 43 +++++++++++++++--
IRDKit/Program.cs | 95 ++++++++++++++++++++++++++++++++++++-
LibIRD/IRD.cs | 78 +++++++++++++++++++++++++++++-
LibIRD/LibIRD.csproj | 14 ++----
4 files changed, 214 insertions(+), 16 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d6326c5..439ee46 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -22,9 +22,6 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.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 Release -p:DebugType=None -p:DebugSymbols=false
@@ -34,3 +31,43 @@ jobs:
with:
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}
path: ${{ matrix.project }}/bin/Release/${{ matrix.framework }}/${{ matrix.runtime }}/publish/*
+
+ pack:
+ runs-on: windows-latest
+
+ strategy:
+ matrix:
+ project: [LibIRD]
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: 'true'
+
+ - name: Setup MSBuild
+ uses: microsoft/setup-msbuild@v2
+
+ - name: Setup NuGet
+ uses: NuGet/setup-nuget@v1
+ with:
+ nuget-version: 'latest'
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9.0.x
+
+ - name: Restore NuGet packages
+ run: nuget restore LibIRD/LibIRD.csproj
+
+ - name: Build
+ run: msbuild ${{ matrix.project }}/${{ matrix.project }}.csproj /p:Configuration=Release
+
+ - name: Pack
+ run: dotnet pack ${{ matrix.project }}/${{ matrix.project }}.csproj -c Release -o ./artifacts
+
+ - name: Upload nupkg
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.project }}
+ path: ./artifacts/*.nupkg
diff --git a/IRDKit/Program.cs b/IRDKit/Program.cs
index b76cd91..371eff0 100644
--- a/IRDKit/Program.cs
+++ b/IRDKit/Program.cs
@@ -113,6 +113,19 @@ namespace IRDKit
public bool Verbose { get; set; }
}
+ ///
+ /// IRD verify command
+ ///
+ [Verb("verify", HelpText = "Verify a folder using an IRD")]
+ public class VerifyOptions
+ {
+ [Value(0, Required = true, HelpText = "Path to IRD file to use for validation")]
+ public string IRDPath { get; set; }
+
+ [Value(1, Required = true, HelpText = "Folder path to validate")]
+ public string FolderPath { get; set; }
+ }
+
#endregion
#region Program
@@ -127,7 +140,7 @@ namespace IRDKit
Console.OutputEncoding = Encoding.UTF8;
// Parse arguments
- var result = Parser.Default.ParseArguments(args).WithParsed(Run);
+ var result = Parser.Default.ParseArguments(args).WithParsed(Run);
}
///
@@ -452,6 +465,60 @@ namespace IRDKit
break;
+ // Process options from a 'rename' command
+ case VerifyOptions opt:
+
+ string verifyIRDPath = opt.IRDPath;
+ string verifyFolderPath = opt.FolderPath;
+
+ // Validate required parameters
+ if (string.IsNullOrEmpty(verifyIRDPath))
+ {
+ Console.Error.WriteLine("Provide a IRD path to verify with");
+ return;
+ }
+ if (string.IsNullOrEmpty(verifyFolderPath))
+ {
+ Console.Error.WriteLine("Provide a folder path to verify");
+ return;
+ }
+
+ bool switched = false;
+ if (!File.Exists(verifyIRDPath))
+ {
+ // Check if arguments are switched
+ if (File.Exists(verifyFolderPath))
+ {
+ switched = true;
+ string temp = verifyIRDPath;
+ verifyIRDPath = verifyFolderPath;
+ verifyFolderPath = temp;
+ }
+ else
+ {
+ Console.Error.WriteLine("Provide a valid IRD file to verify with");
+ return;
+ }
+ }
+
+ if (!Directory.Exists(verifyFolderPath))
+ {
+ if (switched)
+ {
+ Console.Error.WriteLine("Provide a valid IRD file to verify with");
+ return;
+ }
+ else
+ {
+ Console.Error.WriteLine("Provide a valid folder path to verify");
+ return;
+ }
+ }
+
+ VerifyIRD(verifyIRDPath, verifyFolderPath);
+
+ break;
+
// Unknown command
default:
break;
@@ -772,6 +839,32 @@ namespace IRDKit
Console.WriteLine(printText.ToString());
}
+ ///
+ /// Verify a folder matches against an IRD
+ ///
+ /// IRD path to verify with
+ /// Folder containing files to verify against
+ public static void VerifyIRD(string irdPath, string folderPath)
+ {
+ // Get file paths and their hashes to verify with
+ var fileHashes = IRD.Read(irdPath).GetFileHashes();
+
+ // Check that all files and their hashes exist in the folder path
+ foreach (var kvp in fileHashes)
+ {
+ string filePath = Path.Combine(folderPath, kvp.Key);
+ if (!File.Exists(filePath))
+ {
+ Console.WriteLine($"{filePath} doesn't exist");
+ continue;
+ }
+ var hash = HashTool.GetFileHashArray(filePath, HashType.MD5);
+ if (!hash.SequenceEqual(kvp.Value))
+ Console.WriteLine($"{filePath}: {IRD.ByteArrayToHexString(hash)}");
+ }
+ Console.WriteLine("Verification complete!");
+ }
+
///
/// Creates an IRD file from an ISO file
///
diff --git a/LibIRD/IRD.cs b/LibIRD/IRD.cs
index 86d83f9..975f382 100644
--- a/LibIRD/IRD.cs
+++ b/LibIRD/IRD.cs
@@ -1733,7 +1733,83 @@ namespace LibIRD
}
}
-#endregion
+ #endregion
+
+ #region IRD Validation
+
+ ///
+ /// Returns dictionary of file paths and their offset
+ ///
+ /// CDReader object
+ public Dictionary GetFileHashes()
+ {
+ using MemoryStream headerStream = new MemoryStream(Header);
+ using GZipStream gzStream = new GZipStream(headerStream, CompressionMode.Decompress);
+ using MemoryStream isoStream = new MemoryStream();
+#if NETCOREAPP || NETSTANDARD || NET40_OR_GREATER
+ gzStream.CopyTo(isoStream);
+#else
+ byte[] buffer = new byte[SectorSize];
+ int bytesRead;
+ while ((bytesRead = gzStream.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ isoStream.Write(buffer, 0, bytesRead);
+ }
+#endif
+ isoStream.Position = 0;
+ using CDReader reader = new CDReader(isoStream);
+
+ // Get all file paths and their offsets
+ var files = new Dictionary();
+ DiscDirectoryInfo rootDir = reader.GetDirectoryInfo("\\");
+ GetFileOffsets(reader, rootDir, files);
+
+ // Get all paths at each offset
+ var offsetToHash = new Dictionary();
+ for (int i = 0; i < FileCount; i++)
+ offsetToHash[FileKeys[i]] = FileHashes[i];
+
+ // Return dictionary of paths and hashes
+ var result = new Dictionary();
+ foreach (KeyValuePair kvp in files)
+ {
+ if (offsetToHash.ContainsKey(kvp.Value))
+ result[kvp.Key] = offsetToHash[kvp.Value];
+ }
+ return result;
+ }
+
+ ///
+ /// Gets all file paths from the ISO header
+ ///
+ /// CDReader object
+ /// Path to look within
+ /// Dictionary of files and their offset
+ public void GetFileOffsets(CDReader reader, DiscDirectoryInfo path, Dictionary files)
+ {
+ foreach (DiscFileInfo fileInfo in path.GetFiles())
+ {
+ string filePath = fileInfo.FullName;
+ Range[] fileExtent = reader.PathToClusters(filePath);
+ if (fileExtent == null && fileExtent.Length == 0)
+ throw new IOException($"Unexpected file extents for {filePath}");
+ long offset = fileExtent[0].Offset;
+ for (int i = 1; i < fileExtent.Length; i++)
+ {
+ if (fileExtent[i] == null)
+ throw new IOException($"Unexpected file extents for {filePath}");
+ if (fileExtent[i].Offset < offset)
+ offset = fileExtent[i].Offset;
+ }
+ files[filePath] = offset;
+ }
+ foreach (DiscDirectoryInfo dirInfo in path.GetDirectories())
+ {
+ GetFileOffsets(reader, dirInfo, files);
+ }
+ }
+
+ #endregion
#region Helper Functions
diff --git a/LibIRD/LibIRD.csproj b/LibIRD/LibIRD.csproj
index 0eac5fd..aa3a380 100644
--- a/LibIRD/LibIRD.csproj
+++ b/LibIRD/LibIRD.csproj
@@ -2,12 +2,12 @@
- win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64
+ net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0
false
latest
true
true
- 0.9.4
+ 0.9.5
../nupkg
@@ -24,14 +24,6 @@
GPL-3.0-only
True
-
-
- net6.0;net7.0;net8.0;net9.0
-
-
-
- net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0
-
@@ -44,7 +36,7 @@
-
+