13 Commits
Author SHA1 Message Date
Deterous 6edb959e49 Bump IRDKit to v0.6.1 2024-02-27 13:24:08 +09:00
Deterous 96b36fcabe Add rename functionality 2024-02-27 12:49:14 +09:00
Deterous 9d03dbb7b1 Perform hashing in one read pass 2024-02-20 16:37:15 +09:00
Deterous c7b49d0c80 Initial work towards one-pass hashing 2024-02-19 21:38:06 +09:00
Deterous 5ce81f9663 Bump version 2024-02-18 17:28:21 +09:00
Deterous f74220efea Read ISO in larger chunks 2024-02-18 16:59:04 +09:00
Deterous 6e529f0050 Reuse ISO hash if searching redump 2024-02-16 11:11:51 +09:00
Deterous b862057123 Only print warning for non-contiguous files 2024-02-11 12:39:13 +09:00
Deterous cf19596ea7 Alphabetical order, key file path 2024-02-08 09:38:46 +09:00
Deterous 6af41a9bb3 Include dependencies in nupkg 2024-02-07 15:43:23 +09:00
Deterous 89c23622bd Remove DiscUtils as a public dependency 2024-02-07 14:41:14 +09:00
Deterous 8ab6a9d694 Fix file hash for split files with irregular filesize 2024-02-07 09:32:56 +09:00
Deterous bc3df52e75 Cleanup 2024-02-05 09:20:59 +09:00
8 changed files with 596 additions and 266 deletions
+5 -5
View File
@@ -9,7 +9,7 @@
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
<LangVersion>latest</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>0.4.0</Version>
<Version>0.6.1</Version>
<!-- Package Properties -->
<Authors>Deterous</Authors>
@@ -27,11 +27,11 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LibIRD\LibIRD.csproj" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Core\DiscUtils.Core.csproj" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Iso9660\DiscUtils.Iso9660.csproj" />
<ProjectReference Include="..\LibIRD\LibIRD.csproj" PrivateAssets="All" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Core\DiscUtils.Core.csproj" PrivateAssets="All" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Iso9660\DiscUtils.Iso9660.csproj" PrivateAssets="All" />
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.2" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.3" />
</ItemGroup>
</Project>
+210 -51
View File
@@ -11,6 +11,7 @@ using System.IO.Hashing;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Linq;
namespace IRDKit
{
@@ -24,14 +25,14 @@ namespace IRDKit
[Verb("create", HelpText = "Create an IRD from an ISO")]
public class CreateOptions
{
[Value(0, Required = true, HelpText = "Path to an ISO file, or directory of ISO files")]
[Value(0, Required = true, HelpText = "Path to ISO file(s), or directory of ISO files")]
public IEnumerable<string> ISOPath { get; set; }
[Option('o', "output", HelpText = "Path to the IRD file to be created (will overwrite)")]
public string IRDPath { get; set; }
[Option('b', "layerbreak", HelpText = "Layerbreak value in bytes (use with BD-Video hybrid discs). Default: 12219392")]
public long? Layerbreak { get; set; }
public long? Layerbreak { get; set; }
[Option('k', "key", HelpText = "Hexadecimal representation of the disc key")]
public string Key { get; set; }
@@ -55,7 +56,7 @@ namespace IRDKit
[Verb("info", HelpText = "Print information from an IRD or ISO")]
public class InfoOptions
{
[Value(0, Required = true, HelpText = "Path to an IRD or ISO file, or directory of IRD and/or ISO files")]
[Value(0, Required = true, HelpText = "Path to IRD/ISO file(s), or directory of IRD/ISO files")]
public IEnumerable<string> InPath { get; set; }
[Option('o', "output", HelpText = "Path to the text or json file to be created (will overwrite)")]
@@ -68,9 +69,9 @@ namespace IRDKit
public bool Recurse { get; set; }
}
/// <summary>
/// IRD diff command
/// </summary>
/// <summary>
/// IRD diff command
/// </summary>
[Verb("diff", HelpText = "Compare two IRDs and print their differences")]
public class DiffOptions
{
@@ -84,6 +85,31 @@ namespace IRDKit
public string OutPath { get; set; }
}
/// <summary>
/// IRD rename command
/// </summary>
[Verb("rename", HelpText = "Rename one or more IRD files according to the redump PS3 DAT")]
public class RenameOptions
{
[Value(0, Required = true, HelpText = "Path to IRD file(s), or directory of IRD files")]
public IEnumerable<string> IRDPath { get; set; }
[Option('d', "datfile", Required = true, HelpText = "Path to the redump PS3 Datfile")]
public string DATPath { get; set; }
[Option('s', "serial", HelpText = "Appends disc serial to new IRD filename")]
public bool Serial { get; set; }
[Option('c', "crc", HelpText = "Appends ISO CRC to new IRD filename")]
public bool CRC { get; set; }
[Option('r', "recurse", HelpText = "Recurse through all subdirectories and rename all IRDs")]
public bool Recurse { get; set; }
[Option('v', "verbose", HelpText = "Print more information about the renaming")]
public bool Verbose { get; set; }
}
#endregion
#region Program
@@ -98,7 +124,7 @@ namespace IRDKit
Console.OutputEncoding = Encoding.UTF8;
// Parse arguments
var result = Parser.Default.ParseArguments<CreateOptions, InfoOptions, DiffOptions>(args).WithParsed(Run);
var result = Parser.Default.ParseArguments<CreateOptions, InfoOptions, DiffOptions, RenameOptions>(args).WithParsed(Run);
}
/// <summary>
@@ -159,11 +185,13 @@ namespace IRDKit
}
// Determine output IRD folder
string outputPath = Path.GetDirectoryName(opt.IRDPath);
string outputPath = opt.IRDPath;
if (File.Exists(opt.IRDPath))
outputPath = Path.GetDirectoryName(opt.IRDPath);
// Create an IRD file for all ISO files found
foreach (string file in isoFiles)
ISO2IRD(file, irdPath: outputPath, verbose: opt.Verbose);
foreach (string file in isoFiles.OrderBy(x => x))
ISO2IRD(file, irdPath: outputPath, keyPath: opt.KeyFile, verbose: opt.Verbose);
}
else
{
@@ -171,7 +199,7 @@ namespace IRDKit
if (!File.Exists(isoPath))
{
Console.Error.WriteLine($"ISO not found: {isoPath}");
return;
continue;
}
string irdPath;
@@ -328,6 +356,99 @@ namespace IRDKit
break;
// Process options from a 'rename' command
case RenameOptions opt:
// Validate required parameters
if (opt.IRDPath == null || !opt.IRDPath.Any())
{
Console.Error.WriteLine("Provide a valid IRD path to rename");
return;
}
// Read DAT file
XDocument datfile = DatParser(opt.DATPath);
if (datfile == null)
{
Console.Error.WriteLine("Unable to parse DAT file");
return;
}
foreach (string irdPath in opt.IRDPath)
{
// Validate IRD path
if (string.IsNullOrEmpty(irdPath))
continue;
// If directory, search for all ISOs in current directory
if (Directory.Exists(irdPath))
{
// If recurse option enabled, search recursively
IEnumerable<string> irdFiles;
if (opt.Recurse)
{
if (opt.Verbose && irdPath == ".")
Console.WriteLine($"Recursively renaming IRDs in current directory");
else if (opt.Verbose)
Console.WriteLine($"Recursively renaming IRDs in {irdPath}");
irdFiles = Directory.EnumerateFiles(irdPath, "*.ird", SearchOption.AllDirectories);
}
else
{
if (opt.Verbose && irdPath == ".")
Console.WriteLine($"Renaming IRDs in current directory");
else if (opt.Verbose)
Console.WriteLine($"Renaming IRDs in {irdPath}");
irdFiles = Directory.EnumerateFiles(irdPath, "*.ird", SearchOption.TopDirectoryOnly);
}
// Warn if no files are found
if (!irdFiles.Any())
{
if (opt.Recurse)
Console.Error.WriteLine($"No IRDs found in {irdPath} (ensure .ird extension)");
else
Console.Error.WriteLine($"No IRDs found in {irdPath} (ensure .ird extension, or try use -r)");
continue;
}
// Rename all IRD files found
foreach (string file in irdFiles.OrderBy(x => x))
{
try
{
RenameIRD(file, datfile, serial: opt.Serial, crc: opt.CRC, verbose: opt.Verbose);
}
catch (Exception e)
{
Console.Error.WriteLine(e);
}
}
}
else
{
// Check that given file exists
if (!File.Exists(irdPath))
{
Console.Error.WriteLine($"IRD not found: {irdPath}");
continue;
}
// Rename provided IRD path
try
{
RenameIRD(irdPath, datfile, serial: opt.Serial, crc: opt.CRC, verbose: opt.Verbose);
}
catch (Exception e)
{
Console.Error.WriteLine(e);
}
}
}
break;
// Unknown command
default:
break;
@@ -610,7 +731,9 @@ namespace IRDKit
missingOffsets1.Add(IRD2.FileKeys[i]);
}
// Print the file offsets that differ
printText.AppendLine($"File Offsets not Present in {irdPath1}: {string.Join(", ", missingOffsets1)}");
if (missingOffsets1.Count > 0)
printText.AppendLine($"File Offsets not Present in {irdPath1}: {string.Join(", ", missingOffsets1)}");
if (missingOffsets2.Count > 0)
printText.AppendLine($"File Offsets not Present in {irdPath2}: {string.Join(", ", missingOffsets2)}");
// Print any extra config data difference
@@ -700,16 +823,11 @@ namespace IRDKit
return irdPath;
}
}
catch (ArgumentException e)
catch (Exception e)
{
Console.Error.WriteLine(e.Message + ", failed to create IRD");
return null;
}
catch (FileNotFoundException)
{
Console.Error.WriteLine("File not found, failed to create IRD");
return null;
}
}
// Create new reproducible redump-style IRD with a given key file
@@ -717,30 +835,30 @@ namespace IRDKit
{
try
{
// If key directory was given, append filename and .key
if (Directory.Exists(keyPath))
{
keyPath = Path.Combine(keyPath, Path.ChangeExtension(Path.GetFileName(isoPath), ".key"));
}
// Read key from .key file
byte[] discKey = File.ReadAllBytes(keyPath);
if (discKey == null || discKey.Length != 16)
Console.Error.WriteLine($"{hexKey} is not a valid key, detecting key automatically...");
else
{
IRD ird1 = new ReIRD(isoPath, discKey, layerbreak);
Console.WriteLine($"Creating {irdPath} with Key: {Convert.ToHexString(discKey)}");
IRD ird1 = new ReIRD(isoPath, discKey, layerbreak);
ird1.Write(irdPath);
if (verbose)
ird1.Print();
return irdPath;
}
}
catch (ArgumentException e)
catch (Exception e)
{
Console.Error.WriteLine(e.Message + ", failed to create IRD");
return null;
}
catch (FileNotFoundException)
{
Console.Error.WriteLine("File not found, failed to create IRD");
return null;
}
}
// Create new reproducible redump-style IRD with a given GetKey log
@@ -755,16 +873,11 @@ namespace IRDKit
ird1.Print();
return irdPath;
}
catch (ArgumentException e)
catch (Exception e)
{
Console.Error.WriteLine(e.Message + ", failed to create IRD");
return null;
}
catch (FileNotFoundException)
{
Console.Error.WriteLine("File not found, failed to create IRD");
return null;
}
}
// No key provided, try search for .key file
@@ -776,29 +889,24 @@ namespace IRDKit
try
{
// Read key from .key file
byte[] discKey = File.ReadAllBytes(keyPath);
byte[] discKey = File.ReadAllBytes(keyfilePath);
if (discKey == null || discKey.Length != 16)
Console.Error.WriteLine($"{hexKey} is not a valid key, detecting key automatically...");
else
{
IRD ird1 = new ReIRD(isoPath, discKey, layerbreak);
Console.WriteLine($"Creating {irdPath} with Key: {Convert.ToHexString(discKey)}");
IRD ird1 = new ReIRD(isoPath, discKey, layerbreak);
ird1.Write(irdPath);
if (verbose)
ird1.Print();
return irdPath;
}
}
catch (ArgumentException e)
catch (Exception e)
{
Console.Error.WriteLine(e.Message + ", failed to create IRD");
return null;
}
catch (FileNotFoundException)
{
Console.Error.WriteLine("File not found, failed to create IRD");
return null;
}
}
// No key provided, try search for .getkey.log file
@@ -809,23 +917,18 @@ namespace IRDKit
// Found .getkey.log file, check it is valid
try
{
Console.WriteLine($"Creating {irdPath} with key from: {getKeyLog}");
IRD ird1 = new ReIRD(isoPath, getKeyLog);
Console.WriteLine($"Creating {irdPath} with key from: {logfilePath}");
IRD ird1 = new ReIRD(isoPath, logfilePath);
ird1.Write(irdPath);
if (verbose)
ird1.Print();
return irdPath;
}
catch (ArgumentException e)
catch (Exception e)
{
Console.Error.WriteLine(e.Message + ", failed to create IRD");
return null;
}
catch (FileNotFoundException)
{
Console.Error.WriteLine("File not found, failed to create IRD");
return null;
}
}
// No key provided, try get key from redump.org
@@ -834,11 +937,13 @@ namespace IRDKit
// 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);
}
@@ -896,24 +1001,78 @@ namespace IRDKit
Console.WriteLine($"Creating {irdPath} with Key from redump.org: {Convert.ToHexString(key)}");
try
{
IRD ird = new ReIRD(isoPath, key, layerbreak);
IRD ird = new ReIRD(isoPath, key, layerbreak, crc32UInt);
ird.Write(irdPath);
if (verbose)
ird.Print();
return irdPath;
}
catch (ArgumentException e)
catch (Exception e)
{
Console.Error.WriteLine(e.Message + ", failed to create IRD");
return null;
}
catch (FileNotFoundException)
}
public static XDocument DatParser(string datpath = null)
{
try
{
if (!File.Exists(datpath))
return null;
return XDocument.Load(datpath);
}
catch
{
Console.Error.WriteLine("File not found, failed to create IRD");
return null;
}
}
public static string GetDatFilename(IRD ird, XDocument datfile)
{
string crc32 = ird.UID.ToString("X8").ToLower();
XElement node = datfile.Root.Elements("game").Where(e => e.Element("rom").Attribute("crc").Value == crc32).FirstOrDefault() ?? throw new ArgumentException("Cannot find ISO in redump DAT");
return node.Attribute("name").Value;
}
public static void RenameIRD(string irdPath, XDocument datfile, bool serial = false, bool crc = false, bool verbose = false)
{
IRD ird = IRD.Read(irdPath);
if (ird.ExtraConfig != 0x0001)
throw new ArgumentException($"{irdPath} is not a redump-style IRD");
string filename = GetDatFilename(ird, datfile);
if (filename == null)
throw new ArgumentException($"Cannot determine DAT filename for {irdPath}");
if (serial)
filename += $" [{ird.TitleID[..4]}-{ird.TitleID[4..9]}]";
if (crc)
filename += $" [{ird.UID:X8}]";
// Rename irdPath to filename
string directory = Path.GetDirectoryName(Path.GetFullPath(filename));
string filepath;
if (!string.IsNullOrEmpty(directory))
filepath = Path.Combine(Path.GetDirectoryName(irdPath), filename + ".ird");
else
filepath = filename + ".ird";
// Rename IRD to new name
if (irdPath != filepath)
{
if (verbose)
Console.WriteLine($"Renaming {Path.GetFileName(irdPath)} to {Path.GetFileName(filepath)}");
File.Move(irdPath, filepath);
}
else
{
if (verbose)
Console.WriteLine($"Skipping {Path.GetFileName(irdPath)}, already named correctly");
}
}
#endregion
#region Helper Functions
+304 -141
View File
@@ -2,6 +2,7 @@
using DiscUtils.Iso9660;
using DiscUtils.Streams;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.IO.Hashing;
@@ -270,7 +271,7 @@ namespace LibIRD
/// <summary>
/// MD5 hashes for all decrypted files in the image
/// </summary>
/// <remarks><see cref="FileHashes"/> files, 16-bytes per hash, alternating with each <see cref="FileHashes"/> entry</remarks>
/// <remarks><see cref="FileCount"/> files, 16-bytes per hash, alternating with each <see cref="FileHashes"/> entry</remarks>
public byte[][] FileHashes { get; private set; }
/// <summary>
@@ -300,6 +301,11 @@ namespace LibIRD
/// </summary>
private long[] RegionEnd { get; set; }
/// <summary>
/// File extents to hash
/// </summary>
private Range<long, long>[][] FileExtents { get; set; }
#endregion
#region Constructors
@@ -351,11 +357,9 @@ namespace LibIRD
/// <summary>
/// Default constructor for internal derived classes only: resulting object not in usable state
/// Assumes that internally derived class will set fields in its own constructor
/// </summary>
private protected IRD()
{
// Assumes that internally derived class will set fields in its own constructor
}
private protected IRD() { }
/// <summary>
/// Constructor with given required fields
@@ -618,7 +622,7 @@ namespace LibIRD
private protected void GenerateIRD(string isoPath, bool redump = false)
{
// Parse ISO file as a file stream
using FileStream fs = new FileStream(isoPath, FileMode.Open, FileAccess.Read) ?? throw new FileNotFoundException(isoPath);
using FileStream fs = new FileStream(isoPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan) ?? throw new FileNotFoundException(isoPath);
// Validate ISO file stream
if (!CDReader.Detect(fs))
throw new InvalidFileSystemException("Not a valid ISO file");
@@ -697,30 +701,40 @@ namespace LibIRD
// Read and compress the ISO footer
GetFooter(fs);
// 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)
// Get info region info from ISO
GetRegions(fs);
// Recursively count all files in ISO to allocate file arrays
DiscDirectoryInfo rootDir = reader.GetDirectoryInfo("\\");
FileCount = 0;
CountFiles(rootDir);
FileKeys = new long[FileCount];
FileHashes = new byte[FileCount][];
// Determine file offsets and hashes
// Pre-allocate arrays and reset file count
FileKeys = new long[FileCount];
FileExtents = new Range<long, long>[FileCount][];
uint fileCount = FileCount;
FileCount = 0;
HashFiles(fs, reader, rootDir);
// Determine file offsets
GetFiles(fs, reader, rootDir);
// Resize arrays if non-contiguous files were detected
if (FileCount != fileCount)
{
Console.WriteLine($"{isoPath} contains split files: detected {FileCount} out of {fileCount} expected files");
long[] tempFileKeys = FileKeys;
Array.Resize(ref tempFileKeys, (int)FileCount);
FileKeys = tempFileKeys;
Range<long, long>[][] tempFileExtents = FileExtents;
Array.Resize(ref tempFileExtents, (int)FileCount);
FileExtents = tempFileExtents;
}
Array.Sort(FileKeys, FileHashes);
// Sort files by offset
Array.Sort(FileKeys, FileExtents);
// Calculate CRC32 hash of ISO only if generating a redump IRD and the UID is not already set
RegionHashes = new byte[RegionCount][];
FileHashes = new byte[FileCount][];
HashISO(fs, redump && UID == 0x00000000);
}
#endregion
@@ -732,7 +746,7 @@ namespace LibIRD
/// </summary>
/// <remarks>PS3UPDAT.PUP update file version number</remarks>
/// <param name="fs">ISO filestream</param>
/// <param name="reader"></param>
/// <param name="reader">CDReader</param>
/// <exception cref="InvalidFileSystemException"></exception>
private void GetSystemVersion(FileStream fs, CDReader reader)
{
@@ -775,7 +789,7 @@ namespace LibIRD
/// Retreives and stores the header
/// </summary>
/// <param name="fs">ISO filestream</param>
/// <param name="reader"></param>
/// <param name="reader">CDReader</param>
/// <exception cref="InvalidFileSystemException"></exception>
private void GetHeader(FileStream fs, CDReader reader)
{
@@ -845,11 +859,11 @@ namespace LibIRD
}
/// <summary>
/// Determines and stores the hashes for each disc region
/// Retreives and stores the Region extents
/// </summary>
/// <param name="fs"></param>
/// <param name="fs">ISO filestream</param>
/// <exception cref="InvalidFileSystemException"></exception>
private void HashRegions(FileStream fs)
private void GetRegions(FileStream fs)
{
// Determine the number of unencryted regions
fs.Seek(0, SeekOrigin.Begin);
@@ -860,7 +874,6 @@ namespace LibIRD
RegionCount = (byte)(2 * ((uint)decRegionCount[3]) - 1);
if (RegionCount <= 0)
throw new InvalidFileSystemException("No regions detected in ISO");
RegionHashes = new byte[RegionCount][];
RegionStart = new long[RegionCount];
RegionEnd = new long[RegionCount];
@@ -889,40 +902,15 @@ namespace LibIRD
RegionStart[0] = FirstDataSector;
// Remove footer from last region
RegionEnd[^1] = (UpdateEnd / SectorSize) - 1;
// Determine MD5 hashes for each region
using MD5 md5 = MD5.Create();
byte[] buf = new byte[SectorSize];
for (int i = 0; i < RegionCount; i++)
{
// Start reading data from first sector of region
fs.Seek(SectorSize * RegionStart[i], SeekOrigin.Begin);
// Compute MD5 hash for just the region portion of the ISO file
int numBytes;
for (long j = RegionStart[i]; j <= RegionEnd[i]; j++)
{
// Read one sector at a time
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");
// Process MD5 sum one sector at a time
md5.TransformBlock(buf, 0, buf.Length, null, 0);
}
// Compute and store MD5 hash of region
md5.TransformFinalBlock(buf, 0, 0);
RegionHashes[i] = md5.Hash;
}
}
/// <summary>
/// Determine and store hashes for all files and files within subdirectories recursively
/// Determine and store file extents for all files and files within subdirectories recursively
/// </summary>
/// <param name="reader"></param>
/// <param name="path"></param>
private void HashFiles(FileStream fs, CDReader reader, DiscDirectoryInfo dir)
/// <param name="fs">ISO filestream</param>
/// <param name="reader">CDReader</param>
/// <param name="dir">Folder to search for files within</param>
private void GetFiles(FileStream fs, CDReader reader, DiscDirectoryInfo dir)
{
// Process all files in current directory
foreach (DiscFileInfo fileInfo in dir.GetFiles())
@@ -930,127 +918,103 @@ namespace LibIRD
string filePath = fileInfo.FullName;
// Determine the extents of the file via clusters
Range<long, long>[] fileClusters = reader.PathToClusters(filePath);
Range<long, long>[] fileExtent = reader.PathToClusters(filePath);
// If invalid clusters were returned, we can't hash this file
if (fileClusters == null && fileClusters.Length == 0)
if (fileExtent == null && fileExtent.Length == 0)
throw new InvalidFileSystemException($"Unexpected file extents for {filePath}");
// Determine smallest file offset as first sector
long smallestOffset = fileClusters[0].Offset;
for (int i = 1; i < fileClusters.Length; i++)
long smallestOffset = fileExtent[0].Offset;
bool nonContiguous = false;
for (int i = 1; i < fileExtent.Length; i++)
{
if (fileClusters[i] == null)
if (fileExtent[i] == null)
throw new InvalidFileSystemException($"Unexpected file extents for {filePath}");
if (fileClusters[i].Offset < smallestOffset)
smallestOffset = fileClusters[i].Offset;
if (fileExtent[i].Offset * SectorSize != fileExtent[i - 1].Offset * SectorSize + fileExtent[i - 1].Count)
nonContiguous = true;
if (fileExtent[i].Offset < smallestOffset)
smallestOffset = fileExtent[i].Offset;
}
int firstSector = (int)(smallestOffset);
// If already encountered file offset, skip this file
if (Array.Exists(FileKeys, element => element == firstSector))
if (Array.Exists(FileKeys, element => element == smallestOffset))
continue;
else if (nonContiguous)
Console.WriteLine($"Non-contiguous file found: {filePath}");
if (fileClusters.Length > 1)
Console.WriteLine($"Split file detected: {filePath}");
// Add file offset to keys
FileKeys[FileCount] = firstSector;
// Determine whether file is in encrypted or decrypted region
bool encrypted = false;
for (int i = RegionCount - 1; i > 0; i--)
{
if (RegionStart[i] <= firstSector)
{
encrypted = i % 2 == 1;
break;
}
}
// Hash each non-contiguous portion of the ISO file
byte[] buf = new byte[SectorSize];
MD5 md5 = MD5.Create();
for (int i = 0; i < fileClusters.Length; i++)
{
// Start reading data from the beginning of the ISO file
fs.Seek(fileClusters[i].Offset * SectorSize, SeekOrigin.Begin);
int numBytes;
// Read all data before the first data sector
for (int j = 0; j < (fileClusters[i].Count / SectorSize); j++)
{
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");
// Decrypt sector if necessary
if (encrypted)
buf = DecryptSector(buf, firstSector + j);
// Hash sector
md5.TransformBlock(buf, 0, numBytes, null, 0);
}
// Read remaining partial sector
if (fileClusters[i].Count % SectorSize != 0)
{
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");
// 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
md5.TransformFinalBlock(buf, 0, 0);
FileHashes[FileCount] = md5.Hash;
// Add file offset to keys and extents to extents
FileKeys[FileCount] = smallestOffset;
FileExtents[FileCount] = fileExtent;
FileCount++;
}
// Recursively process all subfolders of current directory
foreach (DiscDirectoryInfo dirInfo in dir.GetDirectories())
{
HashFiles(fs, reader, dirInfo);
GetFiles(fs, reader, dirInfo);
}
}
/// <summary>
/// Decrypts a given sector byte array
/// Decrypts a given byte array of sector(s)
/// </summary>
/// <param name="sector">Byte array to be decrypted</param>
/// <param name="buffer">Byte array to be decrypted</param>
/// <param name="sectorNumber">Sector number of first sector being decrypted</param>
/// <param name="offset">Number of sectors to skip</param>
/// <param name="count">Number of sectors to decrypt, beginning from offset</param>
/// <exception cref="InvalidOperationException"></exception>
private protected byte[] DecryptSector(byte[] sector, int sectorNumber)
private protected void DecryptSectors(ref byte[] buffer, int sectorNumber, int offset = 0, int? count = null)
{
ArgumentNullException.ThrowIfNull(buffer);
if (buffer.Length == 0 || buffer.Length % SectorSize != 0)
throw new ArgumentException("Encrypted buffer must be multiple of SectorSize");
if (offset < 0 || offset >= buffer.Length / SectorSize)
throw new ArgumentException("Offset sector must be within buffer");
count ??= (int)(buffer.Length / SectorSize) - offset;
if (count < 0 || count > (buffer.Length / SectorSize) - offset)
throw new ArgumentException("Number of sectors must be within buffer");
// Setup AES decryption
using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings");
// Set AES settings
aes.Key = DiscKey;
aes.Padding = PaddingMode.None;
aes.Mode = CipherMode.CBC;
// Determine Initial Value based on sector number
byte[] iv = new byte[16];
for (int i = 0; i < 16; i++)
// Convert offset and count to number of bytes
offset *= (int)SectorSize;
count *= (int)SectorSize;
// Decrypt buffer one sector at a time
for (int i = offset; i < offset + count; i += (int)SectorSize)
{
byte a = (byte)(sectorNumber & 0xFF);
iv[16 - i - 1] = (byte)(sectorNumber & 0xFF);
sectorNumber >>= 8;
// Determine AES Initial Value based on sector number
byte[] iv = new byte[16];
int tempNum = sectorNumber;
for (int j = 0; j < 16; j++)
{
iv[16 - j - 1] = (byte)(tempNum & 0xFF);
tempNum >>= 8;
}
aes.IV = iv;
// Perform AES decryption
using MemoryStream stream = new();
using CryptoStream cs = new(stream, aes.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(buffer, i, (int)SectorSize);
cs.FlushFinalBlock();
// Write decrypted sector to output
stream.ToArray().CopyTo(buffer, i);
sectorNumber++;
}
aes.IV = iv;
// Perform AES decryption
using MemoryStream stream = new();
using ICryptoTransform dec = aes.CreateDecryptor();
using CryptoStream cs = new(stream, dec, CryptoStreamMode.Write);
cs.Write(sector, 0, sector.Length);
cs.FlushFinalBlock();
return stream.ToArray();
return;
}
/// <summary>
@@ -1064,7 +1028,201 @@ namespace LibIRD
CountFiles(dirInfo);
}
#endregion
/// <summary>
/// Calculate hashes for all region and file extents
/// </summary>
/// <param name="fs">ISO filestream</param>
/// <param name="redump">True if also calculating CRC32 of entire ISO</param>
private void HashISO(FileStream fs, bool redump)
{
// Initialise CRC32 ISO hasher, only used if making redump-style IRD
byte[] crc32;
Crc32 isoHasher = new();
// Initialise MD5 region hashes
List<int> regions = [];
MD5[] regionMD5 = new MD5[RegionCount];
for (int i = 0; i < RegionCount; i++)
{
regions.Add(i);
regionMD5[i] = MD5.Create();
}
// Initialise MD5 file hashes
List<int> files = [];
MD5[] fileMD5 = new MD5[FileCount];
for (int i = 0; i < FileCount; i++)
{
files.Add(i);
fileMD5[i] = MD5.Create();
}
// Start hashing from beginning of ISO
long currentSector = 0;
fs.Seek(currentSector, SeekOrigin.Begin);
// Read from ISO, 1024 sectors at a time
int bufSectors = 1024;
byte[] buf = new byte[bufSectors * SectorSize];
while (true)
{
// Attempt to read a full buffer
bufSectors = 1024;
int numBytes = fs.Read(buf, 0, buf.Length);
// If end of ISO reached, stop reading
if (numBytes == 0)
{
// If making redump-style IRD, save CRC32 hash to UID field
if (redump)
{
crc32 = isoHasher.GetCurrentHash();
UID = BitConverter.ToUInt32(crc32, 0);
}
return;
}
// Keep trying to read to fill buffer, remove once partial buffer hashing is supported
if (numBytes != buf.Length)
{
while (numBytes % SectorSize != 0)
{
int newNumBytes = fs.Read(buf, numBytes, buf.Length - numBytes);
numBytes += newNumBytes;
// If end of ISO reached, trim buffer and hash
if (newNumBytes == 0 && numBytes % SectorSize != 0)
{
//numBytes -= numBytes % (int)SectorSize;
Console.Error.WriteLine("ERROR: ISO filestream ended early");
break;
}
}
// Only hash portion of buffer
bufSectors = numBytes / (int)SectorSize;
if (bufSectors == 0)
Console.Error.WriteLine("ERROR: Trailing partial sector in ISO filestream");
if (numBytes > buf.Length)
throw new InvalidFileSystemException("ERROR: Read more bytes than buffer size???");
}
// Hash ISO
if (redump)
isoHasher.Append(new ReadOnlySpan<byte>(buf, 0, numBytes));
// Hash regions
List<int> regionsEnded = [];
foreach (int i in regions)
{
// Stop hashing regions if current region has not yet started (assumes regions are ordered)
if (RegionStart[i] > currentSector + bufSectors)
break;
// Skip region if it has already ended
//if (RegionEnd[i] < currentSector)
// continue;
// Check if region has ended in this buffer [We know: Start is not in the future, Ending is not in the past]
if (RegionEnd[i] < currentSector + bufSectors)
{
// Determine start byte, if region is entirely within the buffer
int startByte = RegionStart[i] > currentSector ? (int)(SectorSize * (RegionStart[i] - currentSector)) : 0;
// Determine end byte
int endByte = (int)(SectorSize * (RegionEnd[i] - currentSector + 1));
// Close region hash
regionMD5[i].TransformFinalBlock(buf, startByte, endByte - startByte);
RegionHashes[i] = regionMD5[i].Hash;
regionMD5[i].Clear();
regionsEnded.Add(i);
}
// Check if region has already begun
else if (RegionStart[i] <= currentSector)
{
// Hash buffer
regionMD5[i].TransformBlock(buf, 0, (int)SectorSize * bufSectors, null, 0);
}
// Region Start is in this buffer, ending is in the future
else
{
// Hash partial buffer
int regionStart = (int)(SectorSize * (RegionStart[i] - currentSector));
regionMD5[i].TransformBlock(buf, regionStart, (int)SectorSize * bufSectors - regionStart, null, 0);
}
}
if (regionsEnded.Count > 0)
regions.RemoveAll(item => regionsEnded.Contains(item));
// Decrypt any encrypted sectors of buffer
for (int i = 1; i < RegionCount; i += 2)
{
// If the current encrypted region is within the buffer
if (RegionStart[i] < currentSector + bufSectors
&& RegionEnd[i] >= currentSector)
{
// First sector to decrypt from
int encOffset = 0;
// Don't decrypt initial sectors if the encrypted region starts within this buffer
if (RegionStart[i] > currentSector)
encOffset = (int)(RegionStart[i] - currentSector);
// Number of sectors to decrypt
int encCount = bufSectors - encOffset;
// Don't decrypt last sectors if the encrypted region ends within this buffer
if (RegionEnd[i] < currentSector + bufSectors)
encCount -= (int)(currentSector + bufSectors - RegionEnd[i] + 1);
// Decrypt encrypted sectors
DecryptSectors(ref buf, (int)currentSector + encOffset, encOffset, encCount);
}
}
// Hash files
List<int> filesEnded = [];
foreach (int i in files)
{
// Stop hashing files if current file has not yet started (assumes FileKeys are sorted)
if (FileKeys[i] > currentSector + bufSectors)
break;
// Hash each file extent for each file
for (int j = 0; j < FileExtents[i].Length; j++)
{
// Skip hashing file extent if it has not yet started or already ended
if (FileExtents[i][j].Offset > currentSector + bufSectors
|| SectorSize* FileExtents[i][j].Offset + FileExtents[i][j].Count < SectorSize * currentSector)
continue;
// Determine first file byte location in buffer
int startByte = FileExtents[i][j].Offset > currentSector ? (int)(SectorSize * (FileExtents[i][j].Offset - currentSector)) : 0;
// Determine last file byte location in buffer
int endByte = (int)(FileExtents[i][j].Count - SectorSize * (currentSector - FileExtents[i][j].Offset));
// Don't hash more than the buffer size
endByte = endByte < bufSectors * (int)SectorSize ? endByte : bufSectors * (int)SectorSize;
// Hash portion of buffer that file exists in
fileMD5[i].TransformBlock(buf, startByte, endByte - startByte, null, 0);
}
// Check if current file has ended in this buffer (assumes last extent contains last byte)
long lastByte = SectorSize * FileExtents[i][^1].Offset + FileExtents[i][^1].Count;
if (lastByte < SectorSize * (currentSector + bufSectors)
&& lastByte > SectorSize * currentSector)
{
// Close file hash
fileMD5[i].TransformFinalBlock(buf, 0, 0);
FileHashes[i] = fileMD5[i].Hash;
fileMD5[i].Clear();
filesEnded.Add(i);
}
}
if (filesEnded.Count > 0)
files.RemoveAll(item => filesEnded.Contains(item));
currentSector += bufSectors;
}
}
#endregion
#region IRD File
@@ -1131,7 +1289,12 @@ namespace LibIRD
// Hashes for each region
for (int i = 0; i < RegionCount; i++)
bw.Write(RegionHashes[i], 0, 16);
{
if (RegionHashes[i] == null)
bw.Write(NullMD5);
else
bw.Write(RegionHashes[i], 0, 16);
}
// Number of files hashed
bw.Write(FileCount);
@@ -1250,7 +1413,7 @@ namespace LibIRD
uint fileCount = br.ReadUInt32();
long[] fileKeys = new long[fileCount];
byte[][] fileHashes = new byte[fileCount][];
for (int i = 0; i < fileCount; i++)
for (int i = 0; i < fileCount; i++)
{
fileKeys[i] = br.ReadInt64();
fileHashes[i] = br.ReadBytes(16);
+13 -4
View File
@@ -6,7 +6,7 @@
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
<LangVersion>latest</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>0.4.0</Version>
<Version>0.6.0</Version>
<PackageOutputPath>../nupkg</PackageOutputPath>
<!-- Package Properties -->
@@ -26,10 +26,19 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Core\DiscUtils.Core.csproj" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Iso9660\DiscUtils.Iso9660.csproj" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Streams\DiscUtils.Streams.csproj" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Core\DiscUtils.Core.csproj" PrivateAssets="All" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Iso9660\DiscUtils.Iso9660.csproj" PrivateAssets="All" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Streams\DiscUtils.Streams.csproj" PrivateAssets="All" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
</ItemGroup>
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="BuildOnlySettings;ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths-&gt;WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')-&gt;WithMetadataValue('PrivateAssets', 'All'))" />
</ItemGroup>
</Target>
</Project>
+7 -2
View File
@@ -27,7 +27,7 @@ namespace LibIRD
/// A field within the PS3_DISC.SFB file
/// </summary>
/// <remarks>string Key, string Value</remarks>
public Dictionary<string, string> Field { get; private set; }
public Dictionary<string, string> Field { get; private set; }
/// <summary>
/// Constructor using a PARAM.SFO file path
@@ -131,6 +131,11 @@ namespace LibIRD
}
}
/// <summary>
/// Define JSON options once
/// </summary>
private readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true };
/// <summary>
/// Prints parameters extracted from PS3_DISC.SFB to a json object
/// </summary>
@@ -138,7 +143,7 @@ namespace LibIRD
public void PrintJson(string jsonPath = null)
{
// Serialise PS3_Disc.SFB data to a JSON object
string json = JsonSerializer.Serialize(Field, new JsonSerializerOptions { WriteIndented = true });
string json = JsonSerializer.Serialize(Field, JsonOpts);
// If no path given, output to console
if (jsonPath == null)
+10 -10
View File
@@ -43,13 +43,8 @@ namespace LibIRD
/// Constructor using a PARAM.SFO file path
/// </summary>
/// <param name="sfoPath">Full file path to the PARAM.SFO file</param>
/// <exception cref="ArgumentNullException"></exception>
public ParamSFO(string sfoPath)
{
// Validate file path
if (sfoPath == null || sfoPath.Length <= 0)
throw new ArgumentNullException(nameof(sfoPath));
// Read file as a stream, and parse file
using FileStream fs = new(sfoPath, FileMode.Open, FileAccess.Read);
Parse(fs);
@@ -103,7 +98,7 @@ namespace LibIRD
- keyOffset[i];
// Read ith key name
string key = Encoding.ASCII.GetString(br.ReadBytes((int) keyLen)).TrimEnd('\0');
string key = Encoding.ASCII.GetString(br.ReadBytes((int)keyLen)).TrimEnd('\0');
// Move stream to ith data
sfoStream.Position = dataTableStart + dataOffset[i];
@@ -112,13 +107,13 @@ namespace LibIRD
Field[key] = dataFormat[i] switch
{
// Non-null-terminated UTF-8 String
0x0004 => Encoding.UTF8.GetString(br.ReadBytes((int) dataLength[i])),
0x0004 => Encoding.UTF8.GetString(br.ReadBytes((int)dataLength[i])),
// Null-terminated UTF-8 String
0x0204 => Encoding.UTF8.GetString(br.ReadBytes((int) dataLength[i])).TrimEnd('\0'),
0x0204 => Encoding.UTF8.GetString(br.ReadBytes((int)dataLength[i])).TrimEnd('\0'),
// Integer
0x0404 => br.ReadInt32().ToString(),
// Unknown data format, assume null-terminated string
_ => Encoding.UTF8.GetString(br.ReadBytes((int) dataLength[i])).TrimEnd('\0'),
_ => Encoding.UTF8.GetString(br.ReadBytes((int)dataLength[i])).TrimEnd('\0'),
};
}
}
@@ -159,6 +154,11 @@ namespace LibIRD
}
}
/// <summary>
/// Define JSON options once
/// </summary>
private readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true };
/// <summary>
/// Prints parameters extracted from PARAM.SFO to a json object
/// </summary>
@@ -166,7 +166,7 @@ namespace LibIRD
public void PrintJson(string jsonPath = null)
{
// Serialise PS3_Disc.SFB data to a JSON object
string json = JsonSerializer.Serialize(Field, new JsonSerializerOptions { WriteIndented = true });
string json = JsonSerializer.Serialize(Field, JsonOpts);
// If no path given, output to console
if (jsonPath == null)
+46 -52
View File
@@ -2,7 +2,6 @@
using System;
using System.IO;
using System.IO.Hashing;
using System.Security;
namespace LibIRD
{
@@ -60,12 +59,7 @@ namespace LibIRD
/// </summary>
/// <param name="isoPath">Path to the ISO</param>
/// <param name="getKeyLog">Path to the GetKey log file</param>
/// <exception cref="InvalidDataException"></exception>
public ReIRD(string isoPath, string getKeyLog) : base(isoPath, getKeyLog, true)
{
// Generate Unique Identifier using ISO CRC32
UID = GenerateUID(isoPath);
}
public ReIRD(string isoPath, string getKeyLog) : base(isoPath, getKeyLog, true) { }
/// <summary>
/// Constructor with optional additional region to generate a specific Disc ID
@@ -73,11 +67,12 @@ namespace LibIRD
/// <param name="isoPath">Path to the ISO</param>
/// <param name="key">Disc Key, redump-style (AES encrypted Data 1)</param>
/// <param name="layerbreak">Layerbreak value, in sectors</param>
/// <param name="uid">Unique ID, crc32 hash of ISO</param>
/// <param name="region">Disc Region</param>
public ReIRD(string isoPath, byte[] key, long? layerbreak = null, Region region = Region.NONE) : base()
public ReIRD(string isoPath, byte[] key, long? layerbreak = null, uint? uid = null, Region region = Region.NONE) : base()
{
// Generate Unique Identifier using ISO CRC32
UID = GenerateUID(isoPath);
// If the ISO CRC32 is provided, use it as the Unique ID
UID = uid == null ? 0x00000000 : (uint)uid;
// Determine ISO file size
long size = CalculateSize(isoPath);
@@ -136,7 +131,7 @@ namespace LibIRD
if (layerbreak >= 2 * BDLayerSize || layerbreak % SectorSize != 0)
throw new ArgumentException("Unexpected layerbreak value", nameof(size));
}
else
else if (size > BDLayerSize)
{
// If no layerbreak provided, ensure ISO is not BD-Video hybrid
using FileStream fs = new FileStream(isoPath, FileMode.Open, FileAccess.Read) ?? throw new FileNotFoundException(isoPath);
@@ -152,62 +147,62 @@ namespace LibIRD
if (size > BDLayerSize) // if BD-50
{
// Layer 0 start sector = 0x01000000
long l0_start_sector = 1048576;
// Layer 0 end sector = start sector + layerbreak - 2
long l0_end_sector = ((long)layerbreak / SectorSize) + l0_start_sector - 2;
// Convert end sector location to hex values for PIC
byte[] l0es = [(byte)((l0_end_sector >> 24) & 0xFF),
(byte)((l0_end_sector >> 16) & 0xFF),
(byte)((l0_end_sector >> 8) & 0xFF),
(byte)((l0_end_sector >> 0) & 0xFF)];
long l0_start_sector = 1048576;
// Layer 0 end sector = start sector + layerbreak - 2
long l0_end_sector = ((long)layerbreak / SectorSize) + l0_start_sector - 2;
// Convert end sector location to hex values for PIC
byte[] l0es = [(byte)((l0_end_sector >> 24) & 0xFF),
(byte)((l0_end_sector >> 16) & 0xFF),
(byte)((l0_end_sector >> 8) & 0xFF),
(byte)((l0_end_sector >> 0) & 0xFF)];
// Layer 1 start sector = end of disc (0x01EFFFFE) - layerbreak + 2
long l1_start_sector = 32505854 - ((long)layerbreak! / SectorSize) + 2;
// Convert start of start sector location to hex values for PIC
byte[] l1ss = [(byte)((l1_start_sector >> 24) & 0xFF),
(byte)((l1_start_sector >> 16) & 0xFF),
(byte)((l1_start_sector >> 8) & 0xFF),
(byte)((l1_start_sector >> 0) & 0xFF)];
// Total sectors used = num_sectors + Layer 0 start + sectors_between_layers (usually 0x01358C00 - 0x00CA73FE - 3)
long total_sectors = (size / SectorSize) + l0_start_sector + (l1_start_sector - l0_end_sector - 3);
byte[] ts = BitConverter.GetBytes((uint) total_sectors);
// Convert start of start sector location to hex values for PIC
byte[] l1ss = [(byte)((l1_start_sector >> 24) & 0xFF),
(byte)((l1_start_sector >> 16) & 0xFF),
(byte)((l1_start_sector >> 8) & 0xFF),
(byte)((l1_start_sector >> 0) & 0xFF)];
// Define the PIC
pic = [
// Initial portion of PIC (24 bytes)
// [4098 bytes] [2x 0x00] ["DI"] [v1] [10units] [DI num]
// Total sectors used = num_sectors + Layer 0 start + sectors_between_layers (usually 0x01358C00 - 0x00CA73FE - 3)
long total_sectors = (size / SectorSize) + l0_start_sector + (l1_start_sector - l0_end_sector - 3);
byte[] ts = BitConverter.GetBytes((uint)total_sectors);
// Define the PIC
pic = [
// Initial portion of PIC (24 bytes)
// [4098 bytes] [2x 0x00] ["DI"] [v1] [10units] [DI num]
0x10, 0x02, 0x00, 0x00, 0x44, 0x49, 0x01, 0x10, 0x00, 0x00, 0x20, 0x00,
// ["BDR"] [2 layers]
// ["BDR"] [2 layers]
0x42, 0x44, 0x4F, 0x01, 0x21, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
// Total sectors used on disc (4 bytes)
// Total sectors used on disc (4 bytes)
ts[3], ts[2], ts[1], ts[0],
// 1st Layer sector start location (4 bytes)
// 1st Layer sector start location (4 bytes)
0x00, 0x10, 0x00, 0x00,
// 1st Layer sector end location (4 bytes), 0x00CA73FE for default BD layerbreak of 12219392
// 1st Layer sector end location (4 bytes), 0x00CA73FE for default BD layerbreak of 12219392
l0es[0], l0es[1], l0es[2], l0es[3],
// 32 bytes of zeros
// 32 bytes of zeros
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Initial portion of PIC again, for 2nd layer
// ["DI"] [v1] [11unit][DI num]
// Initial portion of PIC again, for 2nd layer
// ["DI"] [v1] [11unit][DI num]
0x44, 0x49, 0x01, 0x11, 0x00, 0x01, 0x20, 0x00,
// ["BDR"] [2 layers]
// ["BDR"] [2 layers]
0x42, 0x44, 0x4F, 0x01, 0x21, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
// Total sectors used on disc
// Total sectors used on disc
ts[3], ts[2], ts[1], ts[0],
// 2nd Layer sector start location, 0x01358C00 for default BD layerbreak of 12219392
// 2nd Layer sector start location, 0x01358C00 for default BD layerbreak of 12219392
l1ss[0], l1ss[1], l1ss[2], l1ss[3],
// 2nd Layer sector end location
// 2nd Layer sector end location
0x01, 0xEF, 0xFF, 0xFE,
// Remaining 32 bytes are zeroes
// Remaining 32 bytes are zeroes
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ];
// 3k3y style: 0x03 at last byte
if (exactIRD)
pic[114] = 0x03;
// 3k3y style: 0x03 at last byte
if (exactIRD)
pic[114] = 0x03;
}
}
else // if BD-25
{
// Total sectors used on disc: num_sectors + layer_sector_end (0x00100000) - 1
@@ -233,7 +228,7 @@ namespace LibIRD
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ];
}
return pic;
}
@@ -241,7 +236,6 @@ namespace LibIRD
/// Generates the UID field by computing the CRC32 hash of the ISO
/// </summary>
/// <param name="isoPath">Path to the ISO</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="FileNotFoundException"></exception>
private static uint GenerateUID(string isoPath)
{
@@ -275,7 +269,7 @@ namespace LibIRD
/// Calculates ISO file size
/// </summary>
/// <param name="isoPath">Path to the ISO</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="FileNotFoundException"></exception>
private static long CalculateSize(string isoPath)
{