1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 04:34:43 +04:00

Merge branch 'dev' into release

This commit is contained in:
MX
2024-01-06 21:57:47 +03:00
207 changed files with 5733 additions and 230 deletions

View File

@@ -2,9 +2,7 @@
**This release has some unresolved issues, if any of those affects your daily usage, stay at 065 release or wait for next releases:** <br>
**Issues from this list will be fixed in next releases**
### Known NFC app regressions and issues:
- Mifare Classic with custom UID add manually option was temporarily removed (Unleashed)
- Mifare Mini clones reading is broken (original mini working fine) (OFW)
- Mifare Classic dict attack fast skip (multiple presses on OK button) causes glitches/incorrect reading (OFW)
- EMV simple data parser was removed with protocol with refactoring (OFW)
- Option to unlock Slix-L (NFC V) with preset or custom password was removed with refactoring (OFW)
- NFC CLI was removed with refactoring (OFW)
@@ -14,30 +12,25 @@
- Also in app **Enhanced Sub-GHz Chat** - NFC part was temporarily removed to make app usable, NFC part of the app requires remaking it with new nfc stack <br>
**API was updated to v50.x**
## New changes
* IR: Updated infrared assets (by @amec0e | PR #677)
* NFC: Fix Saflok edge case 0.5% of UIDs got wrong result (by @noproto | PR #668)
* NFC: Zolotaya Korona transport card parser added (by @Leptopt1los)
* NFC: Parsers cleanup for new api (by @Leptopt1los)
* SubGHz: Temp fix for subghz keyboard lock display issue (furi_timer is not working properly)
* SubGHz: Added new option to delete old signals on full memory
* SubGHz: Faac rc/xt add manually (unverified)
* SubGHz: Better subghz history element removal (by @Willy-JL)
* SubGHz: Fix key display newline issue in came atomo
* NFC: Skip system dict bug fixed (by @Leptopt1los)
* NFC: Set ATQA scene bit numbering changed (by @Leptopt1los)
* NFC: Added plugin to read WashCity card balance (by @yaba | PR #683)
* NFC: Add manually MF Classic with custom UID (by @Leptopt1los | PR #690)
* NFC: Fix MyKey production date parsing by [@augustozanellato](https://github.com/flipperdevices/flipperzero-firmware/pull/3332/files)
* Apps: Move hid and snake apps into main repo (will be included in `c` builds)
* Docs: Remove weird newline in applications/ReadMe.md (by @Eczbek | PR #688)
* SubGHz: Proper fix for subghz keyboard lock display issue (thanks @Willy-JL)
* SubGHz: Use long press to exit transmitter (to avoid unwanted 2 buttons hold condition, holding arrow button and exit causes default button change, which is stays as hidden feature, but this change makes it harder to call it accidentally)
* Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
* OFW: USART Bridge: added support for software control of DE/RE pins
* OFW: ufbt: changed toolchain environment invocation; updated .gitignore for app template
* OFW: Keys Dict: fix PVS warnings
* OFW: NfcDict Refactoring
* OFW: Add AC's Carrier 42QG5A580SC and AUX YKR-H/006E
* OFW: NFC Plugins loading rework
* OFW: MFC emulation fix
* OFW: nfc_util: little endian bytes2num functions added
* OFW: Add MyKey parser
* OFW: Update CLI MOTD
* OFW: NFC NTAG and ISO14443-3b reading fix
* OFW: FuriHal: RTC register reset API. New factory reset routine that wipes all RTC backup registers content.
* OFW: FuriHal: various GPIO improvements
* OFW: SubGhz: changed the name of the button when sending RAW to SubGHz
* OFW: Desktop: fix rpc unlock on pin input screen
* OFW: UI refactor
* OFW: MFC emulation fixes
* OFW: Scripts: fix incorrect handling of storage stress test count option
* OFW: Add Samsung AC remotes DB93 and AR-EH04
* OFW: Update mf_classic_dict.nfc
* OFW: Nfc: HID MFC Plugin
* OFW: RPC: reverse input
* Update slideshow pictures by @Svaarich
----

View File

@@ -79,7 +79,8 @@
- **NFC/RFID/iButton**
* LFRFID/iButton Fuzzer plugins
* Extra Mifare Classic keys
* `Add manually` -> Mifare Classic with custom UID
* NFC `Add manually` -> Mifare Classic with custom UID
* NFC parsers: Umarsh, Zolotaya Korona, Kazan, Metromoney, Moscow Social Card, Troika (reworked) and [many others](https://github.com/DarkFlippers/unleashed-firmware/tree/dev/applications/main/nfc/plugins/supported_cards)
* Picopass/iClass plugin (now with emulation support!) included in releases
- **Quality of life & other features**
- Customizable Flipper name **Update! Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL)
@@ -105,20 +106,7 @@ Keeloq [Not ALL systems supported for decode or emulation!] - [Supported manufac
Encoders or sending made by @xMasterX:
- Nero Radio 57bit (+ 56bit encoder improvements)
- CAME 12bit/24bit encoder fixes (Fixes now merged in OFW)
- Keeloq: HCS101
- Keeloq: AN-Motors
- Keeloq: JCM Tech
- Keeloq: MHouse
- Keeloq: Nice Smilo
- Keeloq: DTM Neo
- Keeloq: FAAC RC,XT
- Keeloq: Mutancode
- Keeloq: Normstahl
- Keeloq: Beninca + Allmatic
- Keeloq: Stilmatic
- Keeloq: CAME Space
- Keeloq: Aprimatic (model TR and similar)
- Keeloq: Centurion Nova (thanks Carlos !)
- Keeloq: HCS101, AN-Motors, JCM Tech, MHouse, Nice Smilo, DTM Neo, FAAC RC,XT, Mutancode, Normstahl, Beninca + Allmatic, Stilmatic, CAME Space, Aprimatic (model TR and similar), Centurion Nova (thanks Carlos !)
Encoders or sending made by @Eng1n33r(first implementation in Q2 2022) & @xMasterX (current version):
- CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)

View File

@@ -40,7 +40,6 @@ Applications for main Flipper menu.
Background services providing system APIs to applications.
- `applications.h` - Firmware application list header
- `bt` - BLE service and application
- `cli` - Console service and API
- `crypto` - Crypto cli tools

View File

@@ -9,9 +9,8 @@ void ibutton_scene_delete_success_on_enter(void* context) {
iButton* ibutton = context;
Popup* popup = ibutton->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, ibutton_scene_delete_success_popup_callback);
popup_set_context(popup, ibutton);
popup_set_timeout(popup, 1500);

View File

@@ -9,9 +9,8 @@ void ibutton_scene_save_success_on_enter(void* context) {
iButton* ibutton = context;
Popup* popup = ibutton->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, ibutton_scene_save_success_popup_callback);
popup_set_context(popup, ibutton);
popup_set_timeout(popup, 1500);

View File

@@ -10,7 +10,7 @@ void ibutton_scene_write_success_on_enter(void* context) {
iButton* ibutton = context;
Popup* popup = ibutton->popup;
popup_set_icon(popup, 0, 12, &I_iButtonDolphinVerySuccess_108x52);
popup_set_icon(popup, 0, 9, &I_iButtonDolphinVerySuccess_92x55);
popup_set_text(popup, "Successfully written!", 40, 12, AlignLeft, AlignBottom);
popup_set_callback(popup, ibutton_scene_write_success_popup_callback);

View File

@@ -883,3 +883,79 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8425 4291 542 1401 544 538 542 1404 518 1427 544 534 570 540 516 1434 542 1399 544 539 544 539 541 1403 541 1402 516 570 517 1427 545 566 511 1406 517 1431 542 534 544 1406 543 536 517 567 543 1404 542 1400 595 525 544 1405 542 534 516 1433 516 1428 544 537 544 535 543 1403 542 505 488 21084 8424 4298 566 535 569 1402 567 533 568 537 570 1403 570 1403 565 534 566 537 566 1402 569 1398 569 533 569 538 592 1446 569 535 570 1400 569 567 535 535 568 1437 568 533 568 1398 569 1402 565 533 567 534 569 1403 568 536 569 1402 568 535 567 534 571 1403 568 1415 566 536 571 1362 489 21084 8403 4313 516 1439 517 574 515 1442 515 1441 518 573 516 574 567 1397 514 1440 515 573 516 575 516 1443 515 1439 518 574 516 1440 517 608 535 1396 517 1441 517 579 515 1438 515 576 517 578 568 1390 569 1391 516 575 518 1439 516 573 517 1445 566 1391 516 571 517 572 516 1441 514 543 487
#
# Model: Samsung DB93
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 667 17837 3089 8903 555 445 578 1411 583 442 554 442 529 468 528 468 554 443 554 442 554 444 552 1441 552 472 523 475 522 1473 522 1473 522 475 547 1447 548 1446 548 1446 548 1446 548 1446 548 449 548 450 547 451 546 474 523 476 521 476 521 476 522 476 521 475 546 451 547 450 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 450 547 451 546 474 523 474 523 476 521 477 520 477 520 477 521 476 521 476 546 450 547 450 547 450 546 450 547 451 546 451 546 1448 546 1472 522 2982 3005 8963 522 1499 495 502 495 502 495 502 496 501 496 501 521 476 522 475 522 475 522 1472 522 475 522 475 522 1473 521 475 522 1473 522 1499 495 1500 494 1500 496 1499 521 1473 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 502 495 503 494 503 495 502 495 501 496 501 521 476 522 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 503 494 503 494 503 493 504 494 502 495 502 495 502 520 477 520 2984 3003 8966 519 1475 520 477 520 477 520 477 520 478 519 477 520 478 519 479 518 504 493 1502 493 504 493 503 494 503 494 1501 519 1476 518 1475 519 478 519 1476 518 1476 519 1477 517 1501 493 1503 491 1503 493 1502 493 1502 518 479 518 479 518 479 518 1476 518 1477 517 1477 517 504 493 504 493 504 493 531 466 507 490 1529 467 506 491 529 468 1527 492 1502 492 505 493 1502 492 1502 492 505 492 504 493 504 492 505 492 506 491 532 465 532 465 531 467 530 467 530 467 1528 467 1527 492
#
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 667 17829 3090 8902 556 444 579 1410 584 441 529 468 529 468 554 443 554 442 555 443 553 444 551 1442 551 474 522 475 522 1473 522 475 522 475 547 1448 547 1447 547 1447 547 1447 548 1447 547 449 548 450 547 474 523 474 523 476 521 476 521 476 521 477 521 475 522 476 546 450 547 449 548 449 548 450 547 449 548 450 547 450 547 450 547 451 546 474 523 474 523 474 523 479 518 479 518 479 518 501 496 501 496 477 545 475 522 452 545 474 523 474 523 1472 522 1472 522 1472 522 1472 523 2981 3005 8990 494 1499 495 502 496 501 496 501 496 501 522 475 522 475 522 475 522 475 522 1473 521 475 522 475 522 1473 521 475 522 1473 521 1500 494 1500 495 1499 520 1474 522 1473 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 476 521 476 521 477 520 503 494 503 494 503 495 502 495 502 520 477 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 504 493 503 494 503 494 503 495 502 495 502 495 502 520 477 520 477 520 2984 3003 8966 519 1475 519 477 520 478 519 478 519 478 519 479 518 503 494 505 492 505 492 1503 493 504 493 504 493 504 518 1477 518 1476 518 1476 518 479 518 1477 517 1477 517 1501 493 1504 490 1528 468 1527 468 1527 493 1501 493 504 493 504 493 504 493 1501 493 1502 492 1502 492 504 493 505 491 532 440 556 466 531 467 530 468 530 467 530 467 1527 492 1503 491 505 492 504 493 504 493 505 491 1503 492 505 467 530 492 506 466 557 464 533 464 532 466 1528 467 1528 467 1528 466 1528 491
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 667 17831 3089 8903 581 420 578 1411 583 442 529 468 528 468 554 443 554 442 554 443 553 445 551 1443 550 475 521 475 522 1473 522 475 547 449 548 1447 548 1446 548 1446 548 1446 548 1446 548 449 548 449 548 450 547 473 524 476 521 475 522 476 520 476 522 475 522 475 547 449 549 449 548 449 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 474 523 474 523 476 521 476 521 476 520 477 521 476 546 451 547 450 547 450 547 449 548 450 547 1447 547 1448 546 1448 546 1472 523 2957 3029 8967 518 1477 517 502 495 501 496 501 521 475 522 475 522 475 522 475 522 475 522 1472 522 474 523 475 522 1473 521 475 522 1472 522 1499 495 1500 495 1499 520 1474 522 1473 521 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 502 495 502 495 503 494 502 496 501 496 501 521 476 522 476 521 475 522 475 522 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 503 494 503 494 503 494 503 495 502 495 502 520 477 520 476 521 476 521 2984 3003 8965 520 1474 521 477 520 477 520 477 520 477 520 477 520 478 519 504 493 504 493 1502 494 503 494 502 495 1500 520 1475 519 1475 519 1475 519 478 519 1475 519 1476 518 1501 493 1502 492 1503 493 1501 494 1501 518 1476 518 478 519 478 519 478 519 1476 518 1476 518 1477 517 504 493 506 491 506 491 506 491 506 492 505 492 504 493 504 518 481 516 1501 493 504 493 504 493 480 517 1501 493 504 493 504 493 504 493 505 491 532 466 531 466 532 466 1528 467 1527 468 1527 492 1502 492
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 667 17832 3088 8903 575 448 555 1411 583 441 529 468 529 468 554 443 554 442 554 443 553 444 551 1443 550 474 522 475 522 1473 522 475 522 475 547 1447 548 1446 548 1446 548 1446 548 1447 547 449 548 449 548 474 523 474 523 476 521 476 521 476 521 477 521 475 522 475 547 450 548 449 548 450 547 449 548 449 548 450 547 450 547 449 547 451 546 452 545 474 523 474 523 477 520 478 519 477 519 478 520 477 545 452 545 451 547 451 546 474 523 474 523 1450 544 1472 522 1472 522 1472 523 2981 3005 8990 494 1499 495 502 496 501 496 501 521 476 521 475 522 475 522 475 522 475 522 1473 521 475 522 475 522 1473 521 476 521 1473 521 1500 494 1500 495 1499 495 1499 521 1473 522 475 522 475 522 476 521 475 522 476 521 476 521 476 521 476 521 476 521 477 520 503 494 503 494 503 495 502 495 502 520 477 520 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 477 520 478 519 503 493 504 493 504 494 503 494 502 519 478 520 477 520 477 520 2984 3003 8966 519 1475 519 477 520 477 520 478 519 478 519 478 519 503 494 504 493 505 492 1503 493 504 493 504 493 503 519 478 519 1476 518 1476 518 478 519 1476 518 1477 517 1501 493 1504 490 1504 490 1503 493 1502 493 1502 517 480 517 504 493 480 517 1477 517 1502 492 1502 492 504 493 504 493 504 493 531 466 531 466 1529 467 1527 467 1527 492 504 493 1502 492 505 492 504 493 505 492 1503 492 505 492 505 492 505 467 557 464 532 441 556 466 532 466 1528 466 1528 491 1503 491 1503 466
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 665 17827 3090 8902 556 444 579 1412 583 441 530 467 529 468 554 442 555 442 554 442 554 444 551 1443 550 473 523 475 522 1472 521 475 523 474 523 1471 549 1446 548 1445 549 1446 548 1445 549 448 549 448 549 449 548 473 523 473 524 475 522 475 522 475 523 475 522 474 548 449 548 449 549 448 549 448 549 448 549 448 549 448 549 449 548 449 548 449 548 450 547 473 524 474 523 476 521 476 521 476 521 476 522 475 547 450 547 450 548 449 548 449 548 1447 547 1447 547 1447 547 1448 546 2957 3030 8962 522 1475 519 501 496 501 497 478 519 500 522 475 522 475 523 474 523 474 523 1472 522 474 523 474 523 1472 522 475 522 1472 522 1499 495 1499 496 1499 496 1498 522 1473 522 474 522 475 522 475 522 474 523 475 522 475 522 475 522 475 522 475 522 475 522 502 495 502 495 502 495 501 496 501 496 501 521 476 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 475 522 476 521 476 521 502 495 502 495 502 495 503 495 502 495 501 521 476 521 476 521 2983 3004 8964 521 1474 520 476 521 476 521 477 520 476 521 477 520 477 520 503 494 503 494 1501 494 502 495 502 495 502 520 477 521 1474 520 1474 520 477 520 1474 520 1475 519 1475 519 1500 494 1502 492 1502 493 1501 494 1500 519 478 519 477 520 477 520 1475 519 1475 519 1475 519 478 519 478 519 503 494 505 492 505 492 505 492 1503 493 1502 493 1502 517 1476 518 479 518 479 518 479 518 480 517 479 518 1501 493 504 493 504 493 530 467 531 466 531 467 1527 468 1527 491 1502 493 1501 493
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 668 17822 3064 8902 582 442 555 1413 581 442 528 468 529 494 528 469 502 495 527 470 526 471 525 1468 552 446 550 448 549 1446 548 448 549 448 549 1446 548 1448 546 1471 523 1473 521 1473 521 476 521 475 522 475 547 449 548 449 548 449 548 449 548 449 548 449 548 449 548 449 548 450 547 474 523 474 523 474 523 477 520 477 520 477 520 477 521 476 521 476 546 450 548 450 547 474 523 474 523 450 547 474 523 474 523 452 545 474 523 474 523 474 523 1500 494 1499 495 1499 496 1498 522 2955 3032 8963 521 1472 522 475 522 474 523 475 522 475 522 475 522 475 522 475 522 475 522 1499 495 502 495 502 495 1499 496 501 521 1473 522 1473 522 1472 522 1472 522 1473 521 1473 522 475 522 475 522 502 495 502 495 502 495 503 495 501 496 501 496 501 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 476 521 477 520 504 493 503 494 503 494 503 495 502 495 502 495 502 520 477 521 476 521 476 521 476 521 477 520 477 520 477 520 476 521 477 520 2984 3003 8967 518 1502 492 504 493 504 494 503 494 503 494 503 519 478 519 478 519 478 519 1476 518 478 518 479 518 478 519 478 519 1501 493 1501 493 506 491 1504 491 1527 468 1503 492 1526 493 1501 493 1501 494 1501 493 1501 493 504 493 504 493 504 493 1528 466 1529 466 1528 467 529 468 529 493 504 493 504 493 504 493 1502 492 1502 492 1502 467 529 493 1502 491 533 465 532 465 532 465 531 467 530 467 1528 467 530 467 530 467 530 467 530 467 530 467 1527 467 1528 466 1528 466 1529 492
#
# Model: Samsung AR-EH04
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 663 17769 3057 8901 529 492 527 1434 556 465 529 464 529 464 555 439 555 438 556 438 554 440 553 1435 552 444 549 470 524 1465 524 1466 522 473 522 1467 522 1466 549 1440 549 1440 548 1440 548 445 549 445 549 446 548 447 547 471 523 470 524 471 523 472 522 472 521 473 522 472 522 472 523 472 548 446 549 445 548 446 548 447 547 447 547 446 548 447 547 470 524 471 523 471 523 471 523 471 523 498 496 475 519 498 496 497 498 497 522 472 523 471 524 470 523 471 523 1443 546 1442 547 2947 3023 8935 522 1466 522 471 523 472 522 474 520 498 496 498 496 498 497 497 497 497 523 1466 523 471 523 471 523 1466 523 471 523 1466 522 1467 522 1467 522 1493 495 1493 496 1493 497 497 522 472 523 471 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 499 495 499 495 499 495 499 495 499 496 498 522 472 523 472 523 471 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 473 521 499 495 500 494 499 495 499 496 499 521 2947 3024 8937 521 1467 521 473 521 473 521 472 521 473 521 473 521 473 521 473 521 474 520 1469 520 500 494 500 494 1495 495 500 495 499 520 474 521 1468 520 1468 521 1468 521 1468 521 1468 521 1469 519 1470 518 1495 493 1496 493 500 495 499 520 474 521 1468 520 1469 520 1469 520 473 521 474 520 474 520 475 519 476 518 500 494 502 492 502 491 1497 493 1495 495 499 495 499 520 474 520 475 519 475 519 475 519 475 519 475 519 500 494 501 493 501 493 501 493 501 493 1498 491 1497 519
#
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 665 17760 3083 8875 553 468 527 1432 584 438 555 439 529 464 555 439 555 438 556 438 555 439 553 1435 552 443 550 470 524 1466 523 471 522 473 521 1467 523 1466 549 1439 549 1440 549 1439 549 445 549 445 549 445 549 446 548 470 524 470 524 471 523 472 522 472 522 473 521 472 522 472 547 447 548 445 549 445 549 445 549 445 549 446 548 445 549 445 549 447 547 470 524 471 523 471 523 471 523 473 521 473 521 473 521 473 521 472 523 472 548 446 548 1441 547 1441 548 1441 547 1441 547 2947 3023 8935 522 1466 522 472 522 474 519 475 519 475 519 474 521 473 546 448 547 448 546 1465 523 449 545 448 546 1466 522 471 523 1467 522 1466 522 1493 495 1493 495 1493 496 1493 522 471 523 471 523 471 523 471 523 471 523 471 523 471 523 472 522 472 522 472 522 472 522 472 522 499 495 498 495 499 496 498 496 498 496 498 522 472 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 499 495 500 494 500 495 499 495 499 495 498 522 2947 3023 8937 520 1467 521 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1495 494 500 494 500 495 500 494 1494 520 1468 521 1467 521 473 521 1468 521 1468 520 1469 519 1469 519 1471 517 1495 494 1495 494 1494 520 474 521 473 521 473 520 1468 521 1468 521 1468 520 474 520 475 519 475 519 500 493 500 494 501 493 501 493 501 493 1495 495 1494 520 474 520 474 520 474 520 475 519 1470 518 475 519 476 518 500 493 501 493 501 493 501 493 1497 492 1497 492 1496 493 1495 518
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 666 17765 3056 8901 529 492 526 1434 556 465 529 464 530 464 555 438 556 438 555 439 554 440 552 1436 551 443 550 470 524 1465 524 471 523 472 522 1467 521 1467 548 1441 549 1440 548 1440 548 445 549 445 549 445 549 446 548 447 547 470 524 471 523 471 523 473 521 472 522 473 521 473 521 473 522 472 548 446 549 446 548 446 548 447 547 447 547 470 524 447 547 471 523 471 523 471 523 471 523 471 523 475 519 498 496 498 496 498 496 497 498 497 523 1466 524 1443 545 1441 548 1442 546 2947 3023 8935 522 1466 522 472 522 472 522 498 496 498 496 498 496 498 496 498 522 472 523 1466 522 471 523 471 523 1466 523 471 523 1466 523 1467 522 1467 521 1493 496 1493 495 1493 497 497 522 472 523 471 523 471 523 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 495 499 496 498 496 498 522 472 522 472 523 472 521 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 474 520 500 494 500 494 500 495 500 495 499 521 2947 3024 8937 520 1467 521 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1469 520 500 494 500 493 1496 494 1494 495 1494 520 1468 520 473 521 1469 519 1468 521 1469 519 1470 519 1470 519 1495 493 1496 493 1495 495 499 520 474 520 474 520 1469 520 1469 519 1469 519 474 520 475 519 500 494 500 494 500 494 502 492 502 492 502 493 501 493 1496 494 500 519 475 520 475 518 1470 519 475 518 476 518 475 519 476 494 525 493 501 493 501 493 1498 491 1498 491 1497 493 1496 518
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 664 17763 3082 8874 553 444 552 1433 583 438 529 464 530 464 555 439 555 438 556 438 555 440 553 1435 552 443 550 470 524 1466 523 472 521 472 522 1467 523 1466 549 1439 549 1439 549 1439 550 444 550 445 549 445 549 447 547 470 524 471 523 471 523 472 522 472 522 473 521 472 523 472 522 471 549 446 549 445 549 446 548 446 548 446 548 446 548 446 548 448 546 471 523 471 523 471 523 471 523 475 519 497 497 474 520 475 520 497 497 496 524 471 524 1465 523 1442 547 1442 547 1442 547 2946 3024 8935 522 1466 522 471 523 474 520 474 520 498 496 474 521 497 522 471 523 471 523 1465 523 471 523 471 523 1466 522 471 523 1466 523 1466 523 1493 495 1493 495 1493 496 1492 523 471 523 471 523 471 523 471 523 471 523 471 523 471 523 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 496 498 497 498 497 497 523 472 522 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 499 495 499 495 499 496 499 496 498 496 498 522 2946 3024 8937 521 1467 521 472 522 472 522 473 521 473 521 473 521 473 521 474 520 474 520 1495 493 500 494 499 495 499 495 499 496 1493 521 1468 520 473 521 1468 520 1468 521 1468 520 1469 519 1470 518 1495 494 1495 494 1494 495 499 520 473 521 473 521 1468 520 1469 520 1468 521 474 520 474 520 475 519 475 519 476 518 1496 492 1496 494 1495 495 499 520 1469 520 474 520 474 520 474 519 1470 520 474 520 476 518 476 519 477 517 500 494 500 494 503 491 1497 492 1496 493 1495 519 1470 519
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 666 17758 3056 8901 528 493 526 1434 580 441 554 439 529 465 554 439 555 439 555 439 554 440 553 1435 552 443 549 470 524 1465 524 472 522 472 521 1467 523 1467 548 1440 549 1440 548 1440 549 445 549 445 549 446 548 446 548 471 523 471 523 471 523 471 523 473 521 473 521 473 521 473 522 472 547 446 549 446 549 445 548 446 548 446 548 446 548 446 548 447 547 471 523 471 523 471 523 471 523 473 521 474 520 473 521 474 521 473 522 473 546 447 548 1441 548 1442 547 1442 547 1442 546 2948 3021 8936 521 1466 522 472 522 498 496 499 494 476 519 476 519 497 497 497 523 472 522 1466 523 471 523 472 522 1466 522 472 522 1466 522 1467 522 1467 522 1493 494 1495 495 1493 522 472 522 472 522 471 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 499 495 499 495 499 495 499 496 498 521 473 522 472 523 472 522 472 522 472 522 473 521 472 522 472 522 472 522 473 521 473 521 474 520 473 521 499 495 500 494 500 495 499 495 499 521 2947 3023 8938 520 1468 520 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1495 494 500 494 500 494 500 495 500 494 1494 520 1468 521 474 520 1468 520 1469 520 1468 520 1469 520 1470 518 1496 493 1496 493 1495 495 499 519 475 520 474 520 1468 520 1469 519 1469 519 474 520 475 519 475 519 476 518 500 494 500 494 1497 492 1497 492 1496 493 1495 519 475 519 475 519 475 519 475 519 475 519 1471 518 476 518 500 494 501 493 501 493 501 493 1498 491 1498 491 1496 518 1472 517
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 664 17757 3057 8901 528 469 550 1434 556 465 553 440 529 465 554 439 555 439 555 438 555 440 553 1435 552 443 550 470 524 1466 522 472 522 472 521 1467 523 1466 549 1440 549 1440 548 1440 548 445 549 445 549 445 549 446 548 470 524 471 523 471 523 471 523 472 522 472 522 473 522 472 523 472 548 446 549 445 550 445 548 446 548 446 548 446 548 446 548 447 547 470 524 471 523 471 523 471 523 471 523 474 519 474 521 474 521 473 522 473 546 447 547 1441 548 1442 546 1442 547 1442 546 2947 3023 8935 522 1466 522 472 522 498 496 498 495 499 496 498 496 498 521 473 522 471 523 1466 522 471 523 471 522 1466 523 471 523 1467 522 1467 522 1467 521 1493 495 1494 496 1493 521 473 522 472 522 471 523 472 522 471 523 472 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 499 495 499 495 499 495 499 496 498 522 473 522 472 523 471 522 472 522 472 522 472 522 472 522 472 522 472 522 473 521 473 521 473 521 473 521 499 495 500 494 500 495 499 496 498 522 2947 3023 8937 521 1468 520 473 521 473 521 473 521 473 521 473 521 473 521 474 520 474 520 1470 518 500 494 500 494 500 494 500 494 1494 521 1468 521 473 521 1468 520 1468 520 1469 520 1469 520 1470 518 1495 493 1496 493 1495 494 500 519 475 520 474 520 1469 519 1469 520 1469 519 474 520 474 520 475 519 477 517 500 494 1496 493 1496 493 1496 493 500 495 1495 519 475 518 475 519 475 518 476 518 475 519 1470 519 500 494 500 494 501 493 501 493 501 493 1499 490 1497 493 1497 492 1496 518

View File

@@ -4,9 +4,8 @@ void infrared_scene_edit_delete_done_on_enter(void* context) {
InfraredApp* infrared = context;
Popup* popup = infrared->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, infrared_popup_closed_callback);
popup_set_context(popup, context);
popup_set_timeout(popup, 1500);

View File

@@ -4,9 +4,8 @@ void infrared_scene_edit_rename_done_on_enter(void* context) {
InfraredApp* infrared = context;
Popup* popup = infrared->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
popup_set_callback(popup, infrared_popup_closed_callback);
popup_set_context(popup, context);
popup_set_timeout(popup, 1500);

View File

@@ -4,12 +4,12 @@ void infrared_scene_learn_done_on_enter(void* context) {
InfraredApp* infrared = context;
Popup* popup = infrared->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
if(infrared->app_state.is_learning_new_remote) {
popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);
popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop);
} else {
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
}
popup_set_callback(popup, infrared_popup_closed_callback);

View File

@@ -55,8 +55,8 @@ void lfrfid_scene_clear_t5577_on_enter(void* context) {
lfrfid_clear_t5577_password_and_config_to_EM(app);
notification_message(app->notifications, &sequence_success);
popup_set_header(popup, "Done!", 94, 10, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 7, &I_RFIDDolphinSuccess_108x57);
popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop);
popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55);
popup_set_context(popup, app);
popup_set_callback(popup, lfrfid_popup_timeout_callback);
popup_set_timeout(popup, 1500);

View File

@@ -4,8 +4,8 @@ void lfrfid_scene_delete_success_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_context(popup, app);
popup_set_callback(popup, lfrfid_popup_timeout_callback);
popup_set_timeout(popup, 1500);

View File

@@ -7,8 +7,8 @@ void lfrfid_scene_save_success_on_enter(void* context) {
// Clear state of data enter scene
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0);
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
popup_set_context(popup, app);
popup_set_callback(popup, lfrfid_popup_timeout_callback);
popup_set_timeout(popup, 1500);

View File

@@ -57,7 +57,7 @@ bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess);
consumed = true;
} else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) {
popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48);
popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop);
popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop);
notification_message(app->notifications, &sequence_blink_start_red);
@@ -65,7 +65,7 @@ bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) {
} else if(
(event.event == LfRfidEventWriteFobCannotBeWritten) ||
(event.event == LfRfidEventWriteTooLongToWrite)) {
popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48);
popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop);
popup_set_text(
popup,

View File

@@ -4,8 +4,8 @@ void lfrfid_scene_write_success_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
popup_set_header(popup, "Successfully\nwritten!", 94, 3, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57);
popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop);
popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55);
popup_set_context(popup, app);
popup_set_callback(popup, lfrfid_popup_timeout_callback);
popup_set_timeout(popup, 1500);

View File

@@ -146,6 +146,24 @@ App(
sources=["plugins/supported_cards/zolotaya_korona.c"],
)
App(
appid="hid_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="hid_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/hid.c"],
)
App(
appid="washcity_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="washcity_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/washcity.c"],
)
App(
appid="nfc_start",
targets=["f7"],

View File

@@ -52,9 +52,15 @@ static NfcCommand
if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
const MfUltralightData* data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
uint32_t event = (data->pages_read == data->pages_total) ? NfcCustomEventPollerSuccess :
NfcCustomEventPollerIncomplete;
view_dispatcher_send_custom_event(instance->view_dispatcher, event);
return NfcCommandStop;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
nfc_device_set_data(
instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));
const MfUltralightData* data =
@@ -90,10 +96,55 @@ static NfcCommand
return NfcCommandContinue;
}
enum {
NfcSceneMfUltralightReadMenuStateCardSearch,
NfcSceneMfUltralightReadMenuStateCardFound,
};
static void nfc_scene_read_setup_view(NfcApp* instance) {
Popup* popup = instance->popup;
popup_reset(popup);
uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneRead);
if(state == NfcSceneMfUltralightReadMenuStateCardSearch) {
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop);
popup_set_text(
instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop);
} else {
popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop);
popup_set_icon(instance->popup, 12, 20, &A_Loading_24);
}
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
}
static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) {
bool unlocking =
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneMfUltralightUnlockWarn);
uint32_t state = unlocking ? NfcSceneMfUltralightReadMenuStateCardSearch :
NfcSceneMfUltralightReadMenuStateCardFound;
scene_manager_set_scene_state(instance->scene_manager, NfcSceneRead, state);
nfc_scene_read_setup_view(instance);
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance);
}
bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, uint32_t event) {
if(event == NfcCustomEventCardDetected) {
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound);
nfc_scene_read_setup_view(instance);
} else if((event == NfcCustomEventPollerIncomplete)) {
notification_message(instance->notifications, &sequence_semi_success);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess);
}
return true;
}
static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instance) {
Submenu* submenu = instance->submenu;
@@ -179,7 +230,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = {
.scene_read =
{
.on_enter = nfc_scene_read_on_enter_mf_ultralight,
.on_event = nfc_protocol_support_common_on_event_empty,
.on_event = nfc_scene_read_on_event_mf_ultralight,
},
.scene_read_menu =
{

View File

@@ -146,8 +146,7 @@ static void nfc_protocol_support_scene_more_info_on_exit(NfcApp* instance) {
// SceneRead
static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) {
popup_set_header(
instance->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop);
popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop);
popup_set_icon(instance->popup, 12, 23, &A_Loading_24);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
@@ -162,7 +161,7 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) {
// Start poller with the appropriate callback
nfc_protocol_support[protocol]->scene_read.on_enter(instance);
nfc_blink_detect_start(instance);
nfc_blink_read_start(instance);
}
static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneManagerEvent event) {
@@ -200,6 +199,10 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
instance->scene_manager, NfcSceneDetect);
}
consumed = true;
} else if(event.event == NfcCustomEventCardDetected) {
const NfcProtocol protocol =
instance->protocols_detected[instance->protocols_detected_selected_idx];
consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event);
}
} else if(event.type == SceneManagerEventTypeBack) {
nfc_poller_stop(instance->poller);

View File

@@ -223,7 +223,7 @@ void nfc_text_store_clear(NfcApp* nfc) {
}
void nfc_blink_read_start(NfcApp* nfc) {
notification_message(nfc->notifications, &sequence_blink_start_cyan);
notification_message(nfc->notifications, &sequence_blink_start_yellow);
}
void nfc_blink_emulate_start(NfcApp* nfc) {
@@ -231,7 +231,7 @@ void nfc_blink_emulate_start(NfcApp* nfc) {
}
void nfc_blink_detect_start(NfcApp* nfc) {
notification_message(nfc->notifications, &sequence_blink_start_yellow);
notification_message(nfc->notifications, &sequence_blink_start_cyan);
}
void nfc_blink_stop(NfcApp* nfc) {

View File

@@ -0,0 +1,153 @@
#include "nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "HID"
static const uint64_t hid_key = 0x484944204953;
bool hid_verify(Nfc* nfc) {
bool verified = false;
do {
const uint8_t verify_sector = 1;
uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);
FURI_LOG_D(TAG, "Verifying sector %u", verify_sector);
MfClassicKey key = {};
nfc_util_num2bytes(hid_key, COUNT_OF(key.data), key.data);
MfClassicAuthContext auth_ctx = {};
MfClassicError error =
mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
break;
}
verified = true;
} while(false);
return verified;
}
static bool hid_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);
bool is_read = false;
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
do {
MfClassicType type = MfClassicType1k;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;
data->type = type;
MfClassicDeviceKeys keys = {};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
nfc_util_num2bytes(hid_key, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
nfc_util_num2bytes(hid_key, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}
error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
break;
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = true;
} while(false);
mf_classic_free(data);
return is_read;
}
static uint8_t get_bit_length(const uint8_t* half_block) {
uint8_t bitLength = 0;
uint32_t* halves = (uint32_t*)half_block;
if(halves[0] == 0) {
uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1]));
bitLength = 31 - leading0s;
} else {
uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0]));
bitLength = 63 - leading0s;
}
return bitLength;
}
static uint64_t get_pacs_bits(const uint8_t* block, uint8_t bitLength) {
// Remove sentinel bit from credential. Byteswapping to handle array of bytes vs 64bit value
uint64_t sentinel = __builtin_bswap64(1ULL << bitLength);
uint64_t swapped = 0;
memcpy(&swapped, block, sizeof(uint64_t));
swapped = __builtin_bswap64(swapped ^ sentinel);
FURI_LOG_D(TAG, "PACS: (%d) %016llx", bitLength, swapped);
return swapped;
}
static bool hid_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// verify key
const uint8_t verify_sector = 1;
MfClassicSectorTrailer* sec_tr =
mf_classic_get_sector_trailer_by_sector(data, verify_sector);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6);
if(key != hid_key) break;
// Currently doesn't support bit length > 63
const uint8_t* credential_block = data->block[5].data + 8;
uint8_t bitLength = get_bit_length(credential_block);
if(bitLength == 0) break;
uint64_t credential = get_pacs_bits(credential_block, bitLength);
if(credential == 0) break;
furi_string_printf(parsed_data, "\e#HID Card\n%dbit\n%llx", bitLength, credential);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin hid_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = hid_verify,
.read = hid_read,
.parse = hid_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor hid_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &hid_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* hid_plugin_ep() {
return &hid_plugin_descriptor;
}

View File

@@ -27,8 +27,7 @@
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <stdbool.h>
#include <stdint.h>
#include <furi_hal_rtc.h>
#define TAG "Kazan"

View File

@@ -24,7 +24,6 @@
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <stdint.h>
#define TAG "Metromoney"

View File

@@ -16,6 +16,35 @@ static bool mykey_has_lockid(const St25tbData* data) {
return (data->blocks[5] & 0xFF) == 0x7F;
}
static bool check_invalid_low_nibble(uint8_t value) {
uint8_t value_lo = value & 0xF;
return value_lo >= 0xA;
}
static bool mykey_get_production_date(
const St25tbData* data,
uint16_t* year_ptr,
uint8_t* month_ptr,
uint8_t* day_ptr) {
uint32_t date_block = data->blocks[8];
uint8_t year = date_block >> 16 & 0xFF;
uint8_t month = date_block >> 8 & 0xFF;
uint8_t day = date_block & 0xFF;
// dates are coded in a peculiar way, the hexadecimal value should in fact be interpreted as a decimal value
// so anything in range A-F is invalid.
if(day > 0x31 || month > 0x12 || day == 0 || month == 0 || year == 0) {
return false;
}
if(check_invalid_low_nibble(day) || check_invalid_low_nibble(month) ||
check_invalid_low_nibble(year) || check_invalid_low_nibble(year >> 4)) {
return false;
}
*year_ptr = year + 0x2000;
*month_ptr = month;
*day_ptr = day;
return true;
}
static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
@@ -34,7 +63,10 @@ static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) {
}
}
if((data->blocks[8] >> 16 & 0xFF) > 0x31 || (data->blocks[8] >> 8 & 0xFF) > 0x12) {
uint16_t mfg_year;
uint8_t mfg_month, mfg_day;
if(!mykey_get_production_date(data, &mfg_year, &mfg_month, &mfg_day)) {
FURI_LOG_D(TAG, "bad mfg date");
return false;
}
@@ -56,13 +88,8 @@ static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no");
furi_string_cat_printf(parsed_data, "LockID: %s\n", mykey_has_lockid(data) ? "maybe" : "no");
uint32_t block8 = data->blocks[8];
furi_string_cat_printf(
parsed_data,
"Prod. date: %02lX/%02lX/%04lX",
block8 >> 16 & 0xFF,
block8 >> 8 & 0xFF,
0x2000 + (block8 & 0xFF));
parsed_data, "Prod. date: %02X/%02X/%04X", mfg_day, mfg_month, mfg_year);
if(!is_blank) {
furi_string_cat_printf(

View File

@@ -32,8 +32,7 @@
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <stdbool.h>
#include <stdint.h>
#include <furi_hal_rtc.h>
#define TAG "Umarsh"

View File

@@ -0,0 +1,193 @@
/*
* Parser for WashCity MarkItaly Card (Europe).
*
* Copyright 2023 Filipe Polido (YaBaPT) <polido@gmail.com>
*
* Based on MetroMoney by Leptoptilos <leptoptilos@icloud.com>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "nfc_supported_card_plugin.h"
#include "protocols/mf_classic/mf_classic.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "WashCity"
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
static const MfClassicKeyPair washcity_1k_keys[] = {
{.a = 0xA0A1A2A3A4A5, .b = 0x010155010100}, // Sector 00
{.a = 0xC78A3D0E1BCD, .b = 0xFFFFFFFFFFFF}, // Sector 01
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 02
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 03
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 04
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 05
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 06
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 07
{.a = 0xC78A3D0E0000, .b = 0xFFFFFFFFFFFF}, // Sector 08
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 09
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 10
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 11
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 12
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 13
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 14
{.a = 0x010155010100, .b = 0xFFFFFFFFFFFF}, // Sector 15
};
static bool washcity_verify(Nfc* nfc) {
bool verified = false;
do {
const uint8_t ticket_sector_number = 0;
const uint8_t ticket_block_number =
mf_classic_get_first_block_num_of_sector(ticket_sector_number) + 1;
FURI_LOG_D(TAG, "Verifying sector %u", ticket_sector_number);
MfClassicKey key = {0};
nfc_util_num2bytes(washcity_1k_keys[ticket_sector_number].a, COUNT_OF(key.data), key.data);
MfClassicAuthContext auth_context;
MfClassicError error = mf_classic_poller_sync_auth(
nfc, ticket_block_number, &key, MfClassicKeyTypeA, &auth_context);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", ticket_block_number, error);
break;
}
verified = true;
} while(false);
return verified;
}
static bool washcity_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);
bool is_read = false;
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
do {
MfClassicType type = MfClassicTypeMini;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;
data->type = type;
if(type != MfClassicType1k) break;
MfClassicDeviceKeys keys = {
.key_a_mask = 0,
.key_b_mask = 0,
};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
nfc_util_num2bytes(washcity_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
nfc_util_num2bytes(washcity_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}
error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
break;
}
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = true;
} while(false);
mf_classic_free(data);
return is_read;
}
static bool washcity_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// Verify key
const uint8_t ticket_sector_number = 1;
const uint8_t ticket_block_number = 0;
const MfClassicSectorTrailer* sec_tr =
mf_classic_get_sector_trailer_by_sector(data, ticket_sector_number);
const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
if(key != washcity_1k_keys[ticket_sector_number].a) break;
// 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 = nfc_util_bytes2num(block_start_ptr + 2, 2);
uint32_t balance_usd = balance / 100;
uint8_t balance_cents = balance % 100;
size_t uid_len = 0;
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
// Card Number is printed in HEX (equal to UID)
uint64_t card_number = nfc_util_bytes2num(uid, uid_len);
furi_string_printf(
parsed_data,
"\e#WashCity\nCard number: %0*llX\nBalance: %lu.%02u USD",
uid_len * 2,
card_number,
balance_usd,
balance_cents);
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin washcity_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = washcity_verify,
.read = washcity_read,
.parse = washcity_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor washcity_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &washcity_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* washcity_plugin_ep() {
return &washcity_plugin_descriptor;
}

View File

@@ -29,8 +29,6 @@
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <stdbool.h>
#include <stdint.h>
#define TAG "Zolotaya Korona"

View File

@@ -3831,3 +3831,37 @@ ADC169F922CB
# Volgograd (Russia) Volna transport cards keys
2B787A063D5D
D37C8F1793F7
# Bandai Namco Passport [fka Banapassport] / Sega Aime Card
6090D00632F5
019761AA8082
574343467632
A99164400748
62742819AD7C
CC5075E42BA1
B9DF35A0814C
8AF9C718F23D
58CD5C3673CB
FC80E88EB88C
7A3CDAD7C023
30424C029001
024E4E44001F
ECBBFA57C6AD
4757698143BD
1D30972E6485
F8526D1A8D6D
1300EC8C7E80
F80A65A87FFA
DEB06ED4AF8E
4AD96BF28190
000390014D41
0800F9917CB0
730050555253
4146D4A956C4
131157FBB126
E69DD9015A43
337237F254D5
9A8389F32FBF
7B8FB4A7100B
C8382A233993
7B304F2A12A6
FC9418BF788B

View File

@@ -10,8 +10,8 @@ void nfc_scene_delete_success_on_enter(void* context) {
// Setup view
Popup* popup = nfc->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, nfc);
popup_set_callback(popup, nfc_scene_delete_success_popup_callback);

View File

@@ -17,8 +17,9 @@ void nfc_scene_detect_on_enter(void* context) {
// Setup view
popup_reset(instance->popup);
popup_set_header(instance->popup, "Reading", 97, 15, AlignCenter, AlignTop);
popup_set_text(
instance->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop);
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);

View File

@@ -12,9 +12,8 @@ void nfc_scene_exit_confirm_on_enter(void* context) {
dialog_ex_set_left_button_text(dialog_ex, "Exit");
dialog_ex_set_right_button_text(dialog_ex, "Stay");
dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 11, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop);
dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 0, AlignCenter, AlignTop);
dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop);
dialog_ex_set_context(dialog_ex, nfc);
dialog_ex_set_result_callback(dialog_ex, nfc_scene_exit_confirm_dialog_callback);

View File

@@ -173,6 +173,17 @@ void nfc_scene_mf_classic_dict_attack_on_enter(void* context) {
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
instance->nfc_dict_context.is_card_present = true;
}
static void nfc_scene_mf_classic_dict_attack_notify_read(NfcApp* instance) {
const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller);
bool is_card_fully_read = mf_classic_is_card_read(mfc_data);
if(is_card_fully_read) {
notification_message(instance->notifications, &sequence_success);
} else {
notification_message(instance->notifications, &sequence_semi_success);
}
}
bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) {
@@ -196,7 +207,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
consumed = true;
} else {
notification_message(instance->notifications, &sequence_success);
nfc_scene_mf_classic_dict_attack_notify_read(instance);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true;
@@ -225,13 +236,13 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic);
nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance);
} else {
notification_message(instance->notifications, &sequence_success);
nfc_scene_mf_classic_dict_attack_notify_read(instance);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess);
}
consumed = true;
} else if(state == DictAttackStateSystemDictInProgress) {
notification_message(instance->notifications, &sequence_success);
nfc_scene_mf_classic_dict_attack_notify_read(instance);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
dolphin_deed(DolphinDeedNfcReadSuccess);
consumed = true;

View File

@@ -11,7 +11,7 @@ void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) {
// Setup view
Popup* popup = instance->popup;
popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48);
popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop);
popup_set_text(
popup,

View File

@@ -12,8 +12,8 @@ void nfc_scene_mf_classic_update_initial_success_on_enter(void* context) {
notification_message(instance->notifications, &sequence_success);
Popup* popup = instance->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom);
popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);
popup_set_header(popup, "Updated", 11, 20, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, instance);
popup_set_callback(popup, nfc_scene_mf_classic_update_initial_success_popup_callback);

View File

@@ -16,7 +16,7 @@ void nfc_scene_mf_classic_write_initial_fail_on_enter(void* context) {
notification_message(instance->notifications, &sequence_error);
widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48);
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
widget_add_string_element(
widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!");
widget_add_string_multiline_element(

View File

@@ -12,8 +12,8 @@ void nfc_scene_mf_classic_write_initial_success_on_enter(void* context) {
notification_message(instance->notifications, &sequence_success);
Popup* popup = instance->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom);
popup_set_header(popup, "Success!", 75, 10, AlignLeft, AlignTop);
popup_set_icon(popup, 0, 9, &I_DolphinSuccess_91x55);
popup_set_timeout(popup, 1500);
popup_set_context(popup, instance);
popup_set_callback(popup, nfc_scene_mf_classic_write_initial_success_popup_callback);

View File

@@ -16,7 +16,7 @@ void nfc_scene_mf_classic_wrong_card_on_enter(void* context) {
notification_message(instance->notifications, &sequence_error);
widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48);
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
widget_add_string_element(
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card");
widget_add_string_multiline_element(

View File

@@ -40,7 +40,7 @@ void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) {
dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop);
dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48);
dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);
dialog_ex_set_center_button_text(dialog_ex, "OK");
}

View File

@@ -16,7 +16,7 @@ void nfc_scene_mf_ultralight_write_fail_on_enter(void* context) {
notification_message(instance->notifications, &sequence_error);
widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48);
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
widget_add_string_element(
widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!");
widget_add_string_multiline_element(

View File

@@ -12,8 +12,8 @@ void nfc_scene_mf_ultralight_write_success_on_enter(void* context) {
notification_message(instance->notifications, &sequence_success);
Popup* popup = instance->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom);
popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);
popup_set_header(popup, "Successfully\nwritten", 5, 22, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, instance);
popup_set_callback(popup, nfc_scene_mf_ultralight_write_success_popup_callback);

View File

@@ -16,7 +16,7 @@ void nfc_scene_mf_ultralight_wrong_card_on_enter(void* context) {
notification_message(instance->notifications, &sequence_error);
widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48);
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
widget_add_string_element(
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card");
widget_add_string_multiline_element(

View File

@@ -10,8 +10,8 @@ void nfc_scene_restore_original_on_enter(void* context) {
// Setup view
Popup* popup = nfc->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Original file\nrestored", 13, 22, AlignLeft, AlignBottom);
popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);
popup_set_header(popup, "Original file\nrestored", 5, 22, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, nfc);
popup_set_callback(popup, nfc_scene_restore_original_popup_callback);

View File

@@ -12,9 +12,8 @@ void nfc_scene_retry_confirm_on_enter(void* context) {
dialog_ex_set_left_button_text(dialog_ex, "Retry");
dialog_ex_set_right_button_text(dialog_ex, "Stay");
dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop);
dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 0, AlignCenter, AlignTop);
dialog_ex_set_text(dialog_ex, "All unsaved data will be lost", 64, 12, AlignCenter, AlignTop);
dialog_ex_set_context(dialog_ex, nfc);
dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback);
@@ -29,7 +28,11 @@ bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) {
if(event.event == DialogExResultRight) {
consumed = scene_manager_previous_scene(nfc->scene_manager);
} else if(event.event == DialogExResultLeft) {
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneDetect)) {
if(scene_manager_has_previous_scene(
nfc->scene_manager, NfcSceneMfUltralightUnlockWarn)) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneDetect)) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneDetect);
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneRead)) {

View File

@@ -10,8 +10,8 @@ void nfc_scene_save_success_on_enter(void* context) {
// Setup view
Popup* popup = nfc->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom);
popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, nfc);
popup_set_callback(popup, nfc_scene_save_success_popup_callback);

View File

@@ -4,7 +4,8 @@
static void nfc_scene_set_atqa_byte_input_changed_callback(void* context) {
NfcApp* instance = context;
iso14443_3a_set_atqa(instance->iso14443_3a_edit_data, instance->byte_input_store);
uint8_t atqa_msb[2] = {instance->byte_input_store[1], instance->byte_input_store[0]};
iso14443_3a_set_atqa(instance->iso14443_3a_edit_data, atqa_msb);
}
void nfc_scene_set_atqa_on_enter(void* context) {

View File

@@ -53,6 +53,15 @@ bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) {
nfc_scene_set_type_init_edit_data(instance->iso14443_3a_edit_data, 4);
scene_manager_next_scene(instance->scene_manager, NfcSceneSetSak);
consumed = true;
} else if(
(event.event == NfcDataGeneratorTypeMfClassic1k_4b) ||
(event.event == NfcDataGeneratorTypeMfClassic1k_7b) ||
(event.event == NfcDataGeneratorTypeMfClassic4k_4b) ||
(event.event == NfcDataGeneratorTypeMfClassic4k_7b) ||
(event.event == NfcDataGeneratorTypeMfClassicMini)) {
nfc_data_generator_fill_data(event.event, instance->nfc_device);
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
consumed = true;
} else {
nfc_data_generator_fill_data(event.event, instance->nfc_device);
scene_manager_set_scene_state(

View File

@@ -2,6 +2,29 @@
#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h"
// Sync UID from #UID to block 0 data
void mfclassic_sync_uid(NfcDevice* instance) {
size_t uid_len;
const uint8_t* uid = nfc_device_get_uid(instance, &uid_len);
MfClassicData* mfc_data = (MfClassicData*)nfc_device_get_data(instance, NfcProtocolMfClassic);
uint8_t* block = mfc_data->block[0].data;
// Sync UID
for(uint8_t i = 0; i < (uint8_t)uid_len; i++) {
block[i] = uid[i];
}
if(uid_len == 4) {
// Calculate BCC
block[uid_len] = 0;
for(uint8_t i = 0; i < (uint8_t)uid_len; i++) {
block[uid_len] ^= block[i];
}
}
}
static void nfc_scene_set_uid_byte_input_changed_callback(void* context) {
NfcApp* instance = context;
// Retrieve previously saved UID length
@@ -45,6 +68,9 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) {
consumed = true;
}
} else {
if(nfc_device_get_protocol(instance->nfc_device) == NfcProtocolMfClassic)
mfclassic_sync_uid(instance->nfc_device);
scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName);
consumed = true;
}

View File

@@ -50,6 +50,7 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) {
if(m->state == DetectReaderStateDone) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Completed!");
canvas_draw_icon(canvas, 20, 23, &I_check_big_20x17);
} else {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Collecting...");

View File

@@ -12,8 +12,8 @@ void subghz_scene_delete_success_on_enter(void* context) {
// Setup view
Popup* popup = subghz->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
popup_set_icon(popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, subghz);
popup_set_callback(popup, subghz_scene_delete_success_popup_callback);

View File

@@ -180,7 +180,6 @@ void subghz_scene_receiver_on_enter(void* context) {
subghz->idx_menu_chosen = 0;
}
subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz));
subghz_view_receiver_set_mode(subghz->subghz_receiver, SubGhzViewReceiverModeLive);
// Load history to receiver
@@ -224,6 +223,8 @@ void subghz_scene_receiver_on_enter(void* context) {
subghz_scene_receiver_update_statusbar(subghz);
subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz));
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver);
}

View File

@@ -96,7 +96,7 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) {
subghz);
}
} else {
widget_add_icon_element(subghz->widget, 37, 15, &I_DolphinCommon_56x48);
widget_add_icon_element(subghz->widget, 83, 22, &I_WarningDolphinFlip_45x42);
widget_add_string_element(
subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse.");
}

View File

@@ -11,8 +11,8 @@ void subghz_scene_save_success_on_enter(void* context) {
// Setup view
Popup* popup = subghz->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom);
popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, subghz);
popup_set_callback(popup, subghz_scene_save_success_popup_callback);

View File

@@ -11,7 +11,7 @@ void subghz_scene_show_error_sub_on_enter(void* context) {
// Setup view
Popup* popup = subghz->popup;
popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48);
popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
popup_set_header(popup, furi_string_get_cstr(subghz->error_str), 14, 15, AlignLeft, AlignTop);
popup_set_timeout(popup, 1500);
popup_set_context(popup, subghz);

View File

@@ -17,7 +17,7 @@ void subghz_scene_show_only_rx_on_enter(void* context) {
popup_set_header(popup, header_text, 63, 3, AlignCenter, AlignTop);
popup_set_text(popup, message_text, 0, 17, AlignLeft, AlignTop);
popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48);
popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
popup_set_timeout(popup, 1500);
popup_set_context(popup, subghz);

View File

@@ -53,7 +53,7 @@ void subghz_dialog_message_freq_error(SubGhz* subghz, bool only_rx) {
dialog_message_set_header(message, header_text, 63, 3, AlignCenter, AlignTop);
dialog_message_set_text(message, message_text, 0, 17, AlignLeft, AlignTop);
dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17);
dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22);
dialog_message_show(dialogs, message);
dialog_message_free(message);

View File

@@ -101,24 +101,6 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) {
true);
}
static void subghz_view_receiver_timer_callback(void* context) {
furi_assert(context);
SubGhzViewReceiver* subghz_receiver = context;
with_view_model(
subghz_receiver->view,
SubGhzViewReceiverModel * model,
{ model->bar_show = SubGhzViewReceiverBarShowDefault; },
true);
if(subghz_receiver->lock_count < UNLOCK_CNT) {
subghz_receiver->callback(
SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context);
} else {
subghz_receiver->lock = false;
subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
}
subghz_receiver->lock_count = 0;
}
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool lock) {
furi_assert(subghz_receiver);
subghz_receiver->lock_count = 0;
@@ -130,7 +112,6 @@ void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool loc
{ model->bar_show = SubGhzViewReceiverBarShowLock; },
true);
furi_timer_start(subghz_receiver->timer, 1000);
subghz_view_receiver_timer_callback(subghz_receiver);
} else {
with_view_model(
subghz_receiver->view,
@@ -443,6 +424,21 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
}
}
static void subghz_view_receiver_timer_callback(void* context) {
furi_assert(context);
SubGhzViewReceiver* subghz_receiver = context;
with_view_model(
subghz_receiver->view,
SubGhzViewReceiverModel * model,
{ model->bar_show = SubGhzViewReceiverBarShowDefault; },
true);
if(subghz_receiver->lock_count < UNLOCK_CNT) {
subghz_receiver->callback(
SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context);
}
subghz_receiver->lock_count = 0;
}
bool subghz_view_receiver_input(InputEvent* event, void* context) {
furi_assert(context);
SubGhzViewReceiver* subghz_receiver = context;
@@ -460,14 +456,14 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
subghz_receiver->lock_count++;
}
if(subghz_receiver->lock_count >= UNLOCK_CNT) {
// subghz_receiver->callback(
// SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
subghz_receiver->callback(
SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
with_view_model(
subghz_receiver->view,
SubGhzViewReceiverModel * model,
{ model->bar_show = SubGhzViewReceiverBarShowUnlock; },
true);
//subghz_receiver->lock = false;
subghz_receiver->lock = false;
furi_timer_start(subghz_receiver->timer, 650);
}

View File

@@ -127,7 +127,8 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
SubGhzViewTransmitter* subghz_transmitter = context;
bool can_be_sent = false;
if(event->key == InputKeyBack && event->type == InputTypeShort) {
if(event->key == InputKeyBack && event->type == InputTypeLong) {
// Reset view model
with_view_model(
subghz_transmitter->view,
SubGhzViewTransmitterModel * model,

View File

@@ -415,7 +415,7 @@ bool desktop_api_is_locked(Desktop* instance) {
void desktop_api_unlock(Desktop* instance) {
furi_assert(instance);
view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked);
view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopGlobalApiUnlock);
}
FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance) {

View File

@@ -83,6 +83,7 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DesktopLockedEventUnlocked:
case DesktopGlobalApiUnlock:
desktop_unlock(desktop);
consumed = true;
break;

View File

@@ -126,6 +126,7 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
consumed = true;
break;
case DesktopPinInputEventUnlocked:
case DesktopGlobalApiUnlock:
desktop_unlock(desktop);
consumed = true;
break;

View File

@@ -55,4 +55,5 @@ typedef enum {
DesktopGlobalBeforeAppStarted,
DesktopGlobalAfterAppFinished,
DesktopGlobalAutoLock,
DesktopGlobalApiUnlock,
} DesktopEvent;

View File

@@ -55,7 +55,7 @@ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const
DialogMessage* message = dialog_message_alloc();
dialog_message_set_header(message, "Update needed", 64, 3, AlignCenter, AlignTop);
dialog_message_set_buttons(message, NULL, NULL, NULL);
dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17);
dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22);
dialog_message_set_text(
message, "Update firmware\nto run this app", 3, 26, AlignLeft, AlignTop);
dialog_message_show(dialogs, message);

View File

@@ -519,6 +519,24 @@ const NotificationSequence sequence_success = {
NULL,
};
const NotificationSequence sequence_semi_success = {
&message_display_backlight_on,
&message_green_255,
&message_vibro_on,
&message_note_c4,
&message_delay_50,
&message_note_e4,
&message_delay_50,
&message_note_g4,
&message_delay_50,
&message_sound_off,
&message_delay_50,
&message_note_c5,
&message_delay_50,
&message_sound_off,
NULL,
};
const NotificationSequence sequence_error = {
&message_display_backlight_on,
&message_red_255,

View File

@@ -138,6 +138,7 @@ extern const NotificationSequence sequence_blink_stop;
extern const NotificationSequence sequence_single_vibro;
extern const NotificationSequence sequence_double_vibro;
extern const NotificationSequence sequence_success;
extern const NotificationSequence sequence_semi_success;
extern const NotificationSequence sequence_error;
extern const NotificationSequence sequence_audiovisual_alert;

View File

@@ -5,6 +5,41 @@
#include <flipper.pb.h>
#include <gui.pb.h>
// Contract assertion
_Static_assert(InputKeyMAX == 6, "InputKeyMAX");
_Static_assert(InputTypeMAX == 5, "InputTypeMAX");
_Static_assert(InputKeyUp == (int32_t)PB_Gui_InputKey_UP, "InputKeyUp != PB_Gui_InputKey_UP");
_Static_assert(
InputKeyDown == (int32_t)PB_Gui_InputKey_DOWN,
"InputKeyDown != PB_Gui_InputKey_DOWN");
_Static_assert(
InputKeyRight == (int32_t)PB_Gui_InputKey_RIGHT,
"InputKeyRight != PB_Gui_InputKey_RIGHT");
_Static_assert(
InputKeyLeft == (int32_t)PB_Gui_InputKey_LEFT,
"InputKeyLeft != PB_Gui_InputKey_LEFT");
_Static_assert(InputKeyOk == (int32_t)PB_Gui_InputKey_OK, "InputKeyOk != PB_Gui_InputKey_OK");
_Static_assert(
InputKeyBack == (int32_t)PB_Gui_InputKey_BACK,
"InputKeyBack != PB_Gui_InputKey_BACK");
_Static_assert(
InputTypePress == (int32_t)PB_Gui_InputType_PRESS,
"InputTypePress != PB_Gui_InputType_PRESS");
_Static_assert(
InputTypeRelease == (int32_t)PB_Gui_InputType_RELEASE,
"InputTypeRelease != PB_Gui_InputType_RELEASE");
_Static_assert(
InputTypeShort == (int32_t)PB_Gui_InputType_SHORT,
"InputTypeShort != PB_Gui_InputType_SHORT");
_Static_assert(
InputTypeLong == (int32_t)PB_Gui_InputType_LONG,
"InputTypeLong != PB_Gui_InputType_LONG");
_Static_assert(
InputTypeRepeat == (int32_t)PB_Gui_InputType_REPEAT,
"InputTypeRepeat != PB_Gui_InputType_REPEAT");
#define TAG "RpcGui"
typedef enum {
@@ -168,63 +203,20 @@ static void
RpcSession* session = rpc_gui->session;
furi_assert(session);
InputEvent event;
bool is_valid = (request->content.gui_send_input_event_request.key < (int32_t)InputKeyMAX) &&
(request->content.gui_send_input_event_request.type < (int32_t)InputTypeMAX);
bool invalid = false;
switch(request->content.gui_send_input_event_request.key) {
case PB_Gui_InputKey_UP:
event.key = InputKeyUp;
break;
case PB_Gui_InputKey_DOWN:
event.key = InputKeyDown;
break;
case PB_Gui_InputKey_RIGHT:
event.key = InputKeyRight;
break;
case PB_Gui_InputKey_LEFT:
event.key = InputKeyLeft;
break;
case PB_Gui_InputKey_OK:
event.key = InputKeyOk;
break;
case PB_Gui_InputKey_BACK:
event.key = InputKeyBack;
break;
default:
// Invalid key
invalid = true;
break;
}
switch(request->content.gui_send_input_event_request.type) {
case PB_Gui_InputType_PRESS:
event.type = InputTypePress;
break;
case PB_Gui_InputType_RELEASE:
event.type = InputTypeRelease;
break;
case PB_Gui_InputType_SHORT:
event.type = InputTypeShort;
break;
case PB_Gui_InputType_LONG:
event.type = InputTypeLong;
break;
case PB_Gui_InputType_REPEAT:
event.type = InputTypeRepeat;
break;
default:
// Invalid type
invalid = true;
break;
}
if(invalid) {
if(!is_valid) {
rpc_send_and_release_empty(
session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS);
return;
}
InputEvent event = {
.key = (int32_t)request->content.gui_send_input_event_request.key,
.type = (int32_t)request->content.gui_send_input_event_request.type,
};
// Event sequence shenanigans
event.sequence_source = INPUT_SEQUENCE_SOURCE_SOFTWARE;
if(event.type == InputTypePress) {
@@ -264,6 +256,29 @@ static void rpc_system_gui_virtual_display_render_callback(Canvas* canvas, void*
canvas_draw_xbm(canvas, 0, 0, canvas->width, canvas->height, rpc_gui->virtual_display_buffer);
}
static void rpc_system_gui_virtual_display_input_callback(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(event->key < InputKeyMAX);
furi_assert(event->type < InputTypeMAX);
furi_assert(context);
RpcGuiSystem* rpc_gui = context;
RpcSession* session = rpc_gui->session;
FURI_LOG_D(TAG, "VirtulDisplay: SendInputEvent");
PB_Main rpc_message = {
.command_id = 0,
.command_status = PB_CommandStatus_OK,
.has_next = false,
.which_content = PB_Main_gui_send_input_event_request_tag,
.content.gui_send_input_event_request.key = (int32_t)event->key,
.content.gui_send_input_event_request.type = (int32_t)event->type,
};
rpc_send_and_release(session, &rpc_message);
}
static void rpc_system_gui_start_virtual_display_process(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
@@ -300,6 +315,15 @@ static void rpc_system_gui_start_virtual_display_process(const PB_Main* request,
rpc_gui->virtual_display_view_port,
rpc_system_gui_virtual_display_render_callback,
rpc_gui);
if(request->content.gui_start_virtual_display_request.send_input) {
FURI_LOG_D(TAG, "VirtulDisplay: input forwarding requested");
view_port_input_callback_set(
rpc_gui->virtual_display_view_port,
rpc_system_gui_virtual_display_input_callback,
rpc_gui);
}
gui_add_view_port(rpc_gui->gui, rpc_gui->virtual_display_view_port, GuiLayerFullscreen);
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);

View File

@@ -10,7 +10,7 @@ void bt_settings_scene_forget_dev_success_on_enter(void* context) {
BtSettingsApp* app = context;
Popup* popup = app->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_icon(popup, 48, 6, &I_DolphinDone_80x58);
popup_set_header(popup, "Done", 14, 15, AlignLeft, AlignTop);
popup_set_timeout(popup, 1500);
popup_set_context(popup, app);

View File

@@ -24,8 +24,8 @@ void desktop_settings_scene_pin_disable_on_enter(void* context) {
popup_set_context(app->popup, app);
popup_set_callback(app->popup, pin_disable_back_callback);
popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_header(app->popup, "PIN\ndeleted!", 95, 9, AlignCenter, AlignCenter);
popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_119x62);
popup_set_header(app->popup, "Deleted", 80, 19, AlignLeft, AlignBottom);
popup_set_timeout(app->popup, 1500);
popup_enable_timeout(app->popup);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup);

View File

@@ -125,7 +125,7 @@ void storage_settings_scene_benchmark_on_enter(void* context) {
view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);
if(sd_status != FSE_OK) {
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);
dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);

View File

@@ -14,7 +14,7 @@ void storage_settings_scene_format_confirm_on_enter(void* context) {
FS_Error sd_status = storage_sd_status(app->fs_api);
if(sd_status == FSE_NOT_READY) {
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);
dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);

View File

@@ -47,7 +47,7 @@ void storage_settings_scene_formatting_on_enter(void* context) {
dialog_ex_set_text(
dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter);
} else {
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);
dialog_ex_set_header(dialog_ex, "Format\ncomplete!", 14, 15, AlignLeft, AlignTop);
}
dialog_ex_set_center_button_text(dialog_ex, "OK");

View File

@@ -19,7 +19,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) {
dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_sd_info_dialog_callback);
if(sd_status != FSE_OK) {
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);
dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);

View File

@@ -42,7 +42,7 @@ void storage_settings_scene_unmounted_on_enter(void* context) {
}
dialog_ex_set_center_button_text(dialog_ex, "OK");
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);
dialog_ex_set_context(dialog_ex, app);
dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_unmounted_dialog_callback);

View File

@@ -0,0 +1,24 @@
App(
appid="hid_usb",
name="USB Keyboard & Mouse",
apptype=FlipperAppType.EXTERNAL,
entry_point="hid_usb_app",
stack_size=1 * 1024,
fap_category="USB",
fap_icon="hid_usb_10px.png",
fap_icon_assets="assets",
fap_icon_assets_symbol="hid",
)
App(
appid="hid_ble",
name="Bluetooth Remote",
apptype=FlipperAppType.EXTERNAL,
entry_point="hid_ble_app",
stack_size=1 * 1024,
fap_category="Bluetooth",
fap_icon="hid_ble_10px.png",
fap_icon_assets="assets",
fap_icon_assets_symbol="hid",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Some files were not shown because too many files have changed in this diff Show More