#include "nfc_dev.h"
 
#include <storage/storage.h>
#include <flipper_format/flipper_format.h>
 
#include "protocols/nfc_util.h"
#include <lib/toolbox/hex.h>
 
static const char* nfc_file_header = "Flipper NFC device";
static const uint32_t nfc_file_version = 3;
static const uint32_t nfc_file_version_with_lsb_atqa = 2;
 
// static const char* nfc_keys_file_header = "Flipper NFC keys";
// static const uint32_t nfc_keys_file_version = 1;
 
// Protocols format versions
static const uint32_t nfc_mifare_classic_data_format_version = 2;
static const uint32_t nfc_mifare_ultralight_data_format_version = 1;
 
struct NfcDev {
    bool shadow_file_exist;
 
    NfcLoadingCallback loading_callback;
    void* loading_callback_context;
};
 
typedef bool (*NfcDevVerifyProtocol)(FuriString* device_type, NfcDevData* data);
typedef bool (*NfcDevDataHandler)(FlipperFormat* file, uint32_t version, NfcDevData* data);
 
typedef struct {
    NfcDevVerifyProtocol verify_handler;
    NfcDevDataHandler load_handler;
    NfcDevDataHandler save_handler;
} NfcDevDataParser;
 
static bool nfc_dev_nfca_load_data(FlipperFormat* file, uint32_t version, NfcaData* data) {
    furi_assert(file);
    furi_assert(data);
 
    uint32_t data_cnt = 0;
    bool parsed = false;
 
    do {
        if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break;
        if(!(data_cnt == 4 || data_cnt == 7)) break;
        data->uid_len = data_cnt;
        if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break;
        if(version == nfc_file_version_with_lsb_atqa) {
            if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
        } else {
            uint8_t atqa[2] = {};
            if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break;
            data->atqa[0] = atqa[1];
            data->atqa[1] = atqa[0];
        }
        if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break;
 
        parsed = true;
    } while(false);
 
    return parsed;
}
 
static bool nfc_dev_nfca_save_data(FlipperFormat* file, NfcaData* data) {
    furi_assert(file);
    furi_assert(data);
 
    bool saved = false;
    do {
        // Write UID, ATQA, SAK
        if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats"))
            break;
        if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break;
        // Save ATQA in MSB order for correct companion apps display
        uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};
        if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break;
        if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break;
        saved = true;
    } while(false);
 
    return saved;
}
 
static bool nfc_dev_nfca_verify_handler(FuriString* device_type, NfcDevData* data) {
    furi_assert(device_type);
    furi_assert(data);
 
    bool verified = (furi_string_cmp_str(device_type, "UID") == 0);
    if(verified) {
        data->protocol = NfcDevProtocolNfca;
    }
 
    return verified;
}
 
static bool nfc_dev_nfca_save_handler(FlipperFormat* file, uint32_t version, NfcDevData* data) {
    furi_assert(file);
    furi_assert(data);
    UNUSED(version);
 
    bool saved = false;
    do {
        if(!flipper_format_write_string_cstr(file, "Device type", "UID")) break;
        if(!nfc_dev_nfca_save_data(file, &data->nfca_data)) break;
        saved = true;
    } while(false);
 
    return saved;
}
 
static bool nfc_dev_nfca_load_handler(FlipperFormat* file, uint32_t version, NfcDevData* data) {
    furi_assert(file);
    furi_assert(data);
    UNUSED(version);
 
    return nfc_dev_nfca_load_data(file, version, &data->nfca_data);
}
 
static bool nfc_dev_mf_ultralight_verify_handler(FuriString* device_type, NfcDevData* data) {
    furi_assert(device_type);
    furi_assert(data);
 
    bool verified = false;
    for(size_t i = 0; i < MfUltralightTypeNum; i++) {
        const char* name = mf_ultralight_get_name(i, true);
        verified = furi_string_equal_str(device_type, name);
        if(verified) {
            data->protocol = NfcDevProtocolMfUltralight;
            data->mf_ul_data.type = i;
            break;
        }
    }
 
    return verified;
}
 
static bool
    nfc_dev_mf_ultralight_save_handler(FlipperFormat* file, uint32_t version, NfcDevData* data) {
    furi_assert(file);
    furi_assert(data);
    UNUSED(version);
 
    FuriString* temp_str = furi_string_alloc();
    MfUltralightData* mfu_data = &data->mf_ul_data;
    bool saved = false;
    do {
        const char* device_type_name = mf_ultralight_get_name(mfu_data->type, true);
        if(!flipper_format_write_string_cstr(file, "Device type", device_type_name)) break;
        if(!nfc_dev_nfca_save_data(file, &data->nfca_data)) break;
        if(!flipper_format_write_comment_cstr(file, "Mifare Ultralight specific data")) break;
        if(!flipper_format_write_uint32(
               file, "Data format version", &nfc_mifare_ultralight_data_format_version, 1))
            break;
        if(!flipper_format_write_hex(
               file, "Signature", mfu_data->signature.data, sizeof(MfUltralightSignature)))
            break;
        if(!flipper_format_write_hex(
               file, "Mifare version", (uint8_t*)&mfu_data->version, sizeof(MfUltralightVersion)))
            break;
 
        // Write conters and tearing flags data
        bool counters_saved = true;
        for(size_t i = 0; i < 3; i++) {
            furi_string_printf(temp_str, "Counter %d", i);
            if(!flipper_format_write_uint32(
                   file, furi_string_get_cstr(temp_str), &mfu_data->counter[i].counter, 1)) {
                counters_saved = false;
                break;
            }
            furi_string_printf(temp_str, "Tearing %d", i);
            if(!flipper_format_write_hex(
                   file, furi_string_get_cstr(temp_str), mfu_data->tearing_flag->data, 1)) {
                counters_saved = false;
                break;
            }
        }
        if(!counters_saved) break;
 
        // Write pages data
        uint32_t pages_total = mfu_data->pages_total;
        uint32_t pages_read = mfu_data->pages_read;
        if(!flipper_format_write_uint32(file, "Pages total", &pages_total, 1)) break;
        if(!flipper_format_write_uint32(file, "Pages read", &pages_read, 1)) break;
        bool pages_saved = true;
        for(size_t i = 0; i < mfu_data->pages_total; i++) {
            furi_string_printf(temp_str, "Page %d", i);
            if(!flipper_format_write_hex(
                   file,
                   furi_string_get_cstr(temp_str),
                   mfu_data->page[i].data,
                   sizeof(MfUltralightPage))) {
                pages_saved = false;
                break;
            }
        }
        if(!pages_saved) break;
 
        // Write authentication counter
        if(!flipper_format_write_uint32(
               file, "Failed authentication attempts", &mfu_data->auth_attempts, 1))
            break;
 
        saved = true;
    } while(false);
 
    furi_string_free(temp_str);
 
    return saved;
}
 
static bool
    nfc_dev_mf_ultralight_load_handler(FlipperFormat* file, uint32_t version, NfcDevData* data) {
    furi_assert(file);
    furi_assert(data);
 
    FuriString* temp_str = furi_string_alloc();
    bool parsed = false;
    do {
        // Read NFCA data
        if(!nfc_dev_nfca_load_data(file, version, &data->mf_ul_data.nfca_data)) break;
 
        // Read Ultralight specific data
        // Read Mifare Ultralight format version
        uint32_t data_format_version = 0;
        if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) {
            if(!flipper_format_rewind(file)) break;
        }
 
        // Read signature
        MfUltralightData* mfu_data = &data->mf_ul_data;
        if(!flipper_format_read_hex(
               file, "Signature", mfu_data->signature.data, sizeof(MfUltralightSignature)))
            break;
        // Read Mifare version
        if(!flipper_format_read_hex(
               file, "Mifare version", (uint8_t*)&mfu_data->version, sizeof(MfUltralightVersion)))
            break;
        // Read counters and tearing flags
        bool counters_parsed = true;
        for(size_t i = 0; i < 3; i++) {
            furi_string_printf(temp_str, "Counter %d", i);
            if(!flipper_format_read_uint32(
                   file, furi_string_get_cstr(temp_str), &mfu_data->counter[i].counter, 1)) {
                counters_parsed = false;
                break;
            }
            furi_string_printf(temp_str, "Tearing %d", i);
            if(!flipper_format_read_hex(
                   file, furi_string_get_cstr(temp_str), mfu_data->tearing_flag[i].data, 1)) {
                counters_parsed = false;
                break;
            }
        }
        if(!counters_parsed) break;
        // Read pages
        uint32_t pages_total = 0;
        if(!flipper_format_read_uint32(file, "Pages total", &pages_total, 1)) break;
        uint32_t pages_read = 0;
        if(data_format_version < nfc_mifare_ultralight_data_format_version) {
            pages_read = pages_total;
        } else {
            if(!flipper_format_read_uint32(file, "Pages read", &pages_read, 1)) break;
        }
        mfu_data->pages_total = pages_total;
        mfu_data->pages_read = pages_read;
 
        if((pages_read > MF_ULTRALIGHT_MAX_PAGE_NUM) || (pages_total > MF_ULTRALIGHT_MAX_PAGE_NUM))
            break;
 
        bool pages_parsed = true;
        for(size_t i = 0; i < pages_total; i++) {
            furi_string_printf(temp_str, "Page %d", i);
            if(!flipper_format_read_hex(
                   file,
                   furi_string_get_cstr(temp_str),
                   mfu_data->page[i].data,
                   sizeof(MfUltralightPage))) {
                pages_parsed = false;
                break;
            }
        }
        if(!pages_parsed) break;
 
        // Read authentication counter
        if(!flipper_format_read_uint32(
               file, "Failed authentication attempts", &mfu_data->auth_attempts, 1)) {
            mfu_data->auth_attempts = 0;
        }
 
        parsed = true;
    } while(false);
 
    furi_string_free(temp_str);
 
    return parsed;
}
 
static bool nfc_dev_mf_classic_verify_handler(FuriString* device_type, NfcDevData* data) {
    furi_assert(device_type);
    furi_assert(data);
 
    bool verified = furi_string_equal_str(device_type, "Mifare Classic");
    if(verified) {
        data->protocol = NfcDevProtocolMfClassic;
    }
 
    return verified;
}
 
static void nfc_dev_set_mf_classic_block_str(
    FuriString* block_str,
    MfClassicData* data,
    uint8_t block_num) {
    furi_string_reset(block_str);
    bool is_sec_trailer = mf_classic_is_sector_trailer(block_num);
    if(is_sec_trailer) {
        uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
        MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num);
        // Write key A
        for(size_t i = 0; i < sizeof(sec_tr->key_a); i++) {
            if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeA)) {
                furi_string_cat_printf(block_str, "%02X ", sec_tr->key_a.data[i]);
            } else {
                furi_string_cat_printf(block_str, "?? ");
            }
        }
        // Write Access bytes
        for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i++) {
            if(mf_classic_is_block_read(data, block_num)) {
                furi_string_cat_printf(block_str, "%02X ", sec_tr->access_bits.data[i]);
            } else {
                furi_string_cat_printf(block_str, "?? ");
            }
        }
        // Write key B
        for(size_t i = 0; i < sizeof(sec_tr->key_b); i++) {
            if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeB)) {
                furi_string_cat_printf(block_str, "%02X ", sec_tr->key_b.data[i]);
            } else {
                furi_string_cat_printf(block_str, "?? ");
            }
        }
    } else {
        // Write data block
        for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) {
            if(mf_classic_is_block_read(data, block_num)) {
                furi_string_cat_printf(block_str, "%02X ", data->block[block_num].data[i]);
            } else {
                furi_string_cat_printf(block_str, "?? ");
            }
        }
    }
    furi_string_trim(block_str);
}
 
static bool
    nfc_dev_mf_classic_save_handler(FlipperFormat* file, uint32_t version, NfcDevData* data) {
    furi_assert(file);
    furi_assert(data);
    UNUSED(version);
 
    FuriString* temp_str = furi_string_alloc();
    MfClassicData* mfc_data = &data->mf_classic_data;
    bool saved = false;
 
    do {
        if(!flipper_format_write_string_cstr(file, "Device type", "Mifare Classic")) break;
        if(!nfc_dev_nfca_save_data(file, &data->nfca_data)) break;
        if(!flipper_format_write_string_cstr(file, "Device type", "")) break;
        if(!flipper_format_write_comment_cstr(file, "Mifare Classic specific data")) break;
        const char* type_name = mf_classic_get_name(mfc_data->type, false);
        if(!flipper_format_write_string_cstr(file, "Mifare Classic type", type_name)) break;
        if(!flipper_format_write_uint32(
               file, "Data format version", &nfc_mifare_classic_data_format_version, 1))
            break;
        if(!flipper_format_write_comment_cstr(
               file, "Mifare Classic blocks, \'??\' means unknown data"))
            break;
 
        uint16_t blocks_total = mf_classic_get_total_block_num(mfc_data->type);
        FuriString* block_str = furi_string_alloc();
        bool block_saved = true;
        for(size_t i = 0; i < blocks_total; i++) {
            furi_string_printf(temp_str, "Block %d", i);
            nfc_dev_set_mf_classic_block_str(block_str, mfc_data, i);
            if(!flipper_format_write_string(file, furi_string_get_cstr(temp_str), block_str)) {
                block_saved = false;
                break;
            }
        }
        furi_string_free(block_str);
        if(!block_saved) break;
 
        saved = true;
    } while(false);
 
    furi_string_free(temp_str);
 
    return saved;
}
 
static void nfc_device_parse_mifare_classic_block(
    FuriString* block_str,
    MfClassicData* data,
    uint8_t block_num) {
    furi_string_trim(block_str);
    MfClassicBlock block_tmp = {};
    bool is_sector_trailer = mf_classic_is_sector_trailer(block_num);
    uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
    uint16_t block_unknown_bytes_mask = 0;
 
    furi_string_trim(block_str);
    for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) {
        char hi = furi_string_get_char(block_str, 3 * i);
        char low = furi_string_get_char(block_str, 3 * i + 1);
        uint8_t byte = 0;
        if(hex_char_to_uint8(hi, low, &byte)) {
            block_tmp.data[i] = byte;
        } else {
            FURI_BIT_SET(block_unknown_bytes_mask, i);
        }
    }
 
    if(block_unknown_bytes_mask != 0xffff) {
        if(is_sector_trailer) {
            MfClassicSectorTrailer* sec_tr_tmp = (MfClassicSectorTrailer*)&block_tmp;
            // Load Key A
            // Key A mask 0b0000000000111111 = 0x003f
            if((block_unknown_bytes_mask & 0x003f) == 0) {
                uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_a.data, sizeof(MfClassicKey));
                mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeA, key);
            }
            // Load Access Bits
            // Access bits mask 0b0000001111000000 = 0x03c0
            if((block_unknown_bytes_mask & 0x03c0) == 0) {
                mf_classic_set_block_read(data, block_num, &block_tmp);
            }
            // Load Key B
            // Key B mask 0b1111110000000000 = 0xfc00
            if((block_unknown_bytes_mask & 0xfc00) == 0) {
                uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_b.data, sizeof(MfClassicKey));
                mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeB, key);
            }
        } else {
            if(block_unknown_bytes_mask == 0) {
                mf_classic_set_block_read(data, block_num, &block_tmp);
            }
        }
    }
}
 
static bool
    nfc_dev_mf_classic_load_handler(FlipperFormat* file, uint32_t version, NfcDevData* data) {
    furi_assert(file);
    furi_assert(data);
 
    MfClassicData* mfc_data = &data->mf_classic_data;
    FuriString* temp_str = furi_string_alloc();
    bool parsed = false;
 
    do {
        // Read NFCA data
        if(!nfc_dev_nfca_load_data(file, version, &data->mf_ul_data.nfca_data)) break;
 
        // Read Mifare Classic type
        if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break;
        bool type_parsed = false;
        for(size_t i = 0; i < MfClassicTypeNum; i++) {
            const char* type_name = mf_classic_get_name(i, false);
            if(furi_string_equal_str(temp_str, type_name)) {
                mfc_data->type = i;
                type_parsed = true;
            }
        }
        if(!type_parsed) break;
 
        // Read format version
        uint32_t data_format_version = 0;
        bool old_format = false;
        // Read Mifare Classic format version
        if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) {
            // Load unread sectors with zero keys access for backward compatibility
            if(!flipper_format_rewind(file)) break;
            old_format = true;
        } else {
            if(data_format_version < nfc_mifare_classic_data_format_version) {
                old_format = true;
            }
        }
 
        // Read Mifare Classic blocks
        bool block_read = true;
        FuriString* block_str = furi_string_alloc();
        uint16_t blocks_total = mf_classic_get_total_block_num(mfc_data->type);
        for(size_t i = 0; i < blocks_total; i++) {
            furi_string_printf(temp_str, "Block %d", i);
            if(!flipper_format_read_string(file, furi_string_get_cstr(temp_str), block_str)) {
                block_read = false;
                break;
            }
            nfc_device_parse_mifare_classic_block(block_str, mfc_data, i);
        }
        furi_string_free(block_str);
        if(!block_read) break;
 
        // Set keys and blocks as unknown for backward compatibility
        if(old_format) {
            mfc_data->key_a_mask = 0ULL;
            mfc_data->key_b_mask = 0ULL;
            memset(mfc_data->block_read_mask, 0, sizeof(mfc_data->block_read_mask));
        }
 
        parsed = true;
    } while(false);
 
    furi_string_free(temp_str);
 
    return parsed;
}
 
static bool nfc_dev_mf_desfire_verify_handler(FuriString* device_type, NfcDevData* data) {
    furi_assert(device_type);
    furi_assert(data);
 
    bool verified = furi_string_equal_str(device_type, "Mifare Desfire");
    if(verified) {
        data->protocol = NfcDevProtocolMfDesfire;
    }
 
    return verified;
}
 
static bool
    nfc_dev_mf_desfire_save_handler(FlipperFormat* file, uint32_t version, NfcDevData* data) {
    furi_assert(file);
    furi_assert(data);
    UNUSED(version);
 
    bool saved = false;
 
    do {
        if(!nfc_dev_nfca_save_data(file, &data->nfca_data)) break;
        saved = true;
    } while(false);
 
    return saved;
}
 
static bool
    nfc_dev_mf_desfire_load_handler(FlipperFormat* file, uint32_t version, NfcDevData* data) {
    furi_assert(file);
    furi_assert(data);
 
    bool parsed = false;
    do {
        // Read NFCA data
        if(!nfc_dev_nfca_load_data(file, version, &data->mf_ul_data.nfca_data)) break;
 
        parsed = true;
    } while(false);
 
    return parsed;
}
 
static const NfcDevDataParser nfc_dev_data_parser[NfcDevProtocolNum] = {
    [NfcDevProtocolNfca] =
        {
            .verify_handler = nfc_dev_nfca_verify_handler,
            .save_handler = nfc_dev_nfca_save_handler,
            .load_handler = nfc_dev_nfca_load_handler,
        },
    [NfcDevProtocolMfUltralight] =
        {
            .verify_handler = nfc_dev_mf_ultralight_verify_handler,
            .save_handler = nfc_dev_mf_ultralight_save_handler,
            .load_handler = nfc_dev_mf_ultralight_load_handler,
        },
    [NfcDevProtocolMfClassic] =
        {
            .verify_handler = nfc_dev_mf_classic_verify_handler,
            .save_handler = nfc_dev_mf_classic_save_handler,
            .load_handler = nfc_dev_mf_classic_load_handler,
        },
    [NfcDevProtocolMfDesfire] =
        {
            .verify_handler = nfc_dev_mf_desfire_verify_handler,
            .save_handler = nfc_dev_mf_desfire_save_handler,
            .load_handler = nfc_dev_mf_desfire_load_handler,
        },
 
};
 
NfcDev* nfc_dev_alloc() {
    NfcDev* instance = malloc(sizeof(NfcDev));
 
    return instance;
}
 
void nfc_dev_free(NfcDev* instance) {
    furi_assert(instance);
    free(instance);
}
 
void nfc_dev_set_loading_callback(NfcDev* instance, NfcLoadingCallback callback, void* context) {
    furi_assert(instance);
    furi_assert(callback);
 
    instance->loading_callback = callback;
    instance->loading_callback_context = context;
}
 
bool nfc_dev_save(NfcDev* instance, NfcDevData* data, const char* path) {
    furi_assert(instance);
    furi_assert(data);
    furi_assert(path);
 
    bool saved = false;
    Storage* storage = furi_record_open(RECORD_STORAGE);
    FlipperFormat* file = flipper_format_file_alloc(storage);
 
    FuriString* temp_str;
    temp_str = furi_string_alloc();
 
    if(instance->loading_callback) {
        instance->loading_callback(instance->loading_callback_context, true);
    }
 
    do {
        // Open file
        if(!flipper_format_file_open_always(file, path)) break;
 
        // Write header
        if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
 
        // Write nfc device type
        if(!flipper_format_write_comment_cstr(
               file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic"))
            break;
 
        for(size_t i = 0; i < COUNT_OF(nfc_dev_data_parser); i++) {
            if(data->protocol == i) {
                saved = nfc_dev_data_parser[i].save_handler(file, nfc_file_version, data);
            }
            if(saved) break;
        }
    } while(false);
 
    if(instance->loading_callback) {
        instance->loading_callback(instance->loading_callback_context, false);
    }
 
    furi_string_free(temp_str);
    flipper_format_free(file);
    furi_record_close(RECORD_STORAGE);
 
    return saved;
}
 
bool nfc_dev_load(NfcDev* instance, NfcDevData* data, const char* path) {
    furi_assert(instance);
    furi_assert(data);
    furi_assert(path);
 
    bool loaded = false;
    Storage* storage = furi_record_open(RECORD_STORAGE);
    FlipperFormat* file = flipper_format_file_alloc(storage);
 
    FuriString* temp_str;
    temp_str = furi_string_alloc();
 
    if(instance->loading_callback) {
        instance->loading_callback(instance->loading_callback_context, true);
    }
 
    do {
        if(!flipper_format_file_open_existing(file, path)) break;
 
        // Read and verify file header
        uint32_t version = 0;
        if(!flipper_format_read_header(file, temp_str, &version)) break;
        if(furi_string_cmp_str(temp_str, nfc_file_header)) break;
        if(version < nfc_file_version_with_lsb_atqa) break;
 
        // Read Nfc device type
        if(!flipper_format_read_string(file, "Device type", temp_str)) break;
 
        for(size_t i = 0; i < COUNT_OF(nfc_dev_data_parser); i++) {
            if(nfc_dev_data_parser[i].verify_handler(temp_str, data)) {
                loaded = nfc_dev_data_parser[i].load_handler(file, version, data);
            }
            if(loaded) break;
        }
    } while(false);
 
    if(instance->loading_callback) {
        instance->loading_callback(instance->loading_callback_context, false);
    }
 
    furi_string_free(temp_str);
    flipper_format_free(file);
    furi_record_close(RECORD_STORAGE);
 
    return loaded;
}

V619 The array 'mfu_data->tearing_flag' is being utilized as a pointer to single object.

V1027 Pointer to an object of the 'MfClassicBlock' class is cast to unrelated 'MfClassicSectorTrailer' class.