Parse PS3_DISC.SFB for redump-style IRDs

This commit is contained in:
Deterous
2023-11-11 19:50:50 +13:00
parent 5a9cce39ae
commit b794ec8f96
5 changed files with 190 additions and 21 deletions
+41 -9
View File
@@ -364,7 +364,8 @@ namespace LibIRD
/// <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 isoPath, byte[] discKey, byte[] discID, byte[] discPIC) /// <param name="redump">True if redump-style IRD</param>
public IRD(string isoPath, byte[] discKey, byte[] discID, byte[] discPIC, bool redump = false)
{ {
// Parse ISO, Disc Key, Disc ID, and PIC // Parse ISO, Disc Key, Disc ID, and PIC
DiscKey = discKey; DiscKey = discKey;
@@ -373,7 +374,7 @@ namespace LibIRD
PIC = discPIC; PIC = discPIC;
// Generate IRD files from ISO // Generate IRD files from ISO
GenerateIRD(isoPath); GenerateIRD(isoPath, redump);
} }
/// <summary> /// <summary>
@@ -381,15 +382,14 @@ namespace LibIRD
/// </summary> /// </summary>
/// <param name="isoPath">Path to the ISO</param> /// <param name="isoPath">Path to the ISO</param>
/// <param name="getKeyLog">Path to the .getkey.log file</param> /// <param name="getKeyLog">Path to the .getkey.log file</param>
/// <exception cref="ArgumentNullException"></exception> /// <param name="redump">True if redump-style IRD</param>
/// <exception cref="InvalidDataException"></exception> public IRD(string isoPath, string getKeyLog, bool redump = false)
public IRD(string isoPath, string getKeyLog)
{ {
// Parse .getkey.log file // Parse .getkey.log file
ParseGetKeyLog(getKeyLog); ParseGetKeyLog(getKeyLog);
// Generate IRD files from ISO // Generate IRD files from ISO
GenerateIRD(isoPath); GenerateIRD(isoPath, redump);
} }
#endregion #endregion
@@ -624,10 +624,11 @@ namespace LibIRD
/// Constructor for generating values from an ISO file /// Constructor for generating values from an ISO file
/// </summary> /// </summary>
/// <param name="isoPath">Path to the ISO</param> /// <param name="isoPath">Path to the ISO</param>
/// <param name="redump">True if redump-style IRD</param>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="FileNotFoundException"></exception> /// <exception cref="FileNotFoundException"></exception>
/// <exception cref="InvalidFileSystemException"></exception> /// <exception cref="InvalidFileSystemException"></exception>
private protected void GenerateIRD(string isoPath) private protected void GenerateIRD(string isoPath, bool redump = false)
{ {
// Parse ISO file as a file stream // 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) ?? throw new FileNotFoundException(isoPath);
@@ -638,15 +639,46 @@ namespace LibIRD
// New ISO Reader from DiscUtils // New ISO Reader from DiscUtils
CDReader reader = new(fs, true, true); 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 // Read PS3 Metadata from PARAM.SFO
using (DiscUtils.Streams.SparseStream s = reader.OpenFile("PS3_GAME\\PARAM.SFO", FileMode.Open, FileAccess.Read)) using (DiscUtils.Streams.SparseStream s = reader.OpenFile("PS3_GAME\\PARAM.SFO", FileMode.Open, FileAccess.Read))
{ {
// Parse PARAM.SFO file // Parse PARAM.SFO file
ParamSFO paramSFO = new(s); ParamSFO paramSFO = new(s);
// Store required values for IRD // Store required values for IRD
TitleID = paramSFO["TITLE_ID"]; if (!redump)
TitleID = paramSFO["TITLE_ID"];
Title = paramSFO["TITLE"]; Title = paramSFO["TITLE"];
GameVersion = paramSFO["VERSION"]; if (GameVersion == null)
GameVersion = paramSFO["VERSION"];
AppVersion = paramSFO["APP_VER"]; AppVersion = paramSFO["APP_VER"];
} }
+127
View File
@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace LibIRD
{
/// <summary>
/// PS3_DISC.SFB file parsing
/// </summary>
public class PS3_DiscSFB
{
/// <summary>
/// PS3_DISC.SFB file signature
/// </summary>
/// <remarks>{ 0x2E, 0x53, 0x46, 0x42 }</remarks>
public static readonly string Magic = ".SFB";
/// <summary>
/// PS3_DISC.SFB file version
/// </summary>
/// <remarks>Typically v1, { 0x00, 0x01 }</remarks>
public ushort Version { get; private set; }
/// <summary>
/// A field within the PS3_DISC.SFB file
/// </summary>
public Dictionary<string, string> Field { get; private set; }
/// <summary>
/// Constructor using a PARAM.SFO file path
/// </summary>
/// <param name="sfbPath">Full file path to the PS3_DISC.SFB file</param>
/// <exception cref="ArgumentNullException"></exception>
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);
}
/// <summary>
/// Parse PS3_DISC.SFB from stream
/// </summary>
/// <param name="sfbStream">SFB file stream</param>
/// <exception cref="FileLoadException"></exception>
public PS3_DiscSFB(Stream sfbStream)
{
// Parse file stream
Parse(sfbStream);
}
/// <summary>
/// Read fields from PS3_DISC.SFB
/// </summary>
/// <param name="sfbStream">File stream for PS3_DISC.SFB</param>
/// <exception cref="FileLoadException"></exception>
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<string, string> { };
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');
}
}
/// <summary>
/// Prints formatted parameters extracted from PS3_DISC.SFB to console
/// </summary>
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<string, string> 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);
}
}
}
+5 -6
View File
@@ -18,7 +18,7 @@ namespace LibIRD
/// <summary> /// <summary>
/// PARAM.SFO file version /// PARAM.SFO file version
/// </summary> /// </summary>
/// <remarks>Typically { 0x01 0x01 0x00 0x00 } (v1.1)</remarks> /// <remarks>Typically { 0x01, 0x01, 0x00, 0x00 } (v1.1)</remarks>
public uint Version { get; private set; } public uint Version { get; private set; }
/// <summary> /// <summary>
@@ -117,7 +117,7 @@ namespace LibIRD
/// <summary> /// <summary>
/// Constructor using a PARAM.SFO file path /// Constructor using a PARAM.SFO file path
/// </summary> /// </summary>
/// <param name="sfoPath">Full file path to the PARAM.SFO</param> /// <param name="sfoPath">Full file path to the PARAM.SFO file</param>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
public ParamSFO(string sfoPath) public ParamSFO(string sfoPath)
{ {
@@ -143,7 +143,7 @@ namespace LibIRD
// Check file signature is correct // Check file signature is correct
string magic = Encoding.ASCII.GetString(br.ReadBytes(4)); string magic = Encoding.ASCII.GetString(br.ReadBytes(4));
if (magic != ParamSFO.Magic) if (magic != ParamSFO.Magic)
throw new FileLoadException("Not a valid PARAM.SFO file"); throw new FileLoadException("Unexpected PARAM.SFO file");
// Parse header // Parse header
Version = br.ReadUInt32(); Version = br.ReadUInt32();
@@ -222,15 +222,14 @@ namespace LibIRD
switch (Params[i].DataFormat) switch (Params[i].DataFormat)
{ {
case 0x0404: case 0x0404:
print.Append(Params[i].IntValue); print.Append(Params[i].IntValue +Environment.NewLine);
print.AppendLine();
break; break;
default: default:
print.AppendLine(Params[i].StringValue); print.AppendLine(Params[i].StringValue);
break; break;
} }
print.Append('\n');
} }
print.Append(Environment.NewLine);
// Ensure UTF-8 will display properly // Ensure UTF-8 will display properly
Console.OutputEncoding = Encoding.UTF8; Console.OutputEncoding = Encoding.UTF8;
+2 -2
View File
@@ -70,7 +70,7 @@ namespace LibIRD
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
/// <exception cref="FileNotFoundException"></exception> /// <exception cref="FileNotFoundException"></exception>
/// <exception cref="InvalidDataException"></exception> /// <exception cref="InvalidDataException"></exception>
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 // Generate Unique Identifier using ISO CRC32
UID = GenerateUID(isoPath); UID = GenerateUID(isoPath);
@@ -117,7 +117,7 @@ namespace LibIRD
PIC = GeneratePIC(Size); PIC = GeneratePIC(Size);
// Generate IRD fields // Generate IRD fields
GenerateIRD(isoPath); GenerateIRD(isoPath, true);
} }
#endregion #endregion
+15 -4
View File
@@ -8,12 +8,23 @@ namespace PrintParams
{ {
static void Main() static void Main()
{ {
string filename = "./PARAM.SFO"; string filename = "./PS3_DISC.SFB";
if (File.Exists(filename)) if (File.Exists(filename))
{ {
ParamSFO paramSFO = new("./PARAM.SFO"); PS3_DiscSFB ps3_DiscSFB = new(filename);
Console.WriteLine("PARAM.SFO for: " + paramSFO["TITLE_ID"] + '\n'); 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(); paramSFO.Print();
} }
else else