diff --git a/BuildIRD/BuildIRD.cs b/BuildIRD/BuildIRD.cs
index 0693e8f..839dc5f 100644
--- a/BuildIRD/BuildIRD.cs
+++ b/BuildIRD/BuildIRD.cs
@@ -3,7 +3,7 @@ using System;
namespace BuildIRD
{
- internal class BuildIRD
+ internal class Program
{
static void Main()
{
@@ -11,7 +11,7 @@ namespace BuildIRD
byte[] discID = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] discPIC = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
IRD ird = new IRD("Z:", discKey, discID, discPIC);
- ird.Write("./ ird.ird");
+ ird.Write("./test.ird");
Console.WriteLine(ird.HeaderLength);
}
}
diff --git a/LibIRD.sln b/LibIRD.sln
index 5f62227..9a91fb2 100644
--- a/LibIRD.sln
+++ b/LibIRD.sln
@@ -6,9 +6,8 @@ MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibIRD", "LibIRD\LibIRD.csproj", "{4A80C34B-D4C5-4536-BEAE-46218BC09980}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildIRD", "BuildIRD\BuildIRD.csproj", "{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}"
- ProjectSection(ProjectDependencies) = postProject
- {4A80C34B-D4C5-4536-BEAE-46218BC09980} = {4A80C34B-D4C5-4536-BEAE-46218BC09980}
- EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrintParams", "PrintParams\PrintParams.csproj", "{B5D9C0AD-EA8D-486E-A1D7-ED2C3C7163BC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -24,6 +23,10 @@ Global
{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B5D9C0AD-EA8D-486E-A1D7-ED2C3C7163BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B5D9C0AD-EA8D-486E-A1D7-ED2C3C7163BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B5D9C0AD-EA8D-486E-A1D7-ED2C3C7163BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B5D9C0AD-EA8D-486E-A1D7-ED2C3C7163BC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/LibIRD/ParamSFO.cs b/LibIRD/ParamSFO.cs
new file mode 100644
index 0000000..af4bed5
--- /dev/null
+++ b/LibIRD/ParamSFO.cs
@@ -0,0 +1,232 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace LibIRD
+{
+ ///
+ /// PARAM.SFO file parsing
+ ///
+ public class ParamSFO
+ {
+ ///
+ /// PARAM.SFO file signature
+ ///
+ /// { 0x00, 0x50, 0x53, 0x46 }
+ public static readonly string Magic = "\0PSF";
+
+ ///
+ /// PARAM.SFO file version
+ ///
+ /// Typically { 0x01 0x01 0x00 0x00 } (v1.1)
+ public uint Version { get; private set; }
+
+ ///
+ /// The location of the first byte of the Key Table
+ ///
+ public uint KeyTableStart { get; private set; }
+
+ ///
+ /// The location of the first byte of the Data Table
+ ///
+ public uint DataTableStart { get; private set; }
+
+ ///
+ /// The number of parameters in the table
+ ///
+ public uint ParamCount { get; private set; }
+
+ ///
+ /// Parameter, a single entry in parameter table
+ ///
+ public class Param
+ {
+ ///
+ /// Offset of key, relative to KeyTableStart
+ ///
+ public ushort KeyOffset { get; internal set; }
+
+ ///
+ /// Format of parameter
+ ///
+ /// 0x0400 is string, 0x0404 is uint
+ public ushort DataFormat { get; internal set; }
+
+ ///
+ /// Number of bytes used for parameter
+ ///
+ public uint DataLength { get; internal set; }
+
+ ///
+ /// Total number of bytes for parameter
+ ///
+ /// DataTotal - DataLength is padding of 0x00
+ public uint DataTotal { get; internal set; }
+
+ ///
+ /// offset of parameter, relative to DataTableStart
+ ///
+ public uint DataOffset { get; internal set; }
+
+ ///
+ /// The name of the parameter
+ ///
+ public string Name { get; internal set; }
+
+ ///
+ /// The value of the parameter, if it is a string
+ ///
+ public string StringValue { get; internal set; } = null;
+
+ ///
+ /// The value of the parameter, if it is a UInt32
+ ///
+ public int IntValue { get; internal set; } = 0;
+ }
+
+ ///
+ /// The parameters in the table
+ ///
+ /// Params in the table
+ public Param[] Params { get; private set; }
+
+ ///
+ /// String index overloading, gets string value of given key
+ ///
+ /// Parameter to be retreived
+ /// The string value of the given key
+ public string this[string key]
+ {
+ get
+ {
+ int index = Array.FindIndex(Params, param => param.Name == key);
+ return Params[index].StringValue;
+ }
+ }
+
+ ///
+ /// Constructor using a PARAM.SFO file stream
+ ///
+ /// SFO file stream
+ public ParamSFO(Stream sfoStream)
+ {
+ // Parse file stream
+ Parse(sfoStream);
+ }
+
+ ///
+ /// Constructor using a PARAM.SFO file path
+ ///
+ /// Full file path to the PARAM.SFO
+ ///
+ public ParamSFO(string sfoPath)
+ {
+ // Validate file path
+ if (sfoPath == null || sfoPath.Length <= 0)
+ throw new ArgumentNullException(nameof(sfoPath));
+
+ // Read file as a stream, and parse file
+ using (FileStream fs = new FileStream(sfoPath, FileMode.Open, FileAccess.Read))
+ Parse(fs);
+ }
+
+ ///
+ /// Parse parameters and PARAM.SFO metadata from stream
+ ///
+ /// SFO file stream
+ ///
+ private void Parse(Stream sfoStream)
+ {
+ // Read binary stream
+ using (BinaryReader br = new BinaryReader(sfoStream))
+ {
+ // 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");
+
+ // Parse header
+ Version = br.ReadUInt32();
+ KeyTableStart = br.ReadUInt32();
+ DataTableStart = br.ReadUInt32();
+ ParamCount = br.ReadUInt32();
+
+ // Parse parameter metadata
+ Params = new Param[ParamCount];
+ for (int i = 0; i < ParamCount; i++)
+ {
+ Params[i] = new Param
+ {
+ KeyOffset = br.ReadUInt16(),
+ DataFormat = br.ReadUInt16(),
+ DataLength = br.ReadUInt32(),
+ DataTotal = br.ReadUInt32(),
+ DataOffset = br.ReadUInt32()
+ };
+ }
+
+ // Parse parameters
+ for (int i = 0; i < ParamCount; i++)
+ {
+ // Move stream to ith key
+ sfoStream.Position = KeyTableStart + Params[i].KeyOffset;
+
+ // Determine ith key length
+ uint keyLen = ((i == ParamCount - 1) ? DataTableStart - KeyTableStart : Params[i + 1].KeyOffset)
+ - Params[i].KeyOffset;
+
+ // Read ith key name
+ Params[i].Name = Encoding.ASCII.GetString(br.ReadBytes((int) keyLen)).TrimEnd('\0');
+
+ // Move stream to ith data
+ sfoStream.Position = DataTableStart + Params[i].DataOffset;
+
+ // Read ith data, based on data format
+ switch (Params[i].DataFormat)
+ {
+ case 0x0400: // Non-null-terminated UTF-8 String
+ Params[i].StringValue = Encoding.UTF8.GetString(br.ReadBytes((int)Params[i].DataLength));
+ break;
+ case 0x0402: // Null-terminated UTF-8 String
+ Params[i].StringValue = Encoding.UTF8.GetString(br.ReadBytes((int)Params[i].DataLength)).TrimEnd('\0');
+ break;
+ case 0x0404: // Integer
+ //if (Params[i].DataLength != 4)
+ //throw new ArgumentException("Integer parameter not 4 bytes?");
+ Params[i].IntValue = br.ReadInt32();
+ break;
+ default: // Unknown data format, assume null-terminated string
+ Params[i].StringValue = Encoding.UTF8.GetString(br.ReadBytes((int)Params[i].DataLength)).TrimEnd('\0');
+ break;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Prints formatted parameters extracted from PARAM.SFO to console
+ ///
+ public void Print()
+ {
+ StringBuilder print = new StringBuilder("PARAM.SFO Contents:\n====================\n");
+ for (int i = 0; i < ParamCount; i++)
+ {
+ print.Append(Params[i].Name);
+ print.Append(' ');
+ for (int j = Params[i].Name.Length; j < 20; j++)
+ print.Append(' ');
+ switch (Params[i].DataFormat)
+ {
+ case 0x0404:
+ print.Append(Params[i].IntValue);
+ break;
+ default:
+ print.Append(Params[i].StringValue);
+ break;
+ }
+ print.Append('\n');
+ }
+ Console.Write(print);
+ }
+ }
+}
diff --git a/PrintParams/PrintParams.cs b/PrintParams/PrintParams.cs
new file mode 100644
index 0000000..64b4f78
--- /dev/null
+++ b/PrintParams/PrintParams.cs
@@ -0,0 +1,25 @@
+using LibIRD;
+using System;
+using System.IO;
+
+namespace PrintParams
+{
+ internal class Program
+ {
+ static void Main()
+ {
+ string filename = "./PARAM.SFO";
+
+ if (File.Exists(filename))
+ {
+ ParamSFO paramSFO = new ParamSFO("./PARAM.SFO");
+ Console.WriteLine(paramSFO["TITLE_ID"]);
+ paramSFO.Print();
+ }
+ else
+ {
+ Console.WriteLine(filename + " not found");
+ }
+ }
+ }
+}
diff --git a/PrintParams/PrintParams.csproj b/PrintParams/PrintParams.csproj
new file mode 100644
index 0000000..2e50058
--- /dev/null
+++ b/PrintParams/PrintParams.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net48;net6.0;net7.0
+ win-x86;win-x64;linux-x64;osx-x64
+ 0.1
+
+
+
+
+
+
+