#include "mf_ultralight.h"
 
#include <furi.h>
 
typedef struct {
    uint16_t total_pages;
    uint16_t config_page;
    uint32_t feature_set;
} MfUltralightFeatures;
 
static const MfUltralightFeatures mf_ultralight_features[MfUltralightTypeNum] = {
    [MfUltralightTypeUnknown] =
        {
            .total_pages = 16,
            .config_page = 0,
            .feature_set = MfUltralightFeatureSupportCompatibleWrite,
        },
    [MfUltralightTypeNTAG203] =
        {
            .total_pages = 42,
            .config_page = 0,
            .feature_set = MfUltralightFeatureSupportCompatibleWrite |
                           MfUltralightFeatureSupportCounterInMemory,
        },
    [MfUltralightTypeUL11] =
        {
            .total_pages = 20,
            .config_page = 16,
            .feature_set =
                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
                MfUltralightFeatureSupportReadCounter |
                MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportFastRead |
                MfUltralightFeatureSupportIncCounter | MfUltralightFeatureSupportCompatibleWrite |
                MfUltralightFeatureSupportAuthentication | MfUltralightFeatureSupportVcsl,
        },
    [MfUltralightTypeUL21] =
        {
            .total_pages = 41,
            .config_page = 37,
            .feature_set =
                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
                MfUltralightFeatureSupportReadCounter |
                MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportFastRead |
                MfUltralightFeatureSupportIncCounter | MfUltralightFeatureSupportCompatibleWrite |
                MfUltralightFeatureSupportAuthentication | MfUltralightFeatureSupportVcsl,
        },
    [MfUltralightTypeNTAG213] =
        {
            .total_pages = 45,
            .config_page = 41,
            .feature_set =
                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
                MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead |
                MfUltralightFeatureSupportCompatibleWrite |
                MfUltralightFeatureSupportAuthentication |
                MfUltralightFeatureSupportSingleCounter | MfUltralightFeatureSupportAsciiMirror,
        },
    [MfUltralightTypeNTAG215] =
        {
            .total_pages = 135,
            .config_page = 131,
            .feature_set =
                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
                MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead |
                MfUltralightFeatureSupportCompatibleWrite |
                MfUltralightFeatureSupportAuthentication |
                MfUltralightFeatureSupportSingleCounter | MfUltralightFeatureSupportAsciiMirror,
        },
    [MfUltralightTypeNTAG216] =
        {
            .total_pages = 231,
            .config_page = 227,
            .feature_set =
                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
                MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead |
                MfUltralightFeatureSupportCompatibleWrite |
                MfUltralightFeatureSupportAuthentication |
                MfUltralightFeatureSupportSingleCounter | MfUltralightFeatureSupportAsciiMirror,
        },
    [MfUltralightTypeNTAGI2C1K] =
        {
            .total_pages = 231,
            .config_page = 0,
            .feature_set =
                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportFastRead |
                MfUltralightFeatureSupportFastWrite | MfUltralightFeatureSupportSectorSelect,
        },
    [MfUltralightTypeNTAGI2C2K] =
        {
            .total_pages = 485,
            .config_page = 0,
            .feature_set =
                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportFastRead |
                MfUltralightFeatureSupportFastWrite | MfUltralightFeatureSupportSectorSelect,
        },
    [MfUltralightTypeNTAGI2CPlus1K] =
        {
            .total_pages = 236,
            .config_page = 227,
            .feature_set =
                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
                MfUltralightFeatureSupportFastRead | MfUltralightFeatureSupportAuthentication |
                MfUltralightFeatureSupportSectorSelect,
        },
    [MfUltralightTypeNTAGI2CPlus2K] =
        {
            .total_pages = 492,
            .config_page = 227,
            .feature_set =
                MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
                MfUltralightFeatureSupportFastRead | MfUltralightFeatureSupportAuthentication |
                MfUltralightFeatureSupportSectorSelect,
        },
};
 
MfUltralightType mf_ultralight_get_type_by_version(MfUltralightVersion* version) {
    furi_assert(version);
 
    MfUltralightType type = MfUltralightTypeUnknown;
 
    if(version->storage_size == 0x0B || version->storage_size == 0x00) {
        type = MfUltralightTypeUL11;
    } else if(version->storage_size == 0x0E) {
        type = MfUltralightTypeUL21;
    } else if(version->storage_size == 0x0F) {
        type = MfUltralightTypeNTAG213;
    } else if(version->storage_size == 0x11) {
        type = MfUltralightTypeNTAG215;
    } else if(version->prod_subtype == 5 && version->prod_ver_major == 2) {
        if(version->prod_ver_minor == 1) {
            if(version->storage_size == 0x13) {
                type = MfUltralightTypeNTAGI2C1K;
            } else if(version->storage_size == 0x15) {
                type = MfUltralightTypeNTAGI2C2K;
            }
        } else if(version->prod_ver_minor == 2) {
            if(version->storage_size == 0x13) {
                type = MfUltralightTypeNTAGI2CPlus1K;
            } else if(version->storage_size == 0x15) {
                type = MfUltralightTypeNTAGI2CPlus2K;
            }
        }
    } else if(version->storage_size == 0x13) {
        type = MfUltralightTypeNTAG216;
    }
 
    return type;
}
 
uint16_t mf_ultralight_get_pages_total(MfUltralightType type) {
    return mf_ultralight_features[type].total_pages;
}
 
uint32_t mf_ultralight_get_feature_support_set(MfUltralightType type) {
    return mf_ultralight_features[type].feature_set;
}
 
const char* mf_ultralight_get_name(MfUltralightType type, bool full_name) {
    if(type == MfUltralightTypeNTAG213) {
        return "NTAG213";
    } else if(type == MfUltralightTypeNTAG215) {
        return "NTAG215";
    } else if(type == MfUltralightTypeNTAG216) {
        return "NTAG216";
    } else if(type == MfUltralightTypeNTAGI2C1K) {
        return "NTAG I2C 1K";
    } else if(type == MfUltralightTypeNTAGI2C2K) {
        return "NTAG I2C 2K";
    } else if(type == MfUltralightTypeNTAGI2CPlus1K) {
        return "NTAG I2C Plus 1K";
    } else if(type == MfUltralightTypeNTAGI2CPlus2K) {
        return "NTAG I2C Plus 2K";
    } else if(type == MfUltralightTypeNTAG203) {
        return "NTAG203";
    } else if(type == MfUltralightTypeUL11 && full_name) {
        return "Mifare Ultralight 11";
    } else if(type == MfUltralightTypeUL21 && full_name) {
        return "Mifare Ultralight 21";
    } else {
        return "Mifare Ultralight";
    }
}
 
bool mf_ultralight_detect_protocol(NfcaData* nfca_data) {
    furi_assert(nfca_data);
 
    bool mfu_detected = (nfca_data->atqa[0] == 0x44) && (nfca_data->atqa[1] == 0x00) &&
                        (nfca_data->sak == 0x00);
 
    return mfu_detected;
}
 
uint16_t mf_ultralight_get_config_page_num(MfUltralightType type) {
    return mf_ultralight_features[type].config_page;
}
 
bool mf_ultralight_get_config_page(MfUltralightData* data, MfUltralightConfigPages** config) {
    furi_assert(data);
    furi_assert(config);
 
    bool config_pages_found = false;
 
    uint16_t config_page = mf_ultralight_features[data->type].config_page;
    if(config_page != 0) {
        *config = (MfUltralightConfigPages*)&data->page[config_page];
        config_pages_found = true;
    }
 
    return config_pages_found;
}
 
bool mf_ultralight_is_all_data_read(MfUltralightData* data) {
    furi_assert(data);
 
    bool all_read = false;
    if(data->pages_read == data->pages_total) {
        // Having read all the pages doesn't mean that we've got everything.
        // By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000,
        // so a default read on an auth-supported NTAG is never complete.
        uint32_t feature_set = mf_ultralight_get_feature_support_set(data->type);
        if(feature_set & MfUltralightFeatureSupportAuthentication) {
            all_read = true;
        } else {
            MfUltralightConfigPages* config = NULL;
            if(mf_ultralight_get_config_page(data, &config)) {
                all_read = ((config->password.pass != 0) || (config->pack.pack != 0));
            }
        }
    }
 
    return all_read;
}
 
bool mf_ultralight_is_counter_configured(MfUltralightData* data) {
    furi_assert(data);
 
    MfUltralightConfigPages* config = NULL;
    bool configured = false;
 
    switch(data->type) {
    case MfUltralightTypeNTAG213:
    case MfUltralightTypeNTAG215:
    case MfUltralightTypeNTAG216:
        if(mf_ultralight_get_config_page(data, &config)) {
            configured = config->access.nfc_cnt_en;
        }
        break;
 
    default:
        configured = true;
        break;
    }
 
    return configured;
}

V1027 Pointer to an object of the 'MfUltralightPage' class is cast to unrelated 'MfUltralightConfigPages' class.