This commit is contained in:
Deterous
2023-11-01 17:48:40 +13:00
parent 743f3210a3
commit fd18b425d1
5 changed files with 128 additions and 140 deletions
+11 -3
View File
@@ -1,5 +1,6 @@
using LibIRD; using LibIRD;
using System; using System;
using System.Text;
namespace BuildIRD namespace BuildIRD
{ {
@@ -7,12 +8,19 @@ namespace BuildIRD
{ {
static void Main() static void Main()
{ {
byte[] discKey = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; Console.OutputEncoding = Encoding.UTF8;
// Create new reproducible redump-style IRD with a key
byte[] discKey = new byte[] { 0x1A, 0x2D, 0x88, 0xFC, 0x19, 0x37, 0x27, 0x44, 0x11, 0x5E, 0xE9, 0x83, 0xA0, 0x47, 0xE2, 0xD5 };
IRD ird1 = new ReIRD("./game.iso", discKey); IRD ird1 = new ReIRD("./game.iso", discKey);
IRD ird2 = new ReIRD("./game.iso", "./log.getkey.log");
ird1.Write("./test1.ird"); ird1.Write("./test1.ird");
Console.WriteLine("IRD for " + ird1.Title + " created");
// Create new reproducible redump-style IRD with a GetKey log
string logPath = "./log.getkey.log";
IRD ird2 = new ReIRD("./game.iso", logPath);
ird2.Write("./test2.ird"); ird2.Write("./test2.ird");
Console.WriteLine(ird1.Title); Console.WriteLine("IRD for " + ird2.Title + " created");
} }
} }
} }
+95 -92
View File
@@ -8,8 +8,9 @@ using System.Text;
namespace LibIRD namespace LibIRD
{ {
/// <summary> /// <summary>
/// ISO Rebuild Data: Generation and writing to IRD file /// ISO Rebuild Data
/// </summary> /// </summary>
/// <remarks>Generates IRD fields and writes to an IRD file</remarks>
public class IRD : PS3ISO public class IRD : PS3ISO
{ {
#region Constants #region Constants
@@ -18,7 +19,7 @@ namespace LibIRD
/// IRD file signature /// IRD file signature
/// </summary> /// </summary>
/// <remarks>"3IRD"</remarks> /// <remarks>"3IRD"</remarks>
private static readonly byte[] Magic = new byte[] { 0x33, 0x49, 0x52, 0x44 }; private static readonly byte[] Magic = { 0x33, 0x49, 0x52, 0x44 };
/// <summary> /// <summary>
/// AES CBC Encryption Key for Data 1 (Disc Key) /// AES CBC Encryption Key for Data 1 (Disc Key)
@@ -66,14 +67,16 @@ namespace LibIRD
public uint UID { get; set; } = 0x00000000; // Default to zeroed UID public uint UID { get; set; } = 0x00000000; // Default to zeroed UID
/// <summary> /// <summary>
/// Extra Config, usually 0x0000 /// Extra Config
/// </summary> /// </summary>
public ushort ExtraConfig { get; set; } = 0x0000; /// <remarks>Reserved, usually set to 0x0000</remarks>
public ushort ExtraConfig { get; set; } = 0x0000; // Default to zero
/// <summary> /// <summary>
/// Attachments, usually 0x0000 /// Attachments
/// </summary> /// </summary>
public ushort Attachments { get; set; } = 0x0000; /// <remarks>Reserved, usually set to 0x0000</remarks>
public ushort Attachments { get; set; } = 0x0000; // Default to zero
/// <summary> /// <summary>
/// D1 key /// D1 key
@@ -139,12 +142,12 @@ namespace LibIRD
private protected void GenerateD1(byte[] key) private protected void GenerateD1(byte[] key)
{ {
// Validate key // Validate key
if (key == null || key.Length <= 0) if (key == null)
throw new ArgumentNullException(nameof(key)); throw new ArgumentNullException(nameof(key));
if (key.Length != 16) if (key.Length != 16)
throw new ArgumentException("Disc Key must be a byte array of length 16", nameof(key)); throw new ArgumentException("Disc Key must be a byte array of length 16", nameof(key));
// AES decryption // Setup AES decryption
using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings");
// Set AES settings // Set AES settings
@@ -154,14 +157,14 @@ namespace LibIRD
aes.Mode = CipherMode.CBC; aes.Mode = CipherMode.CBC;
// Perform AES decryption // Perform AES decryption
using ICryptoTransform decryptor = aes.CreateDecryptor(); using MemoryStream stream = new();
MemoryStream ms = new(); using ICryptoTransform dec = aes.CreateDecryptor();
CryptoStream cs = new(ms, decryptor, CryptoStreamMode.Write); using CryptoStream cs = new(stream, dec, CryptoStreamMode.Write);
cs.Write(key, 0, 16); cs.Write(key, 0, 16);
cs.FlushFinalBlock(); cs.FlushFinalBlock();
Data1Key = ms.ToArray();
ms.Close(); // Save decrypted key to field
cs.Close(); Data1Key = stream.ToArray();
} }
/// <summary> /// <summary>
@@ -173,12 +176,12 @@ namespace LibIRD
private protected void GenerateD2(byte[] id) private protected void GenerateD2(byte[] id)
{ {
// Validate id // Validate id
if (id == null || id.Length <= 0) if (id == null)
throw new ArgumentNullException(nameof(id)); throw new ArgumentNullException(nameof(id));
if (id.Length != 16) if (id.Length != 16)
throw new ArgumentException("Disc ID must be a byte array of length 16", nameof(id)); throw new ArgumentException("Disc ID must be a byte array of length 16", nameof(id));
// AES encryption // Setup AES encryption
using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings"); using Aes aes = Aes.Create() ?? throw new InvalidOperationException("AES not available. Change your system settings");
// Set AES settings // Set AES settings
@@ -188,14 +191,14 @@ namespace LibIRD
aes.Mode = CipherMode.CBC; aes.Mode = CipherMode.CBC;
// Perform AES encryption // Perform AES encryption
using ICryptoTransform encryptor = aes.CreateEncryptor(); using MemoryStream stream = new();
MemoryStream ms = new(); using ICryptoTransform enc = aes.CreateEncryptor();
CryptoStream cs = new(ms, encryptor, CryptoStreamMode.Write); using CryptoStream cs = new(stream, enc, CryptoStreamMode.Write);
cs.Write(id, 0, 16); cs.Write(id, 0, 16);
cs.FlushFinalBlock(); cs.FlushFinalBlock();
Data2Key = ms.ToArray();
ms.Close(); // Save encrypted key to field
cs.Close(); Data2Key = stream.ToArray();
} }
#endregion #endregion
@@ -207,7 +210,7 @@ namespace LibIRD
/// </summary> /// </summary>
private protected IRD(string isoPath) : base(isoPath) private protected IRD(string isoPath) : base(isoPath)
{ {
// Assumes that derived class will set private variables and generate in its own constructor // Assumes that internally derived class will set D1/D2/PIC in its own constructor
} }
/// <summary> /// <summary>
@@ -228,78 +231,78 @@ namespace LibIRD
/// <summary> /// <summary>
/// Constructor that reads required fields from .getkey.log file /// Constructor that reads required fields from .getkey.log file
/// </summary> /// </summary>
/// <param name="isoPath"></param> /// <param name="isoPath">Path to the ISO</param>
/// <param name="getKeyLog"></param> /// <param name="getKeyLog">Path to the .getkey.log file</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidDataException"></exception>
public IRD(string isoPath, string getKeyLog) : base(isoPath) public IRD(string isoPath, string getKeyLog) : base(isoPath)
{ {
// Validate .getkey.log file path // Validate .getkey.log file path
if (getKeyLog == null || !File.Exists(getKeyLog)) if (getKeyLog == null)
throw new ArgumentNullException(nameof(getKeyLog)); throw new ArgumentNullException(nameof(getKeyLog));
if (!File.Exists(getKeyLog))
// Initialise fields throw new FileNotFoundException(nameof(getKeyLog));
byte[] discKey;
byte[] discID;
byte[] discPIC;
// Read from .getkey.log file // Read from .getkey.log file
using (var sr = File.OpenText(getKeyLog)) using StreamReader sr = File.OpenText(getKeyLog);
// Determine whether GetKey was successful
string line;
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("get_dec_key succeeded!") == false) ;
if (line == null)
throw new InvalidDataException(".getkey.log contains errors");
// Look for Disc Key in log
byte[] discKey;
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("disc_key = ") == false) ;
if (line == null)
throw new InvalidDataException("Could not find Disc Key in .getkey.log");
// Get Disc Key from log
string discKeyStr = line["disc_key = ".Length..];
// Validate Disc Key from log
if (discKeyStr.Length != 32)
throw new InvalidDataException("Unexpected Disc Key in .getkey.log");
// Convert Disc Key to byte array
discKey = Utilities.HexToBytes(discKeyStr);
// Read Disc ID
byte[] discID;
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("disc_id = ") == false) ;
if (line == null)
throw new InvalidDataException("Could not find Disc ID in .getkey.log");
// Get Disc ID from log
string discIDStr = line["disc_id = ".Length..];
// Validate Disc ID from log
if (discIDStr.Length != 32)
throw new InvalidDataException("Unexpected Disc ID in .getkey.log");
// Replace X's in Disc ID with 00000001
discIDStr = discIDStr[..24] + "00000001";
// Convert Disc ID to byte array
discID = Utilities.HexToBytes(discIDStr);
// Look for PIC in log
byte[] discPIC;
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("PIC:") == false) ;
if (line == null)
throw new InvalidDataException("Could not find PIC in .getkey.log");
// Get PIC from log
string discPICStr = "";
for (int i = 0; i < 8; i++)
discPICStr += sr.ReadLine() ?? throw new InvalidDataException("Incomplete PIC in .getkey.log");
// Validate PIC from log
if (discPICStr.Length != 256)
throw new InvalidDataException("Unexpected PIC in .getkey.log");
// Convert PIC to byte array
discPIC = Utilities.HexToBytes(discPICStr[..230]);
// Double check for warnings in .getkey.log
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("WARNING") == false && line.Trim().StartsWith("SUCCESS") == false)
{ {
string line; string t = line.Trim();
if (t.StartsWith("WARNING"))
// Determine whether GetKey was successful
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("get_dec_key succeeded!") == false) ;
if (line == null)
throw new InvalidDataException(".getkey.log contains errors"); throw new InvalidDataException(".getkey.log contains errors");
else if (t.StartsWith("SUCCESS"))
// Look for Disc Key in log break;
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("disc_key = ") == false) ;
if (line == null)
throw new InvalidDataException("Could not find Disc Key in .getkey.log");
// Get Disc Key from log
string discKeyStr = line["disc_key = ".Length..];
// Validate Disc Key from log
if (discKeyStr.Length != 32)
throw new InvalidDataException("Unexpected Disc Key in .getkey.log");
// Convert Disc Key to byte array
discKey = Utilities.HexToBytes(discKeyStr);
// Read Disc ID
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("disc_id = ") == false) ;
if (line == null)
throw new InvalidDataException("Could not find Disc ID in .getkey.log");
// Get Disc ID from log
string discIDStr = line["disc_id = ".Length..];
// Validate Disc ID from log
if (discIDStr.Length != 32)
throw new InvalidDataException("Unexpected Disc ID in .getkey.log");
// Replace X's in Disc ID with 00000001
discIDStr = discIDStr[..24] + "00000001";
// Convert Disc ID to byte array
discID = Utilities.HexToBytes(discIDStr);
// Look for PIC in log
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("PIC:") == false) ;
if (line == null)
throw new InvalidDataException("Could not find PIC in .getkey.log");
// Get PIC from log
string discPICStr = "";
for (int i = 0; i < 8; i++)
discPICStr += sr.ReadLine() ?? throw new InvalidDataException("Incomplete PIC in .getkey.log");
// Validate PIC from log
if (discPICStr.Length != 256)
throw new InvalidDataException("Unexpected PIC in .getkey.log");
// Convert PIC to byte array
discPIC = Utilities.HexToBytes(discPICStr[..230]);
// Check for warnings in .getkey.log
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("WARNING") == false && line.Trim().StartsWith("SUCCESS") == false)
{
string t = line.Trim();
if (t.StartsWith("WARNING"))
throw new InvalidDataException(".getkey.log contains errors");
else if (t.StartsWith("SUCCESS"))
break;
}
} }
// Parse DiscKey, DiscID, and PIC // Parse DiscKey, DiscID, and PIC
@@ -315,15 +318,16 @@ namespace LibIRD
/// <summary> /// <summary>
/// Write IRD data to file /// Write IRD data to file
/// </summary> /// </summary>
/// <param name="irdPath">Path to IRD file to be written to</param> /// <param name="irdPath">Path to the ISO</param>
/// <exception cref="ArgumentNullException"></exception>
public void Write(string irdPath) public void Write(string irdPath)
{ {
// Validate irdPath // Validate irdPath
if (irdPath == null || irdPath.Length <= 0) if (irdPath == null || irdPath.Length <= 0)
throw new ArgumentNullException(nameof(irdPath)); throw new ArgumentNullException(nameof(irdPath));
// Create new stream to write to // Create new stream to uncompressed IRD contents
MemoryStream stream = new(); using MemoryStream stream = new();
// Write IRD data to stream in order // Write IRD data to stream in order
using (BinaryWriter bw = new(stream, Encoding.UTF8, true)) using (BinaryWriter bw = new(stream, Encoding.UTF8, true))
@@ -410,7 +414,7 @@ namespace LibIRD
bw.Write(UID); bw.Write(UID);
} }
// Calculate the little-endian 32-bit "IEEE 802.3" CRC value of the entire stream // Calculate the little-endian 32-bit "IEEE 802.3" CRC value of the IRD contents so far
stream.Position = 0; stream.Position = 0;
Crc32 crc32 = new(); Crc32 crc32 = new();
crc32.Append(stream); crc32.Append(stream);
@@ -426,7 +430,6 @@ namespace LibIRD
// Write entire gzipped IRD stream to file // Write entire gzipped IRD stream to file
stream.Position = 0; stream.Position = 0;
stream.CopyTo(gzStream); stream.CopyTo(gzStream);
stream.Close();
} }
#endregion #endregion
+1 -1
View File
@@ -195,7 +195,7 @@ namespace LibIRD
RegionHashes = new byte[RegionCount][]; RegionHashes = new byte[RegionCount][];
for (int i = 0; i < RegionCount; i++) for (int i = 0; i < RegionCount; i++)
RegionHashes[i] = NullMD5; RegionHashes[i] = NullMD5;
FileCount = 1; FileCount = 13;
FileKeys = new ulong[FileCount]; FileKeys = new ulong[FileCount];
for (int i = 0; i < FileCount; i++) for (int i = 0; i < FileCount; i++)
FileKeys[i] = (ulong)(i * 16); FileKeys[i] = (ulong)(i * 16);
+20 -43
View File
@@ -5,53 +5,30 @@ namespace LibIRD
internal class Utilities internal class Utilities
{ {
// Helper function to convert a hex string to a byte array // Helper function to convert a hex string to a byte array
// Source: https://codereview.stackexchange.com/a/53846 // Original source: https://codereview.stackexchange.com/a/53846
private static int HexToInt(char c) private static int HexToInt(char c)
{ {
switch (c) return c switch
{ {
case '0': '0' => 0,
return 0; '1' => 1,
case '1': '2' => 2,
return 1; '3' => 3,
case '2': '4' => 4,
return 2; '5' => 5,
case '3': '6' => 6,
return 3; '7' => 7,
case '4': '8' => 8,
return 4; '9' => 9,
case '5': 'a' or 'A' => 10,
return 5; 'b' or 'B' => 11,
case '6': 'c' or 'C' => 12,
return 6; 'd' or 'D' => 13,
case '7': 'e' or 'E' => 14,
return 7; 'f' or 'F' => 15,
case '8': _ => throw new FormatException("Unrecognized hex char " + c),
return 8; };
case '9':
return 9;
case 'a':
case 'A':
return 10;
case 'b':
case 'B':
return 11;
case 'c':
case 'C':
return 12;
case 'd':
case 'D':
return 13;
case 'e':
case 'E':
return 14;
case 'f':
case 'F':
return 15;
default:
throw new FormatException("Unrecognized hex char " + c);
}
} }
private static readonly byte[,] ByteLookup = new byte[,] private static readonly byte[,] ByteLookup = new byte[,]
+1 -1
View File
@@ -12,7 +12,7 @@ namespace PrintParams
if (File.Exists(filename)) if (File.Exists(filename))
{ {
ParamSFO paramSFO = new ParamSFO("./PARAM.SFO"); ParamSFO paramSFO = new("./PARAM.SFO");
Console.WriteLine(paramSFO["TITLE_ID"]); Console.WriteLine(paramSFO["TITLE_ID"]);
paramSFO.Print(); paramSFO.Print();
} }