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); } } } }