gocard.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #include <flipper_application.h>
  2. #include "../../metroflip_i.h"
  3. #include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
  4. #include <nfc/protocols/mf_classic/mf_classic.h>
  5. #include <nfc/protocols/mf_classic/mf_classic_poller.h>
  6. #include <dolphin/dolphin.h>
  7. #include <bit_lib.h>
  8. #include <furi_hal.h>
  9. #include <nfc/nfc.h>
  10. #include <nfc/nfc_device.h>
  11. #include <nfc/nfc_listener.h>
  12. #include "../../api/metroflip/metroflip_api.h"
  13. #include "../../metroflip_plugins.h"
  14. #define TAG "Metroflip:Scene:gocard"
  15. unsigned short byteArrayToIntReversed(unsigned int dec1, unsigned int dec2) {
  16. unsigned char byte1 = (unsigned char)dec1;
  17. unsigned char byte2 = (unsigned char)dec2;
  18. return ((unsigned short)byte2 << 8) | byte1;
  19. }
  20. static bool gocard_parse(FuriString* parsed_data, const MfClassicData* data) {
  21. bool parsed = false;
  22. do {
  23. // Verify key
  24. //const uint8_t ticket_sector_number = 1;
  25. //const uint8_t ticket_block_number = 1;
  26. //const MfClassicSectorTrailer* sec_tr =
  27. // mf_classic_get_sector_trailer_by_sector(data, ticket_sector_number);
  28. //const uint64_t key =
  29. // bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
  30. ///if(key != gocard_1k_keys[ticket_sector_number].a) break;
  31. //FURI_LOG_D(TAG, "passed key check");
  32. // Parse data
  33. //const uint8_t start_block_num =
  34. // mf_classic_get_first_block_num_of_sector(ticket_sector_number);
  35. //const uint8_t* block_start_ptr =
  36. // &data->block[start_block_num + ticket_block_number].data[0];
  37. //uint32_t balance = bit_lib_bytes_to_num_le(block_start_ptr, 4) - 100;
  38. //uint32_t balance_lari = balance / 100;
  39. //uint8_t balance_tetri = balance % 100;
  40. int balance_slot = 4;
  41. if(data->block[balance_slot].data[13] <= data->block[balance_slot + 1].data[13])
  42. balance_slot++;
  43. unsigned short balancecents = byteArrayToIntReversed(
  44. data->block[balance_slot].data[2], data->block[balance_slot].data[3]);
  45. // Check if the sign flag is set in 'balance'
  46. if((balancecents & 0x8000) == 0x8000) {
  47. balancecents = balancecents & 0x7fff; // Clear the sign flag.
  48. balancecents *= -1; // Negate the balance.
  49. }
  50. // Otherwise, check the sign flag in data->block[4].data[1]
  51. else if((data->block[balance_slot].data[1] & 0x80) == 0x80) {
  52. // seq_go uses a sign flag in an adjacent byte.
  53. balancecents *= -1;
  54. }
  55. double balance = balancecents / 100.0;
  56. furi_string_printf(parsed_data, "\e#Go card\nValue: A$%.2f\n", balance);
  57. parsed = true;
  58. } while(false);
  59. return parsed;
  60. }
  61. static void gocard_on_enter(Metroflip* app) {
  62. dolphin_deed(DolphinDeedNfcRead);
  63. app->sec_num = 0;
  64. if(app->data_loaded) {
  65. Storage* storage = furi_record_open(RECORD_STORAGE);
  66. FlipperFormat* ff = flipper_format_file_alloc(storage);
  67. if(flipper_format_file_open_existing(ff, app->file_path)) {
  68. MfClassicData* mfc_data = mf_classic_alloc();
  69. mf_classic_load(mfc_data, ff, 2);
  70. FuriString* parsed_data = furi_string_alloc();
  71. Widget* widget = app->widget;
  72. furi_string_reset(app->text_box_store);
  73. if(!gocard_parse(parsed_data, mfc_data)) {
  74. furi_string_reset(app->text_box_store);
  75. FURI_LOG_I(TAG, "Unknown card type");
  76. furi_string_printf(parsed_data, "\e#Unknown card\n");
  77. }
  78. widget_add_text_scroll_element(
  79. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  80. widget_add_button_element(
  81. widget, GuiButtonTypeRight, "Exit", metroflip_exit_widget_callback, app);
  82. widget_add_button_element(
  83. widget, GuiButtonTypeCenter, "Delete", metroflip_delete_widget_callback, app);
  84. mf_classic_free(mfc_data);
  85. furi_string_free(parsed_data);
  86. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
  87. }
  88. flipper_format_free(ff);
  89. } else {
  90. // Setup view
  91. Popup* popup = app->popup;
  92. popup_set_header(popup, "unsupported", 68, 30, AlignLeft, AlignTop);
  93. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  94. }
  95. }
  96. static bool gocard_on_event(Metroflip* app, SceneManagerEvent event) {
  97. bool consumed = false;
  98. if(event.type == SceneManagerEventTypeCustom) {
  99. if(event.event == MetroflipCustomEventCardDetected) {
  100. Popup* popup = app->popup;
  101. popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop);
  102. consumed = true;
  103. } else if(event.event == MetroflipCustomEventCardLost) {
  104. Popup* popup = app->popup;
  105. popup_set_header(popup, "Card \n lost", 68, 30, AlignLeft, AlignTop);
  106. consumed = true;
  107. } else if(event.event == MetroflipCustomEventWrongCard) {
  108. Popup* popup = app->popup;
  109. popup_set_header(popup, "WRONG \n CARD", 68, 30, AlignLeft, AlignTop);
  110. consumed = true;
  111. } else if(event.event == MetroflipCustomEventPollerFail) {
  112. Popup* popup = app->popup;
  113. popup_set_header(popup, "Failed", 68, 30, AlignLeft, AlignTop);
  114. consumed = true;
  115. }
  116. } else if(event.type == SceneManagerEventTypeBack) {
  117. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  118. consumed = true;
  119. }
  120. return consumed;
  121. }
  122. static void gocard_on_exit(Metroflip* app) {
  123. widget_reset(app->widget);
  124. if(app->poller && !app->data_loaded) {
  125. nfc_poller_stop(app->poller);
  126. nfc_poller_free(app->poller);
  127. }
  128. // Clear view
  129. popup_reset(app->popup);
  130. metroflip_app_blink_stop(app);
  131. }
  132. /* Actual implementation of app<>plugin interface */
  133. static const MetroflipPlugin gocard_plugin = {
  134. .card_name = "gocard",
  135. .plugin_on_enter = gocard_on_enter,
  136. .plugin_on_event = gocard_on_event,
  137. .plugin_on_exit = gocard_on_exit,
  138. };
  139. /* Plugin descriptor to comply with basic plugin specification */
  140. static const FlipperAppPluginDescriptor gocard_plugin_descriptor = {
  141. .appid = METROFLIP_SUPPORTED_CARD_PLUGIN_APP_ID,
  142. .ep_api_version = METROFLIP_SUPPORTED_CARD_PLUGIN_API_VERSION,
  143. .entry_point = &gocard_plugin,
  144. };
  145. /* Plugin entry point - must return a pointer to const descriptor */
  146. const FlipperAppPluginDescriptor* gocard_plugin_ep(void) {
  147. return &gocard_plugin_descriptor;
  148. }