This commit is contained in:
Deterous
2023-11-10 11:55:58 +13:00
parent 9f3216efff
commit 748dfc70d4
3 changed files with 98 additions and 56 deletions
-1
View File
@@ -1,7 +1,6 @@
using LibIRD; using LibIRD;
using System; using System;
using System.IO; using System.IO;
using System.IO.Enumeration;
using System.Text; using System.Text;
namespace BuildIRD namespace BuildIRD
+3 -7
View File
@@ -131,10 +131,6 @@ namespace LibIRD
#endregion #endregion
#region Helper Functions
#endregion
#region Constructors #region Constructors
/// <summary> /// <summary>
@@ -150,7 +146,7 @@ namespace LibIRD
byte[] header, byte[] header,
byte[] footer, byte[] footer,
byte[][] regionHashes, byte[][] regionHashes,
ulong[] fileKeys, long[] fileKeys,
byte[][] fileHashes, byte[][] fileHashes,
ushort extraConfig, ushort extraConfig,
ushort attachments, ushort attachments,
@@ -535,11 +531,11 @@ namespace LibIRD
// Read file hashes // Read file hashes
uint fileCount = br.ReadUInt32(); uint fileCount = br.ReadUInt32();
ulong[] fileKeys = new ulong[fileCount]; long[] fileKeys = new long[fileCount];
byte[][] fileHashes = new byte[fileCount][]; byte[][] fileHashes = new byte[fileCount][];
for (int i = 0; i < fileCount; i++) for (int i = 0; i < fileCount; i++)
{ {
fileKeys[i] = br.ReadUInt64(); fileKeys[i] = br.ReadInt64();
fileHashes[i] = br.ReadBytes(16); fileHashes[i] = br.ReadBytes(16);
} }
+95 -48
View File
@@ -92,15 +92,15 @@ namespace LibIRD
public byte[][] RegionHashes { get; private protected set; } public byte[][] RegionHashes { get; private protected set; }
/// <summary> /// <summary>
/// Number of decrypted files in the image /// Number of files in the image
/// </summary> /// </summary>
public uint FileCount { get; private protected set; } public uint FileCount { get; private protected set; }
/// <summary> /// <summary>
/// Starting sector for each decrypted file /// Starting sector for each file
/// </summary> /// </summary>
/// <remarks><see cref="FileCount"/> files, alternating with each <see cref="FileHashes"/> entry</remarks> /// <remarks><see cref="FileCount"/> files, alternating with each <see cref="FileHashes"/> entry</remarks>
public ulong[] FileKeys { get; private protected set; } public long[] FileKeys { get; private protected set; }
/// <summary> /// <summary>
/// MD5 hashes for all decrypted files in the image /// MD5 hashes for all decrypted files in the image
@@ -125,8 +125,15 @@ namespace LibIRD
/// <remarks>Offset to use when reading the footer</remarks> /// <remarks>Offset to use when reading the footer</remarks>
private long UpdateEnd { get; set; } private long UpdateEnd { get; set; }
private long[] FileStart { get; set; } /// <summary>
private long[] FileEnd { get; set; } /// First sector of each region
/// </summary>
private long[] RegionStart { get; set; }
/// <summary>
/// Last sector of each region
/// </summary>
private long[] RegionEnd { get; set; }
#endregion #endregion
@@ -143,7 +150,7 @@ namespace LibIRD
byte[] header, byte[] header,
byte[] footer, byte[] footer,
byte[][] regionHashes, byte[][] regionHashes,
ulong[] fileKeys, long[] fileKeys,
byte[][] fileHashes) byte[][] fileHashes)
{ {
TitleID = titleID; TitleID = titleID;
@@ -212,29 +219,25 @@ namespace LibIRD
// Read and compress the ISO footer // Read and compress the ISO footer
GetFooter(fs); GetFooter(fs);
// Recursively count all files in ISO to allocate arrays // Process all regions on ISO
HashRegions(fs);
// TODO: Speed up program by hashing regions and files at the same time (read from filesystem only once)
// Recursively count all files in ISO to allocate file arrays
DiscDirectoryInfo rootDir = reader.GetDirectoryInfo("\\"); DiscDirectoryInfo rootDir = reader.GetDirectoryInfo("\\");
FileCount = 0; FileCount = 0;
CountFiles(rootDir); CountFiles(rootDir);
FileStart = new long[FileCount]; FileKeys = new long[FileCount];
FileEnd = new long[FileCount];
FileKeys = new ulong[FileCount];
FileHashes = new byte[FileCount][]; FileHashes = new byte[FileCount][];
// Determine file offsets and hashes // Determine file offsets and hashes
uint fileCount = FileCount; uint fileCount = FileCount;
FileCount = 0; FileCount = 0;
GetFileExtents(rootDir, reader); ProcessFiles(fs, reader, rootDir);
if (FileCount != fileCount) if (FileCount != fileCount)
throw new InvalidFileSystemException("Unexpected ISO filesystem error: "); throw new InvalidFileSystemException("Unexpected ISO filesystem error: ");
Array.Sort(FileKeys, FileHashes);
// Get MD5 hash for all regions and files on ISO
HashData(fs);
for (int i = 0; i < FileCount; i++)
{
FileKeys[i] = (ulong)FileStart[i];
FileHashes[i] = NullMD5;
}
} }
#endregion #endregion
@@ -375,7 +378,7 @@ namespace LibIRD
/// </summary> /// </summary>
/// <param name="fs"></param> /// <param name="fs"></param>
/// <exception cref="InvalidFileSystemException"></exception> /// <exception cref="InvalidFileSystemException"></exception>
private void HashData(FileStream fs) private void HashRegions(FileStream fs)
{ {
// Determine the number of unencryted regions // Determine the number of unencryted regions
fs.Seek(0, SeekOrigin.Begin); fs.Seek(0, SeekOrigin.Begin);
@@ -387,11 +390,11 @@ namespace LibIRD
if (RegionCount <= 0) if (RegionCount <= 0)
throw new InvalidFileSystemException("No regions detected in ISO"); throw new InvalidFileSystemException("No regions detected in ISO");
RegionHashes = new byte[RegionCount][]; RegionHashes = new byte[RegionCount][];
RegionStart = new long[RegionCount];
RegionEnd = new long[RegionCount];
// Determine the extent for each region // Determine the extent for each region
byte[] regionSector = new byte[4]; byte[] regionSector = new byte[4];
long[] regionStart = new long[RegionCount];
long[] regionEnd = new long[RegionCount];
fs.Seek(8, SeekOrigin.Begin); fs.Seek(8, SeekOrigin.Begin);
fs.Read(regionSector, 0, 4); fs.Read(regionSector, 0, 4);
Array.Reverse(regionSector, 0, 4); Array.Reverse(regionSector, 0, 4);
@@ -399,22 +402,22 @@ 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) + 1;
else else
regionStart[i] = BitConverter.ToInt32(regionSector); RegionStart[i] = BitConverter.ToInt32(regionSector);
// 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) - 1;
else else
regionEnd[i] = BitConverter.ToInt32(regionSector); RegionEnd[i] = BitConverter.ToInt32(regionSector);
} }
// 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; RegionEnd[^1] = (UpdateEnd / SectorSize) - 1;
// Determine MD5 hashes for each region // Determine MD5 hashes for each region
using MD5 md5 = MD5.Create(); using MD5 md5 = MD5.Create();
@@ -422,11 +425,11 @@ namespace LibIRD
for (int i = 0; i < RegionCount; i++) for (int i = 0; i < RegionCount; i++)
{ {
// Start reading data from first sector of region // Start reading data from first sector of region
fs.Seek(SectorSize * regionStart[i], SeekOrigin.Begin); fs.Seek(SectorSize * RegionStart[i], SeekOrigin.Begin);
// Compute MD5 hash for just the region portion of the ISO file // Compute MD5 hash for just the region portion of the ISO file
int numBytes; int numBytes;
for (long j = regionStart[i]; j <= regionEnd[i]; j++) for (long j = RegionStart[i]; j <= RegionEnd[i]; j++)
{ {
// Read one sector at a time // Read one sector at a time
numBytes = fs.Read(buf, 0, buf.Length); numBytes = fs.Read(buf, 0, buf.Length);
@@ -444,23 +447,11 @@ namespace LibIRD
} }
/// <summary> /// <summary>
/// Recursively determines file count /// Determine and store hashes for all files and files within subdirectories recursively
/// </summary>
/// <param name="dir"></param>
private void CountFiles(DiscDirectoryInfo dir)
{
FileCount += (uint)dir.GetFiles().Length;
foreach (DiscDirectoryInfo dirInfo in dir.GetDirectories())
CountFiles(dirInfo);
}
/// <summary>
/// Determine byte extents for all files and files within subdirectories recursively
/// </summary> /// </summary>
/// <param name="reader"></param> /// <param name="reader"></param>
/// <param name="dir"></param> /// <param name="path"></param>
/// <exception cref="InvalidFileSystemException"></exception> private void ProcessFiles(FileStream fs, CDReader reader, DiscDirectoryInfo dir)
private void GetFileExtents(DiscDirectoryInfo dir, CDReader reader)
{ {
// Process all files in current directory // Process all files in current directory
foreach (DiscFileInfo fileInfo in dir.GetFiles()) foreach (DiscFileInfo fileInfo in dir.GetFiles())
@@ -472,18 +463,74 @@ namespace LibIRD
throw new InvalidFileSystemException("Unexpected file extent in ISO filestream for " + filePath); throw new InvalidFileSystemException("Unexpected file extent in ISO filestream for " + filePath);
if (fileExtents.Length > 1) if (fileExtents.Length > 1)
throw new InvalidFileSystemException("Non-contiguous file detected"); throw new InvalidFileSystemException("Non-contiguous file detected");
FileStart[FileCount] = fileExtents[0].Start; long firstByte = fileExtents[0].Start;
FileEnd[FileCount] = fileExtents[0].Length; long fileLength = fileExtents[0].Length;
FileKeys[FileCount] = firstByte / 2048;
// Determine whether file is in encrypted or decrypted region
bool encrypted = false;
for (int i = RegionCount - 1; i > 0; i--)
{
if (RegionStart[i] <= firstByte / 2048)
{
encrypted = i % 2 == 1;
break;
}
}
// Decrypt file if encrypted
if (encrypted)
{
FileHashes[FileCount] = NullMD5;
FileCount++;
continue;
}
// Start reading data from the beginning of the ISO file
fs.Seek(firstByte, SeekOrigin.Begin);
byte[] buf = new byte[SectorSize];
int numBytes;
// Read all data before the first data sector
MD5 md5 = MD5.Create();
for (long i = 0; i < (fileLength / SectorSize); i++)
{
numBytes = fs.Read(buf, 0, buf.Length);
// Check that an entire sector was read
if (numBytes < buf.Length)
throw new InvalidFileSystemException("Disc region ended unexpectedly");
md5.TransformBlock(buf, 0, numBytes, null, 0);
}
// Read remaining partial sector
if (fileLength % SectorSize != 0)
{
numBytes = fs.Read(buf, 0, (int)(fileLength % SectorSize));
md5.TransformBlock(buf, 0, numBytes, null, 0);
}
// Finalise and store MD5 hash
md5.TransformFinalBlock(buf, 0, 0);
FileHashes[FileCount] = md5.Hash;
FileCount++; FileCount++;
} }
// Recursively process all subfolders of current directory // Recursively process all subfolders of current directory
foreach (DiscDirectoryInfo dirInfo in dir.GetDirectories()) foreach (DiscDirectoryInfo dirInfo in dir.GetDirectories())
{ {
GetFileExtents(dirInfo, reader); ProcessFiles(fs, reader, dirInfo);
} }
} }
/// <summary>
/// Recursively determines file count
/// </summary>
/// <param name="dir"></param>
private void CountFiles(DiscDirectoryInfo dir)
{
FileCount += (uint)dir.GetFiles().Length;
foreach (DiscDirectoryInfo dirInfo in dir.GetDirectories())
CountFiles(dirInfo);
}
#endregion #endregion
} }