Hash non-contiguous files
This commit is contained in:
+1
-1
Submodule DiscUtils updated: 59d7cadab8...24d7ddc258
+60
-75
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user