Join Date: Apr 2009
Real First Name: Morgan
Location: Around black mesa.
Just Played: SMB
Posts: 83
SFO File Organization
OK guys, so I'm trying to get my SFO editor working -- only to find out that using the official "System File Viewer" it works *fine* but under the actual PSP it comes up as bad. WTF? As well,
Code:
EBOOTR.SFO
Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 00 50 53 46 01 01 00 00 54 00 00 00 74 00 00 00 .PSF....T...t...
00000010 04 00 00 00 00 00 04 04 04 00 00 00 04 00 00 00 ................
00000020 00 00 00 00 09 00 04 02 03 00 00 00 04 00 00 00 ................
00000030 04 00 00 00 12 00 04 04 04 00 00 00 04 00 00 00 ................
00000040 08 00 00 00 19 00 04 02 0B 00 00 00 0C 00 00 00 ................
00000050 0C 00 00 00 42 4F 4F 54 41 42 4C 45 00 43 41 54 ....BOOTABLE.CAT
00000060 45 47 4F 52 59 00 52 45 47 49 4F 4E 00 54 49 54 EGORY.REGION.TIT
00000070 4C 45 00 00 01 00 00 00 4D 47 00 00 00 80 00 00 LE......MG...€..
00000080 4D 79 20 47 61 6D 65 20 3A 29 00 00 My Game :)..
PBP_UNPACKER.SFO
Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 00 50 53 46 01 01 00 00 54 00 00 00 74 00 00 00 .PSF....T...t...
00000010 04 00 00 00 00 00 04 04 04 00 00 00 04 00 00 00 ................
00000020 00 00 00 00 09 00 04 02 03 00 00 00 04 00 00 00 ................
00000030 04 00 00 00 12 00 04 04 04 00 00 00 04 00 00 00 ................
00000040 08 00 00 00 19 00 04 02 0B 00 00 00 0B 00 00 00 ................
00000050 0C 00 00 00 42 4F 4F 54 41 42 4C 45 00 43 41 54 ....BOOTABLE.CAT
00000060 45 47 4F 52 59 00 52 45 47 49 4F 4E 00 54 49 54 EGORY.REGION.TIT
00000070 4C 45 00 00 01 00 00 00 4D 47 00 00 00 80 00 00 LE......MG...€..
00000080 4D 79 20 47 61 6D 65 20 3A 29 00 00 My Game :)..
the *only* difference is the padding involved (offset 0x4C), FWIK.
Spoiler for code to get SFO bytes :
Code:
public byte[] FormatData()
{
System.IO.MemoryStream keystream = new System.IO.MemoryStream();
System.IO.MemoryStream datastream = new System.IO.MemoryStream();
System.IO.MemoryStream tablestream = new System.IO.MemoryStream();
int idx_key = 0;
int idx_val = 0;
foreach (var item in Dict) // Dict is a SortedDictionary.
{
// things we need to know.
byte[] key = System.Text.Encoding.UTF8.GetBytes(item.Key);
byte[] dwrite = item.Value.data;
int padded_size = (int)RoundUpToMult((UInt32)dwrite.Length, (uint)4);
if (item.Value.type == SFOEntryType.txt)
{
// check if there's an appropriate NULL byte there.
if (Array.IndexOf(dwrite, 0x00) < 0)
{
// there needs to be a NULL there :)
dwrite = System.Text.Encoding.UTF8.GetBytes(item.Value.ToString() + '\0');
padded_size = (int)RoundUpToMult((UInt32)dwrite.Length, (uint)4);
}
}
// write our entry into the table.
tablestream.Write(BitConverter.GetBytes((UInt16)idx_key), 0, 2); // offset of key name into the key table.
tablestream.WriteByte(4); //unknown '4' byte.
tablestream.WriteByte((byte)item.Value.type);
tablestream.Write(BitConverter.GetBytes((UInt32)dwrite.Length), 0, 4);
tablestream.Write(BitConverter.GetBytes((UInt32)padded_size), 0, 4);
tablestream.Write(BitConverter.GetBytes((UInt32)idx_val), 0, 4);
keystream.Write(key, 0, key.Length);
keystream.WriteByte(0x00); // write a null byte, to be safe.
idx_key += key.Length + 1;
Array.Resize(ref dwrite, padded_size);
// here's where we're messing up. We need to write a COMPLETELY FRESH buffer.
//datastream.Write(dwrite, 0, padded_size);
byte[] bx = new byte[padded_size];
bx.Initialize();
Array.Copy(dwrite, bx, dwrite.Length);
datastream.Write(bx, 0, padded_size);
idx_val += padded_size;
}
//byte[] return_array = new byte[20+info_table.Length + keystream.Length + datastream.Length];
byte[] HEADER = { 0x00, 0x50, 0x53, 0x46, 0x01, 0x01, 0x0, 0x00 };
//Array.Copy(HEADER, return_array, 8);
//Array.Copy(BitConverter.GetBytes((UInt32)(20 + tablestream.Length)), 0, return_array, 8, 4);
//Array.Copy(BitConverter.GetBytes((UInt32)(20 + tablestream.Length + keystream.Length)), 0, return_array, 12, 4);
//Array.Copy(BitConverter.GetBytes((UInt32)Dict.Count), 0, return_array, 16, 4);
//Array.Copy(tablestream.ToArray(), 0, return_array, 20, tablestream.Length);
//Array.Copy(keystream.ToArray(), 0, return_array, 20 + tablestream.Length, keystream.Length);
//Array.Copy(datastream.ToArray(), 0, return_array, 20 + tablestream.Length + keystream.Length, datastream.Length);
System.IO.MemoryStream ms = new System.IO.MemoryStream();
ms.Write(HEADER, 0, 8); /* test to see if something is just broken... */
ms.Write(BitConverter.GetBytes((UInt32)(/*20 + */ 20 + tablestream.Length)), 0, 4);
ms.Write(BitConverter.GetBytes((UInt32)(/*20 + */ 20 + tablestream.Length + keystream.Length + 1)), 0, 4);
ms.Write(BitConverter.GetBytes((UInt32)Dict.Count), 0, 4);
ms.Write(tablestream.ToArray(), 0, (int)tablestream.Length);
ms.Write(keystream.ToArray(), 0, (int)keystream.Length);
ms.WriteByte(0x00);
ms.Write(datastream.ToArray(), 0, (int)datastream.Length);
return ms.ToArray();
Any ideas?