Parse PS3_DISC.SFB for redump-style IRDs
This commit is contained in:
+41
-9
@@ -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"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user