 4bf29827f8
			
		
	
	
		4bf29827f8
		
			
		
	
	
	
	
		
			
			* Quicksave 1 * Header stage complete * Source stage complete * Lint & merge fixes * Includes * Documentation step 1 * FBT: output free size considering BT STACK * Documentation step 2 * py lint * Fix music player plugin * unit test stage 1: string allocator, mem, getters, setters, appends, compare, search. * unit test: string equality * unit test: string replace * unit test: string start_with, end_with * unit test: string trim * unit test: utf-8 * Rename * Revert fw_size changes * Simplify CLI backspace handling * Simplify CLI character insert * Merge fixes * Furi: correct filenaming and spelling * Bt: remove furi string include Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
		
			
				
	
	
		
			113 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			113 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "nfc_supported_card.h"
 | |
| #include "all_in_one.h"
 | |
| 
 | |
| #include <gui/modules/widget.h>
 | |
| #include <nfc_worker_i.h>
 | |
| 
 | |
| #include "furi_hal.h"
 | |
| 
 | |
| #define ALL_IN_ONE_LAYOUT_UNKNOWN 0
 | |
| #define ALL_IN_ONE_LAYOUT_A 1
 | |
| #define ALL_IN_ONE_LAYOUT_D 2
 | |
| #define ALL_IN_ONE_LAYOUT_E2 3
 | |
| #define ALL_IN_ONE_LAYOUT_E3 4
 | |
| #define ALL_IN_ONE_LAYOUT_E5 5
 | |
| #define ALL_IN_ONE_LAYOUT_2 6
 | |
| 
 | |
| uint8_t all_in_one_get_layout(NfcDeviceData* dev_data) {
 | |
|     // I absolutely hate what's about to happen here.
 | |
| 
 | |
|     // Switch on the second half of the third byte of page 5
 | |
|     FURI_LOG_I("all_in_one", "Layout byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 2]);
 | |
|     FURI_LOG_I(
 | |
|         "all_in_one", "Layout half-byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 3] & 0x0F);
 | |
|     switch(dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F) {
 | |
|     // If it is A, the layout type is a type A layout
 | |
|     case 0x0A:
 | |
|         return ALL_IN_ONE_LAYOUT_A;
 | |
|     case 0x0D:
 | |
|         return ALL_IN_ONE_LAYOUT_D;
 | |
|     case 0x02:
 | |
|         return ALL_IN_ONE_LAYOUT_2;
 | |
|     default:
 | |
|         FURI_LOG_I(
 | |
|             "all_in_one",
 | |
|             "Unknown layout type: %d",
 | |
|             dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F);
 | |
|         return ALL_IN_ONE_LAYOUT_UNKNOWN;
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
 | |
|     UNUSED(nfc_worker);
 | |
|     // If this is a all_in_one pass, first 2 bytes of page 4 are 0x45 0xD9
 | |
|     MfUltralightReader reader = {};
 | |
|     MfUltralightData data = {};
 | |
| 
 | |
|     if(!mf_ul_read_card(tx_rx, &reader, &data)) {
 | |
|         return false;
 | |
|     } else {
 | |
|         if(data.data[4 * 4] == 0x45 && data.data[4 * 4 + 1] == 0xD9) {
 | |
|             FURI_LOG_I("all_in_one", "Pass verified");
 | |
|             return true;
 | |
|         }
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
 | |
|     MfUltralightReader reader = {};
 | |
|     MfUltralightData data = {};
 | |
|     if(!mf_ul_read_card(tx_rx, &reader, &data)) {
 | |
|         return false;
 | |
|     } else {
 | |
|         memcpy(&nfc_worker->dev_data->mf_ul_data, &data, sizeof(data));
 | |
|         FURI_LOG_I("all_in_one", "Card read");
 | |
|         return true;
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool all_in_one_parser_parse(NfcDeviceData* dev_data) {
 | |
|     if(dev_data->mf_ul_data.data[4 * 4] != 0x45 || dev_data->mf_ul_data.data[4 * 4 + 1] != 0xD9) {
 | |
|         FURI_LOG_I("all_in_one", "Pass not verified");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // If the layout is a then the ride count is stored in the first byte of page 8
 | |
|     uint8_t ride_count = 0;
 | |
|     uint32_t serial = 0;
 | |
|     if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_A) {
 | |
|         ride_count = dev_data->mf_ul_data.data[4 * 8];
 | |
|     } else if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_D) {
 | |
|         // If the layout is D, the ride count is stored in the second byte of page 9
 | |
|         ride_count = dev_data->mf_ul_data.data[4 * 9 + 1];
 | |
|         // I hate this with a burning passion.
 | |
| 
 | |
|         // The number starts at the second half of the third byte on page 4, and is 32 bits long
 | |
|         // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte
 | |
|         // B8 17 A2 A4 BD becomes 81 7A 2A 4B
 | |
|         serial = (dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 |
 | |
|                  dev_data->mf_ul_data.data[4 * 4 + 3] << 20 |
 | |
|                  dev_data->mf_ul_data.data[4 * 4 + 4] << 12 |
 | |
|                  dev_data->mf_ul_data.data[4 * 4 + 5] << 4 |
 | |
|                  (dev_data->mf_ul_data.data[4 * 4 + 6] >> 4);
 | |
|     } else {
 | |
|         FURI_LOG_I("all_in_one", "Unknown layout: %d", all_in_one_get_layout(dev_data));
 | |
|         ride_count = 137;
 | |
|     }
 | |
| 
 | |
|     // I hate this with a burning passion.
 | |
| 
 | |
|     // The number starts at the second half of the third byte on page 4, and is 32 bits long
 | |
|     // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte
 | |
|     // B8 17 A2 A4 BD becomes 81 7A 2A 4B
 | |
|     serial =
 | |
|         (dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 |
 | |
|         dev_data->mf_ul_data.data[4 * 4 + 3] << 20 | dev_data->mf_ul_data.data[4 * 4 + 4] << 12 |
 | |
|         dev_data->mf_ul_data.data[4 * 4 + 5] << 4 | (dev_data->mf_ul_data.data[4 * 4 + 6] >> 4);
 | |
| 
 | |
|     // Format string for rides count
 | |
|     furi_string_printf(
 | |
|         dev_data->parsed_data, "\e#All-In-One\nNumber: %u\nRides left: %u", serial, ride_count);
 | |
|     return true;
 | |
| } |