public SFOData Build() { SFOData sfo = new SFOData(); sfo.Magic = 0x46535000; // _PSF sfo.Version = 0x00000101; sfo.Entries = new List <SFODir>(); var headerSize = 20; var indexTableSize = entries.Count * 16; var keyTableSize = entries.Sum(x => x.Key.Length + 1); if (keyTableSize % 4 != 0) { sfo.Padding = (uint)(4 - keyTableSize % 4); } sfo.KeyTableOffset = (uint)(headerSize + indexTableSize); sfo.DataTableOffset = sfo.KeyTableOffset + (uint)keyTableSize + sfo.Padding; ushort keyOffset = 0; uint dataOffset = 0; for (var i = 0; i < entries.Count; i++) { var entry = entries[i]; var entryLength = GetEntryLength(entry.Key, entry.Value); var maxLength = GetMaxLength(entry.Key); if (entryLength > maxLength) { throw new Exception("Value for {entry.Key} exceeds maximum allowed length"); } sfo.Entries.Add(new SFODir() { KeyOffset = keyOffset, Format = GetEntryType(entry.Key), Length = entryLength, MaxLength = maxLength, DataOffset = dataOffset, Key = entry.Key, Value = entry.Value, }); dataOffset += maxLength; keyOffset += (ushort)(entries[i].Key.Length + 1); } sfo.Size = sfo.DataTableOffset + dataOffset; return(sfo); }
public static void Write(this Stream stream, SFOData sfo) { stream.WriteInteger(sfo.Magic, 1); stream.WriteInteger(sfo.Version, 1); stream.WriteInteger(sfo.KeyTableOffset, 1); stream.WriteInteger(sfo.DataTableOffset, 1); stream.WriteInteger((ushort)sfo.Entries.Count, 1); for (var i = 0; i < sfo.Entries.Count; i++) { var entry = sfo.Entries[i]; stream.WriteShort(entry.KeyOffset, 1); stream.WriteShort(entry.Format, 1); stream.WriteInteger(entry.Length, 1); stream.WriteInteger(entry.MaxLength, 1); stream.WriteInteger(entry.DataOffset, 1); } for (var i = 0; i < sfo.Entries.Count; i++) { var key = sfo.Entries[i].Key; stream.Write(key, 0, key.Length); stream.WriteByte(0); } for (var i = 0; i < sfo.Padding; i++) { stream.WriteByte(0); } for (var i = 0; i < sfo.Entries.Count; i++) { var entry = sfo.Entries[i]; var value = entry.Value; switch (entry.Format) { case 0x0204: stream.Write((string)value, 0, ((string)value).Length); stream.WriteByte(0); for (var j = 0; j < entry.MaxLength - entry.Length; j++) { stream.WriteByte(0); } break; case 0x0404: stream.WriteInteger(Convert.ToInt32(value), 1); break; } } }
public static SFOData ReadSFO(this Stream stream, uint sfoOffset) { var sfo = new SFOData(); sfo.Magic = stream.ReadUInteger(); sfo.Version = stream.ReadUInteger(); sfo.KeyTableOffset = stream.ReadUInteger(); sfo.DataTableOffset = stream.ReadUInteger(); var entries = stream.ReadUInteger(); sfo.Entries = new List <SFODir>(); for (var i = 0; i < entries; i++) { var entry = new SFODir { KeyOffset = stream.ReadUShort(), Format = stream.ReadUShort(), Length = stream.ReadUInteger(), MaxLength = stream.ReadUInteger(), DataOffset = stream.ReadUInteger() }; sfo.Entries.Add(entry); } for (var i = 0; i < sfo.Entries.Count; i++) { var entry = sfo.Entries[i]; stream.Seek(sfoOffset + sfo.KeyTableOffset + entry.KeyOffset, SeekOrigin.Begin); var key = stream.ReadString(128); entry.Key = key.Substring(0, key.IndexOf('\0')); } for (var i = 0; i < sfo.Entries.Count; i++) { var entry = sfo.Entries[i]; stream.Seek(sfoOffset + sfo.DataTableOffset + entry.DataOffset, SeekOrigin.Begin); switch (entry.Format) { case 0x0204: entry.Value = stream.ReadString((int)entry.Length - 1); break; case 0x0404: entry.Value = stream.ReadUInteger(); break; } } return(sfo); }