chovy-trans/CHOVY-TRANSFER/Sfo.cs

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