PARAM.SFO parsing
This commit is contained in:
@@ -3,7 +3,7 @@ using System;
|
|||||||
|
|
||||||
namespace BuildIRD
|
namespace BuildIRD
|
||||||
{
|
{
|
||||||
internal class BuildIRD
|
internal class Program
|
||||||
{
|
{
|
||||||
static void Main()
|
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[] 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 };
|
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 ird = new IRD("Z:", discKey, discID, discPIC);
|
||||||
ird.Write("./ ird.ird");
|
ird.Write("./test.ird");
|
||||||
Console.WriteLine(ird.HeaderLength);
|
Console.WriteLine(ird.HeaderLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -6,9 +6,8 @@ MinimumVisualStudioVersion = 10.0.40219.1
|
|||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibIRD", "LibIRD\LibIRD.csproj", "{4A80C34B-D4C5-4536-BEAE-46218BC09980}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibIRD", "LibIRD\LibIRD.csproj", "{4A80C34B-D4C5-4536-BEAE-46218BC09980}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildIRD", "BuildIRD\BuildIRD.csproj", "{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildIRD", "BuildIRD\BuildIRD.csproj", "{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}"
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
EndProject
|
||||||
{4A80C34B-D4C5-4536-BEAE-46218BC09980} = {4A80C34B-D4C5-4536-BEAE-46218BC09980}
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrintParams", "PrintParams\PrintParams.csproj", "{B5D9C0AD-EA8D-486E-A1D7-ED2C3C7163BC}"
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -0,0 +1,232 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LibIRD
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// PARAM.SFO file parsing
|
||||||
|
/// </summary>
|
||||||
|
public class ParamSFO
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// PARAM.SFO file signature
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>{ 0x00, 0x50, 0x53, 0x46 }</remarks>
|
||||||
|
public static readonly string Magic = "\0PSF";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PARAM.SFO file version
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Typically { 0x01 0x01 0x00 0x00 } (v1.1)</remarks>
|
||||||
|
public uint Version { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The location of the first byte of the Key Table
|
||||||
|
/// </summary>
|
||||||
|
public uint KeyTableStart { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The location of the first byte of the Data Table
|
||||||
|
/// </summary>
|
||||||
|
public uint DataTableStart { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of parameters in the table
|
||||||
|
/// </summary>
|
||||||
|
public uint ParamCount { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parameter, a single entry in parameter table
|
||||||
|
/// </summary>
|
||||||
|
public class Param
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Offset of key, relative to KeyTableStart
|
||||||
|
/// </summary>
|
||||||
|
public ushort KeyOffset { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format of parameter
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>0x0400 is string, 0x0404 is uint</remarks>
|
||||||
|
public ushort DataFormat { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of bytes used for parameter
|
||||||
|
/// </summary>
|
||||||
|
public uint DataLength { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Total number of bytes for parameter
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>DataTotal - DataLength is padding of 0x00</remarks>
|
||||||
|
public uint DataTotal { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// offset of parameter, relative to DataTableStart
|
||||||
|
/// </summary>
|
||||||
|
public uint DataOffset { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the parameter
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value of the parameter, if it is a string
|
||||||
|
/// </summary>
|
||||||
|
public string StringValue { get; internal set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value of the parameter, if it is a UInt32
|
||||||
|
/// </summary>
|
||||||
|
public int IntValue { get; internal set; } = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parameters in the table
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks><see cref="ParamCount"/> Params in the table</remarks>
|
||||||
|
public Param[] Params { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// String index overloading, gets string value of given key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Parameter to be retreived</param>
|
||||||
|
/// <returns>The string value of the given key</returns>
|
||||||
|
public string this[string key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int index = Array.FindIndex(Params, param => param.Name == key);
|
||||||
|
return Params[index].StringValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor using a PARAM.SFO file stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sfoStream">SFO file stream</param>
|
||||||
|
public ParamSFO(Stream sfoStream)
|
||||||
|
{
|
||||||
|
// Parse file stream
|
||||||
|
Parse(sfoStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor using a PARAM.SFO file path
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sfoPath">Full file path to the PARAM.SFO</param>
|
||||||
|
/// <exception cref="ArgumentNullException"></exception>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse parameters and PARAM.SFO metadata from stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sfoStream">SFO file stream</param>
|
||||||
|
/// <exception cref="FileLoadException"></exception>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prints formatted parameters extracted from PARAM.SFO to console
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||||
|
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||||
|
<Version>0.1</Version>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\LibIRD\LibIRD.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user