Parse .getkey.log
This commit is contained in:
@@ -8,9 +8,11 @@ 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 };
|
byte[] discKey = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||||
IRD ird = new ReIRD("./game.iso", discKey);
|
IRD ird1 = new ReIRD("./game.iso", discKey);
|
||||||
ird.Write("./test.ird");
|
IRD ird2 = new ReIRD("./game.iso", "./log.getkey.log");
|
||||||
Console.WriteLine(ird.HeaderLength);
|
ird1.Write("./test1.ird");
|
||||||
|
ird2.Write("./test2.ird");
|
||||||
|
Console.WriteLine(ird1.Title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+118
-29
@@ -180,12 +180,7 @@ namespace LibIRD
|
|||||||
/// <remarks><see cref="FileHashes"/> files, 16-bytes per hash, alternating with each <see cref="FileHashes"/> entry</remarks>
|
/// <remarks><see cref="FileHashes"/> files, 16-bytes per hash, alternating with each <see cref="FileHashes"/> entry</remarks>
|
||||||
public byte[][] FileHashes { get; private set; }
|
public byte[][] FileHashes { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
#endregion
|
||||||
/// IRD content CRC
|
|
||||||
/// </summary>
|
|
||||||
public uint CRC { get; private set; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Generate Fields
|
#region Generate Fields
|
||||||
|
|
||||||
@@ -275,8 +270,6 @@ namespace LibIRD
|
|||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
// TODO: Constructor using D1 and D2 directly, rather than Disc Key / Disc ID
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default constructor for internal derived classes only: resulting object not in usable state
|
/// Default constructor for internal derived classes only: resulting object not in usable state
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -295,27 +288,122 @@ namespace LibIRD
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Public Constructor must be called with required fields
|
/// Constructor that reads required fields from .getkey.log file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="discPath">Path to the ISO</param>
|
/// <param name="isoPath"></param>
|
||||||
|
/// <param name="getKeyLog"></param>
|
||||||
|
public IRD(string isoPath, string getKeyLog)
|
||||||
|
{
|
||||||
|
// Parse Disc Key, Disc ID, and PIC from the .getkey.log file
|
||||||
|
ParseGetKeyLog(getKeyLog);
|
||||||
|
|
||||||
|
// Generate the remaining IRD fields from the disc drive or mounted ISO
|
||||||
|
Generate(isoPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor with given required fields
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isoPath">Path to the ISO</param>
|
||||||
/// <param name="discKey">Disc Key, byte array of length 16</param>
|
/// <param name="discKey">Disc Key, byte array of length 16</param>
|
||||||
/// <param name="discID">Disc ID, byte array of length 16</param>
|
/// <param name="discID">Disc ID, byte array of length 16</param>
|
||||||
/// <param name="discPIC">Disc PIC, byte array of length 115</param>
|
/// <param name="discPIC">Disc PIC, byte array of length 115</param>
|
||||||
public IRD(string discPath, byte[] discKey, byte[] discID, byte[] discPIC)
|
public IRD(string isoPath, byte[] discKey, byte[] discID, byte[] discPIC)
|
||||||
{
|
{
|
||||||
// Store IRD fields that cannot be determined from the ISO
|
// Parse DiscKey, DiscID, and PIC
|
||||||
GenerateD1(discKey);
|
GenerateD1(discKey);
|
||||||
GenerateD2(discID);
|
GenerateD2(discID);
|
||||||
PIC = discPIC;
|
PIC = discPIC;
|
||||||
|
|
||||||
// Generate the remaining IRD fields from the disc drive or mounted ISO
|
// Generate the remaining IRD fields from the disc drive or mounted ISO
|
||||||
Generate(discPath);
|
Generate(isoPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
|
private protected void ParseGetKeyLog(string getKeyLog)
|
||||||
|
{
|
||||||
|
// Validate getKeyLog
|
||||||
|
if (getKeyLog == null || !File.Exists(getKeyLog))
|
||||||
|
throw new ArgumentNullException(nameof(getKeyLog));
|
||||||
|
|
||||||
|
// Read from .getkey.log file
|
||||||
|
byte[] discKey;
|
||||||
|
byte[] discID;
|
||||||
|
byte[] discPIC;
|
||||||
|
using (var sr = File.OpenText(getKeyLog))
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
|
||||||
|
// Look for Disc Key in log
|
||||||
|
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.Substring("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.Substring("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.Substring(0, 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++)
|
||||||
|
{
|
||||||
|
line = sr.ReadLine();
|
||||||
|
if (line == null)
|
||||||
|
throw new InvalidDataException("Incomplete PIC in .getkey.log");
|
||||||
|
discPICStr += line;
|
||||||
|
}
|
||||||
|
// 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.Substring(0, 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store values
|
||||||
|
GenerateD1(discKey);
|
||||||
|
GenerateD2(discID);
|
||||||
|
PIC = discPIC;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate IRD fields from a disc drive or mounted ISO
|
/// Generate IRD fields from a disc drive or mounted ISO
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -330,7 +418,7 @@ namespace LibIRD
|
|||||||
// TODO: Code the difficult part (remove these initial values)
|
// TODO: Code the difficult part (remove these initial values)
|
||||||
TitleID = "ABCD12345";
|
TitleID = "ABCD12345";
|
||||||
Title = "Title";
|
Title = "Title";
|
||||||
SystemVersion = "01.00";
|
SystemVersion = "1.00";
|
||||||
GameVersion = "01.00";
|
GameVersion = "01.00";
|
||||||
AppVersion = "01.00";
|
AppVersion = "01.00";
|
||||||
HeaderLength = 1;
|
HeaderLength = 1;
|
||||||
@@ -341,13 +429,14 @@ namespace LibIRD
|
|||||||
Footer[0] = 0x00;
|
Footer[0] = 0x00;
|
||||||
RegionCount = 1;
|
RegionCount = 1;
|
||||||
RegionHashes = new byte[RegionCount][];
|
RegionHashes = new byte[RegionCount][];
|
||||||
RegionHashes[0] = new byte[] { 0x00 };
|
for (int i = 0; i < RegionCount; i++)
|
||||||
|
RegionHashes[i] = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||||
FileCount = 1;
|
FileCount = 1;
|
||||||
FileKeys = new ulong[FileCount];
|
FileKeys = new ulong[FileCount];
|
||||||
FileKeys[0] = 0x00;
|
FileKeys[0] = 0x00;
|
||||||
FileHashes = new byte[FileCount][];
|
FileHashes = new byte[FileCount][];
|
||||||
FileHashes[0] = new byte[] { 0x00 };
|
for (int i = 0; i < FileCount; i++)
|
||||||
FileHashes[0][0] = 0x00;
|
FileHashes[i] = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -357,7 +446,7 @@ namespace LibIRD
|
|||||||
/// <exception cref="ArgumentException"></exception>
|
/// <exception cref="ArgumentException"></exception>
|
||||||
public void Read(string irdPath)
|
public void Read(string irdPath)
|
||||||
{
|
{
|
||||||
// TODO: Check irdPath is a valid IRD file path
|
// TODO: Validate irdPath
|
||||||
if (irdPath == null || !File.Exists(irdPath))
|
if (irdPath == null || !File.Exists(irdPath))
|
||||||
throw new ArgumentException("IRD File Path invalid", nameof(irdPath));
|
throw new ArgumentException("IRD File Path invalid", nameof(irdPath));
|
||||||
|
|
||||||
@@ -375,13 +464,14 @@ namespace LibIRD
|
|||||||
Footer[0] = 0x00;
|
Footer[0] = 0x00;
|
||||||
RegionCount = 1;
|
RegionCount = 1;
|
||||||
RegionHashes = new byte[RegionCount][];
|
RegionHashes = new byte[RegionCount][];
|
||||||
RegionHashes[0] = new byte[] { 0x00 };
|
for (int i = 0; i < RegionCount; i++)
|
||||||
|
RegionHashes[i] = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||||
FileCount = 1;
|
FileCount = 1;
|
||||||
FileKeys = new ulong[FileCount];
|
FileKeys = new ulong[FileCount];
|
||||||
FileKeys[0] = 0x00;
|
FileKeys[0] = 0x00;
|
||||||
FileHashes = new byte[FileCount][];
|
FileHashes = new byte[FileCount][];
|
||||||
FileHashes[0] = new byte[] { 0x00 };
|
for (int i = 0; i < FileCount; i++)
|
||||||
FileHashes[0][0] = 0x00;
|
FileHashes[i] = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -401,7 +491,7 @@ namespace LibIRD
|
|||||||
using (GZipStream outStream = new GZipStream(fs, CompressionLevel.Optimal))
|
using (GZipStream outStream = new GZipStream(fs, CompressionLevel.Optimal))
|
||||||
{
|
{
|
||||||
// Keep track of the little-endian 32-bit "IEEE 802.3" CRC value
|
// Keep track of the little-endian 32-bit "IEEE 802.3" CRC value
|
||||||
Crc32 crcStream = new Crc32();
|
Crc32 crc32 = new Crc32();
|
||||||
|
|
||||||
// Write IRD data to stream in order
|
// Write IRD data to stream in order
|
||||||
using (BinaryWriter bw = new BinaryWriter(outStream))
|
using (BinaryWriter bw = new BinaryWriter(outStream))
|
||||||
@@ -410,7 +500,7 @@ namespace LibIRD
|
|||||||
bw.Write(Magic);
|
bw.Write(Magic);
|
||||||
|
|
||||||
// IRD File Version
|
// IRD File Version
|
||||||
bw.Write(_version);
|
bw.Write(Version);
|
||||||
|
|
||||||
// PARAM.SFO / TITLE_ID
|
// PARAM.SFO / TITLE_ID
|
||||||
byte[] titleIDBuf = Encoding.ASCII.GetBytes(TitleID);
|
byte[] titleIDBuf = Encoding.ASCII.GetBytes(TitleID);
|
||||||
@@ -461,7 +551,10 @@ namespace LibIRD
|
|||||||
for (int i = 0; i < FileCount; i++)
|
for (int i = 0; i < FileCount; i++)
|
||||||
{
|
{
|
||||||
bw.Write(FileKeys[i]);
|
bw.Write(FileKeys[i]);
|
||||||
bw.Write(FileHashes[i], 0, 16);
|
if (FileHashes[i] == null)
|
||||||
|
bw.Write(NullMD5);
|
||||||
|
else
|
||||||
|
bw.Write(FileHashes[i], 0, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserved fields
|
// Reserved fields
|
||||||
@@ -484,12 +577,8 @@ namespace LibIRD
|
|||||||
if (_version > 7)
|
if (_version > 7)
|
||||||
bw.Write(UID);
|
bw.Write(UID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate final CRC32
|
|
||||||
CRC = BitConverter.ToUInt32(crcStream.GetCurrentHash(), 0);
|
|
||||||
|
|
||||||
// Write final CRC32 to stream
|
// Write final CRC32 to stream
|
||||||
outStream.Write(BitConverter.GetBytes(CRC), 0, 4);
|
outStream.Write(crc32.GetCurrentHash(), 0, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-3
@@ -184,6 +184,44 @@ namespace LibIRD
|
|||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor using .getkey.log for Disc Key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isoPath">Path to the ISO</param>
|
||||||
|
/// <param name="getKeyLog">Path to the GetKey log file</param>
|
||||||
|
/// <exception cref="ArgumentNullException"></exception>
|
||||||
|
/// <exception cref="FileNotFoundException"></exception>
|
||||||
|
/// <exception cref="InvalidDataException"></exception>
|
||||||
|
public ReIRD(string isoPath, string getKeyLog)
|
||||||
|
{
|
||||||
|
// Validate ISO path
|
||||||
|
if (isoPath == null || isoPath.Length <= 0)
|
||||||
|
throw new ArgumentNullException(nameof(isoPath));
|
||||||
|
|
||||||
|
// Check file exists
|
||||||
|
var iso = new FileInfo(isoPath);
|
||||||
|
if (!iso.Exists)
|
||||||
|
throw new FileNotFoundException(isoPath);
|
||||||
|
|
||||||
|
// Calculate size of ISO
|
||||||
|
long size = iso.Length;
|
||||||
|
|
||||||
|
// Parse .getkey.log for the Disc Key (Data1Key)
|
||||||
|
ParseGetKeyLog(getKeyLog);
|
||||||
|
|
||||||
|
// Generate Data 2 using Disc ID, discarding ID from getKeyLog
|
||||||
|
GenerateD2(GenerateID(size));
|
||||||
|
|
||||||
|
// Generate Disc PIC, discarding PIC from getKeyLog
|
||||||
|
GeneratePIC(size);
|
||||||
|
|
||||||
|
// Generate Unique Identifier using ISO CRC32
|
||||||
|
GenerateUID(isoPath);
|
||||||
|
|
||||||
|
// Generate the IRD hashes
|
||||||
|
Generate(isoPath);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor with required redump-style IRD fields
|
/// Constructor with required redump-style IRD fields
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -205,9 +243,6 @@ namespace LibIRD
|
|||||||
// Calculate size of ISO
|
// Calculate size of ISO
|
||||||
long size = iso.Length;
|
long size = iso.Length;
|
||||||
|
|
||||||
// Generate Unique Identifier using ISO CRC32
|
|
||||||
GenerateUID(isoPath);
|
|
||||||
|
|
||||||
// Generate Data 1 using Disc Key
|
// Generate Data 1 using Disc Key
|
||||||
GenerateD1(key);
|
GenerateD1(key);
|
||||||
|
|
||||||
@@ -217,6 +252,9 @@ namespace LibIRD
|
|||||||
// Generate Disc PIC
|
// Generate Disc PIC
|
||||||
GeneratePIC(size);
|
GeneratePIC(size);
|
||||||
|
|
||||||
|
// Generate Unique Identifier using ISO CRC32
|
||||||
|
GenerateUID(isoPath);
|
||||||
|
|
||||||
// Generate the IRD hashes
|
// Generate the IRD hashes
|
||||||
Generate(isoPath);
|
Generate(isoPath);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibIRD
|
||||||
|
{
|
||||||
|
internal class Utilities
|
||||||
|
{
|
||||||
|
// Helper function to convert a hex string to a byte array
|
||||||
|
// Source: https://codereview.stackexchange.com/a/53846
|
||||||
|
|
||||||
|
private static int HexToInt(char c)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '0':
|
||||||
|
return 0;
|
||||||
|
case '1':
|
||||||
|
return 1;
|
||||||
|
case '2':
|
||||||
|
return 2;
|
||||||
|
case '3':
|
||||||
|
return 3;
|
||||||
|
case '4':
|
||||||
|
return 4;
|
||||||
|
case '5':
|
||||||
|
return 5;
|
||||||
|
case '6':
|
||||||
|
return 6;
|
||||||
|
case '7':
|
||||||
|
return 7;
|
||||||
|
case '8':
|
||||||
|
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[,]
|
||||||
|
{
|
||||||
|
// low nibble
|
||||||
|
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
|
||||||
|
// high nibble
|
||||||
|
{0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0}
|
||||||
|
};
|
||||||
|
|
||||||
|
internal static byte[] HexToBytes(string input)
|
||||||
|
{
|
||||||
|
var result = new byte[(input.Length + 1) >> 1];
|
||||||
|
int lastcell = result.Length - 1;
|
||||||
|
int lastchar = input.Length - 1;
|
||||||
|
// count up in characters, but inside the loop will
|
||||||
|
// reference from the end of the input/output.
|
||||||
|
for (int i = 0; i < input.Length; i++)
|
||||||
|
{
|
||||||
|
// i >> 1 - (i / 2) gives the result byte offset from the end
|
||||||
|
// i & 1 - 1 if it is high-nibble, 0 for low-nibble.
|
||||||
|
result[lastcell - (i >> 1)] |= ByteLookup[i & 1, HexToInt(input[lastchar - i])];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user