Parse .getkey.log
This commit is contained in:
+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>
|
||||
public byte[][] FileHashes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// IRD content CRC
|
||||
/// </summary>
|
||||
public uint CRC { get; private set; }
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Generate Fields
|
||||
|
||||
@@ -275,8 +270,6 @@ namespace LibIRD
|
||||
|
||||
#region Constructors
|
||||
|
||||
// TODO: Constructor using D1 and D2 directly, rather than Disc Key / Disc ID
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor for internal derived classes only: resulting object not in usable state
|
||||
/// </summary>
|
||||
@@ -295,27 +288,122 @@ namespace LibIRD
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public Constructor must be called with required fields
|
||||
/// Constructor that reads required fields from .getkey.log file
|
||||
/// </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="discID">Disc ID, byte array of length 16</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);
|
||||
GenerateD2(discID);
|
||||
PIC = discPIC;
|
||||
|
||||
// Generate the remaining IRD fields from the disc drive or mounted ISO
|
||||
Generate(discPath);
|
||||
Generate(isoPath);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#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>
|
||||
/// Generate IRD fields from a disc drive or mounted ISO
|
||||
/// </summary>
|
||||
@@ -330,7 +418,7 @@ namespace LibIRD
|
||||
// TODO: Code the difficult part (remove these initial values)
|
||||
TitleID = "ABCD12345";
|
||||
Title = "Title";
|
||||
SystemVersion = "01.00";
|
||||
SystemVersion = "1.00";
|
||||
GameVersion = "01.00";
|
||||
AppVersion = "01.00";
|
||||
HeaderLength = 1;
|
||||
@@ -341,13 +429,14 @@ namespace LibIRD
|
||||
Footer[0] = 0x00;
|
||||
RegionCount = 1;
|
||||
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;
|
||||
FileKeys = new ulong[FileCount];
|
||||
FileKeys[0] = 0x00;
|
||||
FileHashes = new byte[FileCount][];
|
||||
FileHashes[0] = new byte[] { 0x00 };
|
||||
FileHashes[0][0] = 0x00;
|
||||
for (int i = 0; i < FileCount; i++)
|
||||
FileHashes[i] = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -357,7 +446,7 @@ namespace LibIRD
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public void Read(string irdPath)
|
||||
{
|
||||
// TODO: Check irdPath is a valid IRD file path
|
||||
// TODO: Validate irdPath
|
||||
if (irdPath == null || !File.Exists(irdPath))
|
||||
throw new ArgumentException("IRD File Path invalid", nameof(irdPath));
|
||||
|
||||
@@ -375,13 +464,14 @@ namespace LibIRD
|
||||
Footer[0] = 0x00;
|
||||
RegionCount = 1;
|
||||
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;
|
||||
FileKeys = new ulong[FileCount];
|
||||
FileKeys[0] = 0x00;
|
||||
FileHashes = new byte[FileCount][];
|
||||
FileHashes[0] = new byte[] { 0x00 };
|
||||
FileHashes[0][0] = 0x00;
|
||||
for (int i = 0; i < FileCount; i++)
|
||||
FileHashes[i] = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -401,7 +491,7 @@ namespace LibIRD
|
||||
using (GZipStream outStream = new GZipStream(fs, CompressionLevel.Optimal))
|
||||
{
|
||||
// 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
|
||||
using (BinaryWriter bw = new BinaryWriter(outStream))
|
||||
@@ -410,7 +500,7 @@ namespace LibIRD
|
||||
bw.Write(Magic);
|
||||
|
||||
// IRD File Version
|
||||
bw.Write(_version);
|
||||
bw.Write(Version);
|
||||
|
||||
// PARAM.SFO / TITLE_ID
|
||||
byte[] titleIDBuf = Encoding.ASCII.GetBytes(TitleID);
|
||||
@@ -461,7 +551,10 @@ namespace LibIRD
|
||||
for (int i = 0; i < FileCount; 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
|
||||
@@ -484,12 +577,8 @@ namespace LibIRD
|
||||
if (_version > 7)
|
||||
bw.Write(UID);
|
||||
}
|
||||
|
||||
// Calculate final CRC32
|
||||
CRC = BitConverter.ToUInt32(crcStream.GetCurrentHash(), 0);
|
||||
|
||||
// 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
|
||||
|
||||
/// <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>
|
||||
/// Constructor with required redump-style IRD fields
|
||||
/// </summary>
|
||||
@@ -205,9 +243,6 @@ namespace LibIRD
|
||||
// Calculate size of ISO
|
||||
long size = iso.Length;
|
||||
|
||||
// Generate Unique Identifier using ISO CRC32
|
||||
GenerateUID(isoPath);
|
||||
|
||||
// Generate Data 1 using Disc Key
|
||||
GenerateD1(key);
|
||||
|
||||
@@ -217,6 +252,9 @@ namespace LibIRD
|
||||
// Generate Disc PIC
|
||||
GeneratePIC(size);
|
||||
|
||||
// Generate Unique Identifier using ISO CRC32
|
||||
GenerateUID(isoPath);
|
||||
|
||||
// Generate the IRD hashes
|
||||
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