SCFM

From WiiUBrew
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;
	}
}