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="discID">Disc ID, byte array of length 16</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
DiscKey = discKey;
@@ -373,7 +374,7 @@ namespace LibIRD
PIC = discPIC;
// Generate IRD files from ISO
GenerateIRD(isoPath);
GenerateIRD(isoPath, redump);
}
/// <summary>
@@ -381,15 +382,14 @@ namespace LibIRD
/// </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="InvalidDataException"></exception>
public IRD(string isoPath, string getKeyLog)
/// <param name="redump">True if redump-style IRD</param>
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
/// </summary>
/// <param name="isoPath">Path to the ISO</param>
/// <param name="redump">True if redump-style IRD</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="FileNotFoundException"></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
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"];
}
+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>
/// PARAM.SFO file version
/// </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; }
/// <summary>
@@ -117,7 +117,7 @@ namespace LibIRD
/// <summary>
/// Constructor using a PARAM.SFO file path
/// </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>
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;
+2 -2
View File
@@ -70,7 +70,7 @@ namespace LibIRD
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="FileNotFoundException"></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
UID = GenerateUID(isoPath);
@@ -117,7 +117,7 @@ namespace LibIRD
PIC = GeneratePIC(Size);
// Generate IRD fields
GenerateIRD(isoPath);
GenerateIRD(isoPath, true);
}
#endregion
+15 -4
View File
@@ -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