SCFM
Jump to navigation
Jump to search
The SCFM (SLC Cache For MLC) is a file stored in the primary SLC (not SLCCMPT) as /scfm.img
Format
TODO: someone should probably wikify this
TODO: this might be different on 8GB MLC models; this was only tested on 32GB
#define SCFM_SECTOR_SIZE 0x200
#define SCFM_METADATA_SIZE 0x4000
#define SCFM_REGION_SIZE 0x800000
#define SCFM_REGION_SECTORS SCFM_REGION_SIZE / SCFM_SECTOR_SIZE
#define SCFM_REGION_MAP_OFFSET 0x30
#define SCFM_REGION_MAP_SIZE 0x3c00
#define SCFM_SLOT_RECORDS_OFFSET SCFM_REGION_MAP_OFFSET + SCFM_REGION_MAP_SIZE
#define SCFM_CACHE_LINE_SECTORS 0x100
#define SCFM_CACHE_LINES_PER_SLOT SCFM_REGION_SECTORS / SCFM_CACHE_LINE_SECTORS
#define SCFM_SLOT_VALID 0x80
#define SCFM_SLOT_INDEX_MASK 0x7f
struct scfmDiskSlotRecord {
u8 nextSlot; /* 0x00 */
u8 previousSlot; /* 0x01 */
be16 region; /* 0x02 */
be32 flags; /* 0x04 */
u8 validLines[8]; /* 0x08 */
};
struct scfmDiskHeader {
be32 metadataSize; /* 0x00: 0x00004000 */
be32 slotCount; /* 0x04: 0x00000010 */
be32 slotSize; /* 0x08: 0x00800000 */
u8 unknown_0c[0x0c]; /* 0x0c */
be32 payloadSize; /* 0x18: 0x08000000 */
u8 unknown_1c[0x14]; /* 0x1c through 0x2f */
u8 regionToSlot[SCFM_REGION_MAP_SIZE]; /* 0x38 */
};
Usage
TODO: also wikify this
static const void *disk;
static const struct scfmDiskHeader *header = disk;
static const void *scfmLookupSector(u32 mlcSector) {
u32 region = mlcSector / SCFM_REGION_SECTORS;
u32 sectorInRegion = mlcSector % SCFM_REGION_SECTORS;
u8 mapping;
u32 slot, line;
size_t offset;
struct scfmDiskSlotRecord *records;
if (region >= SCFM_REGION_MAP_SIZE)
return NULL;
mapping = header->regionToSlot[region];
if ((mapping & SCFM_SLOT_VALID) == 0)
return NULL;
slot = mapping & SCFM_SLOT_INDEX_MASK;
if (slot >= header->slotCount)
return NULL; /* Reject corrupt metadata */
line = sectorInRegion / SCFM_CACHE_LINE_SECTORS;
records = (struct scfmDiskSlotRecord *)(disk + SCFM_SLOT_RECORDS_OFFSET);
if ((records[slot].validLines[line >> 3] & (1u << (line & 7))) == 0)
return NULL;
offset = (size_t)header->metadataSize +
(size_t)slot * header->slotSize +
(size_t)sectorInRegion * SCFM_SECTOR_SIZE;
if (offset > diskSize - SCFM_SECTOR_SIZE)
return NULL;
return disk + offset;
}
void scfmReadSectors(u32 firstSect, u32 numSect, void *buf) {
u8 *out = buf;
while (numSect != 0) {
u32 inLine = firstSect % SCFM_CACHE_LINE_SECTORS;
u32 chunk = SCFM_CACHE_LINE_SECTORS - inLine;
const u8 *cached = scfmLookupSector(view, firstSect);
if (chunk > numSect)
chunk = numSect;
if (cached)
memcpy(output, cached, (size_t)chunk * SCFM_SECTOR_SIZE);
else
readMLC(firstSector, chunk, output);
firstSect += chunk;
numSect -= chunk;
output += (size_t)chunk * SCFM_SECTOR_SIZE;
}
}