#include "fs/ivfc.hpp"

namespace IVFC {
	size_t parseIVFC(uintptr_t ivfcStart, IVFC& ivfc) {
		uintptr_t ivfcPointer = ivfcStart;

		char* ivfcCharPtr = (char*)ivfcPointer;
		if (ivfcCharPtr[0] != 'I' || ivfcCharPtr[1] != 'V' || ivfcCharPtr[2] != 'F' || ivfcCharPtr[3] != 'C') {
			printf("Invalid header on IVFC\n");
			return 0;
		}
		ivfcPointer += 4;

		u32 magicIdentifier = *(u32*)ivfcPointer;
		ivfcPointer += 4;

		// RomFS IVFC uses 0x10000, DISA/DIFF IVFC uses 0x20000 here
		if (magicIdentifier != 0x10000 && magicIdentifier != 0x20000) {
			printf("Invalid IVFC magic identifier: %08X\n", magicIdentifier);
			return 0;
		}

		if (magicIdentifier == 0x10000) {
			ivfc.masterHashSize = *(u32*)ivfcPointer;
			ivfcPointer += 4;
			// RomFS IVFC uses 3 levels
			ivfc.levels.resize(3);
		} else {
			ivfc.masterHashSize = *(u64*)ivfcPointer;
			ivfcPointer += 8;
			// DISA/DIFF IVFC uses 4 levels
			ivfc.levels.resize(4);
		}

		for (size_t i = 0; i < ivfc.levels.size(); i++) {
			IVFCLevel level;

			level.logicalOffset = *(u64*)ivfcPointer;
			ivfcPointer += 8;

			level.size = *(u64*)ivfcPointer;
			ivfcPointer += 8;

			// This field is in log2
			level.blockSize = 1 << *(u32*)ivfcPointer;
			ivfcPointer += 4;

			// Skip 4 reserved bytes
			ivfcPointer += 4;

			ivfc.levels[i] = level;
		}

		u64 ivfcDescriptorSize = *(u64*)ivfcPointer;
		ivfcPointer += 8;

		uintptr_t ivfcActualSize = ivfcPointer - ivfcStart;

		// According to 3DBrew, this is usually the case but not guaranteed
		if (ivfcActualSize != ivfcDescriptorSize) {
			printf("IVFC descriptor size mismatch: %llx != %llx\n", ivfcActualSize, ivfcDescriptorSize);
		}

		if (magicIdentifier == 0x10000 && ivfcActualSize != 0x5C) {
			// This is always 0x5C bytes long
			printf("Invalid IVFC size: %08x\n", (u32)ivfcActualSize);
			return 0;
		} else if (magicIdentifier == 0x20000 && ivfcActualSize != 0x78) {
			// This is always 0x78 bytes long
			printf("Invalid IVFC size: %08x\n", (u32)ivfcActualSize);
			return 0;
		}

		return ivfcActualSize;
	}
}  // namespace IVFC
