using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
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
///
/// string Key, string Value
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)
{
// 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 = [];
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
///
/// Optionally print to text file
public void Print(string printPath = null, string isoName = null)
{
// Build string from parameters
StringBuilder printText = new();
if (isoName != null)
printText.AppendLine($"PS3_DISC.SFB Contents: {isoName}");
else
printText.AppendLine("PS3_DISC.SFB Contents:");
printText.AppendLine("======================");
// Loop through all parameters in PARAM.SFO
foreach (KeyValuePair field in Field)
printText.AppendLine(field.Key + ": " + field.Value);
// Blank line
printText.Append(Environment.NewLine);
// If no path given, print to console
if (printPath == null)
{
// Ensure UTF-8 will display properly in console
Console.OutputEncoding = Encoding.UTF8;
// Print formatted string to console
Console.Write(printText);
}
else
{
// Write data to file
File.AppendAllText(printPath, printText.ToString());
}
}
///
/// Define JSON options once
///
private readonly JsonSerializerOptions JsonOpts = new() { WriteIndented = true };
///
/// Prints parameters extracted from PS3_DISC.SFB to a json object
///
/// Optionally print to json file
public void PrintJson(string jsonPath = null)
{
// Serialise PS3_Disc.SFB data to a JSON object
string json = JsonSerializer.Serialize(Field, JsonOpts);
// If no path given, output to console
if (jsonPath == null)
{
// Ensure UTF-8 will display properly in console
Console.OutputEncoding = Encoding.UTF8;
// Print formatted string to console
Console.Write(json);
}
else
{
// Write to path
File.AppendAllText(jsonPath, json);
}
}
}
}