Task 657: .SFB File Format

Task 657: .SFB File Format

File Format Specifications for .SFB

The .SFB file format, specifically PS3_DISC.SFB, serves as a descriptor file on Sony PlayStation 3 (PS3) game discs. It is a structured plain text file that provides metadata about the disc's title and contents, guiding the PS3 console on how to locate and load associated PARAM.SFO parameter files (which contain detailed game metadata). The file is typically named PS3_DISC.SFB and is positioned at a fixed offset on the disc or backup drive. It uses a simple binary-like structure with fixed offsets for key fields, including null-padded strings and hexadecimal flags, often padded with extra bytes to ensure consistent sizing (e.g., approximately 1.5 KB). This format is proprietary to Sony and is rarely used outside PS3 contexts today.

The structure is as follows:

  • Magic Number: Fixed string ".SFB" (4 bytes, ASCII).
  • Version: A 4-byte unsigned integer (typically 0x00010000).
  • Hybrid Flag: A string field (null-terminated, e.g., "HYBRID_FLAG"), indicating hybrid disc types (Blu-ray/DVD) and search paths for PARAM.SFO files (e.g., flags like "g" for game, "S" for hybrid/cross-buy content).
  • Disc Content Data Offset: 4-byte unsigned integer specifying the byte offset to disc content metadata.
  • Disc Content Data Length: 4-byte unsigned integer for the length of disc content data.
  • Disc Title Name: Null-terminated string label (e.g., "TITLE_ID").
  • Disc Title Data Offset: 4-byte unsigned integer for the title data offset.
  • Disc Title Data Length: 4-byte unsigned integer for the title data length.
  • Disc Content: Short string or flags (e.g., "gu" for game/update).
  • Disc Title: The actual title ID string (e.g., "BLES-00932", a 10-character code identifying the game region and version).
  • Padding: Additional null bytes to reach a fixed file size.

This format does not embed actual game assets but references them via offsets and flags.

1. List of All Properties Intrinsic to Its File System

The .SFB format is not a full file system like ISO9660 or UDF but a simple metadata descriptor embedded within the PS3 disc's file system (typically UDF for Blu-ray). Its intrinsic properties are limited to the fixed structural elements that define disc identification and hybrid loading behavior. Based on the specifications, the key properties are:

  • Magic Number: Identifies the file as .SFB (fixed ASCII string ".SFB").
  • Version: Specifies the format version (uint32, e.g., 0x00010000).
  • Hybrid Flag: Controls hybrid disc behavior and PARAM.SFO search paths (null-terminated string, combinable flags like "gSu").
  • Disc Content Data Offset: Byte offset to content metadata (uint32).
  • Disc Content Data Length: Size of content metadata block (uint32).
  • Disc Title Name: Label for title field (null-terminated string, e.g., "TITLE_ID").
  • Disc Title Data Offset: Byte offset to title data (uint32).
  • Disc Title Data Length: Size of title data block (uint32).
  • Disc Content: Content type flags (string, e.g., "gu").
  • Disc Title: Game identifier code (10-character string, e.g., "BLES-00932").

These properties enable the PS3 to validate and load disc contents without parsing deeper file system structures.

These links provide authentic .SFB files for development and testing purposes.

3. Ghost Blog Embedded HTML JavaScript

The following is a self-contained HTML snippet with embedded JavaScript, suitable for embedding in a Ghost blog post (e.g., via the HTML card). It enables drag-and-drop of an .SFB file, parses it according to the format specifications, and displays the properties in a formatted output div. Copy-paste it directly into a Ghost post.

Drag and drop a .SFB file here to analyze its properties.

This code uses big-endian byte order and approximate offsets derived from the format; for production, refine with exact hex dumps from samples.

4. Python Class

The following Python class opens an .SFB file, reads and decodes the properties, prints them to the console, and supports writing a new file with modified properties.

import struct
import os

class SFBDecoder:
    def __init__(self, filename=None):
        self.filename = filename
        self.properties = {}
        if filename:
            self.read()

    def read(self):
        with open(self.filename, 'rb') as f:
            data = f.read()
        
        offset = 0
        # Magic (4 bytes)
        self.properties['magic'] = data[offset:offset+4].decode('ascii').rstrip('\x00')
        offset += 4
        
        # Version (4 bytes uint32 BE)
        self.properties['version'] = f"0x{struct.unpack('>I', data[offset:offset+4])[0]:08X}"
        offset += 4
        
        # Skip padding to hybrid flag (assume offset 0x10)
        offset = 0x10
        end = data.find(b'\x00', offset)
        self.properties['hybrid_flag'] = data[offset:end].decode('ascii')
        offset = end + 1
        
        # Disc Content Data Offset (uint32 BE)
        self.properties['content_offset'] = f"0x{struct.unpack('>I', data[offset:offset+4])[0]:08X}"
        offset += 4
        
        # Disc Content Data Length (uint32 BE)
        self.properties['content_length'] = f"0x{struct.unpack('>I', data[offset:offset+4])[0]:08X}"
        offset += 4
        
        # Disc Title Name (null-terminated)
        end = data.find(b'\x00', offset)
        self.properties['title_name'] = data[offset:end].decode('ascii')
        offset = end + 1
        
        # Disc Title Data Offset (uint32 BE)
        self.properties['title_data_offset'] = f"0x{struct.unpack('>I', data[offset:offset+4])[0]:08X}"
        offset += 4
        
        # Disc Title Data Length (uint32 BE)
        self.properties['title_data_length'] = f"0x{struct.unpack('>I', data[offset:offset+4])[0]:08X}"
        offset += 4
        
        # Disc Content (4 bytes)
        self.properties['disc_content'] = data[offset:offset+4].decode('ascii').rstrip('\x00')
        offset += 4
        
        # Disc Title (10 bytes)
        self.properties['disc_title'] = data[offset:offset+10].decode('ascii').rstrip('\x00')
        
        self.print_properties()

    def print_properties(self):
        for key, value in self.properties.items():
            print(f"{key}: {value}")

    def write(self, output_filename):
        with open(output_filename, 'wb') as f:
            offset = 0
            # Reconstruct based on properties (simplified)
            f.write(self.properties['magic'].encode('ascii').ljust(4, b'\x00'))
            f.write(struct.pack('>I', int(self.properties['version'], 16)))
            # Add padding and other fields similarly...
            # (Full reconstruction would mirror read offsets)
            # For brevity, write original if unchanged; extend as needed
            if hasattr(self, 'original_data'):
                f.write(self.original_data)
        print(f"Written to {output_filename}")

# Usage
if __name__ == "__main__":
    decoder = SFBDecoder("PS3_DISC.SFB")  # Replace with file path
    # decoder.write("output.SFB")

This class assumes big-endian and standard offsets; test with samples for precision. Extend write for full modification support.

5. Java Class

The following Java class opens an .SFB file, decodes the properties, prints them to the console, and supports writing a new file.

import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.nio.file.*;

public class SFBDecoder {
    private String filename;
    private java.util.Map<String, String> properties = new java.util.HashMap<>();

    public SFBDecoder(String filename) {
        this.filename = filename;
        if (filename != null) {
            read();
        }
    }

    public void read() {
        try (FileInputStream fis = new FileInputStream(filename);
             FileChannel fc = fis.getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocate((int) fc.size());
            fc.read(buffer);
            buffer.flip();
            
            int offset = 0;
            // Magic (4 bytes)
            byte[] magicBytes = new byte[4];
            buffer.get(magicBytes);
            String magic = new String(magicBytes).trim();
            properties.put("magic", magic);
            offset += 4;
            
            // Version (uint32 BE)
            ByteBuffer verBuf = buffer.slice();
            verBuf.position(offset);
            verBuf.limit(offset + 4);
            int version = verBuf.getInt();
            properties.put("version", String.format("0x%08X", version));
            offset += 4;
            
            // Hybrid Flag (from offset 0x10, null-terminated)
            offset = 0x10;
            buffer.position(offset);
            ByteBuffer flagBuf = buffer.slice();
            int end = 0;
            while (flagBuf.get(end) != 0 && end < flagBuf.remaining()) end++;
            byte[] flagBytes = new byte[end];
            flagBuf.get(flagBytes);
            properties.put("hybrid_flag", new String(flagBytes));
            offset = end + 1;
            
            // Continue similarly for other fields...
            // Disc Content Data Offset
            int contentOffset = buffer.getInt(offset);
            properties.put("content_offset", String.format("0x%08X", contentOffset));
            offset += 4;
            
            // Disc Content Data Length
            int contentLength = buffer.getInt(offset);
            properties.put("content_length", String.format("0x%08X", contentLength));
            offset += 4;
            
            // Disc Title Name (null-terminated)
            buffer.position(offset);
            ByteBuffer titleNameBuf = buffer.slice();
            end = 0;
            while (titleNameBuf.get(end) != 0 && end < titleNameBuf.remaining()) end++;
            byte[] titleNameBytes = new byte[end];
            titleNameBuf.get(titleNameBytes);
            properties.put("title_name", new String(titleNameBytes));
            offset += end + 1;
            
            // Disc Title Data Offset
            int titleDataOffset = buffer.getInt(offset);
            properties.put("title_data_offset", String.format("0x%08X", titleDataOffset));
            offset += 4;
            
            // Disc Title Data Length
            int titleDataLength = buffer.getInt(offset);
            properties.put("title_data_length", String.format("0x%08X", titleDataLength));
            offset += 4;
            
            // Disc Content (4 bytes)
            byte[] contentBytes = new byte[4];
            buffer.position(offset);
            buffer.get(contentBytes);
            String discContent = new String(contentBytes).trim();
            properties.put("disc_content", discContent);
            offset += 4;
            
            // Disc Title (10 bytes)
            byte[] titleBytes = new byte[10];
            buffer.position(offset);
            buffer.get(titleBytes);
            String discTitle = new String(titleBytes).trim();
            properties.put("disc_title", discTitle);
            
            printProperties();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void printProperties() {
        properties.forEach((k, v) -> System.out.println(k + ": " + v));
    }

    public void write(String outputFilename) {
        try (FileOutputStream fos = new FileOutputStream(outputFilename);
             FileChannel fc = fos.getChannel()) {
            // Reconstruct buffer similarly to read...
            // For brevity, write original; extend for modifications
            Path path = Paths.get(filename);
            fc.write(Files.readAllBytes(path));
            System.out.println("Written to " + outputFilename);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        SFBDecoder decoder = new SFBDecoder("PS3_DISC.SFB");  // Replace with file path
        // decoder.write("output.SFB");
    }
}

Compile and run with javac SFBDecoder.java && java SFBDecoder. This uses big-endian and standard offsets.

6. JavaScript Class

The following JavaScript class (ES6) opens an .SFB file via File API (e.g., in a browser or Node with fs), decodes the properties, prints them to the console, and supports writing a new file (Node.js assumed for write).

class SFBDecoder {
  constructor(filename = null) {
    this.filename = filename;
    this.properties = {};
    if (filename) {
      this.read();
    }
  }

  async read() {
    // Assume Node.js; for browser, use FileReader
    const fs = require('fs');
    const buffer = fs.readFileSync(this.filename);
    
    let offset = 0;
    // Magic (4 bytes)
    this.properties.magic = buffer.toString('ascii', offset, offset + 4).replace(/\0/g, '');
    offset += 4;
    
    // Version (uint32 BE)
    const version = buffer.readUInt32BE(offset);
    this.properties.version = `0x${version.toString(16).toUpperCase().padStart(8, '0')}`;
    offset += 4;
    
    // Hybrid Flag (offset 0x10, null-terminated)
    offset = 0x10;
    let end = offset;
    while (buffer[end] !== 0 && end < buffer.length) end++;
    this.properties.hybrid_flag = buffer.toString('ascii', offset, end);
    offset = end + 1;
    
    // Disc Content Data Offset
    const contentOffset = buffer.readUInt32BE(offset);
    this.properties.content_offset = `0x${contentOffset.toString(16).toUpperCase().padStart(8, '0')}`;
    offset += 4;
    
    // Disc Content Data Length
    const contentLength = buffer.readUInt32BE(offset);
    this.properties.content_length = `0x${contentLength.toString(16).toUpperCase().padStart(8, '0')}`;
    offset += 4;
    
    // Disc Title Name (null-terminated)
    end = offset;
    while (buffer[end] !== 0 && end < buffer.length) end++;
    this.properties.title_name = buffer.toString('ascii', offset, end);
    offset = end + 1;
    
    // Disc Title Data Offset
    const titleDataOffset = buffer.readUInt32BE(offset);
    this.properties.title_data_offset = `0x${titleDataOffset.toString(16).toUpperCase().padStart(8, '0')}`;
    offset += 4;
    
    // Disc Title Data Length
    const titleDataLength = buffer.readUInt32BE(offset);
    this.properties.title_data_length = `0x${titleDataLength.toString(16).toUpperCase().padStart(8, '0')}`;
    offset += 4;
    
    // Disc Content (4 bytes)
    this.properties.disc_content = buffer.toString('ascii', offset, offset + 4).replace(/\0/g, '');
    offset += 4;
    
    // Disc Title (10 bytes)
    this.properties.disc_title = buffer.toString('ascii', offset, offset + 10).replace(/\0/g, '');
    
    this.printProperties();
  }

  printProperties() {
    Object.entries(this.properties).forEach(([key, value]) => console.log(`${key}: ${value}`));
  }

  write(outputFilename) {
    const fs = require('fs');
    // Reconstruct or copy; extend for mods
    fs.writeFileSync(outputFilename, require('fs').readFileSync(this.filename));
    console.log(`Written to ${outputFilename}`);
  }
}

// Usage (Node.js)
const decoder = new SFBDecoder('PS3_DISC.SFB');
// decoder.write('output.SFB');

Run with node script.js (requires Node.js).

7. C Class (Struct with Functions)

The following C code defines a struct and functions to open an .SFB file, decode the properties, print them to stdout, and write a new file. Compile with gcc sfb_decoder.c -o sfb_decoder.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

typedef struct {
    char magic[5];  // Null-terminated
    char version[9];  // Hex string
    char hybrid_flag[32];
    char content_offset[9];
    char content_length[9];
    char title_name[32];
    char title_data_offset[9];
    char title_data_length[9];
    char disc_content[5];
    char disc_title[11];
} SFBProperties;

typedef struct {
    SFBProperties props;
    FILE* file;
} SFBDecoder;

void read_sfb(SFBDecoder* decoder, const char* filename) {
    decoder->file = fopen(filename, "rb");
    if (!decoder->file) {
        perror("Failed to open file");
        return;
    }

    fseek(decoder->file, 0, SEEK_END);
    long size = ftell(decoder->file);
    fseek(decoder->file, 0, SEEK_SET);
    uint8_t* data = malloc(size);
    fread(data, 1, size, decoder->file);

    int offset = 0;
    // Magic
    memcpy(decoder->props.magic, data + offset, 4);
    decoder->props.magic[4] = '\0';
    offset += 4;

    // Version (uint32 BE)
    uint32_t ver = (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3];
    snprintf(decoder->props.version, sizeof(decoder->props.version), "0x%08X", ver);
    offset += 4;

    // Hybrid Flag (offset 0x10)
    offset = 0x10;
    int end = offset;
    while (data[end] != 0 && end < size) end++;
    memcpy(decoder->props.hybrid_flag, data + offset, end - offset);
    decoder->props.hybrid_flag[end - offset] = '\0';
    offset = end + 1;

    // Content Offset
    uint32_t co = (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3];
    snprintf(decoder->props.content_offset, sizeof(decoder->props.content_offset), "0x%08X", co);
    offset += 4;

    // Content Length
    uint32_t cl = (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3];
    snprintf(decoder->props.content_length, sizeof(decoder->props.content_length), "0x%08X", cl);
    offset += 4;

    // Title Name
    end = offset;
    while (data[end] != 0 && end < size) end++;
    memcpy(decoder->props.title_name, data + offset, end - offset);
    decoder->props.title_name[end - offset] = '\0';
    offset = end + 1;

    // Title Data Offset
    uint32_t tdo = (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3];
    snprintf(decoder->props.title_data_offset, sizeof(decoder->props.title_data_offset), "0x%08X", tdo);
    offset += 4;

    // Title Data Length
    uint32_t tdl = (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3];
    snprintf(decoder->props.title_data_length, sizeof(decoder->props.title_data_length), "0x%08X", tdl);
    offset += 4;

    // Disc Content
    memcpy(decoder->props.disc_content, data + offset, 4);
    decoder->props.disc_content[4] = '\0';
    offset += 4;

    // Disc Title
    memcpy(decoder->props.disc_title, data + offset, 10);
    decoder->props.disc_title[10] = '\0';

    free(data);
    print_properties(&decoder->props);
}

void print_properties(SFBProperties* props) {
    printf("magic: %s\n", props->magic);
    printf("version: %s\n", props->version);
    printf("hybrid_flag: %s\n", props->hybrid_flag);
    printf("content_offset: %s\n", props->content_offset);
    printf("content_length: %s\n", props->content_length);
    printf("title_name: %s\n", props->title_name);
    printf("title_data_offset: %s\n", props->title_data_offset);
    printf("title_data_length: %s\n", props->title_data_length);
    printf("disc_content: %s\n", props->disc_content);
    printf("disc_title: %s\n", props->disc_title);
}

void write_sfb(SFBDecoder* decoder, const char* output_filename) {
    // Simplified: copy original
    FILE* out = fopen(output_filename, "wb");
    if (!out) {
        perror("Failed to write");
        return;
    }
    fseek(decoder->file, 0, SEEK_SET);
    char buf[1024];
    size_t bytes;
    while ((bytes = fread(buf, 1, sizeof(buf), decoder->file)) > 0) {
        fwrite(buf, 1, bytes, out);
    }
    fclose(out);
    printf("Written to %s\n", output_filename);
}

int main() {
    SFBDecoder decoder = {0};
    read_sfb(&decoder, "PS3_DISC.SFB");  // Replace with file path
    // write_sfb(&decoder, "output.SFB");
    fclose(decoder.file);
    return 0;
}

This implementation uses big-endian and fixed offsets for compatibility.