Hash non-contiguous files

This commit is contained in:
Deterous
2024-01-31 14:44:35 +09:00
parent 2fe56e994e
commit e6f6d1f41a
2 changed files with 61 additions and 76 deletions
+60 -75
View File
@@ -1,5 +1,6 @@
using DiscUtils; using DiscUtils;
using DiscUtils.Iso9660; using DiscUtils.Iso9660;
using DiscUtils.Streams;
using System; using System;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
@@ -657,7 +658,7 @@ namespace LibIRD
} }
// Read PS3 Metadata from PARAM.SFO // Read PS3 Metadata from PARAM.SFO
using (DiscUtils.Streams.SparseStream s = reader.OpenFile("PS3_GAME\\PARAM.SFO", FileMode.Open, FileAccess.Read)) using (DiscUtils.Streams.SparseStream s = reader.OpenFile("PS3_GAME\\PARAM.SFO", FileMode.Open, FileAccess.Read)) // Path.DirectorySeparatorChar ?
{ {
// Parse PARAM.SFO file // Parse PARAM.SFO file
ParamSFO paramSFO = new(s); ParamSFO paramSFO = new(s);
@@ -709,7 +710,7 @@ namespace LibIRD
// TODO: Speed up program by hashing regions and files at the same time (read from filesystem only once) // 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 // Recursively count all files in ISO to allocate file arrays
DiscDirectoryInfo rootDir = reader.GetDirectoryInfo("\\"); DiscDirectoryInfo rootDir = reader.GetDirectoryInfo("\\"); // Path.DirectorySeparatorChar ?
FileCount = 0; FileCount = 0;
CountFiles(rootDir); CountFiles(rootDir);
FileKeys = new long[FileCount]; FileKeys = new long[FileCount];
@@ -738,33 +739,22 @@ namespace LibIRD
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
DiscUtils.Streams.Range<long, long>[] updateClusters = reader.PathToClusters("\\PS3_UPDATE\\PS3UPDAT.PUP"); DiscUtils.Streams.Range<long, long>[] updateClusters = reader.PathToClusters("\\PS3_UPDATE\\PS3UPDAT.PUP"); // Path.DirectorySeparatorChar ?
if (updateClusters == null || updateClusters.Length <= 0) if (updateClusters == null && updateClusters.Length == 0 && updateClusters[0] == null)
{ throw new InvalidFileSystemException("Invalid file extents for PS3UPDAT.PUP");
// 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"); // PS3UPDAT.PUP file begins at first byte of dedicated cluster
if (updateExtents == null || updateExtents.Length <= 0) UpdateOffset = SectorSize * updateClusters[0].Offset;
throw new InvalidFileSystemException("Unexpected PS3UPDAT.PUP file extent in ISO filestream"); // Update file ends at the last byte of the last cluster
// PS3UPDAT.PUP file begins at start of first extent UpdateEnd = SectorSize * updateClusters[^1].Offset + updateClusters[^1].Count;
UpdateOffset = updateExtents[0].Start;
// Update file ends at the last extent plus its length
UpdateEnd = updateExtents[^1].Start + updateExtents[^1].Length;
}
else
{
// PS3UPDAT.PUP file begins at first byte of dedicated cluster
UpdateOffset = updateClusters[0] != null ? SectorSize * updateClusters[0].Offset : 0;
// Update file ends at the last byte of the last cluster
UpdateEnd = SectorSize * (updateClusters[^1].Offset + updateClusters[^1].Count);
}
// Check PUP file Magic // Check PUP file Magic
fs.Seek(UpdateOffset, SeekOrigin.Begin); fs.Seek(UpdateOffset, SeekOrigin.Begin);
byte[] pupMagic = new byte[5]; byte[] pupMagic = new byte[5];
fs.Read(pupMagic, 0, pupMagic.Length); fs.Read(pupMagic, 0, pupMagic.Length);
// If magic is incorrect, set version to "0000" (unknown) // If magic is incorrect, set version to all nulls, "\0\0\0\0" (unknown)
if (Encoding.ASCII.GetString(pupMagic) != "SCEUF") if (Encoding.ASCII.GetString(pupMagic) != "SCEUF")
SystemVersion = "0000"; SystemVersion = "\0\0\0\0";
else else
{ {
// Determine location of version string // Determine location of version string
@@ -792,20 +782,11 @@ namespace LibIRD
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)
DiscUtils.Streams.Range<long, long>[] sfbClusters = reader.PathToClusters("\\PS3_DISC.SFB"); DiscUtils.Streams.Range<long, long>[] sfbClusters = reader.PathToClusters("\\PS3_DISC.SFB"); // Path.DirectorySeparatorChar ?
if (sfbClusters == null || sfbClusters.Length <= 0) if (sfbClusters == null && sfbClusters.Length == 0 && sfbClusters[0] == null)
{ throw new InvalidFileSystemException("Invalid file extents for PS3_DISC.SFB");
// File too small for dedicated cluster, try get the first sector from the file extents instead // End of header is at beginning of first byte of dedicated cluster
DiscUtils.Streams.StreamExtent[] sfbExtents = reader.PathToExtents("\\PS3_DISC.SFB"); FirstDataSector = sfbClusters[0].Offset;
if (sfbExtents == null || sfbExtents.Length <= 0)
throw new InvalidFileSystemException("Unexpected PS3UPDAT.PUP file extent in ISO filestream");
FirstDataSector = sfbExtents[0].Start;
}
else
{
// End of header is at beginning of first byte of dedicated cluster
FirstDataSector = sfbClusters[0] != null ? sfbClusters[0].Offset : 0;
}
// Begin a GZip stream to write header to // Begin a GZip stream to write header to
using MemoryStream headerStream = new(); using MemoryStream headerStream = new();
@@ -839,7 +820,7 @@ namespace LibIRD
using MemoryStream footerStream = new(); using MemoryStream footerStream = new();
using (GZipStream gzs = new(footerStream, CompressionLevel.SmallestSize)) using (GZipStream gzs = new(footerStream, CompressionLevel.SmallestSize))
{ {
// Start reading data from after last file // Start reading data from after last file (PS3UPDAT.PUP)
fs.Seek(UpdateEnd, SeekOrigin.Begin); fs.Seek(UpdateEnd, SeekOrigin.Begin);
byte[] buf = new byte[SectorSize]; byte[] buf = new byte[SectorSize];
int numBytes = (int)SectorSize; int numBytes = (int)SectorSize;
@@ -942,15 +923,15 @@ namespace LibIRD
{ {
string filePath = fileInfo.FullName; string filePath = fileInfo.FullName;
// Try get the first sector from the file extents instead // Determine the extents of the file via clusters
DiscUtils.Streams.StreamExtent[] fileExtents = reader.PathToExtents(filePath); DiscUtils.Streams.Range<long, long>[] fileClusters = reader.PathToClusters(filePath);
if (fileExtents == null || fileExtents.Length <= 0)
throw new InvalidFileSystemException("Unexpected file extent in ISO filestream for " + filePath); // If invalid clusters were returned, we can't hash this file
if (fileExtents.Length > 1) if (fileClusters == null && fileClusters.Length == 0 && fileClusters[0] == null)
throw new InvalidFileSystemException("Non-contiguous file detected"); throw new InvalidFileSystemException($"Unexpected file extents for {filePath}");
long firstByte = fileExtents[0].Start;
int firstSector = (int)(firstByte / 2048); // Determine file offset
long fileLength = fileExtents[0].Length; int firstSector = (int)(fileClusters[0].Offset); // TODO: Check smallest of all [i] rather than take [0]
FileKeys[FileCount] = firstSector; FileKeys[FileCount] = firstSector;
// Determine whether file is in encrypted or decrypted region // Determine whether file is in encrypted or decrypted region
@@ -964,36 +945,40 @@ namespace LibIRD
} }
} }
// Start reading data from the beginning of the ISO file // Hash each non-contiguous portion of the ISO file
fs.Seek(firstByte, SeekOrigin.Begin);
byte[] buf = new byte[SectorSize]; byte[] buf = new byte[SectorSize];
int numBytes;
// Read all data before the first data sector
MD5 md5 = MD5.Create(); MD5 md5 = MD5.Create();
for (int i = 0; i < (fileLength / SectorSize); i++) for (int i = 0; i < fileClusters.Length; i++)
{ {
numBytes = fs.Read(buf, 0, buf.Length); // Start reading data from the beginning of the ISO file
// Check that an entire sector was read fs.Seek(fileClusters[i].Offset * SectorSize, SeekOrigin.Begin);
if (numBytes < buf.Length) int numBytes;
throw new InvalidFileSystemException("Disc region ended unexpectedly"); // Read all data before the first data sector
// Decrypt sector if necessary for (int j = 0; j < (fileClusters[i].Count / SectorSize); j++)
if (encrypted) {
buf = DecryptSector(buf, firstSector + i); numBytes = fs.Read(buf, 0, buf.Length);
// Hash sector // Check that an entire sector was read
md5.TransformBlock(buf, 0, numBytes, null, 0); if (numBytes < buf.Length)
} throw new InvalidFileSystemException("Disc region ended unexpectedly");
// Read remaining partial sector // Decrypt sector if necessary
if (fileLength % SectorSize != 0) if (encrypted)
{ buf = DecryptSector(buf, firstSector + j);
numBytes = fs.Read(buf, 0, buf.Length); // Hash sector
// Check that an entire sector was read md5.TransformBlock(buf, 0, numBytes, null, 0);
if (numBytes < buf.Length) }
throw new InvalidFileSystemException("Disc region ended unexpectedly"); // Read remaining partial sector
// Decrypt partial sector if necessary if (fileClusters[i].Count % SectorSize != 0)
if (encrypted) {
buf = DecryptSector(buf, firstSector + (int)(fileLength / SectorSize)); numBytes = fs.Read(buf, 0, buf.Length);
// Hash partial sector // Check that an entire sector was read
md5.TransformBlock(buf, 0, (int)(fileLength % SectorSize), null, 0); if (numBytes < buf.Length)
throw new InvalidFileSystemException("Disc region ended unexpectedly");
// Decrypt partial sector if necessary
if (encrypted)
buf = DecryptSector(buf, firstSector + (int)(fileClusters[i].Count / SectorSize));
// Hash partial sector
md5.TransformBlock(buf, 0, (int)(fileClusters[i].Count % SectorSize), null, 0);
}
} }
// Finalise and store MD5 hash // Finalise and store MD5 hash
@@ -1262,7 +1247,7 @@ namespace LibIRD
// Read UID (for Version 8 onwards) // Read UID (for Version 8 onwards)
if (version > 7) if (version > 7)
uid = br.ReadUInt16(); uid = br.ReadUInt32();
// Read and CRC32 hash // Read and CRC32 hash
byte[] crc = br.ReadBytes(4); byte[] crc = br.ReadBytes(4);