From b6bb5f05823a37f7bada526ff4dc825eaed20cd2 Mon Sep 17 00:00:00 2001 From: Deterous <138427222+Deterous@users.noreply.github.com> Date: Tue, 5 Mar 2024 14:52:30 +0900 Subject: [PATCH] Attempt to support net40 with ST.Hashing --- IRDKit/IRDKit.csproj | 7 +- IRDKit/Program.cs | 55 ++++++------ LibIRD/IRD.cs | 210 ++++++++++++++++++++++--------------------- LibIRD/LibIRD.csproj | 5 +- LibIRD/ReIRD.cs | 11 +-- 5 files changed, 147 insertions(+), 141 deletions(-) diff --git a/IRDKit/IRDKit.csproj b/IRDKit/IRDKit.csproj index 910c6ef..c1ede4e 100644 --- a/IRDKit/IRDKit.csproj +++ b/IRDKit/IRDKit.csproj @@ -5,9 +5,11 @@ Exe irdkit irdkit - net6.0;net7.0;net8.0 + net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0 win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64 + false latest + true true 0.6.1 @@ -28,7 +30,8 @@ - + + diff --git a/IRDKit/Program.cs b/IRDKit/Program.cs index c5ddafc..658529d 100644 --- a/IRDKit/Program.cs +++ b/IRDKit/Program.cs @@ -2,13 +2,13 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; -using System.IO.Hashing; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Xml.Linq; using CommandLine; using LibIRD; +using SabreTools.Hashing; using SabreTools.RedumpLib.Web; namespace IRDKit @@ -704,7 +704,7 @@ namespace IRDKit 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])}"); + 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 @@ -720,7 +720,7 @@ namespace IRDKit if (j == -1) missingOffsets2.Add(IRD1.FileKeys[i]); 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++) { @@ -748,15 +748,15 @@ namespace IRDKit // Print any data 1 key difference 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 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 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 if (outPath != null) @@ -808,7 +808,7 @@ namespace IRDKit try { // Get disc key from hex string - byte[] discKey = Convert.FromHexString(hexKey); + byte[] discKey = LibIRD.IRD.HexStringToByteArray(hexKey); if (discKey == null || discKey.Length != 16) Console.Error.WriteLine($"{hexKey} is not a valid key, detecting key automatically..."); else @@ -844,7 +844,7 @@ namespace IRDKit Console.Error.WriteLine($"{hexKey} is not a valid key, detecting key automatically..."); 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); ird1.Write(irdPath); if (verbose) @@ -892,7 +892,7 @@ namespace IRDKit Console.Error.WriteLine($"{hexKey} is not a valid key, detecting key automatically..."); 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); ird1.Write(irdPath); if (verbose) @@ -934,22 +934,17 @@ namespace IRDKit Console.WriteLine("No key provided... Searching for key on redump.org"); // Compute CRC32 hash - byte[] crc32; - uint crc32UInt; - using (FileStream fs = File.OpenRead(isoPath)) - { - Crc32 hasher = new(); - hasher.Append(fs); - crc32 = hasher.GetCurrentHash(); - crc32UInt = BitConverter.ToUInt32(crc32); - // Change endianness - Array.Reverse(crc32); - } - string crc32_hash = Convert.ToHexString(crc32).ToLower(); + string crc32String = HashTool.GetFileHash(isoPath, HashType.CRC32); // TODO: Output byte[] once ST.Hashing supports it + uint crc32UInt = BitConverter.ToUInt32(LibIRD.IRD.HexStringToByteArray(crc32String), 0); // Search for ISO on redump.org +#if !NETFRAMEWORK RedumpHttpClient redump = new(); - List ids = redump.CheckSingleSitePage("http://redump.org/discs/system/ps3/quicksearch/" + crc32_hash).ConfigureAwait(false).GetAwaiter().GetResult(); + List ids = redump.CheckSingleSitePage("http://redump.org/discs/system/ps3/quicksearch/" + crc32String).ConfigureAwait(false).GetAwaiter().GetResult(); +#else + RedumpWebClient redump = new(); + List ids = redump.CheckSingleSitePage("http://redump.org/discs/system/ps3/quicksearch/" + crc32String); +#endif int id; if (ids.Count == 0) { @@ -965,10 +960,14 @@ namespace IRDKit SHA1 hasher = SHA1.Create(); sha1 = hasher.ComputeHash(fs); } - string sha1_hash = Convert.ToHexString(sha1).ToLower(); + string sha1_hash = LibIRD.IRD.ByteArrayToHexString(sha1).ToLower(); // Search redump.org for SHA1 hash +#if !NETFRAMEWORK List ids2 = redump.CheckSingleSitePage("http://redump.org/discs/system/ps3/quicksearch/" + sha1_hash).ConfigureAwait(false).GetAwaiter().GetResult(); +#else + List ids2 = redump.CheckSingleSitePage("http://redump.org/discs/system/ps3/quicksearch/" + sha1_hash); +#endif if (ids2.Count == 0) { Console.Error.WriteLine("ISO not found in redump and no valid key provided, cannot create IRD"); @@ -988,7 +987,11 @@ namespace IRDKit } // Download key file from redump.org +#if !NETFRAMEWORK 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) { Console.Error.WriteLine("Invalid key obtained from redump and no valid key provided, cannot create IRD"); @@ -996,7 +999,7 @@ namespace IRDKit } // 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 { IRD ird = new ReIRD(isoPath, key, layerbreak, crc32UInt); @@ -1045,7 +1048,7 @@ namespace IRDKit if (filename == null) throw new ArgumentException($"Cannot determine DAT filename for {irdPath}"); if (serial) - filename += $" [{ird.TitleID[..4]}-{ird.TitleID[4..9]}]"; + filename += $" [{ird.TitleID.Substring(0, 4)}-{ird.TitleID.Substring(5, 5)}]"; if (crc) filename += $" [{ird.UID:X8}]"; @@ -1071,7 +1074,7 @@ namespace IRDKit } } - #endregion +#endregion #region Helper Functions diff --git a/LibIRD/IRD.cs b/LibIRD/IRD.cs index d01c6b1..abd0afe 100644 --- a/LibIRD/IRD.cs +++ b/LibIRD/IRD.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; -using System.IO.Hashing; using System.Security.Cryptography; using System.Text; using LibIRD.DiscUtils; using LibIRD.DiscUtils.Iso9660; using LibIRD.DiscUtils.Streams; +using SabreTools.Hashing; namespace LibIRD { @@ -803,9 +803,11 @@ namespace LibIRD // Begin a GZip stream to write header to using MemoryStream headerStream = new(); #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 - using (GZipStream gzs = new(headerStream, CompressionLevel.Optimal)) + using GZipStream gzStream = new(headerStream, CompressionMode.Compress); #endif { // Start reading data from the beginning of the ISO file @@ -817,7 +819,7 @@ namespace LibIRD for (int i = 0; i < FirstDataSector; i++) { numBytes = fs.Read(buf, 0, buf.Length); - gzs.Write(buf, 0, numBytes); + gzStream.Write(buf, 0, numBytes); } } @@ -835,9 +837,11 @@ namespace LibIRD // Begin a GZip stream to write footer to using MemoryStream footerStream = new(); #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 - using (GZipStream gzs = new(footerStream, CompressionLevel.Optimal)) + using GZipStream gzStream = new(footerStream, CompressionMode.Compress); #endif { // Start reading data from after last file (PS3UPDAT.PUP) @@ -849,7 +853,7 @@ namespace LibIRD while (numBytes != 0) { numBytes = fs.Read(buf, 0, buf.Length); - gzs.Write(buf, 0, numBytes); + gzStream.Write(buf, 0, numBytes); } } @@ -1038,7 +1042,7 @@ namespace LibIRD { // Initialise CRC32 ISO hasher, only used if making redump-style IRD byte[] crc32; - Crc32 isoHasher = new(); + HashWrapper isoHasher = new(HashType.CRC32); // Initialise MD5 region hashes List regions = []; @@ -1077,7 +1081,7 @@ namespace LibIRD // If making redump-style IRD, save CRC32 hash to UID field if (redump) { - crc32 = isoHasher.GetCurrentHash(); + crc32 = isoHasher.CurrentHashBytes; UID = BitConverter.ToUInt32(crc32, 0); } return; @@ -1109,7 +1113,7 @@ namespace LibIRD // Hash ISO if (redump) - isoHasher.Append(new ReadOnlySpan(buf, 0, numBytes)); + isoHasher.Process(buf, 0, numBytes); // Hash regions List regionsEnded = []; @@ -1241,112 +1245,112 @@ namespace LibIRD // Create new stream to uncompressed IRD contents using MemoryStream stream = new(); - // Write IRD data to stream in order - using (BinaryWriter bw = new(stream, Encoding.UTF8, true)) + // Write IRD data to stream as UTF8 + 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 - 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++) - { - 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); + 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); + // Calculate the little-endian 32-bit "IEEE 802.3" CRC value of the IRD contents so far stream.Position = 0; - Crc32 crc32 = new(); - crc32.Append(stream); - byte[] crc = crc32.GetCurrentHash(); + string crc32String = HashTool.GetStreamHash(stream, HashType.CRC32); + byte[] crc32 = HexStringToByteArray(crc32String); // Write final CRC value to the stream - stream.Write(crc, 0, 4); + stream.Write(crc32, 0, 4); // Create the IRD file stream using FileStream fs = new(irdPath, FileMode.Create, FileAccess.Write); // Create a GZipped IRD file stream #if NET6_0_OR_GREATER using GZipStream gzStream = new(fs, CompressionLevel.SmallestSize); -#else +#elif NETCOREAPP || NET45_OR_GREATER using GZipStream gzStream = new(fs, CompressionLevel.Optimal); +#else + using GZipStream gzStream = new(fs, CompressionMode.Compress); #endif // Write entire gzipped IRD stream to file stream.Position = 0; @@ -1374,7 +1378,7 @@ namespace LibIRD // Check for IRD file signature byte[] magic = br.ReadBytes(4); - if (!((ReadOnlySpan)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"); // Read file version diff --git a/LibIRD/LibIRD.csproj b/LibIRD/LibIRD.csproj index 1175f2d..e48d273 100644 --- a/LibIRD/LibIRD.csproj +++ b/LibIRD/LibIRD.csproj @@ -2,8 +2,9 @@ - net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0 + net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0 win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64 + false latest true true @@ -28,7 +29,7 @@ - + diff --git a/LibIRD/ReIRD.cs b/LibIRD/ReIRD.cs index 27d5829..fcbc884 100644 --- a/LibIRD/ReIRD.cs +++ b/LibIRD/ReIRD.cs @@ -1,6 +1,6 @@ using System; using System.IO; -using System.IO.Hashing; +using SabreTools.Hashing; namespace LibIRD { @@ -252,13 +252,8 @@ namespace LibIRD 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(); - } + string crc32String = HashTool.GetFileHash(isoPath, HashType.CRC32); // TODO: Output byte[] once ST.Hashing supports it + byte[] crc32 = HexStringToByteArray(crc32String); // Redump ISO CRC32 hash is used as the Unique ID in the reproducible IRD return BitConverter.ToUInt32(crc32, 0);