فهرست منبع

Resolved merge conflict in gocard.c

Luu 10 ماه پیش
والد
کامیت
ae0cb269f0

+ 5 - 0
CHANGELOG.md

@@ -37,3 +37,8 @@ Big update!
 - Unified Calypso Parser: A new unified Calypso parser has been introduced (thanks to DocSystem), streamlining Calypso card support.
 - RavKav Moved to Calypso Parser: RavKav has been moved to the new unified Calypso parser (credit to luu176).
 
+## v0.6
+
+- Added a load mode and a save mode to store card info
+- Fixed a major bug due to API symbol not existing
+

+ 4 - 0
api/calypso/transit/ravkav.c

@@ -20,6 +20,10 @@ const char* get_ravkav_issuer(int issuer) {
 
 void show_ravkav_contract_info(RavKavCardContract* contract, FuriString* parsed_data) {
     // Core contract validity period
+    if(contract->balance != 0.0f) {
+        furi_string_cat_printf(parsed_data, "Balance: %.2f ILS\n", (double)contract->balance);
+    }
+
     furi_string_cat_printf(parsed_data, "Valid from: ");
     locale_format_datetime_cat(parsed_data, &contract->start_date, false);
     if(contract->end_date_available) {

+ 1 - 0
api/calypso/transit/ravkav_i.h

@@ -23,6 +23,7 @@ typedef struct {
     bool end_date_available;
 
     bool present;
+    float balance;
 } RavKavCardContract;
 
 typedef struct {

+ 9 - 0
application.fam

@@ -102,3 +102,12 @@ App(
     sources=["scenes/plugins/troika.c"],
     fal_embedded=True,
 )
+
+App(
+    appid="gocard_plugin",
+    apptype=FlipperAppType.PLUGIN,
+    entry_point="gocard_plugin_ep",
+    requires=["metroflip"],
+    sources=["scenes/plugins/gocard.c"],
+    fal_embedded=True,
+)

+ 1 - 1
manifest.yml

@@ -15,7 +15,7 @@ screenshots:
 short_description: 'An implementation of Metrodroid on the Flipper Zero'
 sourcecode:
   location:
-    commit_sha: ac4a57a8fc31cc47195239cad1515ce141fb440c
+    commit_sha: c4e1ed2304ad96a3565f83a58b3c7f8529f9d651
     origin: https://github.com/luu176/Metroflip
     subdir:
   type: git

+ 2 - 1
metroflip.c

@@ -157,7 +157,8 @@ void metroflip_exit_widget_callback(GuiButtonType result, InputType type, void*
     UNUSED(result);
 
     if(type == InputTypeShort) {
-        scene_manager_next_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
     }
 }
 

+ 35 - 0
scenes/keys.c

@@ -33,6 +33,12 @@ const MfClassicKeyPair metromoney_1k_verify_key[] = {
     {.a = 0x9C616585E26D},
 };
 
+const uint8_t gocard_verify_data[1][14] = {
+    {0x16, 0x18, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x5A, 0x5B, 0x20, 0x21, 0x22, 0x23}};
+
+const uint8_t gocard_verify_data2[1][14] = {
+    {0x16, 0x18, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x01, 0x01}};
+
 static bool charliecard_verify(Nfc* nfc, MfClassicData* mfc_data, bool data_loaded) {
     bool verified = false;
     FURI_LOG_I(TAG, "verifying charliecard..");
@@ -244,6 +250,33 @@ static bool troika_verify(Nfc* nfc, MfClassicData* mfc_data, bool data_loaded) {
            troika_verify_type(nfc, mfc_data, data_loaded, MfClassicType4k);
 }
 
+static bool gocard_verify(MfClassicData* mfc_data, bool data_loaded) {
+    bool verified = false;
+    FURI_LOG_I(TAG, "verifying charliecard..");
+    do {
+        if(data_loaded) {
+            uint8_t* buffer = &mfc_data->block[1].data[1];
+            size_t buffer_size = 14;
+
+            if(memcmp(buffer, gocard_verify_data[0], buffer_size) == 0) {
+                FURI_LOG_I(TAG, "Match!");
+            } else {
+                FURI_LOG_I(TAG, "No match.");
+                if(memcmp(buffer, gocard_verify_data2[0], buffer_size) == 0) {
+                    FURI_LOG_I(TAG, "Match!");
+                } else {
+                    FURI_LOG_I(TAG, "No match.");
+                    break;
+                }
+            }
+
+            verified = true;
+        }
+    } while(false);
+
+    return verified;
+}
+
 CardType determine_card_type(Nfc* nfc, MfClassicData* mfc_data, bool data_loaded) {
     FURI_LOG_I(TAG, "checking keys..");
     UNUSED(bip_verify);
@@ -258,6 +291,8 @@ CardType determine_card_type(Nfc* nfc, MfClassicData* mfc_data, bool data_loaded
         return CARD_TYPE_TROIKA;
     } else if(charliecard_verify(nfc, mfc_data, data_loaded)) {
         return CARD_TYPE_CHARLIECARD;
+    } else if(gocard_verify(mfc_data, data_loaded)) {
+        return CARD_TYPE_GOCARD;
     } else {
         FURI_LOG_I(TAG, "its unknown");
         return CARD_TYPE_UNKNOWN;

+ 3 - 1
scenes/keys.h

@@ -9,6 +9,7 @@ typedef enum {
     CARD_TYPE_CHARLIECARD,
     CARD_TYPE_SMARTRIDER,
     CARD_TYPE_TROIKA,
+    CARD_TYPE_GOCARD,
     CARD_TYPE_UNKNOWN
 } CardType;
 
@@ -28,9 +29,10 @@ typedef struct {
 
 extern const MfClassicKeyPair troika_1k_keys[16];
 extern const MfClassicKeyPair troika_4k_keys[40];
-extern const uint8_t SMARTRIDER_STANDARD_KEYS[3][6];
 extern const MfClassicKeyPair charliecard_1k_keys[16];
 extern const MfClassicKeyPair bip_1k_keys[16];
 extern const MfClassicKeyPair metromoney_1k_keys[16];
+extern const uint8_t gocard_verify_data[1][14];
+extern const uint8_t gocard_verify_data2[1][14];
 
 #endif // KEYS_H

+ 7 - 2
scenes/metroflip_scene_load.c

@@ -60,6 +60,10 @@ void metroflip_scene_load_on_enter(void* context) {
                         app->card_type = "troika";
                         FURI_LOG_I(TAG, "Detected: Troika\n");
                         break;
+                    case CARD_TYPE_GOCARD:
+                        app->card_type = "gocard";
+                        FURI_LOG_I(TAG, "Detected: go card\n");
+                        break;
                     case CARD_TYPE_UNKNOWN:
                         app->card_type = "unknown";
                         //popup_set_header(popup, "Unsupported\n card", 58, 31, AlignLeft, AlignTop);
@@ -118,9 +122,10 @@ void metroflip_scene_load_on_enter(void* context) {
             app->card_type = furi_string_get_cstr(card_type);
             has_card_type = false;
         }
+        scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
         scene_manager_next_scene(app->scene_manager, MetroflipSceneParse);
     } else {
-        scene_manager_next_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
     }
     furi_string_free(file_path);
     furi_record_close(RECORD_STORAGE);
@@ -133,7 +138,7 @@ bool metroflip_scene_load_on_event(void* context, SceneManagerEvent event) {
     // If they don't select any file in the brwoser and press back button,
     // the data is not loaded
     if(!app->data_loaded) {
-        scene_manager_next_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
     }
     consumed = true;
 

+ 51 - 6
scenes/plugins/calypso.c

@@ -321,6 +321,8 @@ void metroflip_next_button_widget_callback(GuiButtonType result, InputType type,
             ctx->page_id = 0;
             scene_manager_search_and_switch_to_previous_scene(
                 app->scene_manager, MetroflipSceneStart);
+            scene_manager_set_scene_state(
+                app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
             return;
         }
         if(ctx->page_id < 10) {
@@ -352,6 +354,8 @@ void metroflip_next_button_widget_callback(GuiButtonType result, InputType type,
                 ctx->page_id = 0;
                 scene_manager_search_and_switch_to_previous_scene(
                     app->scene_manager, MetroflipSceneStart);
+                scene_manager_set_scene_state(
+                    app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
                 return;
             }
             ctx->page_id += 1;
@@ -359,6 +363,8 @@ void metroflip_next_button_widget_callback(GuiButtonType result, InputType type,
             ctx->page_id = 0;
             scene_manager_search_and_switch_to_previous_scene(
                 app->scene_manager, MetroflipSceneStart);
+            scene_manager_set_scene_state(
+                app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
             return;
         }
 
@@ -1783,8 +1789,17 @@ static NfcCommand calypso_poller_callback(NfcGenericEvent event, void* context)
                     if(card->card_type == CALYPSO_CARD_RAVKAV) {
                         card->ravkav = malloc(sizeof(RavKavCardData));
 
+                        // Prepare calypso structure
+
+                        CalypsoApp* RavKavContractStructure = get_ravkav_contract_structure();
+                        if(!RavKavContractStructure) {
+                            FURI_LOG_E(TAG, "Failed to load RavKav Contract structure");
+                            break;
+                        }
+
+                        //get balance
                         error = select_new_app(
-                            0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
+                            0x20, 0x2A, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
                         if(error != 0) {
                             FURI_LOG_E(TAG, "Failed to select app for contracts");
                             break;
@@ -1793,15 +1808,44 @@ static NfcCommand calypso_poller_callback(NfcGenericEvent event, void* context)
                         // Check the response after selecting app
                         if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
                             FURI_LOG_E(
-                                TAG, "Failed to check response after selecting app for contracts");
+                                TAG, "Failed to check response after selecting app for counter");
                             break;
                         }
 
-                        // Prepare calypso structure
+                        error = read_new_file(
+                            1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
+                        if(error != 0) {
+                            FURI_LOG_E(TAG, "Failed to read counter %d", 1);
+                            break;
+                        }
 
-                        CalypsoApp* RavKavContractStructure = get_ravkav_contract_structure();
-                        if(!RavKavContractStructure) {
-                            FURI_LOG_E(TAG, "Failed to load RavKav Contract structure");
+                        // Check the response after reading the file
+                        if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
+                            FURI_LOG_E(
+                                TAG, "Failed to check response after reading counter %d", 1);
+                            break;
+                        }
+
+                        uint32_t value = 0;
+                        for(uint8_t i = 0; i < 3; i++) {
+                            value = (value << 8) | bit_buffer_get_byte(rx_buffer, i);
+                        }
+                        float result = value / 100.0f;
+                        FURI_LOG_I(TAG, "Value: %.2f ILS", (double)result);
+
+                        card->ravkav->contracts[0].balance = result;
+
+                        error = select_new_app(
+                            0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
+                        if(error != 0) {
+                            FURI_LOG_E(TAG, "Failed to select app for contracts");
+                            break;
+                        }
+
+                        // Check the response after selecting app
+                        if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
+                            FURI_LOG_E(
+                                TAG, "Failed to check response after selecting app for contracts");
                             break;
                         }
 
@@ -2477,6 +2521,7 @@ static bool calypso_on_event(Metroflip* app, SceneManagerEvent event) {
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
         consumed = true;
     }
 

+ 1 - 0
scenes/plugins/charliecard.c

@@ -1320,6 +1320,7 @@ static bool charliecard_on_event(Metroflip* app, SceneManagerEvent event) {
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
         consumed = true;
     }
 

+ 1 - 0
scenes/plugins/clipper.c

@@ -671,6 +671,7 @@ static bool clipper_on_event(Metroflip* app, SceneManagerEvent event) {
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
         consumed = true;
     }
 

+ 39 - 0
scenes/plugins/gocard.c

@@ -14,6 +14,7 @@
 #include <nfc/nfc_listener.h>
 #include "../../api/metroflip/metroflip_api.h"
 #include "../../metroflip_plugins.h"
+<<<<<<< HEAD
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
@@ -42,12 +43,18 @@ void printConcessionType(unsigned short concession_type, FuriString* parsed_data
     }
 }
 
+=======
+
+#define TAG "Metroflip:Scene:gocard"
+
+>>>>>>> ac4922c34e448b30b3fe49a80f4d4ba236bf1bd9
 unsigned short byteArrayToIntReversed(unsigned int dec1, unsigned int dec2) {
     unsigned char byte1 = (unsigned char)dec1;
     unsigned char byte2 = (unsigned char)dec2;
     return ((unsigned short)byte2 << 8) | byte1;
 }
 
+<<<<<<< HEAD
 // Function to extract a substring and convert binary to decimal
 uint32_t extract_and_convert(const char* str, int start, int length) {
     uint32_t value = 0;
@@ -142,10 +149,38 @@ void parse_gocard_topup_info(FuriString* parsed_data, const MfClassicData* data)
     }
 }
 
+=======
+>>>>>>> ac4922c34e448b30b3fe49a80f4d4ba236bf1bd9
 static bool gocard_parse(FuriString* parsed_data, const MfClassicData* data) {
     bool parsed = false;
 
     do {
+<<<<<<< HEAD
+=======
+        // Verify key
+        //const uint8_t ticket_sector_number = 1;
+        //const uint8_t ticket_block_number = 1;
+
+        //const MfClassicSectorTrailer* sec_tr =
+        //    mf_classic_get_sector_trailer_by_sector(data, ticket_sector_number);
+
+        //const uint64_t key =
+        //    bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
+        ///if(key != gocard_1k_keys[ticket_sector_number].a) break;
+        //FURI_LOG_D(TAG, "passed key check");
+        // Parse data
+        //const uint8_t start_block_num =
+        //    mf_classic_get_first_block_num_of_sector(ticket_sector_number);
+
+        //const uint8_t* block_start_ptr =
+        //    &data->block[start_block_num + ticket_block_number].data[0];
+
+        //uint32_t balance = bit_lib_bytes_to_num_le(block_start_ptr, 4) - 100;
+
+        //uint32_t balance_lari = balance / 100;
+        //uint8_t balance_tetri = balance % 100;
+
+>>>>>>> ac4922c34e448b30b3fe49a80f4d4ba236bf1bd9
         int balance_slot = 4;
 
         if(data->block[balance_slot].data[13] <= data->block[balance_slot + 1].data[13])
@@ -166,6 +201,7 @@ static bool gocard_parse(FuriString* parsed_data, const MfClassicData* data) {
         }
 
         double balance = balancecents / 100.0;
+<<<<<<< HEAD
         furi_string_printf(parsed_data, "\e#go card\nValue: A$%.2f\n", balance); //show balance
 
         hasTravelPassAvailable = (data->block[balance_slot].data[7] != 0x00) ? true : false;
@@ -183,6 +219,9 @@ static bool gocard_parse(FuriString* parsed_data, const MfClassicData* data) {
         printConcessionType(concession_type, parsed_data);
 
         parse_gocard_topup_info(parsed_data, data);
+=======
+        furi_string_printf(parsed_data, "\e#Go card\nValue: A$%.2f\n", balance);
+>>>>>>> ac4922c34e448b30b3fe49a80f4d4ba236bf1bd9
 
         parsed = true;
     } while(false);

+ 1 - 0
scenes/plugins/itso.c

@@ -217,6 +217,7 @@ static bool itso_on_event(Metroflip* app, SceneManagerEvent event) {
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
         consumed = true;
     }
 

+ 1 - 0
scenes/plugins/metromoney.c

@@ -211,6 +211,7 @@ static bool metromoney_on_event(Metroflip* app, SceneManagerEvent event) {
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
         consumed = true;
     }
 

+ 1 - 0
scenes/plugins/myki.c

@@ -200,6 +200,7 @@ static bool myki_on_event(Metroflip* app, SceneManagerEvent event) {
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
         consumed = true;
     }
 

+ 1 - 0
scenes/plugins/opal.c

@@ -314,6 +314,7 @@ static bool opal_on_event(Metroflip* app, SceneManagerEvent event) {
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
         consumed = true;
     }
 

+ 1 - 0
scenes/plugins/smartrider.c

@@ -402,6 +402,7 @@ static bool smartrider_on_event(Metroflip* app, SceneManagerEvent event) {
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
         consumed = true;
     }
 

+ 1 - 0
scenes/plugins/troika.c

@@ -306,6 +306,7 @@ static bool troika_on_event(Metroflip* app, SceneManagerEvent event) {
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
+        scene_manager_set_scene_state(app->scene_manager, MetroflipSceneStart, MetroflipSceneAuto);
         consumed = true;
     }