diff --git a/LibIRD/IRD.cs b/LibIRD/IRD.cs
index dcee529..f5d3092 100644
--- a/LibIRD/IRD.cs
+++ b/LibIRD/IRD.cs
@@ -364,7 +364,8 @@ namespace LibIRD
/// Disc Key, byte array of length 16
/// Disc ID, byte array of length 16
/// Disc PIC, byte array of length 115
- public IRD(string isoPath, byte[] discKey, byte[] discID, byte[] discPIC)
+ /// True if redump-style IRD
+ public IRD(string isoPath, byte[] discKey, byte[] discID, byte[] discPIC, bool redump = false)
{
// Parse ISO, Disc Key, Disc ID, and PIC
DiscKey = discKey;
@@ -373,7 +374,7 @@ namespace LibIRD
PIC = discPIC;
// Generate IRD files from ISO
- GenerateIRD(isoPath);
+ GenerateIRD(isoPath, redump);
}
///
@@ -381,15 +382,14 @@ namespace LibIRD
///
/// Path to the ISO
/// Path to the .getkey.log file
- ///
- ///
- public IRD(string isoPath, string getKeyLog)
+ /// True if redump-style IRD
+ public IRD(string isoPath, string getKeyLog, bool redump = false)
{
// Parse .getkey.log file
ParseGetKeyLog(getKeyLog);
// Generate IRD files from ISO
- GenerateIRD(isoPath);
+ GenerateIRD(isoPath, redump);
}
#endregion
@@ -624,10 +624,11 @@ namespace LibIRD
/// Constructor for generating values from an ISO file
///
/// Path to the ISO
+ /// True if redump-style IRD
///
///
///
- private protected void GenerateIRD(string isoPath)
+ 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);
@@ -638,15 +639,46 @@ namespace LibIRD
// New ISO Reader from DiscUtils
CDReader reader = new(fs, true, true);
+ // Redump-style IRDs set the lowest bit of ExtraConfig to 1
+ ExtraConfig |= 0x01;
+
+ // If generating redump-style IRD, read PS3 Metadata from PS3_DISC.SFB
+ if (redump)
+ {
+ using DiscUtils.Streams.SparseStream s = reader.OpenFile("PS3_DISC.SFB", FileMode.Open, FileAccess.Read);
+
+ // Parse PS3_DISC.SFB file
+ PS3_DiscSFB ps3_DiscSFB = new(s);
+
+ // Redump-style IRDs use the TITLE ID
+ if (ps3_DiscSFB.Field.ContainsKey("TITLE_ID") && ps3_DiscSFB.Field["TITLE_ID"].Length == 10 && ps3_DiscSFB.Field["TITLE_ID"][4] == '-')
+ {
+ // Use the TITLE_ID from PS3_DISC.SFB
+ TitleID = string.Concat(ps3_DiscSFB.Field["TITLE_ID"].AsSpan(0, 4), ps3_DiscSFB.Field["TITLE_ID"].AsSpan(5, 5));
+ }
+ else
+ {
+ // Valid Title ID not found in PS3_DISC.SFB, use the one in PS3_GAME/PARAM.SFO
+ redump = false;
+ }
+
+ // If the version field is present, this is a multi-game disc
+ // Redump-style IRDs use the VERSION field from PS3_DISC.SFB instead of VERSION from PARAM.SFO
+ if (ps3_DiscSFB.Field.ContainsKey("VERSION"))
+ GameVersion = ps3_DiscSFB.Field["VERSION"];
+ }
+
// Read PS3 Metadata from PARAM.SFO
using (DiscUtils.Streams.SparseStream s = reader.OpenFile("PS3_GAME\\PARAM.SFO", FileMode.Open, FileAccess.Read))
{
// Parse PARAM.SFO file
ParamSFO paramSFO = new(s);
// Store required values for IRD
- TitleID = paramSFO["TITLE_ID"];
+ if (!redump)
+ TitleID = paramSFO["TITLE_ID"];
Title = paramSFO["TITLE"];
- GameVersion = paramSFO["VERSION"];
+ if (GameVersion == null)
+ GameVersion = paramSFO["VERSION"];
AppVersion = paramSFO["APP_VER"];
}
diff --git a/LibIRD/PS3_DiscSFB.cs b/LibIRD/PS3_DiscSFB.cs
new file mode 100644
index 0000000..bfe60fa
--- /dev/null
+++ b/LibIRD/PS3_DiscSFB.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace LibIRD
+{
+ ///
+ /// PS3_DISC.SFB file parsing
+ ///
+ public class PS3_DiscSFB
+ {
+ ///
+ /// PS3_DISC.SFB file signature
+ ///
+ /// { 0x2E, 0x53, 0x46, 0x42 }
+ public static readonly string Magic = ".SFB";
+
+ ///
+ /// PS3_DISC.SFB file version
+ ///
+ /// Typically v1, { 0x00, 0x01 }
+ public ushort Version { get; private set; }
+
+ ///
+ /// A field within the PS3_DISC.SFB file
+ ///
+ public Dictionary Field { get; private set; }
+
+ ///
+ /// Constructor using a PARAM.SFO file path
+ ///
+ /// Full file path to the PS3_DISC.SFB file
+ ///
+ public PS3_DiscSFB(string sfbPath)
+ {
+ // Validate file path
+ if (sfbPath == null || sfbPath.Length <= 0)
+ throw new ArgumentNullException(nameof(sfbPath));
+
+ // Read file as a stream, and parse file
+ using FileStream fs = new(sfbPath, FileMode.Open, FileAccess.Read);
+ Parse(fs);
+ }
+
+ ///
+ /// Parse PS3_DISC.SFB from stream
+ ///
+ /// SFB file stream
+ ///
+ public PS3_DiscSFB(Stream sfbStream)
+ {
+ // Parse file stream
+ Parse(sfbStream);
+ }
+
+ ///
+ /// Read fields from PS3_DISC.SFB
+ ///
+ /// File stream for PS3_DISC.SFB
+ ///
+ private void Parse(Stream sfbStream)
+ {
+ // Read binary stream
+ using BinaryReader br = new(sfbStream);
+
+ // Check file signature is correct
+ string magic = Encoding.ASCII.GetString(br.ReadBytes(4));
+ if (magic != PS3_DiscSFB.Magic)
+ throw new FileLoadException("Unexpected PS3_DISC.SFB file");
+
+ // Read SFB file version
+ byte[] buf = br.ReadBytes(2);
+ Array.Reverse(buf);
+ Version = BitConverter.ToUInt16(buf);
+
+ // Process all field headers
+ sfbStream.Seek(0x20, SeekOrigin.Begin);
+ string field = Encoding.ASCII.GetString(br.ReadBytes(0x10)).Trim('\0');
+ Field = new Dictionary { };
+ while (field != null && field != "")
+ {
+ // Find location of value
+ buf = br.ReadBytes(4);
+ Array.Reverse(buf);
+ int offset = BitConverter.ToInt32(buf, 0);
+ buf = br.ReadBytes(4);
+ Array.Reverse(buf);
+ int length = BitConverter.ToInt32(buf, 0);
+
+ // Access and store value
+ long pos = sfbStream.Position;
+ sfbStream.Seek(offset, SeekOrigin.Begin);
+ Field[field] = Encoding.ASCII.GetString(br.ReadBytes(length)).Trim('\0');
+ sfbStream.Seek(pos + 8, SeekOrigin.Begin);
+
+ // Attempt to read new field
+ field = Encoding.ASCII.GetString(br.ReadBytes(0x10)).Trim('\0');
+ }
+ }
+
+ ///
+ /// Prints formatted parameters extracted from PS3_DISC.SFB to console
+ ///
+ public void Print()
+ {
+ // Build string from parameters
+ StringBuilder print = new();
+ print.AppendLine("PS3_DISC.SFB Contents:");
+ print.AppendLine("======================");
+
+ // Loop through all parameters in PARAM.SFO
+ foreach (KeyValuePair field in Field)
+ {
+ print.Append(field.Key);
+ print.Append(": ");
+ print.AppendLine(field.Value);
+ }
+
+ // Ensure UTF-8 will display properly
+ Console.OutputEncoding = Encoding.UTF8;
+
+ // Print formatted string
+ Console.Write(print);
+ }
+ }
+}
diff --git a/LibIRD/ParamSFO.cs b/LibIRD/ParamSFO.cs
index fed4dbb..25ea5c2 100644
--- a/LibIRD/ParamSFO.cs
+++ b/LibIRD/ParamSFO.cs
@@ -18,7 +18,7 @@ namespace LibIRD
///
/// PARAM.SFO file version
///
- /// Typically { 0x01 0x01 0x00 0x00 } (v1.1)
+ /// Typically { 0x01, 0x01, 0x00, 0x00 } (v1.1)
public uint Version { get; private set; }
///
@@ -117,7 +117,7 @@ namespace LibIRD
///
/// Constructor using a PARAM.SFO file path
///
- /// Full file path to the PARAM.SFO
+ /// Full file path to the PARAM.SFO file
///
public ParamSFO(string sfoPath)
{
@@ -143,7 +143,7 @@ namespace LibIRD
// Check file signature is correct
string magic = Encoding.ASCII.GetString(br.ReadBytes(4));
if (magic != ParamSFO.Magic)
- throw new FileLoadException("Not a valid PARAM.SFO file");
+ throw new FileLoadException("Unexpected PARAM.SFO file");
// Parse header
Version = br.ReadUInt32();
@@ -222,15 +222,14 @@ namespace LibIRD
switch (Params[i].DataFormat)
{
case 0x0404:
- print.Append(Params[i].IntValue);
- print.AppendLine();
+ print.Append(Params[i].IntValue +Environment.NewLine);
break;
default:
print.AppendLine(Params[i].StringValue);
break;
}
- print.Append('\n');
}
+ print.Append(Environment.NewLine);
// Ensure UTF-8 will display properly
Console.OutputEncoding = Encoding.UTF8;
diff --git a/LibIRD/ReIRD.cs b/LibIRD/ReIRD.cs
index 2da95f7..ab11d64 100644
--- a/LibIRD/ReIRD.cs
+++ b/LibIRD/ReIRD.cs
@@ -70,7 +70,7 @@ namespace LibIRD
///
///
///
- public ReIRD(string isoPath, string getKeyLog) : base(isoPath, getKeyLog)
+ public ReIRD(string isoPath, string getKeyLog) : base(isoPath, getKeyLog, true)
{
// Generate Unique Identifier using ISO CRC32
UID = GenerateUID(isoPath);
@@ -117,7 +117,7 @@ namespace LibIRD
PIC = GeneratePIC(Size);
// Generate IRD fields
- GenerateIRD(isoPath);
+ GenerateIRD(isoPath, true);
}
#endregion
diff --git a/PrintParams/PrintParams.cs b/PrintParams/PrintParams.cs
index d030880..f53b146 100644
--- a/PrintParams/PrintParams.cs
+++ b/PrintParams/PrintParams.cs
@@ -8,12 +8,23 @@ namespace PrintParams
{
static void Main()
{
- string filename = "./PARAM.SFO";
-
+ string filename = "./PS3_DISC.SFB";
if (File.Exists(filename))
{
- ParamSFO paramSFO = new("./PARAM.SFO");
- Console.WriteLine("PARAM.SFO for: " + paramSFO["TITLE_ID"] + '\n');
+ PS3_DiscSFB ps3_DiscSFB = new(filename);
+ Console.WriteLine("PS3_DISC.SFB for: " + ps3_DiscSFB.Field["TITLE_ID"]);
+ ps3_DiscSFB.Print();
+ }
+ else
+ {
+ Console.WriteLine(filename + " not found");
+ }
+
+ filename = "./PARAM.SFO";
+ if (File.Exists(filename))
+ {
+ ParamSFO paramSFO = new(filename);
+ Console.WriteLine("PARAM.SFO for: " + paramSFO["TITLE_ID"]);
paramSFO.Print();
}
else