diff --git a/LibIRD/PS3ISO.cs b/LibIRD/PS3ISO.cs
index 82b20dc..44ab798 100644
--- a/LibIRD/PS3ISO.cs
+++ b/LibIRD/PS3ISO.cs
@@ -125,6 +125,9 @@ namespace LibIRD
/// Offset to use when reading the footer
private long UpdateEnd { get; set; }
+ private long[] RegionStart { get; set; }
+ private long[] RegionEnd { get; set; }
+
#endregion
#region Constructors
@@ -135,7 +138,7 @@ namespace LibIRD
/// Path to the ISO
///
///
- ///
+ ///
internal PS3ISO(string isoPath)
{
// Validate ISO path
@@ -153,7 +156,7 @@ namespace LibIRD
using FileStream fs = new FileStream(isoPath, FileMode.Open, FileAccess.Read) ?? throw new FileNotFoundException(isoPath);
// Validate ISO file stream
if (!CDReader.Detect(fs))
- throw new InvalidDataException("Not a valid ISO file");
+ throw new InvalidFileSystemException("Not a valid ISO file");
// New ISO Reader from DiscUtils
CDReader reader = new(fs, true, true);
@@ -184,13 +187,17 @@ namespace LibIRD
// Recursively process all files in ISO
FileCount = 0;
- FileKeys = new ulong[99];
- FileHashes = new byte[99][];
- ProcessFiles(reader, "\\");
- for (int i = 0; i < FileCount; i++)
- FileKeys[i] = 0;
- for (int i = 0; i < FileCount; i++)
- FileHashes[i] = NullMD5;
+ DiscDirectoryInfo dir = reader.GetDirectoryInfo("\\");
+ CountFiles(dir);
+
+ // Determine file offsets and hashes
+ FileKeys = new ulong[FileCount];
+ FileHashes = new byte[FileCount][];
+ uint fileCount = FileCount;
+ FileCount = 0;
+ ProcessFiles(fs, reader, dir);
+ if (FileCount != fileCount)
+ throw new InvalidFileSystemException("Unexpected ISO filesystem error");
}
#endregion
@@ -203,7 +210,7 @@ namespace LibIRD
/// PS3UPDAT.PUP update file version number
/// ISO filestream
///
- ///
+ ///
private void GetSystemVersion(FileStream fs, CDReader reader)
{
// Determine PUP file offset via cluster
@@ -213,7 +220,7 @@ namespace LibIRD
// File too small for dedicated cluster, try get the offset from the file extents instead
DiscUtils.Streams.StreamExtent[] updateExtents = reader.PathToExtents("\\PS3_UPDATE\\PS3UPDAT.PUP");
if (updateExtents == null || updateExtents.Length <= 0)
- throw new InvalidDataException("Unexpected PS3UPDAT.PUP file extent in ISO filestream");
+ throw new InvalidFileSystemException("Unexpected PS3UPDAT.PUP file extent in ISO filestream");
// PS3UPDAT.PUP file begins at start of first extent
UpdateOffset = updateExtents[0].Start;
// Update file ends at the last extent plus its length
@@ -257,6 +264,7 @@ namespace LibIRD
///
/// ISO filestream
///
+ ///
private void GetHeader(FileStream fs, CDReader reader)
{
// Determine the extent of the header via cluster (Sector 0 to first data sector)
@@ -266,7 +274,7 @@ namespace LibIRD
// File too small for dedicated cluster, try get the first sector from the file extents instead
DiscUtils.Streams.StreamExtent[] sfbExtents = reader.PathToExtents("\\PS3_DISC.SFB");
if (sfbExtents == null || sfbExtents.Length <= 0)
- throw new InvalidDataException("Unexpected PS3UPDAT.PUP file extent in ISO filestream");
+ throw new InvalidFileSystemException("Unexpected PS3UPDAT.PUP file extent in ISO filestream");
FirstDataSector = sfbExtents[0].Start;
}
else
@@ -301,7 +309,6 @@ namespace LibIRD
/// Retreives and stores the footer
///
/// ISO filestream
- ///
private void GetFooter(FileStream fs)
{
// Begin a GZip stream to write footer to
@@ -326,6 +333,11 @@ namespace LibIRD
FooterLength = (uint)Footer.Length;
}
+ ///
+ /// Determines and stores the hashes for each disc region
+ ///
+ ///
+ ///
private void HashRegions(FileStream fs)
{
// Determine the number of unencryted regions
@@ -336,8 +348,10 @@ namespace LibIRD
// Total number of regions is 2x number of unencrypted regions, minus 1
RegionCount = (byte)(2 * ((uint)decRegionCount[3]) - 1);
if (RegionCount <= 0)
- throw new InvalidDataException("No regions detected in ISO");
+ throw new InvalidFileSystemException("No regions detected in ISO");
RegionHashes = new byte[RegionCount][];
+ RegionStart = new long[RegionCount];
+ RegionEnd = new long[RegionCount];
// Determine the extent for each region
byte[] regionSector = new byte[4];
@@ -353,6 +367,7 @@ namespace LibIRD
startSector[i] = BitConverter.ToInt32(regionSector) + 1;
else
startSector[i] = BitConverter.ToInt32(regionSector);
+ RegionStart[i] = startSector[i];
// Determine end sector offset of this region
fs.Read(regionSector, 0, 4);
Array.Reverse(regionSector, 0, 4);
@@ -360,6 +375,7 @@ namespace LibIRD
endSector[i] = BitConverter.ToInt32(regionSector) - 1;
else
endSector[i] = BitConverter.ToInt32(regionSector);
+ RegionEnd[i] = endSector[i];
}
// Remove header from first region
@@ -383,7 +399,7 @@ namespace LibIRD
numBytes = fs.Read(buf, 0, buf.Length);
// Check that an entire sector was read
if (numBytes < buf.Length)
- throw new InvalidDataException("Disc region ended unexpectedly");
+ throw new InvalidFileSystemException("Disc region ended unexpectedly");
// Process MD5 sum one sector at a time
md5.TransformBlock(buf, 0, buf.Length, null, 0);
}
@@ -395,35 +411,69 @@ namespace LibIRD
}
///
- /// Process all files and files within subdirectories recursively
+ /// Recursively determines file count
+ ///
+ ///
+ private void CountFiles(DiscDirectoryInfo dir)
+ {
+ foreach (DiscFileInfo _ in dir.GetFiles())
+ FileCount++;
+ foreach (DiscDirectoryInfo dirInfo in dir.GetDirectories())
+ CountFiles(dirInfo);
+ }
+
+ ///
+ /// Determine and store hashes for all files and files within subdirectories recursively
///
///
///
- private void ProcessFiles(CDReader reader, string path)
+ private void ProcessFiles(FileStream fs, CDReader reader, DiscDirectoryInfo dir)
{
- // Process current directory
- DiscDirectoryInfo dir = reader.GetDirectoryInfo(path);
-
// Process all files in current directory
foreach (DiscFileInfo fileInfo in dir.GetFiles())
{
- FileKeys[FileCount] = 0;
- FileHashes[FileCount] = NullMD5;
- FileCount += 1;
- // Save directory full path
- // Save offset
- // Save start sector (offset)
- // Save total sectors (count)
+ string filePath = fileInfo.FullName;
+ // Try get the first sector from the file extents instead
+ DiscUtils.Streams.StreamExtent[] fileExtents = reader.PathToExtents(filePath);
+ if (fileExtents == null || fileExtents.Length <= 0)
+ throw new InvalidFileSystemException("Unexpected file extent in ISO filestream for " + filePath);
+ if (fileExtents.Length > 1)
+ throw new InvalidFileSystemException("Non-contiguous file detected");
+ long firstByte = fileExtents[0].Start;
+ long fileLength = fileExtents[0].Length;
+ FileKeys[FileCount] = (ulong)firstByte / 2048;
+
+ // 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++;
}
// Recursively process all subfolders of current directory
foreach (DiscDirectoryInfo dirInfo in dir.GetDirectories())
{
- // Save directory full path
- // Save offset
- // Save start sector (offset)
- // Save total sectors (count)
- ProcessFiles(reader, dirInfo.FullName);
+ ProcessFiles(fs, reader, dirInfo);
}
}