#include "ncsd.h"
#include "ncch.h"

u32 ValidateNcsdHeader(NcsdHeader* header) {
    static const u8 zeroes[16] = { 0 };
    if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number
        (memcmp(header->partitions_fs_type, zeroes, 8) != 0) || !header->mediaId) // prevent detection of NAND images
        return 1;

    u32 data_units = 0;
    for (u32 i = 0; i < 8; i++) {
        NcchPartition* partition = header->partitions + i;
        if ((i == 0) && !partition->size) return 1; // first content must be there
        else if (!partition->size) continue;
        if (partition->offset < data_units)
            return 1; // overlapping partitions, failed
        data_units = partition->offset + partition->size;
    }
    if (data_units > header->size)
        return 1;

    return 0;
}

u64 GetNcsdTrimmedSize(NcsdHeader* header) {
    u32 data_units = 0;
    for (u32 i = 0; i < 8; i++) {
        NcchPartition* partition = header->partitions + i;
        u32 partition_end = partition->offset + partition->size;
        if (!partition->size) continue;
        data_units = (partition_end > data_units) ? partition_end : data_units;
    }

    return data_units * NCSD_MEDIA_UNIT;
}

// on the fly decryptor for NCSD
u32 CryptNcsdSequential(void* data, u32 offset_data, u32 size_data, u16 crypto) {
    // warning: this will only work for sequential processing
    static NcsdHeader ncsd;

    // fetch ncsd header from data
    if ((offset_data == 0) && (size_data >= sizeof(NcsdHeader)))
        memcpy(&ncsd, data, sizeof(NcsdHeader));

    for (u32 i = 0; i < 8; i++) {
        NcchPartition* partition = ncsd.partitions + i;
        u32 offset_p = partition->offset * NCSD_MEDIA_UNIT;
        u32 size_p = partition->size * NCSD_MEDIA_UNIT;
        // check if partition in data
        if ((offset_p >= offset_data + size_data) ||
            (offset_data >= offset_p + size_p) ||
            !size_p) {
            continue; // section not in data
        }
        // determine data / offset / size
        u8* data8 = (u8*)data;
        u8* data_i = data8;
        u32 offset_i = 0;
        u32 size_i = size_p;
        if (offset_p < offset_data)
            offset_i = offset_data - offset_p;
        else data_i = data8 + (offset_p - offset_data);
        size_i = size_p - offset_i;
        if (size_i > size_data - (data_i - data8))
            size_i = size_data - (data_i - data8);
        // decrypt ncch segment
        if (CryptNcchSequential(data_i, offset_i, size_i, crypto) != 0)
            return 1;
    }

    return 0;
}

