17 Commits
13 changed files with 385 additions and 317 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "DiscUtils"] [submodule "DiscUtils"]
path = DiscUtils path = LibIRD/DiscUtils
url = https://github.com/Deterous/DiscUtils url = https://github.com/Deterous/DiscUtils
Submodule DiscUtils deleted from 299030c20b
+11 -7
View File
@@ -5,11 +5,13 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetName>irdkit</TargetName> <TargetName>irdkit</TargetName>
<AssemblyName>irdkit</AssemblyName> <AssemblyName>irdkit</AssemblyName>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks> <TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<Version>0.6.1</Version> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>0.9.1</Version>
<!-- Package Properties --> <!-- Package Properties -->
<Authors>Deterous</Authors> <Authors>Deterous</Authors>
@@ -27,11 +29,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\LibIRD\LibIRD.csproj" PrivateAssets="All" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Core\DiscUtils.Core.csproj" PrivateAssets="All" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Iso9660\DiscUtils.Iso9660.csproj" PrivateAssets="All" />
<PackageReference Include="CommandLineParser" Version="2.9.1" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.3" /> <PackageReference Include="SabreTools.Hashing" Version="1.1.4" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LibIRD\LibIRD.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+54 -53
View File
@@ -1,17 +1,14 @@
using CommandLine; using System;
using DiscUtils;
using DiscUtils.Iso9660;
using LibIRD;
using SabreTools.RedumpLib.Web;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.IO.Hashing;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
using CommandLine;
using LibIRD;
using SabreTools.Hashing;
using SabreTools.RedumpLib.Web;
namespace IRDKit namespace IRDKit
{ {
@@ -100,6 +97,9 @@ namespace IRDKit
[Option('s', "serial", HelpText = "Appends disc serial to new IRD filename")] [Option('s', "serial", HelpText = "Appends disc serial to new IRD filename")]
public bool Serial { get; set; } public bool Serial { get; set; }
[Option('e', "version", HelpText = "Appends disc version to new IRD filename")]
public bool Ver { get; set; }
[Option('c', "crc", HelpText = "Appends ISO CRC to new IRD filename")] [Option('c', "crc", HelpText = "Appends ISO CRC to new IRD filename")]
public bool CRC { get; set; } public bool CRC { get; set; }
@@ -295,7 +295,7 @@ namespace IRDKit
{ {
PrintISO(file, opt.Json, file.Equals(lastISO), opt.OutPath); PrintISO(file, opt.Json, file.Equals(lastISO), opt.OutPath);
} }
catch (InvalidFileSystemException) catch (IOException)
{ {
// Not a valid ISO file despite extension, assume file is an IRD // Not a valid ISO file despite extension, assume file is an IRD
if (!opt.Json) if (!opt.Json)
@@ -417,7 +417,7 @@ namespace IRDKit
{ {
try try
{ {
RenameIRD(file, datfile, serial: opt.Serial, crc: opt.CRC, verbose: opt.Verbose); RenameIRD(file, datfile, serial: opt.Serial, ver: opt.Ver, crc: opt.CRC, verbose: opt.Verbose);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -438,7 +438,7 @@ namespace IRDKit
// Rename provided IRD path // Rename provided IRD path
try try
{ {
RenameIRD(irdPath, datfile, serial: opt.Serial, crc: opt.CRC, verbose: opt.Verbose); RenameIRD(irdPath, datfile, serial: opt.Serial, ver: opt.Ver, crc: opt.CRC, verbose: opt.Verbose);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -476,7 +476,7 @@ namespace IRDKit
PrintISO(inPath, json, single, outPath); PrintISO(inPath, json, single, outPath);
return; return;
} }
catch (InvalidFileSystemException) catch (IOException)
{ {
// Not a valid ISO file despite extension, try open as IRD // Not a valid ISO file despite extension, try open as IRD
} }
@@ -526,18 +526,18 @@ namespace IRDKit
// Open ISO file for reading // Open ISO file for reading
using FileStream fs = new(isoPath, FileMode.Open, FileAccess.Read); using FileStream fs = new(isoPath, FileMode.Open, FileAccess.Read);
// Validate ISO file stream // Validate ISO file stream
if (fs == null || !CDReader.Detect(fs)) if (fs == null || !LibIRD.DiscUtils.Iso9660.CDReader.Detect(fs))
{ {
Console.Error.WriteLine($"{isoPath} is not a valid ISO file"); Console.Error.WriteLine($"{isoPath} is not a valid ISO file");
return; return;
} }
// Create new ISO reader // Create new ISO reader
using CDReader reader = new(fs, true, true); using LibIRD.DiscUtils.Iso9660.CDReader reader = new(fs);
// Write PS3_DISC.SFB info // Write PS3_DISC.SFB info
try try
{ {
using DiscUtils.Streams.SparseStream s = reader.OpenFile("\\PS3_DISC.SFB", FileMode.Open, FileAccess.Read); using LibIRD.DiscUtils.Streams.SparseStream s = reader.OpenFile("\\PS3_DISC.SFB", FileMode.Open, FileAccess.Read);
PS3_DiscSFB ps3_DiscSFB = new(s); PS3_DiscSFB ps3_DiscSFB = new(s);
if (json) if (json)
{ {
@@ -574,7 +574,7 @@ namespace IRDKit
// Write PARAM.SFO info // Write PARAM.SFO info
try try
{ {
using DiscUtils.Streams.SparseStream s = reader.OpenFile("\\PS3_GAME\\PARAM.SFO", FileMode.Open, FileAccess.Read); using LibIRD.DiscUtils.Streams.SparseStream s = reader.OpenFile("\\PS3_GAME\\PARAM.SFO", FileMode.Open, FileAccess.Read);
ParamSFO paramSFO = new(s); ParamSFO paramSFO = new(s);
if (json) if (json)
{ {
@@ -706,7 +706,7 @@ namespace IRDKit
for (int i = 0; i < regionCount; i++) for (int i = 0; i < regionCount; i++)
{ {
if (!IRD1.RegionHashes[i].SequenceEqual(IRD2.RegionHashes[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])}"); printText.AppendLine($"Region {i} Hash: {LibIRD.IRD.ByteArrayToHexString(IRD1.RegionHashes[i])} vs {LibIRD.IRD.ByteArrayToHexString(IRD2.RegionHashes[i])}");
} }
// Print the difference in number of files, if not 0 // Print the difference in number of files, if not 0
@@ -722,7 +722,7 @@ namespace IRDKit
if (j == -1) if (j == -1)
missingOffsets2.Add(IRD1.FileKeys[i]); missingOffsets2.Add(IRD1.FileKeys[i]);
if (j != -1 && !IRD1.FileHashes[i].SequenceEqual(IRD2.FileHashes[j])) if (j != -1 && !IRD1.FileHashes[i].SequenceEqual(IRD2.FileHashes[j]))
printText.AppendLine($"File Hash at Offset {IRD1.FileKeys[i]}: {Convert.ToHexString(IRD1.FileHashes[i])} vs {Convert.ToHexString(IRD2.FileHashes[j])}"); printText.AppendLine($"File Hash at Offset {IRD1.FileKeys[i]}: {LibIRD.IRD.ByteArrayToHexString(IRD1.FileHashes[i])} vs {LibIRD.IRD.ByteArrayToHexString(IRD2.FileHashes[j])}");
} }
for (int i = 0; i < IRD2.FileKeys.Length; i++) for (int i = 0; i < IRD2.FileKeys.Length; i++)
{ {
@@ -734,7 +734,7 @@ namespace IRDKit
if (missingOffsets1.Count > 0) if (missingOffsets1.Count > 0)
printText.AppendLine($"File Offsets not Present in {irdPath1}: {string.Join(", ", missingOffsets1)}"); printText.AppendLine($"File Offsets not Present in {irdPath1}: {string.Join(", ", missingOffsets1)}");
if (missingOffsets2.Count > 0) if (missingOffsets2.Count > 0)
printText.AppendLine($"File Offsets not Present in {irdPath2}: {string.Join(", ", missingOffsets2)}"); printText.AppendLine($"File Offsets not Present in {irdPath2}: {string.Join(", ", missingOffsets2)}");
// Print any extra config data difference // Print any extra config data difference
if (IRD1.ExtraConfig != IRD2.ExtraConfig) if (IRD1.ExtraConfig != IRD2.ExtraConfig)
@@ -750,15 +750,15 @@ namespace IRDKit
// Print any data 1 key difference // Print any data 1 key difference
if (!IRD1.Data1Key.SequenceEqual(IRD2.Data1Key)) if (!IRD1.Data1Key.SequenceEqual(IRD2.Data1Key))
printText.AppendLine($"Data 1 Key: {Convert.ToHexString(IRD1.Data1Key)} vs {Convert.ToHexString(IRD2.Data1Key)}"); printText.AppendLine($"Data 1 Key: {LibIRD.IRD.ByteArrayToHexString(IRD1.Data1Key)} vs {LibIRD.IRD.ByteArrayToHexString(IRD2.Data1Key)}");
// Print any data 2 key difference // Print any data 2 key difference
if (!IRD1.Data2Key.SequenceEqual(IRD2.Data2Key)) if (!IRD1.Data2Key.SequenceEqual(IRD2.Data2Key))
printText.AppendLine($"Data 2 Key: {Convert.ToHexString(IRD1.Data2Key)} vs {Convert.ToHexString(IRD2.Data2Key)}"); printText.AppendLine($"Data 2 Key: {LibIRD.IRD.ByteArrayToHexString(IRD1.Data2Key)} vs {LibIRD.IRD.ByteArrayToHexString(IRD2.Data2Key)}");
// Print any PIC difference // Print any PIC difference
if (!IRD1.PIC.SequenceEqual(IRD2.PIC)) if (!IRD1.PIC.SequenceEqual(IRD2.PIC))
printText.AppendLine($"PIC: {Convert.ToHexString(IRD1.PIC)} vs {Convert.ToHexString(IRD2.PIC)}"); printText.AppendLine($"PIC: {LibIRD.IRD.ByteArrayToHexString(IRD1.PIC)} vs {LibIRD.IRD.ByteArrayToHexString(IRD2.PIC)}");
// Write formatted string to file if output path provided, otherwise to console // Write formatted string to file if output path provided, otherwise to console
if (outPath != null) if (outPath != null)
@@ -810,7 +810,7 @@ namespace IRDKit
try try
{ {
// Get disc key from hex string // Get disc key from hex string
byte[] discKey = Convert.FromHexString(hexKey); byte[] discKey = LibIRD.IRD.HexStringToByteArray(hexKey);
if (discKey == null || discKey.Length != 16) if (discKey == null || discKey.Length != 16)
Console.Error.WriteLine($"{hexKey} is not a valid key, detecting key automatically..."); Console.Error.WriteLine($"{hexKey} is not a valid key, detecting key automatically...");
else else
@@ -846,7 +846,7 @@ namespace IRDKit
Console.Error.WriteLine($"{hexKey} is not a valid key, detecting key automatically..."); Console.Error.WriteLine($"{hexKey} is not a valid key, detecting key automatically...");
else else
{ {
Console.WriteLine($"Creating {irdPath} with Key: {Convert.ToHexString(discKey)}"); Console.WriteLine($"Creating {irdPath} with Key: {LibIRD.IRD.ByteArrayToHexString(discKey)}");
IRD ird1 = new ReIRD(isoPath, discKey, layerbreak); IRD ird1 = new ReIRD(isoPath, discKey, layerbreak);
ird1.Write(irdPath); ird1.Write(irdPath);
if (verbose) if (verbose)
@@ -894,7 +894,7 @@ namespace IRDKit
Console.Error.WriteLine($"{hexKey} is not a valid key, detecting key automatically..."); Console.Error.WriteLine($"{hexKey} is not a valid key, detecting key automatically...");
else else
{ {
Console.WriteLine($"Creating {irdPath} with Key: {Convert.ToHexString(discKey)}"); Console.WriteLine($"Creating {irdPath} with Key: {LibIRD.IRD.ByteArrayToHexString(discKey)}");
IRD ird1 = new ReIRD(isoPath, discKey, layerbreak); IRD ird1 = new ReIRD(isoPath, discKey, layerbreak);
ird1.Write(irdPath); ird1.Write(irdPath);
if (verbose) if (verbose)
@@ -936,22 +936,21 @@ namespace IRDKit
Console.WriteLine("No key provided... Searching for key on redump.org"); Console.WriteLine("No key provided... Searching for key on redump.org");
// Compute CRC32 hash // Compute CRC32 hash
byte[] crc32; byte[] crc32 = HashTool.GetFileHashArray(isoPath, HashType.CRC32);
uint crc32UInt; string crc32String = LibIRD.IRD.ByteArrayToHexString(crc32).ToLowerInvariant();
using (FileStream fs = File.OpenRead(isoPath))
{ // Convert to UInt for use as UID in IRD
Crc32 hasher = new(); Array.Reverse(crc32);
hasher.Append(fs); uint crc32UInt = BitConverter.ToUInt32(crc32, 0);
crc32 = hasher.GetCurrentHash();
crc32UInt = BitConverter.ToUInt32(crc32);
// Change endianness
Array.Reverse(crc32);
}
string crc32_hash = Convert.ToHexString(crc32).ToLower();
// Search for ISO on redump.org // Search for ISO on redump.org
#if !NETFRAMEWORK
RedumpHttpClient redump = new(); RedumpHttpClient redump = new();
List<int> ids = redump.CheckSingleSitePage("http://redump.org/discs/system/ps3/quicksearch/" + crc32_hash).ConfigureAwait(false).GetAwaiter().GetResult(); List<int> ids = redump.CheckSingleSitePage("http://redump.org/discs/system/ps3/quicksearch/" + crc32String).ConfigureAwait(false).GetAwaiter().GetResult();
#else
RedumpWebClient redump = new();
List<int> ids = redump.CheckSingleSitePage("http://redump.org/discs/system/ps3/quicksearch/" + crc32String);
#endif
int id; int id;
if (ids.Count == 0) if (ids.Count == 0)
{ {
@@ -961,16 +960,14 @@ namespace IRDKit
else if (ids.Count > 1) else if (ids.Count > 1)
{ {
// More than one result for the CRC32 hash, compute SHA1 hash instead // More than one result for the CRC32 hash, compute SHA1 hash instead
byte[] sha1; string sha1String = HashTool.GetFileHash(isoPath, HashType.SHA1);
using (FileStream fs = File.OpenRead(isoPath))
{
SHA1 hasher = SHA1.Create();
sha1 = hasher.ComputeHash(fs);
}
string sha1_hash = Convert.ToHexString(sha1).ToLower();
// Search redump.org for SHA1 hash // Search redump.org for SHA1 hash
List<int> ids2 = redump.CheckSingleSitePage("http://redump.org/discs/system/ps3/quicksearch/" + sha1_hash).ConfigureAwait(false).GetAwaiter().GetResult(); #if !NETFRAMEWORK
List<int> ids2 = redump.CheckSingleSitePage("http://redump.org/discs/system/ps3/quicksearch/" + sha1String).ConfigureAwait(false).GetAwaiter().GetResult();
#else
List<int> ids2 = redump.CheckSingleSitePage("http://redump.org/discs/system/ps3/quicksearch/" + sha1String);
#endif
if (ids2.Count == 0) if (ids2.Count == 0)
{ {
Console.Error.WriteLine("ISO not found in redump and no valid key provided, cannot create IRD"); Console.Error.WriteLine("ISO not found in redump and no valid key provided, cannot create IRD");
@@ -990,7 +987,11 @@ namespace IRDKit
} }
// Download key file from redump.org // Download key file from redump.org
#if !NETFRAMEWORK
byte[] key = redump.GetByteArrayAsync($"http://redump.org/disc/{id}/key").ConfigureAwait(false).GetAwaiter().GetResult(); byte[] key = redump.GetByteArrayAsync($"http://redump.org/disc/{id}/key").ConfigureAwait(false).GetAwaiter().GetResult();
#else
byte[] key = redump.DownloadData($"http://redump.org/disc/{id}/key");
#endif
if (key.Length != 16) if (key.Length != 16)
{ {
Console.Error.WriteLine("Invalid key obtained from redump and no valid key provided, cannot create IRD"); Console.Error.WriteLine("Invalid key obtained from redump and no valid key provided, cannot create IRD");
@@ -998,7 +999,7 @@ namespace IRDKit
} }
// Create IRD with key from redump // Create IRD with key from redump
Console.WriteLine($"Creating {irdPath} with Key from redump.org: {Convert.ToHexString(key)}"); Console.WriteLine($"Creating {irdPath} with Key from redump.org: {LibIRD.IRD.ByteArrayToHexString(key)}");
try try
{ {
IRD ird = new ReIRD(isoPath, key, layerbreak, crc32UInt); IRD ird = new ReIRD(isoPath, key, layerbreak, crc32UInt);
@@ -1036,18 +1037,18 @@ namespace IRDKit
return node.Attribute("name").Value; return node.Attribute("name").Value;
} }
public static void RenameIRD(string irdPath, XDocument datfile, bool serial = false, bool crc = false, bool verbose = false) public static void RenameIRD(string irdPath, XDocument datfile, bool serial = false, bool ver = false, bool crc = false, bool verbose = false)
{ {
IRD ird = IRD.Read(irdPath); IRD ird = IRD.Read(irdPath);
if (ird.ExtraConfig != 0x0001) if (ird.ExtraConfig != 0x0001)
throw new ArgumentException($"{irdPath} is not a redump-style IRD"); throw new ArgumentException($"{irdPath} is not a redump-style IRD");
string filename = GetDatFilename(ird, datfile); string filename = GetDatFilename(ird, datfile) ?? throw new ArgumentException($"Cannot determine DAT filename for {irdPath}");
if (filename == null)
throw new ArgumentException($"Cannot determine DAT filename for {irdPath}");
if (serial) if (serial)
filename += $" [{ird.TitleID[..4]}-{ird.TitleID[4..9]}]"; filename += $" [{ird.TitleID.Substring(0, 4).Replace('\0', ' ')}-{ird.TitleID.Substring(4, 5).Replace('\0', ' ')}]";
if (ver)
filename += $" [{ird.DiscVersion.Replace('\0', ' ')}]";
if (crc) if (crc)
filename += $" [{ird.UID:X8}]"; filename += $" [{ird.UID:X8}]";
@@ -1073,7 +1074,7 @@ namespace IRDKit
} }
} }
#endregion #endregion
#region Helper Functions #region Helper Functions
+6
View File
@@ -39,3 +39,9 @@ For all options, run `irdkit help diff`
To compare two IRDs, run `irdkit diff game1.ird game2.ird` To compare two IRDs, run `irdkit diff game1.ird game2.ird`
The comparison can be printed to a file, e.g. `-o out.txt` or `--output=` The comparison can be printed to a file, e.g. `-o out.txt` or `--output=`
## Renaming IRDs
For all options, run `irdkit help rename`
To rename an IRD (or folder of IRDs) according to a redump DAT, run `irdkit rename -d redump.dat game.ird`
+2 -18
View File
@@ -11,16 +11,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.gitattributes = .gitattributes .gitattributes = .gitattributes
.gitignore = .gitignore .gitignore = .gitignore
.gitmodules = .gitmodules
LICENSE.txt = LICENSE.txt LICENSE.txt = LICENSE.txt
publish-irdkit.ps1 = publish-irdkit.ps1
README.md = README.md README.md = README.md
EndProjectSection EndProjectSection
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscUtils.Core", "DiscUtils\Library\DiscUtils.Core\DiscUtils.Core.csproj", "{9E71F5CD-887D-4A61-8460-9F7E8F110B46}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscUtils.Iso9660", "DiscUtils\Library\DiscUtils.Iso9660\DiscUtils.Iso9660.csproj", "{9E1EF05F-388B-42C6-A4E7-98BE20C7C8EC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscUtils.Streams", "DiscUtils\Library\DiscUtils.Streams\DiscUtils.Streams.csproj", "{E8D0B8B0-F165-4FA1-AC7B-194F5F48A5F9}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -35,18 +31,6 @@ Global
{9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Debug|Any CPU.Build.0 = Debug|Any CPU {9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Release|Any CPU.ActiveCfg = Release|Any CPU {9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Release|Any CPU.Build.0 = Release|Any CPU {9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Release|Any CPU.Build.0 = Release|Any CPU
{9E71F5CD-887D-4A61-8460-9F7E8F110B46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E71F5CD-887D-4A61-8460-9F7E8F110B46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E71F5CD-887D-4A61-8460-9F7E8F110B46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E71F5CD-887D-4A61-8460-9F7E8F110B46}.Release|Any CPU.Build.0 = Release|Any CPU
{9E1EF05F-388B-42C6-A4E7-98BE20C7C8EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E1EF05F-388B-42C6-A4E7-98BE20C7C8EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E1EF05F-388B-42C6-A4E7-98BE20C7C8EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E1EF05F-388B-42C6-A4E7-98BE20C7C8EC}.Release|Any CPU.Build.0 = Release|Any CPU
{E8D0B8B0-F165-4FA1-AC7B-194F5F48A5F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E8D0B8B0-F165-4FA1-AC7B-194F5F48A5F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8D0B8B0-F165-4FA1-AC7B-194F5F48A5F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E8D0B8B0-F165-4FA1-AC7B-194F5F48A5F9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
+1
Submodule LibIRD/DiscUtils added at 445a572137
+286 -167
View File
@@ -1,13 +1,13 @@
using DiscUtils; using System;
using DiscUtils.Iso9660;
using DiscUtils.Streams;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.IO.Hashing;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using LibIRD.DiscUtils;
using LibIRD.DiscUtils.Iso9660;
using LibIRD.DiscUtils.Streams;
using SabreTools.Hashing;
namespace LibIRD namespace LibIRD
{ {
@@ -413,7 +413,11 @@ namespace LibIRD
throw new ArgumentException("Disc Key must be a byte array of length 16", nameof(key)); throw new ArgumentException("Disc Key must be a byte array of length 16", nameof(key));
// Setup AES decryption // Setup AES decryption
#if NET35_OR_GREATER || NETCOREAPP
using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings");
#else
using Rijndael aes = Rijndael.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); ;
#endif
// Set AES settings // Set AES settings
aes.Key = D1AesKey; aes.Key = D1AesKey;
@@ -446,7 +450,11 @@ namespace LibIRD
throw new ArgumentException("Disc Key must be a byte array of length 16", nameof(d1)); throw new ArgumentException("Disc Key must be a byte array of length 16", nameof(d1));
// Setup AES decryption // Setup AES decryption
#if NET35_OR_GREATER || NETCOREAPP
using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings");
#else
using Rijndael aes = Rijndael.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); ;
#endif
// Set AES settings // Set AES settings
aes.Key = D1AesKey; aes.Key = D1AesKey;
@@ -478,7 +486,11 @@ namespace LibIRD
if (d2.Length != 16) throw new ArgumentException("Disc ID must be a byte array of length 16", nameof(d2)); if (d2.Length != 16) throw new ArgumentException("Disc ID must be a byte array of length 16", nameof(d2));
// Setup AES encryption // Setup AES encryption
#if NET35_OR_GREATER || NETCOREAPP
using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings");
#else
using Rijndael aes = Rijndael.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); ;
#endif
// Set AES settings // Set AES settings
aes.Key = D2AesKey; aes.Key = D2AesKey;
@@ -511,7 +523,11 @@ namespace LibIRD
throw new ArgumentException("Disc ID must be a byte array of length 16", nameof(d2)); throw new ArgumentException("Disc ID must be a byte array of length 16", nameof(d2));
// Setup AES encryption // Setup AES encryption
#if NET35_OR_GREATER || NETCOREAPP
using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings");
#else
using Rijndael aes = Rijndael.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); ;
#endif
// Set AES settings // Set AES settings
aes.Key = D2AesKey; aes.Key = D2AesKey;
@@ -540,11 +556,11 @@ namespace LibIRD
private protected void ParseGetKeyLog(string getKeyLog) private protected void ParseGetKeyLog(string getKeyLog)
{ {
// Validate .getkey.log file path // Validate .getkey.log file path
if (!File.Exists(getKeyLog)) if (!System.IO.File.Exists(getKeyLog))
throw new FileNotFoundException(nameof(getKeyLog)); throw new FileNotFoundException(nameof(getKeyLog));
// Read from .getkey.log file // Read from .getkey.log file
using StreamReader sr = File.OpenText(getKeyLog); using StreamReader sr = System.IO.File.OpenText(getKeyLog);
// Determine whether GetKey was successful // Determine whether GetKey was successful
string line; string line;
@@ -558,12 +574,12 @@ namespace LibIRD
if (line == null) if (line == null)
throw new InvalidDataException("Could not find Disc Key in .getkey.log"); throw new InvalidDataException("Could not find Disc Key in .getkey.log");
// Get Disc Key from log // Get Disc Key from log
string discKeyStr = line["disc_key = ".Length..]; string discKeyStr = line.Substring("disc_key = ".Length);
// Validate Disc Key from log // Validate Disc Key from log
if (discKeyStr.Length != 32) if (discKeyStr.Length != 32)
throw new InvalidDataException("Unexpected Disc Key in .getkey.log"); throw new InvalidDataException("Unexpected Disc Key in .getkey.log");
// Convert Disc Key to byte array // Convert Disc Key to byte array
discKey = Convert.FromHexString(discKeyStr); discKey = HexStringToByteArray(discKeyStr);
// Read Disc ID // Read Disc ID
byte[] discID; byte[] discID;
@@ -571,14 +587,14 @@ namespace LibIRD
if (line == null) if (line == null)
throw new InvalidDataException("Could not find Disc ID in .getkey.log"); throw new InvalidDataException("Could not find Disc ID in .getkey.log");
// Get Disc ID from log // Get Disc ID from log
string discIDStr = line["disc_id = ".Length..]; string discIDStr = line.Substring("disc_id = ".Length);
// Validate Disc ID from log // Validate Disc ID from log
if (discIDStr.Length != 32) if (discIDStr.Length != 32)
throw new InvalidDataException("Unexpected Disc ID in .getkey.log"); throw new InvalidDataException("Unexpected Disc ID in .getkey.log");
// Replace X's in Disc ID with 00000001 // Replace X's in Disc ID with 00000001
discIDStr = discIDStr[..24] + "00000001"; discIDStr = discIDStr.Substring(0, 24) + "00000001";
// Convert Disc ID to byte array // Convert Disc ID to byte array
discID = Convert.FromHexString(discIDStr); discID = HexStringToByteArray(discIDStr);
// Look for PIC in log // Look for PIC in log
byte[] discPIC; byte[] discPIC;
@@ -593,7 +609,7 @@ namespace LibIRD
if (discPICStr.Length != 256) if (discPICStr.Length != 256)
throw new InvalidDataException("Unexpected PIC in .getkey.log"); throw new InvalidDataException("Unexpected PIC in .getkey.log");
// Convert PIC to byte array // Convert PIC to byte array
discPIC = Convert.FromHexString(discPICStr[..230]); discPIC = HexStringToByteArray(discPICStr.Substring(0, 230));
// Double check for warnings in .getkey.log // Double check for warnings in .getkey.log
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("WARNING") == false && line.Trim().StartsWith("SUCCESS") == false) while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("WARNING") == false && line.Trim().StartsWith("SUCCESS") == false)
@@ -618,17 +634,17 @@ namespace LibIRD
/// <param name="redump">True if redump-style IRD</param> /// <param name="redump">True if redump-style IRD</param>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="FileNotFoundException"></exception> /// <exception cref="FileNotFoundException"></exception>
/// <exception cref="InvalidFileSystemException"></exception> /// <exception cref="IOException"></exception>
private protected void GenerateIRD(string isoPath, bool redump = false) private protected void GenerateIRD(string isoPath, bool redump = false)
{ {
// Parse ISO file as a file stream // Parse ISO file as a file stream
using FileStream fs = new FileStream(isoPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan) ?? throw new FileNotFoundException(isoPath); using FileStream fs = new FileStream(isoPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan) ?? throw new FileNotFoundException(isoPath);
// Validate ISO file stream // Validate ISO file stream
if (!CDReader.Detect(fs)) if (!CDReader.Detect(fs))
throw new InvalidFileSystemException("Not a valid ISO file"); throw new IOException("Not a valid ISO file");
// New ISO Reader from DiscUtils // New ISO Reader from DiscUtils
CDReader reader = new(fs, true, true); CDReader reader = new(fs);
// If generating redump-style IRD // If generating redump-style IRD
if (redump) if (redump)
@@ -645,7 +661,7 @@ namespace LibIRD
bool titleIDFound = ps3_DiscSFB.Field.TryGetValue("TITLE_ID", out string titleID); bool titleIDFound = ps3_DiscSFB.Field.TryGetValue("TITLE_ID", out string titleID);
// If a valid TITLE_ID field is present, remove the hyphen to fit into standard IRD file // If a valid TITLE_ID field is present, remove the hyphen to fit into standard IRD file
if (titleIDFound && titleID.Length == 10 && titleID[4] == '-') if (titleIDFound && titleID.Length == 10 && titleID[4] == '-')
TitleID = string.Concat(titleID.AsSpan(0, 4), titleID.AsSpan(5, 5)); TitleID = titleID.Substring(0, 4) + titleID.Substring(5, 5);
// If the version field is present, this is a multi-game disc // If the version field is present, this is a multi-game disc
// Redump-style IRDs use the VERSION field from PS3_DISC.SFB instead of VERSION from PARAM.SFO // Redump-style IRDs use the VERSION field from PS3_DISC.SFB instead of VERSION from PARAM.SFO
@@ -665,7 +681,7 @@ namespace LibIRD
{ {
bool titleIDFound = paramSFO.Field.TryGetValue("TITLE_ID", out string titleID); bool titleIDFound = paramSFO.Field.TryGetValue("TITLE_ID", out string titleID);
if (titleIDFound) if (titleIDFound)
TitleID = titleID.Length == 9 ? titleID : titleID.PadRight(9, '\0')[..9]; TitleID = titleID.Length == 9 ? titleID : titleID.PadRight(9, '\0').Substring(0, 9);
else else
TitleID = "\0\0\0\0\0\0\0\0\0"; TitleID = "\0\0\0\0\0\0\0\0\0";
} }
@@ -679,7 +695,7 @@ namespace LibIRD
{ {
bool discVersionFound = paramSFO.Field.TryGetValue("VERSION", out string discVersion); bool discVersionFound = paramSFO.Field.TryGetValue("VERSION", out string discVersion);
if (discVersionFound) if (discVersionFound)
DiscVersion = discVersion.Length == 5 ? discVersion : discVersion.PadRight(5, '\0')[..5]; DiscVersion = discVersion.Length == 5 ? discVersion : discVersion.PadRight(5, '\0').Substring(0, 5);
else else
DiscVersion = "\0\0\0\0\0"; DiscVersion = "\0\0\0\0\0";
} }
@@ -687,7 +703,7 @@ namespace LibIRD
// Try use App Version from PARAM.SFO // Try use App Version from PARAM.SFO
bool appVersionFound = paramSFO.Field.TryGetValue("APP_VER", out string appVersion); bool appVersionFound = paramSFO.Field.TryGetValue("APP_VER", out string appVersion);
if (appVersionFound) if (appVersionFound)
AppVersion = appVersion.Length == 5 ? appVersion : appVersion.PadRight(5, '\0')[..5]; AppVersion = appVersion.Length == 5 ? appVersion : appVersion.PadRight(5, '\0').Substring(0, 5);
else else
AppVersion = "\0\0\0\0\0"; AppVersion = "\0\0\0\0\0";
} }
@@ -737,7 +753,7 @@ namespace LibIRD
HashISO(fs, redump && UID == 0x00000000); HashISO(fs, redump && UID == 0x00000000);
} }
#endregion #endregion
#region Reading ISO #region Reading ISO
@@ -747,18 +763,19 @@ namespace LibIRD
/// <remarks>PS3UPDAT.PUP update file version number</remarks> /// <remarks>PS3UPDAT.PUP update file version number</remarks>
/// <param name="fs">ISO filestream</param> /// <param name="fs">ISO filestream</param>
/// <param name="reader">CDReader</param> /// <param name="reader">CDReader</param>
/// <exception cref="InvalidFileSystemException"></exception> /// <exception cref="IOException"></exception>
private void GetSystemVersion(FileStream fs, CDReader reader) private void GetSystemVersion(FileStream fs, CDReader reader)
{ {
// Determine PUP file offset via cluster // Determine PUP file offset via cluster
Range<long, long>[] updateClusters = reader.PathToClusters("\\PS3_UPDATE\\PS3UPDAT.PUP"); Range<long, long>[] updateClusters = reader.PathToClusters("\\PS3_UPDATE\\PS3UPDAT.PUP");
if (updateClusters == null && updateClusters.Length == 0 && updateClusters[0] == null) if (updateClusters == null && updateClusters.Length == 0 && updateClusters[0] == null)
throw new InvalidFileSystemException("Invalid file extents for PS3UPDAT.PUP"); throw new IOException("Invalid file extents for PS3UPDAT.PUP");
// PS3UPDAT.PUP file begins at first byte of dedicated cluster // PS3UPDAT.PUP file begins at first byte of dedicated cluster
UpdateOffset = SectorSize * updateClusters[0].Offset; UpdateOffset = SectorSize * updateClusters[0].Offset;
// Update file ends at the last byte of the last cluster // Update file ends at the last byte of the last cluster
UpdateEnd = SectorSize * updateClusters[^1].Offset + updateClusters[^1].Count; int lastExtent = updateClusters.Length - 1;
UpdateEnd = SectorSize * updateClusters[lastExtent].Offset + updateClusters[lastExtent].Count;
// Check PUP file Magic // Check PUP file Magic
fs.Seek(UpdateOffset, SeekOrigin.Begin); fs.Seek(UpdateOffset, SeekOrigin.Begin);
@@ -790,22 +807,24 @@ namespace LibIRD
/// </summary> /// </summary>
/// <param name="fs">ISO filestream</param> /// <param name="fs">ISO filestream</param>
/// <param name="reader">CDReader</param> /// <param name="reader">CDReader</param>
/// <exception cref="InvalidFileSystemException"></exception> /// <exception cref="IOException"></exception>
private void GetHeader(FileStream fs, CDReader reader) private void GetHeader(FileStream fs, CDReader reader)
{ {
// Determine the extent of the header via cluster (Sector 0 to first data sector) // Determine the extent of the header via cluster (Sector 0 to first data sector)
Range<long, long>[] sfbClusters = reader.PathToClusters("\\PS3_DISC.SFB"); Range<long, long>[] sfbClusters = reader.PathToClusters("\\PS3_DISC.SFB");
if (sfbClusters == null && sfbClusters.Length == 0 && sfbClusters[0] == null) if (sfbClusters == null && sfbClusters.Length == 0 && sfbClusters[0] == null)
throw new InvalidFileSystemException("Invalid file extents for PS3_DISC.SFB"); throw new IOException("Invalid file extents for PS3_DISC.SFB");
// End of header is at beginning of first byte of dedicated cluster // End of header is at beginning of first byte of dedicated cluster
FirstDataSector = sfbClusters[0].Offset; FirstDataSector = sfbClusters[0].Offset;
// Begin a GZip stream to write header to // Begin a GZip stream to write header to
using MemoryStream headerStream = new(); using MemoryStream headerStream = new();
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
using (GZipStream gzs = new(headerStream, CompressionLevel.SmallestSize)) using (GZipStream gzStream = new(headerStream, CompressionLevel.SmallestSize))
#elif NETCOREAPP || NET45_OR_GREATER
using (GZipStream gzStream = new(headerStream, CompressionLevel.Optimal))
#else #else
using (GZipStream gzs = new(headerStream, CompressionLevel.Optimal)) using (GZipStream gzStream = new(headerStream, CompressionMode.Compress))
#endif #endif
{ {
// Start reading data from the beginning of the ISO file // Start reading data from the beginning of the ISO file
@@ -817,7 +836,7 @@ namespace LibIRD
for (int i = 0; i < FirstDataSector; i++) for (int i = 0; i < FirstDataSector; i++)
{ {
numBytes = fs.Read(buf, 0, buf.Length); numBytes = fs.Read(buf, 0, buf.Length);
gzs.Write(buf, 0, numBytes); gzStream.Write(buf, 0, numBytes);
} }
} }
@@ -835,9 +854,11 @@ namespace LibIRD
// Begin a GZip stream to write footer to // Begin a GZip stream to write footer to
using MemoryStream footerStream = new(); using MemoryStream footerStream = new();
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
using (GZipStream gzs = new(footerStream, CompressionLevel.SmallestSize)) using (GZipStream gzStream = new(footerStream, CompressionLevel.SmallestSize))
#elif NETCOREAPP || NET45_OR_GREATER
using (GZipStream gzStream = new(footerStream, CompressionLevel.Optimal))
#else #else
using (GZipStream gzs = new(footerStream, CompressionLevel.Optimal)) using (GZipStream gzStream = new(footerStream, CompressionMode.Compress))
#endif #endif
{ {
// Start reading data from after last file (PS3UPDAT.PUP) // Start reading data from after last file (PS3UPDAT.PUP)
@@ -849,7 +870,7 @@ namespace LibIRD
while (numBytes != 0) while (numBytes != 0)
{ {
numBytes = fs.Read(buf, 0, buf.Length); numBytes = fs.Read(buf, 0, buf.Length);
gzs.Write(buf, 0, numBytes); gzStream.Write(buf, 0, numBytes);
} }
} }
@@ -862,7 +883,7 @@ namespace LibIRD
/// Retreives and stores the Region extents /// Retreives and stores the Region extents
/// </summary> /// </summary>
/// <param name="fs">ISO filestream</param> /// <param name="fs">ISO filestream</param>
/// <exception cref="InvalidFileSystemException"></exception> /// <exception cref="IOException"></exception>
private void GetRegions(FileStream fs) private void GetRegions(FileStream fs)
{ {
// Determine the number of unencryted regions // Determine the number of unencryted regions
@@ -873,7 +894,7 @@ namespace LibIRD
// Total number of regions is 2x number of unencrypted regions, minus 1 // Total number of regions is 2x number of unencrypted regions, minus 1
RegionCount = (byte)(2 * ((uint)decRegionCount[3]) - 1); RegionCount = (byte)(2 * ((uint)decRegionCount[3]) - 1);
if (RegionCount <= 0) if (RegionCount <= 0)
throw new InvalidFileSystemException("No regions detected in ISO"); throw new IOException("No regions detected in ISO");
RegionStart = new long[RegionCount]; RegionStart = new long[RegionCount];
RegionEnd = new long[RegionCount]; RegionEnd = new long[RegionCount];
@@ -886,22 +907,23 @@ namespace LibIRD
{ {
// End sector of previous region is start of this region // End sector of previous region is start of this region
if (i % 2 == 1) if (i % 2 == 1)
RegionStart[i] = BitConverter.ToInt32(regionSector) + 1; RegionStart[i] = BitConverter.ToInt32(regionSector, 0) + 1;
else else
RegionStart[i] = BitConverter.ToInt32(regionSector); RegionStart[i] = BitConverter.ToInt32(regionSector, 0);
// Determine end sector offset of this region // Determine end sector offset of this region
fs.Read(regionSector, 0, 4); fs.Read(regionSector, 0, 4);
Array.Reverse(regionSector, 0, 4); Array.Reverse(regionSector, 0, 4);
if (i % 2 == 1) if (i % 2 == 1)
RegionEnd[i] = BitConverter.ToInt32(regionSector) - 1; RegionEnd[i] = BitConverter.ToInt32(regionSector, 0) - 1;
else else
RegionEnd[i] = BitConverter.ToInt32(regionSector); RegionEnd[i] = BitConverter.ToInt32(regionSector, 0);
} }
// Remove header from first region // Remove header from first region
RegionStart[0] = FirstDataSector; RegionStart[0] = FirstDataSector;
// Remove footer from last region // Remove footer from last region
RegionEnd[^1] = (UpdateEnd / SectorSize) - 1; int lastRegion = RegionEnd.Length - 1;
RegionEnd[lastRegion] = (UpdateEnd / SectorSize) - 1;
} }
/// <summary> /// <summary>
@@ -922,7 +944,7 @@ namespace LibIRD
// If invalid clusters were returned, we can't hash this file // If invalid clusters were returned, we can't hash this file
if (fileExtent == null && fileExtent.Length == 0) if (fileExtent == null && fileExtent.Length == 0)
throw new InvalidFileSystemException($"Unexpected file extents for {filePath}"); throw new IOException($"Unexpected file extents for {filePath}");
// Determine smallest file offset as first sector // Determine smallest file offset as first sector
long smallestOffset = fileExtent[0].Offset; long smallestOffset = fileExtent[0].Offset;
@@ -930,7 +952,7 @@ namespace LibIRD
for (int i = 1; i < fileExtent.Length; i++) for (int i = 1; i < fileExtent.Length; i++)
{ {
if (fileExtent[i] == null) if (fileExtent[i] == null)
throw new InvalidFileSystemException($"Unexpected file extents for {filePath}"); throw new IOException($"Unexpected file extents for {filePath}");
if (fileExtent[i].Offset * SectorSize != fileExtent[i - 1].Offset * SectorSize + fileExtent[i - 1].Count) if (fileExtent[i].Offset * SectorSize != fileExtent[i - 1].Offset * SectorSize + fileExtent[i - 1].Count)
nonContiguous = true; nonContiguous = true;
@@ -968,7 +990,8 @@ namespace LibIRD
/// <exception cref="InvalidOperationException"></exception> /// <exception cref="InvalidOperationException"></exception>
private protected void DecryptSectors(ref byte[] buffer, int sectorNumber, int offset = 0, int? count = null) private protected void DecryptSectors(ref byte[] buffer, int sectorNumber, int offset = 0, int? count = null)
{ {
ArgumentNullException.ThrowIfNull(buffer); if (buffer == null)
throw new ArgumentNullException(nameof(buffer));
if (buffer.Length == 0 || buffer.Length % SectorSize != 0) if (buffer.Length == 0 || buffer.Length % SectorSize != 0)
throw new ArgumentException("Encrypted buffer must be multiple of SectorSize"); throw new ArgumentException("Encrypted buffer must be multiple of SectorSize");
@@ -982,7 +1005,12 @@ namespace LibIRD
throw new ArgumentException("Number of sectors must be within buffer"); throw new ArgumentException("Number of sectors must be within buffer");
// Setup AES decryption // Setup AES decryption
#if NET35_OR_GREATER || NETCOREAPP
using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings");
#else
using Rijndael aes = Rijndael.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); ;
#endif
// Set AES settings // Set AES settings
aes.Key = DiscKey; aes.Key = DiscKey;
aes.Padding = PaddingMode.None; aes.Padding = PaddingMode.None;
@@ -1028,6 +1056,10 @@ namespace LibIRD
CountFiles(dirInfo); CountFiles(dirInfo);
} }
#endregion
#region Hashing
/// <summary> /// <summary>
/// Calculate hashes for all region and file extents /// Calculate hashes for all region and file extents
/// </summary> /// </summary>
@@ -1037,24 +1069,24 @@ namespace LibIRD
{ {
// Initialise CRC32 ISO hasher, only used if making redump-style IRD // Initialise CRC32 ISO hasher, only used if making redump-style IRD
byte[] crc32; byte[] crc32;
Crc32 isoHasher = new(); HashWrapper isoHasher = new(HashType.CRC32);
// Initialise MD5 region hashes // Initialise MD5 region hashes
List<int> regions = []; List<int> regions = [];
MD5[] regionMD5 = new MD5[RegionCount]; HashWrapper[] regionMD5 = new HashWrapper[RegionCount];
for (int i = 0; i < RegionCount; i++) for (int i = 0; i < RegionCount; i++)
{ {
regions.Add(i); regions.Add(i);
regionMD5[i] = MD5.Create(); regionMD5[i] = new(HashType.MD5);
} }
// Initialise MD5 file hashes // Initialise MD5 file hashes
List<int> files = []; List<int> files = [];
MD5[] fileMD5 = new MD5[FileCount]; HashWrapper[] fileMD5 = new HashWrapper[FileCount];
for (int i = 0; i < FileCount; i++) for (int i = 0; i < FileCount; i++)
{ {
files.Add(i); files.Add(i);
fileMD5[i] = MD5.Create(); fileMD5[i] = new(HashType.MD5);
} }
// Start hashing from beginning of ISO // Start hashing from beginning of ISO
@@ -1073,12 +1105,30 @@ namespace LibIRD
// If end of ISO reached, stop reading // If end of ISO reached, stop reading
if (numBytes == 0) if (numBytes == 0)
{ {
// Ensure all hashes have been saved
foreach (int i in regions)
{
// Close region hash
regionMD5[i].Terminate();
RegionHashes[i] = regionMD5[i].CurrentHashBytes;
regionMD5[i].Dispose();
}
foreach (int i in files)
{
// Close file hash
fileMD5[i].Terminate();
FileHashes[i] = fileMD5[i].CurrentHashBytes;
fileMD5[i].Dispose();
}
// If making redump-style IRD, save CRC32 hash to UID field // If making redump-style IRD, save CRC32 hash to UID field
if (redump) if (redump)
{ {
crc32 = isoHasher.GetCurrentHash(); crc32 = isoHasher.CurrentHashBytes;
Array.Reverse(crc32);
UID = BitConverter.ToUInt32(crc32, 0); UID = BitConverter.ToUInt32(crc32, 0);
} }
return; return;
} }
@@ -1103,12 +1153,12 @@ namespace LibIRD
if (bufSectors == 0) if (bufSectors == 0)
Console.Error.WriteLine("ERROR: Trailing partial sector in ISO filestream"); Console.Error.WriteLine("ERROR: Trailing partial sector in ISO filestream");
if (numBytes > buf.Length) if (numBytes > buf.Length)
throw new InvalidFileSystemException("ERROR: Read more bytes than buffer size???"); throw new IOException("ERROR: Read more bytes than buffer size???");
} }
// Hash ISO // Hash ISO
if (redump) if (redump)
isoHasher.Append(new ReadOnlySpan<byte>(buf, 0, numBytes)); isoHasher.Process(buf, 0, numBytes);
// Hash regions // Hash regions
List<int> regionsEnded = []; List<int> regionsEnded = [];
@@ -1130,23 +1180,24 @@ namespace LibIRD
// Determine end byte // Determine end byte
int endByte = (int)(SectorSize * (RegionEnd[i] - currentSector + 1)); int endByte = (int)(SectorSize * (RegionEnd[i] - currentSector + 1));
// Close region hash // Close region hash
regionMD5[i].TransformFinalBlock(buf, startByte, endByte - startByte); regionMD5[i].Process(buf, startByte, endByte - startByte);
RegionHashes[i] = regionMD5[i].Hash; regionMD5[i].Terminate();
regionMD5[i].Clear(); RegionHashes[i] = regionMD5[i].CurrentHashBytes;
regionMD5[i].Dispose();
regionsEnded.Add(i); regionsEnded.Add(i);
} }
// Check if region has already begun // Check if region has already begun
else if (RegionStart[i] <= currentSector) else if (RegionStart[i] <= currentSector)
{ {
// Hash buffer // Hash buffer
regionMD5[i].TransformBlock(buf, 0, (int)SectorSize * bufSectors, null, 0); regionMD5[i].Process(buf, 0, (int)SectorSize * bufSectors);
} }
// Region Start is in this buffer, ending is in the future // Region Start is in this buffer, ending is in the future
else else
{ {
// Hash partial buffer // Hash partial buffer
int regionStart = (int)(SectorSize * (RegionStart[i] - currentSector)); int regionStart = (int)(SectorSize * (RegionStart[i] - currentSector));
regionMD5[i].TransformBlock(buf, regionStart, (int)SectorSize * bufSectors - regionStart, null, 0); regionMD5[i].Process(buf, regionStart, (int)SectorSize * bufSectors - regionStart);
} }
} }
if (regionsEnded.Count > 0) if (regionsEnded.Count > 0)
@@ -1171,7 +1222,7 @@ namespace LibIRD
// Don't decrypt last sectors if the encrypted region ends within this buffer // Don't decrypt last sectors if the encrypted region ends within this buffer
if (RegionEnd[i] < currentSector + bufSectors) if (RegionEnd[i] < currentSector + bufSectors)
encCount -= (int)(currentSector + bufSectors - RegionEnd[i] + 1); encCount -= (int)(currentSector + bufSectors - RegionEnd[i] - 1);
// Decrypt encrypted sectors // Decrypt encrypted sectors
DecryptSectors(ref buf, (int)currentSector + encOffset, encOffset, encCount); DecryptSectors(ref buf, (int)currentSector + encOffset, encOffset, encCount);
@@ -1191,7 +1242,7 @@ namespace LibIRD
{ {
// Skip hashing file extent if it has not yet started or already ended // Skip hashing file extent if it has not yet started or already ended
if (FileExtents[i][j].Offset > currentSector + bufSectors if (FileExtents[i][j].Offset > currentSector + bufSectors
|| SectorSize* FileExtents[i][j].Offset + FileExtents[i][j].Count < SectorSize * currentSector) || SectorSize * FileExtents[i][j].Offset + FileExtents[i][j].Count < SectorSize * currentSector)
continue; continue;
// Determine first file byte location in buffer // Determine first file byte location in buffer
int startByte = FileExtents[i][j].Offset > currentSector ? (int)(SectorSize * (FileExtents[i][j].Offset - currentSector)) : 0; int startByte = FileExtents[i][j].Offset > currentSector ? (int)(SectorSize * (FileExtents[i][j].Offset - currentSector)) : 0;
@@ -1200,18 +1251,19 @@ namespace LibIRD
// Don't hash more than the buffer size // Don't hash more than the buffer size
endByte = endByte < bufSectors * (int)SectorSize ? endByte : bufSectors * (int)SectorSize; endByte = endByte < bufSectors * (int)SectorSize ? endByte : bufSectors * (int)SectorSize;
// Hash portion of buffer that file exists in // Hash portion of buffer that file exists in
fileMD5[i].TransformBlock(buf, startByte, endByte - startByte, null, 0); fileMD5[i].Process(buf, startByte, endByte - startByte);
} }
// Check if current file has ended in this buffer (assumes last extent contains last byte) // Check if current file has ended in this buffer (assumes last extent contains last byte)
long lastByte = SectorSize * FileExtents[i][^1].Offset + FileExtents[i][^1].Count; int lastExtent = FileExtents[i].Length - 1;
if (lastByte < SectorSize * (currentSector + bufSectors) long lastByte = SectorSize * FileExtents[i][lastExtent].Offset + FileExtents[i][lastExtent].Count;
if (lastByte <= SectorSize * (currentSector + bufSectors)
&& lastByte > SectorSize * currentSector) && lastByte > SectorSize * currentSector)
{ {
// Close file hash // Close file hash
fileMD5[i].TransformFinalBlock(buf, 0, 0); fileMD5[i].Terminate();
FileHashes[i] = fileMD5[i].Hash; FileHashes[i] = fileMD5[i].CurrentHashBytes;
fileMD5[i].Clear(); fileMD5[i].Dispose();
filesEnded.Add(i); filesEnded.Add(i);
} }
} }
@@ -1219,7 +1271,7 @@ namespace LibIRD
files.RemoveAll(item => filesEnded.Contains(item)); files.RemoveAll(item => filesEnded.Contains(item));
currentSector += bufSectors; currentSector += bufSectors;
} }
} }
#endregion #endregion
@@ -1240,116 +1292,123 @@ namespace LibIRD
// Create new stream to uncompressed IRD contents // Create new stream to uncompressed IRD contents
using MemoryStream stream = new(); using MemoryStream stream = new();
// Write IRD data to stream in order // Write IRD data to stream as UTF8
using (BinaryWriter bw = new(stream, Encoding.UTF8, true)) using BinaryWriter bw = new(stream, Encoding.UTF8);
// IRD File Signature
bw.Write(Magic);
// IRD File Version
bw.Write(Version);
// PARAM.SFO / TITLE_ID
byte[] titleIDBuf = Encoding.ASCII.GetBytes(TitleID);
bw.Write(titleIDBuf, 0, 9);
// PARAM.SFO / TITLE
bw.Write(Title);
// PARAM.SFO / PS3_SYSTEM_VER
byte[] systemVersionBuf = Encoding.ASCII.GetBytes(SystemVersion);
bw.Write(systemVersionBuf, 0, 4);
// PARAM.SFO / VERSION
byte[] buf = Encoding.ASCII.GetBytes(DiscVersion);
byte[] discVersionBuf = new byte[5];
Array.Copy(buf, 0, discVersionBuf, 0, buf.Length);
bw.Write(discVersionBuf, 0, 5);
// PARAM.SFO / APP_VER
buf = Encoding.ASCII.GetBytes(AppVersion);
byte[] appVersionBuf = new byte[5];
Array.Copy(buf, 0, appVersionBuf, 0, buf.Length);
bw.Write(appVersionBuf, 0, 5);
// IRD Unique Identifier, for version 7
if (_version == 7)
bw.Write(UID);
// Compress Header
bw.Write(HeaderLength);
bw.Write(Header, 0, (int)HeaderLength);
// Compress Footer
bw.Write(FooterLength);
bw.Write(Footer, 0, (int)FooterLength);
// Number of regions hashed
bw.Write(RegionCount);
// Hashes for each region
for (int i = 0; i < RegionCount; i++)
{ {
// IRD File Signature if (RegionHashes[i] == null)
bw.Write(Magic); bw.Write(NullMD5);
else
// IRD File Version bw.Write(RegionHashes[i], 0, 16);
bw.Write(Version);
// PARAM.SFO / TITLE_ID
byte[] titleIDBuf = Encoding.ASCII.GetBytes(TitleID);
bw.Write(titleIDBuf, 0, 9);
// PARAM.SFO / TITLE
bw.Write(Title);
// PARAM.SFO / PS3_SYSTEM_VER
byte[] systemVersionBuf = Encoding.ASCII.GetBytes(SystemVersion);
bw.Write(systemVersionBuf, 0, 4);
// PARAM.SFO / VERSION
byte[] buf = Encoding.ASCII.GetBytes(DiscVersion);
byte[] discVersionBuf = new byte[5];
Array.Copy(buf, 0, discVersionBuf, 0, buf.Length);
bw.Write(discVersionBuf, 0, 5);
// PARAM.SFO / APP_VER
buf = Encoding.ASCII.GetBytes(AppVersion);
byte[] appVersionBuf = new byte[5];
Array.Copy(buf, 0, appVersionBuf, 0, buf.Length);
bw.Write(appVersionBuf, 0, 5);
// IRD Unique Identifier, for version 7
if (_version == 7)
bw.Write(UID);
// Compress Header
bw.Write(HeaderLength);
bw.Write(Header, 0, (int)HeaderLength);
// Compress Footer
bw.Write(FooterLength);
bw.Write(Footer, 0, (int)FooterLength);
// Number of regions hashed
bw.Write(RegionCount);
// Hashes for each region
for (int i = 0; i < RegionCount; i++)
{
if (RegionHashes[i] == null)
bw.Write(NullMD5);
else
bw.Write(RegionHashes[i], 0, 16);
}
// Number of files hashed
bw.Write(FileCount);
// Hashes for each file
for (int i = 0; i < FileCount; i++)
{
bw.Write(FileKeys[i]);
if (FileHashes[i] == null)
bw.Write(NullMD5);
else
bw.Write(FileHashes[i], 0, 16);
}
// Reserved fields
bw.Write(ExtraConfig);
bw.Write(Attachments);
// PIC data is placed here for Version 9
if (_version >= 9)
bw.Write(PIC, 0, 115);
// Disc Authentication keys
bw.Write(Data1Key, 0, 16);
bw.Write(Data2Key, 0, 16);
// PIC data is placed here prior to Version 9
if (_version < 9)
bw.Write(PIC, 0, 115);
// IRD Unique Identifier, for versions after 7
if (_version > 7)
bw.Write(UID);
} }
// Number of files hashed
bw.Write(FileCount);
// Hashes for each file
for (int i = 0; i < FileCount; i++)
{
bw.Write(FileKeys[i]);
if (FileHashes[i] == null)
bw.Write(NullMD5);
else
bw.Write(FileHashes[i], 0, 16);
}
// Reserved fields
bw.Write(ExtraConfig);
bw.Write(Attachments);
// PIC data is placed here for Version 9
if (_version >= 9)
bw.Write(PIC, 0, 115);
// Disc Authentication keys
bw.Write(Data1Key, 0, 16);
bw.Write(Data2Key, 0, 16);
// PIC data is placed here prior to Version 9
if (_version < 9)
bw.Write(PIC, 0, 115);
// IRD Unique Identifier, for versions after 7
if (_version > 7)
bw.Write(UID);
// Calculate the little-endian 32-bit "IEEE 802.3" CRC value of the IRD contents so far // Calculate the little-endian 32-bit "IEEE 802.3" CRC value of the IRD contents so far
stream.Position = 0; stream.Position = 0;
Crc32 crc32 = new(); byte[] crc32 = HashTool.GetStreamHashArray(stream, HashType.CRC32, true);
crc32.Append(stream); Array.Reverse(crc32);
byte[] crc = crc32.GetCurrentHash();
// Write final CRC value to the stream // Write final CRC value to the stream
stream.Write(crc, 0, 4); stream.Write(crc32, 0, 4);
// Create the IRD file stream // Create the IRD file stream
using FileStream fs = new(irdPath, FileMode.Create, FileAccess.Write); using FileStream fs = new(irdPath, FileMode.Create, FileAccess.Write);
// Create a GZipped IRD file stream // Create a GZipped IRD file stream
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
using GZipStream gzStream = new(fs, CompressionLevel.SmallestSize); using GZipStream gzStream = new(fs, CompressionLevel.SmallestSize);
#else #elif NETCOREAPP || NET45_OR_GREATER
using GZipStream gzStream = new(fs, CompressionLevel.Optimal); using GZipStream gzStream = new(fs, CompressionLevel.Optimal);
#else
using GZipStream gzStream = new(fs, CompressionMode.Compress);
#endif #endif
// Write entire gzipped IRD stream to file // Write entire gzipped IRD stream to file
stream.Position = 0; stream.Position = 0;
#if NET40_OR_GREATER || NETCOREAPP
stream.CopyTo(gzStream); stream.CopyTo(gzStream);
#else
byte[] buffer = new byte[1024];
int numBytes;
while ((numBytes = stream.Read(buffer, 0, buffer.Length)) > 0)
gzStream.Write(buffer, 0, numBytes);
#endif
} }
/// <summary> /// <summary>
@@ -1363,7 +1422,7 @@ namespace LibIRD
// Validate irdPath // Validate irdPath
if (irdPath == null || irdPath.Length <= 0) if (irdPath == null || irdPath.Length <= 0)
throw new ArgumentNullException(nameof(irdPath)); throw new ArgumentNullException(nameof(irdPath));
if (!File.Exists(irdPath)) if (!System.IO.File.Exists(irdPath))
throw new FileNotFoundException(nameof(irdPath)); throw new FileNotFoundException(nameof(irdPath));
// Open IRD for reading // Open IRD for reading
@@ -1373,7 +1432,7 @@ namespace LibIRD
// Check for IRD file signature // Check for IRD file signature
byte[] magic = br.ReadBytes(4); byte[] magic = br.ReadBytes(4);
if (!((ReadOnlySpan<byte>)Magic).SequenceEqual(magic)) if (magic == null || magic.Length != 4 || magic[0] != Magic[0] || magic[1] != Magic[1] || magic[2] != Magic[2] || magic[3] != Magic[3])
throw new InvalidDataException("IRD file not recognised"); throw new InvalidDataException("IRD file not recognised");
// Read file version // Read file version
@@ -1492,9 +1551,9 @@ namespace LibIRD
if (Attachments != 0x0000) if (Attachments != 0x0000)
printText.AppendLine($"Attachments: {Attachments:X4}"); printText.AppendLine($"Attachments: {Attachments:X4}");
printText.AppendLine($"Unique ID: {UID:X8}"); printText.AppendLine($"Unique ID: {UID:X8}");
printText.AppendLine($"Data 1 Key: {Convert.ToHexString(Data1Key)}"); printText.AppendLine($"Data 1 Key: {ByteArrayToHexString(Data1Key)}");
printText.AppendLine($"Data 2 Key: {Convert.ToHexString(Data2Key)}"); printText.AppendLine($"Data 2 Key: {ByteArrayToHexString(Data2Key)}");
printText.AppendLine($"PIC: {Convert.ToHexString(PIC)}"); printText.AppendLine($"PIC: {ByteArrayToHexString(PIC)}");
printText.AppendLine(); printText.AppendLine();
if (printPath == null) if (printPath == null)
@@ -1507,7 +1566,7 @@ namespace LibIRD
} }
else else
{ {
File.AppendAllText(printPath, printText.ToString()); System.IO.File.AppendAllText(printPath, printText.ToString());
} }
} }
@@ -1537,9 +1596,9 @@ namespace LibIRD
if (Attachments != 0x0000) if (Attachments != 0x0000)
json.AppendLine($" \"Attachments\": \"{Attachments:X4}\","); json.AppendLine($" \"Attachments\": \"{Attachments:X4}\",");
json.AppendLine($" \"Unique ID\": \"{UID:X8}\","); json.AppendLine($" \"Unique ID\": \"{UID:X8}\",");
json.AppendLine($" \"Data 1 Key\": \"{Convert.ToHexString(Data1Key)}\","); json.AppendLine($" \"Data 1 Key\": \"{ByteArrayToHexString(Data1Key)}\",");
json.AppendLine($" \"Data 2 Key\": \"{Convert.ToHexString(Data2Key)}\","); json.AppendLine($" \"Data 2 Key\": \"{ByteArrayToHexString(Data2Key)}\",");
json.AppendLine($" \"PIC\": \"{Convert.ToHexString(PIC)}\""); json.AppendLine($" \"PIC\": \"{ByteArrayToHexString(PIC)}\"");
if (single) if (single)
json.AppendLine("}"); json.AppendLine("}");
else else
@@ -1557,10 +1616,70 @@ namespace LibIRD
else else
{ {
// Write to path // Write to path
File.AppendAllText(jsonPath, json.ToString()); System.IO.File.AppendAllText(jsonPath, json.ToString());
} }
} }
#endregion
#region Helper Functions
/// <summary>
/// Converts a hex string into a byte array
/// </summary>
/// <param name="hex">Hex string</param>
/// <returns>Converted byte array, or null if invalid hex string</returns>
public static byte[] HexStringToByteArray(string hexString)
{
// Valid hex string must be an even number of characters
if (string.IsNullOrEmpty(hexString) || hexString!.Length % 2 == 1)
return null;
// Convert ASCII to byte via lookup table
int[] hexLookup = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F];
byte[] byteArray = new byte[hexString.Length / 2];
for (int i = 0; i < hexString.Length; i += 2)
{
// Convert next two chars to ASCII value relative to '0'
int a = Char.ToUpper(hexString[i]) - '0';
int b = Char.ToUpper(hexString[i + 1]) - '0';
// Ensure hex string only has '0' through '9' and 'A' through 'F' (case insensitive)
if ((a < 0 || b < 0 || a > 22 || b > 22) || (a > 10 && a < 17) || (b > 10 && b < 17))
return null;
byteArray[i / 2] = (byte)(hexLookup[a] << 4 | hexLookup[b]);
}
return byteArray;
}
/// <summary>
/// Converts a byte array into a hex string
/// </summary>
/// <param name="byteArray">Byte array</param>
/// <returns>Hex string representation of byte array, null if invalid</returns>
public static string ByteArrayToHexString(byte[] byteArray)
{
// Validate byte array
if (byteArray == null || byteArray.Length == 0)
return null;
// Store map of values to hex string
string hex = "0123456789ABCDEF";
// Add two characters per byte value
StringBuilder hexString = new(2 * byteArray.Length);
foreach (byte b in byteArray)
{
hexString.Append(hex[(int)(b >> 4)]);
hexString.Append(hex[(int)(b & 0x0F)]);
}
// Return hex string
return hexString.ToString();
}
#endregion #endregion
} }
} }
+15 -17
View File
@@ -2,11 +2,12 @@
<PropertyGroup> <PropertyGroup>
<!-- Assembly Properties --> <!-- Assembly Properties -->
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<Version>0.6.0</Version> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>0.9.0</Version>
<PackageOutputPath>../nupkg</PackageOutputPath> <PackageOutputPath>../nupkg</PackageOutputPath>
<!-- Package Properties --> <!-- Package Properties -->
@@ -20,25 +21,22 @@
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression> <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition="!$(RuntimeIdentifier.StartsWith(`osx-arm`))">
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="./README.md" Pack="true" PackagePath="" /> <None Include="./README.md" Pack="true" PackagePath="" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Core\DiscUtils.Core.csproj" PrivateAssets="All" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Iso9660\DiscUtils.Iso9660.csproj" PrivateAssets="All" /> <PackageReference Include="SabreTools.Hashing" Version="1.1.4" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Streams\DiscUtils.Streams.csproj" PrivateAssets="All" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="BuildOnlySettings;ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths-&gt;WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')-&gt;WithMetadataValue('PrivateAssets', 'All'))" />
</ItemGroup>
</Target>
</Project> </Project>
+3 -8
View File
@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.Json; using Newtonsoft.Json;
namespace LibIRD namespace LibIRD
{ {
@@ -68,7 +68,7 @@ namespace LibIRD
// Read SFB file version // Read SFB file version
byte[] buf = br.ReadBytes(2); byte[] buf = br.ReadBytes(2);
Array.Reverse(buf); Array.Reverse(buf);
Version = BitConverter.ToUInt16(buf); Version = BitConverter.ToUInt16(buf, 0);
// Process all field headers // Process all field headers
sfbStream.Seek(0x20, SeekOrigin.Begin); sfbStream.Seek(0x20, SeekOrigin.Begin);
@@ -131,11 +131,6 @@ namespace LibIRD
} }
} }
/// <summary>
/// Define JSON options once
/// </summary>
private readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true };
/// <summary> /// <summary>
/// Prints parameters extracted from PS3_DISC.SFB to a json object /// Prints parameters extracted from PS3_DISC.SFB to a json object
/// </summary> /// </summary>
@@ -143,7 +138,7 @@ namespace LibIRD
public void PrintJson(string jsonPath = null) public void PrintJson(string jsonPath = null)
{ {
// Serialise PS3_Disc.SFB data to a JSON object // Serialise PS3_Disc.SFB data to a JSON object
string json = JsonSerializer.Serialize(Field, JsonOpts); string json = JsonConvert.SerializeObject(Field, Formatting.Indented);
// If no path given, output to console // If no path given, output to console
if (jsonPath == null) if (jsonPath == null)
+2 -7
View File
@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.Json; using Newtonsoft.Json;
namespace LibIRD namespace LibIRD
{ {
@@ -154,11 +154,6 @@ namespace LibIRD
} }
} }
/// <summary>
/// Define JSON options once
/// </summary>
private readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true };
/// <summary> /// <summary>
/// Prints parameters extracted from PARAM.SFO to a json object /// Prints parameters extracted from PARAM.SFO to a json object
/// </summary> /// </summary>
@@ -166,7 +161,7 @@ namespace LibIRD
public void PrintJson(string jsonPath = null) public void PrintJson(string jsonPath = null)
{ {
// Serialise PS3_Disc.SFB data to a JSON object // Serialise PS3_Disc.SFB data to a JSON object
string json = JsonSerializer.Serialize(Field, JsonOpts); string json = JsonConvert.SerializeObject(Field, Formatting.Indented);
// If no path given, output to console // If no path given, output to console
if (jsonPath == null) if (jsonPath == null)
+3 -38
View File
@@ -1,7 +1,5 @@
using DiscUtils.Iso9660; using System;
using System;
using System.IO; using System.IO;
using System.IO.Hashing;
namespace LibIRD namespace LibIRD
{ {
@@ -135,7 +133,7 @@ namespace LibIRD
{ {
// If no layerbreak provided, ensure ISO is not BD-Video hybrid // If no layerbreak provided, ensure ISO is not BD-Video hybrid
using FileStream fs = new FileStream(isoPath, FileMode.Open, FileAccess.Read) ?? throw new FileNotFoundException(isoPath); using FileStream fs = new FileStream(isoPath, FileMode.Open, FileAccess.Read) ?? throw new FileNotFoundException(isoPath);
CDReader reader = new(fs, true, true); DiscUtils.Iso9660.CDReader reader = new(fs);
if (reader.DirectoryExists("\\BDMV")) if (reader.DirectoryExists("\\BDMV"))
throw new ArgumentException("Layerbreak must be provided for BD-Video hybrid discs"); throw new ArgumentException("Layerbreak must be provided for BD-Video hybrid discs");
// Assume disc has default layerbreak // Assume disc has default layerbreak
@@ -184,7 +182,7 @@ namespace LibIRD
l0es[0], l0es[1], l0es[2], l0es[3], l0es[0], l0es[1], l0es[2], l0es[3],
// 32 bytes of zeros // 32 bytes of zeros
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Initial portion of PIC again, for 2nd layer // Initial portion of PIC again, for 2nd layer
// ["DI"] [v1] [11unit][DI num] // ["DI"] [v1] [11unit][DI num]
0x44, 0x49, 0x01, 0x11, 0x00, 0x01, 0x20, 0x00, 0x44, 0x49, 0x01, 0x11, 0x00, 0x01, 0x20, 0x00,
@@ -232,39 +230,6 @@ namespace LibIRD
return pic; return pic;
} }
/// <summary>
/// Generates the UID field by computing the CRC32 hash of the ISO
/// </summary>
/// <param name="isoPath">Path to the ISO</param>
/// <exception cref="FileNotFoundException"></exception>
private static uint GenerateUID(string isoPath)
{
// Check file exists
FileInfo iso;
try
{
iso = new FileInfo(isoPath);
}
catch (Exception e)
{
throw new ArgumentException("Invalid ISO Path: " + e.Message);
}
if (!iso.Exists)
throw new FileNotFoundException(nameof(isoPath));
// Compute CRC32 hash
byte[] crc32;
using (FileStream fs = File.OpenRead(isoPath))
{
Crc32 hasher = new();
hasher.Append(fs);
crc32 = hasher.GetCurrentHash();
}
// Redump ISO CRC32 hash is used as the Unique ID in the reproducible IRD
return BitConverter.ToUInt32(crc32, 0);
}
/// <summary> /// <summary>
/// Calculates ISO file size /// Calculates ISO file size
/// </summary> /// </summary>
+1
View File
@@ -13,6 +13,7 @@ IRDKit is a tool that allows direct use of LibIRD functionality from a command l
- Printing info about all ISOs and IRDs in a folder: `irdkit info .` - Printing info about all ISOs and IRDs in a folder: `irdkit info .`
- Creating an IRD from an ISO: `irdkit create game.iso` - Creating an IRD from an ISO: `irdkit create game.iso`
- Finding differences between two IRDs: `irdkit diff game1.ird game2.ird` - Finding differences between two IRDs: `irdkit diff game1.ird game2.ird`
- Renaming an IRD or folder of IRDs using a redump DAT: `irdkit rename -d redump.dat game.ird`
For detailed usage, read more [here](IRDKit). For detailed usage, read more [here](IRDKit).