258 lines
9.2 KiB
C#
258 lines
9.2 KiB
C#
using Li.Utilities;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.Tracing;
|
|
using System.IO;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
// A Sfo Parser Written by Li
|
|
// Because all the others are overly-complicated for no reason!
|
|
// MIT Licensed.
|
|
|
|
namespace Param
|
|
{
|
|
|
|
public class Sfo
|
|
{
|
|
|
|
private struct SfoEntry
|
|
{
|
|
internal string keyName;
|
|
internal byte type;
|
|
internal UInt32 valueSize;
|
|
internal UInt32 totalSize;
|
|
internal byte align;
|
|
internal object value;
|
|
}
|
|
|
|
const int SFO_MAGIC = 0x46535000;
|
|
const byte PSF_TYPE_BIN = 0;
|
|
const byte PSF_TYPE_STR = 2;
|
|
const byte PSF_TYPE_VAL = 4;
|
|
|
|
private Dictionary<string, SfoEntry> sfoEntries;
|
|
public Object this[string index]
|
|
{
|
|
get
|
|
{
|
|
if (sfoEntries.ContainsKey(index))
|
|
return sfoEntries[index].value;
|
|
else
|
|
return null;
|
|
}
|
|
set
|
|
{
|
|
if (sfoEntries.ContainsKey(index))
|
|
{
|
|
SfoEntry sfoEnt = sfoEntries[index];
|
|
sfoEnt.value = value;
|
|
|
|
// update sz
|
|
sfoEnt.valueSize = getObjectSz(sfoEnt.value);
|
|
|
|
if (sfoEnt.valueSize > sfoEnt.totalSize)
|
|
sfoEnt.totalSize = Convert.ToUInt32(MathUtil.CalculatePaddingAmount(Convert.ToInt32(sfoEnt.valueSize), sfoEnt.align));
|
|
|
|
// update type
|
|
sfoEnt.type = getPsfType(sfoEnt.value);
|
|
|
|
sfoEntries[index] = sfoEnt;
|
|
}
|
|
else
|
|
{
|
|
UInt32 sz = getObjectSz(value);
|
|
int alg = MathUtil.CalculatePaddingAmount(Convert.ToInt32(sz), 4);
|
|
|
|
AddKey(index, value, Convert.ToUInt32(sz + alg), 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AddKey(string keyName, object value, UInt32 totalSize, byte align = 4)
|
|
{
|
|
SfoEntry ent = new SfoEntry();
|
|
ent.keyName = keyName;
|
|
ent.type = getPsfType(value);
|
|
ent.valueSize = getObjectSz(value);
|
|
ent.totalSize = Convert.ToUInt32(totalSize + MathUtil.CalculatePaddingAmount(Convert.ToInt32(totalSize), align));
|
|
ent.align = align;
|
|
ent.value = value;
|
|
sfoEntries[ent.keyName] = ent;
|
|
}
|
|
|
|
public Sfo()
|
|
{
|
|
sfoEntries = new Dictionary<string, SfoEntry>();
|
|
}
|
|
|
|
|
|
private static UInt32 getObjectSz(Object obj)
|
|
{
|
|
if (obj is Int32) return 4;
|
|
if (obj is UInt32) return 4;
|
|
if (obj is String) return Convert.ToUInt32((obj as String).Length + 1);
|
|
if (obj is Byte[]) return Convert.ToUInt32((obj as Byte[]).Length);
|
|
throw new Exception("Object is of unsupported type: " + obj.GetType());
|
|
}
|
|
|
|
private static byte getPsfType(Object obj)
|
|
{
|
|
if (obj is Int32 || obj is UInt32) return PSF_TYPE_VAL;
|
|
if (obj is String) return PSF_TYPE_STR;
|
|
if (obj is Byte[]) return PSF_TYPE_BIN;
|
|
throw new Exception("Object is of unsupported type: " + obj.GetType());
|
|
}
|
|
|
|
public byte[] WriteSfo(UInt32 version = 0x101, Byte align = 0x4)
|
|
{
|
|
using (MemoryStream sfoStream = new MemoryStream())
|
|
{
|
|
WriteSfo(sfoStream, version, align);
|
|
byte[] sfoBytes = sfoStream.ToArray();
|
|
return sfoBytes;
|
|
}
|
|
}
|
|
public void WriteSfo(Stream SfoStream, UInt32 version = 0x101, Byte align = 0x4)
|
|
{
|
|
using (MemoryStream sfoStream = new MemoryStream())
|
|
{
|
|
StreamUtil sfoUtil = new StreamUtil(sfoStream);
|
|
|
|
sfoUtil.WriteUInt32(SFO_MAGIC);
|
|
sfoUtil.WriteUInt32(version);
|
|
|
|
sfoUtil.WriteUInt32(0xFFFFFFFF); // key offset
|
|
sfoUtil.WriteUInt32(0xFFFFFFFF); // value offset
|
|
// (will fill these in after the file is created)
|
|
|
|
sfoUtil.WriteInt32(sfoEntries.Count);
|
|
|
|
using (MemoryStream keyTable = new MemoryStream())
|
|
{
|
|
StreamUtil keyUtils = new StreamUtil(keyTable);
|
|
using (MemoryStream valueTable = new MemoryStream())
|
|
{
|
|
StreamUtil valueUtils = new StreamUtil(valueTable);
|
|
foreach (SfoEntry entry in sfoEntries.Values)
|
|
{
|
|
// write name
|
|
sfoUtil.WriteUInt16(Convert.ToUInt16(keyTable.Position));
|
|
keyUtils.WriteCStr(entry.keyName);
|
|
|
|
|
|
|
|
// write entry
|
|
|
|
sfoUtil.WriteByte(align); // align
|
|
sfoUtil.WriteByte(entry.type); // type
|
|
sfoUtil.WriteUInt32(entry.valueSize); // valueSize
|
|
sfoUtil.WriteUInt32(entry.totalSize); // totalSize
|
|
|
|
// write data
|
|
sfoUtil.WriteUInt32(Convert.ToUInt32(valueTable.Position)); // dataOffset
|
|
|
|
switch (entry.type)
|
|
{
|
|
case PSF_TYPE_VAL:
|
|
valueUtils.WriteUInt32(Convert.ToUInt32(entry.value));
|
|
valueUtils.WritePadding(0x00, Convert.ToInt32(entry.totalSize - entry.valueSize));
|
|
break;
|
|
case PSF_TYPE_STR:
|
|
valueUtils.WriteStrWithPadding(entry.value as String, 0x00, Convert.ToInt32(entry.totalSize));
|
|
break;
|
|
case PSF_TYPE_BIN:
|
|
valueUtils.WriteBytesWithPadding(entry.value as Byte[], 0x00, Convert.ToInt32(entry.totalSize));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
keyUtils.AlignTo(0x00, align);
|
|
UInt32 keyOffset = Convert.ToUInt32(sfoStream.Position);
|
|
keyTable.Seek(0x00, SeekOrigin.Begin);
|
|
keyTable.CopyTo(sfoStream);
|
|
|
|
UInt32 valueOffset = Convert.ToUInt32(sfoStream.Position);
|
|
valueTable.Seek(0x00, SeekOrigin.Begin);
|
|
valueTable.CopyTo(sfoStream);
|
|
|
|
sfoStream.Seek(0x8, SeekOrigin.Begin);
|
|
sfoUtil.WriteUInt32(keyOffset); // key offset
|
|
sfoUtil.WriteUInt32(valueOffset); // value offset
|
|
}
|
|
}
|
|
|
|
sfoStream.Seek(0x0, SeekOrigin.Begin);
|
|
sfoStream.CopyTo(SfoStream);
|
|
}
|
|
|
|
}
|
|
|
|
public static Sfo ReadSfo(Stream SfoStream)
|
|
{
|
|
Sfo sfoFile = new Sfo();
|
|
StreamUtil DataUtils = new StreamUtil(SfoStream);
|
|
|
|
// Read Sfo Header
|
|
UInt32 magic = DataUtils.ReadUInt32();
|
|
UInt32 version = DataUtils.ReadUInt32();
|
|
UInt32 keyOffset = DataUtils.ReadUInt32();
|
|
UInt32 valueOffset = DataUtils.ReadUInt32();
|
|
UInt32 count = DataUtils.ReadUInt32();
|
|
|
|
if (magic == SFO_MAGIC) //\x00PSF
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
SfoEntry entry = new SfoEntry();
|
|
|
|
UInt16 nameOffset = DataUtils.ReadUInt16();
|
|
entry.align = DataUtils.ReadByte();
|
|
entry.type = DataUtils.ReadByte();
|
|
entry.valueSize = DataUtils.ReadUInt32();
|
|
entry.totalSize = DataUtils.ReadUInt32();
|
|
UInt32 dataOffset = DataUtils.ReadUInt32();
|
|
|
|
int keyLocation = Convert.ToInt32(keyOffset + nameOffset);
|
|
entry.keyName = DataUtils.ReadStringAt(keyLocation);
|
|
int valueLocation = Convert.ToInt32(valueOffset + dataOffset);
|
|
|
|
|
|
switch (entry.type)
|
|
{
|
|
case PSF_TYPE_STR:
|
|
entry.value = DataUtils.ReadStringAt(valueLocation);
|
|
break;
|
|
|
|
case PSF_TYPE_VAL:
|
|
entry.value = DataUtils.ReadUInt32At(valueLocation);
|
|
break;
|
|
|
|
case PSF_TYPE_BIN:
|
|
entry.value = DataUtils.ReadBytesAt(valueLocation, Convert.ToInt32(entry.valueSize));
|
|
break;
|
|
}
|
|
|
|
|
|
sfoFile.sfoEntries[entry.keyName] = entry;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidDataException("Sfo Magic is Invalid.");
|
|
}
|
|
|
|
return sfoFile;
|
|
}
|
|
|
|
public static Sfo ReadSfo(byte[] Sfo)
|
|
{
|
|
using (MemoryStream SfoStream = new MemoryStream(Sfo))
|
|
{
|
|
return ReadSfo(SfoStream);
|
|
}
|
|
}
|
|
}
|
|
}
|